@orchagent/cli 0.3.83 → 0.3.85

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.
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.extractTemplateVariables = extractTemplateVariables;
40
40
  exports.deriveInputSchema = deriveInputSchema;
41
41
  exports.scanUndeclaredEnvVars = scanUndeclaredEnvVars;
42
+ exports.scanReservedPort = scanReservedPort;
42
43
  exports.detectSdkCompatible = detectSdkCompatible;
43
44
  exports.checkDependencies = checkDependencies;
44
45
  exports.registerPublishCommand = registerPublishCommand;
@@ -167,6 +168,68 @@ async function scanUndeclaredEnvVars(agentDir, requiredSecrets) {
167
168
  // Return env vars that are referenced but not declared or auto-injected
168
169
  return [...found].filter(v => !declared.has(v) && !autoInjected.has(v)).sort();
169
170
  }
171
+ /**
172
+ * Scan code files for patterns that bind to port 8080 — the platform reserves
173
+ * this port for the always-on service health server. Returns true if a likely
174
+ * port-8080 binding is detected.
175
+ */
176
+ async function scanReservedPort(agentDir) {
177
+ // Patterns that indicate binding to port 8080 in Python / JS / TS
178
+ const pyPatterns = [
179
+ /\.listen\s*\(\s*8080\b/, // server.listen(8080
180
+ /\.run\s*\([^)]*port\s*=\s*8080\b/, // app.run(port=8080
181
+ /\.run\s*\([^)]*host\s*=\s*['"][^'"]*['"],?\s*8080\b/, // app.run("0.0.0.0", 8080
182
+ /bind\s*\(\s*\(?['"][^'"]*['"]\s*,\s*8080\b/, // bind(("0.0.0.0", 8080
183
+ /PORT\s*=\s*8080\b/, // PORT = 8080
184
+ /port\s*=\s*8080\b/, // port=8080
185
+ ];
186
+ const jsPatterns = [
187
+ /\.listen\s*\(\s*8080\b/, // app.listen(8080
188
+ /port\s*[:=]\s*8080\b/, // port: 8080 or port = 8080
189
+ /PORT\s*[:=]\s*8080\b/, // PORT = 8080
190
+ ];
191
+ async function scanDir(dir, depth) {
192
+ let entries;
193
+ try {
194
+ entries = await promises_1.default.readdir(dir, { withFileTypes: true });
195
+ if (!entries || !Array.isArray(entries))
196
+ return false;
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ for (const entry of entries) {
202
+ const name = entry.name;
203
+ const fullPath = path_1.default.join(dir, name);
204
+ if (entry.isDirectory() && depth < 2 && !name.startsWith('.') && name !== 'node_modules' && name !== '__pycache__' && name !== 'venv' && name !== '.venv') {
205
+ if (await scanDir(fullPath, depth + 1))
206
+ return true;
207
+ }
208
+ else if (entry.isFile() && name.endsWith('.py')) {
209
+ try {
210
+ const content = await promises_1.default.readFile(fullPath, 'utf-8');
211
+ for (const re of pyPatterns) {
212
+ if (re.test(content))
213
+ return true;
214
+ }
215
+ }
216
+ catch { /* skip */ }
217
+ }
218
+ else if (entry.isFile() && (name.endsWith('.js') || name.endsWith('.ts'))) {
219
+ try {
220
+ const content = await promises_1.default.readFile(fullPath, 'utf-8');
221
+ for (const re of jsPatterns) {
222
+ if (re.test(content))
223
+ return true;
224
+ }
225
+ }
226
+ catch { /* skip */ }
227
+ }
228
+ }
229
+ return false;
230
+ }
231
+ return scanDir(agentDir, 0);
232
+ }
170
233
  /**
171
234
  * Check if orchagent-sdk is listed in requirements.txt or pyproject.toml
172
235
  */
@@ -319,11 +382,8 @@ function inferExecutionEngineFromManifest(manifest, rawType) {
319
382
  return 'managed_loop';
320
383
  if (rawType === 'tool' || rawType === 'code')
321
384
  return 'code_runtime';
322
- if (rawType === 'agentic')
385
+ if (rawType === 'agentic' || rawType === 'agent')
323
386
  return 'managed_loop';
324
- if (rawType === 'agent' && (manifest.custom_tools?.length || manifest.max_turns)) {
325
- return 'managed_loop';
326
- }
327
387
  return 'direct_llm';
328
388
  }
329
389
  function commandForEntrypoint(entrypoint) {
@@ -485,6 +545,7 @@ function registerPublishCommand(program) {
485
545
  description: skillData.frontmatter.description,
486
546
  prompt: skillData.body,
487
547
  is_public: false,
548
+ callable: false,
488
549
  supported_providers: ['any'],
489
550
  default_skills: skillsFromFlag,
490
551
  skills_locked: options.skillsLocked || undefined,
@@ -495,7 +556,7 @@ function registerPublishCommand(program) {
495
556
  const skillVersion = skillResult.agent?.version || 'v1';
496
557
  const skillAgentId = skillResult.agent?.id;
497
558
  await (0, analytics_1.track)('cli_publish', { agent_type: 'skill', multi_file: hasMultipleFiles });
498
- process.stdout.write(`\nPublished skill: ${org.slug}/${skillData.frontmatter.name}@${skillVersion}\n`);
559
+ process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${skillData.frontmatter.name}@${skillVersion} successfully!\n\n`);
499
560
  if (hasMultipleFiles) {
500
561
  process.stdout.write(`Files: ${skillFiles.length} files included\n`);
501
562
  }
@@ -542,7 +603,7 @@ function registerPublishCommand(program) {
542
603
  const { canonicalType, rawType } = canonicalizeManifestType(manifest.type);
543
604
  const runMode = normalizeRunMode(manifest.run_mode);
544
605
  const executionEngine = inferExecutionEngineFromManifest(manifest, rawType);
545
- const callable = Boolean(manifest.callable);
606
+ const callable = manifest.callable !== undefined ? Boolean(manifest.callable) : true;
546
607
  if (canonicalType === 'skill') {
547
608
  throw new errors_1.CliError("Use SKILL.md for publishing skills. Remove orchagent.json and run 'orchagent publish' from a skill directory.");
548
609
  }
@@ -706,6 +767,7 @@ function registerPublishCommand(program) {
706
767
  }
707
768
  let agentUrl = options.url;
708
769
  let shouldUploadBundle = false;
770
+ let servicesUpdated = 0;
709
771
  let runtimeConfig;
710
772
  let bundleEntrypoint = manifest.entrypoint;
711
773
  if (executionEngine === 'code_runtime') {
@@ -766,12 +828,13 @@ function registerPublishCommand(program) {
766
828
  ` Publish each dependency first, then re-run this publish.\n\n`);
767
829
  }
768
830
  if (notCallable.length > 0) {
769
- process.stderr.write(chalk_1.default.yellow(`\n⚠ Dependencies not marked as callable:\n`));
831
+ process.stderr.write(chalk_1.default.yellow(`\n⚠ Dependencies have callable: false:\n`));
770
832
  for (const dep of notCallable) {
771
833
  process.stderr.write(chalk_1.default.yellow(` - ${dep.ref}\n`));
772
834
  }
773
- process.stderr.write(`\n Agents must have callable: true in orchagent.json to be invoked\n` +
774
- ` by orchestrators. Update and republish each dependency.\n\n`);
835
+ process.stderr.write(`\n These agents have explicitly set callable: false, which blocks\n` +
836
+ ` agent-to-agent calls at runtime. Set callable: true (or remove\n` +
837
+ ` the field to use the default) and republish each dependency.\n\n`);
775
838
  }
776
839
  }
777
840
  // Handle dry-run for agents
@@ -882,18 +945,35 @@ function registerPublishCommand(program) {
882
945
  ` (Platform-injected vars like LLM API keys are already excluded.)\n\n`);
883
946
  }
884
947
  }
948
+ // Warn if always_on code binds to port 8080 (reserved for platform health server)
949
+ if (runMode === 'always_on' && executionEngine === 'code_runtime') {
950
+ const usesReservedPort = await scanReservedPort(cwd);
951
+ if (usesReservedPort) {
952
+ process.stderr.write(chalk_1.default.yellow(`\n⚠ Your code appears to bind to port 8080, which is reserved by the\n`) +
953
+ chalk_1.default.yellow(` platform health server for always-on services.\n\n`) +
954
+ ` Your service will crash with EADDRINUSE at runtime.\n` +
955
+ ` Use a different port (e.g. 3000) or read the ORCHAGENT_HEALTH_PORT\n` +
956
+ ` env var to detect the reserved port.\n\n`);
957
+ }
958
+ }
885
959
  // C-1: Block publish if tool/agent type has no required_secrets declared.
886
960
  // Prompt and skill types are exempt (prompt agents get LLM keys from platform,
887
961
  // skills don't run standalone).
962
+ // An explicit empty array (required_secrets: []) is a valid declaration
963
+ // meaning "this agent deliberately needs no secrets."
888
964
  if ((canonicalType === 'tool' || canonicalType === 'agent') &&
889
- (!manifest.required_secrets || manifest.required_secrets.length === 0) &&
965
+ manifest.required_secrets === undefined &&
890
966
  options.requiredSecrets !== false) {
891
967
  process.stderr.write(chalk_1.default.red(`\nError: ${canonicalType} agents must declare required_secrets in orchagent.json.\n\n`) +
892
968
  ` Add the env vars your code needs at runtime:\n` +
893
969
  ` ${chalk_1.default.cyan('"required_secrets": ["ANTHROPIC_API_KEY", "MY_TOKEN"]')}\n\n` +
970
+ ` If this agent genuinely needs no secrets, add an empty array:\n` +
971
+ ` ${chalk_1.default.cyan('"required_secrets": []')}\n\n` +
894
972
  ` These are matched by name against your workspace secrets vault.\n` +
895
973
  ` Use ${chalk_1.default.cyan('--no-required-secrets')} to skip this check.\n`);
896
- throw new errors_1.CliError('Missing required_secrets declaration', errors_1.ExitCodes.INVALID_INPUT);
974
+ const err = new errors_1.CliError('Missing required_secrets declaration', errors_1.ExitCodes.INVALID_INPUT);
975
+ err.displayed = true;
976
+ throw err;
897
977
  }
898
978
  // Create the agent (server auto-assigns version)
899
979
  let result;
@@ -1045,9 +1125,9 @@ function registerPublishCommand(program) {
1045
1125
  process.stdout.write(` ${chalk_1.default.cyan('Using workspace default environment')}\n`);
1046
1126
  }
1047
1127
  }
1048
- // Show service auto-update info
1128
+ // Store service update count for success message
1049
1129
  if (uploadResult.services_updated && uploadResult.services_updated > 0) {
1050
- process.stdout.write(` ${chalk_1.default.green(`Updated ${uploadResult.services_updated} service(s) to ${assignedVersion}`)}\n`);
1130
+ servicesUpdated = uploadResult.services_updated;
1051
1131
  }
1052
1132
  }
1053
1133
  finally {
@@ -1062,7 +1142,11 @@ function registerPublishCommand(program) {
1062
1142
  callable,
1063
1143
  hosted: shouldUploadBundle,
1064
1144
  });
1065
- process.stdout.write(`\nPublished agent: ${org.slug}/${manifest.name}@${assignedVersion}\n`);
1145
+ process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${manifest.name}@${assignedVersion} successfully!\n`);
1146
+ if (servicesUpdated > 0) {
1147
+ process.stdout.write(`${chalk_1.default.green('✔')} Updated ${servicesUpdated} running service(s) to ${assignedVersion} successfully!\n`);
1148
+ }
1149
+ process.stdout.write(`\n`);
1066
1150
  process.stdout.write(`Type: ${canonicalType}\n`);
1067
1151
  process.stdout.write(`Run mode: ${runMode}\n`);
1068
1152
  process.stdout.write(`Execution engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
@@ -1120,6 +1204,5 @@ function registerPublishCommand(program) {
1120
1204
  process.stdout.write(`\nNote: Hosted code execution is in beta. Contact support for full deployment.\n`);
1121
1205
  }
1122
1206
  process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
1123
- process.stdout.write(`\nSkill: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`);
1124
1207
  });
1125
1208
  }
@@ -29,7 +29,13 @@ function parsePullRef(value) {
29
29
  }
30
30
  function canonicalType(typeValue) {
31
31
  const normalized = (typeValue || 'agent').toLowerCase();
32
- return normalized === 'skill' ? 'skill' : 'agent';
32
+ if (['prompt', 'tool', 'agent', 'skill'].includes(normalized))
33
+ return normalized;
34
+ if (normalized === 'code')
35
+ return 'tool';
36
+ if (normalized === 'agentic')
37
+ return 'agent';
38
+ return 'agent';
33
39
  }
34
40
  function resolveEngine(data) {
35
41
  const ee = data.execution_engine;
@@ -200,7 +206,7 @@ function buildManifest(data) {
200
206
  const manifest = {
201
207
  name: data.name,
202
208
  description: data.description || '',
203
- type: canonicalType(data.type) === 'skill' ? 'skill' : 'agent',
209
+ type: canonicalType(data.type),
204
210
  };
205
211
  if (data.run_mode)
206
212
  manifest.run_mode = data.run_mode;
@@ -55,7 +55,6 @@ const output_1 = require("../lib/output");
55
55
  const spinner_1 = require("../lib/spinner");
56
56
  const llm_1 = require("../lib/llm");
57
57
  const analytics_1 = require("../lib/analytics");
58
- const pricing_1 = require("../lib/pricing");
59
58
  const package_json_1 = __importDefault(require("../../package.json"));
60
59
  const DEFAULT_VERSION = 'latest';
61
60
  const AGENTS_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent', 'agents');
@@ -462,16 +461,10 @@ async function downloadAgent(config, org, agent, version, workspaceId) {
462
461
  }
463
462
  }
464
463
  // Non-owner - block with helpful message
465
- if (errorCode === 'PAID_AGENT_SERVER_ONLY') {
466
- const price = payload.error.price_per_call_cents || 0;
467
- const priceStr = price ? `$${(price / 100).toFixed(2)}/call` : 'PAID';
468
- throw new errors_1.CliError(`This agent is paid (${priceStr}) and runs on server only.\n\n` +
469
- `Run without --local: orch run ${org}/${agent}@${version} --data '{...}'`);
470
- }
471
- else {
472
- throw new errors_1.CliError(`This agent is server-only and cannot be downloaded.\n\n` +
473
- `Run without --local: orch run ${org}/${agent}@${version} --data '{...}'`);
474
- }
464
+ // Use the gateway message which has the correct type label (agent/tool/skill/prompt)
465
+ const serverMsg = payload?.error?.message || 'This agent is server-only.';
466
+ throw new errors_1.CliError(`${serverMsg}\n\n` +
467
+ `Run without --local: orch run ${org}/${agent}@${version} --data '{...}'`);
475
468
  }
476
469
  }
477
470
  if (!(err instanceof api_1.ApiError) || err.status !== 404)
@@ -682,7 +675,7 @@ async function executePromptLocally(agentData, inputData, skillPrompts = [], con
682
675
  if (allProviders.length === 0) {
683
676
  const providers = providersToCheck.join(', ');
684
677
  throw new errors_1.CliError(`No LLM key found for: ${providers}\n` +
685
- `Set an environment variable (e.g., OPENAI_API_KEY), run 'orchagent keys add <provider>', or configure in web dashboard`);
678
+ `Set an environment variable (e.g., OPENAI_API_KEY), run 'orch secrets set <PROVIDER>_API_KEY <key>', or configure in web dashboard`);
686
679
  }
687
680
  if (modelOverride && !providerOverride && allProviders.length > 1) {
688
681
  process.stderr.write(`Warning: --model specified without --provider. The model '${modelOverride}' will be used for all ${allProviders.length} fallback providers, which may cause errors if the model is incompatible.\n` +
@@ -709,7 +702,7 @@ async function executePromptLocally(agentData, inputData, skillPrompts = [], con
709
702
  if (!detected) {
710
703
  const providers = providersToCheck.join(', ');
711
704
  throw new errors_1.CliError(`No LLM key found for: ${providers}\n` +
712
- `Set an environment variable (e.g., OPENAI_API_KEY), run 'orchagent keys add <provider>', or configure in web dashboard`);
705
+ `Set an environment variable (e.g., OPENAI_API_KEY), run 'orch secrets set <PROVIDER>_API_KEY <key>', or configure in web dashboard`);
713
706
  }
714
707
  const { provider, key, model: serverModel } = detected;
715
708
  const model = modelOverride || serverModel || agentData.default_models?.[provider] || (0, llm_1.getDefaultModel)(provider);
@@ -743,7 +736,7 @@ async function executeAgentLocally(agentDir, prompt, inputData, outputSchema, cu
743
736
  if (allProviders.length === 0) {
744
737
  const providers = providersToCheck.join(', ');
745
738
  throw new errors_1.CliError(`No LLM key found for: ${providers}\n` +
746
- `Set an environment variable (e.g., ANTHROPIC_API_KEY), run 'orchagent keys add <provider>', or configure in web dashboard`);
739
+ `Set an environment variable (e.g., ANTHROPIC_API_KEY), run 'orch secrets set <PROVIDER>_API_KEY <key>', or configure in web dashboard`);
747
740
  }
748
741
  const primary = allProviders[0];
749
742
  const model = modelOverride || primary.model || (0, llm_1.getDefaultModel)(primary.provider);
@@ -1703,58 +1696,6 @@ async function executeCloud(agentRef, file, options) {
1703
1696
  }
1704
1697
  }
1705
1698
  }
1706
- // Pre-call balance check for paid agents
1707
- let pricingInfo;
1708
- if ((0, pricing_1.isPaidAgent)(agentMeta)) {
1709
- let isOwner = false;
1710
- try {
1711
- const callerOrg = await (0, api_1.getOrg)(resolved, workspaceId);
1712
- const agentOrgId = agentMeta.org_id;
1713
- const agentOrgSlug = agentMeta.org_slug;
1714
- if (agentOrgId && callerOrg.id === agentOrgId) {
1715
- isOwner = true;
1716
- }
1717
- else if (agentOrgSlug && callerOrg.slug === agentOrgSlug) {
1718
- isOwner = true;
1719
- }
1720
- }
1721
- catch {
1722
- isOwner = false;
1723
- }
1724
- if (isOwner) {
1725
- if (!options.json)
1726
- process.stderr.write(`Cost: FREE (author)\n\n`);
1727
- }
1728
- else {
1729
- const price = agentMeta.price_per_call_cents;
1730
- pricingInfo = { price_cents: price ?? null };
1731
- if (!price || price <= 0) {
1732
- if (!options.json)
1733
- process.stderr.write(`Warning: Pricing data unavailable. The server will verify payment.\n\n`);
1734
- }
1735
- else {
1736
- try {
1737
- const balanceData = await (0, api_1.getCreditsBalance)(resolved);
1738
- const balance = balanceData.balance_cents;
1739
- if (balance < price) {
1740
- process.stderr.write(`Insufficient credits:\n` +
1741
- ` Balance: $${(balance / 100).toFixed(2)}\n` +
1742
- ` Required: $${(price / 100).toFixed(2)}\n\n` +
1743
- `Add credits:\n` +
1744
- ` orch billing add 5\n` +
1745
- ` orch billing balance # check current balance\n`);
1746
- process.exit(errors_1.ExitCodes.PERMISSION_DENIED);
1747
- }
1748
- if (!options.json)
1749
- process.stderr.write(`Cost: $${(price / 100).toFixed(2)}/call\n\n`);
1750
- }
1751
- catch (err) {
1752
- if (!options.json)
1753
- process.stderr.write(`Warning: Could not verify balance. The server will check payment.\n\n`);
1754
- }
1755
- }
1756
- }
1757
- }
1758
1699
  const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
1759
1700
  const headers = {
1760
1701
  Authorization: `Bearer ${resolved.apiKey}`,
@@ -1834,12 +1775,6 @@ async function executeCloud(agentRef, file, options) {
1834
1775
  ...(options.model && { model: options.model }),
1835
1776
  };
1836
1777
  }
1837
- else if (cloudEngine !== 'code_runtime') {
1838
- const searchedProviders = effectiveProvider ? [effectiveProvider] : supportedProviders;
1839
- const providerList = searchedProviders.join(', ');
1840
- process.stderr.write(`Warning: No LLM key found for provider(s): ${providerList}\n` +
1841
- `Set an env var (e.g., OPENAI_API_KEY), run 'orchagent keys add <provider>', use --key, or configure in web dashboard\n\n`);
1842
- }
1843
1778
  if (options.skills) {
1844
1779
  headers['X-OrchAgent-Skills'] = options.skills;
1845
1780
  }
@@ -2033,7 +1968,7 @@ async function executeCloud(agentRef, file, options) {
2033
1968
  });
2034
1969
  }
2035
1970
  catch (err) {
2036
- spinner?.fail(`Run failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
1971
+ spinner?.stop();
2037
1972
  throw err;
2038
1973
  }
2039
1974
  if (!response.ok) {
@@ -2048,20 +1983,8 @@ async function executeCloud(agentRef, file, options) {
2048
1983
  const errorCode = typeof payload === 'object' && payload
2049
1984
  ? payload.error?.code
2050
1985
  : undefined;
2051
- if (response.status === 402 || errorCode === 'INSUFFICIENT_CREDITS') {
2052
- spinner?.fail('Insufficient credits');
2053
- let errorMessage = 'Insufficient credits to run this agent.\n\n';
2054
- if (pricingInfo?.price_cents) {
2055
- errorMessage += `This agent costs $${(pricingInfo.price_cents / 100).toFixed(2)} per call.\n\n`;
2056
- }
2057
- errorMessage +=
2058
- 'Add credits:\n' +
2059
- ' orch billing add 5\n' +
2060
- ' orch billing balance # check current balance\n';
2061
- throw new errors_1.CliError(errorMessage, errors_1.ExitCodes.PERMISSION_DENIED);
2062
- }
2063
1986
  if (errorCode === 'CLI_VERSION_TOO_OLD') {
2064
- spinner?.fail('CLI version too old');
1987
+ spinner?.stop();
2065
1988
  const minVersion = typeof payload === 'object' && payload
2066
1989
  ? payload.error?.min_version
2067
1990
  : undefined;
@@ -2070,7 +1993,7 @@ async function executeCloud(agentRef, file, options) {
2070
1993
  'Update with: npm update -g @orchagent/cli');
2071
1994
  }
2072
1995
  if (errorCode === 'LLM_KEY_REQUIRED') {
2073
- spinner?.fail('LLM key required');
1996
+ spinner?.stop();
2074
1997
  throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
2075
1998
  'Use --key <key> --provider <provider> or set OPENAI_API_KEY/ANTHROPIC_API_KEY env var.');
2076
1999
  }
@@ -2078,7 +2001,7 @@ async function executeCloud(agentRef, file, options) {
2078
2001
  const rateLimitMsg = typeof payload === 'object' && payload
2079
2002
  ? payload.error?.message || 'Rate limit exceeded'
2080
2003
  : 'Rate limit exceeded';
2081
- spinner?.fail('Rate limited by LLM provider');
2004
+ spinner?.stop();
2082
2005
  throw new errors_1.CliError(rateLimitMsg + '\n\n' +
2083
2006
  'This is the LLM provider\'s rate limit on your API key, not an OrchAgent limit.\n' +
2084
2007
  'To switch providers: orch run <agent> --provider <gemini|anthropic|openai>', errors_1.ExitCodes.RATE_LIMITED);
@@ -2094,18 +2017,29 @@ async function executeCloud(agentRef, file, options) {
2094
2017
  : undefined;
2095
2018
  const refSuffix = requestId ? `\n\nref: ${requestId}` : '';
2096
2019
  if (errorCode === 'SANDBOX_ERROR') {
2097
- spinner?.fail('Agent execution failed');
2020
+ spinner?.stop();
2098
2021
  const hint = typeof payload === 'object' && payload
2099
2022
  ? payload.error?.hint
2100
2023
  : undefined;
2024
+ // Detect platform errors that surface as SANDBOX_ERROR (BUG-11)
2025
+ const lowerMessage = (message || '').toLowerCase();
2026
+ const isPlatformError = /\b403\b/.test(message || '') ||
2027
+ /\b401\b/.test(message || '') ||
2028
+ lowerMessage.includes('proxy token') ||
2029
+ lowerMessage.includes('orchagent_service_key') ||
2030
+ lowerMessage.includes('orchagent_billing_org');
2031
+ const attribution = isPlatformError
2032
+ ? `This may be a platform configuration issue, not an error in the agent's code.\n` +
2033
+ `If this persists, contact support with the ref below.`
2034
+ : `This is an error in the agent's code, not the platform.\n` +
2035
+ `Check the agent code and requirements, then republish.`;
2101
2036
  throw new errors_1.CliError(`${message}\n\n` +
2102
- `This is an error in the agent's code, not the platform.\n` +
2103
- `Check the agent code and requirements, then republish.` +
2037
+ attribution +
2104
2038
  (hint ? `\n\nHint: ${hint}` : '') +
2105
2039
  refSuffix);
2106
2040
  }
2107
2041
  if (errorCode === 'SANDBOX_TIMEOUT') {
2108
- spinner?.fail('Agent timed out');
2042
+ spinner?.stop();
2109
2043
  throw new errors_1.CliError(`${message}\n\n` +
2110
2044
  `The agent did not complete in time. Try:\n` +
2111
2045
  ` - Simplifying the input\n` +
@@ -2114,7 +2048,7 @@ async function executeCloud(agentRef, file, options) {
2114
2048
  refSuffix);
2115
2049
  }
2116
2050
  if (errorCode === 'MISSING_SECRETS') {
2117
- spinner?.fail('Missing workspace secrets');
2051
+ spinner?.stop();
2118
2052
  // Extract secret names from gateway message:
2119
2053
  // "Agent requires secret(s) not found in workspace: NAME1, NAME2. Add them in Settings > Secrets."
2120
2054
  const secretNames = [];
@@ -2145,13 +2079,13 @@ async function executeCloud(agentRef, file, options) {
2145
2079
  throw new errors_1.CliError(hint + refSuffix);
2146
2080
  }
2147
2081
  if (response.status >= 500) {
2148
- spinner?.fail(`Server error (${response.status})`);
2082
+ spinner?.stop();
2149
2083
  throw new errors_1.CliError(`${message}\n\n` +
2150
2084
  `This is a platform error — try again in a moment.\n` +
2151
2085
  `If it persists, contact support.` +
2152
2086
  refSuffix);
2153
2087
  }
2154
- spinner?.fail(`Run failed: ${message}`);
2088
+ spinner?.stop();
2155
2089
  throw new errors_1.CliError(message + refSuffix);
2156
2090
  }
2157
2091
  // Handle SSE streaming response
@@ -2235,9 +2169,6 @@ async function executeCloud(agentRef, file, options) {
2235
2169
  return;
2236
2170
  }
2237
2171
  spinner?.succeed(`Ran ${org}/${parsed.agent}@${parsed.version}`);
2238
- if (!options.json && (0, pricing_1.isPaidAgent)(agentMeta) && pricingInfo?.price_cents && pricingInfo.price_cents > 0) {
2239
- process.stderr.write(`\nCost: $${(pricingInfo.price_cents / 100).toFixed(2)} USD\n`);
2240
- }
2241
2172
  const inputType = hasInjection
2242
2173
  ? 'file_injection'
2243
2174
  : unkeyedFileArgs.length > 0
@@ -2606,13 +2537,6 @@ Examples:
2606
2537
  orch run joe/summarizer --local --data '{"text": "Hello world"}'
2607
2538
  orch run orchagent/leak-finder --local --download-only
2608
2539
 
2609
- Paid Agents:
2610
- Paid agents charge per call and deduct from your prepaid credits.
2611
- Check your balance: orch billing balance
2612
- Add credits: orch billing add 5
2613
-
2614
- Same-author calls are FREE - you won't be charged for calling your own agents.
2615
-
2616
2540
  File handling (cloud):
2617
2541
  For prompt agents, file content is read and sent as JSON mapped to the agent's
2618
2542
  input schema. Use --file-field to specify the field name (auto-detected by default).
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.registerScheduleCommand = registerScheduleCommand;
7
7
  const cli_table3_1 = __importDefault(require("cli-table3"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
+ const promises_1 = __importDefault(require("readline/promises"));
9
10
  const config_1 = require("../lib/config");
10
11
  const api_1 = require("../lib/api");
11
12
  const errors_1 = require("../lib/errors");
@@ -64,7 +65,8 @@ async function resolveScheduleId(config, partialId, workspaceId) {
64
65
  function registerScheduleCommand(program) {
65
66
  const schedule = program
66
67
  .command('schedule')
67
- .description('Manage scheduled agent runs (cron and webhooks)');
68
+ .description('Manage scheduled agent runs (cron and webhooks)')
69
+ .action(() => { schedule.help(); });
68
70
  // orch schedule list
69
71
  schedule
70
72
  .command('list')
@@ -282,6 +284,7 @@ function registerScheduleCommand(program) {
282
284
  schedule
283
285
  .command('delete <schedule-id>')
284
286
  .description('Delete a schedule')
287
+ .option('-y, --yes', 'Skip confirmation prompt')
285
288
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
286
289
  .action(async (scheduleId, options) => {
287
290
  const config = await (0, config_1.getResolvedConfig)();
@@ -289,6 +292,18 @@ function registerScheduleCommand(program) {
289
292
  throw new errors_1.CliError('Missing API key. Run `orch login` first.');
290
293
  }
291
294
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
295
+ if (!options.yes) {
296
+ const rl = promises_1.default.createInterface({
297
+ input: process.stdin,
298
+ output: process.stdout,
299
+ });
300
+ const answer = await rl.question(`Delete schedule ${scheduleId}? (y/N): `);
301
+ rl.close();
302
+ if (answer.trim().toLowerCase() !== 'y' && answer.trim().toLowerCase() !== 'yes') {
303
+ process.stdout.write('Cancelled.\n');
304
+ return;
305
+ }
306
+ }
292
307
  await (0, api_1.request)(config, 'DELETE', `/workspaces/${workspaceId}/schedules/${scheduleId}`);
293
308
  process.stdout.write(chalk_1.default.green('\u2713') + ` Schedule ${scheduleId} deleted\n`);
294
309
  });
@@ -338,6 +353,7 @@ function registerScheduleCommand(program) {
338
353
  .command('info <schedule-id>')
339
354
  .description('Show detailed schedule information with recent runs and events')
340
355
  .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
356
+ .option('--reveal', 'Show full webhook URL (for webhook schedules)')
341
357
  .option('--json', 'Output as JSON')
342
358
  .action(async (partialScheduleId, options) => {
343
359
  const config = await (0, config_1.getResolvedConfig)();
@@ -346,8 +362,9 @@ function registerScheduleCommand(program) {
346
362
  }
347
363
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
348
364
  const scheduleId = await resolveScheduleId(config, partialScheduleId, workspaceId);
365
+ const revealParam = options.reveal ? '?reveal_webhook=true' : '';
349
366
  const [scheduleRes, runsRes, eventsRes] = await Promise.all([
350
- (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}`),
367
+ (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}${revealParam}`),
351
368
  (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/runs?limit=5`),
352
369
  (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/schedules/${scheduleId}/events?limit=5`),
353
370
  ]);
@@ -364,6 +381,12 @@ function registerScheduleCommand(program) {
364
381
  process.stdout.write(` Cron: ${s.cron_expression}\n`);
365
382
  process.stdout.write(` Timezone: ${s.timezone}\n`);
366
383
  }
384
+ if (s.webhook_url) {
385
+ process.stdout.write(` Webhook: ${s.webhook_url}\n`);
386
+ }
387
+ else if (s.schedule_type === 'webhook' && !options.reveal) {
388
+ process.stdout.write(` Webhook: ${chalk_1.default.gray('(redacted — use --reveal to show)')}\n`);
389
+ }
367
390
  process.stdout.write(` Enabled: ${s.enabled ? chalk_1.default.green('yes') : chalk_1.default.red('no')}\n`);
368
391
  process.stdout.write(` Auto-update: ${s.auto_update === false ? chalk_1.default.yellow('pinned') : chalk_1.default.green('yes')}\n`);
369
392
  if (s.auto_disabled_at) {
@@ -374,6 +397,10 @@ function registerScheduleCommand(program) {
374
397
  if (s.next_run_at) {
375
398
  process.stdout.write(` Next Run: ${formatDate(s.next_run_at)}\n`);
376
399
  }
400
+ if (s.input_data && Object.keys(s.input_data).length > 0) {
401
+ const inputStr = JSON.stringify(s.input_data);
402
+ process.stdout.write(` Input: ${inputStr.length > 100 ? inputStr.slice(0, 100) + '...' : inputStr}\n`);
403
+ }
377
404
  if (s.alert_webhook_url) {
378
405
  process.stdout.write(` Alert URL: ${s.alert_webhook_url.slice(0, 50)}...\n`);
379
406
  }
@@ -54,7 +54,8 @@ async function findSecretByName(config, workspaceId, name) {
54
54
  function registerSecretsCommand(program) {
55
55
  const secrets = program
56
56
  .command('secrets')
57
- .description('Manage workspace secrets (injected as env vars into agent sandboxes)');
57
+ .description('Manage workspace secrets (injected as env vars into agent sandboxes)')
58
+ .action(() => { secrets.help(); });
58
59
  // orch secrets list
59
60
  secrets
60
61
  .command('list')
@@ -222,7 +222,7 @@ Examples:
222
222
  });
223
223
  }
224
224
  catch (err) {
225
- spinner.fail(`Scan failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
225
+ spinner.stop();
226
226
  throw err;
227
227
  }
228
228
  if (!response.ok) {
@@ -239,7 +239,7 @@ Examples:
239
239
  payload.message ||
240
240
  response.statusText
241
241
  : response.statusText;
242
- spinner.fail(`Scan failed: ${message}`);
242
+ spinner.stop();
243
243
  throw new errors_1.CliError(message);
244
244
  }
245
245
  spinner.succeed(`Scan completed for ${agentId}`);