diff --git a/extensions/ab-connect.crx b/extensions/ab-connect.crx index 3561590..c54c221 100644 Binary files a/extensions/ab-connect.crx and b/extensions/ab-connect.crx differ diff --git a/extensions/ab-connect.zip b/extensions/ab-connect.zip index 77ef0cd..8bbf487 100644 Binary files a/extensions/ab-connect.zip and b/extensions/ab-connect.zip differ diff --git a/extensions/ab-connect/background.js b/extensions/ab-connect/background.js index d7b314c..e519090 100644 --- a/extensions/ab-connect/background.js +++ b/extensions/ab-connect/background.js @@ -21,6 +21,9 @@ const SKIP_URL = /^(chrome|chrome-extension|devtools|chrome-untrusted|edge|about /** @type {chrome.runtime.Port|null} */ let port = null +/** Whether the native-messaging host (the local agent-browser CLI) is linked. + * Read by the popup status page. */ +let hostConnected = false let nextSession = 1 /** tabId -> { sessionId, targetId } */ const tabs = new Map() @@ -92,13 +95,16 @@ function connectHost() { if (port) return try { port = chrome.runtime.connectNative(HOST_NAME) + hostConnected = true } catch (e) { port = null + hostConnected = false return } port.onMessage.addListener((msg) => void whenReady(() => onHostMessage(msg))) port.onDisconnect.addListener(() => { port = null + hostConnected = false // Sessions are stale once the host is gone; the daemon re-discovers on // reconnect. Keep chrome.debugger attached so reconnect is cheap. for (const tabId of tabs.keys()) setBadge(tabId, 'connecting') @@ -343,7 +349,18 @@ chrome.tabs.onRemoved.addListener((tabId) => void whenReady(() => detachTab(tabI chrome.runtime.onInstalled.addListener(() => void whenReady(connectHost)) chrome.runtime.onStartup.addListener(() => void whenReady(connectHost)) -chrome.action.onClicked.addListener(() => void whenReady(connectHost)) + +// Popup status page asks for the live pairing state. Attempt a (re)connect on +// demand so opening the popup also nudges the link awake, then report. +chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { + if (msg && msg.type === 'ab-status') { + if (!port) { + try { connectHost() } catch (e) {} + } + sendResponse({ connected: hostConnected, tabCount: tabs.size, host: HOST_NAME }) + } + return true +}) // MV3 service workers get suspended; an alarm wakes us to keep the host link // and badges fresh. diff --git a/extensions/ab-connect/manifest.json b/extensions/ab-connect/manifest.json index aca03e4..74ed496 100644 --- a/extensions/ab-connect/manifest.json +++ b/extensions/ab-connect/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 3, "name": "agent-browser-stealth", - "version": "0.4.0", - "description": "Let agent-browser drive your logged-in Chrome — install once, no token, no per-use confirmation.", + "version": "0.4.1", + "description": "Let agent-browser drive your logged-in Chrome \u2014 install once, no token, no per-use confirmation.", "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6vQIyscGIPYPZdSpPwPL0+0gxUROyRgCpmvCSDoc8XUm4qm97VbKnD9Ijc1lV22lNWZtE78gaRjt6BeSfuMgnBymnhLKjN1gU6AI5QUU0mrJyeHdWKvrKQR5FmsM2A7Xr1ykE2SiiS8zNUS3Y/6O5l+Nva7wrVy6E4a2dkBVQkOsu+DV+nEZvhIyuDY5D5SPXqNwUTWTaglwj5mjvHz36xSwCWlPmrtJ+ED0AUyrb2z4GIOmvk4kqtBVrh/UD058klLo4CkYOnIybB5aV6WYuwarfPY4bF/dLggPem+ewLNTUNBuwrxj/A4nUv0LJTuRO8rR7f8WR9qnRCY0Ic5saQIDAQAB", "icons": { "16": "icons/icon16.png", @@ -24,6 +24,7 @@ "type": "module" }, "action": { - "default_title": "agent-browser-stealth" + "default_title": "agent-browser-stealth", + "default_popup": "popup.html" } } diff --git a/extensions/ab-connect/popup.html b/extensions/ab-connect/popup.html new file mode 100644 index 0000000..1fa300e --- /dev/null +++ b/extensions/ab-connect/popup.html @@ -0,0 +1,126 @@ + + + + + + + +
+ +
+
agent-browser-stealth
+
local automation bridge
+
+
+ +
+
+ +
+
Checking…
+
contacting the local CLI
+
+
+ +

+ Lets your locally-installed agent-browser command-line tool + drive your own logged-in Chrome tabs — entirely on this machine, only when + you run a command. No remote server, no data collection. +

+ +
+ Not linked yet. Install & pair the CLI, then reopen this popup: + agent-browser extension install +
+
+ + + + + + diff --git a/extensions/ab-connect/popup.js b/extensions/ab-connect/popup.js new file mode 100644 index 0000000..76b4234 --- /dev/null +++ b/extensions/ab-connect/popup.js @@ -0,0 +1,64 @@ +// Popup status page for agent-browser-stealth. +// Asks the service worker whether the native-messaging link to the local +// agent-browser CLI is live, and renders a paired / not-paired indicator. + +const dot = document.getElementById('dot') +const label = document.getElementById('statusLabel') +const sub = document.getElementById('statusSub') +const hint = document.getElementById('hint') + +let resolved = false + +function render(state) { + resolved = true + const connected = !!(state && state.connected) + dot.classList.remove('on', 'off') + if (connected) { + dot.classList.add('on') + label.textContent = 'Connected' + const n = state.tabCount | 0 + sub.textContent = + n > 0 + ? `bridged to the local CLI · ${n} tab${n === 1 ? '' : 's'} attached` + : 'bridged to the local CLI · ready' + hint.style.display = 'none' + } else { + dot.classList.add('off') + label.textContent = 'Not paired' + sub.textContent = 'no local agent-browser CLI linked' + hint.style.display = 'block' + } +} + +function queryStatus() { + try { + chrome.runtime.sendMessage({ type: 'ab-status' }, (resp) => { + // lastError fires if the service worker can't be reached. + if (chrome.runtime.lastError) { + render({ connected: false }) + return + } + render(resp) + }) + } catch (e) { + render({ connected: false }) + } +} + +// Open the repo in a real tab (no inline handlers under MV3 CSP). +const repo = document.getElementById('repo') +if (repo) { + repo.addEventListener('click', () => { + chrome.tabs.create({ url: repo.dataset.href }) + }) +} + +// Query now, then once more shortly after — opening the popup also nudges the +// service worker to (re)connect the host, which may complete a beat later. +queryStatus() +setTimeout(queryStatus, 700) + +// Never leave the popup stuck on "Checking…" if the worker never answers. +setTimeout(() => { + if (!resolved) render({ connected: false }) +}, 1500)