@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.
@@ -37,13 +37,6 @@ exports.claudeCodeAdapter = {
37
37
  errors.push('Agent has no prompt content');
38
38
  return { canConvert: false, warnings, errors };
39
39
  }
40
- // Warnings for fields that can't be fully mapped
41
- if (agent.input_schema) {
42
- warnings.push('input_schema will be described in the prompt body, not enforced');
43
- }
44
- if (agent.output_schema) {
45
- warnings.push('output_schema will be described in the prompt body, not enforced');
46
- }
47
40
  return { canConvert: true, warnings, errors };
48
41
  },
49
42
  convert(agent) {
@@ -52,8 +45,11 @@ exports.claudeCodeAdapter = {
52
45
  const frontmatter = {
53
46
  name: normalizedName,
54
47
  description: agent.description || `Delegatable agent: ${agent.name}`,
55
- tools: 'Read, Glob, Grep', // Safe defaults - read-only
56
48
  };
49
+ // Only include tools for agent-type (prompt and skill types are single LLM calls)
50
+ if (agent.type === 'agent') {
51
+ frontmatter.tools = 'Read, Glob, Grep'; // Safe defaults - read-only
52
+ }
57
53
  // Map model if specified
58
54
  if (agent.default_models?.anthropic) {
59
55
  const modelAlias = (0, utils_1.mapModelToAlias)(agent.default_models.anthropic);
@@ -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')) {
@@ -123,6 +123,133 @@ function main() {
123
123
 
124
124
  main();
125
125
  `;
126
+ const ALWAYS_ON_TEMPLATE_PY = `"""
127
+ orchagent always-on service entrypoint.
128
+
129
+ Runs a long-lived HTTP server that handles requests over HTTP.
130
+ This is the standard pattern for always-on services on orchagent.
131
+
132
+ IMPORTANT: Port 8080 is reserved by the platform health server.
133
+ Use a different port (default: 3000).
134
+
135
+ Local development:
136
+ python main.py
137
+ """
138
+
139
+ import json
140
+ import os
141
+ from http.server import HTTPServer, BaseHTTPRequestHandler
142
+
143
+ PORT = int(os.environ.get("PORT", "3000"))
144
+
145
+
146
+ class Handler(BaseHTTPRequestHandler):
147
+ def do_POST(self):
148
+ content_length = int(self.headers.get("Content-Length", 0))
149
+ body = self.rfile.read(content_length)
150
+ try:
151
+ data = json.loads(body) if body else {}
152
+ except json.JSONDecodeError:
153
+ self._respond(400, {"error": "Invalid JSON"})
154
+ return
155
+
156
+ user_input = data.get("input", "")
157
+
158
+ # --- Your logic here ---
159
+ # To use workspace secrets, add them to "required_secrets" in orchagent.json:
160
+ # "required_secrets": ["MY_API_KEY"]
161
+ # Then access via: os.environ["MY_API_KEY"]
162
+ result = f"Received: {user_input}"
163
+ # --- End your logic ---
164
+
165
+ self._respond(200, {"result": result})
166
+
167
+ def do_GET(self):
168
+ if self.path == "/health":
169
+ self._respond(200, {"status": "ok"})
170
+ return
171
+ self._respond(200, {"status": "running"})
172
+
173
+ def _respond(self, code, body):
174
+ self.send_response(code)
175
+ self.send_header("Content-Type", "application/json")
176
+ self.end_headers()
177
+ self.wfile.write(json.dumps(body).encode())
178
+
179
+ def log_message(self, format, *args):
180
+ print(f"[{self.log_date_time_string()}] {format % args}")
181
+
182
+
183
+ if __name__ == "__main__":
184
+ server = HTTPServer(("0.0.0.0", PORT), Handler)
185
+ print(f"Always-on service listening on port {PORT}")
186
+ server.serve_forever()
187
+ `;
188
+ const ALWAYS_ON_TEMPLATE_JS = `/**
189
+ * orchagent always-on service entrypoint.
190
+ *
191
+ * Runs a long-lived HTTP server that handles requests over HTTP.
192
+ * This is the standard pattern for always-on services on orchagent.
193
+ *
194
+ * IMPORTANT: Port 8080 is reserved by the platform health server.
195
+ * Use a different port (default: 3000).
196
+ *
197
+ * Local development:
198
+ * node main.js
199
+ */
200
+
201
+ const http = require('http');
202
+
203
+ const PORT = parseInt(process.env.PORT || '3000', 10);
204
+
205
+ const server = http.createServer((req, res) => {
206
+ if (req.method === 'GET') {
207
+ if (req.url === '/health') {
208
+ res.writeHead(200, { 'Content-Type': 'application/json' });
209
+ res.end(JSON.stringify({ status: 'ok' }));
210
+ return;
211
+ }
212
+ res.writeHead(200, { 'Content-Type': 'application/json' });
213
+ res.end(JSON.stringify({ status: 'running' }));
214
+ return;
215
+ }
216
+
217
+ if (req.method === 'POST') {
218
+ let body = '';
219
+ req.on('data', chunk => { body += chunk; });
220
+ req.on('end', () => {
221
+ let data;
222
+ try {
223
+ data = body ? JSON.parse(body) : {};
224
+ } catch {
225
+ res.writeHead(400, { 'Content-Type': 'application/json' });
226
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
227
+ return;
228
+ }
229
+
230
+ const input = data.input || '';
231
+
232
+ // --- Your logic here ---
233
+ // To use workspace secrets, add them to "required_secrets" in orchagent.json:
234
+ // "required_secrets": ["MY_API_KEY"]
235
+ // Then access via: process.env.MY_API_KEY
236
+ const result = \`Received: \${input}\`;
237
+ // --- End your logic ---
238
+
239
+ res.writeHead(200, { 'Content-Type': 'application/json' });
240
+ res.end(JSON.stringify({ result }));
241
+ });
242
+ return;
243
+ }
244
+
245
+ res.writeHead(405, { 'Content-Type': 'application/json' });
246
+ res.end(JSON.stringify({ error: 'Method not allowed' }));
247
+ });
248
+
249
+ server.listen(PORT, '0.0.0.0', () => {
250
+ console.log(\`Always-on service listening on port \${PORT}\`);
251
+ });
252
+ `;
126
253
  const DISCORD_MAIN_JS = `/**
127
254
  * Discord bot agent — powered by Claude.
128
255
  *
@@ -266,6 +393,7 @@ DISCORD_CHANNEL_IDS=
266
393
  # MODEL=claude-sonnet-4-5-20250929
267
394
  # MAX_TOKENS=1024
268
395
  `;
396
+ const AGENT_BUILDER_HINT = `\n Tip: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`;
269
397
  function readmeTemplate(agentName, flavor) {
270
398
  if (flavor === 'support_agent') {
271
399
  return `# ${agentName}
@@ -852,6 +980,11 @@ function registerInitCommand(program) {
852
980
  throw new errors_1.CliError('JavaScript agent-type agents are not yet supported. Use --type tool for JavaScript agents.');
853
981
  }
854
982
  // JS orchestrators are now supported via the orchagent-sdk npm package
983
+ // Block --language for types that don't create runtime files
984
+ if (isJavaScript && (initMode.type === 'prompt' || initMode.type === 'skill')) {
985
+ throw new errors_1.CliError(`The --language flag has no effect for ${initMode.type} types (no runtime files are created). ` +
986
+ 'Use --type tool or --type agent to create a project with runtime scaffolding.');
987
+ }
855
988
  if (options.template) {
856
989
  const template = options.template.trim().toLowerCase();
857
990
  const validTemplates = ['support-agent', 'discord', 'discord-js', 'github-weekly-summary'];
@@ -988,7 +1121,7 @@ function registerInitCommand(program) {
988
1121
  process.stdout.write(` ${s + 2}. Copy .env.example to .env and add platform tokens\n`);
989
1122
  process.stdout.write(` ${s + 3}. Test locally: pip install -r requirements.txt && python main.py\n`);
990
1123
  process.stdout.write(` ${s + 4}. Deploy: orch publish && orch service deploy\n`);
991
- process.stdout.write(`\n Skill: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`);
1124
+ process.stdout.write(AGENT_BUILDER_HINT);
992
1125
  return;
993
1126
  }
994
1127
  // Handle github-weekly-summary template separately (own file set + output)
@@ -1044,7 +1177,7 @@ function registerInitCommand(program) {
1044
1177
  process.stdout.write(` ${s + 3}. orch run <org>/${agentName} Test it\n`);
1045
1178
  process.stdout.write(` ${s + 4}. orch schedule create <org>/${agentName} --cron "0 9 * * 1" Schedule weekly\n`);
1046
1179
  process.stdout.write(`\n See README.md for full setup guide.\n`);
1047
- process.stdout.write(`\n Skill: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`);
1180
+ process.stdout.write(AGENT_BUILDER_HINT);
1048
1181
  return;
1049
1182
  }
1050
1183
  // Handle discord-js template separately (JS Discord bot)
@@ -1094,7 +1227,7 @@ function registerInitCommand(program) {
1094
1227
  process.stdout.write(` ${stepNum + 2}. Copy .env.example to .env and fill in your tokens\n`);
1095
1228
  process.stdout.write(` ${stepNum + 3}. Test locally: npm install && node main.js\n`);
1096
1229
  process.stdout.write(` ${stepNum + 4}. Deploy: orch publish\n`);
1097
- process.stdout.write(`\n Skill: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`);
1230
+ process.stdout.write(AGENT_BUILDER_HINT);
1098
1231
  return;
1099
1232
  }
1100
1233
  const manifestPath = path_1.default.join(targetDir, 'orchagent.json');
@@ -1182,7 +1315,7 @@ function registerInitCommand(program) {
1182
1315
  }
1183
1316
  else if (initMode.flavor === 'code_runtime') {
1184
1317
  if (isJavaScript) {
1185
- await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), CODE_TEMPLATE_JS);
1318
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.js'), runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_JS : CODE_TEMPLATE_JS);
1186
1319
  await promises_1.default.writeFile(path_1.default.join(targetDir, 'package.json'), JSON.stringify({
1187
1320
  name: agentName,
1188
1321
  private: true,
@@ -1191,7 +1324,7 @@ function registerInitCommand(program) {
1191
1324
  }, null, 2) + '\n');
1192
1325
  }
1193
1326
  else {
1194
- await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), CODE_TEMPLATE_PY);
1327
+ await promises_1.default.writeFile(path_1.default.join(targetDir, 'main.py'), runMode === 'always_on' ? ALWAYS_ON_TEMPLATE_PY : CODE_TEMPLATE_PY);
1195
1328
  }
1196
1329
  await promises_1.default.writeFile(schemaPath, SCHEMA_TEMPLATE);
1197
1330
  }
@@ -1226,12 +1359,13 @@ function registerInitCommand(program) {
1226
1359
  process.stdout.write(` ${prefix}.env.example - Environment variables template\n`);
1227
1360
  }
1228
1361
  else if (initMode.flavor === 'code_runtime') {
1362
+ const entrypointDesc = runMode === 'always_on' ? 'Always-on HTTP server' : 'Agent entrypoint (stdin/stdout JSON)';
1229
1363
  if (isJavaScript) {
1230
- process.stdout.write(` ${prefix}main.js - Agent entrypoint (stdin/stdout JSON)\n`);
1364
+ process.stdout.write(` ${prefix}main.js - ${entrypointDesc}\n`);
1231
1365
  process.stdout.write(` ${prefix}package.json - npm dependencies\n`);
1232
1366
  }
1233
1367
  else {
1234
- process.stdout.write(` ${prefix}main.py - Agent entrypoint (stdin/stdout JSON)\n`);
1368
+ process.stdout.write(` ${prefix}main.py - ${entrypointDesc}\n`);
1235
1369
  }
1236
1370
  }
1237
1371
  else {
@@ -1274,7 +1408,15 @@ function registerInitCommand(program) {
1274
1408
  if (name) {
1275
1409
  process.stdout.write(` 1. cd ${name}\n`);
1276
1410
  }
1277
- if (isJavaScript) {
1411
+ if (runMode === 'always_on') {
1412
+ const mainFile = isJavaScript ? 'main.js' : 'main.py';
1413
+ const testCmd = isJavaScript ? 'node main.js' : 'python main.py';
1414
+ process.stdout.write(` ${stepNum}. Edit ${mainFile} with your service logic\n`);
1415
+ process.stdout.write(` ${stepNum + 1}. Test locally: ${testCmd}\n`);
1416
+ process.stdout.write(` ${stepNum + 2}. Publish: orch publish\n`);
1417
+ process.stdout.write(` ${stepNum + 3}. Deploy: orch service deploy\n`);
1418
+ }
1419
+ else if (isJavaScript) {
1278
1420
  process.stdout.write(` ${stepNum}. Edit main.js with your agent logic\n`);
1279
1421
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
1280
1422
  process.stdout.write(` ${stepNum + 2}. Test: echo '{"input": "test"}' | node main.js\n`);
@@ -1296,6 +1438,6 @@ function registerInitCommand(program) {
1296
1438
  process.stdout.write(` ${stepNum + 1}. Edit schema.json with your input/output schemas\n`);
1297
1439
  process.stdout.write(` ${stepNum + 2}. Run: orchagent publish\n`);
1298
1440
  }
1299
- process.stdout.write(`\n Skill: orch skill install orchagent-public/agent-builder — gives your AI the full platform builder reference\n`);
1441
+ process.stdout.write(AGENT_BUILDER_HINT);
1300
1442
  });
1301
1443
  }
@@ -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)
@@ -56,10 +56,14 @@ function formatDuration(ms) {
56
56
  return `${(ms / 1000).toFixed(1)}s`;
57
57
  return `${(ms / 60000).toFixed(1)}m`;
58
58
  }
59
- /** Detect if a string looks like a UUID (run ID) */
59
+ /** Detect if a string looks like a full UUID (run ID) */
60
60
  function isUuid(value) {
61
61
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
62
62
  }
63
+ /** Detect if a string looks like a short UUID prefix (8+ hex chars) */
64
+ function isShortUuid(value) {
65
+ return /^[0-9a-f]{7,}$/i.test(value) && !value.includes('/');
66
+ }
63
67
  // ============================================
64
68
  // COMMAND REGISTRATION
65
69
  // ============================================
@@ -77,16 +81,38 @@ function registerLogsCommand(program) {
77
81
  throw new errors_1.CliError('Missing API key. Run `orch login` first.');
78
82
  }
79
83
  const workspaceId = await resolveWorkspaceId(config, options.workspace);
80
- // If target looks like a UUID, show detailed logs for that run
84
+ // If target looks like a UUID (full or short prefix), show detailed logs for that run
81
85
  if (target && isUuid(target)) {
82
86
  await showRunLogs(config, workspaceId, target, options.json);
83
87
  return;
84
88
  }
85
- // Otherwise list runs, optionally filtered by agent name
86
- await listRuns(config, workspaceId, target, options);
89
+ if (target && isShortUuid(target)) {
90
+ // Short UUID prefix — find the matching run from the list
91
+ const fullId = await resolveShortRunId(config, workspaceId, target);
92
+ await showRunLogs(config, workspaceId, fullId, options.json);
93
+ return;
94
+ }
95
+ // Otherwise list runs, optionally filtered by agent name.
96
+ // Strip org prefix if provided (e.g. "joe/my-agent" → "my-agent")
97
+ const agentFilter = target?.includes('/') ? target.split('/').pop() : target;
98
+ await listRuns(config, workspaceId, agentFilter, options);
87
99
  });
88
100
  }
89
101
  // ============================================
102
+ // SHORT RUN ID RESOLUTION
103
+ // ============================================
104
+ async function resolveShortRunId(config, workspaceId, shortId) {
105
+ // Server-side prefix matching — searches ALL runs, not just the last 200
106
+ const result = await (0, api_1.request)(config, 'GET', `/workspaces/${workspaceId}/runs?limit=200&run_id_prefix=${encodeURIComponent(shortId)}`);
107
+ if (result.runs.length === 0) {
108
+ throw new errors_1.CliError(`No run found matching '${shortId}'.`);
109
+ }
110
+ if (result.runs.length > 1) {
111
+ throw new errors_1.CliError(`Ambiguous run ID '${shortId}' — matches ${result.runs.length} runs. Use more characters to narrow it down.`);
112
+ }
113
+ return result.runs[0].id;
114
+ }
115
+ // ============================================
90
116
  // LIST RUNS
91
117
  // ============================================
92
118
  async function listRuns(config, workspaceId, agentName, options) {
@@ -161,6 +187,16 @@ async function showRunLogs(config, workspaceId, runId, json) {
161
187
  const exitLabel = result.exit_code === 0 ? chalk_1.default.green(String(result.exit_code)) : chalk_1.default.red(String(result.exit_code));
162
188
  process.stdout.write(`Exit code: ${exitLabel}\n`);
163
189
  }
190
+ // Input data
191
+ if (result.input_data != null && Object.keys(result.input_data).length > 0) {
192
+ process.stdout.write('\n' + chalk_1.default.bold.blue('--- input ---') + '\n' +
193
+ JSON.stringify(result.input_data, null, 2) + '\n');
194
+ }
195
+ // Output data
196
+ if (result.output_data != null && Object.keys(result.output_data).length > 0) {
197
+ process.stdout.write('\n' + chalk_1.default.bold.green('--- output ---') + '\n' +
198
+ JSON.stringify(result.output_data, null, 2) + '\n');
199
+ }
164
200
  // Error message
165
201
  if (result.error_message) {
166
202
  process.stdout.write('\n' + chalk_1.default.red.bold('Error:\n') + chalk_1.default.red(result.error_message) + '\n');