mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
The registry scans mounted Loader entries' dshClient declarations and
composes __DSH_BOOT__ {rev, entries} — inject edges and the immediately
mark come from manifests, never hand-copied; malformed fields fail loud
at load. The composing app owns one flat roster plus the --dev switch
(hmr row and bundle watching are dev-graph decisions).
The rebuild signal is the webserver's own observation: in dev mode the
registry stat-polls each scanned bundle (fs.watchFile; polling because
network mounts deliver no inotify), re-hashes on change, and broadcasts
a rebuilt frame on the /plugins/events SSE channel only when the rev
actually changed. Watch membership follows the table across rescans;
dispose drops all watches; a torn read self-heals on the next tick.
The POST /plugins/rebuilt endpoint is gone — builders and the host
share zero protocol. dsh web --dev logs the watched bundle list and
each rebuilt id with its rev transition.
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
/**
|
|
* `/plugins/events` SSE channel: the system-side push surface for the client
|
|
* entry graph (connect → current graph frame; dev rebuild → rebuilt frame).
|
|
* Presentation-only wire — frames never enter the session log (distinct from
|
|
* the /api/* session SSE, which is api-contract territory). Connections are
|
|
* plain node:http responses held in a set; the server's closeAllConnections
|
|
* tears them down on shutdown.
|
|
*/
|
|
|
|
import type { ServerResponse } from 'node:http'
|
|
import type { WebBootGraph } from './web-plugins.ts'
|
|
|
|
/** One `/plugins/events` frame: the full graph on connect, or one rebuilt bundle notice. */
|
|
export type PluginEventFrame =
|
|
| { type: 'graph'; graph: WebBootGraph }
|
|
| { type: 'rebuilt'; id: string; rev: string }
|
|
|
|
/** Broadcast surface owned by the webserver routing layer. */
|
|
export interface PluginEventChannel {
|
|
/** Adopt one incoming SSE request: writes the SSE preamble and the current-graph frame, then keeps the response open. */
|
|
connect(res: ServerResponse, graph: WebBootGraph): void
|
|
/** Push one frame to every open connection. */
|
|
broadcast(frame: PluginEventFrame): void
|
|
}
|
|
|
|
/** Serialize one frame as an SSE data line. */
|
|
function sseData(frame: PluginEventFrame): string {
|
|
return `data: ${JSON.stringify(frame)}\n\n`
|
|
}
|
|
|
|
/**
|
|
* Create the channel (one per running server).
|
|
* @returns the connect/broadcast surface.
|
|
*/
|
|
export function createPluginEventChannel(): PluginEventChannel {
|
|
const connections = new Set<ServerResponse>()
|
|
return {
|
|
connect(res, graph) {
|
|
res.writeHead(200, {
|
|
'content-type': 'text/event-stream',
|
|
'cache-control': 'no-cache',
|
|
'connection': 'keep-alive',
|
|
})
|
|
// Comment line on open so clients/proxies see a live channel even when
|
|
// no rebuild ever happens; EventSource frame parsing skips it naturally.
|
|
res.write(': connected\n\n')
|
|
res.write(sseData({ type: 'graph', graph }))
|
|
connections.add(res)
|
|
res.on('close', () => { connections.delete(res) })
|
|
},
|
|
broadcast(frame) {
|
|
const line = sseData(frame)
|
|
for (const res of connections) res.write(line)
|
|
},
|
|
}
|
|
}
|