@sriinnu/runic 1.2.0 → 2.11.1
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 +267 -0
- package/LICENSE +373 -21
- package/README.md +98 -84
- package/THIRD-PARTY-NOTICES.md +651 -0
- package/package.json +14 -4
- package/.swiftformat +0 -50
- package/.swiftlint.yml +0 -160
- package/CONTRIBUTING.md +0 -456
- package/Icon.icns +0 -0
- package/Icon.icon/Assets/runic.png +0 -0
- package/Icon.icon/icon.json +0 -36
- package/Package.resolved +0 -51
- package/Package.swift +0 -136
- package/Packages/Helix/LICENSE +0 -21
- package/Packages/Helix/Package.swift +0 -29
- package/Packages/Helix/README.md +0 -976
- package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
- package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
- package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
- package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
- package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
- package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
- package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
- package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
- package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
- package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
- package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
- package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
- package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
- package/Packages/Helix/Sources/Helix/Program.swift +0 -204
- package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
- package/Packages/Silo/LICENSE +0 -21
- package/Packages/Silo/Package.swift +0 -34
- package/Packages/Silo/README.md +0 -466
- package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
- package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
- package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
- package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
- package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
- package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
- package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
- package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
- package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
- package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
- package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
- package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
- package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
- package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
- package/RuniciOS/App/RuniciOSApp.swift +0 -105
- package/RuniciOS/Core/iOSUsageStore.swift +0 -192
- package/RuniciOS/Views/ProviderDetailView.swift +0 -312
- package/RuniciOS/Views/ProviderListView.swift +0 -228
- package/RuniciOS/Widgets/LargeWidget.swift +0 -367
- package/RuniciOS/Widgets/LiveActivity.swift +0 -342
- package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
- package/RuniciOS/Widgets/MediumWidget.swift +0 -370
- package/RuniciOS/Widgets/RunicWidget.swift +0 -274
- package/RuniciOS/Widgets/SmallWidget.swift +0 -281
- package/RuniciOS/Widgets/WidgetModels.swift +0 -285
- package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
- package/Scripts/CREATION-SUMMARY.md +0 -367
- package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
- package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
- package/Scripts/INDEX.md +0 -365
- package/Scripts/QUICK-REFERENCE.md +0 -164
- package/Scripts/README-SCRIPTS.md +0 -926
- package/Scripts/build-android.sh +0 -526
- package/Scripts/build-ios.sh +0 -529
- package/Scripts/build-macos.sh +0 -603
- package/Scripts/build-windows.sh +0 -504
- package/Scripts/build_icon.sh +0 -49
- package/Scripts/changelog-to-html.sh +0 -89
- package/Scripts/check-release-assets.sh +0 -10
- package/Scripts/ci-config.yml +0 -474
- package/Scripts/compile_and_run.sh +0 -219
- package/Scripts/compile_and_run_adhoc.sh +0 -4
- package/Scripts/docs-list.mjs +0 -119
- package/Scripts/make_appcast.sh +0 -58
- package/Scripts/package_app.sh +0 -409
- package/Scripts/release-lib.sh +0 -214
- package/Scripts/release.sh +0 -76
- package/Scripts/setup-all.sh +0 -387
- package/Scripts/setup-ios.sh +0 -404
- package/Scripts/setup-react-native.sh +0 -523
- package/Scripts/sign-and-notarize.sh +0 -122
- package/Scripts/test_live_update.sh +0 -38
- package/Scripts/validate_changelog.sh +0 -23
- package/Scripts/verify_appcast.sh +0 -92
- package/Sources/Runic/About.swift +0 -93
- package/Sources/Runic/AppNotifications.swift +0 -115
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -666
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -518
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -810
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -87
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1407
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -337
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -395
- package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
- package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -80
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1216
- package/Sources/Runic/Core/Rendering/IconView.swift +0 -120
- package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
- package/Sources/Runic/Core/RunicAnimation.swift +0 -53
- package/Sources/Runic/Core/RunicColors.swift +0 -73
- package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
- package/Sources/Runic/Core/RunicSpacing.swift +0 -68
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1711
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -20
- package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
- package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3605
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -119
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -118
- package/Sources/Runic/Core/UsageExporter.swift +0 -193
- package/Sources/Runic/Date+RelativeDescription.swift +0 -21
- package/Sources/Runic/HiddenWindowView.swift +0 -32
- package/Sources/Runic/InstallOrigin.swift +0 -9
- package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
- package/Sources/Runic/MouseLocationReader.swift +0 -65
- package/Sources/Runic/Notifications+Runic.swift +0 -6
- package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
- package/Sources/Runic/ProviderRegistry.swift +0 -100
- package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
- package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
- package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
- package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
- package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -48
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
- package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
- package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
- package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
- package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
- package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
- package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
- package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
- package/Sources/Runic/Resources/provider-context-windows.json +0 -20
- package/Sources/Runic/RunicApp.swift +0 -357
- package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
- package/Sources/Runic/UsagePaceText.swift +0 -52
- package/Sources/Runic/UsageProgressBar.swift +0 -224
- package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
- package/Sources/Runic/Utilities/LoadingPattern.swift +0 -60
- package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -256
- package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -155
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -64
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -268
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
- package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1900
- package/Sources/Runic/Views/Menu/MenuContent.swift +0 -150
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -369
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -42
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -235
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -278
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -209
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -88
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -201
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -232
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -364
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -190
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -618
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -305
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -422
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -641
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -322
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -231
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -567
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -288
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3435
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -25
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -59
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -206
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -371
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
- package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -761
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
- package/Sources/RunicCLI/CLIEntry.swift +0 -942
- package/Sources/RunicCLI/CLIRenderer.swift +0 -87
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -362
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -439
- package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -429
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -168
- package/Sources/RunicClaudeWatchdog/main.swift +0 -122
- package/Sources/RunicClaudeWebProbe/main.swift +0 -62
- package/Sources/RunicCore/AlertRuleStore.swift +0 -187
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
- package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
- package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
- package/Sources/RunicCore/CostUsageModels.swift +0 -479
- package/Sources/RunicCore/CreditsModels.swift +0 -50
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -269
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -633
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -500
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
- package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
- package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
- package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -371
- package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
- package/Sources/RunicCore/PathEnvironment.swift +0 -482
- package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -409
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
- package/Sources/RunicCore/ProjectBudgetStore.swift +0 -99
- package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -66
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -207
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -28
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -243
- package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -24
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -181
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
- package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -114
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -291
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -386
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -65
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -836
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -92
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -203
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
- package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
- package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
- package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
- package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
- package/Sources/RunicCore/Providers/Providers.swift +0 -184
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -315
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -72
- package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -718
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -385
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -310
- package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -288
- package/Sources/RunicCore/Sync/SyncRecord.swift +0 -405
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -294
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -775
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -333
- package/Sources/RunicCore/Team/TeamModels.swift +0 -583
- package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
- package/Sources/RunicCore/Team/TeamStore.swift +0 -792
- package/Sources/RunicCore/TextParsing.swift +0 -55
- package/Sources/RunicCore/UsageFetcher.swift +0 -694
- package/Sources/RunicCore/UsageFormatter.swift +0 -410
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -427
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -494
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -158
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
- package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -660
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
- package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -245
- package/Sources/RunicCore/UsagePace.swift +0 -81
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
- package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
- package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
- package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
- package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
- package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
- package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
- package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
- package/Tests/RunicTests/AppDelegateTests.swift +0 -43
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -22
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -70
- package/Tests/RunicTests/CLICostTests.swift +0 -65
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -22
- package/Tests/RunicTests/CLISnapshotTests.swift +0 -72
- package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -58
- package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -164
- package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -30
- package/Tests/RunicTests/ClaudeUsageTests.swift +0 -370
- package/Tests/RunicTests/CodexOAuthTests.swift +0 -122
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -82
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -75
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -75
- package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -394
- package/Tests/RunicTests/CostUsagePricingTests.swift +0 -50
- package/Tests/RunicTests/CostUsageScannerTests.swift +0 -700
- package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -323
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -69
- package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -28
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -233
- package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -73
- package/Tests/RunicTests/InstallOriginTests.swift +0 -18
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
- package/Tests/RunicTests/LiveAccountTests.swift +0 -24
- package/Tests/RunicTests/LoadingPatternTests.swift +0 -35
- package/Tests/RunicTests/MenuCardModelTests.swift +0 -545
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -68
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -8
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -112
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -17
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -75
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -27
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -134
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -53
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -92
- package/Tests/RunicTests/PathBuilderTests.swift +0 -237
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -160
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -38
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -16
- package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -202
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -51
- package/Tests/RunicTests/RunicTests.swift +0 -120
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -36
- package/Tests/RunicTests/SettingsStoreTests.swift +0 -161
- package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -49
- package/Tests/RunicTests/StatusMenuTests.swift +0 -434
- package/Tests/RunicTests/StatusProbeTests.swift +0 -377
- package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -20
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -65
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -148
- package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -159
- package/Tests/RunicTests/TextParsingTests.swift +0 -12
- package/Tests/RunicTests/UsageFormatterTests.swift +0 -112
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -172
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -213
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -83
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -170
- package/Tests/RunicTests/UsagePaceTests.swift +0 -77
- package/Tests/RunicTests/UsagePaceTextTests.swift +0 -109
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -91
- package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -45
- package/Tests/RunicTests/ZaiProviderTests.swift +0 -61
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -6
- package/TestsLinux/PlatformGatingTests.swift +0 -46
- package/appcast.xml +0 -144
- package/assets/logo-concept-stack.svg +0 -24
- package/assets/logo-minimal-bars.svg +0 -15
- package/assets/logo.svg +0 -20
- package/assets/logo2.svg +0 -43
- package/bin/docs-list +0 -5
- package/build-and-run.sh +0 -369
- package/docs/.nojekyll +0 -1
- package/docs/CNAME +0 -1
- package/docs/antigravity.md +0 -75
- package/docs/architecture.md +0 -32
- package/docs/claude.md +0 -93
- package/docs/cli-commands/integration_guide.md +0 -371
- package/docs/cli-commands/quick_reference.md +0 -362
- package/docs/cli-commands/readme.md +0 -361
- package/docs/cli.md +0 -156
- package/docs/cloudkit_sync_guide.md +0 -495
- package/docs/codebase_review.md +0 -288
- package/docs/codex-oauth.md +0 -635
- package/docs/codex.md +0 -104
- package/docs/codexbar.png +0 -0
- package/docs/copilot.md +0 -44
- package/docs/cursor.md +0 -46
- package/docs/design/designcomparisonchart.md +0 -510
- package/docs/design/implementationguide.md +0 -792
- package/docs/design/menubaricondesignconcepts.md +0 -796
- package/docs/design/mockups/hybridiconconcept.svg +0 -62
- package/docs/design/mockups/infinityiconconcept.svg +0 -41
- package/docs/design/mockups/infinityiconstates.svg +0 -129
- package/docs/design/mockups/wavelogorefined.svg +0 -48
- package/docs/design/quick_summary.md +0 -426
- package/docs/design/readme.md +0 -478
- package/docs/design/sizespecifications.md +0 -575
- package/docs/design-system.md +0 -836
- package/docs/factory.md +0 -103
- package/docs/gemini.md +0 -73
- package/docs/icon.md +0 -37
- package/docs/icon.png +0 -0
- package/docs/index.html +0 -175
- package/docs/keychain_fix.md +0 -274
- package/docs/minimax.md +0 -71
- package/docs/packaging.md +0 -25
- package/docs/progress-bar-improvements.md +0 -240
- package/docs/progress-bar-quick-reference.md +0 -283
- package/docs/progress-bar-technical-details.md +0 -432
- package/docs/progress-bar-visual-test.swift +0 -375
- package/docs/provider.md +0 -202
- package/docs/providers.md +0 -86
- package/docs/refactor/macros.md +0 -35
- package/docs/refresh-loop.md +0 -22
- package/docs/releasing-homebrew.md +0 -35
- package/docs/releasing.md +0 -121
- package/docs/research/concrete_todo_2026-02-22.md +0 -47
- package/docs/research/core-feature-research.md +0 -248
- package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
- package/docs/research/parity_matrix.md +0 -49
- package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
- package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
- package/docs/research/release_gate_checklist.md +0 -57
- package/docs/site.css +0 -505
- package/docs/sparkle.md +0 -28
- package/docs/status.md +0 -28
- package/docs/sync/implementation.md +0 -393
- package/docs/sync/readme.md +0 -317
- package/docs/sync_files_summary.md +0 -391
- package/docs/sync_quick_reference.md +0 -225
- package/docs/todo.md +0 -11
- package/docs/ui.md +0 -29
- package/docs/web-integration.md +0 -9
- package/docs/widgets.md +0 -85
- package/docs/workflows/error-handling-improvements.md +0 -434
- package/docs/workflows/folder_structure.md +0 -299
- package/docs/workflows/loading_states.md +0 -446
- package/docs/workflows/loading_states_examples.md +0 -565
- package/docs/workflows/loading_states_flow.md +0 -430
- package/docs/workflows/performance_optimizations.md +0 -457
- package/docs/workflows/phase1-cli-testing-report.md +0 -313
- package/docs/workflows/quick_reference.md +0 -228
- package/docs/workflows/readme.md +0 -59
- package/docs/workflows/recent_improvements.md +0 -275
- package/docs/workflows/sync_enablement_summary.md +0 -400
- package/docs/zai.md +0 -43
- package/runic.png +0 -0
- package/version.env +0 -2
|
@@ -1,633 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
#if canImport(Security)
|
|
3
|
-
import Security
|
|
4
|
-
#endif
|
|
5
|
-
#if canImport(LocalAuthentication)
|
|
6
|
-
import LocalAuthentication
|
|
7
|
-
#endif
|
|
8
|
-
|
|
9
|
-
/// Generic fetcher for custom API provider usage and balance data
|
|
10
|
-
public actor GenericProviderFetcher {
|
|
11
|
-
private let config: CustomProviderConfig
|
|
12
|
-
private let keychainService = "com.sriinnu.athena.Runic"
|
|
13
|
-
private let log = RunicLog.logger("generic-provider-fetcher")
|
|
14
|
-
|
|
15
|
-
public init(config: CustomProviderConfig) {
|
|
16
|
-
self.config = config
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// MARK: - Public Fetch Methods
|
|
20
|
-
|
|
21
|
-
/// Fetch usage data from the custom provider
|
|
22
|
-
public func fetchUsage() async throws -> UsageData {
|
|
23
|
-
guard let endpoint = config.endpoints.usage else {
|
|
24
|
-
throw FetchError.noUsageEndpoint
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 1. Build URL with variable substitution
|
|
28
|
-
let url = try buildURL(from: endpoint.url, with: endpoint.queryParams)
|
|
29
|
-
|
|
30
|
-
// 2. Create request with auth headers
|
|
31
|
-
var request = URLRequest(url: url)
|
|
32
|
-
request.httpMethod = endpoint.method.rawValue
|
|
33
|
-
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
34
|
-
|
|
35
|
-
// Add auth headers
|
|
36
|
-
try await addAuthHeaders(to: &request)
|
|
37
|
-
|
|
38
|
-
// 3. Make request
|
|
39
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
40
|
-
|
|
41
|
-
guard let httpResponse = response as? HTTPURLResponse else {
|
|
42
|
-
throw FetchError.invalidResponse
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
guard (200...299).contains(httpResponse.statusCode) else {
|
|
46
|
-
throw FetchError.httpError(httpResponse.statusCode)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 4. Parse JSON response
|
|
50
|
-
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
51
|
-
throw FetchError.invalidJSON
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 5. Extract fields using mapping
|
|
55
|
-
return try extractUsageData(from: json, mapping: endpoint.mapping)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/// Fetch balance data from the custom provider
|
|
59
|
-
public func fetchBalance() async throws -> BalanceData {
|
|
60
|
-
guard let endpoint = config.endpoints.balance else {
|
|
61
|
-
throw FetchError.noBalanceEndpoint
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 1. Build URL with variable substitution
|
|
65
|
-
let url = try buildURL(from: endpoint.url, with: nil)
|
|
66
|
-
|
|
67
|
-
// 2. Create request with auth headers
|
|
68
|
-
var request = URLRequest(url: url)
|
|
69
|
-
request.httpMethod = endpoint.method.rawValue
|
|
70
|
-
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
71
|
-
|
|
72
|
-
// Add auth headers
|
|
73
|
-
try await addAuthHeaders(to: &request)
|
|
74
|
-
|
|
75
|
-
// 3. Make request
|
|
76
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
77
|
-
|
|
78
|
-
guard let httpResponse = response as? HTTPURLResponse else {
|
|
79
|
-
throw FetchError.invalidResponse
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
guard (200...299).contains(httpResponse.statusCode) else {
|
|
83
|
-
throw FetchError.httpError(httpResponse.statusCode)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 4. Parse JSON response
|
|
87
|
-
guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
88
|
-
throw FetchError.invalidJSON
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 5. Extract fields using mapping
|
|
92
|
-
return try extractBalanceData(from: json, mapping: endpoint.mapping)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/// Convert usage data to Runic's UsageSnapshot format
|
|
96
|
-
public func toUsageSnapshot(_ data: UsageData) -> UsageSnapshot {
|
|
97
|
-
let usedPercent: Double
|
|
98
|
-
if let quota = data.quota, let used = data.used, quota > 0 {
|
|
99
|
-
usedPercent = (used / quota) * 100.0
|
|
100
|
-
} else if let remaining = data.remaining, let quota = data.quota, quota > 0 {
|
|
101
|
-
usedPercent = ((quota - remaining) / quota) * 100.0
|
|
102
|
-
} else {
|
|
103
|
-
usedPercent = 0
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let primary = RateWindow(
|
|
107
|
-
usedPercent: max(0, min(100, usedPercent)),
|
|
108
|
-
windowMinutes: nil,
|
|
109
|
-
resetsAt: data.resetDate,
|
|
110
|
-
resetDescription: data.resetDate.map { resetDescription(from: $0) }
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
return UsageSnapshot(
|
|
114
|
-
primary: primary,
|
|
115
|
-
secondary: nil,
|
|
116
|
-
tertiary: nil,
|
|
117
|
-
updatedAt: Date(),
|
|
118
|
-
identity: nil
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// MARK: - URL Building
|
|
123
|
-
|
|
124
|
-
/// Build URL with variable substitution and query parameters
|
|
125
|
-
private func buildURL(from template: String, with params: [String: String]?) throws -> URL {
|
|
126
|
-
var urlString = template
|
|
127
|
-
|
|
128
|
-
// Replace date variables
|
|
129
|
-
urlString = try substituteDateVariables(in: urlString)
|
|
130
|
-
|
|
131
|
-
// Build URL components
|
|
132
|
-
guard var components = URLComponents(string: urlString) else {
|
|
133
|
-
throw FetchError.invalidURL(urlString)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Add query parameters
|
|
137
|
-
if let params = params, !params.isEmpty {
|
|
138
|
-
var queryItems = components.queryItems ?? []
|
|
139
|
-
queryItems.append(contentsOf: params.map { URLQueryItem(name: $0.key, value: $0.value) })
|
|
140
|
-
components.queryItems = queryItems
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
guard let url = components.url else {
|
|
144
|
-
throw FetchError.invalidURL(urlString)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
guard url.scheme?.lowercased() == "https" else {
|
|
148
|
-
throw FetchError.insecureURL(urlString)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return url
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/// Replace date variables in URL template
|
|
155
|
-
private func substituteDateVariables(in template: String) throws -> String {
|
|
156
|
-
var result = template
|
|
157
|
-
let now = Date()
|
|
158
|
-
|
|
159
|
-
// Date formatters
|
|
160
|
-
let iso8601Formatter = ISO8601DateFormatter()
|
|
161
|
-
let dateFormatter = DateFormatter()
|
|
162
|
-
dateFormatter.dateFormat = "yyyy-MM-dd"
|
|
163
|
-
|
|
164
|
-
// {{date}} - Today's date in ISO8601 format
|
|
165
|
-
if result.contains("{{date}}") {
|
|
166
|
-
result = result.replacingOccurrences(of: "{{date}}", with: iso8601Formatter.string(from: now))
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// {{date:yyyy-MM-dd}} - Custom date format
|
|
170
|
-
if let customMatch = result.range(of: #"\{\{date:([^}]+)\}\}"#, options: .regularExpression) {
|
|
171
|
-
let pattern = String(result[customMatch])
|
|
172
|
-
if let formatRange = pattern.range(of: "(?<=:)[^}]+", options: .regularExpression) {
|
|
173
|
-
let format = String(pattern[formatRange])
|
|
174
|
-
dateFormatter.dateFormat = format
|
|
175
|
-
result = result.replacingOccurrences(of: pattern, with: dateFormatter.string(from: now))
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// {{start}} and {{end}} - Date range (default: current month)
|
|
180
|
-
if result.contains("{{start}}") || result.contains("{{end}}") {
|
|
181
|
-
let calendar = Calendar.current
|
|
182
|
-
let components = calendar.dateComponents([.year, .month], from: now)
|
|
183
|
-
guard let startOfMonth = calendar.date(from: components) else {
|
|
184
|
-
throw FetchError.invalidDateRange
|
|
185
|
-
}
|
|
186
|
-
let endOfMonth = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) ?? now
|
|
187
|
-
|
|
188
|
-
result = result.replacingOccurrences(of: "{{start}}", with: dateFormatter.string(from: startOfMonth))
|
|
189
|
-
result = result.replacingOccurrences(of: "{{end}}", with: dateFormatter.string(from: endOfMonth))
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// {{timestamp}} - Unix timestamp
|
|
193
|
-
if result.contains("{{timestamp}}") {
|
|
194
|
-
result = result.replacingOccurrences(of: "{{timestamp}}", with: "\(Int(now.timeIntervalSince1970))")
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return result
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// MARK: - Authentication
|
|
201
|
-
|
|
202
|
-
/// Add authentication headers to the request
|
|
203
|
-
private func addAuthHeaders(to request: inout URLRequest) async throws {
|
|
204
|
-
// Load token from keychain
|
|
205
|
-
let token = try await loadToken()
|
|
206
|
-
|
|
207
|
-
// Add auth header based on type
|
|
208
|
-
switch config.auth.type {
|
|
209
|
-
case .apiKey:
|
|
210
|
-
let value = (config.auth.headerPrefix ?? "") + token
|
|
211
|
-
request.setValue(value, forHTTPHeaderField: config.auth.headerName)
|
|
212
|
-
|
|
213
|
-
case .bearer:
|
|
214
|
-
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
215
|
-
|
|
216
|
-
case .basic:
|
|
217
|
-
let encoded = Data("\(token):".utf8).base64EncodedString()
|
|
218
|
-
request.setValue("Basic \(encoded)", forHTTPHeaderField: "Authorization")
|
|
219
|
-
|
|
220
|
-
case .oauth:
|
|
221
|
-
// OAuth typically uses Bearer token
|
|
222
|
-
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
223
|
-
|
|
224
|
-
case .custom:
|
|
225
|
-
// Custom auth uses the exact header name and prefix from config
|
|
226
|
-
let value = (config.auth.headerPrefix ?? "") + token
|
|
227
|
-
request.setValue(value, forHTTPHeaderField: config.auth.headerName)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Add additional headers
|
|
231
|
-
config.auth.additionalHeaders?.forEach { key, value in
|
|
232
|
-
request.setValue(value, forHTTPHeaderField: key)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/// Load token from keychain or environment
|
|
237
|
-
private func loadToken() async throws -> String {
|
|
238
|
-
// Try keychain first
|
|
239
|
-
if let token = keychainToken(account: config.auth.tokenKeychain) {
|
|
240
|
-
return token
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Try environment variable
|
|
244
|
-
let envKey = config.auth.tokenKeychain.uppercased().replacingOccurrences(of: "-", with: "_")
|
|
245
|
-
if let token = ProcessInfo.processInfo.environment[envKey] {
|
|
246
|
-
return token.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
throw FetchError.missingToken(config.auth.tokenKeychain)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/// Read token from keychain
|
|
253
|
-
private func keychainToken(account: String) -> String? {
|
|
254
|
-
#if canImport(Security)
|
|
255
|
-
var result: CFTypeRef?
|
|
256
|
-
var query: [String: Any] = [
|
|
257
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
258
|
-
kSecAttrService as String: keychainService,
|
|
259
|
-
kSecAttrAccount as String: account,
|
|
260
|
-
kSecUseDataProtectionKeychain as String: true,
|
|
261
|
-
kSecUseAuthenticationUI as String: "kSecUseAuthenticationUIFail" as CFString,
|
|
262
|
-
kSecMatchLimit as String: kSecMatchLimitOne,
|
|
263
|
-
kSecReturnData as String: true,
|
|
264
|
-
]
|
|
265
|
-
#if canImport(LocalAuthentication)
|
|
266
|
-
let authContext = LAContext()
|
|
267
|
-
authContext.interactionNotAllowed = true
|
|
268
|
-
query[kSecUseAuthenticationContext as String] = authContext
|
|
269
|
-
#endif
|
|
270
|
-
|
|
271
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
272
|
-
if status == errSecItemNotFound {
|
|
273
|
-
return nil
|
|
274
|
-
}
|
|
275
|
-
if status == errSecInteractionNotAllowed {
|
|
276
|
-
return nil
|
|
277
|
-
}
|
|
278
|
-
guard status == errSecSuccess else {
|
|
279
|
-
log.error("Keychain read failed for \(account): \(status)")
|
|
280
|
-
return nil
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
guard let data = result as? Data,
|
|
284
|
-
let token = String(data: data, encoding: .utf8)?
|
|
285
|
-
.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
286
|
-
!token.isEmpty
|
|
287
|
-
else {
|
|
288
|
-
return nil
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return token
|
|
292
|
-
#else
|
|
293
|
-
_ = account
|
|
294
|
-
return nil
|
|
295
|
-
#endif
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// MARK: - Data Extraction
|
|
299
|
-
|
|
300
|
-
/// Extract usage data using JSONPath-like mapping
|
|
301
|
-
private func extractUsageData(from json: [String: Any], mapping: ResponseMapping) throws -> UsageData {
|
|
302
|
-
var data = UsageData()
|
|
303
|
-
|
|
304
|
-
// Extract quota
|
|
305
|
-
if let quotaPath = mapping.quota {
|
|
306
|
-
data.quota = extractDouble(from: json, path: quotaPath, nested: mapping.nestedPaths)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Extract used amount
|
|
310
|
-
if let usedPath = mapping.used {
|
|
311
|
-
data.used = extractDouble(from: json, path: usedPath, nested: mapping.nestedPaths)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Extract remaining amount
|
|
315
|
-
if let remainingPath = mapping.remaining {
|
|
316
|
-
data.remaining = extractDouble(from: json, path: remainingPath, nested: mapping.nestedPaths)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Extract cost
|
|
320
|
-
if let costPath = mapping.cost {
|
|
321
|
-
data.cost = extractDouble(from: json, path: costPath, nested: mapping.nestedPaths)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Extract reset date
|
|
325
|
-
if let resetPath = mapping.resetDate {
|
|
326
|
-
data.resetDate = extractDate(from: json, path: resetPath, nested: mapping.nestedPaths)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Extract tokens
|
|
330
|
-
if let tokensPath = mapping.tokens {
|
|
331
|
-
data.tokens = extractInt(from: json, path: tokensPath, nested: mapping.nestedPaths)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return data
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/// Extract balance data using JSONPath-like mapping
|
|
338
|
-
private func extractBalanceData(from json: [String: Any], mapping: ResponseMapping) throws -> BalanceData {
|
|
339
|
-
var data = BalanceData()
|
|
340
|
-
|
|
341
|
-
// Extract balance (use 'remaining' or 'used' mapping)
|
|
342
|
-
if let balancePath = mapping.remaining ?? mapping.used {
|
|
343
|
-
data.balance = extractDouble(from: json, path: balancePath, nested: mapping.nestedPaths)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Try to detect currency from response
|
|
347
|
-
data.currency = extractString(from: json, path: "currency", nested: true) ?? "USD"
|
|
348
|
-
|
|
349
|
-
return data
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// MARK: - JSONPath Extraction
|
|
353
|
-
|
|
354
|
-
/// Extract nested value using dot notation path
|
|
355
|
-
private func extractValue(from json: [String: Any], path: String, nested: Bool) -> Any? {
|
|
356
|
-
if !nested {
|
|
357
|
-
return json[path]
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
let components = path.split(separator: ".").map(String.init)
|
|
361
|
-
var current: Any? = json
|
|
362
|
-
|
|
363
|
-
for component in components {
|
|
364
|
-
// Handle array indexing: field[0]
|
|
365
|
-
if let bracketIndex = component.firstIndex(of: "["),
|
|
366
|
-
let closeBracket = component.firstIndex(of: "]") {
|
|
367
|
-
let fieldName = String(component[..<bracketIndex])
|
|
368
|
-
let indexStr = String(component[component.index(after: bracketIndex)..<closeBracket])
|
|
369
|
-
|
|
370
|
-
guard let dict = current as? [String: Any],
|
|
371
|
-
let array = dict[fieldName] as? [Any],
|
|
372
|
-
let index = Int(indexStr),
|
|
373
|
-
index < array.count else {
|
|
374
|
-
return nil
|
|
375
|
-
}
|
|
376
|
-
current = array[index]
|
|
377
|
-
} else {
|
|
378
|
-
// Regular field access
|
|
379
|
-
guard let dict = current as? [String: Any] else {
|
|
380
|
-
return nil
|
|
381
|
-
}
|
|
382
|
-
current = dict[component]
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return current
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/// Extract Double value from JSON path
|
|
390
|
-
private func extractDouble(from json: [String: Any], path: String, nested: Bool) -> Double? {
|
|
391
|
-
guard let value = extractValue(from: json, path: path, nested: nested) else {
|
|
392
|
-
return nil
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Handle different numeric types
|
|
396
|
-
switch value {
|
|
397
|
-
case let double as Double:
|
|
398
|
-
return double
|
|
399
|
-
case let int as Int:
|
|
400
|
-
return Double(int)
|
|
401
|
-
case let string as String:
|
|
402
|
-
return Double(string)
|
|
403
|
-
case let number as NSNumber:
|
|
404
|
-
return number.doubleValue
|
|
405
|
-
default:
|
|
406
|
-
return nil
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/// Extract Int value from JSON path
|
|
411
|
-
private func extractInt(from json: [String: Any], path: String, nested: Bool) -> Int? {
|
|
412
|
-
guard let value = extractValue(from: json, path: path, nested: nested) else {
|
|
413
|
-
return nil
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
switch value {
|
|
417
|
-
case let int as Int:
|
|
418
|
-
return int
|
|
419
|
-
case let double as Double:
|
|
420
|
-
return Int(double)
|
|
421
|
-
case let string as String:
|
|
422
|
-
return Int(string)
|
|
423
|
-
case let number as NSNumber:
|
|
424
|
-
return number.intValue
|
|
425
|
-
default:
|
|
426
|
-
return nil
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/// Extract String value from JSON path
|
|
431
|
-
private func extractString(from json: [String: Any], path: String, nested: Bool) -> String? {
|
|
432
|
-
guard let value = extractValue(from: json, path: path, nested: nested) else {
|
|
433
|
-
return nil
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
switch value {
|
|
437
|
-
case let string as String:
|
|
438
|
-
return string
|
|
439
|
-
case let number as NSNumber:
|
|
440
|
-
return number.stringValue
|
|
441
|
-
default:
|
|
442
|
-
return String(describing: value)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/// Extract Date value from JSON path (supports Unix timestamp and ISO8601)
|
|
447
|
-
private func extractDate(from json: [String: Any], path: String, nested: Bool) -> Date? {
|
|
448
|
-
guard let value = extractValue(from: json, path: path, nested: nested) else {
|
|
449
|
-
return nil
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Try Unix timestamp (seconds or milliseconds)
|
|
453
|
-
if let timestamp = value as? Double {
|
|
454
|
-
// If timestamp > 10 billion, assume milliseconds
|
|
455
|
-
let interval = timestamp > 10_000_000_000 ? timestamp / 1000.0 : timestamp
|
|
456
|
-
return Date(timeIntervalSince1970: interval)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if let timestamp = value as? Int {
|
|
460
|
-
let interval = timestamp > 10_000_000_000 ? Double(timestamp) / 1000.0 : Double(timestamp)
|
|
461
|
-
return Date(timeIntervalSince1970: interval)
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Try ISO8601 string
|
|
465
|
-
if let dateString = value as? String {
|
|
466
|
-
let formatter = ISO8601DateFormatter()
|
|
467
|
-
if let date = formatter.date(from: dateString) {
|
|
468
|
-
return date
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Try RFC3339 with fractional seconds
|
|
472
|
-
formatter.formatOptions.insert(.withFractionalSeconds)
|
|
473
|
-
if let date = formatter.date(from: dateString) {
|
|
474
|
-
return date
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
return nil
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// MARK: - Helpers
|
|
482
|
-
|
|
483
|
-
/// Format reset date into human-readable description
|
|
484
|
-
private func resetDescription(from date: Date) -> String {
|
|
485
|
-
let now = Date()
|
|
486
|
-
let interval = date.timeIntervalSince(now)
|
|
487
|
-
|
|
488
|
-
if interval <= 0 {
|
|
489
|
-
return "Reset now"
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
let hours = Int(interval / 3600)
|
|
493
|
-
let minutes = Int((interval.truncatingRemainder(dividingBy: 3600)) / 60)
|
|
494
|
-
|
|
495
|
-
if hours > 24 {
|
|
496
|
-
let days = hours / 24
|
|
497
|
-
return "Resets in \(days)d"
|
|
498
|
-
} else if hours > 0 {
|
|
499
|
-
return "Resets in \(hours)h \(minutes)m"
|
|
500
|
-
} else {
|
|
501
|
-
return "Resets in \(minutes)m"
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// MARK: - Data Structures
|
|
507
|
-
|
|
508
|
-
/// Usage data fetched from custom provider
|
|
509
|
-
public struct UsageData: Sendable {
|
|
510
|
-
public var quota: Double?
|
|
511
|
-
public var used: Double?
|
|
512
|
-
public var remaining: Double?
|
|
513
|
-
public var cost: Double?
|
|
514
|
-
public var resetDate: Date?
|
|
515
|
-
public var tokens: Int?
|
|
516
|
-
|
|
517
|
-
public init(
|
|
518
|
-
quota: Double? = nil,
|
|
519
|
-
used: Double? = nil,
|
|
520
|
-
remaining: Double? = nil,
|
|
521
|
-
cost: Double? = nil,
|
|
522
|
-
resetDate: Date? = nil,
|
|
523
|
-
tokens: Int? = nil
|
|
524
|
-
) {
|
|
525
|
-
self.quota = quota
|
|
526
|
-
self.used = used
|
|
527
|
-
self.remaining = remaining
|
|
528
|
-
self.cost = cost
|
|
529
|
-
self.resetDate = resetDate
|
|
530
|
-
self.tokens = tokens
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
public func toCustomUsageData() -> CustomUsageData {
|
|
534
|
-
return CustomUsageData(
|
|
535
|
-
quota: self.quota,
|
|
536
|
-
used: self.used,
|
|
537
|
-
remaining: self.remaining,
|
|
538
|
-
cost: self.cost,
|
|
539
|
-
resetDate: self.resetDate,
|
|
540
|
-
tokens: self.tokens
|
|
541
|
-
)
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/// Balance data fetched from custom provider
|
|
546
|
-
public struct BalanceData: Sendable {
|
|
547
|
-
public var balance: Double?
|
|
548
|
-
public var currency: String?
|
|
549
|
-
|
|
550
|
-
public init(balance: Double? = nil, currency: String? = nil) {
|
|
551
|
-
self.balance = balance
|
|
552
|
-
self.currency = currency
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// MARK: - Errors
|
|
557
|
-
|
|
558
|
-
/// Errors that can occur during fetch operations
|
|
559
|
-
public enum FetchError: LocalizedError, Sendable {
|
|
560
|
-
case noUsageEndpoint
|
|
561
|
-
case noBalanceEndpoint
|
|
562
|
-
case invalidURL(String)
|
|
563
|
-
case invalidResponse
|
|
564
|
-
case httpError(Int)
|
|
565
|
-
case invalidJSON
|
|
566
|
-
case missingToken(String)
|
|
567
|
-
case invalidDateRange
|
|
568
|
-
case extractionFailed(String)
|
|
569
|
-
case insecureURL(String)
|
|
570
|
-
|
|
571
|
-
public var errorDescription: String? {
|
|
572
|
-
switch self {
|
|
573
|
-
case .noUsageEndpoint:
|
|
574
|
-
return "No usage endpoint configured for this provider"
|
|
575
|
-
case .noBalanceEndpoint:
|
|
576
|
-
return "No balance endpoint configured for this provider"
|
|
577
|
-
case .invalidURL(let url):
|
|
578
|
-
return "Invalid URL: \(url)"
|
|
579
|
-
case .invalidResponse:
|
|
580
|
-
return "Invalid response from provider API"
|
|
581
|
-
case .httpError(let code):
|
|
582
|
-
return "HTTP error \(code) from provider API"
|
|
583
|
-
case .invalidJSON:
|
|
584
|
-
return "Failed to parse JSON response from provider"
|
|
585
|
-
case .missingToken(let account):
|
|
586
|
-
return "No API token found for '\(account)'. Store it in Keychain or set as environment variable."
|
|
587
|
-
case .invalidDateRange:
|
|
588
|
-
return "Failed to calculate date range for URL template"
|
|
589
|
-
case .extractionFailed(let field):
|
|
590
|
-
return "Failed to extract field '\(field)' from response"
|
|
591
|
-
case .insecureURL(let url):
|
|
592
|
-
return "Only HTTPS URLs are allowed. Got: \(url)"
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
public var failureReason: String? {
|
|
597
|
-
switch self {
|
|
598
|
-
case .missingToken(let account):
|
|
599
|
-
return "The token for account '\(account)' is not configured in Keychain or environment variables"
|
|
600
|
-
case .httpError(let code):
|
|
601
|
-
return "The server returned HTTP status code \(code)"
|
|
602
|
-
case .invalidJSON:
|
|
603
|
-
return "The response is not valid JSON or has unexpected structure"
|
|
604
|
-
case .extractionFailed(let field):
|
|
605
|
-
return "The field '\(field)' was not found in the response or has wrong type"
|
|
606
|
-
case .insecureURL:
|
|
607
|
-
return "The URL scheme is not HTTPS"
|
|
608
|
-
default:
|
|
609
|
-
return nil
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
public var recoverySuggestion: String? {
|
|
614
|
-
switch self {
|
|
615
|
-
case .missingToken(let account):
|
|
616
|
-
return "Store your API token in Keychain with account name '\(account)' or set it as environment variable"
|
|
617
|
-
case .noUsageEndpoint:
|
|
618
|
-
return "Configure a usage endpoint in the provider settings"
|
|
619
|
-
case .noBalanceEndpoint:
|
|
620
|
-
return "Configure a balance endpoint in the provider settings"
|
|
621
|
-
case .httpError(let code) where code == 401:
|
|
622
|
-
return "Check that your API token is valid and has not expired"
|
|
623
|
-
case .httpError(let code) where code == 429:
|
|
624
|
-
return "You have been rate limited. Try again later."
|
|
625
|
-
case .invalidJSON:
|
|
626
|
-
return "Check the API endpoint URL and response mapping configuration"
|
|
627
|
-
case .insecureURL:
|
|
628
|
-
return "Change the provider URL to use HTTPS (e.g., https://api.example.com)"
|
|
629
|
-
default:
|
|
630
|
-
return nil
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|