ttywatch

package
v0.0.53 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Index

Constants

View Source
const (

	// HeadlessWaitingLine is logged to stderr once during the SIGINT grace window.
	HeadlessWaitingLine = "waiting for program to exit..."
)

Variables

This section is empty.

Functions

func AttachRelay added in v0.0.51

func AttachRelay(ctx context.Context, listenAddr, sessionID, attachMode string, cfg AttachRelayConfig, sink AttachSink) error

AttachRelay connects to a live terminal and relays output/input through sink.

func AttachWriter added in v0.0.48

func AttachWriter(listenAddr, sessionID, attachMode string) (detached bool, err error)

AttachWriter attaches stdin/stdout to a live terminal session. Returns detached=true when the user pressed Ctrl-].

func DialTerminal added in v0.0.48

func DialTerminal(listenAddr, sessionID, attachMode string) (*websocket.Conn, error)

DialTerminal connects to a live terminal session and consumes the handshake.

func InjectInput added in v0.0.48

func InjectInput(listenAddr, sessionID string, input []byte) error

InjectInput writes input to a live session via server-side HTTP inject API.

func IsScreenSnapshotFrame

func IsScreenSnapshotFrame(data []byte) bool

IsScreenSnapshotFrame reports ptywrap screen snapshot binary frames.

func IsServeSubcommand

func IsServeSubcommand(arg string) bool

IsServeSubcommand reports whether arg is a slug-based serve reexec token.

func ParseResizeMessage added in v0.0.51

func ParseResizeMessage(data []byte) (cols, rows int, ok bool)

ParseResizeMessage extracts cols/rows from a browser resize JSON message.

func PrepareSessionInjectMode

func PrepareSessionInjectMode(listenAddr, sessionID string) error

PrepareSessionInjectMode disables canonical mode before injecting input.

func ProcessAlive

func ProcessAlive(pid int) bool

ProcessAlive reports whether pid responds to signal 0.

func ReadSnapshot

func ReadSnapshot(listenAddr, sessionID string) (frame string, scrollback string, cols, rows int, err error)

ReadSnapshot fetches the current screen frame for a live session via a single attach_mode=screen WebSocket (no resize). Scrollback is left empty so rendering prefers the server screen frame.

func RegistryDir added in v0.0.48

func RegistryDir(cfg RegistryConfig) string

RegistryDir returns {home}/{subdir}/ for the given config.

func RemoveRegistry

func RemoveRegistry(cfg RegistryConfig, sessionID string)

RemoveRegistry deletes the registry file and any claim for a session.

func RemoveRegistryIfMatch

func RemoveRegistryIfMatch(cfg RegistryConfig, sessionID, listenAddr string, pid int)

RemoveRegistryIfMatch deletes the registry file only when the on-disk entry still belongs to the caller (same listen address and pid). Stale __serve__ cleanup can otherwise delete a newer session that reused the same session id.

func RenderObserverFrame

func RenderObserverFrame(data []byte, cols, rows int) []byte

RenderObserverFrame converts observer-mode PTY bytes to visible text without CSI/C0 leaks.

func RenderSnapshotOutput

func RenderSnapshotOutput(frame, scrollback string, cols, rows int) string

RenderSnapshotOutput converts a ptywrap screen snapshot frame and/or raw scrollback into printable snapshot text, matching the screen attach pipeline used by run.

func RenderSnapshotScrollback

func RenderSnapshotScrollback(raw string, cols, rows int) string

RenderSnapshotScrollback converts accumulated scrollback to printable snapshot text.

func RenderVTStateToText

func RenderVTStateToText(vt vt10x.Terminal, cols, rows int) ([]byte, bool)

RenderVTStateToText extracts printable lines from a vt10x terminal state.

func ReserveCustomSessionID

func ReserveCustomSessionID(cfg RegistryConfig, sessionID string) (func(), error)

ReserveCustomSessionID validates id and ensures it is not held by a live session. Stale registry entries are pruned so the id can be reused. On success a side-car claim file is written so the exclusive flock can be released before __serve__ registers (the claim is not a *.json registry entry, so waiters do not treat it as a ready session).

func ReserveRegistrySessionID

func ReserveRegistrySessionID(cfg RegistryConfig) (string, func(), error)

ReserveRegistrySessionID returns the next session-N id under flock and writes a side-car claim so the lock can be released before WaitForRegistryEntry.

func SanitizeForPrint

func SanitizeForPrint(data string) string

SanitizeForPrint strips CSI, OSC, and C0 control sequences except \n \r \t.

func ScreenSnapshotToText

func ScreenSnapshotToText(data []byte, cols, rows int) ([]byte, bool)

ScreenSnapshotToText renders a screen snapshot frame to plain text.

func SendMessage added in v0.0.48

func SendMessage(listenAddr, sessionID, message string, suffixCR bool) error

SendMessage prepares inject mode and sends verbatim bytes. When suffixCR is true, appends '\r' only if message contains no '\r'.

func ServeSession

func ServeSession(ctx context.Context, opts ServeOptions) error

ServeSession runs the embedded ptywrap HTTP server until the command exits.

func ServeSubcommand

func ServeSubcommand(argv []string) string

ServeSubcommand wraps SlugifyCommandLine as the internal reexec argv token.

func SlugifyCommandLine

func SlugifyCommandLine(argv []string) string

SlugifyCommandLine produces a stable slug from a session command argv.

func SnapshotText

func SnapshotText(listenAddr, sessionID string) (string, error)

SnapshotText returns rendered printable snapshot text for a live session.

func StreamObserver added in v0.0.48

func StreamObserver(listenAddr, sessionID string, w io.Writer) error

StreamObserver streams readonly terminal output to w.

func TCPReachable

func TCPReachable(addr string) bool

TCPReachable reports whether a TCP listen address accepts connections.

func TTYAttachOnConnect added in v0.0.51

func TTYAttachOnConnect(stdout *os.File) func(upstream *wsWriter) error

TTYAttachOnConnect sends an initial resize and watches SIGWINCH when stdout is a TTY.

func TTYAttachTerminalSize added in v0.0.51

func TTYAttachTerminalSize(stdout *os.File) (cols, rows int)

TTYAttachTerminalSize returns the current terminal dimensions when stdout is a TTY.

func TTYWatchHome

func TTYWatchHome() (string, error)

TTYWatchHome returns the tty-watch data directory.

func TerminalWebSocketURL added in v0.0.48

func TerminalWebSocketURL(listenAddr, sessionID, attachMode string) (string, error)

TerminalWebSocketURL builds the WebSocket URL for a terminal session.

func WaitHeadless added in v0.0.48

func WaitHeadless(ctx context.Context, result *HeadlessRunResult, command []string) error

WaitHeadless blocks until the serve child exits, forwarding SIGINT to the PTY session with the same grace window as tty-watch run --headless.

func WebAttachOnConnect added in v0.0.51

func WebAttachOnConnect(listenAddr, sessionID string, out io.Writer, cols, rows int) func(upstream *wsWriter) error

WebAttachOnConnect primes browser output with snapshot scrollback. Resize is sent by the browser client after the websocket opens so upstream resize acks are not relayed before interactive input during attach setup.

func WriteRegistry

func WriteRegistry(cfg RegistryConfig, entry RegistryEntry) error

WriteRegistry creates the registry file for a live session and drops any provisional claim for the same id.

Types

type AttachRelayConfig added in v0.0.51

type AttachRelayConfig struct {
	ExitOnTerminalExit           bool
	SkipScreenSnapshotConversion bool
	Cols, Rows                   int
	OnConnect                    func(upstream *wsWriter) error
}

AttachRelayConfig controls relayTerminalOutput behaviour for an attach session.

type AttachSink added in v0.0.51

type AttachSink interface {
	OutputWriter() io.Writer
	RunInput(ctx context.Context, upstream *wsWriter) error
	// DrainOutputAfterInput reports whether upstream output should be read briefly
	// after stdin reaches EOF (piped attach clients).
	DrainOutputAfterInput() bool
}

AttachSink receives PTY output and forwards interactive input to the upstream terminal.

type CheckWritableFunc added in v0.0.48

type CheckWritableFunc func(scrollback []byte) WritableStatus

CheckWritableFunc inspects scrollback bytes for prompt readiness.

type EphemeralSession

type EphemeralSession struct {
	Home           string
	RegistrySubdir string
	SessionID      string
	Command        []string
	ExtraPaths     []string
	// contains filtered or unexported fields
}

EphemeralSession manages a short-lived tty-watch session without CLI subprocesses.

func NewEphemeralSession

func NewEphemeralSession(home, sessionID string, command []string) *EphemeralSession

NewEphemeralSession returns a session scoped to home, id, and command argv.

func (*EphemeralSession) Entry

func (s *EphemeralSession) Entry() *RegistryEntry

Entry returns the current registry entry when available.

func (*EphemeralSession) Kill

func (s *EphemeralSession) Kill() error

Kill terminates the session and removes its registry entry.

func (*EphemeralSession) Send

func (s *EphemeralSession) Send(message string) error

Send injects follow-up text into the live session.

func (*EphemeralSession) Snapshot

func (s *EphemeralSession) Snapshot() (string, error)

Snapshot returns rendered printable snapshot text.

func (*EphemeralSession) StartDetached

func (s *EphemeralSession) StartDetached(ctx context.Context, binaryPath string) error

StartDetached re-executes binaryPath with a slug-based serve token.

func (*EphemeralSession) StartInProcess

func (s *EphemeralSession) StartInProcess(ctx context.Context) error

StartInProcess launches ServeSession in the current process.

func (*EphemeralSession) WaitWritable

func (s *EphemeralSession) WaitWritable(check CheckWritableFunc, timeout time.Duration) WritableStatus

WaitWritable polls check readiness against live scrollback.

type ExitStatus added in v0.0.48

type ExitStatus struct {
	Code int
}

ExitStatus reports a non-zero exit code from headless wait handling.

func (*ExitStatus) Error added in v0.0.48

func (e *ExitStatus) Error() string

type HeadlessRunOptions added in v0.0.48

type HeadlessRunOptions struct {
	Home           string
	RegistrySubdir string // e.g. "grok-tty-registry"; empty uses tty-watch default
	SessionID      string // empty → auto-reserve session-N
	Command        []string
	BinaryPath     string // re-exec target for __serve__ (os.Args[0])
	Cwd            string
	ExtraPaths     []string
	// KeepAlive, when true, sets TTY_WATCH_KEEP_ALIVE so __serve__ stays alive
	// after the PTY child exits (intentional keep-tty). Default false: serve
	// exits shortly after the child so short attached runs leave no orphans.
	KeepAlive bool
}

HeadlessRunOptions configures a detached __serve__ child session.

type HeadlessRunResult added in v0.0.48

type HeadlessRunResult struct {
	SessionID string
	Entry     *RegistryEntry
	Cmd       *exec.Cmd
	Registry  RegistryConfig
	Wait      func() error
}

HeadlessRunResult is returned after the detached serve child registers.

func HeadlessRun added in v0.0.48

func HeadlessRun(ctx context.Context, opts HeadlessRunOptions) (*HeadlessRunResult, error)

HeadlessRun reserves a session id, spawns a detached __serve__ child, and waits for the registry entry.

type RegistryConfig added in v0.0.48

type RegistryConfig struct {
	Home   string // AGENT_RUN_HOME or TTY_WATCH_HOME
	Subdir string // "registry" for tty-watch; "grok-tty-registry" for agent-run
}

RegistryConfig selects the registry directory under a home path.

func DefaultRegistryConfig added in v0.0.48

func DefaultRegistryConfig(home string) RegistryConfig

DefaultRegistryConfig returns the tty-watch default registry layout.

type RegistryEntry

type RegistryEntry struct {
	SessionID  string   `json:"session_id"`
	ListenAddr string   `json:"listen_addr"`
	PID        int      `json:"pid"`
	CreatedAt  string   `json:"created_at"`
	Command    []string `json:"command"`
	Cwd        string   `json:"cwd,omitempty"`
}

RegistryEntry maps a tty-watch session id to the embedded ptywrap listen address.

func ListRegistryEntries

func ListRegistryEntries(cfg RegistryConfig, prune bool) ([]RegistryEntry, error)

ListRegistryEntries returns all registry entries, optionally pruning unreachable ones.

func LookupAcrossSubdirs added in v0.0.48

func LookupAcrossSubdirs(home string, subdirs []string, sessionID string) (*RegistryEntry, string, error)

LookupAcrossSubdirs searches subdirs under home for a session id. Returns the entry, the subdir where it was found, or an error.

func ReadRegistry

func ReadRegistry(cfg RegistryConfig, sessionID string) (*RegistryEntry, error)

ReadRegistry loads a session registry entry.

func WaitForRegistryEntry

func WaitForRegistryEntry(cfg RegistryConfig, sessionID string, timeout time.Duration) (*RegistryEntry, error)

WaitForRegistryEntry polls until a session registry entry is available.

type ServeOptions

type ServeOptions struct {
	SessionID      string
	Command        []string
	Home           string
	RegistrySubdir string
	Cwd            string
	ExtraPaths     []string
	KeepAlive      bool
}

ServeOptions configures an embedded PTY server session.

type TTYAttachSink added in v0.0.51

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

TTYAttachSink relays attach IO through the process stdin/stdout.

func NewTTYAttachSink added in v0.0.51

func NewTTYAttachSink() (*TTYAttachSink, func(), error)

NewTTYAttachSink prepares stdin/stdout for interactive attach.

func (*TTYAttachSink) Detached added in v0.0.51

func (s *TTYAttachSink) Detached() bool

func (*TTYAttachSink) DrainOutputAfterInput added in v0.0.51

func (s *TTYAttachSink) DrainOutputAfterInput() bool

func (*TTYAttachSink) OutputWriter added in v0.0.51

func (s *TTYAttachSink) OutputWriter() io.Writer

func (*TTYAttachSink) RunInput added in v0.0.51

func (s *TTYAttachSink) RunInput(ctx context.Context, upstream *wsWriter) error

type WSAttachClient added in v0.0.48

type WSAttachClient struct {
	Conn *websocket.Conn

	Role string
	// contains filtered or unexported fields
}

WSAttachClient is a lightweight WebSocket attach helper for doctest harnesses.

func DialPTYAttach added in v0.0.48

func DialPTYAttach(listenAddr, sessionID, attachMode string) (*WSAttachClient, error)

DialPTYAttach connects to a ptywrap session with the given attach mode.

func (*WSAttachClient) Close added in v0.0.48

func (c *WSAttachClient) Close()

Close closes the WebSocket connection.

func (*WSAttachClient) Output added in v0.0.48

func (c *WSAttachClient) Output() string

Output returns accumulated PTY output from the attach client.

func (*WSAttachClient) TryResize added in v0.0.48

func (c *WSAttachClient) TryResize(cols, rows int) error

TryResize sends a resize control message.

func (*WSAttachClient) TryWriteInput added in v0.0.48

func (c *WSAttachClient) TryWriteInput(data []byte) error

TryWriteInput sends binary input when the attach role allows writing.

type WebSocketAttachSink added in v0.0.51

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

WebSocketAttachSink relays attach IO through a browser WebSocket connection.

func NewWebSocketAttachSink added in v0.0.51

func NewWebSocketAttachSink(conn *websocket.Conn) *WebSocketAttachSink

NewWebSocketAttachSink creates a sink for browser terminal websocket clients.

func (*WebSocketAttachSink) DrainOutputAfterInput added in v0.0.51

func (s *WebSocketAttachSink) DrainOutputAfterInput() bool

func (*WebSocketAttachSink) OutputWriter added in v0.0.51

func (s *WebSocketAttachSink) OutputWriter() io.Writer

func (*WebSocketAttachSink) RunInput added in v0.0.51

func (s *WebSocketAttachSink) RunInput(ctx context.Context, upstream *wsWriter) error

type WritableStatus added in v0.0.48

type WritableStatus struct {
	Ready  bool
	Reason string
	State  string
}

WritableStatus reports whether a TTY session is ready to receive injected input.

func WaitUntilWritable added in v0.0.48

func WaitUntilWritable(check CheckWritableFunc, listenAddr, sessionID string, timeout time.Duration) WritableStatus

WaitUntilWritable polls check until ready or timeout. A zero or negative timeout waits indefinitely.

Jump to

Keyboard shortcuts

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