diff --git a/libs/cua-driver-rs/crates/cua-driver/src/cli.rs b/libs/cua-driver-rs/crates/cua-driver/src/cli.rs index c6a6591012..265238f593 100644 --- a/libs/cua-driver-rs/crates/cua-driver/src/cli.rs +++ b/libs/cua-driver-rs/crates/cua-driver/src/cli.rs @@ -206,9 +206,34 @@ pub fn parse_command() -> Command { } Some("call") => { let tool = pos.next().unwrap_or("").to_string(); - let json_args = pos.next() - .and_then(|s| serde_json::from_str(s).ok()) - .or_else(|| read_stdin_json()); + // Differentiate "no positional arg" (fall back to stdin) from + // "positional arg given but didn't parse as JSON" (surface the + // error instead of silently falling back to stdin and letting + // the tool's required-field validator emit a misleading + // "missing field X" later). See #1637. + // + // The common cause of an unparseable positional arg is + // PowerShell 5.1's native-command-arg quote-stripping on + // multi-field JSON — `'{"a":1,"b":2}'` arrives as `{a:1,b:2}` + // which serde_json rejects. The error message points users at + // the stdin-pipe workaround. + let json_args = match pos.next() { + Some(s) => match serde_json::from_str(s) { + Ok(v) => Some(v), + Err(e) => { + eprintln!("error: positional JSON arg to 'cua-driver call' did not parse: {e}"); + eprintln!(" received: {s}"); + eprintln!(); + eprintln!("hint: PowerShell 5.1 strips quotes around JSON field names in"); + eprintln!(" multi-field args. Pipe the JSON via stdin instead:"); + eprintln!(" '{{\"pid\":1234,\"window_id\":5678}}' | cua-driver call {}", tool); + eprintln!(); + eprintln!(" Or use PowerShell 7+ (pwsh) which preserves the quotes."); + process::exit(2); + } + }, + None => read_stdin_json(), + }; Command::Call { tool, json_args, screenshot_out_file } } Some("telemetry") => { @@ -251,10 +276,25 @@ pub fn parse_command() -> Command { } Some(first) => { // Implicit call: unrecognised first positional → treat as tool name. + // Same parse-error handling as the explicit `call` branch above. See #1637. let tool = first.to_string(); - let json_args = pos.next() - .and_then(|s| serde_json::from_str(s).ok()) - .or_else(|| read_stdin_json()); + let json_args = match pos.next() { + Some(s) => match serde_json::from_str(s) { + Ok(v) => Some(v), + Err(e) => { + eprintln!("error: positional JSON arg to 'cua-driver {tool}' did not parse: {e}"); + eprintln!(" received: {s}"); + eprintln!(); + eprintln!("hint: PowerShell 5.1 strips quotes around JSON field names in"); + eprintln!(" multi-field args. Pipe the JSON via stdin instead:"); + eprintln!(" '{{\"pid\":1234,\"window_id\":5678}}' | cua-driver {}", tool); + eprintln!(); + eprintln!(" Or use PowerShell 7+ (pwsh) which preserves the quotes."); + process::exit(2); + } + }, + None => read_stdin_json(), + }; Command::Call { tool, json_args, screenshot_out_file } } } diff --git a/libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs b/libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs index de073fd531..f617a0beaf 100644 --- a/libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs +++ b/libs/cua-driver-rs/crates/platform-windows/src/tools/impl_.rs @@ -843,8 +843,15 @@ impl Tool for LaunchAppTool { } if target.is_none() && urls.is_empty() { - // Match Swift's wording verbatim. - return ToolResult::error("Provide either bundle_id or name to identify the app to launch."); + // Error message lists every field the resolver actually accepts. + // The Swift-original "bundle_id or name" message predated the Windows + // additions (aumid, path, launch_path, urls) and made #1635 look like + // an aumid-specific bug. The actual cause of #1635 was the upstream + // PS argv quote-stripping bug fixed in #1637; this message just stops + // misleading anyone who hits the error for unrelated reasons. + return ToolResult::error( + "Provide one of: bundle_id, name, aumid, path, launch_path, or urls to identify the app to launch.", + ); } // ── Packaged-app (UWP / MSIX) routing decision ──────────────────────