03 嵌入式 SDK 的进程内客户端 本节摘要:第 3 章我们讲过嵌入式 SDK 的概念——进程内用 in-memory fetch 跑同一套路由。本节从「客户端契约」角度补充一个关键细节:嵌入式 SDK 复用的是同一个生成的效应客户端,只是换了传输介质。这种「同一客户端,不同传输」的设计让嵌入式和网络形态行为完全一致。理解了本节,你才完整掌握 codegen 与嵌入式的关系。 一、回顾:嵌入式 SDK 的核心机制 第 3 章讲过,嵌入式 SDK 的核心是「同一套路由 + in-memory fetch」: 它跳过了网络,但保留了 HTTP 编码边界。这是从「服务端路由」侧看的。 本节从「客户端」侧看——那个被注入 in-memory fetch 的客户端,到底是什么?
本节摘要:第 3 章我们讲过嵌入式 SDK 的概念——进程内用 in-memory fetch 跑同一套路由。本节从「客户端契约」角度补充一个关键细节:嵌入式 SDK 复用的是同一个生成的效应客户端,只是换了传输介质。这种「同一客户端,不同传输」的设计让嵌入式和网络形态行为完全一致。理解了本节,你才完整掌握 codegen 与嵌入式的关系。
第 3 章讲过,嵌入式 SDK 的核心是「同一套路由 + in-memory fetch」:
服务端路由 ──► Web Handler ──► 包成 in-memory fetch ──► 注入客户端
它跳过了网络,但保留了 HTTP 编码边界。这是从「服务端路由」侧看的。
本节从「客户端」侧看——那个被注入 in-memory fetch 的客户端,到底是什么?答案是:就是 codegen 生成的效应客户端(第 02 节),只是它的 HTTP 传输层换成了 in-memory fetch。
这是本节的关键认知。效应客户端(以及网络形态用的客户端)和嵌入式 SDK 用的客户端,是同一个生成的客户端。差别只在「传输层」:
| 形态 | 客户端 | 传输层 |
|---|---|---|
| 网络形态(serve) | 生成的效应客户端 | 真实 HTTP(经网络) |
| 嵌入式 SDK | 同一个生成的效应客户端 | in-memory fetch(进程内) |
生成的效应客户端(同一份代码) │ ├─ 网络形态:配真实 fetch ──► 经网络调 serve │ └─ 嵌入式形态:配 in-memory fetch ──► 进程内调路由
客户端代码一行不改,只换它底层的「fetch 实现」。这就是「同一客户端,不同传输」。
这个设计带来一个巨大的红利:嵌入式和网络形态行为完全一致。
为什么一致?因为客户端是同一个(调同样的方法、同样的编解码),路由也是同一个(第 3 章),唯一不同的是传输介质(内存 vs 网络)。传输介质不影响行为——HTTP 编码边界都被保留了。
这意味着:
💡 一致性的价值:开发时用嵌入式(快,无网络开销),部署时用网络形态(跨进程)——代码不用改。这种「开发用快形态、部署用网络形态」的模式,是 codegen + 嵌入式设计带来的工程红利。
虽然客户端相同,但嵌入式形态的路由有个小特殊——它无密码:
网络形态路由:有密码(防外部攻击) 嵌入式形态路由:无密码(同进程可信)
为什么无密码?因为嵌入式是同进程内调用,不存在「外部攻击者」威胁——调用方就是你自己进程里的代码。所以省掉密码层。
但注意:鉴权、编解码这些边界还在(第 3 章)。无密码只是省了「外部认证」这一层,内部的权限/编解码照常。所以嵌入式不是「裸奔」,只是「省了防外部的认证」。
除了复用客户端,嵌入式 SDK 还额外注册一些嵌入式专属能力(通过工具注册器)。这些能力是「嵌入式形态特有的」,网络形态没有。
为什么有专属能力?因为嵌入式形态跑在宿主进程里,宿主可能想给它一些「进程级」的能力(如访问宿主的某些资源)。这些能力通过注册器注入,只在嵌入式形态可见。
把本节和前面合起来,看一个典型的「嵌入式集成」场景:
你做一个 Node/TS 应用,想内嵌 OpenCode │ ▼ 用嵌入式 SDK create() │ ▼ 内部: ├─ 构建应用层(工具、权限) ├─ 创建嵌入式路由(无密码) ├─ 转成 Web Handler ├─ 包成 in-memory fetch ├─ 用这个 fetch 配置「生成的效应客户端」 └─ 返回可用的客户端实例 │ ▼ 你的应用用这个客户端调 API │ ▼ 调用经 in-memory fetch 到路由,完全在进程内
整个过程你的应用只看到一个「正常的客户端」,不知道它是 in-memory 的。这是 codegen + 嵌入式的无缝体验。
客户端和嵌入式都讲清了,最后一节讲贡献——你想改 OpenCode 时该怎么做。