@asteroidai/cli 1.9.111 → 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 +408 -51
- package/dist/core.js +415 -50
- 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",
|
|
@@ -2206,7 +2234,7 @@ var COMMANDS = [
|
|
|
2206
2234
|
command: "portal-skills add",
|
|
2207
2235
|
tool: "portal_skills_add",
|
|
2208
2236
|
title: "Add portal skills",
|
|
2209
|
-
description: "Add a portal's skills to an organization, by
|
|
2237
|
+
description: "Add a portal's skills to an organization, by the `portalId` of a portal skill from `skills_list`. Needs a Startup plan or above. Astro loads the portal's skills once it is added, and `skill_get` serves them. Adding one the organization already has is a no-op.",
|
|
2210
2238
|
method: "POST",
|
|
2211
2239
|
path: "/agents/v2/portal-skills/{portalId}",
|
|
2212
2240
|
args: [
|
|
@@ -2222,27 +2250,6 @@ var COMMANDS = [
|
|
|
2222
2250
|
readOnly: false,
|
|
2223
2251
|
destructive: false
|
|
2224
2252
|
},
|
|
2225
|
-
{
|
|
2226
|
-
command: "portal-skills list",
|
|
2227
|
-
tool: "portal_skills_list",
|
|
2228
|
-
title: "List portal skills",
|
|
2229
|
-
description: "List the skills for third-party portals, or only for the portals matching a portal name or task, such as `caloptima eligibility`. A portal skill is general knowledge about how a portal works: sign-in, page quirks, field mappings. Each portal comes with its skill names, and whether the organization has added it. Add one with `portal_skills_add`.",
|
|
2230
|
-
method: "GET",
|
|
2231
|
-
path: "/astro/v2/portal-skills",
|
|
2232
|
-
args: [],
|
|
2233
|
-
query: [
|
|
2234
|
-
{
|
|
2235
|
-
name: "query",
|
|
2236
|
-
flag: "query",
|
|
2237
|
-
type: "string",
|
|
2238
|
-
required: false,
|
|
2239
|
-
description: "A portal name or task, at least three characters. Omit to list every portal with skills."
|
|
2240
|
-
}
|
|
2241
|
-
],
|
|
2242
|
-
binary: false,
|
|
2243
|
-
readOnly: true,
|
|
2244
|
-
destructive: false
|
|
2245
|
-
},
|
|
2246
2253
|
{
|
|
2247
2254
|
command: "profile clear-cache",
|
|
2248
2255
|
tool: "profile_clear_browser_cache",
|
|
@@ -4018,6 +4025,80 @@ var COMMANDS = [
|
|
|
4018
4025
|
credentialOrganization: false
|
|
4019
4026
|
}
|
|
4020
4027
|
},
|
|
4028
|
+
{
|
|
4029
|
+
command: "skill get",
|
|
4030
|
+
tool: "skill_get",
|
|
4031
|
+
title: "Get skill",
|
|
4032
|
+
description: "Get one skill with the content of every file, SKILL.md first, in the Agent Skills format. A name resolves to the public skill, then a portal's, then the organization's; set `kind` to pick one. A portal skill needs its portal added with `portal_skills_add`. Pass `surface` `mcp` to spell the platform's commands as MCP tools.",
|
|
4033
|
+
method: "GET",
|
|
4034
|
+
path: "/astro/v2/skills/{name}",
|
|
4035
|
+
args: [
|
|
4036
|
+
{
|
|
4037
|
+
name: "name",
|
|
4038
|
+
flag: "name",
|
|
4039
|
+
type: "string",
|
|
4040
|
+
required: true
|
|
4041
|
+
}
|
|
4042
|
+
],
|
|
4043
|
+
query: [
|
|
4044
|
+
{
|
|
4045
|
+
name: "kind",
|
|
4046
|
+
flag: "kind",
|
|
4047
|
+
type: "string",
|
|
4048
|
+
required: false,
|
|
4049
|
+
description: "Only a skill of this kind.",
|
|
4050
|
+
enum: ["public", "portal", "organization"]
|
|
4051
|
+
},
|
|
4052
|
+
{
|
|
4053
|
+
name: "surface",
|
|
4054
|
+
flag: "surface",
|
|
4055
|
+
type: "string",
|
|
4056
|
+
required: false,
|
|
4057
|
+
description: "The surface the skill is rendered for. Defaults to `cli`.",
|
|
4058
|
+
enum: ["cli", "mcp"]
|
|
4059
|
+
}
|
|
4060
|
+
],
|
|
4061
|
+
binary: false,
|
|
4062
|
+
readOnly: true,
|
|
4063
|
+
destructive: false
|
|
4064
|
+
},
|
|
4065
|
+
{
|
|
4066
|
+
command: "skill list",
|
|
4067
|
+
tool: "skills_list",
|
|
4068
|
+
title: "List skills",
|
|
4069
|
+
description: "List the skills an organization can load, by kind then name: Asteroid's `public` skills, the `portal` skills (general knowledge of a third-party portal: sign-in, page quirks, field mappings) with whether the organization has added the portal, and the `organization`'s own. Filter with `kind`, or rank by a `query` such as `caloptima eligibility`. Each skill comes with its file paths and digests; `skill_get` returns the content. Add a portal with `portal_skills_add`.",
|
|
4070
|
+
method: "GET",
|
|
4071
|
+
path: "/astro/v2/skills",
|
|
4072
|
+
args: [],
|
|
4073
|
+
query: [
|
|
4074
|
+
{
|
|
4075
|
+
name: "kind",
|
|
4076
|
+
flag: "kind",
|
|
4077
|
+
type: "string",
|
|
4078
|
+
required: false,
|
|
4079
|
+
description: "Only skills of this kind.",
|
|
4080
|
+
enum: ["public", "portal", "organization"]
|
|
4081
|
+
},
|
|
4082
|
+
{
|
|
4083
|
+
name: "query",
|
|
4084
|
+
flag: "query",
|
|
4085
|
+
type: "string",
|
|
4086
|
+
required: false,
|
|
4087
|
+
description: "A skill name, portal name or task, at least three characters. Best matches first."
|
|
4088
|
+
},
|
|
4089
|
+
{
|
|
4090
|
+
name: "surface",
|
|
4091
|
+
flag: "surface",
|
|
4092
|
+
type: "string",
|
|
4093
|
+
required: false,
|
|
4094
|
+
description: "The surface the skills are rendered for. Defaults to `cli`.",
|
|
4095
|
+
enum: ["cli", "mcp"]
|
|
4096
|
+
}
|
|
4097
|
+
],
|
|
4098
|
+
binary: false,
|
|
4099
|
+
readOnly: true,
|
|
4100
|
+
destructive: false
|
|
4101
|
+
},
|
|
4021
4102
|
{
|
|
4022
4103
|
command: "tag create",
|
|
4023
4104
|
tool: "workflow_tags_create",
|
|
@@ -6147,6 +6228,85 @@ var COMMANDS = [
|
|
|
6147
6228
|
}
|
|
6148
6229
|
];
|
|
6149
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
|
+
|
|
6150
6310
|
// src/sync/commands.ts
|
|
6151
6311
|
function option(name, type, description, extra = {}) {
|
|
6152
6312
|
return { name, flag: name, type, required: false, description, ...extra };
|
|
@@ -6158,6 +6318,12 @@ var local = {
|
|
|
6158
6318
|
destructive: false,
|
|
6159
6319
|
local: true
|
|
6160
6320
|
};
|
|
6321
|
+
var executionId = {
|
|
6322
|
+
name: "executionId",
|
|
6323
|
+
flag: "execution-id",
|
|
6324
|
+
type: "string",
|
|
6325
|
+
required: true
|
|
6326
|
+
};
|
|
6161
6327
|
var SYNC_COMMANDS = [
|
|
6162
6328
|
{
|
|
6163
6329
|
...local,
|
|
@@ -6166,14 +6332,7 @@ var SYNC_COMMANDS = [
|
|
|
6166
6332
|
title: "Pull an execution to disk",
|
|
6167
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.",
|
|
6168
6334
|
readOnly: true,
|
|
6169
|
-
args: [
|
|
6170
|
-
{
|
|
6171
|
-
name: "executionId",
|
|
6172
|
-
flag: "execution-id",
|
|
6173
|
-
type: "string",
|
|
6174
|
-
required: true
|
|
6175
|
-
}
|
|
6176
|
-
],
|
|
6335
|
+
args: [executionId],
|
|
6177
6336
|
query: [
|
|
6178
6337
|
option(
|
|
6179
6338
|
"files",
|
|
@@ -6199,6 +6358,54 @@ var SYNC_COMMANDS = [
|
|
|
6199
6358
|
"Write here instead of `<workflow-name>/executions/<id>/` under the current directory."
|
|
6200
6359
|
)
|
|
6201
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
|
+
]
|
|
6202
6409
|
}
|
|
6203
6410
|
];
|
|
6204
6411
|
|
|
@@ -6657,15 +6864,16 @@ function defineCommands(root, options, dotted2 = /* @__PURE__ */ new Map(), onIn
|
|
|
6657
6864
|
(arg) => options.defaultArg?.(command, arg)
|
|
6658
6865
|
);
|
|
6659
6866
|
const defaulted = defaults[0] !== void 0;
|
|
6867
|
+
const optional = (arg, i) => i === 0 && defaulted || !arg.required;
|
|
6660
6868
|
command.args.forEach((arg) => {
|
|
6661
6869
|
leaf.argument(
|
|
6662
|
-
defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6870
|
+
defaulted || !arg.required ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6663
6871
|
arg.description ?? ""
|
|
6664
6872
|
);
|
|
6665
6873
|
});
|
|
6666
6874
|
if (defaulted) {
|
|
6667
6875
|
const names = command.args.map(
|
|
6668
|
-
(arg, i) => i
|
|
6876
|
+
(arg, i) => optional(arg, i) ? `[${arg.flag}]` : `<${arg.flag}>`
|
|
6669
6877
|
);
|
|
6670
6878
|
leaf.usage(`[options] ${names.join(" ")}`);
|
|
6671
6879
|
}
|
|
@@ -6682,8 +6890,9 @@ ${notes.join("\n")}`);
|
|
|
6682
6890
|
if (positionals.length === command.args.length - 1 && defaults[0] !== void 0) {
|
|
6683
6891
|
positionals = [defaults[0], ...positionals];
|
|
6684
6892
|
}
|
|
6685
|
-
|
|
6686
|
-
|
|
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(" ");
|
|
6687
6896
|
throw new UsageError(
|
|
6688
6897
|
`expected ${expected}, got ${cmd.args.length} argument(s)`
|
|
6689
6898
|
);
|
|
@@ -7205,7 +7414,7 @@ var ApiError = class extends Error {
|
|
|
7205
7414
|
status;
|
|
7206
7415
|
body;
|
|
7207
7416
|
};
|
|
7208
|
-
var VERSION = "1.9.
|
|
7417
|
+
var VERSION = "1.9.122";
|
|
7209
7418
|
function problem(status, body, fallback) {
|
|
7210
7419
|
if (body && typeof body === "object") {
|
|
7211
7420
|
const record2 = body;
|
|
@@ -8017,6 +8226,13 @@ function normalizeNodeName(name) {
|
|
|
8017
8226
|
}
|
|
8018
8227
|
return result;
|
|
8019
8228
|
}
|
|
8229
|
+
function lossySlugOr(name, fallback) {
|
|
8230
|
+
try {
|
|
8231
|
+
return normalizeNodeName(name);
|
|
8232
|
+
} catch {
|
|
8233
|
+
return fallback;
|
|
8234
|
+
}
|
|
8235
|
+
}
|
|
8020
8236
|
function nodeDirName(name) {
|
|
8021
8237
|
if (isNameCharsetConforming(name)) {
|
|
8022
8238
|
return name.replaceAll(" ", "_");
|
|
@@ -8030,8 +8246,8 @@ function agentDirName(name) {
|
|
|
8030
8246
|
return "agent";
|
|
8031
8247
|
}
|
|
8032
8248
|
}
|
|
8033
|
-
function executionDirName(
|
|
8034
|
-
return
|
|
8249
|
+
function executionDirName(executionId2) {
|
|
8250
|
+
return executionId2.slice(0, 8);
|
|
8035
8251
|
}
|
|
8036
8252
|
function workflowDirName(workflowId, version, parentId, parentVersion) {
|
|
8037
8253
|
const short = workflowId.slice(0, 8);
|
|
@@ -8079,16 +8295,6 @@ function matchesGlob(path, pattern) {
|
|
|
8079
8295
|
return new RegExp(`^${source}$`).test(path);
|
|
8080
8296
|
}
|
|
8081
8297
|
|
|
8082
|
-
// src/sync/types.ts
|
|
8083
|
-
var MANIFEST = ".asteroid.json";
|
|
8084
|
-
function get(path, query = {}) {
|
|
8085
|
-
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
8086
|
-
}
|
|
8087
|
-
function statusOf(error) {
|
|
8088
|
-
const status = error?.status;
|
|
8089
|
-
return typeof status === "number" ? status : void 0;
|
|
8090
|
-
}
|
|
8091
|
-
|
|
8092
8298
|
// src/sync/execution-pull.ts
|
|
8093
8299
|
var DIRECTORIES = ["workspace", "downloads", "shared"];
|
|
8094
8300
|
function relativeFilePath(file) {
|
|
@@ -8126,14 +8332,14 @@ async function writeActivities(sync, dir, execution) {
|
|
|
8126
8332
|
return false;
|
|
8127
8333
|
}
|
|
8128
8334
|
}
|
|
8129
|
-
async function listFiles(sync,
|
|
8335
|
+
async function listFiles(sync, executionId2) {
|
|
8130
8336
|
const listing = await sync.remote.json(
|
|
8131
|
-
get(`/agents/v2/executions/${
|
|
8337
|
+
get(`/agents/v2/executions/${executionId2}/files`)
|
|
8132
8338
|
);
|
|
8133
8339
|
return DIRECTORIES.flatMap((name) => listing.directories[name] ?? []);
|
|
8134
8340
|
}
|
|
8135
|
-
async function download(sync,
|
|
8136
|
-
const path = `/agents/v2/executions/${
|
|
8341
|
+
async function download(sync, executionId2, file) {
|
|
8342
|
+
const path = `/agents/v2/executions/${executionId2}/files/${file.id}/signed-url`;
|
|
8137
8343
|
const { url } = await sync.remote.json(get(path));
|
|
8138
8344
|
return sync.remote.download(url);
|
|
8139
8345
|
}
|
|
@@ -8261,6 +8467,114 @@ ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
|
|
|
8261
8467
|
].filter(Boolean).join("\n");
|
|
8262
8468
|
}
|
|
8263
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
|
+
|
|
8264
8578
|
// src/sync/node-files.ts
|
|
8265
8579
|
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
8266
8580
|
import { dirname as dirname2, relative, resolve, sep } from "node:path";
|
|
@@ -8673,6 +8987,35 @@ function integer(parsed, name) {
|
|
|
8673
8987
|
throw new UsageError(`--${name} must be a whole number`);
|
|
8674
8988
|
return Number(raw);
|
|
8675
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
|
+
}
|
|
8676
9019
|
function executionOptions(parsed) {
|
|
8677
9020
|
const summaryOnly = flag(parsed, "summary-only") === "true";
|
|
8678
9021
|
const glob = flag(parsed, "files");
|
|
@@ -8734,6 +9077,20 @@ async function execute(command, parsed, sync, deps) {
|
|
|
8734
9077
|
);
|
|
8735
9078
|
return { result: result2, text: formatWorkflowPush(result2) };
|
|
8736
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
|
+
}
|
|
8737
9094
|
if (command.command !== "execution pull")
|
|
8738
9095
|
throw new UsageError(`unknown command ${command.command}`);
|
|
8739
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",
|
|
@@ -2227,7 +2255,7 @@ var COMMANDS = [
|
|
|
2227
2255
|
command: "portal-skills add",
|
|
2228
2256
|
tool: "portal_skills_add",
|
|
2229
2257
|
title: "Add portal skills",
|
|
2230
|
-
description: "Add a portal's skills to an organization, by
|
|
2258
|
+
description: "Add a portal's skills to an organization, by the `portalId` of a portal skill from `skills_list`. Needs a Startup plan or above. Astro loads the portal's skills once it is added, and `skill_get` serves them. Adding one the organization already has is a no-op.",
|
|
2231
2259
|
method: "POST",
|
|
2232
2260
|
path: "/agents/v2/portal-skills/{portalId}",
|
|
2233
2261
|
args: [
|
|
@@ -2243,27 +2271,6 @@ var COMMANDS = [
|
|
|
2243
2271
|
readOnly: false,
|
|
2244
2272
|
destructive: false
|
|
2245
2273
|
},
|
|
2246
|
-
{
|
|
2247
|
-
command: "portal-skills list",
|
|
2248
|
-
tool: "portal_skills_list",
|
|
2249
|
-
title: "List portal skills",
|
|
2250
|
-
description: "List the skills for third-party portals, or only for the portals matching a portal name or task, such as `caloptima eligibility`. A portal skill is general knowledge about how a portal works: sign-in, page quirks, field mappings. Each portal comes with its skill names, and whether the organization has added it. Add one with `portal_skills_add`.",
|
|
2251
|
-
method: "GET",
|
|
2252
|
-
path: "/astro/v2/portal-skills",
|
|
2253
|
-
args: [],
|
|
2254
|
-
query: [
|
|
2255
|
-
{
|
|
2256
|
-
name: "query",
|
|
2257
|
-
flag: "query",
|
|
2258
|
-
type: "string",
|
|
2259
|
-
required: false,
|
|
2260
|
-
description: "A portal name or task, at least three characters. Omit to list every portal with skills."
|
|
2261
|
-
}
|
|
2262
|
-
],
|
|
2263
|
-
binary: false,
|
|
2264
|
-
readOnly: true,
|
|
2265
|
-
destructive: false
|
|
2266
|
-
},
|
|
2267
2274
|
{
|
|
2268
2275
|
command: "profile clear-cache",
|
|
2269
2276
|
tool: "profile_clear_browser_cache",
|
|
@@ -4039,6 +4046,80 @@ var COMMANDS = [
|
|
|
4039
4046
|
credentialOrganization: false
|
|
4040
4047
|
}
|
|
4041
4048
|
},
|
|
4049
|
+
{
|
|
4050
|
+
command: "skill get",
|
|
4051
|
+
tool: "skill_get",
|
|
4052
|
+
title: "Get skill",
|
|
4053
|
+
description: "Get one skill with the content of every file, SKILL.md first, in the Agent Skills format. A name resolves to the public skill, then a portal's, then the organization's; set `kind` to pick one. A portal skill needs its portal added with `portal_skills_add`. Pass `surface` `mcp` to spell the platform's commands as MCP tools.",
|
|
4054
|
+
method: "GET",
|
|
4055
|
+
path: "/astro/v2/skills/{name}",
|
|
4056
|
+
args: [
|
|
4057
|
+
{
|
|
4058
|
+
name: "name",
|
|
4059
|
+
flag: "name",
|
|
4060
|
+
type: "string",
|
|
4061
|
+
required: true
|
|
4062
|
+
}
|
|
4063
|
+
],
|
|
4064
|
+
query: [
|
|
4065
|
+
{
|
|
4066
|
+
name: "kind",
|
|
4067
|
+
flag: "kind",
|
|
4068
|
+
type: "string",
|
|
4069
|
+
required: false,
|
|
4070
|
+
description: "Only a skill of this kind.",
|
|
4071
|
+
enum: ["public", "portal", "organization"]
|
|
4072
|
+
},
|
|
4073
|
+
{
|
|
4074
|
+
name: "surface",
|
|
4075
|
+
flag: "surface",
|
|
4076
|
+
type: "string",
|
|
4077
|
+
required: false,
|
|
4078
|
+
description: "The surface the skill is rendered for. Defaults to `cli`.",
|
|
4079
|
+
enum: ["cli", "mcp"]
|
|
4080
|
+
}
|
|
4081
|
+
],
|
|
4082
|
+
binary: false,
|
|
4083
|
+
readOnly: true,
|
|
4084
|
+
destructive: false
|
|
4085
|
+
},
|
|
4086
|
+
{
|
|
4087
|
+
command: "skill list",
|
|
4088
|
+
tool: "skills_list",
|
|
4089
|
+
title: "List skills",
|
|
4090
|
+
description: "List the skills an organization can load, by kind then name: Asteroid's `public` skills, the `portal` skills (general knowledge of a third-party portal: sign-in, page quirks, field mappings) with whether the organization has added the portal, and the `organization`'s own. Filter with `kind`, or rank by a `query` such as `caloptima eligibility`. Each skill comes with its file paths and digests; `skill_get` returns the content. Add a portal with `portal_skills_add`.",
|
|
4091
|
+
method: "GET",
|
|
4092
|
+
path: "/astro/v2/skills",
|
|
4093
|
+
args: [],
|
|
4094
|
+
query: [
|
|
4095
|
+
{
|
|
4096
|
+
name: "kind",
|
|
4097
|
+
flag: "kind",
|
|
4098
|
+
type: "string",
|
|
4099
|
+
required: false,
|
|
4100
|
+
description: "Only skills of this kind.",
|
|
4101
|
+
enum: ["public", "portal", "organization"]
|
|
4102
|
+
},
|
|
4103
|
+
{
|
|
4104
|
+
name: "query",
|
|
4105
|
+
flag: "query",
|
|
4106
|
+
type: "string",
|
|
4107
|
+
required: false,
|
|
4108
|
+
description: "A skill name, portal name or task, at least three characters. Best matches first."
|
|
4109
|
+
},
|
|
4110
|
+
{
|
|
4111
|
+
name: "surface",
|
|
4112
|
+
flag: "surface",
|
|
4113
|
+
type: "string",
|
|
4114
|
+
required: false,
|
|
4115
|
+
description: "The surface the skills are rendered for. Defaults to `cli`.",
|
|
4116
|
+
enum: ["cli", "mcp"]
|
|
4117
|
+
}
|
|
4118
|
+
],
|
|
4119
|
+
binary: false,
|
|
4120
|
+
readOnly: true,
|
|
4121
|
+
destructive: false
|
|
4122
|
+
},
|
|
4042
4123
|
{
|
|
4043
4124
|
command: "tag create",
|
|
4044
4125
|
tool: "workflow_tags_create",
|
|
@@ -6527,15 +6608,16 @@ function defineCommands(root, options, dotted2 = /* @__PURE__ */ new Map(), onIn
|
|
|
6527
6608
|
(arg) => options.defaultArg?.(command, arg)
|
|
6528
6609
|
);
|
|
6529
6610
|
const defaulted = defaults[0] !== void 0;
|
|
6611
|
+
const optional = (arg, i) => i === 0 && defaulted || !arg.required;
|
|
6530
6612
|
command.args.forEach((arg) => {
|
|
6531
6613
|
leaf.argument(
|
|
6532
|
-
defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6614
|
+
defaulted || !arg.required ? `[${arg.flag}]` : `<${arg.flag}>`,
|
|
6533
6615
|
arg.description ?? ""
|
|
6534
6616
|
);
|
|
6535
6617
|
});
|
|
6536
6618
|
if (defaulted) {
|
|
6537
6619
|
const names = command.args.map(
|
|
6538
|
-
(arg, i) => i
|
|
6620
|
+
(arg, i) => optional(arg, i) ? `[${arg.flag}]` : `<${arg.flag}>`
|
|
6539
6621
|
);
|
|
6540
6622
|
leaf.usage(`[options] ${names.join(" ")}`);
|
|
6541
6623
|
}
|
|
@@ -6552,8 +6634,9 @@ ${notes.join("\n")}`);
|
|
|
6552
6634
|
if (positionals.length === command.args.length - 1 && defaults[0] !== void 0) {
|
|
6553
6635
|
positionals = [defaults[0], ...positionals];
|
|
6554
6636
|
}
|
|
6555
|
-
|
|
6556
|
-
|
|
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(" ");
|
|
6557
6640
|
throw new UsageError(
|
|
6558
6641
|
`expected ${expected}, got ${cmd.args.length} argument(s)`
|
|
6559
6642
|
);
|
|
@@ -6851,6 +6934,85 @@ function rejectUnknown(flags) {
|
|
|
6851
6934
|
}
|
|
6852
6935
|
}
|
|
6853
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
|
+
|
|
6854
7016
|
// src/sync/commands.ts
|
|
6855
7017
|
function option(name, type, description, extra = {}) {
|
|
6856
7018
|
return { name, flag: name, type, required: false, description, ...extra };
|
|
@@ -6862,6 +7024,12 @@ var local = {
|
|
|
6862
7024
|
destructive: false,
|
|
6863
7025
|
local: true
|
|
6864
7026
|
};
|
|
7027
|
+
var executionId = {
|
|
7028
|
+
name: "executionId",
|
|
7029
|
+
flag: "execution-id",
|
|
7030
|
+
type: "string",
|
|
7031
|
+
required: true
|
|
7032
|
+
};
|
|
6865
7033
|
var SYNC_COMMANDS = [
|
|
6866
7034
|
{
|
|
6867
7035
|
...local,
|
|
@@ -6870,14 +7038,7 @@ var SYNC_COMMANDS = [
|
|
|
6870
7038
|
title: "Pull an execution to disk",
|
|
6871
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.",
|
|
6872
7040
|
readOnly: true,
|
|
6873
|
-
args: [
|
|
6874
|
-
{
|
|
6875
|
-
name: "executionId",
|
|
6876
|
-
flag: "execution-id",
|
|
6877
|
-
type: "string",
|
|
6878
|
-
required: true
|
|
6879
|
-
}
|
|
6880
|
-
],
|
|
7041
|
+
args: [executionId],
|
|
6881
7042
|
query: [
|
|
6882
7043
|
option(
|
|
6883
7044
|
"files",
|
|
@@ -6903,6 +7064,54 @@ var SYNC_COMMANDS = [
|
|
|
6903
7064
|
"Write here instead of `<workflow-name>/executions/<id>/` under the current directory."
|
|
6904
7065
|
)
|
|
6905
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
|
+
]
|
|
6906
7115
|
}
|
|
6907
7116
|
];
|
|
6908
7117
|
|
|
@@ -7015,6 +7224,13 @@ function normalizeNodeName(name) {
|
|
|
7015
7224
|
}
|
|
7016
7225
|
return result;
|
|
7017
7226
|
}
|
|
7227
|
+
function lossySlugOr(name, fallback) {
|
|
7228
|
+
try {
|
|
7229
|
+
return normalizeNodeName(name);
|
|
7230
|
+
} catch {
|
|
7231
|
+
return fallback;
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7018
7234
|
function nodeDirName(name) {
|
|
7019
7235
|
if (isNameCharsetConforming(name)) {
|
|
7020
7236
|
return name.replaceAll(" ", "_");
|
|
@@ -7028,8 +7244,8 @@ function agentDirName(name) {
|
|
|
7028
7244
|
return "agent";
|
|
7029
7245
|
}
|
|
7030
7246
|
}
|
|
7031
|
-
function executionDirName(
|
|
7032
|
-
return
|
|
7247
|
+
function executionDirName(executionId2) {
|
|
7248
|
+
return executionId2.slice(0, 8);
|
|
7033
7249
|
}
|
|
7034
7250
|
function workflowDirName(workflowId, version, parentId, parentVersion) {
|
|
7035
7251
|
const short = workflowId.slice(0, 8);
|
|
@@ -7077,16 +7293,6 @@ function matchesGlob(path, pattern) {
|
|
|
7077
7293
|
return new RegExp(`^${source}$`).test(path);
|
|
7078
7294
|
}
|
|
7079
7295
|
|
|
7080
|
-
// src/sync/types.ts
|
|
7081
|
-
var MANIFEST = ".asteroid.json";
|
|
7082
|
-
function get(path, query = {}) {
|
|
7083
|
-
return { method: "GET", path, query: new URLSearchParams(query) };
|
|
7084
|
-
}
|
|
7085
|
-
function statusOf(error) {
|
|
7086
|
-
const status = error?.status;
|
|
7087
|
-
return typeof status === "number" ? status : void 0;
|
|
7088
|
-
}
|
|
7089
|
-
|
|
7090
7296
|
// src/sync/execution-pull.ts
|
|
7091
7297
|
var DIRECTORIES = ["workspace", "downloads", "shared"];
|
|
7092
7298
|
function relativeFilePath(file) {
|
|
@@ -7124,14 +7330,14 @@ async function writeActivities(sync, dir, execution) {
|
|
|
7124
7330
|
return false;
|
|
7125
7331
|
}
|
|
7126
7332
|
}
|
|
7127
|
-
async function listFiles(sync,
|
|
7333
|
+
async function listFiles(sync, executionId2) {
|
|
7128
7334
|
const listing = await sync.remote.json(
|
|
7129
|
-
get(`/agents/v2/executions/${
|
|
7335
|
+
get(`/agents/v2/executions/${executionId2}/files`)
|
|
7130
7336
|
);
|
|
7131
7337
|
return DIRECTORIES.flatMap((name) => listing.directories[name] ?? []);
|
|
7132
7338
|
}
|
|
7133
|
-
async function download(sync,
|
|
7134
|
-
const path = `/agents/v2/executions/${
|
|
7339
|
+
async function download(sync, executionId2, file) {
|
|
7340
|
+
const path = `/agents/v2/executions/${executionId2}/files/${file.id}/signed-url`;
|
|
7135
7341
|
const { url } = await sync.remote.json(get(path));
|
|
7136
7342
|
return sync.remote.download(url);
|
|
7137
7343
|
}
|
|
@@ -7259,6 +7465,114 @@ ${result.skipped.map((s) => ` - ${s}`).join("\n")}` : null,
|
|
|
7259
7465
|
].filter(Boolean).join("\n");
|
|
7260
7466
|
}
|
|
7261
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
|
+
|
|
7262
7576
|
// src/sync/manifest.ts
|
|
7263
7577
|
async function loadManifest(sync, dir) {
|
|
7264
7578
|
const bytes = await sync.files.read(join(dir, MANIFEST));
|
|
@@ -7671,6 +7985,35 @@ function integer(parsed, name) {
|
|
|
7671
7985
|
throw new UsageError(`--${name} must be a whole number`);
|
|
7672
7986
|
return Number(raw);
|
|
7673
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
|
+
}
|
|
7674
8017
|
function executionOptions(parsed) {
|
|
7675
8018
|
const summaryOnly = flag(parsed, "summary-only") === "true";
|
|
7676
8019
|
const glob = flag(parsed, "files");
|
|
@@ -7732,6 +8075,20 @@ async function execute(command, parsed, sync, deps) {
|
|
|
7732
8075
|
);
|
|
7733
8076
|
return { result: result2, text: formatWorkflowPush(result2) };
|
|
7734
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
|
+
}
|
|
7735
8092
|
if (command.command !== "execution pull")
|
|
7736
8093
|
throw new UsageError(`unknown command ${command.command}`);
|
|
7737
8094
|
const result = await pullExecution(sync, {
|
|
@@ -7754,15 +8111,20 @@ async function runSync(command, parsed, deps) {
|
|
|
7754
8111
|
export {
|
|
7755
8112
|
ALL_COMMANDS,
|
|
7756
8113
|
COMMANDS,
|
|
8114
|
+
INSIGHTS_DIR,
|
|
7757
8115
|
MANIFEST,
|
|
7758
8116
|
SYNC_COMMANDS,
|
|
7759
8117
|
UsageError,
|
|
7760
8118
|
buildRequest,
|
|
7761
8119
|
formatActivitiesMarkdown,
|
|
7762
8120
|
formatExecutionPull,
|
|
8121
|
+
formatExecutionWait,
|
|
8122
|
+
formatInsightPull,
|
|
7763
8123
|
formatWorkflowPull,
|
|
7764
8124
|
formatWorkflowPush,
|
|
7765
8125
|
headFilesRequest,
|
|
8126
|
+
insightFileId,
|
|
8127
|
+
isTerminalExecutionStatus,
|
|
7766
8128
|
leadingWords,
|
|
7767
8129
|
loadManifest,
|
|
7768
8130
|
matchCommand,
|
|
@@ -7770,12 +8132,15 @@ export {
|
|
|
7770
8132
|
parseInvocation,
|
|
7771
8133
|
pullDir,
|
|
7772
8134
|
pullExecution,
|
|
8135
|
+
pullInsights,
|
|
7773
8136
|
pullWorkflow,
|
|
7774
8137
|
pushWorkflow,
|
|
7775
8138
|
readManifest,
|
|
7776
8139
|
render,
|
|
7777
8140
|
runSync,
|
|
7778
8141
|
runsLocally,
|
|
8142
|
+
serializeInsightFile,
|
|
8143
|
+
waitForExecution,
|
|
7779
8144
|
withSyncCommands,
|
|
7780
8145
|
writeManifest
|
|
7781
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": {
|