@aexhq/sdk 0.40.7 → 0.40.8
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/asset-upload-helper.d.ts +38 -0
- package/dist/_contracts/asset-upload-helper.js +111 -0
- package/dist/_contracts/internal.d.ts +5 -9
- package/dist/_contracts/internal.js +5 -79
- package/dist/_contracts/operations.d.ts +9 -2
- package/dist/_contracts/operations.js +141 -16
- package/dist/_contracts/retry-core.d.ts +27 -0
- package/dist/_contracts/retry-core.js +75 -0
- package/dist/_contracts/runtime-types.d.ts +6 -0
- package/dist/asset-upload.d.ts +2 -1
- package/dist/asset-upload.js +28 -61
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +297 -80
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.js +3 -2
- package/dist/client.js.map +1 -1
- package/dist/retry.js +13 -41
- package/dist/retry.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2261,6 +2261,8 @@ function extractErrorMessage(body) {
|
|
|
2261
2261
|
// ../contracts/dist/operations.js
|
|
2262
2262
|
var operations_exports = {};
|
|
2263
2263
|
__export(operations_exports, {
|
|
2264
|
+
OUTPUT_FILE_TRANSFER_ATTEMPTS: () => OUTPUT_FILE_TRANSFER_ATTEMPTS,
|
|
2265
|
+
OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS: () => OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS,
|
|
2264
2266
|
READ_OUTPUT_TEXT_DEFAULT_BYTES: () => READ_OUTPUT_TEXT_DEFAULT_BYTES,
|
|
2265
2267
|
READ_OUTPUT_TEXT_MAX_BYTES: () => READ_OUTPUT_TEXT_MAX_BYTES,
|
|
2266
2268
|
approveSession: () => approveSession,
|
|
@@ -3322,28 +3324,144 @@ function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
3322
3324
|
}
|
|
3323
3325
|
return { ...selector, id: selector.id };
|
|
3324
3326
|
}
|
|
3325
|
-
async function downloadOutput(http, runId, selector) {
|
|
3327
|
+
async function downloadOutput(http, runId, selector, options) {
|
|
3326
3328
|
const output = isPathSelector(selector) ? resolveOutputFileSelector(await listOutputs(http, runId), selector, runId) : resolveOutputFileSelector([], selector, runId);
|
|
3327
|
-
const
|
|
3328
|
-
|
|
3329
|
+
const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
|
|
3330
|
+
const path = `/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`;
|
|
3331
|
+
return { output, bytes: await downloadOutputBytesWithRetry(http, path, timeoutMs) };
|
|
3329
3332
|
}
|
|
3330
3333
|
var READ_OUTPUT_TEXT_MAX_BYTES = 1e7;
|
|
3331
3334
|
var READ_OUTPUT_TEXT_DEFAULT_BYTES = 5e4;
|
|
3335
|
+
var OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS = 15e3;
|
|
3336
|
+
var OUTPUT_FILE_TRANSFER_ATTEMPTS = 2;
|
|
3332
3337
|
async function readOutputText(http, runId, selector, options) {
|
|
3333
3338
|
const maxBytes = Math.max(1, Math.min(options?.maxBytes ?? READ_OUTPUT_TEXT_DEFAULT_BYTES, READ_OUTPUT_TEXT_MAX_BYTES));
|
|
3334
3339
|
const output = isPathSelector(selector) ? resolveOutputFileSelector(await listOutputs(http, runId), selector, runId) : resolveOutputFileSelector([], selector, runId);
|
|
3335
|
-
const
|
|
3336
|
-
const
|
|
3340
|
+
const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
|
|
3341
|
+
const path = `/api/runs/${encodeURIComponent(runId)}/outputs/${encodeURIComponent(output.id)}/download`;
|
|
3342
|
+
const capped = await readOutputTextWithRetry(http, path, maxBytes, timeoutMs);
|
|
3337
3343
|
const text = options?.grep === void 0 ? capped.text : grepLines(capped.text, options.grep);
|
|
3338
3344
|
return { output, text, truncated: capped.truncated, totalBytes: capped.totalBytes };
|
|
3339
3345
|
}
|
|
3340
|
-
async function
|
|
3346
|
+
async function downloadOutputBytesWithRetry(http, path, timeoutMs) {
|
|
3347
|
+
return outputTransferWithRetry(path, timeoutMs, async () => {
|
|
3348
|
+
const response = await downloadOutputResponse(http, path, timeoutMs);
|
|
3349
|
+
return readResponseBytes(response, timeoutMs);
|
|
3350
|
+
});
|
|
3351
|
+
}
|
|
3352
|
+
async function readOutputTextWithRetry(http, path, maxBytes, timeoutMs) {
|
|
3353
|
+
return outputTransferWithRetry(path, timeoutMs, async () => {
|
|
3354
|
+
const response = await downloadOutputResponse(http, path, timeoutMs);
|
|
3355
|
+
return readCappedText(response, maxBytes, timeoutMs);
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
async function downloadOutputResponse(http, path, timeoutMs) {
|
|
3359
|
+
const controller = new AbortController();
|
|
3360
|
+
const { response } = await withOutputTransferTimeout(http.download(path, { signal: controller.signal }), timeoutMs, () => controller.abort(), "output response");
|
|
3361
|
+
return response;
|
|
3362
|
+
}
|
|
3363
|
+
async function outputTransferWithRetry(path, timeoutMs, action) {
|
|
3364
|
+
const startedMs = Date.now();
|
|
3365
|
+
let lastTimeout;
|
|
3366
|
+
for (let attempt = 1; attempt <= OUTPUT_FILE_TRANSFER_ATTEMPTS; attempt += 1) {
|
|
3367
|
+
try {
|
|
3368
|
+
return await action();
|
|
3369
|
+
} catch (err2) {
|
|
3370
|
+
if (!(err2 instanceof OutputTransferTimeoutError))
|
|
3371
|
+
throw err2;
|
|
3372
|
+
lastTimeout = err2;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
throw new AexNetworkError({
|
|
3376
|
+
method: "GET",
|
|
3377
|
+
host: "",
|
|
3378
|
+
path,
|
|
3379
|
+
cause: lastTimeout ?? new OutputTransferTimeoutError("output transfer", timeoutMs),
|
|
3380
|
+
attempts: OUTPUT_FILE_TRANSFER_ATTEMPTS,
|
|
3381
|
+
elapsedMs: Date.now() - startedMs
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
function normalizeOutputTransferTimeoutMs(value) {
|
|
3385
|
+
if (value === void 0)
|
|
3386
|
+
return OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS;
|
|
3387
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
3388
|
+
throw new RunConfigValidationError("outputs.download: timeoutMs must be a positive finite number", {
|
|
3389
|
+
timeoutMs: value
|
|
3390
|
+
});
|
|
3391
|
+
}
|
|
3392
|
+
return Math.max(1, Math.floor(value));
|
|
3393
|
+
}
|
|
3394
|
+
var OutputTransferTimeoutError = class extends Error {
|
|
3395
|
+
code = "ETIMEDOUT";
|
|
3396
|
+
constructor(label, timeoutMs) {
|
|
3397
|
+
super(`${label} timed out after ${timeoutMs}ms`);
|
|
3398
|
+
this.name = "OutputTransferTimeoutError";
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
async function withOutputTransferTimeout(promise, timeoutMs, abort, label) {
|
|
3402
|
+
let timedOut = false;
|
|
3403
|
+
let timeout;
|
|
3404
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3405
|
+
timeout = setTimeout(() => {
|
|
3406
|
+
timedOut = true;
|
|
3407
|
+
reject(new OutputTransferTimeoutError(label, timeoutMs));
|
|
3408
|
+
queueMicrotask(() => {
|
|
3409
|
+
try {
|
|
3410
|
+
abort();
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
});
|
|
3414
|
+
}, timeoutMs);
|
|
3415
|
+
});
|
|
3416
|
+
try {
|
|
3417
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
3418
|
+
} catch (err2) {
|
|
3419
|
+
if (timedOut && isAbortLikeError(err2))
|
|
3420
|
+
throw new OutputTransferTimeoutError(label, timeoutMs);
|
|
3421
|
+
throw err2;
|
|
3422
|
+
} finally {
|
|
3423
|
+
if (timeout !== void 0)
|
|
3424
|
+
clearTimeout(timeout);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
function isAbortLikeError(err2) {
|
|
3428
|
+
const name = err2?.name;
|
|
3429
|
+
return name === "AbortError";
|
|
3430
|
+
}
|
|
3431
|
+
async function readResponseBytes(response, timeoutMs) {
|
|
3432
|
+
const body = response.body;
|
|
3433
|
+
if (!body) {
|
|
3434
|
+
const buffer = await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => {
|
|
3435
|
+
}, "output body");
|
|
3436
|
+
return new Uint8Array(buffer);
|
|
3437
|
+
}
|
|
3438
|
+
const reader = body.getReader();
|
|
3439
|
+
const chunks = [];
|
|
3440
|
+
try {
|
|
3441
|
+
while (true) {
|
|
3442
|
+
const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3443
|
+
void reader.cancel().catch(() => {
|
|
3444
|
+
});
|
|
3445
|
+
}, "output body");
|
|
3446
|
+
if (done)
|
|
3447
|
+
break;
|
|
3448
|
+
if (value && value.byteLength > 0)
|
|
3449
|
+
chunks.push(value);
|
|
3450
|
+
}
|
|
3451
|
+
} finally {
|
|
3452
|
+
void reader.cancel().catch(() => {
|
|
3453
|
+
});
|
|
3454
|
+
}
|
|
3455
|
+
return concatBytes(chunks);
|
|
3456
|
+
}
|
|
3457
|
+
async function readCappedText(response, maxBytes, timeoutMs) {
|
|
3341
3458
|
const declaredRaw = response.headers.get("content-length");
|
|
3342
3459
|
const declared = declaredRaw !== null && /^\d+$/.test(declaredRaw) ? Number(declaredRaw) : void 0;
|
|
3343
3460
|
const decoder = new TextDecoder("utf-8");
|
|
3344
3461
|
const body = response.body;
|
|
3345
3462
|
if (!body) {
|
|
3346
|
-
const buf = new Uint8Array(await response.arrayBuffer())
|
|
3463
|
+
const buf = new Uint8Array(await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => {
|
|
3464
|
+
}, "output body"));
|
|
3347
3465
|
const total = declared ?? buf.byteLength;
|
|
3348
3466
|
return {
|
|
3349
3467
|
text: decoder.decode(buf.subarray(0, maxBytes)),
|
|
@@ -3357,7 +3475,10 @@ async function readCappedText(response, maxBytes) {
|
|
|
3357
3475
|
let sawMore = false;
|
|
3358
3476
|
try {
|
|
3359
3477
|
while (read < maxBytes) {
|
|
3360
|
-
const { done, value } = await reader.read()
|
|
3478
|
+
const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3479
|
+
void reader.cancel().catch(() => {
|
|
3480
|
+
});
|
|
3481
|
+
}, "output body");
|
|
3361
3482
|
if (done)
|
|
3362
3483
|
break;
|
|
3363
3484
|
if (value && value.byteLength > 0) {
|
|
@@ -3366,12 +3487,15 @@ async function readCappedText(response, maxBytes) {
|
|
|
3366
3487
|
}
|
|
3367
3488
|
}
|
|
3368
3489
|
if (read >= maxBytes) {
|
|
3369
|
-
const next = await reader.read()
|
|
3490
|
+
const next = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3491
|
+
void reader.cancel().catch(() => {
|
|
3492
|
+
});
|
|
3493
|
+
}, "output body");
|
|
3370
3494
|
if (!next.done && next.value && next.value.byteLength > 0)
|
|
3371
3495
|
sawMore = true;
|
|
3372
3496
|
}
|
|
3373
3497
|
} finally {
|
|
3374
|
-
|
|
3498
|
+
void reader.cancel().catch(() => {
|
|
3375
3499
|
});
|
|
3376
3500
|
}
|
|
3377
3501
|
const merged = concatBytes(chunks).subarray(0, maxBytes);
|
|
@@ -3443,17 +3567,17 @@ async function getBillingLedger(http, query) {
|
|
|
3443
3567
|
async function getWebhookSigningSecret(http) {
|
|
3444
3568
|
return http.request("/api/webhook/signing-secret", { method: "POST" });
|
|
3445
3569
|
}
|
|
3446
|
-
async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2) {
|
|
3570
|
+
async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2, timeoutMs = OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS) {
|
|
3447
3571
|
const entries = [];
|
|
3448
3572
|
const captured = [];
|
|
3449
3573
|
const errors = [];
|
|
3450
3574
|
for (const item of items) {
|
|
3451
3575
|
const rel = item.filename ?? item.id;
|
|
3452
3576
|
try {
|
|
3453
|
-
const
|
|
3577
|
+
const path = `/api/runs/${encodeURIComponent(runId)}/${namespace2}/${encodeURIComponent(item.id)}/download`;
|
|
3454
3578
|
entries.push({
|
|
3455
3579
|
path: `${zipPrefix}${rel}`,
|
|
3456
|
-
bytes:
|
|
3580
|
+
bytes: await downloadOutputBytesWithRetry(http, path, timeoutMs),
|
|
3457
3581
|
...item.contentType !== void 0 ? { contentType: item.contentType } : {},
|
|
3458
3582
|
customerContent: true
|
|
3459
3583
|
});
|
|
@@ -3679,9 +3803,10 @@ async function download(http, runId) {
|
|
|
3679
3803
|
jsonEntry("manifest.json", manifest)
|
|
3680
3804
|
]);
|
|
3681
3805
|
}
|
|
3682
|
-
async function downloadOutputs(http, runId) {
|
|
3806
|
+
async function downloadOutputs(http, runId, options) {
|
|
3683
3807
|
const outputs = await listOutputs(http, runId);
|
|
3684
|
-
const
|
|
3808
|
+
const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
|
|
3809
|
+
const { entries, captured, errors } = await collectArtifactBytes(http, runId, outputs, "", "outputs", timeoutMs);
|
|
3685
3810
|
return zipEntries([
|
|
3686
3811
|
...entries,
|
|
3687
3812
|
jsonEntry("manifest.json", { runId, namespace: "outputs", outputs: captured, errors })
|
|
@@ -4456,91 +4581,119 @@ function renderVerbHelp(spec) {
|
|
|
4456
4581
|
// dist/host/run-cmd.js
|
|
4457
4582
|
import { resolve as resolvePath } from "node:path";
|
|
4458
4583
|
|
|
4459
|
-
// ../contracts/dist/
|
|
4460
|
-
var
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
var
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
method: "POST",
|
|
4473
|
-
headers: { "content-type": "application/json" },
|
|
4474
|
-
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
4475
|
-
});
|
|
4476
|
-
if (presign.exists) {
|
|
4477
|
-
const contentHash2 = presign.contentHash ?? contentHashHeader;
|
|
4478
|
-
return {
|
|
4479
|
-
assetId: presign.assetId ?? assetIdFromContentHash(contentHash2),
|
|
4480
|
-
contentHash: contentHash2,
|
|
4481
|
-
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
4482
|
-
exists: true
|
|
4483
|
-
};
|
|
4584
|
+
// ../contracts/dist/retry-core.js
|
|
4585
|
+
var RETRYABLE_HTTP_STATUS = [429, 500, 502, 503, 504, 529];
|
|
4586
|
+
var RATE_LIMIT_HTTP_STATUS = [429, 503, 529];
|
|
4587
|
+
var RETRYABLE_HTTP_STATUS_SET = new Set(RETRYABLE_HTTP_STATUS);
|
|
4588
|
+
var RATE_LIMIT_HTTP_STATUS_SET = new Set(RATE_LIMIT_HTTP_STATUS);
|
|
4589
|
+
function parseRetryAfterMs(headerValue, now = Date.now()) {
|
|
4590
|
+
if (headerValue === null || headerValue === void 0)
|
|
4591
|
+
return void 0;
|
|
4592
|
+
const trimmed = headerValue.trim();
|
|
4593
|
+
if (trimmed.length === 0)
|
|
4594
|
+
return void 0;
|
|
4595
|
+
if (/^\d+$/.test(trimmed)) {
|
|
4596
|
+
return Number(trimmed) * 1e3;
|
|
4484
4597
|
}
|
|
4485
|
-
|
|
4486
|
-
|
|
4598
|
+
const dateMs = Date.parse(trimmed);
|
|
4599
|
+
if (!Number.isNaN(dateMs)) {
|
|
4600
|
+
return Math.max(0, dateMs - now);
|
|
4487
4601
|
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4602
|
+
return void 0;
|
|
4603
|
+
}
|
|
4604
|
+
function computeRetryBackoffDelayMs(config, attemptNumber, random) {
|
|
4605
|
+
const exponent = Math.max(0, attemptNumber - 1);
|
|
4606
|
+
const nominal = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** exponent);
|
|
4607
|
+
return Math.round(random() * nominal);
|
|
4608
|
+
}
|
|
4609
|
+
function computeRetryDelayMs(config, attemptNumber, random, retryAfterMs) {
|
|
4610
|
+
const backoff = computeRetryBackoffDelayMs(config, attemptNumber, random);
|
|
4611
|
+
return retryAfterMs === void 0 ? backoff : Math.max(retryAfterMs, backoff);
|
|
4612
|
+
}
|
|
4613
|
+
function abortableSleep(ms, signal) {
|
|
4614
|
+
return new Promise((resolve, reject) => {
|
|
4615
|
+
if (signal?.aborted) {
|
|
4616
|
+
reject(abortReason(signal));
|
|
4617
|
+
return;
|
|
4618
|
+
}
|
|
4619
|
+
const timer = setTimeout(() => {
|
|
4620
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4621
|
+
resolve();
|
|
4622
|
+
}, ms);
|
|
4623
|
+
const onAbort = () => {
|
|
4624
|
+
clearTimeout(timer);
|
|
4625
|
+
reject(signal ? abortReason(signal) : makeAbortError());
|
|
4626
|
+
};
|
|
4627
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4502
4628
|
});
|
|
4503
|
-
const contentHash = fin.contentHash ?? presign.contentHash ?? contentHashHeader;
|
|
4504
|
-
return {
|
|
4505
|
-
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
4506
|
-
contentHash,
|
|
4507
|
-
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
4508
|
-
exists: false
|
|
4509
|
-
};
|
|
4510
4629
|
}
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
if (!subtle) {
|
|
4514
|
-
throw new Error("uploadAsset: globalThis.crypto.subtle is not available; Bun, Node 18+, or a Web-Crypto-capable runtime is required");
|
|
4515
|
-
}
|
|
4516
|
-
const digest = await subtle.digest("SHA-256", bytes);
|
|
4517
|
-
return bufferToHex(digest);
|
|
4630
|
+
function abortReason(signal) {
|
|
4631
|
+
return signal.reason instanceof Error ? signal.reason : makeAbortError();
|
|
4518
4632
|
}
|
|
4519
|
-
function
|
|
4520
|
-
|
|
4521
|
-
|
|
4633
|
+
function makeAbortError() {
|
|
4634
|
+
if (typeof DOMException !== "undefined")
|
|
4635
|
+
return new DOMException("Aborted", "AbortError");
|
|
4636
|
+
const err2 = new Error("Aborted");
|
|
4637
|
+
err2.name = "AbortError";
|
|
4638
|
+
return err2;
|
|
4522
4639
|
}
|
|
4523
|
-
|
|
4524
|
-
|
|
4640
|
+
|
|
4641
|
+
// ../contracts/dist/asset-upload-helper.js
|
|
4642
|
+
var DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
4643
|
+
var DIRECT_UPLOAD_INITIAL_DELAY_MS = 500;
|
|
4644
|
+
var DIRECT_UPLOAD_MAX_DELAY_MS = 5e3;
|
|
4645
|
+
var DIRECT_UPLOAD_MAX_ELAPSED_MS = 3e4;
|
|
4646
|
+
async function putDirectUploadWithRetry(fetchImpl, uploadUrl, init, options = {}) {
|
|
4647
|
+
const config = resolveAssetUploadRetryConfig(options);
|
|
4648
|
+
const sleep5 = options.sleep ?? abortableSleep;
|
|
4649
|
+
const random = options.random ?? Math.random;
|
|
4650
|
+
const now = options.now ?? Date.now;
|
|
4651
|
+
const startedAt = now();
|
|
4652
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
4525
4653
|
let response;
|
|
4526
4654
|
try {
|
|
4527
4655
|
response = await fetchImpl(uploadUrl, init);
|
|
4528
4656
|
} catch (err2) {
|
|
4529
|
-
if (attempt <
|
|
4657
|
+
if (attempt < config.maxAttempts && isRetryableUploadError(err2)) {
|
|
4658
|
+
const delay = directUploadRetryDelayMs(config, attempt, random, void 0);
|
|
4659
|
+
if (!withinDirectUploadRetryBudget(config, startedAt, delay, now)) {
|
|
4660
|
+
throw directUploadNetworkError(uploadUrl, err2, attempt);
|
|
4661
|
+
}
|
|
4662
|
+
await sleep5(delay, init.signal ?? void 0);
|
|
4530
4663
|
continue;
|
|
4531
4664
|
}
|
|
4532
4665
|
throw directUploadNetworkError(uploadUrl, err2, attempt);
|
|
4533
4666
|
}
|
|
4534
4667
|
if (response.ok)
|
|
4535
4668
|
return;
|
|
4536
|
-
|
|
4669
|
+
const retryAfterMs = parseRetryAfterMs(response.headers?.get("retry-after"), now());
|
|
4670
|
+
if (attempt < config.maxAttempts && isRetryableUploadStatus(response.status)) {
|
|
4671
|
+
const delay = directUploadRetryDelayMs(config, attempt, random, retryAfterMs);
|
|
4672
|
+
if (!withinDirectUploadRetryBudget(config, startedAt, delay, now)) {
|
|
4673
|
+
const detail2 = await response.text().catch(() => "");
|
|
4674
|
+
throw directUploadResponseError(uploadUrl, response.status, detail2, attempt);
|
|
4675
|
+
}
|
|
4537
4676
|
await response.text().catch(() => "");
|
|
4677
|
+
await sleep5(delay, init.signal ?? void 0);
|
|
4538
4678
|
continue;
|
|
4539
4679
|
}
|
|
4540
4680
|
const detail = await response.text().catch(() => "");
|
|
4541
4681
|
throw directUploadResponseError(uploadUrl, response.status, detail, attempt);
|
|
4542
4682
|
}
|
|
4543
4683
|
}
|
|
4684
|
+
function resolveAssetUploadRetryConfig(options = {}) {
|
|
4685
|
+
const maxAttempts = Math.max(1, Math.floor(options.maxAttempts ?? DIRECT_UPLOAD_MAX_ATTEMPTS));
|
|
4686
|
+
const initialDelayMs = Math.max(0, options.initialDelayMs ?? DIRECT_UPLOAD_INITIAL_DELAY_MS);
|
|
4687
|
+
const maxDelayMs = Math.max(initialDelayMs, options.maxDelayMs ?? DIRECT_UPLOAD_MAX_DELAY_MS);
|
|
4688
|
+
const maxElapsedMs = Math.max(0, options.maxElapsedMs ?? DIRECT_UPLOAD_MAX_ELAPSED_MS);
|
|
4689
|
+
return { maxAttempts, initialDelayMs, maxDelayMs, maxElapsedMs };
|
|
4690
|
+
}
|
|
4691
|
+
function directUploadRetryDelayMs(config, attempt, random, retryAfterMs) {
|
|
4692
|
+
return computeRetryDelayMs(config, attempt, random, retryAfterMs);
|
|
4693
|
+
}
|
|
4694
|
+
function withinDirectUploadRetryBudget(config, startedAt, delayMs, now) {
|
|
4695
|
+
return now() - startedAt + delayMs <= config.maxElapsedMs;
|
|
4696
|
+
}
|
|
4544
4697
|
function isRetryableUploadStatus(status) {
|
|
4545
4698
|
return status === 408 || status === 425 || status === 429 || status >= 500 && status <= 599;
|
|
4546
4699
|
}
|
|
@@ -4560,6 +4713,9 @@ function directUploadResponseError(uploadUrl, status, detail, attempts) {
|
|
|
4560
4713
|
const safeDetail = sanitizeUploadText(detail).slice(0, 500);
|
|
4561
4714
|
return new Error(`uploadAsset: direct upload PUT failed for ${safeUrl} with status ${status}` + (attempts > 1 ? ` after ${attemptsLabel(attempts)}` : "") + (safeDetail ? `: ${safeDetail}` : ""));
|
|
4562
4715
|
}
|
|
4716
|
+
function sanitizeUploadText(text) {
|
|
4717
|
+
return text.replace(/https?:\/\/[^\s<>"'`]+/g, (raw) => redactUrlPreservingTrailingPunctuation(raw)).replace(/\b(?:X-Amz-(?:Algorithm|Credential|Date|Expires|Security-Token|Signature|SignedHeaders)|AWSAccessKeyId|Signature|Credential|Security-Token|AccessKeyId|SecretAccessKey|SessionToken)=([^&\s<>"'`]+)/gi, "[redacted]").replace(/\bAKIA[0-9A-Z]{8,}\b/g, "[redacted]");
|
|
4718
|
+
}
|
|
4563
4719
|
function attemptsLabel(attempts) {
|
|
4564
4720
|
return attempts === 1 ? "1 attempt" : `${attempts} attempts`;
|
|
4565
4721
|
}
|
|
@@ -4579,14 +4735,75 @@ function stringProperty2(value, key) {
|
|
|
4579
4735
|
const prop = value[key];
|
|
4580
4736
|
return typeof prop === "string" && prop.length > 0 ? prop : void 0;
|
|
4581
4737
|
}
|
|
4582
|
-
function sanitizeUploadText(text) {
|
|
4583
|
-
return text.replace(/https?:\/\/[^\s<>"'`]+/g, (raw) => redactUrlPreservingTrailingPunctuation(raw)).replace(/\b(?:X-Amz-(?:Algorithm|Credential|Date|Expires|Security-Token|Signature|SignedHeaders)|AWSAccessKeyId|Signature|Credential|Security-Token|AccessKeyId|SecretAccessKey|SessionToken)=([^&\s<>"'`]+)/gi, "[redacted]").replace(/\bAKIA[0-9A-Z]{8,}\b/g, "[redacted]");
|
|
4584
|
-
}
|
|
4585
4738
|
function redactUrlPreservingTrailingPunctuation(raw) {
|
|
4586
4739
|
const trailing = raw.match(/[),.;:!?]+$/)?.[0] ?? "";
|
|
4587
4740
|
const candidate = trailing ? raw.slice(0, -trailing.length) : raw;
|
|
4588
4741
|
return `${redactUrl(candidate)}${trailing}`;
|
|
4589
4742
|
}
|
|
4743
|
+
|
|
4744
|
+
// ../contracts/dist/post-hook.js
|
|
4745
|
+
var DEFAULT_POST_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4746
|
+
|
|
4747
|
+
// ../contracts/dist/internal.js
|
|
4748
|
+
async function uploadAsset(args) {
|
|
4749
|
+
const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
|
|
4750
|
+
const actual = await computeSha256Hex(args.bytes);
|
|
4751
|
+
if (actual !== expected) {
|
|
4752
|
+
throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} but caller declared ${args.hash}. Aborting to avoid uploading corrupted data.`);
|
|
4753
|
+
}
|
|
4754
|
+
const contentHashHeader = `sha256:${actual}`;
|
|
4755
|
+
const presign = await args.http.request("/assets/presign", {
|
|
4756
|
+
method: "POST",
|
|
4757
|
+
headers: { "content-type": "application/json" },
|
|
4758
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
4759
|
+
});
|
|
4760
|
+
if (presign.exists) {
|
|
4761
|
+
const contentHash2 = presign.contentHash ?? contentHashHeader;
|
|
4762
|
+
return {
|
|
4763
|
+
assetId: presign.assetId ?? assetIdFromContentHash(contentHash2),
|
|
4764
|
+
contentHash: contentHash2,
|
|
4765
|
+
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
4766
|
+
exists: true
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4769
|
+
if (!presign.uploadUrl) {
|
|
4770
|
+
throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
|
|
4771
|
+
}
|
|
4772
|
+
const doFetch = args.fetch ?? globalThis.fetch;
|
|
4773
|
+
const putHeaders = {
|
|
4774
|
+
"content-type": args.contentType ?? "application/zip",
|
|
4775
|
+
...presign.requiredHeaders ?? {}
|
|
4776
|
+
};
|
|
4777
|
+
await putDirectUploadWithRetry(doFetch, presign.uploadUrl, {
|
|
4778
|
+
method: "PUT",
|
|
4779
|
+
headers: putHeaders,
|
|
4780
|
+
body: args.bytes
|
|
4781
|
+
}, args.retry);
|
|
4782
|
+
const fin = await args.http.request("/assets/finalize", {
|
|
4783
|
+
method: "POST",
|
|
4784
|
+
headers: { "content-type": "application/json" },
|
|
4785
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
4786
|
+
});
|
|
4787
|
+
const contentHash = fin.contentHash ?? presign.contentHash ?? contentHashHeader;
|
|
4788
|
+
return {
|
|
4789
|
+
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
4790
|
+
contentHash,
|
|
4791
|
+
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
4792
|
+
exists: false
|
|
4793
|
+
};
|
|
4794
|
+
}
|
|
4795
|
+
async function computeSha256Hex(bytes) {
|
|
4796
|
+
const subtle = globalThis.crypto?.subtle;
|
|
4797
|
+
if (!subtle) {
|
|
4798
|
+
throw new Error("uploadAsset: globalThis.crypto.subtle is not available; Bun, Node 18+, or a Web-Crypto-capable runtime is required");
|
|
4799
|
+
}
|
|
4800
|
+
const digest = await subtle.digest("SHA-256", bytes);
|
|
4801
|
+
return bufferToHex(digest);
|
|
4802
|
+
}
|
|
4803
|
+
function assetIdFromContentHash(contentHash) {
|
|
4804
|
+
const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
|
|
4805
|
+
return `asset_${hex}`;
|
|
4806
|
+
}
|
|
4590
4807
|
function bufferToHex(buffer) {
|
|
4591
4808
|
const view = new Uint8Array(buffer);
|
|
4592
4809
|
let out = "";
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cdab07311ba092bc586f1d536208b0ca762c2014ddfb8f4219beb21a6302b59b cli.mjs
|
package/dist/client.d.ts
CHANGED
|
@@ -555,6 +555,11 @@ export type OutputFileSelector = Output | OutputFileIdSelector | OutputFilePathS
|
|
|
555
555
|
export type OutputLinkSelector = string | OutputFileSelector | OutputQuery;
|
|
556
556
|
export interface OutputDownloadOptions {
|
|
557
557
|
readonly to?: string;
|
|
558
|
+
/**
|
|
559
|
+
* Per-attempt timeout for fetching and reading the selected output body.
|
|
560
|
+
* Defaults to 15_000ms; idempotent output downloads retry once on timeout.
|
|
561
|
+
*/
|
|
562
|
+
readonly timeoutMs?: number;
|
|
558
563
|
}
|
|
559
564
|
/**
|
|
560
565
|
* Workspace AgentsMd admin operations exposed under `client.agentsMd`.
|
package/dist/client.js
CHANGED
|
@@ -746,9 +746,10 @@ function isSessionEnvelopeTerminal(event) {
|
|
|
746
746
|
async function downloadSessionOutput(http, id, selector, options) {
|
|
747
747
|
// One selector-resolution path: the contracts `downloadOutput` lists-if-path
|
|
748
748
|
// then downloads, throwing with PUBLIC verb names — no duplicated resolver.
|
|
749
|
+
const transferOptions = options?.timeoutMs === undefined ? undefined : { timeoutMs: options.timeoutMs };
|
|
749
750
|
const bytes = selector === undefined
|
|
750
|
-
? await operations.downloadOutputs(http, id)
|
|
751
|
-
: (await operations.downloadOutput(http, id, selector)).bytes;
|
|
751
|
+
? await operations.downloadOutputs(http, id, transferOptions)
|
|
752
|
+
: (await operations.downloadOutput(http, id, selector, transferOptions)).bytes;
|
|
752
753
|
return writeOptionalFile(bytes, options?.to);
|
|
753
754
|
}
|
|
754
755
|
/**
|