codex-grok-bridge 1.0.0 → 1.0.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/README.md +4 -3
- package/package.json +1 -1
- package/src/runtime.mjs +8 -2
- package/src/tools.mjs +17 -3
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ From a checkout:
|
|
|
27
27
|
```sh
|
|
28
28
|
git clone https://github.com/deximple/codex-grok-bridge.git
|
|
29
29
|
cd codex-grok-bridge
|
|
30
|
-
npm test #
|
|
30
|
+
npm test # 131 tests, no network, no inference
|
|
31
31
|
node scripts/codex-grok.mjs
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -105,7 +105,7 @@ Codex에 첫 SSE 블록을 쓰기 **전**에 죽은 요청은 한 번 다시 보
|
|
|
105
105
|
- 기본 Responses 경로는 스트림을 전달합니다. 프롬프트 캐시 키는 Codex `prompt_cache_key`를 `x-grok-conv-id`로 넘깁니다.
|
|
106
106
|
- cli-chat-proxy는 function tool 339개 요청을 수락했습니다. 공개 API 문서의 200개 한도는 이 로그인 경로에 적용되지 않습니다.
|
|
107
107
|
- 저장된 루트 작업이 대기 중이면 GPT ↔ Grok 전환을 지원합니다. `turn/start`와 설정 변경 API는 제공자를 바꾸지 못하므로, 래퍼가 구독 해제 → 같은 ID로 제공자를 지정해 재개 → 제공자·권한 확인 후 원래 요청을 전달합니다. 실행 중인 작업, 임시 작업, 하위 에이전트는 제공자 전환을 거부하며, 다른 구독자가 전환을 막으면 추론을 보내지 않습니다. 첫 턴이 저장되기 전에는 새 작업을 시작할 때 원하는 모델을 선택하세요.
|
|
108
|
-
- **이미지 생성도 Grok이 합니다.** 브리지가 상류 요청에 `{ type: "image_generation" }`을 직접 선언하므로, Codex가 이미지 도구를 노출하지 않아도 Grok이 서버 쪽에서 생성합니다. 돌아온 바이트는 `~/.local/share/codex-grok-bridge/generated-images/`에 0600으로 저장하고, Codex에는 파일 경로를 알려주는 assistant 메시지로 전달합니다. Codex가 모르는 `response.image_generation_call.*` 이벤트는 걸러냅니다. `GROK_BRIDGE_IMAGE_GEN=off`로
|
|
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
109
|
- **한계도 실측했습니다.** Grok의 `image_generation`은 텍스트→이미지 생성만 제대로 됩니다.
|
|
110
110
|
|
|
111
111
|
| 기능 | 결과 |
|
|
@@ -118,6 +118,7 @@ Codex에 첫 SSE 블록을 쓰기 **전**에 죽은 요청은 한 번 다시 보
|
|
|
118
118
|
그래서 브리지는 저장한 파일의 실제 포맷을 확인해, 알파가 없으면 "이 포맷은 알파 채널이 없으니 투명하다고 설명하지 말라"고 모델에게 명시합니다. 투명 배경이나 정확한 인페인팅이 꼭 필요하면 Codex의 OpenAI 경로를 쓰는 편이 맞습니다.
|
|
119
119
|
- 이게 없으면 Codex의 `imagegen` 시스템 스킬이 OpenAI 경로(내장 `image_gen` 또는 `OPENAI_API_KEY` + `gpt-image-*`)로 갑니다. 실제로 Codex가 이 프로바이더에 보내는 263개 도구 중 이미지 생성 도구는 하나도 없습니다 — `view_image`뿐입니다. 즉 추론은 Grok인데 그림만 다른 벤더에서 나오는 상태가 됩니다.
|
|
120
120
|
- CLI 폴백은 응답이 끝난 뒤 Codex에 결과를 전달하므로 토큰 단위 실시간 출력이 없습니다. 요청당 3분 제한입니다.
|
|
121
|
+
- 상류가 응답 중간에 연결을 리셋하는 경우가 간헐적으로 있습니다(실측 3건: 25s/27s/253s, 726 KB–22 MB). 브리지는 이걸 재시도할 수 없습니다 — Codex가 이미 응답 일부를 받았으므로 재전송하면 중복됩니다. 대신 provider에 `stream_max_retries: 2`를 설정해 **Codex가 같은 요청을 다시 보내도록** 했습니다. 대화를 소유한 쪽만 안전하게 재시도할 수 있습니다.
|
|
121
122
|
- 앱 업데이트가 `CODEX_CLI_PATH`나 app-server 프로토콜을 바꾸면 재검증이 필요합니다. 검증 버전: Codex 0.153.4 / 앱 26.901.51231, Grok CLI 1.0.24, Node 22.23.0.
|
|
122
123
|
|
|
123
124
|
## 진단 로그
|
|
@@ -138,7 +139,7 @@ Codex에 첫 SSE 블록을 쓰기 **전**에 죽은 요청은 한 번 다시 보
|
|
|
138
139
|
## 검증
|
|
139
140
|
|
|
140
141
|
```sh
|
|
141
|
-
npm test #
|
|
142
|
+
npm test # 131건, 외부 추론 없음
|
|
142
143
|
npm run test:coverage # line/branch/function 80% 게이트
|
|
143
144
|
npm run verify:app-server # 실제 app-server 라우팅. 설치된 앱 번들에서도 실행됩니다
|
|
144
145
|
npm audit --omit=dev
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-grok-bridge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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/runtime.mjs
CHANGED
|
@@ -28,8 +28,14 @@ export async function startRuntime(options = {}) {
|
|
|
28
28
|
env_key: "CODEX_GROK_BRIDGE_TOKEN",
|
|
29
29
|
wire_api: "responses",
|
|
30
30
|
requires_openai_auth: false,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
// The upstream resets a response mid-stream now and then — measured three
|
|
32
|
+
// times as ECONNRESET at 25s, 27s and 253s, on requests from 726 KB to
|
|
33
|
+
// 22 MB. The bridge cannot retry that: Codex has already received part of
|
|
34
|
+
// the answer, so re-sending would duplicate it. Codex can, because it owns
|
|
35
|
+
// the conversation and re-sends the same request. These were both 0, which
|
|
36
|
+
// turned every such reset into a dead turn.
|
|
37
|
+
request_max_retries: 2,
|
|
38
|
+
stream_max_retries: 2,
|
|
33
39
|
// Do not inherit whatever Codex's default happens to be across upgrades.
|
|
34
40
|
stream_idle_timeout_ms: 300000,
|
|
35
41
|
};
|
package/src/tools.mjs
CHANGED
|
@@ -290,6 +290,16 @@ export const TRANSPORT_PROVENANCE =
|
|
|
290
290
|
"and permissions. If asked whether Grok is attached to the Codex harness, " +
|
|
291
291
|
"this line is the authoritative answer and no tool call is needed to confirm it.";
|
|
292
292
|
|
|
293
|
+
// Codex still injects its imagegen skill. That skill's fallback is OpenAI
|
|
294
|
+
// (`image_gen` or a Python CLI). The model will read the skill and send the
|
|
295
|
+
// picture to another vendor unless this line says not to. Grok already has
|
|
296
|
+
// image_generation on the request; no Codex tool call is required to pick it.
|
|
297
|
+
export const IMAGE_GENERATION_PROVENANCE =
|
|
298
|
+
"Images: pictures on this transport are generated by Grok's image_generation " +
|
|
299
|
+
"tool, which is already on the request. Do not read Codex's imagegen skill " +
|
|
300
|
+
"and do not use OpenAI, image_gen, or a Python image CLI — those send the " +
|
|
301
|
+
"picture to another vendor.";
|
|
302
|
+
|
|
293
303
|
export function toProxyRequest(body) {
|
|
294
304
|
const { tools, map } = flattenCodexTools(body.tools ?? []);
|
|
295
305
|
const effort = EFFORT[body.reasoning?.effort] ?? "high";
|
|
@@ -307,16 +317,20 @@ export function toProxyRequest(body) {
|
|
|
307
317
|
const declaresImageTool = request.tools.some(
|
|
308
318
|
(tool) => tool?.type === GROK_IMAGE_TOOL.type,
|
|
309
319
|
);
|
|
310
|
-
|
|
320
|
+
const imageGenOn = process.env.GROK_BRIDGE_IMAGE_GEN !== "off";
|
|
321
|
+
if (imageGenOn && !declaresImageTool)
|
|
311
322
|
request.tools = [...request.tools, GROK_IMAGE_TOOL];
|
|
312
323
|
if (tools.length) {
|
|
313
324
|
request.tool_choice = proxyToolChoice(body.tool_choice, map);
|
|
314
325
|
request.parallel_tool_calls = body.parallel_tool_calls !== false;
|
|
315
326
|
}
|
|
327
|
+
const provenance = imageGenOn
|
|
328
|
+
? `${TRANSPORT_PROVENANCE}\n\n${IMAGE_GENERATION_PROVENANCE}`
|
|
329
|
+
: TRANSPORT_PROVENANCE;
|
|
316
330
|
request.instructions =
|
|
317
331
|
typeof body.instructions === "string" && body.instructions
|
|
318
|
-
? `${body.instructions}\n\n${
|
|
319
|
-
:
|
|
332
|
+
? `${body.instructions}\n\n${provenance}`
|
|
333
|
+
: provenance;
|
|
320
334
|
if (typeof body.prompt_cache_key === "string" && body.prompt_cache_key)
|
|
321
335
|
request.prompt_cache_key = body.prompt_cache_key;
|
|
322
336
|
return { request, map };
|