ClaudeMods
☰
ZH-TW
● 0 人在線上 · 瀏覽 0 次
贊助提交作品
GitHub 儲存庫 · 發布者 musingfox

omp-quota

[Experimental] 在工作階段中顯示每個 omp provider 的剩餘額度——提示列上方的 /quota 列、/quota refresh,以及 provider 從 ok 變差時的 toast(Claude Mod;需要 Claude Code 2.1.287+)。

musingfox@musingfox

musingfox/cc-plugins/tree/main/omp-quota

已翻譯

關於這個 mod

omp-quota

以一個 Claude Mod(function-hooks 模組,hooks/register.ts)的形式,在 Claude Code 工作階段內顯示每個 omp provider 的剩餘額度。

  • /quota:切換提示列上方的精簡表格,每個 provider 一列。
  • /quota refresh:丟棄 omp 的快取並取得最新額度,不執行模型回合。
  • Toast:當某個 provider 的狀態從 ok 變差時,在工作階段內顯示一次 toast。

需要 Claude Code 2.1.287 或更新版本;在這些版本中 Claude Mods 預設開啟。使用 Claude Code 2.1.287 建置並測試。

omp 如何執行

每次擷取都會執行 omp usage --json,並將 PI_CODING_AGENT_DIR 設為 $HOME/.omp/agent,限時 30 s(2026-09-25 在所有 provider 上進行的冷擷取約耗時 10 s)。這個覆寫設定很重要:Claude Code 可能會傳入自己的 PI_CODING_AGENT_DIR(pi-dispatch 會設定一個),而 omp 讀取那個 home 時會以結束碼 0 回傳且沒有 provider。Claude Code 的 PATH 上可以找到 omp(通常位於 ~/.bun/bin)。如果未設定 HOME,就不會執行 omp,提示列會顯示 Unavailable: HOME is unset,而 /quota refresh 會回答 omp quota refresh failed: HOME is unset。

額度會在工作階段開始時擷取一次,之後每 5 分鐘擷取一次。擷取失敗不會停止輪詢;沒有其他重試。拒絕註冊 /quota 也不會停止輪詢。重新載入再次觸發工作階段開始時,會先取消上一輪輪詢,因此頻率不會加倍。

任何操作都不會等待 omp。工作階段開始(Claude Code 會在第一次提示前等待它完成)會啟動擷取但不等待結果;模組也不會掛接工具呼叫或提示事件,因此慢速的 omp 不會拖住任何工具呼叫或提示。

如何讀取額度

  • 剩餘比例:如果 omp 的 remainingFraction 是數字,就使用它;否則使用 1 − usedFraction;再否則沒有值;結果限制在 0–100%。一個 provider 的比例取其各個限制的最低值。顯示四捨五入後的百分比;無法計算時顯示 —。
  • 狀態:一個 provider 的狀態取其各限制中最差的狀態(exhausted > warning > ok);沒有狀態的限制會被忽略,沒有任何狀態的 provider 不顯示狀態。同名 provider 的報告會合併為一個 provider;沒有名稱的報告列為 (unnamed)。
  • 變差的 provider:上一次成功擷取時狀態為 ok,而這次變成 warning 或 exhausted 的 provider。第一次成功擷取沒有上一次狀態;warning → exhausted、前後任一側沒有狀態的 provider,以及新出現的 provider 都不算。
  • 擷取失敗:omp 沒有回答(無法啟動,或 30 s 後仍在執行)、以非零狀態結束、印出的內容不是 JSON 報告清單,或回報沒有 provider——最後一種情況就是 omp 讀到錯誤 home 時的回答,因此不會清空畫面。omp 的錯誤輸出永遠不會顯示。
  • 帳號資料:只保留 provider 名稱、限制 id 和標籤、比例、狀態及重設時間。omp 的 metadata(email、account id、endpoint)和每個限制的 scope 永遠不會進入 toast、記錄或提示列。

Toast

當一次成功擷取發現 provider 自上一次成功擷取以來變差(見上方的 變差的 provider)時,會顯示一個工作階段內的 toast,列出它們全部:omp quota: openai-codex now warning, anthropic now exhausted。中間發生的失敗擷取不會重設比較。沒有作業系統通知;如果你在另一個視窗中,發生變差時,回來後會在提示列中看到(前提是提示列已開啟)。

命令

  • /quota 開啟或關閉提示列上方的列,並且不在記錄中印出任何內容。只有這個命令會改變提示列;輪詢或狀態變差都不會改變它。選擇保存在外掛儲存的 band key 中,並在工作階段開始時讀取,因此提示列會保持離開時的狀態;沒有儲存內容或儲存失敗時,預設關閉。切換時如果儲存失敗,本次工作階段仍然會切換提示列。
  • /quota refresh 會執行 omp usage invalidate,然後擷取;兩者使用同一個 omp home,並回答一行:omp quota refreshed 或 omp quota refresh failed: <reason>(提示列隨後會在通知行中顯示失敗)。如果 invalidate 失敗,擷取仍會執行,回答最後會加上 (cache not invalidated)。它不會改變提示列,也不需要模型回合。
  • /quota <anything else> 會回答 usage: /quota [refresh],不執行任何內容。

提示列

提示列就是緊貼提示輸入上方的帶狀區域。/quota 開啟後,擷取期間會顯示變暗的通知行(Fetching omp usage);在沒有任何成功擷取時顯示(Unavailable: <reason>);最新擷取失敗時顯示(Stale: <reason>; showing data from <age> ago)。接著為回報限制的每個 provider 顯示一列,按剩餘比例從少到多排列,例如:

cursor              Monthly   0% 11h 25m exhausted
openai-codex        5 hours 100% 4h 59m  7 days    6% 1d 11h warning
anthropic           5 Hour   86% 1h 44m  7 Day    94% 5d 15h

每列對每個視窗顯示一個儲存格(使用 omp 的視窗標籤;沒有視窗標籤時使用限制標籤),按最早重設的順序排列:<window> <share> <time to reset>;只有狀態為 warning 或 exhausted 時才加上視窗狀態。視窗的比例與重設時間來自剩餘比例最低的限制(沒有任何比例時取第一個),因此 antigravity 這類共用 Claude 與 GPT 限制的重複池會折疊成一個儲存格。儲存格會填充空格,讓不同 provider 的視窗對齊。provider 的順序按所有限制中的最低比例排列;沒有比例的 provider 放在最後;沒有限制的 provider(例如 ollama-cloud)會被排除。重設時間顯示為 Xd Yh、Xh Ym 或 Ym;到期時顯示 now,omp 沒有提供時顯示 —。每個比例按顯示的百分比著色:0–30% 為紅色、31–60% 為橙色、61–100% 為綠色;— 不著色。

每一列都會在提示列寬度處截斷並顯示省略號,而不是換行。某個 survey 佔用提示列時,提示列會讓出空間;有其他 mod 在那裡繪製提示列時(例如 /cal),它會共用空間而不是隱藏對方;每次擷取完成(包括失敗的擷取)後都會重繪,因此始終顯示模組持有的最新資料。使用 ctrl+x ctrl+a(或它的 [-] 標記)可以折疊它,而不會關閉它。它只使用 Box 和 Text 繪製,屬性為 flexDirection、color、dimColor 和 wrap。

Tests

claude plugin test omp-quota

測試套件是 hermetic 的:不使用檔案系統、網路或程序;測試進行的每次 $ 呼叫都由外掛下的 stub 回答。

tests/fixtures/snapshot.ts 是從 2026-09-18 取得的即時 omp usage --json 脫敏副本。要更新它,請擷取新的快照,刪除所有 metadata 物件、所有 scope.projectId 和 scope.accountId,以及 resetCredits,然後貼上去。只要還有任何帳號 key 或包含 @ 的字串,tests/snapshot.test.ts 的第一個測試就會失敗。

安裝

請先查看作者 README,確認 marketplace 與外掛名稱;指令可能隨儲存庫結構而變動。

claude plugin marketplace add musingfox/cc-plugins
claude plugin install omp-quota
原文 / README

omp-quota

Shows every omp provider's remaining quota inside a Claude Code session, as one Claude Mod (a function-hooks module, hooks/register.ts).

  • /quota: toggles a compact table above the prompt, one line per provider.
  • /quota refresh: drops omp's cache and fetches fresh quota, without a model turn.
  • Toast: one in-session toast when a provider's status worsens from ok.

Needs Claude Code 2.1.287 or later, where Claude Mods are on by default. Built and tested against Claude Code 2.1.287.

How omp is run

Each fetch runs omp usage --json with PI_CODING_AGENT_DIR set to $HOME/.omp/agent and a 30 s limit (a cold fetch across all providers took about 10 s on 2026-09-25). The override matters: Claude Code may pass down a PI_CODING_AGENT_DIR of its own (pi-dispatch sets one), and omp reading that home answers with exit 0 and no providers. omp is found on Claude Code's PATH (it usually lives in ~/.bun/bin). With HOME unset, omp is not run, the band reads Unavailable: HOME is unset, and /quota refresh answers omp quota refresh failed: HOME is unset.

Quota is fetched once at session start and then every 5 minutes. A failed fetch leaves the poll running; there is no other retry. A refused /quota registration leaves it running too. When a reload re-fires session start, the previous poll is cancelled first, so the cadence never doubles.

Nothing waits on omp. Session start (which Claude Code awaits before the first prompt) starts the fetch without awaiting it, and the module hooks no tool-call or prompt event, so no tool call or prompt is ever held up by a slow omp.

How quota is read

  • Remaining share of a limit: omp's remainingFraction when it is a number, else 1 − usedFraction, else none; clamped to 0–100%. A provider's share is the lowest of its limits' shares. Shown as a rounded percentage, or — when none is computable.
  • Status of a provider: the worst status among its limits (exhausted > warning > ok); limits without one are ignored, and a provider with none has no status. Reports with the same provider name merge into one provider; a report without one is listed as (unnamed).
  • Worsened provider: one whose status was ok in the previous good fetch and is warning or exhausted now. The first good fetch has no previous; warning → exhausted, a provider without a status on either side, and a newly appearing provider do not count.
  • Failed fetch: omp did not answer (could not start, or still running after 30 s), exited non-zero, printed something that is not a JSON report list, or reported no providers — the last is how omp answers when it reads the wrong home, so it never wipes the display. omp's error output is never shown.
  • Account data: only provider names, limit ids and labels, shares, statuses, and reset times are kept. omp's metadata (email, account id, endpoint) and each limit's scope never reach a toast, the transcript, or the band.

The toast

When a good fetch finds providers that worsened since the previous good fetch (see Worsened provider above), one in-session toast names them all: omp quota: openai-codex now warning, anthropic now exhausted. Failed fetches in between do not reset the comparison. There is no OS notification; a worsening that happens while you are in another window shows in the band, if it is on, when you return.

Commands

  • /quota toggles the band above the prompt on or off and prints nothing in the transcript. Only this command changes the band — a poll or a worsening never does. The choice is kept in the plugin's store (key band) and read at session start, so the band stays as you left it; with nothing stored, or a store that fails, it starts off. A store that fails on the toggle still flips the band for this session.
  • /quota refresh runs omp usage invalidate, then fetches, both against the same omp home, and answers one line: omp quota refreshed or omp quota refresh failed: <reason> (the band then shows the failure in its notice line). When the invalidate fails, the fetch still runs and the answer ends in (cache not invalidated). It does not change the band and needs no model turn.
  • /quota <anything else> answers usage: /quota [refresh] and runs nothing.

The band

The band is the strip directly above the prompt input. While /quota has it on, it shows a dim notice line while fetching (Fetching omp usage), when no fetch has succeeded (Unavailable: <reason>), or when the latest fetch failed (Stale: <reason>; showing data from <age> ago). Then one line per provider that reports limits, from the least share left to the most, for example:

cursor              Monthly   0% 11h 25m exhausted
openai-codex        5 hours 100% 4h 59m  7 days    6% 1d 11h warning
anthropic           5 Hour   86% 1h 44m  7 Day    94% 5d 15h

Each line holds one cell per window (omp's window label, or the limit label when it has none), soonest reset first: <window> <share> <time to reset>, plus the window's status only when it is warning or exhausted. A window's share and reset come from its limit with the least share left (the first one when none has a share), so repeated pools such as antigravity's shared Claude & GPT limits collapse into one cell. Cells are padded so the windows line up across providers. A provider's order uses its lowest share across all limits; providers without a share go last, and one reporting no limits (such as ollama-cloud) is left out. Time to reset reads Xd Yh, Xh Ym, or Ym, now when due, and — when omp gives none. Each share is colored by the percentage shown: red for 0–30%, orange for 31–60%, green for 61–100%; — stays uncolored.

Every line is cut at the band's width with an ellipsis rather than wrapped. The band yields to a survey while one holds it, shares its space with any band another mod draws there (/cal, for one) instead of hiding it, and redraws after every settled fetch, failed ones included, so it always shows the latest data the module holds. Collapse it with ctrl+x ctrl+a (or its [-] mark) without turning it off. It is drawn from Box and Text only, with the props flexDirection, color, dimColor, and wrap.

Tests

claude plugin test omp-quota

The test kit is hermetic: no filesystem, network, or process; every $ call a test makes is answered by a stub beneath the plugin.

tests/fixtures/snapshot.ts is a redacted copy of a live omp usage --json taken on 2026-09-18. To refresh it, capture a new snapshot, remove every metadata object, every scope.projectId and scope.accountId, and resetCredits, then paste it in. The first test in tests/snapshot.test.ts fails while any account key or any string holding @ remains.

更多類似作品