Commit Graph

6 Commits

Author SHA1 Message Date
Yaojia Wang
d22dcd24f7 fix: address review report across security, architecture, quality, tests
Implements the fixes from docs/REVIEW_REPORT.md (4-agent parallel review).
typecheck clean; 341 tests pass (16 files, +113); build:web ok; coverage
thresholds (80%) enforced in vitest.config.ts.

Critical:
- multi-device approval race: release held approval only when the last
  client detaches (closing one mirror no longer cancels another's prompt)
- unbounded session creation (DoS): Config.maxSessions cap (env MAX_SESSIONS),
  enforced in manager via the existing M4 exit(-1) path
- signal-handler leak: named SIGINT/SIGTERM/uncaughtException refs removed in close()
- terminal-session initialInput timer tracked + cleared on dispose
- tabs.addEntry null-as-cast type hole removed (build session before entry)

Should-fix:
- security-headers middleware + Origin/CSRF guard on DELETE /live-sessions[/:id]
- history.ts converted to fs/promises (async /sessions handler)
- removed dead clientDims map + blur protocol message end-to-end
- per-connection WS message rate limit (Config.maxMsgsPerSec)
- /sessions behavior kept; documented as accepted LAN risk (TECH_DOC §7)

Tests:
- new tmux / preview-grid / terminal-session (jsdom) / tabs (jsdom) suites
- extended history/config/manager/integration coverage incl. regressions

Hygiene:
- parsePositiveInt -> parseNonNegativeInt; ALLOWED_ORIGINS scheme validation
- log-injection sanitize; isLoopback handles 127.0.0.0/8 + IPv4-mapped
- operational constants moved into Config
- extracted public/preview-grid.ts (DRY launcher/manage)
- doc sweeps: ARCHITECTURE §8 runtime-handle exception, stale comments
2026-06-20 18:27:45 +02:00
Yaojia Wang
9099f73534 feat(v0.3): H1 — tmux keepalive (sessions survive a server restart)
- src/session/tmux.ts: sync tmux CLI wrappers (available/has/kill), best-effort
- config: useTmux from USE_TMUX env (1/0/auto→detect tmux on PATH)
- session: when useTmux, spawn 'tmux new-session -A -s web_<id> <shell>' (the
  node-pty proc is a tmux CLIENT); createSession takes an optional id for
  re-attach; Session.tmuxName; kill() runs tmux kill-session
- manager: handleAttach re-attaches to a surviving 'web_<id>' tmux session after
  a restart (Case 3.5); shutdown kills only the client pty for tmux sessions so
  the shell keeps running
- tests: USE_TMUX:0 in shared integration cfg (no tmux leakage); unit CFGs
  useTmux:false; integration 'H1' (real tmux): set var → restart server →
  re-attach → var survived. 208 tests green.
2026-06-17 19:48:39 +02:00
Yaojia Wang
9a6150c355 feat(v0.3): H3 — remote approve/reject (no typing)
- types/protocol: ClientMessage approve/reject; ServerMessage status.pending
- server: POST /hook/permission HELD until the client decides; approve/reject
  ws messages resolve it with {hookSpecificOutput.decision.behavior allow|deny};
  5-min timeout + release-on-disconnect fall back to Claude's own prompt
- manager.handleHookEvent threads pending flag
- setup-hooks: PermissionRequest uses the held curl (writes decision to stdout);
  status events stay fire-and-forget
- FE: terminal-session approve()/reject() + pending state; tabs approval banner
  (Approve/Reject) for the active tab's held request
- Tests: protocol approve/reject; integration ⑧ held→approve→allow. 207 green.
- Browser-verified: banner 'Claude wants to use Bash' → Approve → resolves allow.
2026-06-17 19:13:07 +02:00
Yaojia Wang
a411c89ee9 feat(v0.3): H2 server — Claude Code hooks → live status side-channel
- types: ClaudeStatus + ServerMessage 'status'; Session.claudeStatus;
  SessionManager.handleHookEvent
- session: inject WEBTERM_SESSION + WEBTERM_HOOK_URL into the spawned shell env
  so hooks know which tab they belong to
- http/hook.ts: pure event→status mapper (PreToolUse→working, PermissionRequest/
  Notification:permission_prompt→waiting, Stop/idle_prompt→idle); 6 unit tests
- server: POST /hook (loopback-only, express.json 64kb) → manager pushes 'status'
- fix: closeIdleConnections() on shutdown so an idle hook keep-alive socket
  doesn't hang close()/SIGTERM
- integration ⑦ (real PTY): attach → POST /hook → ws receives status. 205 tests green.
2026-06-17 18:44:07 +02:00
Yaojia Wang
cdec19a256 test: T15 integration/E2E (real WS, M2/M4/L3/L5, F5/F6)
6 cases against real startServer via ws client:
①bad-origin 401 ②wrong-path destroy ③oversized-frame 1009 ④spawn-fail exit(-1)
— all pass in sandbox; ⑤attach->output ⑥reconnect-replay gated by PTY_AVAILABLE
(auto-skip where posix_spawn is blocked). Orchestrator verified ⑤⑥ sandbox-off:
6/6 pass with real shell — F5/F6 confirmed (replay incl CJK/ANSI intact).
2026-06-17 10:16:14 +02:00
Yaojia Wang
b126cf0e42 feat: T1 scaffold — package.json, tsconfig (backend+web), vitest, dirs
- All deps declared once (express5, ws, node-pty, @xterm/xterm6, addon-fit;
  dev: typescript6, tsx, vitest4, @types/node|ws|express)
- postinstall chmod +x node-pty spawn-helper (prebuild ships it non-exec → posix_spawnp fails)
- node-pty verified: require + real PTY spawn (exitCode 0)
- npm test passes with no tests (passWithNoTests)
- tsconfig.web.json type-checks frontend; browser bundling strategy still TBD (see log)

T1 of docs/PLAN.md
2026-06-16 07:21:42 +02:00