@aexhq/sdk 0.37.2 → 0.37.4
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/event-stream-client.d.ts +11 -0
- package/dist/_contracts/event-stream-client.js +45 -5
- package/dist/_contracts/http.js +79 -7
- package/dist/_contracts/operations.js +42 -3
- package/dist/_contracts/provider-support.d.ts +2 -2
- package/dist/_contracts/provider-support.js +1 -1
- package/dist/_contracts/run-unit.d.ts +1 -1
- package/dist/_contracts/run-unit.js +12 -9
- package/dist/_contracts/runtime-types.d.ts +20 -8
- package/dist/_contracts/sdk-errors.d.ts +44 -2
- package/dist/_contracts/sdk-errors.js +104 -2
- package/dist/_contracts/sdk-secrets.js +18 -1
- package/dist/asset-upload.js +85 -17
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +275 -35
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +17 -1
- package/dist/client.js +145 -15
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/retry.js +66 -6
- package/dist/retry.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +9 -0
- package/docs/concepts/composition.md +1 -1
- package/docs/concepts/subagents.md +6 -3
- package/docs/defaults.md +2 -2
- package/docs/errors.md +8 -4
- package/docs/limits-and-quotas.md +1 -1
- package/docs/provider-runtime-capabilities.md +3 -3
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -22,7 +22,7 @@ var COMMON_EVIDENCE = [
|
|
|
22
22
|
var ANTHROPIC_LIVE_USER_EVIDENCE = [
|
|
23
23
|
{
|
|
24
24
|
label: "Installed-SDK Anthropic live user test",
|
|
25
|
-
href: "../../../apps/user-tests/test/live/live-sdk-anthropic-managed.test.ts"
|
|
25
|
+
href: "../../../apps/user-tests/test/live/providers/live-sdk-anthropic-managed.test.ts"
|
|
26
26
|
}
|
|
27
27
|
];
|
|
28
28
|
var DEEPSEEK_LIVE_USER_EVIDENCE = [
|
|
@@ -652,6 +652,7 @@ var isTerminalType = (e) => e.type === "RUN_FINISHED" || e.type === "RUN_ERROR"
|
|
|
652
652
|
var COORDINATOR_PING = "aex:ping";
|
|
653
653
|
var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
|
|
654
654
|
var DEFAULT_PING_INTERVAL_MS = 15e3;
|
|
655
|
+
var DEFAULT_EVENT_QUIET_RECHECK_MS = 9e4;
|
|
655
656
|
async function* streamCoordinatorEvents(opts) {
|
|
656
657
|
const makeWs = opts.webSocketFactory ?? ((url) => new WebSocket(url));
|
|
657
658
|
const isTerminal = opts.isTerminal ?? isTerminalType;
|
|
@@ -659,6 +660,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
659
660
|
const maxReconnects = opts.maxReconnects ?? Number.POSITIVE_INFINITY;
|
|
660
661
|
const idleTimeoutMs = opts.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
661
662
|
const pingIntervalMs = opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
663
|
+
const eventQuietRecheckMs = opts.eventQuietRecheckMs ?? DEFAULT_EVENT_QUIET_RECHECK_MS;
|
|
662
664
|
let cursor = (opts.from ?? 0) - 1;
|
|
663
665
|
let attempts = 0;
|
|
664
666
|
let done = false;
|
|
@@ -681,6 +683,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
681
683
|
};
|
|
682
684
|
let idleTimer = null;
|
|
683
685
|
let pingTimer = null;
|
|
686
|
+
let quietTimer = null;
|
|
684
687
|
const stopTimers = () => {
|
|
685
688
|
if (idleTimer !== null) {
|
|
686
689
|
clearTimeout(idleTimer);
|
|
@@ -690,6 +693,10 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
690
693
|
clearInterval(pingTimer);
|
|
691
694
|
pingTimer = null;
|
|
692
695
|
}
|
|
696
|
+
if (quietTimer !== null) {
|
|
697
|
+
clearTimeout(quietTimer);
|
|
698
|
+
quietTimer = null;
|
|
699
|
+
}
|
|
693
700
|
};
|
|
694
701
|
const armIdle = () => {
|
|
695
702
|
if (idleTimeoutMs <= 0)
|
|
@@ -706,6 +713,21 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
706
713
|
wake();
|
|
707
714
|
}, idleTimeoutMs);
|
|
708
715
|
};
|
|
716
|
+
const armQuiet = () => {
|
|
717
|
+
if (eventQuietRecheckMs <= 0)
|
|
718
|
+
return;
|
|
719
|
+
if (quietTimer !== null)
|
|
720
|
+
clearTimeout(quietTimer);
|
|
721
|
+
quietTimer = setTimeout(() => {
|
|
722
|
+
quietTimer = null;
|
|
723
|
+
if (closed)
|
|
724
|
+
return;
|
|
725
|
+
closed = true;
|
|
726
|
+
disconnectReason = "quiet_recheck";
|
|
727
|
+
closeQuietly(ws);
|
|
728
|
+
wake();
|
|
729
|
+
}, eventQuietRecheckMs);
|
|
730
|
+
};
|
|
709
731
|
ws.addEventListener("open", () => {
|
|
710
732
|
armIdle();
|
|
711
733
|
if (pingIntervalMs > 0 && typeof ws.send === "function") {
|
|
@@ -724,9 +746,12 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
724
746
|
return;
|
|
725
747
|
try {
|
|
726
748
|
const evt = JSON.parse(data);
|
|
727
|
-
if (typeof evt.sequence === "number"
|
|
728
|
-
|
|
729
|
-
|
|
749
|
+
if (typeof evt.sequence === "number") {
|
|
750
|
+
armQuiet();
|
|
751
|
+
if (evt.sequence > cursor) {
|
|
752
|
+
queue.push(evt);
|
|
753
|
+
wake();
|
|
754
|
+
}
|
|
730
755
|
}
|
|
731
756
|
} catch {
|
|
732
757
|
}
|
|
@@ -756,6 +781,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
756
781
|
};
|
|
757
782
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
758
783
|
armIdle();
|
|
784
|
+
armQuiet();
|
|
759
785
|
try {
|
|
760
786
|
while (true) {
|
|
761
787
|
while (queue.length > 0) {
|
|
@@ -778,6 +804,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
778
804
|
} finally {
|
|
779
805
|
stopTimers();
|
|
780
806
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
807
|
+
closeQuietly(ws);
|
|
781
808
|
}
|
|
782
809
|
if (done || opts.signal?.aborted)
|
|
783
810
|
return;
|
|
@@ -786,7 +813,9 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
786
813
|
console.warn(`[aex] event stream gave up after ${maxReconnects} reconnect attempt(s) (last: ${disconnectReason || "unknown"}); ended before a terminal event at seq ${cursor + 1}`);
|
|
787
814
|
return;
|
|
788
815
|
}
|
|
789
|
-
|
|
816
|
+
if (disconnectReason !== "quiet_recheck") {
|
|
817
|
+
console.warn(`[aex] event stream disconnected (${disconnectReason || "unknown"}); reconnecting attempt ${attempts} from seq ${cursor + 1}`);
|
|
818
|
+
}
|
|
790
819
|
await sleep(reconnectDelayMs, opts.signal);
|
|
791
820
|
}
|
|
792
821
|
}
|
|
@@ -807,17 +836,17 @@ function sleep(ms, signal) {
|
|
|
807
836
|
}
|
|
808
837
|
|
|
809
838
|
// ../contracts/dist/run-unit.js
|
|
810
|
-
function parseRunUnitSubmission(input) {
|
|
839
|
+
function parseRunUnitSubmission(input, fallbackModel) {
|
|
811
840
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
812
|
-
return fallbackFlat();
|
|
841
|
+
return fallbackFlat(fallbackModel);
|
|
813
842
|
}
|
|
814
843
|
const value = input;
|
|
815
844
|
if (value.kind === "submission") {
|
|
816
|
-
return parseFlatProjection(value);
|
|
845
|
+
return parseFlatProjection(value, fallbackModel);
|
|
817
846
|
}
|
|
818
|
-
return fallbackFlat();
|
|
847
|
+
return fallbackFlat(fallbackModel);
|
|
819
848
|
}
|
|
820
|
-
function parseFlatProjection(value) {
|
|
849
|
+
function parseFlatProjection(value, fallbackModel) {
|
|
821
850
|
const submissionRaw = isRecord(value.submission) ? value.submission : {};
|
|
822
851
|
const outputsRaw = isRecord(submissionRaw.outputs) ? submissionRaw.outputs : {};
|
|
823
852
|
const allowedDirs = toOptionalStringArray(outputsRaw.allowedDirs);
|
|
@@ -827,7 +856,7 @@ function parseFlatProjection(value) {
|
|
|
827
856
|
const maxTotalBytes = toOptionalPositiveInteger(outputsRaw.maxTotalBytes);
|
|
828
857
|
const maxFiles = toOptionalPositiveInteger(outputsRaw.maxFiles);
|
|
829
858
|
const submission = {
|
|
830
|
-
model: coerceRunUnitModel(submissionRaw.model),
|
|
859
|
+
model: coerceRunUnitModel(submissionRaw.model ?? fallbackModel),
|
|
831
860
|
...typeof submissionRaw.system === "string" ? { system: submissionRaw.system } : {},
|
|
832
861
|
prompt: toStringArray(submissionRaw.prompt),
|
|
833
862
|
agentsMd: [],
|
|
@@ -859,11 +888,11 @@ function parseSecurityProfile(value) {
|
|
|
859
888
|
function toOptionalPositiveInteger(value) {
|
|
860
889
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
861
890
|
}
|
|
862
|
-
function fallbackFlat() {
|
|
891
|
+
function fallbackFlat(fallbackModel) {
|
|
863
892
|
return {
|
|
864
893
|
kind: "submission",
|
|
865
894
|
submission: {
|
|
866
|
-
model:
|
|
895
|
+
model: coerceRunUnitModel(fallbackModel),
|
|
867
896
|
prompt: [],
|
|
868
897
|
agentsMd: [],
|
|
869
898
|
files: [],
|
|
@@ -892,7 +921,10 @@ function normalizeRunUnit(raw) {
|
|
|
892
921
|
...str3(r.terminalAt) ? { terminalAt: r.terminalAt } : {},
|
|
893
922
|
...str3(r.deletedAt) ? { deletedAt: r.deletedAt } : {},
|
|
894
923
|
attemptCount: typeof r.attemptCount === "number" ? r.attemptCount : Array.isArray(r.attempts) ? r.attempts.length : 0,
|
|
895
|
-
|
|
924
|
+
// Plane responses that project a flat record (no `submission` snapshot)
|
|
925
|
+
// still carry the run's `model` at the top level — prefer it over the
|
|
926
|
+
// static fallback so `unit()` never claims a model the run did not use.
|
|
927
|
+
submission: parseRunUnitSubmission(r.submission, r.model),
|
|
896
928
|
...isRecord(r.capsSnapshot) ? { capsSnapshot: r.capsSnapshot } : {},
|
|
897
929
|
attempts: arr(r.attempts),
|
|
898
930
|
events: {
|
|
@@ -1363,6 +1395,11 @@ var HIGH_ENTROPY_CANDIDATE = /[A-Za-z0-9+/=-]{24,}/g;
|
|
|
1363
1395
|
var ENTROPY_BITS_PER_CHAR = 3;
|
|
1364
1396
|
var MIN_CHAR_CLASSES = 2;
|
|
1365
1397
|
var HIGH_ENTROPY_NO_DIGIT_MIN_LEN = 40;
|
|
1398
|
+
function isCanonicalRunIdHex(input, matchStart, match) {
|
|
1399
|
+
if (!/^[0-9a-f]{32}$/.test(match))
|
|
1400
|
+
return false;
|
|
1401
|
+
return input.slice(Math.max(0, matchStart - 4), matchStart) === "run_";
|
|
1402
|
+
}
|
|
1366
1403
|
function redactSecrets(value) {
|
|
1367
1404
|
if (typeof value === "string") {
|
|
1368
1405
|
return redactString(value);
|
|
@@ -1395,7 +1432,7 @@ function redactString(input, known = []) {
|
|
|
1395
1432
|
// prefix; the rest replace the whole match.
|
|
1396
1433
|
typeof captured === "string" ? `${captured} ${REDACTED}` : REDACTED
|
|
1397
1434
|
)), out);
|
|
1398
|
-
return out.replace(HIGH_ENTROPY_CANDIDATE, (match) => looksHighEntropySecret(match) ? REDACTED : match);
|
|
1435
|
+
return out.replace(HIGH_ENTROPY_CANDIDATE, (match, offset, whole) => !isCanonicalRunIdHex(whole, offset, match) && looksHighEntropySecret(match) ? REDACTED : match);
|
|
1399
1436
|
}
|
|
1400
1437
|
function isSecretKey(key) {
|
|
1401
1438
|
return /(?:api[_-]?key|authorization|token|secret|password|credential)/i.test(key);
|
|
@@ -1437,8 +1474,8 @@ function shannonEntropyBits(value) {
|
|
|
1437
1474
|
var AexError = class extends Error {
|
|
1438
1475
|
code;
|
|
1439
1476
|
details;
|
|
1440
|
-
constructor(code, message, details) {
|
|
1441
|
-
super(redactSecrets(message));
|
|
1477
|
+
constructor(code, message, details, options) {
|
|
1478
|
+
super(redactSecrets(message), options?.cause === void 0 ? void 0 : { cause: options.cause });
|
|
1442
1479
|
this.name = this.constructor.name;
|
|
1443
1480
|
this.code = code;
|
|
1444
1481
|
this.details = details === void 0 ? void 0 : redactSecrets(details);
|
|
@@ -1458,6 +1495,85 @@ var AexApiError = class extends AexError {
|
|
|
1458
1495
|
this.body = redactSecrets(body);
|
|
1459
1496
|
}
|
|
1460
1497
|
};
|
|
1498
|
+
var AexNetworkError = class extends AexError {
|
|
1499
|
+
method;
|
|
1500
|
+
/** Request host — never carries credentials or the query string. */
|
|
1501
|
+
host;
|
|
1502
|
+
path;
|
|
1503
|
+
/** Transport failure code (e.g. `ECONNREFUSED`), when detectable. */
|
|
1504
|
+
causeCode;
|
|
1505
|
+
/** Attempts made when a retry layer exhausted its budget; `1` otherwise. */
|
|
1506
|
+
attempts;
|
|
1507
|
+
constructor(args) {
|
|
1508
|
+
const causeCode = extractErrorCode(args.cause);
|
|
1509
|
+
super("NETWORK_ERROR", networkErrorMessage(args, causeCode), { method: args.method, host: args.host, path: args.path, ...causeCode ? { code: causeCode } : {} }, { cause: args.cause });
|
|
1510
|
+
this.method = args.method;
|
|
1511
|
+
this.host = args.host;
|
|
1512
|
+
this.path = args.path;
|
|
1513
|
+
this.causeCode = causeCode;
|
|
1514
|
+
this.attempts = args.attempts ?? 1;
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
function networkErrorMessage(args, causeCode) {
|
|
1518
|
+
const target = args.host ? `${args.host}${args.path}` : "request";
|
|
1519
|
+
const detail = shortCauseMessage(args.cause, causeCode);
|
|
1520
|
+
const suffix = args.attempts === void 0 ? "" : ` after ${args.attempts} attempt${args.attempts === 1 ? "" : "s"} over ${args.elapsedMs ?? 0}ms`;
|
|
1521
|
+
let message = `${args.method} ${target} failed`;
|
|
1522
|
+
if (causeCode)
|
|
1523
|
+
message += `: ${causeCode}`;
|
|
1524
|
+
if (detail)
|
|
1525
|
+
message += causeCode ? ` (${detail})` : `: ${detail}`;
|
|
1526
|
+
return message + suffix;
|
|
1527
|
+
}
|
|
1528
|
+
function shortCauseMessage(cause, causeCode) {
|
|
1529
|
+
const nested = cause instanceof Error && cause.cause instanceof Error ? cause.cause : cause;
|
|
1530
|
+
const message = nested instanceof Error ? nested.message || nested.name : typeof nested === "string" ? nested : void 0;
|
|
1531
|
+
if (!message || message === causeCode)
|
|
1532
|
+
return void 0;
|
|
1533
|
+
return message.replace(/https?:\/\/[^\s<>"'`]+/g, (raw) => redactUrl(raw)).slice(0, 200);
|
|
1534
|
+
}
|
|
1535
|
+
function extractErrorCode(err2) {
|
|
1536
|
+
const code = stringProperty(err2, "code");
|
|
1537
|
+
if (code)
|
|
1538
|
+
return code;
|
|
1539
|
+
const cause = objectProperty(err2, "cause");
|
|
1540
|
+
const causeCode = stringProperty(cause, "code");
|
|
1541
|
+
if (causeCode)
|
|
1542
|
+
return causeCode;
|
|
1543
|
+
const match = /\bE[A-Z0-9_]+\b/.exec(errorMessageOf(err2));
|
|
1544
|
+
return match?.[0];
|
|
1545
|
+
}
|
|
1546
|
+
function redactUrl(url) {
|
|
1547
|
+
try {
|
|
1548
|
+
const parsed = new URL(url);
|
|
1549
|
+
const auth = parsed.username || parsed.password ? "[redacted]@" : "";
|
|
1550
|
+
const query = parsed.search ? "?[redacted]" : "";
|
|
1551
|
+
return `${parsed.protocol}//${auth}${parsed.host}${parsed.pathname}${query}`;
|
|
1552
|
+
} catch {
|
|
1553
|
+
const withoutAuth = url.replace(/\/\/[^/?#\s]+@/, "//[redacted]@");
|
|
1554
|
+
const queryStart = withoutAuth.indexOf("?");
|
|
1555
|
+
return queryStart === -1 ? withoutAuth : `${withoutAuth.slice(0, queryStart)}?[redacted]`;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function errorMessageOf(err2) {
|
|
1559
|
+
if (err2 instanceof Error)
|
|
1560
|
+
return err2.message || err2.name;
|
|
1561
|
+
if (typeof err2 === "string")
|
|
1562
|
+
return err2;
|
|
1563
|
+
return String(err2);
|
|
1564
|
+
}
|
|
1565
|
+
function objectProperty(value, key) {
|
|
1566
|
+
if (!value || typeof value !== "object")
|
|
1567
|
+
return void 0;
|
|
1568
|
+
const prop = value[key];
|
|
1569
|
+
return prop && typeof prop === "object" ? prop : void 0;
|
|
1570
|
+
}
|
|
1571
|
+
function stringProperty(value, key) {
|
|
1572
|
+
if (!value || typeof value !== "object")
|
|
1573
|
+
return void 0;
|
|
1574
|
+
const prop = value[key];
|
|
1575
|
+
return typeof prop === "string" && prop.length > 0 ? prop : void 0;
|
|
1576
|
+
}
|
|
1461
1577
|
|
|
1462
1578
|
// ../contracts/dist/webhook-verify.js
|
|
1463
1579
|
var encoder2 = new TextEncoder();
|
|
@@ -1474,7 +1590,11 @@ var HttpClient = class {
|
|
|
1474
1590
|
}
|
|
1475
1591
|
const raw = options.baseUrl ?? AEX_DEFAULT_BASE_URL;
|
|
1476
1592
|
const normalized = raw.endsWith("/") ? raw : `${raw}/`;
|
|
1477
|
-
|
|
1593
|
+
try {
|
|
1594
|
+
this.#baseUrl = new URL(normalized);
|
|
1595
|
+
} catch (err2) {
|
|
1596
|
+
throw new Error(`HttpClient: invalid aex baseUrl ${JSON.stringify(redactUrl(raw))} \u2014 expected an absolute URL like "${AEX_DEFAULT_BASE_URL}"`, { cause: err2 });
|
|
1597
|
+
}
|
|
1478
1598
|
this.#apiToken = options.apiToken;
|
|
1479
1599
|
this.#fetch = options.fetch ?? fetch;
|
|
1480
1600
|
this.#debug = options.debug;
|
|
@@ -1499,11 +1619,17 @@ var HttpClient = class {
|
|
|
1499
1619
|
}
|
|
1500
1620
|
}
|
|
1501
1621
|
const startedMs = Date.now();
|
|
1502
|
-
|
|
1622
|
+
let response;
|
|
1623
|
+
try {
|
|
1624
|
+
response = await this.#fetch(url, { ...init, headers });
|
|
1625
|
+
} catch (err2) {
|
|
1626
|
+
throw toNetworkError(init.method, url, err2);
|
|
1627
|
+
}
|
|
1503
1628
|
this.#trace(init.method, url, response.status, startedMs);
|
|
1504
1629
|
const body = await readJson(response);
|
|
1505
1630
|
if (!response.ok) {
|
|
1506
|
-
|
|
1631
|
+
const errorBody = withResponseRequestId(body, response.headers);
|
|
1632
|
+
throw new AexApiError(response.status, extractErrorMessage(errorBody), errorBody);
|
|
1507
1633
|
}
|
|
1508
1634
|
return body;
|
|
1509
1635
|
}
|
|
@@ -1517,15 +1643,33 @@ var HttpClient = class {
|
|
|
1517
1643
|
...normalizeHeaders(init.headers)
|
|
1518
1644
|
};
|
|
1519
1645
|
const startedMs = Date.now();
|
|
1520
|
-
|
|
1646
|
+
let response;
|
|
1647
|
+
try {
|
|
1648
|
+
response = await this.#fetch(url, { ...init, headers });
|
|
1649
|
+
} catch (err2) {
|
|
1650
|
+
throw toNetworkError(init.method, url, err2);
|
|
1651
|
+
}
|
|
1521
1652
|
this.#trace(init.method, url, response.status, startedMs);
|
|
1522
1653
|
if (!response.ok) {
|
|
1523
1654
|
const body = await readJson(response);
|
|
1524
|
-
|
|
1655
|
+
const errorBody = withResponseRequestId(body, response.headers);
|
|
1656
|
+
throw new AexApiError(response.status, extractErrorMessage(errorBody), errorBody);
|
|
1525
1657
|
}
|
|
1526
1658
|
return { response };
|
|
1527
1659
|
}
|
|
1528
1660
|
};
|
|
1661
|
+
function toNetworkError(method, url, err2) {
|
|
1662
|
+
if (err2 instanceof AexError)
|
|
1663
|
+
return err2;
|
|
1664
|
+
if (err2?.name === "AbortError")
|
|
1665
|
+
return err2;
|
|
1666
|
+
return new AexNetworkError({
|
|
1667
|
+
method: (method ?? "GET").toUpperCase(),
|
|
1668
|
+
host: url.host,
|
|
1669
|
+
path: url.pathname,
|
|
1670
|
+
cause: err2
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1529
1673
|
function normalizeHeaders(headers) {
|
|
1530
1674
|
if (!headers)
|
|
1531
1675
|
return {};
|
|
@@ -1545,11 +1689,36 @@ async function readJson(response) {
|
|
|
1545
1689
|
return { raw: text };
|
|
1546
1690
|
}
|
|
1547
1691
|
}
|
|
1692
|
+
function withResponseRequestId(body, headers) {
|
|
1693
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
1694
|
+
return body;
|
|
1695
|
+
const record = body;
|
|
1696
|
+
if (typeof record.requestId === "string" && record.requestId.trim())
|
|
1697
|
+
return body;
|
|
1698
|
+
const requestId = responseRequestId(headers);
|
|
1699
|
+
return requestId ? { ...record, requestId } : body;
|
|
1700
|
+
}
|
|
1701
|
+
function responseRequestId(headers) {
|
|
1702
|
+
for (const name of ["x-request-id", "request-id"]) {
|
|
1703
|
+
const value = headers.get(name)?.trim();
|
|
1704
|
+
if (value)
|
|
1705
|
+
return value;
|
|
1706
|
+
}
|
|
1707
|
+
return void 0;
|
|
1708
|
+
}
|
|
1548
1709
|
function extractErrorMessage(body) {
|
|
1549
1710
|
if (body && typeof body === "object") {
|
|
1550
1711
|
const obj = body;
|
|
1551
|
-
if (typeof obj.error === "string")
|
|
1712
|
+
if (typeof obj.error === "string") {
|
|
1713
|
+
const status2 = body.status;
|
|
1714
|
+
if (obj.error === "session_busy" && typeof status2 === "string") {
|
|
1715
|
+
return `session_busy (session status: ${status2})`;
|
|
1716
|
+
}
|
|
1717
|
+
if (typeof obj.message === "string" && obj.message.length > 0 && obj.message !== obj.error) {
|
|
1718
|
+
return `${obj.error}: ${obj.message}`;
|
|
1719
|
+
}
|
|
1552
1720
|
return obj.error;
|
|
1721
|
+
}
|
|
1553
1722
|
if (obj.error && typeof obj.error === "object" && "message" in obj.error) {
|
|
1554
1723
|
const message = obj.error.message;
|
|
1555
1724
|
if (typeof message === "string")
|
|
@@ -2360,7 +2529,23 @@ async function listRuns(http, query) {
|
|
|
2360
2529
|
params.limit = String(query.limit);
|
|
2361
2530
|
if (query?.cursor !== void 0)
|
|
2362
2531
|
params.cursor = query.cursor;
|
|
2363
|
-
|
|
2532
|
+
const page = await http.request("/api/runs", {}, params);
|
|
2533
|
+
let changed = false;
|
|
2534
|
+
const runs = [];
|
|
2535
|
+
for (const run of page.runs) {
|
|
2536
|
+
if (typeof run.id !== "string" || typeof run.status !== "string" || typeof run.createdAt !== "string" || typeof run.updatedAt !== "string") {
|
|
2537
|
+
changed = true;
|
|
2538
|
+
continue;
|
|
2539
|
+
}
|
|
2540
|
+
if (typeof run.costUsd !== "number" && run.costUsd !== void 0) {
|
|
2541
|
+
const { costUsd: _dropped, ...rest } = run;
|
|
2542
|
+
runs.push(rest);
|
|
2543
|
+
changed = true;
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
runs.push(run);
|
|
2547
|
+
}
|
|
2548
|
+
return changed ? { ...page, runs } : page;
|
|
2364
2549
|
}
|
|
2365
2550
|
function idempotencyHeaders(options) {
|
|
2366
2551
|
return options?.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : void 0;
|
|
@@ -2488,12 +2673,17 @@ async function outputLink(http, runId, selectorOrQuery, options) {
|
|
|
2488
2673
|
method: "POST",
|
|
2489
2674
|
body: JSON.stringify({ expiresInSeconds })
|
|
2490
2675
|
});
|
|
2676
|
+
const effectiveExpiresIn = result.expiresInSeconds ?? expiresInSeconds;
|
|
2491
2677
|
return {
|
|
2492
2678
|
...result,
|
|
2493
|
-
expiresInSeconds:
|
|
2679
|
+
expiresInSeconds: effectiveExpiresIn,
|
|
2680
|
+
expiresAt: result.expiresAt ?? syntheticExpiresAt(effectiveExpiresIn),
|
|
2494
2681
|
output: result.output ?? output
|
|
2495
2682
|
};
|
|
2496
2683
|
}
|
|
2684
|
+
function syntheticExpiresAt(expiresInSeconds) {
|
|
2685
|
+
return new Date(Date.now() + expiresInSeconds * 1e3).toISOString();
|
|
2686
|
+
}
|
|
2497
2687
|
async function createOutputLink(http, runId, selectorOrQuery, options) {
|
|
2498
2688
|
return outputLink(http, runId, selectorOrQuery, options);
|
|
2499
2689
|
}
|
|
@@ -2503,9 +2693,11 @@ async function eventArchiveLink(http, runId, options) {
|
|
|
2503
2693
|
method: "POST",
|
|
2504
2694
|
body: JSON.stringify({ expiresInSeconds })
|
|
2505
2695
|
});
|
|
2696
|
+
const effectiveExpiresIn = result.expiresInSeconds ?? expiresInSeconds;
|
|
2506
2697
|
return {
|
|
2507
2698
|
...result,
|
|
2508
|
-
expiresInSeconds:
|
|
2699
|
+
expiresInSeconds: effectiveExpiresIn,
|
|
2700
|
+
expiresAt: result.expiresAt ?? syntheticExpiresAt(effectiveExpiresIn)
|
|
2509
2701
|
};
|
|
2510
2702
|
}
|
|
2511
2703
|
function resolveOutputFileSelector(outputs, selector, runId) {
|
|
@@ -3127,18 +3319,39 @@ async function resolveCommonHostFlags(io2, argv) {
|
|
|
3127
3319
|
function describeApiError(err2) {
|
|
3128
3320
|
if (err2 instanceof AexApiError) {
|
|
3129
3321
|
const remedy = remedyForStatus(err2.status);
|
|
3322
|
+
const detail = describeErrorBody(err2.body);
|
|
3130
3323
|
return {
|
|
3131
3324
|
code: err2.code,
|
|
3132
|
-
message: err2.message,
|
|
3325
|
+
message: detail ? `${err2.message} \u2014 ${detail}` : err2.message,
|
|
3133
3326
|
status: err2.status,
|
|
3134
3327
|
...remedy ? { remedy } : {}
|
|
3135
3328
|
};
|
|
3136
3329
|
}
|
|
3137
3330
|
if (err2 instanceof AexError) {
|
|
3138
|
-
|
|
3331
|
+
const causeCode = err2 instanceof AexNetworkError ? err2.causeCode : extractErrorCode(err2.cause);
|
|
3332
|
+
const remedy = causeCode ? remedyForNetworkCode(causeCode) : void 0;
|
|
3333
|
+
const message = causeCode && !err2.message.includes(causeCode) ? `${err2.message} (${causeCode})` : err2.message;
|
|
3334
|
+
return { code: err2.code, message, ...remedy ? { remedy } : {} };
|
|
3139
3335
|
}
|
|
3140
3336
|
return { code: "error", message: err2 instanceof Error ? err2.message : String(err2) };
|
|
3141
3337
|
}
|
|
3338
|
+
var STANDARD_ERROR_BODY_KEYS = /* @__PURE__ */ new Set(["ok", "error", "message", "code"]);
|
|
3339
|
+
function describeErrorBody(body) {
|
|
3340
|
+
if (!body || typeof body !== "object")
|
|
3341
|
+
return void 0;
|
|
3342
|
+
const record = body;
|
|
3343
|
+
const extraKeys = Object.keys(record).filter((key) => !STANDARD_ERROR_BODY_KEYS.has(key));
|
|
3344
|
+
if (extraKeys.length === 0)
|
|
3345
|
+
return void 0;
|
|
3346
|
+
let text;
|
|
3347
|
+
try {
|
|
3348
|
+
text = JSON.stringify(Object.fromEntries(extraKeys.map((key) => [key, record[key]])));
|
|
3349
|
+
} catch {
|
|
3350
|
+
return void 0;
|
|
3351
|
+
}
|
|
3352
|
+
const redacted = redactSecrets(text);
|
|
3353
|
+
return redacted.length > 400 ? `${redacted.slice(0, 400)}\u2026 (truncated)` : redacted;
|
|
3354
|
+
}
|
|
3142
3355
|
function remedyForStatus(status2) {
|
|
3143
3356
|
if (status2 === 400)
|
|
3144
3357
|
return "malformed request \u2014 if this is an auth failure, check --api-token or run `aex login`";
|
|
@@ -3154,6 +3367,26 @@ function remedyForStatus(status2) {
|
|
|
3154
3367
|
return "server error \u2014 retry; re-run with --debug to capture the request trace";
|
|
3155
3368
|
return void 0;
|
|
3156
3369
|
}
|
|
3370
|
+
function remedyForNetworkCode(code) {
|
|
3371
|
+
switch (code) {
|
|
3372
|
+
case "ECONNREFUSED":
|
|
3373
|
+
case "ENOTFOUND":
|
|
3374
|
+
case "EAI_AGAIN":
|
|
3375
|
+
case "EHOSTUNREACH":
|
|
3376
|
+
case "ENETUNREACH":
|
|
3377
|
+
return "cannot reach the aex API \u2014 check --aex-url and network connectivity";
|
|
3378
|
+
case "ECONNRESET":
|
|
3379
|
+
case "ETIMEDOUT":
|
|
3380
|
+
case "UND_ERR_CONNECT_TIMEOUT":
|
|
3381
|
+
return "connection dropped \u2014 retry; check --aex-url, network connectivity, or any proxy/VPN";
|
|
3382
|
+
case "CERT_HAS_EXPIRED":
|
|
3383
|
+
case "DEPTH_ZERO_SELF_SIGNED_CERT":
|
|
3384
|
+
case "UNABLE_TO_VERIFY_LEAF_SIGNATURE":
|
|
3385
|
+
return "TLS verification failed \u2014 check --aex-url points at the right host";
|
|
3386
|
+
default:
|
|
3387
|
+
return void 0;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3157
3390
|
function suggest(input, candidates) {
|
|
3158
3391
|
const needle = input.trim();
|
|
3159
3392
|
if (!needle)
|
|
@@ -3411,7 +3644,7 @@ async function runRunCmd(io2, argv) {
|
|
|
3411
3644
|
return USAGE_ERR;
|
|
3412
3645
|
}
|
|
3413
3646
|
rest = providerFlag.remaining;
|
|
3414
|
-
let
|
|
3647
|
+
let explicitProvider;
|
|
3415
3648
|
if (providerFlag.value !== null) {
|
|
3416
3649
|
if (!RUN_PROVIDERS.includes(providerFlag.value)) {
|
|
3417
3650
|
const hint = suggest(providerFlag.value, RUN_PROVIDERS);
|
|
@@ -3419,7 +3652,7 @@ async function runRunCmd(io2, argv) {
|
|
|
3419
3652
|
`);
|
|
3420
3653
|
return USAGE_ERR;
|
|
3421
3654
|
}
|
|
3422
|
-
|
|
3655
|
+
explicitProvider = providerFlag.value;
|
|
3423
3656
|
}
|
|
3424
3657
|
const providerKeyValues = {};
|
|
3425
3658
|
for (const p of RUN_PROVIDERS) {
|
|
@@ -3433,11 +3666,6 @@ async function runRunCmd(io2, argv) {
|
|
|
3433
3666
|
if (flag.value !== null)
|
|
3434
3667
|
providerKeyValues[p] = flag.value;
|
|
3435
3668
|
}
|
|
3436
|
-
if (!providerKeyValues[provider]) {
|
|
3437
|
-
io2.stderr(`--${provider}-api-key is required when --provider is ${provider} (the platform does not store provider keys on your behalf)
|
|
3438
|
-
`);
|
|
3439
|
-
return USAGE_ERR;
|
|
3440
|
-
}
|
|
3441
3669
|
const idempotency = takeFlagValue(rest, "--idempotency-key");
|
|
3442
3670
|
if (idempotency.error) {
|
|
3443
3671
|
io2.stderr(`${idempotency.error}
|
|
@@ -3637,6 +3865,12 @@ async function runRunCmd(io2, argv) {
|
|
|
3637
3865
|
...Object.keys(metadataFlags.entries).length > 0 ? { metadata: { ...metadataFlags.entries } } : {}
|
|
3638
3866
|
};
|
|
3639
3867
|
}
|
|
3868
|
+
const provider = explicitProvider ?? providersForModel(runConfig.model)[0] ?? DEFAULT_RUN_PROVIDER;
|
|
3869
|
+
if (!providerKeyValues[provider]) {
|
|
3870
|
+
io2.stderr(`--${provider}-api-key is required for provider ${provider}${explicitProvider === void 0 ? ` (inferred from --model ${runConfig.model})` : ""} (the platform does not store provider keys on your behalf)
|
|
3871
|
+
`);
|
|
3872
|
+
return USAGE_ERR;
|
|
3873
|
+
}
|
|
3640
3874
|
const mcpServersForSubmission = [...runConfig.mcpServers ?? []];
|
|
3641
3875
|
const mcpServerSecrets = [];
|
|
3642
3876
|
const mcpHeaderBag = new Map(mcpHeadersFromConfig);
|
|
@@ -5436,6 +5670,12 @@ function renderEnvelope(e, options = {}) {
|
|
|
5436
5670
|
}
|
|
5437
5671
|
case "CUSTOM": {
|
|
5438
5672
|
const label = e.message ?? str2(e.data.name) ?? "custom";
|
|
5673
|
+
if (e.data.name === "aex.session.idle") {
|
|
5674
|
+
const value = e.data.value;
|
|
5675
|
+
const reason = value && typeof value.reason === "string" ? value.reason : "";
|
|
5676
|
+
if (reason && reason !== "completed")
|
|
5677
|
+
return `[aex] ${label} (${reason})`;
|
|
5678
|
+
}
|
|
5439
5679
|
return `[aex] ${label}`;
|
|
5440
5680
|
}
|
|
5441
5681
|
case "LOG": {
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c8d969d9ee5c6e6bb2e0a642ba8adce0aa235ef2bba96b99d8832ae1957ed2a3 cli.mjs
|
package/dist/client.d.ts
CHANGED
|
@@ -68,7 +68,14 @@ export interface RunResult {
|
|
|
68
68
|
readonly outputs: readonly Output[];
|
|
69
69
|
/** Aggregate token usage when the deployment exposes it on the record. */
|
|
70
70
|
readonly usage?: UsageSummary;
|
|
71
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* Settle-time showback estimate (USD), from the settle-stamped session
|
|
73
|
+
* record's `costUsd` (the full `costTelemetry` block is served on
|
|
74
|
+
* `GET /api/runs/:id`, not on the session projection). The settle
|
|
75
|
+
* write lands tens of seconds AFTER the turn parks, so by default this is
|
|
76
|
+
* usually absent on a fresh run — pass `settleConsistent: true` to wait for
|
|
77
|
+
* it, or read `sessions.get(runId).costUsd` later.
|
|
78
|
+
*/
|
|
72
79
|
readonly costUsd?: number;
|
|
73
80
|
/** The run's error message when `!ok`. */
|
|
74
81
|
readonly error?: string;
|
|
@@ -82,6 +89,14 @@ export interface RunCollectOptions {
|
|
|
82
89
|
readonly pingIntervalMs?: number;
|
|
83
90
|
/** Throw a {@link RunStateError} when the run does not succeed. Default false. */
|
|
84
91
|
readonly throwOnFailure?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Wait (bounded, ~60s) for the settle write after the turn parks, so the
|
|
94
|
+
* result carries the settle-stamped `costUsd`/`usage`/`errorMessage`. The
|
|
95
|
+
* settle lambda lands tens of seconds after the park event, so this trades
|
|
96
|
+
* latency for a complete record. Default false: return at park; read
|
|
97
|
+
* `sessions.get(runId)` later for the showback.
|
|
98
|
+
*/
|
|
99
|
+
readonly settleConsistent?: boolean;
|
|
85
100
|
}
|
|
86
101
|
export type SessionInput = string | readonly string[];
|
|
87
102
|
export interface SessionEnvironmentOptions extends Omit<PlatformEnvironmentInput, "envVars"> {
|
|
@@ -342,6 +357,7 @@ export declare class SessionClient {
|
|
|
342
357
|
create(options: SessionCreateOptions): Promise<SessionHandle>;
|
|
343
358
|
open(sessionId: string): Promise<SessionHandle>;
|
|
344
359
|
get(sessionId: string): Promise<Session>;
|
|
360
|
+
delete(sessionId: string, options?: Pick<SessionSendOptions, "idempotencyKey">): Promise<void>;
|
|
345
361
|
list(query?: SessionListQuery): Promise<SessionListPage>;
|
|
346
362
|
/**
|
|
347
363
|
* Accessor over one session's captured output files, addressed by id without
|