SPB Git

spb/zyquo-router Public MIT

One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).

Swift 95.7% Python 2.3% Shell 1.2% Makefile 0.9%
3.4 KB · 82 lines swift
Raw Blame History
1//2//  Main.swift3//  Zyquo Router4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//8//  Entry point. CLI modes (verification harness, vault seeding) hang off this9//  in later phases; otherwise the SwiftUI app launches.10//11//  Deliberately a synchronous main: launching NSApplicationMain from an async12//  main() corrupts Swift concurrency's executor setup (background tasks stop13//  being scheduled), so CLI modes must drive their async work explicitly.14//1516import Foundation1718@main19enum Main {20    static func main() {21        let arguments = CommandLine.arguments22        if arguments.contains("--load-vault") {23            loadVaultFromEnvironment()24            exit(0)25        }26        if let flagIndex = arguments.firstIndex(of: "--serve") {27            // Headless mode for scripted verification (`ZyquoRouter --serve [port]`).28            let port = arguments.indices.contains(flagIndex + 1) ? Int(arguments[flagIndex + 1]) ?? 8787 : 878729            Task.detached {30                let localKeys = PersistenceService.shared.load([APIKeyRecord].self, from: "local-keys.json") ?? []31                let routerConfig = PersistenceService.shared.load(RouterConfig.self, from: RouterConfig.fileName) ?? RouterConfig()32                let routes = Routes(33                    router: RequestRouter(34                        aliases: routerConfig.aliases,35                        disabledIDs: routerConfig.disabledIDs,36                        fallbackChains: routerConfig.fallbackChains37                    ),38                    auth: AuthMiddleware(keys: localKeys)39                )40                let server = HTTPServer(host: "127.0.0.1", port: port) { request in41                    await routes.handle(request)42                }43                do {44                    try await server.run {45                        print("Zyquo Router serving on http://127.0.0.1:\(port)/v1")46                    }47                    exit(0)48                } catch {49                    FileHandle.standardError.write(Data("\(error.localizedDescription)\n".utf8))50                    exit(1)51                }52            }53            // Park the main thread servicing the main queue so MainActor work54            // can run (a blocking semaphore here would deadlock).55            dispatchMain()56        }57        ZyquoRouterApp.main()58    }5960    /// Seeds the encrypted vault from environment variables (testing/setup).61    /// Key values are never printed — only which providers were stored.62    private static func loadVaultFromEnvironment() {63        let mapping: [(ProviderID, String)] = [64            (.openai, "OPENAI_API_KEY"), (.anthropic, "ANTHROPIC_API_KEY"),65            (.xai, "XAI_API_KEY"), (.mistral, "MISTRAL_API_KEY"),66            (.gemini, "GEMINI_API_KEY"), (.qwen, "DASHSCOPE_API_KEY"),67            (.deepseek, "DEEPSEEK_API_KEY"), (.kimi, "KIMI_API_KEY"),68            (.perplexity, "PERPLEXITY_API_KEY"), (.together, "TOGETHER_API_KEY"),69            (.deepinfra, "DEEPINFRA_API_KEY"), (.cerebras, "CEREBRAS_API_KEY"),70        ]71        let store = SecureKeyStore()72        var stored: [String] = []73        for (provider, variable) in mapping {74            if let value = ProcessInfo.processInfo.environment[variable], !value.isEmpty {75                try? store.setKey(value, for: provider)76                stored.append(provider.rawValue)77            }78        }79        print("Vault updated (\(stored.count) providers): \(stored.joined(separator: ", "))")80    }81}82