@dprrwt/ports 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deepankar Rawat
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,158 @@
1
+ # ⚡ ports
2
+
3
+ > Beautiful port manager CLI — because `netstat -ano | findstr :3000` is a crime against developer experience.
4
+
5
+ See what's running. Kill by port. Scan ranges. Find free ports. All with colors and sanity.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @dprrwt/ports
11
+ ```
12
+
13
+ Or use without installing:
14
+
15
+ ```bash
16
+ npx @dprrwt/ports
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### List all listening ports
22
+
23
+ ```bash
24
+ ports
25
+ ```
26
+
27
+ ```
28
+ ⚡ ports — what's running on your machine
29
+
30
+ Listening Ports
31
+ ┌──────────────────────┬───────┬───────┬──────────┬───────────┬───────────┐
32
+ │ Port │ Proto │ PID │ Process │ State │ Address │
33
+ ├──────────────────────┼───────┼───────┼──────────┼───────────┼───────────┤
34
+ │ 3000 (Dev Server) │ TCP │ 12345 │ node │ LISTENING │ 0.0.0.0 │
35
+ ├──────────────────────┼───────┼───────┼──────────┼───────────┼───────────┤
36
+ │ 5432 (PostgreSQL) │ TCP │ 6789 │ postgres │ LISTENING │ 127.0.0.1 │
37
+ ├──────────────────────┼───────┼───────┼──────────┼───────────┼───────────┤
38
+ │ 8080 (HTTP Alt) │ TCP │ 4567 │ java │ LISTENING │ 0.0.0.0 │
39
+ └──────────────────────┴───────┴───────┴──────────┴───────────┴───────────┘
40
+ 3 ports found
41
+
42
+ Legend:
43
+ ■ JS Runtime ■ Python ■ Java ■ Container ■ Database ■ System ■ Unknown
44
+ ```
45
+
46
+ ### Check a specific port
47
+
48
+ ```bash
49
+ ports 3000
50
+ # or
51
+ ports check 3000
52
+ ```
53
+
54
+ ```
55
+ Port 3000 (Dev Server)
56
+
57
+ Process: node (PID 12345)
58
+ Proto: TCP
59
+ State: LISTENING
60
+ Address: 0.0.0.0:3000
61
+ ```
62
+
63
+ ### Kill a process by port
64
+
65
+ ```bash
66
+ ports kill 3000 # asks for confirmation
67
+ ports kill 3000 --yes # skip confirmation
68
+ ports kill 3000 -f --yes # force kill (SIGKILL)
69
+ ```
70
+
71
+ ### Scan a port range
72
+
73
+ ```bash
74
+ ports scan 3000-4000
75
+ ```
76
+
77
+ ```
78
+ Scanning ports 3000-4000
79
+ 2 in use · 999 free · 1001 total
80
+
81
+ ┌──────┬───────┬──────┬─────────┬───────────┬─────────┐
82
+ │ Port │ Proto │ PID │ Process │ State │ Address │
83
+ ├──────┼───────┼──────┼─────────┼───────────┼─────────┤
84
+ │ 3000 │ TCP │ 1234 │ node │ LISTENING │ 0.0.0.0 │
85
+ ├──────┼───────┼──────┼─────────┼───────────┼─────────┤
86
+ │ 3306 │ TCP │ 5678 │ mysqld │ LISTENING │ 0.0.0.0 │
87
+ └──────┴───────┴──────┴─────────┴───────────┴─────────┘
88
+ ```
89
+
90
+ ### Find next free port
91
+
92
+ ```bash
93
+ ports free # starts from 3000
94
+ ports free 8000 # starts from 8000
95
+ ```
96
+
97
+ ```
98
+ ✓ Next free port: 3000
99
+ ```
100
+
101
+ ## Features
102
+
103
+ - 🎨 **Color-coded processes** — JS runtimes (green), Python (blue), Java (yellow), databases (magenta), system (gray), unknown (red)
104
+ - 🏷️ **Smart port labels** — recognizes 30+ common ports (PostgreSQL, Redis, Vite, Django, etc.)
105
+ - 🛡️ **Safety warnings** — warns before killing critical processes
106
+ - 📊 **JSON output** — `--json` flag on any command for scripting
107
+ - 🖥️ **Cross-platform** — Windows (netstat) + macOS/Linux (ss/lsof)
108
+ - ⚡ **Fast** — process name caching, no unnecessary lookups
109
+
110
+ ## Options
111
+
112
+ | Flag | Description |
113
+ |------|-------------|
114
+ | `--json` | Output as JSON (works on all commands) |
115
+ | `--tcp` | Show only TCP ports (list command) |
116
+ | `--udp` | Show only UDP ports (list command) |
117
+ | `-a, --all` | Show all states, not just LISTENING |
118
+ | `-f, --force` | Force kill (SIGKILL / taskkill /F) |
119
+ | `-y, --yes` | Skip confirmation prompts |
120
+
121
+ ## Programmatic API
122
+
123
+ ```typescript
124
+ import { getListeningPorts, isPortInUse, findFreePort, killProcess } from "@dprrwt/ports";
125
+
126
+ // Get all listening ports
127
+ const ports = getListeningPorts();
128
+
129
+ // Check if a port is in use
130
+ if (isPortInUse(3000)) {
131
+ console.log("Port 3000 is taken!");
132
+ }
133
+
134
+ // Find next free port
135
+ const free = findFreePort(3000); // → 3001
136
+
137
+ // Kill a process
138
+ killProcess(12345, true); // force = true
139
+ ```
140
+
141
+ ## Why?
142
+
143
+ Every developer has done this dance:
144
+
145
+ 1. "Port 3000 is already in use"
146
+ 2. Google "how to find what's using port 3000"
147
+ 3. Copy-paste some arcane `netstat` or `lsof` incantation
148
+ 4. Parse the output with your eyes
149
+ 5. Find the PID
150
+ 6. Google "how to kill process by PID"
151
+ 7. Finally kill it
152
+ 8. Repeat next week because you forgot the commands
153
+
154
+ **`ports` makes this a one-liner.** See what's running, kill what you need, move on with your life.
155
+
156
+ ## License
157
+
158
+ MIT © [Deepankar Rawat](https://dprrwt.me)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ports — A beautiful port manager CLI
4
+ *
5
+ * Because `netstat -ano | findstr :3000` is a crime against developer experience.
6
+ *
7
+ * Usage:
8
+ * ports List all listening ports
9
+ * ports <port> Check what's on a specific port
10
+ * ports kill <port> Kill whatever's on that port
11
+ * ports scan <range> Scan a port range (e.g. 3000-4000)
12
+ * ports free [start] Find next available port
13
+ *
14
+ * @author Deepankar Rawat <dprrwt@gmail.com>
15
+ */
16
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ports — A beautiful port manager CLI
4
+ *
5
+ * Because `netstat -ano | findstr :3000` is a crime against developer experience.
6
+ *
7
+ * Usage:
8
+ * ports List all listening ports
9
+ * ports <port> Check what's on a specific port
10
+ * ports kill <port> Kill whatever's on that port
11
+ * ports scan <range> Scan a port range (e.g. 3000-4000)
12
+ * ports free [start] Find next available port
13
+ *
14
+ * @author Deepankar Rawat <dprrwt@gmail.com>
15
+ */
16
+ import { Command } from "commander";
17
+ import chalk from "chalk";
18
+ import { getListeningPorts, getPortInfo, killProcess, scanRange, findFreePort, } from "./scanner.js";
19
+ import { displayPortTable, displayPortDetail, displayScanRange, displayFreePort, displayKillResult, displayLegend, } from "./display.js";
20
+ const program = new Command();
21
+ program
22
+ .name("ports")
23
+ .description("Beautiful port manager — see, kill, scan, find")
24
+ .version("1.0.0")
25
+ .option("--json", "Output as JSON")
26
+ .option("--no-legend", "Hide color legend");
27
+ // Default command: list all ports
28
+ program
29
+ .command("list", { isDefault: true })
30
+ .description("List all listening ports")
31
+ .option("-a, --all", "Show all states (not just LISTENING)")
32
+ .option("--tcp", "Show only TCP ports")
33
+ .option("--udp", "Show only UDP ports")
34
+ .option("--json", "Output as JSON")
35
+ .action((opts) => {
36
+ let ports = getListeningPorts();
37
+ if (!opts.all) {
38
+ ports = ports.filter((p) => p.state === "LISTENING" || p.state === "LISTEN" || p.state === "*");
39
+ }
40
+ if (opts.tcp) {
41
+ ports = ports.filter((p) => p.protocol === "TCP");
42
+ }
43
+ if (opts.udp) {
44
+ ports = ports.filter((p) => p.protocol === "UDP");
45
+ }
46
+ if (opts.json || program.opts().json) {
47
+ console.log(JSON.stringify(ports, null, 2));
48
+ return;
49
+ }
50
+ console.log(chalk.bold.cyan("\n ⚡ ports") + chalk.dim(" — what's running on your machine\n"));
51
+ displayPortTable(ports, "Listening Ports");
52
+ displayLegend();
53
+ });
54
+ // Check specific port
55
+ program
56
+ .command("check <port>")
57
+ .description("Check what's using a specific port")
58
+ .option("--json", "Output as JSON")
59
+ .action((portStr, opts) => {
60
+ const port = parseInt(portStr, 10);
61
+ if (isNaN(port) || port < 1 || port > 65535) {
62
+ console.error(chalk.red(`\n ✗ Invalid port: ${portStr} (must be 1-65535)\n`));
63
+ process.exit(1);
64
+ }
65
+ const infos = getPortInfo(port);
66
+ if (opts.json || program.opts().json) {
67
+ console.log(JSON.stringify(infos, null, 2));
68
+ return;
69
+ }
70
+ displayPortDetail(port, infos);
71
+ });
72
+ // Kill process on port
73
+ program
74
+ .command("kill <port>")
75
+ .description("Kill the process using a port")
76
+ .option("-f, --force", "Force kill (SIGKILL / taskkill /F)")
77
+ .option("-y, --yes", "Skip confirmation")
78
+ .option("--json", "Output as JSON")
79
+ .action(async (portStr, opts) => {
80
+ const port = parseInt(portStr, 10);
81
+ if (isNaN(port) || port < 1 || port > 65535) {
82
+ console.error(chalk.red(`\n ✗ Invalid port: ${portStr}\n`));
83
+ process.exit(1);
84
+ }
85
+ const infos = getPortInfo(port);
86
+ if (infos.length === 0) {
87
+ console.log(chalk.yellow(`\n Nothing is running on port ${port}\n`));
88
+ return;
89
+ }
90
+ // Safety check: warn about system processes
91
+ const systemPorts = [18791, 18800]; // OpenClaw
92
+ if (systemPorts.includes(port)) {
93
+ console.log(chalk.red.bold(`\n ⚠ Port ${port} is used by OpenClaw — killing it may break your agent system!`));
94
+ if (!opts.yes) {
95
+ console.log(chalk.dim(" Use --yes to confirm\n"));
96
+ return;
97
+ }
98
+ }
99
+ for (const info of infos) {
100
+ if (!opts.yes) {
101
+ console.log(chalk.yellow(`\n Kill ${chalk.bold(info.processName)} (PID ${info.pid}) on port ${port}?`));
102
+ console.log(chalk.dim(" Use --yes to skip confirmation\n"));
103
+ return;
104
+ }
105
+ const success = killProcess(info.pid, opts.force);
106
+ if (opts.json || program.opts().json) {
107
+ console.log(JSON.stringify({ port, pid: info.pid, processName: info.processName, killed: success }));
108
+ }
109
+ else {
110
+ displayKillResult(port, info.pid, info.processName, success);
111
+ }
112
+ }
113
+ });
114
+ // Scan port range
115
+ program
116
+ .command("scan <range>")
117
+ .description("Scan a port range (e.g. 3000-4000)")
118
+ .option("--json", "Output as JSON")
119
+ .action((range, opts) => {
120
+ const match = range.match(/^(\d+)-(\d+)$/);
121
+ if (!match) {
122
+ console.error(chalk.red(`\n ✗ Invalid range: ${range} (use format: 3000-4000)\n`));
123
+ process.exit(1);
124
+ }
125
+ const startPort = parseInt(match[1], 10);
126
+ const endPort = parseInt(match[2], 10);
127
+ if (startPort > endPort || startPort < 1 || endPort > 65535) {
128
+ console.error(chalk.red(`\n ✗ Invalid range: ${startPort}-${endPort}\n`));
129
+ process.exit(1);
130
+ }
131
+ if (endPort - startPort > 10000) {
132
+ console.log(chalk.yellow(`\n Scanning ${endPort - startPort + 1} ports — this may take a moment...\n`));
133
+ }
134
+ const ports = scanRange(startPort, endPort);
135
+ if (opts.json || program.opts().json) {
136
+ console.log(JSON.stringify(ports, null, 2));
137
+ return;
138
+ }
139
+ displayScanRange(startPort, endPort, ports);
140
+ });
141
+ // Find free port
142
+ program
143
+ .command("free [startPort]")
144
+ .description("Find the next available port")
145
+ .option("--json", "Output as JSON")
146
+ .action((startStr, opts) => {
147
+ const startPort = startStr ? parseInt(startStr, 10) : 3000;
148
+ if (isNaN(startPort) || startPort < 1 || startPort > 65535) {
149
+ console.error(chalk.red(`\n ✗ Invalid start port: ${startStr}\n`));
150
+ process.exit(1);
151
+ }
152
+ const freePort = findFreePort(startPort);
153
+ if (opts.json || program.opts().json) {
154
+ console.log(JSON.stringify({ freePort, startedFrom: startPort }));
155
+ return;
156
+ }
157
+ displayFreePort(freePort, startPort);
158
+ });
159
+ // Handle bare port number: `ports 3000` → `ports check 3000`
160
+ const args = process.argv.slice(2);
161
+ if (args.length > 0 && /^\d+$/.test(args[0])) {
162
+ // Rewrite `ports 3000` → `ports check 3000`
163
+ process.argv.splice(2, 0, "check");
164
+ }
165
+ program.parse();
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Display Layer — Making port data beautiful
3
+ *
4
+ * netstat is powerful but ugly. The information is there — it just needs
5
+ * a human-friendly presentation. Like Da Vinci's anatomical drawings:
6
+ * the same body everyone could see, but rendered with clarity and beauty.
7
+ */
8
+ import type { PortInfo } from "./scanner.js";
9
+ /**
10
+ * Display a full port table
11
+ */
12
+ export declare function displayPortTable(ports: PortInfo[], title?: string): void;
13
+ /**
14
+ * Display compact port info (for single port lookup)
15
+ */
16
+ export declare function displayPortDetail(port: number, infos: PortInfo[]): void;
17
+ /**
18
+ * Display scan range results
19
+ */
20
+ export declare function displayScanRange(startPort: number, endPort: number, ports: PortInfo[]): void;
21
+ /**
22
+ * Display free port result
23
+ */
24
+ export declare function displayFreePort(port: number | null, startPort: number): void;
25
+ /**
26
+ * Display kill result
27
+ */
28
+ export declare function displayKillResult(port: number, pid: number, processName: string, success: boolean): void;
29
+ /**
30
+ * Display legend
31
+ */
32
+ export declare function displayLegend(): void;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Display Layer — Making port data beautiful
3
+ *
4
+ * netstat is powerful but ugly. The information is there — it just needs
5
+ * a human-friendly presentation. Like Da Vinci's anatomical drawings:
6
+ * the same body everyone could see, but rendered with clarity and beauty.
7
+ */
8
+ import chalk from "chalk";
9
+ import Table from "cli-table3";
10
+ // Well-known ports that developers care about
11
+ const KNOWN_PORTS = {
12
+ 80: "HTTP",
13
+ 443: "HTTPS",
14
+ 3000: "Dev Server",
15
+ 3001: "Dev Server",
16
+ 3333: "Dev Server",
17
+ 4000: "Dev Server",
18
+ 4200: "Angular",
19
+ 5000: "Flask/Vite",
20
+ 5173: "Vite",
21
+ 5174: "Vite",
22
+ 5432: "PostgreSQL",
23
+ 5500: "Live Server",
24
+ 6379: "Redis",
25
+ 8000: "Django/FastAPI",
26
+ 8080: "HTTP Alt",
27
+ 8443: "HTTPS Alt",
28
+ 8888: "Jupyter",
29
+ 9000: "PHP-FPM",
30
+ 9090: "Prometheus",
31
+ 9229: "Node Debug",
32
+ 18791: "OpenClaw",
33
+ 18800: "OpenClaw Browser",
34
+ 27017: "MongoDB",
35
+ 2960: "Mission Control",
36
+ };
37
+ // Process name to color category
38
+ function getProcessColor(name) {
39
+ const lower = name.toLowerCase();
40
+ if (lower.includes("node") || lower.includes("deno") || lower.includes("bun")) {
41
+ return chalk.green; // JS runtimes — green (your dev servers)
42
+ }
43
+ if (lower.includes("python") || lower.includes("flask") || lower.includes("django")) {
44
+ return chalk.blue; // Python
45
+ }
46
+ if (lower.includes("java") || lower.includes("gradle") || lower.includes("maven")) {
47
+ return chalk.yellow; // Java
48
+ }
49
+ if (lower.includes("docker") || lower.includes("containerd")) {
50
+ return chalk.cyan; // Containers
51
+ }
52
+ if (lower.includes("postgres") || lower.includes("mysql") || lower.includes("mongo") || lower.includes("redis")) {
53
+ return chalk.magenta; // Databases
54
+ }
55
+ if (lower === "system" || lower === "svchost" || lower.includes("system")) {
56
+ return chalk.gray; // System processes
57
+ }
58
+ if (lower === "unknown") {
59
+ return chalk.red; // Unknown — potential concern
60
+ }
61
+ return chalk.white; // Everything else
62
+ }
63
+ function getStateColor(state) {
64
+ switch (state) {
65
+ case "LISTENING":
66
+ return chalk.green(state);
67
+ case "ESTABLISHED":
68
+ return chalk.cyan(state);
69
+ case "TIME_WAIT":
70
+ return chalk.gray(state);
71
+ case "CLOSE_WAIT":
72
+ return chalk.yellow(state);
73
+ default:
74
+ return chalk.white(state);
75
+ }
76
+ }
77
+ function getPortLabel(port) {
78
+ const label = KNOWN_PORTS[port];
79
+ return label ? chalk.dim(` (${label})`) : "";
80
+ }
81
+ /**
82
+ * Display a full port table
83
+ */
84
+ export function displayPortTable(ports, title) {
85
+ if (ports.length === 0) {
86
+ console.log(chalk.yellow("\n No ports found.\n"));
87
+ return;
88
+ }
89
+ if (title) {
90
+ console.log(chalk.bold(`\n ${title}`));
91
+ }
92
+ const table = new Table({
93
+ head: [
94
+ chalk.bold.white("Port"),
95
+ chalk.bold.white("Proto"),
96
+ chalk.bold.white("PID"),
97
+ chalk.bold.white("Process"),
98
+ chalk.bold.white("State"),
99
+ chalk.bold.white("Address"),
100
+ ],
101
+ style: {
102
+ head: [],
103
+ border: ["gray"],
104
+ },
105
+ chars: {
106
+ top: "─",
107
+ "top-mid": "┬",
108
+ "top-left": "┌",
109
+ "top-right": "┐",
110
+ bottom: "─",
111
+ "bottom-mid": "┴",
112
+ "bottom-left": "└",
113
+ "bottom-right": "┘",
114
+ left: "│",
115
+ "left-mid": "├",
116
+ mid: "─",
117
+ "mid-mid": "┼",
118
+ right: "│",
119
+ "right-mid": "┤",
120
+ middle: "│",
121
+ },
122
+ });
123
+ for (const p of ports) {
124
+ const colorFn = getProcessColor(p.processName);
125
+ table.push([
126
+ chalk.bold.white(String(p.port)) + getPortLabel(p.port),
127
+ p.protocol === "UDP" ? chalk.yellow(p.protocol) : chalk.cyan(p.protocol),
128
+ chalk.dim(String(p.pid)),
129
+ colorFn(p.processName),
130
+ getStateColor(p.state),
131
+ chalk.dim(p.localAddress),
132
+ ]);
133
+ }
134
+ console.log(table.toString());
135
+ console.log(chalk.dim(` ${ports.length} port${ports.length === 1 ? "" : "s"} found\n`));
136
+ }
137
+ /**
138
+ * Display compact port info (for single port lookup)
139
+ */
140
+ export function displayPortDetail(port, infos) {
141
+ if (infos.length === 0) {
142
+ console.log(chalk.green(`\n ✓ Port ${port} is free\n`));
143
+ return;
144
+ }
145
+ console.log(chalk.bold(`\n Port ${port}`) + getPortLabel(port));
146
+ console.log();
147
+ for (const info of infos) {
148
+ const colorFn = getProcessColor(info.processName);
149
+ console.log(` ${chalk.dim("Process:")} ${colorFn(info.processName)} ${chalk.dim(`(PID ${info.pid})`)}`);
150
+ console.log(` ${chalk.dim("Proto:")} ${info.protocol}`);
151
+ console.log(` ${chalk.dim("State:")} ${getStateColor(info.state)}`);
152
+ console.log(` ${chalk.dim("Address:")} ${info.localAddress}:${port}`);
153
+ if (infos.indexOf(info) < infos.length - 1) {
154
+ console.log(chalk.dim(" ─────────────────────────"));
155
+ }
156
+ }
157
+ console.log();
158
+ }
159
+ /**
160
+ * Display scan range results
161
+ */
162
+ export function displayScanRange(startPort, endPort, ports) {
163
+ const total = endPort - startPort + 1;
164
+ const used = new Set(ports.map((p) => p.port)).size;
165
+ const free = total - used;
166
+ console.log(chalk.bold(`\n Scanning ports ${startPort}-${endPort}`));
167
+ console.log(` ${chalk.red(String(used))} in use · ${chalk.green(String(free))} free · ${chalk.dim(String(total) + " total")}`);
168
+ if (ports.length > 0) {
169
+ displayPortTable(ports);
170
+ }
171
+ else {
172
+ console.log(chalk.green(`\n ✓ All ${total} ports are free\n`));
173
+ }
174
+ }
175
+ /**
176
+ * Display free port result
177
+ */
178
+ export function displayFreePort(port, startPort) {
179
+ if (port === null) {
180
+ console.log(chalk.red(`\n ✗ No free ports found starting from ${startPort}\n`));
181
+ }
182
+ else {
183
+ console.log(chalk.green(`\n ✓ Next free port: ${chalk.bold.white(String(port))}\n`));
184
+ }
185
+ }
186
+ /**
187
+ * Display kill result
188
+ */
189
+ export function displayKillResult(port, pid, processName, success) {
190
+ if (success) {
191
+ console.log(chalk.green(`\n ✓ Killed ${processName} (PID ${pid}) on port ${port}\n`));
192
+ }
193
+ else {
194
+ console.log(chalk.red(`\n ✗ Failed to kill ${processName} (PID ${pid}) on port ${port}`));
195
+ console.log(chalk.dim(" Try running with --force or as administrator\n"));
196
+ }
197
+ }
198
+ /**
199
+ * Display legend
200
+ */
201
+ export function displayLegend() {
202
+ console.log(chalk.dim(" Legend:"));
203
+ console.log(` ${chalk.green("■")} JS Runtime ${chalk.blue("■")} Python ${chalk.yellow("■")} Java ${chalk.cyan("■")} Container ${chalk.magenta("■")} Database ${chalk.gray("■")} System ${chalk.red("■")} Unknown`);
204
+ console.log();
205
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @dprrwt/ports — Programmatic API
3
+ *
4
+ * Use the CLI for interactive use, or import these functions
5
+ * for programmatic port management in your Node.js scripts.
6
+ */
7
+ export { getListeningPorts, getPortInfo, isPortInUse, findFreePort, scanRange, killProcess, type PortInfo, } from "./scanner.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @dprrwt/ports — Programmatic API
3
+ *
4
+ * Use the CLI for interactive use, or import these functions
5
+ * for programmatic port management in your Node.js scripts.
6
+ */
7
+ export { getListeningPorts, getPortInfo, isPortInUse, findFreePort, scanRange, killProcess, } from "./scanner.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Port Scanner — Cross-platform port detection
3
+ *
4
+ * Da Vinci's curiosity: How do operating systems track network connections?
5
+ * Windows uses netstat, macOS/Linux use ss/lsof. Different tools, same truth.
6
+ * The abstraction layer here unifies them — like translating between languages
7
+ * to find the same underlying meaning.
8
+ */
9
+ export interface PortInfo {
10
+ port: number;
11
+ pid: number;
12
+ protocol: "TCP" | "UDP";
13
+ state: string;
14
+ localAddress: string;
15
+ processName: string;
16
+ }
17
+ /**
18
+ * Get all listening ports on the system
19
+ */
20
+ export declare function getListeningPorts(): PortInfo[];
21
+ /**
22
+ * Get info for a specific port
23
+ */
24
+ export declare function getPortInfo(port: number): PortInfo[];
25
+ /**
26
+ * Check if a port is in use
27
+ */
28
+ export declare function isPortInUse(port: number): boolean;
29
+ /**
30
+ * Find the next free port starting from a given port
31
+ */
32
+ export declare function findFreePort(startPort?: number, endPort?: number): number | null;
33
+ /**
34
+ * Scan a range of ports and return which are in use
35
+ */
36
+ export declare function scanRange(startPort: number, endPort: number): PortInfo[];
37
+ /**
38
+ * Kill a process by PID
39
+ */
40
+ export declare function killProcess(pid: number, force?: boolean): boolean;