skalpel 2.0.19 → 2.0.21
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/cli/index.js +19 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/proxy-runner.js +61 -9
- package/dist/cli/proxy-runner.js.map +1 -1
- package/dist/index.cjs +61 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -9
- package/dist/index.js.map +1 -1
- package/dist/proxy/index.cjs +61 -9
- package/dist/proxy/index.cjs.map +1 -1
- package/dist/proxy/index.js +61 -9
- package/dist/proxy/index.js.map +1 -1
- package/package.json +1 -1
package/dist/proxy/index.cjs
CHANGED
|
@@ -40,7 +40,9 @@ var init_dispatcher = __esm({
|
|
|
40
40
|
keepAliveTimeout: 1e4,
|
|
41
41
|
keepAliveMaxTimeout: 6e4,
|
|
42
42
|
connections: 100,
|
|
43
|
-
pipelining: 1
|
|
43
|
+
pipelining: 1,
|
|
44
|
+
allowH2: false
|
|
45
|
+
// Force HTTP/1.1 to prevent GCP LB WebSocket downgrade
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
});
|
|
@@ -660,6 +662,12 @@ __export(handler_exports, {
|
|
|
660
662
|
isSkalpelBackendFailure: () => isSkalpelBackendFailure,
|
|
661
663
|
shouldRouteToSkalpel: () => shouldRouteToSkalpel
|
|
662
664
|
});
|
|
665
|
+
function validateCodexAuth(oauthToken, inboundAuth) {
|
|
666
|
+
if (!oauthToken && !inboundAuth) {
|
|
667
|
+
return { valid: false, error: "no_credentials" };
|
|
668
|
+
}
|
|
669
|
+
return { valid: true };
|
|
670
|
+
}
|
|
663
671
|
function collectBody(req) {
|
|
664
672
|
return new Promise((resolve, reject) => {
|
|
665
673
|
const chunks = [];
|
|
@@ -924,6 +932,18 @@ async function handleWebSocketBridge(clientWs, req, config, source, logger) {
|
|
|
924
932
|
const oauthHeader = req.headers["authorization"] ?? "";
|
|
925
933
|
oauthToken = oauthHeader.toLowerCase().startsWith("bearer ") ? oauthHeader.slice(7).trim() : "";
|
|
926
934
|
}
|
|
935
|
+
const authResult = validateCodexAuth(freshOauthToken, oauthToken);
|
|
936
|
+
if (!authResult.valid) {
|
|
937
|
+
clientWs.send(JSON.stringify({
|
|
938
|
+
type: "error",
|
|
939
|
+
error: {
|
|
940
|
+
code: "no_credentials",
|
|
941
|
+
message: "Codex requires OAuth login. Run: codex login"
|
|
942
|
+
}
|
|
943
|
+
}));
|
|
944
|
+
clientWs.close(4e3, "no credentials");
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
927
947
|
const backend = new BackendWsClient({
|
|
928
948
|
url: backendUrl,
|
|
929
949
|
apiKey: config.apiKey,
|
|
@@ -1031,6 +1051,18 @@ function parseSseEvents(buffer) {
|
|
|
1031
1051
|
return { events, rest };
|
|
1032
1052
|
}
|
|
1033
1053
|
async function fallbackToHttp(clientWs, config, source, logger, requestBody, inboundAuth) {
|
|
1054
|
+
const preflightAuth = validateCodexAuth(getFreshAccessToken(), inboundAuth);
|
|
1055
|
+
if (!preflightAuth.valid) {
|
|
1056
|
+
clientWs.send(JSON.stringify({
|
|
1057
|
+
type: "error",
|
|
1058
|
+
error: {
|
|
1059
|
+
code: "no_credentials",
|
|
1060
|
+
message: "Codex requires OAuth login. Run: codex login"
|
|
1061
|
+
}
|
|
1062
|
+
}));
|
|
1063
|
+
clientWs.close(4e3, "no credentials");
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1034
1066
|
try {
|
|
1035
1067
|
const freshToken = getFreshAccessToken();
|
|
1036
1068
|
const authHeader = freshToken !== null ? `Bearer ${freshToken}` : inboundAuth;
|
|
@@ -1060,17 +1092,37 @@ async function fallbackToHttp(clientWs, config, source, logger, requestBody, inb
|
|
|
1060
1092
|
},
|
|
1061
1093
|
body: requestBody
|
|
1062
1094
|
});
|
|
1063
|
-
if (!resp.ok
|
|
1064
|
-
|
|
1065
|
-
JSON.stringify({
|
|
1066
|
-
type: "error",
|
|
1067
|
-
error: { code: resp.status, message: `http fallback status ${resp.status}` }
|
|
1068
|
-
})
|
|
1069
|
-
);
|
|
1095
|
+
if (!resp.ok) {
|
|
1096
|
+
let errorBody = "";
|
|
1070
1097
|
try {
|
|
1071
|
-
|
|
1098
|
+
const bodyPromise = resp.text();
|
|
1099
|
+
const timeoutPromise = new Promise(
|
|
1100
|
+
(_, reject) => setTimeout(() => reject(new Error("body read timeout")), 5e3)
|
|
1101
|
+
);
|
|
1102
|
+
errorBody = await Promise.race([bodyPromise, timeoutPromise]);
|
|
1103
|
+
} catch (e) {
|
|
1104
|
+
errorBody = `status ${resp.status}`;
|
|
1105
|
+
}
|
|
1106
|
+
let errorMessage = `Backend error: ${resp.status}`;
|
|
1107
|
+
try {
|
|
1108
|
+
const parsed = JSON.parse(errorBody);
|
|
1109
|
+
errorMessage = parsed?.error?.message || parsed?.detail || errorMessage;
|
|
1072
1110
|
} catch {
|
|
1111
|
+
if (errorBody.length < 200) errorMessage = errorBody;
|
|
1073
1112
|
}
|
|
1113
|
+
clientWs.send(JSON.stringify({
|
|
1114
|
+
type: "error",
|
|
1115
|
+
error: { code: resp.status, message: errorMessage }
|
|
1116
|
+
}));
|
|
1117
|
+
clientWs.close(1011, "backend error");
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
if (resp.body === null) {
|
|
1121
|
+
clientWs.send(JSON.stringify({
|
|
1122
|
+
type: "error",
|
|
1123
|
+
error: { code: resp.status, message: "empty response body" }
|
|
1124
|
+
}));
|
|
1125
|
+
clientWs.close(1011, "empty body");
|
|
1074
1126
|
return;
|
|
1075
1127
|
}
|
|
1076
1128
|
const reader = resp.body.getReader();
|