134 lines
7.6 KiB
Markdown
134 lines
7.6 KiB
Markdown
# 介面與長連線格式
|
||
|
||
給要改帳號伺服器或寫其他用戶端的人。所有回覆都是 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。
|
||
|
||
看 Bot 的電腦(VNC,一條串流,不是一問一答):
|
||
|
||
| 路徑 | 誰 | 說明 |
|
||
|---|---|---|
|
||
| `GET /screen`、`GET /novnc.js` | 任何人 | 看畫面的網頁和 noVNC 1.6.0(MPL-2.0) |
|
||
| `POST /relay/screen-ticket` `{host}` | 同帳號的主機(host token) | → `{ticket}`:10 分鐘內有效,讓這台的 App 開瀏覽器看那台的電腦 |
|
||
| WebSocket `/relay/vnc/<host>?t=` 或 `?ticket=` | 擁有者、管理員 | 二進位訊息是 VNC;文字訊息是要打進去的字。伺服器把它接到那台 AnyBot 的長連線(下面的 `v*`) |
|
||
|
||
## 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/mac` | 手機 | 看這台 Mac 的畫面、點擊打字(開了「從手機操作」才可以) |
|
||
| GET | `/screen`、`/novnc.js` | 任何人 | 看 Bot 的電腦的網頁(noVNC);本身沒有資料 |
|
||
| WebSocket | `/api/vnc?t=` | 手機(同一個網路) | VNC,直接到這台的 Bot 的電腦(見 vnc.rs) |
|
||
| GET | `/api/sync/screen` | mirror | Bot 的電腦的小縮圖(JPEG,寬 320) |
|
||
| POST | `/api/sync/screen` | mirror | `{kind: "hold", on}`(接管時 Bot 等你)、`{kind: "wake"}` |
|
||
| 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, headers` | call 的回答 |
|
||
| `vopen` | 伺服器 → AnyBot | `sid` | 有人要看這台的 Bot 的電腦:接上它的 VNC(AnyBot 先回 VNC 的開頭) |
|
||
| `vdata` | 雙向 | `sid` | VNC 的位元組(內容) |
|
||
| `vtext` | 伺服器 → AnyBot | `sid` | 看的人打的字(UTF-8),貼進 Bot 的電腦 |
|
||
| `vclose` | 雙向 | `sid`(AnyBot 送的有 `why`) | 結束這條 |
|
||
|
||
兩邊的迴圈有東西在跑時每 2 毫秒、安靜時每 40 毫秒看一次有沒有要讀的(`wire::Pace`),之後立刻送出排隊的訊息。心跳用 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 或名字、或鍵重複時,送整個值。
|