@sriinnu/runic 1.2.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/README.md +8 -1
- 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/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
- 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 +18 -13
- 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 +1 -1
- 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 +1 -3
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
- 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 +187 -161
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
- 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/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 +12 -10
- 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 +18 -15
- 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 +22 -17
- 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 +44 -39
- 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 +73 -75
- 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/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 +3 -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/CodexOAuthUsageFetcher.swift +1 -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/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 +2 -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 +5 -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 +7 -7
- 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 +23 -10
- 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 +26 -13
- 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 +48 -21
- 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 +27 -13
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
- 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 -76
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import Testing
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct GeminiStatusProbeTests {
|
|
7
|
-
|
|
6
|
+
/// Sample /stats output from Gemini CLI (actual format with box-drawing chars)
|
|
8
7
|
static let sampleStatsOutput = """
|
|
9
8
|
│ Model Usage Reqs Usage left │
|
|
10
9
|
│ ─────────────────────────────────────────────────────────────────────── │
|
|
@@ -17,14 +16,14 @@ struct GeminiStatusProbeTests {
|
|
|
17
16
|
// MARK: - Legacy CLI parsing tests (kept for fallback support)
|
|
18
17
|
|
|
19
18
|
@Test
|
|
20
|
-
func
|
|
19
|
+
func `parses minimum percent from multiple models`() throws {
|
|
21
20
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
22
21
|
#expect(snap.dailyPercentLeft == 99.8)
|
|
23
22
|
#expect(snap.resetDescription == "Resets in 20h 37m")
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
@Test
|
|
27
|
-
func
|
|
26
|
+
func `parses lower percent correctly`() throws {
|
|
28
27
|
let output = """
|
|
29
28
|
│ Model Usage Reqs Usage left │
|
|
30
29
|
│ gemini-2.5-flash 10 85.5% (Resets in 12h) │
|
|
@@ -36,7 +35,7 @@ struct GeminiStatusProbeTests {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
@Test
|
|
39
|
-
func
|
|
38
|
+
func `handles zero percent usage`() throws {
|
|
40
39
|
let output = """
|
|
41
40
|
│ gemini-2.5-flash 50 0.0% (Resets in 6h) │
|
|
42
41
|
│ gemini-2.5-pro 20 15.0% (Resets in 6h) │
|
|
@@ -47,7 +46,7 @@ struct GeminiStatusProbeTests {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
@Test
|
|
50
|
-
func
|
|
49
|
+
func `handles100 percent remaining`() throws {
|
|
51
50
|
let output = """
|
|
52
51
|
│ gemini-2.5-flash - 100.0% (Resets in 24h) │
|
|
53
52
|
"""
|
|
@@ -56,14 +55,14 @@ struct GeminiStatusProbeTests {
|
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
@Test
|
|
59
|
-
func
|
|
58
|
+
func `throws on empty output`() {
|
|
60
59
|
#expect(throws: GeminiStatusProbeError.self) {
|
|
61
60
|
try GeminiStatusProbe.parse(text: "")
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
@Test
|
|
66
|
-
func
|
|
65
|
+
func `throws on no usage data`() {
|
|
67
66
|
let output = """
|
|
68
67
|
Welcome to Gemini CLI!
|
|
69
68
|
Type /help for available commands.
|
|
@@ -74,7 +73,7 @@ struct GeminiStatusProbeTests {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
@Test
|
|
77
|
-
func
|
|
76
|
+
func `strips ANSI codes before parsing`() throws {
|
|
78
77
|
// swiftlint:disable:next line_length
|
|
79
78
|
let output = "\u{1B}[32m│\u{1B}[0m gemini-2.5-flash - 75.5% (Resets in 18h) │"
|
|
80
79
|
let snap = try GeminiStatusProbe.parse(text: output)
|
|
@@ -82,7 +81,7 @@ struct GeminiStatusProbeTests {
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
@Test
|
|
85
|
-
func
|
|
84
|
+
func `preserves raw text`() throws {
|
|
86
85
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
87
86
|
#expect(snap.rawText == Self.sampleStatsOutput)
|
|
88
87
|
#expect(snap.accountEmail == nil) // Legacy parse doesn't extract email
|
|
@@ -90,7 +89,7 @@ struct GeminiStatusProbeTests {
|
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
@Test
|
|
93
|
-
func
|
|
92
|
+
func `parses various reset descriptions`() throws {
|
|
94
93
|
let cases: [(String, String)] = [
|
|
95
94
|
("Resets in 24h", "Resets in 24h"),
|
|
96
95
|
("Resets in 1h 30m", "Resets in 1h 30m"),
|
|
@@ -106,7 +105,7 @@ struct GeminiStatusProbeTests {
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
@Test
|
|
109
|
-
func
|
|
108
|
+
func `throws not logged in on auth prompt`() {
|
|
110
109
|
let authOutputs = [
|
|
111
110
|
"Waiting for auth... (Press ESC or CTRL+C to cancel)",
|
|
112
111
|
"Login with Google\nUse Gemini API key",
|
|
@@ -122,7 +121,7 @@ struct GeminiStatusProbeTests {
|
|
|
122
121
|
// MARK: - Model quota grouping tests
|
|
123
122
|
|
|
124
123
|
@Test
|
|
125
|
-
func
|
|
124
|
+
func `parses models into quota array`() throws {
|
|
126
125
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
127
126
|
// Should parse multiple models (exact count may change as Google adds/removes models)
|
|
128
127
|
#expect(snap.modelQuotas.count >= 2)
|
|
@@ -134,14 +133,14 @@ struct GeminiStatusProbeTests {
|
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
@Test
|
|
137
|
-
func
|
|
136
|
+
func `lowest percent left returns minimum`() throws {
|
|
138
137
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
139
138
|
// Flash models are 99.8%, Pro models are 100%, so min should be 99.8
|
|
140
139
|
#expect(snap.lowestPercentLeft == 99.8)
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
@Test
|
|
144
|
-
func
|
|
143
|
+
func `tier grouping by keyword`() throws {
|
|
145
144
|
// Test that flash/pro keyword filtering works (model names may change)
|
|
146
145
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
147
146
|
|
|
@@ -154,7 +153,7 @@ struct GeminiStatusProbeTests {
|
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
@Test
|
|
157
|
-
func
|
|
156
|
+
func `tier minimum calculation`() throws {
|
|
158
157
|
// Use controlled test data to verify min-per-tier logic
|
|
159
158
|
// Model names must start with "gemini-" to match the parser regex
|
|
160
159
|
let output = """
|
|
@@ -176,7 +175,7 @@ struct GeminiStatusProbeTests {
|
|
|
176
175
|
}
|
|
177
176
|
|
|
178
177
|
@Test
|
|
179
|
-
func
|
|
178
|
+
func `quotas have reset descriptions`() throws {
|
|
180
179
|
let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
|
|
181
180
|
|
|
182
181
|
// At least some quotas should have reset descriptions
|
|
@@ -185,7 +184,7 @@ struct GeminiStatusProbeTests {
|
|
|
185
184
|
}
|
|
186
185
|
|
|
187
186
|
@Test
|
|
188
|
-
func
|
|
187
|
+
func `to usage snapshot preserves model labels and tertiary window`() throws {
|
|
189
188
|
let output = """
|
|
190
189
|
│ gemini-2.5-pro 2 40.0% (Resets in 6h) │
|
|
191
190
|
│ gemini-2.5-flash 5 65.0% (Resets in 3h) │
|
|
@@ -202,7 +201,7 @@ struct GeminiStatusProbeTests {
|
|
|
202
201
|
}
|
|
203
202
|
|
|
204
203
|
@Test
|
|
205
|
-
func
|
|
204
|
+
func `to usage snapshot falls back when pro tier is missing`() throws {
|
|
206
205
|
let output = """
|
|
207
206
|
│ gemini-2.5-flash 2 90.0% (Resets in 6h) │
|
|
208
207
|
│ gemini-2.5-flash-lite 3 25.0% (Resets in 6h) │
|
|
@@ -216,7 +215,7 @@ struct GeminiStatusProbeTests {
|
|
|
216
215
|
// MARK: - Live API test
|
|
217
216
|
|
|
218
217
|
@Test
|
|
219
|
-
func
|
|
218
|
+
func `live gemini fetch`() async throws {
|
|
220
219
|
guard ProcessInfo.processInfo.environment["LIVE_GEMINI_FETCH"] == "1" else {
|
|
221
220
|
return
|
|
222
221
|
}
|
|
@@ -2,13 +2,12 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import Runic
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
@MainActor
|
|
7
6
|
struct GoogleWorkspaceStatusTests {
|
|
8
7
|
private let productID = "npdyhgECDJ6tB66MxXyo"
|
|
9
8
|
|
|
10
9
|
@Test
|
|
11
|
-
func
|
|
10
|
+
func `parse workspace status selects worst incident`() throws {
|
|
12
11
|
let data = Data(#"""
|
|
13
12
|
[
|
|
14
13
|
{
|
|
@@ -47,7 +46,7 @@ struct GoogleWorkspaceStatusTests {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
@Test
|
|
50
|
-
func
|
|
49
|
+
func `parse workspace status ignores resolved incidents`() throws {
|
|
51
50
|
let data = Data(#"""
|
|
52
51
|
[
|
|
53
52
|
{
|
|
@@ -2,8 +2,8 @@ import KeyboardShortcuts
|
|
|
2
2
|
import Testing
|
|
3
3
|
|
|
4
4
|
@MainActor
|
|
5
|
-
|
|
6
|
-
@Test func
|
|
5
|
+
struct KeyboardShortcutsBundleTests {
|
|
6
|
+
@Test func `recorder initializes without crashing`() {
|
|
7
7
|
_ = KeyboardShortcuts.RecorderCocoa(for: .init("test.keyboardshortcuts.bundle"))
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import RunicCore
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicCore
|
|
3
3
|
import Testing
|
|
4
4
|
import XCTest
|
|
5
5
|
@testable import Runic
|
|
6
6
|
|
|
7
|
-
@Suite(
|
|
7
|
+
@Suite(.serialized)
|
|
8
8
|
struct LiveAccountTests {
|
|
9
9
|
@Test(.disabled("Set LIVE_TEST=1 to run live Codex account checks."))
|
|
10
|
-
func
|
|
10
|
+
func `codex account email is present`() async throws {
|
|
11
11
|
guard ProcessInfo.processInfo.environment["LIVE_TEST"] == "1" else { return }
|
|
12
12
|
|
|
13
13
|
let fetcher = UsageFetcher()
|
|
@@ -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)
|