refactor: node tools and canvas host url
This commit is contained in:
22
apps/macos/Sources/Clawdis/AsyncTimeout.swift
Normal file
22
apps/macos/Sources/Clawdis/AsyncTimeout.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import Foundation
|
||||
|
||||
enum AsyncTimeout {
|
||||
static func withTimeout<T: Sendable>(
|
||||
seconds: Double,
|
||||
onTimeout: @escaping @Sendable () -> Error,
|
||||
operation: @escaping @Sendable () async throws -> T) async throws -> T
|
||||
{
|
||||
let clamped = max(0, seconds)
|
||||
return try await withThrowingTaskGroup(of: T.self) { group in
|
||||
group.addTask { try await operation() }
|
||||
group.addTask {
|
||||
try await Task.sleep(nanoseconds: UInt64(clamped * 1_000_000_000))
|
||||
throw onTimeout()
|
||||
}
|
||||
let result = try await group.next()
|
||||
group.cancelAll()
|
||||
if let result { return result }
|
||||
throw onTimeout()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,22 @@ actor MacNodeBridgePairingClient {
|
||||
let connection = NWConnection(to: endpoint, using: .tcp)
|
||||
let queue = DispatchQueue(label: "com.steipete.clawdis.macos.bridge-client")
|
||||
defer { connection.cancel() }
|
||||
try await self.withTimeout(seconds: 8, purpose: "connect") {
|
||||
try await AsyncTimeout.withTimeout(seconds: 8, onTimeout: {
|
||||
NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "connect timed out",
|
||||
])
|
||||
}) {
|
||||
try await self.startAndWaitForReady(connection, queue: queue)
|
||||
}
|
||||
|
||||
onStatus?("Authenticating…")
|
||||
try await self.send(hello, over: connection)
|
||||
|
||||
let first = try await self.withTimeout(seconds: 10, purpose: "hello") { () -> ReceivedFrame in
|
||||
let first = try await AsyncTimeout.withTimeout(seconds: 10, onTimeout: {
|
||||
NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "hello timed out",
|
||||
])
|
||||
}) { () -> ReceivedFrame in
|
||||
guard let frame = try await self.receiveFrame(over: connection) else {
|
||||
throw NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Bridge closed connection during hello",
|
||||
@@ -60,7 +68,11 @@ actor MacNodeBridgePairingClient {
|
||||
over: connection)
|
||||
|
||||
onStatus?("Waiting for approval…")
|
||||
let ok = try await self.withTimeout(seconds: 60, purpose: "pairing approval") {
|
||||
let ok = try await AsyncTimeout.withTimeout(seconds: 60, onTimeout: {
|
||||
NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "pairing approval timed out",
|
||||
])
|
||||
}) {
|
||||
while let next = try await self.receiveFrame(over: connection) {
|
||||
switch next.base.type {
|
||||
case "pair-ok":
|
||||
@@ -172,25 +184,5 @@ actor MacNodeBridgePairingClient {
|
||||
}
|
||||
}
|
||||
|
||||
private func withTimeout<T: Sendable>(
|
||||
seconds: Double,
|
||||
purpose: String,
|
||||
operation: @escaping @Sendable () async throws -> T) async throws -> T
|
||||
{
|
||||
try await withThrowingTaskGroup(of: T.self) { group in
|
||||
group.addTask { try await operation() }
|
||||
group.addTask {
|
||||
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
|
||||
throw NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "\(purpose) timed out",
|
||||
])
|
||||
}
|
||||
let result = try await group.next()
|
||||
group.cancelAll()
|
||||
if let result { return result }
|
||||
throw NSError(domain: "Bridge", code: 0, userInfo: [
|
||||
NSLocalizedDescriptionKey: "\(purpose) timed out",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,11 +48,15 @@ actor MacNodeBridgeSession {
|
||||
|
||||
try await Self.waitForReady(stateStream, timeoutSeconds: 6)
|
||||
|
||||
try await Self.withTimeout(seconds: 6) {
|
||||
try await AsyncTimeout.withTimeout(seconds: 6, onTimeout: {
|
||||
TimeoutError(message: "operation timed out")
|
||||
}) {
|
||||
try await self.send(hello)
|
||||
}
|
||||
|
||||
guard let line = try await Self.withTimeout(seconds: 6, operation: {
|
||||
guard let line = try await AsyncTimeout.withTimeout(seconds: 6, onTimeout: {
|
||||
TimeoutError(message: "operation timed out")
|
||||
}, operation: {
|
||||
try await self.receiveLine()
|
||||
}),
|
||||
let data = line.data(using: .utf8),
|
||||
@@ -290,7 +294,9 @@ actor MacNodeBridgeSession {
|
||||
_ stream: AsyncStream<NWConnection.State>,
|
||||
timeoutSeconds: Double) async throws
|
||||
{
|
||||
try await self.withTimeout(seconds: timeoutSeconds) {
|
||||
try await AsyncTimeout.withTimeout(seconds: timeoutSeconds, onTimeout: {
|
||||
TimeoutError(message: "operation timed out")
|
||||
}) {
|
||||
for await state in stream {
|
||||
switch state {
|
||||
case .ready:
|
||||
@@ -311,22 +317,5 @@ actor MacNodeBridgeSession {
|
||||
}
|
||||
}
|
||||
|
||||
private static func withTimeout<T: Sendable>(
|
||||
seconds: Double,
|
||||
operation: @escaping @Sendable () async throws -> T) async throws -> T
|
||||
{
|
||||
try await withThrowingTaskGroup(of: T.self) { group in
|
||||
group.addTask {
|
||||
try await operation()
|
||||
}
|
||||
group.addTask {
|
||||
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
|
||||
throw TimeoutError(message: "operation timed out")
|
||||
}
|
||||
let result = try await group.next()
|
||||
group.cancelAll()
|
||||
if let result { return result }
|
||||
throw TimeoutError(message: "operation timed out")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -228,7 +228,11 @@ final class MacNodeModeCoordinator {
|
||||
_ stream: AsyncStream<NWConnection.State>,
|
||||
timeoutSeconds: Double) async throws
|
||||
{
|
||||
try await self.withTimeout(seconds: timeoutSeconds) {
|
||||
try await AsyncTimeout.withTimeout(seconds: timeoutSeconds, onTimeout: {
|
||||
NSError(domain: "Bridge", code: 22, userInfo: [
|
||||
NSLocalizedDescriptionKey: "operation timed out",
|
||||
])
|
||||
}) {
|
||||
for await state in stream {
|
||||
switch state {
|
||||
case .ready:
|
||||
@@ -249,27 +253,6 @@ final class MacNodeModeCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
private static func withTimeout<T: Sendable>(
|
||||
seconds: Double,
|
||||
operation: @escaping @Sendable () async throws -> T) async throws -> T
|
||||
{
|
||||
try await withThrowingTaskGroup(of: T.self) { group in
|
||||
group.addTask { try await operation() }
|
||||
group.addTask {
|
||||
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
|
||||
throw NSError(domain: "Bridge", code: 22, userInfo: [
|
||||
NSLocalizedDescriptionKey: "operation timed out",
|
||||
])
|
||||
}
|
||||
let result = try await group.next()
|
||||
group.cancelAll()
|
||||
if let result { return result }
|
||||
throw NSError(domain: "Bridge", code: 22, userInfo: [
|
||||
NSLocalizedDescriptionKey: "operation timed out",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private func resolveBridgeEndpoint(timeoutSeconds: Double) async -> NWEndpoint? {
|
||||
let mode = await MainActor.run(body: { AppStateStore.shared.connectionMode })
|
||||
if mode == .remote {
|
||||
|
||||
Reference in New Issue
Block a user