@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,10 +1,9 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
 
5
- @Suite
6
5
  struct GeminiStatusProbeTests {
7
- // Sample /stats output from Gemini CLI (actual format with box-drawing chars)
6
+ /// Sample /stats output from Gemini CLI (actual format with box-drawing chars)
8
7
  static let sampleStatsOutput = """
9
8
  │ Model Usage Reqs Usage left │
10
9
  │ ─────────────────────────────────────────────────────────────────────── │
@@ -17,14 +16,14 @@ struct GeminiStatusProbeTests {
17
16
  // MARK: - Legacy CLI parsing tests (kept for fallback support)
18
17
 
19
18
  @Test
20
- func parsesMinimumPercentFromMultipleModels() throws {
19
+ func `parses minimum percent from multiple models`() throws {
21
20
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
22
21
  #expect(snap.dailyPercentLeft == 99.8)
23
22
  #expect(snap.resetDescription == "Resets in 20h 37m")
24
23
  }
25
24
 
26
25
  @Test
27
- func parsesLowerPercentCorrectly() throws {
26
+ func `parses lower percent correctly`() throws {
28
27
  let output = """
29
28
  │ Model Usage Reqs Usage left │
30
29
  │ gemini-2.5-flash 10 85.5% (Resets in 12h) │
@@ -36,7 +35,7 @@ struct GeminiStatusProbeTests {
36
35
  }
37
36
 
38
37
  @Test
39
- func handlesZeroPercentUsage() throws {
38
+ func `handles zero percent usage`() throws {
40
39
  let output = """
41
40
  │ gemini-2.5-flash 50 0.0% (Resets in 6h) │
42
41
  │ gemini-2.5-pro 20 15.0% (Resets in 6h) │
@@ -47,7 +46,7 @@ struct GeminiStatusProbeTests {
47
46
  }
48
47
 
49
48
  @Test
50
- func handles100PercentRemaining() throws {
49
+ func `handles100 percent remaining`() throws {
51
50
  let output = """
52
51
  │ gemini-2.5-flash - 100.0% (Resets in 24h) │
53
52
  """
@@ -56,14 +55,14 @@ struct GeminiStatusProbeTests {
56
55
  }
57
56
 
58
57
  @Test
59
- func throwsOnEmptyOutput() {
58
+ func `throws on empty output`() {
60
59
  #expect(throws: GeminiStatusProbeError.self) {
61
60
  try GeminiStatusProbe.parse(text: "")
62
61
  }
63
62
  }
64
63
 
65
64
  @Test
66
- func throwsOnNoUsageData() {
65
+ func `throws on no usage data`() {
67
66
  let output = """
68
67
  Welcome to Gemini CLI!
69
68
  Type /help for available commands.
@@ -74,7 +73,7 @@ struct GeminiStatusProbeTests {
74
73
  }
75
74
 
76
75
  @Test
77
- func stripsANSICodesBeforeParsing() throws {
76
+ func `strips ANSI codes before parsing`() throws {
78
77
  // swiftlint:disable:next line_length
79
78
  let output = "\u{1B}[32m│\u{1B}[0m gemini-2.5-flash - 75.5% (Resets in 18h) │"
80
79
  let snap = try GeminiStatusProbe.parse(text: output)
@@ -82,7 +81,7 @@ struct GeminiStatusProbeTests {
82
81
  }
83
82
 
84
83
  @Test
85
- func preservesRawText() throws {
84
+ func `preserves raw text`() throws {
86
85
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
87
86
  #expect(snap.rawText == Self.sampleStatsOutput)
88
87
  #expect(snap.accountEmail == nil) // Legacy parse doesn't extract email
@@ -90,7 +89,7 @@ struct GeminiStatusProbeTests {
90
89
  }
91
90
 
92
91
  @Test
93
- func parsesVariousResetDescriptions() throws {
92
+ func `parses various reset descriptions`() throws {
94
93
  let cases: [(String, String)] = [
95
94
  ("Resets in 24h", "Resets in 24h"),
96
95
  ("Resets in 1h 30m", "Resets in 1h 30m"),
@@ -106,7 +105,7 @@ struct GeminiStatusProbeTests {
106
105
  }
107
106
 
108
107
  @Test
109
- func throwsNotLoggedInOnAuthPrompt() {
108
+ func `throws not logged in on auth prompt`() {
110
109
  let authOutputs = [
111
110
  "Waiting for auth... (Press ESC or CTRL+C to cancel)",
112
111
  "Login with Google\nUse Gemini API key",
@@ -122,7 +121,7 @@ struct GeminiStatusProbeTests {
122
121
  // MARK: - Model quota grouping tests
123
122
 
124
123
  @Test
125
- func parsesModelsIntoQuotaArray() throws {
124
+ func `parses models into quota array`() throws {
126
125
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
127
126
  // Should parse multiple models (exact count may change as Google adds/removes models)
128
127
  #expect(snap.modelQuotas.count >= 2)
@@ -134,14 +133,14 @@ struct GeminiStatusProbeTests {
134
133
  }
135
134
 
136
135
  @Test
137
- func lowestPercentLeftReturnsMinimum() throws {
136
+ func `lowest percent left returns minimum`() throws {
138
137
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
139
138
  // Flash models are 99.8%, Pro models are 100%, so min should be 99.8
140
139
  #expect(snap.lowestPercentLeft == 99.8)
141
140
  }
142
141
 
143
142
  @Test
144
- func tierGroupingByKeyword() throws {
143
+ func `tier grouping by keyword`() throws {
145
144
  // Test that flash/pro keyword filtering works (model names may change)
146
145
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
147
146
 
@@ -154,7 +153,7 @@ struct GeminiStatusProbeTests {
154
153
  }
155
154
 
156
155
  @Test
157
- func tierMinimumCalculation() throws {
156
+ func `tier minimum calculation`() throws {
158
157
  // Use controlled test data to verify min-per-tier logic
159
158
  // Model names must start with "gemini-" to match the parser regex
160
159
  let output = """
@@ -176,7 +175,7 @@ struct GeminiStatusProbeTests {
176
175
  }
177
176
 
178
177
  @Test
179
- func quotasHaveResetDescriptions() throws {
178
+ func `quotas have reset descriptions`() throws {
180
179
  let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
181
180
 
182
181
  // At least some quotas should have reset descriptions
@@ -185,7 +184,7 @@ struct GeminiStatusProbeTests {
185
184
  }
186
185
 
187
186
  @Test
188
- func toUsageSnapshotPreservesModelLabelsAndTertiaryWindow() throws {
187
+ func `to usage snapshot preserves model labels and tertiary window`() throws {
189
188
  let output = """
190
189
  │ gemini-2.5-pro 2 40.0% (Resets in 6h) │
191
190
  │ gemini-2.5-flash 5 65.0% (Resets in 3h) │
@@ -202,7 +201,7 @@ struct GeminiStatusProbeTests {
202
201
  }
203
202
 
204
203
  @Test
205
- func toUsageSnapshotFallsBackWhenProTierIsMissing() throws {
204
+ func `to usage snapshot falls back when pro tier is missing`() throws {
206
205
  let output = """
207
206
  │ gemini-2.5-flash 2 90.0% (Resets in 6h) │
208
207
  │ gemini-2.5-flash-lite 3 25.0% (Resets in 6h) │
@@ -216,7 +215,7 @@ struct GeminiStatusProbeTests {
216
215
  // MARK: - Live API test
217
216
 
218
217
  @Test
219
- func liveGeminiFetch() async throws {
218
+ func `live gemini fetch`() async throws {
220
219
  guard ProcessInfo.processInfo.environment["LIVE_GEMINI_FETCH"] == "1" else {
221
220
  return
222
221
  }
@@ -2,13 +2,12 @@ import Foundation
2
2
  import Testing
3
3
  @testable import Runic
4
4
 
5
- @Suite
6
5
  @MainActor
7
6
  struct GoogleWorkspaceStatusTests {
8
7
  private let productID = "npdyhgECDJ6tB66MxXyo"
9
8
 
10
9
  @Test
11
- func parseWorkspaceStatusSelectsWorstIncident() throws {
10
+ func `parse workspace status selects worst incident`() throws {
12
11
  let data = Data(#"""
13
12
  [
14
13
  {
@@ -47,7 +46,7 @@ struct GoogleWorkspaceStatusTests {
47
46
  }
48
47
 
49
48
  @Test
50
- func parseWorkspaceStatusIgnoresResolvedIncidents() throws {
49
+ func `parse workspace status ignores resolved incidents`() throws {
51
50
  let data = Data(#"""
52
51
  [
53
52
  {
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import Runic
4
4
 
5
- @Suite
6
5
  struct InstallOriginTests {
7
6
  @Test
8
- func detectsHomebrewCaskroom() {
7
+ func `detects homebrew caskroom`() {
9
8
  #expect(
10
9
  InstallOrigin
11
10
  .isHomebrewCask(
@@ -2,8 +2,8 @@ import KeyboardShortcuts
2
2
  import Testing
3
3
 
4
4
  @MainActor
5
- @Suite struct KeyboardShortcutsBundleTests {
6
- @Test func recorderInitializesWithoutCrashing() {
5
+ struct KeyboardShortcutsBundleTests {
6
+ @Test func `recorder initializes without crashing`() {
7
7
  _ = KeyboardShortcuts.RecorderCocoa(for: .init("test.keyboardshortcuts.bundle"))
8
8
  }
9
9
  }
@@ -1,13 +1,13 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
  import XCTest
5
5
  @testable import Runic
6
6
 
7
- @Suite("Live RPC account checks", .serialized)
7
+ @Suite(.serialized)
8
8
  struct LiveAccountTests {
9
9
  @Test(.disabled("Set LIVE_TEST=1 to run live Codex account checks."))
10
- func codexAccountEmailIsPresent() async throws {
10
+ func `codex account email is present`() async throws {
11
11
  guard ProcessInfo.processInfo.environment["LIVE_TEST"] == "1" else { return }
12
12
 
13
13
  let fetcher = UsageFetcher()
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import Runic
4
4
 
5
- @Suite
6
5
  struct LoadingPatternTests {
7
6
  @Test
8
- func valuesStayWithinBounds() {
7
+ func `values stay within bounds`() {
9
8
  for pattern in LoadingPattern.allCases {
10
9
  for phase in stride(from: 0.0, through: Double.pi * 2, by: Double.pi / 6) {
11
10
  let v = pattern.value(phase: phase)
@@ -15,7 +14,7 @@ struct LoadingPatternTests {
15
14
  }
16
15
 
17
16
  @Test
18
- func knightRiderPingPongs() {
17
+ func `knight rider ping pongs`() {
19
18
  let pattern = LoadingPattern.knightRider
20
19
  let mid = pattern.value(phase: 0) // sin 0 = 0 => 50
21
20
  let min = pattern.value(phase: -Double.pi / 2) // sin -pi/2 = -1 => 0
@@ -26,7 +25,7 @@ struct LoadingPatternTests {
26
25
  }
27
26
 
28
27
  @Test
29
- func secondaryOffsetDiffers() {
28
+ func `secondary offset differs`() {
30
29
  let pattern = LoadingPattern.cylon
31
30
  let primary = pattern.value(phase: 0)
32
31
  let secondary = pattern.value(phase: pattern.secondaryOffset)
@@ -1,11 +1,11 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import SwiftUI
4
4
  import Testing
5
5
  @testable import Runic
6
6
 
7
- private extension UsageMenuCardView.Model.Input {
8
- init(
7
+ extension UsageMenuCardView.Model.Input {
8
+ fileprivate init(
9
9
  provider: UsageProvider,
10
10
  metadata: ProviderMetadata,
11
11
  snapshot: UsageSnapshot?,
@@ -30,7 +30,7 @@ private extension UsageMenuCardView.Model.Input {
30
30
  isRefreshing: Bool,
31
31
  lastError: String?,
32
32
  usageBarsShowUsed: Bool,
33
- menuMode: MenuMode = .`operator`,
33
+ menuMode: MenuMode = .operator,
34
34
  tokenCostUsageEnabled: Bool,
35
35
  showOptionalCreditsAndExtraUsage: Bool,
36
36
  now: Date)
@@ -68,10 +68,9 @@ private extension UsageMenuCardView.Model.Input {
68
68
  }
69
69
  }
70
70
 
71
- @Suite
72
71
  struct MenuCardModelTests {
73
72
  @Test
74
- func buildsMetricsUsingRemainingPercent() {
73
+ func `builds metrics using remaining percent`() throws {
75
74
  let now = Date()
76
75
  let identity = ProviderIdentitySnapshot(
77
76
  providerID: .codex,
@@ -91,12 +90,12 @@ struct MenuCardModelTests {
91
90
  resetDescription: nil),
92
91
  updatedAt: now,
93
92
  identity: identity)
94
- let metadata = ProviderDefaults.metadata[.codex]!
95
- let updatedSnap = UsageSnapshot(
93
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
94
+ let updatedSnap = try UsageSnapshot(
96
95
  primary: snapshot.primary,
97
96
  secondary: RateWindow(
98
- usedPercent: snapshot.secondary!.usedPercent,
99
- windowMinutes: snapshot.secondary!.windowMinutes,
97
+ usedPercent: #require(snapshot.secondary?.usedPercent),
98
+ windowMinutes: #require(snapshot.secondary?.windowMinutes),
100
99
  resetsAt: now.addingTimeInterval(3600),
101
100
  resetDescription: nil),
102
101
  tertiary: snapshot.tertiary,
@@ -131,7 +130,7 @@ struct MenuCardModelTests {
131
130
  }
132
131
 
133
132
  @Test
134
- func buildsMetricsUsingUsedPercentWhenEnabled() {
133
+ func `builds metrics using used percent when enabled`() throws {
135
134
  let now = Date()
136
135
  let identity = ProviderIdentitySnapshot(
137
136
  providerID: .codex,
@@ -151,7 +150,7 @@ struct MenuCardModelTests {
151
150
  resetDescription: nil),
152
151
  updatedAt: now,
153
152
  identity: identity)
154
- let metadata = ProviderDefaults.metadata[.codex]!
153
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
155
154
 
156
155
  let dashboard = OpenAIDashboardSnapshot(
157
156
  signedInEmail: "codex@example.com",
@@ -187,7 +186,7 @@ struct MenuCardModelTests {
187
186
  }
188
187
 
189
188
  @Test
190
- func showsCodeReviewMetricWhenDashboardPresent() {
189
+ func `shows code review metric when dashboard present`() throws {
191
190
  let now = Date()
192
191
  let identity = ProviderIdentitySnapshot(
193
192
  providerID: .codex,
@@ -200,7 +199,7 @@ struct MenuCardModelTests {
200
199
  tertiary: nil,
201
200
  updatedAt: now,
202
201
  identity: identity)
203
- let metadata = ProviderDefaults.metadata[.codex]!
202
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
204
203
 
205
204
  let dashboard = OpenAIDashboardSnapshot(
206
205
  signedInEmail: "codex@example.com",
@@ -232,7 +231,7 @@ struct MenuCardModelTests {
232
231
  }
233
232
 
234
233
  @Test
235
- func claudeModelHidesWeeklyWhenUnavailable() {
234
+ func `claude model hides weekly when unavailable`() throws {
236
235
  let now = Date()
237
236
  let identity = ProviderIdentitySnapshot(
238
237
  providerID: .claude,
@@ -249,7 +248,7 @@ struct MenuCardModelTests {
249
248
  tertiary: nil,
250
249
  updatedAt: now,
251
250
  identity: identity)
252
- let metadata = ProviderDefaults.metadata[.claude]!
251
+ let metadata = try #require(ProviderDefaults.metadata[.claude])
253
252
  let model = UsageMenuCardView.Model.make(.init(
254
253
  provider: .claude,
255
254
  metadata: metadata,
@@ -274,8 +273,8 @@ struct MenuCardModelTests {
274
273
  }
275
274
 
276
275
  @Test
277
- func showsErrorSubtitleWhenPresent() {
278
- let metadata = ProviderDefaults.metadata[.codex]!
276
+ func `shows error subtitle when present`() throws {
277
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
279
278
  let model = UsageMenuCardView.Model.make(.init(
280
279
  provider: .codex,
281
280
  metadata: metadata,
@@ -300,9 +299,9 @@ struct MenuCardModelTests {
300
299
  }
301
300
 
302
301
  @Test
303
- func costSectionIncludesLast30DaysTokens() {
302
+ func `cost section includes last30 days tokens`() throws {
304
303
  let now = Date()
305
- let metadata = ProviderDefaults.metadata[.codex]!
304
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
306
305
  let snapshot = UsageSnapshot(
307
306
  primary: RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
308
307
  secondary: nil,
@@ -338,8 +337,8 @@ struct MenuCardModelTests {
338
337
  }
339
338
 
340
339
  @Test
341
- func claudeModelDoesNotLeakCodexPlan() {
342
- let metadata = ProviderDefaults.metadata[.claude]!
340
+ func `claude model does not leak codex plan`() throws {
341
+ let metadata = try #require(ProviderDefaults.metadata[.claude])
343
342
  let model = UsageMenuCardView.Model.make(.init(
344
343
  provider: .claude,
345
344
  metadata: metadata,
@@ -363,7 +362,7 @@ struct MenuCardModelTests {
363
362
  }
364
363
 
365
364
  @Test
366
- func hidesCodexCreditsWhenDisabled() {
365
+ func `hides codex credits when disabled`() throws {
367
366
  let now = Date()
368
367
  let identity = ProviderIdentitySnapshot(
369
368
  providerID: .codex,
@@ -376,7 +375,7 @@ struct MenuCardModelTests {
376
375
  tertiary: nil,
377
376
  updatedAt: now,
378
377
  identity: identity)
379
- let metadata = ProviderDefaults.metadata[.codex]!
378
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
380
379
 
381
380
  let model = UsageMenuCardView.Model.make(.init(
382
381
  provider: .codex,
@@ -400,7 +399,7 @@ struct MenuCardModelTests {
400
399
  }
401
400
 
402
401
  @Test
403
- func hidesClaudeExtraUsageWhenDisabled() {
402
+ func `hides claude extra usage when disabled`() throws {
404
403
  let now = Date()
405
404
  let identity = ProviderIdentitySnapshot(
406
405
  providerID: .claude,
@@ -414,7 +413,7 @@ struct MenuCardModelTests {
414
413
  providerCost: ProviderCostSnapshot(used: 12, limit: 200, currencyCode: "USD", updatedAt: now),
415
414
  updatedAt: now,
416
415
  identity: identity)
417
- let metadata = ProviderDefaults.metadata[.claude]!
416
+ let metadata = try #require(ProviderDefaults.metadata[.claude])
418
417
 
419
418
  let model = UsageMenuCardView.Model.make(.init(
420
419
  provider: .claude,
@@ -438,9 +437,9 @@ struct MenuCardModelTests {
438
437
  }
439
438
 
440
439
  @Test
441
- func insightsShowForecastAndBudgetETAForTopProject() {
440
+ func `insights show forecast and budget ETA for top project`() throws {
442
441
  let now = Date()
443
- let metadata = ProviderDefaults.metadata[.codex]!
442
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
444
443
  let topProject = UsageLedgerProjectSummary(
445
444
  provider: .codex,
446
445
  projectKey: "id:proj-runic",
@@ -448,8 +447,8 @@ struct MenuCardModelTests {
448
447
  projectName: "Runic",
449
448
  entryCount: 5,
450
449
  totals: UsageLedgerTotals(
451
- inputTokens: 2_000,
452
- outputTokens: 1_000,
450
+ inputTokens: 2000,
451
+ outputTokens: 1000,
453
452
  cacheCreationTokens: 0,
454
453
  cacheReadTokens: 0,
455
454
  costUSD: 12.5),
@@ -503,16 +502,16 @@ struct MenuCardModelTests {
503
502
  }
504
503
 
505
504
  @Test
506
- func insightsSurfaceAnomalySeverityLine() {
507
- let metadata = ProviderDefaults.metadata[.codex]!
505
+ func `insights surface anomaly severity line`() throws {
506
+ let metadata = try #require(ProviderDefaults.metadata[.codex])
508
507
  let anomaly = UsageLedgerAnomalySummary(
509
508
  provider: .codex,
510
509
  baselineDays: 7,
511
510
  tokenAnomaly: UsageLedgerAnomalySummary.MetricAnomaly(
512
511
  metric: .tokens,
513
512
  severity: .high,
514
- todayValue: 4_200,
515
- baselineAverage: 1_500,
513
+ todayValue: 4200,
514
+ baselineAverage: 1500,
516
515
  percentIncrease: 1.8),
517
516
  spendAnomaly: UsageLedgerAnomalySummary.MetricAnomaly(
518
517
  metric: .spend,
@@ -1,7 +1,10 @@
1
1
  @testable import Runic
2
2
 
3
3
  struct NoopMiniMaxCookieHeaderStore: MiniMaxCookieHeaderStoring {
4
- func loadHeader() throws -> String? { nil }
4
+ func loadHeader() throws -> String? {
5
+ nil
6
+ }
7
+
5
8
  func storeHeader(_ header: String?) throws {
6
9
  _ = header
7
10
  }
@@ -1,7 +1,10 @@
1
1
  @testable import Runic
2
2
 
3
3
  struct NoopMiniMaxGroupIDStore: MiniMaxGroupIDStoring {
4
- func loadGroupID() throws -> String? { nil }
4
+ func loadGroupID() throws -> String? {
5
+ nil
6
+ }
7
+
5
8
  func storeGroupID(_ groupID: String?) throws {
6
9
  _ = groupID
7
10
  }
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct MiniMaxParsingTests {
7
6
  @Test
8
- func parseManualInputCookieHeader() {
7
+ func `parse manual input cookie header`() {
9
8
  let input = "Cookie: foo=bar; baz=qux"
10
9
  let parsed = MiniMaxWebParsing.parseManualInput(input)
11
10
  #expect(parsed?.cookieHeader == "foo=bar; baz=qux")
@@ -13,7 +12,7 @@ struct MiniMaxParsingTests {
13
12
  }
14
13
 
15
14
  @Test
16
- func parseManualInputCurlExtractsBearerAndGroupID() {
15
+ func `parse manual input curl extracts bearer and group ID`() {
17
16
  let curl = """
18
17
  curl 'https://platform.minimax.io/v1/api/openplatform/coding_plan/remains?GroupId=group_123' \
19
18
  -H 'Cookie: session=abc; foo=bar' \
@@ -26,7 +25,7 @@ struct MiniMaxParsingTests {
26
25
  }
27
26
 
28
27
  @Test
29
- func parseHTMLUsageWithPercentAndReset() {
28
+ func `parse HTML usage with percent and reset`() {
30
29
  let html = """
31
30
  <html>
32
31
  <head><title>MiniMax Pro</title></head>
@@ -45,7 +44,7 @@ struct MiniMaxParsingTests {
45
44
  }
46
45
 
47
46
  @Test
48
- func parseRemainsResponseUsesModelRemains() throws {
47
+ func `parse remains response uses model remains`() throws {
49
48
  let json = """
50
49
  {
51
50
  "base_resp": { "retcode": 0, "msg": "ok", "success": true },
@@ -1,7 +1,10 @@
1
1
  @testable import Runic
2
2
 
3
3
  struct NoopMiniMaxTokenStore: MiniMaxTokenStoring {
4
- func loadToken() throws -> String? { nil }
4
+ func loadToken() throws -> String? {
5
+ nil
6
+ }
7
+
5
8
  func storeToken(_ token: String?) throws {
6
9
  _ = token
7
10
  }
@@ -1,12 +1,10 @@
1
1
  import Foundation
2
2
  import Testing
3
-
4
3
  @testable import RunicCore
5
4
 
6
- @Suite
7
5
  struct OTelGenAILedgerAdapterTests {
8
6
  @Test
9
- func parsesFlatGenAIPayloadIntoLedgerEntry() throws {
7
+ func `parses flat gen AI payload into ledger entry`() throws {
10
8
  let payload = """
11
9
  {
12
10
  "timestamp": "2026-02-23T12:00:00Z",
@@ -40,7 +38,7 @@ struct OTelGenAILedgerAdapterTests {
40
38
  }
41
39
 
42
40
  @Test
43
- func parsesOTLPResourceSpansPayload() throws {
41
+ func `parses OTLP resource spans payload`() throws {
44
42
  let payload = """
45
43
  {
46
44
  "resourceSpans": [
@@ -86,7 +84,7 @@ struct OTelGenAILedgerAdapterTests {
86
84
  }
87
85
 
88
86
  @Test
89
- func featureFlagDisablesIngestion() throws {
87
+ func `feature flag disables ingestion`() throws {
90
88
  let payload = """
91
89
  {"attributes":{"gen_ai.system":"openai","gen_ai.request.model":"gpt-5","gen_ai.usage.input_tokens":1}}
92
90
  """
@@ -97,7 +95,7 @@ struct OTelGenAILedgerAdapterTests {
97
95
  }
98
96
 
99
97
  @Test
100
- func defaultProviderBackfillsUnknownProvider() throws {
98
+ func `default provider backfills unknown provider`() throws {
101
99
  let payload = """
102
100
  {"attributes":{"gen_ai.request.model":"custom-model-v1","gen_ai.usage.input_tokens":9}}
103
101
  """
@@ -1,10 +1,9 @@
1
1
  import RunicCore
2
2
  import Testing
3
3
 
4
- @Suite
5
4
  struct OpenAIDashboardBrowserCookieImporterTests {
6
5
  @Test
7
- func mismatchErrorMentionsSourceLabel() {
6
+ func `mismatch error mentions source label`() {
8
7
  let err = OpenAIDashboardBrowserCookieImporter.ImportError.noMatchingAccount(
9
8
  found: [
10
9
  .init(sourceLabel: "Safari", email: "a@example.com"),
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct OpenAIDashboardFetcherCreditsWaitTests {
7
6
  @Test
8
- func waitsAfterScrollRequest() {
7
+ func `waits after scroll request`() {
9
8
  let now = Date()
10
9
  let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
11
10
  now: now,
@@ -18,7 +17,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
18
17
  }
19
18
 
20
19
  @Test
21
- func waitsBrieflyWhenHeaderVisibleButTableEmpty() {
20
+ func `waits briefly when header visible but table empty`() {
22
21
  let now = Date()
23
22
  let visibleAt = now.addingTimeInterval(-1.0)
24
23
  let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
@@ -32,7 +31,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
32
31
  }
33
32
 
34
33
  @Test
35
- func stopsWaitingAfterHeaderHasBeenVisibleLongEnough() {
34
+ func `stops waiting after header has been visible long enough`() {
36
35
  let now = Date()
37
36
  let visibleAt = now.addingTimeInterval(-3.0)
38
37
  let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
@@ -46,7 +45,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
46
45
  }
47
46
 
48
47
  @Test
49
- func waitsBrieflyAfterFirstDashboardSignalEvenWhenHeaderNotPresentYet() {
48
+ func `waits briefly after first dashboard signal even when header not present yet`() {
50
49
  let now = Date()
51
50
  let startedAt = now.addingTimeInterval(-2.0)
52
51
  let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
@@ -60,7 +59,7 @@ struct OpenAIDashboardFetcherCreditsWaitTests {
60
59
  }
61
60
 
62
61
  @Test
63
- func stopsWaitingEventuallyWhenHeaderNeverAppears() {
62
+ func `stops waiting eventually when header never appears`() {
64
63
  let now = Date()
65
64
  let startedAt = now.addingTimeInterval(-7.0)
66
65
  let shouldWait = OpenAIDashboardFetcher.shouldWaitForCreditsHistory(.init(
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct OpenAIDashboardOffscreenHostTests {
7
6
  @Test
8
- func offscreenHostFrameOnlyIntersectsByASliver() {
7
+ func `offscreen host frame only intersects by A sliver`() {
9
8
  let visibleFrame = CGRect(x: 0, y: 0, width: 1000, height: 800)
10
9
  let frame = OpenAIDashboardFetcher.offscreenHostWindowFrame(for: visibleFrame)
11
10
  let intersection = frame.intersection(visibleFrame)
@@ -19,7 +18,7 @@ struct OpenAIDashboardOffscreenHostTests {
19
18
  }
20
19
 
21
20
  @Test
22
- func offscreenHostAlphaValueIsNonZeroButTiny() {
21
+ func `offscreen host alpha value is non zero but tiny`() {
23
22
  let alpha = OpenAIDashboardFetcher.offscreenHostAlphaValue()
24
23
  #expect(alpha > 0)
25
24
  #expect(alpha <= 0.001)