@nexus-cortex/cli 4.33.0 → 4.34.1
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/bin/cortex.js +11 -368
- package/dist/commands/config/init.d.ts +4 -0
- package/dist/commands/config/init.d.ts.map +1 -0
- package/dist/commands/config/init.js +44 -0
- package/dist/commands/config/init.js.map +1 -0
- package/dist/commands/config/set.d.ts.map +1 -1
- package/dist/commands/config/set.js +7 -4
- package/dist/commands/config/set.js.map +1 -1
- package/dist/commands/uninstall.d.ts +5 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +64 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +21 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.js +40 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/updateCheck.d.ts +13 -0
- package/dist/lifecycle/updateCheck.d.ts.map +1 -0
- package/dist/lifecycle/updateCheck.js +166 -0
- package/dist/lifecycle/updateCheck.js.map +1 -0
- package/package.json +3 -4
- package/docs/architecture.md +0 -203
- package/docs/assets/nexus-cortex-hero.svg +0 -140
- package/docs/authentication.md +0 -95
- package/docs/configuration.md +0 -185
- package/docs/user-guide.md +0 -291
package/bin/cortex.js
CHANGED
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
import { spawn, spawnSync } from 'child_process';
|
|
26
26
|
import { fileURLToPath } from 'url';
|
|
27
27
|
import { dirname, join, resolve } from 'path';
|
|
28
|
-
import { existsSync, readFileSync, realpathSync
|
|
29
|
-
import { homedir } from 'os';
|
|
28
|
+
import { existsSync, readFileSync, realpathSync } from 'fs';
|
|
30
29
|
import { createRequire } from 'module';
|
|
31
30
|
|
|
32
31
|
const __cortex_require = createRequire(import.meta.url);
|
|
@@ -62,65 +61,6 @@ if (!process.env.CORTEX_ROOT) {
|
|
|
62
61
|
const BASE_PORT = process.env.PORT || '4000';
|
|
63
62
|
const BASE_URL = process.env.CORTEX_URL || `http://localhost:${BASE_PORT}`;
|
|
64
63
|
|
|
65
|
-
// ── Self-update (transparent) ─────────────────────────────────────
|
|
66
|
-
// No silent background install (the old approach failed invisibly and locked itself).
|
|
67
|
-
// Instead: a one-line "update available" notice when a newer release exists (read from
|
|
68
|
-
// a tiny cached version check), and `cortex --update` to update on the spot with visible
|
|
69
|
-
// npm output. Silence the notice with CORTEX_NO_UPDATE_NOTICE=true.
|
|
70
|
-
const UPDATE_CACHE = join(homedir(), '.cortex', '.update-check');
|
|
71
|
-
const PKG_VERSION = (() => {
|
|
72
|
-
try { return JSON.parse(readFileSync(join(__cortex_dirname, '..', 'package.json'), 'utf8')).version; }
|
|
73
|
-
catch { return null; }
|
|
74
|
-
})();
|
|
75
|
-
|
|
76
|
-
function semverGt(a, b) {
|
|
77
|
-
const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0);
|
|
78
|
-
const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0);
|
|
79
|
-
for (let i = 0; i < 3; i++) {
|
|
80
|
-
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
81
|
-
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function fetchLatestVersion() {
|
|
87
|
-
try {
|
|
88
|
-
const https = await import('node:https');
|
|
89
|
-
return await new Promise((resolve) => {
|
|
90
|
-
const req = https.get('https://registry.npmjs.org/nexus-cortex/latest', { timeout: 3000 }, (res) => {
|
|
91
|
-
let body = '';
|
|
92
|
-
res.on('data', (c) => { body += c; });
|
|
93
|
-
res.on('end', () => { try { resolve(JSON.parse(body).version); } catch { resolve(null); } });
|
|
94
|
-
});
|
|
95
|
-
req.on('error', () => resolve(null));
|
|
96
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
97
|
-
});
|
|
98
|
-
} catch { return null; }
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Show the notice from cache (instant, no network); refresh the cache in the background
|
|
102
|
-
// when stale — a tiny registry GET, NEVER an install. Best-effort; never blocks/throws.
|
|
103
|
-
function notifyUpdateAvailable() {
|
|
104
|
-
try {
|
|
105
|
-
if (String(process.env.CORTEX_NO_UPDATE_NOTICE).toLowerCase() === 'true') return;
|
|
106
|
-
if (!PKG_VERSION || !__cortex_dirname.includes('node_modules')) return;
|
|
107
|
-
let cache = {};
|
|
108
|
-
try { cache = JSON.parse(readFileSync(UPDATE_CACHE, 'utf8')); } catch { /* none/legacy — refresh below */ }
|
|
109
|
-
if (cache.latest && semverGt(cache.latest, PKG_VERSION)) {
|
|
110
|
-
process.stderr.write(`\n ↑ Update available: ${PKG_VERSION} → ${cache.latest} · run \`cortex --update\`\n\n`);
|
|
111
|
-
}
|
|
112
|
-
const THROTTLE = 12 * 60 * 60 * 1000;
|
|
113
|
-
if (!cache.lastCheck || Date.now() - cache.lastCheck > THROTTLE) {
|
|
114
|
-
fetchLatestVersion().then((latest) => {
|
|
115
|
-
try {
|
|
116
|
-
mkdirSync(dirname(UPDATE_CACHE), { recursive: true });
|
|
117
|
-
writeFileSync(UPDATE_CACHE, JSON.stringify({ lastCheck: Date.now(), latest: latest || cache.latest || null }));
|
|
118
|
-
} catch { /* ignore */ }
|
|
119
|
-
}).catch(() => {});
|
|
120
|
-
}
|
|
121
|
-
} catch { /* never let the notice affect the CLI */ }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
64
|
let serverProcess = null;
|
|
125
65
|
|
|
126
66
|
// ── Argument parsing ──────────────────────────────────────────────
|
|
@@ -140,129 +80,6 @@ if (args.includes('--version') || args.includes('-v')) {
|
|
|
140
80
|
process.exit(0);
|
|
141
81
|
}
|
|
142
82
|
|
|
143
|
-
// ── Self-update command ───────────────────────────────────────────
|
|
144
|
-
// `cortex --update` — update the global install on the spot, with visible npm output.
|
|
145
|
-
if (args.includes('--update')) {
|
|
146
|
-
process.stdout.write(`Updating nexus-cortex (current: ${PKG_VERSION || 'unknown'})…\n\n`);
|
|
147
|
-
const res = spawnSync('npm', ['install', '-g', 'nexus-cortex@latest'], { stdio: 'inherit' });
|
|
148
|
-
if (res.status === 0) {
|
|
149
|
-
try { mkdirSync(dirname(UPDATE_CACHE), { recursive: true }); writeFileSync(UPDATE_CACHE, JSON.stringify({ lastCheck: Date.now(), latest: null })); } catch { /* ignore */ }
|
|
150
|
-
process.stdout.write('\n✓ Updated. Run `cortex --version` to confirm.\n');
|
|
151
|
-
process.exit(0);
|
|
152
|
-
}
|
|
153
|
-
process.stderr.write('\nUpdate failed — see the npm output above. If it is a permissions error, try:\n sudo npm install -g nexus-cortex@latest\n');
|
|
154
|
-
process.exit(res.status || 1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// `cortex --uninstall` — remove the global install. Config/keys at ~/.cortex are LEFT
|
|
158
|
-
// in place (so you don't lose your API key by accident); the message says how to remove them.
|
|
159
|
-
if (args.includes('--uninstall')) {
|
|
160
|
-
process.stdout.write('Uninstalling nexus-cortex…\n\n');
|
|
161
|
-
const res = spawnSync('npm', ['uninstall', '-g', 'nexus-cortex'], { stdio: 'inherit' });
|
|
162
|
-
if (res.status === 0) {
|
|
163
|
-
process.stdout.write('\n✓ Uninstalled. Your config and API key remain at ~/.cortex — to remove those too:\n rm -rf ~/.cortex\n');
|
|
164
|
-
process.exit(0);
|
|
165
|
-
}
|
|
166
|
-
process.stderr.write('\nUninstall failed — see the npm output above. If it is a permissions error, try:\n sudo npm uninstall -g nexus-cortex\n');
|
|
167
|
-
process.exit(res.status || 1);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Update-available notice (skip for machine-readable / quiet output).
|
|
171
|
-
if (!args.includes('--json') && !args.includes('--quiet') && !args.includes('-q')) {
|
|
172
|
-
notifyUpdateAvailable();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ── Setup wizard (interactive API-key onboarding) ─────────────────
|
|
176
|
-
// Global config lives at ~/.cortex/.env so a global npm install works from ANY
|
|
177
|
-
// folder (the server loads it). Runs on `cortex config init` and on first run
|
|
178
|
-
// when no key is configured anywhere.
|
|
179
|
-
const PROVIDERS = [
|
|
180
|
-
{ name: 'Anthropic (Claude)', keyVar: 'ANTHROPIC_API_KEY', model: 'claude-sonnet-4-6', hint: 'sk-ant-…' },
|
|
181
|
-
{ name: 'OpenAI (GPT)', keyVar: 'OPENAI_API_KEY', model: 'gpt-5-mini', hint: 'sk-…' },
|
|
182
|
-
{ name: 'Google (Gemini)', keyVar: 'GEMINI_API_KEY', model: 'gemini-2.5-flash', hint: 'AIza…' },
|
|
183
|
-
{ name: 'DeepSeek', keyVar: 'DEEPSEEK_API_KEY', model: 'deepseek-v4-pro', hint: 'sk-…' },
|
|
184
|
-
{ name: 'xAI (Grok)', keyVar: 'XAI_API_KEY', model: 'grok-4.3', hint: 'xai-…' },
|
|
185
|
-
];
|
|
186
|
-
const KEY_VARS = [...PROVIDERS.map((p) => p.keyVar), 'GOOGLE_API_KEY'];
|
|
187
|
-
const GLOBAL_ENV = join(homedir(), '.cortex', '.env');
|
|
188
|
-
|
|
189
|
-
function hasApiKey() {
|
|
190
|
-
if (KEY_VARS.some((k) => (process.env[k] || '').trim())) return true;
|
|
191
|
-
for (const f of [GLOBAL_ENV, join(process.cwd(), '.env')]) {
|
|
192
|
-
try {
|
|
193
|
-
const txt = readFileSync(f, 'utf8');
|
|
194
|
-
if (KEY_VARS.some((k) => new RegExp('^' + k + '=\\S', 'm').test(txt))) return true;
|
|
195
|
-
} catch { /* file absent */ }
|
|
196
|
-
}
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function writeGlobalEnv(kv) {
|
|
201
|
-
mkdirSync(dirname(GLOBAL_ENV), { recursive: true });
|
|
202
|
-
let lines = [];
|
|
203
|
-
try { lines = readFileSync(GLOBAL_ENV, 'utf8').split('\n').filter((l) => l.length); } catch { /* new file */ }
|
|
204
|
-
for (const [k, v] of Object.entries(kv)) {
|
|
205
|
-
const i = lines.findIndex((l) => l.startsWith(k + '='));
|
|
206
|
-
if (i >= 0) lines[i] = `${k}=${v}`; else lines.push(`${k}=${v}`);
|
|
207
|
-
}
|
|
208
|
-
writeFileSync(GLOBAL_ENV, lines.join('\n') + '\n', { mode: 0o600 }); // user-only — holds a secret
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function runSetupWizard() {
|
|
212
|
-
if (!process.stdin.isTTY) {
|
|
213
|
-
process.stderr.write(
|
|
214
|
-
'\n No API key configured. Run `cortex config init` for interactive setup, or set one:\n' +
|
|
215
|
-
' export ANTHROPIC_API_KEY=sk-ant-…\n' +
|
|
216
|
-
' export DEFAULT_MODEL_ID=claude-sonnet-4-6\n\n',
|
|
217
|
-
);
|
|
218
|
-
process.exit(1);
|
|
219
|
-
}
|
|
220
|
-
const { createInterface } = await import('node:readline/promises');
|
|
221
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
222
|
-
try {
|
|
223
|
-
process.stdout.write('\n Welcome to Nexus Cortex — quick setup (~30s).\n\n Which AI provider?\n');
|
|
224
|
-
PROVIDERS.forEach((p, i) => process.stdout.write(` ${i + 1}) ${p.name}\n`));
|
|
225
|
-
let choice;
|
|
226
|
-
for (;;) {
|
|
227
|
-
const n = parseInt((await rl.question(`\n Choose 1-${PROVIDERS.length}: `)).trim(), 10);
|
|
228
|
-
if (n >= 1 && n <= PROVIDERS.length) { choice = PROVIDERS[n - 1]; break; }
|
|
229
|
-
process.stdout.write(` Please enter a number 1-${PROVIDERS.length}.\n`);
|
|
230
|
-
}
|
|
231
|
-
let key = '';
|
|
232
|
-
while (!key) {
|
|
233
|
-
key = (await rl.question(`\n Paste your ${choice.name} API key (${choice.hint}): `)).trim();
|
|
234
|
-
if (!key) process.stdout.write(' An API key is required.\n');
|
|
235
|
-
}
|
|
236
|
-
const model = (await rl.question(`\n Default model [${choice.model}]: `)).trim() || choice.model;
|
|
237
|
-
writeGlobalEnv({ [choice.keyVar]: key, DEFAULT_MODEL_ID: model });
|
|
238
|
-
process.stdout.write(`\n ✓ Saved to ${GLOBAL_ENV}\n You're set — try: cortex "what is 2 + 2?"\n\n`);
|
|
239
|
-
} catch {
|
|
240
|
-
// Ctrl-C / Ctrl-D / closed input — exit cleanly instead of dumping a stack trace.
|
|
241
|
-
process.stdout.write('\n Setup cancelled. Run `cortex config init` any time to finish.\n');
|
|
242
|
-
rl.close();
|
|
243
|
-
process.exit(1);
|
|
244
|
-
} finally {
|
|
245
|
-
rl.close();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// `cortex config init` — interactive setup (intercept before the Commander handoff).
|
|
250
|
-
if (args[0] === 'config' && args[1] === 'init') {
|
|
251
|
-
await runSetupWizard();
|
|
252
|
-
process.exit(0);
|
|
253
|
-
}
|
|
254
|
-
// `cortex config` (no subcommand) — interactive config panel. `config get/set/...` still
|
|
255
|
-
// delegate to the Commander CLI below.
|
|
256
|
-
if (args[0] === 'config' && !args[1]) {
|
|
257
|
-
await runConfigPanel();
|
|
258
|
-
process.exit(0);
|
|
259
|
-
}
|
|
260
|
-
// `cortex docs [name]` — print the docs (the same ones the agent can read).
|
|
261
|
-
if (args[0] === 'docs') {
|
|
262
|
-
await showDocs(args.slice(1).join(' '));
|
|
263
|
-
process.exit(0);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
83
|
// ── Headless Commander delegation ─────────────────────────────────
|
|
267
84
|
// `cortex` is primarily the HTTP chat/PR client; the headless Commander program
|
|
268
85
|
// (autoresearch, models, message, mcp, config, …) lives in dist/index.js. If the
|
|
@@ -272,6 +89,7 @@ if (args[0] === 'docs') {
|
|
|
272
89
|
const COMMANDER_SUBCOMMANDS = new Set([
|
|
273
90
|
'autoresearch', 'models', 'message', 'mcp', 'config', 'permissions', 'context',
|
|
274
91
|
'tools', 'middleware', 'artifact', 'cache', 'system-messages', 'tmux',
|
|
92
|
+
'update', 'uninstall',
|
|
275
93
|
]);
|
|
276
94
|
const __firstPositional = args.find((a) => !a.startsWith('-'));
|
|
277
95
|
if (__firstPositional && COMMANDER_SUBCOMMANDS.has(__firstPositional)) {
|
|
@@ -360,10 +178,7 @@ if (showHelp) {
|
|
|
360
178
|
cortex — Natural language interface to the Nexus Cortex library
|
|
361
179
|
|
|
362
180
|
USAGE:
|
|
363
|
-
cortex Interactive chat (slash commands: /help /config /docs /model)
|
|
364
181
|
cortex "your prompt here" Send a message (continues current session)
|
|
365
|
-
cortex config Interactive config panel (keys, model, settings)
|
|
366
|
-
cortex docs [name] Print the docs (list, or e.g. cortex docs authentication)
|
|
367
182
|
cortex agent "<task>" Autonomous one-shot agent run (see below)
|
|
368
183
|
cortex --new "start fresh" Start new session, then send message
|
|
369
184
|
cortex --model MODEL_ID "prompt" Use a specific model
|
|
@@ -396,9 +211,6 @@ FLAGS:
|
|
|
396
211
|
--timeout MS Request timeout in ms (default: 600000 = 10 min)
|
|
397
212
|
--idle-timeout SECS Auto-shutdown server after N seconds of inactivity
|
|
398
213
|
--cwd DIR (agent) Run in DIR — the agent's file tools operate there
|
|
399
|
-
--update Update nexus-cortex to the latest version (npm i -g)
|
|
400
|
-
(a notice appears when you're behind; CORTEX_NO_UPDATE_NOTICE=true to silence)
|
|
401
|
-
--uninstall Remove the global nexus-cortex install (keeps ~/.cortex config)
|
|
402
214
|
--shutdown Stop the running server and exit
|
|
403
215
|
--tmux List active tmux sessions with dashboard URLs
|
|
404
216
|
--stats Show current session statistics
|
|
@@ -538,168 +350,6 @@ async function fetchJSON(path, options = {}, timeoutMs = 30000) {
|
|
|
538
350
|
|
|
539
351
|
// ── Commands ──────────────────────────────────────────────────────
|
|
540
352
|
|
|
541
|
-
// Interactive chat REPL — `cortex` with no message drops you here so you can talk
|
|
542
|
-
// line-by-line without the shell mangling ?/*/quotes. The server keeps the session,
|
|
543
|
-
// so it's multi-turn. Reuses the same /v1/messages send as the one-shot path.
|
|
544
|
-
// ── Config panel + docs (shared by /config & /docs and `cortex config`/`cortex docs`) ──
|
|
545
|
-
// Docs ship alongside the install; dev runs from the monorepo. CORTEX_ROOT covers the
|
|
546
|
-
// npm-install case (set to the package root that holds the .cortex scaffold + docs).
|
|
547
|
-
function resolveDocsDir() {
|
|
548
|
-
for (const d of [join(MONOREPO_ROOT, 'docs'), join(CLI_PKG_ROOT, 'docs'), join(process.env.CORTEX_ROOT || '.', 'docs')]) {
|
|
549
|
-
try { if (existsSync(d)) return d; } catch { /* ignore */ }
|
|
550
|
-
}
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
function readGlobalEnvMap() {
|
|
555
|
-
const m = {};
|
|
556
|
-
try {
|
|
557
|
-
for (const l of readFileSync(GLOBAL_ENV, 'utf8').split('\n')) {
|
|
558
|
-
if (!l || l.startsWith('#')) continue;
|
|
559
|
-
const i = l.indexOf('=');
|
|
560
|
-
if (i > 0) m[l.slice(0, i).trim()] = l.slice(i + 1).trim();
|
|
561
|
-
}
|
|
562
|
-
} catch { /* none yet */ }
|
|
563
|
-
return m;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function maskSecret(v) { return !v ? '' : (v.length <= 10 ? '••••' : `${v.slice(0, 6)}…${v.slice(-4)}`); }
|
|
567
|
-
|
|
568
|
-
async function showDocs(name) {
|
|
569
|
-
const dir = resolveDocsDir();
|
|
570
|
-
if (!dir) { process.stdout.write(' No docs found in this install.\n'); return; }
|
|
571
|
-
const { readdirSync } = await import('node:fs');
|
|
572
|
-
let files = [];
|
|
573
|
-
try { files = readdirSync(dir).filter((f) => f.endsWith('.md')); } catch { /* ignore */ }
|
|
574
|
-
if (!name) {
|
|
575
|
-
process.stdout.write(`\n Docs (${dir}):\n`);
|
|
576
|
-
files.forEach((f) => process.stdout.write(` - ${f.replace(/\.md$/, '')}\n`));
|
|
577
|
-
process.stdout.write('\n Read one: /docs <name> (e.g. /docs authentication)\n\n');
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
const match = files.find((f) => f.toLowerCase().includes(name.toLowerCase()));
|
|
581
|
-
if (!match) { process.stdout.write(` No doc matching "${name}". Available: ${files.map((f) => f.replace(/\.md$/, '')).join(', ')}\n`); return; }
|
|
582
|
-
try { process.stdout.write('\n' + readFileSync(join(dir, match), 'utf8') + '\n'); }
|
|
583
|
-
catch (e) { process.stdout.write(` Could not read ${match}: ${e.message}\n`); }
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Interactive config panel. Reuses the caller's readline (from chat) or creates its own
|
|
587
|
-
// (from the `cortex config` shell command). Writes ~/.cortex/.env.
|
|
588
|
-
async function runConfigPanel(existingRl) {
|
|
589
|
-
let rl = existingRl;
|
|
590
|
-
if (!rl) {
|
|
591
|
-
if (!process.stdin.isTTY) { process.stderr.write(' `cortex config` needs an interactive terminal. Use `cortex config set <key> <value>` non-interactively.\n'); process.exit(1); }
|
|
592
|
-
const { createInterface } = await import('node:readline/promises');
|
|
593
|
-
rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
594
|
-
}
|
|
595
|
-
try {
|
|
596
|
-
for (;;) {
|
|
597
|
-
const env = readGlobalEnvMap();
|
|
598
|
-
process.stdout.write(`\n Nexus Cortex config — ${GLOBAL_ENV}\n\n`);
|
|
599
|
-
for (const p of PROVIDERS) {
|
|
600
|
-
const v = env[p.keyVar] || process.env[p.keyVar];
|
|
601
|
-
process.stdout.write(` ${p.keyVar.padEnd(20)} ${v ? '✓ ' + maskSecret(v) : '— not set'}\n`);
|
|
602
|
-
}
|
|
603
|
-
const model = env.DEFAULT_MODEL_ID || process.env.DEFAULT_MODEL_ID;
|
|
604
|
-
process.stdout.write(` ${'DEFAULT_MODEL_ID'.padEnd(20)} ${model || '— not set'}\n`);
|
|
605
|
-
process.stdout.write('\n 1) set / change an API key\n 2) change the default model\n 3) set any variable (KEY=value)\n 4) view docs\n q) done\n');
|
|
606
|
-
const c = (await rl.question('\n > ')).trim().toLowerCase();
|
|
607
|
-
if (c === 'q' || c === 'done' || c === 'quit' || c === '') break;
|
|
608
|
-
if (c === '1') {
|
|
609
|
-
PROVIDERS.forEach((p, i) => process.stdout.write(` ${i + 1}) ${p.name}\n`));
|
|
610
|
-
const p = PROVIDERS[parseInt((await rl.question(` provider 1-${PROVIDERS.length}: `)).trim(), 10) - 1];
|
|
611
|
-
if (!p) { process.stdout.write(' cancelled.\n'); continue; }
|
|
612
|
-
const key = (await rl.question(` ${p.name} key (${p.hint}): `)).trim();
|
|
613
|
-
if (key) {
|
|
614
|
-
const set = { [p.keyVar]: key };
|
|
615
|
-
if (!env.DEFAULT_MODEL_ID && !process.env.DEFAULT_MODEL_ID) set.DEFAULT_MODEL_ID = p.model;
|
|
616
|
-
writeGlobalEnv(set);
|
|
617
|
-
process.stdout.write(` ✓ saved ${p.keyVar}${set.DEFAULT_MODEL_ID ? ` + DEFAULT_MODEL_ID=${set.DEFAULT_MODEL_ID}` : ''}\n`);
|
|
618
|
-
}
|
|
619
|
-
} else if (c === '2') {
|
|
620
|
-
const m = (await rl.question(' default model id (e.g. claude-sonnet-4-6): ')).trim();
|
|
621
|
-
if (m) { writeGlobalEnv({ DEFAULT_MODEL_ID: m }); process.stdout.write(` ✓ DEFAULT_MODEL_ID=${m}\n`); }
|
|
622
|
-
} else if (c === '3') {
|
|
623
|
-
const kv = (await rl.question(' KEY=value: ')).trim();
|
|
624
|
-
const i = kv.indexOf('=');
|
|
625
|
-
if (i > 0) { writeGlobalEnv({ [kv.slice(0, i).trim()]: kv.slice(i + 1).trim() }); process.stdout.write(' ✓ saved\n'); }
|
|
626
|
-
else process.stdout.write(' expected KEY=value\n');
|
|
627
|
-
} else if (c === '4') {
|
|
628
|
-
await showDocs();
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
} finally {
|
|
632
|
-
if (!existingRl) rl.close();
|
|
633
|
-
}
|
|
634
|
-
process.stdout.write(' Config saved to ~/.cortex/.env. New keys take effect on the next server start (`cortex --shutdown`, then run again).\n');
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function printSlashHelp() {
|
|
638
|
-
process.stdout.write(
|
|
639
|
-
'\n Slash commands (anything without a leading / is sent to the model):\n' +
|
|
640
|
-
' /help this list\n' +
|
|
641
|
-
' /config view & change API keys, model, settings\n' +
|
|
642
|
-
' /docs [name] list docs, or print one (e.g. /docs authentication)\n' +
|
|
643
|
-
' /model [id] show or switch the model for this session\n' +
|
|
644
|
-
' /new start a fresh session\n' +
|
|
645
|
-
' /exit leave chat (or Ctrl-D)\n\n',
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
async function runInteractiveChat() {
|
|
650
|
-
if (newSession) {
|
|
651
|
-
try { await fetchJSON('/sessions/new', { method: 'POST' }); } catch { /* fresh session is best-effort */ }
|
|
652
|
-
}
|
|
653
|
-
let currentModel = modelId;
|
|
654
|
-
const { createInterface } = await import('node:readline/promises');
|
|
655
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
656
|
-
process.stdout.write(
|
|
657
|
-
'\n Nexus Cortex — interactive chat. Type a message and press Enter.\n' +
|
|
658
|
-
' Slash commands: /help /config /docs /model /new /exit · Ctrl-D to quit.\n\n',
|
|
659
|
-
);
|
|
660
|
-
try {
|
|
661
|
-
for (;;) {
|
|
662
|
-
let line;
|
|
663
|
-
try { line = (await rl.question('cortex> ')).trim(); }
|
|
664
|
-
catch { break; } // Ctrl-D / closed input
|
|
665
|
-
if (!line) continue;
|
|
666
|
-
if (line.startsWith('/')) {
|
|
667
|
-
const parts = line.slice(1).split(/\s+/);
|
|
668
|
-
const cmd = (parts.shift() || '').toLowerCase();
|
|
669
|
-
const arg = parts.join(' ');
|
|
670
|
-
if (cmd === 'exit' || cmd === 'quit' || cmd === 'q') break;
|
|
671
|
-
if (cmd === 'help' || cmd === '?') { printSlashHelp(); continue; }
|
|
672
|
-
if (cmd === 'config') { await runConfigPanel(rl); continue; }
|
|
673
|
-
if (cmd === 'docs') { await showDocs(arg); continue; }
|
|
674
|
-
if (cmd === 'new') {
|
|
675
|
-
try { await fetchJSON('/sessions/new', { method: 'POST' }); process.stdout.write(' ✓ new session\n'); }
|
|
676
|
-
catch (e) { process.stderr.write(` [error] ${e.message}\n`); }
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
|
-
if (cmd === 'model') {
|
|
680
|
-
if (arg) { currentModel = arg; process.stdout.write(` ✓ model → ${arg}\n`); }
|
|
681
|
-
else process.stdout.write(` model: ${currentModel || '(server default)'}\n`);
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
process.stdout.write(` unknown command: /${cmd} — type /help\n`);
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
const payload = { messages: [{ role: 'user', content: line }] };
|
|
688
|
-
if (currentModel) payload.model = currentModel;
|
|
689
|
-
try {
|
|
690
|
-
const data = await fetchJSON('/v1/messages', { method: 'POST', body: JSON.stringify(payload) }, messageTimeoutMs);
|
|
691
|
-
const text = (data.content || []).filter((b) => b.type === 'text').map((b) => b.text).join('\n');
|
|
692
|
-
process.stdout.write('\n' + (text || '(no text response)') + '\n\n');
|
|
693
|
-
} catch (err) {
|
|
694
|
-
process.stderr.write(`\n [error] ${err.message}\n\n`);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
} finally {
|
|
698
|
-
rl.close();
|
|
699
|
-
}
|
|
700
|
-
process.stdout.write(' Bye.\n');
|
|
701
|
-
}
|
|
702
|
-
|
|
703
353
|
async function run() {
|
|
704
354
|
// --shutdown: stop the server and exit
|
|
705
355
|
if (doShutdown) {
|
|
@@ -707,12 +357,13 @@ async function run() {
|
|
|
707
357
|
return;
|
|
708
358
|
}
|
|
709
359
|
|
|
710
|
-
//
|
|
711
|
-
//
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
await
|
|
715
|
-
|
|
360
|
+
// Update check for the direct path (`cortex "<prompt>"`, `cortex agent`). Commander
|
|
361
|
+
// subcommands are delegated earlier and run this same check via the CLI's preAction
|
|
362
|
+
// hook; this covers the non-delegated chat/agent path. Canonical service, best-effort.
|
|
363
|
+
try {
|
|
364
|
+
const { checkForUpdate } = await import('../dist/lifecycle/updateCheck.js');
|
|
365
|
+
await checkForUpdate();
|
|
366
|
+
} catch { /* dist absent in a source checkout, or check failed — never block */ }
|
|
716
367
|
|
|
717
368
|
await ensureServer();
|
|
718
369
|
|
|
@@ -834,17 +485,9 @@ async function run() {
|
|
|
834
485
|
}
|
|
835
486
|
}
|
|
836
487
|
|
|
837
|
-
//
|
|
838
|
-
// into the chat REPL so the shell never mangles ?/*/quotes. agent/run with no task,
|
|
839
|
-
// or any non-TTY (scripted) use, still gets the usage error — unchanged.
|
|
488
|
+
// Need a prompt for message commands
|
|
840
489
|
if (!prompt) {
|
|
841
|
-
|
|
842
|
-
await runInteractiveChat();
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
console.error(__agentMode
|
|
846
|
-
? 'Usage: cortex agent "<task>" (or cortex run "<task>")'
|
|
847
|
-
: 'Usage: cortex "your prompt here"');
|
|
490
|
+
console.error('Usage: cortex "your prompt here"');
|
|
848
491
|
console.error(' cortex --help for more options');
|
|
849
492
|
process.exit(1);
|
|
850
493
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/config/init.ts"],"names":[],"mappings":"AAiBA,wBAAsB,UAAU,CAAC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAiCjF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize the global user config file (~/.cortex/.env).
|
|
3
|
+
*
|
|
4
|
+
* Creates a schema-templated .env in the user's home directory — the canonical,
|
|
5
|
+
* findable, editable config location — so a globally-installed CLI can be configured
|
|
6
|
+
* by opening one file, regardless of where npm placed the binary. With --force it
|
|
7
|
+
* regenerates the template while preserving any values you've already set.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
10
|
+
import { createDefaultEnvFile, updateEnvFile, getGlobalConfigDir, getGlobalEnvPath, } from '@nexus-cortex/core';
|
|
11
|
+
import { ThemeManager } from '../../themes/ThemeManager.js';
|
|
12
|
+
export async function configInit(options = {}) {
|
|
13
|
+
const theme = ThemeManager.getTheme();
|
|
14
|
+
const dir = getGlobalConfigDir();
|
|
15
|
+
const envPath = getGlobalEnvPath();
|
|
16
|
+
try {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
const exists = existsSync(envPath);
|
|
19
|
+
if (exists && !options.force) {
|
|
20
|
+
console.log(theme.colors.warning(`[skip] config already exists: ${envPath}`));
|
|
21
|
+
console.log(theme.colors.muted(' Open it to edit your settings, or run "cortex config set KEY VALUE".'));
|
|
22
|
+
console.log(theme.colors.muted(' Use "cortex config init --force" to refresh the template (your values are preserved).'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (exists) {
|
|
26
|
+
// Refresh template/comments without discarding existing values.
|
|
27
|
+
updateEnvFile(dir, {});
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
createDefaultEnvFile(dir);
|
|
31
|
+
}
|
|
32
|
+
console.log(theme.colors.success(`[OK] ${exists ? 'refreshed' : 'created'} ${envPath}`));
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(theme.colors.muted(' Set your keys one of two ways:'));
|
|
35
|
+
console.log(theme.colors.highlight(` • open ${envPath} and edit it`));
|
|
36
|
+
console.log(theme.colors.highlight(' • or run: cortex config set ANTHROPIC_API_KEY sk-ant-...'));
|
|
37
|
+
console.log();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error(theme.colors.error(`Failed to initialize config: ${error.message}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/config/init.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAA+B,EAAE;IAChE,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAEnC,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iCAAiC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC,CAAC;YAC1G,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,yFAAyF,CAAC,CAAC,CAAC;YAC3H,OAAO;QACT,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,gEAAgE;YAChE,aAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC;QACzF,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,OAAO,cAAc,CAAC,CAAC,CAAC;QACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,8DAA8D,CAAC,CAAC,CAAC;QACpG,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/commands/config/set.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/commands/config/set.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAuDf"}
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* Unified config: writes to .env via SettingsLoader.
|
|
5
5
|
* Validates against SettingsSchema metadata.
|
|
6
6
|
*/
|
|
7
|
-
import { SettingsLoader, SETTINGS_METADATA, validateSetting, isLiveToggleable, } from '@nexus-cortex/core';
|
|
7
|
+
import { SettingsLoader, SETTINGS_METADATA, validateSetting, isLiveToggleable, getGlobalConfigDir, getGlobalEnvPath, } from '@nexus-cortex/core';
|
|
8
8
|
import { ThemeManager } from '../../themes/ThemeManager.js';
|
|
9
|
-
import { findProjectRoot } from './utils.js';
|
|
10
9
|
/**
|
|
11
10
|
* Set configuration value
|
|
12
11
|
* Validates the key and value before saving to .env
|
|
@@ -35,8 +34,11 @@ export async function configSet(key, value) {
|
|
|
35
34
|
process.exit(1);
|
|
36
35
|
return; // guard: do not write an invalid value if exit is mocked
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Write to the global user config (~/.cortex/.env) so the setting applies from
|
|
38
|
+
// any directory and survives package updates — regardless of where npm installed
|
|
39
|
+
// the binary. A project-local ./.env still overrides this when present.
|
|
40
|
+
const writeDir = getGlobalConfigDir();
|
|
41
|
+
const loader = new SettingsLoader(writeDir);
|
|
40
42
|
const result = loader.set(key, value);
|
|
41
43
|
if (!result.success) {
|
|
42
44
|
console.error(theme.colors.error(`Failed to set ${key}: ${result.error}`));
|
|
@@ -50,6 +52,7 @@ export async function configSet(key, value) {
|
|
|
50
52
|
console.log(theme.colors.muted(` was: ${result.previousValue}`));
|
|
51
53
|
}
|
|
52
54
|
console.log(theme.colors.highlight(` now: ${value}`));
|
|
55
|
+
console.log(theme.colors.muted(` saved to: ${getGlobalEnvPath()}`));
|
|
53
56
|
console.log();
|
|
54
57
|
}
|
|
55
58
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/commands/config/set.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/commands/config/set.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"AAyBA,wBAAsB,YAAY,CAAC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA2ClG"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cortex-cli uninstall` — remove the global install.
|
|
3
|
+
*
|
|
4
|
+
* By default your config + API keys at ~/.cortex are LEFT in place (so you don't lose
|
|
5
|
+
* keys by accident); --purge removes them too. Interactive runs confirm first;
|
|
6
|
+
* non-interactive runs require --yes so a stray invocation can't wipe an install.
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from 'child_process';
|
|
9
|
+
import { rmSync, existsSync } from 'fs';
|
|
10
|
+
import { createInterface } from 'readline';
|
|
11
|
+
import { getGlobalConfigDir } from '@nexus-cortex/core';
|
|
12
|
+
import { ThemeManager } from '../themes/ThemeManager.js';
|
|
13
|
+
const PKG_NAME = 'nexus-cortex';
|
|
14
|
+
function askYesNo(question) {
|
|
15
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
rl.question(question, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(/^y(es)?$/i.test(answer.trim()));
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export async function uninstallCli(options = {}) {
|
|
24
|
+
const theme = ThemeManager.getTheme();
|
|
25
|
+
const dir = getGlobalConfigDir();
|
|
26
|
+
if (!options.yes) {
|
|
27
|
+
if (!process.stdin.isTTY) {
|
|
28
|
+
console.error(theme.colors.error('Refusing to uninstall non-interactively without --yes.'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const ok = await askYesNo(theme.colors.warning(`Uninstall nexus-cortex globally${options.purge ? ` and delete ${dir}` : ''}? [y/N] `));
|
|
33
|
+
if (!ok) {
|
|
34
|
+
console.log(theme.colors.muted('Cancelled.'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log(theme.colors.highlight('Uninstalling nexus-cortex…'));
|
|
39
|
+
console.log();
|
|
40
|
+
const res = spawnSync('npm', ['uninstall', '-g', PKG_NAME], { stdio: 'inherit' });
|
|
41
|
+
if (res.status !== 0) {
|
|
42
|
+
console.error(theme.colors.error('\nUninstall failed — see the npm output above.'));
|
|
43
|
+
console.error(theme.colors.muted('If it is a permissions error: sudo npm uninstall -g nexus-cortex'));
|
|
44
|
+
process.exit(res.status || 1);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (options.purge) {
|
|
48
|
+
try {
|
|
49
|
+
if (existsSync(dir)) {
|
|
50
|
+
rmSync(dir, { recursive: true, force: true });
|
|
51
|
+
console.log(theme.colors.muted(`Removed ${dir}`));
|
|
52
|
+
}
|
|
53
|
+
console.log(theme.colors.success('\n[OK] Uninstalled and purged.'));
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error(theme.colors.error(`Uninstalled, but could not remove ${dir}: ${e.message}`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(theme.colors.success('\n[OK] Uninstalled.'));
|
|
61
|
+
console.log(theme.colors.muted(`Your config + API keys remain at ${dir} — to remove them too: rm -rf ${dir}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=uninstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAQA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAc/C"}
|