← Commit history
v0.16.61 (insiders): nb-bridge#55 (Drew's loop report): when the save dialog belongs to an elevated browser, nb now STOPS fast with errorCode browser_elevated and a plain message ('Chrome is running as administrator, so Adom can't press Save; press Save yourself or restart Chrome normally'), instead of cycling for the full timeout the way Drew hit for 5 minutes; the result is terminal so pup does not retry into a loop
6 files changed
+20−10
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.60'; // keep in lockstep with package.json (pkg version)+const VERSION = '0.16.61'; // 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.60+0.16.61
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser",- "version": "0.16.60",+ "version": "0.16.61", "expectedExtensionVersion": "0.2.22", "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.60",+ "version": "0.16.61", "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+15−5@@ -1802,7 +1802,7 @@ const VERB_META = { nbrowser_demo: { hint: 'ONE-CALL SHOWCASE of layer 2: picks a live profile, opens a representative page in the user\'s REAL signed-in browser (background + badged), and returns that exact window\'s hwnd/tabId plus a screenshot delivered as a file (fullPath; ab pulls it to you) with per-step progress. Built for embedding "drive my real browser" in a web demo without any window hunting.', related: ['nbrowser_state', 'nbrowser_open_window', 'nbrowser_close_window'], pitfalls: ['it OPENS a real window on the user\'s desktop - clean up with nbrowser_close_window {sessionId} (default "nbdemo") when the demo ends', 'idempotent: re-running closes its previous demo window first', 'needs nbe connected in some profile - if readiness says needs-extension it returns no drivable profile'] }, nbrowser_uninstall: { hint: 'CASCADE HOOK, not for normal use: ab calls this itself during bridge_uninstall (ab 2.0.35+) with {cascade:true} BEFORE deleting the bridge cache, so nb can remove artifacts that live outside it - above all the HKCU native-messaging host registration, which would otherwise leave every profile dialing a deleted binary forever ("Native host has exited"). Also clears ~/.adom/bridges/native-browser, the activity trail, and the icon cache.', related: ['nbrowser_readiness'], pitfalls: ['you almost never call this directly - run ab\'s bridge_uninstall and let it cascade', 'it deliberately does NOT remove the nbe extension from the user\'s browser (their profile, their choice) - it only stops working; use uninstalling-the-extension to remove it properly', 'best-effort by contract: a failure here still lets ab delete the cache, so check removed[]/failed[] rather than assuming'] }, nbrowser_dev_reload: { hint: 'MAINTAINER-only: reload the extension to pick up on-disk code changes (2-pass, reconnects in ~10s).', related: ['nbrowser_status'], pitfalls: ['throttled to 1/90s (a reload restarts the SW + native host)', 'not for normal driving - only after editing the extension source', 'reloads ONE profile; other profiles pick up the new code on their own next reload'] },- nbrowser_export_saved_logins: { hint: "Hand pup EVERY login saved in the user's real browser profile, in ONE pass, through CHROME'S OWN consented export (adom/nb-bridge#55). Chrome decrypts it with the user present (one Windows Hello), so nothing outside Chrome touches the sealed store. Needs {confirm:true, reason}. Passwords are NEVER in the reply: the file is written owner-only on the user's machine and you get counts, hostnames and a handoffPath for the LOCAL tool to import. Notes/TOTP are stripped.", related: ['nbrowser_export_site_session', 'nbrowser_drop_handoff', 'nbrowser_credentials'], pitfalls: ['ASK THE USER FIRST and tell them Hello will appear ONCE: confirm:true is your attestation that they agreed', 'do NOT call this per site - the whole point is one Hello for the whole profile; `sites` exists for a caller that really wants a subset and an AI should not reach for it', 'the user must be AT the machine: Hello cannot be answered for them, and awaiting_user means the prompt is sitting there unanswered', 'the passwords live on the USER\'S disk, not in your container: hand handoffPath to the local tool and call nbrowser_drop_handoff the moment it has imported', 'Windows only today - on macOS tell the user to export by hand from the password manager page', 'export_control_not_found means the browser moved its own UI: read the screenshot it returns and file it rather than clicking blind'] },+ nbrowser_export_saved_logins: { hint: "Hand pup EVERY login saved in the user's real browser profile, in ONE pass, through CHROME'S OWN consented export (adom/nb-bridge#55). Chrome decrypts it with the user present (one Windows Hello), so nothing outside Chrome touches the sealed store. Needs {confirm:true, reason}. Passwords are NEVER in the reply: the file is written owner-only on the user's machine and you get counts, hostnames and a handoffPath for the LOCAL tool to import. Notes/TOTP are stripped.", related: ['nbrowser_export_site_session', 'nbrowser_drop_handoff', 'nbrowser_credentials'], pitfalls: ['ASK THE USER FIRST and tell them Hello will appear ONCE: confirm:true is your attestation that they agreed', 'do NOT call this per site - the whole point is one Hello for the whole profile; `sites` exists for a caller that really wants a subset and an AI should not reach for it', 'the user must be AT the machine: Hello cannot be answered for them, and awaiting_user means the prompt is sitting there unanswered', 'the passwords live on the USER\'S disk, not in your container: hand handoffPath to the local tool and call nbrowser_drop_handoff the moment it has imported', 'Windows only today - on macOS tell the user to export by hand from the password manager page', 'export_control_not_found means the browser moved its own UI: read the screenshot it returns and file it rather than clicking blind', 'browser_elevated is terminal: if the user is running Chrome/Edge as administrator, nb cannot drive its save dialog (Windows blocks ab\'s input to a higher-integrity window) - do NOT retry, tell the user to restart the browser normally or press Save themselves, or use nbrowser_export_site_session (#54) which needs no dialog'] }, nbrowser_export_site_session: { hint: "Carry ONE site's live session out of the user's REAL signed-in browser so another tool ON THE SAME MACHINE (pup) can act as them there, without a password and without a fresh sign-in (adom/nb-bridge#54). Needs {domain, confirm:true, reason}. The cookie VALUES are never in the reply: they are written owner-only to a machine-local file and you get a handoffPath plus counts, because a secret in a reply is a secret in an AI transcript. Pass the path to the local tool, then nbrowser_drop_handoff.", related: ['nbrowser_login_state', 'nbrowser_profiles', 'nbrowser_drop_handoff'], pitfalls: ['ASK THE USER FIRST, naming the site: this lets another tool act as them there. confirm:true is your attestation that they said yes', 'ONE registrable domain per call - there is deliberately no "export everything"', 'the handoff file lives on the USER\'S machine, not in your container: do not try to read it from an AI thread, hand the path to the local tool', 'if the profile has no cookies for the domain you get no_session - the user may be signed in under a DIFFERENT profile (nbrowser_profiles)', 'it expires in 10 minutes and nbrowser_drop_handoff removes it sooner - do that as soon as the import is done'] }, nbrowser_drop_handoff: { hint: 'Delete a secret-handoff file created by nbrowser_export_site_session, as soon as the local tool has read it. Read-only otherwise; it never returns the contents.', related: ['nbrowser_export_site_session'], pitfalls: ['dropped:false just means it was already consumed or expired, which is the safe outcome', 'this is the cleanup half of the export - call it rather than leaving a session file sitting on the user\'s disk'] }, nbrowser_clear_cookies: { hint: 'Background logout: drop a site\'s cookies (incl. httpOnly) for ONE registrable domain, leaving other sites (e.g. Google SSO) intact.', related: ['nbrowser_login_state'], pitfalls: ['scoped to one domain so it never nukes unrelated sessions', '⚠️ do NOT use this to log OUT of a site that ties device-trust to cookies (e.g. Gusto) - it forces a fresh 2FA; use the site\'s own Sign out'] },@@ -3417,10 +3417,20 @@ async function exportSavedLogins(opts) { await exportSay(`Step 2 of 3: Adom asked ${brand} to export your logins. The Windows Hello prompt is coming - approve it.`); } }- // If the dialog has been up for a few seconds and our paste-and-enter has not produced a file,- // Windows is blocking our input (an elevated browser). Tell the user plainly and let them finish it.- if (sawDialogAt && fillTries >= 2 && !foundFile && Date.now() - sawDialogAt > 5000) {- await exportSay(`Almost done. ${brand} is asking where to save, and Adom could not press the button for you here. The path is already in the file name box - just press Save (or Ctrl+V then Enter). Adom picks the file up and deletes it straight after.`, { persist: true });+ // The dialog is up, we have tried to fill+commit it a couple of times, and no file has appeared:+ // its owning process is at a higher integrity level than ab (an elevated browser - confirmed on+ // Drew Owens' machine, nb-bridge#55). ab physically cannot press its buttons. STOP NOW with a plain+ // message rather than cycling for the whole budget (the 5-minute loop Drew hit). The path is on the+ // clipboard, so the user can finish it in two keystrokes if they want to, but we do not pretend to.+ if (sawDialogAt && fillTries >= 3 && !foundFile && Date.now() - sawDialogAt > 8000) {+ await exportSay(`${brand} is running as administrator, so Adom can't press Save for you. To finish: press Save yourself (the path is already filled in), or close this, restart ${brand} normally (not as administrator), and try again.`, { persist: true, size: 'medium' });+ await _notify({ id: 'nb-export-logins', level: 'warning', focus: { hwnd: dlg.hwnd },+ title: `${brand} is running as administrator`,+ body: `Adom can't press Save because ${brand} is elevated. Press Save yourself to finish (the file name is already filled), or restart ${brand} normally and try again.` }).catch(() => {});+ return done({ ok: false, errorCode: 'browser_elevated', browserElevated: true, steps,+ error: `the save dialog belongs to an elevated ${brand}; ab runs at normal integrity and Windows blocks its input to it, so it cannot press Save`,+ dialogLeftOnScreen: true,+ _hint: `TERMINAL - do not retry this verb as-is, it will loop. ${brand} is running as administrator, so nb cannot drive its save dialog (Windows UIPI). Either the USER presses Save (the path is already in the field), or they restart ${brand} without "Run as administrator" and you retry. nbrowser_export_site_session (#54) needs no dialog at all and is unaffected by this.` }); } } steps.push(step('answered the save dialog', saved, saved ? `the save dialog was on screen for ${dialogVisibleMs}ms` : 'no save dialog appeared'));
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "nb-bridge",- "version": "0.16.60",+ "version": "0.16.61", "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.",