diff --git a/apps/macos/Sources/Clawdis/CommandResolver.swift b/apps/macos/Sources/Clawdis/CommandResolver.swift index 0729024bc..537c06954 100644 --- a/apps/macos/Sources/Clawdis/CommandResolver.swift +++ b/apps/macos/Sources/Clawdis/CommandResolver.swift @@ -184,7 +184,11 @@ enum CommandResolver { static func hasAnyClawdisInvoker(searchPaths: [String]? = nil) -> Bool { if self.clawdisExecutable(searchPaths: searchPaths) != nil { return true } if self.findExecutable(named: "pnpm", searchPaths: searchPaths) != nil { return true } - if self.findExecutable(named: "node", searchPaths: searchPaths) != nil, self.nodeCliPath() != nil { return true } + if self.findExecutable(named: "node", searchPaths: searchPaths) != nil, + self.nodeCliPath() != nil + { + return true + } return false } @@ -244,7 +248,11 @@ enum CommandResolver { defaults: UserDefaults = .standard, searchPaths: [String]? = nil) -> [String] { - self.clawdisNodeCommand(subcommand: subcommand, extraArgs: extraArgs, defaults: defaults, searchPaths: searchPaths) + self.clawdisNodeCommand( + subcommand: subcommand, + extraArgs: extraArgs, + defaults: defaults, + searchPaths: searchPaths) } // MARK: - SSH helpers diff --git a/apps/macos/Sources/Clawdis/ConfigSettings.swift b/apps/macos/Sources/Clawdis/ConfigSettings.swift index 3f91e42b0..1cd3f9569 100644 --- a/apps/macos/Sources/Clawdis/ConfigSettings.swift +++ b/apps/macos/Sources/Clawdis/ConfigSettings.swift @@ -332,7 +332,9 @@ struct ConfigSettings: View { Text("Using ELEVENLABS_API_KEY from the environment.") .font(.footnote) .foregroundStyle(.secondary) - } else if self.gatewayApiKeyFound && self.talkApiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + } else if self.gatewayApiKeyFound + && self.talkApiKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + { Text("Using API key from the gateway profile.") .font(.footnote) .foregroundStyle(.secondary) diff --git a/apps/macos/Sources/Clawdis/Logging/ClawdisLogging.swift b/apps/macos/Sources/Clawdis/Logging/ClawdisLogging.swift index 21f3c091b..5913acdc4 100644 --- a/apps/macos/Sources/Clawdis/Logging/ClawdisLogging.swift +++ b/apps/macos/Sources/Clawdis/Logging/ClawdisLogging.swift @@ -114,6 +114,7 @@ struct ClawdisOSLogHandler: LogHandler { set { self.metadata[key] = newValue } } + // swiftlint:disable:next function_parameter_count func log( level: Logger.Level, message: Logger.Message, @@ -188,6 +189,7 @@ struct ClawdisFileLogHandler: LogHandler { set { self.metadata[key] = newValue } } + // swiftlint:disable:next function_parameter_count func log( level: Logger.Level, message: Logger.Message, diff --git a/apps/macos/Sources/Clawdis/MenuSessionsInjector.swift b/apps/macos/Sources/Clawdis/MenuSessionsInjector.swift index 7d8e899a9..f9509aae9 100644 --- a/apps/macos/Sources/Clawdis/MenuSessionsInjector.swift +++ b/apps/macos/Sources/Clawdis/MenuSessionsInjector.swift @@ -207,17 +207,25 @@ final class MenuSessionsInjector: NSObject, NSMenuDelegate { } if let error = self.nodesStore.lastError?.nonEmpty { - menu.insertItem(self.makeMessageItem(text: "Error: \(error)", symbolName: "exclamationmark.triangle", - width: width), at: cursor) + menu.insertItem( + self.makeMessageItem( + text: "Error: \(error)", + symbolName: "exclamationmark.triangle", + width: width), + at: cursor) cursor += 1 } else if let status = self.nodesStore.statusMessage?.nonEmpty { - menu.insertItem(self.makeMessageItem(text: status, symbolName: "info.circle", width: width), at: cursor) + menu.insertItem( + self.makeMessageItem(text: status, symbolName: "info.circle", width: width), + at: cursor) cursor += 1 } if entries.isEmpty { let title = self.nodesStore.isLoading ? "Loading devices..." : "No devices yet" - menu.insertItem(self.makeMessageItem(text: title, symbolName: "circle.dashed", width: width), at: cursor) + menu.insertItem( + self.makeMessageItem(text: title, symbolName: "circle.dashed", width: width), + at: cursor) cursor += 1 } else { for entry in entries.prefix(8) { diff --git a/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgePairingClient.swift b/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgePairingClient.swift index fcb8fbaae..3a0612834 100644 --- a/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgePairingClient.swift +++ b/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgePairingClient.swift @@ -17,29 +17,35 @@ actor MacNodeBridgePairingClient { let connection = NWConnection(to: endpoint, using: .tcp) let queue = DispatchQueue(label: "com.steipete.clawdis.macos.bridge-client") defer { connection.cancel() } - try await AsyncTimeout.withTimeout(seconds: 8, onTimeout: { - NSError(domain: "Bridge", code: 0, userInfo: [ - NSLocalizedDescriptionKey: "connect timed out", - ]) - }) { - try await self.startAndWaitForReady(connection, queue: queue) - } + try await AsyncTimeout.withTimeout( + seconds: 8, + onTimeout: { + NSError(domain: "Bridge", code: 0, userInfo: [ + NSLocalizedDescriptionKey: "connect timed out", + ]) + }, + operation: { + try await self.startAndWaitForReady(connection, queue: queue) + }) onStatus?("Authenticating…") try await self.send(hello, over: connection) - 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", + let first = try await AsyncTimeout.withTimeout( + seconds: 10, + onTimeout: { + NSError(domain: "Bridge", code: 0, userInfo: [ + NSLocalizedDescriptionKey: "hello timed out", ]) - } - return frame - } + }, + operation: { () -> 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", + ]) + } + return frame + }) switch first.base.type { case "hello-ok": @@ -68,28 +74,31 @@ actor MacNodeBridgePairingClient { over: connection) onStatus?("Waiting for 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": - return try self.decoder.decode(BridgePairOk.self, from: next.data) - case "error": - let e = try self.decoder.decode(BridgeErrorFrame.self, from: next.data) - throw NSError(domain: "Bridge", code: 2, userInfo: [ - NSLocalizedDescriptionKey: "\(e.code): \(e.message)", - ]) - default: - continue + let ok = try await AsyncTimeout.withTimeout( + seconds: 60, + onTimeout: { + NSError(domain: "Bridge", code: 0, userInfo: [ + NSLocalizedDescriptionKey: "pairing approval timed out", + ]) + }, + operation: { + while let next = try await self.receiveFrame(over: connection) { + switch next.base.type { + case "pair-ok": + return try self.decoder.decode(BridgePairOk.self, from: next.data) + case "error": + let e = try self.decoder.decode(BridgeErrorFrame.self, from: next.data) + throw NSError(domain: "Bridge", code: 2, userInfo: [ + NSLocalizedDescriptionKey: "\(e.code): \(e.message)", + ]) + default: + continue + } } - } - throw NSError(domain: "Bridge", code: 3, userInfo: [ - NSLocalizedDescriptionKey: "Pairing failed: bridge closed connection", - ]) - } + throw NSError(domain: "Bridge", code: 3, userInfo: [ + NSLocalizedDescriptionKey: "Pairing failed: bridge closed connection", + ]) + }) return ok.token diff --git a/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgeSession.swift b/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgeSession.swift index 9725a4f79..489530490 100644 --- a/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgeSession.swift +++ b/apps/macos/Sources/Clawdis/NodeMode/MacNodeBridgeSession.swift @@ -48,17 +48,23 @@ actor MacNodeBridgeSession { try await Self.waitForReady(stateStream, timeoutSeconds: 6) - try await AsyncTimeout.withTimeout(seconds: 6, onTimeout: { - TimeoutError(message: "operation timed out") - }) { - try await self.send(hello) - } + try await AsyncTimeout.withTimeout( + seconds: 6, + onTimeout: { + TimeoutError(message: "operation timed out") + }, + operation: { + try await self.send(hello) + }) - guard let line = try await AsyncTimeout.withTimeout(seconds: 6, onTimeout: { - TimeoutError(message: "operation timed out") - }, operation: { - try await self.receiveLine() - }), + 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), let base = try? self.decoder.decode(BridgeBaseFrame.self, from: data) else { @@ -294,28 +300,31 @@ actor MacNodeBridgeSession { _ stream: AsyncStream, timeoutSeconds: Double) async throws { - try await AsyncTimeout.withTimeout(seconds: timeoutSeconds, onTimeout: { - TimeoutError(message: "operation timed out") - }) { - for await state in stream { - switch state { - case .ready: - return - case let .failed(err): - throw err - case .cancelled: - throw NSError(domain: "Bridge", code: 20, userInfo: [ - NSLocalizedDescriptionKey: "Connection cancelled", - ]) - default: - continue + try await AsyncTimeout.withTimeout( + seconds: timeoutSeconds, + onTimeout: { + TimeoutError(message: "operation timed out") + }, + operation: { + for await state in stream { + switch state { + case .ready: + return + case let .failed(err): + throw err + case .cancelled: + throw NSError(domain: "Bridge", code: 20, userInfo: [ + NSLocalizedDescriptionKey: "Connection cancelled", + ]) + default: + continue + } } - } - throw NSError(domain: "Bridge", code: 21, userInfo: [ - NSLocalizedDescriptionKey: "Connection closed", - ]) - } + throw NSError(domain: "Bridge", code: 21, userInfo: [ + NSLocalizedDescriptionKey: "Connection closed", + ]) + }) } - + } diff --git a/apps/macos/Sources/Clawdis/NodeMode/MacNodeModeCoordinator.swift b/apps/macos/Sources/Clawdis/NodeMode/MacNodeModeCoordinator.swift index a4496d973..4d70e7cd9 100644 --- a/apps/macos/Sources/Clawdis/NodeMode/MacNodeModeCoordinator.swift +++ b/apps/macos/Sources/Clawdis/NodeMode/MacNodeModeCoordinator.swift @@ -228,29 +228,32 @@ final class MacNodeModeCoordinator { _ stream: AsyncStream, timeoutSeconds: Double) async throws { - 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: - return - case let .failed(err): - throw err - case .cancelled: - throw NSError(domain: "Bridge", code: 20, userInfo: [ - NSLocalizedDescriptionKey: "Connection cancelled", - ]) - default: - continue + try await AsyncTimeout.withTimeout( + seconds: timeoutSeconds, + onTimeout: { + NSError(domain: "Bridge", code: 22, userInfo: [ + NSLocalizedDescriptionKey: "operation timed out", + ]) + }, + operation: { + for await state in stream { + switch state { + case .ready: + return + case let .failed(err): + throw err + case .cancelled: + throw NSError(domain: "Bridge", code: 20, userInfo: [ + NSLocalizedDescriptionKey: "Connection cancelled", + ]) + default: + continue + } } - } - throw NSError(domain: "Bridge", code: 21, userInfo: [ - NSLocalizedDescriptionKey: "Connection closed", - ]) - } + throw NSError(domain: "Bridge", code: 21, userInfo: [ + NSLocalizedDescriptionKey: "Connection closed", + ]) + }) } private func resolveBridgeEndpoint(timeoutSeconds: Double) async -> NWEndpoint? { diff --git a/apps/macos/Sources/Clawdis/NotificationManager.swift b/apps/macos/Sources/Clawdis/NotificationManager.swift index e4095ab2e..1c0254955 100644 --- a/apps/macos/Sources/Clawdis/NotificationManager.swift +++ b/apps/macos/Sources/Clawdis/NotificationManager.swift @@ -46,7 +46,8 @@ struct NotificationManager { if Self.hasTimeSensitiveEntitlement { content.interruptionLevel = .timeSensitive } else { - self.logger.debug("time-sensitive notification requested without entitlement; falling back to active") + self.logger.debug( + "time-sensitive notification requested without entitlement; falling back to active") content.interruptionLevel = .active } } diff --git a/apps/macos/Sources/Clawdis/OnboardingView+Pages.swift b/apps/macos/Sources/Clawdis/OnboardingView+Pages.swift index 4925d6afa..7438d3d12 100644 --- a/apps/macos/Sources/Clawdis/OnboardingView+Pages.swift +++ b/apps/macos/Sources/Clawdis/OnboardingView+Pages.swift @@ -746,7 +746,9 @@ extension OnboardingView { Text("Couldn’t load skills from the Gateway.") .font(.footnote.weight(.semibold)) .foregroundStyle(.orange) - Text("Make sure the Gateway is running and connected, then hit Refresh (or open Settings → Skills).") + Text( + "Make sure the Gateway is running and connected, " + + "then hit Refresh (or open Settings → Skills).") .font(.footnote) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) diff --git a/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift b/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift index 13edfaf20..2fe702aa9 100644 --- a/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift +++ b/apps/macos/Sources/Clawdis/PeekabooBridgeHostCoordinator.swift @@ -76,7 +76,10 @@ final class PeekabooBridgeHostCoordinator { } var infoCF: CFDictionary? - guard SecCodeCopySigningInformation(staticCode, SecCSFlags(rawValue: kSecCSSigningInformation), &infoCF) == errSecSuccess, + guard SecCodeCopySigningInformation( + staticCode, + SecCSFlags(rawValue: kSecCSSigningInformation), + &infoCF) == errSecSuccess, let info = infoCF as? [String: Any] else { return nil diff --git a/apps/macos/Sources/Clawdis/TalkModeController.swift b/apps/macos/Sources/Clawdis/TalkModeController.swift index c87dd92da..388a88475 100644 --- a/apps/macos/Sources/Clawdis/TalkModeController.swift +++ b/apps/macos/Sources/Clawdis/TalkModeController.swift @@ -24,7 +24,11 @@ final class TalkModeController { self.phase = phase TalkOverlayController.shared.updatePhase(phase) let effectivePhase = self.isPaused ? "paused" : phase.rawValue - Task { await GatewayConnection.shared.talkMode(enabled: AppStateStore.shared.talkEnabled, phase: effectivePhase) } + Task { + await GatewayConnection.shared.talkMode( + enabled: AppStateStore.shared.talkEnabled, + phase: effectivePhase) + } } func updateLevel(_ level: Double) { @@ -37,7 +41,11 @@ final class TalkModeController { self.isPaused = paused TalkOverlayController.shared.updatePaused(paused) let effectivePhase = paused ? "paused" : self.phase.rawValue - Task { await GatewayConnection.shared.talkMode(enabled: AppStateStore.shared.talkEnabled, phase: effectivePhase) } + Task { + await GatewayConnection.shared.talkMode( + enabled: AppStateStore.shared.talkEnabled, + phase: effectivePhase) + } Task { await TalkModeRuntime.shared.setPaused(paused) } } diff --git a/apps/macos/Sources/Clawdis/TalkModeRuntime.swift b/apps/macos/Sources/Clawdis/TalkModeRuntime.swift index 0289c7c0a..e7866087e 100644 --- a/apps/macos/Sources/Clawdis/TalkModeRuntime.swift +++ b/apps/macos/Sources/Clawdis/TalkModeRuntime.swift @@ -337,7 +337,9 @@ actor TalkModeRuntime { let runId = UUID().uuidString let startedAt = Date().timeIntervalSince1970 self.logger.info( - "talk send start runId=\(runId, privacy: .public) session=\(sessionKey, privacy: .public) chars=\(prompt.count, privacy: .public)") + "talk send start runId=\(runId, privacy: .public) " + + "session=\(sessionKey, privacy: .public) " + + "chars=\(prompt.count, privacy: .public)") do { let response = try await GatewayConnection.shared.chatSend( @@ -348,7 +350,8 @@ actor TalkModeRuntime { attachments: []) guard self.isCurrent(gen) else { return } self.logger.info( - "talk chat.send ok runId=\(response.runId, privacy: .public) session=\(sessionKey, privacy: .public)") + "talk chat.send ok runId=\(response.runId, privacy: .public) " + + "session=\(sessionKey, privacy: .public)") guard let assistantText = await self.waitForAssistantText( sessionKey: sessionKey, @@ -433,6 +436,7 @@ actor TalkModeRuntime { } } + // swiftlint:disable:next cyclomatic_complexity function_body_length private func playAssistant(text: String) async { let gen = self.lifecycleGeneration let parse = TalkDirectiveParser.parse(text) @@ -443,7 +447,9 @@ actor TalkModeRuntime { if !parse.unknownKeys.isEmpty { self.logger - .warning("talk directive ignored keys: \(parse.unknownKeys.joined(separator: ","), privacy: .public)") + .warning( + "talk directive ignored keys: " + + "\(parse.unknownKeys.joined(separator: ","), privacy: .public)") } let requestedVoice = directive?.voiceId?.trimmingCharacters(in: .whitespacesAndNewlines) @@ -490,7 +496,9 @@ actor TalkModeRuntime { self.ttsLogger.warning("talk missing voiceId; falling back to system voice") } else if let voiceId { self.ttsLogger - .info("talk TTS request voiceId=\(voiceId, privacy: .public) chars=\(cleaned.count, privacy: .public)") + .info( + "talk TTS request voiceId=\(voiceId, privacy: .public) " + + "chars=\(cleaned.count, privacy: .public)") } self.lastSpokenText = cleaned @@ -503,7 +511,8 @@ actor TalkModeRuntime { if outputFormat == nil, !desiredOutputFormat.isEmpty { self.logger .warning( - "talk output_format unsupported for local playback: \(desiredOutputFormat, privacy: .public)") + "talk output_format unsupported for local playback: " + + "\(desiredOutputFormat, privacy: .public)") } let modelId = directive?.modelId ?? self.currentModelId ?? self.defaultModelId @@ -558,7 +567,8 @@ actor TalkModeRuntime { } self.ttsLogger .info( - "talk audio result finished=\(result.finished, privacy: .public) interruptedAt=\(String(describing: result.interruptedAt), privacy: .public)") + "talk audio result finished=\(result.finished, privacy: .public) " + + "interruptedAt=\(String(describing: result.interruptedAt), privacy: .public)") if !result.finished, result.interruptedAt == nil { throw NSError(domain: "StreamingAudioPlayer", code: 1, userInfo: [ NSLocalizedDescriptionKey: "audio playback failed", @@ -583,7 +593,9 @@ actor TalkModeRuntime { } } catch { self.ttsLogger - .error("talk TTS failed: \(error.localizedDescription, privacy: .public); falling back to system voice") + .error( + "talk TTS failed: \(error.localizedDescription, privacy: .public); " + + "falling back to system voice") do { if self.interruptOnSpeech { await self.startRecognition() @@ -717,7 +729,10 @@ actor TalkModeRuntime { let modelLabel = (cfg.modelId?.isEmpty == false) ? cfg.modelId! : "none" self.logger .info( - "talk config voiceId=\(voiceLabel, privacy: .public) modelId=\(modelLabel, privacy: .public) apiKey=\(hasApiKey, privacy: .public) interrupt=\(cfg.interruptOnSpeech, privacy: .public)") + "talk config voiceId=\(voiceLabel, privacy: .public) " + + "modelId=\(modelLabel, privacy: .public) " + + "apiKey=\(hasApiKey, privacy: .public) " + + "interrupt=\(cfg.interruptOnSpeech, privacy: .public)") } private struct TalkRuntimeConfig {