Court is a localhost dashboard and verified-model gateway for CLIProxyAPI. It shows connected OAuth accounts, model availability, quota windows, reset times, and custom OpenAI-compatible endpoints. It can also restart the Codex desktop app against the local router, launch it directly with Gemini 3.7 Flash from a connected Antigravity pool, or restore the normal ChatGPT connection.
Court is local administration software. It is not affiliated with OpenAI, Anthropic, Google, xAI, Moonshot AI, or the OpenCode project.
- macOS
- Node.js 20 or newer
- CLIProxyAPI installed and configured
- The Codex desktop app, only if you want the Codex routing toggle
- Gemini CLI, only if you want Google sign-in and Gemini CLI status in Court
git clone https://github.com/mars-s/court-router.git court
cd court
mkdir -p "$HOME/.local/bin"
ln -s "$PWD/court" "$HOME/.local/bin/court"Make sure $HOME/.local/bin is on PATH, then run:
court up
court openStop it with court down. Use court status to check it.
Court uses Node's built-in modules, so there are no npm dependencies to install. Each person needs their own configured CLIProxyAPI installation and connected provider accounts. Account credentials, API keys and saved chats stay in local state directories and are not included in this repository.
With CLIProxyAPI running, connect the providers you want:
court connect codex
court connect claude
court connect antigravityThese commands open the provider's sign-in flow. Connect only the accounts you want to use, then open Court to check which models are available. A connected account does not guarantee access to every model.
If your CLIProxyAPI configuration is outside the default Homebrew path, set
COURT_ROUTER_CONFIG before starting Court:
export COURT_ROUTER_CONFIG=/path/to/cliproxyapi.conf
court upTo update an existing installation, stop Court, pull the latest changes inside the cloned folder, then start it again:
court down
git pull --ff-only
court upConnect your ChatGPT and Claude accounts, then choose Enable ChatGPT + Claude in Court. This one-time setup restarts Codex and selects an available GPT model as the default. In Codex, start a chat with GPT. Once its reply finishes, choose Claude Sonnet 4.6 in that chat's model picker and send your next message. Choose GPT again to continue with ChatGPT. Switching models between turns does not require another restart or a new chat.
Choose Restore ChatGPT to turn Court off. Court restores the native ChatGPT model picker and your previous GPT model, then switches existing Court chats to that GPT model before reopening the app. Active and archived chats keep their history. If a chat cannot be restored, Court reports the failure so you can retry.
Court mode keeps every chat on ChatGPT's own openai provider and points it at
Court with openai_base_url = "http://127.0.0.1:8477/chatgpt". Court picks the
backend for each turn from the model name. GPT turns go to ChatGPT unchanged,
with your own sign-in. Claude and other Router turns go to CLIProxyAPI with the
local router key. Because the provider never changes, picking Claude in a chat
that started with GPT no longer fails with "model is not supported when using
Codex with a ChatGPT account". Court does see your ChatGPT login token: it
forwards it to ChatGPT for GPT turns, and checks it against ~/.codex/auth.json
before it lets a request use the router key. It never logs it. Your dot
continues to run in ChatGPT's cloud and does not switch to Claude when you select
Claude for a local chat.
Claude gets the same Codex tools as GPT. Court turns Codex's GPT-only tool shapes
into plain functions and turns Claude's calls back. tool_search loads deferred
tools, so subagents, Sites, apps such as GitHub and Gmail, MCP servers and
plugins all work. apply_patch edits files. See docs/feature-parity.md for
what was tested and what is still open.
Current GPT chat models and Claude 3 or later accept image attachments through Court. The native model catalogue advertises both text and image inputs for these models, while preserving explicit provider capability metadata. Unknown models and text-only models are not automatically marked as image-capable.
Codex owns the chat and tool execution. Court forwards the selected model on
each request and preserves session headers, messages, instructions, tool calls,
and tool results. When a client sends only new input and a previous_response_id,
Court rebuilds the transcript from saved responses. Full-history requests remain
authoritative, so an edited or rolled-back chat does not regain discarded turns.
Provider-specific reasoning stays with its originating provider. When Claude
takes over a chat that GPT has compacted, GPT writes a plain handoff summary of
its compacted context and Claude receives that, as in T3 Code.
Court stores response history in a private response-history directory under
its state directory, with directory mode 700 and file mode 600. It retains
at most 128 snapshots and 128 MiB for seven days. This includes conversation text
and tool output. Missing delta history or opaque compacted context without a
saved transcript returns an explicit error rather than starting an empty chat.
These limits do not guarantee that a transcript fits the selected model's
context window. Existing chats that contain only opaque provider context may
need a readable transcript before their first switch.
The design follows T3 Code's provider switching and context transfer: one visible conversation and portable history when providers change. Court uses Codex's existing conversation and CLIProxyAPI's Responses translation rather than launching a separate Claude Code session. It does not move hidden reasoning, provider-native approval state, or a native Claude Code session into Codex.
Connect Gemini CLI with court connect gemini, then choose Sign in with
Google. Gemini CLI OAuth is separate from CLIProxyAPI in current 7.3.x
releases, so Court reports the account but does not claim its models are routed
into Codex. Google AI Studio keys and Vertex service accounts use separate API
quotas.
If gemini-3.7-flash-high is available through a connected Antigravity account,
the Overview page offers a dedicated Use Gemini 3.7 Flash button. This is an
Antigravity-backed router mode; it does not reuse Gemini CLI credentials.
After connecting any OpenRouter endpoint, Manage can reuse that stored key to add
openrouter/free and openrouter/auto. The free router selects a compatible free
model for each request. The Auto router is task-aware and charges the rate of the
model it selects.
Court also sets up a second copy of the Claude desktop app, Claude (Gateway), in gateway mode on the same Router. Its model menu holds your Claude, Codex and OpenCode Go models, and it can run Claude Code sessions on another machine over SSH. Your normal Claude app is not changed.
court claude relay install # web search relay, starts at login
court claude app # creates /Applications/Claude (Gateway).app
court claude config # builds the config from the Router's modelsSee docs/claude-desktop.md for the Router config it needs, the settings file and the rules the desktop app imposes.
Court uses these defaults:
- dashboard:
http://127.0.0.1:8477 - verified model API:
http://127.0.0.1:8477/v1 - router:
http://127.0.0.1:8317 - CLIProxyAPI config:
/opt/homebrew/etc/cliproxyapi.conf - private state:
$HOME/.local/state/court - OAuth accounts:
$HOME/.cli-proxy-api - Codex config:
$HOME/.codex/config.toml
Override paths with the environment variables in .env.example. The launcher does not parse .env files automatically.
Court binds only to 127.0.0.1. It validates the Host and Origin headers, applies a restrictive Content Security Policy, caps request bodies, and redacts common tokens from the log viewer. API keys are written outside the repository with mode 600; the browser receives only the last four characters. The dashboard has no remote fonts, scripts, analytics, or CDN requests.
The Codex toggle edits your Codex config and creates a timestamped backup beside it before restarting the app. OAuth login and custom-provider changes alter local CLIProxyAPI state. Review server.mjs before using Court on a shared machine.
Run the included checks before publishing changes:
npm run check
npm test
npm run securityThe automated tests cover catalogue generation, macOS launch commands and conversation forwarding through a simulated upstream service. They do not verify a live provider sign-in or a complete desktop app conversation on your machine.
These tools look inside each layer between the ChatGPT app and the models.
node scripts/debug/doctor.mjsshows Court, the Router, the Codex config, chats with a broken index, Court's recent trace and recent app log errors.- Create
trace.onin Court's state folder, or start Court withCOURT_TRACE=1, to record every model request intrace.jsonl: backend, model, status, error text and the history's item types and reasoning IDs. Message text is never recorded. node scripts/debug/app-server.mjs METHOD 'PARAMS_JSON' ...calls the desktop app's own Codex app-server, the API its UI uses for reading, resuming and editing (thread/revert) chats. Add--home DIRto work on copied chats.node scripts/debug/chat-index.mjslists chats that fail to edit with "durable rollout shrank before projection". Quit ChatGPT and add--applyto rebuild them. Court also does this whenever it restarts ChatGPT.
Codex keeps an index of each chat that records how far into the chat file it
has read. Court's cleanup of older chat files makes those files shorter, so
Court now resets that chat's index at the same time, saving the removed rows
under chat-index-backups in Court's state folder.
npm run bench runs real chats through the Codex engine bundled with the
ChatGPT app, so each turn takes the same path as one typed into the app. It
checks every model and every ordered pair of models in parallel, and takes
about a minute for two models. See docs/benchmark.md
for the full list of checks, options and what a failure means.
After a ChatGPT update, run it first. It starts by checking that the app still has every engine method, event and field Court relies on, and names the missing piece if not.
Court is available under the MIT License. Provider logos remain subject to their owners' trademark rules. See THIRD_PARTY_NOTICES.md and docs/logo-sources.md.