← Commit history
Update 5 file(s)
5 files changed
+26−29
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.40+0.1.41
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Browser Extension",- "version": "0.1.40",+ "version": "0.1.41", "expectedExtensionVersion": "0.1.3", "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",
bridge/package.json+1−1@@ -1,6 +1,6 @@ { "name": "adom-native-browser-bridge",- "version": "0.1.40",+ "version": "0.1.41", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+19−22@@ -1558,11 +1558,6 @@ function dispatch(verb, args) { (async () => { let curWins = []; try { curWins = await chromeWindowsDetailed(); } catch {}- // Opening a plain --new-window into a running browser just ADDS a window; it does not restart it- // (verified live on the user's own single-profile Edge). We do NOT refuse the live profile. The one- // real hazard is that the background MINIMIZE could grab the wrong window, so we compute the set of- // windows that must NEVER be minimized/closed — the workspace/editor and any OTHER thread's live- // session in this profile — and hand it to the watcher as an exclude list. That's the whole guard. const protectedHwnds = new Set(); for (const w of curWins) { const t = String(w.title || ''); if (BRAND.test(t) && WORKSPACE.test(t)) protectedHwnds.add(w.hwnd); } for (const [sid, pk] of sessionProfile) { if (pk === kp.key && sid !== a.sessionId && !osOpened.has(sid)) { const h = sessionHwnd.get(sid); if (h) protectedHwnds.add(h); } }@@ -1575,20 +1570,22 @@ function dispatch(verb, args) { const extSt = adomExtStateForProfile(kp.browser, kp.profileDir); let hwnd = null, minimized = false, flashed = false; if (a.foreground !== true) {- // BACKGROUND with a near-zero blip: one LAPTOP-SIDE script does snapshot -> launch -> watch at- // 30ms resolution -> minimize the new window the instant it exists (often before first paint).- // v1 orchestrated launch + demote from the bridge as two round-trips = a visible ~200ms flash- // of the window; doing it all in-process on the laptop shrinks that to imperceptible.- // Clean per-arg quoting via a PowerShell ARRAY (each element quoted by PS) — NOT an embedded-- // quote string. The old `--profile-directory="Default"` string passed LITERAL quotes to- // Chromium, which can break the singleton handshake and trigger a takeover/restart.- const dirArg = kp.profileDir.replace(/'/g, '');+ // OPEN like Hydrogen Desktop's browser picker (control.rs /open-in-profile), which opens native+ // browser windows reliably: a plain launch, NO `--profile-directory=Default` (only pass a+ // profile dir when it is NOT the default), and NEVER touch the window while it is being created.+ // The old code minimized the new window INSIDE a 30ms poll loop — hammering ShowWindow on a+ // browser window mid-construction, which CRASHED the whole Edge process (John, 2026-07-19). The+ // open was never the problem; the racy background call was. Fix: detect the new window read-only,+ // let it FULLY settle, then ONE gentle SW_SHOWMINNOACTIVE (minimize without stealing focus). const urlArg = url.replace(/['"]/g, '');- const argArr = `@('--profile-directory=${dirArg}','--new-window','${urlArg}')`;+ const parts = [];+ if (kp.profileDir && kp.profileDir !== 'Default') parts.push(`'--profile-directory=${kp.profileDir.replace(/'/g, '')}'`);+ parts.push("'--new-window'", `'${urlArg}'`);+ const argArr = `@(${parts.join(',')})`; const psBody = [ 'Add-Type @"', 'using System;using System.Text;using System.Collections.Generic;using System.Runtime.InteropServices;',- 'public class WWatch {',+ 'public class WW2 {', ' public delegate bool EnumProc(IntPtr h, IntPtr l);', ' [DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr l);', ' [DllImport("user32.dll")] static extern int GetClassName(IntPtr h, StringBuilder sb, int max);',@@ -1596,20 +1593,20 @@ function dispatch(verb, args) { ' public static List<long> Browsers() { var o = new List<long>(); EnumProc cb = delegate(IntPtr h, IntPtr l) { var sb = new StringBuilder(64); GetClassName(h, sb, 64); if (sb.ToString().StartsWith("Chrome_WidgetWin")) o.Add((long)h); return true; }; EnumWindows(cb, IntPtr.Zero); return o; }', '}', '"@',- // PROTECTED windows (workspace / other threads' live sessions) are treated as pre-existing AND- // are never eligible to be the "new" window we minimize - belt AND suspenders vs the incident. `$protected = @(${[...protectedHwnds].map((h) => `[long]${h}`).join(',')})`,- '$pre = [WWatch]::Browsers()',+ '$pre = [WW2]::Browsers()', 'foreach ($p in $protected) { if ($pre -notcontains $p) { $pre += $p } }', `Start-Process -FilePath "${exe}" -ArgumentList ${argArr}`,- '$deadline = (Get-Date).AddSeconds(10)',+ // DETECT ONLY — no ShowWindow in this loop. Just find the new window's hwnd.+ '$deadline = (Get-Date).AddSeconds(12)', '$found = 0', 'while ((Get-Date) -lt $deadline) {',- ' $now = [WWatch]::Browsers()',- ' foreach ($h in $now) { if (($pre -notcontains $h) -and ($protected -notcontains $h)) { [WWatch]::ShowWindow([IntPtr]$h, 6) | Out-Null; $found = $h; break } }',+ ' foreach ($h in [WW2]::Browsers()) { if (($pre -notcontains $h) -and ($protected -notcontains $h)) { $found = $h; break } }', ' if ($found -ne 0) { break }',- ' Start-Sleep -Milliseconds 30',+ ' Start-Sleep -Milliseconds 60', '}',+ // LET IT FULLY SETTLE, then ONE gentle minimize-without-activate. This is the crash-safe part.+ 'if ($found -ne 0) { Start-Sleep -Milliseconds 700; [WW2]::ShowWindow([IntPtr]$found, 7) | Out-Null }', 'Write-Output ("HWND=" + $found)', ].join(String.fromCharCode(13, 10)); const rr = await adCommand(null, 'run_script', { interpreter: 'powershell', body: psBody, timeoutSeconds: 25 });
native-browser-bridge-manifest.json+4−4@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.1.40",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.40/native-browser-bridge-v0.1.40.zip",- "sha256": "701e51b1d70497827089b43b306288ae93e1e26bd4ccad07c011b35267422639",- "size": 86423,+ "version": "0.1.41",+ "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.41/native-browser-bridge-v0.1.41.zip",+ "sha256": "349921157abf29e1d42944e59c7a6a86616e997db881ec0d5bfdfb213e3807fc",+ "size": 86212, "released_at": "2026-07-19T00:00:00Z" }