@swmansion/argent 0.18.1-next.10 → 0.18.1-next.12

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
@@ -6808,6 +6808,7 @@ var FAILURE_CODES = {
6808
6808
  CLI_RUN_FLAG_PARSE_FAILED: "CLI_RUN_FLAG_PARSE_FAILED",
6809
6809
  CLI_RUN_ARGS_NOT_OBJECT: "CLI_RUN_ARGS_NOT_OBJECT",
6810
6810
  CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
6811
+ CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
6811
6812
  CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
6812
6813
  CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
6813
6814
  TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
@@ -8458,6 +8459,12 @@ var FlagParseException = class extends Error {
8458
8459
  function isScalarType(type) {
8459
8460
  return type === "string" || type === "number" || type === "integer" || type === "boolean";
8460
8461
  }
8462
+ function isJsonField(prop) {
8463
+ return prop?.type === "object" || prop?.type === "array" && !isScalarType(prop.items?.type);
8464
+ }
8465
+ function flagNameFor(name, prop) {
8466
+ return isJsonField(prop) ? `--${name}-json` : `--${name}`;
8467
+ }
8461
8468
  function coerceScalar(raw, type, field) {
8462
8469
  if (type === "number") {
8463
8470
  if (raw.trim() === "")
@@ -8622,11 +8629,10 @@ function formatSchemaUsage(schema) {
8622
8629
  return lines.join("\n");
8623
8630
  }
8624
8631
  function renderFlagName(name, prop) {
8625
- if (prop.type === "object" || prop.type === "array" && !isScalarType(prop.items?.type)) {
8626
- return `--${name}-json <json>`;
8627
- }
8628
- if (prop.type === "boolean") return `--${name}`;
8629
- return `--${name} <value>`;
8632
+ const flag = flagNameFor(name, prop);
8633
+ if (isJsonField(prop)) return `${flag} <json>`;
8634
+ if (prop.type === "boolean") return flag;
8635
+ return `${flag} <value>`;
8630
8636
  }
8631
8637
  function renderType(prop) {
8632
8638
  if (prop.enum && Array.isArray(prop.enum)) {
@@ -8640,6 +8646,93 @@ function renderType(prop) {
8640
8646
  return prop.type ?? "any";
8641
8647
  }
8642
8648
 
8649
+ // ../argent-cli/src/run-validation.ts
8650
+ function findMissingRequired(payload, schema) {
8651
+ const required = new Set(schema?.required ?? []);
8652
+ if (required.size === 0) return [];
8653
+ const declared = Object.keys(schema?.properties ?? {});
8654
+ const names = [...declared.filter((n2) => required.has(n2))];
8655
+ for (const name of required) {
8656
+ if (!names.includes(name)) names.push(name);
8657
+ }
8658
+ return names.filter((name) => !Object.hasOwn(payload, name));
8659
+ }
8660
+ function describeServerValidationFailure(err, payload, schema) {
8661
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : null;
8662
+ if (message === null) return null;
8663
+ let parsed;
8664
+ try {
8665
+ parsed = JSON.parse(message);
8666
+ } catch {
8667
+ return null;
8668
+ }
8669
+ if (!Array.isArray(parsed) || parsed.length === 0) return null;
8670
+ if (!parsed.every(isValidationIssue)) return null;
8671
+ const properties = schema?.properties ?? {};
8672
+ const addressesThisTool = (issue) => issue.path.length === 0 || typeof issue.path[0] === "string" && Object.hasOwn(properties, issue.path[0]);
8673
+ if (!parsed.every(addressesThisTool)) return null;
8674
+ const required = new Set(schema?.required ?? []);
8675
+ const missing = [];
8676
+ const invalid = [];
8677
+ for (const issue of parsed) {
8678
+ const head = issue.path[0];
8679
+ if (issue.path.length === 1 && typeof head === "string" && required.has(head) && !Object.hasOwn(payload, head)) {
8680
+ missing.push(head);
8681
+ } else {
8682
+ invalid.push({ path: issue.path, message: issue.message });
8683
+ }
8684
+ }
8685
+ return { missing: sortBySchemaOrder(missing, schema), invalid, rawIssues: parsed };
8686
+ }
8687
+ function formatValidationError(report, schema) {
8688
+ const properties = schema?.properties ?? {};
8689
+ const lines = [];
8690
+ if (report.missing.length > 0) {
8691
+ const flags2 = sortBySchemaOrder(report.missing, schema).map(
8692
+ (name) => flagNameFor(name, properties[name])
8693
+ );
8694
+ const noun = flags2.length === 1 ? "flag" : "flags";
8695
+ lines.push(`missing required ${noun} ${flags2.join(", ")}`);
8696
+ }
8697
+ for (const field of report.invalid) {
8698
+ lines.push(describeInvalidField(field, properties));
8699
+ }
8700
+ return lines.join("\n ");
8701
+ }
8702
+ function missingFlagNames(report, schema) {
8703
+ const properties = schema?.properties ?? {};
8704
+ return sortBySchemaOrder(report.missing, schema).map(
8705
+ (name) => flagNameFor(name, properties[name])
8706
+ );
8707
+ }
8708
+ function describeInvalidField(field, properties) {
8709
+ if (field.path.length === 0) return field.message;
8710
+ const [head, ...rest] = field.path;
8711
+ if (typeof head !== "string") return field.message;
8712
+ const flag = flagNameFor(head, properties[head]);
8713
+ const nested = rest.length > 0 ? ` ${head}${renderPathTail(rest)}` : "";
8714
+ return `${flag}${nested} ${field.message}`;
8715
+ }
8716
+ function renderPathTail(rest) {
8717
+ return rest.map((part) => typeof part === "number" ? `[${part}]` : `.${part}`).join("");
8718
+ }
8719
+ function sortBySchemaOrder(names, schema) {
8720
+ const order = Object.keys(schema?.properties ?? {});
8721
+ return [...names].sort((a2, b) => {
8722
+ const ia = order.indexOf(a2);
8723
+ const ib = order.indexOf(b);
8724
+ if (ia === -1 && ib === -1) return a2.localeCompare(b);
8725
+ if (ia === -1) return 1;
8726
+ if (ib === -1) return -1;
8727
+ return ia - ib;
8728
+ });
8729
+ }
8730
+ function isValidationIssue(value) {
8731
+ if (value === null || typeof value !== "object") return false;
8732
+ const issue = value;
8733
+ return typeof issue.code === "string" && Array.isArray(issue.path) && typeof issue.message === "string";
8734
+ }
8735
+
8643
8736
  // ../argent-cli/src/run.ts
8644
8737
  function splitOptions(argv) {
8645
8738
  let json = false;
@@ -8751,7 +8844,25 @@ Examples:
8751
8844
  `);
8752
8845
  return;
8753
8846
  }
8754
- const { json, outPath, argvForFlags } = splitOptions(rest);
8847
+ let cliOptions;
8848
+ try {
8849
+ cliOptions = splitOptions(rest);
8850
+ } catch (err) {
8851
+ if (err instanceof FlagParseException) {
8852
+ console.error(`Error: ${err.message}
8853
+ `);
8854
+ console.error(`Run \`argent run ${toolName} --help\` to see this tool's flags.`);
8855
+ await trackRunFailure(toolName, startedAt, {
8856
+ error_code: FAILURE_CODES.CLI_RUN_FLAG_PARSE_FAILED,
8857
+ failure_stage: "cli_run_split_options",
8858
+ failure_area: "cli",
8859
+ error_kind: "validation"
8860
+ });
8861
+ process.exit(2);
8862
+ }
8863
+ throw err;
8864
+ }
8865
+ const { json, outPath, argvForFlags } = cliOptions;
8755
8866
  const meta = await fetchTool(toolName);
8756
8867
  if (!meta) {
8757
8868
  console.error(`Tool "${toolName}" not found. Run \`argent tools\` to list available tools.`);
@@ -8763,9 +8874,37 @@ Examples:
8763
8874
  });
8764
8875
  process.exit(1);
8765
8876
  }
8877
+ const schema = meta.inputSchema;
8878
+ const failValidation = async (report, stage) => {
8879
+ const summary = formatValidationError(report, schema);
8880
+ if (json) {
8881
+ console.error(
8882
+ JSON.stringify(
8883
+ {
8884
+ error: summary,
8885
+ missing: missingFlagNames(report, schema),
8886
+ issues: report.rawIssues ?? []
8887
+ },
8888
+ null,
8889
+ 2
8890
+ )
8891
+ );
8892
+ } else {
8893
+ console.error(`Error: ${summary}
8894
+ `);
8895
+ printToolHelp(meta);
8896
+ }
8897
+ await trackRunFailure(toolName, startedAt, {
8898
+ error_code: FAILURE_CODES.CLI_RUN_INPUT_VALIDATION_FAILED,
8899
+ failure_stage: stage,
8900
+ failure_area: "cli",
8901
+ error_kind: "validation"
8902
+ });
8903
+ process.exit(2);
8904
+ };
8766
8905
  let parsed;
8767
8906
  try {
8768
- parsed = parseFlags(argvForFlags, meta.inputSchema);
8907
+ parsed = parseFlags(argvForFlags, schema);
8769
8908
  } catch (err) {
8770
8909
  if (err instanceof FlagParseException) {
8771
8910
  console.error(`Error: ${err.message}
@@ -8818,6 +8957,10 @@ Examples:
8818
8957
  for (const [k, v] of Object.entries(parsed.args)) {
8819
8958
  payload[k] = v;
8820
8959
  }
8960
+ const missing = findMissingRequired(payload, schema);
8961
+ if (missing.length > 0) {
8962
+ await failValidation({ missing, invalid: [], rawIssues: null }, "cli_run_required_flags");
8963
+ }
8821
8964
  let result;
8822
8965
  let note;
8823
8966
  let images = [];
@@ -8833,6 +8976,10 @@ Examples:
8833
8976
  images = materialized.images;
8834
8977
  note = resp.note;
8835
8978
  } catch (err) {
8979
+ const report = describeServerValidationFailure(err, payload, schema);
8980
+ if (report) {
8981
+ await failValidation(report, "cli_run_server_validation");
8982
+ }
8836
8983
  console.error(err instanceof Error ? err.message : String(err));
8837
8984
  await trackRunFailure(toolName, startedAt, {
8838
8985
  error_code: FAILURE_CODES.CLI_RUN_TOOL_CALL_FAILED,
@@ -16034,6 +16034,7 @@ var FAILURE_CODES = {
16034
16034
  CLI_RUN_FLAG_PARSE_FAILED: "CLI_RUN_FLAG_PARSE_FAILED",
16035
16035
  CLI_RUN_ARGS_NOT_OBJECT: "CLI_RUN_ARGS_NOT_OBJECT",
16036
16036
  CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
16037
+ CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
16037
16038
  CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
16038
16039
  CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
16039
16040
  TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
@@ -18064,6 +18064,7 @@ var FAILURE_CODES = {
18064
18064
  CLI_RUN_FLAG_PARSE_FAILED: "CLI_RUN_FLAG_PARSE_FAILED",
18065
18065
  CLI_RUN_ARGS_NOT_OBJECT: "CLI_RUN_ARGS_NOT_OBJECT",
18066
18066
  CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
18067
+ CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
18067
18068
  CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
18068
18069
  CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
18069
18070
  TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
@@ -210,6 +210,7 @@ var init_failure_codes = __esm({
210
210
  CLI_RUN_FLAG_PARSE_FAILED: "CLI_RUN_FLAG_PARSE_FAILED",
211
211
  CLI_RUN_ARGS_NOT_OBJECT: "CLI_RUN_ARGS_NOT_OBJECT",
212
212
  CLI_RUN_ARGS_JSON_INVALID: "CLI_RUN_ARGS_JSON_INVALID",
213
+ CLI_RUN_INPUT_VALIDATION_FAILED: "CLI_RUN_INPUT_VALIDATION_FAILED",
213
214
  CLI_RUN_TOOL_CALL_FAILED: "CLI_RUN_TOOL_CALL_FAILED",
214
215
  CLI_RUN_SAVE_IMAGE_FAILED: "CLI_RUN_SAVE_IMAGE_FAILED",
215
216
  TOOL_CAPABILITY_UNSUPPORTED_OPERATION: "TOOL_CAPABILITY_UNSUPPORTED_OPERATION",
@@ -150716,6 +150717,14 @@ function formatScreenshotDiffSummary(result) {
150716
150717
  const coordinateSpace = coordinateSpaceForSummary(result.imageSize);
150717
150718
  const lines = ["Screenshot diff summary", "", "Overall:"];
150718
150719
  lines.push(`- status: ${status}`);
150720
+ if (result.sizeNormalization) {
150721
+ const { baseline, current, comparedAt } = result.sizeNormalization;
150722
+ lines.push(
150723
+ `- size_normalized: baseline=${formatSize(baseline)} current=${formatSize(current)} compared_at=${formatSize(comparedAt)}`,
150724
+ " - the inputs share an aspect ratio but not a resolution, so the larger was downscaled before comparing; any pixel or text differences below may include resampling artifacts, and the diff images are at compared_at rather than full size",
150725
+ " - re-capture the baseline at the same scale as the current image (screenshot with scale: 1.0) to compare without resampling"
150726
+ );
150727
+ }
150719
150728
  if (result.dimensionMismatch) {
150720
150729
  lines.push(
150721
150730
  `- dimension_mismatch: expected=${formatSize(result.dimensionMismatch.expected)} actual=${formatSize(result.dimensionMismatch.actual)}`
@@ -150777,6 +150786,7 @@ function screenshotDiffStatus(result) {
150777
150786
  if (result.textAnalysis?.status === "ok" && result.textAnalysis.changes.length > 0) {
150778
150787
  return "changed";
150779
150788
  }
150789
+ if (result.sizeNormalization) return "resized_no_change";
150780
150790
  return "unchanged";
150781
150791
  }
150782
150792
  function formatSize(size) {
@@ -151009,6 +151019,11 @@ async function diffPngFiles(options) {
151009
151019
  }
151010
151020
  const baseline = normalized.baseline;
151011
151021
  const current = normalized.current;
151022
+ const sizeNormalization = decodedBaseline.width === decodedCurrent.width && decodedBaseline.height === decodedCurrent.height ? void 0 : {
151023
+ baseline: { width: decodedBaseline.width, height: decodedBaseline.height },
151024
+ current: { width: decodedCurrent.width, height: decodedCurrent.height },
151025
+ comparedAt: { width: baseline.width, height: baseline.height }
151026
+ };
151012
151027
  const totalPixels = baseline.width * baseline.height;
151013
151028
  const pixelDiff = markChangedPixels({
151014
151029
  baseline,
@@ -151051,7 +151066,8 @@ async function diffPngFiles(options) {
151051
151066
  diffPath: artifactPaths.diffPath,
151052
151067
  contextDiffPath: artifactPaths.contextDiffPath,
151053
151068
  regions,
151054
- textAnalysis
151069
+ textAnalysis,
151070
+ ...sizeNormalization && { sizeNormalization }
151055
151071
  });
151056
151072
  }
151057
151073
  function resolveDiffSettings(topMask) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.18.1-next.10",
3
+ "version": "0.18.1-next.12",
4
4
  "mcpName": "io.github.software-mansion/argent",
5
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
6
6
  "license": "Apache-2.0",