@orchagent/cli 0.3.119 → 0.3.120
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/AGENT_GUIDE.md +34 -0
- package/dist/commands/agents.js +15 -2
- package/dist/commands/context.js +76 -0
- package/dist/commands/describe.js +131 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/info.js +8 -2
- package/dist/commands/login.js +18 -6
- package/dist/commands/logs.js +35 -1
- package/dist/commands/publish.js +107 -12
- package/dist/commands/run.js +39 -9
- package/dist/commands/schedule.js +37 -5
- package/dist/commands/schema.js +78 -0
- package/dist/commands/secrets.js +17 -2
- package/dist/commands/service.js +20 -3
- package/dist/commands/storage.js +9 -4
- package/dist/index.js +21 -2
- package/dist/lib/agent-ref.js +3 -0
- package/dist/lib/command-introspection.js +250 -0
- package/dist/lib/errors.js +125 -18
- package/dist/lib/list-options.js +69 -0
- package/dist/lib/llm.js +35 -0
- package/dist/lib/output.js +29 -0
- package/dist/lib/sanitize.js +99 -0
- package/dist/lib/validate.js +17 -0
- package/package.json +3 -2
package/dist/commands/run.js
CHANGED
|
@@ -57,6 +57,7 @@ const config_1 = require("../lib/config");
|
|
|
57
57
|
const resolve_agent_1 = require("../lib/resolve-agent");
|
|
58
58
|
const api_1 = require("../lib/api");
|
|
59
59
|
const errors_1 = require("../lib/errors");
|
|
60
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
60
61
|
const json_input_1 = require("../lib/json-input");
|
|
61
62
|
const output_1 = require("../lib/output");
|
|
62
63
|
const spinner_1 = require("../lib/spinner");
|
|
@@ -124,6 +125,8 @@ function resolveExecutionEngine(agentData) {
|
|
|
124
125
|
}
|
|
125
126
|
// ─── Validation helpers ─────────────────────────────────────────────────────
|
|
126
127
|
async function validateFilePath(filePath) {
|
|
128
|
+
// DX-29: reject ../ traversal in file paths
|
|
129
|
+
(0, sanitize_1.rejectPathTraversal)(filePath, 'file path');
|
|
127
130
|
const stat = await promises_1.default.stat(filePath);
|
|
128
131
|
if (stat.isDirectory()) {
|
|
129
132
|
throw new errors_1.CliError(`Cannot upload a directory for cloud execution: ${filePath}\n\n` +
|
|
@@ -381,6 +384,8 @@ async function readKeyedFiles(args) {
|
|
|
381
384
|
const parsed = isKeyedFileArg(arg);
|
|
382
385
|
if (!parsed)
|
|
383
386
|
continue;
|
|
387
|
+
// DX-29: reject ../ traversal in file paths
|
|
388
|
+
(0, sanitize_1.rejectPathTraversal)(parsed.filePath, 'file path');
|
|
384
389
|
const resolved = path_1.default.resolve(parsed.filePath);
|
|
385
390
|
let stat;
|
|
386
391
|
try {
|
|
@@ -403,6 +408,8 @@ const MOUNT_SKIP_DIRS = new Set([
|
|
|
403
408
|
const MOUNT_MAX_DEPTH = 15;
|
|
404
409
|
const MOUNT_MAX_FILES = 500;
|
|
405
410
|
async function mountDirectory(dirPath) {
|
|
411
|
+
// DX-29: reject ../ traversal in mount paths
|
|
412
|
+
(0, sanitize_1.rejectPathTraversal)(dirPath, 'mount path');
|
|
406
413
|
const resolved = path_1.default.resolve(dirPath);
|
|
407
414
|
let stat;
|
|
408
415
|
try {
|
|
@@ -1856,7 +1863,7 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1856
1863
|
runtime: agentMeta.runtime ?? null,
|
|
1857
1864
|
loop: agentMeta.loop ?? null,
|
|
1858
1865
|
});
|
|
1859
|
-
// Pre-flight: check required secrets before running (F-18)
|
|
1866
|
+
// Pre-flight: check required secrets before running (F-18, DX-3)
|
|
1860
1867
|
// Only for sandbox-backed engines where secrets are injected as env vars
|
|
1861
1868
|
if (cloudEngine !== 'direct_llm') {
|
|
1862
1869
|
const agentRequiredSecrets = agentMeta.required_secrets;
|
|
@@ -1867,12 +1874,15 @@ async function executeCloud(agentRef, file, options) {
|
|
|
1867
1874
|
const existingNames = new Set(secretsResult.secrets.map((s) => s.name));
|
|
1868
1875
|
const missing = agentRequiredSecrets.filter((s) => !existingNames.has(s));
|
|
1869
1876
|
if (missing.length > 0) {
|
|
1870
|
-
|
|
1877
|
+
const err = new errors_1.CliError(`Agent requires ${missing.length} secret${missing.length === 1 ? '' : 's'} not found in workspace '${org}':\n` +
|
|
1871
1878
|
missing.map((s) => ` - ${s}`).join('\n') + '\n\n' +
|
|
1872
1879
|
`Set them before running:\n` +
|
|
1873
1880
|
missing.map((s) => ` orch secrets set ${s} <value>`).join('\n') + '\n\n' +
|
|
1874
1881
|
`Secrets are injected as environment variables into the agent sandbox.\n` +
|
|
1875
|
-
`View existing secrets: orch secrets list
|
|
1882
|
+
`View existing secrets: orch secrets list`, errors_1.ExitCodes.INVALID_INPUT);
|
|
1883
|
+
err.code = errors_1.ErrorCodes.MISSING_SECRETS;
|
|
1884
|
+
err.hint = `Set missing secrets: ${missing.map(s => `orch secrets set ${s} <value>`).join(', ')}`;
|
|
1885
|
+
throw err;
|
|
1876
1886
|
}
|
|
1877
1887
|
}
|
|
1878
1888
|
}
|
|
@@ -2297,6 +2307,14 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2297
2307
|
const sandboxExitCode = typeof payload === 'object' && payload
|
|
2298
2308
|
? payload.metadata?.sandbox_exit_code
|
|
2299
2309
|
: undefined;
|
|
2310
|
+
// DX-1: Read failure classification from gateway
|
|
2311
|
+
const userMessage = typeof payload === 'object' && payload
|
|
2312
|
+
? payload.error?.user_message
|
|
2313
|
+
: undefined;
|
|
2314
|
+
// DX-2: Read stderr tail from gateway for immediate debugging
|
|
2315
|
+
const stderrTail = typeof payload === 'object' && payload
|
|
2316
|
+
? payload.error?.stderr_tail
|
|
2317
|
+
: undefined;
|
|
2300
2318
|
// Detect platform errors that surface as SANDBOX_ERROR (BUG-11)
|
|
2301
2319
|
const lowerMessage = (message || '').toLowerCase();
|
|
2302
2320
|
const isPlatformError = /\b403\b/.test(message || '') ||
|
|
@@ -2304,13 +2322,16 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2304
2322
|
lowerMessage.includes('proxy token') ||
|
|
2305
2323
|
lowerMessage.includes('orchagent_service_key') ||
|
|
2306
2324
|
lowerMessage.includes('orchagent_billing_org');
|
|
2307
|
-
//
|
|
2325
|
+
// DX-1: Prefer user_message from gateway when available
|
|
2308
2326
|
let attribution;
|
|
2309
2327
|
if (isPlatformError) {
|
|
2310
2328
|
attribution =
|
|
2311
2329
|
`This may be a platform configuration issue, not an error in the agent's code.\n` +
|
|
2312
2330
|
`If this persists, contact support with the ref below.`;
|
|
2313
2331
|
}
|
|
2332
|
+
else if (userMessage) {
|
|
2333
|
+
attribution = userMessage;
|
|
2334
|
+
}
|
|
2314
2335
|
else if (errorCategory === 'code_error' || (!errorCategory && sandboxExitCode != null && sandboxExitCode !== 0)) {
|
|
2315
2336
|
attribution =
|
|
2316
2337
|
`This is an error in the agent's code, not the platform.\n` +
|
|
@@ -2322,21 +2343,30 @@ async function executeCloud(agentRef, file, options) {
|
|
|
2322
2343
|
`Check requirements.txt and environment configuration.`;
|
|
2323
2344
|
}
|
|
2324
2345
|
else {
|
|
2325
|
-
// No category from gateway, no exit code — can't determine blame
|
|
2326
2346
|
attribution = `Agent execution failed. Check agent logs for details.`;
|
|
2327
2347
|
}
|
|
2348
|
+
// DX-2: Show stderr tail for immediate debugging
|
|
2349
|
+
const stderrSection = stderrTail
|
|
2350
|
+
? `\n\n--- stderr (last lines) ---\n${stderrTail}`
|
|
2351
|
+
: '';
|
|
2328
2352
|
throw new errors_1.CliError(`${message}\n\n` +
|
|
2329
2353
|
attribution +
|
|
2354
|
+
stderrSection +
|
|
2330
2355
|
(hint ? `\n\nHint: ${hint}` : '') +
|
|
2356
|
+
(requestId ? `\n\nFull logs: orch logs ${requestId.slice(0, 8)}` : '') +
|
|
2331
2357
|
refSuffix);
|
|
2332
2358
|
}
|
|
2333
2359
|
if (errorCode === 'SANDBOX_TIMEOUT') {
|
|
2334
2360
|
spinner?.stop();
|
|
2361
|
+
// DX-1: Use user_message from gateway when available
|
|
2362
|
+
const userMessage = typeof payload === 'object' && payload
|
|
2363
|
+
? payload.error?.user_message
|
|
2364
|
+
: undefined;
|
|
2335
2365
|
throw new errors_1.CliError(`${message}\n\n` +
|
|
2336
|
-
`The agent did not complete in time. Try:\n` +
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2366
|
+
(userMessage || `The agent did not complete in time. Try:\n` +
|
|
2367
|
+
` - Simplifying the input\n` +
|
|
2368
|
+
` - Using a smaller dataset\n` +
|
|
2369
|
+
` - Contacting the agent author to increase the timeout`) +
|
|
2340
2370
|
refSuffix);
|
|
2341
2371
|
}
|
|
2342
2372
|
if (errorCode === 'MISSING_SECRETS') {
|
|
@@ -13,6 +13,7 @@ const api_1 = require("../lib/api");
|
|
|
13
13
|
const errors_1 = require("../lib/errors");
|
|
14
14
|
const json_input_1 = require("../lib/json-input");
|
|
15
15
|
const output_1 = require("../lib/output");
|
|
16
|
+
const list_options_1 = require("../lib/list-options");
|
|
16
17
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
17
18
|
const api_2 = require("../lib/api");
|
|
18
19
|
// ============================================
|
|
@@ -88,6 +89,9 @@ function registerScheduleCommand(program) {
|
|
|
88
89
|
.option('--agent <name>', 'Filter by agent name')
|
|
89
90
|
.option('--type <type>', 'Filter by type (cron or webhook)')
|
|
90
91
|
.option('--json', 'Output as JSON')
|
|
92
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
93
|
+
.option('--limit <n>', 'Maximum number of schedules to return (default: 100)')
|
|
94
|
+
.option('--offset <n>', 'Number of schedules to skip')
|
|
91
95
|
.action(async (options) => {
|
|
92
96
|
const config = await (0, config_1.getResolvedConfig)();
|
|
93
97
|
if (!config.apiKey) {
|
|
@@ -99,11 +103,24 @@ function registerScheduleCommand(program) {
|
|
|
99
103
|
params.set('agent_name', options.agent);
|
|
100
104
|
if (options.type)
|
|
101
105
|
params.set('schedule_type', options.type);
|
|
102
|
-
params.set('limit', '100');
|
|
106
|
+
params.set('limit', options.limit ?? '100');
|
|
107
|
+
if (options.offset) {
|
|
108
|
+
const offset = parseInt(options.offset, 10);
|
|
109
|
+
if (!isNaN(offset) && offset > 0)
|
|
110
|
+
params.set('offset', String(offset));
|
|
111
|
+
}
|
|
103
112
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
104
113
|
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules${qs}`);
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
// --fields implies --json
|
|
115
|
+
const useJson = options.json || !!options.fields;
|
|
116
|
+
if (useJson) {
|
|
117
|
+
if (options.fields) {
|
|
118
|
+
const fields = (0, list_options_1.parseFields)(options.fields);
|
|
119
|
+
(0, output_1.printJson)({ ...result, schedules: (0, list_options_1.filterFields)(result.schedules, fields) });
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
(0, output_1.printJson)(result);
|
|
123
|
+
}
|
|
107
124
|
return;
|
|
108
125
|
}
|
|
109
126
|
if (result.schedules.length === 0) {
|
|
@@ -561,7 +578,9 @@ function registerScheduleCommand(program) {
|
|
|
561
578
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
562
579
|
.option('--status <status>', 'Filter by status (completed, failed, running, timeout)')
|
|
563
580
|
.option('--limit <n>', 'Number of runs to show (default: 20)', '20')
|
|
581
|
+
.option('--offset <n>', 'Number of runs to skip')
|
|
564
582
|
.option('--json', 'Output as JSON')
|
|
583
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
565
584
|
.action(async (scheduleId, options) => {
|
|
566
585
|
const config = await (0, config_1.getResolvedConfig)();
|
|
567
586
|
if (!config.apiKey) {
|
|
@@ -572,10 +591,23 @@ function registerScheduleCommand(program) {
|
|
|
572
591
|
params.set('limit', options.limit);
|
|
573
592
|
if (options.status)
|
|
574
593
|
params.set('status', options.status);
|
|
594
|
+
if (options.offset) {
|
|
595
|
+
const offset = parseInt(options.offset, 10);
|
|
596
|
+
if (!isNaN(offset) && offset > 0)
|
|
597
|
+
params.set('offset', String(offset));
|
|
598
|
+
}
|
|
575
599
|
const qs = `?${params.toString()}`;
|
|
576
600
|
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/runs${qs}`);
|
|
577
|
-
|
|
578
|
-
|
|
601
|
+
// --fields implies --json
|
|
602
|
+
const useJson = options.json || !!options.fields;
|
|
603
|
+
if (useJson) {
|
|
604
|
+
if (options.fields) {
|
|
605
|
+
const fields = (0, list_options_1.parseFields)(options.fields);
|
|
606
|
+
(0, output_1.printJson)({ ...result, runs: (0, list_options_1.filterFields)(result.runs, fields) });
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
(0, output_1.printJson)(result);
|
|
610
|
+
}
|
|
579
611
|
return;
|
|
580
612
|
}
|
|
581
613
|
if (!result.runs.length) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerSchemaCommand = registerSchemaCommand;
|
|
4
|
+
const config_1 = require("../lib/config");
|
|
5
|
+
const api_1 = require("../lib/api");
|
|
6
|
+
const agent_ref_1 = require("../lib/agent-ref");
|
|
7
|
+
const errors_1 = require("../lib/errors");
|
|
8
|
+
const info_1 = require("./info");
|
|
9
|
+
function registerSchemaCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('schema <agent>')
|
|
12
|
+
.description('Show agent input/output schemas as machine-readable JSON')
|
|
13
|
+
.option('--input-only', 'Show only the input schema')
|
|
14
|
+
.option('--output-only', 'Show only the output schema')
|
|
15
|
+
.option('--full', 'Show full agent spec (type, execution_engine, schemas, custom_tools, secrets)')
|
|
16
|
+
.action(async (agentArg, options) => {
|
|
17
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
18
|
+
const parsed = (0, agent_ref_1.parseAgentRef)(agentArg);
|
|
19
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
20
|
+
const org = parsed.org ?? configFile.workspace ?? config.defaultOrg;
|
|
21
|
+
if (!org) {
|
|
22
|
+
throw new errors_1.CliError('Missing org. Use org/agent format or set default org.');
|
|
23
|
+
}
|
|
24
|
+
const { agent, version } = parsed;
|
|
25
|
+
const workspaceId = await (0, api_1.resolveWorkspaceIdForOrg)(config, org);
|
|
26
|
+
const agentData = await (0, info_1.getAgentInfo)(config, org, agent, version, workspaceId);
|
|
27
|
+
const ref = `${org}/${agent}@${agentData.version || version}`;
|
|
28
|
+
if (options.inputOnly) {
|
|
29
|
+
process.stdout.write(JSON.stringify(agentData.input_schema || {}, null, 2) + '\n');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (options.outputOnly) {
|
|
33
|
+
process.stdout.write(JSON.stringify(agentData.output_schema || {}, null, 2) + '\n');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (options.full) {
|
|
37
|
+
const output = {
|
|
38
|
+
agent: ref,
|
|
39
|
+
type: agentData.type,
|
|
40
|
+
callable: agentData.callable ?? false,
|
|
41
|
+
supported_providers: agentData.supported_providers,
|
|
42
|
+
input_schema: agentData.input_schema || {},
|
|
43
|
+
output_schema: agentData.output_schema || {},
|
|
44
|
+
};
|
|
45
|
+
if (agentData.description) {
|
|
46
|
+
output.description = agentData.description;
|
|
47
|
+
}
|
|
48
|
+
if (agentData.required_secrets && agentData.required_secrets.length > 0) {
|
|
49
|
+
output.required_secrets = agentData.required_secrets;
|
|
50
|
+
}
|
|
51
|
+
if (agentData.optional_secrets && agentData.optional_secrets.length > 0) {
|
|
52
|
+
output.optional_secrets = agentData.optional_secrets;
|
|
53
|
+
}
|
|
54
|
+
if (agentData.dependencies && agentData.dependencies.length > 0) {
|
|
55
|
+
output.dependencies = agentData.dependencies;
|
|
56
|
+
}
|
|
57
|
+
if (agentData.default_skills && agentData.default_skills.length > 0) {
|
|
58
|
+
output.default_skills = agentData.default_skills;
|
|
59
|
+
}
|
|
60
|
+
if (agentData.custom_tools && agentData.custom_tools.length > 0) {
|
|
61
|
+
output.custom_tools = agentData.custom_tools;
|
|
62
|
+
}
|
|
63
|
+
if (agentData.environment) {
|
|
64
|
+
output.environment = agentData.environment;
|
|
65
|
+
}
|
|
66
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Default: show both schemas with agent ref
|
|
70
|
+
const output = {
|
|
71
|
+
agent: ref,
|
|
72
|
+
type: agentData.type,
|
|
73
|
+
input_schema: agentData.input_schema || {},
|
|
74
|
+
output_schema: agentData.output_schema || {},
|
|
75
|
+
};
|
|
76
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
77
|
+
});
|
|
78
|
+
}
|
package/dist/commands/secrets.js
CHANGED
|
@@ -10,6 +10,8 @@ const config_1 = require("../lib/config");
|
|
|
10
10
|
const api_1 = require("../lib/api");
|
|
11
11
|
const errors_1 = require("../lib/errors");
|
|
12
12
|
const output_1 = require("../lib/output");
|
|
13
|
+
const list_options_1 = require("../lib/list-options");
|
|
14
|
+
const sanitize_1 = require("../lib/sanitize");
|
|
13
15
|
// Known LLM key names → provider. Must stay in sync with gateway's
|
|
14
16
|
// _PROVIDER_TO_SECRET_NAME (db.py) and publish.ts PROVIDER_TO_SECRET_NAME.
|
|
15
17
|
const LLM_KEY_NAME_TO_PROVIDER = {
|
|
@@ -49,6 +51,8 @@ function formatDate(iso) {
|
|
|
49
51
|
return new Date(iso).toLocaleString();
|
|
50
52
|
}
|
|
51
53
|
function validateSecretName(name) {
|
|
54
|
+
// DX-29: reject control chars before format check
|
|
55
|
+
(0, sanitize_1.rejectControlChars)(name, 'secret name');
|
|
52
56
|
if (!name || name.length > 128) {
|
|
53
57
|
throw new errors_1.CliError('Secret name must be 1-128 characters.');
|
|
54
58
|
}
|
|
@@ -78,6 +82,7 @@ function registerSecretsCommand(program) {
|
|
|
78
82
|
.description('List secrets in your workspace (names and metadata, never values)')
|
|
79
83
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
80
84
|
.option('--json', 'Output as JSON')
|
|
85
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
81
86
|
.action(async (options) => {
|
|
82
87
|
const config = await (0, config_1.getResolvedConfig)();
|
|
83
88
|
if (!config.apiKey) {
|
|
@@ -85,8 +90,16 @@ function registerSecretsCommand(program) {
|
|
|
85
90
|
}
|
|
86
91
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
87
92
|
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/secrets`);
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
// --fields implies --json
|
|
94
|
+
const useJson = options.json || !!options.fields;
|
|
95
|
+
if (useJson) {
|
|
96
|
+
if (options.fields) {
|
|
97
|
+
const fields = (0, list_options_1.parseFields)(options.fields);
|
|
98
|
+
(0, output_1.printJson)({ ...result, secrets: (0, list_options_1.filterFields)(result.secrets, fields) });
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
(0, output_1.printJson)(result);
|
|
102
|
+
}
|
|
90
103
|
return;
|
|
91
104
|
}
|
|
92
105
|
if (result.secrets.length === 0) {
|
|
@@ -127,6 +140,8 @@ function registerSecretsCommand(program) {
|
|
|
127
140
|
throw new errors_1.CliError('Missing API key. Run `orch login` first.');
|
|
128
141
|
}
|
|
129
142
|
validateSecretName(name);
|
|
143
|
+
// DX-29: strip dangerous control chars from secret values (keep \n, \r, \t for PEM keys etc.)
|
|
144
|
+
value = (0, sanitize_1.sanitizeSecretValue)(value);
|
|
130
145
|
if (!value) {
|
|
131
146
|
throw new errors_1.CliError('Secret value cannot be empty.');
|
|
132
147
|
}
|
package/dist/commands/service.js
CHANGED
|
@@ -10,6 +10,7 @@ const config_1 = require("../lib/config");
|
|
|
10
10
|
const api_1 = require("../lib/api");
|
|
11
11
|
const errors_1 = require("../lib/errors");
|
|
12
12
|
const output_1 = require("../lib/output");
|
|
13
|
+
const list_options_1 = require("../lib/list-options");
|
|
13
14
|
const spinner_1 = require("../lib/spinner");
|
|
14
15
|
// ============================================
|
|
15
16
|
// HELPERS
|
|
@@ -227,6 +228,9 @@ function registerServiceCommand(program) {
|
|
|
227
228
|
.option('--workspace <slug>', 'Workspace slug (default: current workspace)')
|
|
228
229
|
.option('--status <state>', 'Filter by state')
|
|
229
230
|
.option('--json', 'Output as JSON')
|
|
231
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
232
|
+
.option('--limit <n>', 'Maximum number of services to return (default: 100)')
|
|
233
|
+
.option('--offset <n>', 'Number of services to skip')
|
|
230
234
|
.action(async (options) => {
|
|
231
235
|
const config = await (0, config_1.getResolvedConfig)();
|
|
232
236
|
if (!config.apiKey) {
|
|
@@ -236,11 +240,24 @@ function registerServiceCommand(program) {
|
|
|
236
240
|
const params = new URLSearchParams();
|
|
237
241
|
if (options.status)
|
|
238
242
|
params.set('status', options.status);
|
|
239
|
-
params.set('limit', '100');
|
|
243
|
+
params.set('limit', options.limit ?? '100');
|
|
244
|
+
if (options.offset) {
|
|
245
|
+
const offset = parseInt(options.offset, 10);
|
|
246
|
+
if (!isNaN(offset) && offset > 0)
|
|
247
|
+
params.set('offset', String(offset));
|
|
248
|
+
}
|
|
240
249
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
241
250
|
const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/services${qs}`);
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
// --fields implies --json
|
|
252
|
+
const useJson = options.json || !!options.fields;
|
|
253
|
+
if (useJson) {
|
|
254
|
+
if (options.fields) {
|
|
255
|
+
const fields = (0, list_options_1.parseFields)(options.fields);
|
|
256
|
+
(0, output_1.printJson)({ ...result, services: (0, list_options_1.filterFields)(result.services, fields) });
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
(0, output_1.printJson)(result);
|
|
260
|
+
}
|
|
244
261
|
return;
|
|
245
262
|
}
|
|
246
263
|
if (!result.services.length) {
|
package/dist/commands/storage.js
CHANGED
|
@@ -42,6 +42,7 @@ const config_1 = require("../lib/config");
|
|
|
42
42
|
const api_1 = require("../lib/api");
|
|
43
43
|
const errors_1 = require("../lib/errors");
|
|
44
44
|
const output_1 = require("../lib/output");
|
|
45
|
+
const list_options_1 = require("../lib/list-options");
|
|
45
46
|
// ============================================
|
|
46
47
|
// HELPERS
|
|
47
48
|
// ============================================
|
|
@@ -171,6 +172,7 @@ function registerStorageCommand(program) {
|
|
|
171
172
|
.option('--limit <n>', 'Max keys to return (default: 100)', '100')
|
|
172
173
|
.option('--cursor <cursor>', 'Pagination cursor from previous response')
|
|
173
174
|
.option('--json', 'Output as JSON')
|
|
175
|
+
.option('--fields <fields>', 'Comma-separated fields to include in JSON output (implies --json)')
|
|
174
176
|
.action(async (namespace, options) => {
|
|
175
177
|
const config = await (0, config_1.getResolvedConfig)();
|
|
176
178
|
if (!config.apiKey) {
|
|
@@ -178,11 +180,14 @@ function registerStorageCommand(program) {
|
|
|
178
180
|
}
|
|
179
181
|
const workspaceId = await resolveWorkspaceId(config, options.workspace);
|
|
180
182
|
const headers = { 'X-Workspace-Id': workspaceId };
|
|
183
|
+
// --fields implies --json
|
|
184
|
+
const useJson = options.json || !!options.fields;
|
|
185
|
+
const parsedFields = options.fields ? (0, list_options_1.parseFields)(options.fields) : undefined;
|
|
181
186
|
if (!namespace) {
|
|
182
187
|
// List namespaces
|
|
183
188
|
const result = await (0, api_1.request)(config, 'GET', '/storage', { headers });
|
|
184
|
-
if (
|
|
185
|
-
(0, output_1.printJson)(result);
|
|
189
|
+
if (useJson) {
|
|
190
|
+
(0, output_1.printJson)(parsedFields ? (0, list_options_1.filterFields)(result, parsedFields) : result);
|
|
186
191
|
return;
|
|
187
192
|
}
|
|
188
193
|
if (result.namespaces.length === 0) {
|
|
@@ -203,8 +208,8 @@ function registerStorageCommand(program) {
|
|
|
203
208
|
if (options.cursor)
|
|
204
209
|
path += `&cursor=${encodeURIComponent(options.cursor)}`;
|
|
205
210
|
const result = await (0, api_1.request)(config, 'GET', path, { headers });
|
|
206
|
-
if (
|
|
207
|
-
(0, output_1.printJson)(result);
|
|
211
|
+
if (useJson) {
|
|
212
|
+
(0, output_1.printJson)(parsedFields ? (0, list_options_1.filterFields)(result, parsedFields) : result);
|
|
208
213
|
return;
|
|
209
214
|
}
|
|
210
215
|
if (result.keys.length === 0) {
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ const errors_1 = require("./lib/errors");
|
|
|
47
47
|
const suggest_1 = require("./lib/suggest");
|
|
48
48
|
const analytics_1 = require("./lib/analytics");
|
|
49
49
|
const config_1 = require("./lib/config");
|
|
50
|
+
const output_1 = require("./lib/output");
|
|
50
51
|
const spinner_1 = require("./lib/spinner");
|
|
51
52
|
const update_notifier_1 = require("./lib/update-notifier");
|
|
52
53
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
@@ -82,12 +83,30 @@ async function main() {
|
|
|
82
83
|
if (config.no_progress) {
|
|
83
84
|
(0, spinner_1.setProgressEnabled)(false);
|
|
84
85
|
}
|
|
85
|
-
// Parse args - hook
|
|
86
|
-
program.hook('preAction', () => {
|
|
86
|
+
// Parse args - hook handles --no-progress and TTY auto-detection
|
|
87
|
+
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
87
88
|
const opts = program.opts();
|
|
88
89
|
if (opts.progress === false) {
|
|
89
90
|
(0, spinner_1.setProgressEnabled)(false);
|
|
90
91
|
}
|
|
92
|
+
// TTY auto-detection: non-TTY → auto-enable JSON on commands that support it
|
|
93
|
+
// This lets piped output (orch agents | jq .) and agent consumers get JSON automatically.
|
|
94
|
+
// Override: ORCHAGENT_OUTPUT=text forces human-readable even in non-TTY.
|
|
95
|
+
const hasJsonOption = actionCommand.options.some((o) => o.long === '--json');
|
|
96
|
+
if (hasJsonOption && actionCommand.getOptionValue('json') === undefined) {
|
|
97
|
+
if ((0, output_1.shouldAutoJson)()) {
|
|
98
|
+
actionCommand.setOptionValue('json', true);
|
|
99
|
+
(0, spinner_1.setProgressEnabled)(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Track JSON mode globally so exitWithError can output structured errors
|
|
103
|
+
if (actionCommand.getOptionValue('json')) {
|
|
104
|
+
(0, output_1.setJsonMode)(true);
|
|
105
|
+
}
|
|
106
|
+
// Also disable progress spinners in non-TTY (even if command has no --json option)
|
|
107
|
+
if (!process.stdout.isTTY) {
|
|
108
|
+
(0, spinner_1.setProgressEnabled)(false);
|
|
109
|
+
}
|
|
91
110
|
});
|
|
92
111
|
await program.parseAsync(process.argv);
|
|
93
112
|
}
|
package/dist/lib/agent-ref.js
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseAgentRef = parseAgentRef;
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
|
+
const sanitize_1 = require("./sanitize");
|
|
5
6
|
function parseAgentRef(value, defaultVersion = 'latest') {
|
|
7
|
+
// DX-29: reject unsafe chars in the raw input before parsing
|
|
8
|
+
(0, sanitize_1.rejectResourceIdChars)(value, 'agent reference');
|
|
6
9
|
const [ref, versionPart] = value.split('@');
|
|
7
10
|
const version = versionPart?.trim() || defaultVersion;
|
|
8
11
|
const segments = ref.split('/');
|