@jaralabs/codex-bridge 0.4.0 → 0.4.1

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.
Files changed (2) hide show
  1. package/README.md +135 -54
  2. 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 of paying for the OpenAI API.
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 [OpenAI Codex CLI][codex]. Because
7
- the Codex CLI reuses the local ChatGPT OAuth session in `~/.codex/auth.json`,
8
- your OpenCode prompts consume your subscription credits — not the OpenAI API
9
- balance.
10
-
11
- [codex]: https://github.com/openai/codex
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 / web
17
- App today with the subscription. `codex-bridge` exposes the same models over
18
- an OpenAI-compatible HTTP surface so OpenCode (and any other OpenAI client
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 Node 18 or newer and the OpenAI Codex CLI installed and logged in
28
- once with `codex login` on a machine with the ChatGPT Business / Team / Plus
29
- session.
41
+ Requires:
30
42
 
31
- ## Wire it into OpenCode
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
- Add a `chatgpt` provider to `~/.config/opencode/opencode.jsonc`:
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
- The exact model list depends on what your plan exposes. `codex-bridge start`
57
- syncs the model list from `~/.codex/models_cache.json` (refreshed by Codex
58
- whenever it talks to ChatGPT) and rewrites the `provider.chatgpt.models`
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
- ## Usage
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
- In another terminal:
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
- opencode run --model=chatgpt/gpt-6.1-sol "say hi"
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
- │ │ ◀────────────────────────────────────── │ 127.0.0.1:11434│
167
+ │ + MCP │ ◀────────────────────────────────────── │ 127.0.0.1:11434│
83
168
  └──────────┘ └────────────────┘
84
169
  │
85
- spawns ▼
170
+ HTTPS + OAuth ▼
86
171
  ┌─────────────────┐
87
- │ codex exec │
88
- │ (uses ChatGPT │
89
- │ OAuth session) │
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.0",
3
+ "version": "0.4.1",
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",