Help us shape the future of Go! Take the 2026 Go Developer Survey

tymux

package
v1.48.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

Documentation

Overview

Package tymux contains the TymuxBackend implementation of session.ProcessManager (session/backend_tymux.go), which delegates to a tymuxd instance over gRPC via the generated Connect-Go client in github.com/tstapler/tymux/clients/go.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotImplemented = errors.New("tymux: not implemented")

ErrNotImplemented is returned by tymuxGRPCSession methods that a later epic (2.3+, standing Attach stream / control-mode) has not yet implemented.

View Source
var ErrNotSupportedOnTymuxBackend = errors.New("not supported by the tymux backend")

ErrNotSupportedOnTymuxBackend is returned by GetPTY/GetPanePID (Story 2.2.5, Pattern Decisions): tymux has no local PTY file descriptor or OS pane PID to hand back — every terminal I/O path goes over gRPC, not a local file/process the caller could read or signal directly. Returning this explicit, typed error (never a bare nil/zero value or a panic) lets a generic ProcessManager caller distinguish "not supported by this backend" from "supported but currently unavailable."

View Source
var ErrTymuxdUnreachable = errors.New("tymux: tymuxd unreachable")

ErrTymuxdUnreachable indicates an RPC could not reach a tymuxd daemon at all — connection refused, or nothing listening on the configured socket/address — as opposed to a request that reached tymuxd but was rejected there (a live daemon telling us "no" is a different failure mode than no daemon to ask).

Story 2.2.6 / adversarial-review.md Blocker: stapler-squad does not start or supervise tymuxd itself (a deliberate scope decision — no ensureServerRunning-equivalent, unlike BackendTmux's recoverFromServerFailure). It assumes an out-of-band, already-running daemon. Callers match this sentinel with errors.Is to distinguish "daemon not started/crashed/misconfigured" from "daemon rejected the request" — research/ux.md:218-224's "should not present as the same 'reconnecting' transient state."

Functions

func CellsToSGR

func CellsToSGR(grid []*v1.Row) (string, error)

CellsToSGR renders a PaneSnapshot's cell grid (PaneSnapshot.grid) into an ANSI/SGR-encoded string matching `tmux capture-pane -p -e`'s attribute-preserving output shape — the renderer CapturePaneContent() (Task 2.2.2d) needs and CapturePaneContentRaw() deliberately doesn't (that method only joins Cell.text, no SGR).

Rows are newline-joined (mirroring rowsToPlainText's join). Within a row, an SGR escape sequence is emitted only when a cell's fg/bg/attrs differ from the previous cell's (Task 2.6.1a/Story 2.6.1) — an unbroken attribute run gets exactly one sequence before it, not one per cell.

func NewRealTransport

func NewRealTransport(addr string) rpcTransport

NewRealTransport constructs an rpcTransport backed by a real Connect-Go gRPC client against a live tymuxd. addr is the tymuxd base URL (e.g. "http://127.0.0.1:7419"); pass "" to use tymuxdAddr()'s TYMUXD_ADDR-or-default resolution.

func ReconnectMetricsSnapshot

func ReconnectMetricsSnapshot() map[string]int64

ReconnectMetricsSnapshot returns a point-in-time copy of tymux_attach_stream_reconnects_total, keyed by cause — exported so a future Observability Plan wiring (e.g. an HTTP /metrics handler) can read it without reaching into package-private state, mirroring session/tmux's own ForkPressureSnapshot() convention.

Types

type ClientFanout

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

ClientFanout is a local, in-process multi-subscriber broadcast for one standing Attach stream's output events (Story 2.3.2): the GoF Observer pattern per Pattern Decisions, satisfying SubscribeToControlModeUpdates' multi-subscriber contract from a single upstream stream instead of opening a second Attach call per subscriber.

Broadcast is non-blocking (drop-if-full) so one slow subscriber can never stall the standing stream's reader goroutine — matching the existing lossy-broadcast precedent tymuxd's own output_gap semantics already establish server-side (ADR-003-adjacent design, see Attach's proto doc).

func NewClientFanout

func NewClientFanout() *ClientFanout

NewClientFanout constructs an empty ClientFanout.

func (*ClientFanout) Broadcast

func (f *ClientFanout) Broadcast(data []byte)

Broadcast sends data to every current subscriber's channel with a non-blocking send — a subscriber whose buffer is full drops this frame rather than blocking the caller (the standing stream's reader goroutine).

func (*ClientFanout) Subscribe

func (f *ClientFanout) Subscribe() (string, chan []byte)

Subscribe registers a new subscriber and returns its id (for Unsubscribe) and a channel that receives every subsequent Broadcast call's data. The channel is closed by Unsubscribe, never by Broadcast.

func (*ClientFanout) Unsubscribe

func (f *ClientFanout) Unsubscribe(id string)

Unsubscribe removes and closes the subscriber channel for id. A no-op if id is unknown or was already unsubscribed.

type TymuxManager

type TymuxManager interface {
	// Lifecycle
	Start(dir string) error
	RestoreWithWorkDir(workDir string) error
	Close() error
	IsAlive() bool

	// Identification
	GetSessionIdentifier() string

	// Existence / state
	HasSession() bool

	// Working directory (via pane introspection)
	GetCurrentWorkingDirectory() (string, error)

	// Terminal I/O
	GetPTY() (*os.File, error)
	SendKeys(keys string) (int, error)
	TapEnter() error
	SendPromptWithEnter(prompt string) error
	SendInputViaControlMode(ctx context.Context, data []byte) error

	// Terminal state
	CapturePaneContent() (string, error)
	CapturePaneContentRaw() (string, error)
	CapturePaneContentWithOptions(startLine, endLine string) (string, error)
	CaptureViewport(lines int) (string, error)
	GetCursorPosition() (x, y int, err error)
	GetPaneDimensions() (width, height int, err error)
	SetWindowSize(cols, rows int) error
	SetDetachedSize(width, height int, instanceTitle string) error
	RefreshClient() error

	// Process metadata
	GetPanePID() (int32, error)

	// Content helpers
	HasUpdated() (updated bool, hasPrompt bool, content string)
	FilterBanners(content string) (string, int)
	HasMeaningfulContent(content string) bool

	// Streaming (control mode)
	StartControlMode() error
	StopControlMode() error
	// SubscribeToControlModeUpdates returns a subscription ID and a bidirectional channel.
	// The channel must be bidirectional (chan []byte, not <-chan []byte) because some callers
	// write synthetic frames for testing. Implementations must not write to the channel themselves.
	SubscribeToControlModeUpdates() (string, chan []byte)
	UnsubscribeFromControlModeUpdates(id string)

	// Attach (interactive TUI)
	Attach() (chan struct{}, error)
	DetachSafely() error

	// Exit notifications
	SetOnExitCallback(fn func(string))
	ResetExitOnce()

	// Reconnect state (Task 2.5.2e): exposes ReconnectLoop's live
	// progress — whether this session's standing stream is currently
	// reconnecting, which attempt it's on, and what triggered it — so a
	// future UI (ux.md Surface 2) doesn't have to reverse-engineer it
	// from the aggregate tymux_attach_stream_reconnects_total metric.
	// The one deliberate addition beyond ProcessManager's mirrored
	// method set (requirements.md's constraint keeps ProcessManager
	// itself untouched); state exposure only, no UI here.
	ReconnectState() (reconnecting bool, attempt int, cause string)

	// BackendRestarted reports whether tymuxd was detected to have restarted
	// out from under this session (Story 2.5.3's daemon-restart contract) —
	// the pane's original process is orphaned, not reattached, per
	// Engine::revive_session's always-spawn-fresh behavior on the tymux
	// side. Exposed on the interface (not just the concrete type) so a real
	// caller holding only a TymuxManager/ProcessManager reference can
	// observe this state, not just tests asserting on the unexported type.
	BackendRestarted() (restarted bool, since time.Time)
}

TymuxManager is the interface satisfied by the concrete tymux gRPC session implementation (tymuxGRPCSession, session.go). It mirrors session.ProcessManager's exact method set so TymuxBackend (session/backend_tymux.go) can forward every call one-to-one, the same shape TmuxBackend/TmuxManager use for the tmux backend (session/tmux_backend.go, session/tmux_process_manager.go).

Defined as its own interface here (rather than the session package's ProcessManager directly) because session/tymux is imported BY the session package — depending on session.ProcessManager here would create an import cycle. Duplicating the method set also gives tests a seam to substitute a fake TymuxManager without a live tymuxd daemon.

func NewTymuxGRPCSession

func NewTymuxGRPCSession(transport rpcTransport) TymuxManager

NewTymuxGRPCSession constructs a tymuxGRPCSession using the given rpcTransport, returned as a TymuxManager since tymuxGRPCSession itself is unexported.

Jump to

Keyboard shortcuts

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