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:
@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user