@jaralabs/codex-bridge 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
# @jaralabs/codex-bridge
|
|
2
2
|
|
|
3
|
-
> Use your ChatGPT Business / Team / Plus subscription from OpenCode instead
|
|
3
|
+
> Use your ChatGPT Business / Team / Plus subscription from OpenCode instead
|
|
4
|
+
> of paying for the OpenAI API.
|
|
4
5
|
|
|
5
6
|
`codex-bridge` runs an OpenAI-compatible HTTP server on `127.0.0.1:11434` that
|
|
6
|
-
forwards chat-completion requests to the
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
balance.
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
forwards chat-completion requests to the ChatGPT Responses API
|
|
8
|
+
(`https://chatgpt.com/backend-api/codex/responses`) using the OAuth session in
|
|
9
|
+
`~/.codex/auth.json`. Your OpenCode prompts consume your subscription credits
|
|
10
|
+
— not the OpenAI API balance.
|
|
11
|
+
|
|
12
|
+
The bridge is a thin, durable proxy:
|
|
13
|
+
|
|
14
|
+
* No dependency on the Codex CLI at runtime. (The CLI rejects ChatGPT Plus
|
|
15
|
+
models when invoked directly; the bridge speaks the same wire format
|
|
16
|
+
itself.)
|
|
17
|
+
* Auto-refreshes the access token via `https://auth.openai.com/oauth/token`
|
|
18
|
+
when the API returns 401, and persists the new tokens to `auth.json`.
|
|
19
|
+
* Wraps every upstream call in retry-with-jitter and a circuit breaker, so
|
|
20
|
+
transient 429/5xx errors do not bubble to OpenCode.
|
|
21
|
+
* Probes the upstream API at boot and persists the wire shape to
|
|
22
|
+
`~/.codex/bridge_capabilities.json`. A drift on the next boot (model
|
|
23
|
+
rename, missing event, status flip) is logged as a warning.
|
|
24
|
+
* Exposes `/v1/health` for in-process resilience metrics (requests, retries,
|
|
25
|
+
circuit state).
|
|
12
26
|
|
|
13
27
|
## Why
|
|
14
28
|
|
|
15
29
|
If you have a ChatGPT Business / Team / Plus plan, the OpenAI API and the
|
|
16
|
-
ChatGPT web subscription are billed separately. You can use Codex CLI /
|
|
17
|
-
App today with the subscription. `codex-bridge` exposes the same models
|
|
18
|
-
an OpenAI-compatible HTTP surface so OpenCode (and any other OpenAI
|
|
19
|
-
like Claude Code, Cline, aider) can use them without an API key.
|
|
30
|
+
ChatGPT web subscription are billed separately. You can use the Codex CLI /
|
|
31
|
+
web App today with the subscription. `codex-bridge` exposes the same models
|
|
32
|
+
over an OpenAI-compatible HTTP surface so OpenCode (and any other OpenAI
|
|
33
|
+
client like Claude Code, Cline, aider) can use them without an API key.
|
|
20
34
|
|
|
21
35
|
## Install
|
|
22
36
|
|
|
@@ -24,76 +38,143 @@ like Claude Code, Cline, aider) can use them without an API key.
|
|
|
24
38
|
npm install -g @jaralabs/codex-bridge
|
|
25
39
|
```
|
|
26
40
|
|
|
27
|
-
Requires
|
|
28
|
-
once with `codex login` on a machine with the ChatGPT Business / Team / Plus
|
|
29
|
-
session.
|
|
41
|
+
Requires:
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
* Node.js 20 or newer.
|
|
44
|
+
* The OpenAI Codex CLI installed and logged in **once** with `codex login`
|
|
45
|
+
on a machine with the ChatGPT Business / Team / Plus session. The bridge
|
|
46
|
+
reads `~/.codex/auth.json` for the OAuth tokens. After the first login,
|
|
47
|
+
the CLI is no longer required at runtime.
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```json
|
|
36
|
-
{
|
|
37
|
-
"provider": {
|
|
38
|
-
"chatgpt": {
|
|
39
|
-
"npm": "@ai-sdk/openai-compatible",
|
|
40
|
-
"name": "ChatGPT Business (codex-bridge)",
|
|
41
|
-
"options": {
|
|
42
|
-
"baseURL": "http://127.0.0.1:11434/v1"
|
|
43
|
-
},
|
|
44
|
-
"models": {
|
|
45
|
-
"gpt-6.1-sol": {
|
|
46
|
-
"name": "GPT-6.1 Sol (Business)",
|
|
47
|
-
"limit": { "context": 272000, "output": 128000 },
|
|
48
|
-
"modalities": { "input": ["text", "image"], "output": ["text"] }
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
49
|
+
## Wire it into OpenCode
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
block to match. Re-run `codex-bridge start` after any plan change.
|
|
51
|
+
There are two pieces: the bridge running on `127.0.0.1:11434`, and the
|
|
52
|
+
OpenCode plugin that registers the `chatgpt` provider plus the default MCP
|
|
53
|
+
servers.
|
|
60
54
|
|
|
61
|
-
|
|
55
|
+
### 1. Run the bridge
|
|
62
56
|
|
|
63
57
|
```bash
|
|
64
58
|
codex-bridge start # sync models + start HTTP server in background
|
|
65
59
|
codex-bridge status # is it up?
|
|
66
|
-
codex-bridge sync # only sync model list (no server)
|
|
67
60
|
codex-bridge logs # tail the server log
|
|
68
61
|
codex-bridge stop # kill the server
|
|
69
62
|
```
|
|
70
63
|
|
|
71
|
-
|
|
64
|
+
Other useful commands:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
codex-bridge sync # only sync the model list (no server)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Install the OpenCode plugin
|
|
71
|
+
|
|
72
|
+
Copy or symlink `lib/plugin.ts` to `~/.config/opencode/plugins/codex-bridge.ts`:
|
|
72
73
|
|
|
73
74
|
```bash
|
|
74
|
-
|
|
75
|
+
mkdir -p ~/.config/opencode/plugins
|
|
76
|
+
ln -s "$(npm root -g)/@jaralabs/codex-bridge/lib/plugin.ts" \
|
|
77
|
+
~/.config/opencode/plugins/codex-bridge.ts
|
|
75
78
|
```
|
|
76
79
|
|
|
80
|
+
The plugin:
|
|
81
|
+
|
|
82
|
+
* Registers the `chatgpt` provider pointing at `http://127.0.0.1:11434/v1`.
|
|
83
|
+
* Adds the live model list from `~/.codex/models_cache.json`.
|
|
84
|
+
* Registers the default MCP servers (Doop, codegraph, codegraph-fe, angecom).
|
|
85
|
+
Disable per-instance with `CODEX_BRIDGE_DISABLE_<NAME>=1`.
|
|
86
|
+
* Seeds a V2 permission policy: read-only tools are `allow`, anything that
|
|
87
|
+
mutates state is `ask`, mirroring the ChatGPT Plus UX.
|
|
88
|
+
|
|
89
|
+
Restart OpenCode after installing the plugin so it picks it up.
|
|
90
|
+
|
|
91
|
+
### 3. Pick a model
|
|
92
|
+
|
|
93
|
+
In OpenCode, choose one of the `chatgpt/*` models from the picker and chat
|
|
94
|
+
normally. Tool calls (Doop, codegraph, etc.) round-trip through the bridge
|
|
95
|
+
and trigger the OpenCode approval prompt when the policy says `ask`.
|
|
96
|
+
|
|
97
|
+
## Endpoints
|
|
98
|
+
|
|
99
|
+
| Method | Path | Purpose |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `GET` | `/v1/models` | List the ChatGPT Plus models available in your plan |
|
|
102
|
+
| `POST` | `/v1/chat/completions` | OpenAI-style chat completions (streaming or JSON) |
|
|
103
|
+
| `GET` | `/v1/tools` | List registered MCP tools the bridge will advertise to the model |
|
|
104
|
+
| `POST` | `/v1/tools` | Register a list of MCP tools (used by the OpenCode plugin on session start) |
|
|
105
|
+
| `DELETE` | `/v1/tools` | Clear the registry |
|
|
106
|
+
| `GET` | `/v1/health` | Resilience metrics: requests, successes, failures, retries, circuit state |
|
|
107
|
+
| `GET` | `/healthz` | Liveness probe (always returns `{ ok: true }`) |
|
|
108
|
+
|
|
109
|
+
## Tool-call flow
|
|
110
|
+
|
|
111
|
+
1. OpenCode sends `POST /v1/chat/completions` with `messages` and optionally
|
|
112
|
+
`tools=[]`.
|
|
113
|
+
2. The bridge translates Chat Completions into the OpenAI Responses API wire
|
|
114
|
+
format. If `tools` is missing, the registered MCP tools are rendered into
|
|
115
|
+
a Plus-style system block describing what the model can call.
|
|
116
|
+
3. The bridge POSTs to `chatgpt.com/backend-api/codex/responses` with the
|
|
117
|
+
current access token. The response is an SSE stream of
|
|
118
|
+
`response.output_text.delta` / `response.function_call_arguments.delta` /
|
|
119
|
+
`response.completed` events.
|
|
120
|
+
4. The bridge streams OpenAI-style chunks back: `delta.content` for text and
|
|
121
|
+
`delta.tool_calls[]` for tool calls. Tool calls carry an OpenAI-shaped
|
|
122
|
+
`tool_call_id` so the AI SDK can match them on the next round-trip.
|
|
123
|
+
5. OpenCode runs the tool against the real MCP server and sends a follow-up
|
|
124
|
+
request with `role: "tool"` messages. The bridge translates those into
|
|
125
|
+
`function_call_output` items and re-issues.
|
|
126
|
+
6. Loop until the model returns no more tool calls.
|
|
127
|
+
|
|
128
|
+
## Tests
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
npm test # smoke (server up, /healthz, /v1/models, chat)
|
|
132
|
+
npm run test:tools # tool transport, registry, multi-turn loop
|
|
133
|
+
npm run test:resilience # retry, circuit breaker, metrics (pure unit)
|
|
134
|
+
npm run test:smoke # live integration test against the real ChatGPT API
|
|
135
|
+
# (requires ~/.codex/auth.json on the machine)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The `test:smoke` script is what the GitHub Actions daily cron runs to catch
|
|
139
|
+
wire-shape drift the day OpenAI changes something. Set
|
|
140
|
+
`CODEX_BRIDGE_SKIP_LIVE=1` to skip the network call in air-gapped CI.
|
|
141
|
+
|
|
142
|
+
## CI
|
|
143
|
+
|
|
144
|
+
The GitHub Actions workflow at `.github/workflows/publish.yml` has three
|
|
145
|
+
jobs:
|
|
146
|
+
|
|
147
|
+
* **test**: resilience unit tests, type-check the OpenCode plugin, and
|
|
148
|
+
`node --check` every ESM source file.
|
|
149
|
+
* **smoke**: writes `CODEX_AUTH_JSON` to `~/.codex/auth.json` and runs
|
|
150
|
+
`test:smoke.mjs` against the real ChatGPT API. Runs on push to main and
|
|
151
|
+
on a daily cron. Skipped silently when the secret is missing, so a
|
|
152
|
+
private repo without the secret can still build.
|
|
153
|
+
* **publish**: on push to main, bumps the package to npm via **OpenID
|
|
154
|
+
Connect Trusted Publishing** (no NPM_TOKEN secret). The trusted publisher
|
|
155
|
+
is configured on the package itself
|
|
156
|
+
(`https://www.npmjs.com/package/@jaralabs/codex-bridge` → Settings →
|
|
157
|
+
Trusted Publishers → `jaralabs/codex-bridge` / `publish.yml`).
|
|
158
|
+
|
|
159
|
+
The publish step only uploads a new version when `package.json`'s version
|
|
160
|
+
differs from the latest on npm, so doc and test commits on main are no-ops.
|
|
161
|
+
|
|
77
162
|
## How it works
|
|
78
163
|
|
|
79
164
|
```
|
|
80
165
|
┌──────────┐ HTTP (OpenAI-compatible) ┌────────────────┐
|
|
81
166
|
│ OpenCode │ ──────────────────────────────────────▶ │ codex-bridge │
|
|
82
|
-
│
|
|
167
|
+
│ + MCP │ ◀────────────────────────────────────── │ 127.0.0.1:11434│
|
|
83
168
|
└──────────┘ └────────────────┘
|
|
84
169
|
│
|
|
85
|
-
|
|
170
|
+
HTTPS + OAuth ▼
|
|
86
171
|
┌─────────────────┐
|
|
87
|
-
│
|
|
88
|
-
│
|
|
89
|
-
│
|
|
172
|
+
│ chatgpt.com/ │
|
|
173
|
+
│ backend-api/ │
|
|
174
|
+
│ codex/responses │
|
|
90
175
|
└─────────────────┘
|
|
91
176
|
```
|
|
92
177
|
|
|
93
|
-
The bridge has zero state of its own. It just maps an OpenAI-style HTTP
|
|
94
|
-
request into a Codex CLI invocation and pipes the result back. Codex handles
|
|
95
|
-
auth, plan routing, and rate limits.
|
|
96
|
-
|
|
97
178
|
## License
|
|
98
179
|
|
|
99
180
|
MIT.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaralabs/codex-bridge",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenAI-compatible HTTP bridge that reuses a local ChatGPT Business / Team / Plus session via codex, so OpenCode can list and call those models without paying for the OpenAI API.",
|
|
6
6
|
"type": "module",
|