@asteroidai/cli 1.9.115 → 1.9.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/asteroid.js +333 -29
- package/dist/core.js +340 -28
- package/package.json +3 -3
package/dist/asteroid.js
CHANGED
|
@@ -1946,6 +1946,34 @@ var COMMANDS = [
|
|
|
1946
1946
|
destructive: false,
|
|
1947
1947
|
capability: "run_execution"
|
|
1948
1948
|
},
|
|
1949
|
+
{
|
|
1950
|
+
command: "execution sync",
|
|
1951
|
+
tool: "execution_sync",
|
|
1952
|
+
title: "Wait for an execution to finish",
|
|
1953
|
+
description: "Wait up to `timeoutSeconds` (1-25, default 25) for a running execution to finish. Returns the terminal `status` and `executionResult` when it finishes in time. Otherwise returns the current status: call it again to keep waiting. A paused or `awaiting_confirmation` execution returns at once, since it needs a person. Use it after `workflow_execute_post`, or after `workflow_execute_sync_post` timed out, instead of polling `execution_get`.",
|
|
1954
|
+
method: "GET",
|
|
1955
|
+
path: "/agents/v2/executions/{executionId}/sync",
|
|
1956
|
+
args: [
|
|
1957
|
+
{
|
|
1958
|
+
name: "executionId",
|
|
1959
|
+
flag: "execution-id",
|
|
1960
|
+
type: "string",
|
|
1961
|
+
required: true
|
|
1962
|
+
}
|
|
1963
|
+
],
|
|
1964
|
+
query: [
|
|
1965
|
+
{
|
|
1966
|
+
name: "timeoutSeconds",
|
|
1967
|
+
flag: "timeout-seconds",
|
|
1968
|
+
type: "integer",
|
|
1969
|
+
required: false,
|
|
1970
|
+
description: "Seconds to wait for the execution to finish before returning 202. Defaults to 25."
|
|
1971
|
+
}
|
|
1972
|
+
],
|
|
1973
|
+
binary: false,
|
|
1974
|
+
readOnly: true,
|
|
1975
|
+
destructive: false
|
|
1976
|
+
},
|
|
1949
1977
|
{
|
|
1950
1978
|
command: "insight get",
|
|
1951
1979
|
tool: "insight_get",
|
|
@@ -6200,6 +6228,85 @@ var COMMANDS = [
|
|
|
6200
6228
|
}
|
|
6201
6229
|
];
|
|
6202
6230
|
|
|
6231
|
+
// src/sync/types.ts
|
|
6232
|
+
var MANIFEST = ".asteroid.json";
|
|
6233
|
+
function get(path, query = {}) {
|
|
6234
|
+
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
6235
|
+
}
|
|
6236
|
+
function statusOf(error) {
|
|
6237
|
+
const status = error?.status;
|
|
6238
|
+
return typeof status === "number" ? status : void 0;
|
|
6239
|
+
}
|
|
6240
|
+
|
|
6241
|
+
// src/sync/execution-wait.ts
|
|
6242
|
+
var WAIT_TIMEOUT_SECONDS = 600;
|
|
6243
|
+
var WAIT_INTERVAL_SECONDS = 15;
|
|
6244
|
+
var MAX_WAIT_TIMEOUT_SECONDS = 3600;
|
|
6245
|
+
var MAX_WAIT_INTERVAL_SECONDS = 120;
|
|
6246
|
+
var TERMINAL = /* @__PURE__ */ new Set(["completed", "cancelled", "failed"]);
|
|
6247
|
+
function isTerminalExecutionStatus(status) {
|
|
6248
|
+
return TERMINAL.has(status);
|
|
6249
|
+
}
|
|
6250
|
+
var SYNC_TIMEOUT_SECONDS = 25;
|
|
6251
|
+
function pause(ms) {
|
|
6252
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
6253
|
+
}
|
|
6254
|
+
async function readStatus(sync, executionId2, timeoutMs) {
|
|
6255
|
+
if (timeoutMs <= 0)
|
|
6256
|
+
return await sync.remote.json(
|
|
6257
|
+
get(`/agents/v2/executions/${executionId2}`)
|
|
6258
|
+
);
|
|
6259
|
+
const timeoutSeconds = Math.min(
|
|
6260
|
+
SYNC_TIMEOUT_SECONDS,
|
|
6261
|
+
Math.max(1, Math.ceil(timeoutMs / 1e3))
|
|
6262
|
+
);
|
|
6263
|
+
const response = await sync.remote.json(
|
|
6264
|
+
get(`/agents/v2/executions/${executionId2}/sync`, {
|
|
6265
|
+
timeoutSeconds: String(timeoutSeconds)
|
|
6266
|
+
})
|
|
6267
|
+
);
|
|
6268
|
+
return {
|
|
6269
|
+
id: response.executionId,
|
|
6270
|
+
status: response.status,
|
|
6271
|
+
executionResult: response.executionResult
|
|
6272
|
+
};
|
|
6273
|
+
}
|
|
6274
|
+
async function waitForExecution(sync, options) {
|
|
6275
|
+
const timeoutMs = (options.timeoutSeconds ?? WAIT_TIMEOUT_SECONDS) * 1e3;
|
|
6276
|
+
const intervalMs = (options.intervalSeconds ?? WAIT_INTERVAL_SECONDS) * 1e3;
|
|
6277
|
+
const now = options.now ?? Date.now;
|
|
6278
|
+
const sleep = options.sleep ?? pause;
|
|
6279
|
+
const started = now();
|
|
6280
|
+
let polls = 0;
|
|
6281
|
+
for (; ; ) {
|
|
6282
|
+
const called = now();
|
|
6283
|
+
const execution = await readStatus(
|
|
6284
|
+
sync,
|
|
6285
|
+
options.executionId,
|
|
6286
|
+
timeoutMs - (called - started)
|
|
6287
|
+
);
|
|
6288
|
+
polls++;
|
|
6289
|
+
const elapsed = now() - started;
|
|
6290
|
+
const waitedSeconds = Math.round(elapsed / 1e3);
|
|
6291
|
+
if (isTerminalExecutionStatus(execution.status))
|
|
6292
|
+
return { execution, terminal: true, waitedSeconds, polls };
|
|
6293
|
+
const remaining = timeoutMs - elapsed;
|
|
6294
|
+
if (remaining <= 0)
|
|
6295
|
+
return { execution, terminal: false, waitedSeconds, polls };
|
|
6296
|
+
const early = intervalMs - (now() - called);
|
|
6297
|
+
if (early > 0) await sleep(Math.min(early, remaining));
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
function formatExecutionWait(result) {
|
|
6301
|
+
const { execution } = result;
|
|
6302
|
+
if (!result.terminal) {
|
|
6303
|
+
return `Execution ${execution.id} still ${execution.status} after ${result.waitedSeconds}s. Wait again, or read what it has so far with \`asteroid execution pull ${execution.id}\`.`;
|
|
6304
|
+
}
|
|
6305
|
+
const line2 = `Execution ${execution.id} ${execution.status}.`;
|
|
6306
|
+
const outcome = execution.executionResult;
|
|
6307
|
+
return outcome ? `${line2} Result: ${outcome.outcome}: ${outcome.reasoning}` : line2;
|
|
6308
|
+
}
|
|
6309
|
+
|
|
6203
6310
|
// src/sync/commands.ts
|
|
6204
6311
|
function option(name, type, description, extra = {}) {
|
|
6205
6312
|
return { name, flag: name, type, required: false, description, ...extra };
|
|
@@ -6211,6 +6318,12 @@ var local = {
|
|
|
6211
6318
|
destructive: false,
|
|
6212
6319
|
local: true
|
|
6213
6320
|
};
|
|
6321
|
+
var executionId = {
|
|
6322
|
+
name: "executionId",
|
|
6323
|
+
flag: "execution-id",
|
|
6324
|
+
type: "string",
|
|
6325
|
+
required: true
|
|
6326
|
+
};
|
|
6214
6327
|
var SYNC_COMMANDS = [
|
|
6215
6328
|
{
|
|
6216
6329
|
...local,
|
|
@@ -6219,14 +6332,7 @@ var SYNC_COMMANDS = [
|
|
|
6219
6332
|
title: "Pull an execution to disk",
|
|
6220
6333
|
description: "Write an execution to `<workflow-name>/executions/<id>/`: summary.json, activities.md (the step-by-step log), and the files the run produced.",
|
|
6221
6334
|
readOnly: true,
|
|
6222
|
-
args: [
|
|
6223
|
-
{
|
|
6224
|
-
name: "executionId",
|
|
6225
|
-
flag: "execution-id",
|
|
6226
|
-
type: "string",
|
|
6227
|
-
required: true
|
|
6228
|
-
}
|
|
6229
|
-
],
|
|
6335
|
+
args: [executionId],
|
|
6230
6336
|
query: [
|
|
6231
6337
|
option(
|
|
6232
6338
|
"files",
|
|
@@ -6252,6 +6358,54 @@ var SYNC_COMMANDS = [
|
|
|
6252
6358
|
"Write here instead of `<workflow-name>/executions/<id>/` under the current directory."
|
|
6253
6359
|
)
|
|
6254
6360
|
]
|
|
6361
|
+
},
|
|
6362
|
+
{
|
|
6363
|
+
...local,
|
|
6364
|
+
command: "execution wait",
|
|
6365
|
+
tool: "execution_wait",
|
|
6366
|
+
title: "Wait for an execution to end",
|
|
6367
|
+
description: "Poll an execution until it is completed, failed or cancelled, then print its status and, when the run produced one, its result. When the timeout passes first, print the status it still has, so you can wait again.",
|
|
6368
|
+
readOnly: true,
|
|
6369
|
+
args: [executionId],
|
|
6370
|
+
query: [
|
|
6371
|
+
option(
|
|
6372
|
+
"timeout",
|
|
6373
|
+
"integer",
|
|
6374
|
+
`Give up after this many seconds, ${WAIT_TIMEOUT_SECONDS} unless set, ${MAX_WAIT_TIMEOUT_SECONDS} at most.`
|
|
6375
|
+
),
|
|
6376
|
+
option(
|
|
6377
|
+
"interval",
|
|
6378
|
+
"integer",
|
|
6379
|
+
`Poll every this many seconds, ${WAIT_INTERVAL_SECONDS} unless set, ${MAX_WAIT_INTERVAL_SECONDS} at most.`
|
|
6380
|
+
)
|
|
6381
|
+
]
|
|
6382
|
+
},
|
|
6383
|
+
{
|
|
6384
|
+
...local,
|
|
6385
|
+
command: "insight pull",
|
|
6386
|
+
tool: "insight_pull",
|
|
6387
|
+
title: "Pull insights to disk",
|
|
6388
|
+
description: "Write insights to `insights/<name>.md`, one markdown file each with YAML frontmatter (id, type, status, name, next_steps). Name an insight to pull that one. A file another insight or an unpushed draft already holds keeps its name; the pulled one gets a -2, -3 suffix.",
|
|
6389
|
+
readOnly: true,
|
|
6390
|
+
args: [
|
|
6391
|
+
{
|
|
6392
|
+
name: "insightId",
|
|
6393
|
+
flag: "insight-id",
|
|
6394
|
+
type: "string",
|
|
6395
|
+
required: false,
|
|
6396
|
+
description: "One insight to pull. Without it, every insight, or one workflow's with --workflow-id."
|
|
6397
|
+
}
|
|
6398
|
+
],
|
|
6399
|
+
query: [
|
|
6400
|
+
option("workflowId", "string", "Only this workflow's insights.", {
|
|
6401
|
+
flag: "workflow-id"
|
|
6402
|
+
}),
|
|
6403
|
+
option(
|
|
6404
|
+
"dir",
|
|
6405
|
+
"string",
|
|
6406
|
+
"Write here instead of `insights/` under the current directory."
|
|
6407
|
+
)
|
|
6408
|
+
]
|
|
6255
6409
|
}
|
|
6256
6410
|
];
|
|
6257
6411
|
|
|
@@ -6710,15 +6864,16 @@ function defineCommands(root, options, dotted2 = /* @__PURE__ */ new Map(), onIn
|
|
|
6710
6864
|
(arg) => options.defaultArg?.(command, arg)
|
|
6711
6865
|
);
|
|
6712
6866
|
const defaulted = defaults[0] !== void 0;
|
|
6867
|
+
const optional = (arg, i) => i === 0 && defaulted || !arg.required;
|
|
6713
6868
|
command.args.forEach((arg) => {
|
|
6714
6869
|
leaf.argument(
|
|
6715
|
-
defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6870
|
+
defaulted || !arg.required ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6716
6871
|
arg.description ?? ""
|
|
6717
6872
|
);
|
|
6718
6873
|
});
|
|
6719
6874
|
if (defaulted) {
|
|
6720
6875
|
const names = command.args.map(
|
|
6721
|
-
(arg, i) => i
|
|
6876
|
+
(arg, i) => optional(arg, i) ? `[${arg.flag}]` : `<${arg.flag}>`
|
|
6722
6877
|
);
|
|
6723
6878
|
leaf.usage(`[options] ${names.join(" ")}`);
|
|
6724
6879
|
}
|
|
@@ -6735,8 +6890,9 @@ ${notes.join("\n")}`);
|
|
|
6735
6890
|
if (positionals.length === command.args.length - 1 && defaults[0] !== void 0) {
|
|
6736
6891
|
positionals = [defaults[0], ...positionals];
|
|
6737
6892
|
}
|
|
6738
|
-
|
|
6739
|
-
|
|
6893
|
+
const required = command.args.filter((arg) => arg.required).length;
|
|
6894
|
+
if (positionals.length < required || positionals.length > command.args.length) {
|
|
6895
|
+
const expected = command.args.map((a, i) => optional(a, i) ? `[${a.flag}]` : `<${a.flag}>`).join(" ");
|
|
6740
6896
|
throw new UsageError(
|
|
6741
6897
|
`expected ${expected}, got ${cmd.args.length} argument(s)`
|
|
6742
6898
|
);
|
|
@@ -7258,7 +7414,7 @@ var ApiError = class extends Error {
|
|
|
7258
7414
|
status;
|
|
7259
7415
|
body;
|
|
7260
7416
|
};
|
|
7261
|
-
var VERSION = "1.9.
|
|
7417
|
+
var VERSION = "1.9.122";
|
|
7262
7418
|
function problem(status, body, fallback) {
|
|
7263
7419
|
if (body && typeof body === "object") {
|
|
7264
7420
|
const record2 = body;
|
|
@@ -8070,6 +8226,13 @@ function normalizeNodeName(name) {
|
|
|
8070
8226
|
}
|
|
8071
8227
|
return result;
|
|
8072
8228
|
}
|
|
8229
|
+
function lossySlugOr(name, fallback) {
|
|
8230
|
+
try {
|
|
8231
|
+
return normalizeNodeName(name);
|
|
8232
|
+
} catch {
|
|
8233
|
+
return fallback;
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8073
8236
|
function nodeDirName(name) {
|
|
8074
8237
|
if (isNameCharsetConforming(name)) {
|
|
8075
8238
|
return name.replaceAll(" ", "_");
|
|
@@ -8083,8 +8246,8 @@ function agentDirName(name) {
|
|
|
8083
8246
|
return "agent";
|
|
8084
8247
|
}
|
|
8085
8248
|
}
|
|
8086
|
-
function executionDirName(
|
|
8087
|
-
return
|
|
8249
|
+
function executionDirName(executionId2) {
|
|
8250
|
+
return executionId2.slice(0, 8);
|
|
8088
8251
|
}
|
|
8089
8252
|
function workflowDirName(workflowId, version, parentId, parentVersion) {
|
|
8090
8253
|
const short = workflowId.slice(0, 8);
|
|
@@ -8132,16 +8295,6 @@ function matchesGlob(path, pattern) {
|
|
|
8132
8295
|
return new RegExp(`^${source}$`).test(path);
|
|
8133
8296
|
}
|
|
8134
8297
|
|
|
8135
|
-
// src/sync/types.ts
|
|
8136
|
-
var MANIFEST = ".asteroid.json";
|
|
8137
|
-
function get(path, query = {}) {
|
|
8138
|
-
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
8139
|
-
}
|
|
8140
|
-
function statusOf(error) {
|
|
8141
|
-
const status = error?.status;
|
|
8142
|
-
return typeof status === "number" ? status : void 0;
|
|
8143
|
-
}
|
|
8144
|
-
|
|
8145
8298
|
// src/sync/execution-pull.ts
|
|
8146
8299
|
var DIRECTORIES = ["workspace", "downloads", "shared"];
|
|
8147
8300
|
function relativeFilePath(file) {
|
|
@@ -8179,14 +8332,14 @@ async function writeActivities(sync, dir, execution) {
|
|
|
8179
8332
|
return false;
|
|
8180
8333
|
}
|
|
8181
8334
|
}
|
|
8182
|
-
async function listFiles(sync,
|
|
8335
|
+
async function listFiles(sync, executionId2) {
|
|
8183
8336
|
const listing = await sync.remote.json(
|
|
8184
|
-
get(`/agents/v2/executions/${
|
|
8337
|
+
get(`/agents/v2/executions/${executionId2}/files`)
|
|
8185
8338
|
);
|
|
8186
8339
|
return DIRECTORIES.flatMap((name) => listing.directories[name] ?? []);
|
|
8187
8340
|
}
|
|
8188
|
-
async function download(sync,
|
|
8189
|
-
const path = `/agents/v2/executions/${
|
|
8341
|
+
async function download(sync, executionId2, file) {
|
|
8342
|
+
const path = `/agents/v2/executions/${executionId2}/files/${file.id}/signed-url`;
|
|
8190
8343
|
const { url } = await sync.remote.json(get(path));
|
|
8191
8344
|
return sync.remote.download(url);
|
|
8192
8345
|
}
|
|
@@ -8314,6 +8467,114 @@ ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
|
|
|
8314
8467
|
].filter(Boolean).join("\n");
|
|
8315
8468
|
}
|
|
8316
8469
|
|
|
8470
|
+
// src/sync/insight-file.ts
|
|
8471
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
8472
|
+
var FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)(?:\r?\n)?/;
|
|
8473
|
+
function serializeInsightFile(meta, body) {
|
|
8474
|
+
const front = {};
|
|
8475
|
+
if (meta.id) front.id = meta.id;
|
|
8476
|
+
front.type = meta.type;
|
|
8477
|
+
if (meta.status) front.status = meta.status;
|
|
8478
|
+
front.name = meta.name;
|
|
8479
|
+
if (meta.nextSteps) front.next_steps = meta.nextSteps;
|
|
8480
|
+
const yaml = stringifyYaml(front).trimEnd();
|
|
8481
|
+
return `---
|
|
8482
|
+
${yaml}
|
|
8483
|
+
---
|
|
8484
|
+
|
|
8485
|
+
${body}`;
|
|
8486
|
+
}
|
|
8487
|
+
function insightFileId(text3) {
|
|
8488
|
+
const match = FRONTMATTER.exec(text3);
|
|
8489
|
+
if (!match) return void 0;
|
|
8490
|
+
try {
|
|
8491
|
+
const id = parseYaml(match[1])?.id;
|
|
8492
|
+
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
8493
|
+
} catch {
|
|
8494
|
+
return void 0;
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
|
|
8498
|
+
// src/sync/insight-pull.ts
|
|
8499
|
+
var INSIGHTS_DIR = "insights";
|
|
8500
|
+
var PAGE_SIZE = 100;
|
|
8501
|
+
async function listInsights(sync, workflowId) {
|
|
8502
|
+
const all = [];
|
|
8503
|
+
for (let page = 1; ; page++) {
|
|
8504
|
+
const query = {
|
|
8505
|
+
page: String(page),
|
|
8506
|
+
pageSize: String(PAGE_SIZE)
|
|
8507
|
+
};
|
|
8508
|
+
if (workflowId) query.workflowId = workflowId;
|
|
8509
|
+
const { items, total } = await sync.remote.json(
|
|
8510
|
+
get("/astro/v2/insights", query)
|
|
8511
|
+
);
|
|
8512
|
+
all.push(...items);
|
|
8513
|
+
if (items.length === 0 || all.length >= total) return all;
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8516
|
+
async function stemOwners(sync, dir) {
|
|
8517
|
+
const owners = /* @__PURE__ */ new Map();
|
|
8518
|
+
for (const name of await sync.files.list(dir)) {
|
|
8519
|
+
if (!name.endsWith(".md") || name.includes("/")) continue;
|
|
8520
|
+
const bytes = await sync.files.read(join3(dir, name));
|
|
8521
|
+
const text3 = bytes ? Buffer.from(bytes).toString("utf8") : "";
|
|
8522
|
+
owners.set(name.slice(0, -".md".length).toLowerCase(), insightFileId(text3));
|
|
8523
|
+
}
|
|
8524
|
+
return owners;
|
|
8525
|
+
}
|
|
8526
|
+
function assignStems(owners, insights) {
|
|
8527
|
+
const used = /* @__PURE__ */ new Set();
|
|
8528
|
+
const heldByOther = (stem, id) => owners.has(stem) && owners.get(stem) !== id;
|
|
8529
|
+
return insights.map((insight) => {
|
|
8530
|
+
const base = lossySlugOr(insight.name, "insight");
|
|
8531
|
+
let stem = base;
|
|
8532
|
+
for (let n = 2; used.has(stem) || heldByOther(stem, insight.id); n++) {
|
|
8533
|
+
stem = `${base}-${n}`;
|
|
8534
|
+
}
|
|
8535
|
+
used.add(stem);
|
|
8536
|
+
return [insight, stem];
|
|
8537
|
+
});
|
|
8538
|
+
}
|
|
8539
|
+
async function pullInsights(sync, options = {}) {
|
|
8540
|
+
const dir = options.dir ?? INSIGHTS_DIR;
|
|
8541
|
+
const insights = options.insightId ? [
|
|
8542
|
+
await sync.remote.json(
|
|
8543
|
+
get(`/astro/v2/insights/${options.insightId}`)
|
|
8544
|
+
)
|
|
8545
|
+
] : await listInsights(sync, options.workflowId);
|
|
8546
|
+
const pulled = [];
|
|
8547
|
+
for (const [insight, stem] of assignStems(
|
|
8548
|
+
await stemOwners(sync, dir),
|
|
8549
|
+
insights
|
|
8550
|
+
)) {
|
|
8551
|
+
const path = join3(dir, `${stem}.md`);
|
|
8552
|
+
const text3 = serializeInsightFile(
|
|
8553
|
+
{
|
|
8554
|
+
id: insight.id,
|
|
8555
|
+
type: insight.type,
|
|
8556
|
+
status: insight.status,
|
|
8557
|
+
name: insight.name,
|
|
8558
|
+
nextSteps: insight.nextSteps ?? void 0
|
|
8559
|
+
},
|
|
8560
|
+
insight.content
|
|
8561
|
+
);
|
|
8562
|
+
await sync.files.write(path, Buffer.from(text3, "utf8"));
|
|
8563
|
+
const { id, name, type, status } = insight;
|
|
8564
|
+
pulled.push({ id, name, type, status, path });
|
|
8565
|
+
}
|
|
8566
|
+
return { dir, insights: pulled };
|
|
8567
|
+
}
|
|
8568
|
+
function formatInsightPull(result) {
|
|
8569
|
+
const count = result.insights.length;
|
|
8570
|
+
return [
|
|
8571
|
+
`Pulled ${count} insight${count === 1 ? "" : "s"} into ${result.dir}/.`,
|
|
8572
|
+
...result.insights.map(
|
|
8573
|
+
(insight) => ` - ${insight.path}: ${insight.name} (${insight.type}, ${insight.status})`
|
|
8574
|
+
)
|
|
8575
|
+
].join("\n");
|
|
8576
|
+
}
|
|
8577
|
+
|
|
8317
8578
|
// src/sync/node-files.ts
|
|
8318
8579
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
8319
8580
|
import { dirname as dirname2, relative, resolve, sep } from "node:path";
|
|
@@ -8726,6 +8987,35 @@ function integer(parsed, name) {
|
|
|
8726
8987
|
throw new UsageError(`--${name} must be a whole number`);
|
|
8727
8988
|
return Number(raw);
|
|
8728
8989
|
}
|
|
8990
|
+
function bounded(parsed, name, min, max) {
|
|
8991
|
+
const value2 = integer(parsed, name);
|
|
8992
|
+
if (value2 === void 0) return void 0;
|
|
8993
|
+
if (value2 < min || value2 > max)
|
|
8994
|
+
throw new UsageError(`--${name} must be between ${min} and ${max}`);
|
|
8995
|
+
return value2;
|
|
8996
|
+
}
|
|
8997
|
+
function executionWaitOptions(parsed) {
|
|
8998
|
+
return {
|
|
8999
|
+
executionId: parsed.positionals[0],
|
|
9000
|
+
timeoutSeconds: bounded(parsed, "timeout", 0, MAX_WAIT_TIMEOUT_SECONDS),
|
|
9001
|
+
intervalSeconds: bounded(parsed, "interval", 1, MAX_WAIT_INTERVAL_SECONDS)
|
|
9002
|
+
};
|
|
9003
|
+
}
|
|
9004
|
+
function given(value2, name) {
|
|
9005
|
+
if (value2 === void 0) return void 0;
|
|
9006
|
+
if (value2 === "") throw new UsageError(`${name} must not be empty`);
|
|
9007
|
+
return value2;
|
|
9008
|
+
}
|
|
9009
|
+
function insightPullOptions(parsed) {
|
|
9010
|
+
const options = {};
|
|
9011
|
+
const insightId = given(parsed.positionals[0], "<insight-id>");
|
|
9012
|
+
if (insightId) options.insightId = insightId;
|
|
9013
|
+
const workflowId = given(flag(parsed, "workflow-id"), "--workflow-id");
|
|
9014
|
+
if (workflowId) options.workflowId = workflowId;
|
|
9015
|
+
const dir = given(flag(parsed, "dir"), "--dir");
|
|
9016
|
+
if (dir) options.dir = dir;
|
|
9017
|
+
return options;
|
|
9018
|
+
}
|
|
8729
9019
|
function executionOptions(parsed) {
|
|
8730
9020
|
const summaryOnly = flag(parsed, "summary-only") === "true";
|
|
8731
9021
|
const glob = flag(parsed, "files");
|
|
@@ -8787,6 +9077,20 @@ async function execute(command, parsed, sync, deps) {
|
|
|
8787
9077
|
);
|
|
8788
9078
|
return { result: result2, text: formatWorkflowPush(result2) };
|
|
8789
9079
|
}
|
|
9080
|
+
if (command.command === "execution wait") {
|
|
9081
|
+
const result2 = await waitForExecution(sync, {
|
|
9082
|
+
...executionWaitOptions(parsed),
|
|
9083
|
+
...deps.executionWait
|
|
9084
|
+
});
|
|
9085
|
+
return { result: result2, text: formatExecutionWait(result2) };
|
|
9086
|
+
}
|
|
9087
|
+
if (command.command === "insight pull") {
|
|
9088
|
+
const result2 = await pullInsights(sync, {
|
|
9089
|
+
...deps.insightPull,
|
|
9090
|
+
...insightPullOptions(parsed)
|
|
9091
|
+
});
|
|
9092
|
+
return { result: result2, text: formatInsightPull(result2) };
|
|
9093
|
+
}
|
|
8790
9094
|
if (command.command !== "execution pull")
|
|
8791
9095
|
throw new UsageError(`unknown command ${command.command}`);
|
|
8792
9096
|
const result = await pullExecution(sync, {
|
package/dist/core.js
CHANGED
|
@@ -1967,6 +1967,34 @@ var COMMANDS = [
|
|
|
1967
1967
|
destructive: false,
|
|
1968
1968
|
capability: "run_execution"
|
|
1969
1969
|
},
|
|
1970
|
+
{
|
|
1971
|
+
command: "execution sync",
|
|
1972
|
+
tool: "execution_sync",
|
|
1973
|
+
title: "Wait for an execution to finish",
|
|
1974
|
+
description: "Wait up to `timeoutSeconds` (1-25, default 25) for a running execution to finish. Returns the terminal `status` and `executionResult` when it finishes in time. Otherwise returns the current status: call it again to keep waiting. A paused or `awaiting_confirmation` execution returns at once, since it needs a person. Use it after `workflow_execute_post`, or after `workflow_execute_sync_post` timed out, instead of polling `execution_get`.",
|
|
1975
|
+
method: "GET",
|
|
1976
|
+
path: "/agents/v2/executions/{executionId}/sync",
|
|
1977
|
+
args: [
|
|
1978
|
+
{
|
|
1979
|
+
name: "executionId",
|
|
1980
|
+
flag: "execution-id",
|
|
1981
|
+
type: "string",
|
|
1982
|
+
required: true
|
|
1983
|
+
}
|
|
1984
|
+
],
|
|
1985
|
+
query: [
|
|
1986
|
+
{
|
|
1987
|
+
name: "timeoutSeconds",
|
|
1988
|
+
flag: "timeout-seconds",
|
|
1989
|
+
type: "integer",
|
|
1990
|
+
required: false,
|
|
1991
|
+
description: "Seconds to wait for the execution to finish before returning 202. Defaults to 25."
|
|
1992
|
+
}
|
|
1993
|
+
],
|
|
1994
|
+
binary: false,
|
|
1995
|
+
readOnly: true,
|
|
1996
|
+
destructive: false
|
|
1997
|
+
},
|
|
1970
1998
|
{
|
|
1971
1999
|
command: "insight get",
|
|
1972
2000
|
tool: "insight_get",
|
|
@@ -6580,15 +6608,16 @@ function defineCommands(root, options, dotted2 = /* @__PURE__ */ new Map(), onIn
|
|
|
6580
6608
|
(arg) => options.defaultArg?.(command, arg)
|
|
6581
6609
|
);
|
|
6582
6610
|
const defaulted = defaults[0] !== void 0;
|
|
6611
|
+
const optional = (arg, i) => i === 0 && defaulted || !arg.required;
|
|
6583
6612
|
command.args.forEach((arg) => {
|
|
6584
6613
|
leaf.argument(
|
|
6585
|
-
defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6614
|
+
defaulted || !arg.required ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6586
6615
|
arg.description ?? ""
|
|
6587
6616
|
);
|
|
6588
6617
|
});
|
|
6589
6618
|
if (defaulted) {
|
|
6590
6619
|
const names = command.args.map(
|
|
6591
|
-
(arg, i) => i
|
|
6620
|
+
(arg, i) => optional(arg, i) ? `[${arg.flag}]` : `<${arg.flag}>`
|
|
6592
6621
|
);
|
|
6593
6622
|
leaf.usage(`[options] ${names.join(" ")}`);
|
|
6594
6623
|
}
|
|
@@ -6605,8 +6634,9 @@ ${notes.join("\n")}`);
|
|
|
6605
6634
|
if (positionals.length === command.args.length - 1 && defaults[0] !== void 0) {
|
|
6606
6635
|
positionals = [defaults[0], ...positionals];
|
|
6607
6636
|
}
|
|
6608
|
-
|
|
6609
|
-
|
|
6637
|
+
const required = command.args.filter((arg) => arg.required).length;
|
|
6638
|
+
if (positionals.length < required || positionals.length > command.args.length) {
|
|
6639
|
+
const expected = command.args.map((a, i) => optional(a, i) ? `[${a.flag}]` : `<${a.flag}>`).join(" ");
|
|
6610
6640
|
throw new UsageError(
|
|
6611
6641
|
`expected ${expected}, got ${cmd.args.length} argument(s)`
|
|
6612
6642
|
);
|
|
@@ -6904,6 +6934,85 @@ function rejectUnknown(flags) {
|
|
|
6904
6934
|
}
|
|
6905
6935
|
}
|
|
6906
6936
|
|
|
6937
|
+
// src/sync/types.ts
|
|
6938
|
+
var MANIFEST = ".asteroid.json";
|
|
6939
|
+
function get(path, query = {}) {
|
|
6940
|
+
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
6941
|
+
}
|
|
6942
|
+
function statusOf(error) {
|
|
6943
|
+
const status = error?.status;
|
|
6944
|
+
return typeof status === "number" ? status : void 0;
|
|
6945
|
+
}
|
|
6946
|
+
|
|
6947
|
+
// src/sync/execution-wait.ts
|
|
6948
|
+
var WAIT_TIMEOUT_SECONDS = 600;
|
|
6949
|
+
var WAIT_INTERVAL_SECONDS = 15;
|
|
6950
|
+
var MAX_WAIT_TIMEOUT_SECONDS = 3600;
|
|
6951
|
+
var MAX_WAIT_INTERVAL_SECONDS = 120;
|
|
6952
|
+
var TERMINAL = /* @__PURE__ */ new Set(["completed", "cancelled", "failed"]);
|
|
6953
|
+
function isTerminalExecutionStatus(status) {
|
|
6954
|
+
return TERMINAL.has(status);
|
|
6955
|
+
}
|
|
6956
|
+
var SYNC_TIMEOUT_SECONDS = 25;
|
|
6957
|
+
function pause(ms) {
|
|
6958
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
6959
|
+
}
|
|
6960
|
+
async function readStatus(sync, executionId2, timeoutMs) {
|
|
6961
|
+
if (timeoutMs <= 0)
|
|
6962
|
+
return await sync.remote.json(
|
|
6963
|
+
get(`/agents/v2/executions/${executionId2}`)
|
|
6964
|
+
);
|
|
6965
|
+
const timeoutSeconds = Math.min(
|
|
6966
|
+
SYNC_TIMEOUT_SECONDS,
|
|
6967
|
+
Math.max(1, Math.ceil(timeoutMs / 1e3))
|
|
6968
|
+
);
|
|
6969
|
+
const response = await sync.remote.json(
|
|
6970
|
+
get(`/agents/v2/executions/${executionId2}/sync`, {
|
|
6971
|
+
timeoutSeconds: String(timeoutSeconds)
|
|
6972
|
+
})
|
|
6973
|
+
);
|
|
6974
|
+
return {
|
|
6975
|
+
id: response.executionId,
|
|
6976
|
+
status: response.status,
|
|
6977
|
+
executionResult: response.executionResult
|
|
6978
|
+
};
|
|
6979
|
+
}
|
|
6980
|
+
async function waitForExecution(sync, options) {
|
|
6981
|
+
const timeoutMs = (options.timeoutSeconds ?? WAIT_TIMEOUT_SECONDS) * 1e3;
|
|
6982
|
+
const intervalMs = (options.intervalSeconds ?? WAIT_INTERVAL_SECONDS) * 1e3;
|
|
6983
|
+
const now = options.now ?? Date.now;
|
|
6984
|
+
const sleep = options.sleep ?? pause;
|
|
6985
|
+
const started = now();
|
|
6986
|
+
let polls = 0;
|
|
6987
|
+
for (; ; ) {
|
|
6988
|
+
const called = now();
|
|
6989
|
+
const execution = await readStatus(
|
|
6990
|
+
sync,
|
|
6991
|
+
options.executionId,
|
|
6992
|
+
timeoutMs - (called - started)
|
|
6993
|
+
);
|
|
6994
|
+
polls++;
|
|
6995
|
+
const elapsed = now() - started;
|
|
6996
|
+
const waitedSeconds = Math.round(elapsed / 1e3);
|
|
6997
|
+
if (isTerminalExecutionStatus(execution.status))
|
|
6998
|
+
return { execution, terminal: true, waitedSeconds, polls };
|
|
6999
|
+
const remaining = timeoutMs - elapsed;
|
|
7000
|
+
if (remaining <= 0)
|
|
7001
|
+
return { execution, terminal: false, waitedSeconds, polls };
|
|
7002
|
+
const early = intervalMs - (now() - called);
|
|
7003
|
+
if (early > 0) await sleep(Math.min(early, remaining));
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
function formatExecutionWait(result) {
|
|
7007
|
+
const { execution } = result;
|
|
7008
|
+
if (!result.terminal) {
|
|
7009
|
+
return `Execution ${execution.id} still ${execution.status} after ${result.waitedSeconds}s. Wait again, or read what it has so far with \`asteroid execution pull ${execution.id}\`.`;
|
|
7010
|
+
}
|
|
7011
|
+
const line2 = `Execution ${execution.id} ${execution.status}.`;
|
|
7012
|
+
const outcome = execution.executionResult;
|
|
7013
|
+
return outcome ? `${line2} Result: ${outcome.outcome}: ${outcome.reasoning}` : line2;
|
|
7014
|
+
}
|
|
7015
|
+
|
|
6907
7016
|
// src/sync/commands.ts
|
|
6908
7017
|
function option(name, type, description, extra = {}) {
|
|
6909
7018
|
return { name, flag: name, type, required: false, description, ...extra };
|
|
@@ -6915,6 +7024,12 @@ var local = {
|
|
|
6915
7024
|
destructive: false,
|
|
6916
7025
|
local: true
|
|
6917
7026
|
};
|
|
7027
|
+
var executionId = {
|
|
7028
|
+
name: "executionId",
|
|
7029
|
+
flag: "execution-id",
|
|
7030
|
+
type: "string",
|
|
7031
|
+
required: true
|
|
7032
|
+
};
|
|
6918
7033
|
var SYNC_COMMANDS = [
|
|
6919
7034
|
{
|
|
6920
7035
|
...local,
|
|
@@ -6923,14 +7038,7 @@ var SYNC_COMMANDS = [
|
|
|
6923
7038
|
title: "Pull an execution to disk",
|
|
6924
7039
|
description: "Write an execution to `<workflow-name>/executions/<id>/`: summary.json, activities.md (the step-by-step log), and the files the run produced.",
|
|
6925
7040
|
readOnly: true,
|
|
6926
|
-
args: [
|
|
6927
|
-
{
|
|
6928
|
-
name: "executionId",
|
|
6929
|
-
flag: "execution-id",
|
|
6930
|
-
type: "string",
|
|
6931
|
-
required: true
|
|
6932
|
-
}
|
|
6933
|
-
],
|
|
7041
|
+
args: [executionId],
|
|
6934
7042
|
query: [
|
|
6935
7043
|
option(
|
|
6936
7044
|
"files",
|
|
@@ -6956,6 +7064,54 @@ var SYNC_COMMANDS = [
|
|
|
6956
7064
|
"Write here instead of `<workflow-name>/executions/<id>/` under the current directory."
|
|
6957
7065
|
)
|
|
6958
7066
|
]
|
|
7067
|
+
},
|
|
7068
|
+
{
|
|
7069
|
+
...local,
|
|
7070
|
+
command: "execution wait",
|
|
7071
|
+
tool: "execution_wait",
|
|
7072
|
+
title: "Wait for an execution to end",
|
|
7073
|
+
description: "Poll an execution until it is completed, failed or cancelled, then print its status and, when the run produced one, its result. When the timeout passes first, print the status it still has, so you can wait again.",
|
|
7074
|
+
readOnly: true,
|
|
7075
|
+
args: [executionId],
|
|
7076
|
+
query: [
|
|
7077
|
+
option(
|
|
7078
|
+
"timeout",
|
|
7079
|
+
"integer",
|
|
7080
|
+
`Give up after this many seconds, ${WAIT_TIMEOUT_SECONDS} unless set, ${MAX_WAIT_TIMEOUT_SECONDS} at most.`
|
|
7081
|
+
),
|
|
7082
|
+
option(
|
|
7083
|
+
"interval",
|
|
7084
|
+
"integer",
|
|
7085
|
+
`Poll every this many seconds, ${WAIT_INTERVAL_SECONDS} unless set, ${MAX_WAIT_INTERVAL_SECONDS} at most.`
|
|
7086
|
+
)
|
|
7087
|
+
]
|
|
7088
|
+
},
|
|
7089
|
+
{
|
|
7090
|
+
...local,
|
|
7091
|
+
command: "insight pull",
|
|
7092
|
+
tool: "insight_pull",
|
|
7093
|
+
title: "Pull insights to disk",
|
|
7094
|
+
description: "Write insights to `insights/<name>.md`, one markdown file each with YAML frontmatter (id, type, status, name, next_steps). Name an insight to pull that one. A file another insight or an unpushed draft already holds keeps its name; the pulled one gets a -2, -3 suffix.",
|
|
7095
|
+
readOnly: true,
|
|
7096
|
+
args: [
|
|
7097
|
+
{
|
|
7098
|
+
name: "insightId",
|
|
7099
|
+
flag: "insight-id",
|
|
7100
|
+
type: "string",
|
|
7101
|
+
required: false,
|
|
7102
|
+
description: "One insight to pull. Without it, every insight, or one workflow's with --workflow-id."
|
|
7103
|
+
}
|
|
7104
|
+
],
|
|
7105
|
+
query: [
|
|
7106
|
+
option("workflowId", "string", "Only this workflow's insights.", {
|
|
7107
|
+
flag: "workflow-id"
|
|
7108
|
+
}),
|
|
7109
|
+
option(
|
|
7110
|
+
"dir",
|
|
7111
|
+
"string",
|
|
7112
|
+
"Write here instead of `insights/` under the current directory."
|
|
7113
|
+
)
|
|
7114
|
+
]
|
|
6959
7115
|
}
|
|
6960
7116
|
];
|
|
6961
7117
|
|
|
@@ -7068,6 +7224,13 @@ function normalizeNodeName(name) {
|
|
|
7068
7224
|
}
|
|
7069
7225
|
return result;
|
|
7070
7226
|
}
|
|
7227
|
+
function lossySlugOr(name, fallback) {
|
|
7228
|
+
try {
|
|
7229
|
+
return normalizeNodeName(name);
|
|
7230
|
+
} catch {
|
|
7231
|
+
return fallback;
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7071
7234
|
function nodeDirName(name) {
|
|
7072
7235
|
if (isNameCharsetConforming(name)) {
|
|
7073
7236
|
return name.replaceAll(" ", "_");
|
|
@@ -7081,8 +7244,8 @@ function agentDirName(name) {
|
|
|
7081
7244
|
return "agent";
|
|
7082
7245
|
}
|
|
7083
7246
|
}
|
|
7084
|
-
function executionDirName(
|
|
7085
|
-
return
|
|
7247
|
+
function executionDirName(executionId2) {
|
|
7248
|
+
return executionId2.slice(0, 8);
|
|
7086
7249
|
}
|
|
7087
7250
|
function workflowDirName(workflowId, version, parentId, parentVersion) {
|
|
7088
7251
|
const short = workflowId.slice(0, 8);
|
|
@@ -7130,16 +7293,6 @@ function matchesGlob(path, pattern) {
|
|
|
7130
7293
|
return new RegExp(`^${source}$`).test(path);
|
|
7131
7294
|
}
|
|
7132
7295
|
|
|
7133
|
-
// src/sync/types.ts
|
|
7134
|
-
var MANIFEST = ".asteroid.json";
|
|
7135
|
-
function get(path, query = {}) {
|
|
7136
|
-
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
7137
|
-
}
|
|
7138
|
-
function statusOf(error) {
|
|
7139
|
-
const status = error?.status;
|
|
7140
|
-
return typeof status === "number" ? status : void 0;
|
|
7141
|
-
}
|
|
7142
|
-
|
|
7143
7296
|
// src/sync/execution-pull.ts
|
|
7144
7297
|
var DIRECTORIES = ["workspace", "downloads", "shared"];
|
|
7145
7298
|
function relativeFilePath(file) {
|
|
@@ -7177,14 +7330,14 @@ async function writeActivities(sync, dir, execution) {
|
|
|
7177
7330
|
return false;
|
|
7178
7331
|
}
|
|
7179
7332
|
}
|
|
7180
|
-
async function listFiles(sync,
|
|
7333
|
+
async function listFiles(sync, executionId2) {
|
|
7181
7334
|
const listing = await sync.remote.json(
|
|
7182
|
-
get(`/agents/v2/executions/${
|
|
7335
|
+
get(`/agents/v2/executions/${executionId2}/files`)
|
|
7183
7336
|
);
|
|
7184
7337
|
return DIRECTORIES.flatMap((name) => listing.directories[name] ?? []);
|
|
7185
7338
|
}
|
|
7186
|
-
async function download(sync,
|
|
7187
|
-
const path = `/agents/v2/executions/${
|
|
7339
|
+
async function download(sync, executionId2, file) {
|
|
7340
|
+
const path = `/agents/v2/executions/${executionId2}/files/${file.id}/signed-url`;
|
|
7188
7341
|
const { url } = await sync.remote.json(get(path));
|
|
7189
7342
|
return sync.remote.download(url);
|
|
7190
7343
|
}
|
|
@@ -7312,6 +7465,114 @@ ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
|
|
|
7312
7465
|
].filter(Boolean).join("\n");
|
|
7313
7466
|
}
|
|
7314
7467
|
|
|
7468
|
+
// src/sync/insight-file.ts
|
|
7469
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
7470
|
+
var FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)(?:\r?\n)?/;
|
|
7471
|
+
function serializeInsightFile(meta, body) {
|
|
7472
|
+
const front = {};
|
|
7473
|
+
if (meta.id) front.id = meta.id;
|
|
7474
|
+
front.type = meta.type;
|
|
7475
|
+
if (meta.status) front.status = meta.status;
|
|
7476
|
+
front.name = meta.name;
|
|
7477
|
+
if (meta.nextSteps) front.next_steps = meta.nextSteps;
|
|
7478
|
+
const yaml = stringifyYaml(front).trimEnd();
|
|
7479
|
+
return `---
|
|
7480
|
+
${yaml}
|
|
7481
|
+
---
|
|
7482
|
+
|
|
7483
|
+
${body}`;
|
|
7484
|
+
}
|
|
7485
|
+
function insightFileId(text2) {
|
|
7486
|
+
const match = FRONTMATTER.exec(text2);
|
|
7487
|
+
if (!match) return void 0;
|
|
7488
|
+
try {
|
|
7489
|
+
const id = parseYaml(match[1])?.id;
|
|
7490
|
+
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
7491
|
+
} catch {
|
|
7492
|
+
return void 0;
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7496
|
+
// src/sync/insight-pull.ts
|
|
7497
|
+
var INSIGHTS_DIR = "insights";
|
|
7498
|
+
var PAGE_SIZE = 100;
|
|
7499
|
+
async function listInsights(sync, workflowId) {
|
|
7500
|
+
const all = [];
|
|
7501
|
+
for (let page = 1; ; page++) {
|
|
7502
|
+
const query = {
|
|
7503
|
+
page: String(page),
|
|
7504
|
+
pageSize: String(PAGE_SIZE)
|
|
7505
|
+
};
|
|
7506
|
+
if (workflowId) query.workflowId = workflowId;
|
|
7507
|
+
const { items, total } = await sync.remote.json(
|
|
7508
|
+
get("/astro/v2/insights", query)
|
|
7509
|
+
);
|
|
7510
|
+
all.push(...items);
|
|
7511
|
+
if (items.length === 0 || all.length >= total) return all;
|
|
7512
|
+
}
|
|
7513
|
+
}
|
|
7514
|
+
async function stemOwners(sync, dir) {
|
|
7515
|
+
const owners = /* @__PURE__ */ new Map();
|
|
7516
|
+
for (const name of await sync.files.list(dir)) {
|
|
7517
|
+
if (!name.endsWith(".md") || name.includes("/")) continue;
|
|
7518
|
+
const bytes = await sync.files.read(join(dir, name));
|
|
7519
|
+
const text2 = bytes ? Buffer.from(bytes).toString("utf8") : "";
|
|
7520
|
+
owners.set(name.slice(0, -".md".length).toLowerCase(), insightFileId(text2));
|
|
7521
|
+
}
|
|
7522
|
+
return owners;
|
|
7523
|
+
}
|
|
7524
|
+
function assignStems(owners, insights) {
|
|
7525
|
+
const used = /* @__PURE__ */ new Set();
|
|
7526
|
+
const heldByOther = (stem, id) => owners.has(stem) && owners.get(stem) !== id;
|
|
7527
|
+
return insights.map((insight) => {
|
|
7528
|
+
const base = lossySlugOr(insight.name, "insight");
|
|
7529
|
+
let stem = base;
|
|
7530
|
+
for (let n = 2; used.has(stem) || heldByOther(stem, insight.id); n++) {
|
|
7531
|
+
stem = `${base}-${n}`;
|
|
7532
|
+
}
|
|
7533
|
+
used.add(stem);
|
|
7534
|
+
return [insight, stem];
|
|
7535
|
+
});
|
|
7536
|
+
}
|
|
7537
|
+
async function pullInsights(sync, options = {}) {
|
|
7538
|
+
const dir = options.dir ?? INSIGHTS_DIR;
|
|
7539
|
+
const insights = options.insightId ? [
|
|
7540
|
+
await sync.remote.json(
|
|
7541
|
+
get(`/astro/v2/insights/${options.insightId}`)
|
|
7542
|
+
)
|
|
7543
|
+
] : await listInsights(sync, options.workflowId);
|
|
7544
|
+
const pulled = [];
|
|
7545
|
+
for (const [insight, stem] of assignStems(
|
|
7546
|
+
await stemOwners(sync, dir),
|
|
7547
|
+
insights
|
|
7548
|
+
)) {
|
|
7549
|
+
const path = join(dir, `${stem}.md`);
|
|
7550
|
+
const text2 = serializeInsightFile(
|
|
7551
|
+
{
|
|
7552
|
+
id: insight.id,
|
|
7553
|
+
type: insight.type,
|
|
7554
|
+
status: insight.status,
|
|
7555
|
+
name: insight.name,
|
|
7556
|
+
nextSteps: insight.nextSteps ?? void 0
|
|
7557
|
+
},
|
|
7558
|
+
insight.content
|
|
7559
|
+
);
|
|
7560
|
+
await sync.files.write(path, Buffer.from(text2, "utf8"));
|
|
7561
|
+
const { id, name, type, status } = insight;
|
|
7562
|
+
pulled.push({ id, name, type, status, path });
|
|
7563
|
+
}
|
|
7564
|
+
return { dir, insights: pulled };
|
|
7565
|
+
}
|
|
7566
|
+
function formatInsightPull(result) {
|
|
7567
|
+
const count = result.insights.length;
|
|
7568
|
+
return [
|
|
7569
|
+
`Pulled ${count} insight${count === 1 ? "" : "s"} into ${result.dir}/.`,
|
|
7570
|
+
...result.insights.map(
|
|
7571
|
+
(insight) => ` - ${insight.path}: ${insight.name} (${insight.type}, ${insight.status})`
|
|
7572
|
+
)
|
|
7573
|
+
].join("\n");
|
|
7574
|
+
}
|
|
7575
|
+
|
|
7315
7576
|
// src/sync/manifest.ts
|
|
7316
7577
|
async function loadManifest(sync, dir) {
|
|
7317
7578
|
const bytes = await sync.files.read(join(dir, MANIFEST));
|
|
@@ -7724,6 +7985,35 @@ function integer(parsed, name) {
|
|
|
7724
7985
|
throw new UsageError(`--${name} must be a whole number`);
|
|
7725
7986
|
return Number(raw);
|
|
7726
7987
|
}
|
|
7988
|
+
function bounded(parsed, name, min, max) {
|
|
7989
|
+
const value2 = integer(parsed, name);
|
|
7990
|
+
if (value2 === void 0) return void 0;
|
|
7991
|
+
if (value2 < min || value2 > max)
|
|
7992
|
+
throw new UsageError(`--${name} must be between ${min} and ${max}`);
|
|
7993
|
+
return value2;
|
|
7994
|
+
}
|
|
7995
|
+
function executionWaitOptions(parsed) {
|
|
7996
|
+
return {
|
|
7997
|
+
executionId: parsed.positionals[0],
|
|
7998
|
+
timeoutSeconds: bounded(parsed, "timeout", 0, MAX_WAIT_TIMEOUT_SECONDS),
|
|
7999
|
+
intervalSeconds: bounded(parsed, "interval", 1, MAX_WAIT_INTERVAL_SECONDS)
|
|
8000
|
+
};
|
|
8001
|
+
}
|
|
8002
|
+
function given(value2, name) {
|
|
8003
|
+
if (value2 === void 0) return void 0;
|
|
8004
|
+
if (value2 === "") throw new UsageError(`${name} must not be empty`);
|
|
8005
|
+
return value2;
|
|
8006
|
+
}
|
|
8007
|
+
function insightPullOptions(parsed) {
|
|
8008
|
+
const options = {};
|
|
8009
|
+
const insightId = given(parsed.positionals[0], "<insight-id>");
|
|
8010
|
+
if (insightId) options.insightId = insightId;
|
|
8011
|
+
const workflowId = given(flag(parsed, "workflow-id"), "--workflow-id");
|
|
8012
|
+
if (workflowId) options.workflowId = workflowId;
|
|
8013
|
+
const dir = given(flag(parsed, "dir"), "--dir");
|
|
8014
|
+
if (dir) options.dir = dir;
|
|
8015
|
+
return options;
|
|
8016
|
+
}
|
|
7727
8017
|
function executionOptions(parsed) {
|
|
7728
8018
|
const summaryOnly = flag(parsed, "summary-only") === "true";
|
|
7729
8019
|
const glob = flag(parsed, "files");
|
|
@@ -7785,6 +8075,20 @@ async function execute(command, parsed, sync, deps) {
|
|
|
7785
8075
|
);
|
|
7786
8076
|
return { result: result2, text: formatWorkflowPush(result2) };
|
|
7787
8077
|
}
|
|
8078
|
+
if (command.command === "execution wait") {
|
|
8079
|
+
const result2 = await waitForExecution(sync, {
|
|
8080
|
+
...executionWaitOptions(parsed),
|
|
8081
|
+
...deps.executionWait
|
|
8082
|
+
});
|
|
8083
|
+
return { result: result2, text: formatExecutionWait(result2) };
|
|
8084
|
+
}
|
|
8085
|
+
if (command.command === "insight pull") {
|
|
8086
|
+
const result2 = await pullInsights(sync, {
|
|
8087
|
+
...deps.insightPull,
|
|
8088
|
+
...insightPullOptions(parsed)
|
|
8089
|
+
});
|
|
8090
|
+
return { result: result2, text: formatInsightPull(result2) };
|
|
8091
|
+
}
|
|
7788
8092
|
if (command.command !== "execution pull")
|
|
7789
8093
|
throw new UsageError(`unknown command ${command.command}`);
|
|
7790
8094
|
const result = await pullExecution(sync, {
|
|
@@ -7807,15 +8111,20 @@ async function runSync(command, parsed, deps) {
|
|
|
7807
8111
|
export {
|
|
7808
8112
|
ALL_COMMANDS,
|
|
7809
8113
|
COMMANDS,
|
|
8114
|
+
INSIGHTS_DIR,
|
|
7810
8115
|
MANIFEST,
|
|
7811
8116
|
SYNC_COMMANDS,
|
|
7812
8117
|
UsageError,
|
|
7813
8118
|
buildRequest,
|
|
7814
8119
|
formatActivitiesMarkdown,
|
|
7815
8120
|
formatExecutionPull,
|
|
8121
|
+
formatExecutionWait,
|
|
8122
|
+
formatInsightPull,
|
|
7816
8123
|
formatWorkflowPull,
|
|
7817
8124
|
formatWorkflowPush,
|
|
7818
8125
|
headFilesRequest,
|
|
8126
|
+
insightFileId,
|
|
8127
|
+
isTerminalExecutionStatus,
|
|
7819
8128
|
leadingWords,
|
|
7820
8129
|
loadManifest,
|
|
7821
8130
|
matchCommand,
|
|
@@ -7823,12 +8132,15 @@ export {
|
|
|
7823
8132
|
parseInvocation,
|
|
7824
8133
|
pullDir,
|
|
7825
8134
|
pullExecution,
|
|
8135
|
+
pullInsights,
|
|
7826
8136
|
pullWorkflow,
|
|
7827
8137
|
pushWorkflow,
|
|
7828
8138
|
readManifest,
|
|
7829
8139
|
render,
|
|
7830
8140
|
runSync,
|
|
7831
8141
|
runsLocally,
|
|
8142
|
+
serializeInsightFile,
|
|
8143
|
+
waitForExecution,
|
|
7832
8144
|
withSyncCommands,
|
|
7833
8145
|
writeManifest
|
|
7834
8146
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteroidai/cli",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.122",
|
|
4
4
|
"description": "The asteroid command-line interface for the Asteroid platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"cli-table3": "^0.6.5",
|
|
24
24
|
"commander": "^15.0.0",
|
|
25
25
|
"env-paths": "^4.0.0",
|
|
26
|
-
"proper-lockfile": "^4.1.2"
|
|
26
|
+
"proper-lockfile": "^4.1.2",
|
|
27
|
+
"yaml": "^2.9.0"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@types/node": "^22.20.1",
|
|
@@ -33,7 +34,6 @@
|
|
|
33
34
|
"tsx": "^4.23.12",
|
|
34
35
|
"typescript": "5.9.3",
|
|
35
36
|
"vitest": "^4.1.11",
|
|
36
|
-
"yaml": "^2.9.0",
|
|
37
37
|
"@asteroid/workflow-files": "0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|