@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
|
@@ -2,10 +2,9 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import Runic
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct LoadingPatternTests {
|
|
7
6
|
@Test
|
|
8
|
-
func
|
|
7
|
+
func `values stay within bounds`() {
|
|
9
8
|
for pattern in LoadingPattern.allCases {
|
|
10
9
|
for phase in stride(from: 0.0, through: Double.pi * 2, by: Double.pi / 6) {
|
|
11
10
|
let v = pattern.value(phase: phase)
|
|
@@ -15,7 +14,7 @@ struct LoadingPatternTests {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
@Test
|
|
18
|
-
func
|
|
17
|
+
func `knight rider ping pongs`() {
|
|
19
18
|
let pattern = LoadingPattern.knightRider
|
|
20
19
|
let mid = pattern.value(phase: 0) // sin 0 = 0 => 50
|
|
21
20
|
let min = pattern.value(phase: -Double.pi / 2) // sin -pi/2 = -1 => 0
|
|
@@ -26,7 +25,7 @@ struct LoadingPatternTests {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
@Test
|
|
29
|
-
func
|
|
28
|
+
func `secondary offset differs`() {
|
|
30
29
|
let pattern = LoadingPattern.cylon
|
|
31
30
|
let primary = pattern.value(phase: 0)
|
|
32
31
|
let secondary = pattern.value(phase: pattern.secondaryOffset)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import SwiftUI
|
|
4
4
|
import Testing
|
|
5
5
|
@testable import Runic
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
init(
|
|
7
|
+
extension UsageMenuCardView.Model.Input {
|
|
8
|
+
fileprivate init(
|
|
9
9
|
provider: UsageProvider,
|
|
10
10
|
metadata: ProviderMetadata,
|
|
11
11
|
snapshot: UsageSnapshot?,
|
|
@@ -30,7 +30,7 @@ private extension UsageMenuCardView.Model.Input {
|
|
|
30
30
|
isRefreshing: Bool,
|
|
31
31
|
lastError: String?,
|
|
32
32
|
usageBarsShowUsed: Bool,
|
|
33
|
-
menuMode: MenuMode =
|
|
33
|
+
menuMode: MenuMode = .operator,
|
|
34
34
|
tokenCostUsageEnabled: Bool,
|
|
35
35
|
showOptionalCreditsAndExtraUsage: Bool,
|
|
36
36
|
now: Date)
|
|
@@ -68,10 +68,9 @@ private extension UsageMenuCardView.Model.Input {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
@Suite
|
|
72
71
|
struct MenuCardModelTests {
|
|
73
72
|
@Test
|
|
74
|
-
func
|
|
73
|
+
func `builds metrics using remaining percent`() throws {
|
|
75
74
|
let now = Date()
|
|
76
75
|
let identity = ProviderIdentitySnapshot(
|
|
77
76
|
providerID: .codex,
|
|
@@ -91,12 +90,12 @@ struct MenuCardModelTests {
|
|
|
91
90
|
resetDescription: nil),
|
|
92
91
|
updatedAt: now,
|
|
93
92
|
identity: identity)
|
|
94
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
95
|
-
let updatedSnap = UsageSnapshot(
|
|
93
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
94
|
+
let updatedSnap = try UsageSnapshot(
|
|
96
95
|
primary: snapshot.primary,
|
|
97
96
|
secondary: RateWindow(
|
|
98
|
-
usedPercent: snapshot.secondary
|
|
99
|
-
windowMinutes: snapshot.secondary
|
|
97
|
+
usedPercent: #require(snapshot.secondary?.usedPercent),
|
|
98
|
+
windowMinutes: #require(snapshot.secondary?.windowMinutes),
|
|
100
99
|
resetsAt: now.addingTimeInterval(3600),
|
|
101
100
|
resetDescription: nil),
|
|
102
101
|
tertiary: snapshot.tertiary,
|
|
@@ -131,7 +130,7 @@ struct MenuCardModelTests {
|
|
|
131
130
|
}
|
|
132
131
|
|
|
133
132
|
@Test
|
|
134
|
-
func
|
|
133
|
+
func `builds metrics using used percent when enabled`() throws {
|
|
135
134
|
let now = Date()
|
|
136
135
|
let identity = ProviderIdentitySnapshot(
|
|
137
136
|
providerID: .codex,
|
|
@@ -151,7 +150,7 @@ struct MenuCardModelTests {
|
|
|
151
150
|
resetDescription: nil),
|
|
152
151
|
updatedAt: now,
|
|
153
152
|
identity: identity)
|
|
154
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
153
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
155
154
|
|
|
156
155
|
let dashboard = OpenAIDashboardSnapshot(
|
|
157
156
|
signedInEmail: "codex@example.com",
|
|
@@ -187,7 +186,7 @@ struct MenuCardModelTests {
|
|
|
187
186
|
}
|
|
188
187
|
|
|
189
188
|
@Test
|
|
190
|
-
func
|
|
189
|
+
func `shows code review metric when dashboard present`() throws {
|
|
191
190
|
let now = Date()
|
|
192
191
|
let identity = ProviderIdentitySnapshot(
|
|
193
192
|
providerID: .codex,
|
|
@@ -200,7 +199,7 @@ struct MenuCardModelTests {
|
|
|
200
199
|
tertiary: nil,
|
|
201
200
|
updatedAt: now,
|
|
202
201
|
identity: identity)
|
|
203
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
202
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
204
203
|
|
|
205
204
|
let dashboard = OpenAIDashboardSnapshot(
|
|
206
205
|
signedInEmail: "codex@example.com",
|
|
@@ -232,7 +231,7 @@ struct MenuCardModelTests {
|
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
@Test
|
|
235
|
-
func
|
|
234
|
+
func `claude model hides weekly when unavailable`() throws {
|
|
236
235
|
let now = Date()
|
|
237
236
|
let identity = ProviderIdentitySnapshot(
|
|
238
237
|
providerID: .claude,
|
|
@@ -249,7 +248,7 @@ struct MenuCardModelTests {
|
|
|
249
248
|
tertiary: nil,
|
|
250
249
|
updatedAt: now,
|
|
251
250
|
identity: identity)
|
|
252
|
-
let metadata = ProviderDefaults.metadata[.claude]
|
|
251
|
+
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
253
252
|
let model = UsageMenuCardView.Model.make(.init(
|
|
254
253
|
provider: .claude,
|
|
255
254
|
metadata: metadata,
|
|
@@ -274,8 +273,8 @@ struct MenuCardModelTests {
|
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
@Test
|
|
277
|
-
func
|
|
278
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
276
|
+
func `shows error subtitle when present`() throws {
|
|
277
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
279
278
|
let model = UsageMenuCardView.Model.make(.init(
|
|
280
279
|
provider: .codex,
|
|
281
280
|
metadata: metadata,
|
|
@@ -300,9 +299,9 @@ struct MenuCardModelTests {
|
|
|
300
299
|
}
|
|
301
300
|
|
|
302
301
|
@Test
|
|
303
|
-
func
|
|
302
|
+
func `cost section includes last30 days tokens`() throws {
|
|
304
303
|
let now = Date()
|
|
305
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
304
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
306
305
|
let snapshot = UsageSnapshot(
|
|
307
306
|
primary: RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
|
|
308
307
|
secondary: nil,
|
|
@@ -338,8 +337,8 @@ struct MenuCardModelTests {
|
|
|
338
337
|
}
|
|
339
338
|
|
|
340
339
|
@Test
|
|
341
|
-
func
|
|
342
|
-
let metadata = ProviderDefaults.metadata[.claude]
|
|
340
|
+
func `claude model does not leak codex plan`() throws {
|
|
341
|
+
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
343
342
|
let model = UsageMenuCardView.Model.make(.init(
|
|
344
343
|
provider: .claude,
|
|
345
344
|
metadata: metadata,
|
|
@@ -363,7 +362,7 @@ struct MenuCardModelTests {
|
|
|
363
362
|
}
|
|
364
363
|
|
|
365
364
|
@Test
|
|
366
|
-
func
|
|
365
|
+
func `hides codex credits when disabled`() throws {
|
|
367
366
|
let now = Date()
|
|
368
367
|
let identity = ProviderIdentitySnapshot(
|
|
369
368
|
providerID: .codex,
|
|
@@ -376,7 +375,7 @@ struct MenuCardModelTests {
|
|
|
376
375
|
tertiary: nil,
|
|
377
376
|
updatedAt: now,
|
|
378
377
|
identity: identity)
|
|
379
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
378
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
380
379
|
|
|
381
380
|
let model = UsageMenuCardView.Model.make(.init(
|
|
382
381
|
provider: .codex,
|
|
@@ -400,7 +399,7 @@ struct MenuCardModelTests {
|
|
|
400
399
|
}
|
|
401
400
|
|
|
402
401
|
@Test
|
|
403
|
-
func
|
|
402
|
+
func `hides claude extra usage when disabled`() throws {
|
|
404
403
|
let now = Date()
|
|
405
404
|
let identity = ProviderIdentitySnapshot(
|
|
406
405
|
providerID: .claude,
|
|
@@ -414,7 +413,7 @@ struct MenuCardModelTests {
|
|
|
414
413
|
providerCost: ProviderCostSnapshot(used: 12, limit: 200, currencyCode: "USD", updatedAt: now),
|
|
415
414
|
updatedAt: now,
|
|
416
415
|
identity: identity)
|
|
417
|
-
let metadata = ProviderDefaults.metadata[.claude]
|
|
416
|
+
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
418
417
|
|
|
419
418
|
let model = UsageMenuCardView.Model.make(.init(
|
|
420
419
|
provider: .claude,
|
|
@@ -438,9 +437,9 @@ struct MenuCardModelTests {
|
|
|
438
437
|
}
|
|
439
438
|
|
|
440
439
|
@Test
|
|
441
|
-
func
|
|
440
|
+
func `insights show forecast and budget ETA for top project`() throws {
|
|
442
441
|
let now = Date()
|
|
443
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
442
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
444
443
|
let topProject = UsageLedgerProjectSummary(
|
|
445
444
|
provider: .codex,
|
|
446
445
|
projectKey: "id:proj-runic",
|
|
@@ -448,8 +447,8 @@ struct MenuCardModelTests {
|
|
|
448
447
|
projectName: "Runic",
|
|
449
448
|
entryCount: 5,
|
|
450
449
|
totals: UsageLedgerTotals(
|
|
451
|
-
inputTokens:
|
|
452
|
-
outputTokens:
|
|
450
|
+
inputTokens: 2000,
|
|
451
|
+
outputTokens: 1000,
|
|
453
452
|
cacheCreationTokens: 0,
|
|
454
453
|
cacheReadTokens: 0,
|
|
455
454
|
costUSD: 12.5),
|
|
@@ -503,16 +502,16 @@ struct MenuCardModelTests {
|
|
|
503
502
|
}
|
|
504
503
|
|
|
505
504
|
@Test
|
|
506
|
-
func
|
|
507
|
-
let metadata = ProviderDefaults.metadata[.codex]
|
|
505
|
+
func `insights surface anomaly severity line`() throws {
|
|
506
|
+
let metadata = try #require(ProviderDefaults.metadata[.codex])
|
|
508
507
|
let anomaly = UsageLedgerAnomalySummary(
|
|
509
508
|
provider: .codex,
|
|
510
509
|
baselineDays: 7,
|
|
511
510
|
tokenAnomaly: UsageLedgerAnomalySummary.MetricAnomaly(
|
|
512
511
|
metric: .tokens,
|
|
513
512
|
severity: .high,
|
|
514
|
-
todayValue:
|
|
515
|
-
baselineAverage:
|
|
513
|
+
todayValue: 4200,
|
|
514
|
+
baselineAverage: 1500,
|
|
516
515
|
percentIncrease: 1.8),
|
|
517
516
|
spendAnomaly: UsageLedgerAnomalySummary.MetricAnomaly(
|
|
518
517
|
metric: .spend,
|
|
@@ -2,10 +2,9 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import RunicCore
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct MiniMaxParsingTests {
|
|
7
6
|
@Test
|
|
8
|
-
func
|
|
7
|
+
func `parse manual input cookie header`() {
|
|
9
8
|
let input = "Cookie: foo=bar; baz=qux"
|
|
10
9
|
let parsed = MiniMaxWebParsing.parseManualInput(input)
|
|
11
10
|
#expect(parsed?.cookieHeader == "foo=bar; baz=qux")
|
|
@@ -13,7 +12,7 @@ struct MiniMaxParsingTests {
|
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
@Test
|
|
16
|
-
func
|
|
15
|
+
func `parse manual input curl extracts bearer and group ID`() {
|
|
17
16
|
let curl = """
|
|
18
17
|
curl 'https://platform.minimax.io/v1/api/openplatform/coding_plan/remains?GroupId=group_123' \
|
|
19
18
|
-H 'Cookie: session=abc; foo=bar' \
|
|
@@ -26,7 +25,7 @@ struct MiniMaxParsingTests {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
@Test
|
|
29
|
-
func
|
|
28
|
+
func `parse HTML usage with percent and reset`() {
|
|
30
29
|
let html = """
|
|
31
30
|
<html>
|
|
32
31
|
<head><title>MiniMax Pro</title></head>
|
|
@@ -45,7 +44,7 @@ struct MiniMaxParsingTests {
|
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
@Test
|
|
48
|
-
func
|
|
47
|
+
func `parse remains response uses model remains`() throws {
|
|
49
48
|
let json = """
|
|
50
49
|
{
|
|
51
50
|
"base_resp": { "retcode": 0, "msg": "ok", "success": true },
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import Testing
|
|
3
|
-
|
|
4
3
|
@testable import RunicCore
|
|
5
4
|
|
|
6
|
-
@Suite
|
|
7
5
|
struct OTelGenAILedgerAdapterTests {
|
|
8
6
|
@Test
|
|
9
|
-
func
|
|
7
|
+
func `parses flat gen AI payload into ledger entry`() throws {
|
|
10
8
|
let payload = """
|
|
11
9
|
{
|
|
12
10
|
"timestamp": "2026-02-23T12:00:00Z",
|
|
@@ -40,7 +38,7 @@ struct OTelGenAILedgerAdapterTests {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
@Test
|
|
43
|
-
func
|
|
41
|
+
func `parses OTLP resource spans payload`() throws {
|
|
44
42
|
let payload = """
|
|
45
43
|
{
|
|
46
44
|
"resourceSpans": [
|
|
@@ -86,7 +84,7 @@ struct OTelGenAILedgerAdapterTests {
|
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
@Test
|
|
89
|
-
func
|
|
87
|
+
func `feature flag disables ingestion`() throws {
|
|
90
88
|
let payload = """
|
|
91
89
|
{"attributes":{"gen_ai.system":"openai","gen_ai.request.model":"gpt-5","gen_ai.usage.input_tokens":1}}
|
|
92
90
|
"""
|
|
@@ -97,7 +95,7 @@ struct OTelGenAILedgerAdapterTests {
|
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
@Test
|
|
100
|
-
func
|
|
98
|
+
func `default provider backfills unknown provider`() throws {
|
|
101
99
|
let payload = """
|
|
102
100
|
{"attributes":{"gen_ai.request.model":"custom-model-v1","gen_ai.usage.input_tokens":9}}
|
|
103
101
|
"""
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import RunicCore
|
|
2
2
|
import Testing
|
|
3
3
|
|
|
4
|
-
@Suite
|
|
5
4
|
struct OpenAIDashboardBrowserCookieImporterTests {
|
|
6
5
|
@Test
|
|
7
|
-
func
|
|
6
|
+
func `mismatch error mentions source label`() {
|
|
8
7
|
let err = OpenAIDashboardBrowserCookieImporter.ImportError.noMatchingAccount(
|
|
9
8
|
found: [
|
|
10
9
|
.init(sourceLabel: "Safari", email: "a@example.com"),
|
|
@@ -2,10 +2,9 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import RunicCore
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct OpenAIDashboardFetcherCreditsWaitTests {
|
|
7
6
|
@Test
|
|
8
|
-
func
|
|
7
|
+
func `waits after scroll request`() {
|
|
9
8
|
let now = Date()
|
|
10
9
|
let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
|
|
11
10
|
now: now,
|
|
@@ -18,7 +17,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
@Test
|
|
21
|
-
func
|
|
20
|
+
func `waits briefly when header visible but table empty`() {
|
|
22
21
|
let now = Date()
|
|
23
22
|
let visibleAt = now.addingTimeInterval(-1.0)
|
|
24
23
|
let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
|
|
@@ -32,7 +31,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
@Test
|
|
35
|
-
func
|
|
34
|
+
func `stops waiting after header has been visible long enough`() {
|
|
36
35
|
let now = Date()
|
|
37
36
|
let visibleAt = now.addingTimeInterval(-3.0)
|
|
38
37
|
let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
|
|
@@ -46,7 +45,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
@Test
|
|
49
|
-
func
|
|
48
|
+
func `waits briefly after first dashboard signal even when header not present yet`() {
|
|
50
49
|
let now = Date()
|
|
51
50
|
let startedAt = now.addingTimeInterval(-2.0)
|
|
52
51
|
let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
|
|
@@ -60,7 +59,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
@Test
|
|
63
|
-
func
|
|
62
|
+
func `stops waiting eventually when header never appears`() {
|
|
64
63
|
let now = Date()
|
|
65
64
|
let startedAt = now.addingTimeInterval(-7.0)
|
|
66
65
|
let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
|
|
@@ -2,10 +2,9 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import RunicCore
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct OpenAIDashboardOffscreenHostTests {
|
|
7
6
|
@Test
|
|
8
|
-
func
|
|
7
|
+
func `offscreen host frame only intersects by A sliver`() {
|
|
9
8
|
let visibleFrame = CGRect(x: 0, y: 0, width: 1000, height: 800)
|
|
10
9
|
let frame = OpenAIDashboardFetcher.offscreenHostWindowFrame(for: visibleFrame)
|
|
11
10
|
let intersection = frame.intersection(visibleFrame)
|
|
@@ -19,7 +18,7 @@ struct OpenAIDashboardOffscreenHostTests {
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
@Test
|
|
22
|
-
func
|
|
21
|
+
func `offscreen host alpha value is non zero but tiny`() {
|
|
23
22
|
let alpha = OpenAIDashboardFetcher.offscreenHostAlphaValue()
|
|
24
23
|
#expect(alpha > 0)
|
|
25
24
|
#expect(alpha <= 0.001)
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import Testing
|
|
4
4
|
@testable import Runic
|
|
5
5
|
|
|
6
|
-
@Suite
|
|
7
6
|
struct OpenAIDashboardParserTests {
|
|
8
7
|
@Test
|
|
9
|
-
func
|
|
8
|
+
func `parses signed in email from client bootstrap HTML`() {
|
|
10
9
|
let html = """
|
|
11
10
|
<html>
|
|
12
11
|
<head></head>
|
|
@@ -22,26 +21,26 @@ struct OpenAIDashboardParserTests {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
@Test
|
|
25
|
-
func
|
|
24
|
+
func `parses code review remaining percent inline`() {
|
|
26
25
|
let body = "Balance\nCode review 42% remaining\nCredits remaining 291"
|
|
27
26
|
#expect(OpenAIDashboardParser.parseCodeReviewRemainingPercent(bodyText: body) == 42)
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
@Test
|
|
31
|
-
func
|
|
30
|
+
func `parses code review remaining percent multiline`() {
|
|
32
31
|
let body = "Balance\nCode review\n100% remaining\nWeekly usage limit\n0% remaining"
|
|
33
32
|
#expect(OpenAIDashboardParser.parseCodeReviewRemainingPercent(bodyText: body) == 100)
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
@Test
|
|
37
|
-
func
|
|
36
|
+
func `parses credits remaining`() {
|
|
38
37
|
let body = "Balance\nCredits remaining 1,234.56\nUsage"
|
|
39
38
|
let value = OpenAIDashboardParser.parseCreditsRemaining(bodyText: body)
|
|
40
39
|
#expect(abs((value ?? 0) - 1234.56) < 0.001)
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
@Test
|
|
44
|
-
func
|
|
43
|
+
func `parses rate limits`() {
|
|
45
44
|
let body = """
|
|
46
45
|
Usage limits
|
|
47
46
|
5h limit
|
|
@@ -60,7 +59,7 @@ struct OpenAIDashboardParserTests {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
@Test
|
|
63
|
-
func
|
|
62
|
+
func `parses plan from client bootstrap`() {
|
|
64
63
|
let html = """
|
|
65
64
|
<html>
|
|
66
65
|
<body>
|
|
@@ -74,7 +73,7 @@ struct OpenAIDashboardParserTests {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
@Test
|
|
77
|
-
func
|
|
76
|
+
func `parses credit events from table rows`() {
|
|
78
77
|
let rows: [[String]] = [
|
|
79
78
|
["Dec 18, 2025", "CLI", "397.205 credits"],
|
|
80
79
|
["Dec 17, 2025", "GitHub Code Review", "506.235 credits"],
|
|
@@ -88,7 +87,7 @@ struct OpenAIDashboardParserTests {
|
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
@Test
|
|
91
|
-
func
|
|
90
|
+
func `builds daily breakdown from events`() throws {
|
|
92
91
|
let calendar = Calendar(identifier: .gregorian)
|
|
93
92
|
var components = DateComponents()
|
|
94
93
|
components.calendar = calendar
|
|
@@ -97,10 +96,10 @@ struct OpenAIDashboardParserTests {
|
|
|
97
96
|
components.year = 2025
|
|
98
97
|
components.month = 12
|
|
99
98
|
components.day = 18
|
|
100
|
-
let dec18 = components.date
|
|
99
|
+
let dec18 = try #require(components.date)
|
|
101
100
|
|
|
102
101
|
components.day = 17
|
|
103
|
-
let dec17 = components.date
|
|
102
|
+
let dec17 = try #require(components.date)
|
|
104
103
|
|
|
105
104
|
let events = [
|
|
106
105
|
CreditEvent(date: dec18, service: "CLI", creditsUsed: 10),
|
|
@@ -116,7 +115,7 @@ struct OpenAIDashboardParserTests {
|
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
@Test
|
|
119
|
-
func
|
|
118
|
+
func `decodes snapshot without usage breakdown field`() throws {
|
|
120
119
|
let json = """
|
|
121
120
|
{
|
|
122
121
|
"signedInEmail": "user@example.com",
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import Testing
|
|
4
4
|
@testable import Runic
|
|
5
5
|
|
|
6
6
|
@MainActor
|
|
7
|
-
@Suite
|
|
8
7
|
struct OpenAIWebAccountSwitchTests {
|
|
9
8
|
@Test
|
|
10
|
-
func
|
|
11
|
-
let settings = SettingsStore(
|
|
9
|
+
func `clears dashboard when codex email changes`() {
|
|
10
|
+
let settings = SettingsStore(
|
|
11
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
12
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
13
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
14
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
15
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
16
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
12
17
|
settings.refreshFrequency = .manual
|
|
13
18
|
|
|
14
19
|
let store = UsageStore(fetcher: UsageFetcher(), settings: settings)
|
|
@@ -30,8 +35,14 @@ struct OpenAIWebAccountSwitchTests {
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
@Test
|
|
33
|
-
func
|
|
34
|
-
let settings = SettingsStore(
|
|
38
|
+
func `keeps dashboard when codex email stays same`() {
|
|
39
|
+
let settings = SettingsStore(
|
|
40
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
41
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
42
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
43
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
44
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
45
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
35
46
|
settings.refreshFrequency = .manual
|
|
36
47
|
|
|
37
48
|
let store = UsageStore(fetcher: UsageFetcher(), settings: settings)
|