mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-10 17:42:49 +08:00
feat: 飞书官方插件迁移 + 配对审批 + Gateway防卡死 + 微信升级修复 + 更新检测修复
- 飞书渠道从 @openclaw/feishu 迁移到 @larksuite/openclaw-lark 官方插件 - 保存飞书配置时自动禁用旧 feishu 插件,防止新旧插件冲突 - 所有主要渠道(飞书/Telegram/Discord/Slack)启用配对审批UI - gateway_command 增加20s超时,超时后force-kill+fresh start - 全平台启动前端口占用检查,防止Guardian无限拉起 - Linux gateway_command 补齐 Duration 导入和 cleanup_zombie 实现 - Guardian自动守护在Tauri桌面端也启用,轮询间隔30s→15s - 微信渠道:升级操作不再弹出扫码二维码,按钮文案区分安装/升级 - 版本更新检测:CI不再将minAppVersion写死为当前版本 - 部署脚本增强OpenClaw检测,支持已安装的官方版 - 日间/夜间模式圆形扩散切换动画(View Transitions API) - API错误信息完整展示(429限流等),URL自动转可点击链接 - 第三方API接入引导优化:移除内置密钥,引导式流程 - 修复全平台 Clippy 警告(strip_prefix/dead_code/unnecessary_unwrap等) - Rust代码格式化修复(cargo fmt) - toast组件支持HTML内容渲染 - Rust后端test_model返回详细错误信息
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
# ClawPanel v0.9.0 规划
|
||||
|
||||
## 命令执行权限管理 (Issue #55)
|
||||
|
||||
### 需求
|
||||
AI 助手执行终端命令时,允许用户配置白名单/黑名单规则,控制哪些命令可以执行。
|
||||
|
||||
### 规则类型
|
||||
|
||||
| 类型 | 示例 | 说明 |
|
||||
|------|------|------|
|
||||
| 精确匹配 | `go run main.go` | 只允许/禁止这一条完整命令 |
|
||||
| 前缀通配 | `go *` | 允许/禁止所有 go 开头的命令 |
|
||||
| 全局 | `*` | 允许/禁止所有命令 |
|
||||
|
||||
### 配置模式
|
||||
|
||||
- **确认模式**(默认)— 每条命令都弹窗确认
|
||||
- **白名单模式** — 匹配白名单的命令自动执行,其余弹窗确认
|
||||
- **黑名单模式** — 匹配黑名单的命令直接拒绝,其余弹窗确认
|
||||
- **无限模式** — 所有命令自动执行(当前已有的 unlimited 模式)
|
||||
|
||||
### 存储位置
|
||||
|
||||
`~/.openclaw/clawpanel.json` → `commandRules` 字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"commandRules": {
|
||||
"mode": "whitelist",
|
||||
"rules": [
|
||||
{ "pattern": "go run *", "action": "allow" },
|
||||
{ "pattern": "npm *", "action": "allow" },
|
||||
{ "pattern": "rm -rf /", "action": "deny" },
|
||||
{ "pattern": "git *", "action": "allow" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 实现方案
|
||||
|
||||
1. **设置页面 UI** — 安全设置页面新增「命令规则」区域
|
||||
- 规则列表(增删改)
|
||||
- 模式切换(确认/白名单/黑名单/无限)
|
||||
- 预设模板(开发者常用、安全最小权限等)
|
||||
|
||||
2. **存储** — 读写 `clawpanel.json` 的 `commandRules`
|
||||
|
||||
3. **拦截逻辑** — `assistant.js` 的 `assistant_exec` 调用前
|
||||
- 解析命令字符串
|
||||
- 按规则列表逐条匹配(支持 glob 通配符)
|
||||
- 匹配到 allow → 自动执行
|
||||
- 匹配到 deny → 直接拒绝并提示
|
||||
- 无匹配 → 按模式决定(弹窗确认或拒绝)
|
||||
|
||||
4. **Rust 后端** — `assistant_exec` 命令增加规则检查
|
||||
- 从 `clawpanel.json` 读取规则
|
||||
- glob 匹配逻辑
|
||||
- 返回 `{ allowed: bool, rule: string }` 给前端
|
||||
|
||||
### 优先级
|
||||
|
||||
中等。当前 AI 助手已有 4 种模式(聊天/规划/执行/无限),命令规则是对「执行」模式的细化增强。
|
||||
|
||||
---
|
||||
|
||||
## 安装体验优化
|
||||
|
||||
### 默认安装原版包
|
||||
当前版本选择器默认选中「汉化版」,改为默认「原版」(official)。用户可切换。
|
||||
|
||||
### 安装页面环境检测实时生效
|
||||
保存自定义 Node.js 路径后,检查是否需要重启才生效。v0.8.0 已改用 RwLock 支持运行时刷新,需验证是否完全生效。
|
||||
|
||||
### Linux Web 版后台更新机制
|
||||
Linux Web 版检测到新版本后,目前只能手动 `git pull`。需要实现:
|
||||
- 面板内一键更新按钮(自动 git pull + npm install + 重启服务)
|
||||
- 或者 systemd 服务自动拉取更新
|
||||
|
||||
---
|
||||
|
||||
## 其他待规划功能
|
||||
|
||||
- [ ] 消息渠道:渠道级别的消息统计(收发量、响应时间)
|
||||
- [ ] 模型配置:支持更多服务商预设(硅基流动、智谱、百川、通义千问等国内模型)
|
||||
- [ ] Docker 桌面版:Rust 原生 Docker API(bollard crate)替代 Node.js 后端
|
||||
- [ ] 前端热更新增量包:只下载变更文件,减小更新包体积
|
||||
- [ ] 多语言支持:i18n 框架(中/英双语)
|
||||
@@ -1,777 +0,0 @@
|
||||
# AI 助手功能扩展规划
|
||||
|
||||
> 基于现有工具架构(TOOL_DEFS + executeTool + getEnabledTools),扩展 6 大能力模块。
|
||||
> 每个模块独立开关,遵循现有的 `_config.tools.xxx` + 设置面板 toggle 模式。
|
||||
|
||||
---
|
||||
|
||||
## 当前架构概览
|
||||
|
||||
```
|
||||
TOOL_DEFS = {
|
||||
system: [get_system_info] // 始终可用
|
||||
process: [list_processes, check_port] // 始终可用
|
||||
interaction: [ask_user] // 始终可用
|
||||
terminal: [run_command] // 开关控制
|
||||
fileOps: [read_file, write_file, list_directory] // 开关控制
|
||||
}
|
||||
|
||||
_config.tools = { terminal: true/false, fileOps: true/false }
|
||||
```
|
||||
|
||||
扩展后:
|
||||
|
||||
```
|
||||
TOOL_DEFS = {
|
||||
...existing,
|
||||
docker: [docker_list, docker_exec, docker_logs, wsl_exec] // 新增
|
||||
webSearch: [web_search, fetch_url] // 新增
|
||||
ssh: [ssh_exec, ssh_read_file, ssh_write_file] // 新增
|
||||
knowledge: [search_knowledge] // 新增
|
||||
}
|
||||
|
||||
_config.tools = {
|
||||
...existing,
|
||||
docker: false, // 默认关闭
|
||||
webSearch: false, // 默认关闭
|
||||
ssh: false, // 默认关闭
|
||||
knowledge: false, // 默认关闭
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 模块一:Docker / WSL 管理工具
|
||||
|
||||
### 场景
|
||||
- 用户的 OpenClaw 可能安装在 Docker 容器或 WSL 中
|
||||
- 本地检测不到时,帮用户在容器/WSL 内操作
|
||||
- 查看容器日志、进入容器执行命令、管理容器生命周期
|
||||
|
||||
### 工具定义
|
||||
|
||||
| 工具名 | 描述 | 参数 | 危险等级 |
|
||||
|--------|------|------|----------|
|
||||
| `docker_list` | 列出 Docker 容器 | `filter?`, `all?` | 安全 |
|
||||
| `docker_exec` | 在容器内执行命令 | `container`, `command` | ⚠️ 危险 |
|
||||
| `docker_logs` | 查看容器日志 | `container`, `lines?` | 安全 |
|
||||
| `docker_compose` | 执行 docker-compose 命令 | `action`, `file?`, `service?` | ⚠️ 危险 |
|
||||
| `wsl_exec` | 在 WSL 内执行命令 | `distro?`, `command` | ⚠️ 危险 |
|
||||
| `wsl_list` | 列出 WSL 发行版 | — | 安全 |
|
||||
|
||||
### 后端实现
|
||||
|
||||
```
|
||||
Tauri (Rust):
|
||||
- docker_list → Command::new("docker").args(["ps", ...])
|
||||
- docker_exec → Command::new("docker").args(["exec", container, ...])
|
||||
- wsl_exec → Command::new("wsl").args(["-d", distro, "-e", ...]) (Windows only)
|
||||
|
||||
dev-api.js (Web):
|
||||
- execSync('docker ps --format json')
|
||||
- execSync(`docker exec ${container} ${command}`)
|
||||
- execSync(`wsl -d ${distro} -e ${command}`) (Windows only)
|
||||
```
|
||||
|
||||
### 安全围栏
|
||||
- `docker_exec` / `wsl_exec` 归入 DANGEROUS_TOOLS
|
||||
- `docker rm`, `docker rmi`, `docker system prune` 归入 CRITICAL_PATTERNS
|
||||
|
||||
### UI 扩展
|
||||
- 设置面板工具权限 tab 新增 toggle:
|
||||
```
|
||||
Docker / WSL 工具 — 允许管理容器和 WSL 环境
|
||||
```
|
||||
|
||||
### 内置技能卡片
|
||||
```js
|
||||
{
|
||||
id: 'detect-docker-openclaw',
|
||||
icon: '🐳',
|
||||
name: '检测 Docker/WSL 中的 OpenClaw',
|
||||
desc: '扫描 Docker 容器和 WSL,查找 OpenClaw 安装',
|
||||
tools: ['docker'],
|
||||
prompt: `请帮我检查 Docker 和 WSL 中是否安装了 OpenClaw。
|
||||
1. 调用 get_system_info 判断操作系统
|
||||
2. 用 docker_list 列出所有容器,过滤包含 openclaw/gateway 的
|
||||
3. 如果是 Windows,用 wsl_list 列出 WSL 发行版
|
||||
4. 对每个 WSL 发行版,用 wsl_exec 执行 "which openclaw" 检测
|
||||
5. 汇总发现的 OpenClaw 实例及其状态`
|
||||
}
|
||||
```
|
||||
|
||||
### 优先级:🔴 高(解决用户最常见困惑)
|
||||
### 工时估算:1-2 天
|
||||
|
||||
---
|
||||
|
||||
## 模块二:联网搜索工具
|
||||
|
||||
### 场景
|
||||
- 用户遇到不常见的错误,AI 知识库可能没有
|
||||
- 搜索 GitHub Issues、文档、Stack Overflow 找到解决方案
|
||||
- 查找最新版本信息、API 文档等
|
||||
|
||||
### 工具定义
|
||||
|
||||
| 工具名 | 描述 | 参数 | 危险等级 |
|
||||
|--------|------|------|----------|
|
||||
| `web_search` | 联网搜索关键词 | `query`, `max_results?` | 安全 |
|
||||
| `fetch_url` | 抓取网页内容 | `url` | 安全 |
|
||||
|
||||
### 后端实现方案(3 选 1)
|
||||
|
||||
#### 方案 A:DuckDuckGo Instant Answer API(推荐,免费无 Key)
|
||||
```js
|
||||
// 搜索
|
||||
const resp = await fetch(`https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json`)
|
||||
// 但 Instant Answer 只返回摘要,不返回搜索结果列表
|
||||
|
||||
// 实际搜索需要用 DuckDuckGo HTML 页面解析或第三方库
|
||||
```
|
||||
|
||||
#### 方案 B:SearXNG 代理(自托管,最灵活)
|
||||
```js
|
||||
// 部署一个 SearXNG 实例,或者用公共实例
|
||||
const resp = await fetch(`https://searx.example.com/search?q=${query}&format=json`)
|
||||
```
|
||||
|
||||
#### 方案 C:Jina Reader API(推荐搭配使用,免费)
|
||||
```js
|
||||
// 将任意 URL 转为纯文本/Markdown
|
||||
const resp = await fetch(`https://r.jina.ai/${targetUrl}`)
|
||||
const text = await resp.text()
|
||||
```
|
||||
|
||||
### 推荐组合
|
||||
- **搜索**:使用 DuckDuckGo 的 `html.duckduckgo.com/html/?q=xxx` 页面解析结果
|
||||
- **内容抓取**:使用 Jina Reader `r.jina.ai/URL` 获取纯文本
|
||||
- 两者都 **免费无 Key**,无需用户配置
|
||||
|
||||
### 系统提示词补充
|
||||
```
|
||||
## web_search 使用指南
|
||||
当你无法确定答案或需要最新信息时,可以使用 web_search 搜索互联网。
|
||||
搜索后,如果需要更多内容,可以用 fetch_url 抓取具体页面。
|
||||
搜索技巧:
|
||||
- 加 site:github.com 搜索 GitHub
|
||||
- 加 site:stackoverflow.com 搜索 StackOverflow
|
||||
- 搜索错误信息时,用引号包裹关键错误文本
|
||||
```
|
||||
|
||||
### 安全围栏
|
||||
- 搜索和抓取不涉及破坏性操作,不归入 DANGEROUS_TOOLS
|
||||
- 但需要网络请求,添加超时保护(10 秒)
|
||||
- URL 抓取限制最大内容长度(100KB → 截断)
|
||||
|
||||
### UI 扩展
|
||||
```
|
||||
联网搜索 — 允许搜索互联网和抓取网页内容(需联网)
|
||||
```
|
||||
|
||||
### 优先级:🔴 高(大幅提升问题解决能力)
|
||||
### 工时估算:0.5-1 天
|
||||
|
||||
---
|
||||
|
||||
## 模块三:SSH 远程管理工具
|
||||
|
||||
### 场景
|
||||
- 用户的 OpenClaw 部署在远程服务器上
|
||||
- 帮用户远程安装、配置、排查 OpenClaw
|
||||
- 远程查看日志、重启服务、修改配置
|
||||
|
||||
### 工具定义
|
||||
|
||||
| 工具名 | 描述 | 参数 | 危险等级 |
|
||||
|--------|------|------|----------|
|
||||
| `ssh_exec` | 在远程服务器执行命令 | `connection_id`, `command` | ⚠️ 危险 |
|
||||
| `ssh_read_file` | 读取远程文件 | `connection_id`, `path` | 安全 |
|
||||
| `ssh_write_file` | 写入远程文件 | `connection_id`, `path`, `content` | ⚠️ 危险 |
|
||||
|
||||
### 配置数据结构
|
||||
|
||||
```js
|
||||
_config.sshConnections = [
|
||||
{
|
||||
id: 'my-server',
|
||||
name: '生产服务器',
|
||||
host: '192.168.1.100',
|
||||
port: 22,
|
||||
user: 'root',
|
||||
authType: 'key', // 'key' | 'password'
|
||||
keyPath: '~/.ssh/id_rsa',
|
||||
// password 不存储在 localStorage,每次询问或用 keytar 安全存储
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 后端实现
|
||||
|
||||
```
|
||||
Tauri (Rust):
|
||||
- 使用 ssh2 crate 或调用系统 ssh CLI
|
||||
- ssh_exec → Command::new("ssh").args(["-p", port, "user@host", command])
|
||||
- ssh_read_file → ssh + cat
|
||||
- ssh_write_file → 通过 stdin pipe 写入
|
||||
|
||||
dev-api.js (Web):
|
||||
- 使用 node-ssh 或 ssh2 npm 包
|
||||
- 或者直接调用 ssh CLI
|
||||
```
|
||||
|
||||
### 设置 UI:新增 tab「远程连接」
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 模型配置 │ 工具权限 │ 远程连接 │ 助手人设 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ 生产服务器 ──────────────── [编辑] [删] │
|
||||
│ │ root@192.168.1.100:22 (密钥认证) │
|
||||
│ └──────────────────────────────────────────│
|
||||
│ │
|
||||
│ ┌─ 测试服务器 ──────────────── [编辑] [删] │
|
||||
│ │ admin@10.0.0.5:22 (密码认证) │
|
||||
│ └──────────────────────────────────────────│
|
||||
│ │
|
||||
│ [+ 添加连接] │
|
||||
│ │
|
||||
│ 提示:推荐使用 SSH 密钥认证。 │
|
||||
│ 生成密钥:ssh-keygen -t ed25519 │
|
||||
│ 复制公钥:ssh-copy-id user@host │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 安全围栏
|
||||
- `ssh_exec`, `ssh_write_file` 归入 DANGEROUS_TOOLS
|
||||
- 密码认证:每次执行时用 ask_user 确认,或使用系统密钥链
|
||||
- SSH 密钥路径验证:检查文件是否存在
|
||||
- 关键命令(rm -rf, reboot 等)在远程同样走 CRITICAL_PATTERNS
|
||||
|
||||
### 系统提示词补充
|
||||
```
|
||||
## SSH 远程管理
|
||||
用户可能配置了远程服务器连接。当操作远程服务器时:
|
||||
- 先用 ask_user 确认要操作哪个连接
|
||||
- 远程命令比本地更谨慎,优先使用只读操作
|
||||
- 修改配置前先备份(cp xxx xxx.bak)
|
||||
```
|
||||
|
||||
### 内置技能卡片
|
||||
```js
|
||||
{
|
||||
id: 'remote-manage',
|
||||
icon: '🌐',
|
||||
name: '远程管理 OpenClaw',
|
||||
desc: '通过 SSH 连接远程服务器,管理 OpenClaw',
|
||||
tools: ['ssh', 'fileOps'],
|
||||
prompt: `请帮我管理远程服务器上的 OpenClaw。
|
||||
1. 获取系统信息,列出已配置的 SSH 连接
|
||||
2. 用 ask_user 让我选择要操作的服务器
|
||||
3. 用 ssh_exec 检查远程 OpenClaw 状态
|
||||
4. 检查 Gateway 进程和端口
|
||||
5. 读取远程配置和日志
|
||||
6. 汇总远程 OpenClaw 状态报告`
|
||||
}
|
||||
```
|
||||
|
||||
### 优先级:🟡 中(用户量较少但价值极高)
|
||||
### 工时估算:2-3 天
|
||||
|
||||
---
|
||||
|
||||
## 模块四:知识库 + 灵魂移植(借尸还魂 🔥)
|
||||
|
||||
### 核心理念
|
||||
|
||||
OpenClaw 的 Agent 有一套完整的**身份系统**,由工作区引导文件定义:
|
||||
|
||||
```
|
||||
~/.openclaw/workspace/ ← Agent 的"灵魂"所在
|
||||
├── AGENTS.md ← 操作指令、规则、记忆管理方式
|
||||
├── SOUL.md ← 人设、边界、语气("Who You Are")
|
||||
├── IDENTITY.md ← 名称、物种、风格、表情符号、头像
|
||||
├── USER.md ← 用户档案(名字、称呼、时区、偏好)
|
||||
├── TOOLS.md ← 工具本地笔记(SSH 配置、设备名等)
|
||||
├── HEARTBEAT.md ← 心跳任务清单
|
||||
├── MEMORY.md ← 精选长期记忆(仅主会话加载)
|
||||
└── memory/ ← 每日记忆日志
|
||||
├── 2026-03-04-1609.md
|
||||
└── ...
|
||||
|
||||
~/.openclaw/agents/<agentId>/agent/ ← Agent 的运行时状态
|
||||
├── models.json ← 模型提供商配置(baseUrl + apiKey + models)
|
||||
├── auth-profiles.json ← 认证配置文件
|
||||
└── auth.json
|
||||
```
|
||||
|
||||
**"借尸还魂"不是复用知识库,而是完整接管 Agent 的灵魂**——
|
||||
ClawPanel 的 AI 助手直接读取这些文件,像 OpenClaw 一样把它们注入 system prompt,
|
||||
从而变成那个 Agent:有他的名字、他的性格、他的记忆、他认识的用户。
|
||||
|
||||
### 4A:灵魂移植(Agent Identity Takeover)
|
||||
|
||||
#### 工作流程
|
||||
|
||||
1. **扫描** `~/.openclaw/workspace/` 和 `~/.openclaw/agents/` 目录
|
||||
2. **发现** 所有可用的 Agent 身份(main、test 等)
|
||||
3. **用户选择** 要附身的 Agent
|
||||
4. **读取** 该 Agent 的全部引导文件:
|
||||
- `SOUL.md` → 注入为人设(替换 ClawPanel 助手的默认人设)
|
||||
- `IDENTITY.md` → 提取名称/表情/风格(替换助手名称和性格描述)
|
||||
- `USER.md` → 注入用户上下文(知道用户叫什么、偏好什么)
|
||||
- `AGENTS.md` → 注入操作规则(Agent 的行为准则)
|
||||
- `TOOLS.md` → 注入工具笔记
|
||||
- `MEMORY.md` → 注入长期记忆
|
||||
- `memory/` → 注入最近的每日记忆(最近 3 天)
|
||||
5. **注入** 到 `buildSystemPrompt()` 中,完全替代默认人设
|
||||
|
||||
#### 实现
|
||||
|
||||
```js
|
||||
// 新增配置项
|
||||
_config.soulSource = null // null = 使用 ClawPanel 默认 | 'openclaw:main' | 'openclaw:test' | 'custom'
|
||||
_config.soulCache = null // 缓存读取的灵魂文件内容
|
||||
|
||||
// buildSystemPrompt 改造
|
||||
function buildSystemPrompt() {
|
||||
if (_config.soulSource?.startsWith('openclaw:')) {
|
||||
// 借尸还魂模式:使用 OpenClaw Agent 的灵魂
|
||||
return buildOpenClawSoulPrompt()
|
||||
}
|
||||
// 默认模式:使用 ClawPanel 自带的系统提示词
|
||||
return buildDefaultPrompt()
|
||||
}
|
||||
|
||||
function buildOpenClawSoulPrompt() {
|
||||
const soul = _config.soulCache
|
||||
if (!soul) return buildDefaultPrompt() // fallback
|
||||
|
||||
let prompt = ''
|
||||
|
||||
// 1. 身份注入
|
||||
if (soul.identity) {
|
||||
prompt += `# Identity\n${soul.identity}\n\n`
|
||||
}
|
||||
|
||||
// 2. 灵魂注入(人设、边界、语气)
|
||||
if (soul.soul) {
|
||||
prompt += `# Soul\n${soul.soul}\n\n`
|
||||
}
|
||||
|
||||
// 3. 用户上下文
|
||||
if (soul.user) {
|
||||
prompt += `# User\n${soul.user}\n\n`
|
||||
}
|
||||
|
||||
// 4. 操作规则
|
||||
if (soul.agents) {
|
||||
prompt += `# Operating Instructions\n${soul.agents}\n\n`
|
||||
}
|
||||
|
||||
// 5. 工具笔记
|
||||
if (soul.tools) {
|
||||
prompt += `# Tool Notes\n${soul.tools}\n\n`
|
||||
}
|
||||
|
||||
// 6. 长期记忆
|
||||
if (soul.memory) {
|
||||
prompt += `# Long-term Memory\n${soul.memory}\n\n`
|
||||
}
|
||||
|
||||
// 7. 最近的每日记忆
|
||||
if (soul.recentMemories?.length) {
|
||||
prompt += `# Recent Memory\n`
|
||||
for (const m of soul.recentMemories) {
|
||||
prompt += `## ${m.date}\n${m.content}\n\n`
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 追加 ClawPanel 特有的工具说明(保持工具能力)
|
||||
prompt += buildToolInstructions()
|
||||
|
||||
return prompt
|
||||
}
|
||||
```
|
||||
|
||||
#### 灵魂加载函数
|
||||
|
||||
```js
|
||||
async function loadOpenClawSoul(agentId = 'main') {
|
||||
const home = await getHomeDir()
|
||||
const ws = `${home}/.openclaw/workspace` // 工作区是全局的,不按 agentId 分
|
||||
|
||||
const readSafe = async (path) => {
|
||||
try { return await api.assistantReadFile(path) }
|
||||
catch { return null }
|
||||
}
|
||||
|
||||
const soul = {
|
||||
identity: await readSafe(`${ws}/IDENTITY.md`),
|
||||
soul: await readSafe(`${ws}/SOUL.md`),
|
||||
user: await readSafe(`${ws}/USER.md`),
|
||||
agents: await readSafe(`${ws}/AGENTS.md`),
|
||||
tools: await readSafe(`${ws}/TOOLS.md`),
|
||||
memory: await readSafe(`${ws}/MEMORY.md`),
|
||||
recentMemories: [],
|
||||
}
|
||||
|
||||
// 读取最近 3 天的每日记忆
|
||||
try {
|
||||
const memDir = await api.assistantListDir(`${ws}/memory`)
|
||||
const files = memDir.split('\n').filter(f => f.match(/\d{4}-\d{2}-\d{2}/))
|
||||
const recent = files.sort().slice(-3)
|
||||
for (const f of recent) {
|
||||
const content = await readSafe(`${ws}/memory/${f.trim()}`)
|
||||
if (content) soul.recentMemories.push({ date: f.trim(), content })
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return soul
|
||||
}
|
||||
```
|
||||
|
||||
#### UI:设置面板「助手人设」Tab 改造
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 模型配置 │ 工具权限 │ 知识库 │ 远程连接 │ 人设 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 身份来源 │
|
||||
│ ┌──────────────────────────────────────────│
|
||||
│ │ ● ClawPanel 默认人设 │ ← 当前默认
|
||||
│ │ ○ OpenClaw Agent 身份(借尸还魂) │ ← 新增
|
||||
│ │ ○ 自定义人设 │
|
||||
│ └──────────────────────────────────────────│
|
||||
│ │
|
||||
│ ─── 当选择「OpenClaw Agent」时显示 ──── │
|
||||
│ │
|
||||
│ 选择 Agent: [main ▼] │
|
||||
│ │
|
||||
│ 📜 灵魂文件预览 │
|
||||
│ ┌──────────────────────────────────────────│
|
||||
│ │ SOUL.md ✅ 已加载 (1.6KB) │
|
||||
│ │ IDENTITY.md ✅ 已加载 (636B) │
|
||||
│ │ USER.md ✅ 已加载 (237B) │
|
||||
│ │ AGENTS.md ✅ 已加载 (7.8KB) │
|
||||
│ │ TOOLS.md ✅ 已加载 (860B) │
|
||||
│ │ MEMORY.md ❌ 未找到 │
|
||||
│ │ memory/ 📝 2 个日志文件 │
|
||||
│ └──────────────────────────────────────────│
|
||||
│ │
|
||||
│ [👻 附身!] [🔄 刷新] │
|
||||
│ │
|
||||
│ ⚠️ 附身后,助手将使用该 Agent 的人格、 │
|
||||
│ 记忆和用户偏好。可随时切回默认。 │
|
||||
│ │
|
||||
│ ─── 当选择「ClawPanel 默认」时显示 ──── │
|
||||
│ │
|
||||
│ 助手名称: [晴辰助手 ] │
|
||||
│ 助手性格: [________________________] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 附身后的效果
|
||||
|
||||
| 维度 | 默认模式 | 附身模式 |
|
||||
|------|----------|----------|
|
||||
| 名称 | "晴辰助手" | IDENTITY.md 中的名称 |
|
||||
| 性格 | 简洁专业 | SOUL.md 定义的风格 |
|
||||
| 称呼用户 | "你" | USER.md 中的称呼(如"爸爸") |
|
||||
| 行为规则 | ClawPanel 内置 | AGENTS.md 的规则体系 |
|
||||
| 记忆 | 无 | MEMORY.md + 每日记忆 |
|
||||
| 工具知识 | ClawPanel 内置 | TOOLS.md 的本地笔记 |
|
||||
| 工具能力 | 保持不变 | 保持 ClawPanel 的工具 |
|
||||
|
||||
**关键设计**:附身只替换"灵魂"(system prompt),**工具能力保持 ClawPanel 的**。
|
||||
因为 OpenClaw 的工具(exec/read/edit/write)和 ClawPanel 的工具本质相同,
|
||||
但 ClawPanel 有独有的 docker/ssh/搜索等扩展工具,这些要保留。
|
||||
|
||||
### 4B:自定义知识库
|
||||
|
||||
在灵魂移植之外,仍然支持用户上传额外的知识文档:
|
||||
|
||||
#### 数据存储
|
||||
```
|
||||
~/.openclaw/clawpanel-kb/
|
||||
├── index.json # 知识库索引
|
||||
├── docs/
|
||||
│ ├── api-guide.md # 用户上传的文档
|
||||
│ ├── faq.md
|
||||
│ └── deploy-notes.txt
|
||||
└── chunks/ # 分块索引(可选,用于大文档)
|
||||
└── ...
|
||||
```
|
||||
|
||||
#### 实现方案
|
||||
|
||||
**V1(简单方案)**:
|
||||
- 小文档(<8KB)直接全文注入 system prompt 尾部
|
||||
- 大文档做关键词搜索(正则匹配 + 上下文窗口)
|
||||
- 总注入 token 上限:4000 tokens
|
||||
- 知识库和灵魂移植可叠加使用
|
||||
|
||||
**V2(进阶方案)**:
|
||||
- embedding 语义搜索
|
||||
- `search_knowledge` 工具让 AI 按需检索
|
||||
|
||||
### 优先级:<E7BAA7> 高(灵魂移植是杀手级差异化功能)
|
||||
### 工时估算:灵魂移植 1-2 天,自定义知识库 V1 额外 1 天
|
||||
|
||||
---
|
||||
|
||||
## 模块五:模型配置自动导入
|
||||
|
||||
### 场景
|
||||
- 用户已安装 OpenClaw 并配置了模型
|
||||
- ClawPanel AI 助手需要单独配置模型(目前手动填写)
|
||||
- 一键从 OpenClaw 配置导入,省去重复配置
|
||||
|
||||
### 实现
|
||||
|
||||
#### 数据来源(两个层级)
|
||||
|
||||
**层级 1:全局配置** `~/.openclaw/openclaw.json`
|
||||
```json
|
||||
{
|
||||
"models": {
|
||||
"providers": {
|
||||
"shengsuanyun": {
|
||||
"baseUrl": "http://127.0.0.1:8082/v1",
|
||||
"apiKey": "sk-xxx",
|
||||
"api": "openai-completions"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**层级 2:Agent 模型注册表** `~/.openclaw/agents/<agentId>/agent/models.json`
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"openai": {
|
||||
"baseUrl": "http://127.0.0.1:8082/v1",
|
||||
"apiKey": "sk-eB3ybVNFvqB4fGrTUp3F8Lq16QxF7tut",
|
||||
"api": "openai-completions",
|
||||
"models": [
|
||||
{ "id": "gpt-5.4", "name": "gpt-5.4", "contextWindow": 200000, "maxTokens": 8192 },
|
||||
{ "id": "gpt-5.2-codex", "name": "gpt-5.2-codex", ... }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**推荐优先读取 Agent 的 models.json**——它有完整的 baseUrl + apiKey + models 列表,
|
||||
一键就能填充 ClawPanel 助手的配置。
|
||||
|
||||
#### 读取逻辑
|
||||
```js
|
||||
async function discoverOpenClawModels() {
|
||||
const home = await getHomeDir()
|
||||
const results = []
|
||||
|
||||
// 1. 扫描所有 Agent 的 models.json
|
||||
try {
|
||||
const agents = await api.assistantListDir(`${home}/.openclaw/agents`)
|
||||
for (const agentId of agents.split('\n').map(s => s.trim()).filter(Boolean)) {
|
||||
try {
|
||||
const raw = await api.assistantReadFile(`${home}/.openclaw/agents/${agentId}/agent/models.json`)
|
||||
const data = JSON.parse(raw)
|
||||
for (const [providerId, provider] of Object.entries(data.providers || {})) {
|
||||
results.push({
|
||||
source: `Agent: ${agentId}`,
|
||||
providerId,
|
||||
baseUrl: provider.baseUrl,
|
||||
apiKey: provider.apiKey,
|
||||
apiType: provider.api === 'openai-completions' ? 'openai' : provider.api,
|
||||
models: (provider.models || []).map(m => m.id || m.name),
|
||||
})
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// 2. 读取全局 openclaw.json 作为补充
|
||||
try {
|
||||
const raw = await api.assistantReadFile(`${home}/.openclaw/openclaw.json`)
|
||||
const config = JSON.parse(raw)
|
||||
for (const [providerId, provider] of Object.entries(config.models?.providers || {})) {
|
||||
// 去重:如果 Agent models.json 已有相同 providerId,跳过
|
||||
if (!results.find(r => r.providerId === providerId)) {
|
||||
results.push({
|
||||
source: '全局配置',
|
||||
providerId,
|
||||
baseUrl: provider.baseUrl,
|
||||
apiKey: provider.apiKey,
|
||||
apiType: 'openai',
|
||||
models: [], // 全局配置没有 models 列表
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return results
|
||||
}
|
||||
```
|
||||
|
||||
#### UI:模型配置 tab 新增「导入」按钮
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ API Base URL API 类型 │
|
||||
│ [________________________] [OpenAI 兼容 ▼] │
|
||||
│ │
|
||||
│ API Key [测试] [拉取] [📥 导入] │ ← 新增「导入」按钮
|
||||
│ [________________________] │
|
||||
│ │
|
||||
│ 模型 温度 │
|
||||
│ [________________________] [0.7] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
点击「📥 导入」弹出选择面板:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ 从 OpenClaw 导入模型配置 │
|
||||
│ │
|
||||
│ 检测到以下已配置的服务商: │
|
||||
│ │
|
||||
│ ○ OpenAI │
|
||||
│ https://api.openai.com/v1 │
|
||||
│ 模型: gpt-4o, gpt-4o-mini │
|
||||
│ │
|
||||
│ ○ DeepSeek │
|
||||
│ https://api.deepseek.com │
|
||||
│ 模型: deepseek-chat, deepseek-reasoner │
|
||||
│ │
|
||||
│ ○ 本地 Ollama │
|
||||
│ http://127.0.0.1:11434/v1 │
|
||||
│ 模型: qwen2.5:7b │
|
||||
│ │
|
||||
│ 选择一个服务商,自动填充配置。 │
|
||||
│ [取消] [导入] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 后端
|
||||
|
||||
```
|
||||
Tauri: 已有 read_openclaw_config 命令
|
||||
dev-api.js: 已有 read_config handler
|
||||
|
||||
// 只需在前端加一个读取+解析+填充的逻辑
|
||||
```
|
||||
|
||||
### 优先级:🔴 高(零成本,纯前端,极大提升体验)
|
||||
### 工时估算:0.5 天
|
||||
|
||||
---
|
||||
|
||||
## 实施路线图
|
||||
|
||||
### Phase 1:快速见效(1-2 天)
|
||||
| 序号 | 功能 | 工时 | 理由 |
|
||||
|------|------|------|------|
|
||||
| 1 | **模型配置自动导入** | 0.5d | 读 Agent models.json → 一键填充,纯前端零风险 |
|
||||
| 2 | **联网搜索工具** | 0.5-1d | DuckDuckGo + Jina,免费无 Key |
|
||||
| 3 | **灵魂移植(借尸还魂)** | 1-2d | 杀手级差异化——读 SOUL/IDENTITY/USER/AGENTS/MEMORY → 变身 |
|
||||
|
||||
### Phase 2:核心扩展(2-3 天)
|
||||
| 序号 | 功能 | 工时 | 理由 |
|
||||
|------|------|------|------|
|
||||
| 4 | **Docker/WSL 工具** | 1-2d | 解决用户最常见的安装困惑 |
|
||||
| 5 | **自定义知识库 V1** | 1d | 用户上传 md/txt → 注入 prompt |
|
||||
|
||||
### Phase 3:高级功能(3-5 天)
|
||||
| 序号 | 功能 | 工时 | 理由 |
|
||||
|------|------|------|------|
|
||||
| 6 | **SSH 远程管理** | 2-3d | 价值最高但复杂度也最高 |
|
||||
| 7 | **知识库 V2(语义搜索)** | 3-5d | 依赖 embedding API |
|
||||
|
||||
---
|
||||
|
||||
## 设置面板 Tab 规划
|
||||
|
||||
当前 3 个 Tab → 扩展为 5 个 Tab:
|
||||
|
||||
```
|
||||
模型配置 │ 工具权限 │ 知识库 │ 远程连接 │ 助手人设
|
||||
```
|
||||
|
||||
### 工具权限 Tab 最终形态
|
||||
|
||||
```
|
||||
基础工具
|
||||
☑ 终端工具 — 允许执行 Shell 命令
|
||||
☑ 文件工具 — 允许读写文件和浏览目录
|
||||
|
||||
扩展工具
|
||||
☐ Docker/WSL — 允许管理容器和 WSL 环境
|
||||
☐ 联网搜索 — 允许搜索互联网和抓取网页
|
||||
☐ SSH 远程 — 允许连接远程服务器(需先配置连接)
|
||||
☐ 知识库 — 允许检索知识库内容
|
||||
|
||||
ℹ️ 进程列表、端口检测、系统信息工具始终可用(非聊天模式下)。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技术注意事项
|
||||
|
||||
### 1. Token 预算管理
|
||||
灵魂移植 + 知识库注入会占用 context window,需要精细管理:
|
||||
|
||||
| 组件 | 预算 | 说明 |
|
||||
|------|------|------|
|
||||
| ClawPanel 基础 prompt | ~2000 tokens | 产品介绍、工具指南、技能卡片 |
|
||||
| SOUL.md | ~500 tokens | 人设通常简短 |
|
||||
| IDENTITY.md | ~200 tokens | 名称/风格 |
|
||||
| USER.md | ~200 tokens | 用户档案 |
|
||||
| AGENTS.md | ~3000 tokens | 操作规则(最大,可截断) |
|
||||
| TOOLS.md | ~300 tokens | 工具笔记 |
|
||||
| MEMORY.md | ~2000 tokens | 长期记忆(截断保留最近部分) |
|
||||
| 每日记忆 (3天) | ~1500 tokens | 自动截断 |
|
||||
| 自定义知识库 | ~4000 tokens | 用户上传文档 |
|
||||
| 搜索结果 | ~2000 tokens | web_search 返回内容 |
|
||||
| **总计上限** | **~16000 tokens** | 留足空间给对话历史 |
|
||||
|
||||
策略:
|
||||
- AGENTS.md 超过 3000 tokens 时截断尾部,保留前面的核心规则
|
||||
- MEMORY.md 超过 2000 tokens 时只保留最后 2000 tokens
|
||||
- 每日记忆超过 500 tokens/天时截断
|
||||
- 灵魂文件加载时计算总 token 并在 UI 中显示
|
||||
|
||||
### 2. 跨平台兼容
|
||||
- Docker CLI 在 Windows/Mac/Linux 都可用
|
||||
- WSL 仅 Windows
|
||||
- SSH 密钥路径:Windows 用 `%USERPROFILE%\.ssh\`,Mac/Linux 用 `~/.ssh/`
|
||||
|
||||
### 3. 安全存储
|
||||
- SSH 密码/API Key:
|
||||
- Tauri 模式:使用 keytar 或 tauri-plugin-store 加密存储
|
||||
- Web 模式:仅支持密钥认证(不存储密码)
|
||||
- 知识库文件:存储在 `~/.openclaw/` 下,与用户数据同目录
|
||||
|
||||
### 4. 工具发现
|
||||
AI 模型需要知道哪些工具可用。当前已在 `buildSystemPrompt()` 中列出技能卡片。
|
||||
新增工具后,需要在系统提示词中补充使用指南(类似现有的 `ask_user` 指南)。
|
||||
|
||||
---
|
||||
|
||||
## 文件变更预估
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/pages/assistant.js` | TOOL_DEFS 新增 4 类 · executeTool 新增 case · getEnabledTools 新增分支 · 设置面板 UI · 模型导入弹窗 |
|
||||
| `src-tauri/src/commands/assistant.rs` | 新增 Rust 命令:docker_*, wsl_*, ssh_*, web_search, fetch_url |
|
||||
| `scripts/dev-api.js` | 新增 Web 模式 handler:同上 |
|
||||
| `src/style/assistant.css` | 知识库管理 UI · SSH 连接管理 UI · 导入弹窗样式 |
|
||||
| `src/pages/assistant.js` (prompt) | 系统提示词新增各工具使用指南 |
|
||||
@@ -1,494 +0,0 @@
|
||||
# ClawPanel Docker 多实例管理 — 技术规划
|
||||
|
||||
> 版本: v1.0 | 日期: 2026-03-08
|
||||
|
||||
## 1. 问题分析
|
||||
|
||||
### 1.1 现状
|
||||
|
||||
ClawPanel 当前架构是 **单实例管理**:
|
||||
|
||||
```
|
||||
浏览器 → ClawPanel 前端
|
||||
│
|
||||
├── /__api/* → dev-api.js → 读写本机 ~/.openclaw/ 文件
|
||||
├── /ws → 代理到本机 Gateway:18789 (WebSocket)
|
||||
└── 静态文件 → dist/
|
||||
```
|
||||
|
||||
**所有页面**(模型配置、Agent 管理、Gateway 设置、日志、聊天等)操作的都是:
|
||||
- 本机文件系统上的 `~/.openclaw/openclaw.json`
|
||||
- 本机运行的 Gateway 进程(端口 18789)
|
||||
|
||||
### 1.2 Phase 1 已完成
|
||||
|
||||
Docker 集群页面实现了 **容器生命周期管理**(通过 Docker Socket API):
|
||||
- 启动/停止/重启/删除容器
|
||||
- 部署新容器(端口映射、数据卷、环境变量)
|
||||
- 查看容器日志
|
||||
- 多节点管理(本机 + 远程 Docker 主机)
|
||||
|
||||
### 1.3 缺口
|
||||
|
||||
Docker 页面能管容器的"壳",但 **无法管理容器里的 OpenClaw**:
|
||||
- 无法配置某个容器内的模型
|
||||
- 无法查看某个容器内的 Gateway 日志
|
||||
- 无法管理某个容器内的 Agent
|
||||
- 聊天功能只连本机 Gateway
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标架构
|
||||
|
||||
### 2.1 核心思路:API 代理 + 实例切换
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ ClawPanel 前端 │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ 实例切换器: [ ● 本机 ▼ ] │ │
|
||||
│ │ [ ○ prod-server (Docker) ] │ │
|
||||
│ │ [ ○ dev-box (远程) ] │ │
|
||||
│ │ [ + 添加实例 ] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 现有页面(模型/Agent/Gateway/日志/聊天...) │
|
||||
│ │ │
|
||||
│ │ api.readOpenclawConfig() │
|
||||
│ │ api.listAgents() │
|
||||
│ ▼ │
|
||||
│ tauri-api.js → webInvoke('read_openclaw_config') │
|
||||
│ │ │
|
||||
│ 自动附带 instanceId │
|
||||
└──────────────────┼───────────────────────────────┘
|
||||
▼
|
||||
dev-api.js (本机后端)
|
||||
│
|
||||
┌────────┼────────┐
|
||||
▼ ▼ ▼
|
||||
本机文件 代理转发 代理转发
|
||||
~/.openclaw ↓ ↓
|
||||
实例 A 实例 B
|
||||
http://host http://192.168.1.100
|
||||
:18790 :1420
|
||||
/__api/* /__api/*
|
||||
```
|
||||
|
||||
**关键点:每个 Docker 容器运行 full 镜像,内含完整的 ClawPanel (serve.js) + Gateway。**
|
||||
因此每个容器已经有自己的 `/__api/*` 端点,我们只需要代理请求过去。
|
||||
|
||||
### 2.2 WebSocket 连接
|
||||
|
||||
```
|
||||
切换实例时:
|
||||
wsClient.disconnect() ← 断开旧连接
|
||||
wsClient.connect(newHost, newToken) ← 连接新实例的 Gateway
|
||||
```
|
||||
|
||||
WebSocket 连接信息从目标实例的配置中读取(通过代理 API 获取 `read_openclaw_config`)。
|
||||
|
||||
### 2.3 自动组网流程
|
||||
|
||||
部署新容器时自动完成:
|
||||
|
||||
```
|
||||
用户点击「部署容器」
|
||||
│
|
||||
├─ 1. Docker API 创建容器(端口映射 hostPort→1420, hostPort→18789)
|
||||
├─ 2. 启动容器,等待健康检查通过
|
||||
├─ 3. 探测容器 Panel 端点:GET http://hostIP:hostPort/__api/check_installation
|
||||
├─ 4. 自动写入实例注册表 ~/.openclaw/instances.json
|
||||
└─ 5. 前端自动刷新实例列表
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据结构
|
||||
|
||||
### 3.1 实例注册表
|
||||
|
||||
文件位置:`~/.openclaw/instances.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"activeId": "local",
|
||||
"instances": [
|
||||
{
|
||||
"id": "local",
|
||||
"name": "本机",
|
||||
"type": "local",
|
||||
"endpoint": null,
|
||||
"gatewayPort": 18789,
|
||||
"addedAt": 1741420800,
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"id": "docker-abc123",
|
||||
"name": "openclaw-prod",
|
||||
"type": "docker",
|
||||
"endpoint": "http://127.0.0.1:18790",
|
||||
"gatewayPort": 18789,
|
||||
"containerId": "abc123def456",
|
||||
"nodeId": "local",
|
||||
"addedAt": 1741420900,
|
||||
"note": "生产环境"
|
||||
},
|
||||
{
|
||||
"id": "remote-1",
|
||||
"name": "办公室服务器",
|
||||
"type": "remote",
|
||||
"endpoint": "http://192.168.1.100:1420",
|
||||
"gatewayPort": 18789,
|
||||
"addedAt": 1741421000,
|
||||
"note": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**三种实例类型:**
|
||||
|
||||
| type | 说明 | 来源 |
|
||||
|------|------|------|
|
||||
| `local` | 本机 OpenClaw | 始终存在,不可删除 |
|
||||
| `docker` | Docker 容器内的 OpenClaw | 部署容器时自动注册 |
|
||||
| `remote` | 远程服务器上的 OpenClaw | 用户手动添加 |
|
||||
|
||||
### 3.2 实例状态(运行时,不持久化)
|
||||
|
||||
```js
|
||||
{
|
||||
id: 'docker-abc123',
|
||||
online: true, // 健康检查结果
|
||||
version: '2026.3.5', // OpenClaw 版本
|
||||
gatewayRunning: true, // Gateway 状态
|
||||
lastCheck: 1741420999, // 上次检查时间
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 改动清单
|
||||
|
||||
### 4.1 后端 dev-api.js
|
||||
|
||||
#### 4.1.1 实例注册表管理(新增)
|
||||
|
||||
```
|
||||
新增 handlers:
|
||||
instance_list → 读取 instances.json
|
||||
instance_add → 添加实例(手动或自动)
|
||||
instance_remove → 删除实例
|
||||
instance_set_active → 切换活跃实例
|
||||
instance_health_check → 健康检查单个实例
|
||||
instance_health_all → 批量健康检查
|
||||
```
|
||||
|
||||
#### 4.1.2 API 代理转发(核心改动)
|
||||
|
||||
改造 `_apiMiddleware`:
|
||||
|
||||
```js
|
||||
// 伪代码
|
||||
async function _apiMiddleware(req, res, next) {
|
||||
if (!req.url?.startsWith('/__api/')) return next()
|
||||
|
||||
const cmd = extractCmd(req.url)
|
||||
const body = await readBody(req)
|
||||
|
||||
// 实例管理命令 → 始终本机处理
|
||||
if (cmd.startsWith('instance_') || cmd.startsWith('docker_') || ALWAYS_LOCAL.has(cmd)) {
|
||||
return handleLocally(cmd, body, res)
|
||||
}
|
||||
|
||||
// 获取当前活跃实例
|
||||
const active = getActiveInstance()
|
||||
|
||||
if (active.type === 'local') {
|
||||
// 本机 → 直接处理(现有逻辑不变)
|
||||
return handleLocally(cmd, body, res)
|
||||
}
|
||||
|
||||
// 远程/Docker 实例 → 代理转发
|
||||
return proxyToInstance(active, cmd, body, res)
|
||||
}
|
||||
```
|
||||
|
||||
**始终在本机处理的命令(ALWAYS_LOCAL):**
|
||||
- `instance_*` — 实例管理本身
|
||||
- `docker_*` — Docker 容器管理
|
||||
- `auth_*` — 认证
|
||||
- `read_panel_config` / `write_panel_config` — 本地面板配置
|
||||
- `assistant_*` — AI 助手(操作本机文件系统)
|
||||
|
||||
**通过代理转发的命令:**
|
||||
- `read_openclaw_config` / `write_openclaw_config` — 目标实例的配置
|
||||
- `get_services_status` / `start_service` / `stop_service` — 目标实例的服务
|
||||
- `list_agents` / `add_agent` / `delete_agent` — 目标实例的 Agent
|
||||
- `read_log_tail` / `search_log` — 目标实例的日志
|
||||
- `get_version_info` / `upgrade_openclaw` — 目标实例的版本
|
||||
- `list_memory_files` / `read_memory_file` — 目标实例的记忆文件
|
||||
- `read_mcp_config` / `write_mcp_config` — 目标实例的 MCP 配置
|
||||
- 等其他 OpenClaw 相关命令
|
||||
|
||||
#### 4.1.3 代理转发实现
|
||||
|
||||
```js
|
||||
async function proxyToInstance(instance, cmd, body, res) {
|
||||
const url = `${instance.endpoint}/__api/${cmd}`
|
||||
try {
|
||||
const resp = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
const data = await resp.text()
|
||||
res.writeHead(resp.status, { 'Content-Type': 'application/json' })
|
||||
res.end(data)
|
||||
} catch (e) {
|
||||
res.writeHead(502, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({ error: `实例 ${instance.name} 不可达: ${e.message}` }))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.1.4 Docker 部署自动注册
|
||||
|
||||
修改 `docker_create_container` handler:
|
||||
- 容器创建并启动后,自动等待健康检查
|
||||
- 通过 `GET http://hostIP:panelPort/__api/check_installation` 验证
|
||||
- 健康检查通过后自动写入 `instances.json`
|
||||
- 返回结果包含 `instanceId`
|
||||
|
||||
### 4.2 前端 tauri-api.js
|
||||
|
||||
#### 4.2.1 新增实例管理 API
|
||||
|
||||
```js
|
||||
// 实例管理
|
||||
instanceList: () => cachedInvoke('instance_list', {}, 10000),
|
||||
instanceAdd: (instance) => { invalidate('instance_list'); return invoke('instance_add', instance) },
|
||||
instanceRemove: (id) => { invalidate('instance_list'); return invoke('instance_remove', { id }) },
|
||||
instanceSetActive: (id) => { invalidate('instance_list'); _cache.clear(); return invoke('instance_set_active', { id }) },
|
||||
instanceHealthCheck: (id) => invoke('instance_health_check', { id }),
|
||||
instanceHealthAll: () => invoke('instance_health_all'),
|
||||
```
|
||||
|
||||
**注意 `instanceSetActive` 清空全部缓存**,因为切换实例后所有缓存数据都过期了。
|
||||
|
||||
#### 4.2.2 无需改动的部分
|
||||
|
||||
现有的 `api.readOpenclawConfig()`、`api.listAgents()` 等方法 **完全不变**。
|
||||
代理逻辑在后端 `_apiMiddleware` 层透明处理。
|
||||
|
||||
### 4.3 前端 app-state.js
|
||||
|
||||
新增:
|
||||
|
||||
```js
|
||||
let _activeInstance = { id: 'local', name: '本机', type: 'local' }
|
||||
let _instanceListeners = []
|
||||
|
||||
export function getActiveInstance() { return _activeInstance }
|
||||
export function onInstanceChange(fn) { ... }
|
||||
|
||||
export async function switchInstance(id) {
|
||||
// 1. 调后端切换
|
||||
await api.instanceSetActive(id)
|
||||
// 2. 更新本地状态
|
||||
_activeInstance = instances.find(i => i.id === id)
|
||||
// 3. 清缓存
|
||||
invalidate() // 清 API 缓存
|
||||
// 4. 断开旧 WebSocket
|
||||
wsClient.disconnect()
|
||||
// 5. 重新检测状态
|
||||
await detectOpenclawStatus()
|
||||
// 6. 连接新实例的 Gateway WebSocket
|
||||
connectToActiveGateway()
|
||||
// 7. 通知所有监听者(侧边栏、页面刷新)
|
||||
_instanceListeners.forEach(fn => fn(_activeInstance))
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 前端 sidebar.js
|
||||
|
||||
在侧边栏顶部 logo 下方添加实例切换器:
|
||||
|
||||
```html
|
||||
<div class="instance-switcher">
|
||||
<button class="instance-current" onclick="toggleDropdown()">
|
||||
<span class="instance-dot online"></span>
|
||||
<span class="instance-name">本机</span>
|
||||
<svg class="chevron">▼</svg>
|
||||
</button>
|
||||
<div class="instance-dropdown">
|
||||
<div class="instance-option active" data-id="local">
|
||||
<span class="instance-dot online"></span> 本机
|
||||
</div>
|
||||
<div class="instance-option" data-id="docker-abc123">
|
||||
<span class="instance-dot online"></span> openclaw-prod
|
||||
<span class="instance-badge">Docker</span>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="instance-option" onclick="addInstance()">
|
||||
<span>+ 添加实例</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4.5 前端 main.js
|
||||
|
||||
`autoConnectWebSocket()` 改为读取当前活跃实例的 Gateway 端点:
|
||||
|
||||
```js
|
||||
async function autoConnectWebSocket() {
|
||||
const instance = getActiveInstance()
|
||||
if (instance.type === 'local') {
|
||||
// 本机:读本地配置
|
||||
const config = await api.readOpenclawConfig()
|
||||
const port = config?.gateway?.port || 18789
|
||||
wsClient.connect(`127.0.0.1:${port}`, token)
|
||||
} else {
|
||||
// 远程/Docker:从实例 endpoint 推导 Gateway 地址
|
||||
const config = await api.readOpenclawConfig() // 已通过代理转发
|
||||
const gwPort = config?.gateway?.port || 18789
|
||||
const url = new URL(instance.endpoint)
|
||||
wsClient.connect(`${url.hostname}:${instance.gatewayPort || gwPort}`, token)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 serve.js WebSocket 代理
|
||||
|
||||
WebSocket 代理改为动态目标:
|
||||
|
||||
```js
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
// 从 query 或 header 中获取目标实例
|
||||
const target = resolveWsTarget(req)
|
||||
const conn = net.createConnection(target.port, target.host, () => { ... })
|
||||
})
|
||||
```
|
||||
|
||||
### 4.7 docker.js 集群页面
|
||||
|
||||
部署对话框增加"自动注册"逻辑:
|
||||
- 容器创建成功后显示"正在等待实例就绪..."
|
||||
- 健康检查通过后自动出现在实例切换器中
|
||||
- 用户可直接切换到新实例进行管理
|
||||
|
||||
### 4.8 现有页面适配
|
||||
|
||||
| 页面 | 改动 | 说明 |
|
||||
|------|------|------|
|
||||
| dashboard.js | 极小 | 页头显示当前实例名称 |
|
||||
| models.js | 无 | API 透明代理 |
|
||||
| agents.js | 无 | API 透明代理 |
|
||||
| gateway.js | 极小 | 远程实例时隐藏部分本机功能 |
|
||||
| logs.js | 无 | API 透明代理 |
|
||||
| chat.js | 无 | WebSocket 已切换到目标实例 |
|
||||
| chat-debug.js | 无 | API 透明代理 |
|
||||
| memory.js | 无 | API 透明代理 |
|
||||
| services.js | 小 | 已有 Docker 适配,远程实例时隐藏 npm/CLI 相关 |
|
||||
| extensions.js | 小 | 远程实例时 cftunnel/clawapp 不可用 |
|
||||
| skills.js | 无 | API 透明代理 |
|
||||
| security.js | 小 | 远程实例的密码管理走代理 |
|
||||
| setup.js | 小 | 远程实例不需要 setup 流程 |
|
||||
| assistant.js | 特殊 | AI 助手始终操作本机(ALWAYS_LOCAL) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 实施步骤
|
||||
|
||||
### Step 1: 实例注册表后端(dev-api.js)
|
||||
- `readInstances()` / `saveInstances()` 工具函数
|
||||
- 6 个 handler:`instance_list` / `add` / `remove` / `set_active` / `health_check` / `health_all`
|
||||
- 预计:~150 行
|
||||
|
||||
### Step 2: API 代理转发(dev-api.js)
|
||||
- 改造 `_apiMiddleware` 添加代理逻辑
|
||||
- `proxyToInstance()` 函数
|
||||
- `ALWAYS_LOCAL` 命令集合
|
||||
- 预计:~80 行
|
||||
|
||||
### Step 3: 前端实例管理 API(tauri-api.js)
|
||||
- 新增 `api.instance*` 方法 + mock 数据
|
||||
- 预计:~40 行
|
||||
|
||||
### Step 4: 前端状态管理(app-state.js)
|
||||
- `_activeInstance` 状态 + `switchInstance()` 函数
|
||||
- 预计:~50 行
|
||||
|
||||
### Step 5: 实例切换器 UI(sidebar.js)
|
||||
- 下拉选择器组件 + CSS
|
||||
- 预计:~100 行 JS + ~80 行 CSS
|
||||
|
||||
### Step 6: WebSocket 动态连接(main.js + serve.js)
|
||||
- 切换实例时重新连接 WebSocket
|
||||
- serve.js WebSocket 代理动态化
|
||||
- 预计:~40 行
|
||||
|
||||
### Step 7: Docker 部署自动注册(docker.js + dev-api.js)
|
||||
- `docker_create_container` 完成后自动注册
|
||||
- 健康检查 + 就绪等待
|
||||
- 预计:~60 行
|
||||
|
||||
### Step 8: 页面微调
|
||||
- dashboard 显示实例名
|
||||
- 远程实例时隐藏本机独占功能
|
||||
- 预计:~30 行
|
||||
|
||||
**总计新增代码:约 600 行**
|
||||
|
||||
---
|
||||
|
||||
## 6. 安全考虑
|
||||
|
||||
### 6.1 认证
|
||||
- 远程实例可能有不同的访问密码
|
||||
- 代理转发时需要携带目标实例的认证凭据
|
||||
- 首次连接时提示输入密码,存入 `instances.json`(加密存储待定)
|
||||
|
||||
### 6.2 网络安全
|
||||
- Docker 容器默认只暴露在宿主机网络
|
||||
- 远程实例建议通过 SSH 隧道或 VPN 连接
|
||||
- 不建议在公网暴露 `/__api/` 端点而不加密码
|
||||
|
||||
### 6.3 权限隔离
|
||||
- AI 助手(assistant_*)始终操作本机文件系统,不代理到远程
|
||||
- Docker 管理(docker_*)始终操作本机 Docker,不代理
|
||||
|
||||
---
|
||||
|
||||
## 7. 边界与约束
|
||||
|
||||
### 7.1 不做的事情
|
||||
- **不做** 统一聚合视图(如"查看所有实例的模型列表")
|
||||
- **不做** 跨实例数据同步(如"把本机模型配置复制到远程")— 后续可做
|
||||
- **不做** 实例间负载均衡
|
||||
- **不做** 复杂的权限角色系统
|
||||
|
||||
### 7.2 前提条件
|
||||
- 远程实例必须运行 ClawPanel(serve.js),版本 >= 0.7.0
|
||||
- Docker 实例使用 full 镜像(含 Panel + Gateway)
|
||||
- 网络可达(ClawPanel 后端能访问远程实例的端口)
|
||||
|
||||
### 7.3 兼容性
|
||||
- 现有单实例用户 **零影响**:默认 activeId 为 "local",行为完全不变
|
||||
- 实例切换器在只有本机时可以隐藏或最小化显示
|
||||
- 所有新功能向后兼容
|
||||
|
||||
---
|
||||
|
||||
## 8. 测试计划
|
||||
|
||||
| 场景 | 验证内容 |
|
||||
|------|---------|
|
||||
| 纯本机使用 | 现有功能不受影响,无回归 |
|
||||
| 部署 Docker 容器 | 自动注册为可管理实例 |
|
||||
| 切换到 Docker 实例 | 模型/Agent/日志等页面显示容器内数据 |
|
||||
| 切换实例后聊天 | WebSocket 连接到正确的 Gateway |
|
||||
| 远程实例离线 | 优雅报错,可切回本机 |
|
||||
| 删除 Docker 容器 | 实例列表自动移除 |
|
||||
| 多实例批量健康检查 | 侧边栏状态点实时更新 |
|
||||
@@ -1,288 +0,0 @@
|
||||
# ClawPanel i18n 国际化方案
|
||||
|
||||
> 本文档是 ClawPanel 多语言国际化的完整技术方案和实施指南。
|
||||
> 任何后续会话开始 i18n 工作时,请先阅读本文档。
|
||||
|
||||
## 一、现状评估
|
||||
|
||||
- **硬编码中文行数**:3508 行(分布在 25+ 个 JS 文件中)
|
||||
- **预估翻译字符串数**:约 1500+ 个
|
||||
- **技术栈**:纯 Vanilla JS(无 React/Vue),Tauri v2 桌面应用
|
||||
- **当前语言**:仅中文
|
||||
|
||||
### 文件中文行数分布(Top 15)
|
||||
|
||||
| 行数 | 文件 | 模块 |
|
||||
|------|------|------|
|
||||
| 838 | assistant.js | AI 助手(含内嵌知识库) |
|
||||
| 312 | docker.js | Docker 集群管理 |
|
||||
| 243 | models.js | 模型配置 |
|
||||
| 183 | chat.js | 实时聊天 |
|
||||
| 156 | chat-debug.js | 系统诊断 |
|
||||
| 148 | openclaw-kb.js | 知识库文本 |
|
||||
| 142 | setup.js | 初始安装引导 |
|
||||
| 136 | channels.js | 消息渠道 |
|
||||
| 136 | main.js | 主入口/路由/横幅 |
|
||||
| 120 | services.js | 服务管理 |
|
||||
| 105 | about.js | 关于页面 |
|
||||
| 93 | cron.js | 定时任务 |
|
||||
| 88 | dashboard.js | 仪表盘 |
|
||||
| 72 | extensions.js | 扩展工具 |
|
||||
| 68 | gateway.js | 网关配置 |
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
### 核心模块:`src/lib/i18n.js`
|
||||
|
||||
```js
|
||||
// 使用方式
|
||||
import { t, setLocale, getLocale } from '../lib/i18n.js'
|
||||
|
||||
// 简单翻译
|
||||
t('common.save') // → "保存" / "Save"
|
||||
t('common.cancel') // → "取消" / "Cancel"
|
||||
|
||||
// 带参数插值
|
||||
t('chat.messageCount', { count: 5 }) // → "5 条消息" / "5 messages"
|
||||
|
||||
// 嵌套 key
|
||||
t('dashboard.gateway.running') // → "运行中" / "Running"
|
||||
|
||||
// 切换语言
|
||||
setLocale('en') // 存 localStorage,触发页面重渲染
|
||||
```
|
||||
|
||||
### 语言检测优先级
|
||||
|
||||
1. `localStorage` 中存储的用户选择 (`clawpanel-locale`)
|
||||
2. 浏览器 `navigator.language`(`zh-CN` → `zh-CN`,`en-US` → `en`)
|
||||
3. 默认值:`zh-CN`
|
||||
|
||||
### 缺失翻译 fallback
|
||||
|
||||
1. 查找当前语言包
|
||||
2. 查找 `zh-CN` 兜底(中文作为最完整的语言)
|
||||
3. 返回 key 本身(如 `common.save`)
|
||||
4. 开发模式下 console.warn 提示缺失翻译
|
||||
|
||||
## 三、语言包结构
|
||||
|
||||
```
|
||||
src/locales/
|
||||
zh-CN.json — 中文简体(默认,最完整)
|
||||
en.json — English
|
||||
zh-TW.json — 中文繁体(未来)
|
||||
ja.json — 日本語(未来)
|
||||
ko.json — 한국어(未来)
|
||||
```
|
||||
|
||||
### JSON 格式规范
|
||||
|
||||
按模块/页面分组,使用扁平化嵌套结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"common": {
|
||||
"save": "保存",
|
||||
"cancel": "取消",
|
||||
"delete": "删除",
|
||||
"confirm": "确定",
|
||||
"close": "关闭",
|
||||
"loading": "加载中...",
|
||||
"error": "错误",
|
||||
"success": "成功",
|
||||
"warning": "警告",
|
||||
"retry": "重试",
|
||||
"refresh": "刷新",
|
||||
"edit": "编辑",
|
||||
"create": "创建",
|
||||
"back": "返回",
|
||||
"next": "下一步",
|
||||
"search": "搜索",
|
||||
"copy": "复制",
|
||||
"download": "下载",
|
||||
"upload": "上传",
|
||||
"enable": "启用",
|
||||
"disable": "禁用",
|
||||
"start": "启动",
|
||||
"stop": "停止",
|
||||
"restart": "重启",
|
||||
"status": "状态",
|
||||
"running": "运行中",
|
||||
"stopped": "已停止",
|
||||
"unknown": "未知",
|
||||
"noData": "暂无数据",
|
||||
"operationFailed": "操作失败: {error}",
|
||||
"confirmDelete": "确定删除 {name}?",
|
||||
"savedSuccessfully": "已保存"
|
||||
},
|
||||
"sidebar": {
|
||||
"dashboard": "仪表盘",
|
||||
"assistant": "晴辰助手",
|
||||
"chat": "实时聊天",
|
||||
"services": "服务管理",
|
||||
"logs": "日志查看",
|
||||
"models": "模型配置",
|
||||
"agents": "Agent 管理",
|
||||
"memory": "记忆文件",
|
||||
"channels": "消息渠道",
|
||||
"gateway": "网关配置",
|
||||
"skills": "Skills 工具",
|
||||
"docker": "Docker 集群",
|
||||
"cron": "定时任务",
|
||||
"extensions": "扩展工具",
|
||||
"about": "关于",
|
||||
"setup": "初始设置",
|
||||
"chatDebug": "系统诊断"
|
||||
},
|
||||
"dashboard": { ... },
|
||||
"chat": { ... },
|
||||
"models": { ... },
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## 四、迁移步骤(每个页面)
|
||||
|
||||
### Step 1: 提取中文字符串
|
||||
|
||||
使用正则或手动扫描,将所有中文文本提取到对应的语言包 key 下。
|
||||
|
||||
**需要翻译的内容**:
|
||||
- UI 文本(按钮文字、标题、描述、提示)
|
||||
- toast 消息
|
||||
- 错误消息
|
||||
- placeholder 文本
|
||||
- confirm 对话框文本
|
||||
- tooltip 文本
|
||||
|
||||
**不需要翻译的内容**:
|
||||
- 代码注释(保持中文)
|
||||
- console.log 调试信息
|
||||
- 技术标识符(如 `Gateway`、`Agent`、`OpenClaw`)
|
||||
- API 错误消息(后端返回的)
|
||||
- 知识库内容 `openclaw-kb.js`(这个特殊处理,按语言版本分文件)
|
||||
|
||||
### Step 2: 替换代码中的硬编码
|
||||
|
||||
```js
|
||||
// Before
|
||||
toast('保存成功', 'success')
|
||||
|
||||
// After
|
||||
toast(t('common.savedSuccessfully'), 'success')
|
||||
```
|
||||
|
||||
```js
|
||||
// Before (HTML 模板)
|
||||
`<button class="btn">${icon('save', 14)} 保存</button>`
|
||||
|
||||
// After
|
||||
`<button class="btn">${icon('save', 14)} ${t('common.save')}</button>`
|
||||
```
|
||||
|
||||
### Step 3: 编写英文翻译
|
||||
|
||||
逐 key 翻译到 `en.json`。
|
||||
|
||||
### Step 4: 测试
|
||||
|
||||
切换语言,检查每个页面的显示是否正常。
|
||||
|
||||
## 五、迁移顺序
|
||||
|
||||
### 第一批(基础 + 框架层,约 80 个字符串)
|
||||
1. `src/lib/i18n.js` — 创建核心模块
|
||||
2. `src/locales/zh-CN.json` — 初始化中文包
|
||||
3. `src/locales/en.json` — 初始化英文包
|
||||
4. `src/components/sidebar.js` — 导航菜单(~20 个)
|
||||
5. `src/components/modal.js` — 公共弹窗(~10 个)
|
||||
6. `src/components/toast.js` — 提示组件
|
||||
7. `src/pages/about.js` — 关于页面 + 语言切换 UI(~30 个)
|
||||
|
||||
### 第二批(核心页面,约 250 个字符串)
|
||||
8. `src/pages/dashboard.js` — 仪表盘(~50 个)
|
||||
9. `src/pages/setup.js` — 初始设置(~80 个)
|
||||
10. `src/pages/chat.js` — 实时聊天(~100 个)
|
||||
11. `src/main.js` — 主入口/横幅(~20 个)
|
||||
|
||||
### 第三批(配置页面,约 350 个字符串)
|
||||
12. `src/pages/models.js` — 模型配置(~120 个)
|
||||
13. `src/pages/channels.js` — 消息渠道(~80 个)
|
||||
14. `src/pages/services.js` — 服务管理(~70 个)
|
||||
15. `src/pages/gateway.js` — 网关配置(~40 个)
|
||||
16. `src/pages/agents.js` — Agent 管理(~40 个)
|
||||
|
||||
### 第四批(功能页面,约 250 个字符串)
|
||||
17. `src/pages/cron.js` — 定时任务(~50 个)
|
||||
18. `src/pages/memory.js` — 记忆管理(~30 个)
|
||||
19. `src/pages/extensions.js` — 扩展工具(~40 个)
|
||||
20. `src/pages/logs.js` — 日志查看(~20 个)
|
||||
21. `src/pages/skills.js` — Skills 工具(~60 个)
|
||||
22. `src/pages/chat-debug.js` — 系统诊断(~50 个)
|
||||
|
||||
### 第五批(大型页面 + 特殊处理,约 600 个字符串)
|
||||
23. `src/pages/docker.js` — Docker 管理(~150 个)
|
||||
24. `src/pages/assistant.js` — AI 助手(~400 个,含系统提示词)
|
||||
25. `src/lib/openclaw-kb.js` — 知识库(按语言分文件)
|
||||
26. `src/lib/error-diagnosis.js` — 错误诊断(~30 个)
|
||||
27. `src/components/engagement.js` — 推荐弹窗(~15 个)
|
||||
|
||||
### 第六批(官网 + 文档)
|
||||
28. `docs/index.html` — 官网英文版
|
||||
29. `README.md` → `README_en.md`
|
||||
30. `CONTRIBUTING.md` → `CONTRIBUTING_en.md`
|
||||
|
||||
## 六、语言切换 UI 设计
|
||||
|
||||
### 位置
|
||||
1. **关于页面底部** — 语言选择下拉框
|
||||
2. **侧边栏底部** — 语言图标 + 当前语言缩写(如 `中` / `EN`)
|
||||
|
||||
### 交互
|
||||
- 选择语言 → 存入 localStorage → 页面自动刷新
|
||||
- 首次访问自动检测浏览器语言
|
||||
|
||||
## 七、注意事项
|
||||
|
||||
### 技术品牌词不翻译
|
||||
以下词保持原样,不翻译:
|
||||
- `OpenClaw`
|
||||
- `ClawPanel`
|
||||
- `Gateway`
|
||||
- `Agent`(Agent 管理不翻译为"代理")
|
||||
- `MCP`
|
||||
- `Skills`
|
||||
- `Docker`
|
||||
- `Tauri`
|
||||
|
||||
### 参数插值语法
|
||||
使用 `{param}` 语法:
|
||||
```json
|
||||
{
|
||||
"chat.sessions": "{count} sessions",
|
||||
"models.providers": "Based on {count} providers"
|
||||
}
|
||||
```
|
||||
|
||||
### 复数形式
|
||||
英文需要处理复数,但 MVP 阶段可以用简单方式:
|
||||
```json
|
||||
{
|
||||
"chat.messageCount": "{count} message(s)"
|
||||
}
|
||||
```
|
||||
|
||||
### Rust 后端
|
||||
后端错误消息暂不国际化(工作量大且用户较少直接看到),保持中文。
|
||||
|
||||
## 八、验证清单
|
||||
|
||||
每批迁移完成后检查:
|
||||
- [ ] 中文模式下所有功能正常
|
||||
- [ ] 英文模式下所有功能正常
|
||||
- [ ] 语言切换后页面正确刷新
|
||||
- [ ] 没有遗漏的硬编码中文
|
||||
- [ ] 参数插值正确显示
|
||||
- [ ] 长英文文本不溢出布局
|
||||
- [ ] toast/modal/confirm 文本正确
|
||||
@@ -34,7 +34,7 @@
|
||||
"description": "OpenClaw AI Agent 可视化管理面板,基于 Tauri v2 的跨平台桌面应用。支持仪表盘监控、多模型配置、消息渠道管理、内置 QQ 机器人、实时 AI 聊天、记忆管理、Agent 管理、网关配置、内网穿透等功能。",
|
||||
"url": "https://claw.qt.cool/",
|
||||
"downloadUrl": "https://github.com/qingchencloud/clawpanel/releases/latest",
|
||||
"softwareVersion": "0.9.7",
|
||||
"softwareVersion": "0.9.8",
|
||||
"author": {
|
||||
"@type": "Organization",
|
||||
"name": "晴辰云 QingchenCloud",
|
||||
@@ -1133,7 +1133,7 @@
|
||||
<div class="orb orb-2" style="top:auto;bottom:-100px"></div>
|
||||
<div class="container-sm" style="position:relative;z-index:10">
|
||||
<div class="section-header">
|
||||
<div class="reveal download-version"><span class="pulse"></span> v0.9.7 最新版</div>
|
||||
<div class="reveal download-version"><span class="pulse"></span> v0.9.8 最新版</div>
|
||||
<h2 class="reveal section-title"><span class="gradient-text">下载安装</span></h2>
|
||||
<p class="reveal section-desc">选择你的操作系统,一键下载安装</p>
|
||||
</div>
|
||||
@@ -1143,11 +1143,11 @@
|
||||
<h3>macOS</h3>
|
||||
<p class="dl-desc">支持 Apple Silicon 和 Intel 芯片</p>
|
||||
<div class="dl-links">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_aarch64.dmg" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_aarch64.dmg" target="_blank" rel="noopener">
|
||||
Apple Silicon (M1/M2/M3/M4)
|
||||
<span class="dl-format">.dmg</span>
|
||||
</a>
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_x64.dmg" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_x64.dmg" target="_blank" rel="noopener">
|
||||
Intel 芯片
|
||||
<span class="dl-format">.dmg</span>
|
||||
</a>
|
||||
@@ -1165,11 +1165,11 @@
|
||||
<h3>Windows</h3>
|
||||
<p class="dl-desc">支持 Windows 10 及以上版本</p>
|
||||
<div class="dl-links">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_x64-setup.exe" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_x64-setup.exe" target="_blank" rel="noopener">
|
||||
安装程序
|
||||
<span class="dl-format">.exe</span>
|
||||
</a>
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_x64_en-US.msi" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_x64_en-US.msi" target="_blank" rel="noopener">
|
||||
MSI 安装包
|
||||
<span class="dl-format">.msi</span>
|
||||
</a>
|
||||
@@ -1180,11 +1180,11 @@
|
||||
<h3>Linux</h3>
|
||||
<p class="dl-desc">支持主流 Linux 发行版</p>
|
||||
<div class="dl-links">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_amd64.AppImage" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_amd64.AppImage" target="_blank" rel="noopener">
|
||||
通用版
|
||||
<span class="dl-format">.AppImage</span>
|
||||
</a>
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.7_amd64.deb" target="_blank" rel="noopener">
|
||||
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.9.8_amd64.deb" target="_blank" rel="noopener">
|
||||
Debian / Ubuntu
|
||||
<span class="dl-format">.deb</span>
|
||||
</a>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"version": "0.9.7",
|
||||
"minAppVersion": "0.9.7",
|
||||
"hash": "sha256:e853ece18ca4f885b8e770601a4b38f553f1c36f2f4469e071c36395b33cdeca",
|
||||
"url": "https://github.com/qingchencloud/clawpanel/releases/download/v0.9.7/web-0.9.7.zip",
|
||||
"size": 1999195,
|
||||
"version": "0.9.8",
|
||||
"minAppVersion": "0.9.0",
|
||||
"hash": "",
|
||||
"url": "https://github.com/qingchencloud/clawpanel/releases/download/v0.9.8/web-0.9.8.zip",
|
||||
"size": 0,
|
||||
"changelog": "",
|
||||
"releasedAt": "2026-03-20T20:20:47Z"
|
||||
"releasedAt": "2026-03-23T00:00:00Z"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user