Commit Graph

269 Commits

Author SHA1 Message Date
晴天
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
晴天
449dbc3862 fix(channels): validate binding accounts by credential fields 2026-05-23 03:35:02 +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
晴天
d0f0d1f6b7 fix(gateway): release 0.16.5 2026-05-22 20:42:16 +08:00
晴天
4d82395b90 fix(engine-select): release 0.16.4 2026-05-22 19:46:07 +08:00
晴天
76c4a56302 fix: stabilize gateway startup and kernel i18n 2026-05-22 15:38:56 +08:00
晴天
41e688843e chore: release v0.16.2 2026-05-21 19:23:43 +08:00
晴天
14525c50e4 chore: release v0.16.1 2026-05-21 14:33:33 +08:00
晴天
14a5b1ee9a chore: release v0.16.0 2026-05-16 15:11:02 +08:00
晴天
12cc9cd6ce fix(assistant): give assistant a Hermes identity, surface raw install hint, unblock CI
Three follow-ups the user spotted in one round.

assistant.js — assistant did not know it was on Hermes
  Both engines (OpenClaw and Hermes Agent) reuse the same /assistant
  page (engines/hermes/index.js comments it as "共用页面/引擎无关"),
  but getSystemPromptBase() hard-coded the OpenClaw self-introduction:
  "你帮助用户管理和排障 OpenClaw AI Agent 平台 / 你精通 OpenClaw 的架
  构…", followed by a CLI cheatsheet for `openclaw gateway start` and
  `openclaw config apply`. Result: under the Hermes engine, the
  assistant happily told users to run `openclaw doctor` and edit
  `~/.openclaw/openclaw.json` — neither of which exists in the Hermes
  world.

  Split into a per-engine dispatcher:
    getSystemPromptBase()
      └ if hermes  → getHermesSystemPromptBase()  (new)
      └ else       → getOpenclawSystemPromptBase() (renamed, same body)

  The new Hermes base prompt covers the facts that actually matter:
   - dual-process layout: Gateway 8642 (chat API, what ClawPanel
     mostly drives) vs Dashboard 9119 (admin/profiles/skills/oauth/
     kanban — must be started separately)
   - Profile system (independent workspaces, switchProfile restarts
     dashboard, multi-gateway view)
   - lazy_deps allowlist and why pre-installing matters
   - paths: ~/.hermes (data) and ~/.hermes-venv (interpreter), with a
     reminder that ~/.openclaw/clawpanel.json is the panel config
     shared with the OpenClaw engine — not Hermes data
   - Top-5 problem playbook (9119 not running, venv missing, channels
     hanging on first launch, gateway crashing, profile drift)
   - Explicit "do not give the user `openclaw …` commands"

  Two more spots in buildSystemPrompt() are also engine-aware now:
   - the "ClawPanel 工具能力" bullet list inside the soul-cache branch
   - the "跨平台路径" reminder (Hermes points to .hermes / .hermes-venv)

lazy-deps.js — "请确认目标资源是否仍存在" was masking the real hint
  When the user has not installed Hermes yet, Rust's
  `hermes_lazy_deps_features` returns the very actionable string
  "Hermes venv 未找到(~/.hermes-venv 不存在)。请先安装 Hermes。".
  humanize-error.js then sees "未找到", classifies the error as
  notFound, and replaces the message with the generic template
  "请确认目标资源是否仍存在" — which tells the user nothing about
  installing Hermes.

  Take humanizeError() but render `message + raw` instead of
  `message + hint`. The user now sees both the friendly title and the
  exact Rust-side instruction. Drop the unused humanizeErrorText
  import that this commit replaces.

config.rs — unblock CI (clippy too_many_arguments on existing code)
  The clippy gate has been red on main since e1eda2d ("import external
  client configs") because two helpers in commands/config.rs take >7
  positional parameters:
    - push_client_candidate (14 params)
    - scan_json_client_file (10 params)

  Both helpers exist purely to push a flat record into a Vec<Value>.
  Wrapping them in a struct just to satisfy clippy would force every
  caller to first build that struct, hurting readability. Suppress
  clippy::too_many_arguments locally on these two functions with an
  inline comment explaining why.

## Verification
- node --check + npm run build: clean
- cargo clippy --all-targets -- -D warnings: now compiles to
  "Finished `dev` profile" with zero errors/warnings (previously
  failed with two too_many_arguments)
- Playwright: import lazy-deps with api.hermesLazyDepsFeatures mocked
  to throw "Hermes venv 未找到 … 请先安装 Hermes。", rendered content
  contains "请先安装 Hermes" (hasRaw=true), does not contain the
  generic "请确认目标资源是否仍存在" (hasGenericNotFound=false), and
  does not contain "[object Object]"
2026-05-16 14:24:45 +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
晴天
207b1c7c55 fix(windows): make gateway terminal window actually visible
The previous implementation passed CREATE_NEW_CONSOLE to a Rust
StdCommand spawning cmd.exe directly, but Rust's default Stdio::inherit
copies the parent stdio handles into STARTUPINFO with
STARTF_USESTDHANDLES, which neutralizes CREATE_NEW_CONSOLE. The cmd
process then ran without a visible window (MainWindowHandle = 0), so
users only saw the OpenClaw node child started by runner.cmd in Task
Manager and got the impression that "the terminal does not pop up".

Wrap the launch in `cmd /c start "OpenClaw Gateway" /D <dir> cmd /D /K
runner.cmd` so the new console is created by the `start` builtin via a
fresh CreateProcess call without inherited stdio. The outer cmd /c
itself is short-lived and uses CREATE_NO_WINDOW so it does not flash a
window, and the inner cmd hosting runner.cmd reliably becomes a normal
visible terminal that the user can close to stop Gateway.

Because the visible terminal is now detached from our process tree,
spawn().id() can no longer be tracked. Instead, poll netstat after the
launch and record the listener PID as the active Gateway PID, which is
what the stop path actually needs to send a precise kill to.

## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
2026-05-16 12:07:04 +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
晴天
c592f47217 fix(windows): harden gateway terminal runner
Use a generated clawpanel-gateway.cmd runner for the visible Windows Gateway terminal instead of embedding the resolved CLI path directly in the cmd /K command string. This keeps the simple visible-terminal model while improving quoting behavior for standalone, npm shim, and .js CLI paths.

## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
- cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
- npm run build
2026-05-15 19:58:15 +08:00
晴天
807a1d87a9 fix(windows): launch gateway in visible terminal
Windows users can now start OpenClaw Gateway in a normal terminal window instead of relying on a hidden background process. Keeping the terminal open keeps Gateway running; closing it stops Gateway.

The backend guardian also treats a closed Windows Gateway terminal as an intentional stop, so it will not immediately pop a new terminal back up after the user closes it. macOS and Linux keep their existing startup behavior.

## Verification
- cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check
- cargo check --manifest-path src-tauri/Cargo.toml
- cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
- npm run build
2026-05-15 19:51:52 +08:00
晴天
dcafd29e51 fix(openclaw): allow upgrading kernel to latest patch
OpenClaw Chinese edition has advanced to 2026.5.12-zh.2 while the panel still recommended 2026.5.12-zh.1 and treated same-base zh patch versions as equivalent.

This updates the recommended Chinese kernel target and makes the version comparison detect suffix-level upgrades such as zh.1 -> zh.2 when both sides expose suffixes. It also adds explicit latest-upstream upgrade actions on the Services and About version cards so users can upgrade to the latest detected upstream version without going through the manual version picker.

## Changes
- update OpenClaw Chinese recommended target to 2026.5.12-zh.2
- detect same-base suffixed patch upgrades in version info
- add Services page "upgrade to latest" action and confirmation copy
- add About page latest-upstream action

## Verification
- npm run build
- cargo check
2026-05-15 19:08:42 +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
晴天
c264224e7c fix(rust): 修 CI 失败的 4 个 clippy + fmt 问题
之前 3 个 commit (cc19a07/1873e23/d97e196/6a0d874/bf55ca0) 推上去后 CI
全部 fail(三平台都 fail),原因不是 JS 改动而是历史 Rust 代码累积的
clippy/fmt 检查问题,本次一次性修齐让 CI 重新跑通。

## 4 个 clippy errors(-D warnings 模式)
1. `field 'name' is never read` (HermesAttachment::name)
   - 该字段是前端可选传入的附件原文件名,目前后端未消费
   - 加 #[allow(dead_code)] + 说明性 doc,保留字段供后续展开附件清单 UI

2. `unnecessary closure used to substitute value for Result::Err` × 2
   - hermes_dashboard_api_proxy 里
     `unwrap_or_else(|_| Value::String(body))` →
     `unwrap_or(Value::String(body))`
   - 闭包捕获 _ 但不用,直接用 unwrap_or

3. `manually reimplementing div_ceil`
   - base64_encode 里 `(bytes.len() + 2) / 3` →
     `bytes.len().div_ceil(3)`(标准库 1.73+ 支持)

## fmt 修复
hermes.rs 多处长 match arm + long argument list 不符合 rustfmt 默认风格,
跑 cargo fmt --all 自动修齐。

## 验证
✓ cargo fmt --all -- --check  PASS
✓ cargo check                 PASS
✓ cargo clippy --all-targets -- -D warnings  PASS(无 warning 无 error)

## 影响
- 不改变任何运行时行为
- 不影响前端 / 不影响 i18n / 不影响 Tauri 命令签名
- 纯代码风格 + lint 修复
2026-05-14 06:54:13 +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
晴天
64f4668522 feat(hermes): Batch 3 §P TTS + Dashboard session token 自动注入
核查发现:
- §P TTS: Hermes 内核没有 HTTP TTS 端点(只有 lazy_deps 的 tts.edge/elevenlabs PyPI 包) → 改用浏览器 Web Speech API(100% 离线、跨平台)
- Dashboard token: 9119 大部分 /api/* 需要 token 鉴权,token 注入到 SPA HTML 的 window.__HERMES_SESSION_TOKEN__ → 必须从 HTML 抓

## §P TTS — 浏览器原生(src/lib/tts.js)

新工具模块 src/lib/tts.js(~110 行):
- speak(text, lang?) - 异步播放,返回 Promise
- toggle(text, lang?) - 重复播放则停止
- stopSpeaking() / isSpeaking() / isSupported()
- 自动语言检测:中/英/日/韩
- pickVoice 启发式:精确匹配 > 前缀匹配 > 默认
- Chrome bug 兼容:voiceschanged 异步加载 + 100ms 兜底

### chat.js 接入
- assistant 消息 footer 复制按钮旁加 🔊 朗读按钮(图标 speaker)
- 仅 tts.isSupported() 时渲染(不支持的浏览器隐藏)
- 点击 toggle,简化文本(去 markdown 代码块、url)
- 失败 toast 提示

### i18n
- chatSpeak / chatSpeakShort / chatSpeakFailed × 3 语言

### CSS
- .hm-chat-msg-tts 复用 copy 按钮风格

## Dashboard session token 自动注入器(解锁 §J/§O/§Q)

校对发现:dashboard 9119 大部分 /api/* 端点 _require_token 保护,
token 是进程启动时 secrets.token_urlsafe(32) 生成,没有公开获取 API,
只能 GET / 抓 SPA HTML 提取 window.__HERMES_SESSION_TOKEN__="..."。

### Rust 后端增强 hermes_dashboard_api_proxy
- 模块级 DASHBOARD_SESSION_TOKEN: Mutex<Option<String>> 缓存
- fetch_dashboard_session_token(port): GET / + 字符串搜索 needle 提取 token
- dashboard_session_token(port, force_refresh): 缓存 + 强刷接口
- proxy 实现:
  1. 拿缓存 token,注入 X-Hermes-Session-Token header
  2. 发请求;若 401 → 强刷 token 重试一次
  3. 仍失败抛 HTTP 错
- build_request 闭包复用避免重复代码

### Web 模式 dev-api 同步
- _fetchDashboardToken(port) 抓 HTML 正则提取
- _getDashboardToken(port, forceRefresh) 模块级缓存
- hermes_dashboard_api_proxy 401 重试逻辑

## 影响
- 已用 dashboard_api_proxy 的 §H Profiles + §M Kanban 立即受益(之前 401 会失败)
- §J dashboard plugin 可见性、§Q OAuth 现在可直接复用
- §O Terminal 走 WS,token 注入路径不同,但 _getDashboardToken 可复用

## 累计
- Rust: ~60 行(token 抓取 + 缓存 + 重试)
- 前端: tts.js 新文件 110 行 + chat.js TTS 按钮 + dev-api token 注入
- i18n: 3 个新键 × 3 语言
- cargo check ✓ + npm build ✓
2026-05-14 05:19:02 +08:00
晴天
3c8c315402 feat(hermes): Batch 2 §I + Batch 3 §M - 流恢复 + Kanban 看板
## Batch 2 §I 流恢复(chat 在切页/刷新后能接上 run)

校对稿:用 run_id 持久化到 localStorage,新页面挂载时查询 status 决定是否重连。

### 后端
- 新 Tauri 命令 hermes_run_status(run_id):
  · GET /v1/runs/{run_id} 返回 { run_id, status, last_event, output?, ... }
  · status: running / stopping / completed / failed / cancelled / waiting_for_approval
  · 404 友好返回 status='not_found' 而不是抛错

### 前端 chat-store
- 新 STORAGE_ACTIVE_RUN 持久化 { runId, sessionId, profile, t }
- hermes-run-started 监听里 safeSet 持久化
- cleanupAfterRun 里 safeRemove 清理
- 新方法 recoverIfRunning():
  · 跨 profile / 1 小时过期 → 直接清
  · status=running/stopping/waiting_for_approval → attachStreamListeners + 恢复 streaming
  · status=已结束 → 拉最新 messages
  · 404 → 静默清

### chat.js
- 页面挂载时 store.recoverIfRunning() — 切页/刷新后无缝接上流

## Batch 3 §M Kanban 看板(Hermes 已内置)

校对稿:「Hermes 已内置 kanban 系统,直接调 /api/plugins/kanban/* 即可」。
设计稿原本是「自建本地存储」(~800 行),复用 Hermes 内置后大幅缩减。

### 新页面 /h/kanban
- 全部走 hermesDashboardApi(复用 §H 的基础设施)
- 顶部 board 切换器 + 「+ 新任务」按钮
- 渲染 board.columns(按状态分列:todo / in_progress / blocked / done / archived)
- 任务卡片:title + summary(2 行截断)+ priority badge + assignee + 评论数
- 点卡片 → showContentModal 显示详情 + 「修改状态」按钮
- 修改状态 → PATCH /api/plugins/kanban/tasks/{id} { status }
- board 切换 → POST /api/plugins/kanban/boards/{slug}/switch

### sidebar
- 「管理」section 加 Kanban 入口(inbox 图标)
- /h/kanban 路由注册

### CSS (.hm-kanban-*)
- 水平滚动 board 容器
- 280px 固定宽度列 + 内部滚动
- 卡片 hover 边框变 accent 色 + 轻阴影
- 优先级 badge(琥珀色)/ assignee(accent 色)

### i18n
- 27 个新键 × 3 语言(zh-CN/en/zh-TW)

## 累计
- Rust: 1 个新命令(hermes_run_status ~30 行)
- 前端: chat-store 流恢复(~40 行)+ kanban 新页面(~230 行)
- i18n: 27 个新键 × 3 语言
- CSS: ~100 行
- cargo check ✓ + npm build ✓
2026-05-14 05:12:37 +08:00
晴天
3168551569 feat(hermes): Batch 2 §H - Profiles 管理 UI + Dashboard API 通用代理
校对稿要点:「Profiles 全部走 HTTP /api/profiles*,无需自己写 CLI 桥接」。

## 基础设施: hermes_dashboard_api_proxy(通用 9119 HTTP 代理)

新增 Tauri 命令 hermes_dashboard_api_proxy(method, path, body, headers):
- 支持 GET/POST/PUT/PATCH/DELETE
- 走 Dashboard 9119 端口(无需 API_SERVER_KEY,本地绑定)
- 自动 JSON parse + 错误时友好提示「请先启动 Dashboard」
- 一次实现,未来 Batch 2/3 的 Profiles/Kanban/OAuth/Sessions 都走这一个入口

前端 wrapper: api.hermesDashboardApi(method, path, body, headers)
dev-api.js: Web 模式同步实现

## Profiles 管理页 /h/profiles

新文件 src/engines/hermes/pages/profiles.js:
- GET /api/profiles 列表 → 渲染卡片网格(复用 .lazy-deps-grid 样式)
- 每张卡片:profile 名 + active 徽章 + Switch/Rename/Delete 按钮
- 「+ 新建」按钮 → showModal 弹窗(name + clone_from_default 选项)
- 「重命名」→ PATCH /api/profiles/{name} { new_name }
- 「删除」→ showConfirm(带 impact 提示「永久清除会话/凭据/记忆」)→ DELETE /api/profiles/{name}
- 「切换到此」→ 复用现有 chatStore.switchProfile(CLI 实现)
- 失败走 humanizeError 友好提示
- active profile 与 chatStore.state.activeProfile 对齐

## sidebar + 路由
- Hermes 引擎「管理」section 加 Profile 管理入口
- 路由 /h/profiles 注册到 hermes/index.js

## i18n
- engine.hermesProfilesTitle / hermesProfilesDesc / hermesProfilesEmpty
- hermesProfileNew / NewTitle / NameLabel / NameRequired / CloneFromDefault / CloneHint
- hermesProfileSwitch / Switched / SwitchFailed
- hermesProfileRename / RenameTitle / NewNameLabel / Renamed / RenameFailed
- hermesProfileDelete / DeleteConfirm / DeleteImpact / Deleted / DeleteFailed
- hermesProfileActive / Created / CreateFailed
- 共 21 个键 × 3 语言

## 累计
- Rust: 1 个新命令(hermes_dashboard_api_proxy ~50 行)
- 前端: 1 个 wrapper + 新页面 ~180 行
- dev-api.js: 1 个 handler
- i18n: 21 个新键 × 3 语言
- cargo check ✓ + npm build ✓
2026-05-14 05:04:53 +08:00
晴天
8eb8a7666e feat(hermes): Batch 3 §K - 多模态图片上传(chat attach + 拖拽 + 粘贴 + base64)
Hermes Agent 已支持 OpenAI 多模态格式(tests/gateway/test_api_server_multimodal.py),
ClawPanel 前端补 attach UI 即可对接 Claude 3.5 / GPT-4o / Gemini 等视觉模型。

## 后端
- HermesAttachment 结构:{ kind, mime, name?, data_base64 }
- build_multimodal_input(text, attachments) 把 text 转成
  [{type:"text",text}, {type:"image_url",image_url:{url:"data:..;base64,.."}}, ...]
- hermes_agent_run 加 attachments 参数(向后兼容:未传时走原 string input)

## 前端
- tauri-api.js: hermesAgentRun 加 attachments 参数
- chat-store sendMessage:
  · 允许 text 空但有 attachments 也能发
  · user 消息记录 attachments[].dataUrl 用于气泡内渲染
  · isTauriRuntime 时 await api.hermesAgentRun(...,atts)
- chat.js 渲染层:
  · renderMessage 在内容前插入 .hm-chat-msg-attachments + .hm-chat-msg-image(点击放大)
  · 输入栏前面增加 .hm-chat-attach-preview 预览条(缩略图 + 文件名 + × 移除)
  · 输入框左侧加 paperclip attach 按钮 + 隐藏 <input type="file" accept="image/*" multiple>
  · 发送按钮 disabled 条件改为 (!text && !attachments)
- chat.js 交互:
  · fileToBase64 用 FileReader 转纯 base64
  · addAttachmentFromFile 校验 image/* + 10MB + 最多 5 张
  · attach 按钮 click → 触发文件选择器
  · 拖拽到输入区 → dragover 高亮 + drop 加附件
  · 粘贴图片 → clipboardData 读 image 文件
  · 移除按钮 splice
- chat-store.sendMessage 后清 pendingAttachments

## 限制
- 单图最大 10 MB(base64 后约 13 MB)
- 一次最多 5 张
- 超限 toast 友好提示
- 非图片格式拒收

## CSS
- .hm-chat-attach-btn / hover / disabled
- .hm-chat-attach-preview / chip / chip-name / chip-remove
- .hm-chat-input-wrap--dragover(拖拽虚线高亮)
- .hm-chat-msg-attachments / msg-image / msg-image--zoom(点击放大模式)

## i18n
- engine.chatAttach / chatAttachRemove / chatAttachOnlyImage
- chatAttachTooBig / chatAttachTooMany / chatAttachReadFailed
- 3 语言(zh-CN/en/zh-TW)

## 注意
- Web 模式(dev-api 走 SSE)暂不支持 attachments 透传(hermes_agent_run_stream 没改),
  原因:Web 模式当前 chat 是 stream-only 路径,需要单独改 dev-api 的 hermes_agent_run_stream handler
- 桌面端 Tauri 模式开箱可用
- 累计变动:6 个文件,~120 行新代码,6 个 i18n 键
- cargo check ✓ + npm build ✓
2026-05-14 04:59:36 +08:00
晴天
112963b2b7 feat(hermes): Batch 1 §E - Sessions 导出(走 dashboard /api/sessions/{id}/messages)
校对稿订正:不走 CLI `hermes sessions export`,直接调 dashboard 9119 HTTP API。

## 后端
- 新 Tauri 命令 hermes_session_export(session_id):
  · GET http://127.0.0.1:{dashboard_port}/api/sessions/{id}/messages
  · 拿原始 JSON 返回前端
  · 错误提示「请先启动 Dashboard」(dashboard server 必须运行)

## 前端
- tauri-api.js: hermesSessionExport wrapper
- sessions.js: 详情面板「打开会话 / Pin / 导出 / 删除」并列布局
  · 点导出 → Blob + URL.createObjectURL + a.download 浏览器下载 hermes-session-{id}.json
  · toast 成功/失败
- dev-api.js: Web 模式 handler 同步调 dashboard 端口

## i18n
- sessionsExport / sessionsExportSuccess / sessionsExportFailed × 3 语言
2026-05-14 04:54:25 +08:00
晴天
832bb9a6ef feat(hermes): Batch 1 §A + §B + P1-3 lazy_deps 优化 - 占位修 + 中文硬编码清 + 自定义 venv 适配
## §A channels.js 占位路由处理
- /h/channels 当前是 487 字节 placeholder
- 已确认 sidebar 没挂入口(getNavItems 不含 channels)
- 路由表保留但加注释表明这是 reserved,完整实现见 Batch 3

## §B config.js i18n 化(清理 5 处硬编码)
- "重新加载" / "保存配置" / "saving…" / "loading…" / "raw yaml editor"
- "配置已保存,建议重启 Hermes Gateway 生效" toast
- 抽 7 个新键到 locales/modules/engine.js(3 语言):
  · hermesConfigEyebrow / hermesConfigReload / hermesConfigSave
  · hermesConfigSaveSuccess / hermesConfigStatusSaving
  · hermesConfigStatusLoading / hermesConfigStatusReady

## P1-3 lazy_deps 校对稿优化(commit b852ebb 的补丁)
hermes_venv_python() 增加 HERMES_PYTHON 环境变量优先级:
1. HERMES_PYTHON env var(适配 brew / uv tool / 容器等自定义 venv)
2. ~/.hermes-venv/{bin/python | Scripts/python.exe}(默认)

allow_lazy_installs 守门:经核实内核 tools.lazy_deps.ensure 内部已检查
security.allow_lazy_installs,禁用时抛 FeatureUnavailable,Rust 端已捕捉
并友好返回错误(无需额外代码)。

## 累计
- 3 个修改文件(hermes/index.js + config.js + engine.js + hermes.rs)
- 7 个新 i18n 键 × 3 语言
- cargo check ✓ + npm build ✓
2026-05-14 04:51:16 +08:00