@sriinnu/runic 1.2.0 → 2.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 (342) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +8 -1
  3. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  4. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  5. package/RuniciOS/Views/ProviderListView.swift +10 -10
  6. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  7. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  8. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  9. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  10. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  11. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  12. package/Sources/Runic/About.swift +3 -3
  13. package/Sources/Runic/AppNotifications.swift +5 -3
  14. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
  15. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  16. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  17. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  18. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
  19. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  20. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  21. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  22. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  23. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  24. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  25. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  26. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  28. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  29. package/Sources/Runic/Core/RunicColors.swift +6 -6
  30. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  31. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  32. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
  33. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
  34. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
  35. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
  36. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
  37. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  38. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
  39. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
  40. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
  41. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
  42. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
  43. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
  44. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
  45. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
  46. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
  47. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
  48. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  49. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
  50. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
  51. package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
  52. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
  53. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  54. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  55. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  56. package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
  57. package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
  58. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
  59. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  60. package/Sources/Runic/ProviderRegistry.swift +1 -1
  61. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  62. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  63. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  81. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  83. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  84. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  85. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  87. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  88. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  107. package/Sources/Runic/RunicApp.swift +46 -36
  108. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  109. package/Sources/Runic/UsagePaceText.swift +1 -1
  110. package/Sources/Runic/UsageProgressBar.swift +13 -9
  111. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  112. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  113. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  114. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  115. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  116. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  117. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  118. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  119. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  120. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
  121. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  122. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  123. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  124. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  125. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  126. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  127. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  128. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  129. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
  130. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  131. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  132. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  133. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  134. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  135. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  136. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
  137. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  138. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  139. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  140. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  141. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  142. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  143. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  144. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  145. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  146. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  147. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  148. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  149. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  150. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  151. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
  152. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  153. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  154. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  155. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  156. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  157. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  158. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  159. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  160. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  161. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  162. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  163. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  164. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  165. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  166. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  167. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  168. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  169. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  170. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  171. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  172. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  173. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  174. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
  175. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  176. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  177. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  178. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  179. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  180. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  181. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  182. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  183. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  184. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  185. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  186. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  187. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  188. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  189. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  190. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
  191. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  192. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  193. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  194. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  195. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  196. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  197. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  198. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  199. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  200. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
  201. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  202. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  204. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  205. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  206. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  207. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  208. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  209. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  210. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  211. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  212. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  213. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  214. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  215. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
  216. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  217. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  218. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  219. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
  220. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  221. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  222. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  225. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
  226. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  227. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  228. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  229. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  230. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
  231. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  232. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  233. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  234. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  235. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  236. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
  237. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  240. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  241. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  242. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  243. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  244. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  245. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  246. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  247. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
  248. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  249. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  250. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  251. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  252. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  253. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  254. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  255. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  256. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  257. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  258. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  259. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
  260. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
  261. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
  262. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  263. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  264. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  265. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  266. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  267. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  268. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  269. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  270. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  271. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  272. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  273. package/Tests/RunicTests/CLICostTests.swift +6 -7
  274. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  275. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  276. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  277. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  278. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  279. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  280. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  281. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  282. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  283. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  284. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  285. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  286. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  287. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  288. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  289. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  290. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  291. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  292. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  293. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  294. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  295. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  296. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  297. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  298. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  299. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  300. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  301. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  302. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  303. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  304. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  305. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  306. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  307. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  308. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  309. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  310. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  311. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  312. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  313. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  314. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  315. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  316. package/Tests/RunicTests/RunicTests.swift +6 -7
  317. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  318. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  319. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  320. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  321. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  322. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  323. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  324. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  325. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  326. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  327. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  328. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  329. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  330. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  331. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  332. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  333. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  334. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  335. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  336. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  337. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  338. package/appcast.xml +36 -76
  339. package/package.json +1 -1
  340. package/runic.png +0 -0
  341. package/runic.svg +15 -0
  342. package/version.env +2 -2
@@ -1,13 +1,12 @@
1
1
  import AppKit
2
- import RunicCore
3
2
  import Foundation
3
+ import RunicCore
4
4
  import Testing
5
5
  @testable import Runic
6
6
 
7
- @Suite
8
7
  struct RunicTests {
9
8
  @Test
10
- func iconRendererProducesTemplateImage() {
9
+ func `icon renderer produces template image`() {
11
10
  let image = IconRenderer.makeIcon(
12
11
  primaryRemaining: 50,
13
12
  weeklyRemaining: 75,
@@ -19,7 +18,7 @@ struct RunicTests {
19
18
  }
20
19
 
21
20
  @Test
22
- func iconRendererRendersAtPixelAlignedSize() {
21
+ func `icon renderer renders at pixel aligned size`() {
23
22
  let image = IconRenderer.makeIcon(
24
23
  primaryRemaining: 50,
25
24
  weeklyRemaining: 75,
@@ -32,7 +31,7 @@ struct RunicTests {
32
31
  }
33
32
 
34
33
  @Test
35
- func iconRendererCachesStaticIcons() {
34
+ func `icon renderer caches static icons`() {
36
35
  let first = IconRenderer.makeIcon(
37
36
  primaryRemaining: 42,
38
37
  weeklyRemaining: 17,
@@ -49,7 +48,7 @@ struct RunicTests {
49
48
  }
50
49
 
51
50
  @Test
52
- func iconRendererCodexEyesPunchThroughWhenUnknown() {
51
+ func `icon renderer codex eyes punch through when unknown`() {
53
52
  // Regression guard: icon should preserve transparent + opaque pixels.
54
53
  let image = IconRenderer.makeIcon(
55
54
  primaryRemaining: nil,
@@ -83,7 +82,7 @@ struct RunicTests {
83
82
  }
84
83
 
85
84
  @Test
86
- func accountInfoParsesAuthToken() throws {
85
+ func `account info parses auth token`() throws {
87
86
  let tmp = try FileManager.default.url(
88
87
  for: .itemReplacementDirectory,
89
88
  in: .userDomainMask,
@@ -1,35 +1,34 @@
1
1
  import Testing
2
2
  @testable import Runic
3
3
 
4
- @Suite
5
4
  struct SessionQuotaNotificationLogicTests {
6
5
  @Test
7
- func doesNothingWithoutPreviousValue() {
6
+ func `does nothing without previous value`() {
8
7
  let transition = SessionQuotaNotificationLogic.transition(previousRemaining: nil, currentRemaining: 0)
9
8
  #expect(transition == .none)
10
9
  }
11
10
 
12
11
  @Test
13
- func detectsDepletedTransition() {
12
+ func `detects depleted transition`() {
14
13
  let transition = SessionQuotaNotificationLogic.transition(previousRemaining: 12, currentRemaining: 0)
15
14
  #expect(transition == .depleted)
16
15
  }
17
16
 
18
17
  @Test
19
- func detectsRestoredTransition() {
18
+ func `detects restored transition`() {
20
19
  let transition = SessionQuotaNotificationLogic.transition(previousRemaining: 0, currentRemaining: 5)
21
20
  #expect(transition == .restored)
22
21
  }
23
22
 
24
23
  @Test
25
- func ignoresNonTransitions() {
24
+ func `ignores non transitions`() {
26
25
  #expect(SessionQuotaNotificationLogic.transition(previousRemaining: 0, currentRemaining: 0) == .none)
27
26
  #expect(SessionQuotaNotificationLogic.transition(previousRemaining: 10, currentRemaining: 10) == .none)
28
27
  #expect(SessionQuotaNotificationLogic.transition(previousRemaining: 10, currentRemaining: 9) == .none)
29
28
  }
30
29
 
31
30
  @Test
32
- func treatsTinyPositiveRemainingAsDepleted() {
31
+ func `treats tiny positive remaining as depleted`() {
33
32
  let transition = SessionQuotaNotificationLogic.transition(previousRemaining: 0, currentRemaining: 0.00001)
34
33
  #expect(transition == .none)
35
34
  }
@@ -1,18 +1,24 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
  @testable import Runic
5
5
 
6
6
  @MainActor
7
- @Suite
8
7
  struct SettingsStoreTests {
9
8
  @Test
10
- func defaultRefreshFrequencyIsManual() {
9
+ func `default refresh frequency is manual`() throws {
11
10
  let suite = "SettingsStoreTests-default"
12
- let defaults = UserDefaults(suiteName: suite)!
11
+ let defaults = try #require(UserDefaults(suiteName: suite))
13
12
  defaults.removePersistentDomain(forName: suite)
14
13
 
15
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
14
+ let store = SettingsStore(
15
+ userDefaults: defaults,
16
+ zaiTokenStore: NoopZaiTokenStore(),
17
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
18
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
19
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
20
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
21
+ groqTokenStore: NoopGroqTokenStore())
16
22
 
17
23
  #expect(store.refreshFrequency == .manual)
18
24
  #expect(store.refreshFrequency.seconds == nil)
@@ -20,141 +26,239 @@ struct SettingsStoreTests {
20
26
  }
21
27
 
22
28
  @Test
23
- func persistsRefreshFrequencyAcrossInstances() {
29
+ func `persists refresh frequency across instances`() throws {
24
30
  let suite = "SettingsStoreTests-persist"
25
- let defaultsA = UserDefaults(suiteName: suite)!
31
+ let defaultsA = try #require(UserDefaults(suiteName: suite))
26
32
  defaultsA.removePersistentDomain(forName: suite)
27
- let storeA = SettingsStore(userDefaults: defaultsA, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
33
+ let storeA = SettingsStore(
34
+ userDefaults: defaultsA,
35
+ zaiTokenStore: NoopZaiTokenStore(),
36
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
37
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
38
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
39
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
40
+ groqTokenStore: NoopGroqTokenStore())
28
41
 
29
42
  storeA.refreshFrequency = .fifteenMinutes
30
43
 
31
- let defaultsB = UserDefaults(suiteName: suite)!
32
- let storeB = SettingsStore(userDefaults: defaultsB, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
44
+ let defaultsB = try #require(UserDefaults(suiteName: suite))
45
+ let storeB = SettingsStore(
46
+ userDefaults: defaultsB,
47
+ zaiTokenStore: NoopZaiTokenStore(),
48
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
49
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
50
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
51
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
52
+ groqTokenStore: NoopGroqTokenStore())
33
53
 
34
54
  #expect(storeB.refreshFrequency == .fifteenMinutes)
35
55
  #expect(storeB.refreshFrequency.seconds == 900)
36
56
  }
37
57
 
38
58
  @Test
39
- func defaultMenuModeIsOperator() {
59
+ func `default menu mode is operator`() throws {
40
60
  let suite = "SettingsStoreTests-menuMode-default"
41
- let defaults = UserDefaults(suiteName: suite)!
61
+ let defaults = try #require(UserDefaults(suiteName: suite))
42
62
  defaults.removePersistentDomain(forName: suite)
43
63
 
44
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
64
+ let store = SettingsStore(
65
+ userDefaults: defaults,
66
+ zaiTokenStore: NoopZaiTokenStore(),
67
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
68
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
69
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
70
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
71
+ groqTokenStore: NoopGroqTokenStore())
45
72
 
46
- #expect(store.menuMode == .`operator`)
73
+ #expect(store.menuMode == .operator)
47
74
  }
48
75
 
49
76
  @Test
50
- func persistsMenuModeAcrossInstances() {
77
+ func `persists menu mode across instances`() throws {
51
78
  let suite = "SettingsStoreTests-menuMode-persist"
52
- let defaultsA = UserDefaults(suiteName: suite)!
79
+ let defaultsA = try #require(UserDefaults(suiteName: suite))
53
80
  defaultsA.removePersistentDomain(forName: suite)
54
- let storeA = SettingsStore(userDefaults: defaultsA, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
81
+ let storeA = SettingsStore(
82
+ userDefaults: defaultsA,
83
+ zaiTokenStore: NoopZaiTokenStore(),
84
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
85
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
86
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
87
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
88
+ groqTokenStore: NoopGroqTokenStore())
55
89
 
56
90
  storeA.menuMode = .analyst
57
91
 
58
- let defaultsB = UserDefaults(suiteName: suite)!
59
- let storeB = SettingsStore(userDefaults: defaultsB, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
92
+ let defaultsB = try #require(UserDefaults(suiteName: suite))
93
+ let storeB = SettingsStore(
94
+ userDefaults: defaultsB,
95
+ zaiTokenStore: NoopZaiTokenStore(),
96
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
97
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
98
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
99
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
100
+ groqTokenStore: NoopGroqTokenStore())
60
101
 
61
102
  #expect(storeB.menuMode == .analyst)
62
103
  }
63
104
 
64
105
  @Test
65
- func persistsSelectedMenuProviderAcrossInstances() {
106
+ func `persists selected menu provider across instances`() throws {
66
107
  let suite = "SettingsStoreTests-selectedMenuProvider"
67
- let defaultsA = UserDefaults(suiteName: suite)!
108
+ let defaultsA = try #require(UserDefaults(suiteName: suite))
68
109
  defaultsA.removePersistentDomain(forName: suite)
69
- let storeA = SettingsStore(userDefaults: defaultsA, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
110
+ let storeA = SettingsStore(
111
+ userDefaults: defaultsA,
112
+ zaiTokenStore: NoopZaiTokenStore(),
113
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
114
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
115
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
116
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
117
+ groqTokenStore: NoopGroqTokenStore())
70
118
 
71
119
  storeA.selectedMenuProvider = .claude
72
120
 
73
- let defaultsB = UserDefaults(suiteName: suite)!
74
- let storeB = SettingsStore(userDefaults: defaultsB, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
121
+ let defaultsB = try #require(UserDefaults(suiteName: suite))
122
+ let storeB = SettingsStore(
123
+ userDefaults: defaultsB,
124
+ zaiTokenStore: NoopZaiTokenStore(),
125
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
126
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
127
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
128
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
129
+ groqTokenStore: NoopGroqTokenStore())
75
130
 
76
131
  #expect(storeB.selectedMenuProvider == .claude)
77
132
  }
78
133
 
79
134
  @Test
80
- func defaultsSessionQuotaNotificationsToEnabled() {
135
+ func `defaults session quota notifications to enabled`() throws {
81
136
  let key = "sessionQuotaNotificationsEnabled"
82
137
  let suite = "SettingsStoreTests-sessionQuotaNotifications"
83
- let defaults = UserDefaults(suiteName: suite)!
138
+ let defaults = try #require(UserDefaults(suiteName: suite))
84
139
  defaults.removePersistentDomain(forName: suite)
85
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
140
+ let store = SettingsStore(
141
+ userDefaults: defaults,
142
+ zaiTokenStore: NoopZaiTokenStore(),
143
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
144
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
145
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
146
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
147
+ groqTokenStore: NoopGroqTokenStore())
86
148
  #expect(store.sessionQuotaNotificationsEnabled == true)
87
149
  #expect(defaults.bool(forKey: key) == true)
88
150
  }
89
151
 
90
152
  @Test
91
- func defaultsClaudeUsageSourceToOAuth() {
153
+ func `defaults claude usage source to O auth`() throws {
92
154
  let suite = "SettingsStoreTests-claude-source"
93
- let defaults = UserDefaults(suiteName: suite)!
155
+ let defaults = try #require(UserDefaults(suiteName: suite))
94
156
  defaults.removePersistentDomain(forName: suite)
95
157
 
96
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
158
+ let store = SettingsStore(
159
+ userDefaults: defaults,
160
+ zaiTokenStore: NoopZaiTokenStore(),
161
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
162
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
163
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
164
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
165
+ groqTokenStore: NoopGroqTokenStore())
97
166
 
98
167
  #expect(store.claudeUsageDataSource == .oauth)
99
168
  }
100
169
 
101
170
  @Test
102
- func defaultsCodexUsageSourceToOAuth() {
171
+ func `defaults codex usage source to O auth`() throws {
103
172
  let suite = "SettingsStoreTests-codex-source"
104
- let defaults = UserDefaults(suiteName: suite)!
173
+ let defaults = try #require(UserDefaults(suiteName: suite))
105
174
  defaults.removePersistentDomain(forName: suite)
106
175
 
107
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
176
+ let store = SettingsStore(
177
+ userDefaults: defaults,
178
+ zaiTokenStore: NoopZaiTokenStore(),
179
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
180
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
181
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
182
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
183
+ groqTokenStore: NoopGroqTokenStore())
108
184
 
109
185
  #expect(store.codexUsageDataSource == .oauth)
110
186
  }
111
187
 
112
188
  @Test
113
- func defaultsOpenAIWebAccessToEnabled() {
189
+ func `defaults open AI web access to enabled`() throws {
114
190
  let suite = "SettingsStoreTests-openai-web"
115
- let defaults = UserDefaults(suiteName: suite)!
191
+ let defaults = try #require(UserDefaults(suiteName: suite))
116
192
  defaults.removePersistentDomain(forName: suite)
117
193
 
118
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
194
+ let store = SettingsStore(
195
+ userDefaults: defaults,
196
+ zaiTokenStore: NoopZaiTokenStore(),
197
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
198
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
199
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
200
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
201
+ groqTokenStore: NoopGroqTokenStore())
119
202
 
120
203
  #expect(store.openAIWebAccessEnabled == true)
121
204
  #expect(defaults.bool(forKey: "openAIWebAccessEnabled") == true)
122
205
  }
123
206
 
124
207
  @Test
125
- func providerOrder_defaultsToAllCases() {
208
+ func `provider order defaults to all cases`() throws {
126
209
  let suite = "SettingsStoreTests-providerOrder-default"
127
- let defaults = UserDefaults(suiteName: suite)!
210
+ let defaults = try #require(UserDefaults(suiteName: suite))
128
211
  defaults.removePersistentDomain(forName: suite)
129
212
  defaults.set(true, forKey: "providerDetectionCompleted")
130
213
 
131
- let store = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
214
+ let store = SettingsStore(
215
+ userDefaults: defaults,
216
+ zaiTokenStore: NoopZaiTokenStore(),
217
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
218
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
219
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
220
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
221
+ groqTokenStore: NoopGroqTokenStore())
132
222
 
133
223
  #expect(store.orderedProviders() == UsageProvider.allCases)
134
224
  }
135
225
 
136
226
  @Test
137
- func providerOrder_persistsAndAppendsNewProviders() {
227
+ func `provider order persists and appends new providers`() throws {
138
228
  let suite = "SettingsStoreTests-providerOrder-persist"
139
- let defaultsA = UserDefaults(suiteName: suite)!
229
+ let defaultsA = try #require(UserDefaults(suiteName: suite))
140
230
  defaultsA.removePersistentDomain(forName: suite)
141
231
  defaultsA.set(true, forKey: "providerDetectionCompleted")
142
232
 
143
233
  // Partial list to mimic "older version" missing providers.
144
234
  defaultsA.set([UsageProvider.gemini.rawValue, UsageProvider.codex.rawValue], forKey: "providerOrder")
145
235
 
146
- let storeA = SettingsStore(userDefaults: defaultsA, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
236
+ let storeA = SettingsStore(
237
+ userDefaults: defaultsA,
238
+ zaiTokenStore: NoopZaiTokenStore(),
239
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
240
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
241
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
242
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
243
+ groqTokenStore: NoopGroqTokenStore())
147
244
 
148
245
  let expectedAppended = UsageProvider.allCases.filter { ![.gemini, .codex].contains($0) }
149
246
  #expect(storeA.orderedProviders() == [.gemini, .codex] + expectedAppended)
150
247
 
151
248
  // Move one provider; ensure it's persisted across instances.
152
- let antigravityIndex = storeA.orderedProviders().firstIndex(of: .antigravity)!
249
+ let antigravityIndex = try #require(storeA.orderedProviders().firstIndex(of: .antigravity))
153
250
  storeA.moveProvider(fromOffsets: IndexSet(integer: antigravityIndex), toOffset: 0)
154
251
 
155
- let defaultsB = UserDefaults(suiteName: suite)!
252
+ let defaultsB = try #require(UserDefaults(suiteName: suite))
156
253
  defaultsB.set(true, forKey: "providerDetectionCompleted")
157
- let storeB = SettingsStore(userDefaults: defaultsB, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
254
+ let storeB = SettingsStore(
255
+ userDefaults: defaultsB,
256
+ zaiTokenStore: NoopZaiTokenStore(),
257
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
258
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
259
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
260
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
261
+ groqTokenStore: NoopGroqTokenStore())
158
262
 
159
263
  #expect(storeB.orderedProviders().first == .antigravity)
160
264
  }
@@ -4,11 +4,16 @@ import Testing
4
4
  @testable import Runic
5
5
 
6
6
  @MainActor
7
- @Suite
8
7
  struct StatusItemAnimationTests {
9
8
  @Test
10
- func mergedIconLoadingAnimationTracksSelectedProviderOnly() {
11
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
9
+ func `merged icon loading animation tracks selected provider only`() {
10
+ let settings = SettingsStore(
11
+ zaiTokenStore: NoopZaiTokenStore(),
12
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
13
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
14
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
15
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
16
+ groqTokenStore: NoopGroqTokenStore())
12
17
  settings.statusChecksEnabled = false
13
18
  settings.refreshFrequency = .manual
14
19
  settings.mergeIcons = true
@@ -4,15 +4,20 @@ import Testing
4
4
  @testable import Runic
5
5
 
6
6
  @MainActor
7
- @Suite
8
7
  struct StatusMenuTests {
9
8
  @Test
10
- func remembersProviderWhenMenuOpens() {
11
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
9
+ func `remembers provider when menu opens`() {
10
+ let settings = SettingsStore(
11
+ zaiTokenStore: NoopZaiTokenStore(),
12
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
13
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
14
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
15
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
16
+ groqTokenStore: NoopGroqTokenStore())
12
17
  settings.statusChecksEnabled = false
13
18
  settings.refreshFrequency = .manual
14
19
  settings.mergeIcons = true
15
- settings.menuMode = .`operator`
20
+ settings.menuMode = .operator
16
21
 
17
22
  let registry = ProviderRegistry.shared
18
23
  if let codexMeta = registry.metadata[.codex] {
@@ -48,12 +53,18 @@ struct StatusMenuTests {
48
53
  }
49
54
 
50
55
  @Test
51
- func hidesOpenAIWebSubmenusWhenNoHistory() {
52
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
56
+ func `hides open AI web submenus when no history`() {
57
+ let settings = SettingsStore(
58
+ zaiTokenStore: NoopZaiTokenStore(),
59
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
60
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
61
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
62
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
63
+ groqTokenStore: NoopGroqTokenStore())
53
64
  settings.statusChecksEnabled = false
54
65
  settings.refreshFrequency = .manual
55
66
  settings.mergeIcons = true
56
- settings.menuMode = .`operator`
67
+ settings.menuMode = .operator
57
68
  settings.selectedMenuProvider = .codex
58
69
 
59
70
  let registry = ProviderRegistry.shared
@@ -93,12 +104,18 @@ struct StatusMenuTests {
93
104
  }
94
105
 
95
106
  @Test
96
- func showsOpenAIWebSubmenusWhenHistoryExists() {
97
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
107
+ func `shows open AI web submenus when history exists`() throws {
108
+ let settings = SettingsStore(
109
+ zaiTokenStore: NoopZaiTokenStore(),
110
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
111
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
112
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
113
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
114
+ groqTokenStore: NoopGroqTokenStore())
98
115
  settings.statusChecksEnabled = false
99
116
  settings.refreshFrequency = .manual
100
117
  settings.mergeIcons = true
101
- settings.menuMode = .`operator`
118
+ settings.menuMode = .operator
102
119
  settings.selectedMenuProvider = .codex
103
120
 
104
121
  let registry = ProviderRegistry.shared
@@ -122,7 +139,7 @@ struct StatusMenuTests {
122
139
  components.year = 2025
123
140
  components.month = 12
124
141
  components.day = 18
125
- let date = components.date!
142
+ let date = try #require(components.date)
126
143
 
127
144
  let events = [CreditEvent(date: date, service: "CLI", creditsUsed: 1)]
128
145
  let breakdown = OpenAIDashboardSnapshot.makeDailyBreakdown(from: events, maxDays: 30)
@@ -153,12 +170,18 @@ struct StatusMenuTests {
153
170
  }
154
171
 
155
172
  @Test
156
- func showsCreditsBeforeCostInCodexMenuCardSections() {
157
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
173
+ func `shows credits before cost in codex menu card sections`() throws {
174
+ let settings = SettingsStore(
175
+ zaiTokenStore: NoopZaiTokenStore(),
176
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
177
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
178
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
179
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
180
+ groqTokenStore: NoopGroqTokenStore())
158
181
  settings.statusChecksEnabled = false
159
182
  settings.refreshFrequency = .manual
160
183
  settings.mergeIcons = true
161
- settings.menuMode = .`operator`
184
+ settings.menuMode = .operator
162
185
  settings.selectedMenuProvider = .codex
163
186
  settings.costUsageEnabled = true
164
187
 
@@ -215,16 +238,22 @@ struct StatusMenuTests {
215
238
  let costIndex = ids.firstIndex(of: "menuCardCost")
216
239
  #expect(creditsIndex != nil)
217
240
  #expect(costIndex != nil)
218
- #expect(creditsIndex! < costIndex!)
241
+ #expect(try #require(creditsIndex) < costIndex!)
219
242
  }
220
243
 
221
244
  @Test
222
- func showsExtraUsageForClaudeWhenUsingMenuCardSections() {
223
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
245
+ func `shows extra usage for claude when using menu card sections`() {
246
+ let settings = SettingsStore(
247
+ zaiTokenStore: NoopZaiTokenStore(),
248
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
249
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
250
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
251
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
252
+ groqTokenStore: NoopGroqTokenStore())
224
253
  settings.statusChecksEnabled = false
225
254
  settings.refreshFrequency = .manual
226
255
  settings.mergeIcons = true
227
- settings.menuMode = .`operator`
256
+ settings.menuMode = .operator
228
257
  settings.selectedMenuProvider = .claude
229
258
  settings.costUsageEnabled = true
230
259
  settings.claudeWebExtrasEnabled = true
@@ -292,8 +321,14 @@ struct StatusMenuTests {
292
321
  }
293
322
 
294
323
  @Test
295
- func glanceModeShowsHeadlineUsageOnly() {
296
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
324
+ func `glance mode shows headline usage only`() {
325
+ let settings = SettingsStore(
326
+ zaiTokenStore: NoopZaiTokenStore(),
327
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
328
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
329
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
330
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
331
+ groqTokenStore: NoopGroqTokenStore())
297
332
  settings.statusChecksEnabled = false
298
333
  settings.refreshFrequency = .manual
299
334
  settings.mergeIcons = true
@@ -361,8 +396,14 @@ struct StatusMenuTests {
361
396
  }
362
397
 
363
398
  @Test
364
- func analystModeShowsSummaryWithoutActions() {
365
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
399
+ func `analyst mode shows summary without actions`() {
400
+ let settings = SettingsStore(
401
+ zaiTokenStore: NoopZaiTokenStore(),
402
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
403
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
404
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
405
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
406
+ groqTokenStore: NoopGroqTokenStore())
366
407
  settings.statusChecksEnabled = false
367
408
  settings.refreshFrequency = .manual
368
409
  settings.mergeIcons = true