codex-grok-bridge 1.0.2 → 1.0.4

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,163 +1,631 @@
1
1
  # codex-grok-bridge
2
2
 
3
- Run **Grok 4.6 as the model inside Codex**, with Codex still owning tools,
4
- permissions, history and MCP. No xAI API key: inference goes through the login
5
- session of the installed `grok` CLI.
3
+ Run **Grok 4.6 as the model inside Codex**. Codex still owns tools, permissions,
4
+ history and MCP. Inference uses the installed `grok` CLI login session — not an
5
+ xAI API key.
6
+
7
+ Codex 안에서 **Grok 4.6**을 모델로 씁니다. 도구·권한·히스토리·MCP는 Codex가
8
+ 그대로 가집니다. 추론은 설치된 `grok` CLI의 로그인 세션으로 하며 `XAI_API_KEY`는
9
+ 쓰지 않습니다.
6
10
 
7
11
  ```
8
- Codex UI/CLI → app-server → codex-wrapper.mjs (adds grok-4.6 to the model list)
12
+ Codex UI/CLI → app-server → scripts/codex-wrapper.mjs (adds grok-4.6 to the model list)
9
13
  → localhost /v1/responses (the bridge)
10
14
  → cli-chat-proxy.grok.com
11
15
  → Codex executes every tool call; results return as the next input
12
16
  ```
13
17
 
14
- **Requirements** macOS, Node.js 22, `/Applications/Codex.app`, the `grok`
15
- CLI at `~/.grok/bin/grok` with a completed `grok login`. Zero runtime
16
- dependencies; the whole bridge is thirteen files under `src/`.
18
+ The published package is **macOS-only** (`"os": ["darwin"]`). Linux and Windows
19
+ installs are rejected by npm.
20
+
21
+ 게시된 패키지는 **macOS 전용**입니다(`"os": ["darwin"]`). Linux·Windows는 npm이
22
+ 설치를 거절합니다.
23
+
24
+ ---
25
+
26
+ # English
27
+
28
+ ## What this is
29
+
30
+ A local bridge that puts Grok 4.6 on Codex’s model list and sends Codex
31
+ `/v1/responses` traffic to `cli-chat-proxy.grok.com`. Grok does the inference.
32
+ Codex runs every tool call (shell, patch, MCP, …) and feeds the results back as
33
+ the next request’s `input`. That is the same agent loop as the GPT path.
34
+
35
+ The bridge does **not** execute Grok-native tools. It translates Codex tools into
36
+ function calling, streams the upstream Responses events, and rewrites names back
37
+ so Codex still recognizes them.
38
+
39
+ Thirteen files under `src/`. Zero runtime dependencies. Node.js ≥ 22.
17
40
 
18
- **Quick start**
41
+ This is not a second-opinion review product, not a Grok 4.7 adapter, and not a
42
+ Grok-native search product. If Codex exposes a web-search tool, Grok can call
43
+ that tool the same way it calls any other Codex tool.
44
+
45
+ ## Requirements
46
+
47
+ - macOS
48
+ - Node.js ≥ 22
49
+ - `/Applications/Codex.app`
50
+ - `grok` CLI at `~/.grok/bin/grok`
51
+ - a completed `grok login`
52
+
53
+ The `.command` launcher resolves paths from its own location, so moving the
54
+ folder does not require edits. Set `NODE=/path/to/node` if `node` is not on
55
+ `PATH`.
56
+
57
+ Verified against: Codex 0.153.4 / app 26.901.51231, Grok CLI 1.0.24, Node 22.23.0.
58
+ An app update that changes `CODEX_CLI_PATH` or the app-server protocol needs
59
+ re-verification.
60
+
61
+ ## Install and run
62
+
63
+ ### Terminal (npm)
19
64
 
20
65
  ```sh
21
66
  npm install -g codex-grok-bridge # macOS, Node ≥ 22
22
- codex-grok # Codex in the terminal, Grok 4.6 selected
67
+ codex-grok # launches Codex with Grok 4.6 available
68
+ codex-grok exec --skip-git-repo-check --sandbox workspace-write 'your task'
23
69
  ```
24
70
 
25
- From a checkout:
71
+ `codex-grok` registers the bridge as a model provider for the Codex process it
72
+ starts, and tears the provider down with that process.
73
+
74
+ ### From a checkout
26
75
 
27
76
  ```sh
28
77
  git clone https://github.com/deximple/codex-grok-bridge.git
29
78
  cd codex-grok-bridge
30
- npm test # 131 tests, no network, no inference
79
+ npm test # 132 tests, no network, no inference
31
80
  node scripts/codex-grok.mjs
32
81
  ```
33
82
 
34
- That is the whole setup for terminal use — the bridge registers itself as a
35
- model provider for the Codex process it launches and tears down with it.
83
+ ### Desktop
84
+
85
+ Double-click **Open Codex with Grok.command** in this folder, or keep a
86
+ **separate** `Codex Grok.app` in sync with
87
+ `scripts/install-codex-grok-app.sh` (see [Desktop install and update](#desktop-install-and-update)).
88
+
89
+ In the new Codex window, **select Grok 4.6 / xAI before starting a new
90
+ thread**. Existing GPT models stay on the list. A Codex window that was already
91
+ open does not get this extension.
92
+
93
+ The dedicated window stores UI data under
94
+ `~/.local/share/codex-grok-bridge/desktop` and **shares** the normal Codex home
95
+ for account, threads and settings. Work and setting changes can show up in other
96
+ Codex windows.
97
+
98
+ The installer never writes the stock Codex.app bundle, its signature,
99
+ `~/.codex/config.toml`, or Grok auth files. It does not register a launch
100
+ agent or a global environment variable.
101
+
102
+ Stop by closing the Codex window this extension opened. Ordinary Codex still
103
+ launches from its usual icon.
104
+
105
+ ## How it connects
106
+
107
+ 1. A wrapper set as `CODEX_CLI_PATH` starts the Codex app-server.
108
+ 2. The wrapper adds Grok to the model catalog and sets the provider of a new
109
+ Grok thread to `grok_build_cli`.
110
+ 3. Codex `/v1/responses` requests go to the localhost bridge.
111
+ 4. The bridge flattens Codex tools (plain functions, namespaced functions,
112
+ freeform custom tools, `web_search`) into function tools, then pipes the
113
+ `cli-chat-proxy.grok.com` Responses stream through.
114
+ 5. Tool results return as the next Codex request `input`.
115
+
116
+ Authentication is the `grok login` session. `XAI_API_KEY` is not used.
117
+
118
+ Upstream sockets use `node:http(s)` with a keep-alive `Agent` (30 s, max 4
119
+ sockets) and a 5-minute DNS cache. A failed lookup still uses a valid cached
120
+ address when one exists, so a 5–20 s tool gap does not force a fresh name
121
+ lookup every time. `GROK_BRIDGE_TRANSPORT=fetch` restores the older `fetch`
122
+ path.
123
+
124
+ A request that dies **before** the first SSE block is written to Codex is
125
+ retried once. After the first block, the bridge never retries — Codex already
126
+ saw bytes, so a replay would duplicate them. Deterministic refusals (422) and
127
+ user aborts are not retried either.
128
+
129
+ If the Responses path misbehaves, `GROK_BRIDGE_INFERENCE=cli` falls back to the
130
+ older CLI envelope. That path pastes the whole JSON into a prompt each turn, so
131
+ it is slower and more expensive, has no token-by-token streaming, and is capped
132
+ at three minutes per request.
133
+
134
+ The default Responses path streams. Codex `prompt_cache_key` is forwarded as
135
+ `x-grok-conv-id`.
136
+
137
+ ## What works and what does not
138
+
139
+ Measured, not guessed:
140
+
141
+ - Mixed catalog of the six GPT models plus `grok-4.6`, with Grok provider
142
+ routing, on a real app-server.
143
+ - Live Grok CLI → Codex `exec_command` → result → Grok final reply
144
+ (`BRIDGE_TOOL_OK`).
145
+ - A separate Codex window showing `Grok 4.6 / xAI Extra High`.
146
+ - cli-chat-proxy accepted a 339-function-tool request. The public API’s 200-tool
147
+ cap does not apply on this login path.
148
+
149
+ ### Provenance lines
150
+
151
+ Codex does not put provider or model into the prompt. The bridge appends a
152
+ `Transport:` line to `instructions` so the model can answer “is Grok attached?”
153
+ without opening config files. That line is transport provenance, in the same
154
+ category as a `User-Agent` header.
155
+
156
+ When image generation is on, an `Images:` line is appended as well: pictures on
157
+ this transport use Grok’s `image_generation` tool already on the request; do
158
+ not read Codex’s `imagegen` skill and do not send the picture to OpenAI.
159
+ `GROK_BRIDGE_IMAGE_GEN=off` drops both the tool and that line.
160
+
161
+ ### Reasoning
162
+
163
+ Plain-text summaries on Codex `reasoning` items are forwarded. Encrypted
164
+ `encrypted_content` and Codex’s own item ids are stripped. This is so a
165
+ multi-call turn can continue its own reasoning. The upstream has been observed
166
+ to accept this shape.
167
+
168
+ ### Concurrency
169
+
170
+ Up to **4** inferences run at once per bridge; the rest wait in a queue of 8.
171
+ Only a full queue returns `429`. Waiting is better than refusing because the
172
+ bridge has already sent response headers and is holding the stream with
173
+ keepalive. Setting concurrency to 1 kills Codex `spawn_agent` child inferences
174
+ that overlap the parent turn.
175
+
176
+ ### Tools
177
+
178
+ Ordinary function tools, namespaced function tools, and freeform custom tools
179
+ are translated. File edits, MCP, and similar work inside whatever Codex
180
+ exposed, at whatever approval policy the user set. Not every tool has been
181
+ live-tested individually.
182
+
183
+ ### Image attachments (vision)
184
+
185
+ PNG / JPEG / WebP. **10 MiB per image, 20 MiB per request**, up to four distinct
186
+ images, PNG up to 32 megapixels. The cap is measured: the upstream answered a
187
+ 12.5 MiB PNG, and the limit sits below that.
188
+
189
+ One unusable attachment no longer kills the conversation. The bridge walks the
190
+ whole history; a single over-limit image used to make every later turn fail
191
+ with 400. Now that attachment is replaced with an explanation and the rest
192
+ goes through. Usable images stay `input_image` (Grok reads them). Remote URLs
193
+ are not fetched.
194
+
195
+ ### Image generation
196
+
197
+ The bridge declares `{ type: "image_generation" }` on the upstream request.
198
+ Codex does not offer an image-generation tool to this provider (263 tools, none
199
+ of them generate — `view_image` only), so without the declaration Codex’s
200
+ `imagegen` skill falls back to OpenAI (`image_gen` or `OPENAI_API_KEY` +
201
+ `gpt-image-*`): thinking on Grok, pixels on another vendor.
202
+
203
+ Returned bytes are written to
204
+ `~/.local/share/codex-grok-bridge/generated-images/` as mode `0600`. Codex
205
+ cannot host those bytes, so the bridge turns the result into an assistant
206
+ message whose path is a markdown `file://` link:
207
+
208
+ `[ /path/to/grok-….jpg ](file:///path/to/grok-….jpg)`
209
+
210
+ Whether that link is clickable depends on Codex’s markdown renderer. Codex
211
+ events it does not know (`response.image_generation_call.*`) are dropped.
212
+
213
+ Grok’s `image_generation` is text-to-image only:
214
+
215
+ | Capability | Result |
216
+ |---|---|
217
+ | Text → image | Works |
218
+ | Transparent background | **No.** Always JPEG, no alpha. The model will say “transparent” and paint a checkerboard into the picture |
219
+ | Tool parameters (`background`, `output_format`) | Accepted and **silently ignored** |
220
+ | Image edit (image-to-image) | **Not a real edit.** The input is described in text and regenerated; composition and resolution change |
221
+
222
+ The bridge inspects the saved file. If there is no alpha channel it tells the
223
+ model not to describe the picture as transparent. If you need a real alpha
224
+ channel or accurate inpainting, use Codex’s OpenAI path.
225
+
226
+ ### GPT ↔ Grok switching
227
+
228
+ Supported on an idle persisted root thread. `turn/start`,
229
+ `thread/settings/update` and `turn/settings/update` cannot change
230
+ `modelProvider`; extra provider fields are ignored. The wrapper unsubscribes,
231
+ resumes the same id with an explicit model/provider, checks the returned
232
+ provider and permissions, then forwards the original request. Active threads,
233
+ ephemeral threads and child agents refuse a switch. Other subscribers can block
234
+ the reload; if they do, no inference is sent. Pick the model you want when
235
+ starting a new thread, before the first turn is saved.
236
+
237
+ ### Not guaranteed
238
+
239
+ Voice, cloud tasks, and video generation are not promised.
240
+
241
+ Upstream sometimes resets the connection mid-response (three measured cases:
242
+ 25 s / 27 s / 253 s, 726 KB–22 MB). The bridge cannot retry after the first
243
+ SSE byte. The provider sets Codex `request_max_retries` / `stream_max_retries`
244
+ to 2 so **Codex** can resend the same request. Only the side that owns the
245
+ conversation can retry safely.
246
+
247
+ ## Diagnostics
248
+
249
+ Every turn is one JSONL line at
250
+ `~/.local/share/codex-grok-bridge/logs/bridge.jsonl` (directory `0700`, file
251
+ `0600`, rotated once to `.1` above 4 MiB). `GROK_BRIDGE_DIAGNOSTICS=off` disables
252
+ it.
253
+
254
+ ```jsonl
255
+ {"at":"…","event":"turn_ok","mode":"proxy","elapsedMs":14118,"requestBytes":469749,"items":4,"tools":29}
256
+ {"at":"…","event":"turn_failed","kind":"dns","signature":"TypeError <- Error[EAI_AGAIN]","elapsedMs":15071,…}
257
+ ```
258
+
259
+ Structural facts only: time, success/failure, error class, error-chain names
260
+ and codes, elapsed ms, request bytes, item and tool counts. **No prompt text,
261
+ tool output, upstream body, or tokens.** `detail` is redacted (bearer tokens,
262
+ JWTs, API keys, home paths) and truncated to 400 characters.
263
+
264
+ Completed turns are logged too: an empty log on failure means the bridge was
265
+ never called.
266
+
267
+ `turn_failed.kind` is one of: `aborted`, `auth`, `dns`, `connect`,
268
+ `upstream_timeout`, `upstream_closed`, `upstream_protocol`, `payload`,
269
+ `internal`. Codex UI shows the same class as `bridge_<kind>`.
36
270
 
37
- `scripts/install-codex-grok-app.sh` keeps a **separate** `Codex Grok.app`
38
- bundle in sync with a checkout, so the desktop app can use the bridge too. It
39
- updates an existing bundle; it does not create one, and it will not touch the
40
- normal `Codex.app`. It never deletes a live bundle and refuses to run while a
41
- Codex Grok window is open. ESM is not hot-reloaded, so reopen the window after
42
- an update.
271
+ Start here when something breaks. Do not open `~/.grok/auth.json` or
272
+ `~/.codex/auth.json` to “check” the bridge.
43
273
 
44
- **Docs** `docs/HANDOFF.md` is the short version (state, pitfalls, open items).
45
- `docs/solution-20260909.md` is the long one: how a fixed 15-second failure was
46
- traced, what it turned out to be, and every measurement behind the fixes.
47
- `docs/experiments/` reproduces those measurements.
274
+ ## Environment variables
48
275
 
49
- **Diagnostics** every turn is recorded to
50
- `~/.local/share/codex-grok-bridge/logs/bridge.jsonl`: structural facts only,
51
- never prompt text, tool output or tokens. Start there when something breaks.
276
+ | Variable | Effect |
277
+ |---|---|
278
+ | `GROK_BRIDGE_IMAGE_GEN=off` | Do not declare Grok `image_generation`; do not append the `Images:` line |
279
+ | `GROK_BRIDGE_TRANSPORT=fetch` | Use Node `fetch` instead of `node:http(s)` |
280
+ | `GROK_BRIDGE_INFERENCE=cli` | Fall back to the CLI envelope path |
281
+ | `GROK_BRIDGE_DIAGNOSTICS=off` | Do not write the JSONL log |
282
+ | `NODE` | Absolute `node` binary for the `.command` launcher / desktop scripts |
283
+ | `CODEX_GROK_APP` | Alternate app bundle path for `install-codex-grok-app.sh` (default `/Applications/Codex Grok.app`) |
284
+
285
+ ## Verify
286
+
287
+ ```sh
288
+ npm test # 132 tests, no remote inference
289
+ npm run test:coverage # 80% line / branch / function gate
290
+ npm run verify:app-server # real app-server routing; also runs against an installed bundle
291
+ npm audit --omit=dev
292
+ ```
293
+
294
+ `npm test` does not call remote inference. `verify:app-server` talks to a real
295
+ app-server for the model list and a temporary thread; it does not run model
296
+ inference. A live CLI check spends the user’s quota.
297
+
298
+ ## Desktop install and update
299
+
300
+ ```sh
301
+ sh scripts/install-codex-grok-app.sh # sync bridge JS only (default)
302
+ sh scripts/install-codex-grok-app.sh --full # also rebuild and sign the launcher applet
303
+ ```
304
+
305
+ The default copies this checkout’s `src/` and `scripts/*.mjs` into the bundle,
306
+ prunes files the checkout no longer has, and `cmp`s every file at the end. It
307
+ never `rm -rf`s the live bundle. It refuses while a Codex Grok window is open
308
+ (`--force` to override). ESM is not hot-reloaded: **reopen the window** after a
309
+ sync.
310
+
311
+ The script updates an existing `Codex Grok.app`. It does not create one, and it
312
+ does not touch `/Applications/Codex.app`.
313
+
314
+ ## Security notes
315
+
316
+ The bridge binds loopback only. Each inference request needs a per-run
317
+ temporary token. Browser `Origin` requests, unsupported models, oversized
318
+ bodies, and unknown tool calls are rejected. Grok prompt temp files are `0600`
319
+ and deleted on exit. CLI error text and credentials are not returned in
320
+ responses. Codex’s and Grok’s own retention policies still apply.
321
+
322
+ ## Further docs
323
+
324
+ - `docs/HANDOFF.md` — current state, pitfalls, open items
325
+ - `docs/solution-20260909.md` — how a fixed 15-second failure was traced, and
326
+ every measurement behind the fixes
327
+ - `docs/experiments/` — scripts that reproduce those measurements
328
+
329
+ ## References
330
+
331
+ - [Grok Build headless scripting](https://docs.x.ai/build/cli/headless-scripting)
332
+ - [Grok Build source](https://github.com/xai-org/grok-build)
333
+ - [Codex source](https://github.com/openai/codex)
52
334
 
53
335
  ---
54
336
 
55
- 아래는 한국어 상세 설명입니다.
337
+ # 한국어
338
+
339
+ ## 이게 뭔가
340
+
341
+ Grok 4.6을 Codex 모델 목록에 넣고, Codex의 `/v1/responses`를
342
+ `cli-chat-proxy.grok.com`으로 넘기는 로컬 브리지입니다. 추론은 Grok이 합니다.
343
+ 도구 호출(셸, 패치, MCP, …)은 Codex가 실행하고, 결과는 다음 요청의 `input`으로
344
+ 돌아갑니다. GPT 경로와 같은 에이전트 루프입니다.
345
+
346
+ 브리지는 Grok 네이티브 도구를 실행하지 않습니다. Codex 도구를 function
347
+ calling으로 옮기고, 상류 Responses 스트림을 전달한 뒤, Codex가 알아보는
348
+ 이름으로 되돌립니다.
349
+
350
+ `src/` 아래 파일 13개. 런타임 의존성 없음. Node.js 22 이상.
56
351
 
57
- ## Codex + Grok Build CLI
352
+ 코드 리뷰 전용 제품이 아니고, Grok 4.7 어댑터도 아니며, Grok 네이티브 검색
353
+ 제품도 아닙니다. Codex가 웹 검색 도구를 노출하면 Grok은 다른 Codex 도구와 같이
354
+ 그 도구를 호출할 수 있습니다.
58
355
 
59
- Grok 4.6 / xAI를 Codex 모델 목록에 추가하고, Grok의 도구 요청을 Codex가 실행하도록 연결하는 로컬 확장입니다. xAI API 키를 사용하지 않습니다. 설치된 `grok` CLI의 로그인 세션으로 추론합니다.
356
+ ## 필요한
60
357
 
61
- ## 실행
358
+ - macOS
359
+ - Node.js 22 이상
360
+ - `/Applications/Codex.app`
361
+ - `~/.grok/bin/grok`
362
+ - 완료된 `grok login`
62
363
 
63
- 이 폴더의 **Open Codex with Grok.command**를 더블 클릭합니다. 별도로 열린 Codex 창에서 **새 작업을 시작하기 전에 Grok 4.6 / xAI를 선택**하세요. 기존 GPT 모델도 목록에 유지됩니다. 현재 실행 중인 일반 Codex 창에는 이 확장이 주입되지 않습니다.
364
+ `.command` 런처는 자기 위치를 기준으로 경로를 잡으므로 폴더를 옮겨도 수정할
365
+ 필요가 없습니다. `PATH`에 `node`가 없으면 `NODE=/path/to/node`로 지정합니다.
64
366
 
65
- 터미널에서는 다음과 같이 실행할 있습니다.
367
+ 검증 버전: Codex 0.153.4 / 앱 26.901.51231, Grok CLI 1.0.24, Node 22.23.0.
368
+ 앱 업데이트가 `CODEX_CLI_PATH`나 app-server 프로토콜을 바꾸면 재검증이
369
+ 필요합니다.
370
+
371
+ ## 설치와 실행
372
+
373
+ ### 터미널 (npm)
66
374
 
67
375
  ```sh
68
- npm install -g codex-grok-bridge
69
- codex-grok
376
+ npm install -g codex-grok-bridge # macOS, Node ≥ 22
377
+ codex-grok # Grok 4.6이 있는 Codex를 띄움
70
378
  codex-grok exec --skip-git-repo-check --sandbox workspace-write '작업 내용'
71
379
  ```
72
380
 
73
- 체크아웃에서는 `node scripts/codex-grok.mjs`가 같은 진입점입니다. 필요한 설치: Node.js 22 이상, `/Applications/Codex.app`, `~/.grok/bin/grok`, 완료된 `grok login`. `.command` 런처는 자기 위치를 기준으로 경로를 잡으므로 폴더를 옮겨도 수정할 필요가 없습니다. `NODE=/path/to/node`로 Node를 지정할 수 있습니다.
381
+ `codex-grok`는 자기가 띄운 Codex 프로세스에만 브리지를 모델 제공자로 등록하고,
382
+ 그 프로세스와 함께 내립니다.
74
383
 
75
- ## 연결 방식
384
+ ### 체크아웃에서
76
385
 
77
- 기본 경로는 Codex-GPT와 같은 Responses 루프입니다. Grok 네이티브 도구는 실행하지 않습니다. 인증은 `grok login` 세션이며 `XAI_API_KEY`를 쓰지 않습니다.
386
+ ```sh
387
+ git clone https://github.com/deximple/codex-grok-bridge.git
388
+ cd codex-grok-bridge
389
+ npm test # 132건, 네트워크·추론 없음
390
+ node scripts/codex-grok.mjs
391
+ ```
392
+
393
+ ### 데스크톱
394
+
395
+ 이 폴더의 **Open Codex with Grok.command**를 더블 클릭하거나,
396
+ `scripts/install-codex-grok-app.sh`로 **별도의** `Codex Grok.app`을 체크아웃과
397
+ 맞춥니다([데스크톱 설치와 갱신](#데스크톱-설치와-갱신)).
398
+
399
+ 새로 열린 Codex 창에서 **새 작업을 시작하기 전에 Grok 4.6 / xAI를 선택**하세요.
400
+ 기존 GPT 모델도 목록에 남습니다. 이미 열려 있던 일반 Codex 창에는 이 확장이
401
+ 주입되지 않습니다.
402
+
403
+ 전용 창은 UI 데이터를 `~/.local/share/codex-grok-bridge/desktop`에 두고,
404
+ 계정·작업·설정은 기존 Codex 홈을 **공유**합니다. 작업 내용과 설정 변경은 다른
405
+ Codex 창에도 보일 수 있습니다.
406
+
407
+ 설치 스크립트는 정품 Codex.app 번들, 코드 서명, `~/.codex/config.toml`, Grok
408
+ 인증 파일을 수정하지 않습니다. 자동 시작 서비스나 전역 환경변수도 등록하지
409
+ 않습니다.
410
+
411
+ 중지하려면 이 확장으로 연 Codex 창을 닫으면 됩니다. 일반 Codex는 기존 아이콘으로
412
+ 실행합니다.
413
+
414
+ ## 연결 방식
78
415
 
79
416
  1. `CODEX_CLI_PATH`로 지정한 래퍼가 Codex app-server를 실행합니다.
80
- 2. 래퍼가 모델 목록에 Grok를 추가하고 새 Grok 작업의 제공자를 `grok_build_cli`로 설정합니다.
81
- 3. Codex의 `/v1/responses` 요청은 localhost 변환기로 갑니다.
82
- 4. 변환기는 Codex 도구를 function calling으로 옮긴 뒤 `cli-chat-proxy.grok.com` Responses 스트림을 그대로 이어줍니다. MCP/셸/패치 실행은 Codex가 합니다.
83
- 5. 도구 결과는 다음 Codex 요청의 `input`으로 다시 들어갑니다. GPT 경로와 같은 에이전트 루프입니다.
417
+ 2. 래퍼가 모델 목록에 Grok를 추가하고, 새 Grok 작업의 제공자를
418
+ `grok_build_cli`로 설정합니다.
419
+ 3. Codex `/v1/responses` 요청은 localhost 브리지로 갑니다.
420
+ 4. 브리지는 Codex 도구(일반 함수, namespace 함수, freeform 커스텀, `web_search`)를
421
+ function tool로 펼친 뒤 `cli-chat-proxy.grok.com` Responses 스트림을 이어줍니다.
422
+ 5. 도구 결과는 다음 Codex 요청의 `input`으로 돌아갑니다.
84
423
 
85
- 상류 연결은 `node:http(s)`와 keep-alive `Agent`(소켓 30초 유지, 최대 4개)로 직접 엽니다. DNS는 5분 TTL로 캐시하며, 조회가 실패해도 유효한 캐시가 있으면 그것으로 버팁니다. 도구 실행으로 5–20초가 비는 사이에 소켓이 닫혀 매번 새로 이름을 찾는 상황을 피하기 위한 것입니다. `GROK_BRIDGE_TRANSPORT=fetch`로 이전 `fetch` 경로로 되돌릴 수 있습니다.
424
+ 인증은 `grok login` 세션입니다. `XAI_API_KEY`는 쓰지 않습니다.
86
425
 
87
- Codex에 SSE 블록을 쓰기 **전**에 죽은 요청은 번 다시 보냅니다. 아직 아무것도 전달하지 않았으므로 재전송이 대화를 오염시키지 않기 때문입니다. 첫 블록을 쓴 뒤에는 절대 재시도하지 않고, 422 같은 결정적 거절과 사용자 중단도 재시도하지 않습니다.
426
+ 상류 소켓은 `node:http(s)` keep-alive `Agent`(30초, 최대 4개)와 TTL 5분 DNS
427
+ 캐시를 씁니다. 조회가 실패해도 유효한 캐시가 있으면 그것으로 버팁니다. 도구
428
+ 실행으로 5–20초가 비어도 매번 이름을 다시 찾지 않기 위해서입니다.
429
+ `GROK_BRIDGE_TRANSPORT=fetch`로 이전 `fetch` 경로로 되돌릴 수 있습니다.
88
430
 
89
- 문제가 있으면 `GROK_BRIDGE_INFERENCE=cli`로 이전 CLI 봉투 경로를 있습니다. CLI 경로는 매 턴 전체 JSON을 프롬프트로 넣기 때문에 토큰과 지연이 큽니다.
431
+ Codex에 SSE 블록을 쓰기 **전**에 죽은 요청은 다시 보냅니다. 아직
432
+ 아무것도 전달하지 않았으므로 재전송이 대화를 오염시키지 않습니다. 첫 블록을
433
+ 쓴 뒤에는 절대 재시도하지 않습니다. 422 같은 결정적 거절과 사용자 중단도
434
+ 재시도하지 않습니다.
90
435
 
91
- 설치된 Codex 번들, 코드 서명, `~/.codex/config.toml`, Grok 인증 파일은 수정하지 않습니다. 전용 창은 `~/.local/share/codex-grok-bridge/desktop`에 UI 데이터를 저장하고 기존 Codex 홈의 계정·작업·설정을 공유합니다. 따라서 실제 사용자 작업 내용과 설정 변경은 다른 Codex 창에서도 보일 수 있습니다.
436
+ Responses 경로가 이상하면 `GROK_BRIDGE_INFERENCE=cli`로 이전 CLI 봉투 경로를
437
+ 씁니다. 매 턴 전체 JSON을 프롬프트로 넣으므로 더 느리고 비싸며, 토큰 단위
438
+ 실시간 출력이 없고, 요청당 3분 제한입니다.
439
+
440
+ 기본 Responses 경로는 스트림을 전달합니다. Codex `prompt_cache_key`는
441
+ `x-grok-conv-id`로 넘깁니다.
92
442
 
93
443
  ## 확인된 동작과 제한
94
444
 
95
- - 실제 app-server에서 GPT 6개 모델과 `grok-4.6`의 혼합 목록 및 Grok 제공자 라우팅 확인.
96
- - 실제 Grok CLI → Codex `exec_command` → 실행 결과 → Grok 최종 응답을 확인. 결과: `BRIDGE_TOOL_OK`.
97
- - 별도로 Codex 창에 `Grok 4.6 / xAI Extra High` 표시 확인.
98
- - 브리지는 상류로 보내는 `instructions` 끝에 출처 한 줄(`Transport: …`)을 붙입니다. Codex는 provider·model을 프롬프트에 넣지 않기 때문에, 이 줄이 없으면 "Grok 붙었는지"를 모델이 확인할 방법이 없어 설정 파일을 뒤지거나 답을 얼버무립니다. 대화 내용을 판단하는 것이 아니라 전송이 자기 출처를 밝히는 것이며, `user-agent` 헤더와 같은 범주입니다.
99
- - Codex `reasoning` 항목의 평문 요약은 상류로 전달합니다. 암호화된 `encrypted_content`와 Codex 자체 아이템 id는 제거합니다. 여러 번 호출이 이어지는 턴에서 모델이 자기 추론을 이어받게 하기 위한 것으로, 실제 상류가 이 형태를 수락하는지 확인했습니다.
100
- - 추론은 변환기당 **동시 4건**까지 실행하고, 그 이상은 큐(기본 8)에서 대기합니다. 큐까지 가득 찼을 때만 `429`를 냅니다. 대기가 거절보다 나은 이유는 브리지가 이미 응답 헤더를 보냈고 keepalive로 스트림을 살려 두기 때문입니다. 동시 1로 조이면 Codex `spawn_agent` 자식 추론이 부모 턴과 겹쳐 죽습니다.
101
- - 일반 함수 도구, namespace 함수 도구, freeform 커스텀 도구를 변환합니다. 파일 변경·MCP 등은 Codex노출한 도구 권한 범위에서 사용할 있지만, 개별 기능을 모두 실검증한 것은 아닙니다.
102
- - 이미지 첨부·인식을 지원합니다. PNG/JPEG/WebP, **이미지당 10 MiB, 요청 전체 20 MiB**, 서로 다른 이미지 4장까지, PNG는 32메가픽셀까지입니다. 한도는 추측이 아니라 실측입니다 — 상류가 12.5 MiB PNG를 받아 답하는 것을 확인하고 그 아래로 잡았습니다.
103
- - **쓸 수 없는 첨부 하나가 대화를 죽이지 않습니다.** 브리지는 대화 전체를 훑기 때문에, 예전에는 한도를 넘는 이미지가 히스토리에 한 번 들어가면 이후 모든 턴이 영구히 400으로 실패했습니다. 지금은 그런 첨부만 이유를 밝힌 텍스트로 바꾸고 나머지는 그대로 보냅니다. 쓸 수 있는 이미지는 `input_image` 그대로 넘어가며(Grok가 직접 읽습니다), 원격 URL은 가져오지 않습니다.
104
- - 음성, 클라우드 작업, 영상 생성은 아직 보장하지 않습니다.
105
- - 기본 Responses 경로는 스트림을 전달합니다. 프롬프트 캐시 키는 Codex `prompt_cache_key`를 `x-grok-conv-id`로 넘깁니다.
106
- - cli-chat-proxyfunction tool 339개 요청을 수락했습니다. 공개 API 문서의 200개 한도는 이 로그인 경로에 적용되지 않습니다.
107
- - 저장된 루트 작업이 대기 중이면 GPT Grok 전환을 지원합니다. `turn/start`와 설정 변경 API는 제공자를 바꾸지 못하므로, 래퍼가 구독 해제 → 같은 ID로 제공자를 지정해 재개 → 제공자·권한 확인 후 원래 요청을 전달합니다. 실행 중인 작업, 임시 작업, 하위 에이전트는 제공자 전환을 거부하며, 다른 구독자가 전환을 막으면 추론을 보내지 않습니다. 첫 턴이 저장되기 전에는 새 작업을 시작할 때 원하는 모델을 선택하세요.
108
- - **이미지 생성도 Grok이 합니다.** 브리지가 상류 요청에 `{ type: "image_generation" }`을 직접 선언하므로, Codex가 이미지 도구를 노출하지 않아도 Grok이 서버 쪽에서 생성합니다. 도구가 켜져 있으면 `instructions` 끝에 `Images:` 출처 한 줄을 붙여 Codex `imagegen` 스킬을 읽지 말라고 합니다. 돌아온 바이트는 `~/.local/share/codex-grok-bridge/generated-images/`에 0600으로 저장하고, Codex에는 파일 경로를 알려주는 assistant 메시지로 전달합니다. Codex가 모르는 `response.image_generation_call.*` 이벤트는 걸러냅니다. `GROK_BRIDGE_IMAGE_GEN=off`로 끄면 도구와 그 줄이 같이 빠집니다.
109
- - **한계도 실측했습니다.** Grok의 `image_generation`은 텍스트→이미지 생성만 제대로 됩니다.
110
-
111
- | 기능 | 결과 |
112
- |---|---|
113
- | 텍스트→이미지 생성 | |
114
- | 투명 배경 | **안 됨.** 항상 JPEG로 오고 알파 채널이 없습니다. 모델은 "투명 배경"이라고 말하면서 **체커보드를 그림에 칠해서** 보냅니다 |
115
- | 도구 파라미터(`background`, `output_format`) | 거부되지 않고 **조용히 무시**됩니다 |
116
- | 이미지 편집(image-to-image) | **진짜 편집이 아닙니다.** 입력 이미지를 텍스트로 묘사해 재생성하므로 구도·크기·해상도가 달라집니다 |
117
-
118
- 그래서 브리지는 저장한 파일의 실제 포맷을 확인해, 알파가 없으면 "이 포맷은 알파 채널이 없으니 투명하다고 설명하지 말라"고 모델에게 명시합니다. 투명 배경이나 정확한 인페인팅이 꼭 필요하면 Codex의 OpenAI 경로를 쓰는 편이 맞습니다.
119
- - 이게 없으면 Codex의 `imagegen` 시스템 스킬이 OpenAI 경로(내장 `image_gen` 또는 `OPENAI_API_KEY` + `gpt-image-*`)로 갑니다. 실제로 Codex 프로바이더에 보내는 263개 도구 이미지 생성 도구는 하나도 없습니다 — `view_image`뿐입니다. 즉 추론은 Grok인데 그림만 다른 벤더에서 나오는 상태가 됩니다.
120
- - CLI 폴백은 응답이 끝난 Codex에 결과를 전달하므로 토큰 단위 실시간 출력이 없습니다. 요청당 3분 제한입니다.
121
- - 상류가 응답 중간에 연결을 리셋하는 경우가 간헐적으로 있습니다(실측 3건: 25s/27s/253s, 726 KB–22 MB). 브리지는 이걸 재시도할 수 없습니다 — Codex가 이미 응답 일부를 받았으므로 재전송하면 중복됩니다. 대신 provider에 `stream_max_retries: 2`를 설정해 **Codex가 같은 요청을 다시 보내도록** 했습니다. 대화를 소유한 쪽만 안전하게 재시도할 수 있습니다.
122
- - 앱 업데이트가 `CODEX_CLI_PATH`나 app-server 프로토콜을 바꾸면 재검증이 필요합니다. 검증 버전: Codex 0.153.4 / 앱 26.901.51231, Grok CLI 1.0.24, Node 22.23.0.
445
+ 추측이 아니라 실측입니다.
446
+
447
+ - 실제 app-server에서 GPT 6개 모델과 `grok-4.6`의 혼합 목록, Grok 제공자 라우팅.
448
+ - 실제 Grok CLI Codex `exec_command` 결과 Grok 최종 응답
449
+ (`BRIDGE_TOOL_OK`).
450
+ - 별도 Codex 창에 `Grok 4.6 / xAI Extra High` 표시.
451
+ - cli-chat-proxyfunction tool 339개 요청을 수락. 공개 API 문서의 200개 한도는
452
+ 로그인 경로에 적용되지 않음.
453
+
454
+ ### 출처
455
+
456
+ Codexprovider·model을 프롬프트에 넣지 않습니다. 브리지는 `instructions` 끝에
457
+ `Transport:` 줄을 붙여, 모델이 설정 파일을 열지 않고도 Grok 붙었는가”에
458
+ 답하게 합니다. 대화 내용을 판단하는 것이 아니라 전송이 자기 출처를 밝히는
459
+ 것이며, `User-Agent` 헤더와 같은 범주입니다.
460
+
461
+ 이미지 생성이 켜져 있으면 `Images:` 줄도 붙습니다. 이 전송의 그림은 요청에 이미
462
+ 있는 Grok `image_generation` 도구로 만들고, Codex `imagegen` 스킬을 읽거나
463
+ OpenAI로 보내지 말라는 뜻입니다. `GROK_BRIDGE_IMAGE_GEN=off`면 도구와 그 줄이
464
+ 같이 빠집니다.
465
+
466
+ ### reasoning
467
+
468
+ Codex `reasoning` 항목의 평문 요약은 상류로 전달합니다. 암호화된
469
+ `encrypted_content`와 Codex 자체 아이템 id는 제거합니다. 여러 호출이 이어지는
470
+ 턴에서 모델이 자기 추론을 이어받게 하기 위한 것이며, 상류가 형태를 수락하는
471
+ 것을 확인했습니다.
472
+
473
+ ### 동시성
474
+
475
+ 변환기당 추론은 **동시 4건**, 나머지는 큐(기본 8)에서 대기합니다. 큐까지 가득
476
+ 찼을 때만 `429`입니다. 브리지가 이미 응답 헤더를 보냈고 keepalive로 스트림을
477
+ 살려 두기 때문에 대기가 거절보다 낫습니다. 동시 1로 조이면 Codex `spawn_agent`
478
+ 자식 추론이 부모 턴과 겹쳐 죽습니다.
479
+
480
+ ### 도구
481
+
482
+ 일반 함수 도구, namespace 함수 도구, freeform 커스텀 도구를 변환합니다. 파일
483
+ 변경·MCP 등은 Codex가 노출한 도구와 사용자가 정한 승인 정책 안에서 동작합니다.
484
+ 개별 기능을 모두 실검증한 것은 아닙니다.
485
+
486
+ ### 이미지 첨부 (인식)
487
+
488
+ PNG / JPEG / WebP. **이미지당 10 MiB, 요청 전체 20 MiB**, 서로 다른 이미지
489
+ 4장까지, PNG는 32메가픽셀까지. 한도는 실측입니다 — 상류가 12.5 MiB PNG를 받아
490
+ 답하는 것을 확인하고 그 아래로 잡았습니다.
491
+
492
+ 쓸 수 없는 첨부 하나가 대화를 죽이지 않습니다. 브리지는 대화 전체를 훑기
493
+ 때문에, 예전에는 한도를 넘는 이미지가 히스토리에 한 번 들어가면 이후 모든 턴이
494
+ 영구히 400이었습니다. 지금은 그 첨부만 이유를 밝힌 텍스트로 바꾸고 나머지는
495
+ 그대로 보냅니다. 쓸 수 있는 이미지는 `input_image` 그대로 넘어가며 Grok가 직접
496
+ 읽습니다. 원격 URL은 가져오지 않습니다.
497
+
498
+ ### 이미지 생성
499
+
500
+ 브리지가 상류 요청에 `{ type: "image_generation" }`을 직접 선언합니다. Codex는
501
+ 이 프로바이더에 이미지 생성 도구를 주지 않습니다(263개 중 없음, `view_image`
502
+ 뿐). 선언이 없으면 Codex `imagegen` 스킬이 OpenAI 경로(`image_gen` 또는
503
+ `OPENAI_API_KEY` + `gpt-image-*`)로 가서, 추론은 Grok인데 그림만 다른 벤더가
504
+ 그립니다.
505
+
506
+ 돌아온 바이트는 `~/.local/share/codex-grok-bridge/generated-images/`에 `0600`으로
507
+ 저장합니다. Codex는 그 바이트를 둘 곳이 없어서, 브리지는 경로를 마크다운
508
+ `file://` 링크로 넣은 assistant 메시지로 바꿉니다.
509
+
510
+ `[ /path/to/grok-….jpg ](file:///path/to/grok-….jpg)`
511
+
512
+ 클릭 여부는 Codex 마크다운 렌더러에 달립니다. Codex가 모르는
513
+ `response.image_generation_call.*` 이벤트는 걸러냅니다.
514
+
515
+ Grok의 `image_generation`은 텍스트→이미지만 제대로 됩니다.
516
+
517
+ | 기능 | 결과 |
518
+ |---|---|
519
+ | 텍스트→이미지 | 됨 |
520
+ | 투명 배경 | **안 됨.** 항상 JPEG, 알파 없음. 모델은 “투명”이라고 말하면서 체커보드를 그림에 칠함 |
521
+ | 도구 파라미터(`background`, `output_format`) | 받아 주고 **조용히 무시** |
522
+ | 이미지 편집(image-to-image) | **진짜 편집이 아님.** 입력을 텍스트로 묘사해 재생성하므로 구도·해상도가 바뀜 |
523
+
524
+ 브리지는 저장한 파일의 포맷을 확인합니다. 알파가 없으면 모델에게 투명하다고
525
+ 설명하지 말라고 적습니다. 진짜 알파나 정확한 인페인팅이 필요하면 Codex의
526
+ OpenAI 경로가 맞습니다.
527
+
528
+ ### GPT ↔ Grok 전환
529
+
530
+ 대기 중인 저장된 루트 작업에서만 됩니다. `turn/start`,
531
+ `thread/settings/update`, `turn/settings/update`는 `modelProvider`를 바꾸지
532
+ 못하고, 추가 provider 필드는 무시됩니다. 래퍼가 구독 해제 → 같은 ID로
533
+ 모델/제공자를 지정해 재개 → 돌아온 제공자·권한을 확인한 뒤 원래 요청을
534
+ 전달합니다. 실행 중인 작업, 임시 작업, 하위 에이전트는 전환을 거부합니다. 다른
535
+ 구독자가 재로드를 막으면 추론을 보내지 않습니다. 첫 턴이 저장되기 전에 새
536
+ 작업을 시작할 때 원하는 모델을 고르세요.
537
+
538
+ ### 아직 보장하지 않는 것
539
+
540
+ 음성, 클라우드 작업, 영상 생성은 약속하지 않습니다.
541
+
542
+ 상류가 응답 중간에 연결을 리셋하는 경우가 있습니다(실측 3건: 25초 / 27초 /
543
+ 253초, 726 KB–22 MB). 첫 SSE 바이트 이후에는 브리지가 재시도할 수 없습니다.
544
+ provider에 Codex `request_max_retries` / `stream_max_retries`를 2로 두어
545
+ **Codex**가 같은 요청을 다시 보내게 했습니다. 대화를 소유한 쪽만 안전하게
546
+ 재시도할 수 있습니다.
123
547
 
124
548
  ## 진단 로그
125
549
 
126
- 브리지는 매 턴을 `~/.local/share/codex-grok-bridge/logs/bridge.jsonl`에 한 줄씩 기록합니다(디렉터리 0700, 파일 0600, 4 MiB 초과 시 `.1`로 1회 회전). 끄려면 `GROK_BRIDGE_DIAGNOSTICS=off`.
550
+ 매 턴을 `~/.local/share/codex-grok-bridge/logs/bridge.jsonl`에 한 줄씩 기록합니다
551
+ (디렉터리 `0700`, 파일 `0600`, 4 MiB 초과 시 `.1`로 1회 회전).
552
+ `GROK_BRIDGE_DIAGNOSTICS=off`로 끕니다.
127
553
 
128
554
  ```jsonl
129
555
  {"at":"…","event":"turn_ok","mode":"proxy","elapsedMs":14118,"requestBytes":469749,"items":4,"tools":29}
130
556
  {"at":"…","event":"turn_failed","kind":"dns","signature":"TypeError <- Error[EAI_AGAIN]","elapsedMs":15071,…}
131
557
  ```
132
558
 
133
- 구조적 사실만 남깁니다 — 시각, 성공/실패, 오류 분류, 오류 체인의 이름·코드, 경과 시간, 요청 바이트, 아이템·도구 개수. **프롬프트 본문, 도구 출력, 상류 응답 본문, 토큰은 기록하지 않습니다.** `detail`에 들어가는 오류 메시지는 Bearer 토큰·JWT·API 키·홈 경로를 치환한 뒤 400자로 자릅니다.
559
+ 구조적 사실만 남깁니다 — 시각, 성공/실패, 오류 분류, 오류 체인의 이름·코드,
560
+ 경과 시간, 요청 바이트, 아이템·도구 개수. **프롬프트 본문, 도구 출력, 상류 응답
561
+ 본문, 토큰은 기록하지 않습니다.** `detail`은 Bearer 토큰·JWT·API 키·홈 경로를
562
+ 지운 뒤 400자로 자릅니다.
563
+
564
+ 성공 턴도 남깁니다. 실패했는데 로그가 비어 있으면 브리지가 호출되지 않은
565
+ 것입니다.
566
+
567
+ `turn_failed.kind`는 다음 중 하나입니다 — `aborted`, `auth`, `dns`, `connect`,
568
+ `upstream_timeout`, `upstream_closed`, `upstream_protocol`, `payload`,
569
+ `internal`. Codex UI에는 같은 분류가 `bridge_<kind>`로 보입니다.
570
+
571
+ 문제가 있으면 여기부터 봅니다. 브리지를 “확인”하려고 `~/.grok/auth.json`이나
572
+ `~/.codex/auth.json`을 열지 마세요.
134
573
 
135
- 성공 턴도 남기는 이유는, 실패 시 로그가 비어 있다는 사실 자체가 "브리지가 호출되지도 않았다"는 진단이 되기 때문입니다.
574
+ ## 환경 변수
136
575
 
137
- `event: "turn_failed"`의 `kind`는 다음 중 하나입니다 — `aborted`(사용자가 중단), `auth`(로그인 만료), `dns`, `connect`, `upstream_timeout`, `upstream_closed`, `upstream_protocol`, `payload`, `internal`. Codex UI에는 같은 분류가 `bridge_<kind>` 코드와 함께 표시됩니다.
576
+ | 변수 | 효과 |
577
+ |---|---|
578
+ | `GROK_BRIDGE_IMAGE_GEN=off` | Grok `image_generation`을 선언하지 않고 `Images:` 줄도 붙이지 않음 |
579
+ | `GROK_BRIDGE_TRANSPORT=fetch` | `node:http(s)` 대신 Node `fetch` |
580
+ | `GROK_BRIDGE_INFERENCE=cli` | CLI 봉투 경로로 폴백 |
581
+ | `GROK_BRIDGE_DIAGNOSTICS=off` | JSONL 로그를 쓰지 않음 |
582
+ | `NODE` | `.command` / 데스크톱 스크립트가 쓸 `node` 절대 경로 |
583
+ | `CODEX_GROK_APP` | `install-codex-grok-app.sh`가 쓸 앱 번들 경로 (기본 `/Applications/Codex Grok.app`) |
138
584
 
139
585
  ## 검증
140
586
 
141
587
  ```sh
142
- npm test # 131건, 외부 추론 없음
588
+ npm test # 132건, 외부 추론 없음
143
589
  npm run test:coverage # line/branch/function 80% 게이트
144
- npm run verify:app-server # 실제 app-server 라우팅. 설치된 앱 번들에서도 실행됩니다
590
+ npm run verify:app-server # 실제 app-server 라우팅. 설치된 앱 번들에서도 실행
145
591
  npm audit --omit=dev
146
592
  ```
147
593
 
148
- ## 설치와 갱신
594
+ `npm test`는 외부 추론을 호출하지 않습니다. `verify:app-server`는 실제
595
+ app-server에 모델 목록과 임시 작업을 요청하며, 모델 추론은 하지 않습니다. 실제
596
+ CLI 검증은 사용자 계정 사용량을 씁니다.
597
+
598
+ ## 데스크톱 설치와 갱신
149
599
 
150
600
  ```sh
151
601
  sh scripts/install-codex-grok-app.sh # 브리지 JS만 동기화 (기본)
152
- sh scripts/install-codex-grok-app.sh --full # 런처 applet 재빌드 + 서명까지
602
+ sh scripts/install-codex-grok-app.sh --full # 런처 applet 재빌드 + 서명
153
603
  ```
154
604
 
155
- 기본 동작은 번들의 `src/`·`scripts/`를 저장소와 일치시키는 것입니다. 디렉터리를 지우지 않고, 저장소에 없는 파일만 골라서 제거하며, 끝에 내용이 일치하는지 확인합니다. Codex Grok 창이 열려 있으면 거부합니다(`--force`로 무시). ESM은 핫리로드되지 않으므로 **동기화 후 창을 다시 열어야** 새 코드가 적용됩니다.
605
+ 기본 동작은 체크아웃의 `src/`와 `scripts/*.mjs`를 번들에 복사하고, 저장소에
606
+ 없는 파일만 고른 뒤, 끝에서 모든 파일을 `cmp`합니다. 살아있는 번들을
607
+ `rm -rf`하지 않습니다. Codex Grok 창이 열려 있으면 거부합니다(`--force`로
608
+ 무시). ESM은 핫리로드되지 않으므로 **동기화 후 창을 다시 열어야** 합니다.
609
+
610
+ 이 스크립트는 이미 있는 `Codex Grok.app`을 갱신합니다. 번들을 새로 만들지
611
+ 않고, `/Applications/Codex.app`은 건드리지 않습니다.
612
+
613
+ ## 보안
614
+
615
+ 브리지는 loopback에만 붙습니다. 추론 요청마다 실행 단위 임시 토큰이 필요합니다.
616
+ 브라우저 `Origin` 요청, 지원하지 않는 모델, 과대 본문, 알 수 없는 도구 호출은
617
+ 거절합니다. Grok 프롬프트 임시 파일은 `0600`으로 만들고 종료 후 지웁니다. CLI
618
+ 오류 원문과 인증 정보는 응답에 넣지 않습니다. Codex와 Grok 자체의 보관 정책은
619
+ 그대로입니다.
156
620
 
157
- 테스트는 외부 추론을 호출하지 않습니다. `verify:app-server`는 실제 app-server에 모델 목록과 임시 작업 생성을 요청하며, 모델 추론은 하지 않습니다. 실제 CLI 검증은 사용자 계정 사용량을 소비합니다.
621
+ ## 깊은 문서
158
622
 
159
- 변환기는 loopback에만 바인딩하며, 추론 요청에 실행마다 생성한 임시 토큰을 요구합니다. 브라우저 Origin 요청, 지원하지 않는 모델, 과대 요청, 알 수 없는 도구 호출은 거부합니다. Grok 프롬프트 임시 파일은 0600 권한으로 생성하고 종료 후 제거합니다. CLI 오류 원문과 인증 정보는 응답에 포함하지 않습니다. Codex와 Grok 자체의 대화 저장 정책은 그대로 적용됩니다.
623
+ - `docs/HANDOFF.md` 현재 상태, 함정, 남은 항목
624
+ - `docs/solution-20260909.md` — 15초 고정 실패를 추적한 기록과 측정
625
+ - `docs/experiments/` — 그 측정을 재현하는 스크립트
160
626
 
161
- 중지하려면 확장으로 연 Codex 창을 종료하세요. 일반 Codex는 기존 아이콘으로 실행하면 됩니다. 자동 시작 서비스나 전역 환경변수는 등록하지 않았습니다.
627
+ ## 참고
162
628
 
163
- 참고: [Grok Build headless scripting](https://docs.x.ai/build/cli/headless-scripting), [Grok Build source](https://github.com/xai-org/grok-build), [Codex source](https://github.com/openai/codex).
629
+ - [Grok Build headless scripting](https://docs.x.ai/build/cli/headless-scripting)
630
+ - [Grok Build source](https://github.com/xai-org/grok-build)
631
+ - [Codex source](https://github.com/openai/codex)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-grok-bridge",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Run Grok 4.6 as the model inside Codex, with Codex still owning tools, permissions, history and MCP. Uses the grok login session, not an API key.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/imagegen.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
4
5
 
5
6
  // Codex offers no image generation tool to this provider — 263 tools arrive and
6
7
  // none of them generates an image — so its imagegen skill falls back to the
@@ -80,9 +81,12 @@ export function describeGeneratedImage(item, saved) {
80
81
  ? " It has an alpha channel."
81
82
  : ` This format carries no alpha channel, so the background is opaque —` +
82
83
  ` do not describe it as transparent or cut out, even if it looks that way.`;
84
+ // Codex renders assistant markdown. A file:// link is the only way to make
85
+ // the path clickable; wrapping the whole note in [] would swallow that link.
86
+ const linked = `[${saved.file}](${pathToFileURL(saved.file).href})`;
83
87
  return (
84
- `[Image generated by Grok and saved to ${saved.file} ` +
88
+ `Image generated by Grok and saved to ${linked} ` +
85
89
  `(${saved.extension.toUpperCase()}, ${kb} KB).${alpha}${prompt} ` +
86
- `Move or copy it into the workspace if the user wants it there, and tell them the path.]`
90
+ `Move or copy it into the workspace if the user wants it there, and tell them the path.`
87
91
  );
88
92
  }