@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
|
@@ -5,38 +5,38 @@ enum UsageLedgerSourceFactory {
|
|
|
5
5
|
static func source(
|
|
6
6
|
for provider: UsageProvider,
|
|
7
7
|
now: Date,
|
|
8
|
-
maxAgeDays: Int
|
|
9
|
-
|
|
8
|
+
maxAgeDays: Int) -> (any UsageLedgerSource)?
|
|
9
|
+
{
|
|
10
10
|
switch provider {
|
|
11
11
|
case .claude:
|
|
12
12
|
return ClaudeUsageLogSource(maxAgeDays: maxAgeDays, now: now)
|
|
13
13
|
case .codex:
|
|
14
14
|
return CodexUsageLogSource(maxAgeDays: maxAgeDays, now: now)
|
|
15
15
|
case .copilot,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
16
|
+
.gemini,
|
|
17
|
+
.antigravity,
|
|
18
|
+
.cursor,
|
|
19
|
+
.factory,
|
|
20
|
+
.zai,
|
|
21
|
+
.minimax,
|
|
22
|
+
.openrouter,
|
|
23
|
+
.groq,
|
|
24
|
+
.deepseek,
|
|
25
|
+
.fireworks,
|
|
26
|
+
.mistral,
|
|
27
|
+
.perplexity,
|
|
28
|
+
.kimi,
|
|
29
|
+
.auggie,
|
|
30
|
+
.together,
|
|
31
|
+
.cohere,
|
|
32
|
+
.xai,
|
|
33
|
+
.cerebras,
|
|
34
|
+
.sambanova,
|
|
35
|
+
.azure,
|
|
36
|
+
.bedrock,
|
|
37
|
+
.vertexai,
|
|
38
|
+
.qwen:
|
|
39
|
+
return self.otelHistorySource(provider: provider, now: now, maxAgeDays: maxAgeDays)
|
|
40
40
|
@unknown default:
|
|
41
41
|
return nil
|
|
42
42
|
}
|
|
@@ -45,8 +45,8 @@ enum UsageLedgerSourceFactory {
|
|
|
45
45
|
private static func otelHistorySource(
|
|
46
46
|
provider: UsageProvider,
|
|
47
47
|
now: Date,
|
|
48
|
-
maxAgeDays: Int
|
|
49
|
-
|
|
48
|
+
maxAgeDays: Int) -> (any UsageLedgerSource)?
|
|
49
|
+
{
|
|
50
50
|
let files = self.otelLedgerFiles(for: provider)
|
|
51
51
|
guard !files.isEmpty else { return nil }
|
|
52
52
|
|
|
@@ -75,7 +75,7 @@ enum UsageLedgerSourceFactory {
|
|
|
75
75
|
Self.splitPathList(env["RUNIC_\(providerKey)_OTEL_GENAI_LOG_PATH"]),
|
|
76
76
|
Self.splitPathList(env["RUNIC_\(providerKey)_OTEL_LOG_PATHS"]),
|
|
77
77
|
Self.splitPathList(env["RUNIC_\(providerKey)_OTEL_LOG_PATH"]),
|
|
78
|
-
].flatMap
|
|
78
|
+
].flatMap(\.self)
|
|
79
79
|
|
|
80
80
|
let urls = candidatePaths.compactMap(Self.expandTildePath)
|
|
81
81
|
return Self.discoverOTelLedgerFiles(from: urls)
|
|
@@ -135,8 +135,7 @@ enum UsageLedgerSourceFactory {
|
|
|
135
135
|
guard let enumerator = FileManager.default.enumerator(
|
|
136
136
|
at: directory,
|
|
137
137
|
includingPropertiesForKeys: [.isRegularFileKey, .isDirectoryKey],
|
|
138
|
-
options: [.skipsHiddenFiles]
|
|
139
|
-
) else { return [] }
|
|
138
|
+
options: [.skipsHiddenFiles]) else { return [] }
|
|
140
139
|
|
|
141
140
|
var files: [URL] = []
|
|
142
141
|
for case let file as URL in enumerator where Self.isSupportedOTelFile(file) {
|
|
@@ -145,7 +144,7 @@ enum UsageLedgerSourceFactory {
|
|
|
145
144
|
return files
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
private struct UsageLedgerProviderFilterSource: UsageLedgerSource
|
|
147
|
+
private struct UsageLedgerProviderFilterSource: UsageLedgerSource {
|
|
149
148
|
private let source: any UsageLedgerSource
|
|
150
149
|
private let provider: UsageProvider
|
|
151
150
|
private let minTimestamp: Date?
|
|
@@ -2,7 +2,7 @@ import Foundation
|
|
|
2
2
|
import RunicCore
|
|
3
3
|
|
|
4
4
|
/// Storage for alert rules, alert history, and webhook configurations
|
|
5
|
-
public
|
|
5
|
+
public enum AlertRuleStore {
|
|
6
6
|
// MARK: - Types
|
|
7
7
|
|
|
8
8
|
public enum AlertType: String, Codable, Sendable {
|
|
@@ -40,8 +40,8 @@ public struct AlertRuleStore {
|
|
|
40
40
|
notifyWebhook: Bool = false,
|
|
41
41
|
webhookURL: String? = nil,
|
|
42
42
|
enabled: Bool = true,
|
|
43
|
-
createdAt: Date = Date()
|
|
44
|
-
|
|
43
|
+
createdAt: Date = Date())
|
|
44
|
+
{
|
|
45
45
|
self.id = id
|
|
46
46
|
self.type = type
|
|
47
47
|
self.projectID = projectID
|
|
@@ -71,8 +71,8 @@ public struct AlertRuleStore {
|
|
|
71
71
|
message: String,
|
|
72
72
|
severity: AlertSeverity,
|
|
73
73
|
acknowledged: Bool = false,
|
|
74
|
-
acknowledgedAt: Date? = nil
|
|
75
|
-
|
|
74
|
+
acknowledgedAt: Date? = nil)
|
|
75
|
+
{
|
|
76
76
|
self.id = id
|
|
77
77
|
self.alertID = alertID
|
|
78
78
|
self.triggeredAt = triggeredAt
|
|
@@ -107,7 +107,7 @@ public struct AlertRuleStore {
|
|
|
107
107
|
// MARK: - Public Methods
|
|
108
108
|
|
|
109
109
|
public static func load() -> AlertsData {
|
|
110
|
-
guard FileManager.default.fileExists(atPath: storageURL.path) else {
|
|
110
|
+
guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
|
|
111
111
|
return AlertsData()
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -128,35 +128,35 @@ public struct AlertRuleStore {
|
|
|
128
128
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
129
129
|
|
|
130
130
|
let data = try encoder.encode(alertsData)
|
|
131
|
-
try data.write(to: storageURL, options: .atomic)
|
|
131
|
+
try data.write(to: self.storageURL, options: .atomic)
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
public static func addRule(_ rule: AlertRule) throws {
|
|
135
|
-
var data = load()
|
|
135
|
+
var data = self.load()
|
|
136
136
|
data.rules.append(rule)
|
|
137
|
-
try save(data)
|
|
137
|
+
try self.save(data)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
public static func removeRule(id: String) throws {
|
|
141
|
-
var data = load()
|
|
141
|
+
var data = self.load()
|
|
142
142
|
data.rules.removeAll { $0.id == id }
|
|
143
|
-
try save(data)
|
|
143
|
+
try self.save(data)
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
public static func updateRule(_ rule: AlertRule) throws {
|
|
147
|
-
var data = load()
|
|
147
|
+
var data = self.load()
|
|
148
148
|
if let index = data.rules.firstIndex(where: { $0.id == rule.id }) {
|
|
149
149
|
data.rules[index] = rule
|
|
150
|
-
try save(data)
|
|
150
|
+
try self.save(data)
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
public static func getEnabledRules() -> [AlertRule] {
|
|
155
|
-
load().rules.filter
|
|
155
|
+
self.load().rules.filter(\.enabled)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
public static func addHistoryEntry(_ entry: AlertHistoryEntry) throws {
|
|
159
|
-
var data = load()
|
|
159
|
+
var data = self.load()
|
|
160
160
|
data.history.append(entry)
|
|
161
161
|
|
|
162
162
|
// Keep last 100 history entries
|
|
@@ -164,24 +164,24 @@ public struct AlertRuleStore {
|
|
|
164
164
|
data.history = Array(data.history.suffix(100))
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
try save(data)
|
|
167
|
+
try self.save(data)
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
public static func acknowledgeAlert(id: String) throws {
|
|
171
|
-
var data = load()
|
|
171
|
+
var data = self.load()
|
|
172
172
|
if let index = data.history.firstIndex(where: { $0.id == id }) {
|
|
173
173
|
data.history[index].acknowledged = true
|
|
174
174
|
data.history[index].acknowledgedAt = Date()
|
|
175
|
-
try save(data)
|
|
175
|
+
try self.save(data)
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
public static func getRecentHistory(limit: Int = 20) -> [AlertHistoryEntry] {
|
|
180
|
-
let history = load().history.sorted { $0.triggeredAt > $1.triggeredAt }
|
|
180
|
+
let history = self.load().history.sorted { $0.triggeredAt > $1.triggeredAt }
|
|
181
181
|
return Array(history.prefix(limit))
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
public static func getUnacknowledgedAlerts() -> [AlertHistoryEntry] {
|
|
185
|
-
load().history.filter { !$0.acknowledged }
|
|
185
|
+
self.load().history.filter { !$0.acknowledged }
|
|
186
186
|
}
|
|
187
187
|
}
|
|
@@ -3,15 +3,15 @@ import Silo
|
|
|
3
3
|
|
|
4
4
|
public typealias BrowserCookieImportOrder = [Browser]
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
extension Collection<Browser> {
|
|
7
7
|
/// Returns a user-friendly hint about which browsers to log in to.
|
|
8
|
-
var loginHint: String {
|
|
8
|
+
public var loginHint: String {
|
|
9
9
|
if self.isEmpty {
|
|
10
10
|
return "your browser"
|
|
11
11
|
} else if self.count == 1, let first = self.first {
|
|
12
12
|
return first.displayName
|
|
13
13
|
} else {
|
|
14
|
-
let names = self.map
|
|
14
|
+
let names = self.map(\.displayName)
|
|
15
15
|
return names.dropLast().joined(separator: ", ") + " or " + (names.last ?? "")
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -377,7 +377,7 @@ public struct CostUsageMonthlyReport: Sendable, Decodable {
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
private struct CostUsageLegacyTotals:
|
|
380
|
+
private struct CostUsageLegacyTotals: Decodable {
|
|
381
381
|
let totalInputTokens: Int?
|
|
382
382
|
let totalOutputTokens: Int?
|
|
383
383
|
let cacheReadTokens: Int?
|
|
@@ -23,8 +23,8 @@ public struct CustomProviderConfig: Codable, Sendable, Identifiable {
|
|
|
23
23
|
endpoints: EndpointConfig,
|
|
24
24
|
colorHex: String? = nil,
|
|
25
25
|
createdAt: Date = Date(),
|
|
26
|
-
updatedAt: Date = Date()
|
|
27
|
-
|
|
26
|
+
updatedAt: Date = Date())
|
|
27
|
+
{
|
|
28
28
|
self.id = id
|
|
29
29
|
self.name = name
|
|
30
30
|
self.icon = icon
|
|
@@ -43,10 +43,10 @@ public struct CustomProviderConfig: Codable, Sendable, Identifiable {
|
|
|
43
43
|
public struct AuthConfig: Codable, Sendable {
|
|
44
44
|
public enum AuthType: String, Codable, Sendable {
|
|
45
45
|
case apiKey = "api_key"
|
|
46
|
-
case bearer
|
|
47
|
-
case basic
|
|
48
|
-
case oauth
|
|
49
|
-
case custom
|
|
46
|
+
case bearer
|
|
47
|
+
case basic
|
|
48
|
+
case oauth
|
|
49
|
+
case custom
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
public let type: AuthType
|
|
@@ -60,8 +60,8 @@ public struct AuthConfig: Codable, Sendable {
|
|
|
60
60
|
headerName: String,
|
|
61
61
|
headerPrefix: String? = nil,
|
|
62
62
|
tokenKeychain: String,
|
|
63
|
-
additionalHeaders: [String: String]? = nil
|
|
64
|
-
|
|
63
|
+
additionalHeaders: [String: String]? = nil)
|
|
64
|
+
{
|
|
65
65
|
self.type = type
|
|
66
66
|
self.headerName = headerName
|
|
67
67
|
self.headerPrefix = headerPrefix
|
|
@@ -79,8 +79,8 @@ public struct EndpointConfig: Codable, Sendable {
|
|
|
79
79
|
|
|
80
80
|
public init(
|
|
81
81
|
usage: UsageEndpoint? = nil,
|
|
82
|
-
balance: BalanceEndpoint? = nil
|
|
83
|
-
|
|
82
|
+
balance: BalanceEndpoint? = nil)
|
|
83
|
+
{
|
|
84
84
|
self.usage = usage
|
|
85
85
|
self.balance = balance
|
|
86
86
|
}
|
|
@@ -99,8 +99,8 @@ public struct UsageEndpoint: Codable, Sendable {
|
|
|
99
99
|
url: String,
|
|
100
100
|
method: HTTPMethod = .GET,
|
|
101
101
|
mapping: ResponseMapping,
|
|
102
|
-
queryParams: [String: String]? = nil
|
|
103
|
-
|
|
102
|
+
queryParams: [String: String]? = nil)
|
|
103
|
+
{
|
|
104
104
|
self.url = url
|
|
105
105
|
self.method = method
|
|
106
106
|
self.mapping = mapping
|
|
@@ -119,8 +119,8 @@ public struct BalanceEndpoint: Codable, Sendable {
|
|
|
119
119
|
public init(
|
|
120
120
|
url: String,
|
|
121
121
|
method: HTTPMethod = .GET,
|
|
122
|
-
mapping: ResponseMapping
|
|
123
|
-
|
|
122
|
+
mapping: ResponseMapping)
|
|
123
|
+
{
|
|
124
124
|
self.url = url
|
|
125
125
|
self.method = method
|
|
126
126
|
self.mapping = mapping
|
|
@@ -146,7 +146,7 @@ public struct ResponseMapping: Codable, Sendable {
|
|
|
146
146
|
public var resetDate: String? // JSONPath to reset date
|
|
147
147
|
public var tokens: String? // JSONPath to token count
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
/// Nested object support (e.g., "data.usage.tokens")
|
|
150
150
|
public var nestedPaths: Bool // Support dot notation
|
|
151
151
|
|
|
152
152
|
public init(
|
|
@@ -156,8 +156,8 @@ public struct ResponseMapping: Codable, Sendable {
|
|
|
156
156
|
cost: String? = nil,
|
|
157
157
|
resetDate: String? = nil,
|
|
158
158
|
tokens: String? = nil,
|
|
159
|
-
nestedPaths: Bool = true
|
|
160
|
-
|
|
159
|
+
nestedPaths: Bool = true)
|
|
160
|
+
{
|
|
161
161
|
self.quota = quota
|
|
162
162
|
self.used = used
|
|
163
163
|
self.remaining = remaining
|
|
@@ -185,8 +185,8 @@ public struct CustomUsageData: Codable, Sendable {
|
|
|
185
185
|
remaining: Double? = nil,
|
|
186
186
|
cost: Double? = nil,
|
|
187
187
|
resetDate: Date? = nil,
|
|
188
|
-
tokens: Int? = nil
|
|
189
|
-
|
|
188
|
+
tokens: Int? = nil)
|
|
189
|
+
{
|
|
190
190
|
self.quota = quota
|
|
191
191
|
self.used = used
|
|
192
192
|
self.remaining = remaining
|
|
@@ -211,8 +211,8 @@ public struct CustomProviderSnapshot: Codable, Sendable {
|
|
|
211
211
|
providerName: String,
|
|
212
212
|
usageData: CustomUsageData,
|
|
213
213
|
updatedAt: Date = Date(),
|
|
214
|
-
error: String? = nil
|
|
215
|
-
|
|
214
|
+
error: String? = nil)
|
|
215
|
+
{
|
|
216
216
|
self.providerID = providerID
|
|
217
217
|
self.providerName = providerName
|
|
218
218
|
self.usageData = usageData
|
|
@@ -223,35 +223,34 @@ public struct CustomProviderSnapshot: Codable, Sendable {
|
|
|
223
223
|
/// Convert to RateWindow for UI compatibility
|
|
224
224
|
public func toRateWindow() -> RateWindow? {
|
|
225
225
|
guard let quota = usageData.quota,
|
|
226
|
-
let used = usageData.used
|
|
226
|
+
let used = usageData.used
|
|
227
|
+
else {
|
|
227
228
|
return nil
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
let usedPercent = quota > 0 ? (used / quota) * 100.0 : 0.0
|
|
231
232
|
let windowMinutes: Int? = nil // Custom providers might not have window concept
|
|
232
|
-
let resetsAt = usageData.resetDate
|
|
233
|
+
let resetsAt = self.usageData.resetDate
|
|
233
234
|
let resetDescription = resetsAt.map { UsageFormatter.resetDescription(from: $0) }
|
|
234
235
|
|
|
235
236
|
return RateWindow(
|
|
236
237
|
usedPercent: usedPercent,
|
|
237
238
|
windowMinutes: windowMinutes,
|
|
238
239
|
resetsAt: resetsAt,
|
|
239
|
-
resetDescription: resetDescription
|
|
240
|
-
)
|
|
240
|
+
resetDescription: resetDescription)
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
/// Create snapshot from usage data and config
|
|
244
244
|
public static func from(
|
|
245
245
|
usageData: CustomUsageData,
|
|
246
|
-
config: CustomProviderConfig
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
config: CustomProviderConfig) -> CustomProviderSnapshot
|
|
247
|
+
{
|
|
248
|
+
CustomProviderSnapshot(
|
|
249
249
|
providerID: config.id,
|
|
250
250
|
providerName: config.name,
|
|
251
251
|
usageData: usageData,
|
|
252
252
|
updatedAt: Date(),
|
|
253
|
-
error: nil
|
|
254
|
-
)
|
|
253
|
+
error: nil)
|
|
255
254
|
}
|
|
256
255
|
}
|
|
257
256
|
|