From 7964fff175aa1f356e015548fa7591e49c891c12 Mon Sep 17 00:00:00 2001 From: musistudio Date: Tue, 9 Sep 2025 21:47:59 +0800 Subject: [PATCH] release v1.0.48 --- README.md | 6 +++- README_zh.md | 6 ++++ blog/images/roadmap.svg | 67 +++++++++++++++++++++++++++++++++++++++ package.json | 3 +- pnpm-lock.yaml | 48 ++++++++++++++++++++++++++++ src/cli.ts | 10 +++--- src/index.ts | 17 +++++----- src/utils/close.ts | 5 +-- src/utils/processCheck.ts | 21 ++++++++---- 9 files changed, 160 insertions(+), 23 deletions(-) create mode 100644 blog/images/roadmap.svg diff --git a/README.md b/README.md index 505e096..0d723bc 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ I am seeking funding support for this project to better sustain its development. ![](blog/images/claude-code.png) +![](blog/images/roadmap.svg) + ## ✨ Features - **Model Routing**: Route requests to different models based on your needs (e.g., background tasks, thinking, long context). @@ -572,6 +574,8 @@ A huge thank you to all our sponsors for their generous support! - @\*更 - @\*. - @F\*t - +- @\*政 +- @\*铭 +- @\*叶 (If your name is masked, please contact me via my homepage email to update it with your GitHub username.) diff --git a/README_zh.md b/README_zh.md index 3c31fba..43f37cf 100644 --- a/README_zh.md +++ b/README_zh.md @@ -10,6 +10,9 @@ ![](blog/images/claude-code.png) +![](blog/images/roadmap.svg) + + ## ✨ 功能 - **模型路由**: 根据您的需求将请求路由到不同的模型(例如,后台任务、思考、长上下文)。 @@ -541,6 +544,9 @@ jobs: - @\*更 - @\*. - @F\*t +- @\*政 +- @\*铭 +- @\*叶 (如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。) diff --git a/blog/images/roadmap.svg b/blog/images/roadmap.svg new file mode 100644 index 0000000..0403023 --- /dev/null +++ b/blog/images/roadmap.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + New Documentation + Clear structure, examples & best practices + + + + + + + + + + + + Plugin Marketplace + Community submissions, ratings & version constraints + + + + + + + + + + + + One More Thing + 🚀 Confidential project · Revealing soon + + + + + + + + + diff --git a/package.json b/package.json index 653b565..ca2a631 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@musistudio/claude-code-router", - "version": "1.0.47", + "version": "1.0.48", "description": "Use Claude Code without an Anthropics account and route it to another LLM provider", "bin": { "ccr": "./dist/cli.js" @@ -22,6 +22,7 @@ "@fastify/static": "^8.2.0", "@musistudio/llms": "^1.0.32", "dotenv": "^16.4.7", + "find-process": "^2.0.0", "json5": "^2.2.3", "openurl": "^1.1.1", "rotating-file-stream": "^3.2.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25d739b..1792678 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: dotenv: specifier: ^16.4.7 version: 16.6.1 + find-process: + specifier: ^2.0.0 + version: 2.0.0 json5: specifier: ^2.2.3 version: 2.2.3 @@ -338,6 +341,10 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -345,6 +352,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -460,6 +471,10 @@ packages: resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} engines: {node: '>=20'} + find-process@2.0.0: + resolution: {integrity: sha512-YUBQnteWGASJoEVVsOXy6XtKAY2O1FCsWnnvQ8y0YwgY1rZiKeVptnFvMu6RSELZAJOGklqseTnUGGs5D0bKmg==} + hasBin: true + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -524,6 +539,10 @@ packages: resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} engines: {node: '>=18'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -609,6 +628,10 @@ packages: light-my-request@6.6.0: resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + lru-cache@11.1.0: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} @@ -865,6 +888,10 @@ packages: resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -1188,12 +1215,19 @@ snapshots: buffer-equal-constant-time@1.0.1: {} + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + commander@12.1.0: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -1351,6 +1385,12 @@ snapshots: fast-querystring: 1.1.2 safe-regex2: 5.0.0 + find-process@2.0.0: + dependencies: + chalk: 4.1.2 + commander: 12.1.0 + loglevel: 1.9.2 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -1458,6 +1498,8 @@ snapshots: transitivePeerDependencies: - supports-color + has-flag@4.0.0: {} + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -1538,6 +1580,8 @@ snapshots: process-warning: 4.0.1 set-cookie-parser: 2.7.1 + loglevel@1.9.2: {} + lru-cache@11.1.0: {} merge2@1.4.1: {} @@ -1741,6 +1785,10 @@ snapshots: strip-eof@1.0.0: {} + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} thread-stream@3.1.0: diff --git a/src/cli.ts b/src/cli.ts index b8c0f9c..7e9692f 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -45,7 +45,8 @@ async function waitForService( const startTime = Date.now(); while (Date.now() - startTime < timeout) { - if (isServiceRunning()) { + const isRunning = await isServiceRunning() + if (isRunning) { // Wait for an additional short period to ensure service is fully ready await new Promise((resolve) => setTimeout(resolve, 500)); return true; @@ -56,6 +57,7 @@ async function waitForService( } async function main() { + const isRunning = await isServiceRunning() switch (command) { case "start": run(); @@ -95,7 +97,7 @@ async function main() { inputData += chunk; } }); - + process.stdin.on("end", async () => { try { const input: StatusLineInput = JSON.parse(inputData); @@ -108,7 +110,7 @@ async function main() { }); break; case "code": - if (!isServiceRunning()) { + if (!isRunning) { console.log("Service not running, starting service..."); const cliPath = join(__dirname, "cli.js"); const startProcess = spawn("node", [cliPath, "start"], { @@ -153,7 +155,7 @@ async function main() { break; case "ui": // Check if service is running - if (!isServiceRunning()) { + if (!isRunning) { console.log("Service not running, starting service..."); const cliPath = join(__dirname, "cli.js"); const startProcess = spawn("node", [cliPath, "start"], { diff --git a/src/index.ts b/src/index.ts index e2adb51..c10534a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,7 +51,8 @@ interface RunOptions { async function run(options: RunOptions = {}) { // Check if service is already running - if (isServiceRunning()) { + const isRunning = await isServiceRunning() + if (isRunning) { console.log("✅ Service is already running in the background."); return; } @@ -244,7 +245,6 @@ async function run(options: RunOptions = {}) { req, config }); - console.log('result', toolResult) toolMessages.push({ "tool_use_id": currentToolId, "type": "tool_result", @@ -295,14 +295,12 @@ async function run(options: RunOptions = {}) { // 检查流是否仍然可写 if (!controller.desiredSize) { - console.log('Stream backpressure detected'); break; } controller.enqueue(value) }catch (readError: any) { if (readError.name === 'AbortError' || readError.code === 'ERR_STREAM_PREMATURE_CLOSE') { - console.log('Stream reading aborted due to client disconnect'); abortController.abort(); // 中止所有相关操作 break; } @@ -318,7 +316,6 @@ async function run(options: RunOptions = {}) { // 处理流提前关闭的错误 if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') { - console.log('Stream prematurely closed, aborting operations'); abortController.abort(); return undefined; } @@ -349,7 +346,7 @@ async function run(options: RunOptions = {}) { } } catch (readError: any) { if (readError.name === 'AbortError' || readError.code === 'ERR_STREAM_PREMATURE_CLOSE') { - console.log('Background read stream closed prematurely'); + console.error('Background read stream closed prematurely'); } else { console.error('Error in background stream reading:', readError); } @@ -362,13 +359,15 @@ async function run(options: RunOptions = {}) { } sessionUsageCache.put(req.sessionId, payload.usage); } - if (typeof payload ==='object' && payload.error) { - return done(payload.error, null) + if (typeof payload ==='object') { + if (payload.error) { + return done(payload.error, null) + } + return done(payload) } done(null, payload) }); server.addHook("onSend", async (req, reply, payload) => { - console.log('主应用onSend') event.emit('onSend', req, reply, payload); return payload; }) diff --git a/src/utils/close.ts b/src/utils/close.ts index 6c1f973..27de6ab 100644 --- a/src/utils/close.ts +++ b/src/utils/close.ts @@ -5,8 +5,9 @@ import { join } from 'path'; export async function closeService() { const PID_FILE = join(HOME_DIR, '.claude-code-router.pid'); - - if (!isServiceRunning()) { + const isRunning = await isServiceRunning() + + if (!isRunning) { console.log("No service is currently running."); return; } diff --git a/src/utils/processCheck.ts b/src/utils/processCheck.ts index 16d864f..1c9eed3 100644 --- a/src/utils/processCheck.ts +++ b/src/utils/processCheck.ts @@ -1,6 +1,16 @@ import { existsSync, readFileSync, writeFileSync } from 'fs'; import { PID_FILE, REFERENCE_COUNT_FILE } from '../constants'; import { readConfigFile } from '.'; +import find from 'find-process'; + +export async function isProcessRunning(pid: number): Promise { + try { + const processes = await find('pid', pid); + return processes.length > 0; + } catch (error) { + return false; + } +} export function incrementReferenceCount() { let count = 0; @@ -27,15 +37,14 @@ export function getReferenceCount(): number { return parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0; } -export function isServiceRunning(): boolean { +export async function isServiceRunning(): Promise { if (!existsSync(PID_FILE)) { return false; } try { const pid = parseInt(readFileSync(PID_FILE, 'utf-8')); - process.kill(pid, 0); - return true; + return await isProcessRunning(pid); } catch (e) { // Process not running, clean up pid file cleanupPidFile(); @@ -62,7 +71,7 @@ export function getServicePid(): number | null { if (!existsSync(PID_FILE)) { return null; } - + try { const pid = parseInt(readFileSync(PID_FILE, 'utf-8')); return isNaN(pid) ? null : pid; @@ -73,10 +82,10 @@ export function getServicePid(): number | null { export async function getServiceInfo() { const pid = getServicePid(); - const running = isServiceRunning(); + const running = await isServiceRunning(); const config = await readConfigFile(); const port = config.PORT || 3456; - + return { running, pid,