Files
kandev/apps/backend/internal/agentctl/server/shell/manager.go
T

102 lines
2.4 KiB
Go

package shell
import (
"fmt"
"sync"
"github.com/kandev/kandev/internal/common/logger"
"go.uber.org/zap"
)
// Manager manages multiple named shell sessions, one per terminal ID.
type Manager struct {
workDir string
logger *logger.Logger
terminals map[string]*Session
mu sync.RWMutex
}
// NewManager creates a new shell session manager.
func NewManager(workDir string, log *logger.Logger) *Manager {
return &Manager{
workDir: workDir,
logger: log.WithFields(zap.String("component", "shell-manager")),
terminals: make(map[string]*Session),
}
}
// Start creates and starts a new shell session for the given terminal ID.
// If a session already exists for this terminal, it is returned as-is.
func (m *Manager) Start(terminalID string, cfg Config) (*Session, error) {
m.mu.Lock()
defer m.mu.Unlock()
if s, ok := m.terminals[terminalID]; ok {
return s, nil
}
if cfg.WorkDir == "" {
cfg.WorkDir = m.workDir
}
s, err := NewSession(cfg, m.logger)
if err != nil {
return nil, fmt.Errorf("failed to create shell for terminal %s: %w", terminalID, err)
}
m.terminals[terminalID] = s
m.logger.Info("terminal shell started", zap.String("terminal_id", terminalID))
return s, nil
}
// Get returns the shell session for the given terminal ID.
func (m *Manager) Get(terminalID string) (*Session, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
s, ok := m.terminals[terminalID]
return s, ok
}
// Stop stops and removes the shell session for the given terminal ID.
func (m *Manager) Stop(terminalID string) error {
m.mu.Lock()
s, ok := m.terminals[terminalID]
if !ok {
m.mu.Unlock()
return nil
}
delete(m.terminals, terminalID)
m.mu.Unlock()
m.logger.Info("stopping terminal shell", zap.String("terminal_id", terminalID))
return s.Stop()
}
// Buffer returns the buffered output for the given terminal ID.
func (m *Manager) Buffer(terminalID string) ([]byte, error) {
m.mu.RLock()
s, ok := m.terminals[terminalID]
m.mu.RUnlock()
if !ok {
return nil, fmt.Errorf("terminal %s not found", terminalID)
}
return s.GetBufferedOutput(), nil
}
// StopAll stops all terminal shell sessions.
func (m *Manager) StopAll() {
m.mu.Lock()
terminals := make(map[string]*Session, len(m.terminals))
for id, s := range m.terminals {
terminals[id] = s
}
m.terminals = make(map[string]*Session)
m.mu.Unlock()
for id, s := range terminals {
m.logger.Info("stopping terminal shell (shutdown)", zap.String("terminal_id", id))
_ = s.Stop()
}
}