coding-tool-x 3.8.1 → 3.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/dist/web/assets/{Analytics-8dRv1aYW.js → Analytics-COEUXmd4.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BQRXSICO.js → ConfigTemplates-DLLkS53H.js} +1 -1
- package/dist/web/assets/{Home-BtzWhKIa.js → Home-DCEyLzwX.js} +1 -1
- package/dist/web/assets/{PluginManager-BTa-Ygf6.js → PluginManager-Oe1ZHoMO.js} +1 -1
- package/dist/web/assets/{ProjectList-F_cB8ldA.js → ProjectList-CWjuAFof.js} +1 -1
- package/dist/web/assets/{SessionList-LOrbPQvu.js → SessionList-v3ehGg9U.js} +1 -1
- package/dist/web/assets/{SkillManager-BzI_t-yV.js → SkillManager-DUF_qP5S.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-Db9RGiz7.js → WorkspaceManager-CjAWBosO.js} +1 -1
- package/dist/web/assets/index-D5hyHPj7.js +2 -0
- package/dist/web/index.html +1 -1
- package/docs/superpowers/plans/2026-08-10-codex-conversion-compatibility.md +251 -0
- package/docs/superpowers/plans/2026-08-11-windows-command-window-hiding.md +90 -0
- package/docs/superpowers/specs/2026-08-10-codex-conversion-compatibility-design.md +61 -0
- package/docs/superpowers/specs/2026-08-11-windows-command-window-hiding-design.md +44 -0
- package/package.json +1 -1
- package/src/commands/update.js +1 -1
- package/src/server/opencode-proxy-server.js +55 -1037
- package/src/server/services/claude-wire.js +694 -0
- package/src/server/services/codex-wire.js +219 -0
- package/src/server/services/gemini-wire.js +759 -0
- package/src/server/services/model-detector.js +49 -131
- package/src/server/services/omp-channels.js +23 -4
- package/src/server/services/omp-gateway.js +29 -1
- package/src/server/services/omp-settings-manager.js +27 -3
- package/src/server/services/opencode-gateway-adapters.js +22 -1147
- package/src/server/services/opencode-normalization.js +546 -0
- package/src/server/services/session-history-index.js +1 -1
- package/src/server/services/session-history-worker.js +2 -1
- package/src/server/services/speed-test.js +85 -303
- package/dist/web/assets/index-DIWJmyL6.js +0 -2
package/dist/web/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>coding-tool-x · AI 编程工作台</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-D5hyHPj7.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-BALVpMgd.js">
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Codex Conversion Compatibility Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Make both OpenCode's Codex conversion proxy and OMP's managed Codex provider work with Codex-only OpenAI-compatible services.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Add a small shared Codex wire helper for current client identity, request metadata, headers, and body normalization. OpenCode uses it before sending upstream; OMP provider API normalization emits `openai-codex-responses` only for explicit Codex channels while generic OpenAI Responses remains unchanged.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js 22 CommonJS, Express/http, Vitest, js-yaml, installed `codex-cli 0.144.1`, OMP 17.2.12.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Lock the Codex wire contract with failing tests
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `tests/unit/services/codex-wire.test.js`
|
|
17
|
+
- Modify: `tests/unit/services/opencode-gateway-adapters.test.js` if the existing adapter test file exists; otherwise extend `scripts/test-opencode-gateway-adapters.js`
|
|
18
|
+
- Modify: `tests/unit/services/omp-settings-manager.test.js:41-90`
|
|
19
|
+
|
|
20
|
+
- [ ] **Step 1: Write the failing shared-wire test**
|
|
21
|
+
|
|
22
|
+
Add a Vitest test that calls the planned `codex-wire` exports with a payload containing `system` input, `temperature`, `top_p`, `max_output_tokens`, `max_completion_tokens`, `service_tier`, `prompt_cache_retention`, and a caller session id. Assert the output has:
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
expect(result.body.input[0].role).toBe('developer');
|
|
26
|
+
expect(result.body.stream).toBe(true);
|
|
27
|
+
expect(result.body.store).toBe(false);
|
|
28
|
+
expect(result.body.include).toContain('reasoning.encrypted_content');
|
|
29
|
+
expect(result.body.temperature).toBeUndefined();
|
|
30
|
+
expect(result.body.max_output_tokens).toBeUndefined();
|
|
31
|
+
expect(result.headers['version']).toBe('0.144.1');
|
|
32
|
+
expect(result.headers.originator).toBe('codex_exec');
|
|
33
|
+
expect(result.headers['session-id']).toBe('session-test');
|
|
34
|
+
expect(result.headers['thread-id']).toBeTruthy();
|
|
35
|
+
expect(result.headers['x-codex-window-id']).toBeTruthy();
|
|
36
|
+
expect(result.body.client_metadata['x-codex-turn-metadata']).toBeTruthy();
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The test must import `createCodexRequest` from `src/server/services/codex-wire.js`, so it fails with module-not-found before implementation.
|
|
40
|
+
|
|
41
|
+
- [ ] **Step 2: Run the focused test and verify the expected red failure**
|
|
42
|
+
|
|
43
|
+
Run: `npx vitest run tests/unit/services/codex-wire.test.js`
|
|
44
|
+
|
|
45
|
+
Expected: FAIL because `src/server/services/codex-wire.js` does not exist.
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 3: Add the OMP mapping regression test**
|
|
48
|
+
|
|
49
|
+
In `tests/unit/services/omp-settings-manager.test.js`, add a test beside the provider writer tests:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
test('writes Codex-source providers with the Codex Responses API while preserving generic Responses', () => {
|
|
53
|
+
const manager = require('../../../src/server/services/omp-settings-manager');
|
|
54
|
+
const target = manager.writeManagedOmpProviders([
|
|
55
|
+
{
|
|
56
|
+
id: 'edge-codex',
|
|
57
|
+
providerKey: 'edge-codex',
|
|
58
|
+
baseUrl: 'https://edge.example/v1',
|
|
59
|
+
apiKey: 'secret',
|
|
60
|
+
gatewaySourceType: 'codex',
|
|
61
|
+
providerApi: 'responses',
|
|
62
|
+
model: 'gpt-5.5'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'generic-openai',
|
|
66
|
+
providerKey: 'generic-openai',
|
|
67
|
+
baseUrl: 'https://generic.example/v1',
|
|
68
|
+
apiKey: 'secret',
|
|
69
|
+
gatewaySourceType: 'openai_compatible',
|
|
70
|
+
providerApi: 'openai-responses',
|
|
71
|
+
model: 'gpt-4.1'
|
|
72
|
+
}
|
|
73
|
+
]);
|
|
74
|
+
const config = yaml.load(fs.readFileSync(target, 'utf8'));
|
|
75
|
+
expect(config.providers['ctx-edge-codex'].api).toBe('openai-codex-responses');
|
|
76
|
+
expect(config.providers['ctx-generic-openai'].api).toBe('openai-responses');
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- [ ] **Step 4: Run the OMP test and verify it fails for the current mapping**
|
|
81
|
+
|
|
82
|
+
Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js -t "Codex-source providers"`
|
|
83
|
+
|
|
84
|
+
Expected: FAIL because `normalizeProviderApi('responses')` currently returns `responses`, or the provider entry does not classify the Codex source.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### Task 2: Implement the shared Codex wire helper
|
|
89
|
+
|
|
90
|
+
**Files:**
|
|
91
|
+
- Create: `src/server/services/codex-wire.js`
|
|
92
|
+
- Test: `tests/unit/services/codex-wire.test.js`
|
|
93
|
+
|
|
94
|
+
- [ ] **Step 1: Implement deterministic request identity and headers**
|
|
95
|
+
|
|
96
|
+
Export:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
const CODEX_CLIENT_VERSION = '0.144.1';
|
|
100
|
+
function createCodexRequest(payload, options = {}) { /* { body, headers, model } */ }
|
|
101
|
+
function normalizeCodexResponsesInput(inputValue) { /* exported for direct tests */ }
|
|
102
|
+
function buildCodexTargetUrl(baseUrl) { /* exported for direct tests */ }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Use `options.sessionId` as the stable prompt/session identity, defaulting to `crypto.randomUUID()`. Generate UUIDs for `threadId`, `windowId`, `turnId`, and a process-local installation id. Use `originator: 'codex_exec'` and `user-agent: codex_exec/0.144.1 (<OS> <release>; <arch>) <terminal> (codex_exec; 0.144.1)`, with `process.platform`, `process.release.name`/`TERM_PROGRAM` fallback, and `process.arch`; tests may inject `userAgent` to avoid platform dependence.
|
|
106
|
+
|
|
107
|
+
Emit lowercase header keys:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
{
|
|
111
|
+
authorization: `Bearer ${apiKey}`,
|
|
112
|
+
accept: 'text/event-stream',
|
|
113
|
+
'content-type': 'application/json',
|
|
114
|
+
originator: 'codex_exec',
|
|
115
|
+
'user-agent': userAgent,
|
|
116
|
+
'session-id': sessionId,
|
|
117
|
+
'thread-id': threadId,
|
|
118
|
+
'x-client-request-id': sessionId,
|
|
119
|
+
'x-codex-window-id': windowId,
|
|
120
|
+
'x-codex-turn-metadata': JSON.stringify({
|
|
121
|
+
installation_id: installationId,
|
|
122
|
+
session_id: sessionId,
|
|
123
|
+
thread_id: threadId,
|
|
124
|
+
turn_id: turnId,
|
|
125
|
+
window_id: windowId,
|
|
126
|
+
request_kind: 'turn'
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Do not include `openai-beta`, `version`, `session_id`, or `conversation_id` because the verified official `0.144.1` request to the target service did not emit them. Never log the authorization value.
|
|
132
|
+
|
|
133
|
+
- [ ] **Step 2: Implement Codex body normalization**
|
|
134
|
+
|
|
135
|
+
Clone JSON-compatible payload data. Fill `model` from `fallbackModel` if absent. Convert string input to a single `message/user/input_text` item. Clone array input and rewrite only `role: 'system'` to `role: 'developer'`. Force `stream: true`, `store: false`; preserve tools, tool choice, reasoning, text, and unknown fields. Ensure `include` is a clean string array containing `reasoning.encrypted_content`.
|
|
136
|
+
|
|
137
|
+
Delete Codex-rejected fields: `max_output_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `top_k`, `min_p`, `presence_penalty`, `frequency_penalty`, `repetition_penalty`, `stop`, `service_tier`, `user`, `previous_response_id`, `prompt_cache_retention`, and `safety_identifier`. Set `prompt_cache_key` to the stable session id unless the caller supplied a non-empty key. Add `client_metadata` with the same identity projection as the official client; preserve non-reserved caller metadata.
|
|
138
|
+
|
|
139
|
+
- [ ] **Step 3: Run focused tests and verify green**
|
|
140
|
+
|
|
141
|
+
Run: `npx vitest run tests/unit/services/codex-wire.test.js`
|
|
142
|
+
|
|
143
|
+
Expected: PASS.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### Task 3: Replace OpenCode's duplicated Codex conversion path
|
|
148
|
+
|
|
149
|
+
**Files:**
|
|
150
|
+
- Modify: `src/server/opencode-proxy-server.js:24-30,1044-1121,1335-1356,3089-3158`
|
|
151
|
+
- Modify: `src/server/services/opencode-gateway-adapters.js:741-811`
|
|
152
|
+
- Create: `tests/unit/services/opencode-codex-proxy.test.js`
|
|
153
|
+
|
|
154
|
+
- [ ] **Step 1: Add the local fake-upstream integration regression test**
|
|
155
|
+
|
|
156
|
+
Start a local `http.createServer` that records request headers/body and returns a minimal successful Responses SSE stream containing `response.created`, `response.completed`, and `[DONE]`. Start the OpenCode proxy on port 0 with stubs for channel allocation, API key, and configuration dependencies as needed by the existing module seams. POST `/v1/responses` with a Codex channel and payload containing a system input plus rejected sampling fields. Assert the fake upstream receives `/v1/responses`, `originator: codex_exec`, `user-agent` containing `0.144.1`, `session-id`, `thread-id`, `x-codex-window-id`, `x-codex-turn-metadata`, and a body without the rejected fields. Assert the downstream response completes and the upstream API key was not written to logs or body metadata.
|
|
157
|
+
|
|
158
|
+
- [ ] **Step 2: Run the integration test and verify the old path fails**
|
|
159
|
+
|
|
160
|
+
Run: `npx vitest run tests/unit/services/opencode-codex-proxy.test.js`
|
|
161
|
+
|
|
162
|
+
Expected: FAIL because the current handler sends `codex_cli_rs/0.101.0`, `Version: 0.101.0`, and lacks the current Codex identity metadata.
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 3: Make the proxy use the shared helper**
|
|
165
|
+
|
|
166
|
+
Import `createCodexRequest` and `buildCodexTargetUrl` in `opencode-proxy-server.js`. Remove the local `normalizeCodexResponsesInput`, `convertOpenCodePayloadToCodexResponses`, and `buildCodexTargetUrl` implementations. Keep the independent response relay functions unchanged.
|
|
167
|
+
|
|
168
|
+
In `handleCodexGatewayRequest`, derive `sessionId` from `extractSessionIdFromRequest(req, originalPayload)` or let the helper generate it. Call `createCodexRequest(originalPayload, { apiKey: effectiveKey, fallbackModel: channel.model, sessionId })`, use its `body` and `headers`, and dispatch to the helper's target URL. Keep `wantsStream`, target-model validation, and existing error reporting behavior.
|
|
169
|
+
|
|
170
|
+
- [ ] **Step 4: Remove the second adapter implementation without changing its public API**
|
|
171
|
+
|
|
172
|
+
Change `convertOpenCodePayloadToCodexResponses` in `opencode-gateway-adapters.js` to delegate to `createCodexRequest(payload, { fallbackModel, apiKey: '' })` and return `{ requestBody: body, model }`, or remove the export only after updating every test/callsite. Keep the adapter export during this cutover because the existing regression script imports it.
|
|
173
|
+
|
|
174
|
+
- [ ] **Step 5: Run focused tests and verify green**
|
|
175
|
+
|
|
176
|
+
Run: `npx vitest run tests/unit/services/codex-wire.test.js tests/unit/services/opencode-codex-proxy.test.js tests/unit/services/opencode-gateway-adapters.test.js`
|
|
177
|
+
|
|
178
|
+
Expected: PASS.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Task 4: Emit the correct Codex API in OMP configuration
|
|
183
|
+
|
|
184
|
+
**Files:**
|
|
185
|
+
- Modify: `src/server/services/omp-settings-manager.js:63-71,308-364`
|
|
186
|
+
- Modify: `src/server/services/omp-channels.js:1-16,608-615`
|
|
187
|
+
- Modify: `tests/unit/services/omp-settings-manager.test.js`
|
|
188
|
+
- Modify: `tests/unit/services/omp-channels.test.js` if a direct normalization seam is available
|
|
189
|
+
|
|
190
|
+
- [ ] **Step 1: Implement explicit Codex alias normalization**
|
|
191
|
+
|
|
192
|
+
Change `normalizeProviderApi(value, options = {})` so:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
if (normalized === 'responses' && options.gatewaySourceType === 'codex') {
|
|
196
|
+
return 'openai-codex-responses';
|
|
197
|
+
}
|
|
198
|
+
if (normalized === 'responses') return 'openai-responses';
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Preserve all explicit supported API identifiers exactly. In `buildProviderEntry`, pass `channel.gatewaySourceType` into `normalizeProviderApi`. In the OMP sync candidate, normalize the provider API once using the source provider's `api` and the selected gateway source; keep the raw `wireApi` for compatibility with existing UI fields.
|
|
202
|
+
|
|
203
|
+
- [ ] **Step 2: Run the OMP mapping regression and verify green**
|
|
204
|
+
|
|
205
|
+
Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js -t "Codex-source providers"`
|
|
206
|
+
|
|
207
|
+
Expected: PASS, with generic OpenAI Responses still serialized as `openai-responses`.
|
|
208
|
+
|
|
209
|
+
- [ ] **Step 3: Run all affected service tests**
|
|
210
|
+
|
|
211
|
+
Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-channels.test.js tests/unit/services/omp-gateway-routing.test.js`
|
|
212
|
+
|
|
213
|
+
Expected: PASS.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Task 5: Verify both live paths and finish cleanup
|
|
218
|
+
|
|
219
|
+
**Files:**
|
|
220
|
+
- Modify: `CHANGELOG.md:7-12`
|
|
221
|
+
- No credential-bearing files
|
|
222
|
+
|
|
223
|
+
- [ ] **Step 1: Run the project's OpenCode adapter regression script**
|
|
224
|
+
|
|
225
|
+
Run: `node scripts/test-opencode-gateway-adapters.js`
|
|
226
|
+
|
|
227
|
+
Expected: `OpenCode gateway adapters regression tests passed`.
|
|
228
|
+
|
|
229
|
+
- [ ] **Step 2: Exercise the OpenCode proxy against the real target**
|
|
230
|
+
|
|
231
|
+
Start the project proxy with a temporary channel/config supplied through an isolated temporary HOME or injected test seam. Send a minimal OpenAI Responses request using `gpt-5.5`, `stream: true`, and a prompt asking for `OK`. Pass the key only through `EDGE_API_KEY` in the process environment. Assert the response reaches `response.completed` and contains non-empty output; capture only redacted headers and status.
|
|
232
|
+
|
|
233
|
+
- [ ] **Step 3: Exercise OMP with generated Codex provider configuration**
|
|
234
|
+
|
|
235
|
+
Generate a temporary `models.yml` entry for `openai-codex-responses` with base URL `https://api.ai-edge.xyz/v1`, model `gpt-5.5`, and an environment-backed key. Run installed `omp` with the temporary config and `EDGE_API_KEY` environment variable, asking for `OK`. Assert the command succeeds. Delete temporary config and ensure the repository contains no key.
|
|
236
|
+
|
|
237
|
+
- [ ] **Step 4: Run the complete relevant verification suite**
|
|
238
|
+
|
|
239
|
+
Run:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
npx vitest run tests/unit/services/codex-wire.test.js tests/unit/services/opencode-codex-proxy.test.js tests/unit/services/opencode-gateway-adapters.test.js tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-gateway-routing.test.js
|
|
243
|
+
node scripts/test-opencode-gateway-adapters.js
|
|
244
|
+
npm run test:basic
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Expected: all commands exit 0. If unrelated existing failures appear, report their exact command/output separately instead of weakening the new assertions.
|
|
248
|
+
|
|
249
|
+
- [ ] **Step 5: Record the fix in the changelog and scan for credentials**
|
|
250
|
+
|
|
251
|
+
Add one Unreleased fixed entry describing the current Codex compatibility headers/metadata and OMP API classification. Run a repository search for the literal key prefix/value and confirm no match in tracked source, tests, docs, or logs. Do not add a “follow-up” placeholder.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Windows Command Window Hiding Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Ensure every non-interactive child process launched by the project hides its Windows console window, without changing interactive session resume behavior.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Keep the existing direct child-process APIs and add `windowsHide: true` at each missing launch boundary. Use the existing injectable command runners and Windows regression script to prove options are passed, and use a focused source audit for launchers without test seams.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Node.js `child_process`, Vitest, Node assertion-based Windows regression script.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Add failing regression coverage for missing launch options
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `scripts/test-windows-regression.js`
|
|
17
|
+
- Test reference: `src/commands/resume.js`, `src/server/services/session-history-worker.js`, `src/commands/update.js`
|
|
18
|
+
|
|
19
|
+
- [ ] **Step 1: Add source-level assertions for known missing launch sites**
|
|
20
|
+
|
|
21
|
+
Add a helper that reads a file and asserts each child-process call's surrounding options contain `windowsHide: true`. Apply it to the session-history worker, update version probe, and resume launcher with separate expectations: the worker and update probe require hidden windows; resume requires both `stdio: 'inherit'` and `windowsHide: true` so its terminal behavior remains explicit.
|
|
22
|
+
|
|
23
|
+
- [ ] **Step 2: Run the focused regression to verify it fails**
|
|
24
|
+
|
|
25
|
+
Run: `npm run test:windows`
|
|
26
|
+
Expected: FAIL because `session-history-worker.js` has no `windowsHide: true`, `update.js`'s `execFileAsync` options omit it, and/or other audited launch sites remain unhidden.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### Task 2: Hide command and worker windows at missing launch boundaries
|
|
31
|
+
|
|
32
|
+
**Files:**
|
|
33
|
+
- Modify: `src/server/services/session-history-worker.js:13-24`
|
|
34
|
+
- Modify: `src/commands/update.js:15-19`
|
|
35
|
+
- Modify: `src/server/services/plugins-service.js:2817-2822`
|
|
36
|
+
- Modify: `src/server/services/notification-hooks.js` generated OpenCode/OMP spawn option templates around lines 393 and 446, if absent after the audit
|
|
37
|
+
- Modify: any additional `spawn`/`exec`/`execFile`/`spawnSync`/`fork` site found by the launch inventory that lacks `windowsHide: true`
|
|
38
|
+
|
|
39
|
+
- [ ] **Step 1: Add only the missing option fields**
|
|
40
|
+
|
|
41
|
+
For `session-history-worker`, add `windowsHide: true` to the `fork` options while preserving `silent`, `stdio`, environment, IPC, timeout, and signal handling.
|
|
42
|
+
|
|
43
|
+
For `update.getLatestVersion`, add `windowsHide: true` to the options passed to `execFileAsync` while preserving the 15-second timeout.
|
|
44
|
+
|
|
45
|
+
For every other non-interactive launcher found by the audit, add `windowsHide: true` directly to its existing options object. Do not change command strings, argument arrays, shell settings, output routing, cwd, environment, or error handling. Keep `src/commands/resume.js`'s `stdio: 'inherit'` unchanged; its existing `windowsHide: true` is retained as an explicit compatibility option.
|
|
46
|
+
|
|
47
|
+
- [ ] **Step 2: Run the focused regression to verify it passes**
|
|
48
|
+
|
|
49
|
+
Run: `npm run test:windows`
|
|
50
|
+
Expected: PASS with `Windows 专项回归测试通过`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Task 3: Add runner-level tests for command option propagation
|
|
55
|
+
|
|
56
|
+
**Files:**
|
|
57
|
+
- Modify: `tests/unit/services/omp-settings-manager.test.js` or the existing closest service test file for each injectable runner
|
|
58
|
+
- Modify: `tests/unit/services/omp-auth-gateway-client.test.js` or relevant existing test file if it already covers the runner
|
|
59
|
+
- Modify: `tests/unit/commands/daemon.test.js` only if a tested command launcher is added there
|
|
60
|
+
|
|
61
|
+
- [ ] **Step 1: Add behavior assertions where the module exposes an injected runner**
|
|
62
|
+
|
|
63
|
+
Capture the options argument in existing `catalogRunner`, `modelsRunner`, `commandRunner`, or equivalent test callbacks and assert `options.windowsHide === true`. Use real module behavior with only the existing runner seam; do not test a mock's own implementation.
|
|
64
|
+
|
|
65
|
+
- [ ] **Step 2: Run the focused unit tests**
|
|
66
|
+
|
|
67
|
+
Run: `npx vitest run tests/unit/services/omp-settings-manager.test.js tests/unit/services/omp-auth-gateway-client.test.js tests/unit/commands/daemon.test.js`
|
|
68
|
+
Expected: PASS with zero failures. If a listed file has no relevant test, run the closest existing test file instead and record the exact command output.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### Task 4: Verify complete launch inventory and interactive behavior
|
|
73
|
+
|
|
74
|
+
**Files:**
|
|
75
|
+
- Modify: `scripts/test-windows-regression.js` only if the audit needs a final explicit launcher list
|
|
76
|
+
|
|
77
|
+
- [ ] **Step 1: Re-run the scoped launch inventory**
|
|
78
|
+
|
|
79
|
+
Run the repository search for `spawn`, `spawnSync`, `exec`, `execFile`, `execSync`, and `fork` under `src`, `bin`, and `scripts`. Review every actual child-process call, ignoring regex `.exec` and database `.exec` calls. Confirm every non-interactive launch has `windowsHide: true` and the resume launcher retains `stdio: 'inherit'`.
|
|
80
|
+
|
|
81
|
+
- [ ] **Step 2: Run all required verification commands**
|
|
82
|
+
|
|
83
|
+
Run: `npm run test:windows`
|
|
84
|
+
Run: `npm run test:unit`
|
|
85
|
+
Run: `npm test`
|
|
86
|
+
Expected: each command exits 0 with no test failures. The Windows-specific script may be run on the current non-Windows host because it uses platform-parameterized helpers and source assertions.
|
|
87
|
+
|
|
88
|
+
- [ ] **Step 3: Inspect the final diff for scope**
|
|
89
|
+
|
|
90
|
+
Confirm only the approved spec/plan, runtime launch options, and focused regression tests changed. Do not alter unrelated existing working-tree changes in `CHANGELOG.md`, `package.json`, `package-lock.json`, `src/server/services/omp-gateway.js`, or `tests/unit/services/omp-gateway.test.js`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Codex Conversion Compatibility Design
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Restore Codex-only upstream compatibility for both OpenCode conversion proxy requests and OMP-managed providers, using `https://api.ai-edge.xyz/v1` with `gpt-5.5` as the live acceptance target without persisting credentials.
|
|
6
|
+
|
|
7
|
+
## Observed failure
|
|
8
|
+
|
|
9
|
+
- The installed official `codex-cli 0.144.1` succeeds against the target service and returns `OK`.
|
|
10
|
+
- The project's current simulated Codex request uses a hard-coded `0.101.0` identity and receives HTTP 403: `This account only allows Codex official clients`.
|
|
11
|
+
- The OpenCode proxy contains a duplicated Codex payload converter in `opencode-proxy-server.js` in addition to `opencode-gateway-adapters.js`.
|
|
12
|
+
- OMP maps `responses` to generic `openai-responses`; Codex-only services require OMP's `openai-codex-responses` provider contract.
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
### Shared Codex compatibility module
|
|
17
|
+
|
|
18
|
+
Add one focused module under `src/server/services/` that owns Codex wire compatibility:
|
|
19
|
+
|
|
20
|
+
- current pinned client identity matching the reference implementation;
|
|
21
|
+
- stable request-scoped session, conversation, thread, window, and client metadata;
|
|
22
|
+
- Codex request headers;
|
|
23
|
+
- Codex Responses body normalization.
|
|
24
|
+
|
|
25
|
+
The body normalization will force streaming upstream, disable storage, convert system roles to developer roles, ensure encrypted reasoning inclusion, and remove unsupported sampling/output-cap fields. It will preserve caller-owned tools, input items, model selection, and response conversion semantics.
|
|
26
|
+
|
|
27
|
+
### OpenCode conversion path
|
|
28
|
+
|
|
29
|
+
`opencode-proxy-server.js` will import the shared compatibility functions instead of maintaining a second payload converter. Its responsibilities remain routing, credential selection, upstream transport, SSE collection/relay, and conversion back to the caller's requested streaming mode.
|
|
30
|
+
|
|
31
|
+
Session identity will derive from an existing prompt cache/session identifier when supplied; otherwise the handler creates one request identity and uses it consistently in headers and body metadata.
|
|
32
|
+
|
|
33
|
+
### OMP configuration path
|
|
34
|
+
|
|
35
|
+
OMP remains a byte-transparent data-plane gateway. For channels configured with Codex conversion semantics, generated provider configuration must use `openai-codex-responses`, allowing OMP's native provider implementation to construct the official Codex wire request. Generic OpenAI Responses providers remain `openai-responses`; no global remapping is allowed.
|
|
36
|
+
|
|
37
|
+
The normalization decision is based on the channel's explicit provider API first and Codex gateway source only when a generic `responses` alias would otherwise be emitted.
|
|
38
|
+
|
|
39
|
+
## Error handling
|
|
40
|
+
|
|
41
|
+
- Invalid or missing models continue to fail before upstream dispatch.
|
|
42
|
+
- Invalid upstream URLs continue to return the existing structured proxy error.
|
|
43
|
+
- Upstream status and body remain visible through the existing failure logger without logging authorization credentials.
|
|
44
|
+
- The compatibility layer is deterministic and side-effect free except for explicitly injected/generated identity values.
|
|
45
|
+
|
|
46
|
+
## Tests
|
|
47
|
+
|
|
48
|
+
1. Adapter unit test: Codex body contains current compatibility metadata and removes every unsupported sampling/output-cap field.
|
|
49
|
+
2. Header unit test: current version, beta, originator, session/conversation/thread/window identifiers, and official-compatible user agent are emitted consistently.
|
|
50
|
+
3. OpenCode proxy integration test with a local fake upstream: inspect the actual headers/body crossing the process boundary and return a minimal Responses SSE stream.
|
|
51
|
+
4. OMP settings test: a Codex-source channel using `responses` emits `openai-codex-responses`; a generic OpenAI channel remains `openai-responses`.
|
|
52
|
+
5. Live acceptance:
|
|
53
|
+
- OpenCode proxy conversion path against the provided service;
|
|
54
|
+
- generated OMP provider exercised by installed OMP against the same service.
|
|
55
|
+
|
|
56
|
+
## Non-goals
|
|
57
|
+
|
|
58
|
+
- No WebSocket implementation in the OpenCode proxy.
|
|
59
|
+
- No OAuth or ChatGPT account-token handling.
|
|
60
|
+
- No change to generic OpenAI, Claude, or Gemini conversion behavior.
|
|
61
|
+
- No persistence of the provided API key in source, fixtures, logs, or design documents.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Windows Command Window Hiding Design
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-11
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Prevent Windows console windows from appearing for every background, non-interactive command launched by the project, including `ps`, PowerShell, `node`, `npm`, PM2, MCP, and worker processes, while preserving the current terminal behavior of interactive session resume commands.
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
The audit covers actual child-process launch APIs under `src/`, `bin/`, and project scripts: `spawn`, `spawnSync`, `exec`, `execFile`, `execSync`, and `fork`.
|
|
12
|
+
|
|
13
|
+
Every non-interactive child-process launch must pass `windowsHide: true`. This includes command probes, process inspection/termination, package installation and update operations, PM2 shell commands, MCP transports, OAuth/provider probes, generated notification helpers, certificate generation, web builds, and background workers.
|
|
14
|
+
|
|
15
|
+
The interactive session-resume path in `src/commands/resume.js` remains terminal-attached (`stdio: 'inherit'`) so Claude/CLI input and output continue to work in the caller's terminal. It is not treated as a hidden background process.
|
|
16
|
+
|
|
17
|
+
## Design
|
|
18
|
+
|
|
19
|
+
Use the existing project convention: add or retain `windowsHide: true` directly in each child-process options object. Do not introduce a process wrapper or global monkey patch. This keeps dependency injection used by tests intact, limits the behavioral change to Windows process creation, and avoids changing command arguments, shell selection, stdio, environment, cwd, timeout, or error handling.
|
|
20
|
+
|
|
21
|
+
The implementation will update only missing call sites. Existing `windowsHide: true` options remain unchanged. For `fork` workers, add the option alongside the existing `stdio`/`silent` configuration. For callback and promise-based APIs, options must be passed to the actual launch call, not merely to a higher-level helper that may be bypassed.
|
|
22
|
+
|
|
23
|
+
## Affected Areas
|
|
24
|
+
|
|
25
|
+
The implementation audit will verify and update the following areas when a launch lacks the option:
|
|
26
|
+
|
|
27
|
+
- `src/commands/`: log following, updates, and any other command launchers; preserve interactive resume semantics.
|
|
28
|
+
- `src/utils/`: process/port inspection and termination commands.
|
|
29
|
+
- `src/plugins/`: plugin installation commands.
|
|
30
|
+
- `src/server/api/`: PM2 autostart and API-triggered command launches.
|
|
31
|
+
- `src/server/services/`: MCP clients, OAuth/provider probes, OMP model probes, marketplace sync, certificate/web-build helpers, notification-generated Node helpers, and all worker forks.
|
|
32
|
+
- `scripts/`: Windows regression coverage; scripts themselves are not runtime package APIs unless they launch child processes as part of tested project behavior.
|
|
33
|
+
|
|
34
|
+
## Testing
|
|
35
|
+
|
|
36
|
+
Add focused regression assertions using existing injectable runners where available. The assertions must verify `windowsHide: true` is passed to command runners and worker/process launch options. Extend the Windows regression test for source-level worker/background launch coverage only where existing source checks are insufficient. Keep the interactive resume test/behavior unchanged and verify its `stdio: 'inherit'` option remains present.
|
|
37
|
+
|
|
38
|
+
Run the focused unit tests for changed modules, `npm run test:windows`, and the project test command appropriate to the changed contract. A passing test must demonstrate the options are present at the actual launch boundary; source-only checks are a fallback for modules without injectable launch seams.
|
|
39
|
+
|
|
40
|
+
## Non-goals
|
|
41
|
+
|
|
42
|
+
- Do not hide the user's interactive Claude/CLI terminal.
|
|
43
|
+
- Do not alter process lifetime, signal handling, shell quoting, command resolution, or output routing.
|
|
44
|
+
- Do not add retries, new abstractions, or unrelated Windows compatibility changes.
|
package/package.json
CHANGED
package/src/commands/update.js
CHANGED
|
@@ -15,7 +15,7 @@ async function getLatestVersion(packageName) {
|
|
|
15
15
|
const { stdout } = await execFileAsync(
|
|
16
16
|
npmCommand,
|
|
17
17
|
['view', packageName, 'version', '--json'],
|
|
18
|
-
{ timeout: 15000 }
|
|
18
|
+
{ timeout: 15000, windowsHide: true }
|
|
19
19
|
);
|
|
20
20
|
const parsed = JSON.parse(stdout.trim());
|
|
21
21
|
if (typeof parsed === 'string') return parsed;
|