monomind 2.0.3 → 2.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.
- package/package.json +9 -5
- package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
- package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
- package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
- package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
- package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +75 -18
- package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +16 -7
- package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +4 -4
- package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +40 -22
- package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
- package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
- package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +54 -4
- package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
- package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
- package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
- package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
- package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
- package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
- package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
- package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
- package/packages/@monomind/cli/bin/cli.js +10 -1
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
- package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
- package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
- package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
- package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +306 -129
- package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
- package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
- package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
- package/packages/@monomind/cli/dist/src/index.js +64 -28
- package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
- package/packages/@monomind/cli/dist/src/init/executor.js +63 -49
- package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
- package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
- package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +51 -34
- package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +65 -43
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
- package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +24 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.js +68 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +30 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +96 -7
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +105 -8
- package/packages/@monomind/cli/dist/src/orgrt/live.html +56 -0
- package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
- package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.js +72 -0
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
- package/packages/@monomind/cli/dist/src/output.js +12 -5
- package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
- package/packages/@monomind/cli/dist/src/parser.js +130 -5
- package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
- package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
- package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +40 -40
- package/packages/@monomind/cli/dist/src/ui/orgs.html +110 -11
- package/packages/@monomind/cli/dist/src/ui/server.mjs +304 -133
- package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
- package/packages/@monomind/cli/package.json +6 -2
|
@@ -2,6 +2,7 @@ import http from 'http';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
|
+
import crypto from 'crypto';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
import { createRequire } from 'module';
|
|
7
8
|
import { collectAll, getWatchPaths, collectProject, collectSessions, collectSwarm, collectSwarmHistory, appendSwarmHistory, collectSwarmEvents, getSwarmDataSize, cleanSwarmData, collectAgents, collectTokens, collectHooks, collectKnowledge, collectMetrics, collectMemory, collectMemoryFiles, collectSystem } from './collector.mjs';
|
|
@@ -242,12 +243,77 @@ async function _initRunDb(monoHome) {
|
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
// Single-writer guard for the run-events.db snapshot persist: bindServer() can start a
|
|
247
|
+
// SECOND dashboard instance on port+1 when 4242 is already taken (two projects' dashboards
|
|
248
|
+
// running simultaneously, or a stale-but-still-bound old instance). Each instance holds an
|
|
249
|
+
// independent in-memory sql.js copy; without a lock, each instance's periodic full-snapshot
|
|
250
|
+
// overwrite silently clobbers the other's accumulated events (last writer wins, no error).
|
|
251
|
+
// Claiming this lock before every snapshot write ensures only ONE live instance actually
|
|
252
|
+
// persists at a time — the other still runs, just skips its write until it can claim the
|
|
253
|
+
// lock (e.g. after the current holder exits and its lock goes stale). This doesn't merge
|
|
254
|
+
// both instances' events into one file, but it stops them from destructively overwriting
|
|
255
|
+
// each other. Pattern mirrors .claude/helpers/utils/fs-helpers.cjs's claimLock/releaseLock
|
|
256
|
+
// (wx-create, rename-to-reclaim-stale) — replicated inline here since this file ships
|
|
257
|
+
// standalone in the published package and can't depend on repo-local dev tooling.
|
|
258
|
+
function _runDbLockPath() {
|
|
259
|
+
return _runDbPath ? `${_runDbPath}.lock` : null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function _claimRunDbLock(staleMs) {
|
|
263
|
+
const lockPath = _runDbLockPath();
|
|
264
|
+
if (!lockPath) return false;
|
|
265
|
+
staleMs = staleMs || 5000;
|
|
266
|
+
const tryCreate = () => {
|
|
267
|
+
try {
|
|
268
|
+
fs.writeFileSync(lockPath, String(process.pid), { flag: 'wx' });
|
|
269
|
+
return true;
|
|
270
|
+
} catch (_) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
if (tryCreate()) return true;
|
|
275
|
+
try {
|
|
276
|
+
const stat = fs.statSync(lockPath);
|
|
277
|
+
if (Date.now() - stat.mtimeMs < staleMs) return false; // held by a live/fresh owner
|
|
278
|
+
// Stale lock — reclaim via atomic rename so only one racing process wins it.
|
|
279
|
+
const claimed = `${lockPath}.${process.pid}.${Date.now()}.stale`;
|
|
280
|
+
try {
|
|
281
|
+
fs.renameSync(lockPath, claimed);
|
|
282
|
+
} catch (_) {
|
|
283
|
+
return false; // another process already reclaimed it
|
|
284
|
+
}
|
|
285
|
+
try { fs.unlinkSync(claimed); } catch (_) {}
|
|
286
|
+
return tryCreate();
|
|
287
|
+
} catch (_) {
|
|
288
|
+
// Lock vanished between our failed create and this stat — retry once.
|
|
289
|
+
return tryCreate();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function _releaseRunDbLock() {
|
|
294
|
+
const lockPath = _runDbLockPath();
|
|
295
|
+
if (!lockPath) return;
|
|
296
|
+
try {
|
|
297
|
+
if (Number(fs.readFileSync(lockPath, 'utf8')) === process.pid) fs.unlinkSync(lockPath);
|
|
298
|
+
} catch (_) {}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function _writeRunDbSnapshot() {
|
|
302
|
+
if (!_runDb || !_runDbPath) return;
|
|
303
|
+
if (!_claimRunDbLock()) return; // another live instance owns the write right now
|
|
304
|
+
try {
|
|
305
|
+
fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export()));
|
|
306
|
+
} catch (_) {
|
|
307
|
+
// best-effort — matches prior swallow-on-error behavior
|
|
308
|
+
} finally {
|
|
309
|
+
_releaseRunDbLock();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
245
313
|
function _persistRunDb() {
|
|
246
314
|
if (!_runDb || !_runDbPath) return;
|
|
247
315
|
clearTimeout(_runDbPersistTimer);
|
|
248
|
-
_runDbPersistTimer = setTimeout(
|
|
249
|
-
try { fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export())); } catch (_) {}
|
|
250
|
-
}, 1000);
|
|
316
|
+
_runDbPersistTimer = setTimeout(_writeRunDbSnapshot, 1000);
|
|
251
317
|
}
|
|
252
318
|
|
|
253
319
|
function _insertRunEvent(ev, source) {
|
|
@@ -266,6 +332,50 @@ function _insertRunEvent(ev, source) {
|
|
|
266
332
|
}
|
|
267
333
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
268
334
|
|
|
335
|
+
// ── Bounded file reads for session JSONL (P3-6b) ────────────────────────────
|
|
336
|
+
// /api/session already caps memory correctly by tailing; /api/session-journal and
|
|
337
|
+
// /api/session-errors used to fs.readFileSync() whole session files regardless of
|
|
338
|
+
// size, so one request against a multi-hundred-MB session log (a real scenario for
|
|
339
|
+
// long-running org sessions) buffers the entire file. These helpers cap the read to
|
|
340
|
+
// a fixed byte window via openSync/readSync (mirrors the byte-offset tail-read the
|
|
341
|
+
// /api/loops handler already uses for ScheduleWakeup lookups) instead of loading the
|
|
342
|
+
// whole file.
|
|
343
|
+
function _readTailLines(filePath, maxBytes) {
|
|
344
|
+
maxBytes = maxBytes || 4 * 1024 * 1024; // 4MB — generous for the most recent activity
|
|
345
|
+
const stat = fs.statSync(filePath);
|
|
346
|
+
const start = Math.max(0, stat.size - maxBytes);
|
|
347
|
+
const len = stat.size - start;
|
|
348
|
+
const buf = Buffer.alloc(len);
|
|
349
|
+
const fd = fs.openSync(filePath, 'r');
|
|
350
|
+
try {
|
|
351
|
+
fs.readSync(fd, buf, 0, len, start);
|
|
352
|
+
} finally {
|
|
353
|
+
fs.closeSync(fd);
|
|
354
|
+
}
|
|
355
|
+
let text = buf.toString('utf8');
|
|
356
|
+
if (start > 0) {
|
|
357
|
+
// We started mid-file — the first line is very likely a partial line, drop it.
|
|
358
|
+
const nl = text.indexOf('\n');
|
|
359
|
+
text = nl === -1 ? '' : text.slice(nl + 1);
|
|
360
|
+
}
|
|
361
|
+
return text.split('\n').filter(Boolean);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function _readHeadText(filePath, maxBytes) {
|
|
365
|
+
maxBytes = maxBytes || 8192; // enough for the JSONL header line(s) we need to inspect
|
|
366
|
+
const stat = fs.statSync(filePath);
|
|
367
|
+
const len = Math.min(maxBytes, stat.size);
|
|
368
|
+
const buf = Buffer.alloc(len);
|
|
369
|
+
const fd = fs.openSync(filePath, 'r');
|
|
370
|
+
try {
|
|
371
|
+
fs.readSync(fd, buf, 0, len, 0);
|
|
372
|
+
} finally {
|
|
373
|
+
fs.closeSync(fd);
|
|
374
|
+
}
|
|
375
|
+
return buf.toString('utf8');
|
|
376
|
+
}
|
|
377
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
378
|
+
|
|
269
379
|
function appendToFile(filePath, line) {
|
|
270
380
|
const prev = _writeQueue.get(filePath) || Promise.resolve();
|
|
271
381
|
const next = prev.then(() => {
|
|
@@ -466,7 +576,7 @@ function bindServer(server, port) {
|
|
|
466
576
|
let attempt = 0;
|
|
467
577
|
|
|
468
578
|
function tryPort(p) {
|
|
469
|
-
server.listen(p, () => resolve(p));
|
|
579
|
+
server.listen(p, '127.0.0.1', () => resolve(p));
|
|
470
580
|
server.once('error', (err) => {
|
|
471
581
|
if (err.code === 'EADDRINUSE' && attempt < maxTries) {
|
|
472
582
|
attempt += 1;
|
|
@@ -534,6 +644,18 @@ function resolveSlugToPath(slug, projDir) {
|
|
|
534
644
|
}
|
|
535
645
|
|
|
536
646
|
export async function startServer({ port = 4242, projectDir, openBrowser = true } = {}) {
|
|
647
|
+
// ── Security: per-process auth credential for mutating (non-GET) requests ─
|
|
648
|
+
// Generated once per server start and written to a well-known location so
|
|
649
|
+
// trusted local callers (CLI, control-start.cjs) can read it and pass it
|
|
650
|
+
// back via an auth header or query param on non-GET requests.
|
|
651
|
+
const authBytes = crypto.randomBytes(24);
|
|
652
|
+
const dashboardAuthValue = authBytes.toString('hex');
|
|
653
|
+
try {
|
|
654
|
+
const authFileDir = path.join(projectDir || process.cwd(), '.monomind');
|
|
655
|
+
fs.mkdirSync(authFileDir, { recursive: true });
|
|
656
|
+
fs.writeFileSync(path.join(authFileDir, 'dashboard-token'), dashboardAuthValue, { mode: 0o600 });
|
|
657
|
+
} catch (_) {}
|
|
658
|
+
|
|
537
659
|
// Parse a .claude/agents/*.md definition into { name, description, capability{}, document }.
|
|
538
660
|
// Tolerant line-based parse of the YAML frontmatter (expertise / task_types as lists).
|
|
539
661
|
function parseAgentDef(raw) {
|
|
@@ -845,13 +967,46 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
845
967
|
}
|
|
846
968
|
}
|
|
847
969
|
}
|
|
848
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
970
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
849
971
|
res.end('{"ok":true}');
|
|
850
972
|
}
|
|
851
973
|
|
|
852
974
|
const server = http.createServer(async (req, res) => {
|
|
853
975
|
const url = req.url.split('?')[0];
|
|
854
976
|
|
|
977
|
+
// ── Security: strict CORS allow-list ────────────────────────────────────
|
|
978
|
+
// Only reflect Origin when it is this dashboard's own loopback origin.
|
|
979
|
+
// Otherwise the header is omitted entirely, so cross-origin reads fail
|
|
980
|
+
// closed rather than defaulting to '*'.
|
|
981
|
+
const _reqOrigin = req.headers.origin || '';
|
|
982
|
+
const _boundPortForCors = currentPort || boundPort || port;
|
|
983
|
+
const _allowedOrigins = new Set([
|
|
984
|
+
`http://localhost:${_boundPortForCors}`,
|
|
985
|
+
`http://127.0.0.1:${_boundPortForCors}`,
|
|
986
|
+
]);
|
|
987
|
+
const corsOrigin = _allowedOrigins.has(_reqOrigin) ? _reqOrigin : undefined;
|
|
988
|
+
|
|
989
|
+
// ── Security: require a matching auth credential on all non-GET routes ──
|
|
990
|
+
// GET requests remain open (read-only loopback dashboard viewing).
|
|
991
|
+
if (req.method !== 'GET' && req.method !== 'HEAD' && req.method !== 'OPTIONS') {
|
|
992
|
+
let _suppliedAuth = req.headers['x-monomind-token'] || '';
|
|
993
|
+
if (!_suppliedAuth) {
|
|
994
|
+
try { _suppliedAuth = new URL(req.url, 'http://localhost').searchParams.get('token') || ''; } catch (_) {}
|
|
995
|
+
}
|
|
996
|
+
const _suppliedAuthBuf = Buffer.from(String(_suppliedAuth));
|
|
997
|
+
const _expectedAuthBuf = Buffer.from(dashboardAuthValue);
|
|
998
|
+
const _authMatches = _suppliedAuthBuf.length === _expectedAuthBuf.length
|
|
999
|
+
&& crypto.timingSafeEqual(_suppliedAuthBuf, _expectedAuthBuf);
|
|
1000
|
+
if (!_suppliedAuth || !_authMatches) {
|
|
1001
|
+
res.writeHead(401, {
|
|
1002
|
+
'Content-Type': 'application/json',
|
|
1003
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1004
|
+
});
|
|
1005
|
+
res.end(JSON.stringify({ error: 'Unauthorized: missing or invalid auth token' }));
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
855
1010
|
// ------------------------------------------------------------------ GET /
|
|
856
1011
|
if (req.method === 'GET' && url === '/') {
|
|
857
1012
|
const htmlPath = path.join(__dirname, 'dashboard.html');
|
|
@@ -905,11 +1060,11 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
905
1060
|
}
|
|
906
1061
|
let branch = '';
|
|
907
1062
|
try { branch = gitExec('git rev-parse --abbrev-ref HEAD', { cwd, encoding: 'utf8' }).trim(); } catch {}
|
|
908
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1063
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
909
1064
|
res.end(JSON.stringify({ name, email, cwd, remoteUrl, branch }));
|
|
910
1065
|
} catch (_) {
|
|
911
1066
|
const cwd2 = projectDir || process.cwd();
|
|
912
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1067
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
913
1068
|
res.end(JSON.stringify({ name: '', email: '', cwd: cwd2, remoteUrl: '', branch: '' }));
|
|
914
1069
|
}
|
|
915
1070
|
return;
|
|
@@ -923,7 +1078,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
923
1078
|
const snapshot = await collectAll(dir);
|
|
924
1079
|
res.writeHead(200, {
|
|
925
1080
|
'Content-Type': 'application/json',
|
|
926
|
-
'Access-Control-Allow-Origin':
|
|
1081
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
927
1082
|
'Cache-Control': 'no-cache',
|
|
928
1083
|
});
|
|
929
1084
|
res.end(JSON.stringify(snapshot));
|
|
@@ -950,7 +1105,11 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
950
1105
|
// system files to any process that can reach localhost:4242.
|
|
951
1106
|
const _resolvedFile = path.resolve(file);
|
|
952
1107
|
const _homeDir = os.homedir();
|
|
953
|
-
|
|
1108
|
+
// Bare-prefix check (no path.sep) is bypassable: with home "/Users/bob", a path
|
|
1109
|
+
// like "/Users/bobsecrets/x.jsonl" starts with the literal string "/Users/bob"
|
|
1110
|
+
// even though it's a sibling directory, not a subdirectory of home. Require the
|
|
1111
|
+
// separator (or exact equality to the home dir itself) so only true descendants pass.
|
|
1112
|
+
if (_resolvedFile !== _homeDir && !_resolvedFile.startsWith(_homeDir + path.sep)) {
|
|
954
1113
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
955
1114
|
res.end(JSON.stringify({ error: 'Access denied: file must be within the home directory' }));
|
|
956
1115
|
return;
|
|
@@ -967,7 +1126,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
967
1126
|
const events = parseSessionLines(lines);
|
|
968
1127
|
res.writeHead(200, {
|
|
969
1128
|
'Content-Type': 'application/json',
|
|
970
|
-
'Access-Control-Allow-Origin':
|
|
1129
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
971
1130
|
'Cache-Control': 'no-cache',
|
|
972
1131
|
});
|
|
973
1132
|
res.end(JSON.stringify({ events, total: allLines.length, shown: lines.length }));
|
|
@@ -1005,7 +1164,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1005
1164
|
const modelBreakdown = {};
|
|
1006
1165
|
const filesTouchedSet = new Set();
|
|
1007
1166
|
try {
|
|
1008
|
-
const lines =
|
|
1167
|
+
const lines = _readTailLines(fp);
|
|
1009
1168
|
let pendingCompact = false;
|
|
1010
1169
|
for (const line of lines) {
|
|
1011
1170
|
let e; try { e = JSON.parse(line); } catch { continue; }
|
|
@@ -1062,7 +1221,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1062
1221
|
const summary = summaries.length ? summaries[summaries.length - 1].text : null;
|
|
1063
1222
|
sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, summary, compactCount, errorCount, totalDurationMs, totalMessages, totalCost, toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, filesTouched, file: fp });
|
|
1064
1223
|
}
|
|
1065
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1224
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1066
1225
|
res.end(JSON.stringify({ sessions }));
|
|
1067
1226
|
} catch (err) {
|
|
1068
1227
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1077,7 +1236,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1077
1236
|
const dir = qs.get('dir') || '';
|
|
1078
1237
|
const q = (qs.get('q') || '').toLowerCase().trim();
|
|
1079
1238
|
if (!q || q.length < 2) {
|
|
1080
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1239
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1081
1240
|
res.end(JSON.stringify({ results: [] }));
|
|
1082
1241
|
return;
|
|
1083
1242
|
}
|
|
@@ -1118,7 +1277,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1118
1277
|
} catch {}
|
|
1119
1278
|
if (matches.length) results.push({ id, file: fp, lastPrompt, mtime, matches });
|
|
1120
1279
|
}
|
|
1121
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1280
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1122
1281
|
res.end(JSON.stringify({ results, q }));
|
|
1123
1282
|
} catch (err) {
|
|
1124
1283
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1181,7 +1340,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1181
1340
|
return tb - ta;
|
|
1182
1341
|
});
|
|
1183
1342
|
|
|
1184
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1343
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1185
1344
|
res.end(JSON.stringify({ events: events.slice(0, limit) }));
|
|
1186
1345
|
} catch (err) {
|
|
1187
1346
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1233,7 +1392,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1233
1392
|
const errors = Object.entries(errorCounts)
|
|
1234
1393
|
.map(([tool, count]) => ({ tool, count, total: totalCounts[tool] || count }))
|
|
1235
1394
|
.sort((a,b) => b.count - a.count);
|
|
1236
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1395
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1237
1396
|
res.end(JSON.stringify({ errors }));
|
|
1238
1397
|
} catch (err) {
|
|
1239
1398
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1284,7 +1443,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1284
1443
|
const tools = Object.entries(toolCounts)
|
|
1285
1444
|
.map(([tool, count]) => ({ tool, count, errors: errorCounts[tool] || 0 }))
|
|
1286
1445
|
.sort((a,b) => b.count - a.count);
|
|
1287
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1446
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1288
1447
|
res.end(JSON.stringify({ tools }));
|
|
1289
1448
|
} catch (err) {
|
|
1290
1449
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1319,7 +1478,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1319
1478
|
if (totalCost > 0) projectCosts.push({ path: projPath, cost: totalCost, sessions: sessionFiles.length });
|
|
1320
1479
|
}
|
|
1321
1480
|
projectCosts.sort((a, b) => b.cost - a.cost);
|
|
1322
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1481
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1323
1482
|
res.end(JSON.stringify({ projects: projectCosts }));
|
|
1324
1483
|
} catch (err) {
|
|
1325
1484
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1352,7 +1511,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1352
1511
|
} catch {}
|
|
1353
1512
|
return { slug, path: projPath, name, sessionCount, memoryCount, lastActivity: lastActivity || null };
|
|
1354
1513
|
}).filter(p => p.sessionCount > 0 || fs.existsSync(path.join(p.path, '.monomind'))).sort((a, b) => (b.lastActivity || 0) - (a.lastActivity || 0));
|
|
1355
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1514
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1356
1515
|
res.end(JSON.stringify({ projects }));
|
|
1357
1516
|
} catch (err) {
|
|
1358
1517
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1381,7 +1540,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1381
1540
|
let kg = [];
|
|
1382
1541
|
try { const raw = fs.readFileSync(path.join(palaceDir, 'kg.json'), 'utf8'); kg = JSON.parse(raw); } catch {}
|
|
1383
1542
|
|
|
1384
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1543
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1385
1544
|
res.end(JSON.stringify({ drawers, identity, kg }));
|
|
1386
1545
|
} catch (err) {
|
|
1387
1546
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1432,7 +1591,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1432
1591
|
}
|
|
1433
1592
|
}
|
|
1434
1593
|
|
|
1435
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1594
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1436
1595
|
res.end(JSON.stringify({ adrs }));
|
|
1437
1596
|
} catch (err) {
|
|
1438
1597
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1501,7 +1660,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1501
1660
|
}
|
|
1502
1661
|
} catch {}
|
|
1503
1662
|
|
|
1504
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1663
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1505
1664
|
res.end(JSON.stringify({ memories: memories.concat(backend), memDir }));
|
|
1506
1665
|
} catch (err) {
|
|
1507
1666
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1534,7 +1693,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1534
1693
|
}
|
|
1535
1694
|
fs.mkdirSync(memDir, { recursive: true });
|
|
1536
1695
|
fs.writeFileSync(fp, content || '', 'utf8');
|
|
1537
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1696
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1538
1697
|
res.end(JSON.stringify({ ok: true }));
|
|
1539
1698
|
} catch (err) {
|
|
1540
1699
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1567,7 +1726,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1567
1726
|
return;
|
|
1568
1727
|
}
|
|
1569
1728
|
fs.unlinkSync(fp);
|
|
1570
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1729
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1571
1730
|
res.end(JSON.stringify({ ok: true }));
|
|
1572
1731
|
} catch (err) {
|
|
1573
1732
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1588,7 +1747,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1588
1747
|
const raw = fs.readFileSync(feedbackPath, 'utf-8');
|
|
1589
1748
|
rows = raw.split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
1590
1749
|
}
|
|
1591
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1750
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1592
1751
|
res.end(JSON.stringify(rows));
|
|
1593
1752
|
} catch (err) {
|
|
1594
1753
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1663,7 +1822,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1663
1822
|
lastWrite,
|
|
1664
1823
|
memDir,
|
|
1665
1824
|
};
|
|
1666
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1825
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1667
1826
|
res.end(JSON.stringify({ stats }));
|
|
1668
1827
|
} catch (err) {
|
|
1669
1828
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1792,7 +1951,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1792
1951
|
if (hasRepeatLoops) loops = loops.filter(l => l.source !== 'scheduled_tasks_lock' && l.source !== 'schedule_wakeup_hook');
|
|
1793
1952
|
|
|
1794
1953
|
loops.sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0));
|
|
1795
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1954
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1796
1955
|
res.end(JSON.stringify({ loops }));
|
|
1797
1956
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1798
1957
|
return;
|
|
@@ -1811,7 +1970,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1811
1970
|
const loopsDir = path.join(_stopDir, '.monomind', 'loops');
|
|
1812
1971
|
fs.mkdirSync(loopsDir, { recursive: true });
|
|
1813
1972
|
fs.writeFileSync(path.join(loopsDir, `${id}.stop`), `stop-requested-${Date.now()}`);
|
|
1814
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1973
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1815
1974
|
res.end(JSON.stringify({ ok: true }));
|
|
1816
1975
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1817
1976
|
});
|
|
@@ -1843,7 +2002,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1843
2002
|
const nowMs = Date.now();
|
|
1844
2003
|
const loop = { id, type: 'repeat', name: name || prompt.slice(0, 40), prompt, interval: interval || '1h', maxReps, status: 'active', currentRep: 0, startedAt: nowMs, lastRunAt: null };
|
|
1845
2004
|
fs.writeFileSync(path.join(loopsDir, `${id}.json`), JSON.stringify(loop, null, 2));
|
|
1846
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2005
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1847
2006
|
res.end(JSON.stringify({ ok: true, id }));
|
|
1848
2007
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1849
2008
|
});
|
|
@@ -1868,18 +2027,20 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1868
2027
|
if (f.includes(sessionId) || sessionId === f.replace('.jsonl', '')) { fp = path.join(projectClaudeDir, f); break; }
|
|
1869
2028
|
}
|
|
1870
2029
|
if (!fp) {
|
|
1871
|
-
// fallback: find by scanning
|
|
2030
|
+
// fallback: find by scanning — the sessionId lives in the first JSONL line, so a
|
|
2031
|
+
// small head-read is enough; no need to load the whole (potentially huge) file.
|
|
1872
2032
|
for (const f of files) {
|
|
1873
|
-
const
|
|
1874
|
-
const
|
|
1875
|
-
if (
|
|
1876
|
-
try { const first = JSON.parse(
|
|
2033
|
+
const head = _readHeadText(path.join(projectClaudeDir, f));
|
|
2034
|
+
const firstLine = head.split('\n', 1)[0];
|
|
2035
|
+
if (firstLine) {
|
|
2036
|
+
try { const first = JSON.parse(firstLine); if (first.sessionId === sessionId) { fp = path.join(projectClaudeDir, f); break; } } catch {}
|
|
1877
2037
|
}
|
|
1878
2038
|
}
|
|
1879
2039
|
}
|
|
1880
2040
|
if (!fp) { res.writeHead(404); res.end(JSON.stringify({ errors: [] })); return; }
|
|
1881
|
-
|
|
1882
|
-
|
|
2041
|
+
// Only the most recent portion of the file matters for a dashboard error feed —
|
|
2042
|
+
// tail-cap the read so a multi-hundred-MB session log doesn't get buffered whole.
|
|
2043
|
+
const lines = _readTailLines(fp);
|
|
1883
2044
|
const errors = [];
|
|
1884
2045
|
for (const line of lines) {
|
|
1885
2046
|
try {
|
|
@@ -1894,7 +2055,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1894
2055
|
}
|
|
1895
2056
|
} catch {}
|
|
1896
2057
|
}
|
|
1897
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2058
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1898
2059
|
res.end(JSON.stringify({ errors: errors.slice(0, 50) }));
|
|
1899
2060
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ errors: [], error: err.message })); }
|
|
1900
2061
|
return;
|
|
@@ -1910,7 +2071,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1910
2071
|
'Content-Type': 'text/event-stream',
|
|
1911
2072
|
'Cache-Control': 'no-cache',
|
|
1912
2073
|
'Connection': 'keep-alive',
|
|
1913
|
-
'Access-Control-Allow-Origin':
|
|
2074
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1914
2075
|
});
|
|
1915
2076
|
const send = (ev, data) => { try { res.write(`event: ${ev}\ndata: ${JSON.stringify(data)}\n\n`); } catch {} };
|
|
1916
2077
|
send('connected', { ts: Date.now() });
|
|
@@ -1954,7 +2115,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1954
2115
|
return;
|
|
1955
2116
|
}
|
|
1956
2117
|
fs.writeFileSync(chunksFile, filtered.map(e => JSON.stringify(e)).join('\n') + (filtered.length ? '\n' : ''), 'utf8');
|
|
1957
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2118
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1958
2119
|
res.end(JSON.stringify({ ok: true, removed: before - filtered.length }));
|
|
1959
2120
|
} catch (err) {
|
|
1960
2121
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1993,7 +2154,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1993
2154
|
}
|
|
1994
2155
|
entries[idx] = { ...entries[idx], text };
|
|
1995
2156
|
fs.writeFileSync(chunksFile, entries.map(e => JSON.stringify(e)).join('\n') + '\n', 'utf8');
|
|
1996
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2157
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1997
2158
|
res.end(JSON.stringify({ ok: true }));
|
|
1998
2159
|
} catch (err) {
|
|
1999
2160
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2037,9 +2198,9 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
2037
2198
|
} finally { closeDb(db); }
|
|
2038
2199
|
} catch {
|
|
2039
2200
|
// Fallback: sqlite3 CLI + inline Sigma.js graph
|
|
2040
|
-
const {
|
|
2201
|
+
const { execFileSync } = await import('child_process');
|
|
2041
2202
|
const runSql = (sql) => {
|
|
2042
|
-
try { return JSON.parse(
|
|
2203
|
+
try { return JSON.parse(execFileSync('sqlite3', ['-json', dbPath], { encoding: 'utf-8', timeout: 15000, maxBuffer: 50*1024*1024, input: sql + ';' }) || '[]'); } catch { return []; }
|
|
2043
2204
|
};
|
|
2044
2205
|
const rawNodes = runSql('SELECT id, name, label, file_path, community_id FROM nodes LIMIT 2000');
|
|
2045
2206
|
const rawEdges = runSql('SELECT source_id, target_id, relation FROM edges');
|
|
@@ -2070,7 +2231,7 @@ ${edgesJson}.forEach(e=>{try{g.addEdge(e.source,e.target,{size:0.5,color:'#333'}
|
|
|
2070
2231
|
new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{color:'#ccc'},labelSize:10});
|
|
2071
2232
|
<\/script></body></html>`;
|
|
2072
2233
|
}
|
|
2073
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
2234
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2074
2235
|
res.end(html);
|
|
2075
2236
|
return;
|
|
2076
2237
|
}
|
|
@@ -2078,7 +2239,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2078
2239
|
// Fallback: try legacy graph.html on disk
|
|
2079
2240
|
const htmlPath = path.join(d, '.monomind', 'graph', 'graph.html');
|
|
2080
2241
|
const html = fs.readFileSync(htmlPath, 'utf-8');
|
|
2081
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
2242
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2082
2243
|
res.end(html);
|
|
2083
2244
|
} catch (err) {
|
|
2084
2245
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
@@ -2122,7 +2283,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2122
2283
|
].join('\n');
|
|
2123
2284
|
} finally { closeDb(db); }
|
|
2124
2285
|
}
|
|
2125
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2286
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2126
2287
|
res.end(JSON.stringify({ exists, report, stats }));
|
|
2127
2288
|
} catch (err) {
|
|
2128
2289
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2140,10 +2301,10 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2140
2301
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2141
2302
|
let nodes = [], edges = [];
|
|
2142
2303
|
if (fs.existsSync(dbPath)) {
|
|
2143
|
-
const {
|
|
2304
|
+
const { execFileSync } = await import('child_process');
|
|
2144
2305
|
const runSql = (sql, timeout = 10000) => {
|
|
2145
2306
|
try {
|
|
2146
|
-
return JSON.parse(
|
|
2307
|
+
return JSON.parse(execFileSync('sqlite3', ['-json', dbPath],
|
|
2147
2308
|
{ encoding: 'utf-8', timeout, maxBuffer: 50 * 1024 * 1024, input: sql + ';' }) || '[]');
|
|
2148
2309
|
} catch { return []; }
|
|
2149
2310
|
};
|
|
@@ -2166,7 +2327,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2166
2327
|
nodes = topNodes.map(n => ({ id: n.id, label: n.name || n.id, type: n.label || 'unknown', degree: degree.get(n.id) || 0 }));
|
|
2167
2328
|
edges = rawEdges.filter(e => topIds.has(e.source_id) && topIds.has(e.target_id)).slice(0, 2000).map(e => ({ source: e.source_id, target: e.target_id, relation: e.relation || 'REF' }));
|
|
2168
2329
|
}
|
|
2169
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2330
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2170
2331
|
res.end(JSON.stringify({ nodes, edges }));
|
|
2171
2332
|
} catch (err) {
|
|
2172
2333
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2195,7 +2356,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2195
2356
|
const importScript = path.join(process.cwd(), 'scripts', 'ua-import.mjs');
|
|
2196
2357
|
const enrichScript = path.join(process.cwd(), 'scripts', 'ua-enrich.mjs');
|
|
2197
2358
|
|
|
2198
|
-
res.writeHead(202, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2359
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2199
2360
|
|
|
2200
2361
|
if (uaGraph && fs.existsSync(importScript)) {
|
|
2201
2362
|
res.end(JSON.stringify({ status: 'importing', source: uaGraph }));
|
|
@@ -2224,7 +2385,19 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2224
2385
|
const dir = qs.get('dir') || projectDir || process.cwd();
|
|
2225
2386
|
const d = path.resolve(dir || process.cwd());
|
|
2226
2387
|
|
|
2227
|
-
|
|
2388
|
+
// Security: this route spawns `node --eval` with `cwd: d`, which lets
|
|
2389
|
+
// Node resolve '@monoes/monograph' against d's node_modules. Only ever
|
|
2390
|
+
// allow this for the server's own project root — never an
|
|
2391
|
+
// attacker-controlled `?dir=` — since a planted node_modules there
|
|
2392
|
+
// would achieve RCE. (See P0-6.)
|
|
2393
|
+
const _serverRoot = path.resolve(projectDir || process.cwd());
|
|
2394
|
+
if (d !== _serverRoot) {
|
|
2395
|
+
res.writeHead(400, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2396
|
+
res.end(JSON.stringify({ error: 'monograph-build only supports the server project root' }));
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2228
2401
|
res.end(JSON.stringify({ status: 'building', dir: d }));
|
|
2229
2402
|
|
|
2230
2403
|
// Build via monograph in background
|
|
@@ -2245,7 +2418,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2245
2418
|
const qs2 = new URL(req.url, 'http://localhost').searchParams;
|
|
2246
2419
|
const d2 = path.resolve(qs2.get('dir') || projectDir || process.cwd());
|
|
2247
2420
|
const state = buildDocsState.get(d2) || { status: 'idle' };
|
|
2248
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2421
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2249
2422
|
res.end(JSON.stringify(state));
|
|
2250
2423
|
return;
|
|
2251
2424
|
}
|
|
@@ -2258,7 +2431,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2258
2431
|
const d = path.resolve(dir || process.cwd());
|
|
2259
2432
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2260
2433
|
if (!fs.existsSync(dbPath)) {
|
|
2261
|
-
res.writeHead(400, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2434
|
+
res.writeHead(400, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2262
2435
|
res.end(JSON.stringify({ error: 'monograph.db not found — run BUILD GRAPH first' }));
|
|
2263
2436
|
return;
|
|
2264
2437
|
}
|
|
@@ -2266,14 +2439,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2266
2439
|
// Reject if already running
|
|
2267
2440
|
const existing = buildDocsState.get(d);
|
|
2268
2441
|
if (existing && existing.status === 'pending') {
|
|
2269
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2442
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2270
2443
|
res.end(JSON.stringify({ status: 'pending', message: 'Build already in progress' }));
|
|
2271
2444
|
return;
|
|
2272
2445
|
}
|
|
2273
2446
|
|
|
2274
2447
|
const startedAt = Date.now();
|
|
2275
2448
|
buildDocsState.set(d, { status: 'pending', sections: 0, files: 0, error: null, startedAt });
|
|
2276
|
-
res.writeHead(202, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2449
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2277
2450
|
res.end(JSON.stringify({ status: 'pending', dir: d }));
|
|
2278
2451
|
|
|
2279
2452
|
// Run doc parsing in background
|
|
@@ -2472,7 +2645,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2472
2645
|
} catch {}
|
|
2473
2646
|
}
|
|
2474
2647
|
} finally { closeDb(db); }
|
|
2475
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2648
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2476
2649
|
res.end(JSON.stringify({ content, filePath, startLine, endLine, language, name, type }));
|
|
2477
2650
|
} catch (err) {
|
|
2478
2651
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2492,7 +2665,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2492
2665
|
const d = path.resolve(dir || process.cwd());
|
|
2493
2666
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2494
2667
|
if (!q) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?q=' })); return; }
|
|
2495
|
-
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2668
|
+
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ nodes: [] })); return; }
|
|
2496
2669
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2497
2670
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2498
2671
|
const db = openDb(dbPath);
|
|
@@ -2505,7 +2678,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2505
2678
|
return { id: h.id, label: h.name, type: h.label, degree: 0, filePath: h.filePath || h.file_path, startLine: h.startLine || h.start_line, snippet };
|
|
2506
2679
|
});
|
|
2507
2680
|
} finally { closeDb(db); }
|
|
2508
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2681
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2509
2682
|
res.end(JSON.stringify({ nodes }));
|
|
2510
2683
|
} catch (err) {
|
|
2511
2684
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2524,7 +2697,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2524
2697
|
const maxDepth = Math.min(4, parseInt(qs.get('depth') || '3', 10));
|
|
2525
2698
|
const d = path.resolve(dir || process.cwd());
|
|
2526
2699
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2527
|
-
if (!id || !fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2700
|
+
if (!id || !fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ related: [] })); return; }
|
|
2528
2701
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2529
2702
|
const db = openDb(dbPath);
|
|
2530
2703
|
const related = [];
|
|
@@ -2548,7 +2721,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2548
2721
|
frontier = next;
|
|
2549
2722
|
}
|
|
2550
2723
|
} finally { closeDb(db); }
|
|
2551
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2724
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2552
2725
|
res.end(JSON.stringify({ related }));
|
|
2553
2726
|
} catch (err) {
|
|
2554
2727
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2608,7 +2781,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2608
2781
|
}
|
|
2609
2782
|
result.markdown = lines2.join('\n');
|
|
2610
2783
|
} finally { closeDb(db); }
|
|
2611
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2784
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2612
2785
|
res.end(JSON.stringify(result));
|
|
2613
2786
|
} catch (err) {
|
|
2614
2787
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2625,7 +2798,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2625
2798
|
const d = path.resolve(dir || process.cwd());
|
|
2626
2799
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2627
2800
|
if (!q) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?q= parameter' })); return; }
|
|
2628
|
-
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2801
|
+
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ success: false, result: 'Graph not built yet. Run: monomind monograph build' })); return; }
|
|
2629
2802
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2630
2803
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2631
2804
|
const db = openDb(dbPath);
|
|
@@ -2644,7 +2817,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2644
2817
|
}
|
|
2645
2818
|
}
|
|
2646
2819
|
} finally { closeDb(db); }
|
|
2647
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2820
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2648
2821
|
res.end(JSON.stringify({ success: true, query: q, result }));
|
|
2649
2822
|
} catch (err) {
|
|
2650
2823
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2662,7 +2835,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2662
2835
|
const d = path.resolve(dir || process.cwd());
|
|
2663
2836
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2664
2837
|
if (!nodeQ) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?node= parameter' })); return; }
|
|
2665
|
-
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2838
|
+
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ success: false, explanation: 'Graph not built yet. Run: monomind monograph build' })); return; }
|
|
2666
2839
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2667
2840
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2668
2841
|
const db = openDb(dbPath);
|
|
@@ -2686,7 +2859,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2686
2859
|
].filter(Boolean).join('\n');
|
|
2687
2860
|
}
|
|
2688
2861
|
} finally { closeDb(db); }
|
|
2689
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2862
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2690
2863
|
res.end(JSON.stringify({ success: true, node: nodeQ, explanation }));
|
|
2691
2864
|
} catch (err) {
|
|
2692
2865
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2705,7 +2878,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2705
2878
|
const d = path.resolve(dir || process.cwd());
|
|
2706
2879
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2707
2880
|
if (!from || !to) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?from= and ?to= parameters' })); return; }
|
|
2708
|
-
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2881
|
+
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ success: false, path: 'Graph not built yet.' })); return; }
|
|
2709
2882
|
// Import only graphology-free storage modules to avoid broken graphology dep
|
|
2710
2883
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2711
2884
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
@@ -2752,7 +2925,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2752
2925
|
pathResult = names.join(' → ') + ` (${p.length - 1} hop${p.length !== 2 ? 's' : ''})`;
|
|
2753
2926
|
}
|
|
2754
2927
|
} finally { closeDb(db); }
|
|
2755
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2928
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2756
2929
|
res.end(JSON.stringify({ success: true, from, to, path: pathResult }));
|
|
2757
2930
|
} catch (err) {
|
|
2758
2931
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2774,7 +2947,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2774
2947
|
process.kill(pid, 0);
|
|
2775
2948
|
running = true;
|
|
2776
2949
|
} catch {}
|
|
2777
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2950
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2778
2951
|
res.end(JSON.stringify({ running, pid }));
|
|
2779
2952
|
} catch (err) {
|
|
2780
2953
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2800,7 +2973,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2800
2973
|
} catch {}
|
|
2801
2974
|
|
|
2802
2975
|
if (wasRunning) {
|
|
2803
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2976
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2804
2977
|
res.end(JSON.stringify({ running: false, action: 'stopped' }));
|
|
2805
2978
|
} else {
|
|
2806
2979
|
const { spawn: sp } = await import('child_process');
|
|
@@ -2808,7 +2981,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2808
2981
|
child.unref();
|
|
2809
2982
|
try { fs.mkdirSync(path.join(d, '.monomind'), { recursive: true }); } catch {}
|
|
2810
2983
|
try { fs.writeFileSync(pidPath, String(child.pid)); } catch {}
|
|
2811
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2984
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2812
2985
|
res.end(JSON.stringify({ running: true, pid: child.pid, action: 'started' }));
|
|
2813
2986
|
}
|
|
2814
2987
|
} catch (err) {
|
|
@@ -2823,7 +2996,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2823
2996
|
let body = '';
|
|
2824
2997
|
req.on('data', c => { body += c; if (body.length > 2097152) { req.destroy(); return; } });
|
|
2825
2998
|
req.on('end', async () => {
|
|
2826
|
-
const json = res => { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2999
|
+
const json = res => { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); };
|
|
2827
3000
|
const ok = (data) => { json(res); res.end(JSON.stringify({ content: [{ type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }] })); };
|
|
2828
3001
|
const err = (msg) => { json(res); res.end(JSON.stringify({ error: msg })); };
|
|
2829
3002
|
try {
|
|
@@ -3272,14 +3445,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3272
3445
|
const gp = fs.existsSync(graphPath) ? graphPath : (fs.existsSync(legacyPath) ? legacyPath : null);
|
|
3273
3446
|
|
|
3274
3447
|
if (!gp) {
|
|
3275
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3448
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3276
3449
|
res.end(JSON.stringify({ available: false }));
|
|
3277
3450
|
return;
|
|
3278
3451
|
}
|
|
3279
3452
|
|
|
3280
3453
|
const { execSync: ex } = await import('child_process');
|
|
3281
3454
|
const out = ex(`graphify benchmark ${gp}`, { encoding: 'utf8', cwd: d, timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
3282
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3455
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3283
3456
|
res.end(JSON.stringify({ available: true, result: out }));
|
|
3284
3457
|
} catch (err) {
|
|
3285
3458
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3417,7 +3590,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3417
3590
|
}
|
|
3418
3591
|
}
|
|
3419
3592
|
|
|
3420
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3593
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3421
3594
|
res.end(JSON.stringify({ nodes, edges }));
|
|
3422
3595
|
} catch (err) {
|
|
3423
3596
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3434,7 +3607,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3434
3607
|
const entries = collectSwarmHistory(path.resolve(dir));
|
|
3435
3608
|
res.writeHead(200, {
|
|
3436
3609
|
'Content-Type': 'application/json',
|
|
3437
|
-
'Access-Control-Allow-Origin':
|
|
3610
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3438
3611
|
'Cache-Control': 'no-cache',
|
|
3439
3612
|
});
|
|
3440
3613
|
res.end(JSON.stringify({ entries }));
|
|
@@ -3459,7 +3632,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3459
3632
|
const agentId = typeof _rawAgentId === 'string' ? _rawAgentId.slice(0, 256) : undefined;
|
|
3460
3633
|
const last = qs.get('last') ? parseInt(qs.get('last')) : undefined;
|
|
3461
3634
|
const events = collectSwarmEvents(path.resolve(dir), { swarmId, agentId, last });
|
|
3462
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3635
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3463
3636
|
res.end(JSON.stringify({ events, count: events.length }));
|
|
3464
3637
|
} catch (err) {
|
|
3465
3638
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3473,7 +3646,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3473
3646
|
try {
|
|
3474
3647
|
const dir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
|
|
3475
3648
|
const size = getSwarmDataSize(path.resolve(dir));
|
|
3476
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3649
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3477
3650
|
res.end(JSON.stringify(size));
|
|
3478
3651
|
} catch (err) {
|
|
3479
3652
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3487,7 +3660,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3487
3660
|
try {
|
|
3488
3661
|
const dir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
|
|
3489
3662
|
const result = cleanSwarmData(path.resolve(dir));
|
|
3490
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3663
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3491
3664
|
res.end(JSON.stringify({ success: true, ...result }));
|
|
3492
3665
|
} catch (err) {
|
|
3493
3666
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3507,7 +3680,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3507
3680
|
const trackerPath = path.join(dir, '.claude', 'helpers', 'token-tracker.cjs');
|
|
3508
3681
|
const fallback = () => {
|
|
3509
3682
|
const summary = (() => { try { return JSON.parse(fs.readFileSync(path.join(dir, '.monomind', 'metrics', 'token-summary.json'), 'utf8')); } catch { return {}; } })();
|
|
3510
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3683
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3511
3684
|
const fbSum = { todayCost: summary.todayCost || 0, cost: summary.todayCost || 0, todayCalls: summary.todayCalls || 0, calls: summary.todayCalls || 0, totalTokens: 0, totalTokensIn: 0, totalTokensOut: 0, cacheTokens: 0, modelCount: 0 };
|
|
3512
3685
|
res.end(JSON.stringify({ summary: fbSum, totalCost: summary.todayCost || 0, totalCalls: summary.todayCalls || 0, totalIn: 0, totalOut: 0, totalCR: 0, totalCW: 0, rows: [], models: [], categories: [], tools: [], mcpServers: [], projects: [], modelBreakdown: {}, categoryBreakdown: {}, toolBreakdown: {}, mcpBreakdown: {}, periodLabel: period }));
|
|
3513
3686
|
};
|
|
@@ -3564,7 +3737,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3564
3737
|
rows.sort((a, b) => b.cost - a.cost);
|
|
3565
3738
|
// Summary object matching client expectations
|
|
3566
3739
|
const summary = { todayCost: totalCost, cost: totalCost, todayCalls: totalCalls, calls: totalCalls, totalTokens: totalIn + totalOut, totalTokensIn: totalIn, totalTokensOut: totalOut, cacheTokens: totalCR, modelCount: models.length };
|
|
3567
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3740
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3568
3741
|
res.end(JSON.stringify({ summary, totalCost, totalCalls, totalIn, totalOut, totalCR, totalCW, rows, models, categories, tools, mcpServers, projects: projectRows, modelBreakdown, categoryBreakdown, toolBreakdown, mcpBreakdown, periodLabel: period }));
|
|
3569
3742
|
} catch (e) { fallback(); }
|
|
3570
3743
|
} catch (err) {
|
|
@@ -3594,7 +3767,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3594
3767
|
}
|
|
3595
3768
|
res.writeHead(200, {
|
|
3596
3769
|
'Content-Type': 'application/json',
|
|
3597
|
-
'Access-Control-Allow-Origin':
|
|
3770
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3598
3771
|
'Cache-Control': 'no-cache',
|
|
3599
3772
|
});
|
|
3600
3773
|
res.end(JSON.stringify(partial));
|
|
@@ -3611,7 +3784,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3611
3784
|
'Content-Type': 'text/event-stream',
|
|
3612
3785
|
'Cache-Control': 'no-cache',
|
|
3613
3786
|
Connection: 'keep-alive',
|
|
3614
|
-
'Access-Control-Allow-Origin':
|
|
3787
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3615
3788
|
'X-Accel-Buffering': 'no',
|
|
3616
3789
|
});
|
|
3617
3790
|
|
|
@@ -3660,11 +3833,11 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3660
3833
|
res.end(JSON.stringify({ exists: false }));
|
|
3661
3834
|
return;
|
|
3662
3835
|
}
|
|
3663
|
-
const {
|
|
3836
|
+
const { execFileSync } = await import('child_process');
|
|
3664
3837
|
// Pipe SQL via stdin to avoid shell quoting issues with single-quoted SQL strings.
|
|
3665
3838
|
const runSql = (sql, timeout = 5000) => {
|
|
3666
3839
|
try {
|
|
3667
|
-
return
|
|
3840
|
+
return execFileSync('sqlite3', ['-json', dbPath],
|
|
3668
3841
|
{ encoding: 'utf-8', timeout: timeout, input: sql + ';' });
|
|
3669
3842
|
} catch (e) { return '[]'; }
|
|
3670
3843
|
};
|
|
@@ -3750,7 +3923,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3750
3923
|
} catch(_) {}
|
|
3751
3924
|
}
|
|
3752
3925
|
}
|
|
3753
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3926
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3754
3927
|
res.end(JSON.stringify(orgs));
|
|
3755
3928
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3756
3929
|
return;
|
|
@@ -3772,7 +3945,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3772
3945
|
fs.mkdirSync(orgsDir, { recursive: true });
|
|
3773
3946
|
const destFile = path.join(orgsDir, `${orgName}.json`);
|
|
3774
3947
|
fs.writeFileSync(destFile, JSON.stringify({ ...cfg, name: orgName }, null, 2), 'utf8');
|
|
3775
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3948
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3776
3949
|
res.end(JSON.stringify({ ok: true, name: orgName, file: destFile }));
|
|
3777
3950
|
} catch (e) {
|
|
3778
3951
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
@@ -3798,7 +3971,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3798
3971
|
const destFile = path.join(orgsDir, `${name}.json`);
|
|
3799
3972
|
const cleanCfg = Object.fromEntries(Object.entries({ ...cfg, name }).filter(([k]) => !k.startsWith('_')));
|
|
3800
3973
|
fs.writeFileSync(destFile, JSON.stringify(cleanCfg, null, 2), 'utf8');
|
|
3801
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3974
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3802
3975
|
res.end(JSON.stringify({ ok: true, name, file: destFile }));
|
|
3803
3976
|
} catch (e) {
|
|
3804
3977
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
@@ -3818,7 +3991,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3818
3991
|
const _orgsOneProjDir = _resolveOrgProjectDir(orgName, _orgsOneRoot) || _orgsOneRoot;
|
|
3819
3992
|
const f = path.join(_orgsOneProjDir, '.monomind', 'orgs', `${orgName}.json`);
|
|
3820
3993
|
if (!fs.existsSync(f)) { res.writeHead(404); res.end('{"error":"not found"}'); return; }
|
|
3821
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3994
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3822
3995
|
res.end(fs.readFileSync(f, 'utf8'));
|
|
3823
3996
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3824
3997
|
return;
|
|
@@ -3904,7 +4077,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3904
4077
|
lastEventAt: _runstateData?.lastEventAt || null,
|
|
3905
4078
|
agentStates: _runstateData?.agentStates || {} };
|
|
3906
4079
|
|
|
3907
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4080
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3908
4081
|
res.end(JSON.stringify(result));
|
|
3909
4082
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3910
4083
|
return;
|
|
@@ -3955,7 +4128,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3955
4128
|
}
|
|
3956
4129
|
|
|
3957
4130
|
const out = events.filter(e => e && e.ts).sort((a, b) => b.ts - a.ts).slice(0, 100);
|
|
3958
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4131
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3959
4132
|
res.end(JSON.stringify(out));
|
|
3960
4133
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3961
4134
|
return;
|
|
@@ -3970,9 +4143,9 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3970
4143
|
const _projsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
3971
4144
|
const d = path.resolve(_projsQs.get('dir') || projectDir || process.cwd());
|
|
3972
4145
|
const projFile = path.join(d, '.monomind', 'orgs', `${orgName}-projects.json`);
|
|
3973
|
-
if (!fs.existsSync(projFile)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4146
|
+
if (!fs.existsSync(projFile)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end('[]'); return; }
|
|
3974
4147
|
const data = JSON.parse(fs.readFileSync(projFile, 'utf8'));
|
|
3975
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4148
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3976
4149
|
res.end(JSON.stringify(data.projects || []));
|
|
3977
4150
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3978
4151
|
return;
|
|
@@ -3988,12 +4161,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3988
4161
|
const d = path.resolve(_membersQs.get('dir') || projectDir || process.cwd());
|
|
3989
4162
|
const membersFile = path.join(d, '.monomind', 'orgs', `${orgName}-members.json`);
|
|
3990
4163
|
if (!fs.existsSync(membersFile)) {
|
|
3991
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4164
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3992
4165
|
res.end('{"members":[],"join_requests":[]}');
|
|
3993
4166
|
return;
|
|
3994
4167
|
}
|
|
3995
4168
|
const data = JSON.parse(fs.readFileSync(membersFile, 'utf8'));
|
|
3996
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4169
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3997
4170
|
res.end(JSON.stringify(data));
|
|
3998
4171
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3999
4172
|
return;
|
|
@@ -4014,7 +4187,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4014
4187
|
const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
|
|
4015
4188
|
let defaultAdapter = 'claude-sonnet-4-6';
|
|
4016
4189
|
try { defaultAdapter = JSON.parse(fs.readFileSync(orgFile, 'utf8'))?.run_config?.ceo_adapter || defaultAdapter; } catch(_) {}
|
|
4017
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4190
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4018
4191
|
res.end(JSON.stringify({ default_adapter: defaultAdapter, adapters: [
|
|
4019
4192
|
{ type: 'claude-local', label: 'Claude (local CLI)', source: 'built-in', disabled: false, modelsCount: 3 },
|
|
4020
4193
|
{ type: 'gemini-local', label: 'Gemini (local)', source: 'built-in', disabled: false, modelsCount: 1 },
|
|
@@ -4023,7 +4196,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4023
4196
|
return;
|
|
4024
4197
|
}
|
|
4025
4198
|
const data = JSON.parse(fs.readFileSync(adaptersFile, 'utf8'));
|
|
4026
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4199
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4027
4200
|
res.end(JSON.stringify(data));
|
|
4028
4201
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4029
4202
|
return;
|
|
@@ -4078,7 +4251,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4078
4251
|
} catch(_) {}
|
|
4079
4252
|
}
|
|
4080
4253
|
|
|
4081
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4254
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4082
4255
|
res.end(JSON.stringify({ skills, role_skill_map: roleSkillMap }));
|
|
4083
4256
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4084
4257
|
return;
|
|
@@ -4134,7 +4307,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4134
4307
|
}
|
|
4135
4308
|
}
|
|
4136
4309
|
|
|
4137
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4310
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
4138
4311
|
res.end(JSON.stringify({ role, definition }));
|
|
4139
4312
|
} catch (_) { res.writeHead(500); res.end('{}'); }
|
|
4140
4313
|
return;
|
|
@@ -4147,7 +4320,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4147
4320
|
const orgName = decodeURIComponent(urlObj.pathname.split('/')[3]);
|
|
4148
4321
|
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('{}'); return; }
|
|
4149
4322
|
const q = (urlObj.searchParams.get('q') || '').toLowerCase().trim();
|
|
4150
|
-
if (!q || q.length < 2) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4323
|
+
if (!q || q.length < 2) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end('{"hits":[]}'); return; }
|
|
4151
4324
|
|
|
4152
4325
|
const d = path.resolve(urlObj.searchParams.get('dir') || projectDir || process.cwd());
|
|
4153
4326
|
const orgsDir = path.join(d, '.monomind', 'orgs');
|
|
@@ -4219,7 +4392,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4219
4392
|
}
|
|
4220
4393
|
}
|
|
4221
4394
|
|
|
4222
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4395
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4223
4396
|
res.end(JSON.stringify({ q, hits: hits.slice(0, 50) }));
|
|
4224
4397
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4225
4398
|
return;
|
|
@@ -4505,7 +4678,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4505
4678
|
skills: r.skills || [],
|
|
4506
4679
|
};
|
|
4507
4680
|
});
|
|
4508
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4681
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4509
4682
|
res.end(JSON.stringify({ agents }));
|
|
4510
4683
|
} catch(_) { res.writeHead(500); res.end('{"agents":[]}'); }
|
|
4511
4684
|
return;
|
|
@@ -4542,7 +4715,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4542
4715
|
ts: a.ts || null,
|
|
4543
4716
|
}));
|
|
4544
4717
|
const pending = approvals.filter(a => a.status === 'pending' || a.status === 'revision_requested').length;
|
|
4545
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4718
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4546
4719
|
res.end(JSON.stringify({ approvals, pending }));
|
|
4547
4720
|
} catch(_) { res.writeHead(500); res.end('{"approvals":[],"pending":0}'); }
|
|
4548
4721
|
return;
|
|
@@ -4585,7 +4758,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4585
4758
|
const event = { type: 'org:approval:resolved', org: orgName, approval_id: approvalId, status, ts: Date.now() };
|
|
4586
4759
|
appendToFile(path.join(path.resolve(_postApprovalsQs.get('dir') || projectDir || process.cwd()), 'data', 'mastermind-events.jsonl'), JSON.stringify(event) + '\n').catch(() => {});
|
|
4587
4760
|
broadcastMm(event);
|
|
4588
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4761
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4589
4762
|
res.end(JSON.stringify({ ok: true, status }));
|
|
4590
4763
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4591
4764
|
return;
|
|
@@ -4613,7 +4786,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4613
4786
|
lastUsedAt: s.lastUsedAt || null,
|
|
4614
4787
|
usageCount: s.usageCount || 0,
|
|
4615
4788
|
}));
|
|
4616
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4789
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4617
4790
|
res.end(JSON.stringify({ secrets }));
|
|
4618
4791
|
} catch(_) { res.writeHead(500); res.end('{"secrets":[]}'); }
|
|
4619
4792
|
return;
|
|
@@ -4770,7 +4943,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4770
4943
|
const goalsFile = path.join(_goalsProjDir, '.monomind', 'orgs', `${orgName}-goals.json`);
|
|
4771
4944
|
let data = { goals: [] };
|
|
4772
4945
|
try { data = JSON.parse(fs.readFileSync(goalsFile, 'utf8')); } catch(_) {}
|
|
4773
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4946
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4774
4947
|
res.end(JSON.stringify({ goals: data.goals || [] }));
|
|
4775
4948
|
} catch(_) { res.writeHead(500); res.end('{"goals":[]}'); }
|
|
4776
4949
|
return;
|
|
@@ -4819,7 +4992,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4819
4992
|
}
|
|
4820
4993
|
} catch(_) {}
|
|
4821
4994
|
}
|
|
4822
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4995
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4823
4996
|
res.end(JSON.stringify({ routines: data.routines || [] }));
|
|
4824
4997
|
} catch(_) { res.writeHead(500); res.end('{"routines":[]}'); }
|
|
4825
4998
|
return;
|
|
@@ -4841,7 +5014,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4841
5014
|
const payload = { org: orgName, updated_at: new Date().toISOString(), goals: parsed.goals };
|
|
4842
5015
|
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
4843
5016
|
fs.renameSync(tmp, goalsFile);
|
|
4844
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5017
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4845
5018
|
res.end(JSON.stringify({ ok: true, count: parsed.goals.length }));
|
|
4846
5019
|
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
4847
5020
|
return;
|
|
@@ -4863,7 +5036,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4863
5036
|
const payload = { org: orgName, updated_at: new Date().toISOString(), routines: parsed.routines };
|
|
4864
5037
|
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
4865
5038
|
fs.renameSync(tmp, routinesFile);
|
|
4866
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5039
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4867
5040
|
res.end(JSON.stringify({ ok: true, count: parsed.routines.length }));
|
|
4868
5041
|
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
4869
5042
|
return;
|
|
@@ -4917,7 +5090,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4917
5090
|
if (fs.existsSync(agentsDir)) walkAgents(agentsDir);
|
|
4918
5091
|
}
|
|
4919
5092
|
files.sort((a, b) => new Date(b.mtime) - new Date(a.mtime));
|
|
4920
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5093
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
4921
5094
|
res.end(JSON.stringify(files));
|
|
4922
5095
|
} catch (e) { res.writeHead(500); res.end(JSON.stringify({ error: e.message })); }
|
|
4923
5096
|
return;
|
|
@@ -4941,7 +5114,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4941
5114
|
if (!stat.isFile()) { res.writeHead(400); res.end('Not a file'); return; }
|
|
4942
5115
|
if (stat.size > 524288) { res.writeHead(413); res.end('File too large'); return; }
|
|
4943
5116
|
const content = fs.readFileSync(resolved, 'utf8');
|
|
4944
|
-
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
5117
|
+
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4945
5118
|
res.end(content);
|
|
4946
5119
|
} catch(_) { res.writeHead(500); res.end('Internal error'); }
|
|
4947
5120
|
return;
|
|
@@ -4984,7 +5157,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4984
5157
|
const deleteEvent = { type: 'org:delete', org: orgName, ts: Date.now() };
|
|
4985
5158
|
appendToFile(path.join(projectDir || process.cwd(), 'data', 'mastermind-events.jsonl'), JSON.stringify(deleteEvent) + '\n').catch(() => {});
|
|
4986
5159
|
broadcastMm(deleteEvent);
|
|
4987
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5160
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4988
5161
|
res.end('{"ok":true}');
|
|
4989
5162
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4990
5163
|
return;
|
|
@@ -5008,7 +5181,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5008
5181
|
fs.writeFileSync(path.join(stopDir, `${orgName}.stop`), String(Date.now()));
|
|
5009
5182
|
} catch(_) {}
|
|
5010
5183
|
broadcastMm(stopEvent);
|
|
5011
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5184
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5012
5185
|
res.end('{"ok":true}');
|
|
5013
5186
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
5014
5187
|
return;
|
|
@@ -5034,7 +5207,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5034
5207
|
try { fs.mkdirSync(destOrgsDir, { recursive: true }); } catch(_) {}
|
|
5035
5208
|
const destFile = path.join(destOrgsDir, `${orgName}.json`);
|
|
5036
5209
|
fs.copyFileSync(srcFile, destFile);
|
|
5037
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5210
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5038
5211
|
res.end(JSON.stringify({ ok: true, destFile }));
|
|
5039
5212
|
} catch(e) { res.writeHead(500); res.end(JSON.stringify({ error: String(e.message || e) })); }
|
|
5040
5213
|
return;
|
|
@@ -5121,7 +5294,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5121
5294
|
break;
|
|
5122
5295
|
}
|
|
5123
5296
|
}
|
|
5124
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5297
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5125
5298
|
res.end(JSON.stringify(runs));
|
|
5126
5299
|
} catch (_) { res.writeHead(500); res.end('[]'); }
|
|
5127
5300
|
return;
|
|
@@ -5147,7 +5320,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5147
5320
|
if (!fs.existsSync(_tf)) continue;
|
|
5148
5321
|
const _tLines = fs.readFileSync(_tf, 'utf8').split('\n').filter(Boolean);
|
|
5149
5322
|
const _tEvs = _tLines.map(l => { try { const e = JSON.parse(l); return { type: 'org:comms', from: e.role || e.from || 'agent', to: e.to || 'all', msg: e.message || e.msg || '', ts: e.ts ? (typeof e.ts === 'number' ? e.ts : new Date(e.ts).getTime()) : Date.now(), org: _rvOrgName, runId: _rvRunId }; } catch { return null; } }).filter(Boolean);
|
|
5150
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5323
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5151
5324
|
res.end(JSON.stringify(_tEvs));
|
|
5152
5325
|
return;
|
|
5153
5326
|
}
|
|
@@ -5187,7 +5360,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5187
5360
|
// For in-progress runs (no .warm.jsonl), org:comms also go to .convs.jsonl (stripped form).
|
|
5188
5361
|
// They're already in .jsonl as full events, so .convs.jsonl would duplicate — skip it.
|
|
5189
5362
|
events.sort((a, b) => (a.ts || 0) - (b.ts || 0));
|
|
5190
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5363
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5191
5364
|
res.end(JSON.stringify(events));
|
|
5192
5365
|
} catch (_) { res.writeHead(500); res.end('[]'); }
|
|
5193
5366
|
return;
|
|
@@ -5210,12 +5383,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5210
5383
|
// Reject files >2MB to avoid blocking the event loop
|
|
5211
5384
|
if (_size > 2 * 1024 * 1024) { res.writeHead(413); res.end(JSON.stringify({ error: 'file too large', size: _size })); return; }
|
|
5212
5385
|
if (!_mime.startsWith('text/') && _mime !== 'application/json') {
|
|
5213
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5386
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5214
5387
|
res.end(JSON.stringify({ binary: true, mimeType: _mime, size: _size }));
|
|
5215
5388
|
return;
|
|
5216
5389
|
}
|
|
5217
5390
|
const _content = fs.readFileSync(_filePath, 'utf8');
|
|
5218
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5391
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5219
5392
|
res.end(JSON.stringify({ content: _content, mimeType: _mime, size: _size }));
|
|
5220
5393
|
} catch (_e) { res.writeHead(500); res.end(JSON.stringify({ error: 'read failed' })); }
|
|
5221
5394
|
return;
|
|
@@ -5233,7 +5406,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5233
5406
|
'Content-Type': 'text/event-stream',
|
|
5234
5407
|
'Cache-Control': 'no-cache',
|
|
5235
5408
|
'Connection': 'keep-alive',
|
|
5236
|
-
'Access-Control-Allow-Origin':
|
|
5409
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
5237
5410
|
});
|
|
5238
5411
|
res.write(': connected\n\n');
|
|
5239
5412
|
addMmClient(res);
|
|
@@ -5300,7 +5473,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5300
5473
|
}
|
|
5301
5474
|
}
|
|
5302
5475
|
allSessions.sort((a,b) => (b.ts||b.startedAt||0)-(a.ts||a.startedAt||0));
|
|
5303
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5476
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5304
5477
|
res.end(JSON.stringify(allSessions.slice(0, limitParam)));
|
|
5305
5478
|
} catch (_) { res.writeHead(200); res.end('[]'); }
|
|
5306
5479
|
return;
|
|
@@ -5332,7 +5505,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5332
5505
|
else if (ev.type === 'session:complete') lines.push(`\`${t}\` **SESSION COMPLETE** — status: ${ev.status}, domains: ${(ev.domains || []).join(', ')}`);
|
|
5333
5506
|
else lines.push(`\`${t}\` ${ev.type} ${JSON.stringify(ev)}`);
|
|
5334
5507
|
}
|
|
5335
|
-
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
5508
|
+
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5336
5509
|
res.end(lines.join('\n'));
|
|
5337
5510
|
} catch (_) { res.writeHead(500); res.end('Error'); }
|
|
5338
5511
|
return;
|
|
@@ -5345,14 +5518,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5345
5518
|
// Check individual session file first
|
|
5346
5519
|
const sessFile = path.join(projectDir || process.cwd(), 'data', 'sessions', `${sid}.json`);
|
|
5347
5520
|
if (fs.existsSync(sessFile)) {
|
|
5348
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5521
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5349
5522
|
res.end(fs.readFileSync(sessFile, 'utf8'));
|
|
5350
5523
|
return;
|
|
5351
5524
|
}
|
|
5352
5525
|
const f = path.join(projectDir || process.cwd(), 'data', 'mastermind-sessions.json');
|
|
5353
5526
|
const sessions = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
5354
5527
|
const s = sessions.find(x => x.id === sid);
|
|
5355
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5528
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5356
5529
|
res.end(JSON.stringify(s || null));
|
|
5357
5530
|
} catch (_) { res.writeHead(200); res.end('null'); }
|
|
5358
5531
|
return;
|
|
@@ -5419,7 +5592,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5419
5592
|
const lines = fs.readFileSync(evPath, 'utf8').split('\n').filter(l => l.trim()).slice(-10);
|
|
5420
5593
|
recentEvents = lines.map(l => { try { return JSON.parse(l); } catch(_) { return null; } }).filter(Boolean);
|
|
5421
5594
|
} catch(_) {}
|
|
5422
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5595
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5423
5596
|
res.end(JSON.stringify({
|
|
5424
5597
|
ts: Date.now(),
|
|
5425
5598
|
pid: process.pid,
|
|
@@ -5469,7 +5642,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5469
5642
|
const detectedRunId = path.basename(runFile, '.jsonl');
|
|
5470
5643
|
const lines = fs.readFileSync(runFile, 'utf8').split('\n').filter(l => l.trim()).slice(-100);
|
|
5471
5644
|
const events = lines.map(l => { try { return JSON.parse(l); } catch(_) { return null; } }).filter(Boolean);
|
|
5472
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5645
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5473
5646
|
res.end(JSON.stringify({ runId: detectedRunId, events, active: activeOrgRuns.has(orgName) }));
|
|
5474
5647
|
} catch(err) {
|
|
5475
5648
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -5672,7 +5845,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5672
5845
|
} catch (_) {}
|
|
5673
5846
|
}
|
|
5674
5847
|
}
|
|
5675
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5848
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5676
5849
|
res.end(JSON.stringify(result));
|
|
5677
5850
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5678
5851
|
return;
|
|
@@ -5746,12 +5919,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5746
5919
|
const dir = path.resolve(qp.get('dir') || projectDir || process.cwd());
|
|
5747
5920
|
const covPath = path.join(dir, '.monomind', 'audit', 'coverage.json');
|
|
5748
5921
|
if (!fs.existsSync(covPath)) {
|
|
5749
|
-
res.writeHead(404, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5922
|
+
res.writeHead(404, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5750
5923
|
res.end('{}');
|
|
5751
5924
|
return;
|
|
5752
5925
|
}
|
|
5753
5926
|
const coverage = JSON.parse(fs.readFileSync(covPath, 'utf8'));
|
|
5754
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5927
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5755
5928
|
res.end(JSON.stringify(coverage));
|
|
5756
5929
|
} catch (e) {
|
|
5757
5930
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -5819,7 +5992,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5819
5992
|
const _mcLine = `data: ${JSON.stringify(_mcEvent)}\n\n`;
|
|
5820
5993
|
for (const _cl of _mcFwdClients) { try { _cl.write(_mcLine); } catch (_) { _mcFwdClients.delete(_cl); } }
|
|
5821
5994
|
}
|
|
5822
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5995
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5823
5996
|
res.end(JSON.stringify({ ok: true, runId: _mcRunId }));
|
|
5824
5997
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5825
5998
|
return;
|
|
@@ -5837,7 +6010,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5837
6010
|
'Content-Type': 'text/event-stream',
|
|
5838
6011
|
'Cache-Control': 'no-cache',
|
|
5839
6012
|
'Connection': 'keep-alive',
|
|
5840
|
-
'Access-Control-Allow-Origin':
|
|
6013
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
5841
6014
|
'X-Accel-Buffering': 'no',
|
|
5842
6015
|
});
|
|
5843
6016
|
res.write(': connected\n\n');
|
|
@@ -5934,7 +6107,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5934
6107
|
const _chLine = `data: ${JSON.stringify(_chEvent)}\n\n`;
|
|
5935
6108
|
for (const _cl of _chFwdClients) { try { _cl.write(_chLine); } catch(_) { _chFwdClients.delete(_cl); } }
|
|
5936
6109
|
}
|
|
5937
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
6110
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5938
6111
|
res.end(JSON.stringify({ ok: true }));
|
|
5939
6112
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5940
6113
|
return;
|
|
@@ -6338,9 +6511,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6338
6511
|
clearInterval(_heartbeatTimer);
|
|
6339
6512
|
// Flush SQLite run-event index to disk before exit (bypasses 1000ms debounce timer)
|
|
6340
6513
|
clearTimeout(_runDbPersistTimer);
|
|
6341
|
-
|
|
6342
|
-
try { fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export())); } catch (_) {}
|
|
6343
|
-
}
|
|
6514
|
+
_writeRunDbSnapshot();
|
|
6344
6515
|
for (const w of activeWatchers) {
|
|
6345
6516
|
try {
|
|
6346
6517
|
w.close();
|