@orchagent/cli 0.3.54 → 0.3.55
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/index.js +4 -0
- package/dist/commands/init.js +73 -52
- package/dist/commands/publish.js +133 -74
- package/dist/commands/run.js +87 -31
- package/dist/commands/schedule.js +128 -1
- package/dist/commands/service.js +401 -0
- package/dist/commands/transfer.js +135 -0
- package/dist/lib/api.js +18 -1
- package/package.json +1 -1
package/dist/commands/run.js
CHANGED
|
@@ -80,6 +80,28 @@ function parseAgentRef(value) {
|
|
|
80
80
|
}
|
|
81
81
|
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
82
82
|
}
|
|
83
|
+
function canonicalAgentType(typeValue) {
|
|
84
|
+
const normalized = (typeValue || 'agent').toLowerCase();
|
|
85
|
+
return normalized === 'skill' ? 'skill' : 'agent';
|
|
86
|
+
}
|
|
87
|
+
function resolveExecutionEngine(agentData) {
|
|
88
|
+
if (agentData.execution_engine === 'direct_llm' || agentData.execution_engine === 'managed_loop' || agentData.execution_engine === 'code_runtime') {
|
|
89
|
+
return agentData.execution_engine;
|
|
90
|
+
}
|
|
91
|
+
const runtimeCommand = agentData.runtime?.command;
|
|
92
|
+
if (runtimeCommand && runtimeCommand.trim())
|
|
93
|
+
return 'code_runtime';
|
|
94
|
+
if (agentData.loop && Object.keys(agentData.loop).length > 0)
|
|
95
|
+
return 'managed_loop';
|
|
96
|
+
const normalized = (agentData.type || '').toLowerCase();
|
|
97
|
+
if (normalized === 'tool' || normalized === 'code')
|
|
98
|
+
return 'code_runtime';
|
|
99
|
+
if (normalized === 'agentic')
|
|
100
|
+
return 'managed_loop';
|
|
101
|
+
if (normalized === 'skill')
|
|
102
|
+
return 'direct_llm';
|
|
103
|
+
return 'direct_llm';
|
|
104
|
+
}
|
|
83
105
|
// ─── Validation helpers ─────────────────────────────────────────────────────
|
|
84
106
|
async function validateFilePath(filePath) {
|
|
85
107
|
const stat = await promises_1.default.stat(filePath);
|
|
@@ -471,6 +493,9 @@ async function downloadAgent(config, org, agent, version) {
|
|
|
471
493
|
return {
|
|
472
494
|
id: targetAgent.id,
|
|
473
495
|
type: targetAgent.type,
|
|
496
|
+
run_mode: targetAgent.run_mode ?? null,
|
|
497
|
+
execution_engine: targetAgent.execution_engine ?? null,
|
|
498
|
+
callable: targetAgent.callable,
|
|
474
499
|
name: targetAgent.name,
|
|
475
500
|
version: targetAgent.version,
|
|
476
501
|
description: targetAgent.description,
|
|
@@ -506,7 +531,13 @@ async function checkDependencies(config, dependencies) {
|
|
|
506
531
|
const [org, agent] = dep.id.split('/');
|
|
507
532
|
try {
|
|
508
533
|
const agentData = await downloadAgent(config, org, agent, dep.version);
|
|
509
|
-
const
|
|
534
|
+
const canonicalType = canonicalAgentType(agentData.type);
|
|
535
|
+
const engine = resolveExecutionEngine(agentData);
|
|
536
|
+
const downloadable = Boolean(canonicalType === 'skill' ||
|
|
537
|
+
engine !== 'code_runtime' ||
|
|
538
|
+
agentData.source_url ||
|
|
539
|
+
agentData.pip_package ||
|
|
540
|
+
agentData.has_bundle);
|
|
510
541
|
results.push({ dep, downloadable, agentData });
|
|
511
542
|
}
|
|
512
543
|
catch {
|
|
@@ -577,7 +608,7 @@ async function downloadDependenciesRecursively(config, depStatuses, visited = ne
|
|
|
577
608
|
if (status.agentData.has_bundle) {
|
|
578
609
|
await saveBundleLocally(config, org, agent, status.dep.version, status.agentData.id);
|
|
579
610
|
}
|
|
580
|
-
if (status.agentData
|
|
611
|
+
if (resolveExecutionEngine(status.agentData) === 'code_runtime' && (status.agentData.source_url || status.agentData.pip_package)) {
|
|
581
612
|
await installTool(status.agentData);
|
|
582
613
|
}
|
|
583
614
|
}, { successText: `Downloaded ${depRef}` });
|
|
@@ -1174,7 +1205,7 @@ async function saveAgentLocally(org, agent, agentData) {
|
|
|
1174
1205
|
const agentDir = path_1.default.join(AGENTS_DIR, org, agent);
|
|
1175
1206
|
await promises_1.default.mkdir(agentDir, { recursive: true });
|
|
1176
1207
|
await promises_1.default.writeFile(path_1.default.join(agentDir, 'agent.json'), JSON.stringify(agentData, null, 2));
|
|
1177
|
-
if (agentData
|
|
1208
|
+
if (resolveExecutionEngine(agentData) !== 'code_runtime' && agentData.prompt) {
|
|
1178
1209
|
await promises_1.default.writeFile(path_1.default.join(agentDir, 'prompt.md'), agentData.prompt);
|
|
1179
1210
|
}
|
|
1180
1211
|
if (agentData.files) {
|
|
@@ -1264,13 +1295,20 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1264
1295
|
`To run a local agent, the directory must contain orchagent.json.\n` +
|
|
1265
1296
|
`Create one with: orch init`);
|
|
1266
1297
|
}
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1298
|
+
const manifestType = manifest.type || 'agent';
|
|
1299
|
+
const localType = canonicalAgentType(manifestType);
|
|
1300
|
+
const localEngine = resolveExecutionEngine({
|
|
1301
|
+
type: manifestType,
|
|
1302
|
+
execution_engine: manifest.execution_engine || null,
|
|
1303
|
+
runtime: manifest.runtime || null,
|
|
1304
|
+
loop: manifest.loop || null,
|
|
1305
|
+
});
|
|
1306
|
+
if (localType === 'skill') {
|
|
1269
1307
|
throw new errors_1.CliError('Skills cannot be run directly.\n\n' +
|
|
1270
1308
|
'Skills are instructions meant to be injected into AI agent contexts.\n' +
|
|
1271
1309
|
`Install with: orchagent skill install <org>/<skill>`);
|
|
1272
1310
|
}
|
|
1273
|
-
if (
|
|
1311
|
+
if (localEngine === 'managed_loop') {
|
|
1274
1312
|
// Read prompt.md
|
|
1275
1313
|
const promptPath = path_1.default.join(resolved, 'prompt.md');
|
|
1276
1314
|
let agentPrompt;
|
|
@@ -1323,7 +1361,7 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1323
1361
|
await executeAgentLocally(resolved, agentPrompt, agentInputData, agentOutputSchema, customTools, manifest, config, options.provider, options.model);
|
|
1324
1362
|
return;
|
|
1325
1363
|
}
|
|
1326
|
-
if (
|
|
1364
|
+
if (localEngine === 'direct_llm') {
|
|
1327
1365
|
// Read prompt.md
|
|
1328
1366
|
const promptPath = path_1.default.join(resolved, 'prompt.md');
|
|
1329
1367
|
let prompt;
|
|
@@ -1347,7 +1385,9 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1347
1385
|
}
|
|
1348
1386
|
// Build AgentDownload from local files
|
|
1349
1387
|
const agentData = {
|
|
1350
|
-
type: '
|
|
1388
|
+
type: 'agent',
|
|
1389
|
+
execution_engine: 'direct_llm',
|
|
1390
|
+
run_mode: manifest.run_mode || 'on_demand',
|
|
1351
1391
|
name: manifest.name || path_1.default.basename(resolved),
|
|
1352
1392
|
version: manifest.version || 'local',
|
|
1353
1393
|
description: manifest.description,
|
|
@@ -1389,7 +1429,7 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1389
1429
|
(0, output_1.printJson)(result);
|
|
1390
1430
|
return;
|
|
1391
1431
|
}
|
|
1392
|
-
//
|
|
1432
|
+
// Code runtime agents with bundle
|
|
1393
1433
|
const entrypoint = manifest.entrypoint || 'sandbox_main.py';
|
|
1394
1434
|
const entrypointPath = path_1.default.join(resolved, entrypoint);
|
|
1395
1435
|
try {
|
|
@@ -1399,7 +1439,9 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1399
1439
|
// No local entrypoint — try run_command
|
|
1400
1440
|
if (manifest.run_command) {
|
|
1401
1441
|
const agentData = {
|
|
1402
|
-
type: '
|
|
1442
|
+
type: 'agent',
|
|
1443
|
+
execution_engine: 'code_runtime',
|
|
1444
|
+
run_mode: manifest.run_mode || 'on_demand',
|
|
1403
1445
|
name: manifest.name || path_1.default.basename(resolved),
|
|
1404
1446
|
version: manifest.version || 'local',
|
|
1405
1447
|
supported_providers: manifest.supported_providers || ['any'],
|
|
@@ -1412,13 +1454,15 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1412
1454
|
}
|
|
1413
1455
|
throw new errors_1.CliError(`No entrypoint found in ${resolved}\n\n` +
|
|
1414
1456
|
`Expected: ${entrypoint}\n` +
|
|
1415
|
-
`For
|
|
1457
|
+
`For code runtime agents, ensure the directory contains the entrypoint file\n` +
|
|
1416
1458
|
`or has run_command set in orchagent.json.`);
|
|
1417
1459
|
}
|
|
1418
1460
|
// Execute bundle-style from local directory
|
|
1419
1461
|
const config = await (0, config_1.getResolvedConfig)();
|
|
1420
1462
|
const agentData = {
|
|
1421
|
-
type: '
|
|
1463
|
+
type: 'agent',
|
|
1464
|
+
execution_engine: 'code_runtime',
|
|
1465
|
+
run_mode: manifest.run_mode || 'on_demand',
|
|
1422
1466
|
name: manifest.name || path_1.default.basename(resolved),
|
|
1423
1467
|
version: manifest.version || 'local',
|
|
1424
1468
|
supported_providers: manifest.supported_providers || ['any'],
|
|
@@ -1561,6 +1605,13 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1561
1605
|
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
1562
1606
|
}
|
|
1563
1607
|
const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version);
|
|
1608
|
+
const cloudType = canonicalAgentType(agentMeta.type);
|
|
1609
|
+
const cloudEngine = resolveExecutionEngine({
|
|
1610
|
+
type: agentMeta.type,
|
|
1611
|
+
execution_engine: agentMeta.execution_engine ?? null,
|
|
1612
|
+
runtime: agentMeta.runtime ?? null,
|
|
1613
|
+
loop: agentMeta.loop ?? null,
|
|
1614
|
+
});
|
|
1564
1615
|
// Pre-call balance check for paid agents
|
|
1565
1616
|
let pricingInfo;
|
|
1566
1617
|
if ((0, pricing_1.isPaidAgent)(agentMeta)) {
|
|
@@ -1680,7 +1731,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1680
1731
|
...(options.model && { model: options.model }),
|
|
1681
1732
|
};
|
|
1682
1733
|
}
|
|
1683
|
-
else if (
|
|
1734
|
+
else if (cloudEngine !== 'code_runtime') {
|
|
1684
1735
|
const searchedProviders = effectiveProvider ? [effectiveProvider] : supportedProviders;
|
|
1685
1736
|
const providerList = searchedProviders.join(', ');
|
|
1686
1737
|
process.stderr.write(`Warning: No LLM key found for provider(s): ${providerList}\n` +
|
|
@@ -1736,7 +1787,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1736
1787
|
// Merge file content into --data
|
|
1737
1788
|
const resolvedBody = await resolveJsonBody(options.data);
|
|
1738
1789
|
const bodyObj = JSON.parse(resolvedBody);
|
|
1739
|
-
if (
|
|
1790
|
+
if (cloudEngine !== 'code_runtime') {
|
|
1740
1791
|
const fieldName = options.fileField || inferFileField(agentMeta.input_schema);
|
|
1741
1792
|
if (filePaths.length === 1) {
|
|
1742
1793
|
await validateFilePath(filePaths[0]);
|
|
@@ -1768,7 +1819,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1768
1819
|
headers['Content-Type'] = 'application/json';
|
|
1769
1820
|
}
|
|
1770
1821
|
else {
|
|
1771
|
-
//
|
|
1822
|
+
// Code-runtime agents: send files as multipart, --data as metadata
|
|
1772
1823
|
let metadata = resolvedBody;
|
|
1773
1824
|
if (llmCredentials) {
|
|
1774
1825
|
const metaObj = JSON.parse(metadata);
|
|
@@ -1793,7 +1844,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1793
1844
|
}
|
|
1794
1845
|
headers['Content-Type'] = 'application/json';
|
|
1795
1846
|
}
|
|
1796
|
-
else if ((filePaths.length > 0 || options.metadata) &&
|
|
1847
|
+
else if ((filePaths.length > 0 || options.metadata) && cloudEngine !== 'code_runtime') {
|
|
1797
1848
|
const fieldName = options.fileField || inferFileField(agentMeta.input_schema);
|
|
1798
1849
|
let bodyObj = {};
|
|
1799
1850
|
if (options.metadata) {
|
|
@@ -1858,16 +1909,16 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1858
1909
|
}
|
|
1859
1910
|
} // end of non-injection path
|
|
1860
1911
|
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}`;
|
|
1861
|
-
// Enable SSE streaming for
|
|
1862
|
-
const
|
|
1863
|
-
const wantStream =
|
|
1912
|
+
// Enable SSE streaming for managed-loop agents (unless --json or --no-stream or --output)
|
|
1913
|
+
const isManagedLoopAgent = cloudType === 'agent' && cloudEngine === 'managed_loop';
|
|
1914
|
+
const wantStream = isManagedLoopAgent && !options.json && !options.noStream && !options.output;
|
|
1864
1915
|
if (wantStream) {
|
|
1865
1916
|
headers['Accept'] = 'text/event-stream';
|
|
1866
1917
|
}
|
|
1867
1918
|
const spinner = options.json ? null : (0, spinner_1.createSpinner)(`Running ${org}/${parsed.agent}@${parsed.version}...`);
|
|
1868
1919
|
spinner?.start();
|
|
1869
|
-
//
|
|
1870
|
-
const timeoutMs =
|
|
1920
|
+
// Managed-loop runs can take longer; use 10 min timeout for streaming.
|
|
1921
|
+
const timeoutMs = isManagedLoopAgent ? 600000 : undefined;
|
|
1871
1922
|
let response;
|
|
1872
1923
|
try {
|
|
1873
1924
|
response = await (0, api_1.safeFetchWithRetryForCalls)(url, {
|
|
@@ -2126,7 +2177,10 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2126
2177
|
catch (err) {
|
|
2127
2178
|
const agentMeta = await (0, api_1.getPublicAgent)(resolved, org, parsed.agent, parsed.version);
|
|
2128
2179
|
return {
|
|
2129
|
-
type: agentMeta.type || '
|
|
2180
|
+
type: agentMeta.type || 'agent',
|
|
2181
|
+
run_mode: agentMeta.run_mode ?? null,
|
|
2182
|
+
execution_engine: agentMeta.execution_engine ?? null,
|
|
2183
|
+
callable: agentMeta.callable,
|
|
2130
2184
|
name: agentMeta.name,
|
|
2131
2185
|
version: agentMeta.version,
|
|
2132
2186
|
description: agentMeta.description || undefined,
|
|
@@ -2134,16 +2188,18 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2134
2188
|
};
|
|
2135
2189
|
}
|
|
2136
2190
|
}, { successText: `Downloaded ${org}/${parsed.agent}@${parsed.version}` });
|
|
2191
|
+
const localType = canonicalAgentType(agentData.type);
|
|
2192
|
+
const localEngine = resolveExecutionEngine(agentData);
|
|
2137
2193
|
// Skills cannot be run directly
|
|
2138
|
-
if (
|
|
2194
|
+
if (localType === 'skill') {
|
|
2139
2195
|
throw new errors_1.CliError('Skills cannot be run directly.\n\n' +
|
|
2140
2196
|
'Skills are instructions meant to be injected into AI agent contexts.\n\n' +
|
|
2141
2197
|
'Options:\n' +
|
|
2142
2198
|
` Install for AI tools: orchagent skill install ${org}/${parsed.agent}\n` +
|
|
2143
2199
|
` Use with an agent: orchagent run <agent> --skills ${org}/${parsed.agent}`);
|
|
2144
2200
|
}
|
|
2145
|
-
//
|
|
2146
|
-
if (
|
|
2201
|
+
// Managed-loop agents execute locally with the agent runner.
|
|
2202
|
+
if (localEngine === 'managed_loop') {
|
|
2147
2203
|
if (!agentData.prompt) {
|
|
2148
2204
|
throw new errors_1.CliError('Agent prompt not available for local execution.\n\n' +
|
|
2149
2205
|
'This agent may have local download disabled.\n' +
|
|
@@ -2218,10 +2274,10 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2218
2274
|
// Save locally
|
|
2219
2275
|
const agentDir = await saveAgentLocally(org, parsed.agent, agentData);
|
|
2220
2276
|
process.stderr.write(`\nAgent saved to: ${agentDir}\n`);
|
|
2221
|
-
if (
|
|
2277
|
+
if (localEngine === 'code_runtime') {
|
|
2222
2278
|
if (agentData.has_bundle) {
|
|
2223
2279
|
if (options.downloadOnly) {
|
|
2224
|
-
process.stdout.write(`\
|
|
2280
|
+
process.stdout.write(`\nCode runtime bundle is available for local execution.\n`);
|
|
2225
2281
|
process.stdout.write(`Run with: orch run ${org}/${parsed.agent} --local [args...]\n`);
|
|
2226
2282
|
return;
|
|
2227
2283
|
}
|
|
@@ -2250,8 +2306,8 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2250
2306
|
await executeTool(agentData, args);
|
|
2251
2307
|
return;
|
|
2252
2308
|
}
|
|
2253
|
-
// Fallback: agent doesn't support local execution
|
|
2254
|
-
process.stdout.write(`\nThis
|
|
2309
|
+
// Fallback: code runtime agent doesn't support local execution.
|
|
2310
|
+
process.stdout.write(`\nThis code runtime agent is configured for server execution.\n`);
|
|
2255
2311
|
process.stdout.write(`\nRun without --local: orch run ${org}/${parsed.agent}@${parsed.version} --data '{...}'\n`);
|
|
2256
2312
|
return;
|
|
2257
2313
|
}
|
|
@@ -2264,9 +2320,9 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2264
2320
|
const execLocalFileArgs = options.file ?? [];
|
|
2265
2321
|
const execLocalKeyedFiles = execLocalFileArgs.filter(a => isKeyedFileArg(a) !== null);
|
|
2266
2322
|
const execLocalHasInjection = execLocalKeyedFiles.length > 0 || (options.mount ?? []).length > 0;
|
|
2267
|
-
//
|
|
2323
|
+
// Direct LLM agents execute locally via prompt composition.
|
|
2268
2324
|
if (!options.input && !execLocalHasInjection) {
|
|
2269
|
-
process.stdout.write(`\
|
|
2325
|
+
process.stdout.write(`\nAgent ready.\n`);
|
|
2270
2326
|
process.stdout.write(`Run with: orch run ${org}/${parsed.agent}@${parsed.version} --local --input '{...}'\n`);
|
|
2271
2327
|
return;
|
|
2272
2328
|
}
|
|
@@ -88,18 +88,26 @@ function registerScheduleCommand(program) {
|
|
|
88
88
|
chalk_1.default.bold('Type'),
|
|
89
89
|
chalk_1.default.bold('Schedule'),
|
|
90
90
|
chalk_1.default.bold('Enabled'),
|
|
91
|
+
chalk_1.default.bold('Fails'),
|
|
91
92
|
chalk_1.default.bold('Last Run'),
|
|
92
93
|
chalk_1.default.bold('Status'),
|
|
93
94
|
chalk_1.default.bold('Runs'),
|
|
94
95
|
],
|
|
95
96
|
});
|
|
96
97
|
result.schedules.forEach((s) => {
|
|
98
|
+
const enabledLabel = s.auto_disabled_at
|
|
99
|
+
? chalk_1.default.bgRed.white(' AUTO-DISABLED ')
|
|
100
|
+
: s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no');
|
|
101
|
+
const failsLabel = s.consecutive_failures > 0
|
|
102
|
+
? chalk_1.default.red(String(s.consecutive_failures))
|
|
103
|
+
: chalk_1.default.gray('0');
|
|
97
104
|
table.push([
|
|
98
105
|
s.id.slice(0, 8),
|
|
99
106
|
`${s.agent_name}@${s.agent_version}`,
|
|
100
107
|
s.schedule_type,
|
|
101
108
|
s.schedule_type === 'cron' ? (s.cron_expression ?? '-') : 'webhook',
|
|
102
|
-
|
|
109
|
+
enabledLabel,
|
|
110
|
+
failsLabel,
|
|
103
111
|
formatDate(s.last_run_at),
|
|
104
112
|
statusColor(s.last_run_status),
|
|
105
113
|
s.run_count.toString(),
|
|
@@ -294,4 +302,123 @@ function registerScheduleCommand(program) {
|
|
|
294
302
|
}
|
|
295
303
|
process.stdout.write('\n');
|
|
296
304
|
});
|
|
305
|
+
// orch schedule info <schedule-id>
|
|
306
|
+
schedule
|
|
307
|
+
.command('info <schedule-id>')
|
|
308
|
+
.description('Show detailed schedule information with recent runs and events')
|
|
309
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
310
|
+
.option('--json', 'Output as JSON')
|
|
311
|
+
.action(async (scheduleId, options) => {
|
|
312
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
313
|
+
if (!config.apiKey) {
|
|
314
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
315
|
+
}
|
|
316
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
317
|
+
const [scheduleRes, runsRes, eventsRes] = await Promise.all([
|
|
318
|
+
(0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}`),
|
|
319
|
+
(0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/runs?limit=5`),
|
|
320
|
+
(0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/events?limit=5`),
|
|
321
|
+
]);
|
|
322
|
+
if (options.json) {
|
|
323
|
+
(0, output_1.printJson)({ schedule: scheduleRes.schedule, runs: runsRes.runs, events: eventsRes.events });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const s = scheduleRes.schedule;
|
|
327
|
+
process.stdout.write(`\n${chalk_1.default.bold('Schedule Details')}\n\n`);
|
|
328
|
+
process.stdout.write(` ID: ${s.id}\n`);
|
|
329
|
+
process.stdout.write(` Agent: ${s.agent_name}@${s.agent_version}\n`);
|
|
330
|
+
process.stdout.write(` Type: ${s.schedule_type}\n`);
|
|
331
|
+
if (s.cron_expression) {
|
|
332
|
+
process.stdout.write(` Cron: ${s.cron_expression}\n`);
|
|
333
|
+
process.stdout.write(` Timezone: ${s.timezone}\n`);
|
|
334
|
+
}
|
|
335
|
+
process.stdout.write(` Enabled: ${s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no')}\n`);
|
|
336
|
+
if (s.auto_disabled_at) {
|
|
337
|
+
process.stdout.write(` ${chalk_1.default.bgRed.white(' AUTO-DISABLED ')} at ${formatDate(s.auto_disabled_at)}\n`);
|
|
338
|
+
}
|
|
339
|
+
process.stdout.write(` Runs: ${s.run_count}\n`);
|
|
340
|
+
process.stdout.write(` Failures: ${s.consecutive_failures > 0 ? chalk_1.default.red(String(s.consecutive_failures)) : '0'} / ${s.max_consecutive_failures}\n`);
|
|
341
|
+
if (s.next_run_at) {
|
|
342
|
+
process.stdout.write(` Next Run: ${formatDate(s.next_run_at)}\n`);
|
|
343
|
+
}
|
|
344
|
+
if (s.alert_webhook_url) {
|
|
345
|
+
process.stdout.write(` Alert URL: ${s.alert_webhook_url.slice(0, 50)}...\n`);
|
|
346
|
+
}
|
|
347
|
+
// Recent runs
|
|
348
|
+
if (runsRes.runs.length > 0) {
|
|
349
|
+
process.stdout.write(`\n${chalk_1.default.bold('Recent Runs')}\n`);
|
|
350
|
+
const runsTable = new cli_table3_1.default({
|
|
351
|
+
head: [chalk_1.default.bold('Status'), chalk_1.default.bold('Duration'), chalk_1.default.bold('Error'), chalk_1.default.bold('Started')],
|
|
352
|
+
});
|
|
353
|
+
for (const r of runsRes.runs) {
|
|
354
|
+
runsTable.push([
|
|
355
|
+
statusColor(r.status),
|
|
356
|
+
r.duration_ms != null ? `${(r.duration_ms / 1000).toFixed(1)}s` : '-',
|
|
357
|
+
r.error_message ? chalk_1.default.red(r.error_message.slice(0, 60)) : '-',
|
|
358
|
+
formatDate(r.started_at),
|
|
359
|
+
]);
|
|
360
|
+
}
|
|
361
|
+
process.stdout.write(`${runsTable.toString()}\n`);
|
|
362
|
+
}
|
|
363
|
+
// Recent events
|
|
364
|
+
if (eventsRes.events.length > 0) {
|
|
365
|
+
process.stdout.write(`\n${chalk_1.default.bold('Recent Events')}\n`);
|
|
366
|
+
for (const e of eventsRes.events) {
|
|
367
|
+
const color = e.event_type.includes('fail') || e.event_type.includes('disabled') ? chalk_1.default.red : chalk_1.default.gray;
|
|
368
|
+
process.stdout.write(` ${chalk_1.default.gray(formatDate(e.created_at))} ${color(e.event_type)} ${e.message || ''}\n`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
process.stdout.write('\n');
|
|
372
|
+
});
|
|
373
|
+
// orch schedule runs <schedule-id>
|
|
374
|
+
schedule
|
|
375
|
+
.command('runs <schedule-id>')
|
|
376
|
+
.description('List run history for a schedule')
|
|
377
|
+
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
378
|
+
.option('--status <status>', 'Filter by status (completed, failed, running, timeout)')
|
|
379
|
+
.option('--limit <n>', 'Number of runs to show (default: 20)', '20')
|
|
380
|
+
.option('--json', 'Output as JSON')
|
|
381
|
+
.action(async (scheduleId, options) => {
|
|
382
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
383
|
+
if (!config.apiKey) {
|
|
384
|
+
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
385
|
+
}
|
|
386
|
+
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
387
|
+
const params = new URLSearchParams();
|
|
388
|
+
params.set('limit', options.limit);
|
|
389
|
+
if (options.status)
|
|
390
|
+
params.set('status', options.status);
|
|
391
|
+
const qs = `?${params.toString()}`;
|
|
392
|
+
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/runs${qs}`);
|
|
393
|
+
if (options.json) {
|
|
394
|
+
(0, output_1.printJson)(result);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (!result.runs.length) {
|
|
398
|
+
process.stdout.write('No runs found.\n');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const table = new cli_table3_1.default({
|
|
402
|
+
head: [
|
|
403
|
+
chalk_1.default.bold('Run ID'),
|
|
404
|
+
chalk_1.default.bold('Status'),
|
|
405
|
+
chalk_1.default.bold('Source'),
|
|
406
|
+
chalk_1.default.bold('Duration'),
|
|
407
|
+
chalk_1.default.bold('Error'),
|
|
408
|
+
chalk_1.default.bold('Started'),
|
|
409
|
+
],
|
|
410
|
+
});
|
|
411
|
+
for (const r of result.runs) {
|
|
412
|
+
table.push([
|
|
413
|
+
r.id.slice(0, 8),
|
|
414
|
+
statusColor(r.status),
|
|
415
|
+
r.trigger_source || '-',
|
|
416
|
+
r.duration_ms != null ? `${(r.duration_ms / 1000).toFixed(1)}s` : '-',
|
|
417
|
+
r.error_message ? chalk_1.default.red(r.error_message.slice(0, 50)) : '-',
|
|
418
|
+
formatDate(r.started_at),
|
|
419
|
+
]);
|
|
420
|
+
}
|
|
421
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
422
|
+
process.stdout.write(chalk_1.default.gray(`\n${result.total} run${result.total !== 1 ? 's' : ''} total\n`));
|
|
423
|
+
});
|
|
297
424
|
}
|