nebula-notebook 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.
Files changed (97) hide show
  1. package/README.md +222 -0
  2. package/bin/nebula-notebook.js +33 -0
  3. package/dist/assets/index-C1h_sArD.css +32 -0
  4. package/dist/assets/index-CDSTBon8.js +658 -0
  5. package/dist/favicon.svg +11 -0
  6. package/dist/index.html +73 -0
  7. package/node-server/dist/app.d.ts +5 -0
  8. package/node-server/dist/app.js +38 -0
  9. package/node-server/dist/auth/auth-middleware.d.ts +24 -0
  10. package/node-server/dist/auth/auth-middleware.js +276 -0
  11. package/node-server/dist/auth/auth-service.d.ts +84 -0
  12. package/node-server/dist/auth/auth-service.js +265 -0
  13. package/node-server/dist/auth/index.d.ts +3 -0
  14. package/node-server/dist/auth/index.js +8 -0
  15. package/node-server/dist/cluster/client-registration.d.ts +43 -0
  16. package/node-server/dist/cluster/client-registration.js +217 -0
  17. package/node-server/dist/cluster/cluster-secret.d.ts +11 -0
  18. package/node-server/dist/cluster/cluster-secret.js +90 -0
  19. package/node-server/dist/cluster/kernel-proxy.d.ts +100 -0
  20. package/node-server/dist/cluster/kernel-proxy.js +361 -0
  21. package/node-server/dist/cluster/server-registry.d.ts +109 -0
  22. package/node-server/dist/cluster/server-registry.js +217 -0
  23. package/node-server/dist/config/output-limits.d.ts +7 -0
  24. package/node-server/dist/config/output-limits.js +10 -0
  25. package/node-server/dist/discovery/discovery-service.d.ts +198 -0
  26. package/node-server/dist/discovery/discovery-service.js +811 -0
  27. package/node-server/dist/discovery/index.d.ts +5 -0
  28. package/node-server/dist/discovery/index.js +21 -0
  29. package/node-server/dist/discovery/types.d.ts +48 -0
  30. package/node-server/dist/discovery/types.js +24 -0
  31. package/node-server/dist/fs/fs-service.d.ts +218 -0
  32. package/node-server/dist/fs/fs-service.js +1422 -0
  33. package/node-server/dist/fs/index.d.ts +5 -0
  34. package/node-server/dist/fs/index.js +21 -0
  35. package/node-server/dist/fs/types.d.ts +132 -0
  36. package/node-server/dist/fs/types.js +5 -0
  37. package/node-server/dist/index.d.ts +13 -0
  38. package/node-server/dist/index.js +556 -0
  39. package/node-server/dist/kernel/default-kernel.d.ts +5 -0
  40. package/node-server/dist/kernel/default-kernel.js +138 -0
  41. package/node-server/dist/kernel/index.d.ts +7 -0
  42. package/node-server/dist/kernel/index.js +23 -0
  43. package/node-server/dist/kernel/kernel-service.d.ts +290 -0
  44. package/node-server/dist/kernel/kernel-service.js +1714 -0
  45. package/node-server/dist/kernel/kernelspec.d.ts +29 -0
  46. package/node-server/dist/kernel/kernelspec.js +210 -0
  47. package/node-server/dist/kernel/session-store.d.ts +87 -0
  48. package/node-server/dist/kernel/session-store.js +303 -0
  49. package/node-server/dist/kernel/types.d.ts +143 -0
  50. package/node-server/dist/kernel/types.js +17 -0
  51. package/node-server/dist/llm/index.d.ts +5 -0
  52. package/node-server/dist/llm/index.js +21 -0
  53. package/node-server/dist/llm/llm-service.d.ts +77 -0
  54. package/node-server/dist/llm/llm-service.js +454 -0
  55. package/node-server/dist/llm/types.d.ts +40 -0
  56. package/node-server/dist/llm/types.js +15 -0
  57. package/node-server/dist/notebook/cell-metadata.d.ts +27 -0
  58. package/node-server/dist/notebook/cell-metadata.js +76 -0
  59. package/node-server/dist/notebook/headless-handler.d.ts +127 -0
  60. package/node-server/dist/notebook/headless-handler.js +1530 -0
  61. package/node-server/dist/notebook/notebook-websocket.d.ts +12 -0
  62. package/node-server/dist/notebook/notebook-websocket.js +103 -0
  63. package/node-server/dist/notebook/operation-router.d.ts +115 -0
  64. package/node-server/dist/notebook/operation-router.js +641 -0
  65. package/node-server/dist/notebook/undoRedoManager.d.ts +194 -0
  66. package/node-server/dist/notebook/undoRedoManager.js +558 -0
  67. package/node-server/dist/output/display-data.d.ts +14 -0
  68. package/node-server/dist/output/display-data.js +134 -0
  69. package/node-server/dist/resources/resource-service.d.ts +69 -0
  70. package/node-server/dist/resources/resource-service.js +363 -0
  71. package/node-server/dist/routes/auth.d.ts +5 -0
  72. package/node-server/dist/routes/auth.js +61 -0
  73. package/node-server/dist/routes/cluster.d.ts +7 -0
  74. package/node-server/dist/routes/cluster.js +94 -0
  75. package/node-server/dist/routes/fs.d.ts +7 -0
  76. package/node-server/dist/routes/fs.js +392 -0
  77. package/node-server/dist/routes/kernel.d.ts +13 -0
  78. package/node-server/dist/routes/kernel.js +637 -0
  79. package/node-server/dist/routes/llm.d.ts +8 -0
  80. package/node-server/dist/routes/llm.js +105 -0
  81. package/node-server/dist/routes/notebook.d.ts +10 -0
  82. package/node-server/dist/routes/notebook.js +335 -0
  83. package/node-server/dist/routes/python.d.ts +8 -0
  84. package/node-server/dist/routes/python.js +187 -0
  85. package/node-server/dist/routes/resources.d.ts +7 -0
  86. package/node-server/dist/routes/resources.js +77 -0
  87. package/node-server/dist/scripts/show-auth-qr.d.ts +1 -0
  88. package/node-server/dist/scripts/show-auth-qr.js +81 -0
  89. package/node-server/dist/terminal/pty-manager.d.ts +100 -0
  90. package/node-server/dist/terminal/pty-manager.js +246 -0
  91. package/node-server/dist/terminal/server.d.ts +19 -0
  92. package/node-server/dist/terminal/server.js +254 -0
  93. package/node-server/dist/terminal/types.d.ts +50 -0
  94. package/node-server/dist/terminal/types.js +9 -0
  95. package/node-server/package.json +45 -0
  96. package/package.json +99 -0
  97. package/scripts/postinstall.cjs +26 -0
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * Terminal Server - Routes and WebSocket setup for PTY management
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.setupTerminalRoutes = setupTerminalRoutes;
40
+ exports.setupTerminalWebSocket = setupTerminalWebSocket;
41
+ exports.cleanupTerminals = cleanupTerminals;
42
+ const ws_1 = require("ws");
43
+ const path = __importStar(require("path"));
44
+ const pty_manager_1 = require("./pty-manager");
45
+ const fs_service_1 = require("../fs/fs-service");
46
+ // Get configured root directory for terminals
47
+ function getDefaultCwd() {
48
+ return fs_service_1.fsService.normalizePath('~');
49
+ }
50
+ // Nebula repo root on the server (parent of node-server). Surfaced so the UI
51
+ // can show a path-qualified MCP setup command — agents/users won't know where
52
+ // the repo lives, and `npm run setup-mcp` only works from inside it.
53
+ // Works from both src/ (tsx) and dist/ (build): each is 3 levels deep.
54
+ const NEBULA_REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
55
+ // Track WebSocket connections by terminal ID
56
+ const wsConnections = new Map();
57
+ /**
58
+ * Setup terminal REST routes as a Fastify plugin
59
+ */
60
+ async function setupTerminalRoutes(fastify) {
61
+ // Terminal health check (includes terminal count and repo root for the
62
+ // path-qualified MCP setup hint shown in the agent terminal UI)
63
+ fastify.get('/api/terminals/health', async (_request, reply) => {
64
+ return reply.send({ status: 'ok', terminals: pty_manager_1.ptyManager.list().length, repo_root: NEBULA_REPO_ROOT });
65
+ });
66
+ // List all terminals
67
+ fastify.get('/api/terminals', async (_request, reply) => {
68
+ return reply.send(pty_manager_1.ptyManager.list());
69
+ });
70
+ // Create a new terminal
71
+ fastify.post('/api/terminals', async (request, reply) => {
72
+ const options = request.body || {};
73
+ // Use configured root directory as default cwd
74
+ const finalOptions = { cwd: getDefaultCwd(), ...options };
75
+ try {
76
+ const terminal = pty_manager_1.ptyManager.create(finalOptions);
77
+ console.log(`[Terminal] Created terminal ${terminal.id} (PID: ${terminal.pid})`);
78
+ return reply.code(201).send(terminal);
79
+ }
80
+ catch (error) {
81
+ console.error('[Terminal] Failed to create terminal:', error);
82
+ return reply.code(500).send({
83
+ error: 'Failed to create terminal',
84
+ message: error instanceof Error ? error.message : 'Unknown error',
85
+ });
86
+ }
87
+ });
88
+ // Get or create a named terminal (for persistent terminals via URL)
89
+ fastify.post('/api/terminals/named/:name', async (request, reply) => {
90
+ const { name } = request.params;
91
+ const options = request.body || {};
92
+ // Use configured root directory as default cwd
93
+ const finalOptions = { cwd: getDefaultCwd(), ...options };
94
+ try {
95
+ const terminal = pty_manager_1.ptyManager.getOrCreate(name, finalOptions);
96
+ console.log(`[Terminal] Get/create named terminal '${name}' -> ${terminal.id}`);
97
+ return reply.send(terminal);
98
+ }
99
+ catch (error) {
100
+ console.error('[Terminal] Failed to get/create named terminal:', error);
101
+ return reply.code(500).send({
102
+ error: 'Failed to get/create terminal',
103
+ message: error instanceof Error ? error.message : 'Unknown error',
104
+ });
105
+ }
106
+ });
107
+ // Get terminal info
108
+ fastify.get('/api/terminals/:id', async (request, reply) => {
109
+ const terminal = pty_manager_1.ptyManager.getTerminalInfo(request.params.id);
110
+ if (!terminal) {
111
+ return reply.code(404).send({ error: 'Terminal not found' });
112
+ }
113
+ return reply.send(terminal);
114
+ });
115
+ // Delete/close a terminal
116
+ fastify.delete('/api/terminals/:id', async (request, reply) => {
117
+ const success = pty_manager_1.ptyManager.kill(request.params.id);
118
+ if (!success) {
119
+ return reply.code(404).send({ error: 'Terminal not found' });
120
+ }
121
+ console.log(`[Terminal] Closed terminal ${request.params.id}`);
122
+ return reply.send({ status: 'ok' });
123
+ });
124
+ // Resize terminal
125
+ fastify.post('/api/terminals/:id/resize', async (request, reply) => {
126
+ const { cols, rows } = request.body;
127
+ if (!cols || !rows) {
128
+ return reply.code(400).send({ error: 'cols and rows are required' });
129
+ }
130
+ const success = pty_manager_1.ptyManager.resize(request.params.id, cols, rows);
131
+ if (!success) {
132
+ return reply.code(404).send({ error: 'Terminal not found' });
133
+ }
134
+ return reply.send({ status: 'ok', cols, rows });
135
+ });
136
+ }
137
+ /**
138
+ * Setup terminal WebSocket server with noServer mode
139
+ * Returns the WSS so the upgrade can be handled externally
140
+ */
141
+ function setupTerminalWebSocket(server) {
142
+ // Use noServer mode to avoid conflicts with kernel WebSocket
143
+ const wss = new ws_1.WebSocketServer({
144
+ noServer: true,
145
+ perMessageDeflate: false, // Disable compression
146
+ });
147
+ // Handle upgrade requests for /ws path
148
+ server.on('upgrade', (request, socket, head) => {
149
+ const pathname = new URL(request.url || '', `http://${request.headers.host}`).pathname;
150
+ if (pathname === '/ws') {
151
+ // Authenticate WebSocket connection
152
+ const { authWebSocketMiddleware } = require('../auth');
153
+ if (!authWebSocketMiddleware(request)) {
154
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
155
+ socket.destroy();
156
+ return;
157
+ }
158
+ wss.handleUpgrade(request, socket, head, (ws) => {
159
+ wss.emit('connection', ws, request);
160
+ });
161
+ }
162
+ // Don't destroy socket for non-matching paths - let other handlers deal with it
163
+ });
164
+ wss.on('connection', (ws, req) => {
165
+ // Extract terminal ID from URL query
166
+ const url = new URL(req.url || '', `http://${req.headers.host}`);
167
+ const terminalId = url.searchParams.get('id');
168
+ if (!terminalId) {
169
+ ws.close(4000, 'Terminal ID required');
170
+ return;
171
+ }
172
+ const session = pty_manager_1.ptyManager.get(terminalId);
173
+ if (!session) {
174
+ ws.close(4004, 'Terminal not found');
175
+ return;
176
+ }
177
+ console.log(`[Terminal] WebSocket connected to terminal ${terminalId}`);
178
+ // Track this connection
179
+ if (!wsConnections.has(terminalId)) {
180
+ wsConnections.set(terminalId, new Set());
181
+ }
182
+ const connections = wsConnections.get(terminalId);
183
+ connections.add(ws);
184
+ // Send buffered output for reconnection
185
+ const buffer = pty_manager_1.ptyManager.getOutputBuffer(terminalId);
186
+ if (buffer) {
187
+ const replayMsg = { type: 'replay', data: buffer };
188
+ ws.send(JSON.stringify(replayMsg));
189
+ }
190
+ // Collaborative mode: broadcast output to ALL connected clients
191
+ const broadcastOutput = (data) => {
192
+ const msg = { type: 'output', data };
193
+ const msgStr = JSON.stringify(msg);
194
+ for (const conn of connections) {
195
+ if (conn.readyState === ws_1.WebSocket.OPEN) {
196
+ conn.send(msgStr);
197
+ }
198
+ }
199
+ };
200
+ pty_manager_1.ptyManager.setOnData(terminalId, broadcastOutput);
201
+ // Set up exit listener - broadcast to all connections
202
+ pty_manager_1.ptyManager.setOnExit(terminalId, (code) => {
203
+ const msg = { type: 'exit', code };
204
+ const msgStr = JSON.stringify(msg);
205
+ for (const conn of connections) {
206
+ if (conn.readyState === ws_1.WebSocket.OPEN) {
207
+ conn.send(msgStr);
208
+ }
209
+ }
210
+ });
211
+ // Handle incoming messages
212
+ ws.on('message', (rawData) => {
213
+ try {
214
+ const message = JSON.parse(rawData.toString());
215
+ switch (message.type) {
216
+ case 'input':
217
+ pty_manager_1.ptyManager.write(terminalId, message.data);
218
+ break;
219
+ case 'resize':
220
+ pty_manager_1.ptyManager.resize(terminalId, message.cols, message.rows);
221
+ break;
222
+ default:
223
+ console.warn(`[Terminal] Unknown message type:`, message);
224
+ }
225
+ }
226
+ catch (error) {
227
+ console.error('[Terminal] Failed to parse message:', error);
228
+ }
229
+ });
230
+ // Handle disconnection
231
+ ws.on('close', () => {
232
+ console.log(`[Terminal] WebSocket disconnected from terminal ${terminalId}`);
233
+ const connections = wsConnections.get(terminalId);
234
+ if (connections) {
235
+ connections.delete(ws);
236
+ if (connections.size === 0) {
237
+ wsConnections.delete(terminalId);
238
+ // Clear the data listener when no connections
239
+ pty_manager_1.ptyManager.setOnData(terminalId, null);
240
+ }
241
+ }
242
+ });
243
+ ws.on('error', (error) => {
244
+ console.error(`[Terminal] WebSocket error for terminal ${terminalId}:`, error);
245
+ });
246
+ });
247
+ return wss;
248
+ }
249
+ /**
250
+ * Cleanup all terminals (call on shutdown)
251
+ */
252
+ function cleanupTerminals() {
253
+ pty_manager_1.ptyManager.killAll();
254
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Terminal Server Types
3
+ */
4
+ export interface TerminalInfo {
5
+ id: string;
6
+ pid: number;
7
+ cwd: string;
8
+ shell: string;
9
+ cols: number;
10
+ rows: number;
11
+ created: number;
12
+ lastActivity: number;
13
+ }
14
+ export interface CreateTerminalRequest {
15
+ cwd?: string;
16
+ shell?: string;
17
+ cols?: number;
18
+ rows?: number;
19
+ }
20
+ export interface ResizeTerminalRequest {
21
+ cols: number;
22
+ rows: number;
23
+ }
24
+ export type ClientMessage = {
25
+ type: 'input';
26
+ data: string;
27
+ } | {
28
+ type: 'resize';
29
+ cols: number;
30
+ rows: number;
31
+ };
32
+ export type ServerMessage = {
33
+ type: 'output';
34
+ data: string;
35
+ } | {
36
+ type: 'replay';
37
+ data: string;
38
+ } | {
39
+ type: 'exit';
40
+ code: number;
41
+ } | {
42
+ type: 'error';
43
+ message: string;
44
+ } | {
45
+ type: 'inactive';
46
+ } | {
47
+ type: 'active';
48
+ };
49
+ export declare const OUTPUT_BUFFER_MAX_SIZE = 100000;
50
+ export declare const OUTPUT_BUFFER_TRIM_SIZE = 50000;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Terminal Server Types
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OUTPUT_BUFFER_TRIM_SIZE = exports.OUTPUT_BUFFER_MAX_SIZE = void 0;
7
+ // Output buffer settings
8
+ exports.OUTPUT_BUFFER_MAX_SIZE = 100000; // ~100KB of scrollback
9
+ exports.OUTPUT_BUFFER_TRIM_SIZE = 50000; // Trim to ~50KB when exceeded
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "nebula-node-server",
3
+ "version": "1.0.0",
4
+ "description": "Node.js server for Nebula Notebook (terminals, filesystem, kernels, LLM)",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "start": "node dist/index.js",
9
+ "dev": "node --watch --import tsx src/index.ts",
10
+ "auth:qr": "tsx src/scripts/show-auth-qr.ts",
11
+ "test": "vitest",
12
+ "test:run": "vitest run"
13
+ },
14
+ "dependencies": {
15
+ "@anthropic-ai/sdk": "^0.32.0",
16
+ "@fastify/cors": "^11.2.0",
17
+ "@fastify/multipart": "^9.4.0",
18
+ "@fastify/static": "^9.0.0",
19
+ "@fastify/websocket": "^11.2.0",
20
+ "@google/genai": "^0.7.0",
21
+ "@homebridge/node-pty-prebuilt-multiarch": "^0.14.0",
22
+ "better-sqlite3": "^11.0.0",
23
+ "fastify": "^5.8.2",
24
+ "jsonwebtoken": "^9.0.2",
25
+ "openai": "^4.77.0",
26
+ "otplib": "^12.0.1",
27
+ "qrcode-terminal": "^0.12.0",
28
+ "uuid": "^9.0.1",
29
+ "ws": "^8.16.0",
30
+ "zeromq": "^6.4.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/better-sqlite3": "^7.6.8",
34
+ "@types/jsonwebtoken": "^9.0.5",
35
+ "@types/node": "^20.10.0",
36
+ "@types/qrcode-terminal": "^0.12.2",
37
+ "@types/supertest": "^6.0.3",
38
+ "@types/uuid": "^9.0.7",
39
+ "@types/ws": "^8.5.10",
40
+ "supertest": "^7.2.2",
41
+ "tsx": "^4.7.0",
42
+ "typescript": "^5.3.0",
43
+ "vitest": "^4.0.16"
44
+ }
45
+ }
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "nebula-notebook",
3
+ "version": "0.1.0",
4
+ "description": "AI-native notebook computing environment \u2014 real Jupyter kernels, real filesystem, built to be driven by agents (Claude Code / Codex) via MCP",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/jzthree/nebula-notebook.git"
10
+ },
11
+ "keywords": [
12
+ "notebook",
13
+ "jupyter",
14
+ "ai",
15
+ "agent",
16
+ "mcp",
17
+ "claude",
18
+ "kernel"
19
+ ],
20
+ "bin": {
21
+ "nebula-notebook": "bin/nebula-notebook.js"
22
+ },
23
+ "files": [
24
+ "bin",
25
+ "dist",
26
+ "node-server/dist/**",
27
+ "node-server/package.json",
28
+ "scripts/postinstall.cjs"
29
+ ],
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "scripts": {
34
+ "dev": "concurrently --kill-others --kill-signal SIGTERM --raw \"cd node-server && PORT=${VITE_API_PORT:-8000} npm run dev\" \"VITE_API_PORT=${VITE_API_PORT:-8000} vite\"",
35
+ "start": "npm run dev",
36
+ "prod": "cd node-server && PORT=3000 npm run dev",
37
+ "auth:qr": "cd node-server && npm run auth:qr",
38
+ "build": "vite build",
39
+ "preview": "vite preview",
40
+ "node-server": "cd node-server && npm run dev",
41
+ "node-server:build": "cd node-server && npm run build",
42
+ "mcp": "npm --prefix packages/mcp run mcp",
43
+ "mcp:build": "npm --prefix packages/mcp run build",
44
+ "mcp:test": "npm --prefix packages/mcp test",
45
+ "setup-mcp": "npm --prefix packages/mcp run setup-mcp",
46
+ "setup": "node setup.cjs",
47
+ "postinstall": "node scripts/postinstall.cjs",
48
+ "prepublishOnly": "npm run build && npm run node-server:build",
49
+ "stop": "lsof -ti:3000,3001,8000 | xargs kill -9 2>/dev/null; echo 'Servers stopped'",
50
+ "test": "vitest",
51
+ "test:run": "vitest run"
52
+ },
53
+ "dependencies": {
54
+ "@anthropic-ai/sdk": "^0.32.0",
55
+ "@fastify/cors": "^11.2.0",
56
+ "@fastify/multipart": "^9.4.0",
57
+ "@fastify/static": "^9.0.0",
58
+ "@fastify/websocket": "^11.2.0",
59
+ "@google/genai": "^0.7.0",
60
+ "@homebridge/node-pty-prebuilt-multiarch": "^0.14.0",
61
+ "better-sqlite3": "^11.0.0",
62
+ "fastify": "^5.8.2",
63
+ "jsonwebtoken": "^9.0.2",
64
+ "openai": "^4.77.0",
65
+ "otplib": "^12.0.1",
66
+ "qrcode-terminal": "^0.12.0",
67
+ "uuid": "^9.0.1",
68
+ "ws": "^8.16.0",
69
+ "zeromq": "^6.4.2"
70
+ },
71
+ "devDependencies": {
72
+ "@codemirror/lang-markdown": "^6.5.0",
73
+ "@codemirror/lang-python": "^6.2.1",
74
+ "@playwright/test": "^1.57.0",
75
+ "@tailwindcss/vite": "^4.2.1",
76
+ "@testing-library/jest-dom": "^6.9.1",
77
+ "@testing-library/react": "^16.3.1",
78
+ "@types/diff-match-patch": "^1.0.36",
79
+ "@types/node": "^22.14.0",
80
+ "@types/react": "^19.2.7",
81
+ "@types/react-dom": "^19.2.3",
82
+ "@uiw/react-codemirror": "^4.25.4",
83
+ "@vitejs/plugin-react": "^5.0.0",
84
+ "@xterm/addon-fit": "^0.11.0",
85
+ "@xterm/addon-web-links": "^0.12.0",
86
+ "@xterm/xterm": "^6.0.0",
87
+ "ansi-to-html": "^0.7.2",
88
+ "concurrently": "^9.1.0",
89
+ "diff-match-patch": "^1.0.5",
90
+ "jsdom": "^27.3.0",
91
+ "lucide-react": "^0.561.0",
92
+ "react": "^19.2.3",
93
+ "react-dom": "^19.2.3",
94
+ "tailwindcss": "^4.2.1",
95
+ "typescript": "~5.8.2",
96
+ "vite": "^6.2.0",
97
+ "vitest": "^4.0.16"
98
+ }
99
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Repo-checkout convenience: install the node-server and packages/mcp
3
+ * subpackage dependencies after a root `npm install`.
4
+ *
5
+ * Skipped entirely when nebula-notebook is installed from npm as a
6
+ * dependency — the published package is self-contained (server runtime
7
+ * deps live in the root package.json; dist artifacts are prebuilt).
8
+ */
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+ const { execSync } = require('child_process');
12
+
13
+ if (__dirname.split(path.sep).includes('node_modules')) {
14
+ process.exit(0);
15
+ }
16
+
17
+ const root = path.join(__dirname, '..');
18
+ for (const dir of ['node-server', path.join('packages', 'mcp')]) {
19
+ const pkg = path.join(root, dir, 'package.json');
20
+ if (!fs.existsSync(pkg)) continue;
21
+ try {
22
+ execSync('npm install', { cwd: path.join(root, dir), stdio: 'inherit' });
23
+ } catch {
24
+ console.warn(`⚠️ postinstall: npm install failed in ${dir}`);
25
+ }
26
+ }