@wengine-ai/llms 2.3.23 → 2.3.232

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 (98) hide show
  1. package/README.md +9 -4
  2. package/dist/api/middleware.d.ts +9 -0
  3. package/dist/api/routes.d.ts +24 -0
  4. package/dist/ccr/admin-routes.d.ts +6 -0
  5. package/dist/ccr/agents/image.agent.d.ts +10 -0
  6. package/dist/ccr/agents/index.d.ts +28 -0
  7. package/dist/ccr/agents/type.d.ts +13 -0
  8. package/dist/ccr/auth.d.ts +8 -0
  9. package/dist/ccr/claude-config-init.d.ts +1 -0
  10. package/dist/ccr/codex-accounts.d.ts +9 -0
  11. package/dist/ccr/codex-usage-cache.d.ts +2 -0
  12. package/dist/ccr/config.d.ts +6 -0
  13. package/dist/ccr/create-server.d.ts +6 -0
  14. package/dist/ccr/health-reconcile.d.ts +22 -0
  15. package/dist/ccr/models.d.ts +26 -0
  16. package/dist/ccr/pino-retention.d.ts +1 -0
  17. package/dist/ccr/plugin-registration.d.ts +6 -0
  18. package/dist/ccr/request-pipeline.d.ts +37 -0
  19. package/dist/ccr/usage-merge.d.ts +32 -0
  20. package/dist/ccr/usage-store.d.ts +109 -0
  21. package/dist/cjs/server.cjs +337 -350
  22. package/dist/cjs/server.cjs.map +4 -4
  23. package/dist/clients/adapters.d.ts +22 -0
  24. package/dist/esm/server.mjs +338 -350
  25. package/dist/esm/server.mjs.map +4 -4
  26. package/dist/index.html +227 -0
  27. package/dist/plugins/index.d.ts +4 -0
  28. package/dist/plugins/output/console-handler.d.ts +23 -0
  29. package/dist/plugins/output/index.d.ts +29 -0
  30. package/dist/plugins/output/output-manager.d.ts +108 -0
  31. package/dist/plugins/output/temp-file-handler.d.ts +48 -0
  32. package/dist/plugins/output/types.d.ts +157 -0
  33. package/dist/plugins/output/webhook-handler.d.ts +35 -0
  34. package/dist/plugins/plugin-manager.d.ts +62 -0
  35. package/dist/plugins/token-speed.d.ts +5 -0
  36. package/dist/plugins/types.d.ts +25 -0
  37. package/dist/server.d.ts +110 -0
  38. package/dist/services/active-probe.d.ts +96 -0
  39. package/dist/services/config.d.ts +30 -0
  40. package/dist/services/provider-health.d.ts +135 -0
  41. package/dist/services/provider.d.ts +32 -0
  42. package/dist/services/proxy.d.ts +58 -0
  43. package/dist/services/quota-adapters.d.ts +16 -0
  44. package/dist/services/quota-store.d.ts +12 -0
  45. package/dist/services/rate-limit.d.ts +26 -0
  46. package/dist/services/tokenizer.d.ts +45 -0
  47. package/dist/services/transformer.d.ts +28 -0
  48. package/dist/tiktoken_bg.wasm +0 -0
  49. package/dist/tokenizer/api-tokenizer.d.ts +46 -0
  50. package/dist/tokenizer/huggingface-tokenizer.d.ts +52 -0
  51. package/dist/tokenizer/tiktoken-tokenizer.d.ts +20 -0
  52. package/dist/transformer/anthropic.transformer.d.ts +18 -0
  53. package/dist/transformer/cerebras.transformer.d.ts +16 -0
  54. package/dist/transformer/cleancache.transformer.d.ts +6 -0
  55. package/dist/transformer/customparams.transformer.d.ts +30 -0
  56. package/dist/transformer/deepseek.transformer.d.ts +7 -0
  57. package/dist/transformer/enhancetool.transformer.d.ts +5 -0
  58. package/dist/transformer/forcereasoning.transformer.d.ts +7 -0
  59. package/dist/transformer/gemini.transformer.d.ts +10 -0
  60. package/dist/transformer/groq.transformer.d.ts +7 -0
  61. package/dist/transformer/index.d.ts +49 -0
  62. package/dist/transformer/kimi.transformer.d.ts +17 -0
  63. package/dist/transformer/maxcompletiontokens.transformer.d.ts +6 -0
  64. package/dist/transformer/maxtoken.transformer.d.ts +9 -0
  65. package/dist/transformer/openai.responses.transformer.d.ts +28 -0
  66. package/dist/transformer/openai.transformer.d.ts +5 -0
  67. package/dist/transformer/opencode.transformer.d.ts +29 -0
  68. package/dist/transformer/openrouter.transformer.d.ts +9 -0
  69. package/dist/transformer/reasoning.transformer.d.ts +10 -0
  70. package/dist/transformer/sampling.transformer.d.ts +13 -0
  71. package/dist/transformer/streamoptions.transformer.d.ts +6 -0
  72. package/dist/transformer/tooluse.transformer.d.ts +7 -0
  73. package/dist/transformer/vercel.transformer.d.ts +10 -0
  74. package/dist/transformer/vertex-claude.transformer.d.ts +8 -0
  75. package/dist/transformer/vertex-gemini.transformer.d.ts +8 -0
  76. package/dist/types/llm.d.ts +233 -0
  77. package/dist/types/transformer.d.ts +23 -0
  78. package/dist/utils/cache.d.ts +17 -0
  79. package/dist/utils/converter.d.ts +12 -0
  80. package/dist/utils/debug-log.d.ts +24 -0
  81. package/dist/utils/fallback-promotion.d.ts +65 -0
  82. package/dist/utils/gemini.util.d.ts +11 -0
  83. package/dist/utils/image.d.ts +1 -0
  84. package/dist/utils/request.d.ts +2 -0
  85. package/dist/utils/router.d.ts +84 -0
  86. package/dist/utils/session-id.d.ts +2 -0
  87. package/dist/utils/sse/SSEParser.transform.d.ts +6 -0
  88. package/dist/utils/sse/SSESerializer.transform.d.ts +3 -0
  89. package/dist/utils/sse/index.d.ts +3 -0
  90. package/dist/utils/sse/rewriteStream.d.ts +9 -0
  91. package/dist/utils/thinking.d.ts +2 -0
  92. package/dist/utils/toolArgumentsParser.d.ts +8 -0
  93. package/dist/utils/vertex-claude.util.d.ts +42 -0
  94. package/package.json +13 -6
  95. package/vitest.config.ts +1 -0
  96. package/dist/index.d.ts +0 -175
  97. package/dist/plugins.d.ts +0 -42
  98. package/dist/provider-health.d.ts +0 -39
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.232** | <ul><li>**严格项目级路由成为权威边界**: 项目配置非空 `Router` 后,目标缺失、格式错误、被禁用、不健康或额度耗尽不再静默回落到全局,而是返回稳定错误码;项目内显式 fallback 仍可用,同时关闭全局 Router/custom router、全局 fallback、客户端与 subagent model override 等项目外逃逸路径。</li><li>**Provider 级代理策略**: 新增顶层 `PROXY_GLOBAL_ENABLED` 与 provider 级 `proxy_enabled`;全局关闭时仅显式标记的 provider 使用共享 `PROXY_URL`,其余直连,未配置或开启时保持全部走代理的旧行为。推理、fallback、健康探测、额度查询、wakeup、provider API tokenizer 等出站请求统一遵循该策略。</li><li>**代理安全校验与 UI 控制**: 保存配置前仅允许 `http://`、`https://`、空值或环境变量占位符,非法协议/畸形 URL 逐项报错且不覆盖原配置;设置页新增「全局生效」开关、provider 卡片新增独立代理开关,并提示仅使用可信代理。ProxyAgent 改为按 URL 缓存复用,关闭时释放,日志自动脱敏代理凭据。</li><li>**per-client adapters 与 pi 上下文路由**: runtime 下沉到 core 并用 adapter 统一 Claude Code、Codex、pi、qwen-code、opencode 的差异;pi 新增 `extendedContextRatio`(默认 80%),不再生成/消费 `[1m]` 后缀,longContext 改用绝对阈值链。</li><li>**修复严格路由、客户端识别与用量回归**: 畸形项目 target 不再逃逸全局;Codex Responses 请求不再被误判为 Claude Code;修复非流式 `upstreamModel` 丢失、失败请求串用上一请求 tokens、`response.completed` 将 usage merge base 归零、项目路由失败清除会话用量基线等问题。</li><li>**修复 runtime 生命周期与错误处理**: `createCcrServer({ port })` 正确采用传入端口,401/403 认证不再挂起,provider/transformer/tokenizer 初始化完成后才监听,preset 注册失败不再静默吞掉。</li></ul> |
97
+ | **v2.3.231** | <ul><li>**项目级接管默认仅接管 Claude Code**: 此前打开项目「CCR 接管」默认接管全部客户端(Claude Code、pi、qwen-code、opencode),会把 `.pi/settings.json`、`.qwen/settings.json`、`opencode.json` 等配置写进项目目录(opencode 首次运行还会自行生成 `AGENTS.md`),污染项目根目录;现在所有默认路径(主开关、多选清空、legacy `enabled: true`)都只接管 Claude Code,其余客户端在多选中显式勾选才接管。</li><li>**修复「检查更新」永远提示已是最新(双重 bug)**: 后端 `checkForUpdates`/`performUpdate` 硬编码了已从 npm unpublish 的旧包名 `claude-code-router-next`,`npm view` 404 被 catch 静默吞掉、恒返回「无更新」,「立即更新」也因同样的错包名安装失败,现改为从 `package.json` 动态读取包名(`@wengine-ai/claude-code-router-next`),今后改包名不会再失效;另外前端更新弹窗条件要求 `changelog` 非空而后端该字段恒为空串,即使有新版本也不弹窗,现去掉该强制判断(弹窗内已有「暂无更新日志」兜底文案)。</li><li>**发布确认点闸门(release gate)**: `scripts/release.sh` 新增 `validate_release_docs`,发布前(含 dry-run)强制校验:6 个 `package.json` 版本一致、`CHANGELOG.md` 有该版本的非空段落、两份 README 表格有该版本行、版本号严格大于 npm 已发布 latest(逐段数字比较,registry 不可达时警告跳过);任一不满足即中止发布。</li><li>**版本号策略:多位 patch 日常小迭代**: 自本版本起日常小迭代在 patch 段追加一位数字(`2.3.23` → `2.3.231` → `2.3.232`),避免每日发布推高主版本数字;patch 按数字比较,用过 `2.3.23x` 后下一个功能版本是 `2.3.240`(`2.3.24` 会被闸门当降级拦截)或直接升 `2.4.0`。</li></ul> |
96
98
  | **v2.3.23** | <ul><li>**修复状态栏 token 速率虚高(撞到 999 上限)**: `ccr statusline` 的 token 速率常显示几百、极端撞到 999 上限,与「用量统计」的几十 t/s 对不上。根因是 token-speed 插件流式过程中每秒上报的是「最近 1 秒到达的 token 数」滑动窗口值,而 SSE delta 常成批到达(同一时间戳)使瞬时计数虚高;现改为与用量统计同一套解码平均公式(`输出 token ÷ (总耗时 − TTFT)`)上报,速率随流式收敛到最终值、与用量统计一致,并移除失效的滑动窗口记账。</li><li>**状态栏默认改为无图标表格风格**: 默认主题不再带装饰图标,模块之间改用细竖线 `│` 分隔,呈简洁表格样式,默认模块顺序为「模型 │ 工作目录 │ git 分支 │ 上下文进度条 │ token 速率 │ 会话总 token」。动机是歧义宽度的 emoji 图标(如闪电 `⚡` U+26A1)会让 Claude Code 误算状态栏显示宽度、在双击等重绘时产生数字重影/位移;改用定宽字符或不带图标可避免。图标仍支持在 UI 中自定义。</li><li>**修复 UI 状态栏图标无法删除 + 旧圆圈模块迁移**: 切换所选模块时图标搜索框(`IconSearchInput`)不随 `value` 同步、与模块真实图标脱节导致看似删不掉,现已在 `value` 变化时同步;同时加载配置时把旧 `contextCircle` 模块迁移为 `contextBar`,配置弹窗显示长条进度条而非旧圆圈图标。</li><li>**`build:ui` 构建后同步产物到 CLI/根 dist**: `pnpm build:ui` 现会把 `index.html` 同步到 `packages/cli/dist` 与根 `dist`,使单独运行 `build:ui` 也能更新本地运行中的 ccr 实际读取的包。</li></ul> |
97
99
  | **v2.3.22** | <ul><li>**状态栏按用户配置的压缩阈值显示上下文上限**: `ccr statusline` 优先用 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(CCR 从顶层 `ContextWindow` 写入)作为上下文上限与百分比分母,未设才回退到 Claude Code 的窗口值,使状态栏与实际 auto-compact 触发点对齐,不再固定显示 1M/200k。</li><li>**接管保留用户手写的 auto-compact 自定义值**: CCR 接管 Claude Code 时不再无条件用 `ContextWindow` 覆盖 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`,改用状态文件(`~/.claude-code-router/client-state.json` 全局、`<project-id>/ccr-state.json` 项目级)区分 CCR 写入值(可随 `ContextWindow` 更新)与用户手写值(保留);卸载只清自己写的、保留用户的;状态缺失时保守保留,绝不误覆盖。</li><li>**UI 上下文窗口与扩展上下文配合提示**: 顶层 `ContextWindow` > 200000 但未启用对应模型家族「扩展上下文 (1M)」(模型名带 `[1m]`)时显示红色警告,并在两处配置补充配合说明——否则 Claude Code 会将其封顶至 200000。</li><li>**修复状态栏百分比按 1M 计算与偶发闪 0%**: statusline 子进程不一定继承项目级 settings 的环境变量,导致项目里设的 `CLAUDE_CODE_AUTO_COMPACT_WINDOW` 被漏读而按 1M 计算,现在依次从 `process.env`→项目 `settings.local.json`→全局 `settings.json` 读取;另 `current_usage` 在请求进行中/刚 auto-compact 后会暂态为空使百分比偶发闪 0%,现回退到 transcript 最近一条 assistant 消息的上下文用量保持稳定。</li><li>**修复项目接管 disable→enable 循环冻结窗口与 disable 残留**: 项目级 `ccr-state.json` 在 disable 后丢失、再 enable 时不重建,导致 CCR 无法识别自己写入的窗口——既会让 `ContextWindow` 变更无法刷新生效,也会在关闭接管时把 `CLAUDE_CODE_AUTO_COMPACT_WINDOW` 当用户值残留。现在状态缺失时按「值等于当前 `ContextWindow`」兜底识别 CCR-managed,enable 重建状态、disable 清残留,真正的用户手写值仍保留。</li></ul> |
98
100
  | **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> |
@@ -101,8 +103,6 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
101
103
  | **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> |
102
104
  | **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> |
103
105
  | **v2.3.16** | <ul><li>**修复 system 消息顺序兼容 DeepSeek/vLLM**: OpenAI 兼容提供商(DeepSeek V4、GLM、vLLM)要求 `[system, user, assistant]` 顺序,此前 system 排在 user/assistant 之后会导致输出乱码;现在将 system 消息前置并去重。</li><li>**接管时去除 Attribution 动态头以提升缓存命中**: CCR 接管 Claude Code 时默认写入 `CLAUDE_CODE_ATTRIBUTION_HEADER=0`,去掉系统提示词开头随请求变化的 attribution 头(客户端版本 + prompt fingerprint),稳定上游 prompt 缓存前缀;可在设置页或 `disableAttributionHeader: false` 关闭。</li><li>**设置页布局紧凑化**: 「日志级别」下拉从独占整行移入两列网格,与「API 密钥」并排显示,减少纵向留白。</li></ul> |
104
- | **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> |
105
- | **v2.3.14** | <ul><li>**修复状态栏显示 `<synthetic>` 模型名**:Claude Code 在 auto-compact 自动压缩、中断恢复时写入的 `<synthetic>` 合成消息(非真实 LLM 响应)此前被状态栏当作模型名显示(从 Claude 账号会话切换到 ccr 接管、或发生自动压缩后尤其常见);现在过滤该合成标识,正确显示实际路由模型,同时不再把合成消息的 usage 计入 token 统计。</li></ul> |
106
106
 
107
107
  > 仅保留最近 10 个版本,更早版本的发布摘要见 [CHANGELOG-archive.md](./CHANGELOG-archive.md),完整详细变更记录见 [CHANGELOG.md](./CHANGELOG.md)。
108
108
 
@@ -117,7 +117,10 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
117
117
  > ```
118
118
 
119
119
  `config.json` 文件有几个关键部分:
120
- - **`PROXY_URL`** (可选): 您可以为 API 请求设置代理,例如:`"PROXY_URL": "http://127.0.0.1:7890"`。
120
+ - **`PROXY_URL`** (可选): 您可以为 API 请求设置代理,例如:`"PROXY_URL": "http://127.0.0.1:7890"`。CCR 进程自身通过此地址连接代理端口,无需开启系统代理、TUN 或代理软件的全局模式。
121
+ - **`PROXY_GLOBAL_ENABLED`** (可选): 控制代理的作用范围。未配置或设为 `true`(默认)时,所有 provider 的出站请求均走代理,保持与旧配置的兼容性。设为 `false` 时,仅标记了 `proxy_enabled: true` 的 provider 走代理,其余 provider 直连。所有 provider 共用顶层 `PROXY_URL`,不支持为每个 provider 单独设置代理地址。如果 `PROXY_URL` 未设置(空地址),则所有代理开关均不生效,全部直连。provider 专属的出站请求(推理、fallback、健康探测、额度查询、wakeup 唤醒、provider API tokenizer 等)均遵循同一 provider 代理策略。
122
+ > [!WARNING]
123
+ > 代理可看到 API key 和请求内容,请仅配置可信代理。
121
124
  - **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。当设置为 `false` 时,将不会创建日志文件。默认值为 `true`。
122
125
  - **`LOG_LEVEL`** (可选): 设置日志级别。可用选项包括:`"fatal"`、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`。默认值为 `"error"`;仅在显式配置为 `"info"`、`"debug"` 或 `"trace"` 时输出详细日志。
123
126
  - **日志系统**: Claude Code Router 使用两个独立的日志系统:
@@ -136,6 +139,7 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
136
139
  {
137
140
  "APIKEY": "your-secret-key",
138
141
  "PROXY_URL": "http://127.0.0.1:7890",
142
+ "PROXY_GLOBAL_ENABLED": false,
139
143
  "LOG": true,
140
144
  "LOG_LEVEL": "error",
141
145
  "API_TIMEOUT_MS": 600000,
@@ -153,7 +157,8 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
153
157
  ],
154
158
  "transformer": {
155
159
  "use": ["openrouter"]
156
- }
160
+ },
161
+ "proxy_enabled": true
157
162
  },
158
163
  {
159
164
  "name": "deepseek",
@@ -0,0 +1,9 @@
1
+ import { FastifyRequest, FastifyReply } from "fastify";
2
+ export interface ApiError extends Error {
3
+ statusCode?: number;
4
+ code?: string;
5
+ type?: string;
6
+ rawBody?: string;
7
+ }
8
+ export declare function createApiError(message: string, statusCode?: number, code?: string, type?: string): ApiError;
9
+ export declare function errorHandler(error: ApiError, request: FastifyRequest, reply: FastifyReply): Promise<never>;
@@ -0,0 +1,24 @@
1
+ import { FastifyInstance } from "fastify";
2
+ import { ConfigService } from "../services/config";
3
+ import { ProviderService } from "../services/provider";
4
+ import { TransformerService } from "../services/transformer";
5
+ export declare const CCR_FAMILY_ALIAS: RegExp;
6
+ /**
7
+ * Normalize a Responses API (Codex) request body into a unified chat request so
8
+ * downstream transformers (and the family router) can process it.
9
+ * Codex sends: { model, input: [...], instructions, tools, stream, ... }
10
+ * We convert to: { model, messages: [...], system, tools, stream, ... }
11
+ */
12
+ export declare function normalizeResponsesBody(body: any): void;
13
+ declare module "fastify" {
14
+ interface FastifyInstance {
15
+ configService: ConfigService;
16
+ providerService: ProviderService;
17
+ transformerService: TransformerService;
18
+ recordUsage?: (data: any) => void;
19
+ }
20
+ interface FastifyRequest {
21
+ provider?: string;
22
+ }
23
+ }
24
+ export declare const registerApiRoutes: (fastify: FastifyInstance) => Promise<void>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Register all CCR admin/management routes on the given Fastify app.
3
+ * This is the core of createServer() from the legacy server, minus the
4
+ * request-pipeline hooks (which are now in createCcrServer).
5
+ */
6
+ export declare function registerAdminRoutes(server: any, config: any): Promise<any>;
@@ -0,0 +1,10 @@
1
+ import { IAgent, ITool } from "./type";
2
+ export declare class ImageAgent implements IAgent {
3
+ name: string;
4
+ tools: Map<string, ITool>;
5
+ constructor();
6
+ shouldHandle(req: any, config: any): boolean;
7
+ appendTools(): void;
8
+ reqHandler(req: any, config: any): void;
9
+ }
10
+ export declare const imageAgent: ImageAgent;
@@ -0,0 +1,28 @@
1
+ import { IAgent } from './type';
2
+ export declare class AgentsManager {
3
+ private agents;
4
+ /**
5
+ * Register an agent
6
+ * @param agent The agent instance to register
7
+ * @param isDefault Whether to set as default agent
8
+ */
9
+ registerAgent(agent: IAgent): void;
10
+ /**
11
+ * Find agent by name
12
+ * @param name Agent name
13
+ * @returns Found agent instance, undefined if not found
14
+ */
15
+ getAgent(name: string): IAgent | undefined;
16
+ /**
17
+ * Get all registered agents
18
+ * @returns Array of all agent instances
19
+ */
20
+ getAllAgents(): IAgent[];
21
+ /**
22
+ * Get all agent tools
23
+ * @returns Array of tools
24
+ */
25
+ getAllTools(): any[];
26
+ }
27
+ declare const agentsManager: AgentsManager;
28
+ export default agentsManager;
@@ -0,0 +1,13 @@
1
+ export interface ITool {
2
+ name: string;
3
+ description: string;
4
+ input_schema: any;
5
+ handler: (args: any, context: any) => Promise<string>;
6
+ }
7
+ export interface IAgent {
8
+ name: string;
9
+ tools: Map<string, ITool>;
10
+ shouldHandle: (req: any, config: any) => boolean;
11
+ reqHandler: (req: any, config: any) => void;
12
+ resHandler?: (payload: any, config: any) => void;
13
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * API key authentication middleware for the CCR runtime.
3
+ *
4
+ * Migrated verbatim from packages/server/src/middleware/auth.ts.
5
+ * Local requests (127.0.0.1) are trusted and skip auth.
6
+ */
7
+ import type { FastifyRequest, FastifyReply } from "fastify";
8
+ export declare const apiKeyAuth: (config: any) => (req: FastifyRequest, reply: FastifyReply, done: () => void) => Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function initializeClaudeConfig(): Promise<void>;
@@ -0,0 +1,9 @@
1
+ export declare function switchCodexAccountBeforeUsageLimit(): Promise<void>;
2
+ export declare function switchCodexAccountAfterRateLimit(reason?: string): Promise<void>;
3
+ /** Periodically refresh managed Codex account tokens, active or not, so refresh_tokens never go stale. */
4
+ export declare function startCodexTokenRefreshScheduler(): void;
5
+ export declare function isRateLimitMessage(statusCode: number, message?: string): boolean;
6
+ export declare function getCurrentCodexAccountForRequest(): Promise<{
7
+ id?: string;
8
+ email?: string;
9
+ }>;
@@ -0,0 +1,2 @@
1
+ import { type CodexAccountsResult } from "@wengine-ai/claude-code-router-shared";
2
+ export declare function computeCodexAccountUsage(config: Record<string, any>): Promise<CodexAccountsResult>;
@@ -0,0 +1,6 @@
1
+ export declare const initDir: () => Promise<void>;
2
+ export declare const readConfigFileRaw: () => Promise<any>;
3
+ export declare const readConfigFile: () => Promise<any>;
4
+ export declare const backupConfigFile: () => Promise<string | null>;
5
+ export declare const writeConfigFile: (config: any) => Promise<void>;
6
+ export declare const initConfig: () => Promise<any>;
@@ -0,0 +1,6 @@
1
+ import Server from "../server";
2
+ export interface CcrRunOptions {
3
+ port?: number;
4
+ logger?: any;
5
+ }
6
+ export declare function createCcrServer(options?: CcrRunOptions): Promise<Server>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Build the set of provider+model pairs the current config can actually route
3
+ * to: every model listed under a provider, plus every "provider,model" target
4
+ * referenced anywhere in Router / fallback. Some providers leave `models` empty
5
+ * and are only reachable via Router, so both sources must be considered to avoid
6
+ * pruning a still-routable model's health state.
7
+ */
8
+ export declare function collectReachableModelKeys(config: any): Set<string>;
9
+ /**
10
+ * Remove circuit-breaker health entries the current config can no longer route
11
+ * to (renamed/removed models, deleted providers). Returns the number pruned.
12
+ */
13
+ export declare function reconcileHealthStore(config: any, logger?: any): number;
14
+ /**
15
+ * Clear every circuit-breaker entry for a provider whose endpoint just probed
16
+ * successfully. The probe checks endpoint-level reachability (GET /v1/models),
17
+ * so a success means any lingering "open" breaker for that provider — including
18
+ * orphaned/renamed model names the per-model recover can't reach — should be
19
+ * cleared. Genuinely broken models will reopen on the next real request.
20
+ * Returns the number of entries cleared.
21
+ */
22
+ export declare function clearProviderHealth(providerName: string, logger?: any): number;
@@ -0,0 +1,26 @@
1
+ /** Anthropic-compatible model metadata exposed by /v1/models. */
2
+ export interface AnthropicModelInfo {
3
+ type: "model";
4
+ id: string;
5
+ display_name: string;
6
+ created_at: string;
7
+ context_window: number;
8
+ contextWindow: number;
9
+ max_input_tokens: number;
10
+ maxInputTokens: number;
11
+ max_tokens: number;
12
+ maxTokens: number;
13
+ max_output_tokens: number;
14
+ maxOutputTokens: number;
15
+ capabilities: {
16
+ context_window: number;
17
+ contextWindow: number;
18
+ max_input_tokens: number;
19
+ maxInputTokens: number;
20
+ max_tokens: number;
21
+ maxTokens: number;
22
+ max_output_tokens: number;
23
+ maxOutputTokens: number;
24
+ };
25
+ }
26
+ export declare function listAnthropicCompatibleModels(config: any): AnthropicModelInfo[];
@@ -0,0 +1 @@
1
+ export declare function startPinoLogRetention(): void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Register plugins from configuration
3
+ * @param serverInstance Server instance
4
+ * @param config Application configuration
5
+ */
6
+ export declare function registerPluginsFromConfig(serverInstance: any, config: any): Promise<void>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * CCR request pipeline — explicit, ordered hooks for the CCR runtime.
3
+ *
4
+ * Hook order (enforced by namespace phases + sequential global registration):
5
+ * 1. onRequest: request timing (requestStartTime)
6
+ * 2. namespace preHandler: request normalize
7
+ * 3. namespace preHandler: adapter
8
+ * 4. global preHandler: auth/Codex
9
+ * 5. global preHandler: agent mutation
10
+ * 6. namespace preHandler: router
11
+ * 7. namespace preHandler: provider model normalization
12
+ * 8. handler: routes.ts handleTransformerEndpoint
13
+ * 9. onSend: agent tool rewrite + usage/upstream-model capture
14
+ * 10. onResponse: TTFT/speed/health/final usage record
15
+ *
16
+ * Fastify runs ancestor preHandler hooks before child plugin hooks. To enforce
17
+ * adapter → auth/Codex → agent → router without relying on registration timing,
18
+ * the namespace registers a single ordered dispatcher (see Server.registerNamespace)
19
+ * whose phases invoke the global callbacks between adapter and router.
20
+ *
21
+ * The pipeline is registered by createCcrServer AFTER admin routes and BEFORE
22
+ * namespace registration + listen, so all hooks are in deterministic order.
23
+ */
24
+ import { EventEmitter } from "node:events";
25
+ export declare function getPipelineEventEmitter(): EventEmitter;
26
+ /**
27
+ * Register all CCR request-pipeline hooks on the server instance.
28
+ * Called by createCcrServer after admin routes are registered.
29
+ */
30
+ export interface CcrPreHandlerCallbacks {
31
+ authCodex(req: any, reply: any): Promise<void>;
32
+ agent(req: any, reply: any): Promise<void>;
33
+ }
34
+ declare function normalizeCcrRequest(req: any): void;
35
+ export declare function createCcrPreHandlerCallbacks(config: any): CcrPreHandlerCallbacks;
36
+ export { normalizeCcrRequest };
37
+ export declare function registerRequestPipeline(serverInstance: any, config: any): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Pure helpers for merging per-frame LLM usage into the per-request usage cache.
3
+ *
4
+ * Extracted from index.ts so the merge contract is unit-testable without
5
+ * standing up the whole Fastify server.
6
+ */
7
+ export declare function getUsageCacheReadInputTokens(usage: any): number;
8
+ export declare function getUsageCacheCreationInputTokens(usage: any): number;
9
+ /**
10
+ * Normalize a raw usage object (from Anthropic, OpenAI Chat Completions, or
11
+ * OpenAI Responses APIs) into a common shape with input_tokens / output_tokens
12
+ * / cache_read_input_tokens / cache_creation_input_tokens fields.
13
+ */
14
+ export declare function normalizeUsagePayload(usage: any): any;
15
+ /**
16
+ * Merge an incoming usage frame into the previously captured usage for a
17
+ * request. Fields are merged so that an all-zero frame canNOT overwrite a real
18
+ * value captured earlier: each field is replaced only when the incoming value
19
+ * is non-zero.
20
+ *
21
+ * Why this matters: some OpenAI-compatible upstreams (e.g. fireworks-hosted
22
+ * GLM-5.2) report real usage on the first chunk (message_start /
23
+ * response.created) but emit an all-zero usage object on the trailing
24
+ * message_delta / response.completed. A plain `{...base, ...incoming}` spread
25
+ * let those zeros clobber the real input_tokens/output_tokens, leaving the
26
+ * whole request recorded as 0/0 in usage stats.
27
+ *
28
+ * `resetBase` should be true on the first frame of a new response
29
+ * (message_start / response.completed) so stale fields from a previous request
30
+ * in the same session don't leak in.
31
+ */
32
+ export declare function mergeUsageCapture(existing: any, incoming: any, resetBase: boolean): any;
@@ -0,0 +1,109 @@
1
+ type UsageStatus = "success" | "error";
2
+ export interface UsageRecord {
3
+ id: string;
4
+ timestamp: string;
5
+ sessionId: string;
6
+ provider: string;
7
+ originalModel: string;
8
+ model: string;
9
+ upstreamModel?: string;
10
+ modelFamily: string;
11
+ scenarioType: string;
12
+ clientType?: string;
13
+ codexAccountId?: string;
14
+ codexAccountEmail?: string;
15
+ stream: boolean;
16
+ inputTokens: number;
17
+ outputTokens: number;
18
+ cacheReadInputTokens: number;
19
+ cacheCreationInputTokens: number;
20
+ ttft: number | null;
21
+ tokensPerSecond: number | null;
22
+ durationMs: number;
23
+ status: UsageStatus;
24
+ errorMessage?: string;
25
+ responseBody?: string;
26
+ }
27
+ export interface UsageSummary {
28
+ totalRequests: number;
29
+ successCount: number;
30
+ errorCount: number;
31
+ totalInputTokens: number;
32
+ totalOutputTokens: number;
33
+ totalCacheReadInputTokens: number;
34
+ totalCacheCreationInputTokens: number;
35
+ avgTtft: number | null;
36
+ avgTokensPerSecond: number | null;
37
+ byModel: Record<string, {
38
+ count: number;
39
+ inputTokens: number;
40
+ outputTokens: number;
41
+ cacheReadInputTokens: number;
42
+ cacheCreationInputTokens: number;
43
+ }>;
44
+ byProvider: Record<string, {
45
+ count: number;
46
+ inputTokens: number;
47
+ outputTokens: number;
48
+ cacheReadInputTokens: number;
49
+ cacheCreationInputTokens: number;
50
+ }>;
51
+ byScenario: Record<string, {
52
+ count: number;
53
+ inputTokens: number;
54
+ outputTokens: number;
55
+ cacheReadInputTokens: number;
56
+ cacheCreationInputTokens: number;
57
+ }>;
58
+ byFamily: Record<string, {
59
+ count: number;
60
+ inputTokens: number;
61
+ outputTokens: number;
62
+ cacheReadInputTokens: number;
63
+ cacheCreationInputTokens: number;
64
+ }>;
65
+ byDay: Record<string, {
66
+ count: number;
67
+ inputTokens: number;
68
+ outputTokens: number;
69
+ cacheReadInputTokens: number;
70
+ cacheCreationInputTokens: number;
71
+ }>;
72
+ byClient: Record<string, {
73
+ count: number;
74
+ inputTokens: number;
75
+ outputTokens: number;
76
+ cacheReadInputTokens: number;
77
+ cacheCreationInputTokens: number;
78
+ }>;
79
+ }
80
+ export interface UsageQueryFilters {
81
+ startTime?: string;
82
+ endTime?: string;
83
+ model?: string;
84
+ provider?: string;
85
+ scenario?: string;
86
+ clientType?: string;
87
+ sessionId?: string;
88
+ status?: UsageStatus;
89
+ page?: number;
90
+ pageSize?: number;
91
+ }
92
+ export interface UsageQueryResult {
93
+ records: UsageRecord[];
94
+ summary: UsageSummary;
95
+ total: number;
96
+ page: number;
97
+ pageSize: number;
98
+ }
99
+ export declare function append(record: UsageRecord): void;
100
+ export declare function query(filters: UsageQueryFilters): UsageQueryResult;
101
+ export declare function querySummary(startTime?: string, endTime?: string, status?: UsageStatus): UsageSummary;
102
+ export declare function clear(beforeDate?: string): void;
103
+ export declare function close(): void;
104
+ export declare function readTokenSpeedStats(sessionId: string): {
105
+ ttft: number | null;
106
+ tokensPerSecond: number | null;
107
+ };
108
+ export declare function getOldestRecordTimestamp(provider: string, startTime: string, endTime: string): string | undefined;
109
+ export {};