github-router 0.3.269 → 0.3.271
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-B1txv_Lk.js → attribution-settings-DLRQEKpU.js} +3 -3
- package/dist/{attribution-settings-B1txv_Lk.js.map → attribution-settings-DLRQEKpU.js.map} +1 -1
- package/dist/{auth-r-my7efo.js → auth-DIFbHOu0.js} +2 -2
- package/dist/{auth-r-my7efo.js.map → auth-DIFbHOu0.js.map} +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-CZr2ZkG0.js → check-usage-DfnrR-d_.js} +3 -3
- package/dist/{check-usage-CZr2ZkG0.js.map → check-usage-DfnrR-d_.js.map} +1 -1
- package/dist/{claude-BjNY0J6e.js → claude-BpQVQE0T.js} +7 -7
- package/dist/{claude-BjNY0J6e.js.map → claude-BpQVQE0T.js.map} +1 -1
- package/dist/{codex-CoRWpkHL.js → codex-CGMiGJy1.js} +5 -5
- package/dist/{codex-CoRWpkHL.js.map → codex-CGMiGJy1.js.map} +1 -1
- package/dist/engine-DUJyXWZn.js +2 -0
- package/dist/{gate-discovery-D-7oua8b.js → gate-discovery-BBA7cV1D.js} +2 -2
- package/dist/{gate-discovery-D-7oua8b.js.map → gate-discovery-BBA7cV1D.js.map} +1 -1
- package/dist/{get-copilot-usage-CFEpeA7E.js → get-copilot-usage-Ds09xSmH.js} +2 -2
- package/dist/{get-copilot-usage-CFEpeA7E.js.map → get-copilot-usage-Ds09xSmH.js.map} +1 -1
- package/dist/{internal-stop-hook-Db5o9RcZ.js → internal-stop-hook-CO2NDmGL.js} +2 -2
- package/dist/{internal-stop-hook-Db5o9RcZ.js.map → internal-stop-hook-CO2NDmGL.js.map} +1 -1
- package/dist/main.js +8 -8
- package/dist/{models-CJLt4XjP.js → models-BFQUp0ov.js} +2 -2
- package/dist/{models-CJLt4XjP.js.map → models-BFQUp0ov.js.map} +1 -1
- package/dist/{peer-mcp-personas-DSbBlgFt.js → peer-mcp-personas-CN8xaXcV.js} +377 -101
- package/dist/peer-mcp-personas-CN8xaXcV.js.map +1 -0
- package/dist/{provision-CA-xwnFu.js → provision-CiPjSx7J.js} +2 -2
- package/dist/{provision-CA-xwnFu.js.map → provision-CiPjSx7J.js.map} +1 -1
- package/dist/{serve-DHny4ewF.js → serve-CpTMr5D9.js} +6 -6
- package/dist/{serve-DHny4ewF.js.map → serve-CpTMr5D9.js.map} +1 -1
- package/dist/{server-setup-Dd293T1O.js → server-setup-B02Myghc.js} +285 -99
- package/dist/server-setup-B02Myghc.js.map +1 -0
- package/dist/{start-P2rmHBkL.js → start-CIV9eXVb.js} +3 -3
- package/dist/{start-P2rmHBkL.js.map → start-CIV9eXVb.js.map} +1 -1
- package/dist/{token-C_q95GF7.js → token-iaSGp76k.js} +303 -143
- package/dist/token-iaSGp76k.js.map +1 -0
- package/package.json +1 -1
- package/dist/engine-DpVC7HRL.js +0 -2
- package/dist/peer-mcp-personas-DSbBlgFt.js.map +0 -1
- package/dist/server-setup-Dd293T1O.js.map +0 -1
- package/dist/token-C_q95GF7.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as sharedServerArgs, i as setupAndServe, n as getCodexEnvVars, o as startKeepAwake, r as parseSharedArgs, t as getClaudeCodeEnvVars, u as runSelfUpdate } from "./server-setup-
|
|
1
|
+
import { $ as browserToolsEnabled, Ct as provisionAndIndexColbert, Et as warmTreeSitterPool, St as colbertDegradedWarning, bt as provisionBrowserAssets } from "./peer-mcp-personas-CN8xaXcV.js";
|
|
2
|
+
import { a as sharedServerArgs, i as setupAndServe, n as getCodexEnvVars, o as startKeepAwake, r as parseSharedArgs, t as getClaudeCodeEnvVars, u as runSelfUpdate } from "./server-setup-B02Myghc.js";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import process$1 from "node:process";
|
|
@@ -129,4 +129,4 @@ const start = defineCommand({
|
|
|
129
129
|
//#endregion
|
|
130
130
|
export { start };
|
|
131
131
|
|
|
132
|
-
//# sourceMappingURL=start-
|
|
132
|
+
//# sourceMappingURL=start-CIV9eXVb.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start-P2rmHBkL.js","names":["process"],"sources":["../src/lib/shell.ts","../src/start.ts"],"sourcesContent":["import process from \"node:process\"\n\ntype ShellName = \"bash\" | \"zsh\" | \"fish\" | \"powershell\" | \"cmd\" | \"sh\"\ntype EnvVars = Record<string, string | undefined>\n\nfunction getShell(): ShellName {\n const { platform, env } = process\n\n if (platform === \"win32\") {\n // Git Bash / MSYS2 / Cygwin set SHELL even on Windows\n if (env.SHELL) {\n if (env.SHELL.endsWith(\"zsh\")) return \"zsh\"\n if (env.SHELL.endsWith(\"fish\")) return \"fish\"\n if (env.SHELL.endsWith(\"bash\")) return \"bash\"\n return \"sh\"\n }\n\n // Windows PowerShell 5.x sets this\n if (env.POWERSHELL_DISTRIBUTION_CHANNEL) return \"powershell\"\n\n // PowerShell (both 5.x and 7+/pwsh) adds user-scoped module paths\n // at runtime. The system-level PSModulePath in CMD lacks these paths.\n if (env.PSModulePath) {\n const lower = env.PSModulePath.toLowerCase()\n if (\n lower.includes(\"documents\\\\powershell\")\n || lower.includes(\"documents\\\\windowspowershell\")\n ) {\n return \"powershell\"\n }\n }\n\n return \"cmd\"\n }\n\n const shellPath = env.SHELL\n if (shellPath) {\n if (shellPath.endsWith(\"zsh\")) return \"zsh\"\n if (shellPath.endsWith(\"fish\")) return \"fish\"\n if (shellPath.endsWith(\"bash\")) return \"bash\"\n }\n\n return \"sh\"\n}\n\nfunction quotePosixValue(value: string): string {\n return `'${value.replace(/'/g, \"'\\\\''\")}'`\n}\n\nfunction quotePowerShellValue(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`\n}\n\n/**\n * Generates a copy-pasteable script to set multiple environment variables\n * and run a subsequent command.\n * @param {EnvVars} envVars - An object of environment variables to set.\n * @param {string} commandToRun - The command to run after setting the variables.\n * @returns {string} The formatted script string.\n */\nexport function generateEnvScript(\n envVars: EnvVars,\n commandToRun: string = \"\",\n): string {\n const shell = getShell()\n const filteredEnvVars = Object.entries(envVars).filter(\n ([, value]) => value !== undefined,\n ) as Array<[string, string]>\n\n let commandBlock: string\n\n switch (shell) {\n case \"powershell\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `$env:${key} = ${quotePowerShellValue(value)}`)\n .join(\"; \")\n break\n }\n case \"cmd\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set \"${key}=${value}\"`)\n .join(\" & \")\n break\n }\n case \"fish\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set -gx ${key} ${quotePosixValue(value)}`)\n .join(\"; \")\n break\n }\n default: {\n // bash, zsh, sh\n const assignments = filteredEnvVars\n .map(([key, value]) => `${key}=${quotePosixValue(value)}`)\n .join(\" \")\n commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : \"\"\n break\n }\n }\n\n if (commandBlock && commandToRun) {\n const separator =\n shell === \"cmd\" ? \" & \" : shell === \"powershell\" ? \"; \" : \" && \"\n return `${commandBlock}${separator}${commandToRun}`\n }\n\n return commandBlock || commandToRun\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport clipboard from \"clipboardy\"\nimport consola from \"consola\"\n\nimport { generateEnvScript } from \"./lib/shell\"\nimport { DEFAULT_CODEX_MODEL, DEFAULT_PORT } from \"./lib/port\"\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport { browserToolsEnabled } from \"./lib/mcp-capabilities\"\nimport { colbertDegradedWarning, provisionAndIndexColbert } from \"./lib/colbert\"\nimport { startKeepAwake } from \"./lib/keep-awake\"\nimport { warmTreeSitterPool } from \"./lib/tree-sitter-pool/pool\"\nimport {\n getClaudeCodeEnvVars,\n getCodexEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport { runSelfUpdate } from \"./lib/self-update\"\n\nfunction printAndCopyCommand(command: string, label: string): void {\n consola.box(`${label}\\n\\n${command}`)\n try {\n clipboard.writeSync(command)\n consola.success(`Copied ${label} command to clipboard!`)\n } catch {\n consola.warn(\"Failed to copy to clipboard. Copy the command above manually.\")\n }\n}\n\nfunction generateClaudeCodeCommand(serverUrl: string, model?: string) {\n const envVars = getClaudeCodeEnvVars(serverUrl, model)\n const command = generateEnvScript(envVars, \"claude --dangerously-skip-permissions\")\n printAndCopyCommand(command, \"Claude Code\")\n}\n\nfunction generateCodexCommand(serverUrl: string, model?: string) {\n const codexModel = model ?? DEFAULT_CODEX_MODEL\n const envVars = getCodexEnvVars(serverUrl)\n const command = generateEnvScript(envVars, `codex -m ${codexModel}`)\n printAndCopyCommand(command, \"Codex CLI\")\n}\n\nexport const start = defineCommand({\n meta: {\n name: \"start\",\n description: \"Start the github-router server\",\n },\n args: {\n ...sharedServerArgs,\n cc: {\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Claude Code with Copilot API config\",\n },\n cx: {\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Codex CLI with Copilot API config\",\n },\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Override the default model (used with --cc or --cx)\",\n },\n },\n async run({ args }) {\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n\n const { serverUrl } = await setupAndServe({\n ...parsed,\n port: parsed.port ?? DEFAULT_PORT,\n silent: false,\n })\n\n // Best-effort self-update (detached, applies next launch). Runs\n // after the server is listening so the bounded probe can't delay it.\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Best-effort ColBERT semantic-search provision + background index of\n // the launch cwd (if a git repo). ON by default; never blocks launch,\n // never throws. Opt out with GH_ROUTER_DISABLE_SEMANTIC_SEARCH=1.\n void provisionAndIndexColbert()\n\n // Surface a terminally-failed semantic index to the HUMAN (see the note\n // in claude.ts). Fire-and-forget; lexical search still works.\n void colbertDegradedWarning()\n .then((warning) => {\n if (warning) process.stderr.write(`${warning}\\n`)\n })\n .catch(() => {})\n\n // Ready one tree-sitter worker in the background so the first code search\n // avoids WASM/grammar initialization. One worker keeps launch CPU bounded;\n // demand grows the pool lazily. Opt out with GH_ROUTER_DISABLE_TS_POOL_WARMUP=1.\n void warmTreeSitterPool()\n\n // Best-effort: keep the machine awake while the proxy serves (win32\n // default-on; opt out with GH_ROUTER_DISABLE_KEEP_AWAKE=1). Holds a\n // SetThreadExecutionState assertion via a persistent PowerShell\n // helper. Self-registers its own SIGINT/SIGTERM/exit reaper — load-\n // bearing here, because `start` has no launchChild/onShutdown.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the\n // stable app-dir and stamp the running version, so a one-time \"Load\n // unpacked\" survives npx/bunx upgrades. Gated on --browse; never\n // blocks launch, never throws.\n if (browserToolsEnabled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n if (args.cc) generateClaudeCodeCommand(serverUrl, args.model)\n if (args.cx) generateCodexCommand(serverUrl, args.model)\n\n consola.box(\n `🌐 Usage Viewer: https://animeshkundu.github.io/github-router/dashboard.html?endpoint=${serverUrl}/usage`,\n )\n },\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"start-CIV9eXVb.js","names":["process"],"sources":["../src/lib/shell.ts","../src/start.ts"],"sourcesContent":["import process from \"node:process\"\n\ntype ShellName = \"bash\" | \"zsh\" | \"fish\" | \"powershell\" | \"cmd\" | \"sh\"\ntype EnvVars = Record<string, string | undefined>\n\nfunction getShell(): ShellName {\n const { platform, env } = process\n\n if (platform === \"win32\") {\n // Git Bash / MSYS2 / Cygwin set SHELL even on Windows\n if (env.SHELL) {\n if (env.SHELL.endsWith(\"zsh\")) return \"zsh\"\n if (env.SHELL.endsWith(\"fish\")) return \"fish\"\n if (env.SHELL.endsWith(\"bash\")) return \"bash\"\n return \"sh\"\n }\n\n // Windows PowerShell 5.x sets this\n if (env.POWERSHELL_DISTRIBUTION_CHANNEL) return \"powershell\"\n\n // PowerShell (both 5.x and 7+/pwsh) adds user-scoped module paths\n // at runtime. The system-level PSModulePath in CMD lacks these paths.\n if (env.PSModulePath) {\n const lower = env.PSModulePath.toLowerCase()\n if (\n lower.includes(\"documents\\\\powershell\")\n || lower.includes(\"documents\\\\windowspowershell\")\n ) {\n return \"powershell\"\n }\n }\n\n return \"cmd\"\n }\n\n const shellPath = env.SHELL\n if (shellPath) {\n if (shellPath.endsWith(\"zsh\")) return \"zsh\"\n if (shellPath.endsWith(\"fish\")) return \"fish\"\n if (shellPath.endsWith(\"bash\")) return \"bash\"\n }\n\n return \"sh\"\n}\n\nfunction quotePosixValue(value: string): string {\n return `'${value.replace(/'/g, \"'\\\\''\")}'`\n}\n\nfunction quotePowerShellValue(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`\n}\n\n/**\n * Generates a copy-pasteable script to set multiple environment variables\n * and run a subsequent command.\n * @param {EnvVars} envVars - An object of environment variables to set.\n * @param {string} commandToRun - The command to run after setting the variables.\n * @returns {string} The formatted script string.\n */\nexport function generateEnvScript(\n envVars: EnvVars,\n commandToRun: string = \"\",\n): string {\n const shell = getShell()\n const filteredEnvVars = Object.entries(envVars).filter(\n ([, value]) => value !== undefined,\n ) as Array<[string, string]>\n\n let commandBlock: string\n\n switch (shell) {\n case \"powershell\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `$env:${key} = ${quotePowerShellValue(value)}`)\n .join(\"; \")\n break\n }\n case \"cmd\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set \"${key}=${value}\"`)\n .join(\" & \")\n break\n }\n case \"fish\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set -gx ${key} ${quotePosixValue(value)}`)\n .join(\"; \")\n break\n }\n default: {\n // bash, zsh, sh\n const assignments = filteredEnvVars\n .map(([key, value]) => `${key}=${quotePosixValue(value)}`)\n .join(\" \")\n commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : \"\"\n break\n }\n }\n\n if (commandBlock && commandToRun) {\n const separator =\n shell === \"cmd\" ? \" & \" : shell === \"powershell\" ? \"; \" : \" && \"\n return `${commandBlock}${separator}${commandToRun}`\n }\n\n return commandBlock || commandToRun\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport clipboard from \"clipboardy\"\nimport consola from \"consola\"\n\nimport { generateEnvScript } from \"./lib/shell\"\nimport { DEFAULT_CODEX_MODEL, DEFAULT_PORT } from \"./lib/port\"\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport { browserToolsEnabled } from \"./lib/mcp-capabilities\"\nimport { colbertDegradedWarning, provisionAndIndexColbert } from \"./lib/colbert\"\nimport { startKeepAwake } from \"./lib/keep-awake\"\nimport { warmTreeSitterPool } from \"./lib/tree-sitter-pool/pool\"\nimport {\n getClaudeCodeEnvVars,\n getCodexEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport { runSelfUpdate } from \"./lib/self-update\"\n\nfunction printAndCopyCommand(command: string, label: string): void {\n consola.box(`${label}\\n\\n${command}`)\n try {\n clipboard.writeSync(command)\n consola.success(`Copied ${label} command to clipboard!`)\n } catch {\n consola.warn(\"Failed to copy to clipboard. Copy the command above manually.\")\n }\n}\n\nfunction generateClaudeCodeCommand(serverUrl: string, model?: string) {\n const envVars = getClaudeCodeEnvVars(serverUrl, model)\n const command = generateEnvScript(envVars, \"claude --dangerously-skip-permissions\")\n printAndCopyCommand(command, \"Claude Code\")\n}\n\nfunction generateCodexCommand(serverUrl: string, model?: string) {\n const codexModel = model ?? DEFAULT_CODEX_MODEL\n const envVars = getCodexEnvVars(serverUrl)\n const command = generateEnvScript(envVars, `codex -m ${codexModel}`)\n printAndCopyCommand(command, \"Codex CLI\")\n}\n\nexport const start = defineCommand({\n meta: {\n name: \"start\",\n description: \"Start the github-router server\",\n },\n args: {\n ...sharedServerArgs,\n cc: {\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Claude Code with Copilot API config\",\n },\n cx: {\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Codex CLI with Copilot API config\",\n },\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Override the default model (used with --cc or --cx)\",\n },\n },\n async run({ args }) {\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n\n const { serverUrl } = await setupAndServe({\n ...parsed,\n port: parsed.port ?? DEFAULT_PORT,\n silent: false,\n })\n\n // Best-effort self-update (detached, applies next launch). Runs\n // after the server is listening so the bounded probe can't delay it.\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Best-effort ColBERT semantic-search provision + background index of\n // the launch cwd (if a git repo). ON by default; never blocks launch,\n // never throws. Opt out with GH_ROUTER_DISABLE_SEMANTIC_SEARCH=1.\n void provisionAndIndexColbert()\n\n // Surface a terminally-failed semantic index to the HUMAN (see the note\n // in claude.ts). Fire-and-forget; lexical search still works.\n void colbertDegradedWarning()\n .then((warning) => {\n if (warning) process.stderr.write(`${warning}\\n`)\n })\n .catch(() => {})\n\n // Ready one tree-sitter worker in the background so the first code search\n // avoids WASM/grammar initialization. One worker keeps launch CPU bounded;\n // demand grows the pool lazily. Opt out with GH_ROUTER_DISABLE_TS_POOL_WARMUP=1.\n void warmTreeSitterPool()\n\n // Best-effort: keep the machine awake while the proxy serves (win32\n // default-on; opt out with GH_ROUTER_DISABLE_KEEP_AWAKE=1). Holds a\n // SetThreadExecutionState assertion via a persistent PowerShell\n // helper. Self-registers its own SIGINT/SIGTERM/exit reaper — load-\n // bearing here, because `start` has no launchChild/onShutdown.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the\n // stable app-dir and stamp the running version, so a one-time \"Load\n // unpacked\" survives npx/bunx upgrades. Gated on --browse; never\n // blocks launch, never throws.\n if (browserToolsEnabled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n if (args.cc) generateClaudeCodeCommand(serverUrl, args.model)\n if (args.cx) generateCodexCommand(serverUrl, args.model)\n\n consola.box(\n `🌐 Usage Viewer: https://animeshkundu.github.io/github-router/dashboard.html?endpoint=${serverUrl}/usage`,\n )\n },\n})\n"],"mappings":"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"}
|
|
@@ -83,123 +83,6 @@ const githubAgentGraphQLHeaders = (state, features) => ({
|
|
|
83
83
|
...features ? { "GraphQL-Features": features } : {}
|
|
84
84
|
});
|
|
85
85
|
//#endregion
|
|
86
|
-
//#region src/lib/error.ts
|
|
87
|
-
var HTTPError = class extends Error {
|
|
88
|
-
response;
|
|
89
|
-
constructor(message, response) {
|
|
90
|
-
super(message);
|
|
91
|
-
this.response = response;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
async function forwardError(c, error) {
|
|
95
|
-
consola.error(`Error occurred at ${c.req.path}:`, error);
|
|
96
|
-
if (error instanceof HTTPError) {
|
|
97
|
-
const errorText = await error.response.text().catch(() => "");
|
|
98
|
-
let errorJson;
|
|
99
|
-
try {
|
|
100
|
-
errorJson = JSON.parse(errorText);
|
|
101
|
-
} catch {
|
|
102
|
-
errorJson = void 0;
|
|
103
|
-
}
|
|
104
|
-
if (isContextOverflow(error.response.status, errorJson, errorText)) {
|
|
105
|
-
const upstream = resolveErrorMessage(errorJson, errorText);
|
|
106
|
-
consola.error("HTTP error (mapped to overflow):", errorJson ?? errorText);
|
|
107
|
-
return c.json({
|
|
108
|
-
type: "error",
|
|
109
|
-
error: {
|
|
110
|
-
type: "invalid_request_error",
|
|
111
|
-
message: `prompt is too long: ${upstream}`
|
|
112
|
-
}
|
|
113
|
-
}, 400);
|
|
114
|
-
}
|
|
115
|
-
const responseStatus = error.response.status === 401 ? 503 : error.response.status;
|
|
116
|
-
if (isAnthropicError(errorJson)) {
|
|
117
|
-
consola.error("HTTP error:", errorJson);
|
|
118
|
-
return c.json(errorJson, responseStatus);
|
|
119
|
-
}
|
|
120
|
-
const message = resolveErrorMessage(errorJson, errorText);
|
|
121
|
-
consola.error("HTTP error:", errorJson ?? errorText);
|
|
122
|
-
return c.json({
|
|
123
|
-
type: "error",
|
|
124
|
-
error: {
|
|
125
|
-
type: resolveErrorType(responseStatus),
|
|
126
|
-
message
|
|
127
|
-
}
|
|
128
|
-
}, responseStatus);
|
|
129
|
-
}
|
|
130
|
-
return c.json({
|
|
131
|
-
type: "error",
|
|
132
|
-
error: {
|
|
133
|
-
type: "api_error",
|
|
134
|
-
message: error instanceof Error ? error.message : String(error)
|
|
135
|
-
}
|
|
136
|
-
}, 500);
|
|
137
|
-
}
|
|
138
|
-
function resolveErrorMessage(errorJson, fallback) {
|
|
139
|
-
if (typeof errorJson !== "object" || errorJson === null) return fallback;
|
|
140
|
-
const errorRecord = errorJson;
|
|
141
|
-
if (errorRecord.message !== void 0) return String(errorRecord.message);
|
|
142
|
-
if (typeof errorRecord.error === "object" && errorRecord.error !== null) {
|
|
143
|
-
const nestedRecord = errorRecord.error;
|
|
144
|
-
if (nestedRecord.message !== void 0) return String(nestedRecord.message);
|
|
145
|
-
}
|
|
146
|
-
return fallback;
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Check if a parsed JSON body is already in Anthropic error format:
|
|
150
|
-
* { type: "error", error: { type: "...", message: "..." } }
|
|
151
|
-
*/
|
|
152
|
-
function isAnthropicError(json) {
|
|
153
|
-
if (typeof json !== "object" || json === null) return false;
|
|
154
|
-
const record = json;
|
|
155
|
-
if (record.type !== "error") return false;
|
|
156
|
-
if (typeof record.error !== "object" || record.error === null) return false;
|
|
157
|
-
const inner = record.error;
|
|
158
|
-
return typeof inner.type === "string" && typeof inner.message === "string";
|
|
159
|
-
}
|
|
160
|
-
const CONTEXT_OVERFLOW_SUBSTRINGS = [
|
|
161
|
-
"prompt is too long",
|
|
162
|
-
"context_length_exceeded",
|
|
163
|
-
"context length exceeded",
|
|
164
|
-
"input is too long",
|
|
165
|
-
"maximum context length",
|
|
166
|
-
"too many tokens"
|
|
167
|
-
];
|
|
168
|
-
/**
|
|
169
|
-
* Detect upstream context-overflow errors so we can remap them to a 400
|
|
170
|
-
* "prompt is too long" shape that triggers Claude Code self-compaction.
|
|
171
|
-
*
|
|
172
|
-
* Always remaps 413 (treated as a hard payload-size signal regardless of
|
|
173
|
-
* body wording). Remaps 400 only when the error text contains one of the
|
|
174
|
-
* known overflow substrings — a regular 400 (e.g. "model not found") must
|
|
175
|
-
* NOT remap.
|
|
176
|
-
*/
|
|
177
|
-
function isContextOverflow(status, errorJson, errorText) {
|
|
178
|
-
if (status === 413) return true;
|
|
179
|
-
if (status !== 400) return false;
|
|
180
|
-
const haystack = (errorText + " " + (typeof errorJson === "object" && errorJson !== null ? JSON.stringify(errorJson) : "")).toLowerCase();
|
|
181
|
-
return CONTEXT_OVERFLOW_SUBSTRINGS.some((s) => haystack.includes(s));
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Map HTTP status to Anthropic error type.
|
|
185
|
-
*
|
|
186
|
-
* Note: a 401 from upstream is remapped to 503 in `forwardError` BEFORE
|
|
187
|
-
* this function is called (no-401 invariant — see comment there). The
|
|
188
|
-
* 401 → "authentication_error" mapping below is preserved for
|
|
189
|
-
* defensive coverage in case any code path calls `resolveErrorType`
|
|
190
|
-
* directly with an unsanitized status.
|
|
191
|
-
*/
|
|
192
|
-
function resolveErrorType(status) {
|
|
193
|
-
if (status === 400) return "invalid_request_error";
|
|
194
|
-
if (status === 401) return "authentication_error";
|
|
195
|
-
if (status === 403) return "permission_error";
|
|
196
|
-
if (status === 404) return "not_found_error";
|
|
197
|
-
if (status === 429) return "rate_limit_error";
|
|
198
|
-
if (status === 503) return "overloaded_error";
|
|
199
|
-
if (status === 529) return "overloaded_error";
|
|
200
|
-
return "api_error";
|
|
201
|
-
}
|
|
202
|
-
//#endregion
|
|
203
86
|
//#region src/lib/upstream-retry.ts
|
|
204
87
|
/**
|
|
205
88
|
* Shared transient-failure retry for upstream calls (`src/lib/upstream-retry.ts`).
|
|
@@ -234,6 +117,154 @@ const DEFAULT_RETRY_STATUSES = [
|
|
|
234
117
|
503,
|
|
235
118
|
504
|
|
236
119
|
];
|
|
120
|
+
const TRANSPORT_MESSAGE_MAX = 512;
|
|
121
|
+
const TRANSIENT_CODES = /* @__PURE__ */ new Set([
|
|
122
|
+
"ECONNRESET",
|
|
123
|
+
"ETIMEDOUT",
|
|
124
|
+
"EPIPE",
|
|
125
|
+
"EAI_AGAIN",
|
|
126
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
127
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
128
|
+
"UND_ERR_SOCKET"
|
|
129
|
+
]);
|
|
130
|
+
const DETERMINISTIC_CODES = /* @__PURE__ */ new Set([
|
|
131
|
+
"ENOTFOUND",
|
|
132
|
+
"ECONNREFUSED",
|
|
133
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
134
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
135
|
+
"UNABLE_TO_GET_ISSUER_CERT",
|
|
136
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
137
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
138
|
+
"CERT_HAS_EXPIRED",
|
|
139
|
+
"CERT_NOT_YET_VALID",
|
|
140
|
+
"ERR_TLS_CERT_ALTNAME_INVALID",
|
|
141
|
+
"ERR_TLS_CERT_SIGNATURE_ALGORITHM_UNSUPPORTED",
|
|
142
|
+
"ERR_SSL_WRONG_VERSION_NUMBER"
|
|
143
|
+
]);
|
|
144
|
+
const CREDENTIAL_RE = /\b(eyJ[A-Za-z0-9_-]{20,}(?:\.[A-Za-z0-9_-]+){0,2}|gh[opsu]_[A-Za-z0-9_]{20,}|Bearer\s+\S{20,})\b/gi;
|
|
145
|
+
const SECRET_ASSIGNMENT_RE = /\b(authorization|api[_-]?key|access[_-]?token|token|password)\s*[:=]\s*([^\s,;]+)/gi;
|
|
146
|
+
const REQUEST_BODY_RE = /\b(request\s+body|body)\s*[:=]\s*.*$/gi;
|
|
147
|
+
var TransportExhaustionError = class extends Error {
|
|
148
|
+
endpoint;
|
|
149
|
+
label;
|
|
150
|
+
attempts;
|
|
151
|
+
classification;
|
|
152
|
+
lastError;
|
|
153
|
+
constructor(metadata, cause) {
|
|
154
|
+
const target = metadata.endpoint ?? metadata.label ?? "upstream";
|
|
155
|
+
const lastError = {
|
|
156
|
+
name: sanitizeTransportText(metadata.lastError.name),
|
|
157
|
+
message: sanitizeTransportText(metadata.lastError.message),
|
|
158
|
+
code: metadata.lastError.code ? sanitizeTransportText(metadata.lastError.code) : void 0,
|
|
159
|
+
causeCode: metadata.lastError.causeCode ? sanitizeTransportText(metadata.lastError.causeCode) : void 0
|
|
160
|
+
};
|
|
161
|
+
const code = lastError.causeCode ?? lastError.code;
|
|
162
|
+
const suffix = code ? ` (${code})` : "";
|
|
163
|
+
super(`Upstream transport ${metadata.classification === "transient" ? "failed" : "is unreachable"} at ${sanitizeTransportText(target)} after ${metadata.attempts} attempt${metadata.attempts === 1 ? "" : "s"}: ${lastError.name}: ${lastError.message}${suffix}`, { cause });
|
|
164
|
+
this.name = "TransportExhaustionError";
|
|
165
|
+
this.endpoint = metadata.endpoint ? sanitizeTransportText(metadata.endpoint) : void 0;
|
|
166
|
+
this.label = metadata.label ? sanitizeTransportText(metadata.label) : void 0;
|
|
167
|
+
this.attempts = metadata.attempts;
|
|
168
|
+
this.classification = metadata.classification;
|
|
169
|
+
this.lastError = lastError;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
function readStringProperty(value, key) {
|
|
173
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) return;
|
|
174
|
+
try {
|
|
175
|
+
const property = value[key];
|
|
176
|
+
return typeof property === "string" ? property : void 0;
|
|
177
|
+
} catch {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function readCause(value) {
|
|
182
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) return;
|
|
183
|
+
try {
|
|
184
|
+
return value.cause;
|
|
185
|
+
} catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function safeString(value) {
|
|
190
|
+
try {
|
|
191
|
+
return String(value);
|
|
192
|
+
} catch {
|
|
193
|
+
return "[unserializable error]";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function sanitizeTransportText(value) {
|
|
197
|
+
const sanitized = value.replace(/\r\n|\r|\n/g, " ").replace(CREDENTIAL_RE, "[REDACTED]").replace(SECRET_ASSIGNMENT_RE, "$1=[REDACTED]").replace(REQUEST_BODY_RE, "$1=[REDACTED]");
|
|
198
|
+
return sanitized.length > TRANSPORT_MESSAGE_MAX ? `${sanitized.slice(0, TRANSPORT_MESSAGE_MAX)}…` : sanitized;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Classify errors thrown before the first response byte. Deterministic
|
|
202
|
+
* connectivity/configuration signals win over generic runtime wording such as
|
|
203
|
+
* TypeError("fetch failed"), because the nested cause is more specific.
|
|
204
|
+
*/
|
|
205
|
+
function classifyTransportError(error, opts = {}) {
|
|
206
|
+
const name = readStringProperty(error, "name") ?? "Error";
|
|
207
|
+
const rawMessage = readStringProperty(error, "message") ?? (typeof error === "string" ? error : safeString(error));
|
|
208
|
+
const message = sanitizeTransportText(rawMessage);
|
|
209
|
+
const code = readStringProperty(error, "code");
|
|
210
|
+
const causes = [];
|
|
211
|
+
const seen = /* @__PURE__ */ new Set();
|
|
212
|
+
let current = readCause(error);
|
|
213
|
+
for (let depth = 0; current !== void 0 && depth < 4; depth++) {
|
|
214
|
+
if ((typeof current === "object" || typeof current === "function") && current !== null) {
|
|
215
|
+
if (seen.has(current)) break;
|
|
216
|
+
seen.add(current);
|
|
217
|
+
}
|
|
218
|
+
causes.push(current);
|
|
219
|
+
current = readCause(current);
|
|
220
|
+
}
|
|
221
|
+
const causeCode = causes.map((cause) => readStringProperty(cause, "code")).find((value) => value !== void 0);
|
|
222
|
+
const codes = [code, ...causes.map((cause) => readStringProperty(cause, "code"))].filter((value) => value !== void 0).map((value) => value.toUpperCase());
|
|
223
|
+
const messages = [rawMessage, ...causes.map((cause) => readStringProperty(cause, "message") ?? "")].join(" ").toLowerCase();
|
|
224
|
+
if (opts.callerCancelled) return {
|
|
225
|
+
classification: "cancelled",
|
|
226
|
+
name,
|
|
227
|
+
message,
|
|
228
|
+
code,
|
|
229
|
+
causeCode
|
|
230
|
+
};
|
|
231
|
+
if (codes.some((value) => DETERMINISTIC_CODES.has(value)) || messages.includes("self signed certificate") || messages.includes("certificate has expired") || messages.includes("unable to verify the first certificate") || messages.includes("unable to get local issuer certificate") || messages.includes("hostname/ip does not match certificate") || messages.includes("tls handshake") || messages.includes("ssl routines") || messages.includes("econnrefused") || messages.includes("enotfound")) return {
|
|
232
|
+
classification: "deterministic",
|
|
233
|
+
name,
|
|
234
|
+
message,
|
|
235
|
+
code,
|
|
236
|
+
causeCode
|
|
237
|
+
};
|
|
238
|
+
if (name === "AbortError" || name === "TimeoutError" || name === "InactivityTimeout" || codes.some((value) => TRANSIENT_CODES.has(value)) || messages.includes("terminated") || messages.includes("fetch failed") || messages.includes("network error") || messages.includes("socket hang up") || messages.includes("socket closed") || messages.includes("econnreset") || messages.includes("etimedout") || messages.includes("epipe") || messages.includes("eai_again")) return {
|
|
239
|
+
classification: "transient",
|
|
240
|
+
name,
|
|
241
|
+
message,
|
|
242
|
+
code,
|
|
243
|
+
causeCode
|
|
244
|
+
};
|
|
245
|
+
return {
|
|
246
|
+
classification: "non_transport",
|
|
247
|
+
name,
|
|
248
|
+
message,
|
|
249
|
+
code,
|
|
250
|
+
causeCode
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function transportExhaustion(error, attempts, opts, details) {
|
|
254
|
+
if (details.classification !== "transient" && details.classification !== "deterministic") throw new TypeError("transportExhaustion requires a transport error");
|
|
255
|
+
return new TransportExhaustionError({
|
|
256
|
+
endpoint: opts.endpoint ?? opts.label,
|
|
257
|
+
label: opts.label,
|
|
258
|
+
attempts,
|
|
259
|
+
classification: details.classification,
|
|
260
|
+
lastError: {
|
|
261
|
+
name: details.name,
|
|
262
|
+
message: details.message,
|
|
263
|
+
code: details.code,
|
|
264
|
+
causeCode: details.causeCode
|
|
265
|
+
}
|
|
266
|
+
}, error);
|
|
267
|
+
}
|
|
237
268
|
function parseRetryAfter(headerValue) {
|
|
238
269
|
if (!headerValue) return void 0;
|
|
239
270
|
const secs = Number(headerValue);
|
|
@@ -241,27 +272,6 @@ function parseRetryAfter(headerValue) {
|
|
|
241
272
|
const dateMs = Date.parse(headerValue);
|
|
242
273
|
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
243
274
|
}
|
|
244
|
-
/**
|
|
245
|
-
* A thrown fetch error that is worth retrying — network resets, broken
|
|
246
|
-
* pipes, DNS hiccups, and timeout aborts. NOTE: the caller must already
|
|
247
|
-
* have ruled out a user cancel (caller signal aborted) before calling
|
|
248
|
-
* this, since a timeout abort and a user-cancel abort look identical.
|
|
249
|
-
*/
|
|
250
|
-
function isTransientNetworkError(err) {
|
|
251
|
-
const e = err;
|
|
252
|
-
if (!e) return false;
|
|
253
|
-
if (e.name === "AbortError" || e.name === "TimeoutError") return true;
|
|
254
|
-
const msg = (e.message ?? "").toLowerCase();
|
|
255
|
-
if (msg.includes("terminated") || msg.includes("fetch failed") || msg.includes("network") || msg.includes("socket") || msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("econnrefused") || msg.includes("epipe") || msg.includes("enotfound")) return true;
|
|
256
|
-
const code = e.code ?? e.cause?.code;
|
|
257
|
-
return code !== void 0 && [
|
|
258
|
-
"ECONNRESET",
|
|
259
|
-
"ETIMEDOUT",
|
|
260
|
-
"ECONNREFUSED",
|
|
261
|
-
"EPIPE",
|
|
262
|
-
"ENOTFOUND"
|
|
263
|
-
].includes(code);
|
|
264
|
-
}
|
|
265
275
|
/** Sleep that resolves early (does not reject) when `signal` aborts — the
|
|
266
276
|
* retry loop re-checks `signal.aborted` at the top and throws there. */
|
|
267
277
|
function abortableSleep(ms, signal) {
|
|
@@ -310,11 +320,14 @@ async function fetchWithTransientRetry(doFetch, opts = {}) {
|
|
|
310
320
|
if (res && !retryStatuses.includes(res.status)) return res;
|
|
311
321
|
if (caught !== void 0) {
|
|
312
322
|
if (signal?.aborted) throw caught;
|
|
313
|
-
|
|
323
|
+
const details = classifyTransportError(caught);
|
|
324
|
+
if (details.classification === "deterministic") throw transportExhaustion(caught, attempt, opts, details);
|
|
325
|
+
if (details.classification !== "transient") throw caught;
|
|
314
326
|
}
|
|
315
327
|
if (attempt >= attempts) {
|
|
316
328
|
if (res) return res;
|
|
317
|
-
|
|
329
|
+
const details = classifyTransportError(caught);
|
|
330
|
+
throw transportExhaustion(caught, attempt, opts, details);
|
|
318
331
|
}
|
|
319
332
|
const retryAfterMs = res ? parseRetryAfter(res.headers.get("retry-after")) : void 0;
|
|
320
333
|
if (res?.body) try {
|
|
@@ -364,7 +377,14 @@ async function withTransientRetry(fn, opts = {}) {
|
|
|
364
377
|
} catch (err) {
|
|
365
378
|
if (signal?.aborted) throw err;
|
|
366
379
|
const status = errorStatus(err);
|
|
367
|
-
|
|
380
|
+
const details = classifyTransportError(err);
|
|
381
|
+
if (status !== void 0) {
|
|
382
|
+
if (!retryStatuses.includes(status) || attempt >= attempts) throw err;
|
|
383
|
+
} else {
|
|
384
|
+
if (details.classification === "deterministic") throw transportExhaustion(err, attempt, opts, details);
|
|
385
|
+
if (details.classification !== "transient") throw err;
|
|
386
|
+
if (attempt >= attempts) throw transportExhaustion(err, attempt, opts, details);
|
|
387
|
+
}
|
|
368
388
|
const retryAfterMs = parseRetryAfter(err?.response?.headers?.get?.("retry-after") ?? null);
|
|
369
389
|
const expCap = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
|
|
370
390
|
const delay = Math.min(maxDelayMs, retryAfterMs ?? Math.round(Math.random() * expCap));
|
|
@@ -374,6 +394,146 @@ async function withTransientRetry(fn, opts = {}) {
|
|
|
374
394
|
}
|
|
375
395
|
}
|
|
376
396
|
//#endregion
|
|
397
|
+
//#region src/lib/error.ts
|
|
398
|
+
var HTTPError = class extends Error {
|
|
399
|
+
response;
|
|
400
|
+
constructor(message, response) {
|
|
401
|
+
super(message);
|
|
402
|
+
this.response = response;
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
async function forwardError(c, error) {
|
|
406
|
+
consola.error(`Error occurred at ${c.req.path}:`, error);
|
|
407
|
+
if (error instanceof TransportExhaustionError) {
|
|
408
|
+
const target = sanitizeTransportText(error.endpoint ?? error.label ?? c.req.path);
|
|
409
|
+
const diagnostic = formatTransportDiagnostic(error);
|
|
410
|
+
if (error.classification === "transient") return c.json({
|
|
411
|
+
type: "error",
|
|
412
|
+
error: {
|
|
413
|
+
type: "overloaded_error",
|
|
414
|
+
message: `Upstream transport was interrupted at ${target} after ${error.attempts} attempts; retry later. ${diagnostic}`
|
|
415
|
+
}
|
|
416
|
+
}, 503);
|
|
417
|
+
return c.json({
|
|
418
|
+
type: "error",
|
|
419
|
+
error: {
|
|
420
|
+
type: "api_error",
|
|
421
|
+
message: `Could not connect to upstream at ${target}. Check DNS, proxy, firewall, and TLS configuration. ${diagnostic}`
|
|
422
|
+
}
|
|
423
|
+
}, 502);
|
|
424
|
+
}
|
|
425
|
+
if (error instanceof HTTPError) {
|
|
426
|
+
const errorText = await error.response.text().catch(() => "");
|
|
427
|
+
let errorJson;
|
|
428
|
+
try {
|
|
429
|
+
errorJson = JSON.parse(errorText);
|
|
430
|
+
} catch {
|
|
431
|
+
errorJson = void 0;
|
|
432
|
+
}
|
|
433
|
+
if (isContextOverflow(error.response.status, errorJson, errorText)) {
|
|
434
|
+
const upstream = resolveErrorMessage(errorJson, errorText);
|
|
435
|
+
consola.error("HTTP error (mapped to overflow):", errorJson ?? errorText);
|
|
436
|
+
return c.json({
|
|
437
|
+
type: "error",
|
|
438
|
+
error: {
|
|
439
|
+
type: "invalid_request_error",
|
|
440
|
+
message: `prompt is too long: ${upstream}`
|
|
441
|
+
}
|
|
442
|
+
}, 400);
|
|
443
|
+
}
|
|
444
|
+
const responseStatus = error.response.status === 401 ? 503 : error.response.status;
|
|
445
|
+
if (isAnthropicError(errorJson)) {
|
|
446
|
+
consola.error("HTTP error:", errorJson);
|
|
447
|
+
return c.json(errorJson, responseStatus);
|
|
448
|
+
}
|
|
449
|
+
const message = resolveErrorMessage(errorJson, errorText);
|
|
450
|
+
consola.error("HTTP error:", errorJson ?? errorText);
|
|
451
|
+
return c.json({
|
|
452
|
+
type: "error",
|
|
453
|
+
error: {
|
|
454
|
+
type: resolveErrorType(responseStatus),
|
|
455
|
+
message
|
|
456
|
+
}
|
|
457
|
+
}, responseStatus);
|
|
458
|
+
}
|
|
459
|
+
return c.json({
|
|
460
|
+
type: "error",
|
|
461
|
+
error: {
|
|
462
|
+
type: "api_error",
|
|
463
|
+
message: error instanceof Error ? error.message : String(error)
|
|
464
|
+
}
|
|
465
|
+
}, 500);
|
|
466
|
+
}
|
|
467
|
+
function formatTransportDiagnostic(error) {
|
|
468
|
+
const code = error.lastError.causeCode ?? error.lastError.code;
|
|
469
|
+
const codeText = code ? ` (${sanitizeTransportText(code)})` : "";
|
|
470
|
+
return `${sanitizeTransportText(error.lastError.name)}: ${sanitizeTransportText(error.lastError.message)}${codeText}`;
|
|
471
|
+
}
|
|
472
|
+
function resolveErrorMessage(errorJson, fallback) {
|
|
473
|
+
if (typeof errorJson !== "object" || errorJson === null) return fallback;
|
|
474
|
+
const errorRecord = errorJson;
|
|
475
|
+
if (errorRecord.message !== void 0) return String(errorRecord.message);
|
|
476
|
+
if (typeof errorRecord.error === "object" && errorRecord.error !== null) {
|
|
477
|
+
const nestedRecord = errorRecord.error;
|
|
478
|
+
if (nestedRecord.message !== void 0) return String(nestedRecord.message);
|
|
479
|
+
}
|
|
480
|
+
return fallback;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Check if a parsed JSON body is already in Anthropic error format:
|
|
484
|
+
* { type: "error", error: { type: "...", message: "..." } }
|
|
485
|
+
*/
|
|
486
|
+
function isAnthropicError(json) {
|
|
487
|
+
if (typeof json !== "object" || json === null) return false;
|
|
488
|
+
const record = json;
|
|
489
|
+
if (record.type !== "error") return false;
|
|
490
|
+
if (typeof record.error !== "object" || record.error === null) return false;
|
|
491
|
+
const inner = record.error;
|
|
492
|
+
return typeof inner.type === "string" && typeof inner.message === "string";
|
|
493
|
+
}
|
|
494
|
+
const CONTEXT_OVERFLOW_SUBSTRINGS = [
|
|
495
|
+
"prompt is too long",
|
|
496
|
+
"context_length_exceeded",
|
|
497
|
+
"context length exceeded",
|
|
498
|
+
"input is too long",
|
|
499
|
+
"maximum context length",
|
|
500
|
+
"too many tokens"
|
|
501
|
+
];
|
|
502
|
+
/**
|
|
503
|
+
* Detect upstream context-overflow errors so we can remap them to a 400
|
|
504
|
+
* "prompt is too long" shape that triggers Claude Code self-compaction.
|
|
505
|
+
*
|
|
506
|
+
* Always remaps 413 (treated as a hard payload-size signal regardless of
|
|
507
|
+
* body wording). Remaps 400 only when the error text contains one of the
|
|
508
|
+
* known overflow substrings — a regular 400 (e.g. "model not found") must
|
|
509
|
+
* NOT remap.
|
|
510
|
+
*/
|
|
511
|
+
function isContextOverflow(status, errorJson, errorText) {
|
|
512
|
+
if (status === 413) return true;
|
|
513
|
+
if (status !== 400) return false;
|
|
514
|
+
const haystack = (errorText + " " + (typeof errorJson === "object" && errorJson !== null ? JSON.stringify(errorJson) : "")).toLowerCase();
|
|
515
|
+
return CONTEXT_OVERFLOW_SUBSTRINGS.some((s) => haystack.includes(s));
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Map HTTP status to Anthropic error type.
|
|
519
|
+
*
|
|
520
|
+
* Note: a 401 from upstream is remapped to 503 in `forwardError` BEFORE
|
|
521
|
+
* this function is called (no-401 invariant — see comment there). The
|
|
522
|
+
* 401 → "authentication_error" mapping below is preserved for
|
|
523
|
+
* defensive coverage in case any code path calls `resolveErrorType`
|
|
524
|
+
* directly with an unsanitized status.
|
|
525
|
+
*/
|
|
526
|
+
function resolveErrorType(status) {
|
|
527
|
+
if (status === 400) return "invalid_request_error";
|
|
528
|
+
if (status === 401) return "authentication_error";
|
|
529
|
+
if (status === 403) return "permission_error";
|
|
530
|
+
if (status === 404) return "not_found_error";
|
|
531
|
+
if (status === 429) return "rate_limit_error";
|
|
532
|
+
if (status === 503) return "overloaded_error";
|
|
533
|
+
if (status === 529) return "overloaded_error";
|
|
534
|
+
return "api_error";
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
377
537
|
//#region src/services/github/get-copilot-token.ts
|
|
378
538
|
/**
|
|
379
539
|
* Allowlist of hosts the router will trust as the Copilot API base URL.
|
|
@@ -1180,6 +1340,6 @@ async function warnIfAgentScopesInsufficient() {
|
|
|
1180
1340
|
}
|
|
1181
1341
|
}
|
|
1182
1342
|
//#endregion
|
|
1183
|
-
export {
|
|
1343
|
+
export { state as A, GITHUB_GRAPHQL_URL as C, githubAgentGraphQLHeaders as D, copilotVersion as E, githubAgentHeaders as O, GITHUB_API_BASE_URL as S, copilotHeaders as T, forwardError as _, cacheCopilotVersion as a, sanitizeTransportText as b, filterBetaHeader as c, resolveModel as d, sleep as f, HTTPError as g, isAllowedCopilotHost as h, tryRefreshAndRetry as i, githubHeaders as k, isNullish as l, getGitHubUser as m, setupGitHubAgentToken as n, cacheModels as o, getModels as p, setupGitHubToken as r, cacheVSCodeVersion as s, setupCopilotToken as t, resolveCodexModel as u, classifyTransportError as v, copilotBaseUrl as w, withTransientRetry as x, fetchWithTransientRetry as y };
|
|
1184
1344
|
|
|
1185
|
-
//# sourceMappingURL=token-
|
|
1345
|
+
//# sourceMappingURL=token-iaSGp76k.js.map
|