attach

package
v0.13.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 14 Imported by: 0

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

View Source
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.

const (
	ReasonDetached   Reason = iota // the user pressed the detach key (or a signal tore down)
	ReasonSessionEnd               // the live stream closed (session ended / superseded)
	ReasonError                    // a fault was recovered; the terminal was still restored
)

func Run

func Run(cfg Config) (reason Reason, err error)

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL