Module Structure
Server 是 Web 与自动化客户端的协议适配层:它拥有活动 run、Runtime residency、SSE 订阅者和 Web Browser Session,但 durable transcript 与任务事实仍由 Session 服务持有。
Directory Layout
packages/cli/src/server/server.ts— Hono 装配、认证/CORS、静态资源和网络生命周期packages/cli/src/server/routes/session.ts— Session、run、消息、交互和 SSE 主控制器packages/cli/src/server/routes/task.ts— 顶层任务提交、重试、diff 与交付 APIpackages/cli/src/server/routes/events.ts— 跨 Session 的看板安全全局事件流packages/cli/src/server/routes/terminal.ts— Bun/Node WebSocket PTY 适配packages/cli/src/server/routes/— 配置、Provider、MCP、插件、Hooks 等管理 APIpackages/cli/src/server/OrderedSseEgress.ts— replay/live 原子切换与有界写入packages/cli/src/server/sessionRef.ts— Session 复合身份规范化packages/cli/src/server/WebBrowserSessionRegistry.ts— Web 专用 Browser Runtime 所有权
Key Entry Points
BladeServer.listenAsync()inpackages/cli/src/server/server.ts— 启动 Bun 或 Node 服务createSessionRouteController()inpackages/cli/src/server/routes/session.ts— 创建路由与 Runtime 管理器dispatchTask()onSessionRouteController— Web、定时任务共用的任务派发入口OrderedSseEgress.finishInitialization()inpackages/cli/src/server/OrderedSseEgress.ts— replay 到 live 的切换点resolveSessionRef()inpackages/cli/src/server/routes/session.ts— 解析精确 Session 所属工作区
Gotchas
sessionId不是 Server 内的完整身份;路由、活动 run、Runtime、锁和 Browser Registry 必须使用规范化的projectPath + sessionId,同名多工作区且未给路径时应返回 409 而不是猜测 (packages/cli/src/server/sessionRef.ts,packages/cli/src/server/routes/session.ts)- Session SSE 初始化必须先订阅 Bus 并缓冲 live,再写 connected、按 JSONL
seq回放、去重排序后切到 live;先回放再订阅会产生不可修复的事件空窗 (packages/cli/src/server/routes/session.ts,packages/cli/src/server/OrderedSseEgress.ts,git:121ea8fc) - 只有 committed event 能携带 SSE
id;ephemeral delta 在 replay 窗口直接丢弃,不能推进Last-Event-ID(packages/cli/src/server/bus.ts,packages/cli/src/server/OrderedSseEgress.ts) EventSource首次连接不能自定义Last-Event-IDheader,因此 Server 同时接受lastEventIdquery;删除 query 支持会破坏页面刷新后的 durable resume (packages/cli/src/server/routes/session.ts)- Session SSE 与
/events全局 SSE 不可互换:全局流只投影白名单化的看板字段,明确排除 prompt 和私有执行细节 (packages/cli/src/server/routes/events.ts) - 慢 SSE subscriber 的 overflow、写超时或 sequence regression 只终止该 subscriber,不能取消 server-owned Agent run 或影响其他订阅者 (
packages/cli/src/server/OrderedSseEgress.ts,docs/reference/surface-egress.md) - 活动 run 收到新 message 时走 durable steering/follow-up,不启动第二个 run;同时切换模型、权限、reasoning、tier、verbosity、style 或 output schema会返回冲突 (
packages/cli/src/server/routes/session.ts) - pending permission 既可能在内存 run 中,也可能只剩磁盘交互记录;响应路由必须先匹配精确 run,再通过
SessionInteractionService.respondAndRecover()冷恢复 (packages/cli/src/server/routes/permission.ts,packages/cli/src/server/routes/session.ts) - API 未设置
BLADE_SERVER_PASSWORD时整体无认证;启用 Basic Auth 后根页面与静态资源仍公开,只有 API 路径受保护 (packages/cli/src/server/server.ts,packages/cli/src/commands/serve.ts) - CORS 默认只放行 localhost、127.0.0.1 和 Tauri origin,额外来源必须通过
--cors;监听0.0.0.0不会自动放宽 CORS (packages/cli/src/server/server.ts,packages/cli/src/cli/network.ts) - Node 与 Bun 的 PTY/WebSocket 适配不同,但最后一个终端 subscriber 断开时都会杀掉 PTY;Terminal 面板重连不会保留无人订阅的进程 (
packages/cli/src/server/routes/terminal.ts) - 删除 Session 时还要释放 Web Browser Runtime、活动 review/run、Runtime residency 和任务 worktree;只删 transcript 会留下进程与浏览器资源 (
packages/cli/src/server/routes/session.ts,packages/cli/src/server/WebBrowserSessionRegistry.ts)
Architecture
server.ts创建一个 SessionRouteController,并把同一 controller 注入 TaskRoutes 和 TaskScheduler,使 HTTP 手工任务、Web 操作和 schedule 复用相同准入与恢复逻辑 (packages/cli/src/server/server.ts)- SessionController 分开管理 hydrated Session、active/recent run、Runtime initialization/disposal 与 residency lease;并发请求通过按 SessionRef 分片的 mutex 串行化消息和交付 (
packages/cli/src/server/routes/session.ts) - Runtime residency 只缓存可驱逐的 idle Runtime;active turn、pending interaction 或其他 pin 会阻止驱逐,容量满时映射为带资源详情的 429 (
packages/cli/src/server/routes/session.ts,packages/cli/src/server/error.ts) Bus是进程内扇出,不是持久消息队列;durable replay 始终从SessionEventLog读取,Bus 只承载当前进程 live 事件 (packages/cli/src/server/bus.ts,packages/cli/src/server/routes/session.ts)- BrowserRoutes 在 SessionController 下共享精确 SessionRef 和全局 Browser admission,但 Web 测试浏览器由独立 registry 持有,不复用 Agent 的 browser runtime (
packages/cli/src/server/routes/browser.ts,packages/cli/src/server/WebBrowserSessionRegistry.ts) - Hono 顶层
onError将容量和已分类BladeServerError保留为稳定状态码,其余错误收敛为 500 JSON envelope (packages/cli/src/server/server.ts,packages/cli/src/server/error.ts)
Decisions
- Server 将 run 生命周期与 HTTP 请求解耦,请求返回 202 后由 Runtime 持续执行;SSE viewer 断开不等于取消任务,显式 abort 路由才改变 run (
packages/cli/src/server/routes/session.ts) - 静态资源使用内存原文/压缩缓存,Brotli 与 gzip 按 q 值选择,hash asset 长缓存而
index.htmlno-cache,以支持单进程直接托管 Web build (packages/cli/src/server/server.ts) - 全局事件流采用字段级投影而不是透传 BusEvent,避免多项目任务看板获得 Session prompt、工具参数或私有结果 (
packages/cli/src/server/routes/events.ts) - Bun
listen()是同步专用入口,跨运行时命令使用listenAsync();Node fallback 只在异步入口组装 HTTP 与wsupgrade (packages/cli/src/server/server.ts)
Patterns
- 每个写 API 先解析 TypeBox schema,再解析精确 SessionRef,再进入 keyed lock/Runtime lease;新增写端点应保持该顺序以避免校验失败后占用运行资源 (
packages/cli/src/server/routes/session.ts,packages/cli/src/server/routes/task.ts) - Session run 的终态顺序是刷新 durable metadata、发送
session.completed/session.error、发送 idle/error status,最后释放 admission、Agent 和 Runtime lease (packages/cli/src/server/routes/session.ts) - Server 输出的工具 metadata 经过 allowlist 投影,展示正文再按表面字符预算裁剪;Browser 诊断与 shell background 元数据另有严格字段校验 (
packages/cli/src/server/routes/session.ts,packages/cli/src/tools/display/ToolResultProjector.ts) - 服务关闭是幂等 single-flight:停止网络接入、关闭 SessionController、停止 scheduler/GC、重置 workspace 资源,并保留首个清理错误 (
packages/cli/src/server/server.ts) - Session Browser Registry 在从 Map 删除引用后再 dispose,批量关闭使用
allSettled回收全部 Runtime 后才抛首个错误 (packages/cli/src/server/WebBrowserSessionRegistry.ts)
Dependencies
- HTTP/SSE 使用 Hono,终端 WebSocket 使用 Bun WebSocket 或
ws+ Node upgrade,PTY 使用bun-pty或node-pty;跨运行时修改必须验证两条启动路径 (packages/cli/src/server/server.ts,packages/cli/src/server/routes/terminal.ts)