← Commit history

v0.1.5: profile-level badge correlation (profile email in OS title is stable while pages navigate)

John Lauer ·68dd557219 ·4mo ago ·parent a47006d
5 files changed +22−12
bridge/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.1.4+0.1.5
bridge/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "native-browser",   "displayName": "Adom Native Browser (Chrome + Edge)",-  "version": "0.1.4",+  "version": "0.1.5",   "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.4",+  "version": "0.1.5",   "private": true,   "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)",   "main": "server.js",
bridge/server.js+15−5
@@ -470,7 +470,9 @@ function matchHwnd(list, bounds, title) { // each window's OS hwnd by BOUNDS (profile-independent). This badges adopted SPAWNED windows AND // SELF-HEALS agent-OPENED windows: if the open-time badge landed on the wrong profile's window (the old // diff bug), a list call re-correlates by bounds, clears the STALE badge, and paints the correct one.-async function badgeSpawnsIn(payload) {+function emailOf(key) { const s = String(key || ''); const i = s.indexOf(':'); return i >= 0 ? s.slice(i + 1) : s; }+async function badgeSpawnsIn(payload, profileKey) {+  const email = emailOf(profileKey);   const spawns = []; // { windowId, bounds, title }   const owned = [];  // { sid, bounds, title }   if (payload && Array.isArray(payload.spawned)) for (const s of payload.spawned) if (s.kind === 'window' && s.windowId != null) spawns.push({ windowId: s.windowId, bounds: s.bounds, title: s.title });@@ -483,11 +485,18 @@ async function badgeSpawnsIn(payload) {   if (!fresh.length && !owned.length) return;   let list; try { list = await chromeWindowsDetailed(); } catch { return; }   for (const s of fresh) { const h = matchHwnd(list, s.bounds, s.title); if (h) { spawnHwnd.set(s.windowId, h); taskbar(h, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); } }+  // PROFILE-LEVEL badge: the OS window title for Google pages contains the profile email, and Windows+  // taskbar buttons group by profile — so badging ANY window of the driven profile lights the right+  // button. This is STABLE while pages navigate (unlike a per-window title), which is why per-window+  // correlation was flaky on the actively-driven work profile. Prefer it; fall back to per-window match.+  let profileHwnd = null;+  if (email.includes('@')) { const m = list.find((w) => (w.title || '').includes(email)); if (m) profileHwnd = m.hwnd; }   for (const o of owned) {-    const h = matchHwnd(list, o.bounds, o.title);+    const h = profileHwnd || matchHwnd(list, o.bounds, o.title);     if (!h) continue;     const old = sessionHwnd.get(o.sid);-    if (old !== h) { if (old) taskbar(old, { overlay: { badge: 'none' } }).catch(() => {}); sessionHwnd.set(o.sid, h); lastHwnd = h; clearBadge(o.sid); applyBadge(o.sid, h); }+    if (old !== h) { if (old && old !== h) taskbar(old, { overlay: { badge: 'none' } }).catch(() => {}); sessionHwnd.set(o.sid, h); lastHwnd = h; clearBadge(o.sid); applyBadge(o.sid, h); }+    else touchBadge(o.sid);   } } // A "come look" moment = a verb in COMELOOK, OR a semantic signal in the payload that the user should@@ -1239,7 +1248,7 @@ function dispatch(verb, args) {         pending.delete(id);         resolve({ success: false, error: `verb ${verb} timed out`, _hint: 'extension did not reply in time' });       }, timeoutFor(verb));-      pending.set(id, { resolve, timer, verb, toFile: !!(args && args.toFile) });+      pending.set(id, { resolve, timer, verb, toFile: !!(args && args.toFile), profileKey: tgt.key });       sock.write(JSON.stringify({ id, verb, args: args || {} }) + '\n');     };     // PIN the session to whatever profile this window opened in, so every later verb carrying this@@ -1311,6 +1320,7 @@ async function onHostFrame(frame) {             // profiles apart and badged the WRONG profile's taskbar button). Diff only as a fallback.             neu = matchHwnd(list, payload.bounds, payload.title); // title-first (DPI-safe), then bounds             if (!neu) neu = list.map((w) => w.hwnd).find((h) => !preOpenHwnds.has(h)) || null; // diff fallback (title empty while loading)+            if (!neu) { const email = emailOf(p.profileKey); if (email.includes('@')) { const m = list.find((w) => (w.title || '').includes(email)); if (m) neu = m.hwnd; } } // profile-level fallback           } catch {}           if (neu) { lastHwnd = neu; if (sid) sessionHwnd.set(sid, neu); applyBadge(sid, neu); break; } // applyBadge = badge + arm idle-expiry           await new Promise((r) => setTimeout(r, 250));@@ -1320,7 +1330,7 @@ async function onHostFrame(frame) {       }       // 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).catch(() => {}); }+      if (p.verb === 'nbrowser_owned' || p.verb === 'nbrowser_list_windows') { badgeSpawnsIn(payload, p.profileKey).catch(() => {}); }       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;
native-browser-bridge-manifest.json+4−4
@@ -1,8 +1,8 @@ {   "manifest_version": 1,-  "version": "0.1.4",-  "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.4/native-browser-bridge-v0.1.4.bin",-  "sha256": "b46af4f0432d4489a4c7ceba88c19ce3173b33f978acb5dafbbdaf3a6f28161a",-  "size": 54596,+  "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,   "released_at": "2026-07-14T00:00:00Z" }\ No newline at end of file