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.
Files changed (3) hide show
  1. package/README.md +25 -0
  2. package/index.js +213 -9
  3. 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
- pids.forEach(pid => {
458
- console.log(`Found process with PID: ${pid}. Nuking it...`);
459
- killPid(pid, () => {
460
- if (shouldWait) {
461
- // Wait a moment for the process to fully terminate
462
- setTimeout(() => {
463
- pollPortUntilFree(port);
464
- }, 1000);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "port-nuker",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "A simple CLI utility to kill processes holding a specific port. Solves EADDRINUSE errors instantly.",
5
5
  "main": "index.js",
6
6
  "bin": {