@aexhq/sdk 0.38.1 → 0.40.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.
- package/README.md +23 -17
- package/dist/_contracts/api-key.d.ts +49 -0
- package/dist/_contracts/api-key.js +87 -0
- package/dist/_contracts/bundle-manifest.d.ts +86 -0
- package/dist/_contracts/bundle-manifest.js +157 -0
- package/dist/_contracts/error-codes.d.ts +26 -0
- package/dist/_contracts/error-codes.js +79 -0
- package/dist/_contracts/error-factory.d.ts +32 -0
- package/dist/_contracts/error-factory.js +142 -0
- package/dist/_contracts/event-envelope.d.ts +33 -10
- package/dist/_contracts/event-envelope.js +46 -10
- package/dist/_contracts/event-view.d.ts +123 -0
- package/dist/_contracts/event-view.js +120 -0
- package/dist/_contracts/http.js +12 -3
- package/dist/_contracts/index.d.ts +8 -4
- package/dist/_contracts/index.js +11 -7
- package/dist/_contracts/models.d.ts +15 -0
- package/dist/_contracts/models.js +33 -0
- package/dist/_contracts/operations.d.ts +70 -2
- package/dist/_contracts/operations.js +143 -7
- package/dist/_contracts/run-config.d.ts +35 -10
- package/dist/_contracts/run-config.js +17 -5
- package/dist/_contracts/run-record.d.ts +3 -2
- package/dist/_contracts/runtime-types.d.ts +148 -36
- package/dist/_contracts/runtime-types.js +33 -1
- package/dist/_contracts/sdk-errors.d.ts +61 -2
- package/dist/_contracts/sdk-errors.js +83 -3
- package/dist/_contracts/sdk-secrets.js +31 -6
- package/dist/_contracts/stable.d.ts +14 -0
- package/dist/_contracts/stable.js +14 -0
- package/dist/_contracts/status.d.ts +28 -1
- package/dist/_contracts/status.js +48 -3
- package/dist/_contracts/submission.d.ts +157 -14
- package/dist/_contracts/submission.js +319 -55
- package/dist/_contracts/suggest.d.ts +15 -0
- package/dist/_contracts/suggest.js +54 -0
- package/dist/asset-upload.d.ts +27 -1
- package/dist/asset-upload.js +219 -4
- package/dist/asset-upload.js.map +1 -1
- package/dist/bundle.d.ts +23 -14
- package/dist/bundle.js +39 -20
- package/dist/bundle.js.map +1 -1
- package/dist/canonical-zip.d.ts +68 -0
- package/dist/canonical-zip.js +307 -0
- package/dist/canonical-zip.js.map +1 -0
- package/dist/cli.mjs +1927 -326
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +273 -70
- package/dist/client.js +885 -335
- package/dist/client.js.map +1 -1
- package/dist/fetch-archive.js +14 -14
- package/dist/fetch-archive.js.map +1 -1
- package/dist/file.d.ts +33 -6
- package/dist/file.js +120 -54
- package/dist/file.js.map +1 -1
- package/dist/index.d.ts +23 -14
- package/dist/index.js +31 -14
- package/dist/index.js.map +1 -1
- package/dist/node-fs.d.ts +26 -9
- package/dist/node-fs.js +13 -38
- package/dist/node-fs.js.map +1 -1
- package/dist/node-walk.d.ts +69 -0
- package/dist/node-walk.js +146 -0
- package/dist/node-walk.js.map +1 -0
- package/dist/retry.d.ts +9 -13
- package/dist/retry.js +18 -17
- package/dist/retry.js.map +1 -1
- package/dist/skill.d.ts +151 -0
- package/dist/skill.js +298 -0
- package/dist/skill.js.map +1 -0
- package/dist/tool.d.ts +14 -2
- package/dist/tool.js +33 -7
- package/dist/tool.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +29 -5
- package/docs/billing.md +6 -0
- package/docs/concepts/agent-tools.md +11 -0
- package/docs/concepts/composition.md +3 -3
- package/docs/defaults.md +1 -0
- package/docs/errors.md +64 -4
- package/docs/events.md +84 -49
- package/docs/limits-and-quotas.md +24 -0
- package/docs/mcp.md +3 -2
- package/docs/networking.md +7 -1
- package/docs/outputs.md +36 -7
- package/docs/provider-runtime-capabilities.md +1 -1
- package/docs/quickstart.md +17 -7
- package/docs/run-config.md +3 -3
- package/docs/secrets.md +14 -0
- package/docs/skills.md +74 -44
- package/docs/vision-skills.md +3 -3
- package/examples/feature-tour.ts +4 -6
- package/examples/spike-settle-latency.ts +125 -0
- package/package.json +4 -3
- package/dist/_contracts/event-guards.d.ts +0 -67
- package/dist/_contracts/event-guards.js +0 -36
- package/dist/skill-tool.d.ts +0 -102
- package/dist/skill-tool.js +0 -190
- package/dist/skill-tool.js.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -7,7 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// dist/cli.js
|
|
9
9
|
import { readFile, writeFile, readdir, stat, mkdir, chmod, rm } from "node:fs/promises";
|
|
10
|
-
import { resolve as
|
|
10
|
+
import { resolve as resolvePath5, join, dirname as dirname2 } from "node:path";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
|
|
13
13
|
// ../contracts/dist/provider-support.js
|
|
@@ -98,6 +98,46 @@ var PROVIDER_PUBLIC_SUPPORT = {
|
|
|
98
98
|
}
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
// ../contracts/dist/suggest.js
|
|
102
|
+
function suggest(input, candidates) {
|
|
103
|
+
const needle = input.trim();
|
|
104
|
+
if (!needle)
|
|
105
|
+
return void 0;
|
|
106
|
+
const lower = needle.toLowerCase();
|
|
107
|
+
const prefixHits = candidates.filter((c) => c.toLowerCase().startsWith(lower));
|
|
108
|
+
if (prefixHits.length === 1)
|
|
109
|
+
return prefixHits[0];
|
|
110
|
+
let best;
|
|
111
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
112
|
+
for (const candidate of candidates) {
|
|
113
|
+
const dist = levenshtein(needle, candidate);
|
|
114
|
+
if (dist < bestDist) {
|
|
115
|
+
bestDist = dist;
|
|
116
|
+
best = candidate;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return bestDist <= 2 ? best : void 0;
|
|
120
|
+
}
|
|
121
|
+
function levenshtein(a, b) {
|
|
122
|
+
const m = a.length;
|
|
123
|
+
const n = b.length;
|
|
124
|
+
if (m === 0)
|
|
125
|
+
return n;
|
|
126
|
+
if (n === 0)
|
|
127
|
+
return m;
|
|
128
|
+
let prev = Array.from({ length: n + 1 }, (_, j) => j);
|
|
129
|
+
let curr = new Array(n + 1);
|
|
130
|
+
for (let i2 = 1; i2 <= m; i2++) {
|
|
131
|
+
curr[0] = i2;
|
|
132
|
+
for (let j = 1; j <= n; j++) {
|
|
133
|
+
const cost = a[i2 - 1] === b[j - 1] ? 0 : 1;
|
|
134
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
135
|
+
}
|
|
136
|
+
[prev, curr] = [curr, prev];
|
|
137
|
+
}
|
|
138
|
+
return prev[n];
|
|
139
|
+
}
|
|
140
|
+
|
|
101
141
|
// ../contracts/dist/models.js
|
|
102
142
|
var MODEL_PROVIDER_IDS = {
|
|
103
143
|
"claude-haiku-4-5": { anthropic: "claude-haiku-4-5" },
|
|
@@ -190,6 +230,21 @@ function providersForModel(model) {
|
|
|
190
230
|
function providerForModel(model) {
|
|
191
231
|
return providersForModel(model)[0];
|
|
192
232
|
}
|
|
233
|
+
function resolveModelProvider(model, provider) {
|
|
234
|
+
const providers = providersForModel(model);
|
|
235
|
+
if (provider !== void 0) {
|
|
236
|
+
if (providers.length > 0 && !providers.includes(provider)) {
|
|
237
|
+
throw new Error(`model ${JSON.stringify(model)} is not available for provider ${provider}; available: ${providers.join(", ")}`);
|
|
238
|
+
}
|
|
239
|
+
return provider;
|
|
240
|
+
}
|
|
241
|
+
const inferred = providers[0];
|
|
242
|
+
if (inferred === void 0) {
|
|
243
|
+
const hint = suggest(model, RUN_MODELS);
|
|
244
|
+
throw new Error(`${JSON.stringify(model)} is not a known model id` + (hint ? ` (did you mean ${JSON.stringify(hint)}?)` : "") + "; pass provider explicitly to run it");
|
|
245
|
+
}
|
|
246
|
+
return inferred;
|
|
247
|
+
}
|
|
193
248
|
function isRunModel(input) {
|
|
194
249
|
return typeof input === "string" && RUN_MODELS.includes(input);
|
|
195
250
|
}
|
|
@@ -201,6 +256,33 @@ function parseRunModel(input, field = "submission.model") {
|
|
|
201
256
|
}
|
|
202
257
|
|
|
203
258
|
// ../contracts/dist/status.js
|
|
259
|
+
var SESSION_LIFECYCLE_STATUSES = [
|
|
260
|
+
"creating",
|
|
261
|
+
"running",
|
|
262
|
+
"idle",
|
|
263
|
+
"suspending",
|
|
264
|
+
"suspended",
|
|
265
|
+
"cancelling",
|
|
266
|
+
"deleting",
|
|
267
|
+
"deleted",
|
|
268
|
+
"expired"
|
|
269
|
+
];
|
|
270
|
+
var SESSION_TERMINAL_OUTCOMES = [
|
|
271
|
+
"succeeded",
|
|
272
|
+
"failed",
|
|
273
|
+
"timed_out",
|
|
274
|
+
"cancelled"
|
|
275
|
+
];
|
|
276
|
+
var SESSION_STATUSES = [
|
|
277
|
+
...SESSION_LIFECYCLE_STATUSES,
|
|
278
|
+
...SESSION_TERMINAL_OUTCOMES,
|
|
279
|
+
"awaiting_approval"
|
|
280
|
+
];
|
|
281
|
+
var SESSION_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
282
|
+
...SESSION_TERMINAL_OUTCOMES,
|
|
283
|
+
"deleted",
|
|
284
|
+
"expired"
|
|
285
|
+
]);
|
|
204
286
|
var TERMINAL_RUN_STATUSES = [
|
|
205
287
|
"succeeded",
|
|
206
288
|
"failed",
|
|
@@ -222,6 +304,9 @@ var CLEANUP_STATUSES = [
|
|
|
222
304
|
];
|
|
223
305
|
|
|
224
306
|
// ../contracts/dist/run-config.js
|
|
307
|
+
var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,127}$/;
|
|
308
|
+
var TOOL_NAME_PATTERN = SKILL_NAME_PATTERN;
|
|
309
|
+
var SKILL_RESERVED_NAMES = /* @__PURE__ */ new Set(["skills", "skill"]);
|
|
225
310
|
var SKILL_BUNDLE_LIMITS = {
|
|
226
311
|
/** Compressed (.zip) ceiling. */
|
|
227
312
|
maxCompressedBytes: 10 * 1024 * 1024 * 1024,
|
|
@@ -244,6 +329,66 @@ var SKILL_BUNDLE_LIMITS = {
|
|
|
244
329
|
/** Stored directory mode. */
|
|
245
330
|
defaultDirMode: 493
|
|
246
331
|
};
|
|
332
|
+
var DEFAULT_FILE_MOUNT_PATH = "/workspace";
|
|
333
|
+
var SkillBundleValidationError = class extends Error {
|
|
334
|
+
constructor(message) {
|
|
335
|
+
super(message);
|
|
336
|
+
this.name = "SkillBundleValidationError";
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
function normaliseSkillBundlePath(input) {
|
|
340
|
+
if (typeof input !== "string") {
|
|
341
|
+
throw new SkillBundleValidationError("bundle entry path must be a string");
|
|
342
|
+
}
|
|
343
|
+
if (input.length === 0 || input.trim().length === 0) {
|
|
344
|
+
throw new SkillBundleValidationError("bundle entry path must be non-empty");
|
|
345
|
+
}
|
|
346
|
+
if (input.length > SKILL_BUNDLE_LIMITS.maxPathLength) {
|
|
347
|
+
throw new SkillBundleValidationError(`bundle entry path exceeds maxPathLength (${SKILL_BUNDLE_LIMITS.maxPathLength}): ${input}`);
|
|
348
|
+
}
|
|
349
|
+
if (input.includes("\0")) {
|
|
350
|
+
throw new SkillBundleValidationError(`bundle entry path contains NUL byte: ${JSON.stringify(input)}`);
|
|
351
|
+
}
|
|
352
|
+
if (input.includes("\\")) {
|
|
353
|
+
throw new SkillBundleValidationError(`bundle entry path uses backslash separator: ${input}`);
|
|
354
|
+
}
|
|
355
|
+
if (/^[A-Za-z]:[\\/]/.test(input)) {
|
|
356
|
+
throw new SkillBundleValidationError(`bundle entry path uses a drive letter: ${input}`);
|
|
357
|
+
}
|
|
358
|
+
if (input.startsWith("/")) {
|
|
359
|
+
throw new SkillBundleValidationError(`bundle entry path must be relative: ${input}`);
|
|
360
|
+
}
|
|
361
|
+
if (input.endsWith("/")) {
|
|
362
|
+
throw new SkillBundleValidationError(`bundle entry path must not end with '/': ${input}`);
|
|
363
|
+
}
|
|
364
|
+
const segments = input.split("/");
|
|
365
|
+
for (const segment of segments) {
|
|
366
|
+
if (segment === "..") {
|
|
367
|
+
throw new SkillBundleValidationError(`bundle entry path contains '..' segment: ${input}`);
|
|
368
|
+
}
|
|
369
|
+
if (segment === "." || segment === "") {
|
|
370
|
+
throw new SkillBundleValidationError(`bundle entry path contains empty or '.' segment: ${input}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (segments.length > SKILL_BUNDLE_LIMITS.maxDepth) {
|
|
374
|
+
throw new SkillBundleValidationError(`bundle entry path exceeds maxDepth (${SKILL_BUNDLE_LIMITS.maxDepth}): ${input}`);
|
|
375
|
+
}
|
|
376
|
+
return input;
|
|
377
|
+
}
|
|
378
|
+
function validateSkillBundleEntry(input) {
|
|
379
|
+
const path = normaliseSkillBundlePath(input.path);
|
|
380
|
+
if (!Number.isFinite(input.size) || !Number.isInteger(input.size) || input.size < 0) {
|
|
381
|
+
throw new SkillBundleValidationError(`bundle entry size must be a non-negative integer (${path})`);
|
|
382
|
+
}
|
|
383
|
+
if (input.size > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
|
|
384
|
+
throw new SkillBundleValidationError(`bundle entry size exceeds maxDecompressedBytes (${SKILL_BUNDLE_LIMITS.maxDecompressedBytes}): ${path}`);
|
|
385
|
+
}
|
|
386
|
+
const mode = (input.mode ?? SKILL_BUNDLE_LIMITS.defaultFileMode) & 511;
|
|
387
|
+
if (mode !== SKILL_BUNDLE_LIMITS.defaultFileMode && mode !== SKILL_BUNDLE_LIMITS.defaultDirMode) {
|
|
388
|
+
return { path, size: input.size, mode: SKILL_BUNDLE_LIMITS.defaultFileMode };
|
|
389
|
+
}
|
|
390
|
+
return { path, size: input.size, mode };
|
|
391
|
+
}
|
|
247
392
|
var REMOTE_MCP_TRANSPORTS = ["http", "sse"];
|
|
248
393
|
var REMOTE_MCP_STDIO_REJECTED_MESSAGE = "stdio MCP servers are not supported by Aex. Aex supports remote MCP servers over HTTP/SSE only.";
|
|
249
394
|
var STDIO_ONLY_FIELDS = ["command", "args", "env"];
|
|
@@ -497,6 +642,36 @@ var DEFAULT_RUNTIME_SIZE = "shared-0.25x-1gb";
|
|
|
497
642
|
var DEFAULT_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
498
643
|
var MAX_RUN_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
499
644
|
var MIN_RUN_TIMEOUT_MS = 60 * 1e3;
|
|
645
|
+
var DURATION_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/;
|
|
646
|
+
function parseDurationToMs(input) {
|
|
647
|
+
const match = DURATION_PATTERN.exec(input.trim());
|
|
648
|
+
if (!match) {
|
|
649
|
+
throw new Error(`invalid duration ${JSON.stringify(input)} (expected e.g. "1h", "90m", "30s", "500ms", or a bare ms integer)`);
|
|
650
|
+
}
|
|
651
|
+
const value = Number(match[1]);
|
|
652
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
653
|
+
throw new Error(`invalid duration ${JSON.stringify(input)} (must be a non-negative number)`);
|
|
654
|
+
}
|
|
655
|
+
const unit = match[2] ?? "ms";
|
|
656
|
+
const factor = unit === "h" ? 36e5 : unit === "m" ? 6e4 : unit === "s" ? 1e3 : 1;
|
|
657
|
+
return Math.round(value * factor);
|
|
658
|
+
}
|
|
659
|
+
function parseRunTimeout(input) {
|
|
660
|
+
if (input === void 0) {
|
|
661
|
+
return void 0;
|
|
662
|
+
}
|
|
663
|
+
if (typeof input !== "string") {
|
|
664
|
+
throw new Error(`timeout must be a duration string (e.g. "1h", "30m"); got ${JSON.stringify(input)}`);
|
|
665
|
+
}
|
|
666
|
+
const ms = parseDurationToMs(input);
|
|
667
|
+
if (ms < MIN_RUN_TIMEOUT_MS) {
|
|
668
|
+
throw new Error(`timeout must be at least ${MIN_RUN_TIMEOUT_MS}ms (1m); got ${ms}ms`);
|
|
669
|
+
}
|
|
670
|
+
if (ms > MAX_RUN_TIMEOUT_MS) {
|
|
671
|
+
throw new Error(`timeout must be at most ${MAX_RUN_TIMEOUT_MS}ms (8h); got ${ms}ms`);
|
|
672
|
+
}
|
|
673
|
+
return ms;
|
|
674
|
+
}
|
|
500
675
|
var RUN_PROCESS_KILL_GRACE_MS = 60 * 1e3;
|
|
501
676
|
var RUN_TERMINAL_GRACE_MS = 90 * 1e3;
|
|
502
677
|
|
|
@@ -540,7 +715,90 @@ var RUN_PROVIDERS = [
|
|
|
540
715
|
"doubao",
|
|
541
716
|
"doubao-cn"
|
|
542
717
|
];
|
|
543
|
-
|
|
718
|
+
function requireRecord(input, field) {
|
|
719
|
+
if (!isRecord(input)) {
|
|
720
|
+
throw new Error(`${field} must be an object`);
|
|
721
|
+
}
|
|
722
|
+
return input;
|
|
723
|
+
}
|
|
724
|
+
function isRecord(input) {
|
|
725
|
+
return typeof input === "object" && input !== null && !Array.isArray(input);
|
|
726
|
+
}
|
|
727
|
+
function requireString(input, field) {
|
|
728
|
+
if (typeof input !== "string" || input.length === 0) {
|
|
729
|
+
throw new Error(`${field} must be a non-empty string`);
|
|
730
|
+
}
|
|
731
|
+
return input;
|
|
732
|
+
}
|
|
733
|
+
function optionalPositiveInt(input, field) {
|
|
734
|
+
if (input === void 0) {
|
|
735
|
+
return void 0;
|
|
736
|
+
}
|
|
737
|
+
if (typeof input !== "number" || !Number.isSafeInteger(input) || input <= 0) {
|
|
738
|
+
throw new Error(`${field} must be a positive safe integer`);
|
|
739
|
+
}
|
|
740
|
+
return input;
|
|
741
|
+
}
|
|
742
|
+
function optionalPositiveNumber(input, field) {
|
|
743
|
+
if (input === void 0) {
|
|
744
|
+
return void 0;
|
|
745
|
+
}
|
|
746
|
+
if (typeof input !== "number" || !Number.isFinite(input) || input <= 0) {
|
|
747
|
+
throw new Error(`${field} must be a positive finite number`);
|
|
748
|
+
}
|
|
749
|
+
return input;
|
|
750
|
+
}
|
|
751
|
+
function parseRunWebhook(input) {
|
|
752
|
+
if (input === void 0) {
|
|
753
|
+
return void 0;
|
|
754
|
+
}
|
|
755
|
+
const value = requireRecord(input, "webhook");
|
|
756
|
+
const allowed = /* @__PURE__ */ new Set(["url"]);
|
|
757
|
+
for (const key of Object.keys(value)) {
|
|
758
|
+
if (!allowed.has(key)) {
|
|
759
|
+
throw new Error(`webhook.${key} is not an allowed field; permitted: ${[...allowed].join(", ")}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const url = requireString(value.url, "webhook.url");
|
|
763
|
+
let parsed;
|
|
764
|
+
try {
|
|
765
|
+
parsed = new URL(url);
|
|
766
|
+
} catch {
|
|
767
|
+
throw new Error(`webhook.url must be a valid absolute URL (got ${JSON.stringify(url)})`);
|
|
768
|
+
}
|
|
769
|
+
if (parsed.protocol !== "https:") {
|
|
770
|
+
throw new Error(`webhook.url must use https (got ${parsed.protocol.replace(/:$/, "")})`);
|
|
771
|
+
}
|
|
772
|
+
if (parsed.username !== "" || parsed.password !== "") {
|
|
773
|
+
throw new Error("webhook.url must not contain userinfo (user:pass@host)");
|
|
774
|
+
}
|
|
775
|
+
return { url };
|
|
776
|
+
}
|
|
777
|
+
function parseRunLimits(input) {
|
|
778
|
+
if (input === void 0) {
|
|
779
|
+
return void 0;
|
|
780
|
+
}
|
|
781
|
+
const value = requireRecord(input, "limits");
|
|
782
|
+
const allowed = /* @__PURE__ */ new Set(["maxConcurrentChildRuns", "maxSubagentDepth", "maxSpendUsd", "maxTurns"]);
|
|
783
|
+
for (const key of Object.keys(value)) {
|
|
784
|
+
if (!allowed.has(key)) {
|
|
785
|
+
throw new Error(`limits.${key} is not an allowed field; permitted: ${[...allowed].join(", ")}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
const maxConcurrentChildRuns = optionalPositiveInt(value.maxConcurrentChildRuns, "limits.maxConcurrentChildRuns");
|
|
789
|
+
const maxSubagentDepth = optionalPositiveInt(value.maxSubagentDepth, "limits.maxSubagentDepth");
|
|
790
|
+
const maxSpendUsd = optionalPositiveNumber(value.maxSpendUsd, "limits.maxSpendUsd");
|
|
791
|
+
const maxTurns = optionalPositiveInt(value.maxTurns, "limits.maxTurns");
|
|
792
|
+
if (maxConcurrentChildRuns === void 0 && maxSubagentDepth === void 0 && maxSpendUsd === void 0 && maxTurns === void 0) {
|
|
793
|
+
return void 0;
|
|
794
|
+
}
|
|
795
|
+
return {
|
|
796
|
+
...maxConcurrentChildRuns !== void 0 ? { maxConcurrentChildRuns } : {},
|
|
797
|
+
...maxSubagentDepth !== void 0 ? { maxSubagentDepth } : {},
|
|
798
|
+
...maxSpendUsd !== void 0 ? { maxSpendUsd } : {},
|
|
799
|
+
...maxTurns !== void 0 ? { maxTurns } : {}
|
|
800
|
+
};
|
|
801
|
+
}
|
|
544
802
|
var BUILTIN_TOOL_NAMES = [
|
|
545
803
|
"bash",
|
|
546
804
|
"read_file",
|
|
@@ -559,10 +817,14 @@ var BUILTIN_TOOL_NAMES = [
|
|
|
559
817
|
"bash_kill",
|
|
560
818
|
"code_execution",
|
|
561
819
|
"wait",
|
|
562
|
-
"git"
|
|
820
|
+
"git",
|
|
821
|
+
"ls",
|
|
822
|
+
"stat",
|
|
823
|
+
"wc"
|
|
563
824
|
];
|
|
564
825
|
var DEFAULT_BUILTIN_TOOLS = BUILTIN_TOOL_NAMES;
|
|
565
826
|
var MAX_OUTPUT_CAPTURE_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
|
|
827
|
+
var SKILLS_MAX = 64;
|
|
566
828
|
|
|
567
829
|
// ../contracts/dist/event-envelope.js
|
|
568
830
|
var AEX_EVENT_SOURCES = ["agent", "api", "runtime", "mcp", "aex", "workflow", "host"];
|
|
@@ -580,7 +842,14 @@ var AEX_EVENT_TYPES = [
|
|
|
580
842
|
// off-the-shelf AG-UI client filters logs out by `channel`.
|
|
581
843
|
"LOG"
|
|
582
844
|
];
|
|
583
|
-
var AEX_SESSION_PARKED_NAMES = [
|
|
845
|
+
var AEX_SESSION_PARKED_NAMES = [
|
|
846
|
+
"aex.session.idle",
|
|
847
|
+
"aex.session.suspended",
|
|
848
|
+
"aex.session.succeeded",
|
|
849
|
+
"aex.session.failed",
|
|
850
|
+
"aex.session.timed_out",
|
|
851
|
+
"aex.session.cancelled"
|
|
852
|
+
];
|
|
584
853
|
function isSessionParked(e) {
|
|
585
854
|
const name = customName(e);
|
|
586
855
|
return name !== null && AEX_SESSION_PARKED_NAMES.includes(name);
|
|
@@ -847,8 +1116,8 @@ function parseRunUnitSubmission(input, fallbackModel) {
|
|
|
847
1116
|
return fallbackFlat(fallbackModel);
|
|
848
1117
|
}
|
|
849
1118
|
function parseFlatProjection(value, fallbackModel) {
|
|
850
|
-
const submissionRaw =
|
|
851
|
-
const outputsRaw =
|
|
1119
|
+
const submissionRaw = isRecord2(value.submission) ? value.submission : {};
|
|
1120
|
+
const outputsRaw = isRecord2(submissionRaw.outputs) ? submissionRaw.outputs : {};
|
|
852
1121
|
const allowedDirs = toOptionalStringArray(outputsRaw.allowedDirs);
|
|
853
1122
|
const deniedDirs = toOptionalStringArray(outputsRaw.deniedDirs);
|
|
854
1123
|
const captureTimeoutMs = toOptionalPositiveInteger(outputsRaw.captureTimeoutMs);
|
|
@@ -901,12 +1170,12 @@ function fallbackFlat(fallbackModel) {
|
|
|
901
1170
|
}
|
|
902
1171
|
};
|
|
903
1172
|
}
|
|
904
|
-
function
|
|
1173
|
+
function isRecord2(value) {
|
|
905
1174
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
906
1175
|
}
|
|
907
1176
|
function normalizeRunUnit(raw) {
|
|
908
|
-
const r =
|
|
909
|
-
const eventsRaw =
|
|
1177
|
+
const r = isRecord2(raw) ? raw : {};
|
|
1178
|
+
const eventsRaw = isRecord2(r.events) ? r.events : {};
|
|
910
1179
|
const str3 = (v) => typeof v === "string" ? v : void 0;
|
|
911
1180
|
const arr = (v) => Array.isArray(v) ? v : [];
|
|
912
1181
|
return {
|
|
@@ -925,7 +1194,7 @@ function normalizeRunUnit(raw) {
|
|
|
925
1194
|
// still carry the run's `model` at the top level — prefer it over the
|
|
926
1195
|
// static fallback so `unit()` never claims a model the run did not use.
|
|
927
1196
|
submission: parseRunUnitSubmission(r.submission, r.model),
|
|
928
|
-
...
|
|
1197
|
+
...isRecord2(r.capsSnapshot) ? { capsSnapshot: r.capsSnapshot } : {},
|
|
929
1198
|
attempts: arr(r.attempts),
|
|
930
1199
|
events: {
|
|
931
1200
|
entries: arr(eventsRaw.entries),
|
|
@@ -936,8 +1205,8 @@ function normalizeRunUnit(raw) {
|
|
|
936
1205
|
rawEventPages: arr(r.rawEventPages),
|
|
937
1206
|
outputs: arr(r.outputs),
|
|
938
1207
|
outputCaptureFailures: arr(r.outputCaptureFailures),
|
|
939
|
-
...
|
|
940
|
-
...
|
|
1208
|
+
...isRecord2(r.costTelemetry) ? { costTelemetry: r.costTelemetry } : {},
|
|
1209
|
+
...isRecord2(r.runtimeManifest) ? { runtimeManifest: r.runtimeManifest } : {}
|
|
941
1210
|
};
|
|
942
1211
|
}
|
|
943
1212
|
function coerceRunUnitModel(value) {
|
|
@@ -950,7 +1219,7 @@ function coerceRunUnitModel(value) {
|
|
|
950
1219
|
}
|
|
951
1220
|
}
|
|
952
1221
|
function isJsonRecord(value) {
|
|
953
|
-
return
|
|
1222
|
+
return isRecord2(value);
|
|
954
1223
|
}
|
|
955
1224
|
function toStringArray(value) {
|
|
956
1225
|
if (!Array.isArray(value)) {
|
|
@@ -979,11 +1248,11 @@ function toMcpServerRefArray(value) {
|
|
|
979
1248
|
return out;
|
|
980
1249
|
}
|
|
981
1250
|
function parseEnvironment(value) {
|
|
982
|
-
if (!
|
|
1251
|
+
if (!isRecord2(value)) {
|
|
983
1252
|
return void 0;
|
|
984
1253
|
}
|
|
985
1254
|
const env = {};
|
|
986
|
-
if (
|
|
1255
|
+
if (isRecord2(value.networking)) {
|
|
987
1256
|
const mode = value.networking.mode;
|
|
988
1257
|
const allowedHosts = value.networking.allowedHosts;
|
|
989
1258
|
if (mode === "limited" || mode === "open") {
|
|
@@ -994,7 +1263,7 @@ function parseEnvironment(value) {
|
|
|
994
1263
|
}
|
|
995
1264
|
}
|
|
996
1265
|
if (Array.isArray(value.packages)) {
|
|
997
|
-
const pkgs = value.packages.filter(
|
|
1266
|
+
const pkgs = value.packages.filter(isRecord2).map((p) => {
|
|
998
1267
|
const r = p;
|
|
999
1268
|
if (typeof r.name !== "string")
|
|
1000
1269
|
return null;
|
|
@@ -1009,7 +1278,7 @@ function parseEnvironment(value) {
|
|
|
1009
1278
|
env.packages = pkgs;
|
|
1010
1279
|
}
|
|
1011
1280
|
}
|
|
1012
|
-
if (
|
|
1281
|
+
if (isRecord2(value.envVars)) {
|
|
1013
1282
|
const out = {};
|
|
1014
1283
|
for (const [k, v] of Object.entries(value.envVars)) {
|
|
1015
1284
|
if (typeof v === "string") {
|
|
@@ -1438,6 +1707,9 @@ function isSecretKey(key) {
|
|
|
1438
1707
|
return /(?:api[_-]?key|authorization|token|secret|password|credential)/i.test(key);
|
|
1439
1708
|
}
|
|
1440
1709
|
function looksHighEntropySecret(value) {
|
|
1710
|
+
return value.split("-").some(isOpaqueSecretRun);
|
|
1711
|
+
}
|
|
1712
|
+
function isOpaqueSecretRun(value) {
|
|
1441
1713
|
if (charClassCount(value) < MIN_CHAR_CLASSES)
|
|
1442
1714
|
return false;
|
|
1443
1715
|
if (shannonEntropyBits(value) < ENTROPY_BITS_PER_CHAR)
|
|
@@ -1481,6 +1753,11 @@ var AexError = class extends Error {
|
|
|
1481
1753
|
this.details = details === void 0 ? void 0 : redactSecrets(details);
|
|
1482
1754
|
}
|
|
1483
1755
|
};
|
|
1756
|
+
var RunConfigValidationError = class extends AexError {
|
|
1757
|
+
constructor(message, details) {
|
|
1758
|
+
super("RUN_CONFIG_INVALID", message, details);
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1484
1761
|
var RunStateError = class extends AexError {
|
|
1485
1762
|
constructor(message, details) {
|
|
1486
1763
|
super("RUN_STATE_ERROR", message, details);
|
|
@@ -1489,10 +1766,58 @@ var RunStateError = class extends AexError {
|
|
|
1489
1766
|
var AexApiError = class extends AexError {
|
|
1490
1767
|
status;
|
|
1491
1768
|
body;
|
|
1492
|
-
|
|
1493
|
-
|
|
1769
|
+
/** The server's stable error code, when the body carried a known one. */
|
|
1770
|
+
apiCode;
|
|
1771
|
+
/** Request id (body `requestId` or a response header) for support correlation. */
|
|
1772
|
+
requestId;
|
|
1773
|
+
constructor(status2, message, body, options) {
|
|
1774
|
+
super("API_ERROR", message, body, options?.cause === void 0 ? void 0 : { cause: options.cause });
|
|
1494
1775
|
this.status = status2;
|
|
1495
1776
|
this.body = redactSecrets(body);
|
|
1777
|
+
this.apiCode = options?.apiCode;
|
|
1778
|
+
this.requestId = options?.requestId;
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
var AexAuthError = class extends AexApiError {
|
|
1782
|
+
/** The scope the endpoint required, when the server named it (insufficient_scope). */
|
|
1783
|
+
requiredScope;
|
|
1784
|
+
constructor(init) {
|
|
1785
|
+
super(init.status, init.message, init.body, {
|
|
1786
|
+
apiCode: init.apiCode,
|
|
1787
|
+
requestId: init.requestId,
|
|
1788
|
+
cause: init.cause
|
|
1789
|
+
});
|
|
1790
|
+
this.requiredScope = init.requiredScope;
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
var AexIdempotencyConflictError = class extends AexApiError {
|
|
1794
|
+
constructor(init) {
|
|
1795
|
+
super(init.status, init.message, init.body, {
|
|
1796
|
+
apiCode: init.apiCode,
|
|
1797
|
+
requestId: init.requestId,
|
|
1798
|
+
cause: init.cause
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1802
|
+
var AexNotFoundError = class extends AexApiError {
|
|
1803
|
+
constructor(init) {
|
|
1804
|
+
super(init.status, init.message, init.body, {
|
|
1805
|
+
apiCode: init.apiCode,
|
|
1806
|
+
requestId: init.requestId,
|
|
1807
|
+
cause: init.cause
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
var AexRateLimitError = class extends AexApiError {
|
|
1812
|
+
/** Suggested backoff (ms), when the server advertised one (Retry-After). */
|
|
1813
|
+
retryAfterMs;
|
|
1814
|
+
constructor(init) {
|
|
1815
|
+
super(init.status, init.message, init.body, {
|
|
1816
|
+
apiCode: init.apiCode,
|
|
1817
|
+
requestId: init.requestId,
|
|
1818
|
+
cause: init.cause
|
|
1819
|
+
});
|
|
1820
|
+
this.retryAfterMs = init.retryAfterMs;
|
|
1496
1821
|
}
|
|
1497
1822
|
};
|
|
1498
1823
|
var AexNetworkError = class extends AexError {
|
|
@@ -1504,6 +1829,8 @@ var AexNetworkError = class extends AexError {
|
|
|
1504
1829
|
causeCode;
|
|
1505
1830
|
/** Attempts made when a retry layer exhausted its budget; `1` otherwise. */
|
|
1506
1831
|
attempts;
|
|
1832
|
+
/** Total elapsed time (ms) across all attempts, when the retry layer set it. */
|
|
1833
|
+
elapsedMs;
|
|
1507
1834
|
constructor(args) {
|
|
1508
1835
|
const causeCode = extractErrorCode(args.cause);
|
|
1509
1836
|
super("NETWORK_ERROR", networkErrorMessage(args, causeCode), { method: args.method, host: args.host, path: args.path, ...causeCode ? { code: causeCode } : {} }, { cause: args.cause });
|
|
@@ -1512,6 +1839,7 @@ var AexNetworkError = class extends AexError {
|
|
|
1512
1839
|
this.path = args.path;
|
|
1513
1840
|
this.causeCode = causeCode;
|
|
1514
1841
|
this.attempts = args.attempts ?? 1;
|
|
1842
|
+
this.elapsedMs = args.elapsedMs;
|
|
1515
1843
|
}
|
|
1516
1844
|
};
|
|
1517
1845
|
function networkErrorMessage(args, causeCode) {
|
|
@@ -1575,9 +1903,185 @@ function stringProperty(value, key) {
|
|
|
1575
1903
|
return typeof prop === "string" && prop.length > 0 ? prop : void 0;
|
|
1576
1904
|
}
|
|
1577
1905
|
|
|
1906
|
+
// ../contracts/dist/bundle-manifest.js
|
|
1907
|
+
var META_TEXT_ENCODER = new TextEncoder();
|
|
1908
|
+
var META_TEXT_DECODER = new TextDecoder("utf-8", { fatal: false });
|
|
1909
|
+
|
|
1578
1910
|
// ../contracts/dist/webhook-verify.js
|
|
1579
1911
|
var encoder2 = new TextEncoder();
|
|
1580
1912
|
|
|
1913
|
+
// ../contracts/dist/error-codes.js
|
|
1914
|
+
var AEX_API_ERROR_CODES = [
|
|
1915
|
+
"unauthorized",
|
|
1916
|
+
"forbidden",
|
|
1917
|
+
"insufficient_scope",
|
|
1918
|
+
"token_invalid",
|
|
1919
|
+
"token_revoked",
|
|
1920
|
+
"token_expired",
|
|
1921
|
+
"malformed_token",
|
|
1922
|
+
"not_found",
|
|
1923
|
+
"idempotency_conflict",
|
|
1924
|
+
"session_busy",
|
|
1925
|
+
"run_not_terminal",
|
|
1926
|
+
"unknown_workspace",
|
|
1927
|
+
"workspace_concurrency_exceeded",
|
|
1928
|
+
"workspace_submit_rate_exceeded",
|
|
1929
|
+
"workspace_spend_cap_exceeded",
|
|
1930
|
+
"insufficient_balance",
|
|
1931
|
+
"rate_limited",
|
|
1932
|
+
"upstream_error",
|
|
1933
|
+
"internal_error"
|
|
1934
|
+
];
|
|
1935
|
+
var AEX_API_ERROR_MESSAGES = {
|
|
1936
|
+
unauthorized: "The request was not authenticated.",
|
|
1937
|
+
forbidden: "The API key is not permitted to perform this action.",
|
|
1938
|
+
insufficient_scope: "The API key is missing a scope this endpoint requires.",
|
|
1939
|
+
token_invalid: "The API key is not valid.",
|
|
1940
|
+
token_revoked: "The API key has been revoked.",
|
|
1941
|
+
token_expired: "The API key has expired.",
|
|
1942
|
+
malformed_token: "The API key is malformed.",
|
|
1943
|
+
not_found: "The requested resource was not found.",
|
|
1944
|
+
idempotency_conflict: "This idempotency key was already used with a different request body.",
|
|
1945
|
+
session_busy: "The session is busy handling another turn.",
|
|
1946
|
+
run_not_terminal: "The run has not reached a terminal state yet.",
|
|
1947
|
+
unknown_workspace: "The workspace could not be resolved from the API key.",
|
|
1948
|
+
workspace_concurrency_exceeded: "The workspace has reached its concurrent-run limit.",
|
|
1949
|
+
workspace_submit_rate_exceeded: "The workspace submit-rate limit was exceeded.",
|
|
1950
|
+
workspace_spend_cap_exceeded: "The workspace monthly spend cap was reached.",
|
|
1951
|
+
insufficient_balance: "The workspace prepaid balance is insufficient to submit this run.",
|
|
1952
|
+
rate_limited: "Too many requests \u2014 retry after a short backoff.",
|
|
1953
|
+
upstream_error: "An upstream provider returned an error.",
|
|
1954
|
+
internal_error: "The aex API encountered an internal error."
|
|
1955
|
+
};
|
|
1956
|
+
var API_ERROR_CODE_SET = new Set(AEX_API_ERROR_CODES);
|
|
1957
|
+
function isAexApiErrorCode(value) {
|
|
1958
|
+
return typeof value === "string" && API_ERROR_CODE_SET.has(value);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// ../contracts/dist/error-factory.js
|
|
1962
|
+
function apiErrorKindForCode(code) {
|
|
1963
|
+
switch (code) {
|
|
1964
|
+
case "unauthorized":
|
|
1965
|
+
case "forbidden":
|
|
1966
|
+
case "insufficient_scope":
|
|
1967
|
+
case "token_invalid":
|
|
1968
|
+
case "token_revoked":
|
|
1969
|
+
case "token_expired":
|
|
1970
|
+
case "malformed_token":
|
|
1971
|
+
return "auth";
|
|
1972
|
+
case "idempotency_conflict":
|
|
1973
|
+
return "idempotency";
|
|
1974
|
+
case "not_found":
|
|
1975
|
+
return "not_found";
|
|
1976
|
+
case "rate_limited":
|
|
1977
|
+
case "workspace_concurrency_exceeded":
|
|
1978
|
+
case "workspace_submit_rate_exceeded":
|
|
1979
|
+
return "rate_limit";
|
|
1980
|
+
case "session_busy":
|
|
1981
|
+
case "run_not_terminal":
|
|
1982
|
+
case "unknown_workspace":
|
|
1983
|
+
case "workspace_spend_cap_exceeded":
|
|
1984
|
+
case "insufficient_balance":
|
|
1985
|
+
case "upstream_error":
|
|
1986
|
+
case "internal_error":
|
|
1987
|
+
return "generic";
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
function kindForStatus(status2) {
|
|
1991
|
+
if (status2 === 401 || status2 === 403)
|
|
1992
|
+
return "auth";
|
|
1993
|
+
if (status2 === 404)
|
|
1994
|
+
return "not_found";
|
|
1995
|
+
if (status2 === 409)
|
|
1996
|
+
return "idempotency";
|
|
1997
|
+
if (status2 === 429)
|
|
1998
|
+
return "rate_limit";
|
|
1999
|
+
return "generic";
|
|
2000
|
+
}
|
|
2001
|
+
function apiErrorFromResponse(input) {
|
|
2002
|
+
const apiCode = apiCodeFromBody(input.body);
|
|
2003
|
+
const requestId = input.requestId ?? requestIdFromBody(input.body);
|
|
2004
|
+
const message = input.message ?? fallbackMessage(apiCode, input.body);
|
|
2005
|
+
const kind = apiCode !== void 0 ? apiErrorKindForCode(apiCode) : kindForStatus(input.status);
|
|
2006
|
+
const init = {
|
|
2007
|
+
status: input.status,
|
|
2008
|
+
message,
|
|
2009
|
+
body: input.body,
|
|
2010
|
+
apiCode,
|
|
2011
|
+
requestId,
|
|
2012
|
+
cause: input.cause
|
|
2013
|
+
};
|
|
2014
|
+
switch (kind) {
|
|
2015
|
+
case "auth":
|
|
2016
|
+
return new AexAuthError({ ...init, requiredScope: requiredScopeFromBody(input.body) });
|
|
2017
|
+
case "idempotency":
|
|
2018
|
+
return new AexIdempotencyConflictError(init);
|
|
2019
|
+
case "not_found":
|
|
2020
|
+
return new AexNotFoundError(init);
|
|
2021
|
+
case "rate_limit":
|
|
2022
|
+
return new AexRateLimitError({ ...init, retryAfterMs: retryAfterMsFromBody(input.body) });
|
|
2023
|
+
case "generic":
|
|
2024
|
+
return new AexApiError(input.status, message, input.body, {
|
|
2025
|
+
apiCode,
|
|
2026
|
+
requestId,
|
|
2027
|
+
...input.cause !== void 0 ? { cause: input.cause } : {}
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
function asRecord(body) {
|
|
2032
|
+
return body !== null && typeof body === "object" && !Array.isArray(body) ? body : void 0;
|
|
2033
|
+
}
|
|
2034
|
+
function apiCodeFromBody(body) {
|
|
2035
|
+
const record = asRecord(body);
|
|
2036
|
+
if (!record)
|
|
2037
|
+
return void 0;
|
|
2038
|
+
if (isAexApiErrorCode(record.error))
|
|
2039
|
+
return record.error;
|
|
2040
|
+
if (isAexApiErrorCode(record.code))
|
|
2041
|
+
return record.code;
|
|
2042
|
+
return void 0;
|
|
2043
|
+
}
|
|
2044
|
+
function requestIdFromBody(body) {
|
|
2045
|
+
const record = asRecord(body);
|
|
2046
|
+
const value = record?.requestId;
|
|
2047
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
2048
|
+
}
|
|
2049
|
+
function requiredScopeFromBody(body) {
|
|
2050
|
+
const record = asRecord(body);
|
|
2051
|
+
if (!record)
|
|
2052
|
+
return void 0;
|
|
2053
|
+
for (const key of ["requiredScope", "required_scope", "scope"]) {
|
|
2054
|
+
const value = record[key];
|
|
2055
|
+
if (typeof value === "string" && value.length > 0)
|
|
2056
|
+
return value;
|
|
2057
|
+
}
|
|
2058
|
+
return void 0;
|
|
2059
|
+
}
|
|
2060
|
+
function retryAfterMsFromBody(body) {
|
|
2061
|
+
const record = asRecord(body);
|
|
2062
|
+
if (!record)
|
|
2063
|
+
return void 0;
|
|
2064
|
+
if (typeof record.retryAfterMs === "number" && Number.isFinite(record.retryAfterMs)) {
|
|
2065
|
+
return record.retryAfterMs;
|
|
2066
|
+
}
|
|
2067
|
+
if (typeof record.retryAfter === "number" && Number.isFinite(record.retryAfter)) {
|
|
2068
|
+
return record.retryAfter * 1e3;
|
|
2069
|
+
}
|
|
2070
|
+
return void 0;
|
|
2071
|
+
}
|
|
2072
|
+
function fallbackMessage(apiCode, body) {
|
|
2073
|
+
if (apiCode !== void 0)
|
|
2074
|
+
return AEX_API_ERROR_MESSAGES[apiCode];
|
|
2075
|
+
const record = asRecord(body);
|
|
2076
|
+
const message = record?.message;
|
|
2077
|
+
if (typeof message === "string" && message.length > 0)
|
|
2078
|
+
return message;
|
|
2079
|
+
const error = record?.error;
|
|
2080
|
+
if (typeof error === "string" && error.length > 0)
|
|
2081
|
+
return error;
|
|
2082
|
+
return "aex API request failed";
|
|
2083
|
+
}
|
|
2084
|
+
|
|
1581
2085
|
// ../contracts/dist/http.js
|
|
1582
2086
|
var HttpClient = class {
|
|
1583
2087
|
#baseUrl;
|
|
@@ -1629,7 +2133,11 @@ var HttpClient = class {
|
|
|
1629
2133
|
const body = await readJson(response);
|
|
1630
2134
|
if (!response.ok) {
|
|
1631
2135
|
const errorBody = withResponseRequestId(body, response.headers);
|
|
1632
|
-
throw
|
|
2136
|
+
throw apiErrorFromResponse({
|
|
2137
|
+
status: response.status,
|
|
2138
|
+
body: errorBody,
|
|
2139
|
+
message: extractErrorMessage(errorBody)
|
|
2140
|
+
});
|
|
1633
2141
|
}
|
|
1634
2142
|
return body;
|
|
1635
2143
|
}
|
|
@@ -1653,7 +2161,11 @@ var HttpClient = class {
|
|
|
1653
2161
|
if (!response.ok) {
|
|
1654
2162
|
const body = await readJson(response);
|
|
1655
2163
|
const errorBody = withResponseRequestId(body, response.headers);
|
|
1656
|
-
throw
|
|
2164
|
+
throw apiErrorFromResponse({
|
|
2165
|
+
status: response.status,
|
|
2166
|
+
body: errorBody,
|
|
2167
|
+
message: extractErrorMessage(errorBody)
|
|
2168
|
+
});
|
|
1657
2169
|
}
|
|
1658
2170
|
return { response };
|
|
1659
2171
|
}
|
|
@@ -1735,6 +2247,7 @@ var operations_exports = {};
|
|
|
1735
2247
|
__export(operations_exports, {
|
|
1736
2248
|
READ_OUTPUT_TEXT_DEFAULT_BYTES: () => READ_OUTPUT_TEXT_DEFAULT_BYTES,
|
|
1737
2249
|
READ_OUTPUT_TEXT_MAX_BYTES: () => READ_OUTPUT_TEXT_MAX_BYTES,
|
|
2250
|
+
approveSession: () => approveSession,
|
|
1738
2251
|
cancelRun: () => cancelRun,
|
|
1739
2252
|
cancelSession: () => cancelSession,
|
|
1740
2253
|
classifyOutput: () => classifyOutput,
|
|
@@ -1748,7 +2261,9 @@ __export(operations_exports, {
|
|
|
1748
2261
|
deleteRun: () => deleteRun,
|
|
1749
2262
|
deleteSecret: () => deleteSecret,
|
|
1750
2263
|
deleteSession: () => deleteSession,
|
|
2264
|
+
deleteSkill: () => deleteSkill,
|
|
1751
2265
|
deleteWorkspaceAsset: () => deleteWorkspaceAsset,
|
|
2266
|
+
denySession: () => denySession,
|
|
1752
2267
|
download: () => download,
|
|
1753
2268
|
downloadEvents: () => downloadEvents,
|
|
1754
2269
|
downloadMetadata: () => downloadMetadata,
|
|
@@ -1770,10 +2285,12 @@ __export(operations_exports, {
|
|
|
1770
2285
|
getSecretValue: () => getSecretValue,
|
|
1771
2286
|
getSession: () => getSession,
|
|
1772
2287
|
getSessionCoordinatorTicket: () => getSessionCoordinatorTicket,
|
|
2288
|
+
getSkill: () => getSkill,
|
|
1773
2289
|
getWebhookSigningSecret: () => getWebhookSigningSecret,
|
|
1774
2290
|
listAgentsMd: () => listAgentsMd,
|
|
1775
2291
|
listFiles: () => listFiles,
|
|
1776
2292
|
listOutputs: () => listOutputs,
|
|
2293
|
+
listRunChildren: () => listRunChildren,
|
|
1777
2294
|
listRunEvents: () => listRunEvents,
|
|
1778
2295
|
listRuns: () => listRuns,
|
|
1779
2296
|
listSecrets: () => listSecrets,
|
|
@@ -1781,17 +2298,23 @@ __export(operations_exports, {
|
|
|
1781
2298
|
listSessionMessages: () => listSessionMessages,
|
|
1782
2299
|
listSessionOutputs: () => listSessionOutputs,
|
|
1783
2300
|
listSessions: () => listSessions,
|
|
2301
|
+
listSkills: () => listSkills,
|
|
1784
2302
|
normalizeOutputLinkExpiresIn: () => normalizeOutputLinkExpiresIn,
|
|
1785
2303
|
outputLink: () => outputLink,
|
|
1786
2304
|
readOutputText: () => readOutputText,
|
|
1787
2305
|
redeliverRunWebhook: () => redeliverRunWebhook,
|
|
2306
|
+
requestApproval: () => requestApproval,
|
|
2307
|
+
resolveIdempotencyKey: () => resolveIdempotencyKey,
|
|
1788
2308
|
resolveOutputFileSelector: () => resolveOutputFileSelector,
|
|
1789
2309
|
resumeSession: () => resumeSession,
|
|
1790
2310
|
rotateSecret: () => rotateSecret,
|
|
1791
2311
|
sendSessionMessage: () => sendSessionMessage,
|
|
2312
|
+
submit: () => submit,
|
|
1792
2313
|
submitRun: () => submitRun,
|
|
1793
2314
|
suspendSession: () => suspendSession,
|
|
2315
|
+
toFilenameMatcher: () => toFilenameMatcher,
|
|
1794
2316
|
uploadWorkspaceAsset: () => uploadWorkspaceAsset,
|
|
2317
|
+
upsertSkill: () => upsertSkill,
|
|
1795
2318
|
whoami: () => whoami
|
|
1796
2319
|
});
|
|
1797
2320
|
|
|
@@ -2512,6 +3035,7 @@ function zipSync(data, opts) {
|
|
|
2512
3035
|
}
|
|
2513
3036
|
|
|
2514
3037
|
// ../contracts/dist/operations.js
|
|
3038
|
+
import { randomUUID } from "node:crypto";
|
|
2515
3039
|
async function getRun(http, runId) {
|
|
2516
3040
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
2517
3041
|
return hasRun(result) ? result.run : result;
|
|
@@ -2547,8 +3071,28 @@ async function listRuns(http, query) {
|
|
|
2547
3071
|
}
|
|
2548
3072
|
return changed ? { ...page, runs } : page;
|
|
2549
3073
|
}
|
|
3074
|
+
function resolveIdempotencyKey(key) {
|
|
3075
|
+
if (key === void 0) {
|
|
3076
|
+
return `aex-idem-${randomUUID()}`;
|
|
3077
|
+
}
|
|
3078
|
+
if (typeof key !== "string" || key.trim().length === 0) {
|
|
3079
|
+
throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
|
|
3080
|
+
field: "idempotencyKey",
|
|
3081
|
+
value: key
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
return key;
|
|
3085
|
+
}
|
|
2550
3086
|
function idempotencyHeaders(options) {
|
|
2551
|
-
|
|
3087
|
+
if (options?.idempotencyKey === void 0)
|
|
3088
|
+
return void 0;
|
|
3089
|
+
if (typeof options.idempotencyKey !== "string" || options.idempotencyKey.trim().length === 0) {
|
|
3090
|
+
throw new RunConfigValidationError("idempotencyKey must be a non-empty, non-whitespace string", {
|
|
3091
|
+
field: "idempotencyKey",
|
|
3092
|
+
value: options.idempotencyKey
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
return { "Idempotency-Key": options.idempotencyKey };
|
|
2552
3096
|
}
|
|
2553
3097
|
async function createSession(http, request, options) {
|
|
2554
3098
|
const headers = idempotencyHeaders(options);
|
|
@@ -2559,6 +3103,10 @@ async function createSession(http, request, options) {
|
|
|
2559
3103
|
});
|
|
2560
3104
|
return unwrapSession(result);
|
|
2561
3105
|
}
|
|
3106
|
+
async function submit(http, request, options) {
|
|
3107
|
+
const session = await createSession(http, request, options);
|
|
3108
|
+
return { runId: session.sessionId ?? session.id, session };
|
|
3109
|
+
}
|
|
2562
3110
|
async function getSession(http, sessionId) {
|
|
2563
3111
|
const result = await http.request(`/api/sessions/${encodeURIComponent(sessionId)}`);
|
|
2564
3112
|
return unwrapSession(result);
|
|
@@ -2605,6 +3153,18 @@ async function resumeSession(http, sessionId, options) {
|
|
|
2605
3153
|
const headers = idempotencyHeaders(options);
|
|
2606
3154
|
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/resume`, { method: "POST", ...headers ? { headers } : {} });
|
|
2607
3155
|
}
|
|
3156
|
+
async function requestApproval(http, sessionId, options) {
|
|
3157
|
+
const headers = idempotencyHeaders(options);
|
|
3158
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/request-approval`, { method: "POST", ...headers ? { headers } : {} });
|
|
3159
|
+
}
|
|
3160
|
+
async function approveSession(http, sessionId, options) {
|
|
3161
|
+
const headers = idempotencyHeaders(options);
|
|
3162
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/approve`, { method: "POST", ...headers ? { headers } : {} });
|
|
3163
|
+
}
|
|
3164
|
+
async function denySession(http, sessionId, options) {
|
|
3165
|
+
const headers = idempotencyHeaders(options);
|
|
3166
|
+
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}/deny`, { method: "POST", ...headers ? { headers } : {} });
|
|
3167
|
+
}
|
|
2608
3168
|
async function deleteSession(http, sessionId, options) {
|
|
2609
3169
|
const headers = idempotencyHeaders(options);
|
|
2610
3170
|
return http.request(`/api/sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE", ...headers ? { headers } : {} });
|
|
@@ -2661,7 +3221,7 @@ async function findOutput(http, runId, query) {
|
|
|
2661
3221
|
return null;
|
|
2662
3222
|
if (matches.length === 1)
|
|
2663
3223
|
return matches[0];
|
|
2664
|
-
throw new RunStateError("
|
|
3224
|
+
throw new RunStateError("outputs.findOne: output query matched multiple files", {
|
|
2665
3225
|
runId,
|
|
2666
3226
|
matches: matches.map((output) => output.filename ?? output.id)
|
|
2667
3227
|
});
|
|
@@ -2704,7 +3264,7 @@ function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
2704
3264
|
if (isPathSelector(selector)) {
|
|
2705
3265
|
const target = normalizeOutputLookupPath(selector.path);
|
|
2706
3266
|
if (!target) {
|
|
2707
|
-
throw new RunStateError("
|
|
3267
|
+
throw new RunStateError("outputs.download: output path must be non-empty", { runId, path: selector.path });
|
|
2708
3268
|
}
|
|
2709
3269
|
const matches = outputs.filter((output) => {
|
|
2710
3270
|
if (typeof output.filename !== "string")
|
|
@@ -2718,15 +3278,15 @@ function resolveOutputFileSelector(outputs, selector, runId) {
|
|
|
2718
3278
|
if (matches.length === 1)
|
|
2719
3279
|
return matches[0];
|
|
2720
3280
|
if (matches.length > 1) {
|
|
2721
|
-
throw new RunStateError(`
|
|
3281
|
+
throw new RunStateError(`outputs.download: output path "${selector.path}" matched multiple files`, { runId, path: selector.path, matches: matches.map((output) => output.filename ?? output.id) });
|
|
2722
3282
|
}
|
|
2723
|
-
throw new RunStateError(`
|
|
3283
|
+
throw new RunStateError(`outputs.download: output path "${selector.path}" was not found`, {
|
|
2724
3284
|
runId,
|
|
2725
3285
|
path: selector.path
|
|
2726
3286
|
});
|
|
2727
3287
|
}
|
|
2728
3288
|
if (typeof selector?.id !== "string" || selector.id.length === 0) {
|
|
2729
|
-
throw new RunStateError("
|
|
3289
|
+
throw new RunStateError("outputs.download: selector must include an output id or path", { runId });
|
|
2730
3290
|
}
|
|
2731
3291
|
return { ...selector, id: selector.id };
|
|
2732
3292
|
}
|
|
@@ -2803,6 +3363,10 @@ function grepLines(text, pattern) {
|
|
|
2803
3363
|
const test = typeof pattern === "string" ? (line) => line.toLowerCase().includes(pattern.toLowerCase()) : (line) => pattern.test(line);
|
|
2804
3364
|
return text.split("\n").filter((line) => test(line)).join("\n");
|
|
2805
3365
|
}
|
|
3366
|
+
async function listRunChildren(http, runId) {
|
|
3367
|
+
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/children`);
|
|
3368
|
+
return Array.isArray(result) ? result : result.children;
|
|
3369
|
+
}
|
|
2806
3370
|
async function cancelRun(http, runId) {
|
|
2807
3371
|
await http.request(`/api/runs/${encodeURIComponent(runId)}/cancel`, { method: "POST" });
|
|
2808
3372
|
}
|
|
@@ -2885,6 +3449,16 @@ function normalizeOutputLookupPath(path) {
|
|
|
2885
3449
|
function filterOutputs(outputs, query) {
|
|
2886
3450
|
return outputs.filter((output) => outputMatchesQuery(output, query));
|
|
2887
3451
|
}
|
|
3452
|
+
function toFilenameMatcher(filename) {
|
|
3453
|
+
if (typeof filename === "string") {
|
|
3454
|
+
const needle = filename.toLowerCase();
|
|
3455
|
+
return (name) => name.toLowerCase().includes(needle);
|
|
3456
|
+
}
|
|
3457
|
+
return (name) => {
|
|
3458
|
+
filename.lastIndex = 0;
|
|
3459
|
+
return filename.test(name);
|
|
3460
|
+
};
|
|
3461
|
+
}
|
|
2888
3462
|
function classifyOutput(output) {
|
|
2889
3463
|
const contentType = normalizeContentType(output.contentType);
|
|
2890
3464
|
if (contentType) {
|
|
@@ -3113,7 +3687,7 @@ function jsonlEntry(path, events) {
|
|
|
3113
3687
|
}
|
|
3114
3688
|
function extractSubmissionSnapshot(run) {
|
|
3115
3689
|
const raw = run.submission;
|
|
3116
|
-
if (!
|
|
3690
|
+
if (!isRecord3(raw) || raw.kind !== "submission" || !isRecord3(raw.submission)) {
|
|
3117
3691
|
return void 0;
|
|
3118
3692
|
}
|
|
3119
3693
|
return {
|
|
@@ -3122,9 +3696,9 @@ function extractSubmissionSnapshot(run) {
|
|
|
3122
3696
|
}
|
|
3123
3697
|
function extractCostTelemetry(run) {
|
|
3124
3698
|
const raw = run.costTelemetry;
|
|
3125
|
-
return
|
|
3699
|
+
return isRecord3(raw) ? raw : void 0;
|
|
3126
3700
|
}
|
|
3127
|
-
function
|
|
3701
|
+
function isRecord3(value) {
|
|
3128
3702
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3129
3703
|
}
|
|
3130
3704
|
async function submitRun(http, request) {
|
|
@@ -3215,6 +3789,43 @@ function unwrapSecret(result) {
|
|
|
3215
3789
|
}
|
|
3216
3790
|
return result;
|
|
3217
3791
|
}
|
|
3792
|
+
async function upsertSkill(http, args) {
|
|
3793
|
+
const result = await http.request(`/api/skills/${encodeURIComponent(args.name)}`, {
|
|
3794
|
+
method: "PUT",
|
|
3795
|
+
body: JSON.stringify({
|
|
3796
|
+
contentHash: args.contentHash,
|
|
3797
|
+
description: args.description,
|
|
3798
|
+
sizeBytes: args.sizeBytes
|
|
3799
|
+
})
|
|
3800
|
+
});
|
|
3801
|
+
if (result && typeof result === "object" && "skill" in result) {
|
|
3802
|
+
const wrapped = result;
|
|
3803
|
+
return { skill: wrapped.skill, updated: wrapped.updated === true };
|
|
3804
|
+
}
|
|
3805
|
+
return { skill: result, updated: true };
|
|
3806
|
+
}
|
|
3807
|
+
async function listSkills(http) {
|
|
3808
|
+
const result = await http.request("/api/skills");
|
|
3809
|
+
if (Array.isArray(result)) {
|
|
3810
|
+
return result;
|
|
3811
|
+
}
|
|
3812
|
+
return result.skills;
|
|
3813
|
+
}
|
|
3814
|
+
async function getSkill(http, name) {
|
|
3815
|
+
const result = await http.request(`/api/skills/${encodeURIComponent(name)}`);
|
|
3816
|
+
return unwrapSkill(result);
|
|
3817
|
+
}
|
|
3818
|
+
async function deleteSkill(http, name) {
|
|
3819
|
+
await http.request(`/api/skills/${encodeURIComponent(name)}`, {
|
|
3820
|
+
method: "DELETE"
|
|
3821
|
+
});
|
|
3822
|
+
}
|
|
3823
|
+
function unwrapSkill(result) {
|
|
3824
|
+
if (result && typeof result === "object" && "skill" in result) {
|
|
3825
|
+
return result.skill;
|
|
3826
|
+
}
|
|
3827
|
+
return result;
|
|
3828
|
+
}
|
|
3218
3829
|
function hasRun(value) {
|
|
3219
3830
|
return Boolean(value && typeof value === "object" && "run" in value);
|
|
3220
3831
|
}
|
|
@@ -3236,6 +3847,16 @@ async function uploadWorkspaceAsset(http, input) {
|
|
|
3236
3847
|
});
|
|
3237
3848
|
}
|
|
3238
3849
|
|
|
3850
|
+
// ../contracts/dist/api-key.js
|
|
3851
|
+
var API_KEY_PLANES = ["dev", "prd"];
|
|
3852
|
+
var API_KEY_REGION_TO_CODE = {
|
|
3853
|
+
"eu-west-2": "euw2",
|
|
3854
|
+
"us-west-2": "usw2",
|
|
3855
|
+
"ap-northeast-1": "apn1"
|
|
3856
|
+
};
|
|
3857
|
+
var CODE_TO_REGION = Object.fromEntries(Object.entries(API_KEY_REGION_TO_CODE).map(([region, code]) => [code, region]));
|
|
3858
|
+
var API_KEY_PLANE_SET = new Set(API_KEY_PLANES);
|
|
3859
|
+
|
|
3239
3860
|
// dist/internal.js
|
|
3240
3861
|
var AEX_INDEX_PATH = "/mnt/session/uploads/aex/index.json";
|
|
3241
3862
|
|
|
@@ -3255,6 +3876,7 @@ function extractCommonHostFlags(argv) {
|
|
|
3255
3876
|
let apiKey = null;
|
|
3256
3877
|
let aexUrl = null;
|
|
3257
3878
|
let debug2 = false;
|
|
3879
|
+
let json = false;
|
|
3258
3880
|
const rest = [];
|
|
3259
3881
|
for (let i2 = 0; i2 < argv.length; i2++) {
|
|
3260
3882
|
const arg = argv[i2];
|
|
@@ -3262,6 +3884,10 @@ function extractCommonHostFlags(argv) {
|
|
|
3262
3884
|
debug2 = true;
|
|
3263
3885
|
continue;
|
|
3264
3886
|
}
|
|
3887
|
+
if (arg === "--json") {
|
|
3888
|
+
json = true;
|
|
3889
|
+
continue;
|
|
3890
|
+
}
|
|
3265
3891
|
if (arg === "--api-key") {
|
|
3266
3892
|
const v = argv[++i2];
|
|
3267
3893
|
if (v === void 0)
|
|
@@ -3284,13 +3910,13 @@ function extractCommonHostFlags(argv) {
|
|
|
3284
3910
|
}
|
|
3285
3911
|
rest.push(arg);
|
|
3286
3912
|
}
|
|
3287
|
-
return { ok: true, flags: { apiKey, aexUrl, debug: debug2, rest } };
|
|
3913
|
+
return { ok: true, flags: { apiKey, aexUrl, debug: debug2, json, rest } };
|
|
3288
3914
|
}
|
|
3289
3915
|
async function resolveCommonHostFlags(io2, argv) {
|
|
3290
3916
|
const extracted = extractCommonHostFlags(argv);
|
|
3291
3917
|
if (!extracted.ok)
|
|
3292
3918
|
return extracted;
|
|
3293
|
-
const { apiKey: flagToken, aexUrl: flagUrl, debug: debug2, rest } = extracted.flags;
|
|
3919
|
+
const { apiKey: flagToken, aexUrl: flagUrl, debug: debug2, json, rest } = extracted.flags;
|
|
3294
3920
|
let token = flagToken;
|
|
3295
3921
|
let url = flagUrl;
|
|
3296
3922
|
let source = flagToken ? "flag" : "none";
|
|
@@ -3314,7 +3940,7 @@ async function resolveCommonHostFlags(io2, argv) {
|
|
|
3314
3940
|
if (!token) {
|
|
3315
3941
|
return { ok: false, reason: "no API key \u2014 pass --api-key or run `aex login`" };
|
|
3316
3942
|
}
|
|
3317
|
-
return { ok: true, flags: { apiKey: token, aexUrl: resolvedUrl, debug: debug2 }, rest };
|
|
3943
|
+
return { ok: true, flags: { apiKey: token, aexUrl: resolvedUrl, debug: debug2, json }, rest };
|
|
3318
3944
|
}
|
|
3319
3945
|
function describeApiError(err2) {
|
|
3320
3946
|
if (err2 instanceof AexApiError) {
|
|
@@ -3387,44 +4013,6 @@ function remedyForNetworkCode(code) {
|
|
|
3387
4013
|
return void 0;
|
|
3388
4014
|
}
|
|
3389
4015
|
}
|
|
3390
|
-
function suggest(input, candidates) {
|
|
3391
|
-
const needle = input.trim();
|
|
3392
|
-
if (!needle)
|
|
3393
|
-
return void 0;
|
|
3394
|
-
const lower = needle.toLowerCase();
|
|
3395
|
-
const prefixHits = candidates.filter((c) => c.toLowerCase().startsWith(lower));
|
|
3396
|
-
if (prefixHits.length === 1)
|
|
3397
|
-
return prefixHits[0];
|
|
3398
|
-
let best;
|
|
3399
|
-
let bestDist = Number.POSITIVE_INFINITY;
|
|
3400
|
-
for (const candidate of candidates) {
|
|
3401
|
-
const dist = levenshtein(needle, candidate);
|
|
3402
|
-
if (dist < bestDist) {
|
|
3403
|
-
bestDist = dist;
|
|
3404
|
-
best = candidate;
|
|
3405
|
-
}
|
|
3406
|
-
}
|
|
3407
|
-
return bestDist <= 2 ? best : void 0;
|
|
3408
|
-
}
|
|
3409
|
-
function levenshtein(a, b) {
|
|
3410
|
-
const m = a.length;
|
|
3411
|
-
const n = b.length;
|
|
3412
|
-
if (m === 0)
|
|
3413
|
-
return n;
|
|
3414
|
-
if (n === 0)
|
|
3415
|
-
return m;
|
|
3416
|
-
let prev = Array.from({ length: n + 1 }, (_, j) => j);
|
|
3417
|
-
let curr = new Array(n + 1);
|
|
3418
|
-
for (let i2 = 1; i2 <= m; i2++) {
|
|
3419
|
-
curr[0] = i2;
|
|
3420
|
-
for (let j = 1; j <= n; j++) {
|
|
3421
|
-
const cost = a[i2 - 1] === b[j - 1] ? 0 : 1;
|
|
3422
|
-
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
3423
|
-
}
|
|
3424
|
-
[prev, curr] = [curr, prev];
|
|
3425
|
-
}
|
|
3426
|
-
return prev[n];
|
|
3427
|
-
}
|
|
3428
4016
|
function makeHttpClient(io2, flags) {
|
|
3429
4017
|
return new HttpClient({
|
|
3430
4018
|
baseUrl: flags.aexUrl,
|
|
@@ -3624,8 +4212,806 @@ async function runOutputsSyncCmd(io2, dirs) {
|
|
|
3624
4212
|
return SUCCESS;
|
|
3625
4213
|
}
|
|
3626
4214
|
|
|
4215
|
+
// dist/host/registry.js
|
|
4216
|
+
var COMMON_HOST_FLAGS = ["--api-key", "--aex-url", "--json", "--debug"];
|
|
4217
|
+
var RUN_FLAGS = [
|
|
4218
|
+
"--provider",
|
|
4219
|
+
"--model",
|
|
4220
|
+
"--system",
|
|
4221
|
+
"--prompt",
|
|
4222
|
+
"--config",
|
|
4223
|
+
"--skill",
|
|
4224
|
+
"--tool",
|
|
4225
|
+
"--agents-md",
|
|
4226
|
+
"--file",
|
|
4227
|
+
"--mcp",
|
|
4228
|
+
"--mcp-auth",
|
|
4229
|
+
"--metadata",
|
|
4230
|
+
"--runtime-size",
|
|
4231
|
+
"--run-timeout",
|
|
4232
|
+
"--idempotency-key",
|
|
4233
|
+
"--webhook",
|
|
4234
|
+
"--follow",
|
|
4235
|
+
"--timeout"
|
|
4236
|
+
];
|
|
4237
|
+
var OUTPUTS_SUBVERBS = ["read", "download", "link", "find", "search"];
|
|
4238
|
+
var CLI_VERBS = [
|
|
4239
|
+
{
|
|
4240
|
+
name: "run",
|
|
4241
|
+
summary: "One-shot: open a session, send the prompt as the first turn (delegates to the SDK).",
|
|
4242
|
+
usage: [
|
|
4243
|
+
"aex run --model M --prompt P [--system S] [--provider name] --<provider>-api-key K",
|
|
4244
|
+
"aex run --config <run.json> --<provider>-api-key K",
|
|
4245
|
+
" --skill @file Attach a workspace skill bundle (repeatable)",
|
|
4246
|
+
" --tool @file.js Attach a custom tool module (repeatable)",
|
|
4247
|
+
" --agents-md @file Attach an AGENTS.md brief (repeatable)",
|
|
4248
|
+
" --file @path Mount a file into /workspace (repeatable)",
|
|
4249
|
+
" --mcp name=url MCP server (repeatable); --mcp-auth name=Hdr:Val for headers",
|
|
4250
|
+
" --metadata key=value Submission metadata (repeatable)",
|
|
4251
|
+
" --runtime-size <s> Managed runtime preset",
|
|
4252
|
+
" --run-timeout <dur> Server-side run deadline (validated client-side by the SDK)",
|
|
4253
|
+
" --webhook <url> Per-run terminal callback (https)",
|
|
4254
|
+
" --follow Stream the turn's events until the session parks"
|
|
4255
|
+
],
|
|
4256
|
+
flags: RUN_FLAGS
|
|
4257
|
+
},
|
|
4258
|
+
{
|
|
4259
|
+
name: "status",
|
|
4260
|
+
summary: "Print a session/run record (GET /api/sessions/:id).",
|
|
4261
|
+
usage: ["aex status <session-id>"]
|
|
4262
|
+
},
|
|
4263
|
+
{
|
|
4264
|
+
name: "deliveries",
|
|
4265
|
+
summary: "List a run's webhook delivery attempts.",
|
|
4266
|
+
usage: ["aex deliveries <session-id>"]
|
|
4267
|
+
},
|
|
4268
|
+
{
|
|
4269
|
+
name: "wait",
|
|
4270
|
+
summary: "Poll a session until it parks; exit code reflects the outcome.",
|
|
4271
|
+
usage: ["aex wait <session-id> [--timeout 8m] [--interval 2s]"],
|
|
4272
|
+
flags: ["--timeout", "--interval"]
|
|
4273
|
+
},
|
|
4274
|
+
{
|
|
4275
|
+
name: "events",
|
|
4276
|
+
summary: "List (or --follow) a session's events as NDJSON.",
|
|
4277
|
+
usage: ["aex events <session-id> [--follow] [--timeout 8m]"],
|
|
4278
|
+
flags: ["--follow", "--timeout"]
|
|
4279
|
+
},
|
|
4280
|
+
{
|
|
4281
|
+
name: "tail",
|
|
4282
|
+
summary: "Live human-readable follow over the coordinator event stream.",
|
|
4283
|
+
usage: ["aex tail <session-id> [--filter <type|source>] [--logs] [--from <seq>] [--settle] [--timeout <dur>]"],
|
|
4284
|
+
flags: ["--filter", "--logs", "--from", "--settle", "--timeout"]
|
|
4285
|
+
},
|
|
4286
|
+
{
|
|
4287
|
+
name: "inspect",
|
|
4288
|
+
summary: "One-shot full-timeline render + jump-to-failure summary.",
|
|
4289
|
+
usage: ["aex inspect <session-id> [--filter <type|source>] [--logs] [--timeout <dur>]"],
|
|
4290
|
+
flags: ["--filter", "--logs", "--timeout"]
|
|
4291
|
+
},
|
|
4292
|
+
{
|
|
4293
|
+
name: "outputs",
|
|
4294
|
+
summary: "List a session's captured outputs, or read/download/link/find/search one file.",
|
|
4295
|
+
usage: [
|
|
4296
|
+
"aex outputs <session-id> List captured outputs (NDJSON)",
|
|
4297
|
+
"aex outputs read <session-id> <path> Read one file as capped text",
|
|
4298
|
+
"aex outputs download <session-id> <path> [--out] Download one file's raw bytes",
|
|
4299
|
+
"aex outputs link <session-id> <path> Mint a temporary download URL",
|
|
4300
|
+
"aex outputs find <session-id> [--name S] [--ext E] [--type T]",
|
|
4301
|
+
"aex outputs search [--query S] [--name S] [--ext E] [--run-id ID] Cross-run"
|
|
4302
|
+
],
|
|
4303
|
+
flags: ["--out", "--name", "--ext", "--type", "--content-type", "--query", "--run-id", "--limit", "--max-bytes"],
|
|
4304
|
+
subverbs: OUTPUTS_SUBVERBS
|
|
4305
|
+
},
|
|
4306
|
+
{
|
|
4307
|
+
name: "download",
|
|
4308
|
+
summary: "Download a session's content as a zip (whole or one namespace).",
|
|
4309
|
+
usage: ["aex download <session-id> [--only outputs|events|metadata] [--out path]"],
|
|
4310
|
+
flags: ["--only", "--out"]
|
|
4311
|
+
},
|
|
4312
|
+
{
|
|
4313
|
+
name: "cancel",
|
|
4314
|
+
summary: "Cancel a running session.",
|
|
4315
|
+
usage: ["aex cancel <session-id>"]
|
|
4316
|
+
},
|
|
4317
|
+
{
|
|
4318
|
+
name: "delete",
|
|
4319
|
+
summary: "Delete a session.",
|
|
4320
|
+
usage: ["aex delete <session-id>"]
|
|
4321
|
+
},
|
|
4322
|
+
{
|
|
4323
|
+
name: "delete-asset",
|
|
4324
|
+
summary: "Delete a workspace asset blob by hash.",
|
|
4325
|
+
usage: ["aex delete-asset <assetId|hash>"]
|
|
4326
|
+
},
|
|
4327
|
+
{
|
|
4328
|
+
name: "runs",
|
|
4329
|
+
summary: "List the workspace's runs (newest first).",
|
|
4330
|
+
usage: ["aex runs [--limit N] [--since ISO]"],
|
|
4331
|
+
flags: ["--limit", "--since"]
|
|
4332
|
+
},
|
|
4333
|
+
{
|
|
4334
|
+
name: "sessions",
|
|
4335
|
+
summary: "List the workspace's sessions (newest first).",
|
|
4336
|
+
usage: ["aex sessions [--limit N]"],
|
|
4337
|
+
flags: ["--limit"]
|
|
4338
|
+
},
|
|
4339
|
+
{
|
|
4340
|
+
name: "whoami",
|
|
4341
|
+
summary: "Resolve the API key to its workspace + scopes.",
|
|
4342
|
+
usage: ["aex whoami [--json]"]
|
|
4343
|
+
},
|
|
4344
|
+
{
|
|
4345
|
+
name: "billing",
|
|
4346
|
+
summary: "Show balance / spend / cap; ledger, upgrade, and portal sub-verbs.",
|
|
4347
|
+
usage: [
|
|
4348
|
+
"aex billing [--json]",
|
|
4349
|
+
"aex billing ledger [--limit N]",
|
|
4350
|
+
"aex billing upgrade pro|team",
|
|
4351
|
+
"aex billing portal"
|
|
4352
|
+
],
|
|
4353
|
+
subverbs: ["ledger", "upgrade", "portal"]
|
|
4354
|
+
},
|
|
4355
|
+
{
|
|
4356
|
+
name: "webhooks",
|
|
4357
|
+
summary: "Reveal the workspace webhook signing secret.",
|
|
4358
|
+
usage: ["aex webhooks secret"],
|
|
4359
|
+
subverbs: ["secret"]
|
|
4360
|
+
},
|
|
4361
|
+
{
|
|
4362
|
+
name: "login",
|
|
4363
|
+
summary: "Persist the API key + url so other verbs need no --api-key.",
|
|
4364
|
+
usage: ["aex login --api-key T [--aex-url U]"]
|
|
4365
|
+
},
|
|
4366
|
+
{
|
|
4367
|
+
name: "logout",
|
|
4368
|
+
summary: "Clear the stored token.",
|
|
4369
|
+
usage: ["aex logout"]
|
|
4370
|
+
},
|
|
4371
|
+
{
|
|
4372
|
+
name: "auth",
|
|
4373
|
+
summary: "Show the resolved config (token never printed).",
|
|
4374
|
+
usage: ["aex auth status"],
|
|
4375
|
+
subverbs: ["status"]
|
|
4376
|
+
},
|
|
4377
|
+
{
|
|
4378
|
+
name: "models",
|
|
4379
|
+
summary: "List models + default provider (no token needed).",
|
|
4380
|
+
usage: ["aex models list [--json]"]
|
|
4381
|
+
},
|
|
4382
|
+
{
|
|
4383
|
+
name: "providers",
|
|
4384
|
+
summary: "List providers + their models (no token needed).",
|
|
4385
|
+
usage: ["aex providers list [--json]"]
|
|
4386
|
+
},
|
|
4387
|
+
{
|
|
4388
|
+
name: "tools",
|
|
4389
|
+
summary: "List builtin tools (no token needed).",
|
|
4390
|
+
usage: ["aex tools list [--json]"]
|
|
4391
|
+
},
|
|
4392
|
+
{
|
|
4393
|
+
name: "runtime-sizes",
|
|
4394
|
+
summary: "List managed runtime presets (no token needed).",
|
|
4395
|
+
usage: ["aex runtime-sizes list [--json]"]
|
|
4396
|
+
},
|
|
4397
|
+
{
|
|
4398
|
+
name: "debug",
|
|
4399
|
+
summary: "Operator: read the AWS plane directly (AWS creds, not --api-key).",
|
|
4400
|
+
usage: ["aex debug <run-id> [--plane dev|prd] [--region eu-west-2] [--cloudwatch] [--with-outputs]"]
|
|
4401
|
+
}
|
|
4402
|
+
];
|
|
4403
|
+
var VERB_BY_NAME = new Map(CLI_VERBS.map((v) => [v.name, v]));
|
|
4404
|
+
var CLI_VERB_NAMES = CLI_VERBS.map((v) => v.name);
|
|
4405
|
+
function findVerbSpec(name) {
|
|
4406
|
+
return VERB_BY_NAME.get(name);
|
|
4407
|
+
}
|
|
4408
|
+
function wantsVerbHelp(argv) {
|
|
4409
|
+
return argv.includes("--help") || argv.includes("-h");
|
|
4410
|
+
}
|
|
4411
|
+
function renderVerbHelp(spec) {
|
|
4412
|
+
const lines = [`aex ${spec.name} \u2014 ${spec.summary}`, "", "Usage:"];
|
|
4413
|
+
for (const u of spec.usage)
|
|
4414
|
+
lines.push(` ${u}`);
|
|
4415
|
+
lines.push("", `Common flags: ${COMMON_HOST_FLAGS.join(" ")}`);
|
|
4416
|
+
return lines.join("\n") + "\n";
|
|
4417
|
+
}
|
|
4418
|
+
|
|
3627
4419
|
// dist/host/run-cmd.js
|
|
3628
4420
|
import { resolve as resolvePath } from "node:path";
|
|
4421
|
+
|
|
4422
|
+
// dist/host/run-submit.js
|
|
4423
|
+
var TEXT = new TextEncoder();
|
|
4424
|
+
var ZIP_EPOCH = new Date(Date.UTC(1980, 0, 1));
|
|
4425
|
+
var UPLOAD_CONCURRENCY = 5;
|
|
4426
|
+
async function submitCliRun(http, fetchImpl, options) {
|
|
4427
|
+
const request = await buildSessionCreateRequest(http, fetchImpl, options);
|
|
4428
|
+
const submitted = await operations_exports.submit(http, request, { idempotencyKey: operations_exports.resolveIdempotencyKey(options.idempotencyKey) });
|
|
4429
|
+
return submitted.session;
|
|
4430
|
+
}
|
|
4431
|
+
async function buildCliSkill(content, source) {
|
|
4432
|
+
const front = extractSkillFrontmatter("Skill.fromContent", { "SKILL.md": content });
|
|
4433
|
+
const name = deriveSkillName("Skill.fromContent", front.name, void 0, void 0);
|
|
4434
|
+
const description = front.description;
|
|
4435
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
4436
|
+
throw new Error(`${source}: a skill description is required in SKILL.md frontmatter`);
|
|
4437
|
+
}
|
|
4438
|
+
if (description.length > 2048) {
|
|
4439
|
+
throw new Error(`${source}: description must be <= 2048 chars`);
|
|
4440
|
+
}
|
|
4441
|
+
const bytes = bundleSkillFiles({ "SKILL.md": content });
|
|
4442
|
+
return {
|
|
4443
|
+
name,
|
|
4444
|
+
description,
|
|
4445
|
+
contentHash: await hashBytes(bytes),
|
|
4446
|
+
bytes
|
|
4447
|
+
};
|
|
4448
|
+
}
|
|
4449
|
+
async function buildCliTool(args) {
|
|
4450
|
+
const inputSchema = { type: "object", properties: {}, additionalProperties: true };
|
|
4451
|
+
const manifest = normalizeToolManifest("Tool.fromFiles", {
|
|
4452
|
+
name: args.name,
|
|
4453
|
+
description: args.description,
|
|
4454
|
+
input_schema: inputSchema,
|
|
4455
|
+
entry: args.entry
|
|
4456
|
+
}, { [args.entry]: args.content });
|
|
4457
|
+
const bytes = bundleToolFiles({ [args.entry]: args.content }, manifest);
|
|
4458
|
+
return {
|
|
4459
|
+
ref: {
|
|
4460
|
+
kind: "asset",
|
|
4461
|
+
contentHash: await hashBytes(bytes),
|
|
4462
|
+
...manifest
|
|
4463
|
+
},
|
|
4464
|
+
bytes
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
async function buildCliAgentsMd(content, name) {
|
|
4468
|
+
if (typeof content !== "string" || content.length === 0) {
|
|
4469
|
+
throw new Error("AgentsMd.fromContent: content must be a non-empty string");
|
|
4470
|
+
}
|
|
4471
|
+
if (!/^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$/.test(name)) {
|
|
4472
|
+
throw new Error("AgentsMd.fromContent: name must be a lowercase workspace slug");
|
|
4473
|
+
}
|
|
4474
|
+
const bytes = zipSync({ "AGENTS.md": [TEXT.encode(content), { mtime: ZIP_EPOCH }] }, { level: 6 });
|
|
4475
|
+
return { name, contentHash: await hashBytes(bytes), bytes };
|
|
4476
|
+
}
|
|
4477
|
+
async function buildCliFile(args) {
|
|
4478
|
+
const filename = sanitiseFilename(args.name);
|
|
4479
|
+
if (filename === void 0) {
|
|
4480
|
+
throw new Error(`File.fromBytes: name ${JSON.stringify(args.name)} is not a valid filename`);
|
|
4481
|
+
}
|
|
4482
|
+
const bytes = TEXT.encode(args.content);
|
|
4483
|
+
if (bytes.byteLength === 0) {
|
|
4484
|
+
throw new Error("File.fromBytes: bytes must be a non-empty Uint8Array");
|
|
4485
|
+
}
|
|
4486
|
+
const zip = zipSync({ [filename]: [bytes, { mtime: ZIP_EPOCH }] }, { level: 6 });
|
|
4487
|
+
return {
|
|
4488
|
+
name: slugFromFilename(filename),
|
|
4489
|
+
contentHash: await hashBytes(zip),
|
|
4490
|
+
mountPath: DEFAULT_FILE_MOUNT_PATH,
|
|
4491
|
+
bytes: zip
|
|
4492
|
+
};
|
|
4493
|
+
}
|
|
4494
|
+
function toCliSessionEnvironment(env) {
|
|
4495
|
+
if (!env)
|
|
4496
|
+
return void 0;
|
|
4497
|
+
const out = {
|
|
4498
|
+
...env.networking ? { networking: env.networking } : {},
|
|
4499
|
+
...env.packages ? { packages: env.packages } : {},
|
|
4500
|
+
...env.envVars ? { variables: env.envVars } : {}
|
|
4501
|
+
};
|
|
4502
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
4503
|
+
}
|
|
4504
|
+
async function buildSessionCreateRequest(http, fetchImpl, options) {
|
|
4505
|
+
const input = normaliseSessionInput(options.message);
|
|
4506
|
+
const provider = resolveModelProvider(options.model, options.provider);
|
|
4507
|
+
validateApiKeys(options.apiKeys, provider);
|
|
4508
|
+
try {
|
|
4509
|
+
parseRunTimeout(options.overrides?.timeout);
|
|
4510
|
+
if (options.webhook !== void 0)
|
|
4511
|
+
parseRunWebhook(options.webhook);
|
|
4512
|
+
} catch (err2) {
|
|
4513
|
+
throw new Error(err2 instanceof Error ? err2.message : String(err2));
|
|
4514
|
+
}
|
|
4515
|
+
const limitsInput = {};
|
|
4516
|
+
if (options.overrides?.maxSpendUsd !== void 0)
|
|
4517
|
+
limitsInput.maxSpendUsd = options.overrides.maxSpendUsd;
|
|
4518
|
+
if (options.overrides?.maxTurns !== void 0)
|
|
4519
|
+
limitsInput.maxTurns = options.overrides.maxTurns;
|
|
4520
|
+
const limits = parseRunLimits(Object.keys(limitsInput).length > 0 ? limitsInput : void 0);
|
|
4521
|
+
const [preparedTools, preparedSkills, preparedAgentsMd, preparedFiles] = await Promise.all([
|
|
4522
|
+
prepareTools(http, fetchImpl, options.tools ?? []),
|
|
4523
|
+
prepareSkills(http, fetchImpl, options.skills ?? []),
|
|
4524
|
+
prepareAgentsMd(http, fetchImpl, options.agentsMd ?? []),
|
|
4525
|
+
prepareFiles(http, fetchImpl, options.files ?? [])
|
|
4526
|
+
]);
|
|
4527
|
+
const { submissionMcpServers, mergedMcpSecrets } = mergeMcpServers(options.mcpServers ?? []);
|
|
4528
|
+
const environment = sessionEnvironmentForWire(options.environment);
|
|
4529
|
+
const submission = {
|
|
4530
|
+
model: options.model,
|
|
4531
|
+
...options.system ? { system: options.system } : {},
|
|
4532
|
+
tools: [...preparedTools.builtinNames, ...preparedTools.refs],
|
|
4533
|
+
...preparedSkills.length > 0 ? { skills: preparedSkills } : {},
|
|
4534
|
+
agentsMd: preparedAgentsMd,
|
|
4535
|
+
files: preparedFiles,
|
|
4536
|
+
mcpServers: submissionMcpServers,
|
|
4537
|
+
...environment ? { environment } : {},
|
|
4538
|
+
...options.metadata ? { metadata: options.metadata } : {}
|
|
4539
|
+
};
|
|
4540
|
+
const secrets = {
|
|
4541
|
+
...options.apiKeys ? { apiKeys: options.apiKeys } : {},
|
|
4542
|
+
...mergedMcpSecrets.length > 0 ? { mcpServers: mergedMcpSecrets } : {}
|
|
4543
|
+
};
|
|
4544
|
+
return {
|
|
4545
|
+
provider,
|
|
4546
|
+
submission,
|
|
4547
|
+
input,
|
|
4548
|
+
...options.runtime ? { runtimeSize: options.runtime } : {},
|
|
4549
|
+
...options.overrides?.timeout ? { timeout: options.overrides.timeout } : {},
|
|
4550
|
+
...limits ? { limits } : {},
|
|
4551
|
+
retention: { idleTtl: options.overrides?.idleTtl ?? "3m" },
|
|
4552
|
+
...options.webhook ? { webhook: options.webhook } : {},
|
|
4553
|
+
secrets
|
|
4554
|
+
};
|
|
4555
|
+
}
|
|
4556
|
+
function normaliseSessionInput(input) {
|
|
4557
|
+
if (typeof input === "string") {
|
|
4558
|
+
if (!input)
|
|
4559
|
+
throw new Error("Aex.submit: message must be a non-empty string");
|
|
4560
|
+
return input;
|
|
4561
|
+
}
|
|
4562
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
4563
|
+
throw new Error("Aex.submit: message must be a non-empty string or string array");
|
|
4564
|
+
}
|
|
4565
|
+
for (const segment of input) {
|
|
4566
|
+
if (typeof segment !== "string" || !segment) {
|
|
4567
|
+
throw new Error("Aex.submit: message segments must be non-empty strings");
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
return [...input];
|
|
4571
|
+
}
|
|
4572
|
+
function validateApiKeys(apiKeys, provider) {
|
|
4573
|
+
const key = apiKeys?.[provider];
|
|
4574
|
+
if (typeof key !== "string" || key.length === 0) {
|
|
4575
|
+
throw new Error(`Aex.submit: a provider API key is required for provider ${provider}; pass apiKeys.${provider}`);
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4578
|
+
async function prepareTools(http, fetchImpl, tools) {
|
|
4579
|
+
const prepared = await mapWithConcurrency(tools, UPLOAD_CONCURRENCY, async (entry, i2) => {
|
|
4580
|
+
if (typeof entry === "string") {
|
|
4581
|
+
if (!BUILTIN_TOOL_NAMES.includes(entry)) {
|
|
4582
|
+
throw new Error(`aex: tools[${i2}] (${JSON.stringify(entry)}) is not a builtin tool name`);
|
|
4583
|
+
}
|
|
4584
|
+
return { kind: "builtin", name: entry };
|
|
4585
|
+
}
|
|
4586
|
+
const uploaded = await uploadAsset(http, fetchImpl, {
|
|
4587
|
+
bytes: entry.bytes,
|
|
4588
|
+
hash: entry.ref.contentHash,
|
|
4589
|
+
contentType: "application/zip"
|
|
4590
|
+
});
|
|
4591
|
+
const { contentHash: _contentHash, ...ref } = entry.ref;
|
|
4592
|
+
void _contentHash;
|
|
4593
|
+
return { kind: "ref", ref: { ...ref, assetId: uploaded.assetId } };
|
|
4594
|
+
});
|
|
4595
|
+
const refs = [];
|
|
4596
|
+
const seenBuiltins = /* @__PURE__ */ new Set();
|
|
4597
|
+
const builtinNames = [];
|
|
4598
|
+
for (const item of prepared) {
|
|
4599
|
+
if (item.kind === "builtin") {
|
|
4600
|
+
if (!seenBuiltins.has(item.name)) {
|
|
4601
|
+
seenBuiltins.add(item.name);
|
|
4602
|
+
builtinNames.push(item.name);
|
|
4603
|
+
}
|
|
4604
|
+
} else {
|
|
4605
|
+
refs.push(item.ref);
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
return { refs, builtinNames };
|
|
4609
|
+
}
|
|
4610
|
+
async function prepareSkills(http, fetchImpl, skills) {
|
|
4611
|
+
if (skills.length > SKILLS_MAX) {
|
|
4612
|
+
throw new Error(`aex: skills exceeds the ${SKILLS_MAX}-skill limit (got ${skills.length})`);
|
|
4613
|
+
}
|
|
4614
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4615
|
+
for (const skill of skills) {
|
|
4616
|
+
if (seen.has(skill.name))
|
|
4617
|
+
throw new Error(`aex: skills duplicate name: ${skill.name}`);
|
|
4618
|
+
seen.add(skill.name);
|
|
4619
|
+
}
|
|
4620
|
+
return mapWithConcurrency(skills, UPLOAD_CONCURRENCY, async (skill) => {
|
|
4621
|
+
await uploadAsset(http, fetchImpl, { bytes: skill.bytes, hash: skill.contentHash, contentType: "application/zip" });
|
|
4622
|
+
await operations_exports.upsertSkill(http, {
|
|
4623
|
+
name: skill.name,
|
|
4624
|
+
contentHash: skill.contentHash,
|
|
4625
|
+
description: skill.description,
|
|
4626
|
+
sizeBytes: skill.bytes.byteLength
|
|
4627
|
+
});
|
|
4628
|
+
return { kind: "skill", name: skill.name };
|
|
4629
|
+
});
|
|
4630
|
+
}
|
|
4631
|
+
async function prepareAgentsMd(http, fetchImpl, agentsMds) {
|
|
4632
|
+
return mapWithConcurrency(agentsMds, UPLOAD_CONCURRENCY, async (entry) => {
|
|
4633
|
+
const uploaded = await uploadAsset(http, fetchImpl, {
|
|
4634
|
+
bytes: entry.bytes,
|
|
4635
|
+
hash: entry.contentHash,
|
|
4636
|
+
contentType: "application/zip"
|
|
4637
|
+
});
|
|
4638
|
+
return { kind: "asset", assetId: uploaded.assetId, name: entry.name };
|
|
4639
|
+
});
|
|
4640
|
+
}
|
|
4641
|
+
async function prepareFiles(http, fetchImpl, files) {
|
|
4642
|
+
return mapWithConcurrency(files, UPLOAD_CONCURRENCY, async (entry) => {
|
|
4643
|
+
const uploaded = await uploadAsset(http, fetchImpl, {
|
|
4644
|
+
bytes: entry.bytes,
|
|
4645
|
+
hash: entry.contentHash,
|
|
4646
|
+
contentType: "application/zip"
|
|
4647
|
+
});
|
|
4648
|
+
return { kind: "asset", assetId: uploaded.assetId, name: entry.name, mountPath: entry.mountPath };
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4651
|
+
function mergeMcpServers(inputs) {
|
|
4652
|
+
const submissionMcpServers = [];
|
|
4653
|
+
const mergedMcpSecrets = [];
|
|
4654
|
+
for (const entry of inputs) {
|
|
4655
|
+
submissionMcpServers.push({ name: entry.name, url: entry.url });
|
|
4656
|
+
if (entry.headers && Object.keys(entry.headers).length > 0) {
|
|
4657
|
+
mergedMcpSecrets.push({ name: entry.name, url: entry.url, headers: { ...entry.headers } });
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
return { submissionMcpServers, mergedMcpSecrets };
|
|
4661
|
+
}
|
|
4662
|
+
function sessionEnvironmentForWire(environment) {
|
|
4663
|
+
if (environment === void 0)
|
|
4664
|
+
return void 0;
|
|
4665
|
+
const { variables, ...rest } = environment;
|
|
4666
|
+
const out = {
|
|
4667
|
+
...rest,
|
|
4668
|
+
...variables !== void 0 ? { envVars: variables } : {}
|
|
4669
|
+
};
|
|
4670
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
4671
|
+
}
|
|
4672
|
+
async function uploadAsset(http, fetchImpl, args) {
|
|
4673
|
+
const expected = args.hash.startsWith("sha256:") ? args.hash.slice("sha256:".length) : args.hash;
|
|
4674
|
+
const actual = await sha256Hex(args.bytes);
|
|
4675
|
+
if (actual !== expected) {
|
|
4676
|
+
throw new Error(`uploadAsset: client-side hash mismatch: computed sha256:${actual} but caller declared ${args.hash}`);
|
|
4677
|
+
}
|
|
4678
|
+
const contentHash = `sha256:${actual}`;
|
|
4679
|
+
const presign = await http.request("/assets/presign", {
|
|
4680
|
+
method: "POST",
|
|
4681
|
+
headers: { "content-type": "application/json" },
|
|
4682
|
+
body: JSON.stringify({ hash: contentHash, sizeBytes: args.bytes.byteLength })
|
|
4683
|
+
});
|
|
4684
|
+
if (presign.exists) {
|
|
4685
|
+
const storedHash2 = presign.contentHash ?? contentHash;
|
|
4686
|
+
return {
|
|
4687
|
+
assetId: presign.assetId ?? assetIdFromContentHash(storedHash2),
|
|
4688
|
+
contentHash: storedHash2,
|
|
4689
|
+
sizeBytes: presign.sizeBytes ?? args.bytes.byteLength,
|
|
4690
|
+
exists: true
|
|
4691
|
+
};
|
|
4692
|
+
}
|
|
4693
|
+
if (!presign.uploadUrl) {
|
|
4694
|
+
throw new Error("uploadAsset: presign returned no uploadUrl and exists:false");
|
|
4695
|
+
}
|
|
4696
|
+
const doFetch = fetchImpl ?? globalThis.fetch;
|
|
4697
|
+
const put = await doFetch(presign.uploadUrl, {
|
|
4698
|
+
method: "PUT",
|
|
4699
|
+
headers: {
|
|
4700
|
+
"content-type": args.contentType ?? "application/zip",
|
|
4701
|
+
...presign.requiredHeaders ?? {}
|
|
4702
|
+
},
|
|
4703
|
+
body: args.bytes
|
|
4704
|
+
});
|
|
4705
|
+
if (!put.ok) {
|
|
4706
|
+
throw new Error(`uploadAsset: direct upload PUT failed with status ${put.status}`);
|
|
4707
|
+
}
|
|
4708
|
+
const fin = await http.request("/assets/finalize", {
|
|
4709
|
+
method: "POST",
|
|
4710
|
+
headers: { "content-type": "application/json" },
|
|
4711
|
+
body: JSON.stringify({ hash: contentHash, sizeBytes: args.bytes.byteLength })
|
|
4712
|
+
});
|
|
4713
|
+
const storedHash = fin.contentHash ?? presign.contentHash ?? contentHash;
|
|
4714
|
+
return {
|
|
4715
|
+
assetId: fin.assetId ?? presign.assetId ?? assetIdFromContentHash(storedHash),
|
|
4716
|
+
contentHash: storedHash,
|
|
4717
|
+
sizeBytes: fin.sizeBytes ?? args.bytes.byteLength,
|
|
4718
|
+
exists: false
|
|
4719
|
+
};
|
|
4720
|
+
}
|
|
4721
|
+
function bundleSkillFiles(files) {
|
|
4722
|
+
const collected = collectBundleFiles("Skill bundle", files, true);
|
|
4723
|
+
return zipCollected(collected);
|
|
4724
|
+
}
|
|
4725
|
+
function bundleToolFiles(files, manifest) {
|
|
4726
|
+
const collected = collectBundleFiles("Tool bundle", files, false);
|
|
4727
|
+
const entryPath = validateSkillBundleEntry({ path: manifest.entry, size: 0 }).path;
|
|
4728
|
+
if (!collected.has(entryPath)) {
|
|
4729
|
+
throw new Error(`Tool bundle entry "${entryPath}" must exist in files`);
|
|
4730
|
+
}
|
|
4731
|
+
if (collected.has("tool.json")) {
|
|
4732
|
+
throw new Error('Tool bundle files must not include reserved "tool.json"; pass manifest fields instead');
|
|
4733
|
+
}
|
|
4734
|
+
collected.set("tool.json", TEXT.encode(`${JSON.stringify(manifest, null, 2)}
|
|
4735
|
+
`));
|
|
4736
|
+
return zipCollected(collected);
|
|
4737
|
+
}
|
|
4738
|
+
function collectBundleFiles(kind, files, requireSkillMd) {
|
|
4739
|
+
const entries = Object.entries(files);
|
|
4740
|
+
if (entries.length === 0)
|
|
4741
|
+
throw new Error(`${kind} files map cannot be empty`);
|
|
4742
|
+
if (entries.length > SKILL_BUNDLE_LIMITS.maxFiles) {
|
|
4743
|
+
throw new Error(`${kind} exceeds ${SKILL_BUNDLE_LIMITS.maxFiles} file limit (got ${entries.length})`);
|
|
4744
|
+
}
|
|
4745
|
+
const collected = /* @__PURE__ */ new Map();
|
|
4746
|
+
let hasSkillMd = false;
|
|
4747
|
+
let total = 0;
|
|
4748
|
+
for (const [rawPath, contents] of entries) {
|
|
4749
|
+
const bytes = typeof contents === "string" ? TEXT.encode(contents) : contents;
|
|
4750
|
+
if (!(bytes instanceof Uint8Array))
|
|
4751
|
+
throw new Error(`${kind} file "${rawPath}" must be a string or Uint8Array`);
|
|
4752
|
+
const entry = validateSkillBundleEntry({ path: rawPath, size: bytes.byteLength });
|
|
4753
|
+
if (entry.path === "SKILL.md")
|
|
4754
|
+
hasSkillMd = true;
|
|
4755
|
+
total += bytes.byteLength;
|
|
4756
|
+
if (total > SKILL_BUNDLE_LIMITS.maxDecompressedBytes) {
|
|
4757
|
+
throw new Error(`${kind} exceeds decompressed cap of ${SKILL_BUNDLE_LIMITS.maxDecompressedBytes} bytes`);
|
|
4758
|
+
}
|
|
4759
|
+
if (collected.has(entry.path))
|
|
4760
|
+
throw new Error(`${kind} contains duplicate path: ${entry.path}`);
|
|
4761
|
+
collected.set(entry.path, bytes);
|
|
4762
|
+
}
|
|
4763
|
+
if (requireSkillMd && !hasSkillMd) {
|
|
4764
|
+
throw new Error('Skill bundle must contain a "SKILL.md" file at the root.');
|
|
4765
|
+
}
|
|
4766
|
+
return collected;
|
|
4767
|
+
}
|
|
4768
|
+
function zipCollected(collected) {
|
|
4769
|
+
const sorted = [...collected.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
|
|
4770
|
+
const zippable = {};
|
|
4771
|
+
for (const [path, bytes] of sorted) {
|
|
4772
|
+
zippable[path] = [bytes, { mtime: ZIP_EPOCH }];
|
|
4773
|
+
}
|
|
4774
|
+
const zip = zipSync(zippable, { level: 6 });
|
|
4775
|
+
if (zip.byteLength > SKILL_BUNDLE_LIMITS.maxCompressedBytes) {
|
|
4776
|
+
throw new Error(`bundle exceeds compressed cap of ${SKILL_BUNDLE_LIMITS.maxCompressedBytes} bytes (got ${zip.byteLength})`);
|
|
4777
|
+
}
|
|
4778
|
+
return zip;
|
|
4779
|
+
}
|
|
4780
|
+
function normalizeToolManifest(source, input, files) {
|
|
4781
|
+
if (typeof input.name !== "string" || !TOOL_NAME_PATTERN.test(input.name)) {
|
|
4782
|
+
throw new Error(`${source}: name must match ${TOOL_NAME_PATTERN.source}`);
|
|
4783
|
+
}
|
|
4784
|
+
if (input.name.includes("__")) {
|
|
4785
|
+
throw new Error(`${source}: name must not contain "__"; that separator is reserved for MCP tools`);
|
|
4786
|
+
}
|
|
4787
|
+
if (typeof input.description !== "string" || input.description.trim().length === 0 || input.description.length > 2048) {
|
|
4788
|
+
throw new Error(`${source}: description must be non-empty and <= 2048 chars`);
|
|
4789
|
+
}
|
|
4790
|
+
const inputSchema = input.input_schema;
|
|
4791
|
+
if (!inputSchema || typeof inputSchema !== "object" || Array.isArray(inputSchema)) {
|
|
4792
|
+
throw new Error(`${source}: inputSchema must be a JSON Schema object`);
|
|
4793
|
+
}
|
|
4794
|
+
if (inputSchema.type !== "object") {
|
|
4795
|
+
throw new Error(`${source}: inputSchema.type must be "object"`);
|
|
4796
|
+
}
|
|
4797
|
+
const entry = normaliseSkillBundlePath(input.entry);
|
|
4798
|
+
if (!/\.(?:js|mjs|cjs)$/i.test(entry.split("/").pop() ?? entry)) {
|
|
4799
|
+
throw new Error(`${source}: entry must be a JS module (.js/.mjs/.cjs)`);
|
|
4800
|
+
}
|
|
4801
|
+
if (!(entry in files) && !(input.entry in files)) {
|
|
4802
|
+
throw new Error(`${source}: entry ${JSON.stringify(input.entry)} is not present in files`);
|
|
4803
|
+
}
|
|
4804
|
+
return { ...input, entry };
|
|
4805
|
+
}
|
|
4806
|
+
function extractSkillFrontmatter(source, files) {
|
|
4807
|
+
const raw = files["SKILL.md"];
|
|
4808
|
+
if (raw === void 0)
|
|
4809
|
+
throw new Error(`${source}: the skill bundle must contain a SKILL.md at its root`);
|
|
4810
|
+
const text = typeof raw === "string" ? raw : new TextDecoder().decode(raw);
|
|
4811
|
+
return parseSkillFrontmatter(text);
|
|
4812
|
+
}
|
|
4813
|
+
function parseSkillFrontmatter(text) {
|
|
4814
|
+
const src = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
4815
|
+
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(src);
|
|
4816
|
+
if (!match)
|
|
4817
|
+
return {};
|
|
4818
|
+
const out = {};
|
|
4819
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
4820
|
+
const kv = /^([A-Za-z0-9_-]+)[ \t]*:[ \t]*(.*)$/.exec(line);
|
|
4821
|
+
if (!kv)
|
|
4822
|
+
continue;
|
|
4823
|
+
const key = kv[1].toLowerCase();
|
|
4824
|
+
if (key !== "name" && key !== "description")
|
|
4825
|
+
continue;
|
|
4826
|
+
let value = kv[2].trim();
|
|
4827
|
+
if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
|
|
4828
|
+
value = value.slice(1, -1);
|
|
4829
|
+
}
|
|
4830
|
+
if (value.length > 0)
|
|
4831
|
+
out[key] = value;
|
|
4832
|
+
}
|
|
4833
|
+
return out;
|
|
4834
|
+
}
|
|
4835
|
+
function deriveSkillName(source, frontmatterName, explicitName, dirBasename) {
|
|
4836
|
+
let name = explicitName ?? frontmatterName;
|
|
4837
|
+
if (name === void 0 && dirBasename !== void 0) {
|
|
4838
|
+
const slug = slugifyName(dirBasename);
|
|
4839
|
+
if (slug.length > 0)
|
|
4840
|
+
name = slug;
|
|
4841
|
+
}
|
|
4842
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
4843
|
+
throw new Error(`${source}: a skill name is required`);
|
|
4844
|
+
}
|
|
4845
|
+
if (!SKILL_NAME_PATTERN.test(name)) {
|
|
4846
|
+
throw new Error(`${source}: name ${JSON.stringify(name)} must match ${SKILL_NAME_PATTERN.source}`);
|
|
4847
|
+
}
|
|
4848
|
+
if (name.includes("__")) {
|
|
4849
|
+
throw new Error(`${source}: name must not contain "__"; that separator is reserved for MCP tools`);
|
|
4850
|
+
}
|
|
4851
|
+
if (SKILL_RESERVED_NAMES.has(name)) {
|
|
4852
|
+
throw new Error(`${source}: name ${JSON.stringify(name)} is reserved (${[...SKILL_RESERVED_NAMES].join(", ")})`);
|
|
4853
|
+
}
|
|
4854
|
+
return name;
|
|
4855
|
+
}
|
|
4856
|
+
function sanitiseFilename(name) {
|
|
4857
|
+
if (typeof name !== "string" || name.length === 0 || name.length > 255)
|
|
4858
|
+
return void 0;
|
|
4859
|
+
if (name.includes("/") || name.includes("\\") || name.includes("\0"))
|
|
4860
|
+
return void 0;
|
|
4861
|
+
if (name === "." || name === "..")
|
|
4862
|
+
return void 0;
|
|
4863
|
+
return name;
|
|
4864
|
+
}
|
|
4865
|
+
function slugFromFilename(filename) {
|
|
4866
|
+
const stem = filename.includes(".") ? filename.slice(0, filename.lastIndexOf(".")) : filename;
|
|
4867
|
+
const slug = slugifyName(stem);
|
|
4868
|
+
return slug.length > 0 ? slug : "file";
|
|
4869
|
+
}
|
|
4870
|
+
function slugifyName(input) {
|
|
4871
|
+
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
4872
|
+
}
|
|
4873
|
+
async function hashBytes(bytes) {
|
|
4874
|
+
return `sha256:${await sha256Hex(bytes)}`;
|
|
4875
|
+
}
|
|
4876
|
+
async function sha256Hex(bytes) {
|
|
4877
|
+
const subtle = globalThis.crypto?.subtle;
|
|
4878
|
+
if (!subtle) {
|
|
4879
|
+
throw new Error("sha256: globalThis.crypto.subtle is not available");
|
|
4880
|
+
}
|
|
4881
|
+
const view = new Uint8Array(bytes.byteLength);
|
|
4882
|
+
view.set(bytes);
|
|
4883
|
+
const digest = await subtle.digest("SHA-256", view.buffer);
|
|
4884
|
+
return bufferToHex(digest);
|
|
4885
|
+
}
|
|
4886
|
+
function bufferToHex(buffer) {
|
|
4887
|
+
const view = new Uint8Array(buffer);
|
|
4888
|
+
let out = "";
|
|
4889
|
+
for (const byte of view) {
|
|
4890
|
+
out += byte.toString(16).padStart(2, "0");
|
|
4891
|
+
}
|
|
4892
|
+
return out;
|
|
4893
|
+
}
|
|
4894
|
+
function assetIdFromContentHash(contentHash) {
|
|
4895
|
+
const hex = contentHash.startsWith("sha256:") ? contentHash.slice("sha256:".length) : contentHash;
|
|
4896
|
+
return `asset_${hex}`;
|
|
4897
|
+
}
|
|
4898
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
4899
|
+
const out = new Array(items.length);
|
|
4900
|
+
let next = 0;
|
|
4901
|
+
const lanes = Array.from({ length: Math.min(Math.max(1, limit), items.length) }, async () => {
|
|
4902
|
+
for (let i2 = next++; i2 < items.length; i2 = next++) {
|
|
4903
|
+
out[i2] = await fn(items[i2], i2);
|
|
4904
|
+
}
|
|
4905
|
+
});
|
|
4906
|
+
await Promise.all(lanes);
|
|
4907
|
+
return out;
|
|
4908
|
+
}
|
|
4909
|
+
|
|
4910
|
+
// dist/host/stream-render.js
|
|
4911
|
+
async function* openEnvelopeStream(io2, http, sessionId, options = {}) {
|
|
4912
|
+
const first = await operations_exports.getSessionCoordinatorTicket(http, sessionId);
|
|
4913
|
+
if (options.debug) {
|
|
4914
|
+
let host = "(invalid)";
|
|
4915
|
+
try {
|
|
4916
|
+
host = new URL(first.wsUrl).host;
|
|
4917
|
+
} catch {
|
|
4918
|
+
}
|
|
4919
|
+
options.debug(`ticket minted: host=${host} expiresInMs=${first.expiresAtMs - Date.now()} from=${options.from ?? 0}`);
|
|
4920
|
+
}
|
|
4921
|
+
yield* streamCoordinatorEvents({
|
|
4922
|
+
wsUrl: first.wsUrl,
|
|
4923
|
+
from: options.from ?? 0,
|
|
4924
|
+
fetchTicket: async () => (await operations_exports.getSessionCoordinatorTicket(http, sessionId)).ticket,
|
|
4925
|
+
...io2.webSocketFactory ? { webSocketFactory: io2.webSocketFactory } : {},
|
|
4926
|
+
...options.settleConsistent ? { isTerminal: isRunSettled } : {},
|
|
4927
|
+
...options.signal ? { signal: options.signal } : {}
|
|
4928
|
+
});
|
|
4929
|
+
}
|
|
4930
|
+
var PREVIEW_MAX = 120;
|
|
4931
|
+
function clip(text, max = PREVIEW_MAX) {
|
|
4932
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
4933
|
+
return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
|
|
4934
|
+
}
|
|
4935
|
+
function str2(v) {
|
|
4936
|
+
return typeof v === "string" ? v : v === void 0 || v === null ? "" : JSON.stringify(v);
|
|
4937
|
+
}
|
|
4938
|
+
function argsPreview(e) {
|
|
4939
|
+
const d = e.data;
|
|
4940
|
+
const candidate = d.input ?? d.args ?? d.arguments ?? d.params;
|
|
4941
|
+
if (candidate === void 0)
|
|
4942
|
+
return "";
|
|
4943
|
+
return clip(typeof candidate === "string" ? candidate : JSON.stringify(candidate));
|
|
4944
|
+
}
|
|
4945
|
+
function renderEnvelope(e, options = {}) {
|
|
4946
|
+
if (channelOf(e) === "log" && !options.logs)
|
|
4947
|
+
return null;
|
|
4948
|
+
switch (e.type) {
|
|
4949
|
+
case "RUN_STARTED":
|
|
4950
|
+
return "\u25B6 run started";
|
|
4951
|
+
case "TEXT_MESSAGE_CONTENT": {
|
|
4952
|
+
const delta = toAGUI(e).type === "TEXT_MESSAGE_CONTENT" ? toAGUI(e).delta : "";
|
|
4953
|
+
return delta ? delta : null;
|
|
4954
|
+
}
|
|
4955
|
+
case "TOOL_CALL_START": {
|
|
4956
|
+
const agui = toAGUI(e);
|
|
4957
|
+
const name = agui.type === "TOOL_CALL_START" ? agui.toolCallName : str2(e.data.name);
|
|
4958
|
+
const preview = argsPreview(e);
|
|
4959
|
+
return `\xB7 tool ${name || "(unnamed)"}(${preview})`;
|
|
4960
|
+
}
|
|
4961
|
+
case "TOOL_CALL_RESULT": {
|
|
4962
|
+
const name = str2(e.data.name) || "result";
|
|
4963
|
+
const content = e.data.content;
|
|
4964
|
+
const ok = content === void 0 || content === null ? "ok" : clip(str2(content));
|
|
4965
|
+
return ` \u2190 ${name} ${ok}`;
|
|
4966
|
+
}
|
|
4967
|
+
case "CUSTOM": {
|
|
4968
|
+
const label = e.message ?? str2(e.data.name) ?? "custom";
|
|
4969
|
+
if (e.data.name === "aex.session.idle") {
|
|
4970
|
+
const value = e.data.value;
|
|
4971
|
+
const reason = value && typeof value.reason === "string" ? value.reason : "";
|
|
4972
|
+
if (reason && reason !== "completed")
|
|
4973
|
+
return `[aex] ${label} (${reason})`;
|
|
4974
|
+
}
|
|
4975
|
+
return `[aex] ${label}`;
|
|
4976
|
+
}
|
|
4977
|
+
case "LOG": {
|
|
4978
|
+
const level = e.level ?? "info";
|
|
4979
|
+
return `[${level}] ${e.message ?? str2(e.data.message)}`;
|
|
4980
|
+
}
|
|
4981
|
+
case "RUN_FINISHED":
|
|
4982
|
+
return "\u2713 run finished";
|
|
4983
|
+
case "RUN_ERROR": {
|
|
4984
|
+
const agui = toAGUI(e);
|
|
4985
|
+
const message = agui.type === "RUN_ERROR" ? agui.message : e.message ?? "run error";
|
|
4986
|
+
return `\u2717 run error: ${message}`;
|
|
4987
|
+
}
|
|
4988
|
+
default:
|
|
4989
|
+
return null;
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
function parseFilters(tokens) {
|
|
4993
|
+
const flattened = tokens.flatMap((t) => t.split(",")).map((s) => s.trim()).filter(Boolean);
|
|
4994
|
+
if (flattened.length === 0)
|
|
4995
|
+
return {};
|
|
4996
|
+
const types = /* @__PURE__ */ new Set();
|
|
4997
|
+
const sources = /* @__PURE__ */ new Set();
|
|
4998
|
+
for (const tok of flattened) {
|
|
4999
|
+
if (AEX_EVENT_TYPES.includes(tok)) {
|
|
5000
|
+
types.add(tok);
|
|
5001
|
+
} else if (AEX_EVENT_SOURCES.includes(tok)) {
|
|
5002
|
+
sources.add(tok);
|
|
5003
|
+
} else {
|
|
5004
|
+
const hint = suggest(tok, [...AEX_EVENT_TYPES, ...AEX_EVENT_SOURCES]);
|
|
5005
|
+
return {
|
|
5006
|
+
error: `unknown --filter token "${tok}"${hint ? `; did you mean "${hint}"?` : ""} (types: ${AEX_EVENT_TYPES.join(",")}; sources: ${AEX_EVENT_SOURCES.join(",")})`
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
const predicate = (e) => (types.size === 0 || types.has(e.type)) && (sources.size === 0 || sources.has(e.source));
|
|
5011
|
+
return { predicate };
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// dist/host/run-cmd.js
|
|
3629
5015
|
var DEFAULT_SESSION_IDLE_TTL = "3m";
|
|
3630
5016
|
async function runRunCmd(io2, argv) {
|
|
3631
5017
|
if (await refuseInsideManagedRun(io2, "run"))
|
|
@@ -3701,9 +5087,10 @@ async function runRunCmd(io2, argv) {
|
|
|
3701
5087
|
}
|
|
3702
5088
|
rest = runTimeoutFlag.remaining;
|
|
3703
5089
|
if (runTimeoutFlag.value) {
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
5090
|
+
try {
|
|
5091
|
+
parseRunTimeout(runTimeoutFlag.value);
|
|
5092
|
+
} catch (err2) {
|
|
5093
|
+
io2.stderr(`--run-timeout: ${err2.message}
|
|
3707
5094
|
`);
|
|
3708
5095
|
return USAGE_ERR;
|
|
3709
5096
|
}
|
|
@@ -3750,6 +5137,34 @@ async function runRunCmd(io2, argv) {
|
|
|
3750
5137
|
return USAGE_ERR;
|
|
3751
5138
|
}
|
|
3752
5139
|
rest = promptFlags.remaining;
|
|
5140
|
+
const skillFlags = collectRepeated(rest, "--skill");
|
|
5141
|
+
if (skillFlags.error) {
|
|
5142
|
+
io2.stderr(`${skillFlags.error}
|
|
5143
|
+
`);
|
|
5144
|
+
return USAGE_ERR;
|
|
5145
|
+
}
|
|
5146
|
+
rest = skillFlags.remaining;
|
|
5147
|
+
const toolFlags = collectRepeated(rest, "--tool");
|
|
5148
|
+
if (toolFlags.error) {
|
|
5149
|
+
io2.stderr(`${toolFlags.error}
|
|
5150
|
+
`);
|
|
5151
|
+
return USAGE_ERR;
|
|
5152
|
+
}
|
|
5153
|
+
rest = toolFlags.remaining;
|
|
5154
|
+
const agentsMdFlags = collectRepeated(rest, "--agents-md");
|
|
5155
|
+
if (agentsMdFlags.error) {
|
|
5156
|
+
io2.stderr(`${agentsMdFlags.error}
|
|
5157
|
+
`);
|
|
5158
|
+
return USAGE_ERR;
|
|
5159
|
+
}
|
|
5160
|
+
rest = agentsMdFlags.remaining;
|
|
5161
|
+
const fileFlags = collectRepeated(rest, "--file");
|
|
5162
|
+
if (fileFlags.error) {
|
|
5163
|
+
io2.stderr(`${fileFlags.error}
|
|
5164
|
+
`);
|
|
5165
|
+
return USAGE_ERR;
|
|
5166
|
+
}
|
|
5167
|
+
rest = fileFlags.remaining;
|
|
3753
5168
|
const mcpFlags = collectRepeatedKv(rest, "--mcp");
|
|
3754
5169
|
if (mcpFlags.error) {
|
|
3755
5170
|
io2.stderr(`${mcpFlags.error}
|
|
@@ -3801,79 +5216,85 @@ async function runRunCmd(io2, argv) {
|
|
|
3801
5216
|
`);
|
|
3802
5217
|
return USAGE_ERR;
|
|
3803
5218
|
}
|
|
3804
|
-
let
|
|
3805
|
-
let
|
|
5219
|
+
let model;
|
|
5220
|
+
let system;
|
|
5221
|
+
let promptArray;
|
|
5222
|
+
let configMcpServers = [];
|
|
5223
|
+
let configEnvironment;
|
|
5224
|
+
let configRuntimeSize;
|
|
5225
|
+
let configTimeout;
|
|
5226
|
+
let metadata;
|
|
5227
|
+
const mcpHeaderBag = /* @__PURE__ */ new Map();
|
|
3806
5228
|
if (config.value) {
|
|
3807
5229
|
if (modelFlag.value || systemFlag.value || promptFlags.values.length || Object.keys(mcpFlags.entries).length || Object.keys(metadataFlags.entries).length) {
|
|
3808
5230
|
io2.stderr("--config cannot be combined with --model/--system/--prompt/--mcp/--metadata\n");
|
|
3809
5231
|
return USAGE_ERR;
|
|
3810
5232
|
}
|
|
5233
|
+
let runConfig;
|
|
3811
5234
|
try {
|
|
3812
5235
|
const absPath = resolvePath(io2.cwd(), config.value);
|
|
3813
5236
|
const text = await io2.readFile(absPath);
|
|
3814
5237
|
const raw = JSON.parse(text);
|
|
3815
5238
|
const { mcpHeaders, normalised } = stripMcpHeadersForParsing(raw);
|
|
3816
|
-
|
|
5239
|
+
for (const [name, headers] of mcpHeaders)
|
|
5240
|
+
mcpHeaderBag.set(name, headers);
|
|
3817
5241
|
runConfig = parseRunRequestConfig(normalised);
|
|
3818
5242
|
} catch (err2) {
|
|
3819
5243
|
io2.stderr(`failed to load --config: ${err2.message}
|
|
3820
5244
|
`);
|
|
3821
5245
|
return USAGE_ERR;
|
|
3822
5246
|
}
|
|
5247
|
+
model = runConfig.model;
|
|
5248
|
+
system = runConfig.system;
|
|
5249
|
+
promptArray = Array.isArray(runConfig.prompt) ? [...runConfig.prompt] : [runConfig.prompt];
|
|
5250
|
+
configMcpServers = runConfig.mcpServers ?? [];
|
|
5251
|
+
configEnvironment = runConfig.environment;
|
|
5252
|
+
configRuntimeSize = runConfig.runtimeSize;
|
|
5253
|
+
configTimeout = runConfig.timeout;
|
|
5254
|
+
metadata = runConfig.metadata ? { ...runConfig.metadata } : void 0;
|
|
3823
5255
|
} else {
|
|
3824
5256
|
if (!modelFlag.value) {
|
|
3825
5257
|
io2.stderr("--model is required when --config is not provided\n");
|
|
3826
5258
|
return USAGE_ERR;
|
|
3827
5259
|
}
|
|
3828
|
-
|
|
3829
|
-
const hint = suggest(modelFlag.value, RUN_MODELS);
|
|
3830
|
-
io2.stderr(`--model must be one of: ${RUN_MODELS.join(", ")} (got: ${modelFlag.value})${hint ? `; did you mean "${hint}"?` : ""}
|
|
3831
|
-
`);
|
|
3832
|
-
return USAGE_ERR;
|
|
3833
|
-
}
|
|
5260
|
+
model = modelFlag.value;
|
|
3834
5261
|
if (promptFlags.values.length === 0) {
|
|
3835
5262
|
io2.stderr("--prompt is required (repeatable)\n");
|
|
3836
5263
|
return USAGE_ERR;
|
|
3837
5264
|
}
|
|
3838
|
-
let resolvedPrompt;
|
|
3839
5265
|
try {
|
|
3840
|
-
|
|
5266
|
+
promptArray = await Promise.all(promptFlags.values.map((v) => readMaybeFile(io2, v)));
|
|
3841
5267
|
} catch (err2) {
|
|
3842
5268
|
io2.stderr(`failed to read --prompt file: ${err2.message}
|
|
3843
5269
|
`);
|
|
3844
5270
|
return USAGE_ERR;
|
|
3845
5271
|
}
|
|
3846
|
-
let resolvedSystem;
|
|
3847
5272
|
if (systemFlag.value !== null) {
|
|
3848
5273
|
try {
|
|
3849
|
-
|
|
5274
|
+
system = await readMaybeFile(io2, systemFlag.value);
|
|
3850
5275
|
} catch (err2) {
|
|
3851
5276
|
io2.stderr(`failed to read --system file: ${err2.message}
|
|
3852
5277
|
`);
|
|
3853
5278
|
return USAGE_ERR;
|
|
3854
5279
|
}
|
|
3855
5280
|
}
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
};
|
|
5281
|
+
configMcpServers = Object.entries(mcpFlags.entries).map(([name, url]) => ({ name, url }));
|
|
5282
|
+
metadata = Object.keys(metadataFlags.entries).length > 0 ? { ...metadataFlags.entries } : void 0;
|
|
5283
|
+
}
|
|
5284
|
+
let provider;
|
|
5285
|
+
try {
|
|
5286
|
+
provider = resolveModelProvider(model, explicitProvider);
|
|
5287
|
+
} catch (err2) {
|
|
5288
|
+
io2.stderr(`--model: ${err2.message}
|
|
5289
|
+
`);
|
|
5290
|
+
return USAGE_ERR;
|
|
3867
5291
|
}
|
|
3868
|
-
const provider = explicitProvider ?? providersForModel(runConfig.model)[0] ?? DEFAULT_RUN_PROVIDER;
|
|
3869
5292
|
if (!providerKeyValues[provider]) {
|
|
3870
|
-
|
|
5293
|
+
const inferred = explicitProvider === void 0 && providersForModel(model).length > 0;
|
|
5294
|
+
io2.stderr(`--${provider}-api-key is required for provider ${provider}${inferred ? ` (inferred from --model ${model})` : ""} (the platform does not store provider keys on your behalf)
|
|
3871
5295
|
`);
|
|
3872
5296
|
return USAGE_ERR;
|
|
3873
5297
|
}
|
|
3874
|
-
const mcpServersForSubmission = [...runConfig.mcpServers ?? []];
|
|
3875
|
-
const mcpServerSecrets = [];
|
|
3876
|
-
const mcpHeaderBag = new Map(mcpHeadersFromConfig);
|
|
3877
5298
|
for (const [name, headerSpec] of mcpAuthFlags.entries) {
|
|
3878
5299
|
const colon = headerSpec.indexOf(":");
|
|
3879
5300
|
if (colon <= 0 || colon >= headerSpec.length - 1) {
|
|
@@ -3897,102 +5318,135 @@ async function runRunCmd(io2, argv) {
|
|
|
3897
5318
|
existing[headerName] = headerValue;
|
|
3898
5319
|
mcpHeaderBag.set(name, existing);
|
|
3899
5320
|
}
|
|
3900
|
-
for (const ref of mcpServersForSubmission) {
|
|
3901
|
-
const headers = mcpHeaderBag.get(ref.name);
|
|
3902
|
-
if (headers && Object.keys(headers).length > 0) {
|
|
3903
|
-
mcpServerSecrets.push({ name: ref.name, url: ref.url, headers });
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
5321
|
for (const name of mcpHeaderBag.keys()) {
|
|
3907
|
-
if (!
|
|
5322
|
+
if (!configMcpServers.some((m) => m.name === name)) {
|
|
3908
5323
|
io2.stderr(`--mcp-auth ${name}: no matching --mcp / mcpServers entry declared
|
|
3909
5324
|
`);
|
|
3910
5325
|
return USAGE_ERR;
|
|
3911
5326
|
}
|
|
3912
5327
|
}
|
|
3913
|
-
const
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
5328
|
+
const mcpServers = configMcpServers.map((m) => {
|
|
5329
|
+
const headers = mcpHeaderBag.get(m.name);
|
|
5330
|
+
return {
|
|
5331
|
+
name: m.name,
|
|
5332
|
+
url: m.url,
|
|
5333
|
+
...headers && Object.keys(headers).length > 0 ? { headers } : {}
|
|
5334
|
+
};
|
|
5335
|
+
});
|
|
5336
|
+
let skills;
|
|
5337
|
+
let tools;
|
|
5338
|
+
let agentsMd;
|
|
5339
|
+
let files;
|
|
5340
|
+
try {
|
|
5341
|
+
skills = await Promise.all(skillFlags.values.map((ref) => buildSkill(io2, ref)));
|
|
5342
|
+
tools = await Promise.all(toolFlags.values.map((ref) => buildTool(io2, ref)));
|
|
5343
|
+
agentsMd = await Promise.all(agentsMdFlags.values.map((ref) => buildAgentsMd(io2, ref)));
|
|
5344
|
+
files = await Promise.all(fileFlags.values.map((ref) => buildFile(io2, ref)));
|
|
5345
|
+
} catch (err2) {
|
|
5346
|
+
io2.stderr(`failed to attach asset: ${err2.message}
|
|
5347
|
+
`);
|
|
5348
|
+
return USAGE_ERR;
|
|
5349
|
+
}
|
|
5350
|
+
const environment = toCliSessionEnvironment(configEnvironment);
|
|
5351
|
+
const runtimeSize = runtimeSizeFlag.value ?? configRuntimeSize;
|
|
5352
|
+
const timeout = runTimeoutFlag.value ?? configTimeout;
|
|
5353
|
+
const options = {
|
|
5354
|
+
message: promptArray,
|
|
3930
5355
|
provider,
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
...
|
|
3935
|
-
...
|
|
3936
|
-
...
|
|
5356
|
+
model,
|
|
5357
|
+
...system ? { system } : {},
|
|
5358
|
+
...skills.length > 0 ? { skills } : {},
|
|
5359
|
+
...tools.length > 0 ? { tools } : {},
|
|
5360
|
+
...agentsMd.length > 0 ? { agentsMd } : {},
|
|
5361
|
+
...files.length > 0 ? { files } : {},
|
|
5362
|
+
...mcpServers.length > 0 ? { mcpServers } : {},
|
|
5363
|
+
...metadata ? { metadata } : {},
|
|
5364
|
+
apiKeys: providerKeyValues,
|
|
5365
|
+
...environment ? { environment } : {},
|
|
5366
|
+
...runtimeSize ? { runtime: runtimeSize } : {},
|
|
5367
|
+
overrides: {
|
|
5368
|
+
idleTtl: DEFAULT_SESSION_IDLE_TTL,
|
|
5369
|
+
...timeout ? { timeout } : {}
|
|
5370
|
+
},
|
|
5371
|
+
...webhookFlag.value ? { webhook: { url: webhookFlag.value } } : {},
|
|
5372
|
+
...idempotency.value ? { idempotencyKey: idempotency.value } : {}
|
|
3937
5373
|
};
|
|
3938
|
-
const createKey = idempotency.value ?? generateIdempotencyKey();
|
|
3939
|
-
const messageKey = `${createKey}:message`;
|
|
3940
5374
|
const http = makeHttpClient(io2, common.flags);
|
|
5375
|
+
if (follow.present && !io2.webSocketFactory) {
|
|
5376
|
+
io2.stderr(JSON.stringify({
|
|
5377
|
+
error: "websocket_unavailable",
|
|
5378
|
+
message: "`aex run --follow` needs a global WebSocket (Bun or Node >= 22). Upgrade Node or run with bun."
|
|
5379
|
+
}) + "\n");
|
|
5380
|
+
return USAGE_ERR;
|
|
5381
|
+
}
|
|
3941
5382
|
let session;
|
|
3942
5383
|
try {
|
|
3943
|
-
session = await
|
|
5384
|
+
session = await submitCliRun(http, io2.fetchImpl, options);
|
|
3944
5385
|
} catch (err2) {
|
|
3945
5386
|
const d = describeApiError(err2);
|
|
3946
|
-
return emitJsonError(io2, "
|
|
5387
|
+
return emitJsonError(io2, "run_failed", d.message, {
|
|
3947
5388
|
...d.status !== void 0 ? { status: d.status } : {},
|
|
3948
5389
|
...d.remedy ? { remedy: d.remedy } : {}
|
|
3949
5390
|
});
|
|
3950
5391
|
}
|
|
3951
|
-
|
|
5392
|
+
io2.stdout(JSON.stringify(session) + "\n");
|
|
5393
|
+
if (!follow.present)
|
|
5394
|
+
return SUCCESS;
|
|
5395
|
+
const controller = new AbortController();
|
|
5396
|
+
let timedOut = false;
|
|
5397
|
+
let interrupted = false;
|
|
5398
|
+
io2.onSignal?.("SIGINT", () => {
|
|
5399
|
+
interrupted = true;
|
|
5400
|
+
controller.abort();
|
|
5401
|
+
});
|
|
5402
|
+
const timer = followTimeoutMs === null ? null : setTimeout(() => {
|
|
5403
|
+
timedOut = true;
|
|
5404
|
+
controller.abort();
|
|
5405
|
+
}, followTimeoutMs);
|
|
5406
|
+
let lastSeq = -1;
|
|
3952
5407
|
try {
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
sessionId: session.id,
|
|
3958
|
-
...d.status !== void 0 ? { status: d.status } : {},
|
|
3959
|
-
...d.remedy ? { remedy: d.remedy } : {}
|
|
5408
|
+
const stream = openEnvelopeStream(io2, http, session.id, {
|
|
5409
|
+
signal: controller.signal,
|
|
5410
|
+
...common.flags.debug ? { debug: (line) => io2.stderr(`[aex] ${line}
|
|
5411
|
+
`) } : {}
|
|
3960
5412
|
});
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
return SUCCESS;
|
|
3965
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3966
|
-
let currentStatus = accepted.session.status;
|
|
3967
|
-
const deadline = followTimeoutMs === null ? Number.POSITIVE_INFINITY : Date.now() + followTimeoutMs;
|
|
3968
|
-
while (!isSessionParked2(currentStatus)) {
|
|
3969
|
-
await sleep2(2e3);
|
|
3970
|
-
try {
|
|
3971
|
-
const events = await operations_exports.listSessionEvents(http, session.id);
|
|
3972
|
-
for (const event of events) {
|
|
3973
|
-
if (!seen.has(event.id)) {
|
|
3974
|
-
seen.add(event.id);
|
|
3975
|
-
io2.stdout(JSON.stringify(event) + "\n");
|
|
3976
|
-
}
|
|
3977
|
-
}
|
|
3978
|
-
} catch (err2) {
|
|
3979
|
-
io2.stderr(`(transient) event poll failed: ${err2.message}
|
|
3980
|
-
`);
|
|
3981
|
-
}
|
|
3982
|
-
try {
|
|
3983
|
-
const updated = await operations_exports.getSession(http, session.id);
|
|
3984
|
-
currentStatus = updated.status;
|
|
3985
|
-
} catch (err2) {
|
|
3986
|
-
io2.stderr(`(transient) status poll failed: ${err2.message}
|
|
3987
|
-
`);
|
|
5413
|
+
for await (const event of stream) {
|
|
5414
|
+
lastSeq = event.sequence;
|
|
5415
|
+
io2.stdout(JSON.stringify(event) + "\n");
|
|
3988
5416
|
}
|
|
3989
|
-
|
|
5417
|
+
} catch (err2) {
|
|
5418
|
+
if (timer)
|
|
5419
|
+
clearTimeout(timer);
|
|
5420
|
+
if (timedOut) {
|
|
3990
5421
|
emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
|
|
3991
5422
|
sessionId: session.id,
|
|
5423
|
+
lastSeq,
|
|
3992
5424
|
hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
|
|
3993
5425
|
});
|
|
3994
5426
|
return TIMEOUT_ERR;
|
|
3995
5427
|
}
|
|
5428
|
+
if (interrupted) {
|
|
5429
|
+
io2.stderr(`(interrupted) followed up to seq ${lastSeq}
|
|
5430
|
+
`);
|
|
5431
|
+
return SUCCESS;
|
|
5432
|
+
}
|
|
5433
|
+
io2.stderr(`(transient) event stream failed: ${err2.message}
|
|
5434
|
+
`);
|
|
5435
|
+
}
|
|
5436
|
+
if (timer)
|
|
5437
|
+
clearTimeout(timer);
|
|
5438
|
+
if (timedOut) {
|
|
5439
|
+
emitJsonError(io2, "run_follow_timeout", `timed out after ${followTimeoutMs}ms following session`, {
|
|
5440
|
+
sessionId: session.id,
|
|
5441
|
+
lastSeq,
|
|
5442
|
+
hint: `aex status ${session.id} | aex events ${session.id} | aex download ${session.id}`
|
|
5443
|
+
});
|
|
5444
|
+
return TIMEOUT_ERR;
|
|
5445
|
+
}
|
|
5446
|
+
if (interrupted) {
|
|
5447
|
+
io2.stderr(`(interrupted) followed up to seq ${lastSeq}
|
|
5448
|
+
`);
|
|
5449
|
+
return SUCCESS;
|
|
3996
5450
|
}
|
|
3997
5451
|
try {
|
|
3998
5452
|
const final = await operations_exports.getSession(http, session.id);
|
|
@@ -4012,6 +5466,50 @@ async function runRunCmd(io2, argv) {
|
|
|
4012
5466
|
return RUNTIME_ERR;
|
|
4013
5467
|
}
|
|
4014
5468
|
}
|
|
5469
|
+
async function buildSkill(io2, ref) {
|
|
5470
|
+
const content = await readAtFile(io2, ref);
|
|
5471
|
+
return buildCliSkill(content, ref);
|
|
5472
|
+
}
|
|
5473
|
+
async function buildTool(io2, ref) {
|
|
5474
|
+
const content = await readAtFile(io2, ref);
|
|
5475
|
+
const entry = baseName(stripAt(ref));
|
|
5476
|
+
const name = deriveName(ref, 1);
|
|
5477
|
+
return buildCliTool({
|
|
5478
|
+
name,
|
|
5479
|
+
description: `Custom tool ${name}`,
|
|
5480
|
+
entry,
|
|
5481
|
+
content
|
|
5482
|
+
});
|
|
5483
|
+
}
|
|
5484
|
+
async function buildAgentsMd(io2, ref) {
|
|
5485
|
+
const content = await readAtFile(io2, ref);
|
|
5486
|
+
return buildCliAgentsMd(content, deriveName(ref, 2));
|
|
5487
|
+
}
|
|
5488
|
+
async function buildFile(io2, ref) {
|
|
5489
|
+
const content = await readAtFile(io2, ref);
|
|
5490
|
+
const name = baseName(stripAt(ref));
|
|
5491
|
+
return buildCliFile({ name, content });
|
|
5492
|
+
}
|
|
5493
|
+
async function readAtFile(io2, value) {
|
|
5494
|
+
const path = resolvePath(io2.cwd(), stripAt(value));
|
|
5495
|
+
return io2.readFile(path);
|
|
5496
|
+
}
|
|
5497
|
+
function stripAt(value) {
|
|
5498
|
+
return value.startsWith("@") ? value.slice(1) : value;
|
|
5499
|
+
}
|
|
5500
|
+
function baseName(p) {
|
|
5501
|
+
const trimmed = p.replace(/[\\/]+$/, "");
|
|
5502
|
+
const i2 = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
5503
|
+
return i2 >= 0 ? trimmed.slice(i2 + 1) : trimmed;
|
|
5504
|
+
}
|
|
5505
|
+
function deriveName(ref, minLen) {
|
|
5506
|
+
const base = baseName(stripAt(ref));
|
|
5507
|
+
const noExt = base.includes(".") ? base.slice(0, base.lastIndexOf(".")) : base;
|
|
5508
|
+
let slug = noExt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5509
|
+
while (slug.length < minLen)
|
|
5510
|
+
slug += "x";
|
|
5511
|
+
return slug;
|
|
5512
|
+
}
|
|
4015
5513
|
function stripMcpHeadersForParsing(input) {
|
|
4016
5514
|
const mcpHeaders = /* @__PURE__ */ new Map();
|
|
4017
5515
|
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
@@ -4053,15 +5551,6 @@ async function readMaybeFile(io2, value) {
|
|
|
4053
5551
|
}
|
|
4054
5552
|
return value;
|
|
4055
5553
|
}
|
|
4056
|
-
function generateIdempotencyKey() {
|
|
4057
|
-
const c = globalThis.crypto;
|
|
4058
|
-
if (c?.randomUUID)
|
|
4059
|
-
return c.randomUUID();
|
|
4060
|
-
return `idem-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
4061
|
-
}
|
|
4062
|
-
function sleep2(ms) {
|
|
4063
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4064
|
-
}
|
|
4065
5554
|
|
|
4066
5555
|
// dist/host/status.js
|
|
4067
5556
|
async function runStatusCmd(io2, argv) {
|
|
@@ -4179,7 +5668,7 @@ async function runWaitCmd(io2, argv) {
|
|
|
4179
5668
|
`);
|
|
4180
5669
|
if (Date.now() >= deadline)
|
|
4181
5670
|
return timeout({});
|
|
4182
|
-
await
|
|
5671
|
+
await sleep2(intervalMs);
|
|
4183
5672
|
continue;
|
|
4184
5673
|
}
|
|
4185
5674
|
if (isSessionParked2(session.status)) {
|
|
@@ -4188,10 +5677,10 @@ async function runWaitCmd(io2, argv) {
|
|
|
4188
5677
|
}
|
|
4189
5678
|
if (Date.now() >= deadline)
|
|
4190
5679
|
return timeout({ lastStatus: session.status });
|
|
4191
|
-
await
|
|
5680
|
+
await sleep2(intervalMs);
|
|
4192
5681
|
}
|
|
4193
5682
|
}
|
|
4194
|
-
function
|
|
5683
|
+
function sleep2(ms) {
|
|
4195
5684
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4196
5685
|
}
|
|
4197
5686
|
|
|
@@ -4251,7 +5740,7 @@ async function runEventsCmd(io2, argv) {
|
|
|
4251
5740
|
`);
|
|
4252
5741
|
if (Date.now() >= deadline)
|
|
4253
5742
|
return emitTimeout(io2, sessionId, timeoutMs);
|
|
4254
|
-
await
|
|
5743
|
+
await sleep3(2e3);
|
|
4255
5744
|
continue;
|
|
4256
5745
|
}
|
|
4257
5746
|
for (const event of events) {
|
|
@@ -4271,18 +5760,19 @@ async function runEventsCmd(io2, argv) {
|
|
|
4271
5760
|
}
|
|
4272
5761
|
if (Date.now() >= deadline)
|
|
4273
5762
|
return emitTimeout(io2, sessionId, timeoutMs);
|
|
4274
|
-
await
|
|
5763
|
+
await sleep3(2e3);
|
|
4275
5764
|
}
|
|
4276
5765
|
}
|
|
4277
5766
|
function emitTimeout(io2, sessionId, timeoutMs) {
|
|
4278
5767
|
emitJsonError(io2, "events_follow_timeout", `timed out after ${timeoutMs}ms following session events`, { sessionId });
|
|
4279
5768
|
return TIMEOUT_ERR;
|
|
4280
5769
|
}
|
|
4281
|
-
function
|
|
5770
|
+
function sleep3(ms) {
|
|
4282
5771
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4283
5772
|
}
|
|
4284
5773
|
|
|
4285
5774
|
// dist/host/outputs.js
|
|
5775
|
+
import { resolve as resolvePath2 } from "node:path";
|
|
4286
5776
|
async function runOutputsCmd(io2, argv) {
|
|
4287
5777
|
if (await refuseInsideManagedRun(io2, "outputs"))
|
|
4288
5778
|
return USAGE_ERR;
|
|
@@ -4292,31 +5782,241 @@ async function runOutputsCmd(io2, argv) {
|
|
|
4292
5782
|
`);
|
|
4293
5783
|
return USAGE_ERR;
|
|
4294
5784
|
}
|
|
4295
|
-
const
|
|
5785
|
+
const args = common.rest;
|
|
5786
|
+
const sub = args[0];
|
|
5787
|
+
const http = makeHttpClient(io2, common.flags);
|
|
5788
|
+
switch (sub) {
|
|
5789
|
+
case "read":
|
|
5790
|
+
return outputsRead(io2, http, args.slice(1));
|
|
5791
|
+
case "download":
|
|
5792
|
+
return outputsDownload(io2, http, args.slice(1), common.flags);
|
|
5793
|
+
case "link":
|
|
5794
|
+
return outputsLink(io2, http, args.slice(1));
|
|
5795
|
+
case "find":
|
|
5796
|
+
return outputsFind(io2, http, args.slice(1));
|
|
5797
|
+
case "search":
|
|
5798
|
+
return outputsSearch(io2, http, args.slice(1));
|
|
5799
|
+
default:
|
|
5800
|
+
return outputsList(io2, http, args);
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
async function outputsList(io2, http, args) {
|
|
5804
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
4296
5805
|
if (positional.length !== 1) {
|
|
4297
5806
|
io2.stderr("usage: aex outputs <session-id> [common flags]\n");
|
|
4298
5807
|
return USAGE_ERR;
|
|
4299
5808
|
}
|
|
4300
5809
|
const sessionId = positional[0];
|
|
4301
|
-
const http = makeHttpClient(io2, common.flags);
|
|
4302
5810
|
try {
|
|
4303
5811
|
const outputs = await operations_exports.listSessionOutputs(http, sessionId);
|
|
4304
|
-
for (const out of outputs)
|
|
5812
|
+
for (const out of outputs)
|
|
4305
5813
|
io2.stdout(JSON.stringify(out) + "\n");
|
|
4306
|
-
}
|
|
4307
5814
|
return SUCCESS;
|
|
4308
5815
|
} catch (err2) {
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
5816
|
+
return outputsError(io2, "outputs_failed", err2, { sessionId });
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
async function outputsRead(io2, http, args) {
|
|
5820
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
5821
|
+
if (positional.length !== 2) {
|
|
5822
|
+
io2.stderr("usage: aex outputs read <session-id> <path> [common flags]\n");
|
|
5823
|
+
return USAGE_ERR;
|
|
5824
|
+
}
|
|
5825
|
+
const [sessionId, selector] = positional;
|
|
5826
|
+
try {
|
|
5827
|
+
const text = await operations_exports.readOutputText(http, sessionId, { path: selector });
|
|
5828
|
+
io2.stdout(JSON.stringify(text) + "\n");
|
|
5829
|
+
return SUCCESS;
|
|
5830
|
+
} catch (err2) {
|
|
5831
|
+
return outputsError(io2, "outputs_read_failed", err2, { sessionId, path: selector });
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
async function outputsDownload(io2, http, args, flags) {
|
|
5835
|
+
const outFlag = takeFlagValue(args, "--out");
|
|
5836
|
+
if (outFlag.error) {
|
|
5837
|
+
io2.stderr(`${outFlag.error}
|
|
5838
|
+
`);
|
|
5839
|
+
return USAGE_ERR;
|
|
5840
|
+
}
|
|
5841
|
+
void flags;
|
|
5842
|
+
const positional = outFlag.remaining.filter((a) => !a.startsWith("--"));
|
|
5843
|
+
if (positional.length !== 2) {
|
|
5844
|
+
io2.stderr("usage: aex outputs download <session-id> <path> [--out file] [common flags]\n");
|
|
5845
|
+
return USAGE_ERR;
|
|
5846
|
+
}
|
|
5847
|
+
const [sessionId, selector] = positional;
|
|
5848
|
+
let bytes;
|
|
5849
|
+
try {
|
|
5850
|
+
bytes = (await operations_exports.downloadOutput(http, sessionId, { path: selector })).bytes;
|
|
5851
|
+
} catch (err2) {
|
|
5852
|
+
return outputsError(io2, "outputs_download_failed", err2, { sessionId, path: selector });
|
|
5853
|
+
}
|
|
5854
|
+
const destination = resolvePath2(io2.cwd(), outFlag.value ?? baseName2(selector));
|
|
5855
|
+
try {
|
|
5856
|
+
await io2.writeFile(destination, bytes);
|
|
5857
|
+
} catch (err2) {
|
|
5858
|
+
return emitJsonError(io2, "write_failed", `failed to write file: ${err2.message}`, { destination });
|
|
5859
|
+
}
|
|
5860
|
+
io2.stdout(JSON.stringify({ sessionId, path: selector, out: destination, bytes: bytes.byteLength }) + "\n");
|
|
5861
|
+
return SUCCESS;
|
|
5862
|
+
}
|
|
5863
|
+
async function outputsLink(io2, http, args) {
|
|
5864
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
5865
|
+
if (positional.length !== 2) {
|
|
5866
|
+
io2.stderr("usage: aex outputs link <session-id> <path> [common flags]\n");
|
|
5867
|
+
return USAGE_ERR;
|
|
5868
|
+
}
|
|
5869
|
+
const [sessionId, selector] = positional;
|
|
5870
|
+
try {
|
|
5871
|
+
const link = await operations_exports.outputLink(http, sessionId, { path: selector });
|
|
5872
|
+
io2.stdout(JSON.stringify(link) + "\n");
|
|
5873
|
+
return SUCCESS;
|
|
5874
|
+
} catch (err2) {
|
|
5875
|
+
return outputsError(io2, "outputs_link_failed", err2, { sessionId, path: selector });
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5878
|
+
async function outputsFind(io2, http, args) {
|
|
5879
|
+
const name = takeFlagValue(args, "--name");
|
|
5880
|
+
const ext = takeFlagValue(name.remaining, "--ext");
|
|
5881
|
+
const type = takeFlagValue(ext.remaining, "--type");
|
|
5882
|
+
const contentType = takeFlagValue(type.remaining, "--content-type");
|
|
5883
|
+
const err2 = name.error ?? ext.error ?? type.error ?? contentType.error;
|
|
5884
|
+
if (err2) {
|
|
5885
|
+
io2.stderr(`${err2}
|
|
5886
|
+
`);
|
|
5887
|
+
return USAGE_ERR;
|
|
5888
|
+
}
|
|
5889
|
+
const positional = contentType.remaining.filter((a) => !a.startsWith("--"));
|
|
5890
|
+
if (positional.length !== 1) {
|
|
5891
|
+
io2.stderr("usage: aex outputs find <session-id> [--name S] [--ext E] [--type T] [--content-type CT] [common flags]\n");
|
|
5892
|
+
return USAGE_ERR;
|
|
5893
|
+
}
|
|
5894
|
+
const sessionId = positional[0];
|
|
5895
|
+
const query = {
|
|
5896
|
+
...name.value !== null ? { filename: name.value } : {},
|
|
5897
|
+
...ext.value !== null ? { extension: ext.value } : {},
|
|
5898
|
+
...type.value !== null ? { type: type.value } : {},
|
|
5899
|
+
...contentType.value !== null ? { contentType: contentType.value } : {}
|
|
5900
|
+
};
|
|
5901
|
+
try {
|
|
5902
|
+
const hits = await searchSessionOutputs(http, sessionId, query);
|
|
5903
|
+
for (const hit of hits)
|
|
5904
|
+
io2.stdout(JSON.stringify(hit) + "\n");
|
|
5905
|
+
return SUCCESS;
|
|
5906
|
+
} catch (err22) {
|
|
5907
|
+
return outputsError(io2, "outputs_find_failed", err22, { sessionId });
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
async function outputsSearch(io2, http, args) {
|
|
5911
|
+
const query = takeFlagValue(args, "--query");
|
|
5912
|
+
const name = takeFlagValue(query.remaining, "--name");
|
|
5913
|
+
const ext = takeFlagValue(name.remaining, "--ext");
|
|
5914
|
+
const contentType = takeFlagValue(ext.remaining, "--content-type");
|
|
5915
|
+
const limit = takeFlagValue(contentType.remaining, "--limit");
|
|
5916
|
+
const runIds = collectRepeated(limit.remaining, "--run-id");
|
|
5917
|
+
const err2 = query.error ?? name.error ?? ext.error ?? contentType.error ?? limit.error ?? runIds.error;
|
|
5918
|
+
if (err2) {
|
|
5919
|
+
io2.stderr(`${err2}
|
|
5920
|
+
`);
|
|
5921
|
+
return USAGE_ERR;
|
|
5922
|
+
}
|
|
5923
|
+
const filename = query.value ?? name.value;
|
|
5924
|
+
let limitValue;
|
|
5925
|
+
if (limit.value !== null) {
|
|
5926
|
+
const n = Number(limit.value);
|
|
5927
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
5928
|
+
io2.stderr(`--limit must be a positive integer (got: ${limit.value})
|
|
5929
|
+
`);
|
|
5930
|
+
return USAGE_ERR;
|
|
5931
|
+
}
|
|
5932
|
+
limitValue = n;
|
|
5933
|
+
}
|
|
5934
|
+
const search = {
|
|
5935
|
+
...filename !== null ? { filename } : {},
|
|
5936
|
+
...ext.value !== null ? { extension: ext.value } : {},
|
|
5937
|
+
...contentType.value !== null ? { contentType: contentType.value } : {},
|
|
5938
|
+
...runIds.values.length > 0 ? { runIds: [...runIds.values] } : {},
|
|
5939
|
+
...limitValue !== void 0 ? { limit: limitValue } : {}
|
|
5940
|
+
};
|
|
5941
|
+
try {
|
|
5942
|
+
const page = await searchWorkspaceOutputs(http, search);
|
|
5943
|
+
io2.stdout(JSON.stringify(page) + "\n");
|
|
5944
|
+
return SUCCESS;
|
|
5945
|
+
} catch (err22) {
|
|
5946
|
+
return outputsError(io2, "outputs_search_failed", err22, {});
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
function outputsError(io2, code, err2, extra) {
|
|
5950
|
+
const d = describeApiError(err2);
|
|
5951
|
+
return emitJsonError(io2, code, d.message, {
|
|
5952
|
+
...extra,
|
|
5953
|
+
...d.status !== void 0 ? { status: d.status } : {},
|
|
5954
|
+
...d.remedy ? { remedy: d.remedy } : {}
|
|
5955
|
+
});
|
|
5956
|
+
}
|
|
5957
|
+
async function searchWorkspaceOutputs(http, query) {
|
|
5958
|
+
assertMetadataOnlyOutputSearch(query, "aex outputs search");
|
|
5959
|
+
const runIds = query.runIds && query.runIds.length > 0 ? [...query.runIds] : void 0;
|
|
5960
|
+
const limit = query.limit ?? 100;
|
|
5961
|
+
const hits = [];
|
|
5962
|
+
const candidates = runIds ?? await listRecentRunIds(http, limit);
|
|
5963
|
+
for (const runId of candidates) {
|
|
5964
|
+
const outputs = await searchSessionOutputs(http, runId, query);
|
|
5965
|
+
for (const output of outputs) {
|
|
5966
|
+
hits.push(outputHit(runId, output));
|
|
5967
|
+
if (hits.length >= limit)
|
|
5968
|
+
return { hits };
|
|
5969
|
+
}
|
|
4315
5970
|
}
|
|
5971
|
+
return { hits };
|
|
5972
|
+
}
|
|
5973
|
+
async function listRecentRunIds(http, limit) {
|
|
5974
|
+
const out = [];
|
|
5975
|
+
let cursor;
|
|
5976
|
+
while (out.length < limit) {
|
|
5977
|
+
const page = await operations_exports.listSessions(http, { limit: Math.min(100, limit - out.length), ...cursor ? { cursor } : {} });
|
|
5978
|
+
out.push(...page.sessions.map((session) => session.id));
|
|
5979
|
+
if (!page.nextCursor)
|
|
5980
|
+
break;
|
|
5981
|
+
cursor = page.nextCursor;
|
|
5982
|
+
}
|
|
5983
|
+
return out;
|
|
5984
|
+
}
|
|
5985
|
+
async function searchSessionOutputs(http, sessionId, query) {
|
|
5986
|
+
const listQuery = {
|
|
5987
|
+
...query.extension !== void 0 ? { extension: query.extension } : {},
|
|
5988
|
+
...query.contentType !== void 0 ? { contentType: query.contentType } : {}
|
|
5989
|
+
};
|
|
5990
|
+
const outputs = await operations_exports.listSessionOutputs(http, sessionId, Object.keys(listQuery).length > 0 ? listQuery : void 0);
|
|
5991
|
+
if (query.filename === void 0)
|
|
5992
|
+
return outputs;
|
|
5993
|
+
const match = operations_exports.toFilenameMatcher(query.filename);
|
|
5994
|
+
return outputs.filter((output) => typeof output.filename === "string" && match(output.filename));
|
|
5995
|
+
}
|
|
5996
|
+
function outputHit(runId, output) {
|
|
5997
|
+
return {
|
|
5998
|
+
runId,
|
|
5999
|
+
outputId: output.id,
|
|
6000
|
+
...output.filename !== void 0 ? { filename: output.filename } : {},
|
|
6001
|
+
...output.sizeBytes !== void 0 ? { sizeBytes: output.sizeBytes } : {},
|
|
6002
|
+
...output.contentType !== void 0 ? { contentType: output.contentType } : {}
|
|
6003
|
+
};
|
|
6004
|
+
}
|
|
6005
|
+
function assertMetadataOnlyOutputSearch(query, surface) {
|
|
6006
|
+
for (const key of ["content", "text", "query", "grep", "body"]) {
|
|
6007
|
+
if (Object.prototype.hasOwnProperty.call(query, key)) {
|
|
6008
|
+
throw new Error(`${surface}: ${key} is not supported; output search is metadata-only`);
|
|
6009
|
+
}
|
|
6010
|
+
}
|
|
6011
|
+
}
|
|
6012
|
+
function baseName2(p) {
|
|
6013
|
+
const trimmed = p.replace(/[\\/]+$/, "");
|
|
6014
|
+
const i2 = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
|
|
6015
|
+
return i2 >= 0 ? trimmed.slice(i2 + 1) : trimmed;
|
|
4316
6016
|
}
|
|
4317
6017
|
|
|
4318
6018
|
// dist/host/download.js
|
|
4319
|
-
import { resolve as
|
|
6019
|
+
import { resolve as resolvePath3 } from "node:path";
|
|
4320
6020
|
var NAMESPACE_DOWNLOADERS = {
|
|
4321
6021
|
outputs: operations_exports.downloadOutputs,
|
|
4322
6022
|
events: operations_exports.downloadEvents,
|
|
@@ -4379,10 +6079,10 @@ async function runDownloadCmd(io2, argv) {
|
|
|
4379
6079
|
}
|
|
4380
6080
|
function resolveDestination(io2, out, sessionId, namespace2) {
|
|
4381
6081
|
if (out) {
|
|
4382
|
-
return
|
|
6082
|
+
return resolvePath3(io2.cwd(), out);
|
|
4383
6083
|
}
|
|
4384
6084
|
const suffix = namespace2 ? `-${namespace2}` : "";
|
|
4385
|
-
return
|
|
6085
|
+
return resolvePath3(io2.cwd(), `aex-session-${sessionId}${suffix}.zip`);
|
|
4386
6086
|
}
|
|
4387
6087
|
|
|
4388
6088
|
// dist/host/cancel.js
|
|
@@ -4543,9 +6243,9 @@ async function runBillingCmd(io2, argv) {
|
|
|
4543
6243
|
if (common.rest[0] === "portal") {
|
|
4544
6244
|
return runBillingPortal(io2, common.rest.slice(1), common.flags);
|
|
4545
6245
|
}
|
|
4546
|
-
const
|
|
4547
|
-
if (
|
|
4548
|
-
io2.stderr(`unexpected arguments: ${
|
|
6246
|
+
const json = common.flags.json;
|
|
6247
|
+
if (common.rest.length > 0) {
|
|
6248
|
+
io2.stderr(`unexpected arguments: ${common.rest.join(" ")}
|
|
4549
6249
|
`);
|
|
4550
6250
|
io2.stderr("usage: aex billing [--json] | aex billing ledger [--limit N] | aex billing upgrade pro|team | aex billing portal [common flags]\n");
|
|
4551
6251
|
return USAGE_ERR;
|
|
@@ -4575,8 +6275,8 @@ async function runBillingCmd(io2, argv) {
|
|
|
4575
6275
|
}
|
|
4576
6276
|
}
|
|
4577
6277
|
async function runBillingUpgrade(io2, argv, flags) {
|
|
4578
|
-
const
|
|
4579
|
-
const { value: successUrl, remaining: rest2 } = takeOptionFlag(
|
|
6278
|
+
const json = flags.json;
|
|
6279
|
+
const { value: successUrl, remaining: rest2 } = takeOptionFlag(argv, "--success-url");
|
|
4580
6280
|
const { value: cancelUrl, remaining: rest3 } = takeOptionFlag(rest2, "--cancel-url");
|
|
4581
6281
|
const { value: idempotencyKey, remaining } = takeOptionFlag(rest3, "--idempotency-key");
|
|
4582
6282
|
const planKey = remaining[0];
|
|
@@ -4605,8 +6305,8 @@ async function runBillingUpgrade(io2, argv, flags) {
|
|
|
4605
6305
|
}
|
|
4606
6306
|
}
|
|
4607
6307
|
async function runBillingPortal(io2, argv, flags) {
|
|
4608
|
-
const
|
|
4609
|
-
const { value: returnUrl, remaining } = takeOptionFlag(
|
|
6308
|
+
const json = flags.json;
|
|
6309
|
+
const { value: returnUrl, remaining } = takeOptionFlag(argv, "--return-url");
|
|
4610
6310
|
if (remaining.length > 0) {
|
|
4611
6311
|
io2.stderr(`unexpected arguments: ${remaining.join(" ")}
|
|
4612
6312
|
`);
|
|
@@ -4786,7 +6486,7 @@ async function runSessionsCmd(io2, argv) {
|
|
|
4786
6486
|
}
|
|
4787
6487
|
|
|
4788
6488
|
// dist/host/debug.js
|
|
4789
|
-
import { dirname, resolve as
|
|
6489
|
+
import { dirname, resolve as resolvePath4 } from "node:path";
|
|
4790
6490
|
function status(source, state, opts = {}) {
|
|
4791
6491
|
return {
|
|
4792
6492
|
source,
|
|
@@ -5104,7 +6804,7 @@ function toMs(iso) {
|
|
|
5104
6804
|
async function writeDebugBundle(io2, baseDir, bundle) {
|
|
5105
6805
|
const encoder3 = new TextEncoder();
|
|
5106
6806
|
for (const file2 of bundle.files) {
|
|
5107
|
-
const abs =
|
|
6807
|
+
const abs = resolvePath4(baseDir, file2.path);
|
|
5108
6808
|
if (io2.mkdirp)
|
|
5109
6809
|
await io2.mkdirp(dirname(abs));
|
|
5110
6810
|
await io2.writeFile(abs, encoder3.encode(file2.content));
|
|
@@ -5376,7 +7076,7 @@ ${USAGE}`);
|
|
|
5376
7076
|
}
|
|
5377
7077
|
sinceMs = d.ms;
|
|
5378
7078
|
}
|
|
5379
|
-
const outDir =
|
|
7079
|
+
const outDir = resolvePath4(io2.cwd(), outF.value ?? `aex-debug-${runId}`);
|
|
5380
7080
|
const client = makeAwsSources(region);
|
|
5381
7081
|
let targets;
|
|
5382
7082
|
try {
|
|
@@ -5449,7 +7149,7 @@ async function runLoginCmd(io2, argv) {
|
|
|
5449
7149
|
return USAGE_ERR;
|
|
5450
7150
|
}
|
|
5451
7151
|
const resolvedUrl = aexUrl ?? AEX_DEFAULT_BASE_URL;
|
|
5452
|
-
const http = makeHttpClient(io2, { apiKey, aexUrl: resolvedUrl, debug: debug2 });
|
|
7152
|
+
const http = makeHttpClient(io2, { apiKey, aexUrl: resolvedUrl, debug: debug2, json: false });
|
|
5453
7153
|
let workspaceId;
|
|
5454
7154
|
try {
|
|
5455
7155
|
const me = await operations_exports.whoami(http);
|
|
@@ -5611,110 +7311,6 @@ function hasUnknown(io2, rest, verb) {
|
|
|
5611
7311
|
return false;
|
|
5612
7312
|
}
|
|
5613
7313
|
|
|
5614
|
-
// dist/host/stream-render.js
|
|
5615
|
-
async function* openEnvelopeStream(io2, http, sessionId, options = {}) {
|
|
5616
|
-
const first = await operations_exports.getSessionCoordinatorTicket(http, sessionId);
|
|
5617
|
-
if (options.debug) {
|
|
5618
|
-
let host = "(invalid)";
|
|
5619
|
-
try {
|
|
5620
|
-
host = new URL(first.wsUrl).host;
|
|
5621
|
-
} catch {
|
|
5622
|
-
}
|
|
5623
|
-
options.debug(`ticket minted: host=${host} expiresInMs=${first.expiresAtMs - Date.now()} from=${options.from ?? 0}`);
|
|
5624
|
-
}
|
|
5625
|
-
yield* streamCoordinatorEvents({
|
|
5626
|
-
wsUrl: first.wsUrl,
|
|
5627
|
-
from: options.from ?? 0,
|
|
5628
|
-
fetchTicket: async () => (await operations_exports.getSessionCoordinatorTicket(http, sessionId)).ticket,
|
|
5629
|
-
...io2.webSocketFactory ? { webSocketFactory: io2.webSocketFactory } : {},
|
|
5630
|
-
...options.settleConsistent ? { isTerminal: isRunSettled } : {},
|
|
5631
|
-
...options.signal ? { signal: options.signal } : {}
|
|
5632
|
-
});
|
|
5633
|
-
}
|
|
5634
|
-
var PREVIEW_MAX = 120;
|
|
5635
|
-
function clip(text, max = PREVIEW_MAX) {
|
|
5636
|
-
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
5637
|
-
return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
|
|
5638
|
-
}
|
|
5639
|
-
function str2(v) {
|
|
5640
|
-
return typeof v === "string" ? v : v === void 0 || v === null ? "" : JSON.stringify(v);
|
|
5641
|
-
}
|
|
5642
|
-
function argsPreview(e) {
|
|
5643
|
-
const d = e.data;
|
|
5644
|
-
const candidate = d.input ?? d.args ?? d.arguments ?? d.params;
|
|
5645
|
-
if (candidate === void 0)
|
|
5646
|
-
return "";
|
|
5647
|
-
return clip(typeof candidate === "string" ? candidate : JSON.stringify(candidate));
|
|
5648
|
-
}
|
|
5649
|
-
function renderEnvelope(e, options = {}) {
|
|
5650
|
-
if (channelOf(e) === "log" && !options.logs)
|
|
5651
|
-
return null;
|
|
5652
|
-
switch (e.type) {
|
|
5653
|
-
case "RUN_STARTED":
|
|
5654
|
-
return "\u25B6 run started";
|
|
5655
|
-
case "TEXT_MESSAGE_CONTENT": {
|
|
5656
|
-
const delta = toAGUI(e).type === "TEXT_MESSAGE_CONTENT" ? toAGUI(e).delta : "";
|
|
5657
|
-
return delta ? delta : null;
|
|
5658
|
-
}
|
|
5659
|
-
case "TOOL_CALL_START": {
|
|
5660
|
-
const agui = toAGUI(e);
|
|
5661
|
-
const name = agui.type === "TOOL_CALL_START" ? agui.toolCallName : str2(e.data.name);
|
|
5662
|
-
const preview = argsPreview(e);
|
|
5663
|
-
return `\xB7 tool ${name || "(unnamed)"}(${preview})`;
|
|
5664
|
-
}
|
|
5665
|
-
case "TOOL_CALL_RESULT": {
|
|
5666
|
-
const name = str2(e.data.name) || "result";
|
|
5667
|
-
const content = e.data.content;
|
|
5668
|
-
const ok = content === void 0 || content === null ? "ok" : clip(str2(content));
|
|
5669
|
-
return ` \u2190 ${name} ${ok}`;
|
|
5670
|
-
}
|
|
5671
|
-
case "CUSTOM": {
|
|
5672
|
-
const label = e.message ?? str2(e.data.name) ?? "custom";
|
|
5673
|
-
if (e.data.name === "aex.session.idle") {
|
|
5674
|
-
const value = e.data.value;
|
|
5675
|
-
const reason = value && typeof value.reason === "string" ? value.reason : "";
|
|
5676
|
-
if (reason && reason !== "completed")
|
|
5677
|
-
return `[aex] ${label} (${reason})`;
|
|
5678
|
-
}
|
|
5679
|
-
return `[aex] ${label}`;
|
|
5680
|
-
}
|
|
5681
|
-
case "LOG": {
|
|
5682
|
-
const level = e.level ?? "info";
|
|
5683
|
-
return `[${level}] ${e.message ?? str2(e.data.message)}`;
|
|
5684
|
-
}
|
|
5685
|
-
case "RUN_FINISHED":
|
|
5686
|
-
return "\u2713 run finished";
|
|
5687
|
-
case "RUN_ERROR": {
|
|
5688
|
-
const agui = toAGUI(e);
|
|
5689
|
-
const message = agui.type === "RUN_ERROR" ? agui.message : e.message ?? "run error";
|
|
5690
|
-
return `\u2717 run error: ${message}`;
|
|
5691
|
-
}
|
|
5692
|
-
default:
|
|
5693
|
-
return null;
|
|
5694
|
-
}
|
|
5695
|
-
}
|
|
5696
|
-
function parseFilters(tokens) {
|
|
5697
|
-
const flattened = tokens.flatMap((t) => t.split(",")).map((s) => s.trim()).filter(Boolean);
|
|
5698
|
-
if (flattened.length === 0)
|
|
5699
|
-
return {};
|
|
5700
|
-
const types = /* @__PURE__ */ new Set();
|
|
5701
|
-
const sources = /* @__PURE__ */ new Set();
|
|
5702
|
-
for (const tok of flattened) {
|
|
5703
|
-
if (AEX_EVENT_TYPES.includes(tok)) {
|
|
5704
|
-
types.add(tok);
|
|
5705
|
-
} else if (AEX_EVENT_SOURCES.includes(tok)) {
|
|
5706
|
-
sources.add(tok);
|
|
5707
|
-
} else {
|
|
5708
|
-
const hint = suggest(tok, [...AEX_EVENT_TYPES, ...AEX_EVENT_SOURCES]);
|
|
5709
|
-
return {
|
|
5710
|
-
error: `unknown --filter token "${tok}"${hint ? `; did you mean "${hint}"?` : ""} (types: ${AEX_EVENT_TYPES.join(",")}; sources: ${AEX_EVENT_SOURCES.join(",")})`
|
|
5711
|
-
};
|
|
5712
|
-
}
|
|
5713
|
-
}
|
|
5714
|
-
const predicate = (e) => (types.size === 0 || types.has(e.type)) && (sources.size === 0 || sources.has(e.source));
|
|
5715
|
-
return { predicate };
|
|
5716
|
-
}
|
|
5717
|
-
|
|
5718
7314
|
// dist/host/tail.js
|
|
5719
7315
|
async function runTailCmd(io2, argv) {
|
|
5720
7316
|
if (await refuseInsideManagedRun(io2, "tail"))
|
|
@@ -5725,8 +7321,8 @@ async function runTailCmd(io2, argv) {
|
|
|
5725
7321
|
`);
|
|
5726
7322
|
return USAGE_ERR;
|
|
5727
7323
|
}
|
|
5728
|
-
const
|
|
5729
|
-
const logsFlag = takeBooleanFlag(
|
|
7324
|
+
const json = common.flags.json;
|
|
7325
|
+
const logsFlag = takeBooleanFlag(common.rest, "--logs");
|
|
5730
7326
|
const settleFlag = takeBooleanFlag(logsFlag.remaining, "--settle");
|
|
5731
7327
|
const filterFlag = collectRepeated(settleFlag.remaining, "--filter");
|
|
5732
7328
|
if (filterFlag.error) {
|
|
@@ -5806,7 +7402,7 @@ async function runTailCmd(io2, argv) {
|
|
|
5806
7402
|
if (filters.predicate && !filters.predicate(e)) {
|
|
5807
7403
|
continue;
|
|
5808
7404
|
}
|
|
5809
|
-
if (
|
|
7405
|
+
if (json) {
|
|
5810
7406
|
if (logsFlag.present || e.channel !== "log") {
|
|
5811
7407
|
io2.stdout(JSON.stringify(e) + "\n");
|
|
5812
7408
|
eventCount++;
|
|
@@ -5879,8 +7475,8 @@ async function runInspectCmd(io2, argv) {
|
|
|
5879
7475
|
`);
|
|
5880
7476
|
return USAGE_ERR;
|
|
5881
7477
|
}
|
|
5882
|
-
const
|
|
5883
|
-
const logsFlag = takeBooleanFlag(
|
|
7478
|
+
const json = common.flags.json;
|
|
7479
|
+
const logsFlag = takeBooleanFlag(common.rest, "--logs");
|
|
5884
7480
|
const filterFlag = collectRepeated(logsFlag.remaining, "--filter");
|
|
5885
7481
|
if (filterFlag.error) {
|
|
5886
7482
|
io2.stderr(`${filterFlag.error}
|
|
@@ -5932,7 +7528,7 @@ async function runInspectCmd(io2, argv) {
|
|
|
5932
7528
|
...d.remedy ? { remedy: d.remedy } : {}
|
|
5933
7529
|
});
|
|
5934
7530
|
}
|
|
5935
|
-
if (!
|
|
7531
|
+
if (!json) {
|
|
5936
7532
|
const model = typeof header.model === "string" ? ` \xB7 ${header.model}` : "";
|
|
5937
7533
|
const created = header.createdAt ? ` \xB7 ${header.createdAt}` : "";
|
|
5938
7534
|
io2.stdout(`session ${sessionId} \xB7 ${header.status}${model}${created}
|
|
@@ -5958,7 +7554,7 @@ async function runInspectCmd(io2, argv) {
|
|
|
5958
7554
|
runErrorEvent = e;
|
|
5959
7555
|
if (filters.predicate && !filters.predicate(e))
|
|
5960
7556
|
continue;
|
|
5961
|
-
if (
|
|
7557
|
+
if (json) {
|
|
5962
7558
|
if (logsFlag.present || e.channel !== "log")
|
|
5963
7559
|
collected.push(e);
|
|
5964
7560
|
continue;
|
|
@@ -5988,7 +7584,7 @@ async function runInspectCmd(io2, argv) {
|
|
|
5988
7584
|
finalSession = await operations_exports.getSession(http, sessionId);
|
|
5989
7585
|
} catch {
|
|
5990
7586
|
}
|
|
5991
|
-
if (
|
|
7587
|
+
if (json) {
|
|
5992
7588
|
io2.stdout(JSON.stringify({ session: finalSession, events: collected }) + "\n");
|
|
5993
7589
|
} else {
|
|
5994
7590
|
if (runErrorEvent) {
|
|
@@ -6039,6 +7635,11 @@ async function dispatch(io2, args) {
|
|
|
6039
7635
|
}
|
|
6040
7636
|
const sub = args[0];
|
|
6041
7637
|
const rest = args.slice(1);
|
|
7638
|
+
const spec = sub === void 0 ? void 0 : findVerbSpec(sub);
|
|
7639
|
+
if (spec && wantsVerbHelp(rest) && !(sub === "outputs" && rest[0] === "sync")) {
|
|
7640
|
+
io2.stdout(renderVerbHelp(spec));
|
|
7641
|
+
return SUCCESS;
|
|
7642
|
+
}
|
|
6042
7643
|
switch (sub) {
|
|
6043
7644
|
case "run":
|
|
6044
7645
|
return runRunCmd(io2, rest);
|
|
@@ -6101,7 +7702,7 @@ async function dispatch(io2, args) {
|
|
|
6101
7702
|
}
|
|
6102
7703
|
}
|
|
6103
7704
|
async function printGlobalHelp(io2) {
|
|
6104
|
-
io2.stdout("aex \u2014 unified CLI for the aex platform (
|
|
7705
|
+
io2.stdout("aex \u2014 unified CLI for the aex platform (a thin pass-through over the SDK)\n\n");
|
|
6105
7706
|
io2.stdout("Usage:\n");
|
|
6106
7707
|
io2.stdout(" aex run --config <run.json> --<provider>-api-key K --api-key T [flags]\n");
|
|
6107
7708
|
io2.stdout(" aex run --model M --prompt P [--system S] [--mcp name=url ...] --<provider>-api-key K --api-key T [flags]\n");
|
|
@@ -6173,7 +7774,7 @@ async function walkDirectory(root) {
|
|
|
6173
7774
|
async function visit(dir) {
|
|
6174
7775
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
6175
7776
|
for (const entry of entries) {
|
|
6176
|
-
const full =
|
|
7777
|
+
const full = resolvePath5(dir, entry.name);
|
|
6177
7778
|
if (entry.isDirectory()) {
|
|
6178
7779
|
await visit(full);
|
|
6179
7780
|
} else if (entry.isFile()) {
|