@sriinnu/runic 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +1 -1
- package/README.md +47 -29
- package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
- package/RuniciOS/Views/ProviderDetailView.swift +9 -9
- package/RuniciOS/Views/ProviderListView.swift +10 -10
- package/RuniciOS/Widgets/LargeWidget.swift +14 -14
- package/RuniciOS/Widgets/LiveActivity.swift +19 -19
- package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
- package/RuniciOS/Widgets/MediumWidget.swift +10 -10
- package/RuniciOS/Widgets/RunicWidget.swift +5 -5
- package/RuniciOS/Widgets/SmallWidget.swift +9 -9
- package/Scripts/package_app.sh +2 -2
- package/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
- package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
- package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
- package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
- package/Sources/Runic/Core/RunicColors.swift +6 -6
- package/Sources/Runic/Core/RunicSpacing.swift +7 -7
- package/Sources/Runic/Core/RunicTypography.swift +206 -0
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
- package/Sources/Runic/Core/UsageExporter.swift +8 -4
- package/Sources/Runic/ProviderRegistry.swift +1 -1
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
- package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/provider-context-windows.json +20 -0
- package/Sources/Runic/RunicApp.swift +46 -36
- package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
- package/Sources/Runic/UsagePaceText.swift +1 -1
- package/Sources/Runic/UsageProgressBar.swift +13 -9
- package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
- package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
- package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
- package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
- package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
- package/Sources/RunicCLI/CLIEntry.swift +83 -42
- package/Sources/RunicCLI/CLIRenderer.swift +1 -1
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
- package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
- package/Sources/RunicClaudeWebProbe/main.swift +1 -1
- package/Sources/RunicCore/AlertRuleStore.swift +20 -20
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
- package/Sources/RunicCore/CostUsageModels.swift +1 -1
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
- package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
- package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
- package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
- package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
- package/Sources/RunicCore/Team/TeamModels.swift +111 -110
- package/Sources/RunicCore/Team/TeamStore.swift +126 -109
- package/Sources/RunicCore/UsageFetcher.swift +2 -2
- package/Sources/RunicCore/UsageFormatter.swift +25 -25
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
- package/Tests/RunicTests/AppDelegateTests.swift +8 -3
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
- package/Tests/RunicTests/CLICostTests.swift +6 -7
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
- package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
- package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
- package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
- package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
- package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
- package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
- package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
- package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
- package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
- package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
- package/Tests/RunicTests/InstallOriginTests.swift +1 -2
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
- package/Tests/RunicTests/LiveAccountTests.swift +3 -3
- package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
- package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
- package/Tests/RunicTests/PathBuilderTests.swift +14 -15
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
- package/Tests/RunicTests/RunicTests.swift +6 -7
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
- package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
- package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
- package/Tests/RunicTests/StatusMenuTests.swift +63 -22
- package/Tests/RunicTests/StatusProbeTests.swift +28 -29
- package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
- package/Tests/RunicTests/TextParsingTests.swift +1 -2
- package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
- package/Tests/RunicTests/UsagePaceTests.swift +5 -6
- package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
- package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
- package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
- package/appcast.xml +36 -37
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -8,7 +8,7 @@ struct GroqModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum GroqUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.groq.com/openai/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> GroqModelsResponse {
|
|
@@ -53,7 +53,7 @@ extension GroqModelsResponse {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
enum GroqAPIError: LocalizedError
|
|
56
|
+
enum GroqAPIError: LocalizedError {
|
|
57
57
|
case invalidResponse
|
|
58
58
|
case httpError(statusCode: Int)
|
|
59
59
|
case decodingError
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -71,7 +71,7 @@ struct KimiAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum KimiSettingsError: LocalizedError
|
|
74
|
+
enum KimiSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct KimiModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum KimiUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.moonshot.ai/v1/models")!
|
|
13
13
|
private static let requestTimeout: TimeInterval = 20
|
|
14
14
|
|
|
@@ -59,7 +59,7 @@ extension KimiModelsResponse {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
enum KimiAPIError: LocalizedError
|
|
62
|
+
enum KimiAPIError: LocalizedError {
|
|
63
63
|
case invalidResponse
|
|
64
64
|
case httpError(statusCode: Int, body: String?)
|
|
65
65
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -37,7 +37,10 @@ public enum MiniMaxProviderDescriptor {
|
|
|
37
37
|
noDataMessage: { "MiniMax cost summary is not supported." }),
|
|
38
38
|
fetchPlan: ProviderFetchPlan(
|
|
39
39
|
sourceModes: [.auto, .api, .web],
|
|
40
|
-
pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [
|
|
40
|
+
pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [
|
|
41
|
+
MiniMaxAPIFetchStrategy(),
|
|
42
|
+
MiniMaxWebFetchStrategy(),
|
|
43
|
+
] })),
|
|
41
44
|
cli: ProviderCLIConfig(
|
|
42
45
|
name: "minimax",
|
|
43
46
|
aliases: ["minimax-api"],
|
|
@@ -49,7 +52,9 @@ struct MiniMaxWebFetchStrategy: ProviderFetchStrategy {
|
|
|
49
52
|
let id: String = "minimax.web"
|
|
50
53
|
let kind: ProviderFetchKind = .web
|
|
51
54
|
|
|
52
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
55
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
56
|
+
true
|
|
57
|
+
}
|
|
53
58
|
|
|
54
59
|
func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
55
60
|
let fetcher = MiniMaxWebUsageFetcher()
|
|
@@ -78,9 +83,9 @@ struct MiniMaxAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
78
83
|
guard let tokenRes = ProviderTokenResolver.minimaxApiKeyResolution(environment: context.env) else {
|
|
79
84
|
throw ProviderFetchError.missingCredentials
|
|
80
85
|
}
|
|
81
|
-
|
|
86
|
+
|
|
82
87
|
let snapshot = try await MiniMaxUsageFetcher.fetchUsage(apiKey: tokenRes.token)
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
return self.makeResult(
|
|
85
90
|
usage: snapshot.toUsageSnapshot(),
|
|
86
91
|
sourceLabel: "API")
|
|
@@ -18,7 +18,7 @@ public struct MiniMaxModelRemain: Decodable {
|
|
|
18
18
|
public let modelName: String
|
|
19
19
|
public let currentIntervalTotalCount: Int
|
|
20
20
|
public let currentIntervalUsageCount: Int
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
enum CodingKeys: String, CodingKey {
|
|
23
23
|
case modelName = "model_name"
|
|
24
24
|
case currentIntervalTotalCount = "current_interval_total_count"
|
|
@@ -62,9 +62,9 @@ extension MiniMaxUsageSnapshot {
|
|
|
62
62
|
// But let's assume standard "usage" field naming means USED.
|
|
63
63
|
// If the user reports it shows 100% when new, we invert it.
|
|
64
64
|
// For now, let's map it as Used/Total.
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
let percent = self.total > 0 ? (Double(self.used) / Double(self.total)) * 100.0 : 0.0
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
let primary = RateWindow(
|
|
69
69
|
usedPercent: percent,
|
|
70
70
|
windowMinutes: 24 * 60,
|
|
@@ -77,7 +77,7 @@ extension MiniMaxUsageSnapshot {
|
|
|
77
77
|
accountEmail: nil,
|
|
78
78
|
accountOrganization: nil,
|
|
79
79
|
loginMethod: "api-key")
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
return UsageSnapshot(
|
|
82
82
|
primary: primary,
|
|
83
83
|
secondary: nil,
|
|
@@ -119,21 +119,18 @@ public struct MiniMaxUsageFetcher: Sendable {
|
|
|
119
119
|
throw MiniMaxUsageError.apiError("HTTP \(httpResponse.statusCode): \(errorMessage)")
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
if let jsonString = String(data: data, encoding: .utf8) {
|
|
124
|
-
Self.log.debug("MiniMax API response: \(jsonString)")
|
|
125
|
-
}
|
|
122
|
+
Self.log.debug("MiniMax API response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
|
|
126
123
|
|
|
127
124
|
let decoder = JSONDecoder()
|
|
128
125
|
do {
|
|
129
126
|
let apiResponse = try decoder.decode(MiniMaxQuotaResponse.self, from: data)
|
|
130
127
|
|
|
131
128
|
if apiResponse.baseResponse.statusCode != 0 {
|
|
132
|
-
|
|
129
|
+
throw MiniMaxUsageError.apiError(apiResponse.baseResponse.statusMsg)
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
guard let firstModel = apiResponse.modelRemains?.first else {
|
|
136
|
-
|
|
133
|
+
throw MiniMaxUsageError.parseFailed("No model quota found")
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
return MiniMaxUsageSnapshot(
|
|
@@ -141,7 +138,7 @@ public struct MiniMaxUsageFetcher: Sendable {
|
|
|
141
138
|
used: firstModel.currentIntervalUsageCount,
|
|
142
139
|
modelName: firstModel.modelName,
|
|
143
140
|
updatedAt: Date())
|
|
144
|
-
|
|
141
|
+
|
|
145
142
|
} catch {
|
|
146
143
|
Self.log.error("MiniMax parsing error: \(error.localizedDescription)")
|
|
147
144
|
throw MiniMaxUsageError.parseFailed(error.localizedDescription)
|
|
@@ -225,13 +225,13 @@ public struct MiniMaxWebUsageFetcher: Sendable {
|
|
|
225
225
|
|
|
226
226
|
// MARK: - Parsing helpers
|
|
227
227
|
|
|
228
|
-
struct MiniMaxManualInput
|
|
228
|
+
struct MiniMaxManualInput {
|
|
229
229
|
let cookieHeader: String
|
|
230
230
|
let accessToken: String?
|
|
231
231
|
let groupID: String?
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
struct MiniMaxParsedUsage
|
|
234
|
+
struct MiniMaxParsedUsage {
|
|
235
235
|
let usedPercent: Double
|
|
236
236
|
let windowMinutes: Int?
|
|
237
237
|
let resetsAt: Date?
|
|
@@ -408,9 +408,9 @@ enum MiniMaxWebParsing {
|
|
|
408
408
|
if let remaining = self.captureGroups(
|
|
409
409
|
in: html,
|
|
410
410
|
pattern: "(?i)Available usage[^0-9]*([0-9]+(?:\\.[0-9]+)?)")?.first,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
let total = self.captureGroups(
|
|
412
|
+
in: html,
|
|
413
|
+
pattern: "(?i)Total usage[^0-9]*([0-9]+(?:\\.[0-9]+)?)")?.first
|
|
414
414
|
{
|
|
415
415
|
let remainingValue = self.doubleValue(remaining) ?? 0
|
|
416
416
|
let totalValue = self.doubleValue(total) ?? 0
|
|
@@ -613,7 +613,7 @@ enum MiniMaxWebParsing {
|
|
|
613
613
|
}
|
|
614
614
|
|
|
615
615
|
#if os(macOS)
|
|
616
|
-
private struct MiniMaxWebSession
|
|
616
|
+
private struct MiniMaxWebSession {
|
|
617
617
|
let cookieHeader: String
|
|
618
618
|
let accessToken: String?
|
|
619
619
|
let groupID: String?
|
|
@@ -621,7 +621,7 @@ private struct MiniMaxWebSession: Sendable {
|
|
|
621
621
|
let isManual: Bool
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
-
private struct MiniMaxCookieSession
|
|
624
|
+
private struct MiniMaxCookieSession {
|
|
625
625
|
let cookieHeader: String
|
|
626
626
|
let sourceLabel: String
|
|
627
627
|
}
|
|
@@ -674,7 +674,7 @@ private enum MiniMaxCookieImporter {
|
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
-
private struct MiniMaxLocalStorageToken
|
|
677
|
+
private struct MiniMaxLocalStorageToken {
|
|
678
678
|
let accessToken: String
|
|
679
679
|
let groupID: String?
|
|
680
680
|
let sourceLabel: String
|
|
@@ -706,7 +706,7 @@ private enum MiniMaxLocalStorageImporter {
|
|
|
706
706
|
return tokens
|
|
707
707
|
}
|
|
708
708
|
|
|
709
|
-
private struct LocalStorageCandidate
|
|
709
|
+
private struct LocalStorageCandidate {
|
|
710
710
|
let label: String
|
|
711
711
|
let levelDBURL: URL
|
|
712
712
|
}
|
|
@@ -789,7 +789,7 @@ private enum MiniMaxLocalStorageImporter {
|
|
|
789
789
|
}
|
|
790
790
|
}
|
|
791
791
|
|
|
792
|
-
private struct TokenMatch
|
|
792
|
+
private struct TokenMatch {
|
|
793
793
|
let accessToken: String
|
|
794
794
|
let groupID: String?
|
|
795
795
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -71,7 +71,7 @@ struct MistralAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum MistralSettingsError: LocalizedError
|
|
74
|
+
enum MistralSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct MistralModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum MistralUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.mistral.ai/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> MistralModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension MistralModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum MistralAPIError: LocalizedError
|
|
60
|
+
enum MistralAPIError: LocalizedError {
|
|
61
61
|
case invalidResponse
|
|
62
62
|
case httpError(statusCode: Int, body: String?)
|
|
63
63
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -74,7 +74,7 @@ struct OpenRouterAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
74
74
|
|
|
75
75
|
// MARK: - Settings Errors
|
|
76
76
|
|
|
77
|
-
enum OpenRouterSettingsError: LocalizedError
|
|
77
|
+
enum OpenRouterSettingsError: LocalizedError {
|
|
78
78
|
case missingToken
|
|
79
79
|
|
|
80
80
|
var errorDescription: String? {
|
|
@@ -51,16 +51,30 @@ struct OpenRouterKeyInfoResponse: Decodable {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
var keyUsage: Double {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
var
|
|
54
|
+
var keyUsage: Double {
|
|
55
|
+
self.data?.usage ?? 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var keyLimit: Double? {
|
|
59
|
+
self.data?.limit
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var isFreeTier: Bool {
|
|
63
|
+
self.data?.is_free_tier ?? false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var rateLimitRequests: Int? {
|
|
67
|
+
self.data?.rate_limit?.requests
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var rateLimitInterval: String? {
|
|
71
|
+
self.data?.rate_limit?.interval
|
|
72
|
+
}
|
|
59
73
|
}
|
|
60
74
|
|
|
61
75
|
// MARK: - Fetcher
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
enum OpenRouterUsageFetcher {
|
|
64
78
|
static let creditsURL = URL(string: "https://openrouter.ai/api/v1/credits")!
|
|
65
79
|
static let keyInfoURL = URL(string: "https://openrouter.ai/api/v1/auth/key")!
|
|
66
80
|
private static let requestTimeout: TimeInterval = 15
|
|
@@ -91,18 +105,16 @@ struct OpenRouterUsageFetcher {
|
|
|
91
105
|
throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
|
|
92
106
|
}
|
|
93
107
|
|
|
94
|
-
|
|
95
|
-
Self.log.debug("OpenRouter credits response: \(json)")
|
|
96
|
-
}
|
|
108
|
+
Self.log.debug("OpenRouter credits response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
|
|
97
109
|
|
|
98
110
|
return try JSONDecoder().decode(OpenRouterCreditsResponse.self, from: data)
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
private static func fetchKeyInfoBestEffort(apiKey: String) async -> OpenRouterKeyInfoResponse? {
|
|
102
114
|
do {
|
|
103
|
-
return try await
|
|
115
|
+
return try await self.fetchKeyInfo(apiKey: apiKey)
|
|
104
116
|
} catch {
|
|
105
|
-
|
|
117
|
+
self.log.info("OpenRouter key info unavailable: \(error.localizedDescription)")
|
|
106
118
|
return nil
|
|
107
119
|
}
|
|
108
120
|
}
|
|
@@ -122,13 +134,10 @@ struct OpenRouterUsageFetcher {
|
|
|
122
134
|
throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
|
|
123
135
|
}
|
|
124
136
|
|
|
125
|
-
|
|
126
|
-
Self.log.debug("OpenRouter key info response: \(json)")
|
|
127
|
-
}
|
|
137
|
+
Self.log.debug("OpenRouter key info response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
|
|
128
138
|
|
|
129
139
|
return try JSONDecoder().decode(OpenRouterKeyInfoResponse.self, from: data)
|
|
130
140
|
}
|
|
131
|
-
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
// MARK: - Snapshot conversion
|
|
@@ -189,7 +198,7 @@ extension OpenRouterCreditsResponse {
|
|
|
189
198
|
|
|
190
199
|
// MARK: - Errors
|
|
191
200
|
|
|
192
|
-
enum OpenRouterAPIError: LocalizedError
|
|
201
|
+
enum OpenRouterAPIError: LocalizedError {
|
|
193
202
|
case invalidResponse
|
|
194
203
|
case httpError(statusCode: Int)
|
|
195
204
|
case decodingError
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -71,7 +71,7 @@ struct PerplexityAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum PerplexitySettingsError: LocalizedError
|
|
74
|
+
enum PerplexitySettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct PerplexityModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum PerplexityUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.perplexity.ai/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> PerplexityModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension PerplexityModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum PerplexityAPIError: LocalizedError
|
|
60
|
+
enum PerplexityAPIError: LocalizedError {
|
|
61
61
|
case invalidResponse
|
|
62
62
|
case httpError(statusCode: Int, body: String?)
|
|
63
63
|
|
|
@@ -170,8 +170,8 @@ public struct ProviderFetchPipeline: Sendable {
|
|
|
170
170
|
|
|
171
171
|
public init(
|
|
172
172
|
resolveStrategies: @escaping @Sendable (ProviderFetchContext) async -> [any ProviderFetchStrategy],
|
|
173
|
-
strategyTimeout: TimeInterval = 45
|
|
174
|
-
|
|
173
|
+
strategyTimeout: TimeInterval = 45)
|
|
174
|
+
{
|
|
175
175
|
self.resolveStrategies = resolveStrategies
|
|
176
176
|
self.strategyTimeout = strategyTimeout
|
|
177
177
|
}
|
|
@@ -472,11 +472,11 @@ public enum ProviderTokenResolver {
|
|
|
472
472
|
if dataProtection {
|
|
473
473
|
query[kSecUseDataProtectionKeychain as String] = true
|
|
474
474
|
}
|
|
475
|
-
#if canImport(LocalAuthentication)
|
|
475
|
+
#if canImport(LocalAuthentication)
|
|
476
476
|
let authContext = LAContext()
|
|
477
477
|
authContext.interactionNotAllowed = true
|
|
478
478
|
query[kSecUseAuthenticationContext as String] = authContext
|
|
479
|
-
#endif
|
|
479
|
+
#endif
|
|
480
480
|
|
|
481
481
|
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
482
482
|
if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -33,7 +33,9 @@ public enum QwenProviderDescriptor {
|
|
|
33
33
|
color: ProviderColor(red: 1.0, green: 0.416, blue: 0.0)),
|
|
34
34
|
tokenCost: ProviderTokenCostConfig(
|
|
35
35
|
supportsTokenCost: false,
|
|
36
|
-
noDataMessage: {
|
|
36
|
+
noDataMessage: {
|
|
37
|
+
"Qwen cost estimates are shown per-model in the details submenu (based on public DashScope pricing)."
|
|
38
|
+
}),
|
|
37
39
|
fetchPlan: ProviderFetchPlan(
|
|
38
40
|
sourceModes: [.auto, .cli],
|
|
39
41
|
pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [QwenAPIFetchStrategy()] })),
|
|
@@ -71,7 +73,7 @@ struct QwenAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
enum QwenSettingsError: LocalizedError
|
|
76
|
+
enum QwenSettingsError: LocalizedError {
|
|
75
77
|
case missingToken
|
|
76
78
|
|
|
77
79
|
var errorDescription: String? {
|
|
@@ -46,10 +46,10 @@ public enum QwenModelPricing {
|
|
|
46
46
|
"qwen-turbo-latest": Tier(inputPerMillion: 0.30, outputPerMillion: 0.60, contextWindow: 131_072),
|
|
47
47
|
"qwen-plus": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
|
|
48
48
|
"qwen-plus-latest": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
|
|
49
|
-
"qwen-max": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow:
|
|
50
|
-
"qwen-max-latest": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow:
|
|
51
|
-
"qwen-vl-max": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow:
|
|
52
|
-
"qwen-vl-max-latest": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow:
|
|
49
|
+
"qwen-max": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32768),
|
|
50
|
+
"qwen-max-latest": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32768),
|
|
51
|
+
"qwen-vl-max": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32768),
|
|
52
|
+
"qwen-vl-max-latest": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32768),
|
|
53
53
|
"qwen-vl-plus": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
|
|
54
54
|
"qwen-long": Tier(inputPerMillion: 0.50, outputPerMillion: 2.00, contextWindow: 10_000_000),
|
|
55
55
|
"qwen2.5-72b-instruct": Tier(inputPerMillion: 0.56, outputPerMillion: 1.12, contextWindow: 131_072),
|
|
@@ -185,11 +185,25 @@ private struct QwenModelUsageRaw: Decodable {
|
|
|
185
185
|
let requests: Int?
|
|
186
186
|
let calls: Int?
|
|
187
187
|
|
|
188
|
-
var resolvedModelID: String? {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
var
|
|
188
|
+
var resolvedModelID: String? {
|
|
189
|
+
self.model_id ?? self.model
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var resolvedInputTokens: Int {
|
|
193
|
+
self.input_tokens ?? 0
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
var resolvedOutputTokens: Int {
|
|
197
|
+
self.output_tokens ?? 0
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var resolvedTotalTokens: Int {
|
|
201
|
+
self.total_tokens ?? self.tokens ?? (self.resolvedInputTokens + self.resolvedOutputTokens)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
var resolvedRequestCount: Int {
|
|
205
|
+
self.request_count ?? self.requests ?? self.calls ?? 0
|
|
206
|
+
}
|
|
193
207
|
}
|
|
194
208
|
|
|
195
209
|
// MARK: - Fetcher
|
|
@@ -218,9 +232,7 @@ public struct QwenUsageFetcher: Sendable {
|
|
|
218
232
|
throw QwenUsageError.apiError("HTTP \(httpResponse.statusCode): \(errorMessage)")
|
|
219
233
|
}
|
|
220
234
|
|
|
221
|
-
|
|
222
|
-
Self.log.debug("DashScope usage response: \(jsonString)")
|
|
223
|
-
}
|
|
235
|
+
Self.log.debug("DashScope usage response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
|
|
224
236
|
|
|
225
237
|
let apiResponse = try JSONDecoder().decode(QwenUsageResponse.self, from: data)
|
|
226
238
|
guard apiResponse.isSuccess else {
|
|
@@ -243,11 +255,10 @@ public struct QwenUsageFetcher: Sendable {
|
|
|
243
255
|
let output = model.resolvedOutputTokens
|
|
244
256
|
let tokens = model.resolvedTotalTokens
|
|
245
257
|
let requests = model.resolvedRequestCount
|
|
246
|
-
let cost: Double?
|
|
247
|
-
|
|
248
|
-
cost = QwenModelPricing.estimateCost(inputTokens: input, outputTokens: output, modelID: modelID)
|
|
258
|
+
let cost: Double? = if input > 0 || output > 0 {
|
|
259
|
+
QwenModelPricing.estimateCost(inputTokens: input, outputTokens: output, modelID: modelID)
|
|
249
260
|
} else {
|
|
250
|
-
|
|
261
|
+
QwenModelPricing.estimateCost(tokens: tokens, modelID: modelID)
|
|
251
262
|
}
|
|
252
263
|
entries.append(QwenModelUsageEntry(
|
|
253
264
|
modelID: modelID,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -71,7 +71,7 @@ struct SambaNovaAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum SambaNovaSettingsError: LocalizedError
|
|
74
|
+
enum SambaNovaSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct SambaNovaModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum SambaNovaUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.sambanova.ai/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> SambaNovaModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension SambaNovaModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum SambaNovaAPIError: LocalizedError
|
|
60
|
+
enum SambaNovaAPIError: LocalizedError {
|
|
61
61
|
case invalidResponse
|
|
62
62
|
case httpError(statusCode: Int, body: String?)
|
|
63
63
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -71,7 +71,7 @@ struct TogetherAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum TogetherSettingsError: LocalizedError
|
|
74
|
+
enum TogetherSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct TogetherModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum TogetherUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.together.xyz/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> TogetherModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension TogetherModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum TogetherAPIError: LocalizedError
|
|
60
|
+
enum TogetherAPIError: LocalizedError {
|
|
61
61
|
case invalidResponse
|
|
62
62
|
case httpError(statusCode: Int, body: String?)
|
|
63
63
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -75,7 +75,7 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
private static func resolveProject(context: ProviderFetchContext) -> String? {
|
|
78
|
-
if let project =
|
|
78
|
+
if let project = cleaned(context.settings?.vertexai?.project) {
|
|
79
79
|
return project
|
|
80
80
|
}
|
|
81
81
|
if let project = Self.cleaned(context.env["VERTEX_AI_PROJECT"]) {
|
|
@@ -91,7 +91,7 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
private static func resolveLocation(context: ProviderFetchContext) -> String? {
|
|
94
|
-
if let location =
|
|
94
|
+
if let location = cleaned(context.settings?.vertexai?.location) {
|
|
95
95
|
return location
|
|
96
96
|
}
|
|
97
97
|
if let location = Self.cleaned(context.env["VERTEX_AI_LOCATION"]) {
|
|
@@ -110,13 +110,13 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
enum VertexAISettingsError: LocalizedError
|
|
113
|
+
enum VertexAISettingsError: LocalizedError {
|
|
114
114
|
case missingProject
|
|
115
115
|
|
|
116
116
|
var errorDescription: String? {
|
|
117
117
|
switch self {
|
|
118
118
|
case .missingProject:
|
|
119
|
-
|
|
119
|
+
"Google Cloud project not found. Set it in Preferences \u{2192} Providers \u{2192} Vertex AI or " +
|
|
120
120
|
"export VERTEX_AI_PROJECT."
|
|
121
121
|
}
|
|
122
122
|
}
|
|
@@ -27,7 +27,7 @@ public struct VertexAISettingsReader: Sendable {
|
|
|
27
27
|
public static func credentials(
|
|
28
28
|
environment: [String: String] = ProcessInfo.processInfo.environment) -> String?
|
|
29
29
|
{
|
|
30
|
-
self.cleaned(environment[credentialsKey])
|
|
30
|
+
self.cleaned(environment[self.credentialsKey])
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
static func cleaned(_ raw: String?) -> String? {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
struct VertexAIModelsResponse: Decodable
|
|
4
|
-
struct Model: Decodable
|
|
3
|
+
struct VertexAIModelsResponse: Decodable {
|
|
4
|
+
struct Model: Decodable {
|
|
5
5
|
let name: String?
|
|
6
6
|
let displayName: String?
|
|
7
7
|
}
|
|
@@ -117,7 +117,7 @@ extension VertexAIModelsResponse {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
enum VertexAICLIError: LocalizedError
|
|
120
|
+
enum VertexAICLIError: LocalizedError {
|
|
121
121
|
case gcloudNotFound
|
|
122
122
|
case commandFailed(statusCode: Int, detail: String?)
|
|
123
123
|
case commandTimedOut
|