Skip to content

fix(bridge): speak for a live Connection Peer on dsh 0.2.1 - #137

Closed
hxphieno wants to merge 1 commit into
omdsh-dev:mainfrom
hxphieno:fix/0.2.1-mandatory-peer
Closed

hxphieno wants to merge 1 commit into
omdsh-dev:mainfrom
hxphieno:fix/0.2.1-mandatory-peer

Conversation

@hxphieno

@hxphieno hxphieno commented Nov 3, 2026

Copy link
Copy Markdown

概述 / Summary

让桥插件在 dsh 0.2.1 上真正跑通:为进程内的 Gateway 调用铸造一个合法的 owner Peer 并透传,同时把 @deepseek-ai/dsh* 的 peer 范围放宽到 0.2.1 线。0.2.0 的行为保持不变。

动机 / Motivation

dsh 0.2.1 把 Gateway 的 Peer 从「可选」改成了「强制」:

  • dsh-api-gateway/lib/index.js 的 prepareInvocation()(约 1171 行)和 openWireStream()(约 983 行)都会先执行 assertPeerLive(peer, endpoint);
  • assertPeerLive 的第一行就是 peer.signal.aborted(约 1493 行);
  • $events/result 还额外要求 peer.principal.kind === 'owner'(约 965 行)。

桥插件在 remote-host-api.ts 里一直把 peer 槽传成 undefined(open(endpoint, payload, EMPTY_WIRE_UPLINK, undefined, signal)),invoke() 的请求体里也没有 peer 字段。于是 0.2.1 上每一个侧栏连接都会立刻失败:

hello.ok caps = {"textOnly":true,"snapshotMaxChars":32000,"maxInteractiveItems":60}
BRIDGE ERROR: stream-failed - TypeError: Cannot read properties of undefined (reading 'signal')

即 issue #130 / #133 / #134 / #135 描述的症状:侧栏连不上,browser_* 工具全部不可用。

此外还有第二道门:插件把 13 个 @deepseek-ai/dsh* peer 精确锁在 0.2.0-rc.2,dsh 0.2.1 的插件管理器在安装阶段就会拒绝:

dsh: installation rejected: Plugin @yuxianglin/dsh-bridge-browser@0.0.7 is incompatible with dsh 0.2.1-rc.5

改动内容 / Changes

  • src/index.ts:新增 createHostPeer()。运行时提供 connection.createPeer 时铸一个 { principal: { kind: 'owner' } } 的 Peer,并用 ctx.effect 把 peer.dispose() 绑到插件生命周期(Peer 被提前 dispose 会让后续调用变成 gateway/peer-disposed)。
  • src/remote-host-api.ts:
    • HostConnectionLike 增加可选 createPeer,新增结构化类型 PeerLike;
    • openWireStream() 接受 peer 并透传,替换原来的 undefined;
    • TypertGatewayLike.invoke() 请求体增加可选 peer,RemoteHostApi 用 invokeRemote() 统一注入(覆盖 session/create、session/page、session/modelCatalog、session/projections 四条路径);
    • EventGeneration、oneShotSessionSnapshot() 同步接收 peer,覆盖 $events、session/follow、workspace/follow 三条 wire stream。
  • package.json:13 个 @deepseek-ai/dsh* peer 从 0.2.0-rc.2 放宽为 0.2.0-rc.2 || >=0.2.1-alpha.0 <0.3.0-0。
  • 测试:remote-host-api.spec.ts 新增「peer 被透传到 stream 与 invoke」用例;index.spec.ts 新增「有 createPeer 时铸造并随作用域释放」「没有 createPeer 时保持空槽(0.2.0)」两个用例。
  • README(中英):注明桥插件同时支持 0.2.1。

兼容性:0.2.0-rc.2 没有 createPeer,PeerLike 分支整体跳过,行为与改动前逐字节一致;原有的 expect(peer).toBeUndefined() 断言保持不变也仍然通过。

测试 / Testing

环境:macOS 26(arm64)、Chrome 156、Node 24、DSH 0.2.1-rc.5 桌面版、插件以 link: 装入 desktop profile。

  1. 失败基线(未打补丁的 main):用一个无头 WebSocket 客户端模拟扩展,完成 hello 后主机立即回 stream-failed - TypeError: Cannot read properties of undefined (reading 'signal'),与 issue 完全一致。
  2. 修复后(同一客户端、同一台机器):hello.ok,$events 流正常开启;session.models 返回真实模型目录({"ok":true,...}),说明 gateway.invoke 的 peer 也通过了校验。
  3. 安装门禁:撤销 allow-version 豁免后重新安装,插件管理器不再拒绝(peer 范围生效)。
  4. 测试套件:pnpm run test → 51 个文件 / 394 个用例全绿(含新增 3 个用例);pnpm run build、pnpm run typecheck 通过。

未覆盖:真实 Chrome 侧栏的端到端点击(本机 Chrome 无法脚本化加载 unpacked 扩展,tests/e2e 在无 Chromium 时自动 SKIP)。

影响范围 / Impact

  • 平台:全部(macOS / Linux / Windows 的 bridge 逻辑一致),扩展侧无需改动。
  • 运行时:0.2.1 从「完全不可用」变为可用;0.2.0-rc.2 行为不变。
  • 安全:Peer 以 owner principal 铸造,与 desktop 浏览器会话同级;桥自身的 token/审批逻辑不变。注意这意味着 bridge 的内进程调用不再受 remoteAccess 的 non-owner 资源策略约束——与 0.2.0 时期的实际效果一致。

Checklist

  • 已在相关平台实测通过 / Tested on the relevant platforms (macOS + Chrome 156 + dsh 0.2.1-rc.5)
  • 已通过 pnpm run build / pnpm run typecheck / pnpm run test
  • 文档已同步更新(README 中英)
  • 不包含无关改动 / No unrelated changes included

dsh 0.2.1 makes the Gateway Peer mandatory. Both `prepareInvocation()` and
`openWireStream()` now call `assertPeerLive(peer, endpoint)`, which dereferences
`peer.signal`, so the bridge's in-process calls that passed `undefined` in the
peer slot fail with

    TypeError: Cannot read properties of undefined (reading 'signal')

Every side-panel stream dies with it (WS 1011 "stream-failed") and no browser_*
tool can run.

Mint one owner Peer through `connection.createPeer({ principal: { kind:
'owner' } })` when the runtime offers it, forward it to `wireStream.open()` and
`gateway.invoke()`, and dispose it with the plugin scope. dsh 0.2.0 has no
`createPeer`, so the slot stays empty there and the previous behaviour is kept.

Widen the `@deepseek-ai/dsh*` peer ranges to the 0.2.1 line as well: the
plugin-manager compatibility gate rejects an exact `0.2.0-rc.2` pin on 0.2.1
before the plugin is even installed.

Verified on macOS, Chrome 156, dsh 0.2.1-rc.5: bridge handshake, `$events`
stream and a `session.models` gateway call all succeed, and the full workspace
suite (394 tests) passes.
@hxphieno hxphieno closed this Nov 4, 2026
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