@orchagent/cli 0.3.85 → 0.3.87
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 +3 -9
- 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 +8 -0
- package/dist/commands/info.js +75 -0
- package/dist/commands/init.js +729 -38
- package/dist/commands/publish.js +244 -22
- package/dist/commands/run.js +275 -29
- package/dist/commands/schedule.js +25 -8
- package/dist/commands/skill.js +3 -3
- package/dist/commands/test.js +68 -1
- 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
package/dist/commands/run.js
CHANGED
|
@@ -37,10 +37,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.localCommandForEntrypoint = localCommandForEntrypoint;
|
|
40
|
+
exports.validateInputSchema = validateInputSchema;
|
|
40
41
|
exports.isKeyedFileArg = isKeyedFileArg;
|
|
41
42
|
exports.readKeyedFiles = readKeyedFiles;
|
|
42
43
|
exports.mountDirectory = mountDirectory;
|
|
43
44
|
exports.buildInjectedPayload = buildInjectedPayload;
|
|
45
|
+
exports.renderProgress = renderProgress;
|
|
44
46
|
exports.registerRunCommand = registerRunCommand;
|
|
45
47
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
46
48
|
const path_1 = __importDefault(require("path"));
|
|
@@ -188,6 +190,69 @@ function applySchemaDefaults(body, schema) {
|
|
|
188
190
|
}
|
|
189
191
|
return body;
|
|
190
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Validates input data against an agent's input_schema before sending to the server.
|
|
195
|
+
* Returns an array of human-readable error strings. Empty array = valid.
|
|
196
|
+
*/
|
|
197
|
+
function validateInputSchema(body, schema) {
|
|
198
|
+
if (!schema)
|
|
199
|
+
return [];
|
|
200
|
+
const s = schema;
|
|
201
|
+
const props = s.properties;
|
|
202
|
+
if (!props || typeof props !== 'object')
|
|
203
|
+
return [];
|
|
204
|
+
const errors = [];
|
|
205
|
+
const required = (s.required ?? []);
|
|
206
|
+
// Check required fields
|
|
207
|
+
for (const field of required) {
|
|
208
|
+
if (body[field] === undefined || body[field] === null) {
|
|
209
|
+
const prop = props[field];
|
|
210
|
+
const desc = prop?.description ? ` (${prop.description})` : '';
|
|
211
|
+
errors.push(`Missing required field: "${field}"${desc}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Check types of provided fields
|
|
215
|
+
for (const [key, value] of Object.entries(body)) {
|
|
216
|
+
const prop = props[key];
|
|
217
|
+
if (!prop || value === undefined || value === null)
|
|
218
|
+
continue;
|
|
219
|
+
const expectedType = prop.type;
|
|
220
|
+
if (!expectedType)
|
|
221
|
+
continue;
|
|
222
|
+
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
223
|
+
let mismatch = false;
|
|
224
|
+
switch (expectedType) {
|
|
225
|
+
case 'string':
|
|
226
|
+
mismatch = actualType !== 'string';
|
|
227
|
+
break;
|
|
228
|
+
case 'number':
|
|
229
|
+
case 'integer':
|
|
230
|
+
mismatch = actualType !== 'number';
|
|
231
|
+
break;
|
|
232
|
+
case 'boolean':
|
|
233
|
+
mismatch = actualType !== 'boolean';
|
|
234
|
+
break;
|
|
235
|
+
case 'array':
|
|
236
|
+
mismatch = actualType !== 'array';
|
|
237
|
+
break;
|
|
238
|
+
case 'object':
|
|
239
|
+
mismatch = actualType !== 'object';
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
if (mismatch) {
|
|
243
|
+
errors.push(`Field "${key}" should be ${expectedType}, got ${actualType}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return errors;
|
|
247
|
+
}
|
|
248
|
+
function warnInputSchemaErrors(body, schema) {
|
|
249
|
+
const errors = validateInputSchema(body, schema);
|
|
250
|
+
if (errors.length === 0)
|
|
251
|
+
return;
|
|
252
|
+
process.stderr.write(chalk_1.default.yellow(`\nInput validation warning:\n`) +
|
|
253
|
+
errors.map(e => chalk_1.default.yellow(` - ${e}`)).join('\n') +
|
|
254
|
+
chalk_1.default.yellow('\n\nThe request will still be sent, but may fail server-side.\n\n'));
|
|
255
|
+
}
|
|
191
256
|
async function readStdin() {
|
|
192
257
|
if (process.stdin.isTTY)
|
|
193
258
|
return null;
|
|
@@ -853,7 +918,13 @@ async function executeAgentLocally(agentDir, prompt, inputData, outputSchema, cu
|
|
|
853
918
|
try {
|
|
854
919
|
const result = JSON.parse(stdout.trim());
|
|
855
920
|
if (exitCode !== 0 && typeof result === 'object' && result !== null && 'error' in result) {
|
|
856
|
-
|
|
921
|
+
const errorText = String(result.error);
|
|
922
|
+
const err = new errors_1.CliError(`Agent error: ${errorText}`);
|
|
923
|
+
// BUG-13: If stderr already showed this error during execution, don't print again
|
|
924
|
+
if (stderr.includes(errorText)) {
|
|
925
|
+
err.displayed = true;
|
|
926
|
+
}
|
|
927
|
+
throw err;
|
|
857
928
|
}
|
|
858
929
|
if (exitCode !== 0) {
|
|
859
930
|
(0, output_1.printJson)(result);
|
|
@@ -871,6 +942,12 @@ async function executeAgentLocally(agentDir, prompt, inputData, outputSchema, cu
|
|
|
871
942
|
}
|
|
872
943
|
}
|
|
873
944
|
else if (exitCode !== 0) {
|
|
945
|
+
// BUG-13: stderr was already relayed to user in real-time; don't embed it again
|
|
946
|
+
if (stderr.trim()) {
|
|
947
|
+
const err = new errors_1.CliError(`Agent exited with code ${exitCode}`);
|
|
948
|
+
err.displayed = true;
|
|
949
|
+
throw err;
|
|
950
|
+
}
|
|
874
951
|
throw new errors_1.CliError(`Agent exited with code ${exitCode} (no output)\n\n` +
|
|
875
952
|
`Common causes:\n` +
|
|
876
953
|
` - Missing LLM API key (check ${apiKeyEnvVar || 'API key env var'})\n` +
|
|
@@ -1177,7 +1254,13 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1177
1254
|
try {
|
|
1178
1255
|
const result = JSON.parse(stdout.trim());
|
|
1179
1256
|
if (exitCode !== 0 && typeof result === 'object' && result !== null && 'error' in result) {
|
|
1180
|
-
|
|
1257
|
+
const errorText = String(result.error);
|
|
1258
|
+
const err = new errors_1.CliError(`Agent error: ${errorText}`);
|
|
1259
|
+
// BUG-13: If stderr already showed this error during execution, don't print again
|
|
1260
|
+
if (stderr.includes(errorText)) {
|
|
1261
|
+
err.displayed = true;
|
|
1262
|
+
}
|
|
1263
|
+
throw err;
|
|
1181
1264
|
}
|
|
1182
1265
|
if (exitCode !== 0) {
|
|
1183
1266
|
(0, output_1.printJson)(result);
|
|
@@ -1195,8 +1278,11 @@ async function executeBundleAgent(config, org, agentName, version, agentData, ar
|
|
|
1195
1278
|
}
|
|
1196
1279
|
}
|
|
1197
1280
|
else if (exitCode !== 0) {
|
|
1281
|
+
// BUG-13: stderr was already relayed to user in real-time; don't embed it again
|
|
1198
1282
|
if (stderr.trim()) {
|
|
1199
|
-
|
|
1283
|
+
const err = new errors_1.CliError(`Agent exited with code ${exitCode}`);
|
|
1284
|
+
err.displayed = true;
|
|
1285
|
+
throw err;
|
|
1200
1286
|
}
|
|
1201
1287
|
throw new errors_1.CliError(`Agent exited with code ${exitCode} (no output)\n\n` +
|
|
1202
1288
|
`Common causes:\n` +
|
|
@@ -1592,7 +1678,13 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1592
1678
|
try {
|
|
1593
1679
|
const result = JSON.parse(stdout.trim());
|
|
1594
1680
|
if (exitCode !== 0 && typeof result === 'object' && result !== null && 'error' in result) {
|
|
1595
|
-
|
|
1681
|
+
const errorText = String(result.error);
|
|
1682
|
+
const err = new errors_1.CliError(`Agent error: ${errorText}`);
|
|
1683
|
+
// BUG-13: If stderr already showed this error during execution, don't print again
|
|
1684
|
+
if (stderr.includes(errorText)) {
|
|
1685
|
+
err.displayed = true;
|
|
1686
|
+
}
|
|
1687
|
+
throw err;
|
|
1596
1688
|
}
|
|
1597
1689
|
if (exitCode !== 0) {
|
|
1598
1690
|
(0, output_1.printJson)(result);
|
|
@@ -1610,15 +1702,53 @@ async function executeLocalFromDir(dirPath, args, options) {
|
|
|
1610
1702
|
}
|
|
1611
1703
|
}
|
|
1612
1704
|
else if (exitCode !== 0) {
|
|
1705
|
+
// BUG-13: stderr was already relayed to user in real-time; don't repeat
|
|
1706
|
+
if (stderr.trim()) {
|
|
1707
|
+
const err = new errors_1.CliError(`Agent exited with code ${exitCode}`);
|
|
1708
|
+
err.displayed = true;
|
|
1709
|
+
throw err;
|
|
1710
|
+
}
|
|
1613
1711
|
throw new errors_1.CliError(`Agent exited with code ${exitCode}`);
|
|
1614
1712
|
}
|
|
1615
1713
|
}
|
|
1616
1714
|
// ─── Cloud execution path ───────────────────────────────────────────────────
|
|
1617
|
-
function renderProgress(event) {
|
|
1715
|
+
function renderProgress(event, verbose = false) {
|
|
1618
1716
|
switch (event.type) {
|
|
1619
1717
|
case 'turn_start':
|
|
1620
1718
|
process.stderr.write(chalk_1.default.gray(` Turn ${event.turn}/${event.max_turns}\n`));
|
|
1621
1719
|
break;
|
|
1720
|
+
case 'sandbox_start':
|
|
1721
|
+
process.stderr.write(chalk_1.default.gray(' Starting sandbox...\n'));
|
|
1722
|
+
break;
|
|
1723
|
+
case 'setup_step': {
|
|
1724
|
+
const name = String(event.name || 'setup');
|
|
1725
|
+
const status = String(event.status || 'started');
|
|
1726
|
+
const icon = status === 'completed' ? chalk_1.default.green('✓') : status === 'failed' ? chalk_1.default.red('x') : chalk_1.default.gray('·');
|
|
1727
|
+
process.stderr.write(` ${icon} ${chalk_1.default.gray(name)} ${chalk_1.default.gray(status)}\n`);
|
|
1728
|
+
break;
|
|
1729
|
+
}
|
|
1730
|
+
case 'stdout': {
|
|
1731
|
+
if (!verbose)
|
|
1732
|
+
break;
|
|
1733
|
+
const text = typeof event.text === 'string' ? event.text : '';
|
|
1734
|
+
if (text)
|
|
1735
|
+
process.stderr.write(chalk_1.default.gray(text));
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1738
|
+
case 'stderr': {
|
|
1739
|
+
if (!verbose)
|
|
1740
|
+
break;
|
|
1741
|
+
const text = typeof event.text === 'string' ? event.text : '';
|
|
1742
|
+
if (text)
|
|
1743
|
+
process.stderr.write(chalk_1.default.yellow(text));
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
case 'heartbeat':
|
|
1747
|
+
// Keepalive for long-running silent steps; avoid noisy output.
|
|
1748
|
+
break;
|
|
1749
|
+
case 'output_truncated':
|
|
1750
|
+
process.stderr.write(chalk_1.default.yellow(' Live output truncated. Use `orch logs <run-id>` for full output.\n'));
|
|
1751
|
+
break;
|
|
1622
1752
|
case 'tool_call': {
|
|
1623
1753
|
const icon = event.tool === 'bash'
|
|
1624
1754
|
? '$'
|
|
@@ -1635,9 +1765,23 @@ function renderProgress(event) {
|
|
|
1635
1765
|
if (event.status === 'error')
|
|
1636
1766
|
process.stderr.write(chalk_1.default.yellow(` (error)\n`));
|
|
1637
1767
|
break;
|
|
1638
|
-
case 'done':
|
|
1768
|
+
case 'done': {
|
|
1639
1769
|
process.stderr.write(chalk_1.default.green(` Done\n`));
|
|
1770
|
+
// In verbose mode, show exit code and execution time from done event
|
|
1771
|
+
if (verbose) {
|
|
1772
|
+
const parts = [];
|
|
1773
|
+
if (typeof event.exit_code === 'number' && event.exit_code !== 0) {
|
|
1774
|
+
parts.push(`exit_code=${event.exit_code}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (typeof event.execution_time_ms === 'number') {
|
|
1777
|
+
parts.push(`${(event.execution_time_ms / 1000).toFixed(1)}s`);
|
|
1778
|
+
}
|
|
1779
|
+
if (parts.length > 0) {
|
|
1780
|
+
process.stderr.write(chalk_1.default.gray(` (${parts.join(', ')})\n`));
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1640
1783
|
break;
|
|
1784
|
+
}
|
|
1641
1785
|
case 'error':
|
|
1642
1786
|
process.stderr.write(chalk_1.default.red(` Error: ${event.message}\n`));
|
|
1643
1787
|
break;
|
|
@@ -1696,6 +1840,50 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1696
1840
|
}
|
|
1697
1841
|
}
|
|
1698
1842
|
}
|
|
1843
|
+
// --estimate: show cost estimate before running and ask for confirmation
|
|
1844
|
+
if (options.estimate) {
|
|
1845
|
+
try {
|
|
1846
|
+
const est = await (0, api_1.getAgentCostEstimate)(resolved, org, parsed.agent, parsed.version);
|
|
1847
|
+
const e = est.estimate;
|
|
1848
|
+
if (e.sample_size === 0) {
|
|
1849
|
+
process.stderr.write(chalk_1.default.yellow('\nNo run history available for cost estimation.\n'));
|
|
1850
|
+
process.stderr.write(chalk_1.default.gray('This agent has not been run before — cost data will appear after first run.\n\n'));
|
|
1851
|
+
}
|
|
1852
|
+
else {
|
|
1853
|
+
const fmtUsd = (v) => {
|
|
1854
|
+
if (v < 0.001)
|
|
1855
|
+
return '<$0.001';
|
|
1856
|
+
if (v < 0.01)
|
|
1857
|
+
return `$${v.toFixed(4)}`;
|
|
1858
|
+
if (v < 1)
|
|
1859
|
+
return `$${v.toFixed(3)}`;
|
|
1860
|
+
return `$${v.toFixed(2)}`;
|
|
1861
|
+
};
|
|
1862
|
+
process.stderr.write('\n' + chalk_1.default.bold('Cost Estimate') + chalk_1.default.gray(` (${e.sample_size} runs, last ${e.period_days}d)`) + '\n');
|
|
1863
|
+
process.stderr.write(` Average: ${chalk_1.default.green(fmtUsd(e.avg_cost_usd))} · Median: ${chalk_1.default.green(fmtUsd(e.p50_cost_usd))} · 95th pct: ${chalk_1.default.yellow(fmtUsd(e.p95_cost_usd))}\n`);
|
|
1864
|
+
if (e.provider_breakdown && e.provider_breakdown.length > 0) {
|
|
1865
|
+
const parts = e.provider_breakdown.map(p => `${p.provider}: ${fmtUsd(p.avg_cost_usd)}`).join(', ');
|
|
1866
|
+
process.stderr.write(chalk_1.default.gray(` By provider: ${parts}\n`));
|
|
1867
|
+
}
|
|
1868
|
+
process.stderr.write('\n');
|
|
1869
|
+
}
|
|
1870
|
+
// Ask for confirmation
|
|
1871
|
+
const rl = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
1872
|
+
const iface = rl.createInterface({ input: process.stdin, output: process.stderr });
|
|
1873
|
+
const answer = await new Promise(resolve => {
|
|
1874
|
+
iface.question('Proceed with run? [Y/n] ', resolve);
|
|
1875
|
+
});
|
|
1876
|
+
iface.close();
|
|
1877
|
+
if (answer.trim().toLowerCase() === 'n') {
|
|
1878
|
+
process.stderr.write('Run cancelled.\n');
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
catch {
|
|
1883
|
+
// Non-fatal: if estimate fails, proceed with the run
|
|
1884
|
+
process.stderr.write(chalk_1.default.gray('Could not fetch cost estimate. Proceeding...\n\n'));
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1699
1887
|
const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
|
|
1700
1888
|
const headers = {
|
|
1701
1889
|
Authorization: `Bearer ${resolved.apiKey}`,
|
|
@@ -1811,6 +1999,8 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1811
1999
|
mountArgs: options.mount,
|
|
1812
2000
|
llmCredentials,
|
|
1813
2001
|
});
|
|
2002
|
+
const injectedObj = JSON.parse(injected.body);
|
|
2003
|
+
warnInputSchemaErrors(injectedObj, agentMeta.input_schema);
|
|
1814
2004
|
body = injected.body;
|
|
1815
2005
|
sourceLabel = injected.sourceLabel;
|
|
1816
2006
|
headers['Content-Type'] = 'application/json';
|
|
@@ -1851,6 +2041,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1851
2041
|
}
|
|
1852
2042
|
}
|
|
1853
2043
|
applySchemaDefaults(bodyObj, agentMeta.input_schema);
|
|
2044
|
+
warnInputSchemaErrors(bodyObj, agentMeta.input_schema);
|
|
1854
2045
|
if (llmCredentials)
|
|
1855
2046
|
bodyObj.llm_credentials = llmCredentials;
|
|
1856
2047
|
body = JSON.stringify(bodyObj);
|
|
@@ -1872,14 +2063,12 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1872
2063
|
else if (options.data) {
|
|
1873
2064
|
const resolvedBody = await resolveJsonBody(options.data);
|
|
1874
2065
|
warnIfLocalPathReference(resolvedBody);
|
|
2066
|
+
const parsedBody = JSON.parse(resolvedBody);
|
|
2067
|
+
warnInputSchemaErrors(parsedBody, agentMeta.input_schema);
|
|
1875
2068
|
if (llmCredentials) {
|
|
1876
|
-
|
|
1877
|
-
bodyObj.llm_credentials = llmCredentials;
|
|
1878
|
-
body = JSON.stringify(bodyObj);
|
|
1879
|
-
}
|
|
1880
|
-
else {
|
|
1881
|
-
body = resolvedBody;
|
|
2069
|
+
parsedBody.llm_credentials = llmCredentials;
|
|
1882
2070
|
}
|
|
2071
|
+
body = JSON.stringify(parsedBody);
|
|
1883
2072
|
headers['Content-Type'] = 'application/json';
|
|
1884
2073
|
}
|
|
1885
2074
|
else if ((filePaths.length > 0 || options.metadata) && cloudEngine !== 'code_runtime') {
|
|
@@ -1919,6 +2108,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1919
2108
|
}
|
|
1920
2109
|
}
|
|
1921
2110
|
applySchemaDefaults(bodyObj, agentMeta.input_schema);
|
|
2111
|
+
warnInputSchemaErrors(bodyObj, agentMeta.input_schema);
|
|
1922
2112
|
if (llmCredentials) {
|
|
1923
2113
|
bodyObj.llm_credentials = llmCredentials;
|
|
1924
2114
|
}
|
|
@@ -1948,16 +2138,27 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1948
2138
|
} // end of non-injection path
|
|
1949
2139
|
const verboseQs = options.verbose ? '?verbose=true' : '';
|
|
1950
2140
|
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}${verboseQs}`;
|
|
1951
|
-
// Enable SSE streaming for
|
|
2141
|
+
// Enable SSE streaming for sandbox-backed engines (unless --json or --no-stream or --output)
|
|
1952
2142
|
const isManagedLoopAgent = cloudType === 'agent' && cloudEngine === 'managed_loop';
|
|
1953
|
-
const
|
|
2143
|
+
const isCodeRuntimeAgent = cloudEngine === 'code_runtime';
|
|
2144
|
+
const streamCapable = isManagedLoopAgent || isCodeRuntimeAgent;
|
|
2145
|
+
const streamDisabled = options.stream === false || options.noStream === true;
|
|
2146
|
+
const wantStream = streamCapable && !options.json && !streamDisabled && !options.output;
|
|
1954
2147
|
if (wantStream) {
|
|
1955
2148
|
headers['Accept'] = 'text/event-stream';
|
|
1956
2149
|
}
|
|
1957
|
-
|
|
2150
|
+
// Print verbose debug info before request
|
|
2151
|
+
if (options.verbose && !options.json) {
|
|
2152
|
+
process.stderr.write(chalk_1.default.gray(`\n[verbose] ${org}/${parsed.agent}@${parsed.version}\n` +
|
|
2153
|
+
`[verbose] type=${cloudType} engine=${cloudEngine} endpoint=${endpoint}\n` +
|
|
2154
|
+
`[verbose] stream=${wantStream ? 'yes' : 'no'} url=${url}\n`));
|
|
2155
|
+
}
|
|
2156
|
+
const { spinner, dispose: disposeSpinner } = options.json
|
|
2157
|
+
? { spinner: null, dispose: () => { } }
|
|
2158
|
+
: (0, spinner_1.createElapsedSpinner)(`Running ${org}/${parsed.agent}@${parsed.version}...`);
|
|
1958
2159
|
spinner?.start();
|
|
1959
|
-
//
|
|
1960
|
-
const timeoutMs =
|
|
2160
|
+
// Streamed sandbox runs can take longer; use 10 min timeout.
|
|
2161
|
+
const timeoutMs = wantStream ? 600000 : undefined;
|
|
1961
2162
|
let response;
|
|
1962
2163
|
try {
|
|
1963
2164
|
response = await (0, api_1.safeFetchWithRetryForCalls)(url, {
|
|
@@ -1990,7 +2191,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1990
2191
|
: undefined;
|
|
1991
2192
|
throw new errors_1.CliError(`Your CLI version (${package_json_1.default.version}) is too old.\n\n` +
|
|
1992
2193
|
(minVersion ? `Minimum required: ${minVersion}\n` : '') +
|
|
1993
|
-
'Update with: npm
|
|
2194
|
+
'Update with: npm install -g @orchagent/cli@latest');
|
|
1994
2195
|
}
|
|
1995
2196
|
if (errorCode === 'LLM_KEY_REQUIRED') {
|
|
1996
2197
|
spinner?.stop();
|
|
@@ -2021,6 +2222,14 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2021
2222
|
const hint = typeof payload === 'object' && payload
|
|
2022
2223
|
? payload.error?.hint
|
|
2023
2224
|
: undefined;
|
|
2225
|
+
// Read error category from gateway (UX-7)
|
|
2226
|
+
const errorCategory = typeof payload === 'object' && payload
|
|
2227
|
+
? payload.error?.category
|
|
2228
|
+
: undefined;
|
|
2229
|
+
// Read sandbox_exit_code for backward compat with older gateways
|
|
2230
|
+
const sandboxExitCode = typeof payload === 'object' && payload
|
|
2231
|
+
? payload.metadata?.sandbox_exit_code
|
|
2232
|
+
: undefined;
|
|
2024
2233
|
// Detect platform errors that surface as SANDBOX_ERROR (BUG-11)
|
|
2025
2234
|
const lowerMessage = (message || '').toLowerCase();
|
|
2026
2235
|
const isPlatformError = /\b403\b/.test(message || '') ||
|
|
@@ -2028,11 +2237,27 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2028
2237
|
lowerMessage.includes('proxy token') ||
|
|
2029
2238
|
lowerMessage.includes('orchagent_service_key') ||
|
|
2030
2239
|
lowerMessage.includes('orchagent_billing_org');
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
`
|
|
2240
|
+
// Categorize: platform > gateway category > exit_code heuristic > neutral
|
|
2241
|
+
let attribution;
|
|
2242
|
+
if (isPlatformError) {
|
|
2243
|
+
attribution =
|
|
2244
|
+
`This may be a platform configuration issue, not an error in the agent's code.\n` +
|
|
2245
|
+
`If this persists, contact support with the ref below.`;
|
|
2246
|
+
}
|
|
2247
|
+
else if (errorCategory === 'code_error' || (!errorCategory && sandboxExitCode != null && sandboxExitCode !== 0)) {
|
|
2248
|
+
attribution =
|
|
2249
|
+
`This is an error in the agent's code, not the platform.\n` +
|
|
2250
|
+
`Check the agent code and requirements, then republish.`;
|
|
2251
|
+
}
|
|
2252
|
+
else if (errorCategory === 'setup_error') {
|
|
2253
|
+
attribution =
|
|
2254
|
+
`The agent failed during setup. This may be a dependency or configuration issue.\n` +
|
|
2255
|
+
`Check requirements.txt and environment configuration.`;
|
|
2256
|
+
}
|
|
2257
|
+
else {
|
|
2258
|
+
// No category from gateway, no exit code — can't determine blame
|
|
2259
|
+
attribution = `Agent execution failed. Check agent logs for details.`;
|
|
2260
|
+
}
|
|
2036
2261
|
throw new errors_1.CliError(`${message}\n\n` +
|
|
2037
2262
|
attribution +
|
|
2038
2263
|
(hint ? `\n\nHint: ${hint}` : '') +
|
|
@@ -2095,11 +2320,22 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2095
2320
|
const { parseSSE } = await Promise.resolve().then(() => __importStar(require('../lib/sse.js')));
|
|
2096
2321
|
let finalPayload = null;
|
|
2097
2322
|
let hadError = false;
|
|
2098
|
-
|
|
2323
|
+
if (options.verbose) {
|
|
2324
|
+
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${parsed.agent}@${parsed.version} (verbose):\n`));
|
|
2325
|
+
process.stderr.write(chalk_1.default.gray(` type=${cloudType} engine=${cloudEngine} endpoint=${endpoint}\n`));
|
|
2326
|
+
}
|
|
2327
|
+
else {
|
|
2328
|
+
process.stderr.write(chalk_1.default.gray(`\nStreaming ${org}/${parsed.agent}@${parsed.version}:\n`));
|
|
2329
|
+
}
|
|
2330
|
+
let progressErrorShown = false;
|
|
2099
2331
|
for await (const { event, data } of parseSSE(response.body)) {
|
|
2100
2332
|
if (event === 'progress') {
|
|
2101
2333
|
try {
|
|
2102
|
-
|
|
2334
|
+
const parsed = JSON.parse(data);
|
|
2335
|
+
renderProgress(parsed, !!options.verbose);
|
|
2336
|
+
if (parsed.type === 'error') {
|
|
2337
|
+
progressErrorShown = true;
|
|
2338
|
+
}
|
|
2103
2339
|
}
|
|
2104
2340
|
catch {
|
|
2105
2341
|
// ignore malformed progress events
|
|
@@ -2134,7 +2370,12 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2134
2370
|
const errMsg = typeof finalPayload === 'object' && finalPayload
|
|
2135
2371
|
? finalPayload.error?.message || 'Agent execution failed'
|
|
2136
2372
|
: 'Agent execution failed';
|
|
2137
|
-
|
|
2373
|
+
const err = new errors_1.CliError(errMsg);
|
|
2374
|
+
// BUG-13: Error was already displayed by renderProgress during streaming
|
|
2375
|
+
if (progressErrorShown) {
|
|
2376
|
+
err.displayed = true;
|
|
2377
|
+
}
|
|
2378
|
+
throw err;
|
|
2138
2379
|
}
|
|
2139
2380
|
if (finalPayload !== null) {
|
|
2140
2381
|
(0, output_1.printJson)(finalPayload);
|
|
@@ -2332,13 +2573,16 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2332
2573
|
process.stderr.write(` orch run ${org}/${parsed.agent}@${parsed.version} --local --data '{\"task\": \"...\"}'\n`);
|
|
2333
2574
|
return;
|
|
2334
2575
|
}
|
|
2576
|
+
// Resolve @file.json / @- stdin syntax before parsing
|
|
2577
|
+
const resolvedInput = await resolveJsonBody(options.input);
|
|
2335
2578
|
let agentInputData;
|
|
2336
2579
|
try {
|
|
2337
|
-
agentInputData = JSON.parse(
|
|
2580
|
+
agentInputData = JSON.parse(resolvedInput);
|
|
2338
2581
|
}
|
|
2339
2582
|
catch {
|
|
2340
2583
|
throw new errors_1.CliError('Invalid JSON input');
|
|
2341
2584
|
}
|
|
2585
|
+
warnInputSchemaErrors(agentInputData, agentData.input_schema);
|
|
2342
2586
|
// Write prompt to temp dir and run
|
|
2343
2587
|
const tempAgentDir = path_1.default.join(os_1.default.tmpdir(), `orchagent-agent-${parsed.agent}-${Date.now()}`);
|
|
2344
2588
|
await promises_1.default.mkdir(tempAgentDir, { recursive: true });
|
|
@@ -2464,6 +2708,7 @@ async function executeLocal(agentRef, args, options) {
|
|
|
2464
2708
|
throw new errors_1.CliError('Invalid JSON input');
|
|
2465
2709
|
}
|
|
2466
2710
|
}
|
|
2711
|
+
warnInputSchemaErrors(inputData, agentData.input_schema);
|
|
2467
2712
|
// Handle skill composition
|
|
2468
2713
|
let skillPrompts = [];
|
|
2469
2714
|
if (!options.noSkills) {
|
|
@@ -2493,14 +2738,15 @@ function registerRunCommand(program) {
|
|
|
2493
2738
|
.option('--data <json>', 'JSON payload (string or @file, @- for stdin)')
|
|
2494
2739
|
.option('--input <json>', 'Alias for --data')
|
|
2495
2740
|
.option('--json', 'Output raw JSON')
|
|
2496
|
-
.option('--verbose', 'Show sandbox stdout/stderr
|
|
2741
|
+
.option('--verbose', 'Show sandbox stdout/stderr and debug info (cloud only)')
|
|
2742
|
+
.option('--estimate', 'Show cost estimate before running and ask for confirmation')
|
|
2497
2743
|
.option('--provider <provider>', 'LLM provider (openai, anthropic, gemini, ollama)')
|
|
2498
2744
|
.option('--model <model>', 'LLM model to use (overrides agent default)')
|
|
2499
2745
|
.option('--key <key>', 'LLM API key (overrides env vars)')
|
|
2500
2746
|
.option('--skills <skills>', 'Add skills (comma-separated)')
|
|
2501
2747
|
.option('--skills-only <skills>', 'Use only these skills')
|
|
2502
2748
|
.option('--no-skills', 'Ignore default skills')
|
|
2503
|
-
.option('--no-stream', 'Disable real-time streaming for
|
|
2749
|
+
.option('--no-stream', 'Disable real-time streaming for stream-capable sandbox runs')
|
|
2504
2750
|
// Cloud-only options
|
|
2505
2751
|
.option('--endpoint <endpoint>', 'Override agent endpoint (cloud only)')
|
|
2506
2752
|
.option('--tenant <tenant>', 'Tenant identifier for multi-tenant callers (cloud only)')
|
|
@@ -41,6 +41,8 @@ function statusColor(status) {
|
|
|
41
41
|
case 'completed': return chalk_1.default.green(status);
|
|
42
42
|
case 'failed': return chalk_1.default.red(status);
|
|
43
43
|
case 'running': return chalk_1.default.yellow(status);
|
|
44
|
+
case 'queued': return chalk_1.default.cyan(status);
|
|
45
|
+
case 'deduplicated': return chalk_1.default.dim(status);
|
|
44
46
|
default: return status;
|
|
45
47
|
}
|
|
46
48
|
}
|
|
@@ -194,14 +196,16 @@ function registerScheduleCommand(program) {
|
|
|
194
196
|
});
|
|
195
197
|
const s = result.schedule;
|
|
196
198
|
process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule created\n\n`);
|
|
197
|
-
process.stdout.write(` ID:
|
|
198
|
-
process.stdout.write(` Agent:
|
|
199
|
-
process.stdout.write(` Type:
|
|
199
|
+
process.stdout.write(` ID: ${s.id}\n`);
|
|
200
|
+
process.stdout.write(` Agent: ${s.agent_name}@${s.agent_version}\n`);
|
|
201
|
+
process.stdout.write(` Type: ${s.schedule_type}\n`);
|
|
202
|
+
process.stdout.write(` Enabled: ${chalk_1.default.green('yes')}\n`);
|
|
203
|
+
process.stdout.write(` Auto-update: ${s.auto_update === false ? chalk_1.default.yellow('no (pinned)') : chalk_1.default.green('yes')}\n`);
|
|
200
204
|
if (s.schedule_type === 'cron') {
|
|
201
|
-
process.stdout.write(` Cron:
|
|
202
|
-
process.stdout.write(`
|
|
205
|
+
process.stdout.write(` Cron: ${s.cron_expression}\n`);
|
|
206
|
+
process.stdout.write(` Timezone: ${s.timezone}\n`);
|
|
203
207
|
if (s.next_run_at) {
|
|
204
|
-
process.stdout.write(` Next:
|
|
208
|
+
process.stdout.write(` Next run: ${formatDate(s.next_run_at)}\n`);
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
else {
|
|
@@ -210,6 +214,9 @@ function registerScheduleCommand(program) {
|
|
|
210
214
|
process.stdout.write(` ${s.webhook_url}\n`);
|
|
211
215
|
}
|
|
212
216
|
}
|
|
217
|
+
if (s.llm_provider) {
|
|
218
|
+
process.stdout.write(` Provider: ${s.llm_provider}\n`);
|
|
219
|
+
}
|
|
213
220
|
process.stdout.write('\n');
|
|
214
221
|
});
|
|
215
222
|
// orch schedule update <schedule-id>
|
|
@@ -335,10 +342,20 @@ function registerScheduleCommand(program) {
|
|
|
335
342
|
body,
|
|
336
343
|
headers: { 'Content-Type': 'application/json' },
|
|
337
344
|
} : {});
|
|
338
|
-
|
|
345
|
+
// Status-aware header message
|
|
346
|
+
const isAsync = result.status === 'queued' || result.status === 'deduplicated';
|
|
347
|
+
if (isAsync) {
|
|
348
|
+
process.stdout.write(chalk_1.default.cyan('\u2713') + ` Run ${result.status}\n\n`);
|
|
349
|
+
}
|
|
350
|
+
else if (result.status === 'failed' || result.status === 'timeout') {
|
|
351
|
+
process.stdout.write(chalk_1.default.red('\u2717') + ` Run ${result.status}\n\n`);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
process.stdout.write(chalk_1.default.green('\u2713') + ` Run completed\n\n`);
|
|
355
|
+
}
|
|
339
356
|
process.stdout.write(` Run ID: ${result.run_id}\n`);
|
|
340
357
|
process.stdout.write(` Status: ${statusColor(result.status)}\n`);
|
|
341
|
-
process.stdout.write(` Duration: ${result.duration_ms}ms\n`);
|
|
358
|
+
process.stdout.write(` Duration: ${result.duration_ms != null ? `${result.duration_ms}ms` : 'pending'}\n`);
|
|
342
359
|
if (result.error) {
|
|
343
360
|
process.stdout.write(` Error: ${chalk_1.default.red(result.error)}\n`);
|
|
344
361
|
}
|
package/dist/commands/skill.js
CHANGED
|
@@ -211,11 +211,11 @@ function registerSkillCommand(program) {
|
|
|
211
211
|
.description('Browse available skills')
|
|
212
212
|
.option('--json', 'Output raw JSON')
|
|
213
213
|
.action(async () => {
|
|
214
|
-
process.stdout.write('
|
|
215
|
-
'Install a skill:\n' +
|
|
214
|
+
process.stdout.write('Install a skill:\n' +
|
|
216
215
|
' orch skill install <org>/<skill-name>\n\n' +
|
|
217
216
|
'View installed skills:\n' +
|
|
218
|
-
' orch update --check\n'
|
|
217
|
+
' orch update --check\n\n' +
|
|
218
|
+
'Learn more: https://docs.orchagent.io/skills\n');
|
|
219
219
|
process.exit(0);
|
|
220
220
|
});
|
|
221
221
|
// orch skill create [name]
|