@sriinnu/runic 2.0.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 +256 -0
- package/LICENSE +373 -21
- package/README.md +96 -89
- 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/RunicTypographyiOS.swift +0 -24
- 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 -117
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -670
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -519
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -824
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -90
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1412
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -339
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -423
- package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
- package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -81
- 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 -1222
- package/Sources/Runic/Core/Rendering/IconView.swift +0 -122
- 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/RunicTypography.swift +0 -206
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1749
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -33
- 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 -3631
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -117
- package/Sources/Runic/Core/UsageExporter.swift +0 -197
- 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 -51
- 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/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/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 -367
- package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
- package/Sources/Runic/UsagePaceText.swift +0 -52
- package/Sources/Runic/UsageProgressBar.swift +0 -228
- package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
- package/Sources/Runic/Utilities/LoadingPattern.swift +0 -62
- package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -254
- 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 -311
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -157
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -63
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -248
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
- package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1889
- package/Sources/Runic/Views/Menu/MenuContent.swift +0 -153
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -371
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -35
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -241
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -281
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -86
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -369
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -191
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -621
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -306
- 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 -418
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -637
- 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 -321
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -250
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -572
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -291
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3480
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -29
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -61
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -213
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -376
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
- package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -762
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
- package/Sources/RunicCLI/CLIEntry.swift +0 -983
- package/Sources/RunicCLI/CLIRenderer.swift +0 -87
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -363
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -453
- package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -430
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -167
- 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 -268
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -631
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -444
- 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 -370
- 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 -426
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
- package/Sources/RunicCore/ProjectBudgetStore.swift +0 -102
- package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -68
- 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 -164
- 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 -209
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -30
- 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 -245
- package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -26
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -183
- 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 -117
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -293
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -385
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -67
- 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 -67
- 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 -67
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -839
- 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 -97
- 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 -216
- 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 -86
- package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -328
- 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 -74
- package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -745
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -384
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -307
- package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -287
- package/Sources/RunicCore/Sync/SyncRecord.swift +0 -402
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -288
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -768
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -338
- package/Sources/RunicCore/Team/TeamModels.swift +0 -584
- package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
- package/Sources/RunicCore/Team/TeamStore.swift +0 -809
- 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 -441
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -502
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -161
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
- package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -675
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
- package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -284
- 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 -48
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -21
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -69
- package/Tests/RunicTests/CLICostTests.swift +0 -64
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -21
- package/Tests/RunicTests/CLISnapshotTests.swift +0 -71
- package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -57
- package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -163
- package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -29
- package/Tests/RunicTests/ClaudeUsageTests.swift +0 -369
- package/Tests/RunicTests/CodexOAuthTests.swift +0 -121
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -80
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -74
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -74
- package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -392
- package/Tests/RunicTests/CostUsagePricingTests.swift +0 -48
- package/Tests/RunicTests/CostUsageScannerTests.swift +0 -698
- package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -322
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -67
- package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -27
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -232
- package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -72
- package/Tests/RunicTests/InstallOriginTests.swift +0 -17
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
- package/Tests/RunicTests/LiveAccountTests.swift +0 -24
- package/Tests/RunicTests/LoadingPatternTests.swift +0 -34
- package/Tests/RunicTests/MenuCardModelTests.swift +0 -544
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -67
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -110
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -16
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -74
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -26
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -133
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -64
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -131
- package/Tests/RunicTests/PathBuilderTests.swift +0 -236
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -159
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -37
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -15
- package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -236
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -50
- package/Tests/RunicTests/RunicTests.swift +0 -119
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -35
- package/Tests/RunicTests/SettingsStoreTests.swift +0 -265
- package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -54
- package/Tests/RunicTests/StatusMenuTests.swift +0 -475
- package/Tests/RunicTests/StatusProbeTests.swift +0 -376
- package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -18
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -64
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -147
- package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -157
- package/Tests/RunicTests/TextParsingTests.swift +0 -11
- package/Tests/RunicTests/UsageFormatterTests.swift +0 -111
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -170
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -211
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -81
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -168
- package/Tests/RunicTests/UsagePaceTests.swift +0 -76
- package/Tests/RunicTests/UsagePaceTextTests.swift +0 -108
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -89
- package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -43
- package/Tests/RunicTests/ZaiProviderTests.swift +0 -59
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -9
- package/TestsLinux/PlatformGatingTests.swift +0 -46
- package/appcast.xml +0 -104
- 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/runic.svg +0 -15
- package/version.env +0 -2
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
import AppKit
|
|
2
|
-
import Observation
|
|
3
|
-
import QuartzCore
|
|
4
|
-
import RunicCore
|
|
5
|
-
import SwiftUI
|
|
6
|
-
|
|
7
|
-
// **StatusItemController** - Manages macOS menubar status item and dropdown menu
|
|
8
|
-
//
|
|
9
|
-
// **Purpose:**
|
|
10
|
-
// Coordinates the menubar icon, animations, menu construction, and user interactions.
|
|
11
|
-
// Acts as the bridge between AppKit (NSStatusItem) and SwiftUI (menu cards).
|
|
12
|
-
//
|
|
13
|
-
// **Responsibilities:**
|
|
14
|
-
// - Create and manage NSStatusItem in menubar
|
|
15
|
-
// - Coordinate icon rendering via IconRenderer
|
|
16
|
-
// - Build rich NSMenu with embedded SwiftUI views
|
|
17
|
-
// - Handle animation lifecycle (60 FPS during pings)
|
|
18
|
-
// - Schedule background "ping" operations when menu opens
|
|
19
|
-
// - Manage keyboard shortcuts and user actions
|
|
20
|
-
//
|
|
21
|
-
// **Architecture Pattern:**
|
|
22
|
-
// This file is split across 4 files via extensions:
|
|
23
|
-
// - `StatusItemController.swift` - Core setup (318 lines)
|
|
24
|
-
// - `StatusItemController+Menu.swift` - Menu construction (1878 lines)
|
|
25
|
-
// - `StatusItemController+Actions.swift` - User actions (200 lines)
|
|
26
|
-
// - `StatusItemController+Animation.swift` - Animation system (469 lines)
|
|
27
|
-
//
|
|
28
|
-
// **Performance:**
|
|
29
|
-
// - **60 FPS cap** during active pings (PerformanceConstants.menubarFPS)
|
|
30
|
-
// - **Auto-stop animations** when data loaded or error encountered
|
|
31
|
-
// - **1.2s delay** before background ping when menu opens (reduce perceived lag)
|
|
32
|
-
//
|
|
33
|
-
// **Dependencies:**
|
|
34
|
-
// - `UsageStore` - Data source
|
|
35
|
-
// - `SettingsStore` - User preferences
|
|
36
|
-
// - `IconRenderer` - Icon generation
|
|
37
|
-
// - `MenuCardView` - SwiftUI menu content
|
|
38
|
-
//
|
|
39
|
-
// **Usage:**
|
|
40
|
-
// ```swift
|
|
41
|
-
// let controller = StatusItemController(
|
|
42
|
-
// store: usageStore,
|
|
43
|
-
// settings: settingsStore,
|
|
44
|
-
// account: accountInfo,
|
|
45
|
-
// updater: updater,
|
|
46
|
-
// preferencesSelection: selection
|
|
47
|
-
// )
|
|
48
|
-
// ```
|
|
49
|
-
|
|
50
|
-
// MARK: - Status item controller (AppKit-hosted icons, SwiftUI popovers)
|
|
51
|
-
|
|
52
|
-
@MainActor
|
|
53
|
-
protocol StatusItemControlling: AnyObject {
|
|
54
|
-
func openMenuFromShortcut()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@MainActor
|
|
58
|
-
final class StatusItemController: NSObject, NSMenuDelegate, StatusItemControlling {
|
|
59
|
-
typealias Factory = (UsageStore, SettingsStore, AccountInfo, UpdaterProviding, PreferencesSelection)
|
|
60
|
-
-> StatusItemControlling
|
|
61
|
-
static let defaultFactory: Factory = { store, settings, account, updater, selection in
|
|
62
|
-
StatusItemController(
|
|
63
|
-
store: store,
|
|
64
|
-
settings: settings,
|
|
65
|
-
account: account,
|
|
66
|
-
updater: updater,
|
|
67
|
-
preferencesSelection: selection)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static var factory: Factory = StatusItemController.defaultFactory
|
|
71
|
-
|
|
72
|
-
let store: UsageStore
|
|
73
|
-
let settings: SettingsStore
|
|
74
|
-
let account: AccountInfo
|
|
75
|
-
let updater: UpdaterProviding
|
|
76
|
-
var statusItem: NSStatusItem
|
|
77
|
-
var statusItems: [UsageProvider: NSStatusItem] = [:]
|
|
78
|
-
var lastMenuProvider: UsageProvider?
|
|
79
|
-
var menuProviders: [ObjectIdentifier: UsageProvider] = [:]
|
|
80
|
-
var menuContentVersion: Int = 0
|
|
81
|
-
var menuVersions: [ObjectIdentifier: Int] = [:]
|
|
82
|
-
var mergedMenu: NSMenu?
|
|
83
|
-
var providerMenus: [UsageProvider: NSMenu] = [:]
|
|
84
|
-
var fallbackMenu: NSMenu?
|
|
85
|
-
var openMenus: [ObjectIdentifier: NSMenu] = [:]
|
|
86
|
-
var menuPingTasks: [ObjectIdentifier: Task<Void, Never>] = [:]
|
|
87
|
-
var blinkTask: Task<Void, Never>?
|
|
88
|
-
var loginTask: Task<Void, Never>? {
|
|
89
|
-
didSet { self.refreshMenusForLoginStateChange() }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
var creditsPurchaseWindow: OpenAICreditsPurchaseWindowController?
|
|
93
|
-
|
|
94
|
-
var activeLoginProvider: UsageProvider? {
|
|
95
|
-
didSet {
|
|
96
|
-
if oldValue != self.activeLoginProvider {
|
|
97
|
-
self.refreshMenusForLoginStateChange()
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
var blinkStates: [UsageProvider: BlinkState] = [:]
|
|
103
|
-
var blinkAmounts: [UsageProvider: CGFloat] = [:]
|
|
104
|
-
var wiggleAmounts: [UsageProvider: CGFloat] = [:]
|
|
105
|
-
var tiltAmounts: [UsageProvider: CGFloat] = [:]
|
|
106
|
-
var blinkForceUntil: Date?
|
|
107
|
-
var loginPhase: LoginPhase = .idle {
|
|
108
|
-
didSet {
|
|
109
|
-
if oldValue != self.loginPhase {
|
|
110
|
-
self.refreshMenusForLoginStateChange()
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
let preferencesSelection: PreferencesSelection
|
|
116
|
-
var animationDriver: DisplayLinkDriver?
|
|
117
|
-
var animationPhase: Double = 0
|
|
118
|
-
var animationPattern: LoadingPattern = .knightRider
|
|
119
|
-
let loginLogger = RunicLog.logger("login")
|
|
120
|
-
var selectedMenuProvider: UsageProvider? {
|
|
121
|
-
get { self.settings.selectedMenuProvider }
|
|
122
|
-
set { self.settings.selectedMenuProvider = newValue }
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
struct BlinkState {
|
|
126
|
-
var nextBlink: Date
|
|
127
|
-
var blinkStart: Date?
|
|
128
|
-
var pendingSecondStart: Date?
|
|
129
|
-
var effect: MotionEffect = .blink
|
|
130
|
-
|
|
131
|
-
static func randomDelay() -> TimeInterval {
|
|
132
|
-
Double.random(in: 3...12)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
enum MotionEffect {
|
|
137
|
-
case blink
|
|
138
|
-
case wiggle
|
|
139
|
-
case tilt
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
enum LoginPhase {
|
|
143
|
-
case idle
|
|
144
|
-
case requesting
|
|
145
|
-
case waitingBrowser
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
init(
|
|
149
|
-
store: UsageStore,
|
|
150
|
-
settings: SettingsStore,
|
|
151
|
-
account: AccountInfo,
|
|
152
|
-
updater: UpdaterProviding,
|
|
153
|
-
preferencesSelection: PreferencesSelection)
|
|
154
|
-
{
|
|
155
|
-
self.store = store
|
|
156
|
-
self.settings = settings
|
|
157
|
-
self.account = account
|
|
158
|
-
self.updater = updater
|
|
159
|
-
self.preferencesSelection = preferencesSelection
|
|
160
|
-
let bar = NSStatusBar.system
|
|
161
|
-
let item = bar.statusItem(withLength: NSStatusItem.variableLength)
|
|
162
|
-
// Ensure the icon is rendered at 1:1 without resampling (crisper edges for template images).
|
|
163
|
-
item.button?.imageScaling = .scaleNone
|
|
164
|
-
self.statusItem = item
|
|
165
|
-
for provider in UsageProvider.allCases {
|
|
166
|
-
let providerItem = bar.statusItem(withLength: NSStatusItem.variableLength)
|
|
167
|
-
// Ensure the icon is rendered at 1:1 without resampling (crisper edges for template images).
|
|
168
|
-
providerItem.button?.imageScaling = .scaleNone
|
|
169
|
-
self.statusItems[provider] = providerItem
|
|
170
|
-
}
|
|
171
|
-
super.init()
|
|
172
|
-
self.wireBindings()
|
|
173
|
-
self.updateIcons()
|
|
174
|
-
self.updateVisibility()
|
|
175
|
-
NotificationCenter.default.addObserver(
|
|
176
|
-
self,
|
|
177
|
-
selector: #selector(self.handleDebugReplayNotification(_:)),
|
|
178
|
-
name: .runicDebugReplayAllAnimations,
|
|
179
|
-
object: nil)
|
|
180
|
-
NotificationCenter.default.addObserver(
|
|
181
|
-
self,
|
|
182
|
-
selector: #selector(self.handleDebugBlinkNotification),
|
|
183
|
-
name: .runicDebugBlinkNow,
|
|
184
|
-
object: nil)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private func wireBindings() {
|
|
188
|
-
self.observeStoreChanges()
|
|
189
|
-
self.observeDebugForceAnimation()
|
|
190
|
-
self.observeSettingsChanges()
|
|
191
|
-
self.observeUpdaterChanges()
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private func observeStoreChanges() {
|
|
195
|
-
withObservationTracking {
|
|
196
|
-
_ = self.store.menuObservationToken
|
|
197
|
-
} onChange: { [weak self] in
|
|
198
|
-
Task { @MainActor [weak self] in
|
|
199
|
-
guard let self else { return }
|
|
200
|
-
self.observeStoreChanges()
|
|
201
|
-
self.invalidateMenus()
|
|
202
|
-
self.updateIcons()
|
|
203
|
-
self.updateBlinkingState()
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
private func observeDebugForceAnimation() {
|
|
209
|
-
withObservationTracking {
|
|
210
|
-
_ = self.store.debugForceAnimation
|
|
211
|
-
} onChange: { [weak self] in
|
|
212
|
-
Task { @MainActor [weak self] in
|
|
213
|
-
guard let self else { return }
|
|
214
|
-
self.observeDebugForceAnimation()
|
|
215
|
-
self.updateVisibility()
|
|
216
|
-
self.updateBlinkingState()
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private func observeSettingsChanges() {
|
|
222
|
-
withObservationTracking {
|
|
223
|
-
_ = self.settings.menuObservationToken
|
|
224
|
-
} onChange: { [weak self] in
|
|
225
|
-
Task { @MainActor [weak self] in
|
|
226
|
-
guard let self else { return }
|
|
227
|
-
self.observeSettingsChanges()
|
|
228
|
-
self.invalidateMenus()
|
|
229
|
-
self.updateVisibility()
|
|
230
|
-
self.updateIcons()
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
private func observeUpdaterChanges() {
|
|
236
|
-
withObservationTracking {
|
|
237
|
-
_ = self.updater.updateStatus.isUpdateReady
|
|
238
|
-
} onChange: { [weak self] in
|
|
239
|
-
Task { @MainActor [weak self] in
|
|
240
|
-
guard let self else { return }
|
|
241
|
-
self.observeUpdaterChanges()
|
|
242
|
-
self.invalidateMenus()
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private func invalidateMenus() {
|
|
248
|
-
self.menuContentVersion &+= 1
|
|
249
|
-
self.refreshOpenMenusIfNeeded()
|
|
250
|
-
Task { @MainActor in
|
|
251
|
-
// AppKit can ignore menu mutations while tracking; retry on the next run loop.
|
|
252
|
-
await Task.yield()
|
|
253
|
-
self.refreshOpenMenusIfNeeded()
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
private func updateIcons() {
|
|
258
|
-
if self.shouldMergeIcons {
|
|
259
|
-
self.applyIcon(phase: nil)
|
|
260
|
-
self.attachMenus()
|
|
261
|
-
} else {
|
|
262
|
-
UsageProvider.allCases.forEach { self.applyIcon(for: $0, phase: nil) }
|
|
263
|
-
self.attachMenus(fallback: self.fallbackProvider)
|
|
264
|
-
}
|
|
265
|
-
self.updateAnimationState()
|
|
266
|
-
self.updateBlinkingState()
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private func updateVisibility() {
|
|
270
|
-
let anyEnabled = !self.store.enabledProviders().isEmpty
|
|
271
|
-
let force = self.store.debugForceAnimation
|
|
272
|
-
if self.shouldMergeIcons {
|
|
273
|
-
self.statusItem.isVisible = anyEnabled || force
|
|
274
|
-
for item in self.statusItems.values {
|
|
275
|
-
item.isVisible = false
|
|
276
|
-
}
|
|
277
|
-
self.attachMenus()
|
|
278
|
-
} else {
|
|
279
|
-
self.statusItem.isVisible = false
|
|
280
|
-
let fallback = self.fallbackProvider
|
|
281
|
-
for provider in UsageProvider.allCases {
|
|
282
|
-
let item = self.statusItems[provider]
|
|
283
|
-
let isEnabled = self.isEnabled(provider)
|
|
284
|
-
item?.isVisible = isEnabled || fallback == provider || force
|
|
285
|
-
}
|
|
286
|
-
self.attachMenus(fallback: fallback)
|
|
287
|
-
}
|
|
288
|
-
self.updateAnimationState()
|
|
289
|
-
self.updateBlinkingState()
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
var fallbackProvider: UsageProvider? {
|
|
293
|
-
self.store.enabledProviders().isEmpty ? .codex : nil
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
func isEnabled(_ provider: UsageProvider) -> Bool {
|
|
297
|
-
self.store.isEnabled(provider)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private func refreshMenusForLoginStateChange() {
|
|
301
|
-
self.invalidateMenus()
|
|
302
|
-
if self.shouldMergeIcons {
|
|
303
|
-
self.attachMenus()
|
|
304
|
-
} else {
|
|
305
|
-
self.attachMenus(fallback: self.fallbackProvider)
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private func attachMenus() {
|
|
310
|
-
if self.mergedMenu == nil {
|
|
311
|
-
self.mergedMenu = self.makeMenu()
|
|
312
|
-
}
|
|
313
|
-
self.statusItem.menu = self.mergedMenu
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private func attachMenus(fallback: UsageProvider? = nil) {
|
|
317
|
-
for provider in UsageProvider.allCases {
|
|
318
|
-
guard let item = self.statusItems[provider] else { continue }
|
|
319
|
-
if self.isEnabled(provider) {
|
|
320
|
-
if self.providerMenus[provider] == nil {
|
|
321
|
-
self.providerMenus[provider] = self.makeMenu(for: provider)
|
|
322
|
-
}
|
|
323
|
-
item.menu = self.providerMenus[provider]
|
|
324
|
-
} else if fallback == provider {
|
|
325
|
-
if self.fallbackMenu == nil {
|
|
326
|
-
self.fallbackMenu = self.makeMenu(for: nil)
|
|
327
|
-
}
|
|
328
|
-
item.menu = self.fallbackMenu
|
|
329
|
-
} else {
|
|
330
|
-
item.menu = nil
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
func isVisible(_ provider: UsageProvider) -> Bool {
|
|
336
|
-
self.store.debugForceAnimation || self.isEnabled(provider) || self.fallbackProvider == provider
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
var shouldMergeIcons: Bool {
|
|
340
|
-
self.settings.mergeIcons && self.store.enabledProviders().count > 1
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
func switchAccountSubtitle(for target: UsageProvider) -> String? {
|
|
344
|
-
guard self.loginTask != nil, let provider = self.activeLoginProvider, provider == target else { return nil }
|
|
345
|
-
let base: String
|
|
346
|
-
switch self.loginPhase {
|
|
347
|
-
case .idle: return nil
|
|
348
|
-
case .requesting: base = "Requesting login…"
|
|
349
|
-
case .waitingBrowser: base = "Waiting in browser…"
|
|
350
|
-
}
|
|
351
|
-
let prefix = ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName
|
|
352
|
-
return "\(prefix): \(base)"
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
deinit {
|
|
356
|
-
self.blinkTask?.cancel()
|
|
357
|
-
self.loginTask?.cancel()
|
|
358
|
-
NotificationCenter.default.removeObserver(self)
|
|
359
|
-
}
|
|
360
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import RunicCore
|
|
3
|
-
|
|
4
|
-
/// Monitors spend forecasts and posts macOS notifications when projected costs breach budget limits.
|
|
5
|
-
@MainActor
|
|
6
|
-
final class BudgetNotificationManager {
|
|
7
|
-
static let shared = BudgetNotificationManager()
|
|
8
|
-
|
|
9
|
-
private let logger = RunicLog.logger("budget-notifications")
|
|
10
|
-
|
|
11
|
-
/// UserDefaults key prefix for tracking which breaches have already been notified.
|
|
12
|
-
private static let notifiedKeyPrefix = "budgetBreachNotified-"
|
|
13
|
-
|
|
14
|
-
/// The calendar month key used to scope notifications (e.g. "2026-03").
|
|
15
|
-
/// Notifications reset each month so users are re-notified in new billing cycles.
|
|
16
|
-
private var currentMonthKey: String {
|
|
17
|
-
let formatter = DateFormatter()
|
|
18
|
-
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
19
|
-
formatter.timeZone = TimeZone.current
|
|
20
|
-
formatter.dateFormat = "yyyy-MM"
|
|
21
|
-
return formatter.string(from: Date())
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private init() {}
|
|
25
|
-
|
|
26
|
-
/// Check all spend forecasts and post notifications for newly detected breaches.
|
|
27
|
-
///
|
|
28
|
-
/// - Parameters:
|
|
29
|
-
/// - forecasts: Per-provider arrays of project-level spend forecasts.
|
|
30
|
-
/// - settings: The settings store, used to check if budget notifications are enabled.
|
|
31
|
-
func checkAndNotify(
|
|
32
|
-
forecasts: [UsageProvider: [UsageLedgerSpendForecast]],
|
|
33
|
-
settings: SettingsStore)
|
|
34
|
-
{
|
|
35
|
-
guard settings.budgetNotificationsEnabled else { return }
|
|
36
|
-
|
|
37
|
-
let monthKey = self.currentMonthKey
|
|
38
|
-
for (provider, providerForecasts) in forecasts {
|
|
39
|
-
for forecast in providerForecasts {
|
|
40
|
-
guard forecast.budgetWillBreach else { continue }
|
|
41
|
-
guard let budgetLimit = forecast.budgetLimitUSD, budgetLimit > 0 else { continue }
|
|
42
|
-
|
|
43
|
-
let projectKey = forecast.projectKey ?? forecast.projectID ?? "global"
|
|
44
|
-
let notificationKey = Self.notifiedKeyPrefix + "\(monthKey)-\(provider.rawValue)-\(projectKey)"
|
|
45
|
-
|
|
46
|
-
// Skip if already notified this month for this project
|
|
47
|
-
if UserDefaults.standard.bool(forKey: notificationKey) {
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let projectName = forecast.projectName
|
|
52
|
-
?? forecast.projectID
|
|
53
|
-
?? ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName
|
|
54
|
-
|
|
55
|
-
let overshoot = forecast.projected30DayCostUSD - budgetLimit
|
|
56
|
-
let limitStr = UsageFormatter.usdString(budgetLimit)
|
|
57
|
-
let overshootStr = UsageFormatter.usdString(max(0, overshoot))
|
|
58
|
-
|
|
59
|
-
let title = "Budget Alert: \(projectName)"
|
|
60
|
-
let body = "Projected to exceed \(limitStr) limit by \(overshootStr) this month"
|
|
61
|
-
|
|
62
|
-
self.logger.info(
|
|
63
|
-
"posting budget breach notification",
|
|
64
|
-
metadata: [
|
|
65
|
-
"provider": provider.rawValue,
|
|
66
|
-
"project": projectKey,
|
|
67
|
-
"limit": limitStr,
|
|
68
|
-
"projected": UsageFormatter.usdString(forecast.projected30DayCostUSD),
|
|
69
|
-
])
|
|
70
|
-
|
|
71
|
-
AppNotifications.shared.post(
|
|
72
|
-
idPrefix: "budget-\(provider.rawValue)-\(projectKey)",
|
|
73
|
-
title: title,
|
|
74
|
-
body: body)
|
|
75
|
-
|
|
76
|
-
// Mark as notified for this month
|
|
77
|
-
UserDefaults.standard.set(true, forKey: notificationKey)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import Darwin
|
|
2
|
-
import Foundation
|
|
3
|
-
import RunicCore
|
|
4
|
-
|
|
5
|
-
struct ClaudeLoginRunner {
|
|
6
|
-
enum Phase {
|
|
7
|
-
case requesting
|
|
8
|
-
case waitingBrowser
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
struct Result {
|
|
12
|
-
enum Outcome {
|
|
13
|
-
case success
|
|
14
|
-
case timedOut
|
|
15
|
-
case failed(status: Int32)
|
|
16
|
-
case missingBinary
|
|
17
|
-
case launchFailed(String)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let outcome: Outcome
|
|
21
|
-
let output: String
|
|
22
|
-
let authLink: String?
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static func run(timeout: TimeInterval = 120, onPhaseChange: @escaping @Sendable (Phase) -> Void) async -> Result {
|
|
26
|
-
await Task(priority: .userInitiated) {
|
|
27
|
-
onPhaseChange(.requesting)
|
|
28
|
-
do {
|
|
29
|
-
let runResult = try self.runPTY(timeout: timeout, onPhaseChange: onPhaseChange)
|
|
30
|
-
let link = self.firstLink(in: runResult.output)
|
|
31
|
-
if let link {
|
|
32
|
-
return Result(outcome: .success, output: runResult.output, authLink: link)
|
|
33
|
-
}
|
|
34
|
-
return Result(outcome: .timedOut, output: runResult.output, authLink: nil)
|
|
35
|
-
} catch LoginError.binaryNotFound {
|
|
36
|
-
return Result(outcome: .missingBinary, output: "", authLink: nil)
|
|
37
|
-
} catch let LoginError.timedOut(text) {
|
|
38
|
-
return Result(outcome: .timedOut, output: text, authLink: self.firstLink(in: text))
|
|
39
|
-
} catch let LoginError.failed(status, text) {
|
|
40
|
-
return Result(outcome: .failed(status: status), output: text, authLink: self.firstLink(in: text))
|
|
41
|
-
} catch {
|
|
42
|
-
return Result(outcome: .launchFailed(error.localizedDescription), output: "", authLink: nil)
|
|
43
|
-
}
|
|
44
|
-
}.value
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// MARK: - PTY runner
|
|
48
|
-
|
|
49
|
-
private enum LoginError: Error {
|
|
50
|
-
case binaryNotFound
|
|
51
|
-
case timedOut(text: String)
|
|
52
|
-
case failed(status: Int32, text: String)
|
|
53
|
-
case launchFailed(String)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private struct PTYRunResult {
|
|
57
|
-
let output: String
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private static func runPTY(
|
|
61
|
-
timeout: TimeInterval,
|
|
62
|
-
onPhaseChange: @escaping @Sendable (Phase) -> Void) throws -> PTYRunResult
|
|
63
|
-
{
|
|
64
|
-
let runner = TTYCommandRunner()
|
|
65
|
-
var options = TTYCommandRunner.Options(rows: 50, cols: 160, timeout: timeout)
|
|
66
|
-
options.extraArgs = ["/login"]
|
|
67
|
-
options.stopOnURL = false // keep running until CLI confirms
|
|
68
|
-
options.stopOnSubstrings = ["Successfully logged in", "Login successful", "Logged in successfully"]
|
|
69
|
-
options.sendEnterEvery = 1.0
|
|
70
|
-
options.settleAfterStop = 0.35
|
|
71
|
-
do {
|
|
72
|
-
let result = try runner.run(
|
|
73
|
-
binary: "claude",
|
|
74
|
-
send: "",
|
|
75
|
-
options: options,
|
|
76
|
-
onURLDetected: { onPhaseChange(.waitingBrowser) })
|
|
77
|
-
return PTYRunResult(output: result.text)
|
|
78
|
-
} catch TTYCommandRunner.Error.binaryNotFound {
|
|
79
|
-
throw LoginError.binaryNotFound
|
|
80
|
-
} catch TTYCommandRunner.Error.timedOut {
|
|
81
|
-
throw LoginError.timedOut(text: "")
|
|
82
|
-
} catch let TTYCommandRunner.Error.launchFailed(msg) {
|
|
83
|
-
throw LoginError.launchFailed(msg)
|
|
84
|
-
} catch {
|
|
85
|
-
throw LoginError.launchFailed(error.localizedDescription)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private static func firstLink(in text: String) -> String? {
|
|
90
|
-
let pattern = #"https?://[A-Za-z0-9._~:/?#\[\]@!$&'()*+,;=%-]+"#
|
|
91
|
-
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
|
|
92
|
-
let nsRange = NSRange(text.startIndex..<text.endIndex, in: text)
|
|
93
|
-
guard let match = regex.firstMatch(in: text, options: [], range: nsRange),
|
|
94
|
-
let range = Range(match.range, in: text) else { return nil }
|
|
95
|
-
var url = String(text[range])
|
|
96
|
-
while let last = url.unicodeScalars.last,
|
|
97
|
-
CharacterSet(charactersIn: ".,;:)]}>\"'").contains(last)
|
|
98
|
-
{
|
|
99
|
-
url.unicodeScalars.removeLast()
|
|
100
|
-
}
|
|
101
|
-
return url
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import Darwin
|
|
2
|
-
import Foundation
|
|
3
|
-
import RunicCore
|
|
4
|
-
|
|
5
|
-
struct CodexLoginRunner {
|
|
6
|
-
struct Result {
|
|
7
|
-
enum Outcome {
|
|
8
|
-
case success
|
|
9
|
-
case timedOut
|
|
10
|
-
case failed(status: Int32)
|
|
11
|
-
case missingBinary
|
|
12
|
-
case launchFailed(String)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let outcome: Outcome
|
|
16
|
-
let output: String
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static func run(timeout: TimeInterval = 120) async -> Result {
|
|
20
|
-
await Task(priority: .userInitiated) {
|
|
21
|
-
var env = ProcessInfo.processInfo.environment
|
|
22
|
-
env["PATH"] = PathBuilder.effectivePATH(
|
|
23
|
-
purposes: [.rpc, .tty, .nodeTooling],
|
|
24
|
-
env: env,
|
|
25
|
-
loginPATH: LoginShellPathCache.shared.current)
|
|
26
|
-
|
|
27
|
-
guard let executable = BinaryLocator.resolveCodexBinary(
|
|
28
|
-
env: env,
|
|
29
|
-
loginPATH: LoginShellPathCache.shared.current)
|
|
30
|
-
else {
|
|
31
|
-
return Result(outcome: .missingBinary, output: "")
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let process = Process()
|
|
35
|
-
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
|
36
|
-
process.arguments = [executable, "login"]
|
|
37
|
-
process.environment = env
|
|
38
|
-
|
|
39
|
-
let stdout = Pipe()
|
|
40
|
-
let stderr = Pipe()
|
|
41
|
-
process.standardOutput = stdout
|
|
42
|
-
process.standardError = stderr
|
|
43
|
-
|
|
44
|
-
var processGroup: pid_t?
|
|
45
|
-
do {
|
|
46
|
-
try process.run()
|
|
47
|
-
processGroup = self.attachProcessGroup(process)
|
|
48
|
-
} catch {
|
|
49
|
-
return Result(outcome: .launchFailed(error.localizedDescription), output: "")
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let timedOut = await self.wait(for: process, timeout: timeout)
|
|
53
|
-
if timedOut {
|
|
54
|
-
self.terminate(process, processGroup: processGroup)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let output = await self.combinedOutput(stdout: stdout, stderr: stderr)
|
|
58
|
-
if timedOut {
|
|
59
|
-
return Result(outcome: .timedOut, output: output)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let status = process.terminationStatus
|
|
63
|
-
if status == 0 {
|
|
64
|
-
return Result(outcome: .success, output: output)
|
|
65
|
-
}
|
|
66
|
-
return Result(outcome: .failed(status: status), output: output)
|
|
67
|
-
}.value
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private static func wait(for process: Process, timeout: TimeInterval) async -> Bool {
|
|
71
|
-
await withTaskGroup(of: Bool.self) { group -> Bool in
|
|
72
|
-
group.addTask {
|
|
73
|
-
process.waitUntilExit()
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
group.addTask {
|
|
77
|
-
let nanos = UInt64(max(0, timeout) * 1_000_000_000)
|
|
78
|
-
try? await Task.sleep(nanoseconds: nanos)
|
|
79
|
-
return true
|
|
80
|
-
}
|
|
81
|
-
let result = await group.next() ?? false
|
|
82
|
-
group.cancelAll()
|
|
83
|
-
return result
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private static func terminate(_ process: Process, processGroup: pid_t?) {
|
|
88
|
-
if let pgid = processGroup {
|
|
89
|
-
kill(-pgid, SIGTERM)
|
|
90
|
-
}
|
|
91
|
-
if process.isRunning {
|
|
92
|
-
process.terminate()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let deadline = Date().addingTimeInterval(2.0)
|
|
96
|
-
while process.isRunning, Date() < deadline {
|
|
97
|
-
usleep(100_000)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if process.isRunning {
|
|
101
|
-
if let pgid = processGroup {
|
|
102
|
-
kill(-pgid, SIGKILL)
|
|
103
|
-
}
|
|
104
|
-
kill(process.processIdentifier, SIGKILL)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private static func attachProcessGroup(_ process: Process) -> pid_t? {
|
|
109
|
-
let pid = process.processIdentifier
|
|
110
|
-
return setpgid(pid, pid) == 0 ? pid : nil
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private static func combinedOutput(stdout: Pipe, stderr: Pipe) async -> String {
|
|
114
|
-
async let out = self.readToEnd(stdout)
|
|
115
|
-
async let err = self.readToEnd(stderr)
|
|
116
|
-
let stdoutText = await out
|
|
117
|
-
let stderrText = await err
|
|
118
|
-
|
|
119
|
-
let merged: String = if !stdoutText.isEmpty, !stderrText.isEmpty {
|
|
120
|
-
[stdoutText, stderrText].joined(separator: "\n")
|
|
121
|
-
} else {
|
|
122
|
-
stdoutText + stderrText
|
|
123
|
-
}
|
|
124
|
-
let trimmed = merged.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
125
|
-
let limited = trimmed.prefix(4000)
|
|
126
|
-
return limited.isEmpty ? "No output captured." : String(limited)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private static func readToEnd(_ pipe: Pipe, timeout: TimeInterval = 3.0) async -> String {
|
|
130
|
-
await withTaskGroup(of: String?.self) { group -> String in
|
|
131
|
-
group.addTask {
|
|
132
|
-
if #available(macOS 13.0, *) {
|
|
133
|
-
if let data = try? pipe.fileHandleForReading.readToEnd() { return self.decode(data) }
|
|
134
|
-
}
|
|
135
|
-
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
|
136
|
-
return Self.decode(data)
|
|
137
|
-
}
|
|
138
|
-
group.addTask {
|
|
139
|
-
let nanos = UInt64(max(0, timeout) * 1_000_000_000)
|
|
140
|
-
try? await Task.sleep(nanoseconds: nanos)
|
|
141
|
-
return nil
|
|
142
|
-
}
|
|
143
|
-
let result = await group.next()
|
|
144
|
-
group.cancelAll()
|
|
145
|
-
if let result, let text = result { return text }
|
|
146
|
-
return ""
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private static func decode(_ data: Data) -> String {
|
|
151
|
-
guard let text = String(data: data, encoding: .utf8) else { return "" }
|
|
152
|
-
return text
|
|
153
|
-
}
|
|
154
|
-
}
|