Skip to content

feat: Workspace browser terminal — walking skeleton (v7.7.0) - #236

Merged
vakovalskii merged 1 commit into
mainfrom
feat/workspace-terminal
Jul 20, 2026
Merged

vakovalskii merged 1 commit into
mainfrom
feat/workspace-terminal

Conversation

@vakovalskii

Copy link
Copy Markdown
Owner

What

First step of the TermDeck merge: a real terminal in the browser (new Workspace view). Single pane for now — splits, tmux-less reattach, saved commands and per-project workspaces come next.

Verified end-to-end: real interactive zsh round-trip, resize, bad-token rejection, and pty teardown when navigating away.

Architecture (optional feature — core stays dependency-free)

  • src/terminal.js — lazily loads @lydell/node-pty (prebuilt-only, never runs node-gyp) and hand-rolls the WebSocket on the stdlib http.Server 'upgrade' event, so ws is NOT a dependency. Binary frames = raw terminal bytes; text frames = JSON control (resize/ready/exit).
  • Security — a per-process token + same-origin Origin check gate the WebSocket (it grants shell access); cwd must pass isSafeLaunchPath. Safe to run the dashboard in server mode.
  • server.js — server.on('upgrade') wiring; GET /api/terminal/status (availability + same-origin token); a strict allowlisted GET /vendor/* route (xterm.js served lazily so the base dashboard never carries the ~490 KB bundle); ws:/wss: added to the connect-src CSP.
  • Frontend — src/frontend/workspace.js lazy-loads the vendored xterm.js + fit addon, opens the WS, wires stdin/stdout/resize; nav item, render() dispatch, NAV_HELP entry, teardown on leaving the view, themed CSS.
  • Deps — @lydell/node-pty in optionalDependencies; if no prebuilt exists for the platform the dashboard still runs and the terminal reports itself disabled. Vendored @xterm/xterm 6.0.0 + addon-fit (MIT). CLAUDE.md updated to reflect the one optional dep.

Testing

  • 9 new unit tests for the WS frame codec (encode / masked-decode / fragmented / multi-frame / round-trip) + token compare.
  • Full suite: 166 pass, 2 skipped (win32), 0 fail.
  • Browser: echo RESULT_$((6*7))_END → RESULT_42_END over the WS; wrong token → connection rejected; leaving the view nulls the socket + disposes the terminal.

Not in this PR (next steps, per docs/SUPERAPP_PLAN.md)

WebGL renderer + context-loss fallback, multi-pane/splits, tmux-less persistence (pty-host + serialize/replay), saved commands & per-project workspaces, proxy.

Version

Minor bump 7.6.0 → 7.7.0 (new feature, core install unchanged).

🤖 Generated with Claude Code

Adds a real terminal in the browser (new "Workspace" view) as the first
step of the TermDeck merge. Single pane for now; splits, tmux-less
reattach, saved commands and per-project workspaces come next.

Architecture (optional feature — core stays dependency-free):
- src/terminal.js: lazily loads @lydell/node-pty (prebuilt-only, no
  node-gyp) and hand-rolls the WebSocket on the stdlib http.Server
  'upgrade' event, so `ws` is NOT a dependency. Binary frames carry raw
  terminal bytes; text frames carry JSON control (resize/ready/exit).
- Security: a per-process token + same-origin Origin check gate the WS
  (it grants shell access), and cwd must pass isSafeLaunchPath. Safe to
  run in server mode.
- server.js: server.on('upgrade') wiring, GET /api/terminal/status
  (reports availability + serves the token same-origin), a strict
  allowlisted GET /vendor/* static route (xterm.js served lazily so the
  base dashboard never carries the ~490KB bundle), and ws:/wss: added to
  connect-src CSP.
- Frontend: src/frontend/workspace.js lazy-loads the vendored xterm.js +
  fit addon, opens the WS, wires stdin/stdout/resize; nav item, render()
  dispatch, NAV_HELP entry, teardown on leaving the view, themed CSS.
- @lydell/node-pty in optionalDependencies; CLAUDE.md updated to reflect
  the one optional dep. Vendored @xterm/xterm 6.0.0 + addon-fit (MIT).

Tests: 9 new unit tests for the WS frame codec + token compare. Verified
end-to-end in a browser (real zsh round-trip, bad-token rejection,
teardown on navigate). Full suite 166 pass / 2 skipped / 0 fail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vakovalskii
vakovalskii merged commit af39358 into main Jul 20, 2026
6 checks passed
@vakovalskii
vakovalskii deleted the feat/workspace-terminal branch July 20, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant