agent

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package agent implements the core Pocketmux agent lifecycle: start, connect, shutdown.

Index

Constants

View Source
const (
	// DefaultCleanupInterval is how often the cleaner scans for stale peers.
	DefaultCleanupInterval = 30 * time.Second

	// DefaultIdleTimeout is how long a peer can go without sending a ping
	// before being considered stale and closed.
	DefaultIdleTimeout = 60 * time.Second
)

Variables

View Source
var ErrAgentNotRunning = errors.New("agent is not running")

ErrAgentNotRunning is returned when the agent is not running and the caller requested a stop. main.go maps this to exit code 1.

Functions

func CleanStalePIDFile

func CleanStalePIDFile(path string) error

CleanStalePIDFile reads the PID file, checks if the process is still running, and removes the file if the process is no longer alive (stale PID). Returns nil if the file was cleaned up or didn't exist. Returns an error only if the PID file exists and the process is still running.

func EnsureRunning

func EnsureRunning(paths config.Paths, store auth.SecretStore, mgr service.Manager) error

EnsureRunning checks if the agent is already running and starts it if not. Returns nil if the agent is running (or was started successfully). Does nothing if no identity exists (agent can't authenticate without one). If mgr is non-nil and a service is installed, tries the service manager first.

Uses flock on the PID file to serialize concurrent callers, preventing duplicate agent spawns from racing pmux commands.

func IsFatalInitError

func IsFatalInitError(err error) bool

IsFatalInitError reports whether err is a FatalInitError.

func IsProcessRunning

func IsProcessRunning(pid int) bool

IsProcessRunning checks whether a process with the given PID is alive. Uses kill(pid, 0) — signal 0 does not send a signal but checks for process existence.

func PIDFilePath

func PIDFilePath(paths config.Paths) string

PIDFilePath returns the path to the agent PID file.

func ReadPIDFile

func ReadPIDFile(path string) (int, error)

ReadPIDFile reads and parses the PID from the given file path. Returns the PID value or an error if the file cannot be read or parsed.

func RemovePIDFile

func RemovePIDFile(path string)

RemovePIDFile removes the PID file at the given path. This is best-effort: errors are silently ignored (e.g., file already removed).

func Run

func Run(ctx context.Context, paths config.Paths, hmacSecret, version, installMethod string) error

Run starts the Pocketmux agent. It connects to the signaling server, handles WebRTC connections, and monitors the tmux server. Blocks until the context is canceled (SIGTERM/SIGINT or fatal error).

func RunAgentDetail added in v0.0.4

func RunAgentDetail(version string, paths config.Paths, mgr service.Manager, w io.Writer) error

RunAgentDetail prints detailed agent status: version, PID, service state, uptime (best-effort via ps), and recent log lines. This backs "pmux agent status".

Returns ErrAgentNotRunning when the agent PID is missing or stale.

func RunAgentStart added in v0.0.4

func RunAgentStart(paths config.Paths, store auth.SecretStore, mgr service.Manager, w io.Writer) error

RunAgentStart starts the Pocketmux agent. It checks whether the agent is already running, tries the OS service manager if installed, and falls back to a direct spawn via EnsureRunning.

func RunAgentStop added in v0.0.4

func RunAgentStop(paths config.Paths, mgr service.Manager, w io.Writer) error

RunAgentStop stops the Pocketmux agent. It tries the OS service manager first (if installed), then falls back to direct PID-based stop via SIGTERM with a SIGKILL fallback after 5 seconds.

Returns ErrAgentNotRunning if no agent process is found. Returns nil on successful stop (including stale PID cleanup).

func RunInit added in v0.0.4

func RunInit(paths config.Paths, cfg config.Config, store auth.SecretStore, mgr service.Manager, tmuxPath string, r io.Reader, w io.Writer) error

RunInit generates a new Pocketmux identity and writes the initial config file. If an identity already exists, it displays the existing device ID and returns. The service manager install is best-effort — failures are reported but non-fatal.

func RunPair added in v0.0.4

func RunPair(paths config.Paths, cfg config.Config, store auth.SecretStore, mgr service.Manager, hmacSecret string, r io.Reader, w io.Writer) error

RunPair pairs this host with a mobile device via the signaling server. It displays a QR code for scanning, waits for the mobile to complete the pairing handshake, and stores the resulting shared secret locally.

func RunStatus

func RunStatus(params StatusParams, w io.Writer) error

RunStatus shows a comprehensive status overview: agent process, service registration, tmux session count, and paired mobile device info.

func RunUninstall

func RunUninstall(paths config.Paths, store auth.SecretStore, mgr service.Manager, keepConfig bool, hmacSecret string, skipConfirm bool, r io.Reader, w io.Writer) error

RunUninstall removes Pocketmux completely — the reverse of init. It stops the agent, uninstalls the OS service, un-registers from the signaling server, and removes local config/keys. If keepConfig is true, local config and keys are preserved.

func RunUnpair

func RunUnpair(paths config.Paths, store auth.SecretStore, hmacSecret string, r io.Reader, w io.Writer) error

RunUnpair removes the paired mobile device after confirmation. It notifies the signaling server (best-effort) and signals the running agent to close active connections before removing the local pairing.

func StopRunning

func StopRunning(paths config.Paths) error

StopRunning stops the background agent if it is running. Returns nil if no agent was running or after a successful stop. Returns an error only if the agent could not be stopped.

func WritePIDFile

func WritePIDFile(path string) error

WritePIDFile writes the current process PID to the given path atomically. Uses write-to-tmp + rename to prevent partial reads by concurrent processes.

Types

type ConnectionCleaner

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

ConnectionCleaner periodically scans for idle peers and closes them. A peer is considered idle if it has not sent a ping within the configured timeout (default: 60s). The cleaner runs a goroutine that ticks at the configured interval (default: 30s).

When a PeerStateChecker is configured (via WithStateChecker), the cleaner also closes peers whose PeerConnection is in "failed" or "closed" state as a safety net for the primary state change handlers.

func NewConnectionCleaner

func NewConnectionCleaner(handler *Handler, closer PeerCloser, logger *slog.Logger) *ConnectionCleaner

NewConnectionCleaner creates a ConnectionCleaner with default timing.

func (*ConnectionCleaner) Run

func (cc *ConnectionCleaner) Run(ctx context.Context)

Run starts the cleanup loop. Blocks until the context is canceled.

func (*ConnectionCleaner) WithInterval

func (cc *ConnectionCleaner) WithInterval(d time.Duration) *ConnectionCleaner

WithInterval sets a custom scan interval (for testing).

func (*ConnectionCleaner) WithStateChecker

func (cc *ConnectionCleaner) WithStateChecker(sc PeerStateChecker) *ConnectionCleaner

WithStateChecker sets a PeerStateChecker for safety-net cleanup. When set, sweep() also closes peers whose PeerConnection is in "failed" or "closed" state.

func (*ConnectionCleaner) WithTimeout

func (cc *ConnectionCleaner) WithTimeout(d time.Duration) *ConnectionCleaner

WithTimeout sets a custom idle timeout (for testing).

type FatalInitError

type FatalInitError struct {
	Err error
}

FatalInitError wraps errors that won't self-resolve on restart, such as missing identity, corrupt config, or secret store failures. These should cause the agent to exit without triggering a service restart.

func (*FatalInitError) Error

func (e *FatalInitError) Error() string

func (*FatalInitError) Unwrap

func (e *FatalInitError) Unwrap() error

type Handler

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

Handler processes protocol messages from mobile clients and dispatches them to the appropriate tmux operations.

func NewHandler

func NewHandler(tmuxClient *tmux.Client, send SendFunc, logger *slog.Logger, agentVersion, updateStateFile string) *Handler

NewHandler creates a protocol message handler.

func (*Handler) GetStalePeers

func (h *Handler) GetStalePeers(timeout time.Duration) []string

GetStalePeers returns peer IDs that have not sent a ping within the given timeout. A peer with no recorded ping time is not considered stale (it may not have connected long enough to send its first ping).

func (*Handler) HandleMessage

func (h *Handler) HandleMessage(peerID string, msg protocol.Message)

HandleMessage processes an incoming protocol message from a peer. This is the ProtocolHandler callback for the PeerManager.

func (*Handler) PeerDisconnected

func (h *Handler) PeerDisconnected(peerID string)

PeerDisconnected cleans up state when a peer disconnects.

func (*Handler) SetContext

func (h *Handler) SetContext(ctx context.Context)

SetContext sets the agent lifecycle context for deriving per-peer contexts. Called by agent.Run() after creating the cancelable agent context.

type PeerCloser

type PeerCloser interface {
	ClosePeer(deviceID string)
}

PeerCloser closes a specific peer connection by device ID.

type PeerStateChecker

type PeerStateChecker interface {
	PeerStates() map[string]string
}

PeerStateChecker provides PeerConnection state information for safety-net cleanup. The ConnectionCleaner uses this to detect peers stuck in failed or closed state that weren't cleaned up by the primary state handlers.

type SendFunc

type SendFunc func(peerID string, msg protocol.Message) error

SendFunc sends a protocol message to a specific peer.

type SessionLister

type SessionLister interface {
	ListSessions() ([]tmux.Session, error)
}

SessionLister abstracts tmux session listing for testability. tmux.Client satisfies this interface.

type StatusParams

type StatusParams struct {
	Version           string
	PairedDevicesPath string
	Store             auth.SecretStore
	PIDFilePath       string
	ServiceManager    service.Manager // nil-safe: treated as "not installed"
	Sessions          SessionLister   // nil-safe: treated as 0 sessions
}

StatusParams holds all dependencies for the RunStatus command.

Jump to

Keyboard shortcuts

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