···3434npx tsx src/cli.ts list
3535npx tsx src/cli.ts restart <name>
3636npx tsx src/cli.ts kill <name>
3737+npx tsx src/cli.ts web
3838+npx tsx src/cli.ts web --port 8080
3939+npx tsx src/cli.ts web --code SECRET
3740```
38413942Detach from any attached/following session with **Ctrl+\\**. Press Ctrl+\\ twice quickly to send it to the process.
···6770 (attach) (attach) (read-only)
6871```
69727373+### Web UI
7474+7575+```
7676+Browser (xterm.js) ←WebSocket→ Web Server (127.0.0.1) ←Unix Socket→ PtyServer daemons
7777+ ←HTTP→ pty web process
7878+```
7979+8080+The `pty web` command starts a separate web server process. It does not modify PtyServer — it connects to Unix sockets as a client on behalf of browsers. Each WebSocket maps 1:1 to a Unix socket connection. The server re-frames PtyServer packets (using `PacketReader`) so each WebSocket message contains exactly one complete protocol packet. The browser uses `DataView` on `ArrayBuffer` to encode/decode the same binary protocol.
8181+8282+Auth is optional (`--code` flag). When set, a connect code must be entered before accessing sessions. The code is validated via `POST /auth`, which sets an HttpOnly cookie. All subsequent requests and WebSocket upgrades check this cookie. When the code is all digits, the login form uses `inputmode="numeric"` for a numeric keyboard on mobile.
8383+7084Each session is a detached Node.js process running `src/server.ts` via tsx. It spawns the command in a PTY, feeds all output through xterm-headless to maintain a screen buffer, and listens on a Unix socket for client connections.
71857286## Protocol
···151165152166- `protocol.test.ts` — Unit tests for packet encoding, decoding, and streaming reassembly (partial reads, split packets, large payloads)
153167- `integration.test.ts` — Full integration tests that spawn real PTY sessions, connect clients via sockets, and verify behavior
168168+- `web.test.ts` — Playwright browser tests for the web UI (run separately via `npm run test:web`)
154169- `screenshot.test.ts` — Screenshot-based tests that capture terminal state (ANSI and plain text) and assert on visual output from real programs (vim, nano, ls, bash). Covers control characters, terminal resize, alternate screen buffer, multiple clients, unicode, and daemon spawning.
155170156171All tests run in `/tmp/` directories to avoid polluting the project folder (e.g., vim swap files). Integration and screenshot tests use real processes and real Unix sockets. Each test creates a uniquely-named session and cleans up afterward. There is a `200ms` delay in some tests to allow xterm-headless to process async writes before checking screen state — this is a known characteristic of xterm's write pipeline, not a flaky test.
···161176npm test # run once
162177npm run test:watch # watch mode
163178npx vitest run -t "peek" # run tests matching "peek"
179179+npm run test:web # run web UI tests (Playwright)
164180```
165181166182### node-pty on macOS
···180196 client.ts attach() and peek() functions
181197 protocol.ts Packet types, encoding, decoding, PacketReader
182198 sessions.ts Session discovery, socket/PID file management
199199+ web/
200200+ server.ts WebServer class (HTTP + WebSocket bridge)
201201+ static/
202202+ index.html Single-page browser app (xterm.js)
183203tests/
184204 protocol.test.ts
185205 integration.test.ts
186206 screenshot.test.ts
207207+ web.test.ts
187208completions/
188209 pty.bash Bash tab completion
189210 pty.zsh Zsh tab completion