dsh-connection-rpc-fix
English | 中文
A community bundle that restores the plugin-facing connection.rpc.handle() API in the DeepSeek Harness Web composition.
Maintained by @Robin1987China
The symptom
A third-party plugin registers a private Web RPC channel, and every browser-side call to it fails:
cannot get property "webServer" without inject
Nothing is logged where the plugin can see it, and the browser request falls through to the static handler and answers HTTP 405.
English: connection.rpc.handle not working · plugin RPC channel returns 405 · cannot get property webServer without inject
中文: 插件 RPC 通道注册不上 · 浏览器端一律 405 · cannot get property webServer without inject
Who this affects
Anyone shipping a plugin that needs a browser-reachable channel through connection.rpc.handle(channel, handler). The official /api transport is not affected — only the plugin-facing API is.
Root cause
In @deepseek-ai/dsh-client-connection:
const inject = ['credentials']; // the package's own injections
async function apply(ctx, config) {
const connection = new HostConnectionService(ctx, ...); // the service holds this ctx
ctx.inject(['webServer'], (webCtx) => {
webCtx.effect(() => webCtx.webServer.register(route), ...); // official /api — works
});
}
// inside the service:
const owner = this.ctx; // = apply's ctx: no webServer
handle: (channel, handler) => this.register(owner, channel, handler),
register(owner, channel, handler) {
return owner.effect(() => owner.webServer.register(route), ...); // throws, swallowed
}
The plugin row declares inject: [webRuntime], so the service's context never carries webServer. The property access throws inside owner.effect, the throw is swallowed, and the channel is never mounted.
The package's own doc comment says it mounts the browser transport “When webServer is present” — the plugin-facing API assumes it unconditionally.
The fix
This bundle patches one row, giving the service's context the service it reads:
- id: connection
inject: [webRuntime, webServer]
Install
As a bundle — the official distribution form:
cd ~/.dsh/profiles/web
npm install dsh-connection-rpc-fix
then add it to that profile's package.json:
{"dsh": {"profile": {"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-connection-rpc-fix"]}}}
Or, without installing anything, patch it yourself in ~/.dsh/profiles/web/cordis.patch.yml:
- id: connection
inject: [webRuntime, webServer]
Verification
A probe plugin calls ctx.connection.rpc.handle('/lab-probe', handler) at startup, and the channel is then probed over HTTP. A registered channel sits behind the connection fence and answers 401 to an unauthenticated request; an unregistered one falls through and answers 405.
| plugin log | POST /lab-probe |
|---|
| before | THREW: cannot get property "webServer" without inject | 405 |
| after | returned without throwing (disposer: function) | 401 |
Both runs used an isolated DSH_HOME on a separate port; the POST /api control answered 401 in both.
The patch was also confirmed to work when delivered as a bundle (listed in dsh.profile.bundles) rather than as an inline patch layer, which is the form this package ships.
Scope and limits
- It patches a composition row, not the package. The underlying access is still unguarded, so read the upstream reports below before assuming a future release is fixed.
- It targets the Web composition. A deployment that mounts
connection without a web server was already broken for this API and gains nothing here.
- It does not make
rpc.handle() tolerate a missing web server; it makes the web server present.
Upstream reports
- #6105 — connection.rpc.handle() cannot register plugin RPC channels (browser gets HTTP 405)
- #6179 — always throws (cannot get property webServer without inject)
- #6289 — cannot mount third-party Web RPC channels
- #6081 — the Web bundle should declare a modules to webServer activation dependency
License
MIT