relese v1.0.34 to optimize the log

This commit is contained in:
musistudio
2025-08-10 21:37:48 +08:00
parent 6510d3aac9
commit b856e1e11b
7 changed files with 106 additions and 15 deletions

View File

@@ -290,6 +290,7 @@ Transformers allow you to modify the request and response payloads to ensure com
- `enhancetool`: Adds a layer of error tolerance to the tool call parameters returned by the LLM (this will cause the tool call information to no longer be streamed). - `enhancetool`: Adds a layer of error tolerance to the tool call parameters returned by the LLM (this will cause the tool call information to no longer be streamed).
- `cleancache`: Clears the `cache_control` field from requests. - `cleancache`: Clears the `cache_control` field from requests.
- `vertex-gemini`: Handles the Gemini API using Vertex authentication. - `vertex-gemini`: Handles the Gemini API using Vertex authentication.
- `qwen-cli` (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b).
**Custom Transformers:** **Custom Transformers:**
@@ -506,5 +507,10 @@ A huge thank you to all our sponsors for their generous support!
- @\*亿 - @\*亿
- @\*辉 - @\*辉
- @JACK - @JACK
- @\*光
- @W\*l
- [@kesku](https://github.com/kesku)
- @水\*丫
- @二吉吉
(If your name is masked, please contact me via my homepage email to update it with your GitHub username.) (If your name is masked, please contact me via my homepage email to update it with your GitHub username.)

View File

@@ -284,6 +284,7 @@ Transformers 允许您修改请求和响应负载,以确保与不同提供商
- `enhancetool`: 对 LLM 返回的工具调用参数增加一层容错处理(这会导致不再流式返回工具调用信息)。 - `enhancetool`: 对 LLM 返回的工具调用参数增加一层容错处理(这会导致不再流式返回工具调用信息)。
- `cleancache`: 清除请求中的 `cache_control` 字段。 - `cleancache`: 清除请求中的 `cache_control` 字段。
- `vertex-gemini`: 处理使用 vertex 鉴权的 gemini api。 - `vertex-gemini`: 处理使用 vertex 鉴权的 gemini api。
- `qwen-cli` (实验性): 通过 Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b) 对 qwen3-coder-plus 的非官方支持。
**自定义 Transformer:** **自定义 Transformer:**
@@ -497,6 +498,11 @@ jobs:
- @\*亿 - @\*亿
- @\*辉 - @\*辉
- @JACK - @JACK
- @\*光
- @W\*l
- [@kesku](https://github.com/kesku)
- @水\*丫
- @二吉吉
(如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。) (如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。)

View File

@@ -1,6 +1,6 @@
{ {
"name": "@musistudio/claude-code-router", "name": "@musistudio/claude-code-router",
"version": "1.0.33", "version": "1.0.34",
"description": "Use Claude Code without an Anthropics account and route it to another LLM provider", "description": "Use Claude Code without an Anthropics account and route it to another LLM provider",
"bin": { "bin": {
"ccr": "./dist/cli.js" "ccr": "./dist/cli.js"
@@ -20,10 +20,11 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/static": "^8.2.0", "@fastify/static": "^8.2.0",
"@musistudio/llms": "v1.0.19", "@musistudio/llms": "^1.0.21",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"json5": "^2.2.3", "json5": "^2.2.3",
"openurl": "^1.1.1", "openurl": "^1.1.1",
"pino-rotating-file-stream": "^0.0.2",
"tiktoken": "^1.0.21", "tiktoken": "^1.0.21",
"uuid": "^11.1.0" "uuid": "^11.1.0"
}, },

26
pnpm-lock.yaml generated
View File

@@ -12,8 +12,8 @@ importers:
specifier: ^8.2.0 specifier: ^8.2.0
version: 8.2.0 version: 8.2.0
'@musistudio/llms': '@musistudio/llms':
specifier: v1.0.19 specifier: ^1.0.21
version: 1.0.19(ws@8.18.3)(zod@3.25.67) version: 1.0.21(ws@8.18.3)(zod@3.25.67)
dotenv: dotenv:
specifier: ^16.4.7 specifier: ^16.4.7
version: 16.6.1 version: 16.6.1
@@ -23,6 +23,9 @@ importers:
openurl: openurl:
specifier: ^1.1.1 specifier: ^1.1.1
version: 1.1.1 version: 1.1.1
pino-rotating-file-stream:
specifier: ^0.0.2
version: 0.0.2
tiktoken: tiktoken:
specifier: ^1.0.21 specifier: ^1.0.21
version: 1.0.21 version: 1.0.21
@@ -257,8 +260,8 @@ packages:
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
engines: {node: '>=8'} engines: {node: '>=8'}
'@musistudio/llms@1.0.19': '@musistudio/llms@1.0.21':
resolution: {integrity: sha512-+U29ZxqXUQJBur5kE9d3e0mC19H0uetwxYvMpWCF4lBtXb2syBPIop2KeolBP+5/vSUz8M45HFd8yKFfVDEO3A==} resolution: {integrity: sha512-oRSs9U0o13HCNaw+fesLnJv75t+AUSKn37LxJOlO1yNWYucjfB76vo3y3rK47wztxv2rmSumrlbu2FzjRG4YuQ==}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
@@ -707,6 +710,9 @@ packages:
pino-abstract-transport@2.0.0: pino-abstract-transport@2.0.0:
resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
pino-rotating-file-stream@0.0.2:
resolution: {integrity: sha512-knF+ReDBMQMB7gzBfuFpUmCrXpRen6YYh5Q9Ymmj//dDHeH4QEMwAV7VoGEEM+30s7VHqfbabazs9wxkMO2BIQ==}
pino-std-serializers@7.0.0: pino-std-serializers@7.0.0:
resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
@@ -757,6 +763,10 @@ packages:
rfdc@1.4.1: rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
rotating-file-stream@3.2.6:
resolution: {integrity: sha512-r8yShzMWUvWXkRzbOXDM1fEaMpc3qo2PzK7bBH/0p0Nl/uz8Mud/Y+0XTQxe3kbSnDF7qBH2tSe83WDKA7o3ww==}
engines: {node: '>=14.0'}
run-parallel@1.2.0: run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -1102,7 +1112,7 @@ snapshots:
'@lukeed/ms@2.0.2': {} '@lukeed/ms@2.0.2': {}
'@musistudio/llms@1.0.19(ws@8.18.3)(zod@3.25.67)': '@musistudio/llms@1.0.21(ws@8.18.3)(zod@3.25.67)':
dependencies: dependencies:
'@anthropic-ai/sdk': 0.54.0 '@anthropic-ai/sdk': 0.54.0
'@fastify/cors': 11.0.1 '@fastify/cors': 11.0.1
@@ -1604,6 +1614,10 @@ snapshots:
dependencies: dependencies:
split2: 4.2.0 split2: 4.2.0
pino-rotating-file-stream@0.0.2:
dependencies:
rotating-file-stream: 3.2.6
pino-std-serializers@7.0.0: {} pino-std-serializers@7.0.0: {}
pino@9.7.0: pino@9.7.0:
@@ -1653,6 +1667,8 @@ snapshots:
rfdc@1.4.1: {} rfdc@1.4.1: {}
rotating-file-stream@3.2.6: {}
run-parallel@1.2.0: run-parallel@1.2.0:
dependencies: dependencies:
queue-microtask: 1.2.3 queue-microtask: 1.2.3

View File

@@ -1,8 +1,8 @@
import { existsSync } from "fs"; import { existsSync } from "fs";
import { writeFile } from "fs/promises"; import { writeFile } from "fs/promises";
import { homedir } from "os"; import { homedir } from "os";
import { join } from "path"; import path, { join } from "path";
import { initConfig, initDir } from "./utils"; import { initConfig, initDir, cleanupLogFiles } from "./utils";
import { createServer } from "./server"; import { createServer } from "./server";
import { router } from "./utils/router"; import { router } from "./utils/router";
import { apiKeyAuth } from "./middleware/auth"; import { apiKeyAuth } from "./middleware/auth";
@@ -12,6 +12,8 @@ import {
savePid, savePid,
} from "./utils/processCheck"; } from "./utils/processCheck";
import { CONFIG_FILE } from "./constants"; import { CONFIG_FILE } from "./constants";
import createWriteStream from "pino-rotating-file-stream";
import { HOME_DIR } from "./constants";
async function initializeClaudeConfig() { async function initializeClaudeConfig() {
const homeDir = homedir(); const homeDir = homedir();
@@ -46,14 +48,14 @@ async function run(options: RunOptions = {}) {
await initializeClaudeConfig(); await initializeClaudeConfig();
await initDir(); await initDir();
// Clean up old log files, keeping only the 10 most recent ones
await cleanupLogFiles();
const config = await initConfig(); const config = await initConfig();
let HOST = config.HOST; let HOST = config.HOST;
if (config.HOST && !config.APIKEY) { if (config.HOST && !config.APIKEY) {
HOST = "127.0.0.1"; HOST = "127.0.0.1";
console.warn( console.warn("⚠️ API key is not set. HOST is forced to 127.0.0.1.");
"⚠️ API key is not set. HOST is forced to 127.0.0.1."
);
} }
const port = config.PORT || 3456; const port = config.PORT || 3456;
@@ -73,12 +75,15 @@ async function run(options: RunOptions = {}) {
cleanupPidFile(); cleanupPidFile();
process.exit(0); process.exit(0);
}); });
console.log(HOST) console.log(HOST);
// Use port from environment variable if set (for background process) // Use port from environment variable if set (for background process)
const servicePort = process.env.SERVICE_PORT const servicePort = process.env.SERVICE_PORT
? parseInt(process.env.SERVICE_PORT) ? parseInt(process.env.SERVICE_PORT)
: port; : port;
const startTime = new Date().toISOString();
const server = createServer({ const server = createServer({
jsonPath: CONFIG_FILE, jsonPath: CONFIG_FILE,
initialConfig: { initialConfig: {
@@ -92,6 +97,15 @@ async function run(options: RunOptions = {}) {
"claude-code-router.log" "claude-code-router.log"
), ),
}, },
logger: {
level: "debug",
stream: createWriteStream({
path: HOME_DIR,
filename: `./logs/ccr-${startTime}.log`,
maxFiles: 3,
interval: "1d",
}),
},
}); });
// Add async preHandler hook for authentication // Add async preHandler hook for authentication
server.addHook("preHandler", async (req, reply) => { server.addHook("preHandler", async (req, reply) => {
@@ -105,8 +119,8 @@ async function run(options: RunOptions = {}) {
}); });
}); });
server.addHook("preHandler", async (req, reply) => { server.addHook("preHandler", async (req, reply) => {
if(req.url.startsWith("/v1/messages")) { if (req.url.startsWith("/v1/messages")) {
router(req, reply, config) router(req, reply, config);
} }
}); });
server.start(); server.start();

View File

@@ -9,6 +9,7 @@ import {
PLUGINS_DIR, PLUGINS_DIR,
} from "../constants"; } from "../constants";
import { getSystemUUID, generateTempAPIKey, getTempAPIKey } from "./systemUUID"; import { getSystemUUID, generateTempAPIKey, getTempAPIKey } from "./systemUUID";
import { cleanupLogFiles } from "./logCleanup";
const ensureDir = async (dir_path: string) => { const ensureDir = async (dir_path: string) => {
try { try {
@@ -139,3 +140,6 @@ export const initConfig = async () => {
// 导出系统UUID相关函数 // 导出系统UUID相关函数
export { getSystemUUID, generateTempAPIKey, getTempAPIKey }; export { getSystemUUID, generateTempAPIKey, getTempAPIKey };
// 导出日志清理函数
export { cleanupLogFiles };

44
src/utils/logCleanup.ts Normal file
View File

@@ -0,0 +1,44 @@
import fs from "node:fs/promises";
import path from "node:path";
import { HOME_DIR } from "../constants";
/**
* Cleans up old log files, keeping only the most recent ones
* @param maxFiles - Maximum number of log files to keep (default: 9)
*/
export async function cleanupLogFiles(maxFiles: number = 9): Promise<void> {
try {
const logsDir = path.join(HOME_DIR, "logs");
// Check if logs directory exists
try {
await fs.access(logsDir);
} catch {
// Logs directory doesn't exist, nothing to clean up
return;
}
// Read all files in the logs directory
const files = await fs.readdir(logsDir);
// Filter for log files (files starting with 'ccr-' and ending with '.log')
const logFiles = files
.filter(file => file.startsWith('ccr-') && file.endsWith('.log'))
.sort()
.reverse(); // Sort in descending order (newest first)
// Delete files exceeding the maxFiles limit
if (logFiles.length > maxFiles) {
for (let i = maxFiles; i < logFiles.length; i++) {
const filePath = path.join(logsDir, logFiles[i]);
try {
await fs.unlink(filePath);
} catch (error) {
console.warn(`Failed to delete log file ${filePath}:`, error);
}
}
}
} catch (error) {
console.warn("Failed to clean up log files:", error);
}
}