Skip to content

fix: 自注册 RPC 通道,兼容 dsh 0.1.5-rc.2 - #1

Merged
better-er merged 3 commits into
mainfrom
fix/dsh-0.1.5-rc2-compat
Sep 10, 2026
Merged

better-er merged 3 commits into
mainfrom
fix/dsh-0.1.5-rc2-compat

Conversation

@better-er

@better-er better-er commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner

dsh-pause 兼容 dsh 0.1.5-rc.2

背景

connection.rpc.handle 登记路由时会在 connection 服务自身上下文解析 webServer,而该上下文的 inject 只有 credentials,于是抛 cannot get property "webServer" without inject,插件树因此加载失败。对照 0.1.2-rc.1 与 0.1.5-rc.2,get rpc 与 register 逐字相同、owner.webServer 行为一致,所以这不是 0.1.5 引入的回归,该 API 自 0.1.2 起即不可用。官方 /api 走 ctx.inject(['webServer'], ...),从不经过 rpc.handle。

自注册并非唯一出路:私有 register(owner, channel, handler) 显式接收 owner,不解析 this.ctx,在注入了 webServer 的作用域里可用,能完整复用官方 transport;代价是它是 private 成员,需要类型断言且上游可能改名或移除。本 PR 选择自行实现同样的信封语义。

改动

  • 新增 src/rpc-channel.ts:mountRpcChannel 直接向 webServer 注册一条 prefix 路由,复用 connection.requestRejection 做 Host 校验与浏览器鉴权,自行实现 client-request/server-response 信封。仅接受 POST,校验 content-type 与 endpoint 段字符集,请求体限长 8 MiB 且超限回 413 带 connection: close,客户端断开即 abort,非 web profile 下不挂载。文件头列明与官方 transport 的有意差异。
  • src/index.ts:不再调用 ctx.connection.rpc.handle,改经 mountRpcChannel 注册 /dsh-pause 通道;删除两行死导入。
  • tests/rpc-channel.spec.ts:新增 14 项通道单测,覆盖信封回写、401/403 透传、非 POST 404、路径与端点非法字符 404、415、bad-request、413 限长、服务缺席不挂载。
  • tests/real-cordis.spec.ts:新增真实 cordis 4.0.2 与真实 client-connection 服务接线冒烟,断言 /api 与 /dsh-pause 同时挂载,以及 connection 缺席时不挂载。
  • tests/index.spec.ts:沿用真实 HTTP 通道测试,覆盖暂停放行、release 带文字并入 user 消息、status 与 setEnabled 收口、空端点 404。
  • package.json 与 pnpm-lock.yaml:@deepseek-ai/dsh-client-connection 升到 0.1.5-rc.2,typecheck 与单测落在目标版本上。
  • README.md:补「关键词」一节与主机侧自注册通道说明。

本 PR 还带上上一提交 27d9598:暂停图标与预览截图更新。

验证

pnpm typecheck、36 项 pnpm test、pnpm build 全绿。

@better-er

better-er commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner Author

事实核对

命令与结果

在 ef70117 上本地执行,工作区干净,三项全过:

  • pnpm typecheck 通过
  • pnpm test 20 项全通过,12 项 controller 加 8 项 host 接线
  • pnpm build 通过,lib/index.js 10544 字节、lib/client.js 7182 字节、lib/index.d.ts 4600 字节齐全

对背景判断的核对

  1. rpc.handle 抛 cannot get property "webServer" without inject 成立,根因也能定位。我用 cordis 4.0.2 复刻 HostConnectionService 的 get rpc 与 register 两个关键成员做探针,直接调用与放进 ctx.inject(['connection', 'webServer']) 作用域内调用都抛同一错误。原因是服务可追踪代理把 this.ctx 的 shadow 指向服务提供者 fiber,服务解析从 connection 自身 fiber 起步,而该 fiber 的 inject 只有 credentials。这条 API 对插件确实不可用,自注册通道能解决问题。

  2. 官方不走 rpc.handle 成立。发行包内 handle 无调用点,官方 /api 用 ctx.inject(['webServer'], ...) 直接调 webServer.register。

  3. 「自注册是唯一可行路径」不成立。私有 register(owner, channel, handler) 在注入了 webServer 的读取作用域里可用,探针里 wc.connection.register(wc, channel, handler) 成功挂载出 /dsh-private 路由,因为它显式接收 owner 而不解析 this.ctx。这条路能完整复用官方 transport。代价是它标了 private,需要 as 断言,上游也可能改名。可留作后续简化,但 rpc-channel.ts 头注释与 PR 描述里「唯一可行路径」的口径应放宽。

  4. 版本因果表述不准确。0.1.2-rc.1 与 0.1.5-rc.2 的 get rpc 与 register 逐字相同,owner.webServer 行为一致,所以这不是 0.1.5 引入的回归。PR 背景「0.1.5-rc.1 升级后」建议改成「该 API 自 0.1.2 起即不可用,本 PR 改用自注册通道」。

与官方 transport 的行为漂移

对照官方 rpcFetchHandler,本实现有以下差异:

  • 不校验 content-type,官方非 application/json 回 415
  • 请求体上限硬编码 8 MiB,官方按 connection 配置 maxRequestBodyBytes 且默认 300 MiB,超限时官方带 connection: close
  • 不把 request.signal 传给 handler,而是新建永不 abort 的 signal;官方传 request.signal,客户端断开即 abort。当前 handler 忽略 signal 所以无实害,但与类型承诺不符
  • handler 抛错时官方回 500 纯文本,本实现回 200 加 code: 'internal';index.ts 的 handler 自带 try 所以当前不可达
  • 信封校验手写,未校验 payload 字段与 rpcId 形状,官方用 clientRequestSchema

这些影响不到当前极小的 payload 与纯内网用法,但属于与官方 transport 的分叉。要么改用私有 register 复用官方实现,要么在文件头列出有意差异并给 413 补上 connection: close。

修改要求

应当

  1. 把兼容性验证落到目标版本。devDependencies 与 pnpm-lock 仍锁 @deepseek-ai/dsh-client-connection 0.1.2-rc.1,而本次声称修的是 0.1.5-rc.2;typecheck 与单测都跑在 0.1.2-rc.1 上,新测试又全程用桩 scope,从未构造真实 HostConnectionService。这次改动是否真能兼容 0.1.5-rc.2,CI 完全没验证到。至少把 devDependency 与 lock 升到 0.1.5-rc.2;更好的是加一条用真实 cordis 4.0.2 与真实 connection 服务断言路由挂载的冒烟测试。

  2. 补测试分支。rpc-channel.ts 目前只有「空端点 404」一条边界用例。至少补上:requestRejection 返回 401 或 403 时原样透传;非 POST 返回 404;method 与 endpoint 不一致返回 gateway/bad-request;请求体超限返回 413;endpoint 段含非法字符返回 404。再补一条 webServer 缺席时不挂载通道的断言,覆盖 PR 描述的「非 web profile 不挂载」。

  3. 清掉死导入。src/index.ts 第 4 行 import { transportError } 全文件未使用;第 3 行 import '@deepseek-ai/dsh-client-connection' 无副作用,却被打进 lib/index.js 成为运行时导入,而该包只在 devDependencies 里。两行都建议删掉,删后插件对 dsh-client-connection 只剩类型依赖,不必在运行时声明该包。

可选

  1. 与 dsh-live-token-stats 的 rpc-channel.ts 逐字重复。同一份 transport 已被复制到第二个插件,任一侧修 bug 都不会同步。抽公共包,或在文件头标注来源与同步点。

  2. README 补一句自注册通道。对外行为不变,但「工作原理」一节现在只讲浏览器侧,主机侧通道的来源值得记一句。

  3. PR 把上一提交 27d9598 一并带入,该提交是暂停图标与预览截图更新,目前只在本地 main 上。若本意如此没问题,但标题只写兼容修复,描述里最好点明还带了图标提交,避免合并时把两个主题一起并入。

@better-er
better-er merged commit c551fcc into main Sep 10, 2026
2 checks passed
@better-er
better-er deleted the fix/dsh-0.1.5-rc2-compat branch September 10, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant