@orchagent/cli 0.3.43 → 0.3.45

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.
@@ -1,577 +1,23 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.registerCallCommand = registerCallCommand;
7
- const promises_1 = __importDefault(require("fs/promises"));
8
- const path_1 = __importDefault(require("path"));
9
- const config_1 = require("../lib/config");
10
- const api_1 = require("../lib/api");
11
- const errors_1 = require("../lib/errors");
12
- const output_1 = require("../lib/output");
13
- const spinner_1 = require("../lib/spinner");
14
- const llm_1 = require("../lib/llm");
15
- const analytics_1 = require("../lib/analytics");
16
- const pricing_1 = require("../lib/pricing");
17
- const DEFAULT_VERSION = 'latest';
18
- // Well-known field names for file content in prompt agent schemas (priority order)
19
- const CONTENT_FIELD_NAMES = ['code', 'content', 'text', 'source', 'input', 'file_content', 'body'];
20
- // Keys that might indicate local file path references in JSON payloads
21
- const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
22
4
  /**
23
- * Check if a parsed JSON object contains keys that might reference local filesystem paths.
24
- * Returns the first matching key found, or undefined if none found.
5
+ * Deprecated: The 'call' command has been merged into 'run'.
6
+ * Cloud execution is now the default behavior of 'orch run'.
7
+ * This file provides a thin alias that prints a deprecation notice and exits.
25
8
  */
26
- function findLocalPathKey(obj) {
27
- if (typeof obj !== 'object' || obj === null) {
28
- return undefined;
29
- }
30
- const keys = Object.keys(obj);
31
- for (const key of keys) {
32
- if (LOCAL_PATH_KEYS.includes(key.toLowerCase())) {
33
- return key;
34
- }
35
- }
36
- return undefined;
37
- }
38
- /**
39
- * Emit a warning to stderr if the payload contains local path references.
40
- */
41
- function warnIfLocalPathReference(jsonBody) {
42
- try {
43
- const parsed = JSON.parse(jsonBody);
44
- const pathKey = findLocalPathKey(parsed);
45
- if (pathKey) {
46
- process.stderr.write(`Warning: Your payload contains a local path reference ('${pathKey}').\n` +
47
- `Remote agents cannot access your local filesystem. The path will be interpreted\n` +
48
- `by the server, not your local machine.\n\n` +
49
- `Tip: Use 'orchagent run <agent>' instead to execute locally with filesystem access.\n\n`);
50
- }
51
- }
52
- catch {
53
- // If parsing fails, skip the warning (the actual error will be thrown later)
54
- }
55
- }
56
- /**
57
- * Infer the best JSON field name for file content based on the agent's input schema.
58
- * Returns the field name to use, or 'content' as a safe default.
59
- */
60
- function inferFileField(inputSchema) {
61
- if (!inputSchema || typeof inputSchema !== 'object')
62
- return 'content';
63
- const props = inputSchema.properties;
64
- if (!props || typeof props !== 'object')
65
- return 'content';
66
- const properties = props;
67
- // Check for well-known field names in priority order
68
- for (const field of CONTENT_FIELD_NAMES) {
69
- if (properties[field] && properties[field].type === 'string')
70
- return field;
71
- }
72
- // If there's exactly one required string property, use that
73
- const required = (inputSchema.required ?? []);
74
- const stringProps = Object.entries(properties)
75
- .filter(([, v]) => v.type === 'string')
76
- .map(([k]) => k);
77
- if (stringProps.length === 1)
78
- return stringProps[0];
79
- const requiredStrings = stringProps.filter(k => required.includes(k));
80
- if (requiredStrings.length === 1)
81
- return requiredStrings[0];
82
- return 'content';
83
- }
84
- function parseAgentRef(value) {
85
- const [ref, versionPart] = value.split('@');
86
- const version = versionPart?.trim() || DEFAULT_VERSION;
87
- const segments = ref.split('/');
88
- if (segments.length === 1) {
89
- return { agent: segments[0], version };
90
- }
91
- if (segments.length === 2) {
92
- return { org: segments[0], agent: segments[1], version };
93
- }
94
- throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
95
- }
96
- async function readStdin() {
97
- if (process.stdin.isTTY)
98
- return null;
99
- const chunks = [];
100
- for await (const chunk of process.stdin) {
101
- chunks.push(Buffer.from(chunk));
102
- }
103
- if (!chunks.length)
104
- return null;
105
- return Buffer.concat(chunks);
106
- }
107
- async function buildMultipartBody(filePaths, metadata) {
108
- if (!filePaths || filePaths.length === 0) {
109
- const stdinData = await readStdin();
110
- if (stdinData) {
111
- const form = new FormData();
112
- form.append('files[]', new Blob([new Uint8Array(stdinData)]), 'stdin');
113
- if (metadata) {
114
- form.append('metadata', metadata);
115
- }
116
- return { body: form, sourceLabel: 'stdin' };
117
- }
118
- if (metadata) {
119
- const form = new FormData();
120
- form.append('metadata', metadata);
121
- return { body: form, sourceLabel: 'metadata' };
122
- }
123
- return {};
124
- }
125
- const form = new FormData();
126
- for (const filePath of filePaths) {
127
- const buffer = await promises_1.default.readFile(filePath);
128
- const filename = path_1.default.basename(filePath);
129
- form.append('files[]', new Blob([new Uint8Array(buffer)]), filename);
130
- }
131
- if (metadata) {
132
- form.append('metadata', metadata);
133
- }
134
- return {
135
- body: form,
136
- sourceLabel: filePaths.length === 1 ? filePaths[0] : `${filePaths.length} files`,
137
- };
138
- }
139
- async function resolveJsonBody(input) {
140
- let raw = input;
141
- if (input.startsWith('@')) {
142
- const source = input.slice(1);
143
- if (!source) {
144
- throw new errors_1.CliError('Invalid JSON input. Use a JSON string or @file.');
145
- }
146
- if (source === '-') {
147
- const stdinData = await readStdin();
148
- if (!stdinData) {
149
- throw new errors_1.CliError('No stdin provided for JSON input.');
150
- }
151
- raw = stdinData.toString('utf8');
152
- }
153
- else {
154
- raw = await promises_1.default.readFile(source, 'utf8');
155
- }
156
- }
157
- try {
158
- return JSON.stringify(JSON.parse(raw));
159
- }
160
- catch {
161
- throw (0, errors_1.jsonInputError)('data');
162
- }
163
- }
164
9
  function registerCallCommand(program) {
165
10
  program
166
- .command('call <agent> [file]')
167
- .description('Call an agent on the server (may incur charges for paid agents)')
168
- .option('--endpoint <endpoint>', 'Override agent endpoint')
169
- .option('--tenant <tenant>', 'Tenant identifier for multi-tenant callers')
170
- .option('--data <json>', 'JSON payload (string or @file, @- for stdin)')
171
- .option('--input <json>', 'Alias for --data')
172
- .option('--key <key>', 'LLM API key (overrides env vars)')
173
- .option('--provider <provider>', 'LLM provider (openai, anthropic, gemini)')
174
- .option('--model <model>', 'LLM model to use (overrides agent default)')
175
- .option('--json', 'Output raw JSON')
176
- .option('--output <file>', 'Save response body to a file')
177
- .option('--skills <skills>', 'Add skills (comma-separated)')
178
- .option('--skills-only <skills>', 'Use only these skills')
179
- .option('--no-skills', 'Ignore default skills')
180
- .option('--file <path...>', 'File(s) to upload (can specify multiple)')
181
- .option('--file-field <field>', 'Schema field name for file content (prompt agents)')
182
- .option('--metadata <json>', 'JSON metadata to send with files')
183
- .addHelpText('after', `
184
- Examples:
185
- orch call orchagent/invoice-scanner invoice.pdf
186
- orch call orchagent/useeffect-checker --file src/App.tsx
187
- orch call orchagent/useeffect-checker --file src/App.tsx --file-field code
188
- orch call orchagent/leak-finder --data '{"repo_url": "https://github.com/org/repo"}'
189
- cat input.json | orch call acme/agent --data @-
190
- orch call acme/image-processor photo.jpg --output result.png
191
-
192
- Note: Use 'call' for server-side execution (requires login), 'run' for local execution.
193
-
194
- File handling:
195
- For prompt agents, file content is read and sent as JSON mapped to the agent's
196
- input schema. Use --file-field to specify the field name (auto-detected by default).
197
- For tools, files are uploaded as multipart form data.
198
-
199
- Important: Remote agents cannot access your local filesystem. If your --data payload
200
- contains keys like 'path', 'directory', 'file', etc., those values will be interpreted
201
- by the server, not your local machine. To send local files, use the positional file
202
- argument or --file option instead.
203
-
204
- Paid Agents:
205
- Paid agents charge per call and deduct from your prepaid credits.
206
- Check your balance: orch billing balance
207
- Add credits: orch billing add 5
208
-
209
- Same-author calls are FREE - you won't be charged for calling your own agents.
210
- `)
211
- .action(async (agentRef, file, options) => {
212
- // Merge --input alias into --data
213
- const dataValue = options.data || options.input;
214
- options.data = dataValue;
215
- const resolved = await (0, config_1.getResolvedConfig)();
216
- if (!resolved.apiKey) {
217
- throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
218
- }
219
- const parsed = parseAgentRef(agentRef);
220
- const configFile = await (0, config_1.loadConfig)();
221
- const org = parsed.org ?? configFile.workspace ?? resolved.defaultOrg;
222
- if (!org) {
223
- throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
224
- }
225
- const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version);
226
- // Part 1: Pre-call balance check for paid agents
227
- let pricingInfo;
228
- if ((0, pricing_1.isPaidAgent)(agentMeta)) {
229
- // Detect ownership: compare agent's org with caller's org
230
- let isOwner = false;
231
- try {
232
- const callerOrg = await (0, api_1.getOrg)(resolved);
233
- // Use org_id when available (preferred), org_slug as fallback
234
- const agentOrgId = agentMeta.org_id;
235
- const agentOrgSlug = agentMeta.org_slug;
236
- if (agentOrgId && callerOrg.id === agentOrgId) {
237
- isOwner = true;
238
- }
239
- else if (agentOrgSlug && callerOrg.slug === agentOrgSlug) {
240
- isOwner = true;
241
- }
242
- }
243
- catch {
244
- // If we can't determine ownership, treat as non-owner (fail-safe)
245
- isOwner = false;
246
- }
247
- if (isOwner) {
248
- // Owner: show free message, no balance check needed
249
- if (!options.json)
250
- process.stderr.write(`Cost: FREE (author)\n\n`);
251
- }
252
- else {
253
- // Non-owner: check balance
254
- const price = agentMeta.price_per_call_cents;
255
- pricingInfo = { price_cents: price ?? null };
256
- if (!price || price <= 0) {
257
- // Price missing or invalid - warn but proceed (server will enforce)
258
- if (!options.json)
259
- process.stderr.write(`Warning: Pricing data unavailable. The server will verify payment.\n\n`);
260
- }
261
- else {
262
- // Valid price - check balance
263
- try {
264
- const balanceData = await (0, api_1.getCreditsBalance)(resolved);
265
- const balance = balanceData.balance_cents;
266
- if (balance < price) {
267
- // Insufficient balance
268
- process.stderr.write(`Insufficient credits:\n` +
269
- ` Balance: $${(balance / 100).toFixed(2)}\n` +
270
- ` Required: $${(price / 100).toFixed(2)}\n\n` +
271
- `Add credits:\n` +
272
- ` orch billing add 5\n` +
273
- ` orch billing balance # check current balance\n`);
274
- process.exit(errors_1.ExitCodes.PERMISSION_DENIED);
275
- }
276
- // Sufficient balance - show cost preview
277
- if (!options.json)
278
- process.stderr.write(`Cost: $${(price / 100).toFixed(2)}/call\n\n`);
279
- }
280
- catch (err) {
281
- // Balance check failed - warn but proceed (server will enforce)
282
- if (!options.json)
283
- process.stderr.write(`Warning: Could not verify balance. The server will check payment.\n\n`);
284
- }
285
- }
286
- }
287
- }
288
- const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
289
- const headers = {
290
- Authorization: `Bearer ${resolved.apiKey}`,
291
- };
292
- if (options.tenant) {
293
- headers['X-OrchAgent-Tenant'] = options.tenant;
294
- }
295
- const supportedProviders = agentMeta.supported_providers || ['any'];
296
- let llmKey;
297
- let llmProvider;
298
- // Resolve effective provider: CLI flag > config default
299
- const configDefaultProvider = await (0, config_1.getDefaultProvider)();
300
- const effectiveProvider = options.provider ?? configDefaultProvider;
301
- if (options.key) {
302
- // Explicit key provided - require provider
303
- if (!effectiveProvider) {
304
- throw new errors_1.CliError('When using --key, you must also specify --provider (openai, anthropic, or gemini)');
305
- }
306
- (0, llm_1.validateProvider)(effectiveProvider);
307
- // Warn on potential model/provider mismatch
308
- if (options.model && effectiveProvider) {
309
- const modelLower = options.model.toLowerCase();
310
- const providerPatterns = {
311
- openai: /^(gpt-|o1-|o3-|davinci|text-)/,
312
- anthropic: /^claude-/,
313
- gemini: /^gemini-/,
314
- ollama: /^(llama|mistral|deepseek|phi|qwen)/,
315
- };
316
- const expectedPattern = providerPatterns[effectiveProvider];
317
- if (expectedPattern && !expectedPattern.test(modelLower)) {
318
- process.stderr.write(`Warning: Model '${options.model}' may not be a ${effectiveProvider} model.\n\n`);
319
- }
320
- }
321
- llmKey = options.key;
322
- llmProvider = effectiveProvider;
323
- }
324
- else {
325
- // Try to detect from environment or server
326
- // If provider specified (flag or config default), prioritize that provider
327
- let providersToCheck = supportedProviders;
328
- if (effectiveProvider) {
329
- (0, llm_1.validateProvider)(effectiveProvider);
330
- providersToCheck = [effectiveProvider];
331
- // Warn on potential model/provider mismatch
332
- if (options.model) {
333
- const modelLower = options.model.toLowerCase();
334
- const providerPatterns = {
335
- openai: /^(gpt-|o1-|o3-|davinci|text-)/,
336
- anthropic: /^claude-/,
337
- gemini: /^gemini-/,
338
- ollama: /^(llama|mistral|deepseek|phi|qwen)/,
339
- };
340
- const expectedPattern = providerPatterns[effectiveProvider];
341
- if (expectedPattern && !expectedPattern.test(modelLower)) {
342
- process.stderr.write(`Warning: Model '${options.model}' may not be a ${effectiveProvider} model.\n\n`);
343
- }
344
- }
345
- }
346
- const detected = await (0, llm_1.detectLlmKey)(providersToCheck, resolved);
347
- if (detected) {
348
- llmKey = detected.key;
349
- llmProvider = detected.provider;
350
- }
351
- }
352
- // LLM credentials will be added to request body (not headers) for security
353
- // Headers can be logged by proxies/load balancers, body is not logged by default
354
- let llmCredentials;
355
- if (llmKey && llmProvider) {
356
- llmCredentials = {
357
- api_key: llmKey,
358
- provider: llmProvider,
359
- ...(options.model && { model: options.model }),
360
- };
361
- }
362
- else if (agentMeta.type === 'prompt') {
363
- // Warn if no key found for prompt-based agent
364
- const searchedProviders = effectiveProvider ? [effectiveProvider] : supportedProviders;
365
- const providerList = searchedProviders.join(', ');
366
- process.stderr.write(`Warning: No LLM key found for provider(s): ${providerList}\n` +
367
- `Set an env var (e.g., OPENAI_API_KEY), run 'orchagent keys add <provider>', use --key, or configure in web dashboard\n\n`);
368
- }
369
- // Add skill headers
370
- if (options.skills) {
371
- headers['X-OrchAgent-Skills'] = options.skills;
372
- }
373
- if (options.skillsOnly) {
374
- headers['X-OrchAgent-Skills-Only'] = options.skillsOnly;
375
- }
376
- if (options.noSkills) {
377
- headers['X-OrchAgent-No-Skills'] = 'true';
378
- }
379
- let body;
380
- let sourceLabel;
381
- const filePaths = [
382
- ...(options.file ?? []),
383
- ...(file ? [file] : []),
384
- ];
385
- if (options.data) {
386
- if (filePaths.length > 0 || options.metadata) {
387
- throw new errors_1.CliError('Cannot use --data with file uploads or --metadata.');
388
- }
389
- // Parse JSON and inject llm_credentials if available
390
- const resolvedBody = await resolveJsonBody(options.data);
391
- // Warn if payload contains local path references
392
- warnIfLocalPathReference(resolvedBody);
393
- if (llmCredentials) {
394
- const bodyObj = JSON.parse(resolvedBody);
395
- bodyObj.llm_credentials = llmCredentials;
396
- body = JSON.stringify(bodyObj);
397
- }
398
- else {
399
- body = resolvedBody;
400
- }
401
- headers['Content-Type'] = 'application/json';
402
- }
403
- else if ((filePaths.length > 0 || options.metadata) && agentMeta.type === 'prompt') {
404
- // Prompt agent + files/metadata: read content and send as JSON
405
- const fieldName = options.fileField || inferFileField(agentMeta.input_schema);
406
- let bodyObj = {};
407
- // Include metadata if provided
408
- if (options.metadata) {
409
- try {
410
- bodyObj = JSON.parse(options.metadata);
411
- }
412
- catch {
413
- throw new errors_1.CliError('--metadata must be valid JSON.');
414
- }
415
- }
416
- if (filePaths.length === 1) {
417
- // Single file: map content to the inferred/specified schema field
418
- const fileContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
419
- bodyObj[fieldName] = fileContent;
420
- sourceLabel = filePaths[0];
421
- }
422
- else if (filePaths.length > 1) {
423
- // Multiple files: map first to the schema field, add all as files object
424
- const allContents = {};
425
- for (const fp of filePaths) {
426
- allContents[path_1.default.basename(fp)] = await promises_1.default.readFile(fp, 'utf-8');
427
- }
428
- // Set the primary field to the first file's content
429
- const firstContent = await promises_1.default.readFile(filePaths[0], 'utf-8');
430
- bodyObj[fieldName] = firstContent;
431
- bodyObj.files = allContents;
432
- sourceLabel = `${filePaths.length} files`;
433
- }
434
- if (llmCredentials) {
435
- bodyObj.llm_credentials = llmCredentials;
436
- }
437
- body = JSON.stringify(bodyObj);
438
- headers['Content-Type'] = 'application/json';
439
- }
440
- else if (filePaths.length > 0 || options.metadata) {
441
- // Tool: handle multipart file uploads
442
- // Inject llm_credentials into metadata if available
443
- let metadata = options.metadata;
444
- if (llmCredentials) {
445
- const metaObj = metadata ? JSON.parse(metadata) : {};
446
- metaObj.llm_credentials = llmCredentials;
447
- metadata = JSON.stringify(metaObj);
448
- }
449
- const multipart = await buildMultipartBody(filePaths, metadata);
450
- body = multipart.body;
451
- sourceLabel = multipart.sourceLabel;
452
- }
453
- else if (llmCredentials) {
454
- // No data or files, but we have LLM credentials - send as JSON body
455
- body = JSON.stringify({ llm_credentials: llmCredentials });
456
- headers['Content-Type'] = 'application/json';
457
- }
458
- else {
459
- // No data, files, or credentials - check for stdin
460
- const multipart = await buildMultipartBody(undefined, options.metadata);
461
- body = multipart.body;
462
- sourceLabel = multipart.sourceLabel;
463
- }
464
- const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}`;
465
- // Make the API call with a spinner (suppress in --json mode for clean machine-readable output)
466
- const spinner = options.json ? null : (0, spinner_1.createSpinner)(`Calling ${org}/${parsed.agent}@${parsed.version}...`);
467
- spinner?.start();
468
- let response;
469
- try {
470
- response = await (0, api_1.safeFetchWithRetryForCalls)(url, {
471
- method: 'POST',
472
- headers,
473
- body,
474
- });
475
- }
476
- catch (err) {
477
- spinner?.fail(`Call failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
478
- throw err;
479
- }
480
- if (!response.ok) {
481
- const text = await response.text();
482
- let payload;
483
- try {
484
- payload = JSON.parse(text);
485
- }
486
- catch {
487
- payload = text;
488
- }
489
- // Handle specific error codes with helpful messages
490
- const errorCode = typeof payload === 'object' && payload
491
- ? payload.error?.code
492
- : undefined;
493
- // Part 2: Handle 402 Payment Required
494
- if (response.status === 402 || errorCode === 'INSUFFICIENT_CREDITS') {
495
- spinner?.fail('Insufficient credits');
496
- let errorMessage = 'Insufficient credits to call this agent.\n\n';
497
- // Use pricing info from pre-call check if available
498
- if (pricingInfo?.price_cents) {
499
- errorMessage += `This agent costs $${(pricingInfo.price_cents / 100).toFixed(2)} per call.\n\n`;
500
- }
501
- errorMessage +=
502
- 'Add credits:\n' +
503
- ' orch billing add 5\n' +
504
- ' orch billing balance # check current balance\n';
505
- throw new errors_1.CliError(errorMessage, errors_1.ExitCodes.PERMISSION_DENIED);
506
- }
507
- if (errorCode === 'LLM_KEY_REQUIRED') {
508
- spinner?.fail('LLM key required');
509
- throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
510
- 'Use --key <key> --provider <provider> or set OPENAI_API_KEY/ANTHROPIC_API_KEY env var.');
511
- }
512
- if (errorCode === 'LLM_RATE_LIMITED') {
513
- const rateLimitMsg = typeof payload === 'object' && payload
514
- ? payload.error?.message || 'Rate limit exceeded'
515
- : 'Rate limit exceeded';
516
- spinner?.fail('Rate limited by LLM provider');
517
- throw new errors_1.CliError(rateLimitMsg + '\n\n' +
518
- 'This is the LLM provider\'s rate limit on your API key, not an OrchAgent limit.\n' +
519
- 'To switch providers: orch call <agent> --provider <gemini|anthropic|openai>', errors_1.ExitCodes.RATE_LIMITED);
520
- }
521
- const message = typeof payload === 'object' && payload
522
- ? payload.error
523
- ?.message ||
524
- payload.message ||
525
- response.statusText
526
- : response.statusText;
527
- spinner?.fail(`Call failed: ${message}`);
528
- throw new errors_1.CliError(message);
529
- }
530
- spinner?.succeed(`Called ${org}/${parsed.agent}@${parsed.version}`);
531
- // After successful call, if it was a paid agent, show cost (suppress in --json mode)
532
- if (!options.json && (0, pricing_1.isPaidAgent)(agentMeta) && pricingInfo?.price_cents && pricingInfo.price_cents > 0) {
533
- process.stderr.write(`\nCost: $${(pricingInfo.price_cents / 100).toFixed(2)} USD\n`);
534
- }
535
- // Track successful call
536
- const inputType = filePaths.length > 0
537
- ? 'file'
538
- : options.data
539
- ? 'json'
540
- : sourceLabel === 'stdin'
541
- ? 'stdin'
542
- : sourceLabel === 'metadata'
543
- ? 'metadata'
544
- : 'empty';
545
- await (0, analytics_1.track)('cli_call', {
546
- agent: `${org}/${parsed.agent}@${parsed.version}`,
547
- input_type: inputType,
548
- });
549
- if (options.output) {
550
- const buffer = Buffer.from(await response.arrayBuffer());
551
- await promises_1.default.writeFile(options.output, buffer);
552
- process.stdout.write(`Saved response to ${options.output}\n`);
553
- return;
554
- }
555
- const text = await response.text();
556
- let payload;
557
- try {
558
- payload = JSON.parse(text);
559
- }
560
- catch {
561
- payload = text;
562
- }
563
- if (options.json) {
564
- if (typeof payload === 'string') {
565
- process.stdout.write(`${payload}\n`);
566
- return;
567
- }
568
- (0, output_1.printJson)(payload);
569
- return;
570
- }
571
- if (typeof payload === 'string') {
572
- process.stdout.write(`${payload}\n`);
573
- return;
574
- }
575
- (0, output_1.printJson)(payload);
11
+ .command('call')
12
+ .description('Deprecated: use "orch run" instead')
13
+ .allowUnknownOption()
14
+ .allowExcessArguments()
15
+ .action(() => {
16
+ process.stderr.write(`The 'call' command has been merged into 'run'.\n\n` +
17
+ `Cloud execution is now the default behavior of 'orch run':\n` +
18
+ ` orch run <agent> --data '{...}' # runs on server (cloud)\n` +
19
+ ` orch run <agent> --local --data '...' # runs locally\n\n` +
20
+ `Replace 'orch call' with 'orch run' in your commands.\n`);
21
+ process.exit(1);
576
22
  });
577
23
  }
@@ -173,7 +173,7 @@ function registerInfoCommand(program) {
173
173
  process.stdout.write(`Price: ${color(priceStr)}\n`);
174
174
  // If paid, show server-only message for non-owners
175
175
  if ((0, pricing_1.isPaidAgent)(agentData)) {
176
- process.stdout.write(chalk_1.default.gray('Note: Paid agents run on server only (use orch call)\n'));
176
+ process.stdout.write(chalk_1.default.gray('Note: Paid agents run on server only (use orch run)\n'));
177
177
  process.stdout.write(chalk_1.default.gray(' Owners can still download for development/testing\n'));
178
178
  }
179
179
  if (agentData.type === 'tool') {