@galaxy-yearn/codex-deepseek-gateway 0.1.4 → 0.1.6
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 +85 -199
- package/bin/codex-deepseek-gateway.js +52 -12
- package/config/codex-model-catalog.json +130 -0
- package/config/gateway.example.json +1 -17
- package/package.json +3 -2
- package/src/codex-launch.js +242 -39
- package/src/codex-sessions.js +72 -16
- package/src/config.js +1 -1
- package/src/firecrawl.js +2 -2
- package/src/protocol.js +640 -130
- package/src/server.js +128 -15
- package/src/tavily.js +3 -3
- package/src/web-search-emulator.js +104 -23
package/README.md
CHANGED
|
@@ -1,64 +1,54 @@
|
|
|
1
1
|
# Codex DeepSeek Gateway
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight local gateway for using DeepSeek models in Codex.
|
|
4
4
|
|
|
5
|
-
Codex keeps
|
|
5
|
+
Codex keeps using the OpenAI `Responses API` wire format. The gateway translates requests to DeepSeek-compatible `Chat Completions`, calls DeepSeek, then translates the result back to Responses JSON or `response.*` SSE events.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The runtime uses only the Node.js standard library. It installs under `~/.codex/deepseek-gateway`, runs as a detached background process, and does not edit your existing Codex config.
|
|
10
|
-
|
|
11
|
-
Use this when you want Codex to stay on its normal Responses API path while the actual model is DeepSeek, including DeepSeek thinking output, function calls, local session resume help, and optional web search emulation through Tavily and Firecrawl.
|
|
7
|
+
Package: [@galaxy-yearn/codex-deepseek-gateway](https://www.npmjs.com/package/@galaxy-yearn/codex-deepseek-gateway)
|
|
12
8
|
|
|
13
9
|
## Requirements
|
|
14
10
|
|
|
15
|
-
- Node.js
|
|
11
|
+
- Node.js 22 or newer
|
|
16
12
|
- A DeepSeek API key
|
|
17
|
-
- Codex
|
|
13
|
+
- Codex CLI 0.142.0 or newer is recommended
|
|
18
14
|
|
|
19
15
|
## Install
|
|
20
16
|
|
|
21
|
-
Run:
|
|
22
|
-
|
|
23
17
|
```sh
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
This copies the runtime to:
|
|
28
|
-
|
|
29
|
-
```text
|
|
30
|
-
~/.codex/deepseek-gateway
|
|
18
|
+
npm install -g @galaxy-yearn/codex-deepseek-gateway
|
|
19
|
+
codex-deepseek-gateway --version
|
|
20
|
+
codex-deepseek-gateway install
|
|
31
21
|
```
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
The runtime is copied to `~/.codex/deepseek-gateway`. Put your DeepSeek API key in:
|
|
34
24
|
|
|
35
25
|
```text
|
|
36
26
|
~/.codex/deepseek-gateway/config/gateway.local.json
|
|
37
|
-
~/.codex/deepseek-gateway/config/model-aliases.json
|
|
38
27
|
```
|
|
39
28
|
|
|
40
|
-
Put your DeepSeek API key in `gateway.local.json`:
|
|
41
|
-
|
|
42
29
|
```json
|
|
43
|
-
|
|
30
|
+
{
|
|
31
|
+
"upstreamApiKey": "sk-..."
|
|
32
|
+
}
|
|
44
33
|
```
|
|
45
34
|
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
-
If the key is already configured, `install` also starts the gateway. If this is your first install, add the key and then run:
|
|
35
|
+
`install` preserves an existing `gateway.local.json`. If this is your first install, add the key and start the gateway:
|
|
49
36
|
|
|
50
37
|
```sh
|
|
51
|
-
|
|
38
|
+
codex-deepseek-gateway start
|
|
39
|
+
codex-deepseek-gateway status
|
|
52
40
|
```
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
`status` should show `"reachable": true`.
|
|
43
|
+
|
|
44
|
+
## Codex Config
|
|
55
45
|
|
|
56
|
-
|
|
46
|
+
Add this provider to `~/.codex/config.toml`:
|
|
57
47
|
|
|
58
48
|
```toml
|
|
59
49
|
model_provider = "deepseek-gateway"
|
|
60
|
-
model = "deepseek-v4-
|
|
61
|
-
model_reasoning_effort = "
|
|
50
|
+
model = "deepseek-v4-pro"
|
|
51
|
+
model_reasoning_effort = "xhigh"
|
|
62
52
|
model_supports_reasoning_summaries = true
|
|
63
53
|
model_reasoning_summary = "auto"
|
|
64
54
|
|
|
@@ -68,225 +58,121 @@ base_url = "http://127.0.0.1:3000/v1"
|
|
|
68
58
|
wire_api = "responses"
|
|
69
59
|
```
|
|
70
60
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Restart Codex after changing `config.toml`.
|
|
61
|
+
Restart Codex after editing `config.toml`.
|
|
74
62
|
|
|
75
|
-
##
|
|
63
|
+
## Usage
|
|
76
64
|
|
|
77
|
-
Start
|
|
65
|
+
Start a new Codex conversation with gateway overrides:
|
|
78
66
|
|
|
79
67
|
```sh
|
|
80
|
-
|
|
81
|
-
npx @galaxy-yearn/codex-deepseek-gateway stop
|
|
82
|
-
npx @galaxy-yearn/codex-deepseek-gateway status
|
|
68
|
+
codex-deepseek-gateway new
|
|
83
69
|
```
|
|
84
70
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
Run `doctor` to check the active Codex config and the DeepSeek request shape the gateway will send:
|
|
71
|
+
Resume a Codex session from the current project:
|
|
88
72
|
|
|
89
73
|
```sh
|
|
90
|
-
|
|
74
|
+
codex-deepseek-gateway sessions
|
|
91
75
|
```
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- `codexConfigUsingGateway` should be `true`
|
|
96
|
-
- `codexModel` should be your gateway model, for example `deepseek-v4-pro`
|
|
97
|
-
- `codexReasoningEffort` should match `model_reasoning_effort`
|
|
98
|
-
- `deepseekThinking` shows the DeepSeek `thinking` payload
|
|
99
|
-
- `deepseekReasoningEffort` shows the DeepSeek effort sent upstream
|
|
100
|
-
- `reasoningDisplayMode` shows whether reasoning is shown as `summary`, `hidden`, or `disabled`
|
|
101
|
-
- `tavilyWebSearchReady` and `firecrawlWebFetchReady` show whether optional web search backends are usable
|
|
77
|
+
When using the gateway, prefer these `new` / `sessions` commands over plain `codex` / `codex resume`. The launcher adds the gateway provider, model catalog, model, and reasoning overrides.
|
|
102
78
|
|
|
103
|
-
|
|
79
|
+
In the interactive session picker, use Up/Down to move through a scrolling window of sessions. Press `n` to start a new conversation instead of resuming an existing session.
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
The installed `config/model-aliases.json` starts with:
|
|
108
|
-
|
|
109
|
-
```json
|
|
110
|
-
{
|
|
111
|
-
"deepseek-v4-flash": {
|
|
112
|
-
"model": "deepseek-v4-flash",
|
|
113
|
-
"thinking": "auto"
|
|
114
|
-
},
|
|
115
|
-
"deepseek-v4-pro": {
|
|
116
|
-
"model": "deepseek-v4-pro",
|
|
117
|
-
"thinking": "auto"
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
The gateway serves these aliases directly on `GET /v1/models`. If you also want to merge DeepSeek's upstream `/models` list, set this in `gateway.local.json`:
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
"fetchUpstreamModels": true
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Whether Codex TUI `/model` shows custom provider models depends on the Codex client build. `config.toml`, `new`, and `sessions` remain the reliable ways to choose a model.
|
|
129
|
-
|
|
130
|
-
## Reasoning
|
|
131
|
-
|
|
132
|
-
Codex reasoning effort maps to DeepSeek V4 thinking mode:
|
|
133
|
-
|
|
134
|
-
| Codex `model_reasoning_effort` | DeepSeek request |
|
|
135
|
-
| --- | --- |
|
|
136
|
-
| `low` | `thinking.type = disabled` |
|
|
137
|
-
| `medium` | `thinking.type = enabled`, `reasoning_effort = high` |
|
|
138
|
-
| `high` | `thinking.type = enabled`, `reasoning_effort = high` |
|
|
139
|
-
| `xhigh` | `thinking.type = enabled`, `reasoning_effort = max` |
|
|
140
|
-
|
|
141
|
-
`model_supports_reasoning_summaries = true` and `model_reasoning_summary = "auto"` tell Codex to use its normal thinking UI path. When DeepSeek returns `reasoning_content`, the gateway keeps the raw text as `reasoning_text` and mirrors it into `summary_text` with a `Reasoning` heading for Codex TUI compatibility.
|
|
142
|
-
|
|
143
|
-
When thinking is enabled, the gateway intentionally buffers visible assistant text and tool-call output until the upstream response completes, then emits reasoning first. This improves thinking order in Codex at the cost of higher first-token latency.
|
|
144
|
-
|
|
145
|
-
## Web Search
|
|
146
|
-
|
|
147
|
-
Web search is off by default. To let Codex's native `web_search` or `web_search_preview` tool work while DeepSeek is active, configure the optional backends in `gateway.local.json`.
|
|
148
|
-
|
|
149
|
-
Tavily provides search results:
|
|
150
|
-
|
|
151
|
-
```json
|
|
152
|
-
"tavilyApiKey": "tvly-...",
|
|
153
|
-
"tavilyWebSearchEnabled": true
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Firecrawl provides opened-page reading and focused page lookup:
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
"firecrawlApiKey": "fc-...",
|
|
160
|
-
"firecrawlWebFetchEnabled": true,
|
|
161
|
-
"firecrawlAutoScrapeTopResults": 3
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Codex does not need MCP, a different tool name, or prompt changes. The gateway converts Codex web search requests into internal Chat Completions tools:
|
|
165
|
-
|
|
166
|
-
- `tavily_search` searches the web and returns citation-ready snippets.
|
|
167
|
-
- `firecrawl_open_page` reads a specific public page.
|
|
168
|
-
- `firecrawl_find_in_page` reads a page with a focused query.
|
|
169
|
-
|
|
170
|
-
DeepSeek receives compact text context containing the search query, sources, snippets, and optional opened-page excerpts. Codex receives Responses-compatible `web_search_call` items, final assistant messages, and URL citation annotations when the final text cites a matching source marker.
|
|
171
|
-
|
|
172
|
-
The web payload is intentionally text-focused. Tavily is called with `include_raw_content: false`; Firecrawl defaults to main content, removes base64 images, rejects local/private URLs, and truncates page text before it reaches the model.
|
|
173
|
-
|
|
174
|
-
## New Conversations
|
|
175
|
-
|
|
176
|
-
Start a new Codex conversation with a temporary gateway model override:
|
|
81
|
+
Useful non-interactive forms:
|
|
177
82
|
|
|
178
83
|
```sh
|
|
179
|
-
|
|
84
|
+
codex-deepseek-gateway new --model deepseek-v4-flash --reasoning-effort low # start with explicit model and effort
|
|
85
|
+
codex-deepseek-gateway sessions --print # list resume commands
|
|
86
|
+
codex-deepseek-gateway sessions --all # include sessions from all projects
|
|
87
|
+
codex-deepseek-gateway sessions --exec <id-or-row> # resume directly by row or session id
|
|
180
88
|
```
|
|
181
89
|
|
|
182
|
-
|
|
90
|
+
`new` chooses a model, then Codex reasoning effort. `sessions` chooses a session first, then model and reasoning effort. Both launch Codex with:
|
|
183
91
|
|
|
184
92
|
```sh
|
|
185
|
-
codex -c model_provider=deepseek-gateway -c model=<model> -c model_reasoning_effort=<effort>
|
|
93
|
+
codex -c model_provider=deepseek-gateway -c model=<model> -c model_reasoning_effort=<effort> -c model_supports_reasoning_summaries=true -c model_reasoning_summary=auto
|
|
186
94
|
```
|
|
187
95
|
|
|
188
|
-
|
|
96
|
+
The installed launcher also passes `model_catalog_json` pointing at the packaged gateway Codex catalog, so Codex-native multi-agent validation accepts the DeepSeek model aliases and `low|medium|high|xhigh` reasoning efforts. This Codex setting replaces the default model catalog for that Codex process; it is not merged into it.
|
|
189
97
|
|
|
190
|
-
|
|
98
|
+
Inside a launcher-started Codex TUI, `/model` can switch between the packaged DeepSeek models and reasoning efforts.
|
|
191
99
|
|
|
192
|
-
|
|
193
|
-
npx @galaxy-yearn/codex-deepseek-gateway new --model deepseek-v4-flash --reasoning-effort low
|
|
194
|
-
```
|
|
100
|
+
Model aliases are read from:
|
|
195
101
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
```sh
|
|
199
|
-
npx @galaxy-yearn/codex-deepseek-gateway new --print
|
|
102
|
+
```text
|
|
103
|
+
~/.codex/deepseek-gateway/config/model-aliases.json
|
|
200
104
|
```
|
|
201
105
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Open a cross-provider session picker from a project:
|
|
106
|
+
`model-aliases.json` is managed by this package and is refreshed on install. The packaged Codex catalog currently allows the default aliases `deepseek-v4-flash` and `deepseek-v4-pro` for Codex-native sub-agent validation.
|
|
205
107
|
|
|
206
|
-
|
|
207
|
-
npx @galaxy-yearn/codex-deepseek-gateway sessions
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The picker is read-only. It scans Codex local transcript files, lists sessions for the current project across providers, then runs:
|
|
108
|
+
## Commands
|
|
211
109
|
|
|
212
110
|
```sh
|
|
213
|
-
codex
|
|
111
|
+
codex-deepseek-gateway install # copy runtime into ~/.codex/deepseek-gateway
|
|
112
|
+
codex-deepseek-gateway start # start the local gateway
|
|
113
|
+
codex-deepseek-gateway stop # stop the local gateway
|
|
114
|
+
codex-deepseek-gateway status # show process and endpoint status
|
|
115
|
+
codex-deepseek-gateway doctor # inspect config and request mapping
|
|
116
|
+
codex-deepseek-gateway new # start a Codex conversation through the launcher
|
|
117
|
+
codex-deepseek-gateway sessions # pick and resume a Codex session through the launcher
|
|
118
|
+
codex-deepseek-gateway uninstall # remove the local runtime
|
|
214
119
|
```
|
|
215
120
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- choose a model from `~/.codex/deepseek-gateway/config/model-aliases.json`
|
|
219
|
-
- choose Codex reasoning effort
|
|
220
|
-
- choose `[New conversation]` or the session to resume
|
|
221
|
-
- use `↑/↓` to select, `Enter` to confirm, `←` to go back, and `Esc` to quit
|
|
222
|
-
|
|
223
|
-
Print copyable resume commands instead of opening the picker:
|
|
121
|
+
`doctor` checks the active Codex config, DeepSeek request shape, reasoning mode, and optional web-search backend readiness.
|
|
224
122
|
|
|
225
|
-
|
|
226
|
-
npx @galaxy-yearn/codex-deepseek-gateway sessions --print
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Include sessions outside the current project:
|
|
123
|
+
To remove the local runtime and then uninstall the global package:
|
|
230
124
|
|
|
231
125
|
```sh
|
|
232
|
-
|
|
126
|
+
codex-deepseek-gateway uninstall
|
|
127
|
+
npm uninstall -g @galaxy-yearn/codex-deepseek-gateway
|
|
233
128
|
```
|
|
234
129
|
|
|
235
|
-
|
|
130
|
+
## Reasoning
|
|
236
131
|
|
|
237
|
-
|
|
238
|
-
npx @galaxy-yearn/codex-deepseek-gateway sessions --exec <id-or-row>
|
|
239
|
-
```
|
|
132
|
+
Codex effort maps to DeepSeek V4 thinking mode:
|
|
240
133
|
|
|
241
|
-
|
|
134
|
+
| Codex effort | DeepSeek request |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `low` | `thinking.type = disabled` |
|
|
137
|
+
| `medium` | `thinking.type = enabled`, `reasoning_effort = high` |
|
|
138
|
+
| `high` | `thinking.type = enabled`, `reasoning_effort = high` |
|
|
139
|
+
| `xhigh` | `thinking.type = enabled`, `reasoning_effort = max` |
|
|
242
140
|
|
|
243
|
-
|
|
244
|
-
npx @galaxy-yearn/codex-deepseek-gateway sessions --limit 50
|
|
245
|
-
```
|
|
141
|
+
When DeepSeek returns `reasoning_content`, the gateway preserves the raw text for DeepSeek history and sends a display-only Markdown-cleaned copy through Codex's reasoning summary UI. The raw reasoning is not duplicated into visible message content.
|
|
246
142
|
|
|
247
|
-
|
|
143
|
+
## Web Search
|
|
248
144
|
|
|
249
|
-
|
|
145
|
+
Web search is optional and off by default. Configure Tavily for search:
|
|
250
146
|
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
npx @galaxy-yearn/codex-deepseek-gateway doctor
|
|
257
|
-
npx @galaxy-yearn/codex-deepseek-gateway new
|
|
258
|
-
npx @galaxy-yearn/codex-deepseek-gateway sessions
|
|
259
|
-
npx @galaxy-yearn/codex-deepseek-gateway uninstall
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"tavilyApiKey": "tvly-...",
|
|
150
|
+
"tavilyWebSearchEnabled": true
|
|
151
|
+
}
|
|
260
152
|
```
|
|
261
153
|
|
|
262
|
-
|
|
154
|
+
Configure Firecrawl if you also want opened-page reading:
|
|
263
155
|
|
|
264
|
-
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"firecrawlApiKey": "fc-...",
|
|
159
|
+
"firecrawlWebFetchEnabled": true
|
|
160
|
+
}
|
|
161
|
+
```
|
|
265
162
|
|
|
266
|
-
|
|
163
|
+
Codex can keep requesting `web_search` / `web_search_preview`. The gateway exposes compact internal web tools to DeepSeek, executes Tavily/Firecrawl calls itself, feeds tool results back to the model, and returns Codex-compatible `web_search_call` items. `TAVILY_MAX_SEARCH_ROUNDS` defaults to `10` and is only a runaway/cost guardrail; when reached, the gateway disables web tools for one final-answer turn.
|
|
267
164
|
|
|
268
|
-
|
|
269
|
-
- streaming and non-streaming responses
|
|
270
|
-
- text input and output
|
|
271
|
-
- image, file, and audio content parts when DeepSeek accepts the corresponding Chat Completions shape
|
|
272
|
-
- function tools and tool-call history
|
|
273
|
-
- DeepSeek thinking mode and `reasoning_content`
|
|
274
|
-
- lightweight local `previous_response_id` / `conversation` history while the gateway process is running
|
|
275
|
-
- `GET /v1/models` with local model aliases and optional upstream discovery
|
|
276
|
-
- optional Tavily/Firecrawl-backed `web_search` emulation
|
|
277
|
-
- new-conversation launcher with per-run Codex config overrides
|
|
278
|
-
- read-only cross-provider session picker
|
|
165
|
+
Final answers should include useful source titles and URLs directly.
|
|
279
166
|
|
|
280
167
|
## Limits
|
|
281
168
|
|
|
282
|
-
Chat Completions is not a full Responses API replacement.
|
|
169
|
+
Chat Completions is not a full Responses API replacement.
|
|
283
170
|
|
|
284
|
-
- Hosted tools
|
|
285
|
-
- Tavily
|
|
286
|
-
- URL citations are returned in the Responses metadata path. Whether they appear as clickable links in the terminal depends on the Codex client build and how it renders custom-provider citation annotations.
|
|
171
|
+
- Hosted tools without a local Codex executor are represented as function shims. Web search is the only hosted tool the gateway emulates directly.
|
|
172
|
+
- Tavily/Firecrawl web emulation is text-focused; it does not provide browser control, screenshots, raw HTML, cookies, crawl jobs, or private-network access.
|
|
287
173
|
- OpenAI `file_id` values are passed through; the gateway cannot fetch private OpenAI-hosted files.
|
|
288
|
-
- In-memory conversation history is lost when the gateway restarts.
|
|
289
|
-
-
|
|
174
|
+
- In-memory `previous_response_id` / `conversation` history is lost when the gateway process restarts.
|
|
175
|
+
- Plain `codex` commands do not automatically load the packaged model catalog. Use the launcher when you want TUI `/model` and sub-agent validation to use the DeepSeek catalog.
|
|
290
176
|
|
|
291
177
|
## License
|
|
292
178
|
|
|
@@ -14,7 +14,7 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { loadConfig } from '../src/config.js';
|
|
16
16
|
import { newConversation } from '../src/codex-launch.js';
|
|
17
|
-
import { sessions } from '../src/codex-sessions.js';
|
|
17
|
+
import { DEFAULT_SESSION_LIMIT, sessions } from '../src/codex-sessions.js';
|
|
18
18
|
import { toProviderChatCompletionsRequest } from '../src/protocol.js';
|
|
19
19
|
|
|
20
20
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
@@ -39,6 +39,7 @@ Usage:
|
|
|
39
39
|
codex-deepseek-gateway uninstall
|
|
40
40
|
|
|
41
41
|
Options:
|
|
42
|
+
-v, --version Print package version
|
|
42
43
|
--dir <path> Install directory, defaults to ~/.codex/deepseek-gateway
|
|
43
44
|
--no-edit Do not open the local config file after install
|
|
44
45
|
--all With sessions, include sessions outside the current project
|
|
@@ -47,9 +48,8 @@ Options:
|
|
|
47
48
|
--reasoning-effort <level>
|
|
48
49
|
With new/sessions, target Codex reasoning effort
|
|
49
50
|
--exec <id> With sessions, run the generated codex resume command
|
|
50
|
-
--limit <n> With sessions, max rows to print, defaults to
|
|
51
|
-
--print With
|
|
52
|
-
copyable resume commands instead of picker
|
|
51
|
+
--limit <n> With sessions, max rows to print or show, defaults to ${DEFAULT_SESSION_LIMIT}
|
|
52
|
+
--print With sessions, print resume commands instead of picker
|
|
53
53
|
`);
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -58,7 +58,9 @@ function parseArgs(argv) {
|
|
|
58
58
|
const rest = [];
|
|
59
59
|
for (let index = 0; index < argv.length; index += 1) {
|
|
60
60
|
const arg = argv[index];
|
|
61
|
-
if (arg === '--
|
|
61
|
+
if (arg === '--version' || arg === '-v') {
|
|
62
|
+
options.version = true;
|
|
63
|
+
} else if (arg === '--dir' || arg === '-d' || arg === '-InstallDir') {
|
|
62
64
|
const value = argv[index + 1];
|
|
63
65
|
if (!value) throw new Error(`${arg} requires a path`);
|
|
64
66
|
options.dir = resolve(value);
|
|
@@ -118,6 +120,12 @@ function serverPath(installDir) {
|
|
|
118
120
|
return join(installDir, 'src', 'server.js');
|
|
119
121
|
}
|
|
120
122
|
|
|
123
|
+
function hasRuntimeMarkers(installDir) {
|
|
124
|
+
return existsSync(join(installDir, 'package.json')) &&
|
|
125
|
+
existsSync(join(installDir, 'bin', 'codex-deepseek-gateway.js')) &&
|
|
126
|
+
existsSync(join(installDir, 'src', 'server.js'));
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
function loadInstalledConfig(installDir) {
|
|
122
130
|
return loadConfig({ ...process.env, GATEWAY_CONFIG_FILE: configPath(installDir) });
|
|
123
131
|
}
|
|
@@ -215,10 +223,9 @@ function copyRuntime(installDir) {
|
|
|
215
223
|
rmSync(join(installDir, 'src'), { recursive: true, force: true });
|
|
216
224
|
cpSync(join(ROOT, 'bin'), join(installDir, 'bin'), { recursive: true });
|
|
217
225
|
cpSync(join(ROOT, 'src'), join(installDir, 'src'), { recursive: true });
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
226
|
+
copyFileSync(join(ROOT, 'config', 'codex-model-catalog.json'), join(installDir, 'config', 'codex-model-catalog.json'));
|
|
227
|
+
rmSync(join(installDir, 'config', 'codex-model-catalog.base.json'), { force: true });
|
|
228
|
+
copyFileSync(join(ROOT, 'config', 'model-aliases.example.json'), join(installDir, 'config', 'model-aliases.json'));
|
|
222
229
|
const localConfig = configPath(installDir);
|
|
223
230
|
if (!existsSync(localConfig)) {
|
|
224
231
|
copyFileSync(join(ROOT, 'config', 'gateway.example.json'), localConfig);
|
|
@@ -253,6 +260,10 @@ async function install(options) {
|
|
|
253
260
|
}
|
|
254
261
|
|
|
255
262
|
async function start(options) {
|
|
263
|
+
if (!existsSync(options.dir)) {
|
|
264
|
+
throw new Error(`Missing runtime at ${options.dir}. Run install first.`);
|
|
265
|
+
}
|
|
266
|
+
copyRuntime(options.dir);
|
|
256
267
|
if (!existsSync(serverPath(options.dir))) {
|
|
257
268
|
throw new Error(`Missing runtime at ${options.dir}. Run install first.`);
|
|
258
269
|
}
|
|
@@ -361,10 +372,19 @@ async function doctor(options) {
|
|
|
361
372
|
const summaryMode = String(config.codexReasoningSummary || '').toLowerCase();
|
|
362
373
|
const hideAgentReasoning = String(config.codexHideAgentReasoning).toLowerCase() === 'true';
|
|
363
374
|
const thinkingEnabled = upstreamRequest.thinking?.type === 'enabled';
|
|
375
|
+
const summaryEnabled = Boolean(summaryMode) &&
|
|
376
|
+
summaryMode !== 'none' &&
|
|
377
|
+
summaryMode !== 'disabled' &&
|
|
378
|
+
summaryMode !== 'off' &&
|
|
379
|
+
summaryMode !== 'false';
|
|
380
|
+
const codexReasoningSummaryEnabled = supportsReasoningSummaries && summaryEnabled;
|
|
381
|
+
const gatewayStreamingReasoningSummary = !hideAgentReasoning && thinkingEnabled;
|
|
364
382
|
const reasoningDisplayMode = hideAgentReasoning
|
|
365
383
|
? 'hidden'
|
|
366
|
-
: thinkingEnabled
|
|
384
|
+
: thinkingEnabled && codexReasoningSummaryEnabled
|
|
367
385
|
? 'summary'
|
|
386
|
+
: thinkingEnabled
|
|
387
|
+
? 'upstream-only'
|
|
368
388
|
: 'disabled';
|
|
369
389
|
print(JSON.stringify({
|
|
370
390
|
packageVersion: packageJson.version,
|
|
@@ -381,25 +401,45 @@ async function doctor(options) {
|
|
|
381
401
|
upstreamModel: upstreamRequest.model,
|
|
382
402
|
deepseekThinking: upstreamRequest.thinking || null,
|
|
383
403
|
deepseekReasoningEffort: upstreamRequest.reasoning_effort || null,
|
|
404
|
+
codexReasoningSummaryEnabled,
|
|
405
|
+
gatewayStreamingReasoningSummary,
|
|
384
406
|
reasoningDisplayMode,
|
|
385
407
|
tavilyWebSearchEnabled: Boolean(config.tavilyWebSearchEnabled),
|
|
386
408
|
tavilyWebSearchReady: Boolean(config.tavilyWebSearchEnabled && config.tavilyApiKey),
|
|
387
409
|
firecrawlWebFetchEnabled: Boolean(config.firecrawlWebFetchEnabled),
|
|
388
410
|
firecrawlWebFetchReady: Boolean(config.firecrawlWebFetchEnabled && config.firecrawlApiKey),
|
|
389
411
|
firecrawlAutoScrapeTopResults: config.firecrawlAutoScrapeTopResults,
|
|
390
|
-
hint: 'The gateway exposes model aliases on /v1/models.
|
|
412
|
+
hint: 'The gateway exposes model aliases on /v1/models. The launcher passes model_catalog_json for Codex multi-agent validation; plain codex commands must pass that override themselves.',
|
|
391
413
|
}, null, 2));
|
|
392
414
|
print('\n');
|
|
393
415
|
}
|
|
394
416
|
|
|
395
417
|
async function uninstall(options) {
|
|
396
|
-
|
|
418
|
+
if (!existsSync(options.dir)) {
|
|
419
|
+
print(`No install found at ${options.dir}\n`);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (resolve(options.dir) === ROOT) {
|
|
423
|
+
throw new Error(`Refusing to remove source checkout ${options.dir}`);
|
|
424
|
+
}
|
|
425
|
+
if (!hasRuntimeMarkers(options.dir)) {
|
|
426
|
+
throw new Error(`Refusing to remove ${options.dir}: it does not look like a codex-deepseek-gateway install directory.`);
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
await stop(options);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
process.stderr.write(`Warning: could not stop gateway before uninstall: ${error.message || error}\n`);
|
|
432
|
+
}
|
|
397
433
|
rmSync(options.dir, { recursive: true, force: true });
|
|
398
434
|
print(`Removed ${options.dir}\n`);
|
|
399
435
|
}
|
|
400
436
|
|
|
401
437
|
async function main() {
|
|
402
438
|
const { command, options } = parseArgs(process.argv.slice(2));
|
|
439
|
+
if (options.version) {
|
|
440
|
+
print(`${packageJson.version}\n`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
403
443
|
if (!command || command === '-h' || command === '--help' || command === 'help') {
|
|
404
444
|
usage();
|
|
405
445
|
return;
|