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>
This commit is contained in:
61
src/cli.ts
61
src/cli.ts
@@ -2,9 +2,9 @@
|
||||
import { run } from "./index";
|
||||
import { showStatus } from "./utils/status";
|
||||
import { executeCodeCommand } from "./utils/codeCommand";
|
||||
import { cleanupPidFile, isServiceRunning } from "./utils/processCheck";
|
||||
import { cleanupPidFile, isServiceRunning, getServiceInfo } from "./utils/processCheck";
|
||||
import { version } from "../package.json";
|
||||
import { spawn } from "child_process";
|
||||
import { spawn, exec } from "child_process";
|
||||
import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
|
||||
import fs, { existsSync, readFileSync } from "fs";
|
||||
import {join} from "path";
|
||||
@@ -20,12 +20,14 @@ Commands:
|
||||
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(
|
||||
@@ -117,6 +119,61 @@ async function main() {
|
||||
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}`);
|
||||
|
||||
@@ -94,9 +94,11 @@ async function run(options: RunOptions = {}) {
|
||||
},
|
||||
});
|
||||
server.addHook("preHandler", apiKeyAuth(config));
|
||||
server.addHook("preHandler", async (req, reply) =>
|
||||
router(req, reply, config)
|
||||
);
|
||||
server.addHook("preHandler", async (req, reply) => {
|
||||
if(req.url.startsWith("/v1/messages")) {
|
||||
router(req, reply, config)
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { FastifyRequest, FastifyReply } from "fastify";
|
||||
export const apiKeyAuth =
|
||||
(config: any) =>
|
||||
(req: FastifyRequest, reply: FastifyReply, done: () => void) => {
|
||||
if (["/", "/health"].includes(req.url)) {
|
||||
if (["/", "/health"].includes(req.url) || req.url.startsWith("/ui")) {
|
||||
return done();
|
||||
}
|
||||
const apiKey = config.APIKEY;
|
||||
|
||||
@@ -1,6 +1,47 @@
|
||||
import Server from "@musistudio/llms";
|
||||
import { readConfigFile, writeConfigFile } from "./utils";
|
||||
import { CONFIG_FILE } from "./constants";
|
||||
import { join } from "path";
|
||||
import { readFileSync } from "fs";
|
||||
import fastifyStatic from "@fastify/static";
|
||||
|
||||
export const createServer = (config: any): Server => {
|
||||
const server = new Server(config);
|
||||
|
||||
// Add endpoint to read config.json
|
||||
server.app.get("/api/config", async () => {
|
||||
return await readConfigFile();
|
||||
});
|
||||
|
||||
// Add endpoint to save config.json
|
||||
server.app.post("/api/config", async (req) => {
|
||||
const newConfig = req.body;
|
||||
await writeConfigFile(newConfig);
|
||||
return { success: true, message: "Config saved successfully" };
|
||||
});
|
||||
|
||||
// Add endpoint to restart the service
|
||||
server.app.post("/api/restart", async (_, reply) => {
|
||||
reply.send({ success: true, message: "Service restart initiated" });
|
||||
|
||||
// Restart the service after a short delay to allow response to be sent
|
||||
setTimeout(() => {
|
||||
const { spawn } = require('child_process');
|
||||
spawn('ccr', ['restart'], { detached: true, stdio: 'ignore' });
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// Register static file serving with caching
|
||||
server.app.register(fastifyStatic, {
|
||||
root: join(__dirname, "..", "dist"),
|
||||
prefix: "/ui/",
|
||||
maxAge: "1h"
|
||||
});
|
||||
|
||||
// Redirect /ui to /ui/ for proper static file serving
|
||||
server.app.get("/ui", async (_, reply) => {
|
||||
return reply.redirect("/ui/");
|
||||
});
|
||||
|
||||
return server;
|
||||
};
|
||||
|
||||
@@ -87,8 +87,7 @@ export const readConfigFile = async () => {
|
||||
|
||||
export const writeConfigFile = async (config: any) => {
|
||||
await ensureDir(HOME_DIR);
|
||||
// Add a comment to indicate JSON5 support
|
||||
const configWithComment = `// This config file supports JSON5 format (comments, trailing commas, etc.)\n${JSON5.stringify(config, null, 2)}`;
|
||||
const configWithComment = `${JSON.stringify(config, null, 2)}`;
|
||||
await fs.writeFile(CONFIG_FILE, configWithComment);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user