this repo has no description

summary: "CodexBar implementation notes: data sources, refresh cadence, UI, and structure." read_when:

  • Modifying usage fetching/parsing for Codex or Claude
  • Changing refresh cadence, background tasks, or menu UI
  • Reviewing architecture before feature work

CodexBar – implementation notes#

Data source#

  • Codex: run codex /status inside a native PTY (no tmux, no web/cookie login). We parse the rendered rows for session/weekly limits and credits; when the CLI shows an update prompt we auto-press Down+Enter and retry.
  • Account info is decoded locally from ~/.codex/auth.json (id_token JWT → email, chatgpt_plan_type); no browser scraping involved.
  • Claude: run claude /usage in a PTY and parse the text panel; retries enter and reports CLI errors verbosely.

Refresh model#

  • RefreshFrequency presets: Manual, 1m, 2m (default), 5m; persisted in UserDefaults.
  • Background Task detaches on app start, wakes per cadence, calls UsageFetcher.loadLatestUsage().
  • Manual “Refresh now” menu item always available; stale/errors are surfaced in-menu and dim the icon.
  • Optional future: auto‑seed a log if none exists via codex exec --skip-git-repo-check --json "ping"; currently not executed to avoid unsolicited usage.

UI / icon#

  • MenuBarExtra only (LSUIElement=YES). No Dock icon. Label replaced with custom NSImage.
  • Icon: 20×18 template image; top bar = 5h window, bottom hairline = weekly window; fill represents “percent remaining.” Dimmed when last refresh failed.
  • Menu shows 5h + weekly rows (percent left, used, reset time), last-updated time, account email + plan, refresh cadence picker, Refresh now, Quit.

App structure (Swift 6, macOS 15+)#

  • UsageFetcher: log discovery + parsing, JWT decode for account.
  • UsageStore: state, refresh loop, error handling.
  • SettingsStore: persisted cadence.
  • IconRenderer: template NSImage for bar.
  • Entry: CodexBarApp.

Packaging & signing#

  • Scripts/package_app.sh: swift build (arm64), writes CodexBar.app + Info.plist, copies Icon.icns if present; seeds Sparkle keys/feed.
  • Scripts/sign-and-notarize.sh: uses APP_STORE_CONNECT_* creds and Developer ID identity (Y5PE65HELJ) to sign, notarize, staple, zip (CodexBar-0.1.0.zip). Adjust identity/versions as needed.
  • Sparkle: Info.plist contains SUFeedURL (GitHub Releases appcast) and SUPublicEDKey placeholder; updater is SPUStandardUpdaterController, menu has “Check for Updates…”.

Limits / edge cases#

  • If no token_count yet in the latest session, menu shows “No usage yet.”
  • Schema changes to Codex events could break parsing; errors surface in the menu.
  • Only arm64 scripted; add x86_64/universal if desired.

Alternatives considered#

  • Fake TTY + /status: unnecessary; structured token_count already present in logs after any prompt.
  • Browser scrape of https://chatgpt.com/codex/settings/usage: skipped (cookie handling & brittleness).

Learnings / decisions#

  • About panel: AboutPanelOptionKey.credits needs NSAttributedString; we supply credits + icon safely.
  • Menu palette: keep primary by default, apply .secondary only to meta lines, and use .buttonStyle(.plain) to avoid tint overriding colors.
  • Usage fetch runs off-main via detached task to keep the menu responsive if logs grow.
  • Emoji branding lives only in README; app name stays CodexBar.
  • Swift 6 strict concurrency enabled via StrictConcurrency upcoming feature to catch data-race risks early.