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.
@@ -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, or a
8
- * `{vendor, skillId, version}` reference to a provider built-in.
9
- * The two shapes are discriminated by `kind` so consumers branch
10
- * mechanically and providers can never accidentally be looked up
11
- * in `skill_bundles`.
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, or a
8
- * `{vendor, skillId, version}` reference to a provider built-in.
9
- * The two shapes are discriminated by `kind` so consumers branch
10
- * mechanically and providers can never accidentally be looked up
11
- * in `skill_bundles`.
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
- throw new Error(`${path}.kind must be 'workspace' or 'provider'`);
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
  }
@@ -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
@@ -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
- function parseSkillRef(input, path) {
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
- throw new Error(`${path}.kind must be 'workspace' or 'provider'`);
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);
@@ -1 +1 @@
1
- 0d11c4f9917f04a478449a308c1a5df2384e0281cea00ad6ecc6adcc284bc6b9 cli.mjs
1
+ 974924653a05ddcd4ac437ebc2c1e38f3ddc833c56c113f838f1c6df235b0473 cli.mjs