Download apps/ios/Sources/Model/NodeAppModel+Canvas.swift from Darochin/openskynet: direct link, hf CLI and curl.
- Browser
- Download file 3.67 kB
-
https://huggingface.co/Darochin/openskynet/resolve/main/apps/ios/Sources/Model/NodeAppModel%2BCanvas.swift
- Command line
-
hf download hf://Darochin/openskynet/apps/ios/Sources/Model/NodeAppModel+Canvas.swift
-
curl -L -o NodeAppModel+Canvas.swift https://huggingface.co/Darochin/openskynet/resolve/main/apps/ios/Sources/Model/NodeAppModel%2BCanvas.swift
3.67 kB
| import Foundation | |
| import Network | |
| import OpenClawKit | |
| enum A2UIReadyState { | |
| case ready(String) | |
| case hostNotConfigured | |
| case hostUnavailable | |
| } | |
| extension NodeAppModel { | |
| func resolveCanvasHostURL() async -> String? { | |
| guard let raw = await self.gatewaySession.currentCanvasHostUrl() else { return nil } | |
| let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) | |
| guard !trimmed.isEmpty, let base = URL(string: trimmed) else { return nil } | |
| if let host = base.host, LoopbackHost.isLoopback(host) { | |
| return nil | |
| } | |
| return base.appendingPathComponent("__openclaw__/canvas/").absoluteString | |
| } | |
| func _test_resolveA2UIHostURL() async -> String? { | |
| await self.resolveA2UIHostURL() | |
| } | |
| func resolveA2UIHostURL() async -> String? { | |
| guard let raw = await self.gatewaySession.currentCanvasHostUrl() else { return nil } | |
| let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines) | |
| guard !trimmed.isEmpty, let base = URL(string: trimmed) else { return nil } | |
| if let host = base.host, LoopbackHost.isLoopback(host) { | |
| return nil | |
| } | |
| return base.appendingPathComponent("__openclaw__/a2ui/").absoluteString + "?platform=ios" | |
| } | |
| func showA2UIOnConnectIfNeeded() async { | |
| await MainActor.run { | |
| // Keep the bundled home canvas as the default connected view. | |
| // Agents can still explicitly present a remote or local canvas later. | |
| self.lastAutoA2uiURL = nil | |
| self.screen.showDefaultCanvas() | |
| } | |
| } | |
| func ensureA2UIReadyWithCapabilityRefresh(timeoutMs: Int = 5000) async -> A2UIReadyState { | |
| guard let initialUrl = await self.resolveA2UIHostURLWithCapabilityRefresh() else { | |
| return .hostNotConfigured | |
| } | |
| self.screen.navigate(to: initialUrl) | |
| if await self.screen.waitForA2UIReady(timeoutMs: timeoutMs) { | |
| return .ready(initialUrl) | |
| } | |
| // First render can fail when scoped capability rotates between reconnects. | |
| guard await self.gatewaySession.refreshNodeCanvasCapability() else { return .hostUnavailable } | |
| guard let refreshedUrl = await self.resolveA2UIHostURL() else { return .hostUnavailable } | |
| self.screen.navigate(to: refreshedUrl) | |
| if await self.screen.waitForA2UIReady(timeoutMs: timeoutMs) { | |
| return .ready(refreshedUrl) | |
| } | |
| return .hostUnavailable | |
| } | |
| func showLocalCanvasOnDisconnect() { | |
| self.lastAutoA2uiURL = nil | |
| self.screen.showDefaultCanvas() | |
| } | |
| private func resolveA2UIHostURLWithCapabilityRefresh() async -> String? { | |
| if let url = await self.resolveA2UIHostURL() { | |
| return url | |
| } | |
| guard await self.gatewaySession.refreshNodeCanvasCapability() else { return nil } | |
| return await self.resolveA2UIHostURL() | |
| } | |
| private func resolveCanvasHostURLWithCapabilityRefresh() async -> String? { | |
| if let url = await self.resolveCanvasHostURL() { | |
| return url | |
| } | |
| guard await self.gatewaySession.refreshNodeCanvasCapability() else { return nil } | |
| return await self.resolveCanvasHostURL() | |
| } | |
| private static func probeTCP(url: URL, timeoutSeconds: Double) async -> Bool { | |
| guard let host = url.host, !host.isEmpty else { return false } | |
| let portInt = url.port ?? ((url.scheme ?? "").lowercased() == "wss" ? 443 : 80) | |
| return await TCPProbe.probe( | |
| host: host, | |
| port: portInt, | |
| timeoutSeconds: timeoutSeconds, | |
| queueLabel: "a2ui.preflight") | |
| } | |
| } | |