@orchagent/cli 0.3.86 → 0.3.88
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/commands/agent-keys.js +21 -7
- package/dist/commands/agents.js +60 -5
- package/dist/commands/config.js +4 -0
- package/dist/commands/delete.js +2 -2
- package/dist/commands/dev.js +226 -0
- package/dist/commands/diff.js +418 -0
- package/dist/commands/estimate.js +105 -0
- package/dist/commands/fork.js +11 -1
- package/dist/commands/health.js +226 -0
- package/dist/commands/index.js +14 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/init.js +729 -38
- package/dist/commands/metrics.js +137 -0
- package/dist/commands/publish.js +237 -21
- package/dist/commands/replay.js +198 -0
- package/dist/commands/run.js +272 -28
- package/dist/commands/schedule.js +11 -6
- package/dist/commands/test.js +68 -1
- package/dist/commands/trace.js +311 -0
- package/dist/lib/api.js +29 -4
- package/dist/lib/batch-publish.js +223 -0
- package/dist/lib/dev-server.js +425 -0
- package/dist/lib/doctor/checks/environment.js +1 -1
- package/dist/lib/key-store.js +121 -0
- package/dist/lib/spinner.js +50 -0
- package/dist/lib/test-mock-runner.js +334 -0
- package/dist/lib/update-notifier.js +1 -1
- package/package.json +1 -1
- package/src/resources/__pycache__/agent_runner.cpython-311.pyc +0 -0
- package/src/resources/__pycache__/agent_runner.cpython-312.pyc +0 -0
- package/src/resources/__pycache__/test_agent_runner_mocks.cpython-311-pytest-9.0.2.pyc +0 -0
- package/src/resources/__pycache__/test_agent_runner_mocks.cpython-312-pytest-8.4.2.pyc +0 -0
- package/src/resources/agent_runner.py +29 -2
- package/src/resources/test_agent_runner_mocks.py +290 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.computeDiffs = computeDiffs;
|
|
7
|
+
exports.registerDiffCommand = registerDiffCommand;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const config_1 = require("../lib/config");
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
const agent_ref_1 = require("../lib/agent-ref");
|
|
12
|
+
const spinner_1 = require("../lib/spinner");
|
|
13
|
+
// ── Helpers ────────────────────────────────────────────────────
|
|
14
|
+
function extractDependencies(manifest) {
|
|
15
|
+
if (!manifest)
|
|
16
|
+
return [];
|
|
17
|
+
const deps = manifest.dependencies;
|
|
18
|
+
if (!Array.isArray(deps))
|
|
19
|
+
return [];
|
|
20
|
+
return deps
|
|
21
|
+
.filter(d => d && typeof d.id === 'string' && typeof d.version === 'string')
|
|
22
|
+
.map(d => ({ id: d.id, version: d.version }));
|
|
23
|
+
}
|
|
24
|
+
function extractCustomTools(manifest) {
|
|
25
|
+
if (!manifest)
|
|
26
|
+
return [];
|
|
27
|
+
const tools = manifest.custom_tools;
|
|
28
|
+
if (!Array.isArray(tools))
|
|
29
|
+
return [];
|
|
30
|
+
return tools
|
|
31
|
+
.filter(t => t && typeof t.name === 'string')
|
|
32
|
+
.map(t => ({ name: t.name, description: t.description, command: t.command }));
|
|
33
|
+
}
|
|
34
|
+
/** Fetch a normalised snapshot for one agent version. */
|
|
35
|
+
async function fetchSnapshot(config, org, agent, version, workspaceId) {
|
|
36
|
+
// Try public endpoint first
|
|
37
|
+
try {
|
|
38
|
+
const pub = await (0, api_1.getPublicAgent)(config, org, agent, version);
|
|
39
|
+
const raw = pub;
|
|
40
|
+
const manifest = raw.manifest;
|
|
41
|
+
return {
|
|
42
|
+
org,
|
|
43
|
+
name: pub.name,
|
|
44
|
+
version: pub.version,
|
|
45
|
+
type: (pub.type || 'tool'),
|
|
46
|
+
description: (pub.description ?? undefined),
|
|
47
|
+
callable: pub.callable ?? false,
|
|
48
|
+
run_mode: pub.run_mode ?? null,
|
|
49
|
+
execution_engine: pub.execution_engine ?? null,
|
|
50
|
+
supported_providers: pub.supported_providers || ['any'],
|
|
51
|
+
tags: pub.tags || [],
|
|
52
|
+
input_schema: pub.input_schema,
|
|
53
|
+
output_schema: pub.output_schema,
|
|
54
|
+
dependencies: extractDependencies(manifest),
|
|
55
|
+
default_skills: raw.default_skills || [],
|
|
56
|
+
custom_tools: extractCustomTools(manifest),
|
|
57
|
+
required_secrets: raw.required_secrets || [],
|
|
58
|
+
source_url: raw.source_url,
|
|
59
|
+
run_command: raw.run_command,
|
|
60
|
+
default_models: raw.default_models,
|
|
61
|
+
timeout_seconds: manifest?.timeout_seconds,
|
|
62
|
+
max_turns: manifest?.max_turns,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
67
|
+
throw err;
|
|
68
|
+
}
|
|
69
|
+
// Fallback to authenticated endpoint
|
|
70
|
+
if (!config.apiKey) {
|
|
71
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
72
|
+
}
|
|
73
|
+
const userOrg = await (0, api_1.getOrg)(config, workspaceId);
|
|
74
|
+
if (userOrg.slug !== org) {
|
|
75
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
76
|
+
}
|
|
77
|
+
const agents = await (0, api_1.listMyAgents)(config, workspaceId);
|
|
78
|
+
const matching = agents.filter(a => a.name === agent);
|
|
79
|
+
if (matching.length === 0) {
|
|
80
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
81
|
+
}
|
|
82
|
+
let target = matching[0];
|
|
83
|
+
if (version !== 'latest') {
|
|
84
|
+
const found = matching.find(a => a.version === version);
|
|
85
|
+
if (!found)
|
|
86
|
+
throw new api_1.ApiError(`Agent '${org}/${agent}@${version}' not found`, 404);
|
|
87
|
+
target = found;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
target = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
91
|
+
}
|
|
92
|
+
const manifest = target.manifest;
|
|
93
|
+
return {
|
|
94
|
+
org,
|
|
95
|
+
name: target.name,
|
|
96
|
+
version: target.version,
|
|
97
|
+
type: target.type,
|
|
98
|
+
description: target.description,
|
|
99
|
+
callable: target.callable ?? false,
|
|
100
|
+
run_mode: target.run_mode ?? null,
|
|
101
|
+
execution_engine: target.execution_engine ?? null,
|
|
102
|
+
supported_providers: target.supported_providers || ['any'],
|
|
103
|
+
tags: target.tags || [],
|
|
104
|
+
input_schema: target.input_schema,
|
|
105
|
+
output_schema: target.output_schema,
|
|
106
|
+
prompt: target.prompt,
|
|
107
|
+
dependencies: extractDependencies(manifest),
|
|
108
|
+
default_skills: target.default_skills || [],
|
|
109
|
+
custom_tools: extractCustomTools(manifest),
|
|
110
|
+
required_secrets: target.required_secrets || [],
|
|
111
|
+
source_url: target.source_url,
|
|
112
|
+
run_command: target.run_command,
|
|
113
|
+
default_models: target.default_models,
|
|
114
|
+
timeout_seconds: manifest?.timeout_seconds,
|
|
115
|
+
max_turns: manifest?.max_turns,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// ── Diffing ────────────────────────────────────────────────────
|
|
119
|
+
function isEqual(a, b) {
|
|
120
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
121
|
+
}
|
|
122
|
+
function isEmpty(val) {
|
|
123
|
+
if (val === undefined || val === null)
|
|
124
|
+
return true;
|
|
125
|
+
if (Array.isArray(val))
|
|
126
|
+
return val.length === 0;
|
|
127
|
+
if (typeof val === 'object')
|
|
128
|
+
return Object.keys(val).length === 0;
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
/** Compare two snapshots field-by-field and return a list of diffs. */
|
|
132
|
+
function computeDiffs(a, b) {
|
|
133
|
+
const diffs = [];
|
|
134
|
+
// Simple scalar fields
|
|
135
|
+
const scalars = [
|
|
136
|
+
{ field: 'type', key: 'type' },
|
|
137
|
+
{ field: 'description', key: 'description' },
|
|
138
|
+
{ field: 'callable', key: 'callable' },
|
|
139
|
+
{ field: 'run_mode', key: 'run_mode' },
|
|
140
|
+
{ field: 'execution_engine', key: 'execution_engine' },
|
|
141
|
+
{ field: 'source_url', key: 'source_url' },
|
|
142
|
+
{ field: 'run_command', key: 'run_command' },
|
|
143
|
+
{ field: 'timeout_seconds', key: 'timeout_seconds' },
|
|
144
|
+
{ field: 'max_turns', key: 'max_turns' },
|
|
145
|
+
];
|
|
146
|
+
for (const { field, key } of scalars) {
|
|
147
|
+
const oldVal = a[key];
|
|
148
|
+
const newVal = b[key];
|
|
149
|
+
if (!isEqual(oldVal, newVal)) {
|
|
150
|
+
if (isEmpty(oldVal) && !isEmpty(newVal)) {
|
|
151
|
+
diffs.push({ field, kind: 'added', new: newVal });
|
|
152
|
+
}
|
|
153
|
+
else if (!isEmpty(oldVal) && isEmpty(newVal)) {
|
|
154
|
+
diffs.push({ field, kind: 'removed', old: oldVal });
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
diffs.push({ field, kind: 'changed', old: oldVal, new: newVal });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Array fields — show as changed with old/new
|
|
162
|
+
const arrays = [
|
|
163
|
+
{ field: 'supported_providers', key: 'supported_providers' },
|
|
164
|
+
{ field: 'tags', key: 'tags' },
|
|
165
|
+
{ field: 'default_skills', key: 'default_skills' },
|
|
166
|
+
{ field: 'required_secrets', key: 'required_secrets' },
|
|
167
|
+
];
|
|
168
|
+
for (const { field, key } of arrays) {
|
|
169
|
+
const oldArr = (a[key] || []);
|
|
170
|
+
const newArr = (b[key] || []);
|
|
171
|
+
if (!isEqual(oldArr, newArr)) {
|
|
172
|
+
if (oldArr.length === 0 && newArr.length > 0) {
|
|
173
|
+
diffs.push({ field, kind: 'added', new: newArr });
|
|
174
|
+
}
|
|
175
|
+
else if (oldArr.length > 0 && newArr.length === 0) {
|
|
176
|
+
diffs.push({ field, kind: 'removed', old: oldArr });
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
diffs.push({ field, kind: 'changed', old: oldArr, new: newArr });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Object fields — schemas, default_models
|
|
184
|
+
const objects = [
|
|
185
|
+
{ field: 'input_schema', key: 'input_schema' },
|
|
186
|
+
{ field: 'output_schema', key: 'output_schema' },
|
|
187
|
+
{ field: 'default_models', key: 'default_models' },
|
|
188
|
+
];
|
|
189
|
+
for (const { field, key } of objects) {
|
|
190
|
+
const oldObj = a[key];
|
|
191
|
+
const newObj = b[key];
|
|
192
|
+
if (!isEqual(oldObj, newObj)) {
|
|
193
|
+
if (isEmpty(oldObj) && !isEmpty(newObj)) {
|
|
194
|
+
diffs.push({ field, kind: 'added', new: newObj });
|
|
195
|
+
}
|
|
196
|
+
else if (!isEmpty(oldObj) && isEmpty(newObj)) {
|
|
197
|
+
diffs.push({ field, kind: 'removed', old: oldObj });
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
diffs.push({ field, kind: 'changed', old: oldObj, new: newObj });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Structured arrays — dependencies, custom_tools
|
|
205
|
+
if (!isEqual(a.dependencies, b.dependencies)) {
|
|
206
|
+
if (a.dependencies.length === 0 && b.dependencies.length > 0) {
|
|
207
|
+
diffs.push({ field: 'dependencies', kind: 'added', new: b.dependencies });
|
|
208
|
+
}
|
|
209
|
+
else if (a.dependencies.length > 0 && b.dependencies.length === 0) {
|
|
210
|
+
diffs.push({ field: 'dependencies', kind: 'removed', old: a.dependencies });
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
diffs.push({ field: 'dependencies', kind: 'changed', old: a.dependencies, new: b.dependencies });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!isEqual(a.custom_tools, b.custom_tools)) {
|
|
217
|
+
if (a.custom_tools.length === 0 && b.custom_tools.length > 0) {
|
|
218
|
+
diffs.push({ field: 'custom_tools', kind: 'added', new: b.custom_tools });
|
|
219
|
+
}
|
|
220
|
+
else if (a.custom_tools.length > 0 && b.custom_tools.length === 0) {
|
|
221
|
+
diffs.push({ field: 'custom_tools', kind: 'removed', old: a.custom_tools });
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
diffs.push({ field: 'custom_tools', kind: 'changed', old: a.custom_tools, new: b.custom_tools });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Prompt (may only be available for owned agents)
|
|
228
|
+
if (a.prompt !== undefined && b.prompt !== undefined && a.prompt !== b.prompt) {
|
|
229
|
+
diffs.push({ field: 'prompt', kind: 'changed', old: a.prompt, new: b.prompt });
|
|
230
|
+
}
|
|
231
|
+
else if (a.prompt === undefined && b.prompt !== undefined) {
|
|
232
|
+
diffs.push({ field: 'prompt', kind: 'added', new: b.prompt });
|
|
233
|
+
}
|
|
234
|
+
else if (a.prompt !== undefined && b.prompt === undefined) {
|
|
235
|
+
diffs.push({ field: 'prompt', kind: 'removed', old: a.prompt });
|
|
236
|
+
}
|
|
237
|
+
return diffs;
|
|
238
|
+
}
|
|
239
|
+
// ── Formatting ─────────────────────────────────────────────────
|
|
240
|
+
function formatValue(val) {
|
|
241
|
+
if (val === undefined || val === null)
|
|
242
|
+
return chalk_1.default.gray('(none)');
|
|
243
|
+
if (typeof val === 'boolean')
|
|
244
|
+
return val ? chalk_1.default.green('true') : chalk_1.default.red('false');
|
|
245
|
+
if (typeof val === 'string') {
|
|
246
|
+
if (val.length > 120)
|
|
247
|
+
return val.slice(0, 117) + '...';
|
|
248
|
+
return val;
|
|
249
|
+
}
|
|
250
|
+
if (Array.isArray(val)) {
|
|
251
|
+
if (val.length === 0)
|
|
252
|
+
return chalk_1.default.gray('[]');
|
|
253
|
+
// Array of strings
|
|
254
|
+
if (typeof val[0] === 'string')
|
|
255
|
+
return val.join(', ');
|
|
256
|
+
// Array of objects — compact JSON
|
|
257
|
+
return JSON.stringify(val, null, 2);
|
|
258
|
+
}
|
|
259
|
+
if (typeof val === 'object')
|
|
260
|
+
return JSON.stringify(val, null, 2);
|
|
261
|
+
return String(val);
|
|
262
|
+
}
|
|
263
|
+
function formatPromptDiff(oldPrompt, newPrompt) {
|
|
264
|
+
const oldLines = (oldPrompt || '').split('\n');
|
|
265
|
+
const newLines = (newPrompt || '').split('\n');
|
|
266
|
+
const lines = [];
|
|
267
|
+
// Simple line-by-line diff (not LCS — good enough for prompt comparison)
|
|
268
|
+
const maxLines = Math.max(oldLines.length, newLines.length);
|
|
269
|
+
const oldSet = new Set(oldLines);
|
|
270
|
+
const newSet = new Set(newLines);
|
|
271
|
+
for (const line of oldLines) {
|
|
272
|
+
if (!newSet.has(line)) {
|
|
273
|
+
lines.push(chalk_1.default.red(` - ${line}`));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
for (const line of newLines) {
|
|
277
|
+
if (!oldSet.has(line)) {
|
|
278
|
+
lines.push(chalk_1.default.green(` + ${line}`));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (lines.length === 0) {
|
|
282
|
+
// Lines are the same but order changed
|
|
283
|
+
lines.push(chalk_1.default.yellow(' (line order changed)'));
|
|
284
|
+
}
|
|
285
|
+
// Truncate if very large
|
|
286
|
+
if (lines.length > 40) {
|
|
287
|
+
const shown = lines.slice(0, 40);
|
|
288
|
+
shown.push(chalk_1.default.gray(` ... and ${lines.length - 40} more lines`));
|
|
289
|
+
return shown.join('\n');
|
|
290
|
+
}
|
|
291
|
+
return lines.join('\n');
|
|
292
|
+
}
|
|
293
|
+
function formatSchemaDiff(field, oldSchema, newSchema) {
|
|
294
|
+
const lines = [];
|
|
295
|
+
const oldProps = oldSchema?.properties || {};
|
|
296
|
+
const newProps = newSchema?.properties || {};
|
|
297
|
+
const oldRequired = new Set(oldSchema?.required || []);
|
|
298
|
+
const newRequired = new Set(newSchema?.required || []);
|
|
299
|
+
const allKeys = new Set([...Object.keys(oldProps), ...Object.keys(newProps)]);
|
|
300
|
+
for (const key of allKeys) {
|
|
301
|
+
const inOld = key in oldProps;
|
|
302
|
+
const inNew = key in newProps;
|
|
303
|
+
if (!inOld && inNew) {
|
|
304
|
+
const reqMark = newRequired.has(key) ? '' : '?';
|
|
305
|
+
lines.push(chalk_1.default.green(` + ${key}${reqMark}: ${newProps[key].type || 'any'}`));
|
|
306
|
+
}
|
|
307
|
+
else if (inOld && !inNew) {
|
|
308
|
+
const reqMark = oldRequired.has(key) ? '' : '?';
|
|
309
|
+
lines.push(chalk_1.default.red(` - ${key}${reqMark}: ${oldProps[key].type || 'any'}`));
|
|
310
|
+
}
|
|
311
|
+
else if (inOld && inNew) {
|
|
312
|
+
const oldType = oldProps[key].type || 'any';
|
|
313
|
+
const newType = newProps[key].type || 'any';
|
|
314
|
+
const wasReq = oldRequired.has(key);
|
|
315
|
+
const isReq = newRequired.has(key);
|
|
316
|
+
if (oldType !== newType || wasReq !== isReq) {
|
|
317
|
+
const oldMark = wasReq ? '' : '?';
|
|
318
|
+
const newMark = isReq ? '' : '?';
|
|
319
|
+
lines.push(chalk_1.default.red(` - ${key}${oldMark}: ${oldType}`));
|
|
320
|
+
lines.push(chalk_1.default.green(` + ${key}${newMark}: ${newType}`));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return lines.join('\n');
|
|
325
|
+
}
|
|
326
|
+
function printDiffs(refA, refB, diffs) {
|
|
327
|
+
process.stdout.write('\n');
|
|
328
|
+
process.stdout.write(chalk_1.default.bold(`${refA} ${chalk_1.default.yellow('→')} ${refB}`) + '\n');
|
|
329
|
+
process.stdout.write('='.repeat(50) + '\n\n');
|
|
330
|
+
if (diffs.length === 0) {
|
|
331
|
+
process.stdout.write(chalk_1.default.green('No differences found.') + '\n');
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
process.stdout.write(`${chalk_1.default.bold(String(diffs.length))} ${diffs.length === 1 ? 'change' : 'changes'}:\n\n`);
|
|
335
|
+
for (const diff of diffs) {
|
|
336
|
+
// Schema fields get special formatting
|
|
337
|
+
if ((diff.field === 'input_schema' || diff.field === 'output_schema') && diff.kind === 'changed') {
|
|
338
|
+
process.stdout.write(chalk_1.default.cyan(`~ ${diff.field}:`) + '\n');
|
|
339
|
+
process.stdout.write(formatSchemaDiff(diff.field, diff.old, diff.new) + '\n\n');
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
// Prompt gets special formatting
|
|
343
|
+
if (diff.field === 'prompt' && diff.kind === 'changed') {
|
|
344
|
+
process.stdout.write(chalk_1.default.cyan(`~ prompt:`) + '\n');
|
|
345
|
+
process.stdout.write(formatPromptDiff(diff.old, diff.new) + '\n\n');
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
// Standard fields
|
|
349
|
+
const prefix = diff.kind === 'added' ? chalk_1.default.green('+')
|
|
350
|
+
: diff.kind === 'removed' ? chalk_1.default.red('-')
|
|
351
|
+
: chalk_1.default.cyan('~');
|
|
352
|
+
if (diff.kind === 'added') {
|
|
353
|
+
process.stdout.write(`${prefix} ${chalk_1.default.bold(diff.field)}: ${formatValue(diff.new)}\n`);
|
|
354
|
+
}
|
|
355
|
+
else if (diff.kind === 'removed') {
|
|
356
|
+
process.stdout.write(`${prefix} ${chalk_1.default.bold(diff.field)}: ${formatValue(diff.old)}\n`);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
process.stdout.write(`${prefix} ${chalk_1.default.bold(diff.field)}: ${formatValue(diff.old)} ${chalk_1.default.yellow('→')} ${formatValue(diff.new)}\n`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// ── Ref parsing ────────────────────────────────────────────────
|
|
364
|
+
/**
|
|
365
|
+
* Parse second ref which can be:
|
|
366
|
+
* - Full ref: "org/agent@version"
|
|
367
|
+
* - Version-only shorthand: "v2" (inherits org/agent from first ref)
|
|
368
|
+
*/
|
|
369
|
+
function parseSecondRef(value, firstOrg, firstName) {
|
|
370
|
+
// If it contains '/', treat as full ref
|
|
371
|
+
if (value.includes('/')) {
|
|
372
|
+
return (0, agent_ref_1.parseAgentRef)(value);
|
|
373
|
+
}
|
|
374
|
+
// Otherwise treat as a version shorthand for the same agent
|
|
375
|
+
return { org: firstOrg, agent: firstName, version: value };
|
|
376
|
+
}
|
|
377
|
+
// ── Command ────────────────────────────────────────────────────
|
|
378
|
+
function registerDiffCommand(program) {
|
|
379
|
+
program
|
|
380
|
+
.command('diff <ref1> [ref2]')
|
|
381
|
+
.description('Compare two versions of an agent')
|
|
382
|
+
.option('--json', 'Output as JSON')
|
|
383
|
+
.action(async (ref1Arg, ref2Arg, options) => {
|
|
384
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
385
|
+
const ref1 = (0, agent_ref_1.parseAgentRef)(ref1Arg);
|
|
386
|
+
let ref2;
|
|
387
|
+
if (ref2Arg) {
|
|
388
|
+
ref2 = parseSecondRef(ref2Arg, ref1.org, ref1.agent);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// No second ref — compare ref1 against latest
|
|
392
|
+
if (ref1.version === 'latest') {
|
|
393
|
+
throw new api_1.ApiError('Two versions are required. Use: orch diff org/agent@v1 v2', 400);
|
|
394
|
+
}
|
|
395
|
+
ref2 = { org: ref1.org, agent: ref1.agent, version: 'latest' };
|
|
396
|
+
}
|
|
397
|
+
// Resolve workspace
|
|
398
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, ref1.org);
|
|
399
|
+
// Fetch both versions in parallel
|
|
400
|
+
const [snapA, snapB] = await (0, spinner_1.withSpinner)('Fetching agent versions', () => Promise.all([
|
|
401
|
+
fetchSnapshot(config, ref1.org, ref1.agent, ref1.version, workspaceId),
|
|
402
|
+
fetchSnapshot(config, ref2.org, ref2.agent, ref2.version, ref2.org === ref1.org ? workspaceId : undefined),
|
|
403
|
+
]), { successText: 'Fetched both versions' });
|
|
404
|
+
const refALabel = `${snapA.org}/${snapA.name}@${snapA.version}`;
|
|
405
|
+
const refBLabel = `${snapB.org}/${snapB.name}@${snapB.version}`;
|
|
406
|
+
const diffs = computeDiffs(snapA, snapB);
|
|
407
|
+
if (options.json) {
|
|
408
|
+
process.stdout.write(JSON.stringify({
|
|
409
|
+
from: refALabel,
|
|
410
|
+
to: refBLabel,
|
|
411
|
+
identical: diffs.length === 0,
|
|
412
|
+
changes: diffs,
|
|
413
|
+
}, null, 2) + '\n');
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
printDiffs(refALabel, refBLabel, diffs);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerEstimateCommand = registerEstimateCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const config_1 = require("../lib/config");
|
|
9
|
+
const api_1 = require("../lib/api");
|
|
10
|
+
const agent_ref_1 = require("../lib/agent-ref");
|
|
11
|
+
const output_1 = require("../lib/output");
|
|
12
|
+
function formatUsd(amount) {
|
|
13
|
+
if (amount < 0.001)
|
|
14
|
+
return '<$0.001';
|
|
15
|
+
if (amount < 0.01)
|
|
16
|
+
return `$${amount.toFixed(4)}`;
|
|
17
|
+
if (amount < 1)
|
|
18
|
+
return `$${amount.toFixed(3)}`;
|
|
19
|
+
return `$${amount.toFixed(2)}`;
|
|
20
|
+
}
|
|
21
|
+
function formatDuration(ms) {
|
|
22
|
+
if (ms < 1000)
|
|
23
|
+
return `${Math.round(ms)}ms`;
|
|
24
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
25
|
+
}
|
|
26
|
+
function formatTokens(count) {
|
|
27
|
+
if (count < 1000)
|
|
28
|
+
return `${Math.round(count)}`;
|
|
29
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
30
|
+
}
|
|
31
|
+
function registerEstimateCommand(program) {
|
|
32
|
+
program
|
|
33
|
+
.command('estimate <agent>')
|
|
34
|
+
.description('Show estimated cost for running an agent')
|
|
35
|
+
.option('--json', 'Output as JSON')
|
|
36
|
+
.action(async (agentArg, options) => {
|
|
37
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
38
|
+
const { org, agent, version } = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
39
|
+
if (!org) {
|
|
40
|
+
process.stderr.write(chalk_1.default.red('Error: org/agent format required (e.g. myorg/my-agent)\n'));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
let data;
|
|
44
|
+
try {
|
|
45
|
+
data = await (0, api_1.getAgentCostEstimate)(config, org, agent, version);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (err instanceof api_1.ApiError && err.status === 404) {
|
|
49
|
+
process.stderr.write(chalk_1.default.red(`Agent '${org}/${agent}@${version}' not found\n`));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
if (options.json) {
|
|
55
|
+
(0, output_1.printJson)(data);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const est = data.estimate;
|
|
59
|
+
process.stdout.write('\n');
|
|
60
|
+
process.stdout.write(`${chalk_1.default.bold(data.agent)}\n`);
|
|
61
|
+
process.stdout.write('='.repeat(40) + '\n\n');
|
|
62
|
+
process.stdout.write(`Type: ${data.type}\n`);
|
|
63
|
+
if (data.execution_engine) {
|
|
64
|
+
process.stdout.write(`Engine: ${data.execution_engine}\n`);
|
|
65
|
+
}
|
|
66
|
+
process.stdout.write(`Providers: ${data.supported_providers.join(', ')}\n`);
|
|
67
|
+
if (est.sample_size === 0) {
|
|
68
|
+
process.stdout.write('\n' + chalk_1.default.yellow('No run history available for cost estimation.\n'));
|
|
69
|
+
process.stdout.write(chalk_1.default.gray('Run the agent once and re-check for estimates.\n'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(`\n${chalk_1.default.bold('Cost Estimate')} ${chalk_1.default.gray(`(${est.sample_size} runs, last ${est.period_days}d)`)}\n`);
|
|
73
|
+
process.stdout.write('-'.repeat(40) + '\n');
|
|
74
|
+
// Main cost stats
|
|
75
|
+
process.stdout.write(` Average: ${chalk_1.default.green(formatUsd(est.avg_cost_usd))}\n`);
|
|
76
|
+
process.stdout.write(` Median: ${chalk_1.default.green(formatUsd(est.p50_cost_usd))}\n`);
|
|
77
|
+
process.stdout.write(` 95th pct: ${chalk_1.default.yellow(formatUsd(est.p95_cost_usd))}\n`);
|
|
78
|
+
// Token averages
|
|
79
|
+
if (est.avg_input_tokens || est.avg_output_tokens) {
|
|
80
|
+
process.stdout.write(`\n${chalk_1.default.bold('Tokens (avg per run)')}\n`);
|
|
81
|
+
process.stdout.write(` Input: ${formatTokens(est.avg_input_tokens)}\n`);
|
|
82
|
+
process.stdout.write(` Output: ${formatTokens(est.avg_output_tokens)}\n`);
|
|
83
|
+
}
|
|
84
|
+
// Duration
|
|
85
|
+
if (est.avg_duration_ms) {
|
|
86
|
+
process.stdout.write(`\n${chalk_1.default.bold('Duration')}\n`);
|
|
87
|
+
process.stdout.write(` Average: ${formatDuration(est.avg_duration_ms)}\n`);
|
|
88
|
+
}
|
|
89
|
+
// Success rate
|
|
90
|
+
if (est.success_rate !== undefined) {
|
|
91
|
+
const rateColor = est.success_rate >= 95 ? chalk_1.default.green : est.success_rate >= 80 ? chalk_1.default.yellow : chalk_1.default.red;
|
|
92
|
+
process.stdout.write(` Success: ${rateColor(est.success_rate + '%')}\n`);
|
|
93
|
+
}
|
|
94
|
+
// Provider breakdown
|
|
95
|
+
if (est.provider_breakdown && est.provider_breakdown.length > 0) {
|
|
96
|
+
process.stdout.write(`\n${chalk_1.default.bold('By Provider')}\n`);
|
|
97
|
+
for (const p of est.provider_breakdown) {
|
|
98
|
+
const model = p.model !== 'unknown' ? ` (${p.model})` : '';
|
|
99
|
+
process.stdout.write(` ${chalk_1.default.cyan(p.provider)}${chalk_1.default.gray(model)}: ` +
|
|
100
|
+
`${formatUsd(p.avg_cost_usd)} avg · ${p.runs} runs\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
process.stdout.write('\n');
|
|
104
|
+
});
|
|
105
|
+
}
|
package/dist/commands/fork.js
CHANGED
|
@@ -11,6 +11,7 @@ const agent_ref_1 = require("../lib/agent-ref");
|
|
|
11
11
|
const errors_1 = require("../lib/errors");
|
|
12
12
|
const analytics_1 = require("../lib/analytics");
|
|
13
13
|
const output_1 = require("../lib/output");
|
|
14
|
+
const key_store_1 = require("../lib/key-store");
|
|
14
15
|
function getWorkspaceAuthError(err) {
|
|
15
16
|
if (!(err instanceof api_1.ApiError) || (err.status !== 401 && err.status !== 403)) {
|
|
16
17
|
return null;
|
|
@@ -101,8 +102,17 @@ Examples:
|
|
|
101
102
|
write(` Workspace: ${targetWorkspace.name} (${targetWorkspace.slug})\n`);
|
|
102
103
|
}
|
|
103
104
|
if (result.service_key) {
|
|
104
|
-
write(`\nService key
|
|
105
|
+
write(`\nService key:\n`);
|
|
105
106
|
write(` ${result.service_key}\n`);
|
|
107
|
+
try {
|
|
108
|
+
const keyPrefix = result.service_key.substring(0, 12);
|
|
109
|
+
const savedPath = await (0, key_store_1.saveServiceKey)(targetOrgSlug, forked.name, forked.version, result.service_key, keyPrefix);
|
|
110
|
+
write(` ${chalk_1.default.gray(`Saved to ${savedPath}`)}\n`);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
write(` ${chalk_1.default.yellow('Could not save key locally. Copy it now — it cannot be retrieved from the server.')}\n`);
|
|
114
|
+
}
|
|
115
|
+
write(` Retrieve later: ${chalk_1.default.cyan(`orch agent-keys list ${targetOrgSlug}/${forked.name}`)}\n`);
|
|
106
116
|
}
|
|
107
117
|
});
|
|
108
118
|
}
|