antpath 0.6.2 → 0.8.0

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.
@@ -1,16 +1,25 @@
1
- import type { JsonValue, PlatformCleanupPolicy, PlatformProxyEndpoint, PlatformTemplateEnvironment } from "./_shared/index.js";
1
+ import type { JsonValue, PlatformCleanupPolicy, PlatformTemplateEnvironment } from "./_shared/index.js";
2
2
  import type { McpServer } from "./mcp-server.js";
3
+ import type { ProxyEndpoint } from "./proxy-endpoint.js";
3
4
  import type { Skill } from "./skill.js";
4
5
  /**
5
6
  * SDK-side blueprint. Mirrors the shared `Blueprint` shape but uses the
6
- * SDK's `Skill` and `McpServer` classes so call sites can write
7
- * `skills: [rules]` and `mcpServers: [gh]` instead of repeating the
8
- * underlying wire types. The `submitRun` method normalises these into
9
- * the wire submission + secrets bag before sending.
7
+ * SDK's `Skill`, `McpServer`, and `ProxyEndpoint` classes so call sites
8
+ * can write `skills: [rules]`, `mcpServers: [gh]`, and
9
+ * `proxyEndpoints: [stripe]` instead of repeating the underlying wire
10
+ * types. The `submitRun` method normalises these into the wire
11
+ * submission + secrets bag before sending.
10
12
  *
11
13
  * `Blueprint` is intentionally `Omit<SubmitRunOptions, "secrets" |
12
14
  * "idempotencyKey" | "signal">` so it can be embedded as a reusable,
13
15
  * credential-free fragment via `defineRun`.
16
+ *
17
+ * Note: Blueprints can legally contain unstaged transient Skills built
18
+ * via `Skill.fromFiles` / `Skill.fromPath`. The transient bytes ride
19
+ * via multipart at `submitRun` time and never persist anywhere —
20
+ * exactly the right shape for a per-run, throwaway skill. Passing a
21
+ * Blueprint through `JSON.stringify`, however, will fail (unstaged
22
+ * Skills are not JSON-serialisable; see `Skill.toJSON`).
14
23
  */
15
24
  export interface Blueprint {
16
25
  readonly model: string;
@@ -20,7 +29,7 @@ export interface Blueprint {
20
29
  readonly mcpServers?: readonly McpServer[];
21
30
  readonly environment?: PlatformTemplateEnvironment;
22
31
  readonly cleanup?: PlatformCleanupPolicy;
23
- readonly proxyEndpoints?: readonly PlatformProxyEndpoint[];
32
+ readonly proxyEndpoints?: readonly ProxyEndpoint[];
24
33
  readonly metadata?: Record<string, JsonValue>;
25
34
  }
26
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAU,QAAwC;IACzE,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,MAAe,EAAa,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAwCA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAU,QAAwC;IACzE,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,MAAe,EAAa,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1D,CAAC"}
package/dist/bundle.d.ts CHANGED
@@ -6,7 +6,13 @@
6
6
  * (`validateSkillBundleEntry`: no `..`, no absolute paths, no Windows
7
7
  * backslashes, depth/length limits). The BFF re-canonicalises and
8
8
  * recomputes the canonical hash on receipt — SDK-side hashing is NOT
9
- * part of any contract, so we don't expose one.
9
+ * part of any contract, so we don't expose one for workspace uploads.
10
+ *
11
+ * For transient (per-run) skills the SDK does compute an advisory
12
+ * `sha256` of the canonicalised zip via `hashSkillBundle()` — it travels
13
+ * in the `TransientSkillRef.contentHash` field, is used for retry
14
+ * de-dup and janitor reconciliation, and is recomputed server-side
15
+ * (mismatch → submission rejected).
10
16
  */
11
17
  export interface BundledSkill {
12
18
  readonly zip: Uint8Array;
@@ -16,3 +22,12 @@ export interface BundledSkill {
16
22
  /** Inline files map: path -> contents (UTF-8 string or raw bytes). */
17
23
  export type SkillFiles = Readonly<Record<string, string | Uint8Array>>;
18
24
  export declare function bundleSkillFiles(files: SkillFiles): BundledSkill;
25
+ /**
26
+ * Compute `sha256:<hex>` of the given canonicalised zip bytes. Used by
27
+ * `Skill.fromFiles` / `Skill.fromPath` to populate the
28
+ * `TransientSkillRef.contentHash` field. The hash is advisory — the BFF
29
+ * recomputes server-side after re-canonicalising the zip; a mismatch is
30
+ * rejected. Web-Crypto-only so the SDK works in Node, edge runtimes,
31
+ * and browsers without polyfills.
32
+ */
33
+ export declare function hashSkillBundle(zipBytes: Uint8Array): Promise<string>;
package/dist/bundle.js CHANGED
@@ -52,4 +52,36 @@ export function bundleSkillFiles(files) {
52
52
  return { zip, fileCount: entries.length, compressedSize: zip.byteLength };
53
53
  }
54
54
  const ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
55
+ /**
56
+ * Compute `sha256:<hex>` of the given canonicalised zip bytes. Used by
57
+ * `Skill.fromFiles` / `Skill.fromPath` to populate the
58
+ * `TransientSkillRef.contentHash` field. The hash is advisory — the BFF
59
+ * recomputes server-side after re-canonicalising the zip; a mismatch is
60
+ * rejected. Web-Crypto-only so the SDK works in Node, edge runtimes,
61
+ * and browsers without polyfills.
62
+ */
63
+ export async function hashSkillBundle(zipBytes) {
64
+ const subtle = globalThis.crypto?.subtle;
65
+ if (!subtle) {
66
+ throw new Error("hashSkillBundle: globalThis.crypto.subtle is not available; Node 18+ or a Web-Crypto-capable runtime is required");
67
+ }
68
+ // crypto.subtle.digest expects a BufferSource. Pass a freshly-sliced
69
+ // copy to detach from any external Uint8Array view (Web Crypto rejects
70
+ // non-zero byteOffset SharedArrayBuffer views, and view detach also
71
+ // protects against the caller mutating the input after the digest is
72
+ // computed).
73
+ const view = new Uint8Array(zipBytes.byteLength);
74
+ view.set(zipBytes);
75
+ const digest = await subtle.digest("SHA-256", view.buffer);
76
+ return "sha256:" + bufferToHex(digest);
77
+ }
78
+ function bufferToHex(buffer) {
79
+ const view = new Uint8Array(buffer);
80
+ let out = "";
81
+ for (let i = 0; i < view.length; i++) {
82
+ const byte = view[i];
83
+ out += byte.toString(16).padStart(2, "0");
84
+ }
85
+ return out;
86
+ }
55
87
  //# sourceMappingURL=bundle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
package/dist/cli.mjs CHANGED
@@ -6,7 +6,12 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // dist/cli.js
9
- import { readFile as readFile2, writeFile } from "node:fs/promises";
9
+ import { readFile as readFile2, writeFile, readdir as readdir2, stat as stat2 } from "node:fs/promises";
10
+ import { resolve as resolvePath4 } from "node:path";
11
+
12
+ // dist/internal.js
13
+ var ANTPATH_INDEX_PATH = "/antpath/index.json";
14
+ var ANTPATH_RUN_TOKEN_PATH = "/antpath/run-token";
10
15
 
11
16
  // ../shared/dist/config.js
12
17
  var DEFAULT_CAPS = {
@@ -54,6 +59,7 @@ var terminalRunStatuses = new Set(TERMINAL_RUN_STATUSES);
54
59
 
55
60
  // ../shared/dist/blueprint.js
56
61
  var SKILL_ID_PATTERN = /^skl_[A-Za-z0-9_-]{8,128}$/;
62
+ var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,127}$/;
57
63
  var SKILL_BUNDLE_LIMITS = {
58
64
  /** Compressed (.zip) ceiling. */
59
65
  maxCompressedBytes: 10 * 1024 * 1024,
@@ -70,7 +76,9 @@ var SKILL_BUNDLE_LIMITS = {
70
76
  /** Stored directory mode. */
71
77
  defaultDirMode: 493
72
78
  };
73
- function parseSkillRef(input, path) {
79
+ var TRANSIENT_SLOT_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
80
+ var TRANSIENT_CONTENT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
81
+ function parseSkillRef(input, path, options = {}) {
74
82
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
75
83
  throw new Error(`${path} must be a SkillRef object`);
76
84
  }
@@ -113,7 +121,30 @@ function parseSkillRef(input, path) {
113
121
  ...version !== void 0 ? { version } : {}
114
122
  };
115
123
  }
116
- throw new Error(`${path}.kind must be 'workspace' or 'provider'`);
124
+ if (kind === "transient") {
125
+ if (options.allowTransient === false) {
126
+ throw new Error(`${path} carries a transient SkillRef, which cannot round-trip through JSON \u2014 transient skills must be supplied at submitRun time with their bytes attached`);
127
+ }
128
+ for (const key of Object.keys(record)) {
129
+ if (key !== "kind" && key !== "slot" && key !== "name" && key !== "contentHash") {
130
+ throw new Error(`${path} contains unexpected field for transient SkillRef: ${key}`);
131
+ }
132
+ }
133
+ const slot = record.slot;
134
+ if (typeof slot !== "string" || !TRANSIENT_SLOT_PATTERN.test(slot)) {
135
+ throw new Error(`${path}.slot must match ${TRANSIENT_SLOT_PATTERN.source}`);
136
+ }
137
+ const name = record.name;
138
+ if (typeof name !== "string" || !SKILL_NAME_PATTERN.test(name)) {
139
+ throw new Error(`${path}.name must match ${SKILL_NAME_PATTERN.source}`);
140
+ }
141
+ const contentHash = record.contentHash;
142
+ if (typeof contentHash !== "string" || !TRANSIENT_CONTENT_HASH_PATTERN.test(contentHash)) {
143
+ throw new Error(`${path}.contentHash must match ${TRANSIENT_CONTENT_HASH_PATTERN.source}`);
144
+ }
145
+ return { kind: "transient", slot, name, contentHash };
146
+ }
147
+ throw new Error(`${path}.kind must be 'workspace', 'provider', or 'transient'`);
117
148
  }
118
149
  var SkillBundleValidationError = class extends Error {
119
150
  constructor(message) {
@@ -315,7 +346,7 @@ function parseBlueprintSkills(value) {
315
346
  if (!Array.isArray(value)) {
316
347
  throw new Error("Blueprint.skills must be an array");
317
348
  }
318
- return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]`));
349
+ return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]`, { allowTransient: false }));
319
350
  }
320
351
  function parseBlueprintMcpServers(value) {
321
352
  if (value === void 0) {
@@ -501,6 +532,9 @@ __export(operations_exports, {
501
532
  createSkillBundle: () => createSkillBundle,
502
533
  deleteRun: () => deleteRun,
503
534
  deleteSkill: () => deleteSkill,
535
+ downloadRunArchive: () => downloadRunArchive,
536
+ findSkillByHash: () => findSkillByHash,
537
+ findSkillByName: () => findSkillByName,
504
538
  getRun: () => getRun,
505
539
  getSkill: () => getSkill,
506
540
  listOutputs: () => listOutputs,
@@ -508,6 +542,7 @@ __export(operations_exports, {
508
542
  listSkills: () => listSkills,
509
543
  submitRun: () => submitRun,
510
544
  submitRunFlat: () => submitRunFlat,
545
+ submitRunFlatMultipart: () => submitRunFlatMultipart,
511
546
  whoami: () => whoami
512
547
  });
513
548
  async function submitRun(http, request) {
@@ -540,12 +575,39 @@ async function deleteRun(http, runId) {
540
575
  async function whoami(http) {
541
576
  return http.request("/api/whoami");
542
577
  }
578
+ async function downloadRunArchive(http, runId) {
579
+ const { response } = await http.download(`/api/runs/${encodeURIComponent(runId)}/download`);
580
+ return response;
581
+ }
543
582
  async function submitRunFlat(http, request) {
544
583
  return http.request("/api/runs", {
545
584
  method: "POST",
546
585
  body: JSON.stringify(request)
547
586
  });
548
587
  }
588
+ async function submitRunFlatMultipart(http, request, bundles) {
589
+ if (!Array.isArray(bundles) || bundles.length === 0) {
590
+ throw new Error("submitRunFlatMultipart: bundles must be a non-empty array");
591
+ }
592
+ const form = new FormData();
593
+ form.append("submission", new Blob([JSON.stringify(request)], { type: "application/json" }), "submission.json");
594
+ const seen = /* @__PURE__ */ new Set();
595
+ for (const bundle of bundles) {
596
+ if (typeof bundle.slot !== "string" || !bundle.slot) {
597
+ throw new Error("submitRunFlatMultipart: each bundle must have a non-empty slot id");
598
+ }
599
+ if (seen.has(bundle.slot)) {
600
+ throw new Error(`submitRunFlatMultipart: duplicate transient skill slot "${bundle.slot}"`);
601
+ }
602
+ seen.add(bundle.slot);
603
+ const blob = toBlob(bundle.bytes, "application/zip");
604
+ form.append(`skill:${bundle.slot}`, blob, bundle.filename);
605
+ }
606
+ return http.request("/api/runs", {
607
+ method: "POST",
608
+ body: form
609
+ });
610
+ }
549
611
  async function createSkillBundle(http, args) {
550
612
  const form = new FormData();
551
613
  form.append("name", args.name);
@@ -573,6 +635,18 @@ async function deleteSkill(http, skillId) {
573
635
  method: "DELETE"
574
636
  });
575
637
  }
638
+ async function findSkillByHash(http, args) {
639
+ const params = new URLSearchParams({
640
+ name: args.name,
641
+ content_hash: args.contentHash
642
+ });
643
+ const result = await http.request(`/api/skills/by-hash?${params.toString()}`);
644
+ return result.skill ?? null;
645
+ }
646
+ async function findSkillByName(http, name) {
647
+ const skills = await listSkills(http);
648
+ return skills.find((skill) => skill.name === name) ?? null;
649
+ }
576
650
  function unwrapSkill(result) {
577
651
  if (result && typeof result === "object" && "skill" in result) {
578
652
  return result.skill;
@@ -619,10 +693,6 @@ function validateProxyAuth(endpoints, auth) {
619
693
  }
620
694
  }
621
695
 
622
- // dist/internal.js
623
- var ANTPATH_INDEX_PATH = "/antpath/index.json";
624
- var ANTPATH_RUN_TOKEN_PATH = "/antpath/run-token";
625
-
626
696
  // dist/host/common.js
627
697
  var SUCCESS = { code: 0 };
628
698
  var USAGE_ERR = { code: 2 };
@@ -780,6 +850,52 @@ function takeBooleanFlag(rest, flag) {
780
850
  return { present, remaining };
781
851
  }
782
852
 
853
+ // dist/outputs-sync.js
854
+ async function runOutputsSyncCmd(io2, dirs) {
855
+ if (dirs.length === 0) {
856
+ io2.stderr("usage: antpath outputs sync <dir> [<dir> ...]\n");
857
+ return USAGE_ERR;
858
+ }
859
+ try {
860
+ await io2.readFile(ANTPATH_INDEX_PATH);
861
+ } catch {
862
+ io2.stderr("`antpath outputs sync` is an in-container internal command and cannot run on the host.\n");
863
+ return USAGE_ERR;
864
+ }
865
+ if (!io2.walkDirectory) {
866
+ io2.stderr("antpath outputs sync: walkDirectory IO is not available\n");
867
+ return RUNTIME_ERR;
868
+ }
869
+ let scanned = 0;
870
+ let missing = 0;
871
+ for (const dir of dirs) {
872
+ if (!dir.startsWith("/")) {
873
+ io2.stderr(JSON.stringify({ dir, error: "non_absolute_path", message: "skipping non-absolute output dir" }) + "\n");
874
+ missing++;
875
+ continue;
876
+ }
877
+ let entries;
878
+ try {
879
+ entries = await io2.walkDirectory(dir);
880
+ } catch (err2) {
881
+ io2.stderr(JSON.stringify({ dir, error: "walk_failed", message: err2.message ?? "walk failed" }) + "\n");
882
+ missing++;
883
+ continue;
884
+ }
885
+ if (entries === null) {
886
+ io2.stderr(JSON.stringify({ dir, error: "missing_or_unreadable" }) + "\n");
887
+ missing++;
888
+ continue;
889
+ }
890
+ for (const entry of entries) {
891
+ io2.stdout(JSON.stringify({ dir, path: entry.path, sizeBytes: entry.sizeBytes }) + "\n");
892
+ scanned++;
893
+ }
894
+ }
895
+ io2.stdout(JSON.stringify({ summary: { dirs: dirs.length, files: scanned, missing } }) + "\n");
896
+ return SUCCESS;
897
+ }
898
+
783
899
  // dist/proxy.js
784
900
  function parseProxyFlags(rest) {
785
901
  let endpointName = null;
@@ -2594,7 +2710,7 @@ async function runOutputsCmd(io2, argv) {
2594
2710
  }
2595
2711
 
2596
2712
  // dist/host/download.js
2597
- import { resolve as resolvePath3, basename as basename2 } from "node:path";
2713
+ import { resolve as resolvePath3 } from "node:path";
2598
2714
  async function runDownloadCmd(io2, argv) {
2599
2715
  if (await refuseInsideManagedRun(io2, "download"))
2600
2716
  return USAGE_ERR;
@@ -2611,51 +2727,38 @@ async function runDownloadCmd(io2, argv) {
2611
2727
  return USAGE_ERR;
2612
2728
  }
2613
2729
  const positional = outFlag.remaining.filter((arg) => !arg.startsWith("--"));
2614
- if (positional.length !== 2) {
2615
- io2.stderr("usage: antpath download <run-id> <output-id> [--out path] [common flags]\n");
2730
+ if (positional.length !== 1) {
2731
+ io2.stderr("usage: antpath download <run-id> [--out path] [common flags]\n");
2616
2732
  return USAGE_ERR;
2617
2733
  }
2618
2734
  const runId = positional[0];
2619
- const outputId = positional[1];
2620
2735
  const http = makeHttpClient(io2, common.flags);
2621
- let link;
2736
+ let response;
2622
2737
  try {
2623
- link = await operations_exports.createOutputLink(http, runId, outputId);
2738
+ response = await operations_exports.downloadRunArchive(http, runId);
2624
2739
  } catch (err2) {
2625
- return emitJsonError(io2, "link_failed", err2.message ?? "create link failed", { runId, outputId });
2740
+ return emitJsonError(io2, "download_failed", err2.message ?? "download failed", { runId });
2626
2741
  }
2627
- let response;
2742
+ let bytes;
2628
2743
  try {
2629
- response = await io2.fetchImpl(link.url, { method: "GET", redirect: "follow" });
2744
+ bytes = new Uint8Array(await response.arrayBuffer());
2630
2745
  } catch (err2) {
2631
- return emitJsonError(io2, "download_failed", `download fetch failed: ${err2.message}`, { runId, outputId });
2746
+ return emitJsonError(io2, "download_failed", `download read failed: ${err2.message}`, { runId });
2632
2747
  }
2633
- if (!response.ok) {
2634
- return emitJsonError(io2, "download_failed", `download HTTP ${response.status}`, { runId, outputId });
2635
- }
2636
- const buffer = new Uint8Array(await response.arrayBuffer());
2637
- const destination = resolveDestination(io2, outFlag.value, outputId, link.url);
2748
+ const destination = resolveDestination(io2, outFlag.value, runId);
2638
2749
  try {
2639
- await io2.writeFile(destination, buffer);
2750
+ await io2.writeFile(destination, bytes);
2640
2751
  } catch (err2) {
2641
- return emitJsonError(io2, "write_failed", `failed to write output: ${err2.message}`, { destination });
2752
+ return emitJsonError(io2, "write_failed", `failed to write archive: ${err2.message}`, { destination });
2642
2753
  }
2643
- io2.stdout(JSON.stringify({ runId, outputId, path: destination, bytes: buffer.byteLength }) + "\n");
2754
+ io2.stdout(JSON.stringify({ runId, path: destination, bytes: bytes.byteLength }) + "\n");
2644
2755
  return SUCCESS;
2645
2756
  }
2646
- function resolveDestination(io2, out, outputId, signedUrl) {
2757
+ function resolveDestination(io2, out, runId) {
2647
2758
  if (out) {
2648
2759
  return resolvePath3(io2.cwd(), out);
2649
2760
  }
2650
- let fileName = `${outputId}`;
2651
- try {
2652
- const url = new URL(signedUrl);
2653
- const tail = basename2(url.pathname);
2654
- if (tail)
2655
- fileName = tail;
2656
- } catch {
2657
- }
2658
- return resolvePath3(io2.cwd(), fileName);
2761
+ return resolvePath3(io2.cwd(), `antpath-run-${runId}.zip`);
2659
2762
  }
2660
2763
 
2661
2764
  // dist/host/cancel.js
@@ -2765,6 +2868,9 @@ async function dispatch(io2, args) {
2765
2868
  case "events":
2766
2869
  return runEventsCmd(io2, rest);
2767
2870
  case "outputs":
2871
+ if (rest[0] === "sync") {
2872
+ return runOutputsSyncCmd(io2, rest.slice(1));
2873
+ }
2768
2874
  return runOutputsCmd(io2, rest);
2769
2875
  case "download":
2770
2876
  return runDownloadCmd(io2, rest);
@@ -2814,7 +2920,7 @@ Protocol version: ${manifest.protocolVersion}
2814
2920
  io2.stdout(" antpath status <run-id> --api-token T\n");
2815
2921
  io2.stdout(" antpath events <run-id> [--follow] --api-token T\n");
2816
2922
  io2.stdout(" antpath outputs <run-id> --api-token T\n");
2817
- io2.stdout(" antpath download <run-id> <output-id> [--out path] --api-token T\n");
2923
+ io2.stdout(" antpath download <run-id> [--out path] --api-token T\n");
2818
2924
  io2.stdout(" antpath cancel <run-id> --api-token T\n");
2819
2925
  io2.stdout(" antpath delete <run-id> --api-token T\n");
2820
2926
  io2.stdout(" antpath whoami --api-token T\n");
@@ -2842,6 +2948,30 @@ Protocol version: ${manifest.protocolVersion}
2842
2948
  }
2843
2949
 
2844
2950
  // dist/cli.js
2951
+ async function walkDirectory(root) {
2952
+ try {
2953
+ const rootStat = await stat2(root);
2954
+ if (!rootStat.isDirectory())
2955
+ return null;
2956
+ } catch {
2957
+ return null;
2958
+ }
2959
+ const out = [];
2960
+ async function visit(dir) {
2961
+ const entries = await readdir2(dir, { withFileTypes: true });
2962
+ for (const entry of entries) {
2963
+ const full = resolvePath4(dir, entry.name);
2964
+ if (entry.isDirectory()) {
2965
+ await visit(full);
2966
+ } else if (entry.isFile()) {
2967
+ const s = await stat2(full);
2968
+ out.push({ path: full, sizeBytes: s.size });
2969
+ }
2970
+ }
2971
+ }
2972
+ await visit(root);
2973
+ return out;
2974
+ }
2845
2975
  var io = {
2846
2976
  readFile: (path) => readFile2(path, "utf8"),
2847
2977
  writeFile: (path, data) => writeFile(path, data),
@@ -2850,6 +2980,7 @@ var io = {
2850
2980
  stderr: (chunk) => process.stderr.write(chunk),
2851
2981
  exit: (code) => process.exit(code),
2852
2982
  argv: process.argv,
2853
- cwd: () => process.cwd()
2983
+ cwd: () => process.cwd(),
2984
+ walkDirectory
2854
2985
  };
2855
2986
  await runCli(io);
@@ -1 +1 @@
1
- 0d11c4f9917f04a478449a308c1a5df2384e0281cea00ad6ecc6adcc284bc6b9 cli.mjs
1
+ e7ee04a6c02d7ddfef1546abddda3b7031c92b8f21471a1e1cec10478a30fa50 cli.mjs