mrmd-server 0.1.0 → 0.1.1
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/LICENSE +21 -0
- package/bin/cli.js +4 -20
- package/package.json +20 -3
- package/src/api/bash.js +72 -189
- package/src/api/file.js +26 -20
- package/src/api/index.js +5 -0
- package/src/api/notebook.js +290 -0
- package/src/api/project.js +178 -12
- package/src/api/pty.js +73 -293
- package/src/api/r.js +337 -0
- package/src/api/session.js +96 -251
- package/src/api/settings.js +782 -0
- package/src/api/system.js +199 -1
- package/src/server.js +117 -6
- package/src/services.js +42 -0
- package/src/sync-manager.js +223 -0
- package/static/favicon.png +0 -0
- package/static/http-shim.js +172 -3
- package/static/index.html +1 -0
package/static/http-shim.js
CHANGED
|
@@ -130,6 +130,12 @@
|
|
|
130
130
|
|
|
131
131
|
getAi: () => GET('/api/system/ai'),
|
|
132
132
|
|
|
133
|
+
// System info and uv management
|
|
134
|
+
system: {
|
|
135
|
+
info: () => GET('/api/system/info'),
|
|
136
|
+
ensureUv: () => POST('/api/system/ensure-uv', {}),
|
|
137
|
+
},
|
|
138
|
+
|
|
133
139
|
// ========================================================================
|
|
134
140
|
// Shell (stubs for browser)
|
|
135
141
|
// ========================================================================
|
|
@@ -191,6 +197,9 @@
|
|
|
191
197
|
// Python management
|
|
192
198
|
// ========================================================================
|
|
193
199
|
|
|
200
|
+
createVenv: (venvPath) =>
|
|
201
|
+
POST('/api/system/create-venv', { venvPath }),
|
|
202
|
+
|
|
194
203
|
installMrmdPython: (venvPath) =>
|
|
195
204
|
POST('/api/system/install-mrmd-python', { venvPath }),
|
|
196
205
|
|
|
@@ -212,11 +221,24 @@
|
|
|
212
221
|
// ========================================================================
|
|
213
222
|
|
|
214
223
|
openFile: async (filePath) => {
|
|
215
|
-
//
|
|
216
|
-
// We'll get project info and session info separately
|
|
224
|
+
// Get project info and session info
|
|
217
225
|
const project = await window.electronAPI.project.get(filePath);
|
|
218
226
|
const session = await window.electronAPI.session.forDocument(filePath);
|
|
219
|
-
|
|
227
|
+
|
|
228
|
+
// Extract filename without extension for docName
|
|
229
|
+
const fileName = filePath.split('/').pop();
|
|
230
|
+
const docName = fileName.replace(/\.md$/, '');
|
|
231
|
+
|
|
232
|
+
// Use syncPort from project response (dynamically assigned per-project)
|
|
233
|
+
const syncPort = project?.syncPort || 4444;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
success: true,
|
|
237
|
+
syncPort,
|
|
238
|
+
docName,
|
|
239
|
+
projectDir: project?.root || filePath.split('/').slice(0, -1).join('/'),
|
|
240
|
+
pythonPort: session?.pythonPort || null,
|
|
241
|
+
};
|
|
220
242
|
},
|
|
221
243
|
|
|
222
244
|
// ========================================================================
|
|
@@ -280,6 +302,144 @@
|
|
|
280
302
|
forDocument: (documentPath) => POST('/api/bash/for-document', { documentPath }),
|
|
281
303
|
},
|
|
282
304
|
|
|
305
|
+
// ========================================================================
|
|
306
|
+
// JULIA SESSION SERVICE
|
|
307
|
+
// ========================================================================
|
|
308
|
+
|
|
309
|
+
julia: {
|
|
310
|
+
list: () => GET('/api/julia'),
|
|
311
|
+
|
|
312
|
+
start: (config) => POST('/api/julia', { config }),
|
|
313
|
+
|
|
314
|
+
stop: (sessionName) => DELETE(`/api/julia/${encodeURIComponent(sessionName)}`),
|
|
315
|
+
|
|
316
|
+
restart: (sessionName) => POST(`/api/julia/${encodeURIComponent(sessionName)}/restart`, {}),
|
|
317
|
+
|
|
318
|
+
forDocument: (documentPath) => POST('/api/julia/for-document', { documentPath }),
|
|
319
|
+
|
|
320
|
+
isAvailable: () => GET('/api/julia/available').then(r => r.available),
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
// ========================================================================
|
|
324
|
+
// PTY SESSION SERVICE (for ```term blocks)
|
|
325
|
+
// ========================================================================
|
|
326
|
+
|
|
327
|
+
pty: {
|
|
328
|
+
list: () => GET('/api/pty'),
|
|
329
|
+
|
|
330
|
+
start: (config) => POST('/api/pty', { config }),
|
|
331
|
+
|
|
332
|
+
stop: (sessionName) => DELETE(`/api/pty/${encodeURIComponent(sessionName)}`),
|
|
333
|
+
|
|
334
|
+
restart: (sessionName) => POST(`/api/pty/${encodeURIComponent(sessionName)}/restart`, {}),
|
|
335
|
+
|
|
336
|
+
forDocument: (documentPath) => POST('/api/pty/for-document', { documentPath }),
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// ========================================================================
|
|
340
|
+
// NOTEBOOK (JUPYTER) SERVICE
|
|
341
|
+
// ========================================================================
|
|
342
|
+
|
|
343
|
+
notebook: {
|
|
344
|
+
convert: (ipynbPath) => POST('/api/notebook/convert', { ipynbPath }),
|
|
345
|
+
|
|
346
|
+
startSync: (ipynbPath) => POST('/api/notebook/start-sync', { ipynbPath }),
|
|
347
|
+
|
|
348
|
+
stopSync: (ipynbPath) => POST('/api/notebook/stop-sync', { ipynbPath }),
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
// ========================================================================
|
|
352
|
+
// R SESSION SERVICE
|
|
353
|
+
// ========================================================================
|
|
354
|
+
|
|
355
|
+
r: {
|
|
356
|
+
list: () => GET('/api/r'),
|
|
357
|
+
|
|
358
|
+
start: (config) => POST('/api/r', { config }),
|
|
359
|
+
|
|
360
|
+
stop: (sessionName) => DELETE(`/api/r/${encodeURIComponent(sessionName)}`),
|
|
361
|
+
|
|
362
|
+
restart: (sessionName) => POST(`/api/r/${encodeURIComponent(sessionName)}/restart`, {}),
|
|
363
|
+
|
|
364
|
+
forDocument: (documentPath) => POST('/api/r/for-document', { documentPath }),
|
|
365
|
+
|
|
366
|
+
isAvailable: () => GET('/api/r/available').then(r => r.available),
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
// ========================================================================
|
|
370
|
+
// SETTINGS SERVICE
|
|
371
|
+
// ========================================================================
|
|
372
|
+
|
|
373
|
+
settings: {
|
|
374
|
+
getAll: () => GET('/api/settings'),
|
|
375
|
+
|
|
376
|
+
get: (key, defaultValue) =>
|
|
377
|
+
GET(`/api/settings/key?path=${encodeURIComponent(key)}${defaultValue !== undefined ? `&default=${encodeURIComponent(defaultValue)}` : ''}`).then(r => r.value),
|
|
378
|
+
|
|
379
|
+
set: (key, value) =>
|
|
380
|
+
POST('/api/settings/key', { key, value }).then(r => r.success),
|
|
381
|
+
|
|
382
|
+
update: (updates) =>
|
|
383
|
+
POST('/api/settings/update', { updates }).then(r => r.success),
|
|
384
|
+
|
|
385
|
+
reset: () =>
|
|
386
|
+
POST('/api/settings/reset', {}).then(r => r.success),
|
|
387
|
+
|
|
388
|
+
getApiKeys: (masked = true) =>
|
|
389
|
+
GET(`/api/settings/api-keys?masked=${masked}`),
|
|
390
|
+
|
|
391
|
+
setApiKey: (provider, key) =>
|
|
392
|
+
POST('/api/settings/api-key', { provider, key }).then(r => r.success),
|
|
393
|
+
|
|
394
|
+
getApiKey: (provider) =>
|
|
395
|
+
GET(`/api/settings/api-key/${encodeURIComponent(provider)}`).then(r => r.key),
|
|
396
|
+
|
|
397
|
+
hasApiKey: (provider) =>
|
|
398
|
+
GET(`/api/settings/api-key/${encodeURIComponent(provider)}/exists`).then(r => r.hasKey),
|
|
399
|
+
|
|
400
|
+
getApiProviders: () =>
|
|
401
|
+
GET('/api/settings/api-providers'),
|
|
402
|
+
|
|
403
|
+
getQualityLevels: () =>
|
|
404
|
+
GET('/api/settings/quality-levels'),
|
|
405
|
+
|
|
406
|
+
setQualityLevelModel: (level, model) =>
|
|
407
|
+
POST(`/api/settings/quality-level/${level}/model`, { model }).then(r => r.success),
|
|
408
|
+
|
|
409
|
+
getCustomSections: () =>
|
|
410
|
+
GET('/api/settings/custom-sections'),
|
|
411
|
+
|
|
412
|
+
addCustomSection: (name) =>
|
|
413
|
+
POST('/api/settings/custom-section', { name }),
|
|
414
|
+
|
|
415
|
+
removeCustomSection: (sectionId) =>
|
|
416
|
+
DELETE(`/api/settings/custom-section/${encodeURIComponent(sectionId)}`).then(r => r.success),
|
|
417
|
+
|
|
418
|
+
addCustomCommand: (sectionId, command) =>
|
|
419
|
+
POST('/api/settings/custom-command', { sectionId, command }),
|
|
420
|
+
|
|
421
|
+
updateCustomCommand: (sectionId, commandId, updates) =>
|
|
422
|
+
apiCall('PUT', '/api/settings/custom-command', { sectionId, commandId, updates }).then(r => r.success),
|
|
423
|
+
|
|
424
|
+
removeCustomCommand: (sectionId, commandId) =>
|
|
425
|
+
apiCall('DELETE', '/api/settings/custom-command', { sectionId, commandId }).then(r => r.success),
|
|
426
|
+
|
|
427
|
+
getAllCustomCommands: () =>
|
|
428
|
+
GET('/api/settings/custom-commands'),
|
|
429
|
+
|
|
430
|
+
getDefaults: () =>
|
|
431
|
+
GET('/api/settings/defaults'),
|
|
432
|
+
|
|
433
|
+
setDefaults: (defaults) =>
|
|
434
|
+
POST('/api/settings/defaults', defaults).then(r => r.success),
|
|
435
|
+
|
|
436
|
+
export: (includeKeys = false) =>
|
|
437
|
+
GET(`/api/settings/export?includeKeys=${includeKeys}`).then(r => r.json),
|
|
438
|
+
|
|
439
|
+
import: (json, mergeKeys = false) =>
|
|
440
|
+
POST('/api/settings/import', { json, mergeKeys }).then(r => r.success),
|
|
441
|
+
},
|
|
442
|
+
|
|
283
443
|
// ========================================================================
|
|
284
444
|
// FILE SERVICE
|
|
285
445
|
// ========================================================================
|
|
@@ -353,6 +513,15 @@
|
|
|
353
513
|
// Remove existing handlers to prevent duplicates
|
|
354
514
|
eventHandlers['sync-server-died'] = [callback];
|
|
355
515
|
},
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Register callback for OS "open with" events.
|
|
519
|
+
* In browser mode, this will never be called (no OS integration).
|
|
520
|
+
*/
|
|
521
|
+
onOpenWithFile: (callback) => {
|
|
522
|
+
// No-op in browser mode - OS file associations don't exist
|
|
523
|
+
// Could potentially be triggered via URL parameters in the future
|
|
524
|
+
},
|
|
356
525
|
};
|
|
357
526
|
|
|
358
527
|
// ==========================================================================
|
package/static/index.html
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>mrmd</title>
|
|
7
|
+
<link rel="icon" type="image/png" href="/favicon.png">
|
|
7
8
|
|
|
8
9
|
<!-- Load the HTTP shim BEFORE anything else -->
|
|
9
10
|
<script src="/http-shim.js"></script>
|