前言¶
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 的生命周期处理 unregister、remote 和 stopServing。
用 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 / 组件树」收敛为「协商文档投影和变更通知」。它没有扩大前端对象本身的传输边界,而是用较窄的远程契约完成跨进程、跨端点的容器复用。