diff --git a/README.md b/README.md index b0149e5..c6a39d9 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The `config.json` file has several key sections: - **`PROXY_URL`** (optional): You can set a proxy for API requests, for example: `"PROXY_URL": "http://127.0.0.1:7890"`. - **`LOG`** (optional): You can enable logging by setting it to `true`. When set to `false`, no log files will be created. Default is `true`. -- **`LOG_LEVEL`** (optional): Set the logging level. Available options are: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`. Default is `"info"`. +- **`LOG_LEVEL`** (optional): Set the logging level. Available options are: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`. Default is `"debug"`. - **Logging Systems**: The Claude Code Router uses two separate logging systems: - **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `~/.claude-code-router/logs/` directory with filenames like `ccr-*.log` - **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log` diff --git a/README_zh.md b/README_zh.md index 2a44e36..40f13d0 100644 --- a/README_zh.md +++ b/README_zh.md @@ -38,7 +38,7 @@ npm install -g @musistudio/claude-code-router `config.json` 文件有几个关键部分: - **`PROXY_URL`** (可选): 您可以为 API 请求设置代理,例如:`"PROXY_URL": "http://127.0.0.1:7890"`。 - **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。当设置为 `false` 时,将不会创建日志文件。默认值为 `true`。 -- **`LOG_LEVEL`** (可选): 设置日志级别。可用选项包括:`"fatal"`、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`。默认值为 `"info"`。 +- **`LOG_LEVEL`** (可选): 设置日志级别。可用选项包括:`"fatal"`、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`。默认值为 `"debug"`。 - **日志系统**: Claude Code Router 使用两个独立的日志系统: - **服务器级别日志**: HTTP 请求、API 调用和服务器事件使用 pino 记录在 `~/.claude-code-router/logs/` 目录中,文件名类似于 `ccr-*.log` - **应用程序级别日志**: 路由决策和业务逻辑事件记录在 `~/.claude-code-router/claude-code-router.log` 文件中 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c6938d..c112797 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 8.2.0 '@musistudio/llms': specifier: ^1.0.25 - version: 1.0.25(ws@8.18.3)(zod@3.25.67) + version: 1.0.25(ws@8.18.3) dotenv: specifier: ^16.4.7 version: 16.6.1 @@ -28,26 +28,26 @@ importers: version: 0.0.2 tiktoken: specifier: ^1.0.21 - version: 1.0.21 + version: 1.0.22 uuid: specifier: ^11.1.0 version: 11.1.0 devDependencies: '@types/node': specifier: ^24.0.15 - version: 24.0.15 + version: 24.3.0 esbuild: specifier: ^0.25.1 - version: 0.25.5 + version: 0.25.9 fastify: specifier: ^5.4.0 - version: 5.4.0 + version: 5.5.0 shx: specifier: ^0.4.0 version: 0.4.0 typescript: specifier: ^5.8.2 - version: 5.8.3 + version: 5.9.2 packages: @@ -55,152 +55,158 @@ packages: resolution: {integrity: sha512-xyoCtHJnt/qg5GG6IgK+UJEndz8h8ljzt/caKXmq3LfBF81nC/BW6E4x2rOWCZcvsLyVW+e8U5mtIr6UCE/kJw==} hasBin: true - '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -211,8 +217,8 @@ packages: '@fastify/ajv-compiler@4.0.2': resolution: {integrity: sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==} - '@fastify/cors@11.0.1': - resolution: {integrity: sha512-dmZaE7M1f4SM8ZZuk5RhSsDJ+ezTgI7v3HHRj8Ow9CneczsPLZV6+2j2uwdaSLn8zhTv6QV0F4ZRcqdalGx1pQ==} + '@fastify/cors@11.1.0': + resolution: {integrity: sha512-sUw8ed8wP2SouWZTIbA7V2OQtMNpLj2W6qJOYhNdcmINTu6gsxVYXjQiM9mdi8UUDlcoDDJ/W2syPo1WB2QjYA==} '@fastify/error@4.2.0': resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} @@ -235,8 +241,8 @@ packages: '@fastify/static@8.2.0': resolution: {integrity: sha512-PejC/DtT7p1yo3p+W7LiUtLMsV8fEvxAK15sozHy9t8kwo5r0uLYmhV/inURmGz1SkHZFz/8CNtHLPyhKcx4SQ==} - '@google/genai@1.8.0': - resolution: {integrity: sha512-n3KiMFesQCy2R9iSdBIuJ0JWYQ1HZBJJkmt4PPZMGZKvlgHhBAGw1kUMyX+vsAIzprN3lK45DI755lm70wPOOg==} + '@google/genai@1.14.0': + resolution: {integrity: sha512-jirYprAAJU1svjwSDVCzyVq+FrJpJd5CSxR/g2Ga/gZ0ZYZpcWjMS75KJl9y71K1mDN+tcx6s21CzCbB2R840g==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.11.0 @@ -275,14 +281,14 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@types/node@24.0.15': - resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==} + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} abstract-logging@2.0.1: resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} ajv-formats@3.0.1: @@ -322,8 +328,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - bignumber.js@9.3.0: - resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -395,8 +401,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -436,8 +442,8 @@ packages: fastify-plugin@5.0.1: resolution: {integrity: sha512-HCxs+YnRaWzCl+cWRYFnHmeRFyR5GVnJTAaCJQiYzQSDwK9MgJdyAsuL3nh0EWRCYMgQ5MeziymvmAhUHYHDUQ==} - fastify@5.4.0: - resolution: {integrity: sha512-I4dVlUe+WNQAhKSyv15w+dwUh2EPiEl4X2lGYMmNSgF83WzTMAPKGdWEv5tPsCQOb+SOZwz8Vlta2vF+OeDgRw==} + fastify@5.5.0: + resolution: {integrity: sha512-ZWSWlzj3K/DcULCnCjEiC2zn2FBPdlZsSA/pnPa/dbUfLvxkD/Nqmb0XXMXLrWkeM4uQPUvjdJpwtXmTfriXqw==} fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -494,8 +500,8 @@ packages: engines: {node: 20 || >=22} hasBin: true - google-auth-library@10.2.0: - resolution: {integrity: sha512-gy/0hRx8+Ye0HlUm3GrfpR4lbmJQ6bJ7F44DmN7GtMxxzWSojLzx0Bhv/hj7Wlj7a2On0FcT8jrz8Y1c1nxCyg==} + google-auth-library@10.2.1: + resolution: {integrity: sha512-HMxFl2NfeHYnaL1HoRIN1XgorKS+6CDaM+z9LSSN+i/nKDDL4KFFEWogMXu7jV4HZQy2MsxpY+wA5XIf3w410A==} engines: {node: '>=18'} google-auth-library@9.15.1: @@ -666,8 +672,8 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - openai@5.8.2: - resolution: {integrity: sha512-8C+nzoHYgyYOXhHGN6r0fcb4SznuEn1R7YZMvlqDbnCuE0FM2mm3T1HiYW6WIcMS/F1Of2up/cSPjLPaWt0X9Q==} + openai@5.12.2: + resolution: {integrity: sha512-xqzHHQch5Tws5PcKR2xsZGX9xtch+JQFz5zb14dGqlshmmDAFBFEWmeIpf7wVqWV+w7Emj7jRgkNJakyKE0tYQ==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -716,8 +722,8 @@ packages: pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@9.7.0: - resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} + pino@9.9.0: + resolution: {integrity: sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==} hasBin: true process-warning@4.0.1: @@ -869,8 +875,8 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - tiktoken@1.0.21: - resolution: {integrity: sha512-/kqtlepLMptX0OgbYD9aMYbM7EFrMZCL7EoHM8Psmg2FuhXoo/bH64KqOiZGGwa6oS9TPdSEDKBnV2LuB8+5vQ==} + tiktoken@1.0.22: + resolution: {integrity: sha512-PKvy1rVF1RibfF3JlXBSP0Jrcw2uq3yXdgcEXtKTYn3QJ/cBRBHDnrJ5jHky+MENZ6DIPwNUGWpkVx+7joCpNA==} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -887,16 +893,16 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - undici@7.11.0: - resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==} + undici@7.13.0: + resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==} engines: {node: '>=20.18.1'} uuid@11.1.0: @@ -949,91 +955,86 @@ packages: utf-8-validate: optional: true - zod-to-json-schema@3.24.6: - resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} - peerDependencies: - zod: ^3.24.1 - - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} - snapshots: '@anthropic-ai/sdk@0.54.0': {} - '@esbuild/aix-ppc64@0.25.5': + '@esbuild/aix-ppc64@0.25.9': optional: true - '@esbuild/android-arm64@0.25.5': + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/android-arm@0.25.5': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/android-x64@0.25.5': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/darwin-arm64@0.25.5': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/darwin-x64@0.25.5': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/freebsd-arm64@0.25.5': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/freebsd-x64@0.25.5': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/linux-arm64@0.25.5': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/linux-arm@0.25.5': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/linux-ia32@0.25.5': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/linux-loong64@0.25.5': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/linux-mips64el@0.25.5': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/linux-ppc64@0.25.5': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/linux-riscv64@0.25.5': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/linux-s390x@0.25.5': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/linux-x64@0.25.5': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/netbsd-arm64@0.25.5': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/netbsd-x64@0.25.5': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.25.5': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.25.5': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/sunos-x64@0.25.5': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@esbuild/win32-arm64@0.25.5': + '@esbuild/sunos-x64@0.25.9': optional: true - '@esbuild/win32-ia32@0.25.5': + '@esbuild/win32-arm64@0.25.9': optional: true - '@esbuild/win32-x64@0.25.5': + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.25.9': optional: true '@fastify/accept-negotiator@2.0.1': {} @@ -1044,7 +1045,7 @@ snapshots: ajv-formats: 3.0.1(ajv@8.17.1) fast-uri: 3.0.6 - '@fastify/cors@11.0.1': + '@fastify/cors@11.1.0': dependencies: fastify-plugin: 5.0.1 toad-cache: 3.7.0 @@ -1083,12 +1084,10 @@ snapshots: fastq: 1.19.1 glob: 11.0.3 - '@google/genai@1.8.0': + '@google/genai@1.14.0': dependencies: google-auth-library: 9.15.1 ws: 8.18.3 - zod: 3.25.67 - zod-to-json-schema: 3.24.6(zod@3.25.67) transitivePeerDependencies: - bufferutil - encoding @@ -1112,18 +1111,18 @@ snapshots: '@lukeed/ms@2.0.2': {} - '@musistudio/llms@1.0.25(ws@8.18.3)(zod@3.25.67)': + '@musistudio/llms@1.0.25(ws@8.18.3)': dependencies: '@anthropic-ai/sdk': 0.54.0 - '@fastify/cors': 11.0.1 - '@google/genai': 1.8.0 + '@fastify/cors': 11.1.0 + '@google/genai': 1.14.0 dotenv: 16.6.1 - fastify: 5.4.0 - google-auth-library: 10.2.0 + fastify: 5.5.0 + google-auth-library: 10.2.1 json5: 2.2.3 jsonrepair: 3.13.0 - openai: 5.8.2(ws@8.18.3)(zod@3.25.67) - undici: 7.11.0 + openai: 5.12.2(ws@8.18.3) + undici: 7.13.0 uuid: 11.1.0 transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -1146,13 +1145,13 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@types/node@24.0.15': + '@types/node@24.3.0': dependencies: - undici-types: 7.8.0 + undici-types: 7.10.0 abstract-logging@2.0.1: {} - agent-base@7.1.3: {} + agent-base@7.1.4: {} ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: @@ -1184,7 +1183,7 @@ snapshots: base64-js@1.5.1: {} - bignumber.js@9.3.0: {} + bignumber.js@9.3.1: {} braces@3.0.3: dependencies: @@ -1244,33 +1243,34 @@ snapshots: dependencies: once: 1.4.0 - esbuild@0.25.5: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.5 - '@esbuild/android-arm': 0.25.5 - '@esbuild/android-arm64': 0.25.5 - '@esbuild/android-x64': 0.25.5 - '@esbuild/darwin-arm64': 0.25.5 - '@esbuild/darwin-x64': 0.25.5 - '@esbuild/freebsd-arm64': 0.25.5 - '@esbuild/freebsd-x64': 0.25.5 - '@esbuild/linux-arm': 0.25.5 - '@esbuild/linux-arm64': 0.25.5 - '@esbuild/linux-ia32': 0.25.5 - '@esbuild/linux-loong64': 0.25.5 - '@esbuild/linux-mips64el': 0.25.5 - '@esbuild/linux-ppc64': 0.25.5 - '@esbuild/linux-riscv64': 0.25.5 - '@esbuild/linux-s390x': 0.25.5 - '@esbuild/linux-x64': 0.25.5 - '@esbuild/netbsd-arm64': 0.25.5 - '@esbuild/netbsd-x64': 0.25.5 - '@esbuild/openbsd-arm64': 0.25.5 - '@esbuild/openbsd-x64': 0.25.5 - '@esbuild/sunos-x64': 0.25.5 - '@esbuild/win32-arm64': 0.25.5 - '@esbuild/win32-ia32': 0.25.5 - '@esbuild/win32-x64': 0.25.5 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 escape-html@1.0.3: {} @@ -1317,7 +1317,7 @@ snapshots: fastify-plugin@5.0.1: {} - fastify@5.4.0: + fastify@5.5.0: dependencies: '@fastify/ajv-compiler': 4.0.2 '@fastify/error': 4.2.0 @@ -1328,7 +1328,7 @@ snapshots: fast-json-stringify: 6.0.1 find-my-way: 9.3.0 light-my-request: 6.6.0 - pino: 9.7.0 + pino: 9.9.0 process-warning: 5.0.0 rfdc: 1.4.1 secure-json-parse: 4.0.0 @@ -1418,7 +1418,7 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.0 - google-auth-library@10.2.0: + google-auth-library@10.2.1: dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 @@ -1475,7 +1475,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 + agent-base: 7.1.4 debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -1512,7 +1512,7 @@ snapshots: json-bigint@1.0.0: dependencies: - bignumber.js: 9.3.0 + bignumber.js: 9.3.1 json-schema-ref-resolver@2.0.1: dependencies: @@ -1586,10 +1586,9 @@ snapshots: dependencies: wrappy: 1.0.2 - openai@5.8.2(ws@8.18.3)(zod@3.25.67): + openai@5.12.2(ws@8.18.3): optionalDependencies: ws: 8.18.3 - zod: 3.25.67 openurl@1.1.1: {} @@ -1620,7 +1619,7 @@ snapshots: pino-std-serializers@7.0.0: {} - pino@9.7.0: + pino@9.9.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -1755,7 +1754,7 @@ snapshots: dependencies: real-require: 0.2.0 - tiktoken@1.0.21: {} + tiktoken@1.0.22: {} to-regex-range@5.0.1: dependencies: @@ -1767,11 +1766,11 @@ snapshots: tr46@0.0.3: {} - typescript@5.8.3: {} + typescript@5.9.2: {} - undici-types@7.8.0: {} + undici-types@7.10.0: {} - undici@7.11.0: {} + undici@7.13.0: {} uuid@11.1.0: {} @@ -1809,9 +1808,3 @@ snapshots: wrappy@1.0.2: {} ws@8.18.3: {} - - zod-to-json-schema@3.24.6(zod@3.25.67): - dependencies: - zod: 3.25.67 - - zod@3.25.67: {} diff --git a/src/index.ts b/src/index.ts index 8d0954d..16fc1b9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,7 @@ import { CONFIG_FILE } from "./constants"; import createWriteStream from "pino-rotating-file-stream"; import { HOME_DIR } from "./constants"; import { configureLogging } from "./utils/log"; +import { sessionUsageCache } from "./utils/cache"; async function initializeClaudeConfig() { const homeDir = homedir(); @@ -52,10 +53,10 @@ async function run(options: RunOptions = {}) { // Clean up old log files, keeping only the 10 most recent ones await cleanupLogFiles(); const config = await initConfig(); - + // Configure logging based on config configureLogging(config); - + let HOST = config.HOST; if (config.HOST && !config.APIKEY) { @@ -88,15 +89,18 @@ async function run(options: RunOptions = {}) { : port; // Configure logger based on config settings - const loggerConfig = config.LOG !== false ? { - level: config.LOG_LEVEL || "debug", - stream: createWriteStream({ - path: HOME_DIR, - filename: config.LOGNAME || `./logs/ccr-${+new Date()}.log`, - maxFiles: 3, - interval: "1d", - }), - } : false; + const loggerConfig = + config.LOG !== false + ? { + level: config.LOG_LEVEL || "debug", + stream: createWriteStream({ + path: HOME_DIR, + filename: config.LOGNAME || `./logs/ccr-${+new Date()}.log`, + maxFiles: 3, + interval: "1d", + }), + } + : false; const server = createServer({ jsonPath: CONFIG_FILE, @@ -129,6 +133,33 @@ async function run(options: RunOptions = {}) { router(req, reply, config); } }); + server.addHook("onSend", async (req, reply, payload) => { + if (req.sessionId && req.url.startsWith("/v1/messages")) { + if (payload instanceof ReadableStream) { + const [originalStream, clonedStream] = payload.tee(); + const reader1 = clonedStream.getReader(); + while (true) { + const { done, value } = await reader1.read(); + if (done) break; + // Process the value if needed + const dataStr = new TextDecoder().decode(value); + if (!dataStr.startsWith("event: message_delta")) { + continue; + } + const str = dataStr.slice(27); + try { + const message = JSON.parse(str); + sessionUsageCache.put(req.sessionId, message.usage); + } catch {} + } + + return originalStream; + } else { + sessionUsageCache.put(req.sessionId, payload.usage); + } + } + return payload; + }); server.start(); } diff --git a/src/utils/cache.ts b/src/utils/cache.ts new file mode 100644 index 0000000..621eaa5 --- /dev/null +++ b/src/utils/cache.ts @@ -0,0 +1,47 @@ +// LRU cache for session usage + +export interface Usage { + input_tokens: number; + output_tokens: number; +} + +class LRUCache { + private capacity: number; + private cache: Map; + + constructor(capacity: number) { + this.capacity = capacity; + this.cache = new Map(); + } + + get(key: K): V | undefined { + if (!this.cache.has(key)) { + return undefined; + } + const value = this.cache.get(key) as V; + // Move to end to mark as recently used + this.cache.delete(key); + this.cache.set(key, value); + return value; + } + + put(key: K, value: V): void { + if (this.cache.has(key)) { + // If key exists, delete it to update its position + this.cache.delete(key); + } else if (this.cache.size >= this.capacity) { + // If cache is full, delete the least recently used item + const leastRecentlyUsedKey = this.cache.keys().next().value; + if (leastRecentlyUsedKey !== undefined) { + this.cache.delete(leastRecentlyUsedKey); + } + } + this.cache.set(key, value); + } + + values(): V[] { + return Array.from(this.cache.values()); + } +} + +export const sessionUsageCache = new LRUCache(100); diff --git a/src/utils/codeCommand.ts b/src/utils/codeCommand.ts index 29eb896..103bd8b 100644 --- a/src/utils/codeCommand.ts +++ b/src/utils/codeCommand.ts @@ -11,12 +11,14 @@ export async function executeCodeCommand(args: string[] = []) { const config = await readConfigFile(); const env: Record = { ...process.env, - ANTHROPIC_AUTH_TOKEN: "test", + ANTHROPIC_AUTH_TOKEN: config?.APIKEY || "test", ANTHROPIC_BASE_URL: `http://127.0.0.1:${config.PORT || 3456}`, API_TIMEOUT_MS: String(config.API_TIMEOUT_MS ?? 600000), // Default to 10 minutes if not set }; - const settingsFlag: Record = {}; + const settingsFlag: Record = { + env, + }; if (config?.StatusLine?.enabled) { settingsFlag.statusLine = { type: "command", @@ -41,10 +43,10 @@ export async function executeCodeCommand(args: string[] = []) { env.ANTHROPIC_SMALL_FAST_MODEL = config.ANTHROPIC_SMALL_FAST_MODEL; } - if (config?.APIKEY) { - env.ANTHROPIC_API_KEY = config.APIKEY; - delete env.ANTHROPIC_AUTH_TOKEN; - } + // if (config?.APIKEY) { + // env.ANTHROPIC_API_KEY = config.APIKEY; + // delete env.ANTHROPIC_AUTH_TOKEN; + // } // Increment reference count when command starts incrementReferenceCount(); diff --git a/src/utils/log.ts b/src/utils/log.ts index 111cc8d..1ff6fb9 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -16,7 +16,7 @@ let logLevel: string = "info"; // Function to configure logging export function configureLogging(config: { LOG?: boolean; LOG_LEVEL?: string }) { isLogEnabled = config.LOG !== false; // Default to true if not explicitly set to false - logLevel = config.LOG_LEVEL || "info"; + logLevel = config.LOG_LEVEL || "debug"; } export function log(...args: any[]) { diff --git a/src/utils/router.ts b/src/utils/router.ts index f592d4d..38e5270 100644 --- a/src/utils/router.ts +++ b/src/utils/router.ts @@ -5,6 +5,7 @@ import { } from "@anthropic-ai/sdk/resources/messages"; import { get_encoding } from "tiktoken"; import { log } from "./log"; +import { sessionUsageCache, Usage } from "./cache"; const enc = get_encoding("cl100k_base"); @@ -62,7 +63,12 @@ const calculateTokenCount = ( return tokenCount; }; -const getUseModel = async (req: any, tokenCount: number, config: any) => { +const getUseModel = async ( + req: any, + tokenCount: number, + config: any, + lastUsage?: Usage | undefined +) => { if (req.body.model.includes(",")) { const [provider, model] = req.body.model.split(","); const finalProvider = config.Providers.find( @@ -78,7 +84,15 @@ const getUseModel = async (req: any, tokenCount: number, config: any) => { } // if tokenCount is greater than the configured threshold, use the long context model const longContextThreshold = config.Router.longContextThreshold || 60000; - if (tokenCount > longContextThreshold && config.Router.longContext) { + const lastUsageThreshold = + lastUsage && + lastUsage.input_tokens > longContextThreshold && + tokenCount > 20000; + const tokenCountThreshold = tokenCount > longContextThreshold; + if ( + (lastUsageThreshold || tokenCountThreshold) && + config.Router.longContext + ) { log( "Using long context model due to token count:", tokenCount, @@ -125,14 +139,15 @@ const getUseModel = async (req: any, tokenCount: number, config: any) => { return config.Router!.default; }; -export const router = async (req: any, _res: any, config: any) => { +export const router = async (req: any, _res: any, config: any) => { // Parse sessionId from metadata.user_id if (req.body.metadata?.user_id) { - const parts = req.body.metadata.user_id.split('_session_'); + const parts = req.body.metadata.user_id.split("_session_"); if (parts.length > 1) { req.sessionId = parts[1]; } } + const lastMessageUsage = sessionUsageCache.get(req.sessionId); const { messages, system = [], tools }: MessageCreateParamsBase = req.body; try { const tokenCount = calculateTokenCount( @@ -152,7 +167,7 @@ export const router = async (req: any, _res: any, config: any) => { } } if (!model) { - model = await getUseModel(req, tokenCount, config); + model = await getUseModel(req, tokenCount, config, lastMessageUsage); } req.body.model = model; } catch (error: any) { diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 837fe88..bce45f8 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -331,7 +331,7 @@ function App() { -
+
@@ -339,7 +339,7 @@ function App() {
-
+
diff --git a/ui/src/components/ConfigProvider.tsx b/ui/src/components/ConfigProvider.tsx index cd8d4ce..d6c01df 100644 --- a/ui/src/components/ConfigProvider.tsx +++ b/ui/src/components/ConfigProvider.tsx @@ -69,7 +69,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) { // Validate the received data to ensure it has the expected structure const validConfig = { LOG: typeof data.LOG === 'boolean' ? data.LOG : false, - LOG_LEVEL: typeof data.LOG_LEVEL === 'string' ? data.LOG_LEVEL : 'info', + LOG_LEVEL: typeof data.LOG_LEVEL === 'string' ? data.LOG_LEVEL : 'debug', CLAUDE_PATH: typeof data.CLAUDE_PATH === 'string' ? data.CLAUDE_PATH : '', HOST: typeof data.HOST === 'string' ? data.HOST : '127.0.0.1', PORT: typeof data.PORT === 'number' ? data.PORT : 3456, @@ -115,7 +115,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) { // Set default empty config when fetch fails setConfig({ LOG: false, - LOG_LEVEL: 'info', + LOG_LEVEL: 'debug', CLAUDE_PATH: '', HOST: '127.0.0.1', PORT: 3456, diff --git a/ui/src/components/Providers.tsx b/ui/src/components/Providers.tsx index a91762c..fadf0a2 100644 --- a/ui/src/components/Providers.tsx +++ b/ui/src/components/Providers.tsx @@ -14,7 +14,7 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { X, Trash2, Plus, Eye, EyeOff } from "lucide-react"; +import { X, Trash2, Plus, Eye, EyeOff, Search, XCircle } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Combobox } from "@/components/ui/combobox"; import { ComboInput } from "@/components/ui/combo-input"; @@ -38,6 +38,7 @@ export function Providers() { const [showApiKey, setShowApiKey] = useState>({}); const [apiKeyError, setApiKeyError] = useState(null); const [nameError, setNameError] = useState(null); + const [searchTerm, setSearchTerm] = useState(""); const comboInputRef = useRef(null); useEffect(() => { @@ -487,15 +488,57 @@ export function Providers() { const editingProvider = editingProviderData || (editingProviderIndex !== null ? validProviders[editingProviderIndex] : null); + // Filter providers based on search term + const filteredProviders = validProviders.filter(provider => { + if (!searchTerm) return true; + const term = searchTerm.toLowerCase(); + // Check provider name and URL + if ( + (provider.name && provider.name.toLowerCase().includes(term)) || + (provider.api_base_url && provider.api_base_url.toLowerCase().includes(term)) + ) { + return true; + } + // Check models + if (provider.models && Array.isArray(provider.models)) { + return provider.models.some(model => + model && model.toLowerCase().includes(term) + ); + } + return false; + }); + return ( - - {t("providers.title")} ({validProviders.length}) - + +
+ {t("providers.title")} ({filteredProviders.length}/{validProviders.length}) + +
+
+
+ + setSearchTerm(e.target.value)} + className="pl-8" + /> +
+ {searchTerm && ( + + )} +
diff --git a/ui/src/components/SettingsDialog.tsx b/ui/src/components/SettingsDialog.tsx index 7cf934a..c29ce8a 100644 --- a/ui/src/components/SettingsDialog.tsx +++ b/ui/src/components/SettingsDialog.tsx @@ -58,12 +58,12 @@ export function SettingsDialog({ isOpen, onOpenChange }: SettingsDialogProps) { }; return ( - - - + + + {t("toplevel.title")} -
+
- +
) : (

- 选择一个组件进行配置 + {t("statusline.select_hint")}

)} diff --git a/ui/src/index.css b/ui/src/index.css index c2b6848..2958cb3 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -119,4 +119,38 @@ body { @apply bg-background text-foreground; } + + /* 美化滚动条 - WebKit浏览器 (Chrome, Safari, Edge) */ + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + ::-webkit-scrollbar-track { + @apply bg-transparent; + border-radius: 4px; + } + + ::-webkit-scrollbar-thumb { + @apply bg-muted-foreground/30; + border-radius: 4px; + transition: background-color 0.2s ease; + } + + ::-webkit-scrollbar-thumb:hover { + @apply bg-muted-foreground/50; + } + + ::-webkit-scrollbar-corner { + @apply bg-transparent; + } + + * { + scrollbar-width: thin; + scrollbar-color: oklch(0.556 0 0) oklch(0.97 0 0); + } + + .dark * { + scrollbar-color: oklch(0.708 0 0) oklch(0.269 0 0); + } } \ No newline at end of file diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index 1dfb7ab..8bd0fb7 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -93,7 +93,8 @@ "select_template": "Select a template...", "api_key_required": "API Key is required", "name_required": "Name is required", - "name_duplicate": "A provider with this name already exists" + "name_duplicate": "A provider with this name already exists", + "search": "Search providers..." }, "router": { @@ -128,9 +129,17 @@ "module_text": "Text", "module_color": "Color", "module_background": "Background", - "add_module": "Add Module", + "module_text_description": "Enter display text, variables can be used:", + "module_color_description": "Select text color", + "module_background_description": "Select background color (optional)", + "module_script_path": "Script Path", + "module_script_path_description": "Enter the absolute path of the Node.js script file", + "add_module": "Add Module", "remove_module": "Remove Module", + "delete_module": "Delete Module", "preview": "Preview", + "components": "Components", + "properties": "Properties", "workDir": "Working Directory", "gitBranch": "Git Branch", "model": "Model", @@ -153,6 +162,16 @@ "color_bright_magenta": "Bright Magenta", "color_bright_cyan": "Bright Cyan", "color_bright_white": "Bright White", + "font_placeholder": "Select Font", + "theme_placeholder": "Select Theme Style", + "icon_placeholder": "Paste icon or search by name...", + "icon_description": "Enter icon character, paste icon, or search icons (optional)", + "text_placeholder": "e.g.: {{workDirName}}", + "script_placeholder": "e.g.: /path/to/your/script.js", + "drag_hint": "Drag components here to configure", + "select_hint": "Select a component to configure", + "no_icons_found": "No icons found", + "no_icons_available": "No icons available", "import_export": "Import/Export", "import": "Import Config", "export": "Export Config", diff --git a/ui/src/locales/zh.json b/ui/src/locales/zh.json index bfc5532..2709b85 100644 --- a/ui/src/locales/zh.json +++ b/ui/src/locales/zh.json @@ -93,7 +93,8 @@ "select_template": "选择一个模板...", "api_key_required": "API 密钥为必填项", "name_required": "名称为必填项", - "name_duplicate": "已存在同名供应商" + "name_duplicate": "已存在同名供应商", + "search": "搜索供应商..." }, "router": { @@ -128,9 +129,17 @@ "module_text": "文本", "module_color": "颜色", "module_background": "背景", - "add_module": "添加模块", + "module_text_description": "输入显示文本,可使用变量:", + "module_color_description": "选择文字颜色", + "module_background_description": "选择背景颜色(可选)", + "module_script_path": "脚本路径", + "module_script_path_description": "输入Node.js脚本文件的绝对路径", + "add_module": "添加模块", "remove_module": "移除模块", + "delete_module": "删除组件", "preview": "预览", + "components": "组件", + "properties": "属性", "workDir": "工作目录", "gitBranch": "Git分支", "model": "模型", @@ -153,6 +162,16 @@ "color_bright_magenta": "亮品红", "color_bright_cyan": "亮青色", "color_bright_white": "亮白色", + "font_placeholder": "选择字体", + "theme_placeholder": "选择主题样式", + "icon_placeholder": "粘贴图标或输入名称搜索...", + "icon_description": "输入图标字符、粘贴图标或搜索图标(可选)", + "text_placeholder": "例如: {{workDirName}}", + "script_placeholder": "例如: /path/to/your/script.js", + "drag_hint": "拖拽组件到此处进行配置", + "select_hint": "选择一个组件进行配置", + "no_icons_found": "未找到图标", + "no_icons_available": "暂无可用图标", "import_export": "导入/导出", "import": "导入配置", "export": "导出配置",