portslayer 1.1.0__tar.gz
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.
- portslayer-1.1.0/LICENSE +21 -0
- portslayer-1.1.0/PKG-INFO +359 -0
- portslayer-1.1.0/README.md +325 -0
- portslayer-1.1.0/portslayer/__init__.py +5 -0
- portslayer-1.1.0/portslayer/cli.py +306 -0
- portslayer-1.1.0/portslayer/core/__init__.py +5 -0
- portslayer-1.1.0/portslayer/core/models.py +52 -0
- portslayer-1.1.0/portslayer/core/port_scanner.py +331 -0
- portslayer-1.1.0/portslayer/core/process_killer.py +150 -0
- portslayer-1.1.0/portslayer/tui.py +268 -0
- portslayer-1.1.0/portslayer/utils/__init__.py +4 -0
- portslayer-1.1.0/portslayer/utils/platform_utils.py +35 -0
- portslayer-1.1.0/portslayer/utils/validators.py +44 -0
- portslayer-1.1.0/portslayer.egg-info/PKG-INFO +359 -0
- portslayer-1.1.0/portslayer.egg-info/SOURCES.txt +22 -0
- portslayer-1.1.0/portslayer.egg-info/dependency_links.txt +1 -0
- portslayer-1.1.0/portslayer.egg-info/entry_points.txt +3 -0
- portslayer-1.1.0/portslayer.egg-info/requires.txt +7 -0
- portslayer-1.1.0/portslayer.egg-info/top_level.txt +1 -0
- portslayer-1.1.0/pyproject.toml +59 -0
- portslayer-1.1.0/setup.cfg +4 -0
- portslayer-1.1.0/tests/test_port_scanner.py +167 -0
- portslayer-1.1.0/tests/test_process_killer.py +175 -0
- portslayer-1.1.0/tests/test_validators.py +57 -0
portslayer-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 PortSlayer Contributors
|
|
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.
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: portslayer
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Cross-platform CLI + interactive TUI to list and kill processes by port
|
|
5
|
+
Author: PortSlayer Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AppestoX/portslayer
|
|
8
|
+
Project-URL: Repository, https://github.com/AppestoX/portslayer
|
|
9
|
+
Project-URL: Issues, https://github.com/AppestoX/portslayer/issues
|
|
10
|
+
Keywords: port,process,kill,network,cli,tui,devtools
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: System :: Networking :: Monitoring
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: typer>=0.12
|
|
28
|
+
Requires-Dist: rich>=13
|
|
29
|
+
Requires-Dist: textual>=0.60
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# PortSlayer ⚡
|
|
36
|
+
|
|
37
|
+
> Cross-platform CLI + interactive TUI to list active ports and safely kill processes by port number.
|
|
38
|
+
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
[](https://python.org)
|
|
41
|
+
[](#)
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- **Interactive TUI dashboard** — just run `pk` (or `portslayer`): arrow keys to navigate, `/` to search, `k` to kill, live auto-refresh
|
|
48
|
+
- **Short alias** — every command works as both `portslayer <cmd>` and the 2-letter `pk <cmd>`
|
|
49
|
+
- **List all active ports** — port, PID, process name, protocol, state, addresses
|
|
50
|
+
- **Filter** by port number, process name, or protocol (TCP/UDP)
|
|
51
|
+
- **Kill processes by port or by process name** — with mandatory confirmation before any destructive action
|
|
52
|
+
- **JSON output** (`--json`) for scripting, plus built-in shell completion (`--install-completion`)
|
|
53
|
+
- **Cross-platform** — Linux (via `ss`/`lsof`), Windows (via `netstat`/`taskkill`), macOS (via `lsof`)
|
|
54
|
+
- **Install however you like** — pip, npx/npm, or Homebrew/Scoop
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Tech Stack
|
|
59
|
+
|
|
60
|
+
| Layer | Technology | Reason |
|
|
61
|
+
|-----------|-----------------------------|---------------------------------------------------------|
|
|
62
|
+
| TUI | Python + Textual | Full-screen keyboard-driven dashboard, no extra runtime for users |
|
|
63
|
+
| CLI | Python + Typer + Rich | Single language, beautiful tables, type-safe commands |
|
|
64
|
+
| Core | Pure Python stdlib | No external dependencies for port scanning / killing |
|
|
65
|
+
| Tests | pytest | Simple, fast, widely adopted |
|
|
66
|
+
| Packaging | pyproject.toml (setuptools) | PEP 517/518 standard, pip-installable |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
Pick whichever package manager you already have — they all install the same
|
|
73
|
+
tool.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install portslayer # Python / pip
|
|
77
|
+
npx @appestox/portslayer # Node.js — try it with zero install
|
|
78
|
+
npm install -g @appestox/portslayer # Node.js — install permanently
|
|
79
|
+
uvx portslayer # zero-install, no Python setup needed
|
|
80
|
+
brew install AppestoX/PortSlayer/portslayer # Homebrew (macOS/Linux)
|
|
81
|
+
scoop bucket add AppestoX https://github.com/AppestoX/scoop-bucket
|
|
82
|
+
scoop install portslayer # Scoop (Windows)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The npm, Homebrew, and Scoop packages are thin wrappers that install the
|
|
86
|
+
same `portslayer` PyPI package underneath — see
|
|
87
|
+
[`packaging/`](packaging/) for how each is built and published.
|
|
88
|
+
|
|
89
|
+
### Prerequisites
|
|
90
|
+
|
|
91
|
+
- Python 3.10 or later (the npm/Homebrew/Scoop wrappers install this
|
|
92
|
+
dependency for you if it's missing)
|
|
93
|
+
|
|
94
|
+
### Install from source
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
git clone https://github.com/AppestoX/portslayer.git
|
|
98
|
+
cd portslayer
|
|
99
|
+
pip install -e ".[dev]"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Linux — additional notes
|
|
103
|
+
|
|
104
|
+
No extra system packages required. `ss` is available by default on all modern
|
|
105
|
+
Linux distributions (`iproute2`). `lsof` is used as a fallback if `ss` is
|
|
106
|
+
not available.
|
|
107
|
+
|
|
108
|
+
To see processes owned by other users you must run with `sudo`:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
sudo portslayer list
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Windows — additional notes
|
|
115
|
+
|
|
116
|
+
Run PowerShell or Command Prompt **as Administrator** to see all processes.
|
|
117
|
+
`netstat` and `taskkill` are built into every Windows installation.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
### Interactive TUI (recommended)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pk
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
(`pk` is the short alias for `portslayer` — both commands are identical and installed together; use whichever you prefer. The rest of this doc uses `portslayer` for clarity, but `pk` works everywhere it does.)
|
|
130
|
+
|
|
131
|
+
Launches a full-screen dashboard — no flags to remember:
|
|
132
|
+
|
|
133
|
+
| Key | Action |
|
|
134
|
+
|-------------|----------------------------------|
|
|
135
|
+
| `↑` / `↓` | Navigate the port list |
|
|
136
|
+
| `/` | Search/filter (port, process, protocol, state) |
|
|
137
|
+
| `k` / `Del` | Kill the selected process (asks to confirm) |
|
|
138
|
+
| `r` | Refresh now (auto-refreshes every 3s anyway) |
|
|
139
|
+
| `Esc` | Clear search |
|
|
140
|
+
| `q` | Quit |
|
|
141
|
+
|
|
142
|
+
### CLI
|
|
143
|
+
|
|
144
|
+
#### List all active ports
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
portslayer list
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Sample output:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
154
|
+
│ Active Ports (24 entries) │
|
|
155
|
+
├──────┬──────────┬─────────────┬───────┬────────────┬──────────────┤
|
|
156
|
+
│ Port │ Protocol │ State │ PID │ Process │ Local Address│
|
|
157
|
+
├──────┼──────────┼─────────────┼───────┼────────────┼──────────────┤
|
|
158
|
+
│ 22 │ TCP │ LISTEN │ 783 │ sshd │ 0.0.0.0:22 │
|
|
159
|
+
│ 80 │ TCP │ LISTEN │ 1042 │ nginx │ 0.0.0.0:80 │
|
|
160
|
+
│ 5432 │ TCP │ LISTEN │ 2103 │ postgres │ 127.0.0.1:… │
|
|
161
|
+
╰──────┴──────────┴─────────────┴───────┴────────────┴──────────────╯
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Filter ports
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# By exact port number
|
|
168
|
+
portslayer list --port 443
|
|
169
|
+
|
|
170
|
+
# By partial port prefix — matches every port starting with these digits
|
|
171
|
+
portslayer list --port 808 # -> 8080, 8081, 8083, ...
|
|
172
|
+
|
|
173
|
+
# By process name (partial match)
|
|
174
|
+
portslayer list --process nginx
|
|
175
|
+
|
|
176
|
+
# By protocol
|
|
177
|
+
portslayer list --protocol tcp
|
|
178
|
+
|
|
179
|
+
# Only LISTENING ports
|
|
180
|
+
portslayer list --listening
|
|
181
|
+
|
|
182
|
+
# Combine filters
|
|
183
|
+
portslayer list --protocol tcp --listening
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Inspect a specific port — or a partial prefix
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
portslayer find 8080 # exact port
|
|
190
|
+
portslayer find 808 # partial prefix -> matches 8080, 8081, 8083, ...
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Typing fewer digits is a feature, not an error — no need to remember or
|
|
194
|
+
type the full number if you just want to see everything in that range.
|
|
195
|
+
|
|
196
|
+
#### Kill the process on a port
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
portslayer kill 3000
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`kill` always requires an **exact** port number (not a prefix) — this is a
|
|
203
|
+
deliberate safety choice so a mistyped digit can't broaden which processes
|
|
204
|
+
get terminated. Use `find`/`list` with a prefix first to see what's there,
|
|
205
|
+
then `kill` the exact port you want.
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Kill by process name instead of port (kills every port that process owns)
|
|
209
|
+
portslayer kill --name node
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
PortSlayer will:
|
|
213
|
+
1. Show full process details
|
|
214
|
+
2. Ask for explicit confirmation
|
|
215
|
+
3. Kill the process only after `y` is entered
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Skip confirmation (use with care)
|
|
219
|
+
portslayer kill 3000 --force
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Scripting with JSON output
|
|
223
|
+
|
|
224
|
+
`list` and `find` both accept `--json` for piping into `jq` or other tools
|
|
225
|
+
instead of printing a table:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
portslayer list --json | jq '.[] | select(.protocol == "TCP")'
|
|
229
|
+
portslayer find 808 --json
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Shell completion
|
|
233
|
+
|
|
234
|
+
Typer wires this up automatically — no extra setup needed:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
portslayer --install-completion # installs completion for your current shell
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### Version
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
portslayer --version
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Project Structure
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
portslayer/
|
|
252
|
+
├── portslayer/
|
|
253
|
+
│ ├── __init__.py # Package version / metadata
|
|
254
|
+
│ ├── cli.py # Typer CLI (list / find / kill) — bare invocation launches the TUI
|
|
255
|
+
│ ├── tui.py # Textual interactive dashboard
|
|
256
|
+
│ ├── core/
|
|
257
|
+
│ │ ├── __init__.py
|
|
258
|
+
│ │ ├── models.py # PortInfo dataclass
|
|
259
|
+
│ │ ├── port_scanner.py # Platform-specific scanning + parsers
|
|
260
|
+
│ │ └── process_killer.py# Safe process termination
|
|
261
|
+
│ └── utils/
|
|
262
|
+
│ ├── __init__.py
|
|
263
|
+
│ ├── platform_utils.py# OS detection, admin check
|
|
264
|
+
│ └── validators.py # Port number validation
|
|
265
|
+
├── tests/
|
|
266
|
+
│ ├── test_validators.py
|
|
267
|
+
│ ├── test_port_scanner.py
|
|
268
|
+
│ └── test_process_killer.py
|
|
269
|
+
├── packaging/
|
|
270
|
+
│ ├── npm/ # npm wrapper package (`npx @appestox/portslayer`)
|
|
271
|
+
│ ├── homebrew/ # Homebrew tap formula
|
|
272
|
+
│ └── scoop/ # Scoop bucket manifest
|
|
273
|
+
├── pyproject.toml
|
|
274
|
+
├── requirements.txt
|
|
275
|
+
├── requirements-dev.txt
|
|
276
|
+
├── LICENSE
|
|
277
|
+
└── README.md
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Running Tests
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
pip install -e ".[dev]"
|
|
286
|
+
pytest
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
With coverage:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
pytest --cov=portslayer --cov-report=term-missing
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Security
|
|
298
|
+
|
|
299
|
+
| Concern | Mitigation |
|
|
300
|
+
|----------------------------|------------------------------------------------------------------------|
|
|
301
|
+
| Command injection | All subprocess calls use **explicit argument lists** — never `shell=True` |
|
|
302
|
+
| Input validation | Port numbers are validated (regex + range check) before any system call |
|
|
303
|
+
| Confirmation requirement | Every kill operation requires explicit user confirmation |
|
|
304
|
+
| Privilege transparency | Users are warned when not running as root/Administrator |
|
|
305
|
+
| PID validation | PID existence is verified before sending signals |
|
|
306
|
+
|
|
307
|
+
**PortSlayer never executes raw shell input from the user.**
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Platform-Specific Commands Used
|
|
312
|
+
|
|
313
|
+
| Platform | List ports | Get process names | Kill process |
|
|
314
|
+
|----------|--------------------------------|-------------------------|-----------------------------|
|
|
315
|
+
| Linux | `ss -tulnp` → `lsof -i -n -P` | embedded in `ss` output | `os.kill(pid, SIGKILL)` |
|
|
316
|
+
| Windows | `netstat -ano` | `tasklist /FO CSV /NH` | `taskkill /PID <pid> /F` |
|
|
317
|
+
| macOS | `lsof -i -n -P` | embedded in `lsof` | `os.kill(pid, SIGKILL)` |
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Publishing (maintainers)
|
|
322
|
+
|
|
323
|
+
Order matters — the npm, Homebrew, and Scoop packages all install the PyPI
|
|
324
|
+
package under the hood, so PyPI goes first.
|
|
325
|
+
|
|
326
|
+
1. **PyPI** (source of truth):
|
|
327
|
+
```bash
|
|
328
|
+
python -m pip install build twine
|
|
329
|
+
python -m build
|
|
330
|
+
twine upload dist/*
|
|
331
|
+
```
|
|
332
|
+
2. **npm wrapper** (`packaging/npm/`) — bump `version` in
|
|
333
|
+
`packaging/npm/package.json` to match the PyPI release, then:
|
|
334
|
+
```bash
|
|
335
|
+
cd packaging/npm && npm publish
|
|
336
|
+
```
|
|
337
|
+
3. **Homebrew tap** (`packaging/homebrew/`) — see
|
|
338
|
+
[`packaging/homebrew/README.md`](packaging/homebrew/README.md) for
|
|
339
|
+
generating dependency resource blocks and the sdist checksum, then push
|
|
340
|
+
to your `homebrew-portslayer` tap repo.
|
|
341
|
+
4. **Scoop bucket** (`packaging/scoop/`) — see
|
|
342
|
+
[`packaging/scoop/README.md`](packaging/scoop/README.md) for the
|
|
343
|
+
checksum step, then push to your bucket repo. Winget requires a compiled
|
|
344
|
+
installer and is documented as a follow-up in the same file.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Contributing
|
|
349
|
+
|
|
350
|
+
1. Fork the repository
|
|
351
|
+
2. Create a feature branch: `git checkout -b feat/my-feature`
|
|
352
|
+
3. Run tests: `pytest`
|
|
353
|
+
4. Open a pull request
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## License
|
|
358
|
+
|
|
359
|
+
[MIT](LICENSE) — free to use, modify, and distribute.
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# PortSlayer ⚡
|
|
2
|
+
|
|
3
|
+
> Cross-platform CLI + interactive TUI to list active ports and safely kill processes by port number.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://python.org)
|
|
7
|
+
[](#)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Interactive TUI dashboard** — just run `pk` (or `portslayer`): arrow keys to navigate, `/` to search, `k` to kill, live auto-refresh
|
|
14
|
+
- **Short alias** — every command works as both `portslayer <cmd>` and the 2-letter `pk <cmd>`
|
|
15
|
+
- **List all active ports** — port, PID, process name, protocol, state, addresses
|
|
16
|
+
- **Filter** by port number, process name, or protocol (TCP/UDP)
|
|
17
|
+
- **Kill processes by port or by process name** — with mandatory confirmation before any destructive action
|
|
18
|
+
- **JSON output** (`--json`) for scripting, plus built-in shell completion (`--install-completion`)
|
|
19
|
+
- **Cross-platform** — Linux (via `ss`/`lsof`), Windows (via `netstat`/`taskkill`), macOS (via `lsof`)
|
|
20
|
+
- **Install however you like** — pip, npx/npm, or Homebrew/Scoop
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Tech Stack
|
|
25
|
+
|
|
26
|
+
| Layer | Technology | Reason |
|
|
27
|
+
|-----------|-----------------------------|---------------------------------------------------------|
|
|
28
|
+
| TUI | Python + Textual | Full-screen keyboard-driven dashboard, no extra runtime for users |
|
|
29
|
+
| CLI | Python + Typer + Rich | Single language, beautiful tables, type-safe commands |
|
|
30
|
+
| Core | Pure Python stdlib | No external dependencies for port scanning / killing |
|
|
31
|
+
| Tests | pytest | Simple, fast, widely adopted |
|
|
32
|
+
| Packaging | pyproject.toml (setuptools) | PEP 517/518 standard, pip-installable |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Pick whichever package manager you already have — they all install the same
|
|
39
|
+
tool.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install portslayer # Python / pip
|
|
43
|
+
npx @appestox/portslayer # Node.js — try it with zero install
|
|
44
|
+
npm install -g @appestox/portslayer # Node.js — install permanently
|
|
45
|
+
uvx portslayer # zero-install, no Python setup needed
|
|
46
|
+
brew install AppestoX/PortSlayer/portslayer # Homebrew (macOS/Linux)
|
|
47
|
+
scoop bucket add AppestoX https://github.com/AppestoX/scoop-bucket
|
|
48
|
+
scoop install portslayer # Scoop (Windows)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The npm, Homebrew, and Scoop packages are thin wrappers that install the
|
|
52
|
+
same `portslayer` PyPI package underneath — see
|
|
53
|
+
[`packaging/`](packaging/) for how each is built and published.
|
|
54
|
+
|
|
55
|
+
### Prerequisites
|
|
56
|
+
|
|
57
|
+
- Python 3.10 or later (the npm/Homebrew/Scoop wrappers install this
|
|
58
|
+
dependency for you if it's missing)
|
|
59
|
+
|
|
60
|
+
### Install from source
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/AppestoX/portslayer.git
|
|
64
|
+
cd portslayer
|
|
65
|
+
pip install -e ".[dev]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Linux — additional notes
|
|
69
|
+
|
|
70
|
+
No extra system packages required. `ss` is available by default on all modern
|
|
71
|
+
Linux distributions (`iproute2`). `lsof` is used as a fallback if `ss` is
|
|
72
|
+
not available.
|
|
73
|
+
|
|
74
|
+
To see processes owned by other users you must run with `sudo`:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sudo portslayer list
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Windows — additional notes
|
|
81
|
+
|
|
82
|
+
Run PowerShell or Command Prompt **as Administrator** to see all processes.
|
|
83
|
+
`netstat` and `taskkill` are built into every Windows installation.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
### Interactive TUI (recommended)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pk
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
(`pk` is the short alias for `portslayer` — both commands are identical and installed together; use whichever you prefer. The rest of this doc uses `portslayer` for clarity, but `pk` works everywhere it does.)
|
|
96
|
+
|
|
97
|
+
Launches a full-screen dashboard — no flags to remember:
|
|
98
|
+
|
|
99
|
+
| Key | Action |
|
|
100
|
+
|-------------|----------------------------------|
|
|
101
|
+
| `↑` / `↓` | Navigate the port list |
|
|
102
|
+
| `/` | Search/filter (port, process, protocol, state) |
|
|
103
|
+
| `k` / `Del` | Kill the selected process (asks to confirm) |
|
|
104
|
+
| `r` | Refresh now (auto-refreshes every 3s anyway) |
|
|
105
|
+
| `Esc` | Clear search |
|
|
106
|
+
| `q` | Quit |
|
|
107
|
+
|
|
108
|
+
### CLI
|
|
109
|
+
|
|
110
|
+
#### List all active ports
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
portslayer list
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Sample output:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
120
|
+
│ Active Ports (24 entries) │
|
|
121
|
+
├──────┬──────────┬─────────────┬───────┬────────────┬──────────────┤
|
|
122
|
+
│ Port │ Protocol │ State │ PID │ Process │ Local Address│
|
|
123
|
+
├──────┼──────────┼─────────────┼───────┼────────────┼──────────────┤
|
|
124
|
+
│ 22 │ TCP │ LISTEN │ 783 │ sshd │ 0.0.0.0:22 │
|
|
125
|
+
│ 80 │ TCP │ LISTEN │ 1042 │ nginx │ 0.0.0.0:80 │
|
|
126
|
+
│ 5432 │ TCP │ LISTEN │ 2103 │ postgres │ 127.0.0.1:… │
|
|
127
|
+
╰──────┴──────────┴─────────────┴───────┴────────────┴──────────────╯
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Filter ports
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# By exact port number
|
|
134
|
+
portslayer list --port 443
|
|
135
|
+
|
|
136
|
+
# By partial port prefix — matches every port starting with these digits
|
|
137
|
+
portslayer list --port 808 # -> 8080, 8081, 8083, ...
|
|
138
|
+
|
|
139
|
+
# By process name (partial match)
|
|
140
|
+
portslayer list --process nginx
|
|
141
|
+
|
|
142
|
+
# By protocol
|
|
143
|
+
portslayer list --protocol tcp
|
|
144
|
+
|
|
145
|
+
# Only LISTENING ports
|
|
146
|
+
portslayer list --listening
|
|
147
|
+
|
|
148
|
+
# Combine filters
|
|
149
|
+
portslayer list --protocol tcp --listening
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Inspect a specific port — or a partial prefix
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
portslayer find 8080 # exact port
|
|
156
|
+
portslayer find 808 # partial prefix -> matches 8080, 8081, 8083, ...
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Typing fewer digits is a feature, not an error — no need to remember or
|
|
160
|
+
type the full number if you just want to see everything in that range.
|
|
161
|
+
|
|
162
|
+
#### Kill the process on a port
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
portslayer kill 3000
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`kill` always requires an **exact** port number (not a prefix) — this is a
|
|
169
|
+
deliberate safety choice so a mistyped digit can't broaden which processes
|
|
170
|
+
get terminated. Use `find`/`list` with a prefix first to see what's there,
|
|
171
|
+
then `kill` the exact port you want.
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Kill by process name instead of port (kills every port that process owns)
|
|
175
|
+
portslayer kill --name node
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
PortSlayer will:
|
|
179
|
+
1. Show full process details
|
|
180
|
+
2. Ask for explicit confirmation
|
|
181
|
+
3. Kill the process only after `y` is entered
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Skip confirmation (use with care)
|
|
185
|
+
portslayer kill 3000 --force
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Scripting with JSON output
|
|
189
|
+
|
|
190
|
+
`list` and `find` both accept `--json` for piping into `jq` or other tools
|
|
191
|
+
instead of printing a table:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
portslayer list --json | jq '.[] | select(.protocol == "TCP")'
|
|
195
|
+
portslayer find 808 --json
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Shell completion
|
|
199
|
+
|
|
200
|
+
Typer wires this up automatically — no extra setup needed:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
portslayer --install-completion # installs completion for your current shell
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Version
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
portslayer --version
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Project Structure
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
portslayer/
|
|
218
|
+
├── portslayer/
|
|
219
|
+
│ ├── __init__.py # Package version / metadata
|
|
220
|
+
│ ├── cli.py # Typer CLI (list / find / kill) — bare invocation launches the TUI
|
|
221
|
+
│ ├── tui.py # Textual interactive dashboard
|
|
222
|
+
│ ├── core/
|
|
223
|
+
│ │ ├── __init__.py
|
|
224
|
+
│ │ ├── models.py # PortInfo dataclass
|
|
225
|
+
│ │ ├── port_scanner.py # Platform-specific scanning + parsers
|
|
226
|
+
│ │ └── process_killer.py# Safe process termination
|
|
227
|
+
│ └── utils/
|
|
228
|
+
│ ├── __init__.py
|
|
229
|
+
│ ├── platform_utils.py# OS detection, admin check
|
|
230
|
+
│ └── validators.py # Port number validation
|
|
231
|
+
├── tests/
|
|
232
|
+
│ ├── test_validators.py
|
|
233
|
+
│ ├── test_port_scanner.py
|
|
234
|
+
│ └── test_process_killer.py
|
|
235
|
+
├── packaging/
|
|
236
|
+
│ ├── npm/ # npm wrapper package (`npx @appestox/portslayer`)
|
|
237
|
+
│ ├── homebrew/ # Homebrew tap formula
|
|
238
|
+
│ └── scoop/ # Scoop bucket manifest
|
|
239
|
+
├── pyproject.toml
|
|
240
|
+
├── requirements.txt
|
|
241
|
+
├── requirements-dev.txt
|
|
242
|
+
├── LICENSE
|
|
243
|
+
└── README.md
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Running Tests
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
pip install -e ".[dev]"
|
|
252
|
+
pytest
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
With coverage:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
pytest --cov=portslayer --cov-report=term-missing
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Security
|
|
264
|
+
|
|
265
|
+
| Concern | Mitigation |
|
|
266
|
+
|----------------------------|------------------------------------------------------------------------|
|
|
267
|
+
| Command injection | All subprocess calls use **explicit argument lists** — never `shell=True` |
|
|
268
|
+
| Input validation | Port numbers are validated (regex + range check) before any system call |
|
|
269
|
+
| Confirmation requirement | Every kill operation requires explicit user confirmation |
|
|
270
|
+
| Privilege transparency | Users are warned when not running as root/Administrator |
|
|
271
|
+
| PID validation | PID existence is verified before sending signals |
|
|
272
|
+
|
|
273
|
+
**PortSlayer never executes raw shell input from the user.**
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Platform-Specific Commands Used
|
|
278
|
+
|
|
279
|
+
| Platform | List ports | Get process names | Kill process |
|
|
280
|
+
|----------|--------------------------------|-------------------------|-----------------------------|
|
|
281
|
+
| Linux | `ss -tulnp` → `lsof -i -n -P` | embedded in `ss` output | `os.kill(pid, SIGKILL)` |
|
|
282
|
+
| Windows | `netstat -ano` | `tasklist /FO CSV /NH` | `taskkill /PID <pid> /F` |
|
|
283
|
+
| macOS | `lsof -i -n -P` | embedded in `lsof` | `os.kill(pid, SIGKILL)` |
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Publishing (maintainers)
|
|
288
|
+
|
|
289
|
+
Order matters — the npm, Homebrew, and Scoop packages all install the PyPI
|
|
290
|
+
package under the hood, so PyPI goes first.
|
|
291
|
+
|
|
292
|
+
1. **PyPI** (source of truth):
|
|
293
|
+
```bash
|
|
294
|
+
python -m pip install build twine
|
|
295
|
+
python -m build
|
|
296
|
+
twine upload dist/*
|
|
297
|
+
```
|
|
298
|
+
2. **npm wrapper** (`packaging/npm/`) — bump `version` in
|
|
299
|
+
`packaging/npm/package.json` to match the PyPI release, then:
|
|
300
|
+
```bash
|
|
301
|
+
cd packaging/npm && npm publish
|
|
302
|
+
```
|
|
303
|
+
3. **Homebrew tap** (`packaging/homebrew/`) — see
|
|
304
|
+
[`packaging/homebrew/README.md`](packaging/homebrew/README.md) for
|
|
305
|
+
generating dependency resource blocks and the sdist checksum, then push
|
|
306
|
+
to your `homebrew-portslayer` tap repo.
|
|
307
|
+
4. **Scoop bucket** (`packaging/scoop/`) — see
|
|
308
|
+
[`packaging/scoop/README.md`](packaging/scoop/README.md) for the
|
|
309
|
+
checksum step, then push to your bucket repo. Winget requires a compiled
|
|
310
|
+
installer and is documented as a follow-up in the same file.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Contributing
|
|
315
|
+
|
|
316
|
+
1. Fork the repository
|
|
317
|
+
2. Create a feature branch: `git checkout -b feat/my-feature`
|
|
318
|
+
3. Run tests: `pytest`
|
|
319
|
+
4. Open a pull request
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## License
|
|
324
|
+
|
|
325
|
+
[MIT](LICENSE) — free to use, modify, and distribute.
|