@wengine-ai/llms 2.3.19 → 2.3.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/README.md +3 -4
- package/dist/cjs/server.cjs +1 -1
- package/dist/cjs/server.cjs.map +1 -1
- package/dist/esm/server.mjs +1 -1
- package/dist/esm/server.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,8 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
|
|
|
93
93
|
|
|
94
94
|
| 版本 | 发布内容 |
|
|
95
95
|
| --- | --- |
|
|
96
|
+
| **v2.3.21** | <ul><li>**新增 pi (earendil-works) 客户端接管**: 新增 pi 作为第三个接管目标(与 Claude Code、Codex 并列),pi 使用 Anthropic `/v1/messages` 协议直连无需 transformer;接管会在 `~/.pi/agent` 写入 `models.json`(注册指向 ccr 代理的 `ccr` provider,暴露 `ccr-opus`/`ccr-sonnet`/`ccr-haiku` 族别名)与 `settings.json`(默认 provider/model 指向 ccr),并备份原文件,仅做配置注入不含账号管理;用量统计也会把 pi 识别为独立客户端(pi 与 Claude Code 共用 `ccr-*` 别名,靠 pi system prompt 特征 + Anthropic SDK 请求头区分)。</li><li>**项目级接管支持多客户端多选**: 「项目级配置」页「CCR 接管」从只接管 Claude Code 改为客户端多选下拉(Claude Code + pi + qwen-code + opencode),不选默认接管全部;pi 项目级接管在项目目录写 `.pi/settings.json` 指向全局 ccr provider 并在 `trust.json` 信任该目录,接管状态从各客户端项目级配置文件实时推导、自动兼容既有项目(Codex 配置全局-only 不纳入)。</li><li>**新增 qwen-code (Alibaba) 客户端接管**: 新增 qwen-code(`@qwen-code/qwen-code`)作为第四个接管目标,走 Anthropic `/v1/messages` 直连;接管在 `~/.qwen/settings.json`(用户级)/ `<项目>/.qwen/settings.json`(项目级)写入指向 ccr 的 `modelProviders.anthropic`(暴露 ccr-opus/sonnet/haiku)、备份原文件,项目级还在 `trustedFolders.json` 信任该目录。用量统计区分 qwen-code:qwen 经代理时会伪装成 Claude Code 的 `claude-cli` UA,故改用 qwen system prompt 特征(`You are Qwen Code`)正向识别、Claude Code 用 `cc_version`/`metadata.user_id` 强信号识别。</li><li>**新增 opencode (opencode.ai) 客户端接管**: 新增 opencode 作为第五个接管目标,走 Anthropic `/v1/messages` 直连;接管在 `~/.config/opencode/opencode.json`(全局)/ `<项目>/opencode.json`(项目级)注入一个 `@ai-sdk/anthropic` 的 `provider.ccr`(baseURL=`…/v1`、apiKey 内联、暴露 ccr-opus/sonnet/haiku)并设 `model: ccr/ccr-opus`,备份原文件,无需 trust。用量统计区分 opencode:其请求带 `opencode/…` UA 且不伪装,直接按 UA 识别。</li></ul> |
|
|
97
|
+
| **v2.3.20** | <ul><li>**清理改名/删除模型后残留的僵尸熔断记录**: 从 provider 的 `models` 重命名/删除模型后(如 `ollama,glm-5.2` → `ollama,glm-5.2:cloud`),旧名字在 `provider-health.json` 中的熔断记录会成为僵尸,UI 上该供应商一直显示 `Failed`,且因状态持久化落盘 `ccr restart` 与点刷新都清不掉(probe 成功只 `recover` 当前配置的模型名)。现在新增 `health-reconcile`:服务启动、保存配置、probe 成功时分别按当前配置对账清理残留记录;「可路由」集合同时取自各 provider `models` 与 Router/fallback 引用,避免误删 `models` 为空但仅靠 Router 路由的模型(如 `阿里云 Coding Plan,glm-5`)健康状态。新增 7 个单元测试。</li></ul> |
|
|
96
98
|
| **v2.3.19** | <ul><li>**新增 OpenCode (opencode.ai) Transformer**: 为暴露 OpenAI 兼容 `/v1/chat/completions`、底层由 GLM/智谱驱动的 OpenCode 类 provider 新增专用 transformer,显式声明 `endPoint` 避免误触 bypass 把 Anthropic 格式工具发给 OpenAI 兼容接口;同时清理 GLM 不识别的 `cache_control`/`image_url`/`media_type`,转换 `reasoning_content` 为 thinking 格式,并把纯数字 `tool_call` ID 替换为 UUID。在 provider `transformer` 中配置 `"opencode"` 启用。</li><li>**设置页代理地址与 API 密钥输入框禁用浏览器自动填充**: 弹窗版与整页版设置的「代理地址」「API 密钥」此前会被 Chrome 用已保存表单数据/密码自动填充覆盖真实值;现在通过 `Input` 的 `disableAutofill` 提供随机 `name`、初始 `readOnly` 至首次聚焦解除、`autoComplete="off"` 及密码管理器忽略标记三重防护,并配合隐藏 honeypot 字段。</li></ul> |
|
|
97
99
|
| **v2.3.18** | <ul><li>**模型族长上下文阈值继承主路由配置**: `ccr-opus`/`ccr-sonnet`/`ccr-haiku` 的 family routing 未配置族级阈值时会继承 `Router.longContextThreshold`,避免主路由设为 100k 时约 70k token 请求仍误进 `<family>/longContext`。</li><li>**fallback 候选模型也执行 Double-Check 重试**: 每个 fallback 候选第一次 `fetch failed`/空 SSE/隐藏错误后会先对同一候选重试一次,第二次仍失败才切到下一个,降低智谱等接口短暂不稳定导致的过早切换;同时修正失败记录的 `originalModel` 显示。</li><li>**全局配置保存后同步项目级 CCR 接管字段**: 跟随全局 Router 且已开启 CCR Takeover 的项目,在保存全局配置或切回使用全局配置时自动刷新 `.claude/settings.local.json` 中的模型族别名、auto-compact、状态栏与代理字段。</li><li>**默认降低日志量并保留最近 7 天**: 服务器日志默认级别从 `debug` 调整为 `error`,只在显式配置 `LOG_LEVEL=info/debug/trace` 时输出详细日志,并自动清理超过 7 天的 `ccr-*.log`。</li></ul> |
|
|
98
100
|
| **v2.3.17** | <ul><li>**修复 fallback 触发时所有备用模型报 Invalid URL**: 主模型限流(如智谱套餐)触发 fallback 后,此前所有备用模型都报 `Invalid URL`;根因是 fallback 用了原始 `ConfigProvider[]`(无 `baseUrl`)而非已注册的 `LLMProvider[]`,改为 `providerService.getProviders()`。</li><li>**修复 fireworks 托管上游用量统计全 0**: fireworks 流式把真实 usage 放在 `finish_reason` 之后的 `choices:[]` 空 chunk 里,此前 `finish_reason` 块用 null usage 覆盖了已 merge 的真实值、且 `break` 丢弃了后续 usage chunk,导致 `input` 有值但 `output`/缓存命中为 0;现改为 finish 后继续读取、只设 stop_reason 不碰 usage、`break` 改 `hasFinished=true`,并修复下游三层用量捕获与 `??` 对 0 不 fallback 的问题。</li><li>**忽略已删除模型的残留路由,防止健康池污染**: 从 provider 配置删除模型后,路由与 fallback 中残留的 `provider,model` 仍被当作有效候选反复失败污染健康池;现在对无法匹配的候选直接返回 `null` 并在请求前校验,`ProviderHealthStore` 统一拦截空 provider/model 防畸形 key 污染,同时抽取 `findProviderModel` 共用函数。</li></ul> |
|
|
@@ -100,10 +102,7 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
|
|
|
100
102
|
| **v2.3.15** | <ul><li>**Fallback 前同模型重试一次**: 模型调用出现一次异常不再立即切换到备用模型,而是先对同一模型自动重试一次(Double-Check),避免因瞬时错误(网络抖动、偶发限流、空 SSE 响应等)导致不必要的模型切换;重试仍失败才走原有 fallback 流程。</li><li>**用量统计显示上游真实模型**: 部分上游网关会偷偷将请求路由/降级到另一个后端模型(如请求 glm-5 实际返回 MiniMax-M2.5)。用量统计的模型映射现在追加上游返回的真实模型,格式为 `originalModel → routedModel → upstreamModel`(如 `ccr-opus → glm-5 → minimax-m2.5`),上游未偷换时自动省略。</li></ul> |
|
|
101
103
|
| **v2.3.14** | <ul><li>**修复状态栏显示 `<synthetic>` 模型名**:Claude Code 在 auto-compact 自动压缩、中断恢复时写入的 `<synthetic>` 合成消息(非真实 LLM 响应)此前被状态栏当作模型名显示(从 Claude 账号会话切换到 ccr 接管、或发生自动压缩后尤其常见);现在过滤该合成标识,正确显示实际路由模型,同时不再把合成消息的 usage 计入 token 统计。</li></ul> |
|
|
102
104
|
| **v2.3.13** | <ul><li>**修复删除项目配置未清理项目 `settings.local.json`**:添加项目时自动写入的 ccr 托管字段(代理地址、模型族路由环境变量、auto-compact、statusline 等)此前在删除项目时未被清理;现在删除前会先关闭 takeover,移除这些字段。</li></ul> |
|
|
103
|
-
| **v2.3.12** | <ul><li>**修复定时唤醒未真正触发计费周期**:唤醒请求由 `max_tokens: 1` 的 dummy ping 改为真实推理 prompt 与 `max_tokens: 10`,确保 Coding Plan 类提供商产生实际 token 消耗并激活日额度周期。</li><li>**修复 Codex 等 `/messages` 端点唤醒 404**:以 `baseUrl` 是否包含 `/messages` 判断 Anthropic 协议,且 `baseUrl` 作为完整路径直接使用,不再拼接后缀。</li><li>**唤醒/探测请求增加来源头**:新增 `x-claude-code-router-source` 与 `x-claude-code-router-version` 请求头,便于上游识别 CCR 内部探测/唤醒流量。</li></ul>
|
|
104
|
-
| **v2.3.11** | <ul><li>**修复新会话首个请求绕过项目级路由(会话检测竞态)**:新会话的第一个请求(如标题生成元请求,比主请求早到约十几毫秒)到达时 session 转写文件可能尚未落盘,导致项目匹配失败、回退全局 `Router`,使该请求绕过项目级路由(如项目已关 `enableFamilyRouting` 仍走全局模型族路由)。现在缓存未命中时短延迟重试(最多 3×50ms)给文件落盘留时间,并保证每个 session 仅重试一次,避免非托管会话每请求都被加延迟。</li></ul> |
|
|
105
|
-
| **v2.3.10** | <ul><li>**修复标题生成等元请求误触发 `think` 场景路由**:`thinking: {type: "disabled"}` 是真值对象,此前会被误判为"已开启思考"并路由到 `think` 模型;现在仅当 `thinking.type === "enabled"` 时才进入 `think` 场景,避免关闭模型族路由的项目仍被路由到全局 think 模型。</li><li>**修复主模型熔断且无 fallback 时返回空模型**:`Router.default` 因健康检查熔断且无可用 fallback 时,此前直接返回空模型导致合成 "provider not found" 错误;现在会跳过健康检查兜底重试主模型,让 Claude Code 收到真实上游响应并自行重试。</li></ul> |
|
|
106
|
-
> 仅保留最近 10 个版本,更早版本的发布摘要见 [CHANGELOG-archive.md](./CHANGELOG-archive.md),完整详细变更记录见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
105
|
+
| **v2.3.12** | <ul><li>**修复定时唤醒未真正触发计费周期**:唤醒请求由 `max_tokens: 1` 的 dummy ping 改为真实推理 prompt 与 `max_tokens: 10`,确保 Coding Plan 类提供商产生实际 token 消耗并激活日额度周期。</li><li>**修复 Codex 等 `/messages` 端点唤醒 404**:以 `baseUrl` 是否包含 `/messages` 判断 Anthropic 协议,且 `baseUrl` 作为完整路径直接使用,不再拼接后缀。</li><li>**唤醒/探测请求增加来源头**:新增 `x-claude-code-router-source` 与 `x-claude-code-router-version` 请求头,便于上游识别 CCR 内部探测/唤醒流量。</li></ul> |> 仅保留最近 10 个版本,更早版本的发布摘要见 [CHANGELOG-archive.md](./CHANGELOG-archive.md),完整详细变更记录见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
107
106
|
|
|
108
107
|
### 2. 配置
|
|
109
108
|
|
package/dist/cjs/server.cjs
CHANGED
|
@@ -71,7 +71,7 @@ ${this.toMarkdown(o,n+1)}`:`${r}- ${o}`).join(`
|
|
|
71
71
|
${this.toMarkdown(s,n+1)}`:`${r}${o}: ${s}`).join(`
|
|
72
72
|
`):`${r}${e}`}async output(e,n={}){try{let r=this.formatData(e,n);switch(this.config.level||"log"){case"info":console.info(r);break;case"warn":console.warn(r);break;case"error":console.error(r);break;case"debug":console.debug(r);break;case"log":default:console.log(r);break}return!0}catch(r){return console.error("[ConsoleOutputHandler] Output failed:",r),!1}}}});var Ns,If=Le(()=>{"use strict";Ns=class{type="webhook";config;defaultTimeout=3e4;constructor(e){if(!e.url)throw new Error("Webhook URL is required");this.config={method:"POST",retry:{maxAttempts:3,backoffMs:1e3},silent:!1,...e}}buildHeaders(){let e={"Content-Type":"application/json",...this.config.headers||{}};if(this.config.auth)switch(this.config.auth.type){case"bearer":this.config.auth.token&&(e.Authorization=`Bearer ${this.config.auth.token}`);break;case"basic":if(this.config.auth.username&&this.config.auth.password){let n=Buffer.from(`${this.config.auth.username}:${this.config.auth.password}`).toString("base64");e.Authorization=`Basic ${n}`}break;case"custom":this.config.auth.custom&&(e[this.config.auth.custom.header]=this.config.auth.custom.value);break}return e}buildBody(e,n){let{format:r="json",timestamp:o=!0,prefix:s,metadata:a}=n||{},u={data:e};return o&&(u.timestamp=new Date().toISOString()),s&&(u.prefix=s),a&&Object.keys(a).length>0&&(u.metadata=a),u}async sendRequest(e,n,r,o,s){let a=new AbortController,u=setTimeout(()=>a.abort(),s);try{let l=await fetch(e,{method:n,headers:r,body:JSON.stringify(o),signal:a.signal});if(clearTimeout(u),!l.ok)throw new Error(`HTTP ${l.status}: ${l.statusText}`);return l}catch(l){throw clearTimeout(u),l}}delay(e){return new Promise(n=>setTimeout(n,e))}async sendWithRetry(e,n,r,o,s,a){let u=null;for(let l=1;l<=a.maxAttempts;l++)try{return await this.sendRequest(e,n,r,o,s)}catch(f){if(u=f,l===a.maxAttempts)break;let d=a.backoffMs*Math.pow(2,l-1);console.warn(`[WebhookOutputHandler] Request failed (attempt ${l}/${a.maxAttempts}), retrying in ${d}ms...`,f.message),await this.delay(d)}throw u}async output(e,n={}){let r=n.timeout||this.defaultTimeout;try{let o=this.buildHeaders(),s=this.buildBody(e,n),a=await this.sendWithRetry(this.config.url,this.config.method,o,s,r,this.config.retry);return!0}catch(o){let s=o instanceof Error?o.message:String(o);if(this.config.silent)return console.error(`[WebhookOutputHandler] Failed to send data: ${s}`),!1;throw new Error(`Webhook output failed: ${s}`)}}}});var Oo,Nf,M_,Bs,Bf=Le(()=>{"use strict";Oo=require("fs"),Nf=require("path"),M_=require("os"),Bs=class{type="temp-file";config;baseDir;constructor(e={}){this.config={subdirectory:"claude-code-router",extension:"json",includeTimestamp:!1,prefix:"session",...e};let n=(0,M_.tmpdir)();this.baseDir=(0,Nf.join)(n,this.config.subdirectory),this.ensureDir()}ensureDir(){try{(0,Oo.existsSync)(this.baseDir)||(0,Oo.mkdirSync)(this.baseDir,{recursive:!0})}catch{}}getFilePath(e){let n=this.config.prefix||"session",r=this.config.extension?`.${this.config.extension}`:"",o;if(this.config.includeTimestamp){let s=Date.now();o=`${n}-${e}-${s}${r}`}else o=`${n}-${e}${r}`;return(0,Nf.join)(this.baseDir,o)}async output(e,n={}){try{let r=n.metadata?.sessionId;if(!r)return!1;let o={...e,timestamp:Date.now(),sessionId:r},s=this.getFilePath(r);return(0,Oo.writeFileSync)(s,JSON.stringify(o,null,2),"utf-8"),!0}catch{return!1}}getBaseDir(){return this.baseDir}}});var Mf,Po,L_=Le(()=>{"use strict";Ff();If();Bf();Mf=class{handlers=new Map;defaultOptions={};registerHandler(e,n){this.handlers.set(e,n)}registerHandlers(e){for(let n of e)if(n.enabled!==!1)try{let r=this.createHandler(n),o=n.type+"_"+Date.now();this.registerHandler(o,r)}catch(r){console.error(`[OutputManager] Failed to register ${n.type} handler:`,r)}}createHandler(e){switch(e.type){case"console":return new Is(e.config);case"webhook":return new Ns(e.config);case"temp-file":return new Bs(e.config);default:throw new Error(`Unknown output handler type: ${e.type}`)}}unregisterHandler(e){return this.handlers.delete(e)}getHandler(e){return this.handlers.get(e)}getAllHandlers(){return new Map(this.handlers)}clearHandlers(){this.handlers.clear()}setDefaultOptions(e){this.defaultOptions={...this.defaultOptions,...e}}getDefaultOptions(){return{...this.defaultOptions}}async output(e,n){let r={...this.defaultOptions,...n},o={success:[],failed:[]},s=Array.from(this.handlers.entries()).map(async([a,u])=>{try{await u.output(e,r)?o.success.push(a):o.failed.push(a)}catch(l){console.error(`[OutputManager] Handler ${a} failed:`,l),o.failed.push(a)}});return await Promise.all(s),o}async outputTo(e,n,r){let o={...this.defaultOptions,...r},s={success:[],failed:[]},a=e.map(async u=>{let l=this.handlers.get(u);if(!l){console.warn(`[OutputManager] Handler ${u} not found`),s.failed.push(u);return}try{await l.output(n,o)?s.success.push(u):s.failed.push(u)}catch(f){console.error(`[OutputManager] Handler ${u} failed:`,f),s.failed.push(u)}});return await Promise.all(a),s}async outputToType(e,n,r){let o=Array.from(this.handlers.entries()).filter(([s,a])=>a.type===e).map(([s])=>s);return this.outputTo(o,n,r)}},Po=new Mf});var FR={};hi(FR,{ActiveProbeService:()=>Ps,ConfigService:()=>hr,ProviderHealthStore:()=>Ho,ProviderService:()=>yr,SSEParserTransform:()=>Or,SSESerializerTransform:()=>Fs,TokenizerService:()=>xr,TransformerService:()=>Rr,calculateTokenCount:()=>xc,default:()=>PR,extractSessionIdFromUserId:()=>Qr,getActiveProbeService:()=>vf,getAllQuotaResults:()=>Dp,getAllRateLimitInfo:()=>Ap,getHealthStore:()=>Je,getQuotaAdapter:()=>Du,getQuotaResult:()=>Ti,getRateLimitInfo:()=>kp,getRuntimeDebugLog:()=>cp,initProviderHealthPersistence:()=>Ei,initQuotaStorePersistence:()=>vi,initRateLimitPersistence:()=>ki,normalizeSessionId:()=>mr,pluginManager:()=>Rf,resetActiveProbeService:()=>O_,rewriteStream:()=>Pf,router:()=>eo,searchProjectBySession:()=>Oc,sessionUsageCache:()=>Ai,setRuntimeDebugLog:()=>up,startActiveProbe:()=>Ru,stopActiveProbe:()=>xu,storeQuotaResult:()=>Si,tokenSpeedPlugin:()=>jf});module.exports=Hh(FR);var q_=xe(require("fastify"),1),z_=xe(require("@fastify/cors"),1);var zo=require("fs"),pc=require("path"),tp=require("dotenv"),np=xe(hc(),1),hr=class{config={};options;constructor(e={jsonPath:"./config.json"}){this.options={envPath:e.envPath||".env",jsonPath:e.jsonPath,useEnvFile:!1,useJsonFile:e.useJsonFile!==!1,useEnvironmentVariables:e.useEnvironmentVariables!==!1,...e},this.loadConfig()}loadConfig(){this.options.useJsonFile&&this.options.jsonPath&&this.loadJsonConfig(),this.options.initialConfig&&(this.config={...this.config,...this.options.initialConfig}),this.options.useEnvFile&&this.loadEnvConfig(),this.config.LOG_FILE&&(process.env.LOG_FILE=this.config.LOG_FILE),this.config.LOG&&(process.env.LOG=this.config.LOG)}loadJsonConfig(){if(!this.options.jsonPath)return;let e=this.isAbsolutePath(this.options.jsonPath)?this.options.jsonPath:(0,pc.join)(process.cwd(),this.options.jsonPath);if((0,zo.existsSync)(e))try{let n=(0,zo.readFileSync)(e,"utf-8"),r=np.default.parse(n);this.config={...this.config,...r},console.log(`Loaded JSON config from: ${e}`)}catch(n){console.warn(`Failed to load JSON config from ${e}:`,n)}else console.warn(`JSON config file not found: ${e}`)}loadEnvConfig(){let e=this.isAbsolutePath(this.options.envPath)?this.options.envPath:(0,pc.join)(process.cwd(),this.options.envPath);if((0,zo.existsSync)(e))try{let n=(0,tp.config)({path:e});n.parsed&&(this.config={...this.config,...this.parseEnvConfig(n.parsed)})}catch(n){console.warn(`Failed to load .env config from ${e}:`,n)}}loadEnvironmentVariables(){let e=this.parseEnvConfig(process.env);this.config={...this.config,...e}}parseEnvConfig(e){let n={};return Object.assign(n,e),n}isAbsolutePath(e){return e.startsWith("/")||e.includes(":")}get(e,n){let r=this.config[e];return r!==void 0?r:n}getAll(){return{...this.config}}getHttpsProxy(){return this.get("HTTPS_PROXY")||this.get("https_proxy")||this.get("httpsProxy")||this.get("PROXY_URL")}has(e){return this.config[e]!==void 0}set(e,n){this.config[e]=n}reload(){this.config={},this.loadConfig()}getConfigSummary(){let e=[];return this.options.initialConfig&&e.push("Initial Config"),this.options.useJsonFile&&this.options.jsonPath&&e.push(`JSON: ${this.options.jsonPath}`),this.options.useEnvFile&&e.push(`ENV: ${this.options.envPath}`),this.options.useEnvironmentVariables&&e.push("Environment Variables"),`Config sources: ${e.join(", ")}`}};function He(t,e=500,n="internal_error",r="api_error"){let o=new Error(t);return o.statusCode=e,o.code=n,o.type=r,o}async function rp(t,e,n){e.log.error(t);let r=t.statusCode||500;if(t.rawBody)try{let s=JSON.parse(t.rawBody);return n.code(r).send(s)}catch{return n.code(r).send(t.rawBody)}let o={error:{message:t.message+(t.stack||"Internal Server Error"),type:t.type||"api_error",code:t.code||"internal_error"}};return n.code(r).send(o)}var op=require("undici");function sp(t,e,n,r,o){let s=new Headers({"Content-Type":"application/json"});n.headers&&Object.entries(n.headers).forEach(([f,d])=>{d&&s.set(f,d)});let a,u=AbortSignal.timeout(n.TIMEOUT??60*1e3*60);if(n.signal){let f=new AbortController,d=()=>f.abort();n.signal.addEventListener("abort",d),u.addEventListener("abort",d),a=f.signal}else a=u;let l={method:"POST",headers:s,body:JSON.stringify(e),signal:a};return n.httpsProxy&&(l.dispatcher=new op.ProxyAgent(new URL(n.httpsProxy).toString())),o?.debug({reqId:r.req.id,request:l,headers:Object.fromEntries(s.entries()),requestUrl:typeof t=="string"?t:t.toString(),useProxy:n.httpsProxy},"final request"),fetch(typeof t=="string"?t:t.toString(),l)}var LC=new Set(["authorization","x-api-key","x-goog-api-key","cookie","set-cookie"]),Gr={maxBodyLength:4096,maxStreamChunks:100},mc=!1;function up(t){mc=t}function cp(){return mc}function _i(t){return mc||t.get("DEBUG_LOG")===!0}function lp(t){let e=t.get("DEBUG_LOG_OPTIONS");return!e||typeof e!="object"?Gr:{maxBodyLength:e.maxBodyLength??Gr.maxBodyLength,maxStreamChunks:e.maxStreamChunks??Gr.maxStreamChunks}}function dp(t){let e={};for(let[n,r]of Object.entries(t))LC.has(n.toLowerCase())?e[n]="***MASKED***":e[n]=r;return e}function bi(t,e){return t.length<=e?t:t.slice(0,e)+`...[truncated, total ${t.length} bytes]`}function fp(t,e,n){let r=typeof n.body=="string"?n.body:JSON.stringify(n.body);t.info({debug_log:!0,reqId:e,phase:"provider_request",url:n.url,headers:dp(n.headers),body:bi(r,Gr.maxBodyLength)})}function hp(t,e,n){t.info({debug_log:!0,reqId:e,phase:"provider_response",status:n.status,headers:dp(n.headers),...n.body!==void 0?{body:bi(n.body,Gr.maxBodyLength)}:{}})}function ip(t,e,n,r){let o=jC(r);t.info({debug_log:!0,reqId:e,phase:"provider_stream_chunk",chunkIndex:n,...o?{usage_monitor:o}:{},data:bi(r,Gr.maxBodyLength)})}function jC(t){let e=[];for(let n of t.split(/\r?\n/)){if(!n.startsWith("data:"))continue;let r=n.slice(5).trim();if(!(!r||r==="[DONE]"))try{let o=JSON.parse(r),s=o?.usage||o?.response?.usage;s&&e.push({path:o?.usage?"usage":"response.usage",usage:s,choicesLength:Array.isArray(o?.choices)?o.choices.length:void 0,finishReason:o?.choices?.[0]?.finish_reason})}catch{e.push({parseError:!0,data:bi(r,512)})}}if(e.length)return e.length===1?e[0]:e}function ap(t,e,n){t.info({debug_log:!0,reqId:e,phase:"provider_stream_end",totalChunks:n})}function pp(t,e,n,r){(async()=>{let o=t.pipeThrough(new TextDecoderStream).getReader(),s=0;try{let a="";for(;;){let{done:u,value:l}=await o.read();if(u)break;a+=l;let f=a.split(`
|
|
73
73
|
|
|
74
|
-
`);a=f.pop()||"";for(let d of f)d.trim()&&(s<r.maxStreamChunks?ip(e,n,s,d):s===r.maxStreamChunks&&e.info({debug_log:!0,reqId:n,phase:"provider_stream_chunk_omitted",message:`[chunks after #${s} omitted, maxStreamChunks=${r.maxStreamChunks}]`}),s++)}a.trim()&&(s<r.maxStreamChunks&&ip(e,n,s,a.trim()),s++),ap(e,n,s)}catch(a){console.error(`[debug_log] Error reading debug stream for reqId=${n}:`,a),ap(e,n,s)}finally{o.releaseLock()}})()}var Ci="2.3.
|
|
74
|
+
`);a=f.pop()||"";for(let d of f)d.trim()&&(s<r.maxStreamChunks?ip(e,n,s,d):s===r.maxStreamChunks&&e.info({debug_log:!0,reqId:n,phase:"provider_stream_chunk_omitted",message:`[chunks after #${s} omitted, maxStreamChunks=${r.maxStreamChunks}]`}),s++)}a.trim()&&(s<r.maxStreamChunks&&ip(e,n,s,a.trim()),s++),ap(e,n,s)}catch(a){console.error(`[debug_log] Error reading debug stream for reqId=${n}:`,a),ap(e,n,s)}finally{o.releaseLock()}})()}var Ci="2.3.21";Wn();var Sn=require("fs"),Ac=require("path"),Ep=require("@wengine-ai/claude-code-router-shared"),Ec=(0,Ac.join)(Ep.HOME_DIR,"runtime"),kc=(0,Ac.join)(Ec,"rate-limit.json"),Vr=new Map,bp=!1,JC=null,VC=["x-ratelimit-remaining-tokens","x-ratelimit-remaining-requests","x-ratelimit-remaining"],KC=["x-ratelimit-limit-tokens","x-ratelimit-limit-requests","x-ratelimit-limit"],YC=["x-ratelimit-reset-tokens","x-ratelimit-reset-requests","x-ratelimit-reset"];function Wo(t,e,n){let r=u=>{if(n instanceof Headers)return n.get(u);let l=Object.keys(n).find(f=>f.toLowerCase()===u);return l?n[l]:null},o=wc(r,VC),s=wc(r,KC),a=wc(r,YC);(o||s||a)&&Vr.set(t,{provider:t,remaining:Cp(o),limit:Cp(s),reset:QC(a),capturedAt:Date.now()})}function kp(t){return Vr.get(t)}function Ap(){return Array.from(Vr.values())}function wc(t,e){for(let n of e){let r=t(n);if(r)return r}return null}function Cp(t){if(!t)return null;let e=Number(t.trim().replace(/,/g,""));return Number.isFinite(e)?e:null}function QC(t){if(!t)return null;let e=t.trim();if(!e)return null;let n=Number(e);if(Number.isFinite(n))return n>1e12?Math.floor(n/1e3):n>1e9?Math.floor(n):Math.floor(Date.now()/1e3+n);let r=Date.parse(e);if(Number.isFinite(r))return Math.floor(r/1e3);let o=XC(e);return o===null?null:Math.floor((Date.now()+o)/1e3)}function XC(t){let e=/(\d+(?:\.\d+)?)(ms|s|m|h|d)/gi,n=0,r=!1;for(let o of t.matchAll(e)){r=!0;let s=Number(o[1]),a=o[2].toLowerCase();if(Number.isFinite(s))switch(a){case"ms":n+=s;break;case"s":n+=s*1e3;break;case"m":n+=s*60*1e3;break;case"h":n+=s*60*60*1e3;break;case"d":n+=s*24*60*60*1e3;break}}return r?n:null}function ZC(){try{if(!(0,Sn.existsSync)(kc))return;let t=JSON.parse((0,Sn.readFileSync)(kc,"utf-8"));if(!Array.isArray(t))return;let e=Date.now();for(let n of t)if(n&&n.provider){if(n.capturedAt&&e-n.capturedAt>3600*1e3)continue;Vr.set(n.provider,n)}}catch{}}function wp(){try{if(Vr.size===0)return;(0,Sn.existsSync)(Ec)||(0,Sn.mkdirSync)(Ec,{recursive:!0}),(0,Sn.writeFileSync)(kc,JSON.stringify(Array.from(Vr.values()),null,2),"utf-8")}catch{}}function ki(){bp||(bp=!0,ZC(),JC=setInterval(wp,6e4),process.on("exit",wp))}wi();var Kr=class{name="openai-responses";endPoint="/v1/responses";getCacheReadInputTokens(e){return e?.cache_read_input_tokens??e?.input_tokens_details?.cached_tokens??e?.prompt_tokens_details?.cached_tokens??0}getCacheCreationInputTokens(e){return e?.cache_creation_input_tokens??e?.input_tokens_details?.cache_creation_tokens??e?.input_tokens_details?.cache_write_tokens??e?.prompt_tokens_details?.cache_creation_tokens??e?.prompt_tokens_details?.cache_write_tokens??0}buildResponsesUsage(e){let n=e?.input_tokens??e?.prompt_tokens??0,r=e?.output_tokens??e?.completion_tokens??0,o=this.getCacheReadInputTokens(e),s=this.getCacheCreationInputTokens(e);return{input_tokens:n,output_tokens:r,total_tokens:e?.total_tokens??n+r,input_tokens_details:{cached_tokens:o,cache_creation_tokens:s},output_tokens_details:{reasoning_tokens:e?.output_tokens_details?.reasoning_tokens??0},cache_read_input_tokens:o,cache_creation_input_tokens:s}}async transformRequestIn(e){delete e.temperature,delete e.max_tokens,e.reasoning&&(e.reasoning={effort:e.reasoning.effort,summary:e.reasoning.summary||"auto"});let n=[],r=e.messages.filter(o=>o.role==="system");if(r.length>0){let o=r[0];Array.isArray(o.content)?o.content.forEach(s=>{let a="";typeof s=="string"?a=s:s&&typeof s=="object"&&"text"in s&&(a=s.text),n.push({role:"system",content:a})}):e.instructions=o.content}if(e.messages.forEach(o=>{if(o.role!=="system"){if(Array.isArray(o.content)){let s=o.content.map(a=>this.normalizeRequestContent(a,o.role)).filter(a=>a!==null);s.length>0?o.content=s:delete o.content}if(o.role==="tool"){let s={...o};s.type="function_call_output",s.call_id=o.tool_call_id,s.output=o.content,delete s.cache_control,delete s.role,delete s.tool_call_id,delete s.content,n.push(s);return}if(o.role==="assistant"&&Array.isArray(o.tool_calls)){o.tool_calls.forEach(s=>{n.push({type:"function_call",arguments:s.function.arguments,name:s.function.name,call_id:s.id})});return}n.push(o)}}),e.input=n,delete e.messages,Array.isArray(e.tools)){let o=e.tools.find(s=>s.function.name==="web_search");e.tools=e.tools.filter(s=>s.function.name!=="web_search").map(s=>(s.function.name==="WebSearch"&&delete s.function.parameters.properties.allowed_domains,s.function.name==="Edit"?{type:s.type,name:s.function.name,description:s.function.description,parameters:{...s.function.parameters,required:["file_path","old_string","new_string","replace_all"]},strict:!0}:{type:s.type,name:s.function.name,description:s.function.description,parameters:s.function.parameters})),o&&e.tools.push({type:"web_search"})}return e.parallel_tool_calls=!1,e}async transformResponseOut(e){let n=e.headers.get("Content-Type")||"";if(n.includes("application/json")){let r=await e.json();if(r.object==="response"&&r.output){let o=this.convertResponseToChat(r);return new Response(JSON.stringify(o),{status:e.status,statusText:e.statusText,headers:e.headers})}return r.type==="message"&&Array.isArray(r.content)?new Response(JSON.stringify(r),{status:e.status,statusText:e.statusText,headers:e.headers}):new Response(JSON.stringify(r),{status:e.status,statusText:e.statusText,headers:e.headers})}else if(n.includes("text/event-stream")){if(!e.body)return e;let[r,o]=e.body.tee(),s=r.getReader(),a="";try{let{value:g,done:_}=await s.read();!_&&g&&(a=new TextDecoder().decode(g))}finally{s.releaseLock()}if(r.cancel().catch(()=>{}),a.includes("message_start")||a.includes("content_block_start")){let g=this.prependToStream(a,o);return new Response(g,{status:e.status,statusText:e.statusText,headers:e.headers})}if(a.includes("response.created")||a.includes("response.output_text.delta")||a.includes("response.in_progress")||a.includes("response.output_item.added")){let g=this.prependToStream(a,o);return new Response(g,{status:e.status,statusText:e.statusText,headers:e.headers})}let f=this.prependToStream(a,o),d=new TextDecoder,h=new TextEncoder,m="",C=!1,w=this,y=new ReadableStream({async start(g){let _=f.getReader(),E=-1,b="",S=v=>(v!==b&&(E++,b=v),E);try{for(;;){let{done:v,value:R}=await _.read();if(v){C||g.enqueue(h.encode(`data: [DONE]
|
|
75
75
|
|
|
76
76
|
`));break}let $=d.decode(R,{stream:!0});m+=$;let H=m.split(/\r?\n/);m=H.pop()||"";for(let P of H)if(P.trim())try{if(P.startsWith("event: "))continue;if(P.startsWith("data: ")){let K=P.slice(5).trim();if(K==="[DONE]"){C=!0,g.enqueue(h.encode(`data: [DONE]
|
|
77
77
|
|