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.
Files changed (76) hide show
  1. package/dist/{attribution-settings-DFIVIRG8.js → attribution-settings-B1txv_Lk.js} +4 -4
  2. package/dist/{attribution-settings-DFIVIRG8.js.map → attribution-settings-B1txv_Lk.js.map} +1 -1
  3. package/dist/{auth-WA88cE9q.js → auth-r-my7efo.js} +3 -3
  4. package/dist/{auth-WA88cE9q.js.map → auth-r-my7efo.js.map} +1 -1
  5. package/dist/browser-bridge/index.js +67 -27
  6. package/dist/browser-ext/manifest.json +1 -1
  7. package/dist/{check-usage-v2apGKz7.js → check-usage-CZr2ZkG0.js} +4 -4
  8. package/dist/{check-usage-v2apGKz7.js.map → check-usage-CZr2ZkG0.js.map} +1 -1
  9. package/dist/{claude-BCI3Sx6_.js → claude-BjNY0J6e.js} +15 -14
  10. package/dist/{claude-BCI3Sx6_.js.map → claude-BjNY0J6e.js.map} +1 -1
  11. package/dist/{codex-DyJIrbJt.js → codex-CoRWpkHL.js} +5 -5
  12. package/dist/{codex-DyJIrbJt.js.map → codex-CoRWpkHL.js.map} +1 -1
  13. package/dist/{debug-vshv4nIv.js → debug-CuuxCk_J.js} +2 -2
  14. package/dist/{debug-vshv4nIv.js.map → debug-CuuxCk_J.js.map} +1 -1
  15. package/dist/engine-DpVC7HRL.js +2 -0
  16. package/dist/{exec-DTO2oin0.js → exec-y8C_MU8A.js} +89 -18
  17. package/dist/exec-y8C_MU8A.js.map +1 -0
  18. package/dist/{gate-discovery-cMbt8Ct2.js → gate-discovery-D-7oua8b.js} +6 -6
  19. package/dist/{gate-discovery-cMbt8Ct2.js.map → gate-discovery-D-7oua8b.js.map} +1 -1
  20. package/dist/{get-copilot-usage-DOFJWJaQ.js → get-copilot-usage-CFEpeA7E.js} +2 -2
  21. package/dist/{get-copilot-usage-DOFJWJaQ.js.map → get-copilot-usage-CFEpeA7E.js.map} +1 -1
  22. package/dist/{internal-artifact-open-CZGO831M.js → internal-artifact-open-CDqPfegE.js} +2 -2
  23. package/dist/{internal-artifact-open-CZGO831M.js.map → internal-artifact-open-CDqPfegE.js.map} +1 -1
  24. package/dist/{internal-plan-review-6gkcOAjT.js → internal-plan-review-CH9qTHoZ.js} +3 -3
  25. package/dist/{internal-plan-review-6gkcOAjT.js.map → internal-plan-review-CH9qTHoZ.js.map} +1 -1
  26. package/dist/{internal-prompt-submit-C0XOPgcP.js → internal-prompt-submit-Bve1s4z_.js} +5 -5
  27. package/dist/{internal-prompt-submit-C0XOPgcP.js.map → internal-prompt-submit-Bve1s4z_.js.map} +1 -1
  28. package/dist/{internal-session-bind-DtSBTkQe.js → internal-session-bind-Cc8bMXHK.js} +2 -2
  29. package/dist/{internal-session-bind-DtSBTkQe.js.map → internal-session-bind-Cc8bMXHK.js.map} +1 -1
  30. package/dist/{internal-stop-hook-CBcE2Js8.js → internal-stop-hook-Db5o9RcZ.js} +6 -6
  31. package/dist/{internal-stop-hook-CBcE2Js8.js.map → internal-stop-hook-Db5o9RcZ.js.map} +1 -1
  32. package/dist/{internal-stop-review-_1Mqz7eT.js → internal-stop-review-CWE7_QVv.js} +2 -2
  33. package/dist/{internal-stop-review-_1Mqz7eT.js.map → internal-stop-review-CWE7_QVv.js.map} +1 -1
  34. package/dist/{lifecycle-DjC9tC1A.js → lifecycle-0XrazT05.js} +2 -2
  35. package/dist/{lifecycle-DjC9tC1A.js.map → lifecycle-0XrazT05.js.map} +1 -1
  36. package/dist/lifecycle-2M0IThRr.js +2 -0
  37. package/dist/{lifecycle-Bd1Fyf4B.js → lifecycle-AgNOtFRv.js} +3 -3
  38. package/dist/{lifecycle-Bd1Fyf4B.js.map → lifecycle-AgNOtFRv.js.map} +1 -1
  39. package/dist/lifecycle-C8kFsAj-.js +2 -0
  40. package/dist/main.js +14 -14
  41. package/dist/{models-Cv6gprpy.js → models-CJLt4XjP.js} +35 -30
  42. package/dist/models-CJLt4XjP.js.map +1 -0
  43. package/dist/{orchestration-B6qidQAd.js → orchestration-B_Q3ncXI.js} +3 -3
  44. package/dist/{orchestration-B6qidQAd.js.map → orchestration-B_Q3ncXI.js.map} +1 -1
  45. package/dist/paths-BbHtQAOs.js +2 -0
  46. package/dist/{paths-BXrbboAS.js → paths-Bg1_DWhi.js} +3 -3
  47. package/dist/{paths-BXrbboAS.js.map → paths-Bg1_DWhi.js.map} +1 -1
  48. package/dist/{peer-mcp-personas-B22ncnbQ.js → peer-mcp-personas-DSbBlgFt.js} +85 -26
  49. package/dist/peer-mcp-personas-DSbBlgFt.js.map +1 -0
  50. package/dist/{plan-review-hook-Bo4y1r3j.js → plan-review-hook-BKOcW5s4.js} +3 -3
  51. package/dist/{plan-review-hook-Bo4y1r3j.js.map → plan-review-hook-BKOcW5s4.js.map} +1 -1
  52. package/dist/{prompt-submit-hook-BFRFLSk5.js → prompt-submit-hook-F7Zih_au.js} +2 -2
  53. package/dist/{prompt-submit-hook-BFRFLSk5.js.map → prompt-submit-hook-F7Zih_au.js.map} +1 -1
  54. package/dist/{provision-DSpLQHC3.js → provision-CA-xwnFu.js} +5 -5
  55. package/dist/{provision-DSpLQHC3.js.map → provision-CA-xwnFu.js.map} +1 -1
  56. package/dist/{serve-D9tBVVoK.js → serve-DHny4ewF.js} +11 -11
  57. package/dist/{serve-D9tBVVoK.js.map → serve-DHny4ewF.js.map} +1 -1
  58. package/dist/{server-setup-BM9Smfek.js → server-setup-Dd293T1O.js} +171 -64
  59. package/dist/server-setup-Dd293T1O.js.map +1 -0
  60. package/dist/{start-D7Hx2T_T.js → start-P2rmHBkL.js} +3 -3
  61. package/dist/{start-D7Hx2T_T.js.map → start-P2rmHBkL.js.map} +1 -1
  62. package/dist/{stop-gate-hook-Bmwla_2u.js → stop-gate-hook-BqpumJtl.js} +3 -3
  63. package/dist/{stop-gate-hook-Bmwla_2u.js.map → stop-gate-hook-BqpumJtl.js.map} +1 -1
  64. package/dist/{stop-gate-policy-CoMKWSsS.js → stop-gate-policy-C0XOBZhB.js} +3 -3
  65. package/dist/{stop-gate-policy-CoMKWSsS.js.map → stop-gate-policy-C0XOBZhB.js.map} +1 -1
  66. package/dist/{token-C8Uh7aUe.js → token-C_q95GF7.js} +54 -5
  67. package/dist/{token-C8Uh7aUe.js.map → token-C_q95GF7.js.map} +1 -1
  68. package/package.json +2 -2
  69. package/dist/engine-D8dlcSW6.js +0 -2
  70. package/dist/exec-DTO2oin0.js.map +0 -1
  71. package/dist/lifecycle-CRTWEbiP.js +0 -2
  72. package/dist/lifecycle-DUPXAOQS.js +0 -2
  73. package/dist/models-Cv6gprpy.js.map +0 -1
  74. package/dist/paths-Dz1WyRJG.js +0 -2
  75. package/dist/peer-mcp-personas-B22ncnbQ.js.map +0 -1
  76. 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-B22ncnbQ.js";
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-BXrbboAS.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-C8Uh7aUe.js";
5
- import { t as getCopilotUsage } from "./get-copilot-usage-DOFJWJaQ.js";
6
- import { a as resolveExecutable, n as killManagedTree, o as runCommandCapture, r as parseBoolEnv, s as runCommandVoid } from "./exec-DTO2oin0.js";
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
- opts.onCancel?.();
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
- opts.onCancel?.();
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-BM9Smfek.js.map
5266
+ //# sourceMappingURL=server-setup-Dd293T1O.js.map