Documentation
¶
Overview ¶
Package agent implements the core Pocketmux agent lifecycle: start, connect, shutdown.
Index ¶
- Constants
- Variables
- func CleanStalePIDFile(path string) error
- func EnsureRunning(paths config.Paths, store auth.SecretStore, mgr service.Manager) error
- func IsFatalInitError(err error) bool
- func IsProcessRunning(pid int) bool
- func PIDFilePath(paths config.Paths) string
- func ReadPIDFile(path string) (int, error)
- func RemovePIDFile(path string)
- func Run(ctx context.Context, paths config.Paths, ...) error
- func RunAgentDetail(version string, paths config.Paths, mgr service.Manager, w io.Writer) error
- func RunAgentStart(paths config.Paths, store auth.SecretStore, mgr service.Manager, w io.Writer) error
- func RunAgentStop(paths config.Paths, mgr service.Manager, w io.Writer) error
- func RunInit(paths config.Paths, cfg config.Config, store auth.SecretStore, ...) error
- func RunPair(paths config.Paths, cfg config.Config, store auth.SecretStore, ...) error
- func RunStatus(params StatusParams, w io.Writer) error
- func RunUninstall(paths config.Paths, store auth.SecretStore, mgr service.Manager, ...) error
- func RunUnpair(paths config.Paths, store auth.SecretStore, hmacSecret string, r io.Reader, ...) error
- func StopRunning(paths config.Paths) error
- func WritePIDFile(path string) error
- type ConnectionCleaner
- type FatalInitError
- type Handler
- type PeerCloser
- type PeerStateChecker
- type SendFunc
- type SessionLister
- type StatusParams
Constants ¶
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 ¶
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 ¶
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 ¶
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 ¶
IsFatalInitError reports whether err is a FatalInitError.
func IsProcessRunning ¶
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 ¶
PIDFilePath returns the path to the agent PID file.
func ReadPIDFile ¶
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 ¶
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
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
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 ¶
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 ¶
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 ¶
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 ¶
HandleMessage processes an incoming protocol message from a peer. This is the ProtocolHandler callback for the PeerManager.
func (*Handler) PeerDisconnected ¶
PeerDisconnected cleans up state when a peer disconnects.
func (*Handler) SetContext ¶
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 ¶
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 SessionLister ¶
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.