feat(connect): make auto-connect to user's Chrome the default behavior

- Auto-connect is now ON by default (was opt-in via --auto-connect)
- Added --launch/--new flags to explicitly start a fresh browser
- CI environments (CI env var) automatically use --launch mode
- Friendly error message with platform-specific Chrome relaunch guide
- Mentions Chrome 144+ runtime CDP toggle (chrome://inspect)
- --cdp and --provider flags implicitly disable auto-connect
- AGENT_BROWSER_NO_AUTO_CONNECT=1 to disable, AGENT_BROWSER_FORCE_LAUNCH=1 to force

Track 3 of native-stealth migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leeguooooo
2026-04-04 01:28:09 +09:00
co-authored by Claude Opus 4.6
parent 4a55e3407b
commit a821006cc0
5 changed files with 100 additions and 41 deletions
+4
View File
@@ -217,6 +217,7 @@ pub struct DaemonOptions<'a> {
pub confirm_actions: Option<&'a str>,
pub engine: Option<&'a str>,
pub auto_connect: bool,
pub force_launch: bool,
pub idle_timeout: Option<&'a str>,
pub cdp: Option<&'a str>,
pub no_auto_dialog: bool,
@@ -295,6 +296,9 @@ fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
if opts.auto_connect {
cmd.env("AGENT_BROWSER_AUTO_CONNECT", "1");
}
if opts.force_launch {
cmd.env("AGENT_BROWSER_FORCE_LAUNCH", "1");
}
if let Some(idle) = opts.idle_timeout {
cmd.env("AGENT_BROWSER_IDLE_TIMEOUT_MS", idle);
}
+15 -2
View File
@@ -285,6 +285,7 @@ pub struct Flags {
pub allow_file_access: bool,
pub device: Option<String>,
pub auto_connect: bool,
pub force_launch: bool,
pub session_name: Option<String>,
pub annotate: bool,
pub color_scheme: Option<String>,
@@ -380,8 +381,11 @@ pub fn parse_flags(args: &[String]) -> Flags {
allow_file_access: env_var_is_truthy("AGENT_BROWSER_ALLOW_FILE_ACCESS")
|| config.allow_file_access.unwrap_or(false),
device: env::var("AGENT_BROWSER_IOS_DEVICE").ok().or(config.device),
auto_connect: env_var_is_truthy("AGENT_BROWSER_AUTO_CONNECT")
|| config.auto_connect.unwrap_or(false),
auto_connect: !env_var_is_truthy("AGENT_BROWSER_NO_AUTO_CONNECT")
&& (env_var_is_truthy("AGENT_BROWSER_AUTO_CONNECT")
|| config.auto_connect.unwrap_or(true)),
force_launch: env_var_is_truthy("AGENT_BROWSER_FORCE_LAUNCH")
|| env::var("CI").is_ok(),
session_name: env::var("AGENT_BROWSER_SESSION_NAME")
.ok()
.or(config.session_name),
@@ -590,10 +594,17 @@ pub fn parse_flags(args: &[String]) -> Flags {
"--auto-connect" => {
let (val, consumed) = parse_bool_arg(args, i);
flags.auto_connect = val;
if !val {
flags.force_launch = true;
}
if consumed {
i += 1;
}
}
"--launch" | "--new" => {
flags.force_launch = true;
flags.auto_connect = false;
}
"--session-name" => {
if let Some(s) = args.get(i + 1) {
flags.session_name = Some(s.clone());
@@ -738,6 +749,8 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
"--ignore-https-errors",
"--allow-file-access",
"--auto-connect",
"--launch",
"--new",
"--annotate",
"--content-boundaries",
"--confirm-interactive",
+6 -20
View File
@@ -511,7 +511,7 @@ fn main() {
}
let args: Vec<String> = env::args().skip(1).collect();
let flags = parse_flags(&args);
let mut flags = parse_flags(&args);
let clean = clean_args(&args);
let has_help = args.iter().any(|a| a == "--help" || a == "-h");
@@ -719,6 +719,7 @@ fn main() {
confirm_actions: flags.confirm_actions.as_deref(),
engine: flags.engine.as_deref(),
auto_connect: flags.auto_connect,
force_launch: flags.force_launch,
idle_timeout: flags.idle_timeout.as_deref(),
cdp: flags.cdp.as_deref(),
no_auto_dialog: flags.no_auto_dialog,
@@ -806,24 +807,9 @@ fn main() {
exit(1);
}
if flags.auto_connect && flags.cdp.is_some() {
let msg = "Cannot use --auto-connect and --cdp together";
if flags.json {
print_json_error(msg);
} else {
eprintln!("{} {}", color::error_indicator(), msg);
}
exit(1);
}
if flags.auto_connect && flags.provider.is_some() {
let msg = "Cannot use --auto-connect and -p/--provider together";
if flags.json {
print_json_error(msg);
} else {
eprintln!("{} {}", color::error_indicator(), msg);
}
exit(1);
// Explicit --cdp or --provider disables auto-connect (they specify the connection)
if flags.cdp.is_some() || flags.provider.is_some() {
flags.auto_connect = false;
}
if flags.provider.is_some() && !flags.extensions.is_empty() {
@@ -1035,7 +1021,7 @@ fn main() {
|| !flags.extensions.is_empty())
&& flags.cdp.is_none()
&& flags.provider.is_none()
&& !flags.auto_connect
&& (flags.force_launch || !flags.auto_connect)
{
let mut launch_cmd = json!({
"id": gen_id(),
+72 -17
View File
@@ -1426,16 +1426,38 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
return Ok(());
}
if env::var("AGENT_BROWSER_AUTO_CONNECT").is_ok() {
state.reset_input_state();
state.browser = Some(connect_auto_with_fresh_tab().await?);
state.subscribe_to_browser_events();
state.start_fetch_handler();
state.start_dialog_handler();
state.update_stream_client().await;
try_auto_restore_state(state).await;
apply_stealth_to_browser(state).await;
return Ok(());
let force_launch = env::var("AGENT_BROWSER_FORCE_LAUNCH").is_ok();
// Default behavior: try to connect to the user's existing Chrome first.
// This shares cookies/sessions so the agent can reuse logged-in state.
// Skip if --launch/--new was passed or running in CI.
if env::var("AGENT_BROWSER_AUTO_CONNECT").is_ok() && !force_launch {
match connect_auto_with_fresh_tab().await {
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;
try_auto_restore_state(state).await;
apply_stealth_to_browser(state).await;
return Ok(());
}
Err(_e) => {
// Could not find a running Chrome with CDP enabled.
// Return a helpful error guiding the user to enable it.
return Err(format!(
"Could not connect to your Chrome browser.\n\n\
To let agent-browser work with your existing Chrome (recommended):\n\
{}\n\n\
Or start a standalone browser with: agent-browser --launch open <url>\n\n\
Tip: On Chrome 144+, you can enable CDP without restarting:\n\
Open chrome://inspect/#remote-debugging and toggle it on.",
chrome_relaunch_hint(),
));
}
}
}
let mgr = BrowserManager::launch(options, engine.as_deref()).await?;
@@ -1462,6 +1484,23 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
}
/// Inject stealth scripts into the active browser session.
/// Platform-specific hint for relaunching Chrome with CDP enabled.
fn chrome_relaunch_hint() -> &'static str {
if cfg!(target_os = "macos") {
" 1. Quit Chrome completely\n\
2. Run: open -a \"Google Chrome\" --args --remote-debugging-port=9222\n\
3. Then retry your agent-browser command"
} else if cfg!(target_os = "windows") {
" 1. Close Chrome completely\n\
2. Run: start chrome --remote-debugging-port=9222\n\
3. Then retry your agent-browser command"
} else {
" 1. Close Chrome completely\n\
2. Run: google-chrome --remote-debugging-port=9222\n\
3. Then retry your agent-browser command"
}
}
/// Called after every successful launch / CDP connect / auto-connect.
async fn apply_stealth_to_browser(state: &DaemonState) {
if env::var("AGENT_BROWSER_STEALTH").map(|v| v == "0").unwrap_or(false) {
@@ -1635,13 +1674,29 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
}
if auto_connect {
state.reset_input_state();
state.browser = Some(connect_auto_with_fresh_tab().await?);
state.subscribe_to_browser_events();
state.start_fetch_handler();
state.start_dialog_handler();
state.update_stream_client().await;
return Ok(json!({ "launched": true }));
match connect_auto_with_fresh_tab().await {
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;
apply_stealth_to_browser(state).await;
return Ok(json!({ "launched": true }));
}
Err(_e) => {
return Err(format!(
"Could not connect to your Chrome browser.\n\n\
To let agent-browser work with your existing Chrome (recommended):\n\
{}\n\n\
Or start a standalone browser with: agent-browser --launch open <url>\n\n\
Tip: On Chrome 144+, you can enable CDP without restarting:\n\
Open chrome://inspect/#remote-debugging and toggle it on.",
chrome_relaunch_hint(),
));
}
}
}
if let Some(provider) = cmd.get("provider").and_then(|v| v.as_str()) {
+3 -2
View File
@@ -2791,8 +2791,9 @@ Authentication:
(or AGENT_BROWSER_SESSION_NAME env)
--state <path> Load saved auth state (cookies + storage) from JSON file
(or AGENT_BROWSER_STATE env)
--auto-connect Connect to a running Chrome to reuse its auth state
Tip: agent-browser --auto-connect state save ./auth.json
--auto-connect Connect to a running Chrome (DEFAULT - shares cookies/sessions)
Tip: enable CDP via chrome://inspect/#remote-debugging
--launch, --new Launch a fresh browser instead of connecting to existing
--headers <json> HTTP headers scoped to URL's origin (e.g., Authorization bearer token)
Options: