From 7dcf505360333fe00aa1ee669574912b373818fa Mon Sep 17 00:00:00 2001 From: Augustin Date: Wed, 22 Apr 2026 20:02:55 +0200 Subject: [PATCH] fix(terminal): improve shell resolution with better error handling and ws proxy support The `len(shell) <= 1` guard was too aggressive and provided no diagnostic info. Now trims whitespace, resolves path in all cases, falls back to /bin/sh, and logs detailed context for debugging. Also enable WebSocket proxying in Vite dev. --- internal/api/terminal.go | 31 ++++++++++++++++++++----------- web/vite.config.js | 1 + 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/internal/api/terminal.go b/internal/api/terminal.go index 90a5a12..31d2e87 100644 --- a/internal/api/terminal.go +++ b/internal/api/terminal.go @@ -56,13 +56,17 @@ func (s *Server) handleTerminalWS(w http.ResponseWriter, r *http.Request) { var initMsg wsMessage _, raw, err := conn.ReadMessage() if err != nil { + log.Printf("terminal: read init message failed: %v", err) conn.WriteJSON(wsMessage{Type: "error", Data: "failed to read init message"}) return } + log.Printf("terminal: init message received: %s", string(raw)) if err := json.Unmarshal(raw, &initMsg); err != nil { + log.Printf("terminal: unmarshal init message failed: %v", err) conn.WriteJSON(wsMessage{Type: "error", Data: "invalid init message"}) return } + log.Printf("terminal: init type=%q data=%q", initMsg.Type, initMsg.Data) var cmd *exec.Cmd @@ -96,23 +100,26 @@ func (s *Server) handleTerminalWS(w http.ResponseWriter, r *http.Request) { cmd = exec.Command("ssh", sshArgs...) } else { - shell := initMsg.Data + shell := strings.TrimSpace(initMsg.Data) + log.Printf("terminal: requested shell=%q, trimmed=%q", initMsg.Data, shell) if shell == "" { shell = detectShell() - } else { - if path, err := exec.LookPath(shell); err == nil { - shell = path - } + log.Printf("terminal: auto-detected shell=%q", shell) } - // Ignore invalid shell paths (e.g., single characters from race condition) - if len(shell) <= 1 { - conn.WriteJSON(wsMessage{Type: "error", Data: "invalid shell config"}) - return + if shell == "" { + log.Printf("terminal: no shell detected, falling back to /bin/sh") + shell = "/bin/sh" + } + + if path, err := exec.LookPath(shell); err == nil { + shell = path + log.Printf("terminal: resolved shell path=%q", shell) } if _, err := os.Stat(shell); err != nil { - conn.WriteJSON(wsMessage{Type: "error", Data: fmt.Sprintf("shell not found: %s", shell)}) + log.Printf("terminal: shell stat failed: %v for %q", err, shell) + conn.WriteJSON(wsMessage{Type: "error", Data: fmt.Sprintf("shell not found: %s (resolved from: %q)", shell, initMsg.Data)}) return } @@ -131,12 +138,14 @@ func (s *Server) handleTerminalWS(w http.ResponseWriter, r *http.Request) { cmd.Env = append(os.Environ(), "TERM=xterm-256color") + log.Printf("terminal: starting pty with cmd=%q args=%v", cmd.Path, cmd.Args) ptmx, err := pty.Start(cmd) if err != nil { - log.Printf("pty start: %v", err) + log.Printf("terminal: pty start failed: %v", err) conn.WriteJSON(wsMessage{Type: "error", Data: err.Error()}) return } + log.Printf("terminal: pty started successfully") defer func() { ptmx.Close() if cmd.Process != nil { diff --git a/web/vite.config.js b/web/vite.config.js index e3523f9..e471e2b 100644 --- a/web/vite.config.js +++ b/web/vite.config.js @@ -13,6 +13,7 @@ export default defineConfig({ '/api': { target: 'http://127.0.0.1:8095', changeOrigin: true, + ws: true, }, }, },