nebula-notebook-mcp 0.1.0
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/README.md +314 -0
- package/bin/nebula-mcp.js +10 -0
- package/dist/circuit-breaker.d.ts +157 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +237 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +314 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +13 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +31 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +237 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/notebook/client.d.ts +643 -0
- package/dist/notebook/client.d.ts.map +1 -0
- package/dist/notebook/client.js +1720 -0
- package/dist/notebook/client.js.map +1 -0
- package/dist/notebook/index.d.ts +6 -0
- package/dist/notebook/index.d.ts.map +1 -0
- package/dist/notebook/index.js +6 -0
- package/dist/notebook/index.js.map +1 -0
- package/dist/notebook/tools.d.ts +244 -0
- package/dist/notebook/tools.d.ts.map +1 -0
- package/dist/notebook/tools.js +279 -0
- package/dist/notebook/tools.js.map +1 -0
- package/dist/tools/execution.d.ts +38 -0
- package/dist/tools/execution.d.ts.map +1 -0
- package/dist/tools/execution.js +116 -0
- package/dist/tools/execution.js.map +1 -0
- package/dist/tools/files.d.ts +70 -0
- package/dist/tools/files.d.ts.map +1 -0
- package/dist/tools/files.js +286 -0
- package/dist/tools/files.js.map +1 -0
- package/dist/tools/index.d.ts +74 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +217 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kernel.d.ts +36 -0
- package/dist/tools/kernel.d.ts.map +1 -0
- package/dist/tools/kernel.js +182 -0
- package/dist/tools/kernel.js.map +1 -0
- package/dist/tools/notebook.d.ts +252 -0
- package/dist/tools/notebook.d.ts.map +1 -0
- package/dist/tools/notebook.js +1089 -0
- package/dist/tools/notebook.js.map +1 -0
- package/dist/tools/types.d.ts +78 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +8 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +473 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/imageResize.d.ts +24 -0
- package/dist/utils/imageResize.d.ts.map +1 -0
- package/dist/utils/imageResize.js +67 -0
- package/dist/utils/imageResize.js.map +1 -0
- package/dist/utils/polling.d.ts +40 -0
- package/dist/utils/polling.d.ts.map +1 -0
- package/dist/utils/polling.js +49 -0
- package/dist/utils/polling.js.map +1 -0
- package/package.json +61 -0
- package/setup-mcp.js +468 -0
package/setup-mcp.js
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Nebula MCP Setup Script
|
|
5
|
+
*
|
|
6
|
+
* Automatically configures Nebula MCP server for detected AI tools:
|
|
7
|
+
* - Claude Code
|
|
8
|
+
* - Claude Desktop
|
|
9
|
+
* - Cursor IDE
|
|
10
|
+
* - Gemini CLI
|
|
11
|
+
* - Codex CLI (OpenAI)
|
|
12
|
+
* - VS Code / GitHub Copilot (workspace)
|
|
13
|
+
* - Antigravity
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
import { execSync } from 'child_process';
|
|
20
|
+
import readline from 'readline';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = path.dirname(__filename);
|
|
24
|
+
|
|
25
|
+
// Get absolute path to nebula-mcp.js
|
|
26
|
+
const MCP_SERVER_PATH = path.join(__dirname, 'bin', 'nebula-mcp.js');
|
|
27
|
+
|
|
28
|
+
// Config file locations
|
|
29
|
+
const CONFIG_PATHS = {
|
|
30
|
+
claudeCode: path.join(process.env.HOME, '.claude.json'),
|
|
31
|
+
claudeDesktop: path.join(process.env.HOME, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
32
|
+
cursor: path.join(process.env.HOME, '.cursor', 'mcp.json'),
|
|
33
|
+
gemini: path.join(process.env.HOME, '.gemini', 'settings.json'),
|
|
34
|
+
codex: path.join(process.env.HOME, '.codex', 'config.toml'),
|
|
35
|
+
vscodeWorkspace: path.join(process.cwd(), '.vscode', 'mcp.json'),
|
|
36
|
+
antigravity: path.join(process.env.HOME, '.gemini', 'antigravity', 'mcp_config.json'),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// When running from an npm install (node_modules, including the npx cache),
|
|
40
|
+
// write configs that invoke `npx -y nebula-notebook-mcp` ā stable across npx
|
|
41
|
+
// cache evictions and package updates. A repo checkout keeps the absolute
|
|
42
|
+
// path (works offline and tracks local edits).
|
|
43
|
+
const IS_NPM_INSTALL = __dirname.split(path.sep).includes('node_modules');
|
|
44
|
+
const MCP_COMMAND = IS_NPM_INSTALL ? 'npx' : 'node';
|
|
45
|
+
const MCP_ARGS = IS_NPM_INSTALL ? ['-y', 'nebula-notebook-mcp'] : [MCP_SERVER_PATH];
|
|
46
|
+
|
|
47
|
+
// Nebula MCP server configuration
|
|
48
|
+
const NEBULA_MCP_CONFIG = {
|
|
49
|
+
command: MCP_COMMAND,
|
|
50
|
+
args: MCP_ARGS,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const NEBULA_MCP_VSCODE_CONFIG = {
|
|
54
|
+
type: 'stdio',
|
|
55
|
+
command: MCP_COMMAND,
|
|
56
|
+
args: MCP_ARGS,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Helper to check if command exists
|
|
60
|
+
function commandExists(command) {
|
|
61
|
+
try {
|
|
62
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Helper to read JSON file
|
|
70
|
+
function readJSON(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Helper to write JSON file
|
|
79
|
+
function writeJSON(filePath, data) {
|
|
80
|
+
const dir = path.dirname(filePath);
|
|
81
|
+
if (!fs.existsSync(dir)) {
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Helper to backup file
|
|
88
|
+
function backupFile(filePath) {
|
|
89
|
+
if (fs.existsSync(filePath)) {
|
|
90
|
+
const backupPath = `${filePath}.backup-${Date.now()}`;
|
|
91
|
+
fs.copyFileSync(filePath, backupPath);
|
|
92
|
+
return backupPath;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isPlainObject(value) {
|
|
98
|
+
return value && typeof value === 'object' && !Array.isArray(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function sortKeys(value) {
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
return value.map(sortKeys);
|
|
104
|
+
}
|
|
105
|
+
if (!isPlainObject(value)) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
return Object.keys(value)
|
|
109
|
+
.sort()
|
|
110
|
+
.reduce((acc, key) => {
|
|
111
|
+
acc[key] = sortKeys(value[key]);
|
|
112
|
+
return acc;
|
|
113
|
+
}, {});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function stableStringify(value) {
|
|
117
|
+
return JSON.stringify(sortKeys(value));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function deepEqual(a, b) {
|
|
121
|
+
return stableStringify(a) === stableStringify(b);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function mergeServerConfig(existing, desired) {
|
|
125
|
+
if (!isPlainObject(existing)) {
|
|
126
|
+
return desired;
|
|
127
|
+
}
|
|
128
|
+
const merged = { ...existing, ...desired };
|
|
129
|
+
if (isPlainObject(existing.env) || isPlainObject(desired.env)) {
|
|
130
|
+
merged.env = {
|
|
131
|
+
...(isPlainObject(existing.env) ? existing.env : {}),
|
|
132
|
+
...(isPlainObject(desired.env) ? desired.env : {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// We intentionally do NOT rely on a default server URL via env.
|
|
136
|
+
// Remove legacy NEBULA_URL to avoid accidental connections to the wrong instance.
|
|
137
|
+
if (isPlainObject(merged.env)) {
|
|
138
|
+
delete merged.env.NEBULA_URL;
|
|
139
|
+
if (Object.keys(merged.env).length === 0) {
|
|
140
|
+
delete merged.env;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return merged;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function upsertMcpServer(config, rootKey, serverName, desiredConfig) {
|
|
147
|
+
if (!isPlainObject(config[rootKey])) {
|
|
148
|
+
config[rootKey] = {};
|
|
149
|
+
}
|
|
150
|
+
const existing = config[rootKey][serverName];
|
|
151
|
+
const next = mergeServerConfig(existing, desiredConfig);
|
|
152
|
+
if (deepEqual(existing, next)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
config[rootKey][serverName] = next;
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Detect installed AI tools
|
|
160
|
+
function detectTools() {
|
|
161
|
+
const tools = {};
|
|
162
|
+
const hasVSCodeCli = commandExists('code') || commandExists('code-insiders');
|
|
163
|
+
const hasAntigravityCli = commandExists('antigravity') || commandExists('agy');
|
|
164
|
+
const hasVSCodeWorkspace = fs.existsSync(path.join(process.cwd(), '.vscode'));
|
|
165
|
+
|
|
166
|
+
// Claude Code
|
|
167
|
+
if (commandExists('claude')) {
|
|
168
|
+
tools.claudeCode = { name: 'Claude Code', configPath: CONFIG_PATHS.claudeCode };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Claude Desktop
|
|
172
|
+
if (fs.existsSync(path.dirname(CONFIG_PATHS.claudeDesktop))) {
|
|
173
|
+
tools.claudeDesktop = { name: 'Claude Desktop', configPath: CONFIG_PATHS.claudeDesktop };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Cursor
|
|
177
|
+
if (commandExists('cursor') || fs.existsSync(path.join(process.env.HOME, '.cursor'))) {
|
|
178
|
+
tools.cursor = { name: 'Cursor IDE', configPath: CONFIG_PATHS.cursor };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Gemini CLI
|
|
182
|
+
if (commandExists('gemini')) {
|
|
183
|
+
tools.gemini = { name: 'Gemini CLI', configPath: CONFIG_PATHS.gemini };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Codex CLI
|
|
187
|
+
if (commandExists('codex')) {
|
|
188
|
+
tools.codex = { name: 'Codex CLI', configPath: CONFIG_PATHS.codex };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// VS Code / GitHub Copilot / Antigravity workspace config
|
|
192
|
+
if (hasVSCodeCli || hasAntigravityCli || hasVSCodeWorkspace) {
|
|
193
|
+
tools.vscodeWorkspace = {
|
|
194
|
+
name: 'VS Code / GitHub Copilot / Antigravity (workspace)',
|
|
195
|
+
configPath: CONFIG_PATHS.vscodeWorkspace
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Antigravity global config (if available)
|
|
200
|
+
if (
|
|
201
|
+
hasAntigravityCli ||
|
|
202
|
+
fs.existsSync(CONFIG_PATHS.antigravity) ||
|
|
203
|
+
fs.existsSync(path.dirname(CONFIG_PATHS.antigravity))
|
|
204
|
+
) {
|
|
205
|
+
tools.antigravity = {
|
|
206
|
+
name: 'Antigravity (global)',
|
|
207
|
+
configPath: CONFIG_PATHS.antigravity
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return tools;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function configureJsonServer(configPath, rootKey, serverConfig, options = {}) {
|
|
215
|
+
const config = readJSON(configPath) || {};
|
|
216
|
+
const changed = upsertMcpServer(config, rootKey, 'nebula-notebook', serverConfig);
|
|
217
|
+
|
|
218
|
+
if (options.dryRun) {
|
|
219
|
+
return { changed };
|
|
220
|
+
}
|
|
221
|
+
if (!changed) {
|
|
222
|
+
return { changed: false };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
writeJSON(configPath, config);
|
|
226
|
+
return { changed: true };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function buildCodexBlock() {
|
|
230
|
+
return [
|
|
231
|
+
'[mcp_servers.nebula-notebook]',
|
|
232
|
+
`command = "${MCP_COMMAND}"`,
|
|
233
|
+
`args = [${MCP_ARGS.map((a) => `"${a}"`).join(', ')}]`,
|
|
234
|
+
].join('\n');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function normalizeToml(value) {
|
|
238
|
+
return value.trim().replace(/\s+/g, ' ');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function escapeRegExp(value) {
|
|
242
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function extractCodexBlock(content) {
|
|
246
|
+
const baseMatch = content.match(/\[mcp_servers\.nebula-notebook\][\s\S]*?(?=\n\[|$)/);
|
|
247
|
+
const parts = [];
|
|
248
|
+
if (baseMatch) {
|
|
249
|
+
parts.push(baseMatch[0].trim());
|
|
250
|
+
}
|
|
251
|
+
return parts.join('\n\n').trim();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function codexHasRequiredConfig(content) {
|
|
255
|
+
const baseMatch = content.match(/\[mcp_servers\.nebula-notebook\][\s\S]*?(?=\n\[|$)/);
|
|
256
|
+
if (!baseMatch) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const baseBlock = baseMatch[0];
|
|
261
|
+
const hasCommand = new RegExp(`command\\s*=\\s*"${escapeRegExp(MCP_COMMAND)}"`).test(baseBlock);
|
|
262
|
+
const expectedArgs = MCP_ARGS.map((a) => `"${escapeRegExp(a)}"`).join('\\s*,\\s*');
|
|
263
|
+
const hasArgs = new RegExp(`args\\s*=\\s*\\[\\s*${expectedArgs}\\s*\\]`).test(baseBlock);
|
|
264
|
+
const envMatch = content.match(/\[mcp_servers\.nebula-notebook\.env\][\s\S]*?(?=\n\[|$)/);
|
|
265
|
+
// If an env block exists, treat as not "clean" since we want to remove legacy NEBULA_URL defaults.
|
|
266
|
+
const hasEnvBlock = !!envMatch;
|
|
267
|
+
|
|
268
|
+
return hasCommand && hasArgs && !hasEnvBlock;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function stripCodexBlock(content) {
|
|
272
|
+
return content
|
|
273
|
+
.replace(/\[mcp_servers\.nebula-notebook\][\s\S]*?(?=\n\[|$)/g, '')
|
|
274
|
+
.replace(/\[mcp_servers\.nebula-notebook\.env\][\s\S]*?(?=\n\[|$)/g, '');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function prepareCodexContent(existingContent) {
|
|
278
|
+
const desiredBlock = buildCodexBlock().trim();
|
|
279
|
+
const baseCount = (existingContent.match(/\[mcp_servers\.nebula-notebook\]/g) || []).length;
|
|
280
|
+
const envCount = (existingContent.match(/\[mcp_servers\.nebula-notebook\.env\]/g) || []).length;
|
|
281
|
+
const existingBlock = extractCodexBlock(existingContent);
|
|
282
|
+
const matchesRequired = codexHasRequiredConfig(existingContent);
|
|
283
|
+
const hasDuplicates = baseCount > 1 || envCount > 1;
|
|
284
|
+
const hasLegacyEnv = envCount > 0;
|
|
285
|
+
const isSame =
|
|
286
|
+
!hasDuplicates &&
|
|
287
|
+
!hasLegacyEnv &&
|
|
288
|
+
(matchesRequired ||
|
|
289
|
+
(existingBlock &&
|
|
290
|
+
normalizeToml(existingBlock) === normalizeToml(desiredBlock) &&
|
|
291
|
+
baseCount <= 1));
|
|
292
|
+
|
|
293
|
+
if (isSame) {
|
|
294
|
+
return { changed: false, content: existingContent };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let baseContent = stripCodexBlock(existingContent).trimEnd();
|
|
298
|
+
if (baseContent.length > 0) {
|
|
299
|
+
baseContent += '\n\n';
|
|
300
|
+
}
|
|
301
|
+
const nextContent = `${baseContent}${desiredBlock}\n`;
|
|
302
|
+
return { changed: true, content: nextContent };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Configure Claude Code
|
|
306
|
+
function configureClaudeCode(configPath, options = {}) {
|
|
307
|
+
return configureJsonServer(configPath, 'mcpServers', NEBULA_MCP_CONFIG, options);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Configure Claude Desktop
|
|
311
|
+
function configureClaudeDesktop(configPath, options = {}) {
|
|
312
|
+
return configureJsonServer(configPath, 'mcpServers', NEBULA_MCP_CONFIG, options);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Configure Cursor
|
|
316
|
+
function configureCursor(configPath, options = {}) {
|
|
317
|
+
return configureJsonServer(configPath, 'mcpServers', NEBULA_MCP_CONFIG, options);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Configure Gemini CLI
|
|
321
|
+
function configureGemini(configPath, options = {}) {
|
|
322
|
+
return configureJsonServer(configPath, 'mcpServers', NEBULA_MCP_CONFIG, options);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Configure VS Code / GitHub Copilot workspace config
|
|
326
|
+
function configureVSCodeWorkspace(configPath, options = {}) {
|
|
327
|
+
return configureJsonServer(configPath, 'servers', NEBULA_MCP_VSCODE_CONFIG, options);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Configure Antigravity global config
|
|
331
|
+
function configureAntigravity(configPath, options = {}) {
|
|
332
|
+
return configureJsonServer(configPath, 'mcpServers', NEBULA_MCP_CONFIG, options);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Configure Codex CLI (TOML format)
|
|
336
|
+
function configureCodex(configPath, options = {}) {
|
|
337
|
+
const existingContent = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf8') : '';
|
|
338
|
+
const { changed, content } = prepareCodexContent(existingContent);
|
|
339
|
+
|
|
340
|
+
if (options.dryRun) {
|
|
341
|
+
return { changed };
|
|
342
|
+
}
|
|
343
|
+
if (!changed) {
|
|
344
|
+
return { changed: false };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const dir = path.dirname(configPath);
|
|
348
|
+
if (!fs.existsSync(dir)) {
|
|
349
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fs.writeFileSync(configPath, content);
|
|
353
|
+
return { changed: true };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Prompt user for confirmation
|
|
357
|
+
function promptUser(question) {
|
|
358
|
+
const rl = readline.createInterface({
|
|
359
|
+
input: process.stdin,
|
|
360
|
+
output: process.stdout
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
return new Promise((resolve) => {
|
|
364
|
+
rl.question(question, (answer) => {
|
|
365
|
+
rl.close();
|
|
366
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Main setup function
|
|
372
|
+
async function main() {
|
|
373
|
+
console.log('\nš Detecting installed AI tools...\n');
|
|
374
|
+
|
|
375
|
+
const tools = detectTools();
|
|
376
|
+
const toolNames = Object.keys(tools);
|
|
377
|
+
|
|
378
|
+
if (toolNames.length === 0) {
|
|
379
|
+
console.log('ā No supported AI tools detected.');
|
|
380
|
+
console.log('\nSupported tools: Claude Code, Claude Desktop, Cursor, Gemini CLI, Codex CLI, VS Code / GitHub Copilot, Antigravity');
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
console.log('Found:');
|
|
385
|
+
toolNames.forEach(key => {
|
|
386
|
+
console.log(` ā ${tools[key].name}`);
|
|
387
|
+
});
|
|
388
|
+
console.log('');
|
|
389
|
+
|
|
390
|
+
// Show configuration details
|
|
391
|
+
console.log('š¦ Nebula MCP server will be configured at:');
|
|
392
|
+
console.log(` ${MCP_SERVER_PATH}\n`);
|
|
393
|
+
|
|
394
|
+
console.log('āļø Configure Nebula MCP for:');
|
|
395
|
+
toolNames.forEach(key => {
|
|
396
|
+
console.log(` [ā] ${tools[key].name} (${tools[key].configPath})`);
|
|
397
|
+
});
|
|
398
|
+
console.log('');
|
|
399
|
+
|
|
400
|
+
// Confirm with user
|
|
401
|
+
const confirmed = await promptUser('Continue? (y/N): ');
|
|
402
|
+
if (!confirmed) {
|
|
403
|
+
console.log('ā Setup cancelled.');
|
|
404
|
+
process.exit(0);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const configurators = {
|
|
408
|
+
claudeCode: configureClaudeCode,
|
|
409
|
+
claudeDesktop: configureClaudeDesktop,
|
|
410
|
+
cursor: configureCursor,
|
|
411
|
+
gemini: configureGemini,
|
|
412
|
+
codex: configureCodex,
|
|
413
|
+
vscodeWorkspace: configureVSCodeWorkspace,
|
|
414
|
+
antigravity: configureAntigravity,
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const plan = {};
|
|
418
|
+
toolNames.forEach(key => {
|
|
419
|
+
plan[key] = configurators[key](tools[key].configPath, { dryRun: true });
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
console.log('\nš¾ Backing up configs...');
|
|
423
|
+
const backups = {};
|
|
424
|
+
toolNames.forEach(key => {
|
|
425
|
+
if (!plan[key]?.changed) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const backup = backupFile(tools[key].configPath);
|
|
429
|
+
if (backup) {
|
|
430
|
+
backups[key] = backup;
|
|
431
|
+
console.log(`ā Backed up ${path.basename(tools[key].configPath)} ā ${path.basename(backup)}`);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Configure each tool
|
|
436
|
+
console.log('');
|
|
437
|
+
for (const key of toolNames) {
|
|
438
|
+
try {
|
|
439
|
+
console.log(`āļø Configuring ${tools[key].name}...`);
|
|
440
|
+
if (!plan[key]?.changed) {
|
|
441
|
+
console.log(`ā· Already configured in ${path.basename(tools[key].configPath)}`);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
configurators[key](tools[key].configPath);
|
|
445
|
+
console.log(`ā Added/updated nebula-notebook in ${path.basename(tools[key].configPath)}`);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.error(`ā Failed to configure ${tools[key].name}: ${error.message}`);
|
|
448
|
+
if (backups[key]) {
|
|
449
|
+
console.log(` ā Restored from backup: ${backups[key]}`);
|
|
450
|
+
fs.copyFileSync(backups[key], tools[key].configPath);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
console.log('\nā
Setup complete! Restart your AI tools to use Nebula MCP.');
|
|
456
|
+
console.log('\nVerify with:');
|
|
457
|
+
if (tools.claudeCode) console.log(' - claude mcp list');
|
|
458
|
+
if (tools.cursor) console.log(' - Cursor ā Settings ā Model Context Protocol');
|
|
459
|
+
if (tools.gemini) console.log(' - gemini mcp list');
|
|
460
|
+
if (tools.codex) console.log(' - codex mcp list');
|
|
461
|
+
if (tools.vscodeWorkspace) console.log(' - VS Code ā Settings ā Model Context Protocol');
|
|
462
|
+
console.log('');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
main().catch(error => {
|
|
466
|
+
console.error('ā Setup failed:', error.message);
|
|
467
|
+
process.exit(1);
|
|
468
|
+
});
|