Files
gastown/internal/connection/connection.go
rictus ca3c6da8ec feat(connection): Add Connection interface for local/remote ops (gt-f9x.7)
Define the Connection interface that abstracts file operations, command
execution, and tmux management for both local and remote (SSH) contexts.

Includes:
- Connection interface with file, exec, and tmux operations
- FileInfo interface for remote stat results
- BasicFileInfo implementation with JSON serialization
- Error types for connection failures

This enables Gas Town to manage rigs on remote machines using the
same interface as local operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 19:20:32 -08:00

171 lines
4.7 KiB
Go

// Package connection provides an abstraction for local and remote operations.
// This allows Gas Town to manage rigs on remote machines via SSH using
// the same interface as local operations.
package connection
import (
"io/fs"
"time"
)
// Connection abstracts file operations, command execution, and tmux management
// for both local and remote (SSH) execution contexts.
type Connection interface {
// Identification
// Name returns a human-readable name for this connection.
Name() string
// IsLocal returns true if this is a local connection.
IsLocal() bool
// File operations
// ReadFile reads the named file and returns its contents.
ReadFile(path string) ([]byte, error)
// WriteFile writes data to the named file with the given permissions.
WriteFile(path string, data []byte, perm fs.FileMode) error
// MkdirAll creates a directory and all parent directories.
MkdirAll(path string, perm fs.FileMode) error
// Remove removes the named file or empty directory.
Remove(path string) error
// RemoveAll removes the named file or directory and any children.
RemoveAll(path string) error
// Stat returns file info for the named file.
Stat(path string) (FileInfo, error)
// Glob returns the names of all files matching the pattern.
Glob(pattern string) ([]string, error)
// Exists returns true if the path exists.
Exists(path string) (bool, error)
// Command execution
// Exec runs a command and returns its combined output.
Exec(cmd string, args ...string) ([]byte, error)
// ExecDir runs a command in the specified directory.
ExecDir(dir, cmd string, args ...string) ([]byte, error)
// ExecEnv runs a command with additional environment variables.
ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error)
// Tmux operations
// TmuxNewSession creates a new tmux session with the given name.
TmuxNewSession(name, dir string) error
// TmuxKillSession terminates the named tmux session.
TmuxKillSession(name string) error
// TmuxSendKeys sends keys to the named tmux session.
TmuxSendKeys(session, keys string) error
// TmuxCapturePane captures the last N lines from a tmux pane.
TmuxCapturePane(session string, lines int) (string, error)
// TmuxHasSession returns true if the named tmux session exists.
TmuxHasSession(name string) (bool, error)
// TmuxListSessions returns a list of all tmux session names.
TmuxListSessions() ([]string, error)
}
// FileInfo abstracts fs.FileInfo for use over remote connections.
// This is needed because fs.FileInfo contains methods that can't be
// easily serialized over SSH.
type FileInfo interface {
// Name returns the base name of the file.
Name() string
// Size returns the length in bytes.
Size() int64
// Mode returns the file mode bits.
Mode() fs.FileMode
// ModTime returns the modification time.
ModTime() time.Time
// IsDir returns true if this is a directory.
IsDir() bool
}
// BasicFileInfo is a simple implementation of FileInfo.
type BasicFileInfo struct {
FileName string `json:"name"`
FileSize int64 `json:"size"`
FileMode fs.FileMode `json:"mode"`
FileModTime time.Time `json:"mod_time"`
FileIsDir bool `json:"is_dir"`
}
// Name implements FileInfo.
func (f BasicFileInfo) Name() string { return f.FileName }
// Size implements FileInfo.
func (f BasicFileInfo) Size() int64 { return f.FileSize }
// Mode implements FileInfo.
func (f BasicFileInfo) Mode() fs.FileMode { return f.FileMode }
// ModTime implements FileInfo.
func (f BasicFileInfo) ModTime() time.Time { return f.FileModTime }
// IsDir implements FileInfo.
func (f BasicFileInfo) IsDir() bool { return f.FileIsDir }
// FromOSFileInfo creates a BasicFileInfo from an os.FileInfo.
func FromOSFileInfo(fi fs.FileInfo) BasicFileInfo {
return BasicFileInfo{
FileName: fi.Name(),
FileSize: fi.Size(),
FileMode: fi.Mode(),
FileModTime: fi.ModTime(),
FileIsDir: fi.IsDir(),
}
}
// Error types for connection operations.
type (
// ConnectionError indicates a connection-level failure.
ConnectionError struct {
Op string // Operation that failed (e.g., "connect", "exec")
Machine string // Machine name or address
Err error // Underlying error
}
// NotFoundError indicates a file or resource was not found.
NotFoundError struct {
Path string
}
// PermissionError indicates an access permission failure.
PermissionError struct {
Path string
Op string
}
)
func (e *ConnectionError) Error() string {
return "connection " + e.Op + " on " + e.Machine + ": " + e.Err.Error()
}
func (e *ConnectionError) Unwrap() error {
return e.Err
}
func (e *NotFoundError) Error() string {
return "not found: " + e.Path
}
func (e *PermissionError) Error() string {
return "permission denied: " + e.Op + " " + e.Path
}