@sriinnu/runic 2.0.0 → 2.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +256 -0
- package/LICENSE +373 -21
- package/README.md +96 -89
- package/THIRD-PARTY-NOTICES.md +651 -0
- package/package.json +14 -4
- package/.swiftformat +0 -50
- package/.swiftlint.yml +0 -160
- package/CONTRIBUTING.md +0 -456
- package/Icon.icns +0 -0
- package/Icon.icon/Assets/runic.png +0 -0
- package/Icon.icon/icon.json +0 -36
- package/Package.resolved +0 -51
- package/Package.swift +0 -136
- package/Packages/Helix/LICENSE +0 -21
- package/Packages/Helix/Package.swift +0 -29
- package/Packages/Helix/README.md +0 -976
- package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
- package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
- package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
- package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
- package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
- package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
- package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
- package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
- package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
- package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
- package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
- package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
- package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
- package/Packages/Helix/Sources/Helix/Program.swift +0 -204
- package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
- package/Packages/Silo/LICENSE +0 -21
- package/Packages/Silo/Package.swift +0 -34
- package/Packages/Silo/README.md +0 -466
- package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
- package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
- package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
- package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
- package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
- package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
- package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
- package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
- package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
- package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
- package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
- package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
- package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
- package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
- package/RuniciOS/App/RuniciOSApp.swift +0 -105
- package/RuniciOS/Core/RunicTypographyiOS.swift +0 -24
- package/RuniciOS/Core/iOSUsageStore.swift +0 -192
- package/RuniciOS/Views/ProviderDetailView.swift +0 -312
- package/RuniciOS/Views/ProviderListView.swift +0 -228
- package/RuniciOS/Widgets/LargeWidget.swift +0 -367
- package/RuniciOS/Widgets/LiveActivity.swift +0 -342
- package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
- package/RuniciOS/Widgets/MediumWidget.swift +0 -370
- package/RuniciOS/Widgets/RunicWidget.swift +0 -274
- package/RuniciOS/Widgets/SmallWidget.swift +0 -281
- package/RuniciOS/Widgets/WidgetModels.swift +0 -285
- package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
- package/Scripts/CREATION-SUMMARY.md +0 -367
- package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
- package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
- package/Scripts/INDEX.md +0 -365
- package/Scripts/QUICK-REFERENCE.md +0 -164
- package/Scripts/README-SCRIPTS.md +0 -926
- package/Scripts/build-android.sh +0 -526
- package/Scripts/build-ios.sh +0 -529
- package/Scripts/build-macos.sh +0 -603
- package/Scripts/build-windows.sh +0 -504
- package/Scripts/build_icon.sh +0 -49
- package/Scripts/changelog-to-html.sh +0 -89
- package/Scripts/check-release-assets.sh +0 -10
- package/Scripts/ci-config.yml +0 -474
- package/Scripts/compile_and_run.sh +0 -219
- package/Scripts/compile_and_run_adhoc.sh +0 -4
- package/Scripts/docs-list.mjs +0 -119
- package/Scripts/make_appcast.sh +0 -58
- package/Scripts/package_app.sh +0 -409
- package/Scripts/release-lib.sh +0 -214
- package/Scripts/release.sh +0 -76
- package/Scripts/setup-all.sh +0 -387
- package/Scripts/setup-ios.sh +0 -404
- package/Scripts/setup-react-native.sh +0 -523
- package/Scripts/sign-and-notarize.sh +0 -122
- package/Scripts/test_live_update.sh +0 -38
- package/Scripts/validate_changelog.sh +0 -23
- package/Scripts/verify_appcast.sh +0 -92
- package/Sources/Runic/About.swift +0 -93
- package/Sources/Runic/AppNotifications.swift +0 -117
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -670
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -519
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -824
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -90
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1412
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -339
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -423
- package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
- package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -81
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1222
- package/Sources/Runic/Core/Rendering/IconView.swift +0 -122
- package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
- package/Sources/Runic/Core/RunicAnimation.swift +0 -53
- package/Sources/Runic/Core/RunicColors.swift +0 -73
- package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
- package/Sources/Runic/Core/RunicSpacing.swift +0 -68
- package/Sources/Runic/Core/RunicTypography.swift +0 -206
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1749
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
- package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3631
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -117
- package/Sources/Runic/Core/UsageExporter.swift +0 -197
- package/Sources/Runic/Date+RelativeDescription.swift +0 -21
- package/Sources/Runic/HiddenWindowView.swift +0 -32
- package/Sources/Runic/InstallOrigin.swift +0 -9
- package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
- package/Sources/Runic/MouseLocationReader.swift +0 -65
- package/Sources/Runic/Notifications+Runic.swift +0 -6
- package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
- package/Sources/Runic/ProviderRegistry.swift +0 -100
- package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
- package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
- package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
- package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
- package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -51
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
- package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
- package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
- package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
- package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
- package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
- package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
- package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
- package/Sources/Runic/Resources/provider-context-windows.json +0 -20
- package/Sources/Runic/RunicApp.swift +0 -367
- package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
- package/Sources/Runic/UsagePaceText.swift +0 -52
- package/Sources/Runic/UsageProgressBar.swift +0 -228
- package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
- package/Sources/Runic/Utilities/LoadingPattern.swift +0 -62
- package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -254
- package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -311
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -157
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -63
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -248
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
- package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1889
- package/Sources/Runic/Views/Menu/MenuContent.swift +0 -153
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -371
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -35
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -241
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -281
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -86
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -369
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -191
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -621
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -306
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -418
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -637
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -321
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -250
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -572
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -291
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3480
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -29
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -61
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -213
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -376
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
- package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -762
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
- package/Sources/RunicCLI/CLIEntry.swift +0 -983
- package/Sources/RunicCLI/CLIRenderer.swift +0 -87
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -363
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -453
- package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -430
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -167
- package/Sources/RunicClaudeWatchdog/main.swift +0 -122
- package/Sources/RunicClaudeWebProbe/main.swift +0 -62
- package/Sources/RunicCore/AlertRuleStore.swift +0 -187
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
- package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
- package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
- package/Sources/RunicCore/CostUsageModels.swift +0 -479
- package/Sources/RunicCore/CreditsModels.swift +0 -50
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -268
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -631
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -444
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
- package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
- package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
- package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -370
- package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
- package/Sources/RunicCore/PathEnvironment.swift +0 -482
- package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -426
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
- package/Sources/RunicCore/ProjectBudgetStore.swift +0 -102
- package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -68
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -164
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -209
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -30
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -245
- package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -26
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -183
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
- package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -117
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -293
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -385
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -839
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -97
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -216
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
- package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
- package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
- package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
- package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
- package/Sources/RunicCore/Providers/Providers.swift +0 -184
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -86
- package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -328
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -74
- package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -745
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -384
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -307
- package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -287
- package/Sources/RunicCore/Sync/SyncRecord.swift +0 -402
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -288
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -768
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -338
- package/Sources/RunicCore/Team/TeamModels.swift +0 -584
- package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
- package/Sources/RunicCore/Team/TeamStore.swift +0 -809
- package/Sources/RunicCore/TextParsing.swift +0 -55
- package/Sources/RunicCore/UsageFetcher.swift +0 -694
- package/Sources/RunicCore/UsageFormatter.swift +0 -410
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -441
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -502
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -161
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
- package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -675
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
- package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -284
- package/Sources/RunicCore/UsagePace.swift +0 -81
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
- package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
- package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
- package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
- package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
- package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
- package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
- package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
- package/Tests/RunicTests/AppDelegateTests.swift +0 -48
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -21
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -69
- package/Tests/RunicTests/CLICostTests.swift +0 -64
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -21
- package/Tests/RunicTests/CLISnapshotTests.swift +0 -71
- package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -57
- package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -163
- package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -29
- package/Tests/RunicTests/ClaudeUsageTests.swift +0 -369
- package/Tests/RunicTests/CodexOAuthTests.swift +0 -121
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -80
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -74
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -74
- package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -392
- package/Tests/RunicTests/CostUsagePricingTests.swift +0 -48
- package/Tests/RunicTests/CostUsageScannerTests.swift +0 -698
- package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -322
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -67
- package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -27
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -232
- package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -72
- package/Tests/RunicTests/InstallOriginTests.swift +0 -17
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
- package/Tests/RunicTests/LiveAccountTests.swift +0 -24
- package/Tests/RunicTests/LoadingPatternTests.swift +0 -34
- package/Tests/RunicTests/MenuCardModelTests.swift +0 -544
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -67
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -110
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -16
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -74
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -26
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -133
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -64
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -131
- package/Tests/RunicTests/PathBuilderTests.swift +0 -236
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -159
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -37
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -15
- package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -236
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -50
- package/Tests/RunicTests/RunicTests.swift +0 -119
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -35
- package/Tests/RunicTests/SettingsStoreTests.swift +0 -265
- package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -54
- package/Tests/RunicTests/StatusMenuTests.swift +0 -475
- package/Tests/RunicTests/StatusProbeTests.swift +0 -376
- package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -18
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -64
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -147
- package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -157
- package/Tests/RunicTests/TextParsingTests.swift +0 -11
- package/Tests/RunicTests/UsageFormatterTests.swift +0 -111
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -170
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -211
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -81
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -168
- package/Tests/RunicTests/UsagePaceTests.swift +0 -76
- package/Tests/RunicTests/UsagePaceTextTests.swift +0 -108
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -89
- package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -43
- package/Tests/RunicTests/ZaiProviderTests.swift +0 -59
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -9
- package/TestsLinux/PlatformGatingTests.swift +0 -46
- package/appcast.xml +0 -104
- package/assets/logo-concept-stack.svg +0 -24
- package/assets/logo-minimal-bars.svg +0 -15
- package/assets/logo.svg +0 -20
- package/assets/logo2.svg +0 -43
- package/bin/docs-list +0 -5
- package/build-and-run.sh +0 -369
- package/docs/.nojekyll +0 -1
- package/docs/CNAME +0 -1
- package/docs/antigravity.md +0 -75
- package/docs/architecture.md +0 -32
- package/docs/claude.md +0 -93
- package/docs/cli-commands/integration_guide.md +0 -371
- package/docs/cli-commands/quick_reference.md +0 -362
- package/docs/cli-commands/readme.md +0 -361
- package/docs/cli.md +0 -156
- package/docs/cloudkit_sync_guide.md +0 -495
- package/docs/codebase_review.md +0 -288
- package/docs/codex-oauth.md +0 -635
- package/docs/codex.md +0 -104
- package/docs/codexbar.png +0 -0
- package/docs/copilot.md +0 -44
- package/docs/cursor.md +0 -46
- package/docs/design/designcomparisonchart.md +0 -510
- package/docs/design/implementationguide.md +0 -792
- package/docs/design/menubaricondesignconcepts.md +0 -796
- package/docs/design/mockups/hybridiconconcept.svg +0 -62
- package/docs/design/mockups/infinityiconconcept.svg +0 -41
- package/docs/design/mockups/infinityiconstates.svg +0 -129
- package/docs/design/mockups/wavelogorefined.svg +0 -48
- package/docs/design/quick_summary.md +0 -426
- package/docs/design/readme.md +0 -478
- package/docs/design/sizespecifications.md +0 -575
- package/docs/design-system.md +0 -836
- package/docs/factory.md +0 -103
- package/docs/gemini.md +0 -73
- package/docs/icon.md +0 -37
- package/docs/icon.png +0 -0
- package/docs/index.html +0 -175
- package/docs/keychain_fix.md +0 -274
- package/docs/minimax.md +0 -71
- package/docs/packaging.md +0 -25
- package/docs/progress-bar-improvements.md +0 -240
- package/docs/progress-bar-quick-reference.md +0 -283
- package/docs/progress-bar-technical-details.md +0 -432
- package/docs/progress-bar-visual-test.swift +0 -375
- package/docs/provider.md +0 -202
- package/docs/providers.md +0 -86
- package/docs/refactor/macros.md +0 -35
- package/docs/refresh-loop.md +0 -22
- package/docs/releasing-homebrew.md +0 -35
- package/docs/releasing.md +0 -121
- package/docs/research/concrete_todo_2026-02-22.md +0 -47
- package/docs/research/core-feature-research.md +0 -248
- package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
- package/docs/research/parity_matrix.md +0 -49
- package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
- package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
- package/docs/research/release_gate_checklist.md +0 -57
- package/docs/site.css +0 -505
- package/docs/sparkle.md +0 -28
- package/docs/status.md +0 -28
- package/docs/sync/implementation.md +0 -393
- package/docs/sync/readme.md +0 -317
- package/docs/sync_files_summary.md +0 -391
- package/docs/sync_quick_reference.md +0 -225
- package/docs/todo.md +0 -11
- package/docs/ui.md +0 -29
- package/docs/web-integration.md +0 -9
- package/docs/widgets.md +0 -85
- package/docs/workflows/error-handling-improvements.md +0 -434
- package/docs/workflows/folder_structure.md +0 -299
- package/docs/workflows/loading_states.md +0 -446
- package/docs/workflows/loading_states_examples.md +0 -565
- package/docs/workflows/loading_states_flow.md +0 -430
- package/docs/workflows/performance_optimizations.md +0 -457
- package/docs/workflows/phase1-cli-testing-report.md +0 -313
- package/docs/workflows/quick_reference.md +0 -228
- package/docs/workflows/readme.md +0 -59
- package/docs/workflows/recent_improvements.md +0 -275
- package/docs/workflows/sync_enablement_summary.md +0 -400
- package/docs/zai.md +0 -43
- package/runic.png +0 -0
- package/runic.svg +0 -15
- package/version.env +0 -2
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
|
|
4
|
-
// MARK: - Errors
|
|
5
|
-
|
|
6
|
-
enum CustomProviderStoreError: LocalizedError {
|
|
7
|
-
case providerNotFound(id: String)
|
|
8
|
-
case duplicateProvider(id: String)
|
|
9
|
-
case invalidData
|
|
10
|
-
case fileSystemError(String)
|
|
11
|
-
|
|
12
|
-
var errorDescription: String? {
|
|
13
|
-
switch self {
|
|
14
|
-
case let .providerNotFound(id):
|
|
15
|
-
"Provider with ID '\(id)' not found."
|
|
16
|
-
case let .duplicateProvider(id):
|
|
17
|
-
"Provider with ID '\(id)' already exists."
|
|
18
|
-
case .invalidData:
|
|
19
|
-
"Invalid provider data."
|
|
20
|
-
case let .fileSystemError(message):
|
|
21
|
-
"File system error: \(message)"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// MARK: - Store
|
|
27
|
-
|
|
28
|
-
public enum CustomProviderStore {
|
|
29
|
-
private static let log = RunicLog.logger("custom-provider-store")
|
|
30
|
-
|
|
31
|
-
// MARK: - Storage Location
|
|
32
|
-
|
|
33
|
-
private static var storageURL: URL {
|
|
34
|
-
let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
35
|
-
let runicDir = appSupport.appendingPathComponent("Runic", isDirectory: true)
|
|
36
|
-
try? FileManager.default.createDirectory(at: runicDir, withIntermediateDirectories: true)
|
|
37
|
-
return runicDir.appendingPathComponent("custom-providers.json")
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// MARK: - Load/Save
|
|
41
|
-
|
|
42
|
-
/// Load all custom providers from disk.
|
|
43
|
-
public static func load() -> CustomProvidersData {
|
|
44
|
-
guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
|
|
45
|
-
self.log.info("No custom providers file found, returning empty data.")
|
|
46
|
-
return CustomProvidersData()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
do {
|
|
50
|
-
let data = try Data(contentsOf: storageURL)
|
|
51
|
-
let decoder = JSONDecoder()
|
|
52
|
-
decoder.dateDecodingStrategy = .iso8601
|
|
53
|
-
let providersData = try decoder.decode(CustomProvidersData.self, from: data)
|
|
54
|
-
Self.log.info("Loaded \(providersData.providers.count) custom provider(s).")
|
|
55
|
-
return providersData
|
|
56
|
-
} catch {
|
|
57
|
-
self.log.error("Failed to load custom providers: \(error)")
|
|
58
|
-
return CustomProvidersData()
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/// Save all custom providers to disk.
|
|
63
|
-
public static func save(_ providersData: CustomProvidersData) throws {
|
|
64
|
-
let encoder = JSONEncoder()
|
|
65
|
-
encoder.dateEncodingStrategy = .iso8601
|
|
66
|
-
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
67
|
-
|
|
68
|
-
do {
|
|
69
|
-
let data = try encoder.encode(providersData)
|
|
70
|
-
try data.write(to: self.storageURL, options: .atomic)
|
|
71
|
-
Self.log.info("Saved \(providersData.providers.count) custom provider(s).")
|
|
72
|
-
} catch {
|
|
73
|
-
Self.log.error("Failed to save custom providers: \(error)")
|
|
74
|
-
throw CustomProviderStoreError.fileSystemError(error.localizedDescription)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// MARK: - CRUD Operations
|
|
79
|
-
|
|
80
|
-
/// Add a new custom provider.
|
|
81
|
-
///
|
|
82
|
-
/// - Parameter provider: The provider configuration to add.
|
|
83
|
-
/// - Throws: `CustomProviderStoreError.duplicateProvider` if a provider with the same ID already exists.
|
|
84
|
-
public static func addProvider(_ provider: CustomProviderConfig) throws {
|
|
85
|
-
var data = Self.load()
|
|
86
|
-
|
|
87
|
-
// Check for duplicates
|
|
88
|
-
if data.providers.contains(where: { $0.id == provider.id }) {
|
|
89
|
-
Self.log.error("Provider with ID '\(provider.id)' already exists.")
|
|
90
|
-
throw CustomProviderStoreError.duplicateProvider(id: provider.id)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
data.providers.append(provider)
|
|
94
|
-
try Self.save(data)
|
|
95
|
-
Self.log.info("Added custom provider: \(provider.name) (ID: \(provider.id))")
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/// Update an existing custom provider.
|
|
99
|
-
///
|
|
100
|
-
/// - Parameter provider: The updated provider configuration.
|
|
101
|
-
/// - Throws: `CustomProviderStoreError.providerNotFound` if the provider doesn't exist.
|
|
102
|
-
public static func updateProvider(_ provider: CustomProviderConfig) throws {
|
|
103
|
-
var data = Self.load()
|
|
104
|
-
|
|
105
|
-
guard let index = data.providers.firstIndex(where: { $0.id == provider.id }) else {
|
|
106
|
-
Self.log.error("Provider with ID '\(provider.id)' not found for update.")
|
|
107
|
-
throw CustomProviderStoreError.providerNotFound(id: provider.id)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
var updatedProvider = provider
|
|
111
|
-
updatedProvider.updatedAt = Date()
|
|
112
|
-
data.providers[index] = updatedProvider
|
|
113
|
-
try Self.save(data)
|
|
114
|
-
Self.log.info("Updated custom provider: \(provider.name) (ID: \(provider.id))")
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/// Remove a custom provider by ID.
|
|
118
|
-
///
|
|
119
|
-
/// - Parameter id: The ID of the provider to remove.
|
|
120
|
-
/// - Throws: `CustomProviderStoreError.providerNotFound` if the provider doesn't exist.
|
|
121
|
-
public static func removeProvider(id: String) throws {
|
|
122
|
-
var data = Self.load()
|
|
123
|
-
|
|
124
|
-
guard let index = data.providers.firstIndex(where: { $0.id == id }) else {
|
|
125
|
-
Self.log.error("Provider with ID '\(id)' not found for removal.")
|
|
126
|
-
throw CustomProviderStoreError.providerNotFound(id: id)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let removedProvider = data.providers.remove(at: index)
|
|
130
|
-
try Self.save(data)
|
|
131
|
-
Self.log.info("Removed custom provider: \(removedProvider.name) (ID: \(id))")
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/// Get a custom provider by ID.
|
|
135
|
-
///
|
|
136
|
-
/// - Parameter id: The ID of the provider to retrieve.
|
|
137
|
-
/// - Returns: The provider configuration, or `nil` if not found.
|
|
138
|
-
public static func getProvider(id: String) -> CustomProviderConfig? {
|
|
139
|
-
let data = Self.load()
|
|
140
|
-
return data.providers.first(where: { $0.id == id })
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/// Get all custom providers.
|
|
144
|
-
///
|
|
145
|
-
/// - Returns: Array of all custom provider configurations.
|
|
146
|
-
public static func getAllProviders() -> [CustomProviderConfig] {
|
|
147
|
-
let data = Self.load()
|
|
148
|
-
return data.providers
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/// Get only enabled custom providers.
|
|
152
|
-
///
|
|
153
|
-
/// - Returns: Array of enabled custom provider configurations.
|
|
154
|
-
public static func getEnabledProviders() -> [CustomProviderConfig] {
|
|
155
|
-
let data = Self.load()
|
|
156
|
-
return data.providers.filter(\.enabled)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
import Security
|
|
4
|
-
|
|
5
|
-
protocol DeepSeekTokenStoring: Sendable {
|
|
6
|
-
func loadToken() throws -> String?
|
|
7
|
-
func storeToken(_ token: String?) throws
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
enum DeepSeekTokenStoreError: LocalizedError {
|
|
11
|
-
case keychainStatus(OSStatus)
|
|
12
|
-
case invalidData
|
|
13
|
-
|
|
14
|
-
var errorDescription: String? {
|
|
15
|
-
switch self {
|
|
16
|
-
case let .keychainStatus(status):
|
|
17
|
-
"Keychain error: \(status)"
|
|
18
|
-
case .invalidData:
|
|
19
|
-
"Keychain returned invalid data."
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
struct KeychainDeepSeekTokenStore: DeepSeekTokenStoring {
|
|
25
|
-
private static let log = RunicLog.logger("deepseek-token-store")
|
|
26
|
-
|
|
27
|
-
private let service = "com.sriinnu.athena.Runic"
|
|
28
|
-
private let account = "deepseek-api-token"
|
|
29
|
-
|
|
30
|
-
func loadToken() throws -> String? {
|
|
31
|
-
// Try standard keychain first.
|
|
32
|
-
if let token = try self.readToken(dataProtection: false) {
|
|
33
|
-
return token
|
|
34
|
-
}
|
|
35
|
-
// Migrate from the old Data Protection keychain if present.
|
|
36
|
-
if let token = try self.readToken(dataProtection: true) {
|
|
37
|
-
Self.log.info("Migrating DeepSeek token from Data Protection keychain")
|
|
38
|
-
// Store first, delete old only on success — prevents data loss.
|
|
39
|
-
if (try? self.storeToken(token)) != nil {
|
|
40
|
-
try? self.deleteToken(dataProtection: true)
|
|
41
|
-
}
|
|
42
|
-
return token
|
|
43
|
-
}
|
|
44
|
-
return nil
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func storeToken(_ token: String?) throws {
|
|
48
|
-
let cleaned = token?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
-
if cleaned == nil || cleaned?.isEmpty == true {
|
|
50
|
-
try? self.deleteToken(dataProtection: false)
|
|
51
|
-
try? self.deleteToken(dataProtection: true)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let data = cleaned!.data(using: .utf8)!
|
|
56
|
-
|
|
57
|
-
// Always delete-then-add to ensure SecAccess ACL is set.
|
|
58
|
-
// SecItemUpdate does NOT update the ACL, so existing items with
|
|
59
|
-
// missing or stale ACLs would continue to prompt for passwords.
|
|
60
|
-
try? self.deleteToken(dataProtection: false)
|
|
61
|
-
try? self.deleteToken(dataProtection: true)
|
|
62
|
-
|
|
63
|
-
var addQuery = self.baseQuery(dataProtection: false)
|
|
64
|
-
addQuery[kSecValueData as String] = data
|
|
65
|
-
addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
66
|
-
|
|
67
|
-
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
68
|
-
guard addStatus == errSecSuccess else {
|
|
69
|
-
Self.log.error("Keychain add failed: \(addStatus)")
|
|
70
|
-
throw DeepSeekTokenStoreError.keychainStatus(addStatus)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// MARK: - Private
|
|
75
|
-
|
|
76
|
-
private func readToken(dataProtection: Bool) throws -> String? {
|
|
77
|
-
var result: CFTypeRef?
|
|
78
|
-
var query = self.baseQuery(dataProtection: dataProtection)
|
|
79
|
-
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
80
|
-
query[kSecReturnData as String] = true
|
|
81
|
-
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail
|
|
82
|
-
|
|
83
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
84
|
-
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
85
|
-
return nil
|
|
86
|
-
}
|
|
87
|
-
guard status == errSecSuccess else {
|
|
88
|
-
Self.log.error("Keychain read failed: \(status)")
|
|
89
|
-
throw DeepSeekTokenStoreError.keychainStatus(status)
|
|
90
|
-
}
|
|
91
|
-
guard let data = result as? Data else {
|
|
92
|
-
throw DeepSeekTokenStoreError.invalidData
|
|
93
|
-
}
|
|
94
|
-
let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
95
|
-
return (token?.isEmpty ?? true) ? nil : token
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private func deleteToken(dataProtection: Bool) throws {
|
|
99
|
-
let query = self.baseQuery(dataProtection: dataProtection)
|
|
100
|
-
let status = SecItemDelete(query as CFDictionary)
|
|
101
|
-
if status == errSecSuccess || status == errSecItemNotFound { return }
|
|
102
|
-
Self.log.error("Keychain delete failed: \(status)")
|
|
103
|
-
throw DeepSeekTokenStoreError.keychainStatus(status)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private func baseQuery(dataProtection: Bool) -> [String: Any] {
|
|
107
|
-
var q: [String: Any] = [
|
|
108
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
109
|
-
kSecAttrService as String: self.service,
|
|
110
|
-
kSecAttrAccount as String: self.account,
|
|
111
|
-
]
|
|
112
|
-
if dataProtection {
|
|
113
|
-
q[kSecUseDataProtectionKeychain as String] = true
|
|
114
|
-
}
|
|
115
|
-
return q
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
import Security
|
|
4
|
-
|
|
5
|
-
protocol FireworksTokenStoring: Sendable {
|
|
6
|
-
func loadToken() throws -> String?
|
|
7
|
-
func storeToken(_ token: String?) throws
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
enum FireworksTokenStoreError: LocalizedError {
|
|
11
|
-
case keychainStatus(OSStatus)
|
|
12
|
-
case invalidData
|
|
13
|
-
|
|
14
|
-
var errorDescription: String? {
|
|
15
|
-
switch self {
|
|
16
|
-
case let .keychainStatus(status):
|
|
17
|
-
"Keychain error: \(status)"
|
|
18
|
-
case .invalidData:
|
|
19
|
-
"Keychain returned invalid data."
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
struct KeychainFireworksTokenStore: FireworksTokenStoring {
|
|
25
|
-
private static let log = RunicLog.logger("fireworks-token-store")
|
|
26
|
-
|
|
27
|
-
private let service = "com.sriinnu.athena.Runic"
|
|
28
|
-
private let account = "fireworks-api-token"
|
|
29
|
-
|
|
30
|
-
func loadToken() throws -> String? {
|
|
31
|
-
// Try standard keychain first.
|
|
32
|
-
if let token = try self.readToken(dataProtection: false) {
|
|
33
|
-
return token
|
|
34
|
-
}
|
|
35
|
-
// Migrate from the old Data Protection keychain if present.
|
|
36
|
-
if let token = try self.readToken(dataProtection: true) {
|
|
37
|
-
Self.log.info("Migrating Fireworks token from Data Protection keychain")
|
|
38
|
-
// Store first, delete old only on success — prevents data loss.
|
|
39
|
-
if (try? self.storeToken(token)) != nil {
|
|
40
|
-
try? self.deleteToken(dataProtection: true)
|
|
41
|
-
}
|
|
42
|
-
return token
|
|
43
|
-
}
|
|
44
|
-
return nil
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func storeToken(_ token: String?) throws {
|
|
48
|
-
let cleaned = token?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
-
if cleaned == nil || cleaned?.isEmpty == true {
|
|
50
|
-
try? self.deleteToken(dataProtection: false)
|
|
51
|
-
try? self.deleteToken(dataProtection: true)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let data = cleaned!.data(using: .utf8)!
|
|
56
|
-
|
|
57
|
-
// Always delete-then-add to ensure SecAccess ACL is set.
|
|
58
|
-
// SecItemUpdate does NOT update the ACL, so existing items with
|
|
59
|
-
// missing or stale ACLs would continue to prompt for passwords.
|
|
60
|
-
try? self.deleteToken(dataProtection: false)
|
|
61
|
-
try? self.deleteToken(dataProtection: true)
|
|
62
|
-
|
|
63
|
-
var addQuery = self.baseQuery(dataProtection: false)
|
|
64
|
-
addQuery[kSecValueData as String] = data
|
|
65
|
-
addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
66
|
-
|
|
67
|
-
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
68
|
-
guard addStatus == errSecSuccess else {
|
|
69
|
-
Self.log.error("Keychain add failed: \(addStatus)")
|
|
70
|
-
throw FireworksTokenStoreError.keychainStatus(addStatus)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// MARK: - Private
|
|
75
|
-
|
|
76
|
-
private func readToken(dataProtection: Bool) throws -> String? {
|
|
77
|
-
var result: CFTypeRef?
|
|
78
|
-
var query = self.baseQuery(dataProtection: dataProtection)
|
|
79
|
-
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
80
|
-
query[kSecReturnData as String] = true
|
|
81
|
-
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail
|
|
82
|
-
|
|
83
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
84
|
-
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
85
|
-
return nil
|
|
86
|
-
}
|
|
87
|
-
guard status == errSecSuccess else {
|
|
88
|
-
Self.log.error("Keychain read failed: \(status)")
|
|
89
|
-
throw FireworksTokenStoreError.keychainStatus(status)
|
|
90
|
-
}
|
|
91
|
-
guard let data = result as? Data else {
|
|
92
|
-
throw FireworksTokenStoreError.invalidData
|
|
93
|
-
}
|
|
94
|
-
let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
95
|
-
return (token?.isEmpty ?? true) ? nil : token
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private func deleteToken(dataProtection: Bool) throws {
|
|
99
|
-
let query = self.baseQuery(dataProtection: dataProtection)
|
|
100
|
-
let status = SecItemDelete(query as CFDictionary)
|
|
101
|
-
if status == errSecSuccess || status == errSecItemNotFound { return }
|
|
102
|
-
Self.log.error("Keychain delete failed: \(status)")
|
|
103
|
-
throw FireworksTokenStoreError.keychainStatus(status)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private func baseQuery(dataProtection: Bool) -> [String: Any] {
|
|
107
|
-
var q: [String: Any] = [
|
|
108
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
109
|
-
kSecAttrService as String: self.service,
|
|
110
|
-
kSecAttrAccount as String: self.account,
|
|
111
|
-
]
|
|
112
|
-
if dataProtection {
|
|
113
|
-
q[kSecUseDataProtectionKeychain as String] = true
|
|
114
|
-
}
|
|
115
|
-
return q
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
import Security
|
|
4
|
-
|
|
5
|
-
protocol GroqTokenStoring: Sendable {
|
|
6
|
-
func loadToken() throws -> String?
|
|
7
|
-
func storeToken(_ token: String?) throws
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
enum GroqTokenStoreError: LocalizedError {
|
|
11
|
-
case keychainStatus(OSStatus)
|
|
12
|
-
case invalidData
|
|
13
|
-
|
|
14
|
-
var errorDescription: String? {
|
|
15
|
-
switch self {
|
|
16
|
-
case let .keychainStatus(status):
|
|
17
|
-
"Keychain error: \(status)"
|
|
18
|
-
case .invalidData:
|
|
19
|
-
"Keychain returned invalid data."
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
struct KeychainGroqTokenStore: GroqTokenStoring {
|
|
25
|
-
private static let log = RunicLog.logger("groq-token-store")
|
|
26
|
-
|
|
27
|
-
private let service = "com.sriinnu.athena.Runic"
|
|
28
|
-
private let account = "groq-api-token"
|
|
29
|
-
|
|
30
|
-
func loadToken() throws -> String? {
|
|
31
|
-
// Try standard keychain first.
|
|
32
|
-
if let token = try self.readToken(dataProtection: false) {
|
|
33
|
-
return token
|
|
34
|
-
}
|
|
35
|
-
// Migrate from the old Data Protection keychain if present.
|
|
36
|
-
if let token = try self.readToken(dataProtection: true) {
|
|
37
|
-
Self.log.info("Migrating Groq token from Data Protection keychain")
|
|
38
|
-
// Store first, delete old only on success — prevents data loss.
|
|
39
|
-
if (try? self.storeToken(token)) != nil {
|
|
40
|
-
try? self.deleteToken(dataProtection: true)
|
|
41
|
-
}
|
|
42
|
-
return token
|
|
43
|
-
}
|
|
44
|
-
return nil
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func storeToken(_ token: String?) throws {
|
|
48
|
-
let cleaned = token?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
-
if cleaned == nil || cleaned?.isEmpty == true {
|
|
50
|
-
try? self.deleteToken(dataProtection: false)
|
|
51
|
-
try? self.deleteToken(dataProtection: true)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let data = cleaned!.data(using: .utf8)!
|
|
56
|
-
|
|
57
|
-
// Always delete-then-add to ensure SecAccess ACL is set.
|
|
58
|
-
// SecItemUpdate does NOT update the ACL, so existing items with
|
|
59
|
-
// missing or stale ACLs would continue to prompt for passwords.
|
|
60
|
-
try? self.deleteToken(dataProtection: false)
|
|
61
|
-
try? self.deleteToken(dataProtection: true)
|
|
62
|
-
|
|
63
|
-
var addQuery = self.baseQuery(dataProtection: false)
|
|
64
|
-
addQuery[kSecValueData as String] = data
|
|
65
|
-
addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
66
|
-
|
|
67
|
-
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
68
|
-
guard addStatus == errSecSuccess else {
|
|
69
|
-
Self.log.error("Keychain add failed: \(addStatus)")
|
|
70
|
-
throw GroqTokenStoreError.keychainStatus(addStatus)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// MARK: - Private
|
|
75
|
-
|
|
76
|
-
private func readToken(dataProtection: Bool) throws -> String? {
|
|
77
|
-
var result: CFTypeRef?
|
|
78
|
-
var query = self.baseQuery(dataProtection: dataProtection)
|
|
79
|
-
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
80
|
-
query[kSecReturnData as String] = true
|
|
81
|
-
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail
|
|
82
|
-
|
|
83
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
84
|
-
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
85
|
-
return nil
|
|
86
|
-
}
|
|
87
|
-
guard status == errSecSuccess else {
|
|
88
|
-
Self.log.error("Keychain read failed: \(status)")
|
|
89
|
-
throw GroqTokenStoreError.keychainStatus(status)
|
|
90
|
-
}
|
|
91
|
-
guard let data = result as? Data else {
|
|
92
|
-
throw GroqTokenStoreError.invalidData
|
|
93
|
-
}
|
|
94
|
-
let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
95
|
-
return (token?.isEmpty ?? true) ? nil : token
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private func deleteToken(dataProtection: Bool) throws {
|
|
99
|
-
let query = self.baseQuery(dataProtection: dataProtection)
|
|
100
|
-
let status = SecItemDelete(query as CFDictionary)
|
|
101
|
-
if status == errSecSuccess || status == errSecItemNotFound { return }
|
|
102
|
-
Self.log.error("Keychain delete failed: \(status)")
|
|
103
|
-
throw GroqTokenStoreError.keychainStatus(status)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private func baseQuery(dataProtection: Bool) -> [String: Any] {
|
|
107
|
-
var q: [String: Any] = [
|
|
108
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
109
|
-
kSecAttrService as String: self.service,
|
|
110
|
-
kSecAttrAccount as String: self.account,
|
|
111
|
-
]
|
|
112
|
-
if dataProtection {
|
|
113
|
-
q[kSecUseDataProtectionKeychain as String] = true
|
|
114
|
-
}
|
|
115
|
-
return q
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
import Security
|
|
4
|
-
|
|
5
|
-
protocol KimiTokenStoring: Sendable {
|
|
6
|
-
func loadToken() throws -> String?
|
|
7
|
-
func storeToken(_ token: String?) throws
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
enum KimiTokenStoreError: LocalizedError {
|
|
11
|
-
case keychainStatus(OSStatus)
|
|
12
|
-
case invalidData
|
|
13
|
-
|
|
14
|
-
var errorDescription: String? {
|
|
15
|
-
switch self {
|
|
16
|
-
case let .keychainStatus(status):
|
|
17
|
-
"Keychain error: \(status)"
|
|
18
|
-
case .invalidData:
|
|
19
|
-
"Keychain returned invalid data."
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
struct KeychainKimiTokenStore: KimiTokenStoring {
|
|
25
|
-
private static let log = RunicLog.logger("kimi-token-store")
|
|
26
|
-
|
|
27
|
-
private let service = "com.sriinnu.athena.Runic"
|
|
28
|
-
private let account = "kimi-api-token"
|
|
29
|
-
|
|
30
|
-
func loadToken() throws -> String? {
|
|
31
|
-
// Try standard keychain first.
|
|
32
|
-
if let token = try self.readToken(dataProtection: false) {
|
|
33
|
-
return token
|
|
34
|
-
}
|
|
35
|
-
// Migrate from the old Data Protection keychain if present.
|
|
36
|
-
if let token = try self.readToken(dataProtection: true) {
|
|
37
|
-
Self.log.info("Migrating Kimi token from Data Protection keychain")
|
|
38
|
-
// Store first, delete old only on success — prevents data loss.
|
|
39
|
-
if (try? self.storeToken(token)) != nil {
|
|
40
|
-
try? self.deleteToken(dataProtection: true)
|
|
41
|
-
}
|
|
42
|
-
return token
|
|
43
|
-
}
|
|
44
|
-
return nil
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func storeToken(_ token: String?) throws {
|
|
48
|
-
let cleaned = token?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
-
if cleaned == nil || cleaned?.isEmpty == true {
|
|
50
|
-
try? self.deleteToken(dataProtection: false)
|
|
51
|
-
try? self.deleteToken(dataProtection: true)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let data = cleaned!.data(using: .utf8)!
|
|
56
|
-
|
|
57
|
-
// Always delete-then-add to ensure SecAccess ACL is set.
|
|
58
|
-
// SecItemUpdate does NOT update the ACL, so existing items with
|
|
59
|
-
// missing or stale ACLs would continue to prompt for passwords.
|
|
60
|
-
try? self.deleteToken(dataProtection: false)
|
|
61
|
-
try? self.deleteToken(dataProtection: true)
|
|
62
|
-
|
|
63
|
-
var addQuery = self.baseQuery(dataProtection: false)
|
|
64
|
-
addQuery[kSecValueData as String] = data
|
|
65
|
-
addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
66
|
-
|
|
67
|
-
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
68
|
-
guard addStatus == errSecSuccess else {
|
|
69
|
-
Self.log.error("Keychain add failed: \(addStatus)")
|
|
70
|
-
throw KimiTokenStoreError.keychainStatus(addStatus)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// MARK: - Private
|
|
75
|
-
|
|
76
|
-
private func readToken(dataProtection: Bool) throws -> String? {
|
|
77
|
-
var result: CFTypeRef?
|
|
78
|
-
var query = self.baseQuery(dataProtection: dataProtection)
|
|
79
|
-
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
80
|
-
query[kSecReturnData as String] = true
|
|
81
|
-
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail
|
|
82
|
-
|
|
83
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
84
|
-
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
85
|
-
return nil
|
|
86
|
-
}
|
|
87
|
-
guard status == errSecSuccess else {
|
|
88
|
-
Self.log.error("Keychain read failed: \(status)")
|
|
89
|
-
throw KimiTokenStoreError.keychainStatus(status)
|
|
90
|
-
}
|
|
91
|
-
guard let data = result as? Data else {
|
|
92
|
-
throw KimiTokenStoreError.invalidData
|
|
93
|
-
}
|
|
94
|
-
let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
95
|
-
return (token?.isEmpty ?? true) ? nil : token
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private func deleteToken(dataProtection: Bool) throws {
|
|
99
|
-
let query = self.baseQuery(dataProtection: dataProtection)
|
|
100
|
-
let status = SecItemDelete(query as CFDictionary)
|
|
101
|
-
if status == errSecSuccess || status == errSecItemNotFound { return }
|
|
102
|
-
Self.log.error("Keychain delete failed: \(status)")
|
|
103
|
-
throw KimiTokenStoreError.keychainStatus(status)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private func baseQuery(dataProtection: Bool) -> [String: Any] {
|
|
107
|
-
var q: [String: Any] = [
|
|
108
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
109
|
-
kSecAttrService as String: self.service,
|
|
110
|
-
kSecAttrAccount as String: self.account,
|
|
111
|
-
]
|
|
112
|
-
if dataProtection {
|
|
113
|
-
q[kSecUseDataProtectionKeychain as String] = true
|
|
114
|
-
}
|
|
115
|
-
return q
|
|
116
|
-
}
|
|
117
|
-
}
|