Skip to content

feat(contrib): SwiftBar menu bar quota plugin for macOS#603

Open
plustar35 wants to merge 2 commits into
ndycode:mainfrom
plustar35:feat/contrib-swiftbar-quota
Open

feat(contrib): SwiftBar menu bar quota plugin for macOS#603
plustar35 wants to merge 2 commits into
ndycode:mainfrom
plustar35:feat/contrib-swiftbar-quota

Conversation

@plustar35

@plustar35 plustar35 commented Jun 11, 2026

Copy link
Copy Markdown

What

Adds an optional SwiftBar plugin under contrib/swiftbar/ that renders per-account quota cards in the macOS menu bar:

⚡88·[90]        ← title: 5h-window remaining % per account, brackets = account currently serving requests

╭──────────────────────────────────╮
│ ● account-a               ACTIVE │
│ 5h █████████░   88%  → 4h 42m    │
│ 7d ████████░░   83%  → 6d 18h    │
╰──────────────────────────────────╯
╭──────────────────────────────────╮
│ ○ account-b                 IDLE │
│ 5h █████████░   90%  → 4h 33m    │
│ 7d █████████░   86%  → 8h 16m    │
╰──────────────────────────────────╯
  • Active account resolved from runtime observability (lastAccountId), falling back to the stored active index
  • 5h/7d windows with remaining %, text progress bar, and reset countdown; rows turn orange <30% and red <10%
  • Reads only the local quota cache → zero quota cost; an explicit "Live refresh" menu action runs codex-multi-auth check (one probe per account)
  • Single self-contained script (re-invokes itself with a livecheck arg for the menu action); honors CODEX_MULTI_AUTH_DIR

Why

With runtime rotation, "how much quota does each account have left, and which one is serving right now" becomes the question users ask most. A glanceable menu bar view answers it without shelling out to check (and without spending probes).

Notes

  • contrib/ is outside the npm files whitelist, so the published package is unchanged
  • Fails soft (⚡?) if the internal cache format changes
  • Card interior is restricted to Menlo-native glyphs; the README documents why (CJK/emoji fallback fonts break border alignment)
  • Tested on macOS 15 + SwiftBar 2.x against a live two-account pool

🤖 Generated with Claude Code

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

adds a self-contained swiftbar plugin under contrib/swiftbar/ that renders per-account codex quota cards in the macOS menu bar, reading only the local cache (zero quota cost) with an optional live-refresh menu action.

  • codex-quota.5m.sh embeds a python3 heredoc that parses quota-cache.json, openai-codex-accounts.json, and runtime-observability.json to render 5h/7d progress bars with color-coded thresholds and reset countdowns; active account is resolved from lastAccountId with fallback to activeIndex
  • the livecheck dispatch re-invokes the script with a hardcoded PATH that misses nvm/volta installs, causing silent failures for users who installed via nvm; the header row's pad() also has no truncation guard for long email local-parts, which can blow past W=32 and break border alignment

Confidence Score: 5/5

safe to merge — contrib-only change, outside npm files whitelist, no core lib or proxy code touched

the plugin is fully isolated in contrib/, cache reads are read-only, clamp_pct correctly bounds floats to ints, and fail-soft behavior is consistent throughout; the two issues found are cosmetic display concerns that don’t affect the rotation proxy or auth flow

contrib/swiftbar/codex-quota.5m.sh — header truncation and livecheck PATH coverage worth a second look before publicising the install guide widely

Important Files Changed

Filename Overview
contrib/swiftbar/codex-quota.5m.sh new self-contained bash+python3 swiftbar plugin; quota % rows are correctly bounded via clamp_pct, but the header row has no truncation guard for long email local-parts, and the livecheck PATH omits nvm/volta bin dirs causing silent failures for common installs
contrib/swiftbar/README.md new readme covering install, data source, refresh model, and glyph constraints; accurate and complete

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([SwiftBar invokes script]) --> B{arg == livecheck?}
    B -- yes --> C[export hardcoded PATH]
    C --> D[codex-multi-auth check]
    D --> E([exit])
    B -- no --> F{uname == Darwin?}
    F -- no --> G([show error, exit])
    F -- yes --> H[invoke python3 heredoc]
    H --> I[load quota-cache.json]
    I --> J{files readable?}
    J -- no --> K([print error])
    J -- yes --> L[resolve active_id]
    L --> M[build quota rows per account]
    M --> N[print title bar]
    N --> O[print card blocks]
    O --> P[print Live refresh action]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
contrib/swiftbar/codex-quota.5m.sh:119
`pad()` adds spaces but never truncates. the header row is `pad(f"{dot} {short}", W - len(tag)) + tag`, so if `short` exceeds `W - len(tag) - 2` chars (24 chars for ACTIVE, 26 for IDLE) the right border blows past `W=32` — the same alignment issue the Menlo-glyph note is guarding against. org emails like `firstname.lastname.department@company.com` easily hit this. clipping `short` to `W - len(tag) - 3` (with a trailing ``) before building the row keeps the card width bounded.

### Issue 2 of 2
contrib/swiftbar/codex-quota.5m.sh:22-25
the hardcoded PATH for `livecheck` omits nvm paths (`~/.nvm/versions/node/*/bin`) and volta paths (`~/.volta/bin`). most macOS devs who followed the codex-multi-auth install docs used nvm, so `codex-multi-auth` lives in an nvm-managed bin and won't be found here. since `terminal=false` is set, the 127 exit is invisible — the menu item appears to do nothing. appending `$HOME/.nvm/versions/node/*/bin` via glob, or delegating to `npx --no codex-multi-auth check`, would cover the common case.

Reviews (2): Last reviewed commit: "fix(contrib): address review feedback on..." | Re-trigger Greptile

Card-style menu bar widget showing per-account 5h/7d quota windows,
active-account marker from runtime observability, reset countdowns,
and an explicit live-refresh action. Reads the local quota cache only
(zero quota cost); not part of the published npm package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@plustar35 plustar35 requested a review from ndycode as a code owner June 11, 2026 13:01
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

adds a new swiftbar menu bar plugin for codex multi-auth quota display. a bash entrypoint wraps embedded python that reads local cache and account store json files, renders per-account quota cards with active/idle indicators and remaining percentages, and outputs swiftbar-formatted menu sections. supports both passive cache reads and live probes via codex-multi-auth check.

Changes

SwiftBar Codex Quota Plugin

Layer / File(s) Summary
Plugin documentation and bash entrypoint
contrib/swiftbar/README.md, contrib/swiftbar/codex-quota.5m.sh:1–18
README describes the menu bar widget display (per-account quota cards with active/idle markers, color thresholds, 5h/7d remaining), installation steps (Homebrew, plugin placement, permissions), and notes on macOS-only rendering and cache format soft failure. Bash entrypoint defines livecheck parameter to trigger explicit codex-multi-auth check probes.
Python initialization, paths, and error handling
contrib/swiftbar/codex-quota.5m.sh:22–81
Embeds Python script with filesystem paths (quota cache, account store, runtime observability), color/display constants, JSON loading helpers, reset time formatting, and error card output. Early exit on missing or unreadable cache/store files.
Account selection and per-account quota cards
contrib/swiftbar/codex-quota.5m.sh:82–120
Builds account lookup structures from store, determines active account from runtime observability (fallback to activeIndex), and generates per-account SwiftBar card blocks: active/idle markers, 5h/7d remaining percentages derived from cached usedPercent, bar visuals, and reset countdowns.
Menu bar rendering and output
contrib/swiftbar/codex-quota.5m.sh:121–134
Prints composed SwiftBar output: headline, card grid with color-overridden rows based on quota level, cache update timestamp, and menu items for live refresh and cache reload.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes


notes for review

  • no regression tests: this plugin has no test coverage. consider adding a fixture-based test for the python card-generation logic, especially the usedPercent → 5h/7d remaining derivation and account selection fallback paths.
  • windows not addressed: the README correctly notes macOS-only behavior (font, menu bar rendering), but the script has no guards or error messages if run on non-darwin systems. consider a minimal safety check or explicit documentation of the skip-on-windows expectation.
  • cache format stability assumption: the code assumes cache usedPercent field always exists and is a number. if cache format evolves, the soft failure (⚡?) relies on shell stderr handling. verify that the JSON parsing errors will bubble cleanly to the menu bar rather than silently dropping quota info.
  • active account fallback logic: determines active account from runtime observability first, then falls back to store activeIndex. confirm that both signals are kept in sync and that the fallback doesn't mask stale activeIndex values during concurrent account switching.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive PR description covers objectives well but lacks validation and docs checklist completion; greptile flagged two cosmetic issues (header truncation and livecheck PATH coverage) requiring fixes before merge. address greptile findings on header truncation (pad() has no guard for long email local-parts) and livecheck PATH missing nvm/volta bin dirs; confirm regression tests cover quota cache parsing and menu rendering edge cases; verify macos-only constraints don't mask windows build breaks.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed title follows conventional commits format with type(scope): summary, 55 chars, lowercase imperative—fully meets requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@plustar35

plustar35 commented Jun 11, 2026 via email

Copy link
Copy Markdown
Author

Comment thread contrib/swiftbar/codex-quota.5m.sh Outdated
age_min = int((time.time() - newest / 1000) / 60)
age = "just now" if age_min < 1 else (f"{age_min}m ago" if age_min < 60 else f"{age_min//60}h ago")
print(f"Cache updated {age} | size=11 color=gray")
print(f"Live refresh (one probe per account) | bash={SELF} param1=livecheck terminal=false refresh=true")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unquoted SELF path in SwiftBar action

bash={SELF} is written without quotes; if the plugin is installed in a path with spaces (e.g. a home dir like /Users/John Doe/) SwiftBar's parameter parser splits on whitespace and treats everything after the first space as a new param, silently dropping the livecheck dispatch and breaking the live-refresh action. SwiftBar supports bash="{SELF}" (or percent-encoding), which handles spaces cleanly.

Prompt To Fix With AI
This is a comment left during a code review.
Path: contrib/swiftbar/codex-quota.5m.sh
Line: 133

Comment:
**Unquoted `SELF` path in SwiftBar action**

`bash={SELF}` is written without quotes; if the plugin is installed in a path with spaces (e.g. a home dir like `/Users/John Doe/`) SwiftBar's parameter parser splits on whitespace and treats everything after the first space as a new param, silently dropping the `livecheck` dispatch and breaking the live-refresh action. SwiftBar supports `bash="{SELF}"` (or percent-encoding), which handles spaces cleanly.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +73 to +90
cache = load(CACHE)
store = load(STORE)
if not cache or not store:
print("⚡?")
print("---")
print("Cannot read quota cache or account store | color=red")
print(f"Expected under: {DATA_DIR} | size=11 color=gray")
sys.exit(0)

emails, order = {}, []
for acc in store.get("accounts", []):
aid = acc.get("accountId", "")
emails[aid] = acc.get("email", aid[-6:] if aid else "?")
order.append(aid)

by_id = cache.get("byAccountId", {})

observ = load(OBSERV) or {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Concurrent reads across three separate files

quota-cache.json, openai-codex-accounts.json, and runtime-observability.json are opened sequentially with no advisory lock. the rotation proxy can write any of these between reads, so order (from the accounts store) and by_id (from the quota cache) can be momentarily inconsistent — a freshly-added account appears in order but has no entry in by_id, producing a "?" quota row. the load() except guard handles full parse failures, but not this cross-file ordering gap. for a 5 m plugin this is cosmetic, but flagging explicitly since the codebase has active concurrent writers.

Prompt To Fix With AI
This is a comment left during a code review.
Path: contrib/swiftbar/codex-quota.5m.sh
Line: 73-90

Comment:
**Concurrent reads across three separate files**

`quota-cache.json`, `openai-codex-accounts.json`, and `runtime-observability.json` are opened sequentially with no advisory lock. the rotation proxy can write any of these between reads, so `order` (from the accounts store) and `by_id` (from the quota cache) can be momentarily inconsistent — a freshly-added account appears in `order` but has no entry in `by_id`, producing a `"?"` quota row. the `load()` except guard handles full parse failures, but not this cross-file ordering gap. for a 5 m plugin this is cosmetic, but flagging explicitly since the codebase has active concurrent writers.

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@contrib/swiftbar/codex-quota.5m.sh`:
- Around line 64-67: The remaining quota value can be outside 0..100 causing
malformed bars and noisy titles; clamp it to that range before formatting or
rendering. Update the bar(remaining, slots=10) usage (or inside bar itself) to
sanitize remaining via remaining = max(0, min(100, remaining)) and ensure the
same clamped value is used when building the title/display strings (the code
that formats the title around the quota and the call sites that render the bar),
so both the bar width and title always use a value constrained to 0–100.
- Around line 1-2: The script codex-quota.5m.sh currently lacks a runtime guard
for non-macOS systems; add an early check right after the shebang (#!/bin/bash)
that verifies the OS (e.g., [[ "$(uname -s)" == "Darwin" ]] or OSTYPE contains
darwin) and if not darwin print a short soft/friendly message and exit 0 so the
plugin fails softly on Linux/Windows; ensure the check is placed before any
SwiftBar logic runs so accidental execution outside macOS exits cleanly.
- Around line 22-135: Add fixture-driven regression tests that cover
active-account fallback, quota_color thresholds (<30 -> ORANGE, <10 -> RED),
title formatting (active account wrapped in brackets), and missing/bad window
handling; refactor the rendering logic into a testable function (e.g., expose
load + the loop that builds titles/blocks as a function that returns (titles,
blocks, newest) or export a render_rows helper used by the main script), then
write tests that load sample CACHE/STORE/OBSERV fixtures and assert either the
generated textual lines (golden) or the structured rows/blocks (preferred) for
each fixture case: active-id missing, primary/secondary missing, usedPercent
edge values (9, 29, 30), and ensure fmt_reset, bar, and quota_color produce the
expected strings/colors.
- Around line 14-17: The live refresh block currently swallows failures by
redirecting codex-multi-auth check output to /dev/null and unconditionally
exiting 0; change the if [ "$1" = "livecheck" ] block so it does not discard
output and it propagates the command's exit status (i.e., run codex-multi-auth
check without >/dev/null 2>&1 and exit with its exit code—use its return value
or short-circuit like running codex-multi-auth check and then exit $?),
referencing the codex-multi-auth check invocation to ensure refresh failures are
detectable.

In `@contrib/swiftbar/README.md`:
- Around line 6-9: The README uses fenced code blocks without language tags
(tripping markdownlint MD040) for the menu-bar title block and the account
display block; update both fenced code blocks (the one containing "⚡88·[90] ←
menu bar title..." and the one starting with
"╭──────────────────────────────────╮" / "│ ● account-a               ACTIVE │")
to include a language like text (i.e., replace ``` with ```text) so markdownlint
passes; ensure you update the other similar fenced block between lines 13–24 the
same way.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: abe7733f-d2d4-4d0c-b94f-3e1e804c54c0

📥 Commits

Reviewing files that changed from the base of the PR and between dae10cb and 4d63b7f.

📒 Files selected for processing (2)
  • contrib/swiftbar/README.md
  • contrib/swiftbar/codex-quota.5m.sh
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

**: # PROJECT KNOWLEDGE BASE

Generated: 2026-04-25
Commit: a87e005
Validated: 2026-06-10 against commit 98d9819 (repo audit; claims re-checked against the tree, content not regenerated)
Branch: main
Package version: 2.3.0-beta.3

OVERVIEW

codex-multi-auth is a Codex CLI-first OAuth account manager and optional forwarding wrapper for the official Codex CLI. The installed codex-multi-auth entrypoint handles account-management commands locally, codex-multi-auth-codex forwards official Codex commands through this package's wrapper when explicitly used, and runtime rotation can route live Responses traffic through a localhost account-rotation proxy by default. The plugin-host entrypoint remains exported for compatibility, but the primary product surface is the account manager, optional wrapper, storage, runtime proxy, and repair tooling.

STRUCTURE

./
├── scripts/
│   ├── codex.js              # codex-multi-auth-codex wrapper, official CLI forwarder, shadow CODEX_HOME/runtime proxy setup
│   ├── codex-multi-auth.js   # standalone package CLI entrypoint
│   ├── codex-routing.js      # auth command and compatibility alias routing
│   ├── codex-bin-resolver.js # official Codex binary discovery
│   ├── codex-app-router.js   # persistent localhost router for packaged Codex app bind
│   └── codex-app-launcher.js # reversible user-level app launcher routing helper
├── index.ts                  # optional plugin-host runtime entry
├── lib/                      # core runtime logic (see lib/AGENTS.md)
│   ├── auth/                 # OAuth flow, PKCE, callback server
│   ├── runtime/              # Codex CLI/app integration helpers, app bind, live sync, runtime observability
│   ├── request/              # request transform, SSE, failover, backoff
│   ├── storage/              # path resolution, migrations, backups, restore, import/export
│   ├── codex-cli/            # Codex CLI state sync and writer helpers
│   ├── codex-manager/        # command modules and...

Files:

  • contrib/swiftbar/README.md
  • contrib/swiftbar/codex-quota.5m.sh
🪛 markdownlint-cli2 (0.22.1)
contrib/swiftbar/README.md

[warning] 6-6: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 13-13: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment thread contrib/swiftbar/codex-quota.5m.sh
Comment thread contrib/swiftbar/codex-quota.5m.sh Outdated
Comment on lines +22 to +135
/usr/bin/python3 - "$SELF" <<'PYEOF'
import json, os, sys, time

SELF = sys.argv[1]
HOME = os.path.expanduser("~")
DATA_DIR = os.environ.get("CODEX_MULTI_AUTH_DIR") or os.path.join(HOME, ".codex", "multi-auth")
CACHE = os.path.join(DATA_DIR, "quota-cache.json")
STORE = os.path.join(DATA_DIR, "openai-codex-accounts.json")
OBSERV = os.path.join(DATA_DIR, "runtime-observability.json")

GREEN = "#34C759"
GRAY = "#9A9A9E"
RED = "#FF3B30"
ORANGE = "#FF9F0A"
# Card interior must stay within Menlo-native glyphs (ASCII, box drawing,
# block elements, arrows, geometric circles). CJK or emoji fall back to other
# fonts with non-integer widths and break the right border alignment.
W = 32

def load(path):
try:
with open(path) as f:
return json.load(f)
except Exception:
return None

def pad(s, width):
return s + " " * max(0, width - len(s))

def fmt_reset(ms):
left = int(ms / 1000 - time.time())
if left <= 0:
return "now"
d, r = divmod(left, 86400)
h, r = divmod(r, 3600)
m = r // 60
if d > 0:
return f"{d}d {h}h"
if h > 0:
return f"{h}h {m}m"
return f"{m}m"

def bar(remaining, slots=10):
filled = round(remaining / 100 * slots)
return "█" * filled + "░" * (slots - filled)

def quota_color(rem):
if rem < 10: return RED
if rem < 30: return ORANGE
return None

cache = load(CACHE)
store = load(STORE)
if not cache or not store:
print("⚡?")
print("---")
print("Cannot read quota cache or account store | color=red")
print(f"Expected under: {DATA_DIR} | size=11 color=gray")
sys.exit(0)

emails, order = {}, []
for acc in store.get("accounts", []):
aid = acc.get("accountId", "")
emails[aid] = acc.get("email", aid[-6:] if aid else "?")
order.append(aid)

by_id = cache.get("byAccountId", {})

observ = load(OBSERV) or {}
active_id = observ.get("lastAccountId")
if active_id not in order:
idx = store.get("activeIndex")
active_id = order[idx] if isinstance(idx, int) and 0 <= idx < len(order) else None

titles, blocks = [], []
newest = 0
for aid in order:
email = emails.get(aid, "?")
short = email.split("@")[0]
is_active = (aid == active_id)
frame = GREEN if is_active else GRAY
dot = "●" if is_active else "○"
tag = "ACTIVE" if is_active else "IDLE"
rows = [(pad(f"{dot} {short}", W - len(tag)) + tag, None)]
q = by_id.get(aid)
if not q:
titles.append("?")
rows.append((pad("no quota data", W), None))
else:
newest = max(newest, q.get("updatedAt", 0))
for key, label in (("primary", "5h"), ("secondary", "7d")):
win = q.get(key, {})
rem = 100 - win.get("usedPercent", 0)
reset = fmt_reset(win["resetAtMs"]) if win.get("resetAtMs") else "-"
rows.append((pad(f"{label} {bar(rem)} {rem:>3}% → {reset}", W), quota_color(rem)))
if key == "primary":
titles.append(f"[{rem}]" if is_active else str(rem))
blocks.append((frame, rows))

print("⚡" + "·".join(titles))
print("---")
style = "font=Menlo size=12 trim=false emojize=false"
for frame, rows in blocks:
print(f"╭{'─' * (W + 2)}╮ | color={frame} {style}")
for text, override in rows:
print(f"│ {text} │ | color={override or frame} {style}")
print(f"╰{'─' * (W + 2)}╯ | color={frame} {style}")
if newest:
age_min = int((time.time() - newest / 1000) / 60)
age = "just now" if age_min < 1 else (f"{age_min}m ago" if age_min < 60 else f"{age_min//60}h ago")
print(f"Cache updated {age} | size=11 color=gray")
print(f"Live refresh (one probe per account) | bash={SELF} param1=livecheck terminal=false refresh=true")
print("Reload from cache | refresh=true")
PYEOF

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 🏗️ Heavy lift

add regression coverage for quota parsing/render formatting.

this feature ships non-trivial transform/render logic with internal-cache compatibility assumptions, but this pr has no tests for: active-account fallback, color thresholds (<30, <10), title formatting, and bad/missing windows. please add fixture-driven regression tests (golden output or structured row assertions) so future cache/schema changes do not silently break the widget.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contrib/swiftbar/codex-quota.5m.sh` around lines 22 - 135, Add fixture-driven
regression tests that cover active-account fallback, quota_color thresholds (<30
-> ORANGE, <10 -> RED), title formatting (active account wrapped in brackets),
and missing/bad window handling; refactor the rendering logic into a testable
function (e.g., expose load + the loop that builds titles/blocks as a function
that returns (titles, blocks, newest) or export a render_rows helper used by the
main script), then write tests that load sample CACHE/STORE/OBSERV fixtures and
assert either the generated textual lines (golden) or the structured rows/blocks
(preferred) for each fixture case: active-id missing, primary/secondary missing,
usedPercent edge values (9, 29, 30), and ensure fmt_reset, bar, and quota_color
produce the expected strings/colors.

Comment thread contrib/swiftbar/codex-quota.5m.sh
Comment thread contrib/swiftbar/README.md Outdated
- guard against non-macOS execution with a soft exit
- clamp quota percentages to 0-100 before rendering bars/title
- propagate codex-multi-auth check exit code from the livecheck action
  instead of swallowing it
- tag README code fences with a language (markdownlint MD040)
- correct the cache-refresh comment: the quota cache is written by
  quota-bearing commands, not passively by proxied traffic

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@plustar35

Copy link
Copy Markdown
Author

Thanks for the review. Pushed fixes for the actionable items:

  • Non-macOS guard: soft-exits with a friendly line on non-Darwin.
  • Clamp 0–100: quota percentages are clamped before the bar and title are built, so malformed cache values can't produce broken bars or noisy titles.
  • Exit code: the livecheck action now runs codex-multi-auth check without discarding output and exits with its status, so refresh failures are detectable.
  • README: code fences tagged with a language (MD040).
  • Also corrected a misleading comment — the quota cache is written by quota-bearing commands (check, forecast --live, the dashboard), not passively by proxied traffic.

On the suggestion to refactor the renderer into testable functions with golden/structured fixtures: I'd prefer to skip that here. This is a self-contained, fail-soft contrib/ helper outside the published package, and a Python test harness for a single display script feels disproportionate to its scope. The clamp/threshold logic is small and now defensive against bad input. Happy to add it if you'd rather have the coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant