codex-openai-proxy 0.1.0-rc.0 → 0.1.0-rc.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 CHANGED
@@ -1,42 +1,46 @@
1
1
  # codex-openai-proxy
2
2
 
3
- `codex-openai-proxy` lets software written for the OpenAI Chat Completions API talk to Codex through a local HTTP endpoint. It runs `codex app-server`, uses your ChatGPT login, and keeps the listener on your machine.
3
+ Use any OpenAI Chat Completions client with Codex. The proxy runs `codex app-server` locally, authenticates with your ChatGPT login, and serves a loopback-only OpenAI-compatible endpoint.
4
4
 
5
- The package is in prerelease. Text completions, streaming, function tools, usage metadata, thread continuation, and per-request Codex policy selection are implemented.
5
+ > Prerelease. Text completions, streaming, function tools, usage metadata, thread continuation, and per-request Codex policy selection are implemented.
6
6
 
7
7
  ## Quick start
8
8
 
9
- Install Node.js 20 or newer, then start the prerelease from the narrowest directory tree Codex should be allowed to use:
9
+ Requires Node.js 20+.
10
10
 
11
11
  ```sh
12
12
  npx --yes codex-openai-proxy@next serve --root /absolute/path/to/project
13
13
  ```
14
14
 
15
- The proxy listens at `http://127.0.0.1:8787` by default. On first use it starts the ChatGPT login flow. Interactive terminals open the authorization page when possible; non-interactive terminals use device-code login.
15
+ - `--root` is the narrowest directory tree Codex may work in (defaults to the launch directory).
16
+ - The proxy listens at `http://127.0.0.1:8787` and starts the ChatGPT login flow on first use.
17
+ - Or install globally: `npm install --global codex-openai-proxy@next`, then `codex-openai-proxy serve --root ...`.
18
+ - Run `codex-openai-proxy --help` for all server, timeout, logging, and state options.
16
19
 
17
- Check that the HTTP process is alive and that Codex is ready:
20
+ Check status:
18
21
 
19
- ```sh
20
- curl http://127.0.0.1:8787/health
21
- curl http://127.0.0.1:8787/ready
22
- ```
22
+ | Endpoint | Meaning |
23
+ | ------------- | ---------------------------------------------------------------------------------------------- |
24
+ | `GET /health` | 200 while the proxy process is alive |
25
+ | `GET /ready` | 200 once Codex is initialized and authenticated; 503 while starting, logging in, or recovering |
23
26
 
24
- `/health` reports proxy liveness. `/ready` returns HTTP 503 until app-server initialization and authentication finish, and while the proxy is recovering from an app-server failure.
27
+ ## Authentication
25
28
 
26
- To install the command instead of using `npx`:
29
+ The proxy uses Codex's ChatGPT account session — no API key is exchanged. If Codex already has a usable account, startup reuses it silently. Otherwise:
27
30
 
28
- ```sh
29
- npm install --global codex-openai-proxy@next
30
- codex-openai-proxy serve --root /absolute/path/to/project
31
- ```
31
+ - **Interactive terminal** — the proxy opens the browser authorization page. Complete the login and leave `serve` running. If the browser can't be launched, the URL is printed to the terminal.
32
+ - **Non-interactive (containers, services, CI)** — the proxy prints a verification URL and one-time device code to stderr. Keep stderr visible until login completes.
32
33
 
33
- After a stable release, omit `@next` from the `npx` and install commands. Installing the package does not start Codex, perform login, or run a proxy install script; those actions begin only when you run `serve`.
34
+ Notes:
34
35
 
35
- The package includes the exact `@openai/codex` version used to generate its app-server contract: `0.144.5`. The proxy uses that package-owned executable by default. An explicit `--codex-path` override must report exactly `codex-cli 0.144.5`; both older and newer overrides are rejected until this package's generated contract is updated.
36
+ - Completions return `app_server_not_ready` and `/ready` returns 503 until login finishes.
37
+ - The default login deadline is 5 minutes; raise it with e.g. `--tool-timeout 10m`.
38
+ - Treat printed authorization URLs and device codes as credentials — don't paste them into issues or logs.
39
+ - Uninstalling the proxy or deleting its state does **not** log out your Codex account; Codex owns the session.
36
40
 
37
41
  ## Use an OpenAI client
38
42
 
39
- Point an OpenAI-compatible client at the local `/v1` base URL. The proxy does not require a local API key, although some client libraries require a non-empty placeholder value.
43
+ Point any OpenAI-compatible client at `http://127.0.0.1:8787/v1`. No API key is required; use any placeholder if your library demands one.
40
44
 
41
45
  ```js
42
46
  import OpenAI from "openai";
@@ -54,7 +58,7 @@ const completion = await client.chat.completions.create({
54
58
  console.log(completion.choices[0].message.content);
55
59
  ```
56
60
 
57
- The equivalent request with `curl` is:
61
+ Or with `curl`:
58
62
 
59
63
  ```sh
60
64
  curl http://127.0.0.1:8787/v1/chat/completions \
@@ -65,61 +69,55 @@ curl http://127.0.0.1:8787/v1/chat/completions \
65
69
  }'
66
70
  ```
67
71
 
68
- ## OpenAI-compatible behavior
69
-
70
- The proxy intentionally implements a focused subset of Chat Completions rather than every OpenAI API endpoint.
72
+ ## What's supported
71
73
 
72
- Standard behavior includes:
74
+ | Supported | Not supported |
75
+ | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
76
+ | `POST /v1/chat/completions` with text-only messages (`system`, `developer`, `user`, `assistant`, `tool`) | Multimodal message content (images, audio) |
77
+ | Streaming (SSE, ends with `data: [DONE]`) and non-streaming | Responses API, embeddings, images, audio, model management |
78
+ | `reasoning_effort` (`none` … `max`, forwarded to Codex) | `tool_choice` other than `"auto"` / `"none"` |
79
+ | Client-defined function tools, `tool_calls`, `finish_reason: "tool_calls"` | More than one choice per response |
80
+ | `stream_options.include_usage` | Remote (non-loopback) serving |
81
+ | OpenAI-shaped JSON errors | |
73
82
 
74
- - `POST /v1/chat/completions` with text-only `system`, `developer`, `user`, `assistant`, and `tool` messages;
75
- - streaming and non-streaming responses;
76
- - Chat Completions SSE framing ending with `data: [DONE]`;
77
- - assistant text in `choices[].message.content` or `choices[].delta.content`;
78
- - client-defined function tools, `tool_calls`, and `finish_reason: "tool_calls"`;
79
- - `stream_options.include_usage`; and
80
- - OpenAI-shaped JSON errors.
81
-
82
- The proxy ignores harmless unsupported top-level Chat Completions fields and writes one structured warning for the request. It rejects malformed, ambiguous, or unsafe input instead of approximating it.
83
-
84
- This is not a general OpenAI API server. It does not provide the Responses API, embeddings, images, audio, model management, or remote network serving. Message content is currently text-only, and `tool_choice` currently supports only `"auto"` and `"none"`.
83
+ Harmless unsupported fields are ignored with one structured warning. Malformed or ambiguous input is rejected rather than approximated.
85
84
 
86
85
  ## Streaming
87
86
 
88
- Set `stream: true` as with a standard Chat Completions request:
87
+ Set `stream: true` as usual:
89
88
 
90
89
  ```sh
91
90
  curl -N http://127.0.0.1:8787/v1/chat/completions \
92
91
  -H 'Content-Type: application/json' \
93
92
  -d '{
94
93
  "model": "gpt-5.4-mini",
94
+ "reasoning_effort": "high",
95
95
  "messages": [{"role": "user", "content": "Describe this repository."}],
96
96
  "stream": true,
97
97
  "stream_options": {"include_usage": true}
98
98
  }'
99
99
  ```
100
100
 
101
- Standard clients can consume assistant text, client-defined function calls, the final finish reason, and the optional usage chunk without understanding Codex-specific data. Internal activity requires clients that tolerate the direct compatibility fields described below.
101
+ Standard clients get assistant text, function calls, the finish reason, and the optional usage chunk. Codex reasoning and internal activity arrive in the nonstandard fields described under [Codex-specific extensions](#codex-specific-extensions).
102
102
 
103
103
  ## Function tools
104
104
 
105
- Function tools use the normal multi-request Chat Completions flow:
105
+ Function tools follow the normal multi-request Chat Completions flow:
106
106
 
107
- 1. Send the function definitions in `tools`.
108
- 2. Receive an assistant response containing `tool_calls`.
109
- 3. Execute the requested functions in your client.
110
- 4. Send the assistant tool-call message followed by matching `role: "tool"` messages, repeating the same `tools` definition and the same `x_codex` policy settings from the original request.
107
+ 1. Send your function definitions in `tools`.
108
+ 2. Receive an assistant response with `tool_calls`.
109
+ 3. Execute the functions in your client.
110
+ 4. Send the assistant tool-call message plus matching `role: "tool"` messages repeating the same `tools`, `reasoning_effort`, and `x_codex` settings as the original request.
111
111
 
112
- By default, the proxy associates tool results with the one pending Codex turn through their `tool_call_id` values. Start it with `--implicit-tool-continuation false` if every tool-result request should instead supply the Codex continuation field described below. Either way, a tool-result request whose effective `x_codex` settings differ from the original request is rejected with `continuation_policy_mismatch` and leaves the pending call intact for a corrected retry. Omitted settings are allowed only when they resolve to the same effective values.
113
-
114
- Pending tool calls are held in memory for five minutes by default. They cannot survive a proxy restart; completed threads can.
112
+ Changing those settings between the call and its results is rejected (`continuation_reasoning_effort_mismatch` / `continuation_policy_mismatch`); the pending call stays intact so you can retry corrected. Pending tool calls are held in memory for 5 minutes, with the deadline restarted whenever an incoming request selects the pending response by `previous_response_id` or matching tool-call IDs. They do not survive a proxy restart.
115
113
 
116
114
  ## Codex-specific extensions
117
115
 
118
- Codex-specific behavior is additive but nonstandard. Clients that require strict Chat Completions response objects must ignore or strip the response extensions described here.
116
+ These are additive but nonstandard. Strict Chat Completions clients should ignore or strip them.
119
117
 
120
118
  ### Continue a Codex thread
121
119
 
122
- `previous_response_id` is a top-level, nonstandard `x_codex` continuation extension; it is not a standard Chat Completions field and is not nested inside the `x_codex` object. Pass the `id` from the newest completed response to continue its persisted Codex thread:
120
+ Pass the `id` of the newest completed response as top-level `previous_response_id` to continue its persisted Codex thread:
123
121
 
124
122
  ```json
125
123
  {
@@ -129,19 +127,27 @@ Codex-specific behavior is additive but nonstandard. Clients that require strict
129
127
  }
130
128
  ```
131
129
 
132
- A continuation must use the same model and function-tool definitions as the original thread. The proxy rejects unknown, expired, superseded, busy, or incompatible response IDs and never silently starts a replacement thread. Only the newest response can be continued; branching from an older response is not supported.
130
+ - Send only the new user message the persisted thread already has the earlier turns.
131
+ - A continuation must use the same model, `reasoning_effort`, tools, and `x_codex` settings as the original.
132
+ - Only the newest response can be continued; unknown, expired, superseded, or busy IDs are rejected — the proxy never silently starts a new thread.
133
+ - Completed threads survive a proxy restart.
133
134
 
134
135
  ### Receive Codex activity
135
136
 
136
- The proxy sends Codex activity directly on the assistant delta/message. Text uses the standard `content` shape and calls use the standard `tool_calls` shape. Exposed reasoning uses the nonstandard `reasoning` field (never `reasoning_summary`) and results use the nonstandard `tool_results` field. These are Codex-specific direct compatibility extensions, not standard Chat Completions fields and not fields inside a response-side `x_codex` object. Streaming order is SSE chunk order; non-streaming responses aggregate those fields while preserving text that appeared before a call.
137
+ Responses can include two nonstandard fields on the assistant delta/message:
138
+
139
+ | Field | Contents |
140
+ | -------------- | --------------------------------------------------------------------------------------------- |
141
+ | `reasoning` | Codex's reasoning summary (string) |
142
+ | `tool_results` | Status/results of Codex's internal activity (commands, file changes, MCP calls, web searches) |
137
143
 
138
- Internal app-server commands, file changes, MCP calls, web searches, collaboration calls, and other supported tool-like items are represented as function-shaped calls. The first event announces a call in `tool_calls`. Later progress and terminal events place bounded status/content plus the matching function metadata in nonstandard `tool_results` without repeating complete call arguments; an orphan result also introduces its reconstructable call. These calls are observational and are already executed by app-server; clients must not execute them. They do not cause `finish_reason: "tool_calls"`.
144
+ Internal activity also appears as function-shaped entries in `tool_calls`. These are **observational** Codex already executed them. Do not execute them, and do not send tool results for them; they never cause `finish_reason: "tool_calls"`. Only your own client-defined functions suspend the turn and require `role: "tool"` follow-ups.
139
145
 
140
- Client-defined dynamic functions still suspend with `finish_reason: "tool_calls"` and require the normal follow-up `role: "tool"` messages. The continuation response begins with the accepted calls and nonstandard `tool_results` together, then streams later nonstandard reasoning, standard text, or internal activity from the same turn. The top-level request extension `previous_response_id` and the policy fields nested under `x_codex` remain distinct from these direct response fields.
146
+ If your client replays a prior assistant message verbatim in a fresh request, the proxy strips these observational fields automatically.
141
147
 
142
148
  ### Select Codex policy
143
149
 
144
- Working-directory, sandbox, and web-search controls are nonstandard request extensions nested under `x_codex`:
150
+ Per-request Codex controls live under a nonstandard top-level `x_codex` object:
145
151
 
146
152
  ```json
147
153
  {
@@ -155,69 +161,52 @@ Working-directory, sandbox, and web-search controls are nonstandard request exte
155
161
  }
156
162
  ```
157
163
 
158
- `cwd` must be an existing absolute directory whose resolved path is the configured root or one of its descendants. Symlink escapes, sibling directories, relative paths, files, and nonexistent paths are rejected. Omit it to use the root.
164
+ | Field | Values | Default | Notes |
165
+ | ------------ | ---------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------------- |
166
+ | `cwd` | absolute path | the configured `--root` | Must be the root or a descendant; symlink escapes and relative paths are rejected |
167
+ | `sandbox` | `read-only`, `workspace-write`, `danger-full-access` | `read-only` | Full access is never selected implicitly |
168
+ | `web_search` | `disabled`, `cached`, `indexed`, `live` | `disabled` | Applied per Codex thread |
159
169
 
160
- `sandbox` accepts `read-only`, `workspace-write`, or `danger-full-access` and defaults to `read-only`. Full access is never selected implicitly and cannot bypass managed app-server requirements. `web_search` accepts `disabled`, `cached`, `indexed`, or `live` and defaults to `disabled`. The proxy applies web-search configuration per Codex thread and never edits shared Codex configuration to simulate a request setting.
170
+ The JSON Schema ships with the package at `protocol/schemas/x-codex.schema.json`.
161
171
 
162
- Approval policy is proxy-owned and non-interactive. The proxy prefers `never`, selects `auto_review` when managed policy permits it, and immediately declines any unexpected approval request with the method's supported response. Organization or machine requirements are loaded from app-server and disallowed selections fail instead of falling back.
163
-
164
- Continuations must repeat the same effective `x_codex` settings. A change is rejected with `continuation_cwd_mismatch` or `continuation_policy_mismatch` before the thread is resumed.
165
-
166
- The machine-readable request-extension schema is [protocol/schemas/x-codex.schema.json](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/protocol/schemas/x-codex.schema.json), shipped inside the installed package at `protocol/schemas/x-codex.schema.json`. `previous_response_id` is the separate top-level nonstandard continuation extension. Response `reasoning` and `tool_results` are nonstandard direct compatibility fields rather than standard Chat Completions fields or fields inside a response-side `x_codex` object.
167
-
168
- > **Project trust side effect:** Starting a new thread with `workspace-write` and a `cwd` can cause app-server to mark that project as trusted in the user's `config.toml`. Set `--root` to the narrowest appropriate boundary; the proxy will not cause app-server to trust a directory outside it.
169
-
170
- > **State directory placement:** Under `workspace-write` the Codex agent can write anywhere in the effective `cwd`. The proxy therefore derives its continuation store from the canonical root and keeps it outside that root by default (under `~/.codex-openai-proxy`, namespaced per root). Startup fails if a broad root would contain the default store. Relative `--state-dir` values are resolved against the canonical root. Pointing `--state-dir` back inside the root explicitly lets a writable-sandbox turn modify the store, so keep it outside the root.
171
-
172
- The versioned on-disk shape is documented by [protocol/schemas/response-mapping.schema.json](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/protocol/schemas/response-mapping.schema.json), shipped inside the installed package at `protocol/schemas/response-mapping.schema.json`.
172
+ > **Project trust:** starting a new thread with `workspace-write` and a `cwd` can cause Codex to mark that project as trusted in your `config.toml`. Keep `--root` as narrow as possible.
173
173
 
174
174
  ## Usage metadata
175
175
 
176
- When app-server reports a complete exact last-turn usage record, the proxy returns standard `prompt_tokens`, `completion_tokens`, and `total_tokens`. It also returns cached-input and reasoning-token detail when available. If app-server reports no complete record, the `usage` object is omitted; unavailable optional details are omitted. Malformed partial records fail instead of being estimated.
177
-
178
- ## Safety and local operation
179
-
180
- The listener accepts only `127.0.0.1`, `::1`, or `localhost`; configuration normalizes `localhost` to `127.0.0.1`. Every route requires an exact `Host` authority of `localhost`, `127.0.0.1`, or `[::1]`, optionally followed by a valid port. Missing, malformed, non-loopback, DNS-alias, and rebinding-style authorities are rejected. Any request carrying an `Origin` header is rejected, including health requests. Chat Completions accepts only JSON POST bodies, so browser-simple form submissions fail closed.
181
-
182
- There is no proxy bearer-token check, so any process running as your local user may be able to call it. Continuation state is private to that user where the platform supports POSIX permissions. See the [security model](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/security.md) for the threat model, audit boundary, and debug-logging policy.
176
+ When Codex reports exact usage for the turn, responses include standard `prompt_tokens`, `completion_tokens`, and `total_tokens`, plus cached-input and reasoning-token detail when available. When no complete record exists, `usage` is omitted never estimated.
183
177
 
184
- The launch directory is the default root for Codex work. Set a narrower boundary when needed:
178
+ ## Safety and limits
185
179
 
186
- ```sh
187
- npx --yes codex-openai-proxy@next serve --root /absolute/path/to/project
188
- ```
189
-
190
- The proxy writes structured JSON logs to stderr. Default logs omit working directories and command details. `--log-level debug` is the opt-in diagnostic mode and may reveal the configured root or redacted app-server diagnostic context, so capture it carefully. Run `npx --yes codex-openai-proxy@next --help` for all server, timeout, capacity, logging, state, and Codex executable options.
191
-
192
- ## Limits and recovery
180
+ - The listener accepts loopback only (`127.0.0.1`, `::1`, `localhost`); non-loopback `Host` authorities and any request with an `Origin` header are rejected.
181
+ - There is no local bearer-token check, so any process running as your user can call the proxy. See the [security model](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/security.md).
182
+ - Structured JSON logs go to stderr. Default logs omit paths and command details; `--log-level debug` may reveal them, so review debug output before sharing.
193
183
 
194
- The safe defaults are a 1 MiB JSON body, 100 concurrent HTTP requests, a 30-second request deadline, a five-minute suspended dynamic-tool deadline, and a 10-second graceful-shutdown deadline. Each is configurable through the CLI. A full HTTP request pool rejects new work with HTTP 429 `overloaded`; requests never enter an unbounded queue. A second request for an active Codex thread is rejected with HTTP 409 `thread_busy`.
184
+ Default limits (all configurable via CLI flags):
195
185
 
196
- If app-server exits unexpectedly, `/ready` returns 503 while the proxy retries after bounded 1, 3, 5, and 10 second delays. Completed continuation records can survive a restart. Suspended client-defined tool calls cannot: shutdown or transport replacement fails their pending app-server requests and expires their mappings.
186
+ | Limit | Default |
187
+ | ------------------------------------------- | ------------------------------------------- |
188
+ | JSON body size | 1 MiB |
189
+ | Concurrent HTTP requests | 100 (excess rejected with 429 `overloaded`) |
190
+ | Request deadline | 30 s |
191
+ | Suspended tool-call deadline | 5 min |
192
+ | Login / startup deadline (`--tool-timeout`) | 5 min |
197
193
 
198
- ## Known incompatibilities
199
-
200
- This proxy implements only the focused Chat Completions subset described above. In particular:
201
-
202
- - content is text-only; multimodal message parts are unsupported;
203
- - only one choice is produced, and harmless unsupported sampling/output fields are ignored with one warning;
204
- - `tool_choice` supports only `auto` and `none`;
205
- - response `reasoning` and `tool_results` require clients that tolerate nonstandard fields;
206
- - continuation is linear and bound to the original model, tools, canonical cwd, and effective policy; and
207
- - no endpoint other than health, readiness, and `POST /v1/chat/completions` is implemented.
194
+ A second request for an active Codex thread returns 409 `thread_busy`. If app-server crashes, the proxy retries with bounded backoff while `/ready` returns 503.
195
+ The request deadline aborts downstream work and closes any response that is still open, including a stream blocked by a client that stopped reading; its concurrency slot is then released.
208
196
 
209
197
  ## Troubleshooting
210
198
 
211
- - If `/ready` returns 503, inspect the structured terminal logs for authentication, managed-policy, version, or bounded-restart failures.
212
- - If startup reports an address conflict, choose another loopback `--port` or stop the process already using it.
213
- - If the package-owned Codex executable is missing, reinstall the package for the current platform. If an override is rejected, remove `--codex-path` or supply an executable whose `--version` output reports exactly `codex-cli 0.144.5`.
214
- - If login fails or times out, keep the terminal open, retry `serve`, and follow the browser or device-code prompt. Inspect only redacted structured logs before enabling debug output.
215
- - If a policy request is denied, choose a value permitted by the loaded managed requirements; the proxy will not silently weaken or substitute policy.
216
- - Use `--log-level debug` only for temporary diagnosis. Debug output is a sensitive-data opt-in and should not be attached to issues without review.
199
+ | Symptom | What to do |
200
+ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
201
+ | `/ready` returns 503 | Login or startup hasn't finished follow [Authentication](#authentication) and check the stderr logs for `app_server_ready` or `startup_failed` |
202
+ | Browser login never appears | Non-terminal stderr selects device-code login; run `serve` in a foreground terminal without redirecting stderr |
203
+ | Address already in use | Choose another loopback `--port` |
204
+ | `--codex-path` override rejected | The override must report exactly `codex-cli 0.144.5` (the version bundled with this package); remove the flag to use the bundled executable |
205
+ | Policy request denied | Managed requirements disallow the value; the proxy never silently weakens policy |
217
206
 
218
- ## Uninstall and state cleanup
207
+ For deeper diagnosis, temporarily add `--log-level debug` — but treat its output as sensitive.
219
208
 
220
- An `npx` invocation does not create a global package installation. Remove an explicit global or project installation with:
209
+ ## Uninstall and cleanup
221
210
 
222
211
  ```sh
223
212
  npm uninstall --global codex-openai-proxy
@@ -225,29 +214,12 @@ npm uninstall --global codex-openai-proxy
225
214
  npm uninstall codex-openai-proxy
226
215
  ```
227
216
 
228
- Uninstalling does not delete continuation mappings. By default they live under `~/.codex-openai-proxy`, with one directory per canonical `--root`. Stop every proxy using the target root before cleanup. Delete only that root's namespace if other roots must remain, or delete `~/.codex-openai-proxy` to remove all default proxy continuation state. If you supplied `--state-dir`, clean that directory instead. Removing a namespace permanently invalidates its `previous_response_id` values but does not remove Codex's own threads or ChatGPT login.
229
-
230
- The namespace name is the first 16 hexadecimal characters of the SHA-256 digest of the canonical absolute root. This command prints it without deleting anything:
231
-
232
- ```sh
233
- node -e 'const c=require("node:crypto"),f=require("node:fs");const p=f.realpathSync(process.argv[1]);console.log(c.createHash("sha256").update(p).digest("hex").slice(0,16))' /absolute/path/to/project
234
- ```
235
-
236
- Inspect `~/.codex-openai-proxy/<printed-namespace>` before deleting it.
237
-
238
- ## Verification modes
239
-
240
- `npm ci && npm run check` is the deterministic offline gate. It regenerates the pinned protocol in a temporary tree for comparison, type-checks both Vitest configurations, and runs bounded property and compatibility tests. The required CI definition runs that gate on Node.js 20, 22, 24, and 26 on Linux and Node.js 24 on macOS and Windows. Coverage and its floors run only on the primary Linux Node.js 24 job and remain enabled by default for local `npm test`.
241
-
242
- `npm run test:package` builds one tarball, seeds an isolated npm cache from the exact Codex packages already installed by `npm ci`, installs that exact proxy tarball in npm offline mode with lifecycle scripts disabled, and invokes the generated npm bin shim against a local fake app-server. The smoke performs no registry request, login, or live model call; its only HTTP traffic is loopback test traffic. The default command cleans up the tarball. Release automation uses `npm run test:package -- --retain` and publishes the retained, tested file rather than rebuilding from the source directory.
243
-
244
- `npm run test:package -- --registry-install` is the separate networked packaging check. It uses an isolated empty cache to fetch the exact runtime and current-platform Codex packages, then runs the same tarball/bin-shim smoke. A dispatch-only three-operating-system workflow keeps this registry availability check outside required offline CI.
245
-
246
- `npm run test:live` is a separate opt-in compatibility check using the dedicated live configuration. It is serial, uses only `gpt-5.4-mini`, normally makes five model calls, and has a hard maximum of six. The manual online workflow additionally requires explicit environment authorization and a nonempty `CODEX_ACCESS_TOKEN`; headless execution never prints device-code URLs or codes. It is never a pull-request prerequisite.
217
+ - Continuation state lives under `~/.codex-openai-proxy` (one namespace per `--root`), or your custom `--state-dir`. Uninstalling does not delete it.
218
+ - Stop every proxy using a root before deleting its namespace. Deleting state invalidates its `previous_response_id` values but does not touch Codex's own threads or your ChatGPT login.
247
219
 
248
- ## Project documentation
220
+ ## Documentation
249
221
 
250
- - [Repository guide](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/development.md) explains the source layout, development commands, tests, and generated protocol files.
251
- - [Security model](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/security.md) records the local threat model and audit decisions.
252
- - [Implementation plan](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/plans/README.md) tracks product decisions, completed stages, and remaining work.
253
- - [App-server protocol reference](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/codex-app-server.md) is the checked-in upstream protocol reference used during implementation.
222
+ - [Development guide](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/development.md) source layout, commands, tests, and verification modes
223
+ - [Security model](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/security.md) threat model and audit boundary
224
+ - [Implementation plan](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/plans/README.md) decisions and remaining work
225
+ - [App-server protocol reference](https://github.com/CIVITAS-John/codex-app-server-to-proxy/blob/main/docs/codex-app-server.md)
@@ -3,7 +3,11 @@ import { once } from "node:events";
3
3
  import { listenForAbort, withDeadline } from "../core/abort.js";
4
4
  /** Ensures app-server has an authenticated OpenAI account. */
5
5
  export async function ensureAuthenticated(options) {
6
- const account = (await options.rpc.request("account/read", { refreshToken: false }, options.signal));
6
+ // Bound the probe so a stalled app-server fails startup instead of hanging it.
7
+ const account = (await withDeadline(options.signal, {
8
+ milliseconds: options.timeoutMs,
9
+ timeoutReason: new Error("account/read timed out."),
10
+ }, async (deadlineSignal) => await options.rpc.request("account/read", { refreshToken: false }, deadlineSignal)));
7
11
  if (typeof account.requiresOpenaiAuth !== "boolean")
8
12
  throw new Error("account/read returned an invalid requiresOpenaiAuth value.");
9
13
  if (!account.requiresOpenaiAuth || account.account != null)
@@ -48,8 +52,12 @@ async function startAndWaitForLogin(options, useDeviceCode) {
48
52
  const disposeDeadline = listenForAbort(deadlineSignal, (abortedSignal) => settle(abortedSignal.reason instanceof Error
49
53
  ? abortedSignal.reason
50
54
  : new Error("ChatGPT login cancelled.")));
55
+ // A transport that dies mid-login must settle the wait immediately
56
+ // rather than reporting a misleading timeout after the full deadline.
57
+ const onTransportClose = (reason) => settle(reason);
58
+ options.rpc.once("close", onTransportClose);
51
59
  try {
52
- const login = (await options.rpc.request("account/login/start", { type: useDeviceCode ? "chatgptDeviceCode" : "chatgpt" }, options.signal));
60
+ const login = (await options.rpc.request("account/login/start", { type: useDeviceCode ? "chatgptDeviceCode" : "chatgpt" }, deadlineSignal));
53
61
  loginId = login.loginId;
54
62
  if (earlyCompletion !== undefined)
55
63
  notification("account/login/completed", earlyCompletion);
@@ -74,6 +82,7 @@ async function startAndWaitForLogin(options, useDeviceCode) {
74
82
  await completion;
75
83
  }
76
84
  finally {
85
+ options.rpc.off("close", onTransportClose);
77
86
  disposeDeadline();
78
87
  }
79
88
  });
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/app-server/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA+BhE,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAA8B;IAE9B,MAAM,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CACxC,cAAc,EACd,EAAE,YAAY,EAAE,KAAK,EAAE,EACvB,OAAO,CAAC,MAAM,CACf,CAAoB,CAAC;IACtB,IAAI,OAAO,OAAO,CAAC,kBAAkB,KAAK,SAAS;QACjD,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,IAAI,CAAC,OAAO,CAAC,kBAAkB,IAAI,OAAO,CAAC,OAAO,IAAI,IAAI;QAAE,OAAO;IACnE,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;IAC3C,MAAM,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AACrD,CAAC;AAED,4EAA4E;AAC5E,KAAK,UAAU,oBAAoB,CACjC,OAA8B,EAC9B,aAAsB;IAEtB,IAAI,OAA2B,CAAC;IAChC,IAAI,eAA2C,CAAC;IAChD,IAAI,MAAgC,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,MAAM,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IACH,KAAK,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,YAAY,GAAG,CAAC,MAAc,EAAE,GAAY,EAAQ,EAAE;QAC1D,IACE,MAAM,KAAK,yBAAyB;YACpC,OAAO,GAAG,KAAK,QAAQ;YACvB,GAAG,KAAK,IAAI;YAEZ,OAAO;QACT,MAAM,MAAM,GAAG,GAAqB,CAAC;QACrC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,eAAe,GAAG,MAAM,CAAC;YACzB,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO;QACjE,MAAM,CACJ,MAAM,CAAC,OAAO;YACZ,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,uBAAuB,CAAC,CACvD,CAAC;IACJ,CAAC,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,MAAM,YAAY,CAChB,OAAO,CAAC,MAAM,EACd;YACE,YAAY,EAAE,OAAO,CAAC,SAAS;YAC/B,aAAa,EAAE,IAAI,KAAK,CAAC,0BAA0B,CAAC;YACpD,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CACtB,MAAM,CAAC,MAAM,YAAY,KAAK;gBAC5B,CAAC,CAAC,MAAM,CAAC,MAAM;gBACf,CAAC,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC;SAC5C,EACD,KAAK,EAAE,cAAc,EAAE,EAAE;YACvB,MAAM,eAAe,GAAG,cAAc,CACpC,cAAc,EACd,CAAC,aAAa,EAAE,EAAE,CAChB,MAAM,CACJ,aAAa,CAAC,MAAM,YAAY,KAAK;gBACnC,CAAC,CAAC,aAAa,CAAC,MAAM;gBACtB,CAAC,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAC1C,CACJ,CAAC;YACF,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CACtC,qBAAqB,EACrB,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,SAAS,EAAE,EACzD,OAAO,CAAC,MAAM,CACf,CAAkB,CAAC;gBACpB,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;gBACxB,IAAI,eAAe,KAAK,SAAS;oBAC/B,YAAY,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAAC;gBAE3D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC,CACtD,KAAK,CAAC,OAAO,CACd,CAAC;oBACF,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,OAAO,CAAC,QAAQ,CACd,yCAAyC,KAAK,CAAC,OAAO,IAAI,CAC3D,CAAC;wBACF,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE;4BAC3C,SAAS,EAAE,YAAY;yBACxB,CAAC,CAAC;oBACL,CAAC;;wBAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;gBACzD,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,QAAQ,CACd,QAAQ,KAAK,CAAC,eAAe,mBAAmB,KAAK,CAAC,QAAQ,KAAK,CACpE,CAAC;oBACF,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,2BAA2B,EAAE;wBAC/C,gBAAgB,EAAE,YAAY;wBAC9B,SAAS,EAAE,YAAY;qBACxB,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,UAAU,CAAC;YACnB,CAAC;oBAAS,CAAC;gBACT,eAAe,EAAE,CAAC;YACpB,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,OAAO,GACX,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,UAAU,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzC,OAAO,IAAI,KAAK,CAAC,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/app-server/auth.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,10 @@ export class JsonRpcTransport extends EventEmitter {
20
20
  #closed = false;
21
21
  constructor(input, output, maxPending = 256) {
22
22
  super();
23
+ // The shared transport intentionally fans notifications out to every active
24
+ // HTTP request. Those request lifecycles detach their listeners, so a fixed
25
+ // warning threshold would misdiagnose valid configured concurrency as a leak.
26
+ this.setMaxListeners(0);
23
27
  this.#output = output;
24
28
  this.#maxPending = maxPending;
25
29
  const lines = createInterface({ input, crlfDelay: Infinity });
@@ -117,9 +121,15 @@ export class JsonRpcTransport extends EventEmitter {
117
121
  if (!pending)
118
122
  return;
119
123
  this.#pending.delete(message.id);
120
- const error = record(message.error);
121
- if (error && typeof error.code === "number")
124
+ if ("error" in message) {
125
+ const error = record(message.error);
126
+ if (!error || typeof error.code !== "number") {
127
+ this.emit("malformed", line);
128
+ pending.reject(new Error("app-server emitted malformed JSON-RPC error"));
129
+ return;
130
+ }
122
131
  pending.reject(new RpcError(error.code, String(error.message ?? "JSON-RPC error")));
132
+ }
123
133
  else
124
134
  pending.resolve(message.result);
125
135
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"json-rpc.js","sourceRoot":"","sources":["../../src/app-server/json-rpc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-rpc.js","sourceRoot":"","sources":["../../src/app-server/json-rpc.ts"],"names":[],"mappings":"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"}
@@ -4,12 +4,15 @@ export { bindingHash, canonicalJson };
4
4
  /** Context that must remain identical over a Codex thread's lifetime. */
5
5
  export interface ThreadBinding {
6
6
  model: string;
7
+ reasoningEffort?: string;
7
8
  cwd: string;
8
9
  toolsHash: string;
9
10
  policyHash: string;
10
11
  }
11
12
  /** One opaque response-to-thread record persisted by the proxy. */
12
13
  export interface ResponseRecord extends ThreadBinding {
14
+ /** Marks records written after reasoning effort became an exact binding. */
15
+ reasoningEffortBound?: true;
13
16
  responseId: string;
14
17
  threadId: string;
15
18
  state: "ready" | "pending_tool" | "expired" | "superseded" | "corrupt";
@@ -60,6 +63,8 @@ export declare class ContinuationCoordinator {
60
63
  suspend(responseId: string, binding: ThreadBinding, calls: PendingToolCall[]): void;
61
64
  /** Returns the live suspension, distinguishing restart tombstones from unknown IDs. */
62
65
  pending(responseId: string): PendingToolCall[] | undefined;
66
+ /** Restarts the deadline for one live suspension selected by a request. */
67
+ refreshPending(responseId: string): boolean;
63
68
  /** Resolves live pending tool IDs to exactly one suspended response. */
64
69
  findPendingResponse(callIds: readonly string[]): string;
65
70
  /** Answers pending calls in deterministic call order and consumes the suspension. */
@@ -60,6 +60,8 @@ export class ResponseStore {
60
60
  const now = Date.now();
61
61
  const stored = {
62
62
  ...record,
63
+ // Absence identifies ambiguous records written by pre-upgrade releases.
64
+ reasoningEffortBound: true,
63
65
  createdAt: now,
64
66
  expiresAt: now + this.retentionMs,
65
67
  };
@@ -231,6 +233,15 @@ export class ContinuationCoordinator {
231
233
  // A concurrently closed transport has already failed the request.
232
234
  }
233
235
  }
236
+ /** Expires a suspension without letting lifecycle cleanup fail on disk errors. */
237
+ #expireBestEffort(responseId) {
238
+ try {
239
+ this.store.update(responseId, { state: "expired" });
240
+ }
241
+ catch {
242
+ // Restart turns a stale pending-tool record into the same expired tombstone.
243
+ }
244
+ }
234
245
  /** Routes one dynamic-tool callback to the sole owner of its thread. */
235
246
  #routeToolRequest = (request) => {
236
247
  if (request.method !== "item/tool/call")
@@ -322,7 +333,7 @@ export class ContinuationCoordinator {
322
333
  }
323
334
  this.#pending.delete(responseId);
324
335
  this.#busy.delete(threadId);
325
- this.store.update(responseId, { state: "expired" });
336
+ this.#expireBestEffort(responseId);
326
337
  }, this.toolTimeoutMs);
327
338
  timer.unref();
328
339
  this.#pending.set(responseId, { calls, timer });
@@ -331,6 +342,14 @@ export class ContinuationCoordinator {
331
342
  pending(responseId) {
332
343
  return this.#pending.get(responseId)?.calls;
333
344
  }
345
+ /** Restarts the deadline for one live suspension selected by a request. */
346
+ refreshPending(responseId) {
347
+ const entry = this.#pending.get(responseId);
348
+ if (!entry)
349
+ return false;
350
+ entry.timer.refresh();
351
+ return true;
352
+ }
334
353
  /** Resolves live pending tool IDs to exactly one suspended response. */
335
354
  findPendingResponse(callIds) {
336
355
  const requested = new Set(callIds);
@@ -400,7 +419,7 @@ export class ContinuationCoordinator {
400
419
  clearTimeout(entry.timer);
401
420
  for (const call of entry.calls)
402
421
  this.#failRequestReplaced(call.request.id);
403
- this.store.update(responseId, { state: "expired" });
422
+ this.#expireBestEffort(responseId);
404
423
  }
405
424
  this.#pending.clear();
406
425
  }
@@ -415,6 +434,8 @@ function isResponseRecord(value) {
415
434
  "threadId",
416
435
  "state",
417
436
  "model",
437
+ "reasoningEffort",
438
+ "reasoningEffortBound",
418
439
  "cwd",
419
440
  "toolsHash",
420
441
  "policyHash",
@@ -438,6 +459,11 @@ function isResponseRecord(value) {
438
459
  record.threadId.length > 0 &&
439
460
  typeof record.model === "string" &&
440
461
  record.model.length > 0 &&
462
+ (record.reasoningEffort === undefined ||
463
+ (typeof record.reasoningEffort === "string" &&
464
+ record.reasoningEffort.length > 0)) &&
465
+ (record.reasoningEffortBound === undefined ||
466
+ record.reasoningEffortBound === true) &&
441
467
  typeof record.cwd === "string" &&
442
468
  record.cwd.length > 0 &&
443
469
  validHash(record.toolsHash) &&