← Commit history
Update 5 file(s)
5 files changed
+23−12
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.16+0.1.17
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser (Chrome + Edge)",- "version": "0.1.16",+ "version": "0.1.17", "expectedExtensionVersion": "0.1.2", "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.16",+ "version": "0.1.17", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+16−5@@ -620,7 +620,10 @@ const SESS_FILE = path.join(ICON_DIR, 'sessions.json'); function persistSessions() { try { const out = {};- for (const [sid, hwnd] of sessionHwnd) out[sid] = { hwnd, profile: sessionProfile.get(sid) || null, thread: sessionThread.get(sid) || null, purpose: sessionPurpose.get(sid) || null };+ // 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, 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 {} }@@ -628,8 +631,10 @@ function restoreSessions(liveHwnds) { try { const j = JSON.parse(fs.readFileSync(SESS_FILE, 'utf8')); for (const [sid, s] of Object.entries(j)) {- if (!s || !liveHwnds.has(s.hwnd)) continue; // window is gone - drop the stale entry- if (!sessionHwnd.has(sid)) sessionHwnd.set(sid, s.hwnd);+ if (!s) continue;+ // hwnd only if that window still exists; profile pin/thread/purpose restore regardless (they+ // keep sid-keyed verbs routing to the RIGHT profile across our restart).+ if (s.hwnd && liveHwnds.has(s.hwnd) && !sessionHwnd.has(sid)) sessionHwnd.set(sid, s.hwnd); 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);@@ -686,7 +691,13 @@ function touchBadge(sid, profileKey) { // sid->hwnd map is persisted + restored at startup, and profile-keyed drives re-correlate via the // profile's OWN extension (touchProfileBadge -> badgeSpawnsIn), which cannot hit a PWA. const h = sessionHwnd.get(sid);- if (!h) return;+ if (!h) {+ // Self-heal: no hwnd for an ACTIVELY-DRIVEN session (e.g. it opened before a bridge restart that+ // predates sessions.json). Ask the profile's own extension for its windows (throttled) and repair.+ const pk = profileKey || sessionProfile.get(sid);+ if (pk) { const now = Date.now(); if ((_profCorrAt.get(pk) || 0) + 30000 <= now) { _profCorrAt.set(pk, now); extQuery(pk, 'nbrowser_list_windows', {}).then((r) => { let pl = r; if (r && typeof r.output === 'string') { try { pl = JSON.parse(r.output); } catch { pl = null; } } if (pl) badgeSpawnsIn(pl, pk).catch(() => {}); }); } }+ return;+ } if (!badgeOn.has(sid)) applyBadge(sid, h, profileKey); else armBadgeIdle(sid); } function clearBadge(sid) { const t = badgeIdle.get(sid); if (t) clearTimeout(t); badgeIdle.delete(sid); badgeOn.delete(sid); }@@ -1536,7 +1547,7 @@ function dispatch(verb, args) { }; // PIN the session to whatever profile this window opened in, so every later verb carrying this // sessionId auto-routes back to the SAME profile (no cross-profile mistakes) even without `profile:`.- if (verb === 'nbrowser_open_window' && args && args.sessionId) { sessionProfile.set(args.sessionId, tgt.key); if (args.thread) sessionThread.set(args.sessionId, args.thread); if (args.purpose) sessionPurpose.set(args.sessionId, args.purpose); }+ if (verb === 'nbrowser_open_window' && args && args.sessionId) { sessionProfile.set(args.sessionId, tgt.key); if (args.thread) sessionThread.set(args.sessionId, args.thread); if (args.purpose) sessionPurpose.set(args.sessionId, args.purpose); persistSessions(); } // For open_window, snapshot the Chrome window list BEFORE forwarding so the post-open diff reliably // catches the new window (the old fire-and-forget snapshot raced the window appearing). if (verb === 'nbrowser_open_window') chromeHwnds().then((s) => { preOpenHwnds = s; forward(); }).catch(forward);
native-browser-bridge-manifest.json+4−4@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.1.16",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.16/native-browser-bridge-v0.1.16.bin",- "sha256": "07c4460b391a11b16c8b8ff0e95b69ca95242b5b5cfe2a8915366a9374659737",- "size": 70617,+ "version": "0.1.17",+ "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.17/native-browser-bridge-v0.1.17.bin",+ "sha256": "6faae072cef04f7042669f1f9f99c72d70ae0768da5c31bdba122ad2c1999059",+ "size": 70919, "released_at": "2026-07-15T00:00:00Z" }