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,100 @@
1
+ /**
2
+ * Kernel Proxy
3
+ *
4
+ * Handles proxying kernel operations to peer servers in a cluster.
5
+ * When a kernel is started on a remote server, this module manages
6
+ * the communication between the client and the remote kernel.
7
+ */
8
+ import { WebSocket } from 'ws';
9
+ import { PeerServer } from './server-registry';
10
+ interface ProxiedSession {
11
+ serverId: string;
12
+ remoteSessionId: string;
13
+ remoteWs?: WebSocket;
14
+ localWs?: WebSocket;
15
+ }
16
+ /**
17
+ * Create a composite session ID that encodes the server
18
+ */
19
+ export declare function createProxiedSessionId(serverId: string, remoteSessionId: string): string;
20
+ /**
21
+ * Parse a composite session ID
22
+ */
23
+ export declare function parseSessionId(sessionId: string): {
24
+ isProxied: boolean;
25
+ serverId?: string;
26
+ remoteSessionId?: string;
27
+ };
28
+ /**
29
+ * Check if a session ID is for a proxied (remote) kernel
30
+ */
31
+ export declare function isProxiedSession(sessionId: string): boolean;
32
+ /**
33
+ * Get the server for a session
34
+ */
35
+ export declare function getSessionServer(sessionId: string): PeerServer | null;
36
+ /**
37
+ * Start a kernel on a remote server
38
+ */
39
+ export declare function startRemoteKernel(serverId: string, kernelName: string, filePath?: string): Promise<{
40
+ sessionId: string;
41
+ created?: boolean;
42
+ createdAt?: number;
43
+ }>;
44
+ /**
45
+ * Interrupt a kernel on a remote server
46
+ */
47
+ export declare function interruptRemoteKernel(sessionId: string): Promise<void>;
48
+ /**
49
+ * Restart a kernel on a remote server
50
+ */
51
+ export declare function restartRemoteKernel(sessionId: string): Promise<void>;
52
+ /**
53
+ * Get status of a kernel on a remote server
54
+ */
55
+ export declare function getRemoteKernelStatus(sessionId: string): Promise<{
56
+ status: string;
57
+ execution_count?: number;
58
+ memory_mb?: number;
59
+ }>;
60
+ /**
61
+ * Shutdown a kernel on a remote server
62
+ */
63
+ export declare function shutdownRemoteKernel(sessionId: string): Promise<void>;
64
+ /**
65
+ * Get available kernels from a remote server
66
+ */
67
+ export declare function getRemoteKernels(serverId: string): Promise<{
68
+ kernels: any[];
69
+ }>;
70
+ /**
71
+ * Get active kernel sessions from a remote server
72
+ */
73
+ export declare function getRemoteKernelSessions(serverId: string): Promise<{
74
+ sessions: any[];
75
+ }>;
76
+ /**
77
+ * Get dead kernel sessions from a remote server
78
+ */
79
+ export declare function getRemoteDeadKernelSessions(serverId: string): Promise<{
80
+ sessions: any[];
81
+ }>;
82
+ /**
83
+ * Cleanup dead kernel sessions on a remote server
84
+ */
85
+ export declare function cleanupRemoteDeadKernelSessions(serverId: string, sessionIds?: string[]): Promise<{
86
+ deleted: number;
87
+ }>;
88
+ /**
89
+ * Create a WebSocket proxy to a remote kernel
90
+ */
91
+ export declare function createWebSocketProxy(sessionId: string, clientWs: WebSocket): WebSocket | null;
92
+ /**
93
+ * Get all proxied sessions
94
+ */
95
+ export declare function getProxiedSessions(): Map<string, ProxiedSession>;
96
+ /**
97
+ * Cleanup all proxied sessions
98
+ */
99
+ export declare function cleanupProxiedSessions(): void;
100
+ export {};
@@ -0,0 +1,361 @@
1
+ "use strict";
2
+ /**
3
+ * Kernel Proxy
4
+ *
5
+ * Handles proxying kernel operations to peer servers in a cluster.
6
+ * When a kernel is started on a remote server, this module manages
7
+ * the communication between the client and the remote kernel.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createProxiedSessionId = createProxiedSessionId;
11
+ exports.parseSessionId = parseSessionId;
12
+ exports.isProxiedSession = isProxiedSession;
13
+ exports.getSessionServer = getSessionServer;
14
+ exports.startRemoteKernel = startRemoteKernel;
15
+ exports.interruptRemoteKernel = interruptRemoteKernel;
16
+ exports.restartRemoteKernel = restartRemoteKernel;
17
+ exports.getRemoteKernelStatus = getRemoteKernelStatus;
18
+ exports.shutdownRemoteKernel = shutdownRemoteKernel;
19
+ exports.getRemoteKernels = getRemoteKernels;
20
+ exports.getRemoteKernelSessions = getRemoteKernelSessions;
21
+ exports.getRemoteDeadKernelSessions = getRemoteDeadKernelSessions;
22
+ exports.cleanupRemoteDeadKernelSessions = cleanupRemoteDeadKernelSessions;
23
+ exports.createWebSocketProxy = createWebSocketProxy;
24
+ exports.getProxiedSessions = getProxiedSessions;
25
+ exports.cleanupProxiedSessions = cleanupProxiedSessions;
26
+ const ws_1 = require("ws");
27
+ const server_registry_1 = require("./server-registry");
28
+ const proxiedSessions = new Map();
29
+ function getClusterHeaders() {
30
+ const secret = process.env.NEBULA_CLUSTER_SECRET;
31
+ return secret ? { 'X-Nebula-Cluster-Secret': secret } : {};
32
+ }
33
+ /**
34
+ * Create a composite session ID that encodes the server
35
+ */
36
+ function createProxiedSessionId(serverId, remoteSessionId) {
37
+ return `${serverId}::${remoteSessionId}`;
38
+ }
39
+ /**
40
+ * Parse a composite session ID
41
+ */
42
+ function parseSessionId(sessionId) {
43
+ if (sessionId.includes('::')) {
44
+ const [serverId, remoteSessionId] = sessionId.split('::', 2);
45
+ return { isProxied: true, serverId, remoteSessionId };
46
+ }
47
+ return { isProxied: false };
48
+ }
49
+ /**
50
+ * Check if a session ID is for a proxied (remote) kernel
51
+ */
52
+ function isProxiedSession(sessionId) {
53
+ return sessionId.includes('::');
54
+ }
55
+ /**
56
+ * Get the server for a session
57
+ */
58
+ function getSessionServer(sessionId) {
59
+ const { isProxied, serverId } = parseSessionId(sessionId);
60
+ if (!isProxied || !serverId)
61
+ return null;
62
+ return server_registry_1.serverRegistry.getServer(serverId) || null;
63
+ }
64
+ /**
65
+ * Start a kernel on a remote server
66
+ */
67
+ async function startRemoteKernel(serverId, kernelName, filePath) {
68
+ const server = server_registry_1.serverRegistry.getServer(serverId);
69
+ if (!server) {
70
+ throw new Error(`Server not found: ${serverId}`);
71
+ }
72
+ if (server.status !== 'online') {
73
+ throw new Error(`Server is offline: ${serverId}`);
74
+ }
75
+ // Call the remote server's kernel start endpoint
76
+ const endpoint = filePath
77
+ ? `${server.url}/api/kernels/for-file`
78
+ : `${server.url}/api/kernels/start`;
79
+ const body = filePath
80
+ ? { file_path: filePath, kernel_name: kernelName }
81
+ : { kernel_name: kernelName };
82
+ const response = await fetch(endpoint, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json', ...getClusterHeaders() },
85
+ body: JSON.stringify(body),
86
+ });
87
+ if (!response.ok) {
88
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
89
+ throw new Error(error.detail || `Failed to start kernel on ${serverId}`);
90
+ }
91
+ const result = await response.json();
92
+ const remoteSessionId = result.session_id;
93
+ // Create composite session ID
94
+ const proxySessionId = createProxiedSessionId(serverId, remoteSessionId);
95
+ // Track the proxied session
96
+ proxiedSessions.set(proxySessionId, {
97
+ serverId,
98
+ remoteSessionId,
99
+ });
100
+ console.log(`[KernelProxy] Started kernel on ${serverId}, session: ${proxySessionId}`);
101
+ return {
102
+ sessionId: proxySessionId,
103
+ created: filePath ? result.created : true,
104
+ createdAt: result.created_at,
105
+ };
106
+ }
107
+ /**
108
+ * Interrupt a kernel on a remote server
109
+ */
110
+ async function interruptRemoteKernel(sessionId) {
111
+ const { serverId, remoteSessionId } = parseSessionId(sessionId);
112
+ if (!serverId || !remoteSessionId) {
113
+ throw new Error('Invalid proxied session ID');
114
+ }
115
+ const server = server_registry_1.serverRegistry.getServer(serverId);
116
+ if (!server) {
117
+ throw new Error(`Server not found: ${serverId}`);
118
+ }
119
+ const response = await fetch(`${server.url}/api/kernels/${remoteSessionId}/interrupt`, {
120
+ method: 'POST',
121
+ headers: { ...getClusterHeaders() },
122
+ });
123
+ if (!response.ok) {
124
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
125
+ throw new Error(error.detail || 'Failed to interrupt kernel');
126
+ }
127
+ }
128
+ /**
129
+ * Restart a kernel on a remote server
130
+ */
131
+ async function restartRemoteKernel(sessionId) {
132
+ const { serverId, remoteSessionId } = parseSessionId(sessionId);
133
+ if (!serverId || !remoteSessionId) {
134
+ throw new Error('Invalid proxied session ID');
135
+ }
136
+ const server = server_registry_1.serverRegistry.getServer(serverId);
137
+ if (!server) {
138
+ throw new Error(`Server not found: ${serverId}`);
139
+ }
140
+ const response = await fetch(`${server.url}/api/kernels/${remoteSessionId}/restart`, {
141
+ method: 'POST',
142
+ headers: { ...getClusterHeaders() },
143
+ });
144
+ if (!response.ok) {
145
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
146
+ throw new Error(error.detail || 'Failed to restart kernel');
147
+ }
148
+ }
149
+ /**
150
+ * Get status of a kernel on a remote server
151
+ */
152
+ async function getRemoteKernelStatus(sessionId) {
153
+ const { serverId, remoteSessionId } = parseSessionId(sessionId);
154
+ if (!serverId || !remoteSessionId) {
155
+ throw new Error('Invalid proxied session ID');
156
+ }
157
+ const server = server_registry_1.serverRegistry.getServer(serverId);
158
+ if (!server) {
159
+ throw new Error(`Server not found: ${serverId}`);
160
+ }
161
+ const response = await fetch(`${server.url}/api/kernels/${remoteSessionId}/status`, {
162
+ headers: { ...getClusterHeaders() },
163
+ });
164
+ if (!response.ok) {
165
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
166
+ throw new Error(error.detail || 'Failed to get kernel status');
167
+ }
168
+ return response.json();
169
+ }
170
+ /**
171
+ * Shutdown a kernel on a remote server
172
+ */
173
+ async function shutdownRemoteKernel(sessionId) {
174
+ const { serverId, remoteSessionId } = parseSessionId(sessionId);
175
+ if (!serverId || !remoteSessionId) {
176
+ throw new Error('Invalid proxied session ID');
177
+ }
178
+ const server = server_registry_1.serverRegistry.getServer(serverId);
179
+ if (!server) {
180
+ throw new Error(`Server not found: ${serverId}`);
181
+ }
182
+ const response = await fetch(`${server.url}/api/kernels/${remoteSessionId}`, {
183
+ method: 'DELETE',
184
+ headers: { ...getClusterHeaders() },
185
+ });
186
+ if (!response.ok) {
187
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
188
+ throw new Error(error.detail || 'Failed to shutdown kernel');
189
+ }
190
+ // Clean up proxied session
191
+ proxiedSessions.delete(sessionId);
192
+ }
193
+ /**
194
+ * Get available kernels from a remote server
195
+ */
196
+ async function getRemoteKernels(serverId) {
197
+ const server = server_registry_1.serverRegistry.getServer(serverId);
198
+ if (!server) {
199
+ throw new Error(`Server not found: ${serverId}`);
200
+ }
201
+ if (server.status !== 'online') {
202
+ throw new Error(`Server is offline: ${serverId}`);
203
+ }
204
+ const response = await fetch(`${server.url}/api/kernels`, {
205
+ headers: { ...getClusterHeaders() },
206
+ });
207
+ if (!response.ok) {
208
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
209
+ throw new Error(error.detail || 'Failed to fetch kernels');
210
+ }
211
+ return response.json();
212
+ }
213
+ /**
214
+ * Get active kernel sessions from a remote server
215
+ */
216
+ async function getRemoteKernelSessions(serverId) {
217
+ const server = server_registry_1.serverRegistry.getServer(serverId);
218
+ if (!server) {
219
+ throw new Error(`Server not found: ${serverId}`);
220
+ }
221
+ if (server.status !== 'online') {
222
+ throw new Error(`Server is offline: ${serverId}`);
223
+ }
224
+ const response = await fetch(`${server.url}/api/kernels/sessions`, {
225
+ headers: { ...getClusterHeaders() },
226
+ });
227
+ if (!response.ok) {
228
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
229
+ throw new Error(error.detail || 'Failed to fetch kernel sessions');
230
+ }
231
+ return response.json();
232
+ }
233
+ /**
234
+ * Get dead kernel sessions from a remote server
235
+ */
236
+ async function getRemoteDeadKernelSessions(serverId) {
237
+ const server = server_registry_1.serverRegistry.getServer(serverId);
238
+ if (!server) {
239
+ throw new Error(`Server not found: ${serverId}`);
240
+ }
241
+ if (server.status !== 'online') {
242
+ throw new Error(`Server is offline: ${serverId}`);
243
+ }
244
+ const response = await fetch(`${server.url}/api/kernels/dead`, {
245
+ headers: { ...getClusterHeaders() },
246
+ });
247
+ if (!response.ok) {
248
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
249
+ throw new Error(error.detail || 'Failed to fetch dead kernel sessions');
250
+ }
251
+ return response.json();
252
+ }
253
+ /**
254
+ * Cleanup dead kernel sessions on a remote server
255
+ */
256
+ async function cleanupRemoteDeadKernelSessions(serverId, sessionIds) {
257
+ const server = server_registry_1.serverRegistry.getServer(serverId);
258
+ if (!server) {
259
+ throw new Error(`Server not found: ${serverId}`);
260
+ }
261
+ if (server.status !== 'online') {
262
+ throw new Error(`Server is offline: ${serverId}`);
263
+ }
264
+ const response = await fetch(`${server.url}/api/kernels/dead/cleanup`, {
265
+ method: 'POST',
266
+ headers: { 'Content-Type': 'application/json', ...getClusterHeaders() },
267
+ body: JSON.stringify({ session_ids: sessionIds }),
268
+ });
269
+ if (!response.ok) {
270
+ const error = await response.json().catch(() => ({ detail: 'Unknown error' }));
271
+ throw new Error(error.detail || 'Failed to cleanup dead kernel sessions');
272
+ }
273
+ return response.json();
274
+ }
275
+ /**
276
+ * Create a WebSocket proxy to a remote kernel
277
+ */
278
+ function createWebSocketProxy(sessionId, clientWs) {
279
+ const { serverId, remoteSessionId } = parseSessionId(sessionId);
280
+ if (!serverId || !remoteSessionId) {
281
+ return null;
282
+ }
283
+ const server = server_registry_1.serverRegistry.getServer(serverId);
284
+ if (!server) {
285
+ console.error(`[KernelProxy] Server not found for WebSocket proxy: ${serverId}`);
286
+ return null;
287
+ }
288
+ // Create WebSocket URL for remote server
289
+ const wsUrl = server.url.replace(/^http/, 'ws') + `/api/kernels/${remoteSessionId}/ws`;
290
+ console.log(`[KernelProxy] Creating WebSocket proxy to ${wsUrl}`);
291
+ const remoteWs = new ws_1.WebSocket(wsUrl, {
292
+ headers: getClusterHeaders(),
293
+ });
294
+ // Forward messages from remote to client
295
+ remoteWs.on('message', (data, isBinary) => {
296
+ if (clientWs.readyState !== ws_1.WebSocket.OPEN) {
297
+ return;
298
+ }
299
+ if (isBinary) {
300
+ clientWs.send(data);
301
+ return;
302
+ }
303
+ clientWs.send(typeof data === 'string' ? data : data.toString());
304
+ });
305
+ // Forward messages from client to remote
306
+ clientWs.on('message', (data, isBinary) => {
307
+ if (remoteWs.readyState !== ws_1.WebSocket.OPEN) {
308
+ return;
309
+ }
310
+ if (isBinary) {
311
+ remoteWs.send(data);
312
+ return;
313
+ }
314
+ remoteWs.send(typeof data === 'string' ? data : data.toString());
315
+ });
316
+ // Handle remote close
317
+ remoteWs.on('close', () => {
318
+ console.log(`[KernelProxy] Remote WebSocket closed for ${sessionId}`);
319
+ if (clientWs.readyState === ws_1.WebSocket.OPEN) {
320
+ clientWs.close();
321
+ }
322
+ });
323
+ // Handle client close
324
+ clientWs.on('close', () => {
325
+ console.log(`[KernelProxy] Client WebSocket closed for ${sessionId}`);
326
+ if (remoteWs.readyState === ws_1.WebSocket.OPEN) {
327
+ remoteWs.close();
328
+ }
329
+ });
330
+ // Handle errors
331
+ remoteWs.on('error', (err) => {
332
+ console.error(`[KernelProxy] Remote WebSocket error for ${sessionId}:`, err);
333
+ });
334
+ // Track the proxy
335
+ const session = proxiedSessions.get(sessionId);
336
+ if (session) {
337
+ session.remoteWs = remoteWs;
338
+ session.localWs = clientWs;
339
+ }
340
+ return remoteWs;
341
+ }
342
+ /**
343
+ * Get all proxied sessions
344
+ */
345
+ function getProxiedSessions() {
346
+ return proxiedSessions;
347
+ }
348
+ /**
349
+ * Cleanup all proxied sessions
350
+ */
351
+ function cleanupProxiedSessions() {
352
+ for (const [sessionId, session] of proxiedSessions.entries()) {
353
+ if (session.remoteWs) {
354
+ session.remoteWs.close();
355
+ }
356
+ if (session.localWs) {
357
+ session.localWs.close();
358
+ }
359
+ proxiedSessions.delete(sessionId);
360
+ }
361
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Server Registry
3
+ *
4
+ * Manages registration of peer servers in a Nebula cluster.
5
+ * Peer servers can register with a main server to enable
6
+ * cross-server kernel access.
7
+ */
8
+ import type { ServerResources } from '../resources/resource-service';
9
+ export interface PeerServer {
10
+ id: string;
11
+ host: string;
12
+ port: number;
13
+ name?: string;
14
+ url: string;
15
+ registeredAt: number;
16
+ lastHeartbeat: number;
17
+ status: 'online' | 'offline' | 'unknown';
18
+ kernelspecs?: string[];
19
+ resources?: ServerResources;
20
+ }
21
+ export interface RegisterRequest {
22
+ host: string;
23
+ port: number;
24
+ name?: string;
25
+ secret?: string;
26
+ resources?: ServerResources;
27
+ }
28
+ declare class ServerRegistry {
29
+ private servers;
30
+ private clusterSecret;
31
+ private cleanupInterval;
32
+ private localServerId;
33
+ constructor();
34
+ /**
35
+ * Set the local server's ID (called on startup)
36
+ */
37
+ setLocalServerId(id: string): void;
38
+ /**
39
+ * Get the local server's ID
40
+ */
41
+ getLocalServerId(): string;
42
+ /**
43
+ * Validate registration secret
44
+ */
45
+ private getClusterSecret;
46
+ private validateSecret;
47
+ /**
48
+ * Generate server ID from host and port
49
+ */
50
+ private generateServerId;
51
+ /**
52
+ * Register a peer server
53
+ */
54
+ register(request: RegisterRequest): {
55
+ success: boolean;
56
+ serverId?: string;
57
+ error?: string;
58
+ };
59
+ /**
60
+ * Unregister a peer server
61
+ */
62
+ unregister(serverId: string): boolean;
63
+ /**
64
+ * Record heartbeat from a peer server
65
+ */
66
+ heartbeat(serverId: string, resources?: ServerResources): boolean;
67
+ /**
68
+ * Get a specific peer server
69
+ */
70
+ getServer(serverId: string): PeerServer | undefined;
71
+ /**
72
+ * Get all registered peer servers
73
+ */
74
+ getAllServers(): PeerServer[];
75
+ /**
76
+ * Get all online peer servers
77
+ */
78
+ getOnlineServers(): PeerServer[];
79
+ /**
80
+ * Check server health and update status
81
+ */
82
+ private checkServerHealth;
83
+ /**
84
+ * Start the cleanup interval
85
+ */
86
+ private startCleanupInterval;
87
+ /**
88
+ * Stop the cleanup interval (for shutdown)
89
+ */
90
+ shutdown(): void;
91
+ /**
92
+ * Get cluster info for API response
93
+ */
94
+ getClusterInfo(localResources?: ServerResources): {
95
+ localServerId: string;
96
+ peerCount: number;
97
+ onlineCount: number;
98
+ servers: Array<{
99
+ id: string;
100
+ name?: string;
101
+ url: string;
102
+ status: string;
103
+ isLocal: boolean;
104
+ resources?: ServerResources;
105
+ }>;
106
+ };
107
+ }
108
+ export declare const serverRegistry: ServerRegistry;
109
+ export {};