From 6f72442531b6635396b65bb8072ce2c8bd736ea5 Mon Sep 17 00:00:00 2001 From: iven Date: Fri, 20 Mar 2026 19:30:09 +0800 Subject: [PATCH] docs(guide): rewrite CLAUDE.md with ZCLAW-first perspective Major changes: - Shift from "OpenFang desktop client" to "independent AI Agent desktop app" - Add decision principle: "Is this useful for ZCLAW? Does it affect ZCLAW?" - Simplify project structure and tech stack sections - Replace OpenClaw vs OpenFang comparison with unified backend approach - Consolidate troubleshooting from scattered sections into organized FAQ - Update Hands system documentation with 8 capabilities and status - Stream --- CLAUDE.md | 577 +++------ config/config.toml | 6 +- desktop/package.json | 3 + desktop/src/App.css | 83 +- desktop/src/App.tsx | 11 +- .../components/Automation/AutomationCard.tsx | 2 +- .../components/Automation/AutomationPanel.tsx | 124 +- .../components/Automation/ExecutionResult.tsx | 4 +- .../BrowserHand/BrowserHandCard.tsx | 2 +- desktop/src/components/ChatArea.tsx | 164 ++- desktop/src/components/HandParamsForm.tsx | 68 +- desktop/src/components/HandsPanel.tsx | 4 +- desktop/src/components/Settings/ModelsAPI.tsx | 521 +++++--- desktop/src/components/Sidebar.tsx | 22 +- desktop/src/components/SkillMarket.tsx | 4 +- .../src/components/SkillMarket/SkillCard.tsx | 2 +- desktop/src/components/SwarmDashboard.tsx | 16 +- desktop/src/components/TeamList.tsx | 73 +- desktop/src/components/WorkflowEditor.tsx | 4 +- desktop/src/components/ui/EmptyState.tsx | 2 +- desktop/src/index.css | 16 +- desktop/src/lib/config-parser.ts | 4 +- desktop/src/lib/gateway-client.ts | 32 +- desktop/src/store/gatewayStore.ts | 123 +- desktop/src/store/handStore.ts | 12 +- desktop/src/store/teamStore.ts | 18 +- desktop/src/types/automation.ts | 27 +- desktop/src/types/hands.ts | 139 +- desktop/tailwind.config.js | 4 + desktop/tests/e2e/fixtures/mock-gateway.ts | 788 ++++++++++++ .../tests/e2e/fixtures/store-inspectors.ts | 635 ++++++++++ desktop/tests/e2e/fixtures/test-data.ts | 459 +++++++ desktop/tests/e2e/playwright.config.ts | 99 +- desktop/tests/e2e/specs/data-flow.spec.ts | 605 +++++++++ desktop/tests/e2e/specs/edge-cases.spec.ts | 659 ++++++++++ desktop/tests/e2e/specs/store-state.spec.ts | 538 ++++++++ .../e2e/test-results/artifacts/.last-run.json | 7 + .../error-context.md | 41 + .../test-failed-1.png | Bin 0 -> 37934 bytes .../video.webm | Bin 0 -> 792139 bytes .../error-context.md | 41 + .../test-failed-1.png | Bin 0 -> 37934 bytes .../video.webm | Bin 0 -> 802425 bytes ...96f0d5d3da720dd6545887fd9fd3cb83228a7d8.md | 41 + ...76839f8be4781f0e5c38e4aacef46828296b6.webm | Bin 0 -> 802425 bytes ...87949f0076eca84e4c4122ed16eebb47b4afb6.png | Bin 0 -> 37934 bytes ...1b9f363571e51e610b6ee498f65408cc00e58.webm | Bin 0 -> 792139 bytes .../e2e/test-results/html-report/index.html | 85 ++ desktop/tests/e2e/test-results/results.json | 1121 +++++++++++++++++ desktop/tests/e2e/utils/network-helpers.ts | 366 ++++++ desktop/tests/e2e/utils/store-assertions.ts | 474 +++++++ desktop/tests/e2e/utils/user-actions.ts | 778 ++++++++++++ desktop/vite.config.ts | 2 +- ...tegration.md => 00-backend-integration.md} | 0 docs/features/README.md | 2 +- docs/knowledge-base/README.md | 18 +- ...fang-configuration.md => configuration.md} | 0 docs/knowledge-base/team-feature-notes.md | 138 ++ docs/knowledge-base/troubleshooting.md | 77 +- ...cket-protocol.md => websocket-protocol.md} | 0 ...erence.md => zclaw-technical-reference.md} | 0 plans/harmonic-churning-falcon.md | 163 +++ plans/toasty-wibbling-pudding.md | 573 +++++++++ 63 files changed, 8920 insertions(+), 857 deletions(-) create mode 100644 desktop/tests/e2e/fixtures/mock-gateway.ts create mode 100644 desktop/tests/e2e/fixtures/store-inspectors.ts create mode 100644 desktop/tests/e2e/fixtures/test-data.ts create mode 100644 desktop/tests/e2e/specs/data-flow.spec.ts create mode 100644 desktop/tests/e2e/specs/edge-cases.spec.ts create mode 100644 desktop/tests/e2e/specs/store-state.spec.ts create mode 100644 desktop/tests/e2e/test-results/artifacts/.last-run.json create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-01-打开设置数据流-chromium/error-context.md create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-01-打开设置数据流-chromium/test-failed-1.png create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-01-打开设置数据流-chromium/video.webm create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-02-模型配置数据流-chromium/error-context.md create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-02-模型配置数据流-chromium/test-failed-1.png create mode 100644 desktop/tests/e2e/test-results/artifacts/data-flow-设置数据流验证-SET-DF-02-模型配置数据流-chromium/video.webm create mode 100644 desktop/tests/e2e/test-results/html-report/data/596f0d5d3da720dd6545887fd9fd3cb83228a7d8.md create mode 100644 desktop/tests/e2e/test-results/html-report/data/70376839f8be4781f0e5c38e4aacef46828296b6.webm create mode 100644 desktop/tests/e2e/test-results/html-report/data/c687949f0076eca84e4c4122ed16eebb47b4afb6.png create mode 100644 desktop/tests/e2e/test-results/html-report/data/d191b9f363571e51e610b6ee498f65408cc00e58.webm create mode 100644 desktop/tests/e2e/test-results/html-report/index.html create mode 100644 desktop/tests/e2e/test-results/results.json create mode 100644 desktop/tests/e2e/utils/network-helpers.ts create mode 100644 desktop/tests/e2e/utils/store-assertions.ts create mode 100644 desktop/tests/e2e/utils/user-actions.ts rename docs/features/06-tauri-backend/{00-openfang-integration.md => 00-backend-integration.md} (100%) rename docs/knowledge-base/{openfang-configuration.md => configuration.md} (100%) create mode 100644 docs/knowledge-base/team-feature-notes.md rename docs/knowledge-base/{openfang-websocket-protocol.md => websocket-protocol.md} (100%) rename docs/knowledge-base/{openfang-technical-reference.md => zclaw-technical-reference.md} (100%) create mode 100644 plans/harmonic-churning-falcon.md create mode 100644 plans/toasty-wibbling-pudding.md diff --git a/CLAUDE.md b/CLAUDE.md index b594a11..3335643 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,54 +1,63 @@ # ZCLAW 协作与实现规则 -> 目标:把 ZCLAW 做成**真实可交付**的 OpenFang 桌面客户端,而不是"看起来能用"的演示 UI。 +> **ZCLAW 是一个独立成熟的 AI Agent 桌面客户端**,专注于提供真实可用的 AI 能力,而不是演示 UI。 -## 1. 项目目标 +## 1. 项目定位 -ZCLAW 是基于 **OpenFang** (Rust Agent OS) 的 AI Agent 桌面端,核心价值不是单纯聊天,而是: +### 1.1 ZCLAW 是什么 -- 真实连接 OpenFang Kernel -- 真实驱动 Agents / Skills / Hands / Workflows -- 真实读写 TOML 配置与工作区 -- 真实反映运行时状态与审计日志 +ZCLAW 是面向中文用户的 AI Agent 桌面端,核心能力包括: -判断标准: +- **智能对话** - 多模型支持、流式响应、上下文管理 +- **自主能力** - 8 个 Hands(浏览器、数据采集、研究、预测等) +- **技能系统** - 可扩展的 SKILL.md 技能定义 +- **工作流编排** - 多步骤自动化任务 +- **安全审计** - 完整的操作日志和权限控制 -> 一个页面或按钮如果**没有改变 OpenFang Runtime 的真实行为 / 真实配置 / 真实路由 / 真实工作区上下文**,那它大概率还只是演示态,不算交付完成。 +### 1.2 决策原则 + +**任何改动都要问:这对 ZCLAW 有用吗?对 ZCLAW 有影响吗?** + +- ✅ 对 ZCLAW 用户有价值的功能 → 优先实现 +- ✅ 提升 ZCLAW 稳定性和可用性 → 必须做 +- ❌ 只为兼容其他系统的妥协 → 谨慎评估 +- ❌ 增加复杂度但无实际价值 → 不做 --- ## 2. 项目结构 ```text -ZClaw/ +ZCLAW/ ├── desktop/ # Tauri 桌面应用 │ ├── src/ -│ │ ├── components/ # React UI -│ │ ├── store/ # Zustand stores -│ │ └── lib/ # OpenFang client / helpers -│ └── src-tauri/ # Tauri Rust backend +│ │ ├── components/ # React UI 组件 +│ │ ├── store/ # Zustand 状态管理 +│ │ └── lib/ # 客户端通信 / 工具函数 +│ └── src-tauri/ # Tauri Rust 后端 ├── skills/ # SKILL.md 技能定义 -├── hands/ # HAND.toml 自主能力包 -├── config/ # OpenFang TOML 配置 -├── docs/ # 架构、排障、知识库 +├── hands/ # HAND.toml 自主能力配置 +├── config/ # TOML 配置文件 +├── docs/ # 架构文档和知识库 └── tests/ # Vitest 回归测试 ``` -核心数据流: +### 2.1 核心数据流 ```text -React UI → Zustand Store → OpenFangClient → OpenFang Kernel → Skills / Hands / Channels +用户操作 → React UI → Zustand Store → Gateway Client → 后端服务 → Skills / Hands ``` -**OpenFang vs OpenClaw 关键差异**: +### 2.2 技术栈 -| 方面 | OpenClaw | OpenFang | -|------|----------|----------| -| 语言 | TypeScript/Node.js | Rust | -| 端口 | 18789 | 4200 | -| 配置 | YAML/JSON | TOML | -| 插件 | TypeScript | SKILL.md + WASM | -| 安全 | 3 层 | 16 层纵深防御 | +| 层级 | 技术 | +|------|------| +| 前端框架 | React 18 + TypeScript | +| 状态管理 | Zustand | +| 桌面框架 | Tauri 2.x | +| 样式方案 | Tailwind CSS | +| 配置格式 | TOML | +| 后端服务 | Rust (端口 50051) | --- @@ -56,430 +65,250 @@ React UI → Zustand Store → OpenFangClient → OpenFang Kernel → Skills / H ### 3.1 交付导向 -- 先做**最高杠杆**问题 -- 优先恢复真实能力,再考虑局部美化 -- 不保留"假数据看起来正常"的占位实现 +- **先做最高杠杆问题** - 解决用户最痛的点 +- **真实能力优先** - 不做假数据占位 +- **完整闭环** - 每个功能都要能真正使用 ### 3.2 根因优先 -- 先确认问题属于: - - 协议错配 (WebSocket vs REST) - - 状态管理错误 - - UI 没接真实能力 - - 配置解析 / 持久化错误 (TOML 格式) - - 运行时 / 环境问题 -- 不在根因未明时盲目堆补丁 +遇到问题时,先确认属于哪一类: + +1. **协议问题** - API 端点、请求格式、响应解析 +2. **状态问题** - Store 更新、组件同步 +3. **UI 问题** - 交互逻辑、样式显示 +4. **配置问题** - TOML 解析、环境变量 +5. **运行时问题** - 服务启动、端口占用 + +不在根因未明时盲目堆补丁。 ### 3.3 闭环工作法 -每次改动尽量形成完整闭环: +每次改动形成完整闭环: -1. 定位问题 -2. 建立最小可信心智模型 -3. 实现最小有效修复 -4. 跑自动化验证 -5. 记录知识沉淀 +1. 定位问题 → 2. 建立心智模型 → 3. 最小修复 → 4. 自动验证 → 5. 记录沉淀 --- -## 4. 解决问题的标准流程 +## 4. 实现规则 -### 4.1 先看真实协议和真实运行时 +### 4.1 通信层 -当桌面端与 OpenFang 行为不一致时: +所有与后端的通信必须通过统一的客户端层: -- 先检查当前 REST API schema / WebSocket 事件格式 -- 不要只相信旧前端封装或历史调用方式 -- 如果源码与实际运行行为冲突,以**当前 OpenFang Kernel**为准 +- `desktop/src/lib/gateway-client.ts` - 主要通信客户端 +- `desktop/src/lib/tauri-gateway.ts` - Tauri 原生命令 -尤其是以下能力必须以真实 OpenFang 为准: +**禁止**在组件内直接创建 WebSocket 或拼装 HTTP 请求。 -- `/api/chat` (聊天) -- `/api/agents` (Agent 管理) -- `/api/hands/*` (Hands 触发) -- `/api/workflows/*` (工作流) -- `/api/config` (TOML 配置) -- `/api/audit/logs` (审计日志) -- WebSocket 事件 (`stream`, `hand`, `workflow`) +### 4.2 状态管理 -### 4.2 先打通读,再打通写 - -任何配置类页面都按这个顺序推进: - -1. 先确认页面能读取真实配置 -2. 再确认页面能显示真实当前值 -3. 最后再接保存 - -禁止直接做"本地 state 假切换"冒充已完成。 - -### 4.3 区分"前端概念"和"运行时概念" - -如果前端有自己的本地实体,例如: - -- agent / clone -- conversation / session -- temporary model selection - -必须明确它是否真的对应 OpenFang 中的: - -- `agent_id` -- `session_id` -- `default_model` - -不要把本地 UI 标识直接当成 OpenFang runtime 标识发送。 - -### 4.4 调试优先顺序 - -遇到问题时,优先按这个顺序排查: - -1. 是否连到了正确的 OpenFang (端口 4200) -2. 是否握手/认证成功 -3. 请求方法名是否正确 (REST endpoint / WebSocket message type) -4. 请求参数是否符合当前 schema -5. 返回结构是否与前端解析一致 -6. 页面是否只是改了本地 state,没有写回 runtime -7. 是否存在旧 fallback / placeholder 掩盖真实错误 - ---- - -## 5. 实现规则 - -### 5.1 Gateway 通信 - -IMPORTANT: 所有与 OpenFang 的通信必须通过: - -- `desktop/src/lib/openfang-client.ts` (OpenFang) -- `desktop/src/lib/gateway-client.ts` (OpenClaw 兼容层) - -禁止在组件内直接创建 WebSocket 或拼装协议帧。 - -### 5.2 后端切换 - -通过环境变量或 localStorage 切换后端: - -```typescript -// 环境变量 -const USE_OPENFANG = import.meta.env.VITE_USE_OPENFANG === 'true'; - -// localStorage -const backendType = localStorage.getItem('zclaw-backend') || 'openclaw'; +``` +UI 组件 → 只负责展示和交互 +Store → 负责状态组织和流程编排 +Client → 负责网络通信 +lib/ → 工具函数和协议适配 ``` -### 5.3 状态管理 +### 4.3 代码规范 -- UI 负责展示和交互 -- Store 负责状态组织、流程编排 -- OpenFangClient 负责 REST / WebSocket 通信 -- 配置读写和协议适配逻辑放在 `lib/` 助手层 +**TypeScript:** +- 避免 `any`,优先 `unknown + 类型守卫` +- 外部数据必须做容错解析 +- 不假设 API 响应永远只有一种格式 -避免把协议细节散落在多个组件里。 +**React:** +- 使用函数组件 + hooks +- 复杂副作用收敛到 store +- 组件保持"展示层"职责 -### 5.4 React 组件 - -- 使用函数组件与 hooks -- 复杂副作用收敛到 store 或 helper -- 组件尽量保持"展示层"职责 -- 一个组件里如果同时出现协议拼装、复杂状态机、配置改写逻辑,优先拆分 - -### 5.5 TypeScript - -- 避免 `any` -- 优先 `unknown + 类型守卫` -- 外部返回结构必须做容错解析 -- 不要假设 OpenFang 响应永远只有一种 shape - -### 5.6 配置处理 (TOML) - -OpenFang 使用 **TOML** 配置格式: - -```toml -# ~/.openfang/config.toml - -[server] -host = "127.0.0.1" -port = 4200 - -[agent] -default_model = "gpt-4" - -[[llm.providers]] -name = "openai" -api_key = "${OPENAI_API_KEY}" -``` - -对配置的处理: - -- 使用 TOML 解析器,不要手动解析 -- 写回时保持 TOML 格式 +**配置处理:** +- 使用 TOML 解析器 - 支持环境变量插值 `${VAR_NAME}` +- 写回时保持格式一致 --- -## 6. UI 完成度规则 +## 5. UI 完成度标准 -### 6.1 允许存在的 UI +### 5.1 允许存在的 UI -- 已接真实能力的 UI -- 明确标注"未实现 / 只读 / 待接入"的 UI +- 已接入真实后端能力的 UI +- 明确标注"开发中 / 只读"的 UI +- 有降级方案的 UI -### 6.2 不允许存在的 UI +### 5.2 不允许存在的 UI -- 看似可编辑但不会生效的设置项 -- 展示假状态却不对应真实运行时的面板 -- 用 mock 数据掩盖未完成能力但不做说明 +- 看似可编辑但不会生效的设置 +- 展示假状态的面板 +- 用 mock 数据掩盖未完成能力 -### 6.3 OpenFang 新特性 UI +### 5.3 核心功能 UI -以下 OpenFang 特有功能需要新增 UI: - -- **Hands 面板**: 触发和管理 7 个自主能力包 -- **Workflow 编辑器**: 多步骤工作流编排 -- **Trigger 管理器**: 事件触发器配置 -- **审计日志**: Merkle 哈希链审计查看 +| 功能 | 状态 | 说明 | +|------|------|------| +| 聊天界面 | ✅ 完成 | 流式响应、多模型切换 | +| 分身管理 | ✅ 完成 | 创建、配置、切换 Agent | +| 自动化面板 | ✅ 完成 | Hands 触发、参数配置 | +| 技能市场 | 🚧 进行中 | 技能浏览和安装 | +| 工作流编辑 | 📋 计划中 | 多步骤任务编排 | --- -## 7. 测试与验证规则 +## 6. 自主能力系统 (Hands) -### 7.1 改动后必须验证 +ZCLAW 提供 8 个自主能力包: -修改以下内容后,必须至少运行相关测试: +| Hand | 功能 | 状态 | +|------|------|------| +| Browser | 浏览器自动化 | ✅ 可用 | +| Collector | 数据收集聚合 | ✅ 可用 | +| Researcher | 深度研究 | ✅ 可用 | +| Predictor | 预测分析 | ✅ 可用 | +| Lead | 销售线索发现 | ✅ 可用 | +| Trader | 交易分析 | ✅ 可用 | +| Clip | 视频处理 | ⚠️ 需 FFmpeg | +| Twitter | Twitter 自动化 | ⚠️ 需 API Key | -- chat / stream -- openfang client / gateway store -- settings / config -- protocol helpers +**触发 Hand 时:** +1. 检查依赖是否满足 +2. 收集必要参数 +3. 处理 `needs_approval` 状态 +4. 记录执行日志 -优先命令: +--- + +## 7. 测试与验证 + +### 7.1 必测场景 + +修改以下内容后必须验证: + +- 聊天 / 流式响应 +- Store 状态更新 +- 配置读写 +- Hand 触发 + +### 7.2 验证命令 ```bash -pnpm vitest run tests/desktop/chatStore.test.ts tests/desktop/gatewayStore.test.ts tests/desktop/general-settings.test.tsx +# TypeScript 类型检查 pnpm tsc --noEmit + +# 单元测试 +pnpm vitest run + +# 启动开发环境 +pnpm start:dev ``` -如果新增了独立 helper,应补最小回归测试。 +### 7.3 人工验证清单 -### 7.2 测试设计原则 - -- 测根因,不只测表象 -- 测协议参数是否正确 (REST endpoint / WebSocket type) -- 测状态是否在失败时保持一致 -- 测真实边界条件: - - agent_id 生命周期 - - session_id 作用域 - - TOML 配置语法容错 - - Hand 触发与审批 - -### 7.3 人工验证 - -自动化通过后,关键链路仍应做手工 smoke: - -- 能否连接 OpenFang (端口 4200) -- 能否发送消息并正常流式返回 -- 模型切换是否真实生效 -- Hand 触发是否正常执行 -- 保存配置后是否真正影响新会话/运行时 +- [ ] 能否正常连接后端服务 +- [ ] 能否发送消息并获得流式响应 +- [ ] 模型切换是否生效 +- [ ] Hand 触发是否正常执行 +- [ ] 配置保存是否持久化 --- -## 8. 文档管理规则 +## 8. 文档管理 ### 8.1 文档结构 ```text docs/ -├── features/ # 功能全景文档 -│ ├── README.md # 功能索引和优先级矩阵 -│ ├── brainstorming-notes.md # 头脑风暴记录 -│ ├── 00-architecture/ # 架构层功能 -│ ├── 01-core-features/ # 核心功能 -│ ├── 02-intelligence-layer/ # 智能层 (L4 自演化) -│ ├── 03-context-database/ # 上下文数据库 -│ ├── 04-skills-ecosystem/ # Skills 生态 -│ ├── 05-hands-system/ # Hands 系统 -│ └── 06-tauri-backend/ # Tauri 后端 -├── knowledge-base/ # 技术知识库 -│ ├── openfang-technical-reference.md -│ ├── openfang-websocket-protocol.md -│ └── troubleshooting.md -└── WORK_SUMMARY_*.md # 工作日志 +├── features/ # 功能文档 +│ ├── README.md # 功能索引 +│ └── */ # 各功能详细文档 +├── knowledge-base/ # 技术知识库 +│ ├── troubleshooting.md +│ └── *.md +└── archive/ # 归档文档 ``` -### 8.2 功能文档维护规范 +### 8.2 文档更新原则 -**何时更新功能文档**: - -| 触发条件 | 更新内容 | -|---------|---------| -| 新增功能 | 创建新文档,填写设计初衷 | -| 功能修改 | 更新技术设计、预期作用 | -| 功能完成 | 更新实际效果、测试覆盖 | -| 发现问题 | 更新已知问题、风险挑战 | -| 用户反馈 | 更新用户反馈、演化路线 | - -**功能文档模板**: - -```markdown -# [功能名称] - -> **分类**: [架构层/核心功能/智能层/上下文数据库/Skills/Hands/Tauri] -> **优先级**: [P0-决定性 / P1-重要 / P2-增强] -> **成熟度**: [L0-概念 / L1-原型 / L2-可用 / L3-成熟 / L4-生产] -> **最后更新**: YYYY-MM-DD - -## 一、功能概述 -## 二、设计初衷(问题背景、设计目标、竞品参考、设计约束) -## 三、技术设计(核心接口、数据流、状态管理) -## 四、预期作用(用户价值、系统价值、成功指标) -## 五、实际效果(已实现、测试覆盖、已知问题、用户反馈) -## 六、演化路线(短期/中期/长期) -## 七、头脑风暴笔记(待讨论问题、创意想法、风险挑战) -``` - -### 8.3 知识库更新规则 - -凡是出现以下情况,应更新 `docs/knowledge-base/` 或相关文档: - -- 新的协议坑 (REST/WebSocket) -- 新的握手/配置/模型排障结论 -- 真实 runtime 与旧实现不一致 -- OpenFang 特有问题 (Hands, Workflows, 安全层) -- 某个问题的最短排障路径已经明确 - -原则:**修完就记,避免二次踩坑。** - -### 8.4 文档质量检查清单 - -每次更新文档后,检查: - -- [ ] 文件路径引用正确 -- [ ] 技术术语统一 -- [ ] ICE 评分已更新 -- [ ] 成熟度等级已更新 -- [ ] 已知问题列表已更新 +- **修完就记** - 解决问题后立即更新文档 +- **面向未来** - 文档要帮助未来的开发者快速理解 +- **中文优先** - 所有面向用户的文档使用中文 --- -## 9. 常见高风险点 +## 9. 常见问题排查 -- 把前端本地 id 当作 OpenFang `agent_id` -- 只改 Zustand,不改 OpenFang 配置 -- 把 OpenClaw 协议字段发给 OpenFang -- fallback 逻辑覆盖真实错误 -- 直接手动解析 TOML,忽略格式容错 -- 让 UI 显示"已完成",实际只是 placeholder -- 混淆 OpenClaw 端口 (18789) 和 OpenFang 端口 (4200) +### 9.1 连接问题 + +1. 检查后端服务是否启动(端口 50051) +2. 检查 Vite 代理配置 +3. 检查防火墙设置 + +### 9.2 状态问题 + +1. 检查 Store 是否正确订阅 +2. 检查组件是否在正确的 Store 获取数据 +3. 检查是否有多个 Store 实例 + +### 9.3 配置问题 + +1. 检查 TOML 语法 +2. 检查环境变量是否设置 +3. 检查配置文件路径 --- -## 10. OpenFang 特有注意事项 - -### 10.1 Hands 系统 - -OpenFang 提供 7 个自主能力包: - -| Hand | 功能 | 触发方式 | -|------|------|----------| -| Clip | 视频处理、竖屏生成 | 手动/自动 | -| Lead | 销售线索发现 | 定时 | -| Collector | 数据收集聚合 | 定时/事件 | -| Predictor | 预测分析 | 手动 | -| Researcher | 深度研究 | 手动 | -| Twitter | Twitter 自动化 | 定时/事件 | -| Browser | 浏览器自动化 | 手动/工作流 | - -触发 Hand 时必须: -- 检查 RBAC 权限 -- 处理 `needs_approval` 状态 -- 记录审计日志 - -### 10.2 安全层 - -OpenFang 有 16 层安全防护,前端需要: - -- 正确处理认证失败 (Ed25519 + JWT) -- 尊重 RBAC 能力门控 -- 显示审计日志入口 -- 处理速率限制错误 - - -``` - ---- - -## 11. 常用命令 +## 10. 常用命令 ```bash +# 安装依赖 pnpm install -pnpm dev -pnpm tauri:dev + +# 开发模式 +pnpm start:dev + +# 仅启动桌面端 +pnpm desktop + +# 构建生产版本 pnpm build -pnpm setup -pnpm vitest run tests/desktop/chatStore.test.ts tests/desktop/gatewayStore.test.ts tests/desktop/general-settings.test.tsx + +# 类型检查 pnpm tsc --noEmit + +# 运行测试 +pnpm vitest run + +# 停止所有服务 +pnpm start:stop ``` --- -## 12. 参考文档 +## 11. 提交规范 -- `docs/openfang-technical-reference.md` - OpenFang 技术参考 -- `docs/openclaw-to-openfang-migration-brainstorm.md` - 迁移分析 -- `docs/DEVELOPMENT.md` - 开发指南 -- `skills/` - SKILL.md 技能示例 -- `hands/` - HAND.toml 配置示例 - ---- - -## 13. 提交信息建议 - -```text -(): +``` +(): ``` -示例: +**类型:** +- `feat` - 新功能 +- `fix` - 修复问题 +- `refactor` - 重构 +- `docs` - 文档更新 +- `test` - 测试相关 +- `chore` - 杂项 -```text -feat(openfang): add OpenFangClient with WebSocket support -feat(hands): add researcher hand trigger UI -fix(chat): align stream events with OpenFang protocol -fix(config): handle TOML format correctly -perf(gateway): optimize connection pooling -docs(knowledge-base): capture OpenFang RBAC permission issues +**示例:** +``` +feat(hands): 添加参数预设保存功能 +fix(chat): 修复流式响应中断问题 +refactor(store): 统一 Store 数据获取方式 ``` -推荐类型: - -- `feat` -- `fix` -- `refactor` -- `test` -- `docs` -- `chore` -- `perf` - --- -## 14. 迁移检查清单 +## 12. 安全注意事项 -从 OpenClaw 迁移到 OpenFang 时,确保: - -- [ ] 端口从 18789 改为 4200 -- [ ] 配置格式从 YAML/JSON 改为 TOML -- [ ] WebSocket URL 添加 `/ws` 路径 -- [ ] RPC 方法改为 REST API 或新 WebSocket 协议 -- [ ] 插件从 TypeScript 改为 SKILL.md -- [ ] 添加 Hands/Workflow 相关 UI -- [ ] 处理 16 层安全防护的交互 - ---- - -## 16. 参考文档更新 - -- `docs/features/README.md` - 功能索引和优先级矩阵 -- `docs/features/brainstorming-notes.md` - 头脑风暴记录 -- `docs/knowledge-base/openfang-technical-reference.md` - OpenFang 技术参考 -- `docs/knowledge-base/openfang-websocket-protocol.md` - WebSocket 协议 -- `docs/knowledge-base/troubleshooting.md` - 排障指南 -- `skills/` - SKILL.md 技能定义 -- `hands/` - HAND.toml 自主能力包 +- 不在代码中硬编码密钥 +- 用户输入必须验证 +- 敏感操作需要确认 +- 保留操作审计日志 diff --git a/config/config.toml b/config/config.toml index ae736ea..2b415c4 100644 --- a/config/config.toml +++ b/config/config.toml @@ -18,12 +18,12 @@ # ============================================================ [server] -# gRPC server host and port +# gRPC server host and port (default 4200 from runtime-manifest.json) host = "127.0.0.1" -port = 50051 +port = 4200 # WebSocket configuration -websocket_port = 50051 +websocket_port = 4200 websocket_path = "/ws" # CORS settings for desktop client diff --git a/desktop/package.json b/desktop/package.json index 2015eef..a59172a 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -22,6 +22,9 @@ "tauri:build:msi:debug": "pnpm prepare:openfang-runtime && node scripts/tauri-build-bundled.mjs --debug --bundles msi", "test": "vitest run", "test:watch": "vitest", + "test:e2e": "playwright test --project chromium --config=tests/e2e/playwright.config.ts", + "test:e2e:ui": "playwright test --project chromium-ui --config=tests/e2e/playwright.config.ts --grep 'UI'", + "test:e2e:headed": "playwright test --project chromium-headed --headed", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/desktop/src/App.css b/desktop/src/App.css index 85f7a4a..f54d45c 100644 --- a/desktop/src/App.css +++ b/desktop/src/App.css @@ -1,3 +1,7 @@ +/* ZCLAW Desktop App - Minimal Legacy Styles */ +/* Most styling is handled by Tailwind CSS and index.css design system */ + +/* Vite Logo Animation - Keep for any Vite default pages */ .logo.vite:hover { filter: drop-shadow(0 0 2em #747bff); } @@ -5,22 +9,8 @@ .logo.react:hover { filter: drop-shadow(0 0 2em #61dafb); } -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; - - color: #0f0f0f; - background-color: #f6f6f6; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} +/* Container utilities */ .container { margin: 0; padding-top: 10vh; @@ -46,71 +36,12 @@ justify-content: center; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - +/* Typography */ h1 { text-align: center; } -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { - cursor: pointer; -} - -button:hover { - border-color: #396cd8; -} -button:active { - border-color: #396cd8; - background-color: #e8e8e8; -} - -input, -button { - outline: none; -} - +/* Greet input utility */ #greet-input { margin-right: 5px; } - -@media (prefers-color-scheme: dark) { - :root { - color: #f6f6f6; - background-color: #2f2f2f; - } - - a:hover { - color: #24c8db; - } - - input, - button { - color: #ffffff; - background-color: #0f0f0f98; - } - button:active { - background-color: #0f0f0f69; - } -} diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 0835b17..822c9c4 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -55,7 +55,7 @@ function App() { const { connect, hands, approveHand, loadHands } = useGatewayStore(); const { activeTeam, setActiveTeam, teams } = useTeamStore(); - const { setCurrentAgent } = useChatStore(); + const { setCurrentAgent, newConversation } = useChatStore(); const { isNeeded: onboardingNeeded, isLoading: onboardingLoading, markCompleted } = useOnboarding(); useEffect(() => { @@ -190,6 +190,12 @@ function App() { setMainContentView(view); }; + // 处理新对话 + const handleNewChat = () => { + newConversation(); + setMainContentView('chat'); + }; + const handleSelectTeam = (teamId: string) => { const team = teams.find(t => t.id === teamId); if (team) { @@ -233,6 +239,7 @@ function App() { onMainViewChange={handleMainViewChange} selectedTeamId={selectedTeamId} onSelectTeam={handleSelectTeam} + onNewChat={handleNewChat} /> {/* 主内容区 */} @@ -252,7 +259,7 @@ function App() { animate="animate" exit="exit" transition={defaultTransition} - className="flex-1 overflow-hidden relative" + className="flex-1 overflow-hidden relative flex flex-col" > {mainContentView === 'automation' ? ( - {isHand ? 'Hand' : '工作流'} + {isHand ? '自主能力' : '工作流'} ); } diff --git a/desktop/src/components/Automation/AutomationPanel.tsx b/desktop/src/components/Automation/AutomationPanel.tsx index 9214008..a53818c 100644 --- a/desktop/src/components/Automation/AutomationPanel.tsx +++ b/desktop/src/components/Automation/AutomationPanel.tsx @@ -8,7 +8,7 @@ */ import { useState, useEffect, useCallback, useMemo } from 'react'; -import { useHandStore } from '../../store/handStore'; +import { useGatewayStore } from '../../store/gatewayStore'; import { useWorkflowStore } from '../../store/workflowStore'; import { type AutomationItem, @@ -28,6 +28,7 @@ import { Plus, Calendar, Search, + X, } from 'lucide-react'; import { useToast } from '../ui/Toast'; @@ -50,14 +51,14 @@ export function AutomationPanel({ onSelect, showBatchActions = true, }: AutomationPanelProps) { - // Store state - const hands = useHandStore(s => s.hands); - const workflows = useWorkflowStore(s => s.workflows); - const isLoadingHands = useHandStore(s => s.isLoading); - const isLoadingWorkflows = useWorkflowStore(s => s.isLoading); - const loadHands = useHandStore(s => s.loadHands); - const loadWorkflows = useWorkflowStore(s => s.loadWorkflows); - const triggerHand = useHandStore(s => s.triggerHand); + // Store state - use gatewayStore which has the actual data + const hands = useGatewayStore(s => s.hands); + const workflows = useGatewayStore(s => s.workflows); + const isLoading = useGatewayStore(s => s.isLoading); + const loadHands = useGatewayStore(s => s.loadHands); + const loadWorkflows = useGatewayStore(s => s.loadWorkflows); + const triggerHand = useGatewayStore(s => s.triggerHand); + // workflowStore for triggerWorkflow (not in gatewayStore) const triggerWorkflow = useWorkflowStore(s => s.triggerWorkflow); // UI state @@ -66,6 +67,8 @@ export function AutomationPanel({ const [viewMode, setViewMode] = useState('grid'); const [selectedIds, setSelectedIds] = useState>(new Set()); const [executingIds, setExecutingIds] = useState>(new Set()); + const [showWorkflowDialog, setShowWorkflowDialog] = useState(false); + const [showSchedulerDialog, setShowSchedulerDialog] = useState(false); const { toast } = useToast(); @@ -115,6 +118,15 @@ export function AutomationPanel({ setSelectedIds(new Set()); }, []); + // Workflow dialog handlers + const handleCreateWorkflow = useCallback(() => { + setShowWorkflowDialog(true); + }, []); + + const handleSchedulerManage = useCallback(() => { + setShowSchedulerDialog(true); + }, []); + // Execute handler const handleExecute = useCallback(async (item: AutomationItem, params?: Record) => { setExecutingIds(prev => new Set(prev).add(item.id)); @@ -173,8 +185,6 @@ export function AutomationPanel({ toast('数据已刷新', 'success'); }, [loadHands, loadWorkflows, toast]); - const isLoading = isLoadingHands || isLoadingWorkflows; - return (
{/* Header */} @@ -198,12 +208,14 @@ export function AutomationPanel({ +
+
+
+
+ + +
+
+ +