用途:capnweb 是本仓库中 Durable Object 与 computerd 之间的 RPC 框架,属于对象能力(object-capability)RPC 系统,支持 promise 流水线、结构化克隆式 stub 传输和双向调用。线上格式为长连接 WebSocket 上的文本 JSON,这是唯一载体;HTTP 批量传输无法传递调用返回的流,而该接口的每次读取都是流。
主要能力与位置:packages/rpc/src/interface.ts 定义类型化线上接口,WorkspaceRPC 为根 stub,组合 SyncRPC 与 ShellRPC,新方法应先在此添加;server.ts 为基于 Database 的实现;client.ts 提供 WebSocket 上的类型化 stub,Durable Object 使用延迟传输以便在升级完成前创建 stub;sync-driver.ts 提供 pullOnce、pushOnce、tick,内部处理释放;debug.ts 提供 enableStubTracking、stubSnapshot 用于排查泄漏。设计意图见 docs/08_capnweb_interface.md,释放契约见 docs/11_lifecycle.md。
核心规则:stub 即能力,持有即有权调用远端对象。stub 不会跨网络被垃圾回收,不显式释放会在连接另一端泄漏资源;由于本仓库连接是长生命周期的,未释放的 stub 会一直存活到连接断开。调用方负责释放所有 stub:参数中传入的 stub 仍归调用方所有,被调用方收到的是副本,调用完成后由 RPC 系统自动释放;结果中返回的 stub 所有权转移给调用方,必须由调用方释放;结果信封始终带有 disposer,即使看似不含 stub 也应释放。
释放方式:优先使用 using 声明,作用域退出时自动释放;其次 try/finally 中调用 resultSymbol.dispose;所有权跨边界时显式释放。仓库约定:驱动拥有其 stub,pullOnce 与 pushOnce 会释放所触及的每个信封;直接流式调用(fetchChanges、fetchObjects、shell.exec、shell.getExec)由调用方拥有信封,排空流不会释放信封本身;client.close() 会释放根 stub,不要自行拆除底层 WebSocket。不要仅为检查而 await 一个 stub。需要保留副本时用 stub.dup(),底层目标在所有副本释放后才销毁;.dup() 也可用于 stub 或 promise 的属性。服务端可声明 Symbol.dispose,在所有指向该目标的 stub 释放后调用一次;同一目标多次传入会得到多次释放调用,可用 new RpcStub(target) 包装合并。stub.onRpcBroken(cb) 在 stub 不可用时触发。
其他要点:RpcPromise 也是其最终结果的 stub,除非本地需要值否则不要 await;属性访问返回的 RpcPromise 没有独立 disposer。.map() 在远端解析上运行同步回调,一次往返完成,回调必须同步且无副作用,捕获的 stub 会随录制发送给对端。ReadableStream 是一等值,线上不内联 blob 字节,变更流携带内容寻址的 (hash, size) 记录,接收方通过 hasObjects/pushObjects 回取缺失部分。
限制与注意:不要发送二进制 WebSocket 帧;不要直接拆除底层载体;修改 WorkspaceRPC 接口需同步更新两端;TypeScript 的 private 不会对 RPC 隐藏,需用 # 前缀;不要依赖垃圾回收清理 stub。测试泄漏可用 enableStubTracking 与 stubSnapshot,生命周期相关改动可运行 script/computerd-stub-soak 并检查 session.getStats() 漂移。