Kilo 开源编码代理的 API 客户端生成体系从 Effect HttpApi 契约到 Promise/Effect 双入口的opencode-ai/client【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocodeopencode-ai/client是 Kilokilocode仓库中面向调用方生成的 TypeScript API 客户端包。它以 OpenCode 服务端权威的 EffectHttpApi契约为唯一事实来源通过代码生成同时产出「零依赖 Promise 客户端」与「基于 Effect 的富网络客户端」两个入口并借助生成等价性测试防止传输层漂移。读完本文你将掌握该客户端包的生成链路、双入口设计动机、请求/SSE/错误处理实现细节以及如何在业务代码中直接构造规范化输入发起会话、消息、权限、PTY 等全量 HTTP 调用。包定位私有生成目标而非手写 SDKpackages/client/README.md 开篇即点明该包的定位它是「Private generation target for clients derived directly from OpenCodes authoritative EffectHttpApi」。换句话说这个包的全部客户端表面client surface不是手写的而是由构建编译器根据服务端 HTTP API 契约自动生成的仓库内该包自身即标注为private: true见 packages/client/package.json。这意味着任何对服务端路由、参数、返回类型的修改只要落在 EffectHttpApi契约上客户端都会自动同步无需人工维护两份接口定义。README 明确给出了开发者的工作流修改契约后运行bun run generate重新生成客户端运行bun run check:generated检测已提交的生成产物是否与最新契约漂移该脚本在生成后执行git diff --exit-code -- src/generated src/generated-effect见 packages/client/package.json。双入口设计Promise 根入口与 Effect 子路径包通过exports字段暴露两个语义不同的入口packages/client/package.json入口说明运行时依赖opencode-ai/client也等价于./promise零 Effect 依赖的 Promise 客户端基于fetch仅标准 Web API无 Core / Effect 运行时opencode-ai/client/effect富 Effect 网络客户端使用环境提供的HttpClient仅依赖 Effect、Schema、Protocol浏览器打包安全两个入口分别对应两个生成产物目录src/generated/与src/generated-effect/。根入口的src/index.ts只是薄薄一层转发export * from ./generated/index并补充导出OpenCodeEvent事件类型外加一个用于兼容上游 session-ui 旧版 Promise 客户端类型的FileDiffInfo联合类型packages/client/src/index.ts。Effect 入口src/effect.ts除了转发generated-effect之外还集中重导出了opencode-ai/schema下的全部领域数据类型Agent、Session、Prompt、Location、Pty、ProjectCopy等并导出 Protocol 的OpenCodeEvent事件类型packages/client/src/effect.ts。这样调用方只依赖客户端表面即可拿到全部类型不必直接引用 schema 包。契约的权威来源与客户端本地投影Server 的权威 APIREADME 指出「The build compiler readsopencode-ai/server/api」即生成器编译时读取服务端导出的权威Api。测试 packages/client/test/contract-identity.test.ts 验证了权威契约的身份Api.groups[server.session].identifier为server.session且Object.keys(ClientApi.groups)与Object.keys(Api.groups)完全一致。客户端本地契约投影由于客户端运行时不允许依赖 Core / Server保证包体积与可移植性Effect 入口实际使用的是「基于 Protocol 构建的客户端本地投影」ClientApi定义在 packages/client/src/contract.ts。它通过makeDefaultApi构造并声明了客户端自身的两个中间件LocationMiddleware位置location相关请求的中间件服务SessionLocationMiddleware会话位置校验中间件声明了可能抛出的错误类型InvalidRequestError与SessionNotFoundError。同一文件还定义了生成期使用的三张映射表groupNames将服务端分组标识如server.health、server.session、server.pty映射为客户端方法名health、sessions、ptys——共 18 个分组endpointNames将特殊端点标识映射为语义化方法名如session.messages→list、integration.connect.key→connectKey、question.request.list→listRequestsomitEndpoints明确从通用 HTTP 客户端中剔除的端点集合{fs.read, pty.connect, pty.connectToken}。生成等价性测试杜绝传输漂移README 强调「a generation-equivalence test preventing transport drift」。这一保障落地在 packages/client/test/contract-identity.test.ts测试分别用compile(Api, …)编译服务端契约、用compile(ClientApi, …)编译客户端投影然后断言emitPromise(client)与emitPromise(server)完全相等——即客户端投影最终生成的 Promise 客户端表面必须与服务端契约生成的表面逐字节一致。同一测试文件还校验了「权威值复用」Core 与 Server 复用的是同一份 Schema / Protocol 值例如AgentV2.ID Agent.ID、CoreLocation.Ref Location.Ref、CorePrompt Prompt并验证Session.ID.create()以ses_前缀、Workspace.ID.create()以wrk_前缀、Project.ID.global global、Provider.ID.anthropic anthropic等事实packages/client/test/contract-identity.test.ts。此外测试确认共享 DTO 构造与解码得到的是普通对象原型为Object.prototype保证跨运行时序列化的纯粹性。零依赖 Promise 客户端内部实现剖析生成产物 packages/client/src/generated/client.ts 是 Promise 客户端的完整实现对外暴露make(options: ClientOptions)工厂函数返回按 18 个分组组织的扁平方法树。配置项export interface ClientOptions { readonly baseUrl: string readonly fetch?: typeof globalThis.fetch readonly headers?: HeadersInit } export interface RequestOptions { readonly signal?: AbortSignal readonly headers?: HeadersInit }baseUrl必填fetch与headers可覆盖全局默认内部为options.fetch ?? globalThis.fetch。每个端点方法还接受可选的RequestOptions用于注入AbortSignal与请求级headers。请求流水线内部以RequestDescriptor描述每个端点HTTP 方法、路径模板、query、headers、body、成功状态码、声明状态码集合与是否空响应体。make()内部依次实现三层prepare用new URL(descriptor.path, options.baseUrl)拼接 URL将 query 对象递归展开进searchParams数组值逐项追加合并全局 headers 与请求级 headers并在有 body 且未显式设置时自动写入content-type: application/jsonbody 统一JSON.stringifyexecute真正调用fetch任何网络异常都被包装为ClientError(Transport, { cause })request按successStatus判定成功若响应状态落在declaredStatuses如 400、401、404、503 等中则抛出生成的服务端错误对象否则取消响应体并抛出ClientError(UnexpectedStatus, { cause: { status } })。empty: true的端点如switchAgent、interrupt、compact不会解析 JSON。内建 SSE 客户端事件流端点sessions.events、events.subscribe不走普通request而是由内部sse函数处理校验text/event-stream内容类型后逐块读取归一化\r\n/\r换行按\n\n边界切分事件块仅提取data:前缀行并JSON.parse后逐条yield。实现还包含多项健壮性保护缓冲上限 1 MiB超出抛MalformedResponse、流中断时reader.cancel()与releaseLock()清理、非法 JSON 包装为ClientError(MalformedResponse)。调用方因此可以for await消费事件流const client make({ baseUrl: https://opencode.example }) for await (const event of client.sessions.events({ sessionID })) { // 逐条处理会话事件 }覆盖的完整分组从 packages/client/src/generated/client.ts 可以看到生成表面覆盖服务端全部标准 HTTP 分组health、location、agents、sessionslist/create/active/get/switchAgent/switchModel/prompt/compact/wait/stage/clear/commit/context/history/events/interrupt/message 共 17 个端点、messages、models、providers、integrations含 connectKey/connectOauth 与 attempt 三件套、credentials、permissions、fileslist/find、commands、skills、events、ptyslist/create/get/update/remove、questions、references与projectCopies。README 特别提醒PTY 的 WebSocket 连接pty.connect/pty.connectToken属于自定义传输仍留在通用 HTTP 客户端之外这也正是omitEndpoints存在的原因。Effect 客户端实现从 HttpApiClient 到适配层packages/client/src/generated-effect/client.ts 展示了 Effect 入口的实现方式。其核心只有一步export const make (options?: { readonly baseUrl?: URL | string }) HttpApiClient.make(ClientApi, options).pipe(Effect.map(adaptClient))即直接基于 Effect 的HttpApiClient工厂构造RawClient再用adaptClient把 Effect 原生返回的「分组 → 端点函数」树适配为与 Promise 客户端一致的扁平命名adaptGroup0…adaptGroup17。每个端点包装都统一做了两件事Effect.mapError(mapClientError)把HttpClientError、Schema 解码错误、SSE 重试错误统一映射为ClientError其余错误原样透传对包装在{ data }中的响应create/prompt/get/active 等额外Effect.map((value) value.data)解包使调用方直接拿到领域对象。事件流端点如session.events、event.subscribe在 Effect 侧被映射为Stream先Stream.unwrap打开底层HttpApiClient流再对整条流Stream.mapError(mapClientError)归一化错误调用方可以用 Effect 的Stream.runForEach等算子消费。规范化输入使用 Effect 入口的完整示例README 给出的 Effect 消费者示例完整展示了「构造规范化解码输入」的用法import { AbsolutePath, Location, OpenCode, Prompt } from opencode-ai/client/effect const client yield * OpenCode.make({ baseUrl: https://opencode.example }) yield * client.sessions.create({ location: Location.Ref.make({ directory: AbsolutePath.make(/workspace) }), }) yield * client.sessions.prompt({ sessionID, prompt: Prompt.make({ text: Hello }) })要点在于Location.Ref、AbsolutePath、Prompt这些「规范解码值」来自轻量级opencode-ai/schema包通过 packages/client/src/effect.ts 的集中重导出对外暴露。因此调用方构造请求时无需关心 schema 包的内部组织且当 schema 内部模型重组时由于客户端表面保持导出不变调用方代码无需迁移README 明确这是保留这些导出的设计意图。由于make返回的是 Effect实际调用需要运行环境典型用法是在Effect.gen中以yield *获取 client或将OpenCode.make放入依赖注入上下文effect在 packages/client/package.json 中作为可选 peer dependency4.0.0-beta.83使用该入口的项目需自行安装匹配版本。依赖边界与浏览器打包安全README 用两条硬约束概括了该包的依赖纪律Promise 根入口保持结构化structural没有任何 Core 或 Effect 运行时依赖——它只依赖标准fetch、URL、Headers、AbortSignal因此可以在任意现代运行时直接使用/effect入口只依赖 Effect、Schema、Protocol且浏览器打包安全——它不 import Core 或 Server这正是要用ClientApi本地投影替代服务端Api的根本原因。这两条 import 图约束由「bundle-boundary tests」在测试中强制校验README 原文Bundle-boundary tests enforce both import graphs确保未来演进中不会有人无意引入破坏性的运行时依赖。结合契约身份测试客户端包的三大质量支柱可以总结为契约等价同一生成表面、依赖纯净无 Core/Server 泄漏、运行时可移植浏览器与 Node 皆可用。本地开发与验证命令在 packages/client 目录下仓库使用 bun 作为包管理器命令作用bun run generate读取opencode-ai/server/api契约并重新生成src/generated与src/generated-effect两个产物目录bun run check:generated重新生成后对产物做git diff --exit-code检测已提交产物是否与契约漂移bun test运行契约身份、依赖边界等单元测试超时 5sbun run typecheck使用tsgo --noEmit做类型检查综上opencode-ai/client是 Kilo 生态中「契约驱动生成 双运行时适配」范式的代表实现开发者只需维护服务端 EffectHttpApi这一份权威契约即可同时获得可移植的 Promise 客户端与类型安全、错误归一化的 Effect 客户端且二者表面形状由测试锁定、永不漂移。【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
