feat: prepare Zhinian desktop client for pilot release

This commit is contained in:
inman
2026-04-29 10:23:20 +08:00
parent f9361e686a
commit 47b83b79fc
149 changed files with 15341 additions and 3590 deletions

200
docs/M1_HANDOFF.md Normal file
View File

@@ -0,0 +1,200 @@
# YINIAN Desktop M1 Handoff
> Updated: 2026-04-26
> Scope: M1 foundation closure after ClawX fork adaptation. See `docs/PILOT_QA.md` for the current M2 pilot demo and QA gate.
## 1. What M1 Delivers
M1 turns the ClawX fork into a YINIAN-ready desktop foundation without removing the original ClawX/OpenClaw capabilities.
Delivered:
- YINIAN login-first product flow.
- Auth session restore and logout cleanup.
- Hotel tenant context and hotel switching.
- Config snapshot sync from mock or HTTP control plane.
- Local skill registry grouped by `hotelId`.
- Skills sync v0 from manifest to registry.
- Today page as the new production home route.
- Skills page replaced with YINIAN skill control surface.
- Gateway auto-start deferred until YINIAN authentication.
- Legacy ClawX E2E compatibility preserved.
- Workspace packages added for future kernel/skill/UI boundaries.
Not delivered in M1:
- Real skill bundle download.
- Signature verification and unpacking.
- Real OTA skill execution.
- Final backend API contract.
- Customer-ready pilot dashboard depth.
## 2. Main Behavior
### Production Flow
1. App starts.
2. Renderer calls `window.yinian.auth.restoreSession()`.
3. If no valid session exists, user is redirected to `/login`.
4. Login succeeds through mock control plane by default, or HTTP mode when configured.
5. App loads config and local skill registry for the current hotel.
6. App navigates to `/today`.
7. Gateway initializes and starts only after authenticated hotel context exists.
### E2E Compatibility Flow
When Electron is launched with `CLAWX_E2E=1`, main process appends `e2e=1` to the renderer URL.
In E2E mode:
- Legacy setup flow remains available.
- `/` renders the original Chat page, not Today.
- Default language is English unless the test changes it.
- Gateway store is initialized after setup, but gateway is not auto-started.
This keeps the inherited ClawX regression suite valid while production behavior moves to YINIAN.
## 3. Environment Switches
| Variable | Values | Default | Purpose |
|---|---|---:|---|
| `YINIAN_API_BASE_URL` | URL | unset | Enables HTTP control plane. When unset, app uses mock mode. |
| `CLAWX_LEGACY_AUTOSTART` | `1` / unset | unset | Restores old main-process gateway auto-start for debugging. Production YINIAN keeps gateway deferred until login. |
| `CLAWX_E2E` | `1` / unset | unset | Enables E2E compatibility mode. Used by Playwright fixtures. |
| `CLAWX_E2E_SKIP_SETUP` | `1` / unset | unset | Adds `e2eSkipSetup=1` for tests that bypass setup. |
## 4. Storage Boundary
YINIAN uses a separate Electron store namespace:
- Store name: `yinian`
- Helper: `electron/yinian/storage.ts`
Stored data:
- `session`: persisted YINIAN session metadata.
- `configs`: config snapshots keyed by hotel id.
- `skillRegistryByHotel`: local skill registries keyed by hotel id.
Important constraints:
- Renderer never reads tokens directly.
- Renderer accesses auth/config/skill data only through `window.yinian`.
- Mock mode persists enough session data to restore the local demo.
- HTTP mode keeps `accessToken` in memory and reserves persistence for `refreshToken`.
- Logout clears session, current hotel config, and current hotel skill registry through the control plane.
## 5. Public Renderer API
The preload exposes `window.yinian`:
```ts
window.yinian.auth.restoreSession()
window.yinian.auth.getSessionState()
window.yinian.auth.loginWithSms(input)
window.yinian.auth.loginWithPassword(input)
window.yinian.auth.logout()
window.yinian.app.getConfig()
window.yinian.app.switchHotel(hotelId)
window.yinian.skills.sync()
window.yinian.skills.listLocal()
window.yinian.skills.getRegistry(hotelId?)
```
Shared types live in `shared/yinian.ts`.
## 6. Module Inventory
### Electron Main
- `electron/main/ipc/yinian.ts`
- IPC handlers for auth, config, hotel switching, skill sync, and registry reads.
- `electron/yinian/control-plane.ts`
- Chooses mock or HTTP control plane.
- `electron/yinian/mock-control-plane.ts`
- Local demo implementation with persisted session/config/registry.
- `electron/yinian/http-control-plane.ts`
- HTTP implementation scaffold for server integration.
- `electron/yinian/storage.ts`
- YINIAN-specific storage namespace.
- `electron/main/index.ts`
- Defers gateway auto-start.
- Adds E2E renderer query parameters.
- `electron/preload/index.ts`
- Exposes `window.yinian`.
### Renderer
- `src/stores/yinian.ts`
- Auth/session/config store.
- Restores session on boot.
- Refreshes config and skill registry after login and hotel switch.
- `src/stores/yinian-skills.ts`
- Local registry and skill sync store.
- `src/pages/YinianLogin/`
- Login UI.
- `src/pages/Today/`
- M1 hotel home surface.
- `src/pages/YinianSkills/`
- Skill registry and sync surface.
- `src/components/layout/YinianTenantBar.tsx`
- Current hotel switcher and logout.
- `src/App.tsx`
- YINIAN auth gate, production `/today`, E2E compatibility paths.
### Workspace Packages
- `packages/kernel-core`
- `packages/kernel-context`
- `packages/kernel-adapter-openclaw`
- `packages/skill-spec`
- `packages/skills-hotel-core`
- `packages/ui-kit`
These are M1 boundary scaffolds. They are intentionally thin and should harden during M2/M3.
### Tests
- `tests/unit/yinian-control-plane.test.ts`
- `tests/unit/yinian-store.test.ts`
- `tests/unit/yinian-skills-store.test.ts`
Inherited ClawX unit and E2E tests are still expected to pass.
## 7. Verification Baseline
Last known green run:
```bash
pnpm run typecheck
pnpm run test
pnpm run test:e2e
```
Results:
- Typecheck: passed.
- Unit tests: 89 files, 572 tests passed.
- E2E: 26 passed, 1 skipped.
Known non-blocking warnings:
- Vitest `MaxListenersExceededWarning`.
- Vite dynamic/static import chunk warnings.
- Vite large chunk warning.
- Playwright/Electron `NO_COLOR` ignored because `FORCE_COLOR` is set.
## 8. M2 Entry Points
Recommended next steps:
1. Define real server contract v0.
2. Upgrade Today page into a pilot operations cockpit.
3. Expand Skills Manager status model and UI.
4. Start design system consolidation across Login, Today, Skills, and tenant switcher.
See `task_plan.md` for the active phased plan.
The first draft of the server contract now lives in `docs/SERVER_CONTRACT_V0.md`.

256
docs/PILOT_QA.md Normal file
View File

@@ -0,0 +1,256 @@
# YINIAN Desktop Pilot QA
> Updated: 2026-04-26
> Scope: M1/M2 pilot demo package for B-end business desktop flow
## 1. Pilot Scope
This pilot validates the desktop product loop that is already implemented:
- Login-first YINIAN entry.
- Session restore and logout cleanup.
- Single workspace/service context in the sidebar.
- Customer-facing sidebar focused on the service Dashboard card, `快速使用`, `对话`, and `设置`.
- Knowledge Base v0 upload/list/search surface.
- Service-managed model/Agent/channel/schedule configuration notice in Settings.
- Config sync in mock or HTTP mode.
- Today operations cockpit.
- Workspace-scoped local skill registry.
- Skills Manager sync/status flow.
- Gateway start after authenticated workspace context.
- Legacy ClawX/OpenClaw compatibility in E2E mode.
Out of scope for this pilot:
- Real skill bundle download.
- Signature verification and unpacking.
- Real OTA/PMS task execution.
- Real payment, provisioning, or account administration.
- Customer-specific branding beyond current YINIAN visual system pass.
## 2. Environment Modes
### Mock Mode
Default mode. Do not set `YINIAN_API_BASE_URL`.
Use this for product walkthroughs, local QA, and stakeholder demos before the NIANXX server is ready.
### HTTP Mode
Set `YINIAN_API_BASE_URL` to enable server-backed control plane calls:
```bash
YINIAN_API_BASE_URL=https://api.example.com pnpm run dev
```
HTTP mode expects the contract documented in `docs/SERVER_CONTRACT_V0.md`.
### E2E Compatibility Mode
Playwright uses `CLAWX_E2E=1` through test fixtures. In this mode, legacy ClawX setup/main flows stay available so inherited regression tests remain meaningful.
## 3. Demo Script
Use mock mode unless the server is explicitly being validated.
1. Start the app:
```bash
pnpm run dev
```
2. Confirm first screen is YINIAN login.
3. Login with mock SMS credentials:
- Phone: `13800000000`
- Code: `123456`
4. Confirm the app lands on `/today`.
5. Review Today cockpit:
- Service name and login user are visible.
- Clicking the sidebar service card returns to Today/Dashboard.
- Application readiness cards render.
- Status queue explains app sync/update/failed states.
- Application status board merges server entitlements and local registry.
6. Open `/skills`.
7. Confirm Skills Manager:
- Entitlement count is visible.
- Local registry count is visible.
- Empty registry warning appears before sync when relevant.
- Each skill explains its current state.
8. Click `同步 Skills`.
9. Confirm:
- Sync completes without leaving the page.
- Skill cards update to installed/skipped/updated states.
- Today page reflects local registry status after returning.
10. Open `知识库`.
11. Confirm:
- Upload entry is visible.
- Empty state is readable before upload.
- Search and document list area are present.
12. Hover `历史会话`.
13. Confirm:
- History popover opens.
- Clicking `历史会话` opens the latest session when history exists, otherwise opens Chat.
14. Open Settings.
15. Confirm:
- `账号与组织` shows the current service and user.
- `同步组织配置` refreshes config for the current service.
- Model, Agent, channel, and scheduling-policy configuration are shown as server-managed capabilities.
- Today and Skills stay scoped to the same workspace context.
16. Click Settings `退出登录`.
13. Confirm:
- App returns to `/login`.
- Current workspace config and local registry context are cleaned for the logged-out session.
## 4. QA Checklist
### Auth And Session
- [ ] Login succeeds in mock mode.
- [ ] App restores a valid mock session after relaunch.
- [ ] Logout returns to `/login`.
- [ ] Logout clears current workspace config and current business skill registry context.
- [ ] HTTP mode restore calls `/auth/refresh` when a persisted refresh token exists.
- [ ] HTTP mode does not persist access tokens.
### Workspace Context
- [ ] Sidebar service context appears only after authentication.
- [ ] No workspace switcher is visible in the product shell.
- [ ] Settings sync refreshes config for the current service.
- [ ] Today and Skills stay scoped to the current workspace registry.
### Sidebar And Settings IA
- [ ] Production sidebar service card opens Today/Dashboard.
- [ ] Production sidebar quick-use area shows `Skills`, `定时任务`, and `知识库`.
- [ ] Chat area shows `新对话` and `历史会话`.
- [ ] Hovering `历史会话` reveals the history popover.
- [ ] Models, Agents, Channels, and Dev Console are not visible in normal customer navigation.
- [ ] Account logout lives in Settings, not as a primary sidebar action.
- [ ] Settings explains that model/provider strategy, Agent orchestration, channels, and scheduling policy are managed by the service side.
### Knowledge Base
- [ ] Knowledge page opens from the sidebar.
- [ ] Upload button opens the system file picker.
- [ ] Uploaded files appear in the local document list.
- [ ] Search filters the visible document list.
- [ ] Empty state distinguishes "no files yet" from "no search matches".
### Gateway Boundary
- [ ] Gateway does not start before login in production mode.
- [ ] Gateway starts once after authenticated workspace context exists.
- [ ] `CLAWX_LEGACY_AUTOSTART=1` restores the old ClawX startup behavior for debugging.
- [ ] E2E compatibility mode keeps legacy setup and Chat defaults available.
### Today
- [ ] Today renders without config errors after login.
- [ ] Empty states are visible when there are no pending items, skills, or actions.
- [ ] Skill status labels are clear:
- `已开通未同步`
- `已安装`
- `已更新`
- `已是最新`
- `有更新`
- `已禁用`
- `同步失败`
- [ ] Refresh button updates config without triggering duplicate gateway starts.
### Skills Manager
- [ ] Empty entitlement state appears when an organization has no opened skills.
- [ ] Empty local registry state is distinct from no entitlements.
- [ ] Sync first install marks skills as installed/updated.
- [ ] Syncing the same manifest again marks same-version skills as skipped.
- [ ] Failed sync surfaces an error and retry affordance.
- [ ] Disabled entitlements cannot run.
- [ ] Version drift is shown as `有更新`.
### Visual System
- [ ] Login, Today, Skills, and sidebar feel like one product.
- [ ] Primary actions use the YINIAN navy treatment.
- [ ] Status colors are restrained and readable.
- [ ] Panels use 8px radius or less.
- [ ] Text does not overflow buttons, cards, or status badges at common desktop widths.
- [ ] No marketing-style hero page appears after authentication.
- [ ] Visual smoke screenshots are refreshed for Login, Today, Skills, Knowledge, and Settings.
## 5. Verification Commands
Run this full gate before a guided demo:
```bash
pnpm run typecheck
pnpm run test
pnpm run build:vite
pnpm run test:e2e
```
Last known green verification:
- Typecheck: passed.
- Unit tests: 89 files, 572 tests passed.
- Vite build: passed.
- E2E: 26 passed, 1 skipped.
- Visual smoke: Login, Today, Skills, Knowledge, and Settings passed.
Known non-blocking warnings:
- Vitest `MaxListenersExceededWarning`.
- Vite dynamic/static import chunk warnings.
- Vite large chunk warning.
- Playwright/Electron `NO_COLOR` ignored because `FORCE_COLOR` is set.
## 6. Packaging Notes
For local macOS pilot packaging, prefer:
```bash
pnpm run package:mac:local
```
This skips preinstalled skills and is more suitable for a fast local artifact. Full release packaging should use the platform-specific `package:*` scripts after dependency and signing decisions are settled.
Do not treat the current pilot build as production-ready until:
- Real server auth is validated end to end.
- Real bundle download and signature verification are implemented.
- The installer signing/notarization path is finalized.
- Customer-specific privacy and logging requirements are reviewed.
## 7. Known Issues And Product Gaps
- Today uses config and local registry as its data source; real PMS/OTA/task data is not connected yet.
- Skills sync consumes manifest metadata only; it does not download, verify, or execute real bundles.
- Knowledge Base v0 is local UI state only; persistence, indexing, permissions, and service sync are not connected yet.
- HTTP control plane contract is still draft v0.
- Device identity is still a placeholder and should become a stable installation id.
- Legacy ClawX configuration pages remain available for compatibility/E2E routes, but should stay hidden from production customer navigation until an admin/developer mode is explicitly designed.
- Bundle size warnings remain from the inherited app structure.

2311
docs/PRD.md Normal file

File diff suppressed because it is too large Load Diff

243
docs/SERVER_CONTRACT_V0.md Normal file
View File

@@ -0,0 +1,243 @@
# YINIAN Server Contract v0
> Updated: 2026-04-26
> Status: Draft contract for M2 integration
> Client switch: set `YINIAN_API_BASE_URL` to enable HTTP mode. Without it, desktop uses mock mode.
## 1. Transport
- Base URL: `YINIAN_API_BASE_URL`
- Content type: `application/json`
- Auth header: `Authorization: Bearer <accessToken>`
- Desktop header: `X-YINIAN-App-Version: 0.1.0`
- Timestamps: Unix milliseconds.
- Field casing: server should prefer `camelCase`; desktop accepts key `snake_case` aliases for M1/M2 migration.
## 2. Error Shape
Non-2xx responses should return:
```json
{
"error": {
"code": "SESSION_EXPIRED",
"message": "Session expired"
}
}
```
The desktop currently surfaces `error.message` and falls back to `YINIAN API request failed: <status>`.
Recommended error codes:
- `INVALID_CREDENTIALS`
- `INVALID_SMS_CODE`
- `SESSION_EXPIRED`
- `WORKSPACE_FORBIDDEN`
- `CONFIG_UNAVAILABLE`
- `SKILLS_MANIFEST_UNAVAILABLE`
## 3. Device Payload
Login requests include:
```json
{
"device": {
"device_id": "dev_local_electron",
"platform": "darwin",
"app_version": "0.1.0",
"machine_name": "YINIAN Desktop"
}
}
```
The exact `device_id` is a placeholder in M2 and should be replaced by a stable desktop installation id later.
## 4. Auth
### POST `/auth/login/sms`
Request:
```json
{
"phone": "13800000000",
"code": "123456",
"device": {}
}
```
Response:
```json
{
"accessToken": "access_demo",
"refreshToken": "refresh_demo",
"accessTokenExpiresAt": 1777188600000
}
```
### POST `/auth/login/password`
Request:
```json
{
"account": "ops@example.com",
"password": "secret",
"device": {}
}
```
Response shape is the same as SMS login.
### POST `/auth/refresh`
Request:
```json
{
"refreshToken": "refresh_demo",
"device": {}
}
```
Response:
```json
{
"accessToken": "access_refreshed",
"refreshToken": "refresh_rotated",
"accessTokenExpiresAt": 1777189500000
}
```
M2 desktop persists only `refreshToken`; `accessToken` remains memory-only.
### GET `/auth/me`
Headers:
```http
Authorization: Bearer access_demo
```
Response:
```json
{
"user": {
"id": "user_ops_001",
"name": "王管理员",
"phone": "13800000000",
"email": "ops@example.com",
"avatar": "https://cdn.example.com/avatar.png"
},
"hotels": [
{
"id": "workspace_hangzhou_ops",
"name": "智念企业组织空间",
"brand": "智念"
}
],
"currentHotelId": "workspace_hangzhou_ops",
"accessTokenExpiresAt": 1777188600000
}
```
## 5. Config Sync
### GET `/config/sync?hotel_id=<hotelId>`
Response:
```json
{
"serverTime": 1777188000000,
"hotel": {
"id": "workspace_hangzhou_ops",
"name": "智念企业组织空间",
"brand": "智念"
},
"entitlements": [
{
"skillId": "daily-report",
"name": "日报生成助手",
"version": "1.0.0",
"enabled": true,
"category": "reporting",
"triggers": ["scheduled", "manual"],
"lastRunAt": 1777184400000
}
],
"notificationChannels": [
{
"id": "wechat_ops",
"kind": "wecom",
"label": "业务通知群",
"recipient": "room_001",
"enabled": true,
"source": "nianxx"
}
],
"featureFlags": {
"skillsSync": true,
"advancedSettings": false
},
"uiPolicy": {
"defaultPage": "today",
"showAdvancedSettings": false
}
}
```
## 6. Skills Manifest
### GET `/skills/manifest?hotel_id=<hotelId>`
Response:
```json
{
"serverTime": 1777188000000,
"hotelId": "workspace_hangzhou_ops",
"manifestVersion": "2026.04.26.1",
"skills": [
{
"skillId": "data-check",
"name": "数据检查助手",
"version": "1.2.0",
"enabled": true,
"bundleSha256": "sha256-demo",
"bundleUrl": "https://cdn.example.com/skills/data-check-1.2.0.tgz"
},
{
"skillId": "customer-reply-helper",
"name": "客户回复助手",
"version": "0.9.0",
"enabled": false,
"bundleSha256": "sha256-disabled"
}
]
}
```
M2 desktop consumes manifest metadata only. It does not download `bundleUrl`, verify signatures, or unpack bundles yet.
## 7. Client Normalization Rules
The desktop accepts:
- `camelCase` and `snake_case` aliases for known fields.
- Missing `user.name` as `未命名用户`.
- Missing hotel fields with safe empty/default values.
- Unknown skill categories as `ops-automation`.
- Unknown trigger values are ignored.
- Missing `uiPolicy.defaultPage` defaults to `today`.
- Missing arrays default to `[]`.
Malformed server responses should not break the shell, but required auth fields still fail fast:
- Missing `accessToken` after login or refresh throws `服务端未返回 access token`.
- Authenticated config/skills calls without a session throw `请先登录`.

47
docs/START_HERE.md Normal file
View File

@@ -0,0 +1,47 @@
# YINIAN Desktop 项目启动说明
## 当前基线
- 上游基础ClawX `0.3.11`
- 产品定位:快速使用 AI Agent 桌面客户端
- 当前分支:`yinian-main`
- PRD`docs/PRD.md`
- M1 交付说明:`docs/M1_HANDOFF.md`
- 服务端契约草案:`docs/SERVER_CONTRACT_V0.md`
- Pilot QA 清单:`docs/PILOT_QA.md`
## 第一阶段目标
1. 保留 ClawX 基础功能可运行。
2. 接入 YINIAN 登录态和工作空间上下文。
3. 建立 `kernel-core` 端口契约。
4. 建立 OpenClaw Adapter 的最小实现。
5. 替换首屏为“今日”工作台。
6. 跑通一个服务端下发 skill 的闭环。
## 工程边界
- Renderer 不直接访问 NIANXX 服务端。
- Renderer 不读取 token。
- 客户端不保存第三方模型 API key。
- Skill bundle 必须签名后才能加载。
- OpenClaw 相关调用必须逐步收口到 `packages/kernel-adapter-openclaw`
## 常用命令
```bash
pnpm install
pnpm run typecheck
pnpm run test
pnpm run build:vite
pnpm run test:e2e
pnpm run dev
```
## 关键环境变量
| 变量 | 用途 |
|---|---|
| `YINIAN_API_BASE_URL` | 配置后启用真实 HTTP control plane未配置时使用 mock。 |
| `CLAWX_LEGACY_AUTOSTART=1` | 调试时恢复 ClawX 旧的 Gateway 启动行为。 |
| `CLAWX_E2E=1` | Playwright E2E 兼容模式,保留旧 setup/main flow。 |

View File

@@ -0,0 +1,34 @@
# ADR 0001: ClawX Fork 与 YINIAN 分层改造策略
## 状态
Accepted
## 背景
智念桌面端基于 ClawX fork目标用户从开发者切换为酒店前台、运营、店长和 NIANXX 实施人员。ClawX 提供了 Electron 壳、OpenClaw Gateway 生命周期、技能、渠道、Cron 等基础能力,但其产品心智仍是通用开发者工具。
## 决策
v1 阶段采取渐进式改造:
1. 保留 ClawX 根应用结构、构建链路和 Gateway 管理能力。
2. 新增 `packages/*` 作为 YINIAN 平台边界层。
3. 在 Renderer 中逐步从通用 ClawX 页面迁移到酒店运营工作台。
4. 在 Main 中新增 Auth、Config Sync、Skill Manager、Kernel Lifecycle 安全增强。
5. 在 ClawX 上游变更稳定后,再评估是否迁移到 `apps/desktop` 目录结构。
## 后果
好处:
- 第一阶段可快速运行和验证。
- 保留上游同步能力。
- YINIAN 领域边界可以逐步变硬。
代价:
- 初期目录结构不是最终理想形态。
- 一段时间内 ClawX 与 YINIAN 命名会共存。
- 需要 CI 逐步补上依赖方向约束。