03-19~20:稳定性修复¶
这是 ClawCraft 项目中最关键的稳定性修复阶段,彻底解决了拖拽建筑导致 SSE 无限重连/页面卡死的问题。
Dad 的关键产品决策¶
开发环境架构决策¶
问题背景:开发环境 craft.dev.dora.restry.cn 需要指向真正部署 ClawCraft 插件的服务器 owl
关键约束:不要动 clawedbot 的 OpenClaw,开发环境应测试 owl 上安装的插件
网络方案:
- owl 的 18789 外网不可达
- 内网 172.16.0.8:18789 可达
- 采用 内网反代 方案
安全与认证临时决策¶
问题:页面出现 /clawcraft/action 疯狂循环调用
Dad 临时决策:
开发模式先禁用 Gateway token,但把这件事记录为严重安全事件,等功能稳定后再补安全
执行:Gateway 改为 auth=none,但保留 loopback 绑定
系统化审查启动¶
03-20 晚间:
- 核心 bug 修复后,Dad 要求执行 /audit
- 要求"一口气把剩余 7 项都搞定"
技术问题与根因分析¶
SSE 死循环问题(核心问题)¶
现象:
- 一拖拽建筑界面就卡死
- layout.load 在极短时间内被调用几十次
- SSE 连接数从个位数暴涨到 70、250、467
排查过程中的错误假设:
1. ❌ 误以为是 layout.save 触发 broadcast → SSE → 重渲染
- 事实:actionLayoutSave 并不 broadcast
2. ❌ 误以为是 Caddy 未正确 flush SSE
- 确实是问题之一,但不是唯一根因
3. ❌ 误以为是 token 获取失败导致重连
- 部分相关,但不是最深层原因
根因 1:SSE 代理缓冲¶
问题:两层 Caddy 都缺少 flush_interval -1
表现:text/event-stream 被缓冲,浏览器收不到 connected 事件
后果:触发 EventSource.onerror -> reconnect 死循环
修复:
根因 2:getAccessToken() 风暴¶
真正导致无限重连的核心:
- 前端 useSSE / authFetch 在开发环境仍不断调用 Logto getAccessToken()
- 在"后端已跳过认证"的开发模式下,前端仍执着获取 token
连锁反应:
1. effect 依赖不稳定
2. 连接建立后很快被清理
3. 旧连接的 onerror 又触发重连
4. 新旧连接互相打架
5. 形成指数级 SSE 风暴
根因 3:Gateway vs 插件双层认证混淆¶
关键认知澄清:
- ClawCraft 虽然是插件,但 /clawcraft/* 请求仍然必须经过 Gateway
- Gateway auth 与插件自身 auth 是两层机制
问题:即使 Gateway auth=none,/clawcraft/* 仍返回 401
根因:插件自己实现了 withAuth(Logto JWT 验证)
解决:
- 给插件增加环境变量 CLAWCRAFT_AUTH_SKIP=1
- 注意:最初改错位置,Gateway 实际加载的是 ~/.openclaw/extensions/clawcraft/index.ts
- 更新 extensions 目录版本并清理 jiti 缓存后才生效
解决方案¶
最终修复方案¶
- Caddy 配置:两层 Caddy 都加
flush_interval -1 - 前端:开发模式下移除/绕过
connect中的getAccessToken()调用 - effect 依赖:稳定
useSSE的 effect 依赖,避免因函数引用变化反复重跑 - 版本号:增加版本号显示到左下角,便于确认部署是否生效
版本号格式:b03201117(b + MMDD + HHMM)
v0.9.5 系统化审查优化¶
安全:
- 用 react-markdown 替换 dangerouslySetInnerHTML
- 清理无用依赖
认证性能:
- authFetch token 缓存,减少 Logto setIsLoading 风暴
可访问性:
- 为 ReconnectBanner、ResourceBar、WelcomeOverlay、EventTicker、ChatDrawer、面板关闭按钮等补充 ARIA
视觉与动效:
- 去掉不必要的 animate-pulse
- 统一 gray -> slate
- 版本号格式更有意义
交互:
- WelcomeOverlay 加 focus trap
- 移动端提示替代硬编码 min-width:1280px
- 统一原生 select 为 StyledSelect
- 常见文案抽到 i18n.ts
性能/结构:
- ChatDrawer 采用"限制渲染数量 + 加载更多"
- WorldCanvas.tsx 大幅拆分,提取 world-helpers.ts
- 文件从 1912 行降到 1151 行
音效问题排查¶
现象:交互音效消失
排查结论:
- 代码层面音效系统、事件绑定、soundManager 基本完整
- 浏览器 AudioContext 的 resume() 是异步的
- 更可能是浏览器缓存/资源缓存导致加载了旧版 chunk
- Caddy 未对 index.html 明确设置 Cache-Control: no-cache
里程碑¶
03-19¶
- [x] 开发环境接入 owl
- [x] 识别认证与环境耦合问题
- [x] 插件
CLAWCRAFT_AUTH_SKIP=1机制 - [x]
/clawcraft/config返回 200
03-20 白天¶
- [x] 定位 SSE 代理缓冲问题
- [x] Caddy
flush_interval -1修复 - [x] 定位
getAccessToken()风暴
03-20 晚上¶
- [x] SSE 死循环彻底修复
- [x] Dad 确认"终于修好了"
- [x] 执行
/audit系统化审查 - [x] v0.9.5 版本发布
版本记录¶
| 版本 | 时间 | 主要变更 |
|---|---|---|
| v0.9.5-b03201117 | 03-20 11:17 | SSE 修复 |
| v0.9.5-b03201154 | 03-20 11:54 | 安全优化 |
| v0.9.5-b03201213 | 03-20 12:13 | 可访问性 |
| v0.9.5-b03201220 | 03-20 12:20 | 视觉统一 |
| v0.9.5-b03201231 | 03-20 12:31 | 交互优化 |
| v0.9.5-b03201245 | 03-20 12:45 | 性能/结构 |
关键经验总结¶
SSE 经过反向代理的必备配置¶
否则浏览器会因收不到流式事件而无限重连。
前后端认证状态必须同步¶
开发模式下若后端跳过认证,前端也必须同步跳过 token 获取;否则会出现认证逻辑与连接生命周期互相干扰的问题。
Gateway auth 与插件 auth 是两层¶
即使 Gateway auth=none,插件自身的 withAuth 仍会生效。排查认证问题时必须区分这两层。