dsh-ui-container:DeepSeek Harness 的 UI 容器与远程投影插件

前言

DSH 的理念是「一切皆插件」,浏览器插件之间经常需要协作:一个插件负责界面表面,另一个插件读取文档状态,或者 Worker、iframe bridge、host IPC adapter 需要复用同一份前端能力。

这类协作如果直接复制组件、同步 DOM,或者把 Cordis service 对象跨端传递,边界会很难控制。下面介绍的 dsh-ui-container 面向这个问题:它提供一个可复用的 UI 表面容器,并用一个较小的远程协议连接另一个前端,远程契约传输文档快照和变更通知,不传输 React 元素、DOM 节点或 Cordis service 对象。

这是什么

dsh-ui-container 是由 CH4ACKO3 维护的 DeepSeek Harness 浏览器插件,包名为 @ch4acko3/dsh-ui-container,许可证为 MIT。

它的定位是 remote-capable recursive UI surface container,核心职责是:

  • 拥有 named UI surfaces
  • 管理 document providers
  • 提供 recursive React hosts
  • 提供连接另一个前端所需的小远程协议

这个插件的关键边界是:接收端仍然在自己的前端里渲染投影;远程契约不搬运 React 元素、DOM 节点或 Cordis service 对象。

核心能力

下面介绍它已经明确的能力。

能力协商

协议版本 1 会在强制握手阶段协商以下能力:

  • documents:解析基于 URI 的文档投影。
  • subscriptions:订阅某个 URI,并在其投影可能变化时发送较小的失效通知。
  • surface_commands:把 open、reveal、close 命令路由到已挂载的 surface session。

其中,surface_commands 默认禁用,需要服务端显式开启。

传输通道

它支持两类通道:

  • 跨进程 MessagePort 通道
  • WebSocket 网络通道

线上契约

线上契约使用 JSON-RPC 2.0,方法名带主版本号。

连接只允许一次握手。握手之后,后续方法必须属于已经协商的能力。

所有远程文档内容和元数据要求 JSON 兼容。

安装与启用

先安装到 DSH profile:

dsh plugin --profile web add github:CH4ACKO3/dsh-ui-container

如果安装来源会触发 Git 依赖的 prepare 构建,使用 pnpm 10 或更新版本时,需要先批准首次安装报告中的精确包键,写入该 profile 的 pnpm-workspace.yaml,再重复执行安装命令。

典型用法

在其他浏览器插件中注入服务

其他浏览器插件可以从 @ch4acko3/dsh-ui-container/client 导入公共 API,并注入 uiContainer Cordis service。之后即可通过 ctx.uiContainer 访问容器能力。

用 MessagePort 连接另一个前端

适用于 Worker、iframe bridge 或 host IPC adapter。先创建 host 和 client 通道,再暴露服务端,最后连接客户端。

const hostChannel = createMessagePortUiRemoteChannel(hostPort)
const clientChannel = createMessagePortUiRemoteChannel(clientPort)

const stopServing = exposeUiContainerRemote(ctx.uiContainer, hostChannel, {
  server: {
    name: 'patchouli-host',
    version: '0.1.0',
    instance_id: crypto.randomUUID(),
  },
})

const remote = await UiContainerRemoteClient.connect(clientChannel, {
  client: {
    name: 'patchouli-window',
    version: '0.1.0',
    instance_id: crypto.randomUUID(),
  },
  protocol_versions: [UI_REMOTE_PROTOCOL_VERSION],
  capabilities: ['documents', 'subscriptions'],
})

const unregister = ctx.uiContainer.documents.registerProvider(
  remote.createDocumentProvider('memory'),
)

这里,exposeUiContainerRemote 把容器暴露到 host 通道,UiContainerRemoteClient.connect 在 client 通道完成握手。经过上面的步骤,客户端侧可以注册一个来自远端的 document provider。

在生命周期结束时,应按拥有这些 ports 的生命周期处理 unregisterremotestopServing

用 WebSocket 暴露容器

远程传输层不定义认证。如果通过 WebSocket 暴露容器,拥有该端点的应用必须在调用 exposeUiContainerRemote 之前,自行处理认证、授权、TLS 和 origin policy。

假设 socket 是已通过应用层安全策略检查的 WebSocket 连接,可以先创建通道,再暴露容器:

const channel = createWebSocketUiRemoteChannel(socket)

exposeUiContainerRemote(ctx.uiContainer, channel, {
  server: {
    name: 'patchouli-host',
    version: '0.1.0',
    instance_id: crypto.randomUUID(),
  },
  capabilities: ['documents', 'subscriptions'],
})

适用场景与注意

适合这类场景:

  • 需要把 DSH 浏览器插件中的 UI 表面、文档投影或 document provider 暴露给另一个前端。
  • 需要在 Worker、iframe bridge、host IPC adapter 或 WebSocket 端点之间共享容器能力,而不想跨边界发送 DOM 或组件树。
  • 希望接收端基于 JSON 兼容的文档投影,在本地完成渲染。

注意以下边界:

  • 插件运行在 DSH 宿主环境中。安装前请检查源码和 MIT 许可证,并确认它对当前 profile 的权限边界。
  • 远程协议本身不提供认证。使用 WebSocket 时,服务端必须自己执行认证、授权、TLS 和 origin policy。
  • surface_commands 默认禁用。服务端只有在明确允许某个主体控制对应前端会话时,才应启用该能力。
  • 每条连接只允许一次握手;握手后只能调用已协商能力内的方法。
  • 如果通过 Git 依赖安装,使用 pnpm 10 或更新版本时,需要批准首次安装报告中的精确包键,再重复安装命令。

结尾

dsh-ui-container 的价值,是把 DSH 插件之间的前端协作从「搬 DOM / 组件树」收敛为「协商文档投影和变更通知」。它没有扩大前端对象本身的传输边界,而是用较窄的远程契约完成跨进程、跨端点的容器复用。

仓库地址:https://github.com/CH4ACKO3/dsh-ui-container

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye