Files
bear-cli/docs/commands.md
T
alex 8cc8c41416 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
2026-08-30 14:24:51 +08:00

8.7 KiB
Raw Blame History

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)。


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

登入並保存 Token。目前有兩種模式:

  • PAT 模式(現行):bear login --token <PAT>,以個人存取權杖驗證 GET /userinfo 後保存 token,全程免瀏覽器。
  • Device Flow(待 P1):bear login,啟動 Device Authorization Grant(RFC 8628),需伺服器端 P1 完成。
bear login [--token <PAT>] [--scope "openid profile email"] [--client-id 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)。

  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 輸出範例(成功):

{"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

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 模式):

{"issuer": "https://alterminal.com", "access_token": "<PAT>", "email": "..."}