@swmansion/argent 0.31.2-next.2 → 0.31.2-next.3
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/bin/argent-android-devtools-0.3.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/bin/darwin/tvos-ax-service +0 -0
- package/bin/darwin/tvos-hid-daemon +0 -0
- package/bin/tcp/ax-service +0 -0
- package/dist/cli-cmds.mjs +94 -24
- package/dist/client.js +71 -14
- package/dist/installer.mjs +8 -1
- package/dist/mcp-server.mjs +75 -17
- package/dist/tool-args.generated.d.ts +15 -7
- package/dist/tool-server.cjs +183 -85
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tcp/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tcp/libKeyboardPatch.dylib +0 -0
- package/dylibs/tcp/libNativeDevtoolsIos.dylib +0 -0
- package/dylibs/tvos/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/tvos/libKeyboardPatch.dylib +0 -0
- package/dylibs/tvos/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +1 -1
- package/skills/argent-create-e2e/references/live-authoring.md +3 -3
package/dist/tool-server.cjs
CHANGED
|
@@ -1323,7 +1323,31 @@ var init_e2e_dirs = __esm({
|
|
|
1323
1323
|
function canonicalToolId(id) {
|
|
1324
1324
|
return Object.hasOwn(TOOL_ID_ALIASES, id) ? TOOL_ID_ALIASES[id] : id;
|
|
1325
1325
|
}
|
|
1326
|
-
|
|
1326
|
+
function toolParamAliases(tool) {
|
|
1327
|
+
const id = canonicalToolId(tool);
|
|
1328
|
+
return Object.hasOwn(TOOL_PARAM_ALIASES, id) ? TOOL_PARAM_ALIASES[id] : {};
|
|
1329
|
+
}
|
|
1330
|
+
function canonicalToolArgs(tool, args) {
|
|
1331
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
1332
|
+
const record2 = args;
|
|
1333
|
+
let out;
|
|
1334
|
+
for (const [former, current] of Object.entries(toolParamAliases(tool))) {
|
|
1335
|
+
if (record2[former] === void 0) continue;
|
|
1336
|
+
if (record2[current] !== void 0) throw new ToolParamAliasError(former, current);
|
|
1337
|
+
out ??= { ...record2 };
|
|
1338
|
+
out[current] = out[former];
|
|
1339
|
+
delete out[former];
|
|
1340
|
+
}
|
|
1341
|
+
return out ?? args;
|
|
1342
|
+
}
|
|
1343
|
+
function withFormerParamSpecs(tool, specs) {
|
|
1344
|
+
const former = Object.entries(toolParamAliases(tool));
|
|
1345
|
+
const copies = specs.flatMap(
|
|
1346
|
+
(spec) => spec.path === `\${${spec.target}}` ? former.filter(([, current]) => current === spec.target).map(([name]) => ({ ...spec, target: name, path: `\${${name}}` })) : []
|
|
1347
|
+
);
|
|
1348
|
+
return copies.length > 0 ? [...specs, ...copies] : specs;
|
|
1349
|
+
}
|
|
1350
|
+
var TOOL_ID_ALIASES, TOOL_PARAM_ALIASES, ToolParamAliasError;
|
|
1327
1351
|
var init_tool_ids = __esm({
|
|
1328
1352
|
"../registry/src/tool-ids.ts"() {
|
|
1329
1353
|
"use strict";
|
|
@@ -1336,6 +1360,19 @@ var init_tool_ids = __esm({
|
|
|
1336
1360
|
"flow-read-prerequisite": "e2e-read-prerequisite",
|
|
1337
1361
|
"flow-execute": "e2e-execute"
|
|
1338
1362
|
});
|
|
1363
|
+
TOOL_PARAM_ALIASES = Object.freeze({
|
|
1364
|
+
"e2e-execute": Object.freeze({ flow_path: "e2e_test_path", flow_file: "e2e_test_file" }),
|
|
1365
|
+
"e2e-read-prerequisite": Object.freeze({
|
|
1366
|
+
flow_path: "e2e_test_path",
|
|
1367
|
+
flow_file: "e2e_test_file"
|
|
1368
|
+
})
|
|
1369
|
+
});
|
|
1370
|
+
ToolParamAliasError = class extends Error {
|
|
1371
|
+
constructor(former, current) {
|
|
1372
|
+
super(`Pass ${current} or its former name ${former}, not both.`);
|
|
1373
|
+
this.name = "ToolParamAliasError";
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1339
1376
|
}
|
|
1340
1377
|
});
|
|
1341
1378
|
|
|
@@ -1382,7 +1419,13 @@ async function resolveFlowRelativeFile(anchorDir, target, addressable) {
|
|
|
1382
1419
|
}
|
|
1383
1420
|
function nestedFlowTarget(args) {
|
|
1384
1421
|
if (typeof args !== "object" || args === null || Array.isArray(args)) return void 0;
|
|
1385
|
-
|
|
1422
|
+
let canonical;
|
|
1423
|
+
try {
|
|
1424
|
+
canonical = canonicalToolArgs("e2e-execute", args);
|
|
1425
|
+
} catch {
|
|
1426
|
+
return void 0;
|
|
1427
|
+
}
|
|
1428
|
+
const { name, project_root: projectRoot, e2e_test_path: flowPath } = canonical;
|
|
1386
1429
|
if (flowPath === void 0) {
|
|
1387
1430
|
if (typeof name !== "string" || !FLOW_NAME_PATTERN.test(name)) return void 0;
|
|
1388
1431
|
if (!isResolvedAbsolute(projectRoot)) return void 0;
|
|
@@ -1391,7 +1434,7 @@ function nestedFlowTarget(args) {
|
|
|
1391
1434
|
}
|
|
1392
1435
|
if (name !== void 0 || !isResolvedAbsolute(flowPath)) return void 0;
|
|
1393
1436
|
if (!FLOW_FILE_NAME_PATTERN.test(path6.basename(flowPath))) return void 0;
|
|
1394
|
-
return { kind: "
|
|
1437
|
+
return { kind: "e2e_test_path", path: flowPath };
|
|
1395
1438
|
}
|
|
1396
1439
|
function isResolvedAbsolute(value) {
|
|
1397
1440
|
return typeof value === "string" && path6.isAbsolute(value) && !value.split(/[\\/]+/).includes("..");
|
|
@@ -22444,6 +22487,7 @@ var init_registry = __esm({
|
|
|
22444
22487
|
init_artifacts();
|
|
22445
22488
|
init_errors();
|
|
22446
22489
|
init_failure_codes();
|
|
22490
|
+
init_tool_ids();
|
|
22447
22491
|
init_urn();
|
|
22448
22492
|
init_zod_to_json_schema();
|
|
22449
22493
|
import_node_crypto2 = require("node:crypto");
|
|
@@ -22524,6 +22568,18 @@ var init_registry = __esm({
|
|
|
22524
22568
|
);
|
|
22525
22569
|
this.events.emit("toolInvoked", toolId, toolInvocationId, startedMsg, calledAs);
|
|
22526
22570
|
try {
|
|
22571
|
+
try {
|
|
22572
|
+
params = canonicalToolArgs(toolId, params);
|
|
22573
|
+
} catch (err) {
|
|
22574
|
+
if (!(err instanceof ToolParamAliasError)) throw err;
|
|
22575
|
+
throw new FailureError(`Invalid params for tool "${toolId}": ${err.message}`, {
|
|
22576
|
+
error_code: FAILURE_CODES.TOOL_INPUT_INVALID,
|
|
22577
|
+
failure_stage: "tool_params_parse",
|
|
22578
|
+
failure_area: "registry",
|
|
22579
|
+
error_kind: "validation"
|
|
22580
|
+
});
|
|
22581
|
+
}
|
|
22582
|
+
effectiveParams = params;
|
|
22527
22583
|
if (definition.zodSchema) {
|
|
22528
22584
|
const parsed = definition.zodSchema.safeParse(params ?? {});
|
|
22529
22585
|
if (!parsed.success) {
|
|
@@ -101024,7 +101080,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
101024
101080
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
101025
101081
|
function readCliVersion() {
|
|
101026
101082
|
if (true) {
|
|
101027
|
-
return "0.31.2-next.
|
|
101083
|
+
return "0.31.2-next.3";
|
|
101028
101084
|
}
|
|
101029
101085
|
return "0.0.0";
|
|
101030
101086
|
}
|
|
@@ -131011,7 +131067,9 @@ function createHttpApp(registry2, options) {
|
|
|
131011
131067
|
)
|
|
131012
131068
|
};
|
|
131013
131069
|
if (def.outputHint) entry.outputHint = renderFlagGated(def.outputHint, isFlagEnabled);
|
|
131014
|
-
if (def.fileInputs && def.fileInputs.length > 0)
|
|
131070
|
+
if (def.fileInputs && def.fileInputs.length > 0) {
|
|
131071
|
+
entry.fileInputs = withFormerParamSpecs(def.id, def.fileInputs);
|
|
131072
|
+
}
|
|
131015
131073
|
if (def.alwaysLoad) entry.alwaysLoad = true;
|
|
131016
131074
|
if (def.searchHint) entry.searchHint = renderFlagGated(def.searchHint, isFlagEnabled);
|
|
131017
131075
|
if (def.longRunning) entry.longRunning = true;
|
|
@@ -131087,11 +131145,29 @@ function createHttpApp(registry2, options) {
|
|
|
131087
131145
|
return;
|
|
131088
131146
|
}
|
|
131089
131147
|
}
|
|
131148
|
+
let body;
|
|
131149
|
+
try {
|
|
131150
|
+
body = canonicalToolArgs(def.id, req.body);
|
|
131151
|
+
} catch (err) {
|
|
131152
|
+
if (!(err instanceof ToolParamAliasError)) throw err;
|
|
131153
|
+
emitHttpFailure(
|
|
131154
|
+
{
|
|
131155
|
+
error_code: FAILURE_CODES.HTTP_ZOD_VALIDATION_FAILED,
|
|
131156
|
+
failure_stage: "http_param_alias",
|
|
131157
|
+
failure_area: "http",
|
|
131158
|
+
error_kind: "validation"
|
|
131159
|
+
},
|
|
131160
|
+
req.body
|
|
131161
|
+
);
|
|
131162
|
+
const issues = [{ code: "custom", path: [], message: err.message }];
|
|
131163
|
+
res.status(400).json({ error: JSON.stringify(issues), message: err.message, issues });
|
|
131164
|
+
return;
|
|
131165
|
+
}
|
|
131090
131166
|
let bodyArgs;
|
|
131091
131167
|
let resolvedFileInputs;
|
|
131092
131168
|
let derivedTargets;
|
|
131093
131169
|
try {
|
|
131094
|
-
const resolved = await resolveFileInputs(def,
|
|
131170
|
+
const resolved = await resolveFileInputs(def, body, (id) => {
|
|
131095
131171
|
const entry = uploads.get(id);
|
|
131096
131172
|
if (entry) uploads.delete(id);
|
|
131097
131173
|
return entry;
|
|
@@ -168118,7 +168194,13 @@ function fromYamlStep(raw, blockDepth = 0) {
|
|
|
168118
168194
|
if ("script" in raw) return parseScriptStep(raw, raw.script);
|
|
168119
168195
|
if ("tool" in raw) {
|
|
168120
168196
|
const r = raw;
|
|
168121
|
-
|
|
168197
|
+
let args = r.args ?? {};
|
|
168198
|
+
try {
|
|
168199
|
+
args = canonicalToolArgs(r.tool, args);
|
|
168200
|
+
} catch (err) {
|
|
168201
|
+
badEntry(raw, err instanceof Error ? err.message : String(err));
|
|
168202
|
+
}
|
|
168203
|
+
const step = { kind: "tool", name: r.tool, args };
|
|
168122
168204
|
if (r.delayMs !== void 0) step.delayMs = r.delayMs;
|
|
168123
168205
|
return step;
|
|
168124
168206
|
}
|
|
@@ -168470,7 +168552,7 @@ var flowStartRecordingTool = {
|
|
|
168470
168552
|
},
|
|
168471
168553
|
description: `Start recording a new e2e test, resetting .argent/e2e/<name>.yaml to an empty e2e test and replacing any existing one.
|
|
168472
168554
|
Use when you want to capture a reusable sequence of device interactions for later replay.
|
|
168473
|
-
Returns { message,
|
|
168555
|
+
Returns { message, e2eTestFile, savedTo } and optionally { restarted, discardedSteps } if a live recording of the same e2e test was discarded.
|
|
168474
168556
|
Without a link, it creates that file and fails if the .argent/e2e/ directory cannot be created or the file cannot be written. Over a link (argent link or ARGENT_TOOLS_URL, also to 127.0.0.1), your client writes the file, and \`savedTo\` is its path on your computer, or null when that write failed.
|
|
168475
168557
|
|
|
168476
168558
|
Several e2e tests can be recorded at once, each keyed by the \`name\` + \`project_root\`
|
|
@@ -168541,12 +168623,14 @@ reports for those steps.`,
|
|
|
168541
168623
|
message: `Restarted recording "${params.name}" \u2014 ${lost} was discarded and ` + reset,
|
|
168542
168624
|
restarted: true,
|
|
168543
168625
|
...discardedSteps === void 0 ? {} : { discardedSteps },
|
|
168626
|
+
e2eTestFile: flowFile,
|
|
168544
168627
|
flowFile,
|
|
168545
168628
|
savedTo
|
|
168546
168629
|
};
|
|
168547
168630
|
}
|
|
168548
168631
|
return {
|
|
168549
168632
|
message: `Started recording "${params.name}" e2e test`,
|
|
168633
|
+
e2eTestFile: flowFile,
|
|
168550
168634
|
flowFile,
|
|
168551
168635
|
savedTo
|
|
168552
168636
|
};
|
|
@@ -169698,7 +169782,7 @@ async function runDirective(env, step) {
|
|
|
169698
169782
|
return {
|
|
169699
169783
|
ok: false,
|
|
169700
169784
|
indeterminate: true,
|
|
169701
|
-
reason: `\`await: { idle }\` cannot run on ${env.device.platform}:
|
|
169785
|
+
reason: `\`await: { idle }\` cannot run on ${env.device.platform}: e2e tests have no UI-tree source on this platform, so screen stillness can never be judged. Replace this step with an explicit \`wait:\`.`
|
|
169702
169786
|
};
|
|
169703
169787
|
}
|
|
169704
169788
|
switch (step.kind) {
|
|
@@ -170647,7 +170731,7 @@ var UPLOAD_STAGE_BY_KIND = {
|
|
|
170647
170731
|
};
|
|
170648
170732
|
function nestedFlowRef(args) {
|
|
170649
170733
|
if (typeof args.name === "string") return ` (name: ${args.name})`;
|
|
170650
|
-
if (typeof args.
|
|
170734
|
+
if (typeof args.e2e_test_path === "string") return ` (e2e_test_path: ${args.e2e_test_path})`;
|
|
170651
170735
|
return "";
|
|
170652
170736
|
}
|
|
170653
170737
|
function toolStepFilePaths(registry2, tool, args) {
|
|
@@ -170760,9 +170844,9 @@ async function prepareNestedFlow(registry2, project, toolId, args) {
|
|
|
170760
170844
|
};
|
|
170761
170845
|
const resolved = await resolveFileInputs(
|
|
170762
170846
|
{ fileInputs: registry2.getTool(toolId)?.fileInputs },
|
|
170763
|
-
{ ...args,
|
|
170847
|
+
{ ...args, e2e_test_file: wire }
|
|
170764
170848
|
);
|
|
170765
|
-
const input2 = resolved.fileInputs?.
|
|
170849
|
+
const input2 = resolved.fileInputs?.e2e_test_file;
|
|
170766
170850
|
if (input2) {
|
|
170767
170851
|
input2.canonical = hop.canonical;
|
|
170768
170852
|
input2.spelling = hop.spelling;
|
|
@@ -170797,7 +170881,8 @@ function count(value) {
|
|
|
170797
170881
|
return typeof value === "number" ? value : 0;
|
|
170798
170882
|
}
|
|
170799
170883
|
function flowExecuteOutcome(result) {
|
|
170800
|
-
const
|
|
170884
|
+
const name = result.e2eTest ?? result.flow;
|
|
170885
|
+
const flow = typeof name === "string" ? name : "the composed e2e test";
|
|
170801
170886
|
if (!("steps" in result) && typeof result.notice === "string") {
|
|
170802
170887
|
const prerequisite = typeof result.executionPrerequisite === "string" && result.executionPrerequisite ? `: ${result.executionPrerequisite}` : "";
|
|
170803
170888
|
return {
|
|
@@ -171350,7 +171435,7 @@ var DIRECTIVE_COMMAND_HINTS = {
|
|
|
171350
171435
|
run: {
|
|
171351
171436
|
tool: "e2e-execute",
|
|
171352
171437
|
rewritten: true,
|
|
171353
|
-
rewriteCondition: "when the target resolves as a sibling e2e test in this recording's folder, on the computer that has the project; a `name` that does not is kept as a raw `tool: e2e-execute` step, and
|
|
171438
|
+
rewriteCondition: "when the target resolves as a sibling e2e test in this recording's folder, on the computer that has the project; a `name` that does not is kept as a raw `tool: e2e-execute` step, and an `e2e_test_path` that is not a sibling is refused outright and records nothing"
|
|
171354
171439
|
},
|
|
171355
171440
|
type: { tool: "keyboard", rewritten: false },
|
|
171356
171441
|
await: { tool: AWAIT_UI_ELEMENT_TOOL_ID, rewritten: false },
|
|
@@ -171399,10 +171484,10 @@ function directiveCommandHint(command) {
|
|
|
171399
171484
|
}
|
|
171400
171485
|
var RUN_TARGET_COMMAND = "e2e-execute";
|
|
171401
171486
|
async function rewriteSiblingFlowPath(session, args, project, linked) {
|
|
171402
|
-
const flowPath = args.
|
|
171487
|
+
const flowPath = args.e2e_test_path;
|
|
171403
171488
|
if (typeof flowPath !== "string" || args.name !== void 0) return;
|
|
171404
171489
|
const invalid = (detail) => new FailureError(
|
|
171405
|
-
`Cannot record an e2e-execute of
|
|
171490
|
+
`Cannot record an e2e-execute of e2e_test_path "${flowPath}": ${detail}. e2e_test_path carries no file-input resolution through e2e-add-step's opaque args \u2014 pass name + project_root for an e2e test saved beside the recording, or add a \`run: <relative path>.yaml\` step to the e2e test YAML by hand for a cross-directory target.`,
|
|
171406
171491
|
{
|
|
171407
171492
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
171408
171493
|
failure_stage: "flow_add_step_flow_path",
|
|
@@ -171446,7 +171531,7 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
|
|
|
171446
171531
|
const suppliedBase = path45.basename(flowPath);
|
|
171447
171532
|
if (linked && project.mode === "host") {
|
|
171448
171533
|
throw new FailureError(
|
|
171449
|
-
`Cannot record an e2e-execute of
|
|
171534
|
+
`Cannot record an e2e-execute of e2e_test_path "${flowPath}": the argent client sent no files with this call, so the recorder cannot check that the file is a sibling of the recording. Update the argent CLI or MCP adapter on the client.`,
|
|
171450
171535
|
{
|
|
171451
171536
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
171452
171537
|
failure_stage: "flow_add_step_flow_path",
|
|
@@ -171461,11 +171546,11 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
|
|
|
171461
171546
|
} catch (err) {
|
|
171462
171547
|
if (path45.resolve(flowDir) === path45.resolve(flowsDir)) throw err;
|
|
171463
171548
|
throw invalid(
|
|
171464
|
-
`over a link, this argent client sends
|
|
171549
|
+
`over a link, this argent client sends an e2e_test_path only for an e2e test beside the recording; update the argent CLI or MCP adapter on the client (${err instanceof Error ? err.message : String(err)})`
|
|
171465
171550
|
);
|
|
171466
171551
|
}
|
|
171467
171552
|
if (spelling.state !== "listed") {
|
|
171468
|
-
const recovery = spelling.state === "absent" ? `pass the basename exactly as it appears on disk` : spelling.addressable ? `pass
|
|
171553
|
+
const recovery = spelling.state === "absent" ? `pass the basename exactly as it appears on disk` : spelling.addressable ? `pass e2e_test_path with the on-disk basename "${spelling.actual}"` : `rename "${spelling.actual}" to "${suppliedBase}" to record it \u2014 e2e test files must be lowercase .yaml`;
|
|
171469
171554
|
throw invalid(
|
|
171470
171555
|
`the file must be named as it appears on disk \u2014 no directory entry is named "${suppliedBase}"` + (spelling.state === "case_folded" ? ` (this filesystem matched it case-insensitively to "${spelling.actual}")` : "") + `, so the recorded run: step would name an e2e test no case-sensitive checkout can find \u2014 ` + recovery
|
|
171471
171556
|
);
|
|
@@ -171476,7 +171561,7 @@ async function rewriteSiblingFlowPath(session, args, project, linked) {
|
|
|
171476
171561
|
throw invalid(`project_root "${projectRoot}" resolves "${stem}" to "${picked.path}" instead`);
|
|
171477
171562
|
}
|
|
171478
171563
|
}
|
|
171479
|
-
delete args.
|
|
171564
|
+
delete args.e2e_test_path;
|
|
171480
171565
|
args.name = stem;
|
|
171481
171566
|
}
|
|
171482
171567
|
async function captureRunTarget(session, args, project) {
|
|
@@ -171538,7 +171623,7 @@ async function captureRunTarget(session, args, project) {
|
|
|
171538
171623
|
function replayRefusal(issue2, args) {
|
|
171539
171624
|
let hint;
|
|
171540
171625
|
if (issue2.kind === "nested") {
|
|
171541
|
-
hint = nestedFlowTarget(args)?.kind === "name" ? " Over a link, a nested e2e-execute runs only when the argent client sends the e2e test it names with the call. Update the argent CLI or MCP adapter on the client." : " Over a link, a nested e2e-execute runs only when it names its e2e test with a test name in name, has an absolute project_root with no .. segment, and has no
|
|
171626
|
+
hint = nestedFlowTarget(args)?.kind === "name" ? " Over a link, a nested e2e-execute runs only when the argent client sends the e2e test it names with the call. Update the argent CLI or MCP adapter on the client." : " Over a link, a nested e2e-execute runs only when it names its e2e test with a test name in name, has an absolute project_root with no .. segment, and has no e2e_test_path.";
|
|
171542
171627
|
} else if (issue2.fixes.length === 0) {
|
|
171543
171628
|
hint = " A replay without a link runs this step. To keep it, add it to the YAML by hand after e2e-finish-recording.";
|
|
171544
171629
|
} else {
|
|
@@ -171598,6 +171683,10 @@ Returns { message, stepCount, recorded, savedTo }; \`recorded\`, not the status,
|
|
|
171598
171683
|
throw err;
|
|
171599
171684
|
}
|
|
171600
171685
|
const authoredArgs = { ...args };
|
|
171686
|
+
try {
|
|
171687
|
+
args = canonicalToolArgs(command, args);
|
|
171688
|
+
} catch {
|
|
171689
|
+
}
|
|
171601
171690
|
const linked = isLinkedRecorderCall(session, ctx);
|
|
171602
171691
|
const stepFiles = ctx?.fileInputs?.project_root?.members;
|
|
171603
171692
|
const project = stepFiles ? new ClientProjectAccess(stepFiles) : new HostProjectAccess();
|
|
@@ -173303,7 +173392,7 @@ var flowFinishRecordingTool = {
|
|
|
173303
173392
|
completedMsg: ({ params }) => `Saved recorded e2e test ${params.name}`,
|
|
173304
173393
|
failedMsg: ({ params, failureSignal: failureSignal2 }) => `Failed to finish recording of e2e test ${params.name}: ${failureSignal2.error_code}`
|
|
173305
173394
|
},
|
|
173306
|
-
description: `Finish recording the e2e test named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary,
|
|
173395
|
+
description: `Finish recording the e2e test named by \`name\` + \`project_root\`, leaving recordings under any other key untouched. Returns { message, path, executionPrerequisite, steps, summary, e2eTestFile, savedTo } - a summary of all recorded steps plus the final YAML. Use when you have added all desired steps and want to finalize the e2e test file. Fails if that e2e test has no recording in progress.
|
|
173307
173396
|
A warning e2e-add-step raised on a recorded \`await-ui-element\` is repeated in \`summary\` as a \`warning:\` line of its own, right below the step it judges, and \`message\` counts them by kind. A warning is repeated only while the step it judges is still identifiable by its number: hand-editing the .yaml during the recording moves the steps, so those warnings are DROPPED rather than pinned on whichever step inherited the number, and \`message\` says how many were dropped. A step that carries a cross-tree warning was re-probed against the runner's tree: read it before converting that wait to \`await:\`/\`assert:\`, which is what the verdict is about and what this moment is for. A step that recorded a wait which did not pass was never probed at all, and its own warning names the CAUSE, because only one of them judges the condition: an unmet wait was read and found false, and it stops the run at replay; a wait whose tree source could not be read, or one that was cancelled, observed nothing and leaves the condition UNKNOWN rather than known-bad. Read those before replaying.
|
|
173308
173397
|
You can still edit the .yaml file directly afterwards to remove or reorder steps.`,
|
|
173309
173398
|
zodSchema: zodSchema70,
|
|
@@ -173341,6 +173430,7 @@ You can still edit the .yaml file directly afterwards to remove or reorder steps
|
|
|
173341
173430
|
executionPrerequisite: flow.executionPrerequisite,
|
|
173342
173431
|
steps: flow.steps.length,
|
|
173343
173432
|
summary,
|
|
173433
|
+
e2eTestFile: flowFile,
|
|
173344
173434
|
flowFile,
|
|
173345
173435
|
savedTo
|
|
173346
173436
|
};
|
|
@@ -175786,15 +175876,15 @@ async function restoreStatusBar(device) {
|
|
|
175786
175876
|
// ../tool-server/src/tools/flows/flow-run.ts
|
|
175787
175877
|
var zodSchema71 = external_exports.object({
|
|
175788
175878
|
name: external_exports.string().optional().describe(
|
|
175789
|
-
'Name of a saved e2e test to run from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when
|
|
175879
|
+
'Name of a saved e2e test to run from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when e2e_test_path is set.'
|
|
175790
175880
|
),
|
|
175791
175881
|
project_root: external_exports.string().describe(
|
|
175792
|
-
"Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with
|
|
175882
|
+
"Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with e2e_test_path, the e2e test, its run: siblings, its script: paths and baselines all resolve beside the YAML instead, so pass the agent's cwd. A script still RUNS in this root whichever source was used."
|
|
175793
175883
|
),
|
|
175794
|
-
|
|
175884
|
+
e2e_test_file: external_exports.string().optional().describe(
|
|
175795
175885
|
"Path to the e2e test .yaml as readable by the tool-server. Internal: the argent client derives it from project_root and name automatically; leave unset."
|
|
175796
175886
|
),
|
|
175797
|
-
|
|
175887
|
+
e2e_test_path: external_exports.string().optional().describe(
|
|
175798
175888
|
"Omit when name is set. Absolute path to an e2e test .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file, its run: fragments and the snapshot baselines of each run, and the tool-server runs those copies. Keep run: fragments under project_root or beside the e2e test file. With updateBaselines, the client writes the new baselines when the run ends. The client also uploads each absolute .png or .yaml path that a tool: step takes as a file argument, when the file is under project_root or beside the e2e test file. A tool: e2e-execute step that names an e2e test with name and an absolute project_root without a .. segment runs: the client also uploads that e2e test and its own files. Over a link, the uploaded e2e tests must not have: script: steps; tool: steps that run an e2e test another way or record an e2e test; tool: steps that take a directory, an app or an output directory; tool: steps whose tool builds a file path from several arguments; tool: steps that name a relative path or a file other than .png or .yaml. Without a link, the tool-server reads the files in place and all step kinds run."
|
|
175799
175889
|
),
|
|
175800
175890
|
device: external_exports.string().optional().describe(
|
|
@@ -175810,32 +175900,32 @@ var zodSchema71 = external_exports.object({
|
|
|
175810
175900
|
"Set to true to confirm the execution prerequisite has been met. Required (LLM path) when a fragment defines an executionPrerequisite."
|
|
175811
175901
|
)
|
|
175812
175902
|
}).superRefine((params, ctx) => {
|
|
175813
|
-
if (params.name === void 0 === (params.
|
|
175903
|
+
if (params.name === void 0 === (params.e2e_test_path === void 0)) {
|
|
175814
175904
|
ctx.addIssue({
|
|
175815
175905
|
code: external_exports.ZodIssueCode.custom,
|
|
175816
|
-
message: params.name !== void 0 ? "Pass exactly one e2e test source: name or
|
|
175817
|
-
// The ROOT, not `
|
|
175818
|
-
// path would prefix the message with "`
|
|
175906
|
+
message: params.name !== void 0 ? "Pass exactly one e2e test source: name or e2e_test_path." : "Pass exactly one e2e test source: name or e2e_test_path. e2e-execute needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
|
|
175907
|
+
// The ROOT, not `e2e_test_path`: the rule spans both source fields, and a
|
|
175908
|
+
// path would prefix the message with "`e2e_test_path`:".
|
|
175819
175909
|
path: []
|
|
175820
175910
|
});
|
|
175821
175911
|
}
|
|
175822
175912
|
});
|
|
175823
175913
|
var fileInputs4 = [
|
|
175824
175914
|
{
|
|
175825
|
-
target: "
|
|
175826
|
-
path: "${
|
|
175915
|
+
target: "e2e_test_path",
|
|
175916
|
+
path: "${e2e_test_path}",
|
|
175827
175917
|
kind: "file",
|
|
175828
175918
|
optional: true,
|
|
175829
175919
|
unwrapWhenSet: "name",
|
|
175830
175920
|
collect: "flow"
|
|
175831
175921
|
},
|
|
175832
175922
|
{
|
|
175833
|
-
target: "
|
|
175923
|
+
target: "e2e_test_file",
|
|
175834
175924
|
// A client that predates `paths` reads the legacy dir only.
|
|
175835
175925
|
path: "${project_root}/.argent/flows/${name}.yaml",
|
|
175836
175926
|
paths: E2E_FILE_TEMPLATES,
|
|
175837
175927
|
kind: "file",
|
|
175838
|
-
skipWhenSet: "
|
|
175928
|
+
skipWhenSet: "e2e_test_path",
|
|
175839
175929
|
collect: "flow"
|
|
175840
175930
|
}
|
|
175841
175931
|
];
|
|
@@ -176126,8 +176216,8 @@ function deviceEnv(state3) {
|
|
|
176126
176216
|
return { ...state3, device: state3.device };
|
|
176127
176217
|
}
|
|
176128
176218
|
function displayFlowName(params) {
|
|
176129
|
-
const stem = params.
|
|
176130
|
-
return params.name || stem || params.
|
|
176219
|
+
const stem = params.e2e_test_path === void 0 ? void 0 : path50.basename(params.e2e_test_path, ".yaml");
|
|
176220
|
+
return params.name || stem || params.e2e_test_path || "(unspecified)";
|
|
176131
176221
|
}
|
|
176132
176222
|
function* walkSteps(steps, within = "") {
|
|
176133
176223
|
for (const [i, step] of steps.entries()) {
|
|
@@ -176273,7 +176363,9 @@ function assertUploadSelfContained(registry2, flow, closure, origin = {
|
|
|
176273
176363
|
throw new FailureError(
|
|
176274
176364
|
`${origin.subject} is not self-contained, and ${origin.arrival}. The steps below read or write project files, which stay on the client:
|
|
176275
176365
|
` + offending.map((o) => ` - ${o.line}`).join("\n") + `
|
|
176276
|
-
Run the e2e test on the computer that runs the tool-server, with no link and no ARGENT_TOOLS_URL, so that the tool-server reads the files in place.${uploadUpdateHint(servable)}
|
|
176366
|
+
Run the e2e test on the computer that runs the tool-server, with no link and no ARGENT_TOOLS_URL, so that the tool-server reads the files in place.${uploadUpdateHint(servable)}` + // An older client lists e2e_test_path from this schema but collects the
|
|
176367
|
+
// files only for flow_path.
|
|
176368
|
+
(servable.length > 0 ? " A client older than the e2e_test_path param sends those files for a test passed as flow_path." : "") + toolFilePathHints(toolFileFixes),
|
|
176277
176369
|
{
|
|
176278
176370
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
176279
176371
|
failure_stage: UPLOAD_STAGE_BY_KIND[offending[0].kind],
|
|
@@ -176294,7 +176386,7 @@ function createRunFlowTool(registry2) {
|
|
|
176294
176386
|
description: `Run a saved YAML e2e test end to end. Use when
|
|
176295
176387
|
asked to replay a recorded path, re-run a QA regression, or check that a known journey still passes; for a
|
|
176296
176388
|
one-off interaction use the gesture tools instead, and to author an e2e test use e2e-start-recording. Pass
|
|
176297
|
-
exactly one e2e test source: name (under project_root) or
|
|
176389
|
+
exactly one e2e test source: name (under project_root) or e2e_test_path.
|
|
176298
176390
|
Returns a per-step report: the first failure stops the run and the rest report as skipped.`,
|
|
176299
176391
|
longRunning: true,
|
|
176300
176392
|
zodSchema: zodSchema71,
|
|
@@ -176305,11 +176397,11 @@ Returns a per-step report: the first failure stops the run and the rest report a
|
|
|
176305
176397
|
const signal = ctx?.signal;
|
|
176306
176398
|
const { filePath, flowName, viaUpload, shadowed } = await resolveFlowSource(
|
|
176307
176399
|
params,
|
|
176308
|
-
ctx?.fileInputs?.
|
|
176309
|
-
ctx?.fileInputs?.
|
|
176400
|
+
ctx?.fileInputs?.e2e_test_file,
|
|
176401
|
+
ctx?.fileInputs?.e2e_test_path
|
|
176310
176402
|
);
|
|
176311
176403
|
const warning = shadowed ? { warning: e2eShadowWarning(filePath, shadowed) } : {};
|
|
176312
|
-
const rootInput = viaUpload ? ctx?.fileInputs?.
|
|
176404
|
+
const rootInput = viaUpload ? ctx?.fileInputs?.e2e_test_path ?? ctx?.fileInputs?.e2e_test_file : void 0;
|
|
176313
176405
|
const clientProject = rootInput?.members ? new ClientProjectAccess(rootInput.members) : void 0;
|
|
176314
176406
|
const project = clientProject ?? new HostProjectAccess();
|
|
176315
176407
|
const canonicalPath = await canonicalFlowPath(filePath);
|
|
@@ -176369,6 +176461,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
|
|
|
176369
176461
|
}
|
|
176370
176462
|
if (flow.executionPrerequisite && !params.prerequisiteAcknowledged) {
|
|
176371
176463
|
return {
|
|
176464
|
+
e2eTest: flowName,
|
|
176372
176465
|
flow: flowName,
|
|
176373
176466
|
notice: "This e2e test has an execution prerequisite that must be fulfilled before it can run. Verify the prerequisite is met and call e2e-execute again with prerequisiteAcknowledged set to true.",
|
|
176374
176467
|
executionPrerequisite: flow.executionPrerequisite,
|
|
@@ -176622,6 +176715,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2, t
|
|
|
176622
176715
|
else errored++;
|
|
176623
176716
|
}
|
|
176624
176717
|
return {
|
|
176718
|
+
e2eTest: flowName,
|
|
176625
176719
|
flow: flowName,
|
|
176626
176720
|
device: deviceId,
|
|
176627
176721
|
executionPrerequisite,
|
|
@@ -176640,6 +176734,7 @@ function summarize(flowName, deviceId, executionPrerequisite, steps, aborted2, t
|
|
|
176640
176734
|
};
|
|
176641
176735
|
}
|
|
176642
176736
|
function pushReport(state3, report) {
|
|
176737
|
+
if (report.e2eTest !== void 0) report.flow = report.e2eTest;
|
|
176643
176738
|
state3.reports.push(report);
|
|
176644
176739
|
state3.onStepReport?.(report);
|
|
176645
176740
|
}
|
|
@@ -176676,7 +176771,7 @@ async function execSteps(state3, steps, scope) {
|
|
|
176676
176771
|
index,
|
|
176677
176772
|
kind: step.kind,
|
|
176678
176773
|
status: "skip",
|
|
176679
|
-
|
|
176774
|
+
e2eTest: stepFlow(step, scope),
|
|
176680
176775
|
target: stepTarget(step),
|
|
176681
176776
|
...depthOf(scope),
|
|
176682
176777
|
...stopReason ? { reason: stopReason } : {},
|
|
@@ -176694,7 +176789,7 @@ async function execSteps(state3, steps, scope) {
|
|
|
176694
176789
|
index,
|
|
176695
176790
|
kind: step.kind,
|
|
176696
176791
|
status: "error",
|
|
176697
|
-
|
|
176792
|
+
e2eTest: scopeFlow(scope),
|
|
176698
176793
|
target: stepTarget(step),
|
|
176699
176794
|
...depthOf(scope),
|
|
176700
176795
|
reason: `step needs a device but the e2e test was resolved as device-free \u2014 pass an explicit device`
|
|
@@ -176710,7 +176805,7 @@ async function execSteps(state3, steps, scope) {
|
|
|
176710
176805
|
kind: step.kind,
|
|
176711
176806
|
status: "skip",
|
|
176712
176807
|
reason: "run aborted",
|
|
176713
|
-
|
|
176808
|
+
e2eTest: stepFlow(step, scope),
|
|
176714
176809
|
target: stepTarget(step),
|
|
176715
176810
|
...depthOf(scope),
|
|
176716
176811
|
...step.kind === "echo" ? { message: step.message } : {}
|
|
@@ -176762,7 +176857,7 @@ function reportBlockSkipped(state3, steps, scope, reason2) {
|
|
|
176762
176857
|
kind: step.kind,
|
|
176763
176858
|
status: "skip",
|
|
176764
176859
|
reason: reason2,
|
|
176765
|
-
|
|
176860
|
+
e2eTest: stepFlow(step, scope),
|
|
176766
176861
|
target: stepTarget(step),
|
|
176767
176862
|
...depthOf(scope),
|
|
176768
176863
|
...step.kind === "echo" ? { message: step.message } : {}
|
|
@@ -176788,7 +176883,7 @@ async function execWhenStep(state3, step, scope) {
|
|
|
176788
176883
|
const marker = {
|
|
176789
176884
|
index,
|
|
176790
176885
|
kind: "when",
|
|
176791
|
-
|
|
176886
|
+
e2eTest: scopeFlow(scope),
|
|
176792
176887
|
target,
|
|
176793
176888
|
...depthOf(scope)
|
|
176794
176889
|
};
|
|
@@ -176848,7 +176943,7 @@ async function execRunStep(state3, step, scope) {
|
|
|
176848
176943
|
index,
|
|
176849
176944
|
kind: "run",
|
|
176850
176945
|
status: "error",
|
|
176851
|
-
|
|
176946
|
+
e2eTest: display,
|
|
176852
176947
|
target,
|
|
176853
176948
|
reason: reason2,
|
|
176854
176949
|
...depthOf(scope),
|
|
@@ -176896,7 +176991,7 @@ async function execRunStep(state3, step, scope) {
|
|
|
176896
176991
|
index,
|
|
176897
176992
|
kind: "run",
|
|
176898
176993
|
status: "pass",
|
|
176899
|
-
|
|
176994
|
+
e2eTest: display,
|
|
176900
176995
|
target,
|
|
176901
176996
|
...depthOf(scope),
|
|
176902
176997
|
durationMs: Date.now() - startedAt
|
|
@@ -176950,7 +177045,7 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
176950
177045
|
const base = {
|
|
176951
177046
|
index,
|
|
176952
177047
|
kind: step.kind,
|
|
176953
|
-
|
|
177048
|
+
e2eTest: scopeFlow(scope),
|
|
176954
177049
|
target: stepTarget(step),
|
|
176955
177050
|
...depthOf(scope)
|
|
176956
177051
|
};
|
|
@@ -177158,7 +177253,7 @@ function errMsg4(err) {
|
|
|
177158
177253
|
return err instanceof Error ? err.message : String(err);
|
|
177159
177254
|
}
|
|
177160
177255
|
function assertFlowPathShape(flowPath) {
|
|
177161
|
-
const windows = windowsPathRefusal("
|
|
177256
|
+
const windows = windowsPathRefusal("e2e_test_path", flowPath);
|
|
177162
177257
|
if (windows) {
|
|
177163
177258
|
throw new FailureError(windows, {
|
|
177164
177259
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
@@ -177169,7 +177264,7 @@ function assertFlowPathShape(flowPath) {
|
|
|
177169
177264
|
}
|
|
177170
177265
|
if (!path50.isAbsolute(flowPath)) {
|
|
177171
177266
|
throw new FailureError(
|
|
177172
|
-
`Invalid
|
|
177267
|
+
`Invalid e2e_test_path "${flowPath}": e2e test paths must be absolute \u2014 a relative path is resolved against the tool server's working directory, not the caller's. Pass the absolute path to the e2e test's YAML.`,
|
|
177173
177268
|
{
|
|
177174
177269
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177175
177270
|
failure_stage: "flow_path_absolute",
|
|
@@ -177180,7 +177275,7 @@ function assertFlowPathShape(flowPath) {
|
|
|
177180
177275
|
}
|
|
177181
177276
|
if (flowPath.split(/[\\/]+/).includes("..")) {
|
|
177182
177277
|
throw new FailureError(
|
|
177183
|
-
`Invalid
|
|
177278
|
+
`Invalid e2e_test_path "${flowPath}": e2e test paths must not contain ".." segments \u2014 a ".." after a symlinked directory can name a different file than the spelling suggests, and the argent client always sends fully resolved paths. Pass the fully resolved absolute path to the e2e test's YAML.`,
|
|
177184
177279
|
{
|
|
177185
177280
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177186
177281
|
failure_stage: "flow_path_dotdot",
|
|
@@ -177195,7 +177290,7 @@ function flowNameOf(clientPath) {
|
|
|
177195
177290
|
const bareExtension = path50.basename(clientPath).toLowerCase() === ".yaml";
|
|
177196
177291
|
if (!bareExtension && clientExt !== ".yaml") {
|
|
177197
177292
|
const detail = clientExt.toLowerCase() === ".yaml" ? `e2e test files must use the lowercase .yaml extension, not "${clientExt}".` : `e2e test files must use the .yaml extension.`;
|
|
177198
|
-
throw new FailureError(`Invalid
|
|
177293
|
+
throw new FailureError(`Invalid e2e_test_path "${clientPath}": ${detail}`, {
|
|
177199
177294
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177200
177295
|
failure_stage: "flow_path_extension",
|
|
177201
177296
|
failure_area: "tool_server",
|
|
@@ -177208,9 +177303,9 @@ function flowNameOf(clientPath) {
|
|
|
177208
177303
|
}
|
|
177209
177304
|
function flowPathCasingError(clientPath, spelling) {
|
|
177210
177305
|
const suppliedBase = path50.basename(clientPath);
|
|
177211
|
-
const recovery = spelling.state === "absent" ? `Pass the basename exactly as it appears on disk.` : spelling.addressable ? `Pass
|
|
177306
|
+
const recovery = spelling.state === "absent" ? `Pass the basename exactly as it appears on disk.` : spelling.addressable ? `Pass e2e_test_path with the on-disk basename "${spelling.actual}".` : `Rename "${spelling.actual}" to "${suppliedBase}" to run it \u2014 e2e test files must be lowercase .yaml.`;
|
|
177212
177307
|
return new FailureError(
|
|
177213
|
-
`Invalid
|
|
177308
|
+
`Invalid e2e_test_path "${clientPath}": the file must be named as it appears on disk \u2014 this filesystem matched "${suppliedBase}" case-insensitively` + (spelling.state === "case_folded" ? ` to "${spelling.actual}"` : "") + `, so the e2e test name (which keys the report and __baselines__/) would be one no directory entry carries. ${recovery}`,
|
|
177214
177309
|
{
|
|
177215
177310
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177216
177311
|
failure_stage: "flow_path_casing",
|
|
@@ -177220,8 +177315,8 @@ function flowPathCasingError(clientPath, spelling) {
|
|
|
177220
177315
|
);
|
|
177221
177316
|
}
|
|
177222
177317
|
async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
177223
|
-
if (params.name === void 0 === (params.
|
|
177224
|
-
throw new FailureError("Pass exactly one e2e test source: name or
|
|
177318
|
+
if (params.name === void 0 === (params.e2e_test_path === void 0)) {
|
|
177319
|
+
throw new FailureError("Pass exactly one e2e test source: name or e2e_test_path.", {
|
|
177225
177320
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177226
177321
|
failure_stage: "flow_source",
|
|
177227
177322
|
failure_area: "tool_server",
|
|
@@ -177229,16 +177324,16 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
|
177229
177324
|
});
|
|
177230
177325
|
}
|
|
177231
177326
|
assertValidProjectRoot(params.project_root);
|
|
177232
|
-
if (params.
|
|
177327
|
+
if (params.e2e_test_path !== void 0) {
|
|
177233
177328
|
if (flowPathInput?.viaUpload) {
|
|
177234
177329
|
const clientPath2 = flowPathInput.clientPath;
|
|
177235
177330
|
assertFlowPathShape(clientPath2);
|
|
177236
|
-
return { filePath: params.
|
|
177331
|
+
return { filePath: params.e2e_test_path, flowName: flowNameOf(clientPath2), viaUpload: true };
|
|
177237
177332
|
}
|
|
177238
|
-
const isVerifiedHostPath = flowPathInput?.presentOnHost === true && flowPathInput.statVerified === true && path50.resolve(params.
|
|
177333
|
+
const isVerifiedHostPath = flowPathInput?.presentOnHost === true && flowPathInput.statVerified === true && path50.resolve(params.e2e_test_path) === path50.resolve(flowPathInput.clientPath);
|
|
177239
177334
|
if (!isVerifiedHostPath) {
|
|
177240
177335
|
throw new FailureError(
|
|
177241
|
-
`Invalid
|
|
177336
|
+
`Invalid e2e_test_path "${params.e2e_test_path}": explicit e2e test paths must be supplied through the e2e_test_path file-input boundary. Pass the client-local path and let the argent client resolve it.`,
|
|
177242
177337
|
{
|
|
177243
177338
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177244
177339
|
failure_stage: "flow_path_boundary",
|
|
@@ -177247,24 +177342,26 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
|
177247
177342
|
}
|
|
177248
177343
|
);
|
|
177249
177344
|
}
|
|
177250
|
-
assertFlowPathShape(params.
|
|
177345
|
+
assertFlowPathShape(params.e2e_test_path);
|
|
177251
177346
|
const clientPath = flowPathInput.clientPath;
|
|
177252
177347
|
const flowName2 = flowNameOf(clientPath);
|
|
177253
177348
|
const spelling = await classifyOnDiskSpelling(
|
|
177254
|
-
path50.dirname(params.
|
|
177349
|
+
path50.dirname(params.e2e_test_path),
|
|
177255
177350
|
path50.basename(clientPath)
|
|
177256
177351
|
);
|
|
177257
177352
|
if (spelling.state !== "listed") throw flowPathCasingError(clientPath, spelling);
|
|
177258
|
-
return { filePath: params.
|
|
177353
|
+
return { filePath: params.e2e_test_path, flowName: flowName2, viaUpload: false };
|
|
177259
177354
|
}
|
|
177260
177355
|
const flowName = params.name;
|
|
177261
177356
|
assertSafeFlowName(flowName);
|
|
177262
177357
|
const candidates = e2eFileCandidates(params.project_root, flowName);
|
|
177263
|
-
if (params.
|
|
177264
|
-
return { filePath: params.
|
|
177265
|
-
if (params.
|
|
177358
|
+
if (params.e2e_test_file && fileInput?.viaUpload)
|
|
177359
|
+
return { filePath: params.e2e_test_file, flowName, viaUpload: true };
|
|
177360
|
+
if (params.e2e_test_file && (!path50.isAbsolute(params.e2e_test_file) || params.e2e_test_file.split(/[\\/]+/).includes("..") || !candidates.some(
|
|
177361
|
+
(candidate) => path50.resolve(candidate) === path50.resolve(params.e2e_test_file)
|
|
177362
|
+
))) {
|
|
177266
177363
|
throw new FailureError(
|
|
177267
|
-
`Invalid
|
|
177364
|
+
`Invalid e2e_test_file "${params.e2e_test_file}": it must resolve to the e2e test's path under the project root ("${candidates.join('" or "')}"). e2e_test_file is internal \u2014 leave it unset and pass project_root + name.`,
|
|
177268
177365
|
{
|
|
177269
177366
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
177270
177367
|
failure_stage: "flow_file_containment",
|
|
@@ -177274,8 +177371,8 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
|
177274
177371
|
);
|
|
177275
177372
|
}
|
|
177276
177373
|
const picked = await pickE2eFile(candidates);
|
|
177277
|
-
const filePath = params.
|
|
177278
|
-
const spellingDirs = params.
|
|
177374
|
+
const filePath = params.e2e_test_file || picked.path;
|
|
177375
|
+
const spellingDirs = params.e2e_test_file || picked.exists ? [path50.dirname(filePath)] : candidates.map((candidate) => path50.dirname(candidate));
|
|
177279
177376
|
for (const dir of spellingDirs) {
|
|
177280
177377
|
const spelling = await classifyOnDiskSpelling(dir, `${flowName}.yaml`);
|
|
177281
177378
|
if (spelling.state === "case_folded") throw flowNameCasingError(flowName, spelling);
|
|
@@ -177290,22 +177387,22 @@ var fs66 = __toESM(require("node:fs/promises"));
|
|
|
177290
177387
|
init_src2();
|
|
177291
177388
|
var zodSchema72 = external_exports.object({
|
|
177292
177389
|
name: external_exports.string().optional().describe(
|
|
177293
|
-
'Name of a saved e2e test to inspect from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when
|
|
177390
|
+
'Name of a saved e2e test to inspect from `.argent/e2e`, or `.argent/flows` when it is not there (e.g. "settings-explore"). Omit when e2e_test_path is set.'
|
|
177294
177391
|
),
|
|
177295
177392
|
project_root: external_exports.string().describe(
|
|
177296
|
-
"Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with
|
|
177393
|
+
"Absolute path to the calling agent's project root: the cwd it is working in. With name, the saved e2e test is read from `.argent/e2e/<name>.yaml`, else `.argent/flows/<name>.yaml`, under this root; with e2e_test_path, the prerequisite is read from that YAML instead, so pass the agent's cwd."
|
|
177297
177394
|
),
|
|
177298
|
-
|
|
177395
|
+
e2e_test_file: external_exports.string().optional().describe(
|
|
177299
177396
|
"Path to the e2e test .yaml as readable by the tool-server. Internal: the argent client derives it from project_root and name automatically; leave unset."
|
|
177300
177397
|
),
|
|
177301
|
-
|
|
177398
|
+
e2e_test_path: external_exports.string().optional().describe(
|
|
177302
177399
|
"Omit when name is set. Absolute path to an e2e test .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file and the tool-server reads the uploaded copy; without a link, the tool-server reads the file in place. Pass the path through the argent client; a raw path that the file-input boundary did not verify is rejected. Pass the same e2e test source here as to e2e-execute, so the prerequisite you read belongs to the e2e test that will run."
|
|
177303
177400
|
)
|
|
177304
177401
|
}).superRefine((params, ctx) => {
|
|
177305
|
-
if (params.name === void 0 === (params.
|
|
177402
|
+
if (params.name === void 0 === (params.e2e_test_path === void 0)) {
|
|
177306
177403
|
ctx.addIssue({
|
|
177307
177404
|
code: external_exports.ZodIssueCode.custom,
|
|
177308
|
-
message: params.name !== void 0 ? "Pass exactly one e2e test source: name or
|
|
177405
|
+
message: params.name !== void 0 ? "Pass exactly one e2e test source: name or e2e_test_path." : "Pass exactly one e2e test source: name or e2e_test_path. e2e-read-prerequisite needs the e2e test's name in `name` \u2014 it resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.",
|
|
177309
177406
|
// The ROOT, matching flow-execute: the rule spans both source fields,
|
|
177310
177407
|
// so it must not be anchored on one of them.
|
|
177311
177408
|
path: []
|
|
@@ -177314,18 +177411,18 @@ var zodSchema72 = external_exports.object({
|
|
|
177314
177411
|
});
|
|
177315
177412
|
var fileInputs5 = [
|
|
177316
177413
|
{
|
|
177317
|
-
target: "
|
|
177318
|
-
path: "${
|
|
177414
|
+
target: "e2e_test_path",
|
|
177415
|
+
path: "${e2e_test_path}",
|
|
177319
177416
|
kind: "file",
|
|
177320
177417
|
optional: true,
|
|
177321
177418
|
unwrapWhenSet: "name"
|
|
177322
177419
|
},
|
|
177323
177420
|
{
|
|
177324
|
-
target: "
|
|
177421
|
+
target: "e2e_test_file",
|
|
177325
177422
|
path: "${project_root}/.argent/flows/${name}.yaml",
|
|
177326
177423
|
paths: E2E_FILE_TEMPLATES,
|
|
177327
177424
|
kind: "file",
|
|
177328
|
-
skipWhenSet: "
|
|
177425
|
+
skipWhenSet: "e2e_test_path"
|
|
177329
177426
|
}
|
|
177330
177427
|
];
|
|
177331
177428
|
var flowReadPrerequisiteTool = {
|
|
@@ -177336,8 +177433,8 @@ var flowReadPrerequisiteTool = {
|
|
|
177336
177433
|
completedMsg: () => "Read e2e test prerequisite",
|
|
177337
177434
|
failedMsg: ({ failureSignal: failureSignal2 }) => `Failed to read e2e test prerequisite: ${failureSignal2.error_code}`
|
|
177338
177435
|
},
|
|
177339
|
-
description: `Read the execution prerequisite of an e2e test without running it: a saved e2e test from the .argent/e2e/ directory (or the older .argent/flows/), or an explicit boundary-managed
|
|
177340
|
-
Returns {
|
|
177436
|
+
description: `Read the execution prerequisite of an e2e test without running it: a saved e2e test from the .argent/e2e/ directory (or the older .argent/flows/), or an explicit boundary-managed e2e_test_path.
|
|
177437
|
+
Returns { e2eTest, executionPrerequisite, warning? }: the logical name, plus the precondition its author recorded
|
|
177341
177438
|
verbatim. Empty when none was declared, which is always so for a self-contained scenario: one opening
|
|
177342
177439
|
on a launch may declare no prerequisite, because it builds its own start state.
|
|
177343
177440
|
Use when deciding whether the device already sits where a fragment expects it (correct app foregrounded,
|
|
@@ -177345,7 +177442,7 @@ correct account, correct screen) before committing to a run, or when relaying th
|
|
|
177345
177442
|
Touches no device: nothing is launched, tapped, dispatched or torn down, and no simulator or emulator
|
|
177346
177443
|
needs booting, so calling this costs nothing but a file read.
|
|
177347
177444
|
Fails if the e2e test file does not exist.
|
|
177348
|
-
Address the e2e test exactly as you will address it in e2e-execute: name or
|
|
177445
|
+
Address the e2e test exactly as you will address it in e2e-execute: name or e2e_test_path, one and only one; supplying both or neither is rejected. The name goes in \`name\`, which resolves <project_root>/.argent/e2e/<name>.yaml, else .argent/flows/<name>.yaml.`,
|
|
177349
177446
|
readOnly: true,
|
|
177350
177447
|
zodSchema: zodSchema72,
|
|
177351
177448
|
fileInputs: fileInputs5,
|
|
@@ -177353,11 +177450,12 @@ Address the e2e test exactly as you will address it in e2e-execute: name or flow
|
|
|
177353
177450
|
async execute(_services, params, ctx) {
|
|
177354
177451
|
const { filePath, flowName, shadowed } = await resolveFlowSource(
|
|
177355
177452
|
params,
|
|
177356
|
-
ctx?.fileInputs?.
|
|
177357
|
-
ctx?.fileInputs?.
|
|
177453
|
+
ctx?.fileInputs?.e2e_test_file,
|
|
177454
|
+
ctx?.fileInputs?.e2e_test_path
|
|
177358
177455
|
);
|
|
177359
177456
|
const flow = parseFlow(await fs66.readFile(filePath, "utf8"));
|
|
177360
177457
|
return {
|
|
177458
|
+
e2eTest: flowName,
|
|
177361
177459
|
flow: flowName,
|
|
177362
177460
|
executionPrerequisite: flow.executionPrerequisite,
|
|
177363
177461
|
...shadowed ? { warning: e2eShadowWarning(filePath, shadowed) } : {}
|