@aexhq/cli 0.25.1 → 0.26.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 (93) hide show
  1. package/README.md +12 -0
  2. package/dist/index.d.ts +2 -7
  3. package/dist/index.js +233 -7
  4. package/package.json +26 -29
  5. package/LICENSE +0 -201
  6. package/dist/cli.d.ts +0 -1
  7. package/dist/cli.js +0 -51
  8. package/dist/cli.js.map +0 -1
  9. package/dist/cli.mjs +0 -4657
  10. package/dist/cli.mjs.sha256 +0 -1
  11. package/dist/host/cancel.d.ts +0 -3
  12. package/dist/host/cancel.js +0 -30
  13. package/dist/host/cancel.js.map +0 -1
  14. package/dist/host/common.d.ts +0 -135
  15. package/dist/host/common.js +0 -281
  16. package/dist/host/common.js.map +0 -1
  17. package/dist/host/delete-asset.d.ts +0 -3
  18. package/dist/host/delete-asset.js +0 -34
  19. package/dist/host/delete-asset.js.map +0 -1
  20. package/dist/host/delete.d.ts +0 -3
  21. package/dist/host/delete.js +0 -30
  22. package/dist/host/delete.js.map +0 -1
  23. package/dist/host/download.d.ts +0 -3
  24. package/dist/host/download.js +0 -80
  25. package/dist/host/download.js.map +0 -1
  26. package/dist/host/events.d.ts +0 -3
  27. package/dist/host/events.js +0 -97
  28. package/dist/host/events.js.map +0 -1
  29. package/dist/host/index.d.ts +0 -17
  30. package/dist/host/index.js +0 -18
  31. package/dist/host/index.js.map +0 -1
  32. package/dist/host/outputs.d.ts +0 -3
  33. package/dist/host/outputs.js +0 -36
  34. package/dist/host/outputs.js.map +0 -1
  35. package/dist/host/run-cmd.d.ts +0 -3
  36. package/dist/host/run-cmd.js +0 -549
  37. package/dist/host/run-cmd.js.map +0 -1
  38. package/dist/host/skills-cmd.d.ts +0 -3
  39. package/dist/host/skills-cmd.js +0 -304
  40. package/dist/host/skills-cmd.js.map +0 -1
  41. package/dist/host/status.d.ts +0 -3
  42. package/dist/host/status.js +0 -31
  43. package/dist/host/status.js.map +0 -1
  44. package/dist/host/wait.d.ts +0 -3
  45. package/dist/host/wait.js +0 -88
  46. package/dist/host/wait.js.map +0 -1
  47. package/dist/host/whoami.d.ts +0 -3
  48. package/dist/host/whoami.js +0 -33
  49. package/dist/host/whoami.js.map +0 -1
  50. package/dist/index.js.map +0 -1
  51. package/dist/internal.d.ts +0 -53
  52. package/dist/internal.js +0 -10
  53. package/dist/internal.js.map +0 -1
  54. package/dist/outputs-sync.d.ts +0 -24
  55. package/dist/outputs-sync.js +0 -70
  56. package/dist/outputs-sync.js.map +0 -1
  57. package/dist/proxy.d.ts +0 -16
  58. package/dist/proxy.js +0 -316
  59. package/dist/proxy.js.map +0 -1
  60. package/dist/run.d.ts +0 -10
  61. package/dist/run.js +0 -166
  62. package/dist/run.js.map +0 -1
  63. package/dist/runtime.d.ts +0 -5
  64. package/dist/runtime.js +0 -4
  65. package/dist/runtime.js.map +0 -1
  66. package/scripts/finalize-bundle.mjs +0 -72
  67. package/src/cli.ts +0 -51
  68. package/src/host/cancel.ts +0 -39
  69. package/src/host/common.ts +0 -329
  70. package/src/host/delete-asset.ts +0 -43
  71. package/src/host/delete.ts +0 -39
  72. package/src/host/download.ts +0 -97
  73. package/src/host/events.ts +0 -113
  74. package/src/host/index.ts +0 -25
  75. package/src/host/outputs.ts +0 -45
  76. package/src/host/run-cmd.ts +0 -578
  77. package/src/host/skills-cmd.ts +0 -317
  78. package/src/host/status.ts +0 -40
  79. package/src/host/wait.ts +0 -110
  80. package/src/host/whoami.ts +0 -42
  81. package/src/index.ts +0 -7
  82. package/src/internal.ts +0 -63
  83. package/src/outputs-sync.ts +0 -76
  84. package/src/proxy.ts +0 -359
  85. package/src/run.ts +0 -187
  86. package/src/runtime.ts +0 -15
  87. package/test/cli.test.ts +0 -519
  88. package/test/host.test.ts +0 -1154
  89. package/test/no-env-vars.test.ts +0 -59
  90. package/test/outputs-sync.test.ts +0 -107
  91. package/tsconfig.build.json +0 -9
  92. package/tsconfig.json +0 -4
  93. package/vitest.config.ts +0 -14
package/src/proxy.ts DELETED
@@ -1,359 +0,0 @@
1
- /**
2
- * aex proxy — in-container subcommand. Calls an upstream HTTP
3
- * endpoint via the managed proxy described by the per-run manifest at
4
- * AEX_INDEX_PATH (`/mnt/session/uploads/aex/index.json`). NO
5
- * env-var reads; manifest + run token come from fixed paths.
6
- *
7
- * This file was extracted from run.ts when the host-side subcommands
8
- * (run/status/events/outputs/download/cancel/whoami) were added.
9
- */
10
- import {
11
- PROXY_PROTOCOL_HEADER,
12
- PROXY_PROTOCOL_VERSION_V2,
13
- PROXY_METHOD_HEADER,
14
- PROXY_PATH_HEADER,
15
- PROXY_QUERY_HEADER,
16
- PROXY_HEADERS_HEADER,
17
- PROXY_RESPONSE_MODE_HEADER,
18
- PROXY_RESPONSE_MODES,
19
- PROXY_RESP_MODE_HEADER,
20
- PROXY_RESP_STATUS_HEADER,
21
- PROXY_RESP_TRUNCATED_HEADER,
22
- PROXY_RESP_UPSTREAM_HEADERS_HEADER,
23
- type ProxyErrorBody,
24
- type ProxyIndexEntry,
25
- type ProxyIndexFile,
26
- type ProxyResponseEnvelope,
27
- type ProxyResponseMode
28
- } from "@aexhq/contracts";
29
- import { AEX_INDEX_PATH, AEX_RUN_TOKEN_PATH, type CliIO } from "./internal.js";
30
- import { SUCCESS, USAGE_ERR, RUNTIME_ERR, type CliExitCode } from "./host/common.js";
31
-
32
- interface ProxyFlags {
33
- readonly endpointName: string | null;
34
- readonly method: string;
35
- readonly path: string;
36
- readonly query: string | null;
37
- readonly headers: ReadonlyMap<string, string>;
38
- readonly dataSpec: string | null;
39
- readonly responseMode: string | null;
40
- readonly showHelp: boolean;
41
- }
42
-
43
- function parseProxyFlags(rest: readonly string[]): { ok: true; flags: ProxyFlags } | { ok: false; reason: string } {
44
- let endpointName: string | null = null;
45
- let method = "GET";
46
- let path = "/";
47
- let query: string | null = null;
48
- const headers = new Map<string, string>();
49
- let dataSpec: string | null = null;
50
- let responseMode: string | null = null;
51
- let showHelp = false;
52
-
53
- for (let i = 0; i < rest.length; i++) {
54
- const arg = rest[i]!;
55
- if (arg === "--help" || arg === "-h") {
56
- showHelp = true;
57
- continue;
58
- }
59
- if (arg === "--method") {
60
- method = expect(rest, ++i, "--method");
61
- continue;
62
- }
63
- if (arg === "--path") {
64
- path = expect(rest, ++i, "--path");
65
- continue;
66
- }
67
- if (arg === "--query") {
68
- query = expect(rest, ++i, "--query");
69
- continue;
70
- }
71
- if (arg === "--header") {
72
- const kv = expect(rest, ++i, "--header");
73
- const eq = kv.indexOf("=");
74
- if (eq <= 0) return { ok: false, reason: "--header must be in the form KEY=VALUE" };
75
- headers.set(kv.slice(0, eq).toLowerCase(), kv.slice(eq + 1));
76
- continue;
77
- }
78
- if (arg === "--data") {
79
- dataSpec = expect(rest, ++i, "--data");
80
- continue;
81
- }
82
- if (arg === "--response-mode") {
83
- responseMode = expect(rest, ++i, "--response-mode");
84
- continue;
85
- }
86
- if (arg.startsWith("--")) {
87
- return { ok: false, reason: `unknown flag: ${arg}` };
88
- }
89
- if (endpointName === null) {
90
- endpointName = arg;
91
- continue;
92
- }
93
- return { ok: false, reason: `unexpected positional argument: ${arg}` };
94
- }
95
- return { ok: true, flags: { endpointName, method, path, query, headers, dataSpec, responseMode, showHelp } };
96
- }
97
-
98
- function expect(arr: readonly string[], idx: number, flag: string): string {
99
- const v = arr[idx];
100
- if (v === undefined) {
101
- throw new CliUsageError(`${flag} requires a value`);
102
- }
103
- return v;
104
- }
105
-
106
- class CliUsageError extends Error {}
107
-
108
- export async function printProxyHelp(io: CliIO): Promise<CliExitCode> {
109
- io.stdout("aex proxy — call an upstream HTTP endpoint via the managed proxy.\n\n");
110
- io.stdout("Usage:\n");
111
- io.stdout(" aex proxy <endpoint-name> [flags]\n\n");
112
- io.stdout("Flags:\n");
113
- io.stdout(" --method <verb> HTTP method (default: GET)\n");
114
- io.stdout(" --path <path> Caller-supplied path; must match policy prefixes\n");
115
- io.stdout(' --query <json> JSON object of query parameters (e.g. \'{"q":"x"}\')\n');
116
- io.stdout(' --header K=V Add a caller header (repeatable)\n');
117
- io.stdout(" --data <value> Request body. Use '-' for stdin, '@<file>' for file content\n");
118
- io.stdout(" --response-mode <mode> status_only | headers_only | full (may only narrow policy)\n");
119
- io.stdout(" --help Show this message\n\n");
120
- const manifest = await tryReadManifest(io);
121
- if (manifest && manifest.endpoints.length > 0) {
122
- io.stdout("Declared endpoints:\n");
123
- for (const ep of manifest.endpoints) {
124
- io.stdout(` • ${formatProxyEndpointSummary(ep)}\n`);
125
- }
126
- }
127
- return SUCCESS;
128
- }
129
-
130
- export function formatProxyEndpointSummary(ep: ProxyIndexEntry): string {
131
- const retry = ep.retry
132
- ? `, retry=${ep.retry.maxAttempts}x ${ep.retry.retryOnMethods.join("/")} ` +
133
- `${ep.retry.retryOnStatuses.join("/")} delay=${ep.retry.initialDelayMs}-${ep.retry.maxDelayMs}ms ` +
134
- `jitter=${ep.retry.jitter}${ep.retry.respectRetryAfter ? " retry-after" : ""}`
135
- : "";
136
- return `${ep.name}: ${ep.allowMethods.join(",")} ${ep.allowPathPrefixes.join(",")} (mode=${ep.responseMode}${retry})`;
137
- }
138
-
139
- export async function runProxy(io: CliIO, rest: readonly string[]): Promise<CliExitCode> {
140
- let parsed;
141
- try {
142
- parsed = parseProxyFlags(rest);
143
- } catch (err) {
144
- if (err instanceof CliUsageError) {
145
- io.stderr(`${err.message}\n`);
146
- return USAGE_ERR;
147
- }
148
- throw err;
149
- }
150
- if (!parsed.ok) {
151
- io.stderr(`${parsed.reason}\n`);
152
- return USAGE_ERR;
153
- }
154
- const f = parsed.flags;
155
- if (f.showHelp) {
156
- return await printProxyHelp(io);
157
- }
158
- if (!f.endpointName) {
159
- io.stderr("missing endpoint-name\n");
160
- io.stderr("usage: aex proxy <endpoint-name> [flags]\n");
161
- return USAGE_ERR;
162
- }
163
- if (f.responseMode && !(PROXY_RESPONSE_MODES as readonly string[]).includes(f.responseMode)) {
164
- io.stderr(`--response-mode must be one of: ${PROXY_RESPONSE_MODES.join(", ")}\n`);
165
- return USAGE_ERR;
166
- }
167
-
168
- const manifest = await tryReadManifest(io);
169
- if (!manifest) {
170
- emitError(io, {
171
- error: "internal_error",
172
- message: "manifest not mounted; this CLI must run inside an aex-managed run"
173
- });
174
- return RUNTIME_ERR;
175
- }
176
- if (!manifest.proxyBaseUrl) {
177
- emitError(io, {
178
- error: "endpoint_not_found",
179
- message: "this run has no proxy endpoints declared",
180
- endpointName: f.endpointName
181
- });
182
- return RUNTIME_ERR;
183
- }
184
-
185
- let token: string;
186
- try {
187
- token = (await io.readFile(AEX_RUN_TOKEN_PATH)).trim();
188
- } catch {
189
- emitError(io, {
190
- error: "unauthorized",
191
- message: "run token file missing; this run has no proxy bearer"
192
- });
193
- return RUNTIME_ERR;
194
- }
195
- if (!token) {
196
- emitError(io, { error: "unauthorized", message: "run token is empty" });
197
- return RUNTIME_ERR;
198
- }
199
-
200
- let body: Uint8Array | undefined;
201
- if (f.dataSpec !== null) {
202
- try {
203
- body = await resolveBody(io, f.dataSpec);
204
- } catch (err) {
205
- io.stderr(`failed to read request body: ${(err as Error).message}\n`);
206
- return RUNTIME_ERR;
207
- }
208
- }
209
-
210
- const url = `${manifest.proxyBaseUrl.replace(/\/+$/, "")}/${encodeURIComponent(f.endpointName)}`;
211
- const requestHeaders = new Headers();
212
- requestHeaders.set("authorization", `Bearer ${token}`);
213
- requestHeaders.set(PROXY_PROTOCOL_HEADER, PROXY_PROTOCOL_VERSION_V2);
214
- requestHeaders.set(PROXY_METHOD_HEADER, f.method.toUpperCase());
215
- requestHeaders.set(PROXY_PATH_HEADER, f.path);
216
- if (f.query) {
217
- requestHeaders.set(PROXY_QUERY_HEADER, f.query);
218
- }
219
- if (f.headers.size > 0) {
220
- requestHeaders.set(PROXY_HEADERS_HEADER, JSON.stringify(Object.fromEntries(f.headers)));
221
- }
222
- if (f.responseMode) {
223
- requestHeaders.set(PROXY_RESPONSE_MODE_HEADER, f.responseMode);
224
- }
225
- if (body !== undefined) {
226
- requestHeaders.set("content-length", String(body.byteLength));
227
- }
228
-
229
- const init: RequestInit = {
230
- method: "POST",
231
- headers: requestHeaders,
232
- redirect: "manual"
233
- };
234
- if (body !== undefined) {
235
- init.body = body;
236
- }
237
-
238
- let response: Response;
239
- try {
240
- response = await io.fetchImpl(url, init);
241
- } catch (err) {
242
- emitError(io, {
243
- error: "upstream_error",
244
- message: `proxy request failed: ${(err as Error).message}`,
245
- endpointName: f.endpointName
246
- });
247
- return RUNTIME_ERR;
248
- }
249
-
250
- // v2 streamed success: the Worker carries the envelope metadata in the
251
- // x-aex-proxy-* response headers and streams the (already byte-capped)
252
- // body. Reconstruct the same ProxyResponseEnvelope JSON the agent saw
253
- // under v1 so the stdout contract is unchanged. A BFF-level error (e.g.
254
- // unsupported_protocol, policy_denied) still returns a JSON error body
255
- // with no status header, so it falls through to the JSON path below.
256
- if (response.headers.get(PROXY_RESP_STATUS_HEADER) !== null) {
257
- const envelope = await readStreamedEnvelope(response, f.endpointName);
258
- io.stdout(JSON.stringify(envelope) + "\n");
259
- return SUCCESS;
260
- }
261
-
262
- const text = await response.text();
263
- let parsedBody: unknown;
264
- try {
265
- parsedBody = text ? JSON.parse(text) : {};
266
- } catch {
267
- // Include HTTP status + a bounded body snippet so the agent's
268
- // tool_result log surfaces what actually came back. Without this,
269
- // a non-JSON response (e.g. a 500 HTML error page from the BFF
270
- // host) is indistinguishable from any other failure.
271
- const snippet = text.slice(0, 200).replace(/\s+/g, " ").trim();
272
- emitError(io, {
273
- error: "internal_error",
274
- message: `proxy returned non-JSON response (HTTP ${response.status}, body=${JSON.stringify(snippet)})`,
275
- endpointName: f.endpointName
276
- });
277
- return RUNTIME_ERR;
278
- }
279
-
280
- if (!response.ok) {
281
- io.stderr(JSON.stringify(parsedBody) + "\n");
282
- return RUNTIME_ERR;
283
- }
284
-
285
- io.stdout(JSON.stringify(parsedBody) + "\n");
286
- return SUCCESS;
287
- }
288
-
289
- async function resolveBody(io: CliIO, spec: string): Promise<Uint8Array> {
290
- if (spec === "-") {
291
- const data = await io.readFile("/dev/stdin");
292
- return new Uint8Array(Buffer.from(data, "utf8"));
293
- }
294
- if (spec.startsWith("@")) {
295
- const path = spec.slice(1);
296
- if (!path) throw new Error("--data @<file> requires a path");
297
- const data = await io.readFile(path);
298
- return new Uint8Array(Buffer.from(data, "utf8"));
299
- }
300
- return new Uint8Array(Buffer.from(spec, "utf8"));
301
- }
302
-
303
- function emitError(io: CliIO, body: ProxyErrorBody): void {
304
- io.stderr(JSON.stringify(body) + "\n");
305
- }
306
-
307
- /**
308
- * Reassemble a {@link ProxyResponseEnvelope} from a v2 streamed response.
309
- * The Worker has already enforced the byte-cap, so reading the body to the
310
- * end here is bounded by `maxResponseBytes` — the OOM risk lived on the
311
- * Worker isolate, not in this per-call container process.
312
- */
313
- async function readStreamedEnvelope(
314
- response: Response,
315
- endpointName: string
316
- ): Promise<ProxyResponseEnvelope> {
317
- const effectiveResponseMode = (response.headers.get(PROXY_RESP_MODE_HEADER) ??
318
- "headers_only") as ProxyResponseMode;
319
- const upstreamStatus = Number.parseInt(response.headers.get(PROXY_RESP_STATUS_HEADER) ?? "0", 10);
320
- let upstreamHeaders: Record<string, string> = {};
321
- const rawHeaders = response.headers.get(PROXY_RESP_UPSTREAM_HEADERS_HEADER);
322
- if (rawHeaders) {
323
- try {
324
- const parsed = JSON.parse(rawHeaders);
325
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
326
- upstreamHeaders = parsed as Record<string, string>;
327
- }
328
- } catch {
329
- // The metadata header is Worker-controlled; a malformed value is a
330
- // protocol bug, not agent input. Degrade to empty rather than crash.
331
- }
332
- }
333
- const truncatedRaw = response.headers.get(PROXY_RESP_TRUNCATED_HEADER);
334
- const bytes = new Uint8Array(await response.arrayBuffer());
335
-
336
- return {
337
- endpointName,
338
- upstreamStatus,
339
- upstreamHeaders,
340
- effectiveResponseMode,
341
- // The streamed path can't echo the request-side clamp decision (it
342
- // isn't carried back); the effective mode is authoritative for the
343
- // agent and matches what v1 surfaced for an un-clamped call.
344
- modeClamped: false,
345
- ...(effectiveResponseMode === "full" && bytes.byteLength > 0
346
- ? { upstreamBodyBase64: Buffer.from(bytes).toString("base64") }
347
- : {}),
348
- ...(truncatedRaw === "true" ? { truncated: true } : {})
349
- };
350
- }
351
-
352
- export async function tryReadManifest(io: CliIO): Promise<ProxyIndexFile | null> {
353
- try {
354
- const raw = await io.readFile(AEX_INDEX_PATH);
355
- return JSON.parse(raw) as ProxyIndexFile;
356
- } catch {
357
- return null;
358
- }
359
- }
package/src/run.ts DELETED
@@ -1,187 +0,0 @@
1
- /**
2
- * aex CLI — main runner. Pure (no `process.*` reads), gets all IO
3
- * via the injected {@link CliIO} surface. The thin entrypoint in
4
- * `cli.ts` wires real stdin/stdout/fetch/readFile and calls `runCli()`.
5
- *
6
- * Subcommands:
7
- * In-container (manifest at AEX_INDEX_PATH —
8
- * `/mnt/session/uploads/aex/index.json` — present):
9
- * - `aex proxy <endpoint-name> [flags]`
10
- *
11
- * Host (manifest absent):
12
- * - `aex run --config <run.json> [flags]`
13
- * - `aex status <run-id>`
14
- * - `aex wait <run-id> [--timeout <dur>] [--interval <dur>]`
15
- * - `aex events <run-id> [--follow] [--timeout <dur>]`
16
- * - `aex outputs <run-id>`
17
- * - `aex download <run-id> [--only outputs|events|metadata] [--out path]`
18
- * - `aex cancel <run-id>`
19
- * - `aex delete <run-id>`
20
- * - `aex whoami`
21
- *
22
- * Every host subcommand requires `--api-token`. `--aex-url` is
23
- * optional and defaults to `https://api.aex.dev`. There is no
24
- * `--workspace` flag — the workspace is derived server-side from the
25
- * API token.
26
- */
27
- import { RUN_PROVIDERS, type ProxyErrorBody } from "@aexhq/contracts";
28
- import type { CliIO } from "./internal.js";
29
- import { runOutputsSyncCmd } from "./outputs-sync.js";
30
- import { formatProxyEndpointSummary, printProxyHelp, runProxy, tryReadManifest } from "./proxy.js";
31
- import {
32
- RUNTIME_ERR,
33
- SUCCESS,
34
- USAGE_ERR,
35
- type CliExitCode,
36
- runCancelCmd,
37
- runDeleteCmd,
38
- runDeleteAssetCmd,
39
- runDownloadCmd,
40
- runEventsCmd,
41
- runOutputsCmd,
42
- runRunCmd,
43
- runSkillsCmd,
44
- runStatusCmd,
45
- runWaitCmd,
46
- runWhoamiCmd
47
- } from "./host/index.js";
48
-
49
- export type { CliExitCode } from "./host/common.js";
50
-
51
- /**
52
- * Top-level CLI entrypoint. Parses argv, dispatches to a subcommand
53
- * handler, returns the desired exit code. Calls `io.exit(code)` itself
54
- * so the entrypoint can simply `await runCli(io)`.
55
- */
56
- export async function runCli(io: CliIO): Promise<void> {
57
- const args = io.argv.slice(2); // skip node + script
58
- try {
59
- const exit = await dispatch(io, args);
60
- io.exit(exit.code);
61
- } catch (err) {
62
- // Defense in depth — runCli should never throw. If it does, emit a
63
- // stable error envelope rather than a stack trace.
64
- const body: ProxyErrorBody = { error: "internal_error", message: (err as Error).message ?? "unknown error" };
65
- io.stderr(JSON.stringify(body) + "\n");
66
- io.exit(RUNTIME_ERR.code);
67
- }
68
- }
69
-
70
- async function dispatch(io: CliIO, args: readonly string[]): Promise<CliExitCode> {
71
- if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
72
- return printGlobalHelp(io);
73
- }
74
- const sub = args[0];
75
- const rest = args.slice(1);
76
- switch (sub) {
77
- case "proxy":
78
- return runProxy(io, rest);
79
- case "run":
80
- return runRunCmd(io, rest);
81
- case "skills":
82
- return runSkillsCmd(io, rest);
83
- case "status":
84
- return runStatusCmd(io, rest);
85
- case "wait":
86
- return runWaitCmd(io, rest);
87
- case "events":
88
- return runEventsCmd(io, rest);
89
- case "outputs":
90
- // `outputs sync <dirs>` is the legacy in-container internal
91
- // capture walker. The bare `outputs <run-id>` form is the
92
- // host-side list verb. We
93
- // distinguish on the first sub-arg rather than on
94
- // manifest-presence so a misconfigured host invocation
95
- // (e.g. `aex outputs sync ...` on a developer machine)
96
- // produces a clear "in-container only" error instead of
97
- // silently routing to the wrong handler.
98
- if (rest[0] === "sync") {
99
- return runOutputsSyncCmd(io, rest.slice(1));
100
- }
101
- return runOutputsCmd(io, rest);
102
- case "download":
103
- return runDownloadCmd(io, rest);
104
- case "cancel":
105
- return runCancelCmd(io, rest);
106
- case "delete":
107
- return runDeleteCmd(io, rest);
108
- case "delete-asset":
109
- return runDeleteAssetCmd(io, rest);
110
- case "whoami":
111
- return runWhoamiCmd(io, rest);
112
- default:
113
- io.stderr(`unknown subcommand: ${sub}\n`);
114
- io.stderr("run `aex --help` for usage\n");
115
- return USAGE_ERR;
116
- }
117
- }
118
-
119
- async function printGlobalHelp(io: CliIO): Promise<CliExitCode> {
120
- const manifest = await tryReadManifest(io);
121
- if (manifest) {
122
- // In-container help — only `proxy` is reachable from inside a run.
123
- io.stdout("aex — in-container CLI for managed run sessions\n\n");
124
- io.stdout("Usage:\n");
125
- io.stdout(" aex proxy <endpoint-name> [flags]\n");
126
- io.stdout(" aex proxy --help\n\n");
127
- if (manifest.endpoints.length === 0) {
128
- io.stdout("This run declared no proxy endpoints.\n");
129
- } else {
130
- io.stdout("Declared proxy endpoints for this run:\n");
131
- for (const ep of manifest.endpoints) {
132
- io.stdout(` • ${formatProxyEndpointSummary(ep)}\n`);
133
- }
134
- }
135
- io.stdout(`\nProtocol version: ${manifest.protocolVersion}\n`);
136
- return SUCCESS;
137
- }
138
-
139
- // Host-side help: the unified surface mirroring the SDK 1:1.
140
- io.stdout("aex — unified CLI for the aex platform (mirrors the SDK 1:1)\n\n");
141
- io.stdout("Usage:\n");
142
- io.stdout(" aex run --config <run.json> --<provider>-api-key K --api-token T [flags]\n");
143
- io.stdout(" aex run --model M --prompt P [--system S] [--mcp name=url ...] --<provider>-api-key K --api-token T [flags]\n");
144
- io.stdout(" aex skills upload --name N --from-path <dir> --api-token T\n");
145
- io.stdout(" aex skills upload --name N --file <path> [--file <path> ...] --api-token T\n");
146
- io.stdout(" aex skills list --api-token T\n");
147
- io.stdout(" aex skills get <skill-id> --api-token T\n");
148
- io.stdout(" aex skills delete <skill-id> --api-token T\n");
149
- io.stdout(" aex status <run-id> --api-token T\n");
150
- io.stdout(" aex wait <run-id> [--timeout 8m] [--interval 2s] --api-token T\n");
151
- io.stdout(" aex events <run-id> [--follow] [--timeout 8m] --api-token T\n");
152
- io.stdout(" aex outputs <run-id> --api-token T\n");
153
- io.stdout(" aex download <run-id> [--only outputs|events|metadata] [--out path] --api-token T\n");
154
- io.stdout(" aex cancel <run-id> --api-token T\n");
155
- io.stdout(" aex delete <run-id> --api-token T\n");
156
- io.stdout(" aex delete-asset <assetId|hash> --api-token T\n");
157
- io.stdout(" aex whoami --api-token T\n");
158
- io.stdout(" aex --help\n\n");
159
- io.stdout("Common flags on every host subcommand:\n");
160
- io.stdout(" --api-token <token> REQUIRED — aex SDK API token (workspace is derived from it)\n");
161
- io.stdout(" --aex-url <url> Optional; defaults to https://api.aex.dev (local/staging/hosted plane)\n");
162
- io.stdout(" --debug Optional; print a redacted per-request trace to stderr (uploads nothing)\n\n");
163
- io.stdout("aex run flags:\n");
164
- io.stdout(` --provider <name> Optional; one of: ${RUN_PROVIDERS.join(", ")} (default anthropic)\n`);
165
- io.stdout(" --runtime managed Optional runtime selector; omitted also uses managed\n");
166
- for (const provider of RUN_PROVIDERS) {
167
- io.stdout(` --${provider}-api-key <key>${" ".repeat(Math.max(1, 13 - provider.length))}REQUIRED when --provider ${provider}; never stored\n`);
168
- }
169
- io.stdout(" --config <path> Run-request JSON (mutually exclusive with the flat --model/--prompt flags)\n");
170
- io.stdout(" --model <model-id> Provider model id (required in flat mode)\n");
171
- io.stdout(" --system @file | <text> System message; @-prefix reads from file\n");
172
- io.stdout(" --prompt @file | <text> User message; @-prefix reads from file (repeatable)\n");
173
- io.stdout(" --mcp name=url MCP server entry (repeatable)\n");
174
- io.stdout(" --mcp-auth name=Hdr:Val Auth header on the matching --mcp; routed into vaulted secrets (repeatable)\n");
175
- io.stdout(" --metadata key=value Submission metadata entry (repeatable)\n");
176
- io.stdout(" --proxy-endpoint '<json>' PlatformProxyEndpoint JSON (repeatable)\n");
177
- io.stdout(" --proxy-auth name=<spec> bearer:tok | basic:u:p | header:v | query:v (repeatable)\n");
178
- io.stdout(" --runtime-size <size> managed runtime preset\n");
179
- io.stdout(" --run-timeout <dur> Server-side run deadline (e.g. 1h); distinct from --timeout\n");
180
- io.stdout(" --idempotency-key <key> Optional; defaults to a fresh UUID\n");
181
- io.stdout(" --follow Poll events to stdout until the run terminates\n");
182
- io.stdout(" --timeout <dur> With --follow: give up after this long (e.g. 8m, 30s, 500ms); exit code 3\n");
183
- return SUCCESS;
184
- }
185
-
186
- // Re-export the proxy printer so existing imports keep working.
187
- export { printProxyHelp };
package/src/runtime.ts DELETED
@@ -1,15 +0,0 @@
1
- export {
2
- printProxyHelp,
3
- runProxy,
4
- tryReadManifest
5
- } from "./proxy.js";
6
- export { runOutputsSyncCmd } from "./outputs-sync.js";
7
- export type { CliExitCode } from "./host/common.js";
8
- export {
9
- AEX_INDEX_PATH,
10
- AEX_RUN_TOKEN_PATH
11
- } from "./internal.js";
12
- export type {
13
- CliIO,
14
- OutputsSyncFileEntry
15
- } from "./internal.js";