深入 OpenClaw Studio 架构:服务器控制面、SQLite 投影存储与 WebSocket 适配器
【免费下载链接】openclaw-studioA clean web dashboard for OpenClaw. Connect your Gateway, manage agents, and ship faster. ⭐️ Star if you like it!项目地址: https://gitcode.com/gh_mirrors/op/openclaw-studio
OpenClaw Studio 是 OpenClaw 的 Web 控制台:连接 Gateway、管理智能体(Agents)、聊天、审批执行与配置定时任务,一站完成。本文深入它的核心架构——服务器控制面(server-owned control plane)、SQLite 投影存储(runtime.db)与WebSocket 适配器(Gateway 连接管理),帮你理解消息如何不丢、连接如何自愈、刷新页面为何依然秒回。
架构总览:三层单向数据流
OpenClaw Studio 已移除浏览器直连 Gateway 的旧方案,生产运行时只保留三条路径(详见 ARCHITECTURE.md):
| 层级 | 路径 | 说明 |
|---|---|---|
| 浏览器 → Studio | /api/runtime/*、/api/intents/*(HTTP) | 读取状态与执行"意图"操作 |
| 浏览器 ← Studio | /api/runtime/stream(SSE) | 实时事件流 + 断线回放 |
| Studio → Gateway | 一条服务器持有的 WebSocket | 由 Node 进程独占打开 |
一句话心智模型:浏览器只与 Studio 说话,Studio 替浏览器保管 Gateway 连接。这也是为什么远程部署时ws://localhost:18789指的是"Studio 所在主机上的 Gateway"。
服务器控制面:进程内单例的"中枢"
控制面运行时的核心是 runtime.ts 中的ControlPlaneRuntime,它是进程级单例,职责清晰:
- 事件扇出:Gateway 事件经适配器回调进入
handleDomainEvent(),先落库再分发给所有订阅者(runtime.ts#L106-L115); - 网关调用边界:所有请求都走
callGateway(),不允许前端绕过服务器直接调用(runtime.ts#L94-L100); - 快照读取:
snapshot()直接返回 SQLite 中的投影状态,读取永远有"数据新鲜度"依据。
浏览器侧的意图路由(发送消息、创建 Agent、审批执行等)全部收敛在/api/intents/*,由 runtimeWriteTransport.ts 驱动,保证写路径单一、可审计。
SQLite 投影存储:runtime.db 的三张表
投影存储实现位于 projection-store.ts,数据库文件位于~/.openclaw/openclaw-studio/runtime.db,初始化时创建三张核心表(projection-store.ts#L343-L368):
| 表 | 作用 |
|---|---|
runtime_projection | 单行投影:连接状态、原因、数据截止时间(as_of) |
outbox | 有序事件日志(outbox),每行带自增id与agent_id索引,是回放与历史分页的游标基准 |
processed_events | 按事件键去重,保证域事件幂等应用,Gateway 重连重推也不会产生重复消息 |
这套设计带来两个直接收益:
- 确定性回放:SSE 携带
Last-Event-ID时从该 id 向后补发;没有时从 outbox 尾部回放最近窗口,刷新页面和进程重启都不丢事件; - 降级可读:Gateway 不可用时,读取路由可以基于投影数据返回带新鲜度元信息的降级响应(见 degraded-read.ts)。
迁移策略遵循"只做增量"的护栏:新增列、新增索引、提升user_version,从不断裂历史数据。
WebSocket 适配器:Gateway 连接的"管家"
适配器实现于 openclaw-adapter.ts,由 Studio Node 进程持有唯一的上游 WebSocket,负责四件事:
- 握手鉴权:收到
connect.challenge后以连接画像(协议版本 3、tool-events能力声明)发起connect请求,画像构建逻辑在 gateway-connect-profile.ts; - 超时保护:8 秒内未收到连接响应即判定
CONNECT_TIMEOUT(openclaw-adapter.ts#L320-L360); - 自动重连:断线后以 1 秒起步、最高 15 秒的退避策略指数退避重连,并在连接画像中支持自动回退旧版 Control UI 握手;
- 方法白名单:仅放行
chat.send、agents.create、cron.add、exec.approval.resolve等显式列出的网关方法(openclaw-adapter.ts#L28-L54),请求一律经过这层"关卡",杜绝越权调用。
Token 由服务器保管(存于~/.openclaw/openclaw-studio/settings.json)并在 API 响应中脱敏,永远不会出现在浏览器里。
SSE 回放:刷新页面为何不丢消息
事件出口是 stream/route.ts,关键参数一目了然:
- 回放上限 2000 条(
REPLAY_LIMIT):连接建立时先按Last-Event-ID补发,再切换实时推送(stream/route.ts#L5-L25); - 15 秒心跳:保持长连接存活,避免代理层误判超时;
- 历史分页:更早的消息走
/api/runtime/agents/[agentId]/history,用beforeOutboxId作排他游标向前翻页(history/route.ts),客户端通过useRuntimeSyncController将分页行灌入与实时 SSE 相同的事件管线,按 outbox id 去重,因此"历史 + 实时"天然无缝拼接。
聊天事件的解析与渲染细节可继续参考 pi-chat-streaming.md,权限与沙箱配置如何流入 Gateway 则见 permissions-sandboxing.md。
关键文件速查
| 模块 | 文件 | 职责 |
|---|---|---|
| 控制面运行时 | src/lib/controlplane/runtime.ts | 单例、订阅扇出、网关调用边界 |
| 投影存储 | src/lib/controlplane/projection-store.ts | runtime.db读写、幂等落库、回放窗口 |
| WebSocket 适配器 | src/lib/controlplane/openclaw-adapter.ts | 握手、白名单、重连退避 |
| 领域契约 | src/lib/controlplane/contracts.ts | 事件、outbox 条目、快照类型定义 |
| SSE 出口 | src/app/api/runtime/stream/route.ts | 回放 + 心跳 + 实时推送 |
| 总览文档 | ARCHITECTURE.md | 边界划分、错误语义、护栏清单 |
小结
OpenClaw Studio 的架构可以浓缩为一句话:"连接在服务器,事实在 SQLite,浏览器只消费 HTTP + SSE"。服务器控制面让 Gateway 连接成为进程级单例,SQLite outbox 让事件持久、幂等、可回放,WebSocket 适配器让断线与重连完全自动化。理解这三块,你就掌握了这个控制台"消息不丢、状态可信、连接自愈"的全部秘密,也为二次开发(新增意图路由、扩展投影字段)找到了正确的落点。
【免费下载链接】openclaw-studioA clean web dashboard for OpenClaw. Connect your Gateway, manage agents, and ship faster. ⭐️ Star if you like it!项目地址: https://gitcode.com/gh_mirrors/op/openclaw-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考