@opengeni/codex 2.0.1 → 2.0.2-canary.38789554071001
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/dist/index.d.ts +1 -0
- package/dist/index.js +319 -0
- package/dist/index.js.map +1 -1
- package/dist/responses-websocket.d.ts +33 -0
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/responses-websocket.ts +428 -0
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./api-client.js";
|
|
|
10
10
|
export * from "./request-context.js";
|
|
11
11
|
export * from "./response-timeout.js";
|
|
12
12
|
export * from "./fetch.js";
|
|
13
|
+
export * from "./responses-websocket.js";
|
|
13
14
|
export * from "./mcp-sanitize.js";
|
|
14
15
|
export * from "./model-output-truncation.js";
|
|
15
16
|
export * from "./opaque-artifact.js";
|
package/dist/index.js
CHANGED
|
@@ -2037,6 +2037,322 @@ function inspectCodexSseBlock(block, source, onSemanticTerminal, onOutput) {
|
|
|
2037
2037
|
return false;
|
|
2038
2038
|
}
|
|
2039
2039
|
|
|
2040
|
+
// src/responses-websocket.ts
|
|
2041
|
+
var CODEX_RESPONSES_WEBSOCKET_BETA = "responses_websockets=2026-02-06";
|
|
2042
|
+
var DEFAULT_IDLE_MS = 5 * 6e4;
|
|
2043
|
+
var DEFAULT_MAX_AGE_MS = 45 * 6e4;
|
|
2044
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
2045
|
+
var DEFAULT_HANDSHAKE_BACKOFF_MS = 5 * 6e4;
|
|
2046
|
+
var PER_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
2047
|
+
"accept",
|
|
2048
|
+
"content-type",
|
|
2049
|
+
"content-length",
|
|
2050
|
+
"idempotency-key",
|
|
2051
|
+
"openai-beta"
|
|
2052
|
+
]);
|
|
2053
|
+
var TERMINAL_EVENTS = /* @__PURE__ */ new Set([
|
|
2054
|
+
"response.completed",
|
|
2055
|
+
"response.done",
|
|
2056
|
+
"response.failed",
|
|
2057
|
+
"response.incomplete",
|
|
2058
|
+
"response.error",
|
|
2059
|
+
"error"
|
|
2060
|
+
]);
|
|
2061
|
+
var RATE_LIMITS_EVENT = "codex.rate_limits";
|
|
2062
|
+
var METADATA_EVENT = "codex.response.metadata";
|
|
2063
|
+
var TIMING_EVENT = "responsesapi.websocket_timing";
|
|
2064
|
+
var HEADER_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
2065
|
+
function bunWebSocketFactory() {
|
|
2066
|
+
const runtime = globalThis;
|
|
2067
|
+
if (!runtime.Bun || typeof runtime.WebSocket !== "function") return null;
|
|
2068
|
+
const Socket = runtime.WebSocket;
|
|
2069
|
+
return (url, headers) => new Socket(url, { headers });
|
|
2070
|
+
}
|
|
2071
|
+
function eventData(data) {
|
|
2072
|
+
if (typeof data === "string") return data;
|
|
2073
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
2074
|
+
if (ArrayBuffer.isView(data)) return new TextDecoder().decode(data);
|
|
2075
|
+
return null;
|
|
2076
|
+
}
|
|
2077
|
+
function codexRateLimitHeaders(event) {
|
|
2078
|
+
const limits = event.rate_limits;
|
|
2079
|
+
if (!limits || typeof limits !== "object") return [];
|
|
2080
|
+
const headers = [];
|
|
2081
|
+
for (const slot of ["primary", "secondary"]) {
|
|
2082
|
+
const window = limits[slot];
|
|
2083
|
+
if (!window || typeof window !== "object") continue;
|
|
2084
|
+
const value = window;
|
|
2085
|
+
const number3 = (field) => typeof value[field] === "number" && Number.isFinite(value[field]) ? Math.round(value[field]) : null;
|
|
2086
|
+
const used = number3("used_percent");
|
|
2087
|
+
const minutes = number3("window_minutes");
|
|
2088
|
+
const resetAfter = number3("reset_after_seconds");
|
|
2089
|
+
const resetAt = number3("reset_at");
|
|
2090
|
+
if (used !== null) headers.push([`x-codex-${slot}-used-percent`, String(used)]);
|
|
2091
|
+
if (minutes !== null) {
|
|
2092
|
+
headers.push([`x-codex-${slot}-window-minutes`, String(minutes)]);
|
|
2093
|
+
headers.push([`x-codex-${slot}-limit-window-seconds`, String(minutes * 60)]);
|
|
2094
|
+
}
|
|
2095
|
+
if (resetAfter !== null)
|
|
2096
|
+
headers.push([`x-codex-${slot}-reset-after-seconds`, String(resetAfter)]);
|
|
2097
|
+
if (resetAt !== null) headers.push([`x-codex-${slot}-reset-at`, String(resetAt)]);
|
|
2098
|
+
}
|
|
2099
|
+
return headers;
|
|
2100
|
+
}
|
|
2101
|
+
function metadataHeaders(event) {
|
|
2102
|
+
const headers = event.headers;
|
|
2103
|
+
if (!headers || typeof headers !== "object") return [];
|
|
2104
|
+
return Object.entries(headers).flatMap(
|
|
2105
|
+
([name, value]) => HEADER_NAME.test(name) && typeof value === "string" && !/[\r\n]/.test(value) ? [[name.toLowerCase(), value]] : []
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
function sseBlock(event) {
|
|
2109
|
+
const type = typeof event.type === "string" ? event.type : "message";
|
|
2110
|
+
return new TextEncoder().encode(`event: ${type}
|
|
2111
|
+
data: ${JSON.stringify(event)}
|
|
2112
|
+
|
|
2113
|
+
`);
|
|
2114
|
+
}
|
|
2115
|
+
function requestUrl(input) {
|
|
2116
|
+
if (typeof input === "string") return input;
|
|
2117
|
+
if (input instanceof URL) return input.toString();
|
|
2118
|
+
return null;
|
|
2119
|
+
}
|
|
2120
|
+
function codexResponsesTransport(http, options = {}) {
|
|
2121
|
+
const createSocket = options.createSocket === void 0 ? bunWebSocketFactory() : options.createSocket;
|
|
2122
|
+
if (!createSocket) return http;
|
|
2123
|
+
const idleMs = options.idleMs ?? DEFAULT_IDLE_MS;
|
|
2124
|
+
const maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
2125
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
2126
|
+
const handshakeBackoffMs = options.handshakeBackoffMs ?? DEFAULT_HANDSHAKE_BACKOFF_MS;
|
|
2127
|
+
const now = options.now ?? Date.now;
|
|
2128
|
+
const pool = /* @__PURE__ */ new Map();
|
|
2129
|
+
const backoffUntil = /* @__PURE__ */ new Map();
|
|
2130
|
+
const retire = (connection) => {
|
|
2131
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
2132
|
+
connection.idleTimer = null;
|
|
2133
|
+
if (pool.get(connection.key) === connection) pool.delete(connection.key);
|
|
2134
|
+
if (!connection.closed) {
|
|
2135
|
+
connection.closed = true;
|
|
2136
|
+
try {
|
|
2137
|
+
connection.socket.close(1e3, "done");
|
|
2138
|
+
} catch {
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
};
|
|
2142
|
+
const release = (connection) => {
|
|
2143
|
+
connection.busy = false;
|
|
2144
|
+
connection.onEvent = null;
|
|
2145
|
+
connection.onClose = null;
|
|
2146
|
+
if (connection.closed) return;
|
|
2147
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
2148
|
+
connection.idleTimer = setTimeout(() => retire(connection), idleMs);
|
|
2149
|
+
connection.idleTimer.unref?.();
|
|
2150
|
+
};
|
|
2151
|
+
const connect = (url, key, headers) => new Promise((resolve, reject) => {
|
|
2152
|
+
let socket;
|
|
2153
|
+
try {
|
|
2154
|
+
socket = createSocket(url, headers);
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
reject(error);
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
const connection = {
|
|
2160
|
+
key,
|
|
2161
|
+
socket,
|
|
2162
|
+
openedAt: now(),
|
|
2163
|
+
busy: false,
|
|
2164
|
+
closed: false,
|
|
2165
|
+
idleTimer: null,
|
|
2166
|
+
onEvent: null,
|
|
2167
|
+
onClose: null
|
|
2168
|
+
};
|
|
2169
|
+
let opened = false;
|
|
2170
|
+
const timer = setTimeout(() => {
|
|
2171
|
+
if (opened) return;
|
|
2172
|
+
connection.closed = true;
|
|
2173
|
+
try {
|
|
2174
|
+
socket.close();
|
|
2175
|
+
} catch {
|
|
2176
|
+
}
|
|
2177
|
+
reject(new Error("Codex response WebSocket handshake timed out"));
|
|
2178
|
+
}, connectTimeoutMs);
|
|
2179
|
+
socket.onopen = () => {
|
|
2180
|
+
opened = true;
|
|
2181
|
+
clearTimeout(timer);
|
|
2182
|
+
resolve(connection);
|
|
2183
|
+
};
|
|
2184
|
+
socket.onerror = () => {
|
|
2185
|
+
};
|
|
2186
|
+
socket.onclose = (event) => {
|
|
2187
|
+
clearTimeout(timer);
|
|
2188
|
+
connection.closed = true;
|
|
2189
|
+
if (pool.get(key) === connection) pool.delete(key);
|
|
2190
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
2191
|
+
if (!opened) {
|
|
2192
|
+
reject(
|
|
2193
|
+
new Error(`Codex response WebSocket handshake failed (${event?.code ?? "closed"})`)
|
|
2194
|
+
);
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
connection.onClose?.(
|
|
2198
|
+
new Error(
|
|
2199
|
+
`Codex response WebSocket closed before the response finished (${event?.code ?? "closed"})`
|
|
2200
|
+
)
|
|
2201
|
+
);
|
|
2202
|
+
};
|
|
2203
|
+
socket.onmessage = (message) => {
|
|
2204
|
+
const text = eventData(message.data);
|
|
2205
|
+
if (text === null) return;
|
|
2206
|
+
let parsed;
|
|
2207
|
+
try {
|
|
2208
|
+
parsed = JSON.parse(text);
|
|
2209
|
+
} catch {
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2213
|
+
connection.onEvent?.(parsed);
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
});
|
|
2217
|
+
return async (input, init) => {
|
|
2218
|
+
const url = requestUrl(input);
|
|
2219
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
2220
|
+
const body = init?.body;
|
|
2221
|
+
if (!url || method !== "POST" || typeof body !== "string" || !/^https:\/\/[^/]+\/.*\/codex\/responses(?:\?|$)/.test(url)) {
|
|
2222
|
+
return await http(input, init);
|
|
2223
|
+
}
|
|
2224
|
+
let payload;
|
|
2225
|
+
try {
|
|
2226
|
+
const parsed = JSON.parse(body);
|
|
2227
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
|
|
2228
|
+
payload = parsed;
|
|
2229
|
+
} catch {
|
|
2230
|
+
return await http(input, init);
|
|
2231
|
+
}
|
|
2232
|
+
if (payload.stream !== true) return await http(input, init);
|
|
2233
|
+
const handshake = {};
|
|
2234
|
+
for (const [name, value] of new Headers(init?.headers).entries()) {
|
|
2235
|
+
if (!PER_REQUEST_HEADERS.has(name)) handshake[name] = value;
|
|
2236
|
+
}
|
|
2237
|
+
handshake["openai-beta"] = CODEX_RESPONSES_WEBSOCKET_BETA;
|
|
2238
|
+
const wsUrl = url.replace(/^https:/, "wss:");
|
|
2239
|
+
const key = JSON.stringify([wsUrl, Object.entries(handshake).sort()]);
|
|
2240
|
+
if ((backoffUntil.get(key) ?? 0) > now()) return await http(input, init);
|
|
2241
|
+
const signal = init?.signal ?? null;
|
|
2242
|
+
signal?.throwIfAborted();
|
|
2243
|
+
let connection = pool.get(key);
|
|
2244
|
+
if (connection && (connection.closed || now() - connection.openedAt > maxAgeMs)) {
|
|
2245
|
+
if (!connection.busy) retire(connection);
|
|
2246
|
+
connection = void 0;
|
|
2247
|
+
}
|
|
2248
|
+
if (connection?.busy) return await http(input, init);
|
|
2249
|
+
if (!connection) {
|
|
2250
|
+
try {
|
|
2251
|
+
connection = await connect(wsUrl, key, handshake);
|
|
2252
|
+
} catch {
|
|
2253
|
+
backoffUntil.set(key, now() + handshakeBackoffMs);
|
|
2254
|
+
return await http(input, init);
|
|
2255
|
+
}
|
|
2256
|
+
if (pool.has(key)) {
|
|
2257
|
+
const existing = pool.get(key);
|
|
2258
|
+
if (!existing.busy && !existing.closed) {
|
|
2259
|
+
retire(connection);
|
|
2260
|
+
connection = existing;
|
|
2261
|
+
} else {
|
|
2262
|
+
pool.set(key, connection);
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
pool.set(key, connection);
|
|
2266
|
+
}
|
|
2267
|
+
if (connection.busy) return await http(input, init);
|
|
2268
|
+
}
|
|
2269
|
+
const active = connection;
|
|
2270
|
+
active.busy = true;
|
|
2271
|
+
if (active.idleTimer) clearTimeout(active.idleTimer);
|
|
2272
|
+
active.idleTimer = null;
|
|
2273
|
+
return await new Promise((resolve, reject) => {
|
|
2274
|
+
const headers = new Headers({ "content-type": "text/event-stream" });
|
|
2275
|
+
let controller = null;
|
|
2276
|
+
let started = false;
|
|
2277
|
+
let finished = false;
|
|
2278
|
+
const pending = [];
|
|
2279
|
+
const finish = (error) => {
|
|
2280
|
+
if (finished) return;
|
|
2281
|
+
finished = true;
|
|
2282
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2283
|
+
if (error !== void 0) {
|
|
2284
|
+
retire(active);
|
|
2285
|
+
active.busy = false;
|
|
2286
|
+
if (started) controller?.error(error);
|
|
2287
|
+
else reject(error);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
release(active);
|
|
2291
|
+
try {
|
|
2292
|
+
controller?.close();
|
|
2293
|
+
} catch {
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
const onAbort = () => finish(signal?.reason ?? new Error("Model request aborted"));
|
|
2297
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2298
|
+
const stream = new ReadableStream({
|
|
2299
|
+
start(streamController) {
|
|
2300
|
+
controller = streamController;
|
|
2301
|
+
for (const block of pending.splice(0)) streamController.enqueue(block);
|
|
2302
|
+
if (finished) streamController.close();
|
|
2303
|
+
},
|
|
2304
|
+
cancel() {
|
|
2305
|
+
if (!finished) finish(new Error("Model response stream cancelled"));
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
const begin = (status = 200, errorBody) => {
|
|
2309
|
+
started = true;
|
|
2310
|
+
if (errorBody !== void 0) {
|
|
2311
|
+
headers.set("content-type", "application/json");
|
|
2312
|
+
resolve(new Response(errorBody, { status, headers }));
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
resolve(new Response(stream, { status, headers }));
|
|
2316
|
+
};
|
|
2317
|
+
active.onClose = (error) => finish(error);
|
|
2318
|
+
active.onEvent = (event) => {
|
|
2319
|
+
if (finished) return;
|
|
2320
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
2321
|
+
if (type === RATE_LIMITS_EVENT) {
|
|
2322
|
+
if (!started)
|
|
2323
|
+
for (const [name, value] of codexRateLimitHeaders(event)) headers.set(name, value);
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
if (type === METADATA_EVENT) {
|
|
2327
|
+
if (!started)
|
|
2328
|
+
for (const [name, value] of metadataHeaders(event)) headers.set(name, value);
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
if (type === TIMING_EVENT) return;
|
|
2332
|
+
if (!started && type === "error" && typeof event.status === "number") {
|
|
2333
|
+
for (const [name, value] of metadataHeaders(event)) headers.set(name, value);
|
|
2334
|
+
const status = event.status >= 400 && event.status < 600 ? event.status : 500;
|
|
2335
|
+
begin(status, JSON.stringify({ error: event.error ?? null }));
|
|
2336
|
+
finished = true;
|
|
2337
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2338
|
+
release(active);
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
if (!started) begin();
|
|
2342
|
+
const block = sseBlock(event);
|
|
2343
|
+
if (controller) controller.enqueue(block);
|
|
2344
|
+
else pending.push(block);
|
|
2345
|
+
if (TERMINAL_EVENTS.has(type)) finish();
|
|
2346
|
+
};
|
|
2347
|
+
try {
|
|
2348
|
+
active.socket.send(JSON.stringify({ ...payload, type: "response.create" }));
|
|
2349
|
+
} catch (error) {
|
|
2350
|
+
finish(error);
|
|
2351
|
+
}
|
|
2352
|
+
});
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2040
2356
|
// src/mcp-sanitize.ts
|
|
2041
2357
|
var VALID_TOOL_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
2042
2358
|
var MAX_TOOL_NAME_LEN = 64;
|
|
@@ -3461,6 +3777,7 @@ export {
|
|
|
3461
3777
|
CODEX_REQUEST_ID_HEADER,
|
|
3462
3778
|
CODEX_REQUEST_MODEL_HEADER,
|
|
3463
3779
|
CODEX_RESPONSES_BASE,
|
|
3780
|
+
CODEX_RESPONSES_WEBSOCKET_BETA,
|
|
3464
3781
|
CODEX_RESPONSE_HEADERS_TIMEOUT_MS,
|
|
3465
3782
|
CODEX_RESPONSE_NO_BYTE_RETRIES,
|
|
3466
3783
|
CODEX_RESPONSE_RETRY_BACKOFF_MS,
|
|
@@ -3505,7 +3822,9 @@ export {
|
|
|
3505
3822
|
codexPlanEntitlementLost,
|
|
3506
3823
|
codexPlanIsUpgrade,
|
|
3507
3824
|
codexPlanKey,
|
|
3825
|
+
codexRateLimitHeaders,
|
|
3508
3826
|
codexRequestStorage,
|
|
3827
|
+
codexResponsesTransport,
|
|
3509
3828
|
codexSubscriptionFetch,
|
|
3510
3829
|
codexSubscriptionHeaders,
|
|
3511
3830
|
codexUsageConfirmsQuotaAvailable,
|