release v1.0.48

This commit is contained in:
musistudio
2025-09-09 21:47:59 +08:00
parent fe06b57032
commit 7964fff175
9 changed files with 160 additions and 23 deletions

View File

@@ -13,6 +13,8 @@ I am seeking funding support for this project to better sustain its development.
![](blog/images/claude-code.png) ![](blog/images/claude-code.png)
![](blog/images/roadmap.svg)
## ✨ Features ## ✨ Features
- **Model Routing**: Route requests to different models based on your needs (e.g., background tasks, thinking, long context). - **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 - @F\*t
- @\*政
- @\*铭
- @\*叶
(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

@@ -10,6 +10,9 @@
![](blog/images/claude-code.png) ![](blog/images/claude-code.png)
![](blog/images/roadmap.svg)
## ✨ 功能 ## ✨ 功能
- **模型路由**: 根据您的需求将请求路由到不同的模型(例如,后台任务、思考、长上下文)。 - **模型路由**: 根据您的需求将请求路由到不同的模型(例如,后台任务、思考、长上下文)。
@@ -541,6 +544,9 @@ jobs:
- @\*更 - @\*更
- @\*. - @\*.
- @F\*t - @F\*t
- @\*政
- @\*铭
- @\*叶
(如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。) (如果您的名字被屏蔽,请通过我的主页电子邮件与我联系,以便使用您的 GitHub 用户名进行更新。)

67
blog/images/roadmap.svg Normal file
View File

@@ -0,0 +1,67 @@
<svg viewBox="0 0 1200 420" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
.road { stroke: #7aa2ff; stroke-width: 6; fill: none; filter: drop-shadow(0 6px 18px rgba(122,162,255,0.25)); }
.dash { stroke: rgba(122,162,255,0.25); stroke-width: 6; fill: none; stroke-dasharray: 2 18; }
.node { filter: drop-shadow(0 3px 10px rgba(126,240,193,0.35)); }
.node-circle { fill: #7ef0c1; }
.node-core { fill: #181b22; stroke: white; stroke-width: 1.5; }
.label-bg { fill: rgba(24,27,34,0.8); stroke: rgba(255,255,255,0.12); rx: 12; }
.label-text { fill: #e8ecf1; font-weight: 700; font-size: 14px; font-family: Arial, sans-serif; }
.label-sub { fill: #9aa6b2; font-weight: 500; font-size: 12px; font-family: Arial, sans-serif; }
.spark { fill: none; stroke: #ffd36e; stroke-width: 1.6; stroke-linecap: round; }
</style>
</defs>
<!-- Background road with dash -->
<path class="dash" d="M60,330 C320,260 460,100 720,160 C930,205 990,260 1140,260"/>
<!-- Main road -->
<path class="road" d="M60,330 C320,260 460,100 720,160 C930,205 990,260 1140,260"/>
<!-- New Documentation Node -->
<g class="node" transform="translate(200,280)">
<circle class="node-circle" r="10"/>
<circle class="node-core" r="6"/>
</g>
<!-- New Documentation Label -->
<g transform="translate(80,120)">
<rect class="label-bg" width="260" height="92"/>
<text class="label-text" x="16" y="34">New Documentation</text>
<text class="label-sub" x="16" y="58">Clear structure, examples &amp; best practices</text>
</g>
<!-- Plugin Marketplace Node -->
<g class="node" transform="translate(640,150)">
<circle class="node-circle" r="10"/>
<circle class="node-core" r="6"/>
</g>
<!-- Plugin Marketplace Label -->
<g transform="translate(560,20)">
<rect class="label-bg" width="320" height="100"/>
<text class="label-text" x="16" y="34">Plugin Marketplace</text>
<text class="label-sub" x="16" y="58">Community submissions, ratings &amp; version constraints</text>
</g>
<!-- One More Thing Node -->
<g class="node" transform="translate(1080,255)">
<circle class="node-circle" r="10"/>
<circle class="node-core" r="6"/>
</g>
<!-- One More Thing Label -->
<g transform="translate(940,300)">
<rect class="label-bg" width="250" height="86"/>
<text class="label-text" x="16" y="34">One More Thing</text>
<text class="label-sub" x="16" y="58">🚀 Confidential project · Revealing soon</text>
</g>
<!-- Spark decorations -->
<g transform="translate(1125,290)">
<path class="spark" d="M0 0 L8 0 M4 -4 L4 4"/>
<path class="spark" d="M14 -2 L22 -2 M18 -6 L18 2"/>
<path class="spark" d="M-10 6 L-2 6 M-6 2 L-6 10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -1,6 +1,6 @@
{ {
"name": "@musistudio/claude-code-router", "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", "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"
@@ -22,6 +22,7 @@
"@fastify/static": "^8.2.0", "@fastify/static": "^8.2.0",
"@musistudio/llms": "^1.0.32", "@musistudio/llms": "^1.0.32",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"find-process": "^2.0.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"openurl": "^1.1.1", "openurl": "^1.1.1",
"rotating-file-stream": "^3.2.7", "rotating-file-stream": "^3.2.7",

48
pnpm-lock.yaml generated
View File

@@ -17,6 +17,9 @@ importers:
dotenv: dotenv:
specifier: ^16.4.7 specifier: ^16.4.7
version: 16.6.1 version: 16.6.1
find-process:
specifier: ^2.0.0
version: 2.0.0
json5: json5:
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3 version: 2.2.3
@@ -338,6 +341,10 @@ packages:
buffer-equal-constant-time@1.0.1: buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
color-convert@2.0.1: color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
@@ -345,6 +352,10 @@ packages:
color-name@1.1.4: color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 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: content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@@ -460,6 +471,10 @@ packages:
resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==} resolution: {integrity: sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==}
engines: {node: '>=20'} engines: {node: '>=20'}
find-process@2.0.0:
resolution: {integrity: sha512-YUBQnteWGASJoEVVsOXy6XtKAY2O1FCsWnnvQ8y0YwgY1rZiKeVptnFvMu6RSELZAJOGklqseTnUGGs5D0bKmg==}
hasBin: true
foreground-child@3.3.1: foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'} engines: {node: '>=14'}
@@ -524,6 +539,10 @@ packages:
resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==}
engines: {node: '>=18'} engines: {node: '>=18'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
hasown@2.0.2: hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -609,6 +628,10 @@ packages:
light-my-request@6.6.0: light-my-request@6.6.0:
resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} 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: lru-cache@11.1.0:
resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
@@ -865,6 +888,10 @@ packages:
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
engines: {node: '>=0.10.0'} 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: supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -1188,12 +1215,19 @@ snapshots:
buffer-equal-constant-time@1.0.1: {} 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: color-convert@2.0.1:
dependencies: dependencies:
color-name: 1.1.4 color-name: 1.1.4
color-name@1.1.4: {} color-name@1.1.4: {}
commander@12.1.0: {}
content-disposition@0.5.4: content-disposition@0.5.4:
dependencies: dependencies:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
@@ -1351,6 +1385,12 @@ snapshots:
fast-querystring: 1.1.2 fast-querystring: 1.1.2
safe-regex2: 5.0.0 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: foreground-child@3.3.1:
dependencies: dependencies:
cross-spawn: 7.0.6 cross-spawn: 7.0.6
@@ -1458,6 +1498,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
has-flag@4.0.0: {}
hasown@2.0.2: hasown@2.0.2:
dependencies: dependencies:
function-bind: 1.1.2 function-bind: 1.1.2
@@ -1538,6 +1580,8 @@ snapshots:
process-warning: 4.0.1 process-warning: 4.0.1
set-cookie-parser: 2.7.1 set-cookie-parser: 2.7.1
loglevel@1.9.2: {}
lru-cache@11.1.0: {} lru-cache@11.1.0: {}
merge2@1.4.1: {} merge2@1.4.1: {}
@@ -1741,6 +1785,10 @@ snapshots:
strip-eof@1.0.0: {} strip-eof@1.0.0: {}
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
thread-stream@3.1.0: thread-stream@3.1.0:

View File

@@ -45,7 +45,8 @@ async function waitForService(
const startTime = Date.now(); const startTime = Date.now();
while (Date.now() - startTime < timeout) { 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 // Wait for an additional short period to ensure service is fully ready
await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500));
return true; return true;
@@ -56,6 +57,7 @@ async function waitForService(
} }
async function main() { async function main() {
const isRunning = await isServiceRunning()
switch (command) { switch (command) {
case "start": case "start":
run(); run();
@@ -95,7 +97,7 @@ async function main() {
inputData += chunk; inputData += chunk;
} }
}); });
process.stdin.on("end", async () => { process.stdin.on("end", async () => {
try { try {
const input: StatusLineInput = JSON.parse(inputData); const input: StatusLineInput = JSON.parse(inputData);
@@ -108,7 +110,7 @@ async function main() {
}); });
break; break;
case "code": case "code":
if (!isServiceRunning()) { if (!isRunning) {
console.log("Service not running, starting service..."); console.log("Service not running, starting service...");
const cliPath = join(__dirname, "cli.js"); const cliPath = join(__dirname, "cli.js");
const startProcess = spawn("node", [cliPath, "start"], { const startProcess = spawn("node", [cliPath, "start"], {
@@ -153,7 +155,7 @@ async function main() {
break; break;
case "ui": case "ui":
// Check if service is running // Check if service is running
if (!isServiceRunning()) { if (!isRunning) {
console.log("Service not running, starting service..."); console.log("Service not running, starting service...");
const cliPath = join(__dirname, "cli.js"); const cliPath = join(__dirname, "cli.js");
const startProcess = spawn("node", [cliPath, "start"], { const startProcess = spawn("node", [cliPath, "start"], {

View File

@@ -51,7 +51,8 @@ interface RunOptions {
async function run(options: RunOptions = {}) { async function run(options: RunOptions = {}) {
// Check if service is already running // Check if service is already running
if (isServiceRunning()) { const isRunning = await isServiceRunning()
if (isRunning) {
console.log("✅ Service is already running in the background."); console.log("✅ Service is already running in the background.");
return; return;
} }
@@ -244,7 +245,6 @@ async function run(options: RunOptions = {}) {
req, req,
config config
}); });
console.log('result', toolResult)
toolMessages.push({ toolMessages.push({
"tool_use_id": currentToolId, "tool_use_id": currentToolId,
"type": "tool_result", "type": "tool_result",
@@ -295,14 +295,12 @@ async function run(options: RunOptions = {}) {
// 检查流是否仍然可写 // 检查流是否仍然可写
if (!controller.desiredSize) { if (!controller.desiredSize) {
console.log('Stream backpressure detected');
break; break;
} }
controller.enqueue(value) controller.enqueue(value)
}catch (readError: any) { }catch (readError: any) {
if (readError.name === 'AbortError' || readError.code === 'ERR_STREAM_PREMATURE_CLOSE') { if (readError.name === 'AbortError' || readError.code === 'ERR_STREAM_PREMATURE_CLOSE') {
console.log('Stream reading aborted due to client disconnect');
abortController.abort(); // 中止所有相关操作 abortController.abort(); // 中止所有相关操作
break; break;
} }
@@ -318,7 +316,6 @@ async function run(options: RunOptions = {}) {
// 处理流提前关闭的错误 // 处理流提前关闭的错误
if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') { if (error.code === 'ERR_STREAM_PREMATURE_CLOSE') {
console.log('Stream prematurely closed, aborting operations');
abortController.abort(); abortController.abort();
return undefined; return undefined;
} }
@@ -349,7 +346,7 @@ async function run(options: RunOptions = {}) {
} }
} catch (readError: any) { } catch (readError: any) {
if (readError.name === 'AbortError' || readError.code === 'ERR_STREAM_PREMATURE_CLOSE') { 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 { } else {
console.error('Error in background stream reading:', readError); console.error('Error in background stream reading:', readError);
} }
@@ -362,13 +359,15 @@ async function run(options: RunOptions = {}) {
} }
sessionUsageCache.put(req.sessionId, payload.usage); sessionUsageCache.put(req.sessionId, payload.usage);
} }
if (typeof payload ==='object' && payload.error) { if (typeof payload ==='object') {
return done(payload.error, null) if (payload.error) {
return done(payload.error, null)
}
return done(payload)
} }
done(null, payload) done(null, payload)
}); });
server.addHook("onSend", async (req, reply, payload) => { server.addHook("onSend", async (req, reply, payload) => {
console.log('主应用onSend')
event.emit('onSend', req, reply, payload); event.emit('onSend', req, reply, payload);
return payload; return payload;
}) })

View File

@@ -5,8 +5,9 @@ import { join } from 'path';
export async function closeService() { export async function closeService() {
const PID_FILE = join(HOME_DIR, '.claude-code-router.pid'); const PID_FILE = join(HOME_DIR, '.claude-code-router.pid');
const isRunning = await isServiceRunning()
if (!isServiceRunning()) {
if (!isRunning) {
console.log("No service is currently running."); console.log("No service is currently running.");
return; return;
} }

View File

@@ -1,6 +1,16 @@
import { existsSync, readFileSync, writeFileSync } from 'fs'; import { existsSync, readFileSync, writeFileSync } from 'fs';
import { PID_FILE, REFERENCE_COUNT_FILE } from '../constants'; import { PID_FILE, REFERENCE_COUNT_FILE } from '../constants';
import { readConfigFile } from '.'; import { readConfigFile } from '.';
import find from 'find-process';
export async function isProcessRunning(pid: number): Promise<boolean> {
try {
const processes = await find('pid', pid);
return processes.length > 0;
} catch (error) {
return false;
}
}
export function incrementReferenceCount() { export function incrementReferenceCount() {
let count = 0; let count = 0;
@@ -27,15 +37,14 @@ export function getReferenceCount(): number {
return parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0; return parseInt(readFileSync(REFERENCE_COUNT_FILE, 'utf-8')) || 0;
} }
export function isServiceRunning(): boolean { export async function isServiceRunning(): Promise<boolean> {
if (!existsSync(PID_FILE)) { if (!existsSync(PID_FILE)) {
return false; return false;
} }
try { try {
const pid = parseInt(readFileSync(PID_FILE, 'utf-8')); const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
process.kill(pid, 0); return await isProcessRunning(pid);
return true;
} catch (e) { } catch (e) {
// Process not running, clean up pid file // Process not running, clean up pid file
cleanupPidFile(); cleanupPidFile();
@@ -62,7 +71,7 @@ export function getServicePid(): number | null {
if (!existsSync(PID_FILE)) { if (!existsSync(PID_FILE)) {
return null; return null;
} }
try { try {
const pid = parseInt(readFileSync(PID_FILE, 'utf-8')); const pid = parseInt(readFileSync(PID_FILE, 'utf-8'));
return isNaN(pid) ? null : pid; return isNaN(pid) ? null : pid;
@@ -73,10 +82,10 @@ export function getServicePid(): number | null {
export async function getServiceInfo() { export async function getServiceInfo() {
const pid = getServicePid(); const pid = getServicePid();
const running = isServiceRunning(); const running = await isServiceRunning();
const config = await readConfigFile(); const config = await readConfigFile();
const port = config.PORT || 3456; const port = config.PORT || 3456;
return { return {
running, running,
pid, pid,