mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 17:55:59 -04:00
script: enforce positional arguments come first
Returns a specific `PositionalMustComeFirst` error when a positional argument is placed after key-value pairs.
This commit is contained in:
@@ -1419,6 +1419,7 @@ pub fn printSlashParseError(self: *Terminal, err: Schema.ParseError, name: []con
|
||||
error.UnknownField => "unknown field (typo?)",
|
||||
error.DuplicateField => "the same field was supplied twice (check for case-variants like Selector vs selector)",
|
||||
error.PositionalNotAllowed => "positional only works for commands with one required field. Use key=value",
|
||||
error.PositionalMustComeFirst => "the positional value must come before key=value pairs",
|
||||
error.UnterminatedQuote => "unterminated quote",
|
||||
error.UnsupportedEscape => "backslash escapes aren't supported in quoted values; use the other quote style or `'''…'''`",
|
||||
error.InvalidValue => "invalid value (check argument type)",
|
||||
|
||||
@@ -162,8 +162,9 @@ pub fn resolveModelName(opts: Config.Agent, resolved: ?ResolvedProvider, remembe
|
||||
|
||||
/// Precedence: explicit `--effort` flag > remembered `.lp-agent.zon` value >
|
||||
/// mode default. The interactive REPL defaults to `.low` so turns stay snappy;
|
||||
/// one-shot `--task` and script runs default to `.medium`, where answer
|
||||
/// quality matters more than per-turn latency.
|
||||
/// one-shot `--task` defaults to `.medium`, where answer quality matters more
|
||||
/// than per-turn latency. (Script runs never call the LLM, so the resolved
|
||||
/// effort is unused there.)
|
||||
pub fn resolveEffort(opts: Config.Agent, remembered: ?Remembered, will_repl: bool) Config.Effort {
|
||||
if (opts.effort) |e| return e;
|
||||
if (remembered) |r| if (r.effort) |e| return e;
|
||||
|
||||
@@ -78,6 +78,7 @@ pub const ParseError = error{
|
||||
DuplicateField,
|
||||
MalformedKv,
|
||||
PositionalNotAllowed,
|
||||
PositionalMustComeFirst,
|
||||
UnterminatedQuote,
|
||||
UnsupportedEscape,
|
||||
InvalidValue,
|
||||
@@ -224,7 +225,13 @@ pub fn parseValueDiag(self: Schema, arena: std.mem.Allocator, rest_raw: []const
|
||||
list.appendAssumeCapacity(.{ .key = self.required[0], .value = stripQuotes(tokens[0]) });
|
||||
}
|
||||
for (tokens[kv_start..]) |tok| {
|
||||
const eq = std.mem.indexOfScalar(u8, tok, '=') orelse return error.MalformedKv;
|
||||
const eq = std.mem.indexOfScalar(u8, tok, '=') orelse {
|
||||
// `/extract save=x '{…}'` — the value would have bound fine as a
|
||||
// leading positional, so point at the ordering instead of the
|
||||
// generic kv complaint.
|
||||
if (self.required.len == 1 and !leading_positional) return error.PositionalMustComeFirst;
|
||||
return error.MalformedKv;
|
||||
};
|
||||
if (eq == 0 or eq == tok.len - 1) return error.MalformedKv;
|
||||
const key = tok[0..eq];
|
||||
// Reject typos like `checke=false` that would otherwise be silently
|
||||
|
||||
@@ -384,6 +384,14 @@ test "parse: /click rejects positional (zero required fields)" {
|
||||
try testing.expectString("Login", cmd.tool_call.args.?.object.get("selector").?.string);
|
||||
}
|
||||
|
||||
test "parse: /extract rejects positional after key=value" {
|
||||
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
try testing.expectError(error.PositionalMustComeFirst, Command.parse(arena.allocator(), "/extract save=front '{\"karma\":\"#karma\"}'"));
|
||||
const cmd = try Command.parse(arena.allocator(), "/extract '{\"karma\":\"#karma\"}' save=front");
|
||||
try testing.expectString("front", cmd.tool_call.args.?.object.get("save").?.string);
|
||||
}
|
||||
|
||||
test "parse: /scroll y=200" {
|
||||
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
Reference in New Issue
Block a user