* chore(serve): remove the /demo debug page The daemon has shipped a real browser UI for a while: `resolveWebShellDir()` finds the bundled Web Shell assets and `mountWebShellAssets()` serves them at `/`, so `qwen serve` already opens onto a full client. `/demo` stayed behind as a 663-line inline-HTML console covering the same ground with none of the reach — nobody drives the daemon through it, and `npm run dev:daemon` starts the Web Shell dev server rather than the demo page. Keeping it around costs more than the dead code. It is the only file in the tree that pairs an event log with daemon HTTP, so work that starts as a Web Shell observation lands there instead: #8762 was found while running `/review` through the Web Shell and was fixed entirely inside the demo page's rendering, with "no Web Shell changes" in its own risk note. Deleting the page removes that decoy. Nothing is lost for protocol-level debugging: `GET /session/:id/events` streams the same raw frames the Events tab printed. `/health` shared `routes/health-demo.ts` with the demo handler, so the module is now `routes/health.ts` / `createHealthRoutes()` and drops its `getPort` dependency. The rate-limit exemption, the boot breadcrumb, and the daemon docs lose their `/demo` arms; the loopback self-origin shim regression test already asserted through `/health` and only needed its title corrected. * test(serve): pin the removed /demo contract and the pre-auth surface Review follow-up. Three of the removal hunks shipped ungated, and two doc sentences the removal rewrote were describing the pre-auth surface wrong — both before and after the edit. Deleting the `/demo` route took its assertions with it, so nothing failed if the handler came back: the Web Shell suite only exercised a generic deep link, and the rate-limit exemption could be widened again with the suite still green. `/demo` is now pinned as what it became — an ordinary unknown path: a non-navigation request 404s, a browser navigation is answered by the SPA fallback like any other deep link, and once a token is configured (with or without `--require-auth`) that navigation is refused with 401, because the fallback sits behind the bearer. The rate-limit test pins that `/health` is the only exempt GET, so re-adding a second pre-auth page to the predicate fails instead of silently escaping the limiter. Each new assertion was checked by reverting the hunk it guards and confirming it goes red. The `--allow-origin '*'` warning and both `--allow-origin` doc paragraphs enumerated `/health` as the residual tokenless surface and said nothing about the Web Shell static assets, which are mounted before the bearer in every launch mode and stay reachable even under `--require-auth` — the enumeration also claimed `/health` stays pre-auth on non-loopback binds, where it is registered behind the bearer and 401s. A probe across all three launch modes established the actual matrix; the warning and the docs now match it and name `--no-web` as the way to remove the residual browser surface. The warning text is asserted by a test for the first time. * fix(serve): correct Web Shell doc claims and re-pin the pre-auth CORS wall Review follow-up. The removal rewrote the daemon docs around the Web Shell, and three of the rewritten claims did not match what the runtime actually does: §1 never said how the bearer reaches the browser (with auth on, the plain URL loads a shell whose every API call 401s), §8 called the shell writable on any bind (on a non-loopback bind without `--allow-origin` its POSTs hit the CORS wall and 403), and §8 served `/session/:id` without the document-navigation qualifier its own code enforces. The §9 call-chain diagram also still listed the deleted `/demo` route, the developer flag references had no `--web`/`--no-web` row despite the new guidance pointing at the flag, and both design docs listed the JSON body parser ahead of post-auth `/health` while `createServeApp()` registers them the other way round. The deleted `/demo` CORS test was also the only assertion that a pre-auth page sits behind the Origin wall — every surviving Origin test targets an API path. Re-pin it for the shell root so a mount-order regression fails instead of exposing the pre-auth HTML surface cross-origin. * fix(serve): finish demo rename sweep and scope pre-auth shell claims to loopback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2.9 KiB
serve server.ts staged split
Goal
Split packages/cli/src/serve/server.ts in stages without changing daemon behavior. The first stage extracts shared helpers and route groups whose boundaries are already clear, while keeping createServeApp() responsible for wiring middleware, stateful dependencies, transport mounts, and final error handling.
Middleware And Route Order
The app assembly order is part of the public behavior and must stay stable:
- same-origin
Originstripping - CORS and host allowlist
- pre-auth
/healthon allowed loopback setups - access logging
- Web Shell static assets
- bearer auth
- rate limit
- post-auth
/healthwhen required - JSON body parser and JSON parser error mapper
- daemon telemetry
- REST route groups
- ACP HTTP and WebSocket routes
- Web Shell fallback
- final error handler
Extracted Boundaries
server/request-helpers.ts owns request-body sanitization, client-id parsing, loopback detection, path/query validators, and permission vote body parsing. Route modules depend on this file instead of importing from server.ts.
server/error-response.ts owns bridge error taxonomy and HTTP response mapping. The exported wrappers accept an optional daemon logger so route modules can keep the existing stderr and daemon-log behavior.
server/session-list.ts owns the persisted-plus-live session list merge used by both REST and ACP HTTP callers.
server/fs-factory.ts owns default workspace filesystem factory construction and fs audit warning emission.
server/telemetry.ts owns route classification and daemon HTTP telemetry middleware.
server/prompt-deadline.ts owns prompt deadline resolution and its abort sentinel class.
Route modules follow the existing registerXRoutes(app, deps) style. They receive only the dependencies they need, not a single god context.
Non-goals
This stage does not change response bodies, status codes, headers, SSE frame format, authentication order, or error taxonomy. It does not delete compatibility re-export shims such as status.ts, event-bus.ts, or in-memory-channel.ts. It does not rename historical docs or cleanup unrelated camelCase paths.
server.ts may remain over 200 lines after this stage. The acceptance criterion is stable boundaries that make later session and SSE extraction mechanical.
Audit Notes
Round 1 checked architecture boundaries and rejected a new Router abstraction because existing route modules already use direct registerXRoutes(app, deps) functions.
Round 2 checked failure paths and kept error taxonomy in one helper so route extraction cannot silently drift HTTP status codes.
Round 3 checked compatibility and keeps the public exports consumed by run-qwen-serve.ts, ACP HTTP dispatch, and tests.
Round 4 checked testing strategy and relies on focused server.test.ts, ACP HTTP, and route tests because this is structural refactoring with no user-visible behavior change.