@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,65 +0,0 @@
|
|
|
1
|
-
import RunicMacroSupport
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
@ProviderDescriptorRegistration
|
|
5
|
-
@ProviderDescriptorDefinition
|
|
6
|
-
public enum CursorProviderDescriptor {
|
|
7
|
-
static func makeDescriptor() -> ProviderDescriptor {
|
|
8
|
-
ProviderDescriptor(
|
|
9
|
-
id: .cursor,
|
|
10
|
-
metadata: ProviderMetadata(
|
|
11
|
-
id: .cursor,
|
|
12
|
-
displayName: "Cursor",
|
|
13
|
-
sessionLabel: "Plan",
|
|
14
|
-
weeklyLabel: "On-Demand",
|
|
15
|
-
opusLabel: nil,
|
|
16
|
-
supportsOpus: false,
|
|
17
|
-
supportsCredits: true,
|
|
18
|
-
creditsHint: "On-demand usage beyond included plan limits.",
|
|
19
|
-
toggleTitle: "Show Cursor usage",
|
|
20
|
-
cliName: "cursor",
|
|
21
|
-
defaultEnabled: false,
|
|
22
|
-
isPrimaryProvider: false,
|
|
23
|
-
usesAccountFallback: false,
|
|
24
|
-
browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder,
|
|
25
|
-
dashboardURL: "https://cursor.com/dashboard?tab=usage",
|
|
26
|
-
statusPageURL: "https://status.cursor.com",
|
|
27
|
-
statusLinkURL: nil,
|
|
28
|
-
usageCoverage: ProviderUsageCoverage(
|
|
29
|
-
supportsModelBreakdown: false,
|
|
30
|
-
supportsTokenMetrics: false,
|
|
31
|
-
supportsProjectAttribution: false)),
|
|
32
|
-
branding: ProviderBranding(
|
|
33
|
-
iconStyle: .cursor,
|
|
34
|
-
iconResourceName: "ProviderIcon-cursor",
|
|
35
|
-
color: ProviderColor(red: 0 / 255, green: 191 / 255, blue: 165 / 255)),
|
|
36
|
-
tokenCost: ProviderTokenCostConfig(
|
|
37
|
-
supportsTokenCost: false,
|
|
38
|
-
noDataMessage: { "Cursor cost summary is not supported." }),
|
|
39
|
-
fetchPlan: ProviderFetchPlan(
|
|
40
|
-
sourceModes: [.auto, .cli],
|
|
41
|
-
pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [CursorStatusFetchStrategy()] })),
|
|
42
|
-
cli: ProviderCLIConfig(
|
|
43
|
-
name: "cursor",
|
|
44
|
-
versionDetector: nil))
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
struct CursorStatusFetchStrategy: ProviderFetchStrategy {
|
|
49
|
-
let id: String = "cursor.web"
|
|
50
|
-
let kind: ProviderFetchKind = .web
|
|
51
|
-
|
|
52
|
-
func isAvailable(_: ProviderFetchContext) async -> Bool { true }
|
|
53
|
-
|
|
54
|
-
func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
|
|
55
|
-
let probe = CursorStatusProbe()
|
|
56
|
-
let snap = try await probe.fetch()
|
|
57
|
-
return self.makeResult(
|
|
58
|
-
usage: snap.toUsageSnapshot(),
|
|
59
|
-
sourceLabel: "web")
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
|
|
63
|
-
false
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,595 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import Silo
|
|
3
|
-
|
|
4
|
-
#if os(macOS)
|
|
5
|
-
|
|
6
|
-
private let cursorCookieImportOrder: BrowserCookieImportOrder =
|
|
7
|
-
ProviderDefaults.metadata[.cursor]?.browserCookieOrder ?? Browser.defaultImportOrder
|
|
8
|
-
|
|
9
|
-
// MARK: - Cursor Cookie Importer
|
|
10
|
-
|
|
11
|
-
/// Imports Cursor session cookies from browser cookies.
|
|
12
|
-
public enum CursorCookieImporter {
|
|
13
|
-
private static let cookieClient = BrowserCookieClient()
|
|
14
|
-
private static let sessionCookieNames: Set<String> = [
|
|
15
|
-
"WorkosCursorSessionToken",
|
|
16
|
-
"__Secure-next-auth.session-token",
|
|
17
|
-
"next-auth.session-token",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
public struct SessionInfo: Sendable {
|
|
21
|
-
public let cookies: [HTTPCookie]
|
|
22
|
-
public let sourceLabel: String
|
|
23
|
-
|
|
24
|
-
public init(cookies: [HTTPCookie], sourceLabel: String) {
|
|
25
|
-
self.cookies = cookies
|
|
26
|
-
self.sourceLabel = sourceLabel
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public var cookieHeader: String {
|
|
30
|
-
self.cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/// Attempts to import Cursor cookies using the standard browser import order.
|
|
35
|
-
public static func importSession(logger: ((String) -> Void)? = nil) throws -> SessionInfo {
|
|
36
|
-
let log: (String) -> Void = { msg in logger?("[cursor-cookie] \(msg)") }
|
|
37
|
-
|
|
38
|
-
let cookieDomains = ["cursor.com", "cursor.sh"]
|
|
39
|
-
for browserSource in cursorCookieImportOrder {
|
|
40
|
-
do {
|
|
41
|
-
let query = BrowserCookieQuery(domains: cookieDomains)
|
|
42
|
-
let sources = try Self.cookieClient.records(
|
|
43
|
-
matching: query,
|
|
44
|
-
in: browserSource)
|
|
45
|
-
for source in sources where !source.records.isEmpty {
|
|
46
|
-
let httpCookies = BrowserCookieClient.makeHTTPCookies(source.records, origin: query.origin)
|
|
47
|
-
if httpCookies.contains(where: { Self.sessionCookieNames.contains($0.name) }) {
|
|
48
|
-
log("Found \(httpCookies.count) Cursor cookies in \(source.label)")
|
|
49
|
-
return SessionInfo(cookies: httpCookies, sourceLabel: source.label)
|
|
50
|
-
} else {
|
|
51
|
-
log("\(source.label) cookies found, but no Cursor session cookie present")
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
log("\(browserSource.displayName) cookie import failed: \(error.localizedDescription)")
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
throw CursorStatusProbeError.noSessionCookie
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/// Check if Cursor session cookies are available
|
|
63
|
-
public static func hasSession(logger: ((String) -> Void)? = nil) -> Bool {
|
|
64
|
-
do {
|
|
65
|
-
let session = try self.importSession(logger: logger)
|
|
66
|
-
return !session.cookies.isEmpty
|
|
67
|
-
} catch {
|
|
68
|
-
return false
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// MARK: - Cursor API Models
|
|
74
|
-
|
|
75
|
-
public struct CursorUsageSummary: Codable, Sendable {
|
|
76
|
-
public let billingCycleStart: String?
|
|
77
|
-
public let billingCycleEnd: String?
|
|
78
|
-
public let membershipType: String?
|
|
79
|
-
public let limitType: String?
|
|
80
|
-
public let isUnlimited: Bool?
|
|
81
|
-
public let autoModelSelectedDisplayMessage: String?
|
|
82
|
-
public let namedModelSelectedDisplayMessage: String?
|
|
83
|
-
public let individualUsage: CursorIndividualUsage?
|
|
84
|
-
public let teamUsage: CursorTeamUsage?
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public struct CursorIndividualUsage: Codable, Sendable {
|
|
88
|
-
public let plan: CursorPlanUsage?
|
|
89
|
-
public let onDemand: CursorOnDemandUsage?
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public struct CursorPlanUsage: Codable, Sendable {
|
|
93
|
-
public let enabled: Bool?
|
|
94
|
-
/// Usage in cents (e.g., 2000 = $20.00)
|
|
95
|
-
public let used: Int?
|
|
96
|
-
/// Limit in cents (e.g., 2000 = $20.00)
|
|
97
|
-
public let limit: Int?
|
|
98
|
-
/// Remaining in cents
|
|
99
|
-
public let remaining: Int?
|
|
100
|
-
public let breakdown: CursorPlanBreakdown?
|
|
101
|
-
public let autoPercentUsed: Double?
|
|
102
|
-
public let apiPercentUsed: Double?
|
|
103
|
-
public let totalPercentUsed: Double?
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public struct CursorPlanBreakdown: Codable, Sendable {
|
|
107
|
-
public let included: Int?
|
|
108
|
-
public let bonus: Int?
|
|
109
|
-
public let total: Int?
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public struct CursorOnDemandUsage: Codable, Sendable {
|
|
113
|
-
public let enabled: Bool?
|
|
114
|
-
/// Usage in cents
|
|
115
|
-
public let used: Int?
|
|
116
|
-
/// Limit in cents (nil if unlimited)
|
|
117
|
-
public let limit: Int?
|
|
118
|
-
/// Remaining in cents (nil if unlimited)
|
|
119
|
-
public let remaining: Int?
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
public struct CursorTeamUsage: Codable, Sendable {
|
|
123
|
-
public let onDemand: CursorOnDemandUsage?
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
public struct CursorUserInfo: Codable, Sendable {
|
|
127
|
-
public let email: String?
|
|
128
|
-
public let emailVerified: Bool?
|
|
129
|
-
public let name: String?
|
|
130
|
-
public let sub: String?
|
|
131
|
-
public let createdAt: String?
|
|
132
|
-
public let updatedAt: String?
|
|
133
|
-
public let picture: String?
|
|
134
|
-
|
|
135
|
-
enum CodingKeys: String, CodingKey {
|
|
136
|
-
case email
|
|
137
|
-
case emailVerified = "email_verified"
|
|
138
|
-
case name
|
|
139
|
-
case sub
|
|
140
|
-
case createdAt = "created_at"
|
|
141
|
-
case updatedAt = "updated_at"
|
|
142
|
-
case picture
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// MARK: - Cursor Status Snapshot
|
|
147
|
-
|
|
148
|
-
public struct CursorStatusSnapshot: Sendable {
|
|
149
|
-
/// Percentage of included plan usage (0-100)
|
|
150
|
-
public let planPercentUsed: Double
|
|
151
|
-
/// Included plan usage in USD
|
|
152
|
-
public let planUsedUSD: Double
|
|
153
|
-
/// Included plan limit in USD
|
|
154
|
-
public let planLimitUSD: Double
|
|
155
|
-
/// On-demand usage in USD
|
|
156
|
-
public let onDemandUsedUSD: Double
|
|
157
|
-
/// On-demand limit in USD (nil if unlimited)
|
|
158
|
-
public let onDemandLimitUSD: Double?
|
|
159
|
-
/// Team on-demand usage in USD (for team plans)
|
|
160
|
-
public let teamOnDemandUsedUSD: Double?
|
|
161
|
-
/// Team on-demand limit in USD
|
|
162
|
-
public let teamOnDemandLimitUSD: Double?
|
|
163
|
-
/// Billing cycle reset date
|
|
164
|
-
public let billingCycleEnd: Date?
|
|
165
|
-
/// Membership type (e.g., "enterprise", "pro", "hobby")
|
|
166
|
-
public let membershipType: String?
|
|
167
|
-
/// User email
|
|
168
|
-
public let accountEmail: String?
|
|
169
|
-
/// User name
|
|
170
|
-
public let accountName: String?
|
|
171
|
-
/// Raw API response for debugging
|
|
172
|
-
public let rawJSON: String?
|
|
173
|
-
|
|
174
|
-
public init(
|
|
175
|
-
planPercentUsed: Double,
|
|
176
|
-
planUsedUSD: Double,
|
|
177
|
-
planLimitUSD: Double,
|
|
178
|
-
onDemandUsedUSD: Double,
|
|
179
|
-
onDemandLimitUSD: Double?,
|
|
180
|
-
teamOnDemandUsedUSD: Double?,
|
|
181
|
-
teamOnDemandLimitUSD: Double?,
|
|
182
|
-
billingCycleEnd: Date?,
|
|
183
|
-
membershipType: String?,
|
|
184
|
-
accountEmail: String?,
|
|
185
|
-
accountName: String?,
|
|
186
|
-
rawJSON: String?)
|
|
187
|
-
{
|
|
188
|
-
self.planPercentUsed = planPercentUsed
|
|
189
|
-
self.planUsedUSD = planUsedUSD
|
|
190
|
-
self.planLimitUSD = planLimitUSD
|
|
191
|
-
self.onDemandUsedUSD = onDemandUsedUSD
|
|
192
|
-
self.onDemandLimitUSD = onDemandLimitUSD
|
|
193
|
-
self.teamOnDemandUsedUSD = teamOnDemandUsedUSD
|
|
194
|
-
self.teamOnDemandLimitUSD = teamOnDemandLimitUSD
|
|
195
|
-
self.billingCycleEnd = billingCycleEnd
|
|
196
|
-
self.membershipType = membershipType
|
|
197
|
-
self.accountEmail = accountEmail
|
|
198
|
-
self.accountName = accountName
|
|
199
|
-
self.rawJSON = rawJSON
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/// Convert to UsageSnapshot for the common provider interface
|
|
203
|
-
public func toUsageSnapshot() -> UsageSnapshot {
|
|
204
|
-
// Primary: Plan usage percentage
|
|
205
|
-
let primary = RateWindow(
|
|
206
|
-
usedPercent: self.planPercentUsed,
|
|
207
|
-
windowMinutes: nil,
|
|
208
|
-
resetsAt: self.billingCycleEnd,
|
|
209
|
-
resetDescription: self.billingCycleEnd.map { Self.formatResetDate($0) })
|
|
210
|
-
|
|
211
|
-
let resolvedOnDemandUsed: Double
|
|
212
|
-
let resolvedOnDemandLimit: Double?
|
|
213
|
-
|
|
214
|
-
if let teamLimit = self.teamOnDemandLimitUSD,
|
|
215
|
-
let teamUsed = self.teamOnDemandUsedUSD
|
|
216
|
-
{
|
|
217
|
-
resolvedOnDemandUsed = teamUsed
|
|
218
|
-
resolvedOnDemandLimit = teamLimit
|
|
219
|
-
} else {
|
|
220
|
-
resolvedOnDemandUsed = self.onDemandUsedUSD
|
|
221
|
-
resolvedOnDemandLimit = self.onDemandLimitUSD
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Secondary: On-demand usage as percentage of limit (team when present, else individual)
|
|
225
|
-
let secondary: RateWindow? = if let limit = resolvedOnDemandLimit,
|
|
226
|
-
limit > 0
|
|
227
|
-
{
|
|
228
|
-
RateWindow(
|
|
229
|
-
usedPercent: (resolvedOnDemandUsed / limit) * 100,
|
|
230
|
-
windowMinutes: nil,
|
|
231
|
-
resetsAt: self.billingCycleEnd,
|
|
232
|
-
resetDescription: self.billingCycleEnd.map { Self.formatResetDate($0) })
|
|
233
|
-
} else {
|
|
234
|
-
nil
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Provider cost snapshot for on-demand usage
|
|
238
|
-
let providerCost: ProviderCostSnapshot? = if resolvedOnDemandUsed > 0 {
|
|
239
|
-
ProviderCostSnapshot(
|
|
240
|
-
used: resolvedOnDemandUsed,
|
|
241
|
-
limit: resolvedOnDemandLimit ?? 0,
|
|
242
|
-
currencyCode: "USD",
|
|
243
|
-
period: "monthly",
|
|
244
|
-
resetsAt: self.billingCycleEnd,
|
|
245
|
-
updatedAt: Date())
|
|
246
|
-
} else {
|
|
247
|
-
nil
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let identity = ProviderIdentitySnapshot(
|
|
251
|
-
providerID: .cursor,
|
|
252
|
-
accountEmail: self.accountEmail,
|
|
253
|
-
accountOrganization: nil,
|
|
254
|
-
loginMethod: self.membershipType.map { Self.formatMembershipType($0) })
|
|
255
|
-
return UsageSnapshot(
|
|
256
|
-
primary: primary,
|
|
257
|
-
secondary: secondary,
|
|
258
|
-
tertiary: nil,
|
|
259
|
-
providerCost: providerCost,
|
|
260
|
-
updatedAt: Date(),
|
|
261
|
-
identity: identity)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
private static func formatResetDate(_ date: Date) -> String {
|
|
265
|
-
let formatter = DateFormatter()
|
|
266
|
-
formatter.dateFormat = "MMM d 'at' h:mma"
|
|
267
|
-
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
268
|
-
return "Resets " + formatter.string(from: date)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
private static func formatMembershipType(_ type: String) -> String {
|
|
272
|
-
switch type.lowercased() {
|
|
273
|
-
case "enterprise":
|
|
274
|
-
"Cursor Enterprise"
|
|
275
|
-
case "pro":
|
|
276
|
-
"Cursor Pro"
|
|
277
|
-
case "hobby":
|
|
278
|
-
"Cursor Hobby"
|
|
279
|
-
case "team":
|
|
280
|
-
"Cursor Team"
|
|
281
|
-
default:
|
|
282
|
-
"Cursor \(type.capitalized)"
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// MARK: - Cursor Status Probe Error
|
|
288
|
-
|
|
289
|
-
public enum CursorStatusProbeError: LocalizedError, Sendable {
|
|
290
|
-
case notLoggedIn
|
|
291
|
-
case networkError(String)
|
|
292
|
-
case parseFailed(String)
|
|
293
|
-
case noSessionCookie
|
|
294
|
-
|
|
295
|
-
public var errorDescription: String? {
|
|
296
|
-
switch self {
|
|
297
|
-
case .notLoggedIn:
|
|
298
|
-
"Not logged in to Cursor. Please log in via the Runic menu."
|
|
299
|
-
case let .networkError(msg):
|
|
300
|
-
"Cursor API error: \(msg)"
|
|
301
|
-
case let .parseFailed(msg):
|
|
302
|
-
"Could not parse Cursor usage: \(msg)"
|
|
303
|
-
case .noSessionCookie:
|
|
304
|
-
"No Cursor session found. Please log in to cursor.com in \(cursorCookieImportOrder.loginHint)."
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// MARK: - Cursor Session Store
|
|
310
|
-
|
|
311
|
-
public actor CursorSessionStore {
|
|
312
|
-
public static let shared = CursorSessionStore()
|
|
313
|
-
|
|
314
|
-
private var sessionCookies: [HTTPCookie] = []
|
|
315
|
-
private let fileURL: URL
|
|
316
|
-
|
|
317
|
-
private init() {
|
|
318
|
-
let fm = FileManager.default
|
|
319
|
-
let appSupport = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
|
|
320
|
-
?? fm.temporaryDirectory
|
|
321
|
-
let dir = appSupport.appendingPathComponent("Runic", isDirectory: true)
|
|
322
|
-
try? fm.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
323
|
-
self.fileURL = dir.appendingPathComponent("cursor-session.json")
|
|
324
|
-
|
|
325
|
-
// Load saved cookies on init
|
|
326
|
-
Task { await self.loadFromDisk() }
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
public func setCookies(_ cookies: [HTTPCookie]) {
|
|
330
|
-
self.sessionCookies = cookies
|
|
331
|
-
self.saveToDisk()
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
public func getCookies() -> [HTTPCookie] {
|
|
335
|
-
self.sessionCookies
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
public func clearCookies() {
|
|
339
|
-
self.sessionCookies = []
|
|
340
|
-
try? FileManager.default.removeItem(at: self.fileURL)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
public func hasValidSession() -> Bool {
|
|
344
|
-
!self.sessionCookies.isEmpty
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private func saveToDisk() {
|
|
348
|
-
// Convert cookie properties to JSON-serializable format
|
|
349
|
-
// Date values must be converted to TimeInterval (Double)
|
|
350
|
-
let cookieData = self.sessionCookies.compactMap { cookie -> [String: Any]? in
|
|
351
|
-
guard let props = cookie.properties else { return nil }
|
|
352
|
-
var serializable: [String: Any] = [:]
|
|
353
|
-
for (key, value) in props {
|
|
354
|
-
let keyString = key.rawValue
|
|
355
|
-
if let date = value as? Date {
|
|
356
|
-
// Convert Date to TimeInterval for JSON compatibility
|
|
357
|
-
serializable[keyString] = date.timeIntervalSince1970
|
|
358
|
-
serializable[keyString + "_isDate"] = true
|
|
359
|
-
} else if let url = value as? URL {
|
|
360
|
-
serializable[keyString] = url.absoluteString
|
|
361
|
-
serializable[keyString + "_isURL"] = true
|
|
362
|
-
} else if JSONSerialization.isValidJSONObject([value]) ||
|
|
363
|
-
value is String ||
|
|
364
|
-
value is Bool ||
|
|
365
|
-
value is NSNumber
|
|
366
|
-
{
|
|
367
|
-
serializable[keyString] = value
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return serializable
|
|
371
|
-
}
|
|
372
|
-
guard !cookieData.isEmpty,
|
|
373
|
-
let data = try? JSONSerialization.data(withJSONObject: cookieData, options: [.prettyPrinted])
|
|
374
|
-
else {
|
|
375
|
-
return
|
|
376
|
-
}
|
|
377
|
-
try? data.write(to: self.fileURL)
|
|
378
|
-
try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: self.fileURL.path)
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
private func loadFromDisk() {
|
|
382
|
-
guard let data = try? Data(contentsOf: self.fileURL),
|
|
383
|
-
let cookieArray = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]]
|
|
384
|
-
else { return }
|
|
385
|
-
|
|
386
|
-
self.sessionCookies = cookieArray.compactMap { props in
|
|
387
|
-
// Convert back to HTTPCookiePropertyKey dictionary
|
|
388
|
-
var cookieProps: [HTTPCookiePropertyKey: Any] = [:]
|
|
389
|
-
for (key, value) in props {
|
|
390
|
-
// Skip marker keys
|
|
391
|
-
if key.hasSuffix("_isDate") || key.hasSuffix("_isURL") { continue }
|
|
392
|
-
|
|
393
|
-
let propKey = HTTPCookiePropertyKey(key)
|
|
394
|
-
|
|
395
|
-
// Check if this was a Date
|
|
396
|
-
if props[key + "_isDate"] as? Bool == true, let interval = value as? TimeInterval {
|
|
397
|
-
cookieProps[propKey] = Date(timeIntervalSince1970: interval)
|
|
398
|
-
}
|
|
399
|
-
// Check if this was a URL
|
|
400
|
-
else if props[key + "_isURL"] as? Bool == true, let urlString = value as? String {
|
|
401
|
-
cookieProps[propKey] = URL(string: urlString)
|
|
402
|
-
} else {
|
|
403
|
-
cookieProps[propKey] = value
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return HTTPCookie(properties: cookieProps)
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// MARK: - Cursor Status Probe
|
|
412
|
-
|
|
413
|
-
public struct CursorStatusProbe: Sendable {
|
|
414
|
-
public let baseURL: URL
|
|
415
|
-
public var timeout: TimeInterval = 15.0
|
|
416
|
-
|
|
417
|
-
public init(baseURL: URL = URL(string: "https://cursor.com")!, timeout: TimeInterval = 15.0) {
|
|
418
|
-
self.baseURL = baseURL
|
|
419
|
-
self.timeout = timeout
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/// Fetch Cursor usage using browser cookies with fallback to stored session.
|
|
423
|
-
public func fetch(logger: ((String) -> Void)? = nil) async throws -> CursorStatusSnapshot {
|
|
424
|
-
let log: (String) -> Void = { msg in logger?("[cursor] \(msg)") }
|
|
425
|
-
|
|
426
|
-
// Try importing cookies from the configured browser order first.
|
|
427
|
-
do {
|
|
428
|
-
let session = try CursorCookieImporter.importSession(logger: log)
|
|
429
|
-
log("Using cookies from \(session.sourceLabel)")
|
|
430
|
-
return try await self.fetchWithCookieHeader(session.cookieHeader)
|
|
431
|
-
} catch {
|
|
432
|
-
log("Browser cookie import failed: \(error.localizedDescription)")
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Fall back to stored session cookies (from "Add Account" login flow)
|
|
436
|
-
let storedCookies = await CursorSessionStore.shared.getCookies()
|
|
437
|
-
if !storedCookies.isEmpty {
|
|
438
|
-
log("Using stored session cookies")
|
|
439
|
-
let cookieHeader = storedCookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
|
|
440
|
-
do {
|
|
441
|
-
return try await self.fetchWithCookieHeader(cookieHeader)
|
|
442
|
-
} catch {
|
|
443
|
-
if case CursorStatusProbeError.notLoggedIn = error {
|
|
444
|
-
// Clear only when auth is invalid; keep for transient failures.
|
|
445
|
-
await CursorSessionStore.shared.clearCookies()
|
|
446
|
-
log("Stored session invalid, cleared")
|
|
447
|
-
} else {
|
|
448
|
-
log("Stored session failed: \(error.localizedDescription)")
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
throw CursorStatusProbeError.noSessionCookie
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
private func fetchWithCookieHeader(_ cookieHeader: String) async throws -> CursorStatusSnapshot {
|
|
457
|
-
async let usageSummaryTask = self.fetchUsageSummary(cookieHeader: cookieHeader)
|
|
458
|
-
async let userInfoTask = self.fetchUserInfo(cookieHeader: cookieHeader)
|
|
459
|
-
|
|
460
|
-
let usageSummary = try await usageSummaryTask
|
|
461
|
-
let userInfo = try? await userInfoTask
|
|
462
|
-
|
|
463
|
-
return self.parseUsageSummary(usageSummary, userInfo: userInfo)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
private func fetchUsageSummary(cookieHeader: String) async throws -> CursorUsageSummary {
|
|
467
|
-
let url = self.baseURL.appendingPathComponent("/api/usage-summary")
|
|
468
|
-
var request = URLRequest(url: url)
|
|
469
|
-
request.timeoutInterval = self.timeout
|
|
470
|
-
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
471
|
-
request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
|
|
472
|
-
|
|
473
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
474
|
-
|
|
475
|
-
guard let httpResponse = response as? HTTPURLResponse else {
|
|
476
|
-
throw CursorStatusProbeError.networkError("Invalid response")
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
|
|
480
|
-
throw CursorStatusProbeError.notLoggedIn
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
guard httpResponse.statusCode == 200 else {
|
|
484
|
-
throw CursorStatusProbeError.networkError("HTTP \(httpResponse.statusCode)")
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
do {
|
|
488
|
-
let decoder = JSONDecoder()
|
|
489
|
-
return try decoder.decode(CursorUsageSummary.self, from: data)
|
|
490
|
-
} catch {
|
|
491
|
-
let rawJSON = String(data: data, encoding: .utf8) ?? "<binary>"
|
|
492
|
-
throw CursorStatusProbeError
|
|
493
|
-
.parseFailed("JSON decode failed: \(error.localizedDescription). Raw: \(rawJSON.prefix(200))")
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
private func fetchUserInfo(cookieHeader: String) async throws -> CursorUserInfo {
|
|
498
|
-
let url = self.baseURL.appendingPathComponent("/api/auth/me")
|
|
499
|
-
var request = URLRequest(url: url)
|
|
500
|
-
request.timeoutInterval = self.timeout
|
|
501
|
-
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
502
|
-
request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
|
|
503
|
-
|
|
504
|
-
let (data, response) = try await URLSession.shared.data(for: request)
|
|
505
|
-
|
|
506
|
-
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
|
507
|
-
throw CursorStatusProbeError.networkError("Failed to fetch user info")
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
let decoder = JSONDecoder()
|
|
511
|
-
return try decoder.decode(CursorUserInfo.self, from: data)
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
func parseUsageSummary(_ summary: CursorUsageSummary, userInfo: CursorUserInfo?) -> CursorStatusSnapshot {
|
|
515
|
-
// Parse billing cycle end date
|
|
516
|
-
let billingCycleEnd: Date? = summary.billingCycleEnd.flatMap { dateString in
|
|
517
|
-
let formatter = ISO8601DateFormatter()
|
|
518
|
-
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
519
|
-
return formatter.date(from: dateString) ?? ISO8601DateFormatter().date(from: dateString)
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Convert cents to USD (plan percent derives from raw values to avoid percent unit mismatches).
|
|
523
|
-
let planUsedRaw = Double(summary.individualUsage?.plan?.used ?? 0)
|
|
524
|
-
let planLimitRaw = Double(summary.individualUsage?.plan?.limit ?? 0)
|
|
525
|
-
let planUsed = planUsedRaw / 100.0
|
|
526
|
-
let planLimit = planLimitRaw / 100.0
|
|
527
|
-
let planPercentUsed: Double = if planLimitRaw > 0 {
|
|
528
|
-
(planUsedRaw / planLimitRaw) * 100
|
|
529
|
-
} else if let totalPercentUsed = summary.individualUsage?.plan?.totalPercentUsed {
|
|
530
|
-
totalPercentUsed <= 1 ? totalPercentUsed * 100 : totalPercentUsed
|
|
531
|
-
} else {
|
|
532
|
-
0
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
let onDemandUsed = Double(summary.individualUsage?.onDemand?.used ?? 0) / 100.0
|
|
536
|
-
let onDemandLimit: Double? = summary.individualUsage?.onDemand?.limit.map { Double($0) / 100.0 }
|
|
537
|
-
|
|
538
|
-
let teamOnDemandUsed: Double? = summary.teamUsage?.onDemand?.used.map { Double($0) / 100.0 }
|
|
539
|
-
let teamOnDemandLimit: Double? = summary.teamUsage?.onDemand?.limit.map { Double($0) / 100.0 }
|
|
540
|
-
|
|
541
|
-
return CursorStatusSnapshot(
|
|
542
|
-
planPercentUsed: planPercentUsed,
|
|
543
|
-
planUsedUSD: planUsed,
|
|
544
|
-
planLimitUSD: planLimit,
|
|
545
|
-
onDemandUsedUSD: onDemandUsed,
|
|
546
|
-
onDemandLimitUSD: onDemandLimit,
|
|
547
|
-
teamOnDemandUsedUSD: teamOnDemandUsed,
|
|
548
|
-
teamOnDemandLimitUSD: teamOnDemandLimit,
|
|
549
|
-
billingCycleEnd: billingCycleEnd,
|
|
550
|
-
membershipType: summary.membershipType,
|
|
551
|
-
accountEmail: userInfo?.email,
|
|
552
|
-
accountName: userInfo?.name,
|
|
553
|
-
rawJSON: nil)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
#else
|
|
558
|
-
|
|
559
|
-
// MARK: - Cursor (Unsupported)
|
|
560
|
-
|
|
561
|
-
public enum CursorStatusProbeError: LocalizedError, Sendable {
|
|
562
|
-
case notSupported
|
|
563
|
-
|
|
564
|
-
public var errorDescription: String? {
|
|
565
|
-
"Cursor is only supported on macOS."
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
public struct CursorStatusSnapshot: Sendable {
|
|
570
|
-
public init() {}
|
|
571
|
-
|
|
572
|
-
public func toUsageSnapshot() -> UsageSnapshot {
|
|
573
|
-
UsageSnapshot(
|
|
574
|
-
primary: RateWindow(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
|
|
575
|
-
secondary: nil,
|
|
576
|
-
tertiary: nil,
|
|
577
|
-
providerCost: nil,
|
|
578
|
-
updatedAt: Date(),
|
|
579
|
-
identity: nil)
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
public struct CursorStatusProbe: Sendable {
|
|
584
|
-
public init(baseURL: URL = URL(string: "https://cursor.com")!, timeout: TimeInterval = 15.0) {
|
|
585
|
-
_ = baseURL
|
|
586
|
-
_ = timeout
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
public func fetch(logger: ((String) -> Void)? = nil) async throws -> CursorStatusSnapshot {
|
|
590
|
-
_ = logger
|
|
591
|
-
throw CursorStatusProbeError.notSupported
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
#endif
|