diff --git a/apps/macos/Sources/Clawdbot/GatewayEndpointStore.swift b/apps/macos/Sources/Clawdbot/GatewayEndpointStore.swift index f89ff27eb..9335146ed 100644 --- a/apps/macos/Sources/Clawdbot/GatewayEndpointStore.swift +++ b/apps/macos/Sources/Clawdbot/GatewayEndpointStore.swift @@ -172,6 +172,10 @@ actor GatewayEndpointStore { return configToken } + if isRemote { + return nil + } + if let token = launchdSnapshot?.token?.trimmingCharacters(in: .whitespacesAndNewlines), !token.isEmpty { diff --git a/apps/macos/Tests/ClawdbotIPCTests/ChannelsSettingsSmokeTests.swift b/apps/macos/Tests/ClawdbotIPCTests/ChannelsSettingsSmokeTests.swift index c84ba0ba0..2b1eced84 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/ChannelsSettingsSmokeTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/ChannelsSettingsSmokeTests.swift @@ -3,6 +3,8 @@ import SwiftUI import Testing @testable import Clawdbot +private typealias SnapshotAnyCodable = Clawdbot.AnyCodable + @Suite(.serialized) @MainActor struct ChannelsSettingsSmokeTests { @@ -17,8 +19,11 @@ struct ChannelsSettingsSmokeTests { "signal": "Signal", "imessage": "iMessage", ], + channelDetailLabels: nil, + channelSystemImages: nil, + channelMeta: nil, channels: [ - "whatsapp": AnyCodable([ + "whatsapp": SnapshotAnyCodable([ "configured": true, "linked": true, "authAgeMs": 86_400_000, @@ -37,7 +42,7 @@ struct ChannelsSettingsSmokeTests { "lastEventAt": 1_700_000_060_000, "lastError": "needs login", ]), - "telegram": AnyCodable([ + "telegram": SnapshotAnyCodable([ "configured": true, "tokenSource": "env", "running": true, @@ -52,7 +57,7 @@ struct ChannelsSettingsSmokeTests { ], "lastProbeAt": 1_700_000_050_000, ]), - "signal": AnyCodable([ + "signal": SnapshotAnyCodable([ "configured": true, "baseUrl": "http://127.0.0.1:8080", "running": true, @@ -65,7 +70,7 @@ struct ChannelsSettingsSmokeTests { ], "lastProbeAt": 1_700_000_050_000, ]), - "imessage": AnyCodable([ + "imessage": SnapshotAnyCodable([ "configured": false, "running": false, "lastError": "not configured", @@ -100,15 +105,18 @@ struct ChannelsSettingsSmokeTests { "signal": "Signal", "imessage": "iMessage", ], + channelDetailLabels: nil, + channelSystemImages: nil, + channelMeta: nil, channels: [ - "whatsapp": AnyCodable([ + "whatsapp": SnapshotAnyCodable([ "configured": false, "linked": false, "running": false, "connected": false, "reconnectAttempts": 0, ]), - "telegram": AnyCodable([ + "telegram": SnapshotAnyCodable([ "configured": false, "running": false, "lastError": "bot missing", @@ -120,7 +128,7 @@ struct ChannelsSettingsSmokeTests { ], "lastProbeAt": 1_700_000_100_000, ]), - "signal": AnyCodable([ + "signal": SnapshotAnyCodable([ "configured": false, "baseUrl": "http://127.0.0.1:8080", "running": false, @@ -133,7 +141,7 @@ struct ChannelsSettingsSmokeTests { ], "lastProbeAt": 1_700_000_200_000, ]), - "imessage": AnyCodable([ + "imessage": SnapshotAnyCodable([ "configured": false, "running": false, "lastError": "not configured", diff --git a/apps/macos/Tests/ClawdbotIPCTests/CronJobEditorSmokeTests.swift b/apps/macos/Tests/ClawdbotIPCTests/CronJobEditorSmokeTests.swift index 02e8c1e69..1a0fbf786 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/CronJobEditorSmokeTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/CronJobEditorSmokeTests.swift @@ -11,16 +11,19 @@ struct CronJobEditorSmokeTests { } @Test func cronJobEditorBuildsBodyForNewJob() { + let channelsStore = ChannelsStore(isPreview: true) let view = CronJobEditor( job: nil, isSaving: .constant(false), error: .constant(nil), + channelsStore: channelsStore, onCancel: {}, onSave: { _ in }) _ = view.body } @Test func cronJobEditorBuildsBodyForExistingJob() { + let channelsStore = ChannelsStore(isPreview: true) let job = CronJob( id: "job-1", agentId: "ops", @@ -54,31 +57,36 @@ struct CronJobEditorSmokeTests { job: job, isSaving: .constant(false), error: .constant(nil), + channelsStore: channelsStore, onCancel: {}, onSave: { _ in }) _ = view.body } @Test func cronJobEditorExercisesBuilders() { + let channelsStore = ChannelsStore(isPreview: true) var view = CronJobEditor( job: nil, isSaving: .constant(false), error: .constant(nil), + channelsStore: channelsStore, onCancel: {}, onSave: { _ in }) view.exerciseForTesting() } @Test func cronJobEditorIncludesDeleteAfterRunForAtSchedule() throws { + let channelsStore = ChannelsStore(isPreview: true) let view = CronJobEditor( job: nil, isSaving: .constant(false), error: .constant(nil), + channelsStore: channelsStore, onCancel: {}, onSave: { _ in }) var root: [String: Any] = [:] - view.applyDeleteAfterRun(to: &root, scheduleKind: .at, deleteAfterRun: true) + view.applyDeleteAfterRun(to: &root, scheduleKind: CronJobEditor.ScheduleKind.at, deleteAfterRun: true) let raw = root["deleteAfterRun"] as? Bool #expect(raw == true) } diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConfigureTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConfigureTests.swift index 22c83d4fd..8ae9c3c0d 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConfigureTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConfigureTests.swift @@ -1,3 +1,4 @@ +import ClawdbotKit import Foundation import os import Testing diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConnectTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConnectTests.swift index 5b1892d80..624392cb9 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConnectTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelConnectTests.swift @@ -1,3 +1,4 @@ +import ClawdbotKit import Foundation import os import Testing diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelRequestTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelRequestTests.swift index d231bed4b..1e7a1740e 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelRequestTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelRequestTests.swift @@ -1,3 +1,4 @@ +import ClawdbotKit import Foundation import os import Testing diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelShutdownTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelShutdownTests.swift index 0f1560cbd..be6ab91f8 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelShutdownTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayChannelShutdownTests.swift @@ -1,3 +1,4 @@ +import ClawdbotKit import Foundation import os import Testing diff --git a/apps/macos/Tests/ClawdbotIPCTests/GatewayConnectionControlTests.swift b/apps/macos/Tests/ClawdbotIPCTests/GatewayConnectionControlTests.swift index 83d01b96f..a7b1c857b 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/GatewayConnectionControlTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/GatewayConnectionControlTests.swift @@ -1,3 +1,4 @@ +import ClawdbotKit import Foundation import Testing @testable import Clawdbot diff --git a/apps/macos/Tests/ClawdbotIPCTests/LowCoverageHelperTests.swift b/apps/macos/Tests/ClawdbotIPCTests/LowCoverageHelperTests.swift index 7d934d6e0..16d919225 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/LowCoverageHelperTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/LowCoverageHelperTests.swift @@ -7,15 +7,17 @@ import Testing @Suite(.serialized) struct LowCoverageHelperTests { + private typealias ProtoAnyCodable = ClawdbotProtocol.AnyCodable + @Test func anyCodableHelperAccessors() throws { - let payload: [String: AnyCodable] = [ - "title": AnyCodable("Hello"), - "flag": AnyCodable(true), - "count": AnyCodable(3), - "ratio": AnyCodable(1.25), - "list": AnyCodable([AnyCodable("a"), AnyCodable(2)]), + let payload: [String: ProtoAnyCodable] = [ + "title": ProtoAnyCodable("Hello"), + "flag": ProtoAnyCodable(true), + "count": ProtoAnyCodable(3), + "ratio": ProtoAnyCodable(1.25), + "list": ProtoAnyCodable([ProtoAnyCodable("a"), ProtoAnyCodable(2)]), ] - let any = AnyCodable(payload) + let any = ProtoAnyCodable(payload) let dict = try #require(any.dictionaryValue) #expect(dict["title"]?.stringValue == "Hello") #expect(dict["flag"]?.boolValue == true) @@ -76,31 +78,27 @@ struct LowCoverageHelperTests { #expect(result.stderr.contains("stderr-1999")) } - @Test func pairedNodesStorePersists() async throws { - let dir = FileManager().temporaryDirectory - .appendingPathComponent("paired-\(UUID().uuidString)", isDirectory: true) - try FileManager().createDirectory(at: dir, withIntermediateDirectories: true) - let url = dir.appendingPathComponent("nodes.json") - let store = PairedNodesStore(fileURL: url) - await store.load() - #expect(await store.all().isEmpty) - - let node = PairedNode( + @Test func nodeInfoCodableRoundTrip() throws { + let info = NodeInfo( nodeId: "node-1", displayName: "Node One", platform: "macOS", version: "1.0", + coreVersion: "1.0-core", + uiVersion: "1.0-ui", deviceFamily: "Mac", modelIdentifier: "MacBookPro", - token: "token", - createdAtMs: 1, - lastSeenAtMs: nil) - try await store.upsert(node) - #expect(await store.find(nodeId: "node-1")?.displayName == "Node One") - - try await store.touchSeen(nodeId: "node-1") - let updated = await store.find(nodeId: "node-1") - #expect(updated?.lastSeenAtMs != nil) + remoteIp: "192.168.1.2", + caps: ["chat"], + commands: ["send"], + permissions: ["send": true], + paired: true, + connected: false) + let data = try JSONEncoder().encode(info) + let decoded = try JSONDecoder().decode(NodeInfo.self, from: data) + #expect(decoded.nodeId == "node-1") + #expect(decoded.isPaired == true) + #expect(decoded.isConnected == false) } @Test @MainActor func presenceReporterHelpers() { diff --git a/apps/macos/Tests/ClawdbotIPCTests/MacGatewayChatTransportMappingTests.swift b/apps/macos/Tests/ClawdbotIPCTests/MacGatewayChatTransportMappingTests.swift index 00cff847b..3e24dc5cb 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/MacGatewayChatTransportMappingTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/MacGatewayChatTransportMappingTests.swift @@ -21,6 +21,7 @@ import Testing features: [:], snapshot: snapshot, canvashosturl: nil, + auth: nil, policy: [:]) let mapped = MacGatewayChatTransport.mapPushToTransportEvent(.snapshot(hello)) diff --git a/apps/macos/Tests/ClawdbotIPCTests/OnboardingWizardStepViewTests.swift b/apps/macos/Tests/ClawdbotIPCTests/OnboardingWizardStepViewTests.swift index 7fd9b9929..3ce965325 100644 --- a/apps/macos/Tests/ClawdbotIPCTests/OnboardingWizardStepViewTests.swift +++ b/apps/macos/Tests/ClawdbotIPCTests/OnboardingWizardStepViewTests.swift @@ -3,13 +3,15 @@ import SwiftUI import Testing @testable import Clawdbot +private typealias ProtoAnyCodable = ClawdbotProtocol.AnyCodable + @Suite(.serialized) @MainActor struct OnboardingWizardStepViewTests { @Test func noteStepBuilds() { let step = WizardStep( id: "step-1", - type: AnyCodable("note"), + type: ProtoAnyCodable("note"), title: "Welcome", message: "Hello", options: nil, @@ -22,17 +24,17 @@ struct OnboardingWizardStepViewTests { } @Test func selectStepBuilds() { - let options: [[String: AnyCodable]] = [ - ["value": AnyCodable("local"), "label": AnyCodable("Local"), "hint": AnyCodable("This Mac")], - ["value": AnyCodable("remote"), "label": AnyCodable("Remote")], + let options: [[String: ProtoAnyCodable]] = [ + ["value": ProtoAnyCodable("local"), "label": ProtoAnyCodable("Local"), "hint": ProtoAnyCodable("This Mac")], + ["value": ProtoAnyCodable("remote"), "label": ProtoAnyCodable("Remote")], ] let step = WizardStep( id: "step-2", - type: AnyCodable("select"), + type: ProtoAnyCodable("select"), title: "Mode", message: "Choose a mode", options: options, - initialvalue: AnyCodable("local"), + initialvalue: ProtoAnyCodable("local"), placeholder: nil, sensitive: nil, executor: nil) diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift index a40a864b7..813af5c0a 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotKit/GatewayChannel.swift @@ -15,6 +15,9 @@ extension URLSessionWebSocketTask: WebSocketTasking {} public struct WebSocketTaskBox: @unchecked Sendable { public let task: any WebSocketTasking + public init(task: any WebSocketTasking) { + self.task = task + } public var state: URLSessionTask.State { self.task.state }