Files
NianToB/docs/SERVER_CONTRACT_V0.md

244 lines
4.7 KiB
Markdown

# 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 `请先登录`.