← Commit history
bridge 0.0.30: FIX native Chrome install - use --silent --install (not /silent, which errored INVALID_OPTION) for the quiet phase + enterprise MSI via msiexec /i /qn for reliable elevated escalation (no de-elevation handoff)
5 files changed
+49−28
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.0.29+0.0.30
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser (Chrome + Edge)",- "version": "0.0.29",+ "version": "0.0.30", "description": "Drives the user's REAL signed-in Chrome/Edge via an installed extension (native-messaging transport). Mirrors the puppeteer bridge's verb surface as nbrowser_* but runs inside the actual logged-in profile, so cookies/SSO/saved-logins/captcha human-trust all work. The bridge binds an ephemeral loopback port (PORTS.md compliant) and writes a discovery file; the native-messaging host pumps stdio <-> this bridge; the extension never touches a TCP port.", "homepage": "https://wiki.adom.inc/adom/adom-browser-extension", "author": "Adom Inc.",
bridge/package.json+1−1@@ -1,6 +1,6 @@ { "name": "adom-native-browser-bridge",- "version": "0.0.29",+ "version": "0.0.30", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+41−20@@ -417,13 +417,18 @@ function flashHint() { // Native Google Chrome installer — for a user on an Edge-only box who wants the AI to drive their // REAL Chrome. The whole point of this bridge is the AI controlling the user's native browser; if they // have no Chrome, we get them one. Mirrors pup's proven flow: download Google's OFFICIAL offline-// installer, run a QUIET user-scoped `/silent /install` (no admin on a normal PC → %LOCALAPPDATA%\+// installer, run a QUIET user-scoped install (.exe --silent --install, no admin on a normal PC → %LOCALAPPDATA%\ // Google\Chrome), and on a locked-down box (VM/managed) ESCALATE to a notify-FIRST UAC (fire a sticky // "Approve now" toast via the AD direct-API, wait for the tap, THEN raise the prompt so the secure // desktop doesn't hide the toast; re-arm if it expires; ping peer ADs if the user is on another // machine). Non-blocking: nbrowser_install_chrome returns immediately; poll nbrowser_chrome_status. // ============================================================================================ const CHROME_STABLE_URL = 'https://dl.google.com/chrome/install/standalonesetup64.exe';+// The enterprise MSI is the RELIABLE escalation path: msiexec /i /qn installs system-wide directly as+// admin with NO de-elevation handoff (the standalone .exe's `--expect-de-elevated` step fails under+// 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 }; let _chromeInFlight = null; @@ -474,17 +479,19 @@ async function _adCanNotify() { return true; } -// Raise the UAC by launching the installer elevated (Start-Process -Verb RunAs).-function _raiseElevatedChrome(installerPath) {+// Raise the UAC by running msiexec ELEVATED on the enterprise MSI. -Wait -PassThru so we get msiexec's+// real exit code (0 = installed; 1602/1223 = user cancelled the UAC/install; others = failure). NOT+// hidden and NOT the .exe path - a hidden elevated .exe hit the `--expect-de-elevated` handoff failure.+function _raiseElevatedMsi(msiPath) { return new Promise((resolve) => { try { const { spawn } = require('child_process');- const ps = `try { Start-Process -FilePath ${_psq(installerPath)} -ArgumentList '/silent','/install' -Verb RunAs -WindowStyle Hidden -ErrorAction Stop; exit 0 } catch { exit 3 }`;- const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps], { windowsHide: true });+ const ps = `try { $p = Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i',${_psq(msiPath)},'/qn','/norestart' -Verb RunAs -Wait -PassThru -ErrorAction Stop; exit $p.ExitCode } catch { exit 1223 }`;+ const child = spawn('powershell.exe', ['-NoProfile', '-Command', ps], { windowsHide: true }); let done = false; const fin = (c) => { if (!done) { done = true; resolve(c); } }; child.on('error', () => fin(-1));- child.on('exit', (code) => fin(code == null ? 0 : code));- setTimeout(() => fin(0), 20000);+ child.on('exit', (code) => fin(code == null ? -1 : code));+ setTimeout(() => fin(-1), 5 * 60 * 1000); // msiexec install can take a couple minutes; guard at 5 } catch { resolve(-1); } }); }@@ -492,7 +499,7 @@ function _raiseElevatedChrome(installerPath) { // Locked-down box path: quiet install didn't land → drive the user to the UAC. Notify-FIRST if AD can // notify (fire toast, WAIT for tap, THEN raise UAC so the secure desktop doesn't hide it; re-arm on // expiry). If AD can't notify, raise ONCE and surface awaiting_uac for the caller to relay (no spam).-async function _elevatedChromeWithNotify(installerPath) {+async function _elevatedChromeWithNotify(msiPath) { const id = 'nbrowser-chrome-install'; chromeState.phase = 'awaiting_uac'; chromeState.awaitingUacSince = Date.now(); const canNotify = await _adCanNotify();@@ -501,8 +508,9 @@ async function _elevatedChromeWithNotify(installerPath) { const pollChrome = async (ms) => { const end = Date.now() + ms; while (Date.now() < end) { await _sleep(2500); const c = detectChrome(); if (c) return c; } return null; }; if (!canNotify) {- await _raiseElevatedChrome(installerPath);- return await pollChrome(150 * 1000);+ // no way to notify - raise the UAC once (msiexec blocks -Wait until the user answers) and check.+ await _raiseElevatedMsi(msiPath);+ const c = detectChrome(); return c || await pollChrome(30 * 1000); } const doneToast = () => _notify({ id, level: 'success', title: 'Chrome is installed ✓', body: 'Google Chrome is ready. Next: sign into your Google account, then load the Adom extension (setting-up-native-chrome).' }); // Fire ONE sticky "Approve now" toast, then poll QUIETLY for the tap (or chrome appearing) for the@@ -522,10 +530,10 @@ async function _elevatedChromeWithNotify(installerPath) { const resp = await _notifyResp(id); const rb = resp && resp.body; if (rb && rb.pending === false && rb.action) {- await _raiseElevatedChrome(installerPath); // they tapped -> raise the UAC once- const c2 = await pollChrome(130 * 1000);+ 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; }- await fireToast(true); // UAC didn't complete -> ONE re-notify, keep polling+ await fireToast(true); // UAC declined/failed -> ONE re-notify, keep polling } } return null;@@ -546,16 +554,29 @@ function installNativeChrome(opts = {}) { chromeState.error = 'Auto-installing Google Chrome is wired up for Windows only.'; return { ok: false, errorCode: chromeState.errorCode, error: chromeState.error }; }- const tmp = path.join(os.tmpdir(), `chrome_installer_${process.pid}.exe`);- await _downloadChrome(CHROME_STABLE_URL, tmp); let found = null;+ // Phase 1 (quiet, no admin): the standalone .exe with the CORRECT `--silent --install` args does a+ // per-user install on a normal PC with NO prompt. (The old code passed `/silent /install`, which+ // this newer Google-Updater installer REJECTS with a "Setup error: INVALID_OPTION" dialog - the+ // root cause of every silent-install failure.)+ const exeTmp = path.join(os.tmpdir(), `chrome_installer_${process.pid}.exe`); if (elevate !== true) {- const { spawn } = require('child_process');- try { spawn(tmp, ['/silent', '/install'], { windowsHide: true }); } catch {}- for (let i = 0; i < 30; i++) { found = detectChrome(); if (found) break; await _sleep(1500); } // ~45s+ try {+ await _downloadChrome(CHROME_STABLE_URL, exeTmp);+ const { spawn } = require('child_process');+ try { spawn(exeTmp, ['--silent', '--install'], { windowsHide: true }); } catch {}+ for (let i = 0; i < 30; i++) { found = detectChrome(); if (found) break; await _sleep(1500); } // ~45s+ } catch {}+ }+ try { fs.unlinkSync(exeTmp); } catch {}+ // Phase 2 (escalate): download the enterprise MSI and run msiexec /i /qn ELEVATED via the+ // notify-first UAC. The MSI installs system-wide directly (no de-elevation handoff) - reliable+ // 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 {} }- if (!found && elevate !== false) found = await _elevatedChromeWithNotify(tmp);- try { fs.unlinkSync(tmp); } catch {} if (!found) { chromeState.phase = 'failed'; if (elevate === false) { chromeState.errorCode = 'chrome_not_detected'; chromeState.error = 'Ran the Chrome installer but chrome.exe did not appear (no elevation, elevate:false).'; }
native-browser-bridge-manifest.json+5−5@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.0.29",- "url": "native-browser-bridge-v0.0.29.bin",- "sha256": "2ae2e05ed1b53aea5e84739a68df4a3b6da18d36902a884afa4322d319cf783c",- "size": 34309,- "released_at": "2026-07-05T12:39:33Z"+ "version": "0.0.30",+ "url": "native-browser-bridge-v0.0.30.bin",+ "sha256": "f67eaf62abf8247dfc0d4694f840182367c6d37d0800e68a4c83c6ed18a2a8c6",+ "size": 35020,+ "released_at": "2026-07-05T14:02:24Z" }