跳转到内容

SSE 实时推送与 Viewer UI

SSE 实时推送与 Viewer UI:Claude-Mem 的具体实现

学习目标

本章聚焦 Claude-Mem 如何通过 Server-Sent Events 实现实时记忆流推送,以及 React Viewer UI 的构建流程。

项目实践

SSE 广播器

SSEBroadcaster.ts(约 1.4KB)实现了轻量级 SSE 推送服务:

广播器的工作流程:

  1. Worker 产生新观察或状态变更时,调用 broadcast(event)
  2. 广播器遍历所有已连接的 SSE 客户端
  3. 将事件格式化为 SSE 消息(data: JSON.stringify(event))推送到每个客户端
  4. 推送失败的客户端自动断开连接

Viewer UI 构建

Viewer UI 是一个 React 应用,构建为单个 HTML 文件:

阶段工具输出
开发React 19 + TypeScriptsrc/ui/viewer/
构建esbuildplugin/ui/viewer.html
服务Worker 内置静态文件服务http://localhost:<port>/

构建产物嵌入到 plugin/ui/viewer.html,Worker Service 直接提供 HTTP 服务,无需额外的 Web 服务器。

Viewer 功能

Viewer UI 默认在 Worker 端口(如 http://localhost:37777)提供服务,支持:

  • 实时记忆流:通过 SSE 接收并展示新记忆
  • 时间线浏览:按时间查看观察和摘要
  • 搜索:通过 Worker API 搜索历史记忆
  • 版本切换:在 Settings 面板切换稳定版 / Beta 版
  • 设置管理:查看和修改 settings.json

Beta 通道

Viewer UI 提供版本切换功能:

  • 稳定版:当前发布的版本
  • Beta 版:包含实验性功能(如 Endless Mode)

切换后,Worker 重新加载插件代码,应用新版本功能。

问题与规避

SSE 连接保持

问题:浏览器可能在网络切换或长时间空闲时断开 SSE 连接。

规避:浏览器端的 EventSource API 自动重连。Worker 侧无需特殊处理,新连接建立后即开始推送新事件。

构建产物同步

问题:Viewer UI 的 React 源码修改后需要重新构建并同步到 plugin/ui/viewer.html

规避npm run build-and-sync 命令自动执行构建和同步,将构建产物复制到 plugin/ 目录下的正确位置。

设计取舍

为什么用 SSE 而非 WebSocket?

维度SSEWebSocket
方向单向(服务端→客户端)双向
协议HTTP 复用独立协议
浏览器支持内建 EventSource API需要 WebSocket API
重连自动需要手动实现
复杂度

Claude-Mem 选择 SSE 因为 Viewer UI 只需要从服务端接收更新,不需要向服务端发送消息(搜索等操作通过 HTTP API 完成)。SSE 的自动重连和内建浏览器支持也降低了实现复杂度。

为什么构建为单文件 HTML?

Viewer UI 构建为单个 HTML 文件而非 SPA + API 分离:

  • 优势:部署简单(只需一个文件),Worker Service 直接提供,无需额外的 Web 服务器
  • 代价:首屏加载时需要下载完整 JS bundle
  • 权衡:Viewer UI 是本地工具,带宽不是问题,简单性优先

参考来源