{"id":"8e76779f-7a70-4b21-94fd-7f39e1f6763c","slug":"nolanho-termdeck","kind":"mcp","name":"termdeck","summary":"Persistent PTY terminal sessions for automation agents with CLI control and observe-only web replay","owner":"NolanHo","repoUrl":"https://github.com/NolanHo/termdeck","license":"MIT","licenseTier":"permissive","trust":"community","stars":4,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-05-03T08:05:49.000Z","addedAt":"2026-10-21T17:19:00.622Z","logoUrl":"https://avatars.githubusercontent.com/u/87118768?u=62c9fe72bdfcc8e160b12fbfbb99f13b864de8ea&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["utilities"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T23:31:33.758Z","sample":false,"url":"https://conduits.dev/tools/nolanho-termdeck","language":"TypeScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# TermDeck\n\nTermDeck is a Linux daemon and CLI for persistent PTY-backed terminal sessions. It lets automation start a shell, send commands, poll output, detect prompts, inspect logs, and expose an observe-only web view without giving browser users terminal input capability.\n\nTermDeck targets agent workflows where the terminal must outlive one CLI invocation. The daemon owns the PTY. The CLI and web UI connect to the daemon over local transports.\n\n## Status\n\n- Platform: Linux\n- Runtime: Node.js 22+\n- Package manager used by this repo: pnpm\n- Release artifact: GitHub Release tarball\n- Web UI: observe-only\n\n## Features\n\n- Persistent PTY sessions via `node-pty`\n- Local daemon: `termdeckd`\n- CLI: `termdeck`\n- MCP server: `termdeck-mcp`\n- Length-prefixed protobuf CLI-to-daemon protocol over Unix socket\n- Server-side terminal buffer via `@xterm/headless`\n- Observe-only web UI with JSON REST control endpoints and binary protobuf WebSocket events\n- Prompt/state classification: running, ready, repl, password, confirm, editor, pager, continuation, eof\n- Session artifacts: transcript, events, commands, interactions, metadata, state\n- Shell command markers for `run` output boundaries and exit-code capture\n- Historical inspection: history, inspect, log, events, replay\n- Password input path that avoids command logging\n- Linux foreground process-group signal targeting via `/proc/<pid>/stat` `tpgid`\n\n## Install\n\nDownload the release tarball from GitHub Releases or install from a local checkout.\n\n```bash\npnpm install\npnpm run build\n```\n\n`node-pty` is a native dependency. pnpm must allow its build script. This repo includes:\n\n```ini\nonly-built-dependencies[]=node-pty\n```\n\nThe package postinstall check fails fast if the native binding is missing.\n\n## Documentation\n\n- [Usage Guide](docs/usage.md)\n- [Scenarios](docs/scenarios.md)\n- [Chinese README](README_zh.md)\n\n## Quick start\n\nStart the daemon:\n\n```bash\ntermdeckd\n```\n\nCreate a session:\n\n```bash\ntermdeck new main --cwd \"$PWD\"\n```\n\nOr let an agent step create it on first use:\n\n```bash\ntermdeck step main 'pwd && ls' --cwd \"$PWD\" --timeout-ms 5000 --autostart\n```\n\nOr use a project-derived session name so callers do not have to manage one:\n\n```bash\ntermdeck project-step 'pwd && ls' --cwd \"$PWD\" --timeout-ms 5000 --autostart\n```\n\nRun a command:\n\n```bash\ntermdeck run main 'pwd && ls' --timeout-ms 5000\n```\n\nPoll new output:\n\n```bash\ntermdeck poll main --quiescence-ms 200\n```\n\nCheck the current state with a rendered screen tail:\n\n```bash\ntermdeck state main --lines 12\n```\n\nRead the rendered screen as plain text:\n\n```bash\ntermdeck screen main\n```\n\nOpen the observe-only web UI:\n\n```bash\nTERMDECK_WEB_PORT=8787 termdeckd\n# browse http://127.0.0.1:8787\n```\n\nThe CLI defaults to agent-readable text. `--raw` prints PTY bytes for debugging. Web rendering uses serialized xterm state and raw live events.\n\n## CLI commands\n\nSession lifecycle:\n\n```bash\ntermdeck new <session> --cwd <path> [--shell <shell>] [--rows N] [--cols N] [--prompt-regex <regex>]\ntermdeck list\ntermdeck kill <session>\ntermdeck resize <session> --rows N --cols N\ntermdeck configure <session> [--prompt-regex <regex>]\n```\n\nTerminal I/O:\n\n```bash\ntermdeck step <session> [command] [--cwd <path>] [--op run|poll|send|paste|ctrl|signal] [--timeout-ms N] [--startup-timeout-ms N] [--quiescence-ms N] [--lines N] [--autostart]\ntermdeck project-step [command] [--cwd <path>] [--name <label>] [--op run|poll|send|paste|ctrl|signal] [--timeout-ms N] [--autostart]\ntermdeck run <session> <command> [--timeout-ms N] [--quiescence-ms N]\ntermdeck script <session> [file] [--inline <script>] [--shell bash] [--timeout-ms N] [--quiescence-ms N]\ntermdeck paste <session> [file] [--inline <text>] [--enter] [--timeout-ms N] [--quiescence-ms N]\ntermdeck send <session> <data> [--timeout-ms N] [--quiescence-ms N]\ntermdeck ctrl <session> <key> [--timeout-ms N] [--quiescence-ms N]\ntermdeck poll <session> [--timeout-ms N] [--quiescence-ms N]\ntermdeck password <session> [--timeout-ms N] [--quiescence-ms N]\ntermdeck signal <session> <signal> [--timeout-ms N] [--quiescence-ms N]\n```\n\nInspection:\n\n```bash\ntermdeck state <session> [--lines N] [--autostart]\ntermdeck summary <session> [--lines N] [--events N] [--autostart]\ntermdeck last-command <session>\ntermdeck search <query> [--session ID] [--cwd PATH] [--task NAME] [--kind transcript,events,commands,metadata,tasks] [--regex] [--limit N] [--context N]\ntermdeck screen <session>\ntermdeck scrollback <session> [--lines N]\ntermdeck transcript <session>\ntermdeck metadata <session>\ntermdeck history\ntermdeck inspect <session>\ntermdeck log <session> [--lines N]\ntermdeck events <session> [--after-seq N] [--limit N]\ntermdeck replay <session> [--lines N]\ntermdeck clear-scrollback <session>\n```\n\nBackground task helpers:\n\n```bash\ntermdeck task start <name> <command> --cwd <path> [--owner USER] [--labels a,b] [--ttl-ms N] [--restart-policy never|on-exit|on-failure] [--max-restarts N] [--backoff-ms N] [--ready-url URL] [--ready-port N] [--expect PATTERN]\ntermdeck task status <name>\ntermdeck task recover <name>\ntermdeck task logs <name> [--lines N]\ntermdeck task list\ntermdeck task dashboard\ntermdeck task prune [--stale] [--expired] [--dry-run]\ntermdeck task stop <name>\n```\n\nSession cleanup:\n\n```bash\ntermdeck list [--cwd <path>] [--name <text>] [--status ready|running|repl|password|confirm|editor|pager|eof|unknown]\ntermdeck prune [--cwd <path>] [--name <text>] [--status STATUS]\n```\n\n## MCP server\n\n`termdeck-mcp` exposes the same TermDeck capability surface as the CLI over stdio MCP. CLI, MCP, and the web UI are peer access surfaces; `termdeckd` remains the owner of PTYs, sessions, transcripts, and web observation.\n\nRegister the server directly. When no environment is provided, CLI and MCP discover an existing daemon by checking the system socket at `/var/lib/termdeck/termdeckd.sock` before falling back to `~/.termdeck/termdeckd.sock`. Agents do not need to choose a backend.\n\n```toml\n[mcp_servers.termdeck]\ncommand = \"termdeck-mcp\"\n```\n\nSet `TERMDECK_HOME` or `TERMDECK_SOCKET` only when you intentionally want project-isolated state or a non-default daemon.\n\nThe MCP `step` tool is the agent-friendly default entrypoint. It discovers or autostarts `termdeckd` by default, creates a missing session when `cwd` is supplied, and returns stable JSON fields such as `status`, `reason`, `prompt`, `exitCode`, `timedOut`, `outputTruncated`, `lastSeq`, `transcriptPath`, and `cwd`. `project_step` goes one level higher by deriving a stable session id from `cwd` and an optional label.\n\n`summary` returns a compact inspection object with a screen tail, output tail, recent events, and likely error lines. `last_command` returns structured command id, command text, seq bounds, duration, exit code, timeout flag, and output tail. Use these when an agent needs state without replaying a large transcript.\n\n`search` scans local sessions and task metadata across transcripts, events, commands, session metadata, and task specs. It supports filters for session id, task name, cwd, kind, regex, limit, and context lines. The Web UI exposes the same search for human inspection.\n\nAgent-facing text views redact common secret-shaped values by default, including returned output, log/events views, summaries, and last-command records. Web snapshots and WebSocket output remain visible because the web surface is a local human observer. Raw transcripts remain local artifacts and should still be treated as sensitive.\n\nTask helpers report stale metadata, expired TTLs, exited backing processes, restart counts, readiness diagnostics, and orphan `task-*` sessions. Optional restart policies can restart exited tasks on any exit or only non-zero exit. The web UI surfaces the same dashboard data with filters for active and attention-needed work, task logs, search results, and safe task stop/recover/prune controls.\n\nSynchronization:\n\n```bash\ntermdeck expect <session> <pattern> [--timeout-ms N]\ntermdeck expect-prompt <session> [--timeout-ms N]\n```\n\n## Daemon configuration\n\nEnvironment variables:\n\n| Variable | Default | Meaning |\n| --- | --- | --- |\n| `TERMDECK_HOME` | discovered daemon, then `~/.termdeck` | Runtime state root |\n| `TERMDECK_SOCKET` | `$TERMDECK_HOME/termdeckd.sock` | Unix socket path |\n| `TERMDECK_WEB_PORT` | unset | Starts web UI on `127.0.0.1:<port>` |\n\nSocket permissions are set to `0600`.\n\n## Data layout\n\nEach session lives under:\n\n```text\n$TERMDECK_HOME/sessions/<session>/\n```\n\nFiles:\n\n```text\ntranscript.log      raw PTY output\ncommands.log        logged commands, excludes password input\ninteraction.log     input/output interaction stream\nevents.jsonl        sequenced daemon events\nsession.json        session metadata\nstate.json          latest detected state\n```\n\n## Protocol\n\nCLI clients use length-prefixed protobuf frames over the Unix socket. Terminal event subscribers use binary protobuf WebSocket frames. Web REST endpoints intentionally return JSON because they are low-frequency control and snapshot endpoints.\n\n## Development\n\n```bash\npnpm install\npnpm run gen\npnpm run typecheck\npnpm run lint\npnpm test\npnpm run build\npnpm pack\n```\n\nCI runs typecheck, lint, tests, and build on GitHub Actions.\n\n## Limitations\n\n- Linux only.\n- Exact terminal semantics still depend on `node-pty` and the host shell.\n- Signal targeting uses Linux `/proc/<pid>/stat` `tpgid`; if unavailable or stale, TermDeck falls back to process-group or process signaling.\n- The web UI is observe-only by design. It does not accept human terminal input.\n- The browser event decoder handles the current event schema only.\n\n## License\n\nMIT\n"}