Commit 0ddb0a2e authored by edy's avatar edy

文档提交

parent bb9bdf66
Pipeline #18500 failed
......@@ -42,3 +42,7 @@ skills/**/api_key.txt
workspace/
# 备份缓存目录
backup-cache/
# 文档目录
docs/
docs-backup-*/
# Commercial Product Blueprint
## 1. Purpose
This document defines the final commercial product shape for `QianjiangClaw`.
It answers the high-level product questions that should not drift during implementation:
- What is the final product being sold
- How customers get access
- How login, entitlement, enterprise seats, and credits work
- Why skills are cloud-orchestrated
- What belongs to the desktop app, cloud services, and bundled OpenClaw runtime
If implementation details conflict with this document, this document wins.
---
## 2. Final Product Goal
The final product goal is locked as:
- the deliverable is a `Windows installer`
- the installer eventually `bundles OpenClaw runtime`
- customers must `sign in with a company account`
- entitlement uses `always-online verification`
- commercial packaging starts with `enterprise seats + platform credits`
- the platform grants `trial credits / initial credits`
- model usage is `platform-managed and platform-routed` by default
- Douyin/Xiaohongshu and similar capabilities are exposed as `skills`
- skills use `cloud orchestration + local presentation/execution entry`
- user profile, organization info, credit balance, and usage history are fetched through `cloud APIs`
The final product is not just a local chat client.
It is:
`a commercially sellable Windows AI desktop product with account, entitlement, and credits systems`
---
## 3. Target User Journey
The target end-state flow is:
1. Customer installs the company installer
2. Customer launches the desktop app
3. Customer signs in with a company-issued account
4. The app verifies online:
- user validity
- organization validity
- seat availability
- subscription/feature entitlement
- credit availability
5. After login and entitlement pass, the customer enters the app
6. The app fetches and displays through APIs:
- user profile
- organization/package/seat status
- current credit balance
- recent usage and consumption history
- skill catalog and allowed skills
7. The customer uses chat, skills, task execution, results, logs, and diagnostics directly
8. If credits are insufficient or entitlement becomes invalid, the UI refreshes the latest server state and explains the reason
For non-technical users, the intended experience is:
- no separate OpenClaw installation
- no local environment setup
- no command line usage
- no need to configure provider/model/API keys manually
- no exposure to Gateway, runtime, Node, or WSL2 concepts
---
## 4. Commercial Model
### 4.1 Access Model
Use:
- `account login`
- `enterprise seats`
- `online entitlement verification`
Do not use offline license keys as the primary control path.
Default rules:
- users must log in before using the product
- each user belongs to an organization
- organizations own subscriptions and seats
- the server decides whether the current user may continue using the product
### 4.2 Connectivity Policy
Use:
- `always-online`
Default behavior:
- login requires network access
- startup requires online entitlement and credit verification
- the app refreshes entitlement, credits, and package state during use
- if entitlement becomes invalid or credits are insufficient:
- new tasks are blocked
- the UI clearly explains the reason
- the server remains the source of truth
### 4.3 Billing Model
Phase 1 commercial delivery uses:
- `platform-issued credits`
- `platform-paid model usage`
- `no customer-provided API key by default`
Meaning:
- the company grants credits to users or organizations
- chat, skills, and task execution consume platform credits
- users see balances, usage, and package information instead of underlying model secrets
- the platform manages providers, routing, and metering centrally
Later expansion can add:
- organization-level credit packages
- package-based monthly credit allocations
- skill-specific consumption rules
- recharge, gifting, compensation, and promotional credits
The default commercial path is no longer `customer-provided API keys`.
---
## 5. Skill Product Direction
The final product can evolve into a business-task AI worker platform.
Examples:
- Douyin operations
- Xiaohongshu operations
- topic planning
- title generation
- comment analysis
- pre-publish review
- multi-platform distribution
These capabilities should not be hardcoded into the desktop app.
They should be delivered through a `Skill Platform`.
Skill design principles:
- business definitions live in the cloud
- workflow orchestration lives in the cloud
- parameter rules live in the cloud
- permissions live in the cloud
- the desktop app collects input, triggers execution, and presents results
This allows:
- faster business iteration without shipping a new installer each time
- skill visibility controlled by organization, package, seat, and credit state
- cleaner commercial control later
---
## 6. System Architecture
### 6.1 Desktop Responsibilities
The desktop app is responsible for:
- installed user entry point
- sign-in
- entitlement status
- user profile and credit display
- chat entry
- skill catalog and run screens
- settings
- logs and diagnostics
- local runtime state display
The core boundary remains:
`Renderer -> preload -> Electron Main -> GatewayClient / RuntimeManager / AuthClient / ProfileClient / CreditClient / SkillClient`
### 6.2 Cloud Responsibilities
The cloud side must at least cover:
- authentication
- user profile APIs
- organizations
- seat management
- subscriptions and entitlements
- credit wallet / balance / grant rules
- credit consumption and usage history
- skill registry and delivery
- workflow orchestration
- provider routing and model metering
- audit and event logging
The first implementation does not need to be a microservice architecture, but these domain boundaries must stay clear.
### 6.3 Local Runtime Responsibilities
In the final product, the local OpenClaw runtime is responsible for:
- being bundled by the installer
- first-run initialization
- local health checks
- session/task hosting
- log output
- stable local Gateway capability
It does not own commercial entitlement, credits, or profile state. Those belong to cloud APIs.
---
## 7. Delivery Phases
### Phase 1: Commercial Transition MVP
Goal:
- add login, entitlement, and credits shell on top of the current external-OpenClaw desktop app
Focus:
- sign-in page
- online entitlement checks
- block core features when unauthorized
- profile API integration
- credit balance / usage API integration
- secure auth token storage
- diagnostics
Result:
- customers must log in with company accounts before using the app
- customers can see profile and credit state in the desktop app
- customers do not need to provide model/API keys
### Phase 2: Enterprise Seats, Credits, and Skill Platform
Goal:
- move from simple login gating to enterprise-seat, package, and credit-based control
Focus:
- organization/member/seat model
- entitlement fetch
- credit account and deduction rules
- cloud skill catalog
- skill forms and parameter definitions
- skill execution entry points
Result:
- different organizations see different capabilities
- capability boundaries can depend on package, seat, and credit state
### Phase 3: Installer Delivery
Goal:
- stabilize the Windows installer delivery path
Focus:
- installer startup reliability
- login, entitlement, profile, credits, and skill APIs in installed environments
- correct token, log, and diagnostics paths
Result:
- the installer can be sent to customers for trial use
### Phase 4: Bundled Runtime PoC
Goal:
- validate that the installer can reliably ship and run bundled OpenClaw runtime
Focus:
- fixed runtime directory layout
- `RuntimeManager` start/stop/health/logs
- first-run initialization
- bundled runtime becomes the preferred local path
- login, credits, and skills continue to work through APIs in bundled-runtime mode
Result:
- the app can run on machines without preinstalled OpenClaw
### Phase 5: Final Commercial Release
Goal:
- non-technical customers can install and use the product directly
Focus:
- bundled runtime
- company account login
- enterprise seat control
- online entitlement
- cloud-driven profile and credits display
- platform-managed model usage
- package-, org-, and credit-based skill access
Result:
- a commercially ready, one-install desktop AI product
---
## 8. Required Capability Additions
Desktop side needs:
- `AuthClient`
- `ProfileClient`
- `CreditClient`
- `EntitlementClient`
- `SkillClient`
- production `SecretManager (keytar)` for auth/session tokens and other local sensitive data
- production `RuntimeManager`
Cloud side needs:
- login/token service
- profile service
- organization/seat/subscription service
- entitlement decision service
- credit wallet and ledger service
- model metering service
- skill registry/publishing service
- skill orchestration service
- audit/event service
---
## 9. AI Usage Rules
Any future AI agent should read, in order:
1. `docs/product-roadmap.zh-CN.md` or `docs/product-roadmap.md`
2. `docs/commercial-product-blueprint.zh-CN.md` or `docs/commercial-product-blueprint.md`
3. `docs/progress-summary.md`
The AI must keep these facts fixed:
- the current external Gateway path is transitional only
- the final product must bundle runtime in the installer
- the final product must include company login, enterprise entitlement, and credits
- profile, credit, and skill state come from cloud APIs
- the default product path does not require customer-provided API keys
- the final product must support commercial skill delivery
- every implementation change must be explainable as part of a defined phase
If future work drifts into:
- only building a chat tool
- ignoring login/entitlement
- ignoring enterprise seats
- ignoring credit accounts and profile APIs
- ignoring cloud skill orchestration
- treating external Gateway as the final product
- keeping customer-provided API keys as the default model
then it is off-plan and must be corrected.
# QianjiangClaw Commercial Product Blueprint
Updated: 2026-03-23
## 1. Product Direction
Current commercial direction is:
- The Windows installer remains the single user-facing entry point.
- Bundled OpenClaw runtime ships inside the installer.
- The first real delivery milestone in Stage D is not full business cloud integration.
- The first real delivery milestone is: installer can become truly usable by letting the user bind an OpenClaw employee `api_key`, fetch runtime config from cloud, and start bundled runtime automatically.
## 2. Product Principles
Current fixed principles:
- The user must first bind an OpenClaw employee `api_key` before OpenClaw can be used.
- This `api_key` is the runtime employee identity key, not the upstream model vendor key.
- The user should not manually enter provider, model, base_url, model vendor api_key, local Node path, OpenClaw entry path, or Gateway token in the normal product flow.
- The desktop app stores the employee `api_key` in Electron Main secret storage.
- Renderer remains a UI layer only and does not own raw credentials.
## 3. First Real Usable Flow
Stage D step 1 user flow should be:
1. User installs the Windows app.
2. User opens the desktop app.
3. User goes to settings or first-run activation view.
4. User binds an OpenClaw employee `api_key`.
5. Electron Main calls `POST /openclaw-employee-config` with `{ api_key, action: "init" }`.
6. Main receives cloud runtime payload.
7. Main generates local managed runtime config.
8. Bundled runtime starts.
9. Gateway becomes available.
10. User can start using the installer product.
## 4. Required Runtime Cloud APIs
Current required runtime-side APIs are:
- `POST /openclaw-employee-config`
- `POST /openclaw-heartbeat`
- `POST /openclaw-employee-events`
For Stage D step 1, only the first endpoint is required for initial delivery.
## 5. Runtime Config Strategy
The current runtime config strategy is:
- user provides employee `api_key`
- cloud returns runtime payload
- desktop app writes local managed config
- bundled runtime starts from that managed config
The `openclaw-employee-config` payload should provide business-controlled runtime fields such as:
- `persona_prompt`
- `welcome_message`
- `work_hours`
- `auto_reply_rules`
- `resource_quota`
- `llm.model_id`
- `llm.temperature`
- `llm.max_tokens`
- `llm.provider.base_url`
- `llm.provider.api_key`
- `skills`
- `channels`
- `config_version`
- `endpoints.heartbeat`
- `endpoints.events`
The desktop app should still add local machine-specific values such as:
- workspace path
- runtime state/log paths
- bundled Gateway bind and port
- local managed config output path
## 6. Security Rules
- Employee `api_key` stays in Electron Main and secret storage.
- Raw `api_key` must not be exposed to Renderer.
- Diagnostics export must not dump raw credentials.
- If employee `api_key` is missing, bundled runtime must not pretend to be available.
- If `openclaw-employee-config` fetch fails, bundled runtime must not start.
## 7. What Is Not Stage D Step 1 Yet
Not part of this first delivery slice:
- full product-side login, account, credits, and Skills production integration
- heartbeat and event reporting implementation
- full entitlement or billing product UX
- final release polish such as icon, signing, and upgrade path
## 8. Acceptance for This First Delivery Slice
Stage D step 1 should only be considered complete when:
- user can bind an OpenClaw employee `api_key`
- app can successfully call `POST /openclaw-employee-config`
- app can generate managed runtime config from real cloud payload
- bundled runtime starts from that config
- installer becomes truly usable without manual provider/model/base_url configuration
# QianjiangClaw Product Roadmap
Updated: 2026-03-23
## 1. Final Goal
The final product goal is locked as:
- Deliver a `Windows installer`
- The installer `bundles OpenClaw runtime`
- End users do `not` need to preinstall OpenClaw, Node, WSL2, or use the command line
- After install, users only need to:
- open the desktop app
- sign in with a company account
- fetch profile, credit status, and skills through cloud APIs
- start chatting and using skills directly
This means the final product is:
- not a Gateway connector only
- not a dev-only desktop shell
- not a "bring your own OpenClaw" tool
- not a desktop shell that expects users to configure provider/model/API keys by default
The final product is:
- a non-technical-user-friendly Windows desktop AI app
- with OpenClaw runtime bundled and managed by the installer
- with login, credits, and skills driven by cloud APIs
## 2. Fixed Product Decisions
These decisions are considered locked unless explicitly changed later:
- Use `Electron + React + TypeScript + pnpm workspace`
- Keep process boundaries:
- `Renderer -> preload -> Electron Main -> GatewayClient / RuntimeManager / AuthClient / ProfileClient / CreditClient / SkillClient`
- Current external-Gateway mode is a transition phase, not the final form
- The final runtime delivery model is:
- `installer bundles OpenClaw runtime`
- The target bundled runtime direction is:
- `native Windows runtime`
- `runtime-manager` will return to the main path in the bundled-runtime phase
- The desktop app remains the single user-facing entry point
- The default product path does not require customer-provided API keys
- Profile, credit status, skill catalog, and skill availability are fetched from `cloud APIs`
- Platform credits are the default consumption model, and model usage is platform-managed
## 3. Current State
What is already true now:
- Desktop shell starts successfully
- Real preload/IPC path works
- The UI can connect to local OpenClaw Gateway
- Sessions/history work
- One real `chat.send` from the Electron UI path has already succeeded
- Smoke harness is now gated behind explicit smoke mode
Current reality:
- The desktop app is no longer just a skeleton
- The transition-phase desktop client is already functional on this machine
- The remaining work is about installer delivery, commercial API integration, and eventually bundled runtime
## 4. Delivery Stages
### Stage A: Transition MVP Cleanup
Goal:
- Turn the current desktop client into a stable, clean MVP baseline
Required outcomes:
- UI has:
- loading state
- error state
- empty state
- reconnect guidance
- chat sending state
- Logs are readable enough for debugging
- Secret storage is upgraded from dev-file fallback to `keytar`
- Diagnostics export exists
- Smoke harness stays opt-in only and does not affect normal usage
Success criteria:
- On a machine with local OpenClaw already running, the app can:
- launch
- connect
- show status
- list sessions
- show history
- send chat
- export diagnostics
### Stage B: Installer MVP
Goal:
- Prove the Windows installer delivery path before bundled runtime is introduced
Required outcomes:
- Build a working Windows installer
- Installer installs the desktop app correctly
- Installed app launches correctly
- Installed app still works against an already installed OpenClaw environment
- Installed app can also complete:
- login
- entitlement checks
- profile fetch
- credit fetch
- skill catalog and availability fetch
Success criteria:
- On a test machine with existing OpenClaw:
- install succeeds
- app launches after install
- preload and renderer assets load correctly
- login and entitlement work
- profile, credits, and skills load from APIs
- connection and chat still work
### Stage C: Bundled Runtime PoC
Goal:
- Prove that bundled native Windows OpenClaw runtime can be shipped and started by the desktop app
Required outcomes:
- Define a fixed bundled runtime directory layout
- `runtime-manager` supports:
- detect
- resolve paths
- start
- stop
- restart
- health
- tail logs
- Desktop app can run in bundled-runtime mode
- Desktop app no longer depends on user-installed OpenClaw in this mode
- Login, credits, and skills continue to work through APIs in bundled-runtime mode
Success criteria:
- On a clean Windows machine without OpenClaw preinstalled:
- installer installs successfully
- bundled runtime can be detected
- bundled runtime can start
- desktop app can connect to its bundled Gateway
- login, profile, credits, and skills still work
- chat works
- failures are diagnosable
### Stage D: One-Click Product Version
Goal:
- Turn the bundled-runtime PoC into the real end-user product
Required outcomes:
- Installer includes runtime payload
- First launch handles initialization automatically
- Desktop app defaults to bundled-runtime mode
- Versioning and diagnostics are stable enough for user delivery
- Login, profile, credits, and skills are fully cloud-driven
Success criteria:
- A non-technical user can:
- install the app
- open it
- sign in
- see profile, credits, and available skills
- use chat and skills
- without manually installing any OpenClaw dependency
- without configuring provider/model/API keys manually
## 5. Execution Order
Do the work in this order:
1. Finish Stage A cleanup
2. Build and verify Stage B installer MVP
3. Implement and validate Stage C bundled-runtime PoC
4. Turn the PoC into Stage D production-style bundled delivery
Do not skip ahead:
- Do not make bundled runtime the immediate next step before installer MVP is stable
- Do not promise one-click install until bundled-runtime PoC passes on a clean machine
- Do not move secret, entitlement, credits, or skill API logic into renderer
## 6. Stage A Implementation Checklist
This is the next practical work queue:
1. UI state completion
- gateway loading state
- gateway error state
- no-session state
- no-message state
- chat sending state
- reconnect hint text
2. Logs cleanup
- strip ANSI escape codes
- strip control characters
- normalize noisy log text
- prefer readable summary text over raw noisy payloads when possible
3. SecretManager upgrade
- replace dev-file secret storage with `keytar`
- keep a safe fallback strategy only when absolutely needed for development
4. Diagnostics export
- export config summary
- export gateway/runtime status
- export recent logs
- export version/path metadata
5. Keep smoke tooling opt-in only
## 7. Stage B Implementation Checklist
1. Finalize `electron-builder` config
2. Verify install paths and asset paths
3. Verify installed app launch behavior
4. Integrate and verify login / entitlement / profile / credits / skills APIs
5. Verify installed app can still connect to an existing OpenClaw environment
6. Record installer smoke-test checklist and failure cases
## 8. Stage C Implementation Checklist
1. Define bundled runtime layout
2. Restore `runtime-manager` as a primary runtime subsystem
3. Add bundled-runtime mode detection and boot flow in Electron Main
4. Add runtime health and lifecycle UI exposure
5. Keep login, credits, and skills APIs working in bundled-runtime mode
6. Validate bundled runtime on a clean Windows machine
## 9. Acceptance Criteria
### Transition MVP accepted when:
- Desktop UI is stable enough for repeated local use
- Errors and empty states are understandable
- Secrets are not stored in the current dev-file-only path
- Diagnostics export works
### Installer MVP accepted when:
- Installer installs cleanly
- Installed app launches
- Installed app works with an already-installed OpenClaw environment
- Installed app displays profile, credits, and skills from APIs correctly
### Bundled Runtime PoC accepted when:
- A clean machine without OpenClaw can use the app after install
- Runtime lifecycle is manageable from the app
- Runtime failures can be diagnosed
- Login, credits, and skills do not regress in bundled-runtime mode
### Final product accepted when:
- User installs once and can use the app directly
- User does not need to install OpenClaw separately
- User does not need to provide model/API keys
- Desktop app is the only visible entry point
## 10. Important Boundaries For Future AI Work
Any future AI agent continuing this project should assume:
- The current external-Gateway implementation is transitional
- The final business/product direction is bundled runtime inside the installer
- The desktop app is not meant to stay a connector-only tool
- `runtime-manager` must come back as a first-class subsystem later
- Electron Main remains responsible for:
- runtime process management
- config management
- auth/session token management
- diagnostics
- API client orchestration
- Renderer should stay UI-only and not directly own secrets, runtime control, or cloud entitlement logic
- Skill catalog and availability should come from APIs, not be hardcoded into the installer
## 11. Recommended Next Task
The recommended next implementation task is:
- finish Stage A productization work, then move directly into installer smoke testing plus commercial API integration
Concrete next steps:
1. installer smoke test
2. login / entitlement API integration
3. profile / credits / skills API integration
4. then move to bundled-runtime work
# QianjiangClaw Product Roadmap
Updated: 2026-03-23
## 1. Current Delivery Order
Current delivery order should be treated as:
- Stage A: complete
- Stage B: complete
- Stage C technical PoC: complete
- Stage D step 1: make the installer truly usable through runtime cloud config integration
- later Stage D work: heartbeat, events, business cloud APIs, and full commercial UX
## 2. Stage D Step 1 Goal
Stage D step 1 goal is now fixed as:
- the installer becomes truly usable
- bundled OpenClaw starts from real cloud-fetched runtime config
- user first binds an OpenClaw employee `api_key`
- app automatically pulls runtime config and starts bundled runtime
This means the current top priority is not full business API integration.
The top priority is runtime usability for the installer.
## 3. Must-Integrate Runtime APIs for Step 1
Current must-integrate runtime API for this first slice:
- `POST /openclaw-employee-config`
Later runtime APIs:
- `POST /openclaw-heartbeat`
- `POST /openclaw-employee-events`
## 4. Step 1 Implementation Order
Implementation should happen in this order:
1. add user-facing binding flow for OpenClaw employee `api_key`
2. store that key in `SecretManager/keytar`
3. implement `OpenClawConfigClient` for `POST /openclaw-employee-config`
4. fetch config with `{ api_key, action: "init" }`
5. transform returned payload into managed local runtime config
6. start bundled runtime from that generated config
7. verify installer startup and usable state
## 5. What Comes After Step 1
Only after installer usability is proven, continue with:
- `POST /openclaw-heartbeat`
- `POST /openclaw-employee-events`
- product-side login/profile/credits/Skills real production integration
- entitlement and billing UX
- clean-machine validation and formal release polish
## 6. Acceptance Gates for Step 1
Stage D step 1 is complete only when:
- user can bind an OpenClaw employee `api_key`
- missing key prevents runtime startup with a clear UI message
- `POST /openclaw-employee-config` succeeds with a real key
- managed runtime config is generated from returned payload
- bundled runtime starts from that config
- installer is usable without manual provider/model/base_url/vendor api_key setup
## 7. Key Product Rule
The product rule is now:
- users still should not manually configure provider, model, base_url, or vendor model api_key
- but users must first bind the OpenClaw employee `api_key` that activates their runtime instance
# QianjiangClaw Architecture
Updated: 2026-03-23
## 1. Stable Desktop Boundary
The stable boundary remains:
- Renderer
- UI only
- does not own raw runtime credentials
- does not directly start runtime processes
- preload
- exposes controlled desktop APIs to Renderer
- Electron Main
- owns secret access
- owns runtime cloud fetch
- owns managed runtime config generation
- owns bundled runtime lifecycle
- GatewayClient / RuntimeManager / cloud clients
## 2. Current Runtime-Cloud Design
Stage D step 1 uses a runtime-first cloud integration model.
The current key rule is:
- the user must first bind an OpenClaw employee `api_key`
- Electron Main uses that key to call `POST /openclaw-employee-config`
- the returned payload becomes the source of truth for runtime business configuration
This `api_key` is not the LLM vendor key. It is the OpenClaw employee identity credential.
## 3. Required Runtime-Side Clients
Main should include or add these runtime-side pieces:
- `OpenClawConfigClient`
- calls `POST /openclaw-employee-config`
- fetches runtime payload for startup
- later:
- heartbeat client for `POST /openclaw-heartbeat`
- event client for `POST /openclaw-employee-events`
## 4. Startup Flow for Stage D Step 1
Installer startup should follow this order:
1. Desktop app loads local settings.
2. Desktop app reads stored employee `api_key` from secret storage.
3. If missing, runtime startup is blocked and UI asks the user to bind the key.
4. If present, Main calls `POST /openclaw-employee-config` with:
- `api_key`
- `action = "init"`
5. Main validates returned runtime payload.
6. Main merges cloud payload with local machine-specific fields.
7. Main writes local managed runtime config.
8. `RuntimeManager.start()` launches bundled runtime using that managed config.
9. Gateway becomes available for the desktop product.
## 5. Runtime Config Responsibility Split
Cloud payload should provide runtime business configuration such as:
- `persona_prompt`
- `welcome_message`
- `work_hours`
- `auto_reply_rules`
- `resource_quota`
- `llm.model_id`
- `llm.temperature`
- `llm.max_tokens`
- `llm.provider.name`
- `llm.provider.base_url`
- `llm.provider.api_key`
- `skills`
- `channels`
- `config_version`
- runtime reporting endpoints
Desktop Main should still provide local host-specific fields such as:
- workspace path
- runtime state dir
- runtime logs dir
- managed config output path
- bundled Gateway bind and port
- generated gateway auth token for local control path
## 6. Security Rules
- Employee `api_key` lives in `SecretManager/keytar`.
- Renderer never receives raw `api_key`.
- Managed runtime config may contain effective runtime credentials, but diagnostics must not export raw sensitive values.
- If cloud config fetch fails, bundled runtime must remain unavailable instead of fake-ready.
## 7. Current State
Already in place:
- installer packaging
- bundled runtime payload delivery
- runtime detect/start/stop/restart/health/tailLogs
- managed config generation path exists locally
- diagnostics and UI pages already exist
Still missing for Stage D step 1:
- user-bound employee `api_key` flow
- `OpenClawConfigClient` wired to `POST /openclaw-employee-config`
- managed config generation from cloud payload
- real installer usability based on cloud-driven startup
# macOS Apple Silicon DMG 改造详细计划
## 1. 背景与目标
本计划基于当前 `dev` 分支代码事实制定,目标是把现有 Windows OpenClaw 客户端安装包链路改造成 **只面向 macOS Apple Silicon 的内部测试 DMG 包**
本次改造不做双平台共存,不保留 Windows NSIS 安装器作为目标链路,也不纳入 Apple Developer ID 签名、公证、staple 等正式外部分发能力。阶段性目标是先让 M 芯片 Mac 上可以构建、安装、启动并跑通 bundled runtime 与 workspace execution。
### 当前代码事实
> **注意**:以下为改造前的初始状态,用于记录基线。实际改造进度见第 9 节。
- `apps/desktop/electron-builder.yml` 目前只配置了 `win``nsis`,产物命名为 `${productName}-Setup-${version}.${ext}`
- `apps/desktop/electron-builder.yml` 引用了 `build/icons/brand-icon.ico`,但当前仓库未发现实际图标文件。
-`package.json``materialize:runtime` 指向 `build/scripts/materialize-runtime-payload.ps1`,是 PowerShell/Windows-only 链路。
-`package.json` 的大部分 smoke 命令都依赖 `.ps1``.exe`、NSIS installer 或 Windows Electron 路径。
- `packages/runtime-manager/src/index.ts` 写死了 `node/node.exe``python/python.exe``ffmpeg.exe``ffprobe.exe``python/Scripts`、PowerShell EPERM fallback 和 `taskkill`
- `apps/desktop/src/main/services/project-workspace-executor.ts` 同样包含 PowerShell wrapper fallback、Windows PATH 扩展名逻辑和 `python/Scripts` 注入。
- `vendor/openclaw-runtime` 目前只有 README 占位,README 描述的是 Windows runtime 布局。
- packaged runtime 路径解析已经适合 macOS 包内资源:`apps/desktop/src/main/index.ts` 会在 packaged 模式使用 `process.resourcesPath/vendor/openclaw-runtime`
### 总体成功标准
- 在 Apple Silicon macOS 上运行 `corepack pnpm run package:mac` 能产出 `.dmg`
- DMG 内 `.app` 包含 `vendor/openclaw-runtime` mac arm64 runtime。
- `.app` 可启动并进入主界面。
- RuntimeManager 能把 bundled runtime 判定为 `ready`
- managed OpenClaw Gateway 能启动并通过 readiness probe。
- workspace execution 能用 bundled Node/Python 跑通主路径。
- Playwright Chromium 能从包内 `playwright-browsers` 被 Python runtime 使用。
- 关闭应用后 managed runtime 能退出,不遗留 Gateway 占用端口。
## 2. 改造边界与默认决策
### 平台边界
- 唯一目标平台:`darwin-arm64`
- 不支持 Windows。
- 不支持 Intel Mac。
- 不做 universal app。
- 不做 macOS x64 fallback。
### 发布边界
- 目标产物:内部测试 `.dmg`
- 不配置正式签名。
- 不配置 notarization。
- 不配置 auto update。
- 允许首次启动时由测试人员手动绕过 Gatekeeper。
### 代码边界
- 保留 Electron 主进程、preload、UI、runtime-manager、workspace execution、OpenClaw package、Python skills。
- 废弃 Windows 打包目标、NSIS、installer.nsh、Windows installer smoke、PowerShell runtime materialize 主入口。
- Windows 相关历史脚本可暂时保留在仓库中,但 mac 分支的正式 scripts 不再引用它们。
### Runtime 目录标准
mac-only 分支统一采用以下布局:
```text
vendor/openclaw-runtime/
node/bin/node
openclaw/index.js
openclaw/package/openclaw.mjs
openclaw/package/package.json
config/openclaw.json
python/bin/python3
python/python-manifest.json
python/runtime-requirements.lock.txt
ffmpeg/bin/ffmpeg
ffmpeg/bin/ffprobe
playwright-browsers/
runtime-manifest.json
README.md
```
## 3. 分阶段实施计划
### Phase 0: 建立实施基线
目标:在开始改代码前固定当前事实和验收边界,避免后续实施时重新决策。
实施内容:
- 确认当前分支为 `dev`,并从这里切出 mac-only 工作分支。
- 记录本计划中的平台边界:只做 `darwin-arm64`
- 确认本次不处理正式签名、公证、自动更新。
- 确认 runtime 物料来源要求:
- Node 使用本机 mac arm64 Node。
- Python 使用可复制的 mac arm64 Python 根目录。
- ffprobe 必须来自 PATH 或显式参数。
- OpenClaw package 必须能从本机安装位置解析。
阶段性目标:
- 有一个明确的 mac-only 工作分支。
- 所有人对“不会保留 Windows 打包兼容”有一致预期。
- 后续实现不再为 Windows/NSIS/PowerShell 设计抽象层。
验收标准:
- `git status --short --branch` 显示工作分支清晰。
- 文档中已记录 mac-only、内部测试 DMG 两个关键决策。
### Phase 1: 改造 Electron Builder 打包配置
目标:把当前 Windows installer 配置改成只生成 macOS arm64 DMG。
涉及文件:
- `apps/desktop/electron-builder.yml`
- `apps/desktop/package.json`
-`package.json`
实施内容:
- 重写 `apps/desktop/electron-builder.yml`
- 保留 `appId``productName``compression``asar``asarUnpack``directories.output``files``extraResources`
- 删除 `win` 配置。
- 删除 `nsis` 配置。
- 删除 `afterPack: build/hooks/after-pack-branding.cjs`
- 删除 `build/icons/brand-icon.ico` extraResource。
-`artifactName` 改成 `${productName}-${version}-mac-arm64.${ext}`
- 新增 `mac.target: dmg`
- 新增 `mac.arch: arm64`
- 新增 `mac.category: public.app-category.productivity`
- 内部测试阶段设置 `mac.identity: null` 或通过 electron-builder CLI 禁用 signing。
- 暂不强制配置 `mac.icon`,避免当前缺少 `.icns` 阻塞。
- DMG 配置采用最小可用布局:
- `dmg.title: ${productName} ${version}`
- `dmg.contents` 包含 app 和 Applications link。
-`package.json` 新增:
- `materialize:runtime:mac`
- `package:mac`
- `apps/desktop/package.json` 新增或调整:
- `package:mac`
推荐命令语义:
```json
{
"materialize:runtime:mac": "node build/scripts/materialize-runtime-payload.mjs",
"package:mac": "corepack pnpm run materialize:runtime:mac && corepack pnpm build && corepack pnpm --filter @qjclaw/desktop exec electron-builder --config electron-builder.yml --mac dmg --arm64"
}
```
阶段性目标:
- electron-builder 不再尝试生成 `.exe` 或 NSIS installer。
- macOS 上可以进入 electron-builder 的 mac target 构建流程。
- 打包命令名称与产物目标清晰对应。
验收标准:
- `rg -n "nsis|win:|Setup|brand-icon.ico|after-pack-branding" apps/desktop/electron-builder.yml` 没有命中。
- `corepack pnpm --filter @qjclaw/desktop exec electron-builder --config electron-builder.yml --mac dmg --arm64 --dir` 至少能进入 mac packaging 阶段。
### Phase 2: 新建 mac runtime materialize 脚本
目标:替代 PowerShell runtime 物料生成链路,生成 mac arm64 可运行的 `vendor/openclaw-runtime`
涉及文件:
- `build/scripts/materialize-runtime-payload.mjs`
- `build/runtime/python/runtime-requirements.lock.txt`
- `vendor/openclaw-runtime/README.md`
-`package.json`
实施内容:
- 新建 Node 脚本 `build/scripts/materialize-runtime-payload.mjs`
- 脚本启动时校验:
- `process.platform === "darwin"`
- `process.arch === "arm64"`
- 支持参数和环境变量:
- `--runtime-dir`
- `--source-config-path`
- `--source-node`
- `--source-python-root`
- `--source-openclaw-entry`
- `--source-ffprobe`
- `--gateway-port`
- `--gateway-token`
- `QJCLAW_MAC_PYTHON_ROOT`
- 默认值:
- runtime dir: `vendor/openclaw-runtime`
- config: `~/.openclaw/openclaw.json`
- requirements: `build/runtime/python/runtime-requirements.lock.txt`
- gateway port: `18889`
- gateway token: `qjc-bundled-runtime-token`
- 生成 staging 目录,全部成功后再原子替换 `vendor/openclaw-runtime`
- 复制 Node:
- 解析 `node` 命令。
- 放入 `node/bin/node`
- `chmod 755`
- 复制 OpenClaw package:
- 优先从 `--source-openclaw-entry` 解析。
- 否则从 PATH 中 `openclaw` 解析。
- 拷贝 package 到 `openclaw/package`
- 生成 `openclaw/index.js` wrapper,按 manifest 载入 `openclaw/package/openclaw.mjs`
- 复制并改写 config:
- 读取源 `openclaw.json`
- 强制设置 `gateway.mode = "local"`
- 强制设置 `gateway.bind = "loopback"`
- 设置 `gateway.port` 和 token。
- 写入 `config/openclaw.json`
- 复制 Python:
-`--source-python-root``QJCLAW_MAC_PYTHON_ROOT` 获取完整 Python 根目录。
- 要求存在 `bin/python3`
- 拷贝到 `python/`
- `chmod 755 python/bin/python3`
- 执行 `python/bin/python3 -m pip install --disable-pip-version-check --upgrade pip`
- 执行 `python/bin/python3 -m pip install --disable-pip-version-check -r build/runtime/python/runtime-requirements.lock.txt`
- 设置 `PLAYWRIGHT_BROWSERS_PATH` 后执行 `python/bin/python3 -m playwright install chromium`
- 复制 ffmpeg/ffprobe:
- `ffmpeg` 优先从 `imageio_ffmpeg` Python package 的 binaries 目录解析。
- `ffprobe``--source-ffprobe` 或 PATH 解析。
- 放入 `ffmpeg/bin/ffmpeg``ffmpeg/bin/ffprobe`
- 两者都 `chmod 755`
- 生成 `python/python-manifest.json`
- Python 版本。
- executable: `bin/python3`
- requirementsPath: `runtime-requirements.lock.txt`
- requestedPackages。
- resolvedPackages。
- 生成 `runtime-manifest.json`
- platform: `darwin`
- arch: `arm64`
- bundledNodeExecutable: `node/bin/node`
- packagedOpenClawEntry: `openclaw/package/openclaw.mjs`
- defaultConfigPath: `config/openclaw.json`
- pythonExecutable: `python/bin/python3`
- pythonManifestPath: `python/python-manifest.json`
- requirementsPath: `python/runtime-requirements.lock.txt`
- ffmpegExecutable: `ffmpeg/bin/ffmpeg`
- ffprobeExecutable: `ffmpeg/bin/ffprobe`
- playwrightBrowsersPath: `playwright-browsers`
- gatewayPort
- gatewayToken
- materializedAt
- materializationKey
- sizeBytes
- fileCount
- 清理低风险冗余文件:
- `__pycache__`
- `*.pyc`
- `*.pyo`
- OpenClaw sourcemap
- 非模板 docs,保留 workspace bootstrap 需要的 templates。
阶段性目标:
- macOS 上可以通过一个 Node 脚本生成完整 runtime payload。
- 生成的 runtime 布局与后续 runtime-manager 路径完全一致。
- PowerShell 不再是 mac runtime 物料生成入口。
验收标准:
- `corepack pnpm run materialize:runtime:mac` 成功退出。
- `vendor/openclaw-runtime/node/bin/node --version` 成功。
- `vendor/openclaw-runtime/python/bin/python3 --version` 成功。
- `vendor/openclaw-runtime/python/bin/python3 -m pip --version` 成功。
- `vendor/openclaw-runtime/runtime-manifest.json``platform``darwin``arch``arm64`
- `vendor/openclaw-runtime/playwright-browsers` 存在且包含 Chromium。
### Phase 3: 改造 RuntimeManager 为 mac-only
目标:让 Electron 主进程能识别、启动、停止 mac bundled runtime。
涉及文件:
- `packages/runtime-manager/src/index.ts`
实施内容:
- `resolveBundledPaths()` 改为 mac 路径:
- `node/bin/node`
- `python/bin/python3`
- `ffmpeg/bin/ffmpeg`
- `ffmpeg/bin/ffprobe`
- 删除 Windows 专用工具:
- `escapePowerShellSingleQuoted`
- `resolveWindowsSystemExecutable`
- `WINDOWS_POWERSHELL_PATH`
- `WINDOWS_TASKKILL_PATH`
- `buildWindowsChildWrapperScript`
- 删除 `execPythonInlineScript()` 中的 Windows EPERM PowerShell fallback。
- 删除 `detectRuntime()` 中 Python manifest EPERM fallback。
- `resolveExecutableOnPath()` 不再处理 PATHEXT,直接按 POSIX PATH 查找。
- `resolveBundledFfmpegExecutable()` 支持:
- `ffmpeg/bin/ffmpeg`
- `python/lib/python*/site-packages/imageio_ffmpeg/binaries/ffmpeg-*`
- `playwright-browsers/ffmpeg-*` 下 mac ffmpeg 文件。
- `resolveBundledFfprobeExecutable()` 支持:
- `ffmpeg/bin/ffprobe`
- 与 ffmpeg 同目录的 `ffprobe`
- `buildManagedChildEnv()` 改为 mac 环境:
- 继承 `HOME``USER``TMPDIR``SHELL``PATH``LANG``LC_ALL` 等必要变量。
- 设置 `HOME = runtimeDataDir`
- 设置 `OPENCLAW_HOME``OPENCLAW_STATE_DIR``OPENCLAW_CONFIG_PATH`
- 设置 `PLAYWRIGHT_BROWSERS_PATH`
- PATH 顺序为 `node/bin``python/bin``ffmpeg/bin`、系统 PATH。
- `performStart()` 直接 `spawn(paths.nodeExecutable, childArgs, spawnOptions)`
- `stop()` 改为:
- 优先对 tracked child 发 `SIGTERM`
- 等待短延迟。
- 如仍未退出,发 `SIGKILL`
- 不调用 `taskkill`
- Python probe 保持现有核心导入检查,并补充当前 lock 文件中已有但 probe 未覆盖的关键包:
- `imageio-ffmpeg` 对应 `imageio_ffmpeg`
- `qiniu`
阶段性目标:
- RuntimeManager 不再有 Windows runtime 路径假设。
- RuntimeManager 能读取 mac runtime manifest 并判定 payload ready。
- RuntimeManager 能启动 Gateway 并在停止时清理进程。
验收标准:
- `rg -n "powershell|taskkill|node.exe|python.exe|ffmpeg.exe|ffprobe.exe|python.*Scripts|win32" packages/runtime-manager/src/index.ts` 不再命中有效运行逻辑。
- `corepack pnpm --filter @qjclaw/runtime-manager typecheck` 通过。
- 在 dev 环境指定 `QJCLAW_SMOKE_RUNTIME_DIR=vendor/openclaw-runtime` 后,RuntimeManager status 中:
- `payloadState``ready`
- `nodeExecutable` 指向 `node/bin/node`
- `pythonExecutable` 指向 `python/bin/python3`
### Phase 4: 改造 workspace execution 为 mac-only
目标:保证项目自动化和 workspace agent runner 都能通过 mac bundled runtime 执行。
涉及文件:
- `apps/desktop/src/main/services/project-workspace-executor.ts`
实施内容:
- 删除 PowerShell helper:
- `escapePowerShellSingleQuoted`
- `getWindowsPowerShellPath`
- 删除 spawn EPERM 后 PowerShell wrapper retry。
- `resolveExecutableOnPath()` 简化为 POSIX PATH 查询。
- `resolveInjectedBinaryPath()` command candidates 改为:
- ffmpeg: `["ffmpeg"]`
- ffprobe: `["ffprobe"]`
- child env PATH 改为:
- `path.dirname(paths.nodeExecutable)`
- `path.dirname(paths.pythonExecutable)`
- `path.dirname(paths.ffmpegExecutable)`
- `path.dirname(paths.ffprobeExecutable)`
- `process.env.PATH`
- 移除 `path.join(paths.runtimeDir, "python", "Scripts")`
- 保留现有协议不变:
- `workspaceAutomation.runtime = "python"`
- `workspaceAutomation.script` 模板解析。
- `project-workspace-agent-runner.js` stdin payload。
- stdout `QJC_WORKSPACE_EVENT` event stream。
阶段性目标:
- workspace automation 使用 `python/bin/python3`
- Node runner 使用 `node/bin/node`
- 所有执行路径都不依赖 shell wrapper。
验收标准:
- `rg -n "powershell|python.*Scripts|ffmpeg.exe|ffprobe.exe|win32" apps/desktop/src/main/services/project-workspace-executor.ts` 不再命中有效运行逻辑。
- `corepack pnpm --filter @qjclaw/desktop typecheck` 通过。
- 用一个最小 `project.json``workspaceAutomation.runtime = "python"` 项目验证 Python 脚本可启动。
### Phase 5: 调整 Electron mac 桌面行为
目标:让 `.app` 在 macOS 上以稳定、符合平台预期的方式启动和关闭。
涉及文件:
- `apps/desktop/src/main/create-window.ts`
- `apps/desktop/src/main/index.ts`
实施内容:
- `create-window.ts`
- 删除 Windows `.ico` 路径解析。
- mac 内部测试阶段可让 Electron 使用默认 app icon。
- 如果后续补充 `.icns`,只在 builder 配置中接入,不在 BrowserWindow 强行设置 `.ico`
- 菜单:
- mac 上不要完全 `Menu.setApplicationMenu(null)`
- 提供最小应用菜单:
- app name
- Quit
- Edit: Copy/Paste/Select All
- Window: Minimize/Close
- Windows 菜单隐藏策略不再作为目标。
- 生命周期:
- 保留 `window-all-closed` 中 mac 不直接 quit 的逻辑。
- 保留 `activate` 恢复或创建主窗口。
- 验证 Dock 点击能恢复窗口。
阶段性目标:
- mac `.app` 首次启动不会因窗口/菜单策略异常退出。
- 关闭窗口后 Dock 生命周期符合 mac 习惯。
验收标准:
- 启动应用出现主窗口。
- `Cmd+Q` 可退出。
- 关闭最后一个窗口后应用仍可通过 Dock 重新打开窗口。
### Phase 6: 清理 package scripts 和 Windows installer 入口
目标:让 mac-only 分支的正式命令不再引用 Windows installer 链路。
涉及文件:
-`package.json`
- `apps/desktop/package.json`
- `build/scripts/README.md`
实施内容:
-`package.json`
-`package` 指向 mac 打包,或保留 `package` 但新增明确的 `package:mac`
- 新增 `materialize:runtime:mac`
- 新增 `smoke:mac:runtime``smoke:mac:package``smoke:mac:workspace-entry`
- 将 Windows installer smoke 从正式验收命令中移除。
- `apps/desktop/package.json`
- 新增 `package:mac`
- `package` 若保留,应避免调用 PowerShell。
- `build/scripts/README.md`
- 更新 runtime materialize 说明为 mac 脚本。
- 标注旧 `.ps1` installer smoke 不属于 mac 分支验收入口。
阶段性目标:
- 新开发者只看 package scripts 就能知道 mac 打包入口。
- `corepack pnpm run package:mac` 是唯一正式 DMG 构建命令。
验收标准:
- `corepack pnpm run package:mac` 不调用 `powershell`
- `rg -n "materialize-runtime-payload.ps1|installer-smoke.ps1|Setup-.*exe" package.json apps/desktop/package.json` 不命中正式 mac scripts。
### Phase 7: 建立 mac smoke 验证脚本
目标:替代 Windows installer smoke,提供发布前最小可信回归。
涉及文件:
- `build/scripts/mac-runtime-smoke.mjs`
- `build/scripts/mac-package-smoke.mjs`
- `build/scripts/mac-workspace-entry-smoke.mjs`
-`package.json`
实施内容:
- `mac-runtime-smoke.mjs`
- 读取 `vendor/openclaw-runtime/runtime-manifest.json`
- 校验 manifest 中 platform/arch。
- 校验 Node、Python、OpenClaw wrapper、config、ffmpeg、ffprobe、playwright-browsers 存在。
- 执行 Node/Python version probe。
- 执行 Python imports probe。
- `mac-package-smoke.mjs`
- 查找 `dist/installer/*.dmg`
- 查找 `dist/installer/mac-arm64/*.app` 或 electron-builder 实际输出目录。
- 校验 `.app/Contents/Resources/vendor/openclaw-runtime` 存在。
- 校验 `.app/Contents/Resources/app.asar` 存在。
- `mac-workspace-entry-smoke.mjs`
- 准备临时 project root。
- 写入最小 `project.json`,包含 Python automation。
- 通过 Electron smoke 或直接调用 executor 需要的主链路进行验证。
- 第一版可先做 runtime Python automation 级别验证,第二版再补 Electron UI 驱动。
- package scripts:
- `smoke:mac:runtime`
- `smoke:mac:package`
- `smoke:mac:workspace-entry`
阶段性目标:
- 能在不安装 DMG 的情况下验证 runtime payload。
- 能验证 electron-builder 产物资源布局。
- 能验证最小 workspace automation。
验收标准:
- `corepack pnpm run smoke:mac:runtime` 通过。
- `corepack pnpm run smoke:mac:package` 通过。
- `corepack pnpm run smoke:mac:workspace-entry` 通过。
### Phase 8: 端到端 DMG 验证
目标:验证最终测试包真实可用。
实施内容:
- 执行完整构建:
```bash
corepack pnpm run package:mac
```
- 打开 DMG。
-`.app` 拖入 `/Applications` 或测试目录。
- 启动 `.app`
- 验证:
- 主窗口出现。
- 设置页/聊天页不报主进程错误。
- Runtime status 显示 bundled runtime ready/running。
- managed Gateway readiness 成功。
- 默认聊天可以发起。
- 有 workspace automation 的项目可以调用 bundled Python。
- Playwright Chromium 能启动。
- 退出应用后 runtime 进程停止。
阶段性目标:
- 内部测试人员拿到 DMG 后可以安装并启动。
- 主要功能链路能完成一轮冒烟验证。
验收标准:
- `dist/installer/千匠问天-0.1.0-mac-arm64.dmg` 或对应版本文件存在。
- DMG 可挂载。
- `.app` 可启动。
- app 内 bundled runtime 可运行。
- 退出后无残留 managed Gateway 进程。
## 4. 推荐实施顺序
1.`dev` 基础上创建 mac-only 工作分支。
2.`electron-builder.yml`,先让 mac DMG target 配置成立。
3. 新建 `materialize-runtime-payload.mjs`,生成 mac runtime payload。
4. 更新 `vendor/openclaw-runtime/README.md` 为 mac 布局。
5.`runtime-manager` 的路径、PATH、ffmpeg/ffprobe、stop/start 逻辑。
6.`project-workspace-executor`,移除 PowerShell fallback 和 Windows PATH 假设。
7. 改 Electron 主窗口图标、菜单和 mac 生命周期细节。
8. 调整根 `package.json``apps/desktop/package.json` 的 mac 打包命令。
9. 增加 mac runtime/package/workspace smoke。
10. 跑 typecheck/build。
11. 跑 materialize runtime。
12. 跑 package:mac。
13. 挂载 DMG 做端到端人工验收。
## 5. 风险与处理策略
### Python runtime 可分发性
风险:直接复制系统 Python 或 Homebrew Python 可能存在动态库路径、venv、site-packages 可移植性问题。
处理策略:
- 第一阶段使用显式 `QJCLAW_MAC_PYTHON_ROOT`,要求输入是已验证可复制的 Python 根目录。
- materialize 后立即在 `vendor/openclaw-runtime/python/bin/python3` 上执行 import probe。
- 若复制式 Python 不稳定,下一阶段改为固定使用 standalone Python distribution。
### ffprobe 来源
风险:`imageio-ffmpeg` 通常提供 ffmpeg,不保证提供 ffprobe。
处理策略:
- materialize 阶段强制要求 `ffprobe` 可解析。
- 支持 `--source-ffprobe`
- 缺失时直接失败,不生成半可用 runtime。
### Playwright 浏览器体积
风险:`playwright-browsers` 会显著增大 DMG。
处理策略:
- v1 保留 Chromium,优先保证功能完整。
- 后续再做瘦身,不在首版 DMG 改造里压缩能力。
### Gatekeeper
风险:内部测试未签名 app 首次打开可能被 macOS 阻止。
处理策略:
- 文档明确当前包是内部测试包。
- 验收时允许通过系统设置或 `xattr -dr com.apple.quarantine` 处理。
- 正式分发另开签名/公证阶段。
### 图标资源缺失
风险:当前仓库未发现 `brand-icon.ico/.icns/.png`,强行接入图标会阻塞。
处理策略:
- v1 不强制 `.icns`
- electron-builder 使用默认 icon。
- 拿到品牌图后再补 `build/icons/brand-icon.icns` 和 DMG background。
## 6. 阶段性交付清单
### Milestone A: DMG 配置可构建
交付物:
- mac-only `apps/desktop/electron-builder.yml`
- `package:mac` 命令
- Windows NSIS 配置从正式打包链路移除
验收:
- electron-builder 能进入 mac arm64 packaging。
### Milestone B: mac runtime 可生成
交付物:
- `build/scripts/materialize-runtime-payload.mjs`
- mac 版 `vendor/openclaw-runtime`
- mac 版 runtime README
验收:
- `materialize:runtime:mac` 成功。
- Node/Python/Playwright/ffmpeg/ffprobe probe 成功。
### Milestone C: app 可启动 bundled runtime
交付物:
- mac-only RuntimeManager
- mac-only workspace executor
- mac child env 和 PATH 注入
验收:
- RuntimeManager `payloadState = ready`
- Gateway readiness 成功。
- runtime stop 能清理进程。
### Milestone D: DMG 可安装可冒烟
交付物:
- `.dmg`
- mac smoke scripts
- 更新后的打包验证文档
验收:
- DMG 可挂载。
- `.app` 可启动。
- 主界面、runtime、workspace automation、Playwright 均通过冒烟。
## 7. 最终验收命令
```bash
corepack pnpm --filter @qjclaw/runtime-manager typecheck
corepack pnpm --filter @qjclaw/desktop typecheck
corepack pnpm build
corepack pnpm run materialize:runtime:mac
corepack pnpm run smoke:mac:runtime
corepack pnpm run package:mac
corepack pnpm run smoke:mac:package
corepack pnpm run smoke:mac:workspace-entry
```
## 8. 后续增强项
这些不进入首版 mac-only 内部测试 DMG 范围:
- Developer ID 签名。
- Notarization。
- Staple。
- Auto update feed。
- Universal app。
- Intel Mac 支持。
- 品牌 `.icns`、DMG background、Applications 拖拽窗口美化。
- runtime payload 瘦身。
- 双平台共存抽象。
## 9. 实施进度跟踪
工作分支:`feature/mac-arm64-dmg`
### Phase 0: 建立实施基线 — ✅ 完成
-`dev` 切出 `feature/mac-arm64-dmg` 分支
- 平台边界、发布边界、代码边界已记录在本计划中
### Phase 1: 改造 Electron Builder 打包配置 — ✅ 代码完成,真实产物待验收
已修改文件:
- `apps/desktop/electron-builder.yml` — 删除 `win`/`nsis`/`ico`/`afterPack`,新增 `mac.target: dmg`/`mac.arch: arm64`/`mac.category`/`mac.identity: null`/`dmg.title`/`dmg.contents``artifactName` 改为 `${productName}-${version}-mac-arm64.${ext}`
- `apps/desktop/package.json` — 新增 `package:mac` 脚本
- `package.json` — 新增 `package:mac``materialize:runtime:mac` 脚本
- `apps/ui/src/assets/brand-icon.svg` — 已接入品牌图资源,当前不再依赖缺失的 Windows `.ico`
验收状态:
- `rg -n "nsis|win:|Setup|brand-icon.ico|after-pack-branding" apps/desktop/electron-builder.yml` 无命中 ✅
- `corepack pnpm build` 已通过 ✅
- `electron-builder --mac dmg --arm64 --dir` 已通过 ✅
- 直连 GitHub 下载 Electron darwin arm64 zip 会长时间卡住;通过本地代理 `127.0.0.1:7897` 并提升权限后下载成功
- 已产出 `dist/installer/mac-arm64/千匠问天.app`
- 已产出 `dist/installer/千匠问天-0.1.0-mac-arm64.dmg`
- `hdiutil verify dist/installer/千匠问天-0.1.0-mac-arm64.dmg` 通过 ✅
- 当前产物内 `Resources/vendor/openclaw-runtime` 仅包含占位 README,尚不是最终可验收 runtime 包
### Phase 2: 新建 mac runtime materialize 脚本 — ✅ 代码完成,真实物料化待验收
新建文件:
- `build/scripts/materialize-runtime-payload.mjs` — 完整的 mac arm64 runtime 物料生成脚本
- 平台校验 `darwin` + `arm64`
- 参数解析:`--runtime-dir``--source-config-path``--source-node``--source-python-root``--source-openclaw-entry``--source-ffprobe``--requirements-path``--gateway-port``--gateway-token`
- 环境变量 `QJCLAW_MAC_PYTHON_ROOT`
- Node / OpenClaw / Python / ffmpeg / ffprobe 复制与 chmod
- config 重写(gateway.mode=local, bind=loopback, port, token)
- pip upgrade + requirements install + Playwright install
- python-manifest.json 生成
- runtime-manifest.json 生成(`platform: "darwin"``arch: "arm64"`;mac 路径:`node/bin/node``python/bin/python3``ffmpeg/bin/ffmpeg``ffmpeg/bin/ffprobe`
- openclaw/index.js wrapper 生成
- README.md 生成(mac 布局)
- cleanup(`__pycache__``.pyc``.pyo``.map`、Python Doc/Tools/test/idlelib/tkinter、OpenClaw docs 保留 templates)
- 原子替换 vendor/openclaw-runtime
- `package.json``materialize:runtime:mac` 已从 placeholder 改为 `node build/scripts/materialize-runtime-payload.mjs`
验收状态:
- `node --check build/scripts/materialize-runtime-payload.mjs` 通过 ✅
- `corepack pnpm run materialize:runtime:mac` 已执行,当前失败在第一处硬前置:`ffprobe is required...`
- 本机前置探测结果:
- Node 是 `darwin arm64`,路径为 `/Users/edy/.nvm/versions/node/v24.15.0/bin/node`
- `QJCLAW_MAC_PYTHON_ROOT` 未设置 ❌
- `command -v openclaw` 无结果 ❌
- `command -v ffprobe` 无结果 ❌
- `~/.openclaw/openclaw.json` 不存在 ❌
- 仓库和 `node_modules` 中未找到可直接复用的 `openclaw.mjs``openclaw.json``ffprobe`
- 后续仍需要:`QJCLAW_MAC_PYTHON_ROOT` 指向包含 `bin/python3` 的可复制 Python 根目录;用 `--source-openclaw-entry` 显式指定 OpenClaw entry;安装 ffmpeg 或用 `--source-ffprobe` 显式指定;准备 `~/.openclaw/openclaw.json` 或用 `--source-config-path` 指定
### Phase 3: 改造 RuntimeManager 为 mac-only — ✅ 代码完成,真实启动待验收
已修改文件:`packages/runtime-manager/src/index.ts`
具体改动:
- 路径改为 mac:`node/bin/node``python/bin/python3``ffmpeg/bin/ffmpeg``ffmpeg/bin/ffprobe`
- Runtime payload readiness probe 已补 `imageio_ffmpeg``qiniu`
- Playwright ffmpeg fallback 已补充到 `python/lib/*/site-packages/imageio_ffmpeg/binaries/ffmpeg-*`
- 删除 `escapePowerShellSingleQuoted``resolveWindowsSystemExecutable``WINDOWS_POWERSHELL_PATH``WINDOWS_TASKKILL_PATH`
- `execPythonInlineScript` 简化为直接执行(删除 EPERM PowerShell fallback)
- `detectRuntime` 中 Python probe fallback 改为通用 manifest fallback(不再限于 win32 EPERM)
- `resolveExecutableOnPath` 简化为 POSIX PATH 查询(删除 PATHEXT 逻辑)
- `resolveBundledFfmpegExecutable` 改为 mac 路径:`ffmpeg/bin/ffmpeg``python/lib/*/site-packages/imageio_ffmpeg/binaries/ffmpeg-*`
- `resolveBundledFfprobeExecutable` 改为 mac 路径:`ffmpeg/bin/ffprobe`
- `stop()` 改为 SIGTERM → 500ms → SIGKILL(删除 `taskkill`/`process.platform === "win32"` 分支)
- `performStart` 删除 EPERM PowerShell wrapper fallback(直接 spawn,失败即报错)
- `buildManagedChildEnv` 环境变量改为 macOS:HOME/USER/TMPDIR/SHELL/LANG/LC_ALL/PATH/XDG_* 等,删除 Windows 变量(ALLUSERSPROFILE/APPDATA/COMSPEC/HOMEDRIVE/HOMEPATH/PATHEXT/WINDIR 等);PATH 删除 `python/Scripts`,加入 `node/bin`;ffmpeg/ffprobe 命令名改为 `["ffmpeg"]`/`["ffprobe"]`;删除 `USERPROFILE`
- 删除 `buildWindowsChildWrapperScript` 方法
- 删除 `windowsHide: false` spawn 选项
验收状态:
- `rg -n "powershell|taskkill|node.exe|python.exe|ffmpeg.exe|ffprobe.exe|python.*Scripts|win32" packages/runtime-manager/src/index.ts` 无命中 ✅
- `corepack pnpm --filter @qjclaw/runtime-manager typecheck` 通过 ✅
- bundled runtime 启动和 Gateway readiness 仍需在真实 runtime payload 生成后验收
- `corepack pnpm run smoke:mac:runtime` 已执行,当前因 `vendor/openclaw-runtime` 只有 README、缺 `runtime-manifest.json`/Node/Python/ffmpeg/ffprobe/Playwright 而失败 ❌
### Phase 4: 改造 workspace execution 为 mac-only — ✅ 代码完成,真实执行待验收
已修改文件:`apps/desktop/src/main/services/project-workspace-executor.ts`
具体改动:
- 删除 `escapePowerShellSingleQuoted``getWindowsPowerShellPath`
- `resolveExecutableOnPath` 简化为 POSIX PATH 查询(删除 PATHEXT / windowsExtensions)
- `resolveInjectedBinaryPath` 中 ffmpeg/ffprobe 命令名改为 `["ffmpeg"]`/`["ffprobe"]`(删除 `.exe`
- child env PATH 删除 `path.join(paths.runtimeDir, "python", "Scripts")`,保留 `path.dirname(paths.pythonExecutable)`
- spawn 删除 EPERM PowerShell wrapper fallback(删除 `let child` + try/catch PowerShell retry,改为 `const child = spawn(...)`)
- 删除 `windowsHide: true` spawn 选项
验收状态:
- `rg -n "powershell|python.*Scripts|ffmpeg.exe|ffprobe.exe|win32" apps/desktop/src/main/services/project-workspace-executor.ts` 无命中 ✅
- `corepack pnpm --filter @qjclaw/desktop typecheck` 通过 ✅
- `smoke:mac:workspace-service` 已新增,用于通过 `ProjectWorkspaceExecutorService` 验证 bundled Python 调用;需在真实 runtime payload 生成后执行
- `corepack pnpm run smoke:mac:workspace-entry` 已执行,当前因缺 `vendor/openclaw-runtime/python/bin/python3` 失败 ❌
- `corepack pnpm run smoke:mac:workspace-service` 已执行,当前因缺 runtime manifest、bundled Python、bundled Node 失败 ❌
### Phase 5: 调整 Electron mac 桌面行为 — ✅ 代码完成,真实启动待验收
已修改文件:`apps/desktop/src/main/create-window.ts`
具体改动:
- 删除 `resolveWindowIcon()` 函数(win32 `.ico` 路径解析)
- 删除 `icon: resolveWindowIcon()` BrowserWindow 选项
- `Menu.setApplicationMenu(null)` 改为 `Menu.setApplicationMenu(buildApplicationMenu())`
- 新增 `buildApplicationMenu()` 函数,提供最小 mac 应用菜单:
- App 菜单:About、Quit (Cmd+Q)
- Edit 菜单:Copy (Cmd+C)、Paste (Cmd+V)、Select All (Cmd+A)
- Window 菜单:Minimize (Cmd+M)、Close (Cmd+W)
`apps/desktop/src/main/index.ts` 无需修改(已有正确的 mac 生命周期逻辑:`window-all-closed` 中对 darwin 不 quit、`activate` 恢复窗口)
验收状态:
- `corepack pnpm --filter @qjclaw/desktop typecheck` 通过 ✅
- `.app` 手工启动、主窗口、设置页、聊天页仍需在 unpacked app/DMG 产出后验收
### Phase 6: 清理 package scripts — ✅ 代码完成
已修改文件:`package.json`
- 新增 `smoke:mac:runtime``node build/scripts/mac-runtime-smoke.mjs`
- 新增 `smoke:mac:package``node build/scripts/mac-package-smoke.mjs`
- 新增 `smoke:mac:workspace-entry``node build/scripts/mac-workspace-entry-smoke.mjs`
- 新增 `smoke:mac:workspace-service``node build/scripts/mac-workspace-service-smoke.mjs`
- Windows PowerShell smoke 脚本暂时保留在 package.json 中,但 mac 分支的正式验收命令不再引用它们
### Phase 7: 建立 mac smoke 验证脚本 — ✅ 代码完成,真实 smoke 待跑
新建文件:
- `build/scripts/mac-runtime-smoke.mjs` — 校验 runtime-manifest.json(platform/arch)、所有必需文件存在、Node/Python 执行 probe、Python 关键 import probe、config gateway 校验
- `build/scripts/mac-package-smoke.mjs` — 查找 `.app` 目录、校验 `vendor/openclaw-runtime` 在 Resources 中、校验 `app.asar`、校验 Info.plist
- `build/scripts/mac-workspace-entry-smoke.mjs` — 创建临时 project(含 Python automation)、执行 Python 脚本、校验 QJC_WORKSPACE_EVENT 事件流、校验 Node 可执行
- `build/scripts/mac-workspace-service-smoke.mjs` — 编译并调用 `ProjectWorkspaceExecutorService`,验证 workspace service 使用 bundled Python 和 runtime env
### Phase 8: 端到端 DMG 验证 — ❌ 未开始
待执行:
```bash
corepack pnpm build
corepack pnpm --filter @qjclaw/desktop exec electron-builder --config electron-builder.yml --mac dmg --arm64 --dir
corepack pnpm run materialize:runtime:mac -- \
--source-python-root "$QJCLAW_MAC_PYTHON_ROOT" \
--source-openclaw-entry "/path/to/openclaw.mjs" \
--source-ffprobe "/path/to/ffprobe"
corepack pnpm run smoke:mac:runtime
corepack pnpm run smoke:mac:workspace-entry
corepack pnpm run smoke:mac:workspace-service
corepack pnpm run package:mac
corepack pnpm run smoke:mac:package
```
当前阻塞:
- `QJCLAW_MAC_PYTHON_ROOT` 未设置,且真实 `materialize:runtime:mac` 需要包含 `bin/python3` 的可复制 Python 根目录。
- `openclaw` 不在 PATH 时需通过 `--source-openclaw-entry /path/to/openclaw.mjs` 指定。
- `ffprobe` 不在 PATH 时需安装 ffmpeg 或通过 `--source-ffprobe /path/to/ffprobe` 指定。
- 默认 `~/.openclaw/openclaw.json` 不存在时需通过 `--source-config-path /path/to/openclaw.json` 指定。
- Electron darwin arm64 zip 直连 GitHub 会卡住;用 `HTTPS_PROXY=http://127.0.0.1:7897 HTTP_PROXY=http://127.0.0.1:7897 ALL_PROXY=http://127.0.0.1:7897 ELECTRON_GET_USE_PROXY=true` 可下载成功。
- `corepack pnpm run smoke:mac:package` 当前失败在 packaged runtime payload 不完整:`.app``app.asar`、DMG 均存在,但 `Resources/vendor/openclaw-runtime` 只有 README,缺 `runtime-manifest.json``node/bin/node``python/bin/python3`
### Milestone 达成状态
| Milestone | 描述 | 状态 | 备注 |
|-----------|------|------|------|
| A | DMG 配置可构建 | ✅ 通过 | 使用本地代理后 `--dir` 产出 `.app` 和 DMG;DMG checksum 验证通过 |
| B | mac runtime 可生成 | ✅ 代码完成,待真实 materialize | 需要 Python/OpenClaw/ffprobe 前置 |
| C | app 可启动 bundled runtime | ✅ 代码完成,待 bundled runtime 启动验证 | typecheck 通过;需在有 runtime payload 时验证 RuntimeManager/Gateway |
| D | DMG 可安装可冒烟 | ❌ 未完成 | 已有无 runtime payload 的 DMG;最终 package smoke 依赖 B 生成完整 runtime 后重跑 |
### 改动文件汇总
已修改(7 个):
- `apps/desktop/electron-builder.yml`
- `apps/desktop/package.json`
- `apps/desktop/src/main/create-window.ts`
- `apps/desktop/src/main/services/project-workspace-executor.ts`
- `package.json`
- `packages/runtime-manager/src/index.ts`
- `docs/dmg包改造方案.md`
新建(5 个):
- `build/scripts/materialize-runtime-payload.mjs`
- `build/scripts/mac-runtime-smoke.mjs`
- `build/scripts/mac-package-smoke.mjs`
- `build/scripts/mac-workspace-entry-smoke.mjs`
- `build/scripts/mac-workspace-service-smoke.mjs`
# OpenClaw 客户端自动更新方案
## Summary
为当前 Electron 客户端增加“版本更新自动推送”能力:客户端启动后自动检查版本,云端可按版本、渠道、员工/组织做灰度或强
制更新;用户收到提示后点击下载并安装,体验接近 WorkBuddy。
默认采用“自有云接口 + electron-updater + NSIS 安装包”的方案,优先适配当前 Windows-first 打包链路。
## Key Changes
### 更新发布链路
- 在 apps/desktop 引入 electron-updater。
- 在 apps/desktop/electron-builder.yml 增加 publish/update feed 配置。
- 打包后产物需要包含:
- 千匠问天-Setup-x.y.z.exe
- latest.yml
- 安装包 sha512 校验信息
- 发布流程调整为:
1. 更新根 package.json 和 apps/desktop/package.json 版本号。
2. 执行 corepack pnpm package。
3. 上传 installer 与 latest.yml 到云端文件存储/CDN。
4. 后端登记该版本的元数据。
### 云端版本接口
新增一个产品更新接口,建议路径:
POST /v1/desktop/update/check
请求包含:
{
"appId": "com.qianjiangclaw.desktop",
"currentVersion": "0.1.0",
"platform": "win32",
"arch": "x64",
"employeeId": "xxx",
"channel": "stable"
}
响应包含:
{
"available": true,
"version": "0.2.0",
"channel": "stable",
"mandatory": false,
"releaseNotes": "修复启动问题,优化专家加载。",
"feedUrl": "https://cdn.example.com/qjclaw/stable/latest.yml",
"downloadUrl": "https://cdn.example.com/qjclaw/stable/千匠问天-Setup-0.2.0.exe",
"sha512": "..."
}
云端需要支持:
- stable/beta/dev 渠道。
- 按员工、组织、版本范围灰度。
- 强制更新标记。
- 最低可用版本,用于阻止过旧客户端继续使用。
- 回滚到上一版本。
### 桌面主进程
新增 AppUpdateService,放在 apps/desktop/src/main/services/app-update.ts。
职责:
- 读取当前 app.getVersion()。
- 调用云端更新检查接口。
- 配置 autoUpdater.setFeedURL(...)。
- 管理状态机:
- idle
- checking
- available
- not-available
- downloading
- downloaded
- installing
- error
- 处理 electron-updater 事件:
- checking-for-update
- update-available
- update-not-available
- download-progress
- update-downloaded
- error
- 提供“立即安装”能力:调用 autoUpdater.quitAndInstall()。
- 开发环境不自动安装,只返回 mock/disabled 状态。
### IPC 与共享类型
在 packages/shared-types/src/index.ts 增加:
- UpdateStatus
- UpdateCheckResult
- UpdateProgress
- UpdateState
- IPC channels:
- app-update:get-status
- app-update:check
- app-update:download
- app-update:install
- app-update:event
在 apps/desktop/src/preload/index.ts 暴露:
qjcDesktop.appUpdate.getStatus()
qjcDesktop.appUpdate.check()
qjcDesktop.appUpdate.download()
qjcDesktop.appUpdate.install()
qjcDesktop.appUpdate.onEvent(listener)
### Renderer UI
在 apps/ui/src/App.tsx 增加更新提示入口,保持现有主布局稳定。
建议 UI 行为:
- 普通更新:
- 顶部 toast/notice 显示“发现新版本 x.y.z”。
- 按钮:“立即更新”“稍后”。
- 下载中显示进度。
- 下载完成显示“重启并安装”。
- 强制更新:
- 显示不可关闭的更新弹窗。
- 允许查看更新说明。
- smoke/mock API 中补 mock update 状态,避免开发模式 UI 崩溃。
- Windows 发布前建议接入代码签名证书。
- 下载包必须校验 sha512。
- 更新失败不得破坏现有安装。
- 下载中断后允许重新下载。
- 强制更新只阻止业务入口,不直接杀进程。
- dev 模式默认禁用真实安装,避免误触发本地更新。
## Test Plan
- corepack pnpm typecheck
- corepack pnpm build
- corepack pnpm package
- 新增 smoke:
- mock 云端返回无更新。
- mock 云端返回普通更新。
- mock 云端返回强制更新。
- mock 下载失败时 UI 显示错误。
- 已下载后触发安装 IPC。
- 手工验证:
- 旧版本安装后能检测到新版本。
- 点击更新能下载安装包。
- 点击“重启并安装”后版本变为新版本。
- 强制更新弹窗不可被普通关闭绕过。
- 设置页显示当前版本正确。
## Assumptions
- 首期只支持 Windows NSIS 更新。
- 更新源使用自有云接口控制版本策略,安装包文件放 CDN/对象存储。
- 首期采用轮询检查,不做真正 WebSocket 实时推送:
- 启动后检查一次。
- 登录/绑定成功后检查一次。
- 每 6 小时后台检查一次。
- 真实“服务端主动推送”可作为二期,在现有状态机上增加 WebSocket/SSE 触发检查即可。
- 未签名也能开发测试,但生产发布建议签名后再开放自动更新。
\ No newline at end of file
# 2026-04-13 代码事实完成度盘点
本文件基于当前仓库代码、workspace 产物和 `.tmp` 验收结果整理,不直接复述历史进度文档。
## 已完成
### 1. Python 运行时依赖与安装包校验链路已补齐
- `packages/runtime-manager/src/index.ts` 已把 `greenlet``playwright``edge-tts``python-dotenv``pillow` 纳入运行时导入校验。
- `build/scripts/installer-smoke.ps1` 已对上述依赖做模块探测。
- `.tmp/installer-smoke/20260410-162021/installer-smoke-result.json` 显示安装包冒烟为成功结果。
结论:
运行时缺依赖导致的新电脑安装后无法启动项目的问题,已经进入“有代码防线 + 有安装包验收”的状态。
### 2. XHS / Douyin 项目包标准化已基本落地
- `workspace/xhs/project.json``workspace/douyin/project.json` 都已存在,并声明 `workspaceAutomation``defaultEntry``boundSkillIds``bundlePackaging` 等关键字段。
- `apps/desktop/src/main/services/project-bundle.ts` 已要求项目必须带 `project.json`,并在打包时保留 `memory``sessions.json``session-messages` 等目录。
结论:
两套项目已经不是“散目录脚本集合”,而是进入标准项目包形态。
### 3. 项目级 skill 绑定与隔离逻辑已实现
- `apps/desktop/src/main/services/project-store.ts` 已按项目处理 `boundSkillIds`,并在 bundle 同步时核对声明与实际 materialized skill 的交集。
结论:
项目技能来源已经收敛到项目自身声明,不再是完全依赖 home 全局状态。
### 4. 客户端附件到项目执行器的主链路代码已打通
- `apps/ui/src/App.tsx` 已支持发送附件。
- `apps/desktop/src/preload/index.ts``apps/desktop/src/main/ipc.ts``apps/desktop/src/main/services/project-workspace-executor.ts` 已把附件传入项目执行层。
- 执行环境中已注入 `QJC_PROJECT_ATTACHMENTS_JSON``QJC_PROJECT_MAIN_IMAGE` 等变量。
- `workspace/douyin/workspace_entry.py``workspace/xhs/workspace_entry.py` 已读取附件信息。
结论:
附件链路的第一批最小闭环代码已经落地,至少从 UI 到 workspace entry 的传递关系在代码上成立。
### 5. 云端 bundle 冒烟链路已有成功结果
- `build/scripts/douyin-expert-cloud-bundle-smoke.ps1`
- `build/scripts/xhs-expert-cloud-bundle-smoke.ps1`
- `.tmp/douyin-expert-cloud-bundle-smoke/result.json`
- `.tmp/xhs-expert-cloud-bundle-smoke/result.json`
结论:
XHS / Douyin 专家项目包至少已经能被当前 smoke 链路拉起并完成基础校验。
### 6. Douyin 已有新的真实成功样例
- `workspace/douyin/memory/output/卖苹果宣传短视频/workflow_summary.json` 显示阶段为 `video_generated`
- `workspace/douyin/memory/output/卖苹果宣传短视频/video_generation_status.json` 显示状态为 `success`
- `workspace/douyin/memory/output/内裤推荐短视频/workflow_summary.json` 也显示成功完成出片。
结论:
Douyin 不是只完成了代码改动,当前仓库内已经有至少两条新的真实出片成功证据。
## 未完成或仍不能按“已验收完成”算
### 1. 还不能认定“新电脑安装后正式链路已最终封口”
虽然 `apps/desktop/src/main/services/cloud-api.ts` 已有员工配置接口和 `download_url` 处理逻辑,但当前仓库里能直接看到的主要是 smoke 成功结果,还不足以证明以下事项已经在正式链路下全部完成最终验收:
- 安装客户端
- 拉取正式员工配置
- 下载项目 zip
- XHS / Douyin 双项目都在最新标准下完成真实运行
结论:
相关代码存在,但“正式环境最终验收关闭”这件事不能仅凭当前仓库证据判定为完成。
### 2. 附件业务链缺少专项验收闭环
虽然附件透传代码已经齐了,但当前没有看到明确的“上传真实图片后,Douyin / XHS 业务任务成功完成”的专项 smoke 或端到端验收结果。
结论:
附件链路可以判定为“代码已落地”,还不能判定为“业务验收已完成”。
### 3. XHS 缺少基于最新标准包的最新 fresh 验收证据
当前能看到的较强 fresh-like 结果是 `.tmp/real-xhs-fresh-smoke-3/result.json`,时间是 2026-04-08,不是本轮 2026-04-10 标准化之后重新产出的结果。
结论:
XHS 仍建议在最新 runtime / bundle / smoke 标准下再做一次 fresh 验收。
### 4. 历史失败案例没有全部回归
- `workspace/douyin/memory/output/我们家的苹果/workflow_summary.json` 仍是失败记录。
结论:
当前状态可以证明“已有成功样例”,但不能证明“历史问题案例已全部清零”。
### 5. 工作区根目录历史遗留尚未清理完
`workspace/xhs``workspace/douyin` 根目录下仍有较多旧目录、脚本和历史运行产物。
结论:
标准项目包虽然已建立,但“源目录收尾清理完成”这件事还不能算结束。
## 当前判断
如果按“代码是否已经落地”看,本轮主要骨架已经完成:
- 运行时依赖补齐
- 项目包标准化
- 项目技能绑定
- 附件透传主链
- 云端 bundle smoke
- Douyin 成功样例产出
如果按“是否已经最终验收关闭”看,剩余重点仍然是:
1. 正式员工配置下载 zip 链路的最终验收
2. 附件业务链专项验收
3. XHS 在最新标准下的 fresh 复验
4. 历史失败案例回归
5. workspace 根目录历史遗留清理
一句话结论:
当前已经完成“能安装、能识别项目、能进入项目、能启动项目运行时,并且 Douyin 已有新的真实成功样例”,但还没有完成“新电脑安装客户端后,XHS 和 Douyin 从正式配置链路拉起后即可直接验收关闭”的最终封口。
## 2026-04-13 补充进展
### 本轮已完成
#### 1. Experts 附件 smoke 已从“只验证 launch”收紧到“验证附件进入项目执行层”
- `build/scripts/douyin-expert-cloud-bundle-smoke.ps1` 已补充真实图片夹具、附件透传和落盘校验。
- `build/scripts/xhs-expert-cloud-bundle-smoke.ps1` 已补充真实图片夹具、附件透传和落盘校验。
- `apps/ui/src/App.tsx``apps/desktop/src/main/index.ts` 的配套改动已经进入构建产物并完成重跑。
- 结果上,Douyin / XHS smoke 都已经能验证附件进入 `inputs/images/main/`,不再只是验证 `workspace-entry` 被拉起。
结论:
“客户端带图 -> 主进程项目目录落盘 -> workspace-entry 可感知附件” 这一层已经从“代码已落地”推进到“执行层 smoke 已补齐”。
#### 2. Douyin 已补齐一条带附件的真实 live-run 验收
- 新增 `build/scripts/douyin-expert-live-run.ps1`,并补充到 `build/scripts/README.md`
- live-run 已验证:
- 从 experts 页发起,而不是 skill fallback
- 附件先进入项目根目录 `inputs/images/main/`
- 业务工作流再把图片带入输出目录
- 产出 `_latest_workflow_summary.json``video_request.json``omnihuman_prompt.txt` 等预览产物
- `.tmp/douyin-expert-live-run/result.json` 已显示当前项目为 `douyin`,并且 `workspaceAttachmentPath``projectMainImagePath` 都存在。
结论:
Douyin 现在不只是“附件能传入执行层”,而是已经有“带附件的真实项目 live-run”证据。
#### 3. XHS 的真实卡点已缩小到“项目脚本与桌面运行时配置不兼容”
- `build/scripts/xhs-expert-live-run.ps1` 已补齐 live-run 脚本,并能稳定把请求推进到 `workspace-entry`
- `.tmp/xhs-expert-live-run-4321/result.json.trace.log` 说明 `workspace-launch-accepted` 已发生。
- `.tmp/xhs-expert-live-run-4321/user-data/projects/xhs/memory/xhs_profile``openclaw_runs/xhs_*.json` 已生成,说明项目脚本已经开始真实运行。
-`xhs_last_note.json` 未生成,`daily-reports/state.json` 里记录了 `chat_stream_failed`
- 对照 `apps/desktop/src/main/services/project-workspace-executor.ts``.tmp/xhs-expert-live-run-4321/user-data/runtime/state/openclaw.runtime.json` 可以确认:
- 桌面项目执行时会注入 `OPENCLAW_CONFIG_PATH`
- 当前注入的 runtime config 里有 `modelstudio``openclaw-cloud``custom-open-bigmodel-cn`
- 没有 `models.providers.qwen`
- 也没有 `models.providers.xhsImage`
-`workspace/xhs/scripts/qwen.py` / `workspace/xhs/scripts/run.py` 仍然硬编码依赖 `qwen``xhsImage`
结论:
XHS 当前不是“项目完全不可用”,而是“在新的桌面项目运行时链路下,模型配置适配没有完成”。这已经是定位清楚的实现问题,不再是模糊的 smoke 现象。
### 本轮未完成
#### 1. XHS live-run 还没有闭环成功
虽然 XHS 已经证明:
- experts 页命中正确
- workspace-entry 启动正确
- 项目脚本实际开始执行
- 附件进入项目上下文
但还没有做到:
- 成功写出 `xhs_last_note.json`
- 成功写出 `memory/generated_images`
- 成功返回完整 assistant 结果
结论:
XHS 仍处于“根因已定位,修复未完成”的阶段。
#### 2. 统一模型配置方案还没有落地
目前项目侧仍然存在较强的“项目私有 provider 名”假设,例如:
- XHS 依赖 `qwen`
- XHS 生图依赖 `xhsImage`
- Douyin 也存在自己的模型 / 供应商命名
而目标方向已经明确应该是:
- 文案模型
- 视觉分析模型
- 生图模型
- 视频模型
都逐步收口到客户端统一配置,再由执行器注入给项目。
结论:
当前只是进入了“发现并开始修这类兼容问题”的阶段,还没有完成统一模型角色抽象。
### 长期展望
#### 1. 项目不再直接读取 provider 名,而是读取统一模型角色
长期目标不应是继续让每个项目自己维护 `qwen``xhsImage``volces` 这类私有 provider 名,而应是:
- 项目只表达能力需求:文案、视觉分析、生图、视频
- 客户端设置决定这些能力最终映射到哪个 provider / model
- 执行器把统一结果注入给项目运行环境
这样做的好处是:
- 客户端设置可以统一收口
- 项目迁移成本更低
- XHS / Douyin / 后续项目都能复用一套模型配置协议
#### 2. 短期修复仍要兼容旧项目,避免一次性硬切
落地路径更适合分两步:
1. 先让现有项目“优先读统一注入,兼容旧字段”
2. 等客户端设置完成后,再逐步移除项目内私有 provider 假设
结论:
短期要以“先跑通 XHS 验收链路”为目标,长期要以“客户端统一模型配置,项目只读抽象角色”为目标。
## 2026-04-13 XHS live-run 最新代码事实
### 已完成
- `workspace/xhs/scripts/qwen.py`
- 已支持从桌面端执行器注入的 managed config 解析文本模型,不再要求项目内必须存在 `models.providers.qwen`
- `workspace/xhs/scripts/run.py`
- 已支持在视觉模型不具备图片输入能力时自动降级为纯文本参考分析。
- 已支持在缺少可用生图 provider 时,复用项目主图/附件图写入 `memory/generated_images`,避免桌面端 XHS 项目因 provider 命名不兼容而整条链路失败。
- `build/scripts/xhs-expert-live-run.ps1`
- 2026-04-13 已基于最新项目脚本重跑成功,输出目录为 `D:\qjclaw\.tmp\xhs-expert-live-run-4322`
- 本次 live-run 结果
- `latestNotePath = D:\qjclaw\.tmp\xhs-expert-live-run-4322\user-data\projects\xhs\memory\openclaw_runs\xhs_last_note.json`
- `generatedImageCount = 1`
- `generatedImages[0] = D:\qjclaw\.tmp\xhs-expert-live-run-4322\user-data\projects\xhs\memory\generated_images\xhs_attachment_1776060534_01.png`
- `workspaceAttachmentPath` 存在。
- `xhsProfilePath` 存在。
### 未完成
- 客户端统一生图模型配置尚未落地。
- 当前 XHS live-run 虽已闭环,但图片产物来自附件兜底复用,不是客户端统一生图模型真正产出。
- 项目侧仍然存在兼容层逻辑。
- 现在的方向是“优先读取客户端 managed config,同时兼容旧项目字段”,还没有进入“项目只读统一模型角色、彻底去掉私有 provider 名”阶段。
### 长期方向不变
- 所有项目的文案模型、视觉分析模型、生图模型、视频模型,最终都应由客户端设置统一配置。
- 项目侧只声明能力角色,不再硬编码 `qwen``xhsImage``volces` 这类 provider 名。
- 本次 XHS 修复方向与该长期目标一致,因为它是在去掉项目对私有 provider 名的强绑定,而不是继续加深这种耦合。
## 2026-04-13 云端 bundle / 安装包最新补充
### 已完成
- 已确认 `Project bundle freshness probe failed with HTTP status 400` 的根因不是当前云端最新 `xhs/douyin` zip 不可用,而是本地残留旧 bundle 记录仍指向历史 URL。
- 已直接验证:
- 新下发 `1776065005429-xhs_4_.zip``1776065250716-douyin_3_.zip``HEAD/GET` 均为 `200`
- 旧残留 `1775532407762-xhs.zip``1775788003542-xiaohongshu-writer.zip``HEAD/GET` 均为 `400`
- 已在 `apps/desktop/src/main/services/project-bundle.ts` 增加按 asset 维度的远端 bundle 容错:
- `freshness probe` / `download` 失败不再默认炸掉整轮 startup
- 已有本地项目缓存时允许复用本地项目
- 无本地缓存且远端失败时允许跳过坏 asset,避免阻断其他项目
- 上述修复已通过 `corepack pnpm typecheck`
- 已重新执行 `corepack pnpm package`,最新安装包已产出:
- `D:\qjclaw\dist\installer\QianjiangClaw-Setup-0.1.0.exe`
### 未完成
- 真实安装包验证仍未最终闭环。最新阻塞点已经从“旧 URL 400”切换为“本地目录被占用无法清理”:
- `Project bundle replacement target could not be cleared: C:\Users\EDY\AppData\Roaming\@qjclaw\desktop\projects\xhs`
- `C:\Users\EDY\AppData\Roaming\@qjclaw\desktop\projects\xhs` 当前已被清到空目录状态,但目录壳子本身仍被本地进程句柄占用,导致:
- `backup-rename` 持续 `EBUSY`
- `backup-copy``remove-path(requireMissing)` 仍失败
- 因此,“最新安装包 + 真实云端配置接口 + 本机已有脏缓存”这条最终验收链路目前还不能判定为完成。
### 当前判断
- 代码层面,旧 bundle URL 400 已完成根因定位并已补充容错,不再是主要阻塞。
- 运行层面,当前主要问题是本机 `AppData` 工作区目录锁,属于本地文件系统/进程占用问题,而不是云端最新 `douyin/xhs` zip 本身不可拉取。
- 只要本机先释放并清理 `C:\Users\EDY\AppData\Roaming\@qjclaw\desktop\projects\xhs` 这个空目录壳子,当前安装包即可继续验证最新云端 bundle。
### 下一步建议
1. 完全退出客户端并结束本机残留 `QianjiangClaw.exe` / 相关 `electron.exe` 进程。
2. 手动删除 `C:\Users\EDY\AppData\Roaming\@qjclaw\desktop\projects\xhs`;若仍删不掉,重启后优先删除。
3. 如需一次性清干净,可直接清理整个 `C:\Users\EDY\AppData\Roaming\@qjclaw\desktop` 工作区,再用最新安装包重新绑定验证。
4. 清理本地目录锁后,再继续验证“真实云端配置接口下发 zip -> 客户端同步 -> 抖音/小红书专家可用”这条最终链路。
# 0410 进度
Date: 2026-04-10
Last Updated: 2026-04-13
Status: in-progress
## 今日最终判断
到 2026-04-10 这一步,项目状态已经不是“方案设计中”,而是:
- 客户端安装链路已基本可用
- XHS / Douyin 项目 bundle 接入客户端主链已基本打通
- fresh install 级别的 bundled runtime 验收今天已重新通过
- Douyin 已有新的完整成功样例,且又补出一条更短的单镜头成功样例
- Douyin 主流程里“视频成功但根状态误写失败”的 coordinator 收尾异常已修
- 客户端到专家项目的附件链路已落第一批最小闭环代码
- 但还不能下“新电脑安装客户端后,两个项目从配置接口拉 zip 就都已正式验收可以直接跑”的最终结论
最准确的口径是:
- “接入链路已通”
- “Douyin 本地真实工作流已跑通两条新样例”
- “XHS 有较强的 fresh-like 旧证据”
- “但客户端专家附件链路虽然已补第一批代码,仍未完成 smoke 和真实端到端验收”
- “但安装版客户端 + 配置接口拉 zip + 两项目都真实自动化跑完”的统一最终验收还没完全补齐
## 已完成
### 1. Runtime 依赖和探测已落地
相关文件:
- `build/runtime/python/requirements.in`
- `build/runtime/python/runtime-requirements.lock.txt`
- `packages/runtime-manager/src/index.ts`
已纳入并锁定:
- `greenlet`
- `playwright`
- `edge-tts`
- `python-dotenv`
- `pillow`
- `imageio-ffmpeg`
runtime 探测已从简单存在性检查升级为真实导入探测,能明确确认:
- Python 可执行可用
- `greenlet` 可导入
- `playwright.async_api` 可导入
- `edge_tts` 可导入
### 2. Bundle 契约已明显收紧
相关文件:
- `apps/desktop/src/main/services/project-bundle.ts`
当前 bundle 安装已从“猜测式安装”转向“强契约安装”:
- bundle 内必须有标准 `project.json`
- 缺少 `id` / `name` 会直接报错
- 已开始以 `project.json` 中声明的 `boundSkillIds` 作为项目绑定来源
### 3. 项目级 skills 收口已落地
相关文件:
- `apps/desktop/src/main/services/project-store.ts`
当前行为已调整为:
- 非 home 项目不再自动混入 curated generic skills
- `boundSkillIds` 会和实际 materialize 出来的项目 skill 目录交叉校验
- skill 缺失会进入诊断,并影响项目 ready 状态
### 4. XHS / Douyin 项目标准化已基本完成
相关文件:
- `workspace/xhs/project.json`
- `workspace/xhs/workspace_entry.py`
- `workspace/douyin/project.json`
- `workspace/douyin/workspace_entry.py`
两边都已经具备:
- 标准 `project.json`
- `workspace-entry` 自然语言入口
- `boundSkillIds`
- `bundlePackaging`
- 运行态写入 `memory/` 目录的路径约束
### 5. 客户端 fresh install runtime 验收今天已重新通过
这是今天最关键的新进展之一。
已完成:
- 重新 materialize runtime
- 重新打包 installer
- 重新跑 installer smoke
最新 installer:
- `dist/installer/QianjiangClaw-Setup-0.1.0.exe`
- 时间:2026-04-10 16:11:58
最新 installer smoke:
- `.tmp/installer-smoke/20260410-162021/installer-smoke-result.json`
- 结果:`ok=true`
已确认通过:
- bundled runtime 正常带入安装包
- installed app smoke 正常完成
- `pythonImportProbe`
- `greenletImportProbe`
- `playwrightAsyncImportProbe`
- `edgeTtsImportProbe`
说明 fresh install 下客户端 runtime 本身已经不再是主要阻塞项。
### 6. installer smoke 自身的假失败点已修掉
相关文件:
- `build/scripts/installer-smoke.ps1`
原先这里用 `python -c` 执行多行 probe,PowerShell 会破坏内层引号,导致假的 Python 语法错误。
现在已改为:
- 写临时 `.py` 文件
- 再调用 packaged Python 执行
- 执行后删除临时文件
### 7. XHS / Douyin 项目接入客户端主链已基本打通
当前已经有明确证据证明,客户端侧以下链路是通的:
- employee key / runtime cloud config fetch
- zip bundle sync
- 项目在客户端出现
- 专家页项目命中
- 会话绑定到 `project:xhs:*` / `project:douyin:*`
- `workspace-entry` 被命中
相关证据:
- `.tmp/xhs-expert-cloud-bundle-smoke/result.json`
- `.tmp/douyin-expert-cloud-bundle-smoke/result.json`
当前可以明确说:
- XHS / Douyin 的“项目接入客户端链路”是基本打通的
- 但这两条 smoke 仍然属于“启动验证”,不是“真实结果验收”
原因是:
- `build/scripts/xhs-expert-cloud-bundle-smoke.ps1`
- `build/scripts/douyin-expert-cloud-bundle-smoke.ps1`
- `build/scripts/electron-smoke.ps1`
目前都允许 `QJCLAW_SMOKE_ACCEPT_WORKSPACE_LAUNCH=1`,所以通过的含义更接近:
- 项目命中成功
- `workspace-entry` 启动成功
而不是:
- 项目真实自动化完整执行并产出最终结果
### 8. Douyin 旧失败点已定位并修掉
旧失败点已经明确不是“抖音项目包路由失败”,而是 Douyin 项目内部工作流问题。
已确认并修复:
- Seedance 请求里单独传 `reference_audio` 会触发 Ark 报错
- 错误:`reference_audio cannot be the only reference input`
- `run_seedance.py` 已加兜底
- 在缺少图/视频参考时,不再单独发送 `reference_audio`
相关文件:
- `workspace/douyin/skills/seedance-2-0-video-generator/scripts/run_seedance.py`
### 9. Douyin 主流程 placeholder 音频误传已修
今天还额外确认并修掉了一个真正影响成片的主流程问题:
- `seedance-silent*.wav` 此前会被当成 `main_audio` 传到 split
- 后续 `ffmpeg -shortest` 会把成片错误截短
当前已改为:
- 不再把 `seedance-silent*.wav` 当成主音频复用
- 不再把它写入新的 `video_request` / `project_inputs.main_audio`
- split 层也做了二次防呆
相关文件:
- `workspace/douyin/skills/douyin-master/scripts/coordinator.py`
- `workspace/douyin/skills/douyin-seedance-split/scripts/run_pipeline.py`
### 10. Douyin 已跑通新的完整验收样例:卖苹果宣传短视频
今天新增跑通了一条新的 Douyin 实战样例:
- 项目目录:`workspace/douyin/memory/output/卖苹果宣传短视频`
- 目标:`我要卖苹果,帮做一个抖音宣传视频,三个镜头就行,短一点`
- 结果:完整主流程成功,项目根状态成功,最终成片成功
关键结果:
- `workflow_summary.json` 已是 `stage=video_generated`
- `video_generation_status.json` 已是 `status=success`
- `latest_seedance_split_summary.json``segment_count=3`
- 最终视频:
- `workspace/douyin/memory/output/卖苹果宣传短视频/latest_seedance_split.mp4`
- `workspace/douyin/memory/output/卖苹果宣传短视频/seedance-split-runs/20260410-192611/final_video.mp4`
这说明到今天这一步,Douyin 不再只是“能启动项目”“能返回分镜”,而是已经至少有一条完整的新样例做到:
- 预览生成成功
- 三镜头拆分成功
- Seedance 分镜生成成功
- 本地拼接成功
- TTS 旁白 mux 成功
- 项目根状态回写成功
### 11. Douyin 又补通一条更短的单镜头验收样例:内裤推荐短视频
`卖苹果宣传短视频` 之外,今天又追加跑通了一条更短的 Douyin 样例:
- 项目目录:`workspace/douyin/memory/output/内裤推荐短视频`
- 目标:更短、更小、分镜更少,优先验证抖音主流程根状态成功回写
- 结果:单镜头成功、最终视频成功、项目根状态成功
关键结果:
- `workflow_summary.json` 已是 `stage=video_generated`
- `video_generation_status.json` 已是 `status=success`
- `latest_seedance_split_summary.json``segment_count=1`
- 最终时长约 `4.34s`
- 最终视频:
- `workspace/douyin/memory/output/内裤推荐短视频/latest_seedance_split.mp4`
- `workspace/douyin/memory/output/内裤推荐短视频/seedance-split-runs/20260410-211028/final_video.mp4`
这条样例的价值在于,它不是三镜头长样例,而是更接近后续“快速生成、短视频、小分镜”的实际使用场景。
### 12. Douyin coordinator 收尾假失败点已修
今天还顺手修掉了一个会影响最终状态口径的收尾问题:
- 之前出现过“Seedance 视频已经生成成功,但项目根状态被写成 error”
- 根因不是视频生成失败,而是 coordinator 收尾阶段日志输出触发 `[Errno 22] Invalid argument`
- 现在日志输出失败不会再把本来成功的工作流误打成失败
相关文件:
- `workspace/douyin/skills/douyin-master/scripts/coordinator.py`
### 13. XHS 有较强的 fresh-like 旧证据,但今天未按最新标准重跑
已有一份较强的旧证据表明,XHS 不只是“能命中项目”,而是在 fresh-like 路径下实际跑到项目产物:
- `.tmp/real-xhs-fresh-smoke-3/result.json`
- `.tmp/real-xhs-fresh-smoke-3/result.json.trace.log`
- `.tmp/real-xhs-fresh-smoke-3/user-data/projects/xhs`
该结果能证明:
- 安装 / 初始化 / bundle 同步 / 项目进入 / 流式执行都跑到了较深层
- `user-data/projects/xhs` 下确实出现了项目目录、技能、会话、memory、profile 和产物文件
但要注意:
- 这份证据时间是 2026-04-08
- 不是今天 2026-04-10 按最新 runtime / bundle / smoke 口径重新做的同轮验收
### 14. 客户端专家附件链路第一批最小闭环代码已落地
这次已经把“客户端 experts 页单图上传 -> 主进程项目内落盘 -> workspace-entry 执行层透传 -> Douyin/XHS Python 入口感知附件”的第一批最小闭环补进代码。
已完成:
- `shared-types` 已新增 `ChatAttachment` / `ProjectResolvedAttachment`
- `DesktopApi.chat.sendPrompt()` / `streamPrompt()` 已支持可选 `attachments`
- 主进程 `ipc.ts` 已支持把图片复制到当前项目 `inputs/images/main/`
- 附件已强制要求走 `workspace-entry`,不再允许图片挂到普通 skill fallback
- `project-workspace-executor` 已通过执行输入和环境变量继续透传附件
- experts 页已补单图选择、展示、移除、发送
- Douyin `workspace_entry.py` 已能读取主图,并在数字人场景下把图传给 coordinator
- XHS `workspace_entry.py` 已能感知附件进入项目上下文
相关文件:
- `packages/shared-types/src/index.ts`
- `apps/desktop/src/preload/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/project-workspace-executor.ts`
- `apps/desktop/src/main/project-workspace-agent-runner.ts`
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `workspace/douyin/workspace_entry.py`
- `workspace/xhs/workspace_entry.py`
本轮已完成基础校验:
- `corepack pnpm typecheck` 通过
- `python -m py_compile workspace/douyin/workspace_entry.py workspace/xhs/workspace_entry.py` 通过
## 尚未完成
### 1. 还不能下“新电脑装完后两个项目都能直接跑起来并正式验收通过”的最终结论
这是当前最重要的边界。
基于目前事实,更准确的结论是:
- 客户端安装链路已通
- 项目 bundle 接入链路已通
- Douyin 已有新的完整成功样例
- XHS 有较强的 fresh-like 旧证据
但还不能等同于:
- 一台全新电脑安装客户端
- 用正式配置接口拉取当前两个 zip 项目包
- 两个项目都在安装版客户端里完整跑完真实自动化
- 并且已经完成统一最终验收
原因:
- 今天的 XHS / Douyin `expert cloud bundle smoke` 仍是“启动验证”性质
- Douyin 的完整成功样例是本地工作区真实流程成功,不是安装版客户端 cloud-bundle smoke 的最终结果验收
- XHS 的 fresh-like 强证据来自 2026-04-08,不是今天同轮复验
- 当前 smoke 使用的是本地 smoke cloud config 服务,不是计划文档中写的正式线上 Supabase 地址
### 2. `我们家的苹果` 旧案例还没有按完整主流程重新收口
`我们家的苹果` 这条线此前已经验证过:
- Seedance 分镜接口本身可以成功返回视频
- 参数问题 `reference_audio cannot be the only reference input` 已修
- `ffmpeg` 缺失问题已补
- placeholder 音频误传问题已修
但这条老案例没有再完整重跑一遍主流程去更新它自己的项目根状态文件,所以它仍然不是今天最后的验收样例。
### 3. Experts smoke 仍然偏“启动验证”
`xhs``douyin` 的 experts smoke 现在仍用了:
- `QJCLAW_SMOKE_ACCEPT_WORKSPACE_LAUNCH=1`
这意味着它更偏向验证:
- 项目是否命中
- `workspace-entry` 是否启动
- 项目会话是否正确绑定
而不是验证:
- 项目自动化流程是否真正执行完成
- 最终文件是否真正产出
### 4. 客户端专家附件链路已落第一批代码,但还没完成验收闭环
这一点是当前最新边界。
当前已经做到:
- experts 页可选 1 张本地图片
- `DesktopApi.chat.sendPrompt()` / `streamPrompt()` 已支持附件
- IPC 层已支持附件协议
- 主进程会把图片复制到当前项目 `inputs/images/main/`
- `project-workspace-executor` / `project-workspace-agent-runner` 已能透传附件
- Douyin / XHS `workspace_entry.py` 已能读取附件上下文
但还没有完成:
- experts smoke 升级到“验证附件真实进入项目执行层”
- 客户端带图实跑一轮 Douyin 数字人端到端验收
- XHS 对附件的业务语义消化仍未继续展开
所以当前不能说“附件链路已经验收完毕”,只能说“附件链路第一批最小闭环代码已经落地”。
专项计划仍见:
- `docs/douyin-xhs-插入/2026-04-10-客户端抖音专家附件打通计划.md`
### 5. Day 1 契约收口仍不算完全结束
当前主要收紧的是:
- bundle 侧
- project store 侧
- runtime / smoke 侧
但计划里点名的这些文件,这次没有看到明确收口:
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/project-workspace-executor.ts`
所以 Day 1 的“配置契约冻结”还不能算 100% 完成。
### 6. XHS / Douyin 源目录收尾清理还没做完
虽然运行态都在往 `memory/` 迁,但项目根目录里仍然有一些历史运行目录和旧说明没有完全清掉。
这会影响:
- bundle 源目录整洁度
- 交接理解成本
- 后续排错时对“哪些是源文件、哪些是运行产物”的判断
## 下一步建议
按优先级,下一步建议直接做下面几件事:
1. 先把 experts smoke 从“接受 workspace launch”收紧到“要求附件真实进入项目执行层”
2. 直接用客户端做一轮“上传人物图 -> Douyin expert -> 数字人”真实端到端验收
3. 用最新 runtime / bundle / smoke 标准,重新做一轮 XHS fresh-like 验收
4. 再完整复跑一次 `我们家的苹果`,把老案例的项目根状态也更新到成功态
5. 继续补 Day 1 的 `cloud-api.ts` / `project-workspace-executor.ts` 契约收口
6. 清理 `workspace/xhs``workspace/douyin` 根目录中的历史运行产物与旧说明
## 当前阶段判断
到 2026-04-10 这一步,项目整体进度可以这样判断:
- 基础运行时、bundle 契约、项目隔离、workspace-entry 已经进入可用阶段
- fresh install runtime 验收今天已经补齐,属于实质性推进
- XHS / Douyin 接入客户端主链已基本打通
- Douyin 已经有两条新的真实出片成功样例
- 客户端专家附件链路第一批最小闭环代码已落地,并完成基础类型/语法校验
- XHS 有较强的 fresh-like 旧证据
- 当前最大剩余风险已经从“客户端素材输入完全未打通”转成“附件链路 smoke 与真实端到端验收还没补完 + 统一最终验收还没完全补完”
一句话结论:
当前已经完成“能安装、能识别项目、能进入项目、能启动项目运行时,而且 Douyin 已有两条新的真实出片成功样例”,并且客户端专家附件链路第一批最小闭环代码也已经落地;但还没有完成“新电脑安装客户端后,XHS 和 Douyin 从配置接口拉 zip 就都已正式验收可直接跑”的最终封口。当前最明确的新阻塞点,已经变成“附件链路 smoke 和真实端到端验收还没补完”。
补充说明:
2026-04-13 基于当前仓库代码与产物的完成度盘点,见 `docs/douyin-xhs-插入/2026-04-13-代码事实完成度盘点.md`
## 2026-04-13 补充
今天新增的关键结论:
- Douyin / XHS 的附件 smoke 已经从“只验证 `workspace-entry` 启动”收紧到“验证附件真实进入项目执行层”。
- Douyin 已补齐一条带附件的真实 live-run 验收,说明附件不只是落盘,而是已经进入真实业务链路。
- XHS 的卡点已经缩小到“项目脚本与桌面运行时配置不兼容”,不是入口没进,也不是附件没进。
- 后续模型配置方向已经明确:长期不再让各项目直接硬编码 `qwen``xhsImage``volces` 这类 provider 名,而是逐步改成由客户端统一配置文案 / 视觉分析 / 生图 / 视频模型,再由执行器注入给项目。
详细盘点见:
- `docs/douyin-xhs-插入/2026-04-13-代码事实完成度盘点.md`
## 2026-04-13 XHS live-run 补充
- `workspace/xhs/scripts/qwen.py` 已补齐对桌面端 managed config 的兼容,不再只认项目内 `models.providers.qwen`,而是优先从执行器注入的 `OPENCLAW_CONFIG_PATH` 解析当前可用文本模型。
- `workspace/xhs/scripts/run.py` 已补齐两类兼容:
- 参考图分析阶段会先判断当前模型是否支持图片输入;若不支持,则退化为纯文本分析,避免把图片硬塞给 `openclaw-cloud/gpt-5.4-mini` 这类文本模型。
- 生图阶段不再把 `xhsImage` 当成唯一前提;当客户端当前 managed config 没有可用生图 provider 时,会把项目主图/附件图复制到 `memory/generated_images` 作为兜底产物,保证桌面端项目链路能闭环。
- 2026-04-13 已重新执行:
- `build/scripts/xhs-expert-live-run.ps1 -SkipMaterializeRuntime -SmokePort 4322 -BaseOutputDir D:\qjclaw\.tmp\xhs-expert-live-run-4322`
- 本次结果已闭环:
- `xhs_last_note.json` 已生成。
- `memory/generated_images` 已生成图片文件。
- `workspaceAttachmentPath` 存在,说明附件已进入项目执行层。
- `xhs_profile` 存在,说明项目脚本已实际运行。
- 需要明确区分的边界:
- 这次 XHS live-run 成功,证明的是“桌面端 managed config + 项目执行器 + XHS 项目脚本”的兼容链路已经打通。
- 这次生成出来的图片文件是附件兜底复用产物 `xhs_attachment_*.png`,不代表“客户端统一生图模型配置”已经完成。
排查结论:问题真实存在,根因在 workspace/xhs/workspace_entry.py:859。
事实链:
- 第一次确认后,状态是 awaiting_confirmation。
- 用户说“采集1条就行”不是“确认”,代码会在 workspace/xhs/workspace_entry.py:859 清掉原来的关键词/业务需求/目标人群,重新分类。
- 重新分类时,extract_collect_params("采集1条就行") 实际得到:
{"xhs_action":"collect","keywords":["1条就行"],"total_count":1}
- 所以它随后提示还缺 business_need、target_audience,再补充后就变成“关键词:1条就行”。
影响范围:只影响“确认页之后用户补充/修改采集数量”的多轮采集流程;不是采集执行、生成内容、飞书上传本身的问题。
建议修复点:awaiting_confirmation 状态下,如果用户输入的是“采集1条/改成1条/1条就行”,应合并到原 payload 的 total_count,保留原 keywords/business_need/target_audience,然后重新展示确认页。
## Summary
- 目标:用户在确认页后说“采集1条就行 / 改成3条 / 1条”时,只更新采集数量,不丢失已确认的关键词、业务需求、目标人群。
- 原则:不靠固定句子硬编码,基于“当前会话状态 + 字段级增量解析 + payload 合并”处理。
## Key Changes
- 在 awaiting_confirmation 状态下,除“确认/取消”外,不直接清空状态。
- 对用户新输入做字段级解析:
- 若能解析出 total_count,合并进原 payload。
- 若解析出关键词、业务需求、目标人群,也只覆盖对应字段。
- 未解析出有效字段时,再按现有逻辑结束当前确认状态并重新分类。
- 抽出通用增量解析函数,例如 extract_collect_updates(text):
- 支持数量表达:1条、采集1条、改成1条、一条、只要1条。
- 返回结构化字段,不返回完整新任务,避免把“1条就行”当关键词。
- 调整关键词提取规则:
- 采集动词后如果主体是数量/确认/修改语义,不作为关键词。
- 关键词仍通过原主题句或显式“关键词是...”提取。
## Risks
- 风险 1:过度合并,把用户的新任务误当成修改旧任务。
- 控制方式:只有当前状态是 awaiting_confirmation 且输入解析到明确字段修改时才合并。
- 风险 2:数量中文表达解析不全。
- 控制方式:复用现有中文数字解析 parse_ref_index 思路,扩展为通用 1-20 数量解析。
- 风险 3:影响确认后的重新发起任务。
- 控制方式:如果输入包含新的完整采集主题和关键词,则按新任务重新分类,不合并旧 payload。
- 风险 4:LLM 与本地正则结果不一致。
- 控制方式:确认态下优先使用本地字段级解析;LLM 只用于新任务分类,不用于覆盖完整 payload。
## Test Plan
- 复现原问题:
- 帮我采集 推荐上海平价川味火锅的笔记
- 种草引流涨粉 给热爱美食的人看
- 采集1条就行
- 期望确认页关键词仍是“推荐上海平价川味火锅”,采集数量为 1。
- 数量修改:
- 确认页后输入 改成3条
- 期望只更新 total_count=3。
- 直接确认:
- 确认页后输入 确认
- 期望行为不变,进入采集执行。
- 新任务覆盖:
- 确认页后输入 帮我采集北京烤肉笔记
- 期望按新任务重新进入参数确认,不沿用旧关键词。
- 回归:
- python -m py_compile workspace\xhs\workspace_entry.py
- corepack pnpm smoke:xhs-local-project-package
\ No newline at end of file
# 2026-04-16 桌面端专家入口问题交接
## 背景
本轮处理的是桌面端以下问题:
1. 小红书和抖音专家页不显示
2. 内容账号规划专家、知乎专家是否为独立专家要按项目事实核查
3. 左侧栏多数专家点不了,要求除独立专家外其余专家点击后跳首页对话
4. 左侧专家列表过长,挤占会话管理,需要独立滚动
5. 首页对话发送“你好”仍路由到抖音,且消息马上消失
## 已完成
### 1. 修复首页快捷专家不可点击
文件:
- `apps/desktop/src/main/services/expert-catalog.ts`
改动:
- `promptAvailable` 由“仅检查 promptFile 是否存在”改为:
-`starterPrompt` 即可用
-`promptFile` 文件存在也可用
影响:
- 公众号专家、X 专家、Tiktok 专家、海报专家、GEO 专家、平台精准线索专家恢复可点击
### 2. 修复首页对话串到抖音、消息消失
文件:
- `apps/ui/src/App.tsx`
改动:
- `chat` 视图发送时,优先使用 `home-chat``sessionScopeProjectId`
- 不再优先使用 `workspace.currentProjectId`
- 新增 `openHomeChat()`,保证切回首页对话时同步归一到 `home-chat`
- 左侧“对话”导航点击改为走 `openHomeChat()`
- 首页快捷专家点击也改为走 `openHomeChat(true)`
影响:
- 首页对话不再把消息建进抖音项目
- 消息不会因为会话作用域错位而“发送后消失”
### 3. 恢复专家页真实项目来源
文件:
- `apps/ui/src/App.tsx`
改动:
- 新增 `buildProjectExpertDefinition()`
- 新增 `projectExpertEntries`
- `expertPageProjects` 不再只取 `standaloneExpertEntries`
- 改为基于真实 `visibleProjects` 生成专家页项目
影响:
- 仓库里真实存在的 `xhs` / `douyin` 项目可以重新进入专家页
- 左侧专家栏也改为优先显示真实项目专家,再拼首页快捷专家
### 4. 修复左侧栏滚动布局
文件:
- `apps/ui/src/styles.css`
改动:
- `sidebar-bottom` 改为两段式布局
- 新增 `.sidebar-expert-scroll`
- 新增 `.sidebar-session-section`
- 让专家区和会话区分别滚动
影响:
- 专家列表过长时,不再把会话管理区整体挤没
### 5. 补齐 smoke 类型定义
文件:
- `apps/ui/src/App.tsx`
改动:
- `SmokeUiSnapshot` 新增 `sessionScopeProjectId`
目的:
- 修复 `typecheck` 因 smoke 快照字段缺失导致的编译错误
## 已验证
已通过:
- `corepack pnpm typecheck`
- `powershell -ExecutionPolicy Bypass -File build/scripts/default-chat-smoke.ps1`
- `powershell -ExecutionPolicy Bypass -File build/scripts/project-routing-smoke.ps1`
- `powershell -ExecutionPolicy Bypass -File build/scripts/desktop-expert-entry-smoke.ps1 -SkipMaterializeRuntime`
`desktop-expert-entry-smoke` 已覆盖:
- `content-account-planning`
- `zhihu`
- `wechat-official-account`
- `x-platform`
- `tiktok`
- `poster`
- `geo`
- `precision-leads`
## 关键事实结论
### 1. 小红书 / 抖音专家页消失的主因
不是项目没了,是前端把专家页数据源改成只看 `standalone` 专家定义,导致真实项目 `workspace/xhs``workspace/douyin` 被过滤掉。
### 2. 其他专家点不了的主因
不是按钮组件坏了,是可用性判定错了。首页快捷专家只有 `starterPrompt`,没有 `promptFile`,因此被错误判成不可用。
### 3. 内容账号规划专家 / 知乎专家当前状态
按仓库事实,它们当前仍不是“仓库内已落地的真实独立专家项目”。
说明:
- 当前仓库 `workspace/` 下没有它们对应真实项目目录
- 但 smoke 已经证明:当用户数据里存在对应项目时,UI 入口和会话隔离链路可以工作
结论:
- 现在更准确的说法是:入口机制和隔离路由已支持
- 但仓库内真实项目包还没补齐
## 当前未完成
### 1. 肉眼页面确认未完成
原因不是代码未生效,而是本机存在一个旧的 Electron 进程:
- `PID 20424`
表现:
- 我已执行 `corepack pnpm build`
- 新的 renderer 已成功输出到 `apps/desktop/dist/renderer`
- 但旧进程无法被我当前环境结束,导致用户看到的仍可能是旧界面
### 2. 旧进程状态
用户侧执行过:
```powershell
taskkill /PID 20424 /F /T
```
结果:
- 失败
- 提示该进程属于父进程 `23116` 的子进程
- 原因是 `拒绝访问`
结论:
- 最稳妥处理是重启机器
## 已完成但需要用户重启后确认的点
重启后需要肉眼确认:
1. 左侧或专家页是否能看到“小红书专家”“抖音专家”
2. 点击公众号/X/Tiktok/海报/GEO/平台精准线索,是否跳转首页对话,而不是进入专家页
3. 首页发送“你好”,是否仍会跑到抖音
4. 发送后的消息是否还会消失
5. 左侧专家区和会话区是否都可以独立滚动
## 重启后继续执行顺序
1. 先启动桌面端
2. 先看小红书 / 抖音专家是否出现
3. 再测首页快捷专家跳转是否正确
4. 再测首页发送“你好”
5. 若页面仍不对,先确认是否真的是新进程和新包
## 如果重启后仍异常,优先检查
### A. 是否加载了新 renderer
重点看:
- `apps/desktop/dist/renderer/index.html`
- `apps/desktop/dist/renderer/assets/index-*.js`
### B. 是否仍有旧 Electron 残留
命令:
```powershell
Get-Process | Where-Object { $_.ProcessName -match 'electron|QianjiangClaw' } | Select-Object Id,ProcessName,Path
```
### C. 如果页面还是没有小红书 / 抖音专家
继续检查:
- `workspace.getSummary()` 返回的 `workspace.projects`
- `visibleProjects`
- `projectExpertEntries`
- `expertPageProjects`
## 相关文件
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/desktop/src/main/services/expert-catalog.ts`
## 备注
本轮没有继续接“龙虾密钥真实绑定态页面验证”,因为用户当前首先反馈的是“小红书和抖音专家在页面上仍没看到”,而该现象与旧 Electron 残留更直接相关。后续等重启后再继续做真实页面 smoke。
# 2026-04-17 客户端模型配置打通进度
## 目标
让客户在桌面客户端自行配置项目所使用的模型信息,不依赖项目包内固定写死;项目从云端配置接口拉取 zip 并解压后,实际执行时由客户端把模型配置注入到项目运行时。
## 已完成
### 1. 客户端配置结构已支持 `modelId`
- 已扩展共享类型,`expertModelConfig.image/copywriting/video` 均支持 `modelId`
- 已扩展桌面端配置持久化与归一化逻辑,保存后不会丢失 `modelId`
- 已扩展 IPC 返回值,前端加载配置时可以拿到 `modelId`
涉及文件:
- [packages/shared-types/src/index.ts](D:/qjclaw/packages/shared-types/src/index.ts)
- [apps/desktop/src/main/services/app-config.ts](D:/qjclaw/apps/desktop/src/main/services/app-config.ts)
- [apps/desktop/src/main/ipc.ts](D:/qjclaw/apps/desktop/src/main/ipc.ts)
### 2. 客户端设置页已可配置项目模型
- 已在设置页增加图片模型 `model_id` 输入
- 已在设置页增加文案模型 `model_id` 输入
- 保存设置后,冒烟态和配置回显会校验 `modelId`
涉及文件:
- [apps/ui/src/App.tsx](D:/qjclaw/apps/ui/src/App.tsx)
### 3. 项目运行前注入逻辑已打通
- 新增项目运行时模型注入服务
- 在执行 `workspace_entry.py` 前,根据当前项目 ID 和客户端已保存配置生成 `memory/project.env`
- 同时把生成后的环境变量注入子进程,确保项目执行链路可直接读取
当前已支持:
- `xhs`
- `QWEN_BASE_URL`
- `QWEN_API_KEY`
- `QWEN_MODEL`
- `QWEN_VISION_MODEL`
- `XHS_IMAGE_PROVIDER=env:xhsImage`
- `XHS_IMAGE_BASE_URL`
- `XHS_IMAGE_API_KEY`
- `XHS_IMAGE_MODEL`
- `douyin`
- `DOUYIN_WRITER_LLM_BASE_URL`
- `DASHSCOPE_API_KEY`
- `QWEN_API_KEY`
- `DOUYIN_WRITER_LLM_MODEL`
- `SEEDREAM_ARK_BASE_URL`
- `SEEDREAM_ARK_API_KEY`
- `SEEDREAM_MODEL`
涉及文件:
- [apps/desktop/src/main/services/project-model-runtime.ts](D:/qjclaw/apps/desktop/src/main/services/project-model-runtime.ts)
- [apps/desktop/src/main/services/project-workspace-executor.ts](D:/qjclaw/apps/desktop/src/main/services/project-workspace-executor.ts)
- [apps/desktop/src/main/ipc.ts](D:/qjclaw/apps/desktop/src/main/ipc.ts)
### 4. 打包链路和端到端冒烟已验证通过
- `corepack pnpm typecheck` 通过
- `corepack pnpm package` 通过
- 安装器冒烟通过
- `xhs` 云端 zip -> 解压 -> 激活项目 -> workspace-entry -> `project.env` 注入 冒烟通过
- `douyin` 云端 zip -> 解压 -> 激活项目 -> workspace-entry -> `project.env` 注入 冒烟通过
结果文件:
- [installer-smoke-result.json](D:/qjclaw/.tmp/installer-smoke/20260416-221908/installer-smoke-result.json)
- [xhs result.json](D:/qjclaw/.tmp/xhs-expert-cloud-bundle-smoke/result.json)
- [douyin result.json](D:/qjclaw/.tmp/douyin-expert-cloud-bundle-smoke/result.json)
对应冒烟脚本:
- [build/scripts/installer-smoke.ps1](D:/qjclaw/build/scripts/installer-smoke.ps1)
- [build/scripts/xhs-expert-cloud-bundle-smoke.ps1](D:/qjclaw/build/scripts/xhs-expert-cloud-bundle-smoke.ps1)
- [build/scripts/douyin-expert-cloud-bundle-smoke.ps1](D:/qjclaw/build/scripts/douyin-expert-cloud-bundle-smoke.ps1)
- [apps/desktop/src/main/index.ts](D:/qjclaw/apps/desktop/src/main/index.ts)
## 当前未完成
### 1. 视频模型链路暂未纳入本次范围
- 当前只重点打通了图片模型和文案模型
- `video.modelId` 类型和配置持久化已预留
- 但视频模型的运行时注入、前端独立配置项联动、项目实际消费逻辑,本次未继续展开
### 2. 目前是“客户端本地配置生效”,不是“云端统一下发模型配置”
- 现在已经支持客户在本机客户端配置模型并立即作用于项目执行
- 但还没有把这份模型配置并入远端配置中心做统一下发
- 如果后续要做多端一致、换机继承、后台代运营配置,需要再单独设计云端配置存储和同步
### 3. 还没有单独补业务说明文档
- 当前代码和烟测已齐
- 但还没有补一份给实施/运维/客户的“字段含义、支持项目、配置示例、常见错误”操作文档
## 结论
当前“客户在客户端配置模型 -> 项目从云端拉 zip -> 客户端解压并执行 -> 执行前注入模型环境变量”主链路已经打通,并已通过打包后的端到端冒烟验证。
如果下一步继续推进,优先级建议是:
1. 补一份面向实施和客户的配置说明文档
2. 决定是否要把模型配置接入云端配置中心
3. 视业务需要再补视频模型链路
# 2026-04-21 桌面单实例续接记录
## 目标
本轮要落实的口径是:
- 已有客户端在运行时,再次启动不应继续跑第二个主流程
- 首实例应恢复/显示/聚焦原窗口
- 不应新增一轮 runtime/gateway 启动或冲突日志
对应自动验证命令:
```powershell
corepack pnpm smoke:desktop-single-instance
```
## 已完成
代码侧已落地以下改动:
- `apps/desktop/src/main/index.ts`
- 增加 `app.requestSingleInstanceLock()`
- 增加 `second-instance` 处理
- 增加主窗口跟踪与唤回逻辑:
- `mainWindow`
- `focusMainWindow()`
- `restoreOrCreateMainWindow()`
- `createTrackedMainWindow()`
- 增加单实例 smoke 辅助状态与输出:
- `QJCLAW_SMOKE_SINGLE_INSTANCE`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_READY_PATH`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_EVENT_PATH`
- `runSingleInstanceSmoke()`
- `secondInstanceEventCount`
- `lastSecondInstanceEventSnapshot`
- `activate` 事件改为统一走 `restoreOrCreateMainWindow("activate")`
- `build/scripts/desktop-single-instance-smoke.ps1`
- 已新增单实例 smoke 脚本
- 流程是:
- 起第一个 Electron 实例
- 等首实例写 `ready`
- 起第二个 Electron 实例
- 校验第二个实例快速退出
- 校验首实例收到 `second-instance`
- 校验窗口数仍为 `1`
- 校验 `Launching bundled runtime command` 次数不增加
- 校验日志中没有:
- `gateway already running`
- `Port 18889 is already in use`
- `schtasks /End /TN`
- `package.json`
- 已新增脚本:
- `smoke:desktop-single-instance`
- `build/scripts/README.md`
- 已登记 `desktop-single-instance-smoke.ps1`
## 已验证
- 已通过:
```powershell
corepack pnpm typecheck
```
结果:通过。
## 当前阻塞
自动 smoke 目前还没有跑通,阻塞点不是 TypeScript 编译,而是本机环境残留了多个无法在当前权限下结束的 `electron.exe`
已观察到的残留 PID:
- `10420`
- `19736`
- `23484`
- `23564`
- `23588`
- `23820`
- `24384`
现象:
- `corepack pnpm smoke:desktop-single-instance` 失败
- 失败点是首实例迟迟没有写出 `ready` 文件
- 手工尝试结束残留进程时,`taskkill` / `Stop-Process` 都报 `Access is denied`
- 在残留进程存在时,单实例锁环境不干净,自动验证结果不可信
失败输出里最关键的信息是:
- `First Electron instance never reported single-instance readiness.`
相关失败产物路径:
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\startup\startup-latest.log`
## 已尝试但未完成
已执行过这些命令,但因为权限问题未能清掉残留进程:
```powershell
taskkill /IM electron.exe /F /T
```
```powershell
Stop-Process -Id 10420,19736,23484,23564,23588,23820,24384 -Force
```
校验残留进程的命令:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
当前结论:
- 代码改动已经在仓库里
- 当前最先要解决的是机器上的残留 Electron 进程
- 在这个问题解决前,不要把 smoke 失败直接归因到单实例代码本身
## 下次继续时先做什么
优先顺序:
1. 先清掉所有残留 `electron.exe`
2. 再确认 `Get-Process electron -ErrorAction SilentlyContinue` 没有输出
3. 再跑单实例 smoke
4. 若 smoke 仍失败,再根据 `.tmp` 里的 trace/log 继续定位
推荐命令:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
如果仍杀不掉,直接重启机器。
重启或清理完成后先跑:
```powershell
corepack pnpm smoke:desktop-single-instance
```
如果还失败,再补跑:
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
目的:
- `desktop-single-instance-smoke` 用来验证单实例口径本身
- `startup-binding-smoke` 用来判断是不是更基础的桌面启动链路也有问题
## 继续排查时要看的文件
- `D:\qjclaw\apps\desktop\src\main\index.ts`
- `D:\qjclaw\build\scripts\desktop-single-instance-smoke.ps1`
- `D:\qjclaw\package.json`
- `D:\qjclaw\build\scripts\README.md`
失败时优先看:
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\startup\startup-latest.log`
- `D:\qjclaw\.tmp\desktop-single-instance-smoke\logs\runtime-manager.log`
## 当前工作区事实
和这次单实例相关的工作区改动包括:
- `apps/desktop/src/main/index.ts`
- `build/scripts/desktop-single-instance-smoke.ps1`
- `package.json`
- `build/scripts/README.md`
其中:
- `build/scripts/desktop-single-instance-smoke.ps1` 当前是新增文件
- 其余几个文件已有未提交改动
## 一句话结论
单实例代码和 smoke 脚本已经落地,`typecheck` 已通过;下次续接前先解决本机残留的高权限 `electron.exe`,否则自动验证不会有可信结果。
## 2026-04-21 最新进展补充
### 本轮已继续完成的代码侧工作
- `apps/desktop/src/main/index.ts`
- 已把单实例入口继续接完整:
- `app.requestSingleInstanceLock()`
- `second-instance` 接管并聚焦现有主窗口
- `mainWindow` 跟踪
- `focusMainWindow()`
- `restoreOrCreateMainWindow()`
- `createTrackedMainWindow()`
- 已补单实例 smoke 专用信号:
- `QJCLAW_SMOKE_SINGLE_INSTANCE`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_READY_PATH`
- `QJCLAW_SMOKE_SINGLE_INSTANCE_EVENT_PATH`
- `runSingleInstanceSmoke()`
- 已补窗口状态与日志辅助信息:
- `secondInstanceEventCount`
- `lastSecondInstanceEventSnapshot`
- `mainWindowLoadState`
- `window.load-state` 启动日志
- 已在 smoke 模式下加:
- `app.disableHardwareAcceleration()`
- `build/scripts/desktop-single-instance-smoke.ps1`
- 已去掉对 `Start-Process` 返回 PID 的“提前失败”判断
- 现在不会因为 Electron 首个 PID 变化而误判首实例退出
### 本轮已执行的验证
已执行并通过:
```powershell
corepack pnpm --filter @qjclaw/desktop build
```
```powershell
corepack pnpm typecheck
```
已执行但失败:
```powershell
corepack pnpm smoke:desktop-single-instance
```
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
### 关键新结论
- 当前问题已经不能再只归因到“单实例 smoke 本身”
- `startup-binding-smoke` 也失败了,说明更底层的桌面启动链路本身就不稳定
- `startup-binding-smoke` 的失败点是:
- Electron 还没写出 smoke output 就退出了
- 退出码是 `-2147483645`
- `desktop-single-instance-smoke` 仍然失败,但现在可以确认:
- 不再是脚本盯着首个 PID 导致的误判
- 更像是首实例窗口/渲染链路没稳定进入可用状态
### 这轮看到的最关键日志现象
- 单实例 smoke 的 trace 只走到:
- `runSingleInstanceSmoke:start`
- 基础启动 smoke 的 trace 走到:
- `runSmokeTest:start`
- `runSmokeTest:renderer-loading`
- `runSmokeTest:loading-renderer-state`
- `startup-latest.log` 里,主窗口 load-state 目前只看到:
- `created`
- 没有继续看到:
- `did-start-loading`
- `dom-ready`
- `did-finish-load`
- `did-fail-load`
- `render-process-gone`
这说明:
- 主窗口创建是发生了的
- 但后续页面加载事件没有稳定落到日志里
- 现象更接近 Electron / Chromium 级别的启动异常或系统态残留干扰
### 运行时相关补充观察
- `runtime-manager.log` 里持续可见:
- `Launching bundled runtime command`
- `Bundled runtime direct spawn was blocked with EPERM; retrying via PowerShell wrapper.`
- `Bundled runtime failed to spawn: spawn EPERM`
这不是本轮唯一问题,但说明当前环境里还有额外权限限制噪音,不能把 smoke 失败简单归因到单实例代码。
### 当前机器状态的新结论
当前机器上确认有一颗高权限残留 `electron.exe`
- PID:`7216`
- Parent PID:`12892`
- 但父进程已经不存在
- `Get-Process` 结果里:
- `HandleCount = 0`
- `Path` 为空
- `StartTime = 2026/4/21 15:30:31`
已尝试但仍失败:
```powershell
taskkill /PID 7216 /F /T
```
```powershell
Stop-Process -Id 7216 -Force
```
以及通过 `schtasks``SYSTEM` 身份触发 `taskkill`
当前判断:
- 这颗进程已经接近“残留/僵尸进程”
- 继续在当前系统态下跑 smoke,结果不可信
- 最稳妥做法仍然是先重启机器
### 更新后的下次继续顺序
1. 先重启机器,清掉 PID `7216`
2. 重启后先确认:
```powershell
Get-Process electron -ErrorAction SilentlyContinue
```
3. 如果没有输出,先跑:
```powershell
powershell -ExecutionPolicy Bypass -File build/scripts/startup-binding-smoke.ps1
```
4. 如果基础启动 smoke 通过,再跑:
```powershell
corepack pnpm smoke:desktop-single-instance
```
5. 如果基础启动 smoke 仍失败,优先看:
- `D:\qjclaw\.tmp\startup-binding-smoke\result.json`
- `D:\qjclaw\.tmp\startup-binding-smoke\result.json.trace.log`
- `D:\qjclaw\.tmp\startup-binding-smoke\logs\startup\startup-latest.log`
- `D:\qjclaw\.tmp\startup-binding-smoke\logs\runtime-manager.log`
### 当前这轮最准确的一句话结论
单实例代码和单实例 smoke 已继续补强,但新的验证结果表明当前主要阻塞已经下沉到更底层的 Electron 启动链路与机器残留进程状态;在重启清掉高权限残留 `electron.exe` 之前,继续跑 smoke 不会有可信结果。
# 2026-04-22 Desktop UI Regression Handoff
## 背景
当前桌面端存在三类已被本地代码事实确认的回归:
1. 幕布/启动遮罩被改坏:
- logo 过大
- 背景视觉失控
2. 主界面被改坏:
- 左侧工作区样式异常
- 右侧工作区白屏/内容消失
3. 窗口控件异常:
- 关闭/最大化/最小化按钮跑到左上角
本交接文档的目标,是把“哪里坏了、为什么坏了、应该怎么修、修的时候要防什么回归、怎么验收”一次性写清楚,供另一个 AI 直接执行。
## 已确认的代码事实
### 1. 幕布/logo 变大的直接原因
`6dec17b feat(客户端): 增加专家模型配置与品牌化打包支持``apps/ui/src/App.tsx` 新增了两组品牌图片节点:
- 侧栏 logo:
- `sidebar-logo-mark-shell`
- `sidebar-logo-mark`
- 启动幕布 logo:
- `startup-overlay-mark-shell`
- `startup-overlay-logo`
对应位置:
- `apps/ui/src/App.tsx` 侧栏 logo 区域
- `apps/ui/src/App.tsx` 启动遮罩品牌区
但在以下样式文件中,没有找到这些类名的样式定义:
- `apps/ui/src/styles.css`
- `apps/ui/src/tailwind.css`
同时,`apps/ui/src/assets/brand-icon.png` 的实际尺寸是 `512x512`。在没有宽高约束的前提下,图片按原始尺寸渲染,足以解释“logo 很大”。
结论:
- 这是明确的样式缺失,不是素材本身问题。
### 2. 幕布背景失控的直接原因
启动幕布的基础背景样式定义在 `apps/ui/src/styles.css``.startup-overlay``.startup-overlay-panel``.startup-overlay-copy h1` 等规则中。
同一个 `6dec17b` 提交把幕布头部结构从“纯文字标题”改成了“图片 logo + 文案”的新结构,但没有同步补齐:
- `startup-overlay-brand-line`
- `startup-overlay-brand-copy`
- `startup-overlay-mark-shell`
- `startup-overlay-logo`
结论:
- 幕布不是单纯“颜色不对”,而是 DOM 结构变了、对应样式没补齐,导致既有幕布布局失衡。
### 3. 主界面左侧/右侧被改坏的主要来源
`91f42aa feat(ui): refresh chat and settings interactions` 对主界面结构做了较大改动,重点包括:
- 侧栏容器改成带大量强制类的新版结构
- 会话区结构改成新的 `sidebar-session-actions` / `sidebar-session-menu`
- 主内容区改成新的 `conversation-panel-head-layout`
- 新增 `page-drag-strip``conversation-drag-strip`
-`conversation-panel``conversation-panel-body``composer-shell` 大量追加 `!` 强制覆盖类
对应文件:
- `apps/ui/src/App.tsx`
但样式层没有补齐同等粒度的定义。已确认缺失或未在样式文件中落地的类名包括:
- `sidebar-session-actions`
- `page-drag-strip`
- `conversation-drag-strip`
- `settings-input-label`
- `sidebar-logo-mark`
- `startup-overlay-logo`
结论:
- 这一步不是“小修小补”,而是把主界面结构先改了,但样式体系没有完整跟上。
- 右侧“白屏/消失”优先按“布局容器回归”处理,不要先假设是数据流或 IPC 问题。
### 4. Tailwind 样式管线放大了回归
`5e19ad5 feat(ui): add tailwind style pipeline` 新增:
- `apps/ui/src/tailwind.css`
- `apps/ui/src/main.tsx` 中在 `styles.css` 后继续引入 `tailwind.css`
事实:
- `tailwind.css``styles.css` 之后加载
- `App.tsx` 中又引入了大量 `!` 优先级类
- 新旧两套样式同时作用于同一批 DOM
结论:
- 当前回归不只是“某几个 class 写错了”,而是旧样式、Tailwind 组件层、内联强制类三套机制叠加,放大了布局失衡。
### 5. 窗口按钮跑到左上角的直接原因
当前磁盘上的 `apps/desktop/src/main/create-window.ts` 包含以下设置:
- `Menu.setApplicationMenu(null)`
- `titleBarStyle: "hidden"`
- `titleBarOverlay: false`
- `frame: false`
这意味着:
- 原生标题栏被去掉
- 窗口拖拽区和窗口控制按钮完全依赖前端自绘
同时,前端 `App.tsx` 中存在自定义窗口控件:
- `window-controls`
- `window-control-button`
但在样式文件中没有找到对应样式定义。
结论:
- “按钮跑到左上角”不是 Electron 自己乱了,而是无边框窗口已开启,但前端窗口控件样式未落地,最终按普通块元素渲染。
### 6. 一个额外风险事实
本地排查时观察到:
- `git rev-parse HEAD:apps/desktop/src/main/create-window.ts` 指向的版本,不包含上面的无边框窗口设置
- 但磁盘上的 `apps/desktop/src/main/create-window.ts` 包含这些设置
- `git status` 没有把这个文件标成已修改
这说明当前工作区存在一个异常点:
- 磁盘文件状态和 Git 记录不一致
在正式修改前,执行方必须先再次核实:
- 实际运行的是哪个文件内容
- 本地是否存在未正常反映到 `git status` 的工作树状态
这里不能拍脑袋直接回退。
## 修复目标
本轮只修复 UI 回归,不扩散到无关功能:
1. 幕布恢复正常:
- logo 尺寸受控
- 品牌区布局稳定
- 背景和文案不冲突
2. 主界面恢复正常:
- 左侧工作区宽度、卡片、滚动区恢复
- 右侧会话区恢复可见和可滚动
- 不再出现大面积空白
3. 窗口控件恢复正常:
- 按钮位置回到右上角
- 拖拽区域和点击区域边界明确
4. 修复方式可回归验证,不引入新的结构性风险。
## 建议修复方案
### 方案总原则
不要在当前基础上继续“补更多 Tailwind 强制类”。优先做减法:
1. 先收敛结构
2. 再补缺失样式
3. 最后决定是否保留 frameless 窗口
### A. 先修幕布和 logo
目标文件:
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
动作:
1. 给以下类补齐明确样式,而不是继续依赖默认图片尺寸:
- `sidebar-logo-mark-shell`
- `sidebar-logo-mark`
- `startup-overlay-brand-line`
- `startup-overlay-brand-copy`
- `startup-overlay-mark-shell`
- `startup-overlay-logo`
2. 明确约束:
- 侧栏 logo 用固定容器,图片宽高固定,`object-fit: contain`
- 幕布 logo 也用固定容器,不允许原图裸渲染
3. 幕布品牌区改为稳定的横向或竖向布局,不要让图片直接参与标题文本的现有排版逻辑。
建议优先值:
- 侧栏 logo 容器:`72px``96px`
- 幕布 logo 容器:`96px``128px`
### B. 收敛主界面结构,先恢复稳定布局
目标文件:
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/ui/src/tailwind.css`
动作:
1.`6dec17b` 之前的主界面稳定骨架为参考,核对以下区域:
- `shell`
- `sidebar`
- `main-shell`
- `content-area`
- `conversation-panel`
- `conversation-panel-body`
2.`91f42aa` 引入的大量 `!` 强制类做收敛:
- 只保留确实必要的交互视觉类
- 去掉会改变主布局边界、宽高、overflow 的强制类
3. 如果某个新类没有对应样式定义,二选一:
- 要么补样式
- 要么删掉该 class 和相关结构
- 不允许继续保留“有 DOM 无样式”的中间状态
4. 优先恢复右侧主内容区的高度链路:
- `shell`
- `main-shell`
- `content-area`
- `conversation-panel`
- `conversation-panel-body`
- `message-list`
重点检查:
- `min-height: 0`
- `overflow: hidden/auto`
- `flex: 1`
- `grid` / `flex` 嵌套层级
### C. 窗口控件修复策略
目标文件:
- `apps/desktop/src/main/create-window.ts`
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/ui/src/tailwind.css`
分两步处理,先选低风险方案。
#### 低风险优先方案
先恢复原生标题栏,不再依赖前端自绘窗口控件。
做法:
1. 去掉或回退:
- `frame: false`
- `titleBarStyle: "hidden"`
- `titleBarOverlay: false`
2. 删除或禁用前端自绘:
- `window-controls`
- `window-control-button`
- `app-drag-region`
- `app-no-drag`
- `page-drag-strip`
- `conversation-drag-strip`
适用条件:
- 如果项目当前没有明确要求必须保留自定义无边框窗口
这是本轮推荐默认方案,因为风险最低。
#### 次选方案
如果必须保留 frameless 窗口,再补完整自绘方案。
要求:
1. `window-controls``window-control-button` 必须补全样式
2. 顶部 drag 区必须是稳定、连续、可见但克制的热区
3. 所有交互控件明确 `no-drag`
4. 聊天页、专家页、设置页都要统一验证
若选择此方案,测试量明显更大。
### D. Tailwind 管线收敛
目标文件:
- `apps/ui/src/tailwind.css`
- `apps/ui/src/main.tsx`
动作:
1. 先确认 `tailwind.css` 中哪些规则是真正必需的
2. 对仅用于本次改坏布局的规则做减法,尤其是:
- 全局字体和背景覆盖
- 与旧样式重复定义的布局类
- 只定义了一半的组件类
3. 如果某一类组件主要靠 `styles.css` 驱动,就不要再在 `tailwind.css` 中半覆盖同一结构
## 修改范围
本轮允许修改:
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/ui/src/tailwind.css`
- `apps/ui/src/main.tsx`
- `apps/desktop/src/main/create-window.ts`
本轮原则上不动:
- IPC
- runtime
- gateway
- shared-types
- 配置存储逻辑
- 专家路由逻辑
除非在修复中确认右侧白屏不是布局问题,而是页面根本没渲染成功。只有出现明确证据时,才扩大范围。
## 风险点
### 1. 工作树状态异常风险
`create-window.ts` 存在“磁盘内容和 HEAD 内容不一致,但 `git status` 不报修改”的现象。
风险:
- 执行方可能以为自己修了源文件,实际运行的却不是这份内容
要求:
- 修复前先做一次内容核对
- 修复后再核对一次实际运行文件和构建产物
### 2. 旧样式和 Tailwind 冲突风险
当前同时存在:
- `styles.css`
- `tailwind.css`
- `App.tsx` 里的大量强制类
风险:
- 只修一层会被另一层覆盖
要求:
- 每改一块布局,都要同时看 DOM、旧样式、Tailwind 样式、强制类四件事
### 3. 无边框窗口交互风险
如果继续保留 `frame: false`,风险包括:
- 拖拽区域误吞点击
- 按钮点击变拖拽
- 页面局部可拖、局部不可拖
要求:
- 没有强需求时,优先回原生标题栏
### 4. 误把白屏归因到数据层的风险
当前没有直接证据证明右侧白屏来自 IPC、workspace summary 或消息加载失败。
风险:
- 执行方误入数据流排查,修一圈还没解决布局
要求:
- 先把布局骨架恢复,再决定是否需要继续查数据层
## 测试计划
### 1. 静态检查
执行:
```powershell
corepack pnpm typecheck
```
目标:
- 确保 UI 改动没有引入类型错误
### 2. 桌面主界面人工回归
验证项:
1. 启动后幕布 logo 尺寸正常,不再按原图 512x512 裸显示
2. 幕布品牌区、文案、进度条不重叠
3. 进入主界面后左侧侧栏宽度正常
4. 左侧 logo 卡片尺寸正常
5. 左侧会话区可以正常显示和滚动
6. 右侧会话区可见,不是整块白屏
7. 消息区和输入区都能正常显示
### 3. 窗口控件回归
验证项:
1. 关闭/最大化/最小化按钮出现在预期位置
2. 点击三个按钮行为正确
3. 窗口拖拽行为正确
4. 不会出现“按钮在左上角裸排”的情况
### 4. 多页面回归
至少验证:
1. 聊天页
2. 专家页
3. 设置页
原因:
- 这些页面都被 `91f42aa` 的结构改动波及
### 5. CSS 冲突回归
验证项:
1. 删除或收敛某批 `!` 强制类后,页面没有因为 Tailwind 覆盖再次错位
2. `styles.css``tailwind.css` 中对同一块布局的控制关系明确
## 建议执行顺序
1. 先核对 `create-window.ts` 的真实工作树状态
2. 先修 logo 与启动幕布缺样式问题
3. 再修主界面布局骨架
4. 再决定窗口控件走“回原生标题栏”还是“补完整自绘”
5. 最后跑 `typecheck` 并做桌面人工回归
## 一句话结论
当前回归不是单点 bug,而是三步叠加造成的:
- `6dec17b` 引入品牌图片和窗口控件结构,但没补齐样式
- `5e19ad5` 引入 Tailwind 样式管线,放大覆盖冲突
- `91f42aa` 重排主界面结构,又继续增加了大量未完全落地的新类和强制样式
修复时要先做减法,优先恢复稳定布局和原生窗口行为,再决定是否保留自定义视觉方案。
# 2026-04-24 抖音客户端配置接入进度
## 2026-04-25 当前状态
截至 2026-04-25,抖音项目这一轮的主结论如下:
- 客户端真实配置注入链路已打通,运行时以客户端配置为主口径
- `workspace_entry.py` 的 pending intake / continue 主链路已恢复到可验证状态
- `VectCut` 已从本轮阻断项中移除,不再作为抖音运行时必填
- 客户端聊天附件入口已从“仅图片”扩展为“图片 + 通用资料文件”
- PDF 附件已在桌面 smoke 中完成真实落盘验证
当前更准确的表述不是“只差 UI 验收”,而是:
- 配置接入、运行时注入、pending intake 回归、通用附件上传已经完成并有验证
- 下一阶段仍然是客户端真实聊天验收
## 本轮新增完成项
### 1. 客户端真实配置接入
- 客户端设置保存后,抖音项目运行时改为从客户端配置注入 `project.env`
- `QJC_CLIENT_CONFIG_ACTIVE=1` 已在 smoke 中确认
- 抖音相关模型配置已覆盖:
- `VIDEO_LLM_ANALYZER_*`
- `REPLICATION_BRIEF_*`
- `SEEDANCE_*`
- `VOLC_*`
- `QINIU_*`
- `VectCut` 不再是本轮闭环前置阻断项
### 2. Pending Intake 回归修复已稳定
- `run_intake()` 已恢复为可调用主路径
- `workspace_entry.py` 的 pending intake / continue 逻辑已重新验证
- 当前不再继续主动改 `run_from_request.py` / `coordinator.py` 主业务编排
### 3. 客户端通用附件上传已接入
- 共享类型 `ChatAttachment.kind` 已从仅 `image` 扩展为 `image | file`
- 桌面端新增统一附件选择接口 `pickAttachments()`
- 当前聊天附件支持:
- 图片:`png/jpg/jpeg/webp/gif/bmp`
- 文档资料:`pdf/ppt/pptx/xls/xlsx/csv/tsv/doc/docx/txt/md/json`
- 图片继续落到 `inputs/images/main`
- 文档资料落到 `inputs/assets/manual`
- 聊天输入支持多附件发送
### 4. PDF 真实样本 smoke 已通过
本轮使用以下真实样本完成验证:
- `C:\Users\EDY\Desktop\douyin-example\2025欧文酵室招商手册(1).pdf`
已确认的事实:
- smoke 中附件类型为 `file`
- PDF 被真实落盘到项目目录:
- `projects/douyin/inputs/assets/manual/...pdf`
- assistant 回复中已包含 `Project attachments:` 回显
- 说明附件没有停留在 UI 层,而是实际进入了抖音项目工作区
## 已完成范围
- 客户端抖音模型配置读写、保存、注入
- 抖音运行时 `project.env` 注入
- `workspace_entry.py` 的 pending intake / continue 适配
- `run_intake()` 回归修复
- `VectCut` 非阻断化
- 客户端聊天附件从“仅图片”扩展到“通用资料文件”
- PDF 资料落盘到 `inputs/assets/manual`
- smoke 对真实配置和真实 PDF 的验证
## 当前未完成范围
- Pending Intake UI 手工闭环验收
- 三条业务链路聊天闭环验收
- OmniHuman / Seedance 聊天闭环验收
- 验收过程中暴露问题后的针对性回修
## 下一步顺序
1. Pending Intake UI 闭环验收
2. `research_replication + crawler``specified_sample + link``direct_creation + none` 三条链路聊天闭环验收
3. OmniHuman / Seedance 聊天闭环验收
4. 若验收暴露问题,再做最小范围回修
## 边界
- 不主动重写 `run_from_request.py` 三条主链路逻辑
- 不主动重写 `coordinator.py` 主业务编排
- `VectCut` 继续排除在本轮主线通过门槛之外
## 验证记录
- 2026-04-25:`python workspace/douyin/test_intake_integration.py` 28/28 通过
- 2026-04-25:`python workspace/douyin/test_pending_intake.py` 68/68 通过
- 2026-04-25:`corepack pnpm typecheck` 通过
- 2026-04-25:`corepack pnpm build` 通过
- 2026-04-25:`build/scripts/douyin-expert-cloud-bundle-smoke.ps1 -SettingsConfigPath "C:\Users\EDY\Desktop\龙虾密钥&模型配置信息.txt" -AttachmentFixturePath "C:\Users\EDY\Desktop\douyin-example\2025欧文酵室招商手册(1).pdf" -SkipMaterializeRuntime`
- 真实配置注入通过
- PDF 附件落盘通过
- assistant `Project attachments:` 回显通过
- 首次运行仍偶发 `Gateway connection closed (1012)`,重试后通过
## 2026-04-26 最新进度
### 已验证通过
- 客户端真实聊天链路中,上传 PDF 并描述需求后,可以走通纯画面 Seedance 视频生成。
- 已修复 Seedance split 最终视频 0 秒 / 缺音轨问题:
- `run_pipeline.py` 在无 `ffprobe` 时改用 `ffmpeg -i` 兜底解析媒体时长。
- audio compose 前后增加最终产物校验,避免 0 秒视频被写成 success。
- `workspace_entry.py` 已支持读取 `results.video_seedance.manifest` 内的嵌套最终视频字段,并在必要时用 `final_video_concat.mp4 + narration_audio_path` 兜底重新 mux。
- 已用坏产物目录验证修复:
- `E:\testworkspace2\projects\douyin\output\招商\seedance-split-runs\20260426-184026`
- 修复后 `final_video.mp4` 时长约 30 秒,包含视频流和音频流。
- `latest_seedance_split.mp4` 已同步更新。
- `D:\qjclaw\workspace\douyin` 已重新打包上传到云端配置。
- 本地开发版客户端已重启,`5173` 和内置网关 `18889` 正常监听。
### 当前仍有问题
- 数字人口播链路仍待排查。
- 上传音频 + 数字人图片生成链路仍待排查。
- 输入抖音链接并走样本/复刻相关链路仍待排查。
- Seedance 平台返回 `OutputVideoSensitiveContentDetected` 时,目前按平台拒绝生成处理,只提示用户调整需求,不做自动改写 prompt。
### 下一步建议
1. 先验证数字人口播:确认音频、数字人图片、模型配置、输出目录和失败日志。
2. 再验证“传音频 + 数字人图片”链路:区分是附件落盘、参数传递、OmniHuman 调用还是后处理失败。
3. 最后验证抖音链接链路:重点看 crawler/downloader/analyzer/selected sample continue 是否在客户端聊天里闭环。
\ No newline at end of file
# Desktop Startup Handoff (2026-04-07)
## Goal
排查并优化 Windows 安装包首启卡在“正在同步员工配置”的问题,重点处理两件事:
1. 修正启动状态汇总,避免把真实错误或后续阶段统一显示成“正在同步员工配置”
2. 增加首启结构化日志和更完整的 diagnostics 信息,便于现场机器定位卡点
## Current Status
当前不是完成态,但主体改动已经做了大半。
已完成:
- 主进程接入 startup logger 基础能力
- diagnostics 导出内容增强
- workspace summary 逻辑已改,新增 `syncing-projects` 阶段,避免“无项目”覆盖真实错误
- UI 已做最小配套修改:
- 支持 `syncing-projects`
- 导出 diagnostics 后显示 `startupLogPath`
- 启动错误遮罩上增加“导出诊断”按钮
未完成:
- 当前还没跑通整仓 typecheck
- 还没跑桌面端/安装包首启验证
- 日志点已接入到部分关键服务,但还没有做完整的 installer 现场回归
## Current Blocker
最新一次整仓类型检查:
```powershell
$env:COREPACK_HOME='D:\qjclaw\.corepack'; corepack pnpm typecheck
```
结果:
- `packages/shared-types` 通过
- `packages/gateway-client` 通过
- `apps/ui` 失败
当前唯一已确认报错:
- `apps/ui/src/App.tsx:969`
- `nextWorkspace` is possibly `null`
对应代码附近逻辑是启动轮询里的:
```ts
const nextWorkspace = await refresh(false);
const nextShouldPoll = Boolean(nextWorkspace) && (
nextWorkspace.chatLaunchState === "starting"
|| (!nextWorkspace.shellReady && nextWorkspace.bindingRequired)
);
```
这里需要把 `nextWorkspace` 收窄后再访问字段。
## Files Changed For This Task
确认与本任务直接相关的文件:
- `apps/desktop/src/main/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/diagnostics.ts`
- `apps/desktop/src/main/services/project-bundle.ts`
- `apps/desktop/src/main/services/startup-logger.ts`
- `packages/shared-types/src/index.ts`
- `apps/ui/src/App.tsx`
## Important Functional Changes
### 1. New startup logger
新增:
- `apps/desktop/src/main/services/startup-logger.ts`
作用:
-`<logsPath>/startup/` 写 JSONL 风格首启日志
- 生成会话日志和 latest 日志
- 对 token / api key / password / URL 做基本脱敏
### 2. Shared types updated
`packages/shared-types/src/index.ts` 已添加:
- `WorkspaceStartupPhase` 新值:`syncing-projects`
- `DiagnosticsExportResult.startupLogPath?: string`
### 3. Diagnostics export enhanced
`apps/desktop/src/main/services/diagnostics.ts` 当前已支持导出更多现场信息:
- `workspaceSummary`
- `bundleSyncStatus`
- `startupLogPath`
- `reason`
并通过 `startupLogger` 写 diagnostics 导出记录。
### 4. Workspace summary fix
`apps/desktop/src/main/ipc.ts` 是本次最关键的修复点。
当前逻辑已经改成:
- `error` 不再被“无项目”覆盖
- 当 runtime cloud ready 但还没有项目时,进入 `syncing-projects`
- 如果 bundle sync 失败,返回明确错误 summary
- summary 变化会写 startup logger
- diagnostics export 时会附带 startup log path / workspace summary / bundle sync status
这部分是本次修复“首页闪一下又被‘正在同步员工配置’盖回去”的核心。
### 5. Index / service wiring
`apps/desktop/src/main/index.ts` 已做的事情:
- 创建 `startupLogger`
- 注入 `DiagnosticsService`
- 注入 `OpenClawConfigClient`
- 注入 `ProjectBundleService`
- 注入 `registerDesktopIpc`
`cloud-api.ts` / `project-bundle.ts` 已开始接入 logger,但后续仍建议在类型检查通过后再补一次日志点覆盖率检查。
### 6. UI minimal support
`apps/ui/src/App.tsx` 当前的目标性修改只有这些:
- `startupCurtainCopy.syncingProjects`
- `getStartupProgress()` 支持 `syncing-projects`
- `getStartupCurtainStatus()` 支持 `syncing-projects`
- diagnostics 成功提示里包含 `startupLogPath`
- 启动错误遮罩新增 diagnostics 导出按钮
## Other Dirty Files In Worktree
当前 `git diff --name-only` 里还有这些未提交改动:
- `apps/desktop/src/main/services/project-store.ts`
- `apps/desktop/src/preload/index.ts`
- `apps/ui/src/styles.css`
- `build/scripts/electron-smoke.ps1`
这些不在我本次“启动日志/状态机”主线修改的核心列表里,可能是已有改动或其他任务改动。
如果另一个 AI 要继续本任务,不要默认回退这些文件。
## Whether This Affects Another Task
会不会影响,取决于另一个任务改哪些文件。
影响较小的情况:
- 另一个任务不改下列文件:
- `apps/desktop/src/main/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/diagnostics.ts`
- `apps/desktop/src/main/services/project-bundle.ts`
- `apps/ui/src/App.tsx`
- `packages/shared-types/src/index.ts`
容易冲突的情况:
- 另一个任务要改 `App.tsx`
- 另一个任务要改 desktop main 启动链路
- 另一个任务要改 diagnostics / preload / shared-types IPC 类型
所以如果你只是临时切去改完全无关模块,通常问题不大;如果另一个任务也碰启动、UI 主壳、共享类型或 preload,冲突概率就高。
## Recommended Pause Strategy
如果要先切走做别的任务,建议:
1. 不要继续在当前改动基础上随手混改启动相关文件
2. 让另一个任务尽量避开本任务相关文件
3. 如果另一个 AI 要继续本任务,先读本文件,再跑一次 typecheck,从 UI 的 nullability 报错开始收尾
## Recommended Next Steps For The Next AI
1. 修复 `apps/ui/src/App.tsx:969` 的空值收窄问题
2. 重新运行:
```powershell
$env:COREPACK_HOME='D:\qjclaw\.corepack'; corepack pnpm typecheck
```
3. 如果 typecheck 继续报主进程相关错误,再逐个修:
- `index.ts`
- `cloud-api.ts`
- `project-bundle.ts`
- `ipc.ts`
4. typecheck 通过后,验证桌面端:
- 启动阶段文案是否出现 `syncing-projects`
- diagnostics 导出是否返回 `startupLogPath`
- 启动错误遮罩是否能导出日志
5. 再做安装包/首启验证,重点看是否能区分:
- 员工配置请求失败
- 项目 bundle 同步失败
- runtime 启动失败
- gateway 连接失败
- summary 被覆盖
## Useful Commands
查看工作区变更:
```powershell
git -c safe.directory=D:/qjclaw diff --name-only
```
重新看 UI 变更:
```powershell
git -c safe.directory=D:/qjclaw diff -- apps/ui/src/App.tsx
```
重新跑类型检查:
```powershell
$env:COREPACK_HOME='D:\qjclaw\.corepack'; corepack pnpm typecheck
```
## Notes
- 这个仓库当前在 Windows 环境下,且 `D:\qjclaw` 需要按 safe.directory 方式使用 git
- 我这轮没有做 destructive revert
- 当前最重要的是先保持工作区稳定,不要在 `App.tsx` 上再叠加大改
\ No newline at end of file
# QianjiangClaw Progress Summary
Updated: 2026-03-24
## 1. Project Goal
Current practical goal:
- Keep the desktop app as the main Windows client while moving from technical PoC to real deliverable integration.
- Use Electron plus React plus TypeScript plus pnpm workspace.
- Keep `Renderer -> preload -> Electron Main -> GatewayClient / RuntimeManager / cloud clients` as the stable boundary.
- Ship a bundled OpenClaw runtime inside the installer so the desktop app becomes the single user-facing entry point.
The practical end goal is:
- user installs one Windows app
- user binds an OpenClaw employee `api_key`
- app fetches runtime configuration from cloud
- app generates local managed runtime config automatically
- app starts bundled OpenClaw runtime locally
- app reports runtime liveness and events back to cloud
- user can use the installer without manually filling provider, model, base_url, or vendor model api_key
## 2. Current Stage
Current stage should now be treated as:
- Stage A: complete
- Stage B: complete
- Stage C technical PoC: complete
- Stage D formal delivery: started, but not complete
Practical interpretation:
- installer generation works
- installer bundled-runtime smoke works
- desktop app can carry and launch bundled runtime on this machine
- real runtime cloud config integration for `openclaw-employee-config` is implemented
- runtime cloud supervisor is implemented for heartbeat, config sync, and event flush
- bundled runtime now includes a preinstalled Python layer for core table/document/web/config tasks
- diagnostics and smoke now expose runtime cloud telemetry
- installer can now be treated as trial-usable, but not yet as a final stable delivery
## 3. What Has Been Completed
### 3.1 Desktop architecture and local runtime path
Completed:
- Electron plus React plus TypeScript desktop app is working
- preload and IPC boundary is in place
- real local Gateway integration works
- sessions, history, chat.send, diagnostics export, and keytar-backed secrets are implemented
- bundled runtime detect, start, stop, restart, health, and tail logs are implemented
### 3.2 Installer and bundled runtime PoC
Completed:
- NSIS installer is generated at `dist/installer/QianjiangClaw-Setup-0.1.0.exe`
- installer smoke passes
- installer bundled-runtime smoke passes
- installer includes `vendor/openclaw-runtime` as extraResources
- generated installer can launch bundled runtime and connect bundled Gateway in the current verification environment
### 3.3 Bundled Python runtime payload
Completed in code and packaging:
- bundled runtime now materializes a Python runtime under `vendor/openclaw-runtime/python`
- the payload includes the first locked dependency set for built-in Skills:
- `openpyxl`
- `pandas`
- `requests`
- `beautifulsoup4`
- `lxml`
- `pypdf`
- `python-docx`
- `charset-normalizer`
- `pyyaml`
- runtime readiness now requires Node/OpenClaw files plus Python payload plus import self-check
- bundled runtime smoke now asserts `pythonReady = true`
- installer smoke now validates the packaged Python payload and imports the locked package set directly from `resources/vendor/openclaw-runtime/python`
### 3.4 Product UI baseline
Completed:
- desktop product UI is in place
- workspace page prioritizes account summary, current skill, execution policy, chat, and session flow
- diagnostics page includes Gateway, bundled runtime, logs, installed paths, runtime cloud, and export snapshot
- settings page hosts runtime credential binding for OpenClaw employee `api_key`
- runtime cloud telemetry status is visible in diagnostics
### 3.5 Runtime cloud configuration integration
Completed in code:
- `OpenClawConfigClient` is implemented around `POST /openclaw-employee-config`
- the desktop app stores employee `api_key` in `SecretManager/keytar`
- the settings flow can save the runtime cloud base URL and employee `api_key`
- bundled runtime startup is now driven by cloud-fetched config instead of only the old local PoC path
- the returned payload is normalized into managed runtime config before runtime startup
- diagnostics now include runtime cloud config status
Implemented runtime-cloud behavior:
- if employee `api_key` is missing, runtime stays unconfigured and should not auto-start as usable
- if `POST /openclaw-employee-config` succeeds, the app extracts runtime-critical fields such as:
- `llm.provider.base_url`
- `llm.provider.api_key`
- `llm.model_id`
- `config_version`
- runtime heartbeat/events endpoints
- those values are merged into the local managed runtime config used by bundled OpenClaw
- runtime config sync is now implemented with a periodic `action: "sync"` flow
- sync results update runtime cloud status and runtime telemetry state
### 3.6 Runtime cloud heartbeat, events, and telemetry
Completed in code:
- `RuntimeCloudSupervisor` is implemented in Electron Main as the runtime cloud orchestrator
- `POST /openclaw-heartbeat` is implemented and scheduled through the supervisor
- `POST /openclaw-employee-events` is implemented with in-memory queueing and batch flush
- runtime cloud event production is wired into desktop lifecycle and chat flow
- runtime telemetry is exposed through preload/IPC as `runtimeTelemetry.getStatus()`
- diagnostics export includes `runtimeTelemetry`
- smoke validates runtime telemetry instead of only runtime startup
Implemented event/telemetry behavior:
- heartbeat loop runs independently of local runtime health and does not stop bundled runtime on cloud failure
- config sync loop runs independently and records changed/unchanged state into telemetry
- event queue batches and flushes runtime cloud events without blocking desktop chat flow
- the current core event set includes:
- `startup`
- `message_received`
- `message_sent`
- `error`
- `config_updated`
- `shutdown`
- runtime telemetry currently records:
- heartbeat success/error and last billing summary
- config sync success/error
- queued and accepted event counts
- message/error counters
- current config version
### 3.7 Real cloud endpoint verification
Verified:
- the real endpoint `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1/openclaw-employee-config` responds successfully with a real employee `api_key`
- the response contains runtime-critical fields including:
- `config_version`
- `llm.provider.base_url`
- `llm.provider.api_key`
- `llm.model_id`
- `endpoints.heartbeat`
- `endpoints.events`
This means the project is no longer only using mock contract data for runtime configuration.
### 3.8 Current verification result
Verified in the latest local acceptance pass:
- `corepack pnpm typecheck` passes
- `corepack pnpm build` passes
- `build/scripts/electron-smoke.ps1` passes
- smoke confirms bundled runtime is running in `bundled-runtime` mode
- smoke confirms the packaged Python payload contains the 9 locked dependencies
- smoke confirms runtime telemetry records heartbeat, config sync, and accepted runtime cloud events
### 3.9 Frontend refactor progress
Completed in the current frontend refactor pass:
- sidebar composition is extracted from `App.tsx` into `apps/ui/src/features/shell/AppSidebar.tsx`
- `AppSidebar` now composes `Sidebar`, `ExpertTree`, and `SessionList`
- expert category config and matching logic now live in `ExpertTree`
- `App.tsx` no longer inlines sidebar nav, expert tree, or session list JSX
- confirmed-unused legacy sidebar/expert CSS rules were removed
- current `App.tsx` size is about 4178 lines, so the sidebar split is complete but further reduction is still needed
Verified:
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:settings`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
## 4. Current Product Decision
The current Stage D product decision is:
- the installer becomes practically usable only after bundled OpenClaw starts from real cloud-fetched config
- the user must first bind an OpenClaw employee `api_key`
- this `api_key` is the employee identity key used by the runtime cloud API
- this is not the upstream LLM vendor api key
- the desktop app stores this key in `SecretManager/keytar`
- the desktop app calls `POST /openclaw-employee-config` with `{ api_key, action: "init" }`
- the returned payload is turned into local managed runtime config before bundled runtime start
- the bundled Python payload is part of the formal runtime contract, not a first-run dynamic install path
- Electron Main owns runtime cloud orchestration for config sync, heartbeat, and events
Current runtime endpoints from the cloud document are:
- `POST /openclaw-employee-config`
- `POST /openclaw-heartbeat`
- `POST /openclaw-employee-events`
At this moment:
- `POST /openclaw-employee-config` is implemented and verified
- `POST /openclaw-heartbeat` is implemented
- `POST /openclaw-employee-events` is implemented
- runtime cloud config sync is implemented
- runtime telemetry is implemented and exported through diagnostics/smoke
## 5. What Is Still Missing
### 5.1 Final stable installer usability is still not complete
Not complete yet:
- installer usability has moved beyond pure PoC, but still needs more real-world validation
- long-running real task execution stability still needs verification
- final product-side account, credits, and Skills production integration is still incomplete
### 5.2 Runtime-side cloud follow-up is still pending
Not complete yet:
- broader runtime event coverage beyond the current core set
- stronger runtime error recovery and offline replay strategy
- production-grade hot-update behavior after config sync changes
- more real-world acceptance for long-running heartbeat/events stability
### 5.3 Product-side cloud integration is still pending
Later work still pending:
- real login/session production integration
- real profile production integration
- real credits production integration
- real Skills production integration
- final model-policy production integration
## 6. Most Important Current Conclusion
The project is no longer primarily blocked on installer packaging or local runtime control-plane work.
The current state is:
- installer exists
- bundled runtime exists inside the installer
- user-bound employee `api_key` can now drive real runtime cloud configuration
- `openclaw-employee-config` can return the real `base_url`, provider `api_key`, and `model_id` needed by OpenClaw
- the desktop app can use those values to generate managed runtime config automatically
- the installer now carries a preinstalled Python layer for the first batch of built-in Skills
- runtime cloud supervisor now keeps heartbeat, config sync, and event flush running in Electron Main
- diagnostics and smoke now verify runtime telemetry, not only runtime startup
So the installer is no longer an empty shell.
However, the project is still not a fully finished formal delivery because:
- runtime cloud core APIs are finished, but broader runtime recovery and event coverage are not
- full business-side cloud APIs are not finished
- broader real-world acceptance is still pending
## 7. Immediate Next Work
The next work should happen in this order:
1. keep the bundled Python payload contract stable in future installer builds
2. expand runtime event coverage and improve failure recovery/offline replay
3. run a broader acceptance pass with installer + real employee `api_key` + real task execution
4. continue product-side production API integration for account, credits, Skills, and model policy
5. evaluate whether config sync should trigger stronger runtime refresh behavior
6. only after that treat Stage D as entering broader product delivery work
## 8. Next-Start Checklist
When work resumes next time, start here:
1. read the runtime cloud API source document under `docs/` (the runtime cloud API integration document)
2. read `docs/progress-summary.md`
3. verify `openclaw-employee-config`, `openclaw-heartbeat`, and `openclaw-employee-events` still respond correctly in the target environment
4. verify the bundled Python payload is still present in `vendor/openclaw-runtime/python`
5. verify the packaged Python payload is still present in `dist/installer/win-unpacked/resources/vendor/openclaw-runtime/python`
6. run smoke and confirm runtime telemetry still shows heartbeat/config sync/event acceptance
7. run installer acceptance with a real employee `api_key`
8. expand runtime-side recovery/event coverage where needed
9. then continue the remaining product-side cloud APIs
## 9. Current One-Line Status
Current status: installer, bundled runtime, preinstalled Python payload, runtime cloud config, heartbeat, events, config sync, and runtime telemetry are all working; the app can now auto-fetch OpenClaw runtime config from user-bound employee `api_key`, start bundled runtime, report cloud heartbeat/events, and export diagnostics, but Stage D is still incomplete until broader runtime hardening, product-side cloud APIs, and wider real-world acceptance are finished.
## 10. 2026-05-07 Frontend Refactor Follow-Up
Completed:
- removed `resolveExpertVisualKey` and `buildShortcutPrompt` from `AppSidebar` / `ExpertTree` props; `ExpertTree` now imports the pure helpers directly
- added `ConversationWorkspaceView` to own the chat/expert workspace display composition while keeping stream, composer, session, and smoke logic in `App.tsx`
- moved workspace status chip derivation into `features/shell/startupStatus.ts`
- reduced `App.tsx` to 2672 lines without changing smoke action shapes
Skipped intentionally:
- did not wire `useChatSessionsController`; current local session flow has subtle active-session reset behavior that should be reconciled separately
- did not delete CSS; duplicate selector areas are in the frozen conversation/final theme layers and were not safe to trim in this pass
Verified:
- App encoding check: first bytes `105,109,112`, no UTF-8 BOM
- `rg -n "�" apps/ui/src/App.tsx apps/ui/src/features apps/ui/src/lib`
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:settings`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
# 安装包瘦身回滚计划
## 当前结论
上一轮回滚不合格:它删除了 runtime cleanup,导致新安装包变成完整 payload,体积超过最近可用备份包。
- 最近可用基线:`dist/installer/backup/千匠问天-20260430-164341.exe`
- 基线大小:`538,315,023 bytes`
- 回滚后的异常包:`dist/installer/千匠问天-Setup-0.1.0.exe`
- 异常包大小:`596,924,319 bytes`
- 增量:`58,609,296 bytes`,约 `55.9 MiB`
## 回滚原则
瘦身失败时,必须回到最近可用小包基线,不能回到更大的完整 runtime payload。
允许保留:
- `ffmpeg/bin/ffmpeg.exe`
- `ffmpeg/bin/ffprobe.exe`
- `playwright-browsers/`
- 当前 runtime 必需 Python 依赖
必须恢复:
- runtime payload cleanup
- cleanup 统计写入 manifest
- OpenClaw docs 裁剪但保留 workspace templates
- Python tests、cache、pyc/pyo、source map 等无运行价值文件清理
## 修正方式
1. 恢复 `build/scripts/materialize-runtime-payload.ps1` 中 cleanup 相关逻辑。
2. 确认 `materializationInputs.schemaVersion` 回到 cleanup 对应版本。
3. 不修改 `apps/desktop/electron-builder.yml`,当前 `compression: store` 是既有状态。
4. 删除并重新生成 runtime payload。
5. 重新打包。
6. 对比新包大小,必须不大于 `538,315,023 bytes`
## 验证命令
```powershell
corepack pnpm materialize:runtime
corepack pnpm package
corepack pnpm typecheck
corepack pnpm smoke:ffmpeg-runtime
```
必要时再运行:
```powershell
corepack pnpm smoke:installer
```
## 接受标准
- `vendor/openclaw-runtime/runtime-manifest.json` 包含 `cleanupSummary``payloadStats`
- 新安装包大小 `<= 538,315,023 bytes`
- `ffmpeg.exe``ffprobe.exe` 仍在 runtime payload 中。
- 抖音视频/ffprobe 相关 smoke 不因瘦身缺少二进制而失败。
## 失败处理
如果新包仍然大于基线,不继续扩大回滚范围;先分析 manifest 的 `topLevelBreakdown``payloadStats`,定位新增体积来源后再补充 cleanup 规则。
# 左侧栏数字员工树形目录与 13/14 寸适配计划
## Summary
在不改业务逻辑的前提下,重构客户端左侧栏的信息密度和响应式表现。目标是:删除顶部 logo 品牌块,让导航和新建对话整体上移;把新建对话下方的专家区改成固定高度的“数字员工”树形目录;同时保持当前蓝紫玻璃质感、浅色渐变、圆角卡片、细描边的原 有
风格,不引入突兀的新视觉语言。
## Design Direction
- 延续现有风格:
- 保留浅色玻璃背景、蓝紫边框、柔和渐变、圆角卡片、轻阴影。
- 不改成纯扁平黑白,也不引入强烈新配色。
- 树形目录使用当前已有的紫蓝高亮、浅底卡片、细滚动条。
- 降低突兀感:
- 分类行高度、字体、圆角接近现有 `nav-item``sidebar-session-card`
- 展开专家项使用现有专家 SVG 图标体系,不用 emoji。
- 动效只保留 150–200ms 的颜色/箭头旋转,不做明显弹层或位移动画。
- 信息层级:
- 顶部:主导航 + 新建对话。
- 中部固定块:标题 `数字员工` + 树形分类。
- 底部弹性块:会话列表。
## Key Changes
- `App.tsx` 结构调整:
- 删除 `<div className="sidebar-logo-block">...千匠问天...</div>`
- 保留 `nav-list``sidebarNewSessionAction` 原 class,避免影响自动化。
-`sidebar-experts-entry` 内新增标题栏:
- 左侧文字:`数字员工`
- 可选右侧轻量计数:专家总数,例如 `8`
- 当前 `CATEGORY_CONFIG` 保留分类 id/name,但移除 emoji icon 依赖。
- 分类头从 `<div onClick>` 改为 `<button type="button">`
- 专家项从 `<div onClick>` 改为 `<button type="button">`
- 展开内容改为行内树结构,不再使用绝对定位弹出层。
- 树形目录行为:
- 点击“内容营销”展开其专家列表,再点收起。
- 点击专家后继续调用现有 `handleExpertSelect(entry)`
- 点击专家后仍关闭所有展开分类,沿用当前 `setExpandedCategories({})` 行为。
- 空分类不展示展开内容;分类行仍显示但可禁用或显示 `0`,推荐显示但禁用展开。
- 样式调整:
- `.sidebar` 改为响应式宽度:`clamp(232px, 19vw, 280px)`
- 删除 logo 后缩小 `.sidebar-top` 的 gap 和顶部 padding,让“对话”区域自然上移。
- `.sidebar-experts-entry` 固定高度:
- 普通桌面:约 `280px`
- 13/14 寸紧凑屏:约 `220–240px`
- `.sidebar-expert-scroll` 只在数字员工块内部滚动:
- `overflow-y: auto`
- `overflow-x: hidden`
- 透明/细滚动条
- `.sidebar-session-section` 保持 `minmax(0, 1fr)`,吃掉剩余高度。
- 13/14 寸适配:
- 增加紧凑断点,推荐条件:
- `@media (max-width: 1440px), (max-height: 820px)`
- 紧凑模式压缩:
- sidebar padding:从约 `24px/16px` 降到 `14–16px/12px`
- nav-item 高度:从 `44px` 降到 `38–40px`
- 新建对话高度:从 `46px` 降到 `40px`
- 数字员工块 padding/gap 下降 2–4px
- 会话卡片高度和间距轻微下降
- 保证主工作区仍使用 `minmax(0, 1fr)`,不产生横向滚动。
- `switchExpert`
- `activateHomeShortcut`
- `openHomeChat`
- `nav-item`
- `conversation-new-session`
- `sidebar-new-session`
- `sidebar-digital-workers-title`
- `expert-tree-list`
- `expert-tree-category`
- `expert-tree-category-trigger`
- `expert-tree-expert`
## Risk Points
- `styles.css` 里侧栏样式重复较多,后面的 `.conversation-shell ...` 规则优先级更高,实际改动应放在后段生效区,避免被覆盖。
- 删除 logo 后,侧栏顶部高度会明显变化,需要检查窗口拖拽区域是否仍可用。
- 固定数字员工高度可能挤压会话列表,必须在 `1366×768` 检查。
- 分类匹配目前依赖正则识别专家 id/name,新增专家可能落到“其他专家”。
- 不要删除核心 DOM class,否则 smoke helper 或自动化选择器可能失效。
- 当前有残留 Electron 进程 PID `32180` 曾拒绝结束,后续跑 smoke 前需确认不会干扰测试。
## Test Plan
- 静态验证:
- `corepack pnpm typecheck`
- `corepack pnpm build`
- 手动视觉检查:
- `1366×768`:13 寸常见低高屏,重点看是否挤压、是否横向滚动。
- `1440×900`:14 寸常见比例。
- `1536×864`:Windows 缩放常见工作区。
- `1920×1080`:标准桌面。
- 功能检查:
- “对话 / 数字员工 / 知识库 / 插件 / 设置”可切换。
- “新对话”可点击。
- “数字员工”标题存在。
- 点击“内容营销”展开专家。
- 点击专家后仍进入对应专家或填入快捷 prompt。
- 会话列表可滚动、切换、关闭。
- 数字员工块内部滚动,不影响整页布局。
- UI 验收:
- 无 logo 和“千匠问天”品牌块。
- 导航和新建对话明显上移。
- 树形目录不浮层遮挡。
- 样式与现有蓝紫玻璃风一致。
- 无横向滚动。
\ No newline at end of file
# 抖音 / 小红书项目包插件化接入方案
Status: implementation-ready
本文是对当前仓库实现和真实项目包现状的落地说明,目标是把抖音、小红书这类云端 zip 项目接入为:
1. 专家页先选项目
2. 进入项目后只显示该项目自己的 skills
3. 会话、上下文、执行入口都保持项目隔离
4. 默认走项目级 `workspace-entry`
本文是对通用规范 [docs/remote-project-bundle-spec.zh-CN.md](D:\qjclaw\docs\remote-project-bundle-spec.zh-CN.md) 的补充,重点回答当前抖音包这种结构是否需要改、最小要改什么、客户端要怎么配合。
## 1. 当前事实
### 1.1 客户端当前已经具备的能力
当前仓库已经实现了这些主链路:
- `ProjectBundleService` 可以把云端 zip 下载、解压、物化到本地项目工作区
- `ProjectStoreService` 可以维护 `projects/<projectId>`、active project、project sessions、bound skills
- `ProjectExecutionRouter` 可以在 `workspace-entry``skill``chat-fallback` 之间路由
- `ProjectContextService` 会把当前项目的 `SOUL.md``USER.md``README.md`、bound skills 注入隔离上下文
- UI 已经有 `projects``currentProjectId``skills` 这些项目级展示能力
也就是说,客户端架构本身已经是“项目优先”,不是“全局 skills 池优先”。
### 1.2 真实抖音包现状
真实目录:
`D:\微信缓存\xwechat_files\wxid_wo77jy2n4p5q21_3789\msg\file\2026-04\douyin`
已确认的结构事实:
- 根目录就是一个完整项目根,不是单 skill 包
- 根目录存在 `skills/`
- 根目录存在 `AGENTS.md`
- 根目录存在 `SOUL.md`
- 根目录存在 `USER.md`
- 根目录存在 `openclaw.json`
- 根目录不存在 `project.json`
- 根目录不存在旧版 `plugin/openclaw.plugin.json`
- `skills/` 下已经有成组的 Douyin skills,例如:
- `douyin-master`
- `douyin-writer`
- `douyin-visual`
- `douyin-publisher`
- `douyin-seedance-split`
- `jimeng-omnihuman`
- `seedance-2-0-video-generator`
- `seedream-image-generator`
这说明该包已经具备“项目内容”和“项目内 skills”,缺的不是技能内容,而是客户端识别项目所需的最小项目清单。
### 1.3 小红书后续约束
后续小红书项目也按与当前抖音包相同的结构交付。
因此本方案要求:
- 抖音包和小红书包都采用相同的项目根组织方式
- 两个包都通过各自的 `project.json` 声明项目身份和默认入口
- 不要求客户端从 `SKILL.md` 反推整个项目
## 2. 结论
基于当前代码现状,两个项目包的最小改动是:
- 抖音包补一个极简 `project.json`
- 小红书包补一个极简 `project.json`
除此之外:
- `skills/` 目录结构可以先不改
- `SKILL.md` 顶部元数据可以继续保留,不需要改成项目级配置来源
- `AGENTS.md``SOUL.md``USER.md` 可以继续作为项目上下文文件存在
不推荐的方案:
- 不推荐只靠扫描 `skills/*/SKILL.md` 顶部 metadata 去推断整个项目
- 不推荐把“项目识别”和“skill 识别”混成一层
- 不推荐让客户端为当前两个包额外做复杂的 `openclaw.json` 兼容推断作为主路径
原因很直接:
- 当前客户端主链路已经围绕 `project.json` 工作
- skill frontmatter 目前只适合做 skill 展示和 skill 路由,不适合承担项目入口声明
- 补一个 `project.json` 的成本远低于补一整套客户端兼容推断逻辑
## 3. 项目包交付约定
### 3.1 一包一项目
每个 zip 只承载一个项目:
- `douyin.zip` 对应 `douyin`
- `xhs.zip` 对应 `xhs`
客户端继续按当前实现处理:
- 云端配置返回多个 zip 下载项
- 客户端下载并解压每个 zip
- 每个 zip 物化成一个独立 project
### 3.2 根目录约定
zip 解压后,根目录直接是项目根。
不要求再额外包一层无意义目录。
推荐最小结构:
```text
<project-root>/
project.json
AGENTS.md
SOUL.md
USER.md
README.md
skills/
output/
```
说明:
- `README.md` 强烈建议有,但不是本次唯一阻塞项
- `output/` 是否存在由项目自身决定,不影响客户端识别
### 3.3 `project.json` 的职责
`project.json` 在本方案里只承担项目级声明:
- 稳定项目 ID
- 专家页展示基础信息
- 项目平台
- 默认执行入口
- 项目绑定的 skills
它不负责:
- 描述每个 skill 的详细说明
- 取代 `SKILL.md`
- 取代 `AGENTS.md` / `SOUL.md` / `USER.md`
## 4. 项目包模板
### 4.1 抖音 `project.json`
放在抖音 zip 根目录:
```json
{
"id": "douyin",
"name": "douyin",
"platform": "douyin",
"projectType": "automation-project",
"description": "抖音视频文案与视频生成专家",
"version": "2026-04",
"defaultEntry": {
"id": "workspace-entry",
"type": "workspace-entry",
"requiresSkills": [],
"capabilities": [
"video-script",
"storyboard",
"video-generation",
"publish"
],
"intentAliases": [
"抖音",
"抖音视频",
"视频文案",
"分镜",
"口播视频",
"数字人视频",
"生成视频"
]
},
"entries": [
{
"id": "workspace-entry",
"type": "workspace-entry",
"requiresSkills": [],
"capabilities": [
"video-script",
"storyboard",
"video-generation",
"publish"
],
"intentAliases": [
"抖音",
"抖音视频",
"视频文案",
"分镜",
"口播视频",
"数字人视频",
"生成视频"
]
}
],
"boundSkillIds": [
"douyin-intake-planner",
"douyin-master",
"douyin-publisher",
"douyin-seedance-split",
"douyin-visual",
"douyin-writer",
"jimeng-omnihuman",
"seedance-2-0-video-generator",
"seedream-image-generator"
],
"ready": true
}
```
### 4.2 小红书 `project.json`
放在小红书 zip 根目录:
```json
{
"id": "xhs",
"name": "xhs",
"platform": "xiaohongshu",
"projectType": "automation-project",
"description": "小红书内容生成与发布专家",
"version": "2026-04",
"defaultEntry": {
"id": "workspace-entry",
"type": "workspace-entry",
"requiresSkills": [],
"capabilities": [
"topic-selection",
"copywriting",
"image-generation",
"publish"
],
"intentAliases": [
"小红书",
"笔记",
"种草文案",
"发笔记",
"封面图",
"生成配图"
]
},
"entries": [
{
"id": "workspace-entry",
"type": "workspace-entry",
"requiresSkills": [],
"capabilities": [
"topic-selection",
"copywriting",
"image-generation",
"publish"
],
"intentAliases": [
"小红书",
"笔记",
"种草文案",
"发笔记",
"封面图",
"生成配图"
]
}
],
"boundSkillIds": [
"xhs",
"xhs-pipeline"
],
"ready": true
}
```
### 4.3 模板约束
两个项目包都必须满足:
- `id` 稳定且不可漂移
- `boundSkillIds` 必须与包内 `skills/<skillId>` 目录名一致
- `defaultEntry.type` 固定为 `"workspace-entry"`
- `entries` 至少显式声明一条 `workspace-entry`
- 不把真实用户密钥、长期 token、生产私密凭证固化进 zip 包
## 5. 客户端改造方案
### 5.1 主链路保持不变
本次不需要推翻现有项目隔离架构。
客户端继续沿用当前实现:
1. 云端配置返回多个 zip
2. `ProjectBundleService` 下载并解压 zip
3. 每个 zip 物化到独立 `projects/<projectId>`
4. `ProjectStoreService` 把项目写入本地 inventory
5. 专家页按 project 展示
6. 进入项目后按当前 active project 读取该项目的 skills、sessions、context
### 5.2 `project-bundle.ts`
保持当前 bundle 同步逻辑,补两点明确约束:
- 安装后校验项目根必须存在 `project.json`
- 若缺失,给出明确错误,而不是静默降级
推荐行为:
- zip 缺少 `project.json` 时,bundle sync 标记为 error
- 错误内容包含 zip 文件名和项目根路径
不建议在这里新增“扫描 `openclaw.json` 自动生成项目”的主路径逻辑。
这类兼容逻辑只应作为临时兜底,不应成为标准交付方式。
### 5.3 `project-store.ts`
这里是本次最值得补强的小改造点。
建议增加:
- `boundSkillIds` 与实际 `skills/` 子目录交叉校验
-`boundSkillIds` 中有不存在的 skill,则:
- 从最终项目 skill 列表中过滤掉
- 记录日志
- 不让整个项目失效
建议保持:
- `listProjectSkills(projectId)` 只返回当前项目绑定的 skills
- session 仍保持 `project:<projectId>:<uuid>` 格式
### 5.4 `project-execution-router.ts`
这里不需要大改。
当前路由规则继续成立:
- 未显式选 skill 时,优先走 `workspace-entry`
- 发布/自动执行类请求,优先走项目级入口
- 只有项目内部需要精确 skill 分流时,才交给 skill router
这和抖音 / 小红书两个项目包的定位是一致的:
- 一级入口是项目
- skill 是项目内能力,不是项目本身
### 5.5 UI / 专家页
UI 也不需要推翻,只需要明确展示层次:
- 专家页展示 project 列表,不展示全局混合 skill 列表
- 点击项目卡片后切换 active project
- 进入项目后,只显示当前项目的 skills
- 当前项目的 sessions 也只属于当前 project
需要坚持的交互约束:
- 抖音项目不显示小红书 skills
- 小红书项目不显示抖音 skills
- 不允许跨项目复用 session skill 绑定
## 6. 为什么不走“只识别 skills 就够了”
“只识别各自 `skills/` 目录就可以跑起来”这句话只对一半:
- 对“项目内 skill 展示和 skill 路由”成立
- 对“项目本身如何被专家页识别、如何确定默认入口”不成立
当前代码里:
- project 识别靠 `project.json` 和项目级元数据
- skill 识别靠 `skills/*/SKILL.md`
这两层不能混成一层。
如果只靠 `SKILL.md` 反推项目,客户端需要新增:
- 从多个 skill 中推断项目 ID
- 从多个 skill 中推断默认入口
- 处理主 skill / 辅助 skill / 通用 skill 歧义
- 处理 skill 缺失时项目是否仍然可见
这比每个包补一个 `project.json` 明显更复杂,也更不稳定。
## 7. 验收标准
至少完成以下验证:
1. 云端配置同时下发 `douyin.zip``xhs.zip`
2. 客户端本地出现两个独立项目
3. 专家页出现两个项目卡片
4. 选中 `douyin` 后,只能看到 douyin skills
5. 选中 `xhs` 后,只能看到 xhs skills
6.`douyin` 项目内发“生成一个苹果抖音视频文案”,默认走 `workspace-entry`
7.`xhs` 项目内发“小红书笔记文案”,默认走 `workspace-entry`
8. 切换项目不会污染对方的 session skill 绑定
9. `boundSkillIds` 配错时,客户端给出可诊断错误,不崩
10. 移除某个 zip 后,对应项目能被清理,另一个项目不受影响
## 8. 推荐实施顺序
### 第一步:项目包侧
先做最小包改动:
- 给抖音包补 `project.json`
- 给小红书包补 `project.json`
- 校对 `boundSkillIds`
### 第二步:客户端侧
补小幅硬化:
- `project-bundle.ts` 增加 `project.json` 缺失校验
- `project-store.ts` 增加 `boundSkillIds` 与实际目录交叉校验
- UI 确认专家页使用 project 维度展示,不混显示全局 skills
### 第三步:联调验收
用真实云端配置跑完整链路:
- cloud config
- zip sync
- 专家页选项目
- 进入项目后发送自然语言
- 命中 `workspace-entry`
## 9. 最终决策
对当前这两个项目包,标准交付方式定为:
- 一包一项目
- 根目录保留现有结构
- 各自补一个极简 `project.json`
- 客户端继续沿用现有项目隔离架构
本期不把“只靠 `skills/*/SKILL.md` metadata 自动反推整个项目”作为主目标。
# 安装包旧 URL 残留与 `_greenlet` 运行时缺陷修复方案
## 1. 背景
当前安装包在 A 电脑上暴露出两类独立问题:
1. 运行时云端地址仍命中旧地址 `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`,导致绑定失败。
2. 删除缓存后,应用可以启动,但在专家页触发小红书专家链路时,报错 `DLL load failed while importing _greenlet: 找不到指定的模块。`
结合项目代码与日志,两个问题不是同一个根因:
- 旧 URL 问题来自本地配置项残留,不是 `runtime-cloud-cache.json` 回退。
- `_greenlet` 问题发生在专家页触发的 Python + Playwright 自动化链,不是启动阶段本身失败。
## 2. 现状结论
### 2.1 旧 URL 问题
- 当前默认运行时云端地址已经是新地址:
- `apps/desktop/src/main/services/app-config.ts`
- 但只要本地 `app-config.json` 中存在 `runtimeCloudApiBaseUrl`,运行时就优先使用配置值。
- 启动日志已经明确显示:
- `baseUrl = https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
- `baseUrlSource = config`
结论:
- A 电脑不是“删了缓存还在读缓存”,而是“本地配置还保留旧地址”。
- 仅删除 `runtime-cloud-cache.json` 不足以修复;还需要处理 `app-config.json` 中的旧值。
### 2.2 `_greenlet` 问题
- 第二份 startup log 显示应用最终进入 `ready`,说明启动链路本身已完成。
- 专家页小红书项目会进入 Python/Playwright 自动化链。
- 当前运行时探测和 installer smoke 只校验了顶层包能否导入,例如 `playwright`
- 但并没有显式验证:
- `greenlet`
- `from playwright.async_api import async_playwright`
- 专家页真实触发后的项目执行路径
结论:
- 当前安装包验证口径偏浅。
- 安装包即使通过现有 smoke,仍可能在真实专家执行链路里因 `_greenlet` 或其 DLL 依赖失败。
## 3. 修复目标
修复后需要满足以下结果:
1. 旧机器升级或重装后,不会继续使用废弃的 runtime cloud URL。
2. 新机器首次安装时,默认直接使用新地址,无需手工清缓存或改配置。
3. 安装包里的 Python 运行时必须能真实支持专家页小红书链路所需的 Playwright/greenlet 依赖。
4. installer 验证必须能在构建阶段拦截这类问题,而不是等到用户机器上暴露。
## 4. 修复方案
### 4.1 运行时云端地址迁移
`apps/desktop/src/main/services/app-config.ts` 增加已废弃 runtime cloud URL 的迁移逻辑:
- 定义废弃地址列表,至少包含:
- `https://xuphfkscoptnjoaecbvn.supabase.co/functions/v1`
- 在配置加载归一化时,如果 `runtimeCloudApiBaseUrl` 命中废弃地址,则自动替换为当前默认地址:
- `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
- 替换后立即持久化写回 `app-config.json`
约束:
- 只迁移“已知废弃地址”
- 其他用户手工填写的非空自定义地址仍保留
- 不依赖用户手工删除配置文件
同时补充诊断:
- 启动日志中记录一次配置迁移事件
- diagnostics 中保留运行时云端地址的最终值,并在可能的情况下标记发生过迁移
### 4.2 打包 Python 运行时补齐 `greenlet`
在运行时 Python 依赖定义中显式纳入 `greenlet`,而不是只依赖 `playwright` 的间接依赖关系:
- 更新:
- `build/runtime/python/requirements.in`
- `build/runtime/python/runtime-requirements.lock.txt`
- 确认 `build/scripts/materialize-runtime-payload.ps1` 物化后的运行时 payload 中实际包含对应依赖
目标:
- 安装包中的 `resources/vendor/openclaw-runtime/python` 具备完整依赖
- 不把 `greenlet` 缺失问题留给最终用户机器
如果最终确认 Windows 下还依赖额外系统 DLL 或 VC runtime,则需要二选一:
1. 能随安装包自带则一并处理
2. 不能自带则在运行时探测和错误提示里明确报缺少系统依赖
### 4.3 提升 Python 运行时探测粒度
修改 `packages/runtime-manager/src/index.ts`
- 在运行时探测清单中加入 `greenlet`
- 对关键模块不再只用 `find_spec`
- 至少对以下内容做真实导入探测:
- `import greenlet`
- `from playwright.async_api import async_playwright`
判定规则调整为:
- 只有关键依赖真实可导入时,`pythonReady` 才能为 `true`
- 如导入失败,错误信息要直接反映缺失模块或 DLL 问题
这样可以避免:
- 顶层包存在,但底层二进制扩展无法加载
- 系统显示 `pythonReady = true`,专家页一执行就崩
### 4.4 补强 installer smoke
修改 `build/scripts/installer-smoke.ps1`,把当前浅层导入校验升级为真实关键依赖校验。
现有检查仅覆盖:
- `import playwright`
应至少追加:
- `import greenlet`
- `from playwright.async_api import async_playwright`
若其中任意一步失败,则直接将安装包判定为:
- `payload-validation-failure`
同时在 smoke 输出中增加以下字段,便于回溯:
- `greenletImportProbe`
- `playwrightAsyncImportProbe`
- 对应 stderr 或异常消息
### 4.5 增加专家页真实链路 smoke
在现有 smoke 基础上新增一条安装包级别的真实专家链路验证:
- 启动已安装应用
- 进入 experts 页
- 选择小红书专家
- 发送一条最小测试消息
- 至少验证流程能进入对应 Python 自动化入口,不在 `_greenlet` 处崩溃
该 smoke 不要求真的发布内容,但要覆盖真实调用路径。
目标:
- 防止“安装包通过了基本 smoke,但真实专家链路仍不可用”
### 4.6 优化用户可见错误
当专家页项目执行失败时,错误呈现需要区分:
1. 启动失败
2. 项目执行失败
3. Python 运行时依赖缺失
改进方向:
- 主进程保留完整 stderr 到日志
- UI 不直接暴露生硬 Python 原始异常
-`_greenlet` / DLL 类错误归类为:
- 打包运行时依赖缺失或损坏
- 页面提示用户导出 diagnostics,而不是只看 startup log
## 5. 测试与验收
### 5.1 旧 URL 迁移验证
1. 人工构造包含旧 `runtimeCloudApiBaseUrl``app-config.json`
2. 启动应用
3. 确认配置被自动改写为新地址
4. 确认启动日志记录了迁移行为
5. 确认 runtime-cloud 请求实际打到新地址
### 5.2 Python 运行时验证
1. 在物化后的打包 Python 环境中执行:
- `import greenlet`
- `from playwright.async_api import async_playwright`
2. 验证 `pythonReady` 只有在两者都通过时才为 `true`
3. 人为破坏依赖后,确认系统能明确报错而不是误判 ready
### 5.3 安装包验证
1. 新机器或全新用户目录安装
- 应默认使用新 runtime cloud 地址
2. 带旧配置目录启动
- 应自动迁移旧地址
3. 运行 installer smoke
- 必须通过 `greenlet``async_playwright` 检查
4. 运行专家页真实链路 smoke
- 不应再出现 `_greenlet` 导入失败
## 6. 风险与默认决策
默认决策:
- 旧地址采用自动迁移,不要求用户手工处理
- `greenlet` 作为打包运行时显式依赖维护
- installer smoke 必须覆盖关键二进制依赖的真实导入
- 专家页真实链路增加单独 smoke,不再只依赖浅层 import 检查
当前假设:
- 当前有效 runtime cloud 默认地址保持为 `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1`
-`xuphfkscoptnjoaecbvn.supabase.co/functions/v1` 已视为废弃地址
- 小红书专家链路继续基于打包 Python + Playwright 运行
- 旧 URL 问题主要影响升级机或迁移机;`_greenlet` 问题则属于安装包本身质量缺口,新电脑也可能遇到
## 7. 后续执行顺序建议
建议按以下顺序落地:
1. 先修 `app-config` 的旧 URL 自动迁移
2. 再补齐 Python 打包依赖与运行时探测
3. 然后升级 installer smoke
4. 最后补上 experts 页真实链路 smoke 与错误提示优化
这样可以先消除旧机器必现问题,再封堵安装包在新机器上的真实运行风险。
# 客户端抖音专家附件打通计划
Status: proposed
Date: 2026-04-10
## 1. 背景
当前客户端到 Douyin expert 的主链路,已经基本具备:
1. 专家页选择 `douyin` 项目
2. 会话绑定到 `project:douyin:*`
3. 默认命中项目 `workspace-entry`
4. 启动项目级执行链路
但这条链路目前仍然基本是“纯文本对话链路”。
对 Douyin 项目来说,这会直接卡住数字人能力,也会限制纯画面路线里的引用素材输入。
## 2. 当前代码事实
### 2.1 已打通部分
- 专家页可以命中项目级 `workspace-entry`
- Douyin 本地纯画面工作流已经有真实成功样例
- Douyin 主流程已支持:
- `--image`
- `--audio`
- `--seedance-reference-image`
- `--seedance-reference-video`
- `--seedance-reference-audio`
### 2.2 当前明确缺口
- 客户端专家页输入框目前只有文本输入,没有图片上传入口
- `DesktopApi.chat.sendPrompt()` / `streamPrompt()` 当前只有:
- `sessionId`
- `prompt`
- `skillId`
- IPC 层当前也只透传纯文本 prompt
- `project-workspace-executor` / `project-workspace-agent-runner` 当前只有文本 prompt 输入,没有附件协议
- Douyin 数字人链路当前明确要求必须有图片
### 2.3 对 Douyin 的直接影响
- 数字人路线:当前客户端无法把图片传给 expert,因此无法从客户端直接跑通数字人
- 纯画面路线:无图纯文案可以跑,但参考图 / 参考视频 / 参考音频能力当前无法从客户端输入
## 3. 目标
本次目标不是一次性做完整多模态聊天,而是先补齐最小闭环:
1. 客户端专家页支持选择并发送 1 张本地图片
2. 图片在 main 进程被复制到当前项目目录下的稳定路径
3. `workspace-entry` 能收到这张图的结构化输入
4. Douyin expert 能把这张图用于数字人主图输入
5. smoke 不再只验证 `workspace-entry launch`,至少验证“附件已进入项目执行层”
## 4. 方案范围
### 4.1 本次纳入
- 图片附件协议
- 专家页单图上传
- main 进程项目内落盘
- `workspace-entry` 附件透传
- Douyin expert 单图数字人最小闭环
- smoke 验证升级
### 4.2 本次不纳入
- 多图上传
- 音频上传
- 剪贴板图片
- URL 素材粘贴
- 通用多模态聊天体系重构
- 去修改 Douyin 项目“数字人必须传图”的业务约束
## 5. 实施方案
### 5.1 聊天协议与 IPC
扩展以下调用签名,新增可选 `attachments`
- `DesktopApi.chat.sendPrompt`
- `DesktopApi.chat.streamPrompt`
新增最小附件结构,v1 先只支持图片:
- `kind: "image"`
- `name`
- `mimeType`
- `localPath`
要求:
- 与当前纯文本调用兼容
- 无附件时行为保持不变
- renderer -> preload -> ipc -> main 全链路统一使用同一份附件结构
### 5.2 专家页 UI
在现有 composer 上补一个最小上传入口:
- 支持选择 1 张本地图片
- 已选择图片可见
- 重选时以最后一次选择覆盖
- 发送成功后清空附件
- 发送失败时保留附件,便于重试
本阶段不引入复杂交互:
- 不做多图排序
- 不做拖拽上传
- 不做素材管理器
### 5.3 Main 进程项目内落盘
main 进程收到图片后,不直接把临时路径传给项目,而是先复制到当前项目目录:
- 目标目录:`inputs/images/main/`
要求:
- 生成稳定文件名
- 绑定当前 `projectId` / `sessionId`
- 禁止跨项目复用错误路径
- 会话未绑定项目时直接返回明确错误
### 5.4 Workspace-entry 执行层
扩展:
- `ProjectExecutionRequest`
- `ProjectWorkspaceExecutionInput`
- `project-workspace-agent-runner` 的输入结构
执行策略:
- 保留原始 `prompt`
- 同时透传 `attachments`
- 如果当前 bundled agent 暂时不能直接吃结构化图片输入,则在 runner 层集中做一次降级,把图片路径转换成稳定提示注入
约束:
- 不在 renderer 层拼接图片说明
- 不在多个中间层重复组装 prompt
- 所有附件降级策略集中在执行层单点处理
### 5.5 Douyin expert 约定
v1 统一约定:
- 客户端上传的单张图片,默认视为“数字人主图 / main image”
这样可以先解当前最刚需的场景:
- 从客户端对话 Douyin expert
- 上传人物图
- 继续生成数字人口播视频
纯画面路线后续可以复用同一套附件协议,再细分为:
- `seedance_image`
- `reference_image`
- `reference_video`
- `reference_audio`
但这些不作为本次第一阶段的强验收项。
## 6. 验收标准
### 6.1 功能验收
满足以下结果即视为本阶段完成:
1. 专家页可选择并发送 1 张图片
2. 图片能落到当前项目 `inputs/images/main/`
3. Douyin expert 在项目执行层能读取到该图片路径
4. 数字人路线不再因为“客户端无法传图”而阻断
### 6.2 回归要求
以下行为不能被破坏:
1. 无附件的纯文本专家对话行为不变
2. 当前已打通的 `workspace-entry` 路由不回退为 skill fallback
3. 现有 Douyin 纯画面文本生成链路不回归
### 6.3 smoke 验收
至少新增或升级以下验证:
1. 专家页上传图片后,附件成功透传到项目执行层
2. `douyin expert cloud-bundle smoke` 不再只接受 `workspace-entry launch`
3. 保留现有纯文本 smoke,确保兼容旧行为
如果环境允许,再补一条本地真实验证:
1. 上传人物图
2. 用文案 + TTS 走数字人
3. 输出 `video_omnihuman.json` 或等效成功产物
## 7. 实施顺序
建议按以下顺序推进,避免前后返工:
1. 先补 shared types / preload / ipc 的附件协议
2. 再补专家页单图上传 UI
3. 再补 main 进程项目内落盘
4. 再补 workspace-entry executor / runner 附件透传
5. 最后补 Douyin smoke 和真实验证
## 8. 默认假设
- v1 只支持单张图片
- v1 只支持本地文件
- v1 不支持音频上传
- v1 不改 Douyin 数字人必须传图的既有约束
- 结构化多模态能力如果当前 runtime 未直接支持,允许 runner 层先做集中降级适配
# 新电脑初始化真实自动化一周计划
Status: proposed
Date Range: 2026-04-10 to 2026-04-16
## 1. 目标
本周目标不是“开发机可以跑”,而是把下面这条链路做成可验收的稳定闭环:
1. 新电脑安装客户端
2. 用户输入 employee key
3. 客户端调用 `https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1/openclaw-employee-config`
4. 从返回的 `skills[].skill.download_url` 识别抖音 / 小红书 zip 项目包
5. 客户端自动下载并解压到本地工作区
6. 专家页出现对应项目
7. 用户发送一句自然语言
8. 客户端命中项目 `workspace-entry`
9. 本地真实启动 Python / Playwright 自动化链路
## 2. 本周验收标准
本周结束时,至少满足以下结果:
1. 空白用户目录下首次安装,保存 employee key 后可自动完成 `init` 拉取和项目同步
2. `workspaceSummary` 中能看到非 home 项目,且 `currentProjectId``projectReady` 为真
3. 专家页发一句话时,默认走 `workspace-entry`,而不是 skill fallback
4. bundled runtime 不再因 `_greenlet``async_playwright` 导入失败而阻断真实自动化
5. 项目运行态在 bundle 更新后不会因为目录替换而丢失
6. 小红书项目在 fresh install 路径下完成至少一次真实自动化启动验证
## 3. 范围
本周纳入:
- `openclaw-employee-config` 返回契约与客户端解析契约对齐
- project bundle 标准化
- bundled runtime 依赖与探测硬化
- installer / experts 真实链路 smoke
- 小红书项目运行态迁移到 `memory/`
- 项目级 skill 展示与校验收口
本周不纳入:
- “扫描 `skills/*/SKILL.md` 自动反推项目”的重构
- 项目 skills 的彻底物理隔离重构
- 超出当前 bundled runtime 能力的大规模新依赖体系设计
## 4. 关键事实基线
- 当前客户端已经有 `employee-key -> runtime cloud fetch -> zip bundle sync -> project inventory -> experts page -> workspace-entry` 主链路
- 当前最大 fresh install 风险在 bundled runtime 的 Python 依赖校验过浅,尤其是 `greenlet` / `async_playwright`
- 当前 project bundle 缺失 `project.json` 时仍会被客户端降级猜测,不适合正式交付
- 当前项目运行态只保留 `sessions.json``session-messages/``memory/`,所以所有可变状态必须迁入 `memory/`
- 当前 `listProjectSkills()` 仍会混入 curated generic skills,不满足“进入项目后只看该项目能力”
## 5. 每日计划
### Day 1: 2026-04-10
目标:冻结契约,避免后续实现反复返工。
任务:
- 明确 `openclaw-employee-config` 返回字段的最小必需项
- 明确 zip 项目包的标准交付契约
- 明确 `project.json` 最低要求必须包含:
- `id`
- `name`
- `platform`
- `projectType`
- `workspaceAutomation`
- `defaultEntry`
- `entries`
- 明确本周验收以小红书链路为主,抖音按同一标准补齐
交付物:
- 本文档
- 项目包契约确认结论
### Day 2: 2026-04-11
目标:先堵住 fresh install 最容易失败的 bundled runtime 问题。
任务:
-`build/runtime/python/requirements.in` 和锁文件中显式纳入 `greenlet`
- 调整 `packages/runtime-manager/src/index.ts`
- 把 Python payload 探测从 `find_spec` 提升为真实关键导入探测
- 至少覆盖:
- `import greenlet`
- `from playwright.async_api import async_playwright`
- 调整 installer smoke,同步校验上述关键导入
交付物:
- runtime 依赖更新
- runtime 探测更新
- installer smoke 更新
验收点:
- 打包 Python 环境可真实导入 `greenlet`
- `pythonReady` 只在关键依赖通过时才为真
### Day 3: 2026-04-12
目标:把 bundle 接入从“能猜”改成“强契约”。
任务:
- 修改 `project-bundle.ts`
- 安装后要求项目根必须存在标准 `project.json`
- 缺失时 bundle sync 明确报错,不写入 ready project
- 保留现有 rollback / replace / preserve local state 逻辑
- 校验项目根结构兼容:
- zip 根就是项目根
- zip 根下 `projects/<projectId>`
交付物:
- bundle 解析与报错硬化
- 缺失 `project.json` 的明确诊断
验收点:
- 非标准 zip 无法被静默安装成 ready project
### Day 4: 2026-04-13
目标:收口项目隔离表现,避免专家页误混全局能力。
任务:
- 修改 `project-store.ts`
- `boundSkillIds` 与实际 `workspace/skills/<skillId>` 做交叉校验
- 对缺失 skill 做诊断日志和降级处理
- 专家项目视图下不再自动混入 curated generic skills
- 保持 session 继续按 `project:<projectId>:<uuid>` 隔离
交付物:
- 项目 skills 列表收口
- `boundSkillIds` 诊断
验收点:
- 进入 `xhs` 后只显示 `xhs` 绑定技能
- 进入 `douyin` 后只显示 `douyin` 绑定技能
### Day 5: 2026-04-14
目标:把小红书项目调整到真正适合 bundle 替换的状态。
任务:
-`workspace/xhs` 为模板,梳理真实自动化入口
- 确保 `project.json` 包含 `workspaceAutomation`
- 确保 `workspace_entry.py` 能处理自然语言 prompt
- 把运行态迁移到 `memory/`
- 重点清理这些项目根运行态:
- `xhs_profile`
- `generated_images`
- `openclaw_runs`
- `openclaw_debug`
- 其他浏览器态 / 草稿态 / 调试态文件
交付物:
- 可交付的小红书标准 bundle 源
验收点:
- bundle 替换后,小红书登录态和会话态仍保留
### Day 6: 2026-04-15
目标:让抖音项目按同一套标准补齐,而不是做特殊兼容。
任务:
- 给抖音项目补标准 `project.json`
-`workspaceAutomation``workspace_entry.py`
- 校对 `boundSkillIds`
- 确认抖音项目依赖不超出本周 bundled runtime 能力
- 如超出,单独列出 runtime 扩容项,不在 bundle 内偷偷引入
交付物:
- 可交付的抖音标准 bundle 源
验收点:
- 抖音和小红书都能以“一包一项目”方式被客户端识别
### Day 7: 2026-04-16
目标:做真正的 fresh install 验收,而不是开发机自测。
任务:
- 用空白用户目录执行 installer smoke
- 用空白用户目录执行 experts 页真实链路 smoke
- 重点验证:
- employee key 初始化
- zip 下载与项目同步
- 专家页项目出现
- `workspace-entry` 命中
- 小红书真实自动化成功启动
- bundle 替换后 `memory/` 保留
- 输出遗留问题清单和是否可以进入下周收尾
交付物:
- fresh install 验收结果
- 遗留问题清单
## 6. 文件级实施清单
本周高优先级涉及的代码文件:
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/project-bundle.ts`
- `apps/desktop/src/main/services/project-store.ts`
- `apps/desktop/src/main/services/project-workspace-executor.ts`
- `packages/runtime-manager/src/index.ts`
- `build/runtime/python/requirements.in`
- `build/runtime/python/runtime-requirements.lock.txt`
- `build/scripts/installer-smoke.ps1`
- `build/scripts/xhs-expert-cloud-bundle-smoke.ps1`
- `build/scripts/xhs-expert-live-run.ps1`
项目包侧高优先级文件:
- `workspace/xhs/project.json`
- `workspace/xhs/workspace_entry.py`
- `workspace/douyin/project.json`
- `workspace/douyin/workspace_entry.py`
如果真实项目源不在仓库内,则需要同步维护一份与仓库规范一致的交付模板。
## 7. 风险
1. 若抖音或小红书项目依赖超出当前 bundled runtime,单靠 zip 交付无法解决
2. 若真实自动化代码继续把运行态写在项目根,bundle 替换时仍会丢状态
3. 当前项目 skills 仍是共享物化到 `workspace/skills`,若未来两个项目出现同名 skill,仍存在覆盖风险
4. 如果服务端返回契约字段不稳定,fresh install 的初始化链路会出现非代码层问题
## 8. 周末结论口径
只有同时满足以下条件,本周工作才算达标:
1. 新电脑或空白用户目录下首次安装可完成初始化
2. employee key 触发的 `openclaw-employee-config` 可稳定下发 zip 项目包
3. 客户端可自动把 zip 物化为 ready project
4. 专家页可以直接选择项目并命中 `workspace-entry`
5. 小红书真实自动化在 fresh install 环境下成功启动
6. installer smoke 和 experts 真实链路 smoke 均通过
如果只做到“项目能出现”或“开发机能跑”,但 fresh install 不稳定,则本周目标不算完成。
# 桌面端专家工作台侧栏、设置页与窗口拖拽改造方案
## 1. 背景
当前桌面端专家工作台有三类明显体验问题:
1. 左侧栏仍存在空壳专家入口与占位标签,用户会误以为这些专家可用,降低完成度与可信度。
2. 设置页缺少面向内容生产场景的模型配置入口,无法分别配置生图、视频、文案三类模型的 `base_url``api_key`
3. 桌面端窗口存在“拖不动”的问题,影响基础可用性。当前主窗口为无边框窗口,拖拽能力依赖前端显式标记的 drag 区域;现状中仅局部区域设置了 `-webkit-app-region: drag`,而窗口控制按钮区域又被标记为 `no-drag`,导致可拖拽热区不足且不稳定。
本轮目标聚焦为**桌面端 UI 与交互改造**,先把入口、图标、设置页结构、配置保存体验以及窗口拖拽体验整理到位;**本轮不接入抖音专家与小红书专家对这些模型配置的实际消费逻辑**
## 2. 改造目标
改造完成后,应满足以下结果:
- 左侧栏只展示真实可用的专家入口,不再出现空壳项。
- 抖音专家不再使用红书书本语义图标,统一替换为音符风格图标。
- 设置页只保留当前需要暴露的能力:
- 更换龙虾密钥
- 三类专家模型配置
- 工作目录
- 导出诊断
- 生图模型、视频模型、文案模型均支持独立填写:
- `base_url`
- `api_key`
- 上述配置可保存、可回显、可区分“已配置 / 未配置”状态。
- 主窗口在顶部应存在稳定、连续、易命中的可拖拽区域;按钮、输入框、导航等交互控件不误触发拖拽。
- 保持现有底层兼容链路,不影响当前 runtime / cloud / 员工密钥能力。
## 3. 本轮范围
### 3.1 包含内容
#### A. 左侧栏专家区整理
- 删除当前硬编码的空壳专家占位入口。
- 左侧栏只保留真实项目型专家入口。
- 如果 `browser-ops` 仍在真实项目列表中,则继续按真实专家展示。
#### B. 抖音专家图标替换
- 保留小红书专家当前书本风格图标。
- 为抖音专家新增独立的音符 SVG 图标。
- 在左侧栏、专家卡片、专家详情 hero 等平台识别位置统一切换图标映射。
#### C. 设置页结构重做
设置页调整为三块:
1. **基础连接**
- 保留“更换龙虾密钥”入口
- 复用现有员工密钥链路,只改 UI 文案
2. **专家模型配置**
- 生图模型配置
- 视频模型配置
- 文案模型配置
- 每组字段统一为:`base_url``api_key`
- 每组显示独立配置状态与用途说明
3. **诊断与工作区**
- 工作目录
- 导出诊断
#### D. 配置保存与回显
- 前端可编辑三组模型配置。
- 保存后重新进入设置页可以回显:
- `base_url`
- `apiKeyConfigured` 状态
- 不重新输入 `api_key` 时,不应误清空已有 key。
#### E. 窗口拖拽热区修复
- 保证 frameless 主窗口存在稳定的顶部拖拽区域。
- 拖拽热区不能依赖过小或被内容挤压的局部容器。
- 窗口控制按钮、输入框、tab、导航按钮等交互元素明确标记为 `no-drag`
- 拖拽区域应覆盖主要页面共享头部,而不是只在个别页面生效。
### 3.2 明确不做
本轮**不做**以下内容:
- 不修改抖音专家、小红书专家的执行逻辑。
- 不让专家任务在本轮实际读取 `expertModelConfig`
- 不改 `project-skill-router.ts``project-intent-router.ts` 等执行链路来接入这些模型配置。
- 不扩展为新的专家调度策略或模型路由策略。
## 4. UI / UX 设计要求
`ui-ux-pro-max` 约束落地,确保整体偏“科技感、高级感、可操作”。
### 4.1 视觉要求
- 所有新增图标使用 SVG,不使用 emoji。
- 保持深色主题一致性,采用卡片式分区。
- 使用细描边、弱发光、轻层次阴影,避免花哨渐变泛滥。
- 保证文字、边框、状态标签在暗色背景下有足够对比度。
### 4.2 交互要求
- 所有输入框必须保留明确 label,不能仅依赖 placeholder。
- 所有可点击元素提供清晰 hover / focus 状态。
- 触控目标不小于 44px。
- 保存按钮需要明确 loading / disabled 状态,避免重复提交。
- hover 不做位移或缩放导致布局抖动,只做颜色、描边、阴影、透明度变化。
- 可拖拽区域与可交互区域边界明确,避免“想点击却拖动窗口”或“想拖动却无响应”。
### 4.3 信息结构要求
- 旧通用配置项在界面中隐藏:
- `Setup Mode`
- `Provider`
- 通用 `Base URL`
- `Default Model`
- 当前“员工密钥”相关对外文案统一改为“龙虾密钥”。
- 工作目录保留,但视觉优先级低于模型配置区。
## 5. 关键改造点
### 5.1 左侧栏专家入口
**目标文件:** `apps/ui/src/App.tsx`
复用现有真实项目数据链路,去掉现在写死的 placeholder 专家标签,例如:
- `知乎专家`
- `视频号专家`
- 重复的 `抖音专家` 占位项
保留并复用现有:
- `workspace.projects`
- `visibleProjects`
- `expertCards`
- `resolveExpertKey()`
### 5.2 抖音专家图标
**目标文件:** `apps/ui/src/App.tsx`
处理方式:
- `xiaohongshu` 继续使用 `RedBookIcon`
- `douyin` 新增 `DouyinNoteIcon`
- `browser` / `general` 保持现有中性图标
需要统一覆盖以下使用场景:
- 左侧栏专家快捷入口
- 专家卡片
- 专家页 hero / 标题区
- 其他按平台渲染图标的位置
### 5.3 设置页结构与表单
**目标文件:** `apps/ui/src/App.tsx`
建议结构:
- 顶部摘要说明当前页用途
- 中间三张模型配置卡片
- 下方诊断与工作区卡片
三组模型配置建议统一结构:
- 标题
- 简短用途描述
- 已配置 / 未配置状态
- `base_url` 输入框
- `api_key` 密码输入框
### 5.4 配置类型契约
**目标文件:** `packages/shared-types/src/index.ts`
在不破坏现有类型兼容的前提下新增:
- `ModelEndpointConfig`
- `ModelEndpointInput`
- `ExpertModelConfig`
建议结构:
```ts
interface ModelEndpointConfig {
baseUrl: string;
apiKeyConfigured: boolean;
}
interface ModelEndpointInput {
baseUrl: string;
apiKey?: string;
}
interface ExpertModelConfig {
image: ModelEndpointConfig;
video: ModelEndpointConfig;
copywriting: ModelEndpointConfig;
}
```
并扩展:
- `AppConfig.expertModelConfig`
- `SaveConfigInput.expertModelConfig?`
保留旧字段继续存在,避免影响当前其它链路。
### 5.5 配置持久化与密钥存储
**目标文件:**
- `apps/desktop/src/main/services/app-config.ts`
- `apps/desktop/src/main/services/secrets.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/preload/index.ts`
处理原则:
#### 普通配置
进入 `app-config.json`
- 三组模型配置中的 `baseUrl`
#### 密钥配置
进入 `SecretManager`
- `imageModelApiKey`
- `videoModelApiKey`
- `copywritingModelApiKey`
#### 返回给前端的内容
仅返回:
- `baseUrl`
- `apiKeyConfigured`
不向 renderer 返回明文 secret。
### 5.6 窗口拖拽热区修复
**目标文件:**
- `apps/ui/src/styles.css`
- `apps/ui/src/App.tsx`
- `apps/desktop/src/main/create-window.ts`
现状代码显示:
- 主窗口为 `frame: false`,并使用 `titleBarStyle: "hidden"`,属于需要前端自行提供拖拽热区的无边框窗口。
- 当前 `apps/ui/src/styles.css` 中窗口控制区为 `-webkit-app-region: no-drag`
- 当前仅 `apps/ui/src/styles.css``.page-topbar` 被标记为 `-webkit-app-region: drag`,拖拽区域过于局部。
改造要求:
- 在共享顶栏或稳定头部容器上提供连续 drag 区域。
- 交互控件所在容器统一补 `-webkit-app-region: no-drag`
- 避免 drag 区域被按钮组、表单、滚动容器完全挤占。
- 如现有布局难以保证稳定热区,可增加独立的顶层拖拽条,但必须保持视觉克制,不破坏科技感界面。
## 6. 关键文件
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
- `apps/desktop/src/main/create-window.ts`
- `packages/shared-types/src/index.ts`
- `apps/desktop/src/main/services/app-config.ts`
- `apps/desktop/src/main/services/secrets.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/preload/index.ts`
## 7. 复用点
优先复用已有逻辑,避免重复造轮子:
- 专家识别:`apps/ui/src/App.tsx``resolveExpertKey()`
- 专家文案:`apps/ui/src/App.tsx``getExpertGuide()`
- 配置读写:`apps/desktop/src/main/services/app-config.ts``AppConfigService.load()` / `save()`
- 密钥存储:`apps/desktop/src/main/services/secrets.ts``SecretManager`
- 配置桥接:`apps/desktop/src/preload/index.ts``apps/desktop/src/main/ipc.ts` 中现有 `config.load/save`
- 诊断导出:现有 `diagnostics.exportSnapshot()` 链路
- 窗口创建:`apps/desktop/src/main/create-window.ts` 中现有 frameless window 配置
## 8. 验证项
### 8.1 左侧栏验证
- 左侧栏不再出现空壳专家 chip。
- 只展示真实专家入口。
- 抖音专家显示音符图标。
- 小红书专家继续显示书本图标。
### 8.2 设置页验证
- 页面只保留目标项:龙虾密钥、三类模型配置、工作目录、导出诊断。
- 三类模型都能填写 `base_url``api_key`
- 不重新输入 `api_key` 时不会清空已保存密钥。
- 保存后重新进入页面,状态能正确回显。
### 8.3 配置持久化验证
- 重启应用后,三组 `baseUrl` 仍存在。
- 三组 `apiKeyConfigured` 状态正确。
- 原有龙虾密钥链路不受影响。
- 原运行时与云端配置链路不受影响。
### 8.4 窗口拖拽验证
- 主窗口顶部存在稳定可拖拽区域。
- 在聊天页、专家页、设置页都能正常拖动窗口。
- 点击最小化、最大化、关闭按钮不会误触发拖拽。
- 点击输入框、按钮、导航项、专家卡片时不会因为 drag 区域覆盖而失去交互。
- Windows 下拖拽体验连续,不出现“只有局部像素能拖动”的情况。
### 8.5 交互与视觉验证
- 深色主题下文本、边框、状态标签清晰可读。
- 输入框 focus 态清楚。
- 按钮 hover / disabled / loading 态明确。
- 无布局抖动、无误导性占位入口。
## 9. 建议落地顺序
1. 先改 `shared-types`,补齐三类模型配置契约。
2. 再改 `app-config` / `secrets` / `ipc` / `preload`,打通保存与回显。
3. 处理窗口拖拽热区:
- 梳理共享头部结构
- 明确 drag / no-drag 分区
- 回归聊天页、专家页、设置页
4. 最后集中改 `apps/ui/src/App.tsx`
- 清理左侧栏空壳专家
- 替换抖音图标
- 重做设置页布局与交互
5. 最后做桌面端手动回归与 smoke 验证。
# 2026-04-27 小红书客户端接入适配方案
## 结论
小红书项目当前真实源码目录是 `D:\qjclaw\workspace\xiaohongshu-client-package`,不是 `D:\qiclaw\...`
该目录目前更接近原始客户端交付包,不是 qjclaw 可直接执行的 workspace 项目。后续接入应新建或迁移到 `D:\qjclaw\workspace\xhs`,并固定项目 ID 为 `xhs`
## 当前项目事实
- 源项目存在于 `workspace\xiaohongshu-client-package`
- 当前缺少 qjclaw 必需的 `project.json`
- 当前缺少 qjclaw 直接执行入口 `workspace_entry.py`
- 当前没有输出 qjclaw 可识别的 `QJC_WORKSPACE_EVENT\t...` 事件协议。
- 当前没有声明 `boundSkillIds`,客户端无法按项目过滤并展示小红书相关技能。
- 当前没有 qjclaw bundle 排除规则,`.env`、运行数据、缓存、生成物和本地调试产物需要显式排除。
已有入口和能力:
- `agent_server.py`:FastAPI 后端,默认监听 `8888`,暴露 `/status``/api/crawler/start``/api/checkpoints``/api/generate``/api/result/latest` 等接口。
- `main.py`:CLI/采集主流程,支持关键词、业务需求、目标人群、主题、数量等参数。
- `regen_v2.py``generate_content.py``print_latest_result.py`:基于已有采集结果生成内容和读取最新结果。
- `requirements.txt`:声明 Python runtime 依赖。
- `CLIENT_INTEGRATION.md`:说明当前项目可用 FastAPI 或 CLI 集成,但这不是 qjclaw workspace 协议。
## 缺口
小红书项目要进入 qjclaw 客户端执行链,至少需要补齐:
- `project.json`
- `workspace_entry.py`
- `workspaceAutomation`
- `defaultEntry.type = "workspace-entry"`
- `entries` 中的 `workspace-entry`
- `workspaceEntryEnabled: true`
- `ready: true`
- `boundSkillIds`
- `bundlePackaging.excludePaths`
- `QJC_WORKSPACE_EVENT` 事件输出
- 客户端模型/密钥配置到项目环境变量的映射
## 适配目录策略
使用新目录:
```text
D:\qjclaw\workspace\xhs
```
原因:
- 项目 ID 固定为 `xhs`,与已有脚本 `smoke:xhs-local-project-package``smoke:xhs-expert-cloud-bundle` 保持一致。
- 保留 `xiaohongshu-client-package` 作为原始交付包,避免在源包上混入 qjclaw 适配层。
- 后续 bundle、客户端项目记录、技能绑定都以 `xhs` 为唯一项目标识。
## project.json 方案
`workspace\xhs\project.json` 使用 qjclaw 已验证字段,不新增未被客户端消费的字段。
建议结构:
```json
{
"id": "xhs",
"name": "xiaohongshu-client-package",
"platform": "xiaohongshu",
"projectType": "automation-project",
"description": "Xiaohongshu content collection, analysis, copywriting and publishing project.",
"version": "2026-04",
"workspaceAutomation": {
"runtime": "python",
"script": "workspace_entry.py",
"args": [
"--prompt",
"{prompt}",
"--prepared-prompt",
"{preparedPrompt}",
"--project-root",
"{projectRoot}",
"--session-id",
"{sessionId}"
]
},
"bundlePackaging": {
"excludePaths": [
".env",
"datas/runtime",
"datas/checkpoints",
"datas/excel_datas",
"datas/media_datas",
"datas/generated",
"datas/uploads",
"datas/debug_pages",
"node_modules",
"__pycache__",
"**/__pycache__",
"**/*.pyc"
]
},
"defaultEntry": {
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["xiaohongshu", "xhs", "小红书"],
"capabilities": ["collection", "analysis", "copywriting", "image-generation", "publish"]
},
"entries": [
{
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["xiaohongshu", "xhs", "小红书"],
"capabilities": ["collection", "analysis", "copywriting", "image-generation", "publish"]
}
],
"boundSkillIds": [
"xhs-create-note",
"xhs-pipeline",
"xhs-publish-note",
"xhs-research"
],
"workspaceEntryEnabled": true,
"ready": true
}
```
## workspace_entry.py 方案
`workspace_entry.py` 只作为 qjclaw 适配层,不重写小红书业务主流程。
职责:
- 解析 `--prompt``--prepared-prompt``--project-root``--session-id`
- 读取客户端注入的附件环境变量,例如 `QJC_PROJECT_ATTACHMENTS_JSON``QJC_PROJECT_MAIN_IMAGE`
- 将用户输入路由到采集、分析、生成、发布等已有脚本或函数。
- 将内部执行状态转换为 `QJC_WORKSPACE_EVENT\t{...}`
- 输出 `started``status``completed``error`,必要时输出 `delta`
最小事件格式:
```text
QJC_WORKSPACE_EVENT {"type":"started","runId":"<session-id>"}
QJC_WORKSPACE_EVENT {"type":"status","runId":"<session-id>","stage":"xhs","label":"Running XHS workflow"}
QJC_WORKSPACE_EVENT {"type":"completed","runId":"<session-id>","content":"..."}
QJC_WORKSPACE_EVENT {"type":"error","runId":"<session-id>","message":"..."}
```
## 模型和环境变量映射
客户端配置应在运行时写入项目环境,不提交 `.env`
小红书项目当前依赖的关键环境变量:
- 文本/视觉模型:`QWEN_API_KEY``QWEN_MODEL``QWEN_VISION_MODEL`
- 图片生成:`ARK_API_KEY``VOLCENGINE_API_KEY``IMAGE_API_KEY``IMAGE_MODEL``ARK_IMAGE_MODEL`
- 小红书登录:`COOKIES`
- 飞书写入:`FEISHU_APP_ID``FEISHU_APP_SECRET``FEISHU_APP_TOKEN``FEISHU_TABLE_ID``FEISHU_TABLE_MAP`
- 采集控制:`XHS_FETCH_COMMENTS``XHS_STATUS_FILE``XHS_RUN_LOG_FILE``XHS_EVENTS_FILE`
接入时由 qjclaw 的项目执行链注入 `project.env`,不要让 renderer 直接读写密钥、cookie、runtime 文件或子进程。
## FastAPI 端口策略
`agent_server.py` 默认端口是 `8888`。接入 qjclaw 时不应把常驻 FastAPI 服务作为唯一入口。
推荐策略:
- 首选 `workspace_entry.py` 直接调用已有 Python 函数或 CLI。
- 若必须复用 FastAPI,则由 `workspace_entry.py` 按需启动本地服务并做端口探测。
- 避免固定占用 `8888` 导致多个项目或多次会话冲突。
- FastAPI 只作为内部适配手段,qjclaw 客户端可见状态仍通过 `QJC_WORKSPACE_EVENT` 返回。
## Python runtime 依赖
按当前 `requirements.txt`,需要纳入 runtime 安装/校验:
- `PyExecJS`
- `requests`
- `loguru`
- `python-dotenv`
- `retry`
- `openpyxl`
- `playwright`
- `openai`
- `fastapi`
- `uvicorn`
- `python-multipart`
- `pdfplumber`
- `python-docx`
- `certifi`
另需确认:
- Playwright Chromium 已安装。
- Windows runtime 可执行 Python 依赖安装。
- Node/npm 仅在确实需要 `plugin` 或 JS 侧辅助能力时保留,否则不要作为 qjclaw 主执行入口。
## Bundle 排除规则
必须排除:
- `.env`
- 小红书 Cookie
- 飞书 Secret
- 模型 API Key
- `datas/runtime`
- `datas/checkpoints`
- `datas/excel_datas`
- `datas/media_datas`
- `datas/generated`
- `datas/uploads`
- `datas/debug_pages`
- `node_modules`
- `__pycache__`
- `**/*.pyc`
应保留:
- Python 源码
- `requirements.txt`
- `project.json`
- `workspace_entry.py`
- 必需的 `apis``xhs_utils``plugin``asset-dropbox-understanding` 等项目能力目录
- 必需的静态 JS 辅助文件
## 抖音隔离
本方案不修改 `workspace\douyin`
边界:
- 不改抖音项目代码。
- 不改抖音分支。
- 不复用抖音项目 ID、技能 ID 或业务状态文件。
- 只复用 qjclaw 通用执行链、bundle 机制、客户端配置注入机制和 smoke 验证方式。
## 实施顺序
1.`workspace\xiaohongshu-client-package` 迁移或复制出 `workspace\xhs`
2.`workspace\xhs` 新增 `project.json`
3.`workspace\xhs` 新增 `workspace_entry.py`
4. 将客户端模型、飞书、小红书 Cookie 配置映射到运行时环境变量。
5. 配置 `bundlePackaging.excludePaths`,确保密钥和历史运行数据不进入 bundle。
6. 先打通 CLI 直连执行,再决定是否保留 FastAPI 内部调用。
7. 用 smoke 验证 xhs 项目包、云端 bundle、客户端聊天入口。
8. 回归抖音 smoke,确认通用执行链没有被小红书适配破坏。
## 验证清单
适配完成后至少运行:
```powershell
corepack pnpm typecheck
corepack pnpm build
corepack pnpm smoke:xhs-local-project-package
corepack pnpm smoke:xhs-expert-cloud-bundle
corepack pnpm smoke:douyin-local-project-package
corepack pnpm smoke:douyin-expert-cloud-bundle
```
通过标准:
- `xhs` 项目能被项目包 materialize。
- `xhs` 项目记录包含 `workspace-entry``boundSkillIds`
- 运行时能收到 `QJC_WORKSPACE_EVENT`
- 客户端聊天能得到小红书工作流的 `completed` 或明确 `error`
- bundle 不包含 `.env`、Cookie、API Key、飞书 Secret 或历史运行数据。
- 抖音项目 smoke 仍通过。
# 2026-04-27 小红书自然语言入口优化方案
## 背景结论
当前小红书专家让用户输入 JSON 指令是不合适的。JSON 只应作为内部自动化或调试兼容入口,客户端用户侧应直接输入自然语言需求。
现状问题集中在 `workspace\xhs\workspace_entry.py`
- 完整执行路径目前主要依赖 `xhs_action` JSON 指令。
- 普通自然语言无法补齐参数时,会兜底返回“请使用 JSON 指令”。
- 入口曾使用 `prepared-prompt` 做自然语言关键词判断;该字段包含系统上下文,容易误触发“发布”等动作。
优化目标:
- 用户自然语言输入需求。
- 入口脚本负责解析、追问、确认、执行。
- 长流程必须确认后才启动。
- 继续只读取客户端注入的模型、Cookie、飞书环境变量。
## 需要修改的文件范围
核心修改:
- `workspace\xhs\workspace_entry.py`
- 自然语言意图解析。
- QWEN 参数抽取。
- 多轮补参状态。
- 参数确认后执行。
- 风险控制和环境校验。
可能需要小改:
- `workspace\xhs\project.json`
- 一般不需要改;如需可补充入口能力描述。
需要更新 smoke:
- `build\scripts\local-project-package-smoke.ps1`
- 补自然语言入口、追问、确认流程断言。
- `build\scripts\xhs-expert-cloud-bundle-smoke.ps1`
- 云端 bundle 入口断言改为自然语言完成/追问,不再依赖 JSON 示例。
不应修改:
- `apps\ui` 渲染层。
- 共享 IPC 类型。
- `workspace\douyin` 业务逻辑。
- 小红书原始包 `workspace\xiaohongshu-client-package`
## 交互与执行设计
入口参数使用策略:
- `--prompt`:唯一用户原文来源。
- `--prepared-prompt`:只作为模型解析上下文,不参与关键词或动作判断。
- `--session-id`:用于隔离多轮补参状态。
自然语言解析策略:
- 优先调用客户端注入的 QWEN:
- `QWEN_BASE_URL`
- `QWEN_API_KEY`
- `QWEN_MODEL`
- QWEN 输出必须校验 schema。
- 只允许白名单动作:
- `collect`
- `generate_from_link`
- `generate_from_collected`
- `preview_latest`
- `check_config`
- `locate_latest`
- `publish_latest`
- QWEN 不可用或解析失败时,降级为规则解析和自然语言追问。
多轮补参:
- pending state 存到 `memory\xhs_workspace_sessions\`。
- 文件按 `session-id` 隔离。
- 状态 TTL:24 小时。
- 完成、取消、切换动作后清理。
采集动作必填字段:
- `keywords`
- `business_need`
- `target_audience`
- `theme`
生成动作必填字段:
- `generate_from_link`:`ref_url`、`topic`
- `generate_from_collected`:`ref_index`、`brand`、`location`
确认机制:
- `collect`、`generate_from_link`、`generate_from_collected` 必须先输出确认摘要。
- 用户明确回复“确认 / 开始 / 执行”后才调用 CLI。
- 用户回复“取消 / 重来”时清理 pending state。
- `check_config`、`preview_latest`、`locate_latest` 可直接执行。
- `publish_latest` 只在用户明确输入“发布 / 现在发布 / 立即发布”时触发。
数量保护:
- `total_count` 默认 5。
- `total_count` 最大 20。
- `top_n` 不超过 `total_count`。
## 风险点与处理
误启动采集:
- 不能忽视。
- 所有长流程强制确认后执行。
- 未确认时只返回参数摘要。
`prepared-prompt` 误判:
- 必须修。
- 自然语言判断只读取 `--prompt`。
- `prepared-prompt` 不再用于动作关键词匹配。
模型解析失败:
- 不能忽视。
- QWEN 失败时返回自然语言追问。
- 不再要求用户输入 JSON。
- 模型输出必须做 action 白名单和字段校验。
会话状态污染:
- 不能忽视。
- 按 session 隔离。
- 完成、取消、切换动作后清理。
- 加 24 小时 TTL。
旧 smoke 兼容:
- 可以低风险处理。
- 不保留“要求 JSON”的用户体验。
- 更新 smoke 到自然语言追问/确认断言。
## 测试计划
脚本级验证:
```powershell
python -m py_compile workspace\xhs\workspace_entry.py
```
自然语言用例:
- 输入“帮我采集防晒霜赛道,面向通勤女性,先抓 5 条”。
- 预期:缺字段时追问,字段完整时返回确认摘要。
- 输入“确认”。
- 预期:开始执行;若缺 `COOKIES`、`QWEN_API_KEY`、飞书配置,则明确 error。
- 输入“取消”。
- 预期:清理 pending state。
- 输入“小红书链接 + 生成内容需求”。
- 预期:进入链接生成补参/确认流程。
- 输入“最新结果 / 预览结果”。
- 预期:直接预览。
- 输入“现在发布”。
- 预期:只做发布条件校验,不误触发其他自然语言。
回归验证:
```powershell
corepack pnpm typecheck
corepack pnpm build
corepack pnpm smoke:xhs-local-project-package
corepack pnpm smoke:xhs-expert-cloud-bundle
corepack pnpm smoke:douyin-local-project-package
corepack pnpm smoke:douyin-expert-cloud-bundle
```
通过标准:
- 小红书专家自然语言入口不再提示用户输入 JSON。
- 长流程不会在未确认时启动。
- 缺环境变量时给出明确错误。
- qjclaw 自动执行不触发扫码登录。
- `prepared-prompt` 中的系统上下文不影响动作判断。
- xhs 本地包、云端 bundle、抖音回归 smoke 全部通过。
# OpenClaw 客户端灰白蓝科技感 UI 改造方案
## 1. 目标
在不改业务逻辑的前提下,重构桌面客户端的视觉系统和交互层级。
最终方向是 **灰白主工作台 + 蓝色科技感 + 克制生产力工具风**
- 保留现有左侧导航栏、右侧工作区结构。
- 保留当前偏灰白的整体基调。
- 蓝色只作为科技感强调色,用在激活态、状态、聚焦、关键操作。
- 降低当前浅蓝玻璃拟态和紫色渐变的存在感。
- 让客户端更像专业 AI 桌面工作台,而不是普通网页后台或聊天 demo。
## 2. 设计参考
本方案结合三类参考,不直接照搬某一种:
- Cursor:右侧 AI 工作区、底部输入、对话执行状态的专注感。
- Linear:克制信息密度、清晰层级、低噪音边框和阴影。
- Raycast:快速操作感、蓝色光感、明确状态反馈。
当前客户端已有的灰白浅色基调继续保留,但需要收敛视觉语言。
## 3. 现有问题
- `apps/ui/src/styles.css` 中核心布局类存在多轮定义和覆盖,维护风险高。
- 当前视觉同时存在紫色、浅蓝、灰白、玻璃、渐变,主线不够统一。
- 左侧栏承载主导航、数字员工、专家树、会话列表,信息密度偏高。
- 右侧工作区的聊天、专家、插件、设置页不像同一套产品系统。
- hover 位移、强阴影、大圆角容易让界面偏轻浮,不够工具化。
- focus、错误提示、键盘导航、滚动区边界需要进一步统一。
- 部分中文注释或文案已有乱码迹象,后续修改必须避免编码扩大污染。
## 4. 修改范围
### 4.1 必改文件
- `apps/ui/src/styles.css`
- 统一设计 token:颜色、边框、阴影、圆角、间距。
- 收敛重复定义的核心布局类。
- 重写 `.conversation-shell ...` 下聊天工作区的视觉样式。
- 统一插件、专家、设置页的卡片、标题区、状态 chip、滚动区。
- 补齐 hover、focus-visible、disabled、scrollbar 等交互状态。
- `apps/ui/src/App.tsx`
- 仅做必要的结构类名整理和无业务语义增强。
- 保留现有事件处理、状态流、IPC 调用、专家选择、会话逻辑。
- 不移除现有 DOM 节点、按钮、自动化 selector。
- 可补充 `aria-label``role="alert"``aria-live` 等可访问性属性。
- 如实现消息 hover 时间,只在消息渲染结构中增加时间节点和格式化 helper,不改 `ChatMessage` 契约。
### 4.2 不修改范围
- 不改 Electron main process。
- 不改 preload bridge。
- 不改 `packages/shared-types` IPC 契约。
- 不改 workspace、runtime、gateway、项目执行逻辑。
- 不改专家、插件、会话、附件上传业务流程。
- 不改构建、打包、安装脚本。
## 5. 布局方案
### 5.1 整体结构
继续使用两列桌面结构:
- 左侧:固定导航栏,建议宽度 `260px-280px`
- 右侧:主工作区,占满剩余宽度。
不得把右侧工作区改成侧栏式单列页面,也不得引入第二套主壳布局。
### 5.2 左侧导航栏
左侧栏分为三段:
- 顶部:品牌区、主导航、新建会话。
- 中部:数字员工和专家分组。
- 底部:会话列表和必要状态入口。
交互要求:
- 当前主导航使用浅蓝底、蓝色左侧激活线或细边框。
- 专家 active 状态使用蓝色边框或蓝色浅底,不使用重阴影。
- 专家树和会话列表分别拥有稳定滚动区。
- 鼠标 hover 只改变背景、边框、文字色,不做明显位移。
### 5.3 右侧工作区
右侧统一为一个灰白工作台:
- 外层背景:浅灰白。
- 主工作面板:白色或近白色。
- 顶部:页面标题、当前专家/模式、运行状态 chip。
- 中部:消息列表、插件卡片、设置表单等内容。
- 底部:Composer 或页面操作区。
聊天页要求:
- 消息列表独立滚动。
- Composer 固定在右侧工作区底部。
- 输入框 resize 不破坏工作区高度。
- 状态提示不遮挡消息内容。
插件、专家、设置页要求:
- 共用同一套灰白卡片系统。
- 标题、说明、状态、操作按钮的层级一致。
- 页面滚动边界清晰,不出现窗口级混乱滚动。
## 6. 视觉方案
### 6.1 色彩
建议色彩方向:
- 页面背景:`#F5F7FA``#F8FAFC`
- 面板背景:`#FFFFFF``#FDFEFF`
- 主文本:`#0F172A`
- 次文本:`#475569`
- 弱文本:`#64748B`
- 主蓝:`#2563EB`
- 亮蓝:`#38BDF8`
- 浅蓝底:`#EFF6FF`
- 边框:`rgba(148, 163, 184, 0.22)`
原则:
- 灰白为主,蓝色为辅。
- 不使用大面积紫色渐变。
- 不使用大面积高透明玻璃。
- CTA 橙色如保留,只能用于极少数关键动作,不进入常规导航。
### 6.2 圆角和阴影
- 小控件:`10px-12px`
- 卡片:`14px-18px`
- 主工作面板:`20px-24px`
- 阴影以轻量为主,优先用细边框表达层级。
- 避免当前过强的彩色阴影和大面积发光。
### 6.3 消息样式
- 用户消息:浅蓝气泡,右对齐。
- AI 消息:白色内容卡,左对齐。
- 代码、trace、执行状态:使用低饱和蓝灰卡片。
- 系统提示:浅蓝状态条,明确但不抢正文注意力。
- 用户消息气泡必须使用浅蓝色,不使用白色或深蓝实心色,确保和 AI 回复卡片区分明显。
- 鼠标移到用户消息气泡或助手回复文本区域时,在该消息左下角显示发送/生成时间。
- 时间格式固定为 `yyyy-mm-dd HH:mm:ss`,例如 `2026-04-29 15:30:08`
- 时间文字使用小字号、低对比度灰蓝色,只在 hover/focus-within 时显示,不常驻占用阅读注意力。
- 时间显示不能改变消息主体布局高度,优先使用绝对定位或预留轻量位置处理。
### 6.4 Composer
- 使用白色输入面板。
- focus 时显示蓝色描边和轻微蓝色光晕。
- 附件、技能、发送按钮统一使用蓝色体系。
- 禁用状态必须可读,不允许只降低到很低透明度。
## 7. 交互方案
- 所有可点击元素必须有 `cursor: pointer`
- hover 不造成布局位移,避免明显 `translateY`
- focus-visible 使用统一蓝色 ring。
- disabled 状态降低强调,但保留文字可读性。
- 错误提示使用 `role="alert"``aria-live="polite"`
- Tab 顺序应与视觉顺序一致。
- 长列表使用内部滚动,不让整个应用窗口滚动。
- 可折叠专家分组必须保留 `aria-expanded``aria-controls`
## 8. 实施步骤
1. 先建立 `styles.css` 覆盖清单,标出所有核心布局类的早期定义、响应式定义、末尾主题覆盖。
2. 明确删除/保留策略:保留基础布局定义和必要响应式规则,删除重复主题覆盖,避免继续靠文件末尾叠加修补。
3. 建立统一 token:颜色、圆角、边框、阴影、间距,并把旧紫色/玻璃/强阴影 token 降级或替换为灰白蓝体系。
4. 收敛主壳样式,只保留一套稳定的 `shell/sidebar/main-shell/content-area` 定义。
5. 重写 `.conversation-shell ...` 范围内的聊天工作区样式,确保聊天页样式不泄漏到插件和设置页。
6. 统一左侧栏主导航、专家树、会话列表的视觉和滚动区。
7. 统一聊天、专家、插件、设置页的卡片和状态样式。
8.`App.tsx` 中只补充必要语义属性、结构类名、消息时间显示节点和格式化 helper,不改业务逻辑。
9. 清理旧样式覆盖,避免新旧主题并存。
10. 跑类型检查和构建。
11. 做聊天、专家、插件、设置四个页面的人工回归和截图验收。
## 9. 风险点
- `styles.css` 现有重复定义较多,删除或调整顺序可能引发布局回归。
- 当前文件末尾已有 `.shell.openclaw-theme``.conversation-shell` 主题覆盖,如果不先收敛覆盖层,会继续形成新旧主题叠加。
- `App.tsx` 文件很大,哪怕小结构调整也可能影响隐藏的自动化依赖。
- 消息时间显示依赖 `ChatMessage.createdAt`,字段已存在,但仍需要修改消息 DOM;应避免新增状态或改 IPC 契约。
- 左侧栏内容较多,小屏高度下仍可能拥挤,需要重点验证滚动。
- Composer 高度依赖 `conversationWorkspaceRef` 和当前 resize 逻辑,CSS 改动可能影响高度计算。
- 插件、专家、设置页共用样式后,局部页面可能需要少量例外样式。
- 中文文案和注释已有乱码,编辑时必须避免扩大编码问题。
- 如果旧样式清理不彻底,会出现新旧主题互相覆盖。
## 10. 测试计划
### 10.1 自动验证
```powershell
corepack pnpm typecheck
corepack pnpm build
```
如条件允许,补充一次截图验证:
- 启动 `corepack pnpm dev`
- 分别截取聊天、专家、插件、设置页。
- 覆盖 `375px``768px``1024px``1440px` 四个宽度。
- 检查右侧工作区不是空白、不是侧栏化、没有明显遮挡或滚动断层。
### 10.2 人工回归
聊天首页:
- 消息列表正常滚动。
- Composer 固定底部。
- 输入框 resize 正常。
- 发送、附件、技能按钮状态正常。
- 系统提示和错误提示可见。
专家页:
- 专家分组展开/收起正常。
- 专家 active 状态清晰。
- 专家切换不破坏当前会话逻辑。
- 专家执行状态可读。
插件页:
- 插件卡片布局正常。
- 状态 chip 可读。
- 页面滚动区不溢出。
设置页:
- 表单、按钮、密钥输入、目录选择样式正常。
- 保存、恢复、错误、成功提示可见。
- 长内容不挤压主工作区。
响应式:
- `375px`
- `768px`
- `1024px`
- `1440px`
可访问性:
- Tab 顺序合理。
- focus-visible 明显。
- 错误提示不是只靠颜色。
- 普通文本对比度不低于 4.5:1。
## 11. 验收标准
- 视觉上明确是灰白主基调,蓝色科技感只做强调。
- 左侧导航和右侧工作区仍保持稳定两列结构。
- 聊天、专家、插件、设置页像同一套产品系统。
- 没有业务行为变化。
- 没有新增 TypeScript 或构建错误。
- 没有明显滚动、遮挡、输入框高度回归。
- 截图验收中聊天、专家、插件、设置页在目标宽度下都保持正确高度和滚动边界。
- 没有新增中文乱码或编码污染。
# 前端 UI 与交互分阶段改造计划
## 背景
当前 renderer 曾长期集中在 `apps/ui/src/App.tsx``apps/ui/src/styles.css`
- `App.tsx` 同时承担 IPC mock、workspace 状态、聊天流、专家入口、设置页、插件页、smoke hooks 和主要 JSX。
- `styles.css` 存在多轮核心布局类定义,后续 UI 调整容易互相覆盖。
- 参考项目 `D:\cloud-desk-hero-main``features/*``stores/*``components/ui/*``lib/ipc/*` 分层方式适合当前项目,但不建议整套直接搬迁。
改造目标是渐进式拆分架构、稳定交互、统一视觉,而不是一次性重写。
## 总体原则
- 保留现有 Electron IPC、preload bridge、shared-types 合约。
- 保留现有 smoke hooks 和自动化依赖的 DOM/selector。
- 保留桌面两栏骨架:左侧 sidebar,右侧 workspace。
- 继续使用核心结构类:`conversation-shell``conversation-main-layout``conversation-content-area``conversation-panel`
- 不直接全量引入参考项目的 shadcn/Radix/Tailwind 3 配置;当前项目是 React 19 + Tailwind 4。
- 每阶段都必须能独立构建、验证、回滚。
## Phase 1:架构拆分准备,不改视觉
目标:先把纯逻辑和 mock 层从巨型 `App.tsx` 拆出,保持 UI 外观和行为不变。
修改范围:
- 新增 `apps/ui/src/lib/desktop-api.ts`,迁移 `desktopApi``mockDesktopApi`、mock stream 相关逻辑。
- 新增 `apps/ui/src/lib/chat-utils.ts`,迁移消息合并、markdown 渲染、附件判断、时间格式化等纯函数。
- 新增 `apps/ui/src/lib/constants.ts`,迁移默认 skill、附件扩展名、composer 尺寸常量。
- `App.tsx` 暂时仍负责顶层状态和 JSX,只减少内联工具逻辑。
当前状态:已完成。
代码事实:
- `apps/ui/src/lib/desktop-api.ts` 已存在。
- `apps/ui/src/lib/chat-utils.ts` 已存在。
- `apps/ui/src/lib/constants.ts` 已存在。
## Phase 2:Shell 与页面分区
目标:建立类似参考项目的 `features/*` 结构,但继续复用现有 CSS class 和交互行为。
修改范围:
- 新增 `features/shell/Sidebar.tsx`
- 新增 `features/shell/TopBar.tsx`
- 新增 `features/chat/ChatWorkspace.tsx`
- 新增 `features/experts/ExpertsView.tsx`
- 新增 `features/plugins/PluginsView.tsx`
- 新增 `features/settings/SettingsView.tsx`
- 新增 `features/knowledge/KnowledgeView.tsx`
- `App.tsx` 改为视图编排层。
当前状态:基本完成。
代码事实:
- `apps/ui/src/features/shell/AppSidebar.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/shell/Sidebar.tsx` 已存在。
- `apps/ui/src/features/shell/TopBar.tsx` 已存在。
- `apps/ui/src/features/chat/ConversationWorkspaceView.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/ChatWorkspace.tsx` 已存在。
- `apps/ui/src/features/experts/ExpertsView.tsx` 已存在。
- `apps/ui/src/features/plugins/PluginsView.tsx` 已存在。
- `apps/ui/src/features/settings/SettingsView.tsx` 已存在。
- `apps/ui/src/features/settings/SettingsPanels.tsx` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/knowledge/KnowledgeView.tsx` 已存在。
剩余边界:
- `App.tsx` 仍保留顶层编排、smoke actions、部分业务状态和回调。
## Phase 3:状态管理分层
目标:参考 store 思路,把 UI 状态、聊天状态、workspace 状态拆开。
修改范围:
- 新增 `useWorkspaceState`
- 新增 `useChatState`
- 新增 `useExpertsState`
- 新增 `useSettingsState`
- 新增 `useUiState`
- 先用 React hooks/context 实现;如状态复杂度仍高,再评估引入 `zustand`
- 下沉 `sendPrompt`、stream event、session 切换、专家入口选择等逻辑。
当前状态:部分完成。
代码事实:
- `apps/ui/src/features/chat/useProjectSessions.ts` 已存在。
- `apps/ui/src/features/chat/useSessionMessages.ts` 已存在。
- `apps/ui/src/features/chat/useMessageTraces.ts` 已存在。
- `apps/ui/src/features/chat/useChatStreamingController.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/useChatSessionsController.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/usePromptSubmission.ts` 已存在并被 `App.tsx` 引用。
- `apps/ui/src/features/chat/useHomeIntentSuggestion.ts` 已存在。
- `apps/ui/src/features/settings/useSettingsState.ts` 已存在。
- `apps/ui/src/features/shell/useHomeNavigation.ts` 已存在并承载 `openHomeChat``activateHomeShortcut``handleNavSelection`
剩余边界:
- `App.tsx` 当前仍有 2054 行。
- `App.tsx` 仍保留 smoke actions、部分顶层状态、页面级编排和部分业务回调;conversation/settings 的页面 props 组装已提到 return 前的 typed props 常量。
- `zustand` 未引入。
## Phase 4:设计系统与交互升级
目标:吸收参考项目视觉体系和组件化优点,但适配当前桌面工具产品。
修改范围:
- 整理 `styles.css`,确保核心布局类只保留最终生效的一套定义。
- 建立轻量 `apps/ui/src/components/ui/*`
- `Button`
- `IconButton`
- `Panel`
- `Tabs`
- `StatusChip`
- `ScrollArea`
- 统一颜色、间距、圆角、focus、hover、disabled、loading、error 状态。
- 优化聊天、专家、插件、设置页的桌面工具型布局。
当前状态:未完成,继续收口中。
代码事实:
- `apps/ui/src/components/ui/Button.tsx` 已存在。
- `apps/ui/src/components/ui/IconButton.tsx` 已存在。
- `apps/ui/src/components/ui/Panel.tsx` 已存在。
- `apps/ui/src/components/ui/ScrollArea.tsx` 已存在。
- `apps/ui/src/components/ui/StatusChip.tsx` 已存在。
- `Tabs` 未实现。
- `lucide-react` 未引入。
- `apps/ui/src/styles.css` 已收口为 8 行入口 import,实际样式拆到 `apps/ui/src/styles/*.css`
- `apps/ui/src/styles/base.css``chat.css``components.css``knowledge.css``plugins.css``settings.css``shell.css``theme-openclaw.css` 已存在并承载主样式。
- `conversation-shell``conversation-main-layout``conversation-content-area``conversation-panel` 等核心骨架类仍保留。
- 本轮已清理部分旧注释、空弹层注释块,并删除被末端 revamp 层覆盖的 `conversation-workspace` 早期视觉声明;布局、滚动和核心骨架类保留。
- 本轮已修复 `ConversationWorkspaceView` 中一处可见 mojibake 文案,恢复为“当前对话”。
剩余边界:
- 继续收口 `styles.css` 中确认安全的历史重复规则。
- 暂不迁移 composer submit、新建会话、home intent actions、smoke actions、stream 主链路这些高风险节点。
- 暂不引入 `lucide-react`、Radix、shadcn、zustand。
## 当前验收状态
截至 2026-05-08,最近一轮验证已通过:
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
- `corepack pnpm smoke:settings`
本轮验证:
- `corepack pnpm --filter @qjclaw/ui typecheck`
- `corepack pnpm build`
- `corepack pnpm smoke:default-chat`
- `corepack pnpm smoke:desktop-session-switch-stream`
- `corepack pnpm smoke:desktop-expert-bootstrap-ui`
- `corepack pnpm smoke:settings`
- 编码检查:`App.tsx``styles.css``ConversationWorkspaceView.tsx`、本文档均无 UTF-8 BOM;UI/文档错码标记扫描通过。
- 人工检查:Vite renderer chat 页面可打开,左侧栏/右侧 workspace 高度稳定,message list 与 composer 未出现横向溢出;Playwright 后续 nav 点击探针超时,未完成五页人工验收。
本轮未完成:
- 未新增 `Tabs`
- 未引入 `lucide-react`、Radix、shadcn、zustand。
- 未迁移 stream 主链路、composer submit、新建会话、home intent actions、smoke actions。
## 当前结论
四个阶段没有全部完成。
- Phase 1:已完成。
- Phase 2:基本完成。
- Phase 3:部分完成。
- Phase 4:未完成,仍处于 CSS 收口和低风险组件接入阶段。
下一步优先级:
- 继续压薄 `App.tsx` 中的顶层编排,但不动 stream 主链路和 smoke action 协议。
- 继续收口 `styles.css`,只删除确认被最终规则覆盖且不影响布局/滚动的历史规则。
- 如要推进设计系统,先补 `Tabs` 或扩大现有 UI 组件接入,但必须逐页 smoke 验证。
# 客户端 UI/UX 整改执行计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 基于当前客户端代码事实,把 renderer 收敛成更稳定、更专业、更易维护的 AI 桌面工作台。
**Architecture:** 不重写业务链路,不改 Electron IPC 合约。先固化事实基线和样式边界,再逐步压薄 `App.tsx`、收敛 CSS 覆盖、补齐工作台交互和可访问性。
**Tech Stack:** Electron renderer, Vite, React 19, TypeScript, plain CSS modules via `apps/ui/src/styles.css`, existing smoke hooks.
## Progress Sync - 2026-05-25
- Current committed progress: `4c81b90 style(ui): calm renderer shell visuals`.
- Validation passed after the CSS work: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
- Phase 1 is complete except baseline screenshots.
- Phase 2 has completed token cleanup, visual-noise reduction, and validation; duplicate layout ownership still needs a dedicated review pass.
- Phase 3, Phase 4, and Phase 5 have not started.
---
## 1. 当前事实基线
### 代码结构
- `apps/ui/src/App.tsx` 当前约 1674 行,仍承担顶层状态编排、视图切换、会话控制、设置 props 组装、smoke hooks 和启动遮罩。
- `apps/ui/src/styles.css` 已是入口文件,只 import `apps/ui/src/styles/*.css`
- 主样式分散在 `base.css``components.css``shell.css``chat.css``settings.css``plugins.css``knowledge.css``tasks.css``automation.css``theme-openclaw.css`
- 聊天主工作区已拆到 `features/chat/ConversationWorkspaceView.tsx`,但 props 数量过多,说明页面边界仍不够稳定。
- 通用 UI 组件已存在:`Button``IconButton``Panel``ScrollArea``StatusChip``Tabs`
### UI/UX 基线
- 产品类型:AI 生产力桌面客户端,核心体验应接近运营工作台,而不是营销页或普通聊天 demo。
- 当前优势:两栏骨架稳定、聊天/附件/流式状态完整、部分 focus 和 `aria-live` 已存在。
- 当前风险:CSS 多层覆盖、视觉蓝紫/玻璃/渐变偏多、移动断点不完整、导航密集但缺少 skip link、部分页面仍有 emoji 或手写 SVG 图标。
- 重点约束:继续保留 `conversation-shell``conversation-main-layout``conversation-content-area``conversation-panel`,不得破坏 smoke helpers 依赖的 DOM 和 selector。
## 2. 执行原则
- 不做一次性重写,每个阶段必须独立通过 typecheck/build。
- 优先修维护风险,再修视觉观感。
- 不改 `packages/shared-types` IPC 契约,除非单独立项。
- 不删除或隐藏现有 UI 控件,除非先确认 smoke helpers 和 renderer test 不依赖。
- 修改 `App.tsx` 时只用小 patch,避免中文编码损坏;修改后检查无 UTF-8 BOM。
- 工作台视觉以灰白为主、蓝色为状态和关键操作辅助,减少紫色渐变、彩色发光和过强阴影。
## 3. Phase 1 - 建立 UI 回归基线
**目标:** 先形成可复测基线,避免后续 UI 调整凭主观判断推进。
**Files:**
- Modify: `docs/2026-05-25-client-ui-ux-execution-plan.md`
- Read-only reference: `apps/ui/src/App.tsx`
- Read-only reference: `apps/ui/src/styles.css`
- Read-only reference: `apps/ui/src/styles/*.css`
- Read-only reference: `apps/ui/test/*.test.ts`
- [x] **Step 1: 记录当前核心文件规模**
Run:
```sh
wc -l apps/ui/src/App.tsx apps/ui/src/styles.css apps/ui/src/styles/*.css
```
Expected:
- 记录 `App.tsx` 行数。
- 记录每个 CSS 文件行数。
- 如果 `theme-openclaw.css``shell.css``components.css` 仍是最大样式文件,后续 CSS 收口优先从这三处开始。
Progress note:
- `App.tsx`: 1674 lines.
- Largest CSS files remain `shell.css` 1782, `theme-openclaw.css` 1600, `components.css` 1521.
- [x] **Step 2: 记录核心布局类定义位置**
Run:
```sh
rg -n "^\\.(shell|sidebar|main-shell|content-area|conversation-panel|message-list|composer-shell)\\b|\\.conversation-shell \\." apps/ui/src/styles/*.css
```
Expected:
- 输出所有核心类定义位置。
- 标出基础定义、聊天覆盖、主题覆盖三类来源。
- 不在本阶段删除样式,只形成清单。
Progress note:
- Base/core definitions: `components.css` (`content-area`, `message-list`, `composer-shell`), `shell.css` (`shell`, `sidebar`, `main-shell`).
- Conversation overrides: `chat.css` under `.conversation-shell ...`.
- Theme skin overrides: `theme-openclaw.css` under `.shell.openclaw-theme ...` and `.conversation-shell ...`.
- [x] **Step 3: 跑基础验证**
Run:
```sh
corepack pnpm --filter @qjclaw/ui typecheck
corepack pnpm build
```
Expected:
- 两个命令通过。
- 如果失败,先修复当前失败,不进入 Phase 2。
Progress note:
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`.
- Passed on 2026-05-25: `corepack pnpm build`.
- [x] **Step 4: 保存基线截图**
Start app:
```sh
corepack pnpm dev
```
Capture these views manually or with Playwright:
- Chat home
- Expert chat
- Task panel
- Automation tasks
- Knowledge
- Settings
Expected:
- 每个页面有桌面宽度截图。
- 至少补一张窄宽度截图,确认 `min-width: 960px` 的实际行为。
Progress note:
- Saved on 2026-05-25 under `docs/artifacts/2026-05-25-client-ui-ux-baseline/`.
- Desktop screenshots: `chat-home.png`, `expert-chat.png`, `task-panel.png`, `automation-tasks.png`, `knowledge.png`, `settings.png`.
- Narrow screenshot: `narrow-720-settings-min-width.png`, confirming the current desktop-first `min-width: 960px` contract creates horizontal overflow below that width.
## 4. Phase 2 - 收敛 CSS 骨架和视觉 token
**目标:** 让核心布局类只有清晰的基础层和主题层,减少后续 UI 回归。
**Files:**
- Modify: `apps/ui/src/styles/base.css`
- Modify: `apps/ui/src/styles/components.css`
- Modify: `apps/ui/src/styles/shell.css`
- Modify: `apps/ui/src/styles/chat.css`
- Modify: `apps/ui/src/styles/theme-openclaw.css`
- [x] **Step 1: 建立 token 边界**
In `apps/ui/src/styles/theme-openclaw.css`, keep all product-level theme variables under:
```css
.shell.openclaw-theme {
--revamp-canvas: #f5f7fa;
--revamp-surface: #ffffff;
--revamp-surface-soft: #f8fafc;
--revamp-text: #0f172a;
--revamp-text-muted: #475569;
--revamp-text-soft: #64748b;
--revamp-blue: #2563eb;
--revamp-blue-strong: #1d4ed8;
--revamp-blue-soft: #eff6ff;
--revamp-border: rgba(148, 163, 184, 0.24);
--revamp-border-strong: rgba(148, 163, 184, 0.38);
--revamp-shadow: 0 14px 32px rgba(15, 23, 42, 0.06);
--revamp-shadow-soft: 0 8px 18px rgba(15, 23, 42, 0.04);
}
```
Expected:
- Purple variables may remain only where still referenced by legacy expert-specific styles.
- New or touched styles use the gray/blue token set above.
Progress note:
- Completed by `4c81b90`; product-level `--revamp-*` variables now live under `.shell.openclaw-theme`.
- Touched visual styles were moved toward gray/blue tokens and reduced purple usage.
- [x] **Step 2: Reduce duplicate core layout definitions**
Keep responsibilities:
- `base.css`: reset, root tokens, font, body.
- `components.css`: generic reusable components only.
- `shell.css`: base shell layout and non-theme structural behavior.
- `chat.css`: conversation-specific structure only under `.conversation-shell`.
- `theme-openclaw.css`: final product skin and current gray/blue visual theme.
Expected:
- No new global `.message-list` or `.composer-shell` visual overrides outside the intended layer.
- Conversation-only changes remain scoped under `.conversation-shell ...`.
Progress note:
- Partially addressed by `4c81b90`.
- Still open: dedicated review of ownership overlap across `components.css`, `shell.css`, `chat.css`, and `theme-openclaw.css`.
- Completed on 2026-05-25: removed shell/sidebar/main-shell responsive overrides from `components.css`, kept shell responsive ownership in `shell.css`, and removed structural `main-shell`/`content-area` overrides from `theme-openclaw.css`.
- Documented the desktop-first `min-width: 960px` shell contract in `shell.css`.
- [x] **Step 3: Remove obvious visual noise**
In touched rules:
- Replace heavy color shadows with `var(--revamp-shadow-soft)` or border-only states.
- Avoid new `translateY` hover movement.
- Keep card radius at or below existing app norms unless the rule already requires larger panels.
- Do not add decorative orbs, bokeh, or unrelated gradients.
Expected:
- Left sidebar and workspace feel calmer.
- Active/hover/focus states remain visible.
Progress note:
- Completed by `4c81b90`; removed several `translateY` hover effects, softened shadows, reduced decorative gradients, and replaced purple scrollbar/drag states with blue/gray variants.
- [x] **Step 4: Validate after CSS edits**
Run:
```sh
corepack pnpm --filter @qjclaw/ui typecheck
corepack pnpm build
```
Expected:
- Both pass.
- `git diff -- apps/ui/src/styles.css apps/ui/src/styles/*.css` shows targeted CSS changes only.
Progress note:
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`.
- Passed on 2026-05-25: `corepack pnpm build`.
- CSS work was committed as `4c81b90 style(ui): calm renderer shell visuals`.
- Re-ran after ownership cleanup on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
## 5. Phase 3 - 压薄 App.tsx 的 UI 编排
**目标:** 降低 `App.tsx` 的维护风险,但不碰 stream 主链路和 smoke action 协议。
**Files:**
- Modify: `apps/ui/src/App.tsx`
- Modify: `apps/ui/src/features/chat/ConversationWorkspaceView.tsx`
- Modify: `apps/ui/src/features/settings/SettingsPanels.tsx`
- Create or modify focused hooks only under existing `apps/ui/src/features/*`
- [x] **Step 1: 拆出纯 props 组装**
Move only pure prop-building logic from `App.tsx` into focused helpers or hooks when all inputs are already available.
Candidate boundaries:
- Conversation workspace props.
- Settings panel props.
- Sidebar derived labels and entries.
Expected:
- No behavior change.
- No IPC calls move across ownership boundaries in this step.
- `App.tsx` line count decreases, but line count is not the success metric; clearer ownership is.
Progress note:
- Completed on 2026-05-25 by grouping `ConversationWorkspaceView` props into focused `status`, `emptyState`, `messages`, `composer`, and `actions` objects.
- IPC calls, stream lifecycle, composer submit/cancel, session actions, startup overlay actions, and smoke hooks remain in `App.tsx`.
- [x] **Step 2: Keep high-risk flows in place**
Do not move these during this phase:
- stream request lifecycle
- `__QJC_SMOKE__`
- `__QJC_SMOKE_ACTIONS__`
- composer submit/cancel
- session creation/closing
- startup overlay retry/export actions
Expected:
- Existing smoke tests do not need selector or action rewrites.
Progress note:
- Confirmed during the prop grouping pass; no smoke contract or high-risk flow was moved.
- [x] **Step 3: Reduce oversized component props**
For `ConversationWorkspaceView`, group related props into typed objects only when ownership is obvious:
- `status`
- `emptyState`
- `messages`
- `composer`
- `actions`
Expected:
- Component call site becomes easier to read.
- Existing child component props stay unchanged unless required.
Progress note:
- `ConversationWorkspaceView` now receives typed grouped props; child components still receive their existing prop shapes.
- [x] **Step 4: Validate App.tsx encoding and tests**
Run:
```sh
/usr/bin/python3 -c "from pathlib import Path; p=Path('apps/ui/src/App.tsx'); b=p.read_bytes(); print(b[:3]); assert b[:3] != b'\\xef\\xbb\\xbf'"
git diff -- apps/ui/src/App.tsx
corepack pnpm --filter @qjclaw/ui typecheck
```
Expected:
- First command prints bytes that are not UTF-8 BOM.
- Diff contains no mojibake and no unrelated Chinese copy changes.
- UI typecheck passes.
Progress note:
- Passed on 2026-05-25: BOM check printed `b'imp'`.
- Inspected `git diff -- apps/ui/src/App.tsx apps/ui/src/features/chat/ConversationWorkspaceView.tsx`; no mojibake or unrelated Chinese copy changes.
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
## 6. Phase 4 - 修正核心工作流 UI/UX
**目标:** 优先改善用户每天会反复使用的界面:聊天、任务、自动化、设置。
**Files:**
- Modify: `apps/ui/src/features/chat/ChatComposer.tsx`
- Modify: `apps/ui/src/features/chat/MessageList.tsx`
- Modify: `apps/ui/src/features/tasks/TaskPanelView.tsx`
- Modify: `apps/ui/src/features/automation/AutomationTasksView.tsx`
- Modify: `apps/ui/src/features/settings/SettingsPanels.tsx`
- Modify: matching CSS files under `apps/ui/src/styles/`
- [x] **Step 1: Chat composer**
Requirements:
- Keep Enter send and Shift+Enter newline behavior.
- Keep attachment upload button and drag-drop.
- Keep resize handle.
- Ensure disabled state remains readable.
- Ensure focus state uses one consistent blue ring.
Expected:
- Composer does not resize the main shell unexpectedly.
- Attachment chip text truncates cleanly.
- Submit button remains icon-first with accessible label.
Progress note:
- Completed on 2026-05-25: added a single blue focus ring on the composer surface and stabilized attachment chip truncation.
- [x] **Step 2: Message list**
Requirements:
- Keep user messages right-aligned and assistant messages left-aligned.
- Keep assistant markdown card readable at long line lengths.
- Keep copy/delete action buttons keyboard reachable.
- Keep streaming trace `aria-live` behavior.
Expected:
- Long Chinese, English, URLs, and code blocks do not break layout.
- Message actions do not shift message height when appearing.
Progress note:
- Completed on 2026-05-25: added long-line wrapping guards for message text, assistant markdown, and code blocks while preserving existing message alignment and `aria-live` trace behavior.
- [x] **Step 3: Task panel icon cleanup**
Replace visible emoji used as UI icons with existing icon components or local SVG components.
Known target:
- `apps/ui/src/features/tasks/TaskPanelView.tsx`: package icon in the content output heading.
Expected:
- No visible emoji icons remain in primary tool controls or headings.
- Existing Chinese text remains unchanged except for necessary label updates.
Progress note:
- Completed on 2026-05-25: replaced the content-output heading emoji with a local SVG icon and changed copy feedback to text-only `已复制`.
- [x] **Step 4: Automation tasks density**
Requirements:
- Keep list, detail, form, and run history usable without window-level scroll confusion.
- Filter buttons and edit/create actions must have clear active and disabled states.
- Error state must use `role="alert"` or visible announced equivalent.
Expected:
- The page reads as an operational tool, not a card-heavy marketing layout.
- Long task prompts truncate or wrap predictably.
Progress note:
- Completed on 2026-05-25: tightened list/detail/form/run-history spacing, preserved `role="alert"` errors, and added clearer active/disabled states.
- [x] **Step 5: Settings hierarchy**
Requirements:
- Keep config source toggle visible and keyboard reachable.
- Secret reveal and copy controls must stay accessible.
- Save/reset/edit buttons must communicate disabled state clearly.
- Tabs must preserve `aria` semantics from existing `Tabs` component.
Expected:
- Users can scan required vs optional config groups quickly.
- Editing one section does not visually imply other sections are editing.
Progress note:
- Completed on 2026-05-25: preserved source toggle, secret reveal/copy, save/reset/edit, and Tabs semantics; copy feedback is now text-only without emoji.
- [x] **Step 6: Validate key workflows**
Run:
```sh
corepack pnpm --filter @qjclaw/ui typecheck
corepack pnpm build
```
If touched flow has existing source tests, also run:
```sh
node --test apps/ui/test/taskPanelViewSource.test.ts
node --test apps/ui/test/settingsPanelsSource.test.ts
node --test apps/ui/test/chatCancelSource.test.ts
```
Expected:
- Typecheck and build pass.
- Relevant source tests pass.
Progress note:
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
- Passed on 2026-05-25: `node --test apps/ui/test/taskPanelViewSource.test.ts`; `node --test apps/ui/test/settingsPanelsSource.test.ts`; `node --test apps/ui/test/chatCancelSource.test.ts`.
## 7. Phase 5 - 可访问性和响应式补齐
**目标:** 解决 UI-ux-pro-max 指出的高优先级问题:键盘导航、skip link、错误提示、图片 alt、响应式边界。
**Files:**
- Modify: `apps/ui/src/App.tsx`
- Modify: `apps/ui/src/features/shell/AppSidebar.tsx`
- Modify: `apps/ui/src/features/shell/Sidebar.tsx`
- Modify: `apps/ui/src/styles/base.css`
- Modify: `apps/ui/src/styles/shell.css`
- Modify: `apps/ui/src/styles/theme-openclaw.css`
- [x] **Step 1: Add skip link**
Add a visually hidden but focus-visible skip link near the top of the shell:
```tsx
<a className="skip-link" href="#main-content">跳到主内容</a>
```
Add id to main content:
```tsx
<main id="main-content" className={...}>
```
Expected:
- First Tab focuses skip link.
- Pressing Enter moves focus target to main content.
Progress note:
- Completed on 2026-05-25: added `跳到主内容` skip link and `main#main-content` with `tabIndex={-1}`.
- [x] **Step 2: Add skip link styles**
Add CSS:
```css
.skip-link {
position: fixed;
top: 10px;
left: 10px;
z-index: 10000;
padding: 8px 12px;
border-radius: 8px;
background: #ffffff;
color: #1d4ed8;
border: 1px solid rgba(37, 99, 235, 0.38);
transform: translateY(-140%);
}
.skip-link:focus-visible {
transform: translateY(0);
outline: 2px solid rgba(37, 99, 235, 0.56);
outline-offset: 2px;
}
```
Expected:
- Hidden by default.
- Visible only when focused.
Progress note:
- Completed on 2026-05-25 in `shell.css`.
- [x] **Step 3: Confirm nav tab order**
Manual validation:
- Tab order starts at skip link.
- Then window controls if focusable.
- Then sidebar primary navigation.
- Then page/workspace content.
- Then composer or page actions.
Expected:
- No keyboard trap.
- No hidden active control receives focus unless intentionally screen-reader-only.
Progress note:
- Confirmed on 2026-05-25 with browser snapshot: first Tab focuses skip link, Enter moves focus to `main#main-content`, then navigation/content controls remain reachable.
- [x] **Step 4: Define desktop responsive contract**
Current shell uses `min-width: 960px`. Keep that if the product is desktop-only, but document it in CSS comments and acceptance notes.
Expected:
- At widths below 960px, behavior is intentional: either horizontal app viewport constraint or a later mobile-specific plan.
- Do not pretend mobile is supported unless the shell actually supports it.
Progress note:
- Documented in `shell.css`; current contract remains desktop-first with `min-width: 960px`.
- [x] **Step 5: Validate accessibility basics**
Manual checks:
- Error messages announce via `role="alert"` or `aria-live`.
- Meaningful images have alt text; decorative images use `alt=""`.
- Buttons with icon-only visuals have `aria-label`.
- Heading hierarchy per page starts with one clear `h1` or equivalent page title.
Expected:
- No obvious keyboard-only blocker in chat, settings, task panel, automation tasks.
Progress note:
- Checked on 2026-05-25: error notices retain `role="alert"`, icon-only controls retain accessible labels, decorative startup image keeps `alt=""`, and settings Tabs preserve existing aria semantics.
## 8. Final Acceptance Checklist
- [x] `corepack pnpm --filter @qjclaw/ui typecheck` passes.
- [x] `corepack pnpm build` passes.
- [x] Relevant source tests pass for touched views.
- [x] `App.tsx` has no UTF-8 BOM.
- [x] `git diff -- apps/ui/src/App.tsx` shows no Chinese mojibake.
- [x] Chat, experts, tasks, automation, knowledge, settings render with correct height and scrolling.
- [x] Left sidebar remains left column; right workspace remains full work area.
- [x] No core smoke selector or `__QJC_SMOKE_ACTIONS__` contract is removed.
- [x] No visible emoji remains in primary tool controls after Phase 4.
- [x] Focus-visible states are visible in sidebar, composer, tabs, settings controls, and message actions.
## 9. Suggested Commit Sequence
1. `docs(ui): add client ui ux execution plan`
2. `refactor(ui): document and reduce shell style overlap`
3. `refactor(ui): reduce app renderer prop assembly`
4. `fix(ui): polish chat workspace interactions`
5. `fix(ui): align task automation settings workbench ui`
6. `fix(ui): add renderer keyboard navigation affordances`
Each commit should include the validation commands run in the commit body or PR description.
# OpenClaw Daily Report 方案
## 1. 目标
为 OpenClaw 增加一个“每日自动汇总上报”能力:
- 每天自动整理当天的对话与工作情况
- 通过文档中定义的 `POST /openclaw-daily-report` 接口上报云端
- 不影响现有聊天、心跳、配置同步等主流程
- 方案适合测试阶段快速验证,也适合正式环境长期运行
## 2. 对接的云端 API
使用 `docs/云端API接入文档.txt` 中定义的接口:
- `POST /openclaw-daily-report`
### 请求字段
- `api_key`
- `summary_date`
- `total_conversations`
- `total_messages`
- `total_tokens`
- `total_errors`
- `highlights`
- `issues`
- `raw_summary_text`
- `active_channels`
### 返回字段
- `ok`
- `summary_id`
- `employee_id`
- `summary_date`
## 3. 数据来源
日报内容从现有桌面端数据里生成,主要来源如下:
- `packages/gateway-client/src/index.ts`
- 读取会话列表
- 读取会话消息
- `apps/desktop/src/main/ipc.ts`
- 现有聊天、会话、配置的主进程入口
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
- 现有主进程定时器、云端状态、运行时 telemetry 的管理模式
- `apps/desktop/src/main/services/cloud-api.ts`
- 云端请求封装与 `api_key` 读取方式
- `apps/desktop/src/main/services/diagnostics.ts`
- 结构化报告的组织方式,可参考其输出风格
## 4. 总体设计
### 4.1 单独做成定时任务
日报能力不挂到聊天发送链路,也不挂到心跳或配置同步链路里,而是单独做成一个主进程侧服务,例如:
- `DailyReportService`
这个服务只做三件事:
1. 到点检查是否需要发送日报
2. 生成日报 payload
3. 调用 `/openclaw-daily-report`
### 4.2 为什么要独立出来
这样可以保证:
- 日报失败不会影响聊天
- 日报失败不会影响心跳
- 日报失败不会影响配置同步
- 日报逻辑更容易测试和维护
## 5. 报表内容设计
建议日报保持“简洁 + 有要点”,避免发送过长原文。
### 5.1 建议 payload 结构
```ts
interface OpenClawDailyReportPayload {
api_key: string;
summary_date: string;
total_conversations: number;
total_messages: number;
total_tokens: number;
total_errors: number;
highlights: string[];
issues: string[];
raw_summary_text: string;
active_channels: Array<{
type: string;
name: string;
}>;
}
```
### 5.2 内容建议
- `summary_date`
- 使用本地日期,格式 `YYYY-MM-DD`
- `total_conversations`
- 当天会话数
- `total_messages`
- 当天消息总数
- `total_tokens`
- 如果当前链路能拿到 token,就统计累计值
- `total_errors`
- 当天错误数
- `highlights`
- 3~5 条关键摘要
- `issues`
- 异常、失败、恢复情况
- `raw_summary_text`
- 一段简短的人类可读总结
- `active_channels`
- 当天活跃的渠道列表
## 6. 调度策略
### 6.1 独立调度器
建议在主进程里单独维护一个定时器:
- 采用 `setInterval`
- 每分钟检查一次当前时间
- 如果当前时间已经到设定时间,且今天还没发过,就发送一次
### 6.2 时间配置
时间做成可配置项,便于测试和正式环境切换。
建议使用环境变量:
- `DAILY_REPORT_HOUR`
规则:
- 正式环境默认 `20`,即晚上 8 点
- 测试阶段可以改成当前小时或附近小时,方便快速触发
- 更改配置后,在下次启动时生效
### 6.3 去重机制
为了避免重复发送:
- 记录 `lastSummaryDate`
- 如果今天已经发过,就跳过
- 重启后也能继续判断,避免重复补发
## 7. 预期改动文件
### 7.1 核心文件
- `apps/desktop/src/main/services/cloud-api.ts`
- 增加 `POST /openclaw-daily-report` 的请求方法
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
- 增加日报生成与调度逻辑,或抽出一个新的主进程服务
- `apps/desktop/src/main/index.ts`
- 在应用启动时拉起日报服务
- `packages/shared-types/src/index.ts`
- 如有需要,补充日报请求/响应类型
### 7.2 可选文件
- `apps/desktop/src/main/ipc.ts`
- 如果需要手动触发测试,可增加一个 IPC 入口
## 8. 实现步骤
### Step 1:接入日报 API
`apps/desktop/src/main/services/cloud-api.ts` 中增加一个专门请求 `POST /openclaw-daily-report` 的方法。
要求:
- 复用现有 HTTP 请求封装
- 复用现有 `api_key` 获取方式
- 返回值结构化处理
### Step 2:生成日报内容
在主进程里读取当天会话数据,计算:
- 会话数
- 消息数
- token 数
- 错误数
- 亮点
- 问题
- 简短文本总结
- 活跃渠道
### Step 3:增加独立定时任务
新增一个主进程服务,例如 `DailyReportService`
- 启动时检查是否需要补发前一天的日报
- 每分钟检查一次时间
- 到点后发送日报
- 发送成功后记录 `lastSummaryDate`
### Step 4:保持主流程隔离
日报失败时:
- 只记录日志
- 不抛到聊天逻辑
- 不影响心跳和配置同步
## 9. 测试计划
### 9.1 代码级检查
```bash
corepack pnpm typecheck
corepack pnpm build
```
### 9.2 单元级检查
验证以下内容:
- 日报 payload 组装正确
- `summary_date` 格式正确
- 统计字段计算正确
- `lastSummaryDate` 能防止重复发送
- 请求失败不会影响聊天或 heartbeat
### 9.3 集成检查
1. 启动桌面端
2. 绑定 employee `api_key`
3. 设置 `DAILY_REPORT_HOUR` 为测试阶段可快速触发的时间
4. 创建几条会话和消息
5. 等定时器触发或手动触发日报
6. 确认 `/openclaw-daily-report` 收到正确 payload
7. 确认返回里有 `ok``summary_id``employee_id``summary_date`
### 9.4 回归检查
- 心跳是否仍正常执行
- 配置同步是否仍正常执行
- 聊天发送是否仍正常执行
- 重启后是否不会对同一天重复上报
### 9.5 失败路径检查
- 模拟接口返回 4xx / 5xx
- 确认不会影响主业务
- 确认日志里能看到失败原因
- 确认没有活动时的策略符合产品预期(跳过或发最小摘要)
## 10. 约定和注意事项
- 请求字段名必须严格按文档来,不要改成事件式字段名
- `raw_summary_text` 要简洁,不要过长
- `summary_date` 使用本地日期字符串 `YYYY-MM-DD`
- 日报功能应保持独立,不能影响现有聊天链路
- 文档后续若要补充细节,可直接在这里扩展
报错:
这个是通过上传pdf生成纯画面视频报错:
[ERROR] Generate Seedance split video failed with exit code 1. See E:\testworkspace2\projects\douyin\output\分析一下这个文件\video_seedance_stderr.txt | split_error=Seedance clip 1 failed. See E:\testworkspace2\projects\douyin\output\分析一下这个文件\seedance-split-runs\20260427-095752\shots\shot-01\stderr.txt
=== Douyin Master Workflow ===
Route: 直出创作链
Source mode: 不走样本调研
Core keyword: 分析一下这个文件
Product: 分析一下这个文件
Audience: 加盟创业人群
Type: 纯画面视频
Normalized type: 纯画面视频
Writer type: 知识干货
Requested video engine: auto
Project folder: 分析一下这个文件
Attempt id: 20260427-095748-052712
Output dir: E:\testworkspace2\projects\douyin\output\分析一下这个文件
Direct image Seedance mode: True
Detected image-driven Seedance request without narration; skip preview copywriting stage.
[status] preview_packaging | 33%(2/6) | 文案生成 | 正在整理预览文件
Resolved video engine: seedance
[status] video_seedance | 67%(4/6) | 视频生成 | Generate Seedance split video
[video_seedance] Generate Seedance split video
[status] video_seedance | 67%(4/6) | 视频生成 | Generate Seedance split video 失败
这个是给抖音链接数字人口播视频报错:
修复抖音工作流两个失败:抖音下载器 + Seedance PDF 图片格式 nage
Context
用户运行了两个抖音工作流任务,分别在不同阶段失败:
1. 宣传一下我家面包店 — 在 "样本准备" 阶段失败,douyin downloader 无法下载抖音视频
2. 分析一下这个文件 — 在 "视频生成" 阶段失败,Seedance API 返回 InvalidParameter.UnsupportedImageFormat,因为 PDF 文件被当作参考图传入了只支持 JPG/PNG/WebP 的 API
错误 1:抖音视频下载失败
- 错误: Error: Unable to parse video detail from page; fallback failed:
- 位置: E:\testworkspace2\projects\douyin\skills\douyin-video-downloader\scripts\douyin.js + run_downloader.py
- 原因: Node.js 解析器和 Playwright 备用方案都无法从抖音页面提取视频信息。可能是抖音 HTML 结构变化或反爬措施
错误 2:Seedance PDF 作为图片传入 API
- 错误: Seedance create task failed with HTTP 400. InvalidParameter.UnsupportedImageFormat
- API 请求中的问题 URL: http://tcwwu6wg4.hd-bkt.clouddn.com/omnihuman/reference-image/...pdf
- 根因链:
a. 用户上传了 PDF 文件(2025欧文酵室招商手册(1).pdf)
b. coordinator.py:5486 — 当 seedance_image 为空时,回退使用 self.config.image(即 PDF 路径)
c. coordinator.py:11286 — resolved_seedance_image = self.config.seedance_image or self.config.image
d. 该 PDF 被上传到七牛,但保留 .pdf 扩展名
e. run_seedance.py:normalize_media_input() — 对远程 URL 不做格式验证,直接传入 API
f. Seedance API 拒绝 PDF 格式
修复方案
Fix 1:Seedance 图片格式校验(高优先)
文件: E:\testworkspace2\projects\douyin\skills\douyin-master\scripts\coordinator.py
在 materialize_project_inputs() 方法中(约 line 5399),当 seedance_image 回退到 self.config.image 时,检查文件是否为 Seedance 支持的图片格式。如果是 PDF,则改用 asset_understanding 提取的图片(已有 extracted_images/ 目录,包含从 PDF
提取的 JPG 页面图)。
具体改动:
- 在 line 5484-5487(seedance_image 回退逻辑)之前,添加 PDF 检测:
- 如果 self.config.image 以 .pdf 结尾,查找 asset_understanding/extracted_images/ 下的 JPG 文件
- 使用第一个提取的 JPG 作为 seedance_image
- 如果没有提取图片,保持 seedance_image 为空(让 Seedance 用 prompt 生成首帧图)
同时在 run_seedance.py 的 normalize_media_input() 中添加远程 URL 格式校验(防御性修复):
- 对 media_kind == "image" 的远程 URL,检查扩展名是否为 .jpg/.jpeg/.png/.webp
- 不支持的格式直接跳过(不做 reference),而不是让 API 400 报错
Fix 2:抖音下载器(低优先,需更多调查)
文件: E:\testworkspace2\projects\douyin\skills\douyin-video-downloader\scripts\douyin.js + run_downloader.py
抖音下载器失败是外部依赖问题(抖音反爬/HTML结构变化),需要:
1. 手动测试 douyin.js 能否用新 URL 正常工作
2. 检查 Playwright 浏览器是否正确安装(vendor/openclaw-runtime/ 内的 Chromium)
3. 可能需要更新解析器适配抖音新页面结构
这个修复比较复杂且涉及第三方网站变化,建议先手动下载视频作为替代方案。
关键文件(源码在 D:\qjclaw\workspace\douyin\,运行时副本在 E:\testworkspace2\projects\douyin\)
- D:\qjclaw\workspace\douyin\skills\douyin-master\scripts\coordinator.py — 主要修复点:PDF→JPG 回退逻辑
- D:\qjclaw\workspace\douyin\skills\seedance-2-0-video-generator\scripts\run_seedance.py — 防御性修复:远程 URL 格式校验
- D:\qjclaw\workspace\douyin\skills\douyin-asset-understanding\ — PDF 图片提取(已有功能,直接复用)
修改源码后,需要重新打包 zip 上传到云端或本地替换,让工作流使用修复后的代码。
验证
1. 修改 coordinator.py 后,重新提交一个带 PDF 附件的抖音任务,确认 Seedance 使用提取的 JPG 而非 PDF
2. 检查 task_create.json 中的 image_url 不再包含 .pdf 扩展名
3. 确认 Seedance API 返回成功(HTTP 200/201)而非 400
# 抖音 / 小红书云端 Zip 项目可插拔改造方案
## Summary
基于当前仓库事实,这条主链路已经成立:
1. 客户端初始化时调用 POST https://spb-bp1wv2oe0hvfvi98.supabase.opentrust.net/functions/v1/openclaw-employee-config
2. 从返回的 skills[].skill.download_url 发现 zip 项目包
3. ProjectBundleService 下载、解压、物化到本地 workspace/projects/<projectId>
4. ProjectStoreService 把项目写入本地 inventory
5. 专家页按 project 展示,进入项目后再使用该项目 skills
6. ProjectExecutionRouter 在 workspace-entry / skill / chat-fallback 之间路由
当前阻塞点不是“云端 zip 能不能下发”,而是“项目包要不要满足客户端现有项目契约”。
按你给的真实抖音包事实,后续小红书同结构时,推荐方案是:
- 两个项目包都补标准 project.json
- 客户端继续沿用现有 openclaw-employee-config -> zip bundle -> project isolation 主链路
- 客户端补少量校验和 UI/展示收口,不做“扫描 skills 自动反推整项目”的大改
- 最终目标是:新电脑安装客户端,初始化成功后自动拉项目包,本地物化后即可在专家页选项目并跑起来
## Key Changes
### 1. 云端协议继续沿用现状,不改接口形态
继续使用现有接口和字段,不新加 projects[]
- 接口:/functions/v1/openclaw-employee-config
- 载荷来源:skills[]
- zip 地址:skills[i].skill.download_url
- 一个 zip 对应一个 project
- 多项目通过多个 skills[] 项表达
服务端交付要求固定为:
- 抖音包和小红书包都从这个接口下发
- 同一项目升级时,保持稳定 projectId
- zip 变更时,保证 URL、etag、lastModified、content-length 或 configVersion 至少有一项能触发客户端重拉
- 不要求服务端生成本地运行态文件,运行态仍由客户端本地维护
### 2. 项目包交付契约改为“标准 project bundle”
后续抖音、小红书 zip 都按统一项目根结构交付:
<project-root>/
project.json
AGENTS.md
SOUL.md
USER.md
README.md
skills/
cron/ # 可选
memory/ # 可选,客户端也会补
output/ # 可选
当前你给的抖音包已满足大部分条件:
- 根目录就是项目根
- 有 skills/
- 有 AGENTS.md / SOUL.md / USER.md
- 缺的是 project.json
因此项目包侧最小改造就是:
- 抖音包补 project.json
- 小红书包补 project.json
- boundSkillIds 与真实 skills/<skillId> 子目录名一致
- defaultEntry.type 固定为 "workspace-entry"
推荐项目包模板约束:
- id 稳定,抖音固定 douyin,小红书固定 xhs
- name、platform、projectType、description 填全
- entries 至少包含一条 workspace-entry
- 不把真实 token、账号密钥、长效凭证打进 zip
- 所有可变运行态统一写到 memory/,避免 bundle 更新时丢失
### 3. 客户端主链路保留,但补 4 处必要改造
#### 3.1 project-bundle.ts
这里是本次最关键的改造点。
当前事实:
- resolveBundleMetadata() 在没有 project.json 时会自动用 zip 名推导 projectId/projectName
- stageProjectDirectory() 会在物化时自动写一个 project.json
- 所以今天“缺 project.json”不会彻底拦住安装,但会把项目识别退化成“按 zip 名猜”,这不适合长期可插拔交付
改造要求:
- 安装后显式校验项目根必须存在 project.json
- 根目录模式和 projects/<id>/ 模式都支持,但都必须最终拿到标准 project.json
- project.json 缺失时:
- bundle sync 标记为 error
- startup log 记录缺失原因
- 不把该 bundle 写入 ready project
- 保留现有 shared skills/cron 物化逻辑
- 保留现有 bundle replace / rollback / preserve local state 逻辑
这样可以保证以后“所有云端项目都按一个标准接入”,而不是靠客户端猜。
#### 3.2 project-store.ts
这里需要把“项目识别”和“项目技能隔离”收紧。
当前事实:
- syncBundleProject() 已支持按 project 写入 inventory
- listProjectSkills(projectId) 会按 boundSkillIds 过滤工作区 skills
- 但当前还会混入 curated generic skills,这和“进入项目后只看该项目自己的 skills”不完全一致
改造要求:
- syncBundleProject() 读取 bundle 物化后的 project.json,把项目级字段完整写入本地记录
- boundSkillIds 必须和实际 workspace/skills/<skillId> 交叉校验
- 若 boundSkillIds 声明了不存在 skill:
- 过滤掉无效项
- 写诊断日志
- 若过滤后为空,则项目标记为 error 或 not ready
- 增加一个明确策略:
- 专家项目页默认只展示本项目 boundSkillIds
- curated generic skills 不自动混入专家项目 skills 列表
- 如需保留通用技能,单独作为 home/global skills 展示,不算项目私有 skills
这是满足“抖音项目里只出现抖音技能,小红书里只出现小红书技能”的关键。
#### 3.3 project-execution-router.ts
当前这里已经基本满足要求。
当前事实:
- 若 project.json.defaultEntry.type === "workspace-entry",优先走 workspace-entry
- 没有 project.json 时,也会因为 AGENTS.md 命中 workspace marker
- 旧版 plugin marker 仍保留兼容
改造要求:
- 保持 workspace-entry 作为专家项目默认入口
- 保持 skill 显式选择时仍可走 skill
- 继续保留旧版 xhs plugin marker 兼容,作为迁移兜底
- 但对新标准 bundle,不再依赖 AGENTS.md 来“猜项目入口”,而是以 project.json 为准
- 若项目 defaultEntry 缺失或不合法:
- 给出明确诊断
- 不静默退化成普通 chat
#### 3.4 UI / IPC / 专家页
当前 UI 已有 projects、currentProjectId、setActive(projectId) 链路,改造重点是收口展示策略。
改造要求:
- 专家页一级列表显示 projects,不显示全局混合 skills
- 点击专家卡片后调用 projects.setActive(projectId)
- 进入项目后:
- 会话隔离在 project:<projectId>:<uuid>
- 技能面板只显示该项目 boundSkillIds
- 上下文只注入该项目的 AGENTS.md / SOUL.md / USER.md / README.md
- fresh install 初始化成功后:
- 首次云端 sync 完成即刷新专家页
- 如至少有一个 ready project,可自动把第一个项目设为 active,或保留显式选择页,但规则要固定
- 项目状态要可见:
- downloading
- syncing
- ready
- error
- 缺 project.json
- skill 缺失
- bundle 更新失败
### 4. 新电脑初始化必须打通的完整流程
目标不是“开发机能跑”,而是“新电脑安装客户端后初始化即可跑项目”。这条链必须定义成标准流程:
1. 安装客户端
2. 用户输入或绑定 employee api_key
3. Main 进程调用 openclaw-employee-config,action=init
4. 从返回的 skills[].skill.download_url 中识别 zip bundle
5. ProjectBundleService 下载并安装所有合法项目包
6. ProjectStoreService 建立本地 project inventory、active project、默认 session
7. 专家页显示抖音 / 小红书项目
8. 用户进入对应专家页发自然语言请求
9. 请求优先走该项目 workspace-entry
10. 项目在本地隔离工作区中执行
要把这条链在新电脑上稳定跑起来,还要补这些交付前约束:
- bundled runtime 必须覆盖项目依赖
- 如果抖音/小红书项目需要新 Python 依赖,不能只改 zip,必须同步扩展客户端 runtime
- 项目首次运行所需浏览器态、登录态、调试输出、缓存全部写入 memory/
- bundle 升级时保留 sessions.json、session-messages/、memory/
## Public Interfaces
需要锁定的公开契约只有这几项:
### 1. 云端返回契约
继续使用现有 openclaw-employee-config:
- skills[].skill.file_name
- skills[].skill.file_size
- skills[].skill.download_url
不新增 projects[]
### 2. 项目包契约
每个 zip 必须包含 project.json,至少包含:
- id
- name
- platform
- projectType
- description
- defaultEntry
- entries
- boundSkillIds
- ready
其中:
- defaultEntry.type = "workspace-entry"
- entries 至少包含一条 workspace-entry
### 3. 项目技能契约
skills/<skillId>/SKILL.md 继续只承担 skill 级职责:
- 展示名
- 描述
- 元数据
- skill 路由辅助信息
不承担项目级默认入口声明,不承担 project identity。
## Test Plan
必须覆盖这些场景,才能算“新电脑初始化后可插拔可运行”:
1. openclaw-employee-config 同时下发 douyin.zip 和 xhs.zip,客户端成功识别两个 bundle。
2. 两个 zip 都含标准 project.json 时,客户端在本地生成两个独立 project。
3. 专家页显示两个项目,切换项目只影响当前 active project,不串会话。
4. 进入 douyin 项目后,只显示 douyin 的 boundSkillIds,不混入 xhs skills,也不混入 curated generic skills。
5. 进入 xhs 项目后,只显示 xhs 的 boundSkillIds。
6. 在 douyin 项目内发送“生成一个苹果抖音视频文案”,默认命中 workspace-entry。
7. 在 xhs 项目内发送“小红书笔记文案”,默认命中 workspace-entry。
8. zip 缺少 project.json 时,bundle sync 明确报错,专家页不显示为 ready。
9. boundSkillIds 写错时,客户端记录诊断并标记项目异常,不崩溃。
10. bundle 更新后,项目目录被替换,但 memory/、sessions.json、session-messages/ 保留。
11. 云端配置移除 douyin.zip 时,本地 douyin project 和共享资源被清理,不影响 xhs。
12. fresh install + 真正 employee key + 真正云端接口下,初始化完成后专家页可直接使用项目。
## Assumptions
- 后续抖音和小红书 zip 都保持与你当前抖音包相同的根目录形态。
- 两个项目都按“一包一个项目”交付,不把多个项目塞进一个 zip。
- 你接受“项目识别靠 project.json,项目内能力靠 skills/*/SKILL.md”这套双层契约。
- 这次不做“客户端扫描全部 skills 自动反推项目”的重构。
- 若项目依赖超出当前 bundled runtime,runtime 扩容是单独任务,不纳入 zip 插件化本身
\ No newline at end of file
# Project Isolation Execution Design
Date: 2026-04-01
Status: Core execution chain, cloud-owned inventory, bundle freshness hardening, replacement/rollback hardening, broader churn coverage, and lifecycle smoke are implemented
## 1. Scope and product constraints
This document describes the current desktop implementation for project-isolated execution.
Confirmed product constraints:
- Project inventory is fixed by backend configuration.
- Projects currently come from `openclaw-employee-config`.
- The active bundle source is `skills[].skill.download_url`.
- `project_bundle_url` is not part of the current implementation.
- Local project CRUD is not part of the target product shape.
- The current backend contract returns a `skills` array.
- Each `skills[]` object currently treats `skill.download_url` as one project zip URL.
- Multiple projects are represented by multiple `skills[]` objects.
- Any future multi-value download URL contract must be handled as a separate compatibility change.
## 2. Implemented architecture
The implemented chain is:
1. Runtime cloud bootstrap or payload sync triggers project bundle sync.
2. Remote zip bundles are discovered from `skills[].skill.download_url`, downloaded, and extracted.
3. Bundle content is materialized into local project roots plus shared `skills/` and `cron/` content.
4. Materialized projects are registered in local project metadata.
5. Empty inventory does not recreate a local fallback project; chat session creation waits for the first cloud-backed project.
6. Sessions are bound to projects through `ProjectStoreService`.
7. `ProjectContextService` builds and caches project snapshots from `SOUL.md`, `USER.md`, `README.md`, and tracked memory files.
8. `ProjectExecutionRouter` routes each request to one of:
- `workspace-entry`
- `skill`
- `chat-fallback`
9. `ipc.ts` prepares each request against the session's project snapshot before execution.
10. Bundle reconciliation removes stale bundle-managed projects, shared `skills/` entries, shared `cron/` entries, and stale manifest records when the expected cloud bundle set changes.
11. Bundle freshness probes the remote bundle with HTTP metadata before deciding whether an existing local materialization can be reused.
12. Same-`projectId` replacement now stages project/shared assets, commits them in order, and either finalizes or rolls back the whole replacement set.
13. Broader churn coverage now verifies survivor, replacement, removal, and addition flows across multiple projects without changing the current cloud protocol.
## 3. Code-truth execution behavior
Current behavior verified from code:
- `workspace-entry` executes inside the isolated project root.
- `skill` routing uses project skill binding and runtime skill bridge prompt preparation.
- `chat-fallback` also receives injected project context and stays project-bound.
- Session to project binding is real.
- Session to snapshot binding is real.
- Project context injection is real.
- Cloud zip sync is real.
- Empty inventory semantics are real.
- Stale bundle reconciliation is real for bundle-managed project and shared asset cleanup.
- Bundle freshness hardening is real.
- Replacement / rollback hardening is real for same-project bundle updates.
Default chat refresh semantics are explicit in code:
- `apps/desktop/src/main/services/project-context-lifecycle.ts` defines shared post-execution refresh behavior.
- `chat-fallback`, `skill`, and `workspace-entry` all schedule post-execution project context refresh.
- The refresh is asynchronous and does not block reply delivery.
- When refresh succeeds, `session.contextSnapshotId` is rebound to the refreshed snapshot.
- The next request reuses the refreshed snapshot instead of discovering it lazily.
Bundle freshness semantics are explicit in code:
- Reuse is no longer based only on `sourceUrl + configVersion`.
- If a manifest record is reusable, the app sends a `HEAD` probe before skipping reinstall.
- The probe records `ETag`, `Last-Modified`, and `Content-Length` when available.
- If remote freshness identity changes, the bundle is re-downloaded and re-materialized.
- If an older manifest has no freshness identity but the remote now exposes one, one re-download is forced to establish the baseline.
Bundle replacement semantics are explicit in code:
- bundle install follows `stage -> commit -> finalize/rollback`
- project root and shared `skills/` / `cron/` content are treated as one replacement transaction
- if metadata sync fails after commit, the previous project root and shared assets are restored
- the next sync can recover cleanly and persist the new manifest ownership
## 4. Verified smoke coverage
Current smoke coverage in the repo:
- `workspace-entry-smoke.ps1`
- validates workspace-entry execution inside an isolated project
- `cloud-bundle-smoke.ps1`
- validates cloud zip to bundle sync to active project to workspace-entry
- covers payload `sync`
- covers cached `init`
- covers same-`projectId` replacement through the Electron UI/main flow
- `project-context-refresh-smoke.ps1`
- validates snapshot cache, invalidation, refresh, and session snapshot rebinding
- `default-chat-smoke.ps1`
- validates `chat-fallback` routing
- validates project context injection into the prepared prompt
- validates post-execution snapshot refresh
- validates session snapshot rebinding
- validates reuse of the refreshed snapshot on the next request
- `project-empty-inventory-smoke.ps1`
- validates no-project inventory stays empty
- validates session listing returns `[]`
- validates session creation is blocked until cloud bundle sync completes
- `project-bundle-reconcile-smoke.ps1`
- validates stale bundle project removal
- validates shared `skills/` cleanup
- validates shared `cron/` cleanup
- validates manifest pruning
- validates active-project fallback after cleanup
- `project-bundle-freshness-smoke.ps1`
- validates re-download when `ETag` / `Last-Modified` change at the same download URL
- validates persisted freshness metadata in bundle manifest
- `project-bundle-replacement-smoke.ps1`
- validates same-project replacement at the service layer
- validates rollback after injected post-commit failure
- validates successful recovery on the next sync
- `project-bundle-churn-smoke.ps1`
- validates multi-project survivor behavior
- validates same-project replacement during broader churn
- validates project removal and project addition in the same sync window
- validates active-project fallback when the active project is removed
- validates session survival inside unaffected projects
- `project-isolation-smoke.ps1`
- aggregates the main project-isolation regression smokes
- now includes reconcile, freshness, replacement, and churn service-level coverage
Electron smoke validation is also stronger now:
- workspace-entry validation no longer assumes the final status label must contain `workspace`
- smoke state records status label history and validates that a workspace-agent phase actually occurred
- smoke action selection now follows the active project session instead of assuming a stable pre-replacement session id
## 5. What is still incomplete
The earlier core gaps are closed.
The main follow-up areas now are:
- deciding whether the `project-isolation-smoke.ps1` and `cloud-bundle-smoke.ps1` gate pair should be wired into broader external CI / release coverage
- expanding deterministic churn coverage into larger or randomized stress matrices only when product changes raise the risk level
- keeping related product and rollout docs aligned with the implementation
- implementing any future protocol migration away from single `skills[].skill.download_url` only when backend is ready
## 6. Recommended next implementation order
1. Keep the current project-isolation smoke set green.
2. Use `project-isolation-smoke.ps1` plus `cloud-bundle-smoke.ps1` as the default higher-level regression gate pair.
3. Expand breadth only when new isolation surfaces or larger multi-project behaviors are introduced.
4. Handle any future `download_url` protocol evolution as a dedicated compatibility task.
## 7. Bottom line
Current status:
- The core project isolation chain is implemented.
- Session and project binding are implemented.
- Cloud-owned inventory enforcement is implemented.
- Project context injection is implemented.
- Cloud zip sync is implemented.
- Default chat refresh is aligned with other project-aware execution paths.
- Stale bundle reconciliation is implemented for bundle-managed cleanup.
- Bundle freshness hardening is implemented and smoke-verified.
- Replacement / rollback hardening is implemented and smoke-verified.
- Multi-project churn coverage is implemented at the service level.
- Remaining work is mostly broader regression breadth, gate wiring, and future protocol evolution.
# Cloud Skill Sync And Execution Flow
Updated: 2026-03-26
## 1. Purpose
This document describes the current real implementation of the cloud skill chain in QianjiangClaw.
It covers:
- how desktop pulls skills from cloud
- where skills are stored locally
- how UI reads skill state
- how chat uses a selected skill
- what runtime mode is required
- what is currently implemented vs. what is still a limitation
This document should be treated as the source of truth for the current code path, not the older planning docs.
## 2. High-Level Summary
Current behavior is:
1. Desktop starts.
2. Electron Main fetches `openclaw-employee-config` from cloud.
3. Returned `skills` are downloaded to local disk under `userData/skills`.
4. Main process writes a local `manifest.json`.
5. UI reads skill availability from that local manifest-derived summary.
6. When the user selects a skill in chat, Main resolves the local file by `skillId`.
7. Main materializes that skill into the OpenClaw runtime's actual skill directory as a temporary `qjclaw-cloud-*` skill.
8. Main rewrites the outgoing prompt so OpenClaw is instructed to use that installed local skill.
9. OpenClaw runtime discovers and reads the materialized local skill and executes it.
Important:
- Cloud skill execution is currently supported only in `bundled-runtime` mode.
- `external-gateway` mode cannot safely assume access to the same local disk, so Main rejects local skill execution in that mode.
## 3. Main Components
### 3.1 Cloud config client
File:
- `apps/desktop/src/main/services/cloud-api.ts`
Responsibilities:
- fetch `openclaw-employee-config`
- validate payload
- keep runtime cloud status cache
- expose remote skill asset metadata
- notify listeners when payload changes
Relevant behavior:
- startup fetch uses `init`
- background sync uses `sync`
- payload updates trigger downstream local skill reconcile
### 3.2 Local skill store
File:
- `apps/desktop/src/main/services/skill-store.ts`
Responsibilities:
- persist downloaded cloud skills under local user data
- maintain `manifest.json`
- expose workspace skill summaries to UI
- expose execution target lookup by `skillId`
Local storage layout:
- `userData/skills/manifest.json`
- `userData/skills/<skillId>/<file_name>`
Manifest tracks:
- `skillId`
- `bindingId`
- `name`
- `description`
- `category`
- `fileName`
- `fileSize`
- `downloadUrl`
- `localPath`
- `downloadState`
- `lastSyncedAt`
- `lastDownloadedAt`
- `lastError`
- `remoteConfigVersion`
Current states:
- `pending`
- `downloading`
- `ready`
- `failed`
- `removed`
### 3.3 Runtime skill bridge
File:
- `apps/desktop/src/main/services/runtime-skill-bridge.ts`
Responsibilities:
- resolve the local downloaded skill file selected by the user
- materialize it as an OpenClaw-readable runtime skill
- assign a runtime skill name like `qjclaw-cloud-*`
- rewrite `SKILL.md` frontmatter `name:` to match that runtime skill name
- clean up old temporary cloud skills
Important implementation detail:
The runtime bridge does not execute directly from `userData/skills/...`.
Instead it:
1. reads the downloaded local skill file from `userData/skills/...`
2. finds the real OpenClaw skill root by reading `vendor/openclaw-runtime/runtime-manifest.json`
3. resolves `sourceOpenClawEntry`
4. writes the temporary runtime skill into the real OpenClaw skill directory next to that entry
This is required because current OpenClaw runtime resolves skills from its own actual installed skill directory, not from `OPENCLAW_HOME/skills`.
### 3.4 IPC chat integration
File:
- `apps/desktop/src/main/ipc.ts`
Responsibilities:
- build workspace summary for renderer
- resolve execution policy
- prepare prompt for gateway
- clear temporary runtime skills when no skill is selected
- reject local skill execution in `external-gateway`
Prompt flow:
- no skill selected:
- clear temporary `qjclaw-cloud-*` skills
- send original prompt
- skill selected:
- ensure runtime mode is `bundled-runtime`
- resolve local downloaded skill
- materialize runtime skill
- rewrite prompt to explicitly instruct OpenClaw to use the installed runtime skill
- send rewritten prompt to gateway
## 4. End-To-End Data Flow
### 4.1 Sync flow
1. App bootstraps Main services in `apps/desktop/src/main/index.ts`.
2. `OpenClawConfigClient.fetchConfig("init")` fetches runtime cloud config.
3. `runtimeCloudClient.onPayloadUpdated(...)` fires.
4. `skillStore.reconcile(skills, configVersion)` compares remote skills and local manifest.
5. New or changed skills are downloaded to `userData/skills/...`.
6. `manifest.json` is rewritten.
7. Renderer reads `workspace.getSummary()` and `skills.list()` from manifest-derived summaries.
### 4.2 Chat execution flow
1. Renderer sends `chat.streamPrompt(sessionId, prompt, skillId?)`.
2. Main checks `skillId`.
3. If present:
- find local skill file via `skillStore.getExecutionTarget(skillId)`
- materialize it into OpenClaw skill root as `qjclaw-cloud-*`
- rewrite prompt to require that installed skill
4. Gateway receives the rewritten prompt.
5. OpenClaw runtime reads the installed local skill file and executes its own skill logic.
## 5. Timing And Sync Policy
Current actual sync policy:
- app startup: one `init` fetch
- runtime running in `bundled-runtime` mode: periodic `sync`
- current default background config sync interval: 4 hours
Implementation:
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
Current default:
- `DEFAULT_CONFIG_SYNC_INTERVAL_MS = 4 * 60 * 60 * 1000`
Notes:
- this is not a daily 6:00 task
- if the app is not running, no sync runs
- the interval can still be overridden by environment variable:
- `QJCLAW_RUNTIME_CLOUD_CONFIG_SYNC_INTERVAL_MS`
## 6. UI Behavior
File:
- `apps/ui/src/App.tsx`
Current renderer behavior:
- skill list comes from workspace summary
- only `ready === true` skills are selectable in chat
- skill page can show non-ready states
- default chat means no cloud skill selected
This means UI is already driven by local readiness, not by raw cloud payload.
## 7. Runtime Modes
### 7.1 Bundled runtime
Supported for cloud skill execution.
Reason:
- Electron Main, local downloaded files, and OpenClaw runtime are all on the same machine
- Main can materialize the runtime skill into the actual OpenClaw skill root
### 7.2 External gateway
Not supported for local cloud skill execution.
Reason:
- external gateway may be remote or otherwise not share the same local disk
- Main cannot guarantee the external gateway can read the local skill file
Current behavior:
- Main throws an explicit error when a selected cloud skill is used under `external-gateway`
## 8. Cleanup Behavior
Current cleanup logic exists to avoid stale temporary runtime skills:
- startup cleanup
- no-skill-selected cleanup
- app before-quit cleanup
What gets cleaned:
- only temporary `qjclaw-cloud-*` skill directories
What does not get cleaned:
- downloaded source files under `userData/skills/...`
- manifest entries
This separation is intentional:
- `userData/skills/...` is the persistent cache
- `qjclaw-cloud-*` runtime skills are ephemeral execution projections
## 9. Real Verified Behavior
Verified with a real employee `api_key`:
- cloud config fetch succeeded
- remote skills were downloaded to local disk
- manifest was updated correctly
- selected cloud skill was materialized into OpenClaw's real skill root
- OpenClaw could discover and read the materialized skill
- renderer streaming worked for at least one real skill end-to-end
Observed limitation from real testing:
- some cloud skills have external prerequisites of their own
- example: `tmux`, `ralphy`, ACP runtime
- example: browser automation and login requirements
- in those cases the chain from desktop to runtime skill is working, but the skill's own runtime workflow may still fail or hang based on environment
This is a skill-content/runtime-environment problem, not a cloud-download-chain problem.
## 10. Current Limitations
### 10.1 Prompt rewrite instead of protocol-level skill execution
Current implementation does not send a dedicated `skillPath` or `skillId` execution contract through gateway protocol.
Instead it relies on:
- materializing a runtime skill locally
- rewriting prompt to instruct OpenClaw to use it
This works today, but it is not the cleanest long-term contract.
### 10.2 OpenClaw skill discovery path is external to QianjiangClaw-managed userData
Current runtime bridge has to project skills into the OpenClaw skill root derived from `runtime-manifest.json`.
That means:
- execution currently depends on how bundled OpenClaw resolves its skill directory
- changes in upstream OpenClaw skill resolution may require bridge updates
### 10.3 Skill prerequisites are not preflight-validated
Current desktop chain does not yet perform a formal preflight for skill-specific dependencies.
Examples:
- `tmux`
- `ralphy`
- ACP runtime backend
- browser automation login state
Recommended future improvement:
- add preflight validation and fast-fail reporting for skills with known dependencies
## 11. Recommended Future Work
### 11.1 Add protocol-level skill execution contract
Possible direction:
- extend gateway/chat protocol with explicit selected skill metadata
- allow runtime to resolve local skill execution through a structured contract instead of prompt rewriting
### 11.2 Add skill preflight checks
For example:
- dependency availability
- browser automation availability
- login-required capabilities
- ACP runtime availability
### 11.3 Add dedicated smoke coverage
Recommended smoke layers:
- cloud config fetch smoke
- local download + manifest smoke
- runtime materialization smoke
- end-to-end execution smoke for a simple non-interactive skill
## 12. Key Files
Core files for future development:
- `apps/desktop/src/main/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/skill-store.ts`
- `apps/desktop/src/main/services/skill-client.ts`
- `apps/desktop/src/main/services/runtime-skill-bridge.ts`
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
- `apps/ui/src/App.tsx`
- `packages/shared-types/src/index.ts`
## 13. One-Sentence Mental Model
Cloud returns skill metadata and download URLs, desktop downloads the skill to local cache, chat selection causes Main to project that cached file into OpenClaw's real skill directory, and bundled runtime then executes that local projected skill.
# 云端 Skill 同步与执行链路
更新日期:2026-03-26
## 1. 目的
本文档说明 QianjiangClaw 当前已经落地的云端 skill 链路实现,包括:
- desktop 如何从云端拉取 skill
- skill 下载后存放在本地哪里
- UI 如何读取 skill 状态
- 聊天里选中 skill 后如何执行
- 依赖什么 runtime 模式
- 当前已经实现了什么,哪些仍然是限制
这份文档描述的是当前真实代码路径,应作为现阶段实现的基准,而不是早期规划文档。
## 2. 高层概览
当前实际行为如下:
1. Desktop 启动。
2. Electron Main 从云端拉取 `openclaw-employee-config`
3. 返回的 `skills` 被下载到本地磁盘 `userData/skills`
4. Main 进程写入本地 `manifest.json`
5. UI 从这份本地 manifest 派生出的摘要里读取 skill 可用状态。
6. 用户在聊天里选中某个 skill 后,Main 按 `skillId` 找到本地文件。
7. Main 把这个 skill 投影到 OpenClaw runtime 实际使用的 skill 目录,生成一个临时的 `qjclaw-cloud-*` skill。
8. Main 改写即将发送出去的 prompt,明确要求 OpenClaw 使用这个已经安装到本地的 runtime skill。
9. OpenClaw runtime 发现并读取这个本地投影出来的 skill,然后执行它。
重要说明:
- 当前云端 skill 执行只支持 `bundled-runtime` 模式。
- `external-gateway` 模式无法安全假设能访问同一块本地磁盘,所以 Main 会直接拒绝这种本地 skill 执行。
## 3. 主要组件
### 3.1 云端配置客户端
文件:
- `apps/desktop/src/main/services/cloud-api.ts`
职责:
- 拉取 `openclaw-employee-config`
- 校验返回 payload
- 维护 runtime cloud 状态缓存
- 暴露远端 skill 资源元数据
- 在 payload 变化时通知下游
相关行为:
- 启动拉取使用 `init`
- 后台同步使用 `sync`
- payload 更新后会触发下游本地 skill reconcile
### 3.2 本地 skill 存储层
文件:
- `apps/desktop/src/main/services/skill-store.ts`
职责:
- 将下载后的云端 skill 持久化到本地 user data 目录
- 维护 `manifest.json`
- 给 UI 暴露 workspace skill 摘要
-`skillId` 暴露执行目标查询
本地存储结构:
- `userData/skills/manifest.json`
- `userData/skills/<skillId>/<file_name>`
manifest 当前记录这些字段:
- `skillId`
- `bindingId`
- `name`
- `description`
- `category`
- `fileName`
- `fileSize`
- `downloadUrl`
- `localPath`
- `downloadState`
- `lastSyncedAt`
- `lastDownloadedAt`
- `lastError`
- `remoteConfigVersion`
当前状态值:
- `pending`
- `downloading`
- `ready`
- `failed`
- `removed`
### 3.3 Runtime skill bridge
文件:
- `apps/desktop/src/main/services/runtime-skill-bridge.ts`
职责:
- 解析用户选中的本地已下载 skill 文件
- 把它投影成 OpenClaw 可读取的 runtime skill
- 分配一个类似 `qjclaw-cloud-*` 的 runtime skill 名
- 改写 `SKILL.md` frontmatter 里的 `name:`,保证与 runtime skill 名一致
- 清理旧的临时云端 skill
重要实现细节:
runtime bridge 并不是直接从 `userData/skills/...` 执行。
它实际会:
1.`userData/skills/...` 读取已经下载好的本地 skill 文件。
2. 读取 `vendor/openclaw-runtime/runtime-manifest.json`,找到真正的 OpenClaw skill 根目录。
3. 解析 `sourceOpenClawEntry`
4. 把临时 runtime skill 写入这个真实 OpenClaw skill 目录旁边的 `skills` 目录里。
之所以这样做,是因为当前 OpenClaw runtime 会从它自己实际安装的 skill 目录发现 skill,而不是从 `OPENCLAW_HOME/skills` 读取。
### 3.4 IPC 聊天集成
文件:
- `apps/desktop/src/main/ipc.ts`
职责:
- 为 renderer 构建 workspace 摘要
- 解析执行策略
- 为 gateway 准备 prompt
- 在未选 skill 时清理临时 runtime skills
-`external-gateway` 下拒绝本地 skill 执行
prompt 流程:
- 未选 skill:
- 清理临时 `qjclaw-cloud-*` skills
- 发送原始 prompt
- 选中 skill:
- 确认当前 runtime mode 是 `bundled-runtime`
- 解析本地已下载 skill
- 投影 runtime skill
- 改写 prompt,明确要求 OpenClaw 使用这个已安装的 runtime skill
- 把改写后的 prompt 发给 gateway
## 4. 端到端数据流
### 4.1 同步链路
1. 应用在 `apps/desktop/src/main/index.ts` 中启动 Main 服务。
2. `OpenClawConfigClient.fetchConfig("init")` 拉取 runtime cloud 配置。
3. `runtimeCloudClient.onPayloadUpdated(...)` 被触发。
4. `skillStore.reconcile(skills, configVersion)` 对比远端 skills 和本地 manifest。
5. 新增或变更的 skill 被下载到 `userData/skills/...`
6. `manifest.json` 被重写。
7. Renderer 通过 `workspace.getSummary()``skills.list()` 读取基于 manifest 派生出的摘要。
### 4.2 聊天执行链路
1. Renderer 发送聊天消息和选中的 `skillId`
2. Main 检查 `skillId`
3. 如果存在:
- 通过 `skillStore.getExecutionTarget(skillId)` 找到本地 skill 文件
- 把它投影到 OpenClaw skill 根目录,生成 `qjclaw-cloud-*`
- 改写 prompt,要求必须使用这个已安装 skill
4. Gateway 收到改写后的 prompt。
5. OpenClaw runtime 读取这个已安装到本地的 skill 文件,并执行它自己的 skill 逻辑。
## 5. 时间与同步策略
当前真实同步策略:
- 应用启动时执行一次 `init`
- runtime 在 `bundled-runtime` 模式运行时周期性执行 `sync`
- 当前默认后台配置同步间隔是 4 小时
实现位置:
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
当前默认值:
- `DEFAULT_CONFIG_SYNC_INTERVAL_MS = 4 * 60 * 60 * 1000`
说明:
- 这不是每天早上 6 点的任务
- 如果应用没有运行,就不会发生同步
- 仍然可以通过环境变量覆盖这个间隔:
- `QJCLAW_RUNTIME_CLOUD_CONFIG_SYNC_INTERVAL_MS`
## 6. UI 行为
文件:
- `apps/ui/src/App.tsx`
当前 renderer 行为:
- skill 列表来自 workspace summary
- 只有 `ready === true` 的 skill 能在聊天里被选中
- skill 页面可以展示非 ready 状态
- 默认对话表示没有选中任何云端 skill
这意味着 UI 现在已经由本地可用状态驱动,而不是直接由原始云端 payload 驱动。
## 7. Runtime 模式
### 7.1 Bundled runtime
支持云端 skill 执行。
原因:
- Electron Main、本地下载文件和 OpenClaw runtime 都在同一台机器上
- Main 可以把 runtime skill 投影到 OpenClaw 实际 skill 根目录
### 7.2 External gateway
不支持本地云端 skill 执行。
原因:
- external gateway 可能是远程的,或者与当前机器不共享同一块本地磁盘
- Main 无法保证 external gateway 能读取本地 skill 文件
当前行为:
-`external-gateway` 下,如果用户选中了云端 skill,Main 会直接抛出明确错误
## 8. 清理行为
当前已经有清理逻辑,用来避免临时 runtime skills 残留:
- 启动时清理
- 未选 skill 时清理
- 应用退出前清理
清理目标:
- 仅清理临时的 `qjclaw-cloud-*` skill 目录
不会清理的内容:
- `userData/skills/...` 下的已下载源文件
- manifest 中的记录
这是刻意区分的:
- `userData/skills/...` 是持久化缓存
- `qjclaw-cloud-*` runtime skill 是一次执行期的临时投影
## 9. 真实验证过的行为
已经使用真实员工 `api_key` 做过验证:
- 云端配置拉取成功
- 远端 skills 成功下载到本地磁盘
- manifest 正确更新
- 选中的云端 skill 能被投影到 OpenClaw 真正的 skill 根目录
- OpenClaw 能发现并读取这个投影后的 skill
- 对至少一个真实 skill,renderer 流式链路已经端到端跑通
真实测试中观察到的限制:
- 某些云端 skill 自身还依赖外部前置条件
- 例如:`tmux``ralphy`、ACP runtime
- 例如:浏览器自动化和登录状态
- 这种情况下,desktop 到 runtime skill 的链路本身是通的,但 skill 自己的运行流程仍可能因为环境问题而失败或卡住
这属于 skill 内容或 runtime 环境问题,不属于云端下载链路问题。
## 10. 当前限制
### 10.1 现在是 prompt 改写,不是协议级 skill 执行
当前实现没有在 gateway 协议里显式传一个专门的 `skillPath``skillId` 执行契约。
现在依赖的是:
- 先把 runtime skill 投影到本地
- 再通过 prompt 改写,要求 OpenClaw 去使用它
这在当前版本能工作,但不是长期来看最干净的执行契约。
### 10.2 OpenClaw 的 skill 发现路径不在 QianjiangClaw 自己的 userData 里
当前 runtime bridge 需要根据 `runtime-manifest.json`,把 skill 投影到 OpenClaw 自己真正使用的 skill 根目录里。
这意味着:
- 当前执行链依赖 bundled OpenClaw 如何解析 skill 目录
- 如果上游 OpenClaw 后续修改了 skill 发现逻辑,这里的 bridge 可能也要跟着调整
### 10.3 还没有做 skill 前置依赖预检
当前 desktop 链路还没有正式的 skill 依赖预检。
例如:
- `tmux`
- `ralphy`
- ACP runtime backend
- 浏览器自动化登录状态
建议后续补充:
- 对已知依赖做 preflight 校验
- 缺依赖时快速失败并明确提示
## 11. 建议的后续工作
### 11.1 增加协议级 skill 执行契约
可能方向:
- 在 gateway 或 chat 协议中增加结构化的 selected skill 元数据
- 让 runtime 通过正式结构化契约解析本地 skill 执行,而不是继续依赖 prompt 改写
### 11.2 增加 skill 前置检查
例如:
- 依赖工具可用性
- 浏览器自动化可用性
- 需要登录的能力是否就绪
- ACP runtime 是否可用
### 11.3 增加专门的 smoke 覆盖
建议拆成几层:
- cloud config fetch smoke
- 本地下载与 manifest smoke
- runtime materialization smoke
- 针对简单无交互 skill 的端到端执行 smoke
## 12. 关键文件
后续开发重点关注这些文件:
- `apps/desktop/src/main/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/skill-store.ts`
- `apps/desktop/src/main/services/skill-client.ts`
- `apps/desktop/src/main/services/runtime-skill-bridge.ts`
- `apps/desktop/src/main/services/runtime-cloud-supervisor.ts`
- `apps/ui/src/App.tsx`
- `packages/shared-types/src/index.ts`
## 13. 一句话理解
云端返回 skill 元数据和下载地址,desktop 把 skill 下载到本地缓存,聊天选中 skill 后,Main 再把这份本地缓存投影到 OpenClaw 真正的 skill 目录里,最后由 bundled runtime 执行这个本地投影出来的 skill。
# Context
当前桌面端实际上有两条**不能混用**的配置链路:
1. **项目 zip / 项目侧链路**:客户端会用员工密钥请求 `openclaw-employee-config`。这条链路服务的是项目包 / 项目运行时相关配置,不是客户端 model 设置来源。
2. **客户端设置链路**:客户端自己的 model 配置必须在设置页里单独绑定与保存。
`workspace` 下的小红书 / 抖音项目仍有多处模型写死:文案、生图、生视频分别在各自脚本里使用默认模型或启发式回退;同时客户端设置页虽然已经有部分 expert 配置能力,但没有完整的“客户端模型配置 UI / 消费链路”。因此现在的问题不是简单复用 employee config,而是要把**客户端设置****项目模型消费方**真正打通。
目标是把客户端设置页作为唯一入口:
- 客户端 model 配置在设置页绑定、保存、回读
- 主进程负责把这份客户端设置下发到项目执行链路
- `workspace` 只消费客户端下发的解析结果,不再各自维护散落的硬编码默认模型
# Recommended approach
## 1. 以现有 `expertModelConfig` 为中心扩展,不新建第二套模型配置对象
优先复用现有设置保存链路,而不是再加一个独立的 `clientModelConfig`
- 现有结构已经覆盖三类能力:`copywriting` / `image` / `video`
- UI、IPC、配置持久化已经能保存这三类 expert 配置
- 只需在每一项上补充 `modelId`(必要时补 `modelDisplayName` 仅用于展示)
建议目标结构:
- `expertModelConfig.copywriting = { baseUrl, apiKey, modelId }`
- `expertModelConfig.image = { baseUrl, apiKey, modelId }`
- `expertModelConfig.video = { baseUrl, apiKey, modelId }`
员工密钥仍保留在顶层 `apiKey`,但它只用于项目 zip / 项目侧链路,不作为客户端 model 配置来源。
## 2. 主进程统一做“客户端设置 -> 项目执行”的模型归一化
由桌面端主进程统一决定最终生效配置,优先级建议统一为:
1. 用户在设置页显式填写的 `modelId`
2. 对应 capability 的本地 `baseUrl` / `apiKey`
3. 现有脚本里的历史回退值(过渡期保留)
这里**不再把 `openclaw-employee-config` 当成客户端 model 默认值来源**。它继续服务项目 zip / 项目侧配置,但客户端模型选择完全来自设置页。
主进程需要输出一份**归一化后的运行时模型配置**,再注入到项目执行链路。不要让 `workspace` 内各脚本继续各自解析一套默认值。
## 3. `workspace` 只消费归一化结果,先兼容旧环境变量,再逐步去硬编码
XHS 和 Douyin 都先改成“优先读主进程传入配置,其次兼容现有 env,最后才走历史默认值”。这样可以分阶段上线,不需要一次性把所有旧分支删掉。
## 4. 首期范围只统一“模型选择”,不扩大到所有生成参数
首期重点打通:
- 客户端聊天 / 文案模型
- 生图模型
- 生视频模型
`IMAGE_SIZE``IMAGE_COUNT`、风格 prompt、Seedance 分辨率/比例、OmniHuman TTS voice 这类“生成参数默认值”暂不并入设置页,避免范围膨胀。它们可以先维持原样,等模型配置链路稳定后再考虑二期治理。
# Files to modify
## Desktop shared types / UI / main process
### `D:\qjclaw\packages\shared-types\src\index.ts`
- 扩展 `AppConfig` / `SaveConfigInput``expertModelConfig` 的类型定义,给 image / video / copywriting 增加 `modelId`
- 确认 `WorkspaceSummary` / 其他摘要结构是否需要暴露 capability 级别模型信息给 UI
### `D:\qjclaw\apps\ui\src\App.tsx`
- 设置页已有保存 `expertModelConfig` 的状态与 `saveConfig()` 链路,需补三类 `modelId` 的 UI 输入与本地 state
- 现有 `saveConfig()` 已会提交 `expertModelConfig`,应在此基础上把 `modelId` 一并提交
- 当前没有“客户端模型配置 UI”,这里是主要补齐点
### `D:\qjclaw\apps\desktop\src\main\ipc.ts`
- 确认 `config.save``workspace.getSummary`、项目执行请求构建处是否需要增加 capability 级模型透传
- 将主进程解析后的模型配置送入项目执行链路,而不是只用于聊天模型
### `D:\qjclaw\apps\desktop\src\main\services\cloud-api.ts`
- 继续保留 `openclaw-employee-config` 的现有职责,但不要把它作为客户端 model 默认值来源
- 仅检查是否有现有逻辑误把 employee config 的 `model_id` 当成客户端聊天模型默认值;如果有,需要与新的本地设置优先级重新梳理
## Workspace — XHS
### `D:\qjclaw\workspace\xhs\scripts\qwen.py`
当前热点:
- `ARK_TEXT_MODEL` 最终回退到 `qwen-plus`
- `ARK_VISION_MODEL` 与 provider 选择依赖启发式逻辑
需要改为:
- 先读归一化后的 copywriting / vision 配置
- 再兼容现有 `ARK_*` / `QWEN_*`
- 最后才回退历史默认值
### `D:\qjclaw\workspace\xhs\scripts\run.py`
当前热点:
- `XHS_IMAGE_MODEL` / provider 选择启发式
- `_candidate_image_provider_names()` / `_pick_image_model_id()` / `IMAGE_MODEL`
需要改为:
- 生图优先读主进程解析后的 image `modelId`
- 减少按 provider 名称、模型名猜测能力的逻辑作为主路径
### `D:\qjclaw\workspace\xhs\index.ts`
- 如果工具描述里仍写死固定模型品牌,需要同步去掉,以免 UI/文案与真实配置不一致
## Workspace — Douyin
### `D:\qjclaw\workspace\douyin\skills\douyin-master\scripts\coordinator.py`
当前热点:
- `writer_model: "qwen3-plus"`
- CLI 默认又是 `qwen3.5-plus`
- 统一编排入口却仍注入写死默认值
需要改为:
- coordinator 统一从客户端下发的归一化配置读取 copywriting / image / video 默认模型
- CLI 参数保留最高优先级覆盖
### `D:\qjclaw\workspace\douyin\skills\douyin-writer\scripts\generator.py`
当前热点:
- `llm_model="qwen3.5-plus"`
- `--llm-model` 默认值仍是硬编码
需要改为:
- 优先消费主进程注入配置
- 保留 CLI / env 兼容
- 最后才落到历史默认值
### `D:\qjclaw\workspace\douyin\skills\douyin-seedance-split\scripts\run_pipeline.py`
当前热点:
- `--llm-model default="qwen3-plus"`
- 与 coordinator / writer 存在重复默认值
需要改为:
- 统一走与 coordinator 相同的配置优先级
- 不再维护第二套 copywriting / image / video 默认模型
### `D:\qjclaw\workspace\douyin\skills\seedream-image-generator\scripts\run_seedream.py`
当前热点:
- `doubao-seedream-4-0-250828` 写死回退
需要改为:
- 优先读客户端设置中的 image `modelId`
- 再兼容 `SEEDREAM_MODEL`
- 最后才保留历史默认值兜底
### `D:\qjclaw\workspace\douyin\skills\seedance-2-0-video-generator\scripts\run_seedance.py`
当前热点:
- `doubao-seedance-2-0-260128` 写死回退
需要改为:
- 优先读客户端设置中的 video `modelId`
- 再兼容 `SEEDANCE_MODEL`
- 最后才保留历史默认值兜底
# Scope and impact
## 明确会动到的范围
1. **桌面端设置模型**
- shared types
- settings UI
- config save/load
- 主进程模型归一化逻辑
2. **客户端到项目的配置传递链路**
- config save -> main process -> project execution handoff
- env 注入 / runtime handoff
- 项目执行入口
3. **项目侧消费方**
- XHS 文案 / 视觉 / 生图
- Douyin 文案 / 生图 / 生视频
## 暂不建议纳入首期的范围
- employee config 接口协议扩展
- 生图尺寸、张数、风格词等生成参数
- Seedance ratio / resolution / audio / watermark 等非模型参数
- Omnihuman TTS voice 等与模型配置不同层级的运行参数
- 全量重构为新的统一 Python 配置库
# Risks
## 1. 现有保存配置缺少 `modelId`,需要平滑兼容
老配置里只有 `baseUrl` / `apiKey`。新增字段后,读取逻辑必须允许 `modelId` 为空;空值应解释为“继续走旧逻辑 / 历史默认值”,不能当成清空。
## 2. 需要把“客户端设置”和“employee config”职责彻底分开
当前部分链路已经会把 employee config 的 `modelId` 暴露到 `WorkspaceSummary` / 运行时配置里。如果后续又把它当客户端 model 默认值,就会重新混淆两条链路。首期要明确:
- employee config:项目 zip / 项目侧
- settings model config:客户端与项目模型选择
## 3. XHS / Douyin 各脚本仍依赖旧 env 名称
现有项目脚本散落读取:
- `ARK_*`
- `QWEN_*`
- `XHS_IMAGE_*`
- `SEEDREAM_*`
- `SEEDANCE_*`
如果主进程不继续注入这些兼容 env,首轮改动容易断链。建议首期保留旧 env 兼容层。
## 4. Douyin 内部默认值不一致,统一后可能改变历史行为
Douyin 当前同一能力存在 `qwen3-plus` / `qwen3.5-plus` 两套默认值。统一后会暴露出行为变化,需要明确“谁是最终默认值来源”。建议以后统一以:
- 用户设置
- 单一保底默认值
两层为准。
## 5. XHS 当前部分逻辑依赖模型能力猜测
例如按 provider 名称或 model id token 推测是不是视觉/生图模型。切到显式 `modelId` 后,这些猜测逻辑可能与新配置冲突。首期应改成“显式配置优先,启发式仅兜底”。
## 6. 调试复杂度会上升,必须增加可观测性
链路变成“设置页 -> 主进程归一化 -> runtime 注入 -> workspace 消费”后,如果没有清晰日志,很难判断最终生效的是哪一层。建议在主进程和项目入口增加脱敏后的 resolved-config 调试输出。
# Verification
## 1. 设置保存与回读
- 在设置页填写:copywriting/image/video 的 `baseUrl``apiKey``modelId`
- 保存后确认:
- UI 能正确回显保存值
- 老配置文件可正常升级加载
- 未填写 `modelId` 的旧配置不报错
## 2. 主进程归一化
- 不依赖 `openclaw-employee-config`,直接验证本地 settings 保存后的 resolved model config
- 验证优先级:
- settings 中显式 `modelId`
- 对应 capability 的 `baseUrl` / `apiKey`
- 历史回退值
## 3. 主进程到项目执行链路
- 在项目启动/执行前检查归一化后的 resolved model config
- 验证 XHS / Douyin 拿到的是同一套解析后的 capability 配置
- 验证旧 env 兼容字段仍被正确注入
## 4. XHS 端到端
- 文案/分析链路:确认 `xhs/scripts/qwen.py` 不再优先落到 `qwen-plus`
- 视觉分析链路:确认 `ARK_VISION_MODEL` 来源于客户端设置下发配置
- 生图链路:确认 `xhs/scripts/run.py` 使用 settings 中的 image `modelId`,而不是 provider heuristics
## 5. Douyin 端到端
- 文案生成:确认 `douyin-writer/scripts/generator.py` 使用统一后的 copywriting `modelId`
- Seedream 生图:确认 `run_seedream.py` 使用 settings 中的 image `modelId`
- Seedance 生视频:确认 `run_seedance.py` 使用 settings 中的 video `modelId`
- split pipeline:确认 `run_pipeline.py` 与 coordinator 使用同一套默认模型来源,不再各自写死
## 6. 回归测试
- 未填写任何 capability `modelId` 时,旧项目仍可执行
- 仅配置 copywriting 时,不影响 image/video
- 仅配置 image/video 时,不影响其他能力
- 保存空白 apiKey 字段时,不会误清除已有 capability 密钥
# Recommended execution order
1. 扩展 shared types 与 settings UI,补齐 `modelId` 保存能力
2. 在主进程建立统一的 resolved model config
3. 先接 Douyin:因为硬编码更多、收益最明显
4. 再接 XHS:收敛 `qwen.py``run.py`
5. 最后清理工具描述、日志与 smoke 测试
# Project Bundle Isolation Plan
Date: 2026-04-01
Status: Bundle ingestion, cloud-owned inventory enforcement, freshness hardening, replacement/rollback hardening, broader churn coverage, and lifecycle smoke are implemented
## 1. Product constraints
This plan follows the confirmed product constraints:
- Projects are defined by backend config, not local CRUD.
- The current source payload is `openclaw-employee-config`.
- The active bundle source is `skills[].skill.download_url`.
- `project_bundle_url` is not part of the current implementation.
- Project inventory is fixed by backend configuration.
- The current protocol returns a `skills` array.
- Each `skills[]` object uses one `skill.download_url` string to point at one project zip.
- Multiple projects are currently expressed by multiple `skills[]` objects.
- If the backend later changes to multi-value download URLs, that will be a separate protocol and implementation task.
## 2. What is already implemented
### Cloud bundle ingestion
- Runtime cloud bootstrap and payload update both trigger project bundle sync.
- Zip-backed remote skills are discovered and downloaded.
- Bundles are extracted and normalized.
- Project files are materialized into local project roots.
- Shared bundle `skills/` and `cron/` content is materialized.
- Synced projects are registered in local project metadata.
### Cloud-owned inventory enforcement
- No implicit local `default` project is created when there are no materialized projects.
- Session listing returns `[]` when no active project exists.
- Session creation is blocked with `Waiting for cloud project bundle sync.` until a cloud-backed project arrives.
- The first synced bundle-backed project becomes active cleanly.
### Project-bound execution
- Sessions are bound to projects.
- Each execution is prepared against the session's project.
- Project context snapshots are injected into request preparation.
- The router can choose `workspace-entry`, `skill`, or `chat-fallback`.
- `chat-fallback`, `skill`, and `workspace-entry` all schedule post-execution project context refresh.
### Stale bundle reconciliation
- Sync derives the expected bundle-backed project set from the current payload.
- Bundle-managed projects missing from the expected set are removed locally.
- Bundle-managed shared `skills/` entries missing from the expected set are removed locally.
- Bundle-managed shared `cron/` entries missing from the expected set are removed locally.
- Stale bundle manifest records are pruned.
- If the active bundle project is removed, active project falls back to the remaining valid project set.
### Freshness hardening
- Reuse is no longer assumed from `sourceUrl + configVersion` alone.
- When a manifest record is otherwise reusable, the bundle service performs a `HEAD` probe.
- The probe captures `ETag`, `Last-Modified`, and `Content-Length` when available.
- If those values change, the bundle is re-downloaded and re-materialized.
- If an older manifest lacks freshness identity and the remote now exposes one, the service forces one refresh to establish the baseline.
- Bundle manifest records now persist remote freshness metadata.
### Replacement and rollback hardening
- Bundle installation now follows `stage -> commit -> finalize/rollback`.
- Replacement can swap the project root plus shared `skills/` and `cron/` entries for the same logical `projectId`.
- If replacement fails after commit but before project metadata is fully updated, the previous project root, shared skill entries, and cron entries are restored.
- Recovery on the next sync is verified and keeps manifest ownership aligned with the recovered state.
### Regression smoke coverage
- `workspace-entry-smoke`
- `cloud-bundle-smoke`
- `project-context-refresh-smoke`
- `default-chat-smoke`
- `project-empty-inventory-smoke`
- `project-bundle-reconcile-smoke`
- `project-bundle-freshness-smoke`
- `project-bundle-replacement-smoke`
- `project-bundle-churn-smoke`
- `project-isolation-smoke`
Additional lifecycle coverage now verified:
- cloud bundle smoke covers payload `sync`
- cloud bundle smoke covers cached `init`
- cloud bundle smoke covers same-`projectId` replacement through Electron UI/main flow
- replacement smoke covers service-level rollback injection and recovery on the next sync
- churn smoke covers multi-project survivor/replacement/removal/addition paths
- `project-isolation-smoke.ps1` now aggregates reconcile, freshness, replacement, and churn service-level coverage
## 3. What is no longer pending
The previously pending items are now implemented:
- full cloud-owned project inventory enforcement
- strong rollback guarantees for partially failed bundle replacement
- broader service-level regression coverage for multi-project churn scenarios
- end-to-end lifecycle smoke for replacement scenarios
## 4. Remaining follow-up work
The main work left is follow-up hardening rather than missing core behavior:
- decide whether `project-isolation-smoke.ps1` and `cloud-bundle-smoke.ps1` should be wired into broader external CI or release gates
- expand deterministic churn coverage to larger or randomized stress matrices only when product changes raise the risk level
- keep the design docs aligned as the project-isolation surface expands
- if backend protocol evolves beyond single `skills[].skill.download_url`, implement that as a separate compatibility task
## 5. File-by-file status
### `apps/desktop/src/main/services/project-bundle.ts`
- Owns bundle freshness probing, expected-set reconciliation, replacement ordering, and rollback behavior.
- Persists manifest ownership and remote freshness metadata for bundle-managed state.
### `apps/desktop/src/main/services/project-store.ts`
- Owns active-project state without recreating a local fallback project when inventory is empty.
- Keeps active-project transitions consistent during bundle arrival, removal, replacement, and churn.
### `apps/desktop/src/main/ipc.ts`
- Keeps send and stream paths aligned on post-execution refresh behavior.
- Preserves background refresh semantics without adding reply latency.
### `build/scripts/*.ps1`
- Current smokes cover empty inventory, bundle reconcile, freshness, replacement rollback, multi-project churn, and Electron lifecycle replacement.
- `cloud-bundle-smoke.ps1` is the main Electron lifecycle regression for bundle replacement.
- `project-isolation-smoke.ps1` is the main service-level aggregation gate for project-isolation coverage.
## 6. Practical status summary
Current phase:
- not design-only
- not blocked on the earlier inventory / rollback gaps
- core execution chain works
- empty inventory semantics work
- stale bundle cleanup works
- bundle freshness hardening works
- replacement / rollback hardening works
- multi-project churn coverage exists at the service level
- lifecycle smoke for replacement is in place
Recommended immediate focus:
- keep the current smoke set green
- wire the current gate pair into broader rollout notes or external CI if needed
- expand regression breadth only if upcoming product changes need it
- keep `download_url` protocol evolution explicitly out of unrelated hardening work until backend is ready
# Remote Project Bundle Spec
Status: active
本文定义通过 `/openclaw-employee-config` 下发 zip 项目包,并在客户端专家页直接拉起执行的最小规范。
目标是让后续项目例如抖音、小红书都按同一约定交付:
1. 云端配置返回下载地址。
2. 客户端下载 zip 并解压到本地 workspace。
3. 专家页显示该项目。
4. 用户在专家页发送一句自然语言。
5. 客户端自动把请求路由到该项目的 `workspace-entry`
6. 项目在本地完整执行自己的自动化流程。
## 1. 适用范围
本规范适用于当前桌面客户端的 bundle 路径:
- 启动时或配置同步时,客户端根据员工配置中的远程 bundle 下载地址拉取 zip。
- 客户端将 zip 安装到本地 `user-data/projects/<projectId>`
- 专家页根据 `project.json` 中的声明决定该项目如何被展示和执行。
当前实现里,bundle 来源仍然是员工配置中的 skill 下载地址,不是单独的 `project_bundle_url`
## 2. 服务端需要提供什么
服务端不需要生成本地运行态文件。
服务端只需要保证:
- 员工配置中包含可下载的 zip 地址。
- zip 内容满足本文规定的目录结构和文件契约。
- 同一个项目更新时,继续使用同一个 `projectId`
- 如果 zip 内容变了,下载地址、配置版本或远端 freshness 元数据能够触发客户端重新拉取。
服务端不需要在 zip 内预生成这些内容:
- `updatedAt`
- `ready`
- `boundSkillIds`
- `memory/`
- `sessions.json`
- `session-messages/`
- `manifests/active-project.json`
- `manifests/project-bundles.json`
这些都由客户端本地生成或维护。
## 3. 一个 zip 里只能有一个项目
当前客户端按“一个 zip = 一个项目”处理。
支持两种 zip 根结构:
### 结构 A:zip 根目录直接就是项目目录
```text
douyin/
project.json
workspace_entry.py
README.md
tools/
...
```
### 结构 B:zip 根目录包含 `projects/`
```text
projects/
douyin/
project.json
workspace_entry.py
README.md
tools/
...
```
如果采用 `projects/` 结构,则 `projects/` 下必须且只能有一个项目目录。
## 4. 必须文件
### 4.1 `project.json`
每个项目包必须包含 `project.json`
最小建议字段如下:
```json
{
"id": "douyin",
"name": "Douyin Automation",
"description": "Project-isolated Douyin workflow.",
"version": "0.1.0",
"projectType": "workspace-package",
"platform": "douyin",
"capabilities": ["publish", "workflow"],
"requires": ["playwright"],
"workspaceAutomation": {
"runtime": "python",
"script": "workspace_entry.py",
"args": [
"--prompt",
"{prompt}",
"--prepared-prompt",
"{preparedPrompt}",
"--project-root",
"{projectRoot}"
]
},
"bundlePackaging": {
"excludePaths": [
"__pycache__"
]
},
"defaultEntry": {
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["douyin", "抖音", "发帖", "发布"],
"capabilities": ["publish", "workflow"]
},
"entries": [
{
"id": "workspace-entry",
"type": "workspace-entry",
"intentAliases": ["douyin", "抖音", "发帖", "发布"],
"capabilities": ["publish", "workflow"]
}
],
"workspaceEntryEnabled": true
}
```
字段说明:
- `id`: 必须稳定。后续同一项目升级时不要改。
- `name`: 专家页展示名的基础来源。
- `platform`: 建议填写,便于展示名和路由归类。
- `workspaceAutomation`: 如果希望专家页一句话直接拉起项目,这是必须的。
- `defaultEntry.type = "workspace-entry"`: 推荐配置,保证默认走项目入口而不是普通聊天。
- `entries`: 推荐显式声明,便于后续扩展 skill entry 或更多 intent alias。
- `bundlePackaging.excludePaths`: 只用于你在本地打 zip 时过滤不该进包的目录。
### 4.2 `workspace_entry.py`
如果项目需要通过专家页直接执行自动化流程,必须提供入口脚本。
当前客户端会把这些参数传给入口脚本:
- `--prompt`
- `--prepared-prompt`
- `--project-root`
入口脚本必须:
1. 在项目根目录内执行。
2. 能从自然语言 prompt 里解析出项目需要的执行参数。
3. 调用项目自身的主流程。
4. 通过 `QJC_WORKSPACE_EVENT\t<json>` 输出 started/status/delta/completed/error 事件。
推荐最小事件协议:
```text
QJC_WORKSPACE_EVENT\t{"type":"started","runId":"..."}
QJC_WORKSPACE_EVENT\t{"type":"status","stage":"...","label":"...","detail":"..."}
QJC_WORKSPACE_EVENT\t{"type":"delta","runId":"...","textDelta":"...","fullText":"..."}
QJC_WORKSPACE_EVENT\t{"type":"completed","runId":"...","content":"..."}
QJC_WORKSPACE_EVENT\t{"type":"error","runId":"...","message":"..."}
```
### 4.3 `README.md`
不是强制必需,但强烈建议有。
原因:
- 客户端会把项目 README 注入到项目上下文。
- 专家页模型在做项目路由和上下文理解时会受益。
## 5. 推荐目录结构
建议以后所有项目都收敛到下面这个结构:
```text
<project-id>/
project.json
workspace_entry.py
README.md
AGENTS.md
tools/
skills/
cron/
memory/
```
说明:
- `tools/`: 项目自己的脚本和模块。
- `skills/`: 可选。需要共享 skill 时放这里。
- `cron/`: 可选。需要共享 cron 时放这里。
- `memory/`: 所有可变运行态都应放这里。
## 6. 运行态文件必须放到 `memory/`
这是当前规范里最重要的一条。
客户端在 bundle 更新时,会整体替换项目目录,只保留以下本地状态:
- `sessions.json`
- `session-messages/`
- `memory/`
这意味着任何不在 `memory/` 下的可变状态,在下一次 bundle 替换时都可能丢失。
因此所有项目都必须遵守:
- 浏览器登录态放在 `memory/`
- Cookie / token / 本地缓存放在 `memory/`
- 调试截图和 HTML dump 放在 `memory/`
- 运行结果文件放在 `memory/`
- 生成图片、临时文件、下载文件放在 `memory/`
不建议把这些运行态写在项目根目录:
- `.env`
- `generated_images/`
- `openclaw_runs/`
- `openclaw_debug/`
- `*_profile/`
如果项目必须写环境文件,建议写成:
```text
memory/project.env
```
并由代码显式从这里读取。
## 7. 当前客户端会自动补哪些内容
bundle 安装完成后,客户端会自动:
- 重写 `project.json` 中的 `id`
- 重写 `project.json` 中的 `name`
- 回写 `updatedAt`
- 回写 `ready = true`
- 回写 `boundSkillIds`
- 创建 `memory/`
- 创建默认 session
- 创建 `sessions.json`
- 创建 `session-messages/`
因此 zip 内不需要预先包含这些运行态内容。
## 8. 专家页能跑起来的前提
如果以后抖音项目也按这个规范做,那么在当前客户端里,拉取后可以在专家页跑起来,但有两个前提。
### 前提 A:项目走 `workspace-entry`
也就是:
- `project.json` 里声明了 `workspaceAutomation`
- `defaultEntry``entries` 明确指向 `workspace-entry`
- `workspace_entry.py` 能处理自然语言 prompt
### 前提 B:项目依赖已经包含在当前 bundled runtime 里
当前 bundled runtime 不会根据 zip 自动安装额外 Python 依赖。
也就是说:
- `project.json.requires` 目前只是元数据,不会自动触发安装。
- 如果项目依赖一个 runtime 里没有的 Python 包,项目仍然会启动失败。
当前已确认的 bundled runtime Python 依赖包括:
- `openpyxl`
- `pandas`
- `requests`
- `beautifulsoup4`
- `lxml`
- `pypdf`
- `python-docx`
- `charset-normalizer`
- `pyyaml`
- `pillow`
- `python-dotenv`
- `playwright`
如果新项目例如抖音项目只依赖这些现有能力,那么它可以直接按本规范打包并在专家页执行。
如果它需要新的 Python 依赖,则必须先扩 runtime,而不是只改项目 zip。
## 9. 对项目作者的约束
最终用户不会修改项目代码。
所以项目包必须满足:
- zip 下载后可直接运行
- 首次初始化逻辑由项目自己完成
- 不依赖用户手工创建目录
- 不依赖用户手工改 `project.json`
- 不依赖用户手工改 Python 代码
可以接受的首次使用动作只有这类:
- 专家页发一句话
- 首次登录某个站点
- 首次授权浏览器
不可以接受的动作:
- 让用户进项目目录改路径
- 让用户自己补脚本
- 让用户手工建状态目录
- 让用户自己 pip install
## 10. 新项目交付检查清单
在交付任何新项目 zip 前,至少检查以下内容:
1. zip 中只有一个项目。
2. `project.json` 存在且 `id` 稳定。
3. `workspaceAutomation` 指向的脚本真实存在。
4. 专家页意图词已经写进 `defaultEntry.intentAliases``entries.intentAliases`
5. 所有可变状态都写入 `memory/`
6. 项目不依赖当前 runtime 中缺失的 Python 包。
7. 项目首次运行所需的浏览器/登录/配置流程已经内置在代码里。
8. 用真实 bundle 路径做过一次 `cloud zip -> 专家页 -> 自动化执行` 验证。
## 11. 推荐的最小模板
```text
douyin/
project.json
workspace_entry.py
README.md
AGENTS.md
tools/
run.py
publish.py
memory/
```
其中:
- `run.py` 是项目自己的完整自动化主流程。
- `workspace_entry.py` 只负责把专家页自然语言转换成 `run.py` 的结构化调用。
- 所有本地状态都进入 `memory/`
## 12. `xhs` 当前状态
`workspace/xhs` 已经完成了“专家页可直接拉起”的主体改造:
-`project.json`
-`workspaceAutomation`
-`workspace_entry.py`
- 真实专家页链路已经验证通过
但它仍然有一部分运行态还写在项目根目录,不完全符合本规范的最终状态。
后续如果要把 `xhs` 作为标准模板,建议再补一轮整理,把这些目录迁移到 `memory/`
- `xhs_profile`
- `generated_images`
- `openclaw_runs`
- `openclaw_debug`
- 项目级环境文件
## 13. 最终结论
以后抖音项目可以按这份规范来做。
只要同时满足下面两点:
- zip 结构和 `workspace-entry` 契约满足本文要求
- 项目依赖不超出当前 bundled runtime 能力
那么客户端在拉取配置并同步 zip 后,就可以在专家页通过对话直接把项目跑起来。
# QianjiangClaw 技能体系与运行时依赖规划
## 1. 目标
这份文档用于定义两层能力:
- 技能体系:用户在界面里看到和选择的能力入口
- 底层依赖体系:小龙虾实际执行任务所需要的运行时、库、工具和外部组件
核心原则:
- 用户应理解“我在使用什么能力”,而不是“我在安装什么库”
- 高频、通用、直接影响首用体验的依赖应随安装包预装
- 低频、体积大、业务定制强的依赖可以按需动态安装
- 技能中心展示的是业务能力;依赖体系由安装包和运行时托管
## 2. 技能体系
### 2.1 内置技能
这些能力应作为默认内置技能出现在产品中,用户无需额外理解底层依赖。
| 技能名称 | 目标能力 | 典型任务 |
|---|---|---|
| 聊天问答 | 通用问答、解释、改写、总结 | 问答、写邮件、改文案、总结会议 |
| 文档处理 | 读取和整理常见文档 | 总结 `txt/md/docx/pdf`、对比文档、抽取重点 |
| 表格处理 | 读取和分析表格数据 | 统计 Excel、筛选 CSV、汇总报表、生成表格结论 |
| 代码与脚本 | 处理工程和脚本任务 | 改代码、跑脚本、生成小工具、分析日志 |
| 文件整理 | 管理本地文件 | 批量重命名、归档、搜索、导出、移动文件 |
| 网页信息处理 | 获取和清洗网页内容 | 抓页面、提取文章、汇总网页信息 |
| 运行诊断 | 排障与运行状态检查 | 导出诊断、查看日志、检查 runtime/Gateway 状态 |
### 2.2 后续扩展技能
这些能力可以作为第二批或按行业定制技能上线。
| 技能名称 | 目标能力 | 说明 |
|---|---|---|
| 数据可视化 | 图表和趋势输出 | 面向表格和统计结果的图表生成 |
| OCR 文档识别 | 扫描件/图片文字提取 | 属于重依赖能力,不建议第一批预装 |
| 浏览器自动化 | 表单填写、网页操作 | 依赖浏览器驱动和更重的自动化能力 |
| 企业系统接入 | 内部业务系统联动 | 取决于客户系统和定制接口 |
| IM/Office 协作 | 企业 IM、邮件、Office 协同 | 强业务定制能力 |
## 3. 运行时依赖体系
### 3.1 建议预装的基础依赖
这些依赖属于高频、稳定、跨场景通用,建议作为 bundled runtime 的标准组成部分。
| 依赖 | 用途 | 是否预装 |
|---|---|---|
| Python | 通用脚本执行基础 | 是 |
| openpyxl | `.xlsx` 读写和基础表格处理 | 是 |
| pandas | 表格统计、清洗、聚合 | 是 |
| requests | HTTP 请求 | 是 |
| beautifulsoup4 | HTML 解析 | 是 |
| lxml | 更稳的 HTML/XML 解析 | 是 |
| pypdf | PDF 文本提取 | 是 |
| python-docx | Word 文档读取 | 是 |
| charset-normalizer | 编码识别和处理 | 是 |
| pyyaml | YAML/配置处理 | 是 |
### 3.2 建议按需动态安装的扩展依赖
这些依赖不适合作为安装包第一批默认预装内容。
| 依赖 | 用途 | 是否默认预装 |
|---|---|---|
| matplotlib | 图表生成 | 否 |
| seaborn | 统计图表美化 | 否 |
| pyxlsb | `.xlsb` 文件解析 | 否 |
| camelot / tabula | PDF 表格抽取 | 否 |
| playwright / selenium | 浏览器自动化 | 否 |
| OCR 相关依赖 | 图片识别、票据识别、扫描件处理 | 否 |
| 数据库驱动 | MySQL/Postgres/SQL Server 等接入 | 否 |
| 企业专有 SDK | 客户内部系统接入 | 否 |
## 4. 技能与依赖映射
| 技能 | 必需依赖 | 建议预装 | 可选扩展 |
|---|---|---|---|
| 聊天问答 | OpenClaw runtime、LLM 配置 | 是 | 无 |
| 文档处理 | Python、pypdf、python-docx、charset-normalizer | 是 | OCR |
| 表格处理 | Python、openpyxl、pandas | 是 | pyxlsb、图表库 |
| 代码与脚本 | Python、本地命令执行能力 | 是 | Node/其他语言工具链 |
| 文件整理 | Python、本地文件工具 | 是 | 无 |
| 网页信息处理 | requests、beautifulsoup4、lxml | 是 | playwright/selenium |
| 运行诊断 | 日志、诊断导出、runtime manager | 是 | 无 |
## 5. 预装与动态安装的判断标准
### 5.1 应预装
满足以下任一条件的依赖应优先预装:
- 高频使用
- 影响首次任务成功率
- 属于核心产品能力
- 体积可接受,版本稳定
当前最明确需要预装的是:
- Python
- openpyxl
- pandas
- requests
- beautifulsoup4
- pypdf
- python-docx
- charset-normalizer
- pyyaml
### 5.2 可动态安装
满足以下特征的依赖更适合按需安装:
- 体积较大
- 低频使用
- 强行业/客户定制
- 平台兼容性复杂
例如:
- 浏览器自动化
- OCR
- 高级图表
- 数据库驱动
- 客户专有工具链
## 6. 当前项目对应的落地建议
### 6.1 第一批必须补齐
为了让“安装包开箱可用”,当前最优先要做到:
1. bundled runtime 预装 Python
2. bundled runtime 预装 `openpyxl`
3. bundled runtime 预装 `pandas`
4. bundled runtime 预装基础文档/网页处理依赖
5. 安装后无需首次任务再 `pip install`
### 6.2 第一批用户可见技能
技能中心第一批建议只放这几项:
- 工作台问答
- 表格处理
- 文档处理
- 文件整理
- 运行诊断
### 6.3 第二批再扩展
当运行时稳定后,再增加:
- 网页自动化
- OCR
- 数据可视化
- 企业系统接入
## 7. 后续实施顺序
### 阶段 1
- 固定第一批内置技能清单
- 固定第一批 bundled runtime 预装依赖清单
- 在安装包构建流程中把这些依赖真正打进去
### 阶段 2
- 技能中心按中文产品页展示这些技能
- 每个技能补充说明、适用场景、输入输出示例
### 阶段 3
- 增加动态依赖安装机制
- 增加依赖安装状态提示和失败恢复
- 区分“预装能力”和“扩展能力”
### 阶段 4
- 对行业型技能做单独打包或远程下发
- 引入更细的权限、积分和套餐控制
## 8. 当前结论
当前小龙虾已经可以通过动态安装依赖完成 Excel 统计任务,说明架构可行。
但正式产品不应长期依赖“首次执行时现场装库”的方式。正确方向应是:
- 技能中心内置“表格处理”等核心技能
- bundled runtime 预装与这些技能对应的高频基础依赖
- 用户只需要选择技能和下任务,不需要理解底层是否安装过 `openpyxl/pandas`
# 启动页预热方案(适配当前代码)
## 背景
当前代码已经把一部分冷启动前移到了主进程,但聊天页仍然会在服务未 ready 时提前打开,并通过发送区禁发来暴露启动过程。用户感知上会变成“聊天窗口已经打开,但发送按钮是灰的,还要等待准备环境”。
这次方案的目标是把初始化完整收拢到启动页,聊天页只在 `chatReady === true` 后进入;同时对云配置增加“缓存优先 + 后台增量同步”,缩短二次启动时间。
## 当前代码问题定位
- 主进程已经有预热链路:`apps/desktop/src/main/index.ts`
- 启动时会拉取员工配置、启动 bundled runtime、连接 gateway。
- 状态聚合已经存在:`apps/desktop/src/main/ipc.ts`
- `WorkspaceSummary` 已包含 `chatReady``chatLaunchState``startupPhase``startupMessage`
- 问题主要出在渲染层:`apps/ui/src/App.tsx`
- 聊天页在未 ready 时提前进入。
- 发送按钮和提示文案绑定到运行时 ready 状态,导致界面像“卡住”。
## 本次适配
### 1. 主进程预热保留,但改成缓存优先
-`OpenClawConfigClient` 中加入运行时云配置缓存。
- 缓存内容包含:
- 上次成功的员工配置 payload
- 配置摘要
- 当前员工密钥指纹
- 启动时优先尝试读取缓存:
- 如果缓存命中且密钥未变,bundled runtime 直接基于缓存配置启动。
- 启动完成后再后台执行一次云端刷新。
- 如果缓存不存在或密钥变更:
- 仍按原始链路阻塞拉取 `fetchConfig("init")`
### 2. 缓存失效策略
- 当用户更换或清空员工密钥时,立即清理旧缓存。
- 缓存只在密钥指纹一致时复用,避免把上一位员工的配置拿来启动。
### 3. 启动页成为聊天入口
- 聊天视图新增启动页门禁:
- 服务未 ready 时,只展示启动页,不渲染聊天消息区和发送区。
- 服务 ready 后,自动进入聊天页。
- 启动页展示:
- 主状态文案
- 进度条
- 四段步骤:读取本地配置、准备本地助手、连接聊天服务、进入对话
- 失败时保留“重新准备”和“打开设置”入口
### 4. 聊天页恢复正常可发状态
- 聊天页不再把发送按钮绑定到启动期状态。
- 进入聊天页后:
- 发送按钮只受“已绑定、输入非空、未发送中、未保存中”控制。
- `ensureChatAvailable()` 保留,但只用于异常恢复:
- 理论上不再承担首开冷启动主路径。
### 5. 现有优化继续保留
- 只展示 `user` / `assistant` 主消息。
- 保留可折叠“思考过程”面板。
- 保留 `completed``delta` 的兜底显示逻辑。
## 涉及文件
- `apps/desktop/src/main/index.ts`
- `apps/desktop/src/main/ipc.ts`
- `apps/desktop/src/main/services/cloud-api.ts`
- `apps/desktop/src/main/services/app-config.ts`
- `apps/ui/src/App.tsx`
- `apps/ui/src/styles.css`
## 验收标准
- 已绑定情况下,打开应用先看到启动页,而不是灰按钮聊天页。
- 二次启动且缓存可用时,启动页等待明显短于首次启动。
- 启动完成后进入聊天页,发送按钮默认可用。
- 更换员工密钥后,不复用旧员工缓存。
- runtime 或 gateway 异常掉线时,仍能通过恢复逻辑重新可用。
## 测试建议
1. 首次启动,无缓存
- 应显示启动页并完成完整预热。
2. 再次启动,有缓存
- 应明显更快进入聊天页。
3. 更换员工密钥
- 旧缓存应失效,不应沿用旧员工配置。
4. 断网启动
- 有缓存时可继续进入;无缓存时停留在启动失败页。
5. 聊天页回归
- 进入聊天页后首条消息不应再承担完整冷启动链路。
# qjclaw 聊天页与设置页 UI/交互整改方案
## Summary
基于项目现状,问题集中在三处:发送反馈弱、核心聊天交互缺失、设置页信息层级混乱。已确认的项目事实是:聊天输入当前仅支持 Ctrl/Cmd+Enter 发送;发送按钮只有静态箭头;消息气泡无复制入口;窗口为 frame: false 且只在局部头部区域可拖动;设置
页工作目录当前可编辑可保存;设置保存后只有顶部短暂提示,没有未保存提醒。
整改目标是把聊天主链路做成“可感知、可键盘操作、可恢复”,把设置页做成“只暴露真实可操作项、减少冗余说明、强化保存状态反馈”。
## 修改范围
- 前端主改动
- apps/ui/src/App.tsx
- apps/ui/src/styles.css
- 可能涉及的桌面壳配合确认
- apps/desktop/src/main/create-window.ts
- 不在本次范围
- IPC 协议与 SaveConfigInput 结构
- 后端配置存储逻辑
- 消息渲染内容格式化能力
- 插件页、专家页业务流程本身
## Key Changes
- 聊天发送区
- 将输入区改为标准 <form onSubmit> 提交。
- 输入规则改为 Enter 发送,Shift+Enter 换行,替代现有 Ctrl/Cmd+Enter。
- 重写发送按钮状态:
- 空闲态改成双蓝色火箭 >> 意象图标。
- 发送中显示橙色旋转圆环,不再只是灰掉。
- 禁用态保留轮廓和可读状态,不做纯灰块。
- 更新占位文案与提示语,避免继续显示旧的快捷键规则。
- 聊天消息区
- 在用户和助手消息气泡下方新增稳定工具栏,提供“复制”按钮。
- 点击复制后按钮短暂反馈“已复制”,随后恢复。
- 仅复制消息正文文本;思考中空消息、trace、状态卡、附件不纳入复制。
- 工具栏默认占位,避免 hover 出现造成消息跳动。
- 窗口拖拽
- 保留 frame: false,但补充稳定 drag 热区。
- 非对话页维持 page-topbar 可拖动,同时确保存在真实空白拖拽区域。
- 对话页在 conversation-panel-head 层增加更明确的 drag strip 或可拖区域。
- 所有按钮、输入框、状态芯片继续标记 no-drag,避免拖拽吞掉点击。
- 设置页
- 工作目录改为只读展示卡,不再提供编辑输入框和“保存工作区设置”按钮。
- 模型配置卡片精简文案,只保留卡片标题、状态、必要字段。
- api_key 等字段标签强化层级,提升与输入框的颜色和字重区分。
- 引入“未保存变更”脏状态:
- 任一 key 字段被修改后显示非阻断提示条“当前修改尚未保存,保存后生效”。
- 切换页面、离开设置页或关闭窗口时,如存在脏状态则弹确认。
- 保存成功后改为明确提示“已保存,新的配置将在后续执行中生效”。
- 视觉规范
- 延续当前浅色桌面工具基调,但降低装饰性,提升功能优先级。
- 重点强化三组对比:字段标签 vs 输入框、可编辑项 vs 只读项、空闲态按钮 vs 处理中按钮。
- 所有新增交互保留可见 focus 状态,满足键盘可达性。
## 风险点
- 输入行为变更风险
- 现有用户已习惯 Ctrl/Cmd+Enter,改为 Enter 直发后,可能误发送多行内容。
- 需要通过占位文案、辅助提示和 Shift+Enter 明示降低迁移成本。
- 如果 drag 区覆盖不准,会出现“按钮点不动”或“窗口仍拖不动”。
- 消息卡新增工具栏后,长消息、流式消息、trace 展开态可能出现间距不协调。
- 需要验证不同消息态下不会破坏对齐关系。
- 设置页脏状态风险
- 草稿值、已保存值、服务端返回值之间如果比较策略不一致,容易出现误报“未保存”或保存后仍然脏。
- 需统一以当前草稿与最新保存配置做精确比较。
- 工作目录只读化风险
- 当前 saveConfig() 仍会带上 workspacePath;前端隐藏编辑后要避免误导实现者继续保留无意义保存入口。
- 本次应只调整 UI 暴露,不触碰底层兼容字段。
- 提示反馈冲突风险
- 现有 infoText/errorText 顶部提示、发送态提示、设置未保存提示可能同时出现。
- 需要明确优先级和位置,避免提示堆叠。
## Public Interfaces / Behavior
- 输入行为从“Ctrl/Cmd+Enter 发送”调整为“Enter 发送,Shift+Enter 换行”。
- 消息项新增复制能力,但不修改消息数据协议;复制反馈优先由 UI 本地状态按 message.id 管理。
- 设置页新增未保存变更判断,但不新增后端字段,不修改 SaveConfigInput。
- 工作目录从可编辑配置项降级为只读展示项,前端不再提供对应保存入口。
## Test Plan
- 手工交互测试
- 聊天输入框按 Enter 可发送,按 Shift+Enter 仅换行。
- 发送中按钮显示橙色 spinner,且不可重复提交。
- 按钮空闲、禁用、发送中三种视觉状态清晰可分。
- 用户消息和助手消息的复制按钮都可用,复制结果与界面正文一致。
- 复制后按钮反馈正确,2 秒左右恢复。
- 长文本、多行文本、流式完成后的文本都能正确复制。
- 聊天页、设置页、插件页顶部都能稳定拖动窗口。
- 顶部按钮、会话按钮、输入框不会被拖拽区影响点击。
- 设置页任一 key 字段修改后出现未保存提示。
- 设置页保存成功后提示更新,未保存提示消失。
- 存在未保存变更时,切页、离开设置页、关闭窗口会触发确认。
- 工作目录区域仅展示,不可编辑,不再出现“保存工作区设置”。
- 样式与布局测试
- 验证 375px、768px、1024px、1440px 下聊天区和设置页不溢出。
- 验证消息气泡新增工具栏后,用户/助手左右对齐仍正确。
- 验证设置卡片字段标签对比度明显提升,标签与输入框层级可快速分辨。
- 验证 toast、错误提示、未保存提示不会互相遮挡。
- 可访问性测试
- Tab 顺序符合视觉顺序。
- 新增复制按钮、发送按钮、设置按钮都有可见 focus ring。
- aria-label 与按钮状态文案在发送中、复制后都正确。
- 颜色不是唯一状态指示,发送中和未保存状态同时有图形/文案辅助。
- 验证命令
- 运行 corepack pnpm typecheck
- 如改动影响桌面壳交互,补跑 corepack pnpm dev 进行窗口拖拽与聊天链路手测
## Assumptions
- 采用已确认默认方案:工作目录用只读展示,不使用只读输入框。
- “需要保存生效”采用非阻断提示为主,离开当前上下文时再弹确认。
- 复制按钮先做纯文本复制,不覆盖 trace、状态卡和附件。
- 主要改动集中在 apps/ui/src/App.tsx 与 apps/ui/src/styles.css,桌面壳仅做必要配合确认。
\ No newline at end of file
# Single Instance + Task Isolation
Date: 2026-04-01
Status: Foundation, cloud-owned inventory, freshness hardening, replacement/rollback hardening, broader bundle regression coverage, and lifecycle smoke are implemented
## 1. Intent
The goal is a single desktop app instance with isolated project execution.
Isolation means:
- each session is bound to a project
- prompts are prepared with that project's context only
- execution routing stays inside the selected project's boundaries
- project state should not leak across sessions or projects
## 2. What is implemented
### Project and session model
- Projects exist in local workspace state.
- Sessions are created per project.
- Active project selection is real.
- Session to project binding is real.
### Cloud-owned inventory behavior
- No project means no implicit local `default` project.
- Session listing returns `[]` when inventory is empty.
- Creating a session while waiting for the first cloud project returns `Waiting for cloud project bundle sync.`.
- The first synced bundle-backed project becomes active without local fallback recreation.
### Project context model
- `ProjectContextService` builds snapshots from project root files.
- `SOUL.md`, `USER.md`, `README.md`, and tracked memory files are included.
- Snapshot caching, invalidation, and refresh are implemented.
- `session.contextSnapshotId` rebinding is implemented.
### Execution routing
The router can choose:
- `workspace-entry`
- `skill`
- `chat-fallback`
All three routes are project-aware.
### Cloud bundle path
- Cloud payload can define zip-backed projects.
- Bundles are downloaded and materialized locally.
- Shared bundle assets can also be materialized.
- Stale bundle-managed projects and shared assets can be removed when the expected cloud bundle set changes.
- Same-URL bundle changes can now trigger re-download through HTTP freshness metadata.
- Same-`projectId` bundle replacement now uses explicit stage/commit/finalize-or-rollback handling.
- If replacement fails after commit but before metadata sync completes, the previous project root and shared assets are restored.
### Current protocol boundary
- The current backend payload returns a `skills` array.
- Each `skills[]` object carries one project zip through its single `skill.download_url` string field.
- Multi-project inventory is currently expressed by multiple `skills[]` objects, one project download URL per object.
- If the backend later changes this field to an array or set, that must be treated as a separate protocol and implementation change.
- That protocol upgrade has not started yet.
## 3. Current default chat behavior
Default chat is no longer a special weak path.
Current behavior:
- it reads the latest available project snapshot before each request
- it injects project context into prepared prompt content
- after a `chat-fallback` turn completes, it queues project context refresh in the background
- when refresh succeeds, `session.contextSnapshotId` is rebound to the refreshed snapshot
- the next request reuses the refreshed snapshot
The refresh remains asynchronous so reply delivery is not blocked.
## 4. Verified coverage
Current repo smoke coverage includes:
- `workspace-entry-smoke.ps1`
- `cloud-bundle-smoke.ps1`
- `project-context-refresh-smoke.ps1`
- `default-chat-smoke.ps1`
- `project-empty-inventory-smoke.ps1`
- `project-bundle-reconcile-smoke.ps1`
- `project-bundle-freshness-smoke.ps1`
- `project-bundle-replacement-smoke.ps1`
- `project-bundle-churn-smoke.ps1`
- `project-isolation-smoke.ps1`
Additional verified points:
- cloud bundle smoke passes with freshness probing enabled
- cloud bundle smoke now validates same-`projectId` replacement through the Electron UI/main chain
- service-level replacement smoke validates rollback injection and recovery on the next sync
- service-level churn smoke validates multi-project survivor/replacement/removal/addition behavior
- `project-isolation-smoke.ps1` now aggregates reconcile, freshness, replacement, and multi-project churn coverage into the broader project-isolation gate
- Electron smoke validates workspace-agent status history instead of relying on the final status label only
## 5. Important current limitations
### Broad UI regression breadth is still selective
The targeted Electron lifecycle smoke for cloud bundle replacement now exists, but the full UI regression matrix is still intentionally selective rather than exhaustive.
### Stress breadth can still expand
The current replacement and churn coverage is strong for deterministic paths, but future changes may still need larger randomized multi-project stress coverage.
## 6. What has been completed so far
Completed enough to count as real implementation:
- project-bound session model
- cloud-owned inventory enforcement
- project context snapshot model
- cloud zip materialization path
- project-aware execution routing
- default chat refresh alignment with other project-aware paths
- stale bundle-managed cleanup for project/skill/cron/manifest state
- bundle freshness hardening using remote metadata probe
- replacement / rollback hardening for same-project bundle updates
- broader service-level churn coverage for multi-project inventory transitions
- smoke coverage for empty inventory, removal, freshness, replacement, churn, and Electron lifecycle validation
This project is no longer at the design-only stage.
## 7. What should happen next
Recommended next work:
1. Keep the current smoke set green as adjacent runtime work lands.
2. Use `project-isolation-smoke.ps1` plus `cloud-bundle-smoke.ps1` as the main higher-level regression gate pair.
3. Expand breadth only when upcoming product changes introduce new isolation surfaces.
4. If backend protocol changes from single `download_url` to a multi-value form, handle it as a separate compatibility task instead of folding it into unrelated hardening work.
## 8. Final summary
The single-instance plus task-isolation foundation is implemented.
The earlier main gaps around cloud-owned inventory, replacement/rollback hardening, and lifecycle smoke have been closed.
The remaining work is mostly broader regression breadth and future protocol evolution, not missing core behavior.
宿主协议不一样。ing Claude's current work───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
──简短结论───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
E:\originzip\xiaohongshu-writer─是─本地 OpenClaw 插件形态,不是─桌面客户端 bundle/workspace 形态。─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
所以在本地 OpenClaw 里,它只要能加载插件和技能就能跑;但在客户端里,宿主需要的不只是技能目录,还需要一套可打包、可注入上下文、可隔离运行态、可稳定调度的工作区协议。
---
为什么本地能跑,客户端不行
1. 源项目是“插件”,不是“workspace automation project”
你这个源项目的形态很明确:
- E:\originzip\xiaohongshu-writer\package.json:4 用的是 openclaw.extensions
- E:\originzip\xiaohongshu-writer\openclaw.plugin.json:1 开始是插件描述
- E:\originzip\xiaohongshu-writer\openclaw.plugin.json:4-9 直接列 skills
也就是说,它的假设是:
- 宿主会按 插件机制 加载
- 宿主认识 openclaw.plugin.json
- 宿主会把 index.js 扩展跑起来
- skills 按插件规则注册
而工作区版 xhs 已经变成了另一套协议:
- D:\qjclaw\workspace\xhs\project.json:4 projectType: "automation-project"
- D:\qjclaw\workspace\xhs\project.json:7-18 workspaceAutomation.script: "workspace_entry.py"
- D:\qjclaw\workspace\xhs\project.json:82-89 boundSkillIds、workspaceEntryEnabled
客户端要的不是“有几个 skills 就行”,而是“这是一个我能托管的 automation project”。
---
2. 客户端需要一个统一入口,不是直接猜怎么跑脚本
源项目的脚本是 CLI 风格的:
- E:\originzip\xiaohongshu-writer\scripts\run.py:1-11 就是在定义命令行入口
- E:\originzip\xiaohongshu-writer\scripts\run.py:27-35 直接本地改 sys.path 并导入脚本模块
- E:\originzip\xiaohongshu-writer\scripts\run.py:134-137 还直接把 debug_search.jpg 写当前目录
这类脚本的前提是:
- 你知道从哪个命令启动
- 你知道传什么参数
- 你知道当前工作目录是什么
- 你知道输出文件落哪
桌面客户端不能靠“猜”这些事情,所以工作区版才会加:
- D:\qjclaw\workspace\xhs\workspace_entry.py:28-37 统一入口与运行脚本位置
- D:\qjclaw\workspace\xhs\project.json:7-18 明确告诉客户端入口脚本和参数模板
也就是说,客户端不是不能执行 scripts/run.py,而是它需要一个稳定入口层来把自然语言请求转换成可执行脚本调用。
---
3. 客户端需要上下文注入,源项目没有这层协议
工作区版 workspace_entry.py 明显承担了宿主桥接职责:
- D:\qjclaw\workspace\xhs\workspace_entry.py:28 QJC_WORKSPACE_EVENT
- D:\qjclaw\workspace\xhs\workspace_entry.py:77-103 load_project_identity() / render_context_prelude()
- D:\qjclaw\workspace\xhs\workspace_entry.py:116-126 load_project_attachments()
这说明客户端运行时不只是“执行脚本”,还要处理:
- 当前项目身份
- 当前 prompt 上下文
- 用户附件
- 宿主和项目之间的事件通信
本地 OpenClaw 插件模式里,这些能力可能是宿主内部直接提供的,或者根本不需要 project workspace 这层包装。
但桌面客户端 bundle 里,如果没有这层桥接,skills/scripts 并不知道:
- 当前用户是在让它“生成笔记”还是“直接发布”
- 当前会话带了哪些附件
- 当前项目应该把上下文隔离到哪里
---
4. 客户端必须做运行态隔离,源项目默认是“本地目录式”
工作区版 xhs 明确把运行态收口到 memory/:
- D:\qjclaw\workspace\xhs\workspace_entry.py:29-34
- MEMORY_ROOT
- RUNS_ROOT
- GENERATED_ROOT
- PROFILE_ROOT
还统一注入环境变量:
- D:\qjclaw\workspace\xhs\workspace_entry.py:129-137
- XHS_MEMORY_ROOT
- XHS_PROFILE_DIR
- XHS_ENV_PATH
而源项目脚本更像默认直接在项目目录跑:
- E:\originzip\xiaohongshu-writer\scripts\run.py:134-137 当前目录写 debug_search.jpg
这在“本地手动跑项目”时问题不大,但在桌面客户端里会有几个问题:
1. bundle 可能解压到临时目录
2. 客户端需要每个项目状态隔离
3. 浏览器 profile / 生成文件 / 缓存文件不能乱落
4. Windows 下路径、权限、清理策略更敏感
所以不是“多做了很多没必要的改动”,而是客户端要把一个原本“开发者自己跑的 repo”,变成“宿主可控的项目单元”。
---
5. 源项目里的插件声明本身就不适合直接打包搬进客户端
这个点很关键:
- E:\originzip\xiaohongshu-writer\openclaw.plugin.json:4-9 里的 skills 路径是绝对路径
指向 C:\Users\15153476162\.openclaw\workspace\projects\xiaohongshu-writer\skills\...
这说明原项目默认假设:
- 它运行在某个固定的本地 OpenClaw 目录结构下
- 宿主知道这个目录布局
- 这个用户路径存在
但客户端 bundle 不能依赖用户机器上有这套固定目录,更不能依赖某个绝对路径。
所以工作区版才改成相对项目根的协议化入口。
---
6. 客户端不仅要“能跑”,还要“能识别能力”
工作区版 project.json 还声明了:
- D:\qjclaw\workspace\xhs\project.json:44-80 defaultEntry / entries
- D:\qjclaw\workspace\xhs\project.json:47-59 intentAliases / capabilities
这对客户端很重要,因为客户端要知道:
- 用户说“写一篇小红书”时该路由到哪个项目
- 这个项目支持什么能力
- 默认入口是什么
而本地插件模式里,skills 可能只是注册后由 OpenClaw 自己管理;桌面客户端如果只“扫目录”,它不知道该怎么做稳定路由和展示。
---
所以“为什么不能直接跑”
因为客户端要解决的是这 4 件事,而源项目只覆盖了其中一部分:
1. 项目识别
源项目:插件识别
客户端:workspace/automation project 识别
2. 入口调度
源项目:人手动/插件机制启动
客户端:统一入口、统一参数、统一路由
3. 上下文与附件
源项目:弱约束或宿主内置
客户端:显式协议注入
4. 运行态治理
源项目:本地 repo 风格
客户端:隔离、可打包、可迁移、可清理
---
你可以把它理解成
E:\originzip\xiaohongshu-writer 更像:
▎ “一个能在本地 OpenClaw 环境里被插件机制加载的项目”
而 D:\qjclaw\workspace\xhs 更像:
▎ “一个能被桌面客户端当成独立工作区项目稳定托管的项目”
两者不是业务代码能不能跑的问题,主要是宿主集成层不一致。
---
哪些改动其实就是在补这个“宿主集成层”
最核心就是这几类:
- 插件形态 → workspace project 形态
- package.json/openclaw.plugin.json → project.json
- 无统一入口 → 有统一入口
- workspace_entry.py
- 脚本直跑 → 宿主桥接后再跑脚本
- prompt / attachment / event / env 注入
- 项目根目录落盘 → memory 隔离目录
- MEMORY_ROOT / RUNS_ROOT / PROFILE_ROOT
# 千匠问天项目文档体系
## 文档结构说明
本项目采用系统化的文档管理结构,将文档分为以下几个主要类别:
### 目录结构
```
docs/
├── 00-overview/ # 项目概述
│ ├── README.md # 项目简介
│ ├── product-vision.md # 产品愿景
│ └── roadmap.md # 产品路线图
├── 01-architecture/ # 架构设计
│ ├── system-design.md # 系统架构设计
│ ├── tech-stack.md # 技术栈说明
│ └── data-flow.md # 数据流设计
├── 02-development/ # 开发指南
│ ├── getting-started.md # 开发环境搭建
│ ├── coding-standards.md # 编码规范
│ └── testing-guide.md # 测试指南
├── 03-api/ # API文档
│ ├── gateway-api.md # Gateway API
│ ├── electron-api.md # Electron API
│ └── runtime-api.md # Runtime API
├── 04-deployment/ # 部署运维
│ ├── installation.md # 安装部署
│ ├── configuration.md # 配置管理
│ └── monitoring.md # 监控维护
├── 05-project-memory/ # 项目记忆
│ ├── decisions/ # 技术决策记录
│ ├── history/ # 项目历史记录
│ ├── lessons/ # 经验教训总结
│ └── meeting-notes/ # 会议记录
├── 06-plans/ # 项目计划
│ ├── sprint-plans/ # 迭代计划
│ ├── feature-plans/ # 功能计划
│ └── milestone/ # 里程碑
├── 07-specs/ # 技术规范
│ ├── api-specs/ # API规范
│ ├── ui-specs/ # UI规范
│ └── data-specs/ # 数据规范
└── 08-references/ # 参考资料
├── tools.md # 工具参考
├── resources.md # 学习资源
└── glossary.md # 术语表
```
### 各目录详细说明
#### 00-overview/ - 项目概述
- **目的**:为新成员提供项目整体认知
- **内容**:项目背景、目标、范围、产品愿景等
- **目标读者**:所有项目相关人员
#### 01-architecture/ - 架构设计
- **目的**:记录系统架构和技术决策
- **内容**:系统架构图、组件设计、技术选型理由
- **目标读者**:开发人员、架构师
#### 02-development/ - 开发指南
- **目的**:指导开发工作流程
- **内容**:环境搭建、开发流程、代码规范、测试方法
- **目标读者**:开发人员
#### 03-api/ - API文档
- **目的**:记录所有API接口
- **内容**:API端点、参数、返回值、使用示例
- **目标读者**:前后端开发人员
#### 04-deployment/ - 部署运维
- **目的**:指导部署和运维工作
- **内容**:部署步骤、配置管理、监控告警、故障处理
- **目标读者**:运维人员、开发人员
#### 05-project-memory/ - 项目记忆
- **目的**:沉淀项目知识和经验
- **内容**:技术决策记录、项目历史、经验教训、会议记录
- **目标读者**:所有项目成员
- **特别说明**:这是项目的"知识库",记录为什么做出某些决策,以及从经验中学到了什么
#### 06-plans/ - 项目计划
- **目的**:记录项目计划和进度
- **内容**:迭代计划、功能规划、里程碑
- **目标读者**:项目经理、开发人员
#### 07-specs/ - 技术规范
- **目的**:定义技术标准和规范
- **内容**:API规范、UI设计规范、数据格式规范
- **目标读者**:开发人员、设计师
#### 08-references/ - 参考资料
- **目的**:收集相关参考资料
- **内容**:工具使用指南、学习资源、术语解释
- **目标读者**:所有项目成员
### 文档编写规范
1. **命名规范**
- 使用英文小写和连字符,如:`getting-started.md`
- 中文文档可以保留中文名,但建议有对应的英文名
2. **格式要求**
- 使用Markdown格式
- 包含必要的元信息(创建时间、作者、更新记录)
- 保持结构清晰,使用适当的标题层级
3. **更新机制**
- 文档随代码变更而更新
- 重要决策需要及时记录到项目记忆中
- 定期回顾和整理文档
### 迁移计划
现有文档将按以下规则迁移到新结构:
1. **第一阶段**:创建新目录结构,建立索引文档
2. **第二阶段**:分类迁移现有文档
3. **第三阶段**:补充缺失的文档内容
4. **第四阶段**:建立文档维护流程
### 贡献指南
1. 新文档应放置在合适的目录中
2. 更新现有文档时,保持结构和格式一致
3. 重要变更需要记录变更原因
4. 定期参与文档评审和维护
---
*最后更新:2026-05-28*
*文档版本:v1.0*
\ No newline at end of file
# 现有文档分类规则
## 分类规则
### 00-overview/ 项目概述
- 包含 "roadmap", "blueprint", "vision", "产品路线图", "商业蓝图"
- 产品级概述文档
### 01-architecture/ 架构设计
- 包含 "architecture", "架构", "design", "设计"
- 系统架构、技术架构相关
### 02-development/ 开发指南
- 包含 "guide", "指南", "development", "开发", "coding", "编码"
- 开发流程、环境搭建指南
### 03-api/ API文档
- 包含 "api", "接口", "endpoint", "协议"
- API接口文档
### 04-deployment/ 部署运维
- 包含 "deployment", "部署", "install", "安装", "运维", "monitoring"
- 部署、安装、运维相关
### 05-project-memory/ 项目记忆
- 包含 "handoff", "交接", "问题", "issue", "进度", "历史", "history", "decision", "决策"
- 项目历史记录、问题记录、交接文档
### 06-plans/ 项目计划
- 以日期开头(如 2026-04-*
- 包含 "plan", "计划", "方案", "sprint", "迭代"
- 项目计划、功能计划、迭代计划
### 07-specs/ 技术规范
- 包含 "spec", "规范", "standard", "标准", "requirement", "需求"
- 技术规范、需求规格
### 08-references/ 参考资料
- 包含 "reference", "参考", "resource", "资源", "tool", "工具"
- 参考资料、工具指南
## 具体文件分类映射
### 明确分类的文件
#### 00-overview/
- `product-roadmap.md` - 产品路线图
- `product-roadmap.zh-CN.md` - 产品路线图(中文)
- `commercial-product-blueprint.md` - 商业产品蓝图
- `commercial-product-blueprint.zh-CN.md` - 商业产品蓝图(中文)
#### 01-architecture/
- `architecture.md` - 架构设计文档
#### 05-project-memory/history/
- `0428问题.md` - 问题记录
- `*handoff.md` - 交接文档
- `*进度.md` - 进度报告
- `desktop-startup-handoff-2026-04-07.md` - 桌面启动交接
#### 06-plans/
- `2026-04-09-installer-runtime-url-greenlet-fix-plan.md`
- `2026-04-15-desktop-expert-sidebar-settings-revamp.md`
- `2026-04-16-desktop-expert-handoff.md`
- `2026-04-17-客户端模型配置打通进度.md`
- `2026-04-21-desktop-single-instance-handoff.md`
- `2026-04-22-desktop-ui-regression-handoff.md`
- `2026-04-24-抖音客户端配置接入进度.md`
- `2026-04-27-小红书客户端接入适配方案.md`
- `2026-04-27-小红书自然语言入口优化方案.md`
- `2026-04-29-desktop-ui-gray-blue-revamp-plan.md`
- `2026-04-30-frontend-ui-phased-refactor-plan.md`
- `2026-05-25-client-ui-ux-execution-plan.md`
- `# 安装包瘦身回滚计划.md`
- `dmg包改造方案.md`
- `openclaw-daily-report-plan.md`
#### 07-specs/
- `cloud-skill-flow.md`
- `cloud-skill-flow.zh-CN.md`
- `skill-runtime-plan.md`
- `project-bundle-isolation-plan.zh-CN.md`
- `remote-project-bundle-spec.zh-CN.md`
- `startup-prewarm-plan.zh-CN.md`
### 需要进一步检查的文件
- `parsed-purring-truffle.md` - 需要查看内容
- `progress-summary.md` - 可能是进度总结
- `报错修复方案.md` - 可能是问题解决方案
- `单一实例 + 任务隔离.md` - 可能是技术方案
- `客户端和本地小龙虾项目集成区别说明.md` - 可能是技术说明
- `客户端自动更新方案.md` - 可能是部署/功能方案
### 子目录文档
- `douyin-xhs-插入/` - 需要检查内容,可能属于06-plans/或05-project-memory/
- `superpowers/` - 需要检查内容
- `聊天页与设置页 UI/` - 可能是UI规范或计划
- `左侧栏数字员工树形目录与 13/14 寸适配计划.md` - 可能是UI计划
## 迁移步骤
1. 创建新目录结构
2. 按分类规则移动文件
3. 检查不确定的文件内容
4. 更新文档内的链接
5. 创建索引和导航
6. 验证文档完整性
## 注意事项
1. 保留原始文件的修改时间
2. 更新相关文档中的链接
3. 记录迁移日志
4. 创建反向映射(新位置->原位置)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment