relese v1.0.34 to optimize the log
This commit is contained in:
@@ -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.)
|
||||||
|
|||||||
@@ -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 用户名进行更新。)
|
||||||
|
|
||||||
|
|||||||
@@ -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
26
pnpm-lock.yaml
generated
@@ -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
|
||||||
|
|||||||
30
src/index.ts
30
src/index.ts
@@ -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();
|
||||||
|
|||||||
@@ -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
44
src/utils/logCleanup.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user