@nima__/oai-proxy
v0.1.0
Published
Local ChatGPT subscription proxy exposing an OpenAI-compatible API.
Readme
oaiproxy
Simple experimental proxy and local bridge that signs into a ChatGPT subscription account and exposes an OpenAI-compatible API on http://127.0.0.1:1456.
Current scope:
POST /v1/chat/completionsGET /v1/modelsGET /v1/models/:idGET /healthPOST /auth/loginPOST /auth/logoutGET /auth/statusGET /auth/callback
Requirements
- Node.js
22.11.0or newer - npm
10+ - A local browser session that can complete OpenAI OAuth
Install
npm install -g @nima__/oai-proxyRun
oaiproxyDefault listen address:
- Host:
127.0.0.1 - Port:
1455
On startup:
- If a usable auth session is found, the server starts immediately.
- If auth is missing, the terminal prompts:
No valid ChatGPT auth found. Launch browser login now? [Y/n] - If you answer
Yor press enter, the browser opens and the callback returns tohttp://localhost:1455/auth/callback
Run As A LaunchAgent
The clean macOS way to keep this running for a local OpenAI-compatible client is a user-scoped launchd agent.
That is exactly what I set up locally:
- a plist under
~/Library/LaunchAgents/ - absolute
nodepath inProgramArguments - repo root as
WorkingDirectory - stdout/stderr sent to log files under
~/Library/Logs/oaiproxy/
Important: install auth first. The background agent has no interactive terminal, so do one successful login with:
oaiproxyAfter auth exists, install the LaunchAgent:
oaiproxy-launchd installUseful commands:
oaiproxy-launchd status
oaiproxy-launchd logs
oaiproxy-launchd uninstallDefaults used by the LaunchAgent:
- Label:
dev.oaiproxy.server - Plist:
~/Library/LaunchAgents/dev.oaiproxy.server.plist - Logs:
~/Library/Logs/oaiproxy/stdout.log~/Library/Logs/oaiproxy/stderr.log
You can override the runtime parameters at install time:
PORT=1555 LOG_LEVEL=debug oaiproxy-launchd installTo change the port after the agent is already installed, edit the plist directly and reload:
# 1. Edit the PORT value in the plist
nano ~/Library/LaunchAgents/dev.oaiproxy.server.plist
# 2. Reload (this stops the running process and starts it on the new port)
launchctl unload ~/Library/LaunchAgents/dev.oaiproxy.server.plist
launchctl load ~/Library/LaunchAgents/dev.oaiproxy.server.plistDo not kill the process with kill or pkill — KeepAlive is set, so launchd will immediately restart it on the old port.
You can also pin a specific Node binary if your shell uses a version manager:
NODE_BINARY="$(command -v node)" oaiproxy-launchd installIf your reusable auth lives under a custom CODEX_HOME, pass that during install too:
CODEX_HOME="/path/to/codex-home" oaiproxy-launchd installConfiguration
Environment variables:
HOST- Default:
127.0.0.1 - Listen host for the local server.
- Default:
PORT- Default:
1455 - Listen port for the local server.
- Default:
LOG_LEVEL- Default:
info - Pino log level. Accepts
fatal,error,warn,info,debug,trace,silent.
- Default:
UPSTREAM_TIMEOUT_MS- Default:
30000 - Timeout used for OAuth token exchange and upstream Codex requests.
- Default:
The OAuth callback URL is derived from the current PORT and always uses:
http://localhost:<PORT>/auth/callback
Auth files
Primary auth file written by this project:
~/.chatgpt-codex/auth.json
Read-only fallback sources:
$CODEX_HOME/auth.json~/.codex/auth.json
If fallback auth is used, refreshes and new logins still write to ~/.chatgpt-codex/auth.json.
Endpoints
GET /health
- Simple liveness check.
GET /auth/status
- Shows whether auth is usable.
- Returns the source auth path, email, plan type, account id, and expiry.
POST /auth/login
- Starts OAuth login and opens the browser.
POST /auth/logout
- Removes the primary local auth file.
GET /v1/models
- Returns the tested model list.
GET /v1/models/:id
- Returns a single model record for supported model ids.
POST /v1/chat/completions
- Supports both
stream: trueandstream: false - Converts OpenAI chat messages into the upstream Codex responses format
- Preserves multimodal user content with OpenAI-compatible
image_urlparts - Translates upstream SSE back into OpenAI-compatible chat completion output
Quick checks
Health:
curl http://127.0.0.1:1455/healthAuth status:
curl http://127.0.0.1:1455/auth/statusModels:
curl http://127.0.0.1:1455/v1/modelsStreaming chat completion:
curl -sN http://127.0.0.1:1455/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.4",
"stream": true,
"messages": [
{ "role": "system", "content": "Reply briefly." },
{ "role": "user", "content": "Say hello in three words." }
]
}'Non-streaming chat completion:
curl http://127.0.0.1:1455/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.4",
"stream": false,
"messages": [
{ "role": "system", "content": "Reply briefly." },
{ "role": "user", "content": "Say hello in three words." }
]
}'Multimodal chat completion:
curl http://127.0.0.1:1455/v1/chat/completions \
-H 'content-type: application/json' \
-d '{
"model": "gpt-5.5",
"stream": false,
"messages": [
{ "role": "system", "content": "Describe images briefly." },
{
"role": "user",
"content": [
{ "type": "text", "text": "What is important in this image?" },
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,...",
"detail": "low"
}
}
]
}
]
}'Client setup
For any OpenAI-compatible client, use:
- Base URL:
http://127.0.0.1:1455 - Model:
gpt-5.4 - API key: leave blank unless the client insists on a value
The prototype route to use is /v1/chat/completions.
If you installed the LaunchAgent with a custom port, use that port in the client too.
Development
Typecheck:
npm run typecheckTests:
npm test