← Commit history

v0.16.8 (insiders): Chrome install on a locked-down box (adom/nb-bridge#45): nbrowser_install_chrome {retryUac:true} re-raises the Windows security prompt from the installer already on the PC (no re-download; the MSI is kept until Chrome is detected), and nbrowser_chrome_status reports the Windows notification switches (Do Not Disturb, per-app Adom Bridge) so a caller knows when the Approve-now toast could not have been seen and what to tell the user. Skill: the fresh-profile extension install is scripted with one human click on the folder picker.

John Lauer ·b9becd3411 ·2mo ago ·parent 1b32118
7 files changed +92−14
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.7';           // keep in lockstep with package.json (pkg version)+const VERSION = '0.16.8';           // 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.7\ No newline at end of file+0.16.8\ No newline at end of file
bridge/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "native-browser",   "displayName": "Adom Native Browser",-  "version": "0.16.7",+  "version": "0.16.8",   "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.7",+  "version": "0.16.8",   "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+73−9
@@ -1187,7 +1187,7 @@ const CHROME_STABLE_URL = 'https://dl.google.com/chrome/install/standalonesetup6 // automation on locked-down boxes) and no INVALID_OPTION arg-format surprises. Used when the quiet // user-scoped .exe install doesn't land. ~156 MB. const CHROME_MSI_URL = 'https://dl.google.com/chrome/install/googlechromestandaloneenterprise64.msi';-const chromeState = { phase: 'idle', error: null, errorCode: null, startedAt: null, finishedAt: null, awaitingUacSince: null, notifyChannel: null };+const chromeState = { phase: 'idle', error: null, errorCode: null, startedAt: null, finishedAt: null, awaitingUacSince: null, notifyChannel: null, msiPath: null, uacRetryRequested: false }; let _chromeInFlight = null;  function detectChrome() {@@ -1247,6 +1247,38 @@ async function _notifyTarget() { } const _notify = async (args) => abCommand(null, 'notify_user', args, await _notifyTarget()); const _notifyResp = async (id) => abCommand(null, 'notify_response', { id }, await _notifyTarget());+// WINDOWS NOTIFICATION SWITCHES (nb-bridge#45). A fresh Windows 11 keeps Do Not Disturb on until the+// one-time "Allow important notifications from Adom Bridge?" prompt is answered, and then every toast,+// including the sticky "Approve now" one that re-raises the Chrome UAC, folds silently into the+// notification center. ab knows (its window says approval banners are muted) but a caller polling+// chrome_status did not. Read the two switches Windows keeps in HKCU: the global toast switch (absent =+// on; 0 when Do Not Disturb / notifications off) and the per-app switch for ab's identity.+let _notifSwCache = { at: 0, v: null };+function _regDword(key, name) {+  return new Promise((resolve) => {+    try {+      require('child_process').execFile('reg', ['query', key, '/v', name], { timeout: 4000, windowsHide: true }, (err, out) => {+        if (err) return resolve(null);+        const m = String(out || '').match(/REG_DWORD\s+0x([0-9a-f]+)/i);+        resolve(m ? parseInt(m[1], 16) : null);+      });+    } catch { resolve(null); }+  });+}+async function notificationSwitches() {+  if (Date.now() - _notifSwCache.at < 10000 && _notifSwCache.v) return _notifSwCache.v;+  if (process.platform !== 'win32') return { checked: false };+  const base = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings';+  const g = await _regDword(base, 'NOC_GLOBAL_SETTING_TOASTS_ENABLED');+  const a = await _regDword(base + '\\inc.adom.bridge', 'Enabled');+  const toastsEnabled = g === null ? true : g !== 0;          // absent = Windows default = on+  const adomBridgeEnabled = a === null ? true : a !== 0;+  const v = { checked: true, toastsEnabled, adomBridgeEnabled, toastMayBeMuted: !(toastsEnabled && adomBridgeEnabled),+    _hint: !toastsEnabled ? 'Windows Do Not Disturb (or all notifications) is ON on this PC: toasts fold into the notification center unseen. Ask the user to turn Do Not Disturb off, or to answer Windows\' one-time "Allow important notifications from Adom Bridge?" prompt.'+         : !adomBridgeEnabled ? 'Notifications for Adom Bridge are switched OFF in Windows Settings > Notifications: its toasts never show. Ask the user to turn them on.'+         : 'toasts can show' };+  _notifSwCache = { at: Date.now(), v }; return v;+} async function _adCanNotify() {   const r = await abCommand(null, 'notify_user', { id: '_probe_', title: '', body: '', level: 'info', _probe: true }).catch(() => null);   // if ab accepted (any 2xx-ish body) treat notify as available; unknown-verb → unavailable@@ -1324,7 +1356,8 @@ async function _elevatedChromeWithNotify(msiPath) {     if (c) { await doneToast(); return c; }     const resp = await _notifyResp(id);     const rb = resp && resp.body;-    if (rb && rb.pending === false && rb.action) {+    const retry = chromeState.uacRetryRequested === true; if (retry) chromeState.uacRetryRequested = false;+    if (retry || (rb && rb.pending === false && rb.action)) {       await _raiseElevatedMsi(msiPath);                 // they tapped -> run msiexec elevated (blocks until UAC answered + install done)       const c2 = detectChrome() || await pollChrome(20 * 1000);       if (c2) { await doneToast(); return c2; }@@ -1369,8 +1402,10 @@ function installNativeChrome(opts = {}) {       // where the .exe's elevated path fails on locked-down boxes.       if (!found && elevate !== false) {         const msiTmp = path.join(os.tmpdir(), `chrome_enterprise_${process.pid}.msi`);-        try { await _downloadChrome(CHROME_MSI_URL, msiTmp); found = await _elevatedChromeWithNotify(msiTmp); } catch {}-        try { fs.unlinkSync(msiTmp); } catch {}+        try { await _downloadChrome(CHROME_MSI_URL, msiTmp); chromeState.msiPath = msiTmp; found = await _elevatedChromeWithNotify(msiTmp); } catch {}+        // Keep the MSI when the UAC was never answered: nbrowser_install_chrome {retryUac:true} re-raises it+        // from this file without another download (nb-bridge#45). It is removed once Chrome is detected.+        if (found) { try { fs.unlinkSync(msiTmp); } catch {} chromeState.msiPath = null; }       }       if (!found) {         chromeState.phase = 'failed';@@ -1393,7 +1428,7 @@ function installNativeChrome(opts = {}) { } function chromeStatus() {   const exe = detectChrome();-  return { installed: !!exe, executablePath: exe || null, phase: exe ? 'ready' : chromeState.phase, error: chromeState.error, errorCode: chromeState.errorCode, installing: !!_chromeInFlight, awaitingUac: chromeState.phase === 'awaiting_uac', notifyChannel: chromeState.notifyChannel };+  return { installed: !!exe, executablePath: exe || null, phase: exe ? 'ready' : chromeState.phase, error: chromeState.error, errorCode: chromeState.errorCode, installing: !!_chromeInFlight, awaitingUac: chromeState.phase === 'awaiting_uac', notifyChannel: chromeState.notifyChannel , canRetryUac: !!chromeState.msiPath || !!_chromeInFlight}; }  // ============================================================================================@@ -1461,7 +1496,7 @@ const NO_FG = 'Open in the BACKGROUND — users automating the browser almost al 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_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: ['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_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_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%'] },@@ -1703,6 +1738,29 @@ function dispatch(verb, args) {     // (no admin on a normal PC); on a locked-down box escalates to a notify-first UAC.     if (verb === 'nbrowser_install_chrome') {       const exe = detectChrome();+      // retryUac (nb-bridge#45): re-raise the elevated installer ON DEMAND, for a caller with eyes on the+      // screen, when the toast could not be seen. Never restarts a download: it uses the MSI already here.+      if (!exe && args && args.retryUac === true) {+        notificationSwitches().then((sw) => {+        if (_chromeInFlight && chromeState.phase === 'awaiting_uac') {+          chromeState.uacRetryRequested = true;+          return resolve({ success: true, output: JSON.stringify({ ok: true, phase: 'awaiting_uac', raising: true, notifications: sw, _hint: 'Re-raising the Windows security prompt now (within ~3s) on this PC. Someone must click YES on it; then poll nbrowser_chrome_status until phase ready.' }) });+        }+        if (chromeState.msiPath && fs.existsSync(chromeState.msiPath)) {+          const msi = chromeState.msiPath;+          chromeState.phase = 'awaiting_uac'; chromeState.awaitingUacSince = Date.now(); chromeState.error = null; chromeState.errorCode = null;+          _chromeInFlight = (async () => {+            try { await _raiseElevatedMsi(msi); const c = detectChrome() || await (async () => { const end = Date.now() + 30000; while (Date.now() < end) { await _sleep(2500); const x = detectChrome(); if (x) return x; } return null; })();+              if (c) { chromeState.phase = 'ready'; chromeState.finishedAt = Date.now(); try { fs.unlinkSync(msi); } catch {} chromeState.msiPath = null; return { ok: true, executablePath: c }; }+              chromeState.phase = 'failed'; chromeState.errorCode = 'chrome_needs_approval'; chromeState.error = 'The Windows security prompt was raised again but not accepted.'; return { ok: false, errorCode: chromeState.errorCode };+            } finally { _chromeInFlight = null; }+          })();+          return resolve({ success: true, output: JSON.stringify({ ok: true, phase: 'awaiting_uac', raising: true, notifications: sw, _hint: 'Re-raising the Windows security prompt from the installer already on this PC (no new download). Someone must click YES; poll nbrowser_chrome_status.' }) });+        }+        return resolve({ success: false, errorCode: 'nothing_to_retry', error: 'No downloaded installer to re-raise; call nbrowser_install_chrome without retryUac to start over.', notifications: sw });+        });+        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.' }) });       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' }) });@@ -1710,14 +1768,20 @@ function dispatch(verb, args) {     // nbrowser_chrome_status - READ-ONLY: is native Chrome installed / how's an in-flight install going?     if (verb === 'nbrowser_chrome_status') {       const st = chromeStatus();+      notificationSwitches().then((sw) => {+      st.notifications = sw;       const hint = 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-          ? (st.notifyChannel ? 'Waiting on the user to approve the Windows security prompt. A sticky "Approve now" toast is on their desktop; tapping it re-opens the UAC. Keep polling; do NOT re-trigger install.' : 'A UAC was raised once but this ab cannot pop a notification. RELAY TO THE USER: go to the machine and click YES on the Google Chrome installer prompt, then re-poll.')+          ? (sw.toastMayBeMuted ? `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).`+             : st.notifyChannel ? 'Waiting on the user to approve the Windows security prompt. A sticky "Approve now" toast is on their desktop; tapping it re-opens the UAC. Keep polling; to show the prompt again for someone already looking at the screen call nbrowser_install_chrome {retryUac:true} (never a plain re-trigger, which restarts the download).' : 'A UAC was raised once but this ab cannot pop a notification. RELAY TO THE USER: go to the machine and click YES on the Google Chrome installer prompt, then re-poll.')           : 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._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.';       return resolve({ success: true, output: JSON.stringify({ ...st, _hint: hint }) });+      });+      return;     }     // nbrowser_describe - per-verb schemas for ab's Verbs tab + inline runner (ab v1.9.23+).     // ab POSTs {command:"nbrowser_describe",args:{}} and renders each verb expandable with these I/O@@ -1734,8 +1798,8 @@ function dispatch(verb, args) {         d('nbrowser_progress', 'Fast poll for measured progress of every in-flight verb (active[] blocks from this machine\'s rolling per-verb history). Empty active[] = idle.', { sessionId: 'optional str (only that session\'s verbs)' }, { ok: 'bool', active: '[{phase,stepLabel,percent,elapsedSec,estimatedSec,etaSec,confidence,startKind,samples,sessionId?}]', count: 'int', verbDurations: '{verb:{avgMs,n}}' }, 10, { example: {} }),         d('nbrowser_activity', 'Bounded "who drove what, when" activity trail (max 600 entries / ~1h, in-memory, resets on bridge restart). Returns recent verb entries + a per-session summary. Debug a stray badge or see what is running.', { sessionId: 'optional str', thread: 'optional str', profile: 'optional str', verb: 'optional str', errorsOnly: 'optional bool', sinceMs: 'optional int', limit: 'optional int (<=600)' }, { ok: 'bool', entries: '[{agoMs,verb,sessionId,thread,profile,ok,errorCode}]', activeSessions: '[{sessionId,thread,purpose,count,lastAgoMs}]' }, 15, { example: {} }),         d('nbrowser_readiness', 'READ-ONLY cold-start probe: is the native-browser extension connected + ready to drive? Never spawns work or installs. Use for "is it ready / do I have the browser extension" instead of nbrowser_status. Also flags a STALE extension (loaded build older than this bridge expects) so you fix it up-front instead of hitting "verb not implemented yet" mid-task.', {}, { ready: 'bool', state: '"ready"|"needs-extension"', installing: 'bool', extensionConnected: 'bool', profiles: '[str]', extensionVersions: '{profile:version}', expectedExtensionVersion: 'str|null', extensionStale: 'bool', staleProfiles: '[str]', chromeInstalled: 'bool', chromeInstallPhase: 'str', claudeExtension: '{installed:bool, profiles:[str]} - Claude for Chrome presence (human fallback; never drivable)' }, 15, { example: {} }),-        d('nbrowser_install_chrome', '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); escalates to a notify-first UAC on a locked-down box. NON-BLOCKING - poll nbrowser_chrome_status. Step 1 of putting the AI onto the user\'s real signed-in browser.', { elevate: 'optional "auto"(default) | true (skip quiet, straight to UAC) | false (quiet only, no prompt)' }, { ok: 'bool', installing: 'bool', installed: 'bool?', alreadyInstalled: 'bool?', executablePath: 'str?', statusVerb: 'str' }, 30, { statusVerb: 'nbrowser_chrome_status', example: {} }),-        d('nbrowser_chrome_status', 'READ-ONLY: is native Google Chrome installed / how is an install going? Poll after nbrowser_install_chrome.', {}, { installed: 'bool', executablePath: 'str?', phase: '"idle"|"installing"|"awaiting_uac"|"ready"|"failed"', installing: 'bool', awaitingUac: 'bool', errorCode: 'str?' }, 15, { example: {} }),+        d('nbrowser_install_chrome', '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); escalates to a notify-first UAC on a locked-down box. NON-BLOCKING - poll nbrowser_chrome_status. Step 1 of putting the AI onto the user\'s real signed-in browser.', { elevate: 'optional "auto"(default) | true (skip quiet, straight to UAC) | false (quiet only, no prompt)', retryUac: 'optional bool - re-raise the Windows security prompt from the installer already on this PC (no re-download), for a caller with eyes on the screen when the toast could not be seen' }, { ok: 'bool', installing: 'bool', installed: 'bool?', alreadyInstalled: 'bool?', executablePath: 'str?', statusVerb: 'str' }, 30, { statusVerb: 'nbrowser_chrome_status', example: {} }),+        d('nbrowser_chrome_status', 'READ-ONLY: is native Google Chrome installed / how is an install going? Poll after nbrowser_install_chrome.', {}, { notifications: '{checked,toastsEnabled,adomBridgeEnabled,toastMayBeMuted,_hint} - Windows toast switches, so a caller knows when the Approve-now toast could not have been seen', canRetryUac: 'bool', installed: 'bool', executablePath: 'str?', phase: '"idle"|"installing"|"awaiting_uac"|"ready"|"failed"', installing: 'bool', awaitingUac: 'bool', errorCode: 'str?' }, 15, { example: {} }),         d('nbrowser_ping', 'Round-trip liveness check through to the extension.', {}, { ok: 'bool', pong: 'bool' }, 15),         d('nbrowser_open_window', 'Open a NEW window in the BACKGROUND (unfocused) and return a sessionId to drive it. REGISTRATION REQUIRED: sessionId + thread + purpose, or refused (session_unregistered). Never steals focus; a topmost driven window is auto-reverted (focus_violation).', { sessionId: 'required str (your short task id)', thread: 'required str (which AI thread you are)', purpose: 'required str (human-readable, e.g. "finishing John\'s IRS signup")', url: 'required str', profile: P, userRequestedForeground: 'optional bool - raise to foreground ONLY when the user asked or a browser/auth feature requires it (default false)', foregroundReason: 'REQUIRED with userRequestedForeground - human-readable why; without it the window STAYS background. Warn the user via ab notify_user FIRST.' }, { ok: 'bool', sessionId: 'str', tabId: 'str', windowId: 'int', hwnd: 'int (OS window handle - use with desktop_screenshot_window to capture the window+chrome+child popups)', url: 'str', _warning: 'str?', keptBackground: 'bool?' }, 30, { example: { sessionId: 'irs', thread: 'thread-7', purpose: 'finishing John IRS signup', url: 'https://wiki.adom.inc' } }),         d('nbrowser_close_window', 'Close a window the agent opened. includeSpawned:true also closes everything that session SPAWNED (composes/popups).', { sessionId: 'required str', includeSpawned: 'optional bool (also close this session\'s spawned windows)', profile: P }, { ok: 'bool', closed: 'bool', sessionId: 'str', alsoClosedSpawned: '[int]' }, 20, { example: { sessionId: 'work', includeSpawned: true } }),
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "nb-bridge",-  "version": "0.16.7",+  "version": "0.16.8",   "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/installing-the-extension/SKILL.md+14
@@ -256,6 +256,20 @@ cache and would downgrade a tier-managed bridge): write the three host files fro id in `allowed_origins`), then register `HKCU:\Software\Google\Chrome\NativeMessagingHosts\inc.adom.native_browser` and the `Microsoft\Edge` twin with the manifest path (no UAC). The next wake connects. +## A fresh profile: what is scripted and what is not (nb-bridge#45)++On a brand-new Windows box `nbrowser_profiles` lists the unsigned browser (`edge:Default`, anonymous) and+Layer 1 opens URLs in it, but nothing can be DRIVEN until the extension is loaded into that profile. The+install IS scripted: this skill's flow drives `edge://extensions` (Developer mode, Load unpacked) through+ab's UI verbs, and the one step a human must do is pick the folder in the Windows folder picker (the OS+will not let an automation choose it). So the fresh-user flow is: offer it, run it, and tell the user the+single click they will be asked for. Chrome on a locked-down box adds a UAC: `nbrowser_install_chrome`+parks on `awaiting_uac` and its Approve-now toast is unseen while Windows keeps Do Not Disturb on (a fresh+Windows 11 does, until its one-time "Allow important notifications from Adom Bridge?" prompt is answered).+From 0.16.8 `nbrowser_chrome_status.notifications` reports those switches and `nbrowser_install_chrome+{retryUac:true}` shows the security prompt again, from the installer already on the PC, for anyone looking+at the screen. A plain re-call restarts the download; do not use it for that.+ ## Recovery / common failures - **Load unpacked missing** -> Developer mode is off (Step A) or the page didn't navigate (re-`desktop_navigate`, verify by screenshot). - **Picker selected the wrong folder** -> the silent killer; always screenshot the picker path before Select.