Files
bear-cli/docs/commands.md
T
alex 3f5965bf36 docs: CLI 設計加入個人存取權杖(PAT)登入(issue #1)
- bear login 新增 --token <PAT>:以個人存取權杖免瀏覽器登入
- 同步更新 whoami/token/logout/status 於 PAT 模式下的行為
- 憑證檔規格區分 Device Flow 與 PAT、退出碼、指令↔端點對應
- 新增 BEAR_TOKEN 環境變數(一次性/CI 用)與開放問題
2026-08-30 13:41:20 +08:00

258 lines
9.3 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)。
> 另支援以**個人存取權杖(PAT)**登入(bear 已於 profile 頁提供 PAT 建立/列表/撤銷,`/userinfo` 接受 PAT 作為 Bearer)。
---
## 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`、`BEAR_TOKEN`)> 設定檔 > 內建預設。
> `BEAR_TOKEN` 為一次性/CI 用的個人存取權杖(PAT);提供後優先於本機憑證檔,但不寫入憑證檔。
---
## 3. 指令詳述
### 3.1 `bear login`
登入 Bear,取得並保存憑證。預設走 **Device Flow**;亦可用 `--token` 直接以**個人存取權杖(PAT)**登入。
```
bear login [--scope "openid profile email"] [--client-id ID]
bear login --token <PAT>
```
| 選項 | 預設 | 說明 |
|------|------|------|
| `--scope` | `openid profile email` | Device Flow 請求的 scope(空白分隔) |
| `--client-id` | 設定檔內值 | OIDC client id |
| `--token <PAT>` | — | 直接以個人存取權杖登入(跳過 Device Flow) |
**行為(Device Flow,未給 `--token`)**:
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>`。
**行為(PAT,給 `--token <PAT>`)**:
1. 以該 PAT 作為 Bearer 呼叫 `GET /userinfo` 驗證可用性並取得身分。
2. 成功 → 將 PAT 直接寫入憑證檔(0600),印出 `已登入:<email>`(無 refresh token)。
3. 失敗(401/無效或已撤銷)→ 提示 PAT 無效,不寫入任何資料。
**退出碼**:`0` 成功;`3` PAT 無效或已撤銷;`4` 等待逾時(`expires_in`);`5` 使用者拒絕;`6` 網路/伺服器錯誤。
**`--json` 輸出範例(成功)**:
```json
{"ok": true, "email": "alice@example.com", "expires_in": 3600}
```
PAT 模式下 `expires_in` 依權杖到期日回傳;無到期日則省略該欄位。
---
### 3.2 `bear whoami`
顯示目前登入身分(`GET /userinfo`,Bearer access token)。
```
bear whoami
```
**行為**:讀取憑證——Device Flow 讀取快取的 access token(過期則先用 refresh token 輪轉換新);PAT 直接使用該 token——呼叫 `/userinfo`,印出 claims(name、email、picture 等)。`/userinfo` 同時接受 OIDC access token 與 PAT 作為 Bearer。
**退出碼**:`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` 換新,更新快取後印出。
- **PAT 模式**:直接印出該 PAT(無 refresh token;`--refresh` 為無效選項,報用法錯誤 `2`)。
- 輸出**只含 token 本體**(單行、無前綴),方便 pipe;診斷訊息一律走 stderr。
**退出碼**:`0` 成功;`3` 未登入;`1` refresh 失敗(token 已撤銷或過期,需重新 login)。
---
### 3.4 `bear logout`
撤銷 refresh token 並清除本機憑證。
```
bear logout
```
**行為**:
- Device Flow:呼叫 `POST /revoke`(撤銷 refresh token),無論伺服器回應如何,皆刪除本機憑證檔。
- PAT:僅刪除本機憑證檔(PAT 的撤銷需於網頁 `/profile/tokens` 進行,CLI 不提供撤銷)。
**退出碼**:`0` 成功;`6` 伺服器呼叫失敗(但本機憑證已清除)。
---
### 3.5 `bear status`
顯示登入狀態與 token 剩餘效期(不呼叫網路,純本機判定)。
```
bear status
```
**行為**:讀取本機憑證,印出登入與否、憑證類型(Device Flow/PAT)、access token 剩餘秒數、refresh token 是否存在。
**退出碼**:`0` 已登入;`3` 未登入。
**輸出範例(Device Flow)**:
```
已登入:alice@example.com
憑證類型:Device Flow
access token 剩餘:3210 秒
refresh token:有
issuer:https://alterminal.com
```
**輸出範例(PAT,無到期日)**:
```
已登入:alice@example.com
憑證類型:Personal Access Token
access token 剩餘:無到期
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` | Device Flow:`refresh_token`+快取 `access_token`/`expires_at`;PAT:`access_token`(無 refresh) | **0600** |
- **Device Flow 只以 refresh token 為主**:access token 短命快取,過期以 refresh token 輪轉換新(沿用 Bear 既有 refresh rotation + 重用偵測)。
- **PAT 直接存 access token**:無 refresh token、無輪轉;效期由權杖本身的 `expires_at` 決定(可無到期日)。
- 寫入憑證檔務必 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 輪轉即可?
5. 是否在 CLI 提供「建立/撤銷 PAT」?目前 PAT 管理僅限網頁(需 session),傾向 CLI 只「使用」不「管理」。
---
## 8. 對應關係
| 本指令 | 對應 OIDC/Bear 端點 |
|--------|----------------------|
| `login` | `/.well-known/openid-configuration` → `POST /device_authorization` → `POST /token`(device_code) |
| `login --token <PAT>` | `GET /userinfo`(Bearer PAT 驗證身分,無 OIDC 端點) |
| `whoami` | `GET /userinfo`(接受 OIDC access token 或 PAT) |
| `token --refresh` | `POST /token`(refresh_token,僅 Device Flow) |
| `logout` | `POST /revoke`(僅 Device Flow;PAT 僅本機刪除,撤銷走網頁 `/profile/tokens`) |
| `apps`(P3) | 待新增「公開應用清單」API |