@zackify/pi-port-forward

A pi extension that adds an interactive SSH local port forwarding command.

Package details

extension

Install @zackify/pi-port-forward from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@zackify/pi-port-forward
Package
@zackify/pi-port-forward
Version
1.0.3
Published
Apr 30, 2026
Downloads
not available
Author
zackify
License
MIT
Types
extension
Size
113.9 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./index.ts"
  ],
  "image": "https://raw.githubusercontent.com/zackify/pi-port-forward/master/assets/pi-port-forward.png"
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-port-forward

pi-port-forward screenshot

A reusable pi package that adds a /port slash command for SSH local port forwarding.

It lists listening TCP ports on configured SSH remotes, shows the process name / PID when available, and lets you toggle forwards from inside pi.

Features

  • Configurable slash command name, default /port
  • Multiple SSH remotes via settings.json
  • Configurable allowed ports and port ranges
  • Configurable local bind host, remote target host, SSH options, and local port offsets
  • Interactive picker:
    • Space toggles the highlighted port and keeps the picker open
    • Enter toggles the highlighted port and closes
    • Esc closes
  • Persistent Forwarded ports widget above the input editor
  • Forwards are stopped when untoggled, when pi reloads/shuts down, or when the parent pi process disappears

Requirements

  • pi with extension/package support
  • SSH access to each configured remote
  • Key-based or otherwise non-interactive SSH auth; the extension uses BatchMode=yes
  • ss or netstat on the remote host
  • Free local ports for the forwards you start

Install

From npm

After this package is published:

pi install npm:@zackify/pi-port-forward

Then reload pi:

/reload

Manual install

Copy the extension into your global pi extensions directory:

mkdir -p ~/.pi/agent/extensions
cp index.ts ~/.pi/agent/extensions/pi-port-forward.ts

Or install it only for one project:

mkdir -p .pi/extensions
cp index.ts .pi/extensions/pi-port-forward.ts

Then add config to ~/.pi/agent/settings.json and reload pi.

Config

The extension reads portForward from ~/.pi/agent/settings.json.

Minimal example using an SSH config alias named dev:

{
  "portForward": {
    "providers": [
      {
        "id": "dev",
        "remote": "dev",
        "label": "dev",
        "allowedPorts": [3000, 5173, { "from": 8000, "to": 8999 }]
      }
    ]
  }
}

Multiple remotes are supported:

{
  "portForward": {
    "command": "port",
    "providers": [
      {
        "id": "dev",
        "remote": "me@dev.example.com",
        "label": "dev",
        "allowedPorts": [3000, { "from": 8080, "to": 9000 }]
      },
      {
        "id": "staging",
        "remote": "staging",
        "label": "staging",
        "allowedPorts": [5173, { "from": 8000, "to": 8999 }]
      }
    ]
  }
}

Avoid local port collisions with localPortOffset:

{
  "portForward": {
    "providers": [
      {
        "id": "dev",
        "remote": "dev",
        "allowedPorts": [3000]
      },
      {
        "id": "staging",
        "remote": "staging",
        "allowedPorts": [3000],
        "localPortOffset": 10000
      }
    ]
  }
}

This maps dev:3000 to 127.0.0.1:3000 and staging:3000 to 127.0.0.1:13000.

Config reference

Field Type Default Description
command string "port" Slash command name, with or without leading /.
maxVisible number 15 Maximum visible picker rows before scrolling.
providers array [] SSH remotes to scan. No personal default remote is configured.
providers[].id string derived from label Stable unique provider ID used for forward state.
providers[].remote string required SSH target, e.g. user@host or an SSH config alias. ssh is also accepted as an alias.
providers[].label string host part of remote Display label. hostLabel is also accepted as an alias.
providers[].allowedPorts array [3000, {"from":8080,"to":9000}] Ports shown in the picker. Entries may be numbers or { "from": number, "to": number } ranges.
providers[].localHost string "127.0.0.1" Local bind address for ssh -L.
providers[].remoteHost string "127.0.0.1" Target host as seen from the remote SSH server.
providers[].localPortOffset number 0 Added to the remote port to choose the local port. Useful when two remotes expose the same port.
providers[].sshOptions string[] [] Extra arguments passed to ssh, for example ["-J", "jump-host"].

Usage

Reload pi after installing/configuring:

/reload

Then run:

/port

Use arrow keys to select a port. Press Space to toggle and keep the picker open, or Enter to toggle and close.

Security notes

Only configure remotes you trust. The extension runs ssh <remote> "ss ... || netstat ..." to list listening ports and opens local SSH forwards. Use allowedPorts to limit what appears in the picker.

Forwards bind to 127.0.0.1 by default so they are only reachable from your local machine unless you explicitly change localHost.

Troubleshooting

No listening TCP ports found

  • Check that the service is running on the remote.
  • Check that its port is included in allowedPorts.
  • Run ssh <remote> 'ss -H -ltnp || netstat -ltnp' manually to verify output.

Could not list ports

  • Check SSH connectivity: ssh <remote>.
  • Ensure non-interactive auth works. Password prompts fail because BatchMode=yes is used.
  • Check SSH aliases in ~/.ssh/config.

localhost port is already in use

  • Stop the local process using that port, or configure localPortOffset for that provider.

Process name is unknown

  • Some systems hide process info from ss / netstat unless run with elevated permissions.
  • The forward can still work even if the process name is unknown.

Releasing

This repository includes a GitHub Actions workflow that publishes to npm when a GitHub release is created.

  1. Add an NPM_TOKEN repository secret.
  2. Create a GitHub release named like v1.2.3 or 1.2.3.
  3. The workflow sets package.json to that version and runs npm publish --provenance --access public.

Files

  • index.ts — extension source
  • package.json — pi package metadata for @zackify/pi-port-forward, including the pi-package keyword
  • .github/workflows/publish.yml — npm publish workflow