← Commit history
nb 0.4.5: readiness proves an unlabelled connection with a live ping probe (flapping host loop reads needs-extension, healthy restart reads ready)
5 files changed
+26−15
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.4.4+0.4.5
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser",- "version": "0.4.4",+ "version": "0.4.5", "expectedExtensionVersion": "0.2.5", "description": "Drives the user's REAL signed-in Chrome/Edge, in two layers. Layer 1 (nb) needs NOTHING installed and works on any machine: nbrowser_open_os_window (open a URL in the user's real profile, background+flashing, the OAuth/consent path), nbrowser_os_windows, nbrowser_profiles, nbrowser_wake_profile, nbrowser_force_close. Layer 2 (nbe, the Adom Native Browser Extension) unlocks the rest: CDP driving (navigate/click/read/screenshot) inside their logged-in profile. The bridge binds an ephemeral loopback port and the native-messaging host pumps stdio <-> bridge.", "homepage": "https://wiki.adom.inc/adom/nb-bridge",
bridge/package.json+1−1@@ -1,6 +1,6 @@ { "name": "adom-native-browser-bridge",- "version": "0.4.4",+ "version": "0.4.5", "private": true, "description": "Native-browser bridge for Bridge (ab): layer 1 (nb) plus layer 2 via nbe, the Adom Native Browser Extension (Chrome + Edge).", "main": "server.js",
bridge/server.js+18−7@@ -1460,11 +1460,23 @@ function dispatch(verb, args) { // being connected. ab's own bridge_readiness aggregates across bridges; this reports THIS one. if (verb === 'nbrowser_readiness') { const live = drivableConns();- // Pending = drivable but not yet LABELLED. It counts toward ready (live includes it); we still- // list it so a caller that needs to name a profile knows to resolve first.+ // Pending = not yet LABELLED. Whether it counts as ready is decided by EVIDENCE, not inference:+ // a healthy post-restart connection answers ping in ~20ms (AdomLapper), while a native-host+ // connect LOOP with no extension behind it (ConfRoomROG hit provSeq 15749) holds a socket that+ // answers nothing and dies in seconds. Both look identical from the key alone - so when only+ // pending conns exist, PROBE them and believe the round-trip. Costs <=1.8s only in the ambiguous+ // case; a resolved profile short-circuits it. const pend = pendingConns(); const zombies = [];- const ready = live.length > 0;+ // ⚠️ dispatch's Promise executor is NOT async (await here = SyntaxError; an async executor+ // swallowed a thrown error into a 30s hang once before) - so the probe is a .then() chain.+ const probe = (!resolvedConns().length && pend.length)+ ? Promise.all(pend.map(([k]) => extQuery(k, 'nbrowser_ping', {}, 1800))).then((answers) =>+ answers.some((a) => { try { return !!(a && a.success && JSON.parse(a.output).pong === true); } catch { return false; } }))+ : Promise.resolve(false);+ return probe.then((pendAnswered) => {+ // Ready = a resolved profile exists, OR an unlabelled connection PROVED itself with a pong.+ const ready = resolvedConns().length > 0 || pendAnswered; const chExe = detectChrome(); // Per-profile extension version + stale check. A loaded-unpacked extension does NOT auto-update // (only the bridge does), so a box can sit on an old build whose new verbs return "not implemented@@ -1509,7 +1521,7 @@ function dispatch(verb, args) { const claudeExt = claudeExtAnywhere(); return resolve({ success: true, output: JSON.stringify({ ready,- state: ready ? 'ready' : (pend.length ? 'resolving' : 'needs-extension'),+ state: ready ? 'ready' : 'needs-extension', // a pending socket that failed its ping probe is NOT "resolving" - nothing real is coming pendingProfiles: pend.map(([k]) => k), // DRIVABLE, but the profile email is not resolved yet zombieHosts: zombies, // always [] - see the isZombie note; kept for shape stability claudeExtension: claudeExt, // Claude for Chrome presence (human-fallback awareness; never drivable)@@ -1529,10 +1541,9 @@ function dispatch(verb, args) { ? `STALE EXTENSION: ${staleDetail}; this bridge expects ${EXPECTED_EXT}. New verbs (wait_for/keepalive/download_wait/set_date/totp/robust-click) will return "verb not implemented yet" on the stale profile(s). SELF-HEAL: (1) refresh the unpacked extension dir to the current build (installing-the-extension -> Updating, or re-sync the extension/ folder), then (2) nbrowser_dev_reload {"profile":"<staleProfile>"} - it re-reads disk in 2 passes and reconnects in ~10s. If the on-disk build is ALREADY current, step (2) alone heals it.` + skewHint : ready ? `nbe (the Adom Native Browser Extension) is connected (${live.length} profile(s)) and current (v${EXPECTED_EXT || '?'}); drive it with nbrowser_open_window. Read-only probe - it never spawns work or installs.`- : pend.length- ? `nbe IS connected (${pend.length} socket(s)) but its PROFILE IDENTITY has not resolved yet (${pend.map(([k]) => k).join(', ')}), so NOTHING is drivable yet - every drive verb will refuse 'extension not connected' until it resolves. This is normal for a few seconds after a browser start. FIX: nbrowser_wake_profile {profile:"chrome:<email>"} (from nbrowser_profiles) forces the resolve, or the user opening any window in that profile completes it. Poll this verb until state:"ready".`- : `nbe (the Adom Native Browser Extension) is NOT connected. ${zombies.length ? `(${zombies.length} ZOMBIE host socket(s) - ${zombies.join(', ')} - are alive with no extension behind them: the browser or the extension went away and left the native host running. They are being reaped and are NOT drivable; do not wait on them.) ` : ''}${NBE_PITCH}${chExe ? '' : 'This box has NO native Google Chrome (Edge may be present). If the user wants the AI to drive their REAL Chrome, offer nbrowser_install_chrome (installs Google Chrome, mostly no prompt). '}${claudeExt.installed ? `NOTE: the user HAS the Claude for Chrome extension (${claudeExt.profiles.join(', ')}) - you cannot drive it (closed control plane), but the honest interim is real: they can ask Claude in their browser to work a page interactively, while you offer nbe for full automation from any AI thread. ` : ''}It may be not-yet-installed (a Level-2 proactively-offered install: OFFER to install it, then run installing-the-extension - never make them do it) OR installed-but-asleep (the SW sleeps when a profile has no open window). Full onboarding: the setting-up-native-chrome skill. Read-only: never installs or spawns.` + skewHint + hostHint,+ : `nbe (the Adom Native Browser Extension) is NOT connected. ${pend.length ? `(${pend.length} host socket(s) - ${pend.map(([k]) => k).join(', ')} - are open but FAILED a live ping probe: a native host is connect-looping with no working extension behind it. Do NOT wait on these; treat this box as needs-extension.) ` : ''}${NBE_PITCH}${chExe ? '' : 'This box has NO native Google Chrome (Edge may be present). If the user wants the AI to drive their REAL Chrome, offer nbrowser_install_chrome (installs Google Chrome, mostly no prompt). '}${claudeExt.installed ? `NOTE: the user HAS the Claude for Chrome extension (${claudeExt.profiles.join(', ')}) - you cannot drive it (closed control plane), but the honest interim is real: they can ask Claude in their browser to work a page interactively, while you offer nbe for full automation from any AI thread. ` : ''}It may be not-yet-installed (a Level-2 proactively-offered install: OFFER to install it, then run installing-the-extension - never make them do it) OR installed-but-asleep (the SW sleeps when a profile has no open window). Full onboarding: the setting-up-native-chrome skill. Read-only: never installs or spawns.` + skewHint + hostHint, }) });+ }); // end probe.then } // nbrowser_install_chrome - install native Google Chrome for the user (Windows). Non-blocking: // returns immediately; poll nbrowser_chrome_status / nbrowser_readiness. Quiet user-scoped install
native-browser-bridge-manifest.json+5−5@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.4.4",- "url": "https://wiki.adom.inc/download/adom/nb-bridge/0.4.4/native-browser-bridge-v0.4.4.zip",- "sha256": "71c08eeab8b52e910cff4a266f7f7d096295f89c2b34602c9c33e2f89163bcda",- "size": 105003,- "released_at": "2026-08-22T14:14:52Z"+ "version": "0.4.5",+ "url": "https://wiki.adom.inc/download/adom/nb-bridge/0.4.5/native-browser-bridge-v0.4.5.zip",+ "sha256": "44f36006ab9631bf5728d1328af70a93f0fa6adbd3c3ec765dadd430d0393083",+ "size": 105314,+ "released_at": "2026-08-22T14:23:40Z" }