Multi-profile Codex CLI quota monitor with CLI and TUI
Project description
codex-quota
A utility for managing isolated Codex CLI profiles and monitoring quota usage across multiple Codex environments.
Includes JSON output for automation and a live terminal dashboard.
Note: This is an independent community tool. It is not affiliated with or endorsed by OpenAI.
Requirements
- Python 3.11+
- Official Codex CLI available as
codex
Install Codex CLI:
npm install -g @openai/codex
or
brew install codex
Tested with codex-cli 0.136.0.
Installation
Install from PyPI:
pip install codex-quota
Install from source:
git clone https://github.com/ssh-den/codex-quota.git
cd codex-quota
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
Quick Start
# Initialize (creates config and profiles directory)
codex-quota init
# Add a profile and log in
codex-quota profile add personal --login
# Check quota status
codex-quota status
# Force an auth keepalive refresh for CI or cron
codex-quota wake
# Launch the live terminal dashboard
codex-quota tui
Security
Authentication is fully delegated to the official Codex CLI. codex-quota never reads, stores, or transmits credentials. It sets CODEX_HOME and calls codex on your behalf.
Profile and config directories are created with 0700 permissions on POSIX systems so auth.json and related state are not exposed by a weak umask on multi-user machines.
Script filesystem layout
~/.codex-quota/profiles/
├── personal/
├── work/
└── testing/
~/.config/codex-quota/
└── config.json
The filesystem is the source of truth. Profiles are discovered dynamically. The configuration file stores application settings only and does not maintain a profile registry.
Configuration
Default configuration:
{
"profiles_dir": "~/.codex-quota/profiles",
"codex_bin": "codex",
"default_model": "gpt-5.4-mini",
"reasoning_effort": "low",
"refresh_seconds": 30.0
}
Edit ~/.config/codex-quota/config.json to change model defaults or reasoning effort.
On successful status-time auth keepalive, codex-quota also stores a per-profile auth_refresh map in this same config file using UTC ISO-8601 timestamps ending in Z. This state is created lazily, preserves unknown config keys, and never inspects or modifies auth.json.
Initialization
codex-quota init
Behavior:
- Create config directory if missing
- Create profiles directory if missing
- Create
config.jsonif missing - Preserve existing configuration
- Verify Codex CLI installation
- Print detected Codex version
- Discover existing profiles by scanning
~/.codex-quota/profiles/*
Overwrite configuration:
codex-quota init --overwrite-config
Run bootstrap plus quota verification (--check is an alias):
codex-quota init --verify
Profile management
List profiles:
codex-quota profile list
Add profile:
codex-quota profile add personal
Create and login immediately:
codex-quota profile add personal --login
Create and force device-code auth on headless or remote machines:
codex-quota profile add personal --login --device-auth
Print profile path:
codex-quota profile path personal
Remove profile:
codex-quota profile remove personal
Force remove (non-interactive):
codex-quota profile remove personal --yes
Profile names are validated. Path traversal, path separators, absolute paths, whitespace, and suspicious characters are rejected.
Login
codex-quota login personal
Equivalent to:
CODEX_HOME="$HOME/.codex-quota/profiles/personal" codex login
Use device authorization instead of the local browser callback flow:
codex-quota login personal --device-auth
Create the profile automatically if missing:
codex-quota login personal --create
Combine both when bootstrapping a remote profile in one step:
codex-quota login personal --create --device-auth
Status
All profiles:
codex-quota status
Single profile:
codex-quota status personal
JSON output:
codex-quota status --json
Status collection opportunistically performs an auth keepalive through the official Codex App Server before reading rate limits. When the last successful refresh for a profile is 4 hours old or older, codex-quota calls account/read with refreshToken=true. This refresh is best-effort:
- Auth-related failures are classified as
AUTH_REQUIRED - Transient refresh failures do not block a later successful rate-limit read
- No separate maintenance command is required for normal operation
Example human-readable output:
Profile Status 5h Left Week Left
personal OK 99% 69%
work AUTH_REQUIRED
Default JSON is safe for CI logs and automation:
- No absolute profile paths
- No raw
rateLimitspayload - No plan or credits metadata unless explicitly requested
Machine-readable status output includes these stable top-level fields per profile:
statusfor explicit classifications such asAUTH_REQUIREDerrorfor the raw internal/backend error string when presentrate_limitsfor normalized quota data
Opt in to the extra fields only when you need them:
codex-quota status --json --json-paths --json-raw
Quota lockout detection uses rateLimitReachedType != null. The tool does not infer lockout solely from usedPercent == 100.
Check command
Automation-oriented health check:
codex-quota check
Single profile:
codex-quota check personal
Exit codes:
| Code | Meaning |
|---|---|
| 0 | Selected profiles are authenticated, readable, and not quota-blocked |
| 2 | Codex missing, auth missing/expired, app-server failure, quota block, or no selected profiles found |
Wake command
Force the official auth keepalive even when the last successful refresh is still recent:
codex-quota wake
Single profile:
codex-quota wake personal
This is mainly useful for scheduled CI or cron jobs that want to keep managed auth warm proactively. wake follows the same login_status() -> account/read(refreshToken=true) -> account/rateLimits/read pipeline as check, prints the same human-readable table, and exits with the same codes.
Exec
Run Codex under a selected profile:
codex-quota exec personal "Reply with exactly: ok"
Disable forced JSON output:
codex-quota exec personal "Say hello" --raw
exec now respects the user's existing Codex config and rules by default instead of disabling them.
TUI
Launch:
codex-quota tui
Custom refresh interval:
codex-quota tui --refresh 15
The TUI uses Textual and dynamically discovers profiles from the filesystem.
Available commands:
refresh
login <profile> [--device-auth]
exec <profile> <prompt>
quit
Internal quota API
Status collection launches:
CODEX_HOME=<profile-home> codex app-server --stdio
Then performs the following JSON-RPC sequence:
1. initialize
2. initialized
3. account/read {"refreshToken": true} when auth refresh is due
4. account/rateLimits/read
Responses are normalized for display and safe JSON output. Human-readable tables show AUTH_REQUIRED for auth failures instead of large backend payloads. Raw rateLimits values are available only through explicit --json-raw, while raw backend error strings remain available in JSON output.
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codex_quota-1.2.0.tar.gz.
File metadata
- Download URL: codex_quota-1.2.0.tar.gz
- Upload date:
- Size: 28.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0eaef089025586a161b31eecac6b29a526793aedfa6a2acd4a15aedc822f5e91
|
|
| MD5 |
a6941b09f805da56dcc587ea81ec202e
|
|
| BLAKE2b-256 |
ae79261fa9308fa1c8ac2a391e51ae92bc4284c22c9aa03b6ea1ad04d687e06b
|
Provenance
The following attestation bundles were made for codex_quota-1.2.0.tar.gz:
Publisher:
publish-pypi.yml on ssh-den/codex-quota
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_quota-1.2.0.tar.gz -
Subject digest:
0eaef089025586a161b31eecac6b29a526793aedfa6a2acd4a15aedc822f5e91 - Sigstore transparency entry: 1732458010
- Sigstore integration time:
-
Permalink:
ssh-den/codex-quota@17cb9a33ee79eb199205199b5f4cd7b60d559947 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ssh-den
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@17cb9a33ee79eb199205199b5f4cd7b60d559947 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file codex_quota-1.2.0-py3-none-any.whl.
File metadata
- Download URL: codex_quota-1.2.0-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f1eecf7db0f06b1445659a13c45210daf234eba20a320350e82e8fa57838705
|
|
| MD5 |
2fbb4d3b148a62f5fd9c32eb59b70ff8
|
|
| BLAKE2b-256 |
7aa5cc6c4007103cdc4c1a6062b150ce79f4574fd4ceb6f2e145f95333b0bdee
|
Provenance
The following attestation bundles were made for codex_quota-1.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on ssh-den/codex-quota
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codex_quota-1.2.0-py3-none-any.whl -
Subject digest:
7f1eecf7db0f06b1445659a13c45210daf234eba20a320350e82e8fa57838705 - Sigstore transparency entry: 1732458047
- Sigstore integration time:
-
Permalink:
ssh-den/codex-quota@17cb9a33ee79eb199205199b5f4cd7b60d559947 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ssh-den
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@17cb9a33ee79eb199205199b5f4cd7b60d559947 -
Trigger Event:
workflow_dispatch
-
Statement type: