UNPKG

aico-claude-code

Version:

Claude Code CLI 的网页用户界面

223 lines (190 loc) 6.29 kB
#!/usr/bin/env node /** * CLI entry point for Claude Code UI * * Provides command-line interface for running the Claude Code UI server * with various configuration options. */ import { spawn } from 'child_process'; import { readFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { parseCliArgs } from './args.js'; import { checkRequirements, buildApplication, initConfig, showSystemInfo, showExtendedHelp } from './commands.js'; // Try to import open, fallback gracefully if not available let open; try { open = (await import('open')).default; } catch { open = null; } const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Parse CLI arguments const args = parseCliArgs(); // Set environment variables based on CLI arguments process.env.PORT = args.port.toString(); process.env.VITE_PORT = args.vitePort.toString(); process.env.HOST = args.host; process.env.DEBUG = args.debug.toString(); if (args.claudePath) { process.env.CLAUDE_PATH = args.claudePath; } if (args.logLevel) { process.env.LOG_LEVEL = args.logLevel; } // Check if we're running from global installation function isGlobalInstallation() { const globalPath = process.execPath; const cliPath = __dirname; return globalPath.includes('node_modules') || cliPath.includes('.nvm'); } // Ensure build exists for production mode async function ensureBuildForProduction() { const distPath = join(__dirname, '../dist'); const serverPath = join(__dirname, '../server/index.js'); if (!existsSync(distPath)) { console.log('🔍 Production build not found, building...'); try { await buildApplication(); } catch (error) { console.error('❌ Failed to build for production:', error.message); console.log('💡 Try running: npm run build'); process.exit(1); } } if (!existsSync(serverPath)) { console.error('❌ Server file not found:', serverPath); process.exit(1); } } // Determine which server to run let serverPath; let serverArgs = []; if (args.prodMode) { // Production mode - ensure build exists ensureBuildForProduction().then(() => { serverPath = join(__dirname, '../server/index.js'); startServer(); }); } else if (args.devMode) { // Development mode - check if npm scripts are available const packageJsonPath = join(__dirname, '../package.json'); if (existsSync(packageJsonPath)) { console.log('🚀 Starting Claude Code UI in development mode...'); console.log(`📡 Backend: http://localhost:${args.port}`); console.log(`🎨 Frontend: http://localhost:${args.vitePort}`); serverPath = 'npm'; serverArgs = ['run', 'dev']; startServer(); } else { console.error('❌ Development mode requires package.json in the same directory'); process.exit(1); } } else { // Default mode - run backend server only serverPath = join(__dirname, '../server/index.js'); startServer(); } // Start the server function startServer() { console.log(`🚀 Starting Claude Code UI...`); console.log(`📡 Server: http://${args.host}:${args.port}`); if (!args.prodMode) { console.log(`🎨 Frontend: http://${args.host}:${args.vitePort}`); } console.log(`📝 Log level: ${args.logLevel}`); if (args.debug) { console.log(`🐛 Debug mode enabled`); } const serverProcess = spawn('node', [serverPath, ...(serverArgs || [])], { stdio: 'inherit', env: { ...process.env }, cwd: dirname(__dirname) }); // Handle process events serverProcess.on('spawn', () => { console.log('✅ Claude Code UI server started successfully'); // Open browser if requested if (args.openBrowser) { setTimeout(() => { const url = `http://${args.host}:${args.vitePort || args.port}`; console.log(`🌐 Opening browser at ${url}`); if (typeof open === 'function') { open(url).catch(err => { console.warn(`Could not open browser: ${err.message}`); }); } else { console.warn('Could not open browser: open package not available'); console.warn('Please install the "open" package with: npm install open'); } }, 2000); } }); serverProcess.on('error', (error) => { console.error('❌ Failed to start server:', error.message); if (error.code === 'ENOENT') { console.log('💡 Make sure npm is installed and in your PATH'); } process.exit(1); }); serverProcess.on('exit', (code) => { if (code !== 0) { console.error(`❌ Server exited with code ${code}`); } else { console.log('👋 Claude Code UI server stopped'); } }); // Handle graceful shutdown const shutdown = (signal) => { console.log(`\n📴 Received ${signal}, shutting down gracefully...`); if (serverProcess && !serverProcess.killed) { serverProcess.kill('SIGTERM'); } process.exit(0); }; process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); return serverProcess; } // Validate Node.js version function validateNodeVersion() { const nodeVersion = process.version; const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10); if (majorVersion < 18) { console.error(`❌ Node.js 18+ is required. Current version: ${nodeVersion}`); console.error('Please upgrade Node.js to continue.'); process.exit(1); } } // Main execution function main() { validateNodeVersion(); // Handle special commands if (process.argv.includes('build')) { buildApplication().catch(console.error); return; } if (process.argv.includes('check')) { checkRequirements(); return; } if (process.argv.includes('init')) { const configPath = process.argv[process.argv.indexOf('init') + 1] || './claude-ui.config.json'; initConfig(configPath); return; } if (process.argv.includes('--info')) { showSystemInfo(); return; } // Start the server for prod/dev modes if (args.prodMode || args.devMode || (!args.prodMode && !args.devMode)) { // Server will be started by the async flow above return; } } // Run CLI if (import.meta.url === `file://${process.argv[1]}`) { main(); }