@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 CHANGED
@@ -1,64 +1,54 @@
1
1
  # Codex DeepSeek Gateway
2
2
 
3
- A small local gateway for using DeepSeek models from Codex.
3
+ A lightweight local gateway for using DeepSeek models in Codex.
4
4
 
5
- Codex keeps sending OpenAI `Responses API` requests to a local endpoint. The gateway converts those requests to DeepSeek-compatible `Chat Completions`, calls DeepSeek, then converts the result back to Responses JSON or streaming `response.*` events.
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
- NPM package: [@galaxy-yearn/codex-deepseek-gateway](https://www.npmjs.com/package/@galaxy-yearn/codex-deepseek-gateway)
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 20 or newer
11
+ - Node.js 22 or newer
16
12
  - A DeepSeek API key
17
- - Codex configured from `~/.codex/config.toml`
13
+ - Codex CLI 0.142.0 or newer is recommended
18
14
 
19
15
  ## Install
20
16
 
21
- Run:
22
-
23
17
  ```sh
24
- npx @galaxy-yearn/codex-deepseek-gateway install
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
- It creates two local config files:
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
- "upstreamApiKey": "sk-..."
30
+ {
31
+ "upstreamApiKey": "sk-..."
32
+ }
44
33
  ```
45
34
 
46
- `model-aliases.json` controls the gateway-facing model IDs exposed on `GET /v1/models` and used by the `new` and `sessions` pickers. Edit it only if you want to add or rename model aliases.
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
- npx @galaxy-yearn/codex-deepseek-gateway start
38
+ codex-deepseek-gateway start
39
+ codex-deepseek-gateway status
52
40
  ```
53
41
 
54
- ## Configure Codex
42
+ `status` should show `"reachable": true`.
43
+
44
+ ## Codex Config
55
45
 
56
- Edit `~/.codex/config.toml`:
46
+ Add this provider to `~/.codex/config.toml`:
57
47
 
58
48
  ```toml
59
49
  model_provider = "deepseek-gateway"
60
- model = "deepseek-v4-flash"
61
- model_reasoning_effort = "low"
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
- Use `deepseek-v4-pro` instead of `deepseek-v4-flash` if you want the pro model.
72
-
73
- Restart Codex after changing `config.toml`.
61
+ Restart Codex after editing `config.toml`.
74
62
 
75
- ## Start And Verify
63
+ ## Usage
76
64
 
77
- Start, stop, and inspect the local background process:
65
+ Start a new Codex conversation with gateway overrides:
78
66
 
79
67
  ```sh
80
- npx @galaxy-yearn/codex-deepseek-gateway start
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
- `status` should show `"reachable": true`.
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
- npx @galaxy-yearn/codex-deepseek-gateway doctor
74
+ codex-deepseek-gateway sessions
91
75
  ```
92
76
 
93
- Important fields:
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
- Avoid running Codex through a proxy while using the local gateway. Some proxy clients intercept `http://127.0.0.1:3000` and can break local gateway requests.
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
- ## Models
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
- npx @galaxy-yearn/codex-deepseek-gateway new
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
- The command first lets you choose a model from `~/.codex/deepseek-gateway/config/model-aliases.json`, then choose Codex reasoning effort, then runs:
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
- This does not edit `~/.codex/config.toml`; the `model_providers.deepseek-gateway` provider entry still needs to exist there.
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
- You can skip the picker:
98
+ Inside a launcher-started Codex TUI, `/model` can switch between the packaged DeepSeek models and reasoning efforts.
191
99
 
192
- ```sh
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
- Print the generated launch command instead of running Codex:
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
- ## Sessions
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
- ```sh
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 resume <session-id> -c model_provider=deepseek-gateway -c model=<model> -c model_reasoning_effort=<effort>
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
- Flow:
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
- ```sh
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
- npx @galaxy-yearn/codex-deepseek-gateway sessions --all
126
+ codex-deepseek-gateway uninstall
127
+ npm uninstall -g @galaxy-yearn/codex-deepseek-gateway
233
128
  ```
234
129
 
235
- Resume a matching session directly by row number or unique session id prefix:
130
+ ## Reasoning
236
131
 
237
- ```sh
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
- Limit how many session rows are shown or offered in the picker:
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
- ```sh
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
- The command does not edit session files, change provider ownership, or change Codex's native resume picker filters. It only helps you find the hidden session id and resume it with explicit config overrides.
143
+ ## Web Search
248
144
 
249
- ## Commands
145
+ Web search is optional and off by default. Configure Tavily for search:
250
146
 
251
- ```sh
252
- npx @galaxy-yearn/codex-deepseek-gateway install
253
- npx @galaxy-yearn/codex-deepseek-gateway start
254
- npx @galaxy-yearn/codex-deepseek-gateway stop
255
- npx @galaxy-yearn/codex-deepseek-gateway status
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
- `start` launches a headless background Node.js process. `stop` stops it. `uninstall` stops the gateway and removes `~/.codex/deepseek-gateway`, but it does not edit `~/.codex/config.toml`.
154
+ Configure Firecrawl if you also want opened-page reading:
263
155
 
264
- If `start` returns without visible output on your terminal, run `status`; `"reachable": true` is the source of truth.
156
+ ```json
157
+ {
158
+ "firecrawlApiKey": "fc-...",
159
+ "firecrawlWebFetchEnabled": true
160
+ }
161
+ ```
265
162
 
266
- ## What Works
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
- - Codex `POST /v1/responses`
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. Some Responses features have no equivalent upstream field.
169
+ Chat Completions is not a full Responses API replacement.
283
170
 
284
- - Hosted tools such as file search, computer use, image generation, and code interpreter are represented as function-tool shims unless Codex executes matching tools locally. Web search is the only hosted tool the gateway can emulate directly.
285
- - Tavily and Firecrawl web emulation is text-focused. It does not expose raw HTML, screenshots, browser actions, crawl/map jobs, cookies, private network access, or provider-specific payloads through Codex `web_search`.
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
- - The gateway exposes model aliases on `/v1/models`, including aliases from `config/model-aliases.json`. Whether Codex TUI `/model` actually shows custom provider models depends on the Codex build. `config.toml`, `new`, and `sessions` remain the reliable ways to choose a model.
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 20
51
- --print With new, print the launch command. With sessions, print
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 === '--dir' || arg === '-d' || arg === '-InstallDir') {
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
- const modelAliasesConfig = join(installDir, 'config', 'model-aliases.json');
219
- if (!existsSync(modelAliasesConfig)) {
220
- copyFileSync(join(ROOT, 'config', 'model-aliases.example.json'), modelAliasesConfig);
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. Whether Codex TUI /model shows them depends on the Codex build.',
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
- await stop(options);
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;