@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
|
@@ -19,8 +19,81 @@ const CATEGORY_NAMES = {
|
|
|
19
19
|
configuration: 'Configuration',
|
|
20
20
|
connectivity: 'Connectivity',
|
|
21
21
|
authentication: 'Authentication',
|
|
22
|
-
llm: 'LLM
|
|
22
|
+
llm: 'LLM Providers',
|
|
23
23
|
};
|
|
24
|
+
// Symbol for server-unknown state
|
|
25
|
+
const UNKNOWN_SYMBOL = chalk_1.default.dim('?');
|
|
26
|
+
/**
|
|
27
|
+
* Render the LLM section with per-provider table layout.
|
|
28
|
+
*/
|
|
29
|
+
function renderLlmSection(checks, verbose) {
|
|
30
|
+
const providerChecks = checks.filter((c) => c.name.startsWith('llm_provider_') && c.name !== 'llm_provider_summary');
|
|
31
|
+
const summaryCheck = checks.find((c) => c.name === 'llm_provider_summary');
|
|
32
|
+
// Calculate padding for aligned columns
|
|
33
|
+
const maxIdLen = Math.max(...providerChecks.map((c) => {
|
|
34
|
+
const id = c.details?.providerId || '';
|
|
35
|
+
return id.length;
|
|
36
|
+
}));
|
|
37
|
+
for (const check of providerChecks) {
|
|
38
|
+
const id = check.details?.providerId || '';
|
|
39
|
+
const padded = id.padEnd(maxIdLen);
|
|
40
|
+
const serverVal = check.details?.server;
|
|
41
|
+
const localVal = check.details?.local;
|
|
42
|
+
const configured = serverVal === true || localVal;
|
|
43
|
+
// Pick symbol: ✓ for configured, ✗ for not configured, ? for server-unknown
|
|
44
|
+
let symbol;
|
|
45
|
+
if (configured) {
|
|
46
|
+
symbol = SYMBOLS.success;
|
|
47
|
+
}
|
|
48
|
+
else if (serverVal === null) {
|
|
49
|
+
symbol = UNKNOWN_SYMBOL;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
symbol = SYMBOLS.error;
|
|
53
|
+
}
|
|
54
|
+
// Build location text
|
|
55
|
+
let location;
|
|
56
|
+
if (serverVal === null) {
|
|
57
|
+
location = localVal ? 'Server unknown, local configured' : 'Server unknown, not local';
|
|
58
|
+
}
|
|
59
|
+
else if (serverVal && localVal) {
|
|
60
|
+
location = 'Configured (server + local)';
|
|
61
|
+
}
|
|
62
|
+
else if (serverVal) {
|
|
63
|
+
location = 'Configured (server)';
|
|
64
|
+
}
|
|
65
|
+
else if (localVal) {
|
|
66
|
+
location = 'Configured (local)';
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
location = 'Not configured';
|
|
70
|
+
}
|
|
71
|
+
process.stdout.write(` ${symbol} ${padded} ${location}\n`);
|
|
72
|
+
// Show format hint as dim indented text
|
|
73
|
+
const formatHint = check.details?.formatHint;
|
|
74
|
+
if (formatHint) {
|
|
75
|
+
process.stdout.write(chalk_1.default.dim(` \u26a0 ${formatHint}\n`));
|
|
76
|
+
}
|
|
77
|
+
// Verbose: show details
|
|
78
|
+
if (verbose && check.details) {
|
|
79
|
+
for (const [key, value] of Object.entries(check.details)) {
|
|
80
|
+
if (key === 'providerId' || key === 'displayName' || key === 'formatHint')
|
|
81
|
+
continue;
|
|
82
|
+
const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
83
|
+
process.stdout.write(chalk_1.default.dim(` ${key}: ${displayValue}\n`));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Summary tip/fix
|
|
88
|
+
if (summaryCheck) {
|
|
89
|
+
process.stdout.write('\n');
|
|
90
|
+
const symbol = SYMBOLS[summaryCheck.status] || SYMBOLS.info;
|
|
91
|
+
process.stdout.write(` ${symbol} ${summaryCheck.message}\n`);
|
|
92
|
+
if (summaryCheck.fix) {
|
|
93
|
+
process.stdout.write(chalk_1.default.dim(` \u2192 ${summaryCheck.fix}\n`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
24
97
|
/**
|
|
25
98
|
* Group check results by category.
|
|
26
99
|
*/
|
|
@@ -65,18 +138,23 @@ function printHumanOutput(results, summary, verbose) {
|
|
|
65
138
|
for (const [category, checks] of groups) {
|
|
66
139
|
const displayName = CATEGORY_NAMES[category] || category;
|
|
67
140
|
process.stdout.write(chalk_1.default.bold(`${displayName}\n`));
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
141
|
+
if (category === 'llm') {
|
|
142
|
+
renderLlmSection(checks, verbose);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
for (const check of checks) {
|
|
146
|
+
const symbol = SYMBOLS[check.status] || SYMBOLS.info;
|
|
147
|
+
process.stdout.write(` ${symbol} ${check.message}\n`);
|
|
148
|
+
// Show fix suggestion for warnings/errors (not for success/info)
|
|
149
|
+
if (check.fix && (check.status === 'warning' || check.status === 'error')) {
|
|
150
|
+
process.stdout.write(chalk_1.default.dim(` \u2192 ${check.fix}\n`));
|
|
151
|
+
}
|
|
152
|
+
// Show details in verbose mode
|
|
153
|
+
if (verbose && check.details) {
|
|
154
|
+
for (const [key, value] of Object.entries(check.details)) {
|
|
155
|
+
const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
|
|
156
|
+
process.stdout.write(chalk_1.default.dim(` ${key}: ${displayValue}\n`));
|
|
157
|
+
}
|
|
80
158
|
}
|
|
81
159
|
}
|
|
82
160
|
}
|
|
@@ -41,18 +41,9 @@ async function runAllChecks() {
|
|
|
41
41
|
message: 'Skipped (gateway unreachable)',
|
|
42
42
|
details: { skipped: true, reason: 'gateway unreachable' },
|
|
43
43
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
status: 'warning',
|
|
48
|
-
message: 'Skipped (gateway unreachable)',
|
|
49
|
-
details: { skipped: true, reason: 'gateway unreachable' },
|
|
50
|
-
});
|
|
51
|
-
// Still check local LLM env vars since they don't require network
|
|
52
|
-
const localLlmCheck = (await (0, llm_1.runLlmChecks)()).find((r) => r.name === 'local_llm_env');
|
|
53
|
-
if (localLlmCheck) {
|
|
54
|
-
results.push(localLlmCheck);
|
|
55
|
-
}
|
|
44
|
+
// LLM checks with server status unknown — local env vars still checked
|
|
45
|
+
const llmResults = await (0, llm_1.runLlmChecks)({ skipServer: true });
|
|
46
|
+
results.push(...llmResults);
|
|
56
47
|
}
|
|
57
48
|
return results;
|
|
58
49
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight CLI update notifier.
|
|
4
|
+
*
|
|
5
|
+
* Design goals:
|
|
6
|
+
* 1. NEVER crash or block the CLI — every call is wrapped in try/catch
|
|
7
|
+
* 2. Zero npm dependencies — uses only Node built-ins (https, fs, path, os)
|
|
8
|
+
* 3. Works on Node 14+ — avoids fetch(), AbortController, and other modern APIs
|
|
9
|
+
* 4. Non-blocking — the HTTP check uses req.unref() so it won't prevent process exit
|
|
10
|
+
* 5. Cached — checks npm registry at most once per 24 hours
|
|
11
|
+
*
|
|
12
|
+
* Flow:
|
|
13
|
+
* - On startup: read ~/.orchagent/update-check.json (sync, ~1ms)
|
|
14
|
+
* - If cache is stale (>24h): fire off a background HTTPS GET to registry, write result
|
|
15
|
+
* - After command completes: if cached version > current version, print one-line notice
|
|
16
|
+
*/
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.checkForUpdates = checkForUpdates;
|
|
22
|
+
exports.printUpdateNotification = printUpdateNotification;
|
|
23
|
+
const https_1 = __importDefault(require("https"));
|
|
24
|
+
const fs_1 = __importDefault(require("fs"));
|
|
25
|
+
const path_1 = __importDefault(require("path"));
|
|
26
|
+
const os_1 = __importDefault(require("os"));
|
|
27
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
28
|
+
const PACKAGE_NAME = '@orchagent/cli';
|
|
29
|
+
const CACHE_DIR = path_1.default.join(os_1.default.homedir(), '.orchagent');
|
|
30
|
+
const CACHE_PATH = path_1.default.join(CACHE_DIR, 'update-check.json');
|
|
31
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
32
|
+
const REQUEST_TIMEOUT_MS = 5000;
|
|
33
|
+
// ── Cache I/O ──────────────────────────────────────────────────────────
|
|
34
|
+
function readCache() {
|
|
35
|
+
try {
|
|
36
|
+
const raw = fs_1.default.readFileSync(CACHE_PATH, 'utf-8');
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
if (parsed && typeof parsed.latest === 'string' && typeof parsed.checkedAt === 'number') {
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function writeCache(latest) {
|
|
48
|
+
try {
|
|
49
|
+
fs_1.default.mkdirSync(CACHE_DIR, { recursive: true });
|
|
50
|
+
fs_1.default.writeFileSync(CACHE_PATH, JSON.stringify({ latest, checkedAt: Date.now() }));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Best-effort — silently ignore write failures
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function isCacheStale(cache) {
|
|
57
|
+
if (!cache)
|
|
58
|
+
return true;
|
|
59
|
+
return Date.now() - cache.checkedAt > CHECK_INTERVAL_MS;
|
|
60
|
+
}
|
|
61
|
+
// ── Version comparison ─────────────────────────────────────────────────
|
|
62
|
+
function isNewer(latest, current) {
|
|
63
|
+
const a = latest.split('.').map(Number);
|
|
64
|
+
const b = current.split('.').map(Number);
|
|
65
|
+
for (let i = 0; i < 3; i++) {
|
|
66
|
+
if ((a[i] || 0) > (b[i] || 0))
|
|
67
|
+
return true;
|
|
68
|
+
if ((a[i] || 0) < (b[i] || 0))
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// ── Background check ───────────────────────────────────────────────────
|
|
74
|
+
function triggerBackgroundCheck() {
|
|
75
|
+
try {
|
|
76
|
+
const url = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
|
|
77
|
+
const req = https_1.default.get(url, { timeout: REQUEST_TIMEOUT_MS }, (res) => {
|
|
78
|
+
if (res.statusCode !== 200) {
|
|
79
|
+
res.resume(); // drain
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
let data = '';
|
|
83
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
84
|
+
res.on('end', () => {
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(data);
|
|
87
|
+
if (typeof parsed.latest === 'string') {
|
|
88
|
+
writeCache(parsed.latest);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Malformed JSON — ignore
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
req.on('error', () => { });
|
|
97
|
+
req.on('timeout', () => { req.destroy(); });
|
|
98
|
+
// Don't keep the process alive waiting for this response
|
|
99
|
+
req.on('socket', (socket) => { socket.unref(); });
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Spawn/setup failure — ignore
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── Public API ──────────────────────────────────────────────────────────
|
|
106
|
+
/** State captured at startup; used later by printUpdateNotification(). */
|
|
107
|
+
let cachedLatest = null;
|
|
108
|
+
/**
|
|
109
|
+
* Call once at CLI startup. Reads the cache (sync, fast) and fires off
|
|
110
|
+
* a background registry check if the cache is stale. Never throws.
|
|
111
|
+
*/
|
|
112
|
+
function checkForUpdates() {
|
|
113
|
+
try {
|
|
114
|
+
// Respect opt-out
|
|
115
|
+
if (process.env.NO_UPDATE_NOTIFIER)
|
|
116
|
+
return;
|
|
117
|
+
const cache = readCache();
|
|
118
|
+
if (cache) {
|
|
119
|
+
cachedLatest = cache.latest;
|
|
120
|
+
}
|
|
121
|
+
if (isCacheStale(cache)) {
|
|
122
|
+
triggerBackgroundCheck();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Absolutely never crash
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Call after the command finishes. Prints a one-line update notice to
|
|
131
|
+
* stderr if a newer version is available. Never throws.
|
|
132
|
+
*/
|
|
133
|
+
function printUpdateNotification() {
|
|
134
|
+
try {
|
|
135
|
+
if (!cachedLatest)
|
|
136
|
+
return;
|
|
137
|
+
const current = package_json_1.default.version;
|
|
138
|
+
if (isNewer(cachedLatest, current)) {
|
|
139
|
+
process.stderr.write(`\nUpdate available: v${current} → v${cachedLatest}\n` +
|
|
140
|
+
`Run \`npm update -g @orchagent/cli\` to update\n`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Absolutely never crash
|
|
145
|
+
}
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.registerLlmConfigCommand = registerLlmConfigCommand;
|
|
4
|
-
const config_1 = require("../lib/config");
|
|
5
|
-
const api_1 = require("../lib/api");
|
|
6
|
-
const errors_1 = require("../lib/errors");
|
|
7
|
-
function registerLlmConfigCommand(program) {
|
|
8
|
-
program
|
|
9
|
-
.command('llm-config')
|
|
10
|
-
.description('Set default LLM configuration for prompt-based agents')
|
|
11
|
-
.option('--endpoint <url>', 'LLM API base URL (e.g., https://api.openai.com/v1)')
|
|
12
|
-
.option('--model <model>', 'Default model name (e.g., gpt-4.1-mini)')
|
|
13
|
-
.option('--api-key <key>', 'LLM API key')
|
|
14
|
-
.option('--clear', 'Clear the stored default LLM config')
|
|
15
|
-
.action(async (options) => {
|
|
16
|
-
const config = await (0, config_1.getResolvedConfig)();
|
|
17
|
-
if (!config.apiKey) {
|
|
18
|
-
throw new errors_1.CliError('Missing API key. Run `orchagent login` first.');
|
|
19
|
-
}
|
|
20
|
-
if (options.clear) {
|
|
21
|
-
await (0, api_1.updateOrg)(config, { default_llm_config: null });
|
|
22
|
-
process.stdout.write('Cleared default LLM config.\n');
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const endpoint = options.endpoint?.trim();
|
|
26
|
-
const model = options.model?.trim();
|
|
27
|
-
const apiKey = options.apiKey?.trim();
|
|
28
|
-
if (!endpoint || !model || !apiKey) {
|
|
29
|
-
throw new errors_1.CliError('Missing required flags. Use --endpoint, --model, and --api-key (or --clear).');
|
|
30
|
-
}
|
|
31
|
-
await (0, api_1.updateOrg)(config, {
|
|
32
|
-
default_llm_config: {
|
|
33
|
-
endpoint,
|
|
34
|
-
model,
|
|
35
|
-
api_key: apiKey,
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
process.stdout.write('Saved default LLM config.\n');
|
|
39
|
-
});
|
|
40
|
-
}
|