Files
claude-code-router/src/cli.ts
T
musistudioandClaude 112d7ef8f9 feat: add UI build to build process
- Created separate build script to handle both CLI and UI building
- Added automatic UI dependency installation
- Copy built UI artifacts to dist directory

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-30 11:15:05 +08:00

227 lines
6.4 KiB
JavaScript

#!/usr/bin/env node
import { run } from "./index";
import { showStatus } from "./utils/status";
import { executeCodeCommand } from "./utils/codeCommand";
import { cleanupPidFile, isServiceRunning, getServiceInfo } from "./utils/processCheck";
import { version } from "../package.json";
import { spawn, exec } from "child_process";
import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
import fs, { existsSync, readFileSync } from "fs";
import {join} from "path";
const command = process.argv[2];
const HELP_TEXT = `
Usage: ccr [command]
Commands:
start Start server
stop Stop server
restart Restart server
status Show server status
code Execute claude command
ui Open the web UI in browser
-v, version Show version information
-h, help Show help information
Example:
ccr start
ccr code "Write a Hello World"
ccr ui
`;
async function waitForService(
timeout = 10000,
initialDelay = 1000
): Promise<boolean> {
// Wait for an initial period to let the service initialize
await new Promise((resolve) => setTimeout(resolve, initialDelay));
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (isServiceRunning()) {
// Wait for an additional short period to ensure service is fully ready
await new Promise((resolve) => setTimeout(resolve, 500));
return true;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
return false;
}
async function main() {
switch (command) {
case "start":
run();
break;
case "stop":
try {
const pid = parseInt(readFileSync(PID_FILE, "utf-8"));
process.kill(pid);
cleanupPidFile();
if (existsSync(REFERENCE_COUNT_FILE)) {
try {
fs.unlinkSync(REFERENCE_COUNT_FILE);
} catch (e) {
// Ignore cleanup errors
}
}
console.log(
"claude code router service has been successfully stopped."
);
} catch (e) {
console.log(
"Failed to stop the service. It may have already been stopped."
);
cleanupPidFile();
}
break;
case "status":
await showStatus();
break;
case "code":
if (!isServiceRunning()) {
console.log("Service not running, starting service...");
const cliPath = join(__dirname, "cli.js");
const startProcess = spawn("node", [cliPath, "start"], {
detached: true,
stdio: "ignore",
});
// let errorMessage = "";
// startProcess.stderr?.on("data", (data) => {
// errorMessage += data.toString();
// });
startProcess.on("error", (error) => {
console.error("Failed to start service:", error.message);
process.exit(1);
});
// startProcess.on("close", (code) => {
// if (code !== 0 && errorMessage) {
// console.error("Failed to start service:", errorMessage.trim());
// process.exit(1);
// }
// });
startProcess.unref();
if (await waitForService()) {
executeCodeCommand(process.argv.slice(3));
} else {
console.error(
"Service startup timeout, please manually run `ccr start` to start the service"
);
process.exit(1);
}
} else {
executeCodeCommand(process.argv.slice(3));
}
break;
case "ui":
// Check if service is running
if (!isServiceRunning()) {
console.log("Service not running, starting service...");
const cliPath = join(__dirname, "cli.js");
const startProcess = spawn("node", [cliPath, "start"], {
detached: true,
stdio: "ignore",
});
startProcess.on("error", (error) => {
console.error("Failed to start service:", error.message);
process.exit(1);
});
startProcess.unref();
if (!(await waitForService())) {
console.error(
"Service startup timeout, please manually run `ccr start` to start the service"
);
process.exit(1);
}
}
// Get service info and open UI
const serviceInfo = await getServiceInfo();
const uiUrl = `${serviceInfo.endpoint}/ui/`;
console.log(`Opening UI at ${uiUrl}`);
// Open URL in browser based on platform
const platform = process.platform;
let openCommand = "";
if (platform === "win32") {
// Windows
openCommand = `start ${uiUrl}`;
} else if (platform === "darwin") {
// macOS
openCommand = `open ${uiUrl}`;
} else if (platform === "linux") {
// Linux
openCommand = `xdg-open ${uiUrl}`;
} else {
console.error("Unsupported platform for opening browser");
process.exit(1);
}
exec(openCommand, (error) => {
if (error) {
console.error("Failed to open browser:", error.message);
process.exit(1);
}
});
break;
case "-v":
case "version":
console.log(`claude-code-router version: ${version}`);
break;
case "restart":
// Stop the service if it's running
try {
const pid = parseInt(readFileSync(PID_FILE, "utf-8"));
process.kill(pid);
cleanupPidFile();
if (existsSync(REFERENCE_COUNT_FILE)) {
try {
fs.unlinkSync(REFERENCE_COUNT_FILE);
} catch (e) {
// Ignore cleanup errors
}
}
console.log("claude code router service has been stopped.");
} catch (e) {
console.log("Service was not running or failed to stop.");
cleanupPidFile();
}
// Start the service again in the background
console.log("Starting claude code router service...");
const cliPath = join(__dirname, "cli.js");
const startProcess = spawn("node", [cliPath, "start"], {
detached: true,
stdio: "ignore",
});
startProcess.on("error", (error) => {
console.error("Failed to start service:", error);
process.exit(1);
});
startProcess.unref();
console.log("✅ Service started successfully in the background.");
break;
case "-h":
case "help":
console.log(HELP_TEXT);
break;
default:
console.log(HELP_TEXT);
process.exit(1);
}
}
main().catch(console.error);