@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.
@@ -19,8 +19,81 @@ const CATEGORY_NAMES = {
19
19
  configuration: 'Configuration',
20
20
  connectivity: 'Connectivity',
21
21
  authentication: 'Authentication',
22
- llm: 'LLM Configuration',
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
- for (const check of checks) {
69
- const symbol = SYMBOLS[check.status] || SYMBOLS.info;
70
- process.stdout.write(` ${symbol} ${check.message}\n`);
71
- // Show fix suggestion for warnings/errors (not for success/info)
72
- if (check.fix && (check.status === 'warning' || check.status === 'error')) {
73
- process.stdout.write(chalk_1.default.dim(` \u2192 ${check.fix}\n`));
74
- }
75
- // Show details in verbose mode
76
- if (verbose && check.details) {
77
- for (const [key, value] of Object.entries(check.details)) {
78
- const displayValue = typeof value === 'object' ? JSON.stringify(value) : String(value);
79
- process.stdout.write(chalk_1.default.dim(` ${key}: ${displayValue}\n`));
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
- results.push({
45
- category: 'llm',
46
- name: 'server_llm_keys',
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.33",
3
+ "version": "0.3.34",
4
4
  "description": "Command-line interface for the orchagent AI agent marketplace",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",
@@ -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
- }