@opengeni/codex 3.3.0 → 3.4.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/dist/index.js +589 -522
- package/dist/index.js.map +1 -1
- package/dist/request-context.d.ts +3 -0
- package/dist/responses-websocket.d.ts +25 -0
- package/package.json +3 -3
- package/src/fetch.ts +6 -0
- package/src/request-context.ts +3 -0
- package/src/responses-websocket.ts +113 -13
package/dist/index.js
CHANGED
|
@@ -873,234 +873,609 @@ function opaqueProviderArtifactFingerprints(input) {
|
|
|
873
873
|
});
|
|
874
874
|
}
|
|
875
875
|
|
|
876
|
-
// src/
|
|
877
|
-
|
|
876
|
+
// src/responses-websocket.ts
|
|
877
|
+
var CODEX_RESPONSES_WEBSOCKET_BETA = "responses_websockets=2026-02-06";
|
|
878
|
+
var CODEX_TRANSPORT_HEADER = "x-opengeni-codex-transport";
|
|
879
|
+
var HTTP_REASONS = /* @__PURE__ */ new Set([
|
|
880
|
+
"not_eligible",
|
|
881
|
+
"connection_busy",
|
|
882
|
+
"handshake_backoff",
|
|
883
|
+
"handshake_failed",
|
|
884
|
+
"unavailable"
|
|
885
|
+
]);
|
|
886
|
+
function formatCodexRequestTransport(transport) {
|
|
887
|
+
return transport.kind === "websocket" ? `websocket; connection=${transport.connection}` : `http; reason=${transport.reason}`;
|
|
888
|
+
}
|
|
889
|
+
function parseCodexRequestTransport(value) {
|
|
890
|
+
if (!value) return null;
|
|
891
|
+
const [kind, ...parameters] = value.split(";").map((part) => part.trim());
|
|
892
|
+
const fields = new Map(
|
|
893
|
+
parameters.map((parameter) => {
|
|
894
|
+
const index = parameter.indexOf("=");
|
|
895
|
+
return [parameter.slice(0, index), parameter.slice(index + 1)];
|
|
896
|
+
})
|
|
897
|
+
);
|
|
898
|
+
if (kind === "websocket") {
|
|
899
|
+
const connection = fields.get("connection");
|
|
900
|
+
return connection === "new" || connection === "reused" ? { kind: "websocket", connection } : null;
|
|
901
|
+
}
|
|
902
|
+
if (kind === "http") {
|
|
903
|
+
const reason = fields.get("reason");
|
|
904
|
+
return reason && HTTP_REASONS.has(reason) ? {
|
|
905
|
+
kind: "http",
|
|
906
|
+
reason
|
|
907
|
+
} : null;
|
|
908
|
+
}
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
function withTransport(response, transport) {
|
|
912
|
+
const value = formatCodexRequestTransport(transport);
|
|
878
913
|
try {
|
|
879
|
-
|
|
914
|
+
response.headers.set(CODEX_TRANSPORT_HEADER, value);
|
|
915
|
+
return response;
|
|
880
916
|
} catch {
|
|
917
|
+
const headers = new Headers(response.headers);
|
|
918
|
+
headers.set(CODEX_TRANSPORT_HEADER, value);
|
|
919
|
+
return new Response(response.body, {
|
|
920
|
+
status: response.status,
|
|
921
|
+
statusText: response.statusText,
|
|
922
|
+
headers
|
|
923
|
+
});
|
|
881
924
|
}
|
|
882
925
|
}
|
|
883
|
-
var
|
|
884
|
-
var
|
|
885
|
-
var
|
|
886
|
-
var
|
|
887
|
-
var
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
926
|
+
var DEFAULT_IDLE_MS = 5 * 6e4;
|
|
927
|
+
var DEFAULT_MAX_AGE_MS = 45 * 6e4;
|
|
928
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
|
|
929
|
+
var DEFAULT_HANDSHAKE_BACKOFF_MS = 5 * 6e4;
|
|
930
|
+
var PER_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
931
|
+
"accept",
|
|
932
|
+
"content-type",
|
|
933
|
+
"content-length",
|
|
934
|
+
"idempotency-key",
|
|
935
|
+
"openai-beta"
|
|
936
|
+
]);
|
|
937
|
+
var TERMINAL_EVENTS = /* @__PURE__ */ new Set([
|
|
938
|
+
"response.completed",
|
|
939
|
+
"response.done",
|
|
940
|
+
"response.failed",
|
|
941
|
+
"response.incomplete",
|
|
942
|
+
"response.error",
|
|
943
|
+
"error"
|
|
944
|
+
]);
|
|
945
|
+
var RATE_LIMITS_EVENT = "codex.rate_limits";
|
|
946
|
+
var METADATA_EVENT = "codex.response.metadata";
|
|
947
|
+
var TIMING_EVENT = "responsesapi.websocket_timing";
|
|
948
|
+
var HEADER_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
949
|
+
function bunWebSocketFactory() {
|
|
950
|
+
const runtime = globalThis;
|
|
951
|
+
if (!runtime.Bun || typeof runtime.WebSocket !== "function") return null;
|
|
952
|
+
const Socket = runtime.WebSocket;
|
|
953
|
+
return (url, headers) => new Socket(url, { headers });
|
|
898
954
|
}
|
|
899
|
-
function
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
current = value.cause;
|
|
905
|
-
}
|
|
906
|
-
return false;
|
|
955
|
+
function eventData(data) {
|
|
956
|
+
if (typeof data === "string") return data;
|
|
957
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
958
|
+
if (ArrayBuffer.isView(data)) return new TextDecoder().decode(data);
|
|
959
|
+
return null;
|
|
907
960
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
if (!
|
|
911
|
-
|
|
912
|
-
for (
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
typeof value.type === "string" ? value.type : "",
|
|
926
|
-
typeof body?.type === "string" ? body.type : ""
|
|
927
|
-
].join(" ").toLowerCase();
|
|
928
|
-
const unsupportedFieldShape = /(?:invalid value|supported values?|unsupported|unknown (?:field|parameter|value))/.test(
|
|
929
|
-
message
|
|
930
|
-
);
|
|
931
|
-
if (status === 400 && !unsupportedFieldShape && /(?:encrypted[_ ]content|encrypted reasoning|reasoning artifact)/.test(message) && /(?:decrypt(?:ed|ion)?|could not be parsed|cannot be parsed|failed to parse)/.test(message)) {
|
|
932
|
-
return { status: 400, kind: "encrypted_content_rejected" };
|
|
961
|
+
function codexRateLimitHeaders(event) {
|
|
962
|
+
const limits = event.rate_limits;
|
|
963
|
+
if (!limits || typeof limits !== "object") return [];
|
|
964
|
+
const headers = [];
|
|
965
|
+
for (const slot of ["primary", "secondary"]) {
|
|
966
|
+
const window = limits[slot];
|
|
967
|
+
if (!window || typeof window !== "object") continue;
|
|
968
|
+
const value = window;
|
|
969
|
+
const number3 = (field) => typeof value[field] === "number" && Number.isFinite(value[field]) ? Math.round(value[field]) : null;
|
|
970
|
+
const used = number3("used_percent");
|
|
971
|
+
const minutes = number3("window_minutes");
|
|
972
|
+
const resetAfter = number3("reset_after_seconds");
|
|
973
|
+
const resetAt = number3("reset_at");
|
|
974
|
+
if (used !== null) headers.push([`x-codex-${slot}-used-percent`, String(used)]);
|
|
975
|
+
if (minutes !== null) {
|
|
976
|
+
headers.push([`x-codex-${slot}-window-minutes`, String(minutes)]);
|
|
977
|
+
headers.push([`x-codex-${slot}-limit-window-seconds`, String(minutes * 60)]);
|
|
933
978
|
}
|
|
934
|
-
|
|
979
|
+
if (resetAfter !== null)
|
|
980
|
+
headers.push([`x-codex-${slot}-reset-after-seconds`, String(resetAfter)]);
|
|
981
|
+
if (resetAt !== null) headers.push([`x-codex-${slot}-reset-at`, String(resetAt)]);
|
|
935
982
|
}
|
|
936
|
-
return
|
|
983
|
+
return headers;
|
|
937
984
|
}
|
|
938
|
-
function
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
985
|
+
function metadataHeaders(event) {
|
|
986
|
+
const headers = event.headers;
|
|
987
|
+
if (!headers || typeof headers !== "object") return [];
|
|
988
|
+
return Object.entries(headers).flatMap(
|
|
989
|
+
([name, value]) => HEADER_NAME.test(name) && typeof value === "string" && !/[\r\n]/.test(value) ? [[name.toLowerCase(), value]] : []
|
|
990
|
+
);
|
|
944
991
|
}
|
|
945
|
-
function
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
992
|
+
function sseBlock(event) {
|
|
993
|
+
const type = typeof event.type === "string" ? event.type : "message";
|
|
994
|
+
return new TextEncoder().encode(`event: ${type}
|
|
995
|
+
data: ${JSON.stringify(event)}
|
|
996
|
+
|
|
997
|
+
`);
|
|
998
|
+
}
|
|
999
|
+
function requestUrl(input) {
|
|
1000
|
+
if (typeof input === "string") return input;
|
|
1001
|
+
if (input instanceof URL) return input.toString();
|
|
954
1002
|
return null;
|
|
955
1003
|
}
|
|
956
|
-
function
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1004
|
+
function codexResponsesTransport(http, options = {}) {
|
|
1005
|
+
const createSocket = options.createSocket === void 0 ? bunWebSocketFactory() : options.createSocket;
|
|
1006
|
+
if (!createSocket) return http;
|
|
1007
|
+
const idleMs = options.idleMs ?? DEFAULT_IDLE_MS;
|
|
1008
|
+
const maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
1009
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
1010
|
+
const handshakeBackoffMs = options.handshakeBackoffMs ?? DEFAULT_HANDSHAKE_BACKOFF_MS;
|
|
1011
|
+
const now = options.now ?? Date.now;
|
|
1012
|
+
const pool = /* @__PURE__ */ new Map();
|
|
1013
|
+
const backoffUntil = /* @__PURE__ */ new Map();
|
|
1014
|
+
const retire = (connection) => {
|
|
1015
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
1016
|
+
connection.idleTimer = null;
|
|
1017
|
+
if (pool.get(connection.key) === connection) pool.delete(connection.key);
|
|
1018
|
+
if (!connection.closed) {
|
|
1019
|
+
connection.closed = true;
|
|
1020
|
+
try {
|
|
1021
|
+
connection.socket.close(1e3, "done");
|
|
1022
|
+
} catch {
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
961
1025
|
};
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
const nowMs = Date.now();
|
|
971
|
-
const primaryResetAt = resolveResetAt(
|
|
972
|
-
headers,
|
|
973
|
-
"x-codex-primary-reset-at",
|
|
974
|
-
"x-codex-primary-reset-after-seconds",
|
|
975
|
-
nowMs
|
|
976
|
-
);
|
|
977
|
-
const secondaryResetAt = resolveResetAt(
|
|
978
|
-
headers,
|
|
979
|
-
"x-codex-secondary-reset-at",
|
|
980
|
-
"x-codex-secondary-reset-after-seconds",
|
|
981
|
-
nowMs
|
|
982
|
-
);
|
|
983
|
-
const firstIsFiveHour = primarySeconds === CODEX_FIVE_HOUR_WINDOW_SECONDS;
|
|
984
|
-
return {
|
|
985
|
-
primaryUsedPercent: firstIsFiveHour ? first : second,
|
|
986
|
-
primaryResetAt: firstIsFiveHour ? primaryResetAt : secondaryResetAt,
|
|
987
|
-
secondaryUsedPercent: firstIsFiveHour ? second : first,
|
|
988
|
-
secondaryResetAt: firstIsFiveHour ? secondaryResetAt : primaryResetAt,
|
|
989
|
-
checkedAt: new Date(nowMs)
|
|
1026
|
+
const release = (connection) => {
|
|
1027
|
+
connection.busy = false;
|
|
1028
|
+
connection.onEvent = null;
|
|
1029
|
+
connection.onClose = null;
|
|
1030
|
+
if (connection.closed) return;
|
|
1031
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
1032
|
+
connection.idleTimer = setTimeout(() => retire(connection), idleMs);
|
|
1033
|
+
connection.idleTimer.unref?.();
|
|
990
1034
|
};
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
phase: "failed",
|
|
1010
|
-
rawError: ev.response?.error,
|
|
1011
|
-
fallbackCode: "response_failed",
|
|
1012
|
-
fallbackMessage: "The Codex response failed"
|
|
1035
|
+
const connect = (url, key, headers) => new Promise((resolve, reject) => {
|
|
1036
|
+
let socket;
|
|
1037
|
+
try {
|
|
1038
|
+
socket = createSocket(url, headers);
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
reject(error);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const connection = {
|
|
1044
|
+
key,
|
|
1045
|
+
socket,
|
|
1046
|
+
openedAt: now(),
|
|
1047
|
+
busy: false,
|
|
1048
|
+
closed: false,
|
|
1049
|
+
requests: 0,
|
|
1050
|
+
idleTimer: null,
|
|
1051
|
+
onEvent: null,
|
|
1052
|
+
onClose: null
|
|
1013
1053
|
};
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1054
|
+
let opened = false;
|
|
1055
|
+
const timer = setTimeout(() => {
|
|
1056
|
+
if (opened) return;
|
|
1057
|
+
connection.closed = true;
|
|
1058
|
+
try {
|
|
1059
|
+
socket.close();
|
|
1060
|
+
} catch {
|
|
1061
|
+
}
|
|
1062
|
+
reject(new Error("Codex response WebSocket handshake timed out"));
|
|
1063
|
+
}, connectTimeoutMs);
|
|
1064
|
+
socket.onopen = () => {
|
|
1065
|
+
opened = true;
|
|
1066
|
+
clearTimeout(timer);
|
|
1067
|
+
resolve(connection);
|
|
1021
1068
|
};
|
|
1022
|
-
|
|
1023
|
-
if (ev.type === "response.incomplete") {
|
|
1024
|
-
const details = ev.response?.incomplete_details;
|
|
1025
|
-
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
1026
|
-
return {
|
|
1027
|
-
phase: "failed",
|
|
1028
|
-
rawError: {
|
|
1029
|
-
code: "response_incomplete",
|
|
1030
|
-
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
1031
|
-
},
|
|
1032
|
-
fallbackCode: "response_incomplete",
|
|
1033
|
-
fallbackMessage: "The Codex response was incomplete"
|
|
1069
|
+
socket.onerror = () => {
|
|
1034
1070
|
};
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1071
|
+
socket.onclose = (event) => {
|
|
1072
|
+
clearTimeout(timer);
|
|
1073
|
+
connection.closed = true;
|
|
1074
|
+
if (pool.get(key) === connection) pool.delete(key);
|
|
1075
|
+
if (connection.idleTimer) clearTimeout(connection.idleTimer);
|
|
1076
|
+
if (!opened) {
|
|
1077
|
+
reject(
|
|
1078
|
+
new Error(`Codex response WebSocket handshake failed (${event?.code ?? "closed"})`)
|
|
1079
|
+
);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
connection.onClose?.(
|
|
1083
|
+
new Error(
|
|
1084
|
+
`Codex response WebSocket closed before the response finished (${event?.code ?? "closed"})`
|
|
1085
|
+
)
|
|
1086
|
+
);
|
|
1050
1087
|
};
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1088
|
+
socket.onmessage = (message) => {
|
|
1089
|
+
const text = eventData(message.data);
|
|
1090
|
+
if (text === null) return;
|
|
1091
|
+
let parsed;
|
|
1092
|
+
try {
|
|
1093
|
+
parsed = JSON.parse(text);
|
|
1094
|
+
} catch {
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1098
|
+
connection.onEvent?.(parsed);
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
});
|
|
1102
|
+
return async (input, init) => {
|
|
1103
|
+
const url = requestUrl(input);
|
|
1104
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
1105
|
+
const body = init?.body;
|
|
1106
|
+
if (!url || method !== "POST" || !/^https:\/\/[^/]+\/.*\/codex\/responses(?:\?|$)/.test(url)) {
|
|
1107
|
+
return await http(input, init);
|
|
1108
|
+
}
|
|
1109
|
+
const viaHttp = async (reason) => withTransport(await http(input, init), { kind: "http", reason });
|
|
1110
|
+
if (typeof body !== "string") return await viaHttp("not_eligible");
|
|
1111
|
+
let payload;
|
|
1112
|
+
try {
|
|
1113
|
+
const parsed = JSON.parse(body);
|
|
1114
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
|
|
1115
|
+
payload = parsed;
|
|
1116
|
+
} catch {
|
|
1117
|
+
return await viaHttp("not_eligible");
|
|
1118
|
+
}
|
|
1119
|
+
if (payload.stream !== true) return await viaHttp("not_eligible");
|
|
1120
|
+
const handshake = {};
|
|
1121
|
+
for (const [name, value] of new Headers(init?.headers).entries()) {
|
|
1122
|
+
if (!PER_REQUEST_HEADERS.has(name)) handshake[name] = value;
|
|
1123
|
+
}
|
|
1124
|
+
handshake["openai-beta"] = CODEX_RESPONSES_WEBSOCKET_BETA;
|
|
1125
|
+
const wsUrl = url.replace(/^https:/, "wss:");
|
|
1126
|
+
const key = JSON.stringify([wsUrl, Object.entries(handshake).sort()]);
|
|
1127
|
+
if ((backoffUntil.get(key) ?? 0) > now()) return await viaHttp("handshake_backoff");
|
|
1128
|
+
const signal = init?.signal ?? null;
|
|
1129
|
+
signal?.throwIfAborted();
|
|
1130
|
+
let connection = pool.get(key);
|
|
1131
|
+
if (connection && (connection.closed || now() - connection.openedAt > maxAgeMs)) {
|
|
1132
|
+
if (!connection.busy) retire(connection);
|
|
1133
|
+
connection = void 0;
|
|
1134
|
+
}
|
|
1135
|
+
if (connection?.busy) return await viaHttp("connection_busy");
|
|
1136
|
+
if (!connection) {
|
|
1137
|
+
try {
|
|
1138
|
+
connection = await connect(wsUrl, key, handshake);
|
|
1139
|
+
} catch {
|
|
1140
|
+
backoffUntil.set(key, now() + handshakeBackoffMs);
|
|
1141
|
+
return await viaHttp("handshake_failed");
|
|
1142
|
+
}
|
|
1143
|
+
if (pool.has(key)) {
|
|
1144
|
+
const existing = pool.get(key);
|
|
1145
|
+
if (!existing.busy && !existing.closed) {
|
|
1146
|
+
retire(connection);
|
|
1147
|
+
connection = existing;
|
|
1148
|
+
} else {
|
|
1149
|
+
pool.set(key, connection);
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
pool.set(key, connection);
|
|
1153
|
+
}
|
|
1154
|
+
if (connection.busy) return await viaHttp("connection_busy");
|
|
1155
|
+
}
|
|
1156
|
+
const active = connection;
|
|
1157
|
+
const transport = {
|
|
1158
|
+
kind: "websocket",
|
|
1159
|
+
connection: active.requests === 0 ? "new" : "reused"
|
|
1160
|
+
};
|
|
1161
|
+
active.requests += 1;
|
|
1162
|
+
active.busy = true;
|
|
1163
|
+
if (active.idleTimer) clearTimeout(active.idleTimer);
|
|
1164
|
+
active.idleTimer = null;
|
|
1165
|
+
return await new Promise((resolve, reject) => {
|
|
1166
|
+
const headers = new Headers({
|
|
1167
|
+
"content-type": "text/event-stream",
|
|
1168
|
+
[CODEX_TRANSPORT_HEADER]: formatCodexRequestTransport(transport)
|
|
1169
|
+
});
|
|
1170
|
+
let controller = null;
|
|
1171
|
+
let started = false;
|
|
1172
|
+
let finished = false;
|
|
1173
|
+
const pending = [];
|
|
1174
|
+
const finish = (error) => {
|
|
1175
|
+
if (finished) return;
|
|
1176
|
+
finished = true;
|
|
1177
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1178
|
+
if (error !== void 0) {
|
|
1179
|
+
retire(active);
|
|
1180
|
+
active.busy = false;
|
|
1181
|
+
if (started) controller?.error(error);
|
|
1182
|
+
else reject(error);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
release(active);
|
|
1186
|
+
try {
|
|
1187
|
+
controller?.close();
|
|
1188
|
+
} catch {
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
const onAbort = () => finish(signal?.reason ?? new Error("Model request aborted"));
|
|
1192
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1193
|
+
const stream = new ReadableStream({
|
|
1194
|
+
start(streamController) {
|
|
1195
|
+
controller = streamController;
|
|
1196
|
+
for (const block of pending.splice(0)) streamController.enqueue(block);
|
|
1197
|
+
if (finished) streamController.close();
|
|
1198
|
+
},
|
|
1199
|
+
cancel() {
|
|
1200
|
+
if (!finished) finish(new Error("Model response stream cancelled"));
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
const begin = (status = 200, errorBody) => {
|
|
1204
|
+
started = true;
|
|
1205
|
+
if (errorBody !== void 0) {
|
|
1206
|
+
headers.set("content-type", "application/json");
|
|
1207
|
+
resolve(new Response(errorBody, { status, headers }));
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
resolve(new Response(stream, { status, headers }));
|
|
1211
|
+
};
|
|
1212
|
+
active.onClose = (error) => finish(error);
|
|
1213
|
+
active.onEvent = (event) => {
|
|
1214
|
+
if (finished) return;
|
|
1215
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
1216
|
+
if (type === RATE_LIMITS_EVENT) {
|
|
1217
|
+
if (!started)
|
|
1218
|
+
for (const [name, value] of codexRateLimitHeaders(event)) headers.set(name, value);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (type === METADATA_EVENT) {
|
|
1222
|
+
if (!started)
|
|
1223
|
+
for (const [name, value] of metadataHeaders(event)) headers.set(name, value);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (type === TIMING_EVENT) return;
|
|
1227
|
+
if (!started && type === "error" && typeof event.status === "number") {
|
|
1228
|
+
for (const [name, value] of metadataHeaders(event)) headers.set(name, value);
|
|
1229
|
+
const status = event.status >= 400 && event.status < 600 ? event.status : 500;
|
|
1230
|
+
begin(status, JSON.stringify({ error: event.error ?? null }));
|
|
1231
|
+
finished = true;
|
|
1232
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1233
|
+
release(active);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
if (!started) begin();
|
|
1237
|
+
const block = sseBlock(event);
|
|
1238
|
+
if (controller) controller.enqueue(block);
|
|
1239
|
+
else pending.push(block);
|
|
1240
|
+
if (TERMINAL_EVENTS.has(type)) finish();
|
|
1241
|
+
};
|
|
1242
|
+
try {
|
|
1243
|
+
active.socket.send(JSON.stringify({ ...payload, type: "response.create" }));
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
finish(error);
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1076
1248
|
};
|
|
1077
1249
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
if (audit.terminalOutcome !== null) {
|
|
1082
|
-
await audit.settlement;
|
|
1083
|
-
return false;
|
|
1084
|
-
}
|
|
1085
|
-
audit.terminalOutcome = terminalOutcome;
|
|
1086
|
-
await settleProviderRequest(
|
|
1087
|
-
audit,
|
|
1088
|
-
definiteErrorStatus(event.status) ? "refused" : event.phase === "completed" ? "response_received" : "unknown"
|
|
1089
|
-
);
|
|
1090
|
-
}
|
|
1091
|
-
const observed = requestEventFor(audit, event);
|
|
1250
|
+
|
|
1251
|
+
// src/fetch.ts
|
|
1252
|
+
function emitRequestPreparationDiagnostic(ctx, phase) {
|
|
1092
1253
|
try {
|
|
1093
|
-
|
|
1254
|
+
ctx.onRequestPreparationDiagnostic?.(phase);
|
|
1094
1255
|
} catch {
|
|
1095
1256
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1257
|
+
}
|
|
1258
|
+
var CODEX_TRANSPORT_ERROR_HEADER = "x-opengeni-codex-transport-error";
|
|
1259
|
+
var CODEX_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-request-body-normalized";
|
|
1260
|
+
var REPLAYABLE_REQUEST_BODY_FACTORY = Symbol.for("opengeni.replayable-request-body-factory");
|
|
1261
|
+
var CODEX_REQUEST_MODEL_HEADER = "x-opengeni-request-model";
|
|
1262
|
+
var CODEX_REQUEST_ID_HEADER = "x-opengeni-request-id";
|
|
1263
|
+
var CODEX_REQUEST_CALLER_STREAM_HEADER = "x-opengeni-request-caller-stream";
|
|
1264
|
+
var MAX_CODEX_ERROR_BODY_BYTES = 64 * 1024;
|
|
1265
|
+
function headersCarryCodexTransportMarker(headers) {
|
|
1266
|
+
if (!headers || typeof headers !== "object") return false;
|
|
1267
|
+
const getter = headers.get;
|
|
1268
|
+
if (typeof getter === "function") {
|
|
1269
|
+
return getter.call(headers, CODEX_TRANSPORT_ERROR_HEADER) === "1";
|
|
1270
|
+
}
|
|
1271
|
+
const record = headers;
|
|
1272
|
+
return record[CODEX_TRANSPORT_ERROR_HEADER] === "1" || record[CODEX_TRANSPORT_ERROR_HEADER.toLowerCase()] === "1";
|
|
1273
|
+
}
|
|
1274
|
+
function isCodexTransportError(error) {
|
|
1275
|
+
let current = error;
|
|
1276
|
+
for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
|
|
1277
|
+
const value = current;
|
|
1278
|
+
if (headersCarryCodexTransportMarker(value.headers)) return true;
|
|
1279
|
+
current = value.cause;
|
|
1280
|
+
}
|
|
1281
|
+
return false;
|
|
1282
|
+
}
|
|
1283
|
+
var CODEX_ENCRYPTED_CONTENT_REJECTION_CODE = "invalid_encrypted_content";
|
|
1284
|
+
function classifyCodexEncryptedArtifactRejection(error) {
|
|
1285
|
+
if (!isCodexTransportError(error)) return null;
|
|
1286
|
+
let current = error;
|
|
1287
|
+
for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
|
|
1288
|
+
const value = current;
|
|
1289
|
+
const body = value.error && typeof value.error === "object" ? value.error : null;
|
|
1290
|
+
const status = Number(value.status ?? body?.status);
|
|
1291
|
+
const exactCode = typeof body?.code === "string" ? body.code : typeof value.code === "string" ? value.code : "";
|
|
1292
|
+
if (status === 400 && exactCode === CODEX_ENCRYPTED_CONTENT_REJECTION_CODE) {
|
|
1293
|
+
return { status: 400, kind: "encrypted_content_rejected" };
|
|
1294
|
+
}
|
|
1295
|
+
const message = [
|
|
1296
|
+
typeof value.message === "string" ? value.message : "",
|
|
1297
|
+
typeof body?.message === "string" ? body.message : "",
|
|
1298
|
+
typeof value.code === "string" ? value.code : "",
|
|
1299
|
+
typeof body?.code === "string" ? body.code : "",
|
|
1300
|
+
typeof value.type === "string" ? value.type : "",
|
|
1301
|
+
typeof body?.type === "string" ? body.type : ""
|
|
1302
|
+
].join(" ").toLowerCase();
|
|
1303
|
+
const unsupportedFieldShape = /(?:invalid value|supported values?|unsupported|unknown (?:field|parameter|value))/.test(
|
|
1304
|
+
message
|
|
1305
|
+
);
|
|
1306
|
+
if (status === 400 && !unsupportedFieldShape && /(?:encrypted[_ ]content|encrypted reasoning|reasoning artifact)/.test(message) && /(?:decrypt(?:ed|ion)?|could not be parsed|cannot be parsed|failed to parse)/.test(message)) {
|
|
1307
|
+
return { status: 400, kind: "encrypted_content_rejected" };
|
|
1308
|
+
}
|
|
1309
|
+
current = value.cause;
|
|
1310
|
+
}
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
function parseIntHeader(value) {
|
|
1314
|
+
if (value === null) {
|
|
1315
|
+
return null;
|
|
1316
|
+
}
|
|
1317
|
+
const n = Number.parseInt(value.trim(), 10);
|
|
1318
|
+
return Number.isFinite(n) ? n : null;
|
|
1319
|
+
}
|
|
1320
|
+
function resolveResetAt(headers, atKey, afterKey, nowMs) {
|
|
1321
|
+
const at = parseIntHeader(headers.get(atKey));
|
|
1322
|
+
if (at !== null) {
|
|
1323
|
+
return new Date(at * 1e3);
|
|
1324
|
+
}
|
|
1325
|
+
const after = parseIntHeader(headers.get(afterKey));
|
|
1326
|
+
if (after !== null) {
|
|
1327
|
+
return new Date(nowMs + after * 1e3);
|
|
1328
|
+
}
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
function parseCodexUsageHeaders(headers) {
|
|
1332
|
+
const duration = (slot) => {
|
|
1333
|
+
const raw = headers.get(`x-codex-${slot}-limit-window-seconds`);
|
|
1334
|
+
if (raw === null || !/^\d+$/.test(raw.trim())) return null;
|
|
1335
|
+
return Number(raw.trim());
|
|
1336
|
+
};
|
|
1337
|
+
const primarySeconds = duration("primary");
|
|
1338
|
+
const secondarySeconds = duration("secondary");
|
|
1339
|
+
if (!(primarySeconds === CODEX_FIVE_HOUR_WINDOW_SECONDS && secondarySeconds === CODEX_WEEKLY_WINDOW_SECONDS || primarySeconds === CODEX_WEEKLY_WINDOW_SECONDS && secondarySeconds === CODEX_FIVE_HOUR_WINDOW_SECONDS)) {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
const first = parseIntHeader(headers.get("x-codex-primary-used-percent"));
|
|
1343
|
+
const second = parseIntHeader(headers.get("x-codex-secondary-used-percent"));
|
|
1344
|
+
if (first === null || second === null) return null;
|
|
1345
|
+
const nowMs = Date.now();
|
|
1346
|
+
const primaryResetAt = resolveResetAt(
|
|
1347
|
+
headers,
|
|
1348
|
+
"x-codex-primary-reset-at",
|
|
1349
|
+
"x-codex-primary-reset-after-seconds",
|
|
1350
|
+
nowMs
|
|
1351
|
+
);
|
|
1352
|
+
const secondaryResetAt = resolveResetAt(
|
|
1353
|
+
headers,
|
|
1354
|
+
"x-codex-secondary-reset-at",
|
|
1355
|
+
"x-codex-secondary-reset-after-seconds",
|
|
1356
|
+
nowMs
|
|
1357
|
+
);
|
|
1358
|
+
const firstIsFiveHour = primarySeconds === CODEX_FIVE_HOUR_WINDOW_SECONDS;
|
|
1359
|
+
return {
|
|
1360
|
+
primaryUsedPercent: firstIsFiveHour ? first : second,
|
|
1361
|
+
primaryResetAt: firstIsFiveHour ? primaryResetAt : secondaryResetAt,
|
|
1362
|
+
secondaryUsedPercent: firstIsFiveHour ? second : first,
|
|
1363
|
+
secondaryResetAt: firstIsFiveHour ? secondaryResetAt : primaryResetAt,
|
|
1364
|
+
checkedAt: new Date(nowMs)
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
function settleProviderRequest(audit, outcome) {
|
|
1368
|
+
if (!audit.admitted) return Promise.resolve();
|
|
1369
|
+
if (!audit.settlement) {
|
|
1370
|
+
audit.settlement = (async () => {
|
|
1371
|
+
await audit.ctx.onProviderRequestSettled?.({
|
|
1372
|
+
requestId: audit.requestId,
|
|
1373
|
+
transportAttempt: audit.transportAttempt,
|
|
1374
|
+
outcome
|
|
1375
|
+
});
|
|
1376
|
+
})();
|
|
1377
|
+
void audit.settlement.catch(() => void 0);
|
|
1378
|
+
}
|
|
1379
|
+
return audit.settlement;
|
|
1380
|
+
}
|
|
1381
|
+
function classifyCodexSseTerminal(ev) {
|
|
1382
|
+
if (ev.type === "response.failed") {
|
|
1383
|
+
return {
|
|
1384
|
+
phase: "failed",
|
|
1385
|
+
rawError: ev.response?.error,
|
|
1386
|
+
fallbackCode: "response_failed",
|
|
1387
|
+
fallbackMessage: "The Codex response failed"
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
if (ev.type === "error" || ev.type === "response.error") {
|
|
1391
|
+
return {
|
|
1392
|
+
phase: "failed",
|
|
1393
|
+
rawError: ev.error ?? ev.response?.error ?? ev,
|
|
1394
|
+
fallbackCode: "response_error",
|
|
1395
|
+
fallbackMessage: "The Codex response stream reported an error"
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
if (ev.type === "response.incomplete") {
|
|
1399
|
+
const details = ev.response?.incomplete_details;
|
|
1400
|
+
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
1401
|
+
return {
|
|
1402
|
+
phase: "failed",
|
|
1403
|
+
rawError: {
|
|
1404
|
+
code: "response_incomplete",
|
|
1405
|
+
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
1406
|
+
},
|
|
1407
|
+
fallbackCode: "response_incomplete",
|
|
1408
|
+
fallbackMessage: "The Codex response was incomplete"
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
if (ev.type !== "response.completed" && ev.type !== "response.done") {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
if (!ev.response) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const responseStatus = ev.response.status;
|
|
1418
|
+
if (responseStatus !== void 0 && responseStatus !== "completed" || ev.response.error !== null && ev.response.error !== void 0) {
|
|
1419
|
+
const incomplete = responseStatus === "incomplete";
|
|
1420
|
+
return {
|
|
1421
|
+
phase: "failed",
|
|
1422
|
+
rawError: ev.response.error,
|
|
1423
|
+
fallbackCode: incomplete ? "response_incomplete" : "response_failed",
|
|
1424
|
+
fallbackMessage: incomplete ? "The Codex response was incomplete" : "The Codex response failed"
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
return { phase: "completed" };
|
|
1428
|
+
}
|
|
1429
|
+
function markSemanticTerminal(state, phase) {
|
|
1430
|
+
if (state.phase === null) {
|
|
1431
|
+
state.phase = phase;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
function semanticCompletionEvidence(state) {
|
|
1435
|
+
return state?.phase === "completed" ? { meaningfulOutput: state.meaningfulOutput === true } : {};
|
|
1436
|
+
}
|
|
1437
|
+
function terminalOutcomeForPhase(phase) {
|
|
1438
|
+
if (phase === "completed" || phase === "failed" || phase === "timed_out") {
|
|
1439
|
+
return phase;
|
|
1440
|
+
}
|
|
1441
|
+
return null;
|
|
1442
|
+
}
|
|
1443
|
+
function requestEventFor(audit, event) {
|
|
1444
|
+
return {
|
|
1445
|
+
requestId: audit.requestId,
|
|
1446
|
+
transportAttempt: audit.transportAttempt,
|
|
1447
|
+
...audit.model ? { model: audit.model } : {},
|
|
1448
|
+
durationMs: Math.max(0, performance.now() - audit.attemptStartedAtMonotonic),
|
|
1449
|
+
timeoutPolicy: audit.policy,
|
|
1450
|
+
...event
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
async function emitRequestEvent(audit, event) {
|
|
1454
|
+
const terminalOutcome = terminalOutcomeForPhase(event.phase);
|
|
1455
|
+
if (terminalOutcome !== null) {
|
|
1456
|
+
if (audit.terminalOutcome !== null) {
|
|
1457
|
+
await audit.settlement;
|
|
1458
|
+
return false;
|
|
1459
|
+
}
|
|
1460
|
+
audit.terminalOutcome = terminalOutcome;
|
|
1461
|
+
await settleProviderRequest(
|
|
1462
|
+
audit,
|
|
1463
|
+
definiteErrorStatus(event.status) ? "refused" : event.phase === "completed" ? "response_received" : "unknown"
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
const observed = requestEventFor(audit, event);
|
|
1467
|
+
try {
|
|
1468
|
+
audit.ctx.onModelRequestDiagnostic?.(observed);
|
|
1469
|
+
} catch {
|
|
1470
|
+
}
|
|
1471
|
+
await audit.ctx.onModelRequestEvent?.(observed);
|
|
1472
|
+
return true;
|
|
1473
|
+
}
|
|
1474
|
+
function definiteErrorStatus(status) {
|
|
1475
|
+
return status !== void 0 && status >= 400 && status < 600 && status !== 408 && status !== 409 && status !== 504;
|
|
1476
|
+
}
|
|
1477
|
+
function providerRequestId(headers) {
|
|
1478
|
+
return headers.get("x-request-id") ?? headers.get("request-id") ?? void 0;
|
|
1104
1479
|
}
|
|
1105
1480
|
async function fetchBeforeHeaders(base, input, init, audit) {
|
|
1106
1481
|
const elapsed = Date.now() - audit.logicalStartedAt;
|
|
@@ -1460,7 +1835,12 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1460
1835
|
phase: "headers",
|
|
1461
1836
|
responseObserved: true,
|
|
1462
1837
|
status: res.status,
|
|
1463
|
-
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1838
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {},
|
|
1839
|
+
// A response the socket transport did not label never passed through it.
|
|
1840
|
+
transport: parseCodexRequestTransport(res.headers.get(CODEX_TRANSPORT_HEADER)) ?? {
|
|
1841
|
+
kind: "http",
|
|
1842
|
+
reason: "unavailable"
|
|
1843
|
+
}
|
|
1464
1844
|
});
|
|
1465
1845
|
res = await observedResponse(res, audit, nextInit.signal, semanticTerminal);
|
|
1466
1846
|
} catch (error) {
|
|
@@ -2037,322 +2417,6 @@ function inspectCodexSseBlock(block, source, onSemanticTerminal, onOutput) {
|
|
|
2037
2417
|
return false;
|
|
2038
2418
|
}
|
|
2039
2419
|
|
|
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
|
-
|
|
2356
2420
|
// src/mcp-sanitize.ts
|
|
2357
2421
|
var VALID_TOOL_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
2358
2422
|
var MAX_TOOL_NAME_LEN = 64;
|
|
@@ -3788,6 +3852,7 @@ export {
|
|
|
3788
3852
|
CODEX_TOKEN_URL,
|
|
3789
3853
|
CODEX_TOOL_OUTPUT_SERIALIZATION_ALLOWANCE,
|
|
3790
3854
|
CODEX_TRANSPORT_ERROR_HEADER,
|
|
3855
|
+
CODEX_TRANSPORT_HEADER,
|
|
3791
3856
|
CODEX_USAGE_LIMIT_ERROR_TYPE,
|
|
3792
3857
|
CODEX_WEEKLY_WINDOW_SECONDS,
|
|
3793
3858
|
CODEX_WHAM_BASE,
|
|
@@ -3839,6 +3904,7 @@ export {
|
|
|
3839
3904
|
fetchCodexRateLimitResetCredits,
|
|
3840
3905
|
fetchCodexRealtimeProviderConfig,
|
|
3841
3906
|
fetchCodexUsage,
|
|
3907
|
+
formatCodexRequestTransport,
|
|
3842
3908
|
generateCodexSubscriptionImage,
|
|
3843
3909
|
isCodexAppsCredentialUnavailable,
|
|
3844
3910
|
isCodexBilledModel,
|
|
@@ -3857,6 +3923,7 @@ export {
|
|
|
3857
3923
|
parseCodexRateLimitResetCreditsDetails,
|
|
3858
3924
|
parseCodexRateLimitResetCreditsSummary,
|
|
3859
3925
|
parseCodexRealtimeV3Event,
|
|
3926
|
+
parseCodexRequestTransport,
|
|
3860
3927
|
parseCodexUsageHeaders,
|
|
3861
3928
|
parseIdToken,
|
|
3862
3929
|
pollDeviceCode,
|