antpath 0.6.2 → 0.7.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/blueprint.d.ts +66 -7
- package/dist/_shared/blueprint.js +53 -8
- package/dist/_shared/operations.d.ts +19 -0
- package/dist/_shared/operations.js +40 -0
- package/dist/_shared/submission.js +9 -1
- package/dist/blueprint.d.ts +15 -6
- package/dist/blueprint.js.map +1 -1
- package/dist/bundle.d.ts +16 -1
- package/dist/bundle.js +32 -0
- package/dist/bundle.js.map +1 -1
- package/dist/cli.mjs +53 -3
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +53 -27
- package/dist/client.js +131 -51
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/proxy-endpoint.d.ts +112 -0
- package/dist/proxy-endpoint.js +131 -0
- package/dist/proxy-endpoint.js.map +1 -0
- package/dist/skill.d.ts +162 -31
- package/dist/skill.js +225 -30
- package/dist/skill.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* Concepts (mirrored in references/architecture-decisions.md):
|
|
5
5
|
*
|
|
6
6
|
* - `SkillRef` is the wire-level reference to a skill — either an
|
|
7
|
-
* `skl_*` id pointing at a workspace-uploaded bundle,
|
|
8
|
-
* `{vendor, skillId, version}` reference to a provider built-in
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* `skl_*` id pointing at a workspace-uploaded bundle, a
|
|
8
|
+
* `{vendor, skillId, version}` reference to a provider built-in, or
|
|
9
|
+
* a `{slot, name, contentHash}` reference to per-run bytes attached
|
|
10
|
+
* as a multipart part on the submitRun call (and torn down at run
|
|
11
|
+
* terminal). The three shapes are discriminated by `kind` so
|
|
12
|
+
* consumers branch mechanically and providers can never accidentally
|
|
13
|
+
* be looked up in `skill_bundles`. Transient refs do NOT round-trip
|
|
14
|
+
* through JSON (bytes can't be serialised back); `parseBlueprint`
|
|
15
|
+
* therefore rejects them while the BFF multipart submission parser
|
|
16
|
+
* accepts them.
|
|
12
17
|
*
|
|
13
18
|
* - `McpServerRef` is the non-secret part of an MCP server declaration:
|
|
14
19
|
* `name` and `url`. Bearer / cookie / per-request headers travel in
|
|
@@ -73,7 +78,7 @@ export declare const SKILL_BUNDLE_LIMITS: {
|
|
|
73
78
|
/** Stored directory mode. */
|
|
74
79
|
readonly defaultDirMode: 493;
|
|
75
80
|
};
|
|
76
|
-
export type SkillRef = WorkspaceSkillRef | ProviderSkillRef;
|
|
81
|
+
export type SkillRef = WorkspaceSkillRef | ProviderSkillRef | TransientSkillRef;
|
|
77
82
|
export interface WorkspaceSkillRef {
|
|
78
83
|
readonly kind: "workspace";
|
|
79
84
|
readonly id: string;
|
|
@@ -84,14 +89,68 @@ export interface ProviderSkillRef {
|
|
|
84
89
|
readonly skillId: string;
|
|
85
90
|
readonly version?: string;
|
|
86
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Per-run, non-persistent skill. The bytes ride alongside the JSON
|
|
94
|
+
* submission as a `name="skill:<slot>"` multipart body part and are
|
|
95
|
+
* uploaded by the BFF directly to the provider's per-run file storage.
|
|
96
|
+
* No `skill_bundles` row, no `run_skill_snapshots` entry, no platform
|
|
97
|
+
* storage stop — the BFF tears the file down at run terminal.
|
|
98
|
+
*
|
|
99
|
+
* `slot` is an SDK-assigned local id that pairs this ref with its
|
|
100
|
+
* multipart part within a single `submitRun` call. The BFF rejects any
|
|
101
|
+
* submission where the set of `kind:"transient"` refs doesn't have a
|
|
102
|
+
* strict 1:1 match against `name="skill:<slot>"` body parts.
|
|
103
|
+
*
|
|
104
|
+
* `contentHash` is a client-side advisory hash of the canonicalised zip
|
|
105
|
+
* (`sha256:<64 hex>`). The BFF recomputes server-side and rejects on
|
|
106
|
+
* mismatch; the value is also used by the janitor for orphan
|
|
107
|
+
* reconciliation. It MUST be deterministic across retries so the
|
|
108
|
+
* idempotency hash converges.
|
|
109
|
+
*
|
|
110
|
+
* Transient refs DO NOT round-trip safely through JSON — the bytes are
|
|
111
|
+
* not in the JSON and cannot be reconstructed. `parseBlueprint` rejects
|
|
112
|
+
* them (Blueprints are JSON-persistable and bytes are gone by the time
|
|
113
|
+
* `run.json` is loaded). `parseFlatSkills` accepts them (the BFF
|
|
114
|
+
* multipart parser is the only place that carries the matching bytes).
|
|
115
|
+
*/
|
|
116
|
+
export interface TransientSkillRef {
|
|
117
|
+
readonly kind: "transient";
|
|
118
|
+
readonly slot: string;
|
|
119
|
+
readonly name: string;
|
|
120
|
+
readonly contentHash: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Slot id format: `^[a-z][a-z0-9_-]{0,63}$`. The SDK generates these
|
|
124
|
+
* positionally as `transient-0`, `transient-1`, … but any client may
|
|
125
|
+
* supply an explicit slot as long as it matches the pattern and is
|
|
126
|
+
* unique within the submission.
|
|
127
|
+
*/
|
|
128
|
+
export declare const TRANSIENT_SLOT_PATTERN: RegExp;
|
|
129
|
+
/**
|
|
130
|
+
* Content-hash format: `^sha256:[0-9a-f]{64}$` (lowercase hex). The
|
|
131
|
+
* BFF accepts only this exact prefix; uppercase, base64, or any other
|
|
132
|
+
* digest is rejected so the wire form has one stable representation.
|
|
133
|
+
*/
|
|
134
|
+
export declare const TRANSIENT_CONTENT_HASH_PATTERN: RegExp;
|
|
87
135
|
export declare function isWorkspaceSkillRef(ref: SkillRef): ref is WorkspaceSkillRef;
|
|
88
136
|
export declare function isProviderSkillRef(ref: SkillRef): ref is ProviderSkillRef;
|
|
137
|
+
export declare function isTransientSkillRef(ref: SkillRef): ref is TransientSkillRef;
|
|
138
|
+
/**
|
|
139
|
+
* Options accepted by `parseSkillRef`. The default (`allowTransient: true`)
|
|
140
|
+
* is the BFF submission parser path. The Blueprint parser passes
|
|
141
|
+
* `allowTransient: false` because Blueprints can be persisted to disk
|
|
142
|
+
* (`antpath run --config run.json`) and the bytes carrying the
|
|
143
|
+
* transient slot would already be gone.
|
|
144
|
+
*/
|
|
145
|
+
export interface ParseSkillRefOptions {
|
|
146
|
+
readonly allowTransient?: boolean;
|
|
147
|
+
}
|
|
89
148
|
/**
|
|
90
149
|
* Parse a `SkillRef` from untrusted input. Used by the BFF run parser
|
|
91
150
|
* and by the operations module when deserialising API responses. Throws
|
|
92
151
|
* with a precise path so the caller can surface a usable error.
|
|
93
152
|
*/
|
|
94
|
-
export declare function parseSkillRef(input: unknown, path: string): SkillRef;
|
|
153
|
+
export declare function parseSkillRef(input: unknown, path: string, options?: ParseSkillRefOptions): SkillRef;
|
|
95
154
|
/**
|
|
96
155
|
* Manifest entry persisted in `skill_bundles.manifest` and
|
|
97
156
|
* `run_skill_snapshots.manifest`. `path` is forward-slash, relative,
|
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* Concepts (mirrored in references/architecture-decisions.md):
|
|
5
5
|
*
|
|
6
6
|
* - `SkillRef` is the wire-level reference to a skill — either an
|
|
7
|
-
* `skl_*` id pointing at a workspace-uploaded bundle,
|
|
8
|
-
* `{vendor, skillId, version}` reference to a provider built-in
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* `skl_*` id pointing at a workspace-uploaded bundle, a
|
|
8
|
+
* `{vendor, skillId, version}` reference to a provider built-in, or
|
|
9
|
+
* a `{slot, name, contentHash}` reference to per-run bytes attached
|
|
10
|
+
* as a multipart part on the submitRun call (and torn down at run
|
|
11
|
+
* terminal). The three shapes are discriminated by `kind` so
|
|
12
|
+
* consumers branch mechanically and providers can never accidentally
|
|
13
|
+
* be looked up in `skill_bundles`. Transient refs do NOT round-trip
|
|
14
|
+
* through JSON (bytes can't be serialised back); `parseBlueprint`
|
|
15
|
+
* therefore rejects them while the BFF multipart submission parser
|
|
16
|
+
* accepts them.
|
|
12
17
|
*
|
|
13
18
|
* - `McpServerRef` is the non-secret part of an MCP server declaration:
|
|
14
19
|
* `name` and `url`. Bearer / cookie / per-request headers travel in
|
|
@@ -78,18 +83,34 @@ export const SKILL_BUNDLE_LIMITS = {
|
|
|
78
83
|
/** Stored directory mode. */
|
|
79
84
|
defaultDirMode: 0o755
|
|
80
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Slot id format: `^[a-z][a-z0-9_-]{0,63}$`. The SDK generates these
|
|
88
|
+
* positionally as `transient-0`, `transient-1`, … but any client may
|
|
89
|
+
* supply an explicit slot as long as it matches the pattern and is
|
|
90
|
+
* unique within the submission.
|
|
91
|
+
*/
|
|
92
|
+
export const TRANSIENT_SLOT_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
93
|
+
/**
|
|
94
|
+
* Content-hash format: `^sha256:[0-9a-f]{64}$` (lowercase hex). The
|
|
95
|
+
* BFF accepts only this exact prefix; uppercase, base64, or any other
|
|
96
|
+
* digest is rejected so the wire form has one stable representation.
|
|
97
|
+
*/
|
|
98
|
+
export const TRANSIENT_CONTENT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
81
99
|
export function isWorkspaceSkillRef(ref) {
|
|
82
100
|
return ref.kind === "workspace";
|
|
83
101
|
}
|
|
84
102
|
export function isProviderSkillRef(ref) {
|
|
85
103
|
return ref.kind === "provider";
|
|
86
104
|
}
|
|
105
|
+
export function isTransientSkillRef(ref) {
|
|
106
|
+
return ref.kind === "transient";
|
|
107
|
+
}
|
|
87
108
|
/**
|
|
88
109
|
* Parse a `SkillRef` from untrusted input. Used by the BFF run parser
|
|
89
110
|
* and by the operations module when deserialising API responses. Throws
|
|
90
111
|
* with a precise path so the caller can surface a usable error.
|
|
91
112
|
*/
|
|
92
|
-
export function parseSkillRef(input, path) {
|
|
113
|
+
export function parseSkillRef(input, path, options = {}) {
|
|
93
114
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
94
115
|
throw new Error(`${path} must be a SkillRef object`);
|
|
95
116
|
}
|
|
@@ -132,7 +153,31 @@ export function parseSkillRef(input, path) {
|
|
|
132
153
|
...(version !== undefined ? { version } : {})
|
|
133
154
|
};
|
|
134
155
|
}
|
|
135
|
-
|
|
156
|
+
if (kind === "transient") {
|
|
157
|
+
if (options.allowTransient === false) {
|
|
158
|
+
throw new Error(`${path} carries a transient SkillRef, which cannot round-trip through JSON — ` +
|
|
159
|
+
`transient skills must be supplied at submitRun time with their bytes attached`);
|
|
160
|
+
}
|
|
161
|
+
for (const key of Object.keys(record)) {
|
|
162
|
+
if (key !== "kind" && key !== "slot" && key !== "name" && key !== "contentHash") {
|
|
163
|
+
throw new Error(`${path} contains unexpected field for transient SkillRef: ${key}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const slot = record.slot;
|
|
167
|
+
if (typeof slot !== "string" || !TRANSIENT_SLOT_PATTERN.test(slot)) {
|
|
168
|
+
throw new Error(`${path}.slot must match ${TRANSIENT_SLOT_PATTERN.source}`);
|
|
169
|
+
}
|
|
170
|
+
const name = record.name;
|
|
171
|
+
if (typeof name !== "string" || !SKILL_NAME_PATTERN.test(name)) {
|
|
172
|
+
throw new Error(`${path}.name must match ${SKILL_NAME_PATTERN.source}`);
|
|
173
|
+
}
|
|
174
|
+
const contentHash = record.contentHash;
|
|
175
|
+
if (typeof contentHash !== "string" || !TRANSIENT_CONTENT_HASH_PATTERN.test(contentHash)) {
|
|
176
|
+
throw new Error(`${path}.contentHash must match ${TRANSIENT_CONTENT_HASH_PATTERN.source}`);
|
|
177
|
+
}
|
|
178
|
+
return { kind: "transient", slot, name, contentHash };
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`${path}.kind must be 'workspace', 'provider', or 'transient'`);
|
|
136
181
|
}
|
|
137
182
|
export class SkillBundleValidationError extends Error {
|
|
138
183
|
constructor(message) {
|
|
@@ -466,7 +511,7 @@ function parseBlueprintSkills(value) {
|
|
|
466
511
|
if (!Array.isArray(value)) {
|
|
467
512
|
throw new Error("Blueprint.skills must be an array");
|
|
468
513
|
}
|
|
469
|
-
return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]
|
|
514
|
+
return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]`, { allowTransient: false }));
|
|
470
515
|
}
|
|
471
516
|
function parseBlueprintMcpServers(value) {
|
|
472
517
|
if (value === undefined) {
|
|
@@ -23,6 +23,25 @@ export declare function cancelRun(http: HttpClient, runId: string): Promise<void
|
|
|
23
23
|
export declare function deleteRun(http: HttpClient, runId: string): Promise<void>;
|
|
24
24
|
export declare function whoami(http: HttpClient): Promise<WhoAmI>;
|
|
25
25
|
export declare function submitRunFlat(http: HttpClient, request: PlatformFlatRunSubmissionInput): Promise<Run>;
|
|
26
|
+
/**
|
|
27
|
+
* Multipart variant of `submitRunFlat` for runs that carry transient
|
|
28
|
+
* (per-run) skill bundles. The JSON submission travels as the
|
|
29
|
+
* `submission` part; each `TransientSkillRef.slot` in
|
|
30
|
+
* `request.submission.skills` MUST be mirrored by exactly one
|
|
31
|
+
* `skill:<slot>` part with the bundle bytes.
|
|
32
|
+
*
|
|
33
|
+
* The BFF re-canonicalises and re-hashes each bundle; a `contentHash`
|
|
34
|
+
* mismatch between the JSON ref and the recomputed hash of the bytes
|
|
35
|
+
* is rejected with a deterministic error.
|
|
36
|
+
*
|
|
37
|
+
* Bytes never persist on antpath storage; the dashboard BFF uploads
|
|
38
|
+
* them straight to Anthropic Files for the lifetime of the run.
|
|
39
|
+
*/
|
|
40
|
+
export declare function submitRunFlatMultipart(http: HttpClient, request: PlatformFlatRunSubmissionInput, bundles: ReadonlyArray<{
|
|
41
|
+
readonly slot: string;
|
|
42
|
+
readonly bytes: Uint8Array;
|
|
43
|
+
readonly filename: string;
|
|
44
|
+
}>): Promise<Run>;
|
|
26
45
|
/**
|
|
27
46
|
* Upload a workspace skill bundle as a zip blob. The dashboard BFF runs
|
|
28
47
|
* the two-phase flow internally (insert pending row, stream bytes into
|
|
@@ -50,6 +50,46 @@ export async function submitRunFlat(http, request) {
|
|
|
50
50
|
body: JSON.stringify(request)
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Multipart variant of `submitRunFlat` for runs that carry transient
|
|
55
|
+
* (per-run) skill bundles. The JSON submission travels as the
|
|
56
|
+
* `submission` part; each `TransientSkillRef.slot` in
|
|
57
|
+
* `request.submission.skills` MUST be mirrored by exactly one
|
|
58
|
+
* `skill:<slot>` part with the bundle bytes.
|
|
59
|
+
*
|
|
60
|
+
* The BFF re-canonicalises and re-hashes each bundle; a `contentHash`
|
|
61
|
+
* mismatch between the JSON ref and the recomputed hash of the bytes
|
|
62
|
+
* is rejected with a deterministic error.
|
|
63
|
+
*
|
|
64
|
+
* Bytes never persist on antpath storage; the dashboard BFF uploads
|
|
65
|
+
* them straight to Anthropic Files for the lifetime of the run.
|
|
66
|
+
*/
|
|
67
|
+
export async function submitRunFlatMultipart(http, request, bundles) {
|
|
68
|
+
if (!Array.isArray(bundles) || bundles.length === 0) {
|
|
69
|
+
throw new Error("submitRunFlatMultipart: bundles must be a non-empty array");
|
|
70
|
+
}
|
|
71
|
+
const form = new FormData();
|
|
72
|
+
// Submission rides as a typed JSON Blob so the BFF reads
|
|
73
|
+
// `multipart["submission"]` with the right content-type and never
|
|
74
|
+
// has to re-detect the body shape.
|
|
75
|
+
form.append("submission", new Blob([JSON.stringify(request)], { type: "application/json" }), "submission.json");
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
for (const bundle of bundles) {
|
|
78
|
+
if (typeof bundle.slot !== "string" || !bundle.slot) {
|
|
79
|
+
throw new Error("submitRunFlatMultipart: each bundle must have a non-empty slot id");
|
|
80
|
+
}
|
|
81
|
+
if (seen.has(bundle.slot)) {
|
|
82
|
+
throw new Error(`submitRunFlatMultipart: duplicate transient skill slot "${bundle.slot}"`);
|
|
83
|
+
}
|
|
84
|
+
seen.add(bundle.slot);
|
|
85
|
+
const blob = toBlob(bundle.bytes, "application/zip");
|
|
86
|
+
form.append(`skill:${bundle.slot}`, blob, bundle.filename);
|
|
87
|
+
}
|
|
88
|
+
return http.request("/api/runs", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: form
|
|
91
|
+
});
|
|
92
|
+
}
|
|
53
93
|
/**
|
|
54
94
|
* Upload a workspace skill bundle as a zip blob. The dashboard BFF runs
|
|
55
95
|
* the two-phase flow internally (insert pending row, stream bytes into
|
|
@@ -795,6 +795,7 @@ function parseFlatSkills(input) {
|
|
|
795
795
|
}
|
|
796
796
|
const seenWorkspace = new Set();
|
|
797
797
|
const seenProvider = new Set();
|
|
798
|
+
const seenTransientSlot = new Set();
|
|
798
799
|
return input.map((item, index) => {
|
|
799
800
|
const ref = parseSkillRef(item, `submission.skills[${index}]`);
|
|
800
801
|
if (ref.kind === "workspace") {
|
|
@@ -803,13 +804,20 @@ function parseFlatSkills(input) {
|
|
|
803
804
|
}
|
|
804
805
|
seenWorkspace.add(ref.id);
|
|
805
806
|
}
|
|
806
|
-
else {
|
|
807
|
+
else if (ref.kind === "provider") {
|
|
807
808
|
const key = `${ref.vendor}:${ref.skillId}:${ref.version ?? ""}`;
|
|
808
809
|
if (seenProvider.has(key)) {
|
|
809
810
|
throw new Error(`submission.skills duplicate provider skill: ${ref.vendor}:${ref.skillId}${ref.version ? `:${ref.version}` : ""}`);
|
|
810
811
|
}
|
|
811
812
|
seenProvider.add(key);
|
|
812
813
|
}
|
|
814
|
+
else {
|
|
815
|
+
// transient
|
|
816
|
+
if (seenTransientSlot.has(ref.slot)) {
|
|
817
|
+
throw new Error(`submission.skills duplicate transient slot: ${ref.slot}`);
|
|
818
|
+
}
|
|
819
|
+
seenTransientSlot.add(ref.slot);
|
|
820
|
+
}
|
|
813
821
|
return ref;
|
|
814
822
|
});
|
|
815
823
|
}
|
package/dist/blueprint.d.ts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import type { JsonValue, PlatformCleanupPolicy,
|
|
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 `
|
|
7
|
-
* `skills: [rules]
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
|
32
|
+
readonly proxyEndpoints?: readonly ProxyEndpoint[];
|
|
24
33
|
readonly metadata?: Record<string, JsonValue>;
|
|
25
34
|
}
|
|
26
35
|
/**
|
package/dist/blueprint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"
|
|
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
|
package/dist/bundle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAiB,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -54,6 +54,7 @@ var terminalRunStatuses = new Set(TERMINAL_RUN_STATUSES);
|
|
|
54
54
|
|
|
55
55
|
// ../shared/dist/blueprint.js
|
|
56
56
|
var SKILL_ID_PATTERN = /^skl_[A-Za-z0-9_-]{8,128}$/;
|
|
57
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,127}$/;
|
|
57
58
|
var SKILL_BUNDLE_LIMITS = {
|
|
58
59
|
/** Compressed (.zip) ceiling. */
|
|
59
60
|
maxCompressedBytes: 10 * 1024 * 1024,
|
|
@@ -70,7 +71,9 @@ var SKILL_BUNDLE_LIMITS = {
|
|
|
70
71
|
/** Stored directory mode. */
|
|
71
72
|
defaultDirMode: 493
|
|
72
73
|
};
|
|
73
|
-
|
|
74
|
+
var TRANSIENT_SLOT_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
75
|
+
var TRANSIENT_CONTENT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
76
|
+
function parseSkillRef(input, path, options = {}) {
|
|
74
77
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
75
78
|
throw new Error(`${path} must be a SkillRef object`);
|
|
76
79
|
}
|
|
@@ -113,7 +116,30 @@ function parseSkillRef(input, path) {
|
|
|
113
116
|
...version !== void 0 ? { version } : {}
|
|
114
117
|
};
|
|
115
118
|
}
|
|
116
|
-
|
|
119
|
+
if (kind === "transient") {
|
|
120
|
+
if (options.allowTransient === false) {
|
|
121
|
+
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`);
|
|
122
|
+
}
|
|
123
|
+
for (const key of Object.keys(record)) {
|
|
124
|
+
if (key !== "kind" && key !== "slot" && key !== "name" && key !== "contentHash") {
|
|
125
|
+
throw new Error(`${path} contains unexpected field for transient SkillRef: ${key}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const slot = record.slot;
|
|
129
|
+
if (typeof slot !== "string" || !TRANSIENT_SLOT_PATTERN.test(slot)) {
|
|
130
|
+
throw new Error(`${path}.slot must match ${TRANSIENT_SLOT_PATTERN.source}`);
|
|
131
|
+
}
|
|
132
|
+
const name = record.name;
|
|
133
|
+
if (typeof name !== "string" || !SKILL_NAME_PATTERN.test(name)) {
|
|
134
|
+
throw new Error(`${path}.name must match ${SKILL_NAME_PATTERN.source}`);
|
|
135
|
+
}
|
|
136
|
+
const contentHash = record.contentHash;
|
|
137
|
+
if (typeof contentHash !== "string" || !TRANSIENT_CONTENT_HASH_PATTERN.test(contentHash)) {
|
|
138
|
+
throw new Error(`${path}.contentHash must match ${TRANSIENT_CONTENT_HASH_PATTERN.source}`);
|
|
139
|
+
}
|
|
140
|
+
return { kind: "transient", slot, name, contentHash };
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`${path}.kind must be 'workspace', 'provider', or 'transient'`);
|
|
117
143
|
}
|
|
118
144
|
var SkillBundleValidationError = class extends Error {
|
|
119
145
|
constructor(message) {
|
|
@@ -315,7 +341,7 @@ function parseBlueprintSkills(value) {
|
|
|
315
341
|
if (!Array.isArray(value)) {
|
|
316
342
|
throw new Error("Blueprint.skills must be an array");
|
|
317
343
|
}
|
|
318
|
-
return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]
|
|
344
|
+
return value.map((item, index) => parseSkillRef(item, `Blueprint.skills[${index}]`, { allowTransient: false }));
|
|
319
345
|
}
|
|
320
346
|
function parseBlueprintMcpServers(value) {
|
|
321
347
|
if (value === void 0) {
|
|
@@ -508,6 +534,7 @@ __export(operations_exports, {
|
|
|
508
534
|
listSkills: () => listSkills,
|
|
509
535
|
submitRun: () => submitRun,
|
|
510
536
|
submitRunFlat: () => submitRunFlat,
|
|
537
|
+
submitRunFlatMultipart: () => submitRunFlatMultipart,
|
|
511
538
|
whoami: () => whoami
|
|
512
539
|
});
|
|
513
540
|
async function submitRun(http, request) {
|
|
@@ -546,6 +573,29 @@ async function submitRunFlat(http, request) {
|
|
|
546
573
|
body: JSON.stringify(request)
|
|
547
574
|
});
|
|
548
575
|
}
|
|
576
|
+
async function submitRunFlatMultipart(http, request, bundles) {
|
|
577
|
+
if (!Array.isArray(bundles) || bundles.length === 0) {
|
|
578
|
+
throw new Error("submitRunFlatMultipart: bundles must be a non-empty array");
|
|
579
|
+
}
|
|
580
|
+
const form = new FormData();
|
|
581
|
+
form.append("submission", new Blob([JSON.stringify(request)], { type: "application/json" }), "submission.json");
|
|
582
|
+
const seen = /* @__PURE__ */ new Set();
|
|
583
|
+
for (const bundle of bundles) {
|
|
584
|
+
if (typeof bundle.slot !== "string" || !bundle.slot) {
|
|
585
|
+
throw new Error("submitRunFlatMultipart: each bundle must have a non-empty slot id");
|
|
586
|
+
}
|
|
587
|
+
if (seen.has(bundle.slot)) {
|
|
588
|
+
throw new Error(`submitRunFlatMultipart: duplicate transient skill slot "${bundle.slot}"`);
|
|
589
|
+
}
|
|
590
|
+
seen.add(bundle.slot);
|
|
591
|
+
const blob = toBlob(bundle.bytes, "application/zip");
|
|
592
|
+
form.append(`skill:${bundle.slot}`, blob, bundle.filename);
|
|
593
|
+
}
|
|
594
|
+
return http.request("/api/runs", {
|
|
595
|
+
method: "POST",
|
|
596
|
+
body: form
|
|
597
|
+
});
|
|
598
|
+
}
|
|
549
599
|
async function createSkillBundle(http, args) {
|
|
550
600
|
const form = new FormData();
|
|
551
601
|
form.append("name", args.name);
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
974924653a05ddcd4ac437ebc2c1e38f3ddc833c56c113f838f1c6df235b0473 cli.mjs
|