From 28740acecfc8bda61eca8bfaef604a247b172d16 Mon Sep 17 00:00:00 2001 From: Nicenonecb <49465503+Nicenonecb@users.noreply.github.com> Date: Sun, 18 Jan 2026 10:25:19 +0800 Subject: [PATCH] Fix CLI/protocol mismatches for select, frame main, and headers (#45) * fix: align CLI command payloads with protocol * fix(cli): support multi-value select in CLI --- cli/src/commands.rs | 41 ++++++++++++++++++++++++++++++++++++----- cli/src/output.rs | 7 ++++--- src/protocol.test.ts | 16 ++++++++++++++++ src/protocol.ts | 1 + 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index e14713e..2053cb4 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -153,13 +153,18 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result { let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments { context: "select".to_string(), - usage: "select ", + usage: "select ", })?; - let val = rest.get(1).ok_or_else(|| ParseError::MissingArguments { + let _val = rest.get(1).ok_or_else(|| ParseError::MissingArguments { context: "select".to_string(), - usage: "select ", + usage: "select ", })?; - Ok(json!({ "id": id, "action": "select", "selector": sel, "value": val })) + let values = &rest[1..]; + if values.len() == 1 { + Ok(json!({ "id": id, "action": "select", "selector": sel, "values": values[0] })) + } else { + Ok(json!({ "id": id, "action": "select", "selector": sel, "values": values })) + } } "drag" => { let src = rest.get(0).ok_or_else(|| ParseError::MissingArguments { @@ -408,7 +413,7 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result { if rest.get(0).map(|s| *s) == Some("main") { - Ok(json!({ "id": id, "action": "frame_main" })) + Ok(json!({ "id": id, "action": "mainframe" })) } else { let sel = rest.get(0).ok_or_else(|| ParseError::MissingArguments { context: "frame".to_string(), @@ -1249,6 +1254,32 @@ mod tests { assert_eq!(cmd["text"], "some text"); } + #[test] + fn test_select() { + let cmd = parse_command(&args("select #menu option1"), &default_flags()).unwrap(); + assert_eq!(cmd["action"], "select"); + assert_eq!(cmd["selector"], "#menu"); + assert_eq!(cmd["values"], "option1"); + } + + #[test] + fn test_select_multiple_values() { + let cmd = parse_command( + &args("select #menu opt1 opt2 opt3"), + &default_flags(), + ) + .unwrap(); + assert_eq!(cmd["action"], "select"); + assert_eq!(cmd["selector"], "#menu"); + assert_eq!(cmd["values"], json!(["opt1", "opt2", "opt3"])); + } + + #[test] + fn test_frame_main() { + let cmd = parse_command(&args("frame main"), &default_flags()).unwrap(); + assert_eq!(cmd["action"], "mainframe"); + } + // === Tabs === #[test] diff --git a/cli/src/output.rs b/cli/src/output.rs index 77b4df1..9e8ed8b 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -423,9 +423,9 @@ Examples: "select" => r##" agent-browser select - Select a dropdown option -Usage: agent-browser select +Usage: agent-browser select -Selects an option in a dropdown by value. Global Options: --json Output as JSON @@ -434,6 +434,7 @@ Global Options: Examples: agent-browser select "#country" "US" agent-browser select @e5 "option2" + agent-browser select "#menu" "opt1" "opt2" "opt3" "##, "drag" => r##" agent-browser drag - Drag and drop @@ -1233,7 +1234,7 @@ Core Commands: focus Focus element check Check checkbox uncheck Uncheck checkbox - select Select dropdown option + select Select dropdown option drag Drag and drop upload Upload files scroll [px] Scroll (up/down/left/right) diff --git a/src/protocol.test.ts b/src/protocol.test.ts index 25fa77e..d886cc0 100644 --- a/src/protocol.test.ts +++ b/src/protocol.test.ts @@ -15,6 +15,22 @@ describe('parseCommand', () => { } }); + it('should parse navigate with headers', () => { + const result = parseCommand( + cmd({ + id: '1', + action: 'navigate', + url: 'https://example.com', + headers: { Authorization: 'Bearer token' }, + }) + ); + expect(result.success).toBe(true); + if (result.success) { + expect(result.command.action).toBe('navigate'); + expect(result.command.headers).toEqual({ Authorization: 'Bearer token' }); + } + }); + it('should reject navigate without url', () => { const result = parseCommand(cmd({ id: '1', action: 'navigate' })); expect(result.success).toBe(false); diff --git a/src/protocol.ts b/src/protocol.ts index a8500b4..e255f8c 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -36,6 +36,7 @@ const navigateSchema = baseCommandSchema.extend({ action: z.literal('navigate'), url: z.string().min(1), waitUntil: z.enum(['load', 'domcontentloaded', 'networkidle']).optional(), + headers: z.record(z.string()).optional(), }); const clickSchema = baseCommandSchema.extend({