@swmansion/argent 0.27.1-next.21 → 0.27.1-next.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-cmds.mjs +86 -11
- package/dist/client.js +76 -5
- package/dist/installer.mjs +1 -1
- package/dist/mcp-server.mjs +76 -5
- package/dist/tool-args.generated.d.ts +1 -1
- package/dist/tool-server.cjs +663 -551
- package/package.json +1 -1
- package/skills/argent-create-flow/references/live-authoring.md +9 -1
package/dist/cli-cmds.mjs
CHANGED
|
@@ -9363,6 +9363,35 @@ var FLOW_NAME_CHARSET = "[A-Za-z0-9_-]+";
|
|
|
9363
9363
|
var FLOW_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}$`);
|
|
9364
9364
|
var FLOW_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.yaml$`);
|
|
9365
9365
|
var SCRIPT_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.mjs$`);
|
|
9366
|
+
function interpolateFileInputPath(template, args) {
|
|
9367
|
+
let missing = false;
|
|
9368
|
+
const out = template.replace(/\$\{([A-Za-z0-9_]+)\}/g, (_m, name) => {
|
|
9369
|
+
const v = args[name];
|
|
9370
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
9371
|
+
missing = true;
|
|
9372
|
+
return "";
|
|
9373
|
+
}
|
|
9374
|
+
return v;
|
|
9375
|
+
});
|
|
9376
|
+
return missing ? null : out;
|
|
9377
|
+
}
|
|
9378
|
+
var TOOL_FILE_EXTENSIONS = [".png", ".yaml"];
|
|
9379
|
+
function hasToolFileExtension(file2) {
|
|
9380
|
+
const name = file2.toLowerCase();
|
|
9381
|
+
return TOOL_FILE_EXTENSIONS.some((extension) => name.endsWith(extension));
|
|
9382
|
+
}
|
|
9383
|
+
function toolStepFiles(specs, args) {
|
|
9384
|
+
const files = [];
|
|
9385
|
+
for (const spec of specs ?? []) {
|
|
9386
|
+
if (spec.skipWhenSet !== void 0 && args[spec.skipWhenSet] !== void 0) continue;
|
|
9387
|
+
const filled = interpolateFileInputPath(spec.path, args);
|
|
9388
|
+
if (filled !== null) files.push({ spec, path: filled });
|
|
9389
|
+
}
|
|
9390
|
+
return files;
|
|
9391
|
+
}
|
|
9392
|
+
function isClientFileArgument({ spec, path: path23 }) {
|
|
9393
|
+
return spec.kind === "file" && spec.path === `\${${spec.target}}` && path23.startsWith("/") && hasToolFileExtension(path23);
|
|
9394
|
+
}
|
|
9366
9395
|
|
|
9367
9396
|
// ../registry/src/flow-file-refs.ts
|
|
9368
9397
|
import * as fs2 from "node:fs/promises";
|
|
@@ -9391,6 +9420,7 @@ function isRecord(value) {
|
|
|
9391
9420
|
function collectFlowRequests(doc) {
|
|
9392
9421
|
const runTargets = /* @__PURE__ */ new Set();
|
|
9393
9422
|
const snapshots = /* @__PURE__ */ new Set();
|
|
9423
|
+
const toolSteps = [];
|
|
9394
9424
|
const seen = /* @__PURE__ */ new Set();
|
|
9395
9425
|
const visit = (steps, depth) => {
|
|
9396
9426
|
if (!Array.isArray(steps) || depth > MAX_BLOCK_NESTING || seen.has(steps)) return;
|
|
@@ -9404,11 +9434,14 @@ function collectFlowRequests(doc) {
|
|
|
9404
9434
|
}
|
|
9405
9435
|
const snapshot = isRecord(step.snapshot) ? step.snapshot.name : step.snapshot;
|
|
9406
9436
|
if (typeof snapshot === "string" && FLOW_NAME_PATTERN.test(snapshot)) snapshots.add(snapshot);
|
|
9437
|
+
if (typeof step.tool === "string") {
|
|
9438
|
+
toolSteps.push({ tool: step.tool, args: isRecord(step.args) ? step.args : {} });
|
|
9439
|
+
}
|
|
9407
9440
|
visit(step.steps, depth + 1);
|
|
9408
9441
|
}
|
|
9409
9442
|
};
|
|
9410
9443
|
if (isRecord(doc)) visit(doc.steps, 0);
|
|
9411
|
-
return { runTargets: [...runTargets], snapshots: [...snapshots] };
|
|
9444
|
+
return { runTargets: [...runTargets], snapshots: [...snapshots], toolSteps };
|
|
9412
9445
|
}
|
|
9413
9446
|
function baselineKeyFor(canonicalPath, flowName) {
|
|
9414
9447
|
const stem = path2.basename(canonicalPath, ".yaml");
|
|
@@ -24978,6 +25011,29 @@ async function readFlowMember(anchorDir, target, roots, budget, opts) {
|
|
|
24978
25011
|
const sent = await sendBytes(member, canonical, budget, opts);
|
|
24979
25012
|
return { ...sent, text: sent.bytes?.toString("utf8") };
|
|
24980
25013
|
}
|
|
25014
|
+
async function readToolMember(file2, roots, budget, opts) {
|
|
25015
|
+
const member = { role: "tool", key: file2, path: file2 };
|
|
25016
|
+
const refuse = (reason) => ({
|
|
25017
|
+
member: { ...member, state: "refused", error: reason },
|
|
25018
|
+
sent: `refused (${reason})`
|
|
25019
|
+
});
|
|
25020
|
+
const { canonical } = await landing(file2);
|
|
25021
|
+
if (!roots.some((root) => isWithin(canonical, root))) {
|
|
25022
|
+
return refuse(`${file2} is outside every root this client serves (${roots.join(", ")})`);
|
|
25023
|
+
}
|
|
25024
|
+
if (!hasToolFileExtension(canonical)) {
|
|
25025
|
+
return refuse(`${file2} links to a file that is not one of ${TOOL_FILE_EXTENSIONS.join(", ")}`);
|
|
25026
|
+
}
|
|
25027
|
+
const st = await stat3(canonical).catch((err) => err);
|
|
25028
|
+
if (st instanceof Error) {
|
|
25029
|
+
if (st.code === "ENOENT" || st.code === "ENOTDIR") {
|
|
25030
|
+
return { member: { ...member, state: "missing" }, sent: "missing" };
|
|
25031
|
+
}
|
|
25032
|
+
return refuse(st.message);
|
|
25033
|
+
}
|
|
25034
|
+
if (st.isDirectory()) return refuse("EISDIR: illegal operation on a directory, read");
|
|
25035
|
+
return sendBytes(member, canonical, budget, opts);
|
|
25036
|
+
}
|
|
24981
25037
|
function callPlatform(args) {
|
|
24982
25038
|
if (typeof args.device === "string" && args.device.startsWith("chromium-cdp-")) return "chromium";
|
|
24983
25039
|
if (typeof args.platform !== "string") return void 0;
|
|
@@ -25032,7 +25088,7 @@ async function sendBytes(member, real, budget, opts) {
|
|
|
25032
25088
|
bytes
|
|
25033
25089
|
};
|
|
25034
25090
|
}
|
|
25035
|
-
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, emit) {
|
|
25091
|
+
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, sent, emit) {
|
|
25036
25092
|
const dir = path5.join(
|
|
25037
25093
|
path5.dirname(canonical),
|
|
25038
25094
|
"__baselines__",
|
|
@@ -25051,6 +25107,7 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
25051
25107
|
if (!name.endsWith(".png")) continue;
|
|
25052
25108
|
if (!updates && !prefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
25053
25109
|
const file2 = path5.join(dir, name);
|
|
25110
|
+
if (sent.has(file2)) continue;
|
|
25054
25111
|
const member = { role: "baseline", key: file2, path: file2 };
|
|
25055
25112
|
const entry = await baselineEntry(file2, roots, updates);
|
|
25056
25113
|
if (entry.state === "refused") {
|
|
@@ -25060,8 +25117,8 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
25060
25117
|
} else if (updates) {
|
|
25061
25118
|
emit({ ...member, state: "listed" }, "listed");
|
|
25062
25119
|
} else {
|
|
25063
|
-
const
|
|
25064
|
-
emit(
|
|
25120
|
+
const sent2 = await sendBytes(member, entry.real, budget, opts);
|
|
25121
|
+
emit(sent2.member, sent2.sent);
|
|
25065
25122
|
}
|
|
25066
25123
|
}
|
|
25067
25124
|
}
|
|
@@ -25096,6 +25153,17 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
25096
25153
|
}
|
|
25097
25154
|
const requests = collectFlowRequests(doc);
|
|
25098
25155
|
for (const name of requests.snapshots) snapshots.add(name);
|
|
25156
|
+
for (const step of requests.toolSteps) {
|
|
25157
|
+
if (step.tool === "flow-execute") continue;
|
|
25158
|
+
for (const file3 of toolStepFiles(opts.toolFileInputs?.(step.tool), step.args)) {
|
|
25159
|
+
const { unwrapWhenSet } = file3.spec;
|
|
25160
|
+
if (unwrapWhenSet !== void 0 && step.args[unwrapWhenSet] !== void 0) continue;
|
|
25161
|
+
if (!isClientFileArgument(file3) || seen.has(file3.path)) continue;
|
|
25162
|
+
seen.add(file3.path);
|
|
25163
|
+
const read = await readToolMember(file3.path, roots, budget, opts);
|
|
25164
|
+
emit(read.member, read.sent);
|
|
25165
|
+
}
|
|
25166
|
+
}
|
|
25099
25167
|
const anchorDir = path5.dirname(file2.canonical);
|
|
25100
25168
|
for (const target of requests.runTargets) {
|
|
25101
25169
|
const key = flowMemberKey(anchorDir, target);
|
|
@@ -25118,6 +25186,7 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
25118
25186
|
roots,
|
|
25119
25187
|
budget,
|
|
25120
25188
|
opts,
|
|
25189
|
+
seen,
|
|
25121
25190
|
emit
|
|
25122
25191
|
);
|
|
25123
25192
|
}
|
|
@@ -25282,7 +25351,8 @@ function createToolsClient(options = {}) {
|
|
|
25282
25351
|
opts?.signal?.throwIfAborted();
|
|
25283
25352
|
const { url: url2, token, remote } = await route();
|
|
25284
25353
|
let finalArgs = args;
|
|
25285
|
-
const
|
|
25354
|
+
const tools2 = await fetchTools({ signal: opts?.signal });
|
|
25355
|
+
const meta3 = tools2.find((t2) => t2.name === name) ?? null;
|
|
25286
25356
|
const baselineDirs = [];
|
|
25287
25357
|
if (meta3?.fileInputs?.length) {
|
|
25288
25358
|
if (remote) assertRequiredPresent(meta3, args);
|
|
@@ -25292,6 +25362,7 @@ function createToolsClient(options = {}) {
|
|
|
25292
25362
|
log: diagnose,
|
|
25293
25363
|
baselineDirs,
|
|
25294
25364
|
collectMembers: collectFlowMembers,
|
|
25365
|
+
toolFileInputs: (tool) => tools2.find((t2) => t2.name === tool)?.fileInputs,
|
|
25295
25366
|
signal: opts?.signal
|
|
25296
25367
|
});
|
|
25297
25368
|
}
|
|
@@ -29877,7 +29948,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
29877
29948
|
var SESSION_ID2 = randomUUID5();
|
|
29878
29949
|
function readCliVersion() {
|
|
29879
29950
|
if (true) {
|
|
29880
|
-
return "0.27.1-next.
|
|
29951
|
+
return "0.27.1-next.22";
|
|
29881
29952
|
}
|
|
29882
29953
|
return "0.0.0";
|
|
29883
29954
|
}
|
|
@@ -31231,11 +31302,15 @@ counts the remaining flows skipped.
|
|
|
31231
31302
|
The CLI sends a run to its tool-server: the local one that starts
|
|
31232
31303
|
automatically, or the one that \`argent link\` or ARGENT_TOOLS_URL names. Over
|
|
31233
31304
|
a link, the CLI uploads the flow file, the run: fragments that it reaches and
|
|
31234
|
-
the snapshot baselines that the run compares.
|
|
31235
|
-
that
|
|
31236
|
-
|
|
31237
|
-
|
|
31238
|
-
- a
|
|
31305
|
+
the snapshot baselines that the run compares. It also uploads each .png or
|
|
31306
|
+
.yaml file that a tool: step takes as a file argument by an absolute path. The
|
|
31307
|
+
CLI writes the new baselines that the run returns. The tool-server rejects the
|
|
31308
|
+
flow before the first step when the flow or a fragment has one of these steps:
|
|
31309
|
+
- a script: step
|
|
31310
|
+
- a tool: step that runs or records a flow
|
|
31311
|
+
- a tool: step that takes a directory, an app or an output directory
|
|
31312
|
+
- a tool: step whose tool builds a file path from several arguments
|
|
31313
|
+
- a tool: step that names a relative path or a file other than .png or .yaml
|
|
31239
31314
|
Without a link, all step kinds run.
|
|
31240
31315
|
|
|
31241
31316
|
Subcommands:
|
package/dist/client.js
CHANGED
|
@@ -8566,6 +8566,35 @@ var FLOW_NAME_CHARSET = "[A-Za-z0-9_-]+";
|
|
|
8566
8566
|
var FLOW_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}$`);
|
|
8567
8567
|
var FLOW_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.yaml$`);
|
|
8568
8568
|
var SCRIPT_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.mjs$`);
|
|
8569
|
+
function interpolateFileInputPath(template, args) {
|
|
8570
|
+
let missing = false;
|
|
8571
|
+
const out = template.replace(/\$\{([A-Za-z0-9_]+)\}/g, (_m, name) => {
|
|
8572
|
+
const v = args[name];
|
|
8573
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
8574
|
+
missing = true;
|
|
8575
|
+
return "";
|
|
8576
|
+
}
|
|
8577
|
+
return v;
|
|
8578
|
+
});
|
|
8579
|
+
return missing ? null : out;
|
|
8580
|
+
}
|
|
8581
|
+
var TOOL_FILE_EXTENSIONS = [".png", ".yaml"];
|
|
8582
|
+
function hasToolFileExtension(file) {
|
|
8583
|
+
const name = file.toLowerCase();
|
|
8584
|
+
return TOOL_FILE_EXTENSIONS.some((extension) => name.endsWith(extension));
|
|
8585
|
+
}
|
|
8586
|
+
function toolStepFiles(specs, args) {
|
|
8587
|
+
const files = [];
|
|
8588
|
+
for (const spec of specs ?? []) {
|
|
8589
|
+
if (spec.skipWhenSet !== void 0 && args[spec.skipWhenSet] !== void 0) continue;
|
|
8590
|
+
const filled = interpolateFileInputPath(spec.path, args);
|
|
8591
|
+
if (filled !== null) files.push({ spec, path: filled });
|
|
8592
|
+
}
|
|
8593
|
+
return files;
|
|
8594
|
+
}
|
|
8595
|
+
function isClientFileArgument({ spec, path: path12 }) {
|
|
8596
|
+
return spec.kind === "file" && spec.path === `\${${spec.target}}` && path12.startsWith("/") && hasToolFileExtension(path12);
|
|
8597
|
+
}
|
|
8569
8598
|
|
|
8570
8599
|
// ../registry/src/flow-file-refs.ts
|
|
8571
8600
|
import * as fs5 from "node:fs/promises";
|
|
@@ -8594,6 +8623,7 @@ function isRecord(value) {
|
|
|
8594
8623
|
function collectFlowRequests(doc) {
|
|
8595
8624
|
const runTargets = /* @__PURE__ */ new Set();
|
|
8596
8625
|
const snapshots = /* @__PURE__ */ new Set();
|
|
8626
|
+
const toolSteps = [];
|
|
8597
8627
|
const seen = /* @__PURE__ */ new Set();
|
|
8598
8628
|
const visit = (steps, depth) => {
|
|
8599
8629
|
if (!Array.isArray(steps) || depth > MAX_BLOCK_NESTING || seen.has(steps)) return;
|
|
@@ -8607,11 +8637,14 @@ function collectFlowRequests(doc) {
|
|
|
8607
8637
|
}
|
|
8608
8638
|
const snapshot = isRecord(step.snapshot) ? step.snapshot.name : step.snapshot;
|
|
8609
8639
|
if (typeof snapshot === "string" && FLOW_NAME_PATTERN.test(snapshot)) snapshots.add(snapshot);
|
|
8640
|
+
if (typeof step.tool === "string") {
|
|
8641
|
+
toolSteps.push({ tool: step.tool, args: isRecord(step.args) ? step.args : {} });
|
|
8642
|
+
}
|
|
8610
8643
|
visit(step.steps, depth + 1);
|
|
8611
8644
|
}
|
|
8612
8645
|
};
|
|
8613
8646
|
if (isRecord(doc)) visit(doc.steps, 0);
|
|
8614
|
-
return { runTargets: [...runTargets], snapshots: [...snapshots] };
|
|
8647
|
+
return { runTargets: [...runTargets], snapshots: [...snapshots], toolSteps };
|
|
8615
8648
|
}
|
|
8616
8649
|
function baselineKeyFor(canonicalPath, flowName) {
|
|
8617
8650
|
const stem = path7.basename(canonicalPath, ".yaml");
|
|
@@ -9562,6 +9595,29 @@ async function readFlowMember(anchorDir, target, roots, budget, opts) {
|
|
|
9562
9595
|
const sent = await sendBytes(member, canonical, budget, opts);
|
|
9563
9596
|
return { ...sent, text: sent.bytes?.toString("utf8") };
|
|
9564
9597
|
}
|
|
9598
|
+
async function readToolMember(file, roots, budget, opts) {
|
|
9599
|
+
const member = { role: "tool", key: file, path: file };
|
|
9600
|
+
const refuse = (reason) => ({
|
|
9601
|
+
member: { ...member, state: "refused", error: reason },
|
|
9602
|
+
sent: `refused (${reason})`
|
|
9603
|
+
});
|
|
9604
|
+
const { canonical } = await landing(file);
|
|
9605
|
+
if (!roots.some((root) => isWithin(canonical, root))) {
|
|
9606
|
+
return refuse(`${file} is outside every root this client serves (${roots.join(", ")})`);
|
|
9607
|
+
}
|
|
9608
|
+
if (!hasToolFileExtension(canonical)) {
|
|
9609
|
+
return refuse(`${file} links to a file that is not one of ${TOOL_FILE_EXTENSIONS.join(", ")}`);
|
|
9610
|
+
}
|
|
9611
|
+
const st = await stat3(canonical).catch((err) => err);
|
|
9612
|
+
if (st instanceof Error) {
|
|
9613
|
+
if (st.code === "ENOENT" || st.code === "ENOTDIR") {
|
|
9614
|
+
return { member: { ...member, state: "missing" }, sent: "missing" };
|
|
9615
|
+
}
|
|
9616
|
+
return refuse(st.message);
|
|
9617
|
+
}
|
|
9618
|
+
if (st.isDirectory()) return refuse("EISDIR: illegal operation on a directory, read");
|
|
9619
|
+
return sendBytes(member, canonical, budget, opts);
|
|
9620
|
+
}
|
|
9565
9621
|
function callPlatform(args) {
|
|
9566
9622
|
if (typeof args.device === "string" && args.device.startsWith("chromium-cdp-")) return "chromium";
|
|
9567
9623
|
if (typeof args.platform !== "string") return void 0;
|
|
@@ -9616,7 +9672,7 @@ async function sendBytes(member, real, budget, opts) {
|
|
|
9616
9672
|
bytes
|
|
9617
9673
|
};
|
|
9618
9674
|
}
|
|
9619
|
-
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, emit) {
|
|
9675
|
+
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, sent, emit) {
|
|
9620
9676
|
const dir = path10.join(
|
|
9621
9677
|
path10.dirname(canonical),
|
|
9622
9678
|
"__baselines__",
|
|
@@ -9635,6 +9691,7 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
9635
9691
|
if (!name.endsWith(".png")) continue;
|
|
9636
9692
|
if (!updates && !prefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
9637
9693
|
const file = path10.join(dir, name);
|
|
9694
|
+
if (sent.has(file)) continue;
|
|
9638
9695
|
const member = { role: "baseline", key: file, path: file };
|
|
9639
9696
|
const entry = await baselineEntry(file, roots, updates);
|
|
9640
9697
|
if (entry.state === "refused") {
|
|
@@ -9644,8 +9701,8 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
9644
9701
|
} else if (updates) {
|
|
9645
9702
|
emit({ ...member, state: "listed" }, "listed");
|
|
9646
9703
|
} else {
|
|
9647
|
-
const
|
|
9648
|
-
emit(
|
|
9704
|
+
const sent2 = await sendBytes(member, entry.real, budget, opts);
|
|
9705
|
+
emit(sent2.member, sent2.sent);
|
|
9649
9706
|
}
|
|
9650
9707
|
}
|
|
9651
9708
|
}
|
|
@@ -9680,6 +9737,17 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
9680
9737
|
}
|
|
9681
9738
|
const requests = collectFlowRequests(doc);
|
|
9682
9739
|
for (const name of requests.snapshots) snapshots.add(name);
|
|
9740
|
+
for (const step of requests.toolSteps) {
|
|
9741
|
+
if (step.tool === "flow-execute") continue;
|
|
9742
|
+
for (const file2 of toolStepFiles(opts.toolFileInputs?.(step.tool), step.args)) {
|
|
9743
|
+
const { unwrapWhenSet } = file2.spec;
|
|
9744
|
+
if (unwrapWhenSet !== void 0 && step.args[unwrapWhenSet] !== void 0) continue;
|
|
9745
|
+
if (!isClientFileArgument(file2) || seen.has(file2.path)) continue;
|
|
9746
|
+
seen.add(file2.path);
|
|
9747
|
+
const read = await readToolMember(file2.path, roots, budget, opts);
|
|
9748
|
+
emit(read.member, read.sent);
|
|
9749
|
+
}
|
|
9750
|
+
}
|
|
9683
9751
|
const anchorDir = path10.dirname(file.canonical);
|
|
9684
9752
|
for (const target of requests.runTargets) {
|
|
9685
9753
|
const key = flowMemberKey(anchorDir, target);
|
|
@@ -9702,6 +9770,7 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
9702
9770
|
roots,
|
|
9703
9771
|
budget,
|
|
9704
9772
|
opts,
|
|
9773
|
+
seen,
|
|
9705
9774
|
emit
|
|
9706
9775
|
);
|
|
9707
9776
|
}
|
|
@@ -9866,7 +9935,8 @@ function createToolsClient(options = {}) {
|
|
|
9866
9935
|
opts?.signal?.throwIfAborted();
|
|
9867
9936
|
const { url, token, remote } = await route();
|
|
9868
9937
|
let finalArgs = args;
|
|
9869
|
-
const
|
|
9938
|
+
const tools = await fetchTools({ signal: opts?.signal });
|
|
9939
|
+
const meta = tools.find((t) => t.name === name) ?? null;
|
|
9870
9940
|
const baselineDirs = [];
|
|
9871
9941
|
if (meta?.fileInputs?.length) {
|
|
9872
9942
|
if (remote) assertRequiredPresent(meta, args);
|
|
@@ -9876,6 +9946,7 @@ function createToolsClient(options = {}) {
|
|
|
9876
9946
|
log: diagnose,
|
|
9877
9947
|
baselineDirs,
|
|
9878
9948
|
collectMembers: collectFlowMembers,
|
|
9949
|
+
toolFileInputs: (tool) => tools.find((t) => t.name === tool)?.fileInputs,
|
|
9879
9950
|
signal: opts?.signal
|
|
9880
9951
|
});
|
|
9881
9952
|
}
|
package/dist/installer.mjs
CHANGED
|
@@ -16736,7 +16736,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16736
16736
|
var SESSION_ID = randomUUID4();
|
|
16737
16737
|
function readCliVersion() {
|
|
16738
16738
|
if (true) {
|
|
16739
|
-
return "0.27.1-next.
|
|
16739
|
+
return "0.27.1-next.22";
|
|
16740
16740
|
}
|
|
16741
16741
|
return "0.0.0";
|
|
16742
16742
|
}
|
package/dist/mcp-server.mjs
CHANGED
|
@@ -23601,6 +23601,35 @@ var FLOW_NAME_CHARSET = "[A-Za-z0-9_-]+";
|
|
|
23601
23601
|
var FLOW_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}$`);
|
|
23602
23602
|
var FLOW_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.yaml$`);
|
|
23603
23603
|
var SCRIPT_FILE_NAME_PATTERN = new RegExp(`^${FLOW_NAME_CHARSET}\\.mjs$`);
|
|
23604
|
+
function interpolateFileInputPath(template, args) {
|
|
23605
|
+
let missing = false;
|
|
23606
|
+
const out = template.replace(/\$\{([A-Za-z0-9_]+)\}/g, (_m, name) => {
|
|
23607
|
+
const v = args[name];
|
|
23608
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
23609
|
+
missing = true;
|
|
23610
|
+
return "";
|
|
23611
|
+
}
|
|
23612
|
+
return v;
|
|
23613
|
+
});
|
|
23614
|
+
return missing ? null : out;
|
|
23615
|
+
}
|
|
23616
|
+
var TOOL_FILE_EXTENSIONS = [".png", ".yaml"];
|
|
23617
|
+
function hasToolFileExtension(file) {
|
|
23618
|
+
const name = file.toLowerCase();
|
|
23619
|
+
return TOOL_FILE_EXTENSIONS.some((extension) => name.endsWith(extension));
|
|
23620
|
+
}
|
|
23621
|
+
function toolStepFiles(specs, args) {
|
|
23622
|
+
const files = [];
|
|
23623
|
+
for (const spec of specs ?? []) {
|
|
23624
|
+
if (spec.skipWhenSet !== void 0 && args[spec.skipWhenSet] !== void 0) continue;
|
|
23625
|
+
const filled = interpolateFileInputPath(spec.path, args);
|
|
23626
|
+
if (filled !== null) files.push({ spec, path: filled });
|
|
23627
|
+
}
|
|
23628
|
+
return files;
|
|
23629
|
+
}
|
|
23630
|
+
function isClientFileArgument({ spec, path: path15 }) {
|
|
23631
|
+
return spec.kind === "file" && spec.path === `\${${spec.target}}` && path15.startsWith("/") && hasToolFileExtension(path15);
|
|
23632
|
+
}
|
|
23604
23633
|
|
|
23605
23634
|
// ../registry/src/flow-file-refs.ts
|
|
23606
23635
|
import * as fs2 from "node:fs/promises";
|
|
@@ -23629,6 +23658,7 @@ function isRecord(value) {
|
|
|
23629
23658
|
function collectFlowRequests(doc) {
|
|
23630
23659
|
const runTargets = /* @__PURE__ */ new Set();
|
|
23631
23660
|
const snapshots = /* @__PURE__ */ new Set();
|
|
23661
|
+
const toolSteps = [];
|
|
23632
23662
|
const seen = /* @__PURE__ */ new Set();
|
|
23633
23663
|
const visit = (steps, depth) => {
|
|
23634
23664
|
if (!Array.isArray(steps) || depth > MAX_BLOCK_NESTING || seen.has(steps)) return;
|
|
@@ -23642,11 +23672,14 @@ function collectFlowRequests(doc) {
|
|
|
23642
23672
|
}
|
|
23643
23673
|
const snapshot = isRecord(step.snapshot) ? step.snapshot.name : step.snapshot;
|
|
23644
23674
|
if (typeof snapshot === "string" && FLOW_NAME_PATTERN.test(snapshot)) snapshots.add(snapshot);
|
|
23675
|
+
if (typeof step.tool === "string") {
|
|
23676
|
+
toolSteps.push({ tool: step.tool, args: isRecord(step.args) ? step.args : {} });
|
|
23677
|
+
}
|
|
23645
23678
|
visit(step.steps, depth + 1);
|
|
23646
23679
|
}
|
|
23647
23680
|
};
|
|
23648
23681
|
if (isRecord(doc)) visit(doc.steps, 0);
|
|
23649
|
-
return { runTargets: [...runTargets], snapshots: [...snapshots] };
|
|
23682
|
+
return { runTargets: [...runTargets], snapshots: [...snapshots], toolSteps };
|
|
23650
23683
|
}
|
|
23651
23684
|
function baselineKeyFor(canonicalPath, flowName) {
|
|
23652
23685
|
const stem = path2.basename(canonicalPath, ".yaml");
|
|
@@ -24636,6 +24669,29 @@ async function readFlowMember(anchorDir, target, roots, budget, opts) {
|
|
|
24636
24669
|
const sent = await sendBytes(member, canonical, budget, opts);
|
|
24637
24670
|
return { ...sent, text: sent.bytes?.toString("utf8") };
|
|
24638
24671
|
}
|
|
24672
|
+
async function readToolMember(file, roots, budget, opts) {
|
|
24673
|
+
const member = { role: "tool", key: file, path: file };
|
|
24674
|
+
const refuse = (reason) => ({
|
|
24675
|
+
member: { ...member, state: "refused", error: reason },
|
|
24676
|
+
sent: `refused (${reason})`
|
|
24677
|
+
});
|
|
24678
|
+
const { canonical } = await landing(file);
|
|
24679
|
+
if (!roots.some((root) => isWithin(canonical, root))) {
|
|
24680
|
+
return refuse(`${file} is outside every root this client serves (${roots.join(", ")})`);
|
|
24681
|
+
}
|
|
24682
|
+
if (!hasToolFileExtension(canonical)) {
|
|
24683
|
+
return refuse(`${file} links to a file that is not one of ${TOOL_FILE_EXTENSIONS.join(", ")}`);
|
|
24684
|
+
}
|
|
24685
|
+
const st = await stat3(canonical).catch((err) => err);
|
|
24686
|
+
if (st instanceof Error) {
|
|
24687
|
+
if (st.code === "ENOENT" || st.code === "ENOTDIR") {
|
|
24688
|
+
return { member: { ...member, state: "missing" }, sent: "missing" };
|
|
24689
|
+
}
|
|
24690
|
+
return refuse(st.message);
|
|
24691
|
+
}
|
|
24692
|
+
if (st.isDirectory()) return refuse("EISDIR: illegal operation on a directory, read");
|
|
24693
|
+
return sendBytes(member, canonical, budget, opts);
|
|
24694
|
+
}
|
|
24639
24695
|
function callPlatform(args) {
|
|
24640
24696
|
if (typeof args.device === "string" && args.device.startsWith("chromium-cdp-")) return "chromium";
|
|
24641
24697
|
if (typeof args.platform !== "string") return void 0;
|
|
@@ -24690,7 +24746,7 @@ async function sendBytes(member, real, budget, opts) {
|
|
|
24690
24746
|
bytes
|
|
24691
24747
|
};
|
|
24692
24748
|
}
|
|
24693
|
-
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, emit) {
|
|
24749
|
+
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, sent, emit) {
|
|
24694
24750
|
const dir = path5.join(
|
|
24695
24751
|
path5.dirname(canonical),
|
|
24696
24752
|
"__baselines__",
|
|
@@ -24709,6 +24765,7 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
24709
24765
|
if (!name.endsWith(".png")) continue;
|
|
24710
24766
|
if (!updates && !prefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
24711
24767
|
const file = path5.join(dir, name);
|
|
24768
|
+
if (sent.has(file)) continue;
|
|
24712
24769
|
const member = { role: "baseline", key: file, path: file };
|
|
24713
24770
|
const entry = await baselineEntry(file, roots, updates);
|
|
24714
24771
|
if (entry.state === "refused") {
|
|
@@ -24718,8 +24775,8 @@ async function collectBaselineMembers(canonical, flowName, snapshots, args, root
|
|
|
24718
24775
|
} else if (updates) {
|
|
24719
24776
|
emit({ ...member, state: "listed" }, "listed");
|
|
24720
24777
|
} else {
|
|
24721
|
-
const
|
|
24722
|
-
emit(
|
|
24778
|
+
const sent2 = await sendBytes(member, entry.real, budget, opts);
|
|
24779
|
+
emit(sent2.member, sent2.sent);
|
|
24723
24780
|
}
|
|
24724
24781
|
}
|
|
24725
24782
|
}
|
|
@@ -24754,6 +24811,17 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
24754
24811
|
}
|
|
24755
24812
|
const requests = collectFlowRequests(doc);
|
|
24756
24813
|
for (const name of requests.snapshots) snapshots.add(name);
|
|
24814
|
+
for (const step of requests.toolSteps) {
|
|
24815
|
+
if (step.tool === "flow-execute") continue;
|
|
24816
|
+
for (const file2 of toolStepFiles(opts.toolFileInputs?.(step.tool), step.args)) {
|
|
24817
|
+
const { unwrapWhenSet } = file2.spec;
|
|
24818
|
+
if (unwrapWhenSet !== void 0 && step.args[unwrapWhenSet] !== void 0) continue;
|
|
24819
|
+
if (!isClientFileArgument(file2) || seen.has(file2.path)) continue;
|
|
24820
|
+
seen.add(file2.path);
|
|
24821
|
+
const read = await readToolMember(file2.path, roots, budget, opts);
|
|
24822
|
+
emit(read.member, read.sent);
|
|
24823
|
+
}
|
|
24824
|
+
}
|
|
24757
24825
|
const anchorDir = path5.dirname(file.canonical);
|
|
24758
24826
|
for (const target of requests.runTargets) {
|
|
24759
24827
|
const key = flowMemberKey(anchorDir, target);
|
|
@@ -24776,6 +24844,7 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
24776
24844
|
roots,
|
|
24777
24845
|
budget,
|
|
24778
24846
|
opts,
|
|
24847
|
+
seen,
|
|
24779
24848
|
emit
|
|
24780
24849
|
);
|
|
24781
24850
|
}
|
|
@@ -24940,7 +25009,8 @@ function createToolsClient(options = {}) {
|
|
|
24940
25009
|
opts?.signal?.throwIfAborted();
|
|
24941
25010
|
const { url, token, remote } = await route();
|
|
24942
25011
|
let finalArgs = args;
|
|
24943
|
-
const
|
|
25012
|
+
const tools = await fetchTools({ signal: opts?.signal });
|
|
25013
|
+
const meta2 = tools.find((t) => t.name === name) ?? null;
|
|
24944
25014
|
const baselineDirs = [];
|
|
24945
25015
|
if (meta2?.fileInputs?.length) {
|
|
24946
25016
|
if (remote) assertRequiredPresent(meta2, args);
|
|
@@ -24950,6 +25020,7 @@ function createToolsClient(options = {}) {
|
|
|
24950
25020
|
log: diagnose,
|
|
24951
25021
|
baselineDirs,
|
|
24952
25022
|
collectMembers: collectFlowMembers,
|
|
25023
|
+
toolFileInputs: (tool) => tools.find((t) => t.name === tool)?.fileInputs,
|
|
24953
25024
|
signal: opts?.signal
|
|
24954
25025
|
});
|
|
24955
25026
|
}
|
|
@@ -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 flow .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 that the run compares, and the tool-server runs those copies. Keep run: fragments under project_root or beside the flow file. With updateBaselines, the client writes the new baselines when the run ends. Over a link, the flow and its fragments must not have script: steps
|
|
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, its run: fragments and the snapshot baselines that the run compares, and the tool-server runs those copies. Keep run: fragments under project_root or beside the flow 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 flow file. Over a link, the flow and its fragments must not have: script: steps; tool: steps that run or record a flow; 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. */
|
|
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;
|