dsh-opencode-session
已弃用 / Deprecated(2026-11-10)
在本项目验证过的 DSH 0.2.2-rc.1 +
@earendil-works/pi-ai1.5.0 组合上,pi-ai 已经对 所有baseURL主机名为opencode.ai的 provider 自动注入x-opencode-session, 包括手写的opencode-go-extra路由。因此本插件不再是必要条件:仓库只读归档,不再发布 新版本,也不建议新装。
- 判定发生在 pi-ai 的通用路径里,与 provider id 无关:
isOpenCode()同时接受opencode/opencode-go与baseURLhostname;Models.applyAuth()对每个注册 provider 合并x-opencode-session: <sessionId>。- 仍可能需要本插件的场景:反代或不以
opencode.ai为 hostname 的端点、调用方拿不到 sessionId、需要自定义头值格式(dsh-前缀 / 摘要)。见「适用与不适用」。- 迁移:从 profile 移除本插件并重启 DSH 即可,自定义路由与模型不受影响(见「迁移」)。
- 本项目从未发布到 npm,不需要
npm deprecate;v0.2.2 的 GitHub Release 资产继续保留。
给自定义的 OpenCode Go 路由(默认 opencode-go-extra)注入每会话稳定的
x-opencode-session,让不同 DSH 会话在 OpenCode 侧拿到不同的路由 / 缓存 ID。
要求 DSH ≥ 0.2.2-rc.1(
llm-pi-ai/request-headerswaterfall 从这一版开始提供), Node.js ≥ 24.9.0(DSH 0.2.2-rc.1 自身的要求)。当前版本0.2.2,首个公开版本,只通过 GitHub Release 分发,不发布到 npm。
为什么需要
三层各自合理的设计叠在一起,出现了一个缺口:
- pi-ai 的模型目录是静态打包的。
opencode-go的模型表来自@earendil-works/pi-ai里的data/opencode-go.json,新模型不会自动出现;对有目录的 provider,模型刷新也直接 返回这份静态表。 - DSH 的协议和端点只能写在路由级。模型条目只支持
id / name / contextWindow / maxTokens / input / reasoningEfforts / compat,没有api/baseURL。opencode-go又是多协议目录(anthropic / google / openai-completions / openai-responses),给路由写死一个api会覆盖全部模型,所以新模型加不进这条内置路由。 - OpenCode 按会话路由,而 pi-ai 只在内置的
opencode/opencode-goprovider 上包了withOpenCodeSessionHeader。
于是想要新模型的人会自建一条路由(api: openai-completions + OpenCode 的
baseURL),这条路由却没有上面那层包装。静态写一个 x-opencode-session 能让请求通过,
但所有会话共用一个 ID,路由与 prompt cache 的隔离就没了——这个插件补的正是这一段。
适用与不适用
下表按 pi-ai 1.5.0 更新;旧版 pi-ai(< 1.5.0)仍需插件为自定义路由补头。
| 场景 | 需要本插件 | 原因 |
|---|---|---|
内置 opencode-go / opencode 路由 |
否 | pi-ai 的 catalog provider 自带该头 |
自定义路由指向 opencode.ai,pi-ai ≥ 1.5.0 |
否 | 通用 Models.applyAuth() 按 hostname 自动补头 |
自定义路由指向 opencode.ai,pi-ai < 1.5.0 |
是 | 旧版只包装内置 provider |
自定义路由 + 静态 x-opencode-session |
否,但不理想 | 插件见到已有头会跳过;全会话共用一个 ID |
反代 / 非 opencode.ai 端点 |
是(若服务端要求该头) | pi-ai 只认精确 hostname opencode.ai |
| 调用没有 sessionId | 是(若服务端要求该头) | pi-ai 无 fallback;插件有 aux-<uuid> |
DSH 0.2.1-rc.1 至 0.2.1-rc.5 |
不支持 | 这些版本没有 llm-pi-ai/request-headers 事件 |
工作方式
- 监听
llm-pi-ai/request-headerswaterfall。每次推理请求都会带上{ provider, model, sessionId, purpose, baseURL },不存在并发串话问题。 - 只对
providers里列出的路由、且baseURL命中配置 host 与路径前缀的请求,补上x-opencode-session。baseURL必须是 HTTP(S),hostname 精确匹配,路径按字面前缀匹配。 - 已有的同名头(大小写不敏感)保持原值,方便从静态头平滑迁移;下游监听器写入的其他头 也会保留。
- 会话 ID 的取值规则:
- 匹配
[A-Za-z0-9._:-]且不超过 128 字符:prefix + sessionId,默认即dsh-<sessionId>; - 其他非空值:
prefix + "h-" + SHA-256(sessionId),完整摘要,不做截断; - 空字符串、
null、缺失:插件实例级aux-<uuid>回退,同一实例内稳定。 h-加 64 位小写十六进制是保留的摘要命名空间;安全 ID 恰好长成这样时也走摘要 分支,避免它与另一个非法 ID 的摘要值相同。
- 匹配
- 不修改
globalThis.fetch,也不使用AsyncLocalStorage;卸载时只移除自己的监听器。
配置
- id: opencode-session
name: dsh-opencode-session
config:
providers:
- opencode-go-extra
# host: opencode.ai # 反代时改成自己的域名,可写 host:port 或完整 URL
# pathPrefix: /inference/go/ # 字面前缀,省略开头斜杠会自动补
# headerName: x-opencode-session
# prefix: dsh-
# debugLog: /tmp/opencode-session.log # 仅排查时开
providers:路由名列表,必须是字符串数组;省略或空数组时使用默认的opencode-go-extra。host:裸主机名、host:port或 HTTP(S) URL;只取 hostname,协议和端口不参与匹配。 裸主机带路径、URL 带凭据、非 HTTP(S) 协议都会在加载时报错。pathPrefix:与url.pathname做字面startsWith比较。想限定一个路径段时请带尾斜杠 (/v1/不会匹配/v10,但/v1会匹配/v10)。只能包含可打印 ASCII,不能包含空白、 查询串或 fragment。headerName:必须是合法 HTTP token(RFC 9110)。prefix:最多 64 个可打印 ASCII 字符,不能以空白开头或结尾;允许空字符串。debugLog:每注入一次追加一行时间<TAB>provider<TAB>sessionId<TAB>headerValue,字段中的控制字符会被转义。写失败不会 影响请求。日志包含会话标识,生产环境不要开启,也不要提交到仓库。
配置在插件加载时校验,非法值会抛出指明字段的 TypeError,而不是等到模型请求时才失败。
安装
新安装仅用于 pi-ai < 1.5.0、反代或需要 fallback 的场景;当前 DSH 0.2.2-rc.1 + pi-ai ≥ 1.5.0 的组合不需要本插件。
从 GitHub Release 下载并校验(推荐):
curl -fLO https://github.com/FFFengMJL/dsh-opencode-session/releases/download/v0.2.2/dsh-opencode-session-0.2.2.tgz
curl -fLO https://github.com/FFFengMJL/dsh-opencode-session/releases/download/v0.2.2/SHA256SUMS
sha256sum -c SHA256SUMS
dsh plugin --profile web add ./dsh-opencode-session-0.2.2.tgz
Windows PowerShell 把最后一行换成下载后的本地路径即可:
dsh plugin --profile web add C:\path\to\dsh-opencode-session-0.2.2.tgz
从本机源码目录安装(开发时):
dsh plugin --profile web add /path/to/dsh-opencode-session
安装后把目标路由的静态 headers.x-opencode-session 删掉,让插件接管。目标机器上的 provider
路由名如果不是 opencode-go-extra,在 profile 的 cordis.patch.yml 里覆盖插件行的
providers 列表即可(不要改插件包内的默认值)。安装/更新插件应使用 dsh plugin 命令,
不要手改 profile 的依赖清单或 node_modules。
迁移
确认不再需要插件时,移除步骤:
- 确认实际解析到
@earendil-works/pi-ai≥ 1.5.0。DSH 0.2.2-rc.1 声明^1.1.0,实际版本 取决于 profile 的锁文件;可查看profiles/node_modules/@earendil-works/pi-ai/package.json。 - 从 profile 移除插件:
dsh plugin --profile web remove dsh-opencode-session,并确认package.json的dsh.profile.bundles与依赖里都没有残留。 - 重启 DSH(插件与 profile 配置只在启动时合成)。
- 验证:发一条消息;服务端抓包或日志中应看到 pi-ai 注入的
x-opencode-session: <原始 session id>,没有dsh-前缀也正常。 - 以后若走反代或需要 fallback,再按旧版本重新安装即可。
已知边界
- 只处理配置的 host + 路径前缀;换域名或换协议路径要同步改
host/pathPrefix。 - 协议(HTTP/HTTPS)和端口不参与匹配;同一 hostname 的不同端口会命中同一条规则。
- 不保证 OpenCode 内部的路由 / 缓存一定按会话隔离;插件只保证发出去的 ID 每会话稳定且不同,不承诺数学意义上的绝对无碰撞。
- 如果路由已经带静态
x-opencode-session,插件会跳过,方便迁移和回退。 - 缺少该头时 OpenCode 是否拒绝请求取决于服务端;能确定的是 pi-ai 把它当作 必需的路由头,而静态头能满足“有头”但牺牲隔离。
- 已发布的 DSH
0.2.1系列没有所需 waterfall。放宽engines.dsh并不能让它工作; 本项目不为旧版提供 fetch 劫持等回退。
开发与验证
pnpm install --frozen-lockfile
pnpm test # 单测 + 真实 Cordis 挂载/卸载
pnpm verify # 语法、测试、打包内容检查
pnpm release:pack # 生成 .release/*.tgz 与 SHA256SUMS
测试使用模拟的 OpenCode 端点,不发送真实付费请求;这些检查也不等同于真实 DSH Web GUI 或 OpenCode 线上行为验证。
退役判定
以下任一变化落地后,这个插件就不再必要(本节保留为历史记录):
- DSH 允许在 catalog 基础上增量追加模型,或允许按模型声明
api/baseURL; - pi-ai 对自定义 provider 也按
baseURL补 OpenCode 会话头; - OpenCode 不再要求按会话的路由头。
2026-11-10:第二条已由 @earendil-works/pi-ai 1.5.0 满足,项目退役并归档。
No comments yet. Be the first to write one.