fix(cli): 修复 type/keyboard 的 --delay 参数解析
将 --delay <ms> 从输入文本中剥离并写入 delay 字段,避免搜索词混入参数。 同时支持使用 -- 终止参数解析以输入字面量 --delay 文本,并补充回归测试与帮助文档。
This commit is contained in:
@@ -143,10 +143,10 @@ agent-browser open <url> # Navigate to URL (aliases: goto, navigate
|
||||
agent-browser click <sel> # Click element (--new-tab to open in new tab)
|
||||
agent-browser dblclick <sel> # Double-click element
|
||||
agent-browser focus <sel> # Focus element
|
||||
agent-browser type <sel> <text> # Type into element
|
||||
agent-browser type <sel> <text> [--delay <ms>] # Type into element
|
||||
agent-browser fill <sel> <text> # Clear and fill
|
||||
agent-browser press <key> # Press key (Enter, Tab, Control+a) (alias: key)
|
||||
agent-browser keyboard type <text> # Type with real keystrokes (no selector, current focus)
|
||||
agent-browser keyboard type <text> [--delay <ms>] # Type with real keystrokes (no selector, current focus)
|
||||
agent-browser keyboard inserttext <text> # Insert text without key events (no selector)
|
||||
agent-browser keydown <key> # Hold key down
|
||||
agent-browser keyup <key> # Release key
|
||||
|
||||
Generated
+1
-1
@@ -4,7 +4,7 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "agent-browser-stealth"
|
||||
version = "0.14.0-fork.2"
|
||||
version = "0.14.0-fork.3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"dirs",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "agent-browser-stealth"
|
||||
version = "0.14.0-fork.2"
|
||||
version = "0.14.0-fork.3"
|
||||
edition = "2021"
|
||||
description = "Stealth browser automation CLI for AI agents with anti-bot evasions"
|
||||
license = "Apache-2.0"
|
||||
|
||||
+112
-9
@@ -71,6 +71,62 @@ pub fn gen_id() -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse free-form text arguments with optional `--delay <ms>`.
|
||||
///
|
||||
/// `--` can be used to stop flag parsing if text must include `--delay` literally.
|
||||
fn parse_text_with_optional_delay(
|
||||
args: &[&str],
|
||||
context: &str,
|
||||
usage: &'static str,
|
||||
) -> Result<(String, Option<u64>), ParseError> {
|
||||
let mut text_parts: Vec<&str> = Vec::new();
|
||||
let mut delay_ms: Option<u64> = None;
|
||||
let mut parse_flags = true;
|
||||
let mut i = 0;
|
||||
|
||||
while i < args.len() {
|
||||
let arg = args[i];
|
||||
|
||||
if parse_flags && arg == "--" {
|
||||
parse_flags = false;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if parse_flags && arg == "--delay" {
|
||||
let raw = args
|
||||
.get(i + 1)
|
||||
.ok_or_else(|| ParseError::MissingArguments {
|
||||
context: format!("{} --delay", context),
|
||||
usage,
|
||||
})?;
|
||||
let parsed = raw.parse::<u64>().map_err(|_| ParseError::InvalidValue {
|
||||
message: format!(
|
||||
"Invalid --delay value: {} (must be a non-negative integer in milliseconds)",
|
||||
raw
|
||||
),
|
||||
usage,
|
||||
})?;
|
||||
delay_ms = Some(parsed);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
text_parts.push(arg);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let text = text_parts.join(" ");
|
||||
if text.is_empty() {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: context.to_string(),
|
||||
usage,
|
||||
});
|
||||
}
|
||||
|
||||
Ok((text, delay_ms))
|
||||
}
|
||||
|
||||
pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError> {
|
||||
if args.is_empty() {
|
||||
return Err(ParseError::MissingArguments {
|
||||
@@ -165,9 +221,18 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
||||
"type" => {
|
||||
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||
context: "type".to_string(),
|
||||
usage: "type <selector> <text>",
|
||||
usage: "type <selector> <text> [--delay <ms>]",
|
||||
})?;
|
||||
Ok(json!({ "id": id, "action": "type", "selector": sel, "text": rest[1..].join(" ") }))
|
||||
let (text, delay) = parse_text_with_optional_delay(
|
||||
&rest[1..],
|
||||
"type",
|
||||
"type <selector> <text> [--delay <ms>]",
|
||||
)?;
|
||||
let mut cmd = json!({ "id": id, "action": "type", "selector": sel, "text": text });
|
||||
if let Some(ms) = delay {
|
||||
cmd["delay"] = json!(ms);
|
||||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
"hover" => {
|
||||
let sel = rest.first().ok_or_else(|| ParseError::MissingArguments {
|
||||
@@ -272,14 +337,16 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
|
||||
})?;
|
||||
match *sub {
|
||||
"type" => {
|
||||
let text: String = rest[1..].join(" ");
|
||||
if text.is_empty() {
|
||||
return Err(ParseError::MissingArguments {
|
||||
context: "keyboard type".to_string(),
|
||||
usage: "keyboard type <text>",
|
||||
});
|
||||
let (text, delay) = parse_text_with_optional_delay(
|
||||
&rest[1..],
|
||||
"keyboard type",
|
||||
"keyboard type <text> [--delay <ms>]",
|
||||
)?;
|
||||
let mut cmd = json!({ "id": id, "action": "keyboard", "subaction": "type", "text": text });
|
||||
if let Some(ms) = delay {
|
||||
cmd["delay"] = json!(ms);
|
||||
}
|
||||
Ok(json!({ "id": id, "action": "keyboard", "subaction": "type", "text": text }))
|
||||
Ok(cmd)
|
||||
}
|
||||
"inserttext" | "insertText" => {
|
||||
let text: String = rest[1..].join(" ");
|
||||
@@ -2300,6 +2367,29 @@ mod tests {
|
||||
assert_eq!(cmd["text"], "some text");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_command_with_delay() {
|
||||
let cmd =
|
||||
parse_command(&args("type #input some text --delay 120"), &default_flags()).unwrap();
|
||||
assert_eq!(cmd["action"], "type");
|
||||
assert_eq!(cmd["selector"], "#input");
|
||||
assert_eq!(cmd["text"], "some text");
|
||||
assert_eq!(cmd["delay"], 120);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_command_with_literal_delay_text() {
|
||||
let cmd = parse_command(
|
||||
&args("type #input -- --delay 120 should be typed"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "type");
|
||||
assert_eq!(cmd["selector"], "#input");
|
||||
assert_eq!(cmd["text"], "--delay 120 should be typed");
|
||||
assert!(cmd.get("delay").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select() {
|
||||
let cmd = parse_command(&args("select #menu option1"), &default_flags()).unwrap();
|
||||
@@ -2479,6 +2569,19 @@ mod tests {
|
||||
assert_eq!(cmd["selector"], "#element");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keyboard_type_with_delay() {
|
||||
let cmd = parse_command(
|
||||
&args("keyboard type natural typing --delay 90"),
|
||||
&default_flags(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cmd["action"], "keyboard");
|
||||
assert_eq!(cmd["subaction"], "type");
|
||||
assert_eq!(cmd["text"], "natural typing");
|
||||
assert_eq!(cmd["delay"], 90);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wait_timeout() {
|
||||
let cmd = parse_command(&args("wait 5000"), &default_flags()).unwrap();
|
||||
|
||||
+8
-4
@@ -719,10 +719,11 @@ Examples:
|
||||
r##"
|
||||
agent-browser type - Type text into an element
|
||||
|
||||
Usage: agent-browser type <selector> <text>
|
||||
Usage: agent-browser type <selector> <text> [--delay <ms>]
|
||||
|
||||
Types text into the specified element character by character.
|
||||
Unlike fill, this does not clear existing content first.
|
||||
Use --delay to add per-character delay (milliseconds).
|
||||
|
||||
Global Options:
|
||||
--json Output as JSON
|
||||
@@ -730,7 +731,9 @@ Global Options:
|
||||
|
||||
Examples:
|
||||
agent-browser type "#search" "hello"
|
||||
agent-browser type "#search" "iphone" --delay 120
|
||||
agent-browser type @e2 "additional text"
|
||||
agent-browser type @e2 -- "--delay 120 (literal text)"
|
||||
|
||||
See Also:
|
||||
For typing into contenteditable editors (Lexical, ProseMirror, etc.)
|
||||
@@ -961,7 +964,7 @@ the current focus — essential for contenteditable editors like
|
||||
Lexical, ProseMirror, CodeMirror, and Monaco.
|
||||
|
||||
Subcommands:
|
||||
type <text> Type text character-by-character with real
|
||||
type <text> [--delay <ms>] Type text character-by-character with real
|
||||
key events (keydown, keypress, keyup per char)
|
||||
inserttext <text> Insert text without key events (like paste)
|
||||
|
||||
@@ -974,6 +977,7 @@ Global Options:
|
||||
|
||||
Examples:
|
||||
agent-browser keyboard type "Hello, World!"
|
||||
agent-browser keyboard type "human pacing" --delay 90
|
||||
agent-browser keyboard type "# My Heading"
|
||||
agent-browser keyboard inserttext "pasted content"
|
||||
|
||||
@@ -2016,10 +2020,10 @@ Core Commands:
|
||||
open <url> Navigate to URL
|
||||
click <sel> Click element (or @ref)
|
||||
dblclick <sel> Double-click element
|
||||
type <sel> <text> Type into element
|
||||
type <sel> <text> [--delay <ms>] Type into element
|
||||
fill <sel> <text> Clear and fill
|
||||
press <key> Press key (Enter, Tab, Control+a)
|
||||
keyboard type <text> Type text with real keystrokes (no selector)
|
||||
keyboard type <text> [--delay <ms>] Type text with real keystrokes (no selector)
|
||||
keyboard inserttext <text> Insert text without key events
|
||||
hover <sel> Hover element
|
||||
focus <sel> Focus element
|
||||
|
||||
@@ -11,9 +11,9 @@ agent-browser open <url> # Navigate (aliases: goto, navigate)
|
||||
agent-browser click <sel> # Click element (--new-tab to open in new tab)
|
||||
agent-browser dblclick <sel> # Double-click
|
||||
agent-browser fill <sel> <text> # Clear and fill
|
||||
agent-browser type <sel> <text> # Type into element
|
||||
agent-browser type <sel> <text> [--delay <ms>] # Type into element
|
||||
agent-browser press <key> # Press key (Enter, Tab, Control+a) (alias: key)
|
||||
agent-browser keyboard type <text> # Type at current focus (no selector needed)
|
||||
agent-browser keyboard type <text> [--delay <ms>] # Type at current focus (no selector needed)
|
||||
agent-browser keyboard inserttext <text> # Insert text without key events
|
||||
agent-browser keydown <key> # Hold key down
|
||||
agent-browser keyup <key> # Release key
|
||||
|
||||
@@ -63,11 +63,11 @@ agent-browser snapshot -s "#selector" # Scope to CSS selector
|
||||
agent-browser click @e1 # Click element
|
||||
agent-browser click @e1 --new-tab # Click and open in new tab
|
||||
agent-browser fill @e2 "text" # Clear and type text
|
||||
agent-browser type @e2 "text" # Type without clearing
|
||||
agent-browser type @e2 "text" --delay 120 # Type without clearing (human-like pacing)
|
||||
agent-browser select @e1 "option" # Select dropdown option
|
||||
agent-browser check @e1 # Check checkbox
|
||||
agent-browser press Enter # Press key
|
||||
agent-browser keyboard type "text" # Type at current focus (no selector)
|
||||
agent-browser keyboard type "text" --delay 90 # Type at current focus (no selector)
|
||||
agent-browser keyboard inserttext "text" # Insert without key events
|
||||
agent-browser scroll down 500 # Scroll page
|
||||
|
||||
|
||||
Reference in New Issue
Block a user