session

package
v0.10.5 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

Package session is uam's native session backend. It replaces the private tmux server: every managed agent runs under a small detached "host" process (`uam __host`, see host.go) that owns the agent's PTY, renders its output through an in-process terminal emulator, and serves peek / reply / attach / kill over a per-session Unix socket. Hosts outlive the uam process that started them, so sessions keep running when the TUI exits — the same lifetime contract the tmux server provided — without requiring tmux to be installed.

Index

Constants

View Source
const (
	AttachPrefixEnv     = "UAM_ATTACH_PREFIX"
	AttachBackDetachEnv = "UAM_ATTACH_BACK_DETACH"

	AttachPolicyMouseEnv      = "UAM_ATTACH_POLICY_MOUSE"
	AttachPolicyPrefixEnv     = "UAM_ATTACH_POLICY_PREFIX"
	AttachPolicyBackDetachEnv = "UAM_ATTACH_POLICY_BACK_DETACH"
)
View Source
const AttachEffectiveProfileEnv = "UAM_ATTACH_EFFECTIVE_PROFILE"
View Source
const AttachMouseEnv = "UAM_ATTACH_MOUSE"
View Source
const AttachQuietEnv = "UAM_ATTACH_QUIET"
View Source
const AttachSelectedProfileEnv = "UAM_ATTACH_SELECTED_PROFILE"
View Source
const ProviderIdentityFileEnv = "UAM_PROVIDER_IDENTITY_FILE"

ProviderIdentityFileEnv names the provider-neutral identity handoff read by the host after the managed process exits.

Variables

View Source
var ErrInvalidSessionName = fmt.Errorf("session name failed allow-list")

ErrInvalidSessionName is returned when a session name fails the allow-list.

View Source
var ErrSessionBusy = errors.New("session controller is attached")

ErrSessionBusy identifies operations rejected while a controller owns the PTY.

View Source
var NameRE = regexp.MustCompile(`^uam-[a-z0-9]+-[0-9a-f]{1,16}$`)

NameRE is the allow-list for session names uam may create. It matches the canonical shape minted by adapter.startSession ("uam-<provider>-<id>"): a lowercase-alphanumeric provider segment and a hex id segment. Names that pass are safe to embed in file paths (no separators, no dots).

Functions

func DefaultDir

func DefaultDir() string

DefaultDir returns the runtime directory holding per-session sockets and state files: $UAM_SESSION_DIR if set, else a per-UID directory under the system temp dir (like tmux's /tmp/tmux-<uid>).

$XDG_RUNTIME_DIR is deliberately NOT used: systemd-logind deletes it when the user's last login session ends, not only on reboot — which would strand still-running detached hosts (they survive logout) with no socket or state file, and a later "resume" would spawn duplicates. The temp dir survives logout and is cleared on reboot, matching the hosts' actual lifetime. Unix socket paths must also stay short (the sockaddr_un limit is ~104 bytes), which rules out deep home paths.

func EnsureDir

func EnsureDir(dir string) error

EnsureDir creates the runtime directory owner-only. The 0700 mode is the security boundary: sockets and state files inside inherit protection from it, so another local user can neither attach to a session nor inject input. Because the default parent is the sticky shared temp dir, the directory is also verified to be a real directory (not a symlink) owned by the current user — a foreign pre-created /tmp/uam-<uid> is refused, like tmux refuses a foreign /tmp/tmux-<uid>.

func PrimaryScreenProvider

func PrimaryScreenProvider(identity string) bool

PrimaryScreenProvider reports whether identity names a provider that owns the primary screen. Exported so the adapter-policy parity test can check the runtime decision against each provider's declaration.

func ProcAlive

func ProcAlive(pid int) bool

ProcAlive reports whether pid is a live process (signal-0 probe). It is the native equivalent of the old tmux.PaneAlive.

func ProcStartTime added in v0.8.0

func ProcStartTime(pid int) int64

ProcStartTime returns a stable identity for pid derived from its kernel start time, or 0 when it cannot be read. Other detached uam services use it to tell their own process apart from a recycled PID.

func ProviderIdentityPath

func ProviderIdentityPath(dir, name string) (string, error)

ProviderIdentityPath returns the canonical provider identity handoff path inside the verified native-session runtime boundary.

func ReadProviderIdentity

func ReadProviderIdentity(dir, name string) (string, error)

ReadProviderIdentity returns the verified provider session identity. A missing handoff is advisory and returns an empty identity without error.

func RunAttach

func RunAttach(args []string) error

RunAttach is the entry point of `uam __attach`: it puts the terminal in raw mode and bridges it to a session host — the native replacement for `tmux attach`. It returns when the user detaches (Ctrl+B d, or Ctrl+Left while nothing is typed — see stdinFilter) or the agent exits.

func RunHost

func RunHost(args []string) error

RunHost is the entry point of the detached per-session host process (`uam __host`). It starts the agent command under a PTY, mirrors all output into a terminal emulator (for peek/replay), serves the control socket, and on agent exit marks the persisted record closed before cleaning up its runtime files. It only returns on fatal startup errors or after the agent exits.

func SocketPath

func SocketPath(dir, name string) string

SocketPath returns the control socket path for a session.

func ValidateName

func ValidateName(name string) error

ValidateName rejects session names outside the canonical allow-list.

func VerifyDir

func VerifyDir(dir string) error

VerifyDir validates an existing runtime directory without changing it. The directory is the local authorization boundary around session sockets and state, so every read/control path must call this before trusting files beneath it.

func WriteProviderIdentity

func WriteProviderIdentity(dir, name, providerSessionID string) error

WriteProviderIdentity atomically publishes a provider session identity in the native-session runtime directory.

Types

type Client

type Client struct {
	// Dir is the runtime directory holding sockets and state files.
	Dir string
	// Exe overrides the binary used to spawn hosts and attach clients
	// (normally the running uam binary itself). Tests point it at the test
	// binary.
	Exe string
}

Client talks to per-session host processes. It is the drop-in replacement for the old tmux.Client: same operations, but against uam's own session hosts instead of a tmux server.

func NewClient

func NewClient() *Client

func (*Client) AttachArgv

func (c *Client) AttachArgv(name string) ([]string, error)

AttachArgv returns the argv that attaches the current terminal to the session — the uam binary's own attach client instead of `tmux attach`.

func (*Client) Capture

func (c *Client) Capture(ctx context.Context, name string, lines int) (string, error)

Capture returns the rendered tail of the session's terminal, like `tmux capture-pane -p -J` did.

func (*Client) CreateProviderSession

func (c *Client) CreateProviderSession(ctx context.Context, spec CreateSpec) error

func (*Client) CreateSession

func (c *Client) CreateSession(ctx context.Context, name, cwd string, env map[string]string, command []string) error

CreateSession spawns a detached host running command in cwd. It returns once the host reports the agent started (or with the host's startup error), mirroring the synchronous contract of `tmux new-session -d`.

func (*Client) Doctor

func (c *Client) Doctor(ctx context.Context, name string) (RuntimeDiagnostic, error)

func (*Client) HasSession

func (c *Client) HasSession(_ context.Context, name string) bool

HasSession reports whether a live host exists for name.

func (*Client) Kill

func (c *Client) Kill(ctx context.Context, name string) error

Kill terminates the session's agent and waits for the host to confirm the session is gone. Killing a session that does not exist is an error, like `tmux kill-session` (callers that need idempotence probe HasSession).

func (*Client) KillAll

func (c *Client) KillAll(ctx context.Context) error

KillAll terminates every managed session. It replaces `tmux kill-server` and is idempotent: an empty (or missing) runtime directory is success.

func (*Client) List

func (c *Client) List(_ context.Context) ([]Info, error)

List enumerates live sessions by scanning the runtime directory's state files — no subprocess, no socket round-trips. Leftovers from a crashed host are swept once both the host and its agent are gone.

func (*Client) RuntimeCount

func (c *Client) RuntimeCount(_ context.Context) (int, error)

func (*Client) SendLine

func (c *Client) SendLine(ctx context.Context, name, text string) error

SendLine types text into the session and submits it with a single Enter (carriage return). Interior newlines are delivered literally so a multi-line prompt lands in the agent's input buffer as one prompt — the same contract the tmux SendLine implemented keystroke-by-keystroke (F13).

func (*Client) SendPrompt

func (c *Client) SendPrompt(ctx context.Context, name, text string) error

SendPrompt is SendLine for a provider that is still starting: the host types the prompt only once the provider has taken the terminal out of canonical mode. Before that switch the line discipline turns the trailing Enter into a newline, and the composer that later reads it never submits.

func (*Client) SetSessionLabel

func (c *Client) SetSessionLabel(ctx context.Context, name, label string) error

SetSessionLabel records the user-facing label for a live session; the host persists it and updates attached terminals' titles. Cosmetic: callers treat failures as non-fatal.

type CreateSpec

type CreateSpec struct {
	Name             string
	Cwd              string
	ProviderIdentity string
	ScrollbackLines  int
	Env              map[string]string
	Command          []string
	InitialPrompt    *os.File
}

type Info

type Info struct {
	Name        string
	CreatedUnix int64
	ChildPID    int
	// Cwd is the agent process's current working directory (live from /proc
	// when available, else the directory the session started in).
	Cwd string
	// Alive reports whether the agent process itself is still running. The
	// host lingers briefly after the child exits, so this is the liveness
	// signal the dashboard's Active/Failed classification keys on.
	Alive bool
}

Info is one live session as reported by List.

type RuntimeDiagnostic

type RuntimeDiagnostic struct {
	Protocols  []int `json:"protocols"`
	Controller int   `json:"controller"`
	Standby    int   `json:"standby"`
	Observer   int   `json:"observer"`
}

type SessionBusyError

type SessionBusyError struct {
	Operation string
}

SessionBusyError preserves the rejected operation across the host protocol.

func (*SessionBusyError) Error

func (err *SessionBusyError) Error() string

func (*SessionBusyError) Is

func (err *SessionBusyError) Is(target error) bool

type State

type State struct {
	Name    string `json:"name"`
	HostPID int    `json:"host_pid"`
	// HostStart / ChildStart are platform-specific stable process identities
	// derived from kernel start times (0 where unavailable). They disambiguate
	// a recycled PID from the original
	// process, so a stale state file can never make uam treat — or worse,
	// signal — an unrelated process as a session.
	HostStart        int64    `json:"host_start,omitempty"`
	ChildPID         int      `json:"child_pid"`
	ChildStart       int64    `json:"child_start,omitempty"`
	CreatedUnix      int64    `json:"created_unix"`
	Cwd              string   `json:"cwd"`
	Label            string   `json:"label,omitempty"`
	ProviderIdentity string   `json:"provider_identity,omitempty"`
	Command          []string `json:"command"`
}

State is the on-disk record a host writes next to its socket. It is the native replacement for `tmux list-sessions` output: List scans these files to enumerate live sessions without dialing every socket.

Jump to

Keyboard shortcuts

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