@orchagent/cli 0.3.102 → 0.3.103

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.
@@ -193,6 +193,10 @@ function registerDagCommand(program) {
193
193
  throw new errors_1.CliError('Missing API key. Run `orch login` first.');
194
194
  }
195
195
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
196
+ // Accept req_xxx format (gateway request_id shown in run output)
197
+ if (/^req_[0-9a-f]+$/i.test(runId)) {
198
+ runId = runId.slice(4);
199
+ }
196
200
  // Resolve short run IDs
197
201
  let resolvedRunId = runId;
198
202
  if (isUuid(runId)) {
@@ -9,6 +9,7 @@ const config_1 = require("../lib/config");
9
9
  const api_1 = require("../lib/api");
10
10
  const agent_ref_1 = require("../lib/agent-ref");
11
11
  const errors_1 = require("../lib/errors");
12
+ const json_input_1 = require("../lib/json-input");
12
13
  const spinner_1 = require("../lib/spinner");
13
14
  const output_1 = require("../lib/output");
14
15
  const package_json_1 = __importDefault(require("../../package.json"));
@@ -194,13 +195,7 @@ function registerHealthCommand(program) {
194
195
  const inputSchema = agentMeta.input_schema;
195
196
  let body;
196
197
  if (options.data) {
197
- try {
198
- JSON.parse(options.data);
199
- body = options.data;
200
- }
201
- catch {
202
- throw new errors_1.CliError('Invalid JSON in --data option.');
203
- }
198
+ body = await (0, json_input_1.resolveJsonBody)(options.data);
204
199
  }
205
200
  else {
206
201
  const sample = generateSampleInput(inputSchema);
@@ -2242,6 +2242,10 @@ function registerInitCommand(program) {
2242
2242
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
2243
2243
  process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
2244
2244
  }
2245
+ if (initMode.flavor === 'managed_loop') {
2246
+ process.stdout.write(`\n Note: supported_providers: ["any"] means anthropic, openai, or gemini\n`);
2247
+ process.stdout.write(` Your vault key determines which is used (set with: orch secrets set)\n`);
2248
+ }
2245
2249
  process.stdout.write(AGENT_BUILDER_HINT);
2246
2250
  });
2247
2251
  }
@@ -548,11 +548,21 @@ async function batchPublish(rootDir, options) {
548
548
  catch {
549
549
  // Non-critical — just won't show org prefix
550
550
  }
551
- const plan = (0, batch_publish_1.formatPublishPlan)(sorted, orgSlug);
552
- process.stderr.write(plan);
551
+ // Dry-run: show ordering preview and exit (no subprocesses)
553
552
  if (options.dryRun) {
554
- process.stderr.write(chalk_1.default.cyan('DRY RUN running orch publish --dry-run in each directory:\n\n'));
553
+ const summary = (0, batch_publish_1.formatDryRunSummary)(sorted, orgSlug);
554
+ process.stderr.write(summary);
555
+ await (0, analytics_1.track)('cli_publish_all', {
556
+ total: sorted.length,
557
+ succeeded: 0,
558
+ failed: 0,
559
+ skipped: sorted.length,
560
+ dry_run: true,
561
+ });
562
+ return;
555
563
  }
564
+ const plan = (0, batch_publish_1.formatPublishPlan)(sorted, orgSlug);
565
+ process.stderr.write(plan);
556
566
  // Build the CLI args to forward (exclude --all)
557
567
  const forwardArgs = [];
558
568
  if (options.profile)
@@ -50,7 +50,7 @@ function commandForEntrypoint(entrypoint) {
50
50
  || entrypoint.endsWith('.ts')) {
51
51
  return `node ${entrypoint}`;
52
52
  }
53
- return `python ${entrypoint}`;
53
+ return `python3 ${entrypoint}`;
54
54
  }
55
55
  // ─── Agent Resolution ───────────────────────────────────────────────────────
56
56
  async function resolveAgent(config, org, agent, version, workspaceId) {
@@ -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 json_input_1 = require("../lib/json-input");
60
61
  const output_1 = require("../lib/output");
61
62
  const spinner_1 = require("../lib/spinner");
62
63
  const llm_1 = require("../lib/llm");
@@ -360,32 +361,6 @@ async function buildMultipartBody(filePaths, metadata) {
360
361
  sourceLabel: filePaths.length === 1 ? filePaths[0] : `${filePaths.length} files`,
361
362
  };
362
363
  }
363
- async function resolveJsonBody(input) {
364
- let raw = input;
365
- if (input.startsWith('@')) {
366
- const source = input.slice(1);
367
- if (!source) {
368
- throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
369
- }
370
- if (source === '-') {
371
- const stdinData = await readStdin();
372
- if (!stdinData) {
373
- throw new errors_1.CliError('No stdin provided for JSON input.');
374
- }
375
- raw = stdinData.toString('utf8');
376
- }
377
- else {
378
- await validateFilePath(source);
379
- raw = await promises_1.default.readFile(source, 'utf8');
380
- }
381
- }
382
- try {
383
- return JSON.stringify(JSON.parse(raw));
384
- }
385
- catch {
386
- throw (0, errors_1.jsonInputError)('data');
387
- }
388
- }
389
364
  // ─── Keyed file & mount helpers ──────────────────────────────────────────────
390
365
  const KEYED_FILE_KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
391
366
  function isKeyedFileArg(arg) {
@@ -494,7 +469,7 @@ async function buildInjectedPayload(options) {
494
469
  let merged = {};
495
470
  // 1. Start with --data
496
471
  if (options.dataOption) {
497
- const resolved = await resolveJsonBody(options.dataOption);
472
+ const resolved = await (0, json_input_1.resolveJsonBody)(options.dataOption);
498
473
  merged = JSON.parse(resolved);
499
474
  }
500
475
  let totalBytes = 0;
@@ -1952,12 +1927,19 @@ async function executeCloud(agentRef, file, options) {
1952
1927
  return;
1953
1928
  }
1954
1929
  }
1955
- catch {
1956
- // Non-fatal: if estimate fails, proceed with the run (or exit if --estimate-only)
1930
+ catch (err) {
1931
+ // Provide specific error messages based on failure type
1932
+ const detail = err instanceof api_1.ApiError && err.status === 404
1933
+ ? 'Agent not found.'
1934
+ : err instanceof api_1.ApiError && err.status === 429
1935
+ ? 'Rate limited — try again shortly.'
1936
+ : err instanceof api_1.ApiError
1937
+ ? `API error (${err.status}).`
1938
+ : 'Network error — check your connection.';
1957
1939
  if (options.estimateOnly) {
1958
- throw new errors_1.CliError('Could not fetch cost estimate.');
1940
+ throw new errors_1.CliError(`Could not fetch cost estimate: ${detail}`);
1959
1941
  }
1960
- process.stderr.write(chalk_1.default.gray('Could not fetch cost estimate. Proceeding...\n\n'));
1942
+ process.stderr.write(chalk_1.default.gray(`Could not fetch cost estimate: ${detail} Proceeding with run...\n\n`));
1961
1943
  }
1962
1944
  }
1963
1945
  const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
@@ -2069,7 +2051,7 @@ async function executeCloud(agentRef, file, options) {
2069
2051
  }
2070
2052
  if (options.data && filePaths.length > 0) {
2071
2053
  // Merge file content into --data
2072
- const resolvedBody = await resolveJsonBody(options.data);
2054
+ const resolvedBody = await (0, json_input_1.resolveJsonBody)(options.data);
2073
2055
  const bodyObj = JSON.parse(resolvedBody);
2074
2056
  if (cloudEngine !== 'code_runtime') {
2075
2057
  const fieldName = resolveFileField(options.fileField, agentMeta.input_schema);
@@ -2117,7 +2099,7 @@ async function executeCloud(agentRef, file, options) {
2117
2099
  }
2118
2100
  }
2119
2101
  else if (options.data) {
2120
- const resolvedBody = await resolveJsonBody(options.data);
2102
+ const resolvedBody = await (0, json_input_1.resolveJsonBody)(options.data);
2121
2103
  warnIfLocalPathReference(resolvedBody);
2122
2104
  const parsedBody = JSON.parse(resolvedBody);
2123
2105
  warnInputSchemaErrors(parsedBody, agentMeta.input_schema);
@@ -2573,7 +2555,22 @@ async function executeCloud(agentRef, file, options) {
2573
2555
  process.stdout.write(`${payload}\n`);
2574
2556
  return;
2575
2557
  }
2576
- (0, output_1.printJson)(payload);
2558
+ // In verbose mode, strip stdout/stderr from the JSON payload since they'll
2559
+ // be displayed in dedicated colored sections below (avoids duplication)
2560
+ if (options.verbose && typeof payload === 'object' && payload !== null && 'metadata' in payload) {
2561
+ const payloadObj = payload;
2562
+ const meta = payloadObj.metadata;
2563
+ if (meta && (meta.stdout || meta.stderr)) {
2564
+ const { stdout: _s, stderr: _e, ...cleanMeta } = meta;
2565
+ (0, output_1.printJson)({ ...payloadObj, metadata: cleanMeta });
2566
+ }
2567
+ else {
2568
+ (0, output_1.printJson)(payload);
2569
+ }
2570
+ }
2571
+ else {
2572
+ (0, output_1.printJson)(payload);
2573
+ }
2577
2574
  // Display timing metadata on stderr (non-json mode only)
2578
2575
  if (typeof payload === 'object' && payload !== null && 'metadata' in payload) {
2579
2576
  const meta = payload.metadata;
@@ -2585,7 +2582,14 @@ async function executeCloud(agentRef, file, options) {
2585
2582
  if (stderr) {
2586
2583
  process.stderr.write(chalk_1.default.bold.yellow('\n--- stderr ---') + '\n' + stderr + '\n');
2587
2584
  }
2588
- if (stdout) {
2585
+ // For code_runtime agents, stdout IS the data — skip if it would
2586
+ // duplicate what's already visible in the JSON data field
2587
+ const dataStr = typeof payload.data === 'string'
2588
+ ? payload.data
2589
+ : JSON.stringify(payload.data);
2590
+ const stdoutDuplicatesData = isCodeRuntimeAgent && stdout && dataStr &&
2591
+ stdout.trim() === dataStr.trim();
2592
+ if (stdout && !stdoutDuplicatesData) {
2589
2593
  process.stderr.write(chalk_1.default.bold.cyan('\n--- stdout ---') + '\n' + stdout + '\n');
2590
2594
  }
2591
2595
  if (!stderr && !stdout) {
@@ -2685,7 +2689,7 @@ async function executeLocal(agentRef, args, options) {
2685
2689
  return;
2686
2690
  }
2687
2691
  // Resolve @file.json / @- stdin syntax before parsing
2688
- const resolvedInput = await resolveJsonBody(options.input);
2692
+ const resolvedInput = await (0, json_input_1.resolveJsonBody)(options.input);
2689
2693
  let agentInputData;
2690
2694
  try {
2691
2695
  agentInputData = JSON.parse(resolvedInput);
@@ -11,6 +11,7 @@ const promises_1 = __importDefault(require("readline/promises"));
11
11
  const config_1 = require("../lib/config");
12
12
  const api_1 = require("../lib/api");
13
13
  const errors_1 = require("../lib/errors");
14
+ const json_input_1 = require("../lib/json-input");
14
15
  const output_1 = require("../lib/output");
15
16
  const agent_ref_1 = require("../lib/agent-ref");
16
17
  const api_2 = require("../lib/api");
@@ -187,12 +188,8 @@ function registerScheduleCommand(program) {
187
188
  const rawInput = options.data ?? options.input;
188
189
  let inputData;
189
190
  if (rawInput) {
190
- try {
191
- inputData = JSON.parse(rawInput);
192
- }
193
- catch {
194
- throw new errors_1.CliError('Invalid JSON in --data. Use single quotes: --data \'{"key": "value"}\'');
195
- }
191
+ const resolved = await (0, json_input_1.resolveJsonBody)(rawInput);
192
+ inputData = JSON.parse(resolved);
196
193
  }
197
194
  const scheduleType = options.webhook ? 'webhook' : 'cron';
198
195
  const body = {
@@ -105,6 +105,10 @@ function registerTraceCommand(program) {
105
105
  throw new errors_1.CliError('Missing API key. Run `orch login` first.');
106
106
  }
107
107
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
108
+ // Accept req_xxx format (gateway request_id shown in run output)
109
+ if (/^req_[0-9a-f]+$/i.test(runId)) {
110
+ runId = runId.slice(4);
111
+ }
108
112
  // Resolve short run IDs
109
113
  let resolvedRunId = runId;
110
114
  if (isUuid(runId)) {
package/dist/lib/api.js CHANGED
@@ -380,15 +380,18 @@ async function getAgentWithFallback(config, org, agentName, version, workspaceId
380
380
  if (!config.apiKey) {
381
381
  throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
382
382
  }
383
- const userOrg = await getOrg(config, workspaceId);
384
- if (userOrg.slug !== org) {
385
- throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
386
- }
383
+ // Try authenticated lookup in the resolved workspace context
387
384
  const myAgent = await getMyAgent(config, agentName, version, workspaceId);
388
- if (!myAgent) {
389
- throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
385
+ if (myAgent)
386
+ return myAgent;
387
+ // Fallback: if workspace was specified, also check personal org —
388
+ // handles cross-workspace lookups (e.g., team context looking up personal agent)
389
+ if (workspaceId) {
390
+ const personalAgent = await getMyAgent(config, agentName, version);
391
+ if (personalAgent)
392
+ return personalAgent;
390
393
  }
391
- return myAgent;
394
+ throw new ApiError(`Agent '${org}/${agentName}@${version}' not found`, 404);
392
395
  }
393
396
  /**
394
397
  * Resolve a workspace ID from an org slug.
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.discoverAgents = discoverAgents;
7
7
  exports.topoSort = topoSort;
8
8
  exports.formatPublishPlan = formatPublishPlan;
9
+ exports.formatDryRunSummary = formatDryRunSummary;
9
10
  const promises_1 = __importDefault(require("fs/promises"));
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const chalk_1 = __importDefault(require("chalk"));
@@ -221,3 +222,75 @@ function formatPublishPlan(sorted, orgSlug) {
221
222
  lines.push('');
222
223
  return lines.join('\n');
223
224
  }
225
+ /**
226
+ * Format an enhanced dry-run summary for --all --dry-run.
227
+ * Shows publish ordering, local/external dependencies, and graph health.
228
+ */
229
+ function formatDryRunSummary(sorted, orgSlug) {
230
+ const lines = [];
231
+ const localNames = new Set(sorted.map(a => a.name));
232
+ // Collect external deps (referenced but not in the project)
233
+ const externalDeps = new Map(); // ref → [agent names that reference it]
234
+ for (const agent of sorted) {
235
+ for (const ref of agent.dependencyRefs) {
236
+ const depName = ref.includes('/') ? ref.split('/')[1] : ref;
237
+ if (!localNames.has(depName)) {
238
+ const consumers = externalDeps.get(ref) || [];
239
+ consumers.push(agent.name);
240
+ externalDeps.set(ref, consumers);
241
+ }
242
+ }
243
+ }
244
+ // Publish order table
245
+ lines.push('');
246
+ lines.push(chalk_1.default.bold(` Publish order (${sorted.length} agent${sorted.length === 1 ? '' : 's'}):`));
247
+ lines.push('');
248
+ for (let i = 0; i < sorted.length; i++) {
249
+ const agent = sorted[i];
250
+ const type = agent.isSkill ? 'skill' : 'agent';
251
+ const prefix = orgSlug ? `${orgSlug}/` : '';
252
+ // Separate local and external deps for clarity
253
+ const localDeps = [];
254
+ const extDeps = [];
255
+ for (const ref of agent.dependencyRefs) {
256
+ const depName = ref.includes('/') ? ref.split('/')[1] : ref;
257
+ if (localNames.has(depName)) {
258
+ localDeps.push(depName);
259
+ }
260
+ else {
261
+ extDeps.push(ref);
262
+ }
263
+ }
264
+ let depInfo = '';
265
+ if (localDeps.length > 0 || extDeps.length > 0) {
266
+ const parts = [];
267
+ if (localDeps.length > 0)
268
+ parts.push(localDeps.join(', '));
269
+ if (extDeps.length > 0)
270
+ parts.push(extDeps.map(d => `${d} ${chalk_1.default.yellow('(external)')}`).join(', '));
271
+ depInfo = ` ${chalk_1.default.gray('→')} ${parts.join(', ')}`;
272
+ }
273
+ lines.push(` ${chalk_1.default.bold(`${i + 1}.`)} ${prefix}${agent.name} ${chalk_1.default.gray(`[${type}]`)}${depInfo}`);
274
+ lines.push(` ${chalk_1.default.gray(agent.dirName + '/')}`);
275
+ }
276
+ // External dependencies section
277
+ if (externalDeps.size > 0) {
278
+ lines.push('');
279
+ lines.push(chalk_1.default.yellow(` External dependencies (must already be published):`));
280
+ for (const [ref, consumers] of externalDeps) {
281
+ lines.push(` ${ref} ${chalk_1.default.gray(`← ${consumers.join(', ')}`)}`);
282
+ }
283
+ }
284
+ // Summary
285
+ lines.push('');
286
+ const skillCount = sorted.filter(a => a.isSkill).length;
287
+ const agentCount = sorted.length - skillCount;
288
+ const parts = [];
289
+ if (agentCount > 0)
290
+ parts.push(`${agentCount} agent${agentCount === 1 ? '' : 's'}`);
291
+ if (skillCount > 0)
292
+ parts.push(`${skillCount} skill${skillCount === 1 ? '' : 's'}`);
293
+ lines.push(chalk_1.default.green(` ✓ ${parts.join(', ')} ready to publish (no circular dependencies)`));
294
+ lines.push('');
295
+ return lines.join('\n');
296
+ }
@@ -0,0 +1,60 @@
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.resolveJsonBody = resolveJsonBody;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const errors_1 = require("./errors");
9
+ async function readStdin() {
10
+ if (process.stdin.isTTY)
11
+ return null;
12
+ const chunks = [];
13
+ for await (const chunk of process.stdin) {
14
+ chunks.push(Buffer.from(chunk));
15
+ }
16
+ if (!chunks.length)
17
+ return null;
18
+ return Buffer.concat(chunks);
19
+ }
20
+ async function validateFilePath(filePath) {
21
+ const stat = await promises_1.default.stat(filePath);
22
+ if (stat.isDirectory()) {
23
+ throw new errors_1.CliError(`Expected a file but got a directory: ${filePath}\n\n` +
24
+ `Provide a JSON file path, e.g. --data @input.json`);
25
+ }
26
+ }
27
+ /**
28
+ * Resolve a --data value to a validated JSON string.
29
+ *
30
+ * Supports three input forms:
31
+ * - Plain JSON string: '{"key":"value"}'
32
+ * - File reference: @input.json
33
+ * - Stdin pipe: @-
34
+ */
35
+ async function resolveJsonBody(input) {
36
+ let raw = input;
37
+ if (input.startsWith('@')) {
38
+ const source = input.slice(1);
39
+ if (!source) {
40
+ throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
41
+ }
42
+ if (source === '-') {
43
+ const stdinData = await readStdin();
44
+ if (!stdinData) {
45
+ throw new errors_1.CliError('No stdin provided for JSON input.');
46
+ }
47
+ raw = stdinData.toString('utf8');
48
+ }
49
+ else {
50
+ await validateFilePath(source);
51
+ raw = await promises_1.default.readFile(source, 'utf8');
52
+ }
53
+ }
54
+ try {
55
+ return JSON.stringify(JSON.parse(raw));
56
+ }
57
+ catch {
58
+ throw (0, errors_1.jsonInputError)('data');
59
+ }
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.102",
3
+ "version": "0.3.103",
4
4
  "description": "Command-line interface for orchagent — deploy and run AI agents for your team",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",