feat: 實作 bear CLI PAT 模式 MVP(login/whoami/token/logout/status)

- 新增 Elixir + Mix escript 專案(Req 0.5+、Jason)
- login --token <PAT>:以 PAT 驗證 /userinfo 後寫入憑證檔(0600)
- whoami:GET /userinfo 顯示身分 claims(支援 --json)
- token:印出 access token 供 pipe(--refresh 在 PAT 模式忽略)
- logout:清除本機憑證;status:純本機判定
- 憑證檔採原子寫入(rename)、0600、O_CREAT|O_EXCL 防 symlink
- 全域選項:--issuer/--config/--json/-v/-h/--version
- 29 個單元測試;mix precommit(compile --warnings-as-errors + format + test)
- 更新 README.md 與 docs/commands.md 反映 PAT 模式 MVP

Device Flow 登入待伺服器端 P1(bear 倉庫)完成後再接。issue #2
This commit is contained in:
2026-08-31 11:14:12 +08:00
parent 14edb84a59
commit 870011f8a7
15 changed files with 1118 additions and 8 deletions
+35 -6
View File
@@ -1,7 +1,7 @@
# Bear CLI 指令規劃(Command Spec)
> 狀態:規劃草案(對應 issue #19「創建 bear cli 倉庫」)
> 範圍:**本文件只規劃指令介面,不實作程式碼**。
> 實作:PAT 模式 MVP 已落地(issue #2),本文件為指令介面規格;Device Flow 待伺服器端 P1。
> 登入採用的 OIDC Device Authorization Grant(RFC 8628)細節,見 bear 倉庫 `docs/cli-feature-plan.md`(issue #17)。
---
@@ -34,18 +34,27 @@
### 3.1 `bear login`
啟動 Device Flow 登入,取得並保存 Token。
登入並保存 Token。目前有兩種模式:
- **PAT 模式(現行)**:`bear login --token <PAT>`,以個人存取權杖驗證 `GET /userinfo` 後保存 token,全程免瀏覽器。
- **Device Flow(待 P1)**:`bear login`,啟動 Device Authorization Grant(RFC 8628),需伺服器端 P1 完成。
```
bear login [--scope "openid profile email"] [--client-id ID]
bear login [--token <PAT>] [--scope "openid profile email"] [--client-id ID]
```
| 選項 | 預設 | 說明 |
|------|------|------|
| `--scope` | `openid profile email` | 請求的 scope(空白分隔) |
| `--client-id` | 設定檔內值 | OIDC client id |
| `--token` | — | PAT 模式:直接提供個人存取權杖(`-` 表示從 stdin 讀取) |
| `--scope` | `openid profile email` | Device Flow:請求的 scope(空白分隔) |
| `--client-id` | 設定檔內值 | Device Flow:OIDC client id |
**行為**:
**PAT 模式行為**:
1. 向 `{issuer}/userinfo` 送 `Authorization: Bearer <PAT>` 驗證 token。
2. 成功 → 寫入憑證檔(0600),印出 `已登入:<email>`;失敗(401)→ 印出錯誤,退出碼 `1`;網路錯誤 → 退出碼 `6`。
**Device Flow 行為**:
1. 讀取設定 → 向 `{issuer}/.well-known/openid-configuration` 取 `device_authorization_endpoint`(**動態發現,不寫死網址**)。
2. `POST /device_authorization`(`client_id` + `scope`)。
@@ -225,3 +234,23 @@ bear apps
| `token --refresh` | `POST /token`(refresh_token) |
| `logout` | `POST /revoke` |
| `apps`(P3) | 待新增「公開應用清單」API |
---
## 9. PAT 模式與原規格差異(現行實作)
目前實作為 PAT 模式 MVP,與上方 Device Flow 規格有以下差異:
| 指令 | 原規格(Device Flow) | PAT 模式現行行為 |
|------|----------------------|------------------|
| `login` | 裝置碼流程 | `--token <PAT>` 驗證 `/userinfo` 後保存 |
| `whoami` | `GET /userinfo`(refresh 輪轉) | `GET /userinfo`(Bearer PAT,無輪轉) |
| `token --refresh` | refresh token 輪轉換新 | 無 refresh token,`--refresh` 忽略並警告 |
| `logout` | `POST /revoke`(撤銷 refresh token) | 僅清除本機憑證;PAT 需至網頁撤銷 |
| `status` | 顯示 access token 剩餘秒數 | 顯示 PAT 模式與 issuer(PAT 無效期資訊) |
憑證檔結構(PAT 模式):
```json
{"issuer": "https://alterminal.com", "access_token": "<PAT>", "email": "..."}
```