1
0
forked from wrenn/wrenn

Remove PTY inactivity timeout to keep terminal sessions alive indefinitely

Sessions now only end on process exit or explicit kill, not idle time.
The keepalive ping every 30s remains to prevent network-level disconnects.
This commit is contained in:
2026-04-15 18:31:48 +06:00
parent 5b4fde055c
commit 5f877afb9e
2 changed files with 0 additions and 40 deletions

View File

@ -23,7 +23,6 @@ import (
) )
const ( const (
ptyInactivityTimeout = 120 * time.Second
ptyKeepaliveInterval = 30 * time.Second ptyKeepaliveInterval = 30 * time.Second
ptyDefaultCmd = "/bin/bash" ptyDefaultCmd = "/bin/bash"
ptyDefaultCols = 80 ptyDefaultCols = 80
@ -246,10 +245,6 @@ func runPtyLoop(
) { ) {
var wg sync.WaitGroup var wg sync.WaitGroup
// Inactivity timer — reset on input/resize, fires kill after timeout.
timer := time.NewTimer(ptyInactivityTimeout)
defer timer.Stop()
// Output pump: read from Connect stream, write to WebSocket. // Output pump: read from Connect stream, write to WebSocket.
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -317,7 +312,6 @@ func runPtyLoop(
})); err != nil { })); err != nil {
slog.Debug("pty send input error", "error", err) slog.Debug("pty send input error", "error", err)
} }
resetTimer(timer, ptyInactivityTimeout)
case "resize": case "resize":
cols := msg.Cols cols := msg.Cols
@ -331,7 +325,6 @@ func runPtyLoop(
})); err != nil { })); err != nil {
slog.Debug("pty resize error", "error", err) slog.Debug("pty resize error", "error", err)
} }
resetTimer(timer, ptyInactivityTimeout)
} }
case "kill": case "kill":
@ -364,26 +357,6 @@ func runPtyLoop(
} }
}() }()
// Inactivity timeout goroutine.
wg.Add(1)
go func() {
defer wg.Done()
select {
case <-timer.C:
slog.Info("pty session timed out", "sandbox_id", sandboxID, "tag", tag)
rpcCtx, rpcCancel := context.WithTimeout(context.Background(), 5*time.Second)
if _, err := agent.PtyKill(rpcCtx, connect.NewRequest(&pb.PtyKillRequest{
SandboxId: sandboxID,
Tag: tag,
})); err != nil {
slog.Debug("pty timeout kill error", "error", err)
}
rpcCancel()
cancel()
case <-ctx.Done():
}
}()
wg.Wait() wg.Wait()
} }
@ -391,15 +364,3 @@ func runPtyLoop(
func newPtyTag() string { func newPtyTag() string {
return "pty-" + id.NewPtyTag() return "pty-" + id.NewPtyTag()
} }
// resetTimer safely resets a timer by stopping it and draining the channel
// before resetting, avoiding the race documented in time.Timer.Reset.
func resetTimer(t *time.Timer, d time.Duration) {
if !t.Stop() {
select {
case <-t.C:
default:
}
}
t.Reset(d)
}

View File

@ -1386,7 +1386,6 @@ paths:
PTY data (input and output) is base64-encoded because it contains raw PTY data (input and output) is base64-encoded because it contains raw
terminal bytes (escape sequences, control codes) that are not valid UTF-8. terminal bytes (escape sequences, control codes) that are not valid UTF-8.
Sessions have a 120-second inactivity timeout (reset on input/resize).
Sessions persist across WebSocket disconnections — the process keeps Sessions persist across WebSocket disconnections — the process keeps
running in the capsule. Use the `tag` from the "started" response to running in the capsule. Use the `tag` from the "started" response to
reconnect later. reconnect later.