pty

package
v0.69.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ProcessStartTime added in v0.48.0

func ProcessStartTime(pid int) (int64, error)

ProcessStartTime returns a value that uniquely identifies the process with the given PID (clock ticks since boot from /proc/[pid]/stat field 22). If the PID is recycled, the new process will have a different start time.

func TailFile added in v0.64.5

func TailFile(path string, lines int) ([]byte, error)

TailFile returns the last `lines` lines from a scrollback file on disk without a live Scrollback. It is used to read logs for stopped sessions whose live PTY has already been torn down. A missing file is returned as an error; an existing but empty file returns (nil, nil).

Types

type AdoptOpts

type AdoptOpts struct {
	ID         string
	Fd         uintptr
	PID        int
	LogPath    string
	MaxLogSize int64
	// Logger routes this session's diagnostics to the daemon's logger. Nil
	// falls back to slog.Default().
	Logger *slog.Logger
}

type Cell added in v0.69.0

type Cell struct {
	Content string
	Style   CellStyle
}

Cell is a single rendered terminal cell. Content is the cell's grapheme cluster (a single rune most of the time, but possibly a wide character, emoji, or base+combining sequence). An empty Content marks the trailing column of a wide (double-width) cell and renders as nothing, since the wide grapheme in the preceding column already occupies the space.

type CellStyle added in v0.69.0

type CellStyle struct {
	FG            Color
	BG            Color
	Bold          bool
	Faint         bool
	Italic        bool
	Underline     bool
	Blink         bool
	Reverse       bool
	Strikethrough bool
}

CellStyle is the visual style of a rendered cell, split out from Cell so the renderer can compare adjacent cells' styles with a single struct equality and only re-emit an SGR sequence when the style changes. All fields are comparable, and the zero value is the terminal's default style.

type Color added in v0.69.0

type Color struct {
	Kind  ColorKind
	Value uint32
}

Color is a backend-neutral terminal cell color. The zero value is the terminal default color, which keeps a freshly rendered cell equal to the unstyled default (see render.go's run-length SGR suppression).

type ColorKind added in v0.69.0

type ColorKind uint8

ColorKind identifies how a Color's Value should be interpreted.

const (
	// ColorDefault is the terminal's default foreground/background — the
	// renderer emits no explicit SGR color for it.
	ColorDefault ColorKind = iota
	// ColorIndexed is an ANSI palette index: 0-7 basic, 8-15 bright, 16-255
	// the 256-color cube/greyscale.
	ColorIndexed
	// ColorRGB is a 24-bit true color packed as 0xRRGGBB in Value.
	ColorRGB
)

type ScreenCapture added in v0.2.0

type ScreenCapture struct {
	Frame         string
	CursorX       int
	CursorY       int
	CursorVisible bool
	Cols          int
	Rows          int
}

type Scrollback

type Scrollback struct {
	// contains filtered or unexported fields
}

Scrollback is an append-only log file. path is immutable after construction, so Tail/TailBytes read it lock-free via independent file descriptors.

func NewScrollback

func NewScrollback(path string, maxSize int64) (*Scrollback, error)

func (*Scrollback) Close

func (s *Scrollback) Close() error

func (*Scrollback) Remove

func (s *Scrollback) Remove() error

func (*Scrollback) SetLogger added in v0.69.0

func (s *Scrollback) SetLogger(log *slog.Logger)

SetLogger routes the scrollback writer's diagnostics to the daemon's logger. The daemon never calls slog.SetDefault and runs with stderr sent to /dev/null, so without this the writer's logs would be discarded (issue #1087). A nil logger is ignored (keeps the slog.Default() fallback set at construction).

func (*Scrollback) Stats added in v0.28.0

func (s *Scrollback) Stats() (written, maxSize int64, saturated bool)

func (*Scrollback) Tail

func (s *Scrollback) Tail(lines int) ([]byte, error)

func (*Scrollback) TailBytes added in v0.6.1

func (s *Scrollback) TailBytes(maxBytes int64) ([]byte, error)

TailBytes returns up to maxBytes from the end of the scrollback file.

func (*Scrollback) Write

func (s *Scrollback) Write(data []byte) (int, error)

type Session

type Session struct {
	ID         string
	Cmd        *exec.Cmd
	Ptmx       *os.File
	Scrollback *Scrollback
	// contains filtered or unexported fields
}

func AdoptSession

func AdoptSession(opts AdoptOpts) (*Session, error)

func NewSession

func NewSession(opts SessionOpts) (*Session, error)

func (*Session) Attach

func (s *Session) Attach(w io.Writer)

func (*Session) BytesRead added in v0.69.0

func (s *Session) BytesRead() int64

BytesRead returns the total number of PTY output bytes read this session lifetime. Zero on a running session that has been up for a while is the signature of a silent agent (issue #1087) — alive but rendering nothing.

func (*Session) Close

func (s *Session) Close()

func (*Session) CreatedAt added in v0.69.0

func (s *Session) CreatedAt() time.Time

CreatedAt returns when this PTY session object was constructed (spawned or adopted). Used to age a silent session for the zero-output diagnostic.

func (*Session) Detach

func (s *Session) Detach()

func (*Session) DetachWriter

func (s *Session) DetachWriter(w io.Writer)

func (*Session) Done

func (s *Session) Done() <-chan struct{}

func (*Session) ExitCode

func (s *Session) ExitCode() int

func (*Session) ExitSignal added in v0.51.0

func (s *Session) ExitSignal() syscall.Signal

func (*Session) Exited

func (s *Session) Exited() bool

func (*Session) Fd

func (s *Session) Fd() uintptr

func (*Session) ForceKill

func (s *Session) ForceKill() error

func (*Session) Interrupt added in v0.66.2

func (s *Session) Interrupt(count int, delay time.Duration) error

Interrupt sends the interrupt byte (Ctrl-C, 0x03) to the PTY count times, pausing delay between successive sends. Some agent TUIs (notably Claude) ignore a single Ctrl-C and need two rapid presses to actually interrupt, so the count and delay are configurable per agent (see issue #620). A count below 1 sends once. The whole operation holds writeMu so the presses aren't interleaved with other input.

func (*Session) Kill

func (s *Session) Kill() error

func (*Session) LastOutputAt added in v0.3.0

func (s *Session) LastOutputAt() time.Time

func (*Session) NotifyUserInput added in v0.52.0

func (s *Session) NotifyUserInput()

NotifyUserInput records that the attached user just typed something. Call this from the passthrough data path, not from gr type.

func (*Session) PeakRSSBytes added in v0.51.0

func (s *Session) PeakRSSBytes() int64

func (*Session) Pgid added in v0.69.0

func (s *Session) Pgid() int

Pgid returns the process-group id graith signals on Kill/ForceKill. For graith-spawned sessions (started with Setsid, see NewSession) the child is a session/group leader and its PGID equals its PID. For adopted sessions graith did not set Setsid, so PGID may differ from the reported pid — but Kill/ ForceKill signal -pid regardless, so this remains an honest report of "the group graith signals". Logging pid+pgid together (issue #1104) makes an OS-level signal trace (e.g. `kill -0 -<pgid>`) possible from the daemon log alone. Returns 0 when the pid is unknown.

func (*Session) Poke added in v0.16.5

func (s *Session) Poke()

Poke sends SIGWINCH to the session's process group. This interrupts blocked reads and forces TUI frameworks to re-check stdin, ensuring recently written input is consumed. The child process was started with Setsid, so its PID equals its process group ID.

func (*Session) ProcessPID

func (s *Session) ProcessPID() int

func (*Session) RecentlyAdopted added in v0.6.1

func (s *Session) RecentlyAdopted(grace time.Duration) bool

RecentlyAdopted returns true if the session was adopted (daemon restart) within the last duration and has not yet received fresh PTY output.

func (*Session) Resize

func (s *Session) Resize(rows, cols uint16) error

func (*Session) ScreenPreview added in v0.2.0

func (s *Session) ScreenPreview() string

func (*Session) ScreenSnapshot added in v0.2.0

func (s *Session) ScreenSnapshot() ScreenCapture

func (*Session) ScrollbackFile added in v0.69.0

func (s *Session) ScrollbackFile() *Scrollback

ScrollbackFile returns the session's scrollback buffer. It exists so a SessionDriver interface can expose the scrollback via a method (interfaces can't have fields), while the exported Scrollback field stays for direct concrete use within this package.

func (*Session) WaitForUserIdle added in v0.52.0

func (s *Session) WaitForUserIdle(idleTimeout, maxWait time.Duration) bool

WaitForUserIdle blocks until at least idleTimeout has elapsed since the last user keystroke, or until maxWait has elapsed (whichever comes first). Returns true if the idle condition was met, false if maxWait expired.

func (*Session) WasAdopted added in v0.69.0

func (s *Session) WasAdopted() bool

WasAdopted reports whether this session was adopted from a prior daemon (a daemon upgrade re-attaching to a surviving agent) rather than freshly spawned. An adopted PTY starts at zero bytes read even though the agent may have rendered before the upgrade, so the silent-session diagnostic can't treat its zero-output as "never rendered" (issue #1087).

func (*Session) WriteInput

func (s *Session) WriteInput(data []byte) error

func (*Session) WriteInputAndSubmit added in v0.29.0

func (s *Session) WriteInputAndSubmit(data []byte) error

WriteInputAndSubmit writes text followed by a carriage return, with a brief pause between the two so that TUI frameworks treat them as separate events. The entire operation holds writeMu to prevent interleaving from other sources.

type SessionOpts

type SessionOpts struct {
	ID         string
	Command    string
	Args       []string
	Dir        string
	Env        map[string]string
	Rows, Cols uint16
	LogPath    string
	MaxLogSize int64
	// Logger routes this session's PTY/scrollback diagnostics to the daemon's
	// logger. Nil falls back to slog.Default(). See the Session.log field.
	Logger *slog.Logger
}

type Terminal added in v0.69.0

type Terminal interface {
	// Write feeds raw PTY output bytes into the emulator. It never blocks on
	// terminal query responses (the implementation drains them).
	Write(p []byte) (int, error)
	// Resize changes the screen dimensions to cols columns by rows rows.
	Resize(cols, rows int)
	// Size returns the current dimensions as (columns, rows).
	Size() (cols, rows int)
	// Cursor returns the cursor column, row (both zero-based), and whether it
	// is currently visible.
	Cursor() (x, y int, visible bool)
	// Cell returns the rendered cell at (x, y). Out-of-range coordinates return
	// a blank cell.
	Cell(x, y int) Cell
	// Close releases resources held by the emulator (e.g. its response-drain
	// goroutine). It is safe to call more than once.
	Close() error
}

Terminal is the terminal-screen emulation surface graith needs: feed it raw PTY output with Write, then read back the rendered screen for previews and snapshots. It deliberately hides the concrete emulator (issue #1211).

Implementations are not required to be safe for concurrent use; callers serialize Write against the Size/Cursor/Cell readers (Session does this with its mutex).

Jump to

Keyboard shortcuts

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