OpenAI Codex (ChatGPT OAuth) provider.
Headless clients use Codex's official device authorization flow, so the browser can be on a phone or desktop while the gateway stays behind NAT. The interactive CLI also retains the registered loopback PKCE flow. Neither path rewrites a provider-registered redirect URI.
Tokens are persisted at ~/.vis/openai-codex-auth.json. The access
token is a JWT; Codex requests require the embedded ChatGPT account
id, so this namespace validates/extracts it during login/refresh.
The dynamic quota report lives here too (dynamic-limits!): it fetches
https://chatgpt.com/backend-api/wham/usage, selects the regular Codex
bucket (or the nested Codex Spark bucket) and exposes the 5h and 7d
percentage windows as normalized Vis limit rows.
OpenAI Codex (ChatGPT OAuth) provider. Headless clients use Codex's official device authorization flow, so the browser can be on a phone or desktop while the gateway stays behind NAT. The interactive CLI also retains the registered loopback PKCE flow. Neither path rewrites a provider-registered redirect URI. Tokens are persisted at `~/.vis/openai-codex-auth.json`. The access token is a JWT; Codex requests require the embedded ChatGPT account id, so this namespace validates/extracts it during login/refresh. The dynamic quota report lives here too (`dynamic-limits!`): it fetches `https://chatgpt.com/backend-api/wham/usage`, selects the regular Codex bucket (or the nested Codex Spark bucket) and exposes the 5h and 7d percentage windows as normalized Vis limit rows.
(account-id access-token)Extract the ChatGPT account id from a Codex access-token JWT.
Extract the ChatGPT account id from a Codex access-token JWT.
(auth-await {:keys [device-auth-id user-code interval-ms expires-at]})Poll Codex's device grant, then exchange and persist only on the gateway. 403/404 mean pending in this protocol (not RFC 8628 JSON errors). Cancellation interrupts the worker; the absolute deadline also survives delayed startup.
Poll Codex's device grant, then exchange and persist only on the gateway. 403/404 mean pending in this protocol (not RFC 8628 JSON errors). Cancellation interrupts the worker; the absolute deadline also survives delayed startup.
(auth-start)Start the official Codex device flow for TUI, desktop and mobile clients. The polling capability stays in :flow on the gateway. Only the one-time user code, verification page and expiry may cross the wire. Protocol: openai/codex codex-rs/login/src/device_code_auth.rs.
Start the official Codex device flow for TUI, desktop and mobile clients. The polling capability stays in :flow on the gateway. Only the one-time user code, verification page and expiry may cross the wire. Protocol: openai/codex codex-rs/login/src/device_code_auth.rs.
(consume-reset-credit! {:keys [account-id idempotency-key] :as attempt})Use one reset explicitly confirmed for account-id. idempotency-key
identifies the logical attempt and MUST survive retries, including a lost
response. A 401/403 refresh retries that same attempt only, with account
identity checked again. A timeout or unknown response never implies success.
Use one reset explicitly confirmed for `account-id`. `idempotency-key` identifies the logical attempt and MUST survive retries, including a lost response. A 401/403 refresh retries that same attempt only, with account identity checked again. A timeout or unknown response never implies success.
(create-authorization-flow)(create-authorization-flow originator)Create PKCE verifier, CSRF state, and OpenAI authorization URL.
Create PKCE verifier, CSRF state, and OpenAI authorization URL.
(detect-credentials)Detect persisted OpenAI Codex credentials. Returns a status-friendly map or nil; does not validate with the network.
Detect persisted OpenAI Codex credentials. Returns a status-friendly map or nil; does not validate with the network.
(dynamic-limits! access-token account-id)(dynamic-limits! access-token account-id model-ref)Fetch quota windows and account-scoped reset availability. Optional
model-ref selects Codex Spark's quota bucket; reset credits remain scoped
to the account, not the model or conversation.
Fetch quota windows and account-scoped reset availability. Optional `model-ref` selects Codex Spark's quota bucket; reset credits remain scoped to the account, not the model or conversation.
(fetch-usage! access-token account-id)Fetch raw ChatGPT/Codex usage JSON from
https://chatgpt.com/backend-api/wham/usage.
Fetch raw ChatGPT/Codex usage JSON from `https://chatgpt.com/backend-api/wham/usage`.
(force-refresh-token!)(force-refresh-token! rejected-token)Force an OAuth refresh-token exchange, persist the rotated credentials, and return the provider-token map.
get-openai-codex-token! only refreshes when the stored token is locally
expired, so a token that is locally-valid but invalidated server-side
(refresh-token rotation by another client/process) would otherwise never
be replaced. The runtime's 401 recovery path calls this. Routes through
the single-flight refresh-and-persist!, so a STORM of 401s collapses
into one exchange instead of racing the rotating refresh token into
HTTP 400.
rejected-token (optional) is the access token the server just 401'd:
the single-flight reuse step will NOT hand it back, forcing a real
exchange when the on-file token is still the dead one. Throws when there
is no refresh token on file.
Force an OAuth refresh-token exchange, persist the rotated credentials, and return the provider-token map. `get-openai-codex-token!` only refreshes when the stored token is locally expired, so a token that is locally-valid but invalidated server-side (refresh-token rotation by another client/process) would otherwise never be replaced. The runtime's 401 recovery path calls this. Routes through the single-flight `refresh-and-persist!`, so a STORM of 401s collapses into one exchange instead of racing the rotating refresh token into HTTP 400. `rejected-token` (optional) is the access token the server just 401'd: the single-flight reuse step will NOT hand it back, forcing a real exchange when the on-file token is still the dead one. Throws when there is no refresh token on file.
(get-openai-codex-token!)Return a fresh Codex access token in the provider-token shape used by
Vis: {:token access-token :api-url CODEX_BASE_URL :llm-headers {...}}.
Return a fresh Codex access token in the provider-token shape used by
Vis: `{:token access-token :api-url CODEX_BASE_URL :llm-headers {...}}`.(limits)Normalized limits envelope for the OpenAI Codex provider.
Static RPM/TPM metadata comes from svar's provider catalog; this fn reports authentication state and live ChatGPT/Codex quota windows when credentials are available. A usage-endpoint 401/403 force-refreshes the rotating OAuth token and retries once so TUI/gateway/iOS status panels do not get stuck on a server-rotated access token.
Normalized limits envelope for the OpenAI Codex provider. Static RPM/TPM metadata comes from svar's provider catalog; this fn reports authentication state and live ChatGPT/Codex quota windows when credentials are available. A usage-endpoint 401/403 force-refreshes the rotating OAuth token and retries once so TUI/gateway/iOS status panels do not get stuck on a server-rotated access token.
(login! printer-fn)(login! printer-fn
{:keys [originator open-browser-fn manual-code-fn force?]
:or {originator "vis"
open-browser-fn open-browser!
manual-code-fn prompt-for-code!}})Run the Codex OAuth flow and persist fresh credentials.
Options:
:originator value forwarded to OpenAI's OAuth request.:open-browser-fn (fn [url] boolean) override for tests or
alternate frontends.:manual-code-fn (fn [printer-fn] string|nil) collector for the
final redirect URL or bare authorization code. CLI uses read-line;
the TUI injects a dialog-backed collector. Pass nil to disable manual
entry entirely.:force? when true, starts a fresh OAuth flow even if
persisted credentials already exist.Run the Codex OAuth flow and persist fresh credentials. Options: - `:originator` value forwarded to OpenAI's OAuth request. - `:open-browser-fn` `(fn [url] boolean)` override for tests or alternate frontends. - `:manual-code-fn` `(fn [printer-fn] string|nil)` collector for the final redirect URL or bare authorization code. CLI uses `read-line`; the TUI injects a dialog-backed collector. Pass nil to disable manual entry entirely. - `:force?` when true, starts a fresh OAuth flow even if persisted credentials already exist.
(parse-authorization-input input)Parse a pasted OAuth callback URL, raw query string, code#state,
or bare code. Returns {:code string? :state string?}.
Parse a pasted OAuth callback URL, raw query string, `code#state`,
or bare code. Returns `{:code string? :state string?}`.(usage->dynamic-limits usage)(usage->dynamic-limits usage model-ref)(usage->dynamic-limits usage model-ref now-ms)Convert ChatGPT/Codex /wham/usage JSON into Vis dynamic limit rows.
model-ref may be a model id string/keyword or a map with :id /
:name. It is used only for Codex Spark, whose bucket is nested in
additional_rate_limits, matching Codex/ChatGPT's usage payload. Rows are
sorted by explicit window size (5h before 7d) rather than trusting provider
bucket names (primary_window / secondary_window).
Convert ChatGPT/Codex `/wham/usage` JSON into Vis dynamic limit rows. `model-ref` may be a model id string/keyword or a map with `:id` / `:name`. It is used only for Codex Spark, whose bucket is nested in `additional_rate_limits`, matching Codex/ChatGPT's usage payload. Rows are sorted by explicit window size (5h before 7d) rather than trusting provider bucket names (`primary_window` / `secondary_window`).
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |