feat: deep-link support (PID navigation + focus existing tab)#26
Merged
feat: deep-link support (PID navigation + focus existing tab)#26
Conversation
…dd PID API design
…eparate serve command
Add pid: number to PtyProcess, declare it in node-pty.d.ts, and implement in both Bun and node-pty backends. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
sessionToJson now returns pid: number | null — the OS PID if the PTY is live, null before first WS connection spawns the process. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Resolves a PTY PID to its session and issues a 302 to /s/<id>. Returns 404 for unknown or invalid PIDs. Enables external tools (e.g. Vibe Island) to jump to a webtty session by OS PID. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
On load, the client posts a focus-request on webtty:focus:<sessionId>. If an existing tab acks within 200ms, the new tab shows a fallback UI instead of mounting a second terminal. The primary tab listens for future focus-requests and brings itself to the front. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Focus existing tab, PID in session API, and PID-based navigation are now implemented. Also corrects /p/<pid> behaviour to 302 redirect rather than direct render. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…features - Expand sessionToJson multi-line to satisfy biome formatter - Test pid null/set in sessionToJson unit tests - Test GET /api/sessions includes pid field - Test GET /p/:pid returns 404 for unknown and non-numeric PIDs - Test GET /p/:pid redirects 302 to /s/<id> after PTY spawns Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
There was a problem hiding this comment.
Pull request overview
Adds deep-link integration features to webtty so external tools can discover sessions, map PTY PIDs to sessions, and navigate/focus appropriately without requiring tool-side changes.
Changes:
- Expose PTY
pidthrough PTY backends → sessions →GET /api/sessions(pid: number | null). - Add
GET /p/<pid>route that resolves a PTY PID to a session and redirects to/s/<id>. - Implement a client-side BroadcastChannel “focus existing tab” handshake to avoid mounting duplicate terminals for the same session.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/server/websocket.test.ts | Adds coverage ensuring /p/:pid redirects after a PTY is spawned via WS. |
| src/server/session.ts | Extends sessionToJson output to include pid (or null). |
| src/server/session.test.ts | Adds unit tests for pid serialization behavior. |
| src/server/routes.ts | Implements GET /p/<pid> PID-to-session redirect logic. |
| src/server/routes.test.ts | Adds route tests for /api/sessions pid presence and /p/:pid 404 cases. |
| src/pty/types.ts | Extends PtyProcess interface with pid: number. |
| src/pty/node.ts | Surfaces ptyProc.pid via the PtyProcess abstraction. |
| src/pty/node-pty.d.ts | Updates local node-pty shim typing to include IPty.pid. |
| src/pty/bun.ts | Surfaces proc.pid via the PtyProcess abstraction. |
| src/client/index.ts | Adds BroadcastChannel focus handshake and fallback UI for secondary tabs. |
| docs/specs/deep-link.md | Documents the deep-link behaviors and third-party integration surface. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…fixes - Fix race condition: remove focus-request handler from handshake promise so only an established primary tab can reply with focus-ack - Add BroadcastChannel feature detection with isPrimary=true fallback for environments that don't support it (older Safari, hardened webviews) - Add e.data runtime guard (null/object/string checks) before branching on message type in both handshake and primary listeners - Update sessionToJson @returns docstring to mention pid field - Fix spec inconsistency: 'renders directly' -> '302 Location: /s/<id>' in both the features table and 3rd party integration table
window.focus() cannot switch tabs on macOS without a user gesture, and window.close() is blocked for tabs not opened via window.open(). The handshake produced worse UX (dead-end fallback UI) than doing nothing. webtty go <id> opens a new tab unconditionally as before. - Remove BroadcastChannel code from src/client/index.ts - Update deep-link spec to mark focus-existing-tab as dropped - Add ADR 024 documenting the constraints and decision
waitForPrompt was called after the resize message, but /bin/sh does not redraw its prompt on SIGWINCH so the prompt never re-appeared, causing a 3 s timeout. Move the waitForPrompt before the resize so the shell is confirmed ready before dimensions are changed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
Implements the three deep-link features from
docs/specs/deep-link.md, enabling third-party tools (e.g. Vibe Island) to integrate with webtty with no changes on their side./s/<id>load, client posts afocus-requestonBroadcastChannel('webtty:focus:<sessionId>'). If an existing tab acks within 200ms, the new tab shows a fallback UI instead of mounting a second terminal to the same PTY.GET /api/sessionsnow includespid: number | nullper session —nullbefore the first WebSocket connection spawns the PTY.GET /p/<pid>resolves the OS PTY PID to a session and issues302 Location: /s/<id>. Returns 404 for unknown or non-integer PIDs.Integration surface for external tools:
GET http://127.0.0.1:2346/api/sessions[{ id, createdAt, connected, pid }]open http://127.0.0.1:2346/s/<id>open http://127.0.0.1:2346/p/<pid>Changes
src/pty/types.tspid: numbertoPtyProcessinterfacesrc/pty/node-pty.d.tspidonIPtysrc/pty/bun.tsproc.pidsrc/pty/node.tsptyProc.pidsrc/server/session.tspidinsessionToJsonoutputsrc/server/routes.tsGET /p/:pidhandlersrc/client/index.tsdocs/specs/deep-link.md/p/<pid>behaviour to 302Manual test steps
bun run build && bunx webttyFocus existing tab
http://127.0.0.1:2346/s/mainin a browser tab — terminal loads normally.Session already open in another tab.(no terminal mounts).window.focus()).PID in session API
"pid": <number>(not null) for connected sessions.POST /api/sessionswithout connecting a WS —pidshould benull.PID-based navigation
pidfrom the step above (e.g.12345).302 Location: /s/mainand land on the terminal page./p/99999) — should return 404.