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,94 @@
1
+ "use strict";
2
+ /**
3
+ * Cluster Management API Routes
4
+ *
5
+ * Endpoints for server registration and cluster management.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.default = clusterRoutes;
9
+ const server_registry_1 = require("../cluster/server-registry");
10
+ const resource_service_1 = require("../resources/resource-service");
11
+ const resourceService = (0, resource_service_1.getResourceService)();
12
+ async function clusterRoutes(fastify) {
13
+ /**
14
+ * GET /servers
15
+ * List all servers in the cluster (local + registered peers)
16
+ * Includes resource info for all servers
17
+ */
18
+ fastify.get('/servers', async (_request, reply) => {
19
+ // Get local resources (cached, never blocks)
20
+ const localResources = resourceService.getResources();
21
+ const clusterInfo = server_registry_1.serverRegistry.getClusterInfo(localResources);
22
+ return reply.send(clusterInfo);
23
+ });
24
+ /**
25
+ * POST /servers/register
26
+ * Register a peer server with this server
27
+ * Body can include { resources } for initial resource info
28
+ */
29
+ fastify.post('/servers/register', async (request, reply) => {
30
+ const { host, port, name, secret, resources } = request.body;
31
+ if (!host || !port) {
32
+ return reply.code(400).send({ error: 'host and port are required' });
33
+ }
34
+ const result = server_registry_1.serverRegistry.register({ host, port, name, secret, resources });
35
+ if (!result.success) {
36
+ return reply.code(403).send({ error: result.error });
37
+ }
38
+ return reply.send({
39
+ registered: true,
40
+ serverId: result.serverId,
41
+ });
42
+ });
43
+ /**
44
+ * DELETE /servers/:serverId
45
+ * Unregister a peer server
46
+ */
47
+ fastify.delete('/servers/:serverId', async (request, reply) => {
48
+ const { serverId } = request.params;
49
+ const { secret } = request.body;
50
+ // Validate secret if configured
51
+ const clusterSecret = process.env.NEBULA_CLUSTER_SECRET;
52
+ if (clusterSecret && secret !== clusterSecret) {
53
+ return reply.code(403).send({ error: 'Invalid cluster secret' });
54
+ }
55
+ const removed = server_registry_1.serverRegistry.unregister(serverId);
56
+ if (!removed) {
57
+ return reply.code(404).send({ error: 'Server not found' });
58
+ }
59
+ return reply.send({ unregistered: true });
60
+ });
61
+ /**
62
+ * POST /servers/:serverId/heartbeat
63
+ * Record heartbeat from a peer server
64
+ * Body can include { resources } for system resource info
65
+ */
66
+ fastify.post('/servers/:serverId/heartbeat', async (request, reply) => {
67
+ const { serverId } = request.params;
68
+ const { resources } = request.body;
69
+ const success = server_registry_1.serverRegistry.heartbeat(serverId, resources);
70
+ if (!success) {
71
+ return reply.code(404).send({ error: 'Server not found' });
72
+ }
73
+ return reply.send({ ok: true });
74
+ });
75
+ /**
76
+ * GET /servers/:serverId
77
+ * Get info about a specific server
78
+ */
79
+ fastify.get('/servers/:serverId', async (request, reply) => {
80
+ const { serverId } = request.params;
81
+ const server = server_registry_1.serverRegistry.getServer(serverId);
82
+ if (!server) {
83
+ return reply.code(404).send({ error: 'Server not found' });
84
+ }
85
+ return reply.send({
86
+ id: server.id,
87
+ name: server.name,
88
+ url: server.url,
89
+ status: server.status,
90
+ registeredAt: server.registeredAt,
91
+ lastHeartbeat: server.lastHeartbeat,
92
+ });
93
+ });
94
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Filesystem API Routes
3
+ */
4
+ import { FastifyInstance } from 'fastify';
5
+ import { fsService } from '../fs/fs-service';
6
+ export default function fsRoutes(fastify: FastifyInstance): Promise<void>;
7
+ export { fsService };
@@ -0,0 +1,392 @@
1
+ "use strict";
2
+ /**
3
+ * Filesystem API Routes
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.fsService = void 0;
40
+ exports.default = fsRoutes;
41
+ const fs_service_1 = require("../fs/fs-service");
42
+ Object.defineProperty(exports, "fsService", { enumerable: true, get: function () { return fs_service_1.fsService; } });
43
+ const path = __importStar(require("path"));
44
+ const os = __importStar(require("os"));
45
+ const nodeFs = __importStar(require("fs"));
46
+ async function fsRoutes(fastify) {
47
+ /**
48
+ * List directory contents
49
+ */
50
+ fastify.get('/fs/list', async (request, reply) => {
51
+ try {
52
+ const dirPath = request.query.path || '~';
53
+ const result = fs_service_1.fsService.listDirectory(dirPath);
54
+ return reply.send(result);
55
+ }
56
+ catch (err) {
57
+ if (err instanceof Error) {
58
+ if (err.message.includes('not found') || err.message.includes('ENOENT')) {
59
+ return reply.code(404).send({ detail: err.message });
60
+ }
61
+ else if (err.message.includes('permission') || err.message.includes('EACCES')) {
62
+ return reply.code(403).send({ detail: err.message });
63
+ }
64
+ else {
65
+ return reply.code(500).send({ detail: err.message });
66
+ }
67
+ }
68
+ else {
69
+ return reply.code(500).send({ detail: 'Unknown error' });
70
+ }
71
+ }
72
+ });
73
+ /**
74
+ * Get current server root directory
75
+ */
76
+ fastify.get('/fs/root', async (_request, reply) => {
77
+ return reply.send({ root: fs_service_1.fsService.getRootDirectory() });
78
+ });
79
+ /**
80
+ * Set server root directory
81
+ */
82
+ fastify.post('/fs/root', async (request, reply) => {
83
+ const { root } = request.body || {};
84
+ if (!root || typeof root !== 'string') {
85
+ return reply.code(400).send({ detail: 'root is required' });
86
+ }
87
+ try {
88
+ const updated = fs_service_1.fsService.setRootDirectory(root);
89
+ return reply.send({ root: updated });
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : 'Failed to set root';
93
+ return reply.code(400).send({ detail: message });
94
+ }
95
+ });
96
+ /**
97
+ * Get directory modification time
98
+ */
99
+ fastify.get('/fs/mtime', async (request, reply) => {
100
+ try {
101
+ const dirPath = request.query.path || '~';
102
+ const result = fs_service_1.fsService.getDirectoryMtime(dirPath);
103
+ return reply.send(result);
104
+ }
105
+ catch (err) {
106
+ if (err instanceof Error && err.message.includes('not found')) {
107
+ return reply.code(404).send({ detail: err.message });
108
+ }
109
+ else {
110
+ const message = err instanceof Error ? err.message : 'Unknown error';
111
+ return reply.code(500).send({ detail: message });
112
+ }
113
+ }
114
+ });
115
+ /**
116
+ * Get file modification time
117
+ */
118
+ fastify.get('/fs/file-mtime', async (request, reply) => {
119
+ try {
120
+ const filePath = request.query.path;
121
+ if (!filePath) {
122
+ return reply.code(400).send({ detail: 'path query parameter is required' });
123
+ }
124
+ const result = fs_service_1.fsService.getFileMtime(filePath);
125
+ return reply.send(result);
126
+ }
127
+ catch (err) {
128
+ if (err instanceof Error && err.message.includes('not found')) {
129
+ return reply.code(404).send({ detail: err.message });
130
+ }
131
+ else {
132
+ const message = err instanceof Error ? err.message : 'Unknown error';
133
+ return reply.code(500).send({ detail: message });
134
+ }
135
+ }
136
+ });
137
+ /**
138
+ * Read file contents
139
+ */
140
+ fastify.get('/fs/read', async (request, reply) => {
141
+ try {
142
+ const filePath = request.query.path;
143
+ if (!filePath) {
144
+ return reply.code(400).send({ detail: 'path query parameter is required' });
145
+ }
146
+ const result = fs_service_1.fsService.readFile(filePath);
147
+ return reply.send(result);
148
+ }
149
+ catch (err) {
150
+ if (err instanceof Error) {
151
+ if (err.message.includes('not found') || err.message.includes('ENOENT')) {
152
+ return reply.code(404).send({ detail: err.message });
153
+ }
154
+ else if (err.message.includes('directory') || err.message.includes('EISDIR')) {
155
+ return reply.code(400).send({ detail: err.message });
156
+ }
157
+ else {
158
+ return reply.code(500).send({ detail: err.message });
159
+ }
160
+ }
161
+ else {
162
+ return reply.code(500).send({ detail: 'Unknown error' });
163
+ }
164
+ }
165
+ });
166
+ /**
167
+ * Write content to a file
168
+ */
169
+ fastify.post('/fs/write', async (request, reply) => {
170
+ try {
171
+ const { path: filePath, content, file_type = 'text' } = request.body;
172
+ if (!filePath) {
173
+ return reply.code(400).send({ detail: 'path is required' });
174
+ }
175
+ fs_service_1.fsService.writeFile(filePath, content, file_type);
176
+ return reply.send({ status: 'ok', path: filePath });
177
+ }
178
+ catch (err) {
179
+ const message = err instanceof Error ? err.message : 'Unknown error';
180
+ return reply.code(500).send({ detail: message });
181
+ }
182
+ });
183
+ /**
184
+ * Create a new file or directory
185
+ */
186
+ fastify.post('/fs/create', async (request, reply) => {
187
+ try {
188
+ const { path: filePath, is_directory = false } = request.body;
189
+ if (!filePath) {
190
+ return reply.code(400).send({ detail: 'path is required' });
191
+ }
192
+ const info = fs_service_1.fsService.createFile(filePath, is_directory);
193
+ return reply.send({ status: 'ok', file: info });
194
+ }
195
+ catch (err) {
196
+ if (err instanceof Error && err.message.includes('exists')) {
197
+ return reply.code(409).send({ detail: err.message });
198
+ }
199
+ else {
200
+ const message = err instanceof Error ? err.message : 'Unknown error';
201
+ return reply.code(500).send({ detail: message });
202
+ }
203
+ }
204
+ });
205
+ /**
206
+ * Delete a file or directory
207
+ */
208
+ fastify.delete('/fs/delete', async (request, reply) => {
209
+ try {
210
+ const filePath = request.query.path;
211
+ if (!filePath) {
212
+ return reply.code(400).send({ detail: 'path query parameter is required' });
213
+ }
214
+ fs_service_1.fsService.deleteFile(filePath);
215
+ return reply.send({ status: 'ok' });
216
+ }
217
+ catch (err) {
218
+ if (err instanceof Error && err.message.includes('not found')) {
219
+ return reply.code(404).send({ detail: err.message });
220
+ }
221
+ else {
222
+ const message = err instanceof Error ? err.message : 'Unknown error';
223
+ return reply.code(500).send({ detail: message });
224
+ }
225
+ }
226
+ });
227
+ /**
228
+ * Rename/move a file or directory
229
+ */
230
+ fastify.post('/fs/rename', async (request, reply) => {
231
+ try {
232
+ const { old_path, new_path } = request.body;
233
+ if (!old_path || !new_path) {
234
+ return reply.code(400).send({ detail: 'old_path and new_path are required' });
235
+ }
236
+ const info = fs_service_1.fsService.renameFile(old_path, new_path);
237
+ return reply.send({ status: 'ok', file: info });
238
+ }
239
+ catch (err) {
240
+ if (err instanceof Error) {
241
+ if (err.message.includes('not found')) {
242
+ return reply.code(404).send({ detail: err.message });
243
+ }
244
+ else if (err.message.includes('exists')) {
245
+ return reply.code(409).send({ detail: err.message });
246
+ }
247
+ else {
248
+ return reply.code(500).send({ detail: err.message });
249
+ }
250
+ }
251
+ else {
252
+ return reply.code(500).send({ detail: 'Unknown error' });
253
+ }
254
+ }
255
+ });
256
+ /**
257
+ * Duplicate a file or directory
258
+ */
259
+ fastify.post('/fs/duplicate', async (request, reply) => {
260
+ try {
261
+ const { path: filePath } = request.body;
262
+ if (!filePath) {
263
+ return reply.code(400).send({ detail: 'path is required' });
264
+ }
265
+ const info = fs_service_1.fsService.duplicateFile(filePath);
266
+ return reply.send({ status: 'ok', file: info });
267
+ }
268
+ catch (err) {
269
+ if (err instanceof Error) {
270
+ if (err.message.includes('not found')) {
271
+ return reply.code(404).send({ detail: err.message });
272
+ }
273
+ else {
274
+ return reply.code(500).send({ detail: err.message });
275
+ }
276
+ }
277
+ else {
278
+ return reply.code(500).send({ detail: 'Unknown error' });
279
+ }
280
+ }
281
+ });
282
+ /**
283
+ * Download a file (raw stream with proper headers)
284
+ */
285
+ fastify.get('/fs/download', async (request, reply) => {
286
+ try {
287
+ const filePath = request.query.path;
288
+ if (!filePath) {
289
+ return reply.code(400).send({ detail: 'path query parameter is required' });
290
+ }
291
+ const normalizedPath = fs_service_1.fsService.normalizePath(filePath);
292
+ if (!nodeFs.existsSync(normalizedPath)) {
293
+ return reply.code(404).send({ detail: `File not found: ${normalizedPath}` });
294
+ }
295
+ const stat = nodeFs.statSync(normalizedPath);
296
+ if (stat.isDirectory()) {
297
+ return reply.code(400).send({ detail: 'Cannot download a directory' });
298
+ }
299
+ const filename = path.basename(normalizedPath);
300
+ const extension = path.extname(normalizedPath).toLowerCase();
301
+ // Set content type based on extension
302
+ const mimeTypes = {
303
+ '.txt': 'text/plain',
304
+ '.json': 'application/json',
305
+ '.ipynb': 'application/json',
306
+ '.py': 'text/x-python',
307
+ '.js': 'application/javascript',
308
+ '.ts': 'application/typescript',
309
+ '.html': 'text/html',
310
+ '.css': 'text/css',
311
+ '.csv': 'text/csv',
312
+ '.md': 'text/markdown',
313
+ '.png': 'image/png',
314
+ '.jpg': 'image/jpeg',
315
+ '.jpeg': 'image/jpeg',
316
+ '.gif': 'image/gif',
317
+ '.svg': 'image/svg+xml',
318
+ '.pdf': 'application/pdf',
319
+ '.zip': 'application/zip',
320
+ };
321
+ const contentType = mimeTypes[extension] || 'application/octet-stream';
322
+ const stream = nodeFs.createReadStream(normalizedPath);
323
+ return reply
324
+ .header('Content-Type', contentType)
325
+ .header('Content-Disposition', `attachment; filename="${filename}"`)
326
+ .header('Content-Length', stat.size)
327
+ .send(stream);
328
+ }
329
+ catch (err) {
330
+ if (err instanceof Error) {
331
+ if (err.message.includes('not found') || err.message.includes('ENOENT')) {
332
+ return reply.code(404).send({ detail: err.message });
333
+ }
334
+ else {
335
+ return reply.code(500).send({ detail: err.message });
336
+ }
337
+ }
338
+ else {
339
+ return reply.code(500).send({ detail: 'Unknown error' });
340
+ }
341
+ }
342
+ });
343
+ /**
344
+ * Upload a file
345
+ * Uses @fastify/multipart (registered in index.ts)
346
+ */
347
+ fastify.post('/fs/upload', async (request, reply) => {
348
+ try {
349
+ const data = await request.file();
350
+ if (!data) {
351
+ return reply.code(400).send({ detail: 'file is required' });
352
+ }
353
+ // Read the dest path from the fields
354
+ const destPathField = data.fields.path;
355
+ const destPath = destPathField && 'value' in destPathField ? destPathField.value : undefined;
356
+ if (!destPath) {
357
+ return reply.code(400).send({ detail: 'path is required' });
358
+ }
359
+ // Save the uploaded file to a temp location first
360
+ const tmpPath = path.join(os.tmpdir(), `upload-${Date.now()}-${data.filename}`);
361
+ const writeStream = nodeFs.createWriteStream(tmpPath);
362
+ await new Promise((resolve, reject) => {
363
+ data.file.pipe(writeStream);
364
+ writeStream.on('finish', resolve);
365
+ writeStream.on('error', reject);
366
+ });
367
+ const info = await fs_service_1.fsService.uploadFile(destPath, tmpPath, data.filename);
368
+ // Clean up temp file
369
+ try {
370
+ nodeFs.unlinkSync(tmpPath);
371
+ }
372
+ catch { /* ignore */ }
373
+ return reply.send({ status: 'ok', file: info });
374
+ }
375
+ catch (err) {
376
+ if (err instanceof Error) {
377
+ if (err.message.includes('not found')) {
378
+ return reply.code(404).send({ detail: err.message });
379
+ }
380
+ else if (err.message.includes('permission')) {
381
+ return reply.code(403).send({ detail: err.message });
382
+ }
383
+ else {
384
+ return reply.code(500).send({ detail: err.message });
385
+ }
386
+ }
387
+ else {
388
+ return reply.code(500).send({ detail: 'Unknown error' });
389
+ }
390
+ }
391
+ });
392
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Kernel API Routes
3
+ */
4
+ import { FastifyInstance } from 'fastify';
5
+ import { WebSocketServer } from 'ws';
6
+ import { KernelService } from '../kernel/kernel-service';
7
+ declare const kernelService: KernelService;
8
+ /**
9
+ * Setup WebSocket handler for kernel execution
10
+ */
11
+ export declare function setupKernelWebSocket(wss: WebSocketServer): void;
12
+ export default function kernelRoutes(fastify: FastifyInstance): Promise<void>;
13
+ export { kernelService };