@a-company/paradigm 3.34.0 → 3.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accept-orchestration-XXANWJVZ.js → accept-orchestration-ZUWQUHSK.js} +6 -6
- package/dist/add-VSPZ6FM4.js +81 -0
- package/dist/{aggregate-XHQ6GI3Z.js → aggregate-SV3VGEIL.js} +2 -2
- package/dist/assess-UHBDYIK7.js +68 -0
- package/dist/{beacon-BTLQMYQL.js → beacon-3SJV4DAP.js} +2 -2
- package/dist/calibration-WWHK73WU.js +135 -0
- package/dist/{chunk-C5ZE6WEX.js → chunk-2SKXFXIT.js} +91 -1
- package/dist/{chunk-S5TDFT5Q.js → chunk-7COU5S2Z.js} +2 -2
- package/dist/{chunk-3BAMPB6I.js → chunk-7WEKMZ46.js} +2 -147
- package/dist/{chunk-H4TVBJD4.js → chunk-AKIMFN6I.js} +3 -3
- package/dist/{chunk-3DYYXGDC.js → chunk-CDMAMDSG.js} +33 -0
- package/dist/{chunk-PFLWLC6J.js → chunk-CZEIK3Y2.js} +855 -34
- package/dist/chunk-F3BCHPYT.js +143 -0
- package/dist/chunk-GT5QGC2H.js +253 -0
- package/dist/{chunk-UQNTJ5VB.js → chunk-HIKKOCXY.js} +1 -1
- package/dist/{chunk-J26YQVAK.js → chunk-J4E6K5MG.js} +1 -1
- package/dist/chunk-L27I3CPZ.js +357 -0
- package/dist/{chunk-WOONGZ3C.js → chunk-P7XSBJE3.js} +1 -1
- package/dist/{chunk-Z7W7HNRG.js → chunk-QDXI2DHR.js} +1 -1
- package/dist/{chunk-BRILIG7Z.js → chunk-QIOCFXDQ.js} +42 -0
- package/dist/{chunk-3BGSDKWD.js → chunk-QWA26UNO.js} +7 -7
- package/dist/{lore-server-ILPHKWLK.js → chunk-RAB5IKPR.js} +77 -112
- package/dist/{chunk-BKMNLROM.js → chunk-RGFANZ4Q.js} +448 -147
- package/dist/{chunk-R2SGQ22F.js → chunk-YW5OCVKB.js} +448 -2
- package/dist/{chunk-CTF6RHKG.js → chunk-ZGUAAVMA.js} +17 -2
- package/dist/{commands-KPT2T2OZ.js → commands-LEPFD7S5.js} +452 -1
- package/dist/config-schema-3YNIFJCJ.js +152 -0
- package/dist/{constellation-LZ6XIKDT.js → constellation-FAGT45TU.js} +2 -2
- package/dist/{context-audit-RI4R2WRH.js → context-audit-557EO6PK.js} +138 -8
- package/dist/{cost-4SZM7OUS.js → cost-UD3WPEKZ.js} +1 -1
- package/dist/{delete-YTASL4SM.js → delete-RRK4RL6Y.js} +1 -1
- package/dist/{diff-T6YJSAAC.js → diff-IP5CIARP.js} +6 -6
- package/dist/{dist-AG5JNIZU-HW2FWNTZ.js → dist-5QE2BB2B-X6DYVSUL.js} +59 -5
- package/dist/{dist-OH4DBV2O.js → dist-OGTSAZ55.js} +16 -1
- package/dist/{dist-IKBGY7FQ.js → dist-RVKYUCRU.js} +3 -1
- package/dist/{dist-QSBAGCZT.js → dist-UXWV4OKX.js} +2 -2
- package/dist/{dist-RMAIFRTW.js → dist-Y7I3CFY5.js} +5 -3
- package/dist/{doctor-INBOLZC7.js → doctor-GKZJU7QG.js} +1 -1
- package/dist/{edit-S7NZD7H7.js → edit-4CLNN5JG.js} +1 -1
- package/dist/{graph-ERNQQQ7C.js → graph-YYUXI3F7.js} +1 -1
- package/dist/graph-server-ZPXRSGCW.js +116 -0
- package/dist/{habits-7BORPC2F.js → habits-O37HTUKE.js} +2 -2
- package/dist/index.js +200 -86
- package/dist/integrity-MK2OP5TA.js +194 -0
- package/dist/integrity-checker-J7YXRTBT.js +11 -0
- package/dist/{lint-MTRZB5EC.js → lint-HYWGS3JJ.js} +1 -1
- package/dist/{list-QTFWN35D.js → list-BTLFHSRC.js} +1 -1
- package/dist/list-IUCYPGMK.js +57 -0
- package/dist/{lore-loader-S5BXMH27.js → lore-loader-VTEEZDX3.js} +3 -1
- package/dist/lore-server-NOOAHKJX.js +118 -0
- package/dist/mcp.js +2581 -112
- package/dist/{migrate-HRN5TUBQ.js → migrate-FQVGQNXZ.js} +21 -3
- package/dist/{migrate-assessments-FPR6C35Z.js → migrate-assessments-JP6Q5KME.js} +1 -1
- package/dist/{orchestrate-3SI6ON33.js → orchestrate-A226N6FC.js} +6 -6
- package/dist/platform-server-KK4OCRTV.js +891 -0
- package/dist/{probe-ABMGCXQG.js → probe-7JK7IDNI.js} +4 -4
- package/dist/{providers-YW3FG6DA.js → providers-YNFSL6HK.js} +1 -1
- package/dist/quiz-I75NU2QQ.js +99 -0
- package/dist/{record-UGN75GTB.js → record-46CLR4OG.js} +11 -2
- package/dist/{reindex-YC7LD4MN.js → reindex-NZQRGKPN.js} +3 -2
- package/dist/{remember-WR6ZVXLT.js → remember-4EUZKIIB.js} +1 -1
- package/dist/{retag-URLJLMSK.js → retag-KC4JVRLE.js} +1 -1
- package/dist/{review-725ZKA7U.js → review-Q7M4CRB5.js} +1 -1
- package/dist/{ripple-QTXKJCEI.js → ripple-RI3LOT6R.js} +2 -2
- package/dist/{sentinel-FUR3QKCJ.js → sentinel-BKYTBT7M.js} +1 -1
- package/dist/sentinel-bridge-IZTXYS5M.js +109 -0
- package/dist/sentinel-ui/assets/{index-Zh1YM0C9.css → index-CJ1Wx083.css} +1 -1
- package/dist/sentinel-ui/assets/index-S1VJ67dT.js +62 -0
- package/dist/sentinel-ui/assets/index-S1VJ67dT.js.map +1 -0
- package/dist/sentinel-ui/index.html +2 -2
- package/dist/sentinel.js +6 -6
- package/dist/{serve-DIALBCTU.js → serve-22A4XOIG.js} +1 -1
- package/dist/{university-A66BMZ4Z.js → serve-2YJ6D2Y6.js} +9 -8
- package/dist/serve-3V2WXLGM.js +33 -0
- package/dist/{server-2VICPDUR.js → server-OFEJ2HJP.js} +25 -2
- package/dist/{server-OWBK2WFS.js → server-RDLQ3DK7.js} +49 -4
- package/dist/{setup-ASR6OMKV.js → setup-M2ZKLKNN.js} +2 -2
- package/dist/{shift-7XLSBLDW.js → shift-LNMKFYLR.js} +63 -14
- package/dist/{show-GEVVQWWG.js → show-P7GYO43X.js} +1 -1
- package/dist/show-PKZMYKRN.js +82 -0
- package/dist/{snapshot-QZFD7YBI.js → snapshot-Y3COXK4T.js} +2 -2
- package/dist/{spawn-DIY7T4QW.js → spawn-SSXZX45U.js} +2 -2
- package/dist/status-KLHALGW4.js +71 -0
- package/dist/{summary-R4CSYNNP.js → summary-5NQNOD3F.js} +2 -2
- package/dist/{sweep-5POCF2E4.js → sweep-EZU3GU6S.js} +1 -1
- package/dist/symphony-ROEKK7VD.js +999 -0
- package/dist/{team-VH3HYABB.js → team-HGLJXWQG.js} +7 -7
- package/dist/{timeline-RKXNRMKF.js → timeline-ANC7LVDL.js} +1 -1
- package/dist/{triage-GJ6GK647.js → triage-POXJ2TIX.js} +2 -2
- package/dist/university-content/courses/.purpose +7 -1
- package/dist/university-content/courses/para-501.json +166 -0
- package/dist/university-content/plsat/.purpose +6 -0
- package/dist/university-content/plsat/v3.0.json +323 -1
- package/dist/university-content/reference.json +48 -0
- package/dist/university-ui/assets/{index-TcsCEBMo.js → index-tfi5xN4Q.js} +2 -2
- package/dist/university-ui/assets/{index-TcsCEBMo.js.map → index-tfi5xN4Q.js.map} +1 -1
- package/dist/university-ui/index.html +1 -1
- package/dist/validate-GD5XWILV.js +134 -0
- package/dist/{validate-OUHUBZPO.js → validate-ZVPNN4FL.js} +1 -1
- package/dist/{workspace-5RBSALXC.js → workspace-UIUTHZTD.js} +5 -5
- package/package.json +4 -2
- package/platform-ui/dist/assets/GitSection-C-GQWHcu.css +1 -0
- package/platform-ui/dist/assets/GitSection-DvyJBF_-.js +4 -0
- package/platform-ui/dist/assets/GraphSection-BiQrXqfs.js +8 -0
- package/platform-ui/dist/assets/GraphSection-BlgXTl53.css +1 -0
- package/platform-ui/dist/assets/LoreSection-BaH1FaRb.js +1 -0
- package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +1 -0
- package/platform-ui/dist/assets/SentinelSection-BI-aIYKL.css +1 -0
- package/platform-ui/dist/assets/SentinelSection-DemAznjI.js +1 -0
- package/platform-ui/dist/assets/index-CfpZFjea.css +1 -0
- package/platform-ui/dist/assets/index-DDKhCt-w.js +57 -0
- package/platform-ui/dist/index.html +14 -0
- package/dist/graph-server-BZ73HTAT.js +0 -251
- package/dist/sentinel-ui/assets/index-C_Wstm64.js +0 -62
- package/dist/sentinel-ui/assets/index-C_Wstm64.js.map +0 -1
- /package/dist/{chunk-VUSCJJ4A.js → chunk-EDOAWN7J.js} +0 -0
- /package/dist/{chunk-5SXMV4SP.js → chunk-FS3WTUHY.js} +0 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createInfoRouter,
|
|
4
|
+
createLoreRouter,
|
|
5
|
+
createSessionsRouter
|
|
6
|
+
} from "./chunk-RAB5IKPR.js";
|
|
7
|
+
import {
|
|
8
|
+
createGraphsRouter,
|
|
9
|
+
createSymbolsRouter
|
|
10
|
+
} from "./chunk-F3BCHPYT.js";
|
|
11
|
+
import "./chunk-ZXMDA7VB.js";
|
|
12
|
+
|
|
13
|
+
// src/platform-server/index.ts
|
|
14
|
+
import express from "express";
|
|
15
|
+
import * as http from "http";
|
|
16
|
+
import * as path2 from "path";
|
|
17
|
+
import * as fs2 from "fs";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
|
|
21
|
+
// src/platform-server/ws/index.ts
|
|
22
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
23
|
+
|
|
24
|
+
// src/platform-server/ws/agent.ts
|
|
25
|
+
function agentColor(agentId) {
|
|
26
|
+
const colors = [
|
|
27
|
+
"#58a6ff",
|
|
28
|
+
"#3fb950",
|
|
29
|
+
"#f85149",
|
|
30
|
+
"#d29922",
|
|
31
|
+
"#bc8cff",
|
|
32
|
+
"#f778ba",
|
|
33
|
+
"#79c0ff",
|
|
34
|
+
"#56d364"
|
|
35
|
+
];
|
|
36
|
+
let hash = 0;
|
|
37
|
+
for (let i = 0; i < agentId.length; i++) {
|
|
38
|
+
hash = (hash << 5) - hash + agentId.charCodeAt(i) | 0;
|
|
39
|
+
}
|
|
40
|
+
return colors[Math.abs(hash) % colors.length];
|
|
41
|
+
}
|
|
42
|
+
var annotationCounter = 0;
|
|
43
|
+
var AgentPresenceManager = class {
|
|
44
|
+
agents = /* @__PURE__ */ new Map();
|
|
45
|
+
staleTimeout = 2 * 60 * 1e3;
|
|
46
|
+
// 2 minutes
|
|
47
|
+
join(agentId) {
|
|
48
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
49
|
+
const presence = {
|
|
50
|
+
agentId,
|
|
51
|
+
color: agentColor(agentId),
|
|
52
|
+
connectedAt: now,
|
|
53
|
+
lastActivity: now
|
|
54
|
+
};
|
|
55
|
+
this.agents.set(agentId, presence);
|
|
56
|
+
return presence;
|
|
57
|
+
}
|
|
58
|
+
touch(agentId) {
|
|
59
|
+
const agent = this.agents.get(agentId);
|
|
60
|
+
if (agent) {
|
|
61
|
+
agent.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
leave(agentId) {
|
|
65
|
+
this.agents.delete(agentId);
|
|
66
|
+
}
|
|
67
|
+
getAll() {
|
|
68
|
+
return Array.from(this.agents.values());
|
|
69
|
+
}
|
|
70
|
+
pruneStale() {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const pruned = [];
|
|
73
|
+
for (const [id, agent] of this.agents) {
|
|
74
|
+
if (now - new Date(agent.lastActivity).getTime() > this.staleTimeout) {
|
|
75
|
+
this.agents.delete(id);
|
|
76
|
+
pruned.push(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return pruned;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var UserStateTracker = class {
|
|
83
|
+
state = {
|
|
84
|
+
section: "overview",
|
|
85
|
+
selectedSymbol: null,
|
|
86
|
+
theme: "dark",
|
|
87
|
+
lastInteraction: Date.now()
|
|
88
|
+
};
|
|
89
|
+
highlights = [];
|
|
90
|
+
annotations = [];
|
|
91
|
+
muted = false;
|
|
92
|
+
updateSection(section) {
|
|
93
|
+
this.state.section = section;
|
|
94
|
+
this.state.lastInteraction = Date.now();
|
|
95
|
+
}
|
|
96
|
+
updateSelectedSymbol(symbol) {
|
|
97
|
+
this.state.selectedSymbol = symbol;
|
|
98
|
+
this.state.lastInteraction = Date.now();
|
|
99
|
+
}
|
|
100
|
+
updateTheme(theme) {
|
|
101
|
+
this.state.theme = theme;
|
|
102
|
+
}
|
|
103
|
+
setMuted(muted) {
|
|
104
|
+
this.muted = muted;
|
|
105
|
+
}
|
|
106
|
+
isMuted() {
|
|
107
|
+
return this.muted;
|
|
108
|
+
}
|
|
109
|
+
isUserActive(thresholdMs = 5e3) {
|
|
110
|
+
return Date.now() - this.state.lastInteraction < thresholdMs;
|
|
111
|
+
}
|
|
112
|
+
getState() {
|
|
113
|
+
return { ...this.state, muted: this.muted };
|
|
114
|
+
}
|
|
115
|
+
addHighlight(h) {
|
|
116
|
+
this.highlights.push(h);
|
|
117
|
+
if (h.duration > 0) {
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
this.highlights = this.highlights.filter((x) => x !== h);
|
|
120
|
+
}, h.duration);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
addAnnotation(a) {
|
|
124
|
+
const annotation = {
|
|
125
|
+
...a,
|
|
126
|
+
id: `ann-${++annotationCounter}`,
|
|
127
|
+
createdAt: Date.now()
|
|
128
|
+
};
|
|
129
|
+
this.annotations.push(annotation);
|
|
130
|
+
if (annotation.duration > 0) {
|
|
131
|
+
setTimeout(() => {
|
|
132
|
+
this.annotations = this.annotations.filter((x) => x !== annotation);
|
|
133
|
+
}, annotation.duration);
|
|
134
|
+
}
|
|
135
|
+
return annotation;
|
|
136
|
+
}
|
|
137
|
+
clearHighlights() {
|
|
138
|
+
this.highlights = [];
|
|
139
|
+
}
|
|
140
|
+
clearAnnotations() {
|
|
141
|
+
this.annotations = [];
|
|
142
|
+
}
|
|
143
|
+
clearAll() {
|
|
144
|
+
this.highlights = [];
|
|
145
|
+
this.annotations = [];
|
|
146
|
+
}
|
|
147
|
+
getHighlights() {
|
|
148
|
+
return [...this.highlights];
|
|
149
|
+
}
|
|
150
|
+
getAnnotations() {
|
|
151
|
+
return [...this.annotations];
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/platform-server/ws/index.ts
|
|
156
|
+
function attachWebSocket(httpServer) {
|
|
157
|
+
const wsClients = /* @__PURE__ */ new Set();
|
|
158
|
+
const agentPresence = new AgentPresenceManager();
|
|
159
|
+
const userState = new UserStateTracker();
|
|
160
|
+
function broadcast(message) {
|
|
161
|
+
const data = JSON.stringify(message);
|
|
162
|
+
for (const client of wsClients) {
|
|
163
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
164
|
+
client.send(data);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const wss = new WebSocketServer({ server: httpServer, path: "/ws" });
|
|
169
|
+
wss.on("connection", (ws) => {
|
|
170
|
+
wsClients.add(ws);
|
|
171
|
+
ws.on("message", (raw) => {
|
|
172
|
+
try {
|
|
173
|
+
const msg = JSON.parse(raw.toString());
|
|
174
|
+
if (msg.type === "user:navigate") {
|
|
175
|
+
userState.updateSection(msg.section);
|
|
176
|
+
} else if (msg.type === "user:select") {
|
|
177
|
+
userState.updateSelectedSymbol(msg.symbol ?? null);
|
|
178
|
+
} else if (msg.type === "user:theme") {
|
|
179
|
+
userState.updateTheme(msg.theme);
|
|
180
|
+
} else if (msg.type === "user:mute") {
|
|
181
|
+
userState.setMuted(msg.muted);
|
|
182
|
+
broadcast({ type: "agent:mute_changed", muted: msg.muted });
|
|
183
|
+
} else if (msg.type === "ping") {
|
|
184
|
+
ws.send(JSON.stringify({ type: "pong", timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
185
|
+
}
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
ws.on("close", () => {
|
|
190
|
+
wsClients.delete(ws);
|
|
191
|
+
});
|
|
192
|
+
ws.on("error", () => {
|
|
193
|
+
wsClients.delete(ws);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
setInterval(() => {
|
|
197
|
+
const pruned = agentPresence.pruneStale();
|
|
198
|
+
for (const agentId of pruned) {
|
|
199
|
+
broadcast({ type: "agent:leave", agentId });
|
|
200
|
+
}
|
|
201
|
+
}, 3e4);
|
|
202
|
+
return {
|
|
203
|
+
broadcast,
|
|
204
|
+
agentPresence,
|
|
205
|
+
userState,
|
|
206
|
+
clientCount: () => wsClients.size
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/platform-server/routes/agent.ts
|
|
211
|
+
import { Router } from "express";
|
|
212
|
+
function createAgentRouter(wsContext) {
|
|
213
|
+
const router = Router();
|
|
214
|
+
router.post("/", (req, res) => {
|
|
215
|
+
const { command, agentId, payload } = req.body;
|
|
216
|
+
if (!command || !agentId) {
|
|
217
|
+
res.status(400).json({ error: "Missing command or agentId" });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const agents = wsContext.agentPresence.getAll();
|
|
221
|
+
if (!agents.find((a) => a.agentId === agentId)) {
|
|
222
|
+
wsContext.agentPresence.join(agentId);
|
|
223
|
+
wsContext.broadcast({ type: "agent:join", agent: wsContext.agentPresence.getAll().find((a) => a.agentId === agentId) });
|
|
224
|
+
}
|
|
225
|
+
wsContext.agentPresence.touch(agentId);
|
|
226
|
+
switch (command) {
|
|
227
|
+
case "navigate": {
|
|
228
|
+
const { section, symbol, loreId } = payload;
|
|
229
|
+
const userActive = wsContext.userState.isUserActive();
|
|
230
|
+
const muted = wsContext.userState.isMuted();
|
|
231
|
+
if (muted) {
|
|
232
|
+
res.json({ navigated: false, reason: "Agent actions are muted by user" });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
wsContext.broadcast({
|
|
236
|
+
type: "agent:navigate",
|
|
237
|
+
agentId,
|
|
238
|
+
section,
|
|
239
|
+
symbol,
|
|
240
|
+
loreId,
|
|
241
|
+
userActive
|
|
242
|
+
});
|
|
243
|
+
if (section) wsContext.userState.updateSection(section);
|
|
244
|
+
if (symbol) wsContext.userState.updateSelectedSymbol(symbol);
|
|
245
|
+
res.json({ navigated: true, section, symbol, userActive });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
case "highlight": {
|
|
249
|
+
const { symbols, color, duration, pulse, label } = payload;
|
|
250
|
+
const muted = wsContext.userState.isMuted();
|
|
251
|
+
if (muted) {
|
|
252
|
+
res.json({ highlighted: false, reason: "Agent actions are muted by user" });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const symbolList = symbols || [];
|
|
256
|
+
wsContext.userState.addHighlight({
|
|
257
|
+
symbols: symbolList,
|
|
258
|
+
color: color || wsContext.agentPresence.getAll().find((a) => a.agentId === agentId)?.color || "#58a6ff",
|
|
259
|
+
duration: duration || 5e3,
|
|
260
|
+
pulse: pulse ?? true,
|
|
261
|
+
label,
|
|
262
|
+
createdAt: Date.now()
|
|
263
|
+
});
|
|
264
|
+
wsContext.broadcast({
|
|
265
|
+
type: "agent:highlight",
|
|
266
|
+
agentId,
|
|
267
|
+
symbols: symbolList,
|
|
268
|
+
color: color || "#58a6ff",
|
|
269
|
+
duration: duration || 5e3,
|
|
270
|
+
pulse: pulse ?? true,
|
|
271
|
+
label
|
|
272
|
+
});
|
|
273
|
+
res.json({ highlighted: true, count: symbolList.length });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
case "annotate": {
|
|
277
|
+
const { type, message, symbol, severity, duration } = payload;
|
|
278
|
+
const muted = wsContext.userState.isMuted();
|
|
279
|
+
if (muted) {
|
|
280
|
+
res.json({ annotated: false, reason: "Agent actions are muted by user" });
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const annotation = wsContext.userState.addAnnotation({
|
|
284
|
+
type: type || "toast",
|
|
285
|
+
message: message || "",
|
|
286
|
+
symbol,
|
|
287
|
+
severity: severity || "info",
|
|
288
|
+
duration: duration || 6e3
|
|
289
|
+
});
|
|
290
|
+
wsContext.broadcast({
|
|
291
|
+
type: "agent:annotate",
|
|
292
|
+
agentId,
|
|
293
|
+
annotation
|
|
294
|
+
});
|
|
295
|
+
res.json({ annotated: true, id: annotation.id });
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
case "observe": {
|
|
299
|
+
const state = wsContext.userState.getState();
|
|
300
|
+
const agentsList = wsContext.agentPresence.getAll();
|
|
301
|
+
const connected = wsContext.clientCount() > 0;
|
|
302
|
+
res.json({
|
|
303
|
+
connected,
|
|
304
|
+
users: wsContext.clientCount(),
|
|
305
|
+
agents: agentsList,
|
|
306
|
+
state: {
|
|
307
|
+
section: state.section,
|
|
308
|
+
selectedSymbol: state.selectedSymbol,
|
|
309
|
+
theme: state.theme,
|
|
310
|
+
muted: state.muted
|
|
311
|
+
},
|
|
312
|
+
highlights: wsContext.userState.getHighlights(),
|
|
313
|
+
annotations: wsContext.userState.getAnnotations()
|
|
314
|
+
});
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
case "clear": {
|
|
318
|
+
const { target } = payload;
|
|
319
|
+
const clearTarget = target || "all";
|
|
320
|
+
if (clearTarget === "highlights" || clearTarget === "all") {
|
|
321
|
+
wsContext.userState.clearHighlights();
|
|
322
|
+
}
|
|
323
|
+
if (clearTarget === "annotations" || clearTarget === "all") {
|
|
324
|
+
wsContext.userState.clearAnnotations();
|
|
325
|
+
}
|
|
326
|
+
wsContext.broadcast({
|
|
327
|
+
type: "agent:clear",
|
|
328
|
+
agentId,
|
|
329
|
+
target: clearTarget
|
|
330
|
+
});
|
|
331
|
+
res.json({ cleared: true, target: clearTarget });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
default:
|
|
335
|
+
res.status(400).json({ error: `Unknown command: ${command}` });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
return router;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/platform-server/routes/overview.ts
|
|
343
|
+
import * as fs from "fs";
|
|
344
|
+
import * as path from "path";
|
|
345
|
+
import simpleGit from "simple-git";
|
|
346
|
+
import * as yaml from "js-yaml";
|
|
347
|
+
function readJsonSafe(filePath) {
|
|
348
|
+
try {
|
|
349
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
350
|
+
} catch {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function readYamlSafe(filePath) {
|
|
355
|
+
try {
|
|
356
|
+
return yaml.load(fs.readFileSync(filePath, "utf-8"));
|
|
357
|
+
} catch {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function getSymbolCounts(projectDir) {
|
|
362
|
+
const indexPath = path.join(projectDir, ".paradigm", "scan-index.json");
|
|
363
|
+
const data = readJsonSafe(indexPath);
|
|
364
|
+
if (!data?.symbols) return { total: 0, byType: {} };
|
|
365
|
+
const byType = {};
|
|
366
|
+
for (const sym of data.symbols) {
|
|
367
|
+
const cat = sym.category || "unknown";
|
|
368
|
+
byType[cat] = (byType[cat] || 0) + 1;
|
|
369
|
+
}
|
|
370
|
+
return { total: data.symbols.length, byType };
|
|
371
|
+
}
|
|
372
|
+
function getLoreStats(projectDir) {
|
|
373
|
+
const entriesDir = path.join(projectDir, ".paradigm", "lore", "entries");
|
|
374
|
+
if (!fs.existsSync(entriesDir)) {
|
|
375
|
+
return { total: 0, thisWeek: 0, lastEntry: null, calibrationScore: null, assessed: 0 };
|
|
376
|
+
}
|
|
377
|
+
const files = fs.readdirSync(entriesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
378
|
+
const now = Date.now();
|
|
379
|
+
const weekAgo = now - 7 * 24 * 60 * 60 * 1e3;
|
|
380
|
+
let lastTimestamp = null;
|
|
381
|
+
let thisWeek = 0;
|
|
382
|
+
let assessedCount = 0;
|
|
383
|
+
let confidenceSum = 0;
|
|
384
|
+
let deltaSum = 0;
|
|
385
|
+
let deltaCount = 0;
|
|
386
|
+
for (const file of files) {
|
|
387
|
+
try {
|
|
388
|
+
const entry = readYamlSafe(path.join(entriesDir, file));
|
|
389
|
+
if (!entry) continue;
|
|
390
|
+
const ts = entry.timestamp;
|
|
391
|
+
if (ts) {
|
|
392
|
+
const entryTime = new Date(ts).getTime();
|
|
393
|
+
if (!lastTimestamp || entryTime > new Date(lastTimestamp).getTime()) {
|
|
394
|
+
lastTimestamp = ts;
|
|
395
|
+
}
|
|
396
|
+
if (entryTime >= weekAgo) thisWeek++;
|
|
397
|
+
}
|
|
398
|
+
if (entry.assessment) {
|
|
399
|
+
assessedCount++;
|
|
400
|
+
if (typeof entry.confidence === "number" && typeof entry.assessment_delta === "number") {
|
|
401
|
+
confidenceSum += entry.confidence;
|
|
402
|
+
deltaSum += Math.abs(entry.assessment_delta);
|
|
403
|
+
deltaCount++;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const calibrationScore = deltaCount > 0 ? Math.max(0, 1 - deltaSum / deltaCount) : null;
|
|
410
|
+
return {
|
|
411
|
+
total: files.length,
|
|
412
|
+
thisWeek,
|
|
413
|
+
lastEntry: lastTimestamp,
|
|
414
|
+
calibrationScore,
|
|
415
|
+
assessed: assessedCount
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function getTaskCounts(projectDir) {
|
|
419
|
+
const tasksDir = path.join(projectDir, ".paradigm", "tasks");
|
|
420
|
+
if (!fs.existsSync(tasksDir)) return { total: 0, inProgress: 0, completed: 0 };
|
|
421
|
+
const files = fs.readdirSync(tasksDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
422
|
+
let inProgress = 0;
|
|
423
|
+
let completed = 0;
|
|
424
|
+
for (const file of files) {
|
|
425
|
+
try {
|
|
426
|
+
const task = readYamlSafe(path.join(tasksDir, file));
|
|
427
|
+
if (!task) continue;
|
|
428
|
+
const status = task.status;
|
|
429
|
+
if (status === "in-progress" || status === "in_progress") inProgress++;
|
|
430
|
+
else if (status === "completed" || status === "done") completed++;
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return { total: files.length, inProgress, completed };
|
|
435
|
+
}
|
|
436
|
+
function getPurposeCoverage(projectDir) {
|
|
437
|
+
const srcDirs = ["src", "lib", "packages"];
|
|
438
|
+
let withPurpose = 0;
|
|
439
|
+
let total = 0;
|
|
440
|
+
for (const dir of srcDirs) {
|
|
441
|
+
const fullPath = path.join(projectDir, dir);
|
|
442
|
+
if (!fs.existsSync(fullPath)) continue;
|
|
443
|
+
countPurposeCoverage(fullPath, { withPurpose: 0, total: 0 }, (stats) => {
|
|
444
|
+
withPurpose += stats.withPurpose;
|
|
445
|
+
total += stats.total;
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
if (fs.existsSync(path.join(projectDir, ".purpose"))) {
|
|
449
|
+
withPurpose++;
|
|
450
|
+
total++;
|
|
451
|
+
}
|
|
452
|
+
return total === 0 ? 1 : withPurpose / total;
|
|
453
|
+
}
|
|
454
|
+
function countPurposeCoverage(dir, _stats, callback) {
|
|
455
|
+
const stats = { withPurpose: 0, total: 0 };
|
|
456
|
+
try {
|
|
457
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
458
|
+
const hasSourceFiles = entries.some(
|
|
459
|
+
(e) => e.isFile() && /\.(ts|tsx|js|jsx|rs|py|go|swift)$/.test(e.name)
|
|
460
|
+
);
|
|
461
|
+
if (hasSourceFiles) {
|
|
462
|
+
stats.total++;
|
|
463
|
+
if (entries.some((e) => e.isFile() && e.name === ".purpose")) {
|
|
464
|
+
stats.withPurpose++;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
for (const entry of entries) {
|
|
468
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist") {
|
|
469
|
+
countPurposeCoverage(path.join(dir, entry.name), stats, (sub) => {
|
|
470
|
+
stats.withPurpose += sub.withPurpose;
|
|
471
|
+
stats.total += sub.total;
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
callback(stats);
|
|
478
|
+
}
|
|
479
|
+
function getGateCompliance(projectDir) {
|
|
480
|
+
const portalPath = path.join(projectDir, "portal.yaml");
|
|
481
|
+
if (!fs.existsSync(portalPath)) return 1;
|
|
482
|
+
const portal = readYamlSafe(portalPath);
|
|
483
|
+
if (!portal?.routes) return 1;
|
|
484
|
+
const routeCount = Object.keys(portal.routes).length;
|
|
485
|
+
return routeCount > 0 ? 1 : 0.5;
|
|
486
|
+
}
|
|
487
|
+
function getAspectAnchors(projectDir) {
|
|
488
|
+
const dbPath = path.join(projectDir, ".paradigm", "aspect-graph.db");
|
|
489
|
+
if (!fs.existsSync(dbPath)) return 1;
|
|
490
|
+
return 1;
|
|
491
|
+
}
|
|
492
|
+
function getConfigInfo(projectDir) {
|
|
493
|
+
const configPath = path.join(projectDir, ".paradigm", "config.yaml");
|
|
494
|
+
const config = readYamlSafe(configPath);
|
|
495
|
+
return {
|
|
496
|
+
name: config?.project || path.basename(projectDir),
|
|
497
|
+
discipline: config?.discipline || "general"
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
async function getRecentCommits(projectDir, limit) {
|
|
501
|
+
try {
|
|
502
|
+
const git = simpleGit(projectDir);
|
|
503
|
+
const log2 = await git.log({ maxCount: limit });
|
|
504
|
+
return log2.all.map((c) => ({
|
|
505
|
+
timestamp: c.date,
|
|
506
|
+
type: "commit",
|
|
507
|
+
summary: c.message.split("\n")[0],
|
|
508
|
+
symbol: extractFirstSymbol(c.message),
|
|
509
|
+
link: c.hash.substring(0, 7)
|
|
510
|
+
}));
|
|
511
|
+
} catch {
|
|
512
|
+
return [];
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function extractFirstSymbol(text) {
|
|
516
|
+
const match = text.match(/[#$^!~][\w-]+/);
|
|
517
|
+
return match ? match[0] : void 0;
|
|
518
|
+
}
|
|
519
|
+
function getLoreActivity(projectDir, limit) {
|
|
520
|
+
const entriesDir = path.join(projectDir, ".paradigm", "lore", "entries");
|
|
521
|
+
if (!fs.existsSync(entriesDir)) return [];
|
|
522
|
+
const files = fs.readdirSync(entriesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
523
|
+
const items = [];
|
|
524
|
+
for (const file of files) {
|
|
525
|
+
try {
|
|
526
|
+
const entry = readYamlSafe(path.join(entriesDir, file));
|
|
527
|
+
if (!entry) continue;
|
|
528
|
+
items.push({
|
|
529
|
+
timestamp: entry.timestamp || "",
|
|
530
|
+
type: "lore",
|
|
531
|
+
summary: entry.title || file,
|
|
532
|
+
symbol: Array.isArray(entry.symbols_touched) ? entry.symbols_touched[0] : void 0,
|
|
533
|
+
link: entry.id
|
|
534
|
+
});
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return items.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
|
|
539
|
+
}
|
|
540
|
+
function createOverviewHandler(projectDir) {
|
|
541
|
+
return async (_req, res) => {
|
|
542
|
+
try {
|
|
543
|
+
const configInfo = getConfigInfo(projectDir);
|
|
544
|
+
const symbolCounts = getSymbolCounts(projectDir);
|
|
545
|
+
const loreStats = getLoreStats(projectDir);
|
|
546
|
+
const taskCounts = getTaskCounts(projectDir);
|
|
547
|
+
const purposeCoverage = getPurposeCoverage(projectDir);
|
|
548
|
+
const gateCompliance = getGateCompliance(projectDir);
|
|
549
|
+
const aspectAnchors = getAspectAnchors(projectDir);
|
|
550
|
+
let branch = "unknown";
|
|
551
|
+
try {
|
|
552
|
+
const git = simpleGit(projectDir);
|
|
553
|
+
const branchInfo = await git.branch();
|
|
554
|
+
branch = branchInfo.current;
|
|
555
|
+
} catch {
|
|
556
|
+
}
|
|
557
|
+
const [commits, loreActivity] = await Promise.all([
|
|
558
|
+
getRecentCommits(projectDir, 20),
|
|
559
|
+
Promise.resolve(getLoreActivity(projectDir, 20))
|
|
560
|
+
]);
|
|
561
|
+
const recentActivity = [...commits, ...loreActivity].sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, 20);
|
|
562
|
+
const loreFreshnessDays = loreStats.lastEntry ? Math.floor((Date.now() - new Date(loreStats.lastEntry).getTime()) / (1e3 * 60 * 60 * 24)) : 0;
|
|
563
|
+
const overview = {
|
|
564
|
+
project: { name: configInfo.name, branch, discipline: configInfo.discipline },
|
|
565
|
+
symbols: symbolCounts,
|
|
566
|
+
lore: { total: loreStats.total, thisWeek: loreStats.thisWeek, lastEntry: loreStats.lastEntry },
|
|
567
|
+
calibration: { score: loreStats.calibrationScore, assessed: loreStats.assessed },
|
|
568
|
+
tasks: taskCounts,
|
|
569
|
+
health: {
|
|
570
|
+
purposeCoverage,
|
|
571
|
+
aspectAnchors,
|
|
572
|
+
gateCompliance,
|
|
573
|
+
calibration: loreStats.calibrationScore ?? 1,
|
|
574
|
+
loreFreshnessDays
|
|
575
|
+
},
|
|
576
|
+
recentActivity
|
|
577
|
+
};
|
|
578
|
+
res.json(overview);
|
|
579
|
+
} catch (err) {
|
|
580
|
+
res.status(500).json({ error: "Failed to aggregate overview", detail: String(err) });
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/platform-server/routes/git.ts
|
|
586
|
+
import { Router as Router2 } from "express";
|
|
587
|
+
import simpleGit2 from "simple-git";
|
|
588
|
+
var SYMBOL_RE = /[#$^!~][\w-]+/g;
|
|
589
|
+
function extractSymbols(text) {
|
|
590
|
+
const matches = text.match(SYMBOL_RE);
|
|
591
|
+
return matches ? [...new Set(matches)] : [];
|
|
592
|
+
}
|
|
593
|
+
function createGitRouter(projectDir) {
|
|
594
|
+
const router = Router2();
|
|
595
|
+
const git = simpleGit2(projectDir);
|
|
596
|
+
router.get("/status", async (_req, res) => {
|
|
597
|
+
try {
|
|
598
|
+
const [status, branchInfo] = await Promise.all([
|
|
599
|
+
git.status(),
|
|
600
|
+
git.branch()
|
|
601
|
+
]);
|
|
602
|
+
res.json({
|
|
603
|
+
branch: branchInfo.current,
|
|
604
|
+
ahead: status.ahead,
|
|
605
|
+
behind: status.behind,
|
|
606
|
+
staged: status.staged,
|
|
607
|
+
unstaged: status.modified.filter((f) => !status.staged.includes(f)),
|
|
608
|
+
untracked: status.not_added
|
|
609
|
+
});
|
|
610
|
+
} catch (err) {
|
|
611
|
+
res.status(500).json({ error: "Failed to get git status", detail: String(err) });
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
router.get("/branches", async (_req, res) => {
|
|
615
|
+
try {
|
|
616
|
+
const branchInfo = await git.branch();
|
|
617
|
+
const branches = Object.values(branchInfo.branches).map((b) => ({
|
|
618
|
+
name: b.name,
|
|
619
|
+
current: b.current,
|
|
620
|
+
commit: b.commit,
|
|
621
|
+
label: b.label
|
|
622
|
+
}));
|
|
623
|
+
res.json({ current: branchInfo.current, branches });
|
|
624
|
+
} catch (err) {
|
|
625
|
+
res.status(500).json({ error: "Failed to get branches", detail: String(err) });
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
router.get("/log", async (req, res) => {
|
|
629
|
+
try {
|
|
630
|
+
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
|
|
631
|
+
const offset = parseInt(req.query.offset) || 0;
|
|
632
|
+
const log2 = await git.log({
|
|
633
|
+
maxCount: limit,
|
|
634
|
+
"--skip": offset
|
|
635
|
+
});
|
|
636
|
+
const commits = log2.all.map((c) => ({
|
|
637
|
+
hash: c.hash,
|
|
638
|
+
shortHash: c.hash.substring(0, 7),
|
|
639
|
+
message: c.message,
|
|
640
|
+
author: c.author_name,
|
|
641
|
+
date: c.date,
|
|
642
|
+
symbols: extractSymbols(c.message)
|
|
643
|
+
}));
|
|
644
|
+
res.json({ commits, total: log2.total });
|
|
645
|
+
} catch (err) {
|
|
646
|
+
res.status(500).json({ error: "Failed to get git log", detail: String(err) });
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
router.get("/diff", async (req, res) => {
|
|
650
|
+
try {
|
|
651
|
+
const filePath = req.query.path;
|
|
652
|
+
const staged = req.query.staged === "true";
|
|
653
|
+
const args = [];
|
|
654
|
+
if (staged) args.push("--cached");
|
|
655
|
+
if (filePath) args.push("--", filePath);
|
|
656
|
+
const diffText = await git.diff(args);
|
|
657
|
+
res.json({ diff: diffText });
|
|
658
|
+
} catch (err) {
|
|
659
|
+
res.status(500).json({ error: "Failed to get diff", detail: String(err) });
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
router.post("/stage", async (req, res) => {
|
|
663
|
+
try {
|
|
664
|
+
const { paths } = req.body;
|
|
665
|
+
if (!paths?.length) {
|
|
666
|
+
res.status(400).json({ error: "paths is required" });
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
await git.add(paths);
|
|
670
|
+
res.json({ staged: paths });
|
|
671
|
+
} catch (err) {
|
|
672
|
+
res.status(500).json({ error: "Failed to stage files", detail: String(err) });
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
router.post("/unstage", async (req, res) => {
|
|
676
|
+
try {
|
|
677
|
+
const { paths } = req.body;
|
|
678
|
+
if (!paths?.length) {
|
|
679
|
+
res.status(400).json({ error: "paths is required" });
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
await git.reset(["HEAD", "--", ...paths]);
|
|
683
|
+
res.json({ unstaged: paths });
|
|
684
|
+
} catch (err) {
|
|
685
|
+
res.status(500).json({ error: "Failed to unstage files", detail: String(err) });
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
router.post("/commit", async (req, res) => {
|
|
689
|
+
try {
|
|
690
|
+
const { message } = req.body;
|
|
691
|
+
if (!message?.trim()) {
|
|
692
|
+
res.status(400).json({ error: "message is required" });
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const result = await git.commit(message);
|
|
696
|
+
res.json({
|
|
697
|
+
hash: result.commit,
|
|
698
|
+
summary: result.summary
|
|
699
|
+
});
|
|
700
|
+
} catch (err) {
|
|
701
|
+
res.status(500).json({ error: "Failed to commit", detail: String(err) });
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
router.post("/push", async (_req, res) => {
|
|
705
|
+
try {
|
|
706
|
+
const result = await git.push();
|
|
707
|
+
res.json({
|
|
708
|
+
pushed: true,
|
|
709
|
+
branch: result.branch,
|
|
710
|
+
remoteMessages: result.remoteMessages
|
|
711
|
+
});
|
|
712
|
+
} catch (err) {
|
|
713
|
+
res.status(500).json({ error: "Failed to push", detail: String(err) });
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
return router;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/platform-server/index.ts
|
|
720
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
721
|
+
var __dirname = path2.dirname(__filename);
|
|
722
|
+
var log = {
|
|
723
|
+
component(name) {
|
|
724
|
+
const symbol = chalk.magenta(`#${name}`);
|
|
725
|
+
return {
|
|
726
|
+
info: (msg, data) => {
|
|
727
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
728
|
+
console.log(`${chalk.blue("i")} ${symbol} ${msg}${dataStr}`);
|
|
729
|
+
},
|
|
730
|
+
success: (msg, data) => {
|
|
731
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
732
|
+
console.log(`${chalk.green("+")} ${symbol} ${msg}${dataStr}`);
|
|
733
|
+
},
|
|
734
|
+
warn: (msg, data) => {
|
|
735
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
736
|
+
console.log(`${chalk.yellow("!")} ${symbol} ${msg}${dataStr}`);
|
|
737
|
+
},
|
|
738
|
+
error: (msg, data) => {
|
|
739
|
+
const dataStr = data ? chalk.gray(` ${Object.entries(data).map(([k, v]) => `${k}=${v}`).join(" ")}`) : "";
|
|
740
|
+
console.error(`${chalk.red("x")} ${symbol} ${msg}${dataStr}`);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
function resolveSections(options) {
|
|
746
|
+
const always = ["overview", "lore", "graph", "git"];
|
|
747
|
+
const requested = options.sections ?? [...always, "sentinel", "university", "symphony"];
|
|
748
|
+
const enabled = /* @__PURE__ */ new Set();
|
|
749
|
+
for (const section of requested) {
|
|
750
|
+
if (always.includes(section)) {
|
|
751
|
+
enabled.add(section);
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (section === "sentinel") {
|
|
755
|
+
const sentinelRoutesPath = path2.join(options.projectDir, ".paradigm");
|
|
756
|
+
if (fs2.existsSync(sentinelRoutesPath)) {
|
|
757
|
+
enabled.add(section);
|
|
758
|
+
}
|
|
759
|
+
} else if (section === "university") {
|
|
760
|
+
enabled.add(section);
|
|
761
|
+
} else if (section === "symphony") {
|
|
762
|
+
const mailDir = path2.join(process.env.HOME || "~", ".paradigm", "score");
|
|
763
|
+
if (fs2.existsSync(mailDir)) {
|
|
764
|
+
enabled.add(section);
|
|
765
|
+
}
|
|
766
|
+
} else {
|
|
767
|
+
enabled.add(section);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return enabled;
|
|
771
|
+
}
|
|
772
|
+
function createPlatformApp(options) {
|
|
773
|
+
const app = express();
|
|
774
|
+
const sections = resolveSections(options);
|
|
775
|
+
app.use(express.json());
|
|
776
|
+
app.use((_req, res, next) => {
|
|
777
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
778
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
779
|
+
res.header("Access-Control-Allow-Headers", "Content-Type");
|
|
780
|
+
if (_req.method === "OPTIONS") {
|
|
781
|
+
res.sendStatus(204);
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
next();
|
|
785
|
+
});
|
|
786
|
+
app.use("/api/lore", createLoreRouter(options.projectDir));
|
|
787
|
+
app.use("/api/info", createInfoRouter(options.projectDir));
|
|
788
|
+
app.use("/api/sessions", createSessionsRouter(options.projectDir));
|
|
789
|
+
app.use("/api/symbols", createSymbolsRouter(options.projectDir));
|
|
790
|
+
app.use("/api/graphs", createGraphsRouter(options.projectDir));
|
|
791
|
+
app.get("/api/platform/overview", createOverviewHandler(options.projectDir));
|
|
792
|
+
app.use("/api/git", createGitRouter(options.projectDir));
|
|
793
|
+
app.get("/api/platform/health", (_req, res) => {
|
|
794
|
+
res.json({
|
|
795
|
+
status: "ok",
|
|
796
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
797
|
+
sections: Array.from(sections)
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
app.get("/api/platform/sections", (_req, res) => {
|
|
801
|
+
res.json({ sections: Array.from(sections) });
|
|
802
|
+
});
|
|
803
|
+
app.get("/api/health", (_req, res) => {
|
|
804
|
+
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
805
|
+
});
|
|
806
|
+
app.set("agentRouterSlot", true);
|
|
807
|
+
let uiDistPath = path2.join(__dirname, "..", "platform-ui", "dist");
|
|
808
|
+
if (!fs2.existsSync(uiDistPath)) {
|
|
809
|
+
uiDistPath = path2.join(__dirname, "..", "..", "platform-ui", "dist");
|
|
810
|
+
}
|
|
811
|
+
if (fs2.existsSync(uiDistPath)) {
|
|
812
|
+
app.use(express.static(uiDistPath));
|
|
813
|
+
app.get("{*path}", (req, res) => {
|
|
814
|
+
if (!req.path.startsWith("/api")) {
|
|
815
|
+
res.sendFile(path2.join(uiDistPath, "index.html"));
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
} else {
|
|
819
|
+
app.get("/", (_req, res) => {
|
|
820
|
+
res.send(`
|
|
821
|
+
<html>
|
|
822
|
+
<head><title>Paradigm Platform</title></head>
|
|
823
|
+
<body style="background:#0d1117;color:#e6edf3;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
|
|
824
|
+
<div style="text-align:center">
|
|
825
|
+
<h1 style="font-size:2rem;margin-bottom:8px">Paradigm Platform</h1>
|
|
826
|
+
<p style="color:#8b949e">UI not built yet. Run <code style="background:#21262d;padding:4px 8px;border-radius:4px">cd platform-ui && npx vite build</code></p>
|
|
827
|
+
<p style="color:#8b949e;margin-top:16px">APIs available:</p>
|
|
828
|
+
<p><a href="/api/lore" style="color:#58a6ff">/api/lore</a> · <a href="/api/symbols" style="color:#58a6ff">/api/symbols</a> · <a href="/api/platform/health" style="color:#58a6ff">/api/platform/health</a></p>
|
|
829
|
+
</div>
|
|
830
|
+
</body>
|
|
831
|
+
</html>
|
|
832
|
+
`);
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
return app;
|
|
836
|
+
}
|
|
837
|
+
async function startPlatformServer(options) {
|
|
838
|
+
const app = createPlatformApp(options);
|
|
839
|
+
const sections = resolveSections(options);
|
|
840
|
+
log.component("platform-server").info("Starting Paradigm Platform", { port: options.port });
|
|
841
|
+
log.component("platform-server").info("Project directory", { path: options.projectDir });
|
|
842
|
+
log.component("platform-server").info("Sections", { enabled: Array.from(sections).join(", ") });
|
|
843
|
+
const httpServer = http.createServer(app);
|
|
844
|
+
const wsContext = attachWebSocket(httpServer);
|
|
845
|
+
app.use("/api/platform/agent-command", createAgentRouter(wsContext));
|
|
846
|
+
if (sections.has("sentinel")) {
|
|
847
|
+
try {
|
|
848
|
+
const { createSentinelBridge } = await import("./sentinel-bridge-IZTXYS5M.js");
|
|
849
|
+
const sentinelRouter = await createSentinelBridge(options.projectDir, wsContext.broadcast);
|
|
850
|
+
if (sentinelRouter) {
|
|
851
|
+
app.use("/api/sentinel", sentinelRouter);
|
|
852
|
+
log.component("platform-server").success("Sentinel routes mounted");
|
|
853
|
+
}
|
|
854
|
+
} catch {
|
|
855
|
+
log.component("platform-server").warn("Sentinel not available");
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return new Promise((resolve, reject) => {
|
|
859
|
+
httpServer.listen(options.port, () => {
|
|
860
|
+
log.component("platform-server").success("Platform running", { url: `http://localhost:${options.port}` });
|
|
861
|
+
log.component("platform-ws").success("WebSocket ready", { url: `ws://localhost:${options.port}/ws` });
|
|
862
|
+
console.log("");
|
|
863
|
+
console.log(chalk.gray(" Sections:"));
|
|
864
|
+
for (const section of sections) {
|
|
865
|
+
console.log(chalk.gray(` ${chalk.cyan("\u25CF")} ${section}`));
|
|
866
|
+
}
|
|
867
|
+
console.log("");
|
|
868
|
+
if (options.open) {
|
|
869
|
+
import("open").then((openModule) => {
|
|
870
|
+
openModule.default(`http://localhost:${options.port}`);
|
|
871
|
+
log.component("platform-server").info("Opened browser");
|
|
872
|
+
}).catch(() => {
|
|
873
|
+
log.component("platform-server").warn("Could not open browser automatically");
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
resolve();
|
|
877
|
+
});
|
|
878
|
+
httpServer.on("error", (err) => {
|
|
879
|
+
if (err.code === "EADDRINUSE") {
|
|
880
|
+
log.component("platform-server").error("Port already in use", { port: options.port });
|
|
881
|
+
} else {
|
|
882
|
+
log.component("platform-server").error("Server error", { error: err.message });
|
|
883
|
+
}
|
|
884
|
+
reject(err);
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
export {
|
|
889
|
+
createPlatformApp,
|
|
890
|
+
startPlatformServer
|
|
891
|
+
};
|