@orchagent/cli 0.3.82 → 0.3.84

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.
@@ -4,119 +4,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.registerBillingCommand = registerBillingCommand;
7
- const cli_table3_1 = __importDefault(require("cli-table3"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
7
  const open_1 = __importDefault(require("open"));
10
8
  const config_1 = require("../lib/config");
11
9
  const api_1 = require("../lib/api");
12
- const errors_1 = require("../lib/errors");
13
- const output_1 = require("../lib/output");
14
10
  function registerBillingCommand(program) {
15
- const billing = program
11
+ program
16
12
  .command('billing')
17
- .description('Manage platform credits and usage');
18
- // orch billing balance
19
- billing
20
- .command('balance')
21
- .description('Show your credit balance and recent transactions')
22
- .option('--json', 'Output as JSON')
23
- .action(async (options) => {
13
+ .description('Open billing portal in your browser')
14
+ .action(async () => {
24
15
  const resolved = await (0, config_1.getResolvedConfig)();
25
- const data = await (0, api_1.getCreditsBalance)(resolved);
26
- if (options.json) {
27
- (0, output_1.printJson)(data);
28
- return;
16
+ if (!resolved.apiKey) {
17
+ process.stderr.write('Not logged in. Run: orch login\n');
18
+ process.exit(1);
19
+ }
20
+ // Get the billing portal URL from the gateway
21
+ try {
22
+ const data = await (0, api_1.request)(resolved, 'GET', '/billing/portal');
23
+ process.stdout.write('Opening billing portal...\n');
24
+ await (0, open_1.default)(data.url);
25
+ process.stdout.write(`If browser doesn't open, visit:\n${data.url}\n`);
26
+ }
27
+ catch {
28
+ // Fallback to direct web URL
29
+ const webUrl = resolved.apiUrl.replace('api.', '').replace('/v1', '');
30
+ const url = `${webUrl}/settings/billing`;
31
+ process.stdout.write('Opening billing page...\n');
32
+ await (0, open_1.default)(url);
33
+ process.stdout.write(`If browser doesn't open, visit:\n${url}\n`);
29
34
  }
30
- // Show balance
31
- const balance = data.balance_cents / 100;
32
- process.stdout.write(chalk_1.default.bold(`\nBalance: ${chalk_1.default.green(`$${balance.toFixed(2)} USD`)}\n\n`));
33
- // Show recent transactions
34
- if (data.recent_transactions && data.recent_transactions.length > 0) {
35
- process.stdout.write(chalk_1.default.bold('Recent Transactions:\n'));
36
- const table = new cli_table3_1.default({
37
- head: [
38
- chalk_1.default.bold('Date'),
39
- chalk_1.default.bold('Type'),
40
- chalk_1.default.bold('Amount'),
41
- chalk_1.default.bold('Balance'),
42
- ],
43
- });
44
- data.recent_transactions.forEach((tx) => {
45
- const date = new Date(tx.created_at).toLocaleDateString();
46
- const amount = (tx.amount_cents / 100).toFixed(2);
47
- const balance = (tx.balance_after_cents / 100).toFixed(2);
48
- const amountColor = tx.amount_cents >= 0 ? chalk_1.default.green : chalk_1.default.red;
49
- table.push([date, tx.transaction_type, amountColor(`$${amount}`), `$${balance}`]);
50
- });
51
- process.stdout.write(`${table.toString()}\n\n`);
52
- }
53
- else {
54
- process.stdout.write('No recent transactions\n\n');
55
- }
56
- process.stdout.write(chalk_1.default.gray('Add credits: orch billing add 5\n'));
57
- });
58
- // orch billing add <amount>
59
- billing
60
- .command('add [amount]')
61
- .description('Add credits via Stripe checkout (minimum $5.00 USD)')
62
- .action(async (amount) => {
63
- const resolved = await (0, config_1.getResolvedConfig)();
64
- // Parse and validate amount
65
- let amountNum;
66
- if (!amount) {
67
- amountNum = 5.00; // Default to $5
68
- }
69
- else {
70
- amountNum = parseFloat(amount);
71
- if (isNaN(amountNum) || amountNum < 5.00) {
72
- throw new errors_1.CliError('Amount must be at least $5.00 USD', errors_1.ExitCodes.INVALID_INPUT);
73
- }
74
- }
75
- const amountCents = Math.round(amountNum * 100);
76
- // Create checkout session
77
- const checkout = await (0, api_1.createCreditCheckout)(resolved, amountCents);
78
- // Open in browser
79
- process.stdout.write(`\nOpening checkout page...\n`);
80
- process.stdout.write(`Amount: $${amountNum.toFixed(2)} USD\n\n`);
81
- await (0, open_1.default)(checkout.checkout_url);
82
- process.stdout.write(chalk_1.default.gray(`If browser doesn't open, visit:\n${checkout.checkout_url}\n`));
83
- });
84
- // orch billing history (alias)
85
- billing
86
- .command('history')
87
- .description('Show transaction history (alias for balance)')
88
- .option('--json', 'Output as JSON')
89
- .action(async (options) => {
90
- // Just call balance command
91
- const resolved = await (0, config_1.getResolvedConfig)();
92
- const data = await (0, api_1.getCreditsBalance)(resolved);
93
- if (options.json) {
94
- (0, output_1.printJson)(data);
95
- return;
96
- }
97
- // Simplified view - just show transactions
98
- if (data.recent_transactions && data.recent_transactions.length > 0) {
99
- const table = new cli_table3_1.default({
100
- head: [
101
- chalk_1.default.bold('Date'),
102
- chalk_1.default.bold('Type'),
103
- chalk_1.default.bold('Amount'),
104
- chalk_1.default.bold('Balance'),
105
- ],
106
- });
107
- data.recent_transactions.forEach((tx) => {
108
- const date = new Date(tx.created_at).toLocaleDateString();
109
- const amount = (tx.amount_cents / 100).toFixed(2);
110
- const balance = (tx.balance_after_cents / 100).toFixed(2);
111
- const amountColor = tx.amount_cents >= 0 ? chalk_1.default.green : chalk_1.default.red;
112
- table.push([date, tx.transaction_type, amountColor(`$${amount}`), `$${balance}`]);
113
- });
114
- process.stdout.write(`\n${table.toString()}\n\n`);
115
- }
116
- else {
117
- process.stdout.write('\nNo transactions found\n\n');
118
- }
119
- const balance = data.balance_cents / 100;
120
- process.stdout.write(chalk_1.default.gray(`Current balance: $${balance.toFixed(2)} USD\n`));
121
35
  });
122
36
  }
@@ -147,7 +147,7 @@ async function getGitHubStatus(config, json) {
147
147
  }
148
148
  process.stdout.write(`GitHub Status:\n\n`);
149
149
  process.stdout.write(` Connected: ${chalk_1.default.green('Yes')}\n`);
150
- process.stdout.write(` Account: ${chalk_1.default.bold(connection.github_account_login)}\n`);
150
+ process.stdout.write(` Account: ${chalk_1.default.bold(connection.github_account_login || 'Unknown')}\n`);
151
151
  if (connection.github_account_type) {
152
152
  process.stdout.write(` Type: ${connection.github_account_type === 'User' ? 'User' : 'Organization'}\n`);
153
153
  }
@@ -8,7 +8,6 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  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
- const pricing_1 = require("../lib/pricing");
12
11
  function formatSchema(schema, indent = ' ') {
13
12
  const lines = [];
14
13
  const props = schema.properties || {};
@@ -66,8 +65,6 @@ async function getAgentInfo(config, org, agent, version, workspaceId) {
66
65
  source_url: meta.source_url,
67
66
  run_command: meta.run_command,
68
67
  url: meta.url,
69
- pricing_mode: publicMeta.pricing_mode,
70
- price_per_call_cents: publicMeta.price_per_call_cents,
71
68
  };
72
69
  }
73
70
  catch (err) {
@@ -111,14 +108,12 @@ async function getAgentInfo(config, org, agent, version, workspaceId) {
111
108
  source_url: targetAgent.source_url,
112
109
  run_command: targetAgent.run_command,
113
110
  url: targetAgent.url,
114
- pricing_mode: targetAgent.pricing_mode,
115
- price_per_call_cents: targetAgent.price_per_call_cents,
116
111
  };
117
112
  }
118
113
  function registerInfoCommand(program) {
119
114
  program
120
115
  .command('info <agent>')
121
- .description('Show agent details including pricing, inputs, and outputs')
116
+ .description('Show agent details including inputs and outputs')
122
117
  .option('--json', 'Output as JSON')
123
118
  .action(async (agentArg, options) => {
124
119
  const config = await (0, config_1.getResolvedConfig)();
@@ -148,15 +143,6 @@ function registerInfoCommand(program) {
148
143
  process.stdout.write(`Callable: ${chalk_1.default.green('yes')} — other agents can invoke this via the orchagent SDK\n`);
149
144
  }
150
145
  process.stdout.write(`Providers: ${agentData.supported_providers.join(', ')}\n`);
151
- // Display pricing information
152
- const priceStr = (0, pricing_1.formatPrice)(agentData);
153
- const color = (0, pricing_1.isPaidAgent)(agentData) ? chalk_1.default.yellow : chalk_1.default.green;
154
- process.stdout.write(`Price: ${color(priceStr)}\n`);
155
- // If paid, show server-only message for non-owners
156
- if ((0, pricing_1.isPaidAgent)(agentData)) {
157
- process.stdout.write(chalk_1.default.gray('Note: Paid agents run on server only (use orch run)\n'));
158
- process.stdout.write(chalk_1.default.gray(' Owners can still download for development/testing\n'));
159
- }
160
146
  if (agentData.type === 'tool') {
161
147
  // Don't show internal routing URLs - they confuse users
162
148
  if (agentData.url && !agentData.url.includes('.internal')) {
@@ -15,7 +15,6 @@ const adapters_1 = require("../adapters");
15
15
  const skill_resolve_1 = require("../lib/skill-resolve");
16
16
  const installed_1 = require("../lib/installed");
17
17
  const agents_md_utils_1 = require("../lib/agents-md-utils");
18
- const pricing_1 = require("../lib/pricing");
19
18
  const DEFAULT_VERSION = 'latest';
20
19
  function parseAgentRef(value) {
21
20
  const [ref, versionPart] = value.split('@');
@@ -44,48 +43,6 @@ async function downloadAgentWithFallback(config, org, name, version, workspaceId
44
43
  throw err;
45
44
  }
46
45
  }
47
- // Check if paid agent
48
- if (publicMeta && (0, pricing_1.isPaidAgent)(publicMeta)) {
49
- // Paid agent - check if owner
50
- if (config.apiKey) {
51
- const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
52
- const isOwner = (publicMeta.org_id && callerOrg.id === publicMeta.org_id) ||
53
- (publicMeta.org_slug && callerOrg.slug === publicMeta.org_slug);
54
- if (isOwner) {
55
- // Owner - fetch from authenticated endpoint with full prompt
56
- const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
57
- const matching = myAgents.filter(a => a.name === name);
58
- if (matching.length > 0) {
59
- let targetAgent;
60
- if (version === 'latest') {
61
- targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
62
- }
63
- else {
64
- const found = matching.find(a => a.version === version);
65
- if (!found) {
66
- throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
67
- }
68
- targetAgent = found;
69
- }
70
- // Fetch full agent data with prompt from authenticated endpoint
71
- const agentData = await (0, api_1.request)(config, 'GET', `/agents/${targetAgent.id}`);
72
- return { ...agentData, org_slug: org };
73
- }
74
- }
75
- else {
76
- // Non-owner - block with helpful message
77
- const price = (0, pricing_1.formatPrice)(publicMeta);
78
- throw new errors_1.CliError(`This agent is paid (${price}) and runs on server only.\n\n` +
79
- `Use: orch run ${org}/${name}@${version} --data '{...}'`);
80
- }
81
- }
82
- else {
83
- // Not authenticated - block
84
- const price = (0, pricing_1.formatPrice)(publicMeta);
85
- throw new errors_1.CliError(`This agent is paid (${price}) and runs on server only.\n\n` +
86
- `Use: orch run ${org}/${name}@${version} --data '{...}'`);
87
- }
88
- }
89
46
  // Check if download is disabled (server-only agent)
90
47
  if (publicMeta && publicMeta.allow_local_download === false) {
91
48
  // Check if owner (can bypass)
@@ -114,7 +71,8 @@ async function downloadAgentWithFallback(config, org, name, version, workspaceId
114
71
  }
115
72
  }
116
73
  }
117
- throw new errors_1.CliError(`This agent is server-only and cannot be downloaded.\n\n` +
74
+ const typeLabel = publicMeta.type || 'agent';
75
+ throw new errors_1.CliError(`This ${typeLabel} is server-only and cannot be downloaded.\n\n` +
118
76
  `Use: orch run ${org}/${name}@${version} --data '{...}'`);
119
77
  }
120
78
  // Free agent - proceed normally with public data
@@ -159,10 +117,6 @@ function registerInstallCommand(program) {
159
117
  .option('--global', 'Install to home directory (alias for --scope user)')
160
118
  .option('--dry-run', 'Show what would be installed without making changes')
161
119
  .option('--json', 'Output result as JSON (for automation/tooling)')
162
- .addHelpText('after', `
163
- Note: Paid agents cannot be installed locally - they run on server only.
164
- Use 'orchagent run' to execute paid agents.
165
- `)
166
120
  .action(async (agentArg, options) => {
167
121
  const jsonMode = options.json === true;
168
122
  const log = (msg) => { if (!jsonMode)
@@ -62,6 +62,8 @@ async function keyBasedLogin(apiKey) {
62
62
  api_url: resolved.apiUrl,
63
63
  default_org: existing.default_org ?? org.slug,
64
64
  };
65
+ // Clear workspace from previous account — workspaces are account-specific
66
+ delete nextConfig.workspace;
65
67
  await (0, config_1.saveConfig)(nextConfig);
66
68
  await (0, analytics_1.track)('cli_login', { method: 'key' });
67
69
  process.stdout.write(`✓ Logged in to ${org.slug}\n`);
@@ -81,6 +83,8 @@ async function browserBasedLogin(port) {
81
83
  api_url: resolved.apiUrl,
82
84
  default_org: existing.default_org ?? result.orgSlug,
83
85
  };
86
+ // Clear workspace from previous account — workspaces are account-specific
87
+ delete nextConfig.workspace;
84
88
  await (0, config_1.saveConfig)(nextConfig);
85
89
  await (0, analytics_1.track)('cli_login', { method: 'browser' });
86
90
  process.stdout.write(`\n✓ Logged in to ${result.orgSlug}\n`);
@@ -495,7 +495,7 @@ function registerPublishCommand(program) {
495
495
  const skillVersion = skillResult.agent?.version || 'v1';
496
496
  const skillAgentId = skillResult.agent?.id;
497
497
  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`);
498
+ process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${skillData.frontmatter.name}@${skillVersion} successfully!\n\n`);
499
499
  if (hasMultipleFiles) {
500
500
  process.stdout.write(`Files: ${skillFiles.length} files included\n`);
501
501
  }
@@ -706,6 +706,7 @@ function registerPublishCommand(program) {
706
706
  }
707
707
  let agentUrl = options.url;
708
708
  let shouldUploadBundle = false;
709
+ let servicesUpdated = 0;
709
710
  let runtimeConfig;
710
711
  let bundleEntrypoint = manifest.entrypoint;
711
712
  if (executionEngine === 'code_runtime') {
@@ -1045,9 +1046,9 @@ function registerPublishCommand(program) {
1045
1046
  process.stdout.write(` ${chalk_1.default.cyan('Using workspace default environment')}\n`);
1046
1047
  }
1047
1048
  }
1048
- // Show service auto-update info
1049
+ // Store service update count for success message
1049
1050
  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`);
1051
+ servicesUpdated = uploadResult.services_updated;
1051
1052
  }
1052
1053
  }
1053
1054
  finally {
@@ -1062,7 +1063,11 @@ function registerPublishCommand(program) {
1062
1063
  callable,
1063
1064
  hosted: shouldUploadBundle,
1064
1065
  });
1065
- process.stdout.write(`\nPublished agent: ${org.slug}/${manifest.name}@${assignedVersion}\n`);
1066
+ process.stdout.write(`\n${chalk_1.default.green('✔')} Published ${org.slug}/${manifest.name}@${assignedVersion} successfully!\n`);
1067
+ if (servicesUpdated > 0) {
1068
+ process.stdout.write(`${chalk_1.default.green('✔')} Updated ${servicesUpdated} running service(s) to ${assignedVersion} successfully!\n`);
1069
+ }
1070
+ process.stdout.write(`\n`);
1066
1071
  process.stdout.write(`Type: ${canonicalType}\n`);
1067
1072
  process.stdout.write(`Run mode: ${runMode}\n`);
1068
1073
  process.stdout.write(`Execution engine: ${executionEngine}${shouldUploadBundle ? ' (hosted)' : ''}\n`);
@@ -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)
@@ -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}`,
@@ -2033,7 +1974,7 @@ async function executeCloud(agentRef, file, options) {
2033
1974
  });
2034
1975
  }
2035
1976
  catch (err) {
2036
- spinner?.fail(`Run failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
1977
+ spinner?.stop();
2037
1978
  throw err;
2038
1979
  }
2039
1980
  if (!response.ok) {
@@ -2048,20 +1989,8 @@ async function executeCloud(agentRef, file, options) {
2048
1989
  const errorCode = typeof payload === 'object' && payload
2049
1990
  ? payload.error?.code
2050
1991
  : 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
1992
  if (errorCode === 'CLI_VERSION_TOO_OLD') {
2064
- spinner?.fail('CLI version too old');
1993
+ spinner?.stop();
2065
1994
  const minVersion = typeof payload === 'object' && payload
2066
1995
  ? payload.error?.min_version
2067
1996
  : undefined;
@@ -2070,7 +1999,7 @@ async function executeCloud(agentRef, file, options) {
2070
1999
  'Update with: npm update -g @orchagent/cli');
2071
2000
  }
2072
2001
  if (errorCode === 'LLM_KEY_REQUIRED') {
2073
- spinner?.fail('LLM key required');
2002
+ spinner?.stop();
2074
2003
  throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
2075
2004
  'Use --key <key> --provider <provider> or set OPENAI_API_KEY/ANTHROPIC_API_KEY env var.');
2076
2005
  }
@@ -2078,7 +2007,7 @@ async function executeCloud(agentRef, file, options) {
2078
2007
  const rateLimitMsg = typeof payload === 'object' && payload
2079
2008
  ? payload.error?.message || 'Rate limit exceeded'
2080
2009
  : 'Rate limit exceeded';
2081
- spinner?.fail('Rate limited by LLM provider');
2010
+ spinner?.stop();
2082
2011
  throw new errors_1.CliError(rateLimitMsg + '\n\n' +
2083
2012
  'This is the LLM provider\'s rate limit on your API key, not an OrchAgent limit.\n' +
2084
2013
  'To switch providers: orch run <agent> --provider <gemini|anthropic|openai>', errors_1.ExitCodes.RATE_LIMITED);
@@ -2094,7 +2023,7 @@ async function executeCloud(agentRef, file, options) {
2094
2023
  : undefined;
2095
2024
  const refSuffix = requestId ? `\n\nref: ${requestId}` : '';
2096
2025
  if (errorCode === 'SANDBOX_ERROR') {
2097
- spinner?.fail('Agent execution failed');
2026
+ spinner?.stop();
2098
2027
  const hint = typeof payload === 'object' && payload
2099
2028
  ? payload.error?.hint
2100
2029
  : undefined;
@@ -2105,7 +2034,7 @@ async function executeCloud(agentRef, file, options) {
2105
2034
  refSuffix);
2106
2035
  }
2107
2036
  if (errorCode === 'SANDBOX_TIMEOUT') {
2108
- spinner?.fail('Agent timed out');
2037
+ spinner?.stop();
2109
2038
  throw new errors_1.CliError(`${message}\n\n` +
2110
2039
  `The agent did not complete in time. Try:\n` +
2111
2040
  ` - Simplifying the input\n` +
@@ -2114,7 +2043,7 @@ async function executeCloud(agentRef, file, options) {
2114
2043
  refSuffix);
2115
2044
  }
2116
2045
  if (errorCode === 'MISSING_SECRETS') {
2117
- spinner?.fail('Missing workspace secrets');
2046
+ spinner?.stop();
2118
2047
  // Extract secret names from gateway message:
2119
2048
  // "Agent requires secret(s) not found in workspace: NAME1, NAME2. Add them in Settings > Secrets."
2120
2049
  const secretNames = [];
@@ -2145,13 +2074,13 @@ async function executeCloud(agentRef, file, options) {
2145
2074
  throw new errors_1.CliError(hint + refSuffix);
2146
2075
  }
2147
2076
  if (response.status >= 500) {
2148
- spinner?.fail(`Server error (${response.status})`);
2077
+ spinner?.stop();
2149
2078
  throw new errors_1.CliError(`${message}\n\n` +
2150
2079
  `This is a platform error — try again in a moment.\n` +
2151
2080
  `If it persists, contact support.` +
2152
2081
  refSuffix);
2153
2082
  }
2154
- spinner?.fail(`Run failed: ${message}`);
2083
+ spinner?.stop();
2155
2084
  throw new errors_1.CliError(message + refSuffix);
2156
2085
  }
2157
2086
  // Handle SSE streaming response
@@ -2235,9 +2164,6 @@ async function executeCloud(agentRef, file, options) {
2235
2164
  return;
2236
2165
  }
2237
2166
  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
2167
  const inputType = hasInjection
2242
2168
  ? 'file_injection'
2243
2169
  : unkeyedFileArgs.length > 0
@@ -2606,13 +2532,6 @@ Examples:
2606
2532
  orch run joe/summarizer --local --data '{"text": "Hello world"}'
2607
2533
  orch run orchagent/leak-finder --local --download-only
2608
2534
 
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
2535
  File handling (cloud):
2617
2536
  For prompt agents, file content is read and sent as JSON mapped to the agent's
2618
2537
  input schema. Use --file-field to specify the field name (auto-detected by default).
@@ -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}`);
@@ -135,7 +135,7 @@ function registerServiceCommand(program) {
135
135
  match = agentsList.find(a => a.name === agentName && a.version === agentVersion);
136
136
  }
137
137
  if (!match) {
138
- spinner.fail('Agent not found');
138
+ spinner.stop();
139
139
  throw new errors_1.CliError(`Agent '${agentName}' (version ${agentVersion}) not found in workspace`);
140
140
  }
141
141
  agentId = match.id;
@@ -144,7 +144,7 @@ function registerServiceCommand(program) {
144
144
  catch (e) {
145
145
  if (e instanceof errors_1.CliError)
146
146
  throw e;
147
- spinner.fail('Failed to resolve agent');
147
+ spinner.stop();
148
148
  throw e;
149
149
  }
150
150
  // C-2: Show deprecation notice when --secret is used
@@ -311,7 +311,7 @@ function registerServiceCommand(program) {
311
311
  process.stdout.write(`${chalk_1.default.green('\u2713')} Service '${result.service.service_name}' restarted (restarts: ${result.service.restart_count})\n`);
312
312
  }
313
313
  catch (e) {
314
- spinner.fail('Restart failed');
314
+ spinner.stop();
315
315
  throw e;
316
316
  }
317
317
  });
@@ -414,7 +414,7 @@ function registerServiceCommand(program) {
414
414
  process.stdout.write(`${chalk_1.default.green('\u2713')} Service '${result.service.service_name}' deleted\n`);
415
415
  }
416
416
  catch (e) {
417
- spinner.fail('Delete failed');
417
+ spinner.stop();
418
418
  throw e;
419
419
  }
420
420
  });
@@ -468,7 +468,7 @@ function registerServiceCommand(program) {
468
468
  }
469
469
  }
470
470
  catch (e) {
471
- spinner.fail('Failed to update environment');
471
+ spinner.stop();
472
472
  throw e;
473
473
  }
474
474
  });
@@ -517,7 +517,7 @@ function registerServiceCommand(program) {
517
517
  }
518
518
  }
519
519
  catch (e) {
520
- spinner.fail('Failed to update environment');
520
+ spinner.stop();
521
521
  throw e;
522
522
  }
523
523
  });
@@ -594,7 +594,7 @@ function registerServiceCommand(program) {
594
594
  }
595
595
  }
596
596
  catch (e) {
597
- spinner.fail('Failed to attach secrets');
597
+ spinner.stop();
598
598
  throw e;
599
599
  }
600
600
  });
@@ -638,7 +638,7 @@ function registerServiceCommand(program) {
638
638
  }
639
639
  }
640
640
  catch (e) {
641
- spinner.fail('Failed to detach secrets');
641
+ spinner.stop();
642
642
  throw e;
643
643
  }
644
644
  });
@@ -45,7 +45,6 @@ const api_1 = require("../lib/api");
45
45
  const errors_1 = require("../lib/errors");
46
46
  const analytics_1 = require("../lib/analytics");
47
47
  const installed_1 = require("../lib/installed");
48
- const pricing_1 = require("../lib/pricing");
49
48
  const package_json_1 = __importDefault(require("../../package.json"));
50
49
  const DEFAULT_VERSION = 'latest';
51
50
  function stripFrontmatter(content) {
@@ -110,55 +109,6 @@ async function downloadSkillWithFallback(config, org, skill, version, workspaceI
110
109
  throw new errors_1.CliError(`${org}/${skill} is not a skill (type: ${skillType || 'prompt'})`);
111
110
  }
112
111
  }
113
- // Check if paid skill BEFORE attempting download
114
- if (skillMeta && (0, pricing_1.isPaidAgent)(skillMeta)) {
115
- // Paid skill - check ownership
116
- if (config.apiKey) {
117
- const callerOrg = await (0, api_1.getOrg)(config, workspaceId);
118
- const isOwner = (skillMeta.org_id && callerOrg.id === skillMeta.org_id) ||
119
- (skillMeta.org_slug && callerOrg.slug === skillMeta.org_slug);
120
- if (isOwner) {
121
- // Owner - fetch from authenticated endpoint with full content
122
- const myAgents = await (0, api_1.listMyAgents)(config, workspaceId);
123
- const matching = myAgents.filter(a => a.name === skill && a.type === 'skill');
124
- if (matching.length > 0) {
125
- let targetAgent;
126
- if (version === 'latest') {
127
- targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
128
- }
129
- else {
130
- const found = matching.find(a => a.version === version);
131
- if (!found) {
132
- throw new api_1.ApiError(`Skill '${org}/${skill}@${version}' not found`, 404);
133
- }
134
- targetAgent = found;
135
- }
136
- // Fetch full skill data with prompt from authenticated endpoint
137
- const skillData = await (0, api_1.request)(config, 'GET', `/agents/${targetAgent.id}`);
138
- // Convert Agent to SkillDownload format
139
- return {
140
- type: skillData.type,
141
- name: skillData.name,
142
- version: skillData.version,
143
- description: skillData.description,
144
- prompt: skillData.prompt,
145
- };
146
- }
147
- }
148
- else {
149
- // Non-owner - block with helpful message
150
- const price = (0, pricing_1.formatPrice)(skillMeta);
151
- throw new errors_1.CliError(`This skill is paid (${price}) and can only be used on the server.\n\n` +
152
- `Paid skills are loaded automatically during server execution.`);
153
- }
154
- }
155
- else {
156
- // Not authenticated - block
157
- const price = (0, pricing_1.formatPrice)(skillMeta);
158
- throw new errors_1.CliError(`This skill is paid (${price}) and can only be used on the server.\n\n` +
159
- `Paid skills are loaded automatically during server execution.`);
160
- }
161
- }
162
112
  // Check if download is disabled (server-only skill)
163
113
  if (skillMeta && skillMeta.allow_local_download === false) {
164
114
  if (config.apiKey) {
@@ -204,11 +154,6 @@ async function downloadSkillWithFallback(config, org, skill, version, workspaceI
204
154
  // If download fails but metadata exists, it might be a 403 for other reasons
205
155
  if (err instanceof api_1.ApiError && err.status === 403) {
206
156
  const payload = err.payload;
207
- if (payload?.error?.code === 'PAID_AGENT_SERVER_ONLY') {
208
- const price = payload.error.price_per_call_cents || 0;
209
- throw new errors_1.CliError(`This skill costs $${(price / 100).toFixed(2)}/call and runs on server only.\n\n` +
210
- `Use: orchagent run ${org}/${skill}@${version} --data '{...}'`);
211
- }
212
157
  if (payload?.error?.code === 'DOWNLOAD_DISABLED') {
213
158
  throw new errors_1.CliError(`This skill is server-only and cannot be downloaded.\n\n` +
214
159
  `Skills are loaded automatically during server execution via 'orchagent run'.`);
@@ -31,13 +31,5 @@ function registerWhoamiCommand(program) {
31
31
  process.stdout.write(`Active workspace: ${configFile.workspace}\n`);
32
32
  }
33
33
  }
34
- // Show balance after org info
35
- try {
36
- const balance = await (0, api_1.getCreditsBalance)(config);
37
- process.stdout.write(`Credits: $${(balance.balance_cents / 100).toFixed(2)} USD\n`);
38
- }
39
- catch {
40
- // Ignore errors - don't let balance check break whoami
41
- }
42
34
  });
43
35
  }
package/dist/lib/api.js CHANGED
@@ -65,8 +65,6 @@ exports.getEnvironment = getEnvironment;
65
65
  exports.createEnvironment = createEnvironment;
66
66
  exports.deleteEnvironment = deleteEnvironment;
67
67
  exports.setWorkspaceDefaultEnvironment = setWorkspaceDefaultEnvironment;
68
- exports.getCreditsBalance = getCreditsBalance;
69
- exports.createCreditCheckout = createCreditCheckout;
70
68
  exports.listAgentKeys = listAgentKeys;
71
69
  exports.createAgentKey = createAgentKey;
72
70
  exports.deleteAgentKey = deleteAgentKey;
@@ -531,16 +529,6 @@ async function setWorkspaceDefaultEnvironment(config, workspaceId, environmentId
531
529
  headers: { 'Content-Type': 'application/json' },
532
530
  });
533
531
  }
534
- // Billing API functions
535
- async function getCreditsBalance(config) {
536
- return request(config, 'GET', '/billing/credits');
537
- }
538
- async function createCreditCheckout(config, amountCents) {
539
- return request(config, 'POST', '/billing/add-credits', {
540
- body: JSON.stringify({ amount_cents: amountCents }),
541
- headers: { 'Content-Type': 'application/json' },
542
- });
543
- }
544
532
  async function listAgentKeys(config, agentId) {
545
533
  return request(config, 'GET', `/agents/${agentId}/keys`);
546
534
  }
@@ -45,6 +45,8 @@ class CliError extends Error {
45
45
  exitCode;
46
46
  cause;
47
47
  responseBody;
48
+ /** When true, exitWithError skips printing — the message was already shown (e.g. via spinner.fail). */
49
+ displayed;
48
50
  constructor(message, exitCode = 1) {
49
51
  super(message);
50
52
  this.exitCode = exitCode;
@@ -78,7 +80,12 @@ async function exitWithError(err) {
78
80
  }
79
81
  // Flush PostHog before exiting
80
82
  await (0, analytics_1.shutdownPostHog)();
81
- process.stderr.write(`${message}\n`);
83
+ // Skip printing if the error was already shown (e.g. by spinner.fail)
84
+ const alreadyDisplayed = (err instanceof CliError && err.displayed) ||
85
+ (err instanceof Error && err._displayed);
86
+ if (!alreadyDisplayed) {
87
+ process.stderr.write(`${message}\n`);
88
+ }
82
89
  if (err instanceof CliError) {
83
90
  process.exit(err.exitCode);
84
91
  }
@@ -96,6 +96,11 @@ async function withSpinner(text, fn, options) {
96
96
  ? options.failText(err)
97
97
  : options?.failText || (err instanceof Error ? err.message : 'Failed');
98
98
  spinner.fail(failMsg);
99
+ // Mark as already displayed so exitWithError doesn't print again
100
+ if (err instanceof Error) {
101
+ ;
102
+ err._displayed = true;
103
+ }
99
104
  throw err;
100
105
  }
101
106
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.82",
3
+ "version": "0.3.84",
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>",
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isPaidAgent = isPaidAgent;
4
- exports.formatPrice = formatPrice;
5
- function isPaidAgent(agent) {
6
- // Fail-closed: per_call mode with missing or >0 price is paid
7
- if (agent.pricing_mode === 'per_call') {
8
- const price = agent.price_per_call_cents;
9
- return price === null || price === undefined || price > 0;
10
- }
11
- return false;
12
- }
13
- function formatPrice(agent) {
14
- if (!isPaidAgent(agent)) {
15
- return 'FREE';
16
- }
17
- const price = agent.price_per_call_cents;
18
- if (!price) {
19
- return 'PAID (server-only)'; // Fail-closed message
20
- }
21
- return `$${(price / 100).toFixed(2)}/call`;
22
- }