mrmd-server 0.2.3 → 0.2.4
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/package.json +1 -1
- package/src/server.js +25 -3
- package/src/sync-manager.js +1 -59
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -13,6 +13,7 @@ import fs from 'fs/promises';
|
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
15
15
|
import { createRequire } from 'module';
|
|
16
|
+
import { Readable } from 'stream';
|
|
16
17
|
|
|
17
18
|
const require = createRequire(import.meta.url);
|
|
18
19
|
|
|
@@ -241,9 +242,30 @@ export async function createServer(config) {
|
|
|
241
242
|
}
|
|
242
243
|
});
|
|
243
244
|
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
// Stream response body to preserve SSE/chunked behavior for execute/stream.
|
|
246
|
+
// Also works for binary assets without buffering entire payload in memory.
|
|
247
|
+
if (!response.body) {
|
|
248
|
+
res.end();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const contentType = (response.headers.get('content-type') || '').toLowerCase();
|
|
253
|
+
if (contentType.includes('text/event-stream')) {
|
|
254
|
+
// Keep SSE connection semantics explicit on downstream response
|
|
255
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
256
|
+
res.setHeader('Connection', 'keep-alive');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const nodeStream = Readable.fromWeb(response.body);
|
|
260
|
+
nodeStream.on('error', (streamErr) => {
|
|
261
|
+
console.error(`[proxy] Stream error from ${targetUrl}:`, streamErr.message);
|
|
262
|
+
if (!res.headersSent) {
|
|
263
|
+
res.status(502).json({ error: `Proxy stream error: ${streamErr.message}` });
|
|
264
|
+
} else {
|
|
265
|
+
res.end();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
nodeStream.pipe(res);
|
|
247
269
|
} catch (err) {
|
|
248
270
|
console.error(`[proxy] Failed to proxy to ${targetUrl}:`, err.message);
|
|
249
271
|
res.status(502).json({ error: `Proxy error: ${err.message}` });
|
package/src/sync-manager.js
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Ported from mrmd-electron/main.js to provide dynamic per-project sync servers.
|
|
5
5
|
* Allows mrmd-server to handle files from any project, not just a fixed projectDir.
|
|
6
|
-
*
|
|
7
|
-
* Supports three sync modes (via SYNC_MODE env var):
|
|
8
|
-
* - legacy: spawn local mrmd-sync per project (default)
|
|
9
|
-
* - mirror: same as legacy (mirroring handled by orchestrator WS proxy)
|
|
10
|
-
* - relay_primary: use relay-client to sync project dir with cloud relay
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
8
|
import { spawn } from 'child_process';
|
|
@@ -24,10 +19,6 @@ import { SYNC_SERVER_MEMORY_MB, DIR_HASH_LENGTH } from 'mrmd-electron/src/config
|
|
|
24
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
20
|
const __dirname = path.dirname(__filename);
|
|
26
21
|
|
|
27
|
-
const SYNC_MODE = process.env.SYNC_MODE || 'legacy';
|
|
28
|
-
const SYNC_RELAY_URL = process.env.SYNC_RELAY_URL || 'ws://localhost:3006';
|
|
29
|
-
const CLOUD_USER_ID = process.env.CLOUD_USER_ID || '';
|
|
30
|
-
|
|
31
22
|
// Track active sync servers by directory hash
|
|
32
23
|
const syncServers = new Map();
|
|
33
24
|
|
|
@@ -103,9 +94,6 @@ export function onSyncDeath(listener) {
|
|
|
103
94
|
* Get or start a sync server for a project directory
|
|
104
95
|
* Uses reference counting so multiple documents can share a sync server
|
|
105
96
|
*
|
|
106
|
-
* In relay_primary mode, creates a relay client instead of a local sync server.
|
|
107
|
-
* The relay client syncs the project dir with the cloud relay bidirectionally.
|
|
108
|
-
*
|
|
109
97
|
* @param {string} projectDir - The project directory to sync
|
|
110
98
|
* @returns {Promise<{port: number, dir: string, refCount: number}>}
|
|
111
99
|
*/
|
|
@@ -116,56 +104,10 @@ export async function acquireSyncServer(projectDir) {
|
|
|
116
104
|
if (syncServers.has(dirHash)) {
|
|
117
105
|
const server = syncServers.get(dirHash);
|
|
118
106
|
server.refCount++;
|
|
119
|
-
console.log(`[sync] Reusing server for ${projectDir} (refCount: ${server.refCount})`);
|
|
107
|
+
console.log(`[sync] Reusing server for ${projectDir} on port ${server.port} (refCount: ${server.refCount})`);
|
|
120
108
|
return server;
|
|
121
109
|
}
|
|
122
110
|
|
|
123
|
-
// ── Relay-primary mode: use relay client instead of local sync server ──
|
|
124
|
-
if (SYNC_MODE === 'relay_primary' && CLOUD_USER_ID) {
|
|
125
|
-
// Derive project name from the directory name
|
|
126
|
-
const projectName = path.basename(projectDir);
|
|
127
|
-
|
|
128
|
-
console.log(`[sync] Starting relay client for ${projectDir} (project: ${projectName})`);
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
// Dynamic import so this doesn't fail in environments without relay-client
|
|
132
|
-
const { createRelayClient } = await import('mrmd-sync/src/relay-client.js');
|
|
133
|
-
|
|
134
|
-
const relayClient = createRelayClient({
|
|
135
|
-
relayUrl: SYNC_RELAY_URL,
|
|
136
|
-
projectDir,
|
|
137
|
-
userId: CLOUD_USER_ID,
|
|
138
|
-
project: projectName,
|
|
139
|
-
writeDebounceMs: 1000,
|
|
140
|
-
log: (msg) => console.log(msg),
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
await relayClient.start();
|
|
144
|
-
|
|
145
|
-
// Return a server-like object that the rest of the code can use.
|
|
146
|
-
// port=0 signals that sync is handled by the relay (not a local WS server).
|
|
147
|
-
// The orchestrator's WS proxy routes sync traffic directly to the relay.
|
|
148
|
-
const server = {
|
|
149
|
-
proc: null,
|
|
150
|
-
port: 0,
|
|
151
|
-
dir: projectDir,
|
|
152
|
-
refCount: 1,
|
|
153
|
-
owned: true,
|
|
154
|
-
isRelay: true,
|
|
155
|
-
relayClient,
|
|
156
|
-
};
|
|
157
|
-
syncServers.set(dirHash, server);
|
|
158
|
-
console.log(`[sync] Relay client started for ${projectDir}`);
|
|
159
|
-
return server;
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.error(`[sync] Failed to start relay client for ${projectDir}: ${err.message}`);
|
|
162
|
-
console.warn(`[sync] Falling back to local sync server`);
|
|
163
|
-
// Fall through to local server below
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ── Legacy / mirror mode: spawn local mrmd-sync process ──
|
|
168
|
-
|
|
169
111
|
// Check for existing server from a PID file (in case of restart)
|
|
170
112
|
const syncStatePath = path.join(os.tmpdir(), `mrmd-sync-${dirHash}`, 'server.pid');
|
|
171
113
|
try {
|