@aexhq/sdk 0.13.6
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/LICENSE +201 -0
- package/README.md +160 -0
- package/dist/_contracts/connection-ticket.d.ts +21 -0
- package/dist/_contracts/connection-ticket.js +49 -0
- package/dist/_contracts/event-envelope.d.ts +276 -0
- package/dist/_contracts/event-envelope.js +324 -0
- package/dist/_contracts/event-stream-client.d.ts +47 -0
- package/dist/_contracts/event-stream-client.js +141 -0
- package/dist/_contracts/http.d.ts +35 -0
- package/dist/_contracts/http.js +114 -0
- package/dist/_contracts/index.d.ts +28 -0
- package/dist/_contracts/index.js +29 -0
- package/dist/_contracts/managed-key.d.ts +74 -0
- package/dist/_contracts/managed-key.js +110 -0
- package/dist/_contracts/operations.d.ts +237 -0
- package/dist/_contracts/operations.js +632 -0
- package/dist/_contracts/provider-support.d.ts +220 -0
- package/dist/_contracts/provider-support.js +90 -0
- package/dist/_contracts/proxy-protocol.d.ts +257 -0
- package/dist/_contracts/proxy-protocol.js +234 -0
- package/dist/_contracts/proxy-validation.d.ts +19 -0
- package/dist/_contracts/proxy-validation.js +51 -0
- package/dist/_contracts/run-artifacts.d.ts +47 -0
- package/dist/_contracts/run-artifacts.js +101 -0
- package/dist/_contracts/run-config.d.ts +304 -0
- package/dist/_contracts/run-config.js +659 -0
- package/dist/_contracts/run-cost.d.ts +125 -0
- package/dist/_contracts/run-cost.js +616 -0
- package/dist/_contracts/run-custody.d.ts +226 -0
- package/dist/_contracts/run-custody.js +465 -0
- package/dist/_contracts/run-record.d.ts +127 -0
- package/dist/_contracts/run-record.js +177 -0
- package/dist/_contracts/run-retention.d.ts +213 -0
- package/dist/_contracts/run-retention.js +484 -0
- package/dist/_contracts/run-unit.d.ts +194 -0
- package/dist/_contracts/run-unit.js +215 -0
- package/dist/_contracts/runner-event.d.ts +114 -0
- package/dist/_contracts/runner-event.js +187 -0
- package/dist/_contracts/runtime-manifest.d.ts +106 -0
- package/dist/_contracts/runtime-manifest.js +98 -0
- package/dist/_contracts/runtime-security-profile.d.ts +27 -0
- package/dist/_contracts/runtime-security-profile.js +82 -0
- package/dist/_contracts/runtime-sizes.d.ts +144 -0
- package/dist/_contracts/runtime-sizes.js +136 -0
- package/dist/_contracts/runtime-types.d.ts +212 -0
- package/dist/_contracts/runtime-types.js +2 -0
- package/dist/_contracts/sdk-errors.d.ts +34 -0
- package/dist/_contracts/sdk-errors.js +52 -0
- package/dist/_contracts/sdk-secrets.d.ts +31 -0
- package/dist/_contracts/sdk-secrets.js +220 -0
- package/dist/_contracts/side-effect-audit.d.ts +129 -0
- package/dist/_contracts/side-effect-audit.js +494 -0
- package/dist/_contracts/sse.d.ts +74 -0
- package/dist/_contracts/sse.js +0 -0
- package/dist/_contracts/stable.d.ts +26 -0
- package/dist/_contracts/stable.js +44 -0
- package/dist/_contracts/status.d.ts +19 -0
- package/dist/_contracts/status.js +61 -0
- package/dist/_contracts/submission.d.ts +383 -0
- package/dist/_contracts/submission.js +1380 -0
- package/dist/agents-md.d.ts +46 -0
- package/dist/agents-md.js +83 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/asset-upload.d.ts +66 -0
- package/dist/asset-upload.js +168 -0
- package/dist/asset-upload.js.map +1 -0
- package/dist/bundle.d.ts +33 -0
- package/dist/bundle.js +89 -0
- package/dist/bundle.js.map +1 -0
- package/dist/cli.mjs +4140 -0
- package/dist/cli.mjs.sha256 +1 -0
- package/dist/client.d.ts +460 -0
- package/dist/client.js +857 -0
- package/dist/client.js.map +1 -0
- package/dist/fetch-archive.d.ts +16 -0
- package/dist/fetch-archive.js +170 -0
- package/dist/fetch-archive.js.map +1 -0
- package/dist/file.d.ts +57 -0
- package/dist/file.js +153 -0
- package/dist/file.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +84 -0
- package/dist/mcp-server.js +114 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/node-fs.d.ts +12 -0
- package/dist/node-fs.js +44 -0
- package/dist/node-fs.js.map +1 -0
- package/dist/proxy-endpoint.d.ts +131 -0
- package/dist/proxy-endpoint.js +147 -0
- package/dist/proxy-endpoint.js.map +1 -0
- package/dist/skill.d.ts +117 -0
- package/dist/skill.js +169 -0
- package/dist/skill.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/docs/cleanup.md +38 -0
- package/docs/credentials.md +153 -0
- package/docs/events.md +76 -0
- package/docs/mcp.md +47 -0
- package/docs/outputs.md +157 -0
- package/docs/product-boundaries.md +57 -0
- package/docs/provider-runtime-capabilities.md +103 -0
- package/docs/quickstart.md +110 -0
- package/docs/release.md +99 -0
- package/docs/run-config.md +53 -0
- package/docs/run-record.md +39 -0
- package/docs/skills.md +139 -0
- package/docs/testing.md +29 -0
- package/package.json +47 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AgentsMdRef } from "./_contracts/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* AgentsMd — a single markdown file delivered to the agent as the
|
|
4
|
+
* first user turn of the session (matches Claude Code's CLAUDE.md
|
|
5
|
+
* behaviour).
|
|
6
|
+
*
|
|
7
|
+
* const rules = await AgentsMd.fromContent("# Be helpful", { name: "rules" });
|
|
8
|
+
* await client.submitRun({ agentsMd: [rules], ... });
|
|
9
|
+
*
|
|
10
|
+
* `client.submitRun` materializes the bytes to the hosted asset store before
|
|
11
|
+
* the run lands. Asset deduplication handles repeated uploads automatically.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AgentsMd {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(ref: AgentsMdRef | DraftAgentsMdRef, zipBytes?: Uint8Array);
|
|
16
|
+
get ref(): AgentsMdRef | DraftAgentsMdRef;
|
|
17
|
+
get isDraft(): boolean;
|
|
18
|
+
get isConsumed(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Build a draft AgentsMd from a markdown string. The SDK zips the
|
|
21
|
+
* content under the canonical filename `AGENTS.md` so the hash is a
|
|
22
|
+
* pure function of the markdown text.
|
|
23
|
+
*/
|
|
24
|
+
static fromContent(content: string, args: {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
}): Promise<AgentsMd>;
|
|
27
|
+
/** Node-only convenience: read a markdown file from disk. */
|
|
28
|
+
static fromPath(path: string, args: {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
}): Promise<AgentsMd>;
|
|
31
|
+
/**
|
|
32
|
+
* Internal: yield the draft's zipped bytes + metadata so
|
|
33
|
+
* `client.submitRun` can upload it as an asset.
|
|
34
|
+
*/
|
|
35
|
+
_takeDraftBundle(): {
|
|
36
|
+
name: string;
|
|
37
|
+
contentHash: string;
|
|
38
|
+
bytes: Uint8Array;
|
|
39
|
+
} | undefined;
|
|
40
|
+
toJSON(): AgentsMdRef;
|
|
41
|
+
}
|
|
42
|
+
export interface DraftAgentsMdRef {
|
|
43
|
+
readonly kind: "draft";
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly contentHash: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { hashSkillBundle } from "./bundle.js";
|
|
3
|
+
import { strToU8, zipSync } from "fflate";
|
|
4
|
+
/**
|
|
5
|
+
* AgentsMd — a single markdown file delivered to the agent as the
|
|
6
|
+
* first user turn of the session (matches Claude Code's CLAUDE.md
|
|
7
|
+
* behaviour).
|
|
8
|
+
*
|
|
9
|
+
* const rules = await AgentsMd.fromContent("# Be helpful", { name: "rules" });
|
|
10
|
+
* await client.submitRun({ agentsMd: [rules], ... });
|
|
11
|
+
*
|
|
12
|
+
* `client.submitRun` materializes the bytes to the hosted asset store before
|
|
13
|
+
* the run lands. Asset deduplication handles repeated uploads automatically.
|
|
14
|
+
*/
|
|
15
|
+
export class AgentsMd {
|
|
16
|
+
#ref;
|
|
17
|
+
#zipBytes;
|
|
18
|
+
#consumed = false;
|
|
19
|
+
constructor(ref, zipBytes) {
|
|
20
|
+
this.#ref = ref;
|
|
21
|
+
this.#zipBytes = zipBytes;
|
|
22
|
+
}
|
|
23
|
+
get ref() {
|
|
24
|
+
return this.#ref;
|
|
25
|
+
}
|
|
26
|
+
get isDraft() {
|
|
27
|
+
return this.#ref.kind === "draft" && !this.#consumed;
|
|
28
|
+
}
|
|
29
|
+
get isConsumed() {
|
|
30
|
+
return this.#consumed;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build a draft AgentsMd from a markdown string. The SDK zips the
|
|
34
|
+
* content under the canonical filename `AGENTS.md` so the hash is a
|
|
35
|
+
* pure function of the markdown text.
|
|
36
|
+
*/
|
|
37
|
+
static async fromContent(content, args) {
|
|
38
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
39
|
+
throw new Error("AgentsMd.fromContent: content must be a non-empty string");
|
|
40
|
+
}
|
|
41
|
+
if (!args || typeof args.name !== "string" || !WORKSPACE_NAME_RE.test(args.name)) {
|
|
42
|
+
throw new Error(`AgentsMd.fromContent: name must match ${WORKSPACE_NAME_RE.source}`);
|
|
43
|
+
}
|
|
44
|
+
const zip = zipSync({ "AGENTS.md": [strToU8(content), { mtime: ZIP_EPOCH }] }, { level: 6 });
|
|
45
|
+
const contentHash = await hashSkillBundle(zip);
|
|
46
|
+
const ref = { kind: "draft", name: args.name, contentHash };
|
|
47
|
+
return new AgentsMd(ref, zip);
|
|
48
|
+
}
|
|
49
|
+
/** Node-only convenience: read a markdown file from disk. */
|
|
50
|
+
static async fromPath(path, args) {
|
|
51
|
+
const content = await readFile(path, "utf8");
|
|
52
|
+
return AgentsMd.fromContent(content, args);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Internal: yield the draft's zipped bytes + metadata so
|
|
56
|
+
* `client.submitRun` can upload it as an asset.
|
|
57
|
+
*/
|
|
58
|
+
_takeDraftBundle() {
|
|
59
|
+
if (this.#consumed) {
|
|
60
|
+
throw new Error("AgentsMd: cannot reuse a consumed AgentsMd in submitRun. Build a fresh one " +
|
|
61
|
+
"via AgentsMd.fromContent(...) / AgentsMd.fromPath(...) per submitRun call.");
|
|
62
|
+
}
|
|
63
|
+
if (this.#ref.kind !== "draft" || !this.#zipBytes) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
this.#consumed = true;
|
|
67
|
+
return {
|
|
68
|
+
name: this.#ref.name,
|
|
69
|
+
contentHash: this.#ref.contentHash,
|
|
70
|
+
bytes: this.#zipBytes
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
toJSON() {
|
|
74
|
+
if (this.#ref.kind === "draft") {
|
|
75
|
+
throw new Error("AgentsMd: draft AgentsMd cannot be JSON-serialised — it only becomes a wire " +
|
|
76
|
+
"ref when client.submitRun uploads the bytes as an asset.");
|
|
77
|
+
}
|
|
78
|
+
return this.#ref;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const WORKSPACE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$/;
|
|
82
|
+
const ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
|
|
83
|
+
//# sourceMappingURL=agents-md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-md.js","sourceRoot":"","sources":["../src/agents-md.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset materialization for the SDK submit path.
|
|
3
|
+
*
|
|
4
|
+
* Every inline `Skill` / `AgentsMd` / `File` draft is materialized to the
|
|
5
|
+
* hosted API's content-addressable asset store before the submit round-trip,
|
|
6
|
+
* so the wire submission carries only storage-neutral `kind:"asset"` refs.
|
|
7
|
+
*
|
|
8
|
+
* Upload is direct-to-storage (no bytes through the hosted API, so bundle size
|
|
9
|
+
* is bounded by the object store rather than by the API's memory /
|
|
10
|
+
* request-payload limits):
|
|
11
|
+
*
|
|
12
|
+
* 1. POST /assets/presign → { exists } | { uploadUrl, requiredHeaders }
|
|
13
|
+
* - `exists:true` is a dedup hit; we're done.
|
|
14
|
+
* - otherwise the Worker mints a presigned PUT scoped to the exact
|
|
15
|
+
* content-addressed key and signs `x-amz-checksum-sha256` so the object
|
|
16
|
+
* store enforces integrity server-side.
|
|
17
|
+
* 2. PUT the bytes straight to `uploadUrl` with `requiredHeaders` (the signed
|
|
18
|
+
* checksum). The store rejects a byte mismatch — a 2xx proves bytes == hash.
|
|
19
|
+
* 3. POST /assets/finalize → confirms the object exists (HEAD only).
|
|
20
|
+
*
|
|
21
|
+
* Fallback: when the hosted API has no object-store upload credentials it
|
|
22
|
+
* answers presign with 503 `presign_unconfigured`; we POST the bytes to the
|
|
23
|
+
* buffered `/assets`
|
|
24
|
+
* path (small bundles only). The runner re-verifies the hash on download in
|
|
25
|
+
* every case.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Subset of `HttpClient` needed by the asset uploader. Defined as a
|
|
29
|
+
* structural type so tests can supply a thin stub without dragging in
|
|
30
|
+
* the full client.
|
|
31
|
+
*/
|
|
32
|
+
export interface AssetsHttpClient {
|
|
33
|
+
request<T>(path: string, init?: RequestInit, query?: Record<string, string>): Promise<T>;
|
|
34
|
+
}
|
|
35
|
+
/** Minimal fetch shape for the direct-to-storage PUT (defaults to global fetch). */
|
|
36
|
+
export type AssetFetch = (input: string, init?: RequestInit) => Promise<{
|
|
37
|
+
readonly ok: boolean;
|
|
38
|
+
readonly status: number;
|
|
39
|
+
text(): Promise<string>;
|
|
40
|
+
}>;
|
|
41
|
+
export interface UploadAssetArgs {
|
|
42
|
+
readonly http: AssetsHttpClient;
|
|
43
|
+
readonly bytes: Uint8Array;
|
|
44
|
+
/** `sha256:<hex>` — the canonical content hash declared on the wire. */
|
|
45
|
+
readonly hash: string;
|
|
46
|
+
readonly contentType?: string;
|
|
47
|
+
/** Injected in tests; defaults to `globalThis.fetch` for the direct PUT. */
|
|
48
|
+
readonly fetch?: AssetFetch;
|
|
49
|
+
}
|
|
50
|
+
export interface UploadedAsset {
|
|
51
|
+
readonly assetId: string;
|
|
52
|
+
readonly contentHash: string;
|
|
53
|
+
readonly sizeBytes: number;
|
|
54
|
+
/** true if identical bytes were already present (dedup hit). */
|
|
55
|
+
readonly exists: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Upload `bytes` to the hosted API's content-addressable asset store via the
|
|
59
|
+
* direct-to-storage presign flow, falling back to the buffered `/assets` POST
|
|
60
|
+
* when the hosted API has no object-store upload credentials.
|
|
61
|
+
*
|
|
62
|
+
* Verifies the advisory hash matches the bytes BEFORE sending so a mismatch
|
|
63
|
+
* fails fast on the client. The store (or the buffered endpoint) re-verifies, and the
|
|
64
|
+
* runner re-checks on download.
|
|
65
|
+
*/
|
|
66
|
+
export declare function uploadAsset(args: UploadAssetArgs): Promise<UploadedAsset>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset materialization for the SDK submit path.
|
|
3
|
+
*
|
|
4
|
+
* Every inline `Skill` / `AgentsMd` / `File` draft is materialized to the
|
|
5
|
+
* hosted API's content-addressable asset store before the submit round-trip,
|
|
6
|
+
* so the wire submission carries only storage-neutral `kind:"asset"` refs.
|
|
7
|
+
*
|
|
8
|
+
* Upload is direct-to-storage (no bytes through the hosted API, so bundle size
|
|
9
|
+
* is bounded by the object store rather than by the API's memory /
|
|
10
|
+
* request-payload limits):
|
|
11
|
+
*
|
|
12
|
+
* 1. POST /assets/presign → { exists } | { uploadUrl, requiredHeaders }
|
|
13
|
+
* - `exists:true` is a dedup hit; we're done.
|
|
14
|
+
* - otherwise the Worker mints a presigned PUT scoped to the exact
|
|
15
|
+
* content-addressed key and signs `x-amz-checksum-sha256` so the object
|
|
16
|
+
* store enforces integrity server-side.
|
|
17
|
+
* 2. PUT the bytes straight to `uploadUrl` with `requiredHeaders` (the signed
|
|
18
|
+
* checksum). The store rejects a byte mismatch — a 2xx proves bytes == hash.
|
|
19
|
+
* 3. POST /assets/finalize → confirms the object exists (HEAD only).
|
|
20
|
+
*
|
|
21
|
+
* Fallback: when the hosted API has no object-store upload credentials it
|
|
22
|
+
* answers presign with 503 `presign_unconfigured`; we POST the bytes to the
|
|
23
|
+
* buffered `/assets`
|
|
24
|
+
* path (small bundles only). The runner re-verifies the hash on download in
|
|
25
|
+
* every case.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Upload `bytes` to the hosted API's content-addressable asset store via the
|
|
29
|
+
* direct-to-storage presign flow, falling back to the buffered `/assets` POST
|
|
30
|
+
* when the hosted API has no object-store upload credentials.
|
|
31
|
+
*
|
|
32
|
+
* Verifies the advisory hash matches the bytes BEFORE sending so a mismatch
|
|
33
|
+
* fails fast on the client. The store (or the buffered endpoint) re-verifies, and the
|
|
34
|
+
* runner re-checks on download.
|
|
35
|
+
*/
|
|
36
|
+
export async function uploadAsset(args) {
|
|
37
|
+
const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
|
|
38
|
+
const actual = await computeSha256Hex(args.bytes);
|
|
39
|
+
if (actual !== expected) {
|
|
40
|
+
throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} ` +
|
|
41
|
+
`but caller declared ${args.hash}. Aborting to avoid uploading corrupted data.`);
|
|
42
|
+
}
|
|
43
|
+
const contentHashHeader = `sha256:${actual}`;
|
|
44
|
+
// ---- Step 1: presign (control plane) ----
|
|
45
|
+
let presign;
|
|
46
|
+
try {
|
|
47
|
+
presign = await args.http.request("/assets/presign", {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: { "content-type": "application/json" },
|
|
50
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
// 503 presign_unconfigured → fall back to the buffered upload path.
|
|
55
|
+
if (isPresignUnconfigured(err)) {
|
|
56
|
+
return uploadAssetBuffered(args, actual);
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
// Dedup hit — identical bytes already vaulted under this workspace.
|
|
61
|
+
if (presign.exists) {
|
|
62
|
+
const contentHash = presign.contentHash ?? contentHashHeader;
|
|
63
|
+
return {
|
|
64
|
+
assetId: presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
65
|
+
contentHash,
|
|
66
|
+
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
67
|
+
exists: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (!presign.uploadUrl) {
|
|
71
|
+
throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
|
|
72
|
+
}
|
|
73
|
+
// ---- Step 2: PUT bytes straight to object storage (data plane, bypasses the hosted API) ----
|
|
74
|
+
const doFetch = args.fetch ?? globalThis.fetch;
|
|
75
|
+
const putHeaders = {
|
|
76
|
+
"content-type": args.contentType ?? "application/zip",
|
|
77
|
+
...(presign.requiredHeaders ?? {})
|
|
78
|
+
};
|
|
79
|
+
const putRes = await doFetch(presign.uploadUrl, {
|
|
80
|
+
method: "PUT",
|
|
81
|
+
headers: putHeaders,
|
|
82
|
+
body: args.bytes
|
|
83
|
+
});
|
|
84
|
+
if (!putRes.ok) {
|
|
85
|
+
const detail = await putRes.text().catch(() => "");
|
|
86
|
+
throw new Error(`uploadAsset: direct upload PUT failed with status ${putRes.status}` +
|
|
87
|
+
(detail ? `: ${detail.slice(0, 500)}` : ""));
|
|
88
|
+
}
|
|
89
|
+
// ---- Step 3: finalize (control plane confirms existence via HEAD) ----
|
|
90
|
+
const fin = await args.http.request("/assets/finalize", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: { "content-type": "application/json" },
|
|
93
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
94
|
+
});
|
|
95
|
+
const contentHash = fin.contentHash ?? presign.contentHash ?? contentHashHeader;
|
|
96
|
+
return {
|
|
97
|
+
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
98
|
+
contentHash,
|
|
99
|
+
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
100
|
+
exists: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Detect the 503 `presign_unconfigured` rejection, regardless of error class. */
|
|
104
|
+
function isPresignUnconfigured(err) {
|
|
105
|
+
if (!err || typeof err !== "object")
|
|
106
|
+
return false;
|
|
107
|
+
const e = err;
|
|
108
|
+
if (e.status !== 503)
|
|
109
|
+
return false;
|
|
110
|
+
const detailCode = e.details?.code;
|
|
111
|
+
return e.code === "presign_unconfigured" || detailCode === "presign_unconfigured";
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Fallback: POST the bytes to the buffered `/assets` endpoint. Used only when
|
|
115
|
+
* the hosted API has no object-store upload credentials (presign 503). Subject to the API's
|
|
116
|
+
* payload limit, so suitable for small bundles only.
|
|
117
|
+
*/
|
|
118
|
+
async function uploadAssetBuffered(args, actualHex) {
|
|
119
|
+
const body = await args.http.request("/assets", {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"content-type": args.contentType ?? "application/zip",
|
|
123
|
+
"content-length": String(args.bytes.byteLength),
|
|
124
|
+
"x-asset-hash": `sha256:${actualHex}`
|
|
125
|
+
},
|
|
126
|
+
body: args.bytes
|
|
127
|
+
});
|
|
128
|
+
const contentHash = body.contentHash ?? body.hash ?? `sha256:${actualHex}`;
|
|
129
|
+
return {
|
|
130
|
+
assetId: body.assetId ?? assetIdFromContentHash(contentHash),
|
|
131
|
+
contentHash,
|
|
132
|
+
sizeBytes: body.sizeBytes,
|
|
133
|
+
exists: body.exists
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Internal helpers
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
/**
|
|
140
|
+
* Compute SHA-256 over `bytes` and return the lowercase hex digest.
|
|
141
|
+
* Uses Web Crypto when available (Node 18+ / browser); the SDK already
|
|
142
|
+
* requires Web Crypto for `hashSkillBundle`.
|
|
143
|
+
*/
|
|
144
|
+
async function computeSha256Hex(bytes) {
|
|
145
|
+
const subtle = globalThis.crypto?.subtle;
|
|
146
|
+
if (!subtle) {
|
|
147
|
+
throw new Error("uploadAsset: globalThis.crypto.subtle is not available; " +
|
|
148
|
+
"Node 18+ or a Web-Crypto-capable runtime is required");
|
|
149
|
+
}
|
|
150
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
151
|
+
copy.set(bytes);
|
|
152
|
+
const digest = await subtle.digest("SHA-256", copy.buffer);
|
|
153
|
+
return bufferToHex(digest);
|
|
154
|
+
}
|
|
155
|
+
function assetIdFromContentHash(contentHash) {
|
|
156
|
+
const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
|
|
157
|
+
return `asset_${hex}`;
|
|
158
|
+
}
|
|
159
|
+
function bufferToHex(buffer) {
|
|
160
|
+
const view = new Uint8Array(buffer);
|
|
161
|
+
let out = "";
|
|
162
|
+
for (let i = 0; i < view.length; i++) {
|
|
163
|
+
const byte = view[i];
|
|
164
|
+
out += byte.toString(16).padStart(2, "0");
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=asset-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-upload.js","sourceRoot":"","sources":["../src/asset-upload.ts"],"names":[],"mappings":"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"}
|
package/dist/bundle.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory skill bundle: a flat path -> bytes map and the
|
|
3
|
+
* deterministically-zipped representation.
|
|
4
|
+
*
|
|
5
|
+
* The SDK runs only the cheap, safety-critical checks here
|
|
6
|
+
* (`validateSkillBundleEntry`: no `..`, no absolute paths, no Windows
|
|
7
|
+
* backslashes, depth/length limits). The BFF re-canonicalises and
|
|
8
|
+
* recomputes the canonical hash on receipt — SDK-side hashing is NOT
|
|
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 `InlineSkillRef.contentHash` field, is used for retry
|
|
14
|
+
* de-dup and janitor reconciliation, and is recomputed server-side
|
|
15
|
+
* (mismatch → submission rejected).
|
|
16
|
+
*/
|
|
17
|
+
export interface BundledSkill {
|
|
18
|
+
readonly zip: Uint8Array;
|
|
19
|
+
readonly fileCount: number;
|
|
20
|
+
readonly compressedSize: number;
|
|
21
|
+
}
|
|
22
|
+
/** Inline files map: path -> contents (UTF-8 string or raw bytes). */
|
|
23
|
+
export type SkillFiles = Readonly<Record<string, string | Uint8Array>>;
|
|
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
|
+
* `InlineSkillRef.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
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { zipSync } from "fflate";
|
|
2
|
+
import { SKILL_BUNDLE_LIMITS, validateSkillBundleEntry } from "./_contracts/index.js";
|
|
3
|
+
const TEXT = new TextEncoder();
|
|
4
|
+
export function bundleSkillFiles(files) {
|
|
5
|
+
if (!files || typeof files !== "object") {
|
|
6
|
+
throw new Error("Skill files map is required");
|
|
7
|
+
}
|
|
8
|
+
const entries = Object.entries(files);
|
|
9
|
+
if (entries.length === 0) {
|
|
10
|
+
throw new Error("Skill files map cannot be empty");
|
|
11
|
+
}
|
|
12
|
+
if (entries.length > SKILL_BUNDLE_LIMITS.maxFiles) {
|
|
13
|
+
throw new Error(`Skill bundle exceeds ${SKILL_BUNDLE_LIMITS.maxFiles} file limit (got ${entries.length})`);
|
|
14
|
+
}
|
|
15
|
+
const collected = new Map();
|
|
16
|
+
let hasSkillMd = false;
|
|
17
|
+
let totalDecompressed = 0;
|
|
18
|
+
for (const [rawPath, contents] of entries) {
|
|
19
|
+
const bytes = typeof contents === "string" ? TEXT.encode(contents) : contents;
|
|
20
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
21
|
+
throw new Error(`Skill file "${rawPath}" must be a string or Uint8Array`);
|
|
22
|
+
}
|
|
23
|
+
const entry = validateSkillBundleEntry({ path: rawPath, size: bytes.byteLength });
|
|
24
|
+
if (entry.path === "SKILL.md") {
|
|
25
|
+
hasSkillMd = true;
|
|
26
|
+
}
|
|
27
|
+
totalDecompressed += bytes.byteLength;
|
|
28
|
+
if (totalDecompressed > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
|
|
29
|
+
throw new Error(`Skill bundle exceeds decompressed cap of ${SKILL_BUNDLE_LIMITS.maxDecompressedBytes} bytes`);
|
|
30
|
+
}
|
|
31
|
+
if (collected.has(entry.path)) {
|
|
32
|
+
throw new Error(`Skill bundle contains duplicate path: ${entry.path}`);
|
|
33
|
+
}
|
|
34
|
+
collected.set(entry.path, bytes);
|
|
35
|
+
}
|
|
36
|
+
if (!hasSkillMd) {
|
|
37
|
+
throw new Error('Skill bundle must contain a "SKILL.md" file at the root. ' +
|
|
38
|
+
"If you want to upload an instructions file or generic agent context, " +
|
|
39
|
+
"use AgentsMd.fromPath / File.fromPath instead.");
|
|
40
|
+
}
|
|
41
|
+
// Sort entries and pin every mtime to the epoch so the byte output is
|
|
42
|
+
// identical across machines and re-runs (the BFF re-canonicalises and
|
|
43
|
+
// recomputes the canonical hash, so this is for retry-safety / debug
|
|
44
|
+
// reproducibility rather than a wire-shape contract).
|
|
45
|
+
const sorted = [...collected.entries()].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
46
|
+
const zippable = {};
|
|
47
|
+
for (const [path, bytes] of sorted) {
|
|
48
|
+
zippable[path] = [bytes, { mtime: ZIP_EPOCH }];
|
|
49
|
+
}
|
|
50
|
+
const zip = zipSync(zippable, { level: 6 });
|
|
51
|
+
if (zip.byteLength > SKILL_BUNDLE_LIMITS.maxCompressedBytes) {
|
|
52
|
+
throw new Error(`Skill bundle exceeds compressed cap of ${SKILL_BUNDLE_LIMITS.maxCompressedBytes} bytes (got ${zip.byteLength})`);
|
|
53
|
+
}
|
|
54
|
+
return { zip, fileCount: entries.length, compressedSize: zip.byteLength };
|
|
55
|
+
}
|
|
56
|
+
const ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
|
|
57
|
+
/**
|
|
58
|
+
* Compute `sha256:<hex>` of the given canonicalised zip bytes. Used by
|
|
59
|
+
* `Skill.fromFiles` / `Skill.fromPath` to populate the
|
|
60
|
+
* `InlineSkillRef.contentHash` field. The hash is advisory — the BFF
|
|
61
|
+
* recomputes server-side after re-canonicalising the zip; a mismatch is
|
|
62
|
+
* rejected. Web-Crypto-only so the SDK works in Node, edge runtimes,
|
|
63
|
+
* and browsers without polyfills.
|
|
64
|
+
*/
|
|
65
|
+
export async function hashSkillBundle(zipBytes) {
|
|
66
|
+
const subtle = globalThis.crypto?.subtle;
|
|
67
|
+
if (!subtle) {
|
|
68
|
+
throw new Error("hashSkillBundle: globalThis.crypto.subtle is not available; Node 18+ or a Web-Crypto-capable runtime is required");
|
|
69
|
+
}
|
|
70
|
+
// crypto.subtle.digest expects a BufferSource. Pass a freshly-sliced
|
|
71
|
+
// copy to detach from any external Uint8Array view (Web Crypto rejects
|
|
72
|
+
// non-zero byteOffset SharedArrayBuffer views, and view detach also
|
|
73
|
+
// protects against the caller mutating the input after the digest is
|
|
74
|
+
// computed).
|
|
75
|
+
const view = new Uint8Array(zipBytes.byteLength);
|
|
76
|
+
view.set(zipBytes);
|
|
77
|
+
const digest = await subtle.digest("SHA-256", view.buffer);
|
|
78
|
+
return "sha256:" + bufferToHex(digest);
|
|
79
|
+
}
|
|
80
|
+
function bufferToHex(buffer) {
|
|
81
|
+
const view = new Uint8Array(buffer);
|
|
82
|
+
let out = "";
|
|
83
|
+
for (let i = 0; i < view.length; i++) {
|
|
84
|
+
const byte = view[i];
|
|
85
|
+
out += byte.toString(16).padStart(2, "0");
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|