@aexhq/sdk 0.35.0 → 0.37.0

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 (72) hide show
  1. package/README.md +17 -16
  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/index.d.ts +3 -4
  6. package/dist/_contracts/index.js +1 -4
  7. package/dist/_contracts/operations.d.ts +31 -1
  8. package/dist/_contracts/operations.js +64 -1
  9. package/dist/_contracts/run-config.d.ts +2 -4
  10. package/dist/_contracts/run-config.js +2 -7
  11. package/dist/_contracts/run-trace.d.ts +0 -86
  12. package/dist/_contracts/run-trace.js +1 -184
  13. package/dist/_contracts/run-unit.d.ts +14 -25
  14. package/dist/_contracts/run-unit.js +56 -2
  15. package/dist/_contracts/runtime-manifest.d.ts +1 -1
  16. package/dist/_contracts/runtime-security-profile.d.ts +0 -2
  17. package/dist/_contracts/runtime-security-profile.js +0 -9
  18. package/dist/_contracts/runtime-sizes.d.ts +2 -2
  19. package/dist/_contracts/runtime-sizes.js +5 -5
  20. package/dist/_contracts/runtime-types.d.ts +123 -4
  21. package/dist/_contracts/stable.d.ts +1 -1
  22. package/dist/_contracts/stable.js +1 -1
  23. package/dist/_contracts/submission.d.ts +8 -76
  24. package/dist/_contracts/submission.js +5 -472
  25. package/dist/cli.mjs +574 -511
  26. package/dist/cli.mjs.sha256 +1 -1
  27. package/dist/client.d.ts +69 -25
  28. package/dist/client.js +338 -68
  29. package/dist/client.js.map +1 -1
  30. package/dist/index.d.ts +8 -16
  31. package/dist/index.js +5 -17
  32. package/dist/index.js.map +1 -1
  33. package/dist/secret.d.ts +2 -2
  34. package/dist/secret.js +1 -1
  35. package/dist/version.d.ts +1 -1
  36. package/dist/version.js +1 -1
  37. package/docs/authentication.md +92 -0
  38. package/docs/billing.md +112 -0
  39. package/docs/concepts/agent-tools.md +4 -4
  40. package/docs/concepts/composition.md +8 -14
  41. package/docs/concepts/providers-and-runtimes.md +4 -1
  42. package/docs/concepts/runs.md +2 -1
  43. package/docs/concepts/subagents.md +85 -0
  44. package/docs/credentials.md +78 -96
  45. package/docs/defaults.md +9 -15
  46. package/docs/errors.md +132 -0
  47. package/docs/events.md +44 -32
  48. package/docs/limits-and-quotas.md +30 -17
  49. package/docs/limits.md +4 -8
  50. package/docs/mcp.md +5 -6
  51. package/docs/networking.md +75 -59
  52. package/docs/outputs.md +4 -7
  53. package/docs/public-surface.json +4 -4
  54. package/docs/quickstart.md +12 -13
  55. package/docs/run-config.md +7 -4
  56. package/docs/secrets.md +6 -1
  57. package/docs/skills.md +3 -3
  58. package/docs/vision-skills.md +52 -101
  59. package/docs/webhooks.md +132 -0
  60. package/examples/feature-tour.ts +4 -21
  61. package/package.json +1 -1
  62. package/dist/_contracts/proxy-protocol.d.ts +0 -305
  63. package/dist/_contracts/proxy-protocol.js +0 -297
  64. package/dist/_contracts/proxy-validation.d.ts +0 -19
  65. package/dist/_contracts/proxy-validation.js +0 -51
  66. package/dist/data-tools.d.ts +0 -82
  67. package/dist/data-tools.js +0 -251
  68. package/dist/data-tools.js.map +0 -1
  69. package/dist/proxy-endpoint.d.ts +0 -131
  70. package/dist/proxy-endpoint.js +0 -144
  71. package/dist/proxy-endpoint.js.map +0 -1
  72. package/examples/chat-corpus.ts +0 -84
package/dist/cli.mjs CHANGED
@@ -10,34 +10,6 @@ import { readFile, writeFile, readdir, stat, mkdir, chmod, rm } from "node:fs/pr
10
10
  import { resolve as resolvePath4, join, dirname as dirname2 } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
 
13
- // ../contracts/dist/proxy-protocol.js
14
- var PROXY_PROTOCOL_VERSION_V2 = "2";
15
- var PROXY_PROTOCOL_HEADER = "x-aex-proxy-protocol";
16
- var PROXY_RESP_STATUS_HEADER = "x-aex-proxy-status";
17
- var PROXY_RESP_MODE_HEADER = "x-aex-proxy-effective-mode";
18
- var PROXY_RESP_TRUNCATED_HEADER = "x-aex-proxy-truncated";
19
- var PROXY_RESP_UPSTREAM_HEADERS_HEADER = "x-aex-proxy-upstream-headers";
20
- var PROXY_METHOD_HEADER = "x-aex-method";
21
- var PROXY_PATH_HEADER = "x-aex-path";
22
- var PROXY_QUERY_HEADER = "x-aex-query";
23
- var PROXY_HEADERS_HEADER = "x-aex-headers";
24
- var PROXY_RESPONSE_MODE_HEADER = "x-aex-response-mode";
25
- var PROXY_RESPONSE_MODES = ["status_only", "headers_only", "full"];
26
- var PROXY_ENDPOINT_DEFAULTS = {
27
- allowHeaders: [],
28
- responseMode: "headers_only",
29
- // 10 MiB. The body is buffered in the hosted API to enforce this cap, while the
30
- // launch default fits practical multimodal/tool POSTs without every endpoint
31
- // needing an override.
32
- maxRequestBytes: 10 * 1024 * 1024,
33
- // Unlimited (0). The request body is buffered to enforce its cap, so that
34
- // stays finite; the response is streamed, so it does not need one.
35
- maxResponseBytes: 0,
36
- // 5 minutes. Long-running upstream tool/model calls should not fail under a
37
- // development-oriented 10s ceiling; endpoints can still set a smaller value.
38
- timeoutMs: 5 * 60 * 1e3
39
- };
40
-
41
13
  // ../contracts/dist/provider-support.js
42
14
  var COMMON_DOCS = [
43
15
  { label: "Secrets", href: "secrets.md" },
@@ -156,6 +128,46 @@ var MODEL_PROVIDER_IDS = {
156
128
  }
157
129
  };
158
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
+ };
159
171
  var PROVIDERS_BY_MODEL = (() => {
160
172
  const map = {};
161
173
  for (const [model, providers] of Object.entries(MODEL_PROVIDER_IDS)) {
@@ -199,6 +211,15 @@ var TERMINAL_RUN_STATUSES = [
199
211
  "cleanup_failed"
200
212
  ];
201
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
+ ];
202
223
 
203
224
  // ../contracts/dist/run-config.js
204
225
  var SKILL_BUNDLE_LIMITS = {
@@ -392,7 +413,6 @@ function parseRunRequestConfig(input) {
392
413
  "environment",
393
414
  "runtimeSize",
394
415
  "timeout",
395
- "proxyEndpoints",
396
416
  "metadata"
397
417
  ]);
398
418
  for (const key of Object.keys(record)) {
@@ -412,14 +432,13 @@ function parseRunRequestConfig(input) {
412
432
  ...system !== void 0 ? { system } : {},
413
433
  prompt,
414
434
  ...mcpServers !== void 0 ? { mcpServers } : {},
415
- // environment / proxyEndpoints / metadata: passed through
416
- // as-is — the BFF revalidates them via `parseRunSubmissionRequest`,
435
+ // environment / metadata: passed through as-is — the BFF revalidates
436
+ // them via `parseRunSubmissionRequest`,
417
437
  // so duplicating the heavyweight parsers here would mean two sources
418
438
  // of truth. The CLI surfaces structural errors at submission time.
419
439
  ...record.environment !== void 0 ? { environment: record.environment } : {},
420
440
  ...record.runtimeSize !== void 0 ? { runtimeSize: record.runtimeSize } : {},
421
441
  ...record.timeout !== void 0 ? { timeout: record.timeout } : {},
422
- ...record.proxyEndpoints !== void 0 ? { proxyEndpoints: record.proxyEndpoints } : {},
423
442
  ...record.metadata !== void 0 ? { metadata: record.metadata } : {}
424
443
  };
425
444
  }
@@ -475,8 +494,8 @@ var RUNTIME_SIZE_PRESETS = {
475
494
  };
476
495
  var RUNTIME_SIZES = Object.keys(RUNTIME_SIZE_PRESETS);
477
496
  var DEFAULT_RUNTIME_SIZE = "shared-0.25x-1gb";
478
- var DEFAULT_RUN_TIMEOUT_MS = 60 * 60 * 1e3;
479
- 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;
480
499
  var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
481
500
  var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
482
501
  var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
@@ -489,7 +508,6 @@ var RUNTIME_SECURITY_PROFILE_CONFIG = Object.freeze({
489
508
  allowOpenNetworking: false,
490
509
  allowRuntimePackages: false,
491
510
  allowCustomerEnvVars: true,
492
- allowProxyEndpoints: true,
493
511
  allowMcpServers: true
494
512
  }),
495
513
  standard: Object.freeze({
@@ -498,7 +516,6 @@ var RUNTIME_SECURITY_PROFILE_CONFIG = Object.freeze({
498
516
  allowOpenNetworking: true,
499
517
  allowRuntimePackages: true,
500
518
  allowCustomerEnvVars: true,
501
- allowProxyEndpoints: true,
502
519
  allowMcpServers: true
503
520
  }),
504
521
  developer: Object.freeze({
@@ -507,12 +524,12 @@ var RUNTIME_SECURITY_PROFILE_CONFIG = Object.freeze({
507
524
  allowOpenNetworking: true,
508
525
  allowRuntimePackages: true,
509
526
  allowCustomerEnvVars: true,
510
- allowProxyEndpoints: true,
511
527
  allowMcpServers: true
512
528
  })
513
529
  });
514
530
 
515
531
  // ../contracts/dist/submission.js
532
+ var PLATFORM_PACKAGE_ECOSYSTEMS = ["apt", "npm", "pip"];
516
533
  var RUN_PROVIDERS = [
517
534
  "anthropic",
518
535
  "deepseek",
@@ -524,9 +541,6 @@ var RUN_PROVIDERS = [
524
541
  "doubao-cn"
525
542
  ];
526
543
  var DEFAULT_RUN_PROVIDER = "anthropic";
527
- var MIN_REDACTION_TARGET_BYTES = 4;
528
- var MIN_PROXY_SECRET_BYTES = 8;
529
- var _MIN_PROXY_SECRET_BYTES_OK = MIN_PROXY_SECRET_BYTES >= MIN_REDACTION_TARGET_BYTES;
530
544
  var BUILTIN_TOOL_NAMES = [
531
545
  "bash",
532
546
  "read_file",
@@ -566,12 +580,17 @@ var AEX_EVENT_TYPES = [
566
580
  // off-the-shelf AG-UI client filters logs out by `channel`.
567
581
  "LOG"
568
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
+ }
569
588
  function customName(e) {
570
589
  return e.type === "CUSTOM" ? str(e.data.name) || null : null;
571
590
  }
572
591
  var AEX_RUN_SETTLED_NAME = "aex.run.settled";
573
592
  function isRunSettled(e) {
574
- return customName(e) === AEX_RUN_SETTLED_NAME;
593
+ return customName(e) === AEX_RUN_SETTLED_NAME || isSessionParked(e);
575
594
  }
576
595
  function channelOf(e) {
577
596
  return e.channel ?? "event";
@@ -629,7 +648,7 @@ function str(v) {
629
648
  var encoder = new TextEncoder();
630
649
 
631
650
  // ../contracts/dist/event-stream-client.js
632
- 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);
633
652
  var COORDINATOR_PING = "aex:ping";
634
653
  var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
635
654
  var DEFAULT_PING_INTERVAL_MS = 15e3;
@@ -787,6 +806,191 @@ function sleep(ms, signal) {
787
806
  });
788
807
  }
789
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
+
790
994
  // ../contracts/dist/runtime-manifest.js
791
995
  var RUNTIME_PATHS = Object.freeze({
792
996
  skillsRoot: "/workspace/skills",
@@ -1365,6 +1569,8 @@ __export(operations_exports, {
1365
1569
  cancelRun: () => cancelRun,
1366
1570
  cancelSession: () => cancelSession,
1367
1571
  classifyOutput: () => classifyOutput,
1572
+ createBillingCheckout: () => createBillingCheckout,
1573
+ createBillingPortal: () => createBillingPortal,
1368
1574
  createOutputLink: () => createOutputLink,
1369
1575
  createSecret: () => createSecret,
1370
1576
  createSession: () => createSession,
@@ -1384,6 +1590,8 @@ __export(operations_exports, {
1384
1590
  findOutput: () => findOutput,
1385
1591
  findOutputs: () => findOutputs,
1386
1592
  getAgentsMd: () => getAgentsMd,
1593
+ getBilling: () => getBilling,
1594
+ getBillingLedger: () => getBillingLedger,
1387
1595
  getCoordinatorTicket: () => getCoordinatorTicket,
1388
1596
  getFile: () => getFile,
1389
1597
  getRun: () => getRun,
@@ -1393,6 +1601,7 @@ __export(operations_exports, {
1393
1601
  getSecretValue: () => getSecretValue,
1394
1602
  getSession: () => getSession,
1395
1603
  getSessionCoordinatorTicket: () => getSessionCoordinatorTicket,
1604
+ getWebhookSigningSecret: () => getWebhookSigningSecret,
1396
1605
  listAgentsMd: () => listAgentsMd,
1397
1606
  listFiles: () => listFiles,
1398
1607
  listOutputs: () => listOutputs,
@@ -1400,6 +1609,7 @@ __export(operations_exports, {
1400
1609
  listRuns: () => listRuns,
1401
1610
  listSecrets: () => listSecrets,
1402
1611
  listSessionEvents: () => listSessionEvents,
1612
+ listSessionMessages: () => listSessionMessages,
1403
1613
  listSessionOutputs: () => listSessionOutputs,
1404
1614
  listSessions: () => listSessions,
1405
1615
  normalizeOutputLinkExpiresIn: () => normalizeOutputLinkExpiresIn,
@@ -2138,7 +2348,7 @@ async function getRun(http, runId) {
2138
2348
  return hasRun(result) ? result.run : result;
2139
2349
  }
2140
2350
  async function getRunUnit(http, runId) {
2141
- return http.request(`/api/runs/${encodeURIComponent(runId)}`);
2351
+ return normalizeRunUnit(await http.request(`/api/runs/${encodeURIComponent(runId)}`));
2142
2352
  }
2143
2353
  async function listRuns(http, query) {
2144
2354
  const params = {};
@@ -2188,6 +2398,16 @@ async function sendSessionMessage(http, sessionId, request, options) {
2188
2398
  body: JSON.stringify(request)
2189
2399
  });
2190
2400
  }
2401
+ async function listSessionMessages(http, sessionId, query) {
2402
+ const params = {};
2403
+ if (query?.limit !== void 0)
2404
+ params.limit = String(query.limit);
2405
+ if (query?.cursor !== void 0)
2406
+ params.cursor = query.cursor;
2407
+ if (query?.since !== void 0)
2408
+ params.since = query.since;
2409
+ return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/messages`, {}, params);
2410
+ }
2191
2411
  async function suspendSession(http, sessionId, options) {
2192
2412
  const headers = idempotencyHeaders(options);
2193
2413
  return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/suspend`, { method: "POST", ...headers ? { headers } : {} });
@@ -2411,6 +2631,30 @@ async function deleteWorkspaceAsset(http, hash) {
2411
2631
  async function whoami(http) {
2412
2632
  return http.request("/api/whoami");
2413
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
+ }
2414
2658
  async function collectArtifactBytes(http, runId, items, zipPrefix, namespace2) {
2415
2659
  const entries = [];
2416
2660
  const captured = [];
@@ -2677,7 +2921,7 @@ function jsonlEntry(path, events) {
2677
2921
  }
2678
2922
  function extractSubmissionSnapshot(run) {
2679
2923
  const raw = run.submission;
2680
- if (!isRecord(raw) || raw.kind !== "submission" || !isRecord(raw.submission)) {
2924
+ if (!isRecord2(raw) || raw.kind !== "submission" || !isRecord2(raw.submission)) {
2681
2925
  return void 0;
2682
2926
  }
2683
2927
  return {
@@ -2686,9 +2930,9 @@ function extractSubmissionSnapshot(run) {
2686
2930
  }
2687
2931
  function extractCostTelemetry(run) {
2688
2932
  const raw = run.costTelemetry;
2689
- return isRecord(raw) ? raw : void 0;
2933
+ return isRecord2(raw) ? raw : void 0;
2690
2934
  }
2691
- function isRecord(value) {
2935
+ function isRecord2(value) {
2692
2936
  return typeof value === "object" && value !== null && !Array.isArray(value);
2693
2937
  }
2694
2938
  async function submitRun(http, request) {
@@ -2800,34 +3044,8 @@ async function uploadWorkspaceAsset(http, input) {
2800
3044
  });
2801
3045
  }
2802
3046
 
2803
- // ../contracts/dist/proxy-validation.js
2804
- function validateProxyAuth(endpoints, auth) {
2805
- const authList = auth ?? [];
2806
- const endpointNames = new Set(endpoints.map((e) => e.name));
2807
- const authNames = /* @__PURE__ */ new Set();
2808
- for (const entry of authList) {
2809
- if (authNames.has(entry.name)) {
2810
- throw new Error(`secrets.proxyEndpointAuth contains duplicate name '${entry.name}'`);
2811
- }
2812
- authNames.add(entry.name);
2813
- if (!endpointNames.has(entry.name)) {
2814
- throw new Error(`secrets.proxyEndpointAuth[].name='${entry.name}' has no matching proxyEndpoints[].name`);
2815
- }
2816
- }
2817
- for (const endpoint of endpoints) {
2818
- const match = authList.find((a) => a.name === endpoint.name);
2819
- if (!match) {
2820
- throw new Error(`proxyEndpoints[].name='${endpoint.name}' is missing a matching secrets.proxyEndpointAuth entry`);
2821
- }
2822
- if (match.value.type !== endpoint.authShape.type) {
2823
- throw new Error(`secrets.proxyEndpointAuth[name='${endpoint.name}'].value.type='${match.value.type}' does not match proxyEndpoints[name='${endpoint.name}'].authShape.type='${endpoint.authShape.type}'`);
2824
- }
2825
- }
2826
- }
2827
-
2828
3047
  // dist/internal.js
2829
3048
  var AEX_INDEX_PATH = "/mnt/session/uploads/aex/index.json";
2830
- var AEX_RUN_TOKEN_PATH = "/mnt/session/uploads/aex/run-token";
2831
3049
 
2832
3050
  // dist/host/common.js
2833
3051
  var SUCCESS = { code: 0 };
@@ -2835,7 +3053,7 @@ var USAGE_ERR = { code: 2 };
2835
3053
  var RUNTIME_ERR = { code: 1 };
2836
3054
  var TIMEOUT_ERR = { code: 3 };
2837
3055
  var TERMINAL_STATUSES = new Set(TERMINAL_RUN_STATUSES);
2838
- function isSessionParked(status2) {
3056
+ function isSessionParked2(status2) {
2839
3057
  return status2 === "idle" || status2 === "suspended" || status2 === "error" || TERMINAL_STATUSES.has(status2);
2840
3058
  }
2841
3059
  function isSessionOk(status2) {
@@ -2922,6 +3140,8 @@ function describeApiError(err2) {
2922
3140
  return { code: "error", message: err2 instanceof Error ? err2.message : String(err2) };
2923
3141
  }
2924
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`";
2925
3145
  if (status2 === 401)
2926
3146
  return "check --api-token, or run `aex login`";
2927
3147
  if (status2 === 403)
@@ -2986,7 +3206,7 @@ async function refuseInsideManagedRun(io2, verb) {
2986
3206
  try {
2987
3207
  await io2.readFile(AEX_INDEX_PATH);
2988
3208
  io2.stderr(`\`aex ${verb}\` is a host command and cannot run inside a managed run container.
2989
- Use \`aex proxy ...\` to call your declared upstream endpoints from inside the run.
3209
+ Make HTTP calls from your code and pass credentials through secrets.
2990
3210
  `);
2991
3211
  return true;
2992
3212
  } catch (err2) {
@@ -3171,285 +3391,6 @@ async function runOutputsSyncCmd(io2, dirs) {
3171
3391
  return SUCCESS;
3172
3392
  }
3173
3393
 
3174
- // dist/proxy.js
3175
- function parseProxyFlags(rest) {
3176
- let endpointName = null;
3177
- let method = "GET";
3178
- let path = "/";
3179
- let query = null;
3180
- const headers = /* @__PURE__ */ new Map();
3181
- let dataSpec = null;
3182
- let responseMode = null;
3183
- let showHelp = false;
3184
- for (let i2 = 0; i2 < rest.length; i2++) {
3185
- const arg = rest[i2];
3186
- if (arg === "--help" || arg === "-h") {
3187
- showHelp = true;
3188
- continue;
3189
- }
3190
- if (arg === "--method") {
3191
- method = expect(rest, ++i2, "--method");
3192
- continue;
3193
- }
3194
- if (arg === "--path") {
3195
- path = expect(rest, ++i2, "--path");
3196
- continue;
3197
- }
3198
- if (arg === "--query") {
3199
- query = expect(rest, ++i2, "--query");
3200
- continue;
3201
- }
3202
- if (arg === "--header") {
3203
- const kv = expect(rest, ++i2, "--header");
3204
- const eq = kv.indexOf("=");
3205
- if (eq <= 0)
3206
- return { ok: false, reason: "--header must be in the form KEY=VALUE" };
3207
- headers.set(kv.slice(0, eq).toLowerCase(), kv.slice(eq + 1));
3208
- continue;
3209
- }
3210
- if (arg === "--data") {
3211
- dataSpec = expect(rest, ++i2, "--data");
3212
- continue;
3213
- }
3214
- if (arg === "--response-mode") {
3215
- responseMode = expect(rest, ++i2, "--response-mode");
3216
- continue;
3217
- }
3218
- if (arg.startsWith("--")) {
3219
- return { ok: false, reason: `unknown flag: ${arg}` };
3220
- }
3221
- if (endpointName === null) {
3222
- endpointName = arg;
3223
- continue;
3224
- }
3225
- return { ok: false, reason: `unexpected positional argument: ${arg}` };
3226
- }
3227
- return { ok: true, flags: { endpointName, method, path, query, headers, dataSpec, responseMode, showHelp } };
3228
- }
3229
- function expect(arr, idx, flag) {
3230
- const v = arr[idx];
3231
- if (v === void 0) {
3232
- throw new CliUsageError(`${flag} requires a value`);
3233
- }
3234
- return v;
3235
- }
3236
- var CliUsageError = class extends Error {
3237
- };
3238
- async function printProxyHelp(io2) {
3239
- io2.stdout("aex proxy \u2014 call an upstream HTTP endpoint via the managed proxy.\n\n");
3240
- io2.stdout("Usage:\n");
3241
- io2.stdout(" aex proxy <endpoint-name> [flags]\n\n");
3242
- io2.stdout("Flags:\n");
3243
- io2.stdout(" --method <verb> HTTP method (default: GET)\n");
3244
- io2.stdout(" --path <path> Caller-supplied path; must match policy prefixes\n");
3245
- io2.stdout(` --query <json> JSON object of query parameters (e.g. '{"q":"x"}')
3246
- `);
3247
- io2.stdout(" --header K=V Add a caller header (repeatable)\n");
3248
- io2.stdout(" --data <value> Request body. Use '-' for stdin, '@<file>' for file content\n");
3249
- io2.stdout(" --response-mode <mode> status_only | headers_only | full (may only narrow policy)\n");
3250
- io2.stdout(" --help Show this message\n\n");
3251
- const manifest = await tryReadManifest(io2);
3252
- if (manifest && manifest.endpoints.length > 0) {
3253
- io2.stdout("Declared endpoints:\n");
3254
- for (const ep of manifest.endpoints) {
3255
- io2.stdout(` \u2022 ${formatProxyEndpointSummary(ep)}
3256
- `);
3257
- }
3258
- }
3259
- return SUCCESS;
3260
- }
3261
- function formatProxyEndpointSummary(ep) {
3262
- const retry = ep.retry ? `, retry=${ep.retry.maxAttempts}x ${ep.retry.retryOnMethods.join("/")} ${ep.retry.retryOnStatuses.join("/")} delay=${ep.retry.initialDelayMs}-${ep.retry.maxDelayMs}ms jitter=${ep.retry.jitter}${ep.retry.respectRetryAfter ? " retry-after" : ""}` : "";
3263
- return `${ep.name}: ${ep.allowMethods.join(",")} ${ep.allowPathPrefixes.join(",")} (mode=${ep.responseMode}${retry})`;
3264
- }
3265
- async function runProxy(io2, rest) {
3266
- let parsed;
3267
- try {
3268
- parsed = parseProxyFlags(rest);
3269
- } catch (err2) {
3270
- if (err2 instanceof CliUsageError) {
3271
- io2.stderr(`${err2.message}
3272
- `);
3273
- return USAGE_ERR;
3274
- }
3275
- throw err2;
3276
- }
3277
- if (!parsed.ok) {
3278
- io2.stderr(`${parsed.reason}
3279
- `);
3280
- return USAGE_ERR;
3281
- }
3282
- const f = parsed.flags;
3283
- if (f.showHelp) {
3284
- return await printProxyHelp(io2);
3285
- }
3286
- if (!f.endpointName) {
3287
- io2.stderr("missing endpoint-name\n");
3288
- io2.stderr("usage: aex proxy <endpoint-name> [flags]\n");
3289
- return USAGE_ERR;
3290
- }
3291
- if (f.responseMode && !PROXY_RESPONSE_MODES.includes(f.responseMode)) {
3292
- io2.stderr(`--response-mode must be one of: ${PROXY_RESPONSE_MODES.join(", ")}
3293
- `);
3294
- return USAGE_ERR;
3295
- }
3296
- const manifest = await tryReadManifest(io2);
3297
- if (!manifest) {
3298
- emitError(io2, {
3299
- error: "internal_error",
3300
- message: "manifest not mounted; this CLI must run inside an aex-managed run"
3301
- });
3302
- return RUNTIME_ERR;
3303
- }
3304
- if (!manifest.proxyBaseUrl) {
3305
- emitError(io2, {
3306
- error: "endpoint_not_found",
3307
- message: "this run has no proxy endpoints declared",
3308
- endpointName: f.endpointName
3309
- });
3310
- return RUNTIME_ERR;
3311
- }
3312
- let token;
3313
- try {
3314
- token = (await io2.readFile(AEX_RUN_TOKEN_PATH)).trim();
3315
- } catch {
3316
- emitError(io2, {
3317
- error: "unauthorized",
3318
- message: "run token file missing; this run has no proxy bearer"
3319
- });
3320
- return RUNTIME_ERR;
3321
- }
3322
- if (!token) {
3323
- emitError(io2, { error: "unauthorized", message: "run token is empty" });
3324
- return RUNTIME_ERR;
3325
- }
3326
- let body;
3327
- if (f.dataSpec !== null) {
3328
- try {
3329
- body = await resolveBody(io2, f.dataSpec);
3330
- } catch (err2) {
3331
- io2.stderr(`failed to read request body: ${err2.message}
3332
- `);
3333
- return RUNTIME_ERR;
3334
- }
3335
- }
3336
- const url = `${manifest.proxyBaseUrl.replace(/\/+$/, "")}/${encodeURIComponent(f.endpointName)}`;
3337
- const requestHeaders = new Headers();
3338
- requestHeaders.set("authorization", `Bearer ${token}`);
3339
- requestHeaders.set(PROXY_PROTOCOL_HEADER, PROXY_PROTOCOL_VERSION_V2);
3340
- requestHeaders.set(PROXY_METHOD_HEADER, f.method.toUpperCase());
3341
- requestHeaders.set(PROXY_PATH_HEADER, f.path);
3342
- if (f.query) {
3343
- requestHeaders.set(PROXY_QUERY_HEADER, f.query);
3344
- }
3345
- if (f.headers.size > 0) {
3346
- requestHeaders.set(PROXY_HEADERS_HEADER, JSON.stringify(Object.fromEntries(f.headers)));
3347
- }
3348
- if (f.responseMode) {
3349
- requestHeaders.set(PROXY_RESPONSE_MODE_HEADER, f.responseMode);
3350
- }
3351
- if (body !== void 0) {
3352
- requestHeaders.set("content-length", String(body.byteLength));
3353
- }
3354
- const init = {
3355
- method: "POST",
3356
- headers: requestHeaders,
3357
- redirect: "manual"
3358
- };
3359
- if (body !== void 0) {
3360
- init.body = body;
3361
- }
3362
- let response;
3363
- try {
3364
- response = await io2.fetchImpl(url, init);
3365
- } catch (err2) {
3366
- emitError(io2, {
3367
- error: "upstream_error",
3368
- message: `proxy request failed: ${err2.message}`,
3369
- endpointName: f.endpointName
3370
- });
3371
- return RUNTIME_ERR;
3372
- }
3373
- if (response.headers.get(PROXY_RESP_STATUS_HEADER) !== null) {
3374
- const envelope = await readStreamedEnvelope(response, f.endpointName);
3375
- io2.stdout(JSON.stringify(envelope) + "\n");
3376
- return SUCCESS;
3377
- }
3378
- const text = await response.text();
3379
- let parsedBody;
3380
- try {
3381
- parsedBody = text ? JSON.parse(text) : {};
3382
- } catch {
3383
- const snippet = text.slice(0, 200).replace(/\s+/g, " ").trim();
3384
- emitError(io2, {
3385
- error: "internal_error",
3386
- message: `proxy returned non-JSON response (HTTP ${response.status}, body=${JSON.stringify(snippet)})`,
3387
- endpointName: f.endpointName
3388
- });
3389
- return RUNTIME_ERR;
3390
- }
3391
- if (!response.ok) {
3392
- io2.stderr(JSON.stringify(parsedBody) + "\n");
3393
- return RUNTIME_ERR;
3394
- }
3395
- io2.stdout(JSON.stringify(parsedBody) + "\n");
3396
- return SUCCESS;
3397
- }
3398
- async function resolveBody(io2, spec) {
3399
- if (spec === "-") {
3400
- const data = await io2.readFile("/dev/stdin");
3401
- return new Uint8Array(Buffer.from(data, "utf8"));
3402
- }
3403
- if (spec.startsWith("@")) {
3404
- const path = spec.slice(1);
3405
- if (!path)
3406
- throw new Error("--data @<file> requires a path");
3407
- const data = await io2.readFile(path);
3408
- return new Uint8Array(Buffer.from(data, "utf8"));
3409
- }
3410
- return new Uint8Array(Buffer.from(spec, "utf8"));
3411
- }
3412
- function emitError(io2, body) {
3413
- io2.stderr(JSON.stringify(body) + "\n");
3414
- }
3415
- async function readStreamedEnvelope(response, endpointName) {
3416
- const effectiveResponseMode = response.headers.get(PROXY_RESP_MODE_HEADER) ?? "headers_only";
3417
- const upstreamStatus = Number.parseInt(response.headers.get(PROXY_RESP_STATUS_HEADER) ?? "0", 10);
3418
- let upstreamHeaders = {};
3419
- const rawHeaders = response.headers.get(PROXY_RESP_UPSTREAM_HEADERS_HEADER);
3420
- if (rawHeaders) {
3421
- try {
3422
- const parsed = JSON.parse(rawHeaders);
3423
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
3424
- upstreamHeaders = parsed;
3425
- }
3426
- } catch {
3427
- }
3428
- }
3429
- const truncatedRaw = response.headers.get(PROXY_RESP_TRUNCATED_HEADER);
3430
- const bytes = new Uint8Array(await response.arrayBuffer());
3431
- return {
3432
- endpointName,
3433
- upstreamStatus,
3434
- upstreamHeaders,
3435
- effectiveResponseMode,
3436
- // The streamed path can't echo the request-side clamp decision (it
3437
- // isn't carried back); the effective mode is authoritative for the
3438
- // agent and matches what v1 surfaced for an un-clamped call.
3439
- modeClamped: false,
3440
- ...effectiveResponseMode === "full" && bytes.byteLength > 0 ? { upstreamBodyBase64: Buffer.from(bytes).toString("base64") } : {},
3441
- ...truncatedRaw === "true" ? { truncated: true } : {}
3442
- };
3443
- }
3444
- async function tryReadManifest(io2) {
3445
- try {
3446
- const raw = await io2.readFile(AEX_INDEX_PATH);
3447
- return JSON.parse(raw);
3448
- } catch {
3449
- return null;
3450
- }
3451
- }
3452
-
3453
3394
  // dist/host/run-cmd.js
3454
3395
  import { resolve as resolvePath } from "node:path";
3455
3396
  var DEFAULT_SESSION_IDLE_TTL = "3m";
@@ -3616,6 +3557,10 @@ async function runRunCmd(io2, argv) {
3616
3557
  return USAGE_ERR;
3617
3558
  }
3618
3559
  rest = proxyAuthFlags.remaining;
3560
+ if (proxyEndpointFlags.values.length > 0 || Object.keys(proxyAuthFlags.entries).length > 0) {
3561
+ io2.stderr("--proxy-endpoint and --proxy-auth are no longer supported; make HTTP calls from your code and pass credentials via secrets.\n");
3562
+ return USAGE_ERR;
3563
+ }
3619
3564
  const positional = rest.filter((a) => !a.startsWith("--"));
3620
3565
  const unknownFlags = rest.filter((a) => a.startsWith("--"));
3621
3566
  if (unknownFlags.length > 0) {
@@ -3727,35 +3672,6 @@ async function runRunCmd(io2, argv) {
3727
3672
  for (const name of mcpHeaderBag.keys()) {
3728
3673
  if (!mcpServersForSubmission.some((m) => m.name === name)) {
3729
3674
  io2.stderr(`--mcp-auth ${name}: no matching --mcp / mcpServers entry declared
3730
- `);
3731
- return USAGE_ERR;
3732
- }
3733
- }
3734
- let proxyEndpoints = runConfig.proxyEndpoints ?? [];
3735
- if (proxyEndpointFlags.values.length > 0) {
3736
- try {
3737
- proxyEndpoints = proxyEndpointFlags.values.map((raw, i2) => parseJsonOrThrow(raw, `--proxy-endpoint[${i2}]`));
3738
- } catch (err2) {
3739
- io2.stderr(`${err2.message}
3740
- `);
3741
- return USAGE_ERR;
3742
- }
3743
- }
3744
- const proxyAuth = [];
3745
- for (const [name, spec] of Object.entries(proxyAuthFlags.entries)) {
3746
- const parsed = parseProxyAuth(spec);
3747
- if (!parsed.ok) {
3748
- io2.stderr(`--proxy-auth ${name}: ${parsed.reason}
3749
- `);
3750
- return USAGE_ERR;
3751
- }
3752
- proxyAuth.push({ name, value: parsed.value });
3753
- }
3754
- if (proxyEndpoints.length > 0) {
3755
- try {
3756
- validateProxyAuth(proxyEndpoints, proxyAuth);
3757
- } catch (err2) {
3758
- io2.stderr(`proxy auth validation failed: ${err2.message}
3759
3675
  `);
3760
3676
  return USAGE_ERR;
3761
3677
  }
@@ -3774,8 +3690,7 @@ async function runRunCmd(io2, argv) {
3774
3690
  const hasAdditionalProviderKeys = Object.keys(providerKeyValues).some((p) => p !== provider);
3775
3691
  const secrets = {
3776
3692
  apiKeys: hasAdditionalProviderKeys ? providerKeyValues : { [provider]: providerKeyValues[provider] },
3777
- ...mcpServerSecrets.length > 0 ? { mcpServers: mcpServerSecrets } : {},
3778
- ...proxyAuth.length > 0 ? { proxyEndpointAuth: proxyAuth } : {}
3693
+ ...mcpServerSecrets.length > 0 ? { mcpServers: mcpServerSecrets } : {}
3779
3694
  };
3780
3695
  const request = {
3781
3696
  provider,
@@ -3784,8 +3699,7 @@ async function runRunCmd(io2, argv) {
3784
3699
  retention: { idleTtl: DEFAULT_SESSION_IDLE_TTL },
3785
3700
  ...runtimeSizeFlag.value ? { runtimeSize: runtimeSizeFlag.value } : runConfig.runtimeSize ? { runtimeSize: runConfig.runtimeSize } : {},
3786
3701
  ...runTimeoutFlag.value ? { timeout: runTimeoutFlag.value } : runConfig.timeout ? { timeout: runConfig.timeout } : {},
3787
- ...webhookFlag.value ? { webhook: { url: webhookFlag.value } } : {},
3788
- ...proxyEndpoints.length > 0 ? { proxyEndpoints } : {}
3702
+ ...webhookFlag.value ? { webhook: { url: webhookFlag.value } } : {}
3789
3703
  };
3790
3704
  const createKey = idempotency.value ?? generateIdempotencyKey();
3791
3705
  const messageKey = `${createKey}:message`;
@@ -3817,7 +3731,7 @@ async function runRunCmd(io2, argv) {
3817
3731
  const seen = /* @__PURE__ */ new Set();
3818
3732
  let currentStatus = accepted.session.status;
3819
3733
  const deadline = followTimeoutMs === null ? Number.POSITIVE_INFINITY : Date.now() + followTimeoutMs;
3820
- while (!isSessionParked(currentStatus)) {
3734
+ while (!isSessionParked2(currentStatus)) {
3821
3735
  await sleep2(2e3);
3822
3736
  try {
3823
3737
  const events = await operations_exports.listSessionEvents(http, session.id);
@@ -3838,7 +3752,7 @@ async function runRunCmd(io2, argv) {
3838
3752
  io2.stderr(`(transient) status poll failed: ${err2.message}
3839
3753
  `);
3840
3754
  }
3841
- if (!isSessionParked(currentStatus) && Date.now() >= deadline) {
3755
+ if (!isSessionParked2(currentStatus) && Date.now() >= deadline) {
3842
3756
  emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
3843
3757
  sessionId: session.id,
3844
3758
  hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
@@ -3864,47 +3778,6 @@ async function runRunCmd(io2, argv) {
3864
3778
  return RUNTIME_ERR;
3865
3779
  }
3866
3780
  }
3867
- function parseProxyAuth(spec) {
3868
- const idx = spec.indexOf(":");
3869
- if (idx <= 0) {
3870
- return { ok: false, reason: `expected '<type>:<value>' (got: ${spec})` };
3871
- }
3872
- const type = spec.slice(0, idx);
3873
- const restValue = spec.slice(idx + 1);
3874
- switch (type) {
3875
- case "bearer":
3876
- if (!restValue)
3877
- return { ok: false, reason: "bearer requires a token value" };
3878
- return { ok: true, value: { type: "bearer", token: restValue } };
3879
- case "header":
3880
- if (!restValue)
3881
- return { ok: false, reason: "header requires a value" };
3882
- return { ok: true, value: { type: "header", value: restValue } };
3883
- case "query":
3884
- if (!restValue)
3885
- return { ok: false, reason: "query requires a value" };
3886
- return { ok: true, value: { type: "query", value: restValue } };
3887
- case "basic": {
3888
- const sep = restValue.indexOf(":");
3889
- if (sep <= 0 || sep >= restValue.length - 1) {
3890
- return { ok: false, reason: "basic requires <username>:<password>" };
3891
- }
3892
- return {
3893
- ok: true,
3894
- value: { type: "basic", username: restValue.slice(0, sep), password: restValue.slice(sep + 1) }
3895
- };
3896
- }
3897
- default:
3898
- return { ok: false, reason: `unknown auth type '${type}' (expected bearer|basic|header|query)` };
3899
- }
3900
- }
3901
- function parseJsonOrThrow(raw, label) {
3902
- try {
3903
- return JSON.parse(raw);
3904
- } catch (err2) {
3905
- throw new Error(`${label} is not valid JSON: ${err2.message}`);
3906
- }
3907
- }
3908
3781
  function stripMcpHeadersForParsing(input) {
3909
3782
  const mcpHeaders = /* @__PURE__ */ new Map();
3910
3783
  if (input === null || typeof input !== "object" || Array.isArray(input)) {
@@ -4075,7 +3948,7 @@ async function runWaitCmd(io2, argv) {
4075
3948
  await sleep3(intervalMs);
4076
3949
  continue;
4077
3950
  }
4078
- if (isSessionParked(session.status)) {
3951
+ if (isSessionParked2(session.status)) {
4079
3952
  io2.stdout(JSON.stringify(session) + "\n");
4080
3953
  return isSessionOk(session.status) ? SUCCESS : RUNTIME_ERR;
4081
3954
  }
@@ -4155,7 +4028,7 @@ async function runEventsCmd(io2, argv) {
4155
4028
  }
4156
4029
  try {
4157
4030
  const session = await operations_exports.getSession(http, sessionId);
4158
- if (isSessionParked(session.status)) {
4031
+ if (isSessionParked2(session.status)) {
4159
4032
  return SUCCESS;
4160
4033
  }
4161
4034
  } catch (err2) {
@@ -4400,25 +4273,26 @@ async function runWhoamiCmd(io2, argv) {
4400
4273
  }
4401
4274
  }
4402
4275
 
4403
- // dist/host/redeem.js
4404
- function messageForStatus(status2, serverMessage) {
4405
- switch (status2) {
4406
- case 404:
4407
- return "coupon code not found";
4408
- case 403:
4409
- return "this coupon can't be redeemed by this workspace";
4410
- case 409:
4411
- return "coupon already redeemed";
4412
- case 400:
4413
- return serverMessage ? `invalid input: ${serverMessage}` : "invalid input";
4414
- case 401:
4415
- return "not authorized \u2014 check --api-token, or run `aex login`";
4416
- default:
4417
- 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 };
4418
4291
  }
4292
+ return { ok: true, limit };
4419
4293
  }
4420
- async function runRedeemCmd(io2, argv) {
4421
- if (await refuseInsideManagedRun(io2, "redeem"))
4294
+ async function runBillingCmd(io2, argv) {
4295
+ if (await refuseInsideManagedRun(io2, "billing"))
4422
4296
  return USAGE_ERR;
4423
4297
  const common = await resolveCommonHostFlags(io2, argv);
4424
4298
  if (!common.ok) {
@@ -4426,54 +4300,255 @@ async function runRedeemCmd(io2, argv) {
4426
4300
  `);
4427
4301
  return USAGE_ERR;
4428
4302
  }
4429
- const positional = common.rest.filter((arg) => !arg.startsWith("--"));
4430
- if (positional.length !== 1) {
4431
- 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");
4432
4317
  return USAGE_ERR;
4433
4318
  }
4434
- const code = positional[0];
4435
- const base = common.flags.aexUrl.replace(/\/+$/, "");
4436
- const url = `${base}/billing/redeem`;
4437
- let response;
4319
+ const http = makeHttpClient(io2, common.flags);
4438
4320
  try {
4439
- response = await io2.fetchImpl(url, {
4440
- method: "POST",
4441
- headers: {
4442
- accept: "application/json",
4443
- "content-type": "application/json",
4444
- authorization: `Bearer ${common.flags.apiToken}`
4445
- },
4446
- body: JSON.stringify({ code })
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 } : {}
4447
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");
4351
+ return USAGE_ERR;
4352
+ }
4353
+ const http = makeHttpClient(io2, flags);
4354
+ try {
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 } : {}
4360
+ });
4361
+ io2.stdout(json ? `${JSON.stringify(session)}
4362
+ ` : `${session.url}
4363
+ `);
4364
+ return SUCCESS;
4448
4365
  } catch (err2) {
4449
- 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(" ")}
4450
4378
  `);
4451
- return RUNTIME_ERR;
4379
+ io2.stderr("usage: aex billing portal [--return-url URL] [--json] [common flags]\n");
4380
+ return USAGE_ERR;
4452
4381
  }
4453
- if (common.flags.debug) {
4454
- 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}
4455
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
+ });
4456
4395
  }
4457
- const text = await response.text();
4458
- 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);
4459
4409
  try {
4460
- if (text.length > 0)
4461
- body = JSON.parse(text);
4462
- } catch {
4463
- 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
+ });
4464
4419
  }
4465
- if (!response.ok) {
4466
- const serverMessage = body && typeof body === "object" && typeof body.message === "string" ? body.message : void 0;
4467
- 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}
4468
4429
  `);
4469
- 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;
4436
+ }
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;
4470
4441
  }
4471
- const ok = body;
4472
- const amountUsd = typeof ok.amountUsd === "number" ? ok.amountUsd : 0;
4473
- const newBalanceUsd = typeof ok.newBalanceUsd === "number" ? ok.newBalanceUsd : 0;
4474
- io2.stdout(`Redeemed $${amountUsd.toFixed(2)}. New balance: $${newBalanceUsd.toFixed(2)}.
4442
+ if (remaining.length > 0) {
4443
+ io2.stderr(`unexpected arguments: ${remaining.join(" ")}
4475
4444
  `);
4476
- 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
+ }
4477
4552
  }
4478
4553
 
4479
4554
  // dist/host/debug.js
@@ -5543,7 +5618,7 @@ async function runTailCmd(io2, argv) {
5543
5618
  }
5544
5619
  if (isSessionOk(finalStatus))
5545
5620
  return SUCCESS;
5546
- if (isSessionParked(finalStatus))
5621
+ if (isSessionParked2(finalStatus))
5547
5622
  return RUNTIME_ERR;
5548
5623
  io2.stderr(JSON.stringify({
5549
5624
  error: "tail_ended_before_terminal",
@@ -5725,8 +5800,6 @@ async function dispatch(io2, args) {
5725
5800
  const sub = args[0];
5726
5801
  const rest = args.slice(1);
5727
5802
  switch (sub) {
5728
- case "proxy":
5729
- return runProxy(io2, rest);
5730
5803
  case "run":
5731
5804
  return runRunCmd(io2, rest);
5732
5805
  case "status":
@@ -5754,10 +5827,16 @@ async function dispatch(io2, args) {
5754
5827
  return runDeleteCmd(io2, rest);
5755
5828
  case "delete-asset":
5756
5829
  return runDeleteAssetCmd(io2, rest);
5830
+ case "runs":
5831
+ return runRunsCmd(io2, rest);
5832
+ case "sessions":
5833
+ return runSessionsCmd(io2, rest);
5757
5834
  case "whoami":
5758
5835
  return runWhoamiCmd(io2, rest);
5759
- case "redeem":
5760
- return runRedeemCmd(io2, rest);
5836
+ case "billing":
5837
+ return runBillingCmd(io2, rest);
5838
+ case "webhooks":
5839
+ return runWebhooksCmd(io2, rest);
5761
5840
  case "login":
5762
5841
  return runLoginCmd(io2, rest);
5763
5842
  case "logout":
@@ -5782,26 +5861,6 @@ async function dispatch(io2, args) {
5782
5861
  }
5783
5862
  }
5784
5863
  async function printGlobalHelp(io2) {
5785
- const manifest = await tryReadManifest(io2);
5786
- if (manifest) {
5787
- io2.stdout("aex \u2014 in-container CLI for managed run sessions\n\n");
5788
- io2.stdout("Usage:\n");
5789
- io2.stdout(" aex proxy <endpoint-name> [flags]\n");
5790
- io2.stdout(" aex proxy --help\n\n");
5791
- if (manifest.endpoints.length === 0) {
5792
- io2.stdout("This run declared no proxy endpoints.\n");
5793
- } else {
5794
- io2.stdout("Declared proxy endpoints for this run:\n");
5795
- for (const ep of manifest.endpoints) {
5796
- io2.stdout(` \u2022 ${formatProxyEndpointSummary(ep)}
5797
- `);
5798
- }
5799
- }
5800
- io2.stdout(`
5801
- Protocol version: ${manifest.protocolVersion}
5802
- `);
5803
- return SUCCESS;
5804
- }
5805
5864
  io2.stdout("aex \u2014 unified CLI for the aex platform (mirrors the SDK 1:1)\n\n");
5806
5865
  io2.stdout("Usage:\n");
5807
5866
  io2.stdout(" aex run --config <run.json> --<provider>-api-key K --api-token T [flags]\n");
@@ -5817,8 +5876,14 @@ Protocol version: ${manifest.protocolVersion}
5817
5876
  io2.stdout(" aex cancel <session-id> --api-token T\n");
5818
5877
  io2.stdout(" aex delete <session-id> --api-token T\n");
5819
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");
5820
5881
  io2.stdout(" aex whoami --api-token T\n");
5821
- 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");
5822
5887
  io2.stdout(" aex login --api-token T [--aex-url U] Persist token + url (then other verbs need no --api-token)\n");
5823
5888
  io2.stdout(" aex logout Clear the stored token\n");
5824
5889
  io2.stdout(" aex auth status Show the resolved config (token never printed)\n");
@@ -5846,10 +5911,8 @@ Protocol version: ${manifest.protocolVersion}
5846
5911
  io2.stdout(" --mcp name=url MCP server entry (repeatable)\n");
5847
5912
  io2.stdout(" --mcp-auth name=Hdr:Val Auth header on the matching --mcp; routed into vaulted secrets (repeatable)\n");
5848
5913
  io2.stdout(" --metadata key=value Submission metadata entry (repeatable)\n");
5849
- io2.stdout(" --proxy-endpoint '<json>' PlatformProxyEndpoint JSON (repeatable)\n");
5850
- io2.stdout(" --proxy-auth name=<spec> bearer:tok | basic:u:p | header:v | query:v (repeatable)\n");
5851
5914
  io2.stdout(" --runtime-size <size> managed runtime preset\n");
5852
- 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");
5853
5916
  io2.stdout(" --idempotency-key <key> Optional; defaults to a fresh UUID\n");
5854
5917
  io2.stdout(" --webhook <url> Optional per-run callback URL (https); receives the terminal run.finished event\n");
5855
5918
  io2.stdout(" --follow Poll events to stdout until the run terminates\n");