fix(native): auto_launch() honours AGENT_BROWSER_PROVIDER for cloud providers (#1126)

When a non-launch command (e.g. open, snapshot) triggers auto_launch()
before the explicit launch command is processed, auto_launch() now checks
AGENT_BROWSER_PROVIDER and connects via the provider API instead of
always falling back to a local Chrome instance.

Also redirects daemon stderr to /dev/null when not in debug mode to
prevent crashes from broken pipe after the CLI drops the piped stderr
handle. Cloud providers may write to stderr during connection setup.

Fixes #1125
Related: #979
This commit is contained in:
Hung-Che Lo
2026-04-04 10:29:04 -05:00
committed by GitHub
parent c52d25d576
commit 4b5ba9f245
2 changed files with 58 additions and 0 deletions
+42
View File
@@ -1481,6 +1481,48 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
return Ok(());
}
// Cloud provider: when AGENT_BROWSER_PROVIDER is set, connect via the
// provider API instead of launching a local Chrome instance. This mirrors
// the logic in handle_launch() so that auto_launch (triggered by any
// command arriving before an explicit "launch") honours the provider env.
if let Ok(provider) = env::var("AGENT_BROWSER_PROVIDER") {
let p = provider.to_lowercase();
// ios/safari are device providers handled via explicit launch command
if !p.is_empty() && p != "ios" && p != "safari" {
let conn = providers::connect_provider(&p).await?;
let ws_headers = if p == "agentcore" {
providers::take_agentcore_ws_headers()
} else {
None
};
let connect_result = if conn.direct_page {
BrowserManager::connect_cdp_direct(&conn.ws_url).await
} else if ws_headers.is_some() {
BrowserManager::connect_cdp_with_headers(&conn.ws_url, ws_headers).await
} else {
BrowserManager::connect_cdp(&conn.ws_url).await
};
match connect_result {
Ok(mgr) => {
state.reset_input_state();
state.browser = Some(mgr);
state.subscribe_to_browser_events();
state.start_fetch_handler();
state.start_dialog_handler();
state.update_stream_client().await;
write_provider_file(&state.session_id, &p);
try_auto_restore_state(state).await;
return Ok(());
}
Err(e) => {
if let Some(ref ps) = conn.session {
providers::close_provider_session(ps).await;
}
return Err(format!("Provider '{}' connection failed: {}", p, e));
}
}
}
}
let mgr = BrowserManager::launch(options, engine.as_deref()).await?;
state.reset_input_state();
state.browser = Some(mgr);
+16
View File
@@ -41,6 +41,22 @@ pub async fn run_daemon(session: &str) {
session
);
}
} else {
// Redirect stderr to /dev/null to prevent daemon crash when the
// parent CLI drops the piped stderr handle after startup. Cloud
// providers (AgentCore, Browserbase, etc.) may write to stderr
// during connection setup; a broken pipe would kill the daemon.
#[cfg(unix)]
{
use std::os::unix::io::IntoRawFd;
if let Ok(devnull) = fs::File::create("/dev/null") {
let fd = devnull.into_raw_fd();
unsafe {
libc::dup2(fd, 2);
libc::close(fd);
}
}
}
}
let pid_path = socket_dir.join(format!("{}.pid", session));