From f63608a6128329a7476eda1ab1f4e1c5d0154a9d Mon Sep 17 00:00:00 2001 From: Ariel Rahmane Date: Wed, 1 Apr 2026 10:57:06 +1100 Subject: [PATCH 01/11] feat: phase 1 --- .gitignore | 6 + cli/src/commands.rs | 318 +------------------------------------------- cli/src/output.rs | 225 ------------------------------- 3 files changed, 12 insertions(+), 537 deletions(-) diff --git a/.gitignore b/.gitignore index 5a40802b7..8ab46fb21 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,9 @@ docs/package-lock.json # next .next/ out/ + +# Claude +.claude/ + +# Dev Internal +.dev-internal/ \ No newline at end of file diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 661595c1c..82dffd8f9 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -1,6 +1,4 @@ -use base64::{engine::general_purpose::STANDARD, Engine}; use serde_json::{json, Value}; -use std::io::{self, BufRead}; use crate::color; use crate::flags::Flags; @@ -91,6 +89,12 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result { + return Err(ParseError::UnknownCommand { command: cmd.to_string() }); + } + // === Navigation === // Maps to "navigate" action in protocol; reflected in ACTION_CATEGORIES in action-policy.ts "open" | "goto" | "navigate" => { @@ -559,183 +563,9 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result { - // Check for flags: -b/--base64 or --stdin - let (is_base64, is_stdin, script_parts): (bool, bool, &[&str]) = - if rest.first() == Some(&"-b") || rest.first() == Some(&"--base64") { - (true, false, &rest[1..]) - } else if rest.first() == Some(&"--stdin") { - (false, true, &rest[1..]) - } else { - (false, false, rest.as_slice()) - }; - - let script = if is_stdin { - // Read script from stdin - let stdin = io::stdin(); - let lines: Vec = stdin - .lock() - .lines() - .map(|l| l.unwrap_or_default()) - .collect(); - lines.join("\n") - } else { - let raw_script = script_parts.join(" "); - if is_base64 { - let decoded = - STANDARD - .decode(&raw_script) - .map_err(|_| ParseError::InvalidValue { - message: "Invalid base64 encoding".to_string(), - usage: "eval -b ", - })?; - String::from_utf8(decoded).map_err(|_| ParseError::InvalidValue { - message: "Base64 decoded to invalid UTF-8".to_string(), - usage: "eval -b ", - })? - } else { - raw_script - } - }; - Ok(json!({ "id": id, "action": "evaluate", "script": script })) - } - // === Close === "close" | "quit" | "exit" => Ok(json!({ "id": id, "action": "close" })), - // === Inspect === - "inspect" => Ok(json!({ "id": id, "action": "inspect" })), - - // === Authentication Vault === - "auth" => { - let sub = rest.first().map(|s| s.as_ref()); - match sub { - Some("save") => { - let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password ", - })?; - - let mut url = None; - let mut username = None; - let mut password = None; - let mut password_stdin = false; - let mut username_selector = None; - let mut password_selector = None; - let mut submit_selector = None; - - let mut j = 2; - while j < rest.len() { - match rest[j] { - "--url" => { - url = rest.get(j + 1).cloned(); - j += 1; - } - "--username" => { - username = rest.get(j + 1).cloned(); - j += 1; - } - "--password" => { - password = rest.get(j + 1).cloned(); - j += 1; - } - "--password-stdin" => { - password_stdin = true; - } - "--username-selector" => { - username_selector = rest.get(j + 1).cloned(); - j += 1; - } - "--password-selector" => { - password_selector = rest.get(j + 1).cloned(); - j += 1; - } - "--submit-selector" => { - submit_selector = rest.get(j + 1).cloned(); - j += 1; - } - other => { - if other.starts_with("--") { - return Err(ParseError::InvalidValue { - message: format!("unknown flag '{}' for auth save", other), - usage: "agent-browser auth save --url --username --password ", - }); - } - } - } - j += 1; - } - - let url_val = url.ok_or_else(|| ParseError::MissingArguments { - context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password [--password-stdin]", - })?; - let user_val = username.ok_or_else(|| ParseError::MissingArguments { - context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password [--password-stdin]", - })?; - - if !password_stdin && password.is_none() { - return Err(ParseError::MissingArguments { - context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password [--password-stdin]", - }); - } - - let mut cmd = json!({ - "id": id, - "action": "auth_save", - "name": name, - "url": url_val, - "username": user_val, - }); - if password_stdin { - cmd["passwordStdin"] = json!(true); - } - if let Some(pass_val) = password { - cmd["password"] = json!(pass_val); - } - if let Some(us) = username_selector { - cmd["usernameSelector"] = json!(us); - } - if let Some(ps) = password_selector { - cmd["passwordSelector"] = json!(ps); - } - if let Some(ss) = submit_selector { - cmd["submitSelector"] = json!(ss); - } - Ok(cmd) - } - Some("login") => { - let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "auth login".to_string(), - usage: "agent-browser auth login ", - })?; - Ok(json!({ "id": id, "action": "auth_login", "name": name })) - } - Some("list") => Ok(json!({ "id": id, "action": "auth_list" })), - Some("delete") | Some("remove") => { - let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "auth delete".to_string(), - usage: "agent-browser auth delete ", - })?; - Ok(json!({ "id": id, "action": "auth_delete", "name": name })) - } - Some("show") => { - let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "auth show".to_string(), - usage: "agent-browser auth show ", - })?; - Ok(json!({ "id": id, "action": "auth_show", "name": name })) - } - _ => Err(ParseError::UnknownSubcommand { - subcommand: sub.unwrap_or("(none)").to_string(), - valid_options: &["save", "login", "list", "delete", "show"], - }), - } - } - // === Action Confirmation === "confirm" => { let cid = rest.first().ok_or_else(|| ParseError::MissingArguments { @@ -752,108 +582,6 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result { - let endpoint = rest.first().ok_or_else(|| ParseError::MissingArguments { - context: "connect".to_string(), - usage: "connect ", - })?; - // Check if it's a URL (ws://, wss://, http://, https://) - if endpoint.starts_with("ws://") - || endpoint.starts_with("wss://") - || endpoint.starts_with("http://") - || endpoint.starts_with("https://") - { - Ok(json!({ "id": id, "action": "launch", "cdpUrl": endpoint })) - } else { - // It's a port number - validate and use cdpPort field - let port: u16 = match endpoint.parse::() { - Ok(0) => { - return Err(ParseError::InvalidValue { - message: "Invalid port: port must be greater than 0".to_string(), - usage: "connect ", - }); - } - Ok(p) if p > 65535 => { - return Err(ParseError::InvalidValue { - message: format!( - "Invalid port: {} is out of range (valid range: 1-65535)", - p - ), - usage: "connect ", - }); - } - Ok(p) => p as u16, - Err(_) => { - return Err(ParseError::InvalidValue { - message: format!( - "Invalid value: '{}' is not a valid port number or URL", - endpoint - ), - usage: "connect ", - }); - } - }; - Ok(json!({ "id": id, "action": "launch", "cdpPort": port })) - } - } - - // === Runtime stream control === - "stream" => match rest.first().copied() { - Some("enable") => { - let mut cmd = json!({ "id": id, "action": "stream_enable" }); - let mut i = 1; - while i < rest.len() { - match rest[i] { - "--port" => { - let value = - rest.get(i + 1) - .ok_or_else(|| ParseError::MissingArguments { - context: "stream enable --port".to_string(), - usage: "stream enable [--port ]", - })?; - let port = - value.parse::().map_err(|_| ParseError::InvalidValue { - message: format!( - "Invalid port: '{}' is not a valid integer", - value - ), - usage: "stream enable [--port ]", - })?; - if port > u16::MAX as u32 { - return Err(ParseError::InvalidValue { - message: format!( - "Invalid port: {} is out of range (valid range: 0-65535)", - port - ), - usage: "stream enable [--port ]", - }); - } - cmd["port"] = json!(port); - i += 2; - } - flag => { - return Err(ParseError::InvalidValue { - message: format!("Unknown flag for stream enable: {}", flag), - usage: "stream enable [--port ]", - }); - } - } - } - Ok(cmd) - } - Some("disable") => Ok(json!({ "id": id, "action": "stream_disable" })), - Some("status") => Ok(json!({ "id": id, "action": "stream_status" })), - Some(sub) => Err(ParseError::UnknownSubcommand { - subcommand: sub.to_string(), - valid_options: &["enable", "disable", "status"], - }), - None => Err(ParseError::MissingArguments { - context: "stream".to_string(), - usage: "stream ", - }), - }, - // === Get === "get" => parse_get(&rest, &id), @@ -1202,27 +930,6 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result match rest.first().copied() { - Some("read") | None => { - Ok(json!({ "id": id, "action": "clipboard", "operation": "read" })) - } - Some("write") => { - rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "clipboard write".to_string(), - usage: "clipboard write ", - })?; - let text = rest[1..].join(" "); - Ok(json!({ "id": id, "action": "clipboard", "operation": "write", "text": text })) - } - Some("copy") => Ok(json!({ "id": id, "action": "clipboard", "operation": "copy" })), - Some("paste") => Ok(json!({ "id": id, "action": "clipboard", "operation": "paste" })), - Some(sub) => Err(ParseError::UnknownSubcommand { - subcommand: sub.to_string(), - valid_options: &["read", "write", "copy", "paste"], - }), - }, - // === State === "state" => { const VALID: &[&str] = &["save", "load", "list", "clear", "show", "clean", "rename"]; @@ -2007,8 +1714,6 @@ fn parse_set(rest: &[&str], id: &str) -> Result { "geolocation", "offline", "headers", - "credentials", - "auth", "media", ]; @@ -2096,17 +1801,6 @@ fn parse_set(rest: &[&str], id: &str) -> Result { })?; Ok(json!({ "id": id, "action": "headers", "headers": headers })) } - Some("credentials") | Some("auth") => { - let user = rest.get(1).ok_or_else(|| ParseError::MissingArguments { - context: "set credentials".to_string(), - usage: "set credentials ", - })?; - let pass = rest.get(2).ok_or_else(|| ParseError::MissingArguments { - context: "set credentials".to_string(), - usage: "set credentials ", - })?; - Ok(json!({ "id": id, "action": "credentials", "username": user, "password": pass })) - } Some("media") => { let color = if rest.contains(&"dark") { "dark" diff --git a/cli/src/output.rs b/cli/src/output.rs index d49aa9723..a6e353c31 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -1560,37 +1560,6 @@ Examples: "## } - // === Eval === - "eval" => { - r##" -agent-browser eval - Execute JavaScript - -Usage: agent-browser eval [options]