@ahoo-wang/godex 0.0.16 → 0.0.17

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,19 +1,104 @@
1
- # Godex
1
+ <div align="center">
2
2
 
3
- OpenAI Responses API gateway — translates `/v1/responses` into upstream Chat Completions API calls, so **any LLM provider can drive Codex**.
3
+ <img src="design/assets/01-logo-system/png/godex-logo-horizontal-transparent-800x233.png" alt="GodeX" width="480" />
4
4
 
5
- [![codecov](https://codecov.io/gh/Ahoo-Wang/Godex/graph/badge.svg?token=dJQrmUAiXu)](https://codecov.io/gh/Ahoo-Wang/Godex)
5
+ **Make every model a Codex engine.**
6
+
7
+ OpenAI-compatible Responses API gateway — translates `/v1/responses` into upstream Chat Completions API calls, connecting Codex, CLI, IDE, and automation tools with any model provider.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@ahoo-wang/godex?logo=npm)](https://www.npmjs.com/package/@ahoo-wang/godex)
10
+ [![codecov](https://codecov.io/gh/Ahoo-Wang/GodeX/graph/badge.svg?token=dJQrmUAiXu)](https://codecov.io/gh/Ahoo-Wang/GodeX)
6
11
  [![Bun](https://img.shields.io/badge/runtime-bun-f9f1e0?logo=bun)](https://bun.sh)
7
12
  [![TypeScript](https://img.shields.io/badge/lang-typescript-3178c6?logo=typescript)](https://www.typescriptlang.org/)
8
13
 
14
+ [Getting Started](https://godex.ahoo.me/01-getting-started/overview) · [Architecture](https://godex.ahoo.me/02-architecture/overview) · [Configuration](https://godex.ahoo.me/07-configuration/config-schema) · [Documentation](https://godex.ahoo.me)
15
+
16
+ </div>
17
+
18
+ ## Features
19
+
20
+ | | Feature | Description |
21
+ |---|---------|-------------|
22
+ | 🔄 | **Protocol Translation** | Bridges OpenAI Responses API and provider-specific Chat Completions APIs |
23
+ | 🔌 | **Provider-agnostic** | Plugin-based adapter system — add providers by implementing a small set of interfaces |
24
+ | ⚡ | **Streaming-first** | 4-stage `TransformStream` pipeline for low-latency SSE delivery |
25
+ | 💾 | **Session History** | Built-in `previous_response_id` chain resolution (SQLite / in-memory) |
26
+ | 🛡️ | **Structured Errors** | Domain-specific error hierarchy with structured codes and diagnostic context |
27
+ | 🔧 | **Built-in Tools** | `local_shell`, `shell`, `apply_patch` — Codex-compatible function tools |
28
+ | 📦 | **Standalone Binary** | Zero runtime dependencies, 6 platform builds via GitHub Actions |
29
+
30
+ ## Quick Start
31
+
32
+ ```bash
33
+ # Install — no Bun required at runtime
34
+ npm install -g @ahoo-wang/godex
35
+
36
+ # Create config interactively
37
+ godex init
38
+
39
+ # Start the gateway
40
+ godex serve
41
+ ```
42
+
43
+ Point Codex CLI at your GodeX instance:
44
+
45
+ ```bash
46
+ export OPENAI_BASE_URL=http://localhost:5678/v1
47
+ export OPENAI_API_KEY=any-value # not validated by GodeX, must be set
48
+ codex
49
+ ```
50
+
51
+ Or use the OpenAI SDK:
52
+
53
+ ```ts
54
+ import OpenAI from "openai";
55
+
56
+ const client = new OpenAI({
57
+ baseURL: "http://localhost:5678/v1",
58
+ apiKey: "any-value",
59
+ });
60
+
61
+ const response = await client.responses.create({
62
+ model: "gpt-4o", // mapped to glm-4.7 via godex.yaml models table
63
+ input: "Hello!",
64
+ });
65
+ ```
66
+
67
+ ## How It Works
68
+
69
+ ```
70
+ Codex / CLI / IDE
71
+
72
+ ▼ POST /v1/responses
73
+ ┌─────────────────────────────────────────┐
74
+ │ GodeX Gateway │
75
+ │ │
76
+ │ Bun.serve → handleResponses() │
77
+ │ → ResponsesContext.create() │
78
+ │ → ModelResolver.resolve() │
79
+ │ → Registrar.resolve() │
80
+ │ → DefaultAdapter.stream/request() │
81
+ │ → ProviderMapper.map() │
82
+ │ → ChatClient.streamChat() │
83
+ │ → 4-stage TransformStream │
84
+ │ → Response (JSON or SSE) │
85
+ └──────────────┬──────────────────────────┘
86
+ │ Provider Adapter
87
+
88
+ ┌─────────────────────────────────────────┐
89
+ │ Chat Completions-compatible API │
90
+ │ (Zhipu, OpenAI, or custom) │
91
+ └─────────────────────────────────────────┘
92
+ ```
93
+
9
94
  ## Architecture
10
95
 
11
96
  ```mermaid
12
97
  C4Context
13
- title Godex — System Context
98
+ title GodeX — System Context
14
99
 
15
100
  Person(user, "Developer / Codex CLI", "Sends Responses API requests<br/>via the OpenAI-compatible endpoint")
16
- System(godex_svr, "Godex Server", "Translates Responses API → Chat Completions API<br/>Bun HTTP server on configurable port")
101
+ System(godex_svr, "GodeX Server", "Translates Responses API → Chat Completions API<br/>Bun.serve on configurable port")
17
102
  SystemDb(sessions, "Session Store", "Stores response history for<br/>previous_response_id chain resolution<br/>SQLite (persistent) or In-Memory")
18
103
  System_Ext(zhipu, "Zhipu (智谱)", "Chat Completions API provider")
19
104
  System_Ext(openai, "OpenAI", "Chat Completions API provider")
@@ -26,72 +111,6 @@ C4Context
26
111
  Rel(godex_svr, other, "POST /chat/completions", "HTTPS")
27
112
  ```
28
113
 
29
- ## Request Flow
30
-
31
- ```mermaid
32
- sequenceDiagram
33
- actor C as Client (Codex CLI)
34
- participant R as Router
35
- participant AC as ApplicationContext
36
- participant RC as ResponsesContext
37
- participant MR as ModelResolver
38
- participant SS as SessionStore
39
- participant REG as Registrar
40
- participant A as Adapter (DefaultAdapter)
41
- participant PM as ProviderMapper
42
- participant CC as ChatClient
43
- participant UP as Upstream API
44
-
45
- C->>R: POST /v1/responses
46
- R->>RC: ResponsesContext.create(app, body)
47
-
48
- activate RC
49
- RC->>MR: resolve(model)
50
- MR-->>RC: { provider, model }
51
- RC->>RC: validate provider config
52
-
53
- opt previous_response_id
54
- RC->>SS: resolveChain(id)
55
- SS-->>RC: session snapshot
56
- end
57
-
58
- RC->>REG: resolve(provider)
59
- REG-->>RC: Provider instance
60
- deactivate RC
61
-
62
- alt stream = true
63
- R->>A: adapter.stream(ctx)
64
- activate A
65
- A->>PM: request.map(ctx)
66
- PM-->>A: upstream request
67
- A->>CC: streamChat(req)
68
- CC->>UP: POST (SSE)
69
- UP-->>CC: SSE chunks
70
- CC-->>A: ReadableStream<SSE>
71
- A->>A: pipeTransform → ProviderEventToResponseTransformer
72
- A->>A: pipeTransform → ResponseSessionPersistenceTransformer
73
- A-->>R: ReadableStream<ResponseStreamEvent>
74
- deactivate A
75
- R->>R: pipeTransform → ResponseSseEncodeTransformer
76
- R-->>C: SSE byte stream
77
- else stream = false
78
- R->>A: adapter.request(ctx)
79
- activate A
80
- A->>PM: request.map(ctx)
81
- PM-->>A: upstream request
82
- A->>CC: chat(req)
83
- CC->>UP: POST
84
- UP-->>CC: JSON response
85
- CC-->>A: upstream response
86
- A->>PM: response.map(ctx, res)
87
- PM-->>A: ResponseObject
88
- A->>SS: save(session)
89
- A-->>R: ResponseObject
90
- deactivate A
91
- R-->>C: JSON response
92
- end
93
- ```
94
-
95
114
  ## Component Model
96
115
 
97
116
  ```mermaid
@@ -99,7 +118,7 @@ classDiagram
99
118
  direction TB
100
119
 
101
120
  class ApplicationContext {
102
- +config: GodexConfig
121
+ +config: GodeXConfig
103
122
  +logger: Logger
104
123
  +resolver: ModelResolver
105
124
  +registrar: Registrar
@@ -115,7 +134,7 @@ classDiagram
115
134
  +provider: Provider
116
135
  +responseId: string
117
136
  +requestId: string
118
- +logger: Logger
137
+ +attributes: Map
119
138
  +create(app, body)$ Promise~ResponsesContext~
120
139
  }
121
140
 
@@ -130,7 +149,6 @@ classDiagram
130
149
  +registerFactory(name, factory)
131
150
  +build(providers)
132
151
  +resolve(name) Provider
133
- +list() string[]
134
152
  }
135
153
 
136
154
  class Adapter {
@@ -165,22 +183,6 @@ classDiagram
165
183
  +streamChat(req) Promise~ReadableStream~
166
184
  }
167
185
 
168
- class RequestMapper {
169
- <<interface>>
170
- +map(ctx) TReq
171
- }
172
-
173
- class ResponseMapper {
174
- <<interface>>
175
- +map(ctx, result) ResponseObject
176
- }
177
-
178
- class StreamMapper {
179
- <<interface>>
180
- +map(ctx, event) ResponseStreamEvent[]
181
- +buildResponseObject(ctx, state) ResponseObject
182
- }
183
-
184
186
  class ResponseSessionStore {
185
187
  <<interface>>
186
188
  +get(id) StoredResponseSession
@@ -190,12 +192,6 @@ classDiagram
190
192
  +close()
191
193
  }
192
194
 
193
- class Router {
194
- -routes: Route[]
195
- +register(route)
196
- +dispatch(req) Promise~Response~
197
- }
198
-
199
195
  ApplicationContext --> ResponsesContext : creates
200
196
  ApplicationContext --> ModelResolver
201
197
  ApplicationContext --> Registrar
@@ -204,14 +200,65 @@ classDiagram
204
200
  ResponsesContext --> Provider : uses
205
201
  Provider --> ProviderMapper
206
202
  Provider --> ChatClient
207
- ProviderMapper --> RequestMapper
208
- ProviderMapper --> ResponseMapper
209
- ProviderMapper --> StreamMapper
210
203
  Adapter <|.. DefaultAdapter
211
204
  DefaultAdapter --> ProviderMapper : calls
212
205
  DefaultAdapter --> ChatClient : calls
213
206
  DefaultAdapter --> ResponseSessionStore : saves
214
- Router --> ResponsesContext : dispatches to
207
+ ```
208
+
209
+ ## Request Flow
210
+
211
+ ```mermaid
212
+ sequenceDiagram
213
+ actor C as Client (Codex CLI)
214
+ participant H as handleResponses
215
+ participant RC as ResponsesContext
216
+ participant MR as ModelResolver
217
+ participant SS as SessionStore
218
+ participant REG as Registrar
219
+ participant A as DefaultAdapter
220
+ participant PM as ProviderMapper
221
+ participant CC as ChatClient
222
+ participant UP as Upstream API
223
+
224
+ C->>H: POST /v1/responses
225
+ H->>RC: ResponsesContext.create(app, body)
226
+ activate RC
227
+ RC->>MR: resolve(model)
228
+ MR-->>RC: { provider, model }
229
+ opt previous_response_id
230
+ RC->>SS: resolveChain(id)
231
+ SS-->>RC: session snapshot
232
+ end
233
+ RC->>REG: resolve(provider)
234
+ REG-->>RC: Provider instance
235
+ deactivate RC
236
+
237
+ alt stream = true
238
+ H->>A: adapter.stream(ctx)
239
+ activate A
240
+ A->>PM: mapper.request.map(ctx)
241
+ A->>CC: chatClient.streamChat(req)
242
+ CC->>UP: POST (SSE)
243
+ UP-->>CC: SSE chunks
244
+ A->>A: pipeTransform → ProviderEventToResponse
245
+ A->>A: pipeTransform → ResponseLog
246
+ A->>A: pipeTransform → ResponseSessionPersistence
247
+ deactivate A
248
+ H->>H: pipeTransform → ResponseSseEncode
249
+ H-->>C: SSE byte stream
250
+ else stream = false
251
+ H->>A: adapter.request(ctx)
252
+ activate A
253
+ A->>PM: mapper.request.map(ctx)
254
+ A->>CC: chatClient.chat(req)
255
+ CC->>UP: POST
256
+ UP-->>A: upstream response
257
+ A->>PM: mapper.response.map(ctx, res)
258
+ A->>SS: save(session)
259
+ deactivate A
260
+ H-->>C: JSON response
261
+ end
215
262
  ```
216
263
 
217
264
  ## Stream Pipeline
@@ -219,120 +266,65 @@ classDiagram
219
266
  ```mermaid
220
267
  flowchart LR
221
268
  subgraph upstream["Upstream Provider"]
222
- SSE["SSE Chunks<br/>(JsonServerSentEvent)"]
269
+ SSE["SSE Chunks"]
223
270
  end
224
271
 
225
- subgraph godex["Godex Stream Pipeline"]
226
- T1["ProviderEventTo<br/>ResponseTransformer"]
227
- T2["ResponseSession<br/>PersistenceTransformer"]
228
- T3["ResponseSse<br/>EncodeTransformer"]
272
+ subgraph godex["GodeX Stream Pipeline"]
273
+ T1["① ProviderEventToResponse"]
274
+ T2["② ResponseLog"]
275
+ T3["③ SessionPersistence"]
276
+ end
277
+
278
+ subgraph server["HTTP Response"]
279
+ T4["④ SseEncode"]
229
280
  end
230
281
 
231
282
  subgraph client["Client"]
232
- BYTES["SSE Bytes<br/>(text/event-stream)"]
283
+ BYTES["SSE Bytes"]
233
284
  end
234
285
 
235
- SSE -->|"pipeThrough(TransformStream)"| T1
236
- T1 -->|"per-event map()<br/>SSE chunk → ResponseStreamEvent[]"| T2
237
- T2 -->|"accumulate StreamState<br/>intercept terminal event<br/>buildResponseObject()<br/>save session"| T3
238
- T3 -->|"serialize to SSE wire format<br/>event: xxx\ndata: {...}\n\n"| BYTES
286
+ SSE -->|pipeThrough| T1
287
+ T1 -->|map per event| T2
288
+ T2 -->|log + pass through| T3
289
+ T3 -->|accumulate + save session| T4
290
+ T4 -->|serialize SSE wire format| BYTES
239
291
 
240
292
  style upstream fill:#1a1a2e,stroke:#16213e,color:#e0e0e0
241
293
  style godex fill:#0f3460,stroke:#16213e,color:#e0e0e0
294
+ style server fill:#1c2333,stroke:#16213e,color:#e0e0e0
242
295
  style client fill:#1a1a2e,stroke:#16213e,color:#e0e0e0
243
296
  ```
244
297
 
245
- ### Transformer Roles
246
-
247
- | Stage | Transformer | Input | Output | Side Effects |
248
- |-------|------------|-------|--------|-------------|
249
- | 1 | `ProviderEventToResponseTransformer` | `JsonServerSentEvent<TChunk>` | `ResponseStreamEvent` | Calls `StreamMapper.map()` per event |
250
- | 2 | `ResponseSessionPersistenceTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | Accumulates `StreamState`, on terminal event calls `buildResponseObject()` + saves session (skipped when `store=false`) |
251
- | 3 | `ResponseSseEncodeTransformer` | `ResponseStreamEvent` | `Uint8Array` (SSE wire format) | Serializes to `event:` / `data:` lines |
252
-
253
- ## Error Hierarchy
254
-
255
- ```mermaid
256
- classDiagram
257
- direction TB
258
-
259
- class GodexError {
260
- +name: string
261
- +code: string
262
- +status: number
263
- +context: object
264
- +toLogEntry() object
265
- }
266
-
267
- class ServerError {
268
- +status: 400-499
269
- +context: object
270
- }
271
-
272
- class AdapterError {
273
- +status: 400-499
274
- +context: unsupported parameters / tools / input items
275
- }
276
-
277
- class ProviderError {
278
- +status: 502
279
- +context: upstream status / body / headers
280
- }
281
-
282
- class SessionError {
283
- +status: 400-409
284
- +context: chain metadata
285
- }
286
-
287
- GodexError <|-- ServerError
288
- GodexError <|-- AdapterError
289
- GodexError <|-- ProviderError
290
- GodexError <|-- SessionError
291
-
292
- note for GodexError "Base error with structured logging support.<br/>All errors carry domain codes (e.g. server.request.invalid_json)."
293
- note for ProviderError "Wraps upstream HTTP failures:<br/>rate limits, timeouts, 5xx."
294
- note for SessionError "Chain resolution failures:<br/>not found, cycles, depth exceeded."
295
- ```
298
+ | Stage | Transformer | Input | Output | Role |
299
+ |-------|------------|-------|--------|------|
300
+ | | `ProviderEventToResponseTransformer` | `JsonServerSentEvent` | `ResponseStreamEvent` | Maps upstream SSE chunks via `StreamMapper.map()` |
301
+ | ② | `ResponseLogTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | Logs stream events for observability |
302
+ | | `ResponseSessionPersistenceTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | Accumulates `StreamState`, saves session on terminal event |
303
+ | | `ResponseSseEncodeTransformer` | `ResponseStreamEvent` | `Uint8Array` | Serializes to `event:` / `data:` wire format |
296
304
 
297
305
  ## Project Structure
298
306
 
299
307
  ```
300
308
  src/
301
- ├── cli/ Commander CLI (serve, config check, init)
309
+ ├── cli/ Commander CLI (serve, config, init)
302
310
  ├── config/ godex.yaml schema, env interpolation, defaults
303
- ├── context/ ApplicationContext (DI container), ResponsesContext (per-request)
311
+ ├── context/ ApplicationContext (DI), ResponsesContext (per-request)
304
312
  ├── adapter/ Adapter interface, DefaultAdapter, stream transformers
305
313
  │ ├── mapper/ RequestMapper / ResponseMapper / StreamMapper contracts
306
- │ └── transformers/ ProviderEventResponseSSE encode pipeline
314
+ │ └── transformers/ 4-stage stream pipeline (map logpersist encode)
307
315
  ├── providers/ Provider registry + builtin factories
308
316
  │ └── zhipu/ Reference provider: mapper, chat-client, tools, messages
309
317
  ├── resolver/ ModelResolver (model selector → provider + model)
310
- ├── server/ Bun HTTP server, Router, routes (/v1/responses, /health, /v1/models)
318
+ ├── server/ Bun.serve, routes (/v1/responses, /health, /v1/models)
311
319
  ├── session/ ResponseSessionStore (Memory + SQLite), chain resolution
312
- ├── error/ GodexError hierarchy with domain codes
320
+ ├── error/ GodeXError hierarchy with domain codes
321
+ ├── tools/ Built-in function tools (local_shell, shell, apply_patch)
313
322
  ├── protocol/openai/ OpenAI-compatible type definitions
314
323
  ├── logger/ Structured JSON logger
315
324
  └── e2e/ End-to-end tests with mocked upstream
316
325
  ```
317
326
 
318
- ## Quick Start
319
-
320
- ```bash
321
- # Install dependencies
322
- bun install
323
-
324
- # Build standalone binary (current platform)
325
- bun run build
326
-
327
- # Create config interactively
328
- bun run start -- init
329
-
330
- # Start server (default port 5678)
331
- bun run dev
332
-
333
- # Or run the compiled binary directly
334
- ./platforms/darwin-arm64/bin/godex serve
335
- ```
327
+ ## Configuration
336
328
 
337
329
  ### godex.yaml
338
330
 
@@ -359,115 +351,65 @@ logging:
359
351
  level: info # trace | debug | info | warn | error
360
352
  ```
361
353
 
362
- ### Adding a Provider
354
+ ### Model Selection
363
355
 
364
- Implement these interfaces in `src/providers/<name>/`:
365
-
366
- | Interface | Purpose |
367
- |-----------|---------|
368
- | `Provider<TReq, TRes, TChunk>` | Bundles mapper + chatClient + capabilities |
369
- | `ProviderMapper<TReq, TRes, TChunk>` | request / response / stream mapping functions |
370
- | `ChatClient<TReq, TRes, TChunk>` | `chat()` and `streamChat()` HTTP calls |
371
-
372
- Register the factory in `src/providers/builtin.ts`:
373
-
374
- ```ts
375
- registrar.registerFactory("myprovider", (config) =>
376
- createMyProvider(config) as Provider<unknown, unknown, unknown>
377
- );
378
356
  ```
379
-
380
- ## Usage
381
-
382
- ```bash
383
- # Install — no Bun required at runtime
384
- npm install -g @ahoo-wang/godex
385
-
386
- # Create config interactively
387
- godex init
388
-
389
- # Start the gateway
390
- godex serve
357
+ model: "gpt-4o" → resolved via default_provider model mapping
358
+ model: "zhipu/glm-4.7" → explicit provider/model selector
359
+ model: "openai/gpt-4o" → routes to configured openai provider
391
360
  ```
392
361
 
393
- Godex ships as a **standalone native binary** with zero runtime dependencies. npm's `postinstall` automatically selects the correct binary for your platform. The only prerequisite is Node.js >= 18 (needed only during `npm install`).
394
-
395
- Godex exposes an **OpenAI-compatible Responses API** at `http://localhost:5678` (port is configurable). Point any tool that speaks the OpenAI protocol at this endpoint:
396
-
397
- ### With Codex CLI
362
+ ### Health Check
398
363
 
399
364
  ```bash
400
- export OPENAI_BASE_URL=http://localhost:5678/v1
401
- export OPENAI_API_KEY=any-value # not validated by Godex, must be set
402
- codex
365
+ curl http://localhost:5678/health
366
+ # {"status":"ok","providers":["zhipu"],"unsupported_providers":[]}
403
367
  ```
404
368
 
405
- ### With OpenAI SDK
406
-
407
- ```ts
408
- import OpenAI from "openai";
369
+ ### Adding a Provider
409
370
 
410
- const client = new OpenAI({
411
- baseURL: "http://localhost:5678/v1",
412
- apiKey: "any-value", // passed through, not validated
413
- });
371
+ Implement three interfaces in `src/providers/<name>/`:
414
372
 
415
- const response = await client.responses.create({
416
- model: "gpt-4o", // mapped to glm-4.7 via godex.yaml models table
417
- input: "Hello!",
418
- });
419
- ```
373
+ | Interface | Purpose |
374
+ |-----------|---------|
375
+ | `Provider` | Bundles mapper + chatClient + capabilities |
376
+ | `ProviderMapper` | request / response / stream mapping functions |
377
+ | `ChatClient` | `chat()` and `streamChat()` HTTP calls |
420
378
 
421
- ### Model selection
379
+ Register the factory in `src/providers/builtin.ts`:
422
380
 
381
+ ```ts
382
+ registrar.registerFactory("myprovider", (config) =>
383
+ createMyProvider(config) as Provider<unknown, unknown, unknown>
384
+ );
423
385
  ```
424
- model: "gpt-4o" → resolved via default_provider model mapping
425
- model: "zhipu/glm-4.7" → explicit provider/model selector
426
- model: "openai/gpt-4o" → routes to configured openai provider
427
- ```
428
-
429
- The `models` map in `godex.yaml` lets you translate standard model names into provider-native ones — no code changes needed in the client.
430
386
 
431
- ### Health check
387
+ ## Development
432
388
 
433
389
  ```bash
434
- curl http://localhost:5678/health
435
- # {"status":"ok","providers":["zhipu"],"unsupported_providers":[]}
390
+ bun install # Install dependencies
391
+ bun run dev # Dev server with hot reload (port 13145)
392
+ bun run test # Unit + integration tests
393
+ bun run test:e2e # E2E tests with mocked upstream
394
+ bun run build # Build standalone binary for current platform
395
+ bun run check # typecheck + lint + test
396
+ bun run ci # Full CI pipeline
436
397
  ```
437
398
 
438
399
  ## Publishing
439
400
 
440
- The main `@ahoo-wang/godex` npm package is a lightweight shell. Native binaries are shipped as platform-specific optional dependencies:
401
+ `@ahoo-wang/godex` is a lightweight npm wrapper. Native binaries ship as platform-specific optional dependencies:
441
402
 
442
403
  ```
443
- @ahoo-wang/godex (wrapper package, 0 runtime deps)
444
- ├── engines: { node: ">=18.0.0" } only for postinstall
445
- ├── postinstall: scripts/install.cjs detects platform, links binary
446
- └── optionalDependencies:
447
- ├── @ahoo-wang/godex-darwin-arm64 macOS Apple Silicon
448
- ├── @ahoo-wang/godex-darwin-x64 macOS Intel
449
- ├── @ahoo-wang/godex-linux-x64 Linux x86_64
450
- ├── @ahoo-wang/godex-linux-arm64 ← Linux ARM64
451
- ├── @ahoo-wang/godex-win32-x64 ← Windows x86_64
452
- └── @ahoo-wang/godex-win32-arm64 ← Windows ARM64
453
-
454
- # Publishing flow:
455
- # 1. Make the GitHub repository public, configure NPM_TOKEN, then push the release commit.
456
- # 2. Create a GitHub Release tagged vX.Y.Z.
457
- # 3. The Release workflow builds all platform binaries.
458
- # 4. The Release workflow uploads binary archives and SHA256SUMS to Release Assets.
459
- # 5. The Release workflow publishes platform packages first, then @ahoo-wang/godex.
404
+ @ahoo-wang/godex
405
+ ├── @ahoo-wang/godex-darwin-arm64 macOS Apple Silicon
406
+ ├── @ahoo-wang/godex-darwin-x64 macOS Intel
407
+ ├── @ahoo-wang/godex-linux-x64 ← Linux x86_64
408
+ ├── @ahoo-wang/godex-linux-arm64 Linux ARM64
409
+ ├── @ahoo-wang/godex-win32-x64 Windows x86_64
410
+ └── @ahoo-wang/godex-win32-arm64 Windows ARM64
460
411
  ```
461
412
 
462
- ## Commands
413
+ ## License
463
414
 
464
- ```bash
465
- bun run dev # Hot-reload dev server on port 13145
466
- bun run build # Compile native binary for current platform
467
- bun run compile:all # Cross-compile all 6 platforms locally
468
- bun run test # Unit + integration tests
469
- bun run test:e2e # E2E with mocked upstream
470
- bun run typecheck # tsc --noEmit
471
- bun run lint # Biome check
472
- bun run ci # Full CI pipeline
473
- ```
415
+ [Apache License 2.0](LICENSE)
package/README.zh-CN.md CHANGED
@@ -1,19 +1,105 @@
1
- # Godex
1
+ <div align="center">
2
2
 
3
- OpenAI Responses API 网关 — 将 `/v1/responses` 请求转换为上游 Chat Completions API 调用,让**任何 LLM 提供商都能驱动 Codex**。
3
+ <img src="design/assets/01-logo-system/png/godex-logo-horizontal-transparent-800x233.png" alt="GodeX" width="480" />
4
4
 
5
- [![codecov](https://codecov.io/gh/Ahoo-Wang/Godex/graph/badge.svg?token=dJQrmUAiXu)](https://codecov.io/gh/Ahoo-Wang/Godex)
5
+ **让每个模型都成为 Codex 引擎。**
6
+
7
+ OpenAI 兼容的 Responses API 网关 — 将 `/v1/responses` 请求转换为上游 Chat Completions API 调用,连接 Codex、CLI、IDE 和自动化开发工具与不同模型供应商。
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@ahoo-wang/godex?logo=npm)](https://www.npmjs.com/package/@ahoo-wang/godex)
10
+ [![codecov](https://codecov.io/gh/Ahoo-Wang/GodeX/graph/badge.svg?token=dJQrmUAiXu)](https://codecov.io/gh/Ahoo-Wang/GodeX)
6
11
  [![Bun](https://img.shields.io/badge/runtime-bun-f9f1e0?logo=bun)](https://bun.sh)
7
12
  [![TypeScript](https://img.shields.io/badge/lang-typescript-3178c6?logo=typescript)](https://www.typescriptlang.org/)
8
13
 
14
+ [快速入门](https://godex.ahoo.me/zh/01-getting-started/overview) · [架构](https://godex.ahoo.me/zh/02-architecture/overview) · [配置](https://godex.ahoo.me/zh/07-configuration/config-schema) · [文档](https://godex.ahoo.me/zh/)
15
+
16
+ </div>
17
+
18
+
19
+ ## 功能特性
20
+
21
+ | | 特性 | 说明 |
22
+ |---|------|------|
23
+ | 🔄 | **协议转换** | 弥补 OpenAI Responses API 与提供商 Chat Completions API 之间的差距 |
24
+ | 🔌 | **提供商无关** | 基于插件的适配器系统 — 添加提供商只需实现少量接口 |
25
+ | ⚡ | **流式优先** | 4 阶段 `TransformStream` 管道,低延迟 SSE 传输 |
26
+ | 💾 | **会话历史** | 内置 `previous_response_id` 链式解析(SQLite / 内存) |
27
+ | 🛡️ | **结构化错误** | 域特定错误层次结构,带结构化编码和诊断上下文 |
28
+ | 🔧 | **内置工具** | `local_shell`、`shell`、`apply_patch` — Codex 兼容函数工具 |
29
+ | 📦 | **独立二进制** | 零运行时依赖,通过 GitHub Actions 构建 6 个平台 |
30
+
31
+ ## 快速开始
32
+
33
+ ```bash
34
+ # 安装 — 运行时无需 Bun
35
+ npm install -g @ahoo-wang/godex
36
+
37
+ # 交互式创建配置
38
+ godex init
39
+
40
+ # 启动网关
41
+ godex serve
42
+ ```
43
+
44
+ 将 Codex CLI 指向你的 GodeX 实例:
45
+
46
+ ```bash
47
+ export OPENAI_BASE_URL=http://localhost:5678/v1
48
+ export OPENAI_API_KEY=any-value # GodeX 不验证此值,但必须设置
49
+ codex
50
+ ```
51
+
52
+ 或使用 OpenAI SDK:
53
+
54
+ ```ts
55
+ import OpenAI from "openai";
56
+
57
+ const client = new OpenAI({
58
+ baseURL: "http://localhost:5678/v1",
59
+ apiKey: "any-value",
60
+ });
61
+
62
+ const response = await client.responses.create({
63
+ model: "gpt-4o", // 通过 godex.yaml 的 models 表映射为 glm-4.7
64
+ input: "Hello!",
65
+ });
66
+ ```
67
+
68
+ ## 工作原理
69
+
70
+ ```
71
+ Codex / CLI / IDE
72
+
73
+ ▼ POST /v1/responses
74
+ ┌─────────────────────────────────────────┐
75
+ │ GodeX 网关 │
76
+ │ │
77
+ │ Bun.serve → handleResponses() │
78
+ │ → ResponsesContext.create() │
79
+ │ → ModelResolver.resolve() │
80
+ │ → Registrar.resolve() │
81
+ │ → DefaultAdapter.stream/request() │
82
+ │ → ProviderMapper.map() │
83
+ │ → ChatClient.streamChat() │
84
+ │ → 4 阶段 TransformStream │
85
+ │ → Response (JSON 或 SSE) │
86
+ └──────────────┬──────────────────────────┘
87
+ │ 提供商适配器
88
+
89
+ ┌─────────────────────────────────────────┐
90
+ │ Chat Completions 兼容 API │
91
+ │ (智谱、OpenAI 或自定义) │
92
+ └─────────────────────────────────────────┘
93
+ ```
94
+
9
95
  ## 架构
10
96
 
11
97
  ```mermaid
12
98
  C4Context
13
- title Godex — 系统上下文
99
+ title GodeX — 系统上下文
14
100
 
15
101
  Person(user, "开发者 / Codex CLI", "通过 OpenAI 兼容端点<br/>发送 Responses API 请求")
16
- System(godex_svr, "Godex 服务器", "转换 Responses API → Chat Completions API<br/>基于 Bun HTTP 服务器,端口可配置")
102
+ System(godex_svr, "GodeX 服务器", "转换 Responses API → Chat Completions API<br/>基于 Bun.serve,端口可配置")
17
103
  SystemDb(sessions, "会话存储", "存储响应历史,用于<br/>previous_response_id 链式解析<br/>SQLite(持久化)或内存")
18
104
  System_Ext(zhipu, "智谱 (Zhipu)", "Chat Completions API 提供商")
19
105
  System_Ext(openai, "OpenAI", "Chat Completions API 提供商")
@@ -26,72 +112,6 @@ C4Context
26
112
  Rel(godex_svr, other, "POST /chat/completions", "HTTPS")
27
113
  ```
28
114
 
29
- ## 请求流程
30
-
31
- ```mermaid
32
- sequenceDiagram
33
- actor C as 客户端 (Codex CLI)
34
- participant R as Router 路由
35
- participant AC as ApplicationContext 应用上下文
36
- participant RC as ResponsesContext 响应上下文
37
- participant MR as ModelResolver 模型解析器
38
- participant SS as SessionStore 会话存储
39
- participant REG as Registrar 注册器
40
- participant A as Adapter 适配器 (DefaultAdapter)
41
- participant PM as ProviderMapper 提供商映射器
42
- participant CC as ChatClient 聊天客户端
43
- participant UP as 上游 API
44
-
45
- C->>R: POST /v1/responses
46
- R->>RC: ResponsesContext.create(app, body)
47
-
48
- activate RC
49
- RC->>MR: resolve(model)
50
- MR-->>RC: { provider, model }
51
- RC->>RC: 验证提供商配置
52
-
53
- opt previous_response_id
54
- RC->>SS: resolveChain(id)
55
- SS-->>RC: 会话快照
56
- end
57
-
58
- RC->>REG: resolve(provider)
59
- REG-->>RC: Provider 实例
60
- deactivate RC
61
-
62
- alt stream = true
63
- R->>A: adapter.stream(ctx)
64
- activate A
65
- A->>PM: request.map(ctx)
66
- PM-->>A: 上游请求
67
- A->>CC: streamChat(req)
68
- CC->>UP: POST (SSE)
69
- UP-->>CC: SSE 数据块
70
- CC-->>A: ReadableStream<SSE>
71
- A->>A: pipeTransform → ProviderEventToResponseTransformer
72
- A->>A: pipeTransform → ResponseSessionPersistenceTransformer
73
- A-->>R: ReadableStream<ResponseStreamEvent>
74
- deactivate A
75
- R->>R: pipeTransform → ResponseSseEncodeTransformer
76
- R-->>C: SSE 字节流
77
- else stream = false
78
- R->>A: adapter.request(ctx)
79
- activate A
80
- A->>PM: request.map(ctx)
81
- PM-->>A: 上游请求
82
- A->>CC: chat(req)
83
- CC->>UP: POST
84
- UP-->>CC: JSON 响应
85
- CC-->>A: 上游响应
86
- A->>PM: response.map(ctx, res)
87
- PM-->>A: ResponseObject
88
- A->>SS: save(session)
89
- A-->>R: ResponseObject
90
- deactivate A
91
- R-->>C: JSON 响应
92
- end
93
- ```
94
-
95
115
  ## 组件模型
96
116
 
97
117
  ```mermaid
@@ -99,7 +119,7 @@ classDiagram
99
119
  direction TB
100
120
 
101
121
  class ApplicationContext {
102
- +config: GodexConfig
122
+ +config: GodeXConfig
103
123
  +logger: Logger
104
124
  +resolver: ModelResolver
105
125
  +registrar: Registrar
@@ -115,7 +135,7 @@ classDiagram
115
135
  +provider: Provider
116
136
  +responseId: string
117
137
  +requestId: string
118
- +logger: Logger
138
+ +attributes: Map
119
139
  +create(app, body)$ Promise~ResponsesContext~
120
140
  }
121
141
 
@@ -130,7 +150,6 @@ classDiagram
130
150
  +registerFactory(name, factory)
131
151
  +build(providers)
132
152
  +resolve(name) Provider
133
- +list() string[]
134
153
  }
135
154
 
136
155
  class Adapter {
@@ -165,22 +184,6 @@ classDiagram
165
184
  +streamChat(req) Promise~ReadableStream~
166
185
  }
167
186
 
168
- class RequestMapper {
169
- <<interface>>
170
- +map(ctx) TReq
171
- }
172
-
173
- class ResponseMapper {
174
- <<interface>>
175
- +map(ctx, result) ResponseObject
176
- }
177
-
178
- class StreamMapper {
179
- <<interface>>
180
- +map(ctx, event) ResponseStreamEvent[]
181
- +buildResponseObject(ctx, state) ResponseObject
182
- }
183
-
184
187
  class ResponseSessionStore {
185
188
  <<interface>>
186
189
  +get(id) StoredResponseSession
@@ -190,28 +193,73 @@ classDiagram
190
193
  +close()
191
194
  }
192
195
 
193
- class Router {
194
- -routes: Route[]
195
- +register(route)
196
- +dispatch(req) Promise~Response~
197
- }
198
-
199
- ApplicationContext --> ResponsesContext : 创建
196
+ ApplicationContext --> ResponsesContext : creates
200
197
  ApplicationContext --> ModelResolver
201
198
  ApplicationContext --> Registrar
202
199
  ApplicationContext --> Adapter
203
200
  ApplicationContext --> ResponseSessionStore
204
- ResponsesContext --> Provider : 使用
201
+ ResponsesContext --> Provider : uses
205
202
  Provider --> ProviderMapper
206
203
  Provider --> ChatClient
207
- ProviderMapper --> RequestMapper
208
- ProviderMapper --> ResponseMapper
209
- ProviderMapper --> StreamMapper
210
204
  Adapter <|.. DefaultAdapter
211
- DefaultAdapter --> ProviderMapper : 调用
212
- DefaultAdapter --> ChatClient : 调用
213
- DefaultAdapter --> ResponseSessionStore : 保存
214
- Router --> ResponsesContext : 分发至
205
+ DefaultAdapter --> ProviderMapper : calls
206
+ DefaultAdapter --> ChatClient : calls
207
+ DefaultAdapter --> ResponseSessionStore : saves
208
+ ```
209
+
210
+ ## 请求流程
211
+
212
+ ```mermaid
213
+ sequenceDiagram
214
+ actor C as 客户端 (Codex CLI)
215
+ participant H as handleResponses
216
+ participant RC as ResponsesContext
217
+ participant MR as ModelResolver
218
+ participant SS as SessionStore
219
+ participant REG as Registrar
220
+ participant A as DefaultAdapter
221
+ participant PM as ProviderMapper
222
+ participant CC as ChatClient
223
+ participant UP as 上游 API
224
+
225
+ C->>H: POST /v1/responses
226
+ H->>RC: ResponsesContext.create(app, body)
227
+ activate RC
228
+ RC->>MR: resolve(model)
229
+ MR-->>RC: { provider, model }
230
+ opt previous_response_id
231
+ RC->>SS: resolveChain(id)
232
+ SS-->>RC: session snapshot
233
+ end
234
+ RC->>REG: resolve(provider)
235
+ REG-->>RC: Provider 实例
236
+ deactivate RC
237
+
238
+ alt stream = true
239
+ H->>A: adapter.stream(ctx)
240
+ activate A
241
+ A->>PM: mapper.request.map(ctx)
242
+ A->>CC: chatClient.streamChat(req)
243
+ CC->>UP: POST (SSE)
244
+ UP-->>CC: SSE 数据块
245
+ A->>A: pipeTransform → ProviderEventToResponse
246
+ A->>A: pipeTransform → ResponseLog
247
+ A->>A: pipeTransform → ResponseSessionPersistence
248
+ deactivate A
249
+ H->>H: pipeTransform → ResponseSseEncode
250
+ H-->>C: SSE 字节流
251
+ else stream = false
252
+ H->>A: adapter.request(ctx)
253
+ activate A
254
+ A->>PM: mapper.request.map(ctx)
255
+ A->>CC: chatClient.chat(req)
256
+ CC->>UP: POST
257
+ UP-->>A: 上游响应
258
+ A->>PM: mapper.response.map(ctx, res)
259
+ A->>SS: save(session)
260
+ deactivate A
261
+ H-->>C: JSON 响应
262
+ end
215
263
  ```
216
264
 
217
265
  ## 流式管道
@@ -219,80 +267,41 @@ classDiagram
219
267
  ```mermaid
220
268
  flowchart LR
221
269
  subgraph upstream["上游提供商"]
222
- SSE["SSE 数据块<br/>(JsonServerSentEvent)"]
270
+ SSE["SSE 数据块"]
271
+ end
272
+
273
+ subgraph godex["GodeX 流式管道"]
274
+ T1["① ProviderEventToResponse"]
275
+ T2["② ResponseLog"]
276
+ T3["③ SessionPersistence"]
223
277
  end
224
278
 
225
- subgraph godex["Godex 流式管道"]
226
- T1["ProviderEventTo<br/>ResponseTransformer"]
227
- T2["ResponseSession<br/>PersistenceTransformer"]
228
- T3["ResponseSse<br/>EncodeTransformer"]
279
+ subgraph server["HTTP 响应"]
280
+ T4["④ SseEncode"]
229
281
  end
230
282
 
231
283
  subgraph client["客户端"]
232
- BYTES["SSE 字节流<br/>(text/event-stream)"]
284
+ BYTES["SSE 字节流"]
233
285
  end
234
286
 
235
- SSE -->|"pipeThrough(TransformStream)"| T1
236
- T1 -->|"逐事件 map()<br/>SSE 数据块 → ResponseStreamEvent[]"| T2
237
- T2 -->|"累积 StreamState<br/>拦截终止事件<br/>buildResponseObject()<br/>保存会话"| T3
238
- T3 -->|"序列化为 SSE 传输格式<br/>event: xxx\ndata: {...}\n\n"| BYTES
287
+ SSE -->|pipeThrough| T1
288
+ T1 -->|逐事件映射| T2
289
+ T2 -->|日志透传| T3
290
+ T3 -->|累积状态并保存会话| T4
291
+ T4 -->|序列化 SSE 格式| BYTES
239
292
 
240
293
  style upstream fill:#1a1a2e,stroke:#16213e,color:#e0e0e0
241
294
  style godex fill:#0f3460,stroke:#16213e,color:#e0e0e0
295
+ style server fill:#1c2333,stroke:#16213e,color:#e0e0e0
242
296
  style client fill:#1a1a2e,stroke:#16213e,color:#e0e0e0
243
297
  ```
244
298
 
245
- ### Transformer 职责
246
-
247
- | 阶段 | Transformer | 输入 | 输出 | 副作用 |
248
- |------|------------|------|------|--------|
249
- | 1 | `ProviderEventToResponseTransformer` | `JsonServerSentEvent<TChunk>` | `ResponseStreamEvent` | 逐事件调用 `StreamMapper.map()` |
250
- | 2 | `ResponseSessionPersistenceTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | 累积 `StreamState`,终止事件时调用 `buildResponseObject()` 并保存会话(`store=false` 时跳过) |
251
- | 3 | `ResponseSseEncodeTransformer` | `ResponseStreamEvent` | `Uint8Array`(SSE 传输格式) | 序列化为 `event:` / `data:` 行 |
252
-
253
- ## 错误体系
254
-
255
- ```mermaid
256
- classDiagram
257
- direction TB
258
-
259
- class GodexError {
260
- +name: string
261
- +code: string
262
- +status: number
263
- +context: object
264
- +toLogEntry() object
265
- }
266
-
267
- class ServerError {
268
- +status: 400-499
269
- +context: object
270
- }
271
-
272
- class AdapterError {
273
- +status: 400-499
274
- +context: 不支持的参数 / 工具 / 输入项
275
- }
276
-
277
- class ProviderError {
278
- +status: 502
279
- +context: 上游状态码 / 响应体 / 响应头
280
- }
281
-
282
- class SessionError {
283
- +status: 400-409
284
- +context: 链元数据
285
- }
286
-
287
- GodexError <|-- ServerError
288
- GodexError <|-- AdapterError
289
- GodexError <|-- ProviderError
290
- GodexError <|-- SessionError
291
-
292
- note for GodexError "基础错误,支持结构化日志。<br/>所有错误携带领域编码(如 server.request.invalid_json)。"
293
- note for ProviderError "包装上游 HTTP 失败:<br/>速率限制、超时、5xx。"
294
- note for SessionError "链式解析失败:<br/>未找到、循环、深度超限。"
295
- ```
299
+ | 阶段 | Transformer | 输入 | 输出 | 职责 |
300
+ |------|------------|------|------|------|
301
+ | | `ProviderEventToResponseTransformer` | `JsonServerSentEvent` | `ResponseStreamEvent` | 通过 `StreamMapper.map()` 映射上游 SSE 数据块 |
302
+ | ② | `ResponseLogTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | 可观测性日志 |
303
+ | | `ResponseSessionPersistenceTransformer` | `ResponseStreamEvent` | `ResponseStreamEvent` | 累积 `StreamState`,终止事件时保存会话 |
304
+ | | `ResponseSseEncodeTransformer` | `ResponseStreamEvent` | `Uint8Array` | 序列化为 `event:` / `data:` 传输格式 |
296
305
 
297
306
  ## 项目结构
298
307
 
@@ -303,36 +312,20 @@ src/
303
312
  ├── context/ ApplicationContext(DI 容器)、ResponsesContext(每请求)
304
313
  ├── adapter/ Adapter 接口、DefaultAdapter、流式 Transformer
305
314
  │ ├── mapper/ RequestMapper / ResponseMapper / StreamMapper 契约
306
- │ └── transformers/ ProviderEventResponseSSE 编码管道
315
+ │ └── transformers/ 4 阶段流式管道(映射 日志持久化 → 编码)
307
316
  ├── providers/ Provider 注册表 + 内置工厂
308
- │ └── zhipu/ 参考提供商实现:映射器、聊天客户端、工具、消息
317
+ │ └── zhipu/ 参考提供商:映射器、聊天客户端、工具、消息
309
318
  ├── resolver/ ModelResolver(模型选择器 → 提供商 + 模型)
310
- ├── server/ Bun HTTP 服务器、Router、路由(/v1/responses、/health、/v1/models)
319
+ ├── server/ Bun.serve、路由(/v1/responses、/health、/v1/models)
311
320
  ├── session/ ResponseSessionStore(内存 + SQLite)、链式解析
312
- ├── error/ GodexError 错误体系及领域编码
321
+ ├── error/ GodeXError 错误体系及领域编码
322
+ ├── tools/ 内置函数工具(local_shell、shell、apply_patch)
313
323
  ├── protocol/openai/ OpenAI 兼容类型定义
314
324
  ├── logger/ 结构化 JSON 日志
315
325
  └── e2e/ 模拟上游的端到端测试
316
326
  ```
317
327
 
318
- ## 快速开始
319
-
320
- ```bash
321
- # 安装依赖
322
- bun install
323
-
324
- # 构建独立二进制文件(当前平台)
325
- bun run build
326
-
327
- # 交互式创建配置
328
- bun run start -- init
329
-
330
- # 启动服务器(默认端口 5678)
331
- bun run dev
332
-
333
- # 或直接运行编译后的二进制文件
334
- ./platforms/darwin-arm64/bin/godex serve
335
- ```
328
+ ## 配置
336
329
 
337
330
  ### godex.yaml
338
331
 
@@ -359,115 +352,65 @@ logging:
359
352
  level: info # trace | debug | info | warn | error
360
353
  ```
361
354
 
362
- ### 添加提供商
363
-
364
- 在 `src/providers/<name>/` 中实现以下接口:
365
-
366
- | 接口 | 用途 |
367
- |------|------|
368
- | `Provider<TReq, TRes, TChunk>` | 组合 mapper + chatClient + capabilities |
369
- | `ProviderMapper<TReq, TRes, TChunk>` | request / response / stream 映射函数 |
370
- | `ChatClient<TReq, TRes, TChunk>` | `chat()` 和 `streamChat()` HTTP 调用 |
371
-
372
- 在 `src/providers/builtin.ts` 中注册工厂:
355
+ ### 模型选择
373
356
 
374
- ```ts
375
- registrar.registerFactory("myprovider", (config) =>
376
- createMyProvider(config) as Provider<unknown, unknown, unknown>
377
- );
378
357
  ```
379
-
380
- ## 使用
381
-
382
- ```bash
383
- # 安装 — 运行时无需 Bun
384
- npm install -g @ahoo-wang/godex
385
-
386
- # 交互式创建配置
387
- godex init
388
-
389
- # 启动网关
390
- godex serve
358
+ model: "gpt-4o" → 通过 default_provider 的模型映射解析
359
+ model: "zhipu/glm-4.7" → 显式指定 provider/model 选择器
360
+ model: "openai/gpt-4o" → 路由到已配置的 openai 提供商
391
361
  ```
392
362
 
393
- Godex 以**独立原生二进制文件**发布,零运行时依赖。npm 的 `postinstall` 脚本自动为您的平台选择正确的二进制文件。唯一前置条件是 Node.js >= 18(仅在 `npm install` 期间需要)。
394
-
395
- Godex 在 `http://localhost:5678` 暴露**与 OpenAI 兼容的 Responses API**(端口可配置)。将任何使用 OpenAI 协议的工具指向此端点即可:
396
-
397
- ### 搭配 Codex CLI
363
+ ### 健康检查
398
364
 
399
365
  ```bash
400
- export OPENAI_BASE_URL=http://localhost:5678/v1
401
- export OPENAI_API_KEY=any-value # Godex 不验证此值,但必须设置
402
- codex
366
+ curl http://localhost:5678/health
367
+ # {"status":"ok","providers":["zhipu"],"unsupported_providers":[]}
403
368
  ```
404
369
 
405
- ### 搭配 OpenAI SDK
370
+ ### 添加提供商
406
371
 
407
- ```ts
408
- import OpenAI from "openai";
372
+ 在 `src/providers/<name>/` 中实现三个接口:
409
373
 
410
- const client = new OpenAI({
411
- baseURL: "http://localhost:5678/v1",
412
- apiKey: "any-value", // 透传,不验证
413
- });
414
-
415
- const response = await client.responses.create({
416
- model: "gpt-4o", // 通过 godex.yaml 的 models 表映射为 glm-4.7
417
- input: "Hello!",
418
- });
419
- ```
374
+ | 接口 | 用途 |
375
+ |------|------|
376
+ | `Provider` | 组合 mapper + chatClient + capabilities |
377
+ | `ProviderMapper` | request / response / stream 映射函数 |
378
+ | `ChatClient` | `chat()` 和 `streamChat()` HTTP 调用 |
420
379
 
421
- ### 模型选择
380
+ `src/providers/builtin.ts` 中注册工厂:
422
381
 
423
- ```
424
- model: "gpt-4o" 通过 default_provider 的模型映射解析
425
- model: "zhipu/glm-4.7" → 显式指定 provider/model 选择器
426
- model: "openai/gpt-4o" → 路由到已配置的 openai 提供商
382
+ ```ts
383
+ registrar.registerFactory("myprovider", (config) =>
384
+ createMyProvider(config) as Provider<unknown, unknown, unknown>
385
+ );
427
386
  ```
428
387
 
429
- `godex.yaml` 中的 `models` 映射表可将标准模型名称转换为提供商原生名称 — 客户端无需修改代码。
430
-
431
- ### 健康检查
388
+ ## 开发
432
389
 
433
390
  ```bash
434
- curl http://localhost:5678/health
435
- # {"status":"ok","providers":["zhipu"],"unsupported_providers":[]}
391
+ bun install # 安装依赖
392
+ bun run dev # 热重载开发服务器(端口 13145)
393
+ bun run test # 单元 + 集成测试
394
+ bun run test:e2e # 模拟上游的端到端测试
395
+ bun run build # 为当前平台编译原生二进制
396
+ bun run check # typecheck + lint + test
397
+ bun run ci # 完整 CI 流水线
436
398
  ```
437
399
 
438
400
  ## 发布
439
401
 
440
- 主包 `@ahoo-wang/godex` 是一个轻量外壳。原生二进制文件以平台特定的可选依赖发布:
402
+ `@ahoo-wang/godex` 是一个轻量 npm 外壳。原生二进制文件以平台特定的可选依赖发布:
441
403
 
442
404
  ```
443
- @ahoo-wang/godex(包装包,0 运行时依赖)
444
- ├── engines: { node: ">=18.0.0" } ← 仅用于 postinstall
445
- ├── postinstall: scripts/install.cjs 检测平台,链接二进制文件
446
- └── optionalDependencies:
447
- ├── @ahoo-wang/godex-darwin-arm64 macOS Apple Silicon
448
- ├── @ahoo-wang/godex-darwin-x64 macOS Intel
449
- ├── @ahoo-wang/godex-linux-x64 Linux x86_64
450
- ├── @ahoo-wang/godex-linux-arm64 ← Linux ARM64
451
- ├── @ahoo-wang/godex-win32-x64 ← Windows x86_64
452
- └── @ahoo-wang/godex-win32-arm64 ← Windows ARM64
453
-
454
- # 发布流程:
455
- # 1. 将 GitHub 仓库设为公开,配置 NPM_TOKEN,然后推送发布提交。
456
- # 2. 创建标签为 vX.Y.Z 的 GitHub Release。
457
- # 3. Release 工作流构建所有平台二进制文件。
458
- # 4. Release 工作流上传二进制压缩包和 SHA256SUMS 到 Release Assets。
459
- # 5. Release 工作流先发布平台包,再发布 @ahoo-wang/godex。
405
+ @ahoo-wang/godex
406
+ ├── @ahoo-wang/godex-darwin-arm64 ← macOS Apple Silicon
407
+ ├── @ahoo-wang/godex-darwin-x64 macOS Intel
408
+ ├── @ahoo-wang/godex-linux-x64 ← Linux x86_64
409
+ ├── @ahoo-wang/godex-linux-arm64 Linux ARM64
410
+ ├── @ahoo-wang/godex-win32-x64 Windows x86_64
411
+ └── @ahoo-wang/godex-win32-arm64 Windows ARM64
460
412
  ```
461
413
 
462
- ## 命令
414
+ ## 许可证
463
415
 
464
- ```bash
465
- bun run dev # 热重载开发服务器,端口 13145
466
- bun run build # 为当前平台编译原生二进制
467
- bun run compile:all # 本地交叉编译全部 6 个平台
468
- bun run test # 单元 + 集成测试
469
- bun run test:e2e # 模拟上游的端到端测试
470
- bun run typecheck # tsc --noEmit
471
- bun run lint # Biome 检查
472
- bun run ci # 完整 CI 流水线
473
- ```
416
+ [Apache License 2.0](LICENSE)
package/bin/godex CHANGED
@@ -59,7 +59,7 @@ function findBinary() {
59
59
  const binary = findBinary();
60
60
  if (!binary) {
61
61
  console.error(
62
- "godex: binary not found. Re-run npm install or build from source.",
62
+ "GodeX: binary not found. Re-run npm install or build from source.",
63
63
  );
64
64
  process.exit(1);
65
65
  }
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@ahoo-wang/godex",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "Make every model a Codex engine through an OpenAI-compatible Responses API gateway",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/Ahoo-Wang/Godex.git"
8
+ "url": "git+https://github.com/Ahoo-Wang/GodeX.git"
9
9
  },
10
- "homepage": "https://github.com/Ahoo-Wang/Godex#readme",
10
+ "homepage": "https://github.com/Ahoo-Wang/GodeX#readme",
11
11
  "bugs": {
12
- "url": "https://github.com/Ahoo-Wang/Godex/issues"
12
+ "url": "https://github.com/Ahoo-Wang/GodeX/issues"
13
13
  },
14
14
  "publishConfig": {
15
15
  "access": "public"
@@ -45,12 +45,12 @@
45
45
  "node": ">=18.0.0"
46
46
  },
47
47
  "optionalDependencies": {
48
- "@ahoo-wang/godex-darwin-arm64": "0.0.16",
49
- "@ahoo-wang/godex-darwin-x64": "0.0.16",
50
- "@ahoo-wang/godex-linux-x64": "0.0.16",
51
- "@ahoo-wang/godex-linux-arm64": "0.0.16",
52
- "@ahoo-wang/godex-win32-x64": "0.0.16",
53
- "@ahoo-wang/godex-win32-arm64": "0.0.16"
48
+ "@ahoo-wang/godex-darwin-arm64": "0.0.17",
49
+ "@ahoo-wang/godex-darwin-x64": "0.0.17",
50
+ "@ahoo-wang/godex-linux-x64": "0.0.17",
51
+ "@ahoo-wang/godex-linux-arm64": "0.0.17",
52
+ "@ahoo-wang/godex-win32-x64": "0.0.17",
53
+ "@ahoo-wang/godex-win32-arm64": "0.0.17"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@ahoo-wang/fetcher": "^3.16.8",
@@ -65,13 +65,13 @@ function link(src, dest) {
65
65
 
66
66
  const platform = detectPlatform();
67
67
  if (!platform) {
68
- console.log("godex: skipping binary setup — unsupported platform", process.platform, process.arch);
68
+ console.log("GodeX: skipping binary setup — unsupported platform", process.platform, process.arch);
69
69
  process.exit(0);
70
70
  }
71
71
 
72
72
  const binary = findBinary("@ahoo-wang/godex-" + platform, platform);
73
73
  if (!binary) {
74
- console.log("godex: no prebuilt binary for", platform, "— dev mode, skipping");
74
+ console.log("GodeX: no prebuilt binary for", platform, "— dev mode, skipping");
75
75
  process.exit(0);
76
76
  }
77
77
 
@@ -80,4 +80,4 @@ if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
80
80
  const dest = path.join(destDir, "godex-binary");
81
81
 
82
82
  link(binary, dest);
83
- console.log("godex: installed native binary for", platform);
83
+ console.log("GodeX: installed native binary for", platform);