fix: auto-close tab on graceful SSH disconnect (exit/Ctrl+D) (#723)

Distinguish between graceful shell exit and unexpected disconnection
using the stream close event's exit code. When the shell exits normally
(code != null), send "session_ended" instead of "disconnected". The
frontend auto-closes the tab on session_ended, and shows the reconnect
overlay only on unexpected disconnections.

Closes Termix-SSH/Support#643
This commit is contained in:
ZacharyZcR
2026-04-30 10:26:27 +08:00
committed by GitHub
parent de5818b92e
commit 77c7240e4e
4 changed files with 32 additions and 7 deletions
+16 -7
View File
@@ -1503,15 +1503,24 @@ wss.on("connection", async (ws: WebSocket, req) => {
}
});
stream.on("close", () => {
stream.on("close", (code: number | null) => {
const session = sessionManager.getSession(boundSessionId);
if (session?.attachedWs?.readyState === WebSocket.OPEN) {
session.attachedWs.send(
JSON.stringify({
type: "disconnected",
message: "Connection lost",
}),
);
if (code != null) {
session.attachedWs.send(
JSON.stringify({
type: "session_ended",
code,
}),
);
} else {
session.attachedWs.send(
JSON.stringify({
type: "disconnected",
message: "Connection lost",
}),
);
}
}
if (boundSessionId) {
sessionManager.destroySession(boundSessionId);
+1
View File
@@ -1676,6 +1676,7 @@
"automaticFallback": "Automatically trying {{method}} authentication...",
"totpTimeout": "TOTP verification timeout. Please reconnect.",
"passwordTimeout": "Password verification timeout. Please reconnect.",
"sessionEnded": "Session ended.",
"connectionRejected": "Connection rejected by server. Please check your authentication and network configuration.",
"hostKeyRejected": "SSH host key verification rejected. Connection cancelled.",
"sessionTakenOver": "Session was opened in another tab. Reconnecting...",
@@ -1187,6 +1187,14 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
);
}
}, 100);
} else if (msg.type === "session_ended") {
wasDisconnectedBySSH.current = true;
setIsConnected(false);
setIsConnecting(false);
shouldNotReconnectRef.current = true;
if (onClose) {
onClose();
}
} else if (msg.type === "disconnected") {
wasDisconnectedBySSH.current = true;
shouldNotReconnectRef.current = true;
+7
View File
@@ -729,6 +729,13 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
);
}
}, 100);
} else if (msg.type === "session_ended") {
wasDisconnectedBySSH.current = true;
shouldNotReconnectRef.current = true;
isConnectingRef.current = false;
setIsConnected(false);
setIsConnecting(false);
updateConnectionError(t("terminal.sessionEnded"));
} else if (msg.type === "disconnected") {
wasDisconnectedBySSH.current = true;
isConnectingRef.current = false;