| @@ -26,14 +26,7 @@ import { · 25 unmodified lines |
| 26 | AuthSessionState, | 26 | AuthSessionState, |
| 27 | AuthWebSocketTicketResult, | 27 | AuthWebSocketTicketResult, |
| 28 | } from "@t3tools/contracts"; | 28 | } from "@t3tools/contracts"; |
| 29 | import { | 29 | import { SshHttpBridgeError, type SshPasswordPromptError } from "@t3tools/ssh/errors"; |
| 30 | SshCommandExecutionError, | | |
| 31 | SshCommandSpawnError, | | |
| 32 | SshHttpBridgeError, | | |
| 33 | type SshPasswordPromptError, | | |
| 34 | SshTunnelMonitorError, | | |
| 35 | SshTunnelSpawnError, | | |
| 36 | } from "@t3tools/ssh/errors"; | | |
| 37 | import { resolveLoopbackSshHttpBaseUrl } from "@t3tools/ssh/tunnel"; | 30 | import { resolveLoopbackSshHttpBaseUrl } from "@t3tools/ssh/tunnel"; |
| 38 | import * as Effect from "effect/Effect"; | 31 | import * as Effect from "effect/Effect"; |
| 39 | import * as Schema from "effect/Schema"; | 32 | import * as Schema from "effect/Schema"; |
| @@ -83,26 +76,6 @@ const isEnvironmentOperationForbiddenError = Schema.is(EnvironmentOperationForbi · 43 unmodified lines |
| 83 | const isEnvironmentRequestInvalidError = Schema.is(EnvironmentRequestInvalidError); | 76 | const isEnvironmentRequestInvalidError = Schema.is(EnvironmentRequestInvalidError); |
| 84 | const isEnvironmentScopeRequiredError = Schema.is(EnvironmentScopeRequiredError); | 77 | const isEnvironmentScopeRequiredError = Schema.is(EnvironmentScopeRequiredError); |
| 85 | const isSshHttpBridgeError = Schema.is(SshHttpBridgeError); | 78 | const isSshHttpBridgeError = Schema.is(SshHttpBridgeError); |
| 86 | const isSshCausePresentationError = Schema.is( | | |
| 87 | Schema.Union([ | | |
| 88 | SshCommandSpawnError, | | |
| 89 | SshCommandExecutionError, | | |
| 90 | SshTunnelSpawnError, | | |
| 91 | SshTunnelMonitorError, | | |
| 92 | ]), | | |
| 93 | ); | | |
| 94 | | | |
| 95 | // Electron only forwards the message from a rejected ipcRenderer.invoke promise. Keep the typed, | | |
| 96 | // structural SSH error intact as the cause for main-process diagnostics while preserving the | | |
| 97 | // actionable process message that the renderer historically presented to the user. | | |
| 98 | export function toDesktopSshIpcPresentationError( | | |
| 99 | error: DesktopSshEnvironment.DesktopSshEnvironmentOperationError, | | |
| 100 | ): DesktopSshEnvironment.DesktopSshEnvironmentOperationError | Error { | | |
| 101 | if (isSshCausePresentationError(error) && error.cause instanceof Error) { | | |
| 102 | return new Error(error.cause.message, { cause: error }); | | |
| 103 | } | | |
| 104 | return error; | | |
| 105 | } | | |
| 106 | | 79 | |
| 107 | function readSshHttpStatus(cause: DesktopSshEnvironmentRequestCause): number | null { | 80 | function readSshHttpStatus(cause: DesktopSshEnvironmentRequestCause): number | null { |
| 108 | if (isRemoteEnvironmentAuthUndeclaredStatusError(cause)) { | 81 | if (isRemoteEnvironmentAuthUndeclaredStatusError(cause)) { |
| @@ -169,6 +142,9 @@ export const discoverSshHosts = DesktopIpc.makeIpcMethod({ · 60 unmodified lines |
| 169 | }), | 142 | }), |
| 170 | }); | 143 | }); |
| 171 | | 144 | |
| | 145 | // Electron forwards only the message from a rejected ipcRenderer.invoke promise to the renderer. |
| | 146 | // Preserve typed SSH errors at this boundary so that message stays structural while main-process |
| | 147 | // diagnostics retain the exact cause and error fields. |
| 172 | export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ | 148 | export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ |
| 173 | channel: IpcChannels.ENSURE_SSH_ENVIRONMENT_CHANNEL, | 149 | channel: IpcChannels.ENSURE_SSH_ENVIRONMENT_CHANNEL, |
| 174 | payload: DesktopSshEnvironmentEnsureInputSchema, | 150 | payload: DesktopSshEnvironmentEnsureInputSchema, |
| @@ -185,7 +161,6 @@ export const ensureSshEnvironment = DesktopIpc.makeIpcMethod({ · 10 unmodified lines |
| 185 | SshPasswordPromptWindowClosedError: handleDesktopSshPasswordPromptCancellation, | 161 | SshPasswordPromptWindowClosedError: handleDesktopSshPasswordPromptCancellation, |
| 186 | SshPasswordPromptServiceStoppedError: handleDesktopSshPasswordPromptCancellation, | 162 | SshPasswordPromptServiceStoppedError: handleDesktopSshPasswordPromptCancellation, |
| 187 | }), | 163 | }), |
| 188 | Effect.mapError(toDesktopSshIpcPresentationError), | | |
| 189 | ); | 164 | ); |
| 190 | }), | 165 | }), |
| 191 | }); | 166 | }); |
| @@ -196,9 +171,7 @@ export const disconnectSshEnvironment = DesktopIpc.makeIpcMethod({ · 4 unmodified lines |
| 196 | result: Schema.Void, | 171 | result: Schema.Void, |
| 197 | handler: Effect.fn("desktop.ipc.sshEnvironment.disconnectEnvironment")(function* (target) { | 172 | handler: Effect.fn("desktop.ipc.sshEnvironment.disconnectEnvironment")(function* (target) { |
| 198 | const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment; | 173 | const sshEnvironment = yield* DesktopSshEnvironment.DesktopSshEnvironment; |
| 199 | yield* sshEnvironment | 174 | yield* sshEnvironment.disconnectEnvironment(target); |
| 200 | .disconnectEnvironment(target) | | |
| 201 | .pipe(Effect.mapError(toDesktopSshIpcPresentationError)); | | |
| 202 | }), | 175 | }), |
| 203 | }); | 176 | }); |
| 204 | | 177 | |