fix: ssh connect flow never fired + 120s host-key prompt budget

- Workspace: saved-credential connections now call connect() directly;
  ConnectFlow only rendered a connecting spinner without ever firing
  openSession, leaving attempts hung forever (root cause of the stuck
  '连接中' modal)
- Host-key prompt timeout raised 30s -> 120s (a security decision needs
  time; 30s aborted while users compare fingerprints)
- main: unhandledRejection/uncaughtException loggers; ssh diagnostics on
  error paths
This commit is contained in:
954801926@qq.com committed 2026-09-06 13:08:47 +08:00
1 parent bf3b2a7c30
commit 0be65370f6
3 files changed
+31 -6

No files matched your search

+7
View File
@@ -41,6 +41,13 @@ function createWindow(): void {
}
}
process.on('unhandledRejection', (reason) => {
console.error('[main] unhandledRejection:', reason)
})
process.on('uncaughtException', (err) => {
console.error('[main] uncaughtException:', err)
})
app.whenReady().then(() => {
registerIpc()
// OS-level effects (login item, sleep blocker) must apply even if the
+9 -3
View File
@@ -53,7 +53,9 @@ export interface SshServiceDeps {
timeoutMs?: { prompt: number; connect: number }
}
const DEFAULT_TIMEOUTS = { prompt: 30_000, connect: 15_000 }
// Host-key confirmation is a security decision — users may take time to
// compare fingerprints. 30s timed out on people who merely screenshot it.
const DEFAULT_TIMEOUTS = { prompt: 120_000, connect: 15_000 }
/**
* Open an ssh session (connect + auth + open shell) for `conn`.
@@ -107,7 +109,8 @@ export async function connectSsh(
try {
fingerprint = fingerprintOf(hostKey)
status = deps.knownHosts.check(conn.host, conn.port, hostKey).status
} catch {
} catch (err) {
console.error(`[ssh] knownHosts.check threw: ${(err as Error).message}`)
fingerprint = fingerprintOf(hostKey)
status = 'new'
}
@@ -287,7 +290,10 @@ function promptUser(
*/
export function resolveHostKey(promptId: string, action: 'accept' | 'reject'): void {
const pending = pendingPrompts.get(promptId)
if (!pending) return
if (!pending) {
console.error(`[ssh] resolveHostKey: no pending prompt ${promptId}`)
return
}
clearTimeout(pending.timer)
pendingPrompts.delete(promptId)
pending.resolve(action === 'accept')
+15 -3
View File
@@ -248,9 +248,21 @@ export default function Workspace({ onOpenSettings }: WorkspaceProps): React.JSX
[message]
)
const handleConnectRequest = useCallback((conn: SshConnection): void => {
setRequestedConn(conn)
}, [])
const handleConnectRequest = useCallback(
(conn: SshConnection): void => {
// Ask-at-connect connections go through the secret prompt first; saved
// credentials must connect IMMEDIATELY — routing them through ConnectFlow
// would only ever *render* a "connecting" spinner without firing openSession.
const needsPassword = conn.auth === 'password' && conn.askPasswordAtConnect
const needsPassphrase = conn.auth === 'privateKey' && conn.askPassphraseAtConnect
if (needsPassword || needsPassphrase) {
setRequestedConn(conn)
return
}
void connect(conn, {})
},
[connect]
)
const handleConnectCancel = useCallback((): void => {
setRequestedConn(null)