@@ -11,6 +11,7 @@ import (
1111 "os"
1212 "os/signal"
1313 "strings"
14+ "sync"
1415 "syscall"
1516
1617 "github.com/gorilla/websocket"
@@ -35,6 +36,8 @@ type execRequest struct {
3536 Env map [string ]string `json:"env,omitempty"`
3637 Cwd string `json:"cwd,omitempty"`
3738 Timeout int32 `json:"timeout,omitempty"`
39+ Rows uint32 `json:"rows,omitempty"`
40+ Cols uint32 `json:"cols,omitempty"`
3841}
3942
4043var execCmd = cli.Command {
@@ -127,6 +130,17 @@ func handleExec(ctx context.Context, cmd *cli.Command) error {
127130 execReq .Timeout = int32 (timeout )
128131 }
129132
133+ // Get terminal size for TTY mode (only if stdout is actually a terminal)
134+ if tty && term .IsTerminal (int (os .Stdout .Fd ())) {
135+ cols , rows , _ := term .GetSize (int (os .Stdout .Fd ()))
136+ if rows > 0 {
137+ execReq .Rows = uint32 (rows )
138+ }
139+ if cols > 0 {
140+ execReq .Cols = uint32 (cols )
141+ }
142+ }
143+
130144 reqBody , err := json .Marshal (execReq )
131145 if err != nil {
132146 return fmt .Errorf ("failed to marshal request: %w" , err )
@@ -213,9 +227,30 @@ func runExecInteractive(ws *websocket.Conn) (int, error) {
213227 signal .Notify (sigCh , os .Interrupt , syscall .SIGTERM )
214228 defer signal .Stop (sigCh )
215229
230+ // Handle SIGWINCH for terminal resize
231+ sigwinch := make (chan os.Signal , 1 )
232+ signal .Notify (sigwinch , syscall .SIGWINCH )
233+ defer signal .Stop (sigwinch )
234+
235+ // Mutex to protect WebSocket writes from concurrent access
236+ var wsMu sync.Mutex
237+
216238 errCh := make (chan error , 2 )
217239 exitCodeCh := make (chan int , 1 )
218240
241+ // Handle terminal resize events
242+ go func () {
243+ for range sigwinch {
244+ cols , rows , _ := term .GetSize (int (os .Stdout .Fd ()))
245+ if rows > 0 && cols > 0 {
246+ msg := fmt .Sprintf (`{"resize":{"rows":%d,"cols":%d}}` , rows , cols )
247+ wsMu .Lock ()
248+ ws .WriteMessage (websocket .TextMessage , []byte (msg ))
249+ wsMu .Unlock ()
250+ }
251+ }
252+ }()
253+
219254 // Forward stdin to WebSocket
220255 go func () {
221256 buf := make ([]byte , 32 * 1024 )
@@ -228,7 +263,10 @@ func runExecInteractive(ws *websocket.Conn) (int, error) {
228263 return
229264 }
230265 if n > 0 {
231- if err := ws .WriteMessage (websocket .BinaryMessage , buf [:n ]); err != nil {
266+ wsMu .Lock ()
267+ err := ws .WriteMessage (websocket .BinaryMessage , buf [:n ])
268+ wsMu .Unlock ()
269+ if err != nil {
232270 errCh <- fmt .Errorf ("websocket write error: %w" , err )
233271 return
234272 }
0 commit comments