Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ This is useful for multimodal AI models that can reason about visual layout, unl
| `--executable-path <path>` | Custom browser executable (or `AGENT_BROWSER_EXECUTABLE_PATH` env) |
| `--extension <path>` | Load browser extension (repeatable; or `AGENT_BROWSER_EXTENSIONS` env) |
| `--args <args>` | Browser launch args, comma or newline separated (or `AGENT_BROWSER_ARGS` env) |
| `--ignore-default-args <list>` | Remove specific default Chrome flags, comma-separated (or `AGENT_BROWSER_IGNORE_DEFAULT_ARGS` env) |
| `--user-agent <ua>` | Custom User-Agent string (or `AGENT_BROWSER_USER_AGENT` env) |
| `--proxy <url>` | Proxy server URL with optional auth (or `AGENT_BROWSER_PROXY` env) |
| `--proxy-bypass <hosts>` | Hosts to bypass proxy (or `AGENT_BROWSER_PROXY_BYPASS` env) |
Expand Down
2 changes: 2 additions & 0 deletions cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2366,6 +2366,8 @@ mod tests {
cli_annotate: false,
cli_download_path: false,
cli_headed: false,
cli_ignore_default_args: false,
ignore_default_args: None,
annotate: false,
color_scheme: None,
download_path: None,
Expand Down
6 changes: 6 additions & 0 deletions cli/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ pub struct DaemonOptions<'a> {
pub default_timeout: Option<u64>,
pub cdp: Option<&'a str>,
pub no_auto_dialog: bool,
pub ignore_default_args: Option<&'a [String]>,
}

fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
Expand Down Expand Up @@ -314,6 +315,11 @@ fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
if opts.no_auto_dialog {
cmd.env("AGENT_BROWSER_NO_AUTO_DIALOG", "1");
}
if let Some(ida) = opts.ignore_default_args {
if !ida.is_empty() {
cmd.env("AGENT_BROWSER_IGNORE_DEFAULT_ARGS", ida.join(","));
}
}
}

/// Check if the running daemon's version matches this CLI binary.
Expand Down
62 changes: 62 additions & 0 deletions cli/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct Config {
pub screenshot_format: Option<String>,
pub idle_timeout: Option<String>,
pub no_auto_dialog: Option<bool>,
pub ignore_default_args: Option<Vec<String>>,
pub model: Option<String>,
}

Expand Down Expand Up @@ -135,6 +136,7 @@ impl Config {
screenshot_format: other.screenshot_format.or(self.screenshot_format),
idle_timeout: other.idle_timeout.or(self.idle_timeout),
no_auto_dialog: other.no_auto_dialog.or(self.no_auto_dialog),
ignore_default_args: other.ignore_default_args.or(self.ignore_default_args),
model: other.model.or(self.model),
}
}
Expand Down Expand Up @@ -221,6 +223,7 @@ fn extract_config_path(args: &[String]) -> Option<Option<String>> {
"--screenshot-quality",
"--screenshot-format",
"--idle-timeout",
"--ignore-default-args",
"--model",
];
let mut i = 0;
Expand Down Expand Up @@ -305,6 +308,7 @@ pub struct Flags {
pub idle_timeout: Option<String>, // Canonical milliseconds string for AGENT_BROWSER_IDLE_TIMEOUT_MS
pub default_timeout: Option<u64>, // AGENT_BROWSER_DEFAULT_TIMEOUT in ms
pub no_auto_dialog: bool,
pub ignore_default_args: Option<Vec<String>>,
pub model: Option<String>,
pub verbose: bool,
pub quiet: bool,
Expand All @@ -323,6 +327,7 @@ pub struct Flags {
pub cli_annotate: bool,
pub cli_download_path: bool,
pub cli_headed: bool,
pub cli_ignore_default_args: bool,
}

pub fn parse_flags(args: &[String]) -> Flags {
Expand Down Expand Up @@ -444,6 +449,15 @@ pub fn parse_flags(args: &[String]) -> Flags {
.and_then(|s| s.parse::<u64>().ok()),
no_auto_dialog: env_var_is_truthy("AGENT_BROWSER_NO_AUTO_DIALOG")
|| config.no_auto_dialog.unwrap_or(false),
ignore_default_args: env::var("AGENT_BROWSER_IGNORE_DEFAULT_ARGS")
.ok()
.map(|s| {
s.split([',', '\n'])
.map(|f| f.trim().to_string())
.filter(|f| !f.is_empty())
.collect()
})
.or(config.ignore_default_args),
model: env::var("AI_GATEWAY_MODEL").ok().or(config.model),
verbose: false,
quiet: false,
Expand All @@ -459,6 +473,7 @@ pub fn parse_flags(args: &[String]) -> Flags {
cli_annotate: false,
cli_download_path: false,
cli_headed: false,
cli_ignore_default_args: false,
};

let mut i = 0;
Expand Down Expand Up @@ -728,6 +743,18 @@ pub fn parse_flags(args: &[String]) -> Flags {
i += 1;
}
}
"--ignore-default-args" => {
if let Some(s) = args.get(i + 1) {
flags.ignore_default_args = Some(
s.split(',')
.map(|f| f.trim().to_string())
.filter(|f| !f.is_empty())
.collect(),
);
flags.cli_ignore_default_args = true;
i += 1;
}
}
"--model" => {
if let Some(s) = args.get(i + 1) {
flags.model = Some(s.clone());
Expand Down Expand Up @@ -801,6 +828,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
"--screenshot-quality",
"--screenshot-format",
"--idle-timeout",
"--ignore-default-args",
"--model",
];

Expand Down Expand Up @@ -1441,4 +1469,38 @@ mod tests {
let clean = clean_args(&input);
assert_eq!(clean, vec!["open", "example.com"]);
}

#[test]
fn test_parse_ignore_default_args_flag() {
let flags = parse_flags(&args(
"--ignore-default-args --disable-component-update,--disable-sync open example.com",
));
assert_eq!(
flags.ignore_default_args,
Some(vec![
"--disable-component-update".to_string(),
"--disable-sync".to_string(),
])
);
assert!(flags.cli_ignore_default_args);
}

#[test]
fn test_parse_ignore_default_args_not_set_without_flag() {
let flags = parse_flags(&args("open example.com"));
assert!(flags.ignore_default_args.is_none());
assert!(!flags.cli_ignore_default_args);
}

#[test]
fn test_clean_args_removes_ignore_default_args() {
let input: Vec<String> = vec![
"--ignore-default-args".to_string(),
"--disable-component-update,--disable-sync".to_string(),
"open".to_string(),
"example.com".to_string(),
];
let clean = clean_args(&input);
assert_eq!(clean, vec!["open", "example.com"]);
}
}
4 changes: 4 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,7 @@ fn main() {
default_timeout: flags.default_timeout,
cdp: flags.cdp.as_deref(),
no_auto_dialog: flags.no_auto_dialog,
ignore_default_args: flags.ignore_default_args.as_deref(),
};

let daemon_result = match ensure_daemon(&flags.session, &daemon_opts) {
Expand Down Expand Up @@ -896,6 +897,9 @@ fn main() {
flags.cli_allow_file_access.then_some("--allow-file-access"),
flags.cli_download_path.then_some("--download-path"),
flags.cli_headed.then_some("--headed"),
flags
.cli_ignore_default_args
.then_some("--ignore-default-args"),
]
.into_iter()
.flatten()
Expand Down
18 changes: 18 additions & 0 deletions cli/src/native/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ fn launch_hash(opts: &LaunchOptions) -> u64 {
opts.proxy_password.hash(&mut h);
opts.user_agent.hash(&mut h);
opts.allow_file_access.hash(&mut h);
opts.ignore_default_args.hash(&mut h);
h.finish()
}

Expand Down Expand Up @@ -1637,6 +1638,14 @@ fn launch_options_from_env() -> LaunchOptions {
color_scheme: env::var("AGENT_BROWSER_COLOR_SCHEME").ok(),
download_path: env::var("AGENT_BROWSER_DOWNLOAD_PATH").ok(),
use_real_keychain: false,
ignore_default_args: env::var("AGENT_BROWSER_IGNORE_DEFAULT_ARGS")
.map(|v| {
v.split([',', '\n'])
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
})
.unwrap_or_default(),
}
}

Expand Down Expand Up @@ -1745,6 +1754,15 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
.and_then(|v| v.as_str())
.map(String::from),
use_real_keychain: false,
ignore_default_args: cmd
.get("ignoreDefaultArgs")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_default(),
};

let new_hash = launch_hash(&launch_options);
Expand Down
71 changes: 71 additions & 0 deletions cli/src/native/cdp/chrome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub struct LaunchOptions {
pub proxy_password: Option<String>,
pub profile: Option<String>,
pub args: Vec<String>,
pub ignore_default_args: Vec<String>,
pub allow_file_access: bool,
pub extensions: Option<Vec<String>>,
pub storage_state: Option<String>,
Expand All @@ -120,6 +121,7 @@ impl Default for LaunchOptions {
proxy_password: None,
profile: None,
args: Vec::new(),
ignore_default_args: Vec::new(),
allow_file_access: false,
extensions: None,
storage_state: None,
Expand Down Expand Up @@ -156,6 +158,10 @@ fn build_chrome_args(options: &LaunchOptions) -> Result<ChromeArgs, String> {
"--metrics-recording-only".to_string(),
];

if !options.ignore_default_args.is_empty() {
args.retain(|arg| !options.ignore_default_args.contains(arg));
}

if !options.use_real_keychain {
args.push("--password-store=basic".to_string());
args.push("--use-mock-keychain".to_string());
Expand Down Expand Up @@ -1826,4 +1832,69 @@ mod tests {
"profile path should keep keychain flags"
);
}

#[test]
fn test_build_args_ignore_default_args_exact_match() {
let opts = LaunchOptions {
ignore_default_args: vec![
"--disable-component-update".to_string(),
"--disable-sync".to_string(),
],
..Default::default()
};
let result = build_chrome_args(&opts).unwrap();
assert!(
!result
.args
.iter()
.any(|a| a == "--disable-component-update"),
"exact-matched flag should be removed"
);
assert!(
!result.args.iter().any(|a| a == "--disable-sync"),
"exact-matched flag should be removed"
);
assert!(
result.args.iter().any(|a| a == "--no-first-run"),
"non-ignored flags should remain"
);
if let Some(ref dir) = result.temp_user_data_dir {
let _ = std::fs::remove_dir_all(dir);
}
}

#[test]
fn test_build_args_ignore_default_args_no_prefix_match() {
let opts = LaunchOptions {
ignore_default_args: vec!["--disable-features".to_string()],
..Default::default()
};
let result = build_chrome_args(&opts).unwrap();
assert!(
result
.args
.iter()
.any(|a| a.starts_with("--disable-features=")),
"--disable-features should not remove --disable-features=Translate (exact match only)"
);
if let Some(ref dir) = result.temp_user_data_dir {
let _ = std::fs::remove_dir_all(dir);
}
}

#[test]
fn test_build_args_ignore_default_args_empty_no_effect() {
let opts = LaunchOptions::default();
let result = build_chrome_args(&opts).unwrap();
assert!(
result
.args
.iter()
.any(|a| a == "--disable-component-update"),
"default flags should remain when ignore list is empty"
);
if let Some(ref dir) = result.temp_user_data_dir {
let _ = std::fs::remove_dir_all(dir);
}
}
}
2 changes: 2 additions & 0 deletions cli/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2868,6 +2868,8 @@ Options:
--extension <path> Load browser extensions (repeatable)
--args <args> Browser launch args, comma or newline separated (or AGENT_BROWSER_ARGS)
e.g., --args "--no-sandbox,--disable-blink-features=AutomationControlled"
--ignore-default-args <list> Remove specific default Chrome flags, comma-separated
(or AGENT_BROWSER_IGNORE_DEFAULT_ARGS)
--user-agent <ua> Custom User-Agent (or AGENT_BROWSER_USER_AGENT)
--proxy <server> Proxy server URL (or AGENT_BROWSER_PROXY, HTTP_PROXY, HTTPS_PROXY, ALL_PROXY)
Supports authenticated proxies: --proxy "http://user:pass@127.0.0.1:7890"
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/cdp-mode/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ This enables control of:
<tr><td><code>--headers &lt;json&gt;</code></td><td>HTTP headers scoped to origin</td></tr>
<tr><td><code>--executable-path</code></td><td>Custom browser executable</td></tr>
<tr><td><code>--args &lt;args&gt;</code></td><td>Browser launch args (comma-separated)</td></tr>
<tr><td><code>--ignore-default-args &lt;list&gt;</code></td><td>Remove specific default Chrome flags (comma-separated)</td></tr>
<tr><td><code>--user-agent &lt;ua&gt;</code></td><td>Custom User-Agent string</td></tr>
<tr><td><code>--proxy &lt;url&gt;</code></td><td>Proxy server URL</td></tr>
<tr><td><code>--proxy-bypass &lt;hosts&gt;</code></td><td>Hosts to bypass proxy</td></tr>
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/commands/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ agent-browser reload # Reload page
--executable-path <path> # Custom browser executable
--extension <path> # Load browser extension (repeatable)
--args <args> # Browser launch args (comma separated)
--ignore-default-args <list> # Remove specific default Chrome flags (comma separated)
--user-agent <ua> # Custom User-Agent string
--proxy <url> # Proxy server URL
--proxy-bypass <hosts> # Hosts to bypass proxy
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/configuration/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Every CLI flag can be set in the config file using its camelCase equivalent:
<tr><td><code>proxy</code></td><td><code>--proxy</code></td><td>string</td></tr>
<tr><td><code>proxyBypass</code></td><td><code>--proxy-bypass</code></td><td>string</td></tr>
<tr><td><code>args</code></td><td><code>--args</code></td><td>string</td></tr>
<tr><td><code>ignoreDefaultArgs</code></td><td><code>--ignore-default-args</code></td><td>string[]</td></tr>
<tr><td><code>userAgent</code></td><td><code>--user-agent</code></td><td>string</td></tr>
<tr><td><code>provider</code></td><td><code>-p, --provider</code></td><td>string</td></tr>
<tr><td><code>device</code></td><td><code>--device</code></td><td>string</td></tr>
Expand Down
1 change: 1 addition & 0 deletions docs/src/app/engines/chrome/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ These features are available only with Chrome:
<tr><td>File URL access</td><td><code>--allow-file-access</code></td></tr>
<tr><td>Headed mode</td><td><code>--headed</code></td></tr>
<tr><td>Custom launch args</td><td><code>--args &lt;args&gt;</code></td></tr>
<tr><td>Remove default flags</td><td><code>--ignore-default-args &lt;list&gt;</code></td></tr>
</tbody>
</table>

Expand Down
14 changes: 14 additions & 0 deletions skills/agent-browser/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,20 @@ Create `agent-browser.json` in the project root for persistent settings:

Priority (lowest to highest): `~/.agent-browser/config.json` < `./agent-browser.json` < env vars < CLI flags. Use `--config <path>` or `AGENT_BROWSER_CONFIG` env var for a custom config file (exits with error if missing/invalid). All CLI options map to camelCase keys (e.g., `--executable-path` -> `"executablePath"`). Boolean flags accept `true`/`false` values (e.g., `--headed false` overrides config). Extensions from user and project configs are merged, not replaced.

## Removing Default Chrome Flags

Use `--ignore-default-args` to selectively remove specific flags from the default set of Chrome launch arguments. This is useful when a default flag conflicts with your use case.

```bash
# Remove specific default flags (comma-separated)
agent-browser --ignore-default-args "--disable-component-update,--disable-sync" open https://example.com

# Via environment variable
AGENT_BROWSER_IGNORE_DEFAULT_ARGS="--disable-component-update,--disable-sync" agent-browser open https://example.com
```

Only exact matches are removed. For example, `--disable-features` will not remove `--disable-features=TranslateUI` or other flags that share the same prefix.

## Deep-Dive Documentation

| Reference | When to Use |
Expand Down