github-router 0.3.263 → 0.3.269
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/{attribution-settings-DFIVIRG8.js → attribution-settings-B1txv_Lk.js} +4 -4
- package/dist/{attribution-settings-DFIVIRG8.js.map → attribution-settings-B1txv_Lk.js.map} +1 -1
- package/dist/{auth-WA88cE9q.js → auth-r-my7efo.js} +3 -3
- package/dist/{auth-WA88cE9q.js.map → auth-r-my7efo.js.map} +1 -1
- package/dist/browser-bridge/index.js +67 -27
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-v2apGKz7.js → check-usage-CZr2ZkG0.js} +4 -4
- package/dist/{check-usage-v2apGKz7.js.map → check-usage-CZr2ZkG0.js.map} +1 -1
- package/dist/{claude-BCI3Sx6_.js → claude-BjNY0J6e.js} +15 -14
- package/dist/{claude-BCI3Sx6_.js.map → claude-BjNY0J6e.js.map} +1 -1
- package/dist/{codex-DyJIrbJt.js → codex-CoRWpkHL.js} +5 -5
- package/dist/{codex-DyJIrbJt.js.map → codex-CoRWpkHL.js.map} +1 -1
- package/dist/{debug-vshv4nIv.js → debug-CuuxCk_J.js} +2 -2
- package/dist/{debug-vshv4nIv.js.map → debug-CuuxCk_J.js.map} +1 -1
- package/dist/engine-DpVC7HRL.js +2 -0
- package/dist/{exec-DTO2oin0.js → exec-y8C_MU8A.js} +89 -18
- package/dist/exec-y8C_MU8A.js.map +1 -0
- package/dist/{gate-discovery-cMbt8Ct2.js → gate-discovery-D-7oua8b.js} +6 -6
- package/dist/{gate-discovery-cMbt8Ct2.js.map → gate-discovery-D-7oua8b.js.map} +1 -1
- package/dist/{get-copilot-usage-DOFJWJaQ.js → get-copilot-usage-CFEpeA7E.js} +2 -2
- package/dist/{get-copilot-usage-DOFJWJaQ.js.map → get-copilot-usage-CFEpeA7E.js.map} +1 -1
- package/dist/{internal-artifact-open-CZGO831M.js → internal-artifact-open-CDqPfegE.js} +2 -2
- package/dist/{internal-artifact-open-CZGO831M.js.map → internal-artifact-open-CDqPfegE.js.map} +1 -1
- package/dist/{internal-plan-review-6gkcOAjT.js → internal-plan-review-CH9qTHoZ.js} +3 -3
- package/dist/{internal-plan-review-6gkcOAjT.js.map → internal-plan-review-CH9qTHoZ.js.map} +1 -1
- package/dist/{internal-prompt-submit-C0XOPgcP.js → internal-prompt-submit-Bve1s4z_.js} +5 -5
- package/dist/{internal-prompt-submit-C0XOPgcP.js.map → internal-prompt-submit-Bve1s4z_.js.map} +1 -1
- package/dist/{internal-session-bind-DtSBTkQe.js → internal-session-bind-Cc8bMXHK.js} +2 -2
- package/dist/{internal-session-bind-DtSBTkQe.js.map → internal-session-bind-Cc8bMXHK.js.map} +1 -1
- package/dist/{internal-stop-hook-CBcE2Js8.js → internal-stop-hook-Db5o9RcZ.js} +6 -6
- package/dist/{internal-stop-hook-CBcE2Js8.js.map → internal-stop-hook-Db5o9RcZ.js.map} +1 -1
- package/dist/{internal-stop-review-_1Mqz7eT.js → internal-stop-review-CWE7_QVv.js} +2 -2
- package/dist/{internal-stop-review-_1Mqz7eT.js.map → internal-stop-review-CWE7_QVv.js.map} +1 -1
- package/dist/{lifecycle-DjC9tC1A.js → lifecycle-0XrazT05.js} +2 -2
- package/dist/{lifecycle-DjC9tC1A.js.map → lifecycle-0XrazT05.js.map} +1 -1
- package/dist/lifecycle-2M0IThRr.js +2 -0
- package/dist/{lifecycle-Bd1Fyf4B.js → lifecycle-AgNOtFRv.js} +3 -3
- package/dist/{lifecycle-Bd1Fyf4B.js.map → lifecycle-AgNOtFRv.js.map} +1 -1
- package/dist/lifecycle-C8kFsAj-.js +2 -0
- package/dist/main.js +14 -14
- package/dist/{models-Cv6gprpy.js → models-CJLt4XjP.js} +35 -30
- package/dist/models-CJLt4XjP.js.map +1 -0
- package/dist/{orchestration-B6qidQAd.js → orchestration-B_Q3ncXI.js} +3 -3
- package/dist/{orchestration-B6qidQAd.js.map → orchestration-B_Q3ncXI.js.map} +1 -1
- package/dist/paths-BbHtQAOs.js +2 -0
- package/dist/{paths-BXrbboAS.js → paths-Bg1_DWhi.js} +3 -3
- package/dist/{paths-BXrbboAS.js.map → paths-Bg1_DWhi.js.map} +1 -1
- package/dist/{peer-mcp-personas-B22ncnbQ.js → peer-mcp-personas-DSbBlgFt.js} +85 -26
- package/dist/peer-mcp-personas-DSbBlgFt.js.map +1 -0
- package/dist/{plan-review-hook-Bo4y1r3j.js → plan-review-hook-BKOcW5s4.js} +3 -3
- package/dist/{plan-review-hook-Bo4y1r3j.js.map → plan-review-hook-BKOcW5s4.js.map} +1 -1
- package/dist/{prompt-submit-hook-BFRFLSk5.js → prompt-submit-hook-F7Zih_au.js} +2 -2
- package/dist/{prompt-submit-hook-BFRFLSk5.js.map → prompt-submit-hook-F7Zih_au.js.map} +1 -1
- package/dist/{provision-DSpLQHC3.js → provision-CA-xwnFu.js} +5 -5
- package/dist/{provision-DSpLQHC3.js.map → provision-CA-xwnFu.js.map} +1 -1
- package/dist/{serve-D9tBVVoK.js → serve-DHny4ewF.js} +11 -11
- package/dist/{serve-D9tBVVoK.js.map → serve-DHny4ewF.js.map} +1 -1
- package/dist/{server-setup-BM9Smfek.js → server-setup-Dd293T1O.js} +171 -64
- package/dist/server-setup-Dd293T1O.js.map +1 -0
- package/dist/{start-D7Hx2T_T.js → start-P2rmHBkL.js} +3 -3
- package/dist/{start-D7Hx2T_T.js.map → start-P2rmHBkL.js.map} +1 -1
- package/dist/{stop-gate-hook-Bmwla_2u.js → stop-gate-hook-BqpumJtl.js} +3 -3
- package/dist/{stop-gate-hook-Bmwla_2u.js.map → stop-gate-hook-BqpumJtl.js.map} +1 -1
- package/dist/{stop-gate-policy-CoMKWSsS.js → stop-gate-policy-C0XOBZhB.js} +3 -3
- package/dist/{stop-gate-policy-CoMKWSsS.js.map → stop-gate-policy-C0XOBZhB.js.map} +1 -1
- package/dist/{token-C8Uh7aUe.js → token-C_q95GF7.js} +54 -5
- package/dist/{token-C8Uh7aUe.js.map → token-C_q95GF7.js.map} +1 -1
- package/package.json +2 -2
- package/dist/engine-D8dlcSW6.js +0 -2
- package/dist/exec-DTO2oin0.js.map +0 -1
- package/dist/lifecycle-CRTWEbiP.js +0 -2
- package/dist/lifecycle-DUPXAOQS.js +0 -2
- package/dist/models-Cv6gprpy.js.map +0 -1
- package/dist/paths-Dz1WyRJG.js +0 -2
- package/dist/peer-mcp-personas-B22ncnbQ.js.map +0 -1
- package/dist/server-setup-BM9Smfek.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { A as searchWeb, B as readIteratorWithTimeout, Dt as toolbeltPathOverride, F as isAdvisorRequested, H as handleMcpDelete, I as buildAnthropicErrorEvent, It as withOneMSuffix, L as buildOpenAIErrorEvent, Lt as withInstallLock, M as ADVISOR_TOOL_INSTRUCTIONS, Mt as UPSTREAM_FETCH_TIMEOUT_MS, N as buildAdvisorStream, Nt as UPSTREAM_INACTIVITY_TIMEOUT_MS, P as injectAdvisorTool, Pt as generateRandomPort, R as isControllerClosedError, U as handleMcpPost, V as relayAnthropicStream, W as agentToolsEnabled, at as countTokens, ct as assembleResponsesPayload, dt as createResponses, ft as createChatCompletions, ht as parseJsonOrDiagnose, it as shimDefaultsToXhigh, j as ADVISOR_INTERNAL_TOOL_NAME, lt as resolveMcpToolTimeoutMs, mt as readResponseBodyCapped, ot as createMessages, pt as MAX_RESPONSE_BODY_BYTES, r as assertMcpToolSurfaceConsistent, st as getTokenCount, ut as pickEndpoint, w as toolbeltEnabled, z as logStreamError } from "./peer-mcp-personas-
|
|
1
|
+
import { A as searchWeb, B as readIteratorWithTimeout, Dt as toolbeltPathOverride, F as isAdvisorRequested, H as handleMcpDelete, I as buildAnthropicErrorEvent, It as withOneMSuffix, L as buildOpenAIErrorEvent, Lt as withInstallLock, M as ADVISOR_TOOL_INSTRUCTIONS, Mt as UPSTREAM_FETCH_TIMEOUT_MS, N as buildAdvisorStream, Nt as UPSTREAM_INACTIVITY_TIMEOUT_MS, P as injectAdvisorTool, Pt as generateRandomPort, R as isControllerClosedError, U as handleMcpPost, V as relayAnthropicStream, W as agentToolsEnabled, at as countTokens, ct as assembleResponsesPayload, dt as createResponses, ft as createChatCompletions, ht as parseJsonOrDiagnose, it as shimDefaultsToXhigh, j as ADVISOR_INTERNAL_TOOL_NAME, lt as resolveMcpToolTimeoutMs, mt as readResponseBodyCapped, ot as createMessages, pt as MAX_RESPONSE_BODY_BYTES, r as assertMcpToolSurfaceConsistent, st as getTokenCount, ut as pickEndpoint, w as toolbeltEnabled, z as logStreamError } from "./peer-mcp-personas-DSbBlgFt.js";
|
|
2
2
|
import { t as getPackageVersion } from "./version-_Q1WpsQp.js";
|
|
3
|
-
import { i as ensurePaths, t as PATHS } from "./paths-
|
|
4
|
-
import { C as copilotHeaders, O as state, S as copilotBaseUrl, a as cacheCopilotVersion, c as filterBetaHeader, d as resolveModel, f as sleep, g as fetchWithTransientRetry, i as tryRefreshAndRetry, l as isNullish, n as setupGitHubAgentToken, o as cacheModels, r as setupGitHubToken, s as cacheVSCodeVersion, t as setupCopilotToken, v as HTTPError, y as forwardError } from "./token-
|
|
5
|
-
import { t as getCopilotUsage } from "./get-copilot-usage-
|
|
6
|
-
import { a as resolveExecutable, n as killManagedTree, o as runCommandCapture, r as parseBoolEnv, s as runCommandVoid } from "./exec-
|
|
3
|
+
import { i as ensurePaths, t as PATHS } from "./paths-Bg1_DWhi.js";
|
|
4
|
+
import { C as copilotHeaders, O as state, S as copilotBaseUrl, a as cacheCopilotVersion, c as filterBetaHeader, d as resolveModel, f as sleep, g as fetchWithTransientRetry, i as tryRefreshAndRetry, l as isNullish, n as setupGitHubAgentToken, o as cacheModels, r as setupGitHubToken, s as cacheVSCodeVersion, t as setupCopilotToken, v as HTTPError, y as forwardError } from "./token-C_q95GF7.js";
|
|
5
|
+
import { t as getCopilotUsage } from "./get-copilot-usage-CFEpeA7E.js";
|
|
6
|
+
import { a as resolveExecutable, n as killManagedTree, o as runCommandCapture, r as parseBoolEnv, s as runCommandVoid } from "./exec-y8C_MU8A.js";
|
|
7
7
|
import consola from "consola";
|
|
8
8
|
import * as fs$2 from "node:fs";
|
|
9
9
|
import fs, { existsSync } from "node:fs";
|
|
@@ -1045,60 +1045,6 @@ function initProxyFromEnv() {
|
|
|
1045
1045
|
}
|
|
1046
1046
|
}
|
|
1047
1047
|
//#endregion
|
|
1048
|
-
//#region package.json
|
|
1049
|
-
var name = "github-router";
|
|
1050
|
-
var version = "0.3.263";
|
|
1051
|
-
//#endregion
|
|
1052
|
-
//#region src/lib/approval.ts
|
|
1053
|
-
const awaitApproval = async () => {
|
|
1054
|
-
if (!await consola.prompt(`Accept incoming request?`, { type: "confirm" })) throw new HTTPError("Request rejected by user", Response.json({ message: "Request rejected by user" }, { status: 403 }));
|
|
1055
|
-
};
|
|
1056
|
-
//#endregion
|
|
1057
|
-
//#region src/lib/rate-limit.ts
|
|
1058
|
-
const RATE_LIMIT_QUEUE_TIMEOUT_MS = 5e3;
|
|
1059
|
-
let rateLimitChain = Promise.resolve();
|
|
1060
|
-
async function checkRateLimit(state) {
|
|
1061
|
-
if (state.rateLimitSeconds === void 0) return;
|
|
1062
|
-
const ticket = { aborted: false };
|
|
1063
|
-
const myTurn = rateLimitChain.then(() => doCheck(state, ticket));
|
|
1064
|
-
rateLimitChain = myTurn.catch(() => {});
|
|
1065
|
-
return Promise.race([myTurn, sleep(RATE_LIMIT_QUEUE_TIMEOUT_MS).then(() => {
|
|
1066
|
-
ticket.aborted = true;
|
|
1067
|
-
throw new HTTPError("Rate limit queue wait exceeded", Response.json({
|
|
1068
|
-
type: "error",
|
|
1069
|
-
error: {
|
|
1070
|
-
type: "rate_limit_error",
|
|
1071
|
-
message: `Rate limit queue exceeded ${RATE_LIMIT_QUEUE_TIMEOUT_MS}ms; try again`
|
|
1072
|
-
}
|
|
1073
|
-
}, { status: 429 }));
|
|
1074
|
-
})]);
|
|
1075
|
-
}
|
|
1076
|
-
async function doCheck(state, ticket) {
|
|
1077
|
-
if (state.rateLimitSeconds === void 0) return;
|
|
1078
|
-
if (ticket.aborted) return;
|
|
1079
|
-
const now = Date.now();
|
|
1080
|
-
if (!state.lastRequestTimestamp) {
|
|
1081
|
-
state.lastRequestTimestamp = now;
|
|
1082
|
-
return;
|
|
1083
|
-
}
|
|
1084
|
-
const elapsedSeconds = (now - state.lastRequestTimestamp) / 1e3;
|
|
1085
|
-
if (elapsedSeconds > state.rateLimitSeconds) {
|
|
1086
|
-
state.lastRequestTimestamp = now;
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
const waitTimeSeconds = Math.ceil(state.rateLimitSeconds - elapsedSeconds);
|
|
1090
|
-
if (!state.rateLimitWait) {
|
|
1091
|
-
consola.warn(`Rate limit exceeded. Need to wait ${waitTimeSeconds} more seconds.`);
|
|
1092
|
-
throw new HTTPError("Rate limit exceeded", Response.json({ message: "Rate limit exceeded" }, { status: 429 }));
|
|
1093
|
-
}
|
|
1094
|
-
const waitTimeMs = waitTimeSeconds * 1e3;
|
|
1095
|
-
consola.warn(`Rate limit reached. Waiting ${waitTimeSeconds} seconds before proceeding...`);
|
|
1096
|
-
await sleep(waitTimeMs);
|
|
1097
|
-
if (ticket.aborted) return;
|
|
1098
|
-
state.lastRequestTimestamp = Date.now();
|
|
1099
|
-
consola.info("Rate limit wait completed, proceeding with request");
|
|
1100
|
-
}
|
|
1101
|
-
//#endregion
|
|
1102
1048
|
//#region src/lib/request-log.ts
|
|
1103
1049
|
/**
|
|
1104
1050
|
* Rolling request-body size distribution.
|
|
@@ -1122,6 +1068,79 @@ function recordBodySize(bytes) {
|
|
|
1122
1068
|
bodySizeIdx = (bodySizeIdx + 1) % BODY_SIZE_RING;
|
|
1123
1069
|
if (bodySizeCount < BODY_SIZE_RING) bodySizeCount++;
|
|
1124
1070
|
}
|
|
1071
|
+
/** Percentile snapshot of observed body sizes, or undefined if none seen. */
|
|
1072
|
+
function bodySizeStats() {
|
|
1073
|
+
if (bodySizeCount === 0) return void 0;
|
|
1074
|
+
const s = Array.from(bodySizes.slice(0, bodySizeCount)).sort((a, b) => a - b);
|
|
1075
|
+
const at = (q) => s[Math.min(s.length - 1, Math.floor(s.length * q))];
|
|
1076
|
+
return {
|
|
1077
|
+
count: bodySizeCount,
|
|
1078
|
+
p50: at(.5),
|
|
1079
|
+
p95: at(.95),
|
|
1080
|
+
p99: at(.99),
|
|
1081
|
+
max: s[s.length - 1]
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Print the observed body-size distribution, if any requests were seen.
|
|
1086
|
+
*
|
|
1087
|
+
* Without this the ring was WRITE-ONLY: `bodySizeStats()` had no caller
|
|
1088
|
+
* anywhere in `src/`, so the one number its own doc comment says the decision
|
|
1089
|
+
* needs ("benchmarks at 4.5 MiB mean nothing if the real p50 is 40 KiB") was
|
|
1090
|
+
* collected every request and readable by nobody.
|
|
1091
|
+
*
|
|
1092
|
+
* At `consola.info` because it is a once-per-session line answering a question
|
|
1093
|
+
* an operator or contributor actually asks; anything noisier would be a reason
|
|
1094
|
+
* to switch it off.
|
|
1095
|
+
*/
|
|
1096
|
+
function logBodySizeStats() {
|
|
1097
|
+
const stats = bodySizeStats();
|
|
1098
|
+
if (!stats) return void 0;
|
|
1099
|
+
const line = `request body sizes (n=${stats.count}): p50 ${formatBytes(stats.p50)}, p95 ${formatBytes(stats.p95)}, p99 ${formatBytes(stats.p99)}, max ${formatBytes(stats.max)}`;
|
|
1100
|
+
consola.info(line);
|
|
1101
|
+
return line;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Report the distribution once, on the way out.
|
|
1105
|
+
*
|
|
1106
|
+
* Self-registered rather than wired into each subcommand's shutdown chain
|
|
1107
|
+
* because `start` has NO `launchChild`/`onShutdown` to hang it on (see the
|
|
1108
|
+
* comment at its `startKeepAwake()` call), and a stat that only appeared under
|
|
1109
|
+
* `claude` would answer the question for one of three entry points. Same
|
|
1110
|
+
* pattern, and same reasoning, as `~/lib/keep-awake`'s own reaper.
|
|
1111
|
+
*
|
|
1112
|
+
* **Windows caveat, measured rather than assumed.** This reports on a clean
|
|
1113
|
+
* exit and on a signal the runtime actually delivers to JS (Ctrl-C in an
|
|
1114
|
+
* interactive console). It does NOT report when the process is killed by
|
|
1115
|
+
* another process on Windows: `child.kill(...)` and `taskkill` terminate
|
|
1116
|
+
* abruptly via `TerminateProcess`, dispatching no JS signal event, so NO
|
|
1117
|
+
* handler runs — not this one, and not `process.on("exit")` either. Verified
|
|
1118
|
+
* on a real win32 host: a child registering both handlers and killed by its
|
|
1119
|
+
* parent produced no output and exited with a null code.
|
|
1120
|
+
*
|
|
1121
|
+
* That is acceptable for a diagnostic — it is a measurement aid, not a
|
|
1122
|
+
* durability guarantee, and the interactive case it exists to serve (run a
|
|
1123
|
+
* session, Ctrl-C, read the distribution) does work. It is NOT acceptable for
|
|
1124
|
+
* anything durable: do not move credential, ledger, or cleanup work onto this
|
|
1125
|
+
* hook. The existing teardown paths that must survive a hard kill are built
|
|
1126
|
+
* differently on purpose (see `~/lib/keep-awake`, which relies on the OS
|
|
1127
|
+
* releasing a thread-scoped assertion rather than on a handler running, and
|
|
1128
|
+
* `~/lib/process-guard`, which uses a separate reaper process).
|
|
1129
|
+
*
|
|
1130
|
+
* Idempotent: `exit` can follow a signal handler, and both call this.
|
|
1131
|
+
*/
|
|
1132
|
+
let statsReported = false;
|
|
1133
|
+
function reportOnce() {
|
|
1134
|
+
if (statsReported) return;
|
|
1135
|
+
statsReported = true;
|
|
1136
|
+
logBodySizeStats();
|
|
1137
|
+
}
|
|
1138
|
+
let exitHooksInstalled = false;
|
|
1139
|
+
function installBodySizeStatsExitHook() {
|
|
1140
|
+
if (exitHooksInstalled) return;
|
|
1141
|
+
exitHooksInstalled = true;
|
|
1142
|
+
process.once("exit", reportOnce);
|
|
1143
|
+
}
|
|
1125
1144
|
/** Format a byte count compactly (1.2M / 15.3K / 900B). */
|
|
1126
1145
|
function formatBytes(n) {
|
|
1127
1146
|
if (n >= 1048576) return `${(n / 1048576).toFixed(1)}M`;
|
|
@@ -1256,6 +1275,60 @@ function collectToolFieldKeys(body) {
|
|
|
1256
1275
|
return [...seen].sort();
|
|
1257
1276
|
}
|
|
1258
1277
|
//#endregion
|
|
1278
|
+
//#region package.json
|
|
1279
|
+
var name = "github-router";
|
|
1280
|
+
var version = "0.3.269";
|
|
1281
|
+
//#endregion
|
|
1282
|
+
//#region src/lib/approval.ts
|
|
1283
|
+
const awaitApproval = async () => {
|
|
1284
|
+
if (!await consola.prompt(`Accept incoming request?`, { type: "confirm" })) throw new HTTPError("Request rejected by user", Response.json({ message: "Request rejected by user" }, { status: 403 }));
|
|
1285
|
+
};
|
|
1286
|
+
//#endregion
|
|
1287
|
+
//#region src/lib/rate-limit.ts
|
|
1288
|
+
const RATE_LIMIT_QUEUE_TIMEOUT_MS = 5e3;
|
|
1289
|
+
let rateLimitChain = Promise.resolve();
|
|
1290
|
+
async function checkRateLimit(state) {
|
|
1291
|
+
if (state.rateLimitSeconds === void 0) return;
|
|
1292
|
+
const ticket = { aborted: false };
|
|
1293
|
+
const myTurn = rateLimitChain.then(() => doCheck(state, ticket));
|
|
1294
|
+
rateLimitChain = myTurn.catch(() => {});
|
|
1295
|
+
return Promise.race([myTurn, sleep(RATE_LIMIT_QUEUE_TIMEOUT_MS).then(() => {
|
|
1296
|
+
ticket.aborted = true;
|
|
1297
|
+
throw new HTTPError("Rate limit queue wait exceeded", Response.json({
|
|
1298
|
+
type: "error",
|
|
1299
|
+
error: {
|
|
1300
|
+
type: "rate_limit_error",
|
|
1301
|
+
message: `Rate limit queue exceeded ${RATE_LIMIT_QUEUE_TIMEOUT_MS}ms; try again`
|
|
1302
|
+
}
|
|
1303
|
+
}, { status: 429 }));
|
|
1304
|
+
})]);
|
|
1305
|
+
}
|
|
1306
|
+
async function doCheck(state, ticket) {
|
|
1307
|
+
if (state.rateLimitSeconds === void 0) return;
|
|
1308
|
+
if (ticket.aborted) return;
|
|
1309
|
+
const now = Date.now();
|
|
1310
|
+
if (!state.lastRequestTimestamp) {
|
|
1311
|
+
state.lastRequestTimestamp = now;
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const elapsedSeconds = (now - state.lastRequestTimestamp) / 1e3;
|
|
1315
|
+
if (elapsedSeconds > state.rateLimitSeconds) {
|
|
1316
|
+
state.lastRequestTimestamp = now;
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const waitTimeSeconds = Math.ceil(state.rateLimitSeconds - elapsedSeconds);
|
|
1320
|
+
if (!state.rateLimitWait) {
|
|
1321
|
+
consola.warn(`Rate limit exceeded. Need to wait ${waitTimeSeconds} more seconds.`);
|
|
1322
|
+
throw new HTTPError("Rate limit exceeded", Response.json({ message: "Rate limit exceeded" }, { status: 429 }));
|
|
1323
|
+
}
|
|
1324
|
+
const waitTimeMs = waitTimeSeconds * 1e3;
|
|
1325
|
+
consola.warn(`Rate limit reached. Waiting ${waitTimeSeconds} seconds before proceeding...`);
|
|
1326
|
+
await sleep(waitTimeMs);
|
|
1327
|
+
if (ticket.aborted) return;
|
|
1328
|
+
state.lastRequestTimestamp = Date.now();
|
|
1329
|
+
consola.info("Rate limit wait completed, proceeding with request");
|
|
1330
|
+
}
|
|
1331
|
+
//#endregion
|
|
1259
1332
|
//#region src/routes/chat-completions/handler.ts
|
|
1260
1333
|
const ENCODER$1 = new TextEncoder();
|
|
1261
1334
|
function formatSSE$1(chunk) {
|
|
@@ -1900,6 +1973,40 @@ function anthropicSseStreamFromEvents(events, opts) {
|
|
|
1900
1973
|
controller.close();
|
|
1901
1974
|
} catch {}
|
|
1902
1975
|
};
|
|
1976
|
+
/**
|
|
1977
|
+
* Tear down the upstream, exactly once, on a PREMATURE end of stream.
|
|
1978
|
+
*
|
|
1979
|
+
* Called from the three places a stream can end early — the stall branch,
|
|
1980
|
+
* the error branch, and consumer `cancel()` — because all three leave the
|
|
1981
|
+
* upstream fetch running and the generator suspended. Both halves matter:
|
|
1982
|
+
* `onCancel` aborts the fetch, and `return()` drives the generator's
|
|
1983
|
+
* `finally`; `return()` alone cannot release a generator parked on a
|
|
1984
|
+
* never-settling await, since it queues behind that await.
|
|
1985
|
+
*
|
|
1986
|
+
* Two things this is deliberately NOT:
|
|
1987
|
+
*
|
|
1988
|
+
* - **Not a `finally` around `pull`.** `pull` re-enters per chunk, so a
|
|
1989
|
+
* `finally` there would fire on every successful chunk.
|
|
1990
|
+
* - **Not called on normal completion** (`res.done`). Aborting an
|
|
1991
|
+
* AbortController whose fetch has already completed DESTROYS the socket
|
|
1992
|
+
* instead of returning it to the keep-alive pool, so every successful
|
|
1993
|
+
* turn would pay a fresh TLS handshake. Premature-only is the whole
|
|
1994
|
+
* correctness condition, and `tornDown` only makes it idempotent — it
|
|
1995
|
+
* does not make an unconditional call safe.
|
|
1996
|
+
*/
|
|
1997
|
+
let tornDown = false;
|
|
1998
|
+
const teardownUpstream = () => {
|
|
1999
|
+
if (tornDown) return;
|
|
2000
|
+
tornDown = true;
|
|
2001
|
+
try {
|
|
2002
|
+
opts.onCancel?.();
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
consola.warn(`Anthropic-translate onCancel threw during teardown at ${opts.routePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2005
|
+
}
|
|
2006
|
+
try {
|
|
2007
|
+
events.return?.(void 0)?.catch?.(() => void 0);
|
|
2008
|
+
} catch {}
|
|
2009
|
+
};
|
|
1903
2010
|
return new ReadableStream({
|
|
1904
2011
|
async pull(controller) {
|
|
1905
2012
|
if (consumerCancelled || finished) {
|
|
@@ -1918,8 +2025,7 @@ function anthropicSseStreamFromEvents(events, opts) {
|
|
|
1918
2025
|
}
|
|
1919
2026
|
const message = `Upstream stalled: no stream activity for ${timeoutMs}ms`;
|
|
1920
2027
|
consola.error(`Anthropic-translate stream inactivity timeout at ${opts.routePath}: ${message}`);
|
|
1921
|
-
|
|
1922
|
-
events.return?.(void 0)?.catch?.(() => void 0);
|
|
2028
|
+
teardownUpstream();
|
|
1923
2029
|
try {
|
|
1924
2030
|
controller.enqueue(enc.encode(buildAnthropicErrorEvent("timeout_error", message)));
|
|
1925
2031
|
} catch (enqueueError) {
|
|
@@ -1938,6 +2044,7 @@ function anthropicSseStreamFromEvents(events, opts) {
|
|
|
1938
2044
|
const name = err instanceof Error ? err.name : "Error";
|
|
1939
2045
|
const message = err instanceof Error ? err.message : String(err);
|
|
1940
2046
|
consola.error(`Anthropic-translate stream interrupted at ${opts.routePath}: ${name}: ${JSON.stringify(message)}`);
|
|
2047
|
+
teardownUpstream();
|
|
1941
2048
|
try {
|
|
1942
2049
|
controller.enqueue(enc.encode(buildAnthropicErrorEvent(name, message)));
|
|
1943
2050
|
} catch (enqueueError) {
|
|
@@ -1968,8 +2075,7 @@ function anthropicSseStreamFromEvents(events, opts) {
|
|
|
1968
2075
|
cancel() {
|
|
1969
2076
|
consumerCancelled = true;
|
|
1970
2077
|
finished = true;
|
|
1971
|
-
|
|
1972
|
-
events.return?.(void 0)?.catch?.(() => void 0);
|
|
2078
|
+
teardownUpstream();
|
|
1973
2079
|
}
|
|
1974
2080
|
});
|
|
1975
2081
|
}
|
|
@@ -4751,6 +4857,7 @@ async function setupAndServe(options) {
|
|
|
4751
4857
|
await cacheModels();
|
|
4752
4858
|
consola.debug(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
4753
4859
|
const serveOptions = buildServeOptions(server.fetch, options.silent);
|
|
4860
|
+
installBodySizeStatsExitHook();
|
|
4754
4861
|
let srvxServer;
|
|
4755
4862
|
if (options.port !== void 0) srvxServer = serve({
|
|
4756
4863
|
...serveOptions,
|
|
@@ -5156,4 +5263,4 @@ function getCodexEnvVars(serverUrl) {
|
|
|
5156
5263
|
//#endregion
|
|
5157
5264
|
export { sharedServerArgs as a, listModelsForEndpoint as c, checkClaudeVersion as d, updateClaude as f, setupAndServe as i, enableFileLogging as l, getCodexEnvVars as n, startKeepAwake as o, parseSharedArgs as r, stopKeepAwake as s, getClaudeCodeEnvVars as t, runSelfUpdate as u };
|
|
5158
5265
|
|
|
5159
|
-
//# sourceMappingURL=server-setup-
|
|
5266
|
+
//# sourceMappingURL=server-setup-Dd293T1O.js.map
|