@sriinnu/runic 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +8 -1
- package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
- package/RuniciOS/Views/ProviderDetailView.swift +9 -9
- package/RuniciOS/Views/ProviderListView.swift +10 -10
- package/RuniciOS/Widgets/LargeWidget.swift +14 -14
- package/RuniciOS/Widgets/LiveActivity.swift +19 -19
- package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
- package/RuniciOS/Widgets/MediumWidget.swift +10 -10
- package/RuniciOS/Widgets/RunicWidget.swift +5 -5
- package/RuniciOS/Widgets/SmallWidget.swift +9 -9
- package/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
- package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
- package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
- package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
- package/Sources/Runic/Core/RunicColors.swift +6 -6
- package/Sources/Runic/Core/RunicSpacing.swift +7 -7
- package/Sources/Runic/Core/RunicTypography.swift +206 -0
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
- package/Sources/Runic/Core/UsageExporter.swift +8 -4
- package/Sources/Runic/ProviderRegistry.swift +1 -1
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
- package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/RunicApp.swift +46 -36
- package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
- package/Sources/Runic/UsagePaceText.swift +1 -1
- package/Sources/Runic/UsageProgressBar.swift +13 -9
- package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
- package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
- package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
- package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
- package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
- package/Sources/RunicCLI/CLIEntry.swift +83 -42
- package/Sources/RunicCLI/CLIRenderer.swift +1 -1
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
- package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
- package/Sources/RunicClaudeWebProbe/main.swift +1 -1
- package/Sources/RunicCore/AlertRuleStore.swift +20 -20
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
- package/Sources/RunicCore/CostUsageModels.swift +1 -1
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
- package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
- package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
- package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
- package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
- package/Sources/RunicCore/Team/TeamModels.swift +111 -110
- package/Sources/RunicCore/Team/TeamStore.swift +126 -109
- package/Sources/RunicCore/UsageFetcher.swift +2 -2
- package/Sources/RunicCore/UsageFormatter.swift +25 -25
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
- package/Tests/RunicTests/AppDelegateTests.swift +8 -3
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
- package/Tests/RunicTests/CLICostTests.swift +6 -7
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
- package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
- package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
- package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
- package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
- package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
- package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
- package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
- package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
- package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
- package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
- package/Tests/RunicTests/InstallOriginTests.swift +1 -2
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
- package/Tests/RunicTests/LiveAccountTests.swift +3 -3
- package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
- package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
- package/Tests/RunicTests/PathBuilderTests.swift +14 -15
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
- package/Tests/RunicTests/RunicTests.swift +6 -7
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
- package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
- package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
- package/Tests/RunicTests/StatusMenuTests.swift +63 -22
- package/Tests/RunicTests/StatusProbeTests.swift +28 -29
- package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
- package/Tests/RunicTests/TextParsingTests.swift +1 -2
- package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
- package/Tests/RunicTests/UsagePaceTests.swift +5 -6
- package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
- package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
- package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
- package/appcast.xml +36 -76
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -3,7 +3,6 @@ import SQLite3
|
|
|
3
3
|
|
|
4
4
|
/// Storage layer for performance metrics using SQLite
|
|
5
5
|
public final class PerformanceStorageImpl {
|
|
6
|
-
|
|
7
6
|
// MARK: - Properties
|
|
8
7
|
|
|
9
8
|
private var db: OpaquePointer?
|
|
@@ -14,8 +13,7 @@ public final class PerformanceStorageImpl {
|
|
|
14
13
|
public init() {
|
|
15
14
|
let appSupport = FileManager.default.urls(
|
|
16
15
|
for: .applicationSupportDirectory,
|
|
17
|
-
in: .userDomainMask
|
|
18
|
-
).first!
|
|
16
|
+
in: .userDomainMask).first!
|
|
19
17
|
let runicDir = appSupport.appendingPathComponent("Runic")
|
|
20
18
|
|
|
21
19
|
// Ensure directory exists
|
|
@@ -25,7 +23,7 @@ public final class PerformanceStorageImpl {
|
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
deinit {
|
|
28
|
-
if let db
|
|
26
|
+
if let db {
|
|
29
27
|
sqlite3_close(db)
|
|
30
28
|
}
|
|
31
29
|
}
|
|
@@ -33,20 +31,20 @@ public final class PerformanceStorageImpl {
|
|
|
33
31
|
// MARK: - Database Connection
|
|
34
32
|
|
|
35
33
|
private func open() throws {
|
|
36
|
-
guard db == nil else { return }
|
|
34
|
+
guard self.db == nil else { return }
|
|
37
35
|
|
|
38
36
|
let flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX
|
|
39
|
-
let result = sqlite3_open_v2(dbPath.path, &db, flags, nil)
|
|
37
|
+
let result = sqlite3_open_v2(dbPath.path, &self.db, flags, nil)
|
|
40
38
|
|
|
41
39
|
guard result == SQLITE_OK else {
|
|
42
|
-
throw StorageError.openFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
40
|
+
throw StorageError.openFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
try createTablesIfNeeded()
|
|
43
|
+
try self.createTablesIfNeeded()
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
private func close() {
|
|
49
|
-
if let db
|
|
47
|
+
if let db {
|
|
50
48
|
sqlite3_close(db)
|
|
51
49
|
self.db = nil
|
|
52
50
|
}
|
|
@@ -120,18 +118,18 @@ public final class PerformanceStorageImpl {
|
|
|
120
118
|
"CREATE INDEX IF NOT EXISTS idx_latency_created ON latency_metrics(created_at)",
|
|
121
119
|
"CREATE INDEX IF NOT EXISTS idx_quality_timestamp ON quality_ratings(timestamp)",
|
|
122
120
|
"CREATE INDEX IF NOT EXISTS idx_error_timestamp ON error_events(timestamp)",
|
|
123
|
-
"CREATE INDEX IF NOT EXISTS idx_daily_stats_date ON daily_stats(date)"
|
|
121
|
+
"CREATE INDEX IF NOT EXISTS idx_daily_stats_date ON daily_stats(date)",
|
|
124
122
|
]
|
|
125
123
|
|
|
126
124
|
for schema in schemas {
|
|
127
|
-
try execute(schema)
|
|
125
|
+
try self.execute(schema)
|
|
128
126
|
}
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
// MARK: - Execute SQL
|
|
132
130
|
|
|
133
131
|
private func execute(_ sql: String) throws {
|
|
134
|
-
guard let db
|
|
132
|
+
guard let db else {
|
|
135
133
|
throw StorageError.notOpen
|
|
136
134
|
}
|
|
137
135
|
|
|
@@ -148,24 +146,29 @@ public final class PerformanceStorageImpl {
|
|
|
148
146
|
// MARK: - Save Metrics
|
|
149
147
|
|
|
150
148
|
public func save(latency: LatencyMetric) async throws {
|
|
151
|
-
try open()
|
|
149
|
+
try self.open()
|
|
152
150
|
|
|
153
151
|
let sql = """
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
INSERT OR REPLACE INTO latency_metrics
|
|
153
|
+
(id, request_id, provider, model, start_time, end_time, duration_ms, success, created_at)
|
|
154
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
155
|
+
"""
|
|
158
156
|
|
|
159
157
|
var statement: OpaquePointer?
|
|
160
158
|
defer { sqlite3_finalize(statement) }
|
|
161
159
|
|
|
162
|
-
guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
163
|
-
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
160
|
+
guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
161
|
+
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
164
162
|
}
|
|
165
163
|
|
|
166
164
|
sqlite3_bind_text(statement, 1, latency.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
167
165
|
sqlite3_bind_text(statement, 2, latency.requestID, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
168
|
-
sqlite3_bind_text(
|
|
166
|
+
sqlite3_bind_text(
|
|
167
|
+
statement,
|
|
168
|
+
3,
|
|
169
|
+
latency.provider.rawValue,
|
|
170
|
+
-1,
|
|
171
|
+
unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
169
172
|
|
|
170
173
|
if let model = latency.model {
|
|
171
174
|
sqlite3_bind_text(statement, 4, model, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
@@ -180,29 +183,34 @@ public final class PerformanceStorageImpl {
|
|
|
180
183
|
sqlite3_bind_double(statement, 9, latency.createdAt.timeIntervalSince1970)
|
|
181
184
|
|
|
182
185
|
guard sqlite3_step(statement) == SQLITE_DONE else {
|
|
183
|
-
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
186
|
+
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
184
187
|
}
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
public func save(rating: QualityRating) async throws {
|
|
188
|
-
try open()
|
|
191
|
+
try self.open()
|
|
189
192
|
|
|
190
193
|
let sql = """
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
INSERT OR REPLACE INTO quality_ratings
|
|
195
|
+
(id, request_id, provider, model, rating, comment, timestamp)
|
|
196
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
197
|
+
"""
|
|
195
198
|
|
|
196
199
|
var statement: OpaquePointer?
|
|
197
200
|
defer { sqlite3_finalize(statement) }
|
|
198
201
|
|
|
199
|
-
guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
200
|
-
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
202
|
+
guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
203
|
+
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
sqlite3_bind_text(statement, 1, rating.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
204
207
|
sqlite3_bind_text(statement, 2, rating.requestID, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
205
|
-
sqlite3_bind_text(
|
|
208
|
+
sqlite3_bind_text(
|
|
209
|
+
statement,
|
|
210
|
+
3,
|
|
211
|
+
rating.provider.rawValue,
|
|
212
|
+
-1,
|
|
213
|
+
unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
206
214
|
|
|
207
215
|
if let model = rating.model {
|
|
208
216
|
sqlite3_bind_text(statement, 4, model, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
@@ -221,29 +229,39 @@ public final class PerformanceStorageImpl {
|
|
|
221
229
|
sqlite3_bind_double(statement, 7, rating.timestamp.timeIntervalSince1970)
|
|
222
230
|
|
|
223
231
|
guard sqlite3_step(statement) == SQLITE_DONE else {
|
|
224
|
-
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
232
|
+
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
225
233
|
}
|
|
226
234
|
}
|
|
227
235
|
|
|
228
236
|
public func save(error: ErrorEvent) async throws {
|
|
229
|
-
try open()
|
|
237
|
+
try self.open()
|
|
230
238
|
|
|
231
239
|
let sql = """
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
240
|
+
INSERT OR REPLACE INTO error_events
|
|
241
|
+
(id, provider, error_type, error_message, retry_count, timestamp)
|
|
242
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
243
|
+
"""
|
|
236
244
|
|
|
237
245
|
var statement: OpaquePointer?
|
|
238
246
|
defer { sqlite3_finalize(statement) }
|
|
239
247
|
|
|
240
|
-
guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
241
|
-
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
248
|
+
guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
249
|
+
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
242
250
|
}
|
|
243
251
|
|
|
244
252
|
sqlite3_bind_text(statement, 1, error.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
245
|
-
sqlite3_bind_text(
|
|
246
|
-
|
|
253
|
+
sqlite3_bind_text(
|
|
254
|
+
statement,
|
|
255
|
+
2,
|
|
256
|
+
error.provider.rawValue,
|
|
257
|
+
-1,
|
|
258
|
+
unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
259
|
+
sqlite3_bind_text(
|
|
260
|
+
statement,
|
|
261
|
+
3,
|
|
262
|
+
error.errorType.rawValue,
|
|
263
|
+
-1,
|
|
264
|
+
unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
247
265
|
|
|
248
266
|
if let message = error.errorMessage {
|
|
249
267
|
sqlite3_bind_text(statement, 4, message, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
|
|
@@ -255,7 +273,7 @@ public final class PerformanceStorageImpl {
|
|
|
255
273
|
sqlite3_bind_double(statement, 6, error.timestamp.timeIntervalSince1970)
|
|
256
274
|
|
|
257
275
|
guard sqlite3_step(statement) == SQLITE_DONE else {
|
|
258
|
-
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
276
|
+
throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
259
277
|
}
|
|
260
278
|
}
|
|
261
279
|
|
|
@@ -264,17 +282,17 @@ public final class PerformanceStorageImpl {
|
|
|
264
282
|
public func fetchDailyStats(
|
|
265
283
|
timeRange: Int,
|
|
266
284
|
provider: UsageProvider? = nil,
|
|
267
|
-
model: String? = nil
|
|
268
|
-
|
|
269
|
-
try open()
|
|
285
|
+
model: String? = nil) async throws -> [DailyPerformanceStats]
|
|
286
|
+
{
|
|
287
|
+
try self.open()
|
|
270
288
|
|
|
271
289
|
var sql = "SELECT * FROM daily_stats WHERE date >= date('now', '-\(timeRange) days')"
|
|
272
290
|
|
|
273
|
-
if let provider
|
|
291
|
+
if let provider {
|
|
274
292
|
sql += " AND provider = '\(provider.rawValue)'"
|
|
275
293
|
}
|
|
276
294
|
|
|
277
|
-
if let model
|
|
295
|
+
if let model {
|
|
278
296
|
sql += " AND model = '\(model)'"
|
|
279
297
|
}
|
|
280
298
|
|
|
@@ -283,8 +301,8 @@ public final class PerformanceStorageImpl {
|
|
|
283
301
|
var statement: OpaquePointer?
|
|
284
302
|
defer { sqlite3_finalize(statement) }
|
|
285
303
|
|
|
286
|
-
guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
287
|
-
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
|
|
304
|
+
guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
|
|
305
|
+
throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
|
|
288
306
|
}
|
|
289
307
|
|
|
290
308
|
var stats: [DailyPerformanceStats] = []
|
|
@@ -337,8 +355,7 @@ public final class PerformanceStorageImpl {
|
|
|
337
355
|
quotaCount: quotaCount,
|
|
338
356
|
networkCount: networkCount,
|
|
339
357
|
apiErrorCount: apiErrorCount,
|
|
340
|
-
createdAt: createdAt
|
|
341
|
-
)
|
|
358
|
+
createdAt: createdAt)
|
|
342
359
|
|
|
343
360
|
stats.append(stat)
|
|
344
361
|
}
|
|
@@ -349,12 +366,12 @@ public final class PerformanceStorageImpl {
|
|
|
349
366
|
// MARK: - Database Maintenance
|
|
350
367
|
|
|
351
368
|
public func vacuum() async throws {
|
|
352
|
-
try open()
|
|
353
|
-
try execute("VACUUM")
|
|
369
|
+
try self.open()
|
|
370
|
+
try self.execute("VACUUM")
|
|
354
371
|
}
|
|
355
372
|
|
|
356
373
|
public func deleteOldData(olderThan days: Int) async throws {
|
|
357
|
-
try open()
|
|
374
|
+
try self.open()
|
|
358
375
|
|
|
359
376
|
let cutoffDate = Date().addingTimeInterval(-Double(days) * 24 * 60 * 60)
|
|
360
377
|
let cutoffTimestamp = cutoffDate.timeIntervalSince1970
|
|
@@ -362,11 +379,11 @@ public final class PerformanceStorageImpl {
|
|
|
362
379
|
let queries = [
|
|
363
380
|
"DELETE FROM latency_metrics WHERE created_at < \(cutoffTimestamp)",
|
|
364
381
|
"DELETE FROM quality_ratings WHERE timestamp < \(cutoffTimestamp)",
|
|
365
|
-
"DELETE FROM error_events WHERE timestamp < \(cutoffTimestamp)"
|
|
382
|
+
"DELETE FROM error_events WHERE timestamp < \(cutoffTimestamp)",
|
|
366
383
|
]
|
|
367
384
|
|
|
368
385
|
for query in queries {
|
|
369
|
-
try execute(query)
|
|
386
|
+
try self.execute(query)
|
|
370
387
|
}
|
|
371
388
|
}
|
|
372
389
|
|
|
@@ -382,15 +399,15 @@ public final class PerformanceStorageImpl {
|
|
|
382
399
|
public var errorDescription: String? {
|
|
383
400
|
switch self {
|
|
384
401
|
case .notOpen:
|
|
385
|
-
|
|
386
|
-
case .openFailed(
|
|
387
|
-
|
|
388
|
-
case .prepareFailed(
|
|
389
|
-
|
|
390
|
-
case .executionFailed(
|
|
391
|
-
|
|
392
|
-
case .insertFailed(
|
|
393
|
-
|
|
402
|
+
"Database not open"
|
|
403
|
+
case let .openFailed(message):
|
|
404
|
+
"Failed to open database: \(message)"
|
|
405
|
+
case let .prepareFailed(message):
|
|
406
|
+
"Failed to prepare statement: \(message)"
|
|
407
|
+
case let .executionFailed(message):
|
|
408
|
+
"Failed to execute statement: \(message)"
|
|
409
|
+
case let .insertFailed(message):
|
|
410
|
+
"Failed to insert data: \(message)"
|
|
394
411
|
}
|
|
395
412
|
}
|
|
396
413
|
}
|
|
@@ -400,7 +417,7 @@ public final class PerformanceStorageImpl {
|
|
|
400
417
|
|
|
401
418
|
extension String {
|
|
402
419
|
fileprivate init(cString pointer: UnsafePointer<UInt8>?) {
|
|
403
|
-
guard let pointer
|
|
420
|
+
guard let pointer else {
|
|
404
421
|
self = ""
|
|
405
422
|
return
|
|
406
423
|
}
|
|
@@ -145,7 +145,7 @@ public enum UsagePolicyEngine {
|
|
|
145
145
|
context: UsagePolicyContext) -> String?
|
|
146
146
|
{
|
|
147
147
|
switch condition {
|
|
148
|
-
case .projectedBudgetOverrun(
|
|
148
|
+
case let .projectedBudgetOverrun(minimumPercent):
|
|
149
149
|
guard let budget = context.budgetLimitUSD, budget > 0 else { return nil }
|
|
150
150
|
guard let projected = context.projectedSpendUSD, projected.isFinite else { return nil }
|
|
151
151
|
let overrunUSD = projected - budget
|
|
@@ -161,7 +161,7 @@ public enum UsagePolicyEngine {
|
|
|
161
161
|
guard observed >= budget else { return nil }
|
|
162
162
|
return "Observed spend \(UsageFormatter.usdString(observed)) reached budget \(UsageFormatter.usdString(budget))."
|
|
163
163
|
|
|
164
|
-
case .anomalySeverityAtLeast(
|
|
164
|
+
case let .anomalySeverityAtLeast(required):
|
|
165
165
|
guard let observed = context.anomalySeverity else { return nil }
|
|
166
166
|
guard observed.rawValue >= required.rawValue else { return nil }
|
|
167
167
|
return "Anomaly severity \(self.severityLabel(observed)) meets threshold \(self.severityLabel(required))."
|
|
@@ -2,11 +2,14 @@ import Foundation
|
|
|
2
2
|
import RunicCore
|
|
3
3
|
|
|
4
4
|
/// Storage for project budget limits and alert thresholds
|
|
5
|
-
public
|
|
5
|
+
public enum ProjectBudgetStore {
|
|
6
6
|
// MARK: - Types
|
|
7
7
|
|
|
8
8
|
public struct ProjectBudget: Codable, Sendable, Identifiable {
|
|
9
|
-
public var id: String {
|
|
9
|
+
public var id: String {
|
|
10
|
+
self.projectID
|
|
11
|
+
}
|
|
12
|
+
|
|
10
13
|
public let projectID: String
|
|
11
14
|
public var projectName: String?
|
|
12
15
|
public var monthlyLimit: Double
|
|
@@ -20,8 +23,8 @@ public struct ProjectBudgetStore {
|
|
|
20
23
|
monthlyLimit: Double,
|
|
21
24
|
alertThreshold: Double = 0.8,
|
|
22
25
|
enabled: Bool = true,
|
|
23
|
-
createdAt: Date = Date()
|
|
24
|
-
|
|
26
|
+
createdAt: Date = Date())
|
|
27
|
+
{
|
|
25
28
|
self.projectID = projectID
|
|
26
29
|
self.projectName = projectName
|
|
27
30
|
self.monthlyLimit = monthlyLimit
|
|
@@ -53,7 +56,7 @@ public struct ProjectBudgetStore {
|
|
|
53
56
|
// MARK: - Public Methods
|
|
54
57
|
|
|
55
58
|
public static func load() -> BudgetsData {
|
|
56
|
-
guard FileManager.default.fileExists(atPath: storageURL.path) else {
|
|
59
|
+
guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
|
|
57
60
|
return BudgetsData()
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -74,26 +77,26 @@ public struct ProjectBudgetStore {
|
|
|
74
77
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
75
78
|
|
|
76
79
|
let data = try encoder.encode(budgetsData)
|
|
77
|
-
try data.write(to: storageURL, options: .atomic)
|
|
80
|
+
try data.write(to: self.storageURL, options: .atomic)
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
public static func getBudget(projectID: String) -> ProjectBudget? {
|
|
81
|
-
load().budgets[projectID]
|
|
84
|
+
self.load().budgets[projectID]
|
|
82
85
|
}
|
|
83
86
|
|
|
84
87
|
public static func setBudget(_ budget: ProjectBudget) throws {
|
|
85
|
-
var data = load()
|
|
88
|
+
var data = self.load()
|
|
86
89
|
data.budgets[budget.projectID] = budget
|
|
87
|
-
try save(data)
|
|
90
|
+
try self.save(data)
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
public static func removeBudget(projectID: String) throws {
|
|
91
|
-
var data = load()
|
|
94
|
+
var data = self.load()
|
|
92
95
|
data.budgets.removeValue(forKey: projectID)
|
|
93
|
-
try save(data)
|
|
96
|
+
try self.save(data)
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
public static func getAllBudgets() -> [ProjectBudget] {
|
|
97
|
-
Array(load().budgets.values).sorted { $0.createdAt < $1.createdAt }
|
|
100
|
+
Array(self.load().budgets.values).sorted { $0.createdAt < $1.createdAt }
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -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 AntigravityStatusFetchStrategy: ProviderFetchStrategy {
|
|
|
49
49
|
let id: String = "antigravity.local"
|
|
50
50
|
let kind: ProviderFetchKind = .localProbe
|
|
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 = AntigravityStatusProbe()
|
|
@@ -309,7 +309,7 @@ public struct AntigravityStatusProbe: Sendable {
|
|
|
309
309
|
|
|
310
310
|
// MARK: - Port detection
|
|
311
311
|
|
|
312
|
-
private struct ProcessInfoResult
|
|
312
|
+
private struct ProcessInfoResult {
|
|
313
313
|
let pid: Int
|
|
314
314
|
let extensionPort: Int?
|
|
315
315
|
let csrfToken: String
|
|
@@ -459,7 +459,7 @@ public struct AntigravityStatusProbe: Sendable {
|
|
|
459
459
|
let body: [String: Any]
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
-
private struct RequestContext
|
|
462
|
+
private struct RequestContext {
|
|
463
463
|
let httpsPort: Int
|
|
464
464
|
let httpPort: Int?
|
|
465
465
|
let csrfToken: String
|
|
@@ -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 AuggieAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
enum AuggieSettingsError: LocalizedError
|
|
74
|
+
enum AuggieSettingsError: LocalizedError {
|
|
75
75
|
case missingToken
|
|
76
76
|
|
|
77
77
|
var errorDescription: String? {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
|
-
struct AuggieUsageMetrics
|
|
3
|
+
struct AuggieUsageMetrics {
|
|
4
4
|
let requestCount: Int?
|
|
5
5
|
let totalTokens: Int?
|
|
6
6
|
let inputTokens: Int?
|
|
@@ -46,7 +46,7 @@ struct AuggieUsageFetcher {
|
|
|
46
46
|
URLQueryItem(name: "window", value: "day"),
|
|
47
47
|
URLQueryItem(name: "days", value: "1"),
|
|
48
48
|
]
|
|
49
|
-
let url = components?.url ?? baseURL
|
|
49
|
+
let url = components?.url ?? self.baseURL
|
|
50
50
|
|
|
51
51
|
var request = URLRequest(url: url)
|
|
52
52
|
request.timeoutInterval = Self.requestTimeout
|
|
@@ -121,7 +121,7 @@ struct AuggieUsageFetcher {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
enum AuggieAPIError: LocalizedError
|
|
124
|
+
enum AuggieAPIError: LocalizedError {
|
|
125
125
|
case invalidResponse
|
|
126
126
|
case decodingError
|
|
127
127
|
case httpError(statusCode: Int, body: String?)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
1
|
import Foundation
|
|
2
|
+
import RunicMacroSupport
|
|
3
3
|
|
|
4
4
|
@ProviderDescriptorRegistration
|
|
5
5
|
@ProviderDescriptorDefinition
|
|
@@ -78,14 +78,14 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
private static func resolveToken(context: ProviderFetchContext) -> ProviderTokenResolution? {
|
|
81
|
-
if let token =
|
|
81
|
+
if let token = cleaned(context.settings?.azure?.apiToken) {
|
|
82
82
|
return ProviderTokenResolution(token: token, source: .keychain)
|
|
83
83
|
}
|
|
84
84
|
return ProviderTokenResolver.azureOpenAIResolution(environment: context.env)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
private static func resolveEndpoint(context: ProviderFetchContext) -> String? {
|
|
88
|
-
if let endpoint =
|
|
88
|
+
if let endpoint = cleaned(context.settings?.azure?.endpoint) {
|
|
89
89
|
return endpoint
|
|
90
90
|
}
|
|
91
91
|
if let endpoint = Self.cleaned(context.env["AZURE_OPENAI_ENDPOINT"]) {
|
|
@@ -98,7 +98,7 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
private static func resolveAPIVersion(context: ProviderFetchContext) -> String {
|
|
101
|
-
if let version =
|
|
101
|
+
if let version = cleaned(context.settings?.azure?.apiVersion) {
|
|
102
102
|
return version
|
|
103
103
|
}
|
|
104
104
|
if let version = Self.cleaned(context.env["AZURE_OPENAI_API_VERSION"]) {
|
|
@@ -108,7 +108,7 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
private static func resolveDeployment(context: ProviderFetchContext) -> String? {
|
|
111
|
-
if let deployment =
|
|
111
|
+
if let deployment = cleaned(context.settings?.azure?.deployment) {
|
|
112
112
|
return deployment
|
|
113
113
|
}
|
|
114
114
|
if let deployment = Self.cleaned(context.env["AZURE_OPENAI_DEPLOYMENT"]) {
|
|
@@ -124,17 +124,17 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
enum AzureSettingsError: LocalizedError
|
|
127
|
+
enum AzureSettingsError: LocalizedError {
|
|
128
128
|
case missingEndpoint
|
|
129
129
|
case missingToken
|
|
130
130
|
|
|
131
131
|
var errorDescription: String? {
|
|
132
132
|
switch self {
|
|
133
133
|
case .missingEndpoint:
|
|
134
|
-
|
|
134
|
+
"Azure OpenAI endpoint not found. Set it in Preferences → Providers → Azure OpenAI or " +
|
|
135
135
|
"export AZURE_OPENAI_ENDPOINT."
|
|
136
136
|
case .missingToken:
|
|
137
|
-
|
|
137
|
+
"Azure OpenAI API key not found. Set it in Preferences → Providers → Azure OpenAI or " +
|
|
138
138
|
"export AZURE_OPENAI_API_KEY."
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
3
|
struct AzureOpenAIDeploymentsResponse: Decodable {
|
|
4
|
-
struct Deployment: Decodable
|
|
4
|
+
struct Deployment: Decodable {
|
|
5
5
|
let id: String?
|
|
6
6
|
let model: String?
|
|
7
7
|
let status: String?
|
|
@@ -77,7 +77,7 @@ struct AzureUsageFetcher {
|
|
|
77
77
|
throw AzureAPIError.invalidResponse
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
guard (200
|
|
80
|
+
guard (200..<300).contains(httpResponse.statusCode) else {
|
|
81
81
|
let body = String(data: data, encoding: .utf8)?
|
|
82
82
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
83
83
|
throw AzureAPIError.httpError(
|
|
@@ -167,7 +167,7 @@ extension AzureOpenAIDeploymentsResponse {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
enum AzureAPIError: LocalizedError
|
|
170
|
+
enum AzureAPIError: LocalizedError {
|
|
171
171
|
case invalidEndpoint(String)
|
|
172
172
|
case invalidResponse
|
|
173
173
|
case httpError(statusCode: Int, body: String?)
|
|
@@ -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 BedrockCLIFetchStrategy: ProviderFetchStrategy {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
private static func resolveRegion(context: ProviderFetchContext) -> String? {
|
|
77
|
-
if let region =
|
|
77
|
+
if let region = cleaned(context.settings?.bedrock?.region) {
|
|
78
78
|
return region
|
|
79
79
|
}
|
|
80
80
|
if let region = Self.cleaned(context.env["AWS_REGION"]) {
|
|
@@ -87,7 +87,7 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
private static func resolveProfile(context: ProviderFetchContext) -> String? {
|
|
90
|
-
if let profile =
|
|
90
|
+
if let profile = cleaned(context.settings?.bedrock?.profile) {
|
|
91
91
|
return profile
|
|
92
92
|
}
|
|
93
93
|
if let profile = Self.cleaned(context.env["AWS_PROFILE"]) {
|
|
@@ -97,7 +97,7 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
private static func resolveModelFilter(context: ProviderFetchContext) -> String? {
|
|
100
|
-
if let model =
|
|
100
|
+
if let model = cleaned(context.settings?.bedrock?.modelID) {
|
|
101
101
|
return model
|
|
102
102
|
}
|
|
103
103
|
if let model = Self.cleaned(context.env["BEDROCK_MODEL_ID"]) {
|
|
@@ -113,13 +113,13 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
enum BedrockSettingsError: LocalizedError
|
|
116
|
+
enum BedrockSettingsError: LocalizedError {
|
|
117
117
|
case missingRegion
|
|
118
118
|
|
|
119
119
|
var errorDescription: String? {
|
|
120
120
|
switch self {
|
|
121
121
|
case .missingRegion:
|
|
122
|
-
|
|
122
|
+
"AWS region not found. Set it in Preferences → Providers → Amazon Bedrock or " +
|
|
123
123
|
"export AWS_REGION."
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -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? {
|