@almyty/agents 0.1.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -16
- package/dist/args.d.ts +28 -0
- package/dist/args.js +80 -0
- package/dist/exit-codes.d.ts +26 -0
- package/dist/exit-codes.js +32 -0
- package/dist/format.d.ts +185 -0
- package/dist/format.js +408 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +322 -192
- package/dist/version.d.ts +2 -0
- package/dist/version.js +22 -0
- package/package.json +16 -4
package/dist/index.js
CHANGED
|
@@ -1,146 +1,174 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @almyty/agents — list,
|
|
3
|
+
* @almyty/agents — list, inspect, and run almyty agents.
|
|
4
4
|
*
|
|
5
5
|
* npx @almyty/agents list
|
|
6
6
|
* npx @almyty/agents get <name|id>
|
|
7
|
-
* npx @almyty/agents run <name|id>
|
|
7
|
+
* npx @almyty/agents run <name|id> --input '<json>' --watch
|
|
8
8
|
* npx @almyty/agents runs <name|id>
|
|
9
|
-
* npx @almyty/agents
|
|
9
|
+
* npx @almyty/agents inspect <name|id> <runId>
|
|
10
|
+
* npx @almyty/agents trace <name|id> <executionId>
|
|
10
11
|
*
|
|
11
12
|
* Reads credentials from ~/.almyty/credentials.json (created by
|
|
12
13
|
* `npx @almyty/auth login`).
|
|
13
14
|
*/
|
|
14
|
-
import { AlmytyClient,
|
|
15
|
-
|
|
15
|
+
import { AlmytyClient, resolveCredentials, } from '@almyty/client';
|
|
16
|
+
import { parseArgs, flagNumber, flagString } from './args.js';
|
|
17
|
+
import { EXIT } from './exit-codes.js';
|
|
18
|
+
import { VERSION } from './version.js';
|
|
19
|
+
import { formatAgentDetail, formatAgentLine, formatExecutionSummary, formatNodeResults, formatRunDetail, formatRunSummary, formatStep, formatTrace, notActiveMessage, runSucceeded, } from './format.js';
|
|
20
|
+
/** Flags that are switches, so they never swallow the next token. */
|
|
21
|
+
const BOOLEAN_FLAGS = ['watch', 'steps'];
|
|
16
22
|
function printHelp() {
|
|
17
|
-
console.log(
|
|
18
|
-
@almyty/agents v${VERSION}
|
|
23
|
+
console.log(`@almyty/agents v${VERSION}
|
|
19
24
|
|
|
20
25
|
Usage:
|
|
21
26
|
npx @almyty/agents <command> [options]
|
|
22
27
|
|
|
23
28
|
Commands:
|
|
24
|
-
list List
|
|
25
|
-
get <name|id>
|
|
26
|
-
run <name|id> Invoke a workflow agent or start an autonomous run
|
|
27
|
-
runs <name|id>
|
|
28
|
-
|
|
29
|
+
list List the agents in your organization
|
|
30
|
+
get <name|id> One agent: mode, status, pipeline shape, tools
|
|
31
|
+
run <name|id> Invoke a workflow agent, or start an autonomous run
|
|
32
|
+
runs <name|id> Recent autonomous runs, newest first
|
|
33
|
+
inspect <name|id> <runId> One autonomous run in full: steps, models, cost, error
|
|
34
|
+
executions <name|id> Recent workflow executions, newest first
|
|
35
|
+
trace <name|id> <execId> Where a workflow execution's calls went, hop by hop
|
|
36
|
+
cancel <name|id> <runId> Cancel an in-flight autonomous run
|
|
29
37
|
help Show this help
|
|
30
38
|
|
|
31
39
|
Run options:
|
|
32
40
|
--input '<json>' Input payload (workflow: object; autonomous: string or object)
|
|
33
|
-
--resume <conversation-id>
|
|
34
|
-
--max-steps <n> Autonomous:
|
|
35
|
-
--max-cost-cents <n> Autonomous:
|
|
36
|
-
--max-duration-ms <ms> Autonomous:
|
|
37
|
-
--watch Autonomous: stream steps
|
|
38
|
-
--
|
|
41
|
+
--resume <conversation-id> Autonomous: continue a previous conversation
|
|
42
|
+
--max-steps <n> Autonomous: step ceiling
|
|
43
|
+
--max-cost-cents <n> Autonomous: cost ceiling, in cents
|
|
44
|
+
--max-duration-ms <ms> Autonomous: wall-clock ceiling
|
|
45
|
+
--watch Autonomous: stream steps until the run ends
|
|
46
|
+
--timeout <s> Autonomous --watch: give up waiting after this long (default 300)
|
|
47
|
+
|
|
48
|
+
List options (runs, executions):
|
|
49
|
+
--limit <n> Rows per page (default 20)
|
|
50
|
+
--page <n> Page number (default 1)
|
|
51
|
+
|
|
52
|
+
Inspect options:
|
|
53
|
+
--steps run: also print per-node detail for a workflow run
|
|
54
|
+
|
|
55
|
+
Global options:
|
|
56
|
+
--json Machine-readable output, no decoration
|
|
57
|
+
--help, -h Show this help
|
|
58
|
+
--version, -v Print the version
|
|
39
59
|
|
|
40
60
|
Environment:
|
|
41
61
|
ALMYTY_TOKEN Token override (skips ~/.almyty/credentials.json)
|
|
42
62
|
ALMYTY_URL API URL override
|
|
63
|
+
NO_COLOR Honoured; these commands emit no ANSI colour anyway
|
|
64
|
+
|
|
65
|
+
Exit codes:
|
|
66
|
+
0 success
|
|
67
|
+
1 unexpected error
|
|
68
|
+
2 usage error (bad flags, unknown command)
|
|
69
|
+
3 not authenticated — run \`npx @almyty/auth login\`
|
|
70
|
+
4 no such agent, run, or execution
|
|
71
|
+
5 the run finished in a non-success state
|
|
72
|
+
|
|
73
|
+
Notes:
|
|
74
|
+
A workflow agent's history is under \`executions\` and \`trace\`; an
|
|
75
|
+
autonomous agent's is under \`runs\` and \`inspect\`. \`get\` tells you
|
|
76
|
+
which mode an agent is in.
|
|
43
77
|
|
|
44
78
|
Login:
|
|
45
79
|
npx @almyty/auth login Browser-based login (writes ~/.almyty/credentials.json)
|
|
46
80
|
`);
|
|
47
81
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
i++;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
result.flags[key] = true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else if (!result.command) {
|
|
77
|
-
result.command = arg;
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
result.positional.push(arg);
|
|
82
|
+
function emitJson(value) {
|
|
83
|
+
console.log(JSON.stringify(value, null, 2));
|
|
84
|
+
}
|
|
85
|
+
function fail(message, code) {
|
|
86
|
+
console.error(message);
|
|
87
|
+
process.exit(code);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The credential, or a clear instruction. Every command in every almyty
|
|
91
|
+
* CLI answers a missing credential the same way and with the same exit
|
|
92
|
+
* code, so a script can branch on 3 instead of matching stderr.
|
|
93
|
+
*/
|
|
94
|
+
function newClient(args) {
|
|
95
|
+
const creds = resolveCredentials();
|
|
96
|
+
if (!creds?.token) {
|
|
97
|
+
if (args?.flags.json) {
|
|
98
|
+
// A --json consumer gets a parseable answer on stdout too, not
|
|
99
|
+
// only an exit code and prose on stderr.
|
|
100
|
+
emitJson({
|
|
101
|
+
error: 'NOT_AUTHENTICATED',
|
|
102
|
+
message: 'Run `npx @almyty/auth login`, or set ALMYTY_TOKEN.',
|
|
103
|
+
});
|
|
81
104
|
}
|
|
82
|
-
|
|
105
|
+
console.error('Not authenticated. Run one of:');
|
|
106
|
+
console.error(' npx @almyty/auth login');
|
|
107
|
+
console.error(' export ALMYTY_TOKEN=<your-token>');
|
|
108
|
+
process.exit(EXIT.AUTH);
|
|
83
109
|
}
|
|
84
|
-
return result;
|
|
85
|
-
}
|
|
86
|
-
function newClient() {
|
|
87
|
-
const creds = resolveCredentialsOrExit();
|
|
88
110
|
return new AlmytyClient(creds.url, creds.token);
|
|
89
111
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
function requireArg(value, usage) {
|
|
113
|
+
if (!value)
|
|
114
|
+
fail(`Usage: npx @almyty/agents ${usage}`, EXIT.USAGE);
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
async function findAgentOrExit(client, ref) {
|
|
118
|
+
const agent = await client.findAgentByNameOrId(ref);
|
|
119
|
+
if (!agent) {
|
|
120
|
+
console.error(`Agent not found: ${ref}`);
|
|
121
|
+
console.error('Run `npx @almyty/agents list` to see what exists.');
|
|
122
|
+
process.exit(EXIT.NOT_FOUND);
|
|
123
|
+
}
|
|
124
|
+
return agent;
|
|
125
|
+
}
|
|
126
|
+
/** `?page=&limit=` from the shared list flags. */
|
|
127
|
+
function pageQuery(args) {
|
|
128
|
+
return {
|
|
129
|
+
page: flagNumber(args.flags, 'page') ?? 1,
|
|
130
|
+
limit: flagNumber(args.flags, 'limit') ?? 20,
|
|
131
|
+
};
|
|
95
132
|
}
|
|
96
133
|
async function cmdList(args) {
|
|
97
|
-
const client = newClient();
|
|
134
|
+
const client = newClient(args);
|
|
98
135
|
const agents = await client.listAgents();
|
|
99
136
|
if (args.flags.json) {
|
|
100
|
-
|
|
137
|
+
emitJson(agents);
|
|
101
138
|
return;
|
|
102
139
|
}
|
|
103
140
|
if (agents.length === 0) {
|
|
104
141
|
console.log('No agents found. Create one at https://app.almyty.com/agents');
|
|
105
142
|
return;
|
|
106
143
|
}
|
|
107
|
-
console.log(
|
|
108
|
-
|
|
109
|
-
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log(`${agents.length} agent(s):`);
|
|
146
|
+
console.log('');
|
|
147
|
+
for (const agent of agents) {
|
|
148
|
+
console.log(formatAgentLine(agent));
|
|
110
149
|
console.log('');
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
async function cmdGet(args) {
|
|
114
|
-
const ref = args.positional[0];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const agent = await client.findAgentByNameOrId(ref);
|
|
121
|
-
if (!agent) {
|
|
122
|
-
console.error(`Agent not found: ${ref}`);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
153
|
+
const ref = requireArg(args.positional[0], 'get <name|id>');
|
|
154
|
+
const client = newClient(args);
|
|
155
|
+
const found = await findAgentOrExit(client, ref);
|
|
156
|
+
// The list endpoint drops tools and the pipeline; fetch the full row
|
|
157
|
+
// so `get` can answer what the agent is actually wired to.
|
|
158
|
+
const agent = await client.getAgent(found.id).catch(() => found);
|
|
125
159
|
if (args.flags.json) {
|
|
126
|
-
|
|
160
|
+
emitJson(agent);
|
|
127
161
|
return;
|
|
128
162
|
}
|
|
129
163
|
console.log('');
|
|
130
|
-
console.log(
|
|
131
|
-
console.log(` id: ${agent.id}`);
|
|
132
|
-
if (agent.mode)
|
|
133
|
-
console.log(` mode: ${agent.mode}`);
|
|
134
|
-
if (agent.status)
|
|
135
|
-
console.log(` status: ${agent.status}`);
|
|
136
|
-
if (agent.description)
|
|
137
|
-
console.log(` desc: ${agent.description}`);
|
|
164
|
+
console.log(formatAgentDetail(agent));
|
|
138
165
|
console.log('');
|
|
139
166
|
}
|
|
140
167
|
function parseInputFlag(value) {
|
|
141
|
-
if (
|
|
168
|
+
if (typeof value !== 'string')
|
|
142
169
|
return undefined;
|
|
143
|
-
// Try JSON first; fall back to raw string
|
|
170
|
+
// Try JSON first; fall back to the raw string (an autonomous agent's
|
|
171
|
+
// input is usually a plain sentence).
|
|
144
172
|
try {
|
|
145
173
|
return JSON.parse(value);
|
|
146
174
|
}
|
|
@@ -148,147 +176,260 @@ function parseInputFlag(value) {
|
|
|
148
176
|
return value;
|
|
149
177
|
}
|
|
150
178
|
}
|
|
151
|
-
function formatStep(step) {
|
|
152
|
-
if (!step)
|
|
153
|
-
return null;
|
|
154
|
-
switch (step.type) {
|
|
155
|
-
case 'llm_call':
|
|
156
|
-
if (step.output?.content) {
|
|
157
|
-
return ` ${step.output.content}`;
|
|
158
|
-
}
|
|
159
|
-
return ` · llm_call (${step.tokens?.input ?? 0}↓ / ${step.tokens?.output ?? 0}↑)`;
|
|
160
|
-
case 'tool_call':
|
|
161
|
-
return ` · tool_call → ${step.input?.tool ?? '?'}`;
|
|
162
|
-
case 'sub_agent_call':
|
|
163
|
-
return ` · sub_agent_call → ${step.input?.agentId ?? '?'}`;
|
|
164
|
-
case 'error':
|
|
165
|
-
return ` ! error: ${step.error}`;
|
|
166
|
-
default:
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
179
|
async function cmdRun(args) {
|
|
171
|
-
const ref = args.positional[0];
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
const ref = requireArg(args.positional[0], "run <name|id> [--input '<json>']");
|
|
181
|
+
const client = newClient(args);
|
|
182
|
+
const agent = await findAgentOrExit(client, ref);
|
|
183
|
+
// A DRAFT agent answers 400 with a JSON body. The status is already
|
|
184
|
+
// here, so say what to do about it instead of printing the body.
|
|
185
|
+
if (agent.status && agent.status.toLowerCase() !== 'active') {
|
|
186
|
+
if (args.flags.json) {
|
|
187
|
+
emitJson({
|
|
188
|
+
error: 'AGENT_NOT_ACTIVE',
|
|
189
|
+
agentId: agent.id,
|
|
190
|
+
agentName: agent.name,
|
|
191
|
+
status: agent.status,
|
|
192
|
+
message: 'Agent must be active to run',
|
|
193
|
+
});
|
|
194
|
+
process.exit(EXIT.FAILED);
|
|
195
|
+
}
|
|
196
|
+
fail(notActiveMessage(agent), EXIT.FAILED);
|
|
181
197
|
}
|
|
182
198
|
const rawInput = parseInputFlag(args.flags.input);
|
|
183
199
|
if (agent.mode === 'autonomous') {
|
|
184
200
|
const limits = {};
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
201
|
+
const maxSteps = flagNumber(args.flags, 'max-steps');
|
|
202
|
+
const maxCostCents = flagNumber(args.flags, 'max-cost-cents');
|
|
203
|
+
const maxDurationMs = flagNumber(args.flags, 'max-duration-ms');
|
|
204
|
+
if (maxSteps !== undefined)
|
|
205
|
+
limits.maxSteps = maxSteps;
|
|
206
|
+
if (maxCostCents !== undefined)
|
|
207
|
+
limits.maxCostCents = maxCostCents;
|
|
208
|
+
if (maxDurationMs !== undefined)
|
|
209
|
+
limits.maxDurationMs = maxDurationMs;
|
|
210
|
+
const resume = flagString(args.flags, 'resume');
|
|
211
|
+
if (resume)
|
|
212
|
+
limits.conversationId = resume;
|
|
193
213
|
const runStub = await client.startRun(agent.id, rawInput ?? '', limits);
|
|
194
214
|
if (!args.flags.watch) {
|
|
195
215
|
if (args.flags.json) {
|
|
196
|
-
|
|
216
|
+
emitJson(runStub);
|
|
197
217
|
}
|
|
198
218
|
else {
|
|
199
219
|
console.log(`Started run ${runStub.id}`);
|
|
200
|
-
console.log(`Watch:
|
|
201
|
-
console.log(`Cancel:
|
|
220
|
+
console.log(`Watch: npx @almyty/agents inspect ${ref} ${runStub.id}`);
|
|
221
|
+
console.log(`Cancel: npx @almyty/agents cancel ${ref} ${runStub.id}`);
|
|
202
222
|
}
|
|
203
223
|
return;
|
|
204
224
|
}
|
|
205
225
|
let printed = 0;
|
|
226
|
+
const timeoutSeconds = flagNumber(args.flags, 'timeout') ?? 300;
|
|
206
227
|
const final = await client.pollRun(agent.id, runStub.id, {
|
|
228
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
207
229
|
onStep: (run) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
230
|
+
if (args.flags.json)
|
|
231
|
+
return;
|
|
232
|
+
const steps = (run.steps ?? []);
|
|
233
|
+
for (let i = printed; i < steps.length; i++) {
|
|
234
|
+
const line = formatStep(steps[i], i);
|
|
211
235
|
if (line)
|
|
212
236
|
console.log(line);
|
|
213
237
|
}
|
|
214
|
-
printed =
|
|
238
|
+
printed = steps.length;
|
|
215
239
|
},
|
|
216
240
|
});
|
|
241
|
+
// A failed run is a failed command.
|
|
242
|
+
//
|
|
243
|
+
// pollRun returns on ANY terminal status, and this printed
|
|
244
|
+
// "Run failed." and exited 0 -- so `almyty agents run deploy-check
|
|
245
|
+
// --watch && ./ship.sh` shipped on a failed check.
|
|
246
|
+
if (!runSucceeded(final.status))
|
|
247
|
+
process.exitCode = EXIT.FAILED;
|
|
217
248
|
if (args.flags.json) {
|
|
218
|
-
|
|
249
|
+
emitJson(final);
|
|
219
250
|
return;
|
|
220
251
|
}
|
|
221
252
|
console.log('');
|
|
222
|
-
console.log(
|
|
253
|
+
console.log(formatRunSummary(final));
|
|
223
254
|
if (final.output != null) {
|
|
224
255
|
console.log('');
|
|
225
|
-
console.log(typeof final.output === 'string'
|
|
256
|
+
console.log(typeof final.output === 'string'
|
|
257
|
+
? final.output
|
|
258
|
+
: JSON.stringify(final.output, null, 2));
|
|
226
259
|
}
|
|
227
|
-
if (final.error)
|
|
260
|
+
if (final.error) {
|
|
261
|
+
console.error('');
|
|
228
262
|
console.error(`Error: ${final.error}`);
|
|
263
|
+
console.error(`Detail: npx @almyty/agents inspect ${ref} ${final.id}`);
|
|
264
|
+
}
|
|
229
265
|
return;
|
|
230
266
|
}
|
|
231
267
|
// workflow mode
|
|
232
268
|
const result = await client.invokeAgent(agent.id, rawInput ?? {});
|
|
269
|
+
// Same rule: the endpoint answers 200 with status 'failed'.
|
|
270
|
+
if (result?.status && !runSucceeded(result.status))
|
|
271
|
+
process.exitCode = EXIT.FAILED;
|
|
233
272
|
if (args.flags.json) {
|
|
234
|
-
|
|
273
|
+
emitJson(result);
|
|
235
274
|
return;
|
|
236
275
|
}
|
|
237
|
-
const output = result?.output ??
|
|
238
|
-
if (output == null)
|
|
276
|
+
const output = result?.output ?? null;
|
|
277
|
+
if (output == null)
|
|
239
278
|
console.log('(no output)');
|
|
240
|
-
|
|
241
|
-
else if (typeof output === 'string') {
|
|
279
|
+
else if (typeof output === 'string')
|
|
242
280
|
console.log(output);
|
|
243
|
-
|
|
244
|
-
else {
|
|
281
|
+
else
|
|
245
282
|
console.log(JSON.stringify(output, null, 2));
|
|
283
|
+
// Attribution and cost, always — a routed workflow answers from
|
|
284
|
+
// whichever model the policy picked, and that has to be visible.
|
|
285
|
+
if (result?.status) {
|
|
286
|
+
console.log('');
|
|
287
|
+
console.log(formatExecutionSummary(result));
|
|
288
|
+
}
|
|
289
|
+
if (args.flags.steps || (result?.status && !runSucceeded(result.status))) {
|
|
290
|
+
const lines = formatNodeResults(result?.nodeResults);
|
|
291
|
+
if (lines.length) {
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log('Nodes:');
|
|
294
|
+
for (const line of lines)
|
|
295
|
+
console.log(line);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (result?.error) {
|
|
299
|
+
console.error('');
|
|
300
|
+
console.error(`Error: ${result.error}`);
|
|
301
|
+
if (result.id) {
|
|
302
|
+
console.error(`Detail: npx @almyty/agents trace ${ref} ${result.id}`);
|
|
303
|
+
}
|
|
246
304
|
}
|
|
247
305
|
}
|
|
248
306
|
async function cmdRuns(args) {
|
|
249
|
-
const ref = args.positional[0];
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
307
|
+
const ref = requireArg(args.positional[0], 'runs <name|id>');
|
|
308
|
+
const client = newClient(args);
|
|
309
|
+
const agent = await findAgentOrExit(client, ref);
|
|
310
|
+
const { page, limit } = pageQuery(args);
|
|
311
|
+
const { data, total } = await client.listRuns(agent.id, page, limit);
|
|
312
|
+
if (args.flags.json) {
|
|
313
|
+
emitJson({ total, page, limit, data });
|
|
314
|
+
return;
|
|
253
315
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
316
|
+
if (data.length === 0) {
|
|
317
|
+
console.log(agent.mode === 'workflow'
|
|
318
|
+
? `No autonomous runs — ${agent.name} is a workflow agent. Try: npx @almyty/agents executions ${ref}`
|
|
319
|
+
: 'No runs yet.');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
console.log('');
|
|
323
|
+
console.log(`${total} run(s), showing page ${page}:`);
|
|
324
|
+
console.log('');
|
|
325
|
+
for (const run of data) {
|
|
326
|
+
const bits = [run.id, (run.status ?? '?').padEnd(10)];
|
|
327
|
+
if (run.createdAt)
|
|
328
|
+
bits.push(run.createdAt);
|
|
329
|
+
console.log(` ${bits.join(' ')}`);
|
|
330
|
+
console.log(` ${formatRunSummary(run)}`);
|
|
331
|
+
}
|
|
332
|
+
console.log('');
|
|
333
|
+
console.log(`Detail: npx @almyty/agents inspect ${ref} <runId>`);
|
|
334
|
+
}
|
|
335
|
+
async function cmdInspect(args) {
|
|
336
|
+
const ref = requireArg(args.positional[0], 'inspect <name|id> <runId>');
|
|
337
|
+
const runId = requireArg(args.positional[1], 'inspect <name|id> <runId>');
|
|
338
|
+
const client = newClient(args);
|
|
339
|
+
const agent = await findAgentOrExit(client, ref);
|
|
340
|
+
let run;
|
|
341
|
+
try {
|
|
342
|
+
run = await client.getRun(agent.id, runId);
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
fail(`Run not found on ${agent.name}: ${runId}\n${err.message}`, EXIT.NOT_FOUND);
|
|
259
346
|
}
|
|
260
|
-
const { data, total } = await client.listRuns(agent.id);
|
|
261
347
|
if (args.flags.json) {
|
|
262
|
-
|
|
348
|
+
emitJson(run);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
console.log('');
|
|
352
|
+
console.log(formatRunDetail(run));
|
|
353
|
+
console.log('');
|
|
354
|
+
}
|
|
355
|
+
if (!runSucceeded(run.status))
|
|
356
|
+
process.exitCode = EXIT.FAILED;
|
|
357
|
+
}
|
|
358
|
+
async function cmdExecutions(args) {
|
|
359
|
+
const ref = requireArg(args.positional[0], 'executions <name|id>');
|
|
360
|
+
const client = newClient(args);
|
|
361
|
+
const agent = await findAgentOrExit(client, ref);
|
|
362
|
+
const { page, limit } = pageQuery(args);
|
|
363
|
+
const body = await client.request(`/agents/${encodeURIComponent(agent.id)}/executions?page=${page}&limit=${limit}`);
|
|
364
|
+
const data = body?.data ?? [];
|
|
365
|
+
const total = body?.pagination?.total ?? data.length;
|
|
366
|
+
if (args.flags.json) {
|
|
367
|
+
emitJson({ total, page, limit, data });
|
|
263
368
|
return;
|
|
264
369
|
}
|
|
265
370
|
if (data.length === 0) {
|
|
266
|
-
console.log(
|
|
371
|
+
console.log(agent.mode === 'autonomous'
|
|
372
|
+
? `No workflow executions — ${agent.name} is an autonomous agent. Try: npx @almyty/agents runs ${ref}`
|
|
373
|
+
: 'No executions yet.');
|
|
267
374
|
return;
|
|
268
375
|
}
|
|
269
|
-
console.log(
|
|
270
|
-
|
|
271
|
-
|
|
376
|
+
console.log('');
|
|
377
|
+
console.log(`${total} execution(s), showing page ${page}:`);
|
|
378
|
+
console.log('');
|
|
379
|
+
for (const execution of data) {
|
|
380
|
+
const bits = [execution.id, (execution.status ?? '?').padEnd(10)];
|
|
381
|
+
if (execution.createdAt)
|
|
382
|
+
bits.push(execution.createdAt);
|
|
383
|
+
console.log(` ${bits.join(' ')}`);
|
|
384
|
+
console.log(` ${formatExecutionSummary(execution)}`);
|
|
272
385
|
}
|
|
386
|
+
console.log('');
|
|
387
|
+
console.log(`Detail: npx @almyty/agents trace ${ref} <executionId>`);
|
|
273
388
|
}
|
|
274
|
-
async function
|
|
275
|
-
const ref = args.positional[0];
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
389
|
+
async function cmdTrace(args) {
|
|
390
|
+
const ref = requireArg(args.positional[0], 'trace <name|id> <executionId>');
|
|
391
|
+
const executionId = requireArg(args.positional[1], 'trace <name|id> <executionId>');
|
|
392
|
+
const client = newClient(args);
|
|
393
|
+
const agent = await findAgentOrExit(client, ref);
|
|
394
|
+
let body;
|
|
395
|
+
try {
|
|
396
|
+
body = await client.request(`/agents/${encodeURIComponent(agent.id)}/executions/${encodeURIComponent(executionId)}/trace`);
|
|
280
397
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (!agent) {
|
|
284
|
-
console.error(`Agent not found: ${ref}`);
|
|
285
|
-
process.exit(1);
|
|
398
|
+
catch (err) {
|
|
399
|
+
fail(`No trace for execution ${executionId} on ${agent.name}.\n${err.message}`, EXIT.NOT_FOUND);
|
|
286
400
|
}
|
|
401
|
+
const trace = body?.data ?? body;
|
|
402
|
+
if (args.flags.json) {
|
|
403
|
+
emitJson(trace);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
console.log('');
|
|
407
|
+
console.log(formatTrace(trace));
|
|
408
|
+
console.log('');
|
|
409
|
+
}
|
|
410
|
+
async function cmdCancel(args) {
|
|
411
|
+
const ref = requireArg(args.positional[0], 'cancel <name|id> <runId>');
|
|
412
|
+
const runId = requireArg(args.positional[1], 'cancel <name|id> <runId>');
|
|
413
|
+
const client = newClient(args);
|
|
414
|
+
const agent = await findAgentOrExit(client, ref);
|
|
287
415
|
await client.cancelRun(agent.id, runId);
|
|
288
|
-
|
|
416
|
+
if (args.flags.json)
|
|
417
|
+
emitJson({ cancelled: true, agentId: agent.id, runId });
|
|
418
|
+
else
|
|
419
|
+
console.log(`Cancelled ${runId}`);
|
|
289
420
|
}
|
|
421
|
+
const COMMANDS = {
|
|
422
|
+
list: cmdList,
|
|
423
|
+
get: cmdGet,
|
|
424
|
+
run: cmdRun,
|
|
425
|
+
runs: cmdRuns,
|
|
426
|
+
inspect: cmdInspect,
|
|
427
|
+
executions: cmdExecutions,
|
|
428
|
+
trace: cmdTrace,
|
|
429
|
+
cancel: cmdCancel,
|
|
430
|
+
};
|
|
290
431
|
async function main() {
|
|
291
|
-
const args = parseArgs(process.argv.slice(2));
|
|
432
|
+
const args = parseArgs(process.argv.slice(2), BOOLEAN_FLAGS);
|
|
292
433
|
if (args.flags.version) {
|
|
293
434
|
console.log(VERSION);
|
|
294
435
|
return;
|
|
@@ -297,29 +438,18 @@ async function main() {
|
|
|
297
438
|
printHelp();
|
|
298
439
|
return;
|
|
299
440
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
await cmdGet(args);
|
|
306
|
-
return;
|
|
307
|
-
case 'run':
|
|
308
|
-
await cmdRun(args);
|
|
309
|
-
return;
|
|
310
|
-
case 'runs':
|
|
311
|
-
await cmdRuns(args);
|
|
312
|
-
return;
|
|
313
|
-
case 'cancel':
|
|
314
|
-
await cmdCancel(args);
|
|
315
|
-
return;
|
|
316
|
-
default:
|
|
317
|
-
console.error(`Unknown command: ${args.command}`);
|
|
318
|
-
printHelp();
|
|
319
|
-
process.exit(1);
|
|
441
|
+
const handler = COMMANDS[args.command];
|
|
442
|
+
if (!handler) {
|
|
443
|
+
console.error(`Unknown command: ${args.command}`);
|
|
444
|
+
console.error(`Commands: ${Object.keys(COMMANDS).join(', ')}. Run --help for detail.`);
|
|
445
|
+
process.exit(EXIT.USAGE);
|
|
320
446
|
}
|
|
447
|
+
await handler(args);
|
|
321
448
|
}
|
|
322
449
|
main().catch((err) => {
|
|
323
450
|
console.error(`Error: ${err.message}`);
|
|
324
|
-
|
|
451
|
+
// An auth failure from anywhere in the client gets the auth code, so
|
|
452
|
+
// `|| almyty login` works whichever call discovered the dead token.
|
|
453
|
+
const authFailure = /Authentication failed|401/.test(err.message ?? '');
|
|
454
|
+
process.exit(authFailure ? EXIT.AUTH : EXIT.ERROR);
|
|
325
455
|
});
|