Compare commits

...

4 Commits

Author SHA1 Message Date
陈大猫
cf9f84767c Merge pull request #583 from binaricat/feat/show-transport-error-in-disconnect-dialog
Some checks failed
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
feat: show transport error in disconnect dialog
2026-03-31 10:41:25 +08:00
bincxz
3a862cbd0c feat: show transport error in disconnect dialog
When a session disconnects due to a transport error (e.g. "Keepalive timeout",
"ECONNRESET"), the error message is now surfaced in the disconnect dialog
instead of showing a generic "Disconnected" label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:37:42 +08:00
陈大猫
6af2a99680 Merge pull request #582 from binaricat/fix/ssh-keepalive-disabled-not-honored
fix: honor keepaliveInterval=0 as disabled instead of falling back to 10s
2026-03-31 10:32:06 +08:00
bincxz
b3d37d134a fix: honor keepaliveInterval=0 as disabled instead of falling back to 10s
When keepaliveInterval was set to 0 (the default, documented as "disabled"),
the code treated 0 as falsy and fell back to 10000ms. This caused ssh2 to
send keepalive@openssh.com global requests every 10s. Devices with non-OpenSSH
SSH implementations (e.g. NOKIA/ALCATEL) that don't reply to these requests
would have their connections terminated after ~40s (4 × 10s keepalive timeout).

Closes #581

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:27:08 +08:00
2 changed files with 10 additions and 7 deletions

View File

@@ -172,7 +172,7 @@ const attachSessionToTerminal = (
term: XTerm,
id: string,
opts?: {
onExitMessage?: (evt: { exitCode?: number; signal?: number }) => string;
onExitMessage?: (evt: { exitCode?: number; signal?: number; error?: string; reason?: string }) => string;
onConnected?: () => void;
// For serial: convert lone LF to CRLF to avoid "staircase effect"
convertLfToCrlf?: boolean;
@@ -209,6 +209,9 @@ const attachSessionToTerminal = (
ctx.disposeExitRef.current = ctx.terminalBackend.onSessionExit(id, (evt) => {
ctx.updateStatus("disconnected");
if (evt.error) {
ctx.setError(evt.error);
}
term.writeln(opts?.onExitMessage?.(evt) ?? "\r\n[session closed]");
if (ctx.onTerminalDataCapture && ctx.serializeAddonRef.current) {

View File

@@ -411,9 +411,9 @@ async function connectThroughChain(event, options, jumpHosts, targetHost, target
username: jump.username || 'root',
readyTimeout: 120000, // 2 minutes to allow for keyboard-interactive (2FA/MFA)
// Use user-configured keepalive interval from options (in seconds -> convert to ms)
// If 0 or not provided, use 10000ms as default
keepaliveInterval: options.keepaliveInterval && options.keepaliveInterval > 0 ? options.keepaliveInterval * 1000 : 10000,
keepaliveCountMax: 3,
// 0 = disabled (no keepalive packets sent)
keepaliveInterval: options.keepaliveInterval > 0 ? options.keepaliveInterval * 1000 : 0,
keepaliveCountMax: options.keepaliveInterval > 0 ? 3 : 0,
// Enable keyboard-interactive authentication (required for 2FA/MFA)
tryKeyboard: true,
algorithms: buildAlgorithms(options.legacyAlgorithms),
@@ -681,9 +681,9 @@ async function startSSHSession(event, options) {
// `readyTimeout` covers the entire connection + authentication flow in ssh2.
readyTimeout: 20000, // Fast failure for non-interactive auth
// Use user-configured keepalive interval (in seconds -> convert to ms)
// If 0 or not provided, use 10000ms as default
keepaliveInterval: options.keepaliveInterval && options.keepaliveInterval > 0 ? options.keepaliveInterval * 1000 : 10000,
keepaliveCountMax: 3,
// 0 = disabled (no keepalive packets sent)
keepaliveInterval: options.keepaliveInterval > 0 ? options.keepaliveInterval * 1000 : 0,
keepaliveCountMax: options.keepaliveInterval > 0 ? 3 : 0,
// Enable keyboard-interactive authentication (required for 2FA/MFA)
tryKeyboard: true,
algorithms: buildAlgorithms(options.legacyAlgorithms),