feat(connection): Add LocalConnection implementation (gt-f9x.8)
Implement LocalConnection that provides local file operations, command execution, and tmux integration: - File ops: ReadFile, WriteFile, MkdirAll, Remove, RemoveAll, Stat, Glob, Exists - Command exec: Exec, ExecDir, ExecEnv - Tmux: NewSession, KillSession, SendKeys, CapturePane, HasSession, ListSessions Uses proper error wrapping with NotFoundError and PermissionError types. Integrates with existing internal/tmux package for session management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
189
internal/connection/local.go
Normal file
189
internal/connection/local.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package connection
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
)
|
||||
|
||||
// LocalConnection implements Connection for local file and command operations.
|
||||
type LocalConnection struct {
|
||||
tmux *tmux.Tmux
|
||||
}
|
||||
|
||||
// NewLocalConnection creates a new local connection.
|
||||
func NewLocalConnection() *LocalConnection {
|
||||
return &LocalConnection{
|
||||
tmux: tmux.NewTmux(),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns "local" for local connections.
|
||||
func (c *LocalConnection) Name() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
// IsLocal returns true for local connections.
|
||||
func (c *LocalConnection) IsLocal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadFile reads the named file.
|
||||
func (c *LocalConnection) ReadFile(path string) ([]byte, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, &NotFoundError{Path: path}
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return nil, &PermissionError{Path: path, Op: "read"}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// WriteFile writes data to the named file.
|
||||
func (c *LocalConnection) WriteFile(path string, data []byte, perm fs.FileMode) error {
|
||||
err := os.WriteFile(path, data, perm)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return &PermissionError{Path: path, Op: "write"}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory and all parent directories.
|
||||
func (c *LocalConnection) MkdirAll(path string, perm fs.FileMode) error {
|
||||
err := os.MkdirAll(path, perm)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return &PermissionError{Path: path, Op: "mkdir"}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the named file or empty directory.
|
||||
func (c *LocalConnection) Remove(path string) error {
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil // Already gone
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return &PermissionError{Path: path, Op: "remove"}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll removes the named file or directory and any children.
|
||||
func (c *LocalConnection) RemoveAll(path string) error {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
if os.IsPermission(err) {
|
||||
return &PermissionError{Path: path, Op: "remove"}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat returns file info for the named file.
|
||||
func (c *LocalConnection) Stat(path string) (FileInfo, error) {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, &NotFoundError{Path: path}
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return nil, &PermissionError{Path: path, Op: "stat"}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return FromOSFileInfo(fi), nil
|
||||
}
|
||||
|
||||
// Glob returns the names of all files matching the pattern.
|
||||
func (c *LocalConnection) Glob(pattern string) ([]string, error) {
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
// Exists returns true if the path exists.
|
||||
func (c *LocalConnection) Exists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Exec runs a command and returns its combined output.
|
||||
func (c *LocalConnection) Exec(cmd string, args ...string) ([]byte, error) {
|
||||
return exec.Command(cmd, args...).CombinedOutput()
|
||||
}
|
||||
|
||||
// ExecDir runs a command in the specified directory.
|
||||
func (c *LocalConnection) ExecDir(dir, cmd string, args ...string) ([]byte, error) {
|
||||
command := exec.Command(cmd, args...)
|
||||
command.Dir = dir
|
||||
return command.CombinedOutput()
|
||||
}
|
||||
|
||||
// ExecEnv runs a command with additional environment variables.
|
||||
func (c *LocalConnection) ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error) {
|
||||
command := exec.Command(cmd, args...)
|
||||
command.Env = os.Environ()
|
||||
for k, v := range env {
|
||||
command.Env = append(command.Env, k+"="+v)
|
||||
}
|
||||
return command.CombinedOutput()
|
||||
}
|
||||
|
||||
// TmuxNewSession creates a new tmux session.
|
||||
func (c *LocalConnection) TmuxNewSession(name, dir string) error {
|
||||
return c.tmux.NewSession(name, dir)
|
||||
}
|
||||
|
||||
// TmuxKillSession terminates a tmux session.
|
||||
func (c *LocalConnection) TmuxKillSession(name string) error {
|
||||
return c.tmux.KillSession(name)
|
||||
}
|
||||
|
||||
// TmuxSendKeys sends keys to a tmux session.
|
||||
func (c *LocalConnection) TmuxSendKeys(session, keys string) error {
|
||||
return c.tmux.SendKeys(session, keys)
|
||||
}
|
||||
|
||||
// TmuxCapturePane captures the last N lines from a tmux pane.
|
||||
func (c *LocalConnection) TmuxCapturePane(session string, lines int) (string, error) {
|
||||
return c.tmux.CapturePane(session, lines)
|
||||
}
|
||||
|
||||
// TmuxHasSession returns true if the session exists.
|
||||
func (c *LocalConnection) TmuxHasSession(name string) (bool, error) {
|
||||
return c.tmux.HasSession(name)
|
||||
}
|
||||
|
||||
// TmuxListSessions returns all tmux session names.
|
||||
func (c *LocalConnection) TmuxListSessions() ([]string, error) {
|
||||
return c.tmux.ListSessions()
|
||||
}
|
||||
|
||||
// Verify LocalConnection implements Connection.
|
||||
var _ Connection = (*LocalConnection)(nil)
|
||||
Reference in New Issue
Block a user