@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.
Files changed (586) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/LICENSE +373 -21
  3. package/README.md +98 -84
  4. package/THIRD-PARTY-NOTICES.md +651 -0
  5. package/package.json +14 -4
  6. package/.swiftformat +0 -50
  7. package/.swiftlint.yml +0 -160
  8. package/CONTRIBUTING.md +0 -456
  9. package/Icon.icns +0 -0
  10. package/Icon.icon/Assets/runic.png +0 -0
  11. package/Icon.icon/icon.json +0 -36
  12. package/Package.resolved +0 -51
  13. package/Package.swift +0 -136
  14. package/Packages/Helix/LICENSE +0 -21
  15. package/Packages/Helix/Package.swift +0 -29
  16. package/Packages/Helix/README.md +0 -976
  17. package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
  18. package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
  19. package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
  20. package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
  21. package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
  22. package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
  23. package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
  24. package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
  25. package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
  26. package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
  27. package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
  28. package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
  29. package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
  30. package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
  31. package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
  32. package/Packages/Helix/Sources/Helix/Program.swift +0 -204
  33. package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
  34. package/Packages/Silo/LICENSE +0 -21
  35. package/Packages/Silo/Package.swift +0 -34
  36. package/Packages/Silo/README.md +0 -466
  37. package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
  38. package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
  39. package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
  40. package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
  41. package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
  42. package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
  43. package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
  44. package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
  45. package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
  46. package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
  47. package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
  48. package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
  49. package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
  50. package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
  51. package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
  52. package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
  53. package/RuniciOS/App/RuniciOSApp.swift +0 -105
  54. package/RuniciOS/Core/iOSUsageStore.swift +0 -192
  55. package/RuniciOS/Views/ProviderDetailView.swift +0 -312
  56. package/RuniciOS/Views/ProviderListView.swift +0 -228
  57. package/RuniciOS/Widgets/LargeWidget.swift +0 -367
  58. package/RuniciOS/Widgets/LiveActivity.swift +0 -342
  59. package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
  60. package/RuniciOS/Widgets/MediumWidget.swift +0 -370
  61. package/RuniciOS/Widgets/RunicWidget.swift +0 -274
  62. package/RuniciOS/Widgets/SmallWidget.swift +0 -281
  63. package/RuniciOS/Widgets/WidgetModels.swift +0 -285
  64. package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
  65. package/Scripts/CREATION-SUMMARY.md +0 -367
  66. package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
  67. package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
  68. package/Scripts/INDEX.md +0 -365
  69. package/Scripts/QUICK-REFERENCE.md +0 -164
  70. package/Scripts/README-SCRIPTS.md +0 -926
  71. package/Scripts/build-android.sh +0 -526
  72. package/Scripts/build-ios.sh +0 -529
  73. package/Scripts/build-macos.sh +0 -603
  74. package/Scripts/build-windows.sh +0 -504
  75. package/Scripts/build_icon.sh +0 -49
  76. package/Scripts/changelog-to-html.sh +0 -89
  77. package/Scripts/check-release-assets.sh +0 -10
  78. package/Scripts/ci-config.yml +0 -474
  79. package/Scripts/compile_and_run.sh +0 -219
  80. package/Scripts/compile_and_run_adhoc.sh +0 -4
  81. package/Scripts/docs-list.mjs +0 -119
  82. package/Scripts/make_appcast.sh +0 -58
  83. package/Scripts/package_app.sh +0 -409
  84. package/Scripts/release-lib.sh +0 -214
  85. package/Scripts/release.sh +0 -76
  86. package/Scripts/setup-all.sh +0 -387
  87. package/Scripts/setup-ios.sh +0 -404
  88. package/Scripts/setup-react-native.sh +0 -523
  89. package/Scripts/sign-and-notarize.sh +0 -122
  90. package/Scripts/test_live_update.sh +0 -38
  91. package/Scripts/validate_changelog.sh +0 -23
  92. package/Scripts/verify_appcast.sh +0 -92
  93. package/Sources/Runic/About.swift +0 -93
  94. package/Sources/Runic/AppNotifications.swift +0 -115
  95. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -666
  96. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -518
  97. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -810
  98. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -87
  99. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1407
  100. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -337
  101. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -395
  102. package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
  103. package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -80
  104. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
  105. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
  106. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
  107. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
  108. package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1216
  109. package/Sources/Runic/Core/Rendering/IconView.swift +0 -120
  110. package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
  111. package/Sources/Runic/Core/RunicAnimation.swift +0 -53
  112. package/Sources/Runic/Core/RunicColors.swift +0 -73
  113. package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
  114. package/Sources/Runic/Core/RunicSpacing.swift +0 -68
  115. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -119
  116. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -119
  117. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -119
  118. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -119
  119. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -119
  120. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
  121. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -119
  122. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -119
  123. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -119
  124. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -119
  125. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -119
  126. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -119
  127. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -119
  128. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -119
  129. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -119
  130. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -119
  131. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
  132. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -119
  133. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -119
  134. package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1711
  135. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -119
  136. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -20
  137. package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
  138. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
  139. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
  140. package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3605
  141. package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -119
  142. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -118
  143. package/Sources/Runic/Core/UsageExporter.swift +0 -193
  144. package/Sources/Runic/Date+RelativeDescription.swift +0 -21
  145. package/Sources/Runic/HiddenWindowView.swift +0 -32
  146. package/Sources/Runic/InstallOrigin.swift +0 -9
  147. package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
  148. package/Sources/Runic/MouseLocationReader.swift +0 -65
  149. package/Sources/Runic/Notifications+Runic.swift +0 -6
  150. package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
  151. package/Sources/Runic/ProviderRegistry.swift +0 -100
  152. package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
  153. package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
  154. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
  155. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
  156. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
  157. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
  158. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
  159. package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
  160. package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
  161. package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
  162. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
  163. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
  164. package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
  165. package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
  166. package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
  167. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
  168. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
  169. package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
  170. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
  171. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
  172. package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
  173. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
  174. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
  175. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
  176. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
  177. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
  178. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
  179. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
  180. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
  181. package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
  182. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -48
  183. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
  184. package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
  185. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
  186. package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
  187. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
  188. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
  189. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
  190. package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
  191. package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
  192. package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
  193. package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
  194. package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
  195. package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
  196. package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
  197. package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
  198. package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
  199. package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
  200. package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
  201. package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
  202. package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
  203. package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
  204. package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
  205. package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
  206. package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
  207. package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
  208. package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
  209. package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
  210. package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
  211. package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
  212. package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
  213. package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
  214. package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
  215. package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
  216. package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
  217. package/Sources/Runic/Resources/provider-context-windows.json +0 -20
  218. package/Sources/Runic/RunicApp.swift +0 -357
  219. package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
  220. package/Sources/Runic/UsagePaceText.swift +0 -52
  221. package/Sources/Runic/UsageProgressBar.swift +0 -224
  222. package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
  223. package/Sources/Runic/Utilities/LoadingPattern.swift +0 -60
  224. package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
  225. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -256
  226. package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
  227. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
  228. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
  229. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
  230. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -312
  231. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -228
  232. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
  233. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -155
  234. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -64
  235. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -268
  236. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
  237. package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1900
  238. package/Sources/Runic/Views/Menu/MenuContent.swift +0 -150
  239. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -369
  240. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -42
  241. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -235
  242. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -278
  243. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -209
  244. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
  245. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -88
  246. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -201
  247. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
  248. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -232
  249. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -364
  250. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -190
  251. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
  252. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -618
  253. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -305
  254. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
  255. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
  256. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -422
  257. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -641
  258. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
  259. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
  260. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -322
  261. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
  262. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -231
  263. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
  264. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -567
  265. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -288
  266. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3435
  267. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -25
  268. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -59
  269. package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -206
  270. package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
  271. package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -371
  272. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
  273. package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -761
  274. package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
  275. package/Sources/RunicCLI/CLIEntry.swift +0 -942
  276. package/Sources/RunicCLI/CLIRenderer.swift +0 -87
  277. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
  278. package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -362
  279. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -439
  280. package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -429
  281. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -168
  282. package/Sources/RunicClaudeWatchdog/main.swift +0 -122
  283. package/Sources/RunicClaudeWebProbe/main.swift +0 -62
  284. package/Sources/RunicCore/AlertRuleStore.swift +0 -187
  285. package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
  286. package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
  287. package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
  288. package/Sources/RunicCore/CostUsageModels.swift +0 -479
  289. package/Sources/RunicCore/CreditsModels.swift +0 -50
  290. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -269
  291. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -633
  292. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -500
  293. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
  294. package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
  295. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
  296. package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
  297. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
  298. package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
  299. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -371
  300. package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
  301. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
  302. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
  303. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
  304. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
  305. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
  306. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
  307. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
  308. package/Sources/RunicCore/PathEnvironment.swift +0 -482
  309. package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
  310. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -409
  311. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
  312. package/Sources/RunicCore/ProjectBudgetStore.swift +0 -99
  313. package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
  314. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -66
  315. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
  316. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
  317. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
  318. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
  319. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
  320. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
  321. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -168
  322. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
  323. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
  324. package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
  325. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
  326. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
  327. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -207
  328. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
  329. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -28
  330. package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
  331. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
  332. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
  333. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
  334. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
  335. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -243
  336. package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
  337. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -24
  338. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -181
  339. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
  340. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
  341. package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
  342. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -114
  343. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
  344. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -291
  345. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -386
  346. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -65
  347. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
  348. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
  349. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
  350. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
  351. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -65
  352. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
  353. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
  354. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
  355. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -65
  356. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -836
  357. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
  358. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
  359. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
  360. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
  361. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -92
  362. package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
  363. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
  364. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
  365. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
  366. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
  367. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
  368. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -203
  369. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
  370. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
  371. package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
  372. package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
  373. package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
  374. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
  375. package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
  376. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
  377. package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
  378. package/Sources/RunicCore/Providers/Providers.swift +0 -184
  379. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -84
  380. package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
  381. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -315
  382. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
  383. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
  384. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
  385. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
  386. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
  387. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
  388. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
  389. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
  390. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
  391. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -72
  392. package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
  393. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -718
  394. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -385
  395. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -310
  396. package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -288
  397. package/Sources/RunicCore/Sync/SyncRecord.swift +0 -405
  398. package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -294
  399. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -775
  400. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -333
  401. package/Sources/RunicCore/Team/TeamModels.swift +0 -583
  402. package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
  403. package/Sources/RunicCore/Team/TeamStore.swift +0 -792
  404. package/Sources/RunicCore/TextParsing.swift +0 -55
  405. package/Sources/RunicCore/UsageFetcher.swift +0 -694
  406. package/Sources/RunicCore/UsageFormatter.swift +0 -410
  407. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -427
  408. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -494
  409. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -158
  410. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
  411. package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
  412. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -660
  413. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
  414. package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
  415. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -245
  416. package/Sources/RunicCore/UsagePace.swift +0 -81
  417. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
  418. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
  419. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
  420. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
  421. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
  422. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
  423. package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
  424. package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
  425. package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
  426. package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
  427. package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
  428. package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
  429. package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
  430. package/Tests/RunicTests/AppDelegateTests.swift +0 -43
  431. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -22
  432. package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -70
  433. package/Tests/RunicTests/CLICostTests.swift +0 -65
  434. package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -22
  435. package/Tests/RunicTests/CLISnapshotTests.swift +0 -72
  436. package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -58
  437. package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -164
  438. package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -30
  439. package/Tests/RunicTests/ClaudeUsageTests.swift +0 -370
  440. package/Tests/RunicTests/CodexOAuthTests.swift +0 -122
  441. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -82
  442. package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -75
  443. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -75
  444. package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -394
  445. package/Tests/RunicTests/CostUsagePricingTests.swift +0 -50
  446. package/Tests/RunicTests/CostUsageScannerTests.swift +0 -700
  447. package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -323
  448. package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -69
  449. package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
  450. package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -28
  451. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
  452. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
  453. package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -233
  454. package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
  455. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -73
  456. package/Tests/RunicTests/InstallOriginTests.swift +0 -18
  457. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
  458. package/Tests/RunicTests/LiveAccountTests.swift +0 -24
  459. package/Tests/RunicTests/LoadingPatternTests.swift +0 -35
  460. package/Tests/RunicTests/MenuCardModelTests.swift +0 -545
  461. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -8
  462. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -8
  463. package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -68
  464. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -8
  465. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -112
  466. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -17
  467. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -75
  468. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -27
  469. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -134
  470. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -53
  471. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -92
  472. package/Tests/RunicTests/PathBuilderTests.swift +0 -237
  473. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -160
  474. package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -38
  475. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -16
  476. package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
  477. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -202
  478. package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -51
  479. package/Tests/RunicTests/RunicTests.swift +0 -120
  480. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -36
  481. package/Tests/RunicTests/SettingsStoreTests.swift +0 -161
  482. package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -49
  483. package/Tests/RunicTests/StatusMenuTests.swift +0 -434
  484. package/Tests/RunicTests/StatusProbeTests.swift +0 -377
  485. package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -20
  486. package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -65
  487. package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -148
  488. package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
  489. package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -159
  490. package/Tests/RunicTests/TextParsingTests.swift +0 -12
  491. package/Tests/RunicTests/UsageFormatterTests.swift +0 -112
  492. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -172
  493. package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -213
  494. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -83
  495. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -170
  496. package/Tests/RunicTests/UsagePaceTests.swift +0 -77
  497. package/Tests/RunicTests/UsagePaceTextTests.swift +0 -109
  498. package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -91
  499. package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -45
  500. package/Tests/RunicTests/ZaiProviderTests.swift +0 -61
  501. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -6
  502. package/TestsLinux/PlatformGatingTests.swift +0 -46
  503. package/appcast.xml +0 -144
  504. package/assets/logo-concept-stack.svg +0 -24
  505. package/assets/logo-minimal-bars.svg +0 -15
  506. package/assets/logo.svg +0 -20
  507. package/assets/logo2.svg +0 -43
  508. package/bin/docs-list +0 -5
  509. package/build-and-run.sh +0 -369
  510. package/docs/.nojekyll +0 -1
  511. package/docs/CNAME +0 -1
  512. package/docs/antigravity.md +0 -75
  513. package/docs/architecture.md +0 -32
  514. package/docs/claude.md +0 -93
  515. package/docs/cli-commands/integration_guide.md +0 -371
  516. package/docs/cli-commands/quick_reference.md +0 -362
  517. package/docs/cli-commands/readme.md +0 -361
  518. package/docs/cli.md +0 -156
  519. package/docs/cloudkit_sync_guide.md +0 -495
  520. package/docs/codebase_review.md +0 -288
  521. package/docs/codex-oauth.md +0 -635
  522. package/docs/codex.md +0 -104
  523. package/docs/codexbar.png +0 -0
  524. package/docs/copilot.md +0 -44
  525. package/docs/cursor.md +0 -46
  526. package/docs/design/designcomparisonchart.md +0 -510
  527. package/docs/design/implementationguide.md +0 -792
  528. package/docs/design/menubaricondesignconcepts.md +0 -796
  529. package/docs/design/mockups/hybridiconconcept.svg +0 -62
  530. package/docs/design/mockups/infinityiconconcept.svg +0 -41
  531. package/docs/design/mockups/infinityiconstates.svg +0 -129
  532. package/docs/design/mockups/wavelogorefined.svg +0 -48
  533. package/docs/design/quick_summary.md +0 -426
  534. package/docs/design/readme.md +0 -478
  535. package/docs/design/sizespecifications.md +0 -575
  536. package/docs/design-system.md +0 -836
  537. package/docs/factory.md +0 -103
  538. package/docs/gemini.md +0 -73
  539. package/docs/icon.md +0 -37
  540. package/docs/icon.png +0 -0
  541. package/docs/index.html +0 -175
  542. package/docs/keychain_fix.md +0 -274
  543. package/docs/minimax.md +0 -71
  544. package/docs/packaging.md +0 -25
  545. package/docs/progress-bar-improvements.md +0 -240
  546. package/docs/progress-bar-quick-reference.md +0 -283
  547. package/docs/progress-bar-technical-details.md +0 -432
  548. package/docs/progress-bar-visual-test.swift +0 -375
  549. package/docs/provider.md +0 -202
  550. package/docs/providers.md +0 -86
  551. package/docs/refactor/macros.md +0 -35
  552. package/docs/refresh-loop.md +0 -22
  553. package/docs/releasing-homebrew.md +0 -35
  554. package/docs/releasing.md +0 -121
  555. package/docs/research/concrete_todo_2026-02-22.md +0 -47
  556. package/docs/research/core-feature-research.md +0 -248
  557. package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
  558. package/docs/research/parity_matrix.md +0 -49
  559. package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
  560. package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
  561. package/docs/research/release_gate_checklist.md +0 -57
  562. package/docs/site.css +0 -505
  563. package/docs/sparkle.md +0 -28
  564. package/docs/status.md +0 -28
  565. package/docs/sync/implementation.md +0 -393
  566. package/docs/sync/readme.md +0 -317
  567. package/docs/sync_files_summary.md +0 -391
  568. package/docs/sync_quick_reference.md +0 -225
  569. package/docs/todo.md +0 -11
  570. package/docs/ui.md +0 -29
  571. package/docs/web-integration.md +0 -9
  572. package/docs/widgets.md +0 -85
  573. package/docs/workflows/error-handling-improvements.md +0 -434
  574. package/docs/workflows/folder_structure.md +0 -299
  575. package/docs/workflows/loading_states.md +0 -446
  576. package/docs/workflows/loading_states_examples.md +0 -565
  577. package/docs/workflows/loading_states_flow.md +0 -430
  578. package/docs/workflows/performance_optimizations.md +0 -457
  579. package/docs/workflows/phase1-cli-testing-report.md +0 -313
  580. package/docs/workflows/quick_reference.md +0 -228
  581. package/docs/workflows/readme.md +0 -59
  582. package/docs/workflows/recent_improvements.md +0 -275
  583. package/docs/workflows/sync_enablement_summary.md +0 -400
  584. package/docs/zai.md +0 -43
  585. package/runic.png +0 -0
  586. package/version.env +0 -2
@@ -1,1711 +0,0 @@
1
- import AppKit
2
- import RunicCore
3
- import Observation
4
- import ServiceManagement
5
-
6
- enum RefreshFrequency: String, CaseIterable, Identifiable {
7
- case manual
8
- case oneMinute
9
- case twoMinutes
10
- case fiveMinutes
11
- case fifteenMinutes
12
-
13
- var id: String { self.rawValue }
14
-
15
- var seconds: TimeInterval? {
16
- switch self {
17
- case .manual: nil
18
- case .oneMinute: 60
19
- case .twoMinutes: 120
20
- case .fiveMinutes: 300
21
- case .fifteenMinutes: 900
22
- }
23
- }
24
-
25
- var label: String {
26
- switch self {
27
- case .manual: "Manual"
28
- case .oneMinute: "1 min"
29
- case .twoMinutes: "2 min"
30
- case .fiveMinutes: "5 min"
31
- case .fifteenMinutes: "15 min"
32
- }
33
- }
34
- }
35
-
36
- enum UsageMetricDisplayMode: String, CaseIterable, Identifiable {
37
- case barsAndPercent
38
- case barsOnly
39
- case percentOnly
40
-
41
- var id: String { self.rawValue }
42
-
43
- var label: String {
44
- switch self {
45
- case .barsAndPercent: "Bars + %"
46
- case .barsOnly: "Bars"
47
- case .percentOnly: "%"
48
- }
49
- }
50
-
51
- var showsBars: Bool {
52
- switch self {
53
- case .barsAndPercent, .barsOnly:
54
- true
55
- case .percentOnly:
56
- false
57
- }
58
- }
59
-
60
- var showsPercent: Bool {
61
- switch self {
62
- case .barsAndPercent, .percentOnly:
63
- true
64
- case .barsOnly:
65
- false
66
- }
67
- }
68
- }
69
-
70
- enum MenuMode: String, CaseIterable, Identifiable {
71
- case glance
72
- case analyst
73
- case `operator`
74
-
75
- var id: String { self.rawValue }
76
-
77
- var label: String {
78
- switch self {
79
- case .glance:
80
- "Glance"
81
- case .analyst:
82
- "Analyst"
83
- case .`operator`:
84
- "Operator"
85
- }
86
- }
87
- }
88
-
89
- enum ChartStyle: String, CaseIterable, Identifiable {
90
- case line
91
- case area
92
- case bar
93
-
94
- var id: String { self.rawValue }
95
-
96
- var label: String {
97
- switch self {
98
- case .line: return "Line"
99
- case .area: return "Area"
100
- case .bar: return "Bar"
101
- }
102
- }
103
- }
104
-
105
- enum NumberFormat: String, CaseIterable, Identifiable {
106
- case abbreviated
107
- case full
108
-
109
- var id: String { self.rawValue }
110
-
111
- var label: String {
112
- switch self {
113
- case .abbreviated: return "Abbreviated"
114
- case .full: return "Full"
115
- }
116
- }
117
- }
118
-
119
- enum DateFormat: String, CaseIterable, Identifiable {
120
- case relative
121
- case absolute
122
-
123
- var id: String { self.rawValue }
124
-
125
- var label: String {
126
- switch self {
127
- case .relative: return "Relative"
128
- case .absolute: return "Absolute"
129
- }
130
- }
131
- }
132
-
133
- enum Theme: String, CaseIterable, Identifiable {
134
- case system
135
- case light
136
- case dark
137
-
138
- var id: String { self.rawValue }
139
-
140
- var label: String {
141
- switch self {
142
- case .system: return "System"
143
- case .light: return "Light"
144
- case .dark: return "Dark"
145
- }
146
- }
147
- }
148
-
149
- enum ProviderSwitcherLayout: String, CaseIterable, Identifiable {
150
- case top
151
- case sidebar
152
-
153
- var id: String { self.rawValue }
154
-
155
- var label: String {
156
- switch self {
157
- case .top: return "Top"
158
- case .sidebar: return "Sidebar"
159
- }
160
- }
161
- }
162
-
163
- enum ProviderSwitcherIconSize: String, CaseIterable, Identifiable {
164
- case small
165
- case medium
166
-
167
- var id: String { self.rawValue }
168
-
169
- var label: String {
170
- switch self {
171
- case .small: return "Small"
172
- case .medium: return "Medium"
173
- }
174
- }
175
- }
176
-
177
- @MainActor
178
- @Observable
179
- final class SettingsStore {
180
- /// Persisted provider display order.
181
- ///
182
- /// Stored as raw `UsageProvider` strings so new providers can be appended automatically without breaking.
183
- private var providerOrderRaw: [String] {
184
- didSet { self.userDefaults.set(self.providerOrderRaw, forKey: "providerOrder") }
185
- }
186
-
187
- var refreshFrequency: RefreshFrequency {
188
- didSet { self.userDefaults.set(self.refreshFrequency.rawValue, forKey: "refreshFrequency") }
189
- }
190
-
191
- var autoDisableRefreshWhenIdleEnabled: Bool {
192
- didSet { self.userDefaults.set(self.autoDisableRefreshWhenIdleEnabled, forKey: "autoDisableRefreshWhenIdleEnabled") }
193
- }
194
-
195
- var autoDisableRefreshWhenIdleMinutes: Int {
196
- didSet { self.userDefaults.set(self.autoDisableRefreshWhenIdleMinutes, forKey: "autoDisableRefreshWhenIdleMinutes") }
197
- }
198
-
199
- var autoDisableRefreshOnSleepEnabled: Bool {
200
- didSet { self.userDefaults.set(self.autoDisableRefreshOnSleepEnabled, forKey: "autoDisableRefreshOnSleepEnabled") }
201
- }
202
-
203
- var autoRefreshWarningEnabled: Bool {
204
- didSet { self.userDefaults.set(self.autoRefreshWarningEnabled, forKey: "autoRefreshWarningEnabled") }
205
- }
206
-
207
- var autoRefreshWarningThreshold: Int {
208
- didSet { self.userDefaults.set(self.autoRefreshWarningThreshold, forKey: "autoRefreshWarningThreshold") }
209
- }
210
-
211
- var autoSuspendInactiveProvidersEnabled: Bool {
212
- didSet {
213
- self.userDefaults.set(self.autoSuspendInactiveProvidersEnabled, forKey: "autoSuspendInactiveProvidersEnabled")
214
- }
215
- }
216
-
217
- var autoSuspendInactiveProvidersMinutes: Int {
218
- didSet {
219
- self.userDefaults.set(self.autoSuspendInactiveProvidersMinutes, forKey: "autoSuspendInactiveProvidersMinutes")
220
- }
221
- }
222
-
223
- var launchAtLogin: Bool {
224
- didSet {
225
- self.userDefaults.set(self.launchAtLogin, forKey: "launchAtLogin")
226
- LaunchAtLoginManager.setEnabled(self.launchAtLogin)
227
- }
228
- }
229
-
230
- /// Hidden toggle to reveal debug-only menu items (enable via defaults write com.sriinnu.athena.Runic debugMenuEnabled
231
- /// -bool YES).
232
- var debugMenuEnabled: Bool {
233
- didSet { self.userDefaults.set(self.debugMenuEnabled, forKey: "debugMenuEnabled") }
234
- }
235
-
236
- private var debugLoadingPatternRaw: String? {
237
- didSet {
238
- if let raw = self.debugLoadingPatternRaw {
239
- self.userDefaults.set(raw, forKey: "debugLoadingPattern")
240
- } else {
241
- self.userDefaults.removeObject(forKey: "debugLoadingPattern")
242
- }
243
- }
244
- }
245
-
246
- var statusChecksEnabled: Bool {
247
- didSet { self.userDefaults.set(self.statusChecksEnabled, forKey: "statusChecksEnabled") }
248
- }
249
-
250
- var sessionQuotaNotificationsEnabled: Bool {
251
- didSet {
252
- self.userDefaults.set(self.sessionQuotaNotificationsEnabled, forKey: "sessionQuotaNotificationsEnabled")
253
- }
254
- }
255
-
256
- /// When enabled, post macOS notifications when spend forecasts breach project budgets.
257
- var budgetNotificationsEnabled: Bool {
258
- didSet {
259
- self.userDefaults.set(self.budgetNotificationsEnabled, forKey: "budgetNotificationsEnabled")
260
- }
261
- }
262
-
263
- /// When enabled, progress bars show "percent used" instead of "percent left".
264
- var usageBarsShowUsed: Bool {
265
- didSet { self.userDefaults.set(self.usageBarsShowUsed, forKey: "usageBarsShowUsed") }
266
- }
267
-
268
- var usageMetricDisplayMode: UsageMetricDisplayMode {
269
- didSet {
270
- self.userDefaults.set(self.usageMetricDisplayMode.rawValue, forKey: "usageMetricDisplayMode")
271
- }
272
- }
273
-
274
- var menuMode: MenuMode {
275
- didSet {
276
- self.userDefaults.set(self.menuMode.rawValue, forKey: "menuMode")
277
- }
278
- }
279
-
280
- var chartStyle: ChartStyle {
281
- didSet { self.userDefaults.set(self.chartStyle.rawValue, forKey: "chartStyle") }
282
- }
283
-
284
- var numberFormat: NumberFormat {
285
- didSet { self.userDefaults.set(self.numberFormat.rawValue, forKey: "numberFormat") }
286
- }
287
-
288
- var dateFormat: DateFormat {
289
- didSet { self.userDefaults.set(self.dateFormat.rawValue, forKey: "dateFormat") }
290
- }
291
-
292
- var theme: Theme {
293
- didSet {
294
- self.userDefaults.set(self.theme.rawValue, forKey: "theme")
295
- RunicApp.applyTheme(self.theme)
296
- }
297
- }
298
-
299
- /// Optional: use provider branding icons with a percentage in the menu bar.
300
- var menuBarShowsBrandIconWithPercent: Bool {
301
- didSet {
302
- self.userDefaults.set(self.menuBarShowsBrandIconWithPercent, forKey: "menuBarShowsBrandIconWithPercent")
303
- }
304
- }
305
-
306
- /// Optional: render the menu bar icon with a vibrant, data-reactive color.
307
- var menuBarVibrantIconEnabled: Bool {
308
- didSet {
309
- self.userDefaults.set(self.menuBarVibrantIconEnabled, forKey: "menuBarVibrantIconEnabled")
310
- }
311
- }
312
-
313
- /// Optional: show provider cost summary from local usage logs (Codex + Claude).
314
- var costUsageEnabled: Bool {
315
- didSet { self.userDefaults.set(self.costUsageEnabled, forKey: "tokenCostUsageEnabled") }
316
- }
317
-
318
- /// Optional: limit how many insight rows appear in the menu before "More…".
319
- var insightsMenuMaxItems: Int {
320
- didSet { self.userDefaults.set(self.insightsMenuMaxItems, forKey: "insightsMenuMaxItems") }
321
- }
322
-
323
- /// Optional: how many days to include in the insights report.
324
- var insightsReportDays: Int {
325
- didSet { self.userDefaults.set(self.insightsReportDays, forKey: "insightsReportDays") }
326
- }
327
-
328
- /// How many days of usage history to scan for ledger data (charts, breakdowns).
329
- /// Options: 3, 7, 30, 365. Default: 30.
330
- var ledgerMaxAgeDays: Int {
331
- didSet { self.userDefaults.set(self.ledgerMaxAgeDays, forKey: "ledgerMaxAgeDays") }
332
- }
333
-
334
- var randomBlinkEnabled: Bool {
335
- didSet { self.userDefaults.set(self.randomBlinkEnabled, forKey: "randomBlinkEnabled") }
336
- }
337
-
338
- /// Optional: augment Claude usage with claude.ai web API (via browser cookies),
339
- /// incl. "Extra usage" spend.
340
- var claudeWebExtrasEnabled: Bool {
341
- didSet { self.userDefaults.set(self.claudeWebExtrasEnabled, forKey: "claudeWebExtrasEnabled") }
342
- }
343
-
344
- /// Optional: show Codex credits + Claude extra usage sections in the menu UI.
345
- var showOptionalCreditsAndExtraUsage: Bool {
346
- didSet {
347
- self.userDefaults.set(self.showOptionalCreditsAndExtraUsage, forKey: "showOptionalCreditsAndExtraUsage")
348
- }
349
- }
350
-
351
- /// Optional: fetch OpenAI web dashboard extras for Codex (browser cookies).
352
- var openAIWebAccessEnabled: Bool {
353
- didSet { self.userDefaults.set(self.openAIWebAccessEnabled, forKey: "openAIWebAccessEnabled") }
354
- }
355
-
356
- private var codexUsageDataSourceRaw: String? {
357
- didSet {
358
- if let raw = self.codexUsageDataSourceRaw {
359
- self.userDefaults.set(raw, forKey: "codexUsageDataSource")
360
- } else {
361
- self.userDefaults.removeObject(forKey: "codexUsageDataSource")
362
- }
363
- }
364
- }
365
-
366
- private var claudeUsageDataSourceRaw: String? {
367
- didSet {
368
- if let raw = self.claudeUsageDataSourceRaw {
369
- self.userDefaults.set(raw, forKey: "claudeUsageDataSource")
370
- } else {
371
- self.userDefaults.removeObject(forKey: "claudeUsageDataSource")
372
- }
373
- }
374
- }
375
-
376
- /// Optional: collapse provider icons into a single menu bar item with an in-menu switcher.
377
- var mergeIcons: Bool {
378
- didSet { self.userDefaults.set(self.mergeIcons, forKey: "mergeIcons") }
379
- }
380
-
381
- /// Optional: show provider icons in the in-menu switcher.
382
- var switcherShowsIcons: Bool {
383
- didSet { self.userDefaults.set(self.switcherShowsIcons, forKey: "switcherShowsIcons") }
384
- }
385
-
386
- /// Optional: position the provider switcher either on top or in a left sidebar.
387
- var providerSwitcherLayout: ProviderSwitcherLayout {
388
- didSet {
389
- self.userDefaults.set(self.providerSwitcherLayout.rawValue, forKey: "providerSwitcherLayout")
390
- }
391
- }
392
-
393
- /// Optional: size of provider icons in the switcher.
394
- var providerSwitcherIconSize: ProviderSwitcherIconSize {
395
- didSet {
396
- self.userDefaults.set(self.providerSwitcherIconSize.rawValue, forKey: "providerSwitcherIconSize")
397
- }
398
- }
399
-
400
- /// Show the built-in providers pane as a sidebar (true) or flat list (false).
401
- var providersPaneSidebar: Bool {
402
- didSet { self.userDefaults.set(self.providersPaneSidebar, forKey: "providersPaneSidebar") }
403
- }
404
-
405
- /// z.ai API token (stored in Keychain).
406
- var zaiAPIToken: String {
407
- didSet {
408
- self.schedulePersistZaiAPIToken()
409
- if !zaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
410
- self.autoEnableProviderIfNeeded(cliName: "zai")
411
- }
412
- }
413
- }
414
-
415
- /// MiniMax API token (stored in Keychain).
416
- var minimaxAPIToken: String {
417
- didSet {
418
- self.schedulePersistMiniMaxAPIToken()
419
- if !minimaxAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
420
- self.autoEnableProviderIfNeeded(cliName: "minimax")
421
- }
422
- }
423
- }
424
-
425
- /// MiniMax manual Cookie header (stored in Keychain).
426
- var minimaxCookieHeader: String {
427
- didSet {
428
- self.schedulePersistMiniMaxCookieHeader()
429
- if !minimaxCookieHeader.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
430
- self.autoEnableProviderIfNeeded(cliName: "minimax")
431
- }
432
- }
433
- }
434
-
435
- /// MiniMax Group ID (stored in Keychain).
436
- var minimaxGroupID: String {
437
- didSet { self.schedulePersistMiniMaxGroupID() }
438
- }
439
-
440
- /// Copilot API token (stored in Keychain).
441
- var copilotAPIToken: String {
442
- didSet {
443
- self.schedulePersistCopilotAPIToken()
444
- if !copilotAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
445
- self.autoEnableProviderIfNeeded(cliName: "copilot")
446
- }
447
- }
448
- }
449
-
450
- /// OpenRouter API key (stored in Keychain).
451
- var openRouterAPIToken: String {
452
- didSet {
453
- self.schedulePersistOpenRouterAPIToken()
454
- if !openRouterAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
455
- self.autoEnableProviderIfNeeded(cliName: "openrouter")
456
- }
457
- }
458
- }
459
-
460
- /// Groq API key (stored in Keychain).
461
- var groqAPIToken: String {
462
- didSet {
463
- self.schedulePersistGroqAPIToken()
464
- if !groqAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
465
- self.autoEnableProviderIfNeeded(cliName: "groq")
466
- }
467
- }
468
- }
469
-
470
- /// DeepSeek API key (stored in Keychain).
471
- var deepSeekAPIToken: String {
472
- didSet {
473
- self.schedulePersistDeepSeekAPIToken()
474
- if !deepSeekAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
475
- self.autoEnableProviderIfNeeded(cliName: "deepseek")
476
- }
477
- }
478
- }
479
-
480
- /// Fireworks API key (stored in Keychain).
481
- var fireworksAPIToken: String {
482
- didSet {
483
- self.schedulePersistFireworksAPIToken()
484
- if !fireworksAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
485
- self.autoEnableProviderIfNeeded(cliName: "fireworks")
486
- }
487
- }
488
- }
489
-
490
- /// Mistral API key (stored in Keychain).
491
- var mistralAPIToken: String {
492
- didSet {
493
- self.schedulePersistMistralAPIToken()
494
- if !mistralAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
495
- self.autoEnableProviderIfNeeded(cliName: "mistral")
496
- }
497
- }
498
- }
499
-
500
- /// Perplexity API key (stored in Keychain).
501
- var perplexityAPIToken: String {
502
- didSet {
503
- self.schedulePersistPerplexityAPIToken()
504
- if !perplexityAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
505
- self.autoEnableProviderIfNeeded(cliName: "perplexity")
506
- }
507
- }
508
- }
509
-
510
- /// Kimi API key (stored in Keychain).
511
- var kimiAPIToken: String {
512
- didSet {
513
- self.schedulePersistKimiAPIToken()
514
- if !kimiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
515
- self.autoEnableProviderIfNeeded(cliName: "kimi")
516
- }
517
- }
518
- }
519
-
520
- /// Auggie API token (stored in Keychain).
521
- var auggieAPIToken: String {
522
- didSet {
523
- self.schedulePersistAuggieAPIToken()
524
- if !auggieAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
525
- self.autoEnableProviderIfNeeded(cliName: "auggie")
526
- }
527
- }
528
- }
529
-
530
- /// Together API key (stored in Keychain).
531
- var togetherAPIToken: String {
532
- didSet {
533
- self.schedulePersistTogetherAPIToken()
534
- if !togetherAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
535
- self.autoEnableProviderIfNeeded(cliName: "together")
536
- }
537
- }
538
- }
539
-
540
- /// Cohere API key (stored in Keychain).
541
- var cohereAPIToken: String {
542
- didSet {
543
- self.schedulePersistCohereAPIToken()
544
- if !cohereAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
545
- self.autoEnableProviderIfNeeded(cliName: "cohere")
546
- }
547
- }
548
- }
549
-
550
- /// xAI API key (stored in Keychain).
551
- var xaiAPIToken: String {
552
- didSet {
553
- self.schedulePersistXAiAPIToken()
554
- if !xaiAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
555
- self.autoEnableProviderIfNeeded(cliName: "xai")
556
- }
557
- }
558
- }
559
-
560
- /// Cerebras API key (stored in Keychain).
561
- var cerebrasAPIToken: String {
562
- didSet {
563
- self.schedulePersistCerebrasAPIToken()
564
- if !cerebrasAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
565
- self.autoEnableProviderIfNeeded(cliName: "cerebras")
566
- }
567
- }
568
- }
569
-
570
- /// Qwen DashScope API key (stored in Keychain).
571
- var qwenAPIToken: String {
572
- didSet {
573
- self.schedulePersistQwenAPIToken()
574
- if !qwenAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
575
- self.autoEnableProviderIfNeeded(cliName: "qwen")
576
- }
577
- }
578
- }
579
-
580
- /// SambaNova API key (stored in Keychain).
581
- var sambaNovaAPIToken: String {
582
- didSet {
583
- self.schedulePersistSambaNovaAPIToken()
584
- if !sambaNovaAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
585
- self.autoEnableProviderIfNeeded(cliName: "sambanova")
586
- }
587
- }
588
- }
589
-
590
- /// Azure OpenAI endpoint URL (stored in UserDefaults).
591
- var azureOpenAIEndpoint: String {
592
- didSet { self.userDefaults.set(self.azureOpenAIEndpoint, forKey: "azureOpenAIEndpoint") }
593
- }
594
-
595
- /// Azure OpenAI deployment name (stored in UserDefaults).
596
- var azureOpenAIDeployment: String {
597
- didSet { self.userDefaults.set(self.azureOpenAIDeployment, forKey: "azureOpenAIDeployment") }
598
- }
599
-
600
- /// Azure OpenAI API version (stored in UserDefaults).
601
- var azureOpenAIAPIVersion: String {
602
- didSet { self.userDefaults.set(self.azureOpenAIAPIVersion, forKey: "azureOpenAIAPIVersion") }
603
- }
604
-
605
- /// Azure OpenAI API key (stored in Keychain).
606
- var azureOpenAIAPIToken: String {
607
- didSet {
608
- self.schedulePersistAzureOpenAIAPIToken()
609
- if !azureOpenAIAPIToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
610
- self.autoEnableProviderIfNeeded(cliName: "azure")
611
- }
612
- }
613
- }
614
-
615
- /// Amazon Bedrock region (stored in UserDefaults).
616
- var bedrockRegion: String {
617
- didSet { self.userDefaults.set(self.bedrockRegion, forKey: "bedrockRegion") }
618
- }
619
-
620
- /// Optional AWS profile for Amazon Bedrock (stored in UserDefaults).
621
- var bedrockAWSProfile: String {
622
- didSet { self.userDefaults.set(self.bedrockAWSProfile, forKey: "bedrockAWSProfile") }
623
- }
624
-
625
- /// Optional model filter for Amazon Bedrock (stored in UserDefaults).
626
- var bedrockModelID: String {
627
- didSet { self.userDefaults.set(self.bedrockModelID, forKey: "bedrockModelID") }
628
- }
629
-
630
- /// Google Cloud project for Vertex AI (stored in UserDefaults).
631
- var vertexaiProject: String {
632
- didSet { self.userDefaults.set(self.vertexaiProject, forKey: "vertexaiProject") }
633
- }
634
-
635
- /// Google Cloud location/region for Vertex AI (stored in UserDefaults).
636
- var vertexaiLocation: String {
637
- didSet { self.userDefaults.set(self.vertexaiLocation, forKey: "vertexaiLocation") }
638
- }
639
-
640
- private var selectedMenuProviderRaw: String? {
641
- didSet {
642
- if let raw = self.selectedMenuProviderRaw {
643
- self.userDefaults.set(raw, forKey: "selectedMenuProvider")
644
- } else {
645
- self.userDefaults.removeObject(forKey: "selectedMenuProvider")
646
- }
647
- }
648
- }
649
-
650
- /// Optional override for the loading animation pattern, exposed via the Debug tab.
651
- var debugLoadingPattern: LoadingPattern? {
652
- get { self.debugLoadingPatternRaw.flatMap(LoadingPattern.init(rawValue:)) }
653
- set {
654
- self.debugLoadingPatternRaw = newValue?.rawValue
655
- }
656
- }
657
-
658
- var selectedMenuProvider: UsageProvider? {
659
- get { self.selectedMenuProviderRaw.flatMap(UsageProvider.init(rawValue:)) }
660
- set {
661
- self.selectedMenuProviderRaw = newValue?.rawValue
662
- }
663
- }
664
-
665
- var codexUsageDataSource: CodexUsageDataSource {
666
- get { CodexUsageDataSource(rawValue: self.codexUsageDataSourceRaw ?? "") ?? .oauth }
667
- set {
668
- self.codexUsageDataSourceRaw = newValue.rawValue
669
- }
670
- }
671
-
672
- var claudeUsageDataSource: ClaudeUsageDataSource {
673
- get { ClaudeUsageDataSource(rawValue: self.claudeUsageDataSourceRaw ?? "") ?? .oauth }
674
- set {
675
- self.claudeUsageDataSourceRaw = newValue.rawValue
676
- if newValue != .cli {
677
- self.claudeWebExtrasEnabled = false
678
- }
679
- }
680
- }
681
-
682
- var menuObservationToken: Int {
683
- _ = self.providerOrderRaw
684
- _ = self.refreshFrequency
685
- _ = self.autoDisableRefreshWhenIdleEnabled
686
- _ = self.autoDisableRefreshWhenIdleMinutes
687
- _ = self.autoDisableRefreshOnSleepEnabled
688
- _ = self.autoRefreshWarningEnabled
689
- _ = self.autoRefreshWarningThreshold
690
- _ = self.autoSuspendInactiveProvidersEnabled
691
- _ = self.autoSuspendInactiveProvidersMinutes
692
- _ = self.launchAtLogin
693
- _ = self.debugMenuEnabled
694
- _ = self.statusChecksEnabled
695
- _ = self.sessionQuotaNotificationsEnabled
696
- _ = self.usageBarsShowUsed
697
- _ = self.usageMetricDisplayMode
698
- _ = self.menuMode
699
- _ = self.chartStyle
700
- _ = self.numberFormat
701
- _ = self.dateFormat
702
- _ = self.theme
703
- _ = self.menuBarShowsBrandIconWithPercent
704
- _ = self.menuBarVibrantIconEnabled
705
- _ = self.costUsageEnabled
706
- _ = self.insightsMenuMaxItems
707
- _ = self.insightsReportDays
708
- _ = self.ledgerMaxAgeDays
709
- _ = self.randomBlinkEnabled
710
- _ = self.claudeWebExtrasEnabled
711
- _ = self.showOptionalCreditsAndExtraUsage
712
- _ = self.openAIWebAccessEnabled
713
- _ = self.codexUsageDataSource
714
- _ = self.claudeUsageDataSource
715
- _ = self.mergeIcons
716
- _ = self.switcherShowsIcons
717
- _ = self.providerSwitcherLayout
718
- _ = self.providerSwitcherIconSize
719
- _ = self.zaiAPIToken
720
- _ = self.minimaxAPIToken
721
- _ = self.minimaxCookieHeader
722
- _ = self.minimaxGroupID
723
- _ = self.copilotAPIToken
724
- _ = self.openRouterAPIToken
725
- _ = self.groqAPIToken
726
- _ = self.deepSeekAPIToken
727
- _ = self.fireworksAPIToken
728
- _ = self.mistralAPIToken
729
- _ = self.perplexityAPIToken
730
- _ = self.kimiAPIToken
731
- _ = self.auggieAPIToken
732
- _ = self.togetherAPIToken
733
- _ = self.cohereAPIToken
734
- _ = self.xaiAPIToken
735
- _ = self.cerebrasAPIToken
736
- _ = self.qwenAPIToken
737
- _ = self.sambaNovaAPIToken
738
- _ = self.azureOpenAIEndpoint
739
- _ = self.azureOpenAIDeployment
740
- _ = self.azureOpenAIAPIVersion
741
- _ = self.azureOpenAIAPIToken
742
- _ = self.bedrockRegion
743
- _ = self.bedrockAWSProfile
744
- _ = self.bedrockModelID
745
- _ = self.vertexaiProject
746
- _ = self.vertexaiLocation
747
- _ = self.debugLoadingPattern
748
- _ = self.selectedMenuProvider
749
- _ = self.providerToggleRevision
750
- return 0
751
- }
752
-
753
- private var providerDetectionCompleted: Bool {
754
- didSet { self.userDefaults.set(self.providerDetectionCompleted, forKey: "providerDetectionCompleted") }
755
- }
756
-
757
- @ObservationIgnored private let userDefaults: UserDefaults
758
- @ObservationIgnored private let toggleStore: ProviderToggleStore
759
- @ObservationIgnored private let zaiTokenStore: any ZaiTokenStoring
760
- @ObservationIgnored private var zaiTokenPersistTask: Task<Void, Never>?
761
- @ObservationIgnored private let minimaxTokenStore: any MiniMaxTokenStoring
762
- @ObservationIgnored private var minimaxTokenPersistTask: Task<Void, Never>?
763
- @ObservationIgnored private let minimaxCookieHeaderStore: any MiniMaxCookieHeaderStoring
764
- @ObservationIgnored private var minimaxCookieHeaderPersistTask: Task<Void, Never>?
765
- @ObservationIgnored private let minimaxGroupIDStore: any MiniMaxGroupIDStoring
766
- @ObservationIgnored private var minimaxGroupIDPersistTask: Task<Void, Never>?
767
- @ObservationIgnored private let copilotTokenStore: any CopilotTokenStoring
768
- @ObservationIgnored private var copilotTokenPersistTask: Task<Void, Never>?
769
- @ObservationIgnored private let openRouterTokenStore: any OpenRouterTokenStoring
770
- @ObservationIgnored private var openRouterTokenPersistTask: Task<Void, Never>?
771
- @ObservationIgnored private let groqTokenStore: any GroqTokenStoring
772
- @ObservationIgnored private var groqTokenPersistTask: Task<Void, Never>?
773
- @ObservationIgnored private let deepSeekTokenStore: any DeepSeekTokenStoring
774
- @ObservationIgnored private var deepSeekTokenPersistTask: Task<Void, Never>?
775
- @ObservationIgnored private let fireworksTokenStore: any FireworksTokenStoring
776
- @ObservationIgnored private var fireworksTokenPersistTask: Task<Void, Never>?
777
- @ObservationIgnored private let mistralTokenStore: any MistralTokenStoring
778
- @ObservationIgnored private var mistralTokenPersistTask: Task<Void, Never>?
779
- @ObservationIgnored private let perplexityTokenStore: any PerplexityTokenStoring
780
- @ObservationIgnored private var perplexityTokenPersistTask: Task<Void, Never>?
781
- @ObservationIgnored private let kimiTokenStore: any KimiTokenStoring
782
- @ObservationIgnored private var kimiTokenPersistTask: Task<Void, Never>?
783
- @ObservationIgnored private let auggieTokenStore: any AuggieTokenStoring
784
- @ObservationIgnored private var auggieTokenPersistTask: Task<Void, Never>?
785
- @ObservationIgnored private let togetherTokenStore: any TogetherTokenStoring
786
- @ObservationIgnored private var togetherTokenPersistTask: Task<Void, Never>?
787
- @ObservationIgnored private let cohereTokenStore: any CohereTokenStoring
788
- @ObservationIgnored private var cohereTokenPersistTask: Task<Void, Never>?
789
- @ObservationIgnored private let xaiTokenStore: any XAITokenStoring
790
- @ObservationIgnored private var xaiTokenPersistTask: Task<Void, Never>?
791
- @ObservationIgnored private let cerebrasTokenStore: any CerebrasTokenStoring
792
- @ObservationIgnored private var cerebrasTokenPersistTask: Task<Void, Never>?
793
- @ObservationIgnored private let sambaNovaTokenStore: any SambaNovaTokenStoring
794
- @ObservationIgnored private var sambaNovaTokenPersistTask: Task<Void, Never>?
795
- @ObservationIgnored private let qwenTokenStore: any QwenTokenStoring
796
- @ObservationIgnored private var qwenTokenPersistTask: Task<Void, Never>?
797
- @ObservationIgnored private let azureOpenAITokenStore: any AzureOpenAITokenStoring
798
- @ObservationIgnored private var azureOpenAITokenPersistTask: Task<Void, Never>?
799
- // Cache enablement so tight UI loops (menu bar animations) don't hit UserDefaults each tick.
800
- @ObservationIgnored private var cachedProviderEnablement: [UsageProvider: Bool] = [:]
801
- @ObservationIgnored private var cachedProviderEnablementRevision: Int = -1
802
- @ObservationIgnored private var cachedEnabledProviders: [UsageProvider] = []
803
- @ObservationIgnored private var cachedEnabledProvidersRevision: Int = -1
804
- @ObservationIgnored private var cachedEnabledProvidersOrderRaw: [String] = []
805
- // Cache order to avoid re-building sets/arrays every animation tick.
806
- @ObservationIgnored private var cachedProviderOrder: [UsageProvider] = []
807
- @ObservationIgnored private var cachedProviderOrderRaw: [String] = []
808
- private var providerToggleRevision: Int = 0
809
-
810
- init(
811
- userDefaults: UserDefaults = .standard,
812
- zaiTokenStore: any ZaiTokenStoring = KeychainZaiTokenStore(),
813
- minimaxTokenStore: any MiniMaxTokenStoring = KeychainMiniMaxTokenStore(),
814
- minimaxCookieHeaderStore: any MiniMaxCookieHeaderStoring = KeychainMiniMaxCookieHeaderStore(),
815
- minimaxGroupIDStore: any MiniMaxGroupIDStoring = KeychainMiniMaxGroupIDStore(),
816
- copilotTokenStore: any CopilotTokenStoring = KeychainCopilotTokenStore(),
817
- openRouterTokenStore: any OpenRouterTokenStoring = KeychainOpenRouterTokenStore(),
818
- groqTokenStore: any GroqTokenStoring = KeychainGroqTokenStore(),
819
- deepSeekTokenStore: any DeepSeekTokenStoring = KeychainDeepSeekTokenStore(),
820
- fireworksTokenStore: any FireworksTokenStoring = KeychainFireworksTokenStore(),
821
- mistralTokenStore: any MistralTokenStoring = KeychainMistralTokenStore(),
822
- perplexityTokenStore: any PerplexityTokenStoring = KeychainPerplexityTokenStore(),
823
- kimiTokenStore: any KimiTokenStoring = KeychainKimiTokenStore(),
824
- auggieTokenStore: any AuggieTokenStoring = KeychainAuggieTokenStore(),
825
- togetherTokenStore: any TogetherTokenStoring = KeychainTogetherTokenStore(),
826
- cohereTokenStore: any CohereTokenStoring = KeychainCohereTokenStore(),
827
- xaiTokenStore: any XAITokenStoring = KeychainXAITokenStore(),
828
- cerebrasTokenStore: any CerebrasTokenStoring = KeychainCerebrasTokenStore(),
829
- sambaNovaTokenStore: any SambaNovaTokenStoring = KeychainSambaNovaTokenStore(),
830
- qwenTokenStore: any QwenTokenStoring = KeychainQwenTokenStore(),
831
- azureOpenAITokenStore: any AzureOpenAITokenStoring = KeychainAzureOpenAITokenStore())
832
- {
833
- self.userDefaults = userDefaults
834
- self.zaiTokenStore = zaiTokenStore
835
- self.minimaxTokenStore = minimaxTokenStore
836
- self.minimaxCookieHeaderStore = minimaxCookieHeaderStore
837
- self.minimaxGroupIDStore = minimaxGroupIDStore
838
- self.copilotTokenStore = copilotTokenStore
839
- self.openRouterTokenStore = openRouterTokenStore
840
- self.groqTokenStore = groqTokenStore
841
- self.deepSeekTokenStore = deepSeekTokenStore
842
- self.fireworksTokenStore = fireworksTokenStore
843
- self.mistralTokenStore = mistralTokenStore
844
- self.perplexityTokenStore = perplexityTokenStore
845
- self.kimiTokenStore = kimiTokenStore
846
- self.auggieTokenStore = auggieTokenStore
847
- self.togetherTokenStore = togetherTokenStore
848
- self.cohereTokenStore = cohereTokenStore
849
- self.xaiTokenStore = xaiTokenStore
850
- self.cerebrasTokenStore = cerebrasTokenStore
851
- self.sambaNovaTokenStore = sambaNovaTokenStore
852
- self.qwenTokenStore = qwenTokenStore
853
- self.azureOpenAITokenStore = azureOpenAITokenStore
854
- self.providerOrderRaw = userDefaults.stringArray(forKey: "providerOrder") ?? []
855
- let raw = userDefaults.string(forKey: "refreshFrequency") ?? RefreshFrequency.manual.rawValue
856
- self.refreshFrequency = RefreshFrequency(rawValue: raw) ?? .manual
857
- self.autoDisableRefreshWhenIdleEnabled = userDefaults.object(
858
- forKey: "autoDisableRefreshWhenIdleEnabled") as? Bool ?? true
859
- self.autoDisableRefreshWhenIdleMinutes = userDefaults.object(
860
- forKey: "autoDisableRefreshWhenIdleMinutes") as? Int ?? 5
861
- self.autoDisableRefreshOnSleepEnabled = userDefaults.object(
862
- forKey: "autoDisableRefreshOnSleepEnabled") as? Bool ?? true
863
- self.autoRefreshWarningEnabled = userDefaults.object(
864
- forKey: "autoRefreshWarningEnabled") as? Bool ?? true
865
- self.autoRefreshWarningThreshold = userDefaults.object(
866
- forKey: "autoRefreshWarningThreshold") as? Int ?? 10
867
- self.autoSuspendInactiveProvidersEnabled = userDefaults.object(
868
- forKey: "autoSuspendInactiveProvidersEnabled") as? Bool ?? true
869
- self.autoSuspendInactiveProvidersMinutes = userDefaults.object(
870
- forKey: "autoSuspendInactiveProvidersMinutes") as? Int ?? 720
871
- self.launchAtLogin = userDefaults.object(forKey: "launchAtLogin") as? Bool ?? false
872
- self.debugMenuEnabled = userDefaults.object(forKey: "debugMenuEnabled") as? Bool ?? false
873
- self.debugLoadingPatternRaw = userDefaults.string(forKey: "debugLoadingPattern")
874
- self.statusChecksEnabled = userDefaults.object(forKey: "statusChecksEnabled") as? Bool ?? true
875
- let sessionQuotaNotificationsDefault = userDefaults.object(
876
- forKey: "sessionQuotaNotificationsEnabled") as? Bool
877
- self.sessionQuotaNotificationsEnabled = sessionQuotaNotificationsDefault ?? true
878
- if sessionQuotaNotificationsDefault == nil {
879
- self.userDefaults.set(true, forKey: "sessionQuotaNotificationsEnabled")
880
- }
881
- self.budgetNotificationsEnabled = userDefaults.object(
882
- forKey: "budgetNotificationsEnabled") as? Bool ?? false
883
- self.usageBarsShowUsed = userDefaults.object(forKey: "usageBarsShowUsed") as? Bool ?? true
884
- let metricDisplayRaw = userDefaults.string(forKey: "usageMetricDisplayMode")
885
- self.usageMetricDisplayMode = UsageMetricDisplayMode(rawValue: metricDisplayRaw ?? "") ?? .barsAndPercent
886
- let menuModeRaw = userDefaults.string(forKey: "menuMode")
887
- self.menuMode = MenuMode(rawValue: menuModeRaw ?? "") ?? .`operator`
888
- let chartStyleRaw = userDefaults.string(forKey: "chartStyle")
889
- self.chartStyle = ChartStyle(rawValue: chartStyleRaw ?? "") ?? .line
890
- let numberFormatRaw = userDefaults.string(forKey: "numberFormat")
891
- self.numberFormat = NumberFormat(rawValue: numberFormatRaw ?? "") ?? .abbreviated
892
- let dateFormatRaw = userDefaults.string(forKey: "dateFormat")
893
- self.dateFormat = DateFormat(rawValue: dateFormatRaw ?? "") ?? .relative
894
- let themeRaw = userDefaults.string(forKey: "theme")
895
- self.theme = Theme(rawValue: themeRaw ?? "") ?? .system
896
- self.menuBarShowsBrandIconWithPercent = userDefaults.object(
897
- forKey: "menuBarShowsBrandIconWithPercent") as? Bool ?? false
898
- self.menuBarVibrantIconEnabled = userDefaults.object(
899
- forKey: "menuBarVibrantIconEnabled") as? Bool ?? true
900
- self.costUsageEnabled = userDefaults.object(forKey: "tokenCostUsageEnabled") as? Bool ?? true
901
- self.insightsMenuMaxItems = userDefaults.object(forKey: "insightsMenuMaxItems") as? Int ?? 4
902
- self.insightsReportDays = userDefaults.object(forKey: "insightsReportDays") as? Int ?? 7
903
- self.ledgerMaxAgeDays = userDefaults.object(forKey: "ledgerMaxAgeDays") as? Int ?? 30
904
- self.randomBlinkEnabled = userDefaults.object(forKey: "randomBlinkEnabled") as? Bool ?? false
905
- self.claudeWebExtrasEnabled = userDefaults.object(forKey: "claudeWebExtrasEnabled") as? Bool ?? false
906
- let creditsExtrasDefault = userDefaults.object(forKey: "showOptionalCreditsAndExtraUsage") as? Bool
907
- self.showOptionalCreditsAndExtraUsage = creditsExtrasDefault ?? true
908
- if creditsExtrasDefault == nil {
909
- self.userDefaults.set(true, forKey: "showOptionalCreditsAndExtraUsage")
910
- }
911
- let openAIWebAccessDefault = userDefaults.object(forKey: "openAIWebAccessEnabled") as? Bool
912
- self.openAIWebAccessEnabled = openAIWebAccessDefault ?? true
913
- if openAIWebAccessDefault == nil {
914
- self.userDefaults.set(true, forKey: "openAIWebAccessEnabled")
915
- }
916
- let codexSourceRaw = userDefaults.string(forKey: "codexUsageDataSource")
917
- self.codexUsageDataSourceRaw = codexSourceRaw ?? CodexUsageDataSource.oauth.rawValue
918
- let claudeSourceRaw = userDefaults.string(forKey: "claudeUsageDataSource")
919
- self.claudeUsageDataSourceRaw = claudeSourceRaw ?? ClaudeUsageDataSource.oauth.rawValue
920
- self.mergeIcons = userDefaults.object(forKey: "mergeIcons") as? Bool ?? true
921
- self.switcherShowsIcons = userDefaults.object(forKey: "switcherShowsIcons") as? Bool ?? true
922
- let layoutRaw = userDefaults.string(forKey: "providerSwitcherLayout")
923
- self.providerSwitcherLayout = ProviderSwitcherLayout(rawValue: layoutRaw ?? "") ?? .top
924
- let iconSizeRaw = userDefaults.string(forKey: "providerSwitcherIconSize")
925
- self.providerSwitcherIconSize = ProviderSwitcherIconSize(rawValue: iconSizeRaw ?? "") ?? .medium
926
- self.providersPaneSidebar = userDefaults.object(forKey: "providersPaneSidebar") as? Bool ?? false
927
- self.azureOpenAIEndpoint = userDefaults.string(forKey: "azureOpenAIEndpoint") ?? ""
928
- self.azureOpenAIDeployment = userDefaults.string(forKey: "azureOpenAIDeployment") ?? ""
929
- self.azureOpenAIAPIVersion = userDefaults.string(forKey: "azureOpenAIAPIVersion")
930
- ?? "2024-10-21"
931
- self.bedrockRegion = userDefaults.string(forKey: "bedrockRegion") ?? ""
932
- self.bedrockAWSProfile = userDefaults.string(forKey: "bedrockAWSProfile") ?? ""
933
- self.bedrockModelID = userDefaults.string(forKey: "bedrockModelID") ?? ""
934
- self.vertexaiProject = userDefaults.string(forKey: "vertexaiProject") ?? ""
935
- self.vertexaiLocation = userDefaults.string(forKey: "vertexaiLocation") ?? ""
936
- self.zaiAPIToken = (try? zaiTokenStore.loadToken()) ?? ""
937
- self.minimaxAPIToken = (try? minimaxTokenStore.loadToken()) ?? ""
938
- self.minimaxCookieHeader = (try? minimaxCookieHeaderStore.loadHeader()) ?? ""
939
- self.minimaxGroupID = (try? minimaxGroupIDStore.loadGroupID()) ?? ""
940
- self.copilotAPIToken = (try? copilotTokenStore.loadToken()) ?? ""
941
- self.openRouterAPIToken = (try? openRouterTokenStore.loadToken()) ?? ""
942
- self.groqAPIToken = (try? groqTokenStore.loadToken()) ?? ""
943
- self.deepSeekAPIToken = (try? deepSeekTokenStore.loadToken()) ?? ""
944
- self.fireworksAPIToken = (try? fireworksTokenStore.loadToken()) ?? ""
945
- self.mistralAPIToken = (try? mistralTokenStore.loadToken()) ?? ""
946
- self.perplexityAPIToken = (try? perplexityTokenStore.loadToken()) ?? ""
947
- self.kimiAPIToken = (try? kimiTokenStore.loadToken()) ?? ""
948
- self.auggieAPIToken = (try? auggieTokenStore.loadToken()) ?? ""
949
- self.togetherAPIToken = (try? togetherTokenStore.loadToken()) ?? ""
950
- self.cohereAPIToken = (try? cohereTokenStore.loadToken()) ?? ""
951
- self.xaiAPIToken = (try? xaiTokenStore.loadToken()) ?? ""
952
- self.cerebrasAPIToken = (try? cerebrasTokenStore.loadToken()) ?? ""
953
- self.sambaNovaAPIToken = (try? sambaNovaTokenStore.loadToken()) ?? ""
954
- self.qwenAPIToken = (try? qwenTokenStore.loadToken()) ?? ""
955
- self.azureOpenAIAPIToken = (try? azureOpenAITokenStore.loadToken()) ?? ""
956
- // Always start on Overview tab.
957
- self.selectedMenuProviderRaw = nil
958
- self.providerDetectionCompleted = userDefaults.object(
959
- forKey: "providerDetectionCompleted") as? Bool ?? false
960
- self.toggleStore = ProviderToggleStore(userDefaults: userDefaults)
961
- self.toggleStore.purgeLegacyKeys()
962
- LaunchAtLoginManager.setEnabled(self.launchAtLogin)
963
- self.runInitialProviderDetectionIfNeeded()
964
- self.applyTokenCostDefaultIfNeeded()
965
- if self.claudeUsageDataSource != .cli {
966
- self.claudeWebExtrasEnabled = false
967
- }
968
- }
969
-
970
- func orderedProviders() -> [UsageProvider] {
971
- let raw = self.providerOrderRaw
972
- if raw == self.cachedProviderOrderRaw, !self.cachedProviderOrder.isEmpty {
973
- return self.cachedProviderOrder
974
- }
975
- let ordered = Self.effectiveProviderOrder(raw: raw)
976
- self.cachedProviderOrderRaw = raw
977
- self.cachedProviderOrder = ordered
978
- return ordered
979
- }
980
-
981
- func moveProvider(fromOffsets: IndexSet, toOffset: Int) {
982
- var order = self.orderedProviders()
983
- order.move(fromOffsets: fromOffsets, toOffset: toOffset)
984
- self.providerOrderRaw = order.map(\.rawValue)
985
- }
986
-
987
- func isProviderEnabled(provider: UsageProvider, metadata: ProviderMetadata) -> Bool {
988
- _ = self.providerToggleRevision
989
- return self.toggleStore.isEnabled(metadata: metadata)
990
- }
991
-
992
- func isProviderEnabledCached(
993
- provider: UsageProvider,
994
- metadataByProvider: [UsageProvider: ProviderMetadata]) -> Bool
995
- {
996
- self.refreshProviderEnablementCacheIfNeeded(metadataByProvider: metadataByProvider)
997
- return self.cachedProviderEnablement[provider] ?? false
998
- }
999
-
1000
- func enabledProvidersOrdered(metadataByProvider: [UsageProvider: ProviderMetadata]) -> [UsageProvider] {
1001
- self.refreshProviderEnablementCacheIfNeeded(metadataByProvider: metadataByProvider)
1002
- let orderRaw = self.providerOrderRaw
1003
- let revision = self.cachedProviderEnablementRevision
1004
- if revision == self.cachedEnabledProvidersRevision,
1005
- orderRaw == self.cachedEnabledProvidersOrderRaw,
1006
- !self.cachedEnabledProviders.isEmpty
1007
- {
1008
- return self.cachedEnabledProviders
1009
- }
1010
- let enabled = self.orderedProviders().filter { self.cachedProviderEnablement[$0] ?? false }
1011
- self.cachedEnabledProviders = enabled
1012
- self.cachedEnabledProvidersRevision = revision
1013
- self.cachedEnabledProvidersOrderRaw = orderRaw
1014
- return enabled
1015
- }
1016
-
1017
- func setProviderEnabled(provider: UsageProvider, metadata: ProviderMetadata, enabled: Bool) {
1018
- self.providerToggleRevision &+= 1
1019
- self.toggleStore.setEnabled(enabled, metadata: metadata)
1020
- }
1021
-
1022
- func rerunProviderDetection() {
1023
- self.runInitialProviderDetectionIfNeeded(force: true)
1024
- }
1025
-
1026
- /// Auto-enable a provider when the user enters a non-empty API token.
1027
- private func autoEnableProviderIfNeeded(cliName: String) {
1028
- let toggles = (self.userDefaults.dictionary(forKey: "providerToggles") as? [String: Bool]) ?? [:]
1029
- guard toggles[cliName] == nil else { return }
1030
- var updated = toggles
1031
- updated[cliName] = true
1032
- self.userDefaults.set(updated, forKey: "providerToggles")
1033
- self.providerToggleRevision &+= 1
1034
- }
1035
-
1036
- // MARK: - Private
1037
-
1038
- func isCostUsageEffectivelyEnabled(for provider: UsageProvider) -> Bool {
1039
- self.costUsageEnabled
1040
- && ProviderDescriptorRegistry.descriptor(for: provider).tokenCost.supportsTokenCost
1041
- }
1042
-
1043
- private static func effectiveProviderOrder(raw: [String]) -> [UsageProvider] {
1044
- var seen: Set<UsageProvider> = []
1045
- var ordered: [UsageProvider] = []
1046
-
1047
- for rawValue in raw {
1048
- guard let provider = UsageProvider(rawValue: rawValue) else { continue }
1049
- guard !seen.contains(provider) else { continue }
1050
- seen.insert(provider)
1051
- ordered.append(provider)
1052
- }
1053
-
1054
- if ordered.isEmpty {
1055
- ordered = UsageProvider.allCases
1056
- seen = Set(ordered)
1057
- }
1058
-
1059
- if !seen.contains(.factory), let zaiIndex = ordered.firstIndex(of: .zai) {
1060
- ordered.insert(.factory, at: zaiIndex)
1061
- seen.insert(.factory)
1062
- }
1063
-
1064
- for provider in UsageProvider.allCases where !seen.contains(provider) {
1065
- ordered.append(provider)
1066
- }
1067
-
1068
- return ordered
1069
- }
1070
-
1071
- private func refreshProviderEnablementCacheIfNeeded(
1072
- metadataByProvider: [UsageProvider: ProviderMetadata])
1073
- {
1074
- let revision = self.providerToggleRevision
1075
- guard revision != self.cachedProviderEnablementRevision else { return }
1076
- var cache: [UsageProvider: Bool] = [:]
1077
- for (provider, metadata) in metadataByProvider {
1078
- cache[provider] = self.toggleStore.isEnabled(metadata: metadata)
1079
- }
1080
- self.cachedProviderEnablement = cache
1081
- self.cachedProviderEnablementRevision = revision
1082
- }
1083
-
1084
- private func runInitialProviderDetectionIfNeeded(force: Bool = false) {
1085
- guard force || !self.providerDetectionCompleted else { return }
1086
- guard let codexMeta = ProviderRegistry.shared.metadata[.codex],
1087
- let claudeMeta = ProviderRegistry.shared.metadata[.claude],
1088
- let geminiMeta = ProviderRegistry.shared.metadata[.gemini],
1089
- let antigravityMeta = ProviderRegistry.shared.metadata[.antigravity] else { return }
1090
-
1091
- LoginShellPathCache.shared.captureOnce { [weak self] _ in
1092
- Task { @MainActor in
1093
- await self?.applyProviderDetection(
1094
- codexMeta: codexMeta,
1095
- claudeMeta: claudeMeta,
1096
- geminiMeta: geminiMeta,
1097
- antigravityMeta: antigravityMeta)
1098
- }
1099
- }
1100
- }
1101
-
1102
- private func applyProviderDetection(
1103
- codexMeta: ProviderMetadata,
1104
- claudeMeta: ProviderMetadata,
1105
- geminiMeta: ProviderMetadata,
1106
- antigravityMeta: ProviderMetadata) async
1107
- {
1108
- guard !self.providerDetectionCompleted else { return }
1109
- let codexInstalled = BinaryLocator.resolveCodexBinary() != nil
1110
- let claudeInstalled = BinaryLocator.resolveClaudeBinary() != nil
1111
- let geminiInstalled = BinaryLocator.resolveGeminiBinary() != nil
1112
- let antigravityRunning = await AntigravityStatusProbe.isRunning()
1113
-
1114
- // If none installed, keep Codex enabled to match previous behavior.
1115
- let noneInstalled = !codexInstalled && !claudeInstalled && !geminiInstalled && !antigravityRunning
1116
- let enableCodex = codexInstalled || noneInstalled
1117
- let enableClaude = claudeInstalled
1118
- let enableGemini = geminiInstalled
1119
- let enableAntigravity = antigravityRunning
1120
-
1121
- self.providerToggleRevision &+= 1
1122
- self.toggleStore.setEnabled(enableCodex, metadata: codexMeta)
1123
- self.toggleStore.setEnabled(enableClaude, metadata: claudeMeta)
1124
- self.toggleStore.setEnabled(enableGemini, metadata: geminiMeta)
1125
- self.toggleStore.setEnabled(enableAntigravity, metadata: antigravityMeta)
1126
- self.providerDetectionCompleted = true
1127
- }
1128
-
1129
- private func applyTokenCostDefaultIfNeeded() {
1130
- // Settings are persisted in UserDefaults.standard.
1131
- guard UserDefaults.standard.object(forKey: "tokenCostUsageEnabled") == nil else { return }
1132
-
1133
- Task { @MainActor [weak self] in
1134
- guard let self else { return }
1135
- let hasSources = await Task.detached(priority: .utility) {
1136
- Self.hasAnyTokenCostUsageSources()
1137
- }.value
1138
- guard hasSources else { return }
1139
- guard UserDefaults.standard.object(forKey: "tokenCostUsageEnabled") == nil else { return }
1140
- self.costUsageEnabled = true
1141
- }
1142
- }
1143
-
1144
- nonisolated static func hasAnyTokenCostUsageSources(
1145
- env: [String: String] = ProcessInfo.processInfo.environment,
1146
- fileManager: FileManager = .default) -> Bool
1147
- {
1148
- func hasAnyJsonl(in root: URL) -> Bool {
1149
- guard fileManager.fileExists(atPath: root.path) else { return false }
1150
- guard let enumerator = fileManager.enumerator(
1151
- at: root,
1152
- includingPropertiesForKeys: [.isRegularFileKey],
1153
- options: [.skipsHiddenFiles, .skipsPackageDescendants])
1154
- else { return false }
1155
-
1156
- for case let url as URL in enumerator where url.pathExtension.lowercased() == "jsonl" {
1157
- return true
1158
- }
1159
- return false
1160
- }
1161
-
1162
- let codexRoot: URL = {
1163
- let raw = env["CODEX_HOME"]?.trimmingCharacters(in: .whitespacesAndNewlines)
1164
- if let raw, !raw.isEmpty {
1165
- return URL(fileURLWithPath: raw).appendingPathComponent("sessions", isDirectory: true)
1166
- }
1167
- return fileManager.homeDirectoryForCurrentUser
1168
- .appendingPathComponent(".codex", isDirectory: true)
1169
- .appendingPathComponent("sessions", isDirectory: true)
1170
- }()
1171
- if hasAnyJsonl(in: codexRoot) { return true }
1172
-
1173
- let claudeRoots: [URL] = {
1174
- if let env = env["CLAUDE_CONFIG_DIR"]?.trimmingCharacters(in: .whitespacesAndNewlines),
1175
- !env.isEmpty
1176
- {
1177
- return env.split(separator: ",").map { part in
1178
- let raw = String(part).trimmingCharacters(in: .whitespacesAndNewlines)
1179
- let url = URL(fileURLWithPath: raw)
1180
- if url.lastPathComponent == "projects" {
1181
- return url
1182
- }
1183
- return url.appendingPathComponent("projects", isDirectory: true)
1184
- }
1185
- }
1186
-
1187
- let home = fileManager.homeDirectoryForCurrentUser
1188
- return [
1189
- home.appendingPathComponent(".config/claude/projects", isDirectory: true),
1190
- home.appendingPathComponent(".claude/projects", isDirectory: true),
1191
- ]
1192
- }()
1193
-
1194
- return claudeRoots.contains(where: hasAnyJsonl(in:))
1195
- }
1196
-
1197
- private func schedulePersistZaiAPIToken() {
1198
- self.zaiTokenPersistTask?.cancel()
1199
- let token = self.zaiAPIToken
1200
- let tokenStore = self.zaiTokenStore
1201
- self.zaiTokenPersistTask = Task { @MainActor in
1202
- do {
1203
- try await Task.sleep(nanoseconds: 350_000_000)
1204
- } catch {
1205
- return
1206
- }
1207
- guard !Task.isCancelled else { return }
1208
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1209
- do {
1210
- try tokenStore.storeToken(token)
1211
- return nil
1212
- } catch {
1213
- return error
1214
- }
1215
- }.value
1216
- if let error {
1217
- // Keep value in memory; persist best-effort.
1218
- RunicLog.logger("zai-token-store").error("Failed to persist z.ai token: \(error)")
1219
- }
1220
- }
1221
- }
1222
-
1223
- private func schedulePersistMiniMaxAPIToken() {
1224
- self.minimaxTokenPersistTask?.cancel()
1225
- let token = self.minimaxAPIToken
1226
- let tokenStore = self.minimaxTokenStore
1227
- self.minimaxTokenPersistTask = Task { @MainActor in
1228
- do {
1229
- try await Task.sleep(nanoseconds: 350_000_000)
1230
- } catch {
1231
- return
1232
- }
1233
- guard !Task.isCancelled else { return }
1234
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1235
- do {
1236
- try tokenStore.storeToken(token)
1237
- return nil
1238
- } catch {
1239
- return error
1240
- }
1241
- }.value
1242
- if let error {
1243
- RunicLog.logger("minimax-token-store").error("Failed to persist MiniMax token: \(error)")
1244
- }
1245
- }
1246
- }
1247
-
1248
- private func schedulePersistMiniMaxCookieHeader() {
1249
- self.minimaxCookieHeaderPersistTask?.cancel()
1250
- let header = self.minimaxCookieHeader
1251
- let store = self.minimaxCookieHeaderStore
1252
- self.minimaxCookieHeaderPersistTask = Task { @MainActor in
1253
- do {
1254
- try await Task.sleep(nanoseconds: 350_000_000)
1255
- } catch {
1256
- return
1257
- }
1258
- guard !Task.isCancelled else { return }
1259
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1260
- do {
1261
- try store.storeHeader(header)
1262
- return nil
1263
- } catch {
1264
- return error
1265
- }
1266
- }.value
1267
- if let error {
1268
- RunicLog.logger("minimax-cookie-store").error("Failed to persist MiniMax cookie header: \(error)")
1269
- }
1270
- }
1271
- }
1272
-
1273
- private func schedulePersistMiniMaxGroupID() {
1274
- self.minimaxGroupIDPersistTask?.cancel()
1275
- let groupID = self.minimaxGroupID
1276
- let groupStore = self.minimaxGroupIDStore
1277
- self.minimaxGroupIDPersistTask = Task { @MainActor in
1278
- do {
1279
- try await Task.sleep(nanoseconds: 350_000_000)
1280
- } catch {
1281
- return
1282
- }
1283
- guard !Task.isCancelled else { return }
1284
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1285
- do {
1286
- try groupStore.storeGroupID(groupID)
1287
- return nil
1288
- } catch {
1289
- return error
1290
- }
1291
- }.value
1292
- if let error {
1293
- RunicLog.logger("minimax-groupid-store").error("Failed to persist MiniMax Group ID: \(error)")
1294
- }
1295
- }
1296
- }
1297
-
1298
- private func schedulePersistCopilotAPIToken() {
1299
- self.copilotTokenPersistTask?.cancel()
1300
- let token = self.copilotAPIToken
1301
- let tokenStore = self.copilotTokenStore
1302
- self.copilotTokenPersistTask = Task { @MainActor in
1303
- do {
1304
- try await Task.sleep(nanoseconds: 350_000_000)
1305
- } catch {
1306
- return
1307
- }
1308
- guard !Task.isCancelled else { return }
1309
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1310
- do {
1311
- try tokenStore.storeToken(token)
1312
- return nil
1313
- } catch {
1314
- return error
1315
- }
1316
- }.value
1317
- if let error {
1318
- RunicLog.logger("copilot-token-store").error("Failed to persist Copilot token: \(error)")
1319
- }
1320
- }
1321
- }
1322
-
1323
- private func schedulePersistOpenRouterAPIToken() {
1324
- self.openRouterTokenPersistTask?.cancel()
1325
- let token = self.openRouterAPIToken
1326
- let tokenStore = self.openRouterTokenStore
1327
- self.openRouterTokenPersistTask = Task { @MainActor in
1328
- do {
1329
- try await Task.sleep(nanoseconds: 350_000_000)
1330
- } catch {
1331
- return
1332
- }
1333
- guard !Task.isCancelled else { return }
1334
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1335
- do {
1336
- try tokenStore.storeToken(token)
1337
- return nil
1338
- } catch {
1339
- return error
1340
- }
1341
- }.value
1342
- if let error {
1343
- RunicLog.logger("openrouter-token-store").error("Failed to persist OpenRouter token: \(error)")
1344
- }
1345
- }
1346
- }
1347
-
1348
- private func schedulePersistGroqAPIToken() {
1349
- self.groqTokenPersistTask?.cancel()
1350
- let token = self.groqAPIToken
1351
- let tokenStore = self.groqTokenStore
1352
- self.groqTokenPersistTask = Task { @MainActor in
1353
- do {
1354
- try await Task.sleep(nanoseconds: 350_000_000)
1355
- } catch {
1356
- return
1357
- }
1358
- guard !Task.isCancelled else { return }
1359
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1360
- do {
1361
- try tokenStore.storeToken(token)
1362
- return nil
1363
- } catch {
1364
- return error
1365
- }
1366
- }.value
1367
- if let error {
1368
- RunicLog.logger("groq-token-store").error("Failed to persist Groq token: \(error)")
1369
- }
1370
- }
1371
- }
1372
-
1373
- private func schedulePersistDeepSeekAPIToken() {
1374
- self.deepSeekTokenPersistTask?.cancel()
1375
- let token = self.deepSeekAPIToken
1376
- let tokenStore = self.deepSeekTokenStore
1377
- self.deepSeekTokenPersistTask = Task { @MainActor in
1378
- do {
1379
- try await Task.sleep(nanoseconds: 350_000_000)
1380
- } catch {
1381
- return
1382
- }
1383
- guard !Task.isCancelled else { return }
1384
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1385
- do {
1386
- try tokenStore.storeToken(token)
1387
- return nil
1388
- } catch {
1389
- return error
1390
- }
1391
- }.value
1392
- if let error {
1393
- RunicLog.logger("deepseek-token-store").error("Failed to persist DeepSeek token: \(error)")
1394
- }
1395
- }
1396
- }
1397
-
1398
- private func schedulePersistFireworksAPIToken() {
1399
- self.fireworksTokenPersistTask?.cancel()
1400
- let token = self.fireworksAPIToken
1401
- let tokenStore = self.fireworksTokenStore
1402
- self.fireworksTokenPersistTask = Task { @MainActor in
1403
- do {
1404
- try await Task.sleep(nanoseconds: 350_000_000)
1405
- } catch {
1406
- return
1407
- }
1408
- guard !Task.isCancelled else { return }
1409
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1410
- do {
1411
- try tokenStore.storeToken(token)
1412
- return nil
1413
- } catch {
1414
- return error
1415
- }
1416
- }.value
1417
- if let error {
1418
- RunicLog.logger("fireworks-token-store").error("Failed to persist Fireworks token: \(error)")
1419
- }
1420
- }
1421
- }
1422
-
1423
- private func schedulePersistMistralAPIToken() {
1424
- self.mistralTokenPersistTask?.cancel()
1425
- let token = self.mistralAPIToken
1426
- let tokenStore = self.mistralTokenStore
1427
- self.mistralTokenPersistTask = Task { @MainActor in
1428
- do {
1429
- try await Task.sleep(nanoseconds: 350_000_000)
1430
- } catch {
1431
- return
1432
- }
1433
- guard !Task.isCancelled else { return }
1434
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1435
- do {
1436
- try tokenStore.storeToken(token)
1437
- return nil
1438
- } catch {
1439
- return error
1440
- }
1441
- }.value
1442
- if let error {
1443
- RunicLog.logger("mistral-token-store").error("Failed to persist Mistral token: \(error)")
1444
- }
1445
- }
1446
- }
1447
-
1448
- private func schedulePersistPerplexityAPIToken() {
1449
- self.perplexityTokenPersistTask?.cancel()
1450
- let token = self.perplexityAPIToken
1451
- let tokenStore = self.perplexityTokenStore
1452
- self.perplexityTokenPersistTask = Task { @MainActor in
1453
- do {
1454
- try await Task.sleep(nanoseconds: 350_000_000)
1455
- } catch {
1456
- return
1457
- }
1458
- guard !Task.isCancelled else { return }
1459
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1460
- do {
1461
- try tokenStore.storeToken(token)
1462
- return nil
1463
- } catch {
1464
- return error
1465
- }
1466
- }.value
1467
- if let error {
1468
- RunicLog.logger("perplexity-token-store").error("Failed to persist Perplexity token: \(error)")
1469
- }
1470
- }
1471
- }
1472
-
1473
- private func schedulePersistKimiAPIToken() {
1474
- self.kimiTokenPersistTask?.cancel()
1475
- let token = self.kimiAPIToken
1476
- let tokenStore = self.kimiTokenStore
1477
- self.kimiTokenPersistTask = Task { @MainActor in
1478
- do {
1479
- try await Task.sleep(nanoseconds: 350_000_000)
1480
- } catch {
1481
- return
1482
- }
1483
- guard !Task.isCancelled else { return }
1484
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1485
- do {
1486
- try tokenStore.storeToken(token)
1487
- return nil
1488
- } catch {
1489
- return error
1490
- }
1491
- }.value
1492
- if let error {
1493
- RunicLog.logger("kimi-token-store").error("Failed to persist Kimi token: \(error)")
1494
- }
1495
- }
1496
- }
1497
-
1498
- private func schedulePersistAuggieAPIToken() {
1499
- self.auggieTokenPersistTask?.cancel()
1500
- let token = self.auggieAPIToken
1501
- let tokenStore = self.auggieTokenStore
1502
- self.auggieTokenPersistTask = Task { @MainActor in
1503
- do {
1504
- try await Task.sleep(nanoseconds: 350_000_000)
1505
- } catch {
1506
- return
1507
- }
1508
- guard !Task.isCancelled else { return }
1509
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1510
- do {
1511
- try tokenStore.storeToken(token)
1512
- return nil
1513
- } catch {
1514
- return error
1515
- }
1516
- }.value
1517
- if let error {
1518
- RunicLog.logger("auggie-token-store").error("Failed to persist Auggie token: \(error)")
1519
- }
1520
- }
1521
- }
1522
-
1523
- private func schedulePersistTogetherAPIToken() {
1524
- self.togetherTokenPersistTask?.cancel()
1525
- let token = self.togetherAPIToken
1526
- let tokenStore = self.togetherTokenStore
1527
- self.togetherTokenPersistTask = Task { @MainActor in
1528
- do {
1529
- try await Task.sleep(nanoseconds: 350_000_000)
1530
- } catch {
1531
- return
1532
- }
1533
- guard !Task.isCancelled else { return }
1534
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1535
- do {
1536
- try tokenStore.storeToken(token)
1537
- return nil
1538
- } catch {
1539
- return error
1540
- }
1541
- }.value
1542
- if let error {
1543
- RunicLog.logger("together-token-store").error("Failed to persist Together token: \(error)")
1544
- }
1545
- }
1546
- }
1547
-
1548
- private func schedulePersistCohereAPIToken() {
1549
- self.cohereTokenPersistTask?.cancel()
1550
- let token = self.cohereAPIToken
1551
- let tokenStore = self.cohereTokenStore
1552
- self.cohereTokenPersistTask = Task { @MainActor in
1553
- do {
1554
- try await Task.sleep(nanoseconds: 350_000_000)
1555
- } catch {
1556
- return
1557
- }
1558
- guard !Task.isCancelled else { return }
1559
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1560
- do {
1561
- try tokenStore.storeToken(token)
1562
- return nil
1563
- } catch {
1564
- return error
1565
- }
1566
- }.value
1567
- if let error {
1568
- RunicLog.logger("cohere-token-store").error("Failed to persist Cohere token: \(error)")
1569
- }
1570
- }
1571
- }
1572
-
1573
- private func schedulePersistXAiAPIToken() {
1574
- self.xaiTokenPersistTask?.cancel()
1575
- let token = self.xaiAPIToken
1576
- let tokenStore = self.xaiTokenStore
1577
- self.xaiTokenPersistTask = Task { @MainActor in
1578
- do {
1579
- try await Task.sleep(nanoseconds: 350_000_000)
1580
- } catch {
1581
- return
1582
- }
1583
- guard !Task.isCancelled else { return }
1584
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1585
- do {
1586
- try tokenStore.storeToken(token)
1587
- return nil
1588
- } catch {
1589
- return error
1590
- }
1591
- }.value
1592
- if let error {
1593
- RunicLog.logger("xai-token-store").error("Failed to persist xAI token: \(error)")
1594
- }
1595
- }
1596
- }
1597
-
1598
- private func schedulePersistCerebrasAPIToken() {
1599
- self.cerebrasTokenPersistTask?.cancel()
1600
- let token = self.cerebrasAPIToken
1601
- let tokenStore = self.cerebrasTokenStore
1602
- self.cerebrasTokenPersistTask = Task { @MainActor in
1603
- do {
1604
- try await Task.sleep(nanoseconds: 350_000_000)
1605
- } catch {
1606
- return
1607
- }
1608
- guard !Task.isCancelled else { return }
1609
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1610
- do {
1611
- try tokenStore.storeToken(token)
1612
- return nil
1613
- } catch {
1614
- return error
1615
- }
1616
- }.value
1617
- if let error {
1618
- RunicLog.logger("cerebras-token-store").error("Failed to persist Cerebras token: \(error)")
1619
- }
1620
- }
1621
- }
1622
-
1623
- private func schedulePersistSambaNovaAPIToken() {
1624
- self.sambaNovaTokenPersistTask?.cancel()
1625
- let token = self.sambaNovaAPIToken
1626
- let tokenStore = self.sambaNovaTokenStore
1627
- self.sambaNovaTokenPersistTask = Task { @MainActor in
1628
- do {
1629
- try await Task.sleep(nanoseconds: 350_000_000)
1630
- } catch {
1631
- return
1632
- }
1633
- guard !Task.isCancelled else { return }
1634
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1635
- do {
1636
- try tokenStore.storeToken(token)
1637
- return nil
1638
- } catch {
1639
- return error
1640
- }
1641
- }.value
1642
- if let error {
1643
- RunicLog.logger("sambanova-token-store").error("Failed to persist SambaNova token: \(error)")
1644
- }
1645
- }
1646
- }
1647
-
1648
- private func schedulePersistAzureOpenAIAPIToken() {
1649
- self.azureOpenAITokenPersistTask?.cancel()
1650
- let token = self.azureOpenAIAPIToken
1651
- let tokenStore = self.azureOpenAITokenStore
1652
- self.azureOpenAITokenPersistTask = Task { @MainActor in
1653
- do {
1654
- try await Task.sleep(nanoseconds: 350_000_000)
1655
- } catch {
1656
- return
1657
- }
1658
- guard !Task.isCancelled else { return }
1659
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1660
- do {
1661
- try tokenStore.storeToken(token)
1662
- return nil
1663
- } catch {
1664
- return error
1665
- }
1666
- }.value
1667
- if let error {
1668
- RunicLog.logger("azure-openai-token-store")
1669
- .error("Failed to persist Azure OpenAI token: \(error)")
1670
- }
1671
- }
1672
- }
1673
-
1674
- private func schedulePersistQwenAPIToken() {
1675
- self.qwenTokenPersistTask?.cancel()
1676
- let token = self.qwenAPIToken
1677
- let tokenStore = self.qwenTokenStore
1678
- self.qwenTokenPersistTask = Task { @MainActor in
1679
- do {
1680
- try await Task.sleep(nanoseconds: 350_000_000)
1681
- } catch {
1682
- return
1683
- }
1684
- guard !Task.isCancelled else { return }
1685
- let error: (any Error)? = await Task.detached(priority: .utility) { () -> (any Error)? in
1686
- do {
1687
- try tokenStore.storeToken(token)
1688
- return nil
1689
- } catch {
1690
- return error
1691
- }
1692
- }.value
1693
- if let error {
1694
- RunicLog.logger("qwen-token-store").error("Failed to persist Qwen token: \(error)")
1695
- }
1696
- }
1697
- }
1698
- }
1699
-
1700
- enum LaunchAtLoginManager {
1701
- @MainActor
1702
- static func setEnabled(_ enabled: Bool) {
1703
- guard #available(macOS 13, *) else { return }
1704
- let service = SMAppService.mainApp
1705
- if enabled {
1706
- try? service.register()
1707
- } else {
1708
- try? service.unregister()
1709
- }
1710
- }
1711
- }