feat: ClawPal v0.1 — Tauri desktop GUI for OpenClaw
4-page layout (Home, Recipes, Settings, Doctor) with sidebar nav and integrated Chat panel powered by OpenClaw agent (--local). - Home: status, agents overview, recommended recipes, recent activity - Recipes: browse, preview diff, apply with params - Settings: model profiles CRUD, chat model selection, provider catalog - Doctor: diagnostics with auto-fix - Chat: OpenClaw agent integration with session persistence, agent selector, read-only advisory context injection - Progressive data loading to avoid UI blocking - API key resolution from OpenClaw agent auth-profiles - Model catalog from openclaw CLI with cache Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
docs/mvp-checklist.md
Normal file
47
docs/mvp-checklist.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ClawPal MVP 验收清单
|
||||
|
||||
## 1. 安装向导
|
||||
|
||||
- [x] 打开 Recipes 列表
|
||||
- [x] 选择一个 Recipe
|
||||
- [x] 参数校验阻止非法输入
|
||||
- [x] 点击 Preview 显示变更
|
||||
- [x] 点击 Apply 成功写入并生成历史快照
|
||||
|
||||
## 2. 历史与回滚
|
||||
|
||||
- [x] 历史列表可见最近记录
|
||||
- [x] 选中历史项可预览回滚 diff
|
||||
- [x] 执行回滚后回填新快照(用于再次回滚)
|
||||
- [x] 回滚后配置文件发生可见变化
|
||||
|
||||
## 3. Doctor
|
||||
|
||||
- [x] 运行 Doctor 返回至少一项问题(如有)
|
||||
- [x] 对语法/字段问题展示修复建议
|
||||
- [x] auto-fix 的问题可点击 fix,状态刷新
|
||||
- [x] 关键问题导致状态 score 下降
|
||||
|
||||
## 4. 可交付性
|
||||
|
||||
- [x] 无需网络也能完成核心流程
|
||||
- [x] 目录存在于 `~/.openclaw`,历史文件落在 `.clawpal/history`
|
||||
- [x] `npm run build` 成功
|
||||
- [ ] `npm run release:dry-run` 输出通过项(无需执行发布)
|
||||
|
||||
## 5. 模型与频道管理(v0.2)
|
||||
|
||||
- [x] 模型 Profile 支持列表、创建、更新、删除
|
||||
- [x] 全局模型绑定可设置与清空
|
||||
- [x] Agent 模型覆盖可设置与清空
|
||||
- [x] Channel 模型绑定可设置与清空
|
||||
- [x] Channel 节点可更新 `type/mode/allowlist/model`
|
||||
- [x] Channel 节点可安全删除
|
||||
- [x] Recipes 支持外部文件/URL 源加载
|
||||
|
||||
## 6. Memory 与 Session 管理(v0.2)
|
||||
|
||||
- [x] Memory 文件列表可见
|
||||
- [x] Memory 单文件删除与清空可用
|
||||
- [x] Session 文件列表可见(active + archive)
|
||||
- [x] Session 单文件删除与按 agent/全部清空可用
|
||||
279
docs/plans/2026-02-15-clawpal-mvp-design.md
Normal file
279
docs/plans/2026-02-15-clawpal-mvp-design.md
Normal file
@@ -0,0 +1,279 @@
|
||||
# ClawPal MVP 设计文档(实现版)
|
||||
|
||||
日期:2026-02-15
|
||||
版本:MVP-1.0
|
||||
目标:用最小投入实现可用产品,覆盖 `design.md` 中 MVP 核心范围(安装向导、快照与回滚、配置诊断)。
|
||||
|
||||
## 1. 范围边界
|
||||
|
||||
### 1.1 本版实现范围(MVP)
|
||||
|
||||
- 安装向导
|
||||
- Recipe 列表(内置静态 Recipes)
|
||||
- 参数 schema 校验(必填、类型、pattern)
|
||||
- 预览变更(diff)
|
||||
- 应用配置(含自动备份)
|
||||
- 版本控制与回滚
|
||||
- 每次写入前自动快照
|
||||
- 历史记录列表
|
||||
- 选中历史版本回滚(回滚前显示 diff)
|
||||
- 回滚过程二次确认
|
||||
- 配置诊断(Doctor)
|
||||
- JSON 解析
|
||||
- 必填字段检查
|
||||
- 端口占用(仅基础检查)
|
||||
- 文件权限检查(读写)
|
||||
- 一键修复建议(语法修复、默认值补齐)
|
||||
|
||||
### 1.2 明确不做
|
||||
|
||||
- 官网、用户系统、在线提交与评论
|
||||
- 复杂的配置兼容迁移(v1/v2)
|
||||
- 深度路径白名单策略(保留可配置白名单雏形)
|
||||
- 插件化/市场化的远程 Recipe 发布
|
||||
|
||||
## 2. 目标架构(Tauri 2 + React)
|
||||
|
||||
### 2.1 分层
|
||||
|
||||
- `src-tauri/src`
|
||||
- `main.rs`:初始化、窗口配置、命令注册
|
||||
- `commands.rs`:所有跨端调用入口
|
||||
- `recipe.rs`:Recipe 定义、参数校验、模板渲染
|
||||
- `config_io.rs`:配置读写(路径发现、json5 解析、备份文件读写)
|
||||
- `history.rs`:快照目录与元数据管理
|
||||
- `doctor.rs`:诊断与修复策略
|
||||
- `src/`
|
||||
- `pages/`
|
||||
- `Home.tsx`:健康状态、版本、按钮入口
|
||||
- `Recipes.tsx`:卡片列表与搜索过滤
|
||||
- `Install.tsx`:安装向导
|
||||
- `History.tsx`:历史快照
|
||||
- `Doctor.tsx`:问题列表与修复动作
|
||||
- `components/`
|
||||
- `RecipeCard.tsx`
|
||||
- `ParamForm.tsx`
|
||||
- `DiffViewer.tsx`
|
||||
- `StatusPill.tsx`
|
||||
- `lib/`
|
||||
- `recipe_catalog.ts`:内置 recipes(内嵌 JSON 或 TS 常量)
|
||||
- `api.ts`:Tauri command 调用封装
|
||||
- `state.ts`:`Context + useReducer` 统一状态与副作用
|
||||
|
||||
### 2.2 目录与文件
|
||||
|
||||
```
|
||||
~/.openclaw/openclaw.json # 当前配置
|
||||
~/.openclaw/.clawpal/ # 本地状态目录
|
||||
~/.openclaw/.clawpal/history/
|
||||
~/.openclaw/.clawpal/metadata.json # 快照元信息(列表可直接读)
|
||||
```
|
||||
|
||||
## 3. Recipe 与配置模型
|
||||
|
||||
### 3.1 Recipe 核心结构
|
||||
|
||||
```ts
|
||||
interface Recipe {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
tags: string[];
|
||||
difficulty: 'easy' | 'normal' | 'advanced';
|
||||
params: RecipeParam[];
|
||||
patchTemplate: string; // JSON Merge Patch with {{param}}
|
||||
impact: {
|
||||
category: 'low' | 'medium' | 'high';
|
||||
summary: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface RecipeParam {
|
||||
id: string;
|
||||
label: string;
|
||||
type: 'string' | 'number' | 'boolean' | 'textarea';
|
||||
required: boolean;
|
||||
pattern?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
placeholder?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 预览与应用结果
|
||||
|
||||
```ts
|
||||
interface PreviewResult {
|
||||
recipeId: string;
|
||||
diff: string;
|
||||
changes: ChangeItem[];
|
||||
overwritesExisting: boolean;
|
||||
canRollback: boolean; // true = 已生成快照
|
||||
impactLevel: 'low' | 'medium' | 'high';
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
interface ChangeItem {
|
||||
path: string;
|
||||
op: 'add' | 'replace' | 'remove';
|
||||
risk: 'low' | 'medium' | 'high';
|
||||
reason?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Tauri Command 设计
|
||||
|
||||
### 4.1 命令边界
|
||||
|
||||
- `get_system_status(): SystemStatus`
|
||||
- `list_recipes(): Recipe[]`
|
||||
- `preview_apply(recipe_id: string, params: Record<string, string>): PreviewResult`
|
||||
- `apply_recipe(recipe_id: string, params: Record<string, string>): ApplyResult`
|
||||
- `list_history(limit: number, offset: number): HistoryPage`
|
||||
- `preview_rollback(snapshot_id: string): PreviewResult`
|
||||
- `rollback(snapshot_id: string): ApplyResult`
|
||||
- `run_doctor(): DoctorReport`
|
||||
- `fix_issues(issue_ids: string[]): FixResult`
|
||||
- `open_config_path(): string`
|
||||
|
||||
### 4.2 关键返回结构
|
||||
|
||||
```ts
|
||||
interface ApplyResult {
|
||||
ok: boolean;
|
||||
snapshotId?: string; // 回滚锚点
|
||||
configPath: string;
|
||||
backupPath?: string;
|
||||
warnings: string[];
|
||||
errors?: string[];
|
||||
}
|
||||
|
||||
interface DoctorReport {
|
||||
ok: boolean;
|
||||
issues: DoctorIssue[];
|
||||
score: number; // 0-100 健康值
|
||||
}
|
||||
|
||||
interface DoctorIssue {
|
||||
id: string;
|
||||
code: string;
|
||||
severity: 'error' | 'warn' | 'info';
|
||||
message: string;
|
||||
autoFixable: boolean;
|
||||
fixHint?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 核心逻辑算法
|
||||
|
||||
### 5.1 参数校验
|
||||
|
||||
1. Recipe 找不到 -> 错误
|
||||
2. 参数逐个校验:
|
||||
- 必填
|
||||
- 类型
|
||||
- pattern(正则)
|
||||
- 长度
|
||||
3. 校验通过后进入渲染
|
||||
|
||||
### 5.2 参数渲染与差异生成
|
||||
|
||||
1. 用 `{{param_id}}` 进行文本替换
|
||||
2. 解析 base 配置与 patch 模板(json5)
|
||||
3. 进行深合并(merge patch)
|
||||
4. Diff 生成:
|
||||
- 将当前配置与待应用配置序列化为 pretty JSON
|
||||
- 输出 unified diff 或关键节点差异
|
||||
|
||||
### 5.3 应用流程(Write path)
|
||||
|
||||
1. 调用 preview
|
||||
2. 创建快照:
|
||||
- 将当前 `openclaw.json` 复制到 `.clawpal/history/<ts>_<slug>.json`
|
||||
- 更新 `metadata.json`(按时间倒序)
|
||||
3. 原子写新配置:
|
||||
- 临时文件写入 -> rename 覆盖
|
||||
4. 失败回滚:
|
||||
- 临时文件清理
|
||||
- 保留快照,但不上报成功
|
||||
|
||||
### 5.4 回滚流程
|
||||
|
||||
1. 读取目标快照
|
||||
2. 计算与当前配置 diff
|
||||
3. 确认后再执行快照(当前入历史)
|
||||
4. 用目标快照替换当前配置
|
||||
|
||||
### 5.5 Doctor 与修复
|
||||
|
||||
- 语法错误:尝试修复尾逗号、未闭合引号等常见问题
|
||||
- 关键字段缺失:按最小安全默认值补齐(仅在用户确认后)
|
||||
- 端口占用:读取端口字段并做最小冲突提示(非阻塞 warning)
|
||||
- 权限问题:展示“文件不可读/不可写+路径来源”建议
|
||||
|
||||
## 6. 安全与约束(MVP 简化)
|
||||
|
||||
- 禁止修改路径初版:
|
||||
- `gateway.auth.*`
|
||||
- `*.token`
|
||||
- `*.apiKey`
|
||||
- `dangerous` 字段提示:
|
||||
- `gateway.port` 修改前增加二次确认
|
||||
- Deep Link 与远程 Recipe 不支持(MVP 不接入官网)
|
||||
- 所有写操作必须先写快照,`apply_result.snapshotId` 作为审计锚点
|
||||
|
||||
## 7. 兼容与平台策略
|
||||
|
||||
- 路径优先级:
|
||||
1. `~/.openclaw/.clawpal`(首次初始化)
|
||||
2. 现有配置路径检测(若 `.openclaw` 已存在则复用)
|
||||
3. WSL2 仅作“只读检测入口”(MVP 只读展示,不做深度自动映射)
|
||||
- Windows/macOS/Linux 使用 Tauri 提供的跨平台路径 API
|
||||
|
||||
## 8. 内置 Recipe(MVP)
|
||||
|
||||
1. Discord 频道专属人设
|
||||
2. Telegram 群组配置
|
||||
3. 定时任务配置
|
||||
4. 模型切换
|
||||
5. 性能优化
|
||||
|
||||
每个 Recipe 均含:
|
||||
- id/name/version/params/patchTemplate
|
||||
- default 示例值与最小校验规则
|
||||
- impact 元信息(影响等级)
|
||||
|
||||
## 9. 里程碑与验收
|
||||
|
||||
### Milestone A(第 1 阶段)
|
||||
- 项目脚手架搭建
|
||||
- Recipe 解析与参数校验
|
||||
- 安装向导基础路径可跑通
|
||||
- 最小 Diff 预览
|
||||
- 验收:选 1 个 Recipe 完整完成 “填参数->预览->应用”
|
||||
|
||||
### Milestone B(第 2 阶段)
|
||||
- 快照元数据与历史列表
|
||||
- 回滚预览与回滚执行
|
||||
- 验收:应用后 1 步回滚成功且 current 状态可恢复
|
||||
|
||||
### Milestone C(第 3 阶段)
|
||||
- Doctor 基础扫描与修复入口
|
||||
- 端口/权限/语法检查
|
||||
- 验收:制造 1 处语法错误,修复后能读取启动
|
||||
|
||||
## 10. 关键风险与回退方案
|
||||
|
||||
- JSON5 风格保持未完全解决:MVP 采用“标准化写回”,后续引入更精细 AST 修改
|
||||
- 端口占用检测可能误报:提示“仅警告”不阻断
|
||||
- Recipe 深入语义冲突:通过 change list 显示冲突路径并要求确认
|
||||
- 若配置文件损坏到不可修复:保留备份、提示手工恢复和重建路径
|
||||
|
||||
## 11. 交付标准
|
||||
|
||||
- 所有命令返回可序列化错误码,不出现裸异常弹窗
|
||||
- 每次 apply 成功都生成可回滚快照(除极端写入错误)
|
||||
- 历史列表支持 `时间/来源/描述` 查看
|
||||
- Doctor 能给出至少一条可执行修复动作
|
||||
- 无法修复时给出建议与重试按钮
|
||||
403
docs/plans/2026-02-15-clawpal-mvp-implementation-plan.md
Normal file
403
docs/plans/2026-02-15-clawpal-mvp-implementation-plan.md
Normal file
@@ -0,0 +1,403 @@
|
||||
# ClawPal MVP Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Deliver a working Tauri MVP for ClawPal that implements install wizard, snapshot/rollback, and doctor repair flows with data-safe write operations.
|
||||
|
||||
**Architecture:** A thin React UI orchestrates a Rust-backed command API in Tauri; all configuration reads/writes, preview/rollback logic, and diagnosis checks are implemented in Rust for deterministic behavior and simpler cross-platform filesystem handling.
|
||||
|
||||
**Tech Stack:** Tauri 2, Rust, React 18, TypeScript, json5, monaco-editor (diff), Vitest, Cargo test
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 初始化项目骨架与基础命令骨架
|
||||
|
||||
**Files:**
|
||||
- Create: `package.json`
|
||||
- Create: `src-tauri/Cargo.toml`
|
||||
- Create: `src-tauri/src/main.rs`
|
||||
- Create: `src-tauri/src/commands.rs`
|
||||
- Create: `src-tauri/src/lib.rs`
|
||||
- Create: `src-tauri/src/commands_tests.rs`
|
||||
- Create: `src/main.tsx`
|
||||
- Create: `src/App.tsx`
|
||||
- Create: `src-tauri/tauri.conf.json`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Create a Rust compile smoke test for command registration entry.
|
||||
|
||||
```rust
|
||||
// src-tauri/src/commands_tests.rs
|
||||
#[test]
|
||||
fn test_register_commands_compiles() {
|
||||
assert!(true);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cd /Users/zhixian/Codes/clawpal/src-tauri && cargo test --test commands_tests -- --nocapture`
|
||||
Expected: FAIL with missing crate/modules (project尚未初始化).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Initialize a valid Tauri app with `invoke_handler` placeholder.
|
||||
- Register placeholder command stubs returning `Ok(())`.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cd /Users/zhixian/Codes/clawpal/src-tauri && cargo test --test commands_tests -- --nocapture`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
cd /Users/zhixian/Codes/clawpal
|
||||
git add package.json src-tauri/Cargo.toml src-tauri/src/main.rs src-tauri/src/lib.rs src-tauri/src/commands.rs src-tauri/src/commands_tests.rs src-tauri/tauri.conf.json src/main.tsx src/App.tsx
|
||||
git commit -m "chore: scaffold Tauri + React MVP project skeleton"
|
||||
```
|
||||
|
||||
### Task 2: 建立类型与路径模型
|
||||
|
||||
**Files:**
|
||||
- Create: `src-tauri/src/models.rs`
|
||||
- Create: `src-tauri/src/config_io.rs`
|
||||
- Modify: `src-tauri/src/lib.rs`
|
||||
- Create: `src-tauri/src/models_tests.rs`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_openclaw_paths_are_resolved() {
|
||||
let paths = resolve_openclaw_paths();
|
||||
assert!(paths.config_path.to_string_lossy().contains(".openclaw"));
|
||||
assert!(paths.history_dir.ends_with(".clawpal/history"));
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cargo test models_tests::test_openclaw_paths_are_resolved`
|
||||
Expected: FAIL (path resolver未实现 / test compile fail).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Define `OpenClawPaths { config_path, history_dir, metadata_path }`.
|
||||
- Implement path resolution from home directory via `dirs` crate and create dirs if missing (non-destructive).
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test models_tests::test_openclaw_paths_are_resolved`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src-tauri/src/models.rs src-tauri/src/config_io.rs src-tauri/src/models_tests.rs src-tauri/src/lib.rs
|
||||
git commit -m "feat: define core path model for openclaw config and history"
|
||||
```
|
||||
|
||||
### Task 3: 实现 Recipe 类型与参数校验
|
||||
|
||||
**Files:**
|
||||
- Create: `src-tauri/src/recipe.rs`
|
||||
- Modify: `src-tauri/src/commands.rs`
|
||||
- Create: `src/lib/recipe_catalog.ts`
|
||||
- Create: `src-tauri/src/recipe_tests.rs`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_validate_recipe_params_missing_required() {
|
||||
let recipe = sample_recipe();
|
||||
let params = serde_json::json!({});
|
||||
assert!(validate_recipe_params(&recipe, ¶ms).is_err());
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cargo test recipe_tests::test_validate_recipe_params_missing_required`
|
||||
Expected: FAIL (validate function未实现).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Implement `Recipe`, `RecipeParam`, `RecipeEngine::validate`.
|
||||
- Parse params from static TS catalog entry and pass through Tauri command `list_recipes`.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test recipe_tests::test_validate_recipe_params_missing_required`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src-tauri/src/recipe.rs src-tauri/src/recipe_tests.rs src-tauri/src/commands.rs src/lib/recipe_catalog.ts
|
||||
git commit -m "feat: define recipe schema and parameter validation"
|
||||
```
|
||||
|
||||
### Task 4: 实现预览与应用流程(含备份)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src-tauri/src/recipe.rs`
|
||||
- Modify: `src-tauri/src/config_io.rs`
|
||||
- Create: `src-tauri/src/history.rs`
|
||||
- Modify: `src-tauri/src/commands.rs`
|
||||
- Create: `src-tauri/src/recipe_flow_tests.rs`
|
||||
- Modify: `src/lib/api.ts`
|
||||
- Create: `src/lib/types.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_apply_writes_backup_before_modify() {
|
||||
// Arrange temp openclaw path & in-memory json
|
||||
// Assert backup exists after apply with different content hash
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cargo test recipe_flow_tests::test_apply_writes_backup_before_modify`
|
||||
Expected: FAIL (apply + history not implemented).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Implement preview by rendering template + merge patch to candidate JSON.
|
||||
- On apply: read current config, create snapshot copy, write temporary file then atomically replace target.
|
||||
- Emit `ApplyResult` with `snapshot_id` and backup path.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test recipe_flow_tests::test_apply_writes_backup_before_modify`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src-tauri/src/recipe.rs src-tauri/src/config_io.rs src-tauri/src/history.rs src-tauri/src/commands.rs src-tauri/src/recipe_flow_tests.rs src/lib/api.ts src/lib/types.ts
|
||||
git commit -m "feat: implement recipe preview/apply with backup snapshot"
|
||||
```
|
||||
|
||||
### Task 5: 实现历史列表和回滚命令
|
||||
|
||||
**Files:**
|
||||
- Modify: `src-tauri/src/history.rs`
|
||||
- Modify: `src-tauri/src/commands.rs`
|
||||
- Create: `src-tauri/src/history_tests.rs`
|
||||
- Create: `src/pages/History.tsx`
|
||||
- Modify: `src/App.tsx`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_history_snapshot_roundtrip_rollback() {
|
||||
// create fake metadata + two snapshots
|
||||
// restore earlier snapshot and verify current differs then matches target
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cargo test history_tests::test_history_snapshot_roundtrip_rollback`
|
||||
Expected: FAIL (history index and rollback 未实现).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Implement metadata read/write format with monotonically sorted IDs.
|
||||
- Implement `list_history`, `preview_rollback`, and `rollback`.
|
||||
- Add history UI with “查看 diff / 回滚” actions.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test history_tests::test_history_snapshot_roundtrip_rollback`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src-tauri/src/history.rs src-tauri/src/commands.rs src-tauri/src/history_tests.rs src/pages/History.tsx src/App.tsx
|
||||
git commit -m "feat: add snapshot history list and rollback flow"
|
||||
```
|
||||
|
||||
### Task 6: 实现 Doctor 检查与修复
|
||||
|
||||
**Files:**
|
||||
- Create: `src-tauri/src/doctor.rs`
|
||||
- Modify: `src-tauri/src/commands.rs`
|
||||
- Create: `src-tauri/src/doctor_tests.rs`
|
||||
- Create: `src/pages/Doctor.tsx`
|
||||
- Modify: `src/lib/api.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn test_doctor_catches_invalid_json_syntax() {
|
||||
// Provide broken json5 content
|
||||
// assert issue with code = json.syntax
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `cargo test doctor_tests::test_doctor_catches_invalid_json_syntax`
|
||||
Expected: FAIL (doctor module未实现).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Check parse validity, mandatory top-level fields, permission/ownership basics.
|
||||
- Implement one-shot fix functions for trailing comma + missing required field default.
|
||||
- Return actionable `DoctorReport` from command.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `cargo test doctor_tests::test_doctor_catches_invalid_json_syntax`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src-tauri/src/doctor.rs src-tauri/src/commands.rs src-tauri/src/doctor_tests.rs src/pages/Doctor.tsx src/lib/api.ts
|
||||
git commit -m "feat: add doctor diagnostics and safe auto-fix hooks"
|
||||
```
|
||||
|
||||
### Task 7: 前端安装向导与 Diff 页面
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/pages/Recipes.tsx`
|
||||
- Create: `src/pages/Install.tsx`
|
||||
- Create: `src/components/RecipeCard.tsx`
|
||||
- Create: `src/components/ParamForm.tsx`
|
||||
- Create: `src/components/DiffViewer.tsx`
|
||||
- Modify: `src/lib/api.ts`
|
||||
- Create: `src/pages/Home.tsx`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```ts
|
||||
// src/__tests__/InstallFlow.spec.ts
|
||||
test('install flow shows preview and apply action', async () => {
|
||||
render(<Install />);
|
||||
// fill form -> click preview -> ensure diff rendered -> apply enabled
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `npx vitest run src/__tests__/InstallFlow.spec.ts`
|
||||
Expected: FAIL (页面未实现).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Wire form to Recipe params from catalog and invoke `preview_apply`.
|
||||
- Add DiffViewer with highlight.
|
||||
- Add confirm/apply button and success toasts.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `npx vitest run src/__tests__/InstallFlow.spec.ts`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/pages/Recipes.tsx src/pages/Install.tsx src/components/RecipeCard.tsx src/components/ParamForm.tsx src/components/DiffViewer.tsx src/lib/api.ts src/pages/Home.tsx src/__tests__/InstallFlow.spec.ts
|
||||
git commit -m "feat: implement recipe install wizard with preview+apply"
|
||||
```
|
||||
|
||||
### Task 8: 主页面路由与状态整合
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/App.tsx`
|
||||
- Modify: `src/lib/state.ts`
|
||||
- Modify: `src/pages/Home.tsx`
|
||||
- Modify: `src/pages/History.tsx`
|
||||
- Create: `src/lib/state_tests.ts`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```ts
|
||||
test('state loads recipes and history on boot', async () => {
|
||||
// mock tauri commands, render App, assert key tiles are loaded
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `npx vitest run src/lib/state_tests.ts`
|
||||
Expected: FAIL (state orchestration未实现).
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Implement shared reducer/events for system status, recipes, history, doctor report.
|
||||
- Add tabs/routes for Home/Recipes/History/Doctor.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `npx vitest run src/lib/state_tests.ts`
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/App.tsx src/lib/state.ts src/lib/state_tests.ts src/pages/Home.tsx src/pages/History.tsx
|
||||
git commit -m "feat: wire app-wide state and navigation"
|
||||
```
|
||||
|
||||
### Task 9: 端到端验收与发布脚手架
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
- Modify: `package.json`
|
||||
- Create: `scripts/release.sh`
|
||||
- Create: `docs/mvp-checklist.md`
|
||||
- Modify: `src-tauri/tauri.conf.json`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
```bash
|
||||
./scripts/release.sh --dry-run
|
||||
```
|
||||
|
||||
Expected: FAIL (脚本不存在).
|
||||
|
||||
**Step 2: Run it to verify it fails**
|
||||
|
||||
Run: `bash scripts/release.sh --dry-run`
|
||||
Expected: FAIL / script not found.
|
||||
|
||||
**Step 3: Write the minimal implementation**
|
||||
|
||||
- Add build scripts and basic build verification matrix (macOS/Windows/Linux placeholders).
|
||||
- Add manual QA checklist to validate install/rollback/doctor on clean and broken states.
|
||||
- Update README for usage.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `bash scripts/release.sh --dry-run`
|
||||
Expected: PASS (dry-run prints validated commands without publishing).
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md package.json scripts/release.sh docs/mvp-checklist.md src-tauri/tauri.conf.json
|
||||
git commit -m "chore: add MVP runbook and release scripts"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 执行方式
|
||||
|
||||
Plan complete and saved to `docs/plans/2026-02-15-clawpal-mvp-implementation-plan.md`. Two execution options:
|
||||
|
||||
1. Subagent-Driven (this session) - 我在当前会话按任务分派并在每步进行复核
|
||||
2. Parallel Session (separate) - 开新会话并使用 executing-plans 执行,有里程碑检查点
|
||||
|
||||
Which approach?
|
||||
260
docs/plans/2026-02-16-clawpal-product-redesign.md
Normal file
260
docs/plans/2026-02-16-clawpal-product-redesign.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# ClawPal 产品精简 & 重新定位
|
||||
|
||||
> 从"全功能配置管理后台"回归"AI 配置助手"
|
||||
|
||||
## 1. 问题
|
||||
|
||||
v0.2 新增了 Models、Channels、Data 三个管理页面后,产品从"场景驱动的配置助手"滑向了"OpenClaw 全功能管理后台"。功能杂糅,偏离了核心用户(新手)的需求。
|
||||
|
||||
## 2. 新产品定位
|
||||
|
||||
**ClawPal = AI 配置助手 + 精选 Recipe 库**
|
||||
|
||||
- 核心用户:不想碰 JSON 的新手
|
||||
- 第一入口:Chat — 用自然语言描述需求,LLM 生成配置方案
|
||||
- 第二入口:Recipe — 社区精选的常见场景,一键安装
|
||||
- 安全保证:所有变更(Chat 或 Recipe)走同一安全链路:快照 → 预览 diff → 用户确认 → 应用
|
||||
|
||||
## 3. 精简前后对比
|
||||
|
||||
| 精简前 (7 页面) | 精简后 | 处理方式 |
|
||||
|---|---|---|
|
||||
| Home (状态仪表盘) | Home (状态 + Agents + 推荐 + Chat) | 重构 |
|
||||
| Recipes | Recipes | 保留 |
|
||||
| Install | Install (从 Recipe/Chat 进入) | 保留 |
|
||||
| History | History | 保留 |
|
||||
| Doctor | Doctor (+ 数据清理) | 扩展 |
|
||||
| Models | Settings > Model Profiles | 降级,移除绑定功能 |
|
||||
| Channels | 删除 | 由 Chat/Recipe 覆盖 |
|
||||
| Data | 合并入 Doctor | 删除独立页面 |
|
||||
|
||||
## 4. 导航结构
|
||||
|
||||
左侧边栏,4 个主入口 + 1 个设置:
|
||||
|
||||
```
|
||||
┌──────────┬──────────────────────────────────┐
|
||||
│ │ │
|
||||
│ Home │ │
|
||||
│ │ │
|
||||
│ Recipes │ 页面内容 │
|
||||
│ │ │
|
||||
│ History │ │
|
||||
│ │ │
|
||||
│ Doctor │ │
|
||||
│ │ │
|
||||
│ ─────── │ │
|
||||
│ Settings│ │
|
||||
│ │ │
|
||||
└──────────┴──────────────────────────────────┘
|
||||
```
|
||||
|
||||
Install 不在侧边栏出现,从 Recipe 卡片或 Chat 触发后进入。
|
||||
|
||||
## 5. Home 页面
|
||||
|
||||
左右分栏布局:左侧主内容区,右侧常驻 Chat 窗口。
|
||||
|
||||
### 5.1 左侧主区域(四段式)
|
||||
|
||||
**状态摘要** — 一行紧凑卡片:
|
||||
- 配置健康状态(✅/❌)
|
||||
- OpenClaw 版本 + 是否有更新
|
||||
- 当前默认模型
|
||||
|
||||
**Agents 概览** — 每个 Agent 一行:
|
||||
- Agent 名称 / ID
|
||||
- 当前使用的模型
|
||||
- 关联的 Channels(Discord#xxx、Telegram 等)
|
||||
- 在线状态
|
||||
|
||||
**推荐 Recipes** — 3-4 张卡片,点击进入安装向导
|
||||
|
||||
**最近操作** — 最近 3-5 条历史记录
|
||||
|
||||
### 5.2 右侧 Chat 窗口
|
||||
|
||||
常驻面板,使用用户在 Settings 中配置的 Model Profile 调用 LLM。
|
||||
|
||||
**LLM 可调用的工具集:**
|
||||
|
||||
| 工具 | 作用 | 安全级别 |
|
||||
|------|------|----------|
|
||||
| `read_config` | 读取当前配置 | 无风险(只读) |
|
||||
| `list_agents` | 列出 Agent 信息 | 无风险 |
|
||||
| `list_recipes` | 搜索/推荐 Recipe | 无风险 |
|
||||
| `preview_change` | 生成配置补丁并展示 diff | 无风险(不写入) |
|
||||
| `apply_change` | 应用配置变更 | 需用户点确认 |
|
||||
| `run_doctor` | 运行诊断 | 无风险 |
|
||||
| `generate_recipe` | 根据描述生成新 Recipe | 无风险(只生成定义) |
|
||||
|
||||
**交互流程:**
|
||||
```
|
||||
用户: "我想让 agent-2 在 Telegram 群里只回复被 @ 的消息"
|
||||
↓
|
||||
LLM: 调用 read_config 了解当前配置
|
||||
↓
|
||||
LLM: 生成配置补丁,调用 preview_change
|
||||
↓
|
||||
Chat 内展示 diff 面板 + [确认应用] [取消] 按钮
|
||||
↓
|
||||
用户点确认 → 自动快照 → 写入配置
|
||||
```
|
||||
|
||||
**关键约束:**
|
||||
- 所有写操作必须先展示 diff,用户手动确认
|
||||
- 使用用户在 Settings 中选定的 Chat 模型
|
||||
- 未配置 Model Profile 时,Chat 区域提示引导去 Settings
|
||||
|
||||
### 5.3 布局示意
|
||||
|
||||
```
|
||||
┌──────────┬──────────────────────────┬──────────────────┐
|
||||
│ │ │ │
|
||||
│ Sidebar │ 状态摘要 (一行卡片) │ Chat 窗口 │
|
||||
│ │ ┌────┐ ┌────┐ ┌────┐ │ │
|
||||
│ │ │健康 │ │版本 │ │模型 │ │ 💬 你想实现 │
|
||||
│ │ │ ✅ │ │2.13│ │gpt4│ │ 什么配置? │
|
||||
│ │ └────┘ └────┘ └────┘ │ │
|
||||
│ │ │ ┌────────────┐ │
|
||||
│ │ Agents │ │ 输入框 │ │
|
||||
│ │ ┌──────────────────────┐ │ └────────────┘ │
|
||||
│ │ │ agent-1 gpt-4o │ │ │
|
||||
│ │ │ ✅ online Discord#1│ │ │
|
||||
│ │ ├──────────────────────┤ │ │
|
||||
│ │ │ agent-2 claude │ │ │
|
||||
│ │ │ ✅ online Telegram │ │ │
|
||||
│ │ ├──────────────────────┤ │ │
|
||||
│ │ │ agent-3 gpt-4o │ │ │
|
||||
│ │ │ ⚠ no channel │ │ │
|
||||
│ │ └──────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ 推荐 Recipes │ │
|
||||
│ │ ┌─────┐ ┌─────┐ ┌─────┐│ │
|
||||
│ │ │人设 │ │模型 │ │性能 ││ │
|
||||
│ │ └─────┘ └─────┘ └─────┘│ │
|
||||
│ │ │ │
|
||||
│ │ 最近操作 │ │
|
||||
│ │ • 2/15 应用了"Discord人设"│ │
|
||||
│ │ • 2/14 运行了 Doctor │ │
|
||||
│ │ │ │
|
||||
└──────────┴──────────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
## 6. Recipes 页面
|
||||
|
||||
保持现有设计:卡片式浏览,支持搜索/筛选/标签。
|
||||
|
||||
新增的 Recipe 类型(替代被删除的页面功能):
|
||||
- "切换默认模型" — 替代 Models 页面的全局绑定
|
||||
- "为 Agent 设置专属模型" — 替代 Models 页面的 Agent 绑定
|
||||
- "配置 Discord 频道白名单" — 替代 Channels 页面
|
||||
- "配置 Telegram mention 规则" — 替代 Channels 页面
|
||||
|
||||
## 7. Doctor 页面(扩展)
|
||||
|
||||
两个区域:
|
||||
|
||||
### 7.1 配置诊断(原有)
|
||||
- JSON 语法检查
|
||||
- 必填字段验证
|
||||
- 端口占用检测
|
||||
- 文件权限检查
|
||||
- 一键修复 + 变更原因展示
|
||||
|
||||
### 7.2 数据清理(从 Data 合并)
|
||||
- Memory 文件统计 + 一键清理
|
||||
- Session 文件统计 + 按 Agent 清理 / 全部清理
|
||||
- 磁盘占用展示
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Doctor │
|
||||
│ │
|
||||
│ 配置诊断 [运行检查] │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ ✅ JSON 语法正确 │ │
|
||||
│ │ ✅ 必填字段完整 │ │
|
||||
│ │ ❌ 端口 8080 被占用 │ │
|
||||
│ │ → [一键修复] 切换到 8081 │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 数据清理 │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Memory: 6 files (2.3 MB) │ │
|
||||
│ │ [清理全部] │ │
|
||||
│ │ Sessions: 23 files (15.1 MB) │ │
|
||||
│ │ agent-1: 12 files (8.2 MB) │ │
|
||||
│ │ agent-2: 11 files (6.9 MB) │ │
|
||||
│ │ [按 Agent 清理] [全部] │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 8. Settings 页面
|
||||
|
||||
### 8.1 Model Profiles
|
||||
|
||||
首次启动自动从 OpenClaw 配置中提取,无需手动操作。
|
||||
|
||||
Profile 卡片展示字段:
|
||||
- 名称
|
||||
- 服务商(provider)
|
||||
- 模型(model)
|
||||
- API Key(脱敏:`sk-proj-abc...7xZ`)
|
||||
- 自定义地址(仅设置时显示)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ GPT-4o ✅ 启用 │
|
||||
│ 服务商: openai │
|
||||
│ 模型: gpt-4o │
|
||||
│ API Key: sk-proj-abc...7xZ │
|
||||
│ [编辑] [删除] │
|
||||
├─────────────────────────────────────┤
|
||||
│ Claude ✅ 启用 │
|
||||
│ 服务商: anthropic │
|
||||
│ 模型: claude-sonnet-4-5 │
|
||||
│ API Key: sk-ant-k03...mNp │
|
||||
│ 自定义地址: https://my-proxy.com/v1 │
|
||||
│ [编辑] [删除] │
|
||||
└─────────────────────────────────────┘
|
||||
│ [+ 新建 Profile] │
|
||||
```
|
||||
|
||||
### 8.2 Chat 模型选择
|
||||
|
||||
下拉选择 Chat 窗口使用的 Model Profile。
|
||||
|
||||
### 8.3 路径配置
|
||||
|
||||
OpenClaw 目录和 ClawPal 数据目录的显示与自定义。
|
||||
|
||||
## 9. 删除清单
|
||||
|
||||
以下代码在实施时需要删除或重构:
|
||||
|
||||
| 文件 | 处理 |
|
||||
|------|------|
|
||||
| `src/pages/Channels.tsx` | 删除 |
|
||||
| `src/pages/Data.tsx` | 删除,功能迁移到 Doctor |
|
||||
| `src/pages/Models.tsx` | 删除,Profile 管理迁移到 Settings |
|
||||
| `src-tauri/src/commands.rs` | 移除 Channel CRUD、Data 相关命令,保留 Model Profile 命令 |
|
||||
| `src/App.tsx` | 移除 Channels/Data/Models 路由,改为侧边栏布局 |
|
||||
|
||||
## 10. 新增开发项
|
||||
|
||||
| 功能 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| 侧边栏布局 | P0 | 替换当前顶部 Tab |
|
||||
| Home 重构 | P0 | 四段式 + 右侧 Chat |
|
||||
| Chat 窗口 | P0 | LLM 工具调用 + diff 确认流程 |
|
||||
| Settings 页面 | P0 | Model Profile 管理 + Chat 模型选择 |
|
||||
| Doctor 扩展 | P1 | 合并数据清理功能 |
|
||||
| 新 Recipes | P1 | 模型切换、频道配置等替代 Recipe |
|
||||
| Agents 概览 API | P1 | 首页 Agent 列表的后端支持 |
|
||||
| 首次启动自动提取 | P2 | 自动从配置提取 Model Profiles |
|
||||
|
||||
---
|
||||
|
||||
*Created: 2026-02-16*
|
||||
1366
docs/plans/2026-02-16-clawpal-redesign-implementation-plan.md
Normal file
1366
docs/plans/2026-02-16-clawpal-redesign-implementation-plan.md
Normal file
File diff suppressed because it is too large
Load Diff
181
docs/plans/2026-02-16-model-channel-management-implementation.md
Normal file
181
docs/plans/2026-02-16-model-channel-management-implementation.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Model & Channel Management Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Add practical in-app management for model auth profiles, model binding strategy, and basic channel configuration (dm/group/allowlist/mode) with safe write operations and Home status visibility.
|
||||
|
||||
**Architecture:** Keep existing Tauri command boundary as the source of truth. Rust keeps all file I/O + patch writes; UI only renders/edit values and submits commands. Configuration changes go through atomic write + snapshot, while model auth profile registry lives in ClawPal metadata directory for separation from OpenClaw config.
|
||||
|
||||
**Tech Stack:** Tauri 2, Rust (`serde`, `serde_json`), React 18, TypeScript.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Define shared data contract for model/channels
|
||||
|
||||
**Files:**
|
||||
- `src/lib/types.ts`
|
||||
- `src-tauri/src/commands.rs`
|
||||
|
||||
**Step 1: Write the structure in types + command models (code first pass)**
|
||||
|
||||
- Add frontend interfaces for:
|
||||
- `ModelProfile` (`id`, `name`, `provider`, `model`, `authRef`, `baseUrl`, `description`, `enabled`)
|
||||
- `ChannelNode` (`path`, `channelType`, `mode`, `allowlist`, `modelValue`, `hasModelField`)
|
||||
- `ModelBinding` (`scope`, `scopeId`, `modelProfileId`, `modelValue`)
|
||||
- API command input/output types.
|
||||
|
||||
**Step 2: Add corresponding Rust serializable structs in `commands.rs`**
|
||||
|
||||
- Add `#[derive(Serialize, Deserialize)]` structs that mirror above.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Backing storage + utilities
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/models.rs`
|
||||
|
||||
**Step 1: Add metadata paths for profile storage**
|
||||
|
||||
- Use `OpenClawPaths` with existing `clawpal_dir` and introduce a `model_profiles.json` file path inside it.
|
||||
|
||||
**Step 2: Implement helper readers/writers**
|
||||
|
||||
- `load_model_profiles(paths) -> Vec<ModelProfile>`
|
||||
- `save_model_profiles(paths, &Vec<ModelProfile>)`
|
||||
- Path-safe setters for nested JSON fields inside `openclaw.json` using dot-path nodes.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Model profile CRUD commands
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src/lib/api.ts`
|
||||
- `src/lib/types.ts`
|
||||
|
||||
**Step 1: Add Tauri commands**
|
||||
|
||||
- `list_model_profiles() -> Vec<ModelProfile>`
|
||||
- `upsert_model_profile(profile: ModelProfile) -> ModelProfile`
|
||||
- `delete_model_profile(profile_id: String) -> bool`
|
||||
|
||||
**Step 2: Register commands in `invoke_handler`**
|
||||
|
||||
- Ensure frontend can call these commands via API wrapper.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Channel discovery and channel config commands
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src/lib/api.ts`
|
||||
- `src/lib/types.ts`
|
||||
|
||||
**Step 1: Discover editable channel nodes**
|
||||
|
||||
- Implement recursion under `/channels` for nodes that carry `type|mode|allowlist|model`.
|
||||
- Return normalized `ChannelNode` list with stable paths.
|
||||
|
||||
**Step 2: Expose write commands**
|
||||
|
||||
- `list_channels() -> Vec<ChannelNode>`
|
||||
- `update_channel_config(path: String, channel_type: Option<String>, mode: Option<String>, allowlist: Vec<String>, model: Option<String>) -> bool`
|
||||
- `delete_channel_node(path: String) -> bool` (safe: remove node if exists)
|
||||
|
||||
**Step 3: Register commands**
|
||||
|
||||
- Add new invoke entries in `lib.rs`.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Model binding commands (global/agent/channel)
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src/lib/api.ts`
|
||||
- `src/lib/types.ts`
|
||||
|
||||
**Step 1: Implement read summary**
|
||||
|
||||
- Add helper to return effective model binding map from current config:
|
||||
- global default model
|
||||
- per-agent model overrides
|
||||
- per-channel model overrides
|
||||
|
||||
**Step 2: Implement commands**
|
||||
|
||||
- `set_global_model(profile_id: Option<String>) -> bool`
|
||||
- `set_agent_model(agent_id: String, profile_id: Option<String>) -> bool`
|
||||
- `set_channel_model(channel_path: String, profile_id: Option<String>) -> bool`
|
||||
|
||||
**Step 3: Snapshot safety**
|
||||
|
||||
- Use existing snapshot flow (`read_openclaw_config` + `add_snapshot`) before each write.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Frontend pages and routing
|
||||
|
||||
**Files:**
|
||||
- `src/App.tsx`
|
||||
- `src/pages/Models.tsx` (new)
|
||||
- `src/pages/Channels.tsx` (new)
|
||||
- `src/lib/api.ts`
|
||||
- `src/lib/types.ts`
|
||||
|
||||
**Step 1: Wire routes for Models and Channels**
|
||||
|
||||
- Extend top nav and render new pages.
|
||||
|
||||
**Step 2: Models page**
|
||||
|
||||
- List model profiles, create/update/delete quickly.
|
||||
- Add `profile id` selector.
|
||||
- Add global / agent / channel assignment panel (simple controls first).
|
||||
|
||||
**Step 3: Channels page**
|
||||
|
||||
- List discovered channel nodes and allow update model/allowlist/mode.
|
||||
- Add minimal inline editing (textarea for allowlist lines).
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Surface in Home
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src/lib/types.ts`
|
||||
- `src/pages/Home.tsx`
|
||||
|
||||
**Step 1: Expand system status with `model` binding counts**
|
||||
|
||||
- Keep current aggregates and make counts update from registry + bindings.
|
||||
|
||||
**Step 2: Display quick links/button actions**
|
||||
|
||||
- From Home open Models / Channels quick nav (simple text action).
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Manual verification checklist
|
||||
|
||||
**Files:**
|
||||
- `docs/mvp-checklist.md`
|
||||
|
||||
**Step 1: Add verification items for this feature set**
|
||||
|
||||
- Profile add/update/delete.
|
||||
- Channel mode/allowlist/model update.
|
||||
- Global/agent/channel model assignment.
|
||||
- Status card updates after edits.
|
||||
|
||||
**Step 2: Execute manual run list**
|
||||
|
||||
- Build frontend and verify command invocation completes.
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Openclaw-Driven Model/Channel Ops Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
## For Feature
|
||||
|
||||
**Goal:** Enable model catalog and model/profile management to use openclaw CLI + caching keyed to openclaw version, add upgrade-check visibility, sync current config models into profiles, and enrich discord channel metadata.
|
||||
|
||||
**Architecture:** Introduce a small openclaw-command layer in Tauri that executes `openclaw` with `--json` options, normalizes JSON outputs, writes cache under clawpal dir keyed by openclaw CLI version, and exposes UI actions for sync/inspect.
|
||||
|
||||
**Tech Stack:** Rust (`std::process` + `serde_json`), React/Tauri IPC, OpenClaw CLI commands.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: OpenClaw command adapter + version-aware model catalog cache
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/models.rs`
|
||||
- `src-tauri/Cargo.toml` (if needed)
|
||||
|
||||
**Step 1: Implement command runner utilities**
|
||||
- Add small wrappers for running CLI commands (`openclaw --version`, `openclaw models list --all --json`, with optional `--provider`, `openclaw channels list --json --no-usage`, `openclaw update status --json`, `openclaw channels resolve ... --json` when needed).
|
||||
- Parse and normalize JSON outputs into internal structs.
|
||||
- Capture stderr for diagnostic messages.
|
||||
|
||||
**Step 2: Add cache structs and paths**
|
||||
- Add catalog cache file path e.g. `model-catalog-cache.json` under clawpal dir.
|
||||
- Include fields: `cliVersion`, `updatedAt`, `providers`, `source`, `error`, `ttlMinutes`.
|
||||
|
||||
**Step 3: Add version-aware refresh policy**
|
||||
- Read current `openclaw --version` and current cache version.
|
||||
- If version changed, or cache stale (e.g. 12h), refresh by running `openclaw models list --all --json`.
|
||||
- If refresh fails, fall back to config-based extraction so UI remains usable.
|
||||
|
||||
**Step 4: Wire `list_model_catalog` to new pipeline**
|
||||
- Return CLI/ cache-normalized `ModelCatalogProvider[]`.
|
||||
- Keep `model/probe` fallback from config providers in emergency path.
|
||||
|
||||
### Task 2: OpenClaw upgrade status endpoint
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src/lib/types.ts`
|
||||
- `src/lib/api.ts`
|
||||
- `src/pages/Home.tsx`
|
||||
|
||||
**Step 1: Add API result model**
|
||||
- Add `OpenclawUpdateCheck` to `types.ts` with `installKind/channel/source/version/availability` etc (at least `outdated`, `currentVersion`, `latestVersion`, `upgradeAvailable`, `updateLine`, `checkedAt`).
|
||||
|
||||
**Step 2: Add backend command**
|
||||
- Add `check_openclaw_update` that runs `openclaw update status --json`.
|
||||
- Return normalized result and errors non-fatal to UI.
|
||||
|
||||
**Step 3: Extend status payload**
|
||||
- Optionally add `openclawUpdate` field to `SystemStatus`, fetched inside `get_system_status`.
|
||||
|
||||
**Step 4: Show on Home page**
|
||||
- Add “OpenClaw update” card: current vs latest, and availability warning if newer exists.
|
||||
|
||||
### Task 3: Auto-extract existing model bindings to profiles
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src/lib/types.ts`
|
||||
- `src/lib/api.ts`
|
||||
- `src/pages/Models.tsx`
|
||||
|
||||
**Step 1: Add extractor command**
|
||||
- New command `extract_model_profiles_from_config(dryRun: bool)` that inspects `/agents/*model`, `channels.*.model`, and defaults for explicit model refs.
|
||||
- For each unique model ref create/refresh profiles from current config, using auth mapping heuristics:
|
||||
- if model has `provider` prefix and `auth` map exists, pick first matching `auth.profiles` for that provider;
|
||||
- fallback to `default`.
|
||||
- Return `created|updated|skipped` stats and preview list.
|
||||
|
||||
**Step 2: Frontend button action**
|
||||
- Add button `Import current model config as profiles` in models page.
|
||||
- Add optional checkbox / confirm to enable overwrite for detected matches.
|
||||
|
||||
**Step 3: Make bindings discover profile matches**
|
||||
- Since extractor writes profiles with exact `provider/model`, channel/agent bindings immediately resolve to profile IDs.
|
||||
- Keep `find_profile_by_model` logic unchanged (already matches by exact string).
|
||||
|
||||
### Task 4: Discord channel name enrichment via CLI resolve
|
||||
|
||||
**Files:**
|
||||
- `src-tauri/src/commands.rs`
|
||||
- `src-tauri/src/lib.rs`
|
||||
- `src-tauri/src/types.ts` (if new shape)
|
||||
- `src/pages/Channels.tsx`
|
||||
|
||||
**Step 1: Add channel-name resolver command**
|
||||
- Parse Discord channel IDs from channel node paths (e.g. `channels.discord.guilds.<guildId>.channels.<channelId>`).
|
||||
- For discovered IDs, call `openclaw channels resolve <id> --channel discord --kind group --json` and map id->name.
|
||||
- Return best effort, with `displayName` and `nameStatus` (`resolved`/`missing`).
|
||||
|
||||
**Step 2: Cache small resolution results**
|
||||
- Add small cache in clawpal dir keyed by `guildId:channelId` and openclaw version.
|
||||
- Graceful fallback to raw ID when resolve fails.
|
||||
|
||||
**Step 3: UI display**
|
||||
- Add `displayName` in channel row and keep path as stable key.
|
||||
|
||||
### Execution flow
|
||||
|
||||
**Use subagent-driven mode with review checkpoints** because this spans Rust + React + IPC.
|
||||
|
||||
Reference in New Issue
Block a user