2026-02-22 17:02:31 -03:00
# Terminal Module Documentation
## Purpose
2026-04-02 00:31:21 +08:00
This module provides WebSocket transport utilities for terminal input and output in the web server runtime, including message normalization, control frame parsing, rate limiting, pathname resolution, and short-lived output replay buffering for terminal WebSocket connections.
2026-02-22 17:02:31 -03:00
## Entrypoints and structure
- `packages/web/server/lib/terminal/` : Terminal module directory.
2026-04-02 00:31:21 +08:00
- `index.js` : Stable module entrypoint that re-exports protocol helpers and replay-buffer helpers.
2026-03-31 18:47:00 +03:00
- `runtime.js` : Runtime module that owns terminal session state, WS server setup, and `/api/terminal/*` route registration.
2026-04-02 00:31:21 +08:00
- `terminal-ws-protocol.js` : Single-file module containing terminal WebSocket protocol utilities.
- `output-replay-buffer.js` : Helper module for buffering recent terminal output so late subscribers can receive startup prompt data.
- `packages/web/server/lib/terminal/terminal-ws-protocol.test.js` : Test file for protocol utilities.
- `packages/web/server/lib/terminal/output-replay-buffer.test.js` : Test file for replay buffer helpers.
2026-02-22 17:02:31 -03:00
Public API entry point: imported by `packages/web/server/index.js` from `./lib/terminal/index.js` .
## Public exports
### Constants
2026-04-02 00:31:21 +08:00
- `TERMINAL_WS_PATH` : Primary WebSocket endpoint path (`/api/terminal/ws` ).
- `TERMINAL_WS_CONTROL_TAG_JSON` : Control frame tag byte (`0x01` ) indicating JSON payload.
- `TERMINAL_WS_MAX_PAYLOAD_BYTES` : Maximum inbound WebSocket payload size (64KB).
- `TERMINAL_OUTPUT_REPLAY_MAX_BYTES` : Maximum buffered terminal output retained for replay (64KB).
2026-02-22 17:02:31 -03:00
### Request Parsing
- `parseRequestPathname(requestUrl)` : Extracts pathname from request URL string. Returns empty string for invalid inputs.
2026-04-02 00:31:21 +08:00
- `isTerminalWsPathname(pathname)` : Returns whether a pathname matches a supported terminal WebSocket route.
2026-02-22 17:02:31 -03:00
### Message Normalization
2026-04-02 00:31:21 +08:00
- `normalizeTerminalWsMessageToBuffer(rawData)` : Normalizes various data types (Buffer, Uint8Array, ArrayBuffer, string, chunk arrays) to a single Buffer.
- `normalizeTerminalWsMessageToText(rawData)` : Normalizes data to UTF-8 text string.
2026-02-22 17:02:31 -03:00
### Control Frame Handling
2026-04-02 00:31:21 +08:00
- `readTerminalWsControlFrame(rawData)` : Parses WebSocket message as control frame. Returns parsed JSON object or null if invalid or malformed.
- `createTerminalWsControlFrame(payload)` : Creates a control frame with JSON payload and prepends the control tag byte.
2026-02-22 17:02:31 -03:00
2026-04-02 00:31:21 +08:00
### Replay Buffer Helpers
- `createTerminalOutputReplayBuffer()` : Creates mutable state for recent terminal output replay.
- `appendTerminalOutputReplayChunk(bufferState, data, maxBytes?)` : Appends a chunk, trimming older buffered data to stay within the configured byte budget.
- `listTerminalOutputReplayChunksSince(bufferState, lastSeenId)` : Returns buffered chunks newer than the provided replay cursor.
- `getLatestTerminalOutputReplayChunkId(bufferState)` : Returns the latest chunk id in the replay buffer, or `0` when empty.
2026-02-22 17:02:31 -03:00
### Rate Limiting
2026-04-02 00:31:21 +08:00
- `pruneRebindTimestamps(timestamps, now, windowMs)` : Filters timestamps to keep only those within the active time window.
- `isRebindRateLimited(timestamps, maxPerWindow)` : Checks if rebind operations have exceeded the configured threshold.
2026-02-22 17:02:31 -03:00
## Usage in web server
2026-04-02 00:31:21 +08:00
The terminal helpers are used by `packages/web/server/index.js` for:
- WebSocket endpoint path definition and matching
- Message normalization for terminal input payloads
- Control frame parsing for session binding, keepalive, and exit signaling
2026-02-22 17:02:31 -03:00
- Rate limiting for session rebind operations
- Request pathname parsing for WebSocket routing
2026-04-02 00:31:21 +08:00
- Replaying startup output such as shell prompts when the client binds after the PTY already emitted data
2026-02-22 17:02:31 -03:00
2026-04-02 00:31:21 +08:00
The web server combines these utilities with `bun-pty` or `node-pty` to drive full-duplex PTY sessions.
2026-02-22 17:02:31 -03:00
## Notes for contributors
2026-04-02 00:31:21 +08:00
- Keep control frames backward-compatible when possible; use explicit `v` values for protocol changes.
- Always normalize incoming WebSocket messages before processing them.
- Keep replay buffering small and memory-only; it exists to cover startup races, not to implement persistent scrollback.
- Add tests for new control frame types, websocket path changes, malformed payload handling, and replay trimming semantics.
- Keep HTTP input and SSE output fallbacks functional unless the rollout explicitly removes them.
2026-02-22 17:02:31 -03:00
## Verification notes
### Manual verification
2026-04-02 00:31:21 +08:00
1. Start the web server and create a terminal session via `/api/terminal/create` .
2. Wait briefly before binding the client to ensure the shell emits its prompt first.
3. Connect to `/api/terminal/ws` WebSocket and bind to the session.
4. Verify the startup prompt and early shell output are replayed before interactive input begins.
5. Verify `/api/terminal/input-ws` is rejected with `404 Not Found` and `/api/terminal/:sessionId/stream` still works as a fallback path.
2026-02-22 17:02:31 -03:00
### Automated verification
2026-04-02 00:31:21 +08:00
- Run `bun test packages/web/server/lib/terminal/terminal-ws-protocol.test.js`
- Run `bun test packages/web/server/lib/terminal/output-replay-buffer.test.js`
- Run `bun run type-check` , `bun run lint` , and `bun run build` before finalizing changes.