redpill-cli 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/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/index.js +7 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miguel Medeiros
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<br />
|
|
3
|
+
<img src="assets/banner.jpg" alt="redpill" width="600" />
|
|
4
|
+
<br />
|
|
5
|
+
<br />
|
|
6
|
+
<code> 💊 redpill </code>
|
|
7
|
+
<br />
|
|
8
|
+
<br />
|
|
9
|
+
<em>"You take the red pill, I show you how deep the rabbit hole goes."</em>
|
|
10
|
+
<br />
|
|
11
|
+
<sub>— Morpheus</sub>
|
|
12
|
+
<br />
|
|
13
|
+
<br />
|
|
14
|
+
<a href="https://www.npmjs.com/package/redpill"><img src="https://img.shields.io/npm/v/redpill.svg?style=flat-square&color=cc0000" alt="npm version" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/redpill"><img src="https://img.shields.io/npm/dm/redpill.svg?style=flat-square&color=cc0000" alt="downloads" /></a>
|
|
16
|
+
<a href="https://github.com/miguelmedeiros/port/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/redpill.svg?style=flat-square&color=cc0000" alt="license" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
> **The Matrix has your ports.** Every developer has seen `EADDRINUSE`. You Google it. You run `lsof`. You copy the PID. You run `kill`. Every. Single. Time.
|
|
22
|
+
>
|
|
23
|
+
> **Take the red pill.** See the truth. Free your ports. One command.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm i -g redpill
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or run directly — no install needed:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx redpill 3000
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Wake Up, Neo...
|
|
38
|
+
|
|
39
|
+
### See the truth about a port
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
redpill 3000
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
💊 redpill 3000
|
|
47
|
+
|
|
48
|
+
PID 2847
|
|
49
|
+
Name node
|
|
50
|
+
Cmd next dev
|
|
51
|
+
User neo
|
|
52
|
+
|
|
53
|
+
Kill this process? (y/n)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Free your port — no questions asked
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
redpill free 3000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> _"There is no spoon."_ There is no port conflict. Just free it.
|
|
63
|
+
|
|
64
|
+
### Free an entire range
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
redpill free 3000-3010
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> _"I know Kung Fu."_ — You, after freeing 10 ports in one command.
|
|
71
|
+
|
|
72
|
+
### See all listening ports
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
redpill list
|
|
76
|
+
# or
|
|
77
|
+
redpill ls
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
💊 redpill ls
|
|
82
|
+
|
|
83
|
+
PORT PID NAME COMMAND
|
|
84
|
+
────────────────────────────────────────────────────────────
|
|
85
|
+
3000 2847 node next dev
|
|
86
|
+
5432 1203 postgres /usr/lib/postgresql/14/bin/…
|
|
87
|
+
8080 3891 node vite
|
|
88
|
+
|
|
89
|
+
3 ports in use
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> _"I can only show you the door. You're the one that has to walk through it."_
|
|
93
|
+
|
|
94
|
+
### Help
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
redpill --help
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Why?
|
|
101
|
+
|
|
102
|
+
You've been living in the **blue pill** world:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Error: listen EADDRINUSE: address already in use :::3000
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# The blue pill way (every single time)
|
|
110
|
+
lsof -i :3000
|
|
111
|
+
# scroll through output...
|
|
112
|
+
kill -9 2847
|
|
113
|
+
# pray it worked...
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Take the red pill:**
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# The red pill way
|
|
120
|
+
redpill 3000
|
|
121
|
+
# → node (PID 2847) - next dev
|
|
122
|
+
# → Kill? y
|
|
123
|
+
# ✓ Done.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> _"Welcome to the real world."_
|
|
127
|
+
|
|
128
|
+
## Platforms
|
|
129
|
+
|
|
130
|
+
Works on **macOS** and **Linux** (uses `lsof` under the hood).
|
|
131
|
+
|
|
132
|
+
> _"Unfortunately, no one can be told what the Matrix is. You have to see it for yourself."_
|
|
133
|
+
>
|
|
134
|
+
> Run `redpill ls` and see.
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{execSync as f}from"child_process";function h(e){try{let n=f(`lsof -i :${e} -P -n -sTCP:LISTEN 2>/dev/null`,{encoding:"utf-8"}).trim().split(`
|
|
3
|
+
`).slice(1),t=new Set,l=[];for(let r of n){let i=r.trim().split(/\s+/);if(i.length<9)continue;let c=i[0],p=parseInt(i[1],10),$=i[2];if(Number.isNaN(p)||t.has(p))continue;t.add(p);let a=N(p)||c;l.push({pid:p,name:c,user:$,command:a})}return l}catch{return[]}}function y(){try{let s=f("lsof -i -P -n -sTCP:LISTEN 2>/dev/null",{encoding:"utf-8"}).trim().split(`
|
|
4
|
+
`).slice(1),n=new Set,t=[];for(let l of s){let r=l.trim().split(/\s+/);if(r.length<9)continue;let i=r[0],c=parseInt(r[1],10),p=r[2],$=r[8];if(Number.isNaN(c))continue;let a=$.match(/:(\d+)$/);if(!a)continue;let S=parseInt(a[1],10),x=`${c}:${S}`;if(n.has(x))continue;n.add(x);let H=N(c)||i;t.push({port:S,process:{pid:c,name:i,user:p,command:H}})}return t.sort((l,r)=>l.port-r.port),t}catch{return[]}}function N(e){try{return f(`ps -p ${e} -o command= 2>/dev/null`,{encoding:"utf-8"}).trim()}catch{return""}}function w(e){try{process.kill(e,"SIGTERM");let s=!0;for(let n=0;n<10;n++)try{process.kill(e,0),f("sleep 0.1")}catch{s=!1;break}if(s)try{process.kill(e,"SIGKILL")}catch{}return!0}catch{return!1}}function k(e){let s=[];for(let n of e)if(n.includes("-")){let[t,l]=n.split("-"),r=parseInt(t,10),i=parseInt(l,10);if(Number.isNaN(r)||Number.isNaN(i)||r>i||!g(r)||!g(i))continue;for(let c=r;c<=i;c++)s.push(c)}else{let t=parseInt(n,10);!Number.isNaN(t)&&g(t)&&s.push(t)}return s}function g(e){return e>=1&&e<=65535}import{createInterface as A}from"readline";import D from"picocolors";function I(e){return new Promise(s=>{let n=A({input:process.stdin,output:process.stdout});n.question(` ${e} ${D.dim("(y/n)")} `,t=>{n.close();let l=t.trim().toLowerCase();s(l==="y"||l==="yes")})})}import o from"picocolors";var L="1.0.0",G=`
|
|
5
|
+
${o.bold(o.red("\u{1F48A} redpill"))} ${o.dim(`v${L}`)}
|
|
6
|
+
${o.dim('Kill "port already in use" forever.')}
|
|
7
|
+
`;function E(){console.log(`redpill v${L}`)}function b(){console.log(G),console.log(` ${o.bold("Usage:")}`),console.log(),console.log(` ${o.red("redpill")} ${o.white("<port>")} Check port & offer to kill`),console.log(` ${o.red("redpill")} ${o.white("free <port|range>")} Free ports (no prompt)`),console.log(` ${o.red("redpill")} ${o.white("list")} | ${o.white("ls")} List all listening ports`),console.log(` ${o.red("redpill")} ${o.dim("-h, --help")} Show this help`),console.log(` ${o.red("redpill")} ${o.dim("-v, --version")} Show version`),console.log(),console.log(` ${o.bold("Examples:")}`),console.log(),console.log(` ${o.dim("$")} ${o.white("redpill 3000")} ${o.dim("Who's using port 3000?")}`),console.log(` ${o.dim("$")} ${o.white("redpill free 3000")} ${o.dim("Kill it immediately")}`),console.log(` ${o.dim("$")} ${o.white("redpill free 3000-3010")} ${o.dim("Free an entire range")}`),console.log(` ${o.dim("$")} ${o.white("redpill ls")} ${o.dim("Show all listening ports")}`),console.log()}function F(e,s){console.log(),console.log(` ${o.bold(o.red("\u{1F48A} redpill"))} ${o.white(String(e))}`),console.log(),console.log(` ${o.dim("PID")} ${o.bold(o.white(String(s.pid)))}`),console.log(` ${o.dim("Name")} ${o.white(s.name)}`),console.log(` ${o.dim("Cmd")} ${o.white(s.command)}`),console.log(` ${o.dim("User")} ${o.white(s.user)}`),console.log()}function K(e){console.log(` ${o.green("\u2713")} Port ${o.bold(String(e))} is free`)}function P(e,s){console.log(` ${o.green("\u2713")} Killed process ${o.bold(String(s))} on port ${o.bold(String(e))}`)}function v(e){console.log(` ${o.red("\u2717")} Failed to kill process ${o.bold(String(e))}. Try with ${o.yellow("sudo")}.`)}function C(){console.log(` ${o.dim("Skipped.")}`)}function u(e){console.log(` ${o.red("\u2717")} ${e}`)}function T(){console.log(),console.log(` ${o.bold(o.red("\u{1F48A} redpill"))} ${o.dim("ls")}`),console.log(),console.log(` ${o.dim(m("PORT",8))}${o.dim(m("PID",10))}${o.dim(m("NAME",16))}${o.dim("COMMAND")}`),console.log(` ${o.dim("\u2500".repeat(60))}`)}function M(e,s){console.log(` ${o.cyan(m(String(e),8))}${o.white(m(String(s.pid),10))}${o.white(m(s.name,16))}${o.dim(U(s.command,40))}`)}function O(){console.log(),console.log(` ${o.dim("No listening ports found.")}`),console.log()}function R(e,s){console.log(),console.log(e===0&&s===0?` ${o.dim("No processes found on the specified ports.")}`:` ${o.green("\u2713")} Freed ${o.bold(String(e))}/${s} ports`),console.log()}function m(e,s){return e.length>=s?`${e} `:e+" ".repeat(s-e.length)}function U(e,s){return e.length>s?`${e.slice(0,s-1)}\u2026`:e}var V=process.argv.slice(2),d=V[0];async function j(){!d||d==="-h"||d==="--help"?b():d==="-v"||d==="--version"?E():d==="list"||d==="ls"?W():d==="free"?await z(V.slice(1)):/^\d+$/.test(d)?await q(parseInt(d,10)):(u(`Unknown command: ${d}`),console.log(),b(),process.exit(1))}async function q(e){g(e)||(u(`Invalid port: ${e}. Must be between 1 and 65535.`),process.exit(1));let s=h(e);if(s.length===0){console.log(),K(e),console.log();return}for(let n of s)F(e,n),await I("Kill this process?")?w(n.pid)?P(e,n.pid):v(n.pid):C(),console.log()}async function z(e){e.length===0&&(u("Please specify a port or range. Example: port free 3000-3010"),process.exit(1));let s=k(e);s.length===0&&(u("No valid ports in the specified range."),process.exit(1));let n=0,t=0;for(let l of s){let r=h(l);for(let i of r)t++,w(i.pid)?(n++,P(l,i.pid)):v(i.pid)}R(n,t)}function W(){let e=y();if(e.length===0){O();return}T();for(let s of e)M(s.port,s.process);console.log(),console.log(` ${e.length} port${e.length!==1?"s":""} in use`),console.log()}j().catch(e=>{u(e instanceof Error?e.message:String(e)),process.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "redpill-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Kill \"port already in use\" forever. A fast CLI to find and free ports.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"redpill": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"test:coverage": "vitest run --coverage",
|
|
18
|
+
"lint": "biome check src/",
|
|
19
|
+
"lint:fix": "biome check --write src/",
|
|
20
|
+
"format": "biome format src/",
|
|
21
|
+
"format:fix": "biome format --write src/",
|
|
22
|
+
"check": "biome check --write .",
|
|
23
|
+
"ci": "biome ci . && vitest run && tsup",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"port",
|
|
28
|
+
"kill",
|
|
29
|
+
"process",
|
|
30
|
+
"cli",
|
|
31
|
+
"lsof",
|
|
32
|
+
"free",
|
|
33
|
+
"dev",
|
|
34
|
+
"server",
|
|
35
|
+
"listen",
|
|
36
|
+
"address-in-use"
|
|
37
|
+
],
|
|
38
|
+
"author": "Miguel Medeiros",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/MiguelMedeiros/redpill.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/MiguelMedeiros/redpill#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/MiguelMedeiros/redpill/issues"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@biomejs/biome": "^2.3.15",
|
|
53
|
+
"@types/node": "^25.2.3",
|
|
54
|
+
"tsup": "^8",
|
|
55
|
+
"typescript": "^5",
|
|
56
|
+
"vitest": "^4.0.18"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"picocolors": "^1"
|
|
60
|
+
}
|
|
61
|
+
}
|