@sriinnu/runic 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (584) hide show
  1. package/.swiftformat +50 -0
  2. package/.swiftlint.yml +160 -0
  3. package/CHANGELOG.md +307 -0
  4. package/CONTRIBUTING.md +456 -0
  5. package/Icon.icns +0 -0
  6. package/Icon.icon/Assets/runic.png +0 -0
  7. package/Icon.icon/icon.json +36 -0
  8. package/LICENSE +21 -0
  9. package/Package.resolved +51 -0
  10. package/Package.swift +136 -0
  11. package/Packages/Helix/LICENSE +21 -0
  12. package/Packages/Helix/Package.swift +29 -0
  13. package/Packages/Helix/README.md +976 -0
  14. package/Packages/Helix/Sources/Helix/Binding.swift +111 -0
  15. package/Packages/Helix/Sources/Helix/CommandParser.swift +160 -0
  16. package/Packages/Helix/Sources/Helix/CommandSignature.swift +117 -0
  17. package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +72 -0
  18. package/Packages/Helix/Sources/Helix/HelpGenerator.swift +175 -0
  19. package/Packages/Helix/Sources/Helix/NameSpecification.swift +98 -0
  20. package/Packages/Helix/Sources/Helix/ParsableCommand.swift +204 -0
  21. package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +44 -0
  22. package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +34 -0
  23. package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +91 -0
  24. package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +94 -0
  25. package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +167 -0
  26. package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +99 -0
  27. package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +146 -0
  28. package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +97 -0
  29. package/Packages/Helix/Sources/Helix/Program.swift +204 -0
  30. package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +367 -0
  31. package/Packages/Silo/LICENSE +21 -0
  32. package/Packages/Silo/Package.swift +34 -0
  33. package/Packages/Silo/README.md +466 -0
  34. package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +169 -0
  35. package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +365 -0
  36. package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +594 -0
  37. package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +326 -0
  38. package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +154 -0
  39. package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +119 -0
  40. package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +121 -0
  41. package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +391 -0
  42. package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +240 -0
  43. package/Packages/Silo/Sources/Silo/CryptoSupport.swift +86 -0
  44. package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +172 -0
  45. package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +172 -0
  46. package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +105 -0
  47. package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +191 -0
  48. package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +41 -0
  49. package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +110 -0
  50. package/README.md +109 -0
  51. package/RuniciOS/App/RuniciOSApp.swift +105 -0
  52. package/RuniciOS/Core/iOSUsageStore.swift +192 -0
  53. package/RuniciOS/Views/ProviderDetailView.swift +312 -0
  54. package/RuniciOS/Views/ProviderListView.swift +228 -0
  55. package/RuniciOS/Widgets/LargeWidget.swift +367 -0
  56. package/RuniciOS/Widgets/LiveActivity.swift +342 -0
  57. package/RuniciOS/Widgets/LockScreenWidget.swift +273 -0
  58. package/RuniciOS/Widgets/MediumWidget.swift +370 -0
  59. package/RuniciOS/Widgets/RunicWidget.swift +274 -0
  60. package/RuniciOS/Widgets/SmallWidget.swift +281 -0
  61. package/RuniciOS/Widgets/WidgetModels.swift +285 -0
  62. package/RuniciOS/Widgets/WidgetTimeline.swift +270 -0
  63. package/Scripts/CREATION-SUMMARY.md +367 -0
  64. package/Scripts/DIRECTORY-STRUCTURE.md +358 -0
  65. package/Scripts/GETTING-STARTED-CHECKLIST.md +364 -0
  66. package/Scripts/INDEX.md +365 -0
  67. package/Scripts/QUICK-REFERENCE.md +164 -0
  68. package/Scripts/README-SCRIPTS.md +926 -0
  69. package/Scripts/build-android.sh +526 -0
  70. package/Scripts/build-ios.sh +529 -0
  71. package/Scripts/build-macos.sh +603 -0
  72. package/Scripts/build-windows.sh +504 -0
  73. package/Scripts/build_icon.sh +49 -0
  74. package/Scripts/changelog-to-html.sh +89 -0
  75. package/Scripts/check-release-assets.sh +10 -0
  76. package/Scripts/ci-config.yml +474 -0
  77. package/Scripts/compile_and_run.sh +219 -0
  78. package/Scripts/compile_and_run_adhoc.sh +4 -0
  79. package/Scripts/docs-list.mjs +119 -0
  80. package/Scripts/make_appcast.sh +58 -0
  81. package/Scripts/package_app.sh +409 -0
  82. package/Scripts/release-lib.sh +214 -0
  83. package/Scripts/release.sh +76 -0
  84. package/Scripts/setup-all.sh +387 -0
  85. package/Scripts/setup-ios.sh +404 -0
  86. package/Scripts/setup-react-native.sh +523 -0
  87. package/Scripts/sign-and-notarize.sh +122 -0
  88. package/Scripts/test_live_update.sh +38 -0
  89. package/Scripts/validate_changelog.sh +23 -0
  90. package/Scripts/verify_appcast.sh +92 -0
  91. package/Sources/Runic/About.swift +93 -0
  92. package/Sources/Runic/AppNotifications.swift +115 -0
  93. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +657 -0
  94. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +518 -0
  95. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +810 -0
  96. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +87 -0
  97. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +1379 -0
  98. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +337 -0
  99. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +395 -0
  100. package/Sources/Runic/Controllers/StatusItemController.swift +360 -0
  101. package/Sources/Runic/Core/BudgetNotificationManager.swift +80 -0
  102. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +103 -0
  103. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +154 -0
  104. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +206 -0
  105. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +95 -0
  106. package/Sources/Runic/Core/Rendering/IconRenderer.swift +1216 -0
  107. package/Sources/Runic/Core/Rendering/IconView.swift +120 -0
  108. package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +71 -0
  109. package/Sources/Runic/Core/RunicAnimation.swift +53 -0
  110. package/Sources/Runic/Core/RunicColors.swift +73 -0
  111. package/Sources/Runic/Core/RunicCornerRadius.swift +15 -0
  112. package/Sources/Runic/Core/RunicSpacing.swift +68 -0
  113. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +143 -0
  114. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +143 -0
  115. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +143 -0
  116. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +143 -0
  117. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +143 -0
  118. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +158 -0
  119. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +143 -0
  120. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +143 -0
  121. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +143 -0
  122. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +143 -0
  123. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +143 -0
  124. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +143 -0
  125. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +143 -0
  126. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +143 -0
  127. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +143 -0
  128. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +143 -0
  129. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +30 -0
  130. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +143 -0
  131. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +143 -0
  132. package/Sources/Runic/Core/Stores/SettingsStore.swift +1702 -0
  133. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +143 -0
  134. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +20 -0
  135. package/Sources/Runic/Core/Stores/UsageStore+Status.swift +189 -0
  136. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +33 -0
  137. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +67 -0
  138. package/Sources/Runic/Core/Stores/UsageStore.swift +3564 -0
  139. package/Sources/Runic/Core/Stores/XAITokenStore.swift +143 -0
  140. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +143 -0
  141. package/Sources/Runic/Core/UsageExporter.swift +193 -0
  142. package/Sources/Runic/Date+RelativeDescription.swift +21 -0
  143. package/Sources/Runic/HiddenWindowView.swift +32 -0
  144. package/Sources/Runic/InstallOrigin.swift +9 -0
  145. package/Sources/Runic/KeyboardShortcuts+Names.swift +6 -0
  146. package/Sources/Runic/MouseLocationReader.swift +65 -0
  147. package/Sources/Runic/Notifications+Runic.swift +6 -0
  148. package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +503 -0
  149. package/Sources/Runic/ProviderRegistry.swift +100 -0
  150. package/Sources/Runic/ProviderSwitcherButtons.swift +267 -0
  151. package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +11 -0
  152. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +14 -0
  153. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +24 -0
  154. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +52 -0
  155. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +42 -0
  156. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +24 -0
  157. package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +26 -0
  158. package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +14 -0
  159. package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +18 -0
  160. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +15 -0
  161. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +24 -0
  162. package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +193 -0
  163. package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +110 -0
  164. package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +28 -0
  165. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +15 -0
  166. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +24 -0
  167. package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +12 -0
  168. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +15 -0
  169. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +24 -0
  170. package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +20 -0
  171. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +15 -0
  172. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +24 -0
  173. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +24 -0
  174. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +32 -0
  175. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +24 -0
  176. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +24 -0
  177. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +24 -0
  178. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +24 -0
  179. package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +15 -0
  180. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +48 -0
  181. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +66 -0
  182. package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +12 -0
  183. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +99 -0
  184. package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +18 -0
  185. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +24 -0
  186. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +24 -0
  187. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +24 -0
  188. package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +2 -0
  189. package/Sources/Runic/Resources/ProviderIcon-auggie.svg +3 -0
  190. package/Sources/Runic/Resources/ProviderIcon-azure.svg +5 -0
  191. package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +7 -0
  192. package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +3 -0
  193. package/Sources/Runic/Resources/ProviderIcon-claude.svg +1 -0
  194. package/Sources/Runic/Resources/ProviderIcon-codex.svg +1 -0
  195. package/Sources/Runic/Resources/ProviderIcon-cohere.svg +3 -0
  196. package/Sources/Runic/Resources/ProviderIcon-copilot.svg +1 -0
  197. package/Sources/Runic/Resources/ProviderIcon-cursor.svg +3 -0
  198. package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +4 -0
  199. package/Sources/Runic/Resources/ProviderIcon-factory.svg +3 -0
  200. package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +3 -0
  201. package/Sources/Runic/Resources/ProviderIcon-gemini.svg +1 -0
  202. package/Sources/Runic/Resources/ProviderIcon-groq.svg +1 -0
  203. package/Sources/Runic/Resources/ProviderIcon-kimi.svg +4 -0
  204. package/Sources/Runic/Resources/ProviderIcon-minimax.svg +1 -0
  205. package/Sources/Runic/Resources/ProviderIcon-mistral.svg +5 -0
  206. package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +7 -0
  207. package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +4 -0
  208. package/Sources/Runic/Resources/ProviderIcon-qwen.svg +9 -0
  209. package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +4 -0
  210. package/Sources/Runic/Resources/ProviderIcon-together.svg +5 -0
  211. package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +17 -0
  212. package/Sources/Runic/Resources/ProviderIcon-xai.svg +3 -0
  213. package/Sources/Runic/Resources/ProviderIcon-zai.svg +5 -0
  214. package/Sources/Runic/Resources/RunicMenubarIcon.svg +7 -0
  215. package/Sources/Runic/RunicApp.swift +357 -0
  216. package/Sources/Runic/SessionQuotaNotifications.swift +58 -0
  217. package/Sources/Runic/UsagePaceText.swift +52 -0
  218. package/Sources/Runic/UsageProgressBar.swift +224 -0
  219. package/Sources/Runic/Utilities/DisplayLink.swift +96 -0
  220. package/Sources/Runic/Utilities/LoadingPattern.swift +60 -0
  221. package/Sources/Runic/Utilities/PerformanceConstants.swift +85 -0
  222. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +256 -0
  223. package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +22 -0
  224. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +275 -0
  225. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +377 -0
  226. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +312 -0
  227. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +312 -0
  228. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +228 -0
  229. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +228 -0
  230. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +162 -0
  231. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +64 -0
  232. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +268 -0
  233. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +104 -0
  234. package/Sources/Runic/Views/Menu/MenuCardView.swift +1900 -0
  235. package/Sources/Runic/Views/Menu/MenuContent.swift +150 -0
  236. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +369 -0
  237. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +42 -0
  238. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +235 -0
  239. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +141 -0
  240. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +209 -0
  241. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +220 -0
  242. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +88 -0
  243. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +201 -0
  244. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +398 -0
  245. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +232 -0
  246. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +417 -0
  247. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +190 -0
  248. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +132 -0
  249. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +618 -0
  250. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +305 -0
  251. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +271 -0
  252. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +268 -0
  253. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +422 -0
  254. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +641 -0
  255. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +344 -0
  256. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +230 -0
  257. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +322 -0
  258. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +469 -0
  259. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +231 -0
  260. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +50 -0
  261. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +497 -0
  262. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +288 -0
  263. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +3435 -0
  264. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +25 -0
  265. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +59 -0
  266. package/Sources/Runic/Views/Preferences/PreferencesView.swift +206 -0
  267. package/Sources/Runic/Views/Rating/QualityRatingView.swift +238 -0
  268. package/Sources/Runic/Views/Team/TeamDashboardView.swift +371 -0
  269. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +175 -0
  270. package/Sources/Runic/Views/Team/TeamManagementView.swift +761 -0
  271. package/Sources/Runic/Views/Team/TeamMemberRow.swift +181 -0
  272. package/Sources/RunicCLI/CLIEntry.swift +942 -0
  273. package/Sources/RunicCLI/CLIRenderer.swift +87 -0
  274. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +588 -0
  275. package/Sources/RunicCLI/Commands/ModelsCommand.swift +362 -0
  276. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +439 -0
  277. package/Sources/RunicCLI/Commands/ResetCommand.swift +429 -0
  278. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +168 -0
  279. package/Sources/RunicClaudeWatchdog/main.swift +122 -0
  280. package/Sources/RunicClaudeWebProbe/main.swift +62 -0
  281. package/Sources/RunicCore/AlertRuleStore.swift +187 -0
  282. package/Sources/RunicCore/BrowserCookieImportOrder.swift +25 -0
  283. package/Sources/RunicCore/CopilotUsageModels.swift +84 -0
  284. package/Sources/RunicCore/CostUsageFetcher.swift +119 -0
  285. package/Sources/RunicCore/CostUsageModels.swift +479 -0
  286. package/Sources/RunicCore/CreditsModels.swift +50 -0
  287. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +269 -0
  288. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +622 -0
  289. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +500 -0
  290. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +679 -0
  291. package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +135 -0
  292. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +173 -0
  293. package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +79 -0
  294. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +86 -0
  295. package/Sources/RunicCore/Logging/RunicLog.swift +123 -0
  296. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +371 -0
  297. package/Sources/RunicCore/OpenAIDashboardModels.swift +210 -0
  298. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +676 -0
  299. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +492 -0
  300. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +51 -0
  301. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +483 -0
  302. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +558 -0
  303. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +204 -0
  304. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +78 -0
  305. package/Sources/RunicCore/PathEnvironment.swift +482 -0
  306. package/Sources/RunicCore/Performance/PerformanceModels.swift +371 -0
  307. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +409 -0
  308. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +178 -0
  309. package/Sources/RunicCore/ProjectBudgetStore.swift +99 -0
  310. package/Sources/RunicCore/ProviderCostSnapshot.swift +29 -0
  311. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +66 -0
  312. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +685 -0
  313. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +83 -0
  314. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +142 -0
  315. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +141 -0
  316. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +186 -0
  317. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +126 -0
  318. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +168 -0
  319. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +84 -0
  320. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +75 -0
  321. package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +332 -0
  322. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +180 -0
  323. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +137 -0
  324. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +207 -0
  325. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +654 -0
  326. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +28 -0
  327. package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +480 -0
  328. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +780 -0
  329. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +146 -0
  330. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +269 -0
  331. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +100 -0
  332. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +243 -0
  333. package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +125 -0
  334. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +24 -0
  335. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +181 -0
  336. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +83 -0
  337. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +85 -0
  338. package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +123 -0
  339. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +114 -0
  340. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +100 -0
  341. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +291 -0
  342. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +386 -0
  343. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +65 -0
  344. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +594 -0
  345. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +85 -0
  346. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +108 -0
  347. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +398 -0
  348. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +65 -0
  349. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +1275 -0
  350. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +84 -0
  351. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +75 -0
  352. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +65 -0
  353. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +835 -0
  354. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +83 -0
  355. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +71 -0
  356. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +83 -0
  357. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +77 -0
  358. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +92 -0
  359. package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +54 -0
  360. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +171 -0
  361. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +911 -0
  362. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +84 -0
  363. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +75 -0
  364. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +87 -0
  365. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +207 -0
  366. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +84 -0
  367. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +75 -0
  368. package/Sources/RunicCore/Providers/ProviderBranding.swift +25 -0
  369. package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +17 -0
  370. package/Sources/RunicCore/Providers/ProviderDescriptor.swift +129 -0
  371. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +263 -0
  372. package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +98 -0
  373. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +501 -0
  374. package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +68 -0
  375. package/Sources/RunicCore/Providers/Providers.swift +184 -0
  376. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +84 -0
  377. package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +30 -0
  378. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +317 -0
  379. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +84 -0
  380. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +75 -0
  381. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +83 -0
  382. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +75 -0
  383. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +123 -0
  384. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +48 -0
  385. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +144 -0
  386. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +83 -0
  387. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +75 -0
  388. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +72 -0
  389. package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +41 -0
  390. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +724 -0
  391. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +385 -0
  392. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +310 -0
  393. package/Sources/RunicCore/Sync/SyncProtocol.swift +288 -0
  394. package/Sources/RunicCore/Sync/SyncRecord.swift +405 -0
  395. package/Sources/RunicCore/Sync/SyncUsageExample.swift +294 -0
  396. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +775 -0
  397. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +333 -0
  398. package/Sources/RunicCore/Team/TeamModels.swift +583 -0
  399. package/Sources/RunicCore/Team/TeamShowbackExporter.swift +495 -0
  400. package/Sources/RunicCore/Team/TeamStore.swift +792 -0
  401. package/Sources/RunicCore/TextParsing.swift +55 -0
  402. package/Sources/RunicCore/UsageFetcher.swift +694 -0
  403. package/Sources/RunicCore/UsageFormatter.swift +410 -0
  404. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +329 -0
  405. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +448 -0
  406. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +550 -0
  407. package/Sources/RunicCore/UsageLedger/UsageLedger.swift +29 -0
  408. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +660 -0
  409. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +209 -0
  410. package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +417 -0
  411. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +245 -0
  412. package/Sources/RunicCore/UsagePace.swift +81 -0
  413. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +73 -0
  414. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +74 -0
  415. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +208 -0
  416. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +455 -0
  417. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +132 -0
  418. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +589 -0
  419. package/Sources/RunicCore/WidgetSnapshot.swift +183 -0
  420. package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +14 -0
  421. package/Sources/RunicMacros/ProviderRegistrationMacros.swift +216 -0
  422. package/Sources/RunicWidget/RunicWidgetBundle.swift +79 -0
  423. package/Sources/RunicWidget/RunicWidgetProvider.swift +283 -0
  424. package/Sources/RunicWidget/RunicWidgetViews.swift +680 -0
  425. package/Tests/RunicTests/AntigravityStatusProbeTests.swift +50 -0
  426. package/Tests/RunicTests/AppDelegateTests.swift +43 -0
  427. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +22 -0
  428. package/Tests/RunicTests/CLIArgumentParsingTests.swift +70 -0
  429. package/Tests/RunicTests/CLICostTests.swift +65 -0
  430. package/Tests/RunicTests/CLIProviderSelectionTests.swift +22 -0
  431. package/Tests/RunicTests/CLISnapshotTests.swift +72 -0
  432. package/Tests/RunicTests/CLIWebFallbackTests.swift +58 -0
  433. package/Tests/RunicTests/ClaudeOAuthTests.swift +164 -0
  434. package/Tests/RunicTests/ClaudeResilienceTests.swift +30 -0
  435. package/Tests/RunicTests/ClaudeUsageTests.swift +370 -0
  436. package/Tests/RunicTests/CodexOAuthTests.swift +122 -0
  437. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +82 -0
  438. package/Tests/RunicTests/CopilotTokenResolverTests.swift +75 -0
  439. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +75 -0
  440. package/Tests/RunicTests/CostUsageDecodingTests.swift +394 -0
  441. package/Tests/RunicTests/CostUsagePricingTests.swift +50 -0
  442. package/Tests/RunicTests/CostUsageScannerTests.swift +700 -0
  443. package/Tests/RunicTests/CursorStatusProbeTests.swift +323 -0
  444. package/Tests/RunicTests/FactoryStatusProbeTests.swift +69 -0
  445. package/Tests/RunicTests/GeminiAPITestHelpers.swift +94 -0
  446. package/Tests/RunicTests/GeminiLoginAlertTests.swift +28 -0
  447. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +283 -0
  448. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +279 -0
  449. package/Tests/RunicTests/GeminiStatusProbeTests.swift +233 -0
  450. package/Tests/RunicTests/GeminiTestEnvironment.swift +87 -0
  451. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +73 -0
  452. package/Tests/RunicTests/InstallOriginTests.swift +18 -0
  453. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +9 -0
  454. package/Tests/RunicTests/LiveAccountTests.swift +24 -0
  455. package/Tests/RunicTests/LoadingPatternTests.swift +35 -0
  456. package/Tests/RunicTests/MenuCardModelTests.swift +545 -0
  457. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +8 -0
  458. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +8 -0
  459. package/Tests/RunicTests/MiniMaxParsingTests.swift +68 -0
  460. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +8 -0
  461. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +112 -0
  462. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +17 -0
  463. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +75 -0
  464. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +27 -0
  465. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +134 -0
  466. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +53 -0
  467. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +92 -0
  468. package/Tests/RunicTests/PathBuilderTests.swift +237 -0
  469. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +160 -0
  470. package/Tests/RunicTests/ProviderIconResourcesTests.swift +38 -0
  471. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +16 -0
  472. package/Tests/RunicTests/ProviderRegistryTests.swift +29 -0
  473. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +202 -0
  474. package/Tests/RunicTests/ProviderToggleStoreTests.swift +51 -0
  475. package/Tests/RunicTests/RunicTests.swift +120 -0
  476. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +36 -0
  477. package/Tests/RunicTests/SettingsStoreTests.swift +161 -0
  478. package/Tests/RunicTests/StatusItemAnimationTests.swift +49 -0
  479. package/Tests/RunicTests/StatusMenuTests.swift +434 -0
  480. package/Tests/RunicTests/StatusProbeTests.swift +377 -0
  481. package/Tests/RunicTests/SubprocessRunnerTests.swift +20 -0
  482. package/Tests/RunicTests/SubscriptionDetectionTests.swift +65 -0
  483. package/Tests/RunicTests/TTYCommandRunnerTests.swift +148 -0
  484. package/Tests/RunicTests/TTYIntegrationTests.swift +41 -0
  485. package/Tests/RunicTests/TeamShowbackExporterTests.swift +159 -0
  486. package/Tests/RunicTests/TextParsingTests.swift +12 -0
  487. package/Tests/RunicTests/UsageFormatterTests.swift +112 -0
  488. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +172 -0
  489. package/Tests/RunicTests/UsageLedgerForecastTests.swift +213 -0
  490. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +83 -0
  491. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +170 -0
  492. package/Tests/RunicTests/UsagePaceTests.swift +77 -0
  493. package/Tests/RunicTests/UsagePaceTextTests.swift +109 -0
  494. package/Tests/RunicTests/UsagePolicyEngineTests.swift +91 -0
  495. package/Tests/RunicTests/WidgetSnapshotTests.swift +45 -0
  496. package/Tests/RunicTests/ZaiProviderTests.swift +61 -0
  497. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +6 -0
  498. package/TestsLinux/PlatformGatingTests.swift +46 -0
  499. package/appcast.xml +105 -0
  500. package/assets/logo-concept-stack.svg +24 -0
  501. package/assets/logo-minimal-bars.svg +15 -0
  502. package/assets/logo.svg +20 -0
  503. package/assets/logo2.svg +43 -0
  504. package/bin/docs-list +5 -0
  505. package/bin/install-runic-cli.sh +32 -0
  506. package/build-and-run.sh +369 -0
  507. package/docs/.nojekyll +1 -0
  508. package/docs/CNAME +1 -0
  509. package/docs/antigravity.md +75 -0
  510. package/docs/architecture.md +32 -0
  511. package/docs/claude.md +93 -0
  512. package/docs/cli-commands/integration_guide.md +371 -0
  513. package/docs/cli-commands/quick_reference.md +362 -0
  514. package/docs/cli-commands/readme.md +361 -0
  515. package/docs/cli.md +156 -0
  516. package/docs/cloudkit_sync_guide.md +495 -0
  517. package/docs/codebase_review.md +288 -0
  518. package/docs/codex-oauth.md +635 -0
  519. package/docs/codex.md +104 -0
  520. package/docs/codexbar.png +0 -0
  521. package/docs/copilot.md +44 -0
  522. package/docs/cursor.md +46 -0
  523. package/docs/design/designcomparisonchart.md +510 -0
  524. package/docs/design/implementationguide.md +792 -0
  525. package/docs/design/menubaricondesignconcepts.md +796 -0
  526. package/docs/design/mockups/hybridiconconcept.svg +62 -0
  527. package/docs/design/mockups/infinityiconconcept.svg +41 -0
  528. package/docs/design/mockups/infinityiconstates.svg +129 -0
  529. package/docs/design/mockups/wavelogorefined.svg +48 -0
  530. package/docs/design/quick_summary.md +426 -0
  531. package/docs/design/readme.md +478 -0
  532. package/docs/design/sizespecifications.md +575 -0
  533. package/docs/design-system.md +836 -0
  534. package/docs/factory.md +103 -0
  535. package/docs/gemini.md +73 -0
  536. package/docs/icon.md +37 -0
  537. package/docs/icon.png +0 -0
  538. package/docs/index.html +175 -0
  539. package/docs/keychain_fix.md +274 -0
  540. package/docs/minimax.md +71 -0
  541. package/docs/packaging.md +25 -0
  542. package/docs/progress-bar-improvements.md +240 -0
  543. package/docs/progress-bar-quick-reference.md +283 -0
  544. package/docs/progress-bar-technical-details.md +432 -0
  545. package/docs/progress-bar-visual-test.swift +375 -0
  546. package/docs/provider.md +202 -0
  547. package/docs/providers.md +86 -0
  548. package/docs/refactor/macros.md +35 -0
  549. package/docs/refresh-loop.md +22 -0
  550. package/docs/releasing-homebrew.md +35 -0
  551. package/docs/releasing.md +121 -0
  552. package/docs/research/concrete_todo_2026-02-22.md +47 -0
  553. package/docs/research/core-feature-research.md +248 -0
  554. package/docs/research/feature_uiux_roadmap_2026-02-22.md +202 -0
  555. package/docs/research/parity_matrix.md +49 -0
  556. package/docs/research/provider_expansion_candidates_2026-02-22.md +80 -0
  557. package/docs/research/provider_model_token_project_matrix_2026-02-23.md +46 -0
  558. package/docs/research/release_gate_checklist.md +57 -0
  559. package/docs/site.css +505 -0
  560. package/docs/sparkle.md +28 -0
  561. package/docs/status.md +28 -0
  562. package/docs/sync/implementation.md +393 -0
  563. package/docs/sync/readme.md +317 -0
  564. package/docs/sync_files_summary.md +391 -0
  565. package/docs/sync_quick_reference.md +225 -0
  566. package/docs/todo.md +11 -0
  567. package/docs/ui.md +29 -0
  568. package/docs/web-integration.md +9 -0
  569. package/docs/widgets.md +85 -0
  570. package/docs/workflows/error-handling-improvements.md +434 -0
  571. package/docs/workflows/folder_structure.md +299 -0
  572. package/docs/workflows/loading_states.md +446 -0
  573. package/docs/workflows/loading_states_examples.md +565 -0
  574. package/docs/workflows/loading_states_flow.md +430 -0
  575. package/docs/workflows/performance_optimizations.md +457 -0
  576. package/docs/workflows/phase1-cli-testing-report.md +313 -0
  577. package/docs/workflows/quick_reference.md +228 -0
  578. package/docs/workflows/readme.md +59 -0
  579. package/docs/workflows/recent_improvements.md +275 -0
  580. package/docs/workflows/sync_enablement_summary.md +400 -0
  581. package/docs/zai.md +43 -0
  582. package/package.json +36 -0
  583. package/runic.png +0 -0
  584. package/version.env +2 -0
@@ -0,0 +1,84 @@
1
+ import RunicMacroSupport
2
+ import Foundation
3
+
4
+ @ProviderDescriptorRegistration
5
+ @ProviderDescriptorDefinition
6
+ public enum MistralProviderDescriptor {
7
+ static func makeDescriptor() -> ProviderDescriptor {
8
+ ProviderDescriptor(
9
+ id: .mistral,
10
+ metadata: ProviderMetadata(
11
+ id: .mistral,
12
+ displayName: "Mistral",
13
+ sessionLabel: "Models",
14
+ weeklyLabel: "",
15
+ opusLabel: nil,
16
+ supportsOpus: false,
17
+ supportsCredits: false,
18
+ creditsHint: "Shows model availability from Mistral API.",
19
+ toggleTitle: "Show Mistral usage",
20
+ cliName: "mistral",
21
+ defaultEnabled: false,
22
+ isPrimaryProvider: false,
23
+ usesAccountFallback: false,
24
+ dashboardURL: "https://console.mistral.ai",
25
+ statusPageURL: nil,
26
+ usageCoverage: ProviderUsageCoverage(
27
+ supportsModelBreakdown: true,
28
+ supportsTokenMetrics: false,
29
+ supportsProjectAttribution: false)),
30
+ branding: ProviderBranding(
31
+ iconStyle: .mistral,
32
+ iconResourceName: "ProviderIcon-mistral",
33
+ color: ProviderColor(red: 255 / 255, green: 90 / 255, blue: 52 / 255)),
34
+ tokenCost: ProviderTokenCostConfig(
35
+ supportsTokenCost: false,
36
+ noDataMessage: { "Mistral cost summary is not supported." }),
37
+ fetchPlan: ProviderFetchPlan(
38
+ sourceModes: [.auto, .cli],
39
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [MistralAPIFetchStrategy()] })),
40
+ cli: ProviderCLIConfig(
41
+ name: "mistral",
42
+ aliases: [],
43
+ versionDetector: nil))
44
+ }
45
+ }
46
+
47
+ struct MistralAPIFetchStrategy: ProviderFetchStrategy {
48
+ let id: String = "mistral.api"
49
+ let kind: ProviderFetchKind = .apiToken
50
+
51
+ func isAvailable(_ context: ProviderFetchContext) async -> Bool {
52
+ Self.resolveTokenResolution(environment: context.env) != nil
53
+ }
54
+
55
+ func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
56
+ guard let tokenRes = Self.resolveTokenResolution(environment: context.env) else {
57
+ throw MistralSettingsError.missingToken
58
+ }
59
+ let usage = try await MistralUsageFetcher.fetchModels(apiKey: tokenRes.token)
60
+ return self.makeResult(
61
+ usage: usage.toUsageSnapshot(),
62
+ sourceLabel: tokenRes.source.rawValue)
63
+ }
64
+
65
+ func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
66
+ false
67
+ }
68
+
69
+ private static func resolveTokenResolution(environment: [String: String]) -> ProviderTokenResolution? {
70
+ ProviderTokenResolver.mistralResolution(environment: environment)
71
+ }
72
+ }
73
+
74
+ enum MistralSettingsError: LocalizedError, Sendable {
75
+ case missingToken
76
+
77
+ var errorDescription: String? {
78
+ switch self {
79
+ case .missingToken:
80
+ "Mistral API key not found. Set it in Preferences → Providers → Mistral or " +
81
+ "export MISTRAL_API_KEY."
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,75 @@
1
+ import Foundation
2
+
3
+ struct MistralModelsResponse: Decodable {
4
+ let data: [Model]?
5
+
6
+ struct Model: Decodable {
7
+ let id: String?
8
+ }
9
+ }
10
+
11
+ struct MistralUsageFetcher {
12
+ static let apiURL = URL(string: "https://api.mistral.ai/v1/models")!
13
+
14
+ static func fetchModels(apiKey: String) async throws -> MistralModelsResponse {
15
+ var request = URLRequest(url: apiURL)
16
+ request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
17
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
18
+
19
+ let (data, response) = try await URLSession.shared.data(for: request)
20
+
21
+ guard let httpResponse = response as? HTTPURLResponse else {
22
+ throw MistralAPIError.invalidResponse
23
+ }
24
+
25
+ guard httpResponse.statusCode == 200 else {
26
+ let body = String(data: data, encoding: .utf8)?
27
+ .trimmingCharacters(in: .whitespacesAndNewlines)
28
+ throw MistralAPIError.httpError(
29
+ statusCode: httpResponse.statusCode,
30
+ body: body?.isEmpty == false ? body : nil)
31
+ }
32
+
33
+ let decoder = JSONDecoder()
34
+ return try decoder.decode(MistralModelsResponse.self, from: data)
35
+ }
36
+ }
37
+
38
+ extension MistralModelsResponse {
39
+ func toUsageSnapshot() -> UsageSnapshot {
40
+ let models = (self.data ?? []).compactMap(\.id)
41
+ var summary = "Models available: \(models.count)"
42
+ let preview = models.prefix(3).joined(separator: ", ")
43
+ if !preview.isEmpty {
44
+ summary += " (\(preview))"
45
+ }
46
+
47
+ return UsageSnapshot(
48
+ primary: RateWindow(
49
+ usedPercent: 0,
50
+ windowMinutes: nil,
51
+ resetsAt: nil,
52
+ resetDescription: summary),
53
+ secondary: nil,
54
+ tertiary: nil,
55
+ updatedAt: Date(),
56
+ identity: nil)
57
+ }
58
+ }
59
+
60
+ enum MistralAPIError: LocalizedError, Sendable {
61
+ case invalidResponse
62
+ case httpError(statusCode: Int, body: String?)
63
+
64
+ var errorDescription: String? {
65
+ switch self {
66
+ case .invalidResponse:
67
+ return "Invalid response from Mistral API"
68
+ case let .httpError(statusCode, body):
69
+ if let body, !body.isEmpty {
70
+ return "Mistral API returned status code \(statusCode): \(body)"
71
+ }
72
+ return "Mistral API returned status code \(statusCode)"
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,87 @@
1
+ import RunicMacroSupport
2
+ import Foundation
3
+
4
+ @ProviderDescriptorRegistration
5
+ @ProviderDescriptorDefinition
6
+ public enum OpenRouterProviderDescriptor {
7
+ static func makeDescriptor() -> ProviderDescriptor {
8
+ ProviderDescriptor(
9
+ id: .openrouter,
10
+ metadata: ProviderMetadata(
11
+ id: .openrouter,
12
+ displayName: "OpenRouter",
13
+ sessionLabel: "Credits",
14
+ weeklyLabel: "",
15
+ opusLabel: nil,
16
+ supportsOpus: false,
17
+ supportsCredits: true,
18
+ creditsHint: "Shows credits from OpenRouter API",
19
+ toggleTitle: "Show OpenRouter usage",
20
+ cliName: "openrouter",
21
+ defaultEnabled: false,
22
+ isPrimaryProvider: false,
23
+ usesAccountFallback: false,
24
+ dashboardURL: "https://openrouter.ai/account",
25
+ statusPageURL: nil,
26
+ usageCoverage: ProviderUsageCoverage(
27
+ supportsModelBreakdown: false,
28
+ supportsTokenMetrics: false,
29
+ supportsProjectAttribution: false)),
30
+ branding: ProviderBranding(
31
+ iconStyle: .openrouter,
32
+ iconResourceName: "ProviderIcon-openrouter",
33
+ color: ProviderColor(red: 255 / 255, green: 90 / 255, blue: 0 / 255)),
34
+ tokenCost: ProviderTokenCostConfig(
35
+ supportsTokenCost: false,
36
+ noDataMessage: { "OpenRouter cost summary is not supported." }),
37
+ fetchPlan: ProviderFetchPlan(
38
+ sourceModes: [.auto, .cli],
39
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [OpenRouterAPIFetchStrategy()] })),
40
+ cli: ProviderCLIConfig(
41
+ name: "openrouter",
42
+ aliases: ["or"],
43
+ versionDetector: nil))
44
+ }
45
+ }
46
+
47
+ struct OpenRouterAPIFetchStrategy: ProviderFetchStrategy {
48
+ let id: String = "openrouter.api"
49
+ let kind: ProviderFetchKind = .apiToken
50
+
51
+ func isAvailable(_ context: ProviderFetchContext) async -> Bool {
52
+ Self.resolveToken(environment: context.env) != nil
53
+ }
54
+
55
+ func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
56
+ guard let apiKey = Self.resolveToken(environment: context.env) else {
57
+ throw OpenRouterSettingsError.missingToken
58
+ }
59
+ let (credits, keyInfo) = try await OpenRouterUsageFetcher.fetchAll(apiKey: apiKey)
60
+ return self.makeResult(
61
+ usage: credits.toUsageSnapshot(keyInfo: keyInfo),
62
+ credits: credits.toCreditsSnapshot(),
63
+ sourceLabel: "api")
64
+ }
65
+
66
+ func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
67
+ false
68
+ }
69
+
70
+ private static func resolveToken(environment: [String: String]) -> String? {
71
+ ProviderTokenResolver.openRouterToken(environment: environment)
72
+ }
73
+ }
74
+
75
+ // MARK: - Settings Errors
76
+
77
+ enum OpenRouterSettingsError: LocalizedError, Sendable {
78
+ case missingToken
79
+
80
+ var errorDescription: String? {
81
+ switch self {
82
+ case .missingToken:
83
+ "OpenRouter API key not found. Set it in Preferences → Providers → OpenRouter or " +
84
+ "export OPENROUTER_API_KEY."
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,207 @@
1
+ import Foundation
2
+
3
+ // MARK: - API response models
4
+
5
+ struct OpenRouterCreditsResponse: Decodable {
6
+ let data: CreditData?
7
+ let credits: Double?
8
+
9
+ struct CreditData: Decodable {
10
+ let credits: Double?
11
+ let total_credits: Double?
12
+ let total_usage: Double?
13
+ }
14
+
15
+ /// Resolved total credits purchased.
16
+ var totalCredits: Double {
17
+ self.data?.total_credits ?? self.data?.credits ?? self.credits ?? 0
18
+ }
19
+
20
+ /// Resolved total credits consumed.
21
+ var totalUsage: Double {
22
+ self.data?.total_usage ?? 0
23
+ }
24
+
25
+ /// Remaining balance.
26
+ var remaining: Double {
27
+ max(0, self.totalCredits - self.totalUsage)
28
+ }
29
+
30
+ /// Usage percentage (0–100).
31
+ var usedPercent: Double {
32
+ guard self.totalCredits > 0 else { return 0 }
33
+ return min(100, max(0, (self.totalUsage / self.totalCredits) * 100))
34
+ }
35
+ }
36
+
37
+ /// Response from /api/v1/auth/key — lightweight key info.
38
+ struct OpenRouterKeyInfoResponse: Decodable {
39
+ let data: KeyData?
40
+
41
+ struct KeyData: Decodable {
42
+ let label: String?
43
+ let usage: Double?
44
+ let limit: Double?
45
+ let is_free_tier: Bool?
46
+ let rate_limit: RateLimit?
47
+
48
+ struct RateLimit: Decodable {
49
+ let requests: Int?
50
+ let interval: String?
51
+ }
52
+ }
53
+
54
+ var keyUsage: Double { self.data?.usage ?? 0 }
55
+ var keyLimit: Double? { self.data?.limit }
56
+ var isFreeTier: Bool { self.data?.is_free_tier ?? false }
57
+ var rateLimitRequests: Int? { self.data?.rate_limit?.requests }
58
+ var rateLimitInterval: String? { self.data?.rate_limit?.interval }
59
+ }
60
+
61
+ // MARK: - Fetcher
62
+
63
+ struct OpenRouterUsageFetcher {
64
+ static let creditsURL = URL(string: "https://openrouter.ai/api/v1/credits")!
65
+ static let keyInfoURL = URL(string: "https://openrouter.ai/api/v1/auth/key")!
66
+ private static let requestTimeout: TimeInterval = 15
67
+ private static let log = RunicLog.logger("openrouter-usage")
68
+
69
+ /// Fetches credits + key info in parallel for maximum data.
70
+ static func fetchAll(apiKey: String) async throws -> (OpenRouterCreditsResponse, OpenRouterKeyInfoResponse?) {
71
+ async let creditsTask = Self.fetchCredits(apiKey: apiKey)
72
+ async let keyInfoTask = Self.fetchKeyInfoBestEffort(apiKey: apiKey)
73
+
74
+ let credits = try await creditsTask
75
+ let keyInfo = await keyInfoTask
76
+ return (credits, keyInfo)
77
+ }
78
+
79
+ static func fetchCredits(apiKey: String) async throws -> OpenRouterCreditsResponse {
80
+ var request = URLRequest(url: creditsURL)
81
+ request.timeoutInterval = Self.requestTimeout
82
+ request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
83
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
84
+
85
+ let (data, response) = try await URLSession.shared.data(for: request)
86
+
87
+ guard let httpResponse = response as? HTTPURLResponse else {
88
+ throw OpenRouterAPIError.invalidResponse
89
+ }
90
+ guard httpResponse.statusCode == 200 else {
91
+ throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
92
+ }
93
+
94
+ if let json = String(data: data, encoding: .utf8) {
95
+ Self.log.debug("OpenRouter credits response: \(json)")
96
+ }
97
+
98
+ return try JSONDecoder().decode(OpenRouterCreditsResponse.self, from: data)
99
+ }
100
+
101
+ private static func fetchKeyInfoBestEffort(apiKey: String) async -> OpenRouterKeyInfoResponse? {
102
+ do {
103
+ return try await Self.fetchKeyInfo(apiKey: apiKey)
104
+ } catch {
105
+ Self.log.info("OpenRouter key info unavailable: \(error.localizedDescription)")
106
+ return nil
107
+ }
108
+ }
109
+
110
+ static func fetchKeyInfo(apiKey: String) async throws -> OpenRouterKeyInfoResponse {
111
+ var request = URLRequest(url: keyInfoURL)
112
+ request.timeoutInterval = Self.requestTimeout
113
+ request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
114
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
115
+
116
+ let (data, response) = try await URLSession.shared.data(for: request)
117
+
118
+ guard let httpResponse = response as? HTTPURLResponse else {
119
+ throw OpenRouterAPIError.invalidResponse
120
+ }
121
+ guard httpResponse.statusCode == 200 else {
122
+ throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
123
+ }
124
+
125
+ if let json = String(data: data, encoding: .utf8) {
126
+ Self.log.debug("OpenRouter key info response: \(json)")
127
+ }
128
+
129
+ return try JSONDecoder().decode(OpenRouterKeyInfoResponse.self, from: data)
130
+ }
131
+
132
+ }
133
+
134
+ // MARK: - Snapshot conversion
135
+
136
+ extension OpenRouterCreditsResponse {
137
+ func toUsageSnapshot(keyInfo: OpenRouterKeyInfoResponse? = nil) -> UsageSnapshot {
138
+ let balance = self.remaining
139
+ let used = self.totalUsage
140
+ let total = self.totalCredits
141
+ let percent = self.usedPercent
142
+
143
+ let balanceStr = String(format: "$%.2f", balance)
144
+ let usedStr = String(format: "$%.2f", used)
145
+
146
+ var resetDesc = "Balance: \(balanceStr)"
147
+ if used > 0 {
148
+ resetDesc += " · Spent: \(usedStr)"
149
+ }
150
+
151
+ var identityMethod: String?
152
+ if let keyInfo {
153
+ var parts: [String] = []
154
+ if keyInfo.isFreeTier { parts.append("Free tier") }
155
+ if let label = keyInfo.data?.label, !label.isEmpty { parts.append(label) }
156
+ if let rpm = keyInfo.rateLimitRequests, let interval = keyInfo.rateLimitInterval {
157
+ parts.append("\(rpm) req/\(interval)")
158
+ }
159
+ if !parts.isEmpty { identityMethod = parts.joined(separator: " · ") }
160
+ }
161
+
162
+ let identity = identityMethod.map {
163
+ ProviderIdentitySnapshot(
164
+ providerID: .openrouter,
165
+ accountEmail: nil,
166
+ accountOrganization: nil,
167
+ loginMethod: $0)
168
+ }
169
+
170
+ return UsageSnapshot(
171
+ primary: RateWindow(
172
+ usedPercent: total > 0 ? percent : 0,
173
+ windowMinutes: nil,
174
+ resetsAt: nil,
175
+ resetDescription: resetDesc),
176
+ secondary: nil,
177
+ tertiary: nil,
178
+ updatedAt: Date(),
179
+ identity: identity)
180
+ }
181
+
182
+ func toCreditsSnapshot() -> CreditsSnapshot {
183
+ CreditsSnapshot(
184
+ remaining: self.remaining,
185
+ events: [],
186
+ updatedAt: Date())
187
+ }
188
+ }
189
+
190
+ // MARK: - Errors
191
+
192
+ enum OpenRouterAPIError: LocalizedError, Sendable {
193
+ case invalidResponse
194
+ case httpError(statusCode: Int)
195
+ case decodingError
196
+
197
+ var errorDescription: String? {
198
+ switch self {
199
+ case .invalidResponse:
200
+ "Invalid response from OpenRouter API"
201
+ case let .httpError(statusCode):
202
+ "OpenRouter API returned status code \(statusCode)"
203
+ case .decodingError:
204
+ "Failed to decode OpenRouter API response"
205
+ }
206
+ }
207
+ }
@@ -0,0 +1,84 @@
1
+ import RunicMacroSupport
2
+ import Foundation
3
+
4
+ @ProviderDescriptorRegistration
5
+ @ProviderDescriptorDefinition
6
+ public enum PerplexityProviderDescriptor {
7
+ static func makeDescriptor() -> ProviderDescriptor {
8
+ ProviderDescriptor(
9
+ id: .perplexity,
10
+ metadata: ProviderMetadata(
11
+ id: .perplexity,
12
+ displayName: "Perplexity",
13
+ sessionLabel: "Models",
14
+ weeklyLabel: "",
15
+ opusLabel: nil,
16
+ supportsOpus: false,
17
+ supportsCredits: false,
18
+ creditsHint: "Shows model availability from Perplexity API.",
19
+ toggleTitle: "Show Perplexity usage",
20
+ cliName: "perplexity",
21
+ defaultEnabled: false,
22
+ isPrimaryProvider: false,
23
+ usesAccountFallback: false,
24
+ dashboardURL: "https://www.perplexity.ai/settings/api",
25
+ statusPageURL: nil,
26
+ usageCoverage: ProviderUsageCoverage(
27
+ supportsModelBreakdown: true,
28
+ supportsTokenMetrics: false,
29
+ supportsProjectAttribution: false)),
30
+ branding: ProviderBranding(
31
+ iconStyle: .perplexity,
32
+ iconResourceName: "ProviderIcon-perplexity",
33
+ color: ProviderColor(red: 35 / 255, green: 180 / 255, blue: 146 / 255)),
34
+ tokenCost: ProviderTokenCostConfig(
35
+ supportsTokenCost: false,
36
+ noDataMessage: { "Perplexity cost summary is not supported." }),
37
+ fetchPlan: ProviderFetchPlan(
38
+ sourceModes: [.auto, .cli],
39
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [PerplexityAPIFetchStrategy()] })),
40
+ cli: ProviderCLIConfig(
41
+ name: "perplexity",
42
+ aliases: ["pplx"],
43
+ versionDetector: nil))
44
+ }
45
+ }
46
+
47
+ struct PerplexityAPIFetchStrategy: ProviderFetchStrategy {
48
+ let id: String = "perplexity.api"
49
+ let kind: ProviderFetchKind = .apiToken
50
+
51
+ func isAvailable(_ context: ProviderFetchContext) async -> Bool {
52
+ Self.resolveTokenResolution(environment: context.env) != nil
53
+ }
54
+
55
+ func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
56
+ guard let tokenRes = Self.resolveTokenResolution(environment: context.env) else {
57
+ throw PerplexitySettingsError.missingToken
58
+ }
59
+ let usage = try await PerplexityUsageFetcher.fetchModels(apiKey: tokenRes.token)
60
+ return self.makeResult(
61
+ usage: usage.toUsageSnapshot(),
62
+ sourceLabel: tokenRes.source.rawValue)
63
+ }
64
+
65
+ func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
66
+ false
67
+ }
68
+
69
+ private static func resolveTokenResolution(environment: [String: String]) -> ProviderTokenResolution? {
70
+ ProviderTokenResolver.perplexityResolution(environment: environment)
71
+ }
72
+ }
73
+
74
+ enum PerplexitySettingsError: LocalizedError, Sendable {
75
+ case missingToken
76
+
77
+ var errorDescription: String? {
78
+ switch self {
79
+ case .missingToken:
80
+ "Perplexity API key not found. Set it in Preferences → Providers → Perplexity or " +
81
+ "export PPLX_API_KEY."
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,75 @@
1
+ import Foundation
2
+
3
+ struct PerplexityModelsResponse: Decodable {
4
+ let data: [Model]?
5
+
6
+ struct Model: Decodable {
7
+ let id: String?
8
+ }
9
+ }
10
+
11
+ struct PerplexityUsageFetcher {
12
+ static let apiURL = URL(string: "https://api.perplexity.ai/models")!
13
+
14
+ static func fetchModels(apiKey: String) async throws -> PerplexityModelsResponse {
15
+ var request = URLRequest(url: apiURL)
16
+ request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
17
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
18
+
19
+ let (data, response) = try await URLSession.shared.data(for: request)
20
+
21
+ guard let httpResponse = response as? HTTPURLResponse else {
22
+ throw PerplexityAPIError.invalidResponse
23
+ }
24
+
25
+ guard httpResponse.statusCode == 200 else {
26
+ let body = String(data: data, encoding: .utf8)?
27
+ .trimmingCharacters(in: .whitespacesAndNewlines)
28
+ throw PerplexityAPIError.httpError(
29
+ statusCode: httpResponse.statusCode,
30
+ body: body?.isEmpty == false ? body : nil)
31
+ }
32
+
33
+ let decoder = JSONDecoder()
34
+ return try decoder.decode(PerplexityModelsResponse.self, from: data)
35
+ }
36
+ }
37
+
38
+ extension PerplexityModelsResponse {
39
+ func toUsageSnapshot() -> UsageSnapshot {
40
+ let models = (self.data ?? []).compactMap(\.id)
41
+ var summary = "Models available: \(models.count)"
42
+ let preview = models.prefix(3).joined(separator: ", ")
43
+ if !preview.isEmpty {
44
+ summary += " (\(preview))"
45
+ }
46
+
47
+ return UsageSnapshot(
48
+ primary: RateWindow(
49
+ usedPercent: 0,
50
+ windowMinutes: nil,
51
+ resetsAt: nil,
52
+ resetDescription: summary),
53
+ secondary: nil,
54
+ tertiary: nil,
55
+ updatedAt: Date(),
56
+ identity: nil)
57
+ }
58
+ }
59
+
60
+ enum PerplexityAPIError: LocalizedError, Sendable {
61
+ case invalidResponse
62
+ case httpError(statusCode: Int, body: String?)
63
+
64
+ var errorDescription: String? {
65
+ switch self {
66
+ case .invalidResponse:
67
+ return "Invalid response from Perplexity API"
68
+ case let .httpError(statusCode, body):
69
+ if let body, !body.isEmpty {
70
+ return "Perplexity API returned status code \(statusCode): \(body)"
71
+ }
72
+ return "Perplexity API returned status code \(statusCode)"
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,25 @@
1
+ import Foundation
2
+
3
+ public struct ProviderColor: Sendable, Equatable {
4
+ public let red: Double
5
+ public let green: Double
6
+ public let blue: Double
7
+
8
+ public init(red: Double, green: Double, blue: Double) {
9
+ self.red = red
10
+ self.green = green
11
+ self.blue = blue
12
+ }
13
+ }
14
+
15
+ public struct ProviderBranding: Sendable {
16
+ public let iconStyle: IconStyle
17
+ public let iconResourceName: String
18
+ public let color: ProviderColor
19
+
20
+ public init(iconStyle: IconStyle, iconResourceName: String, color: ProviderColor) {
21
+ self.iconStyle = iconStyle
22
+ self.iconResourceName = iconResourceName
23
+ self.color = color
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ import Foundation
2
+
3
+ public struct ProviderCLIConfig: Sendable {
4
+ public let name: String
5
+ public let aliases: [String]
6
+ public let versionDetector: (@Sendable () -> String?)?
7
+
8
+ public init(
9
+ name: String,
10
+ aliases: [String] = [],
11
+ versionDetector: (@Sendable () -> String?)?)
12
+ {
13
+ self.name = name
14
+ self.aliases = aliases
15
+ self.versionDetector = versionDetector
16
+ }
17
+ }