diff --git a/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift b/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift index acc8b7015..f7ee4aa79 100644 --- a/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift +++ b/apps/ios/Sources/Chat/IOSGatewayChatTransport.swift @@ -95,16 +95,24 @@ struct IOSGatewayChatTransport: ClawdbotChatTransport, Sendable { continuation.yield(.seqGap) case "health": guard let payload = evt.payload else { break } - let ok = (try? GatewayPayloadDecoding.decode(payload, as: ClawdbotGatewayHealthOK.self))?.ok ?? true + let ok = (try? GatewayPayloadDecoding.decode( + payload, + as: ClawdbotGatewayHealthOK.self))?.ok ?? true continuation.yield(.health(ok: ok)) case "chat": guard let payload = evt.payload else { break } - if let chatPayload = try? GatewayPayloadDecoding.decode(payload, as: ClawdbotChatEventPayload.self) { + if let chatPayload = try? GatewayPayloadDecoding.decode( + payload, + as: ClawdbotChatEventPayload.self) + { continuation.yield(.chat(chatPayload)) } case "agent": guard let payload = evt.payload else { break } - if let agentPayload = try? GatewayPayloadDecoding.decode(payload, as: ClawdbotAgentEventPayload.self) { + if let agentPayload = try? GatewayPayloadDecoding.decode( + payload, + as: ClawdbotAgentEventPayload.self) + { continuation.yield(.agent(agentPayload)) } default: diff --git a/apps/ios/Sources/Gateway/GatewayConnectionController.swift b/apps/ios/Sources/Gateway/GatewayConnectionController.swift index a80946713..0f1cd02cf 100644 --- a/apps/ios/Sources/Gateway/GatewayConnectionController.swift +++ b/apps/ios/Sources/Gateway/GatewayConnectionController.swift @@ -55,7 +55,11 @@ final class GatewayConnectionController { guard let host = self.resolveGatewayHost(gateway) else { return } let port = gateway.gatewayPort ?? 18789 let tlsParams = self.resolveDiscoveredTLSParams(gateway: gateway) - guard let url = self.buildGatewayURL(host: host, port: port, useTLS: tlsParams?.required == true) else { return } + guard let url = self.buildGatewayURL( + host: host, + port: port, + useTLS: tlsParams?.required == true) + else { return } self.didAutoConnect = true self.startAutoConnect( url: url, @@ -72,7 +76,11 @@ final class GatewayConnectionController { let password = GatewaySettingsStore.loadGatewayPassword(instanceId: instanceId) let stableID = self.manualStableID(host: host, port: port) let tlsParams = self.resolveManualTLSParams(stableID: stableID, tlsEnabled: useTLS) - guard let url = self.buildGatewayURL(host: host, port: port, useTLS: tlsParams?.required == true) else { return } + guard let url = self.buildGatewayURL( + host: host, + port: port, + useTLS: tlsParams?.required == true) + else { return } self.didAutoConnect = true self.startAutoConnect( url: url, diff --git a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift index faf03e520..52ada8d80 100644 --- a/apps/ios/Sources/Gateway/GatewaySettingsStore.swift +++ b/apps/ios/Sources/Gateway/GatewaySettingsStore.swift @@ -89,7 +89,9 @@ enum GatewaySettingsStore { } static func loadGatewayPassword(instanceId: String) -> String? { - KeychainStore.loadString(service: self.gatewayService, account: self.gatewayPasswordAccount(instanceId: instanceId))? + KeychainStore.loadString( + service: self.gatewayService, + account: self.gatewayPasswordAccount(instanceId: instanceId))? .trimmingCharacters(in: .whitespacesAndNewlines) } @@ -193,7 +195,9 @@ enum GatewaySettingsStore { if defaults.object(forKey: self.manualEnabledDefaultsKey) == nil, defaults.object(forKey: self.legacyManualEnabledDefaultsKey) != nil { - defaults.set(defaults.bool(forKey: self.legacyManualEnabledDefaultsKey), forKey: self.manualEnabledDefaultsKey) + defaults.set( + defaults.bool(forKey: self.legacyManualEnabledDefaultsKey), + forKey: self.manualEnabledDefaultsKey) } if defaults.string(forKey: self.manualHostDefaultsKey)?.isEmpty != false, @@ -206,7 +210,9 @@ enum GatewaySettingsStore { if defaults.integer(forKey: self.manualPortDefaultsKey) == 0, defaults.integer(forKey: self.legacyManualPortDefaultsKey) > 0 { - defaults.set(defaults.integer(forKey: self.legacyManualPortDefaultsKey), forKey: self.manualPortDefaultsKey) + defaults.set( + defaults.integer(forKey: self.legacyManualPortDefaultsKey), + forKey: self.manualPortDefaultsKey) } if defaults.object(forKey: self.discoveryDebugLogsDefaultsKey) == nil, diff --git a/apps/ios/Sources/Model/NodeAppModel.swift b/apps/ios/Sources/Model/NodeAppModel.swift index 7d6e191c1..e788d5db8 100644 --- a/apps/ios/Sources/Model/NodeAppModel.swift +++ b/apps/ios/Sources/Model/NodeAppModel.swift @@ -857,17 +857,20 @@ final class NodeAppModel { return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: payload) } - private func locationMode() -> ClawdbotLocationMode { +} + +private extension NodeAppModel { + func locationMode() -> ClawdbotLocationMode { let raw = UserDefaults.standard.string(forKey: "location.enabledMode") ?? "off" return ClawdbotLocationMode(rawValue: raw) ?? .off } - private func isLocationPreciseEnabled() -> Bool { + func isLocationPreciseEnabled() -> Bool { if UserDefaults.standard.object(forKey: "location.preciseEnabled") == nil { return true } return UserDefaults.standard.bool(forKey: "location.preciseEnabled") } - private static func decodeParams(_ type: T.Type, from json: String?) throws -> T { + static func decodeParams(_ type: T.Type, from json: String?) throws -> T { guard let json, let data = json.data(using: .utf8) else { throw NSError(domain: "Gateway", code: 20, userInfo: [ NSLocalizedDescriptionKey: "INVALID_REQUEST: paramsJSON required", @@ -876,7 +879,7 @@ final class NodeAppModel { return try JSONDecoder().decode(type, from: data) } - private static func encodePayload(_ obj: some Encodable) throws -> String { + static func encodePayload(_ obj: some Encodable) throws -> String { let data = try JSONEncoder().encode(obj) guard let json = String(bytes: data, encoding: .utf8) else { throw NSError(domain: "NodeAppModel", code: 21, userInfo: [ @@ -886,17 +889,17 @@ final class NodeAppModel { return json } - private func isCameraEnabled() -> Bool { + func isCameraEnabled() -> Bool { // Default-on: if the key doesn't exist yet, treat it as enabled. if UserDefaults.standard.object(forKey: "camera.enabled") == nil { return true } return UserDefaults.standard.bool(forKey: "camera.enabled") } - private func triggerCameraFlash() { + func triggerCameraFlash() { self.cameraFlashNonce &+= 1 } - private func showCameraHUD(text: String, kind: CameraHUDKind, autoHideSeconds: Double? = nil) { + func showCameraHUD(text: String, kind: CameraHUDKind, autoHideSeconds: Double? = nil) { self.cameraHUDDismissTask?.cancel() withAnimation(.spring(response: 0.25, dampingFraction: 0.85)) { diff --git a/apps/macos/Sources/Clawdbot/ExecApprovals.swift b/apps/macos/Sources/Clawdbot/ExecApprovals.swift index 993e466d3..907490ba5 100644 --- a/apps/macos/Sources/Clawdbot/ExecApprovals.swift +++ b/apps/macos/Sources/Clawdbot/ExecApprovals.swift @@ -86,9 +86,9 @@ enum ExecApprovalDecision: String, Codable, Sendable { struct ExecAllowlistEntry: Codable, Hashable { var pattern: String - var lastUsedAt: Double? = nil - var lastUsedCommand: String? = nil - var lastResolvedPath: String? = nil + var lastUsedAt: Double? + var lastUsedCommand: String? + var lastResolvedPath: String? } struct ExecApprovalsDefaults: Codable { diff --git a/apps/macos/Sources/Clawdbot/ExecApprovalsSocket.swift b/apps/macos/Sources/Clawdbot/ExecApprovalsSocket.swift index 9e1532274..4d6541287 100644 --- a/apps/macos/Sources/Clawdbot/ExecApprovalsSocket.swift +++ b/apps/macos/Sources/Clawdbot/ExecApprovalsSocket.swift @@ -87,16 +87,19 @@ enum ExecApprovalsSocketClient { let trimmedToken = token.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedPath.isEmpty, !trimmedToken.isEmpty else { return nil } do { - return try await AsyncTimeout.withTimeoutMs(timeoutMs: timeoutMs, onTimeout: { - TimeoutError(message: "exec approvals socket timeout") - }, operation: { - try await Task.detached { - try self.requestDecisionSync( - socketPath: trimmedPath, - token: trimmedToken, - request: request) - }.value - }) + return try await AsyncTimeout.withTimeoutMs( + timeoutMs: timeoutMs, + onTimeout: { + TimeoutError(message: "exec approvals socket timeout") + }, + operation: { + try await Task.detached { + try self.requestDecisionSync( + socketPath: trimmedPath, + token: trimmedToken, + request: request) + }.value + }) } catch { return nil } diff --git a/apps/macos/Sources/Clawdbot/NodeMode/MacNodeRuntime.swift b/apps/macos/Sources/Clawdbot/NodeMode/MacNodeRuntime.swift index 3466bae90..51f6c8e82 100644 --- a/apps/macos/Sources/Clawdbot/NodeMode/MacNodeRuntime.swift +++ b/apps/macos/Sources/Clawdbot/NodeMode/MacNodeRuntime.swift @@ -733,7 +733,9 @@ actor MacNodeRuntime { return BridgeInvokeResponse(id: req.id, ok: true) } } +} +extension MacNodeRuntime { private static func decodeParams(_ type: T.Type, from json: String?) throws -> T { guard let json, let data = json.data(using: .utf8) else { throw NSError(domain: "Gateway", code: 20, userInfo: [ diff --git a/apps/macos/Sources/Clawdbot/SystemRunSettingsView.swift b/apps/macos/Sources/Clawdbot/SystemRunSettingsView.swift index 411568501..0ac799e6d 100644 --- a/apps/macos/Sources/Clawdbot/SystemRunSettingsView.swift +++ b/apps/macos/Sources/Clawdbot/SystemRunSettingsView.swift @@ -80,10 +80,7 @@ struct SystemRunSettingsView: View { .labelsHidden() .pickerStyle(.menu) - Text(self.model.isDefaultsScope - ? "Defaults apply when an agent has no overrides. Ask controls prompt behavior; fallback is used when no companion UI is reachable." - : - "Security controls whether system.run can execute on this Mac when paired as a node. Ask controls prompt behavior; fallback is used when no companion UI is reachable.") + Text(self.scopeMessage) .font(.footnote) .foregroundStyle(.tertiary) .fixedSize(horizontal: false, vertical: true) @@ -138,6 +135,15 @@ struct SystemRunSettingsView: View { } } } + + private var scopeMessage: String { + if self.model.isDefaultsScope { + return "Defaults apply when an agent has no overrides. " + + "Ask controls prompt behavior; fallback is used when no companion UI is reachable." + } + return "Security controls whether system.run can execute on this Mac when paired as a node. " + + "Ask controls prompt behavior; fallback is used when no companion UI is reachable." + } } private enum ExecApprovalsSettingsTab: String, CaseIterable, Identifiable {