← Commit history
v0.16.14 (insiders): On an Edge-only box nb no longer steers the AI toward installing Chrome (adom/nb-bridge#46): chrome_status and readiness report edgeInstalled / nativeBrowserPresent / chromeNeeded and say Chrome is optional when Edge is there; the install offer appears only when no supported browser exists or the user asks for Chrome by name. notifications now prefers ab 2.1.73's mayBeSuppressed flag and hint.
7 files changed
+47−13
bin/nb-dashboard+1−1@@ -20,7 +20,7 @@ const { execFile, spawn } = require('child_process'); const fs = require('fs'); const crypto = require('crypto'); -const VERSION = '0.16.13'; // keep in lockstep with package.json (pkg version)+const VERSION = '0.16.14'; // keep in lockstep with package.json (pkg version) // SEND TO AI IS UN-PARKED (John: "yeah, we are re-enabling the send to ai. it seems to be good now"). // The ah build thread certified the crash family dead on the gate-fixed build and the delivery leg is // proven end to end. The park const is DELETED rather than flipped, so no dead branch can resurrect it.
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.16.13+0.16.14
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser",- "version": "0.16.13",+ "version": "0.16.14", "expectedExtensionVersion": "0.2.10", "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.16.13",+ "version": "0.16.14", "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+34−8@@ -1190,6 +1190,20 @@ const CHROME_MSI_URL = 'https://dl.google.com/chrome/install/googlechromestandal const chromeState = { phase: 'idle', error: null, errorCode: null, startedAt: null, finishedAt: null, awaitingUacSince: null, notifyChannel: null, msiPath: null, uacRetryRequested: false }; let _chromeInFlight = null; +// nb drives WHATEVER native browser is on the box (John, adom/nb-bridge#46). Edge ships with Windows, so on+// a fresh machine it is the browser; Chrome is optional and is only worth installing when the user asks for+// Chrome by name or when NO supported browser exists at all.+function detectEdge() {+ for (const p of ['C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', 'C:/Program Files/Microsoft/Edge/Application/msedge.exe']) { try { if (fs.existsSync(p)) return p; } catch {} }+ return null;+}+function nativeBrowserSummary() {+ const chrome = detectChrome(), edge = detectEdge();+ const profiles = knownProfiles();+ const edgeKeys = profiles.filter((p) => p.browser === 'edge').map((p) => p.key);+ const chromeKeys = profiles.filter((p) => p.browser === 'chrome').map((p) => p.key);+ return { chromeInstalled: !!chrome, edgeInstalled: !!edge, nativeBrowserPresent: !!(chrome || edge), chromeNeeded: !(chrome || edge), edgeProfiles: edgeKeys, chromeProfiles: chromeKeys };+} function detectChrome() { if (process.platform !== 'win32') return null; const home = process.env.USERPROFILE || os.homedir();@@ -1303,10 +1317,12 @@ async function notificationSwitches() { if (!ab) { try { const r = await abCommand('desktop', 'status', {}); let out = r && r.body; if (out && typeof out.output === 'string') { try { out = JSON.parse(out.output); } catch {} } ab = pick(out); } catch {} } const bannersEnabled = ab ? ab.bannersEnabled !== false : true; const urgentAllowed = ab ? ab.urgentAllowed === true : null;- const toastMayBeMuted = !(toastsEnabled && adomBridgeEnabled && bannersEnabled);+ // ab 2.1.73+ folds both conditions into ONE flag to branch on; prefer it when present.+ const abSuppressed = ab && typeof ab.mayBeSuppressed === 'boolean' ? ab.mayBeSuppressed : null;+ const toastMayBeMuted = !(toastsEnabled && adomBridgeEnabled && bannersEnabled) || abSuppressed === true; const mayBeHiddenByDnd = !toastMayBeMuted && urgentAllowed === false;- const v = { checked: true, toastsEnabled, adomBridgeEnabled, bannersEnabled, urgentAllowed, toastMayBeMuted, mayBeHiddenByDnd, abRead: !!ab, _abDiag: ab ? undefined : diag,- _hint: !toastsEnabled ? 'All Windows notifications are OFF on this PC: toasts fold into the notification center unseen. Ask the user to turn notifications back on.'+ const v = { checked: true, toastsEnabled, adomBridgeEnabled, bannersEnabled, urgentAllowed, mayBeSuppressed: abSuppressed, toastMayBeMuted, mayBeHiddenByDnd, abRead: !!ab, _abDiag: ab ? undefined : diag,+ _hint: abSuppressed === true && ab._hint ? String(ab._hint) : !toastsEnabled ? 'All Windows notifications are OFF on this PC: toasts fold into the notification center unseen. Ask the user to turn notifications back on.' : (!adomBridgeEnabled || !bannersEnabled) ? 'Notifications for Adom Bridge are switched OFF in Windows Settings > Notifications: its toasts never show. Ask the user to turn them on (ab notification_settings_open opens that page).' : mayBeHiddenByDnd ? 'This PC has never granted Adom Bridge the one-time "Allow important notifications" permission, so while Do Not Disturb is on (a fresh Windows 11 keeps it on) every ab toast, including the Approve-now one, is hidden. A human clicks Allow on Windows\' prompt or enables the priority option under Settings > System > Notifications > Adom Bridge (ab notification_settings_open); until then do not rely on the toast.' : 'toasts can show' };@@ -1528,9 +1544,9 @@ const NBE_PITCH = 'WORTH SAYING TO THE USER: you can drive their browser with th const NO_FG = 'Open in the BACKGROUND — users automating the browser almost always have OTHER things in the foreground, and a raised window STEALS their keyboard focus (it has typed a user\'s own keystrokes into a username field and dismissed Chrome\'s password dropdown mid-task). Background is where automation SUCCEEDS. NEVER fall back to OS-level launches (Start-Process/launch/msedge/chrome --new-window) or bring_to_front/maximize to surface a window. To genuinely foreground (some auth pages / browser features need it) you must do real work: (1) FIRST warn the user with a Bridge notify (adom-bridge notify_user {message:"Adom is about to bring a browser window forward for <reason>"}), then (2) pass userRequestedForeground:true AND foregroundReason:"<why>". A bare userRequestedForeground with no foregroundReason STAYS background; any driven window that lands topmost is auto-reverted + logged (focus_violation / foreground_raised).'; const VERB_META = { nbrowser_status: { hint: 'Connectivity check: connected profiles + active browser. Call this first if any verb errors "not connected".', related: ['nbrowser_profiles', 'nbrowser_ping', 'nbrowser_open_window'], pitfalls: ['a profile\'s extension SLEEPS when that profile has no open windows - it then shows disconnected; open a window to wake it'] },- nbrowser_readiness: { hint: 'READ-ONLY cold-start probe: is the native-browser extension connected + ready to drive? NEVER spawns work or installs anything. state:"ready" = drive now; state:"needs-extension" = offer to install it (Level-2, see installing-the-extension) or it is asleep (open a window in that profile). Also reports chromeInstalled: if false, the user has no native Chrome and you can offer nbrowser_install_chrome.', related: ['nbrowser_status', 'nbrowser_open_window', 'nbrowser_install_chrome'], pitfalls: ['"needs-extension" can mean not-installed OR installed-but-asleep - nbrowser_status\'s tooltip distinguishes', 'this verb never installs; to install, run the installing-the-extension flow', 'for a machine-wide "what do I have" answer prefer ab\'s bridge_readiness (aggregates all bridges, spawns none)'] },+ nbrowser_readiness: { hint: 'READ-ONLY cold-start probe: is the native-browser extension connected + ready to drive? NEVER spawns work or installs anything. state:"ready" = drive now; state:"needs-extension" = offer to install it (Level-2, see installing-the-extension) or it is asleep (open a window in that profile). Also reports chromeInstalled: false does NOT mean install Chrome: nb drives whatever native browser is there (Edge ships with Windows); read nativeBrowser.chromeNeeded, which is true only when NO supported browser exists, and offer nbrowser_install_chrome only then or when the user asks for Chrome by name.', related: ['nbrowser_status', 'nbrowser_open_window', 'nbrowser_install_chrome'], pitfalls: ['"needs-extension" can mean not-installed OR installed-but-asleep - nbrowser_status\'s tooltip distinguishes', 'this verb never installs; to install, run the installing-the-extension flow', 'for a machine-wide "what do I have" answer prefer ab\'s bridge_readiness (aggregates all bridges, spawns none)'] }, nbrowser_install_chrome: { hint: 'Install NATIVE Google Chrome for the user (Windows). Downloads Google\'s official installer + runs a QUIET user-scoped install (no admin on a normal PC). NON-BLOCKING - returns immediately; poll nbrowser_chrome_status. On a locked-down box it escalates to a notify-first UAC (fires a sticky "Approve now" toast, waits for the tap, then raises the prompt). This is step 1 of getting the AI onto the user\'s REAL browser - after Chrome is in, help them sign into Google + load the extension.', related: ['nbrowser_chrome_status', 'nbrowser_readiness', 'installing-the-extension'], pitfalls: ['on a locked-down box the quiet install parks on a UAC; a fresh Windows 11 keeps Do Not Disturb on until its one-time Allow-notifications prompt is answered, so the Approve-now toast is never seen: nbrowser_chrome_status.notifications says so, and {retryUac:true} shows the prompt again for someone looking at the screen without re-downloading', 'non-blocking: do NOT expect Chrome to exist on return - POLL nbrowser_chrome_status until phase:"ready"', 'awaiting_uac means a prompt is on the user\'s desktop - relay it; do not re-trigger install', 'Windows only for now', 'this only INSTALLS Chrome; the user still signs into Google themselves (their credential) - guide them via setting-up-native-chrome'] },- nbrowser_chrome_status: { hint: 'READ-ONLY: is native Google Chrome installed, and how is an in-flight install going? Returns {installed, executablePath, phase, installing, awaitingUac}. phase: idle|installing|awaiting_uac|ready|failed. Poll this after nbrowser_install_chrome. Never installs or spawns.', related: ['nbrowser_install_chrome', 'nbrowser_readiness'], pitfalls: ['awaiting_uac + notifyChannel:true = a toast is on the desktop, keep polling; notifyChannel:false = relay the UAC to the user yourself', 'installed:true means Chrome.exe is present - next help the user sign into Google + load the extension'] },+ nbrowser_chrome_status: { hint: 'READ-ONLY: is native Google Chrome installed, and how is an in-flight install going? Returns {installed, executablePath, phase, installing, awaitingUac, edgeInstalled, nativeBrowserPresent, chromeNeeded, notifications}. chromeNeeded is true ONLY when no supported browser exists at all: on an Edge-only box Chrome is optional and the hint says so, so do not install it unless the user asks for Chrome by name. phase: idle|installing|awaiting_uac|ready|failed. Poll this after nbrowser_install_chrome. Never installs or spawns.', related: ['nbrowser_install_chrome', 'nbrowser_readiness'], pitfalls: ['awaiting_uac + notifyChannel:true = a toast is on the desktop, keep polling; notifyChannel:false = relay the UAC to the user yourself', 'installed:true means Chrome.exe is present - next help the user sign into Google + load the extension'] }, nbrowser_ping: { hint: 'Round-trip liveness through to the extension.', related: ['nbrowser_status'], pitfalls: ['a pong proves the extension is reachable, NOT that any profile has a drivable window — use nbrowser_status/readiness for that'] }, nbrowser_progress: { hint: 'FAST poll for MEASURED progress of every in-flight verb: active[] carries one block per running verb (phase, stepLabel, percent, elapsedSec, estimatedSec, etaSec, confidence measured|estimated, startKind, sessionId). Estimates come from this machine\'s own rolling per-verb history (also returned as verbDurations). Poll once a second while something slow runs; empty active[] = idle.', related: ['nbrowser_activity', 'nbrowser_status'], pitfalls: ['percent is capped at 95 until the verb actually completes - the reply of the slow verb itself carries the 100% terminal block', 'confidence is estimated (draw it dimmed) until three real samples exist for that verb on this machine', 'open-ended verbs (wait, wait_for, download_wait, keepalive, record_start) report no percent: render an indeterminate sweep, never a fake 0%'] }, nbrowser_activity: { hint: 'Bounded "who drove what, when" activity trail. Query recent verbs + a per-session summary (activeSessions, each with lastAgoMs) to debug things like "why is this profile badged" or "what is running right now". Filter with {sessionId, thread, profile, verb, errorsOnly, sinceMs, limit}.', related: ['nbrowser_events', 'nbrowser_owned', 'nbrowser_status'], pitfalls: ['the verb serves the in-memory ring (max 600 entries / ~1h, resets on restart); a persistent self-janitored JSONL survives restarts at ~/.adom/bridge-logs/native-browser-activity.jsonl for deeper forensics', 'a session with a large lastAgoMs is idle - its "Adom is driving" badge idle-expires after ~3 min'] },@@ -1758,6 +1774,9 @@ function dispatch(verb, args) { unidentifiedProfiles, // connected but pre-hello (version unknown yet); resolves on next hello chromeInstalled: !!chExe, // is native Google Chrome present on the box? chromeInstallPhase: chExe ? 'ready' : chromeState.phase, // idle|installing|awaiting_uac|ready|failed+ // nb drives whatever native browser is present (adom/nb-bridge#46): on an Edge-only box Chrome is+ // optional, and an AI reading chromeInstalled:false must not conclude it should install Chrome.+ nativeBrowser: nativeBrowserSummary(), // {chromeInstalled, edgeInstalled, nativeBrowserPresent, chromeNeeded, edgeProfiles, chromeProfiles} _hint: extensionStale ? `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@@ -1795,15 +1814,21 @@ function dispatch(verb, args) { return; } if (exe) return resolve({ success: true, output: JSON.stringify({ ok: true, installed: true, alreadyInstalled: true, executablePath: exe, _hint: 'Native Google Chrome is already installed. Next in the onboarding: help the user sign into their Google account (so passwords/bookmarks/autofill sync), then load the Adom extension (installing-the-extension). See setting-up-native-chrome.' }) });+ const nbSum = nativeBrowserSummary(); installNativeChrome({ elevate: (args && args.elevate !== undefined) ? args.elevate : 'auto' }); // fire-and-forget; poll status- return resolve({ success: true, output: JSON.stringify({ ok: true, installing: true, phase: 'installing', _hint: 'Installing Google Chrome in the BACKGROUND (downloading Google\'s official installer, then a quiet user-scoped install). On a normal PC this is silent, NO prompt. On a locked-down box (VM/managed) it needs a Windows approval - the bridge fires a sticky "Approve now" toast and waits for the user to tap it, then raises the UAC. Poll nbrowser_chrome_status: phase goes installing -> awaiting_uac -> ready. Then help the user sign into Google + load the extension (setting-up-native-chrome).', statusVerb: 'nbrowser_chrome_status' }) });+ return resolve({ success: true, output: JSON.stringify({ ok: true, installing: true, phase: 'installing', edgeInstalled: nbSum.edgeInstalled, chromeNeeded: nbSum.chromeNeeded, _note: nbSum.edgeInstalled ? 'Edge is already on this box and nb drives it; Chrome is optional. Continuing because you asked.' : undefined, _hint: 'Installing Google Chrome in the BACKGROUND (downloading Google\'s official installer, then a quiet user-scoped install). On a normal PC this is silent, NO prompt. On a locked-down box (VM/managed) it needs a Windows approval - the bridge fires a sticky "Approve now" toast and waits for the user to tap it, then raises the UAC. Poll nbrowser_chrome_status: phase goes installing -> awaiting_uac -> ready. Then help the user sign into Google + load the extension (setting-up-native-chrome).', statusVerb: 'nbrowser_chrome_status' }) }); } // nbrowser_chrome_status - READ-ONLY: is native Chrome installed / how's an in-flight install going? if (verb === 'nbrowser_chrome_status') { const st = chromeStatus();+ const nb = nativeBrowserSummary();+ Object.assign(st, { edgeInstalled: nb.edgeInstalled, nativeBrowserPresent: nb.nativeBrowserPresent, chromeNeeded: nb.chromeNeeded, edgeProfiles: nb.edgeProfiles }); notificationSwitches().then((sw) => { st.notifications = sw;- const hint = st.installed+ const edgeEnough = !st.installed && nb.edgeInstalled && !st.installing && st.phase !== 'awaiting_uac';+ const hint = edgeEnough && st.phase !== 'failed'+ ? `Chrome is not installed and is NOT needed: Microsoft Edge is installed${nb.edgeProfiles.length ? ` and drivable (${nb.edgeProfiles.join(', ')})` : ''}, and nb drives whatever native browser is on the box (nbrowser_profiles, then nbrowser_open_window or nbrowser_open_os_window in an edge: profile; the extension loads into an Edge profile the same way). Install Chrome only if the user asks for Chrome by name.`+ : st.installed ? 'Native Google Chrome is installed. Next: help the user sign into their Google account (open chrome://settings or accounts.google.com and turn ON sync so passwords/bookmarks/autofill carry over), then load the Adom extension (installing-the-extension). Full flow: setting-up-native-chrome.' : st.awaitingUac ? ((sw.toastMayBeMuted || sw.mayBeHiddenByDnd) ? `Waiting on a Windows security prompt that the user has probably NOT been told about: ${sw._hint} Meanwhile, anyone looking at this PC's screen can be shown the prompt again with nbrowser_install_chrome {retryUac:true} (no re-download).`@@ -1811,7 +1836,8 @@ function dispatch(verb, args) { : st.installing ? 'Chrome install in progress (downloading / silent install). Poll again in a few seconds.' : st.phase === 'failed' && st.errorCode === 'chrome_needs_approval' && st.canRetryUac ? `The Windows security prompt was never accepted (${(sw.toastMayBeMuted || sw.mayBeHiddenByDnd) ? sw._hint : 'the toast went unanswered'}). The installer is still on this PC: nbrowser_install_chrome {retryUac:true} shows the prompt again for someone looking at the screen.` : st.phase === 'failed' ? `Chrome install failed (${st.errorCode}): ${st.error}. Fixes depend on the code - e.g. free disk, check network, or ask the user to install Chrome manually.`- : 'No native Chrome yet and no install running. Call nbrowser_install_chrome to install it.';+ : nb.edgeInstalled ? 'Chrome is not installed and no install is running. Edge is on this box and nb drives it; Chrome is optional (install only if the user asks for Chrome by name).'+ : 'NO native browser found on this box (no Chrome, no Edge). nbrowser_install_chrome installs Google Chrome (about 150 MB download; may need a Windows security prompt on a locked-down box).'; return resolve({ success: true, output: JSON.stringify({ ...st, _hint: hint }) }); }); return;
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "nb-bridge",- "version": "0.16.13",+ "version": "0.16.14", "type": "app", "title": "NB - Native Browser Bridge", "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",
skills/adom-native-browser/SKILL.md+8@@ -12,6 +12,14 @@ human-trust signals all apply. It sails through **login walls and captchas** tha Chrome-for-Testing. When a task needs the user's *real* browser or their *real* logins, this is the tool - not pup. +## Use the browser that is there (nb-bridge#46)++nb drives whatever native browser the box has. Edge ships with Windows, so on a fresh machine Edge IS the+browser: load the extension into `edge:Default` and drive it. `nbrowser_chrome_status` and+`nbrowser_readiness.nativeBrowser` say `chromeNeeded:true` only when NO supported browser exists; on an+Edge-only box they say Chrome is optional. Install Chrome only when the user asks for Chrome by name (or+there is no browser at all): it is a 150 MB download and, on a locked-down box, a Windows security prompt.+ ## A fresh box with no signed-in browser (Layer 1 still works, from 0.16.6) `nbrowser_profiles` lists profiles with NO signed-in account as `anonymous:true`, keyed by directory