npm-killer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/bin/index.js +133 -0
- package/lib/port-killer.js +166 -0
- package/npm-killer-1.0.0.tgz +0 -0
- package/package.json +35 -0
- package/test/port-killer.test.js +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# npm-killer
|
|
2
|
+
|
|
3
|
+
Interactive CLI tool to find and kill Node.js processes running on ports. Unlike other port killers, this tool is designed to be **interactive and developer-friendly**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Interactive Mode** - Visual process selection with checkboxes
|
|
8
|
+
- 🔍 **Smart Detection** - Finds only Node.js processes (node, npm, yarn, pnpm, bun)
|
|
9
|
+
- 📋 **List Mode** - See all Node.js processes with their ports
|
|
10
|
+
- ⚡ **Quick Kill** - Kill by port number or kill all at once
|
|
11
|
+
- 🛡️ **Safe by Default** - Confirmation prompts before killing
|
|
12
|
+
- 🎨 **Beautiful Output** - Color-coded, easy to read
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g npm-killer
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or use with npx:
|
|
21
|
+
```bash
|
|
22
|
+
npx npm-killer
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Interactive Mode (Default)
|
|
28
|
+
```bash
|
|
29
|
+
npm-killer
|
|
30
|
+
```
|
|
31
|
+
Shows a checklist of all Node.js processes with open ports. Select multiple processes with spacebar, press Enter to confirm.
|
|
32
|
+
|
|
33
|
+
### List Processes
|
|
34
|
+
```bash
|
|
35
|
+
npm-killer --list
|
|
36
|
+
# or
|
|
37
|
+
npm-killer -l
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Kill by Port
|
|
41
|
+
```bash
|
|
42
|
+
npm-killer --port 3000
|
|
43
|
+
# or
|
|
44
|
+
npm-killer -p 3000
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Kill All Node.js Processes
|
|
48
|
+
```bash
|
|
49
|
+
npm-killer --all
|
|
50
|
+
# or
|
|
51
|
+
npm-killer -a
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Force Kill (No Confirmation)
|
|
55
|
+
```bash
|
|
56
|
+
npm-killer --port 3000 --force
|
|
57
|
+
npm-killer -p 3000 -f
|
|
58
|
+
|
|
59
|
+
npm-killer --all --force
|
|
60
|
+
npm-killer -a -f
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Example Output
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
$ npm-killer
|
|
67
|
+
|
|
68
|
+
Select processes to kill:
|
|
69
|
+
|
|
70
|
+
✓ 1. node (PID: 12345) - Ports: 3000, 3001
|
|
71
|
+
✓ 2. npm (PID: 12346) - Ports: 8080
|
|
72
|
+
──────────────
|
|
73
|
+
◯ Kill ALL Node.js processes
|
|
74
|
+
◯ Exit without killing
|
|
75
|
+
|
|
76
|
+
Kill 2 process(es)? (y/N) y
|
|
77
|
+
|
|
78
|
+
✓ Killed node (PID: 12345)
|
|
79
|
+
✓ Killed npm (PID: 12346)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Why npm-killer?
|
|
83
|
+
|
|
84
|
+
Existing tools like `fuser`, `lsof`, or `kill-port` require you to know the port or PID upfront. `npm-killer` is built for developers who:
|
|
85
|
+
- Run multiple Node.js services and forget which is on which port
|
|
86
|
+
- Want a visual overview before killing anything
|
|
87
|
+
- Prefer interactive selection over memorizing commands
|
|
88
|
+
- Work with npm, yarn, pnpm, bun - all detected automatically
|
|
89
|
+
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Node.js 18+
|
|
93
|
+
- macOS, Linux, or Windows (WSL)
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { findNodeProcesses, killProcess, interactiveKill } from '../lib/port-killer.js';
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.name('npm-killer')
|
|
9
|
+
.description('Interactive CLI tool to kill Node.js processes on ports')
|
|
10
|
+
.version('1.0.0')
|
|
11
|
+
.option('-p, --port <port>', 'Kill process on specific port')
|
|
12
|
+
.option('-a, --all', 'Kill all Node.js processes')
|
|
13
|
+
.option('-l, --list', 'List all Node.js processes with ports')
|
|
14
|
+
.option('-f, --force', 'Force kill without confirmation')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
try {
|
|
17
|
+
if (options.list) {
|
|
18
|
+
await listProcesses();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (options.port) {
|
|
23
|
+
await killByPort(options.port, options.force);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (options.all) {
|
|
28
|
+
await killAll(options.force);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Default: interactive mode
|
|
33
|
+
await interactiveMode();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(chalk.red('Error:'), error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function listProcesses() {
|
|
41
|
+
const processes = await findNodeProcesses();
|
|
42
|
+
|
|
43
|
+
if (processes.length === 0) {
|
|
44
|
+
console.log(chalk.yellow('No Node.js processes found with open ports.'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(chalk.bold('\n Node.js Processes with Open Ports:\n'));
|
|
49
|
+
|
|
50
|
+
// Calculate column widths
|
|
51
|
+
const maxNameLen = Math.max(...processes.map(p => p.name.length), 4);
|
|
52
|
+
const maxPidLen = Math.max(...processes.map(p => String(p.pid).length), 3);
|
|
53
|
+
|
|
54
|
+
// Header
|
|
55
|
+
console.log(` ${chalk.cyan('#'.padEnd(3))} ${chalk.bold('Name'.padEnd(maxNameLen))} ${chalk.bold('PID'.padStart(maxPidLen))} ${chalk.bold('Ports')} ${chalk.gray('Command')}`);
|
|
56
|
+
console.log(` ${chalk.gray('─'.repeat(3))} ${chalk.gray('─'.repeat(maxNameLen))} ${chalk.gray('─'.repeat(maxPidLen))} ${chalk.gray('─────')} ${chalk.gray('───────')}`);
|
|
57
|
+
|
|
58
|
+
processes.forEach((proc, index) => {
|
|
59
|
+
const ports = proc.ports.length > 0 ? proc.ports.join(',') : '—';
|
|
60
|
+
// Truncate command to fit
|
|
61
|
+
const cmd = proc.cmd.length > 60 ? proc.cmd.substring(0, 57) + '...' : proc.cmd;
|
|
62
|
+
const name = proc.name.length > maxNameLen ? proc.name.substring(0, maxNameLen - 1) + '…' : proc.name.padEnd(maxNameLen);
|
|
63
|
+
console.log(` ${chalk.cyan(String(index + 1).padEnd(3))} ${chalk.white(name)} ${chalk.green(String(proc.pid).padStart(maxPidLen))} ${chalk.yellow(ports.padEnd(5))} ${chalk.gray(cmd)}`);
|
|
64
|
+
});
|
|
65
|
+
console.log();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function killByPort(port, force) {
|
|
69
|
+
const processes = await findNodeProcesses();
|
|
70
|
+
const target = processes.find(p => p.ports.includes(parseInt(port)));
|
|
71
|
+
|
|
72
|
+
if (!target) {
|
|
73
|
+
console.log(chalk.yellow(`No Node.js process found on port ${port}`));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!force) {
|
|
78
|
+
const inquirer = (await import('inquirer')).default;
|
|
79
|
+
const answer = await inquirer.prompt([{
|
|
80
|
+
type: 'confirm',
|
|
81
|
+
name: 'confirm',
|
|
82
|
+
message: `Kill ${target.name} (PID: ${target.pid}) on port ${port}?`,
|
|
83
|
+
default: false
|
|
84
|
+
}]);
|
|
85
|
+
|
|
86
|
+
if (!answer.confirm) {
|
|
87
|
+
console.log(chalk.gray('Cancelled.'));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await killProcess(target.pid);
|
|
93
|
+
console.log(chalk.green(`✓ Killed ${target.name} (PID: ${target.pid}) on port ${port}`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function killAll(force) {
|
|
97
|
+
const processes = await findNodeProcesses();
|
|
98
|
+
|
|
99
|
+
if (processes.length === 0) {
|
|
100
|
+
console.log(chalk.yellow('No Node.js processes found with open ports.'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!force) {
|
|
105
|
+
const inquirer = (await import('inquirer')).default;
|
|
106
|
+
const answer = await inquirer.prompt([{
|
|
107
|
+
type: 'confirm',
|
|
108
|
+
name: 'confirm',
|
|
109
|
+
message: `Kill all ${processes.length} Node.js process(es)?`,
|
|
110
|
+
default: false
|
|
111
|
+
}]);
|
|
112
|
+
|
|
113
|
+
if (!answer.confirm) {
|
|
114
|
+
console.log(chalk.gray('Cancelled.'));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const proc of processes) {
|
|
120
|
+
try {
|
|
121
|
+
await killProcess(proc.pid);
|
|
122
|
+
console.log(chalk.green(`✓ Killed ${proc.name} (PID: ${proc.pid})`));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(chalk.red(`✗ Failed to kill ${proc.name} (PID: ${proc.pid}):`), error.message);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function interactiveMode() {
|
|
130
|
+
await interactiveKill();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
program.parse();
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import psList from 'ps-list';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
export async function findNodeProcesses() {
|
|
7
|
+
const processes = await psList();
|
|
8
|
+
|
|
9
|
+
const nodeProcesses = processes.filter(proc => {
|
|
10
|
+
const name = proc.name.toLowerCase();
|
|
11
|
+
const cmd = proc.cmd.toLowerCase();
|
|
12
|
+
return name === 'node' ||
|
|
13
|
+
name.includes('node') ||
|
|
14
|
+
name.includes('next-server') ||
|
|
15
|
+
cmd.includes('node') ||
|
|
16
|
+
cmd.includes('npm') ||
|
|
17
|
+
cmd.includes('yarn') ||
|
|
18
|
+
cmd.includes('pnpm') ||
|
|
19
|
+
cmd.includes('bun');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const processesWithPorts = [];
|
|
23
|
+
|
|
24
|
+
for (const proc of nodeProcesses) {
|
|
25
|
+
const ports = await getProcessPorts(proc.pid);
|
|
26
|
+
if (ports.length > 0) {
|
|
27
|
+
processesWithPorts.push({
|
|
28
|
+
pid: proc.pid,
|
|
29
|
+
name: proc.name,
|
|
30
|
+
cmd: proc.cmd,
|
|
31
|
+
ports
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return processesWithPorts;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function getProcessPorts(pid) {
|
|
40
|
+
try {
|
|
41
|
+
const output = execSync(`lsof -i -P -p ${pid} 2>/dev/null`, {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const ports = [];
|
|
47
|
+
const lines = output.trim().split('\n');
|
|
48
|
+
const pidStr = String(pid);
|
|
49
|
+
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
// Match lines where column 2 (PID) matches our target PID and it's LISTENing
|
|
52
|
+
// lsof format: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
|
|
53
|
+
const parts = line.trim().split(/\s+/);
|
|
54
|
+
if (parts.length >= 2 && parts[1] === pidStr) {
|
|
55
|
+
const match = line.match(/:(\d+)\s+\(LISTEN\)$/);
|
|
56
|
+
if (match) {
|
|
57
|
+
ports.push(parseInt(match[1]));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [...new Set(ports)];
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function killProcess(pid, signal = 'SIGTERM') {
|
|
69
|
+
try {
|
|
70
|
+
process.kill(pid, signal);
|
|
71
|
+
|
|
72
|
+
// Wait a bit and check if process is still alive
|
|
73
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
process.kill(pid, 0); // Check if process exists
|
|
77
|
+
// If we get here, process still exists, force kill
|
|
78
|
+
if (signal !== 'SIGKILL') {
|
|
79
|
+
return killProcess(pid, 'SIGKILL');
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// Process is dead, success
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw new Error(`Failed to kill process ${pid}: ${error.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function interactiveKill() {
|
|
91
|
+
const processes = await findNodeProcesses();
|
|
92
|
+
|
|
93
|
+
if (processes.length === 0) {
|
|
94
|
+
console.log(chalk.yellow('\n No Node.js processes found with open ports.\n'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log(chalk.bold('\n Select processes to kill:\n'));
|
|
99
|
+
|
|
100
|
+
const choices = processes.map((proc, index) => {
|
|
101
|
+
const ports = proc.ports.length > 0 ? proc.ports.join(', ') : 'none';
|
|
102
|
+
return {
|
|
103
|
+
name: `${chalk.cyan(index + 1)}. ${chalk.white(proc.name)} ${chalk.gray(`(PID: ${proc.pid})`)} - ${chalk.yellow('Ports:')} ${chalk.yellow(ports)}`,
|
|
104
|
+
value: proc,
|
|
105
|
+
short: `${proc.name} (${proc.pid})`
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
choices.push(new inquirer.Separator());
|
|
110
|
+
choices.push({
|
|
111
|
+
name: chalk.red('Kill ALL Node.js processes'),
|
|
112
|
+
value: 'ALL'
|
|
113
|
+
});
|
|
114
|
+
choices.push({
|
|
115
|
+
name: chalk.gray('Exit without killing'),
|
|
116
|
+
value: 'EXIT'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const { selected } = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'checkbox',
|
|
122
|
+
name: 'selected',
|
|
123
|
+
message: 'Use space to select, enter to confirm:',
|
|
124
|
+
choices,
|
|
125
|
+
validate: (answer) => {
|
|
126
|
+
if (answer.length === 0) {
|
|
127
|
+
return 'Select at least one process or choose Exit';
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
if (selected.includes('EXIT') || selected.length === 0) {
|
|
135
|
+
console.log(chalk.gray('\n Cancelled.\n'));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const toKill = selected.includes('ALL') ? processes : selected.filter(s => s !== 'ALL');
|
|
140
|
+
|
|
141
|
+
// Final confirmation
|
|
142
|
+
const { confirm } = await inquirer.prompt([
|
|
143
|
+
{
|
|
144
|
+
type: 'confirm',
|
|
145
|
+
name: 'confirm',
|
|
146
|
+
message: `Kill ${toKill.length} process(es)?`,
|
|
147
|
+
default: false
|
|
148
|
+
}
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
if (!confirm) {
|
|
152
|
+
console.log(chalk.gray('\n Cancelled.\n'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log('');
|
|
157
|
+
for (const proc of toKill) {
|
|
158
|
+
try {
|
|
159
|
+
await killProcess(proc.pid);
|
|
160
|
+
console.log(chalk.green(` ✓ Killed ${proc.name} (PID: ${proc.pid})`));
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error(chalk.red(` ✗ Failed to kill ${proc.name} (PID: ${proc.pid}):`), error.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
console.log('');
|
|
166
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "npm-killer",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive CLI tool to kill Node.js processes on ports",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"npm-killer": "bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/index.js",
|
|
11
|
+
"dev": "node bin/index.js",
|
|
12
|
+
"test": "node --test test/*.test.js",
|
|
13
|
+
"prepublishOnly": "npm test"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cli",
|
|
17
|
+
"port",
|
|
18
|
+
"kill",
|
|
19
|
+
"node",
|
|
20
|
+
"process",
|
|
21
|
+
"interactive"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"commander": "^12.0.0",
|
|
27
|
+
"chalk": "^5.3.0",
|
|
28
|
+
"inquirer": "^9.2.0",
|
|
29
|
+
"ps-list": "^8.1.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"type": "module"
|
|
35
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, before, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { findNodeProcesses, killProcess, getProcessPorts } from '../lib/port-killer.js';
|
|
5
|
+
|
|
6
|
+
describe('port-killer', () => {
|
|
7
|
+
let testServer;
|
|
8
|
+
let testPort = 34567;
|
|
9
|
+
|
|
10
|
+
before(async () => {
|
|
11
|
+
// Start a test HTTP server
|
|
12
|
+
testServer = spawn('node', ['-e', `
|
|
13
|
+
const http = require('http');
|
|
14
|
+
const server = http.createServer((req, res) => res.end('test'));
|
|
15
|
+
server.listen(${testPort}, () => console.log('Server started on port ${testPort}'));
|
|
16
|
+
`], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
17
|
+
|
|
18
|
+
// Wait for server to start
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
after(async () => {
|
|
23
|
+
if (testServer) {
|
|
24
|
+
testServer.kill();
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should find Node.js processes with open ports', async () => {
|
|
30
|
+
const processes = await findNodeProcesses();
|
|
31
|
+
assert.ok(Array.isArray(processes));
|
|
32
|
+
|
|
33
|
+
// Our test server should be in the list
|
|
34
|
+
const testProc = processes.find(p => p.ports.includes(testPort));
|
|
35
|
+
assert.ok(testProc, `Should find test process on port ${testPort}`);
|
|
36
|
+
assert.ok(testProc.pid > 0);
|
|
37
|
+
// Name might be 'node' or include 'node'
|
|
38
|
+
assert.ok(testProc.name.toLowerCase() === 'node' || testProc.name.toLowerCase().includes('node'));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should get process ports correctly', async () => {
|
|
42
|
+
const processes = await findNodeProcesses();
|
|
43
|
+
const testProc = processes.find(p => p.ports.includes(testPort));
|
|
44
|
+
|
|
45
|
+
if (testProc) {
|
|
46
|
+
const ports = await getProcessPorts(testProc.pid);
|
|
47
|
+
assert.ok(ports.includes(testPort));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should kill a process by PID', async () => {
|
|
52
|
+
// Start another test process to kill
|
|
53
|
+
const killServer = spawn('node', ['-e', `
|
|
54
|
+
const http = require('http');
|
|
55
|
+
const server = http.createServer((req, res) => res.end('test'));
|
|
56
|
+
server.listen(34568, () => console.log('Server started on port 34568'));
|
|
57
|
+
`], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
58
|
+
|
|
59
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
60
|
+
|
|
61
|
+
const processes = await findNodeProcesses();
|
|
62
|
+
const killProc = processes.find(p => p.ports.includes(34568));
|
|
63
|
+
|
|
64
|
+
assert.ok(killProc, 'Should find process to kill');
|
|
65
|
+
|
|
66
|
+
await killProcess(killProc.pid);
|
|
67
|
+
|
|
68
|
+
// Verify it's dead
|
|
69
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
70
|
+
|
|
71
|
+
const processesAfter = await findNodeProcesses();
|
|
72
|
+
const deadProc = processesAfter.find(p => p.pid === killProc.pid);
|
|
73
|
+
assert.strictEqual(deadProc, undefined, 'Process should be dead');
|
|
74
|
+
});
|
|
75
|
+
});
|