port-nuker 1.0.4 ā 1.0.5
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 +25 -0
- package/index.js +213 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@ nuke 5000 --wait && echo "Port is free!"
|
|
|
90
90
|
## Features
|
|
91
91
|
|
|
92
92
|
- **Cross-Platform**: Works on Windows (using `netstat` + `taskkill`) and Unix-like systems (Linux/macOS using `lsof` + `kill`)
|
|
93
|
+
- **Docker Awareness**: Detects when a port is held by Docker and offers to stop the specific container instead of killing the entire Docker daemon
|
|
93
94
|
- **Safe Mode Protection**: Protected ports (22, 80, 443, 3306, 5432, 6379, 27017, 5000, 8080) require `--force` flag to prevent accidental termination of critical services
|
|
94
95
|
- **Interactive Mode**: Browse all active ports with `nuke list` and select which one to kill
|
|
95
96
|
- **Wait Mode**: Block until port is actually released with `--wait` flag for safe command chaining
|
|
@@ -136,6 +137,30 @@ nuke 443 --force
|
|
|
136
137
|
# 5000 (Flask/Docker), 8080 (Alt HTTP)
|
|
137
138
|
```
|
|
138
139
|
|
|
140
|
+
### Docker Containers
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# When a port is held by Docker, port-nuker detects it automatically
|
|
144
|
+
nuke 3000
|
|
145
|
+
|
|
146
|
+
# Output:
|
|
147
|
+
# š³ Detected Docker process (PID: 12345)
|
|
148
|
+
# Searching for container using this port...
|
|
149
|
+
#
|
|
150
|
+
# š¦ Found container using port 3000:
|
|
151
|
+
# Name: my-app
|
|
152
|
+
# ID: abc123def456
|
|
153
|
+
# Ports: 0.0.0.0:3000->3000/tcp
|
|
154
|
+
#
|
|
155
|
+
# What would you like to do?
|
|
156
|
+
# 1. Stop container 'my-app' (recommended)
|
|
157
|
+
# 2. ā ļø Kill Docker daemon (stops ALL containers)
|
|
158
|
+
# 3. Cancel
|
|
159
|
+
|
|
160
|
+
# Selecting option 1 stops only the specific container
|
|
161
|
+
# ā
Successfully stopped container abc123def456
|
|
162
|
+
```
|
|
163
|
+
|
|
139
164
|
## Requirements
|
|
140
165
|
|
|
141
166
|
- Node.js >= 12.0.0
|
package/index.js
CHANGED
|
@@ -72,6 +72,151 @@ function checkSafePort(port, force) {
|
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// Check if a process is Docker-related
|
|
76
|
+
function isDockerProcess(commandName) {
|
|
77
|
+
if (!commandName) return false;
|
|
78
|
+
|
|
79
|
+
const dockerProcessNames = [
|
|
80
|
+
'docker',
|
|
81
|
+
'dockerd',
|
|
82
|
+
'com.docker.backend',
|
|
83
|
+
'docker desktop',
|
|
84
|
+
'containerd',
|
|
85
|
+
'docker.exe'
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const lowerCommand = commandName.toLowerCase();
|
|
89
|
+
return dockerProcessNames.some(name => lowerCommand.includes(name));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Find Docker container using a specific port
|
|
93
|
+
async function findDockerContainerByPort(port) {
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
exec('docker ps --format "{{.ID}}|{{.Names}}|{{.Ports}}"', (error, stdout) => {
|
|
96
|
+
if (error) {
|
|
97
|
+
resolve(null);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const lines = stdout.trim().split('\n');
|
|
102
|
+
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
if (!line) continue;
|
|
105
|
+
|
|
106
|
+
const [id, name, ports] = line.split('|');
|
|
107
|
+
|
|
108
|
+
// Check if this container has the target port
|
|
109
|
+
// Formats: 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp, etc.
|
|
110
|
+
const portRegex = new RegExp(`[:\\s]${port}->|[:\\s]${port}/`);
|
|
111
|
+
|
|
112
|
+
if (ports && portRegex.test(ports)) {
|
|
113
|
+
resolve({ id, name, ports });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
resolve(null);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Handle Docker process - offer to stop container instead of killing daemon
|
|
124
|
+
async function handleDockerProcess(port, pid, processCommand) {
|
|
125
|
+
console.log(`\nš³ Detected Docker process (PID: ${pid})`);
|
|
126
|
+
console.log(' Searching for container using this port...\n');
|
|
127
|
+
|
|
128
|
+
const container = await findDockerContainerByPort(port);
|
|
129
|
+
|
|
130
|
+
if (!container) {
|
|
131
|
+
console.log('ā ļø Could not find a specific container using this port.');
|
|
132
|
+
console.log(' The port might be used by Docker\'s internal networking.\n');
|
|
133
|
+
|
|
134
|
+
const answer = await inquirer.prompt([
|
|
135
|
+
{
|
|
136
|
+
type: 'list',
|
|
137
|
+
name: 'action',
|
|
138
|
+
message: 'What would you like to do?',
|
|
139
|
+
choices: [
|
|
140
|
+
{ name: 'Cancel (recommended)', value: 'cancel' },
|
|
141
|
+
{ name: 'ā ļø Kill Docker daemon (stops ALL containers)', value: 'kill' }
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
if (answer.action === 'kill') {
|
|
147
|
+
const confirm = await inquirer.prompt([
|
|
148
|
+
{
|
|
149
|
+
type: 'confirm',
|
|
150
|
+
name: 'confirmed',
|
|
151
|
+
message: 'ā ļø This will stop ALL Docker containers. Are you sure?',
|
|
152
|
+
default: false
|
|
153
|
+
}
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
if (confirm.confirmed) {
|
|
157
|
+
killPid(pid);
|
|
158
|
+
} else {
|
|
159
|
+
console.log('Cancelled.');
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
console.log('Cancelled.');
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Found a container
|
|
168
|
+
console.log(`š¦ Found container using port ${port}:`);
|
|
169
|
+
console.log(` Name: ${container.name}`);
|
|
170
|
+
console.log(` ID: ${container.id}`);
|
|
171
|
+
console.log(` Ports: ${container.ports}\n`);
|
|
172
|
+
|
|
173
|
+
const answer = await inquirer.prompt([
|
|
174
|
+
{
|
|
175
|
+
type: 'list',
|
|
176
|
+
name: 'action',
|
|
177
|
+
message: 'What would you like to do?',
|
|
178
|
+
choices: [
|
|
179
|
+
{ name: `Stop container '${container.name}' (recommended)`, value: 'stop' },
|
|
180
|
+
{ name: 'ā ļø Kill Docker daemon (stops ALL containers)', value: 'kill' },
|
|
181
|
+
{ name: 'Cancel', value: 'cancel' }
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
if (answer.action === 'stop') {
|
|
187
|
+
console.log(`\nStopping container '${container.name}'...`);
|
|
188
|
+
stopDockerContainer(container.id);
|
|
189
|
+
} else if (answer.action === 'kill') {
|
|
190
|
+
const confirm = await inquirer.prompt([
|
|
191
|
+
{
|
|
192
|
+
type: 'confirm',
|
|
193
|
+
name: 'confirmed',
|
|
194
|
+
message: 'ā ļø This will stop ALL Docker containers. Are you sure?',
|
|
195
|
+
default: false
|
|
196
|
+
}
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
if (confirm.confirmed) {
|
|
200
|
+
killPid(pid);
|
|
201
|
+
} else {
|
|
202
|
+
console.log('Cancelled.');
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
console.log('Cancelled.');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Stop a Docker container
|
|
210
|
+
function stopDockerContainer(containerId) {
|
|
211
|
+
exec(`docker stop ${containerId}`, (error, stdout, stderr) => {
|
|
212
|
+
if (error) {
|
|
213
|
+
console.error(`Failed to stop container: ${error.message}`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
console.log(`ā
Successfully stopped container ${containerId}`);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
75
220
|
// List all active ports with process details
|
|
76
221
|
function listPorts() {
|
|
77
222
|
const platform = os.platform();
|
|
@@ -454,17 +599,76 @@ function nukePort(port, shouldWait = false, shouldForce = false) {
|
|
|
454
599
|
return;
|
|
455
600
|
}
|
|
456
601
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
602
|
+
// Get process details to check for Docker
|
|
603
|
+
const pidArray = Array.from(pids);
|
|
604
|
+
|
|
605
|
+
if (platform === 'win32') {
|
|
606
|
+
// Get process details on Windows
|
|
607
|
+
const tasklistCmd = `tasklist /FI "PID eq ${pidArray.join('" /FI "PID eq ')}" /FO CSV /NH`;
|
|
608
|
+
|
|
609
|
+
exec(tasklistCmd, async (error, tasklistOutput) => {
|
|
610
|
+
let processCommand = null;
|
|
611
|
+
|
|
612
|
+
if (!error && tasklistOutput) {
|
|
613
|
+
const lines = tasklistOutput.trim().split('\n');
|
|
614
|
+
if (lines.length > 0) {
|
|
615
|
+
const matches = lines[0].match(/"([^"]+)"/g);
|
|
616
|
+
if (matches && matches.length > 0) {
|
|
617
|
+
processCommand = matches[0].replace(/"/g, '');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Check if it's a Docker process
|
|
623
|
+
if (processCommand && isDockerProcess(processCommand)) {
|
|
624
|
+
await handleDockerProcess(port, pidArray[0], processCommand);
|
|
625
|
+
} else {
|
|
626
|
+
// Normal kill
|
|
627
|
+
pidArray.forEach(pid => {
|
|
628
|
+
console.log(`Found process with PID: ${pid}. Nuking it...`);
|
|
629
|
+
killPid(pid, () => {
|
|
630
|
+
if (shouldWait) {
|
|
631
|
+
setTimeout(() => {
|
|
632
|
+
pollPortUntilFree(port);
|
|
633
|
+
}, 1000);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
});
|
|
465
637
|
}
|
|
466
638
|
});
|
|
467
|
-
}
|
|
639
|
+
} else {
|
|
640
|
+
// Unix-like: use lsof to get process command
|
|
641
|
+
exec(`lsof -i :${port} -n -P`, async (error, lsofOutput) => {
|
|
642
|
+
let processCommand = null;
|
|
643
|
+
|
|
644
|
+
if (!error && lsofOutput) {
|
|
645
|
+
const lines = lsofOutput.trim().split('\n');
|
|
646
|
+
if (lines.length > 1) {
|
|
647
|
+
const parts = lines[1].trim().split(/\s+/);
|
|
648
|
+
if (parts.length > 0) {
|
|
649
|
+
processCommand = parts[0];
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Check if it's a Docker process
|
|
655
|
+
if (processCommand && isDockerProcess(processCommand)) {
|
|
656
|
+
await handleDockerProcess(port, pidArray[0], processCommand);
|
|
657
|
+
} else {
|
|
658
|
+
// Normal kill
|
|
659
|
+
pidArray.forEach(pid => {
|
|
660
|
+
console.log(`Found process with PID: ${pid}. Nuking it...`);
|
|
661
|
+
killPid(pid, () => {
|
|
662
|
+
if (shouldWait) {
|
|
663
|
+
setTimeout(() => {
|
|
664
|
+
pollPortUntilFree(port);
|
|
665
|
+
}, 1000);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
468
672
|
});
|
|
469
673
|
}
|
|
470
674
|
|