fix: stabilize gateway ws + iOS
This commit is contained in:
@@ -44,7 +44,7 @@ public protocol WebSocketSessioning: AnyObject {
|
||||
}
|
||||
|
||||
extension URLSession: WebSocketSessioning {
|
||||
func makeWebSocketTask(url: URL) -> WebSocketTaskBox {
|
||||
public func makeWebSocketTask(url: URL) -> WebSocketTaskBox {
|
||||
let task = self.webSocketTask(with: url)
|
||||
// Avoid "Message too long" receive errors for large snapshots / history payloads.
|
||||
task.maximumMessageSize = 16 * 1024 * 1024 // 16 MB
|
||||
@@ -54,6 +54,10 @@ extension URLSession: WebSocketSessioning {
|
||||
|
||||
public struct WebSocketSessionBox: @unchecked Sendable {
|
||||
public let session: any WebSocketSessioning
|
||||
|
||||
public init(session: any WebSocketSessioning) {
|
||||
self.session = session
|
||||
}
|
||||
}
|
||||
|
||||
public struct GatewayConnectOptions: Sendable {
|
||||
@@ -472,7 +476,7 @@ public actor GatewayChannelActor {
|
||||
|
||||
public func request(
|
||||
method: String,
|
||||
params: [String: ClawdbotProtocol.AnyCodable]?,
|
||||
params: [String: AnyCodable]?,
|
||||
timeoutMs: Double? = nil) async throws -> Data
|
||||
{
|
||||
do {
|
||||
@@ -525,8 +529,8 @@ public actor GatewayChannelActor {
|
||||
if res.ok == false {
|
||||
let code = res.error?["code"]?.value as? String
|
||||
let msg = res.error?["message"]?.value as? String
|
||||
let details: [String: ClawdbotProtocol.AnyCodable] = (res.error ?? [:]).reduce(into: [:]) { acc, pair in
|
||||
acc[pair.key] = ClawdbotProtocol.AnyCodable(pair.value.value)
|
||||
let details: [String: AnyCodable] = (res.error ?? [:]).reduce(into: [:]) { acc, pair in
|
||||
acc[pair.key] = AnyCodable(pair.value.value)
|
||||
}
|
||||
throw GatewayResponseError(method: method, code: code, message: msg, details: details)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public actor GatewayNodeSession {
|
||||
private var serverEventSubscribers: [UUID: AsyncStream<EventFrame>.Continuation] = [:]
|
||||
private var canvasHostUrl: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public func connect(
|
||||
url: URL,
|
||||
token: String?,
|
||||
@@ -107,9 +109,9 @@ public actor GatewayNodeSession {
|
||||
|
||||
public func sendEvent(event: String, payloadJSON: String?) async {
|
||||
guard let channel = self.channel else { return }
|
||||
let params: [String: ClawdbotProtocol.AnyCodable] = [
|
||||
"event": ClawdbotProtocol.AnyCodable(event),
|
||||
"payloadJSON": ClawdbotProtocol.AnyCodable(payloadJSON ?? NSNull()),
|
||||
let params: [String: AnyCodable] = [
|
||||
"event": AnyCodable(event),
|
||||
"payloadJSON": AnyCodable(payloadJSON ?? NSNull()),
|
||||
]
|
||||
do {
|
||||
_ = try await channel.request(method: "node.event", params: params, timeoutMs: 8000)
|
||||
@@ -174,16 +176,16 @@ public actor GatewayNodeSession {
|
||||
|
||||
private func sendInvokeResult(request: NodeInvokeRequestPayload, response: BridgeInvokeResponse) async {
|
||||
guard let channel = self.channel else { return }
|
||||
var params: [String: ClawdbotProtocol.AnyCodable] = [
|
||||
"id": ClawdbotProtocol.AnyCodable(request.id),
|
||||
"nodeId": ClawdbotProtocol.AnyCodable(request.nodeId),
|
||||
"ok": ClawdbotProtocol.AnyCodable(response.ok),
|
||||
"payloadJSON": ClawdbotProtocol.AnyCodable(response.payloadJSON ?? NSNull()),
|
||||
var params: [String: AnyCodable] = [
|
||||
"id": AnyCodable(request.id),
|
||||
"nodeId": AnyCodable(request.nodeId),
|
||||
"ok": AnyCodable(response.ok),
|
||||
"payloadJSON": AnyCodable(response.payloadJSON ?? NSNull()),
|
||||
]
|
||||
if let error = response.error {
|
||||
params["error"] = ClawdbotProtocol.AnyCodable([
|
||||
"code": ClawdbotProtocol.AnyCodable(error.code.rawValue),
|
||||
"message": ClawdbotProtocol.AnyCodable(error.message),
|
||||
params["error"] = AnyCodable([
|
||||
"code": AnyCodable(error.code.rawValue),
|
||||
"message": AnyCodable(error.message),
|
||||
])
|
||||
}
|
||||
do {
|
||||
@@ -194,7 +196,7 @@ public actor GatewayNodeSession {
|
||||
}
|
||||
|
||||
private func decodeParamsJSON(
|
||||
_ paramsJSON: String?) throws -> [String: ClawdbotProtocol.AnyCodable]?
|
||||
_ paramsJSON: String?) throws -> [String: AnyCodable]?
|
||||
{
|
||||
guard let paramsJSON, !paramsJSON.isEmpty else { return nil }
|
||||
guard let data = paramsJSON.data(using: .utf8) else {
|
||||
@@ -207,13 +209,13 @@ public actor GatewayNodeSession {
|
||||
return nil
|
||||
}
|
||||
return dict.reduce(into: [:]) { acc, entry in
|
||||
acc[entry.key] = ClawdbotProtocol.AnyCodable(entry.value)
|
||||
acc[entry.key] = AnyCodable(entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
private func broadcastServerEvent(_ evt: EventFrame) {
|
||||
for (id, continuation) in self.serverEventSubscribers {
|
||||
if continuation.yield(evt) == .terminated {
|
||||
if case .terminated = continuation.yield(evt) {
|
||||
self.serverEventSubscribers.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ public enum GatewayPayloadDecoding {
|
||||
return try JSONDecoder().decode(T.self, from: data)
|
||||
}
|
||||
|
||||
public static func decode<T: Decodable>(
|
||||
_ payload: AnyCodable,
|
||||
as _: T.Type = T.self) throws -> T
|
||||
{
|
||||
let data = try JSONEncoder().encode(payload)
|
||||
return try JSONDecoder().decode(T.self, from: data)
|
||||
}
|
||||
|
||||
public static func decodeIfPresent<T: Decodable>(
|
||||
_ payload: ClawdbotProtocol.AnyCodable?,
|
||||
as _: T.Type = T.self) throws -> T?
|
||||
@@ -17,4 +25,12 @@ public enum GatewayPayloadDecoding {
|
||||
guard let payload else { return nil }
|
||||
return try self.decode(payload, as: T.self)
|
||||
}
|
||||
|
||||
public static func decodeIfPresent<T: Decodable>(
|
||||
_ payload: AnyCodable?,
|
||||
as _: T.Type = T.self) throws -> T?
|
||||
{
|
||||
guard let payload else { return nil }
|
||||
return try self.decode(payload, as: T.self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,17 @@ public enum InstanceIdentity {
|
||||
UserDefaults(suiteName: suiteName) ?? .standard
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
private static func readMainActor<T: Sendable>(_ body: @MainActor () -> T) -> T {
|
||||
if Thread.isMainThread {
|
||||
return MainActor.assumeIsolated { body() }
|
||||
}
|
||||
return DispatchQueue.main.sync {
|
||||
MainActor.assumeIsolated { body() }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static let instanceId: String = {
|
||||
let defaults = Self.defaults
|
||||
if let existing = defaults.string(forKey: instanceIdKey)?
|
||||
@@ -28,7 +39,9 @@ public enum InstanceIdentity {
|
||||
|
||||
public static let displayName: String = {
|
||||
#if canImport(UIKit)
|
||||
let name = UIDevice.current.name.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let name = Self.readMainActor {
|
||||
UIDevice.current.name.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
return name.isEmpty ? "clawdbot" : name
|
||||
#else
|
||||
if let name = Host.current().localizedName?.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||
@@ -65,10 +78,12 @@ public enum InstanceIdentity {
|
||||
|
||||
public static let deviceFamily: String = {
|
||||
#if canImport(UIKit)
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad: return "iPad"
|
||||
case .phone: return "iPhone"
|
||||
default: return "iOS"
|
||||
return Self.readMainActor {
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad: return "iPad"
|
||||
case .phone: return "iPhone"
|
||||
default: return "iOS"
|
||||
}
|
||||
}
|
||||
#else
|
||||
return "Mac"
|
||||
@@ -78,11 +93,12 @@ public enum InstanceIdentity {
|
||||
public static let platformString: String = {
|
||||
let v = ProcessInfo.processInfo.operatingSystemVersion
|
||||
#if canImport(UIKit)
|
||||
let name: String
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad: name = "iPadOS"
|
||||
case .phone: name = "iOS"
|
||||
default: name = "iOS"
|
||||
let name = Self.readMainActor {
|
||||
switch UIDevice.current.userInterfaceIdiom {
|
||||
case .pad: return "iPadOS"
|
||||
case .phone: return "iOS"
|
||||
default: return "iOS"
|
||||
}
|
||||
}
|
||||
return "\(name) \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user