Files
reyna-cli/native/ReynaCLIHost/Sources/ReynaCLIHostCore/SystemInfoProvider.swift
T
Adolfo Reyna 9fd04b0ce4 feat(privacy-host): add signed native calendar contacts and reminders host
Add the owner-only AF_UNIX Reyna CLI privacy host, strict signed-app installation, and typed native routing for Calendar, Contacts, and Reminders.\n\nAdd bounded system-status paths and config-only direct local-service wrappers. Preserve MacMiniMCP pending explicit cutover approval.\n\nApple Notes is intentionally deferred: no native Notes operations, Apple Events declaration, or Automation helper are included; legacy Notes handling remains untouched.
2026-08-03 20:27:54 -04:00

82 lines
3.0 KiB
Swift

import Foundation
// MARK: - System info data models (read-only, no TCC)
struct SystemInfoItem: Codable, Equatable, Sendable {
let macos_version: String
let build: String?
let uname: String?
let hw_model: String?
let cpu_brand: String?
let is_macos_26_plus: Bool?
let speech_analyzer_expected: String?
}
struct SpeechApiStatusItem: Codable, Equatable, Sendable {
let system: SystemInfoItem
let swift_availability: [String: String]? // minimal stub
let conclusion: String
}
enum SystemProviderError: Error, Equatable, Sendable {
case unavailable(String)
}
protocol SystemInfoProviding: Sendable {
func getSystemInfo() throws -> SystemInfoItem
func getSpeechApiStatus() throws -> SpeechApiStatusItem
}
// Production provider - reads sw_vers / uname / sysctl, no permission needed
struct ProductionSystemInfoProvider: SystemInfoProviding {
func getSystemInfo() throws -> SystemInfoItem {
let ver = ProcessInfo.processInfo.operatingSystemVersion
let verString = "\(ver.majorVersion).\(ver.minorVersion).\(ver.patchVersion)"
// Best-effort hw model / cpu / uname without spawning processes in Swift? Use sysctl/mib.
var hwModel: String? = nil
var cpuBrand: String? = nil
var unameStr: String? = nil
// Use ProcessInfo hostName as fallback for minimal
// For hw.model, use sysctlbyname where possible - but keep simple fallback to avoid C interop complexity
// We'll try reading via sysctl nametable via Foundation
#if os(macOS)
hwModel = sysctlString("hw.model")
cpuBrand = sysctlString("machdep.cpu.brand_string")
#endif
let m = ver.majorVersion
let is26 = m >= 26
let expected = is26 ? "likely available (macOS 26+)" : "not available - requires macOS 26+"
return SystemInfoItem(
macos_version: verString,
build: nil,
uname: unameStr,
hw_model: hwModel,
cpu_brand: cpuBrand,
is_macos_26_plus: is26,
speech_analyzer_expected: expected
)
}
func getSpeechApiStatus() throws -> SpeechApiStatusItem {
let info = try getSystemInfo()
let major = ProcessInfo.processInfo.operatingSystemVersion.majorVersion
let conclusion: String
if major >= 26 {
conclusion = "macOS 26+ detected — SpeechAnalyzer/SpeechTranscriber should be available per Apple docs."
} else {
conclusion = "macOS \(info.macos_version) detected — SpeechAnalyzer requires macOS 26+."
}
return SpeechApiStatusItem(system: info, swift_availability: nil, conclusion: conclusion)
}
private func sysctlString(_ name: String) -> String? {
var size = 0
let rc1 = sysctlbyname(name, nil, &size, nil, 0)
if rc1 != 0 { return nil }
var buffer = [CChar](repeating: 0, count: size)
let rc2 = sysctlbyname(name, &buffer, &size, nil, 0)
if rc2 != 0 { return nil }
return String(cString: buffer)
}
}