Metadata-Version: 2.4
Name: codex-api-proxy
Version: 0.1.6
Summary: Local OpenAI-compatible HTTP proxy backed by Codex/OpenAI credentials
Author: codex-api-proxy contributors
License-Expression: MIT
Keywords: codex,openai,proxy,api,local
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi<1,>=0.115
Requires-Dist: httpx<1,>=0.27
Requires-Dist: uvicorn[standard]<1,>=0.30
Provides-Extra: dev
Requires-Dist: pytest<9,>=8; extra == "dev"
Requires-Dist: pytest-asyncio<1,>=0.23; extra == "dev"

# codex-api-proxy

Local OpenAI-compatible HTTP proxy backed by Codex/OpenAI credentials.

The proxy has one runtime path: it reads credentials from `~/.codex/auth.json` or `CODEX_HOME/auth.json`, then forwards requests to the upstream OpenAI/Codex API. It no longer runs local `codex exec`, starts `codex app-server`, manages workspaces, or exposes sandbox/agent/fast engine settings.

## Product Scope

This package is intentionally narrow:

- expose OpenAI-compatible local endpoints for clients such as OpenClaw;
- reuse the user's existing Codex/OpenAI login credentials;
- support `/v1/chat/completions`, `/v1/responses`, `/v1/messages`, and `/v1/models`;
- convert Chat Completions to Responses API when using ChatGPT Codex credentials;
- convert Anthropic-style Messages API requests to Responses API for local Anthropic-compatible clients such as Claude Code;
- accept image input for vision tasks through Chat Completions `image_url` parts and Anthropic `image` content blocks;
- otherwise transparently forward OpenAI API-key requests to OpenAI's native `/chat/completions`.

Out of scope:

- local command execution;
- workspace read/write control;
- Codex sandbox configuration;
- app-server workers;
- simulated tool-call wrapping;
- custom model execution engines.

Tool calls and function calls are handled by the upstream OpenAI-compatible API response, not by local prompt wrapping.

## Install

```bash
pip3 install codex-api-proxy
```

For local development from this checkout:

```bash
python3 -m pip install -e '.[dev]'
```

## Credentials

The proxy reads Codex credentials from:

```text
~/.codex/auth.json
```

or, when `CODEX_HOME` is set:

```text
$CODEX_HOME/auth.json
```

Supported auth shapes:

- `OPENAI_API_KEY`: forwarded to `https://api.openai.com/v1`;
- Codex/ChatGPT `tokens.access_token`: forwarded to `https://chatgpt.com/backend-api/codex`.

For Codex/ChatGPT token auth, the proxy refreshes tokens automatically:

- before a request when the access token JWT expires within 5 minutes;
- before a request when `last_refresh` is older than 8 days and the access token has no parseable expiry;
- after an upstream `401`, then it retries the original request once.

Refreshed tokens are written back to `auth.json` with `0600` permissions when supported by the platform.

Outbound upstream proxy settings are controlled by environment variables:

- `CODEX_API_PROXY_HTTPS_PROXY`
- `HTTPS_PROXY`
- `https_proxy`

When none are set, the proxy connects directly to the upstream API without a proxy.

You can also set the upstream proxy explicitly at startup:

```bash
codex-api-proxy start --proxy=http://127.0.0.1:8118
```

If the upstream connection passes through a corporate TLS proxy or another endpoint
using a private/self-signed certificate chain, point the proxy at a CA bundle that
trusts that chain:

- `CODEX_API_PROXY_CA_BUNDLE`
- `REQUESTS_CA_BUNDLE`
- `SSL_CERT_FILE`

For example:

```bash
CODEX_API_PROXY_CA_BUNDLE=/path/to/internal-ca-bundle.pem codex-api-proxy start
```

## Run

Start in the background:

```bash
codex-api-proxy start
```

Run in the foreground with debug logs:

```bash
codex-api-proxy start --foreground --log-level debug
```

Bind to all interfaces with local bearer auth:

```bash
codex-api-proxy start --host 0.0.0.0 --api-key local-secret
```

Binding to `0.0.0.0`, `::`, or `[::]` requires `--api-key`; the proxy refuses to start on public interfaces without local bearer auth.

Start with an explicit upstream proxy:

```bash
codex-api-proxy start --proxy=http://127.0.0.1:8118
```

Restart using the last saved start config:

```bash
codex-api-proxy restart
```

Stop:

```bash
codex-api-proxy stop
```

Status:

```bash
codex-api-proxy status --verbose
```

Run diagnostics:

```bash
codex-api-proxy doctor
```

## CLI Options

- `--host`: bind host, default `127.0.0.1`
- `--port`: bind port, default `8765`
- `--api-key`: require client requests to send `Authorization: Bearer <key>`
- `--proxy`: upstream HTTP(S) proxy URL for OpenAI/Codex API calls; omitted means direct upstream connections
- `--log-level`: `debug`, `info`, `warning`, or `error`
- `--log-format`: `text` or `json` for application event logs
- `--models-cache-ttl`: seconds to cache `/v1/models`, default `300`; use `0` to disable
- `--pid-file`: daemon pid file, default `~/.codex-api-proxy/codex-api-proxy.pid`
- `--log-file`: daemon log file, default `~/.codex-api-proxy/codex-api-proxy.log`
- `--state-file`: daemon config file, default `~/.codex-api-proxy/config.toml`
- `--foreground`: run without daemonizing

Environment variables for the local server:

- `CODEX_PROXY_HOST`
- `CODEX_PROXY_PORT`
- `CODEX_PROXY_API_KEY`
- `CODEX_API_PROXY_HTTPS_PROXY`
- `CODEX_API_PROXY_CA_BUNDLE`
- `CODEX_PROXY_LOG_LEVEL`

Token refresh compatibility variables:

- `CODEX_REFRESH_TOKEN_URL_OVERRIDE`: override the OAuth refresh endpoint
- `CODEX_APP_SERVER_LOGIN_CLIENT_ID`: override the OAuth client id

## API

Health:

```bash
curl -sS http://127.0.0.1:8765/health
```

Models:

```bash
curl -sS http://127.0.0.1:8765/v1/models
```

`/v1/models` responses are cached locally for `--models-cache-ttl` seconds. Use `?refresh=true` to bypass and refresh the cache.

Chat completion:

```bash
curl -sS http://127.0.0.1:8765/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","messages":[{"role":"user","content":"Reply with exactly: pong"}]}'
```

Streaming chat completion:

```bash
curl -N http://127.0.0.1:8765/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","stream":true,"messages":[{"role":"user","content":"Reply with exactly: pong"}]}'
```

Responses API passthrough:

```bash
curl -sS http://127.0.0.1:8765/v1/responses \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","input":"Reply with exactly: pong"}'
```

Anthropic-style Messages API:

```bash
curl -sS http://127.0.0.1:8765/v1/messages \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "gpt-5.5",
    "max_tokens": 128,
    "system": "Be brief.",
    "messages": [{"role":"user","content":"Reply with exactly: pong"}]
  }'
```

Streaming Anthropic-style Messages API:

```bash
curl -N http://127.0.0.1:8765/v1/messages \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "gpt-5.5",
    "max_tokens": 128,
    "stream": true,
    "messages": [{"role":"user","content":[{"type":"text","text":"Reply with exactly: pong"}]}]
  }'
```

When local `--api-key` auth is enabled, `/v1/messages` accepts either `Authorization: Bearer <key>` or `x-api-key: <key>` because Anthropic-compatible clients vary in which header they send.

The Anthropic compatibility layer currently covers text messages, system prompts, streaming text deltas, and image content blocks. Full Anthropic `tool_use` / `tool_result` round-tripping is not yet implemented.

When `--api-key` is configured:

```bash
curl -sS http://127.0.0.1:8765/v1/chat/completions \
  -H 'Authorization: Bearer local-secret' \
  -H 'Content-Type: application/json' \
  -d '{"model":"gpt-5.5","messages":[{"role":"user","content":"Reply with exactly: pong"}]}'
```

## Image Recognition

The proxy forwards image input to the upstream Codex/OpenAI Responses API for vision tasks.

Supported input shapes:

- Chat Completions: `image_url` parts with a `data:` URL or remote `https://` URL, plus optional `detail`
- Chat Completions: Responses-style `input_image` parts (passed through as-is)
- Anthropic Messages: `image` content blocks with `source.type` of `base64` or `url`

When using ChatGPT Codex credentials, image parts are converted to Responses API `input_image` parts before the upstream call. `/v1/responses` requests with `input_image` are passed through unchanged.

Chat Completions example:

```bash
BASE64_IMAGE=$(base64 < image.jpg)
curl -sS http://127.0.0.1:8765/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "gpt-5.5",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "What is in this image?"},
        {
          "type": "image_url",
          "image_url": {
            "url": "data:image/jpeg;base64,'"$BASE64_IMAGE"'",
            "detail": "high"
          }
        }
      ]
    }]
  }'
```

Anthropic Messages example:

```bash
BASE64_IMAGE=$(base64 < image.jpg)
curl -sS http://127.0.0.1:8765/v1/messages \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "gpt-5.5",
    "max_tokens": 256,
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "What is in this image?"},
        {
          "type": "image",
          "source": {
            "type": "base64",
            "media_type": "image/jpeg",
            "data": "'"$BASE64_IMAGE"'"
          }
        }
      ]
    }]
  }'
```

## Claude Code

Claude Code speaks the Anthropic Messages API. Point it at this proxy so requests are converted to the upstream Codex/OpenAI Responses API using your existing `~/.codex/auth.json` credentials.

### 1. Start the proxy

Start the proxy with a local API key if you want to protect the local endpoint:

```bash
codex-api-proxy start --api-key sk-tmp
```

`ANTHROPIC_AUTH_TOKEN` in Claude Code must match the value passed to `--api-key`. If you start the proxy without `--api-key`, local bearer auth is disabled and `ANTHROPIC_AUTH_TOKEN` is not required.

### 2. Configure Claude Code

Edit `~/.claude/settings.json` and set the Anthropic base URL to this proxy:

```json
{
  "env": {
    "ANTHROPIC_BASE_URL": "http://127.0.0.1:8765",
    "ANTHROPIC_AUTH_TOKEN": "sk-tmp",
    "ANTHROPIC_MODEL": "gpt-5.5",
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
    "CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING": "1"
  },
  "enabledPlugins": {
    "superpowers@claude-plugins-official": true
  },
  "skipDangerousModePermissionPrompt": true,
  "theme": "dark"
}
```

Key fields:

- `ANTHROPIC_BASE_URL`: local proxy URL, default `http://127.0.0.1:8765`
- `ANTHROPIC_AUTH_TOKEN`: must match `codex-api-proxy start --api-key ...` when local auth is enabled
- `ANTHROPIC_MODEL`: upstream model id exposed by `/v1/models`, for example `gpt-5.5`

Restart Claude Code after changing `settings.json`.

### 3. Verify

```bash
codex-api-proxy status --verbose
curl -sS http://127.0.0.1:8765/health
```

Then launch Claude Code from a terminal:

```bash
claude
```

Image attachments in Claude Code are sent as Anthropic `image` blocks and are converted by the proxy before the upstream request. Tool calling in Claude Code depends on Anthropic `tool_use` / `tool_result` support, which is not fully implemented in this proxy yet.

## Operations

`/health` is a process liveness check. It returns the proxy version and uptime without loading credentials, refreshing tokens, writing `auth.json`, or contacting upstream services.

`/ready` checks that credentials can be loaded and shows the upstream mode without exposing secrets. It may refresh Codex/ChatGPT tokens when refresh criteria are met.

`/metrics` returns local request counters as JSON. `/metrics/prometheus` returns Prometheus text format metrics.

Successful proxied responses include `x-request-id`. The proxy uses incoming `x-request-id` or `x-correlation-id` when present, otherwise it generates one.

`/metrics` returns local request counters:

- `requests_total`
- `requests_ok`
- `requests_error`
- `errors_by_status`
- `uptime_seconds`

When `--log-level debug` is set, chat completions log input messages and output messages through `codex_api_proxy.messages` and the daemon's `uvicorn.error` log stream. Streaming output is logged after the stream finishes. These logs can contain prompt and response data; use debug logging only in trusted environments.

Latency summaries are logged through `codex_api_proxy.latency`. Set `--log-format json` for structured JSON event logs.
