const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); const args = process.argv.slice(2); let port = 9997; let debugPort = null; for (let i = 0; i < args.length; i++) { if (args[i] === '--port' && i + 1 < args.length) { port = parseInt(args[i + 1], 10); } else if (args[i] === '--debug-port' && i + 1 < args.length) { debugPort = args[i + 1]; } } if (isNaN(port)) { console.error('Invalid port number:', args[i + 1]); process.exit(1); } const env = { ...process.env, PORT: port.toString() }; const serverPath = path.join(__dirname, '../src/backend/server.js'); const child = spawn(process.execPath, [serverPath], { env, stdio: 'inherit', detached: false }); child.on('error', (err) => { console.error('Failed to start server:', err); process.exit(1); }); child.on('exit', (code, signal) => { process.exit(code || 0); }); process.on('SIGINT', () => { child.kill('SIGINT'); process.exit(0); }); process.on('SIGTERM', () => { child.kill('SIGTERM'); process.exit(0); }); const terminals = new Map(); wss.on('connection', (ws, req) => { const urlParams = new URLSearchParams(req.url.split('?')[1]); const shell = urlParams.get('shell') || SHELL; console.log(`[${new Date().toISOString()}] New terminal connection: shell=${shell}`); let ptyProcess; try { ptyProcess = pty.spawn(shell, [], { name: 'xterm-256color', cols: 80, rows: 24, cwd: process.env.HOME || process.env.USERPROFILE || '/', env: process.env }); } catch (err) { console.error('Failed to spawn PTY:', err); ws.close(1000, 'Failed to start shell'); return; } terminals.set(ws, ptyProcess); ptyProcess.onData((data) => { if (ws.readyState === WebSocket.OPEN) { ws.send(data); } }); ptyProcess.onExit(({ exitCode, signal }) => { console.log(`[${new Date().toISOString()}] PTY exited: code=${exitCode}, signal=${signal}`); if (ws.readyState === WebSocket.OPEN) { ws.send('\r\n[Process exited]\r\n'); ws.close(); } terminals.delete(ws); }); ws.on('message', (message) => { ptyProcess.write(message); }); ws.on('close', () => { console.log(`[${new Date().toISOString()}] Terminal connection closed`); ptyProcess.kill(); terminals.delete(ws); }); ws.on('error', (err) => { console.error('WebSocket error:', err); ptyProcess.kill(); terminals.delete(ws); }); }); process.on('SIGINT', () => { console.log('\nShutting down...'); terminals.forEach((proc) => proc.kill()); process.exit(0); }); app.commandLine.appendSwitch('disable-gpu'); app.commandLine.appendSwitch('no-sandbox'); app.disableHardwareAcceleration(); app.whenReady().then(() => { server.listen(PORT, () => { console.log(` ╔═══════════════════════════════════════════════════════════╗ ║ XCTerminal (Electron Headless) ║ ╠═══════════════════════════════════════════════════════════╣ ║ Server running at: http://localhost:${PORT} ║ ║ WebSocket endpoint: ws://localhost:${PORT} ║ ║ ║ ║ Usage: electron . --port 9997 ║ ╚═══════════════════════════════════════════════════════════╝ `); }); }); app.on('window-all-closed', () => { }); process.on('exit', () => { terminals.forEach((proc) => proc.kill()); });