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).
- `cleancache`: Clears the `cache_control` field from requests.
- `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:**
@@ -506,5 +507,10 @@ A huge thank you to all our sponsors for their generous support!
- @\*亿
- @\*辉
- @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.)

View File

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

View File

@@ -1,6 +1,6 @@
{
"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",
"bin": {
"ccr": "./dist/cli.js"
@@ -20,10 +20,11 @@
"license": "MIT",
"dependencies": {
"@fastify/static": "^8.2.0",
"@musistudio/llms": "v1.0.19",
"@musistudio/llms": "^1.0.21",
"dotenv": "^16.4.7",
"json5": "^2.2.3",
"openurl": "^1.1.1",
"pino-rotating-file-stream": "^0.0.2",
"tiktoken": "^1.0.21",
"uuid": "^11.1.0"
},

26
pnpm-lock.yaml generated
View File

@@ -12,8 +12,8 @@ importers:
specifier: ^8.2.0
version: 8.2.0
'@musistudio/llms':
specifier: v1.0.19
version: 1.0.19(ws@8.18.3)(zod@3.25.67)
specifier: ^1.0.21
version: 1.0.21(ws@8.18.3)(zod@3.25.67)
dotenv:
specifier: ^16.4.7
version: 16.6.1
@@ -23,6 +23,9 @@ importers:
openurl:
specifier: ^1.1.1
version: 1.1.1
pino-rotating-file-stream:
specifier: ^0.0.2
version: 0.0.2
tiktoken:
specifier: ^1.0.21
version: 1.0.21
@@ -257,8 +260,8 @@ packages:
resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==}
engines: {node: '>=8'}
'@musistudio/llms@1.0.19':
resolution: {integrity: sha512-+U29ZxqXUQJBur5kE9d3e0mC19H0uetwxYvMpWCF4lBtXb2syBPIop2KeolBP+5/vSUz8M45HFd8yKFfVDEO3A==}
'@musistudio/llms@1.0.21':
resolution: {integrity: sha512-oRSs9U0o13HCNaw+fesLnJv75t+AUSKn37LxJOlO1yNWYucjfB76vo3y3rK47wztxv2rmSumrlbu2FzjRG4YuQ==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
@@ -707,6 +710,9 @@ packages:
pino-abstract-transport@2.0.0:
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:
resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==}
@@ -757,6 +763,10 @@ packages:
rfdc@1.4.1:
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:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -1102,7 +1112,7 @@ snapshots:
'@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:
'@anthropic-ai/sdk': 0.54.0
'@fastify/cors': 11.0.1
@@ -1604,6 +1614,10 @@ snapshots:
dependencies:
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@9.7.0:
@@ -1653,6 +1667,8 @@ snapshots:
rfdc@1.4.1: {}
rotating-file-stream@3.2.6: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3

View File

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

View File

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