codex-helper 0.3.0

A Rust-based local helper / proxy for Codex CLI traffic with multi-provider routing, usage-aware switching, filtering, and session helpers.
codex-helper-0.3.0 is not a library.

codex-helper (Codex CLI Local Helper / Proxy)

Put Codex / Claude Code behind a small local “bumper”:
centralize all your relays / keys / quotas, auto-switch when an upstream is exhausted or failing, and get handy CLI helpers for sessions, filtering, and diagnostics.

中文说明: README.md


Why codex-helper?

codex-helper is a good fit if any of these sound familiar:

  • You’re tired of hand-editing ~/.codex/config.toml
    Changing model_provider / base_url by hand is easy to break and annoying to restore.

  • You juggle multiple relays / keys and switch often
    You’d like OpenAI / Packy / your own relays managed in one place, and a single command to select the “current” one.

  • You discover exhausted quotas only after 401/429s
    You’d prefer “auto-switch to a backup upstream when quota is exhausted” instead of debugging failures.

  • You want a CLI way to quickly resume Codex sessions
    For example: “show me the last session for this project and give me codex resume <ID>.”

  • You want a local layer for redaction + logging
    Requests go through a filter first, and all traffic is logged to a JSONL file for analysis and troubleshooting.


Quick Start (TL;DR)

1. Install (recommended: cargo-binstall)

cargo install cargo-binstall
cargo binstall codex-helper   # installs codex-helper and the short alias `ch`

This installs codex-helper and ch into your Cargo bin directory (usually ~/.cargo/bin).
Make sure that directory is on your PATH so you can run them from anywhere.

Prefer building from source?
Run cargo build --release and use target/release/codex-helper / ch.

2. One-command helper for Codex (recommended)

codex-helper
# or shorter:
ch

This will:

  • Start a Codex proxy on 127.0.0.1:3211;
  • Guard and, if needed, rewrite ~/.codex/config.toml to point Codex at the local proxy (backing up the original config on first run);
  • If ~/.codex-helper/config.json is still empty, bootstrap a default upstream from ~/.codex/config.toml + auth.json;
  • If running in an interactive terminal, show a built-in TUI dashboard (disable with --no-tui; press q to quit);
  • On Ctrl+C, attempt to restore the original Codex config from the backup.

After that, you keep using your usual codex ... commands; codex-helper just sits in the middle.

3. Optional: switch the default target to Claude (experimental)

By default, commands assume Codex. If you primarily use Claude Code, you can flip the default:

codex-helper default --claude   # set default target service to Claude (experimental)

After this:

  • codex-helper serve (without flags) will start a Claude proxy on 127.0.0.1:3210;
  • codex-helper config list/add/set-active (without --codex/--claude) will operate on Claude configs.

You can always check the current default with:

codex-helper default

Common configuration: multi-upstream failover

The most common and powerful way to use codex-helper is to let it fail over between multiple upstreams automatically when one is failing or out of quota.

The key idea: put your primary and backup upstreams in the same config’s upstreams array, instead of as separate configs.

Example ~/.codex-helper/config.json:

{
  "version": 1,
  "codex": {
    "active": "codex-main",
    "configs": {
      "codex-main": {
        "name": "codex-main",
        "alias": null,
        "upstreams": [
          {
            "base_url": "https://codex-api.packycode.com/v1",
            "auth": { "auth_token_env": "PACKYCODE_API_KEY" },
            "tags": { "provider_id": "packycode", "source": "codex-config" }
          },
          {
            "base_url": "https://co.yes.vg/v1",
            "auth": { "auth_token_env": "YESCODE_API_KEY" },
            "tags": { "provider_id": "yes", "source": "codex-config" }
          }
        ]
      }
    }
  },
  "claude": { "active": null, "configs": {} },
  "default_service": null
}

With this layout:

  • active = "codex-main" → the load balancer chooses between upstreams[0] (Packy) and upstreams[1] (Yes);
  • when an upstream either:
    • exceeds the failure threshold (FAILURE_THRESHOLD in src/lb.rs:6), or
    • is marked usage_exhausted = true by usage_providers, the LB will prefer the other upstream whenever possible.

Command cheatsheet

Daily use

  • Start Codex helper (recommended):
    • codex-helper / ch
  • Explicit Codex / Claude proxy:
    • codex-helper serve (Codex, default port 3211)
    • codex-helper serve --no-tui (disable the built-in TUI dashboard)
    • codex-helper serve --codex
    • codex-helper serve --claude (Claude, default port 3210)

Turn Codex / Claude on/off via local proxy

  • Switch Codex / Claude to the local proxy:

    codex-helper switch on          # Codex
    codex-helper switch on --claude # Claude (experimental)
    
  • Restore original configs from backup:

    codex-helper switch off
    codex-helper switch off --claude
    
  • Inspect current switch status:

    codex-helper switch status
    codex-helper switch status --codex
    codex-helper switch status --claude
    

Manage upstream configs (providers / relays)

  • List configs (defaults to Codex, can target Claude explicitly):

    codex-helper config list
    codex-helper config list --claude
    
  • Add a new config:

    # Codex
    codex-helper config add openai-main \
      --base-url https://api.openai.com/v1 \
      --auth-token-env OPENAI_API_KEY \
      --alias "Main OpenAI quota"
    
    # Claude (experimental)
    codex-helper config add claude-main \
      --base-url https://api.anthropic.com/v1 \
      --auth-token-env ANTHROPIC_AUTH_TOKEN \
      --alias "Claude main quota" \
      --claude
    
  • Set the active config:

    codex-helper config set-active openai-main
    codex-helper config set-active claude-main --claude
    

Sessions, usage, diagnostics

  • Session helpers (Codex):

    codex-helper session list
    codex-helper session last
    
  • Usage & logs:

    codex-helper usage summary
    codex-helper usage tail --limit 20 --raw
    
  • Status & doctor:

    codex-helper status
    codex-helper doctor
    
    # JSON outputs for scripts / UI integration
    codex-helper status --json | jq .
    codex-helper doctor --json | jq '.checks[] | select(.status != "ok")'
    

Example workflows

Scenario 1: Manage multiple relays / keys and switch quickly

# 1. Add configs for different providers
codex-helper config add openai-main \
  --base-url https://api.openai.com/v1 \
  --auth-token-env OPENAI_API_KEY \
  --alias "Main OpenAI quota"

codex-helper config add packy-main \
  --base-url https://codex-api.packycode.com/v1 \
  --auth-token-env PACKYCODE_API_KEY \
  --alias "Packy relay"

codex-helper config list

# 2. Select which config is active
codex-helper config set-active openai-main   # use OpenAI
codex-helper config set-active packy-main    # use Packy

# 3. Point Codex at the local proxy (once)
codex-helper switch on

# 4. Start the proxy with the current active config
codex-helper

Scenario 2: Resume Codex sessions by project

cd ~/code/my-app

codex-helper session list   # list recent sessions for this project
codex-helper session last   # show last session + a codex resume command

You can also query sessions for any directory without cd:

codex-helper session list --path ~/code/my-app
codex-helper session last --path ~/code/my-app

This is especially handy when juggling multiple side projects: you don’t need to remember session IDs, just tell codex-helper which directory you care about and it will find the most relevant sessions and suggest codex resume <ID>.


Advanced configuration (optional)

Most users do not need to touch these. If you want deeper customization, these files are relevant:

  • Main config: ~/.codex-helper/config.json
  • Filter rules: ~/.codex-helper/filter.json
  • Usage providers: ~/.codex-helper/usage_providers.json
  • Request logs: ~/.codex-helper/logs/requests.jsonl
  • Detailed debug logs (optional): ~/.codex-helper/logs/requests_debug.jsonl (only created when http_debug split is enabled)

Codex official files:

  • ~/.codex/auth.json: managed by codex login; codex-helper only reads it.
  • ~/.codex/config.toml: managed by Codex CLI; codex-helper touches it only via switch on/off.

config.json structure (brief)

{
  "codex": {
    "active": "openai-main",
    "configs": {
      "openai-main": {
        "name": "openai-main",
        "alias": "Main OpenAI quota",
        "upstreams": [
          {
            "base_url": "https://api.openai.com/v1",
            "auth": {
              "auth_token": null,
              "auth_token_env": "OPENAI_API_KEY",
              "api_key": null,
              "api_key_env": null
            },
            "tags": {
              "source": "codex-config",
              "provider_id": "openai"
            }
          }
        ]
      }
    }
  }
}

Key ideas:

  • active: the name of the currently active config;
  • configs: a map of named configs;
  • each upstream is one endpoint, ordered by priority (primary → backups).

usage_providers.json

Path: ~/.codex-helper/usage_providers.json. If it does not exist, codex-helper will write a default file similar to:

{
  "providers": [
    {
      "id": "packycode",
      "kind": "budget_http_json",
      "domains": ["packycode.com"],
      "endpoint": "https://www.packycode.com/api/backend/users/info",
      "token_env": null,
      "poll_interval_secs": 60
    }
  ]
}

For budget_http_json:

  • up to date usage is obtained by calling endpoint with a Bearer token (from token_env or the associated upstream’s auth_token / auth_token_env);
  • if the upstream uses auth_token_env, the token is read from that environment variable at runtime;
  • the response is inspected for fields like monthly_budget_usd / monthly_spent_usd to decide if the quota is exhausted;
  • associated upstreams are then marked usage_exhausted = true in LB state; when possible, LB avoids these upstreams.

Filtering & logging

  • Filter rules: ~/.codex-helper/filter.json, e.g.:

    [
      { "op": "replace", "source": "your-company.com", "target": "[REDACTED_DOMAIN]" },
      { "op": "remove",  "source": "super-secret-token" }
    ]
    

    Filters are applied to the request body before sending it upstream; rules are reloaded based on file mtime.

  • Logs: ~/.codex-helper/logs/requests.jsonl, each line is a JSON object like:

    {
      "timestamp_ms": 1730000000000,
      "service": "codex",
      "method": "POST",
      "path": "/v1/responses",
      "status_code": 200,
      "duration_ms": 1234,
      "config_name": "openai-main",
      "upstream_base_url": "https://api.openai.com/v1",
      "usage": {
        "input_tokens": 123,
        "output_tokens": 456,
        "reasoning_tokens": 0,
        "total_tokens": 579
      }
    }
    

These fields form a stable contract: future versions will only add fields, not remove or rename existing ones, so you can safely build scripts and dashboards on top of them.

When retries happen, logs may also include a retry object (e.g. retry.attempts and retry.upstream_chain) to help you understand which upstreams were tried before the final result.

Optional HTTP debug logs (for 4xx/5xx)

To help diagnose upstream 400 and other non-2xx responses, codex-helper can optionally attach an http_debug object to each log line (request headers, request body preview, upstream response headers/body preview, etc.).

Enable it via env vars (off by default):

  • CODEX_HELPER_HTTP_DEBUG=1: only write http_debug for non-2xx upstream responses
  • CODEX_HELPER_HTTP_DEBUG_ALL=1: write http_debug for all requests (can grow logs quickly)
  • CODEX_HELPER_HTTP_DEBUG_BODY_MAX=65536: max bytes for request/response body preview (will truncate)
  • CODEX_HELPER_HTTP_DEBUG_SPLIT=1: write large http_debug blobs to requests_debug.jsonl and keep only http_debug_ref in requests.jsonl (recommended when *_ALL=1)

You can also print a truncated http_debug JSON directly to the terminal on non-2xx responses (off by default):

  • CODEX_HELPER_HTTP_WARN=1: emit a warn log with http_debug JSON for non-2xx upstream responses
  • CODEX_HELPER_HTTP_WARN_ALL=1: emit for all requests (not recommended)
  • CODEX_HELPER_HTTP_WARN_BODY_MAX=65536: max bytes for body preview used by terminal output (will truncate)

Sensitive headers are redacted automatically (e.g. Authorization/Cookie). If you need to scrub secrets inside request bodies, consider using ~/.codex-helper/filter.json.

Upstream retries (default 2 attempts)

Some upstream failures are transient (network hiccups, 502/503/504/524, or Cloudflare/WAF-like HTML challenge pages). codex-helper can perform a small number of retries before any response bytes are streamed to the client, and will try to switch to a different upstream when possible.

  • Global defaults live under the retry block in ~/.codex-helper/config.json. Environment variables with the same names can override them at runtime (useful for temporary debugging).
  • CODEX_HELPER_RETRY_MAX_ATTEMPTS=2: max attempts (default from retry.max_attempts; max 8; set to 1 to disable)
  • CODEX_HELPER_RETRY_ON_STATUS=502,503,504,524: retry on these status codes (supports ranges like 500-599)
  • CODEX_HELPER_RETRY_ON_CLASS=upstream_transport_error,cloudflare_timeout,cloudflare_challenge: retry on these error classes
  • CODEX_HELPER_RETRY_BACKOFF_MS=200 / CODEX_HELPER_RETRY_BACKOFF_MAX_MS=2000 / CODEX_HELPER_RETRY_JITTER_MS=100: retry backoff (ms)
  • CODEX_HELPER_RETRY_CLOUDFLARE_CHALLENGE_COOLDOWN_SECS=300 / CODEX_HELPER_RETRY_CLOUDFLARE_TIMEOUT_COOLDOWN_SECS=60 / CODEX_HELPER_RETRY_TRANSPORT_COOLDOWN_SECS=30: upstream cooldown penalties (seconds)

Example config (~/.codex-helper/config.json):

{
  "retry": {
    "max_attempts": 2,
    "backoff_ms": 200,
    "backoff_max_ms": 2000,
    "jitter_ms": 100,
    "on_status": "502,503,504,524",
    "on_class": ["upstream_transport_error", "cloudflare_timeout", "cloudflare_challenge"],
    "cloudflare_challenge_cooldown_secs": 300,
    "cloudflare_timeout_cooldown_secs": 60,
    "transport_cooldown_secs": 30
  }
}

Note: retries may replay non-idempotent POST requests (potential double-billing or duplicate writes). Only enable retries if you accept this risk, and keep the attempt count low.

Log file size control (recommended)

requests.jsonl is append-only by default. To avoid it growing without bound, codex-helper supports automatic log rotation (enabled by default):

  • CODEX_HELPER_REQUEST_LOG_MAX_BYTES=52428800: maximum bytes per log file before rotating (requests.jsonlrequests.<timestamp_ms>.jsonl; requests_debug.jsonlrequests_debug.<timestamp_ms>.jsonl) (default 50MB)
  • CODEX_HELPER_REQUEST_LOG_MAX_FILES=10: how many rotated files to keep (default 10)
  • CODEX_HELPER_REQUEST_LOG_ONLY_ERRORS=1: only log non-2xx requests (reduces disk usage; off by default)

Relationship to cli_proxy and cc-switch

  • cli_proxy: a multi-service daemon + Web UI with a broader scope (Codex, Claude, etc.) and centralized monitoring.
  • cc-switch: a desktop GUI supplier/MCP manager focused on “manage configs in one place, apply to many clients”.

codex-helper takes inspiration from both, but stays deliberately lightweight:

  • focused on Codex CLI (with experimental Claude support);
  • single binary, no daemon, no Web UI;
  • designed to be a small CLI companion you can run ad hoc, or embed into your own scripts and tooling.