FMG-11: Add ChatGPT as a third usage provider #1

Open
butterrobot wants to merge 3 commits from butterrobot/fmg-11-chatgpt-provider into main AGit
Collaborator

Adds a ChatGPT provider alongside Cursor and Claude with the same three auth modes: auto (Codex CLI session from ~/.codex/auth.json, CODEX_HOME aware), manual access token, and OpenAI Admin API key for 7-day tokens and cost. Also extracts a shared JWT payload helper reused by CursorAuthService, replaces the duplicated provider lookup with ProviderID.provider, builds the Settings provider tabs from ProviderID.allCases, and starts newly-introduced providers disabled for existing installs. The ChatGPT usage endpoint is unofficial (same caveat the README carries for Cursor and Claude) and could not be exercised against a live account from the build environment.

Adds a ChatGPT provider alongside Cursor and Claude with the same three auth modes: auto (Codex CLI session from ~/.codex/auth.json, CODEX_HOME aware), manual access token, and OpenAI Admin API key for 7-day tokens and cost. Also extracts a shared JWT payload helper reused by CursorAuthService, replaces the duplicated provider lookup with ProviderID.provider, builds the Settings provider tabs from ProviderID.allCases, and starts newly-introduced providers disabled for existing installs. The ChatGPT usage endpoint is unofficial (same caveat the README carries for Cursor and Claude) and could not be exercised against a live account from the build environment.
Adds a ChatGPT provider alongside Cursor and Claude, following the same
provider/auth/metric structure:

- Auto mode reads the Codex CLI session from ~/.codex/auth.json (CODEX_HOME
  aware) and queries the ChatGPT usage endpoint for the 5-hour and weekly
  rate-limit windows.
- Manual mode accepts a pasted ChatGPT access token; the account id and plan
  are recovered from the token's own claims.
- Admin mode reports 7-day input/output tokens and cost from the OpenAI
  organization usage and costs APIs.

Supporting changes:

- Extract base64url JWT payload decoding into a shared JWT helper, reused by
  CursorAuthService.
- Replace the duplicated provider lookup in UsageRefreshService and
  SettingsView with ProviderID.provider.
- Build the Settings provider tabs from ProviderID.allCases.
- Providers missing from previously saved settings now start disabled, so the
  upgrade does not add an unconfigured indicator to an existing menu bar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ChatGPT provider no longer touches credentials at all. Usage now comes
from `codex app-server`, the CLI's stdio JSON-RPC interface: handshake, then
`account/rateLimits/read`. Codex owns authentication end to end, so this works
wherever it keeps its credentials — file, keyring or ephemeral — and the app
never reads, stores or forwards a token.

Removed accordingly:

- Reading ~/.codex/auth.json and the CODEX_HOME environment variable.
- The manual-token and Admin API key modes for this provider, along with the
  OpenAI organization usage/cost metrics. ChatGPT now offers Auto only, and
  Settings no longer queries the Keychain for a provider that cannot use it.
- The shared JWT helper, which existed only to read claims out of Codex
  tokens; CursorAuthService is back to its original form.

This also drops the HTTP path, whose endpoint was wrong: the earlier commit
used /backend-api/codex/usage, while Codex actually polls
/backend-api/wham/usage with a different response shape.

The app-server command is documented as experimental and
`account/rateLimits/read` is undocumented, so the decoder accepts the windows
wrapped in `rateLimits` or inlined at the root, and an absolute `resetsAt` or
a `resetsInSeconds` countdown.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit's messages were rejected by Codex. Checked against the
app-server documentation rather than guessing again:

- app-server omits the `"jsonrpc": "2.0"` header on the wire; we were sending
  it on every message.
- `initialize` takes `params.capabilities.experimentalApi`, which part of the
  server's surface is gated behind. We sent only `clientInfo`.
- The documented invocation is a bare `codex app-server`. The `-s read-only
  -a never` prefix was defensive but undocumented, and nothing here starts a
  thread or runs a turn for it to constrain.

Also fixed alongside:

- A PATH-only install was unreachable: `URL(fileURLWithPath: "codex")`
  resolves against the working directory, so the fallback now goes through
  `/usr/bin/env`.
- stdout and stderr are drained as they fill instead of after exit, which
  removes both the pipe-buffer deadlock and the full timeout wait for a
  server that outlives its stdin. The request returns as soon as its
  response lands.
- Failures now carry the exit status and the stderr tail instead of a bare
  "could not parse", so the next report says what actually happened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/1/head:butterrobot/fmg-11-chatgpt-provider
git switch butterrobot/fmg-11-chatgpt-provider

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff butterrobot/fmg-11-chatgpt-provider
git switch butterrobot/fmg-11-chatgpt-provider
git rebase main
git switch main
git merge --ff-only butterrobot/fmg-11-chatgpt-provider
git switch butterrobot/fmg-11-chatgpt-provider
git rebase main
git switch main
git merge --no-ff butterrobot/fmg-11-chatgpt-provider
git switch main
git merge --squash butterrobot/fmg-11-chatgpt-provider
git switch main
git merge --ff-only butterrobot/fmg-11-chatgpt-provider
git switch main
git merge butterrobot/fmg-11-chatgpt-provider
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
fmartingr/macos-ai-menubar-usage!1
No description provided.