feat(mobile): live terminal screen over the relay — WebView xterm, quick keys, PTY reattach

Closed
SA
3mo ago
+722-1

First surface of the terminal-first mobile direction: a live terminal attached to a host PTY over the relay, at workspace/[id]/terminal/[terminalId] (deep-link only for now — home-screen rows land with the terminals-as-sessions swap after #6328).

Architecture

  • The WebSocket lives inside the WebView, next to xterm — PTY bytes never cross the RN bridge. The page (xterm.js + fit addon + a small reconnect runtime, ~403 KiB self-contained HTML generated by scripts/generate-terminal-html.ts and committed as terminalHtml.generated.ts) speaks the same wire contract as web's TerminalConnection: binary frames = raw PTY bytes, JSON text frames = control (attached/title/exit/error), client sends input/resize, _whoowns preflight before each dial for fly affinity + definitive 403 detection, exponential backoff with a 12-attempt budget, replay=0 once bytes have been received.
  • React Native owns auth and chrome: signs each dial URL with a fresh JWT (getHostAuthToken, expiry-aware), sets the nav title from the PTY's OSC title, shows connection banners, relays app-resume to the page, and renders a quick-keys row (esc · tab · ⇧tab · arrows · ^C) writing raw bytes through the bridge.
  • New native dep: react-native-webview 13.16.1 — dev clients need a rebuild (bunx expo run:ios).

Also in this PR

  • fix(mobile): pin expo-font 56.0.5 — bun resolved 56.0.7 (June 15 batch), whose precompiled framework references a Record.from(dictionary:appContext:) symbol that doesn't exist in our expo-modules-core 56.0.15 (June 5 batch) → DYLD missing-symbol crash at launch. Anyone prebuilding from current main hits this.

Verified on simulator (iPhone 16 Pro, full local stack: api + relay + desktop-spawned host-service)

End-to-end, driven via Maestro:

  • Deep link opens the screen; xterm renders a live Claude Code TUI streaming over the relay; the PTY's title lands in the native header.
  • Input path: tapping the ⇧tab quick key cycled Claude Code's permission mode on the host (auto → manual) and the TUI update streamed back.
  • Reattach: leaving and re-opening the deep link resumed the same PTY with a full screen replay (server adopts by terminalId).
  • Typecheck (repo-wide) and lint clean.

Known follow-ups

  • Soft-keyboard input: tapping the terminal doesn't reliably focus xterm's hidden textarea in WKWebView, so freeform typing needs the planned native input bar writing via terminal.send/writeInput. Quick keys (the bridge path) work today.
  • One [RNScreens] sheetPresentationController is null redbox appeared on a single deep-link entry and never reproduced; watching for it.

Summary by cubic

Adds a live terminal screen on mobile at workspace/[id]/terminal/[terminalId], powered by xterm in a WebView, with quick keys, reconnect, and session reattach. Also adds required deps and pins expo-font to fix a launch crash; dev clients must rebuild.

  • New Features

    • xterm runs inside WebView; socket lives in-page so PTY bytes don’t cross the RN bridge.
    • Quick keys row (esc, tab, ⇧tab, arrows, ^C) sends raw bytes to the PTY.
    • Reconnect with backoff and reattach by terminalId with replay; exit overlay shows code.
    • Native chrome: sets title from PTY OSC, shows connection banners; deep-link only for now.
  • Dependencies

    • Add react-native-webview@13.16.1 (native change; rebuild dev clients, e.g. bunx expo run:ios).
    • Add @xterm/xterm and @xterm/addon-fit for the generated terminal page (scripts/generate-terminal-html.ts).
    • Pin expo-font@56.0.5 to avoid a DYLD missing-symbol crash with expo-modules-core@56.0.15.

Summary by CodeRabbit

  • New Features
    • Added mobile terminal access within workspace sessions.
    • Added live terminal interaction with connection, loading, offline, error, and ended-session states.
    • Added retry and reconnect support for interrupted sessions.
    • Added quick-access controls for Escape, Tab, Shift-Tab, arrow keys, and Ctrl-C.
    • Added mobile-friendly keyboard handling, terminal resizing, and session close actions.
    • Added a dedicated Terminal screen with streamlined navigation and presentation.