@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,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)
|
|
@@ -1,91 +1,130 @@
|
|
|
1
1
|
@testable import Runic
|
|
2
2
|
|
|
3
3
|
struct NoopOpenRouterTokenStore: OpenRouterTokenStoring {
|
|
4
|
-
func loadToken() throws -> String? {
|
|
4
|
+
func loadToken() throws -> String? {
|
|
5
|
+
nil
|
|
6
|
+
}
|
|
7
|
+
|
|
5
8
|
func storeToken(_ token: String?) throws {
|
|
6
9
|
_ = token
|
|
7
10
|
}
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
struct NoopGroqTokenStore: GroqTokenStoring {
|
|
11
|
-
func loadToken() throws -> String? {
|
|
14
|
+
func loadToken() throws -> String? {
|
|
15
|
+
nil
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
func storeToken(_ token: String?) throws {
|
|
13
19
|
_ = token
|
|
14
20
|
}
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
struct NoopDeepSeekTokenStore: DeepSeekTokenStoring {
|
|
18
|
-
func loadToken() throws -> String? {
|
|
24
|
+
func loadToken() throws -> String? {
|
|
25
|
+
nil
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
func storeToken(_ token: String?) throws {
|
|
20
29
|
_ = token
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
struct NoopFireworksTokenStore: FireworksTokenStoring {
|
|
25
|
-
func loadToken() throws -> String? {
|
|
34
|
+
func loadToken() throws -> String? {
|
|
35
|
+
nil
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
func storeToken(_ token: String?) throws {
|
|
27
39
|
_ = token
|
|
28
40
|
}
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
struct NoopMistralTokenStore: MistralTokenStoring {
|
|
32
|
-
func loadToken() throws -> String? {
|
|
44
|
+
func loadToken() throws -> String? {
|
|
45
|
+
nil
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
func storeToken(_ token: String?) throws {
|
|
34
49
|
_ = token
|
|
35
50
|
}
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
struct NoopPerplexityTokenStore: PerplexityTokenStoring {
|
|
39
|
-
func loadToken() throws -> String? {
|
|
54
|
+
func loadToken() throws -> String? {
|
|
55
|
+
nil
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
func storeToken(_ token: String?) throws {
|
|
41
59
|
_ = token
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
struct NoopKimiTokenStore: KimiTokenStoring {
|
|
46
|
-
func loadToken() throws -> String? {
|
|
64
|
+
func loadToken() throws -> String? {
|
|
65
|
+
nil
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
func storeToken(_ token: String?) throws {
|
|
48
69
|
_ = token
|
|
49
70
|
}
|
|
50
71
|
}
|
|
51
72
|
|
|
52
73
|
struct NoopAuggieTokenStore: AuggieTokenStoring {
|
|
53
|
-
func loadToken() throws -> String? {
|
|
74
|
+
func loadToken() throws -> String? {
|
|
75
|
+
nil
|
|
76
|
+
}
|
|
77
|
+
|
|
54
78
|
func storeToken(_ token: String?) throws {
|
|
55
79
|
_ = token
|
|
56
80
|
}
|
|
57
81
|
}
|
|
58
82
|
|
|
59
83
|
struct NoopTogetherTokenStore: TogetherTokenStoring {
|
|
60
|
-
func loadToken() throws -> String? {
|
|
84
|
+
func loadToken() throws -> String? {
|
|
85
|
+
nil
|
|
86
|
+
}
|
|
87
|
+
|
|
61
88
|
func storeToken(_ token: String?) throws {
|
|
62
89
|
_ = token
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
92
|
|
|
66
93
|
struct NoopCohereTokenStore: CohereTokenStoring {
|
|
67
|
-
func loadToken() throws -> String? {
|
|
94
|
+
func loadToken() throws -> String? {
|
|
95
|
+
nil
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
func storeToken(_ token: String?) throws {
|
|
69
99
|
_ = token
|
|
70
100
|
}
|
|
71
101
|
}
|
|
72
102
|
|
|
73
103
|
struct NoopXAITokenStore: XAITokenStoring {
|
|
74
|
-
func loadToken() throws -> String? {
|
|
104
|
+
func loadToken() throws -> String? {
|
|
105
|
+
nil
|
|
106
|
+
}
|
|
107
|
+
|
|
75
108
|
func storeToken(_ token: String?) throws {
|
|
76
109
|
_ = token
|
|
77
110
|
}
|
|
78
111
|
}
|
|
79
112
|
|
|
80
113
|
struct NoopCerebrasTokenStore: CerebrasTokenStoring {
|
|
81
|
-
func loadToken() throws -> String? {
|
|
114
|
+
func loadToken() throws -> String? {
|
|
115
|
+
nil
|
|
116
|
+
}
|
|
117
|
+
|
|
82
118
|
func storeToken(_ token: String?) throws {
|
|
83
119
|
_ = token
|
|
84
120
|
}
|
|
85
121
|
}
|
|
86
122
|
|
|
87
123
|
struct NoopSambaNovaTokenStore: SambaNovaTokenStoring {
|
|
88
|
-
func loadToken() throws -> String? {
|
|
124
|
+
func loadToken() throws -> String? {
|
|
125
|
+
nil
|
|
126
|
+
}
|
|
127
|
+
|
|
89
128
|
func storeToken(_ token: String?) throws {
|
|
90
129
|
_ = token
|
|
91
130
|
}
|
|
@@ -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 PathBuilderTests {
|
|
8
7
|
@Test
|
|
9
|
-
func
|
|
8
|
+
func `merges login shell path when available`() {
|
|
10
9
|
let seeded = PathBuilder.effectivePATH(
|
|
11
10
|
purposes: [.rpc],
|
|
12
11
|
env: ["PATH": "/custom/bin:/usr/bin"],
|
|
@@ -15,7 +14,7 @@ struct PathBuilderTests {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
@Test
|
|
18
|
-
func
|
|
17
|
+
func `falls back to existing path when no login path`() {
|
|
19
18
|
let seeded = PathBuilder.effectivePATH(
|
|
20
19
|
purposes: [.tty],
|
|
21
20
|
env: ["PATH": "/custom/bin:/usr/bin"],
|
|
@@ -24,7 +23,7 @@ struct PathBuilderTests {
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
@Test
|
|
27
|
-
func
|
|
26
|
+
func `uses fallback when no path available`() {
|
|
28
27
|
let seeded = PathBuilder.effectivePATH(
|
|
29
28
|
purposes: [.tty],
|
|
30
29
|
env: [:],
|
|
@@ -33,7 +32,7 @@ struct PathBuilderTests {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
@Test
|
|
36
|
-
func
|
|
35
|
+
func `resolves codex from env override`() {
|
|
37
36
|
let overridePath = "/custom/bin/codex"
|
|
38
37
|
let fm = MockFileManager(executables: [overridePath])
|
|
39
38
|
|
|
@@ -46,7 +45,7 @@ struct PathBuilderTests {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
@Test
|
|
49
|
-
func
|
|
48
|
+
func `resolves codex from login path`() {
|
|
50
49
|
let fm = MockFileManager(executables: ["/login/bin/codex"])
|
|
51
50
|
let resolved = BinaryLocator.resolveCodexBinary(
|
|
52
51
|
env: ["PATH": "/env/bin"],
|
|
@@ -57,7 +56,7 @@ struct PathBuilderTests {
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
@Test
|
|
60
|
-
func
|
|
59
|
+
func `resolves codex from env path`() {
|
|
61
60
|
let fm = MockFileManager(executables: ["/env/bin/codex"])
|
|
62
61
|
let resolved = BinaryLocator.resolveCodexBinary(
|
|
63
62
|
env: ["PATH": "/env/bin:/usr/bin"],
|
|
@@ -68,7 +67,7 @@ struct PathBuilderTests {
|
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
@Test
|
|
71
|
-
func
|
|
70
|
+
func `resolves codex from interactive shell`() {
|
|
72
71
|
let fm = MockFileManager(executables: ["/shell/bin/codex"])
|
|
73
72
|
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
|
|
74
73
|
#expect(tool == "codex")
|
|
@@ -88,7 +87,7 @@ struct PathBuilderTests {
|
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
@Test
|
|
91
|
-
func
|
|
90
|
+
func `resolves claude from interactive shell`() {
|
|
92
91
|
let fm = MockFileManager(executables: ["/shell/bin/claude"])
|
|
93
92
|
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
|
|
94
93
|
#expect(tool == "claude")
|
|
@@ -108,7 +107,7 @@ struct PathBuilderTests {
|
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
@Test
|
|
111
|
-
func
|
|
110
|
+
func `resolves gemini from interactive shell`() {
|
|
112
111
|
let fm = MockFileManager(executables: ["/shell/bin/gemini"])
|
|
113
112
|
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
|
|
114
113
|
#expect(tool == "gemini")
|
|
@@ -128,7 +127,7 @@ struct PathBuilderTests {
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
@Test
|
|
131
|
-
func
|
|
130
|
+
func `resolves claude from login path`() {
|
|
132
131
|
let fm = MockFileManager(executables: ["/login/bin/claude"])
|
|
133
132
|
let resolved = BinaryLocator.resolveClaudeBinary(
|
|
134
133
|
env: ["PATH": "/env/bin"],
|
|
@@ -139,7 +138,7 @@ struct PathBuilderTests {
|
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
@Test
|
|
142
|
-
func
|
|
141
|
+
func `resolves claude from alias when other lookups fail`() {
|
|
143
142
|
let aliasPath = "/home/test/.claude/local/bin/claude"
|
|
144
143
|
let fm = MockFileManager(executables: [aliasPath])
|
|
145
144
|
var aliasCalled = false
|
|
@@ -169,7 +168,7 @@ struct PathBuilderTests {
|
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
@Test
|
|
172
|
-
func
|
|
171
|
+
func `resolves codex from alias when other lookups fail`() {
|
|
173
172
|
let aliasPath = "/home/test/.codex/bin/codex"
|
|
174
173
|
let fm = MockFileManager(executables: [aliasPath])
|
|
175
174
|
var aliasCalled = false
|
|
@@ -199,7 +198,7 @@ struct PathBuilderTests {
|
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
@Test
|
|
202
|
-
func
|
|
201
|
+
func `skips alias when command V resolves`() {
|
|
203
202
|
let path = "/shell/bin/claude"
|
|
204
203
|
let fm = MockFileManager(executables: [path])
|
|
205
204
|
var aliasCalled = false
|
|
@@ -2,7 +2,6 @@ import Foundation
|
|
|
2
2
|
import Testing
|
|
3
3
|
@testable import RunicCore
|
|
4
4
|
|
|
5
|
-
@Suite
|
|
6
5
|
struct ProviderFetchPipelineTests {
|
|
7
6
|
private struct SlowStrategy: ProviderFetchStrategy {
|
|
8
7
|
let id: String
|
|
@@ -90,7 +89,7 @@ struct ProviderFetchPipelineTests {
|
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
@Test
|
|
93
|
-
func
|
|
92
|
+
func `hangs are converted to strategy timeout`() async {
|
|
94
93
|
let pipeline = ProviderFetchPipeline(
|
|
95
94
|
resolveStrategies: { _ in [SlowStrategy(id: "provider.fetchPipeline.hang", shouldFallbackValue: false)] },
|
|
96
95
|
strategyTimeout: 0.05)
|
|
@@ -101,7 +100,7 @@ struct ProviderFetchPipelineTests {
|
|
|
101
100
|
#expect(outcome.attempts[0].strategyID == "provider.fetchPipeline.hang")
|
|
102
101
|
#expect(outcome.attempts[0].wasAvailable == true)
|
|
103
102
|
|
|
104
|
-
guard case .failure(
|
|
103
|
+
guard case let .failure(error) = outcome.result else {
|
|
105
104
|
Issue.record("Expected fetch failure")
|
|
106
105
|
return
|
|
107
106
|
}
|
|
@@ -115,7 +114,7 @@ struct ProviderFetchPipelineTests {
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
@Test
|
|
118
|
-
func
|
|
117
|
+
func `strategy fallback still honors order`() async {
|
|
119
118
|
let expected = Self.makeStubResult()
|
|
120
119
|
let pipeline = ProviderFetchPipeline(
|
|
121
120
|
resolveStrategies: { _ in [
|
|
@@ -3,10 +3,9 @@ import Foundation
|
|
|
3
3
|
import Testing
|
|
4
4
|
|
|
5
5
|
@MainActor
|
|
6
|
-
@Suite
|
|
7
6
|
struct ProviderIconResourcesTests {
|
|
8
7
|
@Test
|
|
9
|
-
func
|
|
8
|
+
func `provider icon SV gs exist`() throws {
|
|
10
9
|
let root = try Self.repoRoot()
|
|
11
10
|
let resources = root.appending(path: "Sources/Runic/Resources", directoryHint: .isDirectory)
|
|
12
11
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import Testing
|
|
2
2
|
@testable import RunicCore
|
|
3
3
|
|
|
4
|
-
@Suite
|
|
5
4
|
struct ProviderMetadataStatusLinkTests {
|
|
6
5
|
@Test
|
|
7
|
-
func
|
|
6
|
+
func `workspace status link matches product ID`() {
|
|
8
7
|
for (provider, meta) in ProviderDefaults.metadata {
|
|
9
8
|
guard let productID = meta.statusWorkspaceProductID else { continue }
|
|
10
9
|
let expected = "https://www.google.com/appsstatus/dashboard/products/\(productID)/history"
|
|
@@ -1,17 +1,23 @@
|
|
|
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
7
|
@MainActor
|
|
8
|
-
@Suite
|
|
9
8
|
struct ProviderSettingsDescriptorTests {
|
|
10
9
|
@Test
|
|
11
|
-
func
|
|
12
|
-
let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-unique")
|
|
10
|
+
func `toggle I ds are unique across providers`() throws {
|
|
11
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-unique"))
|
|
13
12
|
defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-unique")
|
|
14
|
-
let settings = SettingsStore(
|
|
13
|
+
let settings = SettingsStore(
|
|
14
|
+
userDefaults: defaults,
|
|
15
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
16
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
17
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
18
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
19
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
20
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
15
21
|
let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
|
|
16
22
|
|
|
17
23
|
var statusByID: [String: String] = [:]
|
|
@@ -52,7 +58,7 @@ struct ProviderSettingsDescriptorTests {
|
|
|
52
58
|
},
|
|
53
59
|
requestConfirmation: { _ in })
|
|
54
60
|
|
|
55
|
-
let impl = ProviderCatalog.implementation(for: provider)
|
|
61
|
+
let impl = try #require(ProviderCatalog.implementation(for: provider))
|
|
56
62
|
let toggles = impl.settingsToggles(context: context)
|
|
57
63
|
for toggle in toggles {
|
|
58
64
|
#expect(!seenToggleIDs.contains(toggle.id))
|
|
@@ -67,10 +73,17 @@ struct ProviderSettingsDescriptorTests {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
@Test
|
|
70
|
-
func
|
|
71
|
-
let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-codex")
|
|
76
|
+
func `codex does not expose open AI web toggle`() throws {
|
|
77
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-codex"))
|
|
72
78
|
defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-codex")
|
|
73
|
-
let settings = SettingsStore(
|
|
79
|
+
let settings = SettingsStore(
|
|
80
|
+
userDefaults: defaults,
|
|
81
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
82
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
83
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
84
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
85
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
86
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
74
87
|
let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
|
|
75
88
|
|
|
76
89
|
let context = ProviderSettingsContext(
|
|
@@ -98,10 +111,17 @@ struct ProviderSettingsDescriptorTests {
|
|
|
98
111
|
}
|
|
99
112
|
|
|
100
113
|
@Test
|
|
101
|
-
func
|
|
102
|
-
let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude")
|
|
114
|
+
func `claude does not expose settings toggles`() throws {
|
|
115
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude"))
|
|
103
116
|
defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-claude")
|
|
104
|
-
let settings = SettingsStore(
|
|
117
|
+
let settings = SettingsStore(
|
|
118
|
+
userDefaults: defaults,
|
|
119
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
120
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
121
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
122
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
123
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
124
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
105
125
|
let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
|
|
106
126
|
|
|
107
127
|
let context = ProviderSettingsContext(
|
|
@@ -128,10 +148,17 @@ struct ProviderSettingsDescriptorTests {
|
|
|
128
148
|
}
|
|
129
149
|
|
|
130
150
|
@Test
|
|
131
|
-
func
|
|
132
|
-
let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude-invariant")
|
|
151
|
+
func `claude web extras auto disables when leaving CLI`() throws {
|
|
152
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude-invariant"))
|
|
133
153
|
defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-claude-invariant")
|
|
134
|
-
let settings = SettingsStore(
|
|
154
|
+
let settings = SettingsStore(
|
|
155
|
+
userDefaults: defaults,
|
|
156
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
157
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
158
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
159
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
160
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
161
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
135
162
|
settings.debugMenuEnabled = true
|
|
136
163
|
settings.claudeUsageDataSource = .cli
|
|
137
164
|
settings.claudeWebExtrasEnabled = true
|
|
@@ -141,10 +168,17 @@ struct ProviderSettingsDescriptorTests {
|
|
|
141
168
|
}
|
|
142
169
|
|
|
143
170
|
@Test
|
|
144
|
-
func
|
|
145
|
-
let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-extra-fields")
|
|
171
|
+
func `extra providers expose expected settings fields`() throws {
|
|
172
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-extra-fields"))
|
|
146
173
|
defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-extra-fields")
|
|
147
|
-
let settings = SettingsStore(
|
|
174
|
+
let settings = SettingsStore(
|
|
175
|
+
userDefaults: defaults,
|
|
176
|
+
zaiTokenStore: NoopZaiTokenStore(),
|
|
177
|
+
minimaxTokenStore: NoopMiniMaxTokenStore(),
|
|
178
|
+
minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
|
|
179
|
+
minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
|
|
180
|
+
openRouterTokenStore: NoopOpenRouterTokenStore(),
|
|
181
|
+
groqTokenStore: NoopGroqTokenStore())
|
|
148
182
|
let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
|
|
149
183
|
|
|
150
184
|
let expectedFields: [UsageProvider: Set<String>] = [
|
|
@@ -168,7 +202,7 @@ struct ProviderSettingsDescriptorTests {
|
|
|
168
202
|
}
|
|
169
203
|
|
|
170
204
|
@Test
|
|
171
|
-
func
|
|
205
|
+
func `all providers have app side implementation`() {
|
|
172
206
|
for provider in UsageProvider.allCases {
|
|
173
207
|
#expect(ProviderCatalog.implementation(for: provider) != nil)
|
|
174
208
|
}
|
|
@@ -3,41 +3,40 @@ import Testing
|
|
|
3
3
|
@testable import Runic
|
|
4
4
|
|
|
5
5
|
@MainActor
|
|
6
|
-
@Suite
|
|
7
6
|
struct ProviderToggleStoreTests {
|
|
8
7
|
@Test
|
|
9
|
-
func
|
|
10
|
-
let defaults = UserDefaults(suiteName: "ProviderToggleStoreTests-defaults")
|
|
8
|
+
func `defaults match metadata`() throws {
|
|
9
|
+
let defaults = try #require(UserDefaults(suiteName: "ProviderToggleStoreTests-defaults"))
|
|
11
10
|
defaults.removePersistentDomain(forName: "ProviderToggleStoreTests-defaults")
|
|
12
11
|
let store = ProviderToggleStore(userDefaults: defaults)
|
|
13
12
|
let registry = ProviderRegistry.shared
|
|
14
|
-
let codexMeta = registry.metadata[.codex]
|
|
15
|
-
let claudeMeta = registry.metadata[.claude]
|
|
13
|
+
let codexMeta = try #require(registry.metadata[.codex])
|
|
14
|
+
let claudeMeta = try #require(registry.metadata[.claude])
|
|
16
15
|
|
|
17
16
|
#expect(store.isEnabled(metadata: codexMeta))
|
|
18
17
|
#expect(!store.isEnabled(metadata: claudeMeta))
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
@Test
|
|
22
|
-
func
|
|
21
|
+
func `persists changes`() throws {
|
|
23
22
|
let suite = "ProviderToggleStoreTests-persist"
|
|
24
|
-
let defaultsA = UserDefaults(suiteName: suite)
|
|
23
|
+
let defaultsA = try #require(UserDefaults(suiteName: suite))
|
|
25
24
|
defaultsA.removePersistentDomain(forName: suite)
|
|
26
25
|
let storeA = ProviderToggleStore(userDefaults: defaultsA)
|
|
27
26
|
let registry = ProviderRegistry.shared
|
|
28
|
-
let claudeMeta = registry.metadata[.claude]
|
|
27
|
+
let claudeMeta = try #require(registry.metadata[.claude])
|
|
29
28
|
|
|
30
29
|
storeA.setEnabled(true, metadata: claudeMeta)
|
|
31
30
|
|
|
32
|
-
let defaultsB = UserDefaults(suiteName: suite)
|
|
31
|
+
let defaultsB = try #require(UserDefaults(suiteName: suite))
|
|
33
32
|
let storeB = ProviderToggleStore(userDefaults: defaultsB)
|
|
34
33
|
#expect(storeB.isEnabled(metadata: claudeMeta))
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
@Test
|
|
38
|
-
func
|
|
37
|
+
func `purges legacy keys`() throws {
|
|
39
38
|
let suite = "ProviderToggleStoreTests-purge"
|
|
40
|
-
let defaults = UserDefaults(suiteName: suite)
|
|
39
|
+
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
41
40
|
defaults.removePersistentDomain(forName: suite)
|
|
42
41
|
defaults.set(false, forKey: "showCodexUsage")
|
|
43
42
|
defaults.set(true, forKey: "showClaudeUsage")
|