use std::env; pub struct Flags { pub json: bool, pub full: bool, pub headed: bool, pub debug: bool, pub session: String, pub headers: Option, pub executable_path: Option, pub cdp: Option, pub extensions: Vec, pub profile: Option, pub proxy: Option, pub proxy_bypass: Option, pub args: Option, pub user_agent: Option, pub provider: Option, pub ignore_https_errors: bool, } pub fn parse_flags(args: &[String]) -> Flags { let extensions_env = env::var("AGENT_BROWSER_EXTENSIONS") .ok() .map(|s| s.split(',').map(|p| p.trim().to_string()).filter(|p| !p.is_empty()).collect::>()) .unwrap_or_default(); let mut flags = Flags { json: false, full: false, headed: false, debug: false, session: env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()), headers: None, executable_path: env::var("AGENT_BROWSER_EXECUTABLE_PATH").ok(), cdp: None, extensions: extensions_env, profile: env::var("AGENT_BROWSER_PROFILE").ok(), proxy: env::var("AGENT_BROWSER_PROXY").ok(), proxy_bypass: env::var("AGENT_BROWSER_PROXY_BYPASS").ok(), args: env::var("AGENT_BROWSER_ARGS").ok(), user_agent: env::var("AGENT_BROWSER_USER_AGENT").ok(), provider: env::var("AGENT_BROWSER_PROVIDER").ok(), ignore_https_errors: false, }; let mut i = 0; while i < args.len() { match args[i].as_str() { "--json" => flags.json = true, "--full" | "-f" => flags.full = true, "--headed" => flags.headed = true, "--debug" => flags.debug = true, "--session" => { if let Some(s) = args.get(i + 1) { flags.session = s.clone(); i += 1; } } "--headers" => { if let Some(h) = args.get(i + 1) { flags.headers = Some(h.clone()); i += 1; } } "--executable-path" => { if let Some(s) = args.get(i + 1) { flags.executable_path = Some(s.clone()); i += 1; } }, "--extension" => { if let Some(s) = args.get(i + 1) { flags.extensions.push(s.clone()); i += 1; } }, "--cdp" => { if let Some(s) = args.get(i + 1) { flags.cdp = Some(s.clone()); i += 1; } } "--profile" => { if let Some(s) = args.get(i + 1) { flags.profile = Some(s.clone()); i += 1; } } "--proxy" => { if let Some(p) = args.get(i + 1) { flags.proxy = Some(p.clone()); i += 1; } } "--proxy-bypass" => { if let Some(s) = args.get(i + 1) { flags.proxy_bypass = Some(s.clone()); i += 1; } } "--args" => { if let Some(s) = args.get(i + 1) { flags.args = Some(s.clone()); i += 1; } } "--user-agent" => { if let Some(s) = args.get(i + 1) { flags.user_agent = Some(s.clone()); i += 1; } } "-p" | "--provider" => { if let Some(p) = args.get(i + 1) { flags.provider = Some(p.clone()); i += 1; } } "--ignore-https-errors" => flags.ignore_https_errors = true, _ => {} } i += 1; } flags } pub fn clean_args(args: &[String]) -> Vec { let mut result = Vec::new(); let mut skip_next = false; // Global flags that should be stripped from command args const GLOBAL_FLAGS: &[&str] = &["--json", "--full", "--headed", "--debug", "--ignore-https-errors"]; // Global flags that take a value (need to skip the next arg too) const GLOBAL_FLAGS_WITH_VALUE: &[&str] = &[ "--session", "--headers", "--executable-path", "--cdp", "--extension", "--profile", "--proxy", "--proxy-bypass", "--args", "--user-agent", "-p", "--provider", ]; for arg in args.iter() { if skip_next { skip_next = false; continue; } if GLOBAL_FLAGS_WITH_VALUE.contains(&arg.as_str()) { skip_next = true; continue; } // Only strip known global flags, not command-specific flags if GLOBAL_FLAGS.contains(&arg.as_str()) || arg == "-f" { continue; } result.push(arg.clone()); } result } #[cfg(test)] mod tests { use super::*; fn args(s: &str) -> Vec { s.split_whitespace().map(String::from).collect() } #[test] fn test_parse_headers_flag() { let flags = parse_flags(&args(r#"open example.com --headers {"Auth":"token"}"#)); assert_eq!(flags.headers, Some(r#"{"Auth":"token"}"#.to_string())); } #[test] fn test_parse_headers_flag_with_spaces() { // Headers JSON is passed as a single quoted argument in shell let input: Vec = vec![ "open".to_string(), "example.com".to_string(), "--headers".to_string(), r#"{"Authorization": "Bearer token"}"#.to_string(), ]; let flags = parse_flags(&input); assert_eq!( flags.headers, Some(r#"{"Authorization": "Bearer token"}"#.to_string()) ); } #[test] fn test_parse_no_headers_flag() { let flags = parse_flags(&args("open example.com")); assert!(flags.headers.is_none()); } #[test] fn test_clean_args_removes_headers() { let input: Vec = vec![ "open".to_string(), "example.com".to_string(), "--headers".to_string(), r#"{"Auth":"token"}"#.to_string(), ]; let clean = clean_args(&input); assert_eq!(clean, vec!["open", "example.com"]); } #[test] fn test_clean_args_removes_headers_at_start() { let input: Vec = vec![ "--headers".to_string(), r#"{"Auth":"token"}"#.to_string(), "open".to_string(), "example.com".to_string(), ]; let clean = clean_args(&input); assert_eq!(clean, vec!["open", "example.com"]); } #[test] fn test_headers_with_other_flags() { let input: Vec = vec![ "open".to_string(), "example.com".to_string(), "--headers".to_string(), r#"{"Auth":"token"}"#.to_string(), "--json".to_string(), "--headed".to_string(), ]; let flags = parse_flags(&input); assert_eq!(flags.headers, Some(r#"{"Auth":"token"}"#.to_string())); assert!(flags.json); assert!(flags.headed); let clean = clean_args(&input); assert_eq!(clean, vec!["open", "example.com"]); } #[test] fn test_parse_executable_path_flag() { let flags = parse_flags(&args( "--executable-path /path/to/chromium open example.com", )); assert_eq!(flags.executable_path, Some("/path/to/chromium".to_string())); } #[test] fn test_parse_executable_path_flag_no_value() { let flags = parse_flags(&args("--executable-path")); assert_eq!(flags.executable_path, None); } #[test] fn test_clean_args_removes_executable_path() { let cleaned = clean_args(&args( "--executable-path /path/to/chromium open example.com", )); assert_eq!(cleaned, vec!["open", "example.com"]); } #[test] fn test_clean_args_removes_executable_path_with_other_flags() { let cleaned = clean_args(&args( "--json --executable-path /path/to/chromium --headed open example.com", )); assert_eq!(cleaned, vec!["open", "example.com"]); } #[test] fn test_parse_flags_with_session_and_executable_path() { let flags = parse_flags(&args( "--session test --executable-path /custom/chrome open example.com", )); assert_eq!(flags.session, "test"); assert_eq!(flags.executable_path, Some("/custom/chrome".to_string())); } }