@aexhq/sdk 0.36.0 → 0.37.1

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.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/_contracts/event-envelope.d.ts +22 -1
  3. package/dist/_contracts/event-envelope.js +26 -2
  4. package/dist/_contracts/event-stream-client.js +7 -1
  5. package/dist/_contracts/operations.d.ts +30 -1
  6. package/dist/_contracts/operations.js +54 -1
  7. package/dist/_contracts/run-config.d.ts +1 -1
  8. package/dist/_contracts/run-unit.d.ts +12 -0
  9. package/dist/_contracts/run-unit.js +55 -0
  10. package/dist/_contracts/runtime-sizes.d.ts +2 -2
  11. package/dist/_contracts/runtime-sizes.js +5 -5
  12. package/dist/_contracts/runtime-types.d.ts +98 -0
  13. package/dist/_contracts/submission.d.ts +4 -4
  14. package/dist/cli.mjs +554 -69
  15. package/dist/cli.mjs.sha256 +1 -1
  16. package/dist/client.d.ts +40 -1
  17. package/dist/client.js +90 -5
  18. package/dist/client.js.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/version.d.ts +1 -1
  22. package/dist/version.js +1 -1
  23. package/docs/authentication.md +92 -0
  24. package/docs/billing.md +112 -0
  25. package/docs/concepts/agent-tools.md +4 -4
  26. package/docs/concepts/providers-and-runtimes.md +4 -1
  27. package/docs/concepts/runs.md +2 -1
  28. package/docs/concepts/subagents.md +85 -0
  29. package/docs/credentials.md +27 -3
  30. package/docs/defaults.md +9 -7
  31. package/docs/errors.md +132 -0
  32. package/docs/events.md +36 -23
  33. package/docs/limits-and-quotas.md +29 -13
  34. package/docs/limits.md +2 -2
  35. package/docs/mcp.md +1 -1
  36. package/docs/networking.md +68 -42
  37. package/docs/outputs.md +4 -3
  38. package/docs/public-surface.json +1 -1
  39. package/docs/quickstart.md +9 -6
  40. package/docs/run-config.md +1 -1
  41. package/docs/secrets.md +5 -0
  42. package/docs/webhooks.md +132 -0
  43. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -128,6 +128,46 @@ var MODEL_PROVIDER_IDS = {
128
128
  }
129
129
  };
130
130
  var RUN_MODELS = Object.keys(MODEL_PROVIDER_IDS);
131
+ var Models = {
132
+ /** Claude Haiku 4.5 — Anthropic. */
133
+ CLAUDE_HAIKU_4_5: "claude-haiku-4-5",
134
+ /** Claude 3.5 Haiku (latest) — Anthropic. */
135
+ CLAUDE_3_5_HAIKU_LATEST: "claude-3-5-haiku-latest",
136
+ /** Claude 3.5 Sonnet (latest) — Anthropic. */
137
+ CLAUDE_3_5_SONNET_LATEST: "claude-3-5-sonnet-latest",
138
+ /** Claude Sonnet 4.6 — Anthropic (1M context, reasoning-capable). */
139
+ CLAUDE_SONNET_4_6: "claude-sonnet-4-6",
140
+ /** DeepSeek V4 Flash — DeepSeek (non-thinking, fast). */
141
+ DEEPSEEK_V4_FLASH: "deepseek-v4-flash",
142
+ /** DeepSeek V4 Pro — DeepSeek (reasoning-heavy). */
143
+ DEEPSEEK_V4_PRO: "deepseek-v4-pro",
144
+ /** GPT-4.1 — OpenAI. */
145
+ GPT_4_1: "gpt-4.1",
146
+ /** GPT-4o mini — OpenAI, or via OpenRouter (`provider: Providers.OPENROUTER`). */
147
+ GPT_4O_MINI: "gpt-4o-mini",
148
+ /** GPT-4o — via OpenRouter (`provider: Providers.OPENROUTER`). */
149
+ GPT_4O: "gpt-4o",
150
+ /** Gemini 2.0 Flash — Gemini, or via OpenRouter (`provider: Providers.OPENROUTER`). */
151
+ GEMINI_2_0_FLASH: "gemini-2.0-flash",
152
+ /** Gemini 2.5 Flash — Gemini. */
153
+ GEMINI_2_5_FLASH: "gemini-2.5-flash",
154
+ /** Mistral Large (latest) — Mistral. */
155
+ MISTRAL_LARGE_LATEST: "mistral-large-latest",
156
+ /** Mistral Small (latest) — Mistral. */
157
+ MISTRAL_SMALL_LATEST: "mistral-small-latest",
158
+ /**
159
+ * Doubao Seed 1.8 — ByteDance, via the official Ark API. Default routes
160
+ * through the international BytePlus gateway (`Providers.DOUBAO`); pair with
161
+ * `Providers.DOUBAO_CN` for the China Volcengine gateway.
162
+ */
163
+ DOUBAO_SEED_PRO: "doubao-seed-pro",
164
+ /**
165
+ * Doubao Seed 1.6 Flash — ByteDance, via the official Ark API (fast/cheap).
166
+ * Default `Providers.DOUBAO` (international); pair with `Providers.DOUBAO_CN`
167
+ * for China.
168
+ */
169
+ DOUBAO_SEED_FLASH: "doubao-seed-flash"
170
+ };
131
171
  var PROVIDERS_BY_MODEL = (() => {
132
172
  const map = {};
133
173
  for (const [model, providers] of Object.entries(MODEL_PROVIDER_IDS)) {
@@ -171,6 +211,15 @@ var TERMINAL_RUN_STATUSES = [
171
211
  "cleanup_failed"
172
212
  ];
173
213
  var terminalRunStatuses = new Set(TERMINAL_RUN_STATUSES);
214
+ var CLEANUP_STATUSES = [
215
+ "not_started",
216
+ "pending",
217
+ "running",
218
+ "succeeded",
219
+ "failed_retryable",
220
+ "failed_terminal",
221
+ "skipped"
222
+ ];
174
223
 
175
224
  // ../contracts/dist/run-config.js
176
225
  var SKILL_BUNDLE_LIMITS = {
@@ -445,8 +494,8 @@ var RUNTIME_SIZE_PRESETS = {
445
494
  };
446
495
  var RUNTIME_SIZES = Object.keys(RUNTIME_SIZE_PRESETS);
447
496
  var DEFAULT_RUNTIME_SIZE = "shared-0.25x-1gb";
448
- var DEFAULT_RUN_TIMEOUT_MS = 60 * 60 * 1e3;
449
- var MAX_RUN_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
497
+ var DEFAULT_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
498
+ var MAX_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
450
499
  var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
451
500
  var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
452
501
  var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
@@ -480,6 +529,7 @@ var RUNTIME_SECURITY_PROFILE_CONFIG = Object.freeze({
480
529
  });
481
530
 
482
531
  // ../contracts/dist/submission.js
532
+ var PLATFORM_PACKAGE_ECOSYSTEMS = ["apt", "npm", "pip"];
483
533
  var RUN_PROVIDERS = [
484
534
  "anthropic",
485
535
  "deepseek",
@@ -530,12 +580,17 @@ var AEX_EVENT_TYPES = [
530
580
  // off-the-shelf AG-UI client filters logs out by `channel`.
531
581
  "LOG"
532
582
  ];
583
+ var AEX_SESSION_PARKED_NAMES = ["aex.session.idle", "aex.session.error", "aex.session.suspended"];
584
+ function isSessionParked(e) {
585
+ const name = customName(e);
586
+ return name !== null && AEX_SESSION_PARKED_NAMES.includes(name);
587
+ }
533
588
  function customName(e) {
534
589
  return e.type === "CUSTOM" ? str(e.data.name) || null : null;
535
590
  }
536
591
  var AEX_RUN_SETTLED_NAME = "aex.run.settled";
537
592
  function isRunSettled(e) {
538
- return customName(e) === AEX_RUN_SETTLED_NAME;
593
+ return customName(e) === AEX_RUN_SETTLED_NAME || isSessionParked(e);
539
594
  }
540
595
  function channelOf(e) {
541
596
  return e.channel ?? "event";
@@ -593,7 +648,7 @@ function str(v) {
593
648
  var encoder = new TextEncoder();
594
649
 
595
650
  // ../contracts/dist/event-stream-client.js
596
- var isTerminalType = (e) => e.type === "RUN_FINISHED" || e.type === "RUN_ERROR";
651
+ var isTerminalType = (e) => e.type === "RUN_FINISHED" || e.type === "RUN_ERROR" || isSessionParked(e);
597
652
  var COORDINATOR_PING = "aex:ping";
598
653
  var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
599
654
  var DEFAULT_PING_INTERVAL_MS = 15e3;
@@ -751,6 +806,191 @@ function sleep(ms, signal) {
751
806
  });
752
807
  }
753
808
 
809
+ // ../contracts/dist/run-unit.js
810
+ function parseRunUnitSubmission(input) {
811
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
812
+ return fallbackFlat();
813
+ }
814
+ const value = input;
815
+ if (value.kind === "submission") {
816
+ return parseFlatProjection(value);
817
+ }
818
+ return fallbackFlat();
819
+ }
820
+ function parseFlatProjection(value) {
821
+ const submissionRaw = isRecord(value.submission) ? value.submission : {};
822
+ const outputsRaw = isRecord(submissionRaw.outputs) ? submissionRaw.outputs : {};
823
+ const allowedDirs = toOptionalStringArray(outputsRaw.allowedDirs);
824
+ const deniedDirs = toOptionalStringArray(outputsRaw.deniedDirs);
825
+ const captureTimeoutMs = toOptionalPositiveInteger(outputsRaw.captureTimeoutMs);
826
+ const maxFileBytes = toOptionalPositiveInteger(outputsRaw.maxFileBytes);
827
+ const maxTotalBytes = toOptionalPositiveInteger(outputsRaw.maxTotalBytes);
828
+ const maxFiles = toOptionalPositiveInteger(outputsRaw.maxFiles);
829
+ const submission = {
830
+ model: coerceRunUnitModel(submissionRaw.model),
831
+ ...typeof submissionRaw.system === "string" ? { system: submissionRaw.system } : {},
832
+ prompt: toStringArray(submissionRaw.prompt),
833
+ agentsMd: [],
834
+ files: [],
835
+ mcpServers: toMcpServerRefArray(submissionRaw.mcpServers),
836
+ tools: [],
837
+ ...parseEnvironment(submissionRaw.environment) ? { environment: parseEnvironment(submissionRaw.environment) } : {},
838
+ ...parseSecurityProfile(submissionRaw.securityProfile) ? { securityProfile: parseSecurityProfile(submissionRaw.securityProfile) } : {},
839
+ ...isJsonRecord(submissionRaw.metadata) ? { metadata: submissionRaw.metadata } : {},
840
+ ...allowedDirs || deniedDirs || captureTimeoutMs !== void 0 || maxFileBytes !== void 0 || maxTotalBytes !== void 0 || maxFiles !== void 0 ? {
841
+ outputs: {
842
+ ...allowedDirs ? { allowedDirs } : {},
843
+ ...deniedDirs ? { deniedDirs } : {},
844
+ ...captureTimeoutMs !== void 0 ? { captureTimeoutMs } : {},
845
+ ...maxFileBytes !== void 0 ? { maxFileBytes } : {},
846
+ ...maxTotalBytes !== void 0 ? { maxTotalBytes } : {},
847
+ ...maxFiles !== void 0 ? { maxFiles } : {}
848
+ }
849
+ } : {}
850
+ };
851
+ return {
852
+ kind: "submission",
853
+ submission
854
+ };
855
+ }
856
+ function parseSecurityProfile(value) {
857
+ return value === "strict" || value === "standard" || value === "developer" ? value : void 0;
858
+ }
859
+ function toOptionalPositiveInteger(value) {
860
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
861
+ }
862
+ function fallbackFlat() {
863
+ return {
864
+ kind: "submission",
865
+ submission: {
866
+ model: Models.CLAUDE_HAIKU_4_5,
867
+ prompt: [],
868
+ agentsMd: [],
869
+ files: [],
870
+ mcpServers: [],
871
+ tools: []
872
+ }
873
+ };
874
+ }
875
+ function isRecord(value) {
876
+ return typeof value === "object" && value !== null && !Array.isArray(value);
877
+ }
878
+ function normalizeRunUnit(raw) {
879
+ const r = isRecord(raw) ? raw : {};
880
+ const eventsRaw = isRecord(r.events) ? r.events : {};
881
+ const str3 = (v) => typeof v === "string" ? v : void 0;
882
+ const arr = (v) => Array.isArray(v) ? v : [];
883
+ return {
884
+ id: str3(r.id) ?? "",
885
+ workspaceId: str3(r.workspaceId) ?? "",
886
+ status: str3(r.status) ?? "unknown",
887
+ ...str3(r.lifecyclePhase) ? { lifecyclePhase: r.lifecyclePhase } : {},
888
+ cleanupStatus: CLEANUP_STATUSES.includes(r.cleanupStatus) ? r.cleanupStatus : "not_started",
889
+ createdAt: str3(r.createdAt) ?? "",
890
+ updatedAt: str3(r.updatedAt) ?? "",
891
+ ...str3(r.startedAt) ? { startedAt: r.startedAt } : {},
892
+ ...str3(r.terminalAt) ? { terminalAt: r.terminalAt } : {},
893
+ ...str3(r.deletedAt) ? { deletedAt: r.deletedAt } : {},
894
+ attemptCount: typeof r.attemptCount === "number" ? r.attemptCount : Array.isArray(r.attempts) ? r.attempts.length : 0,
895
+ submission: parseRunUnitSubmission(r.submission),
896
+ ...isRecord(r.capsSnapshot) ? { capsSnapshot: r.capsSnapshot } : {},
897
+ attempts: arr(r.attempts),
898
+ events: {
899
+ entries: arr(eventsRaw.entries),
900
+ totalCount: typeof eventsRaw.totalCount === "number" ? eventsRaw.totalCount : 0,
901
+ truncated: eventsRaw.truncated === true,
902
+ ...str3(eventsRaw.nextCursor) ? { nextCursor: eventsRaw.nextCursor } : {}
903
+ },
904
+ rawEventPages: arr(r.rawEventPages),
905
+ outputs: arr(r.outputs),
906
+ outputCaptureFailures: arr(r.outputCaptureFailures),
907
+ ...isRecord(r.costTelemetry) ? { costTelemetry: r.costTelemetry } : {},
908
+ ...isRecord(r.runtimeManifest) ? { runtimeManifest: r.runtimeManifest } : {}
909
+ };
910
+ }
911
+ function coerceRunUnitModel(value) {
912
+ if (typeof value !== "string")
913
+ return Models.CLAUDE_HAIKU_4_5;
914
+ try {
915
+ return parseRunModel(value, "run unit submission.model");
916
+ } catch {
917
+ return Models.CLAUDE_HAIKU_4_5;
918
+ }
919
+ }
920
+ function isJsonRecord(value) {
921
+ return isRecord(value);
922
+ }
923
+ function toStringArray(value) {
924
+ if (!Array.isArray(value)) {
925
+ return [];
926
+ }
927
+ return value.filter((item) => typeof item === "string");
928
+ }
929
+ function toOptionalStringArray(value) {
930
+ if (!Array.isArray(value) || value.length === 0) {
931
+ return void 0;
932
+ }
933
+ const filtered = value.filter((item) => typeof item === "string");
934
+ return filtered.length === 0 ? void 0 : filtered;
935
+ }
936
+ function toMcpServerRefArray(value) {
937
+ if (!Array.isArray(value)) {
938
+ return [];
939
+ }
940
+ const out = [];
941
+ for (let i2 = 0; i2 < value.length; i2++) {
942
+ try {
943
+ out.push(parseMcpServerRef(value[i2], `submission.mcpServers[${i2}]`));
944
+ } catch {
945
+ }
946
+ }
947
+ return out;
948
+ }
949
+ function parseEnvironment(value) {
950
+ if (!isRecord(value)) {
951
+ return void 0;
952
+ }
953
+ const env = {};
954
+ if (isRecord(value.networking)) {
955
+ const mode = value.networking.mode;
956
+ const allowedHosts = value.networking.allowedHosts;
957
+ if (mode === "limited" || mode === "open") {
958
+ env.networking = {
959
+ mode,
960
+ ...Array.isArray(allowedHosts) ? { allowedHosts: toStringArray(allowedHosts) } : {}
961
+ };
962
+ }
963
+ }
964
+ if (Array.isArray(value.packages)) {
965
+ const pkgs = value.packages.filter(isRecord).map((p) => {
966
+ const r = p;
967
+ if (typeof r.name !== "string")
968
+ return null;
969
+ const ecosystem = typeof r.ecosystem === "string" && PLATFORM_PACKAGE_ECOSYSTEMS.includes(r.ecosystem) ? r.ecosystem : "apt";
970
+ return {
971
+ name: r.name,
972
+ ...typeof r.version === "string" ? { version: r.version } : {},
973
+ ecosystem
974
+ };
975
+ }).filter((p) => p !== null);
976
+ if (pkgs.length > 0) {
977
+ env.packages = pkgs;
978
+ }
979
+ }
980
+ if (isRecord(value.envVars)) {
981
+ const out = {};
982
+ for (const [k, v] of Object.entries(value.envVars)) {
983
+ if (typeof v === "string") {
984
+ out[k] = v;
985
+ }
986
+ }
987
+ if (Object.keys(out).length > 0) {
988
+ env.envVars = out;
989
+ }
990
+ }
991
+ return env.networking || env.packages || env.envVars ? env : void 0;
992
+ }
993
+
754
994
  // ../contracts/dist/runtime-manifest.js
755
995
  var RUNTIME_PATHS = Object.freeze({
756
996
  skillsRoot: "/workspace/skills",
@@ -1329,6 +1569,8 @@ __export(operations_exports, {
1329
1569
  cancelRun: () => cancelRun,
1330
1570
  cancelSession: () => cancelSession,
1331
1571
  classifyOutput: () => classifyOutput,
1572
+ createBillingCheckout: () => createBillingCheckout,
1573
+ createBillingPortal: () => createBillingPortal,
1332
1574
  createOutputLink: () => createOutputLink,
1333
1575
  createSecret: () => createSecret,
1334
1576
  createSession: () => createSession,
@@ -1348,6 +1590,8 @@ __export(operations_exports, {
1348
1590
  findOutput: () => findOutput,
1349
1591
  findOutputs: () => findOutputs,
1350
1592
  getAgentsMd: () => getAgentsMd,
1593
+ getBilling: () => getBilling,
1594
+ getBillingLedger: () => getBillingLedger,
1351
1595
  getCoordinatorTicket: () => getCoordinatorTicket,
1352
1596
  getFile: () => getFile,
1353
1597
  getRun: () => getRun,
@@ -1357,6 +1601,7 @@ __export(operations_exports, {
1357
1601
  getSecretValue: () => getSecretValue,
1358
1602
  getSession: () => getSession,
1359
1603
  getSessionCoordinatorTicket: () => getSessionCoordinatorTicket,
1604
+ getWebhookSigningSecret: () => getWebhookSigningSecret,
1360
1605
  listAgentsMd: () => listAgentsMd,
1361
1606
  listFiles: () => listFiles,
1362
1607
  listOutputs: () => listOutputs,
@@ -2103,7 +2348,7 @@ async function getRun(http, runId) {
2103
2348
  return hasRun(result) ? result.run : result;
2104
2349
  }
2105
2350
  async function getRunUnit(http, runId) {
2106
- return http.request(`/api/runs/${encodeURIComponent(runId)}`);
2351
+ return normalizeRunUnit(await http.request(`/api/runs/${encodeURIComponent(runId)}`));
2107
2352
  }
2108
2353
  async function listRuns(http, query) {
2109
2354
  const params = {};
@@ -2386,6 +2631,30 @@ async function deleteWorkspaceAsset(http, hash) {
2386
2631
  async function whoami(http) {
2387
2632
  return http.request("/api/whoami");
2388
2633
  }
2634
+ async function getBilling(http) {
2635
+ return http.request("/api/billing");
2636
+ }
2637
+ async function createBillingCheckout(http, request) {
2638
+ return http.request("/api/billing/checkout", {
2639
+ method: "POST",
2640
+ body: JSON.stringify(request)
2641
+ });
2642
+ }
2643
+ async function createBillingPortal(http, request = {}) {
2644
+ return http.request("/api/billing/portal", {
2645
+ method: "POST",
2646
+ body: JSON.stringify(request)
2647
+ });
2648
+ }
2649
+ async function getBillingLedger(http, query) {
2650
+ const params = {};
2651
+ if (query?.limit !== void 0)
2652
+ params.limit = String(query.limit);
2653
+ return http.request("/api/billing/ledger", {}, params);
2654
+ }
2655
+ async function getWebhookSigningSecret(http) {
2656
+ return http.request("/api/webhook/signing-secret", { method: "POST" });
2657
+ }
2389
2658
  async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2) {
2390
2659
  const entries = [];
2391
2660
  const captured = [];
@@ -2652,7 +2921,7 @@ function jsonlEntry(path, events) {
2652
2921
  }
2653
2922
  function extractSubmissionSnapshot(run) {
2654
2923
  const raw = run.submission;
2655
- if (!isRecord(raw) || raw.kind !== "submission" || !isRecord(raw.submission)) {
2924
+ if (!isRecord2(raw) || raw.kind !== "submission" || !isRecord2(raw.submission)) {
2656
2925
  return void 0;
2657
2926
  }
2658
2927
  return {
@@ -2661,9 +2930,9 @@ function extractSubmissionSnapshot(run) {
2661
2930
  }
2662
2931
  function extractCostTelemetry(run) {
2663
2932
  const raw = run.costTelemetry;
2664
- return isRecord(raw) ? raw : void 0;
2933
+ return isRecord2(raw) ? raw : void 0;
2665
2934
  }
2666
- function isRecord(value) {
2935
+ function isRecord2(value) {
2667
2936
  return typeof value === "object" && value !== null && !Array.isArray(value);
2668
2937
  }
2669
2938
  async function submitRun(http, request) {
@@ -2784,7 +3053,7 @@ var USAGE_ERR = { code: 2 };
2784
3053
  var RUNTIME_ERR = { code: 1 };
2785
3054
  var TIMEOUT_ERR = { code: 3 };
2786
3055
  var TERMINAL_STATUSES = new Set(TERMINAL_RUN_STATUSES);
2787
- function isSessionParked(status2) {
3056
+ function isSessionParked2(status2) {
2788
3057
  return status2 === "idle" || status2 === "suspended" || status2 === "error" || TERMINAL_STATUSES.has(status2);
2789
3058
  }
2790
3059
  function isSessionOk(status2) {
@@ -2871,6 +3140,8 @@ function describeApiError(err2) {
2871
3140
  return { code: "error", message: err2 instanceof Error ? err2.message : String(err2) };
2872
3141
  }
2873
3142
  function remedyForStatus(status2) {
3143
+ if (status2 === 400)
3144
+ return "malformed request \u2014 if this is an auth failure, check --api-token or run `aex login`";
2874
3145
  if (status2 === 401)
2875
3146
  return "check --api-token, or run `aex login`";
2876
3147
  if (status2 === 403)
@@ -3460,7 +3731,7 @@ async function runRunCmd(io2, argv) {
3460
3731
  const seen = /* @__PURE__ */ new Set();
3461
3732
  let currentStatus = accepted.session.status;
3462
3733
  const deadline = followTimeoutMs === null ? Number.POSITIVE_INFINITY : Date.now() + followTimeoutMs;
3463
- while (!isSessionParked(currentStatus)) {
3734
+ while (!isSessionParked2(currentStatus)) {
3464
3735
  await sleep2(2e3);
3465
3736
  try {
3466
3737
  const events = await operations_exports.listSessionEvents(http, session.id);
@@ -3481,7 +3752,7 @@ async function runRunCmd(io2, argv) {
3481
3752
  io2.stderr(`(transient) status poll failed: ${err2.message}
3482
3753
  `);
3483
3754
  }
3484
- if (!isSessionParked(currentStatus) && Date.now() >= deadline) {
3755
+ if (!isSessionParked2(currentStatus) && Date.now() >= deadline) {
3485
3756
  emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
3486
3757
  sessionId: session.id,
3487
3758
  hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
@@ -3677,7 +3948,7 @@ async function runWaitCmd(io2, argv) {
3677
3948
  await sleep3(intervalMs);
3678
3949
  continue;
3679
3950
  }
3680
- if (isSessionParked(session.status)) {
3951
+ if (isSessionParked2(session.status)) {
3681
3952
  io2.stdout(JSON.stringify(session) + "\n");
3682
3953
  return isSessionOk(session.status) ? SUCCESS : RUNTIME_ERR;
3683
3954
  }
@@ -3757,7 +4028,7 @@ async function runEventsCmd(io2, argv) {
3757
4028
  }
3758
4029
  try {
3759
4030
  const session = await operations_exports.getSession(http, sessionId);
3760
- if (isSessionParked(session.status)) {
4031
+ if (isSessionParked2(session.status)) {
3761
4032
  return SUCCESS;
3762
4033
  }
3763
4034
  } catch (err2) {
@@ -4002,25 +4273,26 @@ async function runWhoamiCmd(io2, argv) {
4002
4273
  }
4003
4274
  }
4004
4275
 
4005
- // dist/host/redeem.js
4006
- function messageForStatus(status2, serverMessage) {
4007
- switch (status2) {
4008
- case 404:
4009
- return "coupon code not found";
4010
- case 403:
4011
- return "this coupon can't be redeemed by this workspace";
4012
- case 409:
4013
- return "coupon already redeemed";
4014
- case 400:
4015
- return serverMessage ? `invalid input: ${serverMessage}` : "invalid input";
4016
- case 401:
4017
- return "not authorized \u2014 check --api-token, or run `aex login`";
4018
- default:
4019
- return serverMessage ? `redeem failed: ${serverMessage}` : `redeem failed (HTTP ${status2})`;
4276
+ // dist/host/billing.js
4277
+ function usd(value) {
4278
+ return typeof value === "number" && Number.isFinite(value) ? `$${value.toFixed(2)}` : "-";
4279
+ }
4280
+ function isPaidPlanKey(value) {
4281
+ return value === "pro" || value === "team";
4282
+ }
4283
+ function parseLimit(io2, raw) {
4284
+ if (raw === void 0)
4285
+ return { ok: true, limit: void 0 };
4286
+ const limit = Number(raw);
4287
+ if (!Number.isInteger(limit) || limit < 1) {
4288
+ io2.stderr(`--limit must be a positive integer (got: ${raw})
4289
+ `);
4290
+ return { ok: false };
4020
4291
  }
4292
+ return { ok: true, limit };
4021
4293
  }
4022
- async function runRedeemCmd(io2, argv) {
4023
- if (await refuseInsideManagedRun(io2, "redeem"))
4294
+ async function runBillingCmd(io2, argv) {
4295
+ if (await refuseInsideManagedRun(io2, "billing"))
4024
4296
  return USAGE_ERR;
4025
4297
  const common = await resolveCommonHostFlags(io2, argv);
4026
4298
  if (!common.ok) {
@@ -4028,54 +4300,255 @@ async function runRedeemCmd(io2, argv) {
4028
4300
  `);
4029
4301
  return USAGE_ERR;
4030
4302
  }
4031
- const positional = common.rest.filter((arg) => !arg.startsWith("--"));
4032
- if (positional.length !== 1) {
4033
- io2.stderr("usage: aex redeem <code> [common flags]\n");
4303
+ if (common.rest[0] === "ledger") {
4304
+ return runBillingLedger(io2, common.rest.slice(1), common.flags);
4305
+ }
4306
+ if (common.rest[0] === "upgrade") {
4307
+ return runBillingUpgrade(io2, common.rest.slice(1), common.flags);
4308
+ }
4309
+ if (common.rest[0] === "portal") {
4310
+ return runBillingPortal(io2, common.rest.slice(1), common.flags);
4311
+ }
4312
+ const { present: json, remaining } = takeBooleanFlag(common.rest, "--json");
4313
+ if (remaining.length > 0) {
4314
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4315
+ `);
4316
+ io2.stderr("usage: aex billing [--json] | aex billing ledger [--limit N] | aex billing upgrade pro|team | aex billing portal [common flags]\n");
4317
+ return USAGE_ERR;
4318
+ }
4319
+ const http = makeHttpClient(io2, common.flags);
4320
+ try {
4321
+ const billing = await operations_exports.getBilling(http);
4322
+ if (json) {
4323
+ io2.stdout(JSON.stringify(billing) + "\n");
4324
+ return SUCCESS;
4325
+ }
4326
+ io2.stdout(`Balance: ${usd(billing.balanceUsd)}
4327
+ `);
4328
+ io2.stdout(`Month spend: ${usd(billing.monthSpendUsd)}
4329
+ `);
4330
+ io2.stdout(`Spend cap: ${usd(billing.spendCapUsd)}
4331
+ `);
4332
+ io2.stdout(`Plan: ${billing.planKey} (subscription: ${billing.subscriptionStatus})
4333
+ `);
4334
+ return SUCCESS;
4335
+ } catch (err2) {
4336
+ const d = describeApiError(err2);
4337
+ return emitJsonError(io2, "billing_failed", d.message, {
4338
+ ...d.status !== void 0 ? { status: d.status } : {},
4339
+ ...d.remedy ? { remedy: d.remedy } : {}
4340
+ });
4341
+ }
4342
+ }
4343
+ async function runBillingUpgrade(io2, argv, flags) {
4344
+ const { present: json, remaining: rest1 } = takeBooleanFlag(argv, "--json");
4345
+ const { value: successUrl, remaining: rest2 } = takeOptionFlag(rest1, "--success-url");
4346
+ const { value: cancelUrl, remaining: rest3 } = takeOptionFlag(rest2, "--cancel-url");
4347
+ const { value: idempotencyKey, remaining } = takeOptionFlag(rest3, "--idempotency-key");
4348
+ const planKey = remaining[0];
4349
+ if (!isPaidPlanKey(planKey) || remaining.length !== 1) {
4350
+ io2.stderr("usage: aex billing upgrade pro|team [--success-url URL] [--cancel-url URL] [--idempotency-key KEY] [--json] [common flags]\n");
4034
4351
  return USAGE_ERR;
4035
4352
  }
4036
- const code = positional[0];
4037
- const base = common.flags.aexUrl.replace(/\/+$/, "");
4038
- const url = `${base}/billing/redeem`;
4039
- let response;
4353
+ const http = makeHttpClient(io2, flags);
4040
4354
  try {
4041
- response = await io2.fetchImpl(url, {
4042
- method: "POST",
4043
- headers: {
4044
- accept: "application/json",
4045
- "content-type": "application/json",
4046
- authorization: `Bearer ${common.flags.apiToken}`
4047
- },
4048
- body: JSON.stringify({ code })
4355
+ const session = await operations_exports.createBillingCheckout(http, {
4356
+ planKey,
4357
+ ...successUrl !== void 0 ? { successUrl } : {},
4358
+ ...cancelUrl !== void 0 ? { cancelUrl } : {},
4359
+ ...idempotencyKey !== void 0 ? { idempotencyKey } : {}
4049
4360
  });
4361
+ io2.stdout(json ? `${JSON.stringify(session)}
4362
+ ` : `${session.url}
4363
+ `);
4364
+ return SUCCESS;
4050
4365
  } catch (err2) {
4051
- io2.stderr(`redeem failed: ${err2 instanceof Error ? err2.message : String(err2)}
4366
+ const d = describeApiError(err2);
4367
+ return emitJsonError(io2, "billing_checkout_failed", d.message, {
4368
+ ...d.status !== void 0 ? { status: d.status } : {},
4369
+ ...d.remedy ? { remedy: d.remedy } : {}
4370
+ });
4371
+ }
4372
+ }
4373
+ async function runBillingPortal(io2, argv, flags) {
4374
+ const { present: json, remaining: rest1 } = takeBooleanFlag(argv, "--json");
4375
+ const { value: returnUrl, remaining } = takeOptionFlag(rest1, "--return-url");
4376
+ if (remaining.length > 0) {
4377
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4052
4378
  `);
4053
- return RUNTIME_ERR;
4379
+ io2.stderr("usage: aex billing portal [--return-url URL] [--json] [common flags]\n");
4380
+ return USAGE_ERR;
4054
4381
  }
4055
- if (common.flags.debug) {
4056
- io2.stderr(`[aex] POST /billing/redeem -> ${response.status}
4382
+ const http = makeHttpClient(io2, flags);
4383
+ try {
4384
+ const session = await operations_exports.createBillingPortal(http, returnUrl !== void 0 ? { returnUrl } : void 0);
4385
+ io2.stdout(json ? `${JSON.stringify(session)}
4386
+ ` : `${session.url}
4057
4387
  `);
4388
+ return SUCCESS;
4389
+ } catch (err2) {
4390
+ const d = describeApiError(err2);
4391
+ return emitJsonError(io2, "billing_portal_failed", d.message, {
4392
+ ...d.status !== void 0 ? { status: d.status } : {},
4393
+ ...d.remedy ? { remedy: d.remedy } : {}
4394
+ });
4058
4395
  }
4059
- const text = await response.text();
4060
- let body = {};
4396
+ }
4397
+ async function runBillingLedger(io2, argv, flags) {
4398
+ const { value: rawLimit, remaining } = takeOptionFlag(argv, "--limit");
4399
+ if (remaining.length > 0) {
4400
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4401
+ `);
4402
+ io2.stderr("usage: aex billing ledger [--limit N] [common flags]\n");
4403
+ return USAGE_ERR;
4404
+ }
4405
+ const parsed = parseLimit(io2, rawLimit);
4406
+ if (!parsed.ok)
4407
+ return USAGE_ERR;
4408
+ const http = makeHttpClient(io2, flags);
4061
4409
  try {
4062
- if (text.length > 0)
4063
- body = JSON.parse(text);
4064
- } catch {
4065
- body = {};
4410
+ const page = await operations_exports.getBillingLedger(http, parsed.limit !== void 0 ? { limit: parsed.limit } : void 0);
4411
+ io2.stdout(JSON.stringify(page.entries) + "\n");
4412
+ return SUCCESS;
4413
+ } catch (err2) {
4414
+ const d = describeApiError(err2);
4415
+ return emitJsonError(io2, "billing_ledger_failed", d.message, {
4416
+ ...d.status !== void 0 ? { status: d.status } : {},
4417
+ ...d.remedy ? { remedy: d.remedy } : {}
4418
+ });
4066
4419
  }
4067
- if (!response.ok) {
4068
- const serverMessage = body && typeof body === "object" && typeof body.message === "string" ? body.message : void 0;
4069
- io2.stderr(`${messageForStatus(response.status, serverMessage)}
4420
+ }
4421
+
4422
+ // dist/host/webhooks-cmd.js
4423
+ async function runWebhooksCmd(io2, argv) {
4424
+ if (await refuseInsideManagedRun(io2, "webhooks"))
4425
+ return USAGE_ERR;
4426
+ const common = await resolveCommonHostFlags(io2, argv);
4427
+ if (!common.ok) {
4428
+ io2.stderr(`${common.reason}
4070
4429
  `);
4071
- return RUNTIME_ERR;
4430
+ return USAGE_ERR;
4431
+ }
4432
+ const [sub, ...rest] = common.rest;
4433
+ if (sub !== "secret") {
4434
+ io2.stderr("usage: aex webhooks secret [common flags]\n");
4435
+ return USAGE_ERR;
4072
4436
  }
4073
- const ok = body;
4074
- const amountUsd = typeof ok.amountUsd === "number" ? ok.amountUsd : 0;
4075
- const newBalanceUsd = typeof ok.newBalanceUsd === "number" ? ok.newBalanceUsd : 0;
4076
- io2.stdout(`Redeemed $${amountUsd.toFixed(2)}. New balance: $${newBalanceUsd.toFixed(2)}.
4437
+ const { present: rotate, remaining } = takeBooleanFlag(rest, "--rotate");
4438
+ if (rotate) {
4439
+ io2.stderr("--rotate is not supported: the hosted API reveals (or creates on first use) the workspace webhook signing secret but does not rotate it. Run `aex webhooks secret` to reveal the current value.\n");
4440
+ return USAGE_ERR;
4441
+ }
4442
+ if (remaining.length > 0) {
4443
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4077
4444
  `);
4078
- return SUCCESS;
4445
+ io2.stderr("usage: aex webhooks secret [common flags]\n");
4446
+ return USAGE_ERR;
4447
+ }
4448
+ const http = makeHttpClient(io2, common.flags);
4449
+ try {
4450
+ const { whsec } = await operations_exports.getWebhookSigningSecret(http);
4451
+ io2.stdout(`${whsec}
4452
+ `);
4453
+ return SUCCESS;
4454
+ } catch (err2) {
4455
+ const d = describeApiError(err2);
4456
+ return emitJsonError(io2, "webhooks_secret_failed", d.message, {
4457
+ ...d.status !== void 0 ? { status: d.status } : {},
4458
+ ...d.remedy ? { remedy: d.remedy } : {}
4459
+ });
4460
+ }
4461
+ }
4462
+
4463
+ // dist/host/list-cmds.js
4464
+ function parseLimit2(io2, raw) {
4465
+ if (raw === void 0)
4466
+ return { ok: true, limit: void 0 };
4467
+ const limit = Number(raw);
4468
+ if (!Number.isInteger(limit) || limit < 1) {
4469
+ io2.stderr(`--limit must be a positive integer (got: ${raw})
4470
+ `);
4471
+ return { ok: false };
4472
+ }
4473
+ return { ok: true, limit };
4474
+ }
4475
+ async function runRunsCmd(io2, argv) {
4476
+ if (await refuseInsideManagedRun(io2, "runs"))
4477
+ return USAGE_ERR;
4478
+ const common = await resolveCommonHostFlags(io2, argv);
4479
+ if (!common.ok) {
4480
+ io2.stderr(`${common.reason}
4481
+ `);
4482
+ return USAGE_ERR;
4483
+ }
4484
+ const { value: rawLimit, remaining: afterLimit } = takeOptionFlag(common.rest, "--limit");
4485
+ const { value: since, remaining } = takeOptionFlag(afterLimit, "--since");
4486
+ if (remaining.length > 0) {
4487
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4488
+ `);
4489
+ io2.stderr("usage: aex runs [--limit N] [--since ISO-8601] [common flags]\n");
4490
+ return USAGE_ERR;
4491
+ }
4492
+ const parsed = parseLimit2(io2, rawLimit);
4493
+ if (!parsed.ok)
4494
+ return USAGE_ERR;
4495
+ const sinceMs = since !== void 0 ? Date.parse(since) : void 0;
4496
+ if (sinceMs !== void 0 && Number.isNaN(sinceMs)) {
4497
+ io2.stderr(`--since must be an ISO-8601 timestamp (got: ${since})
4498
+ `);
4499
+ return USAGE_ERR;
4500
+ }
4501
+ const http = makeHttpClient(io2, common.flags);
4502
+ try {
4503
+ const page = await operations_exports.listRuns(http, {
4504
+ ...parsed.limit !== void 0 ? { limit: parsed.limit } : {},
4505
+ ...since !== void 0 ? { since } : {}
4506
+ });
4507
+ const runs = sinceMs === void 0 ? page.runs : page.runs.filter((run) => {
4508
+ const created = Date.parse(run.createdAt);
4509
+ return !Number.isNaN(created) && created >= sinceMs;
4510
+ });
4511
+ io2.stdout(JSON.stringify({ ...page, runs }) + "\n");
4512
+ return SUCCESS;
4513
+ } catch (err2) {
4514
+ const d = describeApiError(err2);
4515
+ return emitJsonError(io2, "runs_failed", d.message, {
4516
+ ...d.status !== void 0 ? { status: d.status } : {},
4517
+ ...d.remedy ? { remedy: d.remedy } : {}
4518
+ });
4519
+ }
4520
+ }
4521
+ async function runSessionsCmd(io2, argv) {
4522
+ if (await refuseInsideManagedRun(io2, "sessions"))
4523
+ return USAGE_ERR;
4524
+ const common = await resolveCommonHostFlags(io2, argv);
4525
+ if (!common.ok) {
4526
+ io2.stderr(`${common.reason}
4527
+ `);
4528
+ return USAGE_ERR;
4529
+ }
4530
+ const { value: rawLimit, remaining } = takeOptionFlag(common.rest, "--limit");
4531
+ if (remaining.length > 0) {
4532
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4533
+ `);
4534
+ io2.stderr("usage: aex sessions [--limit N] [common flags]\n");
4535
+ return USAGE_ERR;
4536
+ }
4537
+ const parsed = parseLimit2(io2, rawLimit);
4538
+ if (!parsed.ok)
4539
+ return USAGE_ERR;
4540
+ const http = makeHttpClient(io2, common.flags);
4541
+ try {
4542
+ const page = await operations_exports.listSessions(http, parsed.limit !== void 0 ? { limit: parsed.limit } : void 0);
4543
+ io2.stdout(JSON.stringify(page) + "\n");
4544
+ return SUCCESS;
4545
+ } catch (err2) {
4546
+ const d = describeApiError(err2);
4547
+ return emitJsonError(io2, "sessions_failed", d.message, {
4548
+ ...d.status !== void 0 ? { status: d.status } : {},
4549
+ ...d.remedy ? { remedy: d.remedy } : {}
4550
+ });
4551
+ }
4079
4552
  }
4080
4553
 
4081
4554
  // dist/host/debug.js
@@ -5145,7 +5618,7 @@ async function runTailCmd(io2, argv) {
5145
5618
  }
5146
5619
  if (isSessionOk(finalStatus))
5147
5620
  return SUCCESS;
5148
- if (isSessionParked(finalStatus))
5621
+ if (isSessionParked2(finalStatus))
5149
5622
  return RUNTIME_ERR;
5150
5623
  io2.stderr(JSON.stringify({
5151
5624
  error: "tail_ended_before_terminal",
@@ -5354,10 +5827,16 @@ async function dispatch(io2, args) {
5354
5827
  return runDeleteCmd(io2, rest);
5355
5828
  case "delete-asset":
5356
5829
  return runDeleteAssetCmd(io2, rest);
5830
+ case "runs":
5831
+ return runRunsCmd(io2, rest);
5832
+ case "sessions":
5833
+ return runSessionsCmd(io2, rest);
5357
5834
  case "whoami":
5358
5835
  return runWhoamiCmd(io2, rest);
5359
- case "redeem":
5360
- return runRedeemCmd(io2, rest);
5836
+ case "billing":
5837
+ return runBillingCmd(io2, rest);
5838
+ case "webhooks":
5839
+ return runWebhooksCmd(io2, rest);
5361
5840
  case "login":
5362
5841
  return runLoginCmd(io2, rest);
5363
5842
  case "logout":
@@ -5397,8 +5876,14 @@ async function printGlobalHelp(io2) {
5397
5876
  io2.stdout(" aex cancel <session-id> --api-token T\n");
5398
5877
  io2.stdout(" aex delete <session-id> --api-token T\n");
5399
5878
  io2.stdout(" aex delete-asset <assetId|hash> --api-token T\n");
5879
+ io2.stdout(" aex runs [--limit N] [--since ISO] --api-token T List the workspace's runs (newest first, JSON)\n");
5880
+ io2.stdout(" aex sessions [--limit N] --api-token T List the workspace's sessions (newest first, JSON)\n");
5400
5881
  io2.stdout(" aex whoami --api-token T\n");
5401
- io2.stdout(" aex redeem <code> --api-token T Redeem a coupon code into the workspace prepaid balance\n");
5882
+ io2.stdout(" aex billing [--json] --api-token T Show prepaid balance, month spend, and spend cap\n");
5883
+ io2.stdout(" aex billing ledger [--limit N] --api-token T Recent credit-ledger entries (newest first, JSON)\n");
5884
+ io2.stdout(" aex billing upgrade pro|team --api-token T Create a hosted checkout session and print its URL\n");
5885
+ io2.stdout(" aex billing portal --api-token T Create a hosted billing portal session and print its URL\n");
5886
+ io2.stdout(" aex webhooks secret --api-token T Reveal (create on first use) the webhook signing secret\n");
5402
5887
  io2.stdout(" aex login --api-token T [--aex-url U] Persist token + url (then other verbs need no --api-token)\n");
5403
5888
  io2.stdout(" aex logout Clear the stored token\n");
5404
5889
  io2.stdout(" aex auth status Show the resolved config (token never printed)\n");
@@ -5427,7 +5912,7 @@ async function printGlobalHelp(io2) {
5427
5912
  io2.stdout(" --mcp-auth name=Hdr:Val Auth header on the matching --mcp; routed into vaulted secrets (repeatable)\n");
5428
5913
  io2.stdout(" --metadata key=value Submission metadata entry (repeatable)\n");
5429
5914
  io2.stdout(" --runtime-size <size> managed runtime preset\n");
5430
- io2.stdout(" --run-timeout <dur> Server-side run deadline (e.g. 1h); distinct from --timeout\n");
5915
+ io2.stdout(" --run-timeout <dur> Server-side run deadline (e.g. 1h, max 8h); distinct from --timeout\n");
5431
5916
  io2.stdout(" --idempotency-key <key> Optional; defaults to a fresh UUID\n");
5432
5917
  io2.stdout(" --webhook <url> Optional per-run callback URL (https); receives the terminal run.finished event\n");
5433
5918
  io2.stdout(" --follow Poll events to stdout until the run terminates\n");