antpath 0.7.0 → 0.9.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.
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.js +1 -0
- package/dist/_shared/operations.d.ts +47 -0
- package/dist/_shared/operations.js +58 -0
- package/dist/_shared/proxy-protocol.d.ts +10 -2
- package/dist/_shared/proxy-protocol.js +3 -2
- package/dist/_shared/run-unit.d.ts +206 -0
- package/dist/_shared/run-unit.js +213 -0
- package/dist/_shared/runtime-types.d.ts +19 -0
- package/dist/_shared/submission.d.ts +21 -0
- package/dist/_shared/submission.js +93 -3
- package/dist/cli.mjs +120 -35
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +82 -1
- package/dist/client.js +83 -1
- package/dist/client.js.map +1 -1
- package/dist/proxy-endpoint.d.ts +26 -7
- package/dist/proxy-endpoint.js +19 -3
- package/dist/proxy-endpoint.js.map +1 -1
- package/dist/skill.d.ts +41 -0
- package/dist/skill.js +52 -0
- package/dist/skill.js.map +1 -1
- package/docs/credentials.md +34 -0
- package/docs/events.md +7 -0
- package/docs/mcp.md +28 -0
- package/docs/outputs.md +92 -12
- package/docs/quickstart.md +37 -0
- package/docs/skills.md +49 -0
- package/package.json +1 -1
|
@@ -175,6 +175,27 @@ export interface PlatformFlatSubmission {
|
|
|
175
175
|
readonly mcpServers: readonly McpServerRef[];
|
|
176
176
|
readonly environment?: PlatformTemplateEnvironment;
|
|
177
177
|
readonly metadata?: Record<string, JsonValue>;
|
|
178
|
+
/**
|
|
179
|
+
* Opt-in container paths to capture as `output_objects` at session
|
|
180
|
+
* terminal. When omitted, the worker still persists run metadata
|
|
181
|
+
* (status, events, snapshots, cleanup state) but does not capture
|
|
182
|
+
* any container file bytes. When present, the worker drives a
|
|
183
|
+
* synthetic agent turn at session terminal that instructs the agent
|
|
184
|
+
* to register every file under these paths via the Anthropic Files
|
|
185
|
+
* API, then walks the resulting list and copies bytes into private
|
|
186
|
+
* Supabase Storage.
|
|
187
|
+
*
|
|
188
|
+
* Validation:
|
|
189
|
+
* - Absolute UNIX paths only (starts with `/`).
|
|
190
|
+
* - No `..` segments, no NUL bytes, no embedded newlines.
|
|
191
|
+
* - Max 32 entries.
|
|
192
|
+
* - Max 512 bytes per entry.
|
|
193
|
+
*
|
|
194
|
+
* Entries are normalised (collapse `/+`, drop trailing `/` except
|
|
195
|
+
* for `/`) and deduplicated. The normalised list is what travels in
|
|
196
|
+
* the idempotency hash and the run snapshot.
|
|
197
|
+
*/
|
|
198
|
+
readonly outputDirs?: readonly string[];
|
|
178
199
|
}
|
|
179
200
|
export interface PlatformFlatRunSubmissionRequest {
|
|
180
201
|
readonly workspaceId: string;
|
|
@@ -272,6 +272,9 @@ function parseProxyAuthShape(input, field) {
|
|
|
272
272
|
const value = requireRecord(input, field);
|
|
273
273
|
const type = requireString(value.type, `${field}.type`);
|
|
274
274
|
switch (type) {
|
|
275
|
+
case "none":
|
|
276
|
+
assertOnlyKeys(value, field, ["type"]);
|
|
277
|
+
return { type: "none" };
|
|
275
278
|
case "bearer":
|
|
276
279
|
assertOnlyKeys(value, field, ["type"]);
|
|
277
280
|
return { type: "bearer" };
|
|
@@ -293,7 +296,7 @@ function parseProxyAuthShape(input, field) {
|
|
|
293
296
|
return { type: "query", name };
|
|
294
297
|
}
|
|
295
298
|
default:
|
|
296
|
-
throw new Error(`${field}.type must be one of: bearer, basic, header, query`);
|
|
299
|
+
throw new Error(`${field}.type must be one of: none, bearer, basic, header, query`);
|
|
297
300
|
}
|
|
298
301
|
}
|
|
299
302
|
function parseProxyMethods(input, field) {
|
|
@@ -382,6 +385,16 @@ function crossValidateProxyEndpointsAndAuth(endpoints, auth) {
|
|
|
382
385
|
const authByName = new Map(authList.map((a) => [a.name, a]));
|
|
383
386
|
for (const endpoint of endpointsList) {
|
|
384
387
|
const authEntry = authByName.get(endpoint.name);
|
|
388
|
+
if (endpoint.authShape.type === "none") {
|
|
389
|
+
// Keyless endpoints carry no auth value. Reject any matching
|
|
390
|
+
// auth entry so callers don't accidentally ship a secret bound
|
|
391
|
+
// to a "none" endpoint (which would be silently ignored at
|
|
392
|
+
// request time — confusing and a leak risk).
|
|
393
|
+
if (authEntry) {
|
|
394
|
+
throw new Error(`proxyEndpoints[${endpoint.name}] has authShape "none" but a matching secrets.proxyEndpointAuth entry was supplied; remove the auth entry`);
|
|
395
|
+
}
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
385
398
|
if (!authEntry) {
|
|
386
399
|
throw new Error(`proxyEndpoints[${endpoint.name}] has no matching secrets.proxyEndpointAuth entry`);
|
|
387
400
|
}
|
|
@@ -742,7 +755,8 @@ function parseFlatSubmission(input) {
|
|
|
742
755
|
"skills",
|
|
743
756
|
"mcpServers",
|
|
744
757
|
"environment",
|
|
745
|
-
"metadata"
|
|
758
|
+
"metadata",
|
|
759
|
+
"outputDirs"
|
|
746
760
|
]);
|
|
747
761
|
for (const key of Object.keys(value)) {
|
|
748
762
|
if (!allowed.has(key)) {
|
|
@@ -756,6 +770,7 @@ function parseFlatSubmission(input) {
|
|
|
756
770
|
const mcpServers = parseFlatMcpServers(value.mcpServers);
|
|
757
771
|
const environment = parseTemplateEnvironment(value.environment);
|
|
758
772
|
const metadata = optionalJsonRecord(value.metadata, "submission.metadata");
|
|
773
|
+
const outputDirs = parseOutputDirs(value.outputDirs);
|
|
759
774
|
return {
|
|
760
775
|
model,
|
|
761
776
|
...(system ? { system } : {}),
|
|
@@ -763,9 +778,84 @@ function parseFlatSubmission(input) {
|
|
|
763
778
|
skills,
|
|
764
779
|
mcpServers,
|
|
765
780
|
...(environment ? { environment } : {}),
|
|
766
|
-
...(metadata ? { metadata } : {})
|
|
781
|
+
...(metadata ? { metadata } : {}),
|
|
782
|
+
...(outputDirs ? { outputDirs } : {})
|
|
767
783
|
};
|
|
768
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* Maximum number of `outputDirs` entries accepted per submission.
|
|
787
|
+
*
|
|
788
|
+
* 32 is enough room for the typical "one or two capture roots" pattern
|
|
789
|
+
* plus a generous margin for legitimate multi-root use cases (per-tool
|
|
790
|
+
* output directory + scratch state + logs, repeated across a few
|
|
791
|
+
* subdirectories), without inviting abuse of the synthetic-turn path
|
|
792
|
+
* the worker drives at session terminal.
|
|
793
|
+
*/
|
|
794
|
+
const MAX_OUTPUT_DIRS = 32;
|
|
795
|
+
/**
|
|
796
|
+
* Maximum byte length of a single `outputDirs` entry (after UTF-8
|
|
797
|
+
* encoding). 512 bytes comfortably covers `/very/long/nested/path`
|
|
798
|
+
* style entries without letting a misuse smuggle large blobs through
|
|
799
|
+
* the field.
|
|
800
|
+
*/
|
|
801
|
+
const MAX_OUTPUT_DIR_BYTES = 512;
|
|
802
|
+
function parseOutputDirs(input) {
|
|
803
|
+
if (input === undefined) {
|
|
804
|
+
return undefined;
|
|
805
|
+
}
|
|
806
|
+
if (!Array.isArray(input)) {
|
|
807
|
+
throw new Error("submission.outputDirs must be an array of absolute UNIX paths");
|
|
808
|
+
}
|
|
809
|
+
if (input.length === 0) {
|
|
810
|
+
// Treat an empty array as omission so the idempotency hash matches
|
|
811
|
+
// the "no outputDirs" case.
|
|
812
|
+
return undefined;
|
|
813
|
+
}
|
|
814
|
+
if (input.length > MAX_OUTPUT_DIRS) {
|
|
815
|
+
throw new Error(`submission.outputDirs has ${input.length} entries; max is ${MAX_OUTPUT_DIRS}`);
|
|
816
|
+
}
|
|
817
|
+
const seen = new Set();
|
|
818
|
+
const normalised = [];
|
|
819
|
+
for (let i = 0; i < input.length; i++) {
|
|
820
|
+
const item = input[i];
|
|
821
|
+
if (typeof item !== "string") {
|
|
822
|
+
throw new Error(`submission.outputDirs[${i}] must be a string`);
|
|
823
|
+
}
|
|
824
|
+
if (item.length === 0) {
|
|
825
|
+
throw new Error(`submission.outputDirs[${i}] must be a non-empty absolute UNIX path`);
|
|
826
|
+
}
|
|
827
|
+
const bytes = new TextEncoder().encode(item).length;
|
|
828
|
+
if (bytes > MAX_OUTPUT_DIR_BYTES) {
|
|
829
|
+
throw new Error(`submission.outputDirs[${i}] exceeds ${MAX_OUTPUT_DIR_BYTES} bytes (got ${bytes})`);
|
|
830
|
+
}
|
|
831
|
+
if (!item.startsWith("/")) {
|
|
832
|
+
throw new Error(`submission.outputDirs[${i}] must be an absolute UNIX path (start with '/')`);
|
|
833
|
+
}
|
|
834
|
+
if (item.includes("\0")) {
|
|
835
|
+
throw new Error(`submission.outputDirs[${i}] must not contain NUL bytes`);
|
|
836
|
+
}
|
|
837
|
+
if (item.includes("\n") || item.includes("\r")) {
|
|
838
|
+
throw new Error(`submission.outputDirs[${i}] must not contain newline characters`);
|
|
839
|
+
}
|
|
840
|
+
const segments = item.split("/");
|
|
841
|
+
if (segments.includes("..")) {
|
|
842
|
+
throw new Error(`submission.outputDirs[${i}] must not contain '..' segments`);
|
|
843
|
+
}
|
|
844
|
+
const collapsed = segments
|
|
845
|
+
.filter((seg, idx) => seg.length > 0 || idx === 0)
|
|
846
|
+
.join("/");
|
|
847
|
+
const stripped = collapsed.length > 1 && collapsed.endsWith("/")
|
|
848
|
+
? collapsed.slice(0, -1)
|
|
849
|
+
: collapsed;
|
|
850
|
+
const canonical = stripped.length === 0 ? "/" : stripped;
|
|
851
|
+
if (seen.has(canonical)) {
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
seen.add(canonical);
|
|
855
|
+
normalised.push(canonical);
|
|
856
|
+
}
|
|
857
|
+
return normalised;
|
|
858
|
+
}
|
|
769
859
|
function parseFlatPrompt(input) {
|
|
770
860
|
if (typeof input === "string") {
|
|
771
861
|
if (input.length === 0) {
|
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 = {
|
|
@@ -527,7 +532,11 @@ __export(operations_exports, {
|
|
|
527
532
|
createSkillBundle: () => createSkillBundle,
|
|
528
533
|
deleteRun: () => deleteRun,
|
|
529
534
|
deleteSkill: () => deleteSkill,
|
|
535
|
+
downloadRunArchive: () => downloadRunArchive,
|
|
536
|
+
findSkillByHash: () => findSkillByHash,
|
|
537
|
+
findSkillByName: () => findSkillByName,
|
|
530
538
|
getRun: () => getRun,
|
|
539
|
+
getRunUnit: () => getRunUnit,
|
|
531
540
|
getSkill: () => getSkill,
|
|
532
541
|
listOutputs: () => listOutputs,
|
|
533
542
|
listRunEvents: () => listRunEvents,
|
|
@@ -547,6 +556,9 @@ async function getRun(http, runId) {
|
|
|
547
556
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
548
557
|
return hasRun(result) ? result.run : result;
|
|
549
558
|
}
|
|
559
|
+
async function getRunUnit(http, runId) {
|
|
560
|
+
return http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
561
|
+
}
|
|
550
562
|
async function listRunEvents(http, runId) {
|
|
551
563
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/events`);
|
|
552
564
|
return result.events;
|
|
@@ -567,6 +579,10 @@ async function deleteRun(http, runId) {
|
|
|
567
579
|
async function whoami(http) {
|
|
568
580
|
return http.request("/api/whoami");
|
|
569
581
|
}
|
|
582
|
+
async function downloadRunArchive(http, runId) {
|
|
583
|
+
const { response } = await http.download(`/api/runs/${encodeURIComponent(runId)}/download`);
|
|
584
|
+
return response;
|
|
585
|
+
}
|
|
570
586
|
async function submitRunFlat(http, request) {
|
|
571
587
|
return http.request("/api/runs", {
|
|
572
588
|
method: "POST",
|
|
@@ -623,6 +639,18 @@ async function deleteSkill(http, skillId) {
|
|
|
623
639
|
method: "DELETE"
|
|
624
640
|
});
|
|
625
641
|
}
|
|
642
|
+
async function findSkillByHash(http, args) {
|
|
643
|
+
const params = new URLSearchParams({
|
|
644
|
+
name: args.name,
|
|
645
|
+
content_hash: args.contentHash
|
|
646
|
+
});
|
|
647
|
+
const result = await http.request(`/api/skills/by-hash?${params.toString()}`);
|
|
648
|
+
return result.skill ?? null;
|
|
649
|
+
}
|
|
650
|
+
async function findSkillByName(http, name) {
|
|
651
|
+
const skills = await listSkills(http);
|
|
652
|
+
return skills.find((skill) => skill.name === name) ?? null;
|
|
653
|
+
}
|
|
626
654
|
function unwrapSkill(result) {
|
|
627
655
|
if (result && typeof result === "object" && "skill" in result) {
|
|
628
656
|
return result.skill;
|
|
@@ -669,10 +697,6 @@ function validateProxyAuth(endpoints, auth) {
|
|
|
669
697
|
}
|
|
670
698
|
}
|
|
671
699
|
|
|
672
|
-
// dist/internal.js
|
|
673
|
-
var ANTPATH_INDEX_PATH = "/antpath/index.json";
|
|
674
|
-
var ANTPATH_RUN_TOKEN_PATH = "/antpath/run-token";
|
|
675
|
-
|
|
676
700
|
// dist/host/common.js
|
|
677
701
|
var SUCCESS = { code: 0 };
|
|
678
702
|
var USAGE_ERR = { code: 2 };
|
|
@@ -830,6 +854,52 @@ function takeBooleanFlag(rest, flag) {
|
|
|
830
854
|
return { present, remaining };
|
|
831
855
|
}
|
|
832
856
|
|
|
857
|
+
// dist/outputs-sync.js
|
|
858
|
+
async function runOutputsSyncCmd(io2, dirs) {
|
|
859
|
+
if (dirs.length === 0) {
|
|
860
|
+
io2.stderr("usage: antpath outputs sync <dir> [<dir> ...]\n");
|
|
861
|
+
return USAGE_ERR;
|
|
862
|
+
}
|
|
863
|
+
try {
|
|
864
|
+
await io2.readFile(ANTPATH_INDEX_PATH);
|
|
865
|
+
} catch {
|
|
866
|
+
io2.stderr("`antpath outputs sync` is an in-container internal command and cannot run on the host.\n");
|
|
867
|
+
return USAGE_ERR;
|
|
868
|
+
}
|
|
869
|
+
if (!io2.walkDirectory) {
|
|
870
|
+
io2.stderr("antpath outputs sync: walkDirectory IO is not available\n");
|
|
871
|
+
return RUNTIME_ERR;
|
|
872
|
+
}
|
|
873
|
+
let scanned = 0;
|
|
874
|
+
let missing = 0;
|
|
875
|
+
for (const dir of dirs) {
|
|
876
|
+
if (!dir.startsWith("/")) {
|
|
877
|
+
io2.stderr(JSON.stringify({ dir, error: "non_absolute_path", message: "skipping non-absolute output dir" }) + "\n");
|
|
878
|
+
missing++;
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
let entries;
|
|
882
|
+
try {
|
|
883
|
+
entries = await io2.walkDirectory(dir);
|
|
884
|
+
} catch (err2) {
|
|
885
|
+
io2.stderr(JSON.stringify({ dir, error: "walk_failed", message: err2.message ?? "walk failed" }) + "\n");
|
|
886
|
+
missing++;
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (entries === null) {
|
|
890
|
+
io2.stderr(JSON.stringify({ dir, error: "missing_or_unreadable" }) + "\n");
|
|
891
|
+
missing++;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
for (const entry of entries) {
|
|
895
|
+
io2.stdout(JSON.stringify({ dir, path: entry.path, sizeBytes: entry.sizeBytes }) + "\n");
|
|
896
|
+
scanned++;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
io2.stdout(JSON.stringify({ summary: { dirs: dirs.length, files: scanned, missing } }) + "\n");
|
|
900
|
+
return SUCCESS;
|
|
901
|
+
}
|
|
902
|
+
|
|
833
903
|
// dist/proxy.js
|
|
834
904
|
function parseProxyFlags(rest) {
|
|
835
905
|
let endpointName = null;
|
|
@@ -2644,7 +2714,7 @@ async function runOutputsCmd(io2, argv) {
|
|
|
2644
2714
|
}
|
|
2645
2715
|
|
|
2646
2716
|
// dist/host/download.js
|
|
2647
|
-
import { resolve as resolvePath3
|
|
2717
|
+
import { resolve as resolvePath3 } from "node:path";
|
|
2648
2718
|
async function runDownloadCmd(io2, argv) {
|
|
2649
2719
|
if (await refuseInsideManagedRun(io2, "download"))
|
|
2650
2720
|
return USAGE_ERR;
|
|
@@ -2661,51 +2731,38 @@ async function runDownloadCmd(io2, argv) {
|
|
|
2661
2731
|
return USAGE_ERR;
|
|
2662
2732
|
}
|
|
2663
2733
|
const positional = outFlag.remaining.filter((arg) => !arg.startsWith("--"));
|
|
2664
|
-
if (positional.length !==
|
|
2665
|
-
io2.stderr("usage: antpath download <run-id>
|
|
2734
|
+
if (positional.length !== 1) {
|
|
2735
|
+
io2.stderr("usage: antpath download <run-id> [--out path] [common flags]\n");
|
|
2666
2736
|
return USAGE_ERR;
|
|
2667
2737
|
}
|
|
2668
2738
|
const runId = positional[0];
|
|
2669
|
-
const outputId = positional[1];
|
|
2670
2739
|
const http = makeHttpClient(io2, common.flags);
|
|
2671
|
-
let
|
|
2740
|
+
let response;
|
|
2672
2741
|
try {
|
|
2673
|
-
|
|
2742
|
+
response = await operations_exports.downloadRunArchive(http, runId);
|
|
2674
2743
|
} catch (err2) {
|
|
2675
|
-
return emitJsonError(io2, "
|
|
2744
|
+
return emitJsonError(io2, "download_failed", err2.message ?? "download failed", { runId });
|
|
2676
2745
|
}
|
|
2677
|
-
let
|
|
2746
|
+
let bytes;
|
|
2678
2747
|
try {
|
|
2679
|
-
|
|
2748
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
2680
2749
|
} catch (err2) {
|
|
2681
|
-
return emitJsonError(io2, "download_failed", `download
|
|
2682
|
-
}
|
|
2683
|
-
if (!response.ok) {
|
|
2684
|
-
return emitJsonError(io2, "download_failed", `download HTTP ${response.status}`, { runId, outputId });
|
|
2750
|
+
return emitJsonError(io2, "download_failed", `download read failed: ${err2.message}`, { runId });
|
|
2685
2751
|
}
|
|
2686
|
-
const
|
|
2687
|
-
const destination = resolveDestination(io2, outFlag.value, outputId, link.url);
|
|
2752
|
+
const destination = resolveDestination(io2, outFlag.value, runId);
|
|
2688
2753
|
try {
|
|
2689
|
-
await io2.writeFile(destination,
|
|
2754
|
+
await io2.writeFile(destination, bytes);
|
|
2690
2755
|
} catch (err2) {
|
|
2691
|
-
return emitJsonError(io2, "write_failed", `failed to write
|
|
2756
|
+
return emitJsonError(io2, "write_failed", `failed to write archive: ${err2.message}`, { destination });
|
|
2692
2757
|
}
|
|
2693
|
-
io2.stdout(JSON.stringify({ runId,
|
|
2758
|
+
io2.stdout(JSON.stringify({ runId, path: destination, bytes: bytes.byteLength }) + "\n");
|
|
2694
2759
|
return SUCCESS;
|
|
2695
2760
|
}
|
|
2696
|
-
function resolveDestination(io2, out,
|
|
2761
|
+
function resolveDestination(io2, out, runId) {
|
|
2697
2762
|
if (out) {
|
|
2698
2763
|
return resolvePath3(io2.cwd(), out);
|
|
2699
2764
|
}
|
|
2700
|
-
|
|
2701
|
-
try {
|
|
2702
|
-
const url = new URL(signedUrl);
|
|
2703
|
-
const tail = basename2(url.pathname);
|
|
2704
|
-
if (tail)
|
|
2705
|
-
fileName = tail;
|
|
2706
|
-
} catch {
|
|
2707
|
-
}
|
|
2708
|
-
return resolvePath3(io2.cwd(), fileName);
|
|
2765
|
+
return resolvePath3(io2.cwd(), `antpath-run-${runId}.zip`);
|
|
2709
2766
|
}
|
|
2710
2767
|
|
|
2711
2768
|
// dist/host/cancel.js
|
|
@@ -2815,6 +2872,9 @@ async function dispatch(io2, args) {
|
|
|
2815
2872
|
case "events":
|
|
2816
2873
|
return runEventsCmd(io2, rest);
|
|
2817
2874
|
case "outputs":
|
|
2875
|
+
if (rest[0] === "sync") {
|
|
2876
|
+
return runOutputsSyncCmd(io2, rest.slice(1));
|
|
2877
|
+
}
|
|
2818
2878
|
return runOutputsCmd(io2, rest);
|
|
2819
2879
|
case "download":
|
|
2820
2880
|
return runDownloadCmd(io2, rest);
|
|
@@ -2864,7 +2924,7 @@ Protocol version: ${manifest.protocolVersion}
|
|
|
2864
2924
|
io2.stdout(" antpath status <run-id> --api-token T\n");
|
|
2865
2925
|
io2.stdout(" antpath events <run-id> [--follow] --api-token T\n");
|
|
2866
2926
|
io2.stdout(" antpath outputs <run-id> --api-token T\n");
|
|
2867
|
-
io2.stdout(" antpath download <run-id>
|
|
2927
|
+
io2.stdout(" antpath download <run-id> [--out path] --api-token T\n");
|
|
2868
2928
|
io2.stdout(" antpath cancel <run-id> --api-token T\n");
|
|
2869
2929
|
io2.stdout(" antpath delete <run-id> --api-token T\n");
|
|
2870
2930
|
io2.stdout(" antpath whoami --api-token T\n");
|
|
@@ -2892,6 +2952,30 @@ Protocol version: ${manifest.protocolVersion}
|
|
|
2892
2952
|
}
|
|
2893
2953
|
|
|
2894
2954
|
// dist/cli.js
|
|
2955
|
+
async function walkDirectory(root) {
|
|
2956
|
+
try {
|
|
2957
|
+
const rootStat = await stat2(root);
|
|
2958
|
+
if (!rootStat.isDirectory())
|
|
2959
|
+
return null;
|
|
2960
|
+
} catch {
|
|
2961
|
+
return null;
|
|
2962
|
+
}
|
|
2963
|
+
const out = [];
|
|
2964
|
+
async function visit(dir) {
|
|
2965
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
2966
|
+
for (const entry of entries) {
|
|
2967
|
+
const full = resolvePath4(dir, entry.name);
|
|
2968
|
+
if (entry.isDirectory()) {
|
|
2969
|
+
await visit(full);
|
|
2970
|
+
} else if (entry.isFile()) {
|
|
2971
|
+
const s = await stat2(full);
|
|
2972
|
+
out.push({ path: full, sizeBytes: s.size });
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
await visit(root);
|
|
2977
|
+
return out;
|
|
2978
|
+
}
|
|
2895
2979
|
var io = {
|
|
2896
2980
|
readFile: (path) => readFile2(path, "utf8"),
|
|
2897
2981
|
writeFile: (path, data) => writeFile(path, data),
|
|
@@ -2900,6 +2984,7 @@ var io = {
|
|
|
2900
2984
|
stderr: (chunk) => process.stderr.write(chunk),
|
|
2901
2985
|
exit: (code) => process.exit(code),
|
|
2902
2986
|
argv: process.argv,
|
|
2903
|
-
cwd: () => process.cwd()
|
|
2987
|
+
cwd: () => process.cwd(),
|
|
2988
|
+
walkDirectory
|
|
2904
2989
|
};
|
|
2905
2990
|
await runCli(io);
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c4520d53e89222d7073d467e376774789532d5b9a932c7ae6821e527cb718e0e cli.mjs
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpClient, type FetchLike, type Output, type PlatformFlatRunSubmissionInput, type PlatformFlatSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type Run, type RunEvent, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_shared/index.js";
|
|
1
|
+
import { HttpClient, type FetchLike, type Output, type PlatformFlatRunSubmissionInput, type PlatformFlatSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type Run, type RunEvent, type RunUnit, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_shared/index.js";
|
|
2
2
|
import type { Blueprint } from "./blueprint.js";
|
|
3
3
|
import { McpServer } from "./mcp-server.js";
|
|
4
4
|
import { ProxyEndpoint } from "./proxy-endpoint.js";
|
|
@@ -51,6 +51,21 @@ export interface SubmitRunOptions {
|
|
|
51
51
|
readonly metadata?: PlatformFlatSubmission["metadata"];
|
|
52
52
|
readonly cleanup?: PlatformFlatRunSubmissionInput["cleanup"];
|
|
53
53
|
readonly proxyEndpoints?: readonly ProxyEndpoint[];
|
|
54
|
+
/**
|
|
55
|
+
* Container paths to capture as `output_objects` at session terminal.
|
|
56
|
+
*
|
|
57
|
+
* - Omitted: run metadata (status/events/snapshots/cleanup state) is
|
|
58
|
+
* still persisted, but no container file bytes are captured.
|
|
59
|
+
* - Present: the worker drives an agent-side sync at session terminal
|
|
60
|
+
* that registers every file under these paths via the Anthropic
|
|
61
|
+
* Files API. The bytes land in private Supabase Storage and can be
|
|
62
|
+
* retrieved via `RunRef.outputs()` / `RunRef.download()`.
|
|
63
|
+
*
|
|
64
|
+
* Paths are absolute UNIX paths (start with `/`), max 32 entries,
|
|
65
|
+
* max 512 bytes per entry, no `..` segments, no NUL bytes. See
|
|
66
|
+
* `packages/sdk/docs/outputs.md` for the full contract.
|
|
67
|
+
*/
|
|
68
|
+
readonly outputDirs?: readonly string[];
|
|
54
69
|
readonly secrets: PlatformInlineSecrets;
|
|
55
70
|
readonly idempotencyKey?: string;
|
|
56
71
|
readonly signal?: AbortSignal;
|
|
@@ -74,10 +89,34 @@ export declare class RunRef {
|
|
|
74
89
|
readonly runId: string;
|
|
75
90
|
constructor(client: AntpathClient, runId: string);
|
|
76
91
|
get(): Promise<Run>;
|
|
92
|
+
/** Convenience wrapper for `AntpathClient.getRunUnit`. */
|
|
93
|
+
getUnit(): Promise<RunUnit>;
|
|
77
94
|
events(): Promise<readonly RunEvent[]>;
|
|
78
95
|
stream(options?: StreamEventsOptions): AsyncIterable<RunEvent>;
|
|
79
96
|
wait(options?: WaitForRunOptions): Promise<Run>;
|
|
80
97
|
outputs(): Promise<readonly Output[]>;
|
|
98
|
+
/**
|
|
99
|
+
* Download the per-run archive zip. Lifecycle-aware in one method:
|
|
100
|
+
*
|
|
101
|
+
* - Pre-session (run still `queued`): rejects with a 409
|
|
102
|
+
* `run_not_started` error. Poll `RunRef.get()` until the status
|
|
103
|
+
* leaves `queued` before retrying.
|
|
104
|
+
* - Mid-session: returns whatever has been captured so far. The
|
|
105
|
+
* archive's `manifest.json` carries `partial: true` and a
|
|
106
|
+
* `missing[]` entry advising the caller to redownload after
|
|
107
|
+
* terminal for the full archive.
|
|
108
|
+
* - Terminal: returns the complete archive sourced from durable
|
|
109
|
+
* Supabase Storage. `manifest.json` carries `partial: false`.
|
|
110
|
+
*
|
|
111
|
+
* Pass `to` to write the archive zip to a file path; omit it to
|
|
112
|
+
* receive the raw streaming `Response` (use this for piping to a
|
|
113
|
+
* custom sink). `to` resolves through Node's `fs/promises` and is
|
|
114
|
+
* not available in browsers — browser callers omit `to` and pipe
|
|
115
|
+
* `Response.body` themselves.
|
|
116
|
+
*/
|
|
117
|
+
download(options?: {
|
|
118
|
+
readonly to?: string;
|
|
119
|
+
}): Promise<Response>;
|
|
81
120
|
cancel(): Promise<void>;
|
|
82
121
|
delete(): Promise<void>;
|
|
83
122
|
}
|
|
@@ -99,6 +138,31 @@ export declare class SkillsClient {
|
|
|
99
138
|
list(): Promise<readonly SkillRecord[]>;
|
|
100
139
|
get(skillId: string): Promise<SkillRecord>;
|
|
101
140
|
delete(skillId: string): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Lookup a live workspace skill by `(name, contentHash)`.
|
|
143
|
+
*
|
|
144
|
+
* Returns the matching `Skill` record or `null` when no live row
|
|
145
|
+
* carries that hash. The `contentHash` is the wire format
|
|
146
|
+
* `sha256:<hex>` returned by `hashSkillBundle` (and stored verbatim
|
|
147
|
+
* on every skill row). The hash space is unique enough that one
|
|
148
|
+
* row at most can match, so this is a single keyed lookup.
|
|
149
|
+
*
|
|
150
|
+
* Powers `Skill.uploadIfChanged(client)` internally; consumers can
|
|
151
|
+
* also call it directly when they already have a hash in hand and
|
|
152
|
+
* want to know whether the skill is already persisted.
|
|
153
|
+
*/
|
|
154
|
+
findByHash(args: {
|
|
155
|
+
readonly name: string;
|
|
156
|
+
readonly contentHash: string;
|
|
157
|
+
}): Promise<SkillRecord | null>;
|
|
158
|
+
/**
|
|
159
|
+
* Lookup a live workspace skill by `name`. Returns the matching
|
|
160
|
+
* `Skill` record or `null` when no live row carries that name.
|
|
161
|
+
* Implemented as a list-and-filter against the existing `/api/skills`
|
|
162
|
+
* endpoint — typical workspace skill counts are small enough that
|
|
163
|
+
* the cost is negligible.
|
|
164
|
+
*/
|
|
165
|
+
findByName(name: string): Promise<SkillRecord | null>;
|
|
102
166
|
/**
|
|
103
167
|
* Internal: post a pre-bundled skill zip to the BFF. Only
|
|
104
168
|
* `Skill.upload` calls this. NOT part of the public API.
|
|
@@ -157,6 +221,16 @@ export declare class AntpathClient {
|
|
|
157
221
|
*/
|
|
158
222
|
submitRun(options: SubmitRunOptions): Promise<RunRef>;
|
|
159
223
|
getRun(runId: string): Promise<Run>;
|
|
224
|
+
/**
|
|
225
|
+
* Fetch the self-contained `RunUnit`: parsed submission inputs,
|
|
226
|
+
* attempts, indexed events (inline + cursor for the tail), raw
|
|
227
|
+
* provider-event Storage manifest, outputs, capture failures,
|
|
228
|
+
* proxy-call audit, pinned workspace skills, provider skills,
|
|
229
|
+
* transient skills. Backed by the same endpoint as `getRun` but
|
|
230
|
+
* typed against the full wire shape — use this when you need
|
|
231
|
+
* fields beyond `{id, status, timestamps, usage}`.
|
|
232
|
+
*/
|
|
233
|
+
getRunUnit(runId: string): Promise<RunUnit>;
|
|
160
234
|
listEvents(runId: string): Promise<readonly RunEvent[]>;
|
|
161
235
|
/**
|
|
162
236
|
* Poll the events endpoint and yield new events as they arrive. Stops
|
|
@@ -174,5 +248,12 @@ export declare class AntpathClient {
|
|
|
174
248
|
cancelRun(runId: string): Promise<void>;
|
|
175
249
|
deleteRun(runId: string): Promise<void>;
|
|
176
250
|
whoami(): Promise<WhoAmI>;
|
|
251
|
+
/**
|
|
252
|
+
* Stream the per-run archive zip body. Returned `Response` body is
|
|
253
|
+
* `application/zip` and may be piped directly to disk; the SDK never
|
|
254
|
+
* buffers the archive. Callers requiring a "write to a path" verb
|
|
255
|
+
* should use `RunRef.download({ to })`.
|
|
256
|
+
*/
|
|
257
|
+
downloadRunArchive(runId: string): Promise<Response>;
|
|
177
258
|
}
|
|
178
259
|
export type { Blueprint, PlatformProxyEndpoint, PlatformProxyEndpointAuth };
|
package/dist/client.js
CHANGED
|
@@ -17,6 +17,10 @@ export class RunRef {
|
|
|
17
17
|
get() {
|
|
18
18
|
return this.#client.getRun(this.runId);
|
|
19
19
|
}
|
|
20
|
+
/** Convenience wrapper for `AntpathClient.getRunUnit`. */
|
|
21
|
+
getUnit() {
|
|
22
|
+
return this.#client.getRunUnit(this.runId);
|
|
23
|
+
}
|
|
20
24
|
events() {
|
|
21
25
|
return this.#client.listEvents(this.runId);
|
|
22
26
|
}
|
|
@@ -29,6 +33,34 @@ export class RunRef {
|
|
|
29
33
|
outputs() {
|
|
30
34
|
return this.#client.listOutputs(this.runId);
|
|
31
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Download the per-run archive zip. Lifecycle-aware in one method:
|
|
38
|
+
*
|
|
39
|
+
* - Pre-session (run still `queued`): rejects with a 409
|
|
40
|
+
* `run_not_started` error. Poll `RunRef.get()` until the status
|
|
41
|
+
* leaves `queued` before retrying.
|
|
42
|
+
* - Mid-session: returns whatever has been captured so far. The
|
|
43
|
+
* archive's `manifest.json` carries `partial: true` and a
|
|
44
|
+
* `missing[]` entry advising the caller to redownload after
|
|
45
|
+
* terminal for the full archive.
|
|
46
|
+
* - Terminal: returns the complete archive sourced from durable
|
|
47
|
+
* Supabase Storage. `manifest.json` carries `partial: false`.
|
|
48
|
+
*
|
|
49
|
+
* Pass `to` to write the archive zip to a file path; omit it to
|
|
50
|
+
* receive the raw streaming `Response` (use this for piping to a
|
|
51
|
+
* custom sink). `to` resolves through Node's `fs/promises` and is
|
|
52
|
+
* not available in browsers — browser callers omit `to` and pipe
|
|
53
|
+
* `Response.body` themselves.
|
|
54
|
+
*/
|
|
55
|
+
async download(options) {
|
|
56
|
+
const response = await this.#client.downloadRunArchive(this.runId);
|
|
57
|
+
if (options?.to !== undefined) {
|
|
58
|
+
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
59
|
+
const { writeFile } = await import("node:fs/promises");
|
|
60
|
+
await writeFile(options.to, buffer);
|
|
61
|
+
}
|
|
62
|
+
return response;
|
|
63
|
+
}
|
|
32
64
|
cancel() {
|
|
33
65
|
return this.#client.cancelRun(this.runId);
|
|
34
66
|
}
|
|
@@ -62,6 +94,32 @@ export class SkillsClient {
|
|
|
62
94
|
delete(skillId) {
|
|
63
95
|
return operations.deleteSkill(this.#http, skillId);
|
|
64
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Lookup a live workspace skill by `(name, contentHash)`.
|
|
99
|
+
*
|
|
100
|
+
* Returns the matching `Skill` record or `null` when no live row
|
|
101
|
+
* carries that hash. The `contentHash` is the wire format
|
|
102
|
+
* `sha256:<hex>` returned by `hashSkillBundle` (and stored verbatim
|
|
103
|
+
* on every skill row). The hash space is unique enough that one
|
|
104
|
+
* row at most can match, so this is a single keyed lookup.
|
|
105
|
+
*
|
|
106
|
+
* Powers `Skill.uploadIfChanged(client)` internally; consumers can
|
|
107
|
+
* also call it directly when they already have a hash in hand and
|
|
108
|
+
* want to know whether the skill is already persisted.
|
|
109
|
+
*/
|
|
110
|
+
findByHash(args) {
|
|
111
|
+
return operations.findSkillByHash(this.#http, args);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Lookup a live workspace skill by `name`. Returns the matching
|
|
115
|
+
* `Skill` record or `null` when no live row carries that name.
|
|
116
|
+
* Implemented as a list-and-filter against the existing `/api/skills`
|
|
117
|
+
* endpoint — typical workspace skill counts are small enough that
|
|
118
|
+
* the cost is negligible.
|
|
119
|
+
*/
|
|
120
|
+
findByName(name) {
|
|
121
|
+
return operations.findSkillByName(this.#http, name);
|
|
122
|
+
}
|
|
65
123
|
/**
|
|
66
124
|
* Internal: post a pre-bundled skill zip to the BFF. Only
|
|
67
125
|
* `Skill.upload` calls this. NOT part of the public API.
|
|
@@ -156,7 +214,10 @@ export class AntpathClient {
|
|
|
156
214
|
skills: skillRefs,
|
|
157
215
|
mcpServers: submissionMcpServers,
|
|
158
216
|
...(options.environment ? { environment: options.environment } : {}),
|
|
159
|
-
...(options.metadata ? { metadata: options.metadata } : {})
|
|
217
|
+
...(options.metadata ? { metadata: options.metadata } : {}),
|
|
218
|
+
...(options.outputDirs && options.outputDirs.length > 0
|
|
219
|
+
? { outputDirs: options.outputDirs }
|
|
220
|
+
: {})
|
|
160
221
|
};
|
|
161
222
|
const secrets = {
|
|
162
223
|
...options.secrets,
|
|
@@ -180,6 +241,18 @@ export class AntpathClient {
|
|
|
180
241
|
getRun(runId) {
|
|
181
242
|
return operations.getRun(this.#http, runId);
|
|
182
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Fetch the self-contained `RunUnit`: parsed submission inputs,
|
|
246
|
+
* attempts, indexed events (inline + cursor for the tail), raw
|
|
247
|
+
* provider-event Storage manifest, outputs, capture failures,
|
|
248
|
+
* proxy-call audit, pinned workspace skills, provider skills,
|
|
249
|
+
* transient skills. Backed by the same endpoint as `getRun` but
|
|
250
|
+
* typed against the full wire shape — use this when you need
|
|
251
|
+
* fields beyond `{id, status, timestamps, usage}`.
|
|
252
|
+
*/
|
|
253
|
+
getRunUnit(runId) {
|
|
254
|
+
return operations.getRunUnit(this.#http, runId);
|
|
255
|
+
}
|
|
183
256
|
listEvents(runId) {
|
|
184
257
|
return operations.listRunEvents(this.#http, runId);
|
|
185
258
|
}
|
|
@@ -241,6 +314,15 @@ export class AntpathClient {
|
|
|
241
314
|
whoami() {
|
|
242
315
|
return operations.whoami(this.#http);
|
|
243
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Stream the per-run archive zip body. Returned `Response` body is
|
|
319
|
+
* `application/zip` and may be piped directly to disk; the SDK never
|
|
320
|
+
* buffers the archive. Callers requiring a "write to a path" verb
|
|
321
|
+
* should use `RunRef.download({ to })`.
|
|
322
|
+
*/
|
|
323
|
+
downloadRunArchive(runId) {
|
|
324
|
+
return operations.downloadRunArchive(this.#http, runId);
|
|
325
|
+
}
|
|
244
326
|
}
|
|
245
327
|
const TERMINAL_STATUSES = new Set([
|
|
246
328
|
"succeeded",
|