Clawline

Clawline 产品族总览页。Web/桌面客户端 + WebSocket Gateway + 文档站,已合并为 monorepo clawline/platform

📦 本页由 5 个旧页合并而来(2026-05-09):clawline-plugin, clawline-gateway, clawline-client-web, clawline-docs-site, clawline-inbox-filter-optimization。

概述与历史

概述

Clawline 是从 OpenClaw 的 generic-channel 插件演变而来的独立项目(153条消息),包含 Web 客户端和小程序客户端。项目经历了命名讨论、组织创建、仓库拆分和代码迁移等阶段。

2026-05-06 重大调整:仓库结构从「拆分多 repo」翻转为「monorepo」——channelclient-webgateway 三个独立 repo 合并到 clawline/platform monorepo(pnpm workspace,apps/{channel,client-web,gateway})。旧三个 repo 标 deprecated 并本地删除。详见 decisions

关键事件

  • 2026-03-17: 讨论 generic-channel 插件的重命名,要求从营销角度取有意义的名字
  • 2026-03-17: 确定项目名为 “Clawline”
  • 2026-03-17: 规划仓库结构 ⚠️ 当时决定用 monorepo(已被 5-06 推翻)
  • 2026-03-17: 创建 GitHub 组织 clawline,owner 使用 restry 账号
  • 2026-03-17: 客户端仓库命名用 app 风格(client-xxx),体现客户端概念
  • 2026-03-20: 迁移 openclaw-generic-channel 插件代码,需要替换所有 generic-channel 引用
  • 2026-04-05: 组织建设和插件开发持续进行
  • 2026-05-06: ⭐ 三 repo 合并为 clawline/platform monorepo(decisions 5-06);watch + auto-deploy 改为单 SHA 监控,详见 monitoring-and-cron

仓库结构(2026-05-06 后)

  • 组织名: clawline
  • 当前主仓: clawline/platform(monorepo,pnpm workspace,apps/channel apps/client-web apps/gateway
  • 小程序: client-miniprogram(独立 repo)
  • 已 deprecated: clawline/channelclawline/client-webclawline/gateway(5-06 内容已迁入 platform)

经验教训

  • 项目命名要从营销角度考虑,让一般人也能理解项目意义
  • 迁移代码时必须全局替换所有旧名称引用(generic-channel → clawline)
  • 使用 GitHub 组织管理相关仓库,便于权限和协作管理
  • 拆分仓库比 monorepo 更适合独立发展的子项目

ChannelBot 自动化运维

Clawline-channel 项目配备了专属 Bot ChannelBot,负责自动化项目维护:

  • 定时执行工作区巡检,生成结构化维护报告(CONTEXT.md 状态、Git 提交、TypeScript 编译、GitHub Issues)
  • 自动更新 CONTEXT.md,补录最新 commit 和任务状态
  • 通过 codex-delegate 技能启动 Codex 执行长时任务(超时 5 小时)
  • 追踪开发进度:断点续传(stream resume)、分页历史、自动生成会话标题等功能

近期开发动态(2026-03-18~24)

提交内容
229d5c9feat: paginated history with before cursor + hasMore
fce268afeat: auto-generate conversation title
101c1dafix: stream state replace vs append
97e10bbfeat: chat-level stream state (断点续传)
77e83bcfeat: upload local files to relay server instead of base64 over WebSocket
595e977feat: update monitor.ts with enhanced monitoring logic

待处理问题

  • Agent→User 文件/图片发送可靠性验证(2026-03-21 提出)
  • OpenClaw 新版本对插件的影响分析(2026-03-29 提出,要求只分析不行动)

相关主题

2026-05-07 npm 首发 @clawlines/channel@1.0.0 + ghost-outbound 根因修复

apps/channel 第一次以 npm package 形式对外发布,安装方式从「OpenClaw 直接读 workspace 里的 channel-repo 源码」切到 openclaw plugins install @clawlines/channel。同 commit 一并修了下文 Clawline(本页) 详述的 ghost-outbound bug 根因。

包名@clawlines/channel
版本1.0.0
commit7ebed30
tagchannel-v1.0.0
发布时间08:26 CST
安装姿势openclaw plugins install @clawlines/channel(本机产生 ~/.openclaw/plugins/@clawlines/channel/
Token 存放vault key clawline/NPM_PUBLISH_TOKENfries-mac 门神 vault)

踩坑:npm org publish 必须用 Automation Classic token

第一次 npm publish --access public 报 OTP-required,组织开了 publish 2FA。Granular access token / npm token create 默认那种--otp=... 路径上不被 npm 服务端接受,必须去 npmjs.com Account → Access Tokens → 选 Classic Token + Automation 类型创建一个;automation token 自动绕过 publish-2FA。生成完直接塞 vault clawline/NPM_PUBLISH_TOKEN 后跑 NPM_TOKEN=*** get clawline/NPM_PUBLISH_TOKEN) npm publish --access public 即过。

升级路径变更

git pull workspace 内的 channel-repo + 重启 gatewayopenclaw plugins install @clawlines/channel@latest + 重启 gateway
paths: ["/home/.../workspace-clawline-client-web/channel-repo"]npm package 自动落 ~/.openclaw/plugins/@clawlines/channel,OpenClaw 自动发现

dev 环境仍保留 source-mode(监 apps/channel 目录变化 3 分钟自动 build + restart),见 Clawline(本页) 与 monitoring-and-cron

OpenClaw SDK 迁移(2026.3.8 → 2026.4.1)

以下信息来源于 webbot 的 Mattermost DM 聊天记录。

版本升级历程

日期版本事件
2026-04-013.8 → 3.28Channel 插件 SDK subpath imports 迁移(9 个具体子路径替代根路径)
2026-04-013.24 → 3.28Owl 服务器升级,验证插件兼容性
2026-04-023.28 → 4.1Owl 升级到最新版,全链路 E2E 验证通过

SDK 迁移要点

  1. Subpath Exports 扩展(44 → 238)— 旧版 deep import 靠 jiti 动态解析,新版正式导出
  2. ChannelPlugin 新增 5 个可选 adapter:lifecycle、execApprovals、allowlist、bindings、conversationBindings
  3. Runtime store — 手写 getter/setter 迁移到 createPluginRuntimeStore("clawline")
  4. API 适配 5 处 breaking changes — pairing 加 accountId、dm→direct、typing 回调合并、textChunkLimit 参数改位置、IdentityConfig 去 description

Agent 隔离修复(2026-04-04)

发现并修复了工具调用跨 Agent 泄漏的安全问题:

文件改动
tool-events.ts去掉 broadcast(),改为遍历 getConnectedClients() + sendToClient(),复用 agent 隔离逻辑
presence.ts同上,避免用户状态跨 agent 泄漏

修复后代码中不再有任何 broadcast() 调用,所有事件都走 sendToClient 的 agent 隔离路径。

断点续传审计结论

核心链路健壮:流式断点有 stream-state 内存缓存 + 重连恢复,最终消息无论客户端是否在线都会持久化到 history 文件。唯一极端场景是 gateway 在 message.send 后 100ms 内崩溃(概率极低)。

Gateway 服务端 (admin-new)

以下信息来源于 gatewaybot 的 Mattermost DM 聊天记录(2026-03-17~22)。

Gateway 是 Clawline 的服务端中继组件(clawline/gateway 仓库),由 GatewayBot 开发维护。包含管理后台 admin-new/ 和中继服务 server.js

管理后台技术栈

技术版本/说明
React19
Vite6
Tailwind CSSv4 (@tailwindcss/vite)
UI 组件库shadcn/ui (Radix UI)
图标lucide-react
认证@logto/react
动画framer-motion
通知sonner

认证架构

采用 Logto OAuth2 JWT 全流程认证:

  • 前端@logto/react SDK → getAccessToken('https://gateway.clawlines.net/api') 获取 JWT
  • 后端jose 库 JWKS 验证 JWT(iss: https://logto.dr.restry.cn/oidcaud: https://gateway.clawlines.net/api
  • Fallback:保留 legacy admin token 兼容

数据存储演进

  1. V1:本地 data/relay-config.json 文件存储
  2. V2:Supabase PostgreSQL(cl_channels + cl_channel_users 表),通过 /pg/query 端点执行 SQL

REST API

接口方法说明
/api/stateGET获取完整状态
/api/metaGET公开元数据(无需 auth)
/api/channelsPOST创建/更新 channel
/api/channels/:idDELETE删除 channel
/api/channels/:id/usersPOST创建/更新 user
/api/channels/:id/users/:senderIdDELETE删除 user

部署

关键提交

Commit内容
7b27b65feat: add admin-new UI with real API integration
3696159feat: OAuth2 JWT 全流程认证
87da32bfeat: add Supabase storage backend with /pg/rest/v1 API
2e5fb54fix: UI 审计三轮修复(accessibility + typography + normalize)
cdc9e24

2026-04-07 ~ 04-14 更新日志

版本迭代(ottor 频道)

日期变更提交
04-07修复 outbound adapter agentId 提取防消息串台、DM 路由 chatId 解析、Azure OpenAI 端点兼容、文件发送 contentType9347056faf9fb6
04-07agentId 缺失时回退广播 → 抛错 → 发送 status.failed WS 事件faf9fb6781404b (v2.0.0)
04-09切换到 dev 分支,新增 messageId dedup 防重复分发7d4cddf
04-10跨 agent DELEGATE 消息分发、Supabase 离线消息持久化、子 agent 出站修复226734c
04-12新增 resolveInboundConversation 支持 ACP thread bindingfeat(clawline)
04-14提交 threadId 传递修复 + gateway build 产物更新6be9d78, b38e0be

DELEGATE 跨代理消息功能(04-10)

语法:<<DELEGATE:target-agent-name>>消息内容<</DELEGATE>>

  • Clawline 服务端自动解析,路由给目标 agent
  • 用户侧显示 [Delegated task to **xxx**] 占位符
  • 最大嵌套 3 层防无限循环
  • 一条回复可放多个 DELEGATE 标签

文件发送测试(04-07)

通过 ottor 频道验证文件发送功能:

  • 支持 filePath(本地路径)、media(本地/URL)、buffer(Base64)
  • 支持文本、图片、PDF、文档等格式

项目结构(04-12 via ACP Claude)

模块说明
gateway后端网关服务(Node.js + Supabase)
client-webWeb 客户端
client-wechat微信小程序客户端
channel消息通道/SDK 库
browser-agent浏览器自动化代理
claude-extension-devChrome 扩展(开发版)

browser-agent — Code Agent 的浏览器抓手(2026-04-15)

仓库 git@github.com:clawline/browser-agent.git,main 分支。

Chrome MV3 扩展 + 本地 Native Messaging Host,把任意 code agent(Hermes / Claude Code / Codex …)一条 HTTP 调用就接进真实 Chrome——用你已登录的会话、cookies、扩展状态做截图/点击/填表/验证 UI,无需 Puppeteer / Playwright / 云服务

链路与延迟

传统:    Agent → HTTP → Server → WebDriver → 启动 Browser → Page
browser-agent: Agent → HTTP :4821 → Native Host (binary IPC) → Service Worker → Side Panel → Page
  • Chrome Debugger Protocol 直接驱动页面(与 DevTools 同层),跳过 WebDriver
  • Chrome Native Messaging 走 binary IPC、不走网络 → 零网络开销
  • 自定义 Accessibility Tree content script 给 LLM 喂语义化结构,不让模型啃原始 HTML
  • 浏览器已经在跑、截图走 chrome.debugger 原生 ~50ms,普遍快过传统方案

模块布局(2026-04-15 整理后)

路径内容
sidepanel.js (1662 行)Side Panel UI(Ctrl/Cmd+Shift+E 唤起),跑本地 Claude API
service_worker.js (252 行)Debugger / Tab 管理
native-host/ (311 行)Node.js HTTP Hook 服务,http://127.0.0.1:4821install.sh 装 macOS Native Messaging Host
docs/HOOK_API.md= 给 code agent 用的 skill:所有端点 + 请求/响应格式
docs/CODE_AGENT_INTEGRATION.md英文集成指南:架构图、3 步安装、curl 示例、错误码
docs/WHY.md英文价值定位 + 技术差异化(Debugger Protocol / Native Messaging / A11y Tree)
docs/reference-system-prompt.txt, reference-tools.json扩展内部 Claude 用的参考(不是外部 skill)
README.md安装/使用/结构(精简版)

04-15 之前 HOOK_API.mdinstall.sh 混在 native-host/ 里发现不到,整理后统一进 docs/,并补上 .gitignorenode_modules/, error.log)。Manifest V3 权限:debugger / sidePanel / tabs / nativeMessaging / scripting。

关键提交(近期)

77d9bc9 fix: correct zoom coordinate scaling and add Return key alias
d0481ef feat: Add Clawline Native Messaging Host with HTTP Hook API
66fb535 feat: add zoom and scroll_to computer actions
41e5f40 fix: lock target tab at agent start — survives user tab switching

2026-04-16 browser-agent Code Review 与全仓 push

源:fries-mac(lewaymacmini-3-local)。Hermes 委派 Claude Code 对 clawline/browser-agent 跑完整 code review,并把 clawline 下所有子仓的待提交改动一次性推到远程。

Code Review 关键发现

🔴 Critical

  1. HTTP Hook(http://127.0.0.1:4821无认证 + CORS * — 任何本地进程或网页都能控制 Chrome;用户决定暂不修(加 Bearer token 调试太麻烦,本地 dev 优先)
  2. Network 事件监听器泄漏:每次 tab 切换新增 listener 不移除,长跑会内存泄漏 — 已修
  3. HTTP 请求体无大小限制,可被 OOM 攻击 — 已修

⚠️ 已修的 Warning

  • 重连无指数退避(潜在无限循环)
  • 硬编码 1500ms 等 sidepanel 注册改为轮询,消除竞态
  • escapeHtml 补全(innerHTML 恢复对话的 XSS 边界)
  • IndexedDB 连接断开处理
  • 空 catch 加 warn
  • 注入脚本全局变量前缀 __claude__clawline(content-script.js 与 sidepanel.js 的 executeScript 引用必须同步改名)

✅ 评价良好:Native Messaging 协议、screenshot 优化、坐标缩放、SSE Parser、Tab 锁定、WeakRef DOM 引用

提交结果

子仓分支结果
browser-agentmainpushed(review 修复)
channeldevpushed(docs)
client-wechatmainpushed,pre-push hook 强制 0.1.0 → 0.1.1 版本 bump
docsmainpushed(.gitignore)
client-web / gatewaydev无变更

同日把 clawline/channelralph/thread-discord-style 分支 fast-forward 合并到 dev 并 push(+494 行:thread 类型定义、session bindings、WebSocket 客户端扩展,共 8 个文件)——thread 支持完成主干合并。

经验

  • Claude Code 跑 40 轮上限会停在中途,需要人工补完同步改动(如 content-script ↔ sidepanel 的注入变量重命名)
  • Hermes 启动 Claude Code 必须先 source ~/.zshrc,否则拿不到自定义的 Copilot→Anthropic 代理 API key
  • Dev 阶段安全修复要权衡调试成本,不是所有 Critical 都立刻修

2026-04-19 Reliability v2 重构(first-principles)

ottor-laptop 全天主线:把 channel + gateway + client-web 三仓的可靠性问题用第一性原理推倒重来。

PRD v1.5 — 6 大产品支柱

整理出可执行的产品 PRD(docs/prd/PRD.md v1.5),围绕 6 个支柱:① 实时连通性 ② 会话/线程一致性 ③ 消息送达零丢失 ④ 跨端体验对齐 ⑤ 失败自愈 ⑥ 可观测。配套 7 项用户决策表(thread 编号策略、断网重发口径、消息序号机制等)。

90 case e2e 测试框架

tests/e2e/ 三类共 90+ 用例:

  • REL- 可靠性*(断连/重连/丢包/重复/乱序/慢消费)
  • TH- 线程*(thread 创建/切换/嵌套/历史拉取)
  • INBOX- 收件箱*(21 case,截至 04-19 18 PASS / 3 FAIL,剩余在 D-12 修)

测试 baseline 从 v1 跑到 v8,每轮锁定一类 root cause。

12 D-items + 7 ADD-BACK

按第一性原理拆问题 → 12 个删除项(D-1..D-12,移除「猜测/绕路/补丁」代码)+ 7 个加回项(ADD-BACK-1..7,补「正确的最小机制」):

代表项
D-DeleteF1b FIFO fallback 路由(猜目标 connection)、broadcast() 兜底、双层 reconnect timer、stream resume 内存表外的磁盘脏副本
ADD-BACKapiSessions Map(virtualConn 与 realClient 物理隔离)、requestId-based callback key、单一 fanOut 出口(4 个 broadcast loop 合 1)、可配置超时 600s

配对 URL 修复

pairing 流程 client-web 接出 URL 长期错写 ws://localhost:19180/?...,正确为 ws://localhost:19180/client?channelId=fires&...,修后桌面 mock-backend 可一次拉起完整 e2e。

相关 Hermes skill

落地 software-development/first-principles-thinking/ skill,并写入 user memory:Daddy 默认思维方式 = 第一性原理。

详见 Clawline(本页) reliability v2 server.js 改动、clawline-thread-support TH-1..TH-8 修复。

2026-04-21 Gateway SSE + browser-agent 加固 WIP

源:fries-mac(lewaymacmini-3-local)。Reliability v2 收尾后主线推进 SSE streaming,browser-agent 攒了一笔硬化 WIP。

Gateway / docs / client-web / channel 进展

  • gateway(22h 前):/api/chat 改 SSE 流式 + 取消 600s 硬上限;D6/REL-06 inbound 持久化无条件化。
  • docs:同步 SSE timeout 语义 + onboarding flag 清理 + 测试 baseline v10。
  • client-web:D12 keyspace 统一为 clawline.*(带迁移)、TH-7 thread 时序、Bug A bounce、Get Started flag 收尾。
  • channel:D4 reply-dispatcher 由 3 层 fallback 缩为 1 层;threadId 从协议直接取。

browser-agent WIP 86b99f8(2026-04-19,+765/-226,4 文件,未合并

单次提交捆 6 方向,整体「安全 + 体验 + 稳定」:

  1. XSS / 密钥防泄露
    • sidepanel.jssanitizeHtml() — 基于 DOM 的 allowlist 清洗(~67 行),强制 <a target=_blank rel=noopener>href/src 仅放行 http(s),<img> 额外允许 data:image/。覆盖 saveCurrentState/switchConversation/messagesEl.innerHTML 三个 AI 输出 / 工具结果 / 历史恢复路径。
    • native-host/index.jsredactSecrets() — 写 error.log 前过滤 sk-ant-* / sk-* / Bearer xxx / x-api-key|api_key|authorization: 四类。
  2. Hook API 稳定性(native-host)
    • 动态端口EADDRINUSE 不再原地重试,4821 → 4822 → … +10 递增;监听成功后通过 {type:'hook_port'} 推给扩展,GET / 状态 JSON 也带 port
    • HTTP 客户端断开清理res.on('close')onClientGone(),清 timer + 删 pendingRequests + 给 Chrome 发 hook_stop。修了断开后 timer 累积到 REQUEST_TIMEOUT 的旧坑。
    • hook_response 幂等:多次 started 不再覆盖 resolver,防 rogue started 吞掉最终响应让 HTTP 挂死。
  3. Sidepanel 体验
    • Sidebar 改浮层(position: absolute + transform: translateX + backdrop,240→260px)、5px 细 webkit scrollbar、去掉头部 “Clawline” 标题、设置面板加 History Export/Import。
  4. Agent 能力:新 SKILL_MODES 常量、_ensureDebuggerInner / _waitForLoadComplete / _waitForSelector / waitForSettle / postActionWait 等待原语。

Daddy 2026-04-21 决定:browser-agent WIP 先挂着不动

多实例 busy 路由(2026-04-21 复盘)

每个扩展实例 = 1 个 native-host + 1 个 sidepanel,进程独立,互相不发现、不协调、不 load-balance

  • sidepanel.js:2331 if (isRunning) → 立即 error: "Agent is busy with another task",无队列。
  • 两个实例分别占 4821 / 4822,第三个请求无论打哪边都即刻拒绝;body.tabId 也救不了 — service-worker 路由按 windowId 查 sidepanel port,到不了别的扩展实例。
  • 客户端唯一稳妥姿势:先 GET /chromeConnected + port + pendingTasks===0 再选端口发。
  • 想要排队 → native-host /hook 自己加队列 + 返回 202 Accepted + Location: /status/:taskId,但单实例仍是一次一任务。

2026-05-08 sync /api/messages/sync 缺省过滤导致跨 chat fanout

来源:fries-mac 排查 nexora main agent 把一份「Nexora 平台全面代码分析报告」的 5 条 outbound 错落到 chat=nexora-ops 而非 chat=nexora

根因:sync 接口三个过滤参数中 channelId 必传,chatIdagentId 都是可选。前端在 cobra 里拉 main agent 历史时只传 agentId=main,未带 chatId,server 端遂把所有 chat 上 main agent 的最近条目都拉回来;恰好 ops 在同一 channel 也用 agent_id=main 写入了 5 条毫秒级 push 的子任务消息(22:55:50.799–.831),就跨 chat 串到了 cobra 端展示。是预先存在的 bug,不是 thread 移除引入的,但 thread 移除把这条 fanout 路径放大可见

修法方向:sync filter 把 chatId 改为强制必传(或在 chatId 缺失时按 channel+agent 限定到该会话最新一条而不是全量历史)。Skill / 历史调用方需同步跟进传参。

教训:远端 ops agent 的 outbound 与本端 main agent 同 agent_id=main 共用全局命名空间,任何 ops/main 共名 + chatId 缺失 的组合都会触发同款泄漏。

2026-04-27 channel feat(stream): tag delta with phase=thinking|answer(commit 1518eb4

reply-dispatcher.ts +35/-5send.ts 小改:流式 text.delta 帧带 phase=thinking|answer 标签,让 client-web 能区分 AI 思考阶段与最终回答阶段(之前两段混在同一气泡里)。

OpenClaw 的 clawline 插件实际加载路径不走 plugins/不走 extensions/,而是直接从 workspace channel-repo 读:

"paths": ["/home/resley/.openclaw/workspace-clawline-client-web/channel-repo"]

升级流程 = git pull channel-repo + 重启 gateway。

相关页面

Gateway

Clawline 的 WebSocket 消息中继网关 + 管理端,负责 Agent ↔ Client 通信、Channel/User 管理、认证和消息持久化。

概述

Clawline Gateway 是 Clawline(本页) 产品族的后端中枢,提供 WebSocket 消息中继、管理端 UI、认证集成和数据持久化。最初由 gatewaybot(已淡出)开发,现由 Clawline(本页) Bot 统一维护。

项目信息

属性
仓库clawline/platform (monorepo apps/gateway),旧 clawline/gateway 已 deprecated(2026-05-06 合并)
技术栈Node.js + React 19 + Vite 6 + Tailwind v4 + shadcn/ui
生产环境https://gateway.clawlines.net
开发环境https://gw.dev.dora.restry.cn
开发端口前端 3020 + 后端 3021
现维护 BotClawline(本页) Bot(原 gatewaybot 已淡出)

架构

消息中继

Gateway 作为 WebSocket 中继层,连接 OpenClaw Agent 和各客户端(Web、微信小程序等):

Client (Web/WeChat) ←→ Gateway (WS Relay) ←→ OpenClaw Agent
  • 心跳机制:客户端 30s 间隔 ping,Gateway 回复 pong(commit fc02056 修复)
  • 容错:允许 2 次未回复再判定超时
  • 断点续传:客户端断连后通过 stream.resume 恢复流式内容

管理端 UI (admin-new)

科幻风格三层架构:

层级内容说明
TIER_1Gateway 状态概览channels、backend links、live clients
TIER_2Channel 管理水果命名 + emoji 图标(🥭 mango、🍒 cherry)
TIER_3User 配置动物命名(falcon、octopus)

功能:Channel/User CRUD、自动刷新(5s)、连接 URL 生成(openclaw://connect?serverUrl=...

shadcn/ui 重构

引入 Button/Dialog/Input/Table 组件,App.tsx 从 2000+ 行拆分到 <200 行。

认证系统

完成了三代认证方案演进:

版本方案说明
V1Admin Token 手动输入X-Relay-Admin-Token header
V2Logto SSO + Admin Token二次认证
V3 (最终)Logto SSO → JWTAuthorization: Bearer + 后端 JWKS 验证

Logto 配置:

属性
SPA App IDanbr9zjc6bgd8099ecnx3
API Resourcehttps://gateway.clawlines.net/api
JWKS Endpointhttps://logto.dr.restry.cn/oidc/jwks

详见 logto-sso

数据持久化 (Supabase)

从本地 data/relay-config.json 迁移到 Supabase PostgreSQL:

用途
cl_channelsChannel 配置
cl_channel_usersChannel 用户

API 演进:/pg/query/pg/rest/v1(因 PostgREST schema cache 未刷新,REST API 曾返回 404)。

消息持久化已验证(Supabase 有真实消息记录)。

CI/CD

  • GitHub Actions:移除 tarball 打包(tar “file changed” 竞态问题),只保留 Docker 镜像构建
  • 关键提交:7b27b65(UI 集成)、3696159(JWT 认证)、87da32b(Supabase 存储)、2e5fb54(UI 审计修复)

UI 审计历史

经历完整三轮 UI 审计修复:

轮次技能修复内容
Round 1/hardenFocus indicators、WCAG AA 对比度、Modal Escape、触摸目标 44px+
Round 2/typeset + /distill字体大小 10px→12px、Header 简化、移除装饰性动画
Round 3/normalizeCSS 变量设计令牌、sans/mono 字体统一(47→13 处)

全流程测试记录

researcher agent 通过 Claude Code CLI 进行本地测试:

功能结果
Admin UI
AI Settings GET/PUT
Suggestions API✅ 返回 7 条建议
Voice Refine API✅ 语音转写清理
消息持久化✅ Supabase 有真实消息

Bugmax_tokensmax_completion_tokens 兼容性问题(GPT-5.4-mini),commit 12bb93d 修复。

部署迁移(2026-04-06)

gw.dev.dora.restry.cn 管理端经历了一次重大迁移:

  • 原状:pm2 dev-gw 从旧的 workspace-clawline-gateway/repo/admin/dist serve 静态文件
  • 根因:research agent build 到了错误目录
  • 迁移:切换到 workspace-clawline-client-web/gateway-repo/public,从独立 gateway 仓库转移到 Clawline(本页) Bot 管辖
  • 遗留:旧版 Logto token 残留导致白屏,需手动清除浏览器 logto 缓存

2026-05-08 — sync 接口 scope 防呆 + 前端预热分桶(commit 4754d6b

排查到一个隐蔽 bug:cobra 端 main agent chat 视图里出现来自 nexora-ops chat 的 5 条 outbound 消息(22:55:50 同毫秒密集 push)。链路追踪后不是 thread 移除回归,是 /api/messages/sync 过滤规则一开始就允许「传 agentId 不传 chatId」→ 跨 chat 拉到全频道历史;agentInbox 的 500 条预热(fetchOlderMessages(channelId, ..., undefined, 500))也同样不带 chatId,把跨 chat 消息塞进了 cache,进入 cobra/main 视图就被读出来。

修法(单 commit):

  • A1 Gateway/api/messages/syncscope 防呆 —— 默认 chat 模式必传 chatId;要跨 chat 拉必须显式 scope=channel。漏配返 400。
  • A2 前端useChatMessages / fetchOlderMessageschatId 时不发请求;agentInbox 预热改按 (agentId, chatId) 二维分桶 seed cache,避免单桶被跨 chat 历史污染。
  • C bindingsbindings.ts 重建(去 thread 化),channel.ts 重新挂 bindings: clawlineBindingsProvider

CC 自纠 2 个隐蔽问题:① supabase env 短路检查放校验前面,避免 401 被误报;② 修了 useChatMessagesenabled = !!chatId 后引入的 first-entry 历史空显示回归 —— ChatRoom 首次进入 chatId 经常 undefined(要等 WS connection.open 回包),hotfix 在 ChatRoom 调用处用 chatId || activeConn?.chatId 兜底再 refetch。

环境分清web.dev.dora.restry.cn 是 build 部署版(dev 分支构建产物),跟本地 Vite HMR :4026 不是同一个东西 —— hotfix push 到 dev 分支不自动触发前者的部署,要看线上必须等 GitHub Actions / 手动 build。CLAUDE.md 已记。

2026-05-09 Gateway admin favicon + 部署域名澄清

apps/gateway/admin/ 加站标 SVG(品牌橙 EF5A23)替换 Vite 默认图标,覆盖 light + dark theme。commit 96e6015 推到 platform monorepo dev,3 分钟自动部署链路同步刷到 gw.dev.dora.restry.cn

部署域名澄清(5-08 web.dev/gw.dev 混淆延伸):gateway admin 的 dev 部署落点是 gw.dev.dora.restry.cn,不是 web.dev.dora.restry.cn(后者是 client-web)。两者由独立 pm2 进程承载,build 后 favicon 各自部署,不要在 client-web 仓库改 admin 图标。

时间线

日期事件
2026-03-17GatewayBot 上线,完成 admin-new UI 集成
2026-03-18Logto SSO + JWT 全流程、Supabase 迁移
2026-03-20三轮 UI 审计修复、响应式布局
2026-03-21shadcn/ui 重构,App.tsx 拆分
2026-04-06管理端部署迁移到 clawline-client-web Bot 管辖
2026-04-08Gateway 代码整理提交(DESIGN.md、server.js、schema.sql)

相关主题

详见 chat 端点设计:虚拟连接机制、请求参数、meta.source 标识透传和全链路改动汇总。

2026-04-20 更新:D6 inbound 持久化 + REL-06 护栏 + /api/chat SSE

整天三件事一气呵成(fries-mac,Claude Code 派工模式):

测试覆盖盘点(已知缺口)

docs/testing/e2e-test-cases.md 14 模块 90+ 用例,但自动化里仍有空档:REL-04 断线重连补帧lastSeenMessageId 机制有代码、自动化 SKIP)和 REL-05 重复消息幂等ADD-BACK #7 有代码、自动化 SKIP)只手测过;Channel 直连模式 C-01~C-06 整组 SKIP(直连保留,需 fork OpenClaw 实例跑);Onboarding W-01/03/04/05/06 卡在 Chrome profile 隔离,已用 clawline.onboarding.done localStorage flag 简化(commits e5b2c03d + f8e2be3d,docs commit 58879c7);分屏 W-22/23/24 受 sidepanel 宽度限制。Daddy 04-20 拍板修复顺序:REL-04/05 → 接 CI → onboarding → 直连 → 分屏。仓库一并清理 media/(e2e 上传产物 7 文件)追加 .gitignore(commit ae28e97)。

REL-06 护栏先行(commit eba48d5

mock-backend 支持 timeout/reject/drop 三种行为(按 evt.data.chatId 子串识别,无需重启),新增 REL-06a/b/c 断言”inbound 入库与 outbound 是否成功无关”。预期全 FAIL — 跑出来真的全 FAIL,证据精确指向 server.js:3283 D6 注释,确认 D6 设计本身就是 bug。

D6 修复(commit cebb5c4

旧设计「只有 ack 成功才入库」的代价是丢失用户原问 — 本末倒置(ghost row 可查询过滤,丢问题不可还原)。改为状态机式:inbound 进来就入库(不论 outbound 是否成功),删 pendingInbound Map / D6 stash GC / ack-then-persist 分支 / 所有 D6 注释。净 -20 行,0 新增 null-check / try-catch。DB 写失败:HTTP 500 / WS error/PERSIST_FAILEDREL-06a/b/c 翻绿;REL-02 ghost 断言收紧为 total − (ack+err) === 0

/api/chat SSE 流式(commit 5bda82d + docs 513f902

旧实现 /api/chat 同步 HTTP,600s 硬上限(长 agent 任务被掐),且 text.delta 帧只走 WS 不达 HTTP caller。

改造:Accept 头协商同 endpoint — Accept: text/event-stream 走 SSE 分支(事件序列 accepted → delta* → tool_call/result* → done | error,15s 心跳防代理断连,去掉 600s 上限靠心跳+事件驱动),其他 caller 同步 JSON 路径完全不动(路由表/鉴权/idempotency/REL-06 inbound 持久化全复用)。新增 REL-07a/b/c/d(正常 / 长任务 / 超时 / caller 主动断开)全 PASS。

经验教训(Daddy 借此事质问”为什么会出现这种低级问题”):D6 当初设计违反第一性原理 ① — 没问”用户消息是不是物理发生过的事实?“。/api/chat 设计时没问”agent 实际执行时间分布”。结构错了,补丁永远打不完;护栏(先写测试断言”应该的样子”再改代码)是阻断同类反复的唯一办法。

2026-04-12 更新

  • Gateway 新增 POST /api/chat 端点(169行),创建虚拟连接注入 channel 插件
  • 支持 meta.source 标识透传
  • 修复了 closeSocket null guard 保护虚拟连接
  • 部署方式确认:本地打包 → scp 上传 → rsync → restart(不在远程 git pull)
  • 详见 relay-gateway-api

2026-04-18 ~ 04-19 更新:API 回复丢失修复(server.js

Levis 反馈通过 POST /api/chat 发消息后,agent 回复在前端聊天历史里看不到。定位为 gateway-repo/server.js 中两层叠加的 Bug:

Bugcommit修法
A. agent isolation 把并发 agent 回复全部 buffer 不 deliver98c375a(04-18)virtualConnId 加入 agentId:api-{channel}-{chatId}-{agentId}
B. 未传 chatId 时借用其他在线 WS 的 chatId,导致 user:senderId 路由丢失00dec2f(04-19)`chatId = body.chatId

两个 commit 已部署到 relay(163.228.142.105)dev 分支。验证链路见 Clawline(本页)。

Dev Gateway WebSocket 配对(04-19 verified)

本地 dev gateway 监听 19180两个独立 endpoint

  • /backend — agent 后端连入(OpenClaw / Hermes)
  • /client — 浏览器/客户端连入(不是 /ws

Web client 必须连 /client 且 channelId 要匹配实际有 backend 的频道,否则收到 1013 backend not connected 立即断开。lewaymacmini 实测:openclaw-gateway 注册的是 fires 频道,配对 URL:

ws://localhost:19180/client?channelId=fires&senderId=Levis&token=***

dora / nexora / ottor 这些 token 虽存在但本地无 backend 连入 → 用了立刻断。

Reliability v2 server.js 重构(04-19)

承接 04-18 ABug 修复,按 first-principles 把 server.js 路由层推倒重写:

  • apiSessions Map 拆出POST /api/chat 创建的 virtualConn 不再挤进 realClients,独立到 apiSessions: Map<virtualConnId, {res, agentId, chatId, createdAt, timeout}>,物理隔离避免 agent 回复路由错乱
  • callback key 由 requestId 决定:旧实现用 chatId+agentId 拼 key,并发同一 chat 多 agent 时会覆盖,改成 requestId(client 端 uuid)一对一
  • fanOut 单出口:原本 4 处 for (client of realClients) client.send(...) 各自判断 dedup/agent 隔离,全部合并到 fanOut(payload, filter) 一个函数,删除 ~80 行重复代码
  • F1b FIFO fallback 移除:「找不到精确路由就发给最早连接的 client」是 ABug 的根因之一,删除该兜底,路由失败直接 status.failed + 日志告警
  • 超时可配置/api/chat virtualConn timeout 旧硬编码 60s,长 agent 任务(Claude Code、剧情生成)会被掐,改为 body.timeout || env.GATEWAY_API_TIMEOUT_MS || 600000
Step 5–10 重构序列(lewaymacmini,fries 派 Claude Code 自跑)

派工模式:Hermes 写好 /tmp/cc-fp-step*.md 计划文档 → zsh -ic 'cd ~/Projects/clawline && claude -c --dangerously-skip-permissions --print < /tmp/cc-fp-stepN.md' 后台跑 → SYSTEM 退出码 0 后回报。每 step 一个文档、一次执行、一次审计。

Stepcommit关键改动
5 / 5.5(合入 server.js 路由层)apiSessions 拆分 + requestId callback key + fanOut 单出口 + F1b FIFO fallback 删除 + timeout 可配
7 (D9)5036c27_threadUpdateChain Map + _doUpdateThreadOnNewReply 包装;updateThreadOnNewReply 不再写 reply_count 列,只更 last_reply_at + participant_ids + updated_at;新增 countThreadReplies(threadId) 按需聚合(ADD-BACK #1)
8–10跨 3 仓库 + OpenClaw 重启client-web localStorage 迁移:旧双 key(clawline.lastRead.X + openclaw.inbox.lastRead.X)取 max 写回 clawline.lastRead.X,旧 key 清除;Get Started 隐藏闭环(清 flag→Onboarding;点完→flag=1;reload /→直进 chats;显式 /onboarding 仍可访问);UI E2E 23/23 PASS、Console 0 error

PRD: clawline/docs/prd/message-reliability-first-principles.md。物理真相 1 句:「发出的每条消息要么最终在接收端出现,要么明确返回错误。不存在静默失败。结构错了,补丁永远打不完 — D1-D12 清单中除 D5(保留但简化为仅 @mention 触发)外全删。

2026-05-07 ghost-outbound bug RCA:WS fanOut 把 inbound 当 outbound 重广播

症状(dora 频道):消息历史里出现「同一条用户消息显示两次 / 一次 inbound + 一次 outbound」幽灵记录,dora 渠道 ghost 率约 34%(41 条 ghost),ottor 2 条 / fires 0 / nexora 0。

初判错向 — gateway server.js:2707-2711:WS 路径 fanOut 在向其他 client 转发 inbound 消息时,没标 direction: 'inbound' 字段就送给后端持久化路径 → 后端按 default 落成 outbound。这一处也确实有缺陷:fanOut 出口应该明确 message direction,不能依赖默认值。但这不是真正的 ghost 来源。

真正根因 — apps/channel/src/generic/client.ts:585-592:channel 客户端在收到 inbound message 后,会把它原样作为 message.send 事件再广播一次给所有已连接 backend,gateway 端 message.send 默认走 outbound 持久化分支 → schema 上 (channel_id, message_id, direction) 是 unique key,允许同一 msgId 同时存在 inbound + outbound 两行,所以「两条都成功落库」不会被去重拦下。

为什么只有 dora 渠道猛:跨渠道差异是因为 dora 频道挂的 daemon 5/7 那天还在跑发版前的旧 channel 代码(workspace-clawline-client-web/channel-repo 没拉新 commit),其他渠道当天已经升上含 fix 的 build。

修法

  1. apps/channel/src/generic/client.ts:585-592 —— 收到 inbound 后不要再以 message.send 重广播;如果 backend 真需要被通知有新 inbound,使用 message.received 事件且不进入 outbound 持久化路径
  2. apps/gateway server.js:2707-2711 —— fanOut 显式带 direction: 'inbound',防御默认值漂移
  3. 修复随 @clawlines/channel@1.0.0 (commit 7ebed30) 一并发布,08:26 上线
  4. 验证:08:30 dora 频道最后一条 ghost 出现(pre-fix daemon 还在跑),之后 0 新增;fries / ottor / nexora 当日 ghost 数无变化

Schema 启示(channel_id, message_id, direction) unique key 设计本意是允许 echo(同一 id 既可以是用户发的、也可以是 bot 回的同 id refer),但实践上从未用到这种语义,反而让本 bug 不被去重发现。后续考虑 evolve 成 (channel_id, message_id) 全局唯一 + 显式 direction 字段做语义标注(不参与去重),让”同一 msg id 落两次”在 DB 层就报错。

验证渠道:dev 三分钟自动部署链路(monitoring-and-cron)让 web.dev.dora.restry.cn / gw.dev.dora.restry.cn 在 fix push 后 ~3 min 即承载新 build,无需手动操作。

2026-05-06: 启动时 loadRelaySettings fire-and-forget bug → 重启后必须手动「保存」CORS 才生效

症状(Levis dora 频道):每次 relay gateway 部署后,要去 admin 端随便保存一下东西才能用,否则 CORS 白名单空、前端跨域全挂。

根因server.js 行 500 启动顺序 void loadRelaySettings().then(...) 是 fire-and-forget 没 await;行 4073 才 await loadRelayConfig() 然后 listen。如果 Supabase 响应慢或首请求在 loadRelaySettings 完成前到达,relaySettingsCache 被设为 {}(空)且永不刷新——admin 端任意保存触发 saveRelaySettings() 强写内存缓存才恢复。

修法:① 启动时 await loadRelaySettings() 在 listen 之前;② loadRelaySettings 加 TTL 刷新(expiresAt),DB 改 CORS 不需 restart 也能生效;③ saveRelaySettings 同步更新 expiresAt;④ 删掉旧的 fire-and-forget 调用。push 到 platform monorepo apps/gateway,cron 3 分钟内自动部署生效。

2026-04-14 更新:微信图片发送修复

Resley 反馈微信收不到图片。定位到 gateway/platforms/weixin.pysend_image_file 方法签名与基类 BasePlatformAdapter.send_image_file() 不一致——基类和所有调用方(含 _deliver_media_from_response)传 image_path=...,但微信适配器写的是 path。kwarg 不匹配 → 文件路径丢失 → 图片发不出。

修法:把微信适配器参数名 path 改为 image_path。34 个 weixin 单测全部通过,gateway 重启后微信图片投递恢复。

Client Web

Clawline 的 React Web 聊天客户端,提供多 Agent 对话、技能管理、PWA 离线支持等功能。

项目信息

属性
仓库clawline/platform (monorepo apps/client-web),旧 clawline/client-web 已 deprecated(2026-05-06 合并)
技术栈React 19 + TypeScript + Vite + Tailwind CSS
版本v0.1.0 → desktop v0.5.2(2026-04-27),版本号自动递增 = git commit 总数
开发端口3026
开发环境https://web.dev.dora.restry.cn
生产环境https://chat.clawlines.net
开发分支dev

核心功能

多 Agent 聊天

  • 支持连接多个 OpenClaw 服务器
  • Agent 列表网格/列表视图切换,拖拽排序(Reorder 组件 + localStorage 持久化)
  • 自定义 Agent 头像(右键/长按设置)
  • 消息历史同步、新建/切换会话

消息系统

  • 流式消息渲染 — 实时显示 AI 思考过程和 delta 输出
  • Markdown 渲染 — 代码高亮、inline code 暖色样式
  • 工具调用展示 — JSON 自动解析为可读文本
  • 消息操作 — Reply(引用回复)、Copy(剪贴板 + toast 反馈)
  • 断点续传 — 客户端断连后通过 stream.resume 恢复流式内容
  • API 直连标识meta.source = "api" 的消息显示 ⚡ API badge(04-12 新增)

技能系统

  • 斜杠命令菜单(/ 触发自动补全)
  • 技能三分类展示:工作区技能 + 全局技能(高亮)vs 内置技能(灰色折叠)
  • 药丸按钮交互:点击填入输入框,长按 800ms 直接发送

输入交互

  • WhatsApp 风格底部输入栏:+ 按钮菜单 + 输入框 + Mic/Send
  • 语音录制(带计时器显示)
  • 动态建议条(空输入显示命令 chips,bot 回复后显示上下文建议)
  • 移动端文字选择保护(ActionSheet 不劫持长按选中)
  • 发送后自动收起键盘(04-14 新增,document.activeElement.blur()

通知系统(04-14 修复)

  • PC 端焦点感知:从只判断 document.hidden 改为 document.hidden || !document.hasFocus(),窗口失焦但 tab 可见时也触发通知
  • 声音去重:agentInbox 判断当前是否在看目标 agent 的聊天,在看时跳过声音
  • 通知逻辑统一:删除 ChatRoom 里重复的 Notification 调用,统一到 agentInbox

PWA & 离线

  • Service Worker 缓存策略(详见 PWA 部署页面
  • 自动版本更新检测 + UpdateBanner
  • BUILD_HASH 从随机 MD5 改为 git commit hash

架构演进

ChatRoom 大重构(2026-03-25)

通过 adversarial-qa(左右互搏 QA)自动化审核,经过 5 轮迭代完成:

轮次ChatRoom 行数子组件数评分
初始26190
R51879996

提取的 9 个组件:types.tsutils.tsDeliveryTicksMessageItemActionSheetSuggestionBarHistoryDrawerHeaderMenuConnectionBanner

UI 风格确立

  • 不要聊天气泡 — 坚持 Discord/Slack 平铺风格
  • 品牌色 — 从绿色 #67B88B 统一为橙色 #EF5A23
  • 文字对比度 — 主文字 #1F2328,次要 #4D5561

扫码即用配对(2026-04-21)

之前 Gateway 管理端生成的用户二维码/链接是 openclaw://connect?serverUrl=...&token=... 自定义协议,浏览器打不开 → 用户必须先打开 web 前端再手动粘贴到 Pairing 页。04-21 改造为扫码直接跳 web 前端自动完成连接

  • Gateway 管理端新增「Web 前端地址」多值配置(如 https://web.dev.dora.restry.cn),写入 /api/settings
  • 分享二维码生成的链接变成 `https:///pair?serverUrl=…&token=*** pairing
  • web 端的手动识别兼容同一种 URL 格式
  • Gateway 86dff4a、client-web a153820 同日 push + pm2 restart 到 dev(web.dev.dora.restry.cn / gw.dev.dora.restry.cn);relay 不涉及部署
  • 默认模型同日改为 github-copilot/claude-opus-4.6

单窗口锁定模式 / 扫码直跳聊天(2026-04-23)

在 04-21「扫码即用配对」基础上再前进一步——很多分享场景下,客户不需要看 agent 列表,只想被直接丢进指定聊天窗口(例如把 mazu-cs 客服链接塞进小程序内嵌 webview)。

**Admin 端(gateway-repo UserConnectModal)**新增「Link Behaviour」区域,两个 toggle:

  • Direct jump to chat — 开启后生成的链接/二维码带 directChat=1,打开即直跳聊天,跳过列表
  • Allow back to listdirectChat=1 时出现,对应 allowBack=1,关掉就锁定单窗口不能返回列表

Client-web 端(client-web 配套三处改动:

  • App.tsx/connect 读取 directChat=1,写入 localStorage.clawline.singleWindowgetSingleWindowConfig());AppShell 初始化 screen 时 direct jump 模式直接进 chat_room 跳过 chats
  • AppShell.navigate('chats')handleSwipeBacksingleWindow && !allowBack 时被拦截
  • ChatRoom.tsx 输入栏单窗口精简版isSingleWindow 为 true 时隐藏 ModelPicker、快捷命令 chips、SuggestionBar、voice 按钮和 Row 1 的 + 菜单;Row 1 只保留 textarea + Image + File 直出,Row 2 只保留 Send
  • ChatHeader.tsx 新增 hideBack prop,单窗口模式隐藏返回按钮

部署踩坑:首轮只 git commit 了 Admin 源码没 build,用户刷新看不到开关。需要跑 pnpm build + pm2 restartgw.dev.dora.restry.cn 才生效——Gateway 前端改动必须 build + restart,不能只 push。Client-web 一路 build + type-check 通过(TS2339 AppErrorBoundary.props 是预存 lint,无关此次改动)。

Web 前端 avatar 限制(2026-04-23)

Dora 尝试把客服代理头像统一成莆阳 Logo:在每个客服代理 openclaw.json 写了 identity.avatar = avatars/puyang-logo.jpg 并通过 openclaw 配置下发。web.dev.dora.restry.cn 前端只读 emoji 字段渲染头像,不读 identity.avatar,新用户进来看到的还是 emoji。

  • 根因:Clawline Web 前端当前渲染逻辑只有 emoji path,没消费 avatar 字段
  • 结论:这是前端渲染限制而非配置问题;若要走图片头像需要前端改造(优先读 avatar,fallback 到 emoji)
  • 暂列 TODO,非阻塞(emoji 够用)

2026-04-27 桌面端 v0.2.0 → v0.4.1 连发 + SuggestionBar 恢复 + senderId 展示 + 代码块复制

一连串面向桌面端用户的修复+发布,dev → tag → CI → publish 全流程跑了三次。

SuggestionBar 误删恢复

2f9e315a(Tauri 桌面版 commit)误删了输入框上方的 AI 推荐快捷栏SuggestionBar.tsx + suggestions.ts 整套 + Voice 润色 (refineVoiceText) + Dashboard 屏幕。Daddy 要求恢复 SuggestionBar:找回组件 + service,ChatRoom.tsx 重新引入并渲染(位置:slash menu </AnimatePresence> 之后、Reply bar 之前)。

senderId 在 API badge 旁展示

Daddy 反馈:通过 /api/chat 调用 nexora-cs 等客服代理时,多个微信用户的消息看起来全部混在 web 端同一对话里。排查链路:

  • Gateway 代码 sessionId = "api-${channelId}-${chatId}-${agentId}" 已正确隔离,chatId 透传无问题
  • 调用方传 chatId = senderId = "wechat-<openid>" 也都对
  • 实际消息确实路由到不同 OpenClaw session,但 client-web 是按 agent 视角渲染历史,所以多个用户与同一 agent 对话会聚集在该 agent 主对话视图里

短期方案(不改后端):在 API badge 右边追加 senderId,效果 ⚡ API wechat-oXXXX

  • ChatRoom.tsx — 实时 / 历史 / user-echo 三处消息构建时都从 packet.data.senderId 提取放入 meta.senderId
  • MessageItem.tsx — API badge 旁渲染 meta.senderId

长期方案(未做):让 /api/chat 强制 senderId 必填或加独立 sessionId 字段,前端按 session 分割视图。

Markdown 代码块 Copy 按钮

代码块头部之前只有语言标签,新增 Copy 按钮(点击后变 ✓ Copied);没有语言标签的代码块也展示。

桌面端发布流程(一日内三个 tag)

Tag内容结果
desktop-0.1.1手动建的空 tag(漏 v 前缀),污染 GitHub releases/latest → Tauri updater latest.json 404,桌面端”检查更新”全挂。
desktop-v0.2.0已有 draft,CI 凌晨已跑完(Mac aarch64/x64 dmg + Win exe + Linux deb/AppImage 全平台齐)publish
desktop-v0.3.0bump + push + 打 tag → 触发 CI → publish
desktop-v0.4.0tag 打在了未含 senderId 改动的旧 commit,CI 产物不含改动作废,删 draft
desktop-v0.4.1重新 bump 到含 senderId + Copy 按钮的最新 commit → push + tag → CI → publish✅ updater manifest 确认

踩坑 / 教训

  • pull 前应该先 commitgit pull不必先 stash(Daddy 当场纠正:“为什么 stash?”)
  • 打 tag 前必须确认 HEAD 已含目标改动,否则 CI 产物缺改动 → 需要重 bump 重发
  • Tauri 桌面端:Mac aarch64 / Mac x64 / Windows x64 / Linux deb + AppImage 五份产物,CI ~8-10 分钟

本地 skill clawline-deploy 同步更新加入桌面端发布完整流程。

2026-04-27 续:v0.5.0 thinking/answer 分相 + v0.5.1 sender 角色 + v0.5.2 UpdateModal

同日下午 / 晚间在 v0.4.1 基础上又连发三版,重点是协议侧把”思考”和”答案”显式分相,以及 React 化 native dialog。

v0.5.0 thinking/answer 分相

之前 channel 把 thinking delta 和 answer delta 混在同一条流,前端要靠 heuristic 拆——会出现”思考内容追加在答案后面”的错位。

协议改动:channel 新增 phase 字段(thinking / answer),每相内 delta 累积(不再增量拼),相切换时前端开新气泡。客户端按 phase 路由到不同 UI 区域,thinking 折叠默认收起。

v0.5.1 determineSenderRole + 滚动加载历史

Bug 1 — 跨客户端 sender 误判:A 客户端发的消息在 B 客户端展示成”自己发的”。根因:旧代码只判断 senderId === localSenderId,但同一用户多设备 senderId 可能不同。抽 determineSenderRole(msg, ctx) helper:综合 senderId / chatId / agentId / 消息源(user vs assistant)综合判定。

Bug 2 — 滚动到顶不加载历史useEffect 监听 scroll 但 deps 写成 [],handler 闭包永远拿初始 state,hasMore 永远为 true 时也加载不到下一页。修正 deps + 用 ref 存最新分页 cursor。

v0.5.2 UpdateModal React 化

之前 window.confirm() 弹更新提示,桌面端 Tauri 下样式丑 + 无法自定义。改为 UpdateModal React 组件:shadcn 风格,“立即更新 / 稍后” 双按钮,“稍后” 24h 内不再弹同版本。

v0.5.3 滚动加载历史的真根因(commit 80ffbd5e

v0.5.1 修过一次”滚动到顶不加载历史”,CC 当时把 useEffect deps 改成 [] 想”只挂一次”,没修透。Daddy 自查代码定位两个真 bug:

  • scroll listener 永远不挂载scrollContainerRef.current 在 ChatRoom 首次渲染(lazy load + React StrictMode 双调)时是 null,effect 早退后再也不会重试。修法:deps 从 [] 改回 [scrollContainerRef.current, agentId, chatId],ref 挂上后 effect 重跑。
  • 缓存短时 hasMore 锁死setHasMoreHistory(cached.length > INITIAL_PAGE) —— 缓存少于 30 条直接 false。但 warmCache 只拉有界窗口,缓存少不代表 server 没数据。修法:默认 setHasMoreHistory(true),让 HTTP 路径在真正没东西时才置 false。

教训沉淀:CC 修两次都没修对,小逻辑改动以后 Daddy 自己 review。bump 0.5.3 + tag desktop-v0.5.3 push 触发 CI 自动 publish。

MsgId namespace collision RCA

排查”同一消息在两个客户端 id 冲突”时定位:

  • Channel 端 msgId = substring(7) 取 5 位 base36(约 60M 空间)
  • Client 端 msgId = 6 位 base36(约 2.1B 空间)

两边 namespace 不一致,channel 短 id 偶发撞上 client 长 id 的前 5 位 → dedup 集合误命中、消息被吞。修法:统一到 6 位 base36,channel 端补一位。沉淀防再撞 note 在 channel-repo lib/msgid.ts 注释。

2026-04-30 desktop-v0.5.4 — 滚动 anchor 保留 + CC 测试卡死治理

接 v0.5.3「滚到顶能加载历史」之后,视觉跳动还在:滚到顶 prepend 30 条后,原本视野里那条消息 boundingClientRect.top 整体被推走,体验上就是”页面跳了一下”。

修法:scroll anchor preservation

ChatRoom.tsx 加 anchor 逻辑——prepend 前记录可视区一条 anchor message + 它的 boundingClientRect.top,prepend 后用 scrollTop 补差,使 |ΔT| < 5px。commit 1f8d0670(CC 提)→ 330848df 推到 dev

发版

Tag内容结果
desktop-v0.5.4bump 0.5.3 → 0.5.4(tauri.conf.json / Cargo.toml / Cargo.lock 三处版本号),tag push 触发 CI(run id 25169843617,~8-10 min × 4 matrix)✅ publish 为 Latestlatest.json 返回 0.5.4,4 平台 8 个产物齐全

老用户 macOS app 「检查更新」如果第一时间还显示 5.3,是 GitHub Releases CDN 对 latest.json 的短期缓存,再点一次基本就刷新;Preferences 的「当前版本=0.5.3」是已装版本号,装完 0.5.4 才会变(不是 bug)。

CC 跑 E2E 卡死治理(重要操作教训)

本次发版前两轮派 CC 重测 scroll-anchor 都卡死

  • 9:06PM 派出去测 → 20 分钟无产出,卡在 puppeteer-mcp(明明要求用 browser-agent)
  • 9:29PM 二次派 → 卡在 browser-agent hook 的 curl 120s 等待
  • Daddy 让停,全部 kill 干净;连同 Wed 21:00 残留的 playwright test-server 旧进程一并清理

教训:

  • 派 CC 跑 E2E 测试必须给硬超时 + 出问题立刻 BLOCK 报告,不然容易在浏览器自动化层(puppeteer / browser-agent)静默转圈,外面看不到任何输出
  • CC “上一轮验证翻车”复盘:上次自报 PASS 的是「8px spinner 微量 prepend」,根本没复现 60+ 条消息真实滚顶场景——测试要求必须明确指定数据规模、操作路径、断言指标(如 |ΔT| < 5px + 截屏),不然 CC 会偷懒挑最容易过的子集

最终 v0.5.4 修没修透没有跑通自动化验证,是 Daddy 自查代码 + 看用户反馈点头放行的——open loop:scroll-anchor 真值验证欠一份。

2026-04-28 dev 分支同步收尾 + auto-deploy 是死的

接 04-27 桌面端连发,client-web dev 分支还有 1 条本地领先 origin 的 commit 3ed92def feat: add copy button to code blocks in markdown renderer,Levis 让 push → 推上去 ✅。其他 5 个 repo(channel / gateway / docs / sdk / wechat)dev 分支均与 origin 同步,无落后。

dist/ 跟踪噪声仍未根治:client-web 工作树仍有 M dist/index.html M dist/sw.js 的未提交噪声,结构上 04-20 应已 git rm --cached,下次工作区干净时确认是否回潮。

channel-repo 拉到 1518eb4(变化集中在 reply-dispatcher.ts +35/-5send.ts 小改),同时澄清 OpenClaw clawline 插件的实际加载路径不走 plugins/ 也不走 extensions/,而是直接从 workspace channel-repo 读:

"paths": ["/home/resley/.openclaw/workspace-clawline-client-web/channel-repo"]

→ 升级流程 = git pull channel-repo + 重启 gateway 即可。

auto-deploy 现状(重要)AGENTS.md 写的 skills/auto-deploy/watch-repos.sh “每次心跳运行”,但 agent 没有心跳 cron,唯一相关的 web-project-sync cron 还是 disabled 状态 → watch-repos.sh 实际从不运行,自动部署链路是死的,所有部署目前只能手动 git pull + build + restart。Daddy 暂不要求加心跳 cron,留 open loop。

POST /api/chat HTTP 直连接口(04-12)

新增约 100 行改动跨 5 个文件,实现 HTTP 直接调用 Agent(无需 WS 连接):

  • Gateway 创建虚拟连接注入 channel 插件
  • meta.source = "api" 标识贯穿全链路
  • 前端 MessageItem 渲染 ⚡ API badge
  • 支持 Admin Token / Bearer JWT / Channel Token 三种认证
  • 详见 relay-gateway-api

GET /api/agents 接口(04-14)

新增返回所有机器人信息的接口,包含所属服务器、在线状态和元数据信息,通过 Admin Token 调用。

API 代理回复列表不可见(2026-04-18 ~ 04-19 已修复)

Levis 报告:通过 POST /api/chat 发消息到代理后,代理回复不出现在聊天历史列表里(nexora-xipu 最后 8 条验证)。Supabase cl_messages 表 inbound(api- 前缀)与 outbound(msg- 前缀)都有入库,gateway 无 persist 失败日志,问题出在 channel 侧。

最终定位两层叠加的 Bug,均在 gateway-repo/server.js 修复并部署到 relay(163.228.142.105):

Bug A — agent isolation 导致回复不 deliver(commit 98c375a

多个 agent 并发调 /api/chat 时,共用同一个 virtualConnId = api-{channel}-{chatId}。channel 端 agent isolation 把其他 agent 的回复全部 buffer,永远不 deliver 也不 persist → Supabase 看不到 outbound。

修法:virtualConnId 加入 agentId → api-{channel}-{chatId}-{agentId},每个 agent 独立虚拟连接。

Bug B — chatId 借用导致路由断裂(commit 00dec2f,04-19)

原逻辑在 body.chatId 未传时,gateway 去”借”一个在线真实 WS 连接的 chatId(例如 cobra)。导致:

  1. 虚拟连接注册到 chatId = 'cobra'
  2. bot.ts DM 回复用 user:${senderId}(例如 user:eagle
  3. sendToClient('eagle') 找不到注册在 'cobra' 下的虚拟连接 → fallback 到 relay.server.persist,写入错乱
  4. history 中 inbound 在 'cobra'、outbound 在 'eagle'history.sync 两边都不完整 → 前端列表空白

修法chatId = body.chatId || senderId,整条链路统一 chatId,user:senderId 路由命中。

验证步骤:POST /api/chat 不传 chatId → Supabase inbound + outbound 的 sender_id 一致 → 前端刷新聊天列表正常出现 conversation → 并发发给不同 agent 验证 isolation。04-20 在 Nexora 9 个 agent 全员验证通过(HTTP 200,25–89s)。

Gateway reliability-v2 大重构(2026-04-19)

04-19 下午 17:21 三组件联合部署:gateway25 个 commitreliability-v2)、client-web 1 个 commit(TH-7 thread 消息时间顺序修复)、channel 一轮 code review。重点改动:

系列主题
API chat消息路由、多设备同步、超时配置、502 错误上报
Thread (TH-2~TH-7)threadId 路由、reply_count 序列化、mark_read 广播、search 防护、消息时间顺序
可靠性 (D1~D11)精简状态机;D9 删除 _threadUpdateChain mutex;D11outbox/history/persist/online 四路广播合并为 fanOut()D6 ack-then-persist
测试基础mock-backend + Makefile

04-20 23:28 在 relay (relay.restry.cn / 163.228.142.105) 比对确认最新 commit 5036c27 (04-19 21:30) 已上线:fanOut() 在、_threadUpdateChain 仅剩注释、ack-then-persist 生效,5 个后端连接,11 客户端在线,/healthz ok。

/api/chat 502/504 根因与超时策略(2026-04-20 诊断)

Levis 汇报 relay.restry.cn 有大量 504/“消息没进队列”错误。gateway 侧日志比对后定位:

历史 502(已在 reliability-v2 修复)

TypeError: Cannot read properties of null (reading 'readyState') — sibling 广播缺 null-guard,某连接已断 ws = null 触发整进程 crash,systemd 拉起,Caddy 把在途 /api/chat 响应为 502(不是 504)。04-14、04-16、04-18 期间共 18 次 502,gateway 自动重启约 48 次。04-18 05:03 crash 与 Caddy 502 精确同秒。reliability-v2 加 null-guard + fanOut() 后根治。

当前 504 触发场景

  1. Agent 真超时:默认 300s,可通过 body.timeoutRELAY_API_CHAT_TIMEOUT_MS 调整,硬上限 600s。LLM 慢、卡死都走这条
  2. replyTo 不匹配:D3 之后用 messageId 严格匹配回调,若 channel 插件端没把 replyTo 原样带回,或走旧 FIFO,sess.requests.get(messageId) 拿不到,timer 到期 504
  3. relay.client.open 发出但 50ms settle 没等够(硬编码),极端并发下偶发

流式输出仍未实现

/api/chat 是同步 HTTP,等到 message.send 才回包:

  • 硬超时 600s,没有逃生路径
  • text.delta 中间帧只 persist + fanOut 给 WS 客户端,HTTP caller 看不到
  • 临时方案:caller 传 body.timeout(≤600s)或改走 WS;根本解是把 /api/chat 改成 SSE / chunked 流式响应(TODO)

用户 inbound 不落库(D6 设计 bug,2026-04-20 已修复

历史症状:/api/chat 下用户消息不 persist,前端历史只看到 bot 回复。根因是 D6 注释 // D6: do NOT persist inbound here. Persistence is deferred to the ack path —— 等 channel ack 再写,但 api 流程没对应 ack 触发 → inbound 丢库。

2026-04-20 早间三连提交(gateway)

时间Commit说明
10:02REL-06 测试先行先写失败的 guardrail 测试,精确暴露 timeout/reject/drop 三种场景下 inbound 行缺失 FAIL
10:15D6-fixinbound 持久化无条件执行 — 消息进 gateway 立即写库,彻底推翻”等 ack 才写”
~10:30自动部署全部通过 auto-deploy 上 relay,healthz 每次 OK

三个 commit 随自动发布流水线上线,现 /api/chat 的 inbound/outbound 在 Supabase cl_messages 同步落库。

部署流水线:client-web dist/sw.js 冲突卡住(2026-04-20 修复)

auto-deploy 报 dist/sw.js: needs merge / fatal: Exiting because of an unmerged files,导致 client-web 若干 commit 没部署成功。根因:dist/ 早在 .gitignore,但历史上 dist/sw.jsdist/index.html 被手动 git add 强制跟踪进 index。修法git rm --cached dist/sw.js dist/index.html 从 index 移除并随 upstream pull 合并;working tree 与 origin/dev 同步,之后不会再出现。同日所有 repo 部署状态核对为绿:client-web 9bd3ccf、channel OWL D4、gateway relay SSE streaming 在线。

代码库状态快照(2026-04-19)

6 个仓库均在 dev 分支,与 remote 同步:

仓库HEAD说明
client-web41864e6fix(ui): keep input focus after send on desktop
channel12cfae1fix: code review fixes(listener leak / body limit / reconnect)
gateway00dec2ffix: use senderId as API chatId(Bug B)
sdk8872739feat: create @clawlines/sdk
docsdd7dfb1docs: 覆盖率 50%→89%
wechat45eb386Merge branch feat/wx-sync

新动态:ralph/thread-discord-style 分支在 client-web / channel / gateway 三 repo 同时出现(ralph 在做 Discord-style thread 功能),client-web 新打 tag v0.2.0-62ba202。dev 环境 web.dev.dora.restry.cn 当日同步发布,返回 200。

部署铁律:AGENTS.md 规定只部署 dev 分支,所有 repo 保持一致。

ACP Thread 卡片渲染(04-14 排查)

threadId 传递链路在代码层面已打通(SDK → outbound.ts → sendMessageGeneric → WS → ChatRoom → history.sync),但实际运行行为需 ACP session 触发验证。发现 history.sync 用驼峰 threadIdsyncMessageToLocal 读下划线 thread_id,两条不同路径对应不同数据源,属正常设计。详见 acp-thread-card-rendering

关键 Bug 修复记录

Bug根因修复
Agent.list 死循环useEffect 依赖 + ensureAgentsLoaded 无条件调 connect加 agents 已有守卫
iOS ActionSheet 被输入栏遮挡fixedoverflow:auto 容器内的定位 bug移出 scroll 容器
复制文字功能失效长按 400ms 劫持系统文字选择stopPropagation + execCommand
iOS PWA 旧版本不更新SW 不自动 skipWaitingindex.html 加缓存清理脚本
技能按钮点击不灵28px 按钮太小 + 动画干扰onPointerDown + 36px
骨架屏卡死StrictMode 双执行下 agent.selected 丢失connection.open 后请求 agent.list 兜底
消息重复多 WS 连接导致 outbound 多次写入 + outbox flush 重复Channel 层 rolling Set 去重 + Client dequeue-first
WS 疯狂重连Gateway 不回复 ping(当普通消息转发)Gateway 拦截 ping 回复 pong,Client 心跳 15s→30s
PC 通知不触发只判断 document.hidden改为 `hidden
聊天时响声音agentInbox 无条件调 playNewMessage判断 activeAgentId + hasFocus 跳过
PC 端发完消息失去焦点第 1462 行 blur() 原注释是 “Dismiss mobile keyboard”,但未加平台判断,PC 也 blur移动端继续 blur 收起键盘,PC 端用 setTimeout 重新 focus 输入框(04-16 修复并部署到 web.dev.dora.restry.cn)

插件更新方式

Clawline 插件通过 GitHub 多仓库管理:

仓库作用
clawline/channelOpenClaw Channel 插件(后端信道)
clawline/client-webReact Web 客户端
clawline/client-wechat微信小程序客户端
clawline/gatewayWebSocket 消息中继网关

加载方式:~/.openclaw/extensions/clawline/ 是 symlink → 指向 ~/Projects/clawline/channel/。更新流程为 git pull → 重启 gateway。

Browser-Agent E2E 调试踩坑:localhost vs 127.0.0.1 不同 origin(2026-04-19)

fries-mac 通过 Hermes → Claude Code → Browser-Agent skill(http://127.0.0.1:4821,Default Chrome profile,windowId 420604943)测试 client-web dev 环境时,被诱导连了 http://127.0.0.1:4026/,看到”Get Started 流程、左侧无 agent”,回报”agent 列表是空的”。Daddy 立刻指出:本机日常浏览器开的是 http://localhost:4026/——浏览器把 localhost127.0.0.1 视为不同 originlocalStorage 不共享,所以前者已配对的 agent 在后者完全看不见,被误判为 “client-web 没渲染左侧列表”。改连 localhost:4026 后两个 agent(dora / fires)一目了然。

经验:Browser-Agent 用 user 的 Default profile 跑 E2E 时,URL 必须严格匹配用户日常使用的 host(localhost vs 127.0.0.1 不能混),否则 localStorage / cookies / IndexedDB 全部错位,所有”看不到/没数据”的报告都不可信。

相关主题

2026-04-20 更新:Bug A 根因修复 + localStorage 完成迁移

Levis 反馈点开聊天界面应有 /api/chat 历史消息但页面立刻跳回 /chats 列表。Claude Code review(5/10 分)指出两处架构错位:

  1. messageCache 没有 chatId 维度 — 通过 /api/chat 发消息带的 chatId(如 api-test-1)和 UI 里 agent 对应的 chatId 不是同一 key
  2. ChatList 用 connection.chatId 冒充 agent.chatId — 路由失败 → 判空 → 自动 navigate 回 /chats

之前 4/19 commit 全在症状层打补丁(URL→Screen 加 if、ChatRoom 加去重、双重 fetch),属”加 null check 掩盖该删的零件”反模式。

修复(commit 9bd3ccf1 — 三处根因全拔:

  • ChatList 不再用 connection.chatId 冒充 agent.chatId
  • pathToScreen('/') 抽象成 'home' intent,由 resolveScreen() 单点决策
  • messageCache 加 chatId 维度 + 读时过滤;删 ChatRoom defensive 去重

D13 localStorage 迁移完成 — 17 个文件 + 一次性迁移表把 openclaw.*clawline.* 全清,验证 PASS。N1 threadStore 入 cache 补 timestamp(修 sort 稳定性);N3 Inbox 改 useSyncExternalStore

Onboarding 闭环(已生效)clawline.onboarding.done flag 不存在 → 展示 Get Started;存在 → / 直跳 /chats。E2E 用例 W-01/02/05/06 不再需要走真实 OIDC,只要清/写 flag 验证两态即可。

Bug B(iOS PWA 顶部挡 + swipe-back 失效)暂未修 — 需要 Daddy 给真实复现机型 / 截图 / iOS 版本,避免硬加 safe-area 引发 double-inset 回归。

2026-05-07 Ghost-outbound 链路 RCA + dev 三分钟自动部署确认

dora 频道 ghost 消息率约 34% 的根因不在 client-web 本身,而是 channel 客户端 apps/channel/src/generic/client.ts:585-592 在收到 inbound 后把消息原样作为 message.send 重广播 → backend 把 inbound 当 outbound 落库。详细 RCA 见 Clawline(本页) 同日记录与 Clawline(本页) 5-07 npm 发包章节。

client-web 侧的连带验证:fix 随 @clawlines/channel@1.0.0 08:26 push 后,dev 环境 3 分钟自动部署链路 把新 channel + gateway build 拉到 gw.dev.dora.restry.cn,client-web 8:30 起观察到 dora 频道不再产生新 ghost 行(schema (channel_id, message_id, direction) unique 允许同一 msgId 同时存在 inbound + outbound,所以历史 ghost 行不会被自动清理;需要手动 SQL 删旧 ghost 后再做长尾观察)。

dev 自动部署链路(5-07 重新确认)

  • cron/watch-repos.sh 每 3 分钟 gh apiclawline/platform:dev 最新 SHA,与本地 .watch-state/platform_dev.sha 对比,变化才触发 auto-deploy.sh
  • 全程从 source 跑 build(不依赖 npm registry),保证 dev 永远跟上 monorepo dev 分支
  • 客户报「dev 没生效」时第一步先看 cron.log 是否真的检测到 SHA 变化;常见误判是浏览器 SW 缓存,强刷 Ctrl+Shift+R 即可

详见 monitoring-and-cronclawline/platform monorepo 自动部署 章节。

2026-05-08 sync scope=chat|channel 防呆 + ChatRoom chatId 三连自愈

接 5-07 ghost-outbound RCA:cobra/main 客户端仍偶发”别人的消息出现在自己窗口”。继续顺藤排查找到两层叠加 bug,client-web 一晚连发 4 commits。

/api/messages/sync scope 防呆(commit 4754d6b

之前 sync 接口只按 (channelId, since) 拉历史,不区分查询粒度——agent 主对话视图(channel-wide)和单聊视图(chatId-bound)共用同一查询 → 多用户经 /api/chat 进同一 agent 时,B 用户能看到 A 用户的入站消息。

修:sync 接口新增 scope 必填参数,scope=channel 走老路径,scope=chat 必带 chatId 且 server 端强校验。client-web 调用方按视图类型显式传 scope,杜绝默认值兜底。

ChatRoom effectiveChatId 三连自愈(eea9659 / d98465d / 9699aa8

ChatRoom 进场拿不到 chatId 时之前直接渲染空、不重试。三个 commit 串起完整自愈链路:

Commit修法
eea9659effectiveChatId fallback:props.chatId → URL ?chatId= → connection 上次 chatId → agent 默认;任一非空即可启动消息流
d98465d/connect else 分支补 chatId 提取——之前只有”首次配对”路径解析 chatId,已配对用户重连走 else 分支直接丢字段
9699aa8已存在 connection 但 chatId 不一致时,自愈 用最新 chatId 覆写 connection store,而不是 silent 忽略

三段一起构成「无论什么入口进 ChatRoom,最后必有合法 chatId 或显式报错」的硬保证。

dev 自动部署链路 5-08 复核

monorepo 1 commit / 5 commit / 8 commit 的 push 都在 3 分钟内被 cron/watch-repos.sh 拉走、build、pm2 restart dev-webweb.dev.dora.restry.cn 全程 200,无人值守。

2026-05-06 历史消息加载 3 修(commit a816538 fix(client-web): scroll history load

Levis 反馈历史消息有时加载不出来。一次 commit 修了 3 个相关问题:

  1. 上滑加载偶尔没反应 — scroll listener 在 DOM 切换(路由/重渲染)时丢失。改用 callback ref,在 ref 变化时重新绑定 listener。
  2. 冷启动聊天无法翻页 — 第一次进入空 chat 时,messages 为空 → fetchOlder 用最旧 message 时间戳作为 cursor → 没有 message 直接 return → 永远翻不出来。Fallback 到 Date.now(),跳过缓存直接请求。
  3. 网络一次失败永久 hasMore:falsefetchOlderMessages 网络错误时静默 return hasMore:false,之后即便用户刷新也再不会请求。改为抛错让上层可重试。

属于 platform monorepo 合并后第一波 client-web 修复(详见 decisions 5-06 monorepo 决策)。生产部署经历了「以为没生效 → 再 push 一次空 commit 触发完整 rebuild」才确认起作用,验证时强刷(Ctrl+Shift+R)清缓存看效果。

Docs Site

基于 VitePress 的 Clawline 产品文档站,聚合渲染五个子仓库的文档,提供产品介绍、使用手册和开发指南。

概述

Clawline 文档站是为 Clawline(本页) 产品线建设的统一文档网站。采用”文档跟着代码走,站点聚合渲染”的架构,各子项目在自己的 docs/ 目录维护文档,文档站构建时自动拉取合并渲染。

项目由 Ottor 主导产品定义和首页内容编写,webbot 负责站点框架搭建。

域名与环境

环境域名
生产www.clawlines.net
开发docs.dev.dora.restry.cn

技术方案

  • 框架:VitePress(Vue 生态,Markdown 原生驱动,中文生态好)
  • 语言:中文为主
  • 仓库clawline/docs(独立 repo)

文档站结构

clawline/docs/
├── .vitepress/config.ts    ← 导航、侧边栏配置
├── index.md                ← 首页(产品介绍)
├── guide/
│   ├── what-is.md          ← Clawline 是什么
│   ├── architecture.md     ← 三端协作架构图
│   └── quickstart.md       ← 5 分钟跑起来
├── scripts/sync.mjs        ← 构建前从三个 repo 拉最新 docs/
└── package.json

聚合渲染机制

构建时 sync.mjs 从五个子仓库拉取最新文档:

gateway/docs/     → docs/gateway/
channel/docs/     → docs/channel/
client-web/docs/  → docs/client-web/
sdk/docs/         → docs/sdk/
  • 各 Bot 改代码时,docs/ 就在手边,同 PR 更新
  • 90% 的文档内容在各自项目里维护
  • 文档站 repo 主要是”壳”——只存首页和跨项目概述
  • CI 触发:任一 repo push → webhook 触发文档站重新构建

五个子仓库

Repo职责语言
channelOpenClaw 插件,WS 事件、历史持久化、agent 隔离TypeScript
client-webReact 前端聊天客户端TypeScript (React + Vite)
gatewayWS 代理,连接管理、channel 路由、用户认证JavaScript (Node.js)
sdk@clawlines/sdk 通用 WS 客户端库TypeScript
docsVitePress 文档站Markdown + Vue

产品定位

Clawline 的核心定位是 OpenClaw 的 Web 接入方案,而非独立产品。

首页叙事:痛点 → 方案 → 优势

没有 Clawline 的痛点:

  1. OpenClaw 只能通过 Telegram/Discord/微信等第三方平台对话——被平台绑架
  2. 想嵌入自己的网站/App?得从零写 WebSocket、鉴权、消息路由、文件上传、断线重连
  3. 第三方平台有 rate limit、API 随时变、UI 不可控、数据不在自己手里
  4. 内网部署的 Agent 无法暴露公网端口给第三方 webhook

Clawline 的优势:

  1. 自有渠道 — 你的域名、你的 UI、你的数据
  2. Relay 架构 — Agent 主动连出来,不需要公网端口,穿墙友好
  3. 开箱即用 — 三个组件拼起来就能跑,PWA 移动端直接用
  4. 已验证的全链路 — 流式输出、文件上传、记忆、历史记录、断线续传全部内置
  5. 可替换 — 不喜欢 Client Web?只用 Gateway + Channel,自己写前端

页面总数

22 页,涵盖 Guide、Gateway、Channel、Client-Web、SDK 五大板块。

相关页面

附录:Inbox 过滤优化(2026-04-07)

从 Clawline Client Web(本页) 拆分。

Inbox 页面系统消息/状态消息过滤逻辑经历了多轮修复,最终通过 6 个入口全链路审查 达到”私人秘书”级别的降噪效果。

问题

Inbox 充斥系统日志、状态播报、空卡片等噪音,核心价值”降噪”未实现。

修复过程(3 轮 commit)

Commit修复内容
27f69b4bWebSocket 包白名单机制(只处理 message.send/message.receive/thinking.*/agent.list)+ 空内容/无效内容过滤([Image]📎 File*[cancelled]*
98e58b5a历史脏数据清洗(localStorage 启动时清洗)+ 隐藏零对话幽灵 Agent + getInboxItems 强制过滤
a40c03b6修复 Inbox↔Chats Tab 切换导致聊天框状态丢失(activeAgent 被无条件设为 null)

6 道防线(全链路审查)

  1. localStorage 旧缓存 — 启动时清洗脏数据
  2. IndexedDB 历史数据populateAgentFromMessages 应用 isContentMessage 过滤
  3. 实时 AI 回复 (WS message.send) — 内容校验拦截空/无效内容
  4. 实时用户消息 (WS message.receive) — 同等内容校验
  5. 其他实时系统包 (typing/status/delta) — 白名单机制完全拦截
  6. UI 渲染输出 (getInboxItems) — 强制过滤无有效 lastMessage 的空白 Agent

已知边界:后端 Channel 插件若错误使用 message.send 协议发送系统文本(如 "Agent connected"),客户端无法区分,需后端改用 status.update/system.alert 协议。

2026-04-19 PRD v1 战略:先聚合视图,后自动化

承接 Clawline(本页) reliability v2 当天写的 docs/prd/clawline-v1.md,Inbox 路线由 fries-mac 与 Daddy 一轮对齐:

  • 第一阶段(当前):把”聚合视图”做扎实——多 Agent 消息一条流、按未读/最近活跃排序、噪声过滤(已落地的 6 道防线)
  • 第二阶段(后续):在现有”生成建议”按钮上叠加战略思考回复——不是新增独立工作流,而是让建议能跨多条消息考虑上下文/任务跟踪,给出可一键发送的回复草稿
  • 暂不做:跨 inbox 的全自动跟进/任务管理工作流——优先级压后

设计原则:Inbox 是”私人秘书面板”,聚合 + 起草是核心,自动决策不是。详见 Clawline(本页) PRD v1 章节。