feat: TTY/PTY support for interactive processes (#58) #59

Open
timur wants to merge 1 commit from development_tty2 into development
Owner

Summary

  • Add PTY allocation and registry so jobs with tty: true spawn with a pseudo-terminal
  • WebSocket endpoint at /api/jobs/{id}/pty bridges browser/CLI to the PTY master FD
  • xterm.js terminal in the web UI with a "Terminal" button on running TTY jobs
  • CLI zinit attach <job_id> command with raw mode and Ctrl+P,Ctrl+Q detach

Details

Clean reimplementation of issue #58 on the current job-based architecture (the old development_tty branch diverged too far to merge).

Phase 1 — PTY Infrastructure:

  • ActionSpec gains tty: bool (serde default, no DB migration needed)
  • PtyRegistry holds master FDs keyed by job_id, with resize (TIOCSWINSZ) support
  • Executor branches into run_job_piped() (original) vs run_job_with_pty() (openpty + setsid + TIOCSCTTY)
  • Registry cleared during graceful/force shutdown and cancel_all

Phase 2 — WebSocket endpoint:

  • GET /api/jobs/{id}/pty validates PTY exists, upgrades to WebSocket
  • Binary frames = raw terminal data, Text frames = JSON control ({"resize":{"cols":N,"rows":M}})
  • Uses AsyncFd + mpsc channel for non-blocking PTY reads

Phase 3 — UI integration:

  • xterm.js 5.3.0 + fit addon embedded as static assets
  • terminal.js module: openTerminal(jobId) / closeTerminal(jobId)
  • zinit_ui proxies WebSocket from browser to zinit_server over Unix socket

Phase 4 — CLI attach:

  • zinit attach <job_id> connects WebSocket over Unix socket
  • crossterm raw mode, stdin→WS binary, WS binary→stdout
  • Ctrl+P, Ctrl+Q detach sequence

Test plan

  • cargo build --workspace compiles (verified)
  • All 56 existing tests pass (verified)
  • Create job with tty: true, script: "/bin/bash" → process runs with PTY
  • zinit attach <job_id> enters interactive session
  • xterm.js terminal in UI connects and renders
  • Resize propagates correctly
  • Detach/close does not kill the process
  • Existing tty: false jobs work unchanged

🤖 Generated with Claude Code

## Summary - Add PTY allocation and registry so jobs with `tty: true` spawn with a pseudo-terminal - WebSocket endpoint at `/api/jobs/{id}/pty` bridges browser/CLI to the PTY master FD - xterm.js terminal in the web UI with a "Terminal" button on running TTY jobs - CLI `zinit attach <job_id>` command with raw mode and Ctrl+P,Ctrl+Q detach ## Details Clean reimplementation of issue #58 on the current job-based architecture (the old `development_tty` branch diverged too far to merge). **Phase 1 — PTY Infrastructure:** - `ActionSpec` gains `tty: bool` (serde default, no DB migration needed) - `PtyRegistry` holds master FDs keyed by job_id, with resize (TIOCSWINSZ) support - Executor branches into `run_job_piped()` (original) vs `run_job_with_pty()` (openpty + setsid + TIOCSCTTY) - Registry cleared during graceful/force shutdown and cancel_all **Phase 2 — WebSocket endpoint:** - `GET /api/jobs/{id}/pty` validates PTY exists, upgrades to WebSocket - Binary frames = raw terminal data, Text frames = JSON control (`{"resize":{"cols":N,"rows":M}}`) - Uses AsyncFd + mpsc channel for non-blocking PTY reads **Phase 3 — UI integration:** - xterm.js 5.3.0 + fit addon embedded as static assets - `terminal.js` module: `openTerminal(jobId)` / `closeTerminal(jobId)` - zinit_ui proxies WebSocket from browser to zinit_server over Unix socket **Phase 4 — CLI attach:** - `zinit attach <job_id>` connects WebSocket over Unix socket - crossterm raw mode, stdin→WS binary, WS binary→stdout - Ctrl+P, Ctrl+Q detach sequence ## Test plan - [ ] `cargo build --workspace` compiles (verified) - [ ] All 56 existing tests pass (verified) - [ ] Create job with `tty: true, script: "/bin/bash"` → process runs with PTY - [ ] `zinit attach <job_id>` enters interactive session - [ ] xterm.js terminal in UI connects and renders - [ ] Resize propagates correctly - [ ] Detach/close does not kill the process - [ ] Existing `tty: false` jobs work unchanged 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: add TTY/PTY support for interactive processes (#58)
Some checks failed
Tests / test (pull_request) Failing after 3s
Build and Test / build (pull_request) Failing after 3s
d65a286464
Implement PTY allocation, WebSocket bridging, UI terminal, and CLI
attach so interactive processes (shells, TUI apps) can run under
zinit supervision and be attached to live.

Key changes:
- ActionSpec gains `tty: bool` field (serde default, no migration)
- PtyRegistry holds master FDs keyed by job ID with resize support
- Executor branches into piped vs PTY spawning paths
- WebSocket endpoint at /api/jobs/{id}/pty bridges PTY I/O
- zinit_ui proxies WebSocket and renders xterm.js terminal
- CLI `zinit attach <job_id>` with raw mode and Ctrl+P,Q detach

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Some checks failed
Tests / test (pull_request) Failing after 3s
Build and Test / build (pull_request) Failing after 3s
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin development_tty2:development_tty2
git switch development_tty2

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch development
git merge --no-ff development_tty2
git switch development_tty2
git rebase development
git switch development
git merge --ff-only development_tty2
git switch development_tty2
git rebase development
git switch development
git merge --no-ff development_tty2
git switch development
git merge --squash development_tty2
git switch development
git merge --ff-only development_tty2
git switch development
git merge development_tty2
git push origin development
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
geomind_code/zinit!59
No description provided.