@monoes/monomindcli 2.16.11 → 2.16.13
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/.claude/agents/core/coder.md +1 -1
- package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
- package/.claude/agents/github/repo-architect.md +1 -1
- package/.claude/agents/testing/production-validator.md +1 -1
- package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
- package/.claude/commands/github/pr-manager.md +1 -1
- package/.claude/helpers/pick-rank.cjs +34 -4
- package/.claude/skills/monolean-audit/SKILL.md +1 -1
- package/dist/src/commands/cleanup-data.d.ts +30 -0
- package/dist/src/commands/cleanup-data.d.ts.map +1 -0
- package/dist/src/commands/cleanup-data.js +138 -0
- package/dist/src/commands/cleanup-data.js.map +1 -0
- package/dist/src/commands/cleanup-origin.d.ts +24 -0
- package/dist/src/commands/cleanup-origin.d.ts.map +1 -0
- package/dist/src/commands/cleanup-origin.js +67 -0
- package/dist/src/commands/cleanup-origin.js.map +1 -0
- package/dist/src/commands/cleanup-scratch.d.ts +43 -0
- package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
- package/dist/src/commands/cleanup-scratch.js +125 -0
- package/dist/src/commands/cleanup-scratch.js.map +1 -0
- package/dist/src/commands/cleanup.d.ts +2 -39
- package/dist/src/commands/cleanup.d.ts.map +1 -1
- package/dist/src/commands/cleanup.js +9 -296
- package/dist/src/commands/cleanup.js.map +1 -1
- package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
- package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-gitignore-checks.js +141 -0
- package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.js +292 -0
- package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
- package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
- package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-memory-checks.js +298 -0
- package/dist/src/commands/doctor-memory-checks.js.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.js +331 -0
- package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +7 -66
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +10 -1331
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
- package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-routing-checks.js +194 -0
- package/dist/src/commands/doctor-routing-checks.js.map +1 -0
- package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
- package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-worker-checks.js +122 -0
- package/dist/src/commands/doctor-worker-checks.js.map +1 -0
- package/dist/src/commands/hooks-routing-commands.js +1 -1
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +6 -15
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/never-commit.d.ts +3 -3
- package/dist/src/init/types.d.ts +1 -1
- package/dist/src/init/types.js +1 -1
- package/dist/src/knowledge/document-store.js +1 -1
- package/dist/src/knowledge/document-store.js.map +1 -1
- package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
- package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
- package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.js +408 -0
- package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
- package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
- package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-route.js +251 -0
- package/dist/src/mcp-tools/hooks-route.js.map +1 -0
- package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +6 -1727
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-session.js +243 -0
- package/dist/src/mcp-tools/hooks-session.js.map +1 -0
- package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
- package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-task.js +427 -0
- package/dist/src/mcp-tools/hooks-task.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/memory-tools.js +1 -1
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/memory/bm25-index.d.ts +2 -2
- package/dist/src/memory/bm25-index.js +2 -2
- package/dist/src/memory/embedding-operations.js +2 -2
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/entity-name-key.d.ts +1 -1
- package/dist/src/memory/entity-name-key.js +1 -1
- package/dist/src/memory/hnsw-operations.d.ts +1 -1
- package/dist/src/memory/hnsw-operations.js +1 -1
- package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
- package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-backend.js +363 -0
- package/dist/src/memory/memory-bridge-backend.js.map +1 -0
- package/dist/src/memory/memory-bridge-core.d.ts +42 -0
- package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-core.js +126 -0
- package/dist/src/memory/memory-bridge-core.js.map +1 -0
- package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
- package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-entries.js +227 -0
- package/dist/src/memory/memory-bridge-entries.js.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.js +118 -0
- package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
- package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
- package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-paths.js +335 -0
- package/dist/src/memory/memory-bridge-paths.js.map +1 -0
- package/dist/src/memory/memory-bridge-search.d.ts +48 -0
- package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-search.js +383 -0
- package/dist/src/memory/memory-bridge-search.js.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.js +109 -0
- package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
- package/dist/src/memory/memory-bridge-store.d.ts +58 -0
- package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-store.js +201 -0
- package/dist/src/memory/memory-bridge-store.js.map +1 -0
- package/dist/src/memory/memory-bridge.d.ts +10 -438
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +14 -1787
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-kg-claims.d.ts +52 -0
- package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-claims.js +99 -0
- package/dist/src/memory/memory-kg-claims.js.map +1 -0
- package/dist/src/memory/memory-kg-index.d.ts +86 -0
- package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-index.js +256 -0
- package/dist/src/memory/memory-kg-index.js.map +1 -0
- package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
- package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-ingest.js +361 -0
- package/dist/src/memory/memory-kg-ingest.js.map +1 -0
- package/dist/src/memory/memory-kg-model.d.ts +168 -0
- package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-model.js +158 -0
- package/dist/src/memory/memory-kg-model.js.map +1 -0
- package/dist/src/memory/memory-kg-names.d.ts +64 -0
- package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-names.js +187 -0
- package/dist/src/memory/memory-kg-names.js.map +1 -0
- package/dist/src/memory/memory-kg-promote.d.ts +79 -0
- package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-promote.js +248 -0
- package/dist/src/memory/memory-kg-promote.js.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.js +299 -0
- package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
- package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
- package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rollback.js +193 -0
- package/dist/src/memory/memory-kg-rollback.js.map +1 -0
- package/dist/src/memory/memory-kg-rules.d.ts +64 -0
- package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rules.js +251 -0
- package/dist/src/memory/memory-kg-rules.js.map +1 -0
- package/dist/src/memory/memory-kg-scan.d.ts +118 -0
- package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-scan.js +209 -0
- package/dist/src/memory/memory-kg-scan.js.map +1 -0
- package/dist/src/memory/memory-kg-search.d.ts +80 -0
- package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-search.js +239 -0
- package/dist/src/memory/memory-kg-search.js.map +1 -0
- package/dist/src/memory/memory-kg.d.ts +19 -547
- package/dist/src/memory/memory-kg.d.ts.map +1 -1
- package/dist/src/memory/memory-kg.js +10 -2389
- package/dist/src/memory/memory-kg.js.map +1 -1
- package/dist/src/orgrt/sandbox-stubs.d.ts +4 -0
- package/dist/src/orgrt/sandbox-stubs.d.ts.map +1 -1
- package/dist/src/orgrt/sandbox-stubs.js +12 -2
- package/dist/src/orgrt/sandbox-stubs.js.map +1 -1
- package/dist/src/ui/org-hil.mjs +1 -1
- package/dist/src/ui/routes-org-agents.mjs +318 -0
- package/dist/src/ui/routes-org-approvals.mjs +133 -0
- package/dist/src/ui/routes-org-config.mjs +459 -0
- package/dist/src/ui/routes-org-control.mjs +300 -0
- package/dist/src/ui/routes-org-files.mjs +172 -0
- package/dist/src/ui/routes-org-helpers.mjs +84 -0
- package/dist/src/ui/routes-org-knowledge.mjs +262 -0
- package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
- package/dist/src/ui/routes-org-live.mjs +278 -0
- package/dist/src/ui/routes-org-mastermind.mjs +346 -0
- package/dist/src/ui/routes-org-planning.mjs +398 -0
- package/dist/src/ui/routes-org-runs.mjs +430 -0
- package/dist/src/ui/routes-org-status.mjs +342 -0
- package/dist/src/ui/routes-org-workflows.mjs +212 -0
- package/dist/src/ui/routes-org.mjs +26 -3847
- package/dist/src/ui/server.mjs +1 -1
- package/dist/src/utils/native-error.d.ts +1 -1
- package/dist/src/utils/native-error.js +1 -1
- package/dist/src/utils/paths.d.ts +2 -1
- package/dist/src/utils/paths.d.ts.map +1 -1
- package/dist/src/utils/paths.js +2 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/org-skills/adversarial-reviewer/SKILL.md +1 -1
- package/org-skills/board-deck-builder/SKILL.md +1 -1
- package/org-skills/code-reviewer/SKILL.md +1 -1
- package/org-skills/debugging-wizard/SKILL.md +1 -1
- package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
- package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
// forwarder.js is compiled from orgrt/forwarder.ts (dist/src/orgrt/forwarder.js
|
|
4
|
+
// sits alongside this file's own compiled copy in dist/src/ui/ after
|
|
5
|
+
// build; under vitest the .js specifier resolves straight to the .ts source,
|
|
6
|
+
// same as every other cross-reference in this codebase). translate()/
|
|
7
|
+
// companionEvents() are pure — no filesystem or process side effects — so
|
|
8
|
+
// importing them here doesn't pull in attachForwarder's spawn/heal logic.
|
|
9
|
+
import { companionEvents, translate } from '../orgrt/forwarder.js';
|
|
10
|
+
import { getMmClientCount } from './sse-manager.mjs';
|
|
11
|
+
|
|
12
|
+
// Org dashboard routes: loops, status, current run, history and memory.
|
|
13
|
+
// Registered, in order, by handleOrgRoutes in routes-org.mjs.
|
|
14
|
+
export async function handleOrgLiveRoutes(req, res, url, corsOrigin, ctx) {
|
|
15
|
+
// GET /api/mastermind/loops — list all active loop state files
|
|
16
|
+
if (req.method === 'GET' && url === '/api/mastermind/loops') {
|
|
17
|
+
try {
|
|
18
|
+
const loopsDir = path.join(ctx.projectDir || process.cwd(), '.monomind', 'loops');
|
|
19
|
+
const loops = [];
|
|
20
|
+
if (fs.existsSync(loopsDir)) {
|
|
21
|
+
const files = fs
|
|
22
|
+
.readdirSync(loopsDir)
|
|
23
|
+
.filter((f) => f.endsWith('.json') && !f.includes('-hil'));
|
|
24
|
+
for (const f of files) {
|
|
25
|
+
try {
|
|
26
|
+
const d = JSON.parse(fs.readFileSync(path.join(loopsDir, f), 'utf8'));
|
|
27
|
+
loops.push(d);
|
|
28
|
+
} catch (_) {}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
loops.sort((a, b) => (b.lastRunAt || 0) - (a.lastRunAt || 0));
|
|
32
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
33
|
+
res.end(JSON.stringify({ loops }));
|
|
34
|
+
} catch (_) {
|
|
35
|
+
res.writeHead(500);
|
|
36
|
+
res.end('{"loops":[]}');
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// GET /api/status — live system snapshot for dashboard polling
|
|
42
|
+
if (req.method === 'GET' && url === '/api/status') {
|
|
43
|
+
try {
|
|
44
|
+
const root = ctx.projectDir || process.cwd();
|
|
45
|
+
// Active org runs: { orgName -> runId }
|
|
46
|
+
const orgRuns = {};
|
|
47
|
+
ctx.activeOrgRuns.forEach((runId, org) => {
|
|
48
|
+
orgRuns[org] = runId;
|
|
49
|
+
});
|
|
50
|
+
// Recent events (last 10)
|
|
51
|
+
let recentEvents = [];
|
|
52
|
+
try {
|
|
53
|
+
const evPath = path.join(root, 'data', 'mastermind-events.jsonl');
|
|
54
|
+
const lines = fs
|
|
55
|
+
.readFileSync(evPath, 'utf8')
|
|
56
|
+
.split('\n')
|
|
57
|
+
.filter((l) => l.trim())
|
|
58
|
+
.slice(-10);
|
|
59
|
+
recentEvents = lines
|
|
60
|
+
.map((l) => {
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(l);
|
|
63
|
+
} catch (_) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
} catch (_) {}
|
|
69
|
+
res.writeHead(200, {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
72
|
+
});
|
|
73
|
+
res.end(
|
|
74
|
+
JSON.stringify({
|
|
75
|
+
ts: Date.now(),
|
|
76
|
+
pid: process.pid,
|
|
77
|
+
uptime: process.uptime(),
|
|
78
|
+
dir: root,
|
|
79
|
+
sseClients: getMmClientCount(),
|
|
80
|
+
activeOrgs: Object.keys(orgRuns).length,
|
|
81
|
+
orgRuns,
|
|
82
|
+
recentEvents,
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
87
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
88
|
+
}
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// GET /api/orgs/:name/runs/current — events from the active run file for an org
|
|
93
|
+
if (req.method === 'GET' && /^\/api\/orgs\/[^/]+\/runs\/current$/.test(url)) {
|
|
94
|
+
try {
|
|
95
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
96
|
+
const _curQs = new URL(req.url, 'http://localhost').searchParams;
|
|
97
|
+
const root = path.resolve(_curQs.get('dir') || ctx.projectDir || process.cwd());
|
|
98
|
+
// Validate orgName
|
|
99
|
+
if (!orgName || orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
100
|
+
res.writeHead(400);
|
|
101
|
+
res.end('{"error":"invalid org name"}');
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
const runId = ctx.activeOrgRuns.get(orgName);
|
|
105
|
+
const monoDir = ctx._getGitMonomindDir(root) || path.join(root, '.monomind');
|
|
106
|
+
// Try active run first, then fall back to most recent run file.
|
|
107
|
+
// Org Runtime v2 (#238): the active run's own events live under
|
|
108
|
+
// orgs/<org>/<runId>/bus.jsonl, never the v1 flat orgs/<org>/runs/
|
|
109
|
+
// layout — check that shape (and translate it) before the v1 lookup.
|
|
110
|
+
let runFile = null;
|
|
111
|
+
let isV2 = false;
|
|
112
|
+
if (runId) {
|
|
113
|
+
const v2Candidate = path.join(monoDir, 'orgs', orgName, runId, 'bus.jsonl');
|
|
114
|
+
if (fs.existsSync(v2Candidate)) {
|
|
115
|
+
runFile = v2Candidate;
|
|
116
|
+
isV2 = true;
|
|
117
|
+
} else {
|
|
118
|
+
const candidate = path.join(monoDir, 'orgs', orgName, 'runs', `${runId}.jsonl`);
|
|
119
|
+
if (fs.existsSync(candidate)) runFile = candidate;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!runFile) {
|
|
123
|
+
const orgDir = path.join(monoDir, 'orgs', orgName);
|
|
124
|
+
const runDirs = fs.existsSync(orgDir)
|
|
125
|
+
? fs
|
|
126
|
+
.readdirSync(orgDir)
|
|
127
|
+
.filter(
|
|
128
|
+
(d) => d.startsWith('run-') && fs.existsSync(path.join(orgDir, d, 'bus.jsonl')),
|
|
129
|
+
)
|
|
130
|
+
.sort()
|
|
131
|
+
.reverse()
|
|
132
|
+
: [];
|
|
133
|
+
if (runDirs.length) {
|
|
134
|
+
runFile = path.join(orgDir, runDirs[0], 'bus.jsonl');
|
|
135
|
+
isV2 = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!runFile) {
|
|
139
|
+
const runsDir = path.join(monoDir, 'orgs', orgName, 'runs');
|
|
140
|
+
if (fs.existsSync(runsDir)) {
|
|
141
|
+
const files = fs
|
|
142
|
+
.readdirSync(runsDir)
|
|
143
|
+
.filter((f) => f.endsWith('.jsonl') && !f.startsWith('._'));
|
|
144
|
+
if (files.length) {
|
|
145
|
+
files.sort();
|
|
146
|
+
runFile = path.join(runsDir, files[files.length - 1]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!runFile) {
|
|
151
|
+
res.writeHead(404);
|
|
152
|
+
res.end('{"events":[],"runId":null}');
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
const detectedRunId = isV2
|
|
156
|
+
? path.basename(path.dirname(runFile))
|
|
157
|
+
: path.basename(runFile, '.jsonl');
|
|
158
|
+
const lines = fs
|
|
159
|
+
.readFileSync(runFile, 'utf8')
|
|
160
|
+
.split('\n')
|
|
161
|
+
.filter((l) => l.trim())
|
|
162
|
+
.slice(-100);
|
|
163
|
+
const rawEvents = lines
|
|
164
|
+
.map((l) => {
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(l);
|
|
167
|
+
} catch (_) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.filter(Boolean);
|
|
172
|
+
// v1's stored events are already dashboard-native; v2's raw BusEvents
|
|
173
|
+
// need the same translate()/companionEvents() pass the live view gets.
|
|
174
|
+
const events = isV2
|
|
175
|
+
? rawEvents.flatMap((e) => [...companionEvents(e), translate(e)])
|
|
176
|
+
: rawEvents;
|
|
177
|
+
res.writeHead(200, {
|
|
178
|
+
'Content-Type': 'application/json',
|
|
179
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
180
|
+
});
|
|
181
|
+
res.end(
|
|
182
|
+
JSON.stringify({ runId: detectedRunId, events, active: ctx.activeOrgRuns.has(orgName) }),
|
|
183
|
+
);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
res.writeHead(500);
|
|
186
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// GET /api/orgs/:name/history — per-run outcome summaries (history.jsonl, newest first)
|
|
192
|
+
if (req.method === 'GET' && /^\/api\/orgs\/[^/]+\/history$/.test(url)) {
|
|
193
|
+
try {
|
|
194
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
195
|
+
const _histQs = new URL(req.url, 'http://localhost').searchParams;
|
|
196
|
+
const root = path.resolve(_histQs.get('dir') || ctx.projectDir || process.cwd());
|
|
197
|
+
if (!orgName || orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
198
|
+
res.writeHead(400);
|
|
199
|
+
res.end('{"error":"invalid org name"}');
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
const monoDir = ctx._getGitMonomindDir(root) || path.join(root, '.monomind');
|
|
203
|
+
const histFile = path.join(monoDir, 'orgs', orgName, 'history.jsonl');
|
|
204
|
+
let runs = [];
|
|
205
|
+
if (fs.existsSync(histFile)) {
|
|
206
|
+
runs = fs
|
|
207
|
+
.readFileSync(histFile, 'utf8')
|
|
208
|
+
.split('\n')
|
|
209
|
+
.filter((l) => l.trim())
|
|
210
|
+
.map((l) => {
|
|
211
|
+
try {
|
|
212
|
+
return JSON.parse(l);
|
|
213
|
+
} catch (_) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.filter(Boolean)
|
|
218
|
+
.reverse()
|
|
219
|
+
.slice(0, 50);
|
|
220
|
+
}
|
|
221
|
+
res.writeHead(200, {
|
|
222
|
+
'Content-Type': 'application/json',
|
|
223
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
224
|
+
});
|
|
225
|
+
res.end(JSON.stringify({ runs }));
|
|
226
|
+
} catch (err) {
|
|
227
|
+
res.writeHead(500);
|
|
228
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// GET /api/orgs/:name/memory — org knowledge-graph stats + glossary + rules
|
|
234
|
+
if (req.method === 'GET' && /^\/api\/orgs\/[^/]+\/memory$/.test(url)) {
|
|
235
|
+
try {
|
|
236
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
237
|
+
const _memQs = new URL(req.url, 'http://localhost').searchParams;
|
|
238
|
+
const root = path.resolve(_memQs.get('dir') || ctx.projectDir || process.cwd());
|
|
239
|
+
if (!orgName || orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
240
|
+
res.writeHead(400);
|
|
241
|
+
res.end('{"error":"invalid org name"}');
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
const monoDir = ctx._getGitMonomindDir(root) || path.join(root, '.monomind');
|
|
245
|
+
const dbPath = path.join(monoDir, 'org-memory');
|
|
246
|
+
if (!fs.existsSync(dbPath)) {
|
|
247
|
+
res.writeHead(200, {
|
|
248
|
+
'Content-Type': 'application/json',
|
|
249
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
250
|
+
});
|
|
251
|
+
res.end(JSON.stringify({ nodes: 0, edges: 0, rules: 0, glossary: [], ruleTexts: [] }));
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
const kg = await import('../memory/memory-kg.js');
|
|
255
|
+
const [stats, glossary, ruleList] = await Promise.all([
|
|
256
|
+
kg.kgStats({ dbPath }),
|
|
257
|
+
kg.kgGlossary({ dbPath, limit: 12 }),
|
|
258
|
+
kg.kgListRules({ dbPath, limit: 10 }),
|
|
259
|
+
]);
|
|
260
|
+
res.writeHead(200, {
|
|
261
|
+
'Content-Type': 'application/json',
|
|
262
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
263
|
+
});
|
|
264
|
+
res.end(
|
|
265
|
+
JSON.stringify({
|
|
266
|
+
...stats,
|
|
267
|
+
glossary,
|
|
268
|
+
ruleTexts: ruleList.map((r) => r.rule.slice(0, 200)),
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
} catch (err) {
|
|
272
|
+
res.writeHead(500);
|
|
273
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
274
|
+
}
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Org dashboard routes: artifacts, mastermind events, stream, sessions and pages.
|
|
5
|
+
// Registered, in order, by handleOrgRoutes in routes-org.mjs.
|
|
6
|
+
export async function handleOrgMastermindRoutes(req, res, url, corsOrigin, ctx) {
|
|
7
|
+
// GET /api/org/:name/artifact — serve file content for chat "View" button
|
|
8
|
+
if (req.method === 'GET' && /^\/api\/org\/[^/]+\/artifact/.test(url)) {
|
|
9
|
+
try {
|
|
10
|
+
const _artOrgName = decodeURIComponent(url.split('/')[3] || '');
|
|
11
|
+
if (_artOrgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(_artOrgName)) {
|
|
12
|
+
res.writeHead(400);
|
|
13
|
+
res.end(JSON.stringify({ error: 'Invalid org name' }));
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const _artQp = new URL(`http://x${req.url}`).searchParams;
|
|
17
|
+
const _rawPath = _artQp.get('path');
|
|
18
|
+
if (!_rawPath) {
|
|
19
|
+
res.writeHead(400);
|
|
20
|
+
res.end(JSON.stringify({ error: 'path required' }));
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const _filePath = path.resolve(decodeURIComponent(_rawPath));
|
|
24
|
+
// Security: this endpoint used to allow reads anywhere under the project
|
|
25
|
+
// root or any data/known-projects.json entry — letting any dashboard
|
|
26
|
+
// client read .env, .git/config, or other arbitrary project files.
|
|
27
|
+
// Restrict it, like /api/file-content above, to this org's own
|
|
28
|
+
// .monomind/orgs/<name>/ directory — never outside .monomind.
|
|
29
|
+
const _artBaseDir = path.resolve(_artQp.get('dir') || ctx.projectDir || process.cwd());
|
|
30
|
+
const _artProjDir = ctx._resolveOrgProjectDir(_artOrgName, _artBaseDir) || _artBaseDir;
|
|
31
|
+
const _artMonoDir =
|
|
32
|
+
ctx._getGitMonomindDir(_artProjDir) || path.join(_artProjDir, '.monomind');
|
|
33
|
+
const _artOrgDir = path.join(_artMonoDir, 'orgs', _artOrgName);
|
|
34
|
+
const _safe = _filePath === _artOrgDir || _filePath.startsWith(_artOrgDir + path.sep);
|
|
35
|
+
if (!_safe) {
|
|
36
|
+
res.writeHead(403);
|
|
37
|
+
res.end(JSON.stringify({ error: 'path not allowed' }));
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (!fs.existsSync(_filePath)) {
|
|
41
|
+
res.writeHead(404);
|
|
42
|
+
res.end(JSON.stringify({ error: 'file not found' }));
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const _mime = ctx._detectMimeType(_filePath);
|
|
46
|
+
const _size = fs.statSync(_filePath).size;
|
|
47
|
+
// Reject files >2MB to avoid blocking the event loop
|
|
48
|
+
if (_size > 2 * 1024 * 1024) {
|
|
49
|
+
res.writeHead(413);
|
|
50
|
+
res.end(JSON.stringify({ error: 'file too large', size: _size }));
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (!_mime.startsWith('text/') && _mime !== 'application/json') {
|
|
54
|
+
res.writeHead(200, {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
57
|
+
});
|
|
58
|
+
res.end(JSON.stringify({ binary: true, mimeType: _mime, size: _size }));
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const _content = fs.readFileSync(_filePath, 'utf8');
|
|
62
|
+
res.writeHead(200, {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
65
|
+
});
|
|
66
|
+
res.end(JSON.stringify({ content: _content, mimeType: _mime, size: _size }));
|
|
67
|
+
} catch (_e) {
|
|
68
|
+
res.writeHead(500);
|
|
69
|
+
res.end(JSON.stringify({ error: 'read failed' }));
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ------------------------------------------------- Mastermind event system
|
|
75
|
+
// POST /api/mastermind/event — ingest event from mastermind skill
|
|
76
|
+
if (req.method === 'POST' && url === '/api/mastermind/event') {
|
|
77
|
+
await ctx.handleMastermindEvent(req, res, corsOrigin);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// GET /api/mastermind-stream — SSE for real-time events
|
|
82
|
+
if (req.method === 'GET' && url === '/api/mastermind-stream') {
|
|
83
|
+
res.writeHead(200, {
|
|
84
|
+
'Content-Type': 'text/event-stream',
|
|
85
|
+
'Cache-Control': 'no-cache',
|
|
86
|
+
Connection: 'keep-alive',
|
|
87
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
88
|
+
});
|
|
89
|
+
res.write(': connected\n\n');
|
|
90
|
+
ctx.addMmClient(res);
|
|
91
|
+
// Replay last 50 events from disk (use ?project= param if provided)
|
|
92
|
+
try {
|
|
93
|
+
const _sseQp = new URL(`http://x${req.url}`).searchParams;
|
|
94
|
+
const _sseProj = _sseQp.get('project');
|
|
95
|
+
const root2 = _sseProj || ctx.projectDir || process.cwd();
|
|
96
|
+
const evFile = path.join(root2, 'data', 'mastermind-events.jsonl');
|
|
97
|
+
const lines = fs.readFileSync(evFile, 'utf8').trim().split('\n').filter(Boolean).slice(-50);
|
|
98
|
+
for (const l of lines) res.write(`data: ${l}\n\n`);
|
|
99
|
+
} catch (_) {}
|
|
100
|
+
const ka = setInterval(() => {
|
|
101
|
+
try {
|
|
102
|
+
res.write(': ping\n\n');
|
|
103
|
+
} catch (_) {
|
|
104
|
+
clearInterval(ka);
|
|
105
|
+
ctx.removeMmClient(res);
|
|
106
|
+
}
|
|
107
|
+
}, 20000);
|
|
108
|
+
req.on('close', () => {
|
|
109
|
+
ctx.removeMmClient(res);
|
|
110
|
+
clearInterval(ka);
|
|
111
|
+
});
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// GET /api/mastermind/sessions
|
|
116
|
+
if (req.method === 'GET' && url.startsWith('/api/mastermind/sessions')) {
|
|
117
|
+
try {
|
|
118
|
+
const qp = new URL(`http://x${req.url}`).searchParams;
|
|
119
|
+
const filterProject = qp.get('project');
|
|
120
|
+
const limitParam = Math.min(parseInt(qp.get('limit') || '200', 10) || 200, 500);
|
|
121
|
+
const serverRoot = ctx.projectDir || process.cwd();
|
|
122
|
+
// Collect all project dirs to aggregate
|
|
123
|
+
const projectDirs = new Set([serverRoot]);
|
|
124
|
+
try {
|
|
125
|
+
const known = JSON.parse(
|
|
126
|
+
fs.readFileSync(path.join(serverRoot, 'data', 'known-projects.json'), 'utf8'),
|
|
127
|
+
);
|
|
128
|
+
known.forEach((p) => projectDirs.add(p));
|
|
129
|
+
} catch (_) {}
|
|
130
|
+
let allSessions = [];
|
|
131
|
+
for (const pd of projectDirs) {
|
|
132
|
+
if (filterProject && pd !== filterProject) continue;
|
|
133
|
+
const sessDir = path.join(pd, 'data', 'sessions');
|
|
134
|
+
const indexFile = path.join(sessDir, '_index.json');
|
|
135
|
+
// ── New format: per-session JSONL + _index.json ──
|
|
136
|
+
if (fs.existsSync(indexFile)) {
|
|
137
|
+
try {
|
|
138
|
+
const idx = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
|
|
139
|
+
const top = idx.slice(0, limitParam);
|
|
140
|
+
for (const entry of top) {
|
|
141
|
+
const _sid = String(entry.id || '').trim();
|
|
142
|
+
if (!_sid || !ctx.SESSION_ID_RE.test(_sid)) continue;
|
|
143
|
+
let events = [];
|
|
144
|
+
try {
|
|
145
|
+
const jl = fs.readFileSync(path.join(sessDir, `${_sid}.jsonl`), 'utf8');
|
|
146
|
+
events = jl
|
|
147
|
+
.trim()
|
|
148
|
+
.split('\n')
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.map((l) => {
|
|
151
|
+
try {
|
|
152
|
+
return JSON.parse(l);
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
.filter(Boolean);
|
|
158
|
+
} catch (_) {}
|
|
159
|
+
allSessions.push({ ...entry, events, project: pd });
|
|
160
|
+
}
|
|
161
|
+
} catch (_) {}
|
|
162
|
+
} else {
|
|
163
|
+
// ── Legacy fallback: mastermind-sessions.json ──
|
|
164
|
+
const f = path.join(pd, 'data', 'mastermind-sessions.json');
|
|
165
|
+
if (fs.existsSync(f)) {
|
|
166
|
+
try {
|
|
167
|
+
const s = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
168
|
+
s.forEach((sess) => {
|
|
169
|
+
if (!sess.project) sess.project = pd;
|
|
170
|
+
});
|
|
171
|
+
allSessions = allSessions.concat(s);
|
|
172
|
+
} catch (_) {}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
allSessions.sort((a, b) => (b.ts || b.startedAt || 0) - (a.ts || a.startedAt || 0));
|
|
177
|
+
res.writeHead(200, {
|
|
178
|
+
'Content-Type': 'application/json',
|
|
179
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
180
|
+
});
|
|
181
|
+
res.end(JSON.stringify(allSessions.slice(0, limitParam)));
|
|
182
|
+
} catch (_) {
|
|
183
|
+
res.writeHead(200);
|
|
184
|
+
res.end('[]');
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// GET /api/mastermind/session/:id/trace — human-readable markdown trace
|
|
190
|
+
if (req.method === 'GET' && url.match(/^\/api\/mastermind\/session\/[^/]+\/trace$/)) {
|
|
191
|
+
try {
|
|
192
|
+
const sid = url.split('/')[4];
|
|
193
|
+
const sessFile = path.join(
|
|
194
|
+
ctx.projectDir || process.cwd(),
|
|
195
|
+
'data',
|
|
196
|
+
'sessions',
|
|
197
|
+
`${sid}.json`,
|
|
198
|
+
);
|
|
199
|
+
let s = null;
|
|
200
|
+
if (fs.existsSync(sessFile)) {
|
|
201
|
+
s = JSON.parse(fs.readFileSync(sessFile, 'utf8'));
|
|
202
|
+
} else {
|
|
203
|
+
const f = path.join(ctx.projectDir || process.cwd(), 'data', 'mastermind-sessions.json');
|
|
204
|
+
const sessions = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
205
|
+
s = sessions.find((x) => x.id === sid);
|
|
206
|
+
}
|
|
207
|
+
if (!s) {
|
|
208
|
+
res.writeHead(404);
|
|
209
|
+
res.end('Session not found');
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
const fmt = (ts) => `${new Date(ts).toISOString().replace('T', ' ').slice(0, 19)} UTC`;
|
|
213
|
+
const lines = [
|
|
214
|
+
`# Mastermind Session Trace: ${s.id}`,
|
|
215
|
+
``,
|
|
216
|
+
`**Prompt:** ${s.prompt || '(none)'}`,
|
|
217
|
+
`**Status:** ${s.status}`,
|
|
218
|
+
`**Started:** ${fmt(s.ts)}`,
|
|
219
|
+
s.endTs ? `**Ended:** ${fmt(s.endTs)}` : '',
|
|
220
|
+
`**Domains:** ${(s.domains || []).join(', ') || '(none yet)'}`,
|
|
221
|
+
``,
|
|
222
|
+
];
|
|
223
|
+
for (const ev of s.events || []) {
|
|
224
|
+
const t = fmt(ev.ts);
|
|
225
|
+
if (ev.type === 'session:start')
|
|
226
|
+
lines.push(`\`${t}\` **SESSION START** — prompt: "${ev.prompt || ''}"`);
|
|
227
|
+
else if (ev.type === 'domain:dispatch')
|
|
228
|
+
lines.push(`\`${t}\` **DOMAIN DISPATCH** → \`${ev.domain}\` — ${ev.cmd || ''}`);
|
|
229
|
+
else if (ev.type === 'agent:spawn')
|
|
230
|
+
lines.push(
|
|
231
|
+
`\`${t}\` **AGENT SPAWN** [\`${ev.domain}\`] → agent: \`${ev.agent}\` — ${ev.task || ''}`,
|
|
232
|
+
);
|
|
233
|
+
else if (ev.type === 'intercom')
|
|
234
|
+
lines.push(`\`${t}\` **INTERCOM** \`${ev.from}\` → \`${ev.to}\`: ${ev.msg || ''}`);
|
|
235
|
+
else if (ev.type === 'domain:complete')
|
|
236
|
+
lines.push(
|
|
237
|
+
`\`${t}\` **DOMAIN COMPLETE** [\`${ev.domain}\`] status: ${ev.status}${ev.artifacts?.length ? ` — artifacts: ${ev.artifacts.join(', ')}` : ''}`,
|
|
238
|
+
);
|
|
239
|
+
else if (ev.type === 'session:complete')
|
|
240
|
+
lines.push(
|
|
241
|
+
`\`${t}\` **SESSION COMPLETE** — status: ${ev.status}, domains: ${(ev.domains || []).join(', ')}`,
|
|
242
|
+
);
|
|
243
|
+
else lines.push(`\`${t}\` ${ev.type} ${JSON.stringify(ev)}`);
|
|
244
|
+
}
|
|
245
|
+
res.writeHead(200, {
|
|
246
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
247
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
248
|
+
});
|
|
249
|
+
res.end(lines.join('\n'));
|
|
250
|
+
} catch (_) {
|
|
251
|
+
res.writeHead(500);
|
|
252
|
+
res.end('Error');
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// GET /api/mastermind/session/:id
|
|
258
|
+
if (req.method === 'GET' && url.startsWith('/api/mastermind/session/')) {
|
|
259
|
+
try {
|
|
260
|
+
const sid = url.slice('/api/mastermind/session/'.length);
|
|
261
|
+
// Check individual session file first
|
|
262
|
+
const sessFile = path.join(
|
|
263
|
+
ctx.projectDir || process.cwd(),
|
|
264
|
+
'data',
|
|
265
|
+
'sessions',
|
|
266
|
+
`${sid}.json`,
|
|
267
|
+
);
|
|
268
|
+
if (fs.existsSync(sessFile)) {
|
|
269
|
+
res.writeHead(200, {
|
|
270
|
+
'Content-Type': 'application/json',
|
|
271
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
272
|
+
});
|
|
273
|
+
res.end(fs.readFileSync(sessFile, 'utf8'));
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
const f = path.join(ctx.projectDir || process.cwd(), 'data', 'mastermind-sessions.json');
|
|
277
|
+
const sessions = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
278
|
+
const s = sessions.find((x) => x.id === sid);
|
|
279
|
+
res.writeHead(200, {
|
|
280
|
+
'Content-Type': 'application/json',
|
|
281
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
282
|
+
});
|
|
283
|
+
res.end(JSON.stringify(s || null));
|
|
284
|
+
} catch (_) {
|
|
285
|
+
res.writeHead(200);
|
|
286
|
+
res.end('null');
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// -------------------------------------------------------- GET /mastermind
|
|
292
|
+
if (req.method === 'GET' && url === '/mastermind') {
|
|
293
|
+
// Serve local file if present (dev), otherwise fall back to bundled HTML
|
|
294
|
+
const root = ctx.projectDir || process.cwd();
|
|
295
|
+
const htmlPath = path.join(root, 'docs', 'mastermind-diagram.html');
|
|
296
|
+
let html = ctx.MASTERMIND_DIAGRAM_HTML;
|
|
297
|
+
try {
|
|
298
|
+
html = fs.readFileSync(htmlPath, 'utf8');
|
|
299
|
+
} catch (_) {}
|
|
300
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
301
|
+
res.end(html);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ----------------------------------------------------------- GET /orgs
|
|
306
|
+
if (req.method === 'GET' && url === '/orgs') {
|
|
307
|
+
try {
|
|
308
|
+
const htmlPath = path.join(ctx.__dirname, 'orgs.html');
|
|
309
|
+
let html = fs.readFileSync(htmlPath, 'utf8');
|
|
310
|
+
// Inject this process's auth credential the same way GET / does for
|
|
311
|
+
// dashboard.html — orgs.html's fetch() calls hit the same now
|
|
312
|
+
// default-closed /api/* routes and need a way to know the token.
|
|
313
|
+
html = html.replace(
|
|
314
|
+
'<head>',
|
|
315
|
+
`<head>\n<meta name="mm-token" content="${ctx.dashboardAuthValue}">`,
|
|
316
|
+
);
|
|
317
|
+
res.writeHead(200, {
|
|
318
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
319
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
320
|
+
});
|
|
321
|
+
res.end(html);
|
|
322
|
+
} catch (err) {
|
|
323
|
+
res.writeHead(404);
|
|
324
|
+
res.end(`orgs.html not found: ${err.message}`);
|
|
325
|
+
}
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ------------------------------------------------ GET /orgs-files.js
|
|
330
|
+
// Files-tab + diff-view script, split out of orgs.html (see orgs.html's
|
|
331
|
+
// script-src comment). Not a generic static handler — this UI serves each
|
|
332
|
+
// sibling asset via its own hardcoded route, same as GET /orgs above.
|
|
333
|
+
if (req.method === 'GET' && url === '/orgs-files.js') {
|
|
334
|
+
try {
|
|
335
|
+
const jsPath = path.join(ctx.__dirname, 'orgs-files.js');
|
|
336
|
+
const js = fs.readFileSync(jsPath, 'utf8');
|
|
337
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
338
|
+
res.end(js);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
res.writeHead(404);
|
|
341
|
+
res.end(`orgs-files.js not found: ${err.message}`);
|
|
342
|
+
}
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|