@aexhq/sdk 0.40.5 → 0.40.7
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/_contracts/api-key.js +0 -1
- package/dist/_contracts/bundle-manifest.js +0 -1
- package/dist/_contracts/connection-ticket.js +0 -1
- package/dist/_contracts/error-codes.js +0 -1
- package/dist/_contracts/error-factory.js +0 -1
- package/dist/_contracts/event-envelope.js +0 -1
- package/dist/_contracts/event-stream-client.js +0 -1
- package/dist/_contracts/event-view.js +0 -1
- package/dist/_contracts/http.js +0 -1
- package/dist/_contracts/index.js +0 -1
- package/dist/_contracts/internal.d.ts +36 -0
- package/dist/_contracts/internal.js +155 -6
- package/dist/_contracts/models.js +0 -1
- package/dist/_contracts/operations.js +1 -2
- package/dist/_contracts/post-hook.js +0 -1
- package/dist/_contracts/provider-support.js +0 -1
- package/dist/_contracts/run-artifacts.js +0 -1
- package/dist/_contracts/run-config.js +0 -1
- package/dist/_contracts/run-cost.js +0 -1
- package/dist/_contracts/run-custody.js +0 -1
- package/dist/_contracts/run-record.js +0 -1
- package/dist/_contracts/run-retention.js +0 -1
- package/dist/_contracts/run-trace.js +0 -1
- package/dist/_contracts/run-unit.js +0 -1
- package/dist/_contracts/runner-event.js +0 -1
- package/dist/_contracts/runtime-manifest.js +0 -1
- package/dist/_contracts/runtime-security-profile.js +0 -1
- package/dist/_contracts/runtime-sizes.js +0 -1
- package/dist/_contracts/runtime-types.js +0 -1
- package/dist/_contracts/sdk-errors.js +0 -1
- package/dist/_contracts/sdk-secrets.js +0 -1
- package/dist/_contracts/side-effect-audit.js +0 -1
- package/dist/_contracts/sse.js +0 -0
- package/dist/_contracts/stable.d.ts +10 -5
- package/dist/_contracts/stable.js +10 -6
- package/dist/_contracts/status.js +0 -1
- package/dist/_contracts/submission.js +0 -1
- package/dist/_contracts/suggest.js +0 -1
- package/dist/_contracts/webhook-verify.js +0 -1
- package/dist/asset-upload.d.ts +3 -43
- package/dist/asset-upload.js +1 -109
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +354 -781
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.js +5 -11
- package/dist/client.js.map +1 -1
- package/dist/fetch-archive.js +76 -16
- package/dist/fetch-archive.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +5 -5
- package/docs/secrets.md +0 -5
- package/docs/webhooks.md +0 -4
- package/package.json +1 -1
package/dist/_contracts/http.js
CHANGED
package/dist/_contracts/index.js
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
1
|
export * from "./models.js";
|
|
2
2
|
export * from "./post-hook.js";
|
|
3
3
|
export * from "./submission.js";
|
|
4
|
+
/**
|
|
5
|
+
* Subset of `HttpClient` needed by the asset uploader. Defined structurally so
|
|
6
|
+
* SDK and CLI tests can supply thin stubs without dragging in the full client.
|
|
7
|
+
*/
|
|
8
|
+
export interface AssetsHttpClient {
|
|
9
|
+
request<T>(path: string, init?: RequestInit, query?: Record<string, string>): Promise<T>;
|
|
10
|
+
}
|
|
11
|
+
/** Minimal fetch shape for the direct-to-storage PUT. */
|
|
12
|
+
export type AssetFetch = (input: string, init?: RequestInit) => Promise<{
|
|
13
|
+
readonly ok: boolean;
|
|
14
|
+
readonly status: number;
|
|
15
|
+
text(): Promise<string>;
|
|
16
|
+
readonly headers?: {
|
|
17
|
+
get(name: string): string | null;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export interface UploadAssetArgs {
|
|
21
|
+
readonly http: AssetsHttpClient;
|
|
22
|
+
readonly bytes: Uint8Array;
|
|
23
|
+
/** `sha256:<hex>` - the canonical content hash declared on the wire. */
|
|
24
|
+
readonly hash: string;
|
|
25
|
+
readonly contentType?: string;
|
|
26
|
+
readonly fetch?: AssetFetch;
|
|
27
|
+
}
|
|
28
|
+
export interface UploadedAsset {
|
|
29
|
+
readonly assetId: string;
|
|
30
|
+
readonly contentHash: string;
|
|
31
|
+
readonly sizeBytes: number;
|
|
32
|
+
/** true if identical bytes were already present (dedup hit). */
|
|
33
|
+
readonly exists: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Upload `bytes` to the hosted API's content-addressable asset store via the
|
|
37
|
+
* direct-to-storage presign flow shared by SDK and CLI callers.
|
|
38
|
+
*/
|
|
39
|
+
export declare function uploadAsset(args: UploadAssetArgs): Promise<UploadedAsset>;
|
|
@@ -1,10 +1,159 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
import { extractErrorCode, redactUrl } from "./sdk-errors.js";
|
|
2
|
+
// Workspace-internal entry point. Re-exports submission building blocks and
|
|
3
|
+
// hosts small shared helpers so public packages can avoid hand-mirroring
|
|
4
|
+
// behavior. NOT part of the public `@aexhq/contracts` surface; do NOT add this
|
|
5
|
+
// to the package `index`. Consumers reach it via the explicit
|
|
6
6
|
// `@aexhq/contracts/internal` subpath.
|
|
7
7
|
export * from "./models.js";
|
|
8
8
|
export * from "./post-hook.js";
|
|
9
9
|
export * from "./submission.js";
|
|
10
|
-
|
|
10
|
+
const DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
11
|
+
/**
|
|
12
|
+
* Upload `bytes` to the hosted API's content-addressable asset store via the
|
|
13
|
+
* direct-to-storage presign flow shared by SDK and CLI callers.
|
|
14
|
+
*/
|
|
15
|
+
export async function uploadAsset(args) {
|
|
16
|
+
const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
|
|
17
|
+
const actual = await computeSha256Hex(args.bytes);
|
|
18
|
+
if (actual !== expected) {
|
|
19
|
+
throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} ` +
|
|
20
|
+
`but caller declared ${args.hash}. Aborting to avoid uploading corrupted data.`);
|
|
21
|
+
}
|
|
22
|
+
const contentHashHeader = `sha256:${actual}`;
|
|
23
|
+
const presign = await args.http.request("/assets/presign", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: { "content-type": "application/json" },
|
|
26
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
27
|
+
});
|
|
28
|
+
if (presign.exists) {
|
|
29
|
+
const contentHash = presign.contentHash ?? contentHashHeader;
|
|
30
|
+
return {
|
|
31
|
+
assetId: presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
32
|
+
contentHash,
|
|
33
|
+
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
34
|
+
exists: true
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!presign.uploadUrl) {
|
|
38
|
+
throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
|
|
39
|
+
}
|
|
40
|
+
const doFetch = args.fetch ?? globalThis.fetch;
|
|
41
|
+
const putHeaders = {
|
|
42
|
+
"content-type": args.contentType ?? "application/zip",
|
|
43
|
+
...(presign.requiredHeaders ?? {})
|
|
44
|
+
};
|
|
45
|
+
await putWithRetry(doFetch, presign.uploadUrl, {
|
|
46
|
+
method: "PUT",
|
|
47
|
+
headers: putHeaders,
|
|
48
|
+
body: args.bytes
|
|
49
|
+
});
|
|
50
|
+
const fin = await args.http.request("/assets/finalize", {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: { "content-type": "application/json" },
|
|
53
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
54
|
+
});
|
|
55
|
+
const contentHash = fin.contentHash ?? presign.contentHash ?? contentHashHeader;
|
|
56
|
+
return {
|
|
57
|
+
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
58
|
+
contentHash,
|
|
59
|
+
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
60
|
+
exists: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function computeSha256Hex(bytes) {
|
|
64
|
+
const subtle = globalThis.crypto?.subtle;
|
|
65
|
+
if (!subtle) {
|
|
66
|
+
throw new Error("uploadAsset: globalThis.crypto.subtle is not available; " +
|
|
67
|
+
"Bun, Node 18+, or a Web-Crypto-capable runtime is required");
|
|
68
|
+
}
|
|
69
|
+
const digest = await subtle.digest("SHA-256", bytes);
|
|
70
|
+
return bufferToHex(digest);
|
|
71
|
+
}
|
|
72
|
+
function assetIdFromContentHash(contentHash) {
|
|
73
|
+
const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
|
|
74
|
+
return `asset_${hex}`;
|
|
75
|
+
}
|
|
76
|
+
async function putWithRetry(fetchImpl, uploadUrl, init) {
|
|
77
|
+
for (let attempt = 1; attempt <= DIRECT_UPLOAD_MAX_ATTEMPTS; attempt++) {
|
|
78
|
+
let response;
|
|
79
|
+
try {
|
|
80
|
+
response = await fetchImpl(uploadUrl, init);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (attempt < DIRECT_UPLOAD_MAX_ATTEMPTS && isRetryableUploadError(err)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
throw directUploadNetworkError(uploadUrl, err, attempt);
|
|
87
|
+
}
|
|
88
|
+
if (response.ok)
|
|
89
|
+
return;
|
|
90
|
+
if (attempt < DIRECT_UPLOAD_MAX_ATTEMPTS && isRetryableUploadStatus(response.status)) {
|
|
91
|
+
await response.text().catch(() => "");
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const detail = await response.text().catch(() => "");
|
|
95
|
+
throw directUploadResponseError(uploadUrl, response.status, detail, attempt);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function isRetryableUploadStatus(status) {
|
|
99
|
+
return status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599);
|
|
100
|
+
}
|
|
101
|
+
function isRetryableUploadError(err) {
|
|
102
|
+
if (isNamedError(err, "AbortError"))
|
|
103
|
+
return false;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
function directUploadNetworkError(uploadUrl, err, attempts) {
|
|
107
|
+
const safeUrl = redactUrl(uploadUrl);
|
|
108
|
+
const code = extractErrorCode(err);
|
|
109
|
+
const detail = sanitizeUploadText(errorMessage(err)).slice(0, 500);
|
|
110
|
+
return new Error(`uploadAsset: direct upload PUT failed for ${safeUrl} after ${attemptsLabel(attempts)}` +
|
|
111
|
+
(code ? ` (${code})` : "") +
|
|
112
|
+
(detail ? `: ${detail}` : ""));
|
|
113
|
+
}
|
|
114
|
+
function directUploadResponseError(uploadUrl, status, detail, attempts) {
|
|
115
|
+
const safeUrl = redactUrl(uploadUrl);
|
|
116
|
+
const safeDetail = sanitizeUploadText(detail).slice(0, 500);
|
|
117
|
+
return new Error(`uploadAsset: direct upload PUT failed for ${safeUrl} with status ${status}` +
|
|
118
|
+
(attempts > 1 ? ` after ${attemptsLabel(attempts)}` : "") +
|
|
119
|
+
(safeDetail ? `: ${safeDetail}` : ""));
|
|
120
|
+
}
|
|
121
|
+
function attemptsLabel(attempts) {
|
|
122
|
+
return attempts === 1 ? "1 attempt" : `${attempts} attempts`;
|
|
123
|
+
}
|
|
124
|
+
function errorMessage(err) {
|
|
125
|
+
if (err instanceof Error)
|
|
126
|
+
return err.message || err.name;
|
|
127
|
+
if (typeof err === "string")
|
|
128
|
+
return err;
|
|
129
|
+
return String(err);
|
|
130
|
+
}
|
|
131
|
+
function isNamedError(err, name) {
|
|
132
|
+
return stringProperty(err, "name") === name;
|
|
133
|
+
}
|
|
134
|
+
function stringProperty(value, key) {
|
|
135
|
+
if (!value || typeof value !== "object")
|
|
136
|
+
return undefined;
|
|
137
|
+
const prop = value[key];
|
|
138
|
+
return typeof prop === "string" && prop.length > 0 ? prop : undefined;
|
|
139
|
+
}
|
|
140
|
+
function sanitizeUploadText(text) {
|
|
141
|
+
return text
|
|
142
|
+
.replace(/https?:\/\/[^\s<>"'`]+/g, (raw) => redactUrlPreservingTrailingPunctuation(raw))
|
|
143
|
+
.replace(/\b(?:X-Amz-(?:Algorithm|Credential|Date|Expires|Security-Token|Signature|SignedHeaders)|AWSAccessKeyId|Signature|Credential|Security-Token|AccessKeyId|SecretAccessKey|SessionToken)=([^&\s<>"'`]+)/gi, "[redacted]")
|
|
144
|
+
.replace(/\bAKIA[0-9A-Z]{8,}\b/g, "[redacted]");
|
|
145
|
+
}
|
|
146
|
+
function redactUrlPreservingTrailingPunctuation(raw) {
|
|
147
|
+
const trailing = raw.match(/[),.;:!?]+$/)?.[0] ?? "";
|
|
148
|
+
const candidate = trailing ? raw.slice(0, -trailing.length) : raw;
|
|
149
|
+
return `${redactUrl(candidate)}${trailing}`;
|
|
150
|
+
}
|
|
151
|
+
function bufferToHex(buffer) {
|
|
152
|
+
const view = new Uint8Array(buffer);
|
|
153
|
+
let out = "";
|
|
154
|
+
for (let i = 0; i < view.length; i++) {
|
|
155
|
+
const byte = view[i];
|
|
156
|
+
out += byte.toString(16).padStart(2, "0");
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
@@ -445,7 +445,7 @@ async function readCappedText(response, maxBytes) {
|
|
|
445
445
|
await reader.cancel().catch(() => { });
|
|
446
446
|
}
|
|
447
447
|
const merged = concatBytes(chunks).subarray(0, maxBytes);
|
|
448
|
-
const truncated = declared !== undefined ? declared > maxBytes : sawMore;
|
|
448
|
+
const truncated = declared !== undefined ? declared > maxBytes : read > maxBytes || sawMore;
|
|
449
449
|
const totalBytes = declared ?? read;
|
|
450
450
|
return { text: decoder.decode(merged), truncated, totalBytes };
|
|
451
451
|
}
|
|
@@ -1082,4 +1082,3 @@ export async function uploadWorkspaceAsset(http, input) {
|
|
|
1082
1082
|
body: input.bytes
|
|
1083
1083
|
});
|
|
1084
1084
|
}
|
|
1085
|
-
//# sourceMappingURL=operations.js.map
|
package/dist/_contracts/sse.js
CHANGED
|
Binary file
|
|
@@ -22,18 +22,23 @@
|
|
|
22
22
|
* exact default API plane.
|
|
23
23
|
*/
|
|
24
24
|
export declare const AEX_DEFAULT_BASE_URL = "https://api.aex.dev";
|
|
25
|
+
/**
|
|
26
|
+
* Canonical hosted dev API plane URL. Used when a self-describing dev API key is
|
|
27
|
+
* passed with no explicit `baseUrl`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const AEX_DEV_BASE_URL = "https://dev-api.aex.dev";
|
|
25
30
|
/**
|
|
26
31
|
* Plane → API base URL. When the SDK constructor is given no explicit `baseUrl`
|
|
27
32
|
* it DERIVES the target from the API key's embedded plane (see
|
|
28
33
|
* {@link import("./api-key.js").parseApiKey}) rather than blindly defaulting to
|
|
29
|
-
* prod — so a dev key
|
|
34
|
+
* prod — so a dev key routes to `dev-api.aex.dev` and a prd key routes to
|
|
35
|
+
* `api.aex.dev`.
|
|
30
36
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* — but the plane-mismatch guard (dev key + prd baseUrl) still fires.
|
|
37
|
+
* The plane-mismatch guard still fires when a key is pointed at the wrong
|
|
38
|
+
* canonical host (for example, a dev key with `baseUrl=https://api.aex.dev`).
|
|
34
39
|
*/
|
|
35
40
|
export declare const PLANE_BASE_URLS: {
|
|
36
|
-
readonly dev:
|
|
41
|
+
readonly dev: "https://dev-api.aex.dev";
|
|
37
42
|
readonly prd: "https://api.aex.dev";
|
|
38
43
|
};
|
|
39
44
|
export declare function stableStringify(value: unknown): string;
|
|
@@ -23,18 +23,23 @@ import { createHash } from "node:crypto";
|
|
|
23
23
|
* exact default API plane.
|
|
24
24
|
*/
|
|
25
25
|
export const AEX_DEFAULT_BASE_URL = "https://api.aex.dev";
|
|
26
|
+
/**
|
|
27
|
+
* Canonical hosted dev API plane URL. Used when a self-describing dev API key is
|
|
28
|
+
* passed with no explicit `baseUrl`.
|
|
29
|
+
*/
|
|
30
|
+
export const AEX_DEV_BASE_URL = "https://dev-api.aex.dev";
|
|
26
31
|
/**
|
|
27
32
|
* Plane → API base URL. When the SDK constructor is given no explicit `baseUrl`
|
|
28
33
|
* it DERIVES the target from the API key's embedded plane (see
|
|
29
34
|
* {@link import("./api-key.js").parseApiKey}) rather than blindly defaulting to
|
|
30
|
-
* prod — so a dev key
|
|
35
|
+
* prod — so a dev key routes to `dev-api.aex.dev` and a prd key routes to
|
|
36
|
+
* `api.aex.dev`.
|
|
31
37
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* — but the plane-mismatch guard (dev key + prd baseUrl) still fires.
|
|
38
|
+
* The plane-mismatch guard still fires when a key is pointed at the wrong
|
|
39
|
+
* canonical host (for example, a dev key with `baseUrl=https://api.aex.dev`).
|
|
35
40
|
*/
|
|
36
41
|
export const PLANE_BASE_URLS = {
|
|
37
|
-
dev:
|
|
42
|
+
dev: AEX_DEV_BASE_URL,
|
|
38
43
|
prd: AEX_DEFAULT_BASE_URL
|
|
39
44
|
};
|
|
40
45
|
export function stableStringify(value) {
|
|
@@ -55,4 +60,3 @@ function sortValue(value) {
|
|
|
55
60
|
}
|
|
56
61
|
return value;
|
|
57
62
|
}
|
|
58
|
-
//# sourceMappingURL=stable.js.map
|
package/dist/asset-upload.d.ts
CHANGED
|
@@ -19,50 +19,10 @@
|
|
|
19
19
|
* 3. POST /assets/finalize → confirms the object exists (HEAD only).
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
|
+
import type { AssetFetch, AssetsHttpClient, UploadedAsset } from "./_contracts/internal.js";
|
|
22
23
|
import type { ByteSink } from "./canonical-zip.js";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* structural type so tests can supply a thin stub without dragging in
|
|
26
|
-
* the full client.
|
|
27
|
-
*/
|
|
28
|
-
export interface AssetsHttpClient {
|
|
29
|
-
request<T>(path: string, init?: RequestInit, query?: Record<string, string>): Promise<T>;
|
|
30
|
-
}
|
|
31
|
-
/** Minimal fetch shape for the direct-to-storage PUT (defaults to global fetch). */
|
|
32
|
-
export type AssetFetch = (input: string, init?: RequestInit) => Promise<{
|
|
33
|
-
readonly ok: boolean;
|
|
34
|
-
readonly status: number;
|
|
35
|
-
text(): Promise<string>;
|
|
36
|
-
/** Present on real `fetch` Responses; used to read a multipart part's ETag. */
|
|
37
|
-
readonly headers?: {
|
|
38
|
-
get(name: string): string | null;
|
|
39
|
-
};
|
|
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.
|
|
60
|
-
*
|
|
61
|
-
* Verifies the advisory hash matches the bytes BEFORE sending so a mismatch
|
|
62
|
-
* fails fast on the client. Object storage re-verifies via the signed checksum,
|
|
63
|
-
* and the runner re-checks on download.
|
|
64
|
-
*/
|
|
65
|
-
export declare function uploadAsset(args: UploadAssetArgs): Promise<UploadedAsset>;
|
|
24
|
+
export { uploadAsset } from "./_contracts/internal.js";
|
|
25
|
+
export type { AssetFetch, AssetsHttpClient, UploadAssetArgs, UploadedAsset } from "./_contracts/internal.js";
|
|
66
26
|
/** Default multipart part size — 16 MiB covers the 100 GiB cap within S3's 10 000-part limit. */
|
|
67
27
|
export declare const DEFAULT_MULTIPART_PART_SIZE: number;
|
|
68
28
|
/** Default in-flight part concurrency. */
|