@swmansion/argent 0.27.1-next.20 → 0.27.1-next.21

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.
@@ -2241,8 +2241,8 @@ var require_resolve = __commonJS({
2241
2241
  }
2242
2242
  return count;
2243
2243
  }
2244
- function getFullPath(resolver, id = "", normalize2) {
2245
- if (normalize2 !== false)
2244
+ function getFullPath(resolver, id = "", normalize3) {
2245
+ if (normalize3 !== false)
2246
2246
  id = normalizeId(id);
2247
2247
  const p = resolver.parse(id);
2248
2248
  return _getFullPath(resolver, p);
@@ -3638,7 +3638,7 @@ var require_fast_uri = __commonJS({
3638
3638
  "use strict";
3639
3639
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3640
3640
  var { SCHEMES, getSchemeHandler } = require_schemes();
3641
- function normalize2(uri, options) {
3641
+ function normalize3(uri, options) {
3642
3642
  if (typeof uri === "string") {
3643
3643
  uri = /** @type {T} */
3644
3644
  normalizeString(uri, options);
@@ -3905,7 +3905,7 @@ var require_fast_uri = __commonJS({
3905
3905
  }
3906
3906
  var fastUri = {
3907
3907
  SCHEMES,
3908
- normalize: normalize2,
3908
+ normalize: normalize3,
3909
3909
  resolve: resolve8,
3910
3910
  resolveComponent,
3911
3911
  equal,
@@ -23628,6 +23628,7 @@ function isRecord(value) {
23628
23628
  }
23629
23629
  function collectFlowRequests(doc) {
23630
23630
  const runTargets = /* @__PURE__ */ new Set();
23631
+ const snapshots = /* @__PURE__ */ new Set();
23631
23632
  const seen = /* @__PURE__ */ new Set();
23632
23633
  const visit = (steps, depth) => {
23633
23634
  if (!Array.isArray(steps) || depth > MAX_BLOCK_NESTING || seen.has(steps)) return;
@@ -23639,11 +23640,17 @@ function collectFlowRequests(doc) {
23639
23640
  const target = completeRunExtension(run);
23640
23641
  if (FLOW_FILE_NAME_PATTERN.test(path2.posix.basename(target))) runTargets.add(target);
23641
23642
  }
23643
+ const snapshot = isRecord(step.snapshot) ? step.snapshot.name : step.snapshot;
23644
+ if (typeof snapshot === "string" && FLOW_NAME_PATTERN.test(snapshot)) snapshots.add(snapshot);
23642
23645
  visit(step.steps, depth + 1);
23643
23646
  }
23644
23647
  };
23645
23648
  if (isRecord(doc)) visit(doc.steps, 0);
23646
- return { runTargets: [...runTargets] };
23649
+ return { runTargets: [...runTargets], snapshots: [...snapshots] };
23650
+ }
23651
+ function baselineKeyFor(canonicalPath, flowName) {
23652
+ const stem = path2.basename(canonicalPath, ".yaml");
23653
+ return FLOW_NAME_PATTERN.test(stem) ? stem : flowName;
23647
23654
  }
23648
23655
  function flowMemberKey(anchorDir, target) {
23649
23656
  return `${anchorDir}\0${target}`;
@@ -24119,7 +24126,17 @@ async function getResolvedToolsUrl() {
24119
24126
  // ../argent-tools-client/src/file-inputs.ts
24120
24127
  import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
24121
24128
  import { createReadStream, rmSync } from "node:fs";
24122
- import { mkdir as mkdir3, readFile as readFile3, rm as rm2, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
24129
+ import {
24130
+ chmod as chmod3,
24131
+ lstat,
24132
+ mkdir as mkdir3,
24133
+ readFile as readFile3,
24134
+ realpath as realpath2,
24135
+ rename as rename2,
24136
+ rm as rm2,
24137
+ stat as stat2,
24138
+ writeFile as writeFile3
24139
+ } from "node:fs/promises";
24123
24140
  import { constants, tmpdir } from "node:os";
24124
24141
  import * as path4 from "node:path";
24125
24142
 
@@ -24428,9 +24445,61 @@ function isAllowedClientFilePath(p) {
24428
24445
  function isClientFileDirective2(value) {
24429
24446
  return !!value && typeof value === "object" && value[CLIENT_FILE_MARKER2] === true && typeof value.path === "string" && typeof value.content === "string";
24430
24447
  }
24431
- async function applyClientFileDirectives(result) {
24448
+ async function replaceFile(target, bytes, mode) {
24449
+ const temp = path4.join(path4.dirname(target), `.baseline-${randomUUID3()}.tmp`);
24450
+ try {
24451
+ await writeFile3(temp, bytes, { flag: "wx" });
24452
+ if (mode !== void 0) await chmod3(temp, mode & 511);
24453
+ await rename2(temp, target);
24454
+ } catch (err) {
24455
+ await rm2(temp, { force: true });
24456
+ throw err;
24457
+ }
24458
+ }
24459
+ async function writeBaselineFile(file, bytes, allowedDirs) {
24460
+ if (!path4.isAbsolute(file) || path4.normalize(file) !== file || file.split(/[\\/]/).includes("..") || !file.endsWith(".png") || !allowedDirs.includes(path4.dirname(file))) {
24461
+ return allowedDirs.length === 0 ? "this call writes no baselines" : `${file} is not a baseline of this call (${allowedDirs.map((d) => `${d}/<name>.png`).join(", ")})`;
24462
+ }
24463
+ if (bytes.length > MAX_CONTENT_BYTES) {
24464
+ return `${file}: the baseline is larger than the 32 MiB cap on a file this client writes`;
24465
+ }
24466
+ const dir = path4.dirname(file);
24467
+ await mkdir3(dir, { recursive: true });
24468
+ const link = await lstat(file).catch(() => null);
24469
+ const real = link === null ? null : await realpath2(file).catch(() => null);
24470
+ if (link !== null && real === null) return `${file} is a symbolic link to a missing file`;
24471
+ if (real !== null) {
24472
+ if (!real.endsWith(".png")) return `${file} links to a file that is not a PNG file`;
24473
+ if (path4.dirname(real) !== await realpath2(dir)) {
24474
+ return `${file} links outside its baseline directory`;
24475
+ }
24476
+ }
24477
+ const existing = real === null ? null : await stat2(real);
24478
+ if (existing !== null && !existing.isFile()) return `${file} is not a regular file`;
24479
+ await replaceFile(
24480
+ real ?? path4.join(await realpath2(dir), path4.basename(file)),
24481
+ bytes,
24482
+ existing?.mode
24483
+ );
24484
+ return null;
24485
+ }
24486
+ async function applyClientFileDirectives(result, opts = {}) {
24432
24487
  const written = [];
24488
+ const failed = [];
24433
24489
  async function walk(value) {
24490
+ if (isClientFileDirective2(value) && value.encoding === "base64") {
24491
+ const error2 = await writeBaselineFile(
24492
+ value.path,
24493
+ Buffer.from(value.content, "base64"),
24494
+ opts.allowedDirs ?? []
24495
+ ).catch((err) => err instanceof Error ? err.message : String(err));
24496
+ if (error2 !== null) {
24497
+ failed.push({ path: value.path, error: error2 });
24498
+ return { path: value.path, error: error2 };
24499
+ }
24500
+ written.push(value.path);
24501
+ return value.path;
24502
+ }
24434
24503
  if (isClientFileDirective2(value)) {
24435
24504
  if (!isAllowedClientFilePath(value.path)) return null;
24436
24505
  try {
@@ -24443,7 +24512,9 @@ async function applyClientFileDirectives(result) {
24443
24512
  }
24444
24513
  }
24445
24514
  if (Array.isArray(value)) {
24446
- return Promise.all(value.map(walk));
24515
+ const out = [];
24516
+ for (const item of value) out.push(await walk(item));
24517
+ return out;
24447
24518
  }
24448
24519
  if (value && typeof value === "object") {
24449
24520
  const out = {};
@@ -24455,12 +24526,12 @@ async function applyClientFileDirectives(result) {
24455
24526
  return value;
24456
24527
  }
24457
24528
  const rewritten = await walk(result);
24458
- return { result: rewritten, written };
24529
+ return { result: rewritten, written, failed };
24459
24530
  }
24460
24531
 
24461
24532
  // ../argent-tools-client/src/flow-files.ts
24462
24533
  var import_yaml = __toESM(require_dist2(), 1);
24463
- import { readFile as readFile4, realpath as realpath2, stat as stat3 } from "node:fs/promises";
24534
+ import { readdir as readdir4, readFile as readFile4, realpath as realpath3, stat as stat3 } from "node:fs/promises";
24464
24535
  import * as path5 from "node:path";
24465
24536
  var INLINE_MEMBERS_BYTES = 256 * 1024;
24466
24537
  var FLOW_FILES_LOG_ENV = "ARGENT_FLOW_FILES_LOG";
@@ -24495,7 +24566,7 @@ function savedFlowProject(file) {
24495
24566
  }
24496
24567
  async function landing(spelled) {
24497
24568
  try {
24498
- const canonical = await realpath2(spelled);
24569
+ const canonical = await realpath3(spelled);
24499
24570
  return { canonical, fence: canonical };
24500
24571
  } catch (err) {
24501
24572
  const failure = err.code === "ENOENT" ? { missing: true } : { error: err.message };
@@ -24504,7 +24575,7 @@ async function landing(spelled) {
24504
24575
  for (; ; ) {
24505
24576
  const parent = path5.dirname(at);
24506
24577
  if (parent === at) return { canonical, fence: spelled, ...failure };
24507
- const real = await realpath2(parent).catch(() => null);
24578
+ const real = await realpath3(parent).catch(() => null);
24508
24579
  if (real !== null) {
24509
24580
  return { canonical, fence: path5.join(real, path5.basename(at)), ...failure };
24510
24581
  }
@@ -24524,7 +24595,7 @@ async function closureRoots(rootPath, rootCanonical, projectRoot) {
24524
24595
  }
24525
24596
  const real = [];
24526
24597
  for (const candidate of candidates) {
24527
- const resolved = await realpath2(candidate).catch(() => null);
24598
+ const resolved = await realpath3(candidate).catch(() => null);
24528
24599
  if (resolved !== null && !real.includes(resolved)) real.push(resolved);
24529
24600
  }
24530
24601
  return real.filter((root) => !real.some((other) => other !== root && isWithin(root, other)));
@@ -24562,31 +24633,96 @@ async function readFlowMember(anchorDir, target, roots, budget, opts) {
24562
24633
  if (!/\.ya?ml$/i.test(path5.basename(canonical))) {
24563
24634
  return refuse(`${target} links to a file that is not a YAML file`);
24564
24635
  }
24565
- const read = await readFileInputWire(canonical, { includeContent: true });
24636
+ const sent = await sendBytes(member, canonical, budget, opts);
24637
+ return { ...sent, text: sent.bytes?.toString("utf8") };
24638
+ }
24639
+ function callPlatform(args) {
24640
+ if (typeof args.device === "string" && args.device.startsWith("chromium-cdp-")) return "chromium";
24641
+ if (typeof args.platform !== "string") return void 0;
24642
+ return args.platform === "ios-remote" ? "ios" : args.platform;
24643
+ }
24644
+ async function baselineEntry(file, roots, forWrite) {
24645
+ const { canonical, error: error2 } = await landing(file);
24646
+ if (!roots.some((root) => isWithin(canonical, root))) {
24647
+ return {
24648
+ state: "refused",
24649
+ error: `${file} is outside every root this client serves (${roots.join(", ")})`
24650
+ };
24651
+ }
24652
+ if (error2 !== void 0) return { state: "refused", error: error2 };
24653
+ const st = await stat3(canonical).catch((err) => err);
24654
+ if (st instanceof Error) {
24655
+ if (st.code !== "ENOENT") return { state: "refused", error: st.message };
24656
+ return forWrite ? { state: "refused", error: `${file} is a symbolic link to a missing file` } : { state: "missing" };
24657
+ }
24658
+ if (!canonical.endsWith(".png")) {
24659
+ return { state: "refused", error: `${file} links to a file that is not a PNG file` };
24660
+ }
24661
+ if (forWrite && !st.isFile()) return { state: "refused", error: `${file} is not a regular file` };
24662
+ if (st.isDirectory()) {
24663
+ return { state: "refused", error: "EISDIR: illegal operation on a directory, read" };
24664
+ }
24665
+ return st.isFile() ? { state: "ok", real: canonical } : { state: "missing" };
24666
+ }
24667
+ async function sendBytes(member, real, budget, opts) {
24668
+ const read = await readFileInputWire(real, { includeContent: true });
24566
24669
  if (read?.contentOmitted) {
24567
- return refuse(`${canonical} is larger than the 32 MiB cap on a file sent to the tool-server`);
24670
+ const error2 = `${real} is larger than the 32 MiB cap on a file sent to the tool-server`;
24671
+ return { member: { ...member, state: "refused", error: error2 }, sent: `refused (${error2})` };
24568
24672
  }
24569
24673
  if (read?.content === void 0) {
24570
- return refuse(
24571
- await readFile4(canonical).then(
24572
- () => `${canonical} could not be read on this client`,
24573
- (err) => err instanceof Error ? err.message : String(err)
24574
- )
24674
+ const error2 = await readFile4(real).then(
24675
+ () => `${real} could not be read on this client`,
24676
+ (err) => err instanceof Error ? err.message : String(err)
24575
24677
  );
24678
+ return { member: { ...member, state: "refused", error: error2 }, sent: `refused (${error2})` };
24576
24679
  }
24577
- const text = Buffer.from(read.content, "base64").toString("utf8");
24680
+ const bytes = Buffer.from(read.content, "base64");
24578
24681
  const size = read.size ?? 0;
24579
24682
  if (budget.inline + size <= INLINE_MEMBERS_BYTES || !opts.uploadEndpoint) {
24580
24683
  budget.inline += size;
24581
- return { member: { ...member, ...read }, text, sent: `inline ${size}` };
24684
+ return { member: { ...member, ...read }, sent: `inline ${size}`, bytes };
24582
24685
  }
24583
- const uploaded = await uploadFile(canonical, opts.uploadEndpoint, opts.signal);
24686
+ const uploaded = await uploadFile(real, opts.uploadEndpoint, opts.signal);
24584
24687
  return {
24585
24688
  member: { ...member, size: read.size, mtimeMs: read.mtimeMs, ...uploaded },
24586
- text,
24587
- sent: `upload ${size}`
24689
+ sent: `upload ${size}`,
24690
+ bytes
24588
24691
  };
24589
24692
  }
24693
+ async function collectBaselineMembers(canonical, flowName, snapshots, args, roots, budget, opts, emit) {
24694
+ const dir = path5.join(
24695
+ path5.dirname(canonical),
24696
+ "__baselines__",
24697
+ baselineKeyFor(canonical, flowName)
24698
+ );
24699
+ const real = (await landing(dir)).canonical;
24700
+ if (!roots.some((root) => isWithin(real, root))) return;
24701
+ const updates = args.updateBaselines === true;
24702
+ if (updates) opts.baselineDirs?.push(dir);
24703
+ const platform = callPlatform(args);
24704
+ const prefixes = snapshots.map(
24705
+ (name) => `${name}__${platform === void 0 ? "" : `${platform}-`}`
24706
+ );
24707
+ const names = await readdir4(dir).catch(() => []);
24708
+ for (const name of names.sort()) {
24709
+ if (!name.endsWith(".png")) continue;
24710
+ if (!updates && !prefixes.some((prefix) => name.startsWith(prefix))) continue;
24711
+ const file = path5.join(dir, name);
24712
+ const member = { role: "baseline", key: file, path: file };
24713
+ const entry = await baselineEntry(file, roots, updates);
24714
+ if (entry.state === "refused") {
24715
+ emit({ ...member, state: "refused", error: entry.error }, `refused (${entry.error})`);
24716
+ } else if (entry.state === "missing") {
24717
+ emit({ ...member, state: "missing" }, "missing");
24718
+ } else if (updates) {
24719
+ emit({ ...member, state: "listed" }, "listed");
24720
+ } else {
24721
+ const sent = await sendBytes(member, entry.real, budget, opts);
24722
+ emit(sent.member, sent.sent);
24723
+ }
24724
+ }
24725
+ }
24590
24726
  async function collectFlowMembers(rootPath, rootBytes, args, opts) {
24591
24727
  if (!namesValidFlow(args)) return {};
24592
24728
  const canonical = await canonicalFlowPath(rootPath);
@@ -24598,10 +24734,16 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
24598
24734
  const roots = await closureRoots(rootPath, canonical, args.project_root);
24599
24735
  const members = [];
24600
24736
  const seen = /* @__PURE__ */ new Set();
24737
+ const snapshots = /* @__PURE__ */ new Set();
24601
24738
  const budget = { inline: 0 };
24602
24739
  const logging = process.env[FLOW_FILES_LOG_ENV] === "1";
24603
24740
  const log = opts.log ?? ((line) => void process.stderr.write(`${line}
24604
24741
  `));
24742
+ const emit = (member, sent) => {
24743
+ members.push(member);
24744
+ const subject = member.role === "flow" ? member.canonical : member.key;
24745
+ if (logging) log(printable(`[flow-files] ${member.role} ${subject}: ${sent}`));
24746
+ };
24605
24747
  const queue = [{ canonical, text: rootBytes.toString("utf8"), hop: 0 }];
24606
24748
  for (let file = queue.shift(); file !== void 0; file = queue.shift()) {
24607
24749
  let doc;
@@ -24610,19 +24752,33 @@ async function collectFlowMembers(rootPath, rootBytes, args, opts) {
24610
24752
  } catch {
24611
24753
  continue;
24612
24754
  }
24755
+ const requests = collectFlowRequests(doc);
24756
+ for (const name of requests.snapshots) snapshots.add(name);
24613
24757
  const anchorDir = path5.dirname(file.canonical);
24614
- for (const target of collectFlowRequests(doc).runTargets) {
24758
+ for (const target of requests.runTargets) {
24615
24759
  const key = flowMemberKey(anchorDir, target);
24616
24760
  if (seen.has(key)) continue;
24617
24761
  seen.add(key);
24618
24762
  const { member, text, sent } = await readFlowMember(anchorDir, target, roots, budget, opts);
24619
- members.push(member);
24620
- if (logging) log(printable(`[flow-files] flow ${member.canonical}: ${sent}`));
24763
+ emit(member, sent);
24621
24764
  if (text !== void 0 && file.hop + 1 < MAX_RUN_DEPTH) {
24622
24765
  queue.push({ canonical: member.canonical, text, hop: file.hop + 1 });
24623
24766
  }
24624
24767
  }
24625
24768
  }
24769
+ if (snapshots.size > 0) {
24770
+ const flowName = path5.basename(rootPath, ".yaml");
24771
+ await collectBaselineMembers(
24772
+ canonical,
24773
+ flowName,
24774
+ [...snapshots],
24775
+ args,
24776
+ roots,
24777
+ budget,
24778
+ opts,
24779
+ emit
24780
+ );
24781
+ }
24626
24782
  return { canonical, spelling, members };
24627
24783
  }
24628
24784
 
@@ -24657,7 +24813,20 @@ async function consumeToolStream(name, body, onProgress, signal) {
24657
24813
  }
24658
24814
  };
24659
24815
  const decoder = new TextDecoder();
24660
- let buffered = "";
24816
+ let pieces = [];
24817
+ const take = (text) => {
24818
+ let start = 0;
24819
+ let newline;
24820
+ while ((newline = text.indexOf("\n", start)) !== -1) {
24821
+ signal?.throwIfAborted();
24822
+ pieces.push(text.slice(start, newline));
24823
+ const line = pieces.join("");
24824
+ pieces = [];
24825
+ start = newline + 1;
24826
+ handleLine(line);
24827
+ }
24828
+ if (start < text.length) pieces.push(text.slice(start));
24829
+ };
24661
24830
  try {
24662
24831
  for (; ; ) {
24663
24832
  let chunk;
@@ -24669,17 +24838,11 @@ async function consumeToolStream(name, body, onProgress, signal) {
24669
24838
  }
24670
24839
  const { done, value } = chunk;
24671
24840
  if (done) break;
24672
- buffered += decoder.decode(value, { stream: true });
24673
- let newline;
24674
- while ((newline = buffered.indexOf("\n")) !== -1) {
24675
- signal?.throwIfAborted();
24676
- const line = buffered.slice(0, newline);
24677
- buffered = buffered.slice(newline + 1);
24678
- handleLine(line);
24679
- }
24841
+ take(decoder.decode(value, { stream: true }));
24680
24842
  }
24681
- buffered += decoder.decode();
24682
- if (buffered.trim()) handleLine(buffered);
24843
+ take(decoder.decode());
24844
+ const last = pieces.join("");
24845
+ if (last.trim()) handleLine(last);
24683
24846
  } catch (err) {
24684
24847
  void reader.cancel().catch(() => {
24685
24848
  });
@@ -24689,8 +24852,7 @@ async function consumeToolStream(name, body, onProgress, signal) {
24689
24852
  if (!final) {
24690
24853
  throw brokenStream(name, "the stream ended without a result", progress);
24691
24854
  }
24692
- const { result: data } = await applyClientFileDirectives(final.data);
24693
- return { data, note: final.note };
24855
+ return { data: final.data, note: final.note };
24694
24856
  }
24695
24857
  function errorBodyMessage(body) {
24696
24858
  if (Array.isArray(body.issues) && typeof body.message === "string") return body.message;
@@ -24779,16 +24941,28 @@ function createToolsClient(options = {}) {
24779
24941
  const { url, token, remote } = await route();
24780
24942
  let finalArgs = args;
24781
24943
  const meta2 = await fetchTool(name, { signal: opts?.signal });
24944
+ const baselineDirs = [];
24782
24945
  if (meta2?.fileInputs?.length) {
24783
24946
  if (remote) assertRequiredPresent(meta2, args);
24784
24947
  finalArgs = await prepareFileInputs(meta2.fileInputs, args ?? {}, {
24785
24948
  includeContent: remote,
24786
24949
  uploadEndpoint: remote ? { url, token } : void 0,
24787
24950
  log: diagnose,
24951
+ baselineDirs,
24788
24952
  collectMembers: collectFlowMembers,
24789
24953
  signal: opts?.signal
24790
24954
  });
24791
24955
  }
24956
+ const settle = async (data) => {
24957
+ const applied = await applyClientFileDirectives(data, { allowedDirs: baselineDirs });
24958
+ for (const { path: file, error: error2 } of applied.failed) {
24959
+ diagnose(`The baseline ${file} was not written on this client: ${error2}`);
24960
+ }
24961
+ if (process.env.ARGENT_FLOW_FILES_LOG === "1") {
24962
+ for (const file of applied.written) diagnose(`[flow-files] baseline ${file}: written`);
24963
+ }
24964
+ return applied.result;
24965
+ };
24792
24966
  const stream = opts?.onProgress !== void 0 || carriesMembers(finalArgs);
24793
24967
  const sentOnce = carriesUpload(finalArgs);
24794
24968
  const res = await doFetch(
@@ -24820,7 +24994,11 @@ function createToolsClient(options = {}) {
24820
24994
  }),
24821
24995
  opts?.signal
24822
24996
  );
24823
- return { ...streamed, outputHint: meta2?.outputHint };
24997
+ return {
24998
+ data: await settle(streamed.data),
24999
+ note: streamed.note,
25000
+ outputHint: meta2?.outputHint
25001
+ };
24824
25002
  }
24825
25003
  let json;
24826
25004
  try {
@@ -24844,17 +25022,16 @@ function createToolsClient(options = {}) {
24844
25022
  });
24845
25023
  }
24846
25024
  opts?.signal?.throwIfAborted();
24847
- const { result: data } = await applyClientFileDirectives(json.data);
24848
- return { data, note: json.note, outputHint: meta2?.outputHint };
25025
+ return { data: await settle(json.data), note: json.note, outputHint: meta2?.outputHint };
24849
25026
  }
24850
25027
  return { fetchTools, fetchTool, callTool, baseUrl };
24851
25028
  }
24852
25029
 
24853
25030
  // ../argent-tools-client/src/artifacts.ts
24854
- import { copyFile, mkdir as mkdir4, readFile as readFile5, realpath as realpath3, rm as rm3, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
25031
+ import { copyFile, mkdir as mkdir4, readFile as readFile5, realpath as realpath4, rm as rm3, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
24855
25032
  import { constants as fsConstants } from "node:fs";
24856
25033
  import { tmpdir as tmpdir2 } from "node:os";
24857
- 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";
25034
+ 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";
24858
25035
  import { createHash as createHash3 } from "node:crypto";
24859
25036
 
24860
25037
  // ../configuration-core/src/flags.ts
@@ -25298,8 +25475,8 @@ function durableBaseDir() {
25298
25475
  const projectRoot = findProjectRoot(process.cwd());
25299
25476
  return projectRoot ?? dirname7(argentHomeDir());
25300
25477
  }
25301
- var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize(".argent/recordings")]);
25302
- var RECORDINGS_SAVE_DIR = normalize(".argent/recordings");
25478
+ var ALLOWED_SAVE_DIRS = /* @__PURE__ */ new Set([normalize2(".argent/recordings")]);
25479
+ var RECORDINGS_SAVE_DIR = normalize2(".argent/recordings");
25303
25480
  function configuredRecordingsDir() {
25304
25481
  let value;
25305
25482
  try {
@@ -25358,7 +25535,7 @@ async function writeDurableUnique(dir, filename, write) {
25358
25535
  }
25359
25536
  function durableSaveTarget(handle) {
25360
25537
  if (typeof handle.saveDir !== "string" || !handle.saveDir || handle.archive) return null;
25361
- const rel = normalize(handle.saveDir);
25538
+ const rel = normalize2(handle.saveDir);
25362
25539
  if (isAbsolute5(rel) || rel === ".." || rel.startsWith(`..${sep4}`) || rel.split(sep4).includes("..")) {
25363
25540
  return null;
25364
25541
  }
@@ -25380,8 +25557,8 @@ function durableSaveTarget(handle) {
25380
25557
  }
25381
25558
  async function confineToRealBase(dir, base, rel) {
25382
25559
  try {
25383
- const realDir = await realpath3(dir);
25384
- const realBase = await realpath3(base);
25560
+ const realDir = await realpath4(dir);
25561
+ const realBase = await realpath4(base);
25385
25562
  return realDir === join11(realBase, rel);
25386
25563
  } catch {
25387
25564
  return false;
@@ -26534,6 +26711,12 @@ async function flowRunToMcpContent(result, ctx) {
26534
26711
  blocks.push(...await stepArtifactBlocks(step.artifacts, step.status, ctx, step.depth));
26535
26712
  }
26536
26713
  }
26714
+ for (const write of result.baselineWrites ?? []) {
26715
+ const { path: path15, error: error2 } = isRecord2(write) ? write : {};
26716
+ if (typeof path15 === "string" && typeof error2 === "string") {
26717
+ blocks.push({ type: "text", text: `\u2717 baseline not written: ${path15}: ${error2}` });
26718
+ }
26719
+ }
26537
26720
  if (result.ok !== void 0) {
26538
26721
  const counted = (result.passed ?? 0) + (result.failed ?? 0) + (result.errored ?? 0) + (result.skipped ?? 0);
26539
26722
  const note = result.ok && counted === 0 ? " (no test steps)" : "";
@@ -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 and its run: fragments, and the tool-server runs those copies. Keep run: fragments under project_root or beside the flow file. Over a link, the flow and its fragments must not have script: or snapshot: steps, or tool: steps that take a file or record a flow. Without a link, the tool-server reads the files in place and all step kinds run. */
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, or tool: steps that take a file or record a flow. 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;