@swmansion/argent 0.32.1-next.10 → 0.32.1-next.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli-cmds.mjs CHANGED
@@ -8917,7 +8917,7 @@ var require_src = __commonJS({
8917
8917
 
8918
8918
  // ../argent-cli/src/run.ts
8919
8919
  import * as fs12 from "node:fs";
8920
- import * as path17 from "node:path";
8920
+ import * as path18 from "node:path";
8921
8921
 
8922
8922
  // ../argent-tools-client/src/launcher.ts
8923
8923
  import * as net from "node:net";
@@ -10452,6 +10452,8 @@ var FAILURE_CODES = {
10452
10452
  CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
10453
10453
  CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
10454
10454
  CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
10455
+ // The result carried the tool-server's `failure` field.
10456
+ CLI_RUN_TOOL_RESULT_FAILED: "CLI_RUN_TOOL_RESULT_FAILED",
10455
10457
  CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
10456
10458
  TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
10457
10459
  TOOL_PLATFORM_NOT_IMPLEMENTED: "TOOL_PLATFORM_NOT_IMPLEMENTED",
@@ -31651,7 +31653,8 @@ async function consumeToolStream(name, body, onProgress, signal) {
31651
31653
  final = {
31652
31654
  data: msg.data,
31653
31655
  note: msg.note,
31654
- ...msg.profiling === true ? { profiling: true } : {}
31656
+ ...msg.profiling === true ? { profiling: true } : {},
31657
+ ...typeof msg.failure === "string" ? { failure: msg.failure } : {}
31655
31658
  };
31656
31659
  } else if (msg.event === "error") {
31657
31660
  throw new ToolInvocationError(msg.error ?? "tool invocation failed", {
@@ -31703,7 +31706,8 @@ async function consumeToolStream(name, body, onProgress, signal) {
31703
31706
  return {
31704
31707
  data: final.data,
31705
31708
  note: final.note,
31706
- ...final.profiling ? { profiling: true } : {}
31709
+ ...final.profiling ? { profiling: true } : {},
31710
+ ...final.failure !== void 0 ? { failure: final.failure } : {}
31707
31711
  };
31708
31712
  }
31709
31713
  function errorBodyMessage(body) {
@@ -31868,6 +31872,7 @@ function createToolsClient(options = {}) {
31868
31872
  }
31869
31873
  const shadowWarning = await e2eShadowWarningFor(name, args);
31870
31874
  const baselineDirs = [];
31875
+ let unwrittenBaselines2 = [];
31871
31876
  if (meta3?.fileInputs?.length) {
31872
31877
  if (remote) assertRequiredPresent(meta3, finalArgs);
31873
31878
  finalArgs = await prepareFileInputs(meta3.fileInputs, finalArgs ?? {}, {
@@ -31881,8 +31886,9 @@ function createToolsClient(options = {}) {
31881
31886
  signal: opts?.signal
31882
31887
  });
31883
31888
  }
31884
- const settle2 = async (data) => {
31885
- const applied = await applyClientFileDirectives(data, { allowedDirs: baselineDirs });
31889
+ const settle2 = async (data2) => {
31890
+ const applied = await applyClientFileDirectives(data2, { allowedDirs: baselineDirs });
31891
+ unwrittenBaselines2 = applied.failed.map((f) => f.path);
31886
31892
  for (const { path: file2, error: error63 } of applied.failed) {
31887
31893
  diagnose(`The baseline ${file2} was not written on this client: ${error63}`);
31888
31894
  }
@@ -31899,6 +31905,7 @@ function createToolsClient(options = {}) {
31899
31905
  }
31900
31906
  return { ...result, warning: shadowWarning };
31901
31907
  };
31908
+ const unwrittenFailure = () => unwrittenBaselines2.length > 0 ? `Baselines not written on this client: ${unwrittenBaselines2.join(", ")}` : void 0;
31902
31909
  const stream = opts?.onProgress !== void 0 || carriesMembers(finalArgs);
31903
31910
  const sentOnce = carriesUpload(finalArgs);
31904
31911
  const forwarded = remote ? await forwardedSecrets(finalArgs) : void 0;
@@ -31962,11 +31969,14 @@ function createToolsClient(options = {}) {
31962
31969
  }),
31963
31970
  opts?.signal
31964
31971
  );
31972
+ const data2 = await settle2(streamed.data);
31973
+ const failure3 = streamed.failure ?? unwrittenFailure();
31965
31974
  return {
31966
- data: await settle2(streamed.data),
31975
+ data: data2,
31967
31976
  note: streamed.note,
31968
31977
  outputHint: meta3?.outputHint,
31969
- ...streamed.profiling ? { profiling: true } : {}
31978
+ ...streamed.profiling ? { profiling: true } : {},
31979
+ ...failure3 !== void 0 ? { failure: failure3 } : {}
31970
31980
  };
31971
31981
  }
31972
31982
  let json2;
@@ -32002,11 +32012,14 @@ function createToolsClient(options = {}) {
32002
32012
  });
32003
32013
  }
32004
32014
  opts?.signal?.throwIfAborted();
32015
+ const data = await settle2(json2.data);
32016
+ const failure2 = (typeof json2.failure === "string" ? json2.failure : void 0) ?? unwrittenFailure();
32005
32017
  return {
32006
- data: await settle2(json2.data),
32018
+ data,
32007
32019
  note: json2.note,
32008
32020
  outputHint: meta3?.outputHint,
32009
- ...json2.profiling === true ? { profiling: true } : {}
32021
+ ...json2.profiling === true ? { profiling: true } : {},
32022
+ ...failure2 !== void 0 ? { failure: failure2 } : {}
32010
32023
  };
32011
32024
  }
32012
32025
  return { fetchTools, fetchTool, callTool, baseUrl };
@@ -36760,391 +36773,106 @@ function isValidationIssue(value) {
36760
36773
  return typeof issue2.code === "string" && Array.isArray(issue2.path) && typeof issue2.message === "string";
36761
36774
  }
36762
36775
 
36763
- // ../argent-cli/src/run.ts
36764
- function splitOptions(argv) {
36765
- let json2 = false;
36766
- let outPath = null;
36767
- const rest = [];
36776
+ // ../argent-cli/src/e2e.ts
36777
+ import * as fsp from "node:fs/promises";
36778
+ import { constants as fsConstants2 } from "node:fs";
36779
+ import { createHash as createHash5 } from "node:crypto";
36780
+ import * as path17 from "node:path";
36781
+
36782
+ // ../argent-cli/src/command-args.ts
36783
+ var UsageError = class extends Error {
36784
+ constructor(message) {
36785
+ super(message);
36786
+ this.name = "UsageError";
36787
+ }
36788
+ };
36789
+ function listChoices(choices) {
36790
+ const quoted = choices.map((c2) => `"${c2}"`);
36791
+ if (quoted.length <= 1) return quoted.join("");
36792
+ return `${quoted.slice(0, -1).join(", ")} or ${quoted[quoted.length - 1]}`;
36793
+ }
36794
+ function resolveName(tok, specs) {
36795
+ if (tok.startsWith("--")) return tok.slice(2) in specs ? tok.slice(2) : null;
36796
+ const short = tok.slice(1);
36797
+ for (const [name, spec] of Object.entries(specs)) {
36798
+ if (spec.alias === short) return name;
36799
+ }
36800
+ return null;
36801
+ }
36802
+ function parseCommandArgs(argv, specs) {
36803
+ const positionals = [];
36804
+ const options = {};
36768
36805
  for (let i2 = 0; i2 < argv.length; i2++) {
36769
36806
  const tok = argv[i2];
36770
- if (tok === "--json") {
36771
- json2 = true;
36772
- continue;
36807
+ if (tok === "--") {
36808
+ positionals.push(...argv.slice(i2 + 1));
36809
+ break;
36773
36810
  }
36774
- if (tok === "--out") {
36775
- const v = argv[i2 + 1];
36776
- if (!v) throw new FlagParseException("--out requires a path");
36777
- outPath = v;
36778
- i2 += 1;
36811
+ if (!tok.startsWith("-") || tok === "-") {
36812
+ positionals.push(tok);
36779
36813
  continue;
36780
36814
  }
36781
- if (tok.startsWith("--out=")) {
36782
- outPath = tok.slice("--out=".length);
36815
+ const eq = tok.startsWith("--") ? tok.indexOf("=") : -1;
36816
+ const flag = eq === -1 ? tok : tok.slice(0, eq);
36817
+ const inlineValue = eq === -1 ? void 0 : tok.slice(eq + 1);
36818
+ const name = resolveName(flag, specs);
36819
+ if (name === null) throw new UsageError(`Unknown flag: ${tok}`);
36820
+ const spec = specs[name];
36821
+ const display = `--${name}`;
36822
+ if (spec.kind === "boolean") {
36823
+ if (inlineValue !== void 0) throw new UsageError(`${display} does not take a value`);
36824
+ const next = argv[i2 + 1]?.trim().toLowerCase();
36825
+ if (next === "true" || next === "false") {
36826
+ throw new UsageError(
36827
+ `${display} does not take a value \u2014 it is a switch; omit it to leave the option off`
36828
+ );
36829
+ }
36830
+ options[name] = true;
36783
36831
  continue;
36784
36832
  }
36785
- rest.push(tok);
36786
- }
36787
- return { json: json2, outPath, argvForFlags: rest };
36788
- }
36789
- async function readStdin() {
36790
- return new Promise((resolve13, reject) => {
36791
- let data = "";
36792
- process.stdin.setEncoding("utf8");
36793
- process.stdin.on("data", (chunk) => {
36794
- data += chunk;
36795
- });
36796
- process.stdin.on("end", () => resolve13(data));
36797
- process.stdin.on("error", reject);
36798
- });
36799
- }
36800
- function printToolHelp(meta3) {
36801
- const description = meta3.description?.trim() ?? "";
36802
- const schema = meta3.inputSchema;
36803
- const hasOwnArgsField = schema?.properties?.args !== void 0;
36804
- console.log(`argent run ${meta3.name} [flags]`);
36805
- if (description) console.log(`
36806
- ${description}
36807
- `);
36808
- console.log("Flags:");
36809
- console.log(formatSchemaUsage(schema));
36810
- console.log("\nGlobal flags:");
36811
- if (!hasOwnArgsField) {
36812
- console.log(" --args <json> Pass the entire payload as JSON (overrides flags)");
36813
- console.log(" --args - Read the entire payload as JSON from stdin");
36814
- }
36815
- console.log(" --<field>-json <json> Pass a single field as JSON (objects/nested arrays)");
36816
- console.log(" --json Print the raw JSON result");
36817
- console.log(" --out <path> For image results, save to <path> instead of fetching URL");
36818
- console.log(" --help, -h Show this help");
36819
- }
36820
- async function fetchImageToFile(result, outPath) {
36821
- const url2 = result.url;
36822
- if (!url2) {
36823
- throw new Error("Tool result did not include a `url`; cannot save image");
36824
- }
36825
- const res = await fetch(url2);
36826
- if (!res.ok) throw new Error(`Failed to download image: ${res.status} ${res.statusText}`);
36827
- const buf = Buffer.from(await res.arrayBuffer());
36828
- fs12.mkdirSync(path17.dirname(path17.resolve(outPath)), { recursive: true });
36829
- fs12.writeFileSync(outPath, buf);
36830
- }
36831
- function renderResult(result, outputHint, images, json2) {
36832
- if (json2) return JSON.stringify(result, null, 2);
36833
- if (outputHint === "image") {
36834
- if (images.length > 0) return `Saved screenshot: ${images[0].localPath}`;
36835
- if (result && typeof result === "object" && "path" in result && typeof result.path === "string") {
36836
- return `Saved screenshot: ${result.path}`;
36833
+ let value = inlineValue;
36834
+ if (value === void 0) {
36835
+ const next = argv[i2 + 1];
36836
+ if (next !== void 0 && (!next.startsWith("-") || next === "-")) {
36837
+ value = next;
36838
+ i2 += 1;
36839
+ }
36840
+ } else if (value === "") {
36841
+ value = void 0;
36842
+ }
36843
+ if (value === void 0) throw new UsageError(`${display} requires a value`);
36844
+ if (spec.choices && !spec.choices.includes(value)) {
36845
+ throw new UsageError(`${display} must be ${listChoices(spec.choices)}, got "${value}"`);
36837
36846
  }
36847
+ options[name] = value;
36838
36848
  }
36839
- if (typeof result === "string") return result;
36840
- return JSON.stringify(result, null, 2);
36849
+ return { positionals, options };
36841
36850
  }
36842
- var SAFE_TOOL_RE = /^[a-z][a-z0-9_-]{0,63}$/;
36843
- function safeToolName(toolName) {
36844
- return toolName && SAFE_TOOL_RE.test(toolName) ? toolName : "unknown";
36851
+
36852
+ // ../argent-cli/src/e2e.ts
36853
+ var STATUS_GLYPH = {
36854
+ pass: "\u2713",
36855
+ fail: "\u2717",
36856
+ error: "\u2717",
36857
+ skip: "\xB7"
36858
+ };
36859
+ var MAX_RENDER_DEPTH = 20;
36860
+ function stepIndent(depth) {
36861
+ if (typeof depth !== "number" || !Number.isInteger(depth) || depth <= 0) return "";
36862
+ return " ".repeat(Math.min(depth, MAX_RENDER_DEPTH));
36845
36863
  }
36846
- async function trackRunFailure(toolName, startedAt, signal) {
36847
- const tool = toolName ? canonicalToolId(toolName) : toolName;
36848
- track("cli:run_fail", {
36849
- tool: safeToolName(tool),
36850
- ...tool !== toolName ? { alias_used: true } : {},
36851
- duration_ms: performance.now() - startedAt,
36852
- ...signal
36853
- });
36854
- await shutdown();
36864
+ function durationSuffix(ms) {
36865
+ if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return "";
36866
+ const tenths = Math.round(ms / 100);
36867
+ if (tenths < 600) return ` (${(tenths / 10).toFixed(1)}s)`;
36868
+ const seconds = Math.round(ms / 1e3);
36869
+ return ` (${Math.floor(seconds / 60)}m ${seconds % 60}s)`;
36855
36870
  }
36856
- async function run(argv, options) {
36857
- init("cli");
36858
- const startedAt = performance.now();
36859
- const { fetchTool, callTool, baseUrl } = createToolsClient({ paths: options.paths });
36860
- const [toolName, ...rest] = argv;
36861
- if (!toolName || toolName === "--help" || toolName === "-h") {
36862
- console.log(`Usage: argent run <tool> [flags]
36871
+ function printHelp(toStderr = false) {
36872
+ const print = toStderr ? console.error : console.log;
36873
+ print(`Usage: argent e2e <subcommand> [options]
36863
36874
 
36864
- Invoke a tool exposed by the argent tool-server. Run \`argent tools\` to list
36865
- available tools, or \`argent tools describe <name>\` to see one tool's flags.
36866
-
36867
- Examples:
36868
- argent run list-devices
36869
- argent run gesture-tap --udid <UDID> --x 0.5 --y 0.5
36870
- argent run screenshot --udid <UDID> --out ./screen.png
36871
- argent run run-sequence --udid <UDID> --steps-json '[{"tool":"button","args":{"button":"home"}}]'
36872
- argent run gesture-tap --args '{"udid":"<UDID>","x":0.5,"y":0.5}'
36873
- `);
36874
- return;
36875
- }
36876
- let cliOptions;
36877
- try {
36878
- cliOptions = splitOptions(rest);
36879
- } catch (err) {
36880
- if (err instanceof FlagParseException) {
36881
- console.error(`Error: ${err.message}
36882
- `);
36883
- console.error(`Run \`argent run ${toolName} --help\` to see this tool's flags.`);
36884
- await trackRunFailure(toolName, startedAt, {
36885
- error_code: FAILURE_CODES.CLI_RUN_FLAG_PARSE_FAILED,
36886
- failure_stage: "cli_run_split_options",
36887
- failure_area: "cli",
36888
- error_kind: "validation"
36889
- });
36890
- process.exit(2);
36891
- }
36892
- throw err;
36893
- }
36894
- const { json: json2, outPath, argvForFlags } = cliOptions;
36895
- const meta3 = await fetchTool(toolName);
36896
- if (!meta3) {
36897
- console.error(`Tool "${toolName}" not found. Run \`argent tools\` to list available tools.`);
36898
- await trackRunFailure(toolName, startedAt, {
36899
- error_code: FAILURE_CODES.CLI_RUN_TOOL_NOT_FOUND,
36900
- failure_stage: "cli_run_fetch_tool",
36901
- failure_area: "cli",
36902
- error_kind: "not_found"
36903
- });
36904
- process.exit(1);
36905
- }
36906
- const schema = meta3.inputSchema;
36907
- const failInvocation = async (summary, report, failure2) => {
36908
- if (json2) {
36909
- console.error(
36910
- JSON.stringify(
36911
- {
36912
- error: summary,
36913
- missing: report ? missingFlagNames(report, schema) : [],
36914
- issues: report?.rawIssues ?? []
36915
- },
36916
- null,
36917
- 2
36918
- )
36919
- );
36920
- } else {
36921
- console.error(`Error: ${summary}
36922
- `);
36923
- printToolHelp(meta3);
36924
- }
36925
- await trackRunFailure(toolName, startedAt, {
36926
- ...failure2,
36927
- failure_area: "cli",
36928
- error_kind: "validation"
36929
- });
36930
- process.exit(2);
36931
- };
36932
- const failValidation = (report, stage) => failInvocation(formatValidationError(report, schema), report, {
36933
- error_code: FAILURE_CODES.CLI_RUN_INPUT_VALIDATION_FAILED,
36934
- failure_stage: stage
36935
- });
36936
- let parsed;
36937
- try {
36938
- parsed = parseFlags(argvForFlags, schema);
36939
- } catch (err) {
36940
- if (err instanceof FlagParseException) {
36941
- await failInvocation(err.message, null, {
36942
- error_code: FAILURE_CODES.CLI_RUN_FLAG_PARSE_FAILED,
36943
- failure_stage: "cli_run_parse_flags"
36944
- });
36945
- }
36946
- throw err;
36947
- }
36948
- if (parsed.helpRequested) {
36949
- printToolHelp(meta3);
36950
- return;
36951
- }
36952
- if (parsed.positional.length > 0) {
36953
- console.error(
36954
- `Note: ignoring unused argument(s): ${parsed.positional.join(", ")}. Pass values as --flag <value> or --flag=<value>.`
36955
- );
36956
- }
36957
- let payload = {};
36958
- if (parsed.rawArgs !== null) {
36959
- let rawJson = parsed.rawArgs;
36960
- if (rawJson === "-") {
36961
- rawJson = await readStdin();
36962
- }
36963
- try {
36964
- const parsedRaw = JSON.parse(rawJson);
36965
- if (parsedRaw === null || typeof parsedRaw !== "object" || Array.isArray(parsedRaw)) {
36966
- console.error("--args must be a JSON object");
36967
- await trackRunFailure(toolName, startedAt, {
36968
- error_code: FAILURE_CODES.CLI_RUN_ARGS_NOT_OBJECT,
36969
- failure_stage: "cli_run_parse_raw_args",
36970
- failure_area: "cli",
36971
- error_kind: "validation"
36972
- });
36973
- process.exit(2);
36974
- }
36975
- payload = parsedRaw;
36976
- } catch (err) {
36977
- console.error(`--args is not valid JSON: ${err instanceof Error ? err.message : err}`);
36978
- await trackRunFailure(toolName, startedAt, {
36979
- error_code: FAILURE_CODES.CLI_RUN_ARGS_JSON_INVALID,
36980
- failure_stage: "cli_run_parse_raw_args",
36981
- failure_area: "cli",
36982
- error_kind: "validation"
36983
- });
36984
- process.exit(2);
36985
- }
36986
- }
36987
- for (const [k, v] of Object.entries(parsed.args)) {
36988
- payload[k] = v;
36989
- }
36990
- const missing = findMissingRequired(payload, schema);
36991
- if (missing.length > 0) {
36992
- await failValidation({ missing, invalid: [], rawIssues: null }, "cli_run_required_flags");
36993
- }
36994
- let result;
36995
- let note;
36996
- let images = [];
36997
- try {
36998
- const resp = await callTool(toolName, payload);
36999
- const { url: url2, token } = await baseUrl();
37000
- const materialized = await materializeArtifacts(resp.data, {
37001
- toolsUrl: url2,
37002
- authToken: token,
37003
- deviceId: getDeviceIdFromArgs(payload)
37004
- });
37005
- result = materialized.result;
37006
- images = materialized.images;
37007
- note = resp.note;
37008
- } catch (err) {
37009
- const report = describeServerValidationFailure(err, payload, schema);
37010
- if (report) {
37011
- await failValidation(report, "cli_run_server_validation");
37012
- }
37013
- console.error(err instanceof Error ? err.message : String(err));
37014
- await trackRunFailure(toolName, startedAt, {
37015
- error_code: FAILURE_CODES.CLI_RUN_TOOL_CALL_FAILED,
37016
- failure_stage: "cli_run_call_tool",
37017
- failure_area: "cli",
37018
- error_kind: "unknown"
37019
- });
37020
- process.exit(1);
37021
- }
37022
- if (outPath && meta3.outputHint === "image") {
37023
- try {
37024
- if (images.length > 0) {
37025
- fs12.mkdirSync(path17.dirname(path17.resolve(outPath)), { recursive: true });
37026
- fs12.writeFileSync(outPath, images[0].data);
37027
- } else if (result && typeof result === "object") {
37028
- await fetchImageToFile(result, outPath);
37029
- }
37030
- } catch (err) {
37031
- console.error(`Failed to save image: ${err instanceof Error ? err.message : err}`);
37032
- await trackRunFailure(toolName, startedAt, {
37033
- error_code: FAILURE_CODES.CLI_RUN_SAVE_IMAGE_FAILED,
37034
- failure_stage: "cli_run_save_image",
37035
- failure_area: "cli",
37036
- error_kind: "unknown"
37037
- });
37038
- process.exit(1);
37039
- }
37040
- }
37041
- if (note) console.error(note);
37042
- console.log(renderResult(result, meta3.outputHint, images, json2));
37043
- if (outPath && meta3.outputHint === "image" && !json2) {
37044
- console.log(`Wrote: ${outPath}`);
37045
- }
37046
- }
37047
-
37048
- // ../argent-cli/src/e2e.ts
37049
- import * as fsp from "node:fs/promises";
37050
- import { constants as fsConstants2 } from "node:fs";
37051
- import { createHash as createHash5 } from "node:crypto";
37052
- import * as path18 from "node:path";
37053
-
37054
- // ../argent-cli/src/command-args.ts
37055
- var UsageError = class extends Error {
37056
- constructor(message) {
37057
- super(message);
37058
- this.name = "UsageError";
37059
- }
37060
- };
37061
- function listChoices(choices) {
37062
- const quoted = choices.map((c2) => `"${c2}"`);
37063
- if (quoted.length <= 1) return quoted.join("");
37064
- return `${quoted.slice(0, -1).join(", ")} or ${quoted[quoted.length - 1]}`;
37065
- }
37066
- function resolveName(tok, specs) {
37067
- if (tok.startsWith("--")) return tok.slice(2) in specs ? tok.slice(2) : null;
37068
- const short = tok.slice(1);
37069
- for (const [name, spec] of Object.entries(specs)) {
37070
- if (spec.alias === short) return name;
37071
- }
37072
- return null;
37073
- }
37074
- function parseCommandArgs(argv, specs) {
37075
- const positionals = [];
37076
- const options = {};
37077
- for (let i2 = 0; i2 < argv.length; i2++) {
37078
- const tok = argv[i2];
37079
- if (tok === "--") {
37080
- positionals.push(...argv.slice(i2 + 1));
37081
- break;
37082
- }
37083
- if (!tok.startsWith("-") || tok === "-") {
37084
- positionals.push(tok);
37085
- continue;
37086
- }
37087
- const eq = tok.startsWith("--") ? tok.indexOf("=") : -1;
37088
- const flag = eq === -1 ? tok : tok.slice(0, eq);
37089
- const inlineValue = eq === -1 ? void 0 : tok.slice(eq + 1);
37090
- const name = resolveName(flag, specs);
37091
- if (name === null) throw new UsageError(`Unknown flag: ${tok}`);
37092
- const spec = specs[name];
37093
- const display = `--${name}`;
37094
- if (spec.kind === "boolean") {
37095
- if (inlineValue !== void 0) throw new UsageError(`${display} does not take a value`);
37096
- const next = argv[i2 + 1]?.trim().toLowerCase();
37097
- if (next === "true" || next === "false") {
37098
- throw new UsageError(
37099
- `${display} does not take a value \u2014 it is a switch; omit it to leave the option off`
37100
- );
37101
- }
37102
- options[name] = true;
37103
- continue;
37104
- }
37105
- let value = inlineValue;
37106
- if (value === void 0) {
37107
- const next = argv[i2 + 1];
37108
- if (next !== void 0 && (!next.startsWith("-") || next === "-")) {
37109
- value = next;
37110
- i2 += 1;
37111
- }
37112
- } else if (value === "") {
37113
- value = void 0;
37114
- }
37115
- if (value === void 0) throw new UsageError(`${display} requires a value`);
37116
- if (spec.choices && !spec.choices.includes(value)) {
37117
- throw new UsageError(`${display} must be ${listChoices(spec.choices)}, got "${value}"`);
37118
- }
37119
- options[name] = value;
37120
- }
37121
- return { positionals, options };
37122
- }
37123
-
37124
- // ../argent-cli/src/e2e.ts
37125
- var STATUS_GLYPH = {
37126
- pass: "\u2713",
37127
- fail: "\u2717",
37128
- error: "\u2717",
37129
- skip: "\xB7"
37130
- };
37131
- var MAX_RENDER_DEPTH = 20;
37132
- function stepIndent(depth) {
37133
- if (typeof depth !== "number" || !Number.isInteger(depth) || depth <= 0) return "";
37134
- return " ".repeat(Math.min(depth, MAX_RENDER_DEPTH));
37135
- }
37136
- function durationSuffix(ms) {
37137
- if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return "";
37138
- const tenths = Math.round(ms / 100);
37139
- if (tenths < 600) return ` (${(tenths / 10).toFixed(1)}s)`;
37140
- const seconds = Math.round(ms / 1e3);
37141
- return ` (${Math.floor(seconds / 60)}m ${seconds % 60}s)`;
37142
- }
37143
- function printHelp(toStderr = false) {
37144
- const print = toStderr ? console.error : console.log;
37145
- print(`Usage: argent e2e <subcommand> [options]
37146
-
37147
- Run a YAML e2e test without an LLM in the loop. \`run\` takes any of these forms:
36875
+ Run a YAML e2e test without an LLM in the loop. \`run\` takes any of these forms:
37148
36876
 
37149
36877
  a name An e2e test saved under .argent/e2e or .argent/flows, resolved
37150
36878
  from the current directory. "checkout" runs
@@ -37405,14 +37133,14 @@ function shellQuoteArg(arg) {
37405
37133
  return `'${arg.replaceAll("'", "'\\''")}'`;
37406
37134
  }
37407
37135
  function rerunCommand(flowPath, rel, projectRoot, args) {
37408
- const pathArg = (p) => shellQuoteArg(p.startsWith("-") ? `.${path18.sep}${p}` : p);
37409
- const fromCwd = path18.relative(projectRoot, flowPath);
37410
- const shown = path18.isAbsolute(fromCwd) || fromCwd.split(/[\\/]+/).includes("..") ? flowPath : fromCwd;
37136
+ const pathArg = (p) => shellQuoteArg(p.startsWith("-") ? `.${path17.sep}${p}` : p);
37137
+ const fromCwd = path17.relative(projectRoot, flowPath);
37138
+ const shown = path17.isAbsolute(fromCwd) || fromCwd.split(/[\\/]+/).includes("..") ? flowPath : fromCwd;
37411
37139
  const parts = ["argent e2e run", pathArg(shown)];
37412
37140
  if (args.device) parts.push("--device", shellQuoteArg(args.device));
37413
37141
  if (args.platform) parts.push("--platform", shellQuoteArg(args.platform));
37414
37142
  if (args.updateBaselines) parts.push("--update-baselines");
37415
- if (args.output) parts.push("--output", pathArg(path18.join(args.output, path18.dirname(rel))));
37143
+ if (args.output) parts.push("--output", pathArg(path17.join(args.output, path17.dirname(rel))));
37416
37144
  return parts.join(" ");
37417
37145
  }
37418
37146
  var EXPORT_SOURCE_MARKER = ".argent-e2e-source";
@@ -37420,8 +37148,8 @@ var LEGACY_EXPORT_SOURCE_MARKER = ".argent-flow-source";
37420
37148
  var MARKER_READ_RETRIES = 4;
37421
37149
  var MARKER_READ_DELAY_MS = 5;
37422
37150
  async function readExportMarker(dir) {
37423
- const owner = await readMarkerFile(path18.join(dir, EXPORT_SOURCE_MARKER));
37424
- return owner !== void 0 ? owner : await readMarkerFile(path18.join(dir, LEGACY_EXPORT_SOURCE_MARKER));
37151
+ const owner = await readMarkerFile(path17.join(dir, EXPORT_SOURCE_MARKER));
37152
+ return owner !== void 0 ? owner : await readMarkerFile(path17.join(dir, LEGACY_EXPORT_SOURCE_MARKER));
37425
37153
  }
37426
37154
  async function readMarkerFile(marker) {
37427
37155
  for (let attempt = 0; ; attempt++) {
@@ -37458,7 +37186,7 @@ function occupiedBy(owner) {
37458
37186
  async function takeExportDir(dir, flowPath) {
37459
37187
  try {
37460
37188
  await fsp.mkdir(dir, { recursive: true });
37461
- await fsp.writeFile(path18.join(dir, EXPORT_SOURCE_MARKER), `${flowPath}
37189
+ await fsp.writeFile(path17.join(dir, EXPORT_SOURCE_MARKER), `${flowPath}
37462
37190
  `, { flag: "wx" });
37463
37191
  return null;
37464
37192
  } catch (err) {
@@ -37477,7 +37205,7 @@ async function claimExportDirName(outputDir, flowPath, stem) {
37477
37205
  }
37478
37206
  const avoided = [];
37479
37207
  for (const name of candidates) {
37480
- const dir = path18.join(outputDir, name);
37208
+ const dir = path17.join(outputDir, name);
37481
37209
  const claim2 = await classifyExportDir(dir, flowPath);
37482
37210
  const reason = claim2.state === "foreign" ? occupiedBy(claim2.owner) : await takeExportDir(dir, flowPath);
37483
37211
  if (reason !== null) {
@@ -37497,7 +37225,7 @@ async function claimExportDirName(outputDir, flowPath, stem) {
37497
37225
  return null;
37498
37226
  }
37499
37227
  async function exportFailureArtifacts(report, outputDir, flowPath, ctx) {
37500
- const stem = path18.basename(flowPath, ".yaml");
37228
+ const stem = path17.basename(flowPath, ".yaml");
37501
37229
  if (!SAFE_ARTIFACT_NAME.test(stem)) {
37502
37230
  console.error(
37503
37231
  `warning: skipping artifact export for unsafe e2e test filename ${JSON.stringify(stem)}`
@@ -37530,11 +37258,11 @@ async function exportFailureArtifacts(report, outputDir, flowPath, ctx) {
37530
37258
  if (dir === null) {
37531
37259
  const dirName = await claimExportDirName(outputDir, flowPath, stem);
37532
37260
  if (dirName === null) return;
37533
- dir = path18.join(outputDir, dirName);
37261
+ dir = path17.join(outputDir, dirName);
37534
37262
  }
37535
- const dest = path18.join(dir, fileName(role));
37536
- const rel = path18.relative(outputDir, dest);
37537
- if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
37263
+ const dest = path17.join(dir, fileName(role));
37264
+ const rel = path17.relative(outputDir, dest);
37265
+ if (rel.startsWith("..") || path17.isAbsolute(rel)) continue;
37538
37266
  try {
37539
37267
  await fsp.copyFile(value, dest);
37540
37268
  s.artifacts[role] = dest;
@@ -37549,7 +37277,7 @@ async function exportFailureArtifacts(report, outputDir, flowPath, ctx) {
37549
37277
  function keyFromBaselinePath(artifacts) {
37550
37278
  const baseline = artifacts.baseline;
37551
37279
  if (typeof baseline !== "string") return null;
37552
- return path18.basename(baseline).replace(/\.png$/, "");
37280
+ return path17.basename(baseline).replace(/\.png$/, "");
37553
37281
  }
37554
37282
  function resolveArtifactDisplayPaths(report) {
37555
37283
  for (const s of report.steps) {
@@ -37606,17 +37334,17 @@ async function collectRunnableFlowPaths(dir, relDir = "") {
37606
37334
  const found = [];
37607
37335
  const entries = await fsp.readdir(dir, { withFileTypes: true });
37608
37336
  for (const entry of entries) {
37609
- const rel = relDir ? path18.join(relDir, entry.name) : entry.name;
37337
+ const rel = relDir ? path17.join(relDir, entry.name) : entry.name;
37610
37338
  if (entry.isDirectory()) {
37611
37339
  if (entry.name === "__baselines__") continue;
37612
37340
  found.push(
37613
- ...await collectRunnableFlowPaths(path18.join(dir, entry.name), rel).catch(() => [])
37341
+ ...await collectRunnableFlowPaths(path17.join(dir, entry.name), rel).catch(() => [])
37614
37342
  );
37615
37343
  continue;
37616
37344
  }
37617
37345
  if (!entry.name.endsWith(".yaml")) continue;
37618
- if (!SAFE_FLOW_NAME.test(path18.basename(entry.name, ".yaml"))) continue;
37619
- if (await isRunnableFlowFile(path18.join(dir, entry.name))) found.push(rel);
37346
+ if (!SAFE_FLOW_NAME.test(path17.basename(entry.name, ".yaml"))) continue;
37347
+ if (await isRunnableFlowFile(path17.join(dir, entry.name))) found.push(rel);
37620
37348
  }
37621
37349
  return found;
37622
37350
  }
@@ -37624,26 +37352,26 @@ async function resolveFlowRef(ref, projectRoot) {
37624
37352
  if (!SAFE_FLOW_NAME.test(ref)) return { suppliedPath: ref, fromName: false };
37625
37353
  const picked = await resolveE2eFile(projectRoot, ref);
37626
37354
  return {
37627
- suppliedPath: path18.relative(projectRoot, picked.path),
37355
+ suppliedPath: path17.relative(projectRoot, picked.path),
37628
37356
  fromName: true,
37629
- ...picked.shadowed === void 0 ? {} : { shadowed: path18.relative(projectRoot, picked.shadowed) }
37357
+ ...picked.shadowed === void 0 ? {} : { shadowed: path17.relative(projectRoot, picked.shadowed) }
37630
37358
  };
37631
37359
  }
37632
37360
  async function savedFlowHint(projectRoot, missingPath) {
37633
- const dirs = E2E_DIRS.map((rel) => ({ rel, abs: path18.resolve(projectRoot, rel) }));
37361
+ const dirs = E2E_DIRS.map((rel) => ({ rel, abs: path17.resolve(projectRoot, rel) }));
37634
37362
  for (const { abs } of dirs) {
37635
- const within = path18.relative(abs, missingPath);
37636
- if (!within.startsWith("..") && !path18.isAbsolute(within)) return "";
37363
+ const within = path17.relative(abs, missingPath);
37364
+ if (!within.startsWith("..") && !path17.isAbsolute(within)) return "";
37637
37365
  }
37638
- const wanted = `${path18.basename(missingPath, ".yaml")}.yaml`;
37366
+ const wanted = `${path17.basename(missingPath, ".yaml")}.yaml`;
37639
37367
  for (const { rel, abs } of dirs) {
37640
37368
  const entries = await fsp.readdir(abs).catch(() => null);
37641
37369
  if (entries === null) continue;
37642
37370
  const actual = entries.includes(wanted) ? wanted : entries.find((name2) => name2.toLowerCase() === wanted.toLowerCase());
37643
37371
  if (actual === void 0) continue;
37644
- const name = path18.basename(actual, ".yaml");
37372
+ const name = path17.basename(actual, ".yaml");
37645
37373
  if (!SAFE_FLOW_NAME.test(name)) return "";
37646
- if (!await isRunnableFlowFile(path18.join(abs, actual))) return "";
37374
+ if (!await isRunnableFlowFile(path17.join(abs, actual))) return "";
37647
37375
  return `
37648
37376
  An e2e test named "${name}" is saved under ${rel} \u2014 did you mean: argent e2e run ${name}`;
37649
37377
  }
@@ -37653,17 +37381,17 @@ async function collectFlowFiles(dir, recursive2) {
37653
37381
  const found = [];
37654
37382
  const walk = async (current, rel) => {
37655
37383
  for (const entry of await fsp.readdir(current, { withFileTypes: true })) {
37656
- const entryRel = rel ? path18.join(rel, entry.name) : entry.name;
37384
+ const entryRel = rel ? path17.join(rel, entry.name) : entry.name;
37657
37385
  if (entry.isDirectory()) {
37658
37386
  if (recursive2 && !entry.name.startsWith(".") && entry.name !== "node_modules") {
37659
- await walk(path18.join(current, entry.name), entryRel).catch(() => {
37387
+ await walk(path17.join(current, entry.name), entryRel).catch(() => {
37660
37388
  });
37661
37389
  }
37662
37390
  continue;
37663
37391
  }
37664
37392
  if (!entry.name.endsWith(".yaml")) continue;
37665
- if (!SAFE_FLOW_NAME.test(path18.basename(entry.name, ".yaml"))) continue;
37666
- if (await isRunnableFlowFile(path18.join(current, entry.name))) found.push(entryRel);
37393
+ if (!SAFE_FLOW_NAME.test(path17.basename(entry.name, ".yaml"))) continue;
37394
+ if (await isRunnableFlowFile(path17.join(current, entry.name))) found.push(entryRel);
37667
37395
  }
37668
37396
  };
37669
37397
  await walk(dir, "");
@@ -37810,7 +37538,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
37810
37538
  );
37811
37539
  }
37812
37540
  const { callTool, baseUrl } = runToolsClient(args, options);
37813
- const outputBase = args.output ? path18.resolve(args.output) : void 0;
37541
+ const outputBase = args.output ? path17.resolve(args.output) : void 0;
37814
37542
  const results = [];
37815
37543
  const failures = [];
37816
37544
  const batchStartedAt = Date.now();
@@ -37823,7 +37551,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
37823
37551
  if (!args.json) console.log(` ${STATUS_GLYPH.skip} not run (batch stopped)`);
37824
37552
  continue;
37825
37553
  }
37826
- const flowPath = path18.join(dir, rel);
37554
+ const flowPath = path17.join(dir, rel);
37827
37555
  const rerun = rerunCommand(flowPath, rel, projectRoot, args);
37828
37556
  let report;
37829
37557
  try {
@@ -37865,7 +37593,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
37865
37593
  printWarnings(nestedStepWarnings(report.steps), args.json);
37866
37594
  await exportAndResolveArtifacts(
37867
37595
  report,
37868
- outputBase ? path18.join(outputBase, path18.dirname(rel)) : void 0,
37596
+ outputBase ? path17.join(outputBase, path17.dirname(rel)) : void 0,
37869
37597
  flowPath,
37870
37598
  baseUrl
37871
37599
  );
@@ -37931,14 +37659,14 @@ async function e2e(argv, options) {
37931
37659
  for (const dir of E2E_DIRS) {
37932
37660
  let rels;
37933
37661
  try {
37934
- rels = (await collectRunnableFlowPaths(path18.join(process.cwd(), dir))).sort();
37662
+ rels = (await collectRunnableFlowPaths(path17.join(process.cwd(), dir))).sort();
37935
37663
  } catch {
37936
37664
  continue;
37937
37665
  }
37938
37666
  anyDir = true;
37939
37667
  for (const rel of rels) {
37940
- const shown = path18.join(dir, rel);
37941
- const name = path18.dirname(rel) === "." ? path18.basename(rel, ".yaml") : void 0;
37668
+ const shown = path17.join(dir, rel);
37669
+ const name = path17.dirname(rel) === "." ? path17.basename(rel, ".yaml") : void 0;
37942
37670
  const usedByName = name === void 0 ? void 0 : byName.get(name);
37943
37671
  if (name !== void 0 && usedByName === void 0) byName.set(name, shown);
37944
37672
  lines.push(shown);
@@ -38009,10 +37737,10 @@ async function e2e(argv, options) {
38009
37737
  }
38010
37738
  if (suppliedPath.split(/[\\/]+/).includes("..")) {
38011
37739
  let recovery = "Pass the fully resolved path to the e2e test's YAML.";
38012
- const suppliedBase2 = path18.basename(suppliedPath);
37740
+ const suppliedBase2 = path17.basename(suppliedPath);
38013
37741
  if (suppliedBase2 !== ".." && suppliedBase2 !== ".") {
38014
37742
  try {
38015
- const resolved = path18.join(await fsp.realpath(path18.dirname(suppliedPath)), suppliedBase2);
37743
+ const resolved = path17.join(await fsp.realpath(path17.dirname(suppliedPath)), suppliedBase2);
38016
37744
  await fsp.stat(resolved);
38017
37745
  recovery = `Did you mean: argent e2e run ${shellQuoteArg(resolved)}`;
38018
37746
  } catch {
@@ -38024,7 +37752,7 @@ async function e2e(argv, options) {
38024
37752
  2
38025
37753
  );
38026
37754
  }
38027
- const resolvedPath = path18.resolve(projectRoot, suppliedPath);
37755
+ const resolvedPath = path17.resolve(projectRoot, suppliedPath);
38028
37756
  let isDirectory = false;
38029
37757
  try {
38030
37758
  isDirectory = (await fsp.stat(resolvedPath)).isDirectory();
@@ -38047,16 +37775,16 @@ async function e2e(argv, options) {
38047
37775
  }
38048
37776
  const separatorTrimmedPath = suppliedPath.replace(/[\\/]+$/, "");
38049
37777
  if (separatorTrimmedPath !== suppliedPath && separatorTrimmedPath !== "") {
38050
- const hint = SAFE_FLOW_NAME.test(separatorTrimmedPath) ? `.${path18.sep}${separatorTrimmedPath}` : separatorTrimmedPath;
37778
+ const hint = SAFE_FLOW_NAME.test(separatorTrimmedPath) ? `.${path17.sep}${separatorTrimmedPath}` : separatorTrimmedPath;
38051
37779
  return fail(
38052
37780
  `E2E test path must not end in a path separator \u2014 the separator claims a directory, which the kernel would refuse to open as a file, so the CLI would run a file this string does not name: ${suppliedPath}
38053
37781
  Did you mean: argent e2e run ${shellQuoteArg(hint)}`,
38054
37782
  2
38055
37783
  );
38056
37784
  }
38057
- if (path18.extname(suppliedPath) !== ".yaml") {
38058
- const looksLikeName = !suppliedPath.includes("/") && !suppliedPath.includes("\\") && path18.extname(suppliedPath) === "";
38059
- if (path18.basename(suppliedPath).toLowerCase() === ".yaml") {
37785
+ if (path17.extname(suppliedPath) !== ".yaml") {
37786
+ const looksLikeName = !suppliedPath.includes("/") && !suppliedPath.includes("\\") && path17.extname(suppliedPath) === "";
37787
+ if (path17.basename(suppliedPath).toLowerCase() === ".yaml") {
38060
37788
  return fail(
38061
37789
  `E2E test filename must have a non-empty name containing only letters, numbers, "_", or "-": ${suppliedPath}`,
38062
37790
  2
@@ -38069,15 +37797,15 @@ Names run \`${E2E_DIRS.map((dir) => `${dir}/<name>.yaml`).join("` or `")}\`; pas
38069
37797
  2
38070
37798
  );
38071
37799
  }
38072
- if (path18.extname(suppliedPath).toLowerCase() === ".yaml") {
37800
+ if (path17.extname(suppliedPath).toLowerCase() === ".yaml") {
38073
37801
  return fail(
38074
- `E2E test extension must be lowercase .yaml, not ${path18.extname(suppliedPath)}: ${suppliedPath}`,
37802
+ `E2E test extension must be lowercase .yaml, not ${path17.extname(suppliedPath)}: ${suppliedPath}`,
38075
37803
  2
38076
37804
  );
38077
37805
  }
38078
37806
  return fail(`E2E test path must end in .yaml: ${suppliedPath}`, 2);
38079
37807
  }
38080
- const flowName = path18.basename(suppliedPath, ".yaml");
37808
+ const flowName = path17.basename(suppliedPath, ".yaml");
38081
37809
  if (!SAFE_FLOW_NAME.test(flowName)) {
38082
37810
  return fail(
38083
37811
  `E2E test filename must have a non-empty name containing only letters, numbers, "_", or "-": ${suppliedPath}`,
@@ -38101,12 +37829,12 @@ No e2e test named "${args.flowRef}" is saved in ${E2E_DIRS.join(" or ")} \u2014
38101
37829
  }
38102
37830
  return fail(`${detail}: ${flowPath}${recovery}`, 2);
38103
37831
  }
38104
- const suppliedBase = path18.basename(flowPath);
38105
- const siblings = await fsp.readdir(path18.dirname(flowPath)).catch(() => null);
37832
+ const suppliedBase = path17.basename(flowPath);
37833
+ const siblings = await fsp.readdir(path17.dirname(flowPath)).catch(() => null);
38106
37834
  if (siblings !== null && !siblings.includes(suppliedBase)) {
38107
37835
  const actual = siblings.find((name) => name.toLowerCase() === suppliedBase.toLowerCase());
38108
- const actualRunnable = actual !== void 0 && path18.extname(actual) === ".yaml" && SAFE_FLOW_NAME.test(path18.basename(actual, ".yaml"));
38109
- const recovery = actualRunnable ? fromName ? `Did you mean: argent e2e run ${path18.basename(actual, ".yaml")}` : `Did you mean: argent e2e run ${shellQuoteArg(path18.join(path18.dirname(suppliedPath), actual))}` : actual !== void 0 ? `Rename ${actual} to ${suppliedBase} to run it \u2014 e2e test files must be lowercase .yaml.` : "Pass the e2e test file's name exactly as it appears on disk.";
37836
+ const actualRunnable = actual !== void 0 && path17.extname(actual) === ".yaml" && SAFE_FLOW_NAME.test(path17.basename(actual, ".yaml"));
37837
+ const recovery = actualRunnable ? fromName ? `Did you mean: argent e2e run ${path17.basename(actual, ".yaml")}` : `Did you mean: argent e2e run ${shellQuoteArg(path17.join(path17.dirname(suppliedPath), actual))}` : actual !== void 0 ? `Rename ${actual} to ${suppliedBase} to run it \u2014 e2e test files must be lowercase .yaml.` : "Pass the e2e test file's name exactly as it appears on disk.";
38110
37838
  return fail(
38111
37839
  `E2E test path must name the file as it appears on disk \u2014 this filesystem matched ${JSON.stringify(suppliedBase)} case-insensitively${actual !== void 0 ? ` to ${JSON.stringify(actual)}` : ""}, so the test name (which keys the report, __baselines__/, and --output) would be one no file carries: ${suppliedPath}
38112
37840
  ${recovery}`,
@@ -38165,7 +37893,7 @@ ${recovery}`,
38165
37893
  try {
38166
37894
  await exportAndResolveArtifacts(
38167
37895
  report,
38168
- args.output ? path18.resolve(args.output) : void 0,
37896
+ args.output ? path17.resolve(args.output) : void 0,
38169
37897
  flowPath,
38170
37898
  baseUrl
38171
37899
  );
@@ -38194,6 +37922,313 @@ ${renderSummary(report, { withDevice: true })}`);
38194
37922
  return exitAfterFlush(report.ok && unwritten.length === 0 ? 0 : 1);
38195
37923
  }
38196
37924
 
37925
+ // ../argent-cli/src/run.ts
37926
+ function splitOptions(argv) {
37927
+ let json2 = false;
37928
+ let outPath = null;
37929
+ const rest = [];
37930
+ for (let i2 = 0; i2 < argv.length; i2++) {
37931
+ const tok = argv[i2];
37932
+ if (tok === "--json") {
37933
+ json2 = true;
37934
+ continue;
37935
+ }
37936
+ if (tok === "--out") {
37937
+ const v = argv[i2 + 1];
37938
+ if (!v) throw new FlagParseException("--out requires a path");
37939
+ outPath = v;
37940
+ i2 += 1;
37941
+ continue;
37942
+ }
37943
+ if (tok.startsWith("--out=")) {
37944
+ outPath = tok.slice("--out=".length);
37945
+ continue;
37946
+ }
37947
+ rest.push(tok);
37948
+ }
37949
+ return { json: json2, outPath, argvForFlags: rest };
37950
+ }
37951
+ async function readStdin() {
37952
+ return new Promise((resolve13, reject) => {
37953
+ let data = "";
37954
+ process.stdin.setEncoding("utf8");
37955
+ process.stdin.on("data", (chunk) => {
37956
+ data += chunk;
37957
+ });
37958
+ process.stdin.on("end", () => resolve13(data));
37959
+ process.stdin.on("error", reject);
37960
+ });
37961
+ }
37962
+ function printToolHelp(meta3) {
37963
+ const description = meta3.description?.trim() ?? "";
37964
+ const schema = meta3.inputSchema;
37965
+ const hasOwnArgsField = schema?.properties?.args !== void 0;
37966
+ console.log(`argent run ${meta3.name} [flags]`);
37967
+ if (description) console.log(`
37968
+ ${description}
37969
+ `);
37970
+ console.log("Flags:");
37971
+ console.log(formatSchemaUsage(schema));
37972
+ console.log("\nGlobal flags:");
37973
+ if (!hasOwnArgsField) {
37974
+ console.log(" --args <json> Pass the entire payload as JSON (overrides flags)");
37975
+ console.log(" --args - Read the entire payload as JSON from stdin");
37976
+ }
37977
+ console.log(" --<field>-json <json> Pass a single field as JSON (objects/nested arrays)");
37978
+ console.log(" --json Print the raw JSON result");
37979
+ console.log(" --out <path> For image results, save to <path> instead of fetching URL");
37980
+ console.log(" --help, -h Show this help");
37981
+ }
37982
+ async function fetchImageToFile(result, outPath) {
37983
+ const url2 = result.url;
37984
+ if (!url2) {
37985
+ throw new Error("Tool result did not include a `url`; cannot save image");
37986
+ }
37987
+ const res = await fetch(url2);
37988
+ if (!res.ok) throw new Error(`Failed to download image: ${res.status} ${res.statusText}`);
37989
+ const buf = Buffer.from(await res.arrayBuffer());
37990
+ fs12.mkdirSync(path18.dirname(path18.resolve(outPath)), { recursive: true });
37991
+ fs12.writeFileSync(outPath, buf);
37992
+ }
37993
+ function renderResult(result, outputHint, images, json2) {
37994
+ if (json2) return JSON.stringify(result, null, 2);
37995
+ if (outputHint === "image") {
37996
+ if (images.length > 0) return `Saved screenshot: ${images[0].localPath}`;
37997
+ if (result && typeof result === "object" && "path" in result && typeof result.path === "string") {
37998
+ return `Saved screenshot: ${result.path}`;
37999
+ }
38000
+ }
38001
+ if (typeof result === "string") return result;
38002
+ return JSON.stringify(result, null, 2);
38003
+ }
38004
+ var SAFE_TOOL_RE = /^[a-z][a-z0-9_-]{0,63}$/;
38005
+ function safeToolName(toolName) {
38006
+ return toolName && SAFE_TOOL_RE.test(toolName) ? toolName : "unknown";
38007
+ }
38008
+ async function trackRunFailure(toolName, startedAt, signal) {
38009
+ const tool = toolName ? canonicalToolId(toolName) : toolName;
38010
+ track("cli:run_fail", {
38011
+ tool: safeToolName(tool),
38012
+ ...tool !== toolName ? { alias_used: true } : {},
38013
+ duration_ms: performance.now() - startedAt,
38014
+ ...signal
38015
+ });
38016
+ await shutdown();
38017
+ }
38018
+ async function run(argv, options) {
38019
+ init("cli");
38020
+ const startedAt = performance.now();
38021
+ const { fetchTool, callTool, baseUrl } = createToolsClient({ paths: options.paths });
38022
+ const [toolName, ...rest] = argv;
38023
+ if (!toolName || toolName === "--help" || toolName === "-h") {
38024
+ console.log(`Usage: argent run <tool> [flags]
38025
+
38026
+ Invoke a tool exposed by the argent tool-server. Run \`argent tools\` to list
38027
+ available tools, or \`argent tools describe <name>\` to see one tool's flags.
38028
+
38029
+ Examples:
38030
+ argent run list-devices
38031
+ argent run gesture-tap --udid <UDID> --x 0.5 --y 0.5
38032
+ argent run screenshot --udid <UDID> --out ./screen.png
38033
+ argent run run-sequence --udid <UDID> --steps-json '[{"tool":"button","args":{"button":"home"}}]'
38034
+ argent run gesture-tap --args '{"udid":"<UDID>","x":0.5,"y":0.5}'
38035
+
38036
+ Exits 1 when the tool fails. Also exits 1 when the result reports that the
38037
+ call did not do its job: an e2e test that does not pass, an await-ui-element
38038
+ wait with success=false, a run-sequence that a step stopped, a debugger tool
38039
+ with no connection or an error, a native-devtools precheck that stopped the
38040
+ tool, a native-profiler-analyze with status analysis_failed, an e2e recording
38041
+ call that recorded nothing, or a baseline that was not written on this client.
38042
+ Other results exit 0, also a negative answer such as await-screen-idle's
38043
+ settled=false or debugger-status's not_connected.
38044
+ `);
38045
+ return;
38046
+ }
38047
+ let cliOptions;
38048
+ try {
38049
+ cliOptions = splitOptions(rest);
38050
+ } catch (err) {
38051
+ if (err instanceof FlagParseException) {
38052
+ console.error(`Error: ${err.message}
38053
+ `);
38054
+ console.error(`Run \`argent run ${toolName} --help\` to see this tool's flags.`);
38055
+ await trackRunFailure(toolName, startedAt, {
38056
+ error_code: FAILURE_CODES.CLI_RUN_FLAG_PARSE_FAILED,
38057
+ failure_stage: "cli_run_split_options",
38058
+ failure_area: "cli",
38059
+ error_kind: "validation"
38060
+ });
38061
+ process.exit(2);
38062
+ }
38063
+ throw err;
38064
+ }
38065
+ const { json: json2, outPath, argvForFlags } = cliOptions;
38066
+ const meta3 = await fetchTool(toolName);
38067
+ if (!meta3) {
38068
+ console.error(`Tool "${toolName}" not found. Run \`argent tools\` to list available tools.`);
38069
+ await trackRunFailure(toolName, startedAt, {
38070
+ error_code: FAILURE_CODES.CLI_RUN_TOOL_NOT_FOUND,
38071
+ failure_stage: "cli_run_fetch_tool",
38072
+ failure_area: "cli",
38073
+ error_kind: "not_found"
38074
+ });
38075
+ process.exit(1);
38076
+ }
38077
+ const schema = meta3.inputSchema;
38078
+ const failInvocation = async (summary, report, failure3) => {
38079
+ if (json2) {
38080
+ console.error(
38081
+ JSON.stringify(
38082
+ {
38083
+ error: summary,
38084
+ missing: report ? missingFlagNames(report, schema) : [],
38085
+ issues: report?.rawIssues ?? []
38086
+ },
38087
+ null,
38088
+ 2
38089
+ )
38090
+ );
38091
+ } else {
38092
+ console.error(`Error: ${summary}
38093
+ `);
38094
+ printToolHelp(meta3);
38095
+ }
38096
+ await trackRunFailure(toolName, startedAt, {
38097
+ ...failure3,
38098
+ failure_area: "cli",
38099
+ error_kind: "validation"
38100
+ });
38101
+ process.exit(2);
38102
+ };
38103
+ const failValidation = (report, stage) => failInvocation(formatValidationError(report, schema), report, {
38104
+ error_code: FAILURE_CODES.CLI_RUN_INPUT_VALIDATION_FAILED,
38105
+ failure_stage: stage
38106
+ });
38107
+ let parsed;
38108
+ try {
38109
+ parsed = parseFlags(argvForFlags, schema);
38110
+ } catch (err) {
38111
+ if (err instanceof FlagParseException) {
38112
+ await failInvocation(err.message, null, {
38113
+ error_code: FAILURE_CODES.CLI_RUN_FLAG_PARSE_FAILED,
38114
+ failure_stage: "cli_run_parse_flags"
38115
+ });
38116
+ }
38117
+ throw err;
38118
+ }
38119
+ if (parsed.helpRequested) {
38120
+ printToolHelp(meta3);
38121
+ return;
38122
+ }
38123
+ if (parsed.positional.length > 0) {
38124
+ console.error(
38125
+ `Note: ignoring unused argument(s): ${parsed.positional.join(", ")}. Pass values as --flag <value> or --flag=<value>.`
38126
+ );
38127
+ }
38128
+ let payload = {};
38129
+ if (parsed.rawArgs !== null) {
38130
+ let rawJson = parsed.rawArgs;
38131
+ if (rawJson === "-") {
38132
+ rawJson = await readStdin();
38133
+ }
38134
+ try {
38135
+ const parsedRaw = JSON.parse(rawJson);
38136
+ if (parsedRaw === null || typeof parsedRaw !== "object" || Array.isArray(parsedRaw)) {
38137
+ console.error("--args must be a JSON object");
38138
+ await trackRunFailure(toolName, startedAt, {
38139
+ error_code: FAILURE_CODES.CLI_RUN_ARGS_NOT_OBJECT,
38140
+ failure_stage: "cli_run_parse_raw_args",
38141
+ failure_area: "cli",
38142
+ error_kind: "validation"
38143
+ });
38144
+ process.exit(2);
38145
+ }
38146
+ payload = parsedRaw;
38147
+ } catch (err) {
38148
+ console.error(`--args is not valid JSON: ${err instanceof Error ? err.message : err}`);
38149
+ await trackRunFailure(toolName, startedAt, {
38150
+ error_code: FAILURE_CODES.CLI_RUN_ARGS_JSON_INVALID,
38151
+ failure_stage: "cli_run_parse_raw_args",
38152
+ failure_area: "cli",
38153
+ error_kind: "validation"
38154
+ });
38155
+ process.exit(2);
38156
+ }
38157
+ }
38158
+ for (const [k, v] of Object.entries(parsed.args)) {
38159
+ payload[k] = v;
38160
+ }
38161
+ const missing = findMissingRequired(payload, schema);
38162
+ if (missing.length > 0) {
38163
+ await failValidation({ missing, invalid: [], rawIssues: null }, "cli_run_required_flags");
38164
+ }
38165
+ let result;
38166
+ let note;
38167
+ let failure2;
38168
+ let images = [];
38169
+ try {
38170
+ const resp = await callTool(toolName, payload);
38171
+ const { url: url2, token } = await baseUrl();
38172
+ const materialized = await materializeArtifacts(resp.data, {
38173
+ toolsUrl: url2,
38174
+ authToken: token,
38175
+ deviceId: getDeviceIdFromArgs(payload)
38176
+ });
38177
+ result = materialized.result;
38178
+ images = materialized.images;
38179
+ note = resp.note;
38180
+ failure2 = resp.failure;
38181
+ } catch (err) {
38182
+ const report = describeServerValidationFailure(err, payload, schema);
38183
+ if (report) {
38184
+ await failValidation(report, "cli_run_server_validation");
38185
+ }
38186
+ console.error(err instanceof Error ? err.message : String(err));
38187
+ await trackRunFailure(toolName, startedAt, {
38188
+ error_code: FAILURE_CODES.CLI_RUN_TOOL_CALL_FAILED,
38189
+ failure_stage: "cli_run_call_tool",
38190
+ failure_area: "cli",
38191
+ error_kind: "unknown"
38192
+ });
38193
+ process.exit(1);
38194
+ }
38195
+ if (outPath && meta3.outputHint === "image") {
38196
+ try {
38197
+ if (images.length > 0) {
38198
+ fs12.mkdirSync(path18.dirname(path18.resolve(outPath)), { recursive: true });
38199
+ fs12.writeFileSync(outPath, images[0].data);
38200
+ } else if (result && typeof result === "object") {
38201
+ await fetchImageToFile(result, outPath);
38202
+ }
38203
+ } catch (err) {
38204
+ console.error(`Failed to save image: ${err instanceof Error ? err.message : err}`);
38205
+ await trackRunFailure(toolName, startedAt, {
38206
+ error_code: FAILURE_CODES.CLI_RUN_SAVE_IMAGE_FAILED,
38207
+ failure_stage: "cli_run_save_image",
38208
+ failure_area: "cli",
38209
+ error_kind: "unknown"
38210
+ });
38211
+ process.exit(1);
38212
+ }
38213
+ }
38214
+ if (note) console.error(note);
38215
+ console.log(renderResult(result, meta3.outputHint, images, json2));
38216
+ if (outPath && meta3.outputHint === "image" && !json2) {
38217
+ console.log(`Wrote: ${outPath}`);
38218
+ }
38219
+ if (failure2) {
38220
+ await new Promise((resolve13) => process.stdout.write("", () => resolve13()));
38221
+ console.error(failure2);
38222
+ await trackRunFailure(toolName, startedAt, {
38223
+ error_code: FAILURE_CODES.CLI_RUN_TOOL_RESULT_FAILED,
38224
+ failure_stage: "cli_run_tool_result",
38225
+ failure_area: "cli",
38226
+ error_kind: "unknown"
38227
+ });
38228
+ await exitAfterFlush(1, [process.stderr]);
38229
+ }
38230
+ }
38231
+
38197
38232
  // ../argent-cli/src/tools.ts
38198
38233
  function summarize(description, max = 80) {
38199
38234
  if (!description) return "";