@orchagent/cli 0.3.28 → 0.3.31
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.
- package/dist/commands/agents.js +87 -10
- package/dist/commands/billing.js +122 -0
- package/dist/commands/call.js +85 -1
- package/dist/commands/index.js +8 -0
- package/dist/commands/info.js +56 -3
- package/dist/commands/install.js +64 -5
- package/dist/commands/pricing.js +72 -0
- package/dist/commands/publish.js +71 -0
- package/dist/commands/run.js +50 -0
- package/dist/commands/search.js +15 -0
- package/dist/commands/security.js +344 -0
- package/dist/commands/seller.js +142 -0
- package/dist/commands/skill.js +83 -8
- package/dist/commands/whoami.js +8 -0
- package/dist/index.js +4 -0
- package/dist/lib/api.js +41 -0
- package/dist/lib/errors.js +2 -0
- package/dist/lib/output.js +5 -1
- package/dist/lib/pricing.js +22 -0
- package/package.json +4 -3
- package/dist/commands/llm-config.js +0 -40
- package/dist/commands/publish.test.js +0 -475
- package/dist/commands/run.test.js +0 -330
- package/dist/lib/api.test.js +0 -230
- package/dist/lib/config.test.js +0 -144
package/dist/commands/agents.js
CHANGED
|
@@ -1,20 +1,97 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
2
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
39
|
exports.registerAgentsCommand = registerAgentsCommand;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const config_1 = require("../lib/config");
|
|
42
|
+
const api_1 = require("../lib/api");
|
|
43
|
+
const output_1 = require("../lib/output");
|
|
44
|
+
const pricing_1 = require("../lib/pricing");
|
|
4
45
|
function registerAgentsCommand(program) {
|
|
5
46
|
program
|
|
6
47
|
.command('agents')
|
|
7
|
-
.description('
|
|
48
|
+
.description('List your published agents')
|
|
8
49
|
.option('--filter <text>', 'Filter by name')
|
|
9
50
|
.option('--json', 'Output raw JSON')
|
|
10
|
-
.action(async () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
51
|
+
.action(async (options) => {
|
|
52
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
53
|
+
const agents = await (0, api_1.listMyAgents)(config);
|
|
54
|
+
// Apply filter if provided
|
|
55
|
+
const filteredAgents = options.filter
|
|
56
|
+
? agents.filter(a => a.name.toLowerCase().includes(options.filter.toLowerCase()))
|
|
57
|
+
: agents;
|
|
58
|
+
if (options.json) {
|
|
59
|
+
(0, output_1.printJson)(filteredAgents);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (filteredAgents.length === 0) {
|
|
63
|
+
process.stdout.write(options.filter
|
|
64
|
+
? `No agents found matching "${options.filter}"\n`
|
|
65
|
+
: 'No agents published yet.\n\nPublish an agent: orch publish\n');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Print table with pricing
|
|
69
|
+
const Table = (await Promise.resolve().then(() => __importStar(require('cli-table3')))).default;
|
|
70
|
+
const table = new Table({
|
|
71
|
+
head: [
|
|
72
|
+
chalk_1.default.bold('Agent'),
|
|
73
|
+
chalk_1.default.bold('Version'),
|
|
74
|
+
chalk_1.default.bold('Type'),
|
|
75
|
+
chalk_1.default.bold('Stars'),
|
|
76
|
+
chalk_1.default.bold('Price'),
|
|
77
|
+
chalk_1.default.bold('Description'),
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
filteredAgents.forEach((agent) => {
|
|
81
|
+
const name = agent.name;
|
|
82
|
+
const version = agent.version;
|
|
83
|
+
const type = agent.type || 'code';
|
|
84
|
+
const stars = agent.stars_count ?? 0;
|
|
85
|
+
const price = (0, pricing_1.formatPrice)(agent);
|
|
86
|
+
const coloredPrice = (0, pricing_1.isPaidAgent)(agent) ? chalk_1.default.yellow(price) : chalk_1.default.green(price);
|
|
87
|
+
const desc = agent.description
|
|
88
|
+
? agent.description.length > 30
|
|
89
|
+
? agent.description.slice(0, 27) + '...'
|
|
90
|
+
: agent.description
|
|
91
|
+
: '-';
|
|
92
|
+
table.push([name, version, type, stars.toString(), coloredPrice, desc]);
|
|
93
|
+
});
|
|
94
|
+
process.stdout.write(`${table.toString()}\n`);
|
|
95
|
+
process.stdout.write(`\nTotal: ${filteredAgents.length} agent${filteredAgents.length === 1 ? '' : 's'}\n`);
|
|
19
96
|
});
|
|
20
97
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerBillingCommand = registerBillingCommand;
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const open_1 = __importDefault(require("open"));
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
const errors_1 = require("../lib/errors");
|
|
13
|
+
const output_1 = require("../lib/output");
|
|
14
|
+
function registerBillingCommand(program) {
|
|
15
|
+
const billing = program
|
|
16
|
+
.command('billing')
|
|
17
|
+
.description('Manage prepaid credits for calling paid agents');
|
|
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) => {
|
|
24
|
+
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;
|
|
29
|
+
}
|
|
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
|
+
});
|
|
122
|
+
}
|
package/dist/commands/call.js
CHANGED
|
@@ -13,6 +13,7 @@ const output_1 = require("../lib/output");
|
|
|
13
13
|
const spinner_1 = require("../lib/spinner");
|
|
14
14
|
const llm_1 = require("../lib/llm");
|
|
15
15
|
const analytics_1 = require("../lib/analytics");
|
|
16
|
+
const pricing_1 = require("../lib/pricing");
|
|
16
17
|
const DEFAULT_VERSION = 'latest';
|
|
17
18
|
// Keys that might indicate local file path references in JSON payloads
|
|
18
19
|
const LOCAL_PATH_KEYS = ['path', 'directory', 'file', 'filepath', 'dir', 'folder', 'local'];
|
|
@@ -132,7 +133,7 @@ async function resolveJsonBody(input) {
|
|
|
132
133
|
function registerCallCommand(program) {
|
|
133
134
|
program
|
|
134
135
|
.command('call <agent> [file]')
|
|
135
|
-
.description('Call an agent
|
|
136
|
+
.description('Call an agent on the server (may incur charges for paid agents)')
|
|
136
137
|
.option('--endpoint <endpoint>', 'Override agent endpoint')
|
|
137
138
|
.option('--tenant <tenant>', 'Tenant identifier for multi-tenant callers')
|
|
138
139
|
.option('--data <json>', 'JSON payload (string or @file, @- for stdin)')
|
|
@@ -160,6 +161,13 @@ Important: Remote agents cannot access your local filesystem. If your --data pay
|
|
|
160
161
|
contains keys like 'path', 'directory', 'file', etc., those values will be interpreted
|
|
161
162
|
by the server, not your local machine. To send local files, use the positional file
|
|
162
163
|
argument or --file option instead.
|
|
164
|
+
|
|
165
|
+
Paid Agents:
|
|
166
|
+
Paid agents charge per call and deduct from your prepaid credits.
|
|
167
|
+
Check your balance: orch billing balance
|
|
168
|
+
Add credits: orch billing add 5
|
|
169
|
+
|
|
170
|
+
Same-author calls are FREE - you won't be charged for calling your own agents.
|
|
163
171
|
`)
|
|
164
172
|
.action(async (agentRef, file, options) => {
|
|
165
173
|
// Merge --input alias into --data
|
|
@@ -176,6 +184,64 @@ argument or --file option instead.
|
|
|
176
184
|
throw new errors_1.CliError('Missing org. Use org/agent or set default org.');
|
|
177
185
|
}
|
|
178
186
|
const agentMeta = await (0, api_1.getAgentWithFallback)(resolved, org, parsed.agent, parsed.version);
|
|
187
|
+
// Part 1: Pre-call balance check for paid agents
|
|
188
|
+
let pricingInfo;
|
|
189
|
+
if ((0, pricing_1.isPaidAgent)(agentMeta)) {
|
|
190
|
+
// Detect ownership: compare agent's org with caller's org
|
|
191
|
+
let isOwner = false;
|
|
192
|
+
try {
|
|
193
|
+
const callerOrg = await (0, api_1.getOrg)(resolved);
|
|
194
|
+
// Use org_id when available (preferred), org_slug as fallback
|
|
195
|
+
const agentOrgId = agentMeta.org_id;
|
|
196
|
+
const agentOrgSlug = agentMeta.org_slug;
|
|
197
|
+
if (agentOrgId && callerOrg.id === agentOrgId) {
|
|
198
|
+
isOwner = true;
|
|
199
|
+
}
|
|
200
|
+
else if (agentOrgSlug && callerOrg.slug === agentOrgSlug) {
|
|
201
|
+
isOwner = true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// If we can't determine ownership, treat as non-owner (fail-safe)
|
|
206
|
+
isOwner = false;
|
|
207
|
+
}
|
|
208
|
+
if (isOwner) {
|
|
209
|
+
// Owner: show free message, no balance check needed
|
|
210
|
+
process.stderr.write(`Cost: FREE (author)\n\n`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// Non-owner: check balance
|
|
214
|
+
const price = agentMeta.price_per_call_cents;
|
|
215
|
+
pricingInfo = { price_cents: price ?? null };
|
|
216
|
+
if (!price || price <= 0) {
|
|
217
|
+
// Price missing or invalid - warn but proceed (server will enforce)
|
|
218
|
+
process.stderr.write(`Warning: Pricing data unavailable. The server will verify payment.\n\n`);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Valid price - check balance
|
|
222
|
+
try {
|
|
223
|
+
const balanceData = await (0, api_1.getCreditsBalance)(resolved);
|
|
224
|
+
const balance = balanceData.balance_cents;
|
|
225
|
+
if (balance < price) {
|
|
226
|
+
// Insufficient balance
|
|
227
|
+
process.stderr.write(`Insufficient credits:\n` +
|
|
228
|
+
` Balance: $${(balance / 100).toFixed(2)}\n` +
|
|
229
|
+
` Required: $${(price / 100).toFixed(2)}\n\n` +
|
|
230
|
+
`Add credits:\n` +
|
|
231
|
+
` orch billing add 5\n` +
|
|
232
|
+
` orch billing balance # check current balance\n`);
|
|
233
|
+
process.exit(errors_1.ExitCodes.PERMISSION_DENIED);
|
|
234
|
+
}
|
|
235
|
+
// Sufficient balance - show cost preview
|
|
236
|
+
process.stderr.write(`Cost: $${(price / 100).toFixed(2)}/call\n\n`);
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
// Balance check failed - warn but proceed (server will enforce)
|
|
240
|
+
process.stderr.write(`Warning: Could not verify balance. The server will check payment.\n\n`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
179
245
|
const endpoint = options.endpoint?.trim() || agentMeta.default_endpoint || 'analyze';
|
|
180
246
|
const headers = {
|
|
181
247
|
Authorization: `Bearer ${resolved.apiKey}`,
|
|
@@ -341,6 +407,20 @@ argument or --file option instead.
|
|
|
341
407
|
const errorCode = typeof payload === 'object' && payload
|
|
342
408
|
? payload.error?.code
|
|
343
409
|
: undefined;
|
|
410
|
+
// Part 2: Handle 402 Payment Required
|
|
411
|
+
if (response.status === 402 || errorCode === 'INSUFFICIENT_CREDITS') {
|
|
412
|
+
spinner.fail('Insufficient credits');
|
|
413
|
+
let errorMessage = 'Insufficient credits to call this agent.\n\n';
|
|
414
|
+
// Use pricing info from pre-call check if available
|
|
415
|
+
if (pricingInfo?.price_cents) {
|
|
416
|
+
errorMessage += `This agent costs $${(pricingInfo.price_cents / 100).toFixed(2)} per call.\n\n`;
|
|
417
|
+
}
|
|
418
|
+
errorMessage +=
|
|
419
|
+
'Add credits:\n' +
|
|
420
|
+
' orch billing add 5\n' +
|
|
421
|
+
' orch billing balance # check current balance\n';
|
|
422
|
+
throw new errors_1.CliError(errorMessage, errors_1.ExitCodes.PERMISSION_DENIED);
|
|
423
|
+
}
|
|
344
424
|
if (errorCode === 'LLM_KEY_REQUIRED') {
|
|
345
425
|
spinner.fail('LLM key required');
|
|
346
426
|
throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
|
|
@@ -356,6 +436,10 @@ argument or --file option instead.
|
|
|
356
436
|
throw new errors_1.CliError(message);
|
|
357
437
|
}
|
|
358
438
|
spinner.succeed(`Called ${org}/${parsed.agent}@${parsed.version}`);
|
|
439
|
+
// After successful call, if it was a paid agent, show cost
|
|
440
|
+
if ((0, pricing_1.isPaidAgent)(agentMeta) && pricingInfo?.price_cents && pricingInfo.price_cents > 0) {
|
|
441
|
+
process.stderr.write(`\nCost: $${(pricingInfo.price_cents / 100).toFixed(2)} USD\n`);
|
|
442
|
+
}
|
|
359
443
|
// Track successful call
|
|
360
444
|
const inputType = filePaths.length > 0
|
|
361
445
|
? 'file'
|
package/dist/commands/index.js
CHANGED
|
@@ -28,6 +28,10 @@ const update_1 = require("./update");
|
|
|
28
28
|
const env_1 = require("./env");
|
|
29
29
|
const list_1 = require("./list");
|
|
30
30
|
const test_1 = require("./test");
|
|
31
|
+
const security_1 = require("./security");
|
|
32
|
+
const billing_1 = require("./billing");
|
|
33
|
+
const seller_1 = require("./seller");
|
|
34
|
+
const pricing_1 = require("./pricing");
|
|
31
35
|
function registerCommands(program) {
|
|
32
36
|
(0, login_1.registerLoginCommand)(program);
|
|
33
37
|
(0, whoami_1.registerWhoamiCommand)(program);
|
|
@@ -56,4 +60,8 @@ function registerCommands(program) {
|
|
|
56
60
|
(0, env_1.registerEnvCommand)(program);
|
|
57
61
|
(0, list_1.registerListCommand)(program);
|
|
58
62
|
(0, test_1.registerTestCommand)(program);
|
|
63
|
+
(0, security_1.registerSecurityCommand)(program);
|
|
64
|
+
(0, billing_1.registerBillingCommand)(program);
|
|
65
|
+
(0, seller_1.registerSellerCommand)(program);
|
|
66
|
+
(0, pricing_1.registerPricingCommand)(program);
|
|
59
67
|
}
|
package/dist/commands/info.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.registerInfoCommand = registerInfoCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
4
8
|
const config_1 = require("../lib/config");
|
|
5
9
|
const api_1 = require("../lib/api");
|
|
6
10
|
const agent_ref_1 = require("../lib/agent-ref");
|
|
11
|
+
const pricing_1 = require("../lib/pricing");
|
|
7
12
|
function formatSchema(schema, indent = ' ') {
|
|
8
13
|
const lines = [];
|
|
9
14
|
const props = schema.properties || {};
|
|
@@ -45,11 +50,48 @@ async function fetchReadme(url) {
|
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
async function downloadAgentWithFallback(config, org, agent, version) {
|
|
48
|
-
//
|
|
53
|
+
// First fetch public metadata to get pricing fields
|
|
54
|
+
let publicMeta = null;
|
|
49
55
|
try {
|
|
50
|
-
|
|
56
|
+
publicMeta = await (0, api_1.getPublicAgent)(config, org, agent, version);
|
|
51
57
|
}
|
|
52
58
|
catch (err) {
|
|
59
|
+
// If public metadata not found, continue to try download
|
|
60
|
+
if (!(err instanceof api_1.ApiError) || err.status !== 404) {
|
|
61
|
+
// Some other error, rethrow
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Try public download endpoint
|
|
66
|
+
try {
|
|
67
|
+
const downloadData = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${agent}/${version}/download`);
|
|
68
|
+
// Merge pricing fields from publicMeta if not present
|
|
69
|
+
if (publicMeta && !downloadData.pricing_mode) {
|
|
70
|
+
downloadData.pricing_mode = publicMeta.pricing_mode;
|
|
71
|
+
downloadData.price_per_call_cents = publicMeta.price_per_call_cents;
|
|
72
|
+
}
|
|
73
|
+
return downloadData;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// Handle 403 PAID_AGENT_SERVER_ONLY error
|
|
77
|
+
if (err instanceof api_1.ApiError && err.status === 403) {
|
|
78
|
+
const payload = err.payload;
|
|
79
|
+
if (payload?.error?.code === 'PAID_AGENT_SERVER_ONLY') {
|
|
80
|
+
// For non-owners, use public metadata
|
|
81
|
+
if (publicMeta) {
|
|
82
|
+
return {
|
|
83
|
+
type: publicMeta.type,
|
|
84
|
+
name: publicMeta.name,
|
|
85
|
+
version: publicMeta.version,
|
|
86
|
+
description: publicMeta.description,
|
|
87
|
+
supported_providers: publicMeta.supported_providers || ['any'],
|
|
88
|
+
source_url: publicMeta.source_url,
|
|
89
|
+
pricing_mode: publicMeta.pricing_mode,
|
|
90
|
+
price_per_call_cents: publicMeta.price_per_call_cents,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
53
95
|
if (!(err instanceof api_1.ApiError) || err.status !== 404)
|
|
54
96
|
throw err;
|
|
55
97
|
}
|
|
@@ -93,12 +135,14 @@ async function downloadAgentWithFallback(config, org, agent, version) {
|
|
|
93
135
|
run_command: targetAgent.run_command,
|
|
94
136
|
url: targetAgent.url,
|
|
95
137
|
sdk_compatible: false,
|
|
138
|
+
pricing_mode: targetAgent.pricing_mode,
|
|
139
|
+
price_per_call_cents: targetAgent.price_per_call_cents,
|
|
96
140
|
};
|
|
97
141
|
}
|
|
98
142
|
function registerInfoCommand(program) {
|
|
99
143
|
program
|
|
100
144
|
.command('info <agent>')
|
|
101
|
-
.description('Show agent
|
|
145
|
+
.description('Show agent details including pricing, inputs, and outputs')
|
|
102
146
|
.option('--json', 'Output as JSON')
|
|
103
147
|
.action(async (agentArg, options) => {
|
|
104
148
|
const config = await (0, config_1.getResolvedConfig)();
|
|
@@ -123,6 +167,15 @@ function registerInfoCommand(program) {
|
|
|
123
167
|
}
|
|
124
168
|
process.stdout.write(`Type: ${agentData.type}\n`);
|
|
125
169
|
process.stdout.write(`Providers: ${agentData.supported_providers.join(', ')}\n`);
|
|
170
|
+
// Display pricing information
|
|
171
|
+
const priceStr = (0, pricing_1.formatPrice)(agentData);
|
|
172
|
+
const color = (0, pricing_1.isPaidAgent)(agentData) ? chalk_1.default.yellow : chalk_1.default.green;
|
|
173
|
+
process.stdout.write(`Price: ${color(priceStr)}\n`);
|
|
174
|
+
// If paid, show server-only message for non-owners
|
|
175
|
+
if ((0, pricing_1.isPaidAgent)(agentData)) {
|
|
176
|
+
process.stdout.write(chalk_1.default.gray('Note: Paid agents run on server only (use orch call)\n'));
|
|
177
|
+
process.stdout.write(chalk_1.default.gray(' Owners can still download for development/testing\n'));
|
|
178
|
+
}
|
|
126
179
|
if (agentData.type === 'code') {
|
|
127
180
|
// Don't show internal routing URLs - they confuse users
|
|
128
181
|
if (agentData.url && !agentData.url.includes('.internal')) {
|
package/dist/commands/install.js
CHANGED
|
@@ -14,6 +14,7 @@ const analytics_1 = require("../lib/analytics");
|
|
|
14
14
|
const adapters_1 = require("../adapters");
|
|
15
15
|
const installed_1 = require("../lib/installed");
|
|
16
16
|
const agents_md_utils_1 = require("../lib/agents-md-utils");
|
|
17
|
+
const pricing_1 = require("../lib/pricing");
|
|
17
18
|
const DEFAULT_VERSION = 'latest';
|
|
18
19
|
function parseAgentRef(value) {
|
|
19
20
|
const [ref, versionPart] = value.split('@');
|
|
@@ -28,16 +29,70 @@ function parseAgentRef(value) {
|
|
|
28
29
|
throw new errors_1.CliError('Invalid agent reference. Use org/agent or agent format.');
|
|
29
30
|
}
|
|
30
31
|
async function downloadAgentWithFallback(config, org, name, version) {
|
|
31
|
-
//
|
|
32
|
+
// Fetch public metadata first to check if paid
|
|
33
|
+
let publicMeta;
|
|
32
34
|
try {
|
|
33
|
-
|
|
34
|
-
return { ...agent, org_slug: org };
|
|
35
|
+
publicMeta = await (0, api_1.getPublicAgent)(config, org, name, version);
|
|
35
36
|
}
|
|
36
37
|
catch (err) {
|
|
37
|
-
|
|
38
|
+
// If 404, might be private agent - will handle below
|
|
39
|
+
if (err instanceof api_1.ApiError && err.status === 404) {
|
|
40
|
+
publicMeta = null;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
38
43
|
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check if paid agent
|
|
47
|
+
if (publicMeta && (0, pricing_1.isPaidAgent)(publicMeta)) {
|
|
48
|
+
// Paid agent - check if owner
|
|
49
|
+
if (config.apiKey) {
|
|
50
|
+
const callerOrg = await (0, api_1.getOrg)(config);
|
|
51
|
+
const isOwner = (publicMeta.org_id && callerOrg.id === publicMeta.org_id) ||
|
|
52
|
+
(publicMeta.org_slug && callerOrg.slug === publicMeta.org_slug);
|
|
53
|
+
if (isOwner) {
|
|
54
|
+
// Owner - fetch from authenticated endpoint with full prompt
|
|
55
|
+
const myAgents = await (0, api_1.listMyAgents)(config);
|
|
56
|
+
const matching = myAgents.filter(a => a.name === name);
|
|
57
|
+
if (matching.length > 0) {
|
|
58
|
+
let targetAgent;
|
|
59
|
+
if (version === 'latest') {
|
|
60
|
+
targetAgent = matching.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())[0];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const found = matching.find(a => a.version === version);
|
|
64
|
+
if (!found) {
|
|
65
|
+
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
66
|
+
}
|
|
67
|
+
targetAgent = found;
|
|
68
|
+
}
|
|
69
|
+
// Fetch full agent data with prompt from authenticated endpoint
|
|
70
|
+
const agentData = await (0, api_1.request)(config, 'GET', `/agents/${targetAgent.id}`);
|
|
71
|
+
return { ...agentData, org_slug: org };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Non-owner - block with helpful message
|
|
76
|
+
const price = (0, pricing_1.formatPrice)(publicMeta);
|
|
77
|
+
throw new errors_1.CliError(`This agent is paid (${price}) and runs on server only.\n\n` +
|
|
78
|
+
`Use: orch call ${org}/${name}@${version} --input '{...}'`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Not authenticated - block
|
|
83
|
+
const price = (0, pricing_1.formatPrice)(publicMeta);
|
|
84
|
+
throw new errors_1.CliError(`This agent is paid (${price}) and runs on server only.\n\n` +
|
|
85
|
+
`Use: orch call ${org}/${name}@${version} --input '{...}'`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Free agent - proceed normally with public data
|
|
89
|
+
if (publicMeta) {
|
|
90
|
+
// Cast PublicAgent to Agent for use as CanonicalAgent
|
|
91
|
+
// publicRequest already returns full agent data for free agents
|
|
92
|
+
const agent = await (0, api_1.publicRequest)(config, `/public/agents/${org}/${name}/${version}`);
|
|
93
|
+
return { ...agent, org_slug: org };
|
|
39
94
|
}
|
|
40
|
-
//
|
|
95
|
+
// No public metadata found - fallback to authenticated endpoint for private agents
|
|
41
96
|
if (!config.apiKey) {
|
|
42
97
|
throw new api_1.ApiError(`Agent '${org}/${name}@${version}' not found`, 404);
|
|
43
98
|
}
|
|
@@ -71,6 +126,10 @@ function registerInstallCommand(program) {
|
|
|
71
126
|
.option('--scope <scope>', 'Install scope: user (home dir) or project (current dir)', 'user')
|
|
72
127
|
.option('--dry-run', 'Show what would be installed without making changes')
|
|
73
128
|
.option('--json', 'Output result as JSON (for automation/tooling)')
|
|
129
|
+
.addHelpText('after', `
|
|
130
|
+
Note: Paid agents cannot be installed locally - they run on server only.
|
|
131
|
+
Use 'orch call' to execute paid agents.
|
|
132
|
+
`)
|
|
74
133
|
.action(async (agentArg, options) => {
|
|
75
134
|
const jsonMode = options.json === true;
|
|
76
135
|
const log = (msg) => { if (!jsonMode)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerPricingCommand = registerPricingCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const config_1 = require("../lib/config");
|
|
9
|
+
const api_1 = require("../lib/api");
|
|
10
|
+
const errors_1 = require("../lib/errors");
|
|
11
|
+
function registerPricingCommand(program) {
|
|
12
|
+
program
|
|
13
|
+
.command('pricing <agent> <mode>')
|
|
14
|
+
.description('Set pricing for your agent (free or per-call in USD)')
|
|
15
|
+
.action(async (agentRef, mode) => {
|
|
16
|
+
const resolved = await (0, config_1.getResolvedConfig)();
|
|
17
|
+
// Parse agent reference
|
|
18
|
+
const [orgOrAgent, maybeName] = agentRef.split('/');
|
|
19
|
+
let org;
|
|
20
|
+
let agentName;
|
|
21
|
+
if (maybeName) {
|
|
22
|
+
// Format: org/agent
|
|
23
|
+
org = orgOrAgent;
|
|
24
|
+
agentName = maybeName;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// Format: agent (use default org)
|
|
28
|
+
const userOrg = await (0, api_1.getOrg)(resolved);
|
|
29
|
+
org = userOrg.slug;
|
|
30
|
+
agentName = orgOrAgent;
|
|
31
|
+
}
|
|
32
|
+
// Look up agent ID
|
|
33
|
+
const myAgents = await (0, api_1.listMyAgents)(resolved);
|
|
34
|
+
const agent = myAgents.find(a => a.org_slug === org && a.name === agentName);
|
|
35
|
+
if (!agent) {
|
|
36
|
+
throw new errors_1.CliError(`Agent '${org}/${agentName}' not found in your agents.\n\n` +
|
|
37
|
+
`List your agents: orch agents`, errors_1.ExitCodes.NOT_FOUND);
|
|
38
|
+
}
|
|
39
|
+
// Parse pricing mode
|
|
40
|
+
let pricingMode;
|
|
41
|
+
let pricePerCallCents;
|
|
42
|
+
if (mode === 'free' || mode === '0') {
|
|
43
|
+
pricingMode = 'free';
|
|
44
|
+
pricePerCallCents = undefined;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Parse as dollar amount
|
|
48
|
+
const priceFloat = parseFloat(mode);
|
|
49
|
+
if (isNaN(priceFloat)) {
|
|
50
|
+
throw new errors_1.CliError(`Invalid pricing mode: ${mode}\n\n` +
|
|
51
|
+
`Use "free" or a dollar amount (e.g., "0.50" for $0.50 USD)`, errors_1.ExitCodes.INVALID_INPUT);
|
|
52
|
+
}
|
|
53
|
+
if (priceFloat < 0.01) {
|
|
54
|
+
throw new errors_1.CliError('Price must be at least $0.01 USD', errors_1.ExitCodes.INVALID_INPUT);
|
|
55
|
+
}
|
|
56
|
+
pricingMode = 'per_call';
|
|
57
|
+
pricePerCallCents = Math.round(priceFloat * 100);
|
|
58
|
+
}
|
|
59
|
+
// Set pricing
|
|
60
|
+
await (0, api_1.setAgentPricing)(resolved, agent.id, pricingMode, pricePerCallCents);
|
|
61
|
+
// Show confirmation
|
|
62
|
+
process.stdout.write(chalk_1.default.green('✓ Pricing updated\n'));
|
|
63
|
+
process.stdout.write(`Agent: ${org}/${agentName}\n`);
|
|
64
|
+
if (pricingMode === 'free') {
|
|
65
|
+
process.stdout.write(`Mode: FREE\n`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
process.stdout.write(`Mode: Pay per call\n`);
|
|
69
|
+
process.stdout.write(`Price: $${(pricePerCallCents / 100).toFixed(2)} USD per call\n`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|