AnyBot/docs/protocol.md

118 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 介面與長連線格式
給要改帳號伺服器或寫其他用戶端的人。所有回覆都是 JSON;錯誤是 `{"error": "白話的說明"}` 加上對應的 HTTP 狀態碼。
## 身分
| 誰 | 怎麼證明 | 從哪來 |
|---|---|---|
| 人(手機) | `Authorization: Bearer <session>` | `POST /relay/login` |
| 一台 AnyBot(主機) | `Authorization: Bearer <host token>` | `POST /relay/hosts`(登入後)或 `POST /relay/pair/claim`(配對碼) |
伺服器只存密碼和金鑰的雜湊值。
## 帳號伺服器(relay)
### 帳號
| 方法 | 路徑 | 說明 |
|---|---|---|
| POST | `/relay/signup` | `{user, password, invite}`。第一個帳號不用邀請碼,之後要 `ANYBOT_RELAY_INVITE` |
| POST | `/relay/login` | `{user, password, device}` → `{token}`。錯 8 次停 5 分鐘 |
| POST | `/relay/logout` | 登出這個 session |
| GET | `/relay/me` | 帳號、可用的 AnyBot(含 `online`、`uses`、`hands`)、登入過的裝置、有沒有備份 |
### 主機
| 方法 | 路徑 | 說明 |
|---|---|---|
| POST | `/relay/hosts` | (人)`{name}` → `{id, token}`。同名的主機會換新金鑰 |
| POST | `/relay/hosts/<id>/remove` | (人)移除主機 |
| GET | `/relay/host/ws` | (主機)長連線,見下面 |
| GET | `/relay/host/next` | (主機)舊式長輪詢:有要求就回 job,25 秒沒有回 204 |
| POST | `/relay/host/reply/<job id>` | (主機)回答舊式 job |
| GET | `/relay/host/peers` | (主機)帳號裡的其他主機 |
| GET/POST | `/relay/host/members` | (主機)團隊成員和角色(admin、member) |
| POST | `/relay/host/notify` | (主機)推播通知到帳號的手機 |
| POST | `/relay/host/sites/<name>` | (主機)發布 Bot 做的網頁,公開在 `/s/<name>/` |
| PUT/GET | `/relay/backup` | 加密備份(伺服器打不開) |
### 配對碼
| 方法 | 路徑 | 說明 |
|---|---|---|
| POST | `/relay/pair` | (人或主機)`{for: 大腦的主機 id}` → `{code, expires_in: 600}` |
| POST | `/relay/pair/claim` | (不用登入)`{code, name}` → `{id, token, user, for, for_name}`。只能用一次;同一個位址錯 8 次停 5 分鐘 |
### 轉送
| 路徑 | 誰 | 轉成 |
|---|---|---|
| `/h/<host>/api/...` | 手機 | 那台 AnyBot 的 `/api/...`,角色是 owner/admin/member |
| `/relay/hosts/<host>/api/sync[/...]` | 同帳號的主機 | 大腦的 `/api/sync...`,角色 `mirror` |
| `/relay/hosts/<host>/api/device/run` | 同帳號的主機 | 手腳的 `/api/device/run`,角色 `device` |
轉送時伺服器附上 `user`、`role` 和 `from`(問的那台主機 id)。對方不在線時立刻回 503「目前離線」;收到了但 35 秒內沒回答,回 504。
## AnyBot 提供的介面(`remote::handle`)
只有經過帳號伺服器(或同一個網路、用手機密碼登入)才到得了。
| 方法 | 路徑 | 角色 | 說明 |
|---|---|---|---|
| GET | `/api/state?since=` | 手機 | Bot 清單、誰在工作、等核准的事(有變化才回,最多等 20 秒) |
| GET | `/api/bots/<id>?since=` | 手機 | 一個 Bot 的訊息 |
| POST | `/api/bots/<id>/<動作>` | 手機 | `send`、`stop`、`answer`、`read`、`new`、`upload`… |
| GET/POST | `/api/screen/<box\|mac>` | 手機 | 看畫面、點擊打字 |
| POST | `/api/device/run` | device | `{tool, args, who}` → `{text, ok, image}`:在這台跑一個工具 |
| GET | `/api/sync?since=&client=` | mirror | 大腦的變化(見 architecture.md) |
| POST | `/api/sync/edit` | mirror | `{ops, client, seq}` → `{ok, applied}` |
| POST | `/api/sync/act` | mirror | `{bot, action, …}`:send、stop、answer、read、new、regenerate、reply、restore |
| POST | `/api/sync/upload/<bot>?name=` | mirror | 原始檔案內容 → `{path}` |
| GET | `/api/sync/file?path=` | mirror | 大腦上的附件(只限 `attachments/` 裡的檔案) |
| POST | `/api/sync/models` | mirror | `{provider}` → `{models}`:用大腦的金鑰取得模型清單 |
## 長連線的格式(`src/wire.rs`)
WebSocket 的二進位訊息,每則是:
```
[4 位元組,表頭長度,big-endian] [表頭 JSON] [內容的原始位元組]
```
內容不做 base64。`z: true` 表示內容用 deflate 壓過;`z_ok: true` 表示問的人看得懂壓過的回答。
| t | 方向 | 表頭欄位 | 意思 |
|---|---|---|---|
| `hello` | AnyBot → 伺服器 | `brain`、`hands` | 這台在看哪個大腦、是不是手腳(手機和設定畫面用) |
| `job` | 伺服器 → AnyBot | `id, method, path, query, user, role, from, z, z_ok` | 給這台的要求 |
| `reply` | AnyBot → 伺服器 | `id, status, ctype, headers, z` | 回答 job |
| `call` | AnyBot → 伺服器 | `id, to, method, path, query, z` | 要問帳號裡的另一台(只能是 `/api/sync…` 或 `/api/device/run`) |
| `ret` | 伺服器 → AnyBot | `id, status, ctype, z` | call 的回答 |
心跳用 WebSocket 的 ping/pong。連線斷掉時,伺服器把還在等的 job 都回 503「斷線了」,AnyBot 把還在等的 call 都回「和帳號伺服器的連線斷了」。
## 同步的改動(`/api/sync/edit` 的 ops)
| op | 欄位 | 說明 |
|---|---|---|
| `set` | `key, value` | 換掉一個設定(`next_*` 計數只會變大) |
| `patch` | `key, patch` | 清單或對照表逐項修改 |
| `merge` | `key, value` | 搬移時補上大腦沒有的(providers、skills、mcp_servers) |
| `bot_add` | `bot` | 新增 Bot(id 撞到會換) |
| `bot_del` | `id` | 刪除(主要 Bot 不能刪) |
| `bot_set` | `id, key, value` | 改 Bot 的一個欄位 |
| `bot_patch` | `id, key, patch` | 逐項修改 Bot 的清單欄位(例行任務保留大腦記錄的執行時間) |
| `bot_read` | `id` | 標為已讀 |
| `bot_order` | `ids` | Bot 的順序 |
`patch` 的格式:
```json
{"value": …} 換掉整個值
{"map": {"put": {"鍵": 值}, "del": ["鍵"]}} 對照表
{"list": {"put": [項目], "del": [鍵], "order": [鍵]}} 清單;鍵是項目的 id,沒有就用 name,純值就是值本身
```
清單裡的項目沒有 id 或名字、或鍵重複時,送整個值。