@swmansion/argent 0.27.1-next.20 → 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 +331 -56
- package/dist/client.js +296 -48
- package/dist/installer.mjs +17 -7
- package/dist/mcp-server.mjs +306 -52
- package/dist/tool-args.generated.d.ts +1 -1
- package/dist/tool-server.cjs +853 -591
- 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";
|
|
@@ -9390,6 +9419,8 @@ function isRecord(value) {
|
|
|
9390
9419
|
}
|
|
9391
9420
|
function collectFlowRequests(doc) {
|
|
9392
9421
|
const runTargets = /* @__PURE__ */ new Set();
|
|
9422
|
+
const snapshots = /* @__PURE__ */ new Set();
|
|
9423
|
+
const toolSteps = [];
|
|
9393
9424
|
const seen = /* @__PURE__ */ new Set();
|
|
9394
9425
|
const visit = (steps, depth) => {
|
|
9395
9426
|
if (!Array.isArray(steps) || depth > MAX_BLOCK_NESTING || seen.has(steps)) return;
|
|
@@ -9401,11 +9432,20 @@ function collectFlowRequests(doc) {
|
|
|
9401
9432
|
const target = completeRunExtension(run2);
|
|
9402
9433
|
if (FLOW_FILE_NAME_PATTERN.test(path2.posix.basename(target))) runTargets.add(target);
|
|
9403
9434
|
}
|
|
9435
|
+
const snapshot = isRecord(step.snapshot) ? step.snapshot.name : step.snapshot;
|
|
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
|
+
}
|
|
9404
9440
|
visit(step.steps, depth + 1);
|
|
9405
9441
|
}
|
|
9406
9442
|
};
|
|
9407
9443
|
if (isRecord(doc)) visit(doc.steps, 0);
|
|
9408
|
-
return { runTargets: [...runTargets] };
|
|
9444
|
+
return { runTargets: [...runTargets], snapshots: [...snapshots], toolSteps };
|
|
9445
|
+
}
|
|
9446
|
+
function baselineKeyFor(canonicalPath, flowName) {
|
|
9447
|
+
const stem = path2.basename(canonicalPath, ".yaml");
|
|
9448
|
+
return FLOW_NAME_PATTERN.test(stem) ? stem : flowName;
|
|
9409
9449
|
}
|
|
9410
9450
|
function flowMemberKey(anchorDir, target) {
|
|
9411
9451
|
return `${anchorDir}\0${target}`;
|
|
@@ -24461,7 +24501,17 @@ function parseLinkTarget(input) {
|
|
|
24461
24501
|
// ../argent-tools-client/src/file-inputs.ts
|
|
24462
24502
|
import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
|
|
24463
24503
|
import { createReadStream, rmSync } from "node:fs";
|
|
24464
|
-
import {
|
|
24504
|
+
import {
|
|
24505
|
+
chmod as chmod3,
|
|
24506
|
+
lstat,
|
|
24507
|
+
mkdir as mkdir3,
|
|
24508
|
+
readFile as readFile3,
|
|
24509
|
+
realpath as realpath2,
|
|
24510
|
+
rename as rename2,
|
|
24511
|
+
rm as rm2,
|
|
24512
|
+
stat as stat2,
|
|
24513
|
+
writeFile as writeFile3
|
|
24514
|
+
} from "node:fs/promises";
|
|
24465
24515
|
import { constants, tmpdir } from "node:os";
|
|
24466
24516
|
import * as path4 from "node:path";
|
|
24467
24517
|
|
|
@@ -24770,9 +24820,61 @@ function isAllowedClientFilePath(p) {
|
|
|
24770
24820
|
function isClientFileDirective2(value) {
|
|
24771
24821
|
return !!value && typeof value === "object" && value[CLIENT_FILE_MARKER2] === true && typeof value.path === "string" && typeof value.content === "string";
|
|
24772
24822
|
}
|
|
24773
|
-
async function
|
|
24823
|
+
async function replaceFile(target, bytes, mode) {
|
|
24824
|
+
const temp = path4.join(path4.dirname(target), `.baseline-${randomUUID3()}.tmp`);
|
|
24825
|
+
try {
|
|
24826
|
+
await writeFile3(temp, bytes, { flag: "wx" });
|
|
24827
|
+
if (mode !== void 0) await chmod3(temp, mode & 511);
|
|
24828
|
+
await rename2(temp, target);
|
|
24829
|
+
} catch (err) {
|
|
24830
|
+
await rm2(temp, { force: true });
|
|
24831
|
+
throw err;
|
|
24832
|
+
}
|
|
24833
|
+
}
|
|
24834
|
+
async function writeBaselineFile(file2, bytes, allowedDirs) {
|
|
24835
|
+
if (!path4.isAbsolute(file2) || path4.normalize(file2) !== file2 || file2.split(/[\\/]/).includes("..") || !file2.endsWith(".png") || !allowedDirs.includes(path4.dirname(file2))) {
|
|
24836
|
+
return allowedDirs.length === 0 ? "this call writes no baselines" : `${file2} is not a baseline of this call (${allowedDirs.map((d) => `${d}/<name>.png`).join(", ")})`;
|
|
24837
|
+
}
|
|
24838
|
+
if (bytes.length > MAX_CONTENT_BYTES) {
|
|
24839
|
+
return `${file2}: the baseline is larger than the 32 MiB cap on a file this client writes`;
|
|
24840
|
+
}
|
|
24841
|
+
const dir = path4.dirname(file2);
|
|
24842
|
+
await mkdir3(dir, { recursive: true });
|
|
24843
|
+
const link2 = await lstat(file2).catch(() => null);
|
|
24844
|
+
const real = link2 === null ? null : await realpath2(file2).catch(() => null);
|
|
24845
|
+
if (link2 !== null && real === null) return `${file2} is a symbolic link to a missing file`;
|
|
24846
|
+
if (real !== null) {
|
|
24847
|
+
if (!real.endsWith(".png")) return `${file2} links to a file that is not a PNG file`;
|
|
24848
|
+
if (path4.dirname(real) !== await realpath2(dir)) {
|
|
24849
|
+
return `${file2} links outside its baseline directory`;
|
|
24850
|
+
}
|
|
24851
|
+
}
|
|
24852
|
+
const existing = real === null ? null : await stat2(real);
|
|
24853
|
+
if (existing !== null && !existing.isFile()) return `${file2} is not a regular file`;
|
|
24854
|
+
await replaceFile(
|
|
24855
|
+
real ?? path4.join(await realpath2(dir), path4.basename(file2)),
|
|
24856
|
+
bytes,
|
|
24857
|
+
existing?.mode
|
|
24858
|
+
);
|
|
24859
|
+
return null;
|
|
24860
|
+
}
|
|
24861
|
+
async function applyClientFileDirectives(result, opts = {}) {
|
|
24774
24862
|
const written = [];
|
|
24863
|
+
const failed = [];
|
|
24775
24864
|
async function walk(value) {
|
|
24865
|
+
if (isClientFileDirective2(value) && value.encoding === "base64") {
|
|
24866
|
+
const error52 = await writeBaselineFile(
|
|
24867
|
+
value.path,
|
|
24868
|
+
Buffer.from(value.content, "base64"),
|
|
24869
|
+
opts.allowedDirs ?? []
|
|
24870
|
+
).catch((err) => err instanceof Error ? err.message : String(err));
|
|
24871
|
+
if (error52 !== null) {
|
|
24872
|
+
failed.push({ path: value.path, error: error52 });
|
|
24873
|
+
return { path: value.path, error: error52 };
|
|
24874
|
+
}
|
|
24875
|
+
written.push(value.path);
|
|
24876
|
+
return value.path;
|
|
24877
|
+
}
|
|
24776
24878
|
if (isClientFileDirective2(value)) {
|
|
24777
24879
|
if (!isAllowedClientFilePath(value.path)) return null;
|
|
24778
24880
|
try {
|
|
@@ -24785,7 +24887,9 @@ async function applyClientFileDirectives(result) {
|
|
|
24785
24887
|
}
|
|
24786
24888
|
}
|
|
24787
24889
|
if (Array.isArray(value)) {
|
|
24788
|
-
|
|
24890
|
+
const out = [];
|
|
24891
|
+
for (const item of value) out.push(await walk(item));
|
|
24892
|
+
return out;
|
|
24789
24893
|
}
|
|
24790
24894
|
if (value && typeof value === "object") {
|
|
24791
24895
|
const out = {};
|
|
@@ -24797,12 +24901,12 @@ async function applyClientFileDirectives(result) {
|
|
|
24797
24901
|
return value;
|
|
24798
24902
|
}
|
|
24799
24903
|
const rewritten = await walk(result);
|
|
24800
|
-
return { result: rewritten, written };
|
|
24904
|
+
return { result: rewritten, written, failed };
|
|
24801
24905
|
}
|
|
24802
24906
|
|
|
24803
24907
|
// ../argent-tools-client/src/flow-files.ts
|
|
24804
24908
|
var import_yaml = __toESM(require_dist(), 1);
|
|
24805
|
-
import { readFile as readFile4, realpath as
|
|
24909
|
+
import { readdir as readdir4, readFile as readFile4, realpath as realpath3, stat as stat3 } from "node:fs/promises";
|
|
24806
24910
|
import * as path5 from "node:path";
|
|
24807
24911
|
var INLINE_MEMBERS_BYTES = 256 * 1024;
|
|
24808
24912
|
var FLOW_FILES_LOG_ENV = "ARGENT_FLOW_FILES_LOG";
|
|
@@ -24837,7 +24941,7 @@ function savedFlowProject(file2) {
|
|
|
24837
24941
|
}
|
|
24838
24942
|
async function landing(spelled) {
|
|
24839
24943
|
try {
|
|
24840
|
-
const canonical = await
|
|
24944
|
+
const canonical = await realpath3(spelled);
|
|
24841
24945
|
return { canonical, fence: canonical };
|
|
24842
24946
|
} catch (err) {
|
|
24843
24947
|
const failure = err.code === "ENOENT" ? { missing: true } : { error: err.message };
|
|
@@ -24846,7 +24950,7 @@ async function landing(spelled) {
|
|
|
24846
24950
|
for (; ; ) {
|
|
24847
24951
|
const parent = path5.dirname(at);
|
|
24848
24952
|
if (parent === at) return { canonical, fence: spelled, ...failure };
|
|
24849
|
-
const real = await
|
|
24953
|
+
const real = await realpath3(parent).catch(() => null);
|
|
24850
24954
|
if (real !== null) {
|
|
24851
24955
|
return { canonical, fence: path5.join(real, path5.basename(at)), ...failure };
|
|
24852
24956
|
}
|
|
@@ -24866,7 +24970,7 @@ async function closureRoots(rootPath, rootCanonical, projectRoot) {
|
|
|
24866
24970
|
}
|
|
24867
24971
|
const real = [];
|
|
24868
24972
|
for (const candidate of candidates) {
|
|
24869
|
-
const resolved = await
|
|
24973
|
+
const resolved = await realpath3(candidate).catch(() => null);
|
|
24870
24974
|
if (resolved !== null && !real.includes(resolved)) real.push(resolved);
|
|
24871
24975
|
}
|
|
24872
24976
|
return real.filter((root) => !real.some((other) => other !== root && isWithin(root, other)));
|
|
@@ -24904,31 +25008,120 @@ async function readFlowMember(anchorDir, target, roots, budget, opts) {
|
|
|
24904
25008
|
if (!/\.ya?ml$/i.test(path5.basename(canonical))) {
|
|
24905
25009
|
return refuse(`${target} links to a file that is not a YAML file`);
|
|
24906
25010
|
}
|
|
24907
|
-
const
|
|
25011
|
+
const sent = await sendBytes(member, canonical, budget, opts);
|
|
25012
|
+
return { ...sent, text: sent.bytes?.toString("utf8") };
|
|
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
|
+
}
|
|
25037
|
+
function callPlatform(args) {
|
|
25038
|
+
if (typeof args.device === "string" && args.device.startsWith("chromium-cdp-")) return "chromium";
|
|
25039
|
+
if (typeof args.platform !== "string") return void 0;
|
|
25040
|
+
return args.platform === "ios-remote" ? "ios" : args.platform;
|
|
25041
|
+
}
|
|
25042
|
+
async function baselineEntry(file2, roots, forWrite) {
|
|
25043
|
+
const { canonical, error: error52 } = await landing(file2);
|
|
25044
|
+
if (!roots.some((root) => isWithin(canonical, root))) {
|
|
25045
|
+
return {
|
|
25046
|
+
state: "refused",
|
|
25047
|
+
error: `${file2} is outside every root this client serves (${roots.join(", ")})`
|
|
25048
|
+
};
|
|
25049
|
+
}
|
|
25050
|
+
if (error52 !== void 0) return { state: "refused", error: error52 };
|
|
25051
|
+
const st = await stat3(canonical).catch((err) => err);
|
|
25052
|
+
if (st instanceof Error) {
|
|
25053
|
+
if (st.code !== "ENOENT") return { state: "refused", error: st.message };
|
|
25054
|
+
return forWrite ? { state: "refused", error: `${file2} is a symbolic link to a missing file` } : { state: "missing" };
|
|
25055
|
+
}
|
|
25056
|
+
if (!canonical.endsWith(".png")) {
|
|
25057
|
+
return { state: "refused", error: `${file2} links to a file that is not a PNG file` };
|
|
25058
|
+
}
|
|
25059
|
+
if (forWrite && !st.isFile()) return { state: "refused", error: `${file2} is not a regular file` };
|
|
25060
|
+
if (st.isDirectory()) {
|
|
25061
|
+
return { state: "refused", error: "EISDIR: illegal operation on a directory, read" };
|
|
25062
|
+
}
|
|
25063
|
+
return st.isFile() ? { state: "ok", real: canonical } : { state: "missing" };
|
|
25064
|
+
}
|
|
25065
|
+
async function sendBytes(member, real, budget, opts) {
|
|
25066
|
+
const read = await readFileInputWire(real, { includeContent: true });
|
|
24908
25067
|
if (read?.contentOmitted) {
|
|
24909
|
-
|
|
25068
|
+
const error52 = `${real} is larger than the 32 MiB cap on a file sent to the tool-server`;
|
|
25069
|
+
return { member: { ...member, state: "refused", error: error52 }, sent: `refused (${error52})` };
|
|
24910
25070
|
}
|
|
24911
25071
|
if (read?.content === void 0) {
|
|
24912
|
-
|
|
24913
|
-
|
|
24914
|
-
|
|
24915
|
-
(err) => err instanceof Error ? err.message : String(err)
|
|
24916
|
-
)
|
|
25072
|
+
const error52 = await readFile4(real).then(
|
|
25073
|
+
() => `${real} could not be read on this client`,
|
|
25074
|
+
(err) => err instanceof Error ? err.message : String(err)
|
|
24917
25075
|
);
|
|
25076
|
+
return { member: { ...member, state: "refused", error: error52 }, sent: `refused (${error52})` };
|
|
24918
25077
|
}
|
|
24919
|
-
const
|
|
25078
|
+
const bytes = Buffer.from(read.content, "base64");
|
|
24920
25079
|
const size = read.size ?? 0;
|
|
24921
25080
|
if (budget.inline + size <= INLINE_MEMBERS_BYTES || !opts.uploadEndpoint) {
|
|
24922
25081
|
budget.inline += size;
|
|
24923
|
-
return { member: { ...member, ...read },
|
|
25082
|
+
return { member: { ...member, ...read }, sent: `inline ${size}`, bytes };
|
|
24924
25083
|
}
|
|
24925
|
-
const uploaded = await uploadFile(
|
|
25084
|
+
const uploaded = await uploadFile(real, opts.uploadEndpoint, opts.signal);
|
|
24926
25085
|
return {
|
|
24927
25086
|
member: { ...member, size: read.size, mtimeMs: read.mtimeMs, ...uploaded },
|
|
24928
|
-
|
|
24929
|
-
|
|
25087
|
+
sent: `upload ${size}`,
|
|
25088
|
+
bytes
|
|
24930
25089
|
};
|
|
24931
25090
|
}
|
|
25091
|
+
async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, sent, emit) {
|
|
25092
|
+
const dir = path5.join(
|
|
25093
|
+
path5.dirname(canonical),
|
|
25094
|
+
"__baselines__",
|
|
25095
|
+
baselineKeyFor(canonical, flowName)
|
|
25096
|
+
);
|
|
25097
|
+
const real = (await landing(dir)).canonical;
|
|
25098
|
+
if (!roots.some((root) => isWithin(real, root))) return;
|
|
25099
|
+
const updates = args.updateBaselines === true;
|
|
25100
|
+
if (updates) opts.baselineDirs?.push(dir);
|
|
25101
|
+
const platform = callPlatform(args);
|
|
25102
|
+
const prefixes = snapshots.map(
|
|
25103
|
+
(name) => `${name}__${platform === void 0 ? "" : `${platform}-`}`
|
|
25104
|
+
);
|
|
25105
|
+
const names = await readdir4(dir).catch(() => []);
|
|
25106
|
+
for (const name of names.sort()) {
|
|
25107
|
+
if (!name.endsWith(".png")) continue;
|
|
25108
|
+
if (!updates && !prefixes.some((prefix) => name.startsWith(prefix))) continue;
|
|
25109
|
+
const file2 = path5.join(dir, name);
|
|
25110
|
+
if (sent.has(file2)) continue;
|
|
25111
|
+
const member = { role: "baseline", key: file2, path: file2 };
|
|
25112
|
+
const entry = await baselineEntry(file2, roots, updates);
|
|
25113
|
+
if (entry.state === "refused") {
|
|
25114
|
+
emit({ ...member, state: "refused", error: entry.error }, `refused (${entry.error})`);
|
|
25115
|
+
} else if (entry.state === "missing") {
|
|
25116
|
+
emit({ ...member, state: "missing" }, "missing");
|
|
25117
|
+
} else if (updates) {
|
|
25118
|
+
emit({ ...member, state: "listed" }, "listed");
|
|
25119
|
+
} else {
|
|
25120
|
+
const sent2 = await sendBytes(member, entry.real, budget, opts);
|
|
25121
|
+
emit(sent2.member, sent2.sent);
|
|
25122
|
+
}
|
|
25123
|
+
}
|
|
25124
|
+
}
|
|
24932
25125
|
async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
24933
25126
|
if (!namesValidFlow(args)) return {};
|
|
24934
25127
|
const canonical = await canonicalFlowPath(rootPath);
|
|
@@ -24940,10 +25133,16 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
24940
25133
|
const roots = await closureRoots(rootPath, canonical, args.project_root);
|
|
24941
25134
|
const members = [];
|
|
24942
25135
|
const seen = /* @__PURE__ */ new Set();
|
|
25136
|
+
const snapshots = /* @__PURE__ */ new Set();
|
|
24943
25137
|
const budget = { inline: 0 };
|
|
24944
25138
|
const logging = process.env[FLOW_FILES_LOG_ENV] === "1";
|
|
24945
25139
|
const log2 = opts.log ?? ((line) => void process.stderr.write(`${line}
|
|
24946
25140
|
`));
|
|
25141
|
+
const emit = (member, sent) => {
|
|
25142
|
+
members.push(member);
|
|
25143
|
+
const subject = member.role === "flow" ? member.canonical : member.key;
|
|
25144
|
+
if (logging) log2(printable(`[flow-files] ${member.role} ${subject}: ${sent}`));
|
|
25145
|
+
};
|
|
24947
25146
|
const queue = [{ canonical, text: rootBytes.toString("utf8"), hop: 0 }];
|
|
24948
25147
|
for (let file2 = queue.shift(); file2 !== void 0; file2 = queue.shift()) {
|
|
24949
25148
|
let doc;
|
|
@@ -24952,19 +25151,45 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
|
|
|
24952
25151
|
} catch {
|
|
24953
25152
|
continue;
|
|
24954
25153
|
}
|
|
25154
|
+
const requests = collectFlowRequests(doc);
|
|
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
|
+
}
|
|
24955
25167
|
const anchorDir = path5.dirname(file2.canonical);
|
|
24956
|
-
for (const target of
|
|
25168
|
+
for (const target of requests.runTargets) {
|
|
24957
25169
|
const key = flowMemberKey(anchorDir, target);
|
|
24958
25170
|
if (seen.has(key)) continue;
|
|
24959
25171
|
seen.add(key);
|
|
24960
25172
|
const { member, text: text2, sent } = await readFlowMember(anchorDir, target, roots, budget, opts);
|
|
24961
|
-
|
|
24962
|
-
if (logging) log2(printable(`[flow-files] flow ${member.canonical}: ${sent}`));
|
|
25173
|
+
emit(member, sent);
|
|
24963
25174
|
if (text2 !== void 0 && file2.hop + 1 < MAX_RUN_DEPTH) {
|
|
24964
25175
|
queue.push({ canonical: member.canonical, text: text2, hop: file2.hop + 1 });
|
|
24965
25176
|
}
|
|
24966
25177
|
}
|
|
24967
25178
|
}
|
|
25179
|
+
if (snapshots.size > 0) {
|
|
25180
|
+
const flowName = path5.basename(rootPath, ".yaml");
|
|
25181
|
+
await collectBaselineMembers(
|
|
25182
|
+
canonical,
|
|
25183
|
+
flowName,
|
|
25184
|
+
[...snapshots],
|
|
25185
|
+
args,
|
|
25186
|
+
roots,
|
|
25187
|
+
budget,
|
|
25188
|
+
opts,
|
|
25189
|
+
seen,
|
|
25190
|
+
emit
|
|
25191
|
+
);
|
|
25192
|
+
}
|
|
24968
25193
|
return { canonical, spelling, members };
|
|
24969
25194
|
}
|
|
24970
25195
|
|
|
@@ -24999,7 +25224,20 @@ async function consumeToolStream(name, body, onProgress, signal) {
|
|
|
24999
25224
|
}
|
|
25000
25225
|
};
|
|
25001
25226
|
const decoder = new TextDecoder();
|
|
25002
|
-
let
|
|
25227
|
+
let pieces = [];
|
|
25228
|
+
const take = (text2) => {
|
|
25229
|
+
let start = 0;
|
|
25230
|
+
let newline;
|
|
25231
|
+
while ((newline = text2.indexOf("\n", start)) !== -1) {
|
|
25232
|
+
signal?.throwIfAborted();
|
|
25233
|
+
pieces.push(text2.slice(start, newline));
|
|
25234
|
+
const line = pieces.join("");
|
|
25235
|
+
pieces = [];
|
|
25236
|
+
start = newline + 1;
|
|
25237
|
+
handleLine(line);
|
|
25238
|
+
}
|
|
25239
|
+
if (start < text2.length) pieces.push(text2.slice(start));
|
|
25240
|
+
};
|
|
25003
25241
|
try {
|
|
25004
25242
|
for (; ; ) {
|
|
25005
25243
|
let chunk;
|
|
@@ -25011,17 +25249,11 @@ async function consumeToolStream(name, body, onProgress, signal) {
|
|
|
25011
25249
|
}
|
|
25012
25250
|
const { done, value } = chunk;
|
|
25013
25251
|
if (done) break;
|
|
25014
|
-
|
|
25015
|
-
let newline;
|
|
25016
|
-
while ((newline = buffered.indexOf("\n")) !== -1) {
|
|
25017
|
-
signal?.throwIfAborted();
|
|
25018
|
-
const line = buffered.slice(0, newline);
|
|
25019
|
-
buffered = buffered.slice(newline + 1);
|
|
25020
|
-
handleLine(line);
|
|
25021
|
-
}
|
|
25252
|
+
take(decoder.decode(value, { stream: true }));
|
|
25022
25253
|
}
|
|
25023
|
-
|
|
25024
|
-
|
|
25254
|
+
take(decoder.decode());
|
|
25255
|
+
const last = pieces.join("");
|
|
25256
|
+
if (last.trim()) handleLine(last);
|
|
25025
25257
|
} catch (err) {
|
|
25026
25258
|
void reader.cancel().catch(() => {
|
|
25027
25259
|
});
|
|
@@ -25031,8 +25263,7 @@ async function consumeToolStream(name, body, onProgress, signal) {
|
|
|
25031
25263
|
if (!final) {
|
|
25032
25264
|
throw brokenStream(name, "the stream ended without a result", progress);
|
|
25033
25265
|
}
|
|
25034
|
-
|
|
25035
|
-
return { data, note: final.note };
|
|
25266
|
+
return { data: final.data, note: final.note };
|
|
25036
25267
|
}
|
|
25037
25268
|
function errorBodyMessage(body) {
|
|
25038
25269
|
if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
|
|
@@ -25120,17 +25351,31 @@ function createToolsClient(options = {}) {
|
|
|
25120
25351
|
opts?.signal?.throwIfAborted();
|
|
25121
25352
|
const { url: url2, token, remote } = await route();
|
|
25122
25353
|
let finalArgs = args;
|
|
25123
|
-
const
|
|
25354
|
+
const tools2 = await fetchTools({ signal: opts?.signal });
|
|
25355
|
+
const meta3 = tools2.find((t2) => t2.name === name) ?? null;
|
|
25356
|
+
const baselineDirs = [];
|
|
25124
25357
|
if (meta3?.fileInputs?.length) {
|
|
25125
25358
|
if (remote) assertRequiredPresent(meta3, args);
|
|
25126
25359
|
finalArgs = await prepareFileInputs(meta3.fileInputs, args ?? {}, {
|
|
25127
25360
|
includeContent: remote,
|
|
25128
25361
|
uploadEndpoint: remote ? { url: url2, token } : void 0,
|
|
25129
25362
|
log: diagnose,
|
|
25363
|
+
baselineDirs,
|
|
25130
25364
|
collectMembers: collectFlowMembers,
|
|
25365
|
+
toolFileInputs: (tool) => tools2.find((t2) => t2.name === tool)?.fileInputs,
|
|
25131
25366
|
signal: opts?.signal
|
|
25132
25367
|
});
|
|
25133
25368
|
}
|
|
25369
|
+
const settle = async (data) => {
|
|
25370
|
+
const applied = await applyClientFileDirectives(data, { allowedDirs: baselineDirs });
|
|
25371
|
+
for (const { path: file2, error: error52 } of applied.failed) {
|
|
25372
|
+
diagnose(`The baseline ${file2} was not written on this client: ${error52}`);
|
|
25373
|
+
}
|
|
25374
|
+
if (process.env.ARGENT_FLOW_FILES_LOG === "1") {
|
|
25375
|
+
for (const file2 of applied.written) diagnose(`[flow-files] baseline ${file2}: written`);
|
|
25376
|
+
}
|
|
25377
|
+
return applied.result;
|
|
25378
|
+
};
|
|
25134
25379
|
const stream = opts?.onProgress !== void 0 || carriesMembers(finalArgs);
|
|
25135
25380
|
const sentOnce = carriesUpload(finalArgs);
|
|
25136
25381
|
const res = await doFetch(
|
|
@@ -25162,7 +25407,11 @@ function createToolsClient(options = {}) {
|
|
|
25162
25407
|
}),
|
|
25163
25408
|
opts?.signal
|
|
25164
25409
|
);
|
|
25165
|
-
return {
|
|
25410
|
+
return {
|
|
25411
|
+
data: await settle(streamed.data),
|
|
25412
|
+
note: streamed.note,
|
|
25413
|
+
outputHint: meta3?.outputHint
|
|
25414
|
+
};
|
|
25166
25415
|
}
|
|
25167
25416
|
let json2;
|
|
25168
25417
|
try {
|
|
@@ -25186,17 +25435,16 @@ function createToolsClient(options = {}) {
|
|
|
25186
25435
|
});
|
|
25187
25436
|
}
|
|
25188
25437
|
opts?.signal?.throwIfAborted();
|
|
25189
|
-
|
|
25190
|
-
return { data, note: json2.note, outputHint: meta3?.outputHint };
|
|
25438
|
+
return { data: await settle(json2.data), note: json2.note, outputHint: meta3?.outputHint };
|
|
25191
25439
|
}
|
|
25192
25440
|
return { fetchTools, fetchTool, callTool, baseUrl };
|
|
25193
25441
|
}
|
|
25194
25442
|
|
|
25195
25443
|
// ../argent-tools-client/src/artifacts.ts
|
|
25196
|
-
import { copyFile, mkdir as mkdir4, readFile as readFile5, realpath as
|
|
25444
|
+
import { copyFile, mkdir as mkdir4, readFile as readFile5, realpath as realpath4, rm as rm3, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
|
|
25197
25445
|
import { constants as fsConstants } from "node:fs";
|
|
25198
25446
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
25199
|
-
import { basename as basename6, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute5, join as join11, normalize, resolve as resolve6, sep as sep4 } from "node:path";
|
|
25447
|
+
import { basename as basename6, dirname as dirname7, extname as extname2, isAbsolute as isAbsolute5, join as join11, normalize as normalize2, resolve as resolve6, sep as sep4 } from "node:path";
|
|
25200
25448
|
import { createHash as createHash3 } from "node:crypto";
|
|
25201
25449
|
|
|
25202
25450
|
// ../configuration-core/src/flags.ts
|
|
@@ -25935,8 +26183,8 @@ function durableBaseDir() {
|
|
|
25935
26183
|
const projectRoot = findProjectRoot(process.cwd());
|
|
25936
26184
|
return projectRoot ?? dirname7(argentHomeDir());
|
|
25937
26185
|
}
|
|
25938
|
-
var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([
|
|
25939
|
-
var RECORDINGS_SAVE_DIR =
|
|
26186
|
+
var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize2(".argent/recordings")]);
|
|
26187
|
+
var RECORDINGS_SAVE_DIR = normalize2(".argent/recordings");
|
|
25940
26188
|
function configuredRecordingsDir() {
|
|
25941
26189
|
let value;
|
|
25942
26190
|
try {
|
|
@@ -25995,7 +26243,7 @@ async function writeDurableUnique(dir, filename, write) {
|
|
|
25995
26243
|
}
|
|
25996
26244
|
function durableSaveTarget(handle) {
|
|
25997
26245
|
if (typeof handle.saveDir !== "string" || !handle.saveDir || handle.archive) return null;
|
|
25998
|
-
const rel =
|
|
26246
|
+
const rel = normalize2(handle.saveDir);
|
|
25999
26247
|
if (isAbsolute5(rel) || rel === ".." || rel.startsWith(`..${sep4}`) || rel.split(sep4).includes("..")) {
|
|
26000
26248
|
return null;
|
|
26001
26249
|
}
|
|
@@ -26017,8 +26265,8 @@ function durableSaveTarget(handle) {
|
|
|
26017
26265
|
}
|
|
26018
26266
|
async function confineToRealBase(dir, base, rel) {
|
|
26019
26267
|
try {
|
|
26020
|
-
const realDir = await
|
|
26021
|
-
const realBase = await
|
|
26268
|
+
const realDir = await realpath4(dir);
|
|
26269
|
+
const realBase = await realpath4(base);
|
|
26022
26270
|
return realDir === join11(realBase, rel);
|
|
26023
26271
|
} catch {
|
|
26024
26272
|
return false;
|
|
@@ -29700,7 +29948,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
29700
29948
|
var SESSION_ID2 = randomUUID5();
|
|
29701
29949
|
function readCliVersion() {
|
|
29702
29950
|
if (true) {
|
|
29703
|
-
return "0.27.1-next.
|
|
29951
|
+
return "0.27.1-next.22";
|
|
29704
29952
|
}
|
|
29705
29953
|
return "0.0.0";
|
|
29706
29954
|
}
|
|
@@ -31053,11 +31301,16 @@ counts the remaining flows skipped.
|
|
|
31053
31301
|
|
|
31054
31302
|
The CLI sends a run to its tool-server: the local one that starts
|
|
31055
31303
|
automatically, or the one that \`argent link\` or ARGENT_TOOLS_URL names. Over
|
|
31056
|
-
a link, the CLI uploads the flow file
|
|
31057
|
-
|
|
31058
|
-
|
|
31059
|
-
|
|
31060
|
-
|
|
31304
|
+
a link, the CLI uploads the flow file, the run: fragments that it reaches and
|
|
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
|
|
31061
31314
|
Without a link, all step kinds run.
|
|
31062
31315
|
|
|
31063
31316
|
Subcommands:
|
|
@@ -31596,6 +31849,14 @@ function isFlowReport(data) {
|
|
|
31596
31849
|
const steps = data?.steps;
|
|
31597
31850
|
return Array.isArray(steps) && steps.every((step) => !!step && typeof step === "object");
|
|
31598
31851
|
}
|
|
31852
|
+
function unwrittenBaselines(report) {
|
|
31853
|
+
const writes = report.baselineWrites;
|
|
31854
|
+
if (!Array.isArray(writes)) return [];
|
|
31855
|
+
return writes.flatMap((write) => {
|
|
31856
|
+
const { path: file2, error: error52 } = write ?? {};
|
|
31857
|
+
return typeof file2 === "string" && typeof error52 === "string" ? [`${file2}: ${error52}`] : [];
|
|
31858
|
+
});
|
|
31859
|
+
}
|
|
31599
31860
|
async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
31600
31861
|
const printError = (message, err = message) => {
|
|
31601
31862
|
console.error(args.json ? JSON.stringify(errorRecord(err, message)) : message);
|
|
@@ -31666,8 +31927,17 @@ async function runFlowDirectory(dir, args, projectRoot, options) {
|
|
|
31666
31927
|
flowPath,
|
|
31667
31928
|
baseUrl
|
|
31668
31929
|
);
|
|
31669
|
-
|
|
31930
|
+
const unwritten = unwrittenBaselines(report);
|
|
31931
|
+
results.push({ path: rel, status: report.ok && !unwritten.length ? "pass" : "fail", report });
|
|
31670
31932
|
if (!report.ok) failures.push({ path: rel, ...summarizeFailure(report), rerun });
|
|
31933
|
+
else if (unwritten.length) {
|
|
31934
|
+
failures.push({
|
|
31935
|
+
path: rel,
|
|
31936
|
+
headline: "baselines not written",
|
|
31937
|
+
detail: unwritten.join("\n"),
|
|
31938
|
+
rerun
|
|
31939
|
+
});
|
|
31940
|
+
}
|
|
31671
31941
|
if (!args.json) {
|
|
31672
31942
|
for (const line of renderFailedSteps(report)) console.log(line);
|
|
31673
31943
|
console.log(` ${renderSummary(report, { withDevice: true })}`);
|
|
@@ -31928,7 +32198,12 @@ ${renderSummary(report, { withDevice: true })}`);
|
|
|
31928
32198
|
} else {
|
|
31929
32199
|
console.log(renderReport(report));
|
|
31930
32200
|
}
|
|
31931
|
-
|
|
32201
|
+
const unwritten = unwrittenBaselines(report);
|
|
32202
|
+
if (!args.json && !args.jsonStream) {
|
|
32203
|
+
for (const line of unwritten)
|
|
32204
|
+
console.log(` ${STATUS_GLYPH.error} baseline not written: ${line}`);
|
|
32205
|
+
}
|
|
32206
|
+
return exitAfterFlush(report.ok && unwritten.length === 0 ? 0 : 1);
|
|
31932
32207
|
}
|
|
31933
32208
|
|
|
31934
32209
|
// ../argent-cli/src/tools.ts
|