@almyty/agents 1.2.0 → 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 +98 -19
- 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 +2 -2
package/dist/format.js
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering for @almyty/agents.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions, no I/O, so the shape of what a user sees is testable
|
|
5
|
+
* without a backend. The interesting part is attribution: a routed call
|
|
6
|
+
* stamps `routing` on the step / node result, and the CLI used to throw
|
|
7
|
+
* all of it away — so `agents run` could not answer "which model
|
|
8
|
+
* answered this, and what did it cost", which is the first question
|
|
9
|
+
* anyone asks about a multi-model agent.
|
|
10
|
+
*/
|
|
11
|
+
/** Terminal statuses that mean the run did what was asked. */
|
|
12
|
+
export const RUN_SUCCEEDED = new Set(['completed', 'succeeded']);
|
|
13
|
+
/** A status that will never change on its own. */
|
|
14
|
+
export const RUN_TERMINAL = new Set([
|
|
15
|
+
'completed',
|
|
16
|
+
'succeeded',
|
|
17
|
+
'failed',
|
|
18
|
+
'cancelled',
|
|
19
|
+
'timeout',
|
|
20
|
+
]);
|
|
21
|
+
export function runSucceeded(status) {
|
|
22
|
+
return status !== undefined && RUN_SUCCEEDED.has(status);
|
|
23
|
+
}
|
|
24
|
+
/** `$0.0042`, or `—` when nothing was recorded. A zero cost prints as $0. */
|
|
25
|
+
export function formatCost(cost) {
|
|
26
|
+
if (typeof cost !== 'number' || !Number.isFinite(cost))
|
|
27
|
+
return '—';
|
|
28
|
+
if (cost === 0)
|
|
29
|
+
return '$0';
|
|
30
|
+
return `$${cost < 0.01 ? cost.toFixed(6) : cost.toFixed(4)}`;
|
|
31
|
+
}
|
|
32
|
+
export function formatTokens(tokens) {
|
|
33
|
+
if (typeof tokens === 'number' && Number.isFinite(tokens)) {
|
|
34
|
+
return tokens.toLocaleString('en-US');
|
|
35
|
+
}
|
|
36
|
+
if (tokens && typeof tokens === 'object') {
|
|
37
|
+
const t = tokens;
|
|
38
|
+
const inTok = t.input ?? 0;
|
|
39
|
+
const outTok = t.output ?? 0;
|
|
40
|
+
if (inTok === 0 && outTok === 0)
|
|
41
|
+
return '—';
|
|
42
|
+
return `${inTok.toLocaleString('en-US')} in / ${outTok.toLocaleString('en-US')} out`;
|
|
43
|
+
}
|
|
44
|
+
return '—';
|
|
45
|
+
}
|
|
46
|
+
export function formatDuration(ms) {
|
|
47
|
+
if (typeof ms !== 'number' || !Number.isFinite(ms))
|
|
48
|
+
return '—';
|
|
49
|
+
if (ms < 1000)
|
|
50
|
+
return `${Math.round(ms)}ms`;
|
|
51
|
+
if (ms < 60_000)
|
|
52
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
53
|
+
const minutes = Math.floor(ms / 60_000);
|
|
54
|
+
const seconds = Math.round((ms % 60_000) / 1000);
|
|
55
|
+
return `${minutes}m${seconds.toString().padStart(2, '0')}s`;
|
|
56
|
+
}
|
|
57
|
+
/** Whichever name a routed call gives for the model that answered. */
|
|
58
|
+
export function modelOf(routing) {
|
|
59
|
+
if (!routing)
|
|
60
|
+
return null;
|
|
61
|
+
return routing.vendorModelId || routing.modelId || null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One line naming the model that answered and why it was picked.
|
|
65
|
+
* `null` when the call was not routed (a pinned provider leaves no
|
|
66
|
+
* attribution, and inventing one would be worse than saying nothing).
|
|
67
|
+
*/
|
|
68
|
+
export function formatRouting(routing) {
|
|
69
|
+
const model = modelOf(routing);
|
|
70
|
+
if (!model || !routing)
|
|
71
|
+
return null;
|
|
72
|
+
const parts = [model];
|
|
73
|
+
if (typeof routing.attempt === 'number' && routing.attempt > 1) {
|
|
74
|
+
parts.push(`attempt ${routing.attempt}`);
|
|
75
|
+
}
|
|
76
|
+
if (routing.rationale)
|
|
77
|
+
parts.push(routing.rationale);
|
|
78
|
+
const skipped = (routing.tried ?? []).length + (routing.rejected ?? []).length;
|
|
79
|
+
if (skipped > 0)
|
|
80
|
+
parts.push(`${skipped} candidate(s) passed over`);
|
|
81
|
+
return parts.join(' · ');
|
|
82
|
+
}
|
|
83
|
+
/** The routing attribution a step carries, wherever it was stamped. */
|
|
84
|
+
export function routingOfStep(step) {
|
|
85
|
+
const routing = step?.output?.routing;
|
|
86
|
+
return routing && typeof routing === 'object' ? routing : undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* One line per step, as `run --watch` streams them.
|
|
90
|
+
* Returns null for a step with nothing worth showing.
|
|
91
|
+
*/
|
|
92
|
+
export function formatStep(step, index) {
|
|
93
|
+
if (!step)
|
|
94
|
+
return null;
|
|
95
|
+
const n = index === undefined ? '' : `${index + 1}. `;
|
|
96
|
+
const meta = [];
|
|
97
|
+
const routing = formatRouting(routingOfStep(step));
|
|
98
|
+
if (routing)
|
|
99
|
+
meta.push(routing);
|
|
100
|
+
if (typeof step.cost === 'number' && step.cost > 0)
|
|
101
|
+
meta.push(formatCost(step.cost));
|
|
102
|
+
const tokens = formatTokens(step.tokens);
|
|
103
|
+
if (tokens !== '—')
|
|
104
|
+
meta.push(tokens);
|
|
105
|
+
if (typeof step.duration === 'number')
|
|
106
|
+
meta.push(formatDuration(step.duration));
|
|
107
|
+
const suffix = meta.length ? ` [${meta.join(' · ')}]` : '';
|
|
108
|
+
switch (step.type) {
|
|
109
|
+
case 'llm_call': {
|
|
110
|
+
const status = step.output?.status;
|
|
111
|
+
if (typeof step.output?.content === 'string' && step.output.content.length > 0) {
|
|
112
|
+
return ` ${n}${step.output.content}${suffix}`;
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(step.output?.toolCalls) && step.output.toolCalls.length > 0) {
|
|
115
|
+
const names = step.output.toolCalls
|
|
116
|
+
.map((tc) => tc?.name ?? '?')
|
|
117
|
+
.join(', ');
|
|
118
|
+
return ` ${n}llm_call → tools: ${names}${suffix}`;
|
|
119
|
+
}
|
|
120
|
+
if (status === 'waiting_input') {
|
|
121
|
+
return ` ${n}waiting for input: ${step.output?.question ?? '(no question given)'}${suffix}`;
|
|
122
|
+
}
|
|
123
|
+
if (status === 'sleeping') {
|
|
124
|
+
return ` ${n}sleeping: ${step.output?.reason ?? '(no reason given)'}${suffix}`;
|
|
125
|
+
}
|
|
126
|
+
return ` ${n}llm_call${suffix}`;
|
|
127
|
+
}
|
|
128
|
+
case 'tool_call':
|
|
129
|
+
return ` ${n}tool_call → ${step.input?.tool ?? step.input?.name ?? '?'}${suffix}`;
|
|
130
|
+
case 'sub_agent_call':
|
|
131
|
+
return ` ${n}sub_agent_call → ${step.input?.agentId ?? '?'}${suffix}`;
|
|
132
|
+
case 'error':
|
|
133
|
+
return ` ${n}error: ${step.error ?? 'unknown'}${suffix}`;
|
|
134
|
+
default:
|
|
135
|
+
return step.type ? ` ${n}${step.type}${suffix}` : null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The closing line of a run: status, what it cost, and which models
|
|
140
|
+
* answered. Someone reading a CI log should not have to open the UI to
|
|
141
|
+
* learn any of the three.
|
|
142
|
+
*/
|
|
143
|
+
export function formatRunSummary(run) {
|
|
144
|
+
const models = new Set();
|
|
145
|
+
for (const step of run.steps ?? []) {
|
|
146
|
+
const model = modelOf(routingOfStep(step));
|
|
147
|
+
if (model)
|
|
148
|
+
models.add(model);
|
|
149
|
+
}
|
|
150
|
+
const bits = [`Run ${run.status ?? 'unknown'}`];
|
|
151
|
+
if (models.size > 0)
|
|
152
|
+
bits.push(`answered by ${[...models].join(', ')}`);
|
|
153
|
+
bits.push(formatCost(run.totalCost));
|
|
154
|
+
const tokens = formatTokens(run.totalTokens);
|
|
155
|
+
if (tokens !== '—')
|
|
156
|
+
bits.push(`${tokens} tokens`);
|
|
157
|
+
if (typeof run.executionTime === 'number' && run.executionTime > 0) {
|
|
158
|
+
bits.push(formatDuration(run.executionTime));
|
|
159
|
+
}
|
|
160
|
+
return bits.join(' · ');
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The same closing line for a workflow execution, whose attribution
|
|
164
|
+
* lives per node rather than per step.
|
|
165
|
+
*/
|
|
166
|
+
export function formatExecutionSummary(execution) {
|
|
167
|
+
const models = new Set();
|
|
168
|
+
let failed = 0;
|
|
169
|
+
for (const result of Object.values(execution.nodeResults ?? {})) {
|
|
170
|
+
const model = modelOf(result?.routing);
|
|
171
|
+
if (model)
|
|
172
|
+
models.add(model);
|
|
173
|
+
if (result?.error)
|
|
174
|
+
failed++;
|
|
175
|
+
}
|
|
176
|
+
const bits = [`Run ${execution.status ?? 'unknown'}`];
|
|
177
|
+
if (models.size > 0)
|
|
178
|
+
bits.push(`answered by ${[...models].join(', ')}`);
|
|
179
|
+
if (failed > 0)
|
|
180
|
+
bits.push(`${failed} node(s) failed`);
|
|
181
|
+
bits.push(formatCost(execution.totalCost));
|
|
182
|
+
const tokens = formatTokens(execution.totalTokens);
|
|
183
|
+
if (tokens !== '—')
|
|
184
|
+
bits.push(`${tokens} tokens`);
|
|
185
|
+
if (typeof execution.executionTime === 'number' && execution.executionTime > 0) {
|
|
186
|
+
bits.push(formatDuration(execution.executionTime));
|
|
187
|
+
}
|
|
188
|
+
return bits.join(' · ');
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Per-node detail for a workflow run: which node failed, what was tried,
|
|
192
|
+
* and what answered. This is what you would otherwise open the UI for.
|
|
193
|
+
*/
|
|
194
|
+
export function formatNodeResults(nodeResults) {
|
|
195
|
+
const lines = [];
|
|
196
|
+
// Skipped nodes never ran, so they carry no timestamp. Treating a
|
|
197
|
+
// missing startedAt as 0 floated them to the top of the list, above
|
|
198
|
+
// the nodes that actually executed.
|
|
199
|
+
const entries = Object.entries(nodeResults ?? {}).sort((a, b) => (a[1]?.startedAt ?? Infinity) - (b[1]?.startedAt ?? Infinity));
|
|
200
|
+
for (const [nodeId, result] of entries) {
|
|
201
|
+
if (result?.skipped) {
|
|
202
|
+
lines.push(` ${nodeId} skipped`);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (result?.error) {
|
|
206
|
+
lines.push(` ${nodeId} FAILED ${result.error}`);
|
|
207
|
+
if (result.errorCode) {
|
|
208
|
+
lines.push(` code: ${result.errorCode}${result.errorModel ? ` model: ${result.errorModel}` : ''}`);
|
|
209
|
+
}
|
|
210
|
+
for (const tried of result.triedModels ?? []) {
|
|
211
|
+
lines.push(` tried ${tried.modelId}: ${tried.reason ?? 'failed'}`);
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const meta = [];
|
|
216
|
+
const routing = formatRouting(result?.routing);
|
|
217
|
+
if (routing)
|
|
218
|
+
meta.push(routing);
|
|
219
|
+
if (typeof result?.cost === 'number')
|
|
220
|
+
meta.push(formatCost(result.cost));
|
|
221
|
+
if (result?.tokens)
|
|
222
|
+
meta.push(`${formatTokens(result.tokens)} tokens`);
|
|
223
|
+
if (typeof result?.executionTime === 'number')
|
|
224
|
+
meta.push(formatDuration(result.executionTime));
|
|
225
|
+
lines.push(` ${nodeId} ok${meta.length ? ` [${meta.join(' · ')}]` : ''}`);
|
|
226
|
+
}
|
|
227
|
+
return lines;
|
|
228
|
+
}
|
|
229
|
+
/** The full detail of one autonomous run, for `agents inspect`. */
|
|
230
|
+
export function formatRunDetail(run) {
|
|
231
|
+
const lines = [];
|
|
232
|
+
lines.push(`Run ${run.id ?? '(no id)'}`);
|
|
233
|
+
lines.push(` status: ${run.status ?? 'unknown'}`);
|
|
234
|
+
if (run.mode)
|
|
235
|
+
lines.push(` mode: ${run.mode}`);
|
|
236
|
+
if (run.createdAt)
|
|
237
|
+
lines.push(` started: ${run.createdAt}`);
|
|
238
|
+
if (run.updatedAt)
|
|
239
|
+
lines.push(` updated: ${run.updatedAt}`);
|
|
240
|
+
if (run.conversationId)
|
|
241
|
+
lines.push(` conversation: ${run.conversationId}`);
|
|
242
|
+
lines.push(` steps: ${run.steps?.length ?? run.currentStep ?? 0}${run.maxSteps ? ` of max ${run.maxSteps}` : ''}`);
|
|
243
|
+
lines.push(` cost: ${formatCost(run.totalCost)}`);
|
|
244
|
+
lines.push(` tokens: ${formatTokens(run.totalTokens)}`);
|
|
245
|
+
lines.push(` duration: ${formatDuration(run.executionTime)}`);
|
|
246
|
+
const models = new Set();
|
|
247
|
+
for (const step of run.steps ?? []) {
|
|
248
|
+
const model = modelOf(routingOfStep(step));
|
|
249
|
+
if (model)
|
|
250
|
+
models.add(model);
|
|
251
|
+
}
|
|
252
|
+
if (models.size > 0)
|
|
253
|
+
lines.push(` models: ${[...models].join(', ')}`);
|
|
254
|
+
if (run.error) {
|
|
255
|
+
lines.push('');
|
|
256
|
+
lines.push(` error: ${run.error}`);
|
|
257
|
+
}
|
|
258
|
+
const steps = run.steps ?? [];
|
|
259
|
+
if (steps.length > 0) {
|
|
260
|
+
lines.push('');
|
|
261
|
+
lines.push('Steps:');
|
|
262
|
+
steps.forEach((step, i) => {
|
|
263
|
+
const line = formatStep(step, i);
|
|
264
|
+
if (line)
|
|
265
|
+
lines.push(line);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (run.output != null) {
|
|
269
|
+
lines.push('');
|
|
270
|
+
lines.push('Output:');
|
|
271
|
+
lines.push(typeof run.output === 'string'
|
|
272
|
+
? run.output
|
|
273
|
+
: JSON.stringify(run.output, null, 2));
|
|
274
|
+
}
|
|
275
|
+
return lines.join('\n');
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The hop-by-hop trace the backend assembles at
|
|
279
|
+
* /agents/:id/executions/:executionId/trace. An opaque hop is printed
|
|
280
|
+
* as opaque, never as zero — the backend is deliberate about that and
|
|
281
|
+
* flattening it here would undo the point.
|
|
282
|
+
*/
|
|
283
|
+
export function formatTrace(trace) {
|
|
284
|
+
const lines = [];
|
|
285
|
+
lines.push(`Trace for execution ${trace.executionId ?? '(no id)'}`);
|
|
286
|
+
if (trace.strategyKey) {
|
|
287
|
+
lines.push(` strategy: ${trace.strategyKey}${trace.strategyChosenBy ? ` (chosen by ${trace.strategyChosenBy})` : ''}`);
|
|
288
|
+
}
|
|
289
|
+
if (trace.strategyFallbackReason) {
|
|
290
|
+
lines.push(` fell back because: ${trace.strategyFallbackReason}`);
|
|
291
|
+
}
|
|
292
|
+
for (const step of trace.steps ?? []) {
|
|
293
|
+
lines.push('');
|
|
294
|
+
const head = [step.nodeId];
|
|
295
|
+
if (step.type)
|
|
296
|
+
head.push(step.type);
|
|
297
|
+
if (step.durationMs != null)
|
|
298
|
+
head.push(formatDuration(step.durationMs));
|
|
299
|
+
lines.push(head.join(' '));
|
|
300
|
+
if (step.error)
|
|
301
|
+
lines.push(` error: ${step.error}`);
|
|
302
|
+
if (!step.hops?.length) {
|
|
303
|
+
lines.push(' (no hops recorded — the call was not routed)');
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
for (const hop of step.hops) {
|
|
307
|
+
const cost = hop.opaqueCost
|
|
308
|
+
? 'cost opaque'
|
|
309
|
+
: hop.costEstimateCents != null
|
|
310
|
+
? `${hop.costEstimateCents.toFixed(4)}¢`
|
|
311
|
+
: 'cost not recorded';
|
|
312
|
+
const bits = [`${hop.layer ?? 'hop'}`, hop.chosen ?? '?', cost];
|
|
313
|
+
if (hop.latencyMs != null)
|
|
314
|
+
bits.push(formatDuration(hop.latencyMs));
|
|
315
|
+
lines.push(` ${bits.join(' ')}`);
|
|
316
|
+
if (hop.decidedBy)
|
|
317
|
+
lines.push(` decided by: ${hop.decidedBy}`);
|
|
318
|
+
if (hop.reason)
|
|
319
|
+
lines.push(` reason: ${hop.reason}`);
|
|
320
|
+
if (hop.alternatives?.length) {
|
|
321
|
+
lines.push(` passed over: ${hop.alternatives.join(', ')}`);
|
|
322
|
+
}
|
|
323
|
+
if (hop.divergent) {
|
|
324
|
+
lines.push(` DIVERGENT: asked for ${hop.requestedModel ?? '?'}, served ${hop.servedModel ?? '?'}`);
|
|
325
|
+
}
|
|
326
|
+
if (hop.capabilitiesDropped?.length) {
|
|
327
|
+
lines.push(` capabilities dropped: ${hop.capabilitiesDropped.join(', ')}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const summary = trace.summary;
|
|
332
|
+
if (summary) {
|
|
333
|
+
lines.push('');
|
|
334
|
+
lines.push('Summary:');
|
|
335
|
+
lines.push(` known cost: ${(summary.knownCostCents ?? 0).toFixed(4)}¢`);
|
|
336
|
+
lines.push(` opaque hops: ${summary.opaqueHops ?? 0}`);
|
|
337
|
+
lines.push(` divergences: ${summary.divergences?.length ?? 0}`);
|
|
338
|
+
if (summary.capabilitiesDropped?.length) {
|
|
339
|
+
lines.push(` dropped: ${summary.capabilitiesDropped.join(', ')}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return lines.join('\n');
|
|
343
|
+
}
|
|
344
|
+
/** One agent per block, for `agents list`. */
|
|
345
|
+
export function formatAgentLine(agent) {
|
|
346
|
+
const mode = agent.mode ? ` [${agent.mode}]` : '';
|
|
347
|
+
const status = agent.status ? ` (${agent.status})` : '';
|
|
348
|
+
const desc = agent.description ? `\n ${agent.description}` : '';
|
|
349
|
+
return ` ${agent.name}${mode}${status}${desc}`;
|
|
350
|
+
}
|
|
351
|
+
/** Everything `agents get` knows, so the next question is not "and then?". */
|
|
352
|
+
export function formatAgentDetail(agent) {
|
|
353
|
+
const lines = [];
|
|
354
|
+
lines.push(` ${agent.name}`);
|
|
355
|
+
lines.push(` id: ${agent.id}`);
|
|
356
|
+
if (agent.slug)
|
|
357
|
+
lines.push(` slug: ${agent.slug}`);
|
|
358
|
+
if (agent.mode)
|
|
359
|
+
lines.push(` mode: ${agent.mode}`);
|
|
360
|
+
if (agent.status)
|
|
361
|
+
lines.push(` status: ${agent.status}`);
|
|
362
|
+
if (agent.description)
|
|
363
|
+
lines.push(` desc: ${agent.description}`);
|
|
364
|
+
const model = agent.modelConfig ?? {};
|
|
365
|
+
const modelBits = [];
|
|
366
|
+
if (typeof model.model === 'string')
|
|
367
|
+
modelBits.push(model.model);
|
|
368
|
+
if (typeof model.providerId === 'string')
|
|
369
|
+
modelBits.push(`provider ${model.providerId}`);
|
|
370
|
+
if (model.routing)
|
|
371
|
+
modelBits.push('routed (policy on the agent)');
|
|
372
|
+
if (typeof model.temperature === 'number')
|
|
373
|
+
modelBits.push(`temp ${model.temperature}`);
|
|
374
|
+
if (modelBits.length)
|
|
375
|
+
lines.push(` model: ${modelBits.join(' · ')}`);
|
|
376
|
+
const nodes = agent.pipeline?.nodes ?? [];
|
|
377
|
+
if (nodes.length) {
|
|
378
|
+
const byType = new Map();
|
|
379
|
+
for (const node of nodes)
|
|
380
|
+
byType.set(node.type, (byType.get(node.type) ?? 0) + 1);
|
|
381
|
+
const shape = [...byType.entries()].map(([t, n]) => `${n}x ${t}`).join(', ');
|
|
382
|
+
lines.push(` pipeline: ${nodes.length} node(s) — ${shape}`);
|
|
383
|
+
}
|
|
384
|
+
if (agent.tools?.length) {
|
|
385
|
+
lines.push(` tools: ${agent.tools.map((t) => t.name).join(', ')}`);
|
|
386
|
+
}
|
|
387
|
+
// The single most common first failure: invoking a DRAFT agent.
|
|
388
|
+
if (agent.status && agent.status.toLowerCase() !== 'active') {
|
|
389
|
+
lines.push('');
|
|
390
|
+
lines.push(` This agent is ${agent.status} — activate it before running it.`);
|
|
391
|
+
}
|
|
392
|
+
return lines.join('\n');
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The message for trying to run a non-active agent.
|
|
396
|
+
*
|
|
397
|
+
* The API answers 400 with a JSON body, and the CLI printed the body:
|
|
398
|
+
* `API error 400: {"success":false,"message":"Agent must be active to
|
|
399
|
+
* invoke","error":"AGENT_NOT_ACTIVE"}`. The agent's status is already
|
|
400
|
+
* in hand before the call, so say it plainly and skip the round trip.
|
|
401
|
+
*/
|
|
402
|
+
export function notActiveMessage(agent) {
|
|
403
|
+
return [
|
|
404
|
+
`Agent "${agent.name}" is ${agent.status ?? 'not active'} and cannot be run.`,
|
|
405
|
+
`Activate it, then try again:`,
|
|
406
|
+
` in the app: https://app.almyty.com/agents/${agent.id}`,
|
|
407
|
+
].join('\n');
|
|
408
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
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`).
|