@heungtae/codex-chat-bridge 0.1.0
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/Cargo.lock +2114 -0
- package/Cargo.toml +25 -0
- package/README.md +87 -0
- package/USAGE.md +374 -0
- package/bin/codex-chat-bridge.js +34 -0
- package/conf.toml +10 -0
- package/package.json +32 -0
- package/scripts/postinstall-build.cjs +21 -0
- package/scripts/run_codex_with_bridge.sh +47 -0
- package/src/main.rs +1172 -0
package/Cargo.toml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "codex-chat-bridge"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
|
|
7
|
+
[[bin]]
|
|
8
|
+
name = "codex-chat-bridge"
|
|
9
|
+
path = "src/main.rs"
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
anyhow = "1"
|
|
13
|
+
async-stream = "0.3.6"
|
|
14
|
+
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio"] }
|
|
15
|
+
bytes = "1"
|
|
16
|
+
clap = { version = "4", features = ["derive"] }
|
|
17
|
+
futures = { version = "0.3", default-features = false }
|
|
18
|
+
reqwest = { version = "0.12", features = ["stream", "json", "rustls-tls"], default-features = false }
|
|
19
|
+
serde = { version = "1", features = ["derive"] }
|
|
20
|
+
serde_json = "1"
|
|
21
|
+
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "signal"] }
|
|
22
|
+
toml = "0.8"
|
|
23
|
+
tracing = "0.1"
|
|
24
|
+
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
|
25
|
+
uuid = { version = "1", features = ["v7"] }
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# codex-chat-bridge
|
|
2
|
+
|
|
3
|
+
`codex-chat-bridge` lets Codex keep using the Responses wire API while forwarding requests to an OpenAI-compatible `/v1/chat/completions` upstream.
|
|
4
|
+
|
|
5
|
+
This is intended for "no core source change" integration: run this bridge locally, then override `model_provider` to point Codex at the bridge.
|
|
6
|
+
|
|
7
|
+
Detailed guide: `USAGE.md`
|
|
8
|
+
|
|
9
|
+
## Install with npm
|
|
10
|
+
|
|
11
|
+
Node.js 20+ and Rust/Cargo are required because npm installation compiles the Rust binary locally.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @heungtae/codex-chat-bridge
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Private registry publish/install is also supported:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm publish --registry <private-registry> --access restricted
|
|
21
|
+
npm install @heungtae/codex-chat-bridge --registry <private-registry>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## What it does
|
|
25
|
+
|
|
26
|
+
- Accepts `POST /v1/responses`
|
|
27
|
+
- Translates request payload into `POST /v1/chat/completions`
|
|
28
|
+
- Streams upstream Chat Completions chunks back as Responses-style SSE events:
|
|
29
|
+
- `response.created`
|
|
30
|
+
- `response.output_item.added` (assistant message start)
|
|
31
|
+
- `response.output_text.delta`
|
|
32
|
+
- `response.output_item.done` (assistant message and function calls)
|
|
33
|
+
- `response.completed`
|
|
34
|
+
- `response.failed` (for upstream/network errors)
|
|
35
|
+
|
|
36
|
+
## Run
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx @heungtae/codex-chat-bridge --port 8787 --api-key-env OPENAI_API_KEY
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
By default, the bridge also reads `conf.toml` in the current directory.
|
|
43
|
+
CLI flags override file values.
|
|
44
|
+
|
|
45
|
+
Or run the binary directly via Cargo:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
cargo run --bin codex-chat-bridge -- --port 8787 --api-key-env OPENAI_API_KEY
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use a custom config file path:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @heungtae/codex-chat-bridge --config /path/to/conf.toml
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Codex override example
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codex exec \
|
|
61
|
+
-c 'model_providers.chat-bridge={name="Chat Bridge",base_url="http://127.0.0.1:8787/v1",env_key="OPENAI_API_KEY",wire_api="responses"}' \
|
|
62
|
+
-c 'model_provider="chat-bridge"' \
|
|
63
|
+
'Say hello and call tools when needed.'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
You can also set `OPENAI_BASE_URL=http://127.0.0.1:8787/v1` and keep `model_provider=openai`.
|
|
67
|
+
|
|
68
|
+
## Wrapper script
|
|
69
|
+
|
|
70
|
+
Use `scripts/run_codex_with_bridge.sh` to run the bridge and `codex exec` together:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
scripts/run_codex_with_bridge.sh "Summarize this repo."
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Package Scripts
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run build:bridge
|
|
80
|
+
npm run pack:check
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Endpoints
|
|
84
|
+
|
|
85
|
+
- `POST /v1/responses`
|
|
86
|
+
- `GET /healthz`
|
|
87
|
+
- `GET /shutdown` (only when `--http-shutdown` is enabled)
|
package/USAGE.md
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# codex-chat-bridge 사용 및 설정 가이드
|
|
2
|
+
|
|
3
|
+
## 0. npm 배포/설치 빠른 시작
|
|
4
|
+
|
|
5
|
+
패키지명: `@heungtae/codex-chat-bridge`
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @heungtae/codex-chat-bridge
|
|
9
|
+
npx @heungtae/codex-chat-bridge --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
private registry 배포:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm publish --registry <private-registry> --access restricted
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 1. 개요
|
|
19
|
+
|
|
20
|
+
`codex-chat-bridge`는 Codex가 내부적으로 `responses` API를 쓰는 구조를 유지하면서, 실제 업스트림 호출은 `chat/completions`로 보내도록 중간 변환해주는 로컬 브리지입니다.
|
|
21
|
+
|
|
22
|
+
핵심 동작:
|
|
23
|
+
- Codex -> `POST /v1/responses` (브리지)
|
|
24
|
+
- 브리지 -> `POST /v1/chat/completions` (업스트림)
|
|
25
|
+
- 브리지 -> Responses 스타일 SSE 이벤트로 재변환 후 Codex에 전달
|
|
26
|
+
|
|
27
|
+
## 2. 언제 필요한가
|
|
28
|
+
|
|
29
|
+
다음 상황에서 사용합니다.
|
|
30
|
+
|
|
31
|
+
- 현재 환경/벤더가 `responses`보다 `chat/completions` 호환성이 더 좋을 때
|
|
32
|
+
- Codex 본체 소스 수정 없이 연결 방식을 바꾸고 싶을 때
|
|
33
|
+
- 기존 Codex 워크플로(도구 호출 포함)를 유지하고 싶을 때
|
|
34
|
+
|
|
35
|
+
## 3. 사전 준비
|
|
36
|
+
|
|
37
|
+
필수:
|
|
38
|
+
- Node.js 20+
|
|
39
|
+
- npm
|
|
40
|
+
- Rust/Cargo 설치
|
|
41
|
+
- `OPENAI_API_KEY`(또는 원하는 이름의 API 키 환경변수)
|
|
42
|
+
- Codex CLI 사용 가능 상태
|
|
43
|
+
|
|
44
|
+
확인:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm --version
|
|
48
|
+
cargo --version
|
|
49
|
+
codex --version
|
|
50
|
+
echo "${OPENAI_API_KEY:+set}"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 4. 브리지 실행
|
|
54
|
+
|
|
55
|
+
프로젝트 루트(`chat-bridge`) 기준:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx @heungtae/codex-chat-bridge -- \
|
|
59
|
+
--port 8787 \
|
|
60
|
+
--api-key-env OPENAI_API_KEY
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
기본적으로 현재 디렉터리의 `conf.toml`을 자동으로 읽습니다.
|
|
64
|
+
우선순위는 `CLI 옵션 > conf.toml > 내장 기본값`입니다.
|
|
65
|
+
|
|
66
|
+
또는 Cargo 직접 실행:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cargo run --bin codex-chat-bridge -- \
|
|
70
|
+
--port 8787 \
|
|
71
|
+
--api-key-env OPENAI_API_KEY
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
다른 설정 파일을 쓰려면:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx @heungtae/codex-chat-bridge --config /path/to/conf.toml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
옵션 설명:
|
|
81
|
+
- `--config <FILE>`: 설정 파일 경로 (기본값: `conf.toml`)
|
|
82
|
+
- `--port`: 브리지 포트 (기본: 랜덤 포트)
|
|
83
|
+
- `--api-key-env`: 업스트림 호출에 쓸 API 키 환경변수 이름
|
|
84
|
+
- `--upstream-url`: 기본값 `https://api.openai.com/v1/chat/completions`
|
|
85
|
+
- `--server-info <FILE>`: 시작 시 포트/프로세스 정보 JSON 저장
|
|
86
|
+
- `--http-shutdown`: `GET /shutdown` 허용
|
|
87
|
+
|
|
88
|
+
헬스체크:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
curl --fail --silent --show-error http://127.0.0.1:8787/healthz
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 5. Codex를 브리지로 연결 (권장: 세션 오버라이드)
|
|
95
|
+
|
|
96
|
+
`exec` 예시:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
codex exec \
|
|
100
|
+
-c 'model_providers.chat-bridge={name="Chat Bridge",base_url="http://127.0.0.1:8787/v1",env_key="OPENAI_API_KEY",wire_api="responses"}' \
|
|
101
|
+
-c 'model_provider="chat-bridge"' \
|
|
102
|
+
'이 코드베이스에서 TODO를 찾아 수정해줘'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
대화형(`codex`) 실행 시도 동일하게 가능:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
codex \
|
|
109
|
+
-c 'model_providers.chat-bridge={name="Chat Bridge",base_url="http://127.0.0.1:8787/v1",env_key="OPENAI_API_KEY",wire_api="responses"}' \
|
|
110
|
+
-c 'model_provider="chat-bridge"'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
중요:
|
|
114
|
+
- 브리지를 켜두기만 하면 자동 전환되지 않습니다.
|
|
115
|
+
- 반드시 `model_provider`가 브리지를 가리켜야 합니다.
|
|
116
|
+
|
|
117
|
+
## 6. 대안 연결 방식
|
|
118
|
+
|
|
119
|
+
### 6.1 `OPENAI_BASE_URL` 방식
|
|
120
|
+
|
|
121
|
+
기존 `openai` provider를 그대로 쓰고 base URL만 브리지로 바꾸는 방식입니다.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
export OPENAI_BASE_URL=http://127.0.0.1:8787/v1
|
|
125
|
+
codex exec '간단한 테스트를 해줘'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 6.2 래퍼 스크립트 사용
|
|
129
|
+
|
|
130
|
+
브리지 기동/종료 + Codex 실행을 한 번에 처리:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
scripts/run_codex_with_bridge.sh "이 저장소 구조를 설명해줘"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
추가 인자 전달:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
scripts/run_codex_with_bridge.sh \
|
|
140
|
+
"테스트 코드 생성해줘" \
|
|
141
|
+
--model gpt-4.1
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 6.3 npm 스크립트 사용
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm run build:bridge
|
|
148
|
+
npm run pack:check
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 7. 영구 설정 (선택)
|
|
152
|
+
|
|
153
|
+
반복 사용 시 `~/.codex/config.toml`에 provider를 추가할 수 있습니다.
|
|
154
|
+
|
|
155
|
+
```toml
|
|
156
|
+
[model_providers.chat-bridge]
|
|
157
|
+
name = "Chat Bridge"
|
|
158
|
+
base_url = "http://127.0.0.1:8787/v1"
|
|
159
|
+
env_key = "OPENAI_API_KEY"
|
|
160
|
+
wire_api = "responses"
|
|
161
|
+
|
|
162
|
+
model_provider = "chat-bridge"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
주의:
|
|
166
|
+
- Codex는 여전히 `responses` wire를 사용합니다.
|
|
167
|
+
- `wire_api = "chat"`는 지원되지 않습니다.
|
|
168
|
+
|
|
169
|
+
## 8. 동작 검증 방법
|
|
170
|
+
|
|
171
|
+
## 8.1 브리지가 받는 요청 확인
|
|
172
|
+
|
|
173
|
+
브리지 로그를 터미널에서 보고, Codex 요청 시 에러가 없는지 확인합니다.
|
|
174
|
+
|
|
175
|
+
## 8.2 실제 chat/completions 경유 여부 확인
|
|
176
|
+
|
|
177
|
+
`--upstream-url`을 명시적으로 지정해 예상 엔드포인트로만 트래픽이 가게 만듭니다.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
cargo run --bin codex-chat-bridge -- \
|
|
181
|
+
--port 8787 \
|
|
182
|
+
--api-key-env OPENAI_API_KEY \
|
|
183
|
+
--upstream-url https://api.openai.com/v1/chat/completions
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 8.3 툴 호출 동작 확인
|
|
187
|
+
|
|
188
|
+
도구 호출이 필요한 프롬프트를 주고, 브리지가 `response.output_item.done`(function_call)을 내보내는지 확인합니다.
|
|
189
|
+
|
|
190
|
+
## 9. 트러블슈팅
|
|
191
|
+
|
|
192
|
+
`missing or empty env var`:
|
|
193
|
+
- `OPENAI_API_KEY`가 비어 있거나 설정되지 않음
|
|
194
|
+
- `--api-key-env` 값과 실제 환경변수 이름이 다름
|
|
195
|
+
|
|
196
|
+
`upstream returned 401/403`:
|
|
197
|
+
- API 키 권한/조직/프로젝트 헤더 확인
|
|
198
|
+
- 필요 시 `openai-organization`, `openai-project` 헤더 전달 상태 확인
|
|
199
|
+
|
|
200
|
+
`upstream returned 404`:
|
|
201
|
+
- `--upstream-url` 경로 확인 (`/v1/chat/completions`)
|
|
202
|
+
|
|
203
|
+
브리지 실행은 되지만 Codex가 여전히 기본 경로를 탐:
|
|
204
|
+
- `-c 'model_provider="chat-bridge"'` 누락 여부 확인
|
|
205
|
+
- `-c` 인용부호 깨짐 여부 확인 (특히 쉘에서 `'`/`"` 혼용)
|
|
206
|
+
|
|
207
|
+
스트리밍 중단:
|
|
208
|
+
- 네트워크 끊김 시 `response.failed` 이벤트로 종료될 수 있음
|
|
209
|
+
- 장시간 작업은 재시도/재실행 전략 권장
|
|
210
|
+
|
|
211
|
+
## 10. 보안/운영 권장 사항
|
|
212
|
+
|
|
213
|
+
- 브리지는 기본적으로 로컬 바인딩(`127.0.0.1`)만 사용하세요.
|
|
214
|
+
- 공유 서버에서는 `--http-shutdown` 사용을 최소화하세요.
|
|
215
|
+
- API 키는 쉘 히스토리에 직접 남기지 말고 환경변수로 주입하세요.
|
|
216
|
+
- 운영 환경에서는 systemd/supervisor로 브리지 생명주기 관리 권장
|
|
217
|
+
|
|
218
|
+
## 11. 현재 제한사항
|
|
219
|
+
|
|
220
|
+
- 완전한 Responses 기능 1:1 재현이 아니라, Codex 동작에 필요한 핵심 이벤트 중심 변환입니다.
|
|
221
|
+
- 모델/벤더별 Chat delta 형식 차이가 있으면 추가 매핑이 필요할 수 있습니다.
|
|
222
|
+
- `responses` 전용 고급 필드는 일부 축약 또는 무시될 수 있습니다.
|
|
223
|
+
|
|
224
|
+
## 12. 빠른 실행 요약
|
|
225
|
+
|
|
226
|
+
1. 브리지 실행
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
cargo run --bin codex-chat-bridge -- --port 8787 --api-key-env OPENAI_API_KEY
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
2. Codex를 브리지 provider로 실행
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
codex exec \
|
|
236
|
+
-c 'model_providers.chat-bridge={name="Chat Bridge",base_url="http://127.0.0.1:8787/v1",env_key="OPENAI_API_KEY",wire_api="responses"}' \
|
|
237
|
+
-c 'model_provider="chat-bridge"' \
|
|
238
|
+
'작업을 수행해줘'
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## 13. Ollama `gpt-oss:20b` 기준 Configuration (전체)
|
|
242
|
+
|
|
243
|
+
이 섹션은 `Ollama + gpt-oss:20b`를 `codex-chat-bridge`로 연결할 때 필요한 설정을 한 번에 정리한 것입니다.
|
|
244
|
+
|
|
245
|
+
전제:
|
|
246
|
+
- Ollama OpenAI 호환 엔드포인트 사용: `http://127.0.0.1:11434/v1/chat/completions`
|
|
247
|
+
- 모델: `gpt-oss:20b`
|
|
248
|
+
- Codex는 브리지에 `responses`로 요청하고, 브리지가 Ollama `chat/completions`로 변환
|
|
249
|
+
|
|
250
|
+
### 13.1 Ollama 준비
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
ollama pull gpt-oss:20b
|
|
254
|
+
ollama serve
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
확인:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
curl --silent http://127.0.0.1:11434/api/tags | jq '.models[].name' | grep 'gpt-oss:20b'
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 13.2 환경변수 설정
|
|
264
|
+
|
|
265
|
+
`codex-chat-bridge`는 API 키 문자열이 비어 있지 않아야 하므로, Ollama에서는 더미 값을 사용합니다.
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
export OLLAMA_API_KEY=ollama
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 13.3 브리지 실행 설정 (전체 옵션 예시)
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
cargo run --bin codex-chat-bridge -- \
|
|
275
|
+
--host 127.0.0.1 \
|
|
276
|
+
--port 8787 \
|
|
277
|
+
--api-key-env OLLAMA_API_KEY \
|
|
278
|
+
--upstream-url http://127.0.0.1:11434/v1/chat/completions \
|
|
279
|
+
--server-info /tmp/codex-chat-bridge-info.json \
|
|
280
|
+
--http-shutdown
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
옵션 설명:
|
|
284
|
+
|
|
285
|
+
| 옵션 | 예시 값 | 설명 |
|
|
286
|
+
|---|---|---|
|
|
287
|
+
| `--host` | `127.0.0.1` | 브리지 바인딩 주소 |
|
|
288
|
+
| `--port` | `8787` | 브리지 포트 |
|
|
289
|
+
| `--api-key-env` | `OLLAMA_API_KEY` | 브리지가 읽을 API 키 환경변수 이름 (Ollama에서는 더미 가능) |
|
|
290
|
+
| `--upstream-url` | `http://127.0.0.1:11434/v1/chat/completions` | 실제 업스트림 chat endpoint |
|
|
291
|
+
| `--server-info` | `/tmp/codex-chat-bridge-info.json` | 실행 포트/프로세스 정보 파일 |
|
|
292
|
+
| `--http-shutdown` | enabled | `GET /shutdown` 허용 |
|
|
293
|
+
|
|
294
|
+
### 13.4 Codex 실행 설정 (CLI 오버라이드 방식)
|
|
295
|
+
|
|
296
|
+
`model`은 `gpt-oss:20b`로 지정해야 합니다.
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
export OLLAMA_API_KEY=ollama
|
|
300
|
+
|
|
301
|
+
codex exec \
|
|
302
|
+
--model gpt-oss:20b \
|
|
303
|
+
-c 'model_providers.chat-bridge-ollama={name="Chat Bridge Ollama",base_url="http://127.0.0.1:8787/v1",env_key="OLLAMA_API_KEY",wire_api="responses"}' \
|
|
304
|
+
-c 'model_provider="chat-bridge-ollama"' \
|
|
305
|
+
'현재 디렉터리 프로젝트 구조를 요약해줘'
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### 13.5 Codex 영구 설정 (`~/.codex/config.toml`)
|
|
309
|
+
|
|
310
|
+
매번 `-c`를 넣기 싫다면 아래처럼 고정할 수 있습니다.
|
|
311
|
+
|
|
312
|
+
```toml
|
|
313
|
+
[model_providers.chat-bridge-ollama]
|
|
314
|
+
name = "Chat Bridge Ollama"
|
|
315
|
+
base_url = "http://127.0.0.1:8787/v1"
|
|
316
|
+
env_key = "OLLAMA_API_KEY"
|
|
317
|
+
wire_api = "responses"
|
|
318
|
+
|
|
319
|
+
model_provider = "chat-bridge-ollama"
|
|
320
|
+
model = "gpt-oss:20b"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
이후 실행:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
export OLLAMA_API_KEY=ollama
|
|
327
|
+
codex
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 13.6 Configuration 전체 항목 설명
|
|
331
|
+
|
|
332
|
+
#### A) 브리지(`codex-chat-bridge`) 설정 항목
|
|
333
|
+
|
|
334
|
+
| 항목 | 필요 여부 | 설명 |
|
|
335
|
+
|---|---|---|
|
|
336
|
+
| `host` | 선택 | 브리지 수신 IP |
|
|
337
|
+
| `port` | 선택 | 브리지 수신 포트 (미지정 시 랜덤) |
|
|
338
|
+
| `upstream_url` | 필수(실질) | 변환 후 요청을 보낼 chat endpoint |
|
|
339
|
+
| `api_key_env` | 필수 | 브리지가 읽는 토큰 환경변수 이름 |
|
|
340
|
+
| `server_info` | 선택 | 포트/PID 파일 출력 |
|
|
341
|
+
| `http_shutdown` | 선택 | HTTP 종료 엔드포인트 허용 |
|
|
342
|
+
|
|
343
|
+
#### B) Codex provider 설정 항목 (`model_providers.<id>`)
|
|
344
|
+
|
|
345
|
+
| 항목 | 필요 여부 | 설명 |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| `name` | 권장 | 표시용 provider 이름 |
|
|
348
|
+
| `base_url` | 필수 | Codex가 호출할 Responses base URL (`http://127.0.0.1:8787/v1`) |
|
|
349
|
+
| `env_key` | 필수 | Codex가 provider 인증 토큰을 읽을 환경변수 |
|
|
350
|
+
| `wire_api` | 필수 | 반드시 `"responses"` |
|
|
351
|
+
|
|
352
|
+
#### C) Codex 런타임 선택 항목
|
|
353
|
+
|
|
354
|
+
| 항목 | 필요 여부 | 설명 |
|
|
355
|
+
|---|---|---|
|
|
356
|
+
| `model_provider` | 필수 | 위에서 정의한 provider id 선택 |
|
|
357
|
+
| `model` | 필수(실질) | Ollama 모델명 (`gpt-oss:20b`) |
|
|
358
|
+
|
|
359
|
+
### 13.7 연결 검증 체크리스트
|
|
360
|
+
|
|
361
|
+
1. 브리지 헬스체크:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
curl --fail --silent --show-error http://127.0.0.1:8787/healthz
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
2. Codex 실행 시 provider/model 확인:
|
|
368
|
+
- provider: `chat-bridge-ollama`
|
|
369
|
+
- model: `gpt-oss:20b`
|
|
370
|
+
|
|
371
|
+
3. 실패 시 우선 점검:
|
|
372
|
+
- Ollama가 `11434`에서 실행 중인지
|
|
373
|
+
- 브리지 `--upstream-url` 경로가 정확한지
|
|
374
|
+
- `OLLAMA_API_KEY`가 빈 문자열이 아닌지
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { existsSync } = require("node:fs");
|
|
4
|
+
const { spawnSync } = require("node:child_process");
|
|
5
|
+
const { resolve } = require("node:path");
|
|
6
|
+
|
|
7
|
+
const root = resolve(__dirname, "..");
|
|
8
|
+
const releaseBin = resolve(root, "target", "release", "codex-chat-bridge");
|
|
9
|
+
const debugBin = resolve(root, "target", "debug", "codex-chat-bridge");
|
|
10
|
+
|
|
11
|
+
const binPath = existsSync(releaseBin)
|
|
12
|
+
? releaseBin
|
|
13
|
+
: existsSync(debugBin)
|
|
14
|
+
? debugBin
|
|
15
|
+
: null;
|
|
16
|
+
|
|
17
|
+
if (!binPath) {
|
|
18
|
+
console.error("error: codex-chat-bridge binary not found.");
|
|
19
|
+
console.error("run `npm run build:bridge` to compile the Rust binary.");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const child = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
24
|
+
|
|
25
|
+
if (child.error) {
|
|
26
|
+
console.error(`error: failed to start bridge binary: ${child.error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof child.status === "number") {
|
|
31
|
+
process.exit(child.status);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
process.exit(1);
|
package/conf.toml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# codex-chat-bridge runtime configuration
|
|
2
|
+
#
|
|
3
|
+
# Priority: CLI flags > conf.toml > built-in defaults
|
|
4
|
+
|
|
5
|
+
# host = "127.0.0.1"
|
|
6
|
+
# port = 8787
|
|
7
|
+
# upstream_url = "https://api.openai.com/v1/chat/completions"
|
|
8
|
+
# api_key_env = "OPENAI_API_KEY"
|
|
9
|
+
# server_info = "/tmp/codex-chat-bridge-info.json"
|
|
10
|
+
# http_shutdown = false
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heungtae/codex-chat-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Responses-to-chat/completions bridge for Codex workflows",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"codex-chat-bridge": "bin/codex-chat-bridge.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "restricted"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"bin/",
|
|
18
|
+
"scripts/",
|
|
19
|
+
"src/",
|
|
20
|
+
"conf.toml",
|
|
21
|
+
"Cargo.toml",
|
|
22
|
+
"Cargo.lock",
|
|
23
|
+
"README.md",
|
|
24
|
+
"USAGE.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build:bridge": "cargo build --release --bin codex-chat-bridge",
|
|
28
|
+
"postinstall": "node scripts/postinstall-build.cjs",
|
|
29
|
+
"start": "node bin/codex-chat-bridge.js",
|
|
30
|
+
"pack:check": "npm pack --dry-run"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require("node:child_process");
|
|
4
|
+
|
|
5
|
+
const result = spawnSync(
|
|
6
|
+
"cargo",
|
|
7
|
+
["build", "--release", "--bin", "codex-chat-bridge"],
|
|
8
|
+
{ stdio: "inherit" }
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
if (result.error) {
|
|
12
|
+
console.error("error: failed to execute `cargo build` during npm postinstall.");
|
|
13
|
+
console.error("install Rust/Cargo first: https://www.rust-lang.org/tools/install");
|
|
14
|
+
console.error(`detail: ${result.error.message}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (result.status !== 0) {
|
|
19
|
+
console.error("error: cargo build failed during npm postinstall.");
|
|
20
|
+
process.exit(result.status ?? 1);
|
|
21
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ $# -lt 1 ]]; then
|
|
5
|
+
echo "usage: $0 '<prompt>' [extra codex args...]" >&2
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
PROMPT="$1"
|
|
10
|
+
shift || true
|
|
11
|
+
|
|
12
|
+
API_KEY_ENV="${API_KEY_ENV:-OPENROUTER_API_KEY}"
|
|
13
|
+
UPSTREAM_URL="${UPSTREAM_URL:-https://openrouter.ai/api/v1/chat/completions}"
|
|
14
|
+
BRIDGE_PORT="${BRIDGE_PORT:-8787}"
|
|
15
|
+
SERVER_INFO="${SERVER_INFO:-/tmp/codex-chat-bridge-info.json}"
|
|
16
|
+
CODEX_BRIDGE_RUST_LOG="${CODEX_BRIDGE_RUST_LOG:-${RUST_LOG:-info,codex_core::rollout::list=off}}"
|
|
17
|
+
|
|
18
|
+
if [[ -z "${!API_KEY_ENV:-}" ]]; then
|
|
19
|
+
echo "error: missing required env var ${API_KEY_ENV}" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
cleanup() {
|
|
24
|
+
curl --silent --show-error --fail "http://127.0.0.1:${BRIDGE_PORT}/shutdown" >/dev/null 2>&1 || true
|
|
25
|
+
}
|
|
26
|
+
trap cleanup EXIT
|
|
27
|
+
|
|
28
|
+
cargo run --bin codex-chat-bridge -- \
|
|
29
|
+
--port "${BRIDGE_PORT}" \
|
|
30
|
+
--api-key-env "${API_KEY_ENV}" \
|
|
31
|
+
--upstream-url "${UPSTREAM_URL}" \
|
|
32
|
+
--server-info "${SERVER_INFO}" \
|
|
33
|
+
--http-shutdown >/tmp/codex-chat-bridge.log 2>&1 &
|
|
34
|
+
|
|
35
|
+
for _ in $(seq 1 40); do
|
|
36
|
+
if curl --silent --show-error --fail "http://127.0.0.1:${BRIDGE_PORT}/healthz" >/dev/null 2>&1; then
|
|
37
|
+
break
|
|
38
|
+
fi
|
|
39
|
+
sleep 0.25
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
RUST_LOG="${CODEX_BRIDGE_RUST_LOG}" codex exec \
|
|
43
|
+
-c "model_providers.chat-bridge={name='Chat Bridge',base_url='http://127.0.0.1:${BRIDGE_PORT}/v1',env_key='${API_KEY_ENV}',wire_api='responses'}" \
|
|
44
|
+
-c 'model_provider="chat-bridge"' \
|
|
45
|
+
-c 'model="arcee-ai/trinity-large-preview:free"' \
|
|
46
|
+
-c 'web_search="disabled"' \
|
|
47
|
+
"$PROMPT" "$@"
|