monomind 2.0.3 → 2.1.3
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/README.md +74 -92
- package/package.json +11 -7
- 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 +99 -28
- package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +18 -9
- package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
- package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +33 -4
- package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +75 -45
- 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 +75 -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/monograph.cjs +73 -2
- package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
- package/packages/@monomind/cli/README.md +74 -92
- package/packages/@monomind/cli/bin/cli.js +10 -1
- package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
- package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
- 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-env-checks.js +5 -0
- 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 +321 -127
- package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
- 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 +93 -55
- 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/auto-install.d.ts +8 -8
- package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
- 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 +56 -35
- 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 +88 -41
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
- package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
- 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/ewc-consolidation.d.ts +12 -0
- 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/memory/sona-optimizer.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/orgrt/broker.js +73 -0
- package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +46 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +201 -18
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +138 -8
- package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
- package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
- package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
- 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 +9 -0
- package/packages/@monomind/cli/dist/src/orgrt/server.js +39 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +10 -1
- package/packages/@monomind/cli/dist/src/orgrt/session.js +24 -1
- 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/orgrt/types.d.ts +52 -52
- package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
- package/packages/@monomind/cli/dist/src/output.js +22 -7
- 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/types.d.ts +2 -2
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +243 -49
- package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
- package/packages/@monomind/cli/dist/src/ui/orgs.html +79 -11
- package/packages/@monomind/cli/dist/src/ui/server.mjs +346 -139
- 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 +15 -15
|
@@ -2,12 +2,16 @@ 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';
|
|
8
9
|
import { addSseClient, removeSseClient, broadcast, getSseClientCount, closeSseClients, addMmClient, removeMmClient, broadcastMm, getMmClientCount } from './sse-manager.mjs';
|
|
9
10
|
|
|
10
11
|
const JSONL_SIZE_CAP = 10 * 1024 * 1024; // 10 MB — skip files larger than this in /api/graph
|
|
12
|
+
// Session id format for data/sessions/<id>.jsonl persistence — no path traversal (".."), starts
|
|
13
|
+
// with a word char, rest is word chars/dot/dash. Shared by every session-id-accepting endpoint.
|
|
14
|
+
const SESSION_ID_RE = /^(?!.*\.\.)[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/;
|
|
11
15
|
const buildDocsState = new Map();
|
|
12
16
|
|
|
13
17
|
// Pricing per token (mirrors token-tracker.cjs FALLBACK_PRICING)
|
|
@@ -242,12 +246,77 @@ async function _initRunDb(monoHome) {
|
|
|
242
246
|
}
|
|
243
247
|
}
|
|
244
248
|
|
|
249
|
+
// Single-writer guard for the run-events.db snapshot persist: bindServer() can start a
|
|
250
|
+
// SECOND dashboard instance on port+1 when 4242 is already taken (two projects' dashboards
|
|
251
|
+
// running simultaneously, or a stale-but-still-bound old instance). Each instance holds an
|
|
252
|
+
// independent in-memory sql.js copy; without a lock, each instance's periodic full-snapshot
|
|
253
|
+
// overwrite silently clobbers the other's accumulated events (last writer wins, no error).
|
|
254
|
+
// Claiming this lock before every snapshot write ensures only ONE live instance actually
|
|
255
|
+
// persists at a time — the other still runs, just skips its write until it can claim the
|
|
256
|
+
// lock (e.g. after the current holder exits and its lock goes stale). This doesn't merge
|
|
257
|
+
// both instances' events into one file, but it stops them from destructively overwriting
|
|
258
|
+
// each other. Pattern mirrors .claude/helpers/utils/fs-helpers.cjs's claimLock/releaseLock
|
|
259
|
+
// (wx-create, rename-to-reclaim-stale) — replicated inline here since this file ships
|
|
260
|
+
// standalone in the published package and can't depend on repo-local dev tooling.
|
|
261
|
+
function _runDbLockPath() {
|
|
262
|
+
return _runDbPath ? `${_runDbPath}.lock` : null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function _claimRunDbLock(staleMs) {
|
|
266
|
+
const lockPath = _runDbLockPath();
|
|
267
|
+
if (!lockPath) return false;
|
|
268
|
+
staleMs = staleMs || 5000;
|
|
269
|
+
const tryCreate = () => {
|
|
270
|
+
try {
|
|
271
|
+
fs.writeFileSync(lockPath, String(process.pid), { flag: 'wx' });
|
|
272
|
+
return true;
|
|
273
|
+
} catch (_) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
if (tryCreate()) return true;
|
|
278
|
+
try {
|
|
279
|
+
const stat = fs.statSync(lockPath);
|
|
280
|
+
if (Date.now() - stat.mtimeMs < staleMs) return false; // held by a live/fresh owner
|
|
281
|
+
// Stale lock — reclaim via atomic rename so only one racing process wins it.
|
|
282
|
+
const claimed = `${lockPath}.${process.pid}.${Date.now()}.stale`;
|
|
283
|
+
try {
|
|
284
|
+
fs.renameSync(lockPath, claimed);
|
|
285
|
+
} catch (_) {
|
|
286
|
+
return false; // another process already reclaimed it
|
|
287
|
+
}
|
|
288
|
+
try { fs.unlinkSync(claimed); } catch (_) {}
|
|
289
|
+
return tryCreate();
|
|
290
|
+
} catch (_) {
|
|
291
|
+
// Lock vanished between our failed create and this stat — retry once.
|
|
292
|
+
return tryCreate();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function _releaseRunDbLock() {
|
|
297
|
+
const lockPath = _runDbLockPath();
|
|
298
|
+
if (!lockPath) return;
|
|
299
|
+
try {
|
|
300
|
+
if (Number(fs.readFileSync(lockPath, 'utf8')) === process.pid) fs.unlinkSync(lockPath);
|
|
301
|
+
} catch (_) {}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function _writeRunDbSnapshot() {
|
|
305
|
+
if (!_runDb || !_runDbPath) return;
|
|
306
|
+
if (!_claimRunDbLock()) return; // another live instance owns the write right now
|
|
307
|
+
try {
|
|
308
|
+
fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export()));
|
|
309
|
+
} catch (_) {
|
|
310
|
+
// best-effort — matches prior swallow-on-error behavior
|
|
311
|
+
} finally {
|
|
312
|
+
_releaseRunDbLock();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
245
316
|
function _persistRunDb() {
|
|
246
317
|
if (!_runDb || !_runDbPath) return;
|
|
247
318
|
clearTimeout(_runDbPersistTimer);
|
|
248
|
-
_runDbPersistTimer = setTimeout(
|
|
249
|
-
try { fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export())); } catch (_) {}
|
|
250
|
-
}, 1000);
|
|
319
|
+
_runDbPersistTimer = setTimeout(_writeRunDbSnapshot, 1000);
|
|
251
320
|
}
|
|
252
321
|
|
|
253
322
|
function _insertRunEvent(ev, source) {
|
|
@@ -266,6 +335,50 @@ function _insertRunEvent(ev, source) {
|
|
|
266
335
|
}
|
|
267
336
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
268
337
|
|
|
338
|
+
// ── Bounded file reads for session JSONL (P3-6b) ────────────────────────────
|
|
339
|
+
// /api/session already caps memory correctly by tailing; /api/session-journal and
|
|
340
|
+
// /api/session-errors used to fs.readFileSync() whole session files regardless of
|
|
341
|
+
// size, so one request against a multi-hundred-MB session log (a real scenario for
|
|
342
|
+
// long-running org sessions) buffers the entire file. These helpers cap the read to
|
|
343
|
+
// a fixed byte window via openSync/readSync (mirrors the byte-offset tail-read the
|
|
344
|
+
// /api/loops handler already uses for ScheduleWakeup lookups) instead of loading the
|
|
345
|
+
// whole file.
|
|
346
|
+
function _readTailLines(filePath, maxBytes) {
|
|
347
|
+
maxBytes = maxBytes || 4 * 1024 * 1024; // 4MB — generous for the most recent activity
|
|
348
|
+
const stat = fs.statSync(filePath);
|
|
349
|
+
const start = Math.max(0, stat.size - maxBytes);
|
|
350
|
+
const len = stat.size - start;
|
|
351
|
+
const buf = Buffer.alloc(len);
|
|
352
|
+
const fd = fs.openSync(filePath, 'r');
|
|
353
|
+
try {
|
|
354
|
+
fs.readSync(fd, buf, 0, len, start);
|
|
355
|
+
} finally {
|
|
356
|
+
fs.closeSync(fd);
|
|
357
|
+
}
|
|
358
|
+
let text = buf.toString('utf8');
|
|
359
|
+
if (start > 0) {
|
|
360
|
+
// We started mid-file — the first line is very likely a partial line, drop it.
|
|
361
|
+
const nl = text.indexOf('\n');
|
|
362
|
+
text = nl === -1 ? '' : text.slice(nl + 1);
|
|
363
|
+
}
|
|
364
|
+
return text.split('\n').filter(Boolean);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function _readHeadText(filePath, maxBytes) {
|
|
368
|
+
maxBytes = maxBytes || 8192; // enough for the JSONL header line(s) we need to inspect
|
|
369
|
+
const stat = fs.statSync(filePath);
|
|
370
|
+
const len = Math.min(maxBytes, stat.size);
|
|
371
|
+
const buf = Buffer.alloc(len);
|
|
372
|
+
const fd = fs.openSync(filePath, 'r');
|
|
373
|
+
try {
|
|
374
|
+
fs.readSync(fd, buf, 0, len, 0);
|
|
375
|
+
} finally {
|
|
376
|
+
fs.closeSync(fd);
|
|
377
|
+
}
|
|
378
|
+
return buf.toString('utf8');
|
|
379
|
+
}
|
|
380
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
381
|
+
|
|
269
382
|
function appendToFile(filePath, line) {
|
|
270
383
|
const prev = _writeQueue.get(filePath) || Promise.resolve();
|
|
271
384
|
const next = prev.then(() => {
|
|
@@ -466,7 +579,7 @@ function bindServer(server, port) {
|
|
|
466
579
|
let attempt = 0;
|
|
467
580
|
|
|
468
581
|
function tryPort(p) {
|
|
469
|
-
server.listen(p, () => resolve(p));
|
|
582
|
+
server.listen(p, '127.0.0.1', () => resolve(p));
|
|
470
583
|
server.once('error', (err) => {
|
|
471
584
|
if (err.code === 'EADDRINUSE' && attempt < maxTries) {
|
|
472
585
|
attempt += 1;
|
|
@@ -498,10 +611,26 @@ function bindServer(server, port) {
|
|
|
498
611
|
* uses a greedy BFS over the real filesystem to find the correct path.
|
|
499
612
|
* Falls back to cwd in session files, then to direct slug replacement.
|
|
500
613
|
*/
|
|
614
|
+
const _slugPathCache = new Map();
|
|
615
|
+
|
|
501
616
|
function resolveSlugToPath(slug, projDir) {
|
|
502
|
-
|
|
617
|
+
if (_slugPathCache.has(slug)) return _slugPathCache.get(slug);
|
|
618
|
+
const resolved = _resolveSlugToPathUncached(slug, projDir);
|
|
619
|
+
_slugPathCache.set(slug, resolved);
|
|
620
|
+
return resolved;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function _resolveSlugToPathUncached(slug, projDir) {
|
|
624
|
+
// 1. Try filesystem BFS (most reliable). Branching is O(2^hyphens) in the
|
|
625
|
+
// worst case (a slug with N hyphens can require exploring both "new path
|
|
626
|
+
// segment" and "hyphen is part of this segment's name" at each of the N
|
|
627
|
+
// positions) — bound total exploration so a slug with many hyphens (e.g.
|
|
628
|
+
// a UUID-embedded temp/scratchpad dir) can't hang the event loop.
|
|
503
629
|
const parts = slug.replace(/^-/, '').split('-');
|
|
630
|
+
const MAX_CALLS = 20000;
|
|
631
|
+
let calls = 0;
|
|
504
632
|
function tryPaths(idx, current) {
|
|
633
|
+
if (++calls > MAX_CALLS) return null;
|
|
505
634
|
if (idx === parts.length) return fs.existsSync(current) ? current : null;
|
|
506
635
|
// Option A: next part is a new path component
|
|
507
636
|
const asDir = path.join(current, parts[idx]);
|
|
@@ -534,6 +663,18 @@ function resolveSlugToPath(slug, projDir) {
|
|
|
534
663
|
}
|
|
535
664
|
|
|
536
665
|
export async function startServer({ port = 4242, projectDir, openBrowser = true } = {}) {
|
|
666
|
+
// ── Security: per-process auth credential for mutating (non-GET) requests ─
|
|
667
|
+
// Generated once per server start and written to a well-known location so
|
|
668
|
+
// trusted local callers (CLI, control-start.cjs) can read it and pass it
|
|
669
|
+
// back via an auth header or query param on non-GET requests.
|
|
670
|
+
const authBytes = crypto.randomBytes(24);
|
|
671
|
+
const dashboardAuthValue = authBytes.toString('hex');
|
|
672
|
+
try {
|
|
673
|
+
const authFileDir = path.join(projectDir || process.cwd(), '.monomind');
|
|
674
|
+
fs.mkdirSync(authFileDir, { recursive: true });
|
|
675
|
+
fs.writeFileSync(path.join(authFileDir, 'dashboard-token'), dashboardAuthValue, { mode: 0o600 });
|
|
676
|
+
} catch (_) {}
|
|
677
|
+
|
|
537
678
|
// Parse a .claude/agents/*.md definition into { name, description, capability{}, document }.
|
|
538
679
|
// Tolerant line-based parse of the YAML frontmatter (expertise / task_types as lists).
|
|
539
680
|
function parseAgentDef(raw) {
|
|
@@ -568,7 +709,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
568
709
|
// Extracted from the request dispatcher to reduce cyclomatic complexity.
|
|
569
710
|
// Handles POST /api/mastermind/event: parses body, enriches with runId/session,
|
|
570
711
|
// persists to JSONL files, broadcasts to SSE clients, returns {ok:true}.
|
|
571
|
-
async function handleMastermindEvent(req, res) {
|
|
712
|
+
async function handleMastermindEvent(req, res, corsOrigin) {
|
|
572
713
|
let body = '';
|
|
573
714
|
for await (const chunk of req) { body += chunk; if (body.length > 2097152) { req.destroy(); break; } }
|
|
574
715
|
let event = {};
|
|
@@ -765,7 +906,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
765
906
|
// Format: data/sessions/<sessionId>.jsonl + data/sessions/_index.json
|
|
766
907
|
try {
|
|
767
908
|
const _sid = String(event.session || '').trim();
|
|
768
|
-
if (_sid.length > 0 && _sid.length <= 128 &&
|
|
909
|
+
if (_sid.length > 0 && _sid.length <= 128 && SESSION_ID_RE.test(_sid)) {
|
|
769
910
|
const sessDir = path.join(dataDir, 'sessions');
|
|
770
911
|
fs.mkdirSync(sessDir, { recursive: true });
|
|
771
912
|
// Append event to per-session JSONL (O(1), no read)
|
|
@@ -845,13 +986,46 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
845
986
|
}
|
|
846
987
|
}
|
|
847
988
|
}
|
|
848
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
989
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
849
990
|
res.end('{"ok":true}');
|
|
850
991
|
}
|
|
851
992
|
|
|
852
993
|
const server = http.createServer(async (req, res) => {
|
|
853
994
|
const url = req.url.split('?')[0];
|
|
854
995
|
|
|
996
|
+
// ── Security: strict CORS allow-list ────────────────────────────────────
|
|
997
|
+
// Only reflect Origin when it is this dashboard's own loopback origin.
|
|
998
|
+
// Otherwise the header is omitted entirely, so cross-origin reads fail
|
|
999
|
+
// closed rather than defaulting to '*'.
|
|
1000
|
+
const _reqOrigin = req.headers.origin || '';
|
|
1001
|
+
const _boundPortForCors = currentPort || boundPort || port;
|
|
1002
|
+
const _allowedOrigins = new Set([
|
|
1003
|
+
`http://localhost:${_boundPortForCors}`,
|
|
1004
|
+
`http://127.0.0.1:${_boundPortForCors}`,
|
|
1005
|
+
]);
|
|
1006
|
+
const corsOrigin = _allowedOrigins.has(_reqOrigin) ? _reqOrigin : undefined;
|
|
1007
|
+
|
|
1008
|
+
// ── Security: require a matching auth credential on all non-GET routes ──
|
|
1009
|
+
// GET requests remain open (read-only loopback dashboard viewing).
|
|
1010
|
+
if (req.method !== 'GET' && req.method !== 'HEAD' && req.method !== 'OPTIONS') {
|
|
1011
|
+
let _suppliedAuth = req.headers['x-monomind-token'] || '';
|
|
1012
|
+
if (!_suppliedAuth) {
|
|
1013
|
+
try { _suppliedAuth = new URL(req.url, 'http://localhost').searchParams.get('token') || ''; } catch (_) {}
|
|
1014
|
+
}
|
|
1015
|
+
const _suppliedAuthBuf = Buffer.from(String(_suppliedAuth));
|
|
1016
|
+
const _expectedAuthBuf = Buffer.from(dashboardAuthValue);
|
|
1017
|
+
const _authMatches = _suppliedAuthBuf.length === _expectedAuthBuf.length
|
|
1018
|
+
&& crypto.timingSafeEqual(_suppliedAuthBuf, _expectedAuthBuf);
|
|
1019
|
+
if (!_suppliedAuth || !_authMatches) {
|
|
1020
|
+
res.writeHead(401, {
|
|
1021
|
+
'Content-Type': 'application/json',
|
|
1022
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1023
|
+
});
|
|
1024
|
+
res.end(JSON.stringify({ error: 'Unauthorized: missing or invalid auth token' }));
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
855
1029
|
// ------------------------------------------------------------------ GET /
|
|
856
1030
|
if (req.method === 'GET' && url === '/') {
|
|
857
1031
|
const htmlPath = path.join(__dirname, 'dashboard.html');
|
|
@@ -905,11 +1079,11 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
905
1079
|
}
|
|
906
1080
|
let branch = '';
|
|
907
1081
|
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':
|
|
1082
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
909
1083
|
res.end(JSON.stringify({ name, email, cwd, remoteUrl, branch }));
|
|
910
1084
|
} catch (_) {
|
|
911
1085
|
const cwd2 = projectDir || process.cwd();
|
|
912
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1086
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
913
1087
|
res.end(JSON.stringify({ name: '', email: '', cwd: cwd2, remoteUrl: '', branch: '' }));
|
|
914
1088
|
}
|
|
915
1089
|
return;
|
|
@@ -923,7 +1097,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
923
1097
|
const snapshot = await collectAll(dir);
|
|
924
1098
|
res.writeHead(200, {
|
|
925
1099
|
'Content-Type': 'application/json',
|
|
926
|
-
'Access-Control-Allow-Origin':
|
|
1100
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
927
1101
|
'Cache-Control': 'no-cache',
|
|
928
1102
|
});
|
|
929
1103
|
res.end(JSON.stringify(snapshot));
|
|
@@ -950,7 +1124,11 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
950
1124
|
// system files to any process that can reach localhost:4242.
|
|
951
1125
|
const _resolvedFile = path.resolve(file);
|
|
952
1126
|
const _homeDir = os.homedir();
|
|
953
|
-
|
|
1127
|
+
// Bare-prefix check (no path.sep) is bypassable: with home "/Users/bob", a path
|
|
1128
|
+
// like "/Users/bobsecrets/x.jsonl" starts with the literal string "/Users/bob"
|
|
1129
|
+
// even though it's a sibling directory, not a subdirectory of home. Require the
|
|
1130
|
+
// separator (or exact equality to the home dir itself) so only true descendants pass.
|
|
1131
|
+
if (_resolvedFile !== _homeDir && !_resolvedFile.startsWith(_homeDir + path.sep)) {
|
|
954
1132
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
955
1133
|
res.end(JSON.stringify({ error: 'Access denied: file must be within the home directory' }));
|
|
956
1134
|
return;
|
|
@@ -967,7 +1145,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
967
1145
|
const events = parseSessionLines(lines);
|
|
968
1146
|
res.writeHead(200, {
|
|
969
1147
|
'Content-Type': 'application/json',
|
|
970
|
-
'Access-Control-Allow-Origin':
|
|
1148
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
971
1149
|
'Cache-Control': 'no-cache',
|
|
972
1150
|
});
|
|
973
1151
|
res.end(JSON.stringify({ events, total: allLines.length, shown: lines.length }));
|
|
@@ -1005,7 +1183,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1005
1183
|
const modelBreakdown = {};
|
|
1006
1184
|
const filesTouchedSet = new Set();
|
|
1007
1185
|
try {
|
|
1008
|
-
const lines =
|
|
1186
|
+
const lines = _readTailLines(fp);
|
|
1009
1187
|
let pendingCompact = false;
|
|
1010
1188
|
for (const line of lines) {
|
|
1011
1189
|
let e; try { e = JSON.parse(line); } catch { continue; }
|
|
@@ -1062,7 +1240,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1062
1240
|
const summary = summaries.length ? summaries[summaries.length - 1].text : null;
|
|
1063
1241
|
sessions.push({ id, mtime, firstTs, lastTs, lastPrompt, summaries, summary, compactCount, errorCount, totalDurationMs, totalMessages, totalCost, toolCalls, userMessages, cacheReadTokens, totalInputTokens, modelBreakdown, filesTouched, file: fp });
|
|
1064
1242
|
}
|
|
1065
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1243
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1066
1244
|
res.end(JSON.stringify({ sessions }));
|
|
1067
1245
|
} catch (err) {
|
|
1068
1246
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1077,7 +1255,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1077
1255
|
const dir = qs.get('dir') || '';
|
|
1078
1256
|
const q = (qs.get('q') || '').toLowerCase().trim();
|
|
1079
1257
|
if (!q || q.length < 2) {
|
|
1080
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1258
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1081
1259
|
res.end(JSON.stringify({ results: [] }));
|
|
1082
1260
|
return;
|
|
1083
1261
|
}
|
|
@@ -1118,7 +1296,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1118
1296
|
} catch {}
|
|
1119
1297
|
if (matches.length) results.push({ id, file: fp, lastPrompt, mtime, matches });
|
|
1120
1298
|
}
|
|
1121
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1299
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1122
1300
|
res.end(JSON.stringify({ results, q }));
|
|
1123
1301
|
} catch (err) {
|
|
1124
1302
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1181,7 +1359,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1181
1359
|
return tb - ta;
|
|
1182
1360
|
});
|
|
1183
1361
|
|
|
1184
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1362
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1185
1363
|
res.end(JSON.stringify({ events: events.slice(0, limit) }));
|
|
1186
1364
|
} catch (err) {
|
|
1187
1365
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1233,7 +1411,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1233
1411
|
const errors = Object.entries(errorCounts)
|
|
1234
1412
|
.map(([tool, count]) => ({ tool, count, total: totalCounts[tool] || count }))
|
|
1235
1413
|
.sort((a,b) => b.count - a.count);
|
|
1236
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1414
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1237
1415
|
res.end(JSON.stringify({ errors }));
|
|
1238
1416
|
} catch (err) {
|
|
1239
1417
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1284,7 +1462,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1284
1462
|
const tools = Object.entries(toolCounts)
|
|
1285
1463
|
.map(([tool, count]) => ({ tool, count, errors: errorCounts[tool] || 0 }))
|
|
1286
1464
|
.sort((a,b) => b.count - a.count);
|
|
1287
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1465
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1288
1466
|
res.end(JSON.stringify({ tools }));
|
|
1289
1467
|
} catch (err) {
|
|
1290
1468
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1319,7 +1497,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1319
1497
|
if (totalCost > 0) projectCosts.push({ path: projPath, cost: totalCost, sessions: sessionFiles.length });
|
|
1320
1498
|
}
|
|
1321
1499
|
projectCosts.sort((a, b) => b.cost - a.cost);
|
|
1322
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1500
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1323
1501
|
res.end(JSON.stringify({ projects: projectCosts }));
|
|
1324
1502
|
} catch (err) {
|
|
1325
1503
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1352,7 +1530,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1352
1530
|
} catch {}
|
|
1353
1531
|
return { slug, path: projPath, name, sessionCount, memoryCount, lastActivity: lastActivity || null };
|
|
1354
1532
|
}).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':
|
|
1533
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1356
1534
|
res.end(JSON.stringify({ projects }));
|
|
1357
1535
|
} catch (err) {
|
|
1358
1536
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1381,7 +1559,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1381
1559
|
let kg = [];
|
|
1382
1560
|
try { const raw = fs.readFileSync(path.join(palaceDir, 'kg.json'), 'utf8'); kg = JSON.parse(raw); } catch {}
|
|
1383
1561
|
|
|
1384
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1562
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1385
1563
|
res.end(JSON.stringify({ drawers, identity, kg }));
|
|
1386
1564
|
} catch (err) {
|
|
1387
1565
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1432,7 +1610,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1432
1610
|
}
|
|
1433
1611
|
}
|
|
1434
1612
|
|
|
1435
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1613
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1436
1614
|
res.end(JSON.stringify({ adrs }));
|
|
1437
1615
|
} catch (err) {
|
|
1438
1616
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1501,7 +1679,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1501
1679
|
}
|
|
1502
1680
|
} catch {}
|
|
1503
1681
|
|
|
1504
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1682
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
1505
1683
|
res.end(JSON.stringify({ memories: memories.concat(backend), memDir }));
|
|
1506
1684
|
} catch (err) {
|
|
1507
1685
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1534,7 +1712,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1534
1712
|
}
|
|
1535
1713
|
fs.mkdirSync(memDir, { recursive: true });
|
|
1536
1714
|
fs.writeFileSync(fp, content || '', 'utf8');
|
|
1537
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1715
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1538
1716
|
res.end(JSON.stringify({ ok: true }));
|
|
1539
1717
|
} catch (err) {
|
|
1540
1718
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1567,7 +1745,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1567
1745
|
return;
|
|
1568
1746
|
}
|
|
1569
1747
|
fs.unlinkSync(fp);
|
|
1570
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1748
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1571
1749
|
res.end(JSON.stringify({ ok: true }));
|
|
1572
1750
|
} catch (err) {
|
|
1573
1751
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1588,7 +1766,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1588
1766
|
const raw = fs.readFileSync(feedbackPath, 'utf-8');
|
|
1589
1767
|
rows = raw.split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
1590
1768
|
}
|
|
1591
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1769
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1592
1770
|
res.end(JSON.stringify(rows));
|
|
1593
1771
|
} catch (err) {
|
|
1594
1772
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1663,7 +1841,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1663
1841
|
lastWrite,
|
|
1664
1842
|
memDir,
|
|
1665
1843
|
};
|
|
1666
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1844
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1667
1845
|
res.end(JSON.stringify({ stats }));
|
|
1668
1846
|
} catch (err) {
|
|
1669
1847
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1792,7 +1970,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1792
1970
|
if (hasRepeatLoops) loops = loops.filter(l => l.source !== 'scheduled_tasks_lock' && l.source !== 'schedule_wakeup_hook');
|
|
1793
1971
|
|
|
1794
1972
|
loops.sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0));
|
|
1795
|
-
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 } : {}), 'Cache-Control': 'no-cache' });
|
|
1796
1974
|
res.end(JSON.stringify({ loops }));
|
|
1797
1975
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1798
1976
|
return;
|
|
@@ -1811,7 +1989,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1811
1989
|
const loopsDir = path.join(_stopDir, '.monomind', 'loops');
|
|
1812
1990
|
fs.mkdirSync(loopsDir, { recursive: true });
|
|
1813
1991
|
fs.writeFileSync(path.join(loopsDir, `${id}.stop`), `stop-requested-${Date.now()}`);
|
|
1814
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
1992
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1815
1993
|
res.end(JSON.stringify({ ok: true }));
|
|
1816
1994
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1817
1995
|
});
|
|
@@ -1843,7 +2021,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1843
2021
|
const nowMs = Date.now();
|
|
1844
2022
|
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
2023
|
fs.writeFileSync(path.join(loopsDir, `${id}.json`), JSON.stringify(loop, null, 2));
|
|
1846
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2024
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1847
2025
|
res.end(JSON.stringify({ ok: true, id }));
|
|
1848
2026
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); }
|
|
1849
2027
|
});
|
|
@@ -1868,18 +2046,20 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1868
2046
|
if (f.includes(sessionId) || sessionId === f.replace('.jsonl', '')) { fp = path.join(projectClaudeDir, f); break; }
|
|
1869
2047
|
}
|
|
1870
2048
|
if (!fp) {
|
|
1871
|
-
// fallback: find by scanning
|
|
2049
|
+
// fallback: find by scanning — the sessionId lives in the first JSONL line, so a
|
|
2050
|
+
// small head-read is enough; no need to load the whole (potentially huge) file.
|
|
1872
2051
|
for (const f of files) {
|
|
1873
|
-
const
|
|
1874
|
-
const
|
|
1875
|
-
if (
|
|
1876
|
-
try { const first = JSON.parse(
|
|
2052
|
+
const head = _readHeadText(path.join(projectClaudeDir, f));
|
|
2053
|
+
const firstLine = head.split('\n', 1)[0];
|
|
2054
|
+
if (firstLine) {
|
|
2055
|
+
try { const first = JSON.parse(firstLine); if (first.sessionId === sessionId) { fp = path.join(projectClaudeDir, f); break; } } catch {}
|
|
1877
2056
|
}
|
|
1878
2057
|
}
|
|
1879
2058
|
}
|
|
1880
2059
|
if (!fp) { res.writeHead(404); res.end(JSON.stringify({ errors: [] })); return; }
|
|
1881
|
-
|
|
1882
|
-
|
|
2060
|
+
// Only the most recent portion of the file matters for a dashboard error feed —
|
|
2061
|
+
// tail-cap the read so a multi-hundred-MB session log doesn't get buffered whole.
|
|
2062
|
+
const lines = _readTailLines(fp);
|
|
1883
2063
|
const errors = [];
|
|
1884
2064
|
for (const line of lines) {
|
|
1885
2065
|
try {
|
|
@@ -1894,7 +2074,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1894
2074
|
}
|
|
1895
2075
|
} catch {}
|
|
1896
2076
|
}
|
|
1897
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2077
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1898
2078
|
res.end(JSON.stringify({ errors: errors.slice(0, 50) }));
|
|
1899
2079
|
} catch (err) { res.writeHead(500); res.end(JSON.stringify({ errors: [], error: err.message })); }
|
|
1900
2080
|
return;
|
|
@@ -1910,7 +2090,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1910
2090
|
'Content-Type': 'text/event-stream',
|
|
1911
2091
|
'Cache-Control': 'no-cache',
|
|
1912
2092
|
'Connection': 'keep-alive',
|
|
1913
|
-
'Access-Control-Allow-Origin':
|
|
2093
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1914
2094
|
});
|
|
1915
2095
|
const send = (ev, data) => { try { res.write(`event: ${ev}\ndata: ${JSON.stringify(data)}\n\n`); } catch {} };
|
|
1916
2096
|
send('connected', { ts: Date.now() });
|
|
@@ -1954,7 +2134,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1954
2134
|
return;
|
|
1955
2135
|
}
|
|
1956
2136
|
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':
|
|
2137
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1958
2138
|
res.end(JSON.stringify({ ok: true, removed: before - filtered.length }));
|
|
1959
2139
|
} catch (err) {
|
|
1960
2140
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -1993,7 +2173,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1993
2173
|
}
|
|
1994
2174
|
entries[idx] = { ...entries[idx], text };
|
|
1995
2175
|
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':
|
|
2176
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
1997
2177
|
res.end(JSON.stringify({ ok: true }));
|
|
1998
2178
|
} catch (err) {
|
|
1999
2179
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2037,9 +2217,9 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
2037
2217
|
} finally { closeDb(db); }
|
|
2038
2218
|
} catch {
|
|
2039
2219
|
// Fallback: sqlite3 CLI + inline Sigma.js graph
|
|
2040
|
-
const {
|
|
2220
|
+
const { execFileSync } = await import('child_process');
|
|
2041
2221
|
const runSql = (sql) => {
|
|
2042
|
-
try { return JSON.parse(
|
|
2222
|
+
try { return JSON.parse(execFileSync('sqlite3', ['-json', dbPath], { encoding: 'utf-8', timeout: 15000, maxBuffer: 50*1024*1024, input: sql + ';' }) || '[]'); } catch { return []; }
|
|
2043
2223
|
};
|
|
2044
2224
|
const rawNodes = runSql('SELECT id, name, label, file_path, community_id FROM nodes LIMIT 2000');
|
|
2045
2225
|
const rawEdges = runSql('SELECT source_id, target_id, relation FROM edges');
|
|
@@ -2070,7 +2250,7 @@ ${edgesJson}.forEach(e=>{try{g.addEdge(e.source,e.target,{size:0.5,color:'#333'}
|
|
|
2070
2250
|
new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{color:'#ccc'},labelSize:10});
|
|
2071
2251
|
<\/script></body></html>`;
|
|
2072
2252
|
}
|
|
2073
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
2253
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2074
2254
|
res.end(html);
|
|
2075
2255
|
return;
|
|
2076
2256
|
}
|
|
@@ -2078,7 +2258,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2078
2258
|
// Fallback: try legacy graph.html on disk
|
|
2079
2259
|
const htmlPath = path.join(d, '.monomind', 'graph', 'graph.html');
|
|
2080
2260
|
const html = fs.readFileSync(htmlPath, 'utf-8');
|
|
2081
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
2261
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2082
2262
|
res.end(html);
|
|
2083
2263
|
} catch (err) {
|
|
2084
2264
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
@@ -2122,7 +2302,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2122
2302
|
].join('\n');
|
|
2123
2303
|
} finally { closeDb(db); }
|
|
2124
2304
|
}
|
|
2125
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2305
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2126
2306
|
res.end(JSON.stringify({ exists, report, stats }));
|
|
2127
2307
|
} catch (err) {
|
|
2128
2308
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2140,10 +2320,10 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2140
2320
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2141
2321
|
let nodes = [], edges = [];
|
|
2142
2322
|
if (fs.existsSync(dbPath)) {
|
|
2143
|
-
const {
|
|
2323
|
+
const { execFileSync } = await import('child_process');
|
|
2144
2324
|
const runSql = (sql, timeout = 10000) => {
|
|
2145
2325
|
try {
|
|
2146
|
-
return JSON.parse(
|
|
2326
|
+
return JSON.parse(execFileSync('sqlite3', ['-json', dbPath],
|
|
2147
2327
|
{ encoding: 'utf-8', timeout, maxBuffer: 50 * 1024 * 1024, input: sql + ';' }) || '[]');
|
|
2148
2328
|
} catch { return []; }
|
|
2149
2329
|
};
|
|
@@ -2166,7 +2346,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2166
2346
|
nodes = topNodes.map(n => ({ id: n.id, label: n.name || n.id, type: n.label || 'unknown', degree: degree.get(n.id) || 0 }));
|
|
2167
2347
|
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
2348
|
}
|
|
2169
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2349
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
2170
2350
|
res.end(JSON.stringify({ nodes, edges }));
|
|
2171
2351
|
} catch (err) {
|
|
2172
2352
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2195,7 +2375,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2195
2375
|
const importScript = path.join(process.cwd(), 'scripts', 'ua-import.mjs');
|
|
2196
2376
|
const enrichScript = path.join(process.cwd(), 'scripts', 'ua-enrich.mjs');
|
|
2197
2377
|
|
|
2198
|
-
res.writeHead(202, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2378
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2199
2379
|
|
|
2200
2380
|
if (uaGraph && fs.existsSync(importScript)) {
|
|
2201
2381
|
res.end(JSON.stringify({ status: 'importing', source: uaGraph }));
|
|
@@ -2224,7 +2404,19 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2224
2404
|
const dir = qs.get('dir') || projectDir || process.cwd();
|
|
2225
2405
|
const d = path.resolve(dir || process.cwd());
|
|
2226
2406
|
|
|
2227
|
-
|
|
2407
|
+
// Security: this route spawns `node --eval` with `cwd: d`, which lets
|
|
2408
|
+
// Node resolve '@monoes/monograph' against d's node_modules. Only ever
|
|
2409
|
+
// allow this for the server's own project root — never an
|
|
2410
|
+
// attacker-controlled `?dir=` — since a planted node_modules there
|
|
2411
|
+
// would achieve RCE. (See P0-6.)
|
|
2412
|
+
const _serverRoot = path.resolve(projectDir || process.cwd());
|
|
2413
|
+
if (d !== _serverRoot) {
|
|
2414
|
+
res.writeHead(400, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2415
|
+
res.end(JSON.stringify({ error: 'monograph-build only supports the server project root' }));
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2228
2420
|
res.end(JSON.stringify({ status: 'building', dir: d }));
|
|
2229
2421
|
|
|
2230
2422
|
// Build via monograph in background
|
|
@@ -2245,7 +2437,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2245
2437
|
const qs2 = new URL(req.url, 'http://localhost').searchParams;
|
|
2246
2438
|
const d2 = path.resolve(qs2.get('dir') || projectDir || process.cwd());
|
|
2247
2439
|
const state = buildDocsState.get(d2) || { status: 'idle' };
|
|
2248
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2440
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2249
2441
|
res.end(JSON.stringify(state));
|
|
2250
2442
|
return;
|
|
2251
2443
|
}
|
|
@@ -2258,7 +2450,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2258
2450
|
const d = path.resolve(dir || process.cwd());
|
|
2259
2451
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2260
2452
|
if (!fs.existsSync(dbPath)) {
|
|
2261
|
-
res.writeHead(400, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2453
|
+
res.writeHead(400, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2262
2454
|
res.end(JSON.stringify({ error: 'monograph.db not found — run BUILD GRAPH first' }));
|
|
2263
2455
|
return;
|
|
2264
2456
|
}
|
|
@@ -2266,14 +2458,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2266
2458
|
// Reject if already running
|
|
2267
2459
|
const existing = buildDocsState.get(d);
|
|
2268
2460
|
if (existing && existing.status === 'pending') {
|
|
2269
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2461
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2270
2462
|
res.end(JSON.stringify({ status: 'pending', message: 'Build already in progress' }));
|
|
2271
2463
|
return;
|
|
2272
2464
|
}
|
|
2273
2465
|
|
|
2274
2466
|
const startedAt = Date.now();
|
|
2275
2467
|
buildDocsState.set(d, { status: 'pending', sections: 0, files: 0, error: null, startedAt });
|
|
2276
|
-
res.writeHead(202, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2468
|
+
res.writeHead(202, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2277
2469
|
res.end(JSON.stringify({ status: 'pending', dir: d }));
|
|
2278
2470
|
|
|
2279
2471
|
// Run doc parsing in background
|
|
@@ -2472,7 +2664,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2472
2664
|
} catch {}
|
|
2473
2665
|
}
|
|
2474
2666
|
} finally { closeDb(db); }
|
|
2475
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2667
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2476
2668
|
res.end(JSON.stringify({ content, filePath, startLine, endLine, language, name, type }));
|
|
2477
2669
|
} catch (err) {
|
|
2478
2670
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2492,7 +2684,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2492
2684
|
const d = path.resolve(dir || process.cwd());
|
|
2493
2685
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2494
2686
|
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':
|
|
2687
|
+
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end(JSON.stringify({ nodes: [] })); return; }
|
|
2496
2688
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2497
2689
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2498
2690
|
const db = openDb(dbPath);
|
|
@@ -2505,7 +2697,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2505
2697
|
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
2698
|
});
|
|
2507
2699
|
} finally { closeDb(db); }
|
|
2508
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2700
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2509
2701
|
res.end(JSON.stringify({ nodes }));
|
|
2510
2702
|
} catch (err) {
|
|
2511
2703
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2524,7 +2716,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2524
2716
|
const maxDepth = Math.min(4, parseInt(qs.get('depth') || '3', 10));
|
|
2525
2717
|
const d = path.resolve(dir || process.cwd());
|
|
2526
2718
|
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':
|
|
2719
|
+
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
2720
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2529
2721
|
const db = openDb(dbPath);
|
|
2530
2722
|
const related = [];
|
|
@@ -2548,7 +2740,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2548
2740
|
frontier = next;
|
|
2549
2741
|
}
|
|
2550
2742
|
} finally { closeDb(db); }
|
|
2551
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2743
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2552
2744
|
res.end(JSON.stringify({ related }));
|
|
2553
2745
|
} catch (err) {
|
|
2554
2746
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2608,7 +2800,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2608
2800
|
}
|
|
2609
2801
|
result.markdown = lines2.join('\n');
|
|
2610
2802
|
} finally { closeDb(db); }
|
|
2611
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2803
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2612
2804
|
res.end(JSON.stringify(result));
|
|
2613
2805
|
} catch (err) {
|
|
2614
2806
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -2625,7 +2817,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2625
2817
|
const d = path.resolve(dir || process.cwd());
|
|
2626
2818
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2627
2819
|
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':
|
|
2820
|
+
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
2821
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2630
2822
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2631
2823
|
const db = openDb(dbPath);
|
|
@@ -2644,7 +2836,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2644
2836
|
}
|
|
2645
2837
|
}
|
|
2646
2838
|
} finally { closeDb(db); }
|
|
2647
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2839
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2648
2840
|
res.end(JSON.stringify({ success: true, query: q, result }));
|
|
2649
2841
|
} catch (err) {
|
|
2650
2842
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2662,7 +2854,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2662
2854
|
const d = path.resolve(dir || process.cwd());
|
|
2663
2855
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2664
2856
|
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':
|
|
2857
|
+
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
2858
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2667
2859
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
2668
2860
|
const db = openDb(dbPath);
|
|
@@ -2686,7 +2878,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2686
2878
|
].filter(Boolean).join('\n');
|
|
2687
2879
|
}
|
|
2688
2880
|
} finally { closeDb(db); }
|
|
2689
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2881
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2690
2882
|
res.end(JSON.stringify({ success: true, node: nodeQ, explanation }));
|
|
2691
2883
|
} catch (err) {
|
|
2692
2884
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2705,7 +2897,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2705
2897
|
const d = path.resolve(dir || process.cwd());
|
|
2706
2898
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
2707
2899
|
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':
|
|
2900
|
+
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
2901
|
// Import only graphology-free storage modules to avoid broken graphology dep
|
|
2710
2902
|
const { openDb, closeDb } = await import(new URL('../../../../monograph/dist/src/storage/db.js', import.meta.url).href);
|
|
2711
2903
|
const { ftsSearch } = await import(new URL('../../../../monograph/dist/src/storage/fts-store.js', import.meta.url).href);
|
|
@@ -2752,7 +2944,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2752
2944
|
pathResult = names.join(' → ') + ` (${p.length - 1} hop${p.length !== 2 ? 's' : ''})`;
|
|
2753
2945
|
}
|
|
2754
2946
|
} finally { closeDb(db); }
|
|
2755
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2947
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2756
2948
|
res.end(JSON.stringify({ success: true, from, to, path: pathResult }));
|
|
2757
2949
|
} catch (err) {
|
|
2758
2950
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2774,7 +2966,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2774
2966
|
process.kill(pid, 0);
|
|
2775
2967
|
running = true;
|
|
2776
2968
|
} catch {}
|
|
2777
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2969
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2778
2970
|
res.end(JSON.stringify({ running, pid }));
|
|
2779
2971
|
} catch (err) {
|
|
2780
2972
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -2800,7 +2992,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2800
2992
|
} catch {}
|
|
2801
2993
|
|
|
2802
2994
|
if (wasRunning) {
|
|
2803
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
2995
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2804
2996
|
res.end(JSON.stringify({ running: false, action: 'stopped' }));
|
|
2805
2997
|
} else {
|
|
2806
2998
|
const { spawn: sp } = await import('child_process');
|
|
@@ -2808,7 +3000,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2808
3000
|
child.unref();
|
|
2809
3001
|
try { fs.mkdirSync(path.join(d, '.monomind'), { recursive: true }); } catch {}
|
|
2810
3002
|
try { fs.writeFileSync(pidPath, String(child.pid)); } catch {}
|
|
2811
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3003
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
2812
3004
|
res.end(JSON.stringify({ running: true, pid: child.pid, action: 'started' }));
|
|
2813
3005
|
}
|
|
2814
3006
|
} catch (err) {
|
|
@@ -2823,7 +3015,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
2823
3015
|
let body = '';
|
|
2824
3016
|
req.on('data', c => { body += c; if (body.length > 2097152) { req.destroy(); return; } });
|
|
2825
3017
|
req.on('end', async () => {
|
|
2826
|
-
const json = res => { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3018
|
+
const json = res => { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); };
|
|
2827
3019
|
const ok = (data) => { json(res); res.end(JSON.stringify({ content: [{ type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }] })); };
|
|
2828
3020
|
const err = (msg) => { json(res); res.end(JSON.stringify({ error: msg })); };
|
|
2829
3021
|
try {
|
|
@@ -3272,14 +3464,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3272
3464
|
const gp = fs.existsSync(graphPath) ? graphPath : (fs.existsSync(legacyPath) ? legacyPath : null);
|
|
3273
3465
|
|
|
3274
3466
|
if (!gp) {
|
|
3275
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3467
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3276
3468
|
res.end(JSON.stringify({ available: false }));
|
|
3277
3469
|
return;
|
|
3278
3470
|
}
|
|
3279
3471
|
|
|
3280
3472
|
const { execSync: ex } = await import('child_process');
|
|
3281
3473
|
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':
|
|
3474
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3283
3475
|
res.end(JSON.stringify({ available: true, result: out }));
|
|
3284
3476
|
} catch (err) {
|
|
3285
3477
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3417,7 +3609,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3417
3609
|
}
|
|
3418
3610
|
}
|
|
3419
3611
|
|
|
3420
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3612
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3421
3613
|
res.end(JSON.stringify({ nodes, edges }));
|
|
3422
3614
|
} catch (err) {
|
|
3423
3615
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3434,7 +3626,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3434
3626
|
const entries = collectSwarmHistory(path.resolve(dir));
|
|
3435
3627
|
res.writeHead(200, {
|
|
3436
3628
|
'Content-Type': 'application/json',
|
|
3437
|
-
'Access-Control-Allow-Origin':
|
|
3629
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3438
3630
|
'Cache-Control': 'no-cache',
|
|
3439
3631
|
});
|
|
3440
3632
|
res.end(JSON.stringify({ entries }));
|
|
@@ -3459,7 +3651,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3459
3651
|
const agentId = typeof _rawAgentId === 'string' ? _rawAgentId.slice(0, 256) : undefined;
|
|
3460
3652
|
const last = qs.get('last') ? parseInt(qs.get('last')) : undefined;
|
|
3461
3653
|
const events = collectSwarmEvents(path.resolve(dir), { swarmId, agentId, last });
|
|
3462
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3654
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3463
3655
|
res.end(JSON.stringify({ events, count: events.length }));
|
|
3464
3656
|
} catch (err) {
|
|
3465
3657
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3473,7 +3665,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3473
3665
|
try {
|
|
3474
3666
|
const dir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
|
|
3475
3667
|
const size = getSwarmDataSize(path.resolve(dir));
|
|
3476
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3668
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3477
3669
|
res.end(JSON.stringify(size));
|
|
3478
3670
|
} catch (err) {
|
|
3479
3671
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3487,7 +3679,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3487
3679
|
try {
|
|
3488
3680
|
const dir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
|
|
3489
3681
|
const result = cleanSwarmData(path.resolve(dir));
|
|
3490
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3682
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3491
3683
|
res.end(JSON.stringify({ success: true, ...result }));
|
|
3492
3684
|
} catch (err) {
|
|
3493
3685
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -3507,7 +3699,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3507
3699
|
const trackerPath = path.join(dir, '.claude', 'helpers', 'token-tracker.cjs');
|
|
3508
3700
|
const fallback = () => {
|
|
3509
3701
|
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':
|
|
3702
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3511
3703
|
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
3704
|
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
3705
|
};
|
|
@@ -3564,7 +3756,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3564
3756
|
rows.sort((a, b) => b.cost - a.cost);
|
|
3565
3757
|
// Summary object matching client expectations
|
|
3566
3758
|
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':
|
|
3759
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
3568
3760
|
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
3761
|
} catch (e) { fallback(); }
|
|
3570
3762
|
} catch (err) {
|
|
@@ -3594,7 +3786,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3594
3786
|
}
|
|
3595
3787
|
res.writeHead(200, {
|
|
3596
3788
|
'Content-Type': 'application/json',
|
|
3597
|
-
'Access-Control-Allow-Origin':
|
|
3789
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3598
3790
|
'Cache-Control': 'no-cache',
|
|
3599
3791
|
});
|
|
3600
3792
|
res.end(JSON.stringify(partial));
|
|
@@ -3611,7 +3803,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3611
3803
|
'Content-Type': 'text/event-stream',
|
|
3612
3804
|
'Cache-Control': 'no-cache',
|
|
3613
3805
|
Connection: 'keep-alive',
|
|
3614
|
-
'Access-Control-Allow-Origin':
|
|
3806
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
3615
3807
|
'X-Accel-Buffering': 'no',
|
|
3616
3808
|
});
|
|
3617
3809
|
|
|
@@ -3660,11 +3852,11 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3660
3852
|
res.end(JSON.stringify({ exists: false }));
|
|
3661
3853
|
return;
|
|
3662
3854
|
}
|
|
3663
|
-
const {
|
|
3855
|
+
const { execFileSync } = await import('child_process');
|
|
3664
3856
|
// Pipe SQL via stdin to avoid shell quoting issues with single-quoted SQL strings.
|
|
3665
3857
|
const runSql = (sql, timeout = 5000) => {
|
|
3666
3858
|
try {
|
|
3667
|
-
return
|
|
3859
|
+
return execFileSync('sqlite3', ['-json', dbPath],
|
|
3668
3860
|
{ encoding: 'utf-8', timeout: timeout, input: sql + ';' });
|
|
3669
3861
|
} catch (e) { return '[]'; }
|
|
3670
3862
|
};
|
|
@@ -3750,7 +3942,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3750
3942
|
} catch(_) {}
|
|
3751
3943
|
}
|
|
3752
3944
|
}
|
|
3753
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3945
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3754
3946
|
res.end(JSON.stringify(orgs));
|
|
3755
3947
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3756
3948
|
return;
|
|
@@ -3772,7 +3964,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3772
3964
|
fs.mkdirSync(orgsDir, { recursive: true });
|
|
3773
3965
|
const destFile = path.join(orgsDir, `${orgName}.json`);
|
|
3774
3966
|
fs.writeFileSync(destFile, JSON.stringify({ ...cfg, name: orgName }, null, 2), 'utf8');
|
|
3775
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3967
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3776
3968
|
res.end(JSON.stringify({ ok: true, name: orgName, file: destFile }));
|
|
3777
3969
|
} catch (e) {
|
|
3778
3970
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
@@ -3798,7 +3990,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3798
3990
|
const destFile = path.join(orgsDir, `${name}.json`);
|
|
3799
3991
|
const cleanCfg = Object.fromEntries(Object.entries({ ...cfg, name }).filter(([k]) => !k.startsWith('_')));
|
|
3800
3992
|
fs.writeFileSync(destFile, JSON.stringify(cleanCfg, null, 2), 'utf8');
|
|
3801
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
3993
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3802
3994
|
res.end(JSON.stringify({ ok: true, name, file: destFile }));
|
|
3803
3995
|
} catch (e) {
|
|
3804
3996
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
@@ -3818,7 +4010,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3818
4010
|
const _orgsOneProjDir = _resolveOrgProjectDir(orgName, _orgsOneRoot) || _orgsOneRoot;
|
|
3819
4011
|
const f = path.join(_orgsOneProjDir, '.monomind', 'orgs', `${orgName}.json`);
|
|
3820
4012
|
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':
|
|
4013
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3822
4014
|
res.end(fs.readFileSync(f, 'utf8'));
|
|
3823
4015
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3824
4016
|
return;
|
|
@@ -3904,7 +4096,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3904
4096
|
lastEventAt: _runstateData?.lastEventAt || null,
|
|
3905
4097
|
agentStates: _runstateData?.agentStates || {} };
|
|
3906
4098
|
|
|
3907
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4099
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3908
4100
|
res.end(JSON.stringify(result));
|
|
3909
4101
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3910
4102
|
return;
|
|
@@ -3955,7 +4147,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3955
4147
|
}
|
|
3956
4148
|
|
|
3957
4149
|
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':
|
|
4150
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3959
4151
|
res.end(JSON.stringify(out));
|
|
3960
4152
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3961
4153
|
return;
|
|
@@ -3970,9 +4162,9 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3970
4162
|
const _projsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
3971
4163
|
const d = path.resolve(_projsQs.get('dir') || projectDir || process.cwd());
|
|
3972
4164
|
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':
|
|
4165
|
+
if (!fs.existsSync(projFile)) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end('[]'); return; }
|
|
3974
4166
|
const data = JSON.parse(fs.readFileSync(projFile, 'utf8'));
|
|
3975
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4167
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3976
4168
|
res.end(JSON.stringify(data.projects || []));
|
|
3977
4169
|
} catch(_) { res.writeHead(500); res.end('[]'); }
|
|
3978
4170
|
return;
|
|
@@ -3988,12 +4180,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
3988
4180
|
const d = path.resolve(_membersQs.get('dir') || projectDir || process.cwd());
|
|
3989
4181
|
const membersFile = path.join(d, '.monomind', 'orgs', `${orgName}-members.json`);
|
|
3990
4182
|
if (!fs.existsSync(membersFile)) {
|
|
3991
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4183
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3992
4184
|
res.end('{"members":[],"join_requests":[]}');
|
|
3993
4185
|
return;
|
|
3994
4186
|
}
|
|
3995
4187
|
const data = JSON.parse(fs.readFileSync(membersFile, 'utf8'));
|
|
3996
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4188
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
3997
4189
|
res.end(JSON.stringify(data));
|
|
3998
4190
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3999
4191
|
return;
|
|
@@ -4014,7 +4206,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4014
4206
|
const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
|
|
4015
4207
|
let defaultAdapter = 'claude-sonnet-4-6';
|
|
4016
4208
|
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':
|
|
4209
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4018
4210
|
res.end(JSON.stringify({ default_adapter: defaultAdapter, adapters: [
|
|
4019
4211
|
{ type: 'claude-local', label: 'Claude (local CLI)', source: 'built-in', disabled: false, modelsCount: 3 },
|
|
4020
4212
|
{ type: 'gemini-local', label: 'Gemini (local)', source: 'built-in', disabled: false, modelsCount: 1 },
|
|
@@ -4023,7 +4215,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4023
4215
|
return;
|
|
4024
4216
|
}
|
|
4025
4217
|
const data = JSON.parse(fs.readFileSync(adaptersFile, 'utf8'));
|
|
4026
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4218
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4027
4219
|
res.end(JSON.stringify(data));
|
|
4028
4220
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4029
4221
|
return;
|
|
@@ -4078,7 +4270,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4078
4270
|
} catch(_) {}
|
|
4079
4271
|
}
|
|
4080
4272
|
|
|
4081
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4273
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4082
4274
|
res.end(JSON.stringify({ skills, role_skill_map: roleSkillMap }));
|
|
4083
4275
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4084
4276
|
return;
|
|
@@ -4134,7 +4326,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4134
4326
|
}
|
|
4135
4327
|
}
|
|
4136
4328
|
|
|
4137
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4329
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
4138
4330
|
res.end(JSON.stringify({ role, definition }));
|
|
4139
4331
|
} catch (_) { res.writeHead(500); res.end('{}'); }
|
|
4140
4332
|
return;
|
|
@@ -4147,7 +4339,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4147
4339
|
const orgName = decodeURIComponent(urlObj.pathname.split('/')[3]);
|
|
4148
4340
|
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('{}'); return; }
|
|
4149
4341
|
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':
|
|
4342
|
+
if (!q || q.length < 2) { res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) }); res.end('{"hits":[]}'); return; }
|
|
4151
4343
|
|
|
4152
4344
|
const d = path.resolve(urlObj.searchParams.get('dir') || projectDir || process.cwd());
|
|
4153
4345
|
const orgsDir = path.join(d, '.monomind', 'orgs');
|
|
@@ -4219,7 +4411,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4219
4411
|
}
|
|
4220
4412
|
}
|
|
4221
4413
|
|
|
4222
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4414
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4223
4415
|
res.end(JSON.stringify({ q, hits: hits.slice(0, 50) }));
|
|
4224
4416
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4225
4417
|
return;
|
|
@@ -4505,7 +4697,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4505
4697
|
skills: r.skills || [],
|
|
4506
4698
|
};
|
|
4507
4699
|
});
|
|
4508
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4700
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4509
4701
|
res.end(JSON.stringify({ agents }));
|
|
4510
4702
|
} catch(_) { res.writeHead(500); res.end('{"agents":[]}'); }
|
|
4511
4703
|
return;
|
|
@@ -4542,7 +4734,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4542
4734
|
ts: a.ts || null,
|
|
4543
4735
|
}));
|
|
4544
4736
|
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':
|
|
4737
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4546
4738
|
res.end(JSON.stringify({ approvals, pending }));
|
|
4547
4739
|
} catch(_) { res.writeHead(500); res.end('{"approvals":[],"pending":0}'); }
|
|
4548
4740
|
return;
|
|
@@ -4585,7 +4777,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4585
4777
|
const event = { type: 'org:approval:resolved', org: orgName, approval_id: approvalId, status, ts: Date.now() };
|
|
4586
4778
|
appendToFile(path.join(path.resolve(_postApprovalsQs.get('dir') || projectDir || process.cwd()), 'data', 'mastermind-events.jsonl'), JSON.stringify(event) + '\n').catch(() => {});
|
|
4587
4779
|
broadcastMm(event);
|
|
4588
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4780
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4589
4781
|
res.end(JSON.stringify({ ok: true, status }));
|
|
4590
4782
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4591
4783
|
return;
|
|
@@ -4613,7 +4805,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4613
4805
|
lastUsedAt: s.lastUsedAt || null,
|
|
4614
4806
|
usageCount: s.usageCount || 0,
|
|
4615
4807
|
}));
|
|
4616
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4808
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4617
4809
|
res.end(JSON.stringify({ secrets }));
|
|
4618
4810
|
} catch(_) { res.writeHead(500); res.end('{"secrets":[]}'); }
|
|
4619
4811
|
return;
|
|
@@ -4770,7 +4962,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4770
4962
|
const goalsFile = path.join(_goalsProjDir, '.monomind', 'orgs', `${orgName}-goals.json`);
|
|
4771
4963
|
let data = { goals: [] };
|
|
4772
4964
|
try { data = JSON.parse(fs.readFileSync(goalsFile, 'utf8')); } catch(_) {}
|
|
4773
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
4965
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4774
4966
|
res.end(JSON.stringify({ goals: data.goals || [] }));
|
|
4775
4967
|
} catch(_) { res.writeHead(500); res.end('{"goals":[]}'); }
|
|
4776
4968
|
return;
|
|
@@ -4819,7 +5011,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4819
5011
|
}
|
|
4820
5012
|
} catch(_) {}
|
|
4821
5013
|
}
|
|
4822
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5014
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4823
5015
|
res.end(JSON.stringify({ routines: data.routines || [] }));
|
|
4824
5016
|
} catch(_) { res.writeHead(500); res.end('{"routines":[]}'); }
|
|
4825
5017
|
return;
|
|
@@ -4841,7 +5033,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4841
5033
|
const payload = { org: orgName, updated_at: new Date().toISOString(), goals: parsed.goals };
|
|
4842
5034
|
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
4843
5035
|
fs.renameSync(tmp, goalsFile);
|
|
4844
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5036
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4845
5037
|
res.end(JSON.stringify({ ok: true, count: parsed.goals.length }));
|
|
4846
5038
|
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
4847
5039
|
return;
|
|
@@ -4863,7 +5055,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4863
5055
|
const payload = { org: orgName, updated_at: new Date().toISOString(), routines: parsed.routines };
|
|
4864
5056
|
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
4865
5057
|
fs.renameSync(tmp, routinesFile);
|
|
4866
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5058
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4867
5059
|
res.end(JSON.stringify({ ok: true, count: parsed.routines.length }));
|
|
4868
5060
|
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
4869
5061
|
return;
|
|
@@ -4917,7 +5109,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4917
5109
|
if (fs.existsSync(agentsDir)) walkAgents(agentsDir);
|
|
4918
5110
|
}
|
|
4919
5111
|
files.sort((a, b) => new Date(b.mtime) - new Date(a.mtime));
|
|
4920
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5112
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
4921
5113
|
res.end(JSON.stringify(files));
|
|
4922
5114
|
} catch (e) { res.writeHead(500); res.end(JSON.stringify({ error: e.message })); }
|
|
4923
5115
|
return;
|
|
@@ -4941,7 +5133,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4941
5133
|
if (!stat.isFile()) { res.writeHead(400); res.end('Not a file'); return; }
|
|
4942
5134
|
if (stat.size > 524288) { res.writeHead(413); res.end('File too large'); return; }
|
|
4943
5135
|
const content = fs.readFileSync(resolved, 'utf8');
|
|
4944
|
-
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
5136
|
+
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4945
5137
|
res.end(content);
|
|
4946
5138
|
} catch(_) { res.writeHead(500); res.end('Internal error'); }
|
|
4947
5139
|
return;
|
|
@@ -4984,7 +5176,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4984
5176
|
const deleteEvent = { type: 'org:delete', org: orgName, ts: Date.now() };
|
|
4985
5177
|
appendToFile(path.join(projectDir || process.cwd(), 'data', 'mastermind-events.jsonl'), JSON.stringify(deleteEvent) + '\n').catch(() => {});
|
|
4986
5178
|
broadcastMm(deleteEvent);
|
|
4987
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5179
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4988
5180
|
res.end('{"ok":true}');
|
|
4989
5181
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
4990
5182
|
return;
|
|
@@ -5008,7 +5200,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5008
5200
|
fs.writeFileSync(path.join(stopDir, `${orgName}.stop`), String(Date.now()));
|
|
5009
5201
|
} catch(_) {}
|
|
5010
5202
|
broadcastMm(stopEvent);
|
|
5011
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5203
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5012
5204
|
res.end('{"ok":true}');
|
|
5013
5205
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
5014
5206
|
return;
|
|
@@ -5034,7 +5226,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5034
5226
|
try { fs.mkdirSync(destOrgsDir, { recursive: true }); } catch(_) {}
|
|
5035
5227
|
const destFile = path.join(destOrgsDir, `${orgName}.json`);
|
|
5036
5228
|
fs.copyFileSync(srcFile, destFile);
|
|
5037
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5229
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5038
5230
|
res.end(JSON.stringify({ ok: true, destFile }));
|
|
5039
5231
|
} catch(e) { res.writeHead(500); res.end(JSON.stringify({ error: String(e.message || e) })); }
|
|
5040
5232
|
return;
|
|
@@ -5121,7 +5313,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5121
5313
|
break;
|
|
5122
5314
|
}
|
|
5123
5315
|
}
|
|
5124
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5316
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5125
5317
|
res.end(JSON.stringify(runs));
|
|
5126
5318
|
} catch (_) { res.writeHead(500); res.end('[]'); }
|
|
5127
5319
|
return;
|
|
@@ -5147,7 +5339,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5147
5339
|
if (!fs.existsSync(_tf)) continue;
|
|
5148
5340
|
const _tLines = fs.readFileSync(_tf, 'utf8').split('\n').filter(Boolean);
|
|
5149
5341
|
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':
|
|
5342
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5151
5343
|
res.end(JSON.stringify(_tEvs));
|
|
5152
5344
|
return;
|
|
5153
5345
|
}
|
|
@@ -5187,7 +5379,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5187
5379
|
// For in-progress runs (no .warm.jsonl), org:comms also go to .convs.jsonl (stripped form).
|
|
5188
5380
|
// They're already in .jsonl as full events, so .convs.jsonl would duplicate — skip it.
|
|
5189
5381
|
events.sort((a, b) => (a.ts || 0) - (b.ts || 0));
|
|
5190
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5382
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5191
5383
|
res.end(JSON.stringify(events));
|
|
5192
5384
|
} catch (_) { res.writeHead(500); res.end('[]'); }
|
|
5193
5385
|
return;
|
|
@@ -5210,12 +5402,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5210
5402
|
// Reject files >2MB to avoid blocking the event loop
|
|
5211
5403
|
if (_size > 2 * 1024 * 1024) { res.writeHead(413); res.end(JSON.stringify({ error: 'file too large', size: _size })); return; }
|
|
5212
5404
|
if (!_mime.startsWith('text/') && _mime !== 'application/json') {
|
|
5213
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5405
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5214
5406
|
res.end(JSON.stringify({ binary: true, mimeType: _mime, size: _size }));
|
|
5215
5407
|
return;
|
|
5216
5408
|
}
|
|
5217
5409
|
const _content = fs.readFileSync(_filePath, 'utf8');
|
|
5218
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5410
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5219
5411
|
res.end(JSON.stringify({ content: _content, mimeType: _mime, size: _size }));
|
|
5220
5412
|
} catch (_e) { res.writeHead(500); res.end(JSON.stringify({ error: 'read failed' })); }
|
|
5221
5413
|
return;
|
|
@@ -5224,7 +5416,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5224
5416
|
// ------------------------------------------------- Mastermind event system
|
|
5225
5417
|
// POST /api/mastermind/event — ingest event from mastermind skill
|
|
5226
5418
|
if (req.method === 'POST' && url === '/api/mastermind/event') {
|
|
5227
|
-
return handleMastermindEvent(req, res);
|
|
5419
|
+
return handleMastermindEvent(req, res, corsOrigin);
|
|
5228
5420
|
}
|
|
5229
5421
|
|
|
5230
5422
|
// GET /api/mastermind-stream — SSE for real-time events
|
|
@@ -5233,7 +5425,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5233
5425
|
'Content-Type': 'text/event-stream',
|
|
5234
5426
|
'Cache-Control': 'no-cache',
|
|
5235
5427
|
'Connection': 'keep-alive',
|
|
5236
|
-
'Access-Control-Allow-Origin':
|
|
5428
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
5237
5429
|
});
|
|
5238
5430
|
res.write(': connected\n\n');
|
|
5239
5431
|
addMmClient(res);
|
|
@@ -5276,7 +5468,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5276
5468
|
const top = idx.slice(0, limitParam);
|
|
5277
5469
|
for (const entry of top) {
|
|
5278
5470
|
const _sid = String(entry.id || '').trim();
|
|
5279
|
-
if (!_sid ||
|
|
5471
|
+
if (!_sid || !SESSION_ID_RE.test(_sid)) continue;
|
|
5280
5472
|
let events = [];
|
|
5281
5473
|
try {
|
|
5282
5474
|
const jl = fs.readFileSync(path.join(sessDir, `${_sid}.jsonl`), 'utf8');
|
|
@@ -5300,7 +5492,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5300
5492
|
}
|
|
5301
5493
|
}
|
|
5302
5494
|
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':
|
|
5495
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5304
5496
|
res.end(JSON.stringify(allSessions.slice(0, limitParam)));
|
|
5305
5497
|
} catch (_) { res.writeHead(200); res.end('[]'); }
|
|
5306
5498
|
return;
|
|
@@ -5332,7 +5524,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5332
5524
|
else if (ev.type === 'session:complete') lines.push(`\`${t}\` **SESSION COMPLETE** — status: ${ev.status}, domains: ${(ev.domains || []).join(', ')}`);
|
|
5333
5525
|
else lines.push(`\`${t}\` ${ev.type} ${JSON.stringify(ev)}`);
|
|
5334
5526
|
}
|
|
5335
|
-
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', 'Access-Control-Allow-Origin':
|
|
5527
|
+
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5336
5528
|
res.end(lines.join('\n'));
|
|
5337
5529
|
} catch (_) { res.writeHead(500); res.end('Error'); }
|
|
5338
5530
|
return;
|
|
@@ -5345,14 +5537,14 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5345
5537
|
// Check individual session file first
|
|
5346
5538
|
const sessFile = path.join(projectDir || process.cwd(), 'data', 'sessions', `${sid}.json`);
|
|
5347
5539
|
if (fs.existsSync(sessFile)) {
|
|
5348
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5540
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5349
5541
|
res.end(fs.readFileSync(sessFile, 'utf8'));
|
|
5350
5542
|
return;
|
|
5351
5543
|
}
|
|
5352
5544
|
const f = path.join(projectDir || process.cwd(), 'data', 'mastermind-sessions.json');
|
|
5353
5545
|
const sessions = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
5354
5546
|
const s = sessions.find(x => x.id === sid);
|
|
5355
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5547
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5356
5548
|
res.end(JSON.stringify(s || null));
|
|
5357
5549
|
} catch (_) { res.writeHead(200); res.end('null'); }
|
|
5358
5550
|
return;
|
|
@@ -5384,6 +5576,23 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5384
5576
|
return;
|
|
5385
5577
|
}
|
|
5386
5578
|
|
|
5579
|
+
// ------------------------------------------------ GET /orgs-files.js
|
|
5580
|
+
// Files-tab + diff-view script, split out of orgs.html (see orgs.html's
|
|
5581
|
+
// script-src comment). Not a generic static handler — this UI serves each
|
|
5582
|
+
// sibling asset via its own hardcoded route, same as GET /orgs above.
|
|
5583
|
+
if (req.method === 'GET' && url === '/orgs-files.js') {
|
|
5584
|
+
try {
|
|
5585
|
+
const jsPath = path.join(__dirname, 'orgs-files.js');
|
|
5586
|
+
const js = fs.readFileSync(jsPath, 'utf8');
|
|
5587
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
5588
|
+
res.end(js);
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
res.writeHead(404);
|
|
5591
|
+
res.end(`orgs-files.js not found: ${err.message}`);
|
|
5592
|
+
}
|
|
5593
|
+
return;
|
|
5594
|
+
}
|
|
5595
|
+
|
|
5387
5596
|
// GET /api/mastermind/loops — list all active loop state files
|
|
5388
5597
|
if (req.method === 'GET' && url === '/api/mastermind/loops') {
|
|
5389
5598
|
try {
|
|
@@ -5419,7 +5628,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5419
5628
|
const lines = fs.readFileSync(evPath, 'utf8').split('\n').filter(l => l.trim()).slice(-10);
|
|
5420
5629
|
recentEvents = lines.map(l => { try { return JSON.parse(l); } catch(_) { return null; } }).filter(Boolean);
|
|
5421
5630
|
} catch(_) {}
|
|
5422
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5631
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5423
5632
|
res.end(JSON.stringify({
|
|
5424
5633
|
ts: Date.now(),
|
|
5425
5634
|
pid: process.pid,
|
|
@@ -5469,7 +5678,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5469
5678
|
const detectedRunId = path.basename(runFile, '.jsonl');
|
|
5470
5679
|
const lines = fs.readFileSync(runFile, 'utf8').split('\n').filter(l => l.trim()).slice(-100);
|
|
5471
5680
|
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':
|
|
5681
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5473
5682
|
res.end(JSON.stringify({ runId: detectedRunId, events, active: activeOrgRuns.has(orgName) }));
|
|
5474
5683
|
} catch(err) {
|
|
5475
5684
|
res.writeHead(500); res.end(JSON.stringify({ error: err.message }));
|
|
@@ -5672,7 +5881,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5672
5881
|
} catch (_) {}
|
|
5673
5882
|
}
|
|
5674
5883
|
}
|
|
5675
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5884
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5676
5885
|
res.end(JSON.stringify(result));
|
|
5677
5886
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5678
5887
|
return;
|
|
@@ -5746,12 +5955,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5746
5955
|
const dir = path.resolve(qp.get('dir') || projectDir || process.cwd());
|
|
5747
5956
|
const covPath = path.join(dir, '.monomind', 'audit', 'coverage.json');
|
|
5748
5957
|
if (!fs.existsSync(covPath)) {
|
|
5749
|
-
res.writeHead(404, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5958
|
+
res.writeHead(404, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5750
5959
|
res.end('{}');
|
|
5751
5960
|
return;
|
|
5752
5961
|
}
|
|
5753
5962
|
const coverage = JSON.parse(fs.readFileSync(covPath, 'utf8'));
|
|
5754
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
5963
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}), 'Cache-Control': 'no-cache' });
|
|
5755
5964
|
res.end(JSON.stringify(coverage));
|
|
5756
5965
|
} catch (e) {
|
|
5757
5966
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
@@ -5819,7 +6028,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5819
6028
|
const _mcLine = `data: ${JSON.stringify(_mcEvent)}\n\n`;
|
|
5820
6029
|
for (const _cl of _mcFwdClients) { try { _cl.write(_mcLine); } catch (_) { _mcFwdClients.delete(_cl); } }
|
|
5821
6030
|
}
|
|
5822
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
6031
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5823
6032
|
res.end(JSON.stringify({ ok: true, runId: _mcRunId }));
|
|
5824
6033
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5825
6034
|
return;
|
|
@@ -5837,7 +6046,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5837
6046
|
'Content-Type': 'text/event-stream',
|
|
5838
6047
|
'Cache-Control': 'no-cache',
|
|
5839
6048
|
'Connection': 'keep-alive',
|
|
5840
|
-
'Access-Control-Allow-Origin':
|
|
6049
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
5841
6050
|
'X-Accel-Buffering': 'no',
|
|
5842
6051
|
});
|
|
5843
6052
|
res.write(': connected\n\n');
|
|
@@ -5934,7 +6143,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5934
6143
|
const _chLine = `data: ${JSON.stringify(_chEvent)}\n\n`;
|
|
5935
6144
|
for (const _cl of _chFwdClients) { try { _cl.write(_chLine); } catch(_) { _chFwdClients.delete(_cl); } }
|
|
5936
6145
|
}
|
|
5937
|
-
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin':
|
|
6146
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5938
6147
|
res.end(JSON.stringify({ ok: true }));
|
|
5939
6148
|
} catch (e) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: e.message })); }
|
|
5940
6149
|
return;
|
|
@@ -6020,7 +6229,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6020
6229
|
const _migIndex = [];
|
|
6021
6230
|
for (const sess of (_migOld || [])) {
|
|
6022
6231
|
const _msid = String(sess.id || '').trim();
|
|
6023
|
-
if (!_msid ||
|
|
6232
|
+
if (!_msid || !SESSION_ID_RE.test(_msid)) continue;
|
|
6024
6233
|
// Write per-session JSONL
|
|
6025
6234
|
const _mEvts = (sess.events || []);
|
|
6026
6235
|
const _mLines = _mEvts.map(e => JSON.stringify(e)).join('\n');
|
|
@@ -6338,9 +6547,7 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
6338
6547
|
clearInterval(_heartbeatTimer);
|
|
6339
6548
|
// Flush SQLite run-event index to disk before exit (bypasses 1000ms debounce timer)
|
|
6340
6549
|
clearTimeout(_runDbPersistTimer);
|
|
6341
|
-
|
|
6342
|
-
try { fs.writeFileSync(_runDbPath, Buffer.from(_runDb.export())); } catch (_) {}
|
|
6343
|
-
}
|
|
6550
|
+
_writeRunDbSnapshot();
|
|
6344
6551
|
for (const w of activeWatchers) {
|
|
6345
6552
|
try {
|
|
6346
6553
|
w.close();
|