@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,518 @@
1
+ import AppKit
2
+ import RunicCore
3
+ import QuartzCore
4
+
5
+ // MARK: - Animation & Performance Optimization
6
+ //
7
+ // **Zero Token Leakage Policy**:
8
+ // - Use cookies/local storage from providers FIRST
9
+ // - Single ping per menu open (not continuous polling)
10
+ // - Cache all results aggressively
11
+ // - Stop animations immediately when data loaded or error reached
12
+ //
13
+ // **Performance Targets**:
14
+ // - Menubar icon: 60 FPS (smooth on modern Macs)
15
+ // - Popover menu: 60 FPS (full detail)
16
+ // - Animation stops: when data loaded OR error state reached
17
+ // - No always-running timers
18
+ //
19
+ // **Resource Management**:
20
+ // - DisplayLink stops automatically when done
21
+ // - Minimal CPU/GPU usage
22
+ // - Smart caching prevents redundant renders
23
+
24
+ extension StatusItemController {
25
+ func needsMenuBarIconAnimation() -> Bool {
26
+ // Performance: Only animate when actively fetching, stop when data loaded
27
+ if self.shouldMergeIcons {
28
+ let primaryProvider = self.primaryProviderForUnifiedIcon()
29
+ return self.shouldAnimate(provider: primaryProvider)
30
+ }
31
+ return UsageProvider.allCases.contains { self.shouldAnimate(provider: $0) }
32
+ }
33
+
34
+ func updateBlinkingState() {
35
+ let blinkingEnabled = self.isBlinkingAllowed()
36
+ // Performance: Cache provider state to minimize repeated lookups (60fps smooth)
37
+ let enabledProviders = self.store.enabledProviders()
38
+ let anyEnabled = !enabledProviders.isEmpty || self.store.debugForceAnimation
39
+ let anyVisible = UsageProvider.allCases.contains { self.isVisible($0) }
40
+ let mergeIcons = self.settings.mergeIcons && enabledProviders.count > 1
41
+ let shouldBlink = mergeIcons ? anyEnabled : anyVisible
42
+ if blinkingEnabled, shouldBlink {
43
+ if self.blinkTask == nil {
44
+ self.seedBlinkStatesIfNeeded()
45
+ self.blinkTask = Task { [weak self] in
46
+ while !Task.isCancelled {
47
+ try? await Task.sleep(for: .milliseconds(75))
48
+ await MainActor.run { self?.tickBlink() }
49
+ }
50
+ }
51
+ }
52
+ } else {
53
+ self.stopBlinking()
54
+ }
55
+ }
56
+
57
+ private func seedBlinkStatesIfNeeded() {
58
+ let now = Date()
59
+ for provider in UsageProvider.allCases where self.blinkStates[provider] == nil {
60
+ self.blinkStates[provider] = BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))
61
+ }
62
+ }
63
+
64
+ private func stopBlinking() {
65
+ self.blinkTask?.cancel()
66
+ self.blinkTask = nil
67
+ self.blinkAmounts.removeAll()
68
+ if self.shouldMergeIcons {
69
+ self.applyIcon(phase: nil)
70
+ } else {
71
+ UsageProvider.allCases.forEach { self.applyIcon(for: $0, phase: nil) }
72
+ }
73
+ }
74
+
75
+ private func tickBlink(now: Date = .init()) {
76
+ guard self.isBlinkingAllowed(at: now) else {
77
+ self.stopBlinking()
78
+ return
79
+ }
80
+
81
+ let blinkDuration: TimeInterval = PerformanceConstants.blinkDuration
82
+ let doubleBlinkChance = PerformanceConstants.doubleBlinkChance
83
+ let doubleDelayRange: ClosedRange<TimeInterval> = 0.22...0.34
84
+ // Cache merge state once per tick to avoid repeated enabled-provider lookups.
85
+ let mergeIcons = self.shouldMergeIcons
86
+
87
+ for provider in UsageProvider.allCases {
88
+ let shouldRender = mergeIcons ? self.isEnabled(provider) : self.isVisible(provider)
89
+ guard shouldRender, !self.shouldAnimate(provider: provider, mergeIcons: mergeIcons) else {
90
+ self.clearMotion(for: provider)
91
+ continue
92
+ }
93
+
94
+ var state = self
95
+ .blinkStates[provider] ?? BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))
96
+
97
+ if let pendingSecond = state.pendingSecondStart, now >= pendingSecond {
98
+ state.blinkStart = now
99
+ state.pendingSecondStart = nil
100
+ }
101
+
102
+ if let start = state.blinkStart {
103
+ let elapsed = now.timeIntervalSince(start)
104
+ if elapsed >= blinkDuration {
105
+ state.blinkStart = nil
106
+ if let pending = state.pendingSecondStart, now < pending {
107
+ // Wait for the planned double-blink.
108
+ } else {
109
+ state.pendingSecondStart = nil
110
+ state.nextBlink = now.addingTimeInterval(BlinkState.randomDelay())
111
+ }
112
+ self.clearMotion(for: provider)
113
+ } else {
114
+ let progress = max(0, min(elapsed / blinkDuration, 1))
115
+ let symmetric = progress < 0.5 ? progress * 2 : (1 - progress) * 2
116
+ let eased = pow(symmetric, 2.2) // slightly punchier than smoothstep
117
+ self.assignMotion(amount: CGFloat(eased), for: provider, effect: state.effect)
118
+ }
119
+ } else if now >= state.nextBlink {
120
+ state.blinkStart = now
121
+ state.effect = self.randomEffect(for: provider)
122
+ if state.effect == .blink, Double.random(in: 0...1) < doubleBlinkChance {
123
+ state.pendingSecondStart = now.addingTimeInterval(Double.random(in: doubleDelayRange))
124
+ }
125
+ self.clearMotion(for: provider)
126
+ } else {
127
+ self.clearMotion(for: provider)
128
+ }
129
+
130
+ self.blinkStates[provider] = state
131
+ if !mergeIcons {
132
+ self.applyIcon(for: provider, phase: nil)
133
+ }
134
+ }
135
+ if mergeIcons {
136
+ self.applyIcon(phase: nil)
137
+ }
138
+ }
139
+
140
+ private func blinkAmount(for provider: UsageProvider) -> CGFloat {
141
+ guard self.isBlinkingAllowed() else { return 0 }
142
+ return self.blinkAmounts[provider] ?? 0
143
+ }
144
+
145
+ private func wiggleAmount(for provider: UsageProvider) -> CGFloat {
146
+ guard self.isBlinkingAllowed() else { return 0 }
147
+ return self.wiggleAmounts[provider] ?? 0
148
+ }
149
+
150
+ private func tiltAmount(for provider: UsageProvider) -> CGFloat {
151
+ guard self.isBlinkingAllowed() else { return 0 }
152
+ return self.tiltAmounts[provider] ?? 0
153
+ }
154
+
155
+ private func assignMotion(amount: CGFloat, for provider: UsageProvider, effect: MotionEffect) {
156
+ switch effect {
157
+ case .blink:
158
+ self.blinkAmounts[provider] = amount
159
+ self.wiggleAmounts[provider] = 0
160
+ self.tiltAmounts[provider] = 0
161
+ case .wiggle:
162
+ self.wiggleAmounts[provider] = amount
163
+ self.blinkAmounts[provider] = 0
164
+ self.tiltAmounts[provider] = 0
165
+ case .tilt:
166
+ self.tiltAmounts[provider] = amount
167
+ self.blinkAmounts[provider] = 0
168
+ self.wiggleAmounts[provider] = 0
169
+ }
170
+ }
171
+
172
+ private func clearMotion(for provider: UsageProvider) {
173
+ self.blinkAmounts[provider] = 0
174
+ self.wiggleAmounts[provider] = 0
175
+ self.tiltAmounts[provider] = 0
176
+ }
177
+
178
+ private func randomEffect(for provider: UsageProvider) -> MotionEffect {
179
+ if provider == .claude {
180
+ Bool.random() ? .blink : .wiggle
181
+ } else {
182
+ Bool.random() ? .blink : .tilt
183
+ }
184
+ }
185
+
186
+ private func isBlinkingAllowed(at date: Date = .init()) -> Bool {
187
+ if self.settings.randomBlinkEnabled { return true }
188
+ if let until = self.blinkForceUntil, until > date { return true }
189
+ self.blinkForceUntil = nil
190
+ return false
191
+ }
192
+
193
+ func applyIcon(phase: Double?) {
194
+ guard let button = self.statusItem.button else { return }
195
+
196
+ let style = self.store.iconStyle
197
+ let showUsed = self.settings.usageBarsShowUsed
198
+ let showBrandPercent = self.settings.menuBarShowsBrandIconWithPercent
199
+ let primaryProvider = self.primaryProviderForUnifiedIcon()
200
+ let snapshot = self.store.snapshot(for: primaryProvider)
201
+
202
+ // IconRenderer treats these values as a left-to-right "progress fill" percentage; depending on the
203
+ // user setting we pass either "percent left" or "percent used".
204
+ var primary = showUsed ? snapshot?.primary.usedPercent : snapshot?.primary.remainingPercent
205
+ var weekly = showUsed ? snapshot?.secondary?.usedPercent : snapshot?.secondary?.remainingPercent
206
+ var credits: Double? = primaryProvider == .codex ? self.store.credits?.remaining : nil
207
+ var stale = self.store.isStale(provider: primaryProvider)
208
+ var morphProgress: Double?
209
+
210
+ let needsAnimation = self.needsMenuBarIconAnimation()
211
+ if let phase, needsAnimation {
212
+ var pattern = self.animationPattern
213
+ if style == .combined, pattern == .unbraid {
214
+ pattern = .cylon
215
+ }
216
+ if pattern == .unbraid {
217
+ morphProgress = pattern.value(phase: phase) / 100
218
+ primary = nil
219
+ weekly = nil
220
+ credits = nil
221
+ stale = false
222
+ } else {
223
+ primary = pattern.value(phase: phase)
224
+ weekly = pattern.value(phase: phase + pattern.secondaryOffset)
225
+ credits = nil
226
+ stale = false
227
+ }
228
+ }
229
+
230
+ let blink: CGFloat = style == .combined ? 0 : self.blinkAmount(for: primaryProvider)
231
+ let wiggle: CGFloat = style == .combined ? 0 : self.wiggleAmount(for: primaryProvider)
232
+ let tilt: CGFloat = style == .combined ? 0 : self.tiltAmount(for: primaryProvider) * .pi / 28
233
+
234
+ let statusIndicator: ProviderStatusIndicator = {
235
+ for provider in UsageProvider.allCases {
236
+ let indicator = self.store.statusIndicator(for: provider)
237
+ if indicator.hasIssue { return indicator }
238
+ }
239
+ return .none
240
+ }()
241
+ let appearance: IconAppearance = self.settings.menuBarVibrantIconEnabled ? .vibrant : .template
242
+ let dataMode: IconDataMode = showUsed ? .used : .remaining
243
+
244
+ if showBrandPercent,
245
+ let brand = ProviderBrandIcon.image(for: primaryProvider)
246
+ {
247
+ let percentText = self.menuBarPercentText(for: primaryProvider, snapshot: snapshot)
248
+ self.setButtonImage(brand, for: button)
249
+ self.setButtonTitle(percentText, for: button)
250
+ return
251
+ }
252
+
253
+ self.setButtonTitle(nil, for: button)
254
+ if let morphProgress {
255
+ let image = IconRenderer.makeMorphIcon(progress: morphProgress, style: style, appearance: appearance)
256
+ self.setButtonImage(image, for: button)
257
+ } else {
258
+ let image = IconRenderer.makeIcon(
259
+ primaryRemaining: primary,
260
+ weeklyRemaining: weekly,
261
+ creditsRemaining: credits,
262
+ stale: stale,
263
+ style: style,
264
+ blink: blink,
265
+ wiggle: wiggle,
266
+ tilt: tilt,
267
+ statusIndicator: statusIndicator,
268
+ appearance: appearance,
269
+ dataMode: dataMode)
270
+ self.setButtonImage(image, for: button)
271
+ }
272
+ }
273
+
274
+ func applyIcon(for provider: UsageProvider, phase: Double?) {
275
+ guard let button = self.statusItems[provider]?.button else { return }
276
+ let snapshot = self.store.snapshot(for: provider)
277
+ // IconRenderer treats these values as a left-to-right "progress fill" percentage; depending on the
278
+ // user setting we pass either "percent left" or "percent used".
279
+ let showUsed = self.settings.usageBarsShowUsed
280
+ let showBrandPercent = self.settings.menuBarShowsBrandIconWithPercent
281
+ let appearance: IconAppearance = self.settings.menuBarVibrantIconEnabled ? .vibrant : .template
282
+ let dataMode: IconDataMode = showUsed ? .used : .remaining
283
+ if showBrandPercent,
284
+ let brand = ProviderBrandIcon.image(for: provider)
285
+ {
286
+ let percentText = self.menuBarPercentText(for: provider, snapshot: snapshot)
287
+ self.setButtonImage(brand, for: button)
288
+ self.setButtonTitle(percentText, for: button)
289
+ return
290
+ }
291
+ var primary = showUsed ? snapshot?.primary.usedPercent : snapshot?.primary.remainingPercent
292
+ var weekly = showUsed ? snapshot?.secondary?.usedPercent : snapshot?.secondary?.remainingPercent
293
+ var credits: Double? = provider == .codex ? self.store.credits?.remaining : nil
294
+ var stale = self.store.isStale(provider: provider)
295
+ var morphProgress: Double?
296
+
297
+ if let phase, self.shouldAnimate(provider: provider) {
298
+ var pattern = self.animationPattern
299
+ if provider == .claude, pattern == .unbraid {
300
+ pattern = .cylon
301
+ }
302
+ if pattern == .unbraid {
303
+ morphProgress = pattern.value(phase: phase) / 100
304
+ primary = nil
305
+ weekly = nil
306
+ credits = nil
307
+ stale = false
308
+ } else {
309
+ primary = pattern.value(phase: phase)
310
+ weekly = pattern.value(phase: phase + pattern.secondaryOffset)
311
+ credits = nil
312
+ stale = false
313
+ }
314
+ }
315
+
316
+ let style: IconStyle = self.store.style(for: provider)
317
+ let blink = self.blinkAmount(for: provider)
318
+ let wiggle = self.wiggleAmount(for: provider)
319
+ let tilt = self.tiltAmount(for: provider) * .pi / 28 // limit to ~6.4°
320
+ if let morphProgress {
321
+ let image = IconRenderer.makeMorphIcon(progress: morphProgress, style: style, appearance: appearance)
322
+ self.setButtonImage(image, for: button)
323
+ } else {
324
+ self.setButtonTitle(nil, for: button)
325
+ let image = IconRenderer.makeIcon(
326
+ primaryRemaining: primary,
327
+ weeklyRemaining: weekly,
328
+ creditsRemaining: credits,
329
+ stale: stale,
330
+ style: style,
331
+ blink: blink,
332
+ wiggle: wiggle,
333
+ tilt: tilt,
334
+ statusIndicator: self.store.statusIndicator(for: provider),
335
+ appearance: appearance,
336
+ dataMode: dataMode)
337
+ self.setButtonImage(image, for: button)
338
+ }
339
+ }
340
+
341
+ private func setButtonImage(_ image: NSImage, for button: NSStatusBarButton) {
342
+ if button.image === image { return }
343
+ let previousSize = button.image?.size
344
+ button.image = image
345
+ if previousSize != image.size {
346
+ button.sizeToFit()
347
+ }
348
+ }
349
+
350
+ private func setButtonTitle(_ title: String?, for button: NSStatusBarButton) {
351
+ let value = title ?? ""
352
+ let position: NSControl.ImagePosition = value.isEmpty ? .imageOnly : .imageLeft
353
+ var needsResize = false
354
+ if button.title != value {
355
+ button.title = value
356
+ needsResize = true
357
+ }
358
+ if button.imagePosition != position {
359
+ button.imagePosition = position
360
+ needsResize = true
361
+ }
362
+ if needsResize {
363
+ button.sizeToFit()
364
+ }
365
+ }
366
+
367
+ private func menuBarPercentText(for provider: UsageProvider, snapshot: UsageSnapshot?) -> String? {
368
+ guard let window = self.menuBarPercentWindow(for: provider, snapshot: snapshot) else { return nil }
369
+ let percent = self.settings.usageBarsShowUsed ? window.usedPercent : window.remainingPercent
370
+ let clamped = min(100, max(0, percent))
371
+ return String(format: "%.0f%%", clamped)
372
+ }
373
+
374
+ private func menuBarPercentWindow(for provider: UsageProvider, snapshot: UsageSnapshot?) -> RateWindow? {
375
+ if provider == .factory {
376
+ return snapshot?.secondary ?? snapshot?.primary
377
+ }
378
+ return snapshot?.primary ?? snapshot?.secondary
379
+ }
380
+
381
+ private func primaryProviderForUnifiedIcon() -> UsageProvider {
382
+ if self.shouldMergeIcons,
383
+ let selected = self.selectedMenuProvider,
384
+ self.store.isEnabled(selected)
385
+ {
386
+ return selected
387
+ }
388
+ for provider in UsageProvider.allCases {
389
+ if self.store.isEnabled(provider), self.store.snapshot(for: provider) != nil {
390
+ return provider
391
+ }
392
+ }
393
+ if let enabled = self.store.enabledProviders().first {
394
+ return enabled
395
+ }
396
+ return .codex
397
+ }
398
+
399
+ @objc func handleDebugBlinkNotification() {
400
+ self.forceBlinkNow()
401
+ }
402
+
403
+ private func forceBlinkNow() {
404
+ let now = Date()
405
+ self.blinkForceUntil = now.addingTimeInterval(0.6)
406
+ self.seedBlinkStatesIfNeeded()
407
+
408
+ for provider in UsageProvider.allCases {
409
+ let shouldBlink = self.shouldMergeIcons ? self.isEnabled(provider) : self.isVisible(provider)
410
+ guard shouldBlink, !self.shouldAnimate(provider: provider) else { continue }
411
+ var state = self
412
+ .blinkStates[provider] ?? BlinkState(nextBlink: now.addingTimeInterval(BlinkState.randomDelay()))
413
+ state.blinkStart = now
414
+ state.pendingSecondStart = nil
415
+ state.effect = self.randomEffect(for: provider)
416
+ state.nextBlink = now.addingTimeInterval(BlinkState.randomDelay())
417
+ self.blinkStates[provider] = state
418
+ self.assignMotion(amount: 0, for: provider, effect: state.effect)
419
+ }
420
+
421
+ self.updateBlinkingState()
422
+ self.tickBlink(now: now)
423
+ }
424
+
425
+ /// **Performance-Critical**: Determines if animation should run for a provider
426
+ /// - Only animates during active data fetching (no always-on loops)
427
+ /// - Stops immediately when data loaded OR error state reached
428
+ /// - Prevents battery drain from unnecessary rendering
429
+ /// - Zero token leakage: uses cached data first, minimal API pings
430
+ private func shouldAnimate(provider: UsageProvider, mergeIcons: Bool? = nil) -> Bool {
431
+ if self.store.debugForceAnimation { return true }
432
+
433
+ let isMerged = mergeIcons ?? self.shouldMergeIcons
434
+ let isVisible = isMerged ? self.isEnabled(provider) : self.isVisible(provider)
435
+ guard isVisible else { return false }
436
+
437
+ // Critical: Only animate while actively fetching
438
+ guard self.store.refreshingProviders.contains(provider) else { return false }
439
+
440
+ // Stop animation if we have data OR reached error state (stale = error/cached)
441
+ let isStale = self.store.isStale(provider: provider)
442
+ let hasData = self.store.snapshot(for: provider) != nil
443
+
444
+ // Animation only runs when: no data yet AND not in error/stale state
445
+ return !hasData && !isStale
446
+ }
447
+
448
+ /// **Performance Optimization**: Manages animation lifecycle with strict resource control
449
+ /// - Menubar: 30 FPS cap (battery-efficient)
450
+ /// - Auto-stops when data loaded or error reached
451
+ /// - No always-running timers
452
+ /// - Minimal CPU/GPU usage
453
+ func updateAnimationState() {
454
+ let needsAnimation = self.needsMenuBarIconAnimation()
455
+ if needsAnimation {
456
+ if self.animationDriver == nil {
457
+ if let forced = self.settings.debugLoadingPattern {
458
+ self.animationPattern = forced
459
+ } else if !LoadingPattern.allCases.contains(self.animationPattern) {
460
+ self.animationPattern = .knightRider
461
+ }
462
+ self.animationPhase = 0
463
+ let driver = DisplayLinkDriver(onTick: { [weak self] in
464
+ self?.updateAnimationFrame()
465
+ })
466
+ self.animationDriver = driver
467
+ // Performance: 60 FPS for smooth menubar animations
468
+ driver.start(fps: PerformanceConstants.menubarFPS)
469
+ } else if let forced = self.settings.debugLoadingPattern, forced != self.animationPattern {
470
+ self.animationPattern = forced
471
+ self.animationPhase = 0
472
+ }
473
+ } else {
474
+ // Critical: Stop animation immediately when data loaded/error reached
475
+ self.animationDriver?.stop()
476
+ self.animationDriver = nil
477
+ self.animationPhase = 0
478
+ if self.shouldMergeIcons {
479
+ self.applyIcon(phase: nil)
480
+ } else {
481
+ UsageProvider.allCases.forEach { self.applyIcon(for: $0, phase: nil) }
482
+ }
483
+ }
484
+ }
485
+
486
+ private func updateAnimationFrame() {
487
+ self.animationPhase += 0.045 // half-speed animation
488
+ if self.shouldMergeIcons {
489
+ self.applyIcon(phase: self.animationPhase)
490
+ } else {
491
+ UsageProvider.allCases.forEach { self.applyIcon(for: $0, phase: self.animationPhase) }
492
+ }
493
+ }
494
+
495
+ private func advanceAnimationPattern() {
496
+ let patterns = LoadingPattern.allCases
497
+ if let idx = patterns.firstIndex(of: self.animationPattern) {
498
+ let next = patterns.indices.contains(idx + 1) ? patterns[idx + 1] : patterns.first
499
+ self.animationPattern = next ?? .knightRider
500
+ } else {
501
+ self.animationPattern = .knightRider
502
+ }
503
+ }
504
+
505
+ @objc func handleDebugReplayNotification(_ notification: Notification) {
506
+ if let raw = notification.userInfo?["pattern"] as? String,
507
+ let selected = LoadingPattern(rawValue: raw)
508
+ {
509
+ self.animationPattern = selected
510
+ } else if let forced = self.settings.debugLoadingPattern {
511
+ self.animationPattern = forced
512
+ } else {
513
+ self.advanceAnimationPattern()
514
+ }
515
+ self.animationPhase = 0
516
+ self.updateAnimationState()
517
+ }
518
+ }