npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sriinnu/portpilot

v3.4.0

Published

Port management from your menu bar — monitor ports, discover daemons, proxy traffic, kill processes. macOS app + cross-platform CLI + rich terminal UI.

Readme

PortPilot

PortPilot inspects what is running on the machine: listening TCP/UDP ports, Unix-socket daemons, established connections, and cronjobs. It ships as a macOS menu bar app (PortPilot.app), a cross-platform CLI (portpilot), and a terminal UI (portpilot-tui). It classifies processes by executable path, kills or pauses processes by port, guards ports against squatters, tracks port lifecycle events on a timeline, and runs a local TCP proxy.

Related docs: ARCHITECTURE.md (source tree, tech stack), CHANGELOG.md (release history).

Requirements

| Component | Requires | |-----------|----------| | macOS app | macOS 13.0+, Xcode 15+, Swift 5.9+, xcodegen | | CLI / TUI | Swift 5.9+ (macOS, Linux, WSL, Windows) | | npm scripts | Node.js 18+ (optional wrappers around the build commands) |

Installation

Homebrew (macOS app only)

brew install --cask sriinnu/tap/portpilot

Installs PortPilot.app to /Applications. Cask source: sriinnu/homebrew-tap. The cask installs only the app — build the CLI and TUI from source.

GitHub Releases

Download from the releases page. Tag builds attach:

  • PortPilot-macOS-app-unsigned.zip — ad-hoc signed, not notarized. On first launch, right-click the app and choose Open. A signed and notarized PortPilot-macOS-app.zip is attached when published from the maintainer's local release flow.
  • portpilot-macos-cli, portpilot-macos-tui — universal binaries (arm64 + x86_64)
  • portpilot-linux-cli, portpilot-linux-tui
  • SHA256SUMS.txt

Build from source (macOS: app + CLI + TUI)

git clone https://github.com/sriinnu/portpilot.git
cd portpilot
npm run release

Builds all three products and installs PortPilot.app to /Applications, portpilot and portpilot-tui to /usr/local/bin.

App only, without npm:

./scripts/dev-install.sh           # xcodegen + xcodebuild Release + install to /Applications
./scripts/dev-install.sh --clean   # same, after wiping .build and DerivedData

Build from source (Linux / WSL: CLI + TUI)

Install Swift via Swiftly if not present:

curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
swiftly install latest
swift --version

Build and install:

npm run setup:linux

# or directly
swift build -c release --product portpilot
swift build -c release --product portpilot-tui
sudo cp .build/release/portpilot .build/release/portpilot-tui /usr/local/bin/

Build from source (Windows: CLI)

Requires Swift for Windows:

swift build -c release --product portpilot

The TUI requires a POSIX terminal. On Windows, use WSL or the CLI.

Development

Run from the repo root.

swift test                                   # unit tests (parsers, cron math, blocklist, layout)
swift build                                  # debug build of CLI + TUI
swift build -c release --product portpilot
swift build -c release --product portpilot-tui

xcodegen generate                            # regenerate PortPilot.xcodeproj from project.yml
xcodebuild -project PortPilot.xcodeproj -scheme PortPilot \
    -configuration Release -derivedDataPath .build/xcode build

The Xcode scheme for the CLI target is PortPilotCLI, not portpilot — the two scheme names collide on case-insensitive filesystems (see CHANGELOG 3.1.0).

CI (.github/workflows/build.yml) runs swift test on macOS and Linux, builds the universal binaries and the app, and publishes the release artifacts listed above on v* tags.

npm scripts

| Script | Effect | |--------|--------| | npm run build | Build the macOS app (Release, via xcodegen + xcodebuild) | | npm run build:debug | Debug app build | | npm run build:cli / build:tui | swift build -c release for each product | | npm run build:all | App + CLI + TUI | | npm run build:all:linux | CLI + TUI | | npm run setup:linux | Build CLI + TUI and install to /usr/local/bin (uses sudo) | | npm run install:app / install:cli / install:tui | Copy built artifacts to /Applications / /usr/local/bin | | npm run release | build:all plus install of app, CLI, and TUI | | npm run dev | Debug app build and open it | | npm run uninstall | Remove /Applications/PortPilot.app and the two binaries | | npm run clean | Remove .build |

Repository layout

Sources/
├── PortPilot/        # macOS menu bar app (SwiftUI + AppKit)
├── TerminalTUI/      # zero-dependency TUI engine (library)
├── PortPilotTUI/     # terminal UI app built on TerminalTUI
├── PortManagerLib/   # shared port/socket/connection/cronjob discovery, classification, proxy
└── PortKillerCLI/    # CLI (swift-argument-parser)
Tests/PortPilotTests/ # unit tests; no shelling out to lsof/ps, no tty
scripts/              # dev-install.sh, create_release.sh, release_signed_macos.sh, generate_sparkle_key.sh
project.yml           # xcodegen spec for PortPilot.xcodeproj
Package.swift         # SwiftPM manifest (CLI, TUI, TerminalTUI, PortManagerLib)

File-level tree and tech stack: ARCHITECTURE.md.

CLI reference

portpilot                                  # list (default subcommand)
portpilot list --start 3000 --end 9999     # port range
portpilot list --proto tcp --json          # protocol filter, JSON output
portpilot kill 5173                        # SIGTERM the process on the port
portpilot kill 5173 --force                # SIGKILL
portpilot kill :8080                       # colon prefix accepted
portpilot kill --timeout 3000 5173         # graceful-kill timeout in ms
portpilot pause 5173                       # SIGSTOP — socket stays bound, state kept
portpilot resume 5173                      # SIGCONT — resumes where it froze
portpilot pid 8080                         # print PID for a port
portpilot pids 3000 3001 3002              # PIDs for multiple ports
portpilot find --start 8000 --end 8999     # free ports in a range
portpilot kill-all                         # kill every listed process
portpilot connections                      # established connections, grouped by process
portpilot connections --suspect            # only processes with >50 connections
portpilot connections --blocklist          # only blocklisted remote hosts
portpilot connections --kill 12345         # kill by PID
portpilot connections --json
portpilot cronjobs                         # user + system crontab entries
portpilot cronjobs --user-only             # personal crontab only
portpilot cronjobs --system-only           # /etc/crontab and /etc/cron.d only
portpilot cronjobs --json
portpilot docker                           # Docker containers behind ports
portpilot program-pids --program node      # PIDs for a program name
portpilot program-kill --program node      # kill all PIDs for a program name
portpilot proxy --port 1080 --ssh user@host  # runs ssh -D 1080 -N user@host (SOCKS)
portpilot tui                              # launch portpilot-tui
portpilot interactive                      # basic interactive kill mode

--json output fields for list: port, protocolName, pid, user, command, fullCommand, parentPID, startTime, workingDirectory, processPath, socketPath, cpuUsage, memoryMB.

TUI

portpilot-tui        # or: portpilot tui

Tabs: Ports, Sockets, Schedules, Connections. Blocklisted connections are marked in the Connections tab.

| Key | Action | |-----|--------| | ↑↓ / j k | Navigate | | Enter | Kill process (confirmation prompt) | | / | Search / clear filter | | Tab | Switch tab | | i | Process detail view (info + connections) | | r | Refresh | | q | Quit |

Configuration

~/.portpilot/blocklist.txt — one entry per line: domain, domain suffix (leading . matches subdomains), IP, IPv6 prefix, or CIDR range.

upload.dev
52.45.119.88
192.168.1.0/24
2a06:98c1:310b
.evil.com

Matching connections are flagged in portpilot connections, the TUI, and the menu bar app.

Platform support

| Platform | App | Port discovery | Connections | |----------|-----|----------------|-------------| | macOS 13+ | Menu bar + window | lsof + proc_pidpath | lsof | | Linux / WSL | - | ss -tulnp | ss | | Windows | - | netstat -ano + tasklist /FO CSV | netstat |

Using TerminalTUI as a library

TerminalTUI is exported as a standalone SwiftPM library (no dependencies):

dependencies: [
    .package(url: "https://github.com/sriinnu/portpilot.git", from: "3.0.0"),
],
targets: [
    .executableTarget(
        name: "MyApp",
        dependencies: [.product(name: "TerminalTUI", package: "portpilot")]
    ),
]
import TerminalTUI

struct MyScreen: TUIScreen {
    mutating func render(into screen: inout Screen) {
        screen.put(row: 0, col: 0, text: "Hello", style: ANSI.bold + ANSI.fg(.cyan))
    }
    mutating func handleKey(_ key: KeyEvent) -> ScreenAction {
        switch key {
        case .char("q"): return .quit
        default: return .`continue`
        }
    }
}

let app = TUIApp(screen: MyScreen())
app.run()

License

MIT — see LICENSE.