Introduction¶
In DeepSeek Harness (DSH) plugin development, host-side state changes (such as setting updates and build completion) need to notify the browser UI in real time. Existing polling approaches introduce latency, RPC patterns depend on user interaction, and browser-side WebSocket connections are limited (typically around 6 connections per domain). dsh-event-relay provides a shared SSE endpoint to solve this communication problem.
Installation¶
The installation command is as follows:
pnpm dsh plugin --profile web add /path/to/dsh-event-relay
After installation, restart Harness. The host side will load the plugin at startup. Browser-side plugins consume the service via ctx.get('eventRelay').
Core Features¶
- Shared SSE route: Uses a single
/relay/eventsroute to keep connections for all subscribers and push JSON messages. - Server-side filtering: The browser side specifies subscribed topic prefixes via URL parameters, and the server sends only matching messages.
- Reconnect signal: A
__relay/opensignal is sent when a connection is established or reconnected, helping the client determine connection state. - No dependencies: Pure native implementation with no additional dependencies.
Usage Examples¶
Host-side (Producer)¶
The host plugin sends events through relay.publish.
const relay = ctx.get('eventRelay') // 可选,如果缺失则降级处理
// 直接发送
relay.publish('my-topic', payload)
// 映射 Cordis 事件
ctx.on('my/event', (data) => relay.publish('my/event', data))
Browser-side (Consumer)¶
The browser side subscribes to topics through relay.subscribe, or uses EventSource directly.
const relay = ctx.get('eventRelay')
// 使用客户端服务订阅
const unsubscribe = relay.subscribe('kanban', (topic, payload) => {
// 处理消息
})
// 或使用原生 EventSource,配合主题过滤
// new EventSource('/relay/events?topics=kanban,notifications')
Design Notes¶
- One-way communication: The plugin is designed for one-way communication, supporting only host-to-browser push. The browser initiates the request, and the host responds.
- Optional payload: The
payloadin the message is optional. Some plugins use it only as a “doorbell” signal to trigger their own data refresh; others directly consume the message content. - Resilience first: The plugin may not exist, so consumers should have fallback mechanisms (such as issuing a pull request on
__relay/open) and should not rely entirely on the stream for state synchronization. - Route contract:
/relay/eventsis a composition-level convention. Other composition roots (such as/api,/plugins,/workspace-history,/notifications, and/granular-settings) are also part of this kind of contract.
Debugging¶
Monitor the SSE endpoint in a terminal to verify whether the host side is sending messages correctly:
curl -N 'http://127.0.0.1:3080/relay/events'
Summary¶
dsh-event-relay addresses the need for a shared event stream on the browser side for multiple plugins, avoids connection limits, and provides reliable connection-state signals. Developers can choose to consume the payload directly or use it only as a trigger, depending on their needs.