4.0 KiB
@deepseek-ai/dsh-host-apiproxy
English | 中文
所有客户端形态共用的 API 网关:TS 契约(src/api/,不依赖 Node,可从浏览器导入)、fetch 载体对(src/fetch/:宿主侧的 toFetchHandler,以及客户端侧的 AbstractApiClient 与平台子类)和宿主侧实现(src/api-proxy.ts:createApiProxy 加上默认导出的 ApiProxyService 网关插件,其配置为 {provider, model, workspaceRoot?},提供 ctx.apiProxy)。该包(package)在设计上与传输方式无关,不注册任何路由;载体(目前为 HTTP,未来可以是 IPC)自行包装 ctx.apiProxy。已发布的核心组合位于 apps/cli/cordis.yml。
契约层(/api)
协议消息组成一个四象限可辨识联合:发起方 × 请求/响应,与物理通道解耦。四种消息分别是 ClientRequest(POST /api/<method> 的请求体)、ServerResponse(该 POST 的响应体)、ServerRequest(SSE 帧)和 ClientResponse(POST /api/respond 的请求体)。响应始终回显对应请求的 rpcId,绝不签发新值。方法的参数与返回值结构只存在于领域接口签名(SessionsApi、HostApi、EventsApi)中;RpcMethodMap 注册方法,其他所有位置均通过 RequestPayload<K>/ResponseValue<K> 派生。Zod schema 以 satisfies z.ZodType<Wire<T>> 锚定类型,并分两层解析:先解析信封,再解析业务载荷,随后按方法分发。业务错误由 RpcResult 的错误分支承载(RpcErrorDetailsMap 封闭错误码集合);HTTP 状态只表达载体层结果。
分层与协议决策记录在 GUI 分层与 RPC 协议 RFC中;浏览器侧消费架构记录在 Web 客户端架构 RFC中。
mux 流会在每个已附加会话的订阅基线之后,以及对应的实时原始标题事件之后,立即把基于日志的最新标题投影为经过校验的 session/title 控制帧。该投影不会把标题加入 session.list;冷会话在其中仍只有元数据,直到打开或恢复操作附加其日志。
Workspace 列表与 Session 列表是相互独立的重连基线。workspace.create 会创建唯一名称或接纳现有目录,session.create 接受可选的预分配 Session id,host/workspace-changed 与 host/session-added 则以任意到达顺序携带已提交的增量。前端 Workspace Intent 与 Session Intent 只存在于客户端,没有协议方法。
载体层(/client + 根路径)
AbstractApiClient 持有全部协议不变量:签发 rpcId、包装/解包信封、Zod 解析、SSE 帧解码、一元请求超时,以及按微任务批处理的信封观测(subscribeEnvelopes);平台子类只提供 doFetch 传输环节。InProcessApiClient 以 toFetchHandler(api) 为基础,是同构接点:它运行完整的协议序列化与校验路径而不经过网络,供 dsh -p headless 模式使用。
模型体验
无。该包定义客户端与宿主间的协议契约和载体,其中没有任何内容会进入模型请求。
KV 缓存影响
无;该包既不组装也不发送提供方请求。
已知限制与延期工作
respond路由已经发布,但待处理交互状态仍属宿主侧工作:协议形状(POST/api/respond、RpcReceipt)已经定型;使延迟或重复回答具有明确语义的待处理表位于src/api-proxy.ts,目前仍很精简(只支持问题,不支持审批)。- 预留 seam 不进入
RpcMethodMap:session.fork、prompt.mode: 'inject'、task.list、host.listModels和描述字段hostInstanceId都是已记录的预留项;未知方法会在信封解析时直接失败,而不会返回「尚未实现」错误码。 - 没有协议版本字段:客户端与宿主一同发布;只有出现独立发布的客户端后,
host.describe才会增加版本协商字段。