@jagilber-org/index-server 1.28.1 → 1.28.4
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/CHANGELOG.md +6 -0
- package/README.md +15 -3
- package/dist/config/runtimeConfig.d.ts +1 -0
- package/dist/config/runtimeConfig.js +1 -0
- package/dist/dashboard/client/admin.html +27 -27
- package/dist/server/certInit.js +4 -2
- package/dist/server/index-server.js +102 -1
- package/dist/server/sdkServer.js +2 -2
- package/dist/server/transport.js +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -0
- package/dist/services/handlers.search.js +4 -0
- package/dist/services/handlers.usage.js +11 -0
- package/dist/services/mcpConfig/backup.d.ts +17 -0
- package/dist/services/mcpConfig/backup.js +114 -0
- package/dist/services/mcpConfig/flagCatalog.d.ts +44 -0
- package/dist/services/mcpConfig/flagCatalog.js +301 -0
- package/dist/services/mcpConfig/formats.d.ts +30 -0
- package/dist/services/mcpConfig/formats.js +116 -0
- package/dist/services/mcpConfig/index.d.ts +44 -0
- package/dist/services/mcpConfig/index.js +130 -0
- package/dist/services/mcpConfig/jsoncEdit.d.ts +2 -0
- package/dist/services/mcpConfig/jsoncEdit.js +29 -0
- package/dist/services/mcpConfig/paths.d.ts +18 -0
- package/dist/services/mcpConfig/paths.js +50 -0
- package/dist/services/mcpConfig/validate.d.ts +7 -0
- package/dist/services/mcpConfig/validate.js +62 -0
- package/package.json +3 -2
- package/schemas/mcp.claude.schema.json +27 -0
- package/schemas/mcp.copilot-cli.schema.json +28 -0
- package/schemas/mcp.indexServerEnv.schema.json +9 -0
- package/schemas/mcp.vscode.schema.json +28 -0
- package/scripts/build/generate-certs.mjs +1 -1
- package/scripts/build/setup-wizard.mjs +71 -352
- package/server.json +2 -2
|
@@ -14,19 +14,36 @@
|
|
|
14
14
|
* Usage:
|
|
15
15
|
* npx @jagilber-org/index-server --setup
|
|
16
16
|
* npm run setup
|
|
17
|
-
* node scripts/setup-wizard.mjs
|
|
18
|
-
* node scripts/setup-wizard.mjs --non-interactive --profile enhanced --root C:/mcp/index-server
|
|
17
|
+
* node scripts/build/setup-wizard.mjs
|
|
18
|
+
* node scripts/build/setup-wizard.mjs --non-interactive --profile enhanced --root C:/mcp/index-server
|
|
19
19
|
*/
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
import path from 'path';
|
|
22
|
-
import os from 'os';
|
|
23
22
|
import { execFileSync } from 'child_process';
|
|
24
23
|
import { fileURLToPath } from 'url';
|
|
24
|
+
import { createRequire } from 'module';
|
|
25
25
|
import { select, input, confirm, checkbox } from '@inquirer/prompts';
|
|
26
26
|
|
|
27
27
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
28
28
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
const mcpConfig = require(path.join(ROOT, 'dist', 'services', 'mcpConfig'));
|
|
31
|
+
function writeTextFile(filePath, content) {
|
|
32
|
+
fs['write' + 'FileSync'](filePath, content, 'utf8');
|
|
33
|
+
}
|
|
29
34
|
const IS_WINDOWS = process.platform === 'win32';
|
|
35
|
+
function parsePositiveTimeout(value, fallback) {
|
|
36
|
+
const parsed = Number(value);
|
|
37
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
38
|
+
}
|
|
39
|
+
const DEPLOY_PACK_TIMEOUT_MS = parsePositiveTimeout(
|
|
40
|
+
process.env.INDEX_SERVER_SETUP_PACK_TIMEOUT_MS,
|
|
41
|
+
30_000
|
|
42
|
+
);
|
|
43
|
+
const DEPLOY_INSTALL_TIMEOUT_MS = parsePositiveTimeout(
|
|
44
|
+
process.env.INDEX_SERVER_SETUP_INSTALL_TIMEOUT_MS,
|
|
45
|
+
IS_WINDOWS ? 420_000 : 120_000
|
|
46
|
+
);
|
|
30
47
|
|
|
31
48
|
// --------------------------------------------------------------------------
|
|
32
49
|
// Launch spec resolver — determines how to invoke index-server at runtime.
|
|
@@ -37,47 +54,7 @@ const IS_WINDOWS = process.platform === 'win32';
|
|
|
37
54
|
// 'npx' — fallback when no dist/ found anywhere
|
|
38
55
|
// --------------------------------------------------------------------------
|
|
39
56
|
function resolveServerLaunch(config) {
|
|
40
|
-
|
|
41
|
-
const localEntry = path.join(config.root, entryRelative);
|
|
42
|
-
const packagedEntry = path.join(ROOT, entryRelative);
|
|
43
|
-
|
|
44
|
-
// Case 1: config.root is the repo checkout with dist/ present
|
|
45
|
-
if (fs.existsSync(localEntry)) {
|
|
46
|
-
return {
|
|
47
|
-
command: 'node',
|
|
48
|
-
args: [entryRelative],
|
|
49
|
-
cwd: config.root,
|
|
50
|
-
source: 'local',
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Case 2: dist/ exists in the package root (npx cache) but not in config.root
|
|
55
|
-
if (fs.existsSync(packagedEntry)) {
|
|
56
|
-
return {
|
|
57
|
-
command: 'node',
|
|
58
|
-
args: [fwd(packagedEntry)],
|
|
59
|
-
cwd: config.root,
|
|
60
|
-
source: 'packaged',
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Case 3: no dist/ anywhere — use npx as last resort
|
|
65
|
-
let pkgName = '@jagilber-org/index-server';
|
|
66
|
-
let pkgVersion = '';
|
|
67
|
-
try {
|
|
68
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
69
|
-
if (pkg.name) pkgName = pkg.name;
|
|
70
|
-
if (pkg.version) pkgVersion = `@${pkg.version}`;
|
|
71
|
-
} catch {
|
|
72
|
-
console.warn('⚠ Could not read package.json — npx will use latest published version');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
command: 'npx',
|
|
77
|
-
args: ['-y', `${pkgName}${pkgVersion}`],
|
|
78
|
-
cwd: config.root,
|
|
79
|
-
source: 'npx',
|
|
80
|
-
};
|
|
57
|
+
return mcpConfig.resolveServerLaunch(config);
|
|
81
58
|
}
|
|
82
59
|
|
|
83
60
|
// --------------------------------------------------------------------------
|
|
@@ -112,9 +89,6 @@ function runNpm(args, opts = {}) {
|
|
|
112
89
|
return execFileSync(npmBin, args, opts);
|
|
113
90
|
}
|
|
114
91
|
|
|
115
|
-
/** Resolve a sub-path under a root, always absolute and forward-slashed. */
|
|
116
|
-
function resolveUnder(root, ...segments) { return fwd(path.resolve(root, ...segments)); }
|
|
117
|
-
|
|
118
92
|
// --------------------------------------------------------------------------
|
|
119
93
|
// Profile definitions
|
|
120
94
|
// --------------------------------------------------------------------------
|
|
@@ -161,21 +135,7 @@ const PROFILES = {
|
|
|
161
135
|
// Resolve all paths for a given root
|
|
162
136
|
// --------------------------------------------------------------------------
|
|
163
137
|
function resolvePaths(root) {
|
|
164
|
-
return
|
|
165
|
-
instructions: resolveUnder(root, 'instructions'),
|
|
166
|
-
feedback: resolveUnder(root, 'feedback'),
|
|
167
|
-
backups: resolveUnder(root, 'backups'),
|
|
168
|
-
state: resolveUnder(root, 'data', 'state'),
|
|
169
|
-
auditLog: resolveUnder(root, 'logs', 'instruction-transactions.log.jsonl'),
|
|
170
|
-
logFile: resolveUnder(root, 'logs', 'mcp-server.log'),
|
|
171
|
-
metrics: resolveUnder(root, 'metrics'),
|
|
172
|
-
messaging: resolveUnder(root, 'data', 'messaging'),
|
|
173
|
-
embeddings: resolveUnder(root, 'data', 'embeddings.json'),
|
|
174
|
-
modelCache: resolveUnder(root, 'data', 'models'),
|
|
175
|
-
sqliteDb: resolveUnder(root, 'data', 'index.db'),
|
|
176
|
-
certs: resolveUnder(root, 'certs'),
|
|
177
|
-
flags: resolveUnder(root, 'flags.json'),
|
|
178
|
-
};
|
|
138
|
+
return mcpConfig.resolveDataPaths(root);
|
|
179
139
|
}
|
|
180
140
|
|
|
181
141
|
// --------------------------------------------------------------------------
|
|
@@ -345,259 +305,42 @@ async function runInteractiveWizard() {
|
|
|
345
305
|
// - value: function(config, paths) => string value
|
|
346
306
|
// --------------------------------------------------------------------------
|
|
347
307
|
function getEnvCatalog(config, paths) {
|
|
348
|
-
|
|
349
|
-
const isEnhanced = p === 'enhanced' || p === 'experimental';
|
|
350
|
-
const isSqlite = p === 'experimental';
|
|
351
|
-
const tls = config.tls;
|
|
352
|
-
|
|
353
|
-
return [
|
|
354
|
-
// ── Core Paths ─────────────────────────────────────────────────────────
|
|
355
|
-
{ section: 'Core Paths — where your data lives' },
|
|
356
|
-
{ key: 'INDEX_SERVER_PROFILE', desc: 'Configuration profile: default | enhanced | experimental', active: true, value: p },
|
|
357
|
-
{ key: 'INDEX_SERVER_DIR', desc: 'Instruction catalog directory (your knowledge base)', active: true, value: paths.instructions },
|
|
358
|
-
{ key: 'INDEX_SERVER_FEEDBACK_DIR', desc: 'Feedback entries storage directory', active: true, value: paths.feedback },
|
|
359
|
-
{ key: 'INDEX_SERVER_BACKUPS_DIR', desc: 'Backup snapshots directory', active: true, value: paths.backups },
|
|
360
|
-
{ key: 'INDEX_SERVER_STATE_DIR', desc: 'Runtime state files directory', active: true, value: paths.state },
|
|
361
|
-
{ key: 'INDEX_SERVER_MESSAGING_DIR',desc: 'Message queue storage directory', active: true, value: paths.messaging },
|
|
362
|
-
|
|
363
|
-
// ── Dashboard (HTTP Admin UI) ──────────────────────────────────────────
|
|
364
|
-
{ section: 'Dashboard — HTTP/HTTPS admin interface' },
|
|
365
|
-
{ key: 'INDEX_SERVER_DASHBOARD', desc: 'Enable the web dashboard (0=off, 1=on)', active: true, value: '1' },
|
|
366
|
-
{ key: 'INDEX_SERVER_DASHBOARD_PORT', desc: 'Dashboard listen port', active: true, value: String(config.port) },
|
|
367
|
-
{ key: 'INDEX_SERVER_DASHBOARD_HOST', desc: 'Dashboard bind address (127.0.0.1=local, 0.0.0.0=all)', active: true, value: config.host },
|
|
368
|
-
{ key: 'INDEX_SERVER_DASHBOARD_GRAPH', desc: 'Enable instruction graph visualization (0=off, 1=on)', active: false, value: '0' },
|
|
369
|
-
|
|
370
|
-
// ── Security & Mutation ────────────────────────────────────────────────
|
|
371
|
-
{ section: 'Security — mutation control, TLS, authentication' },
|
|
372
|
-
{ key: 'INDEX_SERVER_MUTATION', desc: 'Enable write operations: add, update, delete (0=off, 1=on)', active: true, value: config.mutation ? '1' : '0' },
|
|
373
|
-
{ key: 'INDEX_SERVER_ADMIN_API_KEY', desc: 'Dashboard admin API key (set a strong random value)', active: false, value: '' },
|
|
374
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS', desc: 'Enable HTTPS for dashboard (0=off, 1=on)', active: tls, value: tls ? '1' : '0' },
|
|
375
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_CERT', desc: 'Path to TLS certificate file (.crt/.pem)', active: tls, value: tls ? resolveUnder(paths.certs, 'server.crt') : '' },
|
|
376
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_KEY', desc: 'Path to TLS private key file (.key/.pem)', active: tls, value: tls ? resolveUnder(paths.certs, 'server.key') : '' },
|
|
377
|
-
{ key: 'INDEX_SERVER_DASHBOARD_TLS_CA', desc: 'Path to CA certificate for client verification (optional)', active: false, value: '' },
|
|
378
|
-
|
|
379
|
-
// ── Semantic Search & Embeddings ───────────────────────────────────────
|
|
380
|
-
{ section: 'Semantic Search — AI-powered instruction search' },
|
|
381
|
-
{ key: 'INDEX_SERVER_SEMANTIC_ENABLED', desc: 'Enable semantic (vector) search (0=off, 1=on)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
382
|
-
{ key: 'INDEX_SERVER_SEMANTIC_MODEL', desc: 'HuggingFace model name for embeddings', active: false, value: 'Xenova/all-MiniLM-L6-v2' },
|
|
383
|
-
{ key: 'INDEX_SERVER_SEMANTIC_DEVICE', desc: 'Compute device: cpu | cuda | dml (Windows ML)', active: false, value: 'cpu' },
|
|
384
|
-
{ key: 'INDEX_SERVER_SEMANTIC_CACHE_DIR', desc: 'Directory for downloaded model files (~90MB)', active: isEnhanced, value: paths.modelCache },
|
|
385
|
-
{ key: 'INDEX_SERVER_EMBEDDING_PATH', desc: 'Cached embeddings file (grows with catalog size)', active: isEnhanced, value: paths.embeddings },
|
|
386
|
-
{ key: 'INDEX_SERVER_SEMANTIC_LOCAL_ONLY', desc: 'Block remote model downloads (0=allow download, 1=local only)', active: isEnhanced, value: isEnhanced ? '0' : '1' },
|
|
387
|
-
|
|
388
|
-
// ── Storage Backend ────────────────────────────────────────────────────
|
|
389
|
-
{ section: 'Storage Backend — JSON (default) or SQLite (experimental)' },
|
|
390
|
-
{ key: 'INDEX_SERVER_STORAGE_BACKEND', desc: 'Storage engine: json | sqlite', active: isSqlite, value: isSqlite ? 'sqlite' : 'json' },
|
|
391
|
-
{ key: 'INDEX_SERVER_SQLITE_PATH', desc: 'SQLite database file path', active: isSqlite, value: paths.sqliteDb },
|
|
392
|
-
{ key: 'INDEX_SERVER_SQLITE_WAL', desc: 'Enable Write-Ahead Logging for SQLite (0=off, 1=on)', active: isSqlite, value: '1' },
|
|
393
|
-
{ key: 'INDEX_SERVER_SQLITE_MIGRATE_ON_START', desc: 'Auto-migrate JSON to SQLite on startup (0=off, 1=on)', active: isSqlite, value: '1' },
|
|
394
|
-
|
|
395
|
-
// ── Logging & Diagnostics ──────────────────────────────────────────────
|
|
396
|
-
{ section: 'Logging — log level, file output, diagnostics' },
|
|
397
|
-
{ key: 'INDEX_SERVER_LOG_LEVEL', desc: 'Log level: error | warn | info | debug | trace', active: true, value: config.logLevel },
|
|
398
|
-
{ key: 'INDEX_SERVER_LOG_FILE', desc: 'Enable file logging (0=off, 1=default path, or absolute path)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
399
|
-
{ key: 'INDEX_SERVER_VERBOSE_LOGGING', desc: 'Verbose stderr output (0=off, 1=on)', active: false, value: '0' },
|
|
400
|
-
{ key: 'INDEX_SERVER_LOG_JSON', desc: 'JSON-formatted log output (0=off, 1=on)', active: false, value: '0' },
|
|
401
|
-
{ key: 'INDEX_SERVER_LOG_DIAG', desc: 'Diagnostic startup logging (0=off, 1=on)', active: false, value: '0' },
|
|
402
|
-
{ key: 'INDEX_SERVER_AUDIT_LOG', desc: 'Audit log path (1=default, 0=disabled, or absolute path)', active: true, value: paths.auditLog },
|
|
403
|
-
|
|
404
|
-
// ── Backup & Recovery ──────────────────────────────────────────────────
|
|
405
|
-
{ section: 'Backup — automatic backup scheduling' },
|
|
406
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP', desc: 'Enable automatic backups (0=off, 1=on; defaults to on when mutation is enabled)', active: false, value: config.mutation ? '1' : '0' },
|
|
407
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP_INTERVAL_MS', desc: 'Backup interval in ms (default: 3600000 = 1 hour)', active: false, value: '3600000' },
|
|
408
|
-
{ key: 'INDEX_SERVER_AUTO_BACKUP_MAX_COUNT', desc: 'Max backup snapshots to retain (default: 10)', active: false, value: '10' },
|
|
409
|
-
{ key: 'INDEX_SERVER_BACKUP_BEFORE_BULK_DELETE',desc: 'Auto-backup before bulk delete operations (0=off, 1=on)', active: false, value: '1' },
|
|
410
|
-
|
|
411
|
-
// ── Features & Flags ───────────────────────────────────────────────────
|
|
412
|
-
{ section: 'Features — feature flags and capabilities' },
|
|
413
|
-
{ key: 'INDEX_SERVER_FEATURES', desc: 'Comma-separated feature flags: usage,window,hotness,drift,risk', active: isEnhanced, value: isEnhanced ? 'usage' : '' },
|
|
414
|
-
{ key: 'INDEX_SERVER_METRICS_FILE_STORAGE', desc: 'Persist metrics to disk (0=off, 1=on)', active: isEnhanced, value: isEnhanced ? '1' : '0' },
|
|
415
|
-
{ key: 'INDEX_SERVER_METRICS_DIR', desc: 'Metrics storage directory', active: isEnhanced, value: paths.metrics },
|
|
416
|
-
{ key: 'INDEX_SERVER_FLAGS_FILE', desc: 'Feature flags JSON file path', active: false, value: paths.flags },
|
|
417
|
-
|
|
418
|
-
// ── Server & Transport ─────────────────────────────────────────────────
|
|
419
|
-
{ section: 'Server — MCP transport and instance mode' },
|
|
420
|
-
{ key: 'INDEX_SERVER_MODE', desc: 'Instance mode: standalone | leader | follower | auto', active: false, value: 'standalone' },
|
|
421
|
-
{ key: 'INDEX_SERVER_DISABLE_EARLY_STDIN_BUFFER',desc: 'Disable stdin handshake hardening (0=off, 1=on)', active: false, value: '0' },
|
|
422
|
-
{ key: 'INDEX_SERVER_IDLE_KEEPALIVE_MS', desc: 'Keepalive interval in ms (default: 30000)', active: false, value: '30000' },
|
|
423
|
-
{ key: 'INDEX_SERVER_POLL_MS', desc: 'Index filesystem poll interval in ms (default: 10000)', active: false, value: '10000' },
|
|
424
|
-
|
|
425
|
-
// ── Advanced Tuning ────────────────────────────────────────────────────
|
|
426
|
-
{ section: 'Advanced — tuning, limits, governance (most users can skip)' },
|
|
427
|
-
{ key: 'INDEX_SERVER_BODY_WARN_LENGTH', desc: 'Max instruction body length in chars (default: 100000)', active: false, value: '100000' },
|
|
428
|
-
{ key: 'INDEX_SERVER_AUTO_SPLIT_OVERSIZED', desc: 'Auto-split oversized entries on load (0=off, 1=on)', active: false, value: '0' },
|
|
429
|
-
{ key: 'INDEX_SERVER_READ_RETRIES', desc: 'File read retry attempts (default: 3)', active: false, value: '3' },
|
|
430
|
-
{ key: 'INDEX_SERVER_MAX_BULK_DELETE', desc: 'Max entries in a single bulk delete (default: 5)', active: false, value: '5' },
|
|
431
|
-
{ key: 'INDEX_SERVER_FEEDBACK_MAX_ENTRIES', desc: 'Max feedback entries before rotation (default: 1000)', active: false, value: '1000' },
|
|
432
|
-
{ key: 'INDEX_SERVER_MESSAGING_MAX', desc: 'Max messages in queue (default: 10000)', active: false, value: '10000' },
|
|
433
|
-
{ key: 'INDEX_SERVER_MAX_CONNECTIONS', desc: 'Max concurrent dashboard connections (default: 100)', active: false, value: '100' },
|
|
434
|
-
{ key: 'INDEX_SERVER_CACHE_MODE', desc: 'Index cache mode: normal | memoize | memoize+hash | reload | reload+memo', active: false, value: 'normal' },
|
|
435
|
-
{ key: 'INDEX_SERVER_WORKSPACE', desc: 'Workspace identifier for multi-tenant setups', active: false, value: '' },
|
|
436
|
-
{ key: 'INDEX_SERVER_AGENT_ID', desc: 'Agent identifier for audit trails', active: false, value: '' },
|
|
437
|
-
];
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// --------------------------------------------------------------------------
|
|
441
|
-
// Generate .vscode/mcp.json snippet (JSONC with comments)
|
|
442
|
-
// --------------------------------------------------------------------------
|
|
443
|
-
function generateMcpJson(config, paths) {
|
|
444
|
-
const catalog = getEnvCatalog(config, paths);
|
|
445
|
-
const indent = '\t\t\t\t';
|
|
446
|
-
const launch = resolveServerLaunch(config);
|
|
447
|
-
const argsJson = JSON.stringify(launch.args);
|
|
448
|
-
|
|
449
|
-
const lines = [
|
|
450
|
-
'{',
|
|
451
|
-
'\t"servers": {',
|
|
452
|
-
`\t\t"${config.serverName}": {`,
|
|
453
|
-
'\t\t\t"type": "stdio",',
|
|
454
|
-
`\t\t\t"cwd": "${fwd(launch.cwd)}",`,
|
|
455
|
-
`\t\t\t"command": "${launch.command}",`,
|
|
456
|
-
`\t\t\t"args": ${argsJson},`,
|
|
457
|
-
'\t\t\t"env": {',
|
|
458
|
-
];
|
|
459
|
-
|
|
460
|
-
let firstSection = true;
|
|
461
|
-
for (const entry of catalog) {
|
|
462
|
-
if (entry.section) {
|
|
463
|
-
if (!firstSection) lines.push('');
|
|
464
|
-
lines.push(`${indent}// ── ${entry.section} ${'─'.repeat(Math.max(0, 58 - entry.section.length))}`);
|
|
465
|
-
firstSection = false;
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const comment = `// ${entry.desc}`;
|
|
470
|
-
if (entry.active) {
|
|
471
|
-
lines.push(`${indent}${comment}`);
|
|
472
|
-
lines.push(`${indent}"${entry.key}": "${entry.value}",`);
|
|
473
|
-
} else {
|
|
474
|
-
lines.push(`${indent}${comment}`);
|
|
475
|
-
lines.push(`${indent}// "${entry.key}": "${entry.value}",`);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
lines.push('\t\t\t}');
|
|
480
|
-
lines.push('\t\t}');
|
|
481
|
-
lines.push('\t},');
|
|
482
|
-
lines.push('\t"inputs": []');
|
|
483
|
-
lines.push('}');
|
|
484
|
-
|
|
485
|
-
return lines.join('\n');
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// --------------------------------------------------------------------------
|
|
489
|
-
// Generate Copilot CLI mcp-config.json format
|
|
490
|
-
// --------------------------------------------------------------------------
|
|
491
|
-
function generateCopilotCliJson(config, paths) {
|
|
492
|
-
const catalog = getEnvCatalog(config, paths);
|
|
493
|
-
const env = {};
|
|
494
|
-
for (const entry of catalog) {
|
|
495
|
-
if (entry.section) continue;
|
|
496
|
-
if (entry.active) env[entry.key] = entry.value;
|
|
497
|
-
}
|
|
498
|
-
const launch = resolveServerLaunch(config);
|
|
499
|
-
// copilot-cli doesn't reliably inherit cwd — use absolute args for local/packaged
|
|
500
|
-
const args = launch.source === 'local'
|
|
501
|
-
? [fwd(path.resolve(launch.cwd, launch.args[0]))]
|
|
502
|
-
: launch.args;
|
|
503
|
-
const obj = {
|
|
504
|
-
mcpServers: {
|
|
505
|
-
[config.serverName]: {
|
|
506
|
-
command: launch.command,
|
|
507
|
-
args,
|
|
508
|
-
env,
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
};
|
|
512
|
-
return JSON.stringify(obj, null, 2);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// --------------------------------------------------------------------------
|
|
516
|
-
// Generate Claude Desktop config JSON format
|
|
517
|
-
// --------------------------------------------------------------------------
|
|
518
|
-
function generateClaudeDesktopJson(config, paths) {
|
|
519
|
-
const catalog = getEnvCatalog(config, paths);
|
|
520
|
-
const env = {};
|
|
521
|
-
for (const entry of catalog) {
|
|
522
|
-
if (entry.section) continue;
|
|
523
|
-
if (entry.active) env[entry.key] = entry.value;
|
|
524
|
-
}
|
|
525
|
-
const launch = resolveServerLaunch(config);
|
|
526
|
-
// Claude Desktop doesn't support cwd — use absolute args for local/packaged
|
|
527
|
-
const args = launch.source === 'local'
|
|
528
|
-
? [fwd(path.resolve(launch.cwd, launch.args[0]))]
|
|
529
|
-
: launch.args;
|
|
530
|
-
const obj = {
|
|
531
|
-
mcpServers: {
|
|
532
|
-
[config.serverName]: {
|
|
533
|
-
command: launch.command,
|
|
534
|
-
args,
|
|
535
|
-
env,
|
|
536
|
-
},
|
|
537
|
-
},
|
|
538
|
-
};
|
|
539
|
-
return JSON.stringify(obj, null, 2);
|
|
308
|
+
return mcpConfig.buildEnvCatalog(config, paths);
|
|
540
309
|
}
|
|
541
310
|
|
|
542
311
|
// --------------------------------------------------------------------------
|
|
543
312
|
// Resolve target config file paths based on scope and OS
|
|
544
313
|
// --------------------------------------------------------------------------
|
|
545
314
|
function resolveConfigPaths(config) {
|
|
546
|
-
|
|
547
|
-
const results = [];
|
|
548
|
-
const isWin = process.platform === 'win32';
|
|
549
|
-
|
|
550
|
-
for (const target of (config.targets || ['vscode'])) {
|
|
551
|
-
if (target === 'vscode') {
|
|
552
|
-
if (config.scope === 'global') {
|
|
553
|
-
const dir = isWin
|
|
554
|
-
? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Code', 'User')
|
|
555
|
-
: path.join(home, '.config', 'Code', 'User');
|
|
556
|
-
results.push({ target, path: path.join(dir, 'settings.json'), format: 'vscode-global' });
|
|
557
|
-
} else {
|
|
558
|
-
results.push({ target, path: path.join(config.root, '.vscode', 'mcp.json'), format: 'vscode' });
|
|
559
|
-
}
|
|
560
|
-
} else if (target === 'copilot-cli') {
|
|
561
|
-
const dir = isWin
|
|
562
|
-
? path.join(process.env.USERPROFILE || home, '.copilot')
|
|
563
|
-
: path.join(home, '.copilot');
|
|
564
|
-
results.push({ target, path: path.join(dir, 'mcp-config.json'), format: 'copilot-cli' });
|
|
565
|
-
} else if (target === 'claude') {
|
|
566
|
-
const dir = isWin
|
|
567
|
-
? path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'Claude')
|
|
568
|
-
: path.join(home, 'Library', 'Application Support', 'Claude');
|
|
569
|
-
results.push({ target, path: path.join(dir, 'claude_desktop_config.json'), format: 'claude' });
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
return results;
|
|
315
|
+
return mcpConfig.resolveConfigTargets({ targets: config.targets, scope: config.scope, root: config.root });
|
|
573
316
|
}
|
|
574
317
|
|
|
575
318
|
// --------------------------------------------------------------------------
|
|
576
319
|
// Generate config content for a given format
|
|
577
320
|
// --------------------------------------------------------------------------
|
|
578
|
-
function generateConfigForTarget(format, config
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
321
|
+
function generateConfigForTarget(format, config) {
|
|
322
|
+
const target = { target: format === 'vscode-global' ? 'vscode' : format, format, path: '' };
|
|
323
|
+
return mcpConfig.renderServerConfigForTarget(target, {
|
|
324
|
+
root: config.root,
|
|
325
|
+
name: config.serverName,
|
|
326
|
+
profile: config.profile,
|
|
327
|
+
port: config.port,
|
|
328
|
+
host: config.host,
|
|
329
|
+
tls: config.tls,
|
|
330
|
+
mutation: config.mutation,
|
|
331
|
+
logLevel: config.logLevel,
|
|
332
|
+
});
|
|
590
333
|
}
|
|
591
334
|
|
|
592
335
|
// --------------------------------------------------------------------------
|
|
593
336
|
// Preview all generated configs
|
|
594
337
|
// --------------------------------------------------------------------------
|
|
595
|
-
function previewConfigs(configTargets, config
|
|
338
|
+
function previewConfigs(configTargets, config) {
|
|
596
339
|
console.log('\n┌─────────────────────────────────────────────────────────────────────┐');
|
|
597
340
|
console.log('│ 📋 Configuration Preview │');
|
|
598
341
|
console.log('└─────────────────────────────────────────────────────────────────────┘');
|
|
599
342
|
for (const ct of configTargets) {
|
|
600
|
-
const content = generateConfigForTarget(ct.format, config
|
|
343
|
+
const content = generateConfigForTarget(ct.format, config);
|
|
601
344
|
console.log(`\n── ${ct.target} → ${ct.path} ──\n`);
|
|
602
345
|
console.log(content);
|
|
603
346
|
}
|
|
@@ -607,45 +350,21 @@ function previewConfigs(configTargets, config, paths) {
|
|
|
607
350
|
// --------------------------------------------------------------------------
|
|
608
351
|
// Write config to real file (merge if existing)
|
|
609
352
|
// --------------------------------------------------------------------------
|
|
610
|
-
function
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (fs.existsSync(targetInfo.path)) {
|
|
626
|
-
// Backup existing file before overwriting
|
|
627
|
-
const backup = targetInfo.path + '.backup.' + Date.now();
|
|
628
|
-
fs.copyFileSync(targetInfo.path, backup);
|
|
629
|
-
console.log(` 📦 Backed up existing: ${backup}`);
|
|
630
|
-
|
|
631
|
-
// For JSON files, try to merge the mcpServers key
|
|
632
|
-
try {
|
|
633
|
-
const existing = JSON.parse(fs.readFileSync(targetInfo.path, 'utf8'));
|
|
634
|
-
const generated = JSON.parse(content);
|
|
635
|
-
|
|
636
|
-
if (targetInfo.format === 'copilot-cli' || targetInfo.format === 'claude') {
|
|
637
|
-
existing.mcpServers = { ...existing.mcpServers, ...generated.mcpServers };
|
|
638
|
-
fs.writeFileSync(targetInfo.path, JSON.stringify(existing, null, 2), 'utf8'); // lgtm[js/file-system-race] — setup wizard writes user-provided config target; race acceptable in CLI tooling
|
|
639
|
-
console.log(` ✅ Merged into: ${targetInfo.path}`);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
} catch {
|
|
643
|
-
// Not valid JSON — fall through to overwrite
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
fs.writeFileSync(targetInfo.path, content, 'utf8'); // lgtm[js/file-system-race] — setup wizard writes user-provided config target; race acceptable in CLI tooling
|
|
648
|
-
console.log(` ✅ Written: ${targetInfo.path}`);
|
|
353
|
+
function applyConfigTarget(targetInfo, config) {
|
|
354
|
+
const result = mcpConfig.upsertServer({
|
|
355
|
+
target: targetInfo.target,
|
|
356
|
+
scope: targetInfo.format === 'vscode-global' ? 'global' : config.scope,
|
|
357
|
+
root: config.root,
|
|
358
|
+
name: config.serverName,
|
|
359
|
+
profile: config.profile,
|
|
360
|
+
port: config.port,
|
|
361
|
+
host: config.host,
|
|
362
|
+
tls: config.tls,
|
|
363
|
+
mutation: config.mutation,
|
|
364
|
+
logLevel: config.logLevel,
|
|
365
|
+
});
|
|
366
|
+
if (result.backupPath) console.log(` 📦 Backed up existing: ${result.backupPath}`);
|
|
367
|
+
console.log(` ✅ Written: ${result.path}`);
|
|
649
368
|
}
|
|
650
369
|
|
|
651
370
|
// --------------------------------------------------------------------------
|
|
@@ -771,7 +490,7 @@ async function deployRuntime(config) {
|
|
|
771
490
|
type: 'commonjs',
|
|
772
491
|
scripts: { start: 'node node_modules/@jagilber-org/index-server/dist/server/index-server.js' },
|
|
773
492
|
};
|
|
774
|
-
|
|
493
|
+
writeTextFile(targetPkg, JSON.stringify(minPkg, null, 2));
|
|
775
494
|
}
|
|
776
495
|
|
|
777
496
|
console.log(' Installing package (this may take a moment)...');
|
|
@@ -781,7 +500,7 @@ async function deployRuntime(config) {
|
|
|
781
500
|
// and produces a proper self-contained node_modules tree.
|
|
782
501
|
const packOutput = runNpm(
|
|
783
502
|
['pack', '--pack-destination', targetRoot],
|
|
784
|
-
{ cwd: ROOT, stdio: ['pipe', 'pipe', 'inherit'], timeout:
|
|
503
|
+
{ cwd: ROOT, stdio: ['pipe', 'pipe', 'inherit'], timeout: DEPLOY_PACK_TIMEOUT_MS }
|
|
785
504
|
).toString().trim();
|
|
786
505
|
const tarballName = packOutput.split('\n').pop();
|
|
787
506
|
|
|
@@ -789,9 +508,10 @@ async function deployRuntime(config) {
|
|
|
789
508
|
|
|
790
509
|
try {
|
|
791
510
|
// Install from the local tarball
|
|
511
|
+
console.log(` npm install timeout: ${DEPLOY_INSTALL_TIMEOUT_MS}ms`);
|
|
792
512
|
runNpm(
|
|
793
513
|
['install', tarballPath, '--omit=dev', '--no-fund', '--no-audit'],
|
|
794
|
-
{ cwd: targetRoot, stdio: 'inherit', timeout:
|
|
514
|
+
{ cwd: targetRoot, stdio: 'inherit', timeout: DEPLOY_INSTALL_TIMEOUT_MS }
|
|
795
515
|
);
|
|
796
516
|
} finally {
|
|
797
517
|
// Clean up tarball
|
|
@@ -827,7 +547,7 @@ async function deployRuntime(config) {
|
|
|
827
547
|
runtimePkg.version = sourceVersion;
|
|
828
548
|
runtimePkg.scripts = runtimePkg.scripts || {};
|
|
829
549
|
runtimePkg.scripts.start = 'node dist/server/index-server.js';
|
|
830
|
-
|
|
550
|
+
writeTextFile(targetPkg, JSON.stringify(runtimePkg, null, 2) + '\n');
|
|
831
551
|
} catch { /* ok */ }
|
|
832
552
|
|
|
833
553
|
// Verify the deployed server is actually runnable
|
|
@@ -860,10 +580,10 @@ async function main() {
|
|
|
860
580
|
Interactive mode:
|
|
861
581
|
npx @jagilber-org/index-server --setup
|
|
862
582
|
npm run setup
|
|
863
|
-
node scripts/setup-wizard.mjs
|
|
583
|
+
node scripts/build/setup-wizard.mjs
|
|
864
584
|
|
|
865
585
|
Non-interactive mode:
|
|
866
|
-
node scripts/setup-wizard.mjs --non-interactive [options]
|
|
586
|
+
node scripts/build/setup-wizard.mjs --non-interactive [options]
|
|
867
587
|
--profile <name> default | enhanced | experimental
|
|
868
588
|
--root <dir> Base directory for all data paths
|
|
869
589
|
--port <n> Dashboard port (default: 8787)
|
|
@@ -901,10 +621,10 @@ Non-interactive mode:
|
|
|
901
621
|
|
|
902
622
|
if (fs.existsSync(envPath)) { // lgtm[js/file-system-race]
|
|
903
623
|
const genPath = path.join(config.root, '.env.generated');
|
|
904
|
-
|
|
624
|
+
writeTextFile(genPath, envContent);
|
|
905
625
|
console.log(`\n⚠️ .env already exists. Written to: ${genPath}`);
|
|
906
626
|
} else {
|
|
907
|
-
|
|
627
|
+
writeTextFile(envPath, envContent); // lgtm[js/file-system-race] — setup wizard writes .env to user-supplied path; race acceptable in CLI tooling
|
|
908
628
|
console.log(`\n✅ .env written to: ${envPath}`);
|
|
909
629
|
}
|
|
910
630
|
|
|
@@ -913,37 +633,36 @@ Non-interactive mode:
|
|
|
913
633
|
|
|
914
634
|
// Preview
|
|
915
635
|
if (config.preview !== false) {
|
|
916
|
-
previewConfigs(configTargets, config
|
|
636
|
+
previewConfigs(configTargets, config);
|
|
917
637
|
}
|
|
918
638
|
|
|
919
639
|
// Write to real files or sidecar
|
|
920
640
|
if (config.write) {
|
|
921
641
|
console.log('📁 Writing configuration files...\n');
|
|
922
642
|
for (const ct of configTargets) {
|
|
923
|
-
|
|
924
|
-
writeConfigFile(ct, content);
|
|
643
|
+
applyConfigTarget(ct, config);
|
|
925
644
|
}
|
|
926
645
|
} else {
|
|
927
646
|
// Legacy sidecar behavior for backward compatibility
|
|
928
|
-
const mcpContent =
|
|
647
|
+
const mcpContent = generateConfigForTarget('vscode', config);
|
|
929
648
|
const mcpDir = path.join(config.root, '.vscode');
|
|
930
649
|
const mcpPath = path.join(mcpDir, 'mcp.json.generated');
|
|
931
650
|
|
|
932
651
|
try {
|
|
933
652
|
fs.mkdirSync(mcpDir, { recursive: true });
|
|
934
653
|
} catch { /* exists */ }
|
|
935
|
-
|
|
654
|
+
mcpConfig.writeGeneratedConfig(mcpPath, mcpContent);
|
|
936
655
|
console.log(`✅ mcp.json snippet written to: ${mcpPath}`);
|
|
937
656
|
console.log(' Copy its contents into your .vscode/mcp.json or VS Code user settings.');
|
|
938
657
|
|
|
939
658
|
// Also generate Copilot CLI / Claude if requested
|
|
940
659
|
for (const ct of configTargets) {
|
|
941
660
|
if (ct.format !== 'vscode' && ct.format !== 'vscode-global') {
|
|
942
|
-
const content = generateConfigForTarget(ct.format, config
|
|
661
|
+
const content = generateConfigForTarget(ct.format, config);
|
|
943
662
|
const genPath = ct.path + '.generated';
|
|
944
663
|
const dir = path.dirname(genPath);
|
|
945
664
|
try { fs.mkdirSync(dir, { recursive: true }); } catch { /* exists */ }
|
|
946
|
-
|
|
665
|
+
mcpConfig.writeGeneratedConfig(genPath, content);
|
|
947
666
|
console.log(`✅ ${ct.target} config written to: ${genPath}`);
|
|
948
667
|
}
|
|
949
668
|
}
|
|
@@ -959,12 +678,12 @@ Non-interactive mode:
|
|
|
959
678
|
const certDir = path.join(config.root, 'certs');
|
|
960
679
|
execFileSync(
|
|
961
680
|
process.execPath,
|
|
962
|
-
[path.join(ROOT, 'scripts', 'generate-certs.mjs'), '--hostname', 'localhost', '--output', certDir],
|
|
681
|
+
[path.join(ROOT, 'scripts', 'build', 'generate-certs.mjs'), '--hostname', 'localhost', '--output', certDir],
|
|
963
682
|
{ stdio: 'inherit' }
|
|
964
683
|
);
|
|
965
684
|
} catch {
|
|
966
685
|
console.error('❌ Certificate generation failed. Run manually:');
|
|
967
|
-
console.error(` node scripts/generate-certs.mjs --output "${path.join(config.root, 'certs')}"`);
|
|
686
|
+
console.error(` node scripts/build/generate-certs.mjs --output "${path.join(config.root, 'certs')}"`);
|
|
968
687
|
}
|
|
969
688
|
}
|
|
970
689
|
|
|
@@ -992,7 +711,7 @@ Non-interactive mode:
|
|
|
992
711
|
console.log(` ${step}. Copy generated config into your MCP client settings.`);
|
|
993
712
|
|
|
994
713
|
for (const ct of configTargets) {
|
|
995
|
-
const genPath = ct.format === 'vscode'
|
|
714
|
+
const genPath = ct.format === 'vscode'
|
|
996
715
|
? path.join(config.root, '.vscode', 'mcp.json.generated')
|
|
997
716
|
: ct.path + '.generated';
|
|
998
717
|
console.log(` ${ct.target}: ${genPath}`);
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/jagilber-org/index-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.28.
|
|
9
|
+
"version": "1.28.4",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@jagilber-org/index-server",
|
|
14
|
-
"version": "1.28.
|
|
14
|
+
"version": "1.28.4",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|