package service import ( "log/slog" "io" "codespace/internal/shell" "codespace/internal/workspace" ) // ShellService manages interactive shells for workspaces. type ShellService struct { workspaces workspace.Manager shells shell.Manager logger *slog.Logger } // NewShellService creates a ShellService. // If lg is nil, slog.Default() is used. func NewShellService(workspaces workspace.Manager, shells shell.Manager, lg *slog.Logger) *ShellService { if lg == nil { lg = slog.Default() } return &ShellService{workspaces: workspaces, shells: shells, logger: lg} } // Start starts an interactive shell for the given workspace. func (s *ShellService) Start(workspaceID string) error { ws, err := s.workspaces.Get(workspaceID) if err != nil { return err } if err := s.shells.Start(workspaceID, ws.Root); err != nil { s.logger.Error("shell start failed", "workspace_id", workspaceID, "error", err) return err } status := s.shells.Status(workspaceID) s.logger.Info("shell started", "workspace_id", workspaceID, "pid", status.PID) return nil } // Stop stops the interactive shell for the given workspace. func (s *ShellService) Stop(workspaceID string) error { if err := s.shells.Stop(workspaceID); err != nil { s.logger.Error("shell stop failed", "workspace_id", workspaceID, "error", err) return err } s.logger.Info("shell stopped", "workspace_id", workspaceID) return nil } // Restart restarts the interactive shell for the given workspace. func (s *ShellService) Restart(workspaceID string) error { ws, err := s.workspaces.Get(workspaceID) if err != nil { return err } if err := s.shells.Restart(workspaceID, ws.Root); err != nil { s.logger.Error("shell restart failed", "workspace_id", workspaceID, "error", err) return err } status := s.shells.Status(workspaceID) s.logger.Info("shell restarted", "workspace_id", workspaceID, "pid", status.PID) return nil } // Status returns the shell status for the given workspace. func (s *ShellService) Status(workspaceID string) (shell.Status, error) { if _, err := s.workspaces.Get(workspaceID); err != nil { return shell.Status{}, err } return s.shells.Status(workspaceID), nil } // Subscribe subscribes to output events for the workspace shell. func (s *ShellService) Subscribe(workspaceID string) (shell.Subscription, error) { if _, err := s.workspaces.Get(workspaceID); err != nil { return nil, err } return s.shells.Subscribe(workspaceID) } // Input returns the stdin writer for the workspace shell. func (s *ShellService) Input(workspaceID string) (io.WriteCloser, error) { if _, err := s.workspaces.Get(workspaceID); err != nil { return nil, err } return s.shells.Stdin(workspaceID) } // ExitStatus returns the exit status for the workspace shell. func (s *ShellService) ExitStatus(workspaceID string) (shell.ExitInfo, error) { if _, err := s.workspaces.Get(workspaceID); err != nil { return shell.ExitInfo{}, err } return s.shells.ExitStatus(workspaceID) }