Files
bear-cli/docs/commands.md
T
alex 14edb84a59 docs: 新增 bear CLI 指令規劃(issue #19)
- 建立獨立倉庫 bear-cli,用於實現 bear 的終端機 CLI 客戶端
- README.md:專案簡介、指令總覽、全域選項、憑證與里程碑
- docs/commands.md:完整指令規格(login/whoami/token/logout/status/apps)
  - 沿用 RFC 8628 Device Authorization Grant 方向(見 bear issue #17 設計文件)
  - 定義全域選項、各指令行為、退出碼、設定/憑證檔與安全考量
- 依需求「先規劃指令、暫不實作」,程式碼留待 P2 起各自開 issue 再進行
2026-08-29 17:25:53 +08:00

228 lines
7.1 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.
# Bear CLI 指令規劃(Command Spec)
> 狀態:規劃草案(對應 issue #19「創建 bear cli 倉庫」)
> 範圍:**本文件只規劃指令介面,不實作程式碼**。
> 登入採用的 OIDC Device Authorization Grant(RFC 8628)細節,見 bear 倉庫 `docs/cli-feature-plan.md`(issue #17)。
---
## 1. 命名與呼叫慣例
- 可執行檔名:`bear`(Linux 終端機呼叫 `bear <command>`)。
- 指令採「動詞在前」的扁平結構,不設多層子指令(首版維持簡單,後續需要再擴充)。
- 全部指令支援 `--json` 輸出機器可讀結果;人類可讀輸出為預設。
- 輸出語言跟隨系統 Locale,優先繁體中文(zh_TW),英文(en)為後備。
---
## 2. 全域選項(Global Options)
| 選項 | 型別 | 預設 | 說明 |
|------|------|------|------|
| `--issuer URL` | string | `https://alterminal.com` | Bear(OIDC Provider)位址;可被設定檔覆寫 |
| `--config PATH` | string | `~/.config/bear/config.json` | 設定檔路徑 |
| `--json` | bool | `false` | 機器可讀輸出(JSON) |
| `-v, --verbose` | bool | `false` | 顯示詳細日誌(含 HTTP 請求摘要,但不含 token) |
| `-h, --help` | bool | — | 顯示說明 |
| `--version` | bool | — | 顯示版本後離開 |
優先序:CLI flag > 環境變數(`BEAR_ISSUER`)> 設定檔 > 內建預設。
---
## 3. 指令詳述
### 3.1 `bear login`
啟動 Device Flow 登入,取得並保存 Token。
```
bear login [--scope "openid profile email"] [--client-id ID]
```
| 選項 | 預設 | 說明 |
|------|------|------|
| `--scope` | `openid profile email` | 請求的 scope(空白分隔) |
| `--client-id` | 設定檔內值 | OIDC client id |
**行為**:
1. 讀取設定 → 向 `{issuer}/.well-known/openid-configuration` 取 `device_authorization_endpoint`(**動態發現,不寫死網址**)。
2. `POST /device_authorization`(`client_id` + `scope`)。
3. 終端印出:
```
請在瀏覽器開啟:https://alterminal.com/device
輸入代碼:ABCD-EFGH
```
若終端支援,同步印出 QR(掃碼直達 `verification_uri_complete`)。
4. 以 `interval` 週期輪詢 `POST /token`(`grant_type=device_code`):
- `authorization_pending` → 繼續等待;
- `slow_down` → 加大輪詢間隔;
- `access_denied` → 中止並提示;
- 成功 → 寫入憑證檔(0600),印出 `已登入:<email>`。
**退出碼**:`0` 成功;`4` 等待逾時(`expires_in`);`5` 使用者拒絕;`6` 網路/伺服器錯誤。
**`--json` 輸出範例(成功)**:
```json
{"ok": true, "email": "alice@example.com", "expires_in": 3600}
```
---
### 3.2 `bear whoami`
顯示目前登入身分(`GET /userinfo`,Bearer access token)。
```
bear whoami
```
**行為**:讀取快取的 access token(過期則先用 refresh token 輪轉換新),呼叫 `/userinfo`,印出 claims(name、email、picture 等)。
**退出碼**:`0` 成功;`3` 未登入。
**輸出範例**:
```
sub : 0190...uuid
name : Alice Chen
email : alice@example.com
picture : https://...
```
---
### 3.3 `bear token [--refresh]`
印出 access token 到 stdout,供 pipe 給其他工具(如 `curl`、腳本)。
```
bear token [--refresh]
```
| 選項 | 說明 |
|------|------|
| `--refresh` | 強制先以 refresh token 輪轉換新 access token |
**行為**:
- 若無 `--refresh` 且快取的 access token 未過期 → 直接印出。
- 若過期(或指定 `--refresh`)→ 以 refresh token 向 `/token` 換新,更新快取後印出。
- 輸出**只含 token 本體**(單行、無前綴),方便 pipe;診斷訊息一律走 stderr。
**退出碼**:`0` 成功;`3` 未登入;`1` refresh 失敗(token 已撤銷或過期,需重新 login)。
---
### 3.4 `bear logout`
撤銷 refresh token 並清除本機憑證。
```
bear logout
```
**行為**:呼叫 `POST /revoke`(撤銷 refresh token),無論伺服器回應如何,皆刪除本機憑證檔。
**退出碼**:`0` 成功;`6` 伺服器呼叫失敗(但本機憑證已清除)。
---
### 3.5 `bear status`
顯示登入狀態與 token 剩餘效期(不呼叫網路,純本機判定)。
```
bear status
```
**行為**:讀取本機憑證,印出登入與否、access token 剩餘秒數、refresh token 是否存在。
**退出碼**:`0` 已登入;`3` 未登入。
**輸出範例**:
```
已登入:alice@example.com
access token 剩餘:3210 秒
refresh token:有
issuer:https://alterminal.com
```
---
### 3.6 `bear apps`(P3,暫緩)
列出「公開應用」清單,供 Launch/跳轉參考。
```
bear apps
```
**前置**:bear 伺服器端需新增「公開應用清單」API(列為 P3,待確認)。
**退出碼**:`0` 成功;`3` 未登入;`6` API 尚未提供。
---
## 4. 退出碼總表
| 碼 | 語意 |
|----|------|
| `0` | 成功 |
| `1` | 一般錯誤(含 refresh 失敗、憑證檔讀寫失敗) |
| `2` | 用法錯誤(未知指令/參數) |
| `3` | 未登入(需先 `bear login`) |
| `4` | 授權等待逾時(authorization_pending 超過 `expires_in`) |
| `5` | 授權被拒絕(access_denied) |
| `6` | 網路/伺服器錯誤 |
| `7` | 設定檔/憑證檔格式損毀 |
---
## 5. 設定檔與憑證檔
| 檔 | 路徑 | 內容 | 權限 |
|----|------|------|------|
| 設定 | `~/.config/bear/config.json` | `issuer`、`client_id`、`scope` | 0644 |
| 憑證 | `~/.local/state/bear/credentials.json` | `refresh_token`(優先)+快取 `access_token`/`expires_at` | **0600** |
- **只以 refresh token 為主**:access token 短命快取,過期以 refresh token 輪轉換新(沿用 Bear 既有 refresh rotation + 重用偵測)。
- 寫入憑證檔務必 0600,且以 `O_CREAT|O_EXCL` 防止 symlink 攻擊。
- 未來可擴充系統 keyring(libsecret / Keychain),避免明文檔。
---
## 6. 錯誤處理與安全
- `authorization_pending`、`slow_down`、`access_denied`、`invalid_grant` 依 RFC 8628 §3.5 顯示對應中文提示。
- 所有診斷訊息走 stderr;`--json` 模式將錯誤以 `{"ok": false, "error": "...", "code": N}` 輸出。
- token、user_code、device_code 一律不入 log、不入 stdout(除 `bear token` 之目的外)、不入版本庫。
- 網路層一律使用 `Req`,遵循 bear 倉庫 HTTP 客戶端慣例。
---
## 7. 開放問題(待確認)
1. CLI 的 OIDC client 採新 `method: "device"`(client auth 為 `none`)或沿用 `PKCE`+JWK?傾向前者(見 bear `cli-feature-plan.md` §5.5)。
2. 發行格式:escript(目標機需 Erlang)或 self-contained release(bakeware/burrito)?
3. `bear apps` 所需的「公開應用清單」API 是否納入 P3?
4. 是否支援 `offline_access` scope,或沿用現行 refresh 輪轉即可?
---
## 8. 對應關係
| 本指令 | 對應 OIDC/Bear 端點 |
|--------|----------------------|
| `login` | `/.well-known/openid-configuration` → `POST /device_authorization` → `POST /token`(device_code) |
| `whoami` | `GET /userinfo` |
| `token --refresh` | `POST /token`(refresh_token) |
| `logout` | `POST /revoke` |
| `apps`(P3) | 待新增「公開應用清單」API |