Commit Graph

151 Commits

Author SHA1 Message Date
晴天
b2146b54cb feat(hermes): add tirith security settings 2026-05-24 22:08:12 +08:00
晴天
7be0ec66cc feat(hermes): add unauthorized dm policy form 2026-05-24 21:41:17 +08:00
晴天
2de5d1e38a feat(hermes): add quick commands config form 2026-05-24 21:21:16 +08:00
晴天
01f17f0a2a feat(hermes): add skills config form 2026-05-24 20:54:29 +08:00
晴天
d9fc9a8783 feat(hermes): add memory flush setting 2026-05-24 20:34:12 +08:00
晴天
56e41dd512 feat(hermes): add terminal execution config form 2026-05-24 20:06:50 +08:00
晴天
122d7a63be feat(hermes): add execution limits config form 2026-05-24 19:28:30 +08:00
晴天
c0b40070c0 feat(hermes): add channel display settings form 2026-05-24 18:42:34 +08:00
晴天
a1307716dd feat(hermes): add streaming config form 2026-05-24 08:40:53 +08:00
晴天
1883e18f02 feat(hermes): add memory config form 2026-05-24 08:04:31 +08:00
晴天
18ca140af8 feat(hermes): add tool loop guardrails form 2026-05-24 07:38:42 +08:00
晴天
5dd6f1be40 feat(hermes): add compression config form 2026-05-24 07:16:00 +08:00
晴天
f500da39c1 feat(hermes): add session runtime config form 2026-05-24 06:49:17 +08:00
晴天
4c29ed68ab fix(hermes): validate raw config saves 2026-05-24 06:10:07 +08:00
晴天
ff4da27eeb feat(hermes): add bundled plugin channel configs 2026-05-24 05:12:17 +08:00
晴天
7b32b533fb feat(channels): add Tlon config compatibility 2026-05-24 02:03:55 +08:00
晴天
7e3bb71fca feat(channels): add IRC config compatibility 2026-05-24 00:57:37 +08:00
晴天
326c5597df feat(channels): add Nostr config compatibility 2026-05-23 09:25:07 +08:00
晴天
dcc3751ded feat(channels): add Twitch config compatibility 2026-05-23 08:42:54 +08:00
晴天
8623efd49c feat(channels): add Nextcloud Talk config compatibility 2026-05-23 08:25:34 +08:00
晴天
579d706075 feat(channels): add ClickClack config compatibility 2026-05-23 08:07:39 +08:00
晴天
d933177ec3 feat(channels): restore WhatsApp config compatibility 2026-05-23 07:51:16 +08:00
晴天
8d745e7543 feat(channels): add iMessage config compatibility 2026-05-23 07:31:06 +08:00
晴天
6c947a1fec feat(hermes): support Discord plugin runtime config 2026-05-23 06:41:49 +08:00
晴天
f188bb85f7 feat(channels): improve Microsoft Teams config compatibility 2026-05-23 06:22:23 +08:00
晴天
49be118c5f feat(channels): improve Signal config compatibility 2026-05-23 05:56:59 +08:00
晴天
09bc45ae4c feat(channels): add Synology and Google Chat config 2026-05-23 05:37:06 +08:00
晴天
53fe25a277 feat(channels): add LINE and Mattermost config 2026-05-23 05:08:51 +08:00
晴天
780b1bdde5 feat(channels): add Zalo channel configuration 2026-05-23 04:38:49 +08:00
晴天
067389d65f fix(channels): add generic channel diagnostics 2026-05-23 04:14:45 +08:00
晴天
d3d527ca34 feat(hermes): add dingtalk channel config 2026-05-23 03:48:33 +08:00
晴天
e2eee18e05 fix(channels): preserve Discord application id 2026-05-23 03:25:22 +08:00
晴天
b6a353d622 fix(channels): stabilize default account selection 2026-05-23 03:12:40 +08:00
晴天
01dff38a97 fix(channels): save multi-account platform configs 2026-05-23 03:06:52 +08:00
晴天
f7518ae4b3 fix(hermes): align channel secrets with runtime env 2026-05-23 02:49:31 +08:00
晴天
6aa7a05f36 fix(channels): preserve SecretRef credentials 2026-05-23 02:23:52 +08:00
晴天
eccf91ed1e feat(hermes): add channel configuration editor 2026-05-23 01:51:08 +08:00
晴天
27b35b6298 fix(channels): normalize OpenClaw channel config policies 2026-05-23 01:14:42 +08:00
晴天
f4d644ea06 fix(web): support openclaw path conflict scan 2026-05-23 00:20:47 +08:00
晴天
d0f0d1f6b7 fix(gateway): release 0.16.5 2026-05-22 20:42:16 +08:00
晴天
7d75486a53 feat(gateway): surface negotiated handshake protocol version in UI
Users have reported confusion about "when will ClawPanel update its
gateway protocol to v4". This is actually a misreading: ClawPanel v0.15+
already advertises `minProtocol=3, maxProtocol=4` in its connect frame,
and negotiates v4 transparently when the kernel is >= 2026.5.12. The
`v3|` prefix users were seeing in dev-api.js is the device signature
payload string schema version, which is a completely separate concept
from the handshake protocol version.

Make this visible and unambiguous:

UI
- Add a "Proto v4" badge next to the Gateway service name in
  /services once the WS handshake succeeds, with a tooltip explaining
  that this is the WS handshake protocol version (not the device
  signature payload v3 format).
- Add the same protocol info to the WebSocket row in /chat-debug.

API
- WsClient now exposes `negotiatedProtocol` which prefers the explicit
  field from the hello payload (`protocol` / `protocolVersion` /
  `negotiatedProtocol`) and falls back to inferring from serverVersion:
  kernels >= 2026.5.12 are reported as v4, older as v3. This matches
  the panel's advertised range of [3, 4].
- KernelSnapshot grows a `protocol` field so feature gates and UIs that
  already consume the snapshot can read it without touching wsClient.

Comments
- Expand the KERNEL_TARGET comment in feature-catalog.js to spell out
  the two-distinct-version-numbers rule explicitly.
- Add matching clarifying comments next to the `v3|...` payload string
  in both scripts/dev-api.js and src-tauri/src/commands/device.rs, so
  the next reader does not confuse payload schema with handshake.

## Verification
- node --check on all touched JS files
- npm run build
- cargo fmt --check && cargo check (clippy errors that surface are
  pre-existing debt in config.rs, untouched here)
- Playwright /services: mock wsClient state, observe `协议 v4` badge
  rendered with `rgba(99, 102, 241, 0.1)` background and accent color,
  for both the explicit-protocol path and the version-inferred path.
2026-05-16 13:02:08 +08:00
晴天
e1eda2db55 feat(models): import external client configs
Add a model client import flow that scans local Codex, Claude Code, Gemini CLI, and common environment variable configurations without reading or copying OAuth tokens.

The new backend command returns safe import candidates with provider metadata, model IDs, and API key environment-variable references. Tauri and Web/dev-api both implement the scanner, and Web mode keeps the scan local even when a remote instance is active.

The Models page now offers an import wizard that lets users select importable candidates, adds providers without overwriting existing keys, preserves secrets as ${ENV_VAR} references, and leaves OAuth-only Codex entries as guidance rather than direct OpenClaw imports.

## Verification
- node --check src/pages/models.js
- node --check src/lib/tauri-api.js
- node --check src/locales/modules/models.js
- node --check scripts/dev-api.js
- cargo fmt --check
- cargo check
- npm run build
2026-05-15 23:18:50 +08:00
晴天
6af9819b89 fix(ui): sync latest upgrade copy in locale bundles
The runtime locale JSON bundles still contained the older About/Services policy copy even after the module-level strings were updated, so the About page could continue to show the old "recommended only" message in Chinese.

This syncs the zh-CN/en locale bundles with the latest-upstream upgrade copy and mirrors the suffixed OpenClaw version comparison fix in the Web dev API.

## Verification
- npm run build
- node --check scripts/dev-api.js
- cargo check
2026-05-15 19:31:44 +08:00
晴天
8b690cb6a7 fix(openclaw): update recommended kernel target and delta protocol handling
面板原本只声明 maxProtocol=3 的握手范围,且 chat delta 处理只接受前缀扩展。
连接到 2026.5.12+ 内核时会出现握手兼容问题;agent 输出在内容回滚或重排场景也会
丢失最新文本,前端 streaming 气泡停留在旧前缀。

## Connect 握手协议范围
- 将桌面端 connect frame 的 maxProtocol 从 3 扩展到 4
- 将 Web 模式 connect frame 的 maxProtocol 从 3 扩展到 4
- minProtocol 继续保留 3,用于兼容历史内核
- 范围 [3, 4] 同时覆盖旧内核和 5.12+ 内核,不需要按版本分支

## Chat delta replace 语义
- 当 payload.replace=true 时,无条件覆盖当前 AI 文本缓存
- 保留前缀扩展场景的增量更新逻辑
- 修复内容回滚或重排时文本长度变短导致 UI 不刷新的问题

## 推荐内核版本
- official 推荐目标升到 2026.5.12
- chinese 推荐目标升到 2026.5.12-zh.1
- KERNEL_FLOOR 不变,继续兼容历史安装

## 验证
- cargo check:PASS
- npm run build:PASS
2026-05-15 18:30:04 +08:00
晴天
0b4ef11971 fix(hermes): restore gateway message sending dependencies
Hermes Gateway 能启动但消息发送链路仍可能不可用:工具安装环境缺少运行时依赖,
同时自定义端点场景下 provider 字段可能被省略,导致消息路由继续落到错误 provider。

## 补齐运行时依赖
- uv tool install 固定带上 HTTP 客户端依赖
- 补齐 OpenAI SDK 依赖
- 补齐 Gateway HTTP server 所需 aiohttp
- 补齐 browser/dialog 等工具链会触发的 websocket 依赖
- 安装日志同步展示完整安装参数,便于用户排查环境问题

## 明确 custom provider
- 自定义 base_url 时显式写入 provider: custom
- 如果 model 段已有 provider,则覆盖为 custom
- 如果 model 段缺少 provider,则补写 provider: custom
- 继续保留 OPENAI_API_KEY alias 自愈,确保辅助客户端和主流程读取同一份凭证

## 范围
- 桌面 Tauri 安装与配置自愈逻辑
- Web dev API 安装与配置自愈逻辑
2026-05-15 17:32:56 +08:00
晴天
583f5401ac fix(hermes): stabilize gateway provider routing and startup
Hermes Gateway 之前容易出现两类问题:配置里的 provider 与 base_url 不一致,
以及多个启动入口并发触发 Gateway start,导致日志里反复出现启动流程,运行状态也不稳定。

## Provider / base_url 自愈
- 归一化 provider URL(去掉尾斜杠和常见 API path 后缀)
- 当 openrouter provider 搭配自定义 base_url 时,自动切换为 custom provider
- 在读取配置、写入模型配置、启动 Gateway 前都执行一次自愈
- Web 模式同步实现相同逻辑,避免桌面端和浏览器端行为不一致

## API Key 别名兼容
- custom provider 优先读取 OPENAI_API_KEY
- 当 .env 只有 CUSTOM_API_KEY 时,自动补齐 OPENAI_API_KEY
- 避免辅助客户端读取不到凭证或落到错误 provider

## Gateway 启动互斥
- 增加 Gateway start guard,串行化启动流程
- 如果已有启动流程在进行中,直接复用健康检查结果
- 避免重复 Gateway 进程、重复日志和竞态状态覆盖

## 范围
- 桌面 Tauri 命令
- Web dev API 运行时
- Hermes provider 注册表
2026-05-15 17:32:38 +08:00
晴天
b9a7c043d2 fix(audit): 复查发现的 5 个 bug — 双 listener / duplicate stub / timer leak / i18n
逐项排错盘点(OpenClaw + Hermes 引擎 + 共享层全面复查)。

## Bug #1 — Hermes 引擎双 listener 数组混用
src/engines/hermes/index.js

onStateChange / onReadyChange 共用一个 `_listeners` 数组:
```js
onStateChange(fn) { _listeners.push(fn); ... }
onReadyChange(fn) { _listeners.push(fn); ... }   ← 同一数组
```

main.js 注册 sidebar 渲染回调时两个都注册:
```js
_engineStateUnsub = engine.onStateChange(() => renderSidebar(sidebar))
_engineReadyUnsub = engine.onReadyChange(() => renderSidebar(sidebar))
```

结果:每次 detectHermesStatus(15s 一次 poll)触发,sidebar 被
renderSidebar 调两遍。OpenClaw 引擎用的 lib/app-state.js 早就是分开
两个数组(_gwListeners + _listeners),Hermes 是退化实现。

修复:
- 拆成 _stateListeners / _readyListeners 两个数组
- 加 prevReady / prevRunning 做 diff,仅在状态实际变化时通知

## Bug #2 — Web 模式下 check_panel_update 永远返回 false
scripts/dev-api.js

`check_panel_update` 在 line 6785 有完整实现(fetch GitHub/Gitee
release API),但 line 8586 又 stub 了一次:
```js
check_panel_update() { return { hasUpdate: false } }
```

Object literal 后定义覆盖前定义,Web 模式下用户永远看不到「有新版」
提示,必须升级到桌面客户端才能查更新。

修复:删掉重复 stub,留注释说明真实实现位置。

esbuild 之前就在 build 里 warn `Duplicate key "check_panel_update"
in object literal`,现在 warning 也消失了。

## Bug #3 — engine-select.js setTimeout 不在 cleanup 时清
src/pages/engine-select.js

choose 动画里两个 setTimeout (600ms + 1300ms) 没保存 id,路由
cleanup 时不清。极端情况:用户点完 OpenClaw 后立刻点 secondary
"稍后再说" → 1300ms 后被强制 navigate 回原 targetRoute,把用户拉走。

修复:
- 模块级 _animTimers 数组追踪所有动画 setTimeout id
- cleanup 时 clearTimeout 全清
- stage.dataset 防御性访问(路由切走后 stage 可能已不在 DOM)

## Bug #4 — router.js 三处中文硬编码
src/router.js

之前页面 loading / 加载失败 / 重新加载按钮直接写死"加载中..."
"页面加载失败" "重新加载"。i18n 里 `common.loading` /
`common.pageLoadFailed` / `common.reloadRetry` 早已存在但没用上。

修复:import t() + 三处替换。

## Bug #5 — dashboard.js Promise 超时 Error 写死中文
src/pages/dashboard.js

`new Error(\`超时(${ms/1000}s)\`)` — 这条错误最后被 humanizeError
处理后展示给用户,本来应该走 i18n。改成英文 `Timed out after Xs`,
统一与日志聚合(其他地方都是英文)。

## 验证
- npm run build:PASS(1.85s, 无 duplicate-key warning)
- cargo fmt --check:PASS(无改动)
- 受影响场景:
  - 引擎切换 sidebar 性能(Hermes 双重渲染消失)
  - Web 模式更新提示(恢复正常)
  - engine-select 动画中途切走(不再被强拉回)
  - 加载/错误页(i18n 完整)
2026-05-14 07:17:28 +08:00
晴天
1873e23371 fix(critical): 5 个用户报告的真 bug 一次性修
## P0 Bug 1: chat.js `t is not defined` (页面渲染崩溃)
- chat.js 用了 30+ 处 t() 但**完全忘记 import i18n**
- 路由进入 /h/chat 立即抛 ReferenceError,整页渲染失败
- 修复:补 `import { t } from '../../../lib/i18n.js'`

## P0 Bug 2: i18n 嵌套对象解析失败 (所有 humanizeError 显示原始 key)
- 用户截图:`common.errorHint.generic` 直接显示而非翻译
- 根因:buildLocales() 不递归嵌套对象,把 common.error / errorHint /
  errorAction 这种嵌套 dict 当作 _() 翻译对象处理 →
  `result[lang].common.errorHint = 'errorHint'` (字符串)
- 影响:t('common.errorHint.network'/auth/timeout/...) 全部返回 key 自身
  → toast 用 humanizeError 的所有页面都看到原始 key
- 修复:buildLocales 加 _isTranslationObject + _materialize 递归

## P0 Bug 3: dev-api.js `Dynamic require of "node:fs" is not supported`
- 用户截图:文件管理器红字 "Dynamic require of node:fs"
- 根因:我之前在 hermes_fs_* / _validateFsPath / _hermesHome 用了
  `const fs = require('node:fs')` —— 但 Vite 插件运行在 ESM 上下文,
  CommonJS dynamic require 不支持
- 修复:删除所有 require('node:fs/path/os') —— 文件顶部已经 ESM
  `import fs from 'fs'` 等,直接复用即可

## P1 Bug 4: profiles/kanban/oauth fetch failed 错误丑陋
- 用户截图:3 个页面都显示红字 "fetch failed"(无任何上下文)
- 修复 1(dev-api.js):hermes_dashboard_api_proxy 把 ECONNREFUSED /
  fetch failed 转成 "Hermes Dashboard 未运行(端口 9119 无服务)。
  请在桌面端 ClawPanel 启动 Hermes Agent" — 含「未运行」关键字会被
  humanizeError 的 gatewayDown 正则匹配
- 修复 2(3 个页面):用 humanizeError 拆出 message + hint + 折叠 raw,
  渲染统一的 .page-inline-error 卡片(icon + 主行 + 副提示 + 技术详情
  details)。错误对象不再 stringify,保留给 humanizeError 完整识别

## P2 Bug 5: 重复的 require('node:path') in hermes_fs_list
- 同 Bug 3,循环里又 require 了一次 path,现已用顶部 import

## 影响面
 /h/chat 不再崩溃,profile switcher 正常显示
 所有用 humanizeError 的 toast 看到正确翻译(不再显示原始 key)
 文件管理器 Web 模式正常列出 ~/.hermes 目录
 Profile / Kanban / OAuth 显示「Hermes Dashboard 未运行」+
   「请前往仪表盘启动 Gateway 后重试」+ 折叠原始错误(点击展开)
 npm build pass
2026-05-14 06:35:20 +08:00
晴天
129d8c0ac1 feat(hermes): Batch 3 §L - 文件管理器(限定在 ~/.hermes 子树内)
校对:Hermes 没有 file HTTP API,必须自建。

## Rust 后端:3 个安全 fs 命令(~180 行)

### 安全策略
- 所有路径必须在 hermes_home() 子树内(防 path traversal)
- canonicalize 后用 starts_with 验证
- 拒绝绝对路径跳出 + .. 跳出
- 5 MB 文件大小上限
- 单次列目录最多 2000 条

### 命令
- hermes_fs_list(path) → { path, entries: [{name, kind, size, modified}] }
  · 隐藏文件默认不显示(.env 除外)
  · 排序:目录在前,文件在后,按名字
- hermes_fs_read(path) → { path, size, text?, binary_b64? }
  · UTF-8 文本 → text
  · 二进制 → 内置 base64 编码(不引新依赖)
- hermes_fs_write(path, content) → { path, size }
  · 父目录必须存在
  · 5 MB 上限

## 前端 /h/files 页面(~270 行)

### UI 布局
- 左侧:面包屑 + 目录树(点目录进入 / 点文件选中)
- 右侧:编辑器(文本)/ 预览(图片)/ 元信息(其他二进制)

### 文件树
- 文件图标按扩展名(📁/🔗/🖼️/📝/⚙️/📄)
- 文件大小显示(B/KB/MB)
- ".." 返回上级
- 选中态高亮

### 编辑器
- textarea 编辑,monospace 字体
- "保存 *" 状态标记 dirty
- 切目录/文件前 showConfirm 「丢弃未保存修改?」
- 保存成功 toast

### 二进制预览
- 图片 (png/jpg/gif/webp/svg) → 用 data: URL 显示
- 其他 → 「二进制文件(不可编辑)」+ 文件大小

## sidebar
- 「管理」section 加文件管理器入口(folder icon)
- /h/files 路由注册(含意外的 routes 顺序修复)

### dev-api.js
- Web 模式走 Node fs.readdirSync/readFileSync/writeFileSync
- 同样的安全策略:根目录 hermes_home() = process.env.HERMES_HOME 或 ~/.hermes
- realpath 验证 + 5 MB 限制

### CSS(~165 行)
- .hm-files-layout: grid 双栏(响应式 → 单栏)
- .hm-files-tree: 左侧树,breadcrumb + entry 列表
- .hm-files-pane: 右侧编辑器/预览
- .hm-files-editor: 全宽 textarea,monospace
- 响应式:768px 以下单栏

### i18n
- 12 个新键 × 3 语言(hermesFiles*)

## 修复
- src/engines/hermes/index.js 末尾多余 `}` 删除(之前 edit 留下)

## 累计
- Rust ~180 行 + 前端 ~270 行 + CSS ~165 行 + dev-api ~55 行
- 12 个 i18n × 3 语言
- cargo check ✓ + npm build ✓
2026-05-14 05:36:50 +08:00
晴天
0d6c4614e4 feat(hermes): Batch 2 §G - 多 Gateway 看板(同时运行多 profile Gateway)
设计:让用户给多个 profile 各自配 Gateway 实例,同时运行。
端口完全由 profile 的 config.yaml model.gateway.port 决定,ClawPanel 只负责 spawn + PID 跟踪。

## Rust 后端(~350 行)

### 数据结构
- MULTI_GW_PIDS: Mutex<Option<HashMap<String, u32>>> 全局 PID 表
- 持久化在 panelConfig.hermes.multiGateways: [{name, profile}]

### Helper 函数
- multi_gw_pids_get/set/remove: 线程安全 PID 表读写
- read_multi_gateways_config / write_multi_gateways_config: panel config R/W(保留其他字段)
- read_profile_gateway_port(profile): 缩进感知解析 profile config.yaml 的 model.gateway.port
- pid_is_alive(pid): Windows 用 tasklist /FI,Unix 用 kill -0 检测

### 5 个新 Tauri 命令
- hermes_multi_gateway_list() → [{name, profile, port, running, pid, owned}]
  · running = PID 存活 || TCP 探测可达
  · owned = ClawPanel spawn(可控制)vs 外部进程占着端口
- hermes_multi_gateway_add(name, profile) - 写入 panel config,名称合法性检查
- hermes_multi_gateway_remove(name) - 先停掉,再从配置删除
- hermes_multi_gateway_start(name) - spawn `hermes --profile <name> gateway run`
  · 注入 profile 的 .env
  · 等待端口可达(8 秒超时)
  · 记 PID
- hermes_multi_gateway_stop(name) - taskkill /F /PID (Windows) 或 kill -TERM (Unix)

### 发射 hermes-multi-gateway-changed 事件(前端可监听刷新)

## 前端(~230 行)

### /h/gateways 页面
- 顶部 + 添加 按钮
- 卡片网格(复用 .lazy-deps-grid)显示每个 Gateway:
  · name + status badge (运行中/已停止)
  · profile + port + PID + owned 提示
  · 启动 / 停止 / 删除 按钮
- 添加弹窗:name 输入 + profile 下拉(拉自 hermesProfilesList)
- 停止 / 删除均有 showConfirm(带 in-flight 警告)
- 外部进程占端口时 stop 按钮 disabled + tooltip

### sidebar
- 「管理」section 加 Gateways 入口(gateway icon)
- /h/gateways 路由注册

### dev-api.js
- Web 模式 fallback: 多 Gateway 不支持本地进程管理(throw friendly error)

### i18n
- 26 个新键 × 3 语言(hermesGateway*)

## 累计
- Rust ~350 行 + 前端 ~230 行 + i18n 78 字符串 + 路由/sidebar
- cargo check ✓ + npm build ✓
2026-05-14 05:30:18 +08:00