← 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)

John Lauer ·32a3c2c132 ·4mo ago ·parent e261832
5 files changed +49−28
bridge/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.0.29\ No newline at end of file+0.0.30\ 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 (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" }\ No newline at end of file