@koreanhole/opencodex 3.2.0 → 3.2.1

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
@@ -109,6 +109,19 @@ npm install -g @koreanhole/opencodex@latest
109
109
 
110
110
  Automatic upstream update checks are disabled for the delegated runtime.
111
111
 
112
+ ## Release
113
+
114
+ After every required verification and live E2E gate passes, bump the package to
115
+ the next semver patch version, commit and push `main`, then publish:
116
+
117
+ ```bash
118
+ npm publish --access public
119
+ ```
120
+
121
+ Never force-publish or reuse an already published version. If any gate is
122
+ missing, failing, or blocked, stop before commit, push, or publish and report
123
+ the blocker.
124
+
112
125
  ## Security boundary
113
126
 
114
127
  - Cline does not receive filesystem or terminal capability from the proxy.
@@ -136,14 +149,16 @@ bun run typecheck
136
149
  bun test tests
137
150
  bun run test:e2e
138
151
  bun run test:e2e:gemini
152
+ bun run test:e2e:gemini-slow
139
153
  bun run privacy:scan
140
154
  bun run structure:check
141
155
  ```
142
156
 
143
157
  `test:e2e:gemini` starts the original runtime and the adapter gateway, then uses
144
- a real Codex client and a real authenticated Gemini CLI. It fails, not skips, on
145
- a missing binary, authentication, Flash Lite model, response marker, or child
146
- cleanup assertion.
158
+ a real Codex client and a real authenticated Gemini CLI. It runs both the minimal
159
+ completion and a 12-second pre-event slow-start regression. It fails, not skips,
160
+ on a missing binary, authentication, Flash Lite model, response marker, timeout,
161
+ or child cleanup assertion. `test:e2e:gemini-slow` runs only the slow-start case.
147
162
 
148
163
  Optional overrides are `CLINE_BIN_PATH`, `GEMINI_BIN_PATH`, and
149
164
  `CODEX_BIN_PATH`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koreanhole/opencodex",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Full opencodex runtime with Cline JSON and Gemini CLI headless provider adapters",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -30,7 +30,8 @@
30
30
  "start": "bun run src/cli/entry.ts start",
31
31
  "test": "bun test tests",
32
32
  "test:e2e": "bun test tests/e2e/cline-only.test.ts",
33
- "test:e2e:gemini": "bun test ./tests/e2e/gemini-codex.live.ts -t \"one minimal Flash Lite completion\"",
33
+ "test:e2e:gemini": "bun test ./tests/e2e/gemini-codex.live.ts -t \"full-stack:\"",
34
+ "test:e2e:gemini-slow": "bun test ./tests/e2e/gemini-codex.live.ts -t \"full-stack: a slow Gemini start\"",
34
35
  "test:e2e:gemini-tool": "bun test ./tests/e2e/gemini-codex.live.ts -t \"one short native Gemini edit\"",
35
36
  "test:e2e:gemini-multiturn": "bun test ./tests/e2e/gemini-codex.live.ts -t \"reuses a live Codex multi-turn session\"",
36
37
  "test:e2e:gemini-previous-response": "bun test ./tests/e2e/gemini-codex.live.ts -t \"loads a live Gemini session for a previous_response_id continuation\"",
@@ -58,7 +58,7 @@ export function startAdapterGateway(options: AdapterGatewayOptions): AdapterGate
58
58
  }
59
59
  }
60
60
  let server: ReturnType<typeof Bun.serve>;
61
- const handleRequest = async (req: Request): Promise<Response> => {
61
+ const handleRequest = async (req: Request, bunServer: Bun.Server<unknown>): Promise<Response> => {
62
62
  const url = new URL(req.url);
63
63
  if (url.pathname === "/healthz") {
64
64
  return Response.json({
@@ -84,6 +84,9 @@ export function startAdapterGateway(options: AdapterGatewayOptions): AdapterGate
84
84
  error: { message: "CLI adapter is unavailable.", type: "provider_unavailable" },
85
85
  }, { status: 502 });
86
86
  }
87
+ if (req.method === "POST" && (match.path === "/v1/responses" || match.path === "/v1/messages")) {
88
+ try { bunServer.timeout(req, 0); } catch { /* request already closed */ }
89
+ }
87
90
  const headers = new Headers(req.headers);
88
91
  headers.delete("authorization");
89
92
  headers.delete("x-api-key");
@@ -107,6 +110,7 @@ export function startAdapterGateway(options: AdapterGatewayOptions): AdapterGate
107
110
  server = Bun.serve({
108
111
  hostname: "127.0.0.1",
109
112
  port: 0,
113
+ idleTimeout: options.idleTimeoutSeconds ?? 255,
110
114
  fetch: handleRequest,
111
115
  });
112
116
  return {
@@ -31,6 +31,8 @@ export interface ServerOptions {
31
31
  geminiDeps?: GeminiTurnDeps;
32
32
  geminiPackageRoot?: string;
33
33
  providerScope?: ProviderId;
34
+ /** Test seam for exercising Bun's idle timeout; production uses 255 seconds. */
35
+ idleTimeoutSeconds?: number;
34
36
  onWebSocketRequest?: (observation: WebSocketRequestObservation) => void;
35
37
  }
36
38
 
@@ -148,6 +150,9 @@ export function startServer(config: RuntimeConfig | number, options: ServerOptio
148
150
  return new Response("WebSocket upgrade failed.", { status: 400 });
149
151
  }
150
152
  try {
153
+ if (req.method === "POST" && (url.pathname === "/v1/responses" || url.pathname === "/v1/messages")) {
154
+ try { bunServer.timeout(req, 0); } catch { /* request already closed */ }
155
+ }
151
156
  if (req.method === "GET" && url.pathname === "/v1/models") {
152
157
  const known = await discoverModels(env, options.clineDeps, options.geminiDeps, options.geminiPackageRoot, options.providerScope);
153
158
  return Response.json({ object: "list", data: modelList(known, options.providerScope) });
@@ -193,7 +198,7 @@ export function startServer(config: RuntimeConfig | number, options: ServerOptio
193
198
  server = Bun.serve({
194
199
  hostname: config.host,
195
200
  port: config.port,
196
- idleTimeout: 255,
201
+ idleTimeout: options.idleTimeoutSeconds ?? 255,
197
202
  fetch,
198
203
  websocket: createWebSocketHandlers(websocketContext),
199
204
  });