UNPKG

@nodedaemon/core

Version:

Production-ready Node.js process manager with zero external dependencies

427 lines (416 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandParser = void 0; const util_1 = require("util"); const helpers_1 = require("../utils/helpers"); class CommandParser { parse(argv) { if (argv.length < 3) { return { command: 'help', options: {}, args: [] }; } const command = argv[2]; const commandArgs = argv.slice(3); switch (command) { case 'daemon': return this.parseDaemonCommand(commandArgs); case 'start': return this.parseStartCommand(commandArgs); case 'stop': return this.parseStopCommand(commandArgs); case 'restart': return this.parseRestartCommand(commandArgs); case 'list': case 'ls': return this.parseListCommand(commandArgs); case 'status': return this.parseStatusCommand(commandArgs); case 'logs': return this.parseLogsCommand(commandArgs); case 'shutdown': return this.parseShutdownCommand(commandArgs); case 'webui': return this.parseWebUICommand(commandArgs); case 'help': case '--help': case '-h': return { command: 'help', options: {}, args: [] }; case 'version': case '--version': case '-v': return { command: 'version', options: {}, args: [] }; default: throw new Error(`Unknown command: ${command}`); } } parseDaemonCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { detach: { type: 'boolean', short: 'd' }, 'log-level': { type: 'string', default: 'info' }, 'socket-path': { type: 'string' } }, allowPositionals: true }); return { command: 'daemon', options: values, args: positionals }; } parseStartCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { name: { type: 'string', short: 'n' }, instances: { type: 'string', short: 'i' }, watch: { type: 'boolean', short: 'w' }, 'watch-paths': { type: 'string', multiple: true }, env: { type: 'string', multiple: true, short: 'e' }, 'env-file': { type: 'string' }, cwd: { type: 'string' }, args: { type: 'string', multiple: true }, interpreter: { type: 'string' }, 'max-memory': { type: 'string' }, 'max-restarts': { type: 'string' }, 'restart-delay': { type: 'string' }, 'min-uptime': { type: 'string' }, 'auto-restart-memory': { type: 'boolean' }, 'auto-restart-cpu': { type: 'boolean' }, 'memory-threshold': { type: 'string' }, 'cpu-threshold': { type: 'string' }, 'no-daemon': { type: 'boolean' } }, allowPositionals: true }); if (positionals.length === 0) { throw new Error('Script path is required'); } const script = positionals[0]; if (!(0, helpers_1.isFile)(script)) { throw new Error(`Script file not found: ${script}`); } // Parse environment variables const env = {}; if (values.env && Array.isArray(values.env)) { values.env.forEach(envVar => { const [key, value] = envVar.split('=', 2); if (key && value !== undefined) { env[key] = value; } }); } // Parse instances let instances = 1; if (values.instances) { if (values.instances === 'max') { instances = 'max'; } else { const parsed = parseInt(values.instances, 10); if (isNaN(parsed) || parsed < 1) { throw new Error('instances must be a positive number or "max"'); } instances = parsed; } } // Parse watch paths let watch = false; if (values.watch) { watch = true; } if (values['watch-paths']) { watch = values['watch-paths']; } const config = { script, name: values.name, instances, watch, env: Object.keys(env).length > 0 ? env : undefined, envFile: values['env-file'], cwd: values.cwd, args: positionals.slice(1).concat(values.args || []), interpreter: values.interpreter, maxMemory: values['max-memory'], maxRestarts: values['max-restarts'] ? parseInt(values['max-restarts'], 10) : undefined, restartDelay: values['restart-delay'] ? parseInt(values['restart-delay'], 10) : undefined, minUptime: values['min-uptime'] ? parseInt(values['min-uptime'], 10) : undefined, autoRestartOnHighMemory: values['auto-restart-memory'], autoRestartOnHighCpu: values['auto-restart-cpu'], memoryThreshold: values['memory-threshold'], cpuThreshold: values['cpu-threshold'] ? parseInt(values['cpu-threshold'], 10) : undefined }; // Validate config (0, helpers_1.validateProcessConfig)(config); return { command: 'start', options: { config, noDaemon: values['no-daemon'] }, args: positionals }; } parseStopCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { force: { type: 'boolean', short: 'f' }, name: { type: 'string', short: 'n' }, id: { type: 'string' } }, allowPositionals: true }); const target = positionals[0] || values.name || values.id; if (!target) { throw new Error('Process name or ID is required'); } return { command: 'stop', options: { target, force: values.force, byName: !values.id && (values.name || !target.includes('-')) }, args: positionals }; } parseRestartCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { name: { type: 'string', short: 'n' }, id: { type: 'string' }, graceful: { type: 'boolean', short: 'g' } }, allowPositionals: true }); const target = positionals[0] || values.name || values.id; if (!target) { throw new Error('Process name or ID is required'); } return { command: 'restart', options: { target, byName: !values.id && (values.name || !target.includes('-')), graceful: values.graceful }, args: positionals }; } parseListCommand(args) { const { values } = (0, util_1.parseArgs)({ args, options: { format: { type: 'string', short: 'f' }, json: { type: 'boolean' }, watch: { type: 'boolean', short: 'w' } }, allowPositionals: false }); return { command: 'list', options: { format: values.format || 'table', json: values.json, watch: values.watch }, args: [] }; } parseStatusCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { name: { type: 'string', short: 'n' }, id: { type: 'string' }, json: { type: 'boolean' } }, allowPositionals: true }); const target = positionals[0] || values.name || values.id; return { command: 'status', options: { target, byName: target && !values.id && (values.name || !target.includes('-')), json: values.json }, args: positionals }; } parseLogsCommand(args) { const { values, positionals } = (0, util_1.parseArgs)({ args, options: { name: { type: 'string', short: 'n' }, id: { type: 'string' }, lines: { type: 'string', short: 'l' }, follow: { type: 'boolean', short: 'f' }, json: { type: 'boolean' } }, allowPositionals: true }); const target = positionals[0] || values.name || values.id; const lines = values.lines ? parseInt(values.lines, 10) : 100; if (isNaN(lines) || lines < 1) { throw new Error('lines must be a positive number'); } return { command: 'logs', options: { target, byName: target && !values.id && (values.name || !target.includes('-')), lines, follow: values.follow, json: values.json }, args: positionals }; } parseShutdownCommand(args) { const { values } = (0, util_1.parseArgs)({ args, options: { force: { type: 'boolean', short: 'f' } }, allowPositionals: false }); return { command: 'shutdown', options: { force: values.force }, args: [] }; } parseWebUICommand(args) { if (args.length === 0) { throw new Error('WebUI subcommand required: start, stop, status'); } const subcommand = args[0]; const subArgs = args.slice(1); switch (subcommand) { case 'start': const { values: startValues } = (0, util_1.parseArgs)({ args: subArgs, options: { port: { type: 'string', short: 'p' }, host: { type: 'string', short: 'h' }, username: { type: 'string', short: 'u' }, password: { type: 'string' } }, allowPositionals: false }); return { command: 'webui', options: { action: 'start', ...startValues }, args: [] }; case 'stop': return { command: 'webui', options: { action: 'stop' }, args: [] }; case 'status': return { command: 'webui', options: { action: 'status' }, args: [] }; default: throw new Error(`Unknown webui subcommand: ${subcommand}`); } } getHelp() { return ` NodeDaemon - Production-ready Node.js process manager USAGE: nodedaemon <command> [options] COMMANDS: daemon Start the daemon process start <script> [options] Start a new process stop <name|id> [options] Stop a process restart <name|id> Restart a process list|ls [options] List all processes status [name|id] Show process status logs <name|id> [options] Show process logs webui <subcommand> Manage Web UI shutdown Shutdown the daemon help Show this help version Show version DAEMON OPTIONS: -d, --detach Run daemon in background --log-level <level> Set log level (debug, info, warn, error) --socket-path <path> Custom IPC socket path START OPTIONS: -n, --name <name> Process name -i, --instances <count> Number of instances (default: 1, or 'max' for CPU count) -w, --watch Watch for file changes and restart --watch-paths <paths> Specific paths to watch (comma-separated) -e, --env <KEY=VALUE> Environment variables (can be used multiple times) --env-file <file> Load environment from file (.env, .env.local, etc) --cwd <path> Working directory --args <args> Arguments to pass to script --interpreter <cmd> Custom interpreter (default: node) --max-memory <size> Maximum memory before restart (e.g., 512MB) --max-restarts <count> Maximum restart attempts --restart-delay <ms> Delay between restarts --min-uptime <ms> Minimum uptime to reset restart counter --auto-restart-memory Auto-restart on high memory usage --auto-restart-cpu Auto-restart on high CPU usage --memory-threshold <size> Memory threshold for auto-restart (default: 512MB) --cpu-threshold <percent> CPU threshold for auto-restart (default: 80) --no-daemon Don't start daemon if not running STOP OPTIONS: -f, --force Force kill process -n, --name <name> Stop by process name --id <id> Stop by process ID LIST OPTIONS: -f, --format <format> Output format (table, json) --json Output as JSON -w, --watch Watch for changes STATUS OPTIONS: -n, --name <name> Show status by process name --id <id> Show status by process ID --json Output as JSON LOGS OPTIONS: -n, --name <name> Show logs by process name --id <id> Show logs by process ID -l, --lines <count> Number of lines to show (default: 100) -f, --follow Follow log output --json Output as JSON WEBUI SUBCOMMANDS: start Start the Web UI server stop Stop the Web UI server status Show Web UI status WEBUI START OPTIONS: -p, --port <port> Port to listen on (default: 8080) -h, --host <host> Host to bind to (default: 127.0.0.1) -u, --username <user> Basic auth username --password <pass> Basic auth password EXAMPLES: nodedaemon daemon -d # Start daemon in background nodedaemon start app.js -n myapp -i 4 -w # Start app with 4 instances and watch nodedaemon start server.js -e NODE_ENV=prod # Start with environment variable nodedaemon list # List all processes nodedaemon stop myapp # Stop process by name nodedaemon logs myapp -l 50 -f # Follow last 50 log lines nodedaemon status # Show daemon status nodedaemon webui start -p 3000 # Start Web UI on port 3000 nodedaemon webui status # Check Web UI status nodedaemon shutdown # Shutdown daemon `; } getVersion() { return '1.0.2'; } } exports.CommandParser = CommandParser; //# sourceMappingURL=CommandParser.js.map