@cnstra/devtools-server 3.0.0 → 3.5.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 +465 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +70 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +466 -100
- package/dist/index.js.map +1 -1
- package/dist/pollingTransport.d.ts +72 -0
- package/dist/pollingTransport.d.ts.map +1 -0
- package/dist/start.d.ts +32 -5
- package/dist/start.d.ts.map +1 -1
- package/package.json +7 -1
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 TCNSAppDTO,\n CNSAppBatchMessageDTOSchema,\n CNSClientMessageDTOSchema,\n TCNSTopologyMessageDTO,\n} from '@cnstra/devtools-dto';\nimport type {\n TCNSStimulationFilter,\n TCNSStimulationTask,\n TCNSStimulationAttempt,\n} from '@cnstra/devtools-dto';\nimport { buildAttemptId, buildCloneId } from '@cnstra/devtools-dto';\nimport type {\n ICNSStimulationRepository,\n TCNSStimulationRosterFilter,\n} from '@cnstra/persist';\nimport {\n CNSInMemoryStimulationRepository,\n supportsRosterQuery,\n loadRosterEntries,\n keysetPage,\n computeRosterFacets,\n} from '@cnstra/persist';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type TCNSTopologySnapshotDTO = Omit<TCNSTopologyMessageDTO, 'type'>;\n\nexport interface ICNSDevToolsServerRepository {\n // Apps\n upsertApp(app: TCNSAppDTO): void | Promise<void>;\n listApps(): TCNSAppDTO[] | Promise<TCNSAppDTO[]>;\n\n // Topology\n saveTopology(snapshot: TCNSTopologySnapshotDTO): void | Promise<void>;\n getTopology(cnsId?: string): TCNSTopologySnapshotDTO[] | Promise<TCNSTopologySnapshotDTO[]>;\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 resumable 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 = CNSAppBatchMessageDTOSchema.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 = CNSClientMessageDTOSchema.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.list':\n await this.handleStimulationsList(ws, msg);\n break;\n case 'cns.stimulations.facets':\n await this.handleStimulationsFacets(ws, msg);\n break;\n case 'cns.stimulations.snapshot':\n await this.handleStimulationsSnapshot(ws, msg);\n break;\n case 'cns.stimulation.detail':\n await this.handleStimulationDetail(ws, msg);\n break;\n }\n }\n\n /**\n * Explorer protocol (bucket-shaped). The roster row = resumable state minus\n * `progress`, plus envelope summaries derived from the run's attempts.\n *\n * Pushdown: if the store implements `ICNSStimulationRosterQuery` (e.g. the\n * Postgres store), the filter + keyset + GROUP BY run INSIDE it (SQL). Else we\n * fall back to the shared reference derivation over the reader — identical\n * results, only the work moves. Roster shapes are structurally the wire DTOs,\n * so no mapping is needed at the boundary.\n */\n private toRosterFilter(\n scopeName: string | undefined,\n filter: TCNSStimulationFilter | undefined\n ): TCNSStimulationRosterFilter {\n return { ...(filter ?? {}), ...(scopeName ? { scopeName } : {}) };\n }\n\n private async handleStimulationsList(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const limit = typeof msg.limit === 'number' ? msg.limit : 50;\n const opts = { cursor: msg.cursor, limit };\n const store = this.stimulationRepository;\n const page = supportsRosterQuery(store)\n ? await store.queryRoster(filter, opts)\n : keysetPage(\n (await loadRosterEntries(store, filter)).map(e => e.row),\n opts\n );\n this.send(ws, {\n type: 'cns.stimulations.list.result',\n requestId: msg.requestId,\n stimulations: page.rows,\n nextCursor: page.nextCursor,\n total: page.total,\n });\n }\n\n private async handleStimulationsFacets(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const store = this.stimulationRepository;\n const facets = supportsRosterQuery(store)\n ? await store.queryFacets(filter, { metricPath: msg.metricPath })\n : computeRosterFacets(await loadRosterEntries(store, filter), {\n metricPath: msg.metricPath,\n });\n this.send(ws, {\n type: 'cns.stimulations.facets.result',\n requestId: msg.requestId,\n facets,\n });\n }\n\n private async handleStimulationDetail(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const attempts = await this.stimulationRepository.getAttempts(\n msg.stimulationId\n );\n const tasks: TCNSStimulationTask[] = [];\n for (const a of attempts) {\n const ts = await this.stimulationRepository.getTasks(\n a.stimulationAttemptId\n );\n tasks.push(...ts);\n }\n this.send(ws, {\n type: 'cns.stimulation.detail.result',\n requestId: msg.requestId,\n attempts,\n tasks,\n });\n }\n\n /**\n * The topology/signal views' bulk feed: a bounded, newest-first set of runs as\n * FLAT buckets (stimulations + attempts + tasks) the panel joins client-side by\n * id. Replaces the retired nested `cns.stimulations.query` tree — same data, no\n * server-side join, no client-side flatten.\n */\n private async handleStimulationsSnapshot(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const store = this.stimulationRepository;\n const limit = typeof msg.limit === 'number' ? msg.limit : 200;\n const page = supportsRosterQuery(store)\n ? await store.queryRoster(filter, { limit })\n : keysetPage(\n (await loadRosterEntries(store, filter)).map(e => e.row),\n { limit }\n );\n const attempts: TCNSStimulationAttempt[] = [];\n const tasks: TCNSStimulationTask[] = [];\n for (const row of page.rows) {\n const as = await store.getAttempts(row.stimulationId);\n attempts.push(...as);\n for (const a of as)\n tasks.push(...(await store.getTasks(a.stimulationAttemptId)));\n }\n this.send(ws, {\n type: 'cns.stimulations.snapshot.result',\n requestId: msg.requestId,\n stimulations: page.rows,\n attempts,\n tasks,\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<TCNSAppDTO[]> {\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 resumable 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 resumable 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: TCNSAppDTO = {\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 // ─── Resumable 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 resumable 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 = buildAttemptId(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 = buildCloneId(msg.stimulationId);\n const stimulationAttemptId = buildAttemptId(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"]}
|
|
1
|
+
{"version":3,"sources":["../src/pollingTransport.ts","../src/start.ts","../src/index.ts"],"names":["url"],"mappings":"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","file":"index.js","sourcesContent":["import type { CNSDevToolsServer } from './index';\n\n/**\n * An HTTP long-polling transport for the DevTools protocol.\n *\n * A WebSocket is the right connection for this — it is what the panel and the\n * apps use by default. But an upgrade does not always survive the trip: an\n * ingress with upgrades off, a corporate proxy, a platform that cuts long-lived\n * connections. Polling is the fallback for exactly those, and nothing else about\n * the protocol changes.\n *\n * The server's own logic is already transport-agnostic — `handleMessage` takes\n * anything with `send(data: string)` — so a polling participant is represented by\n * one of those with a mailbox behind it: messages the server \"sends\" are queued,\n * and a `GET` drains them.\n */\n\n/** How the protocol reaches a participant that cannot hold a socket open. */\nexport interface ICNSPollingSession {\n /** The socket-shaped object handed to `handleMessage` / `removeClient`. */\n readonly client: { send(data: string): void; readyState: number };\n /** Messages queued since the last drain. */\n take(): string[];\n /** Park until a message arrives or the wait elapses; then drain. */\n wait(timeoutMs: number): Promise<string[]>;\n /** Last time this session was seen, for expiry. */\n lastSeenAt: number;\n}\n\nconst createSession = (): ICNSPollingSession => {\n let queued: string[] = [];\n let wake: (() => void) | undefined;\n\n const session: ICNSPollingSession = {\n client: {\n // `readyState` 1 is what `liveSockets` checks before routing an action\n // to a participant: a polling session is reachable as long as it has\n // not expired, and expiry removes it outright.\n readyState: 1,\n send(data: string) {\n queued.push(data);\n wake?.();\n },\n },\n take() {\n const out = queued;\n queued = [];\n return out;\n },\n async wait(timeoutMs: number) {\n if (queued.length) return session.take();\n\n await new Promise<void>(resolve => {\n const timer = setTimeout(() => {\n wake = undefined;\n resolve();\n }, timeoutMs);\n wake = () => {\n clearTimeout(timer);\n wake = undefined;\n resolve();\n };\n });\n\n return session.take();\n },\n lastSeenAt: Date.now(),\n };\n\n return session;\n};\n\nexport type TCNSPollingOptions = {\n /**\n * How long a `GET` parks before answering empty. Long enough that an idle\n * panel is not a request loop, short enough to stay under the read timeout of\n * whatever proxy made polling necessary in the first place.\n */\n waitMs?: number;\n /**\n * Drop a session not seen for this long. A polling participant has no `close`\n * event, so silence is the only signal that it is gone — and a session that is\n * never dropped keeps its queue, and its place in the action routing, forever.\n */\n sessionTtlMs?: number;\n};\n\n/**\n * Handles the two polling endpoints against a {@link CNSDevToolsServer}.\n *\n * - `POST …?sessionId=X` — body is one protocol message, same as a socket frame.\n * - `GET …?sessionId=X` — resolves with the messages queued for that session,\n * parking up to `waitMs` if there are none.\n *\n * Returns `undefined` for a request it does not recognise, so the caller can fall\n * through to serving the panel's assets.\n */\nexport class CNSDevToolsPollingTransport {\n private readonly sessions = new Map<string, ICNSPollingSession>();\n private readonly waitMs: number;\n private readonly sessionTtlMs: number;\n\n constructor(\n private readonly server: CNSDevToolsServer,\n options: TCNSPollingOptions = {}\n ) {\n this.waitMs = options.waitMs ?? 25_000;\n this.sessionTtlMs = options.sessionTtlMs ?? 60_000;\n }\n\n /** Sessions currently held. Exposed so a leak is observable in tests. */\n get sessionCount(): number {\n return this.sessions.size;\n }\n\n /** Deliver one message from a participant. */\n async post(sessionId: string, body: string): Promise<void> {\n const session = this.session(sessionId);\n await this.server.handleMessage(session.client as never, body);\n }\n\n /** Drain what is queued for a participant, parking if nothing is. */\n async poll(sessionId: string): Promise<string[]> {\n const session = this.session(sessionId);\n return session.wait(this.waitMs);\n }\n\n /** Forget a participant and take it out of the server's routing. */\n close(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (!session) return;\n this.sessions.delete(sessionId);\n this.server.removeClient(session.client as never);\n }\n\n private session(sessionId: string): ICNSPollingSession {\n this.sweep();\n\n const existing = this.sessions.get(sessionId);\n if (existing) {\n existing.lastSeenAt = Date.now();\n return existing;\n }\n\n const created = createSession();\n this.sessions.set(sessionId, created);\n return created;\n }\n\n /** Drop sessions that stopped polling. Silence is the only signal they give. */\n private sweep(): void {\n const cutoff = Date.now() - this.sessionTtlMs;\n for (const [id, session] of this.sessions) {\n if (session.lastSeenAt < cutoff) {\n this.sessions.delete(id);\n this.server.removeClient(session.client as never);\n }\n }\n }\n}\n","import { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { createRequire } from 'module';\nimport { WebSocketServer, WebSocket } from 'ws';\nimport { CNSDevToolsServer } from './index';\nimport { CNSDevToolsPollingTransport } from './pollingTransport';\nimport type { TCNSPollingOptions } from './pollingTransport';\nimport type { TCNSUiConfig } from './index';\nimport type { ICNSScheduledStimulationReader, TCNSDevToolsActionMiddleware } from '@cnstra/persist-dto';\nimport type {\n CNSPersistOptionsRegistry,\n ICNSStimulationRepository,\n} from '@cnstra/persist';\nimport type { ICNS, TCNSNeuron } from '@cnstra/types';\n\nconst _require = createRequire(join(process.cwd(), 'package.json'));\n\nfunction findPanelUIDir(): string | null {\n try {\n const pkg = _require.resolve('@cnstra/devtools-panel-ui/package.json');\n return join(dirname(pkg), 'dist');\n } catch {}\n try {\n const here = dirname(_require.resolve('@cnstra/devtools-server/package.json'));\n const monorepoPath = join(here, '..', '..', 'devtools-panel-ui', 'dist');\n if (existsSync(join(monorepoPath, 'index.html'))) return monorepoPath;\n } catch {}\n return null;\n}\n\nconst MIME: Record<string, string> = {\n html: 'text/html', js: 'application/javascript', css: 'text/css',\n svg: 'image/svg+xml', png: 'image/png', woff2: 'font/woff2',\n};\n\nfunction serveStatic(panelUIDir: string) {\n return (_req: IncomingMessage, res: ServerResponse) => {\n const safe = (_req.url ?? '/').split('?')[0].replace(/\\.\\./g, '');\n const file = safe === '/' ? '/index.html' : safe;\n const full = join(panelUIDir, file);\n\n if (existsSync(full) && full.startsWith(panelUIDir)) {\n const ext = full.split('.').pop() ?? '';\n res.writeHead(200, { 'Content-Type': MIME[ext] ?? 'text/plain' });\n res.end(readFileSync(full));\n } else {\n const idx = join(panelUIDir, 'index.html');\n if (existsSync(idx)) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(readFileSync(idx));\n } else {\n res.writeHead(503);\n res.end('DevTools UI not built. Run: npm run build --workspace=@cnstra/devtools-panel-ui');\n }\n }\n };\n}\n\nexport type TCNSDevToolsStartOptions = {\n /** Port for HTTP (UI) and WebSocket. Default: 3141 */\n port?: number;\n /**\n * Links that carry out a panel action (Retry / Clone), in order.\n *\n * Without one, an action is forwarded over the app's socket — which reaches\n * every connected replica of the same `appId`, so one Retry click runs on all\n * of them. Pass `createCNSBullActions(queue)` (or the pg-boss equivalent) and the\n * retry goes through the broker instead, where delivery and de-duplication are\n * its problem. The socket link is always appended last, so a run that came\n * from somewhere else still reaches an in-process CNS.\n */\n actions?: readonly TCNSDevToolsActionMiddleware[];\n /**\n * What the panel may offer. `{ actions: { retry: false } }` hides Retry — for\n * a deployment where a run's record must never be overwritten in place, so a\n * rerun has to go through Clone.\n */\n ui?: TCNSUiConfig;\n /**\n * Tuning for the HTTP polling fallback (always available at\n * `/__cnstra/poll`). The socket stays the default; polling is what a client\n * picks when an upgrade will not survive the network in between.\n */\n polling?: TCNSPollingOptions;\n /** Automatically open browser. Default: true in TTY */\n open?: boolean;\n /** App ID shown in DevTools. Default: 'app' */\n appId?: string;\n consoleLogEnabled?: boolean;\n /**\n * The canonical name-based Stimulation → Attempt → Task store the Explorer\n * reads its runs / facets / waterfall from. Pass the SAME instance you give a\n * pg-boss / BullMQ worker as its `observe` store, and every job run shows up\n * in the Explorer live (one process, shared memory). Use a\n * `CNSPostgresStimulationRepository` to keep history across restarts, or omit\n * for a fresh ephemeral in-memory store.\n */\n stimulationRepository?: ICNSStimulationRepository;\n scheduledStimulationReader?: ICNSScheduledStimulationReader;\n};\n\nexport type TCNSDevToolsHandle = {\n url: string;\n stop: () => void;\n};\n\n/**\n * Starts the CNStra DevTools UI and connects your CNS instance to it.\n *\n * @example\n * import { startCNSDevTools } from '@cnstra/devtools-server';\n * import { cns } from './cns';\n * import { registry } from './neurons/registry';\n *\n * await startCNSDevTools(cns, registry);\n * // → 🧠 CNStra DevTools: http://localhost:3141\n */\nexport async function startCNSDevTools(\n cns: ICNS<TCNSNeuron<any>>,\n registry: CNSPersistOptionsRegistry,\n options: TCNSDevToolsStartOptions = {}\n): Promise<TCNSDevToolsHandle> {\n const port = options.port ?? 3141;\n const appId = options.appId ?? 'app';\n const logEnabled = options.consoleLogEnabled ?? false;\n const panelUIDir = findPanelUIDir();\n\n if (!panelUIDir) {\n console.warn(\n '\\n⚠️ CNStra DevTools UI not found.\\n' +\n ' WebSocket server will start but the UI will not be served.\\n' +\n ' Install @cnstra/devtools-panel-ui or build it from the monorepo.\\n'\n );\n }\n\n const { CNSDevToolsServerRepositoryInMemory } = await import(\n '@cnstra/devtools-server-repository-in-memory'\n );\n const dtServer = new CNSDevToolsServer(\n new CNSDevToolsServerRepositoryInMemory(),\n options.stimulationRepository,\n options.actions,\n options.ui,\n options.scheduledStimulationReader\n );\n const polling = new CNSDevToolsPollingTransport(dtServer, options.polling);\n\n const staticHandler = panelUIDir\n ? serveStatic(panelUIDir)\n : (_: IncomingMessage, res: ServerResponse) => {\n res.writeHead(503);\n res.end('DevTools UI not available.');\n };\n\n const readBody = (req: IncomingMessage): Promise<string> =>\n new Promise(resolve => {\n let body = '';\n req.on('data', chunk => (body += chunk));\n req.on('end', () => resolve(body));\n });\n\n const httpServer = createServer(\n async (req: IncomingMessage, res: ServerResponse) => {\n // Polling endpoints first: the same protocol for a network that will\n // not carry a WebSocket upgrade. The socket stays the default — this\n // is only reached by a client that asked for it.\n if (req.url?.startsWith('/__cnstra/poll')) {\n // A WebSocket is not subject to CORS; polling is. Without these\n // headers the fallback fails exactly where it is needed — a panel\n // loaded from file:// (electron) or another origin could use the\n // socket and not the thing that exists for when the socket cannot\n // get through. Nothing is loosened: this server has no auth and\n // already serves the panel to whoever can reach it.\n res.setHeader('access-control-allow-origin', '*');\n res.setHeader('access-control-allow-methods', 'GET,POST,DELETE,OPTIONS');\n res.setHeader('access-control-allow-headers', 'content-type');\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n return res.end();\n }\n\n const url = new URL(req.url, 'http://localhost');\n const sessionId = url.searchParams.get('sessionId');\n if (!sessionId) {\n res.writeHead(400);\n return res.end('sessionId required');\n }\n\n if (req.method === 'POST') {\n const body = await readBody(req);\n await polling.post(sessionId, body);\n res.writeHead(204);\n return res.end();\n }\n if (req.method === 'DELETE') {\n polling.close(sessionId);\n res.writeHead(204);\n return res.end();\n }\n const messages = await polling.poll(sessionId);\n res.writeHead(200, { 'content-type': 'application/json' });\n return res.end(JSON.stringify({ messages }));\n }\n\n return staticHandler(req, res);\n }\n );\n const wss = new WebSocketServer({ server: httpServer });\n\n\n wss.on('connection', (ws) => {\n ws.on('message', async (data) => {\n try {\n await dtServer.handleMessage(ws as any, data.toString());\n } catch {}\n });\n ws.on('close', () => dtServer.removeClient(ws as any));\n });\n\n await new Promise<void>((resolve) => httpServer.listen(port, resolve));\n\n const url = `http://localhost:${port}`;\n\n const { CNSDevTools } = await import('@cnstra/devtools');\n const { CNSDevToolsTransportWs } = await import('@cnstra/devtools-transport-ws');\n\n const transport = new CNSDevToolsTransportWs({ url: `ws://localhost:${port}` });\n const devtools = new CNSDevTools(appId, transport, {\n appName: appId,\n consoleLogEnabled: logEnabled,\n });\n devtools.registerCNS(cns, registry);\n\n if (logEnabled || process.stdout.isTTY) {\n console.log(`\\n🧠 CNStra DevTools: ${url}\\n`);\n }\n\n const shouldOpen = options.open ?? process.stdout.isTTY;\n if (shouldOpen) {\n const { exec } = await import('child_process');\n const cmd = process.platform === 'darwin' ? 'open' :\n process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${cmd} ${url}`);\n }\n\n return {\n url,\n stop: () => { dtServer.stop(); wss.close(); httpServer.close(); },\n };\n}\n","import {\n TCNSAppDTO,\n CNSAppBatchMessageDTOSchema,\n CNSClientMessageDTOSchema,\n TCNSTopologyMessageDTO,\n} from '@cnstra/devtools-dto';\nimport type {\n TCNSStimulationFilter,\n TCNSStimulationTask,\n TCNSStimulationAttempt,\n} from '@cnstra/devtools-dto';\nimport { buildCNSAttemptId, buildCNSCloneId } from '@cnstra/devtools-dto';\nimport type {\n ICNSStimulationRepository,\n TCNSStimulationRosterFilter,\n} from '@cnstra/persist';\nimport { runCNSDevToolsAction } from '@cnstra/persist-dto';\nimport type {\n ICNSScheduledStimulationReader,\n TCNSDevToolsAction,\n TCNSDevToolsActionMiddleware,\n TCNSScheduledStimulationFilter,\n TCNSStimulationCommandAccepted,\n TCNSStimulationCommandRejected,\n TCNSStimulationCommandRequest,\n TCNSStimulationLaunchAccepted,\n TCNSStimulationLaunchRejected,\n TCNSStimulationLaunchRequest,\n} from '@cnstra/persist-dto';\nimport {\n CNSInMemoryStimulationRepository,\n supportsCNSRosterQuery,\n loadCNSRosterEntries,\n cnsKeysetPage,\n computeCNSRosterFacets,\n} from '@cnstra/persist';\nimport { randomUUID } from 'crypto';\nimport { WebSocket } from 'ws';\n\n// ─── Repository interface ─────────────────────────────────────────────────────\n\nexport type TCNSTopologySnapshotDTO = Omit<TCNSTopologyMessageDTO, 'type'>;\n\n/** Server-side switches the panel reads on connect. */\nexport type TCNSUiConfig = {\n /** Which run actions the panel may offer. Unset ⇒ allowed. */\n actions?: { retry?: boolean; clone?: boolean };\n /**\n * How far back the panel looks when it opens, in milliseconds. Defaults to\n * five minutes; `0` opens on the whole history.\n *\n * The Explorer is time-ordered throughout, and an unbounded first query makes\n * the store aggregate everything it holds to show one page. This is the knob\n * for a deployment whose history is large — or small enough not to bother.\n */\n defaultWindowMs?: number;\n};\n\nexport interface ICNSDevToolsServerRepository {\n saveTopology(snapshot: TCNSTopologySnapshotDTO): void | Promise<void>;\n getTopology(cnsId?: string): TCNSTopologySnapshotDTO[] | Promise<TCNSTopologySnapshotDTO[]>;\n\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 resumable 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 /**\n * Links that carry out a panel action (Retry / Clone / Launch), in order.\n *\n * The runs a panel shows do not all come from the same place: some are queue\n * jobs, some were started by an HTTP handler, some by a cron. A link that owns\n * a run acts and stops the chain; one that does not calls `next()`. Wrapping a\n * built-in — to log, authorize, or refuse — is the same mechanism.\n *\n * The server appends its own link LAST: forwarding over the app's socket, the\n * only way to reach a plain in-process CNS with no broker. So an empty chain\n * behaves exactly as before.\n */\n private readonly actions: readonly TCNSDevToolsActionMiddleware[];\n\n /**\n * What the panel may offer, handed to it on connect.\n *\n * Retry and Clone are different operations: Retry continues the SAME run and\n * updates its record in place, Clone starts a new run and leaves the original\n * alone. A deployment that must never overwrite a record turns Retry off; one\n * that must not multiply run ids turns Clone off. Unset means allowed.\n */\n private readonly ui: TCNSUiConfig;\n\n constructor(\n private readonly repository: ICNSDevToolsServerRepository,\n stimulationRepository: ICNSStimulationRepository = new CNSInMemoryStimulationRepository(),\n actions: readonly TCNSDevToolsActionMiddleware[] = [],\n ui: TCNSUiConfig = {},\n private readonly scheduledStimulationReader?: ICNSScheduledStimulationReader\n ) {\n this.stimulationRepository = stimulationRepository;\n this.actions = [...actions, this.createSocketAction()];\n this.ui = ui;\n }\n\n /**\n * Run an action through the chain and flatten the three outcomes into one\n * value: `true` acted, `false` nobody took it, a string is the reason a link\n * failed. A link that throws must not escape into the socket's message\n * handler, where nothing answers the panel.\n */\n private async carryOut(\n action: TCNSDevToolsAction\n ): Promise<true | false | string> {\n try {\n return (await runCNSDevToolsAction(this.actions, action)) || false;\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n }\n\n /**\n * The fallback link: forward the action to the app over its own socket. This\n * is what reaches a CNS running in-process with no queue behind it — and it\n * is why the chain always ends here rather than rejecting.\n */\n private createSocketAction(): TCNSDevToolsActionMiddleware {\n return async (action, next) => {\n if (action.type === 'command') return next();\n const appSockets = await this.resolveScopeApp(action.scopeName);\n if (!appSockets) return next();\n\n const payload =\n action.type === 'resume'\n ? JSON.stringify({\n type: 'cns.stimulation.resume',\n requestId: action.requestId,\n scopeName: action.scopeName,\n stimulationId: action.stimulationId,\n stimulationAttemptId: action.stimulationAttemptId,\n attemptNumber: action.attemptNumber,\n entry: action.entry,\n progress: action.progress,\n replayOf: action.replayOf,\n options: action.options,\n })\n : JSON.stringify({\n type: 'cns.stimulation.launch',\n requestId: action.requestId,\n scopeName: action.scopeName,\n stimulationId: action.stimulationId,\n stimulationAttemptId: action.stimulationAttemptId,\n entry: action.entry,\n replayOf: action.replayOf,\n options: action.options,\n });\n\n for (const appWs of appSockets) {\n try {\n appWs.send(payload);\n } catch {}\n }\n };\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 = CNSAppBatchMessageDTOSchema.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 = CNSClientMessageDTOSchema.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 '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.list':\n await this.handleStimulationsList(ws, msg);\n break;\n case 'cns.stimulations.facets':\n await this.handleStimulationsFacets(ws, msg);\n break;\n case 'cns.stimulations.snapshot':\n await this.handleStimulationsSnapshot(ws, msg);\n break;\n case 'cns.stimulation.detail':\n await this.handleStimulationDetail(ws, msg);\n break;\n case 'cns.scheduled.list':\n await this.handleScheduledStimulationsList(ws, msg);\n break;\n case 'stimulation.command':\n await this.handleStimulationCommand(ws, msg);\n break;\n case 'stimulation.launch':\n await this.handleStimulationLaunch(ws, msg);\n break;\n }\n }\n\n /**\n * Explorer protocol (bucket-shaped). The roster row = resumable state minus\n * `progress`, plus envelope summaries derived from the run's attempts.\n *\n * Pushdown: if the store implements `ICNSStimulationRosterQuery` (e.g. the\n * Postgres store), the filter + keyset + GROUP BY run INSIDE it (SQL). Else we\n * fall back to the shared reference derivation over the reader — identical\n * results, only the work moves. Roster shapes are structurally the wire DTOs,\n * so no mapping is needed at the boundary.\n */\n private toRosterFilter(\n scopeName: string | undefined,\n filter: TCNSStimulationFilter | undefined\n ): TCNSStimulationRosterFilter {\n return { ...(filter ?? {}), ...(scopeName ? { scopeName } : {}) };\n }\n\n private async handleStimulationsList(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const limit = typeof msg.limit === 'number' ? msg.limit : 50;\n const opts = { cursor: msg.cursor, limit };\n const store = this.stimulationRepository;\n const page = supportsCNSRosterQuery(store)\n ? await store.queryRoster(filter, opts)\n : cnsKeysetPage(\n (await loadCNSRosterEntries(store, filter)).map(e => e.row),\n opts\n );\n this.send(ws, {\n type: 'cns.stimulations.list.result',\n requestId: msg.requestId,\n stimulations: page.rows,\n nextCursor: page.nextCursor,\n total: page.total,\n });\n }\n\n private async handleStimulationsFacets(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const store = this.stimulationRepository;\n const facets = supportsCNSRosterQuery(store)\n ? await store.queryFacets(filter, { metricPath: msg.metricPath })\n : computeCNSRosterFacets(await loadCNSRosterEntries(store, filter), {\n metricPath: msg.metricPath,\n });\n this.send(ws, {\n type: 'cns.stimulations.facets.result',\n requestId: msg.requestId,\n facets,\n });\n }\n\n private async handleStimulationLaunch(ws: WebSocket, msg: TCNSStimulationLaunchRequest): Promise<void> {\n const newStimulationId = `${msg.entry.collateralName}-${randomUUID()}`;\n const handled = await this.carryOut({\n type: 'launch',\n requestId: msg.requestId,\n stimulationId: newStimulationId,\n stimulationAttemptId: buildCNSAttemptId(newStimulationId, 1),\n entry: msg.entry,\n });\n if (handled === true) {\n this.send(ws, { type: 'stimulation.launch.accepted', requestId: msg.requestId, newStimulationId } satisfies TCNSStimulationLaunchAccepted);\n return;\n }\n this.send(ws, {\n type: 'stimulation.launch.rejected',\n requestId: msg.requestId,\n reason: handled === false ? 'no runtime accepted the launch' : handled,\n } satisfies TCNSStimulationLaunchRejected);\n }\n\n private async handleStimulationCommand(ws: WebSocket, msg: TCNSStimulationCommandRequest): Promise<void> {\n const handled = await this.carryOut({\n type: 'command',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n command: msg.command,\n });\n if (handled === true) {\n this.send(ws, { type: 'stimulation.command.accepted', requestId: msg.requestId, stimulationId: msg.stimulationId } satisfies TCNSStimulationCommandAccepted);\n return;\n }\n this.send(ws, {\n type: 'stimulation.command.rejected',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n reason: handled === false ? `no runtime accepted the ${msg.command.name} command` : handled,\n } satisfies TCNSStimulationCommandRejected);\n }\n\n private async handleScheduledStimulationsList(\n ws: WebSocket,\n msg: { requestId: string; filter?: TCNSScheduledStimulationFilter; cursor?: string; limit?: number }\n ): Promise<void> {\n const reader = this.scheduledStimulationReader;\n const page = reader\n ? await reader.list(msg.filter ?? {}, msg.cursor ?? null, msg.limit ?? 50)\n : { rows: [], schedules: [], types: [], nextCursor: null };\n this.send(ws, {\n type: 'cns.scheduled.list.result',\n requestId: msg.requestId,\n available: reader !== undefined,\n ...page,\n });\n }\n\n private async handleStimulationDetail(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const attempts = await this.stimulationRepository.getAttempts(\n msg.stimulationId\n );\n // One read, the way the snapshot handler already does it — this looped\n // per attempt while the bulk method sat two methods away.\n const attemptIds = attempts.map(a => a.stimulationAttemptId);\n const store = this.stimulationRepository;\n const tasks: TCNSStimulationTask[] = store.getTasksFor\n ? await store.getTasksFor(attemptIds)\n : (\n await Promise.all(\n attemptIds.map(id => store.getTasks(id))\n )\n ).flat();\n this.send(ws, {\n type: 'cns.stimulation.detail.result',\n requestId: msg.requestId,\n attempts,\n tasks,\n });\n }\n\n /**\n * The topology/signal views' bulk feed: a bounded, newest-first set of runs as\n * FLAT buckets (stimulations + attempts + tasks) the panel joins client-side by\n * id. Replaces the retired nested `cns.stimulations.query` tree — same data, no\n * server-side join, no client-side flatten.\n */\n private async handleStimulationsSnapshot(\n ws: WebSocket,\n msg: any\n ): Promise<void> {\n const filter = this.toRosterFilter(msg.scopeName, msg.filter);\n const store = this.stimulationRepository;\n const limit = typeof msg.limit === 'number' ? msg.limit : 200;\n const page = supportsCNSRosterQuery(store)\n ? await store.queryRoster(filter, { limit })\n : cnsKeysetPage(\n (await loadCNSRosterEntries(store, filter)).map(e => e.row),\n { limit }\n );\n // Two calls, not two per run. The page can be hundreds of runs with\n // several attempts each; asking per id turned one snapshot into hundreds\n // of round trips whose count grew with the page rather than the data.\n //\n // And only for the runs the caller does not already hold. The rows\n // themselves always go in full — they are what says a run moved — but a\n // finished run's tasks never change, and re-reading and re-sending them\n // every two seconds is the whole payload.\n const skip = new Set(\n Array.isArray(msg.skipDetailFor) ? msg.skipDetailFor : []\n );\n const ids = page.rows\n .map(row => row.stimulationId)\n .filter(id => !skip.has(id));\n const attempts: TCNSStimulationAttempt[] = store.getAttemptsFor\n ? await store.getAttemptsFor(ids)\n : (\n await Promise.all(ids.map(id => store.getAttempts(id)))\n ).flat();\n\n const attemptIds = attempts.map(a => a.stimulationAttemptId);\n const tasks: TCNSStimulationTask[] = store.getTasksFor\n ? await store.getTasksFor(attemptIds)\n : (\n await Promise.all(attemptIds.map(id => store.getTasks(id)))\n ).flat();\n this.send(ws, {\n type: 'cns.stimulations.snapshot.result',\n requestId: msg.requestId,\n stimulations: page.rows,\n attempts,\n tasks,\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\n // An app socket is registered in TWO places (appSockets + the per-app\n // routing set). Forgetting the second one made `resolveScopeApp` keep\n // reporting a dead app as reachable: retry/clone were ACCEPTED and then\n // written into a closed socket, and the set grew by one corpse per\n // reconnect.\n const appId = this.appSockets.get(ws);\n this.appSockets.delete(ws);\n if (appId !== undefined) {\n const sockets = this.appWsByAppId.get(appId);\n if (sockets) {\n sockets.delete(ws);\n if (sockets.size === 0) this.appWsByAppId.delete(appId);\n }\n }\n\n if (this.uiClients.size === 0) this.stopMetrics();\n }\n\n async getActiveApps(): Promise<TCNSAppDTO[]> {\n return CNSDevToolsServer.appsOf(await this.repository.getTopology());\n }\n\n private static appsOf(snapshots: readonly TCNSTopologySnapshotDTO[]): TCNSAppDTO[] {\n const latest = new Map<string, TCNSTopologySnapshotDTO>();\n for (const snapshot of snapshots) {\n const known = latest.get(snapshot.appId);\n if (!known || snapshot.timestamp > known.timestamp) latest.set(snapshot.appId, snapshot);\n }\n return [...latest.values()].map(snapshot => ({ id: snapshot.appId, name: snapshot.appName, version: snapshot.version }));\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 resumable 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 resumable 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 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: { id: appId, name: appName, version }, 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 // First, so the panel knows which actions to draw before any run arrives.\n this.send(ws, {\n type: 'ui.config',\n // Five minutes unless told otherwise: the panel opens on what is\n // happening now, not on everything that ever happened.\n defaultWindowMs: this.ui.defaultWindowMs ?? 5 * 60_000,\n ...this.ui,\n });\n const snapshots = await this.repository.getTopology();\n this.send(ws, { type: 'apps.result', requestId: '__init__', items: CNSDevToolsServer.appsOf(snapshots) });\n this.send(ws, { type: 'topology.result', requestId: '__init__', snapshots });\n }\n\n private async handleAppsQuery(ws: WebSocket, requestId: string): Promise<void> {\n this.send(ws, { type: 'apps.result', requestId, items: await this.getActiveApps() });\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 // ─── Resumable 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 resumable 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 return appId ? this.liveSockets(appId) : undefined;\n }\n\n /**\n * Sockets of `appId` that can still receive a command, or `undefined` when\n * none can. `close` is not always dispatched before we route (a half-dead\n * socket, an embedding that never calls `removeClient`), so being registered\n * is not proof of being reachable — readyState is.\n */\n private liveSockets(appId: string): Set<WebSocket> | undefined {\n const sockets = this.appWsByAppId.get(appId);\n if (!sockets || sockets.size === 0) return undefined;\n const open = new Set<WebSocket>();\n for (const ws of sockets) {\n if ((ws as any).readyState === 1) open.add(ws);\n }\n return open.size > 0 ? open : 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 // No connectivity check here any more: whether anything can carry the\n // action out is the chain's answer, and a broker link needs no connected\n // app at all. The socket link is simply its last resort.\n const attempts = await this.stimulationRepository.getAttempts(msg.stimulationId);\n // The next attempt number is one past the HIGHEST attempt, not one past\n // the COUNT: retention may have pruned attempt 1 (leaving 2,3 → count 2\n // → a collision with the live attempt 3), and two panels reading the\n // same array would otherwise mint the same id twice.\n const latest = attempts.reduce<TCNSStimulationAttempt | undefined>(\n (max, a) => (!max || a.attemptNumber > max.attemptNumber ? a : max),\n undefined\n );\n const nextAttempt = (latest?.attemptNumber ?? 0) + 1;\n const stimulationAttemptId = buildCNSAttemptId(msg.stimulationId, nextAttempt);\n\n // Accept only once something has taken the action — telling the panel\n // \"accepted\" and then \"rejected\" for one click is worse than either.\n //\n // A link may also THROW: a second, concurrent Retry reaches BullMQ's\n // `job.retry()` on a job that has already left the failed set, and it\n // rejects rather than queueing the run twice. That is the right outcome,\n // but the person who clicked has to be told — an escaping exception\n // leaves the panel with no answer at all.\n const handled = await this.carryOut({\n type: '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 // Provenance: which attempt this retry continues from.\n replayOf: latest?.stimulationAttemptId,\n options: msg.options,\n });\n // Nobody could carry it out. Saying so beats a button that reports\n // \"accepted\" and then does nothing.\n if (handled !== true) {\n return reject(\n handled === false\n ? 'no runtime accepted the retry'\n : `retry failed: ${handled}`\n );\n }\n\n this.send(ws, {\n type: 'stimulation.retry.accepted',\n requestId: msg.requestId,\n stimulationId: msg.stimulationId,\n newStimulationAttemptId: stimulationAttemptId,\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 newStimulationId = buildCNSCloneId(msg.stimulationId);\n const stimulationAttemptId = buildCNSAttemptId(newStimulationId, 1);\n // `replayOf` is an ATTEMPT id by contract, so the provenance of a clone is\n // the source run's latest attempt — not the source stimulation id.\n const sourceAttempts = await this.stimulationRepository.getAttempts(\n msg.stimulationId\n );\n // Highest attempt, not last-in-array: `getAttempts` ordering is not part\n // of the repository contract, and only the in-memory store happens to\n // sort ascending.\n const replayOf = sourceAttempts.reduce<TCNSStimulationAttempt | undefined>(\n (max, a) => (!max || a.attemptNumber > max.attemptNumber ? a : max),\n undefined\n )?.stimulationAttemptId;\n\n // Same as retry: accept only after something took the action, and a\n // throwing link is reported rather than lost.\n const handled = await this.carryOut({\n type: 'launch',\n requestId: msg.requestId,\n scopeName: stim.scopeName,\n stimulationId: newStimulationId,\n stimulationAttemptId,\n entry: stim.entry,\n // A clone gets a NEW stimulationId, so this is the ONLY link back to\n // the run it came from.\n replayOf,\n options: msg.options,\n });\n if (handled !== true) {\n return reject(\n handled === false\n ? 'no runtime accepted the clone'\n : `clone failed: ${handled}`\n );\n }\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\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\nexport { CNSDevToolsPollingTransport } from './pollingTransport';\nexport type {\n TCNSPollingOptions,\n ICNSPollingSession,\n} from './pollingTransport';\nexport { startCNSDevTools } from './start';\nexport type { TCNSDevToolsStartOptions, TCNSDevToolsHandle } from './start';\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { CNSDevToolsServer } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* An HTTP long-polling transport for the DevTools protocol.
|
|
4
|
+
*
|
|
5
|
+
* A WebSocket is the right connection for this — it is what the panel and the
|
|
6
|
+
* apps use by default. But an upgrade does not always survive the trip: an
|
|
7
|
+
* ingress with upgrades off, a corporate proxy, a platform that cuts long-lived
|
|
8
|
+
* connections. Polling is the fallback for exactly those, and nothing else about
|
|
9
|
+
* the protocol changes.
|
|
10
|
+
*
|
|
11
|
+
* The server's own logic is already transport-agnostic — `handleMessage` takes
|
|
12
|
+
* anything with `send(data: string)` — so a polling participant is represented by
|
|
13
|
+
* one of those with a mailbox behind it: messages the server "sends" are queued,
|
|
14
|
+
* and a `GET` drains them.
|
|
15
|
+
*/
|
|
16
|
+
/** How the protocol reaches a participant that cannot hold a socket open. */
|
|
17
|
+
export interface ICNSPollingSession {
|
|
18
|
+
/** The socket-shaped object handed to `handleMessage` / `removeClient`. */
|
|
19
|
+
readonly client: {
|
|
20
|
+
send(data: string): void;
|
|
21
|
+
readyState: number;
|
|
22
|
+
};
|
|
23
|
+
/** Messages queued since the last drain. */
|
|
24
|
+
take(): string[];
|
|
25
|
+
/** Park until a message arrives or the wait elapses; then drain. */
|
|
26
|
+
wait(timeoutMs: number): Promise<string[]>;
|
|
27
|
+
/** Last time this session was seen, for expiry. */
|
|
28
|
+
lastSeenAt: number;
|
|
29
|
+
}
|
|
30
|
+
export type TCNSPollingOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* How long a `GET` parks before answering empty. Long enough that an idle
|
|
33
|
+
* panel is not a request loop, short enough to stay under the read timeout of
|
|
34
|
+
* whatever proxy made polling necessary in the first place.
|
|
35
|
+
*/
|
|
36
|
+
waitMs?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Drop a session not seen for this long. A polling participant has no `close`
|
|
39
|
+
* event, so silence is the only signal that it is gone — and a session that is
|
|
40
|
+
* never dropped keeps its queue, and its place in the action routing, forever.
|
|
41
|
+
*/
|
|
42
|
+
sessionTtlMs?: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Handles the two polling endpoints against a {@link CNSDevToolsServer}.
|
|
46
|
+
*
|
|
47
|
+
* - `POST …?sessionId=X` — body is one protocol message, same as a socket frame.
|
|
48
|
+
* - `GET …?sessionId=X` — resolves with the messages queued for that session,
|
|
49
|
+
* parking up to `waitMs` if there are none.
|
|
50
|
+
*
|
|
51
|
+
* Returns `undefined` for a request it does not recognise, so the caller can fall
|
|
52
|
+
* through to serving the panel's assets.
|
|
53
|
+
*/
|
|
54
|
+
export declare class CNSDevToolsPollingTransport {
|
|
55
|
+
private readonly server;
|
|
56
|
+
private readonly sessions;
|
|
57
|
+
private readonly waitMs;
|
|
58
|
+
private readonly sessionTtlMs;
|
|
59
|
+
constructor(server: CNSDevToolsServer, options?: TCNSPollingOptions);
|
|
60
|
+
/** Sessions currently held. Exposed so a leak is observable in tests. */
|
|
61
|
+
get sessionCount(): number;
|
|
62
|
+
/** Deliver one message from a participant. */
|
|
63
|
+
post(sessionId: string, body: string): Promise<void>;
|
|
64
|
+
/** Drain what is queued for a participant, parking if nothing is. */
|
|
65
|
+
poll(sessionId: string): Promise<string[]>;
|
|
66
|
+
/** Forget a participant and take it out of the server's routing. */
|
|
67
|
+
close(sessionId: string): void;
|
|
68
|
+
private session;
|
|
69
|
+
/** Drop sessions that stopped polling. Silence is the only signal they give. */
|
|
70
|
+
private sweep;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=pollingTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pollingTransport.d.ts","sourceRoot":"","sources":["../src/pollingTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,4CAA4C;IAC5C,IAAI,IAAI,MAAM,EAAE,CAAC;IACjB,oEAAoE;IACpE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;CACtB;AA6CD,MAAM,MAAM,kBAAkB,GAAG;IAC7B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,2BAA2B;IAMhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAL3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;IAClE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAGjB,MAAM,EAAE,iBAAiB,EAC1C,OAAO,GAAE,kBAAuB;IAMpC,yEAAyE;IACzE,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,8CAA8C;IACxC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,qEAAqE;IAC/D,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKhD,oEAAoE;IACpE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO9B,OAAO,CAAC,OAAO;IAcf,gFAAgF;IAChF,OAAO,CAAC,KAAK;CAShB"}
|
package/dist/start.d.ts
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
|
+
import type { TCNSPollingOptions } from './pollingTransport';
|
|
2
|
+
import type { TCNSUiConfig } from './index';
|
|
3
|
+
import type { ICNSScheduledStimulationReader, TCNSDevToolsActionMiddleware } from '@cnstra/persist-dto';
|
|
1
4
|
import type { CNSPersistOptionsRegistry, ICNSStimulationRepository } from '@cnstra/persist';
|
|
2
5
|
import type { ICNS, TCNSNeuron } from '@cnstra/types';
|
|
3
|
-
export type
|
|
6
|
+
export type TCNSDevToolsStartOptions = {
|
|
4
7
|
/** Port for HTTP (UI) and WebSocket. Default: 3141 */
|
|
5
8
|
port?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Links that carry out a panel action (Retry / Clone), in order.
|
|
11
|
+
*
|
|
12
|
+
* Without one, an action is forwarded over the app's socket — which reaches
|
|
13
|
+
* every connected replica of the same `appId`, so one Retry click runs on all
|
|
14
|
+
* of them. Pass `createCNSBullActions(queue)` (or the pg-boss equivalent) and the
|
|
15
|
+
* retry goes through the broker instead, where delivery and de-duplication are
|
|
16
|
+
* its problem. The socket link is always appended last, so a run that came
|
|
17
|
+
* from somewhere else still reaches an in-process CNS.
|
|
18
|
+
*/
|
|
19
|
+
actions?: readonly TCNSDevToolsActionMiddleware[];
|
|
20
|
+
/**
|
|
21
|
+
* What the panel may offer. `{ actions: { retry: false } }` hides Retry — for
|
|
22
|
+
* a deployment where a run's record must never be overwritten in place, so a
|
|
23
|
+
* rerun has to go through Clone.
|
|
24
|
+
*/
|
|
25
|
+
ui?: TCNSUiConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Tuning for the HTTP polling fallback (always available at
|
|
28
|
+
* `/__cnstra/poll`). The socket stays the default; polling is what a client
|
|
29
|
+
* picks when an upgrade will not survive the network in between.
|
|
30
|
+
*/
|
|
31
|
+
polling?: TCNSPollingOptions;
|
|
6
32
|
/** Automatically open browser. Default: true in TTY */
|
|
7
33
|
open?: boolean;
|
|
8
34
|
/** App ID shown in DevTools. Default: 'app' */
|
|
@@ -17,8 +43,9 @@ export type StartDevToolsOptions = {
|
|
|
17
43
|
* for a fresh ephemeral in-memory store.
|
|
18
44
|
*/
|
|
19
45
|
stimulationRepository?: ICNSStimulationRepository;
|
|
46
|
+
scheduledStimulationReader?: ICNSScheduledStimulationReader;
|
|
20
47
|
};
|
|
21
|
-
export type
|
|
48
|
+
export type TCNSDevToolsHandle = {
|
|
22
49
|
url: string;
|
|
23
50
|
stop: () => void;
|
|
24
51
|
};
|
|
@@ -26,12 +53,12 @@ export type DevToolsHandle = {
|
|
|
26
53
|
* Starts the CNStra DevTools UI and connects your CNS instance to it.
|
|
27
54
|
*
|
|
28
55
|
* @example
|
|
29
|
-
* import {
|
|
56
|
+
* import { startCNSDevTools } from '@cnstra/devtools-server';
|
|
30
57
|
* import { cns } from './cns';
|
|
31
58
|
* import { registry } from './neurons/registry';
|
|
32
59
|
*
|
|
33
|
-
* await
|
|
60
|
+
* await startCNSDevTools(cns, registry);
|
|
34
61
|
* // → 🧠 CNStra DevTools: http://localhost:3141
|
|
35
62
|
*/
|
|
36
|
-
export declare function
|
|
63
|
+
export declare function startCNSDevTools(cns: ICNS<TCNSNeuron<any>>, registry: CNSPersistOptionsRegistry, options?: TCNSDevToolsStartOptions): Promise<TCNSDevToolsHandle>;
|
|
37
64
|
//# sourceMappingURL=start.d.ts.map
|
package/dist/start.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,8BAA8B,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACxG,OAAO,KAAK,EACR,yBAAyB,EACzB,yBAAyB,EAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AA6CtD,MAAM,MAAM,wBAAwB,GAAG;IACnC,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,4BAA4B,EAAE,CAAC;IAClD;;;;OAIG;IACH,EAAE,CAAC,EAAE,YAAY,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,yBAAyB,CAAC;IAClD,0BAA0B,CAAC,EAAE,8BAA8B,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAClC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAC1B,QAAQ,EAAE,yBAAyB,EACnC,OAAO,GAAE,wBAA6B,GACvC,OAAO,CAAC,kBAAkB,CAAC,CAgI7B"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cnstra/devtools-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "CNStra DevTools server (WebSocket only for now)",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/abaikov/cnstra.git",
|
|
8
|
+
"directory": "packages/devtools-server"
|
|
9
|
+
},
|
|
5
10
|
"keywords": [
|
|
6
11
|
"cnstra",
|
|
7
12
|
"devtools",
|
|
@@ -36,6 +41,7 @@
|
|
|
36
41
|
"dependencies": {
|
|
37
42
|
"@cnstra/devtools-dto": "^3.0.0",
|
|
38
43
|
"@cnstra/devtools-server-repository-in-memory": "^3.0.0",
|
|
44
|
+
"@cnstra/persist-dto": "^0.2.4",
|
|
39
45
|
"@cnstra/types": "^0.2.0",
|
|
40
46
|
"ws": "^8.18.0"
|
|
41
47
|
},
|