@oxog/port-terminator
Version:
Cross-platform utility to terminate processes on ports with zero dependencies
240 lines (234 loc) • 9.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLI = void 0;
const index_1 = require("../index");
const command_parser_1 = require("../utils/command-parser");
const logger_1 = require("../utils/logger");
const validators_1 = require("../utils/validators");
const errors_1 = require("../errors");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../../package.json');
class CLI {
constructor() {
this.logger = new logger_1.Logger('info', false);
}
async run(args = process.argv.slice(2)) {
try {
const options = command_parser_1.CommandParser.parseArgs(args);
if (options.help) {
this.showHelp();
return;
}
if (options.version) {
this.showVersion();
return;
}
if (options.silent) {
this.logger.setSilent(true);
}
const result = await this.executeCommand(options);
if (options.json) {
console.log(JSON.stringify(result, null, 2));
}
else if (result.message) {
if (result.success) {
this.logger.info(result.message);
}
else {
this.logger.error(result.message);
}
}
process.exit(result.success ? 0 : 1);
}
catch (error) {
const message = error instanceof Error ? error.message : 'Unknown error occurred';
this.logger.error(message);
process.exit(1);
}
}
async executeCommand(options) {
const ports = this.resolvePorts(options);
if (ports.length === 0) {
return {
success: false,
message: 'No ports specified. Use --help for usage information.',
};
}
if (options.dryRun) {
return this.executeDryRun(ports, options);
}
return this.executeTermination(ports, options);
}
resolvePorts(options) {
const ports = [];
if (options.ports) {
ports.push(...options.ports);
}
if (options.range) {
const rangePorts = (0, validators_1.parsePortRange)(options.range);
ports.push(...rangePorts);
}
return [...new Set(ports)].sort((a, b) => a - b);
}
async executeDryRun(ports, options) {
const results = [];
for (const port of ports) {
try {
const processes = await (0, index_1.getProcessesOnPort)(port, {
method: options.method,
silent: true,
});
results.push({ port, processes });
}
catch (error) {
results.push({ port, processes: [] });
}
}
const totalProcesses = results.reduce((sum, result) => sum + result.processes.length, 0);
if (options.json) {
return {
success: true,
data: {
dryRun: true,
ports: results,
totalProcesses,
},
};
}
let message = `Dry run: Would terminate ${totalProcesses} process(es) on ${ports.length} port(s)\n`;
for (const result of results) {
if (result.processes.length > 0) {
message += `\nPort ${result.port}:\n`;
for (const process of result.processes) {
message += ` - PID ${process.pid}: ${process.name} (${process.protocol})\n`;
if (process.command) {
message += ` Command: ${process.command}\n`;
}
}
}
}
return {
success: true,
message: message.trim(),
};
}
async executeTermination(ports, options) {
const terminator = new index_1.PortTerminator({
method: options.method,
timeout: options.timeout,
force: options.force,
silent: options.silent,
gracefulTimeout: options.gracefulTimeout,
});
try {
const results = await terminator.terminateWithDetails(ports);
const successCount = results.filter((r) => r.success).length;
const totalProcesses = results.reduce((sum, result) => sum + result.processes.length, 0);
if (options.json) {
return {
success: successCount === results.length,
data: {
results,
summary: {
totalPorts: ports.length,
successfulPorts: successCount,
totalProcessesKilled: totalProcesses,
},
},
};
}
let message = `Successfully terminated ${totalProcesses} process(es) on ${successCount}/${results.length} port(s)`;
const failedResults = results.filter((r) => !r.success);
if (failedResults.length > 0) {
message += `\n\nFailed ports:`;
for (const result of failedResults) {
message += `\n - Port ${result.port}: ${result.error || 'Unknown error'}`;
}
}
if (!options.silent && totalProcesses > 0) {
message += `\n\nTerminated processes:`;
for (const result of results.filter((r) => r.processes.length > 0)) {
message += `\n\nPort ${result.port}:`;
for (const process of result.processes) {
message += `\n - PID ${process.pid}: ${process.name} (${process.protocol})`;
}
}
}
return {
success: successCount === results.length,
message: message.trim(),
};
}
catch (error) {
const message = error instanceof errors_1.PortTerminatorError
? `${error.name}: ${error.message}`
: `Error: ${error instanceof Error ? error.message : String(error)}`;
return {
success: false,
message,
};
}
}
showHelp() {
const help = `
@oxog/port-terminator v${packageJson.version}
Cross-platform utility to terminate processes running on specified ports
USAGE:
port-terminator <port> [options]
port-terminator <port1> <port2> ... [options]
pt <port> [options]
OPTIONS:
-r, --range <start-end> Kill processes in port range (e.g., 3000-3005)
-f, --force Force kill without graceful timeout
-t, --timeout <ms> Overall operation timeout (default: 30000)
-g, --graceful-timeout <ms> Graceful shutdown timeout (default: 5000)
-m, --method <protocol> Protocol to target: tcp, udp, or both (default: both)
-n, --dry-run Show what would be killed without actually killing
-j, --json Output results in JSON format
-s, --silent Suppress all output except errors
-h, --help Show this help message
-v, --version Show version number
EXAMPLES:
port-terminator 3000 # Kill process on port 3000
port-terminator 3000 3001 3002 # Kill processes on multiple ports
port-terminator --range 3000-3005 # Kill processes on port range
port-terminator 3000 --force # Force kill without grace period
port-terminator 3000 --dry-run # Preview what would be killed
port-terminator 3000 --method tcp # Only kill TCP processes
port-terminator 3000 --json # Output in JSON format
pt 3000 # Short alias
EXIT CODES:
0 Success
1 Error or failure
For more information, visit: https://github.com/ersinkoc/port-terminator
`.trim();
console.log(help);
}
showVersion() {
console.log(`@oxog/port-terminator v${packageJson.version}`);
}
}
exports.CLI = CLI;
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason) => {
const logger = new logger_1.Logger();
logger.error('Unhandled promise rejection:', reason);
process.exit(1);
});
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
const logger = new logger_1.Logger();
logger.error('Uncaught exception:', error.message);
process.exit(1);
});
// Run CLI if this file is executed directly
if (require.main === module) {
const cli = new CLI();
cli.run().catch((error) => {
const logger = new logger_1.Logger();
logger.error('CLI error:', error instanceof Error ? error.message : 'Unknown error');
process.exit(1);
});
}
//# sourceMappingURL=index.js.map