@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
struct BedrockModelsResponse: Decodable
|
|
4
|
-
struct ModelSummary: Decodable
|
|
3
|
+
struct BedrockModelsResponse: Decodable {
|
|
4
|
+
struct ModelSummary: Decodable {
|
|
5
5
|
let modelID: String?
|
|
6
6
|
let modelName: String?
|
|
7
7
|
let providerName: String?
|
|
@@ -14,10 +14,6 @@ struct BedrockModelsResponse: Decodable, Sendable {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
let modelSummaries: [ModelSummary]?
|
|
17
|
-
|
|
18
|
-
init(modelSummaries: [ModelSummary]?) {
|
|
19
|
-
self.modelSummaries = modelSummaries
|
|
20
|
-
}
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
struct BedrockUsageFetcher {
|
|
@@ -141,7 +137,7 @@ extension BedrockModelsResponse {
|
|
|
141
137
|
}
|
|
142
138
|
}
|
|
143
139
|
|
|
144
|
-
enum BedrockCLIError: LocalizedError
|
|
140
|
+
enum BedrockCLIError: LocalizedError {
|
|
145
141
|
case awsCLINotFound
|
|
146
142
|
case commandFailed(statusCode: Int, detail: String?)
|
|
147
143
|
case commandTimedOut
|
|
@@ -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 CerebrasAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum CerebrasSettingsError: LocalizedError
|
|
74
|
+
enum CerebrasSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct CerebrasModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum CerebrasUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.cerebras.ai/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> CerebrasModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension CerebrasModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum CerebrasAPIError: LocalizedError
|
|
60
|
+
enum CerebrasAPIError: LocalizedError {
|
|
61
61
|
case invalidResponse
|
|
62
62
|
case httpError(statusCode: Int, body: String?)
|
|
63
63
|
|
|
@@ -82,7 +82,7 @@ enum ClaudeOAuthUsageFetcher {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
struct OAuthUsageResponse: Decodable
|
|
85
|
+
struct OAuthUsageResponse: Decodable {
|
|
86
86
|
let fiveHour: OAuthUsageWindow?
|
|
87
87
|
let sevenDay: OAuthUsageWindow?
|
|
88
88
|
let sevenDayOAuthApps: OAuthUsageWindow?
|
|
@@ -102,7 +102,7 @@ struct OAuthUsageResponse: Decodable, Sendable {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
struct OAuthUsageWindow: Decodable
|
|
105
|
+
struct OAuthUsageWindow: Decodable {
|
|
106
106
|
let utilization: Double?
|
|
107
107
|
let resetsAt: String?
|
|
108
108
|
|
|
@@ -112,7 +112,7 @@ struct OAuthUsageWindow: Decodable, Sendable {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
struct OAuthExtraUsage: Decodable
|
|
115
|
+
struct OAuthExtraUsage: Decodable {
|
|
116
116
|
let isEnabled: Bool?
|
|
117
117
|
let monthlyLimit: Double?
|
|
118
118
|
let usedCredits: Double?
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -191,7 +191,9 @@ struct ClaudeCLIFetchStrategy: ProviderFetchStrategy {
|
|
|
191
191
|
let kind: ProviderFetchKind = .cli
|
|
192
192
|
let useWebExtras: Bool
|
|
193
193
|
|
|
194
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
194
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
195
|
+
true
|
|
196
|
+
}
|
|
195
197
|
|
|
196
198
|
func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
197
199
|
let fetcher = ClaudeUsageFetcher(dataSource: .cli, useWebExtras: self.useWebExtras)
|
|
@@ -326,7 +326,7 @@ public struct ClaudeStatusProbe: Sendable {
|
|
|
326
326
|
return nil
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
/// Collect remaining percentages in the order they appear; used as a backup when labels move/rename.
|
|
330
330
|
private static func allPercents(_ text: String) -> [Int] {
|
|
331
331
|
let pat = #"([0-9]{1,3})\p{Zs}*%\s*(left|used)"#
|
|
332
332
|
guard let regex = try? NSRegularExpression(pattern: pat, options: [.caseInsensitive]) else { return [] }
|
|
@@ -379,7 +379,7 @@ public struct ClaudeStatusProbe: Sendable {
|
|
|
379
379
|
.joined(separator: " ")
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
|
|
382
|
+
/// Capture all "Resets ..." strings to surface in the menu.
|
|
383
383
|
private static func allResets(_ text: String) -> [String] {
|
|
384
384
|
let pat = #"Resets[^\r\n]*"#
|
|
385
385
|
guard let regex = try? NSRegularExpression(pattern: pat, options: [.caseInsensitive]) else { return [] }
|
|
@@ -500,7 +500,7 @@ public struct ClaudeStatusProbe: Sendable {
|
|
|
500
500
|
return nil
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
-
|
|
503
|
+
/// Extract login/plan string from CLI output.
|
|
504
504
|
private static func extractLoginMethod(text: String) -> String? {
|
|
505
505
|
guard !text.isEmpty else { return nil }
|
|
506
506
|
if let explicit = self.extractFirst(pattern: #"(?i)login\s+method:\s*(.+)"#, text: text) {
|
|
@@ -627,7 +627,7 @@ public struct ClaudeStatusProbe: Sendable {
|
|
|
627
627
|
}
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
-
|
|
630
|
+
/// Run claude CLI inside a PTY so we can respond to interactive permission prompts.
|
|
631
631
|
private static func capture(subcommand: String, binary: String, timeout: TimeInterval) async throws -> String {
|
|
632
632
|
let stopOnSubstrings = subcommand == "/usage" ? ["Current session"] : []
|
|
633
633
|
do {
|
|
@@ -133,6 +133,7 @@ public enum CodexOAuthCredentialsStore {
|
|
|
133
133
|
let directory = url.deletingLastPathComponent()
|
|
134
134
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
135
135
|
try data.write(to: url, options: .atomic)
|
|
136
|
+
try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path)
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
private static func parseLastRefresh(from raw: Any?) -> Date? {
|
|
@@ -192,12 +192,23 @@ public enum CodexOAuthUsageFetcher {
|
|
|
192
192
|
self.resolveUsageURL(env: ProcessInfo.processInfo.environment, configContents: nil)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
private static let allowedHostSuffixes = ["openai.com", "chatgpt.com"]
|
|
196
|
+
|
|
195
197
|
private static func resolveUsageURL(env: [String: String], configContents: String?) -> URL {
|
|
196
198
|
let baseURL = self.resolveChatGPTBaseURL(env: env, configContents: configContents)
|
|
197
199
|
let normalized = self.normalizeChatGPTBaseURL(baseURL)
|
|
198
200
|
let path = normalized.contains("/backend-api") ? Self.chatGPTUsagePath : Self.codexUsagePath
|
|
199
201
|
let full = normalized + path
|
|
200
|
-
|
|
202
|
+
guard let url = URL(string: full), self.isAllowedHost(url) else {
|
|
203
|
+
return URL(string: Self.defaultChatGPTBaseURL + Self.chatGPTUsagePath)!
|
|
204
|
+
}
|
|
205
|
+
return url
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private static func isAllowedHost(_ url: URL) -> Bool {
|
|
209
|
+
guard url.scheme?.lowercased() == "https" else { return false }
|
|
210
|
+
guard let host = url.host?.lowercased() else { return false }
|
|
211
|
+
return self.allowedHostSuffixes.contains { host == $0 || host.hasSuffix("." + $0) }
|
|
201
212
|
}
|
|
202
213
|
|
|
203
214
|
private static func resolveChatGPTBaseURL(env: [String: String], configContents: String?) -> String {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -119,7 +119,9 @@ struct CodexCLIUsageStrategy: ProviderFetchStrategy {
|
|
|
119
119
|
let id: String = "codex.cli"
|
|
120
120
|
let kind: ProviderFetchKind = .cli
|
|
121
121
|
|
|
122
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
122
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
123
|
+
true
|
|
124
|
+
}
|
|
123
125
|
|
|
124
126
|
func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
125
127
|
let usage = try await context.fetcher.loadLatestUsage()
|
|
@@ -39,7 +39,7 @@ public struct CodexWebDashboardStrategy: ProviderFetchStrategy {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
private struct OpenAIWebCodexResult
|
|
42
|
+
private struct OpenAIWebCodexResult {
|
|
43
43
|
let usage: UsageSnapshot
|
|
44
44
|
let credits: CreditsSnapshot?
|
|
45
45
|
let dashboard: OpenAIDashboardSnapshot
|
|
@@ -56,7 +56,7 @@ private enum OpenAIWebCodexError: LocalizedError {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
private struct OpenAIWebOptions
|
|
59
|
+
private struct OpenAIWebOptions {
|
|
60
60
|
let timeout: TimeInterval
|
|
61
61
|
let debugDumpHTML: Bool
|
|
62
62
|
let verbose: Bool
|
|
@@ -168,7 +168,9 @@ public struct CodexWebDashboardStrategy: ProviderFetchStrategy {
|
|
|
168
168
|
|
|
169
169
|
public init() {}
|
|
170
170
|
|
|
171
|
-
public func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
171
|
+
public func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
172
|
+
false
|
|
173
|
+
}
|
|
172
174
|
|
|
173
175
|
public func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
174
176
|
throw ProviderFetchError.noAvailableStrategy(.codex)
|
|
@@ -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 CohereAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum CohereSettingsError: LocalizedError
|
|
74
|
+
enum CohereSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -9,7 +9,7 @@ struct CohereModelsResponse: Decodable {
|
|
|
9
9
|
let name: String?
|
|
10
10
|
|
|
11
11
|
var modelID: String? {
|
|
12
|
-
let candidate = id ?? name
|
|
12
|
+
let candidate = self.id ?? self.name
|
|
13
13
|
guard let candidate else { return nil }
|
|
14
14
|
let trimmed = candidate.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
15
15
|
return trimmed.isEmpty ? nil : trimmed
|
|
@@ -17,7 +17,7 @@ struct CohereModelsResponse: Decodable {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
enum CohereUsageFetcher {
|
|
21
21
|
static let apiURL = URL(string: "https://api.cohere.ai/v1/models")!
|
|
22
22
|
|
|
23
23
|
static func fetchModels(apiKey: String) async throws -> CohereModelsResponse {
|
|
@@ -67,7 +67,7 @@ extension CohereModelsResponse {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
enum CohereAPIError: LocalizedError
|
|
70
|
+
enum CohereAPIError: LocalizedError {
|
|
71
71
|
case invalidResponse
|
|
72
72
|
case httpError(statusCode: Int, body: String?)
|
|
73
73
|
|
|
@@ -67,7 +67,7 @@ public enum CopilotGitHubCLITokenReader {
|
|
|
67
67
|
|
|
68
68
|
private static func isLikelyGitHubToken(_ token: String) -> Bool {
|
|
69
69
|
guard !token.isEmpty else { return false }
|
|
70
|
-
return tokenPrefixes.contains { token.hasPrefix($0) }
|
|
70
|
+
return self.tokenPrefixes.contains { token.hasPrefix($0) }
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
private static func candidateHostConfigPaths(environment: [String: String]) -> [URL] {
|
|
@@ -97,9 +97,12 @@ public enum CopilotGitHubCLITokenReader {
|
|
|
97
97
|
|
|
98
98
|
for home in homes {
|
|
99
99
|
let homeURL = URL(fileURLWithPath: home)
|
|
100
|
-
addCandidate(homeURL.appendingPathComponent(".config").appendingPathComponent("gh")
|
|
101
|
-
|
|
102
|
-
addCandidate(homeURL.appendingPathComponent("
|
|
100
|
+
addCandidate(homeURL.appendingPathComponent(".config").appendingPathComponent("gh")
|
|
101
|
+
.appendingPathComponent("hosts.yml"))
|
|
102
|
+
addCandidate(homeURL.appendingPathComponent("Library").appendingPathComponent("Application Support")
|
|
103
|
+
.appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
|
|
104
|
+
addCandidate(homeURL.appendingPathComponent(".local").appendingPathComponent("share")
|
|
105
|
+
.appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
return candidates
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -85,11 +85,11 @@ struct CopilotAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
85
85
|
private static func sourceLabel(from source: ProviderTokenSource, sourceKey: String?) -> String {
|
|
86
86
|
switch source {
|
|
87
87
|
case .keychain:
|
|
88
|
-
|
|
88
|
+
sourceKey ?? "keychain"
|
|
89
89
|
case .environment:
|
|
90
|
-
|
|
90
|
+
sourceKey ?? "env"
|
|
91
91
|
case .vscode:
|
|
92
|
-
|
|
92
|
+
sourceKey ?? "vscode"
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -158,7 +158,8 @@ public struct CopilotUsageFetcher: Sendable {
|
|
|
158
158
|
guard httpResponse.statusCode == 200 else {
|
|
159
159
|
let body = self.errorSnippet(from: data)
|
|
160
160
|
if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
|
|
161
|
-
throw CopilotUsageFetchError
|
|
161
|
+
throw CopilotUsageFetchError
|
|
162
|
+
.unauthorized(details: "token exchange HTTP \(httpResponse.statusCode). \(body)")
|
|
162
163
|
}
|
|
163
164
|
throw CopilotUsageFetchError.invalidResponse(
|
|
164
165
|
details: "token exchange HTTP \(httpResponse.statusCode). \(body)")
|
|
@@ -253,7 +254,9 @@ public struct CopilotUsageFetcher: Sendable {
|
|
|
253
254
|
.replacingOccurrences(of: "-", with: " ")
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
private func orderedQuotaSnapshots(_ snapshots: CopilotUsageResponse
|
|
257
|
+
private func orderedQuotaSnapshots(_ snapshots: CopilotUsageResponse
|
|
258
|
+
.QuotaSnapshots?) -> [CopilotUsageResponse.QuotaSnapshot]
|
|
259
|
+
{
|
|
257
260
|
guard let snapshots else { return [] }
|
|
258
261
|
let candidates = [snapshots.premiumInteractions, snapshots.completions, snapshots.chat]
|
|
259
262
|
var ordered: [CopilotUsageResponse.QuotaSnapshot] = []
|
|
@@ -287,5 +290,4 @@ public struct CopilotUsageFetcher: Sendable {
|
|
|
287
290
|
let end = text.index(text.startIndex, offsetBy: 220)
|
|
288
291
|
return "\(text[..<end])…"
|
|
289
292
|
}
|
|
290
|
-
|
|
291
293
|
}
|
|
@@ -10,7 +10,7 @@ import CryptoKit
|
|
|
10
10
|
#endif
|
|
11
11
|
|
|
12
12
|
public enum CopilotVSCodeTokenReader {
|
|
13
|
-
private struct VSCodeStoreCandidate
|
|
13
|
+
private struct VSCodeStoreCandidate {
|
|
14
14
|
let databaseURL: URL
|
|
15
15
|
let safeStorageService: String
|
|
16
16
|
}
|
|
@@ -36,9 +36,9 @@ public enum CopilotVSCodeTokenReader {
|
|
|
36
36
|
guard let password = self.readSafeStoragePassword(
|
|
37
37
|
service: candidate.safeStorageService,
|
|
38
38
|
allowUserInteraction: allowUserInteraction),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
let legacyKey = self.deriveLegacyKey(from: password),
|
|
40
|
+
let decryptedJSON = self.decryptSecretPayload(encryptedPayload, legacyKey: legacyKey),
|
|
41
|
+
let token = self.extractToken(fromDecryptedJSON: decryptedJSON)
|
|
42
42
|
else {
|
|
43
43
|
continue
|
|
44
44
|
}
|
|
@@ -217,14 +217,13 @@ public enum CopilotVSCodeTokenReader {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
private static func decryptSecretPayload(_ payload: Data, legacyKey: Data) -> String? {
|
|
220
|
-
let encryptedPayload: Data
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
prefix == "v10" || prefix == "v11"
|
|
220
|
+
let encryptedPayload: Data = if payload.count >= 3,
|
|
221
|
+
let prefix = String(data: payload.prefix(3), encoding: .utf8),
|
|
222
|
+
prefix == "v10" || prefix == "v11"
|
|
224
223
|
{
|
|
225
|
-
|
|
224
|
+
Data(payload.dropFirst(3))
|
|
226
225
|
} else {
|
|
227
|
-
|
|
226
|
+
payload
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
let iv = Data(repeating: 0x20, count: kCCBlockSizeAES128)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -49,7 +49,9 @@ struct CursorStatusFetchStrategy: ProviderFetchStrategy {
|
|
|
49
49
|
let id: String = "cursor.web"
|
|
50
50
|
let kind: ProviderFetchKind = .web
|
|
51
51
|
|
|
52
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
52
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
53
|
+
true
|
|
54
|
+
}
|
|
53
55
|
|
|
54
56
|
func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
55
57
|
let probe = CursorStatusProbe()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -72,7 +72,7 @@ struct DeepSeekAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
enum DeepSeekSettingsError: LocalizedError
|
|
75
|
+
enum DeepSeekSettingsError: LocalizedError {
|
|
76
76
|
case missingToken
|
|
77
77
|
|
|
78
78
|
var errorDescription: String? {
|
|
@@ -25,7 +25,7 @@ struct DeepSeekBalanceResponse: Decodable {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
enum DeepSeekUsageFetcher {
|
|
29
29
|
static let apiURL = URL(string: "https://api.deepseek.com/user/balance")!
|
|
30
30
|
|
|
31
31
|
static func fetchBalance(apiKey: String) async throws -> DeepSeekBalanceResponse {
|
|
@@ -90,7 +90,7 @@ extension DeepSeekBalanceResponse {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
enum DeepSeekAPIError: LocalizedError
|
|
93
|
+
enum DeepSeekAPIError: LocalizedError {
|
|
94
94
|
case invalidResponse
|
|
95
95
|
case httpError(statusCode: Int, body: String?)
|
|
96
96
|
|
|
@@ -5,7 +5,7 @@ import SQLite3
|
|
|
5
5
|
|
|
6
6
|
#if os(macOS)
|
|
7
7
|
enum FactoryLocalStorageImporter {
|
|
8
|
-
struct TokenInfo
|
|
8
|
+
struct TokenInfo {
|
|
9
9
|
let refreshToken: String
|
|
10
10
|
let accessToken: String?
|
|
11
11
|
let organizationID: String?
|
|
@@ -61,12 +61,12 @@ enum FactoryLocalStorageImporter {
|
|
|
61
61
|
|
|
62
62
|
// MARK: - Chrome local storage discovery
|
|
63
63
|
|
|
64
|
-
private enum LocalStorageSourceKind
|
|
64
|
+
private enum LocalStorageSourceKind {
|
|
65
65
|
case chromeLevelDB(URL)
|
|
66
66
|
case safariSQLite(URL)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
private struct LocalStorageCandidate
|
|
69
|
+
private struct LocalStorageCandidate {
|
|
70
70
|
let label: String
|
|
71
71
|
let kind: LocalStorageSourceKind
|
|
72
72
|
}
|
|
@@ -207,7 +207,7 @@ enum FactoryLocalStorageImporter {
|
|
|
207
207
|
|
|
208
208
|
// MARK: - Token extraction
|
|
209
209
|
|
|
210
|
-
private struct WorkOSTokenMatch
|
|
210
|
+
private struct WorkOSTokenMatch {
|
|
211
211
|
let refreshToken: String
|
|
212
212
|
let accessToken: String?
|
|
213
213
|
let organizationID: String?
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -49,7 +49,9 @@ struct FactoryStatusFetchStrategy: ProviderFetchStrategy {
|
|
|
49
49
|
let id: String = "factory.web"
|
|
50
50
|
let kind: ProviderFetchKind = .web
|
|
51
51
|
|
|
52
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
52
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
53
|
+
true
|
|
54
|
+
}
|
|
53
55
|
|
|
54
56
|
func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
55
57
|
let probe = FactoryStatusProbe()
|
|
@@ -464,6 +464,7 @@ public actor FactorySessionStore {
|
|
|
464
464
|
return
|
|
465
465
|
}
|
|
466
466
|
try? data.write(to: self.fileURL)
|
|
467
|
+
try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: self.fileURL.path)
|
|
467
468
|
}
|
|
468
469
|
|
|
469
470
|
private func loadFromDisk() {
|
|
@@ -535,7 +536,7 @@ public struct FactoryStatusProbe: Sendable {
|
|
|
535
536
|
"client_01HNM792M5G5G1A2THWPXKFMXB",
|
|
536
537
|
]
|
|
537
538
|
|
|
538
|
-
private struct WorkOSAuthResponse: Decodable
|
|
539
|
+
private struct WorkOSAuthResponse: Decodable {
|
|
539
540
|
let access_token: String
|
|
540
541
|
let refresh_token: String?
|
|
541
542
|
let organization_id: String?
|
|
@@ -577,7 +578,7 @@ public struct FactoryStatusProbe: Sendable {
|
|
|
577
578
|
throw FactoryStatusProbeError.noSessionCookie
|
|
578
579
|
}
|
|
579
580
|
|
|
580
|
-
private enum FetchAttemptResult
|
|
581
|
+
private enum FetchAttemptResult {
|
|
581
582
|
case success(FactoryStatusSnapshot)
|
|
582
583
|
case failure(Error)
|
|
583
584
|
case skipped
|
|
@@ -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 FireworksAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum FireworksSettingsError: LocalizedError
|
|
74
|
+
enum FireworksSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -8,7 +8,7 @@ struct FireworksModelsResponse: Decodable {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
enum FireworksUsageFetcher {
|
|
12
12
|
static let apiURL = URL(string: "https://api.fireworks.ai/inference/v1/models")!
|
|
13
13
|
|
|
14
14
|
static func fetchModels(apiKey: String) async throws -> FireworksModelsResponse {
|
|
@@ -57,7 +57,7 @@ extension FireworksModelsResponse {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
enum FireworksAPIError: LocalizedError
|
|
60
|
+
enum FireworksAPIError: 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
|
|
@@ -49,7 +49,9 @@ struct GeminiStatusFetchStrategy: ProviderFetchStrategy {
|
|
|
49
49
|
let id: String = "gemini.api"
|
|
50
50
|
let kind: ProviderFetchKind = .apiToken
|
|
51
51
|
|
|
52
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
52
|
+
func isAvailable(_: ProviderFetchContext) async -> Bool {
|
|
53
|
+
true
|
|
54
|
+
}
|
|
53
55
|
|
|
54
56
|
func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
55
57
|
let probe = GeminiStatusProbe()
|
|
@@ -21,8 +21,11 @@ public struct GeminiStatusSnapshot: Sendable {
|
|
|
21
21
|
self.modelQuotas.min(by: { $0.percentLeft < $1.percentLeft })?.percentLeft
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
public var dailyPercentLeft: Double? {
|
|
24
|
+
/// Legacy compatibility
|
|
25
|
+
public var dailyPercentLeft: Double? {
|
|
26
|
+
self.lowestPercentLeft
|
|
27
|
+
}
|
|
28
|
+
|
|
26
29
|
public var resetDescription: String? {
|
|
27
30
|
self.modelQuotas.min(by: { $0.percentLeft < $1.percentLeft })?.resetDescription
|
|
28
31
|
}
|
|
@@ -619,6 +622,7 @@ public struct GeminiStatusProbe: Sendable {
|
|
|
619
622
|
|
|
620
623
|
let updatedData = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted])
|
|
621
624
|
try updatedData.write(to: credsURL, options: .atomic)
|
|
625
|
+
try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: credsURL.path)
|
|
622
626
|
}
|
|
623
627
|
|
|
624
628
|
private static func loadCredentials(homeDirectory: String) throws -> OAuthCredentials {
|
|
@@ -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 GroqAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum GroqSettingsError: LocalizedError
|
|
74
|
+
enum GroqSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|