@sriinnu/runic 1.0.0 → 2.0.0
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 +11 -0
- package/LICENSE +1 -1
- package/README.md +47 -29
- package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
- package/RuniciOS/Views/ProviderDetailView.swift +9 -9
- package/RuniciOS/Views/ProviderListView.swift +10 -10
- package/RuniciOS/Widgets/LargeWidget.swift +14 -14
- package/RuniciOS/Widgets/LiveActivity.swift +19 -19
- package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
- package/RuniciOS/Widgets/MediumWidget.swift +10 -10
- package/RuniciOS/Widgets/RunicWidget.swift +5 -5
- package/RuniciOS/Widgets/SmallWidget.swift +9 -9
- package/Scripts/package_app.sh +2 -2
- package/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
- package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
- package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
- package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
- package/Sources/Runic/Core/RunicColors.swift +6 -6
- package/Sources/Runic/Core/RunicSpacing.swift +7 -7
- package/Sources/Runic/Core/RunicTypography.swift +206 -0
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
- package/Sources/Runic/Core/UsageExporter.swift +8 -4
- package/Sources/Runic/ProviderRegistry.swift +1 -1
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
- 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/provider-context-windows.json +20 -0
- package/Sources/Runic/RunicApp.swift +46 -36
- package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
- package/Sources/Runic/UsagePaceText.swift +1 -1
- package/Sources/Runic/UsageProgressBar.swift +13 -9
- package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
- package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
- package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
- package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
- package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
- package/Sources/RunicCLI/CLIEntry.swift +83 -42
- package/Sources/RunicCLI/CLIRenderer.swift +1 -1
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
- package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
- package/Sources/RunicClaudeWebProbe/main.swift +1 -1
- package/Sources/RunicCore/AlertRuleStore.swift +20 -20
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
- package/Sources/RunicCore/CostUsageModels.swift +1 -1
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
- package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
- package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
- package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
- package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
- package/Sources/RunicCore/Team/TeamModels.swift +111 -110
- package/Sources/RunicCore/Team/TeamStore.swift +126 -109
- package/Sources/RunicCore/UsageFetcher.swift +2 -2
- package/Sources/RunicCore/UsageFormatter.swift +25 -25
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
- package/Tests/RunicTests/AppDelegateTests.swift +8 -3
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
- package/Tests/RunicTests/CLICostTests.swift +6 -7
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
- package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
- package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
- package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
- package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
- package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
- package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
- package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
- package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
- package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
- package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
- package/Tests/RunicTests/InstallOriginTests.swift +1 -2
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
- package/Tests/RunicTests/LiveAccountTests.swift +3 -3
- package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
- package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
- package/Tests/RunicTests/PathBuilderTests.swift +14 -15
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
- package/Tests/RunicTests/RunicTests.swift +6 -7
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
- package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
- package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
- package/Tests/RunicTests/StatusMenuTests.swift +63 -22
- package/Tests/RunicTests/StatusProbeTests.swift +28 -29
- package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
- package/Tests/RunicTests/TextParsingTests.swift +1 -2
- package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
- package/Tests/RunicTests/UsagePaceTests.swift +5 -6
- package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
- package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
- package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
- package/appcast.xml +36 -37
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import AppKit
|
|
2
|
-
import RunicCore
|
|
3
2
|
import Observation
|
|
3
|
+
import RunicCore
|
|
4
4
|
import ServiceManagement
|
|
5
5
|
|
|
6
6
|
enum RefreshFrequency: String, CaseIterable, Identifiable {
|
|
@@ -10,7 +10,9 @@ enum RefreshFrequency: String, CaseIterable, Identifiable {
|
|
|
10
10
|
case fiveMinutes
|
|
11
11
|
case fifteenMinutes
|
|
12
12
|
|
|
13
|
-
var id: String {
|
|
13
|
+
var id: String {
|
|
14
|
+
self.rawValue
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
var seconds: TimeInterval? {
|
|
16
18
|
switch self {
|
|
@@ -38,7 +40,9 @@ enum UsageMetricDisplayMode: String, CaseIterable, Identifiable {
|
|
|
38
40
|
case barsOnly
|
|
39
41
|
case percentOnly
|
|
40
42
|
|
|
41
|
-
var id: String {
|
|
43
|
+
var id: String {
|
|
44
|
+
self.rawValue
|
|
45
|
+
}
|
|
42
46
|
|
|
43
47
|
var label: String {
|
|
44
48
|
switch self {
|
|
@@ -72,7 +76,9 @@ enum MenuMode: String, CaseIterable, Identifiable {
|
|
|
72
76
|
case analyst
|
|
73
77
|
case `operator`
|
|
74
78
|
|
|
75
|
-
var id: String {
|
|
79
|
+
var id: String {
|
|
80
|
+
self.rawValue
|
|
81
|
+
}
|
|
76
82
|
|
|
77
83
|
var label: String {
|
|
78
84
|
switch self {
|
|
@@ -80,7 +86,7 @@ enum MenuMode: String, CaseIterable, Identifiable {
|
|
|
80
86
|
"Glance"
|
|
81
87
|
case .analyst:
|
|
82
88
|
"Analyst"
|
|
83
|
-
case
|
|
89
|
+
case .operator:
|
|
84
90
|
"Operator"
|
|
85
91
|
}
|
|
86
92
|
}
|
|
@@ -91,13 +97,15 @@ enum ChartStyle: String, CaseIterable, Identifiable {
|
|
|
91
97
|
case area
|
|
92
98
|
case bar
|
|
93
99
|
|
|
94
|
-
var id: String {
|
|
100
|
+
var id: String {
|
|
101
|
+
self.rawValue
|
|
102
|
+
}
|
|
95
103
|
|
|
96
104
|
var label: String {
|
|
97
105
|
switch self {
|
|
98
|
-
case .line:
|
|
99
|
-
case .area:
|
|
100
|
-
case .bar:
|
|
106
|
+
case .line: "Line"
|
|
107
|
+
case .area: "Area"
|
|
108
|
+
case .bar: "Bar"
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
}
|
|
@@ -106,12 +114,14 @@ enum NumberFormat: String, CaseIterable, Identifiable {
|
|
|
106
114
|
case abbreviated
|
|
107
115
|
case full
|
|
108
116
|
|
|
109
|
-
var id: String {
|
|
117
|
+
var id: String {
|
|
118
|
+
self.rawValue
|
|
119
|
+
}
|
|
110
120
|
|
|
111
121
|
var label: String {
|
|
112
122
|
switch self {
|
|
113
|
-
case .abbreviated:
|
|
114
|
-
case .full:
|
|
123
|
+
case .abbreviated: "Abbreviated"
|
|
124
|
+
case .full: "Full"
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
}
|
|
@@ -120,12 +130,14 @@ enum DateFormat: String, CaseIterable, Identifiable {
|
|
|
120
130
|
case relative
|
|
121
131
|
case absolute
|
|
122
132
|
|
|
123
|
-
var id: String {
|
|
133
|
+
var id: String {
|
|
134
|
+
self.rawValue
|
|
135
|
+
}
|
|
124
136
|
|
|
125
137
|
var label: String {
|
|
126
138
|
switch self {
|
|
127
|
-
case .relative:
|
|
128
|
-
case .absolute:
|
|
139
|
+
case .relative: "Relative"
|
|
140
|
+
case .absolute: "Absolute"
|
|
129
141
|
}
|
|
130
142
|
}
|
|
131
143
|
}
|
|
@@ -135,13 +147,15 @@ enum Theme: String, CaseIterable, Identifiable {
|
|
|
135
147
|
case light
|
|
136
148
|
case dark
|
|
137
149
|
|
|
138
|
-
var id: String {
|
|
150
|
+
var id: String {
|
|
151
|
+
self.rawValue
|
|
152
|
+
}
|
|
139
153
|
|
|
140
154
|
var label: String {
|
|
141
155
|
switch self {
|
|
142
|
-
case .system:
|
|
143
|
-
case .light:
|
|
144
|
-
case .dark:
|
|
156
|
+
case .system: "System"
|
|
157
|
+
case .light: "Light"
|
|
158
|
+
case .dark: "Dark"
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
161
|
}
|
|
@@ -150,12 +164,14 @@ enum ProviderSwitcherLayout: String, CaseIterable, Identifiable {
|
|
|
150
164
|
case top
|
|
151
165
|
case sidebar
|
|
152
166
|
|
|
153
|
-
var id: String {
|
|
167
|
+
var id: String {
|
|
168
|
+
self.rawValue
|
|
169
|
+
}
|
|
154
170
|
|
|
155
171
|
var label: String {
|
|
156
172
|
switch self {
|
|
157
|
-
case .top:
|
|
158
|
-
case .sidebar:
|
|
173
|
+
case .top: "Top"
|
|
174
|
+
case .sidebar: "Sidebar"
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
177
|
}
|
|
@@ -164,12 +180,14 @@ enum ProviderSwitcherIconSize: String, CaseIterable, Identifiable {
|
|
|
164
180
|
case small
|
|
165
181
|
case medium
|
|
166
182
|
|
|
167
|
-
var id: String {
|
|
183
|
+
var id: String {
|
|
184
|
+
self.rawValue
|
|
185
|
+
}
|
|
168
186
|
|
|
169
187
|
var label: String {
|
|
170
188
|
switch self {
|
|
171
|
-
case .small:
|
|
172
|
-
case .medium:
|
|
189
|
+
case .small: "Small"
|
|
190
|
+
case .medium: "Medium"
|
|
173
191
|
}
|
|
174
192
|
}
|
|
175
193
|
}
|
|
@@ -189,15 +207,21 @@ final class SettingsStore {
|
|
|
189
207
|
}
|
|
190
208
|
|
|
191
209
|
var autoDisableRefreshWhenIdleEnabled: Bool {
|
|
192
|
-
didSet { self.userDefaults.set(
|
|
210
|
+
didSet { self.userDefaults.set(
|
|
211
|
+
self.autoDisableRefreshWhenIdleEnabled,
|
|
212
|
+
forKey: "autoDisableRefreshWhenIdleEnabled") }
|
|
193
213
|
}
|
|
194
214
|
|
|
195
215
|
var autoDisableRefreshWhenIdleMinutes: Int {
|
|
196
|
-
didSet { self.userDefaults.set(
|
|
216
|
+
didSet { self.userDefaults.set(
|
|
217
|
+
self.autoDisableRefreshWhenIdleMinutes,
|
|
218
|
+
forKey: "autoDisableRefreshWhenIdleMinutes") }
|
|
197
219
|
}
|
|
198
220
|
|
|
199
221
|
var autoDisableRefreshOnSleepEnabled: Bool {
|
|
200
|
-
didSet {
|
|
222
|
+
didSet {
|
|
223
|
+
self.userDefaults.set(self.autoDisableRefreshOnSleepEnabled, forKey: "autoDisableRefreshOnSleepEnabled")
|
|
224
|
+
}
|
|
201
225
|
}
|
|
202
226
|
|
|
203
227
|
var autoRefreshWarningEnabled: Bool {
|
|
@@ -210,13 +234,17 @@ final class SettingsStore {
|
|
|
210
234
|
|
|
211
235
|
var autoSuspendInactiveProvidersEnabled: Bool {
|
|
212
236
|
didSet {
|
|
213
|
-
self.userDefaults.set(
|
|
237
|
+
self.userDefaults.set(
|
|
238
|
+
self.autoSuspendInactiveProvidersEnabled,
|
|
239
|
+
forKey: "autoSuspendInactiveProvidersEnabled")
|
|
214
240
|
}
|
|
215
241
|
}
|
|
216
242
|
|
|
217
243
|
var autoSuspendInactiveProvidersMinutes: Int {
|
|
218
244
|
didSet {
|
|
219
|
-
self.userDefaults.set(
|
|
245
|
+
self.userDefaults.set(
|
|
246
|
+
self.autoSuspendInactiveProvidersMinutes,
|
|
247
|
+
forKey: "autoSuspendInactiveProvidersMinutes")
|
|
220
248
|
}
|
|
221
249
|
}
|
|
222
250
|
|
|
@@ -227,7 +255,8 @@ final class SettingsStore {
|
|
|
227
255
|
}
|
|
228
256
|
}
|
|
229
257
|
|
|
230
|
-
/// Hidden toggle to reveal debug-only menu items (enable via defaults write com.sriinnu.athena.Runic
|
|
258
|
+
/// Hidden toggle to reveal debug-only menu items (enable via defaults write com.sriinnu.athena.Runic
|
|
259
|
+
/// debugMenuEnabled
|
|
231
260
|
/// -bool YES).
|
|
232
261
|
var debugMenuEnabled: Bool {
|
|
233
262
|
didSet { self.userDefaults.set(self.debugMenuEnabled, forKey: "debugMenuEnabled") }
|
|
@@ -296,6 +325,13 @@ final class SettingsStore {
|
|
|
296
325
|
}
|
|
297
326
|
}
|
|
298
327
|
|
|
328
|
+
var selectedFontFamily: String {
|
|
329
|
+
didSet {
|
|
330
|
+
self.userDefaults.set(self.selectedFontFamily, forKey: "selectedFontFamily")
|
|
331
|
+
RunicFont.family = self.selectedFontFamily
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
299
335
|
/// Optional: use provider branding icons with a percentage in the menu bar.
|
|
300
336
|
var menuBarShowsBrandIconWithPercent: Bool {
|
|
301
337
|
didSet {
|
|
@@ -325,6 +361,12 @@ final class SettingsStore {
|
|
|
325
361
|
didSet { self.userDefaults.set(self.insightsReportDays, forKey: "insightsReportDays") }
|
|
326
362
|
}
|
|
327
363
|
|
|
364
|
+
/// How many days of usage history to scan for ledger data (charts, breakdowns).
|
|
365
|
+
/// Options: 3, 7, 30, 365. Default: 30.
|
|
366
|
+
var ledgerMaxAgeDays: Int {
|
|
367
|
+
didSet { self.userDefaults.set(self.ledgerMaxAgeDays, forKey: "ledgerMaxAgeDays") }
|
|
368
|
+
}
|
|
369
|
+
|
|
328
370
|
var randomBlinkEnabled: Bool {
|
|
329
371
|
didSet { self.userDefaults.set(self.randomBlinkEnabled, forKey: "randomBlinkEnabled") }
|
|
330
372
|
}
|
|
@@ -400,7 +442,7 @@ final class SettingsStore {
|
|
|
400
442
|
var zaiAPIToken: String {
|
|
401
443
|
didSet {
|
|
402
444
|
self.schedulePersistZaiAPIToken()
|
|
403
|
-
if !zaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
445
|
+
if !self.zaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
404
446
|
self.autoEnableProviderIfNeeded(cliName: "zai")
|
|
405
447
|
}
|
|
406
448
|
}
|
|
@@ -410,7 +452,7 @@ final class SettingsStore {
|
|
|
410
452
|
var minimaxAPIToken: String {
|
|
411
453
|
didSet {
|
|
412
454
|
self.schedulePersistMiniMaxAPIToken()
|
|
413
|
-
if !minimaxAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
455
|
+
if !self.minimaxAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
414
456
|
self.autoEnableProviderIfNeeded(cliName: "minimax")
|
|
415
457
|
}
|
|
416
458
|
}
|
|
@@ -420,7 +462,7 @@ final class SettingsStore {
|
|
|
420
462
|
var minimaxCookieHeader: String {
|
|
421
463
|
didSet {
|
|
422
464
|
self.schedulePersistMiniMaxCookieHeader()
|
|
423
|
-
if !minimaxCookieHeader.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
465
|
+
if !self.minimaxCookieHeader.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
424
466
|
self.autoEnableProviderIfNeeded(cliName: "minimax")
|
|
425
467
|
}
|
|
426
468
|
}
|
|
@@ -435,7 +477,7 @@ final class SettingsStore {
|
|
|
435
477
|
var copilotAPIToken: String {
|
|
436
478
|
didSet {
|
|
437
479
|
self.schedulePersistCopilotAPIToken()
|
|
438
|
-
if !copilotAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
480
|
+
if !self.copilotAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
439
481
|
self.autoEnableProviderIfNeeded(cliName: "copilot")
|
|
440
482
|
}
|
|
441
483
|
}
|
|
@@ -445,7 +487,7 @@ final class SettingsStore {
|
|
|
445
487
|
var openRouterAPIToken: String {
|
|
446
488
|
didSet {
|
|
447
489
|
self.schedulePersistOpenRouterAPIToken()
|
|
448
|
-
if !openRouterAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
490
|
+
if !self.openRouterAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
449
491
|
self.autoEnableProviderIfNeeded(cliName: "openrouter")
|
|
450
492
|
}
|
|
451
493
|
}
|
|
@@ -455,7 +497,7 @@ final class SettingsStore {
|
|
|
455
497
|
var groqAPIToken: String {
|
|
456
498
|
didSet {
|
|
457
499
|
self.schedulePersistGroqAPIToken()
|
|
458
|
-
if !groqAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
500
|
+
if !self.groqAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
459
501
|
self.autoEnableProviderIfNeeded(cliName: "groq")
|
|
460
502
|
}
|
|
461
503
|
}
|
|
@@ -465,7 +507,7 @@ final class SettingsStore {
|
|
|
465
507
|
var deepSeekAPIToken: String {
|
|
466
508
|
didSet {
|
|
467
509
|
self.schedulePersistDeepSeekAPIToken()
|
|
468
|
-
if !deepSeekAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
510
|
+
if !self.deepSeekAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
469
511
|
self.autoEnableProviderIfNeeded(cliName: "deepseek")
|
|
470
512
|
}
|
|
471
513
|
}
|
|
@@ -475,7 +517,7 @@ final class SettingsStore {
|
|
|
475
517
|
var fireworksAPIToken: String {
|
|
476
518
|
didSet {
|
|
477
519
|
self.schedulePersistFireworksAPIToken()
|
|
478
|
-
if !fireworksAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
520
|
+
if !self.fireworksAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
479
521
|
self.autoEnableProviderIfNeeded(cliName: "fireworks")
|
|
480
522
|
}
|
|
481
523
|
}
|
|
@@ -485,7 +527,7 @@ final class SettingsStore {
|
|
|
485
527
|
var mistralAPIToken: String {
|
|
486
528
|
didSet {
|
|
487
529
|
self.schedulePersistMistralAPIToken()
|
|
488
|
-
if !mistralAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
530
|
+
if !self.mistralAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
489
531
|
self.autoEnableProviderIfNeeded(cliName: "mistral")
|
|
490
532
|
}
|
|
491
533
|
}
|
|
@@ -495,7 +537,7 @@ final class SettingsStore {
|
|
|
495
537
|
var perplexityAPIToken: String {
|
|
496
538
|
didSet {
|
|
497
539
|
self.schedulePersistPerplexityAPIToken()
|
|
498
|
-
if !perplexityAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
540
|
+
if !self.perplexityAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
499
541
|
self.autoEnableProviderIfNeeded(cliName: "perplexity")
|
|
500
542
|
}
|
|
501
543
|
}
|
|
@@ -505,7 +547,7 @@ final class SettingsStore {
|
|
|
505
547
|
var kimiAPIToken: String {
|
|
506
548
|
didSet {
|
|
507
549
|
self.schedulePersistKimiAPIToken()
|
|
508
|
-
if !kimiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
550
|
+
if !self.kimiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
509
551
|
self.autoEnableProviderIfNeeded(cliName: "kimi")
|
|
510
552
|
}
|
|
511
553
|
}
|
|
@@ -515,7 +557,7 @@ final class SettingsStore {
|
|
|
515
557
|
var auggieAPIToken: String {
|
|
516
558
|
didSet {
|
|
517
559
|
self.schedulePersistAuggieAPIToken()
|
|
518
|
-
if !auggieAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
560
|
+
if !self.auggieAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
519
561
|
self.autoEnableProviderIfNeeded(cliName: "auggie")
|
|
520
562
|
}
|
|
521
563
|
}
|
|
@@ -525,7 +567,7 @@ final class SettingsStore {
|
|
|
525
567
|
var togetherAPIToken: String {
|
|
526
568
|
didSet {
|
|
527
569
|
self.schedulePersistTogetherAPIToken()
|
|
528
|
-
if !togetherAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
570
|
+
if !self.togetherAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
529
571
|
self.autoEnableProviderIfNeeded(cliName: "together")
|
|
530
572
|
}
|
|
531
573
|
}
|
|
@@ -535,7 +577,7 @@ final class SettingsStore {
|
|
|
535
577
|
var cohereAPIToken: String {
|
|
536
578
|
didSet {
|
|
537
579
|
self.schedulePersistCohereAPIToken()
|
|
538
|
-
if !cohereAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
580
|
+
if !self.cohereAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
539
581
|
self.autoEnableProviderIfNeeded(cliName: "cohere")
|
|
540
582
|
}
|
|
541
583
|
}
|
|
@@ -545,7 +587,7 @@ final class SettingsStore {
|
|
|
545
587
|
var xaiAPIToken: String {
|
|
546
588
|
didSet {
|
|
547
589
|
self.schedulePersistXAiAPIToken()
|
|
548
|
-
if !xaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
590
|
+
if !self.xaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
549
591
|
self.autoEnableProviderIfNeeded(cliName: "xai")
|
|
550
592
|
}
|
|
551
593
|
}
|
|
@@ -555,7 +597,7 @@ final class SettingsStore {
|
|
|
555
597
|
var cerebrasAPIToken: String {
|
|
556
598
|
didSet {
|
|
557
599
|
self.schedulePersistCerebrasAPIToken()
|
|
558
|
-
if !cerebrasAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
600
|
+
if !self.cerebrasAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
559
601
|
self.autoEnableProviderIfNeeded(cliName: "cerebras")
|
|
560
602
|
}
|
|
561
603
|
}
|
|
@@ -565,7 +607,7 @@ final class SettingsStore {
|
|
|
565
607
|
var qwenAPIToken: String {
|
|
566
608
|
didSet {
|
|
567
609
|
self.schedulePersistQwenAPIToken()
|
|
568
|
-
if !qwenAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
610
|
+
if !self.qwenAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
569
611
|
self.autoEnableProviderIfNeeded(cliName: "qwen")
|
|
570
612
|
}
|
|
571
613
|
}
|
|
@@ -575,7 +617,7 @@ final class SettingsStore {
|
|
|
575
617
|
var sambaNovaAPIToken: String {
|
|
576
618
|
didSet {
|
|
577
619
|
self.schedulePersistSambaNovaAPIToken()
|
|
578
|
-
if !sambaNovaAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
620
|
+
if !self.sambaNovaAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
579
621
|
self.autoEnableProviderIfNeeded(cliName: "sambanova")
|
|
580
622
|
}
|
|
581
623
|
}
|
|
@@ -600,7 +642,7 @@ final class SettingsStore {
|
|
|
600
642
|
var azureOpenAIAPIToken: String {
|
|
601
643
|
didSet {
|
|
602
644
|
self.schedulePersistAzureOpenAIAPIToken()
|
|
603
|
-
if !azureOpenAIAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
645
|
+
if !self.azureOpenAIAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
604
646
|
self.autoEnableProviderIfNeeded(cliName: "azure")
|
|
605
647
|
}
|
|
606
648
|
}
|
|
@@ -699,6 +741,7 @@ final class SettingsStore {
|
|
|
699
741
|
_ = self.costUsageEnabled
|
|
700
742
|
_ = self.insightsMenuMaxItems
|
|
701
743
|
_ = self.insightsReportDays
|
|
744
|
+
_ = self.ledgerMaxAgeDays
|
|
702
745
|
_ = self.randomBlinkEnabled
|
|
703
746
|
_ = self.claudeWebExtrasEnabled
|
|
704
747
|
_ = self.showOptionalCreditsAndExtraUsage
|
|
@@ -877,7 +920,7 @@ final class SettingsStore {
|
|
|
877
920
|
let metricDisplayRaw = userDefaults.string(forKey: "usageMetricDisplayMode")
|
|
878
921
|
self.usageMetricDisplayMode = UsageMetricDisplayMode(rawValue: metricDisplayRaw ?? "") ?? .barsAndPercent
|
|
879
922
|
let menuModeRaw = userDefaults.string(forKey: "menuMode")
|
|
880
|
-
self.menuMode = MenuMode(rawValue: menuModeRaw ?? "") ??
|
|
923
|
+
self.menuMode = MenuMode(rawValue: menuModeRaw ?? "") ?? .operator
|
|
881
924
|
let chartStyleRaw = userDefaults.string(forKey: "chartStyle")
|
|
882
925
|
self.chartStyle = ChartStyle(rawValue: chartStyleRaw ?? "") ?? .line
|
|
883
926
|
let numberFormatRaw = userDefaults.string(forKey: "numberFormat")
|
|
@@ -886,6 +929,7 @@ final class SettingsStore {
|
|
|
886
929
|
self.dateFormat = DateFormat(rawValue: dateFormatRaw ?? "") ?? .relative
|
|
887
930
|
let themeRaw = userDefaults.string(forKey: "theme")
|
|
888
931
|
self.theme = Theme(rawValue: themeRaw ?? "") ?? .system
|
|
932
|
+
self.selectedFontFamily = userDefaults.string(forKey: "selectedFontFamily") ?? "Fira Code"
|
|
889
933
|
self.menuBarShowsBrandIconWithPercent = userDefaults.object(
|
|
890
934
|
forKey: "menuBarShowsBrandIconWithPercent") as? Bool ?? false
|
|
891
935
|
self.menuBarVibrantIconEnabled = userDefaults.object(
|
|
@@ -893,6 +937,7 @@ final class SettingsStore {
|
|
|
893
937
|
self.costUsageEnabled = userDefaults.object(forKey: "tokenCostUsageEnabled") as? Bool ?? true
|
|
894
938
|
self.insightsMenuMaxItems = userDefaults.object(forKey: "insightsMenuMaxItems") as? Int ?? 4
|
|
895
939
|
self.insightsReportDays = userDefaults.object(forKey: "insightsReportDays") as? Int ?? 7
|
|
940
|
+
self.ledgerMaxAgeDays = userDefaults.object(forKey: "ledgerMaxAgeDays") as? Int ?? 30
|
|
896
941
|
self.randomBlinkEnabled = userDefaults.object(forKey: "randomBlinkEnabled") as? Bool ?? false
|
|
897
942
|
self.claudeWebExtrasEnabled = userDefaults.object(forKey: "claudeWebExtrasEnabled") as? Bool ?? false
|
|
898
943
|
let creditsExtrasDefault = userDefaults.object(forKey: "showOptionalCreditsAndExtraUsage") as? Bool
|
|
@@ -945,7 +990,8 @@ final class SettingsStore {
|
|
|
945
990
|
self.sambaNovaAPIToken = (try? sambaNovaTokenStore.loadToken()) ?? ""
|
|
946
991
|
self.qwenAPIToken = (try? qwenTokenStore.loadToken()) ?? ""
|
|
947
992
|
self.azureOpenAIAPIToken = (try? azureOpenAITokenStore.loadToken()) ?? ""
|
|
948
|
-
|
|
993
|
+
// Always start on Overview tab.
|
|
994
|
+
self.selectedMenuProviderRaw = nil
|
|
949
995
|
self.providerDetectionCompleted = userDefaults.object(
|
|
950
996
|
forKey: "providerDetectionCompleted") as? Bool ?? false
|
|
951
997
|
self.toggleStore = ProviderToggleStore(userDefaults: userDefaults)
|
|
@@ -956,6 +1002,7 @@ final class SettingsStore {
|
|
|
956
1002
|
if self.claudeUsageDataSource != .cli {
|
|
957
1003
|
self.claudeWebExtrasEnabled = false
|
|
958
1004
|
}
|
|
1005
|
+
RunicFont.family = self.selectedFontFamily
|
|
959
1006
|
}
|
|
960
1007
|
|
|
961
1008
|
func orderedProviders() -> [UsageProvider] {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import Security
|
|
4
4
|
|
|
5
5
|
protocol TogetherTokenStoring: Sendable {
|
|
@@ -53,29 +53,16 @@ struct KeychainTogetherTokenStore: TogetherTokenStoring {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
let data = cleaned!.data(using: .utf8)!
|
|
56
|
-
let query = self.baseQuery(dataProtection: false)
|
|
57
|
-
let attributes: [String: Any] = [
|
|
58
|
-
kSecValueData as String: data,
|
|
59
|
-
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
// Try update first.
|
|
63
|
-
let updateStatus = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
|
64
|
-
if updateStatus == errSecSuccess { return }
|
|
65
|
-
|
|
66
|
-
if updateStatus != errSecItemNotFound {
|
|
67
|
-
// Item exists but update failed — delete and re-add to reset ACL.
|
|
68
|
-
try? self.deleteToken(dataProtection: false)
|
|
69
|
-
}
|
|
70
56
|
|
|
71
|
-
|
|
72
|
-
|
|
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)
|
|
73
62
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
addQuery[kSecAttrAccess as String] = access
|
|
78
|
-
}
|
|
63
|
+
var addQuery = self.baseQuery(dataProtection: false)
|
|
64
|
+
addQuery[kSecValueData as String] = data
|
|
65
|
+
addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
|
|
79
66
|
|
|
80
67
|
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
81
68
|
guard addStatus == errSecSuccess else {
|
|
@@ -91,6 +78,7 @@ struct KeychainTogetherTokenStore: TogetherTokenStoring {
|
|
|
91
78
|
var query = self.baseQuery(dataProtection: dataProtection)
|
|
92
79
|
query[kSecMatchLimit as String] = kSecMatchLimitOne
|
|
93
80
|
query[kSecReturnData as String] = true
|
|
81
|
+
query[kSecUseAuthenticationUI as String] = kSecUseAuthenticationUIFail
|
|
94
82
|
|
|
95
83
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
96
84
|
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
@@ -126,18 +114,4 @@ struct KeychainTogetherTokenStore: TogetherTokenStoring {
|
|
|
126
114
|
}
|
|
127
115
|
return q
|
|
128
116
|
}
|
|
129
|
-
|
|
130
|
-
/// Creates a SecAccess that grants the calling application permanent
|
|
131
|
-
/// read/write access without triggering the macOS Keychain password dialog.
|
|
132
|
-
private static func createSelfAccess() -> SecAccess? {
|
|
133
|
-
var trustedSelf: SecTrustedApplication?
|
|
134
|
-
guard SecTrustedApplicationCreateFromPath(nil, &trustedSelf) == errSecSuccess,
|
|
135
|
-
let trustedSelf else { return nil }
|
|
136
|
-
var access: SecAccess?
|
|
137
|
-
guard SecAccessCreate(
|
|
138
|
-
"Runic API Token" as CFString,
|
|
139
|
-
[trustedSelf] as CFArray,
|
|
140
|
-
&access) == errSecSuccess else { return nil }
|
|
141
|
-
return access
|
|
142
|
-
}
|
|
143
117
|
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
|
|
4
4
|
extension UsageStore {
|
|
5
|
-
var codexSnapshot: UsageSnapshot? {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
var codexSnapshot: UsageSnapshot? {
|
|
6
|
+
self.snapshots[.codex]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
var claudeSnapshot: UsageSnapshot? {
|
|
10
|
+
self.snapshots[.claude]
|
|
11
|
+
}
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
var lastCodexError: String? {
|
|
14
|
+
self.errors[.codex]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var lastClaudeError: String? {
|
|
18
|
+
self.errors[.claude]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func error(for provider: UsageProvider) -> String? {
|
|
22
|
+
self.errors[provider]
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
func status(for provider: UsageProvider) -> ProviderStatus? {
|
|
13
26
|
guard self.statusChecksEnabled else { return nil }
|