codex-openai-proxy 0.1.0-rc.0
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/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/app-server/app-server.d.ts +39 -0
- package/dist/app-server/app-server.js +261 -0
- package/dist/app-server/app-server.js.map +1 -0
- package/dist/app-server/auth.d.ts +16 -0
- package/dist/app-server/auth.js +102 -0
- package/dist/app-server/auth.js.map +1 -0
- package/dist/app-server/json-rpc.d.ts +29 -0
- package/dist/app-server/json-rpc.js +141 -0
- package/dist/app-server/json-rpc.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/cli.d.ts +6 -0
- package/dist/cli/cli.js +319 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/continuation/state.d.ts +71 -0
- package/dist/continuation/state.js +460 -0
- package/dist/continuation/state.js.map +1 -0
- package/dist/core/abort.d.ts +13 -0
- package/dist/core/abort.js +74 -0
- package/dist/core/abort.js.map +1 -0
- package/dist/core/canonical.d.ts +6 -0
- package/dist/core/canonical.js +22 -0
- package/dist/core/canonical.js.map +1 -0
- package/dist/core/config.d.ts +33 -0
- package/dist/core/config.js +139 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/logger.d.ts +17 -0
- package/dist/core/logger.js +44 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/policy.d.ts +98 -0
- package/dist/core/policy.js +242 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/redact.d.ts +2 -0
- package/dist/core/redact.js +35 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/http/chat-execute.d.ts +24 -0
- package/dist/http/chat-execute.js +491 -0
- package/dist/http/chat-execute.js.map +1 -0
- package/dist/http/chat-normalize.d.ts +100 -0
- package/dist/http/chat-normalize.js +379 -0
- package/dist/http/chat-normalize.js.map +1 -0
- package/dist/http/chat-sse.d.ts +14 -0
- package/dist/http/chat-sse.js +52 -0
- package/dist/http/chat-sse.js.map +1 -0
- package/dist/http/chat-validate.d.ts +37 -0
- package/dist/http/chat-validate.js +190 -0
- package/dist/http/chat-validate.js.map +1 -0
- package/dist/http/chat.d.ts +8 -0
- package/dist/http/chat.js +137 -0
- package/dist/http/chat.js.map +1 -0
- package/dist/http/errors.d.ts +19 -0
- package/dist/http/errors.js +56 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/server.d.ts +19 -0
- package/dist/http/server.js +254 -0
- package/dist/http/server.js.map +1 -0
- package/package.json +67 -0
- package/protocol/VERSION.json +10 -0
- package/protocol/schemas/response-mapping.schema.json +38 -0
- package/protocol/schemas/x-codex.schema.json +12 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 John Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# codex-openai-proxy
|
|
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.
|
|
4
|
+
|
|
5
|
+
The package is in prerelease. Text completions, streaming, function tools, usage metadata, thread continuation, and per-request Codex policy selection are implemented.
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
Install Node.js 20 or newer, then start the prerelease from the narrowest directory tree Codex should be allowed to use:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx --yes codex-openai-proxy@next serve --root /absolute/path/to/project
|
|
13
|
+
```
|
|
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.
|
|
16
|
+
|
|
17
|
+
Check that the HTTP process is alive and that Codex is ready:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
curl http://127.0.0.1:8787/health
|
|
21
|
+
curl http://127.0.0.1:8787/ready
|
|
22
|
+
```
|
|
23
|
+
|
|
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.
|
|
25
|
+
|
|
26
|
+
To install the command instead of using `npx`:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install --global codex-openai-proxy@next
|
|
30
|
+
codex-openai-proxy serve --root /absolute/path/to/project
|
|
31
|
+
```
|
|
32
|
+
|
|
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
|
+
|
|
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
|
+
|
|
37
|
+
## Use an OpenAI client
|
|
38
|
+
|
|
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.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import OpenAI from "openai";
|
|
43
|
+
|
|
44
|
+
const client = new OpenAI({
|
|
45
|
+
baseURL: "http://127.0.0.1:8787/v1",
|
|
46
|
+
apiKey: "local",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const completion = await client.chat.completions.create({
|
|
50
|
+
model: "gpt-5.4-mini",
|
|
51
|
+
messages: [{ role: "user", content: "Summarize this project." }],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log(completion.choices[0].message.content);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The equivalent request with `curl` is:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
curl http://127.0.0.1:8787/v1/chat/completions \
|
|
61
|
+
-H 'Content-Type: application/json' \
|
|
62
|
+
-d '{
|
|
63
|
+
"model": "gpt-5.4-mini",
|
|
64
|
+
"messages": [{"role": "user", "content": "Summarize this project."}]
|
|
65
|
+
}'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## OpenAI-compatible behavior
|
|
69
|
+
|
|
70
|
+
The proxy intentionally implements a focused subset of Chat Completions rather than every OpenAI API endpoint.
|
|
71
|
+
|
|
72
|
+
Standard behavior includes:
|
|
73
|
+
|
|
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"`.
|
|
85
|
+
|
|
86
|
+
## Streaming
|
|
87
|
+
|
|
88
|
+
Set `stream: true` as with a standard Chat Completions request:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
curl -N http://127.0.0.1:8787/v1/chat/completions \
|
|
92
|
+
-H 'Content-Type: application/json' \
|
|
93
|
+
-d '{
|
|
94
|
+
"model": "gpt-5.4-mini",
|
|
95
|
+
"messages": [{"role": "user", "content": "Describe this repository."}],
|
|
96
|
+
"stream": true,
|
|
97
|
+
"stream_options": {"include_usage": true}
|
|
98
|
+
}'
|
|
99
|
+
```
|
|
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.
|
|
102
|
+
|
|
103
|
+
## Function tools
|
|
104
|
+
|
|
105
|
+
Function tools use the normal multi-request Chat Completions flow:
|
|
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.
|
|
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.
|
|
115
|
+
|
|
116
|
+
## Codex-specific extensions
|
|
117
|
+
|
|
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.
|
|
119
|
+
|
|
120
|
+
### Continue a Codex thread
|
|
121
|
+
|
|
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:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"model": "gpt-5.4-mini",
|
|
127
|
+
"messages": [{ "role": "user", "content": "Now explain the test strategy." }],
|
|
128
|
+
"previous_response_id": "chatcmpl_codex_..."
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
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.
|
|
133
|
+
|
|
134
|
+
### Receive Codex activity
|
|
135
|
+
|
|
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
|
+
|
|
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"`.
|
|
139
|
+
|
|
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.
|
|
141
|
+
|
|
142
|
+
### Select Codex policy
|
|
143
|
+
|
|
144
|
+
Working-directory, sandbox, and web-search controls are nonstandard request extensions nested under `x_codex`:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"model": "gpt-5.4-mini",
|
|
149
|
+
"messages": [{ "role": "user", "content": "Review this project." }],
|
|
150
|
+
"x_codex": {
|
|
151
|
+
"cwd": "/absolute/path/to/project",
|
|
152
|
+
"sandbox": "workspace-write",
|
|
153
|
+
"web_search": "disabled"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
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.
|
|
159
|
+
|
|
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.
|
|
161
|
+
|
|
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`.
|
|
173
|
+
|
|
174
|
+
## Usage metadata
|
|
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.
|
|
183
|
+
|
|
184
|
+
The launch directory is the default root for Codex work. Set a narrower boundary when needed:
|
|
185
|
+
|
|
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
|
|
193
|
+
|
|
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`.
|
|
195
|
+
|
|
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.
|
|
197
|
+
|
|
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.
|
|
208
|
+
|
|
209
|
+
## Troubleshooting
|
|
210
|
+
|
|
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.
|
|
217
|
+
|
|
218
|
+
## Uninstall and state cleanup
|
|
219
|
+
|
|
220
|
+
An `npx` invocation does not create a global package installation. Remove an explicit global or project installation with:
|
|
221
|
+
|
|
222
|
+
```sh
|
|
223
|
+
npm uninstall --global codex-openai-proxy
|
|
224
|
+
# Or, from a project that installed it locally:
|
|
225
|
+
npm uninstall codex-openai-proxy
|
|
226
|
+
```
|
|
227
|
+
|
|
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.
|
|
247
|
+
|
|
248
|
+
## Project documentation
|
|
249
|
+
|
|
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.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
+
import { JsonRpcTransport } from "./json-rpc.js";
|
|
3
|
+
import type { Logger } from "../core/logger.js";
|
|
4
|
+
import { type PolicyRequirements } from "../core/policy.js";
|
|
5
|
+
/** Identifies this proxy to app-server during initialization. */
|
|
6
|
+
export declare const CLIENT_NAME = "codex-openai-proxy";
|
|
7
|
+
/** Version of this proxy package and app-server client. */
|
|
8
|
+
export declare const CLIENT_VERSION: string;
|
|
9
|
+
/** Exact Codex CLI version supported by this proxy build. */
|
|
10
|
+
export declare const PINNED_CODEX_VERSION: string;
|
|
11
|
+
/** Process command and argument prefix used to invoke Codex without a shell. */
|
|
12
|
+
export interface CodexInvocation {
|
|
13
|
+
command: string;
|
|
14
|
+
prefixArgs: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Resolves the package-owned Codex executable unless explicitly overridden. */
|
|
17
|
+
export declare function resolveCodexExecutable(configuredPath: string): string;
|
|
18
|
+
/** Resolves a cross-platform process invocation for package-owned or explicit Codex. */
|
|
19
|
+
export declare function resolveCodexInvocation(configuredPath: string): CodexInvocation;
|
|
20
|
+
/** Owns the initialized app-server process and its JSON-RPC transport. */
|
|
21
|
+
export interface AppServer {
|
|
22
|
+
rpc: JsonRpcTransport;
|
|
23
|
+
requirements: PolicyRequirements;
|
|
24
|
+
child: ChildProcessWithoutNullStreams;
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/** Configures app-server process startup and shutdown. */
|
|
28
|
+
export interface StartAppServerOptions {
|
|
29
|
+
codexPath: string;
|
|
30
|
+
root: string;
|
|
31
|
+
startupTimeoutMs: number;
|
|
32
|
+
shutdownTimeoutMs: number;
|
|
33
|
+
log: Logger;
|
|
34
|
+
diagnosticLogging?: boolean;
|
|
35
|
+
spawnProcess?: typeof spawn;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}
|
|
38
|
+
/** Starts, verifies, and initializes an app-server child process. */
|
|
39
|
+
export declare function startAppServer(options: StartAppServerOptions): Promise<AppServer>;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { spawn, } from "node:child_process";
|
|
2
|
+
import { once } from "node:events";
|
|
3
|
+
import { JsonRpcTransport, RpcError } from "./json-rpc.js";
|
|
4
|
+
import { parsePolicyRequirements, selectApprovalPolicy, selectApprovalsReviewer, UNRESTRICTED_POLICY_REQUIREMENTS, } from "../core/policy.js";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { redact } from "../core/redact.js";
|
|
8
|
+
import { listenForAbort, withDeadline } from "../core/abort.js";
|
|
9
|
+
/** Identifies this proxy to app-server during initialization. */
|
|
10
|
+
export const CLIENT_NAME = "codex-openai-proxy";
|
|
11
|
+
/** Version of this proxy package and app-server client. */
|
|
12
|
+
export const CLIENT_VERSION = createRequire(import.meta.url)("../../package.json").version;
|
|
13
|
+
/** Reads the exact runtime dependency used as the compatibility contract. */
|
|
14
|
+
function codexPackageMetadata() {
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const packageJsonPath = require.resolve("@openai/codex/package.json");
|
|
17
|
+
return {
|
|
18
|
+
packageJsonPath,
|
|
19
|
+
packageJson: require(packageJsonPath),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Exact Codex CLI version supported by this proxy build. */
|
|
23
|
+
export const PINNED_CODEX_VERSION = codexPackageMetadata().packageJson.version;
|
|
24
|
+
/** Resolves the package-owned Codex executable unless explicitly overridden. */
|
|
25
|
+
export function resolveCodexExecutable(configuredPath) {
|
|
26
|
+
if (configuredPath !== "codex")
|
|
27
|
+
return configuredPath;
|
|
28
|
+
const { packageJsonPath, packageJson } = codexPackageMetadata();
|
|
29
|
+
const bin = typeof packageJson.bin === "string"
|
|
30
|
+
? packageJson.bin
|
|
31
|
+
: packageJson.bin?.codex;
|
|
32
|
+
if (!bin)
|
|
33
|
+
throw new Error("@openai/codex does not declare a codex binary");
|
|
34
|
+
return resolve(dirname(packageJsonPath), bin);
|
|
35
|
+
}
|
|
36
|
+
/** Resolves a cross-platform process invocation for package-owned or explicit Codex. */
|
|
37
|
+
export function resolveCodexInvocation(configuredPath) {
|
|
38
|
+
const executable = resolveCodexExecutable(configuredPath);
|
|
39
|
+
return configuredPath === "codex"
|
|
40
|
+
? { command: process.execPath, prefixArgs: [executable] }
|
|
41
|
+
: { command: executable, prefixArgs: [] };
|
|
42
|
+
}
|
|
43
|
+
/** Starts, verifies, and initializes an app-server child process. */
|
|
44
|
+
export async function startAppServer(options) {
|
|
45
|
+
const spawnProcess = options.spawnProcess ?? spawn;
|
|
46
|
+
const invocation = resolveCodexInvocation(options.codexPath);
|
|
47
|
+
await verifyCodex(invocation, options.root, options.startupTimeoutMs, spawnProcess, options.signal);
|
|
48
|
+
if (options.signal?.aborted)
|
|
49
|
+
throw abortReason(options.signal);
|
|
50
|
+
const child = spawnProcess(invocation.command, [...invocation.prefixArgs, "app-server"], {
|
|
51
|
+
cwd: options.root,
|
|
52
|
+
shell: false,
|
|
53
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
54
|
+
});
|
|
55
|
+
await waitForSpawn(child, options.signal);
|
|
56
|
+
const rpc = new JsonRpcTransport(child.stdout, child.stdin);
|
|
57
|
+
let stopping;
|
|
58
|
+
const stop = () => (stopping ??= stopChild(child, rpc, options.shutdownTimeoutMs));
|
|
59
|
+
child.stderr.setEncoding("utf8").on("data", (chunk) => {
|
|
60
|
+
options.log("warn", "app_server_stderr", {
|
|
61
|
+
message: "[REDACTED_DIAGNOSTIC]",
|
|
62
|
+
});
|
|
63
|
+
if (options.diagnosticLogging)
|
|
64
|
+
options.log("debug", "app_server_stderr_detail", {
|
|
65
|
+
message: redact(chunk, options.root).trim().slice(0, 2_000),
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
rpc.on("malformed", () => options.log("error", "app_server_malformed_output"));
|
|
69
|
+
rpc.on("request", (request) => failClosed(rpc, request, options.log));
|
|
70
|
+
child.once("exit", (code, signal) => {
|
|
71
|
+
rpc.close(new Error(`app-server exited (${code ?? signal ?? "unknown"})`));
|
|
72
|
+
options.log("error", "app_server_exited", { code, signal });
|
|
73
|
+
});
|
|
74
|
+
let requirements;
|
|
75
|
+
try {
|
|
76
|
+
await requestWithTimeout(rpc, "initialize", {
|
|
77
|
+
clientInfo: {
|
|
78
|
+
name: CLIENT_NAME,
|
|
79
|
+
title: "Codex OpenAI proxy",
|
|
80
|
+
version: CLIENT_VERSION,
|
|
81
|
+
},
|
|
82
|
+
capabilities: { experimentalApi: true },
|
|
83
|
+
}, options.startupTimeoutMs, options.signal);
|
|
84
|
+
rpc.notify("initialized");
|
|
85
|
+
requirements = await readConfigRequirements(rpc, options.startupTimeoutMs, options.signal);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
await stop();
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
rpc,
|
|
93
|
+
requirements,
|
|
94
|
+
child,
|
|
95
|
+
async stop() {
|
|
96
|
+
await stop();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Reads optional managed constraints without hiding malformed responses. */
|
|
101
|
+
async function readConfigRequirements(rpc, timeoutMs, signal) {
|
|
102
|
+
let value;
|
|
103
|
+
try {
|
|
104
|
+
value = await requestWithTimeout(rpc, "configRequirements/read", undefined, timeoutMs, signal);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof RpcError && error.rpcCode === -32601)
|
|
108
|
+
return UNRESTRICTED_POLICY_REQUIREMENTS;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
const requirements = parsePolicyRequirements(value);
|
|
112
|
+
// Managed requirements are fixed for the process lifetime, so an allowlist
|
|
113
|
+
// that permits no proxy-supported approval policy or reviewer is a deployment
|
|
114
|
+
// misconfiguration. Fail startup here rather than surfacing it as a per-request
|
|
115
|
+
// 400 that misleadingly blames the client's x_codex.
|
|
116
|
+
selectApprovalPolicy(requirements);
|
|
117
|
+
selectApprovalsReviewer(requirements);
|
|
118
|
+
return requirements;
|
|
119
|
+
}
|
|
120
|
+
/** Verifies that a configured executable matches the pinned contract version. */
|
|
121
|
+
async function verifyCodex(invocation, cwd, timeoutMs, spawnProcess, externalSignal) {
|
|
122
|
+
if (externalSignal?.aborted)
|
|
123
|
+
throw abortReason(externalSignal);
|
|
124
|
+
const child = spawnProcess(invocation.command, [...invocation.prefixArgs, "--version"], {
|
|
125
|
+
cwd,
|
|
126
|
+
shell: false,
|
|
127
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
128
|
+
});
|
|
129
|
+
await waitForSpawn(child, externalSignal);
|
|
130
|
+
const output = [];
|
|
131
|
+
child.stdout.on("data", (chunk) => output.push(chunk));
|
|
132
|
+
const timeoutReason = new Error("Codex version check timed out.");
|
|
133
|
+
await withDeadline(externalSignal, {
|
|
134
|
+
milliseconds: timeoutMs,
|
|
135
|
+
timeoutReason,
|
|
136
|
+
abortReason,
|
|
137
|
+
}, async (deadlineSignal) => {
|
|
138
|
+
const disposeAbort = listenForAbort(deadlineSignal, () => {
|
|
139
|
+
child.kill("SIGKILL");
|
|
140
|
+
});
|
|
141
|
+
try {
|
|
142
|
+
const [code, exitSignal] = await once(child, "exit");
|
|
143
|
+
// A parent cancellation wins even when it races the version deadline.
|
|
144
|
+
if (externalSignal?.aborted)
|
|
145
|
+
throw abortReason(externalSignal);
|
|
146
|
+
if (deadlineSignal.reason === timeoutReason && exitSignal === "SIGKILL")
|
|
147
|
+
throw timeoutReason;
|
|
148
|
+
if (code !== 0)
|
|
149
|
+
throw new Error("Codex version check failed.");
|
|
150
|
+
const value = Buffer.concat(output).toString("utf8").trim();
|
|
151
|
+
const match = /^codex-cli (\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/.exec(value);
|
|
152
|
+
if (!match)
|
|
153
|
+
throw new Error("The configured executable did not identify itself as Codex.");
|
|
154
|
+
if (match[1] !== PINNED_CODEX_VERSION)
|
|
155
|
+
throw new Error(`Unsupported Codex version ${match[1]}; expected ${PINNED_CODEX_VERSION}.`);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
disposeAbort();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/** Bounds an initialization request with an abortable deadline. */
|
|
163
|
+
async function requestWithTimeout(rpc, method, params, timeoutMs, externalSignal) {
|
|
164
|
+
return await withDeadline(externalSignal, {
|
|
165
|
+
milliseconds: timeoutMs,
|
|
166
|
+
timeoutReason: new Error(`${method} timed out.`),
|
|
167
|
+
abortReason,
|
|
168
|
+
}, async (deadlineSignal) => await rpc.request(method, params, deadlineSignal));
|
|
169
|
+
}
|
|
170
|
+
/** Gracefully stops app-server, escalating to SIGKILL after the deadline. */
|
|
171
|
+
async function stopChild(child, rpc, timeoutMs) {
|
|
172
|
+
rpc.close(new Error("proxy shutting down"));
|
|
173
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
174
|
+
return;
|
|
175
|
+
// Keep stdin open while signaling: on Linux, closing the pipe can make the
|
|
176
|
+
// child exit on EOF before its JavaScript SIGTERM handler is dispatched.
|
|
177
|
+
// Node closes the child's stdio automatically once the process exits.
|
|
178
|
+
child.kill("SIGTERM");
|
|
179
|
+
await withDeadline(undefined, {
|
|
180
|
+
milliseconds: timeoutMs,
|
|
181
|
+
timeoutReason: new Error("app-server shutdown timed out"),
|
|
182
|
+
}, async (deadlineSignal) => {
|
|
183
|
+
const disposeDeadline = listenForAbort(deadlineSignal, () => {
|
|
184
|
+
child.kill("SIGKILL");
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
await once(child, "exit");
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
disposeDeadline();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/** Waits until a child either spawns successfully or reports an error. */
|
|
195
|
+
async function waitForSpawn(child, signal) {
|
|
196
|
+
if (child.pid !== undefined)
|
|
197
|
+
return;
|
|
198
|
+
await new Promise((resolve, reject) => {
|
|
199
|
+
let disposeAbort = () => undefined;
|
|
200
|
+
const cleanup = () => {
|
|
201
|
+
child.off("spawn", onSpawn);
|
|
202
|
+
child.off("error", onError);
|
|
203
|
+
disposeAbort();
|
|
204
|
+
};
|
|
205
|
+
const onSpawn = () => {
|
|
206
|
+
cleanup();
|
|
207
|
+
resolve();
|
|
208
|
+
};
|
|
209
|
+
const onError = (error) => {
|
|
210
|
+
cleanup();
|
|
211
|
+
reject(error);
|
|
212
|
+
};
|
|
213
|
+
const onAbort = (abortedSignal) => {
|
|
214
|
+
cleanup();
|
|
215
|
+
child.kill("SIGKILL");
|
|
216
|
+
reject(abortReason(abortedSignal));
|
|
217
|
+
};
|
|
218
|
+
child.once("spawn", onSpawn);
|
|
219
|
+
child.once("error", onError);
|
|
220
|
+
disposeAbort = listenForAbort(signal, onAbort);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/** Returns a stable Error reason for lifecycle cancellation. */
|
|
224
|
+
function abortReason(signal) {
|
|
225
|
+
return signal.reason instanceof Error
|
|
226
|
+
? signal.reason
|
|
227
|
+
: new Error("app-server startup cancelled");
|
|
228
|
+
}
|
|
229
|
+
/** Method-specific result bodies for unsolicited app-server requests. */
|
|
230
|
+
const FAIL_CLOSED_RESPONSES = new Map([
|
|
231
|
+
["item/tool/requestUserInput", { answers: {} }],
|
|
232
|
+
["mcpServer/elicitation/request", { action: "decline", content: null }],
|
|
233
|
+
["item/commandExecution/requestApproval", { decision: "decline" }],
|
|
234
|
+
["item/fileChange/requestApproval", { decision: "decline" }],
|
|
235
|
+
["applyPatchApproval", { decision: "denied" }],
|
|
236
|
+
["execCommandApproval", { decision: "denied" }],
|
|
237
|
+
[
|
|
238
|
+
"item/permissions/requestApproval",
|
|
239
|
+
// Omitting every requested permission is the protocol's explicit denial.
|
|
240
|
+
{ permissions: {}, scope: "turn" },
|
|
241
|
+
],
|
|
242
|
+
]);
|
|
243
|
+
/** Declines one unsolicited app-server request with its method-specific response. */
|
|
244
|
+
function failClosed(rpc, request, log) {
|
|
245
|
+
if (request.method === "item/tool/call") {
|
|
246
|
+
// The continuation coordinator centrally routes this callback by thread.
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
const response = FAIL_CLOSED_RESPONSES.get(request.method);
|
|
251
|
+
if (response === undefined)
|
|
252
|
+
rpc.respondError(request.id, {
|
|
253
|
+
code: -32601,
|
|
254
|
+
message: "Unsupported server request",
|
|
255
|
+
});
|
|
256
|
+
else
|
|
257
|
+
rpc.respond(request.id, response);
|
|
258
|
+
}
|
|
259
|
+
log("warn", "app_server_request_declined", { method: request.method });
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=app-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-server.js","sourceRoot":"","sources":["../../src/app-server/app-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JsonRpcTransport } from "./json-rpc.js";
|
|
2
|
+
import type { Logger } from "../core/logger.js";
|
|
3
|
+
/** Dependencies and policy inputs for authentication. */
|
|
4
|
+
export interface AuthenticationOptions {
|
|
5
|
+
rpc: JsonRpcTransport;
|
|
6
|
+
log: Logger;
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
interactive: boolean;
|
|
9
|
+
terminal: (message: string) => void;
|
|
10
|
+
launch?: (url: string) => Promise<boolean>;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}
|
|
13
|
+
/** Ensures app-server has an authenticated OpenAI account. */
|
|
14
|
+
export declare function ensureAuthenticated(options: AuthenticationOptions): Promise<void>;
|
|
15
|
+
/** Opens a login URL with the platform browser without invoking a shell. */
|
|
16
|
+
export declare function launchBrowser(url: string): Promise<boolean>;
|