feat(connect): attach existing tabs + extension connect one-command UX
Completes the zero-confirmation real-Chrome feature.
- Drive the user's EXISTING logged-in tabs (not just newly-created ones):
extension attachTab now treats "already attached" (a lingering chrome.debugger
binding after a service-worker restart) as success and announces the tab
anyway, instead of skipping it. The nm-host also sends {method:"attachAll"}
when an agent-browser CDP client connects, so the daemon doesn't race an empty
target list.
- `agent-browser extension connect` auto-discovers the relay's CDP url
(~/.agent-browser/relay-cdp-url) and attaches — no copying a ws URL. Rewrites
into the normal `connect <url>` flow; `extension install/status/uninstall`
unchanged.
- Skill docs: a "drive your real, logged-in Chrome (extension)" section.
Verified end-to-end: `extension connect` listed the user's real tabs (Lark,
LINUX DO, Rakuten, Discord) and read a logged-in Lark doc's title — zero token,
zero confirmation. Full suite 768 passed.
This commit is contained in:
@@ -76,6 +76,13 @@ async function onHostMessage(msg) {
|
||||
postToHost({ method: 'pong' })
|
||||
return
|
||||
}
|
||||
// Daemon (re)connected — (re)attach and announce every tab so it discovers
|
||||
// the user's existing tabs rather than racing an empty target list.
|
||||
if (msg.method === 'attachAll') {
|
||||
reannounceAttachedTabs()
|
||||
await attachAllTabs()
|
||||
return
|
||||
}
|
||||
if (typeof msg.id !== 'undefined' && msg.method === 'forwardCDPCommand') {
|
||||
try {
|
||||
const result = await handleForwardCdpCommand(msg)
|
||||
@@ -163,7 +170,18 @@ async function attachTab(tabId) {
|
||||
const existing = tabs.get(tabId)
|
||||
if (existing) return existing
|
||||
const dbg = { tabId }
|
||||
await chrome.debugger.attach(dbg, '1.3')
|
||||
try {
|
||||
await chrome.debugger.attach(dbg, '1.3')
|
||||
} catch (e) {
|
||||
// After a service-worker restart, chrome.debugger may still be bound to
|
||||
// this tab from the previous instance — "Another debugger is already
|
||||
// attached". The tab is still controllable via {tabId}, so don't skip it
|
||||
// (skipping is why existing tabs went un-announced and the daemon opened a
|
||||
// blank tab instead). Re-announce it. Any other error (restricted page) is
|
||||
// surfaced and the caller skips this tab.
|
||||
const msg = String((e && e.message) || e)
|
||||
if (!/already attached|already being debugged/i.test(msg)) throw e
|
||||
}
|
||||
await chrome.debugger.sendCommand(dbg, 'Page.enable').catch(() => {})
|
||||
const info = /** @type {any} */ (await chrome.debugger.sendCommand(dbg, 'Target.getTargetInfo'))
|
||||
const targetInfo = info?.targetInfo
|
||||
|
||||
Reference in New Issue
Block a user