@aexhq/sdk 0.40.7 → 0.40.9
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 +143 -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 +299 -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,146 @@ 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(), "download-open");
|
|
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("unknown", 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
|
+
phase;
|
|
3397
|
+
constructor(phase, timeoutMs) {
|
|
3398
|
+
super(`output transfer phase=${phase} timed out after ${timeoutMs}ms`);
|
|
3399
|
+
this.name = "OutputTransferTimeoutError";
|
|
3400
|
+
this.phase = phase;
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
async function withOutputTransferTimeout(promise, timeoutMs, abort, phase) {
|
|
3404
|
+
let timedOut = false;
|
|
3405
|
+
let timeout;
|
|
3406
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3407
|
+
timeout = setTimeout(() => {
|
|
3408
|
+
timedOut = true;
|
|
3409
|
+
reject(new OutputTransferTimeoutError(phase, timeoutMs));
|
|
3410
|
+
queueMicrotask(() => {
|
|
3411
|
+
try {
|
|
3412
|
+
abort();
|
|
3413
|
+
} catch {
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
}, timeoutMs);
|
|
3417
|
+
});
|
|
3418
|
+
try {
|
|
3419
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
3420
|
+
} catch (err2) {
|
|
3421
|
+
if (timedOut && isAbortLikeError(err2))
|
|
3422
|
+
throw new OutputTransferTimeoutError(phase, timeoutMs);
|
|
3423
|
+
throw err2;
|
|
3424
|
+
} finally {
|
|
3425
|
+
if (timeout !== void 0)
|
|
3426
|
+
clearTimeout(timeout);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
function isAbortLikeError(err2) {
|
|
3430
|
+
const name = err2?.name;
|
|
3431
|
+
return name === "AbortError";
|
|
3432
|
+
}
|
|
3433
|
+
async function readResponseBytes(response, timeoutMs) {
|
|
3434
|
+
const body = response.body;
|
|
3435
|
+
if (!body) {
|
|
3436
|
+
const buffer = await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => {
|
|
3437
|
+
}, "body-read");
|
|
3438
|
+
return new Uint8Array(buffer);
|
|
3439
|
+
}
|
|
3440
|
+
const reader = body.getReader();
|
|
3441
|
+
const chunks = [];
|
|
3442
|
+
try {
|
|
3443
|
+
while (true) {
|
|
3444
|
+
const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3445
|
+
void reader.cancel().catch(() => {
|
|
3446
|
+
});
|
|
3447
|
+
}, "body-read");
|
|
3448
|
+
if (done)
|
|
3449
|
+
break;
|
|
3450
|
+
if (value && value.byteLength > 0)
|
|
3451
|
+
chunks.push(value);
|
|
3452
|
+
}
|
|
3453
|
+
} finally {
|
|
3454
|
+
void reader.cancel().catch(() => {
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
return concatBytes(chunks);
|
|
3458
|
+
}
|
|
3459
|
+
async function readCappedText(response, maxBytes, timeoutMs) {
|
|
3341
3460
|
const declaredRaw = response.headers.get("content-length");
|
|
3342
3461
|
const declared = declaredRaw !== null && /^\d+$/.test(declaredRaw) ? Number(declaredRaw) : void 0;
|
|
3343
3462
|
const decoder = new TextDecoder("utf-8");
|
|
3344
3463
|
const body = response.body;
|
|
3345
3464
|
if (!body) {
|
|
3346
|
-
const buf = new Uint8Array(await response.arrayBuffer())
|
|
3465
|
+
const buf = new Uint8Array(await withOutputTransferTimeout(response.arrayBuffer(), timeoutMs, () => {
|
|
3466
|
+
}, "body-read"));
|
|
3347
3467
|
const total = declared ?? buf.byteLength;
|
|
3348
3468
|
return {
|
|
3349
3469
|
text: decoder.decode(buf.subarray(0, maxBytes)),
|
|
@@ -3357,7 +3477,10 @@ async function readCappedText(response, maxBytes) {
|
|
|
3357
3477
|
let sawMore = false;
|
|
3358
3478
|
try {
|
|
3359
3479
|
while (read < maxBytes) {
|
|
3360
|
-
const { done, value } = await reader.read()
|
|
3480
|
+
const { done, value } = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3481
|
+
void reader.cancel().catch(() => {
|
|
3482
|
+
});
|
|
3483
|
+
}, "body-read");
|
|
3361
3484
|
if (done)
|
|
3362
3485
|
break;
|
|
3363
3486
|
if (value && value.byteLength > 0) {
|
|
@@ -3366,12 +3489,15 @@ async function readCappedText(response, maxBytes) {
|
|
|
3366
3489
|
}
|
|
3367
3490
|
}
|
|
3368
3491
|
if (read >= maxBytes) {
|
|
3369
|
-
const next = await reader.read()
|
|
3492
|
+
const next = await withOutputTransferTimeout(reader.read(), timeoutMs, () => {
|
|
3493
|
+
void reader.cancel().catch(() => {
|
|
3494
|
+
});
|
|
3495
|
+
}, "body-read");
|
|
3370
3496
|
if (!next.done && next.value && next.value.byteLength > 0)
|
|
3371
3497
|
sawMore = true;
|
|
3372
3498
|
}
|
|
3373
3499
|
} finally {
|
|
3374
|
-
|
|
3500
|
+
void reader.cancel().catch(() => {
|
|
3375
3501
|
});
|
|
3376
3502
|
}
|
|
3377
3503
|
const merged = concatBytes(chunks).subarray(0, maxBytes);
|
|
@@ -3443,17 +3569,17 @@ async function getBillingLedger(http, query) {
|
|
|
3443
3569
|
async function getWebhookSigningSecret(http) {
|
|
3444
3570
|
return http.request("/api/webhook/signing-secret", { method: "POST" });
|
|
3445
3571
|
}
|
|
3446
|
-
async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2) {
|
|
3572
|
+
async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2, timeoutMs = OUTPUT_FILE_TRANSFER_DEFAULT_TIMEOUT_MS) {
|
|
3447
3573
|
const entries = [];
|
|
3448
3574
|
const captured = [];
|
|
3449
3575
|
const errors = [];
|
|
3450
3576
|
for (const item of items) {
|
|
3451
3577
|
const rel = item.filename ?? item.id;
|
|
3452
3578
|
try {
|
|
3453
|
-
const
|
|
3579
|
+
const path = `/api/runs/${encodeURIComponent(runId)}/${namespace2}/${encodeURIComponent(item.id)}/download`;
|
|
3454
3580
|
entries.push({
|
|
3455
3581
|
path: `${zipPrefix}${rel}`,
|
|
3456
|
-
bytes:
|
|
3582
|
+
bytes: await downloadOutputBytesWithRetry(http, path, timeoutMs),
|
|
3457
3583
|
...item.contentType !== void 0 ? { contentType: item.contentType } : {},
|
|
3458
3584
|
customerContent: true
|
|
3459
3585
|
});
|
|
@@ -3679,9 +3805,10 @@ async function download(http, runId) {
|
|
|
3679
3805
|
jsonEntry("manifest.json", manifest)
|
|
3680
3806
|
]);
|
|
3681
3807
|
}
|
|
3682
|
-
async function downloadOutputs(http, runId) {
|
|
3808
|
+
async function downloadOutputs(http, runId, options) {
|
|
3683
3809
|
const outputs = await listOutputs(http, runId);
|
|
3684
|
-
const
|
|
3810
|
+
const timeoutMs = normalizeOutputTransferTimeoutMs(options?.timeoutMs);
|
|
3811
|
+
const { entries, captured, errors } = await collectArtifactBytes(http, runId, outputs, "", "outputs", timeoutMs);
|
|
3685
3812
|
return zipEntries([
|
|
3686
3813
|
...entries,
|
|
3687
3814
|
jsonEntry("manifest.json", { runId, namespace: "outputs", outputs: captured, errors })
|
|
@@ -4456,91 +4583,119 @@ function renderVerbHelp(spec) {
|
|
|
4456
4583
|
// dist/host/run-cmd.js
|
|
4457
4584
|
import { resolve as resolvePath } from "node:path";
|
|
4458
4585
|
|
|
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
|
-
};
|
|
4586
|
+
// ../contracts/dist/retry-core.js
|
|
4587
|
+
var RETRYABLE_HTTP_STATUS = [429, 500, 502, 503, 504, 529];
|
|
4588
|
+
var RATE_LIMIT_HTTP_STATUS = [429, 503, 529];
|
|
4589
|
+
var RETRYABLE_HTTP_STATUS_SET = new Set(RETRYABLE_HTTP_STATUS);
|
|
4590
|
+
var RATE_LIMIT_HTTP_STATUS_SET = new Set(RATE_LIMIT_HTTP_STATUS);
|
|
4591
|
+
function parseRetryAfterMs(headerValue, now = Date.now()) {
|
|
4592
|
+
if (headerValue === null || headerValue === void 0)
|
|
4593
|
+
return void 0;
|
|
4594
|
+
const trimmed = headerValue.trim();
|
|
4595
|
+
if (trimmed.length === 0)
|
|
4596
|
+
return void 0;
|
|
4597
|
+
if (/^\d+$/.test(trimmed)) {
|
|
4598
|
+
return Number(trimmed) * 1e3;
|
|
4484
4599
|
}
|
|
4485
|
-
|
|
4486
|
-
|
|
4600
|
+
const dateMs = Date.parse(trimmed);
|
|
4601
|
+
if (!Number.isNaN(dateMs)) {
|
|
4602
|
+
return Math.max(0, dateMs - now);
|
|
4487
4603
|
}
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4604
|
+
return void 0;
|
|
4605
|
+
}
|
|
4606
|
+
function computeRetryBackoffDelayMs(config, attemptNumber, random) {
|
|
4607
|
+
const exponent = Math.max(0, attemptNumber - 1);
|
|
4608
|
+
const nominal = Math.min(config.maxDelayMs, config.initialDelayMs * 2 ** exponent);
|
|
4609
|
+
return Math.round(random() * nominal);
|
|
4610
|
+
}
|
|
4611
|
+
function computeRetryDelayMs(config, attemptNumber, random, retryAfterMs) {
|
|
4612
|
+
const backoff = computeRetryBackoffDelayMs(config, attemptNumber, random);
|
|
4613
|
+
return retryAfterMs === void 0 ? backoff : Math.max(retryAfterMs, backoff);
|
|
4614
|
+
}
|
|
4615
|
+
function abortableSleep(ms, signal) {
|
|
4616
|
+
return new Promise((resolve, reject) => {
|
|
4617
|
+
if (signal?.aborted) {
|
|
4618
|
+
reject(abortReason(signal));
|
|
4619
|
+
return;
|
|
4620
|
+
}
|
|
4621
|
+
const timer = setTimeout(() => {
|
|
4622
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4623
|
+
resolve();
|
|
4624
|
+
}, ms);
|
|
4625
|
+
const onAbort = () => {
|
|
4626
|
+
clearTimeout(timer);
|
|
4627
|
+
reject(signal ? abortReason(signal) : makeAbortError());
|
|
4628
|
+
};
|
|
4629
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4502
4630
|
});
|
|
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
4631
|
}
|
|
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);
|
|
4632
|
+
function abortReason(signal) {
|
|
4633
|
+
return signal.reason instanceof Error ? signal.reason : makeAbortError();
|
|
4518
4634
|
}
|
|
4519
|
-
function
|
|
4520
|
-
|
|
4521
|
-
|
|
4635
|
+
function makeAbortError() {
|
|
4636
|
+
if (typeof DOMException !== "undefined")
|
|
4637
|
+
return new DOMException("Aborted", "AbortError");
|
|
4638
|
+
const err2 = new Error("Aborted");
|
|
4639
|
+
err2.name = "AbortError";
|
|
4640
|
+
return err2;
|
|
4522
4641
|
}
|
|
4523
|
-
|
|
4524
|
-
|
|
4642
|
+
|
|
4643
|
+
// ../contracts/dist/asset-upload-helper.js
|
|
4644
|
+
var DIRECT_UPLOAD_MAX_ATTEMPTS = 3;
|
|
4645
|
+
var DIRECT_UPLOAD_INITIAL_DELAY_MS = 500;
|
|
4646
|
+
var DIRECT_UPLOAD_MAX_DELAY_MS = 5e3;
|
|
4647
|
+
var DIRECT_UPLOAD_MAX_ELAPSED_MS = 3e4;
|
|
4648
|
+
async function putDirectUploadWithRetry(fetchImpl, uploadUrl, init, options = {}) {
|
|
4649
|
+
const config = resolveAssetUploadRetryConfig(options);
|
|
4650
|
+
const sleep5 = options.sleep ?? abortableSleep;
|
|
4651
|
+
const random = options.random ?? Math.random;
|
|
4652
|
+
const now = options.now ?? Date.now;
|
|
4653
|
+
const startedAt = now();
|
|
4654
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
4525
4655
|
let response;
|
|
4526
4656
|
try {
|
|
4527
4657
|
response = await fetchImpl(uploadUrl, init);
|
|
4528
4658
|
} catch (err2) {
|
|
4529
|
-
if (attempt <
|
|
4659
|
+
if (attempt < config.maxAttempts && isRetryableUploadError(err2)) {
|
|
4660
|
+
const delay = directUploadRetryDelayMs(config, attempt, random, void 0);
|
|
4661
|
+
if (!withinDirectUploadRetryBudget(config, startedAt, delay, now)) {
|
|
4662
|
+
throw directUploadNetworkError(uploadUrl, err2, attempt);
|
|
4663
|
+
}
|
|
4664
|
+
await sleep5(delay, init.signal ?? void 0);
|
|
4530
4665
|
continue;
|
|
4531
4666
|
}
|
|
4532
4667
|
throw directUploadNetworkError(uploadUrl, err2, attempt);
|
|
4533
4668
|
}
|
|
4534
4669
|
if (response.ok)
|
|
4535
4670
|
return;
|
|
4536
|
-
|
|
4671
|
+
const retryAfterMs = parseRetryAfterMs(response.headers?.get("retry-after"), now());
|
|
4672
|
+
if (attempt < config.maxAttempts && isRetryableUploadStatus(response.status)) {
|
|
4673
|
+
const delay = directUploadRetryDelayMs(config, attempt, random, retryAfterMs);
|
|
4674
|
+
if (!withinDirectUploadRetryBudget(config, startedAt, delay, now)) {
|
|
4675
|
+
const detail2 = await response.text().catch(() => "");
|
|
4676
|
+
throw directUploadResponseError(uploadUrl, response.status, detail2, attempt);
|
|
4677
|
+
}
|
|
4537
4678
|
await response.text().catch(() => "");
|
|
4679
|
+
await sleep5(delay, init.signal ?? void 0);
|
|
4538
4680
|
continue;
|
|
4539
4681
|
}
|
|
4540
4682
|
const detail = await response.text().catch(() => "");
|
|
4541
4683
|
throw directUploadResponseError(uploadUrl, response.status, detail, attempt);
|
|
4542
4684
|
}
|
|
4543
4685
|
}
|
|
4686
|
+
function resolveAssetUploadRetryConfig(options = {}) {
|
|
4687
|
+
const maxAttempts = Math.max(1, Math.floor(options.maxAttempts ?? DIRECT_UPLOAD_MAX_ATTEMPTS));
|
|
4688
|
+
const initialDelayMs = Math.max(0, options.initialDelayMs ?? DIRECT_UPLOAD_INITIAL_DELAY_MS);
|
|
4689
|
+
const maxDelayMs = Math.max(initialDelayMs, options.maxDelayMs ?? DIRECT_UPLOAD_MAX_DELAY_MS);
|
|
4690
|
+
const maxElapsedMs = Math.max(0, options.maxElapsedMs ?? DIRECT_UPLOAD_MAX_ELAPSED_MS);
|
|
4691
|
+
return { maxAttempts, initialDelayMs, maxDelayMs, maxElapsedMs };
|
|
4692
|
+
}
|
|
4693
|
+
function directUploadRetryDelayMs(config, attempt, random, retryAfterMs) {
|
|
4694
|
+
return computeRetryDelayMs(config, attempt, random, retryAfterMs);
|
|
4695
|
+
}
|
|
4696
|
+
function withinDirectUploadRetryBudget(config, startedAt, delayMs, now) {
|
|
4697
|
+
return now() - startedAt + delayMs <= config.maxElapsedMs;
|
|
4698
|
+
}
|
|
4544
4699
|
function isRetryableUploadStatus(status) {
|
|
4545
4700
|
return status === 408 || status === 425 || status === 429 || status >= 500 && status <= 599;
|
|
4546
4701
|
}
|
|
@@ -4560,6 +4715,9 @@ function directUploadResponseError(uploadUrl, status, detail, attempts) {
|
|
|
4560
4715
|
const safeDetail = sanitizeUploadText(detail).slice(0, 500);
|
|
4561
4716
|
return new Error(`uploadAsset: direct upload PUT failed for ${safeUrl} with status ${status}` + (attempts > 1 ? ` after ${attemptsLabel(attempts)}` : "") + (safeDetail ? `: ${safeDetail}` : ""));
|
|
4562
4717
|
}
|
|
4718
|
+
function sanitizeUploadText(text) {
|
|
4719
|
+
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]");
|
|
4720
|
+
}
|
|
4563
4721
|
function attemptsLabel(attempts) {
|
|
4564
4722
|
return attempts === 1 ? "1 attempt" : `${attempts} attempts`;
|
|
4565
4723
|
}
|
|
@@ -4579,14 +4737,75 @@ function stringProperty2(value, key) {
|
|
|
4579
4737
|
const prop = value[key];
|
|
4580
4738
|
return typeof prop === "string" && prop.length > 0 ? prop : void 0;
|
|
4581
4739
|
}
|
|
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
4740
|
function redactUrlPreservingTrailingPunctuation(raw) {
|
|
4586
4741
|
const trailing = raw.match(/[),.;:!?]+$/)?.[0] ?? "";
|
|
4587
4742
|
const candidate = trailing ? raw.slice(0, -trailing.length) : raw;
|
|
4588
4743
|
return `${redactUrl(candidate)}${trailing}`;
|
|
4589
4744
|
}
|
|
4745
|
+
|
|
4746
|
+
// ../contracts/dist/post-hook.js
|
|
4747
|
+
var DEFAULT_POST_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4748
|
+
|
|
4749
|
+
// ../contracts/dist/internal.js
|
|
4750
|
+
async function uploadAsset(args) {
|
|
4751
|
+
const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
|
|
4752
|
+
const actual = await computeSha256Hex(args.bytes);
|
|
4753
|
+
if (actual !== expected) {
|
|
4754
|
+
throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} but caller declared ${args.hash}. Aborting to avoid uploading corrupted data.`);
|
|
4755
|
+
}
|
|
4756
|
+
const contentHashHeader = `sha256:${actual}`;
|
|
4757
|
+
const presign = await args.http.request("/assets/presign", {
|
|
4758
|
+
method: "POST",
|
|
4759
|
+
headers: { "content-type": "application/json" },
|
|
4760
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
4761
|
+
});
|
|
4762
|
+
if (presign.exists) {
|
|
4763
|
+
const contentHash2 = presign.contentHash ?? contentHashHeader;
|
|
4764
|
+
return {
|
|
4765
|
+
assetId: presign.assetId ?? assetIdFromContentHash(contentHash2),
|
|
4766
|
+
contentHash: contentHash2,
|
|
4767
|
+
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
4768
|
+
exists: true
|
|
4769
|
+
};
|
|
4770
|
+
}
|
|
4771
|
+
if (!presign.uploadUrl) {
|
|
4772
|
+
throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
|
|
4773
|
+
}
|
|
4774
|
+
const doFetch = args.fetch ?? globalThis.fetch;
|
|
4775
|
+
const putHeaders = {
|
|
4776
|
+
"content-type": args.contentType ?? "application/zip",
|
|
4777
|
+
...presign.requiredHeaders ?? {}
|
|
4778
|
+
};
|
|
4779
|
+
await putDirectUploadWithRetry(doFetch, presign.uploadUrl, {
|
|
4780
|
+
method: "PUT",
|
|
4781
|
+
headers: putHeaders,
|
|
4782
|
+
body: args.bytes
|
|
4783
|
+
}, args.retry);
|
|
4784
|
+
const fin = await args.http.request("/assets/finalize", {
|
|
4785
|
+
method: "POST",
|
|
4786
|
+
headers: { "content-type": "application/json" },
|
|
4787
|
+
body: JSON.stringify({ hash: contentHashHeader, sizeBytes: args.bytes.byteLength })
|
|
4788
|
+
});
|
|
4789
|
+
const contentHash = fin.contentHash ?? presign.contentHash ?? contentHashHeader;
|
|
4790
|
+
return {
|
|
4791
|
+
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(contentHash),
|
|
4792
|
+
contentHash,
|
|
4793
|
+
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
4794
|
+
exists: false
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
async function computeSha256Hex(bytes) {
|
|
4798
|
+
const subtle = globalThis.crypto?.subtle;
|
|
4799
|
+
if (!subtle) {
|
|
4800
|
+
throw new Error("uploadAsset: globalThis.crypto.subtle is not available; Bun, Node 18+, or a Web-Crypto-capable runtime is required");
|
|
4801
|
+
}
|
|
4802
|
+
const digest = await subtle.digest("SHA-256", bytes);
|
|
4803
|
+
return bufferToHex(digest);
|
|
4804
|
+
}
|
|
4805
|
+
function assetIdFromContentHash(contentHash) {
|
|
4806
|
+
const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
|
|
4807
|
+
return `asset_${hex}`;
|
|
4808
|
+
}
|
|
4590
4809
|
function bufferToHex(buffer) {
|
|
4591
4810
|
const view = new Uint8Array(buffer);
|
|
4592
4811
|
let out = "";
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
eb3b73297a9e0ef272eefa66f60dc4a13da741cb24a809c7558de2ee5d4d3555 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
|
/**
|