@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,5 +1,6 @@
|
|
|
1
1
|
import AppKit
|
|
2
2
|
import RunicCore
|
|
3
|
+
import Security
|
|
3
4
|
import SwiftUI
|
|
4
5
|
|
|
5
6
|
@MainActor
|
|
@@ -9,7 +10,7 @@ struct IntegrationsPane: View {
|
|
|
9
10
|
|
|
10
11
|
@AppStorage("vaayuAPIServerEnabled") private var vaayuAPIServerEnabled = false
|
|
11
12
|
@AppStorage("vaayuAPIServerPort") private var vaayuAPIServerPort = 3000
|
|
12
|
-
@
|
|
13
|
+
@State private var vaayuAPIKey = ""
|
|
13
14
|
@AppStorage("defaultWebhookURL") private var defaultWebhookURL = ""
|
|
14
15
|
@AppStorage("webhookFormat") private var webhookFormat = "slack"
|
|
15
16
|
@AppStorage("githubIntegrationEnabled") private var githubIntegrationEnabled = false
|
|
@@ -28,15 +29,20 @@ struct IntegrationsPane: View {
|
|
|
28
29
|
var isRunning: Bool
|
|
29
30
|
var port: Int
|
|
30
31
|
|
|
31
|
-
var statusIndicator: String {
|
|
32
|
-
|
|
32
|
+
var statusIndicator: String {
|
|
33
|
+
"●"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var statusColor: Color {
|
|
37
|
+
self.isRunning ? .green : .red
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
var body: some View {
|
|
36
42
|
PreferencesPane {
|
|
37
43
|
SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
|
|
38
44
|
Text("vaayu Integration")
|
|
39
|
-
.font(.caption)
|
|
45
|
+
.font(RunicFont.caption)
|
|
40
46
|
.foregroundStyle(.secondary)
|
|
41
47
|
.textCase(.uppercase)
|
|
42
48
|
|
|
@@ -49,11 +55,11 @@ struct IntegrationsPane: View {
|
|
|
49
55
|
|
|
50
56
|
if self.vaayuAPIServerEnabled {
|
|
51
57
|
Text("Running on port \(self.vaayuAPIServerPort)")
|
|
52
|
-
.font(.footnote)
|
|
58
|
+
.font(RunicFont.footnote)
|
|
53
59
|
.foregroundStyle(.green)
|
|
54
60
|
} else {
|
|
55
61
|
Text("Stopped")
|
|
56
|
-
.font(.footnote)
|
|
62
|
+
.font(RunicFont.footnote)
|
|
57
63
|
.foregroundStyle(.secondary)
|
|
58
64
|
}
|
|
59
65
|
}
|
|
@@ -61,21 +67,21 @@ struct IntegrationsPane: View {
|
|
|
61
67
|
if self.vaayuAPIServerEnabled {
|
|
62
68
|
VStack(alignment: .leading, spacing: 4) {
|
|
63
69
|
Text("Port number")
|
|
64
|
-
.font(.body)
|
|
70
|
+
.font(RunicFont.body)
|
|
65
71
|
|
|
66
72
|
TextField("Port", value: self.$vaayuAPIServerPort, format: .number)
|
|
67
73
|
.textFieldStyle(.roundedBorder)
|
|
68
74
|
.frame(maxWidth: 150)
|
|
69
75
|
|
|
70
76
|
Text("Default: 3000. Restart required after changing port.")
|
|
71
|
-
.font(.footnote)
|
|
77
|
+
.font(RunicFont.footnote)
|
|
72
78
|
.foregroundStyle(.tertiary)
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
VStack(alignment: .leading, spacing: 4) {
|
|
76
82
|
HStack {
|
|
77
83
|
Text("API Key")
|
|
78
|
-
.font(.body)
|
|
84
|
+
.font(RunicFont.body)
|
|
79
85
|
|
|
80
86
|
Spacer()
|
|
81
87
|
|
|
@@ -98,7 +104,7 @@ struct IntegrationsPane: View {
|
|
|
98
104
|
|
|
99
105
|
if !self.vaayuAPIKey.isEmpty {
|
|
100
106
|
Text(self.maskedAPIKey)
|
|
101
|
-
.font(.footnote.monospaced())
|
|
107
|
+
.font(RunicFont.footnote.monospaced())
|
|
102
108
|
.foregroundStyle(.secondary)
|
|
103
109
|
.padding(RunicSpacing.xs)
|
|
104
110
|
.background(Color(nsColor: .controlBackgroundColor))
|
|
@@ -107,14 +113,14 @@ struct IntegrationsPane: View {
|
|
|
107
113
|
|
|
108
114
|
if let generatedKey = self.generatedAPIKey {
|
|
109
115
|
Text("New key generated: \(generatedKey)")
|
|
110
|
-
.font(.footnote)
|
|
116
|
+
.font(RunicFont.footnote)
|
|
111
117
|
.foregroundStyle(.green)
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
Text("Exposes Runic usage data via REST API for external integrations.")
|
|
117
|
-
.font(.footnote)
|
|
123
|
+
.font(RunicFont.footnote)
|
|
118
124
|
.foregroundStyle(.tertiary)
|
|
119
125
|
}
|
|
120
126
|
}
|
|
@@ -123,14 +129,14 @@ struct IntegrationsPane: View {
|
|
|
123
129
|
|
|
124
130
|
SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
|
|
125
131
|
Text("MCP Servers")
|
|
126
|
-
.font(.caption)
|
|
132
|
+
.font(RunicFont.caption)
|
|
127
133
|
.foregroundStyle(.secondary)
|
|
128
134
|
.textCase(.uppercase)
|
|
129
135
|
|
|
130
136
|
VStack(spacing: RunicSpacing.xs) {
|
|
131
137
|
HStack {
|
|
132
138
|
Text("Configured servers")
|
|
133
|
-
.font(.body)
|
|
139
|
+
.font(RunicFont.body)
|
|
134
140
|
Spacer()
|
|
135
141
|
Button {
|
|
136
142
|
self.showingAddServerSheet = true
|
|
@@ -147,10 +153,10 @@ struct IntegrationsPane: View {
|
|
|
147
153
|
.font(.system(size: 32))
|
|
148
154
|
.foregroundStyle(.tertiary)
|
|
149
155
|
Text("No MCP servers yet")
|
|
150
|
-
.font(.body.weight(.semibold))
|
|
156
|
+
.font(RunicFont.body.weight(.semibold))
|
|
151
157
|
.foregroundStyle(.secondary)
|
|
152
158
|
Text("Add a server to manage connection details")
|
|
153
|
-
.font(.footnote)
|
|
159
|
+
.font(RunicFont.footnote)
|
|
154
160
|
.foregroundStyle(.tertiary)
|
|
155
161
|
}
|
|
156
162
|
.frame(maxWidth: .infinity)
|
|
@@ -160,13 +166,13 @@ struct IntegrationsPane: View {
|
|
|
160
166
|
HStack(spacing: RunicSpacing.sm) {
|
|
161
167
|
Text(server.statusIndicator)
|
|
162
168
|
.foregroundStyle(server.statusColor)
|
|
163
|
-
.font(.title3)
|
|
169
|
+
.font(RunicFont.title3)
|
|
164
170
|
|
|
165
171
|
VStack(alignment: .leading, spacing: RunicSpacing.xxs) {
|
|
166
172
|
Text(server.name)
|
|
167
|
-
.font(.body)
|
|
173
|
+
.font(RunicFont.body)
|
|
168
174
|
Text("Port \(server.port)")
|
|
169
|
-
.font(.footnote)
|
|
175
|
+
.font(RunicFont.footnote)
|
|
170
176
|
.foregroundStyle(.secondary)
|
|
171
177
|
}
|
|
172
178
|
|
|
@@ -205,7 +211,7 @@ struct IntegrationsPane: View {
|
|
|
205
211
|
}
|
|
206
212
|
|
|
207
213
|
Text("MCP servers enable Claude Desktop and other tools to access Runic data.")
|
|
208
|
-
.font(.footnote)
|
|
214
|
+
.font(RunicFont.footnote)
|
|
209
215
|
.foregroundStyle(.tertiary)
|
|
210
216
|
}
|
|
211
217
|
|
|
@@ -213,26 +219,26 @@ struct IntegrationsPane: View {
|
|
|
213
219
|
|
|
214
220
|
SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
|
|
215
221
|
Text("Webhooks")
|
|
216
|
-
.font(.caption)
|
|
222
|
+
.font(RunicFont.caption)
|
|
217
223
|
.foregroundStyle(.secondary)
|
|
218
224
|
.textCase(.uppercase)
|
|
219
225
|
|
|
220
226
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
221
227
|
VStack(alignment: .leading, spacing: 4) {
|
|
222
228
|
Text("Default webhook URL")
|
|
223
|
-
.font(.body)
|
|
229
|
+
.font(RunicFont.body)
|
|
224
230
|
|
|
225
231
|
TextField("https://hooks.slack.com/services/...", text: self.$defaultWebhookURL)
|
|
226
232
|
.textFieldStyle(.roundedBorder)
|
|
227
233
|
|
|
228
234
|
Text("Used for all alerts unless overridden in alert rule settings.")
|
|
229
|
-
.font(.footnote)
|
|
235
|
+
.font(RunicFont.footnote)
|
|
230
236
|
.foregroundStyle(.tertiary)
|
|
231
237
|
}
|
|
232
238
|
|
|
233
239
|
VStack(alignment: .leading, spacing: 4) {
|
|
234
240
|
Text("Webhook format")
|
|
235
|
-
.font(.body)
|
|
241
|
+
.font(RunicFont.body)
|
|
236
242
|
|
|
237
243
|
Picker("", selection: self.$webhookFormat) {
|
|
238
244
|
Text("Slack").tag("slack")
|
|
@@ -243,7 +249,7 @@ struct IntegrationsPane: View {
|
|
|
243
249
|
.frame(maxWidth: 300)
|
|
244
250
|
|
|
245
251
|
Text("Formats webhook payloads for different platforms.")
|
|
246
|
-
.font(.footnote)
|
|
252
|
+
.font(RunicFont.footnote)
|
|
247
253
|
.foregroundStyle(.tertiary)
|
|
248
254
|
}
|
|
249
255
|
|
|
@@ -256,7 +262,7 @@ struct IntegrationsPane: View {
|
|
|
256
262
|
|
|
257
263
|
if let result = self.testWebhookResult {
|
|
258
264
|
Text(result)
|
|
259
|
-
.font(.footnote)
|
|
265
|
+
.font(RunicFont.footnote)
|
|
260
266
|
.foregroundStyle(result.contains("Success") ? .green : .red)
|
|
261
267
|
}
|
|
262
268
|
}
|
|
@@ -267,7 +273,7 @@ struct IntegrationsPane: View {
|
|
|
267
273
|
|
|
268
274
|
SettingsSection(contentSpacing: PreferencesLayoutMetrics.sectionSpacing) {
|
|
269
275
|
Text("GitHub Integration")
|
|
270
|
-
.font(.caption)
|
|
276
|
+
.font(RunicFont.caption)
|
|
271
277
|
.foregroundStyle(.secondary)
|
|
272
278
|
.textCase(.uppercase)
|
|
273
279
|
|
|
@@ -280,7 +286,7 @@ struct IntegrationsPane: View {
|
|
|
280
286
|
if self.githubIntegrationEnabled {
|
|
281
287
|
VStack(alignment: .leading, spacing: 4) {
|
|
282
288
|
Text("Repository path")
|
|
283
|
-
.font(.body)
|
|
289
|
+
.font(RunicFont.body)
|
|
284
290
|
|
|
285
291
|
HStack {
|
|
286
292
|
TextField("/path/to/repo", text: self.$githubRepositoryPath)
|
|
@@ -299,7 +305,7 @@ struct IntegrationsPane: View {
|
|
|
299
305
|
Image(systemName: "checkmark.circle.fill")
|
|
300
306
|
.foregroundStyle(.green)
|
|
301
307
|
Text("Valid Git repository")
|
|
302
|
-
.font(.footnote)
|
|
308
|
+
.font(RunicFont.footnote)
|
|
303
309
|
.foregroundStyle(.green)
|
|
304
310
|
}
|
|
305
311
|
} else {
|
|
@@ -307,7 +313,7 @@ struct IntegrationsPane: View {
|
|
|
307
313
|
Image(systemName: "xmark.circle.fill")
|
|
308
314
|
.foregroundStyle(.red)
|
|
309
315
|
Text("Not a valid Git repository")
|
|
310
|
-
.font(.footnote)
|
|
316
|
+
.font(RunicFont.footnote)
|
|
311
317
|
.foregroundStyle(.red)
|
|
312
318
|
}
|
|
313
319
|
}
|
|
@@ -319,6 +325,22 @@ struct IntegrationsPane: View {
|
|
|
319
325
|
}
|
|
320
326
|
.onAppear {
|
|
321
327
|
self.loadMCPServers()
|
|
328
|
+
self.vaayuAPIKey = VaayuKeychainHelper.load() ?? ""
|
|
329
|
+
// Migrate any existing value out of UserDefaults
|
|
330
|
+
let defaults = UserDefaults.standard
|
|
331
|
+
if let legacyKey = defaults.string(forKey: "vaayuAPIKey"), !legacyKey.isEmpty {
|
|
332
|
+
if VaayuKeychainHelper.save(legacyKey) {
|
|
333
|
+
self.vaayuAPIKey = legacyKey
|
|
334
|
+
defaults.removeObject(forKey: "vaayuAPIKey")
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
.onChange(of: self.vaayuAPIKey) { _, newValue in
|
|
339
|
+
if newValue.isEmpty {
|
|
340
|
+
VaayuKeychainHelper.delete()
|
|
341
|
+
} else {
|
|
342
|
+
VaayuKeychainHelper.save(newValue)
|
|
343
|
+
}
|
|
322
344
|
}
|
|
323
345
|
.sheet(isPresented: self.$showingAddServerSheet) {
|
|
324
346
|
AddMCPServerSheet(
|
|
@@ -462,11 +484,11 @@ private struct AddMCPServerSheet: View {
|
|
|
462
484
|
var body: some View {
|
|
463
485
|
VStack(alignment: .leading, spacing: RunicSpacing.lg) {
|
|
464
486
|
Text("Add MCP Server")
|
|
465
|
-
.font(.title2.weight(.semibold))
|
|
487
|
+
.font(RunicFont.title2.weight(.semibold))
|
|
466
488
|
|
|
467
489
|
VStack(alignment: .leading, spacing: RunicSpacing.xs) {
|
|
468
490
|
Text("Name")
|
|
469
|
-
.font(.subheadline.weight(.medium))
|
|
491
|
+
.font(RunicFont.subheadline.weight(.medium))
|
|
470
492
|
TextField("Local MCP Server", text: self.$name)
|
|
471
493
|
.textFieldStyle(.roundedBorder)
|
|
472
494
|
.focused(self.$isNameFocused)
|
|
@@ -474,7 +496,7 @@ private struct AddMCPServerSheet: View {
|
|
|
474
496
|
|
|
475
497
|
VStack(alignment: .leading, spacing: RunicSpacing.xs) {
|
|
476
498
|
Text("Port")
|
|
477
|
-
.font(.subheadline.weight(.medium))
|
|
499
|
+
.font(RunicFont.subheadline.weight(.medium))
|
|
478
500
|
TextField("", value: self.$port, format: .number)
|
|
479
501
|
.textFieldStyle(.roundedBorder)
|
|
480
502
|
.frame(width: 140)
|
|
@@ -495,3 +517,56 @@ private struct AddMCPServerSheet: View {
|
|
|
495
517
|
.onAppear { self.isNameFocused = true }
|
|
496
518
|
}
|
|
497
519
|
}
|
|
520
|
+
|
|
521
|
+
// MARK: - Vaayu API Key Keychain Helper
|
|
522
|
+
|
|
523
|
+
private enum VaayuKeychainHelper {
|
|
524
|
+
private static let service = "com.sriinnu.athena.Runic"
|
|
525
|
+
private static let account = "vaayu-api-key"
|
|
526
|
+
|
|
527
|
+
static func load() -> String? {
|
|
528
|
+
var result: CFTypeRef?
|
|
529
|
+
let query: [String: Any] = [
|
|
530
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
531
|
+
kSecAttrService as String: self.service,
|
|
532
|
+
kSecAttrAccount as String: self.account,
|
|
533
|
+
kSecMatchLimit as String: kSecMatchLimitOne,
|
|
534
|
+
kSecReturnData as String: true,
|
|
535
|
+
kSecUseAuthenticationUI as String: kSecUseAuthenticationUIFail,
|
|
536
|
+
]
|
|
537
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
538
|
+
guard status == errSecSuccess,
|
|
539
|
+
let data = result as? Data,
|
|
540
|
+
let token = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
541
|
+
!token.isEmpty
|
|
542
|
+
else {
|
|
543
|
+
return nil
|
|
544
|
+
}
|
|
545
|
+
return token
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@discardableResult
|
|
549
|
+
static func save(_ value: String) -> Bool {
|
|
550
|
+
self.delete()
|
|
551
|
+
let data = Data(value.utf8)
|
|
552
|
+
let query: [String: Any] = [
|
|
553
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
554
|
+
kSecAttrService as String: self.service,
|
|
555
|
+
kSecAttrAccount as String: self.account,
|
|
556
|
+
kSecValueData as String: data,
|
|
557
|
+
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
|
|
558
|
+
]
|
|
559
|
+
return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
@discardableResult
|
|
563
|
+
static func delete() -> Bool {
|
|
564
|
+
let query: [String: Any] = [
|
|
565
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
566
|
+
kSecAttrService as String: self.service,
|
|
567
|
+
kSecAttrAccount as String: self.account,
|
|
568
|
+
]
|
|
569
|
+
let status = SecItemDelete(query as CFDictionary)
|
|
570
|
+
return status == errSecSuccess || status == errSecItemNotFound
|
|
571
|
+
}
|
|
572
|
+
}
|
|
@@ -23,17 +23,19 @@ struct PerformancePane: View {
|
|
|
23
23
|
@State private var appeared = false
|
|
24
24
|
|
|
25
25
|
enum QualityRatingFrequency: String, CaseIterable, Identifiable {
|
|
26
|
-
case every
|
|
26
|
+
case every
|
|
27
27
|
case over1000 = "over_1000"
|
|
28
28
|
case over5000 = "over_5000"
|
|
29
29
|
|
|
30
|
-
var id: String {
|
|
30
|
+
var id: String {
|
|
31
|
+
self.rawValue
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
var label: String {
|
|
33
35
|
switch self {
|
|
34
|
-
case .every:
|
|
35
|
-
case .over1000:
|
|
36
|
-
case .over5000:
|
|
36
|
+
case .every: "After every response"
|
|
37
|
+
case .over1000: "Only >1000 tokens"
|
|
38
|
+
case .over5000: "Only >5000 tokens"
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
}
|
|
@@ -47,8 +49,9 @@ struct PerformancePane: View {
|
|
|
47
49
|
binding: self.$performanceTrackingEnabled)
|
|
48
50
|
|
|
49
51
|
if !self.performanceTrackingEnabled {
|
|
50
|
-
Text(
|
|
51
|
-
.
|
|
52
|
+
Text(
|
|
53
|
+
"Performance tracking is disabled. Historical data is preserved but new metrics won't be collected.")
|
|
54
|
+
.font(RunicFont.footnote)
|
|
52
55
|
.foregroundStyle(.orange)
|
|
53
56
|
.padding(.vertical, RunicSpacing.xs)
|
|
54
57
|
}
|
|
@@ -59,7 +62,7 @@ struct PerformancePane: View {
|
|
|
59
62
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
60
63
|
VStack(alignment: .leading, spacing: 4) {
|
|
61
64
|
Text("Raw metrics retention")
|
|
62
|
-
.font(.body)
|
|
65
|
+
.font(RunicFont.body)
|
|
63
66
|
|
|
64
67
|
Picker("", selection: self.$rawMetricsRetentionDays) {
|
|
65
68
|
Text("30 days").tag(30)
|
|
@@ -70,13 +73,13 @@ struct PerformancePane: View {
|
|
|
70
73
|
.frame(maxWidth: 300)
|
|
71
74
|
|
|
72
75
|
Text("How long to keep detailed latency and error records.")
|
|
73
|
-
.font(.footnote)
|
|
76
|
+
.font(RunicFont.footnote)
|
|
74
77
|
.foregroundStyle(.tertiary)
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
VStack(alignment: .leading, spacing: 4) {
|
|
78
81
|
Text("Aggregated stats retention")
|
|
79
|
-
.font(.body)
|
|
82
|
+
.font(RunicFont.body)
|
|
80
83
|
|
|
81
84
|
Picker("", selection: self.$aggregatedStatsRetentionYears) {
|
|
82
85
|
Text("1 year").tag(1)
|
|
@@ -87,7 +90,7 @@ struct PerformancePane: View {
|
|
|
87
90
|
.frame(maxWidth: 300)
|
|
88
91
|
|
|
89
92
|
Text("How long to keep daily performance summaries.")
|
|
90
|
-
.font(.footnote)
|
|
93
|
+
.font(RunicFont.footnote)
|
|
91
94
|
.foregroundStyle(.tertiary)
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -104,7 +107,7 @@ struct PerformancePane: View {
|
|
|
104
107
|
if self.qualityRatingPromptsEnabled {
|
|
105
108
|
VStack(alignment: .leading, spacing: 4) {
|
|
106
109
|
Text("Prompt frequency")
|
|
107
|
-
.font(.body)
|
|
110
|
+
.font(RunicFont.body)
|
|
108
111
|
|
|
109
112
|
Picker("", selection: self.$qualityRatingFrequency) {
|
|
110
113
|
ForEach(QualityRatingFrequency.allCases) { freq in
|
|
@@ -115,7 +118,7 @@ struct PerformancePane: View {
|
|
|
115
118
|
.frame(maxWidth: 400)
|
|
116
119
|
|
|
117
120
|
Text("When to show rating prompts based on response size.")
|
|
118
|
-
.font(.footnote)
|
|
121
|
+
.font(RunicFont.footnote)
|
|
119
122
|
.foregroundStyle(.tertiary)
|
|
120
123
|
}
|
|
121
124
|
|
|
@@ -135,9 +138,9 @@ struct PerformancePane: View {
|
|
|
135
138
|
VStack(alignment: .leading, spacing: RunicSpacing.sm) {
|
|
136
139
|
HStack {
|
|
137
140
|
Text("Current database size:")
|
|
138
|
-
.font(.body)
|
|
141
|
+
.font(RunicFont.body)
|
|
139
142
|
Text(self.databaseSize)
|
|
140
|
-
.font(.body.weight(.semibold))
|
|
143
|
+
.font(RunicFont.body.weight(.semibold))
|
|
141
144
|
.foregroundStyle(.secondary)
|
|
142
145
|
}
|
|
143
146
|
|
|
@@ -157,13 +160,13 @@ struct PerformancePane: View {
|
|
|
157
160
|
|
|
158
161
|
if let status = self.vacuumStatus {
|
|
159
162
|
Text(status)
|
|
160
|
-
.font(.footnote)
|
|
163
|
+
.font(RunicFont.footnote)
|
|
161
164
|
.foregroundStyle(status.contains("Success") ? .green : .red)
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
167
|
|
|
165
168
|
Text("Optimizes database file by reclaiming unused space.")
|
|
166
|
-
.font(.footnote)
|
|
169
|
+
.font(RunicFont.footnote)
|
|
167
170
|
.foregroundStyle(.tertiary)
|
|
168
171
|
|
|
169
172
|
Divider()
|
|
@@ -185,13 +188,13 @@ struct PerformancePane: View {
|
|
|
185
188
|
|
|
186
189
|
if let status = self.clearDataStatus {
|
|
187
190
|
Text(status)
|
|
188
|
-
.font(.footnote)
|
|
191
|
+
.font(RunicFont.footnote)
|
|
189
192
|
.foregroundStyle(status.contains("Success") ? .green : .red)
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
Text("Removes metrics older than configured retention periods.")
|
|
194
|
-
.font(.footnote)
|
|
197
|
+
.font(RunicFont.footnote)
|
|
195
198
|
.foregroundStyle(.tertiary)
|
|
196
199
|
}
|
|
197
200
|
}
|
|
@@ -204,8 +207,9 @@ struct PerformancePane: View {
|
|
|
204
207
|
binding: self.$anonymousUsageStatsEnabled)
|
|
205
208
|
|
|
206
209
|
if self.anonymousUsageStatsEnabled {
|
|
207
|
-
Text(
|
|
208
|
-
.
|
|
210
|
+
Text(
|
|
211
|
+
"Only aggregate statistics are shared. Request IDs, prompts, and responses are never included.")
|
|
212
|
+
.font(RunicFont.footnote)
|
|
209
213
|
.foregroundStyle(.tertiary)
|
|
210
214
|
.padding(.vertical, RunicSpacing.xs)
|
|
211
215
|
}
|
|
@@ -223,8 +227,7 @@ struct PerformancePane: View {
|
|
|
223
227
|
Task {
|
|
224
228
|
let appSupport = FileManager.default.urls(
|
|
225
229
|
for: .applicationSupportDirectory,
|
|
226
|
-
in: .userDomainMask
|
|
227
|
-
).first!
|
|
230
|
+
in: .userDomainMask).first!
|
|
228
231
|
let dbPath = appSupport.appendingPathComponent("Runic/performance.db")
|
|
229
232
|
|
|
230
233
|
if let attributes = try? FileManager.default.attributesOfItem(atPath: dbPath.path),
|