spb/zyquo-agent Public MIT
The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.
Swift 94.7%
Shell 4.1%
Python 0.7%
Makefile 0.5%
1//2// KeyVaultStore.swift3// Zyquo Agent4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// Observable wrapper around SecureKeyStore for the Settings UI: per-provider9// key presence, redacted display, and "Test" with latency. Decrypted keys are10// fetched on demand and never retained beyond the call.11//1213import Foundation1415@MainActor16final class KeyVaultStore: ObservableObject {17 enum KeyStatus: Equatable {18 case unset19 case saved // present, not yet verified this session20 case testing21 case verified(latency: TimeInterval)22 case failed(message: String)23 }2425 @Published private(set) var statuses: [ProviderID: KeyStatus] = [:]26 /// Redacted display strings (••••1234) for providers with saved keys.27 @Published private(set) var redactedKeys: [ProviderID: String] = [:]2829 private let store: SecureKeyStore3031 init(store: SecureKeyStore = SecureKeyStore()) {32 self.store = store33 refresh()34 }3536 func refresh() {37 let keys = (try? store.loadKeys()) ?? [:]38 for provider in ProviderID.builtIn {39 if let key = keys[provider.rawValue], !key.isEmpty {40 redactedKeys[provider] = SecureKeyStore.redacted(key)41 if case .verified = statuses[provider] ?? .unset {} else {42 statuses[provider] = .saved43 }44 } else {45 redactedKeys[provider] = nil46 statuses[provider] = .unset47 }48 }49 }5051 func hasKey(for provider: ProviderID) -> Bool {52 redactedKeys[provider] != nil53 }5455 /// Decrypts and returns the key — call sites use it immediately and drop it.56 func apiKey(for provider: ProviderID) throws -> String {57 guard let key = try store.key(for: provider), !key.isEmpty else {58 throw ProviderError.missingAPIKey(provider)59 }60 return key61 }6263 func setKey(_ key: String, for provider: ProviderID) {64 let trimmed = key.trimmingCharacters(in: .whitespacesAndNewlines)65 guard !trimmed.isEmpty else { return }66 try? store.setKey(trimmed, for: provider)67 statuses[provider] = .saved68 refresh()69 }7071 func deleteKey(for provider: ProviderID) {72 try? store.deleteKey(for: provider)73 statuses[provider] = .unset74 refresh()75 }7677 /// Runs the cheapest authenticated call and records latency or failure.78 func testKey(for provider: ProviderID, catalog: ModelCatalog) async {79 guard let key = try? apiKey(for: provider) else {80 statuses[provider] = .failed(message: "No key saved")81 return82 }83 statuses[provider] = .testing84 let client = ProviderRegistry.client(for: provider)85 let fallback = catalog.cheapestModel(for: provider)86 do {87 let latency = try await client.testKey(key, fallbackModel: fallback)88 statuses[provider] = .verified(latency: latency)89 } catch {90 statuses[provider] = .failed(message: error.localizedDescription)91 }92 }93}94