@swmansion/argent 0.27.1-next.16 → 0.27.1-next.18
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/assets/manifest.json +2 -2
- package/bin/{argent-android-devtools-0.1.0.apk → argent-android-devtools-0.2.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 +108 -56
- package/dist/installer.mjs +1 -1
- package/dist/tool-args.generated.d.ts +2 -2
- package/dist/tool-server.cjs +178 -106
- 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-flow/references/live-authoring.md +1 -1
package/assets/manifest.json
CHANGED
|
Binary file
|
package/bin/darwin/ax-service
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/tcp/ax-service
CHANGED
|
Binary file
|
package/dist/cli-cmds.mjs
CHANGED
|
@@ -21916,7 +21916,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
21916
21916
|
var SESSION_ID2 = randomUUID5();
|
|
21917
21917
|
function readCliVersion() {
|
|
21918
21918
|
if (true) {
|
|
21919
|
-
return "0.27.1-next.
|
|
21919
|
+
return "0.27.1-next.18";
|
|
21920
21920
|
}
|
|
21921
21921
|
return "0.0.0";
|
|
21922
21922
|
}
|
|
@@ -23267,8 +23267,11 @@ file, or a device it cannot resolve. A transport failure, a rejection the server
|
|
|
23267
23267
|
does not mark as validation, or a reply that is not a report stops the batch and
|
|
23268
23268
|
counts the remaining flows skipped.
|
|
23269
23269
|
|
|
23270
|
-
|
|
23271
|
-
|
|
23270
|
+
The CLI sends a run to its tool-server: the local one that starts
|
|
23271
|
+
automatically, or the one that \`argent link\` or ARGENT_TOOLS_URL names. With
|
|
23272
|
+
either, the CLI uploads the flow file, and the tool-server rejects a flow with
|
|
23273
|
+
run:, script: or snapshot: steps, or with tool: steps that take a file or
|
|
23274
|
+
record a flow, before the first step. With neither, all step kinds run.
|
|
23272
23275
|
|
|
23273
23276
|
Subcommands:
|
|
23274
23277
|
run <flow|flow.yaml|dir> Run a saved flow by name, a YAML file by path, or
|
|
@@ -23282,6 +23285,7 @@ Options (run):
|
|
|
23282
23285
|
auto-detection (ios never picks a remote simulator)
|
|
23283
23286
|
--update-baselines Write/refresh screenshot baselines instead of diffing
|
|
23284
23287
|
--output <dir> Also write failed snapshot images (baseline/current/diff)
|
|
23288
|
+
and failed-step screenshots (step-<n>-screen.png)
|
|
23285
23289
|
under <dir>/<flow>/ \u2014 a stable path for CI artifact
|
|
23286
23290
|
upload; a directory run keys nested flows as
|
|
23287
23291
|
<dir>/<subdir>/<flow>/. A different flow file with the
|
|
@@ -23557,23 +23561,34 @@ async function exportFailureArtifacts(report, outputDir, flowPath, ctx) {
|
|
|
23557
23561
|
return;
|
|
23558
23562
|
}
|
|
23559
23563
|
let dir = null;
|
|
23564
|
+
let n2 = 0;
|
|
23560
23565
|
for (const s of report.steps) {
|
|
23561
|
-
if (s.kind !== "
|
|
23562
|
-
|
|
23563
|
-
|
|
23564
|
-
|
|
23565
|
-
s.
|
|
23566
|
-
|
|
23566
|
+
if (s.kind !== "echo") n2++;
|
|
23567
|
+
if (!s.artifacts) continue;
|
|
23568
|
+
let artifacts;
|
|
23569
|
+
let fileName;
|
|
23570
|
+
if (s.kind === "snapshot" && s.status === "fail") {
|
|
23571
|
+
const key = s.snapshotKey ?? keyFromBaselinePath(s.artifacts);
|
|
23572
|
+
if (!key || !SAFE_ARTIFACT_NAME.test(key)) continue;
|
|
23573
|
+
artifacts = s.artifacts;
|
|
23574
|
+
const remote = s.snapshotRemote === true ? "-remote" : "";
|
|
23575
|
+
fileName = (role) => `${key}${remote}-${role}.png`;
|
|
23576
|
+
} else if ((s.status === "fail" || s.status === "error") && s.artifacts.screen != null) {
|
|
23577
|
+
artifacts = { screen: s.artifacts.screen };
|
|
23578
|
+
fileName = () => `step-${n2}-screen.png`;
|
|
23579
|
+
} else {
|
|
23580
|
+
continue;
|
|
23581
|
+
}
|
|
23582
|
+
const { result } = await materializeArtifacts(artifacts, ctx);
|
|
23583
|
+
Object.assign(s.artifacts, result);
|
|
23584
|
+
for (const [role, value] of Object.entries(result)) {
|
|
23567
23585
|
if (typeof value !== "string") continue;
|
|
23568
23586
|
if (dir === null) {
|
|
23569
23587
|
const dirName = await claimExportDirName(outputDir, flowPath, stem);
|
|
23570
23588
|
if (dirName === null) return;
|
|
23571
23589
|
dir = path14.join(outputDir, dirName);
|
|
23572
23590
|
}
|
|
23573
|
-
const dest = path14.join(
|
|
23574
|
-
dir,
|
|
23575
|
-
`${key}${s.snapshotRemote === true ? "-remote" : ""}-${role}.png`
|
|
23576
|
-
);
|
|
23591
|
+
const dest = path14.join(dir, fileName(role));
|
|
23577
23592
|
const rel = path14.relative(outputDir, dest);
|
|
23578
23593
|
if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
|
|
23579
23594
|
try {
|
|
@@ -23700,13 +23715,6 @@ async function collectFlowFiles(dir, recursive) {
|
|
|
23700
23715
|
await walk(dir, "");
|
|
23701
23716
|
return found.sort();
|
|
23702
23717
|
}
|
|
23703
|
-
async function requireLocalToolServer() {
|
|
23704
|
-
const routing = await getResolvedToolsUrl();
|
|
23705
|
-
if (routing.source === "none") return void 0;
|
|
23706
|
-
const recovery = routing.source === "env" ? routing.shadowedLink ? `Unset ARGENT_TOOLS_URL and run \`argent unlink\`, then try again \u2014 a link to ${routing.shadowedLink.url} is also configured and takes over once the env var is unset.` : "Unset ARGENT_TOOLS_URL and try again." : "Run `argent unlink` and try again.";
|
|
23707
|
-
return `argent flow run requires the auto-started local tool server; ${routing.source} routing is configured.
|
|
23708
|
-
${recovery}`;
|
|
23709
|
-
}
|
|
23710
23718
|
function buildRunPayload(flowPath, projectRoot, args) {
|
|
23711
23719
|
const payload = {
|
|
23712
23720
|
flow_path: flowPath,
|
|
@@ -23722,16 +23730,51 @@ function buildRunPayload(flowPath, projectRoot, args) {
|
|
|
23722
23730
|
function writeJsonStreamRecord(record2) {
|
|
23723
23731
|
console.log(JSON.stringify(record2));
|
|
23724
23732
|
}
|
|
23733
|
+
var ToolServerUnreachableError = class extends Error {
|
|
23734
|
+
errorCode = "TOOL_SERVER_UNREACHABLE";
|
|
23735
|
+
errorKind = "network";
|
|
23736
|
+
};
|
|
23737
|
+
var UNREACHABLE_VERDICT = "not run (tool-server unreachable)";
|
|
23738
|
+
var CONNECT_FAILURES = /* @__PURE__ */ new Map([
|
|
23739
|
+
["ECONNREFUSED", "connection refused"],
|
|
23740
|
+
["ENOTFOUND", "host not found"],
|
|
23741
|
+
["EAI_AGAIN", "host not found"],
|
|
23742
|
+
["ETIMEDOUT", "timed out"],
|
|
23743
|
+
["UND_ERR_CONNECT_TIMEOUT", "timed out"],
|
|
23744
|
+
["EHOSTUNREACH", "host unreachable"],
|
|
23745
|
+
["ENETUNREACH", "host unreachable"]
|
|
23746
|
+
]);
|
|
23747
|
+
async function toolServerUnreachable(err) {
|
|
23748
|
+
if (!(err instanceof TypeError)) return void 0;
|
|
23749
|
+
const cause = err.cause;
|
|
23750
|
+
const reason = typeof cause?.code === "string" ? CONNECT_FAILURES.get(cause.code) : void 0;
|
|
23751
|
+
const syscall = cause?.syscall;
|
|
23752
|
+
if (!reason || syscall !== void 0 && syscall !== "connect" && syscall !== "getaddrinfo") {
|
|
23753
|
+
return void 0;
|
|
23754
|
+
}
|
|
23755
|
+
const routing = await getResolvedToolsUrl();
|
|
23756
|
+
if (routing.source === "none") return void 0;
|
|
23757
|
+
const recovery = routing.source === "link" ? "Start the tool-server on the linked machine, or run `argent unlink`." : routing.shadowedLink ? `Start that tool-server, or unset ARGENT_TOOLS_URL and run \`argent unlink\` to use the local one. A link to ${routing.shadowedLink.url} is also configured and takes over once the env var is unset.` : "Start that tool-server, or unset ARGENT_TOOLS_URL to use the local one.";
|
|
23758
|
+
const setBy = routing.source === "env" ? "ARGENT_TOOLS_URL" : "argent link";
|
|
23759
|
+
return new ToolServerUnreachableError(
|
|
23760
|
+
`Could not reach the tool-server at ${routing.url} (set by ${setBy}): ${reason}.
|
|
23761
|
+
${recovery}`
|
|
23762
|
+
);
|
|
23763
|
+
}
|
|
23725
23764
|
function failureSignal2(err) {
|
|
23726
|
-
if (!(err instanceof ToolInvocationError))
|
|
23765
|
+
if (!(err instanceof ToolInvocationError) && !(err instanceof ToolServerUnreachableError)) {
|
|
23766
|
+
return {};
|
|
23767
|
+
}
|
|
23727
23768
|
return {
|
|
23728
23769
|
...err.errorCode ? { error_code: err.errorCode } : {},
|
|
23729
23770
|
...err.errorKind ? { error_kind: err.errorKind } : {}
|
|
23730
23771
|
};
|
|
23731
23772
|
}
|
|
23773
|
+
function errorRecord(err, message = err instanceof Error ? err.message : String(err)) {
|
|
23774
|
+
return { event: "error", error: message, ...failureSignal2(err) };
|
|
23775
|
+
}
|
|
23732
23776
|
function writeJsonStreamError(err) {
|
|
23733
|
-
|
|
23734
|
-
writeJsonStreamRecord({ event: "error", error: message, ...failureSignal2(err) });
|
|
23777
|
+
writeJsonStreamRecord(errorRecord(err));
|
|
23735
23778
|
}
|
|
23736
23779
|
async function exportAndResolveArtifacts(report, outputDir, flowPath, baseUrl) {
|
|
23737
23780
|
if (outputDir) {
|
|
@@ -23757,22 +23800,23 @@ function isFlowReport(data) {
|
|
|
23757
23800
|
return Array.isArray(steps) && steps.every((step) => !!step && typeof step === "object");
|
|
23758
23801
|
}
|
|
23759
23802
|
async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
23803
|
+
const printError = (message, err = message) => {
|
|
23804
|
+
console.error(args.json ? JSON.stringify(errorRecord(err, message)) : message);
|
|
23805
|
+
};
|
|
23806
|
+
const reject = (message) => {
|
|
23807
|
+
printError(message);
|
|
23808
|
+
return exitAfterFlush(2);
|
|
23809
|
+
};
|
|
23760
23810
|
let flows;
|
|
23761
23811
|
try {
|
|
23762
23812
|
flows = await collectFlowFiles(dir, args.recursive);
|
|
23763
23813
|
} catch {
|
|
23764
|
-
|
|
23765
|
-
return exitAfterFlush(2);
|
|
23814
|
+
return reject(`Could not read flow directory: ${dir}`);
|
|
23766
23815
|
}
|
|
23767
23816
|
if (flows.length === 0) {
|
|
23768
|
-
|
|
23769
|
-
|
|
23770
|
-
|
|
23771
|
-
}
|
|
23772
|
-
const refusal = await requireLocalToolServer();
|
|
23773
|
-
if (refusal) {
|
|
23774
|
-
console.error(refusal);
|
|
23775
|
-
return exitAfterFlush(2);
|
|
23817
|
+
return reject(
|
|
23818
|
+
`No flows found in ${dir}` + (args.recursive ? "" : "\nPass -r/--recursive to include subdirectories.")
|
|
23819
|
+
);
|
|
23776
23820
|
}
|
|
23777
23821
|
const { callTool, baseUrl } = createToolsClient({ paths: options.paths });
|
|
23778
23822
|
const outputBase = args.output ? path14.resolve(args.output) : void 0;
|
|
@@ -23780,6 +23824,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
|
23780
23824
|
const failures = [];
|
|
23781
23825
|
const batchStartedAt = Date.now();
|
|
23782
23826
|
let stopped = false;
|
|
23827
|
+
let unreachable = false;
|
|
23783
23828
|
for (const [i2, rel] of flows.entries()) {
|
|
23784
23829
|
if (!args.json) console.log(`[${i2 + 1}/${flows.length}] ${rel}`);
|
|
23785
23830
|
if (stopped) {
|
|
@@ -23793,23 +23838,26 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
|
23793
23838
|
try {
|
|
23794
23839
|
const resp = await callTool("flow-execute", buildRunPayload(flowPath, projectRoot, args));
|
|
23795
23840
|
if (isFlowReport(resp.data)) report = resp.data;
|
|
23796
|
-
} catch (
|
|
23841
|
+
} catch (thrown) {
|
|
23842
|
+
const unreachableErr = await toolServerUnreachable(thrown);
|
|
23843
|
+
const err = unreachableErr ?? thrown;
|
|
23797
23844
|
const message = err instanceof Error ? err.message : String(err);
|
|
23798
23845
|
const toolErr = err instanceof ToolInvocationError ? err : void 0;
|
|
23799
23846
|
const rejectedThisFlowOnly = toolErr?.errorKind === "validation";
|
|
23800
|
-
const verdict = rejectedThisFlowOnly ? rejectionVerdict(toolErr?.errorCode) : "did not finish (run error)";
|
|
23847
|
+
const verdict = unreachableErr ? UNREACHABLE_VERDICT : rejectedThisFlowOnly ? rejectionVerdict(toolErr?.errorCode) : "did not finish (run error)";
|
|
23801
23848
|
if (!args.json) console.log(` ${STATUS_GLYPH.error} ${verdict}`);
|
|
23802
|
-
|
|
23849
|
+
printError(message, err);
|
|
23803
23850
|
results.push({ path: rel, status: "fail", error: message, ...failureSignal2(err) });
|
|
23804
23851
|
failures.push({ path: rel, headline: verdict, detail: message, rerun });
|
|
23805
23852
|
if (!rejectedThisFlowOnly) stopped = true;
|
|
23853
|
+
if (unreachableErr) unreachable = true;
|
|
23806
23854
|
continue;
|
|
23807
23855
|
}
|
|
23808
23856
|
if (!report) {
|
|
23809
23857
|
const message = `"${rel}" did not produce a run report.`;
|
|
23810
23858
|
const verdict = "did not finish (no run report)";
|
|
23811
23859
|
if (!args.json) console.log(` ${STATUS_GLYPH.error} ${verdict}`);
|
|
23812
|
-
|
|
23860
|
+
printError(message);
|
|
23813
23861
|
results.push({ path: rel, status: "fail", error: message });
|
|
23814
23862
|
failures.push({ path: rel, headline: verdict, detail: message, rerun });
|
|
23815
23863
|
stopped = true;
|
|
@@ -23844,7 +23892,7 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
|
23844
23892
|
console.log(`
|
|
23845
23893
|
${renderBatchSummary(counts, durationMs)}`);
|
|
23846
23894
|
}
|
|
23847
|
-
return exitAfterFlush(counts.failed === 0 ? 0 : 1);
|
|
23895
|
+
return exitAfterFlush(unreachable ? 2 : counts.failed === 0 ? 0 : 1);
|
|
23848
23896
|
}
|
|
23849
23897
|
async function flow(argv, options) {
|
|
23850
23898
|
const [sub, ...rest] = argv;
|
|
@@ -23867,38 +23915,42 @@ async function flow(argv, options) {
|
|
|
23867
23915
|
console.error(`Unknown flow subcommand "${sub}". Run \`argent flow --help\`.`);
|
|
23868
23916
|
return exitAfterFlush(2);
|
|
23869
23917
|
}
|
|
23870
|
-
const
|
|
23871
|
-
|
|
23872
|
-
);
|
|
23918
|
+
const flagGiven = (flag) => rest.some((tok) => tok === flag || tok.startsWith(`${flag}=`));
|
|
23919
|
+
const jsonStream = flagGiven("--json-stream");
|
|
23920
|
+
const json2 = !jsonStream && flagGiven("--json");
|
|
23873
23921
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
23874
23922
|
printHelp(jsonStream);
|
|
23875
23923
|
return;
|
|
23876
23924
|
}
|
|
23877
|
-
const
|
|
23878
|
-
if (
|
|
23879
|
-
|
|
23880
|
-
|
|
23925
|
+
const usageError = (message, prose) => {
|
|
23926
|
+
if (json2) {
|
|
23927
|
+
console.error(JSON.stringify(errorRecord(message)));
|
|
23928
|
+
return exitAfterFlush(2);
|
|
23929
|
+
}
|
|
23930
|
+
if (jsonStream) writeJsonStreamError(message);
|
|
23931
|
+
console.error(prose);
|
|
23932
|
+
printHelp(jsonStream);
|
|
23933
|
+
return exitAfterFlush(2);
|
|
23881
23934
|
};
|
|
23882
23935
|
let args;
|
|
23883
23936
|
try {
|
|
23884
23937
|
args = parseRunArgs(rest);
|
|
23885
23938
|
} catch (err) {
|
|
23886
23939
|
if (err instanceof FlagParseException) {
|
|
23887
|
-
|
|
23888
|
-
console.error(`Error: ${err.message}
|
|
23940
|
+
return usageError(err.message, `Error: ${err.message}
|
|
23889
23941
|
`);
|
|
23890
|
-
printHelp(jsonStream);
|
|
23891
|
-
return exitAfterFlush(2);
|
|
23892
23942
|
}
|
|
23893
23943
|
throw err;
|
|
23894
23944
|
}
|
|
23895
23945
|
if (!args.flowRef) {
|
|
23896
23946
|
const message = "argent flow run <flow|flow.yaml|dir> requires a flow name, a YAML file path, or a directory path.";
|
|
23897
|
-
|
|
23898
|
-
console.error(message);
|
|
23899
|
-
printHelp(jsonStream);
|
|
23900
|
-
return exitAfterFlush(2);
|
|
23947
|
+
return usageError(message, message);
|
|
23901
23948
|
}
|
|
23949
|
+
const fail = (message, code, err = message) => {
|
|
23950
|
+
if (jsonStream) writeJsonStreamError(err);
|
|
23951
|
+
console.error(args.json ? JSON.stringify(errorRecord(err, message)) : message);
|
|
23952
|
+
return exitAfterFlush(code);
|
|
23953
|
+
};
|
|
23902
23954
|
const projectRoot = process.cwd();
|
|
23903
23955
|
const { suppliedPath, fromName } = resolveFlowRef(args.flowRef);
|
|
23904
23956
|
if (suppliedPath.split(/[\\/]+/).includes("..")) {
|
|
@@ -24007,8 +24059,6 @@ ${recovery}`,
|
|
|
24007
24059
|
2
|
|
24008
24060
|
);
|
|
24009
24061
|
}
|
|
24010
|
-
const refusal = await requireLocalToolServer();
|
|
24011
|
-
if (refusal) return fail(refusal, 2);
|
|
24012
24062
|
const { callTool, baseUrl } = createToolsClient({ paths: options.paths });
|
|
24013
24063
|
const payload = buildRunPayload(flowPath, projectRoot, args);
|
|
24014
24064
|
let liveSteps = 0;
|
|
@@ -24040,12 +24090,14 @@ ${recovery}`,
|
|
|
24040
24090
|
);
|
|
24041
24091
|
report = resp.data;
|
|
24042
24092
|
} catch (err) {
|
|
24093
|
+
const unreachableErr = await toolServerUnreachable(err);
|
|
24043
24094
|
if (!args.json && !args.jsonStream) {
|
|
24044
24095
|
if (liveSteps === 0) console.log(`Flow "${flowName}"`);
|
|
24045
24096
|
console.log(
|
|
24046
|
-
` ${STATUS_GLYPH.error} ` + (err instanceof ToolInvocationError && err.errorKind === "validation" ? rejectionVerdict(err.errorCode) : "did not finish (run error)")
|
|
24097
|
+
` ${STATUS_GLYPH.error} ` + (unreachableErr ? UNREACHABLE_VERDICT : err instanceof ToolInvocationError && err.errorKind === "validation" ? rejectionVerdict(err.errorCode) : "did not finish (run error)")
|
|
24047
24098
|
);
|
|
24048
24099
|
}
|
|
24100
|
+
if (unreachableErr) return fail(unreachableErr.message, 2, unreachableErr);
|
|
24049
24101
|
return fail(err instanceof Error ? err.message : String(err), 1, err);
|
|
24050
24102
|
}
|
|
24051
24103
|
if (!isFlowReport(report)) {
|
package/dist/installer.mjs
CHANGED
|
@@ -16730,7 +16730,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16730
16730
|
var SESSION_ID = randomUUID4();
|
|
16731
16731
|
function readCliVersion() {
|
|
16732
16732
|
if (true) {
|
|
16733
|
-
return "0.27.1-next.
|
|
16733
|
+
return "0.27.1-next.18";
|
|
16734
16734
|
}
|
|
16735
16735
|
return "0.0.0";
|
|
16736
16736
|
}
|
|
@@ -225,7 +225,7 @@ export interface ArgentToolArgs {
|
|
|
225
225
|
project_root: string;
|
|
226
226
|
/** Path to the flow .yaml as readable by the tool-server. Internal — the argent client derives it from project_root and name automatically; leave unset. */
|
|
227
227
|
flow_file?: string;
|
|
228
|
-
/** Omit when name is set. Absolute path to a
|
|
228
|
+
/** Omit when name is set. Absolute path to a flow .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file and the tool-server runs the uploaded copy. An uploaded flow must be self-contained: no run:, script: or snapshot: steps, and no tool: steps that take a file or record a flow. Without a link, the tool-server reads the file in place and all step kinds run. */
|
|
229
229
|
flow_path?: string;
|
|
230
230
|
/** Device id to run against (iOS UDID, Android/Vega serial, Chromium id) — the id list-devices reports. Auto-detected when omitted, but only when exactly one booted device matches (optionally narrowed by `platform`); with several booted the run fails and lists them, so pass this explicitly whenever more than one device is up. */
|
|
231
231
|
device?: string;
|
|
@@ -249,7 +249,7 @@ export interface ArgentToolArgs {
|
|
|
249
249
|
project_root: string;
|
|
250
250
|
/** Path to the flow .yaml as readable by the tool-server. Internal — the argent client derives it from project_root and name automatically; leave unset. */
|
|
251
251
|
flow_file?: string;
|
|
252
|
-
/** Omit when name is set. Absolute path to a
|
|
252
|
+
/** Omit when name is set. Absolute path to a flow .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 flow source here as to flow-execute, so the prerequisite you read belongs to the flow that will run. */
|
|
253
253
|
flow_path?: string;
|
|
254
254
|
};
|
|
255
255
|
"flow-start-recording": {
|
package/dist/tool-server.cjs
CHANGED
|
@@ -1108,6 +1108,18 @@ var init_artifacts = __esm({
|
|
|
1108
1108
|
function isFileInputWire(value) {
|
|
1109
1109
|
return !!value && typeof value === "object" && value[FILE_INPUT_MARKER] === true && typeof value.path === "string";
|
|
1110
1110
|
}
|
|
1111
|
+
function interpolateFileInputPath(template, args) {
|
|
1112
|
+
let missing = false;
|
|
1113
|
+
const out = template.replace(/\$\{([A-Za-z0-9_]+)\}/g, (_m, name) => {
|
|
1114
|
+
const v = args[name];
|
|
1115
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
1116
|
+
missing = true;
|
|
1117
|
+
return "";
|
|
1118
|
+
}
|
|
1119
|
+
return v;
|
|
1120
|
+
});
|
|
1121
|
+
return missing ? null : out;
|
|
1122
|
+
}
|
|
1111
1123
|
var FILE_INPUT_MARKER, FLOW_NAME_CHARSET, FLOW_NAME_PATTERN, FLOW_FILE_NAME_PATTERN, SCRIPT_FILE_NAME_PATTERN, CLIENT_FILE_MARKER;
|
|
1112
1124
|
var init_file_inputs = __esm({
|
|
1113
1125
|
"../registry/src/file-inputs.ts"() {
|
|
@@ -79147,16 +79159,16 @@ var require_path_is_absolute = __commonJS({
|
|
|
79147
79159
|
function posix6(path51) {
|
|
79148
79160
|
return path51.charAt(0) === "/";
|
|
79149
79161
|
}
|
|
79150
|
-
function
|
|
79162
|
+
function win322(path51) {
|
|
79151
79163
|
var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
|
|
79152
79164
|
var result = splitDeviceRe.exec(path51);
|
|
79153
79165
|
var device = result[1] || "";
|
|
79154
79166
|
var isUnc = Boolean(device && device.charAt(1) !== ":");
|
|
79155
79167
|
return Boolean(result[2] || isUnc);
|
|
79156
79168
|
}
|
|
79157
|
-
module2.exports = process.platform === "win32" ?
|
|
79169
|
+
module2.exports = process.platform === "win32" ? win322 : posix6;
|
|
79158
79170
|
module2.exports.posix = posix6;
|
|
79159
|
-
module2.exports.win32 =
|
|
79171
|
+
module2.exports.win32 = win322;
|
|
79160
79172
|
}
|
|
79161
79173
|
});
|
|
79162
79174
|
|
|
@@ -94814,7 +94826,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
94814
94826
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
94815
94827
|
function readCliVersion() {
|
|
94816
94828
|
if (true) {
|
|
94817
|
-
return "0.27.1-next.
|
|
94829
|
+
return "0.27.1-next.18";
|
|
94818
94830
|
}
|
|
94819
94831
|
return "0.0.0";
|
|
94820
94832
|
}
|
|
@@ -113595,26 +113607,30 @@ init_android_binary();
|
|
|
113595
113607
|
// ../tool-server/src/utils/android-helper-install.ts
|
|
113596
113608
|
init_adb();
|
|
113597
113609
|
async function probeInstalledVersion(serial, packageName) {
|
|
113598
|
-
let out;
|
|
113599
113610
|
try {
|
|
113600
|
-
|
|
113601
|
-
|
|
113602
|
-
|
|
113603
|
-
|
|
113604
|
-
|
|
113605
|
-
|
|
113606
|
-
|
|
113607
|
-
|
|
113611
|
+
const out2 = await adbShell(
|
|
113612
|
+
serial,
|
|
113613
|
+
`cmd package list packages --show-versioncode ${packageName}`,
|
|
113614
|
+
{ timeoutMs: 5e3 }
|
|
113615
|
+
);
|
|
113616
|
+
for (const line of out2.split("\n")) {
|
|
113617
|
+
const match = line.trim().match(/^package:([^\s]+)\s+versionCode:(\d+)$/);
|
|
113618
|
+
if (match?.[1] === packageName) {
|
|
113619
|
+
return { installed: true, versionCode: parseInt(match[2], 10) };
|
|
113620
|
+
}
|
|
113608
113621
|
}
|
|
113622
|
+
} catch {
|
|
113609
113623
|
}
|
|
113610
|
-
|
|
113611
|
-
|
|
113612
|
-
|
|
113613
|
-
|
|
113614
|
-
|
|
113615
|
-
return { installed: true, versionCode: Number.isFinite(versionCode) ? versionCode : null };
|
|
113624
|
+
let out;
|
|
113625
|
+
try {
|
|
113626
|
+
out = await adbShell(serial, `dumpsys package ${packageName}`, { timeoutMs: 5e3 });
|
|
113627
|
+
} catch {
|
|
113628
|
+
return { installed: false, versionCode: null };
|
|
113616
113629
|
}
|
|
113617
|
-
|
|
113630
|
+
const block = out.split(`Package [${packageName}]`)[1];
|
|
113631
|
+
if (block === void 0) return { installed: false, versionCode: null };
|
|
113632
|
+
const versionCode = block.match(/versionCode=(\d+)/);
|
|
113633
|
+
return { installed: true, versionCode: versionCode ? parseInt(versionCode[1], 10) : null };
|
|
113618
113634
|
}
|
|
113619
113635
|
async function ensureAndroidDevtoolsInstalled(serial, options = {}) {
|
|
113620
113636
|
const manifest = helperManifest();
|
|
@@ -114906,6 +114922,14 @@ async function resolveOne(spec, wire, tempDirs, lookupUpload) {
|
|
|
114906
114922
|
`Path "${wire.path}" does not exist on the tool-server host and no upload was provided. Update argent to a version that supports uploads for remote sessions.`
|
|
114907
114923
|
);
|
|
114908
114924
|
}
|
|
114925
|
+
if (spec.kind === "file" && typeof wire.content === "string") {
|
|
114926
|
+
const { filePath, dir } = await materializeUpload(wire);
|
|
114927
|
+
tempDirs.push(dir);
|
|
114928
|
+
return {
|
|
114929
|
+
value: filePath,
|
|
114930
|
+
meta: { clientPath: wire.path, presentOnHost: probe3.present, viaUpload: true }
|
|
114931
|
+
};
|
|
114932
|
+
}
|
|
114909
114933
|
if (meta3.presentOnHost) {
|
|
114910
114934
|
return { value: wire.path, meta: meta3 };
|
|
114911
114935
|
}
|
|
@@ -114914,11 +114938,6 @@ async function resolveOne(spec, wire, tempDirs, lookupUpload) {
|
|
|
114914
114938
|
`Directory "${wire.path}" does not exist on the tool-server host. This tool reads a directory tree from the tool-server's filesystem, which cannot be uploaded with the call \u2014 when the tool-server runs on a different machine, pass a path that exists on that machine (e.g. the server-side checkout of the project).`
|
|
114915
114939
|
);
|
|
114916
114940
|
}
|
|
114917
|
-
if (typeof wire.content === "string") {
|
|
114918
|
-
const { filePath, dir } = await materializeUpload(wire);
|
|
114919
|
-
tempDirs.push(dir);
|
|
114920
|
-
return { value: filePath, meta: { ...meta3, viaUpload: true } };
|
|
114921
|
-
}
|
|
114922
114941
|
if (wire.contentOmitted === "size-limit") {
|
|
114923
114942
|
throw new FileInputError(
|
|
114924
114943
|
`File "${wire.path}" is ${formatBytes(wire.size)} \u2014 larger than the ${formatBytes(MAX_UPLOAD_BYTES)} file-input transfer limit, so the client did not upload it, and it was not found on the tool-server host. Copy the file to the tool-server machine and pass that path, or use a smaller file.`
|
|
@@ -147148,7 +147167,22 @@ function selectRotateCandidate(candidates) {
|
|
|
147148
147167
|
|
|
147149
147168
|
// ../tool-server/src/tools/flows/flow-utils.ts
|
|
147150
147169
|
var FLOWS_DIR_NAME = path38.join(".argent", "flows");
|
|
147170
|
+
function windowsPathRefusal(label, value) {
|
|
147171
|
+
if (process.platform === "win32" || path38.isAbsolute(value) || !path38.win32.isAbsolute(value)) {
|
|
147172
|
+
return void 0;
|
|
147173
|
+
}
|
|
147174
|
+
return `${label} "${value}" is a Windows path. A tool-server on macOS or Linux cannot open Windows paths. Use argent on the computer that runs the tool-server, or start the tool-server on the Windows computer.`;
|
|
147175
|
+
}
|
|
147151
147176
|
function assertValidProjectRoot(root) {
|
|
147177
|
+
const windows = windowsPathRefusal("project_root", root);
|
|
147178
|
+
if (windows) {
|
|
147179
|
+
throw new FailureError(windows, {
|
|
147180
|
+
error_code: FAILURE_CODES.FLOW_PROJECT_ROOT_INVALID,
|
|
147181
|
+
failure_stage: "flow_project_root_set",
|
|
147182
|
+
failure_area: "tool_server",
|
|
147183
|
+
error_kind: "validation"
|
|
147184
|
+
});
|
|
147185
|
+
}
|
|
147152
147186
|
if (!path38.isAbsolute(root)) {
|
|
147153
147187
|
throw new FailureError(
|
|
147154
147188
|
`project_root must be an absolute path (got "${root}"). Pass the absolute path to the project root directory \u2014 the same cwd the calling agent is working in.`,
|
|
@@ -151775,6 +151809,7 @@ var NESTED_RECORDER_TOOLS = {
|
|
|
151775
151809
|
"flow-start-recording": "`flow-start-recording` truncates the flow it names. Recording it as a step would erase this flow at replay; call it directly when you want to start a recording.",
|
|
151776
151810
|
"flow-finish-recording": "`flow-finish-recording` ends the recording, so it cannot also be a step in it. Call it directly when the walkthrough is complete."
|
|
151777
151811
|
};
|
|
151812
|
+
var RECORDING_TOOL_IDS = new Set(Object.keys(NESTED_RECORDER_TOOLS));
|
|
151778
151813
|
function isToolNotFound(err, command) {
|
|
151779
151814
|
return err instanceof ToolNotFoundError && err.toolId === command;
|
|
151780
151815
|
}
|
|
@@ -156152,7 +156187,7 @@ var zodSchema68 = external_exports.object({
|
|
|
156152
156187
|
"Path to the flow .yaml as readable by the tool-server. Internal \u2014 the argent client derives it from project_root and name automatically; leave unset."
|
|
156153
156188
|
),
|
|
156154
156189
|
flow_path: external_exports.string().optional().describe(
|
|
156155
|
-
"Omit when name is set. Absolute path to a
|
|
156190
|
+
"Omit when name is set. Absolute path to a flow .yaml on the client. Over a link (argent link or ARGENT_TOOLS_URL), the argent client uploads the file and the tool-server runs the uploaded copy. An uploaded flow must be self-contained: no run:, script: or snapshot: steps, and no tool: steps that take a file or record a flow. Without a link, the tool-server reads the file in place and all step kinds run."
|
|
156156
156191
|
),
|
|
156157
156192
|
device: external_exports.string().optional().describe(
|
|
156158
156193
|
"Device id to run against (iOS UDID, Android/Vega serial, Chromium id) \u2014 the id list-devices reports. Auto-detected when omitted, but only when exactly one booted device matches (optionally narrowed by `platform`); with several booted the run fails and lists them, so pass this explicitly whenever more than one device is up."
|
|
@@ -156532,42 +156567,69 @@ function findRetiredToolArg(registry2, steps) {
|
|
|
156532
156567
|
function retiredArgReason(use) {
|
|
156533
156568
|
return `${use.where} as written (echo included) passes ${use.tool}'s retired \`${use.key}\` key${use.guidance ? `: ${use.guidance}` : ""}`;
|
|
156534
156569
|
}
|
|
156535
|
-
|
|
156536
|
-
|
|
156570
|
+
var UPLOAD_STAGE_BY_KIND = {
|
|
156571
|
+
run: "flow_upload_run_composition",
|
|
156572
|
+
script: "flow_upload_script_step",
|
|
156573
|
+
snapshot: "flow_upload_snapshot_baseline",
|
|
156574
|
+
nested: "flow_upload_nested_flow",
|
|
156575
|
+
toolFile: "flow_upload_tool_file_input",
|
|
156576
|
+
recording: "flow_upload_recording_tool"
|
|
156577
|
+
};
|
|
156578
|
+
function nestedFlowRef(args) {
|
|
156579
|
+
if (typeof args.name === "string") return ` (name: ${args.name})`;
|
|
156580
|
+
if (typeof args.flow_path === "string") return ` (flow_path: ${args.flow_path})`;
|
|
156581
|
+
return "";
|
|
156582
|
+
}
|
|
156583
|
+
function toolStepFilePaths(registry2, tool, args) {
|
|
156584
|
+
const paths = [];
|
|
156585
|
+
for (const spec of registry2.getTool(tool)?.fileInputs ?? []) {
|
|
156586
|
+
if (spec.skipWhenSet !== void 0 && args[spec.skipWhenSet] !== void 0) continue;
|
|
156587
|
+
const filled = interpolateFileInputPath(spec.path, args);
|
|
156588
|
+
if (filled !== null) paths.push(filled);
|
|
156589
|
+
}
|
|
156590
|
+
return paths;
|
|
156591
|
+
}
|
|
156592
|
+
function assertUploadSelfContained(registry2, flow) {
|
|
156593
|
+
const offending = [];
|
|
156594
|
+
for (const { step, where } of walkSteps(flow.steps)) {
|
|
156537
156595
|
if (step.kind === "run") {
|
|
156538
|
-
|
|
156539
|
-
|
|
156540
|
-
|
|
156541
|
-
|
|
156542
|
-
|
|
156543
|
-
|
|
156544
|
-
|
|
156545
|
-
|
|
156546
|
-
|
|
156547
|
-
|
|
156548
|
-
if (step.kind === "
|
|
156549
|
-
|
|
156550
|
-
|
|
156551
|
-
{
|
|
156552
|
-
|
|
156553
|
-
|
|
156554
|
-
|
|
156555
|
-
|
|
156556
|
-
|
|
156557
|
-
|
|
156558
|
-
|
|
156559
|
-
|
|
156560
|
-
|
|
156561
|
-
`This flow uses a snapshot step ("snapshot: ${step.name}"), whose baselines live beside the flow's file \u2014 an uploaded flow materializes to a fresh temp directory each call, so a plain snapshot can never find a baseline and updateBaselines (--update-baselines) writes PNGs no later run can read. Use name + project_root with a co-located client and tool server for snapshot flows.`,
|
|
156562
|
-
{
|
|
156563
|
-
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
156564
|
-
failure_stage: "flow_upload_snapshot_baseline",
|
|
156565
|
-
failure_area: "tool_server",
|
|
156566
|
-
error_kind: "validation"
|
|
156567
|
-
}
|
|
156568
|
-
);
|
|
156596
|
+
offending.push({ kind: "run", line: `${where}: run: ${step.flow}` });
|
|
156597
|
+
} else if (step.kind === "script") {
|
|
156598
|
+
offending.push({ kind: "script", line: `${where}: script: { path: ${step.path} }` });
|
|
156599
|
+
} else if (step.kind === "snapshot") {
|
|
156600
|
+
offending.push({ kind: "snapshot", line: `${where}: snapshot: ${step.name}` });
|
|
156601
|
+
} else if (step.kind === "tool" && step.name === "flow-execute") {
|
|
156602
|
+
offending.push({
|
|
156603
|
+
kind: "nested",
|
|
156604
|
+
line: `${where}: tool: flow-execute${nestedFlowRef(step.args)}`
|
|
156605
|
+
});
|
|
156606
|
+
} else if (step.kind === "tool" && RECORDING_TOOL_IDS.has(step.name)) {
|
|
156607
|
+
offending.push({
|
|
156608
|
+
kind: "recording",
|
|
156609
|
+
line: `${where}: tool: ${step.name} (records a flow)`
|
|
156610
|
+
});
|
|
156611
|
+
} else if (step.kind === "tool") {
|
|
156612
|
+
const paths = toolStepFilePaths(registry2, step.name, step.args);
|
|
156613
|
+
if (paths.length > 0) {
|
|
156614
|
+
offending.push({
|
|
156615
|
+
kind: "toolFile",
|
|
156616
|
+
line: `${where}: tool: ${step.name} (${paths.join(", ")})`
|
|
156617
|
+
});
|
|
156618
|
+
}
|
|
156569
156619
|
}
|
|
156570
156620
|
}
|
|
156621
|
+
if (offending.length === 0) return;
|
|
156622
|
+
throw new FailureError(
|
|
156623
|
+
`This flow is not self-contained, and it arrived as an upload. The steps below read or write project files, which stay on the client:
|
|
156624
|
+
` + offending.map((o) => ` - ${o.line}`).join("\n") + `
|
|
156625
|
+
Run the flow 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.`,
|
|
156626
|
+
{
|
|
156627
|
+
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
156628
|
+
failure_stage: UPLOAD_STAGE_BY_KIND[offending[0].kind],
|
|
156629
|
+
failure_area: "tool_server",
|
|
156630
|
+
error_kind: "validation"
|
|
156631
|
+
}
|
|
156632
|
+
);
|
|
156571
156633
|
}
|
|
156572
156634
|
function createRunFlowTool(registry2) {
|
|
156573
156635
|
return {
|
|
@@ -156597,7 +156659,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
|
|
|
156597
156659
|
const canonicalPath = await canonicalFlowPath2(filePath);
|
|
156598
156660
|
const flowsDir = path46.dirname(canonicalPath);
|
|
156599
156661
|
const flow = parseFlow(await fs62.readFile(canonicalPath, "utf8"));
|
|
156600
|
-
if (viaUpload) assertUploadSelfContained(flow);
|
|
156662
|
+
if (viaUpload) assertUploadSelfContained(registry2, flow);
|
|
156601
156663
|
const retiredArg = findRetiredToolArg(registry2, flow.steps);
|
|
156602
156664
|
if (retiredArg) {
|
|
156603
156665
|
throw new FailureError(`Flow "${flowName}" ${retiredArgReason(retiredArg)}`, {
|
|
@@ -156800,7 +156862,7 @@ async function resolveAppPath(specPath, flowDir) {
|
|
|
156800
156862
|
async function bootChromiumForFlow(spec, flowDir, viaUpload) {
|
|
156801
156863
|
if (viaUpload && !path46.isAbsolute(spec.path)) {
|
|
156802
156864
|
throw new FailureError(
|
|
156803
|
-
`A relative chromium app path ("${spec.path}") resolves against the flow file's directory, which requires a
|
|
156865
|
+
`A relative chromium app path ("${spec.path}") resolves against the flow file's directory, which requires a run with no link and no ARGENT_TOOLS_URL \u2014 an uploaded flow has no real flow directory on this host. Use an absolute tool-server path instead.`,
|
|
156804
156866
|
{
|
|
156805
156867
|
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
156806
156868
|
failure_stage: "flow_upload_chromium_app_path",
|
|
@@ -157349,6 +157411,55 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
157349
157411
|
function errMsg4(err) {
|
|
157350
157412
|
return err instanceof Error ? err.message : String(err);
|
|
157351
157413
|
}
|
|
157414
|
+
function assertFlowPathShape(flowPath) {
|
|
157415
|
+
const windows = windowsPathRefusal("flow_path", flowPath);
|
|
157416
|
+
if (windows) {
|
|
157417
|
+
throw new FailureError(windows, {
|
|
157418
|
+
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157419
|
+
failure_stage: "flow_path_absolute",
|
|
157420
|
+
failure_area: "tool_server",
|
|
157421
|
+
error_kind: "validation"
|
|
157422
|
+
});
|
|
157423
|
+
}
|
|
157424
|
+
if (!path46.isAbsolute(flowPath)) {
|
|
157425
|
+
throw new FailureError(
|
|
157426
|
+
`Invalid flow_path "${flowPath}": flow 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 flow's YAML.`,
|
|
157427
|
+
{
|
|
157428
|
+
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157429
|
+
failure_stage: "flow_path_absolute",
|
|
157430
|
+
failure_area: "tool_server",
|
|
157431
|
+
error_kind: "validation"
|
|
157432
|
+
}
|
|
157433
|
+
);
|
|
157434
|
+
}
|
|
157435
|
+
if (flowPath.split(/[\\/]+/).includes("..")) {
|
|
157436
|
+
throw new FailureError(
|
|
157437
|
+
`Invalid flow_path "${flowPath}": flow 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 flow's YAML.`,
|
|
157438
|
+
{
|
|
157439
|
+
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157440
|
+
failure_stage: "flow_path_dotdot",
|
|
157441
|
+
failure_area: "tool_server",
|
|
157442
|
+
error_kind: "validation"
|
|
157443
|
+
}
|
|
157444
|
+
);
|
|
157445
|
+
}
|
|
157446
|
+
}
|
|
157447
|
+
function flowNameOf(clientPath) {
|
|
157448
|
+
const clientExt = path46.extname(clientPath);
|
|
157449
|
+
const bareExtension = path46.basename(clientPath).toLowerCase() === ".yaml";
|
|
157450
|
+
if (!bareExtension && clientExt !== ".yaml") {
|
|
157451
|
+
const detail = clientExt.toLowerCase() === ".yaml" ? `flow files must use the lowercase .yaml extension, not "${clientExt}".` : `flow files must use the .yaml extension.`;
|
|
157452
|
+
throw new FailureError(`Invalid flow_path "${clientPath}": ${detail}`, {
|
|
157453
|
+
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157454
|
+
failure_stage: "flow_path_extension",
|
|
157455
|
+
failure_area: "tool_server",
|
|
157456
|
+
error_kind: "validation"
|
|
157457
|
+
});
|
|
157458
|
+
}
|
|
157459
|
+
const flowName = bareExtension ? "" : path46.basename(clientPath, ".yaml");
|
|
157460
|
+
assertSafeFlowName(flowName);
|
|
157461
|
+
return flowName;
|
|
157462
|
+
}
|
|
157352
157463
|
async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
157353
157464
|
if (params.name === void 0 === (params.flow_path === void 0)) {
|
|
157354
157465
|
throw new FailureError("Pass exactly one flow source: name or flow_path.", {
|
|
@@ -157361,15 +157472,9 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
|
157361
157472
|
assertValidProjectRoot(params.project_root);
|
|
157362
157473
|
if (params.flow_path !== void 0) {
|
|
157363
157474
|
if (flowPathInput?.viaUpload) {
|
|
157364
|
-
|
|
157365
|
-
|
|
157366
|
-
|
|
157367
|
-
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157368
|
-
failure_stage: "flow_path_shared_filesystem",
|
|
157369
|
-
failure_area: "tool_server",
|
|
157370
|
-
error_kind: "validation"
|
|
157371
|
-
}
|
|
157372
|
-
);
|
|
157475
|
+
const clientPath2 = flowPathInput.clientPath;
|
|
157476
|
+
assertFlowPathShape(clientPath2);
|
|
157477
|
+
return { filePath: params.flow_path, flowName: flowNameOf(clientPath2), viaUpload: true };
|
|
157373
157478
|
}
|
|
157374
157479
|
const isVerifiedHostPath = flowPathInput?.presentOnHost === true && flowPathInput.statVerified === true && path46.resolve(params.flow_path) === path46.resolve(flowPathInput.clientPath);
|
|
157375
157480
|
if (!isVerifiedHostPath) {
|
|
@@ -157383,42 +157488,9 @@ async function resolveFlowSource(params, fileInput, flowPathInput) {
|
|
|
157383
157488
|
}
|
|
157384
157489
|
);
|
|
157385
157490
|
}
|
|
157386
|
-
|
|
157387
|
-
throw new FailureError(
|
|
157388
|
-
`Invalid flow_path "${params.flow_path}": flow 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 flow's YAML.`,
|
|
157389
|
-
{
|
|
157390
|
-
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157391
|
-
failure_stage: "flow_path_absolute",
|
|
157392
|
-
failure_area: "tool_server",
|
|
157393
|
-
error_kind: "validation"
|
|
157394
|
-
}
|
|
157395
|
-
);
|
|
157396
|
-
}
|
|
157397
|
-
if (params.flow_path.split(/[\\/]+/).includes("..")) {
|
|
157398
|
-
throw new FailureError(
|
|
157399
|
-
`Invalid flow_path "${params.flow_path}": flow 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 flow's YAML.`,
|
|
157400
|
-
{
|
|
157401
|
-
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157402
|
-
failure_stage: "flow_path_dotdot",
|
|
157403
|
-
failure_area: "tool_server",
|
|
157404
|
-
error_kind: "validation"
|
|
157405
|
-
}
|
|
157406
|
-
);
|
|
157407
|
-
}
|
|
157491
|
+
assertFlowPathShape(params.flow_path);
|
|
157408
157492
|
const clientPath = flowPathInput.clientPath;
|
|
157409
|
-
const
|
|
157410
|
-
const bareExtension = path46.basename(clientPath).toLowerCase() === ".yaml";
|
|
157411
|
-
if (!bareExtension && clientExt !== ".yaml") {
|
|
157412
|
-
const detail = clientExt.toLowerCase() === ".yaml" ? `flow files must use the lowercase .yaml extension, not "${clientExt}".` : `flow files must use the .yaml extension.`;
|
|
157413
|
-
throw new FailureError(`Invalid flow_path "${clientPath}": ${detail}`, {
|
|
157414
|
-
error_code: FAILURE_CODES.FLOW_FILE_INVALID,
|
|
157415
|
-
failure_stage: "flow_path_extension",
|
|
157416
|
-
failure_area: "tool_server",
|
|
157417
|
-
error_kind: "validation"
|
|
157418
|
-
});
|
|
157419
|
-
}
|
|
157420
|
-
const flowName2 = bareExtension ? "" : path46.basename(clientPath, ".yaml");
|
|
157421
|
-
assertSafeFlowName(flowName2);
|
|
157493
|
+
const flowName2 = flowNameOf(clientPath);
|
|
157422
157494
|
const suppliedBase = path46.basename(clientPath);
|
|
157423
157495
|
const spelling2 = await classifyOnDiskSpelling(path46.dirname(params.flow_path), suppliedBase);
|
|
157424
157496
|
if (spelling2.state !== "listed") {
|
|
@@ -157481,7 +157553,7 @@ var zodSchema69 = external_exports.object({
|
|
|
157481
157553
|
"Path to the flow .yaml as readable by the tool-server. Internal \u2014 the argent client derives it from project_root and name automatically; leave unset."
|
|
157482
157554
|
),
|
|
157483
157555
|
flow_path: external_exports.string().optional().describe(
|
|
157484
|
-
"Omit when name is set. Absolute path to a
|
|
157556
|
+
"Omit when name is set. Absolute path to a flow .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 flow source here as to flow-execute, so the prerequisite you read belongs to the flow that will run."
|
|
157485
157557
|
)
|
|
157486
157558
|
}).superRefine((params, ctx) => {
|
|
157487
157559
|
if (params.name === void 0 === (params.flow_path === void 0)) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -266,7 +266,7 @@ Resolve every hit and confirm:
|
|
|
266
266
|
|
|
267
267
|
Run `flow-execute` on the complete YAML with the absolute project root. For a fragment, verify its prerequisite before setting `prerequisiteAcknowledged: true`.
|
|
268
268
|
|
|
269
|
-
`flow-execute` takes exactly one flow source: `name`, for a flow saved under `.argent/flows/`, or `flow_path`, an absolute path to any flow `.yaml`. `
|
|
269
|
+
`flow-execute` takes exactly one flow source: `name`, for a flow saved under `.argent/flows/`, or `flow_path`, an absolute path to any flow `.yaml`. Over `argent link` or `ARGENT_TOOLS_URL`, either source sends only that one YAML file, and the tool server runs that copy. The server checks the whole flow before it runs the first step. It refuses a flow with a `run:`, `script:`, or `snapshot:` step, or with a `tool:` step that takes a file (such as `screenshot-diff`, `reinstall-app`, or `flow-execute`) or records a flow, at any depth. The error lists each such step. Replay only a self-contained flow over a link. A flow that composes, runs a script, or takes a snapshot runs only with no link and no `ARGENT_TOOLS_URL`, on the computer that runs the tool server.
|
|
270
270
|
|
|
271
271
|
Manual rescue invalidates the pass. An `errored` step was never evaluated: an `idle` wait whose tree source could not be read, a step that threw, an unresolvable `run:` target, or a `launch:` that did not start the app. Read the reason — most name the environment, but a failed `launch:` is a verdict about the app. Unconfirmed focus is not in this class at all: the replay focus poll has no failure return, so a `type:` step whose focus was never confirmed is scored a **pass**, and only the value check after typing catches it.
|
|
272
272
|
|