Add getOpenPorts() — cross-platform port discovery for a terminal's process tree#114
Merged
Conversation
Discovers TCP listening ports opened by a shell process and all of its
descendant subprocesses, mapped back to the owning PID. Three native
backends, no new dependencies:
- Linux: scan /proc/<pid>/stat for the process tree, map /proc/<pid>/fd
socket inodes to LISTEN rows in /proc/net/tcp{,6}.
- macOS: ps for the tree, lsof -iTCP -sTCP:LISTEN for the ports.
- Windows: Get-CimInstance Win32_Process for the tree, Get-NetTCPConnection
(with a netstat -ano fallback) for the ports.
Returns { protocol, family, address, port, pid, processName } records,
de-duplicated by (family, address, port) and sorted by port, so callers
also learn which interface each socket is bound to.
Wires up a getOpenPorts(id, requestId) manager method and adds a node --test
script to the sidecar package so these (and the previously un-run sidecar)
tests execute in CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exposes the pty-core port discovery as PlatformAdapter.getOpenPorts(id), threaded end-to-end through every backend the same way getCwd is: - types.ts: OpenPort type + getOpenPorts() on PlatformAdapter. - VS Code: pty:getOpenPorts/pty:openPorts message types, message-router case, pty-manager.getOpenPorts() (4s timeout), pty-host case. - Tauri: sidecar main.js case, pty_get_open_ports Rust command (8s timeout) + handler registration, tauri-adapter method. - Fake adapter: getOpenPorts() backed by a settable map for playground/tests. Documents the mechanism and invariants in docs/specs/transport.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- pty-manager.ts: drop the OpenPortEntry interface and import the shared OpenPort type from lib (matches how message-types.ts sources it). - pty-core.js: add a runPowerShellJson() helper to collapse the repeated runPowerShell + JSON.parse + normalizeJsonArray dance at the two Win32_Process call sites. - pty-core.js: memoize /proc/<pid>/comm reads so a pid with multiple listening ports is only read once. - pty-core.js: drop the redundant try/catch in the getOpenPorts manager method — getOpenPortsForPid is already fail-soft, and getCwd doesn't wrap its helper either. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dormouse-bot
approved these changes
May 29, 2026
nedtwigg
commented
May 29, 2026
Member
Author
nedtwigg
left a comment
There was a problem hiding this comment.
Can we centralize the timeout to be OPEN_PORT_TIMEOUT somewhere in lib? Let's set it to 3 seconds
Comment on lines
+95
to
+103
| 1. **Process tree** (`getDescendantPids`) — Linux scans `/proc/<pid>/stat` for the pid→ppid table; macOS uses `ps -axo pid=,ppid=`; Windows uses `Get-CimInstance Win32_Process`. A shared BFS (`buildDescendantSet`) collects the root pid plus all transitive children. | ||
| 2. **Listening sockets** (`getListeningPortsForPids`) — Linux maps each pid's `/proc/<pid>/fd` socket inodes to `0A` (LISTEN) rows in `/proc/net/tcp{,6}`; macOS runs `lsof -nP -iTCP -sTCP:LISTEN -p <csv>`; Windows runs `Get-NetTCPConnection -State Listen` with a `netstat -ano` fallback for hosts lacking the cmdlet. | ||
|
|
||
| Invariants: | ||
|
|
||
| - **Listening TCP only.** Established/outbound connections and UDP are intentionally excluded — the signal is "what server is this terminal running," not raw connection churn. | ||
| - **Fail soft, never throw.** Any platform-specific failure (missing `/proc`, `lsof`/PowerShell error, timeout) yields `[]`, never an exception. The Tauri command uses an 8s timeout and the VS Code path a 4s timeout, both wider than the 1s cwd query because enumeration shells out on macOS/Windows. | ||
| - **`address` is the bind interface.** `0.0.0.0`/`::` mean all interfaces; `127.0.0.1`/`::1` mean loopback-only. Results are de-duplicated by `(family, address, port)` and sorted by port. | ||
|
|
Member
Author
There was a problem hiding this comment.
This part of the spec is unnecessary, redundant with the implementation.
Collaborator
There was a problem hiding this comment.
Done — dropped the platform-step breakdown and invariants list in 65304c9. Kept a source-of-truth pointer to getOpenPortsForPid() in the message-table row instead of a separate section.
Member
Author
|
@dormouse-bot please address the comments I made above, and also merge |
Define a single OPEN_PORT_TIMEOUT_MS=3000 constant in lib and use it at every transport boundary (vscode-adapter, pty-manager, Tauri pty_get_open_ports) and for the per-subprocess execs inside pty-core (ps, lsof, PowerShell, netstat). Rust and pty-core.js mirror the value with cross-reference comments since lib's TS source isn't importable from those runtimes. Drop the "Open-port discovery" section in transport.md — its platform-step breakdown and invariants list duplicated the implementation. The message-protocol row keeps a Source-of-truth pointer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # vscode-ext/src/message-types.ts
Deploying mouseterm with
|
| Latest commit: |
549d63a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d879d388.mouseterm.pages.dev |
| Branch Preview URL: | https://port-discovery.mouseterm.pages.dev |
Collaborator
|
Done — pushed 549d63a.
Local: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
getOpenPorts(id)query to thePlatformAdapterAPI. Given a terminal pane, it returns every TCP listening port opened by that pane's shell process and all of its descendant subprocesses — so you can answer "what server is this terminal running" (e.g. a dev server on:3000).Each record also carries the bind
address, so callers learn which interface(s) a socket is on (0.0.0.0/::= all interfaces,127.0.0.1/::1= loopback-only).How
Works out of the box for any Dormouse pane on any platform — Dormouse always holds the root shell PID of the PTYs it spawns, so no terminal-side cooperation is needed. Discovery is two native steps per OS, with zero new dependencies:
/proc/<pid>/stat/proc/<pid>/fdinodes →/proc/net/tcp{,6}LISTEN rowsps -axo pid=,ppid=lsof -iTCP -sTCP:LISTENGet-CimInstance Win32_ProcessGet-NetTCPConnection(→netstat -anofallback)The core (
getOpenPortsForPid) lives in the sharedstandalone/sidecar/pty-core.js(the VS Code extension loads it via thelib/pty-core.cjsshim) and is fail-soft — any platform error yields[], never an exception. It is wired end-to-end through every backend exactly the way the siblinggetCwdquery is:PlatformAdapter→ VS Code (pty-host/pty-manager/message-router) and Tauri (sidecar/main.js/pty_get_open_portsRust command /tauri-adapter), plus the fake adapter for tests/playground.Scope
Tests
pty-core.test.js), with injectedfs/execFileSyncfixtures. Also added atestscript to thedormouse-sidecarpackage so these (and the previously un-run sidecar tests) now execute in CI.127.0.0.1and0.0.0.0listeners are found, and that querying a parent PID surfaces a child process's port (the subprocess requirement).tsc -bclean, vscode-ext bundles,cargo checkclean, full lib + fake-adapter suites pass.🤖 Generated with Claude Code