← Commit history
Update 5 file(s)
5 files changed
+29−9
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.32+0.1.33
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Browser Extension",- "version": "0.1.32",+ "version": "0.1.33", "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.32",+ "version": "0.1.33", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+22−2@@ -698,7 +698,7 @@ function persistSessions() { const out = {}; // Persist every pinned session (profile routing matters across restarts even before an hwnd is // captured) plus every hwnd-mapped one.- for (const [sid, profile] of sessionProfile) out[sid] = { hwnd: sessionHwnd.get(sid) || null, profile, thread: sessionThread.get(sid) || null, purpose: sessionPurpose.get(sid) || null };+ for (const [sid, profile] of sessionProfile) out[sid] = { hwnd: sessionHwnd.get(sid) || null, profile, thread: sessionThread.get(sid) || null, purpose: sessionPurpose.get(sid) || null, os: osOpened.has(sid) || undefined }; for (const [sid, hwnd] of sessionHwnd) if (!out[sid]) out[sid] = { hwnd, profile: sessionProfile.get(sid) || null, thread: sessionThread.get(sid) || null, purpose: sessionPurpose.get(sid) || null }; fs.writeFileSync(SESS_FILE, JSON.stringify(out)); } catch {}@@ -714,6 +714,7 @@ function restoreSessions(liveHwnds) { if (s.profile && !sessionProfile.has(sid)) sessionProfile.set(sid, s.profile); if (s.thread && !sessionThread.has(sid)) sessionThread.set(sid, s.thread); if (s.purpose && !sessionPurpose.has(sid)) sessionPurpose.set(sid, s.purpose);+ if (s.os) osOpened.add(sid); } persistSessions(); } catch {}@@ -741,6 +742,7 @@ function extQuery(profileKey, verb, args, timeoutMs) { // hit another profile's window or a PWA. const _profCorrAt = new Map(); // profileKey -> last correlation attempt ts const _osCloseTickets = new Map(); // OS-level force_close: ticket -> {hwnd, expires}+const osOpened = new Set(); // sessionIds created by nbrowser_open_os_window (no extension session behind them) function touchProfileBadge(profileKey) { if (!BADGE_ENABLED || !profileKey) return; let touched = false;@@ -1577,7 +1579,7 @@ function dispatch(verb, args) { try { await taskbar(hwnd, { flash: 'until_focused' }); flashed = true; } catch {} } logActivity({ verb: 'nbrowser_open_os_window', sessionId: a.sessionId, thread: a.thread, profile: kp.key, ok: !!hwnd, errorCode: hwnd ? null : 'hwnd_not_found' });- sessionThread.set(a.sessionId, a.thread); sessionPurpose.set(a.sessionId, a.purpose); sessionProfile.set(a.sessionId, kp.key); if (hwnd) sessionHwnd.set(a.sessionId, hwnd); persistSessions();+ sessionThread.set(a.sessionId, a.thread); sessionPurpose.set(a.sessionId, a.purpose); sessionProfile.set(a.sessionId, kp.key); if (hwnd) sessionHwnd.set(a.sessionId, hwnd); osOpened.add(a.sessionId); persistSessions(); const upsell = extSt && extSt.installed ? `NOTE: the Adom extension IS installed in this profile${extSt.enabled ? '' : ' (but DISABLED)'} - you could have FULL control instead: nbrowser_wake_profile then nbrowser_open_window gives you navigation, reading, clicking, screenshots, and ownership guards. Prefer that path.` : `UPSELL (tell the user when the moment is right): this window is OPEN but I cannot see or drive it - the Adom extension is not installed in this profile. With ABE installed (see installing-the-extension - it can be installed FOR the user in ~a minute) the assistant could fill the form, read the result, screenshot progress, and clean up after itself. Until then the USER must do the clicking.`;@@ -1805,6 +1807,24 @@ function dispatch(verb, args) { if (verb === 'nbrowser_close') { return resolve({ success: false, error: 'nbrowser_close is not an extension verb', _hint: 'Close a specific window with nbrowser_close_window {sessionId} or a tab with nbrowser_close_tab {sessionId,tabId}. There is no global "detach all" - the extension idle-detaches the debugger automatically when no verb is running.' }); }+ // First-party close for OS-OPENED sessions (nbrowser_open_os_window): no extension session exists+ // behind them, so close_window is handled bridge-side with a graceful WM_CLOSE. force_close correctly+ // refuses these (they ARE owned - by the session that opened them); this is that owner's close path.+ if (verb === 'nbrowser_close_window' && args && args.sessionId && osOpened.has(args.sessionId)) {+ const sid = args.sessionId; const h = sessionHwnd.get(sid);+ (async () => {+ let closed = false;+ if (h) {+ const psBody = ['Add-Type @"','using System;using System.Runtime.InteropServices;','public class WMC3 { [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr h, uint m, IntPtr w, IntPtr l); }','"@',`[WMC3]::PostMessage([IntPtr]${h},0x0010,[IntPtr]::Zero,[IntPtr]::Zero)`].join(String.fromCharCode(13,10));+ const rr = await adCommand(null, 'run_script', { interpreter: 'powershell', body: psBody, timeoutSeconds: 15 });+ closed = !!(rr && rr.body && (rr.body.success === true || rr.body.ok === true));+ }+ sessionHwnd.delete(sid); osOpened.delete(sid); clearBadge(sid); persistSessions();+ logActivity({ verb: 'nbrowser_close_window', sessionId: sid, thread: sessionThread.get(sid) || null, profile: sessionProfile.get(sid) || null, ok: closed, errorCode: closed ? null : (h ? 'wm_close_failed' : 'no_hwnd') });+ resolve({ success: true, output: JSON.stringify({ ok: true, closed, osLevel: true, hwnd: h || null, _hint: closed ? 'OS-opened window closed (graceful WM_CLOSE) and the session cleaned up.' : 'No live hwnd for this OS-opened session (window may already be closed) - session cleaned up.' }) });+ })();+ return;+ } // nbrowser_set_file_input pre-flight (the bridge lives ON the laptop, so it can stat + stage): // - files:[{name, contentBase64}] -> write to a janitored temp dir and substitute filePaths, so a // cloud agent uploads small files in ONE call (no send_files round-trip);
native-browser-bridge-manifest.json+4−4@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.1.32",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.32/native-browser-bridge-v0.1.32.zip",- "sha256": "884f1d44801becf3eb0b21bdd84894ae7b1b1d101096a2b63c6d143c7c4f8edb",- "size": 83965,+ "version": "0.1.33",+ "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.33/native-browser-bridge-v0.1.33.zip",+ "sha256": "5659d68fe707bebbfe350298117df10611cbf1bdbe42c15b2b35b473ed9ef285",+ "size": 84376, "released_at": "2026-07-19T00:00:00Z" }