From b6b2ca56caf29cf2601495925af4aedb1f1f05c9 Mon Sep 17 00:00:00 2001 From: leeguooooo Date: Thu, 11 Jun 2026 21:17:02 +0900 Subject: [PATCH] fix(connect): never fall back to the consent-dialog raw port when the extension is installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the recurring "Allow remote debugging?" dialog: when the ab-connect relay was momentarily down (MV3 service worker drops the relay-url file across a Chrome restart / idle wake), auto_connect_cdp silently fell through to the raw :9222 DevToolsActivePort path — which pops Chrome 136+'s consent modal, the exact thing the extension exists to avoid. Even a relay-aware build hit this if it connected during the blip. Fix: if the native-messaging host is installed (connect::host_installed() — the durable signal that the user chose the extension path), auto_connect retries the relay for ~5s while the SW reconnects, and then ERRORS with an actionable message instead of attaching to a raw debug port. The raw :9222 path now runs only when no extension is set up (where the dialog is expected). `--cdp ` still forces the raw path explicitly. --- cli/src/connect.rs | 13 ++++++++++++ cli/src/native/cdp/chrome.rs | 40 +++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/cli/src/connect.rs b/cli/src/connect.rs index 499c83d..d53c17e 100644 --- a/cli/src/connect.rs +++ b/cli/src/connect.rs @@ -344,6 +344,19 @@ fn host_manifest_path_for_chrome() -> Option { }) } +/// True if the ab-connect native-messaging host manifest is present — i.e. the +/// user has set up the extension path. When installed, auto-connect treats the +/// dialog-free extension relay as the *intended* transport and refuses to fall +/// back to a raw debug port (which would pop Chrome 136+'s "Allow remote +/// debugging?" consent modal). The relay-url file comes and goes with the +/// service worker; this manifest is the durable signal that the extension is +/// the chosen path. +pub fn host_installed() -> bool { + native_messaging_dirs() + .into_iter() + .any(|d| d.join(format!("{HOST_NAME}.json")).exists()) +} + fn report(json: bool, ok: bool, msg: &str) { if json { println!( diff --git a/cli/src/native/cdp/chrome.rs b/cli/src/native/cdp/chrome.rs index 24aec9c..936e227 100644 --- a/cli/src/native/cdp/chrome.rs +++ b/cli/src/native/cdp/chrome.rs @@ -783,14 +783,40 @@ pub async fn auto_connect_cdp() -> Result { // / :9222 probes below: if the user's Chrome happens to also be listening on a // debug port, attaching there would pop the consent dialog and defeat the // whole zero-interaction extension path. - if let Some(relay) = crate::connect::relay_url() { - // The relay is a local CDP-over-WS endpoint we connect to like Chrome. - // A bare TCP liveness check (no WS upgrade) confirms it is actually - // accepting before we commit, mirroring the consent-free probe used for - // DevToolsActivePort. - if relay_is_live(&relay).await { - return Ok(relay); + // If the extension is installed, it is the *intended* transport. The relay + // URL file comes and goes with the MV3 service worker (a Chrome restart or an + // idle SW briefly drops it), so a single failed probe doesn't mean "no + // extension" — retry for a few seconds while it reconnects. Crucially, when + // the extension is set up we must NEVER fall through to the raw :9222 path + // below: that pops Chrome 136+'s "Allow remote debugging?" dialog, the exact + // thing the extension exists to avoid. + let host_installed = crate::connect::host_installed(); + let relay_attempts = if host_installed { 10 } else { 1 }; + for attempt in 0..relay_attempts { + if let Some(relay) = crate::connect::relay_url() { + // The relay is a local CDP-over-WS endpoint we connect to like Chrome. + // A bare TCP liveness check (no WS upgrade) confirms it is actually + // accepting before we commit, mirroring the consent-free probe used + // for DevToolsActivePort. + if relay_is_live(&relay).await { + return Ok(relay); + } } + if host_installed && attempt + 1 < relay_attempts { + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + } + } + + if host_installed { + return Err( + "The agent-browser-stealth extension is installed, but its relay \ + isn't connected right now. Wake it up — click the extension's \ + toolbar icon, or reload it at chrome://extensions — then retry. \ + (agent-browser will not attach to a raw --remote-debugging-port \ + while the extension is set up, because that pops Chrome's \"Allow \ + remote debugging?\" dialog. Use --cdp to force the raw path.)" + .to_string(), + ); } let user_data_dirs = get_chrome_user_data_dirs();