Skip to content

fix(host): mount dedicated RPC channels via webServer for post-alpha.2 DSH masters - #177

Closed
CnsMaple wants to merge 1 commit into
xmanrui:mainfrom
CnsMaple:main
Closed

fix(host): mount dedicated RPC channels via webServer for post-alpha.2 DSH masters#177
CnsMaple wants to merge 1 commit into
xmanrui:mainfrom
CnsMaple:main

Conversation

@CnsMaple

@CnsMaple CnsMaple commented Sep 8, 2026

Copy link
Copy Markdown

Problem

On a DSH built from deepseek-harness master, every dsh-im channel settings page shows transport errors such as:

无法读取飞书机器人
transport failure for /feishu/connection.status: HTTP 405

The plugin boots, but all dedicated RPC channels (/feishu, /dingtalk, /qq, /wecom, /slack, /telegram, /whatsapp, /weixin, /office, /dsh-im, delivery and inbound-TTL channels) are silently missing; their requests fall through to the SPA handler and are answered 405.

Version attribution (corrected after @xmanrui's review): the underlying inject change landed in unreleased master only2ef85b1e17 ("fix: windows build", merged via the feat/electron chain). The dsh-v0.1.3-alpha.1 and dsh-v0.1.3-alpha.2 tags and the published npm packages still inject webServer in client-connection, so connection.rpc.handle() works there (as the maintainer verified — an independent init-ordering bug, fixed in 2dd915d, is what reproduces 405s on alpha.2). This PR is forward-compatibility for the next DSH release (alpha.3/rc will break every rpc.handle plugin otherwise; dsh-pocket crashes the whole plugin tree on master with cannot get property "webServer" without inject).

Root cause (master)

Master's client-connection plugin changed its top-level inject from ['webServer', 'credentials'] to ['credentials'] (making Connection carrier-neutral). rpc.handle() still mounts its channel as a webServer prefix route by reading webServer off the Connection plugin's own fiber context — which can no longer resolve it — so from any plugin caller it throws:

Error: dsh: plugin tree failed to load: failed to apply loader entry dsh-pocket (dsh-pocket): cannot get property "webServer" without inject
Error: cannot get property "webServer" without inject
    at Fiber.<anonymous> (packages/client/connection/lib/index.js:618:35)   // owner.webServer.register(route)
    at Proxy.register (packages/client/connection/lib/index.js:618:16)
    at Object.handle (packages/client/connection/lib/index.js:543:39)       // get rpc() { const owner = this.ctx; ... }
    ...

For dsh-im specifically, activateChannels() isolates each channel with try/catch, so boot survives and the channels just vanish (dsh-im #177 context; identical mechanism as the dsh-pocket crash above).

Fix

Route all 13 mount sites through one shared helper, plugin-src/host/rpc-mount.mjs:

  • Resolve webServer with the inject-free ctx.get('webServer') and register the prefix route directly, replicating the Connection plugin's own route semantics 1:1: the same auth fence (connection.requestRejection, 401/403 branches), the same client-request/server-response wire envelope (404/415/400/gateway/bad-request/500 branches, buffered-body cap matching the Connection default). The third { authority } option was never consumed by Connection and is only passed through on the fallback path.
  • Registration is wrapped in the caller's ctx.effect() and returns a disposer, preserving the previous lifetime semantics.
  • When no webServer service exists (released-host fixtures, programmatic test fakes) it falls back to connection.rpc.handle() verbatim. On released DSH (alpha.2 and earlier) webServer is always provided by the web host, where the direct mount is behaviorally equivalent to what rpc.handle() does internally.

Validation

  • Rebased onto upstream 2dd915d (management-RPC ordering fix).
  • New regression tests test/rpc-mount.test.mjs (direct mount, auth fence, envelope branches, 500, legacy fallback, TypeError) — 6/6 pass.
  • Key suites after rebase: rpc-mount + test/channels/shared/startup.test.mjs (new upstream startup tests) + host.test.mjs + update/delivery rpc — 80/80 pass.
  • Full suite before/after on Windows (node 26): base 35 failing → with patch 31 failing, zero new failures (all remaining are pre-existing Windows-environment failures: 0600 file-mode assertions, path-regex escaping, etc.).
  • Rebuilt lib/index.js with the repo's own node plugin-src/host/build.mjs.
  • Confirmed end-to-end on a real DSH master source build: channel settings pages read bot state again.

中文摘要:DSH **master(未发布,commit 2ef85b1e17)**将 client-connection 的 inject 从 ['webServer','credentials'] 收缩为 ['credentials']connection.rpc.handle() 内部解析 webServer 的路径从任何插件调用方必抛 cannot get property "webServer" without inject;alpha.1/alpha.2 tag 与 npm 包仍注入 webServer、不受影响(维护者在 alpha.2 上验证到的是另一个初始化顺序问题,已由 2dd915d 修复)。本 PR 为下一个 DSH 发布版本提供前向兼容:13 处通道挂载改走共享 installRpcChannel()——优先用免 inject 的 ctx.get('webServer') 直挂 prefix 路由,认证门与 RPC 信封语义逐分支对齐 Connection 原实现,线协议不变;无 webServer 服务时原样回退 rpc.handle(兼容已发布版本与测试 mock)。已 rebase 至 2dd915d,80/80 关键测试通过。

@xmanrui

xmanrui commented Sep 8, 2026

Copy link
Copy Markdown
Owner

感谢提交修复和复现信息。我们核对了本地 alpha.2 源码及官方 npm 包,其中 Connection 的 inject 仍包含 webServer,实际 HTTP 测试中 connection.rpc.handle() 也能正常挂载。因此,暂时无法确认 PR 中“alpha.2 移除了 webServer 注入”的根因判断。

排查中发现,渠道初始化失败发生在管理接口挂载之前,也会导致请求落入静态页面兜底并返回 405。我们已将管理接口提前挂载,并为全部 11 个渠道/连接器入口增加初始化状态和错误提示,修复已合入 main:2dd915d。2531 项测试及 alpha.2 官方组件的 HTTP 验证通过。

你报告的 13 个接口同时失联仍需要进一步定位。请补充 DSH 的安装来源、实际加载的 Connection 包版本,以及 cannot get property "webServer" without inject 的完整调用栈;也欢迎用最新 main 再验证一次。

目前先暂缓合并直接挂载 WebServer 的方案,待确认环境差异后再决定是否需要修改传输层。

…2 DSH masters

Dropped webServer from client-connection top-level inject in unreleased DSH
master (commit 2ef85b1e17 fix: windows build; the alpha.2 tag still injects it),
so connection.rpc.handle() will throw cannot get property "webServer" without
inject for every plugin caller once the next DSH release ships. dsh-im
activates channels inside try/catch, so boot survives but channel transports
are silently lost: browser RPC calls (e.g. /feishu/connection.status) fall
through to the SPA handler and answer HTTP 405.

Route all 13 mount sites through one shared installRpcChannel helper that
resolves webServer via the inject-free ctx.get(), registers the prefix route
with the same auth fence (connection.requestRejection) and client-request /
server-response envelope semantics as the Connection plugin, and falls back
to connection.rpc.handle when no webServer service exists (released DSH
versions and programmatic test fakes). Add regression tests for the new
mount path.
@CnsMaple CnsMaple changed the title fix(host): mount dedicated RPC channels via webServer for DSH v0.1.3-alpha.2 fix(host): mount dedicated RPC channels via webServer for post-alpha.2 DSH masters Sep 8, 2026
@CnsMaple

CnsMaple commented Sep 9, 2026

Copy link
Copy Markdown
Author

感谢核对,你们是对的——原描述版本归属有误:收缩 inject 的提交是 2ef85b1e17("fix: windows build",经 feat/electron 合并链进入 master,不在任何已发布 tag)。alpha.1/alpha.2 源码与 npm 包确实仍注入 webServer,你们在 alpha.2 上 HTTP 验证 rpc.handle 正常完全合理——alpha.2 环境复现 405 的正是你们已修复的初始化顺序问题。

按请求补充环境信息与最新验证结果:

  • DSH 安装来源:git 源码 checkout 运行 deepseek-harness(非 npm 包),HEAD c389f96bf3(master,晚于 alpha.2 tag)

  • 实际加载的 Connectionpackages/client/connection/lib/index.js 工作区产物,const inject = ["credentials"](即 2ef85b1e17 之后的状态)

  • 完整栈(同环境下 dsh-pocket,同一机制,只是它不包 try/catch,所以表现为 boot 崩溃而不是静默失联):

    Error: failed to apply loader entry dsh-pocket: cannot get property "webServer" without inject
        at Fiber.<anonymous> (packages/client/connection/lib/index.js:618:35)   // owner.webServer.register(route)
        at Proxy.register (packages/client/connection/lib/index.js:618:16)
        at Object.handle (packages/client/connection/lib/index.js:543:39)       // get rpc() { const owner = this.ctx; ... }
    
  • 上游最新 main 实测:刚在 DSH master 构建上安装 dsh-im 上游 main(2dd915d,不含本 PR),13 个通道仍然失联(GUI 依旧 405);应用本 PR(分支已 rebase 到 2dd915d)后各渠道状态恢复正常。关键套件 80/80 通过(含你们新增的 test/channels/shared/startup.test.mjs 与本 PR 的 6 个回归用例)。

结论:两个原因相互独立且都成立——npm alpha.2 用户的 405 已由你们的顺序修复解决;而 master 环境下(以及 alpha.3/rc 等下一个发布版本起,届时所有 rpc.handle 插件会集体失联)需要本 PR 的挂载方式。PR 描述已改正版本归属。是否现在合并、还是等新版 DSH 临近发布时再合,由你们决定;我方可先以 fork 维护过渡。

@CnsMaple

CnsMaple commented Sep 9, 2026

Copy link
Copy Markdown
Author

已确认 503a24a(management-rpc 改走 /api 共享通道的 Fetch 路由)正面解决了同一根因:master 上 Connection 的 rpc.handle2ef85b1e17 收缩 inject 而对所有插件调用方必抛 cannot get property "webServer" without inject。补充一条你们矩阵的定序信息:该收缩已随 dsh-v0.1.5-alpha.1 正式发布(tag 内容与当前 master 相同),即 alpha.2 之后的首个发布版就会让所有 rpc.handle 插件失联——你们在 5 个已发布版上的验证正覆盖了这个场景。

既然上游已用自有方案(且连同客户端协议与鉴权策略一起调整)解决,本 PR 撤回关闭,避免双轨维护。留档:dsh-pocket 侧同根因的 PR 见 shaobeichen/dsh-pocket#108(该仓库尚未跟进的话,connection.fetch.register 路线已被验证可行)。

感谢快速响应和版本归属的纠正。

@CnsMaple

CnsMaple commented Sep 9, 2026

Copy link
Copy Markdown
Author

Superseded by 503a24a (fetch-route approach). Closing.

@CnsMaple CnsMaple closed this Sep 9, 2026
@CnsMaple

CnsMaple commented Sep 9, 2026

Copy link
Copy Markdown
Author

补充一条实况更新:此前在 alpha.2 环境反馈"仍未修复",实为升级后 host 进程未换血——磁盘已装新版但运行中的 server 还持有旧挂载代码,浏览器新客户端打到旧进程上表现为 transport failure for /api/dsh-im/feishu: HTTP 404(探针确认 /feishu/* 无路由、/api auth 门在位)。执行 dsh plugin --profile web up 重装解析并在 live 热重载/重启后,v4.17.1 的 /api Fetch 路由管理通道完全恢复,与 master(v0.1.5-alpha.1)兼容正常。给其他遇到同症状用户的提示:升级 dsh-im 后需确保宿主进程真正加载新包(重启或确认热重载生效)再验证。

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.

2 participants