ClaudeMods
☰
ZH-CN
● 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.

更多类似作品