@orchagent/cli 0.3.33 → 0.3.34
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/call.js +27 -14
- package/dist/commands/publish.js +76 -1
- package/dist/commands/search.js +9 -4
- package/dist/commands/security.js +2 -2
- package/dist/commands/star.js +10 -3
- package/dist/commands/whoami.js +18 -0
- package/dist/index.js +6 -1
- package/dist/lib/api.js +3 -1
- package/dist/lib/doctor/checks/llm.js +128 -106
- package/dist/lib/doctor/output.js +91 -13
- package/dist/lib/doctor/runner.js +3 -12
- package/dist/lib/update-notifier.js +146 -0
- package/package.json +1 -1
- 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/call.js
CHANGED
|
@@ -246,7 +246,8 @@ Paid Agents:
|
|
|
246
246
|
}
|
|
247
247
|
if (isOwner) {
|
|
248
248
|
// Owner: show free message, no balance check needed
|
|
249
|
-
|
|
249
|
+
if (!options.json)
|
|
250
|
+
process.stderr.write(`Cost: FREE (author)\n\n`);
|
|
250
251
|
}
|
|
251
252
|
else {
|
|
252
253
|
// Non-owner: check balance
|
|
@@ -254,7 +255,8 @@ Paid Agents:
|
|
|
254
255
|
pricingInfo = { price_cents: price ?? null };
|
|
255
256
|
if (!price || price <= 0) {
|
|
256
257
|
// Price missing or invalid - warn but proceed (server will enforce)
|
|
257
|
-
|
|
258
|
+
if (!options.json)
|
|
259
|
+
process.stderr.write(`Warning: Pricing data unavailable. The server will verify payment.\n\n`);
|
|
258
260
|
}
|
|
259
261
|
else {
|
|
260
262
|
// Valid price - check balance
|
|
@@ -272,11 +274,13 @@ Paid Agents:
|
|
|
272
274
|
process.exit(errors_1.ExitCodes.PERMISSION_DENIED);
|
|
273
275
|
}
|
|
274
276
|
// Sufficient balance - show cost preview
|
|
275
|
-
|
|
277
|
+
if (!options.json)
|
|
278
|
+
process.stderr.write(`Cost: $${(price / 100).toFixed(2)}/call\n\n`);
|
|
276
279
|
}
|
|
277
280
|
catch (err) {
|
|
278
281
|
// Balance check failed - warn but proceed (server will enforce)
|
|
279
|
-
|
|
282
|
+
if (!options.json)
|
|
283
|
+
process.stderr.write(`Warning: Could not verify balance. The server will check payment.\n\n`);
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
286
|
}
|
|
@@ -455,9 +459,9 @@ Paid Agents:
|
|
|
455
459
|
sourceLabel = multipart.sourceLabel;
|
|
456
460
|
}
|
|
457
461
|
const url = `${resolved.apiUrl.replace(/\/$/, '')}/${org}/${parsed.agent}/${parsed.version}/${endpoint}`;
|
|
458
|
-
// Make the API call with a spinner
|
|
459
|
-
const spinner = (0, spinner_1.createSpinner)(`Calling ${org}/${parsed.agent}@${parsed.version}...`);
|
|
460
|
-
spinner
|
|
462
|
+
// Make the API call with a spinner (suppress in --json mode for clean machine-readable output)
|
|
463
|
+
const spinner = options.json ? null : (0, spinner_1.createSpinner)(`Calling ${org}/${parsed.agent}@${parsed.version}...`);
|
|
464
|
+
spinner?.start();
|
|
461
465
|
let response;
|
|
462
466
|
try {
|
|
463
467
|
response = await (0, api_1.safeFetchWithRetryForCalls)(url, {
|
|
@@ -467,7 +471,7 @@ Paid Agents:
|
|
|
467
471
|
});
|
|
468
472
|
}
|
|
469
473
|
catch (err) {
|
|
470
|
-
spinner
|
|
474
|
+
spinner?.fail(`Call failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
471
475
|
throw err;
|
|
472
476
|
}
|
|
473
477
|
if (!response.ok) {
|
|
@@ -485,7 +489,7 @@ Paid Agents:
|
|
|
485
489
|
: undefined;
|
|
486
490
|
// Part 2: Handle 402 Payment Required
|
|
487
491
|
if (response.status === 402 || errorCode === 'INSUFFICIENT_CREDITS') {
|
|
488
|
-
spinner
|
|
492
|
+
spinner?.fail('Insufficient credits');
|
|
489
493
|
let errorMessage = 'Insufficient credits to call this agent.\n\n';
|
|
490
494
|
// Use pricing info from pre-call check if available
|
|
491
495
|
if (pricingInfo?.price_cents) {
|
|
@@ -498,22 +502,31 @@ Paid Agents:
|
|
|
498
502
|
throw new errors_1.CliError(errorMessage, errors_1.ExitCodes.PERMISSION_DENIED);
|
|
499
503
|
}
|
|
500
504
|
if (errorCode === 'LLM_KEY_REQUIRED') {
|
|
501
|
-
spinner
|
|
505
|
+
spinner?.fail('LLM key required');
|
|
502
506
|
throw new errors_1.CliError('This public agent requires you to provide an LLM key.\n' +
|
|
503
507
|
'Use --key <key> --provider <provider> or set OPENAI_API_KEY/ANTHROPIC_API_KEY env var.');
|
|
504
508
|
}
|
|
509
|
+
if (errorCode === 'LLM_RATE_LIMITED') {
|
|
510
|
+
const rateLimitMsg = typeof payload === 'object' && payload
|
|
511
|
+
? payload.error?.message || 'Rate limit exceeded'
|
|
512
|
+
: 'Rate limit exceeded';
|
|
513
|
+
spinner?.fail('Rate limited by LLM provider');
|
|
514
|
+
throw new errors_1.CliError(rateLimitMsg + '\n\n' +
|
|
515
|
+
'This is the LLM provider\'s rate limit on your API key, not an OrchAgent limit.\n' +
|
|
516
|
+
'To switch providers: orch call <agent> --provider <gemini|anthropic|openai>', errors_1.ExitCodes.RATE_LIMITED);
|
|
517
|
+
}
|
|
505
518
|
const message = typeof payload === 'object' && payload
|
|
506
519
|
? payload.error
|
|
507
520
|
?.message ||
|
|
508
521
|
payload.message ||
|
|
509
522
|
response.statusText
|
|
510
523
|
: response.statusText;
|
|
511
|
-
spinner
|
|
524
|
+
spinner?.fail(`Call failed: ${message}`);
|
|
512
525
|
throw new errors_1.CliError(message);
|
|
513
526
|
}
|
|
514
|
-
spinner
|
|
515
|
-
// After successful call, if it was a paid agent, show cost
|
|
516
|
-
if ((0, pricing_1.isPaidAgent)(agentMeta) && pricingInfo?.price_cents && pricingInfo.price_cents > 0) {
|
|
527
|
+
spinner?.succeed(`Called ${org}/${parsed.agent}@${parsed.version}`);
|
|
528
|
+
// After successful call, if it was a paid agent, show cost (suppress in --json mode)
|
|
529
|
+
if (!options.json && (0, pricing_1.isPaidAgent)(agentMeta) && pricingInfo?.price_cents && pricingInfo.price_cents > 0) {
|
|
517
530
|
process.stderr.write(`\nCost: $${(pricingInfo.price_cents / 100).toFixed(2)} USD\n`);
|
|
518
531
|
}
|
|
519
532
|
// Track successful call
|
package/dist/commands/publish.js
CHANGED
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extractTemplateVariables = extractTemplateVariables;
|
|
7
|
+
exports.deriveInputSchema = deriveInputSchema;
|
|
6
8
|
exports.registerPublishCommand = registerPublishCommand;
|
|
7
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
10
|
const path_1 = __importDefault(require("path"));
|
|
@@ -14,6 +16,44 @@ const api_1 = require("../lib/api");
|
|
|
14
16
|
const errors_1 = require("../lib/errors");
|
|
15
17
|
const analytics_1 = require("../lib/analytics");
|
|
16
18
|
const bundle_1 = require("../lib/bundle");
|
|
19
|
+
/**
|
|
20
|
+
* Extract template placeholders from a prompt template.
|
|
21
|
+
* Matches double-brace patterns like {{variable}}.
|
|
22
|
+
* Returns unique variable names in order of first appearance.
|
|
23
|
+
*/
|
|
24
|
+
function extractTemplateVariables(template) {
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const result = [];
|
|
27
|
+
// Match double-brace template variables: two opening braces, word chars, two closing braces
|
|
28
|
+
const pattern = /\{\{(\w+)\}\}/g;
|
|
29
|
+
let match;
|
|
30
|
+
while ((match = pattern.exec(template)) !== null) {
|
|
31
|
+
const name = match[1];
|
|
32
|
+
if (!seen.has(name)) {
|
|
33
|
+
seen.add(name);
|
|
34
|
+
result.push(name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Derive a JSON Schema input object from template variable names.
|
|
41
|
+
* Each variable becomes a required string property.
|
|
42
|
+
*/
|
|
43
|
+
function deriveInputSchema(variables) {
|
|
44
|
+
const properties = {};
|
|
45
|
+
for (const name of variables) {
|
|
46
|
+
properties[name] = {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: `Value for the ${name} template variable`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties,
|
|
54
|
+
required: [...variables],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
17
57
|
/**
|
|
18
58
|
* Handle security flagged error response (422 with error: 'content_flagged')
|
|
19
59
|
* Returns true if the error was handled, false otherwise
|
|
@@ -273,6 +313,7 @@ function registerPublishCommand(program) {
|
|
|
273
313
|
const price = parseFloat(options.price);
|
|
274
314
|
process.stdout.write(`Pricing: $${price.toFixed(2)} USD per call\n`);
|
|
275
315
|
}
|
|
316
|
+
process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
|
|
276
317
|
}
|
|
277
318
|
catch (err) {
|
|
278
319
|
if (handleSecurityFlaggedError(err)) {
|
|
@@ -338,12 +379,14 @@ function registerPublishCommand(program) {
|
|
|
338
379
|
// Read schemas
|
|
339
380
|
let inputSchema;
|
|
340
381
|
let outputSchema;
|
|
382
|
+
let schemaFromFile = false;
|
|
341
383
|
const schemaPath = path_1.default.join(cwd, 'schema.json');
|
|
342
384
|
try {
|
|
343
385
|
const raw = await promises_1.default.readFile(schemaPath, 'utf-8');
|
|
344
386
|
const schemas = JSON.parse(raw);
|
|
345
387
|
inputSchema = schemas.input;
|
|
346
388
|
outputSchema = schemas.output;
|
|
389
|
+
schemaFromFile = true;
|
|
347
390
|
}
|
|
348
391
|
catch (err) {
|
|
349
392
|
// Schema is optional
|
|
@@ -351,6 +394,33 @@ function registerPublishCommand(program) {
|
|
|
351
394
|
throw new errors_1.CliError(`Failed to read schema.json: ${err}`);
|
|
352
395
|
}
|
|
353
396
|
}
|
|
397
|
+
// For prompt agents, derive input schema from template variables if needed
|
|
398
|
+
if (prompt && (manifest.type === 'prompt' || manifest.type === 'skill')) {
|
|
399
|
+
const templateVars = extractTemplateVariables(prompt);
|
|
400
|
+
if (templateVars.length > 0) {
|
|
401
|
+
if (!schemaFromFile) {
|
|
402
|
+
// No schema.json provided - auto-generate from template variables
|
|
403
|
+
inputSchema = deriveInputSchema(templateVars);
|
|
404
|
+
}
|
|
405
|
+
else if (inputSchema && 'properties' in inputSchema) {
|
|
406
|
+
// schema.json exists - check for mismatches with template variables
|
|
407
|
+
const schemaProps = Object.keys(inputSchema.properties || {});
|
|
408
|
+
const missing = templateVars.filter(v => !schemaProps.includes(v));
|
|
409
|
+
const extra = schemaProps.filter(p => !templateVars.includes(p));
|
|
410
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
411
|
+
const parts = [];
|
|
412
|
+
if (missing.length > 0) {
|
|
413
|
+
parts.push(`template uses {{${missing.join('}}, {{')}}} but schema.json doesn't define ${missing.join(', ')}`);
|
|
414
|
+
}
|
|
415
|
+
if (extra.length > 0) {
|
|
416
|
+
parts.push(`schema.json defines ${extra.join(', ')} but template doesn't use {{${extra.join('}}, {{')}}}`);
|
|
417
|
+
}
|
|
418
|
+
process.stderr.write(chalk_1.default.yellow(`Warning: Schema mismatch - ${parts.join('; ')}.\n`));
|
|
419
|
+
process.stderr.write(chalk_1.default.yellow(` Consider updating schema.json to match your prompt.md template variables.\n`));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
354
424
|
// For code-based agents, either --url is required OR we bundle the code
|
|
355
425
|
let agentUrl = options.url;
|
|
356
426
|
let shouldUploadBundle = false;
|
|
@@ -393,10 +463,14 @@ function registerPublishCommand(program) {
|
|
|
393
463
|
// Prompt agent validations
|
|
394
464
|
const promptBytes = prompt ? Buffer.byteLength(prompt, 'utf-8') : 0;
|
|
395
465
|
process.stderr.write(` ✓ prompt.md found (${promptBytes.toLocaleString()} bytes)\n`);
|
|
396
|
-
if (
|
|
466
|
+
if (schemaFromFile) {
|
|
397
467
|
const schemaTypes = [inputSchema ? 'input' : null, outputSchema ? 'output' : null].filter(Boolean).join(' + ');
|
|
398
468
|
process.stderr.write(` ✓ schema.json found (${schemaTypes} schemas)\n`);
|
|
399
469
|
}
|
|
470
|
+
else if (inputSchema) {
|
|
471
|
+
const vars = prompt ? extractTemplateVariables(prompt) : [];
|
|
472
|
+
process.stderr.write(` ✓ Input schema derived from template variables: ${vars.join(', ')}\n`);
|
|
473
|
+
}
|
|
400
474
|
}
|
|
401
475
|
else if (manifest.type === 'code') {
|
|
402
476
|
// Code agent validations
|
|
@@ -583,5 +657,6 @@ function registerPublishCommand(program) {
|
|
|
583
657
|
if (shouldUploadBundle) {
|
|
584
658
|
process.stdout.write(`\nNote: Hosted code execution is in beta. Contact support for full deployment.\n`);
|
|
585
659
|
}
|
|
660
|
+
process.stdout.write(`\nView analytics and usage: https://orchagent.io/dashboard\n`);
|
|
586
661
|
});
|
|
587
662
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -16,6 +16,7 @@ function registerSearchCommand(program) {
|
|
|
16
16
|
.option('--recent', 'Show most recently published')
|
|
17
17
|
.option('--mine', 'Show only your own agents (including private)')
|
|
18
18
|
.option('--type <type>', 'Filter by type: agents, skills, code, prompt, skill, all (default: all)', 'all')
|
|
19
|
+
.option('--tags <tags>', 'Filter by tags (comma-separated, e.g., security,devops)')
|
|
19
20
|
.option('--limit <n>', `Max results (default: ${DEFAULT_LIMIT})`, String(DEFAULT_LIMIT))
|
|
20
21
|
.option('--free', 'Show only free agents')
|
|
21
22
|
.option('--paid', 'Show only paid agents')
|
|
@@ -25,6 +26,9 @@ Pricing Filters:
|
|
|
25
26
|
--free Show only free agents
|
|
26
27
|
--paid Show only paid agents
|
|
27
28
|
|
|
29
|
+
Tag Filters:
|
|
30
|
+
--tags security,devops Show agents matching any of these tags
|
|
31
|
+
|
|
28
32
|
Ownership Filters:
|
|
29
33
|
--mine Show your own agents (public and private). Requires login.
|
|
30
34
|
`)
|
|
@@ -34,6 +38,7 @@ Ownership Filters:
|
|
|
34
38
|
// Map type filter for API (null means no filter)
|
|
35
39
|
const typeFilter = options.type === 'all' ? undefined : options.type;
|
|
36
40
|
const sort = options.popular ? 'stars' : options.recent ? 'recent' : undefined;
|
|
41
|
+
const tags = options.tags ? options.tags.split(',').map(t => t.trim()).filter(Boolean) : undefined;
|
|
37
42
|
let agents;
|
|
38
43
|
if (options.mine) {
|
|
39
44
|
// --mine: search within user's own agents (public + private)
|
|
@@ -51,12 +56,12 @@ Ownership Filters:
|
|
|
51
56
|
options.popular = true;
|
|
52
57
|
}
|
|
53
58
|
if (query) {
|
|
54
|
-
agents = await (0, api_1.searchAgents)(config, query, { sort, type: typeFilter });
|
|
55
|
-
await (0, analytics_1.track)('cli_search', { query, type: options.type });
|
|
59
|
+
agents = await (0, api_1.searchAgents)(config, query, { sort, tags, type: typeFilter });
|
|
60
|
+
await (0, analytics_1.track)('cli_search', { query, type: options.type, tags: options.tags });
|
|
56
61
|
}
|
|
57
62
|
else {
|
|
58
|
-
agents = await (0, api_1.listPublicAgents)(config, { sort, type: typeFilter });
|
|
59
|
-
await (0, analytics_1.track)('cli_search', { mode: options.popular ? 'popular' : 'recent', type: options.type });
|
|
63
|
+
agents = await (0, api_1.listPublicAgents)(config, { sort, tags, type: typeFilter });
|
|
64
|
+
await (0, analytics_1.track)('cli_search', { mode: options.popular ? 'popular' : 'recent', type: options.type, tags: options.tags });
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
// Filter by pricing if requested
|
|
@@ -67,7 +67,7 @@ function formatSummaryOutput(result) {
|
|
|
67
67
|
process.stdout.write('━'.repeat(50) + '\n\n');
|
|
68
68
|
// Agent info
|
|
69
69
|
process.stdout.write(`${chalk_1.default.bold('Agent:')} ${result.agent_id}\n`);
|
|
70
|
-
process.stdout.write(`${chalk_1.default.bold('Scan Time:')} ${result.
|
|
70
|
+
process.stdout.write(`${chalk_1.default.bold('Scan Time:')} ${result.scanned_at}\n\n`);
|
|
71
71
|
// Risk level banner
|
|
72
72
|
process.stdout.write(`${chalk_1.default.bold('Risk Level:')} ${riskLevelColor(result.risk_level)}\n\n`);
|
|
73
73
|
// Summary stats
|
|
@@ -282,7 +282,7 @@ function generateMarkdownReport(result) {
|
|
|
282
282
|
lines.push('# Security Scan Report');
|
|
283
283
|
lines.push('');
|
|
284
284
|
lines.push(`**Agent:** ${result.agent_id}`);
|
|
285
|
-
lines.push(`**Scan Time:** ${result.
|
|
285
|
+
lines.push(`**Scan Time:** ${result.scanned_at}`);
|
|
286
286
|
lines.push(`**Risk Level:** ${result.risk_level.toUpperCase()}`);
|
|
287
287
|
lines.push('');
|
|
288
288
|
lines.push('## Summary');
|
package/dist/commands/star.js
CHANGED
|
@@ -33,9 +33,16 @@ function registerStarCommand(program) {
|
|
|
33
33
|
process.stdout.write(`Removed star from ${org}/${name}/${version}\n`);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
await (0, api_1.starAgent)(config, agentInfo.id);
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const result = await (0, api_1.starAgent)(config, agentInfo.id);
|
|
37
|
+
if (result.starred) {
|
|
38
|
+
await (0, analytics_1.track)('cli_star', { agent: `${org}/${name}/${version}` });
|
|
39
|
+
process.stdout.write(`Starred ${org}/${name}/${version}\n`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Already starred — toggle off
|
|
43
|
+
await (0, api_1.unstarAgent)(config, agentInfo.id);
|
|
44
|
+
process.stdout.write(`Unstarred ${org}/${name}/${version}\n`);
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
47
|
});
|
|
41
48
|
}
|
package/dist/commands/whoami.js
CHANGED
|
@@ -13,6 +13,24 @@ function registerWhoamiCommand(program) {
|
|
|
13
13
|
process.stdout.write(`Logged in as: ${org.name}\n`);
|
|
14
14
|
process.stdout.write(`Org slug: ${org.slug}\n`);
|
|
15
15
|
process.stdout.write(`API URL: ${config.apiUrl}\n`);
|
|
16
|
+
// Show active workspace if one is set
|
|
17
|
+
const configFile = await (0, config_1.loadConfig)();
|
|
18
|
+
if (configFile.workspace) {
|
|
19
|
+
try {
|
|
20
|
+
const response = await (0, api_1.request)(config, 'GET', '/workspaces');
|
|
21
|
+
const workspace = response.workspaces.find((w) => w.slug === configFile.workspace);
|
|
22
|
+
if (workspace) {
|
|
23
|
+
process.stdout.write(`Active workspace: ${workspace.name} (${workspace.slug})\n`);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
process.stdout.write(`Active workspace: ${configFile.workspace} (not found)\n`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Workspace fetch failed - show slug only
|
|
31
|
+
process.stdout.write(`Active workspace: ${configFile.workspace}\n`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
16
34
|
// Show balance after org info
|
|
17
35
|
try {
|
|
18
36
|
const balance = await (0, api_1.getCreditsBalance)(config);
|
package/dist/index.js
CHANGED
|
@@ -47,8 +47,10 @@ const errors_1 = require("./lib/errors");
|
|
|
47
47
|
const analytics_1 = require("./lib/analytics");
|
|
48
48
|
const config_1 = require("./lib/config");
|
|
49
49
|
const spinner_1 = require("./lib/spinner");
|
|
50
|
+
const update_notifier_1 = require("./lib/update-notifier");
|
|
50
51
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
51
52
|
(0, analytics_1.initPostHog)();
|
|
53
|
+
(0, update_notifier_1.checkForUpdates)();
|
|
52
54
|
const program = new commander_1.Command();
|
|
53
55
|
program
|
|
54
56
|
.name('orchagent')
|
|
@@ -89,4 +91,7 @@ async function main() {
|
|
|
89
91
|
}
|
|
90
92
|
main()
|
|
91
93
|
.catch(errors_1.exitWithError)
|
|
92
|
-
.finally(() =>
|
|
94
|
+
.finally(() => {
|
|
95
|
+
(0, update_notifier_1.printUpdateNotification)();
|
|
96
|
+
return (0, analytics_1.shutdownPostHog)();
|
|
97
|
+
});
|
package/dist/lib/api.js
CHANGED
|
@@ -238,6 +238,8 @@ async function listPublicAgents(config, options) {
|
|
|
238
238
|
const params = new URLSearchParams();
|
|
239
239
|
if (options?.sort)
|
|
240
240
|
params.append('sort', options.sort);
|
|
241
|
+
if (options?.tags?.length)
|
|
242
|
+
params.append('tags', options.tags.join(','));
|
|
241
243
|
if (options?.type)
|
|
242
244
|
params.append('type', options.type);
|
|
243
245
|
const queryStr = params.toString();
|
|
@@ -257,7 +259,7 @@ async function createAgent(config, data) {
|
|
|
257
259
|
});
|
|
258
260
|
}
|
|
259
261
|
async function starAgent(config, agentId) {
|
|
260
|
-
|
|
262
|
+
return request(config, 'POST', `/agents/${agentId}/star`);
|
|
261
263
|
}
|
|
262
264
|
async function unstarAgent(config, agentId) {
|
|
263
265
|
await request(config, 'DELETE', `/agents/${agentId}/star`);
|
|
@@ -1,128 +1,150 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkServerLlmKeys = checkServerLlmKeys;
|
|
4
|
-
exports.checkLocalLlmEnvVars = checkLocalLlmEnvVars;
|
|
5
3
|
exports.runLlmChecks = runLlmChecks;
|
|
6
4
|
const config_1 = require("../../config");
|
|
7
5
|
const api_1 = require("../../api");
|
|
8
|
-
//
|
|
9
|
-
const
|
|
10
|
-
{
|
|
11
|
-
{
|
|
12
|
-
{
|
|
13
|
-
{
|
|
6
|
+
// All supported LLM providers (single source of truth)
|
|
7
|
+
const PROVIDERS = [
|
|
8
|
+
{ id: 'openai', displayName: 'OpenAI', envVars: ['OPENAI_API_KEY'], keyPrefix: 'sk-' },
|
|
9
|
+
{ id: 'anthropic', displayName: 'Anthropic', envVars: ['ANTHROPIC_API_KEY'], keyPrefix: 'sk-ant-' },
|
|
10
|
+
{ id: 'gemini', displayName: 'Gemini', envVars: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'] },
|
|
11
|
+
{ id: 'ollama', displayName: 'Ollama', envVars: ['OLLAMA_HOST'], isEndpoint: true },
|
|
14
12
|
];
|
|
15
13
|
/**
|
|
16
|
-
*
|
|
14
|
+
* Get a format hint for a key value, or null if the format looks OK.
|
|
15
|
+
* Hints are informational only — never errors or warnings.
|
|
17
16
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
details: { reason: 'no api key' },
|
|
27
|
-
};
|
|
17
|
+
function getFormatHint(provider, value) {
|
|
18
|
+
if ('isEndpoint' in provider && provider.isEndpoint)
|
|
19
|
+
return null;
|
|
20
|
+
if (!('keyPrefix' in provider) || !provider.keyPrefix)
|
|
21
|
+
return null;
|
|
22
|
+
const prefix = provider.keyPrefix;
|
|
23
|
+
if (!value.startsWith(prefix)) {
|
|
24
|
+
return `Key doesn't match expected format (${prefix}...)`;
|
|
28
25
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
category: 'llm',
|
|
55
|
-
name: 'server_llm_keys',
|
|
56
|
-
status: 'success',
|
|
57
|
-
message: `Server LLM keys configured (${providers.join(', ')})`,
|
|
58
|
-
details: { count: keys.length, providers },
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
if (err instanceof api_1.ApiError && err.status === 401) {
|
|
63
|
-
return {
|
|
64
|
-
category: 'llm',
|
|
65
|
-
name: 'server_llm_keys',
|
|
66
|
-
status: 'warning',
|
|
67
|
-
message: 'Cannot check server LLM keys (auth failed)',
|
|
68
|
-
details: { error: err.message },
|
|
69
|
-
};
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gather per-provider status from server keys and local env vars.
|
|
30
|
+
*/
|
|
31
|
+
function gatherProviderStatuses(serverProviders) {
|
|
32
|
+
return PROVIDERS.map((provider) => {
|
|
33
|
+
// Server status
|
|
34
|
+
const server = serverProviders === null ? null : serverProviders.includes(provider.id);
|
|
35
|
+
// Local status — check each env var
|
|
36
|
+
let local = false;
|
|
37
|
+
let localEnvVar;
|
|
38
|
+
let formatHint;
|
|
39
|
+
for (const envVar of provider.envVars) {
|
|
40
|
+
const value = process.env[envVar];
|
|
41
|
+
if (value) {
|
|
42
|
+
local = true;
|
|
43
|
+
localEnvVar = envVar;
|
|
44
|
+
const hint = getFormatHint(provider, value);
|
|
45
|
+
if (hint)
|
|
46
|
+
formatHint = hint;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
70
49
|
}
|
|
71
50
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
51
|
+
providerId: provider.id,
|
|
52
|
+
displayName: provider.displayName,
|
|
53
|
+
server,
|
|
54
|
+
local,
|
|
55
|
+
localEnvVar,
|
|
56
|
+
formatHint,
|
|
77
57
|
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build a human-readable location string from server/local status.
|
|
62
|
+
*/
|
|
63
|
+
function locationString(status) {
|
|
64
|
+
if (status.server === null) {
|
|
65
|
+
// Server unknown (offline)
|
|
66
|
+
if (status.local)
|
|
67
|
+
return 'Server unknown, local configured';
|
|
68
|
+
return 'Server unknown, not local';
|
|
78
69
|
}
|
|
70
|
+
if (status.server && status.local)
|
|
71
|
+
return 'Configured (server + local)';
|
|
72
|
+
if (status.server)
|
|
73
|
+
return 'Configured (server)';
|
|
74
|
+
if (status.local)
|
|
75
|
+
return 'Configured (local)';
|
|
76
|
+
return 'Not configured';
|
|
79
77
|
}
|
|
80
78
|
/**
|
|
81
|
-
*
|
|
79
|
+
* Run all LLM configuration checks with per-provider breakdown.
|
|
80
|
+
*
|
|
81
|
+
* When skipServer is true, server status is null for all providers
|
|
82
|
+
* (shown as "unknown" in output). Use this when the gateway is unreachable.
|
|
82
83
|
*/
|
|
83
|
-
async function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
async function runLlmChecks(options) {
|
|
85
|
+
let serverProviders = null;
|
|
86
|
+
if (!options?.skipServer) {
|
|
87
|
+
try {
|
|
88
|
+
const config = await (0, config_1.getResolvedConfig)();
|
|
89
|
+
if (config.apiKey) {
|
|
90
|
+
const keys = await (0, api_1.fetchLlmKeys)(config);
|
|
91
|
+
serverProviders = keys.map((k) => k.provider);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
// If we can't reach the server, treat as unknown
|
|
96
|
+
if (err instanceof api_1.ApiError && err.status === 401) {
|
|
97
|
+
// Auth failed — server providers unknown
|
|
98
|
+
}
|
|
99
|
+
// Network error or other — server providers unknown
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
const statuses = gatherProviderStatuses(serverProviders);
|
|
103
|
+
const results = [];
|
|
104
|
+
// Per-provider results
|
|
105
|
+
for (const status of statuses) {
|
|
106
|
+
const configured = status.server === true || status.local;
|
|
107
|
+
results.push({
|
|
95
108
|
category: 'llm',
|
|
96
|
-
name:
|
|
97
|
-
status: '
|
|
98
|
-
message:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
name: `llm_provider_${status.providerId}`,
|
|
110
|
+
status: configured ? 'success' : 'info',
|
|
111
|
+
message: `${status.providerId} — ${locationString(status)}`,
|
|
112
|
+
details: {
|
|
113
|
+
providerId: status.providerId,
|
|
114
|
+
displayName: status.displayName,
|
|
115
|
+
server: status.server,
|
|
116
|
+
local: status.local,
|
|
117
|
+
...(status.localEnvVar && { localEnvVar: status.localEnvVar }),
|
|
118
|
+
...(status.formatHint && { formatHint: status.formatHint }),
|
|
119
|
+
},
|
|
120
|
+
});
|
|
102
121
|
}
|
|
103
|
-
//
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// Users who only use server-side calls don't need local keys
|
|
122
|
-
if (serverResult.status === 'success' && localResult.status === 'warning') {
|
|
123
|
-
localResult.status = 'info';
|
|
124
|
-
localResult.message = 'No local LLM API keys (using server keys)';
|
|
125
|
-
localResult.fix = undefined;
|
|
122
|
+
// Summary result
|
|
123
|
+
const configuredCount = statuses.filter((s) => s.server === true || s.local).length;
|
|
124
|
+
const firstUnconfigured = statuses.find((s) => s.server !== true && !s.local);
|
|
125
|
+
let summaryStatus;
|
|
126
|
+
let summaryMessage;
|
|
127
|
+
let summaryFix;
|
|
128
|
+
if (configuredCount === 0) {
|
|
129
|
+
summaryStatus = 'warning';
|
|
130
|
+
summaryMessage = 'No LLM providers configured';
|
|
131
|
+
summaryFix = firstUnconfigured ? `Run: orch keys add ${firstUnconfigured.providerId}` : undefined;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
summaryStatus = 'success';
|
|
135
|
+
summaryMessage =
|
|
136
|
+
configuredCount < 2
|
|
137
|
+
? 'Tip: Multiple providers enable automatic rate limit fallback.'
|
|
138
|
+
: `${configuredCount} providers configured`;
|
|
139
|
+
summaryFix = firstUnconfigured ? `Run: orch keys add ${firstUnconfigured.providerId}` : undefined;
|
|
126
140
|
}
|
|
127
|
-
|
|
141
|
+
results.push({
|
|
142
|
+
category: 'llm',
|
|
143
|
+
name: 'llm_provider_summary',
|
|
144
|
+
status: summaryStatus,
|
|
145
|
+
message: summaryMessage,
|
|
146
|
+
fix: summaryFix,
|
|
147
|
+
details: { configuredCount, totalProviders: PROVIDERS.length },
|
|
148
|
+
});
|
|
149
|
+
return results;
|
|
128
150
|
}
|