From 62f624b66b17805c1dd1582b0573e9d74ed3cd71 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 21 Dec 2025 00:22:46 +0100 Subject: [PATCH] fix(mac): re-ensure remote gateway tunnel --- .../Sources/Clawdis/GatewayConnection.swift | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/apps/macos/Sources/Clawdis/GatewayConnection.swift b/apps/macos/Sources/Clawdis/GatewayConnection.swift index 0a4c176d3..ddba672a5 100644 --- a/apps/macos/Sources/Clawdis/GatewayConnection.swift +++ b/apps/macos/Sources/Clawdis/GatewayConnection.swift @@ -109,24 +109,61 @@ actor GatewayConnection { do { return try await client.request(method: method, params: params, timeoutMs: timeoutMs) } catch { + if error is GatewayResponseError || error is GatewayDecodingError { + throw error + } + // Auto-recover in local mode by spawning/attaching a gateway and retrying a few times. // Canvas interactions should "just work" even if the local gateway isn't running yet. - let isLocal = await MainActor.run { AppStateStore.shared.connectionMode == .local } - guard isLocal else { throw error } + let mode = await MainActor.run { AppStateStore.shared.connectionMode } + switch mode { + case .local: + await MainActor.run { GatewayProcessManager.shared.setActive(true) } - await MainActor.run { GatewayProcessManager.shared.setActive(true) } + var lastError: Error = error + for delayMs in [150, 400, 900] { + try await Task.sleep(nanoseconds: UInt64(delayMs) * 1_000_000) + do { + return try await client.request(method: method, params: params, timeoutMs: timeoutMs) + } catch { + lastError = error + } + } - var lastError: Error = error - for delayMs in [150, 400, 900] { - try await Task.sleep(nanoseconds: UInt64(delayMs) * 1_000_000) + throw lastError + case .remote: + let nsError = error as NSError + guard nsError.domain == URLError.errorDomain else { throw error } + + var lastError: Error = error + await RemoteTunnelManager.shared.stopAll() do { - return try await client.request(method: method, params: params, timeoutMs: timeoutMs) + _ = try await GatewayEndpointStore.shared.ensureRemoteControlTunnel() } catch { lastError = error } - } - throw lastError + for delayMs in [150, 400, 900] { + try await Task.sleep(nanoseconds: UInt64(delayMs) * 1_000_000) + do { + let cfg = try await self.configProvider() + await self.configure(url: cfg.url, token: cfg.token) + guard let client = self.client else { + throw NSError( + domain: "Gateway", + code: 0, + userInfo: [NSLocalizedDescriptionKey: "gateway not configured"]) + } + return try await client.request(method: method, params: params, timeoutMs: timeoutMs) + } catch { + lastError = error + } + } + + throw lastError + case .unconfigured: + throw error + } } }