Download apps/macos/Sources/OpenClaw/NodeMode/MacNodeBrowserProxy.swift from Darochin/openskynet: direct link, hf CLI and curl.
- Browser
- Download file 10 kB
-
https://huggingface.co/Darochin/openskynet/resolve/main/apps/macos/Sources/OpenClaw/NodeMode/MacNodeBrowserProxy.swift
- Command line
-
hf download hf://Darochin/openskynet/apps/macos/Sources/OpenClaw/NodeMode/MacNodeBrowserProxy.swift
-
curl -L -o MacNodeBrowserProxy.swift https://huggingface.co/Darochin/openskynet/resolve/main/apps/macos/Sources/OpenClaw/NodeMode/MacNodeBrowserProxy.swift
10 kB
| import Foundation | |
| import OpenClawProtocol | |
| import UniformTypeIdentifiers | |
| actor MacNodeBrowserProxy { | |
| static let shared = MacNodeBrowserProxy() | |
| struct Endpoint { | |
| let baseURL: URL | |
| let token: String? | |
| let password: String? | |
| } | |
| private struct RequestParams: Decodable { | |
| let method: String? | |
| let path: String? | |
| let query: [String: OpenClawProtocol.AnyCodable]? | |
| let body: OpenClawProtocol.AnyCodable? | |
| let timeoutMs: Int? | |
| let profile: String? | |
| } | |
| private struct ProxyFilePayload { | |
| let path: String | |
| let base64: String | |
| let mimeType: String? | |
| func asJSON() -> [String: Any] { | |
| var json: [String: Any] = [ | |
| "path": self.path, | |
| "base64": self.base64, | |
| ] | |
| if let mimeType = self.mimeType { | |
| json["mimeType"] = mimeType | |
| } | |
| return json | |
| } | |
| } | |
| private static let maxProxyFileBytes = 10 * 1024 * 1024 | |
| private let endpointProvider: () -> Endpoint | |
| private let performRequest: (URLRequest) async throws -> (Data, URLResponse) | |
| init( | |
| session: URLSession = .shared, | |
| endpointProvider: ( () -> Endpoint)? = nil, | |
| performRequest: ( (URLRequest) async throws -> (Data, URLResponse))? = nil) | |
| { | |
| self.endpointProvider = endpointProvider ?? MacNodeBrowserProxy.defaultEndpoint | |
| self.performRequest = performRequest ?? { request in | |
| try await session.data(for: request) | |
| } | |
| } | |
| func request(paramsJSON: String?) async throws -> String { | |
| let params = try Self.decodeRequestParams(from: paramsJSON) | |
| let request = try Self.makeRequest(params: params, endpoint: self.endpointProvider()) | |
| let (data, response) = try await self.performRequest(request) | |
| let http = try Self.requireHTTPResponse(response) | |
| guard (200..<300).contains(http.statusCode) else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: http.statusCode, userInfo: [ | |
| NSLocalizedDescriptionKey: Self.httpErrorMessage(statusCode: http.statusCode, data: data), | |
| ]) | |
| } | |
| let result = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) | |
| let files = try Self.loadProxyFiles(from: result) | |
| var payload: [String: Any] = ["result": result] | |
| if !files.isEmpty { | |
| payload["files"] = files.map { $0.asJSON() } | |
| } | |
| let payloadData = try JSONSerialization.data(withJSONObject: payload) | |
| guard let payloadJSON = String(data: payloadData, encoding: .utf8) else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 2, userInfo: [ | |
| NSLocalizedDescriptionKey: "browser proxy returned invalid UTF-8", | |
| ]) | |
| } | |
| return payloadJSON | |
| } | |
| private static func defaultEndpoint() -> Endpoint { | |
| let config = GatewayEndpointStore.localConfig() | |
| let controlPort = GatewayEnvironment.gatewayPort() + 2 | |
| let baseURL = URL(string: "http://127.0.0.1:\(controlPort)")! | |
| return Endpoint(baseURL: baseURL, token: config.token, password: config.password) | |
| } | |
| private static func decodeRequestParams(from raw: String?) throws -> RequestParams { | |
| guard let raw else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 3, userInfo: [ | |
| NSLocalizedDescriptionKey: "INVALID_REQUEST: paramsJSON required", | |
| ]) | |
| } | |
| return try JSONDecoder().decode(RequestParams.self, from: Data(raw.utf8)) | |
| } | |
| private static func makeRequest(params: RequestParams, endpoint: Endpoint) throws -> URLRequest { | |
| let method = (params.method ?? "GET").trimmingCharacters(in: .whitespacesAndNewlines).uppercased() | |
| let path = (params.path ?? "").trimmingCharacters(in: .whitespacesAndNewlines) | |
| guard !path.isEmpty else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 1, userInfo: [ | |
| NSLocalizedDescriptionKey: "INVALID_REQUEST: path required", | |
| ]) | |
| } | |
| let normalizedPath = path.hasPrefix("/") ? path : "/\(path)" | |
| guard var components = URLComponents( | |
| url: endpoint.baseURL.appendingPathComponent(String(normalizedPath.dropFirst())), | |
| resolvingAgainstBaseURL: false) | |
| else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 4, userInfo: [ | |
| NSLocalizedDescriptionKey: "INVALID_REQUEST: invalid browser proxy URL", | |
| ]) | |
| } | |
| var queryItems: [URLQueryItem] = [] | |
| if let query = params.query { | |
| for key in query.keys.sorted() { | |
| let value = query[key]?.value | |
| guard value != nil, !(value is NSNull) else { continue } | |
| queryItems.append(URLQueryItem(name: key, value: Self.stringValue(for: value))) | |
| } | |
| } | |
| let profile = params.profile?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" | |
| if !profile.isEmpty, !queryItems.contains(where: { $0.name == "profile" }) { | |
| queryItems.append(URLQueryItem(name: "profile", value: profile)) | |
| } | |
| if !queryItems.isEmpty { | |
| components.queryItems = queryItems | |
| } | |
| guard let url = components.url else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 5, userInfo: [ | |
| NSLocalizedDescriptionKey: "INVALID_REQUEST: invalid browser proxy URL", | |
| ]) | |
| } | |
| var request = URLRequest(url: url) | |
| request.httpMethod = method | |
| request.timeoutInterval = params.timeoutMs.map { TimeInterval(max($0, 1)) / 1000 } ?? 5 | |
| request.setValue("application/json", forHTTPHeaderField: "Accept") | |
| if let token = endpoint.token?.trimmingCharacters(in: .whitespacesAndNewlines), !token.isEmpty { | |
| request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") | |
| } else if let password = endpoint.password?.trimmingCharacters(in: .whitespacesAndNewlines), | |
| !password.isEmpty | |
| { | |
| request.setValue(password, forHTTPHeaderField: "x-openclaw-password") | |
| } | |
| if method != "GET", let body = params.body { | |
| request.httpBody = try JSONSerialization.data(withJSONObject: body.foundationValue, options: [.fragmentsAllowed]) | |
| request.setValue("application/json", forHTTPHeaderField: "Content-Type") | |
| } | |
| return request | |
| } | |
| private static func requireHTTPResponse(_ response: URLResponse) throws -> HTTPURLResponse { | |
| guard let http = response as? HTTPURLResponse else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 6, userInfo: [ | |
| NSLocalizedDescriptionKey: "browser proxy returned a non-HTTP response", | |
| ]) | |
| } | |
| return http | |
| } | |
| private static func httpErrorMessage(statusCode: Int, data: Data) -> String { | |
| if let object = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) as? [String: Any], | |
| let error = object["error"] as? String, | |
| !error.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty | |
| { | |
| return error | |
| } | |
| if let text = String(data: data, encoding: .utf8)? | |
| .trimmingCharacters(in: .whitespacesAndNewlines), | |
| !text.isEmpty | |
| { | |
| return text | |
| } | |
| return "HTTP \(statusCode)" | |
| } | |
| private static func stringValue(for value: Any?) -> String? { | |
| guard let value else { return nil } | |
| if let string = value as? String { return string } | |
| if let bool = value as? Bool { return bool ? "true" : "false" } | |
| if let number = value as? NSNumber { return number.stringValue } | |
| return String(describing: value) | |
| } | |
| private static func loadProxyFiles(from result: Any) throws -> [ProxyFilePayload] { | |
| let paths = self.collectProxyPaths(from: result) | |
| return try paths.map(self.loadProxyFile) | |
| } | |
| private static func collectProxyPaths(from payload: Any) -> [String] { | |
| guard let object = payload as? [String: Any] else { return [] } | |
| var paths = Set<String>() | |
| if let path = object["path"] as? String, !path.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { | |
| paths.insert(path.trimmingCharacters(in: .whitespacesAndNewlines)) | |
| } | |
| if let imagePath = object["imagePath"] as? String, | |
| !imagePath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty | |
| { | |
| paths.insert(imagePath.trimmingCharacters(in: .whitespacesAndNewlines)) | |
| } | |
| if let download = object["download"] as? [String: Any], | |
| let path = download["path"] as? String, | |
| !path.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty | |
| { | |
| paths.insert(path.trimmingCharacters(in: .whitespacesAndNewlines)) | |
| } | |
| return paths.sorted() | |
| } | |
| private static func loadProxyFile(path: String) throws -> ProxyFilePayload { | |
| let url = URL(fileURLWithPath: path) | |
| let values = try url.resourceValues(forKeys: [.isRegularFileKey, .fileSizeKey]) | |
| guard values.isRegularFile == true else { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 7, userInfo: [ | |
| NSLocalizedDescriptionKey: "browser proxy file not found: \(path)", | |
| ]) | |
| } | |
| if let fileSize = values.fileSize, fileSize > Self.maxProxyFileBytes { | |
| throw NSError(domain: "MacNodeBrowserProxy", code: 8, userInfo: [ | |
| NSLocalizedDescriptionKey: "browser proxy file exceeds 10MB: \(path)", | |
| ]) | |
| } | |
| let data = try Data(contentsOf: url) | |
| let mimeType = UTType(filenameExtension: url.pathExtension)?.preferredMIMEType | |
| return ProxyFilePayload(path: path, base64: data.base64EncodedString(), mimeType: mimeType) | |
| } | |
| } | |