前言

當我在 Pro / Plus 方案下同時使用 Claude Code 和 Codex 時,很常會碰到速率限制(5 小時額度/每週額度)。一旦撞到,工作就會停下來,所以我一直是邊調整邊使用:在 Codex 和 Claude 之間切換、在模型之間切換,盡量不要碰到額度上限。

不過,要做這個判斷,前提是得知道「現在兩邊各剩多少」。因此我常常得開瀏覽器去確認速率限制。

為了消除這個麻煩,最後我乾脆自己做了一個能在終端機單一窗格中常駐顯示 Claude / Codex 剩餘額度的工具

  • 預設讀者:同時使用 Claude Code / Codex,且覺得確認剩餘額度很麻煩的人
  • 執行環境:Claude Code 2.1.169 / Codex CLI 0.137.0 / macOS 26.5.1(Claude:Pro、Codex:ChatGPT Plus)
  • 完整腳本:放在文末的 gist

反覆確認剩餘額度的日子

如前所述,依照我的使用方式,我需要掌握「5 小時速率限制大概還剩多少」、「每週額度還有多少」、「什麼時候重置」。

  • 平衡使用
  • 快碰到 5 小時額度時,從 Claude 切到 Codex
  • 如果每週額度偏向某一邊,就做調整
  • 如果還是不行,就換模型

每次要做判斷,都得開瀏覽器確認剩餘量。

先試著放到 statusLine

一開始想到的方法,是把剩餘量顯示在 Claude Code / Codex 的 statusLine 上。statusLine 是 CLI 畫面底部常駐的一行;在 Claude Code 裡,只要透過設定指定腳本,就能直接顯示該腳本的標準輸出。腳本端會收到模型名稱與工作階段資訊的 JSON,因此顯示內容可以做得相當自由(官方文件)。

我也試著把速率剩餘量、context 使用量、git 分支名稱、docker 容器數量等都顯示出來,心想「這樣就解決了吧」,但實際用起來卻不太理想。

放棄的原因

  • 開著 Claude 時只看得到 Claude 的剩餘量,開著 Codex 時只看得到 Codex 的剩餘量,無法同時掌握兩者
  • Codex 端對 statusLine 的自訂自由度不高
  • 只有在啟動 claude / codex 的那個終端機裡才會顯示,平常工作時看不到
  • 無法確定數值是哪個時間點的,最後還是得開瀏覽器確認「最新」狀態

很可惜,就算放在 statusLine,最後我還是會跑去看瀏覽器,所以沒什麼意義。因此我放棄了 statusLine 這條路。

收穫:已經知道如何取得剩餘量

不過,在調整 statusLine 的過程中,我已經弄清楚 Claude 和 Codex 各自剩餘額度的取得方式。

完成型:在 cmux 的 1 個窗格中常駐顯示

我平常使用的是 cmux 這個多窗格終端機,所以我決定直接佔用一個窗格,常駐繪製剩餘量。畫面大概像這樣:

 Agent Status  · updated 20:13:41                  ports
 ─────────────────────────────────────────         3001   api-api-1     …/apps/api
  Claude  5h ██████████░░░░  73% 1h3m  → 20:20     3308   api-db-1 …/apps/api
          7d █░░░░░░░░░░░░░  10% 6d4h  → 6/7 00:00 6380   api-redis-1   …/apps/api
  Codex   5h ██░░░░░░░░░░░░  16% 4h19m → 23:35
          7d ░░░░░░░░░░░░░░   2% 6d23h → 6/7 18:35
  • 同時顯示 Claude / Codex 的 5 小時額度與週額度、使用率、進度條、距離重置剩餘時間、重置時間
  • 不管 CLI 裡正在開什麼,工作時都能一直看到
  • 定期更新,所以「這是什麼時間點的數值」很清楚
  • 順便把空位拿來顯示目前使用中的 port 清單(開發伺服器的監聽 port 一眼就能看出來)

雖然很不起眼,但視線邊緣一直有剩餘量其實比想像中還要方便;從做好的那天開始,我就再也沒有特地去瀏覽器確認過了。

⚠️ 從這裡開始用到的 Claude usage endpoint 與 Codex 的 app-server,都不是公開且穩定的 API。請把這篇當成是「給自己用的工具」來看待,前提是 CLI 版本可能會改變欄位名稱或 endpoint。

取得 Claude 的剩餘量

Claude 是從 OAuth 的 usage endpoint 取得。token 位於 ~/.claude/.credentials.json 或 keychain 中。

# token 來自 credentials.json 或 keychain
tok=$(jq -r '.claudeAiOauth.accessToken // empty' "$HOME/.claude/.credentials.json")
[ -z "$tok" ] && tok=$(security find-generic-password -s "Claude Code-credentials" -w \
                         | jq -r '.claudeAiOauth.accessToken // empty')

resp=$(curl -s --max-time 10 "https://api.anthropic.com/api/oauth/usage" \
         -H "Authorization: Bearer $tok" \
         -H "anthropic-beta: oauth-2025-04-20")

回應長這樣(這是實際回傳內容。使用率是 0〜100 的數字,resets_at 是 ISO8601 / UTC)。

{
  "five_hour": { "utilization": 3.0,  "resets_at": "2026-06-09T15:30:00+00:00" },
  "seven_day": { "utilization": 26.0, "resets_at": "2026-06-13T15:00:00+00:00" },
  "seven_day_opus":   null,
  "seven_day_sonnet": { "utilization": 0.0, "resets_at": null },
  "extra_usage": { "is_enabled": false }
}
  • 需要 anthropic-beta: oauth-2025-04-20 標頭
  • 只會使用 five_hour / seven_day(另外還會回傳一些看起來像是內部用途的欄位)
  • 如果高頻率呼叫會變成 429,所以我加了最短 55 秒快取,以及失敗時沿用前一次數值的處理

取得 Codex 的剩餘量(codex app-server

Codex 可以透過 codex app-server 傳 JSON-RPC 取得目前數值。先 initialize,再呼叫 account/rateLimits/read

rl=$( { printf '%s\n' \
          '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"agent-status","version":"1.0"}}}' \
          '{"jsonrpc":"2.0","id":2,"method":"account/rateLimits/read","params":{}}'
        sleep 6
      } | codex app-server 2>/dev/null \
        | jq -c 'select(.id==2).result.rateLimits' | head -1 )

sleep 6 是為了避免在回應還沒回來前就關閉 stdin,導致 app-server 提前結束;實際時間可依環境調整。

用 jq 擷取 .result.rateLimits 後,結果會像這樣(credits.balance 等欄位已遮蔽)。

{
  "primary":   { "usedPercent": 1,  "windowDurationMins": 300,   "resetsAt": 1781030643 },
  "secondary": { "usedPercent": 24, "windowDurationMins": 10080,  "resetsAt": 1781150626 },
  "credits":   { "hasCredits": true, "unlimited": false, "balance": "***" },
  "planType": "plus"
}
  • primary 是 5 小時額度(windowDurationMins: 300),secondary 是週額度(10080 = 7 天)
  • resetsAt 是 epoch 秒。Claude 回傳的是 ISO8601 字串,所以我把格式轉換的處理分開了

分開取得(daemon)與顯示(viewer)

我平常會同時碰多個專案,而且也會使用多個 cmux workspace,因此也會想在多個地方開顯示窗格。不過,如果每個窗格都各自去打 API,Claude 很可能碰到 429,Codex 的 app-server 也會被重複啟動很多次。雖然我實際上沒有真的撞到 429,但讓每個窗格都各自去抓同一份數值,明顯是浪費,所以我事先把它拆成兩部分:

  • 取得(daemon):固定間隔收集 Claude / Codex / port,然後只寫入一份 state.json
  • 顯示(viewer):只讀取那份 state.json 來繪製;各個窗格都跑這個

真正去打 API 的只有 daemon 那一條,因此就算顯示窗格增加,也只是讀 JSON 而已。daemon 會在沒有 viewer 時自動停止,而 viewer 在沒有 daemon 時會自動把它喚起,所以只要開啟窗格就會開始彙整,關閉窗格就會停止。完全不用煩惱收尾,這點很輕鬆。

啟動方法

cmux 可以切分分頁,所以只要在切分後的窗格中啟動 viewer 就好。在窗格裡輸入 agent-status-pane,就會開始常駐顯示(daemon 會由 viewer 自動喚起)。

# 結構(3 個檔案)
~/scripts/agent-status/agent-status-daemon.sh   # 取得(API → state.json)
~/scripts/agent-status/agent-status-pane.sh     # 顯示(state.json → 畫面)
~/scripts/agent-status/agent-status-labels.conf # 路徑 → 標籤替換

# 在 PATH 可找到的位置建立 symlink(pane.sh 支援解析 symlink)
ln -s ~/scripts/agent-status/agent-status-pane.sh ~/.local/bin/agent-status-pane

總結

最後做的事情其實很單純:把從 API 取得 Claude 和 Codex 剩餘量、寫入 state.json 的 daemon,和讀取它並繪製到 cmux 單一窗格上的 viewer 分開,然後讓它們常駐。雖然 statusLine 沒成功,但也正因為在那裡摸清了取得方法,最後才做出一個最適合我自己的工具。

另外,這個作品幾乎都是交給 Claude Code 完成的;我邊看 YouTube、邊偶爾確認進度,前後不到 2 小時。就我自己實際動手的時間來看,還更短。

我再次感受到的是,現在只要請 AI 幫你寫,就能很輕鬆做出屬於自己的工具。即使裝了現成的應用程式或工具,也常常會因為沒有注意到自己真正需要的功能,或覺得「好像哪裡不對」而最後用不順手。相對地,自己做出來的東西,不只理解其功能,而且最重要的是,它就是你真正想要的。

在去找「有沒有好工具」之前,主動自己做出來也不錯。
如果這篇能成為有類似小麻煩的人的參考,那就太好了。

(附錄)完整腳本

三個檔案都放在 gist 裡了(在 macOS 上可直接執行)。

👉 agent-status(gist)

  • agent-status-daemon.sh — 取得(API → state.json
  • agent-status-pane.sh — 顯示(state.json → 畫面)
  • agent-status-labels.conf — 路徑 → 標籤替換(範例)

另外,由於腳本使用了 date -j / date -r / security / osascript 這類 macOS 專用命令,因此不能直接在 Linux 上執行(需要替換日期處理與 keychain 相關部分)。

Xincere 株式會社

xincere 株式會社 會聘用沒有實務經驗的工程師與學生工程實習生,並一起工作。
※ 關於 Xincere 的工作方式,請見這裡

在 Xincere,每年大約有 100 位左右沒有實務經驗的人報名並接受技術面試。
透過這些經驗,這裡會介紹希望沒有實務經驗的朋友們特別具備的技術能力(語法)。


原文出處:https://qiita.com/tatsuya582/items/5ca0c12a8495530f7d09


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝7   💬1  
353
🥈
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登