← Commit history
v0.1.4: badge idle-expiry (auto-clears after 3min of no commands, re-applies on next drive verb)
5 files changed
+25−9
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.3+0.1.4
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Native Browser (Chrome + Edge)",- "version": "0.1.3",+ "version": "0.1.4", "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.3",+ "version": "0.1.4", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+18−2@@ -424,6 +424,20 @@ function taskbar(hwnd, opts) { return adCommand('desktop', 'desktop_taskbar', { hwnd, ...opts }).catch(() => {}); } function flashHwnd(hwnd, stop) { taskbar(hwnd, { flash: stop ? 'stop' : 'until_focused' }); }++// ── BADGE IDLE-EXPIRY (John, 2026-07-14): the "Adom is driving" badge should reflect ACTIVE driving. If+// the AI stops sending commands to a window for a while, the badge auto-clears (so it doesn't imply the AI+// is still driving when it's idle/done); it re-appears on the next command. Re-touched on every drive verb.+const BADGE_IDLE_MS = 180000; // 3 min of no commands → badge fades+const badgeIdle = new Map(); // sessionId -> clear timer+const badgeOn = new Set(); // sessionIds currently badged+function armBadgeIdle(sid) {+ const prev = badgeIdle.get(sid); if (prev) clearTimeout(prev);+ 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); } // 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();@@ -473,7 +487,7 @@ async function badgeSpawnsIn(payload) { const h = 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; taskbar(h, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); }+ 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); } } } // A "come look" moment = a verb in COMELOOK, OR a semantic signal in the payload that the user should@@ -1298,7 +1312,7 @@ async function onHostFrame(frame) { 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) } catch {}- if (neu) { lastHwnd = neu; if (sid) sessionHwnd.set(sid, neu); taskbar(neu, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); break; }+ 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)); } payload = { ...payload, ...(neu ? { hwnd: neu } : {}), _taskbarHint: flashHint(),@@ -1311,12 +1325,14 @@ async function onHostFrame(frame) { const sid = payload.sessionId; const h = (sid && sessionHwnd.get(sid)) || lastHwnd; if (h && p.verb === 'nbrowser_close_window') { taskbar(h, { progress: { state: 'none' }, overlay: { badge: 'none' }, flash: 'stop' }); if (sid) sessionHwnd.delete(sid); }+ if (sid) clearBadge(sid); // stop the idle-expiry timer for a closed session // clear the badge on any spawned windows this close/cleanup removed for (const wid of (payload.alsoClosedSpawned || []).concat((payload.closed || []).map((c) => c.windowId).filter(Boolean))) { const sh = spawnHwnd.get(wid); if (sh) { taskbar(sh, { overlay: { badge: 'none' }, flash: 'stop' }); spawnHwnd.delete(wid); } } } 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 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.3",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.3/native-browser-bridge-v0.1.3.bin",- "sha256": "10124cf825d36e6c734c607b238d89705ddf4446b6d1b72f8f6886ae3cd3b4ec",- "size": 54092,+ "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, "released_at": "2026-07-14T00:00:00Z" }