@cnstra/devtools-server 1.1.2 → 2.0.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/index.cjs +171 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +21 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +171 -63
- package/dist/index.js.map +1 -1
- package/dist/start.d.ts +2 -1
- package/dist/start.d.ts.map +1 -1
- package/package.json +21 -14
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var devtoolsDto = require('@cnstra/devtools-dto');
|
|
4
|
+
var persist = require('@cnstra/persist');
|
|
4
5
|
|
|
5
6
|
// src/index.ts
|
|
6
7
|
var CNSDevToolsServer = class {
|
|
7
|
-
constructor(repository) {
|
|
8
|
+
constructor(repository, stimulationRepository = new persist.CNSInMemoryStimulationRepository()) {
|
|
8
9
|
this.repository = repository;
|
|
9
10
|
/** WebSocket → appId (for app clients) */
|
|
10
11
|
this.appSockets = /* @__PURE__ */ new Map();
|
|
@@ -14,6 +15,7 @@ var CNSDevToolsServer = class {
|
|
|
14
15
|
this.uiClients = /* @__PURE__ */ new Set();
|
|
15
16
|
this.lastCpuUsage = process.cpuUsage();
|
|
16
17
|
this.lastCpuTime = Date.now();
|
|
18
|
+
this.stimulationRepository = stimulationRepository;
|
|
17
19
|
}
|
|
18
20
|
// ─── Message entry point ─────────────────────────────────────────────────
|
|
19
21
|
async handleMessage(ws, raw) {
|
|
@@ -47,16 +49,77 @@ var CNSDevToolsServer = class {
|
|
|
47
49
|
case "topology.query":
|
|
48
50
|
await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);
|
|
49
51
|
break;
|
|
50
|
-
case "executions.query":
|
|
51
|
-
await this.handleExecutionsQuery(ws, msg);
|
|
52
|
-
break;
|
|
53
|
-
case "hops.query":
|
|
54
|
-
await this.handleHopsQuery(ws, msg);
|
|
55
|
-
break;
|
|
56
52
|
case "replay.start":
|
|
57
53
|
await this.handleReplayStart(ws, msg);
|
|
58
54
|
break;
|
|
55
|
+
case "stimulation.retry":
|
|
56
|
+
await this.handleStimulationRetry(ws, msg);
|
|
57
|
+
break;
|
|
58
|
+
case "stimulation.clone":
|
|
59
|
+
await this.handleStimulationClone(ws, msg);
|
|
60
|
+
break;
|
|
61
|
+
case "cns.stimulations.query":
|
|
62
|
+
await this.handleRunsQuery(ws, msg);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Name-based observability (Phase 2b-3): project the durable
|
|
68
|
+
* `ICNSStimulationRepository` into the run/attempt/task roster the panel
|
|
69
|
+
* renders. This replaces the legacy id-based `stimulations.query`/`hops.query`.
|
|
70
|
+
*/
|
|
71
|
+
async handleRunsQuery(ws, msg) {
|
|
72
|
+
const stims = await this.stimulationRepository.listStimulations(
|
|
73
|
+
msg.scopeName ? { scopeName: msg.scopeName } : void 0
|
|
74
|
+
);
|
|
75
|
+
const runs = [];
|
|
76
|
+
for (const stim of stims) {
|
|
77
|
+
const attempts = await this.stimulationRepository.getAttempts(
|
|
78
|
+
stim.stimulationId
|
|
79
|
+
);
|
|
80
|
+
const attemptViews = [];
|
|
81
|
+
for (const a of attempts) {
|
|
82
|
+
const tasks = await this.stimulationRepository.getTasks(
|
|
83
|
+
a.stimulationAttemptId
|
|
84
|
+
);
|
|
85
|
+
attemptViews.push({
|
|
86
|
+
attemptNumber: a.attemptNumber,
|
|
87
|
+
status: a.status,
|
|
88
|
+
hopCount: a.hopCount,
|
|
89
|
+
startedAt: a.startedAt,
|
|
90
|
+
completedAt: a.completedAt ?? null,
|
|
91
|
+
tasks: tasks.map((t) => ({
|
|
92
|
+
index: t.index,
|
|
93
|
+
neuronName: t.neuronName,
|
|
94
|
+
dendriteCollateralName: t.dendriteCollateralName,
|
|
95
|
+
status: t.status,
|
|
96
|
+
output: t.output ? {
|
|
97
|
+
collateralName: t.output.collateralName,
|
|
98
|
+
payload: t.output.payload
|
|
99
|
+
} : null,
|
|
100
|
+
error: t.error ?? null,
|
|
101
|
+
startedAt: t.startedAt,
|
|
102
|
+
duration: t.duration ?? null
|
|
103
|
+
}))
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
runs.push({
|
|
107
|
+
runId: stim.stimulationId,
|
|
108
|
+
status: stim.status,
|
|
109
|
+
scopeName: stim.scopeName,
|
|
110
|
+
entry: {
|
|
111
|
+
collateralName: stim.entry.collateralName,
|
|
112
|
+
payload: stim.entry.payload
|
|
113
|
+
},
|
|
114
|
+
frontier: stim.progress.tasks.map((t) => t.neuronName),
|
|
115
|
+
attempts: attemptViews
|
|
116
|
+
});
|
|
59
117
|
}
|
|
118
|
+
this.send(ws, {
|
|
119
|
+
type: "cns.stimulations.result",
|
|
120
|
+
requestId: msg.requestId,
|
|
121
|
+
runs
|
|
122
|
+
});
|
|
60
123
|
}
|
|
61
124
|
addClient(ws) {
|
|
62
125
|
this.uiClients.add(ws);
|
|
@@ -79,17 +142,22 @@ var CNSDevToolsServer = class {
|
|
|
79
142
|
case "topology":
|
|
80
143
|
await this.handleTopology(ws, item);
|
|
81
144
|
break;
|
|
82
|
-
|
|
83
|
-
|
|
145
|
+
// ── Name-based durable model ──
|
|
146
|
+
case "cns.stimulation":
|
|
147
|
+
await this.stimulationRepository.saveStimulation(item.data);
|
|
84
148
|
break;
|
|
85
|
-
case "
|
|
86
|
-
await this.
|
|
149
|
+
case "cns.stimulation.attempt":
|
|
150
|
+
await this.stimulationRepository.saveAttempt(item.data);
|
|
87
151
|
break;
|
|
88
|
-
case "
|
|
89
|
-
await this.
|
|
152
|
+
case "cns.stimulation.task":
|
|
153
|
+
await this.stimulationRepository.appendTask(item.data);
|
|
90
154
|
break;
|
|
91
155
|
}
|
|
92
156
|
}
|
|
157
|
+
/** The name-based durable store, for tests/integrations that want to read it back. */
|
|
158
|
+
getStimulationRepository() {
|
|
159
|
+
return this.stimulationRepository;
|
|
160
|
+
}
|
|
93
161
|
async handleTopology(ws, msg) {
|
|
94
162
|
const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;
|
|
95
163
|
const now = Date.now();
|
|
@@ -114,35 +182,6 @@ var CNSDevToolsServer = class {
|
|
|
114
182
|
this.broadcast({ type: "app.connected", app, topology: { cnsId, neurons, collaterals, dendrites } });
|
|
115
183
|
this.broadcastRaw(JSON.stringify(topologyBroadcast));
|
|
116
184
|
}
|
|
117
|
-
async handleExecutionStarted(ws, execution) {
|
|
118
|
-
const appId = this.appSockets.get(ws);
|
|
119
|
-
if (appId) {
|
|
120
|
-
const apps = await this.repository.listApps();
|
|
121
|
-
const app = apps.find((a) => a.id === appId);
|
|
122
|
-
if (app) await this.repository.upsertApp({ ...app, lastSeenAt: Date.now() });
|
|
123
|
-
}
|
|
124
|
-
await this.repository.saveExecution(execution);
|
|
125
|
-
this.broadcast({ type: "execution.started", execution });
|
|
126
|
-
}
|
|
127
|
-
async handleExecutionHop(hop) {
|
|
128
|
-
await this.repository.saveHop(hop);
|
|
129
|
-
this.broadcast({ type: "execution.hop", hop });
|
|
130
|
-
}
|
|
131
|
-
async handleExecutionCompleted(item) {
|
|
132
|
-
await this.repository.completeExecution(
|
|
133
|
-
item.executionId,
|
|
134
|
-
item.completedAt,
|
|
135
|
-
item.hopCount,
|
|
136
|
-
item.hasError
|
|
137
|
-
);
|
|
138
|
-
this.broadcast({
|
|
139
|
-
type: "execution.completed",
|
|
140
|
-
executionId: item.executionId,
|
|
141
|
-
completedAt: item.completedAt,
|
|
142
|
-
hopCount: item.hopCount,
|
|
143
|
-
hasError: item.hasError
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
185
|
// ─── UI client query handlers ─────────────────────────────────────────────
|
|
147
186
|
async handleClientConnect(ws) {
|
|
148
187
|
this.addClient(ws);
|
|
@@ -162,25 +201,6 @@ var CNSDevToolsServer = class {
|
|
|
162
201
|
if (appId) snapshots = snapshots.filter((s) => s.appId === appId);
|
|
163
202
|
this.send(ws, { type: "topology.result", requestId, snapshots });
|
|
164
203
|
}
|
|
165
|
-
async handleExecutionsQuery(ws, msg) {
|
|
166
|
-
const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : void 0);
|
|
167
|
-
if (!appId) {
|
|
168
|
-
this.send(ws, { type: "executions.result", requestId: msg.requestId, items: [], total: 0, offset: 0 });
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const { items, total } = await this.repository.getExecutions(appId, msg.filter ?? {});
|
|
172
|
-
this.send(ws, {
|
|
173
|
-
type: "executions.result",
|
|
174
|
-
requestId: msg.requestId,
|
|
175
|
-
items,
|
|
176
|
-
total,
|
|
177
|
-
offset: msg.filter?.offset ?? 0
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
async handleHopsQuery(ws, msg) {
|
|
181
|
-
const items = await this.repository.getHops(msg.executionId);
|
|
182
|
-
this.send(ws, { type: "hops.result", requestId: msg.requestId, items });
|
|
183
|
-
}
|
|
184
204
|
async handleReplayStart(ws, msg) {
|
|
185
205
|
const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : void 0);
|
|
186
206
|
const appSockets = appId ? this.appWsByAppId.get(appId) : void 0;
|
|
@@ -188,8 +208,8 @@ var CNSDevToolsServer = class {
|
|
|
188
208
|
this.send(ws, { type: "replay.rejected", replayId: msg.replayId, reason: "App not connected" });
|
|
189
209
|
return;
|
|
190
210
|
}
|
|
191
|
-
const
|
|
192
|
-
this.send(ws, { type: "replay.accepted", replayId: msg.replayId,
|
|
211
|
+
const newStimulationId = `${msg.stimulationId}-replay-${Date.now()}`;
|
|
212
|
+
this.send(ws, { type: "replay.accepted", replayId: msg.replayId, newStimulationId });
|
|
193
213
|
const payload = JSON.stringify(msg);
|
|
194
214
|
for (const appWs of appSockets) {
|
|
195
215
|
try {
|
|
@@ -198,6 +218,94 @@ var CNSDevToolsServer = class {
|
|
|
198
218
|
}
|
|
199
219
|
}
|
|
200
220
|
}
|
|
221
|
+
// ─── Durable actions (retry / clone), Phase 2b-2 ──────────────────────────────
|
|
222
|
+
//
|
|
223
|
+
// The UI's request is thin (a stimulationId). The CNS + name→ref registry live in
|
|
224
|
+
// the APP, so the server can't execute it: it ENRICHES from the durable store (the
|
|
225
|
+
// stored entry + progress + a server-assigned attempt identity) and forwards a
|
|
226
|
+
// resume/launch command to the owning app, routed by scopeName (= the cns id).
|
|
227
|
+
/** Resolve the app that owns a stimulation's scope, or reject if unreachable. */
|
|
228
|
+
async resolveScopeApp(scopeName) {
|
|
229
|
+
const appId = scopeName ? await this.repository.findAppByCns(scopeName) : void 0;
|
|
230
|
+
const sockets = appId ? this.appWsByAppId.get(appId) : void 0;
|
|
231
|
+
return sockets && sockets.size > 0 ? sockets : void 0;
|
|
232
|
+
}
|
|
233
|
+
async handleStimulationRetry(ws, msg) {
|
|
234
|
+
const reject = (reason) => this.send(ws, {
|
|
235
|
+
type: "stimulation.retry.rejected",
|
|
236
|
+
requestId: msg.requestId,
|
|
237
|
+
stimulationId: msg.stimulationId,
|
|
238
|
+
reason
|
|
239
|
+
});
|
|
240
|
+
const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);
|
|
241
|
+
if (!stim) return reject("unknown stimulation");
|
|
242
|
+
if (stim.progress.tasks.length === 0)
|
|
243
|
+
return reject("nothing to resume (empty frontier)");
|
|
244
|
+
const appSockets = await this.resolveScopeApp(stim.scopeName);
|
|
245
|
+
if (!appSockets) return reject("app not connected");
|
|
246
|
+
const attempts = await this.stimulationRepository.getAttempts(msg.stimulationId);
|
|
247
|
+
const nextAttempt = attempts.length + 1;
|
|
248
|
+
const stimulationAttemptId = `${msg.stimulationId}#${nextAttempt}`;
|
|
249
|
+
this.send(ws, {
|
|
250
|
+
type: "stimulation.retry.accepted",
|
|
251
|
+
requestId: msg.requestId,
|
|
252
|
+
stimulationId: msg.stimulationId,
|
|
253
|
+
newStimulationAttemptId: stimulationAttemptId
|
|
254
|
+
});
|
|
255
|
+
const payload = JSON.stringify({
|
|
256
|
+
type: "cns.stimulation.resume",
|
|
257
|
+
requestId: msg.requestId,
|
|
258
|
+
scopeName: stim.scopeName,
|
|
259
|
+
stimulationId: msg.stimulationId,
|
|
260
|
+
stimulationAttemptId,
|
|
261
|
+
attemptNumber: nextAttempt,
|
|
262
|
+
entry: stim.entry,
|
|
263
|
+
progress: stim.progress,
|
|
264
|
+
options: msg.options
|
|
265
|
+
});
|
|
266
|
+
for (const appWs of appSockets) {
|
|
267
|
+
try {
|
|
268
|
+
appWs.send(payload);
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async handleStimulationClone(ws, msg) {
|
|
274
|
+
const reject = (reason) => this.send(ws, {
|
|
275
|
+
type: "stimulation.clone.rejected",
|
|
276
|
+
requestId: msg.requestId,
|
|
277
|
+
stimulationId: msg.stimulationId,
|
|
278
|
+
reason
|
|
279
|
+
});
|
|
280
|
+
const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);
|
|
281
|
+
if (!stim) return reject("unknown stimulation");
|
|
282
|
+
const appSockets = await this.resolveScopeApp(stim.scopeName);
|
|
283
|
+
if (!appSockets) return reject("app not connected");
|
|
284
|
+
const newStimulationId = `${msg.stimulationId}-clone-${Date.now()}`;
|
|
285
|
+
const stimulationAttemptId = `${newStimulationId}#1`;
|
|
286
|
+
this.send(ws, {
|
|
287
|
+
type: "stimulation.clone.accepted",
|
|
288
|
+
requestId: msg.requestId,
|
|
289
|
+
stimulationId: msg.stimulationId,
|
|
290
|
+
newStimulationId,
|
|
291
|
+
newStimulationAttemptId: stimulationAttemptId
|
|
292
|
+
});
|
|
293
|
+
const payload = JSON.stringify({
|
|
294
|
+
type: "cns.stimulation.launch",
|
|
295
|
+
requestId: msg.requestId,
|
|
296
|
+
scopeName: stim.scopeName,
|
|
297
|
+
stimulationId: newStimulationId,
|
|
298
|
+
stimulationAttemptId,
|
|
299
|
+
entry: stim.entry,
|
|
300
|
+
options: msg.options
|
|
301
|
+
});
|
|
302
|
+
for (const appWs of appSockets) {
|
|
303
|
+
try {
|
|
304
|
+
appWs.send(payload);
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
201
309
|
// ─── Broadcast helpers ────────────────────────────────────────────────────
|
|
202
310
|
broadcast(message) {
|
|
203
311
|
this.broadcastRaw(JSON.stringify(message));
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["CNSDTOAppBatchMessageSchema","CNSDTOClientMessageSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n CNSDTOApp,\n CNSDTOAppBatchMessageSchema,\n CNSDTOClientMessageSchema,\n CNSDTOExecution,\n CNSDTOExecutionFilter,\n CNSDTOHop,\n CNSDTOTopologyMessage,\n} from '@cnstra/devtools-dto';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type CNSDTOTopologySnapshot = Omit<CNSDTOTopologyMessage, 'type'>;\n\nexport interface ICNSDevToolsServerRepository {\n // Apps\n upsertApp(app: CNSDTOApp): void | Promise<void>;\n listApps(): CNSDTOApp[] | Promise<CNSDTOApp[]>;\n\n // Topology\n saveTopology(snapshot: CNSDTOTopologySnapshot): void | Promise<void>;\n getTopology(cnsId?: string): CNSDTOTopologySnapshot[] | Promise<CNSDTOTopologySnapshot[]>;\n\n // Executions\n saveExecution(execution: CNSDTOExecution): void | Promise<void>;\n completeExecution(\n executionId: string,\n completedAt: number,\n hopCount: number,\n hasError: boolean\n ): void | Promise<void>;\n getExecutions(\n appId: string,\n filter: CNSDTOExecutionFilter\n ): { items: CNSDTOExecution[]; total: number } | Promise<{ items: CNSDTOExecution[]; total: number }>;\n\n // Hops\n saveHop(hop: CNSDTOHop): void | Promise<void>;\n getHops(executionId: string): CNSDTOHop[] | Promise<CNSDTOHop[]>;\n\n // CNS ↔ App mapping\n addCnsToApp(appId: string, cnsId: string): void | Promise<void>;\n getCnsByApp(appId: string): string[] | Promise<string[]>;\n findAppByCns(cnsId: string): string | undefined | Promise<string | undefined>;\n}\n\n// ─── Server ───────────────────────────────────────────────────────────────────\n\nexport class CNSDevToolsServer {\n /** WebSocket → appId (for app clients) */\n private appSockets = new Map<WebSocket, string>();\n /** appId → WebSocket[] (one app may have multiple CNS instances) */\n private appWsByAppId = new Map<string, Set<WebSocket>>();\n /** WebSocket clients that registered as UI clients */\n private uiClients = new Set<WebSocket>();\n\n private metricsTimer?: NodeJS.Timeout;\n private lastCpuUsage = process.cpuUsage();\n private lastCpuTime = Date.now();\n\n constructor(private readonly repository: ICNSDevToolsServerRepository) {}\n\n // ─── Message entry point ─────────────────────────────────────────────────\n\n async handleMessage(\n ws: WebSocket,\n raw: unknown\n ): Promise<void> {\n if (typeof raw !== 'string' && typeof raw !== 'object') return;\n\n let data: unknown;\n try {\n data = typeof raw === 'string' ? JSON.parse(raw) : raw;\n } catch {\n return;\n }\n\n if (!data || typeof data !== 'object' || !('type' in data)) return;\n const type = (data as any).type as string;\n\n // App batch (topology + execution events)\n if (type === 'batch') {\n const result = CNSDTOAppBatchMessageSchema.safeParse(data);\n if (!result.success) return;\n for (const item of result.data.items) {\n await this.handleAppBatchItem(ws, item);\n }\n return;\n }\n\n // UI client message\n const clientResult = CNSDTOClientMessageSchema.safeParse(data);\n if (!clientResult.success) return;\n const msg = clientResult.data;\n\n switch (msg.type) {\n case 'client.connect':\n await this.handleClientConnect(ws);\n break;\n case 'apps.query':\n await this.handleAppsQuery(ws, msg.requestId);\n break;\n case 'topology.query':\n await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);\n break;\n case 'executions.query':\n await this.handleExecutionsQuery(ws, msg);\n break;\n case 'hops.query':\n await this.handleHopsQuery(ws, msg);\n break;\n case 'replay.start':\n await this.handleReplayStart(ws, msg);\n break;\n }\n }\n\n addClient(ws: WebSocket): void {\n this.uiClients.add(ws);\n if (this.uiClients.size === 1) this.startMetrics();\n }\n\n removeClient(ws: WebSocket): void {\n this.uiClients.delete(ws);\n this.appSockets.delete(ws);\n if (this.uiClients.size === 0) this.stopMetrics();\n }\n\n async getActiveApps(): Promise<CNSDTOApp[]> {\n return this.repository.listApps();\n }\n\n stop(): void {\n this.stopMetrics();\n }\n\n // ─── App batch item handlers ──────────────────────────────────────────────\n\n private async handleAppBatchItem(ws: WebSocket, item: any): Promise<void> {\n switch (item.type) {\n case 'topology':\n await this.handleTopology(ws, item);\n break;\n case 'execution.started':\n await this.handleExecutionStarted(ws, item.execution);\n break;\n case 'execution.hop':\n await this.handleExecutionHop(item.hop);\n break;\n case 'execution.completed':\n await this.handleExecutionCompleted(item);\n break;\n }\n }\n\n private async handleTopology(ws: WebSocket, msg: any): Promise<void> {\n const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;\n\n const now = Date.now();\n const app: CNSDTOApp = {\n id: appId,\n name: appName,\n version,\n connectedAt: now,\n lastSeenAt: now,\n };\n\n await this.repository.upsertApp(app);\n await this.repository.addCnsToApp(appId, cnsId);\n await this.repository.saveTopology({ appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites });\n\n // Track app socket\n this.appSockets.set(ws, appId);\n if (!this.appWsByAppId.has(appId)) this.appWsByAppId.set(appId, new Set());\n this.appWsByAppId.get(appId)!.add(ws);\n\n // Broadcast topology + app connected to UI clients\n const topologyBroadcast = {\n type: 'topology.result' as const,\n requestId: '__broadcast__',\n snapshots: await this.repository.getTopology(cnsId),\n };\n\n this.broadcast({ type: 'app.connected', app, topology: { cnsId, neurons, collaterals, dendrites } });\n // Also send topology snapshot so UI can refresh graph\n this.broadcastRaw(JSON.stringify(topologyBroadcast));\n }\n\n private async handleExecutionStarted(ws: WebSocket, execution: CNSDTOExecution): Promise<void> {\n // Update lastSeenAt for the app\n const appId = this.appSockets.get(ws);\n if (appId) {\n const apps = await this.repository.listApps();\n const app = apps.find(a => a.id === appId);\n if (app) await this.repository.upsertApp({ ...app, lastSeenAt: Date.now() });\n }\n\n await this.repository.saveExecution(execution);\n this.broadcast({ type: 'execution.started', execution });\n }\n\n private async handleExecutionHop(hop: CNSDTOHop): Promise<void> {\n await this.repository.saveHop(hop);\n this.broadcast({ type: 'execution.hop', hop });\n }\n\n private async handleExecutionCompleted(item: any): Promise<void> {\n await this.repository.completeExecution(\n item.executionId,\n item.completedAt,\n item.hopCount,\n item.hasError\n );\n this.broadcast({\n type: 'execution.completed',\n executionId: item.executionId,\n completedAt: item.completedAt,\n hopCount: item.hopCount,\n hasError: item.hasError,\n });\n }\n\n // ─── UI client query handlers ─────────────────────────────────────────────\n\n private async handleClientConnect(ws: WebSocket): Promise<void> {\n this.addClient(ws);\n this.send(ws, { type: 'apps.result', requestId: '__init__', items: await this.repository.listApps() });\n this.send(ws, {\n type: 'topology.result',\n requestId: '__init__',\n snapshots: await this.repository.getTopology(),\n });\n }\n\n private async handleAppsQuery(ws: WebSocket, requestId: string): Promise<void> {\n const items = await this.repository.listApps();\n this.send(ws, { type: 'apps.result', requestId, items });\n }\n\n private async handleTopologyQuery(\n ws: WebSocket,\n requestId: string,\n cnsId?: string,\n appId?: string\n ): Promise<void> {\n let snapshots = await this.repository.getTopology(cnsId);\n if (appId) snapshots = snapshots.filter(s => s.appId === appId);\n this.send(ws, { type: 'topology.result', requestId, snapshots });\n }\n\n private async handleExecutionsQuery(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n\n if (!appId) {\n this.send(ws, { type: 'executions.result', requestId: msg.requestId, items: [], total: 0, offset: 0 });\n return;\n }\n\n const { items, total } = await this.repository.getExecutions(appId, msg.filter ?? {});\n this.send(ws, {\n type: 'executions.result',\n requestId: msg.requestId,\n items,\n total,\n offset: msg.filter?.offset ?? 0,\n });\n }\n\n private async handleHopsQuery(ws: WebSocket, msg: any): Promise<void> {\n const items = await this.repository.getHops(msg.executionId);\n this.send(ws, { type: 'hops.result', requestId: msg.requestId, items });\n }\n\n private async handleReplayStart(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n const appSockets = appId ? this.appWsByAppId.get(appId) : undefined;\n\n if (!appSockets || appSockets.size === 0) {\n this.send(ws, { type: 'replay.rejected', replayId: msg.replayId, reason: 'App not connected' });\n return;\n }\n\n const newExecutionId = `${msg.executionId}-replay-${Date.now()}`;\n this.send(ws, { type: 'replay.accepted', replayId: msg.replayId, newExecutionId });\n\n const payload = JSON.stringify(msg);\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Broadcast helpers ────────────────────────────────────────────────────\n\n private broadcast(message: object): void {\n this.broadcastRaw(JSON.stringify(message));\n }\n\n private broadcastRaw(payload: string): void {\n for (const client of this.uiClients) {\n try {\n if ((client as any).readyState === 1) client.send(payload);\n } catch {}\n }\n }\n\n private send(ws: WebSocket, message: object): void {\n try {\n if ((ws as any).readyState === 1) ws.send(JSON.stringify(message));\n } catch {}\n }\n\n // ─── Metrics ──────────────────────────────────────────────────────────────\n\n private startMetrics(): void {\n if (this.metricsTimer) return;\n this.metricsTimer = setInterval(() => {\n const mem = process.memoryUsage();\n const now = Date.now();\n const cpu = process.cpuUsage(this.lastCpuUsage);\n const elapsed = (now - this.lastCpuTime) * 1000;\n const cpuPercent = elapsed > 0 ? ((cpu.user + cpu.system) / elapsed) * 100 : 0;\n this.lastCpuUsage = process.cpuUsage();\n this.lastCpuTime = now;\n\n this.broadcast({\n type: 'server.metrics',\n timestamp: now,\n rssMB: mem.rss / 1024 / 1024,\n heapUsedMB: mem.heapUsed / 1024 / 1024,\n heapTotalMB: mem.heapTotal / 1024 / 1024,\n cpuPercent,\n });\n }, 1000);\n }\n\n private stopMetrics(): void {\n if (this.metricsTimer) {\n clearInterval(this.metricsTimer);\n this.metricsTimer = undefined;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["CNSInMemoryStimulationRepository","CNSDTOAppBatchMessageSchema","CNSDTOClientMessageSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n CNSDTOApp,\n CNSDTOAppBatchMessageSchema,\n CNSDTOClientMessageSchema,\n CNSDTOTopologyMessage,\n} from '@cnstra/devtools-dto';\nimport type { ICNSStimulationRepository } from '@cnstra/persist';\nimport { CNSInMemoryStimulationRepository } from '@cnstra/persist';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type CNSDTOTopologySnapshot = Omit<CNSDTOTopologyMessage, 'type'>;\n\nexport interface ICNSDevToolsServerRepository {\n // Apps\n upsertApp(app: CNSDTOApp): void | Promise<void>;\n listApps(): CNSDTOApp[] | Promise<CNSDTOApp[]>;\n\n // Topology\n saveTopology(snapshot: CNSDTOTopologySnapshot): void | Promise<void>;\n getTopology(cnsId?: string): CNSDTOTopologySnapshot[] | Promise<CNSDTOTopologySnapshot[]>;\n\n // CNS ↔ App mapping\n addCnsToApp(appId: string, cnsId: string): void | Promise<void>;\n getCnsByApp(appId: string): string[] | Promise<string[]>;\n findAppByCns(cnsId: string): string | undefined | Promise<string | undefined>;\n}\n\n// ─── Server ───────────────────────────────────────────────────────────────────\n\nexport class CNSDevToolsServer {\n /** WebSocket → appId (for app clients) */\n private appSockets = new Map<WebSocket, string>();\n /** appId → WebSocket[] (one app may have multiple CNS instances) */\n private appWsByAppId = new Map<string, Set<WebSocket>>();\n /** WebSocket clients that registered as UI clients */\n private uiClients = new Set<WebSocket>();\n\n private metricsTimer?: NodeJS.Timeout;\n private lastCpuUsage = process.cpuUsage();\n private lastCpuTime = Date.now();\n\n /**\n * Name-based durable store (Stimulation/Attempt/Task). Written from the three\n * `cns.stimulation*` batch items the producer emits when `trackStimulations` is on.\n * Defaults to in-memory; the example-app can inject a Postgres store.\n */\n private readonly stimulationRepository: ICNSStimulationRepository;\n\n constructor(\n private readonly repository: ICNSDevToolsServerRepository,\n stimulationRepository: ICNSStimulationRepository = new CNSInMemoryStimulationRepository()\n ) {\n this.stimulationRepository = stimulationRepository;\n }\n\n // ─── Message entry point ─────────────────────────────────────────────────\n\n async handleMessage(\n ws: WebSocket,\n raw: unknown\n ): Promise<void> {\n if (typeof raw !== 'string' && typeof raw !== 'object') return;\n\n let data: unknown;\n try {\n data = typeof raw === 'string' ? JSON.parse(raw) : raw;\n } catch {\n return;\n }\n\n if (!data || typeof data !== 'object' || !('type' in data)) return;\n const type = (data as any).type as string;\n\n // App batch (topology + stimulation events)\n if (type === 'batch') {\n const result = CNSDTOAppBatchMessageSchema.safeParse(data);\n if (!result.success) return;\n for (const item of result.data.items) {\n await this.handleAppBatchItem(ws, item);\n }\n return;\n }\n\n // UI client message\n const clientResult = CNSDTOClientMessageSchema.safeParse(data);\n if (!clientResult.success) return;\n const msg = clientResult.data;\n\n switch (msg.type) {\n case 'client.connect':\n await this.handleClientConnect(ws);\n break;\n case 'apps.query':\n await this.handleAppsQuery(ws, msg.requestId);\n break;\n case 'topology.query':\n await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);\n break;\n case 'replay.start':\n await this.handleReplayStart(ws, msg);\n break;\n case 'stimulation.retry':\n await this.handleStimulationRetry(ws, msg);\n break;\n case 'stimulation.clone':\n await this.handleStimulationClone(ws, msg);\n break;\n case 'cns.stimulations.query':\n await this.handleRunsQuery(ws, msg);\n break;\n }\n }\n\n /**\n * Name-based observability (Phase 2b-3): project the durable\n * `ICNSStimulationRepository` into the run/attempt/task roster the panel\n * renders. This replaces the legacy id-based `stimulations.query`/`hops.query`.\n */\n private async handleRunsQuery(ws: WebSocket, msg: any): Promise<void> {\n const stims = await this.stimulationRepository.listStimulations(\n msg.scopeName ? { scopeName: msg.scopeName } : undefined\n );\n const runs = [];\n for (const stim of stims) {\n const attempts = await this.stimulationRepository.getAttempts(\n stim.stimulationId\n );\n const attemptViews = [];\n for (const a of attempts) {\n const tasks = await this.stimulationRepository.getTasks(\n a.stimulationAttemptId\n );\n attemptViews.push({\n attemptNumber: a.attemptNumber,\n status: a.status,\n hopCount: a.hopCount,\n startedAt: a.startedAt,\n completedAt: a.completedAt ?? null,\n tasks: tasks.map(t => ({\n index: t.index,\n neuronName: t.neuronName,\n dendriteCollateralName: t.dendriteCollateralName,\n status: t.status,\n output: t.output\n ? {\n collateralName: t.output.collateralName,\n payload: t.output.payload,\n }\n : null,\n error: t.error ?? null,\n startedAt: t.startedAt,\n duration: t.duration ?? null,\n })),\n });\n }\n runs.push({\n runId: stim.stimulationId,\n status: stim.status,\n scopeName: stim.scopeName,\n entry: {\n collateralName: stim.entry.collateralName,\n payload: stim.entry.payload,\n },\n frontier: stim.progress.tasks.map(t => t.neuronName),\n attempts: attemptViews,\n });\n }\n this.send(ws, {\n type: 'cns.stimulations.result',\n requestId: msg.requestId,\n runs,\n });\n }\n\n addClient(ws: WebSocket): void {\n this.uiClients.add(ws);\n if (this.uiClients.size === 1) this.startMetrics();\n }\n\n removeClient(ws: WebSocket): void {\n this.uiClients.delete(ws);\n this.appSockets.delete(ws);\n if (this.uiClients.size === 0) this.stopMetrics();\n }\n\n async getActiveApps(): Promise<CNSDTOApp[]> {\n return this.repository.listApps();\n }\n\n stop(): void {\n this.stopMetrics();\n }\n\n // ─── App batch item handlers ──────────────────────────────────────────────\n\n private async handleAppBatchItem(ws: WebSocket, item: any): Promise<void> {\n switch (item.type) {\n case 'topology':\n await this.handleTopology(ws, item);\n break;\n // ── Name-based durable model ──\n case 'cns.stimulation':\n await this.stimulationRepository.saveStimulation(item.data);\n break;\n case 'cns.stimulation.attempt':\n await this.stimulationRepository.saveAttempt(item.data);\n break;\n case 'cns.stimulation.task':\n await this.stimulationRepository.appendTask(item.data);\n break;\n }\n }\n\n /** The name-based durable store, for tests/integrations that want to read it back. */\n getStimulationRepository(): ICNSStimulationRepository {\n return this.stimulationRepository;\n }\n\n private async handleTopology(ws: WebSocket, msg: any): Promise<void> {\n const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;\n\n const now = Date.now();\n const app: CNSDTOApp = {\n id: appId,\n name: appName,\n version,\n connectedAt: now,\n lastSeenAt: now,\n };\n\n await this.repository.upsertApp(app);\n await this.repository.addCnsToApp(appId, cnsId);\n await this.repository.saveTopology({ appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites });\n\n // Track app socket\n this.appSockets.set(ws, appId);\n if (!this.appWsByAppId.has(appId)) this.appWsByAppId.set(appId, new Set());\n this.appWsByAppId.get(appId)!.add(ws);\n\n // Broadcast topology + app connected to UI clients\n const topologyBroadcast = {\n type: 'topology.result' as const,\n requestId: '__broadcast__',\n snapshots: await this.repository.getTopology(cnsId),\n };\n\n this.broadcast({ type: 'app.connected', app, topology: { cnsId, neurons, collaterals, dendrites } });\n // Also send topology snapshot so UI can refresh graph\n this.broadcastRaw(JSON.stringify(topologyBroadcast));\n }\n\n // ─── UI client query handlers ─────────────────────────────────────────────\n\n private async handleClientConnect(ws: WebSocket): Promise<void> {\n this.addClient(ws);\n this.send(ws, { type: 'apps.result', requestId: '__init__', items: await this.repository.listApps() });\n this.send(ws, {\n type: 'topology.result',\n requestId: '__init__',\n snapshots: await this.repository.getTopology(),\n });\n }\n\n private async handleAppsQuery(ws: WebSocket, requestId: string): Promise<void> {\n const items = await this.repository.listApps();\n this.send(ws, { type: 'apps.result', requestId, items });\n }\n\n private async handleTopologyQuery(\n ws: WebSocket,\n requestId: string,\n cnsId?: string,\n appId?: string\n ): Promise<void> {\n let snapshots = await this.repository.getTopology(cnsId);\n if (appId) snapshots = snapshots.filter(s => s.appId === appId);\n this.send(ws, { type: 'topology.result', requestId, snapshots });\n }\n\n private async handleReplayStart(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n const appSockets = appId ? this.appWsByAppId.get(appId) : undefined;\n\n if (!appSockets || appSockets.size === 0) {\n this.send(ws, { type: 'replay.rejected', replayId: msg.replayId, reason: 'App not connected' });\n return;\n }\n\n const newStimulationId = `${msg.stimulationId}-replay-${Date.now()}`;\n this.send(ws, { type: 'replay.accepted', replayId: msg.replayId, newStimulationId });\n\n const payload = JSON.stringify(msg);\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Durable actions (retry / clone), Phase 2b-2 ──────────────────────────────\n //\n // The UI's request is thin (a stimulationId). The CNS + name→ref registry live in\n // the APP, so the server can't execute it: it ENRICHES from the durable store (the\n // stored entry + progress + a server-assigned attempt identity) and forwards a\n // resume/launch command to the owning app, routed by scopeName (= the cns id).\n\n /** Resolve the app that owns a stimulation's scope, or reject if unreachable. */\n private async resolveScopeApp(\n scopeName: string | undefined\n ): Promise<Set<WebSocket> | undefined> {\n const appId = scopeName\n ? await this.repository.findAppByCns(scopeName)\n : undefined;\n const sockets = appId ? this.appWsByAppId.get(appId) : undefined;\n return sockets && sockets.size > 0 ? sockets : undefined;\n }\n\n private async handleStimulationRetry(ws: WebSocket, msg: any): Promise<void> {\n const reject = (reason: string) =>\n this.send(ws, {\n type: 'stimulation.retry.rejected',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n reason,\n });\n\n const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);\n if (!stim) return reject('unknown stimulation');\n if (stim.progress.tasks.length === 0)\n return reject('nothing to resume (empty frontier)');\n\n const appSockets = await this.resolveScopeApp(stim.scopeName);\n if (!appSockets) return reject('app not connected');\n\n const attempts = await this.stimulationRepository.getAttempts(msg.stimulationId);\n const nextAttempt = attempts.length + 1;\n const stimulationAttemptId = `${msg.stimulationId}#${nextAttempt}`;\n\n this.send(ws, {\n type: 'stimulation.retry.accepted',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n newStimulationAttemptId: stimulationAttemptId,\n });\n\n const payload = JSON.stringify({\n type: 'cns.stimulation.resume',\n requestId: msg.requestId,\n scopeName: stim.scopeName,\n stimulationId: msg.stimulationId,\n stimulationAttemptId,\n attemptNumber: nextAttempt,\n entry: stim.entry,\n progress: stim.progress,\n options: msg.options,\n });\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n private async handleStimulationClone(ws: WebSocket, msg: any): Promise<void> {\n const reject = (reason: string) =>\n this.send(ws, {\n type: 'stimulation.clone.rejected',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n reason,\n });\n\n const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);\n if (!stim) return reject('unknown stimulation');\n\n const appSockets = await this.resolveScopeApp(stim.scopeName);\n if (!appSockets) return reject('app not connected');\n\n const newStimulationId = `${msg.stimulationId}-clone-${Date.now()}`;\n const stimulationAttemptId = `${newStimulationId}#1`;\n\n this.send(ws, {\n type: 'stimulation.clone.accepted',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n newStimulationId,\n newStimulationAttemptId: stimulationAttemptId,\n });\n\n const payload = JSON.stringify({\n type: 'cns.stimulation.launch',\n requestId: msg.requestId,\n scopeName: stim.scopeName,\n stimulationId: newStimulationId,\n stimulationAttemptId,\n entry: stim.entry,\n options: msg.options,\n });\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Broadcast helpers ────────────────────────────────────────────────────\n\n private broadcast(message: object): void {\n this.broadcastRaw(JSON.stringify(message));\n }\n\n private broadcastRaw(payload: string): void {\n for (const client of this.uiClients) {\n try {\n if ((client as any).readyState === 1) client.send(payload);\n } catch {}\n }\n }\n\n private send(ws: WebSocket, message: object): void {\n try {\n if ((ws as any).readyState === 1) ws.send(JSON.stringify(message));\n } catch {}\n }\n\n // ─── Metrics ──────────────────────────────────────────────────────────────\n\n private startMetrics(): void {\n if (this.metricsTimer) return;\n this.metricsTimer = setInterval(() => {\n const mem = process.memoryUsage();\n const now = Date.now();\n const cpu = process.cpuUsage(this.lastCpuUsage);\n const elapsed = (now - this.lastCpuTime) * 1000;\n const cpuPercent = elapsed > 0 ? ((cpu.user + cpu.system) / elapsed) * 100 : 0;\n this.lastCpuUsage = process.cpuUsage();\n this.lastCpuTime = now;\n\n this.broadcast({\n type: 'server.metrics',\n timestamp: now,\n rssMB: mem.rss / 1024 / 1024,\n heapUsedMB: mem.heapUsed / 1024 / 1024,\n heapTotalMB: mem.heapTotal / 1024 / 1024,\n cpuPercent,\n });\n }, 1000);\n }\n\n private stopMetrics(): void {\n if (this.metricsTimer) {\n clearInterval(this.metricsTimer);\n this.metricsTimer = undefined;\n }\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { CNSDTOApp,
|
|
1
|
+
import { CNSDTOApp, CNSDTOTopologyMessage } from '@cnstra/devtools-dto';
|
|
2
|
+
import type { ICNSStimulationRepository } from '@cnstra/persist';
|
|
2
3
|
import { WebSocket } from 'ws';
|
|
3
4
|
export type CNSDTOTopologySnapshot = Omit<CNSDTOTopologyMessage, 'type'>;
|
|
4
5
|
export interface ICNSDevToolsServerRepository {
|
|
@@ -6,17 +7,6 @@ export interface ICNSDevToolsServerRepository {
|
|
|
6
7
|
listApps(): CNSDTOApp[] | Promise<CNSDTOApp[]>;
|
|
7
8
|
saveTopology(snapshot: CNSDTOTopologySnapshot): void | Promise<void>;
|
|
8
9
|
getTopology(cnsId?: string): CNSDTOTopologySnapshot[] | Promise<CNSDTOTopologySnapshot[]>;
|
|
9
|
-
saveExecution(execution: CNSDTOExecution): void | Promise<void>;
|
|
10
|
-
completeExecution(executionId: string, completedAt: number, hopCount: number, hasError: boolean): void | Promise<void>;
|
|
11
|
-
getExecutions(appId: string, filter: CNSDTOExecutionFilter): {
|
|
12
|
-
items: CNSDTOExecution[];
|
|
13
|
-
total: number;
|
|
14
|
-
} | Promise<{
|
|
15
|
-
items: CNSDTOExecution[];
|
|
16
|
-
total: number;
|
|
17
|
-
}>;
|
|
18
|
-
saveHop(hop: CNSDTOHop): void | Promise<void>;
|
|
19
|
-
getHops(executionId: string): CNSDTOHop[] | Promise<CNSDTOHop[]>;
|
|
20
10
|
addCnsToApp(appId: string, cnsId: string): void | Promise<void>;
|
|
21
11
|
getCnsByApp(appId: string): string[] | Promise<string[]>;
|
|
22
12
|
findAppByCns(cnsId: string): string | undefined | Promise<string | undefined>;
|
|
@@ -32,23 +22,36 @@ export declare class CNSDevToolsServer {
|
|
|
32
22
|
private metricsTimer?;
|
|
33
23
|
private lastCpuUsage;
|
|
34
24
|
private lastCpuTime;
|
|
35
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Name-based durable store (Stimulation/Attempt/Task). Written from the three
|
|
27
|
+
* `cns.stimulation*` batch items the producer emits when `trackStimulations` is on.
|
|
28
|
+
* Defaults to in-memory; the example-app can inject a Postgres store.
|
|
29
|
+
*/
|
|
30
|
+
private readonly stimulationRepository;
|
|
31
|
+
constructor(repository: ICNSDevToolsServerRepository, stimulationRepository?: ICNSStimulationRepository);
|
|
36
32
|
handleMessage(ws: WebSocket, raw: unknown): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Name-based observability (Phase 2b-3): project the durable
|
|
35
|
+
* `ICNSStimulationRepository` into the run/attempt/task roster the panel
|
|
36
|
+
* renders. This replaces the legacy id-based `stimulations.query`/`hops.query`.
|
|
37
|
+
*/
|
|
38
|
+
private handleRunsQuery;
|
|
37
39
|
addClient(ws: WebSocket): void;
|
|
38
40
|
removeClient(ws: WebSocket): void;
|
|
39
41
|
getActiveApps(): Promise<CNSDTOApp[]>;
|
|
40
42
|
stop(): void;
|
|
41
43
|
private handleAppBatchItem;
|
|
44
|
+
/** The name-based durable store, for tests/integrations that want to read it back. */
|
|
45
|
+
getStimulationRepository(): ICNSStimulationRepository;
|
|
42
46
|
private handleTopology;
|
|
43
|
-
private handleExecutionStarted;
|
|
44
|
-
private handleExecutionHop;
|
|
45
|
-
private handleExecutionCompleted;
|
|
46
47
|
private handleClientConnect;
|
|
47
48
|
private handleAppsQuery;
|
|
48
49
|
private handleTopologyQuery;
|
|
49
|
-
private handleExecutionsQuery;
|
|
50
|
-
private handleHopsQuery;
|
|
51
50
|
private handleReplayStart;
|
|
51
|
+
/** Resolve the app that owns a stimulation's scope, or reject if unreachable. */
|
|
52
|
+
private resolveScopeApp;
|
|
53
|
+
private handleStimulationRetry;
|
|
54
|
+
private handleStimulationClone;
|
|
52
55
|
private broadcast;
|
|
53
56
|
private broadcastRaw;
|
|
54
57
|
private send;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EAGT,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EAGT,qBAAqB,EACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAI/B,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,4BAA4B;IAEzC,SAAS,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,QAAQ,IAAI,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAG/C,YAAY,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,sBAAsB,EAAE,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAG1F,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACjF;AAID,qBAAa,iBAAiB;IAoBtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAnB/B,0CAA0C;IAC1C,OAAO,CAAC,UAAU,CAAgC;IAClD,oEAAoE;IACpE,OAAO,CAAC,YAAY,CAAqC;IACzD,sDAAsD;IACtD,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAc;IAEjC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA4B;gBAG7C,UAAU,EAAE,4BAA4B,EACzD,qBAAqB,GAAE,yBAAkE;IAOvF,aAAa,CACf,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC;IAqDhB;;;;OAIG;YACW,eAAe;IAwD7B,SAAS,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAK9B,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAM3B,aAAa,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAI3C,IAAI,IAAI,IAAI;YAME,kBAAkB;IAkBhC,sFAAsF;IACtF,wBAAwB,IAAI,yBAAyB;YAIvC,cAAc;YAmCd,mBAAmB;YAUnB,eAAe;YAKf,mBAAmB;YAWnB,iBAAiB;IAyB/B,iFAAiF;YACnE,eAAe;YAUf,sBAAsB;YA4CtB,sBAAsB;IA0CpC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,WAAW;CAMtB"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CNSDTOAppBatchMessageSchema, CNSDTOClientMessageSchema } from '@cnstra/devtools-dto';
|
|
2
|
+
import { CNSInMemoryStimulationRepository } from '@cnstra/persist';
|
|
2
3
|
|
|
3
4
|
// src/index.ts
|
|
4
5
|
var CNSDevToolsServer = class {
|
|
5
|
-
constructor(repository) {
|
|
6
|
+
constructor(repository, stimulationRepository = new CNSInMemoryStimulationRepository()) {
|
|
6
7
|
this.repository = repository;
|
|
7
8
|
/** WebSocket → appId (for app clients) */
|
|
8
9
|
this.appSockets = /* @__PURE__ */ new Map();
|
|
@@ -12,6 +13,7 @@ var CNSDevToolsServer = class {
|
|
|
12
13
|
this.uiClients = /* @__PURE__ */ new Set();
|
|
13
14
|
this.lastCpuUsage = process.cpuUsage();
|
|
14
15
|
this.lastCpuTime = Date.now();
|
|
16
|
+
this.stimulationRepository = stimulationRepository;
|
|
15
17
|
}
|
|
16
18
|
// ─── Message entry point ─────────────────────────────────────────────────
|
|
17
19
|
async handleMessage(ws, raw) {
|
|
@@ -45,16 +47,77 @@ var CNSDevToolsServer = class {
|
|
|
45
47
|
case "topology.query":
|
|
46
48
|
await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);
|
|
47
49
|
break;
|
|
48
|
-
case "executions.query":
|
|
49
|
-
await this.handleExecutionsQuery(ws, msg);
|
|
50
|
-
break;
|
|
51
|
-
case "hops.query":
|
|
52
|
-
await this.handleHopsQuery(ws, msg);
|
|
53
|
-
break;
|
|
54
50
|
case "replay.start":
|
|
55
51
|
await this.handleReplayStart(ws, msg);
|
|
56
52
|
break;
|
|
53
|
+
case "stimulation.retry":
|
|
54
|
+
await this.handleStimulationRetry(ws, msg);
|
|
55
|
+
break;
|
|
56
|
+
case "stimulation.clone":
|
|
57
|
+
await this.handleStimulationClone(ws, msg);
|
|
58
|
+
break;
|
|
59
|
+
case "cns.stimulations.query":
|
|
60
|
+
await this.handleRunsQuery(ws, msg);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Name-based observability (Phase 2b-3): project the durable
|
|
66
|
+
* `ICNSStimulationRepository` into the run/attempt/task roster the panel
|
|
67
|
+
* renders. This replaces the legacy id-based `stimulations.query`/`hops.query`.
|
|
68
|
+
*/
|
|
69
|
+
async handleRunsQuery(ws, msg) {
|
|
70
|
+
const stims = await this.stimulationRepository.listStimulations(
|
|
71
|
+
msg.scopeName ? { scopeName: msg.scopeName } : void 0
|
|
72
|
+
);
|
|
73
|
+
const runs = [];
|
|
74
|
+
for (const stim of stims) {
|
|
75
|
+
const attempts = await this.stimulationRepository.getAttempts(
|
|
76
|
+
stim.stimulationId
|
|
77
|
+
);
|
|
78
|
+
const attemptViews = [];
|
|
79
|
+
for (const a of attempts) {
|
|
80
|
+
const tasks = await this.stimulationRepository.getTasks(
|
|
81
|
+
a.stimulationAttemptId
|
|
82
|
+
);
|
|
83
|
+
attemptViews.push({
|
|
84
|
+
attemptNumber: a.attemptNumber,
|
|
85
|
+
status: a.status,
|
|
86
|
+
hopCount: a.hopCount,
|
|
87
|
+
startedAt: a.startedAt,
|
|
88
|
+
completedAt: a.completedAt ?? null,
|
|
89
|
+
tasks: tasks.map((t) => ({
|
|
90
|
+
index: t.index,
|
|
91
|
+
neuronName: t.neuronName,
|
|
92
|
+
dendriteCollateralName: t.dendriteCollateralName,
|
|
93
|
+
status: t.status,
|
|
94
|
+
output: t.output ? {
|
|
95
|
+
collateralName: t.output.collateralName,
|
|
96
|
+
payload: t.output.payload
|
|
97
|
+
} : null,
|
|
98
|
+
error: t.error ?? null,
|
|
99
|
+
startedAt: t.startedAt,
|
|
100
|
+
duration: t.duration ?? null
|
|
101
|
+
}))
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
runs.push({
|
|
105
|
+
runId: stim.stimulationId,
|
|
106
|
+
status: stim.status,
|
|
107
|
+
scopeName: stim.scopeName,
|
|
108
|
+
entry: {
|
|
109
|
+
collateralName: stim.entry.collateralName,
|
|
110
|
+
payload: stim.entry.payload
|
|
111
|
+
},
|
|
112
|
+
frontier: stim.progress.tasks.map((t) => t.neuronName),
|
|
113
|
+
attempts: attemptViews
|
|
114
|
+
});
|
|
57
115
|
}
|
|
116
|
+
this.send(ws, {
|
|
117
|
+
type: "cns.stimulations.result",
|
|
118
|
+
requestId: msg.requestId,
|
|
119
|
+
runs
|
|
120
|
+
});
|
|
58
121
|
}
|
|
59
122
|
addClient(ws) {
|
|
60
123
|
this.uiClients.add(ws);
|
|
@@ -77,17 +140,22 @@ var CNSDevToolsServer = class {
|
|
|
77
140
|
case "topology":
|
|
78
141
|
await this.handleTopology(ws, item);
|
|
79
142
|
break;
|
|
80
|
-
|
|
81
|
-
|
|
143
|
+
// ── Name-based durable model ──
|
|
144
|
+
case "cns.stimulation":
|
|
145
|
+
await this.stimulationRepository.saveStimulation(item.data);
|
|
82
146
|
break;
|
|
83
|
-
case "
|
|
84
|
-
await this.
|
|
147
|
+
case "cns.stimulation.attempt":
|
|
148
|
+
await this.stimulationRepository.saveAttempt(item.data);
|
|
85
149
|
break;
|
|
86
|
-
case "
|
|
87
|
-
await this.
|
|
150
|
+
case "cns.stimulation.task":
|
|
151
|
+
await this.stimulationRepository.appendTask(item.data);
|
|
88
152
|
break;
|
|
89
153
|
}
|
|
90
154
|
}
|
|
155
|
+
/** The name-based durable store, for tests/integrations that want to read it back. */
|
|
156
|
+
getStimulationRepository() {
|
|
157
|
+
return this.stimulationRepository;
|
|
158
|
+
}
|
|
91
159
|
async handleTopology(ws, msg) {
|
|
92
160
|
const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;
|
|
93
161
|
const now = Date.now();
|
|
@@ -112,35 +180,6 @@ var CNSDevToolsServer = class {
|
|
|
112
180
|
this.broadcast({ type: "app.connected", app, topology: { cnsId, neurons, collaterals, dendrites } });
|
|
113
181
|
this.broadcastRaw(JSON.stringify(topologyBroadcast));
|
|
114
182
|
}
|
|
115
|
-
async handleExecutionStarted(ws, execution) {
|
|
116
|
-
const appId = this.appSockets.get(ws);
|
|
117
|
-
if (appId) {
|
|
118
|
-
const apps = await this.repository.listApps();
|
|
119
|
-
const app = apps.find((a) => a.id === appId);
|
|
120
|
-
if (app) await this.repository.upsertApp({ ...app, lastSeenAt: Date.now() });
|
|
121
|
-
}
|
|
122
|
-
await this.repository.saveExecution(execution);
|
|
123
|
-
this.broadcast({ type: "execution.started", execution });
|
|
124
|
-
}
|
|
125
|
-
async handleExecutionHop(hop) {
|
|
126
|
-
await this.repository.saveHop(hop);
|
|
127
|
-
this.broadcast({ type: "execution.hop", hop });
|
|
128
|
-
}
|
|
129
|
-
async handleExecutionCompleted(item) {
|
|
130
|
-
await this.repository.completeExecution(
|
|
131
|
-
item.executionId,
|
|
132
|
-
item.completedAt,
|
|
133
|
-
item.hopCount,
|
|
134
|
-
item.hasError
|
|
135
|
-
);
|
|
136
|
-
this.broadcast({
|
|
137
|
-
type: "execution.completed",
|
|
138
|
-
executionId: item.executionId,
|
|
139
|
-
completedAt: item.completedAt,
|
|
140
|
-
hopCount: item.hopCount,
|
|
141
|
-
hasError: item.hasError
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
183
|
// ─── UI client query handlers ─────────────────────────────────────────────
|
|
145
184
|
async handleClientConnect(ws) {
|
|
146
185
|
this.addClient(ws);
|
|
@@ -160,25 +199,6 @@ var CNSDevToolsServer = class {
|
|
|
160
199
|
if (appId) snapshots = snapshots.filter((s) => s.appId === appId);
|
|
161
200
|
this.send(ws, { type: "topology.result", requestId, snapshots });
|
|
162
201
|
}
|
|
163
|
-
async handleExecutionsQuery(ws, msg) {
|
|
164
|
-
const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : void 0);
|
|
165
|
-
if (!appId) {
|
|
166
|
-
this.send(ws, { type: "executions.result", requestId: msg.requestId, items: [], total: 0, offset: 0 });
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
const { items, total } = await this.repository.getExecutions(appId, msg.filter ?? {});
|
|
170
|
-
this.send(ws, {
|
|
171
|
-
type: "executions.result",
|
|
172
|
-
requestId: msg.requestId,
|
|
173
|
-
items,
|
|
174
|
-
total,
|
|
175
|
-
offset: msg.filter?.offset ?? 0
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
async handleHopsQuery(ws, msg) {
|
|
179
|
-
const items = await this.repository.getHops(msg.executionId);
|
|
180
|
-
this.send(ws, { type: "hops.result", requestId: msg.requestId, items });
|
|
181
|
-
}
|
|
182
202
|
async handleReplayStart(ws, msg) {
|
|
183
203
|
const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : void 0);
|
|
184
204
|
const appSockets = appId ? this.appWsByAppId.get(appId) : void 0;
|
|
@@ -186,8 +206,8 @@ var CNSDevToolsServer = class {
|
|
|
186
206
|
this.send(ws, { type: "replay.rejected", replayId: msg.replayId, reason: "App not connected" });
|
|
187
207
|
return;
|
|
188
208
|
}
|
|
189
|
-
const
|
|
190
|
-
this.send(ws, { type: "replay.accepted", replayId: msg.replayId,
|
|
209
|
+
const newStimulationId = `${msg.stimulationId}-replay-${Date.now()}`;
|
|
210
|
+
this.send(ws, { type: "replay.accepted", replayId: msg.replayId, newStimulationId });
|
|
191
211
|
const payload = JSON.stringify(msg);
|
|
192
212
|
for (const appWs of appSockets) {
|
|
193
213
|
try {
|
|
@@ -196,6 +216,94 @@ var CNSDevToolsServer = class {
|
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
218
|
}
|
|
219
|
+
// ─── Durable actions (retry / clone), Phase 2b-2 ──────────────────────────────
|
|
220
|
+
//
|
|
221
|
+
// The UI's request is thin (a stimulationId). The CNS + name→ref registry live in
|
|
222
|
+
// the APP, so the server can't execute it: it ENRICHES from the durable store (the
|
|
223
|
+
// stored entry + progress + a server-assigned attempt identity) and forwards a
|
|
224
|
+
// resume/launch command to the owning app, routed by scopeName (= the cns id).
|
|
225
|
+
/** Resolve the app that owns a stimulation's scope, or reject if unreachable. */
|
|
226
|
+
async resolveScopeApp(scopeName) {
|
|
227
|
+
const appId = scopeName ? await this.repository.findAppByCns(scopeName) : void 0;
|
|
228
|
+
const sockets = appId ? this.appWsByAppId.get(appId) : void 0;
|
|
229
|
+
return sockets && sockets.size > 0 ? sockets : void 0;
|
|
230
|
+
}
|
|
231
|
+
async handleStimulationRetry(ws, msg) {
|
|
232
|
+
const reject = (reason) => this.send(ws, {
|
|
233
|
+
type: "stimulation.retry.rejected",
|
|
234
|
+
requestId: msg.requestId,
|
|
235
|
+
stimulationId: msg.stimulationId,
|
|
236
|
+
reason
|
|
237
|
+
});
|
|
238
|
+
const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);
|
|
239
|
+
if (!stim) return reject("unknown stimulation");
|
|
240
|
+
if (stim.progress.tasks.length === 0)
|
|
241
|
+
return reject("nothing to resume (empty frontier)");
|
|
242
|
+
const appSockets = await this.resolveScopeApp(stim.scopeName);
|
|
243
|
+
if (!appSockets) return reject("app not connected");
|
|
244
|
+
const attempts = await this.stimulationRepository.getAttempts(msg.stimulationId);
|
|
245
|
+
const nextAttempt = attempts.length + 1;
|
|
246
|
+
const stimulationAttemptId = `${msg.stimulationId}#${nextAttempt}`;
|
|
247
|
+
this.send(ws, {
|
|
248
|
+
type: "stimulation.retry.accepted",
|
|
249
|
+
requestId: msg.requestId,
|
|
250
|
+
stimulationId: msg.stimulationId,
|
|
251
|
+
newStimulationAttemptId: stimulationAttemptId
|
|
252
|
+
});
|
|
253
|
+
const payload = JSON.stringify({
|
|
254
|
+
type: "cns.stimulation.resume",
|
|
255
|
+
requestId: msg.requestId,
|
|
256
|
+
scopeName: stim.scopeName,
|
|
257
|
+
stimulationId: msg.stimulationId,
|
|
258
|
+
stimulationAttemptId,
|
|
259
|
+
attemptNumber: nextAttempt,
|
|
260
|
+
entry: stim.entry,
|
|
261
|
+
progress: stim.progress,
|
|
262
|
+
options: msg.options
|
|
263
|
+
});
|
|
264
|
+
for (const appWs of appSockets) {
|
|
265
|
+
try {
|
|
266
|
+
appWs.send(payload);
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async handleStimulationClone(ws, msg) {
|
|
272
|
+
const reject = (reason) => this.send(ws, {
|
|
273
|
+
type: "stimulation.clone.rejected",
|
|
274
|
+
requestId: msg.requestId,
|
|
275
|
+
stimulationId: msg.stimulationId,
|
|
276
|
+
reason
|
|
277
|
+
});
|
|
278
|
+
const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);
|
|
279
|
+
if (!stim) return reject("unknown stimulation");
|
|
280
|
+
const appSockets = await this.resolveScopeApp(stim.scopeName);
|
|
281
|
+
if (!appSockets) return reject("app not connected");
|
|
282
|
+
const newStimulationId = `${msg.stimulationId}-clone-${Date.now()}`;
|
|
283
|
+
const stimulationAttemptId = `${newStimulationId}#1`;
|
|
284
|
+
this.send(ws, {
|
|
285
|
+
type: "stimulation.clone.accepted",
|
|
286
|
+
requestId: msg.requestId,
|
|
287
|
+
stimulationId: msg.stimulationId,
|
|
288
|
+
newStimulationId,
|
|
289
|
+
newStimulationAttemptId: stimulationAttemptId
|
|
290
|
+
});
|
|
291
|
+
const payload = JSON.stringify({
|
|
292
|
+
type: "cns.stimulation.launch",
|
|
293
|
+
requestId: msg.requestId,
|
|
294
|
+
scopeName: stim.scopeName,
|
|
295
|
+
stimulationId: newStimulationId,
|
|
296
|
+
stimulationAttemptId,
|
|
297
|
+
entry: stim.entry,
|
|
298
|
+
options: msg.options
|
|
299
|
+
});
|
|
300
|
+
for (const appWs of appSockets) {
|
|
301
|
+
try {
|
|
302
|
+
appWs.send(payload);
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
199
307
|
// ─── Broadcast helpers ────────────────────────────────────────────────────
|
|
200
308
|
broadcast(message) {
|
|
201
309
|
this.broadcastRaw(JSON.stringify(message));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n CNSDTOApp,\n CNSDTOAppBatchMessageSchema,\n CNSDTOClientMessageSchema,\n CNSDTOExecution,\n CNSDTOExecutionFilter,\n CNSDTOHop,\n CNSDTOTopologyMessage,\n} from '@cnstra/devtools-dto';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type CNSDTOTopologySnapshot = Omit<CNSDTOTopologyMessage, 'type'>;\n\nexport interface ICNSDevToolsServerRepository {\n // Apps\n upsertApp(app: CNSDTOApp): void | Promise<void>;\n listApps(): CNSDTOApp[] | Promise<CNSDTOApp[]>;\n\n // Topology\n saveTopology(snapshot: CNSDTOTopologySnapshot): void | Promise<void>;\n getTopology(cnsId?: string): CNSDTOTopologySnapshot[] | Promise<CNSDTOTopologySnapshot[]>;\n\n // Executions\n saveExecution(execution: CNSDTOExecution): void | Promise<void>;\n completeExecution(\n executionId: string,\n completedAt: number,\n hopCount: number,\n hasError: boolean\n ): void | Promise<void>;\n getExecutions(\n appId: string,\n filter: CNSDTOExecutionFilter\n ): { items: CNSDTOExecution[]; total: number } | Promise<{ items: CNSDTOExecution[]; total: number }>;\n\n // Hops\n saveHop(hop: CNSDTOHop): void | Promise<void>;\n getHops(executionId: string): CNSDTOHop[] | Promise<CNSDTOHop[]>;\n\n // CNS ↔ App mapping\n addCnsToApp(appId: string, cnsId: string): void | Promise<void>;\n getCnsByApp(appId: string): string[] | Promise<string[]>;\n findAppByCns(cnsId: string): string | undefined | Promise<string | undefined>;\n}\n\n// ─── Server ───────────────────────────────────────────────────────────────────\n\nexport class CNSDevToolsServer {\n /** WebSocket → appId (for app clients) */\n private appSockets = new Map<WebSocket, string>();\n /** appId → WebSocket[] (one app may have multiple CNS instances) */\n private appWsByAppId = new Map<string, Set<WebSocket>>();\n /** WebSocket clients that registered as UI clients */\n private uiClients = new Set<WebSocket>();\n\n private metricsTimer?: NodeJS.Timeout;\n private lastCpuUsage = process.cpuUsage();\n private lastCpuTime = Date.now();\n\n constructor(private readonly repository: ICNSDevToolsServerRepository) {}\n\n // ─── Message entry point ─────────────────────────────────────────────────\n\n async handleMessage(\n ws: WebSocket,\n raw: unknown\n ): Promise<void> {\n if (typeof raw !== 'string' && typeof raw !== 'object') return;\n\n let data: unknown;\n try {\n data = typeof raw === 'string' ? JSON.parse(raw) : raw;\n } catch {\n return;\n }\n\n if (!data || typeof data !== 'object' || !('type' in data)) return;\n const type = (data as any).type as string;\n\n // App batch (topology + execution events)\n if (type === 'batch') {\n const result = CNSDTOAppBatchMessageSchema.safeParse(data);\n if (!result.success) return;\n for (const item of result.data.items) {\n await this.handleAppBatchItem(ws, item);\n }\n return;\n }\n\n // UI client message\n const clientResult = CNSDTOClientMessageSchema.safeParse(data);\n if (!clientResult.success) return;\n const msg = clientResult.data;\n\n switch (msg.type) {\n case 'client.connect':\n await this.handleClientConnect(ws);\n break;\n case 'apps.query':\n await this.handleAppsQuery(ws, msg.requestId);\n break;\n case 'topology.query':\n await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);\n break;\n case 'executions.query':\n await this.handleExecutionsQuery(ws, msg);\n break;\n case 'hops.query':\n await this.handleHopsQuery(ws, msg);\n break;\n case 'replay.start':\n await this.handleReplayStart(ws, msg);\n break;\n }\n }\n\n addClient(ws: WebSocket): void {\n this.uiClients.add(ws);\n if (this.uiClients.size === 1) this.startMetrics();\n }\n\n removeClient(ws: WebSocket): void {\n this.uiClients.delete(ws);\n this.appSockets.delete(ws);\n if (this.uiClients.size === 0) this.stopMetrics();\n }\n\n async getActiveApps(): Promise<CNSDTOApp[]> {\n return this.repository.listApps();\n }\n\n stop(): void {\n this.stopMetrics();\n }\n\n // ─── App batch item handlers ──────────────────────────────────────────────\n\n private async handleAppBatchItem(ws: WebSocket, item: any): Promise<void> {\n switch (item.type) {\n case 'topology':\n await this.handleTopology(ws, item);\n break;\n case 'execution.started':\n await this.handleExecutionStarted(ws, item.execution);\n break;\n case 'execution.hop':\n await this.handleExecutionHop(item.hop);\n break;\n case 'execution.completed':\n await this.handleExecutionCompleted(item);\n break;\n }\n }\n\n private async handleTopology(ws: WebSocket, msg: any): Promise<void> {\n const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;\n\n const now = Date.now();\n const app: CNSDTOApp = {\n id: appId,\n name: appName,\n version,\n connectedAt: now,\n lastSeenAt: now,\n };\n\n await this.repository.upsertApp(app);\n await this.repository.addCnsToApp(appId, cnsId);\n await this.repository.saveTopology({ appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites });\n\n // Track app socket\n this.appSockets.set(ws, appId);\n if (!this.appWsByAppId.has(appId)) this.appWsByAppId.set(appId, new Set());\n this.appWsByAppId.get(appId)!.add(ws);\n\n // Broadcast topology + app connected to UI clients\n const topologyBroadcast = {\n type: 'topology.result' as const,\n requestId: '__broadcast__',\n snapshots: await this.repository.getTopology(cnsId),\n };\n\n this.broadcast({ type: 'app.connected', app, topology: { cnsId, neurons, collaterals, dendrites } });\n // Also send topology snapshot so UI can refresh graph\n this.broadcastRaw(JSON.stringify(topologyBroadcast));\n }\n\n private async handleExecutionStarted(ws: WebSocket, execution: CNSDTOExecution): Promise<void> {\n // Update lastSeenAt for the app\n const appId = this.appSockets.get(ws);\n if (appId) {\n const apps = await this.repository.listApps();\n const app = apps.find(a => a.id === appId);\n if (app) await this.repository.upsertApp({ ...app, lastSeenAt: Date.now() });\n }\n\n await this.repository.saveExecution(execution);\n this.broadcast({ type: 'execution.started', execution });\n }\n\n private async handleExecutionHop(hop: CNSDTOHop): Promise<void> {\n await this.repository.saveHop(hop);\n this.broadcast({ type: 'execution.hop', hop });\n }\n\n private async handleExecutionCompleted(item: any): Promise<void> {\n await this.repository.completeExecution(\n item.executionId,\n item.completedAt,\n item.hopCount,\n item.hasError\n );\n this.broadcast({\n type: 'execution.completed',\n executionId: item.executionId,\n completedAt: item.completedAt,\n hopCount: item.hopCount,\n hasError: item.hasError,\n });\n }\n\n // ─── UI client query handlers ─────────────────────────────────────────────\n\n private async handleClientConnect(ws: WebSocket): Promise<void> {\n this.addClient(ws);\n this.send(ws, { type: 'apps.result', requestId: '__init__', items: await this.repository.listApps() });\n this.send(ws, {\n type: 'topology.result',\n requestId: '__init__',\n snapshots: await this.repository.getTopology(),\n });\n }\n\n private async handleAppsQuery(ws: WebSocket, requestId: string): Promise<void> {\n const items = await this.repository.listApps();\n this.send(ws, { type: 'apps.result', requestId, items });\n }\n\n private async handleTopologyQuery(\n ws: WebSocket,\n requestId: string,\n cnsId?: string,\n appId?: string\n ): Promise<void> {\n let snapshots = await this.repository.getTopology(cnsId);\n if (appId) snapshots = snapshots.filter(s => s.appId === appId);\n this.send(ws, { type: 'topology.result', requestId, snapshots });\n }\n\n private async handleExecutionsQuery(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n\n if (!appId) {\n this.send(ws, { type: 'executions.result', requestId: msg.requestId, items: [], total: 0, offset: 0 });\n return;\n }\n\n const { items, total } = await this.repository.getExecutions(appId, msg.filter ?? {});\n this.send(ws, {\n type: 'executions.result',\n requestId: msg.requestId,\n items,\n total,\n offset: msg.filter?.offset ?? 0,\n });\n }\n\n private async handleHopsQuery(ws: WebSocket, msg: any): Promise<void> {\n const items = await this.repository.getHops(msg.executionId);\n this.send(ws, { type: 'hops.result', requestId: msg.requestId, items });\n }\n\n private async handleReplayStart(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n const appSockets = appId ? this.appWsByAppId.get(appId) : undefined;\n\n if (!appSockets || appSockets.size === 0) {\n this.send(ws, { type: 'replay.rejected', replayId: msg.replayId, reason: 'App not connected' });\n return;\n }\n\n const newExecutionId = `${msg.executionId}-replay-${Date.now()}`;\n this.send(ws, { type: 'replay.accepted', replayId: msg.replayId, newExecutionId });\n\n const payload = JSON.stringify(msg);\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Broadcast helpers ────────────────────────────────────────────────────\n\n private broadcast(message: object): void {\n this.broadcastRaw(JSON.stringify(message));\n }\n\n private broadcastRaw(payload: string): void {\n for (const client of this.uiClients) {\n try {\n if ((client as any).readyState === 1) client.send(payload);\n } catch {}\n }\n }\n\n private send(ws: WebSocket, message: object): void {\n try {\n if ((ws as any).readyState === 1) ws.send(JSON.stringify(message));\n } catch {}\n }\n\n // ─── Metrics ──────────────────────────────────────────────────────────────\n\n private startMetrics(): void {\n if (this.metricsTimer) return;\n this.metricsTimer = setInterval(() => {\n const mem = process.memoryUsage();\n const now = Date.now();\n const cpu = process.cpuUsage(this.lastCpuUsage);\n const elapsed = (now - this.lastCpuTime) * 1000;\n const cpuPercent = elapsed > 0 ? ((cpu.user + cpu.system) / elapsed) * 100 : 0;\n this.lastCpuUsage = process.cpuUsage();\n this.lastCpuTime = now;\n\n this.broadcast({\n type: 'server.metrics',\n timestamp: now,\n rssMB: mem.rss / 1024 / 1024,\n heapUsedMB: mem.heapUsed / 1024 / 1024,\n heapTotalMB: mem.heapTotal / 1024 / 1024,\n cpuPercent,\n });\n }, 1000);\n }\n\n private stopMetrics(): void {\n if (this.metricsTimer) {\n clearInterval(this.metricsTimer);\n this.metricsTimer = undefined;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n CNSDTOApp,\n CNSDTOAppBatchMessageSchema,\n CNSDTOClientMessageSchema,\n CNSDTOTopologyMessage,\n} from '@cnstra/devtools-dto';\nimport type { ICNSStimulationRepository } from '@cnstra/persist';\nimport { CNSInMemoryStimulationRepository } from '@cnstra/persist';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type CNSDTOTopologySnapshot = Omit<CNSDTOTopologyMessage, 'type'>;\n\nexport interface ICNSDevToolsServerRepository {\n // Apps\n upsertApp(app: CNSDTOApp): void | Promise<void>;\n listApps(): CNSDTOApp[] | Promise<CNSDTOApp[]>;\n\n // Topology\n saveTopology(snapshot: CNSDTOTopologySnapshot): void | Promise<void>;\n getTopology(cnsId?: string): CNSDTOTopologySnapshot[] | Promise<CNSDTOTopologySnapshot[]>;\n\n // CNS ↔ App mapping\n addCnsToApp(appId: string, cnsId: string): void | Promise<void>;\n getCnsByApp(appId: string): string[] | Promise<string[]>;\n findAppByCns(cnsId: string): string | undefined | Promise<string | undefined>;\n}\n\n// ─── Server ───────────────────────────────────────────────────────────────────\n\nexport class CNSDevToolsServer {\n /** WebSocket → appId (for app clients) */\n private appSockets = new Map<WebSocket, string>();\n /** appId → WebSocket[] (one app may have multiple CNS instances) */\n private appWsByAppId = new Map<string, Set<WebSocket>>();\n /** WebSocket clients that registered as UI clients */\n private uiClients = new Set<WebSocket>();\n\n private metricsTimer?: NodeJS.Timeout;\n private lastCpuUsage = process.cpuUsage();\n private lastCpuTime = Date.now();\n\n /**\n * Name-based durable store (Stimulation/Attempt/Task). Written from the three\n * `cns.stimulation*` batch items the producer emits when `trackStimulations` is on.\n * Defaults to in-memory; the example-app can inject a Postgres store.\n */\n private readonly stimulationRepository: ICNSStimulationRepository;\n\n constructor(\n private readonly repository: ICNSDevToolsServerRepository,\n stimulationRepository: ICNSStimulationRepository = new CNSInMemoryStimulationRepository()\n ) {\n this.stimulationRepository = stimulationRepository;\n }\n\n // ─── Message entry point ─────────────────────────────────────────────────\n\n async handleMessage(\n ws: WebSocket,\n raw: unknown\n ): Promise<void> {\n if (typeof raw !== 'string' && typeof raw !== 'object') return;\n\n let data: unknown;\n try {\n data = typeof raw === 'string' ? JSON.parse(raw) : raw;\n } catch {\n return;\n }\n\n if (!data || typeof data !== 'object' || !('type' in data)) return;\n const type = (data as any).type as string;\n\n // App batch (topology + stimulation events)\n if (type === 'batch') {\n const result = CNSDTOAppBatchMessageSchema.safeParse(data);\n if (!result.success) return;\n for (const item of result.data.items) {\n await this.handleAppBatchItem(ws, item);\n }\n return;\n }\n\n // UI client message\n const clientResult = CNSDTOClientMessageSchema.safeParse(data);\n if (!clientResult.success) return;\n const msg = clientResult.data;\n\n switch (msg.type) {\n case 'client.connect':\n await this.handleClientConnect(ws);\n break;\n case 'apps.query':\n await this.handleAppsQuery(ws, msg.requestId);\n break;\n case 'topology.query':\n await this.handleTopologyQuery(ws, msg.requestId, msg.cnsId, msg.appId);\n break;\n case 'replay.start':\n await this.handleReplayStart(ws, msg);\n break;\n case 'stimulation.retry':\n await this.handleStimulationRetry(ws, msg);\n break;\n case 'stimulation.clone':\n await this.handleStimulationClone(ws, msg);\n break;\n case 'cns.stimulations.query':\n await this.handleRunsQuery(ws, msg);\n break;\n }\n }\n\n /**\n * Name-based observability (Phase 2b-3): project the durable\n * `ICNSStimulationRepository` into the run/attempt/task roster the panel\n * renders. This replaces the legacy id-based `stimulations.query`/`hops.query`.\n */\n private async handleRunsQuery(ws: WebSocket, msg: any): Promise<void> {\n const stims = await this.stimulationRepository.listStimulations(\n msg.scopeName ? { scopeName: msg.scopeName } : undefined\n );\n const runs = [];\n for (const stim of stims) {\n const attempts = await this.stimulationRepository.getAttempts(\n stim.stimulationId\n );\n const attemptViews = [];\n for (const a of attempts) {\n const tasks = await this.stimulationRepository.getTasks(\n a.stimulationAttemptId\n );\n attemptViews.push({\n attemptNumber: a.attemptNumber,\n status: a.status,\n hopCount: a.hopCount,\n startedAt: a.startedAt,\n completedAt: a.completedAt ?? null,\n tasks: tasks.map(t => ({\n index: t.index,\n neuronName: t.neuronName,\n dendriteCollateralName: t.dendriteCollateralName,\n status: t.status,\n output: t.output\n ? {\n collateralName: t.output.collateralName,\n payload: t.output.payload,\n }\n : null,\n error: t.error ?? null,\n startedAt: t.startedAt,\n duration: t.duration ?? null,\n })),\n });\n }\n runs.push({\n runId: stim.stimulationId,\n status: stim.status,\n scopeName: stim.scopeName,\n entry: {\n collateralName: stim.entry.collateralName,\n payload: stim.entry.payload,\n },\n frontier: stim.progress.tasks.map(t => t.neuronName),\n attempts: attemptViews,\n });\n }\n this.send(ws, {\n type: 'cns.stimulations.result',\n requestId: msg.requestId,\n runs,\n });\n }\n\n addClient(ws: WebSocket): void {\n this.uiClients.add(ws);\n if (this.uiClients.size === 1) this.startMetrics();\n }\n\n removeClient(ws: WebSocket): void {\n this.uiClients.delete(ws);\n this.appSockets.delete(ws);\n if (this.uiClients.size === 0) this.stopMetrics();\n }\n\n async getActiveApps(): Promise<CNSDTOApp[]> {\n return this.repository.listApps();\n }\n\n stop(): void {\n this.stopMetrics();\n }\n\n // ─── App batch item handlers ──────────────────────────────────────────────\n\n private async handleAppBatchItem(ws: WebSocket, item: any): Promise<void> {\n switch (item.type) {\n case 'topology':\n await this.handleTopology(ws, item);\n break;\n // ── Name-based durable model ──\n case 'cns.stimulation':\n await this.stimulationRepository.saveStimulation(item.data);\n break;\n case 'cns.stimulation.attempt':\n await this.stimulationRepository.saveAttempt(item.data);\n break;\n case 'cns.stimulation.task':\n await this.stimulationRepository.appendTask(item.data);\n break;\n }\n }\n\n /** The name-based durable store, for tests/integrations that want to read it back. */\n getStimulationRepository(): ICNSStimulationRepository {\n return this.stimulationRepository;\n }\n\n private async handleTopology(ws: WebSocket, msg: any): Promise<void> {\n const { appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites } = msg;\n\n const now = Date.now();\n const app: CNSDTOApp = {\n id: appId,\n name: appName,\n version,\n connectedAt: now,\n lastSeenAt: now,\n };\n\n await this.repository.upsertApp(app);\n await this.repository.addCnsToApp(appId, cnsId);\n await this.repository.saveTopology({ appId, cnsId, appName, version, timestamp, neurons, collaterals, dendrites });\n\n // Track app socket\n this.appSockets.set(ws, appId);\n if (!this.appWsByAppId.has(appId)) this.appWsByAppId.set(appId, new Set());\n this.appWsByAppId.get(appId)!.add(ws);\n\n // Broadcast topology + app connected to UI clients\n const topologyBroadcast = {\n type: 'topology.result' as const,\n requestId: '__broadcast__',\n snapshots: await this.repository.getTopology(cnsId),\n };\n\n this.broadcast({ type: 'app.connected', app, topology: { cnsId, neurons, collaterals, dendrites } });\n // Also send topology snapshot so UI can refresh graph\n this.broadcastRaw(JSON.stringify(topologyBroadcast));\n }\n\n // ─── UI client query handlers ─────────────────────────────────────────────\n\n private async handleClientConnect(ws: WebSocket): Promise<void> {\n this.addClient(ws);\n this.send(ws, { type: 'apps.result', requestId: '__init__', items: await this.repository.listApps() });\n this.send(ws, {\n type: 'topology.result',\n requestId: '__init__',\n snapshots: await this.repository.getTopology(),\n });\n }\n\n private async handleAppsQuery(ws: WebSocket, requestId: string): Promise<void> {\n const items = await this.repository.listApps();\n this.send(ws, { type: 'apps.result', requestId, items });\n }\n\n private async handleTopologyQuery(\n ws: WebSocket,\n requestId: string,\n cnsId?: string,\n appId?: string\n ): Promise<void> {\n let snapshots = await this.repository.getTopology(cnsId);\n if (appId) snapshots = snapshots.filter(s => s.appId === appId);\n this.send(ws, { type: 'topology.result', requestId, snapshots });\n }\n\n private async handleReplayStart(ws: WebSocket, msg: any): Promise<void> {\n const appId = msg.appId ?? (msg.cnsId ? await this.repository.findAppByCns(msg.cnsId) : undefined);\n const appSockets = appId ? this.appWsByAppId.get(appId) : undefined;\n\n if (!appSockets || appSockets.size === 0) {\n this.send(ws, { type: 'replay.rejected', replayId: msg.replayId, reason: 'App not connected' });\n return;\n }\n\n const newStimulationId = `${msg.stimulationId}-replay-${Date.now()}`;\n this.send(ws, { type: 'replay.accepted', replayId: msg.replayId, newStimulationId });\n\n const payload = JSON.stringify(msg);\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Durable actions (retry / clone), Phase 2b-2 ──────────────────────────────\n //\n // The UI's request is thin (a stimulationId). The CNS + name→ref registry live in\n // the APP, so the server can't execute it: it ENRICHES from the durable store (the\n // stored entry + progress + a server-assigned attempt identity) and forwards a\n // resume/launch command to the owning app, routed by scopeName (= the cns id).\n\n /** Resolve the app that owns a stimulation's scope, or reject if unreachable. */\n private async resolveScopeApp(\n scopeName: string | undefined\n ): Promise<Set<WebSocket> | undefined> {\n const appId = scopeName\n ? await this.repository.findAppByCns(scopeName)\n : undefined;\n const sockets = appId ? this.appWsByAppId.get(appId) : undefined;\n return sockets && sockets.size > 0 ? sockets : undefined;\n }\n\n private async handleStimulationRetry(ws: WebSocket, msg: any): Promise<void> {\n const reject = (reason: string) =>\n this.send(ws, {\n type: 'stimulation.retry.rejected',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n reason,\n });\n\n const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);\n if (!stim) return reject('unknown stimulation');\n if (stim.progress.tasks.length === 0)\n return reject('nothing to resume (empty frontier)');\n\n const appSockets = await this.resolveScopeApp(stim.scopeName);\n if (!appSockets) return reject('app not connected');\n\n const attempts = await this.stimulationRepository.getAttempts(msg.stimulationId);\n const nextAttempt = attempts.length + 1;\n const stimulationAttemptId = `${msg.stimulationId}#${nextAttempt}`;\n\n this.send(ws, {\n type: 'stimulation.retry.accepted',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n newStimulationAttemptId: stimulationAttemptId,\n });\n\n const payload = JSON.stringify({\n type: 'cns.stimulation.resume',\n requestId: msg.requestId,\n scopeName: stim.scopeName,\n stimulationId: msg.stimulationId,\n stimulationAttemptId,\n attemptNumber: nextAttempt,\n entry: stim.entry,\n progress: stim.progress,\n options: msg.options,\n });\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n private async handleStimulationClone(ws: WebSocket, msg: any): Promise<void> {\n const reject = (reason: string) =>\n this.send(ws, {\n type: 'stimulation.clone.rejected',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n reason,\n });\n\n const stim = await this.stimulationRepository.getStimulation(msg.stimulationId);\n if (!stim) return reject('unknown stimulation');\n\n const appSockets = await this.resolveScopeApp(stim.scopeName);\n if (!appSockets) return reject('app not connected');\n\n const newStimulationId = `${msg.stimulationId}-clone-${Date.now()}`;\n const stimulationAttemptId = `${newStimulationId}#1`;\n\n this.send(ws, {\n type: 'stimulation.clone.accepted',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n newStimulationId,\n newStimulationAttemptId: stimulationAttemptId,\n });\n\n const payload = JSON.stringify({\n type: 'cns.stimulation.launch',\n requestId: msg.requestId,\n scopeName: stim.scopeName,\n stimulationId: newStimulationId,\n stimulationAttemptId,\n entry: stim.entry,\n options: msg.options,\n });\n for (const appWs of appSockets) {\n try { appWs.send(payload); } catch {}\n }\n }\n\n // ─── Broadcast helpers ────────────────────────────────────────────────────\n\n private broadcast(message: object): void {\n this.broadcastRaw(JSON.stringify(message));\n }\n\n private broadcastRaw(payload: string): void {\n for (const client of this.uiClients) {\n try {\n if ((client as any).readyState === 1) client.send(payload);\n } catch {}\n }\n }\n\n private send(ws: WebSocket, message: object): void {\n try {\n if ((ws as any).readyState === 1) ws.send(JSON.stringify(message));\n } catch {}\n }\n\n // ─── Metrics ──────────────────────────────────────────────────────────────\n\n private startMetrics(): void {\n if (this.metricsTimer) return;\n this.metricsTimer = setInterval(() => {\n const mem = process.memoryUsage();\n const now = Date.now();\n const cpu = process.cpuUsage(this.lastCpuUsage);\n const elapsed = (now - this.lastCpuTime) * 1000;\n const cpuPercent = elapsed > 0 ? ((cpu.user + cpu.system) / elapsed) * 100 : 0;\n this.lastCpuUsage = process.cpuUsage();\n this.lastCpuTime = now;\n\n this.broadcast({\n type: 'server.metrics',\n timestamp: now,\n rssMB: mem.rss / 1024 / 1024,\n heapUsedMB: mem.heapUsed / 1024 / 1024,\n heapTotalMB: mem.heapTotal / 1024 / 1024,\n cpuPercent,\n });\n }, 1000);\n }\n\n private stopMetrics(): void {\n if (this.metricsTimer) {\n clearInterval(this.metricsTimer);\n this.metricsTimer = undefined;\n }\n }\n}\n"]}
|
package/dist/start.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CNSPersistOptionsRegistry } from '@cnstra/persist';
|
|
2
|
+
import type { ICNS, TCNSNeuron } from '@cnstra/types';
|
|
2
3
|
export type StartDevToolsOptions = {
|
|
3
4
|
/** Port for HTTP (UI) and WebSocket. Default: 3141 */
|
|
4
5
|
port?: number;
|
package/dist/start.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AA6CtD,MAAM,MAAM,oBAAoB,GAAG;IAC/B,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAC/B,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAC/B,QAAQ,EAAE,yBAAyB,EACnC,OAAO,GAAE,oBAAyB,GACnC,OAAO,CAAC,cAAc,CAAC,CAiEzB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cnstra/devtools-server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "CNStra DevTools server (WebSocket only for now)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cnstra",
|
|
@@ -34,15 +34,30 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@cnstra/devtools-dto": "^
|
|
38
|
-
"@cnstra/devtools-server-repository-in-memory": "^
|
|
37
|
+
"@cnstra/devtools-dto": "^2.0.0",
|
|
38
|
+
"@cnstra/devtools-server-repository-in-memory": "^2.0.0",
|
|
39
|
+
"@cnstra/types": "^0.1.0",
|
|
39
40
|
"ws": "^8.18.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
43
|
"@cnstra/core": "^2.1.0",
|
|
43
|
-
"@cnstra/devtools": "^
|
|
44
|
-
"@cnstra/devtools-panel-ui": "^
|
|
45
|
-
"@cnstra/devtools-transport-ws": "^1.0.0"
|
|
44
|
+
"@cnstra/devtools": "^2.0.0",
|
|
45
|
+
"@cnstra/devtools-panel-ui": "^2.0.0",
|
|
46
|
+
"@cnstra/devtools-transport-ws": "^1.0.0",
|
|
47
|
+
"@cnstra/persist": "^0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@cnstra/core": "^2.1.0",
|
|
51
|
+
"@cnstra/devtools": "^2.0.0",
|
|
52
|
+
"@cnstra/devtools-panel-ui": "^2.0.0",
|
|
53
|
+
"@cnstra/devtools-transport-ws": "^1.0.0",
|
|
54
|
+
"@cnstra/persist": "^0.1.0",
|
|
55
|
+
"@types/node": "^22.7.4",
|
|
56
|
+
"@types/ws": "^8.5.10",
|
|
57
|
+
"jest": "^29.7.0",
|
|
58
|
+
"ts-jest": "^29.1.2",
|
|
59
|
+
"tsup": "8.5.1",
|
|
60
|
+
"typescript": "^5.8.3"
|
|
46
61
|
},
|
|
47
62
|
"peerDependenciesMeta": {
|
|
48
63
|
"@cnstra/devtools-panel-ui": {
|
|
@@ -55,14 +70,6 @@
|
|
|
55
70
|
"optional": true
|
|
56
71
|
}
|
|
57
72
|
},
|
|
58
|
-
"devDependencies": {
|
|
59
|
-
"@types/node": "^22.7.4",
|
|
60
|
-
"@types/ws": "^8.5.10",
|
|
61
|
-
"jest": "^29.7.0",
|
|
62
|
-
"ts-jest": "^29.1.2",
|
|
63
|
-
"tsup": "8.5.1",
|
|
64
|
-
"typescript": "^5.8.3"
|
|
65
|
-
},
|
|
66
73
|
"author": "Andrei Baikov <abaikov23@gmail.com>",
|
|
67
74
|
"license": "MIT",
|
|
68
75
|
"publishConfig": {
|