Documentation
¶
Overview ¶
Package attach is the Epic 8 PART A raw attach passthrough (E8.1-E8.5): the client-side loop that puts the controlling terminal in raw mode, paints the one snapshot (S10), then streams live frames straight to the terminal while forwarding keystrokes and resizes to the session — and ALWAYS gives the terminal back (on detach, session end, panic, and SIGINT/SIGTERM/SIGHUP).
The loop lives in its own bubbletea-free package so it is unit-testable against injected seams (TermControl + Session) AND provable end-to-end through a real PTY (see pty_test.go). internal/tui/attach.go is the thin bubbletea adapter that releases the tea terminal and calls Run with a real TermControl.
SIGKILL restoration is NOT claimed: no handler can run under SIGKILL, so a hard kill -9 leaves the terminal raw. That is the documented boundary of the restore guarantee (pinned by TestPTY_SIGKILLLeavesRawUnrestored_DocumentedLimitation), not a bug — restoring it would require a wrapper process, which v1.0 does not add.
Index ¶
Constants ¶
const DefaultDetachKey = 0x11
DefaultDetachKey is Ctrl+q (0x11) — the DC1 control byte. It is layout-friendly (the Q position is identical on US/Swiss/QWERTZ/AZERTY, unlike the near-untypeable Ctrl+\), and although 0x11 is XON, raw mode clears IXON (A-1) so it is delivered as a plain byte with no flow-control collision (ADR-006).
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Term TermControl
Session Session
DetachKey byte // default DefaultDetachKey (0x11, Ctrl+q) when zero
ReadOnly bool // completed/lost: paint final snapshot, forward no input (G3)
Chrome bool // reserve the real bottom row for the return hint (ADR-006 v0.3); off = full passthrough (A-5)
Name string // session label rendered in the reserved-row hint
}
Config parameterizes one attach passthrough run.
type Reason ¶
type Reason int
Reason is why Run returned, so the caller knows whether to return to the general view (detached / session ended) or surface an error.
func Run ¶
Run blocks driving the passthrough and ALWAYS restores the terminal before it returns. It paints the snapshot exactly once (raw-then-paint), then streams live frames to Out while forwarding keystrokes (except the detach key) and resizes to the session. It returns when the user detaches, the stream closes, a termination signal arrives, or a fault is recovered.
type Session ¶
type Session interface {
Snapshot() []byte
Frames() <-chan []byte
Input(p []byte) error
Resize(cols, rows int) error
Detach() error
Generation() uint64
}
Session is the subset of *protocol.Attachment the loop drives (stubbable): the one snapshot, the live output stream, and input/resize/detach under the lease.
type TermControl ¶
type TermControl interface {
// MakeRaw puts the terminal in raw mode (ISIG/ICANON/ECHO/IXON off) and returns
// a restore func that returns it to its prior state. restore is idempotent and
// safe to call from the loop's teardown after a signal.
MakeRaw() (restore func() error, err error)
// Size reports the current terminal size in cells.
Size() (cols, rows int, err error)
// In is the raw keystroke source.
In() io.Reader
// Out is the passthrough sink.
Out() io.Writer
// Resizes yields one tick per SIGWINCH; stop releases the notification.
Resizes() (events <-chan struct{}, stop func())
// Signals yields SIGINT/SIGTERM/SIGHUP to restore-then-exit on; stop releases it.
Signals() (sigs <-chan os.Signal, stop func())
}
TermControl is the injectable seam over the real controlling terminal, so the loop is unit-testable without a TTY and a PTY smoke test proves termios restore.
func NewTermControl ¶
func NewTermControl(in, out *os.File) (TermControl, error)
NewTermControl builds the production TermControl over in/out (stdin/stdout).