← Commit history
v0.1.6: badge tracks ACTIVE driving (lazy-correlate on drive verbs + idle-expiry), not ownership - fixes stale badge on an idle owned window
5 files changed
+31−13
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.5+0.1.6
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser (Chrome + Edge)",- "version": "0.1.5",+ "version": "0.1.6", "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.5",+ "version": "0.1.6", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+24−6@@ -436,8 +436,26 @@ function armBadgeIdle(sid) { badgeIdle.set(sid, setTimeout(() => { const h = sessionHwnd.get(sid); if (h) taskbar(h, { overlay: { badge: 'none' } }).catch(() => {}); badgeOn.delete(sid); badgeIdle.delete(sid); }, BADGE_IDLE_MS)); } function applyBadge(sid, hwnd) { if (!sid || !hwnd) return; taskbar(hwnd, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); badgeOn.add(sid); armBadgeIdle(sid); }-function touchBadge(sid) { if (!sid) return; const h = sessionHwnd.get(sid); if (!h) return; if (!badgeOn.has(sid)) applyBadge(sid, h); else armBadgeIdle(sid); }-function clearBadge(sid) { const t = badgeIdle.get(sid); if (t) clearTimeout(t); badgeIdle.delete(sid); badgeOn.delete(sid); }+// Called on every DRIVE verb: the badge tracks ACTIVE driving, not mere ownership. On the first activity+// (or after a bridge restart) it lazily correlates the driven profile's taskbar window; then it just+// re-arms the idle timer. An IDLE owned window (a thread opened it then went quiet) is NEVER badged here,+// so it fades and stays faded — fixing the "personal profile badged when nothing's driving it" bug.+const badgeTry = new Map(); // sid -> last correlate-attempt ts (throttles the desktop_list_windows call)+async function touchBadge(sid, profileKey) {+ if (!sid) return;+ let h = sessionHwnd.get(sid);+ if (!h) {+ const last = badgeTry.get(sid) || 0;+ if (Date.now() - last < 8000) return; // throttle correlation attempts+ badgeTry.set(sid, Date.now());+ const email = emailOf(profileKey);+ if (!email.includes('@')) return;+ try { const list = await chromeWindowsDetailed(); const m = list.find((w) => (w.title || '').includes(email)); if (m) { h = m.hwnd; sessionHwnd.set(sid, h); } } catch {}+ if (!h) return;+ }+ if (!badgeOn.has(sid)) applyBadge(sid, h); else armBadgeIdle(sid);+}+function clearBadge(sid) { const t = badgeIdle.get(sid); if (t) clearTimeout(t); badgeIdle.delete(sid); badgeOn.delete(sid); badgeTry.delete(sid); } // Capture the driven window's hwnd by DIFFING the real-Chrome window list across the open (the new // hwnd is the one we opened). Robust vs Windows foreground-lock - no GetForegroundWindow guessing. let preOpenHwnds = new Set();@@ -1328,9 +1346,9 @@ async function onHostFrame(frame) { payload = { ...payload, ...(neu ? { hwnd: neu } : {}), _taskbarHint: flashHint(), _screenshotHint: 'To SEE this window without foregrounding it: nbrowser_screenshot {sessionId} = the PAGE bitmap (fast, default). To see the WINDOW FRAME or a native popup/dialog (Chrome password dropdown, print dialog, alert/confirm), use AD desktop_screenshot_window {hwnd:' + (neu || '<from desktop_list_windows by title>') + '} - it captures the window + its OWNED CHILD windows (parent/child hwnds). NEVER a fullscreen/monitor screenshot to inspect a browser window.' }; }- // Badge any adopted SPAWNED windows surfaced by owned/list_windows (they have their own hwnd, no- // sessionId, so we correlate by bounds on-demand — there is no push channel).- if (p.verb === 'nbrowser_owned' || p.verb === 'nbrowser_list_windows') { badgeSpawnsIn(payload, p.profileKey).catch(() => {}); }+ // NOTE: list_windows/owned deliberately do NOT badge here anymore — badging an owned-but-IDLE window+ // is what left a "driving" badge on a profile no thread was actually driving. The badge is applied+ // ONLY by active driving (touchBadge, below) + open_window, and idle-expires on its own. if (p.verb === 'nbrowser_close_window' || p.verb === 'nbrowser_cleanup') { // clean up the taskbar when we stop driving const sid = payload.sessionId; const h = (sid && sessionHwnd.get(sid)) || lastHwnd;@@ -1342,7 +1360,7 @@ async function onHostFrame(frame) { } } maybeAutoFlash(p.verb, payload);- if (payload && payload.sessionId && ACTIVITY.has(p.verb)) touchBadge(payload.sessionId); // keep the badge alive while actively driving; it idle-expires when commands stop+ if (payload && payload.sessionId && ACTIVITY.has(p.verb)) touchBadge(payload.sessionId, p.profileKey).catch(() => {}); // badge = ACTIVE driving; lazily correlates, idle-expires when commands stop resolve_ok(p.resolve, payload); } else p.resolve({ success: false, error: frame.error || 'unknown error', _hint: frame._hint }); return;
native-browser-bridge-manifest.json+4−4@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.1.5",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.5/native-browser-bridge-v0.1.5.bin",- "sha256": "37900c24ec1d583f201f74577c41523c0864cf4fa7bc4025e907eee80767edad",- "size": 54953,+ "version": "0.1.6",+ "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.6/native-browser-bridge-v0.1.6.bin",+ "sha256": "0d8f72596f87f42c9d8b42797d6c0fc07838c3722bafe17d0b227c24f8a3f105",+ "size": 55297, "released_at": "2026-07-14T00:00:00Z" }