Skip to content

Commit a7d1f74

Browse files
committed
fix: prevent post-send presentation failures from discarding resolved SSH password
After send-prompt-request, the renderer can resolve the deferred password at any yield boundary. Wrap the post-send presentation operations (window destroy checks, restore, focus) with Effect.ignore so their failures do not shadow an already-resolved password. The window-closed listener and timeout still handle the case where the window is truly gone.
1 parent 5ee7cfe commit a7d1f74

1 file changed

Lines changed: 36 additions & 28 deletions

File tree

apps/desktop/src/ssh/DesktopSshPasswordPrompts.ts

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -413,34 +413,42 @@ export const make = Effect.fn("desktop.sshPasswordPrompts.make")(function* (
413413
yield* runPresentationOperation("send-prompt-request", () =>
414414
window.value.webContents.send(SSH_PASSWORD_PROMPT_CHANNEL, promptRequest),
415415
);
416-
const unavailableAfterSend = yield* runPresentationOperation("check-window-after-send", () =>
417-
window.value.isDestroyed(),
418-
);
419-
if (unavailableAfterSend) {
420-
return yield* new DesktopSshPromptWindowUnavailableError({
421-
destination: input.destination,
422-
requestId,
423-
stage: "after-send",
424-
});
425-
}
426-
const minimized = yield* runPresentationOperation("check-window-minimized", () =>
427-
window.value.isMinimized(),
428-
);
429-
if (minimized) {
430-
yield* runPresentationOperation("restore-window", () => window.value.restore());
431-
}
432-
const unavailableAfterRestore = yield* runPresentationOperation(
433-
"check-window-after-restore",
434-
() => window.value.isDestroyed(),
435-
);
436-
if (unavailableAfterRestore) {
437-
return yield* new DesktopSshPromptWindowUnavailableError({
438-
destination: input.destination,
439-
requestId,
440-
stage: "after-restore",
441-
});
442-
}
443-
yield* runPresentationOperation("focus-window", () => window.value.focus());
416+
// Once the prompt is sent the renderer can resolve the password at any
417+
// scheduling boundary. The remaining steps only improve visibility
418+
// (focus / restore) so their failures must not shadow an already-resolved
419+
// password. Wrap them as best-effort; the window-closed listener and
420+
// timeout still protect against a truly-gone window.
421+
yield* Effect.gen(function* () {
422+
const unavailableAfterSend = yield* runPresentationOperation(
423+
"check-window-after-send",
424+
() => window.value.isDestroyed(),
425+
);
426+
if (unavailableAfterSend) {
427+
return yield* new DesktopSshPromptWindowUnavailableError({
428+
destination: input.destination,
429+
requestId,
430+
stage: "after-send",
431+
});
432+
}
433+
const minimized = yield* runPresentationOperation("check-window-minimized", () =>
434+
window.value.isMinimized(),
435+
);
436+
if (minimized) {
437+
yield* runPresentationOperation("restore-window", () => window.value.restore());
438+
}
439+
const unavailableAfterRestore = yield* runPresentationOperation(
440+
"check-window-after-restore",
441+
() => window.value.isDestroyed(),
442+
);
443+
if (unavailableAfterRestore) {
444+
return yield* new DesktopSshPromptWindowUnavailableError({
445+
destination: input.destination,
446+
requestId,
447+
stage: "after-restore",
448+
});
449+
}
450+
yield* runPresentationOperation("focus-window", () => window.value.focus());
451+
}).pipe(Effect.ignore);
444452
return yield* waitForPassword;
445453
}).pipe(Effect.ensuring(cleanup));
446454
});

0 commit comments

Comments
 (0)