← Commit history

v0.1.4: badge idle-expiry (auto-clears after 3min of no commands, re-applies on next drive verb)

John Lauer ·a47006d8b7 ·4mo ago ·parent b68ed45
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" }\ No newline at end of file