From f37b1c11e068025c45d2dd1db0a39eb86540eb27 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 20:12:50 +0000 Subject: [PATCH] refactor(macos): centralize presence system info --- .../Sources/OpenClaw/InstancesStore.swift | 49 +--------------- .../Sources/OpenClaw/PresenceReporter.swift | 58 +++---------------- .../Sources/OpenClaw/SystemPresenceInfo.swift | 51 ++++++++++++++++ 3 files changed, 60 insertions(+), 98 deletions(-) create mode 100644 apps/macos/Sources/OpenClaw/SystemPresenceInfo.swift diff --git a/apps/macos/Sources/OpenClaw/InstancesStore.swift b/apps/macos/Sources/OpenClaw/InstancesStore.swift index 929f12c1699..566340337db 100644 --- a/apps/macos/Sources/OpenClaw/InstancesStore.swift +++ b/apps/macos/Sources/OpenClaw/InstancesStore.swift @@ -158,7 +158,7 @@ final class InstancesStore { private func localFallbackInstance(reason: String) -> InstanceInfo { let host = Host.current().localizedName ?? "this-mac" - let ip = Self.primaryIPv4Address() + let ip = SystemPresenceInfo.primaryIPv4Address() let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String let osVersion = ProcessInfo.processInfo.operatingSystemVersion let platform = "macos \(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" @@ -172,58 +172,13 @@ final class InstancesStore { platform: platform, deviceFamily: "Mac", modelIdentifier: InstanceIdentity.modelIdentifier, - lastInputSeconds: Self.lastInputSeconds(), + lastInputSeconds: SystemPresenceInfo.lastInputSeconds(), mode: "local", reason: reason, text: text, ts: ts) } - private static func lastInputSeconds() -> Int? { - let anyEvent = CGEventType(rawValue: UInt32.max) ?? .null - let seconds = CGEventSource.secondsSinceLastEventType(.combinedSessionState, eventType: anyEvent) - if seconds.isNaN || seconds.isInfinite || seconds < 0 { return nil } - return Int(seconds.rounded()) - } - - private static func primaryIPv4Address() -> String? { - var addrList: UnsafeMutablePointer? - guard getifaddrs(&addrList) == 0, let first = addrList else { return nil } - defer { freeifaddrs(addrList) } - - var fallback: String? - var en0: String? - - for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) { - let flags = Int32(ptr.pointee.ifa_flags) - let isUp = (flags & IFF_UP) != 0 - let isLoopback = (flags & IFF_LOOPBACK) != 0 - let name = String(cString: ptr.pointee.ifa_name) - let family = ptr.pointee.ifa_addr.pointee.sa_family - if !isUp || isLoopback || family != UInt8(AF_INET) { continue } - - var addr = ptr.pointee.ifa_addr.pointee - var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) - let result = getnameinfo( - &addr, - socklen_t(ptr.pointee.ifa_addr.pointee.sa_len), - &buffer, - socklen_t(buffer.count), - nil, - 0, - NI_NUMERICHOST) - guard result == 0 else { continue } - let len = buffer.prefix { $0 != 0 } - let bytes = len.map { UInt8(bitPattern: $0) } - guard let ip = String(bytes: bytes, encoding: .utf8) else { continue } - - if name == "en0" { en0 = ip; break } - if fallback == nil { fallback = ip } - } - - return en0 ?? fallback - } - // MARK: - Helpers /// Keep the last raw payload for logging. diff --git a/apps/macos/Sources/OpenClaw/PresenceReporter.swift b/apps/macos/Sources/OpenClaw/PresenceReporter.swift index 16d70b8a92c..2e7a1d4c472 100644 --- a/apps/macos/Sources/OpenClaw/PresenceReporter.swift +++ b/apps/macos/Sources/OpenClaw/PresenceReporter.swift @@ -1,5 +1,4 @@ import Cocoa -import Darwin import Foundation import OSLog @@ -33,10 +32,10 @@ final class PresenceReporter { private func push(reason: String) async { let mode = await MainActor.run { AppStateStore.shared.connectionMode.rawValue } let host = InstanceIdentity.displayName - let ip = Self.primaryIPv4Address() ?? "ip-unknown" + let ip = SystemPresenceInfo.primaryIPv4Address() ?? "ip-unknown" let version = Self.appVersionString() let platform = Self.platformString() - let lastInput = Self.lastInputSeconds() + let lastInput = SystemPresenceInfo.lastInputSeconds() let text = Self.composePresenceSummary(mode: mode, reason: reason) var params: [String: AnyHashable] = [ "instanceId": AnyHashable(self.instanceId), @@ -64,9 +63,9 @@ final class PresenceReporter { private static func composePresenceSummary(mode: String, reason: String) -> String { let host = InstanceIdentity.displayName - let ip = Self.primaryIPv4Address() ?? "ip-unknown" + let ip = SystemPresenceInfo.primaryIPv4Address() ?? "ip-unknown" let version = Self.appVersionString() - let lastInput = Self.lastInputSeconds() + let lastInput = SystemPresenceInfo.lastInputSeconds() let lastLabel = lastInput.map { "last input \($0)s ago" } ?? "last input unknown" return "Node: \(host) (\(ip)) · app \(version) · \(lastLabel) · mode \(mode) · reason \(reason)" } @@ -87,50 +86,7 @@ final class PresenceReporter { return "macos \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)" } - private static func lastInputSeconds() -> Int? { - let anyEvent = CGEventType(rawValue: UInt32.max) ?? .null - let seconds = CGEventSource.secondsSinceLastEventType(.combinedSessionState, eventType: anyEvent) - if seconds.isNaN || seconds.isInfinite || seconds < 0 { return nil } - return Int(seconds.rounded()) - } - - private static func primaryIPv4Address() -> String? { - var addrList: UnsafeMutablePointer? - guard getifaddrs(&addrList) == 0, let first = addrList else { return nil } - defer { freeifaddrs(addrList) } - - var fallback: String? - var en0: String? - - for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) { - let flags = Int32(ptr.pointee.ifa_flags) - let isUp = (flags & IFF_UP) != 0 - let isLoopback = (flags & IFF_LOOPBACK) != 0 - let name = String(cString: ptr.pointee.ifa_name) - let family = ptr.pointee.ifa_addr.pointee.sa_family - if !isUp || isLoopback || family != UInt8(AF_INET) { continue } - - var addr = ptr.pointee.ifa_addr.pointee - var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) - let result = getnameinfo( - &addr, - socklen_t(ptr.pointee.ifa_addr.pointee.sa_len), - &buffer, - socklen_t(buffer.count), - nil, - 0, - NI_NUMERICHOST) - guard result == 0 else { continue } - let len = buffer.prefix { $0 != 0 } - let bytes = len.map { UInt8(bitPattern: $0) } - guard let ip = String(bytes: bytes, encoding: .utf8) else { continue } - - if name == "en0" { en0 = ip; break } - if fallback == nil { fallback = ip } - } - - return en0 ?? fallback - } + // (SystemPresenceInfo) last input + primary IPv4. } #if DEBUG @@ -148,11 +104,11 @@ extension PresenceReporter { } static func _testLastInputSeconds() -> Int? { - self.lastInputSeconds() + SystemPresenceInfo.lastInputSeconds() } static func _testPrimaryIPv4Address() -> String? { - self.primaryIPv4Address() + SystemPresenceInfo.primaryIPv4Address() } } #endif diff --git a/apps/macos/Sources/OpenClaw/SystemPresenceInfo.swift b/apps/macos/Sources/OpenClaw/SystemPresenceInfo.swift new file mode 100644 index 00000000000..a80f470773c --- /dev/null +++ b/apps/macos/Sources/OpenClaw/SystemPresenceInfo.swift @@ -0,0 +1,51 @@ +import CoreGraphics +import Darwin +import Foundation + +enum SystemPresenceInfo { + static func lastInputSeconds() -> Int? { + let anyEvent = CGEventType(rawValue: UInt32.max) ?? .null + let seconds = CGEventSource.secondsSinceLastEventType(.combinedSessionState, eventType: anyEvent) + if seconds.isNaN || seconds.isInfinite || seconds < 0 { return nil } + return Int(seconds.rounded()) + } + + static func primaryIPv4Address() -> String? { + var addrList: UnsafeMutablePointer? + guard getifaddrs(&addrList) == 0, let first = addrList else { return nil } + defer { freeifaddrs(addrList) } + + var fallback: String? + var en0: String? + + for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) { + let flags = Int32(ptr.pointee.ifa_flags) + let isUp = (flags & IFF_UP) != 0 + let isLoopback = (flags & IFF_LOOPBACK) != 0 + let name = String(cString: ptr.pointee.ifa_name) + let family = ptr.pointee.ifa_addr.pointee.sa_family + if !isUp || isLoopback || family != UInt8(AF_INET) { continue } + + var addr = ptr.pointee.ifa_addr.pointee + var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + let result = getnameinfo( + &addr, + socklen_t(ptr.pointee.ifa_addr.pointee.sa_len), + &buffer, + socklen_t(buffer.count), + nil, + 0, + NI_NUMERICHOST) + guard result == 0 else { continue } + let len = buffer.prefix { $0 != 0 } + let bytes = len.map { UInt8(bitPattern: $0) } + guard let ip = String(bytes: bytes, encoding: .utf8) else { continue } + + if name == "en0" { en0 = ip; break } + if fallback == nil { fallback = ip } + } + + return en0 ?? fallback + } +} +