Files
deepseek-harness/packages/host/webserver/README.md
T
Turtle 2ee2ee2f96 refactor(webserver): extract SPA dist serving to the frontend-static fallback seat
The webserver's built-in static dist serving becomes a single-owner fallback
seat (registerFallback/applyIndexTaps); the SPA server moves to the new
@deepseek-ai/dsh-frontend-static plugin so the composing application owns its
dist as composition, not carrier config. distIndex leaves the webserver
schema; unclaimed fallback answers 404.
2026-08-06 04:39:52 +08:00

4.0 KiB

@deepseek-ai/dsh-host-webserver

English | 中文

Web HTTP and upgrade-route registration plugin (default-exported HttpServerService, config {host, port}): a node:http server that listens on activation and provides ctx.httpServer. register(route) adds a named exact/prefix HTTP route; registerUpgrade(route) adds an upgrade route for an exact pathname. A duplicate path within either table throws because route patterns are a composition-level contract and a collision is a misconfiguration; both methods return a disposer that removes the registration. registerFallback(handler) claims the single fallback seat answering everything no named route matches — one owner only (a second claim throws; the SPA dist server dsh-frontend-static is the shipped owner), 404 while unclaimed. tapIndex(transform) adds an index.html transform, and applyIndexTaps(html) runs a body through the registered transforms in order — the fallback owner calls it on every index response. port reads the listening port (the OS-assigned value when port is 0), and host reads the configured bind host (composition-time facts other plugins adapt to, e.g. the directory-picker chooser). HTTP match order is fixed: exact over the whole table, then longest prefix, then the fallback seat. Upgrades match exactly and unmatched connections are closed; registration order carries no request-facing semantics.

The package knows no harness concepts and serves no files: the /api HTTP bridge and downlink WebSockets are routes owned by the connection plugin, plugin bundles and the HMR event stream are routes owned by the modules/hmr plugins, and dist serving belongs to the fallback owner. The upgrade handler owns the protocol handshake and connection contents; the webserver only delivers the raw socket and request. host accepts only 127.0.0.1 (default posture) and 0.0.0.0 (deliberate network exposure). Web (browser) shape only — Electron loads dist over file:// and carries fetch over an IPC bridge, not this server. This package never prints; the URL line belongs to the shell.

A listen failure (EADDRINUSE…) throws out of activation and rejects Loader composition with the bind diagnostic; the failed candidate fiber is disposed. An HTTP request whose handling throws (a fallback owner's decodeURIComponent on a malformed %-escape, a client dropping mid-body) is answered 400 — or the socket destroyed when headers are already out — and logged as a warning; it never exits the process. An upgrade-handler exception or upgraded-socket transport error is logged as a warning and destroys its socket. Disposal starts close() and closeAllConnections(), destroys every tracked upgraded socket, and returns only after the HTTP server and those sockets have closed.

In development, the client-plugin registry synchronously captures each built bundle's stat baseline before it returns, then polls those baselines and re-hashes changed content. Each rescan stages its candidate table, graph, and watch map before publishing them, so a baseline failure preserves the prior graph. An immediate rebuild therefore cannot disappear into an asynchronously established watch baseline; a rename window marks the path dirty, retains the last successful baseline, and forces a re-hash when the bundle reappears even with identical metadata.

Model Experience

None, as the package is a Web carrier between the browser and the HTTP/upgrade routes other plugins register; nothing here reaches a model request.

KV Cache effect

None; this package neither assembles nor sends a provider request.

Known Limitations and Deferred Work

  • No TLS, auth, or origin policy — binding a non-loopback address exposes the server to that network; deployment hardening (or fronting it with a real reverse proxy) is deliberately out of scope for the dev-facing v1.
  • Socket options are fixed — config selects the bind host and port, while backlog and other socket settings remain internal until a deployment needs them.