@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,12 +1,11 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
  @testable import Runic
5
5
 
6
- @Suite
7
6
  struct OpenAIDashboardParserTests {
8
7
  @Test
9
- func parsesSignedInEmailFromClientBootstrapHTML() {
8
+ func `parses signed in email from client bootstrap HTML`() {
10
9
  let html = """
11
10
  <html>
12
11
  <head></head>
@@ -22,26 +21,26 @@ struct OpenAIDashboardParserTests {
22
21
  }
23
22
 
24
23
  @Test
25
- func parsesCodeReviewRemainingPercent_inline() {
24
+ func `parses code review remaining percent inline`() {
26
25
  let body = "Balance\nCode review 42% remaining\nCredits remaining 291"
27
26
  #expect(OpenAIDashboardParser.parseCodeReviewRemainingPercent(bodyText: body) == 42)
28
27
  }
29
28
 
30
29
  @Test
31
- func parsesCodeReviewRemainingPercent_multiline() {
30
+ func `parses code review remaining percent multiline`() {
32
31
  let body = "Balance\nCode review\n100% remaining\nWeekly usage limit\n0% remaining"
33
32
  #expect(OpenAIDashboardParser.parseCodeReviewRemainingPercent(bodyText: body) == 100)
34
33
  }
35
34
 
36
35
  @Test
37
- func parsesCreditsRemaining() {
36
+ func `parses credits remaining`() {
38
37
  let body = "Balance\nCredits remaining 1,234.56\nUsage"
39
38
  let value = OpenAIDashboardParser.parseCreditsRemaining(bodyText: body)
40
39
  #expect(abs((value ?? 0) - 1234.56) < 0.001)
41
40
  }
42
41
 
43
42
  @Test
44
- func parsesRateLimits() {
43
+ func `parses rate limits`() {
45
44
  let body = """
46
45
  Usage limits
47
46
  5h limit
@@ -60,7 +59,7 @@ struct OpenAIDashboardParserTests {
60
59
  }
61
60
 
62
61
  @Test
63
- func parsesPlanFromClientBootstrap() {
62
+ func `parses plan from client bootstrap`() {
64
63
  let html = """
65
64
  <html>
66
65
  <body>
@@ -74,7 +73,7 @@ struct OpenAIDashboardParserTests {
74
73
  }
75
74
 
76
75
  @Test
77
- func parsesCreditEventsFromTableRows() {
76
+ func `parses credit events from table rows`() {
78
77
  let rows: [[String]] = [
79
78
  ["Dec 18, 2025", "CLI", "397.205 credits"],
80
79
  ["Dec 17, 2025", "GitHub Code Review", "506.235 credits"],
@@ -88,7 +87,7 @@ struct OpenAIDashboardParserTests {
88
87
  }
89
88
 
90
89
  @Test
91
- func buildsDailyBreakdownFromEvents() {
90
+ func `builds daily breakdown from events`() throws {
92
91
  let calendar = Calendar(identifier: .gregorian)
93
92
  var components = DateComponents()
94
93
  components.calendar = calendar
@@ -97,10 +96,10 @@ struct OpenAIDashboardParserTests {
97
96
  components.year = 2025
98
97
  components.month = 12
99
98
  components.day = 18
100
- let dec18 = components.date!
99
+ let dec18 = try #require(components.date)
101
100
 
102
101
  components.day = 17
103
- let dec17 = components.date!
102
+ let dec17 = try #require(components.date)
104
103
 
105
104
  let events = [
106
105
  CreditEvent(date: dec18, service: "CLI", creditsUsed: 10),
@@ -116,7 +115,7 @@ struct OpenAIDashboardParserTests {
116
115
  }
117
116
 
118
117
  @Test
119
- func decodesSnapshotWithoutUsageBreakdownField() throws {
118
+ func `decodes snapshot without usage breakdown field`() throws {
120
119
  let json = """
121
120
  {
122
121
  "signedInEmail": "user@example.com",
@@ -1,14 +1,19 @@
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 OpenAIWebAccountSwitchTests {
9
8
  @Test
10
- func clearsDashboardWhenCodexEmailChanges() {
11
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
9
+ func `clears dashboard when codex email changes`() {
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.refreshFrequency = .manual
13
18
 
14
19
  let store = UsageStore(fetcher: UsageFetcher(), settings: settings)
@@ -30,8 +35,14 @@ struct OpenAIWebAccountSwitchTests {
30
35
  }
31
36
 
32
37
  @Test
33
- func keepsDashboardWhenCodexEmailStaysSame() {
34
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
38
+ func `keeps dashboard when codex email stays same`() {
39
+ let settings = SettingsStore(
40
+ zaiTokenStore: NoopZaiTokenStore(),
41
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
42
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
43
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
44
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
45
+ groqTokenStore: NoopGroqTokenStore())
35
46
  settings.refreshFrequency = .manual
36
47
 
37
48
  let store = UsageStore(fetcher: UsageFetcher(), settings: settings)
@@ -1,91 +1,130 @@
1
1
  @testable import Runic
2
2
 
3
3
  struct NoopOpenRouterTokenStore: OpenRouterTokenStoring {
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
  }
8
11
  }
9
12
 
10
13
  struct NoopGroqTokenStore: GroqTokenStoring {
11
- func loadToken() throws -> String? { nil }
14
+ func loadToken() throws -> String? {
15
+ nil
16
+ }
17
+
12
18
  func storeToken(_ token: String?) throws {
13
19
  _ = token
14
20
  }
15
21
  }
16
22
 
17
23
  struct NoopDeepSeekTokenStore: DeepSeekTokenStoring {
18
- func loadToken() throws -> String? { nil }
24
+ func loadToken() throws -> String? {
25
+ nil
26
+ }
27
+
19
28
  func storeToken(_ token: String?) throws {
20
29
  _ = token
21
30
  }
22
31
  }
23
32
 
24
33
  struct NoopFireworksTokenStore: FireworksTokenStoring {
25
- func loadToken() throws -> String? { nil }
34
+ func loadToken() throws -> String? {
35
+ nil
36
+ }
37
+
26
38
  func storeToken(_ token: String?) throws {
27
39
  _ = token
28
40
  }
29
41
  }
30
42
 
31
43
  struct NoopMistralTokenStore: MistralTokenStoring {
32
- func loadToken() throws -> String? { nil }
44
+ func loadToken() throws -> String? {
45
+ nil
46
+ }
47
+
33
48
  func storeToken(_ token: String?) throws {
34
49
  _ = token
35
50
  }
36
51
  }
37
52
 
38
53
  struct NoopPerplexityTokenStore: PerplexityTokenStoring {
39
- func loadToken() throws -> String? { nil }
54
+ func loadToken() throws -> String? {
55
+ nil
56
+ }
57
+
40
58
  func storeToken(_ token: String?) throws {
41
59
  _ = token
42
60
  }
43
61
  }
44
62
 
45
63
  struct NoopKimiTokenStore: KimiTokenStoring {
46
- func loadToken() throws -> String? { nil }
64
+ func loadToken() throws -> String? {
65
+ nil
66
+ }
67
+
47
68
  func storeToken(_ token: String?) throws {
48
69
  _ = token
49
70
  }
50
71
  }
51
72
 
52
73
  struct NoopAuggieTokenStore: AuggieTokenStoring {
53
- func loadToken() throws -> String? { nil }
74
+ func loadToken() throws -> String? {
75
+ nil
76
+ }
77
+
54
78
  func storeToken(_ token: String?) throws {
55
79
  _ = token
56
80
  }
57
81
  }
58
82
 
59
83
  struct NoopTogetherTokenStore: TogetherTokenStoring {
60
- func loadToken() throws -> String? { nil }
84
+ func loadToken() throws -> String? {
85
+ nil
86
+ }
87
+
61
88
  func storeToken(_ token: String?) throws {
62
89
  _ = token
63
90
  }
64
91
  }
65
92
 
66
93
  struct NoopCohereTokenStore: CohereTokenStoring {
67
- func loadToken() throws -> String? { nil }
94
+ func loadToken() throws -> String? {
95
+ nil
96
+ }
97
+
68
98
  func storeToken(_ token: String?) throws {
69
99
  _ = token
70
100
  }
71
101
  }
72
102
 
73
103
  struct NoopXAITokenStore: XAITokenStoring {
74
- func loadToken() throws -> String? { nil }
104
+ func loadToken() throws -> String? {
105
+ nil
106
+ }
107
+
75
108
  func storeToken(_ token: String?) throws {
76
109
  _ = token
77
110
  }
78
111
  }
79
112
 
80
113
  struct NoopCerebrasTokenStore: CerebrasTokenStoring {
81
- func loadToken() throws -> String? { nil }
114
+ func loadToken() throws -> String? {
115
+ nil
116
+ }
117
+
82
118
  func storeToken(_ token: String?) throws {
83
119
  _ = token
84
120
  }
85
121
  }
86
122
 
87
123
  struct NoopSambaNovaTokenStore: SambaNovaTokenStoring {
88
- func loadToken() throws -> String? { nil }
124
+ func loadToken() throws -> String? {
125
+ nil
126
+ }
127
+
89
128
  func storeToken(_ token: String?) throws {
90
129
  _ = token
91
130
  }
@@ -1,12 +1,11 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
  @testable import Runic
5
5
 
6
- @Suite
7
6
  struct PathBuilderTests {
8
7
  @Test
9
- func mergesLoginShellPathWhenAvailable() {
8
+ func `merges login shell path when available`() {
10
9
  let seeded = PathBuilder.effectivePATH(
11
10
  purposes: [.rpc],
12
11
  env: ["PATH": "/custom/bin:/usr/bin"],
@@ -15,7 +14,7 @@ struct PathBuilderTests {
15
14
  }
16
15
 
17
16
  @Test
18
- func fallsBackToExistingPathWhenNoLoginPath() {
17
+ func `falls back to existing path when no login path`() {
19
18
  let seeded = PathBuilder.effectivePATH(
20
19
  purposes: [.tty],
21
20
  env: ["PATH": "/custom/bin:/usr/bin"],
@@ -24,7 +23,7 @@ struct PathBuilderTests {
24
23
  }
25
24
 
26
25
  @Test
27
- func usesFallbackWhenNoPathAvailable() {
26
+ func `uses fallback when no path available`() {
28
27
  let seeded = PathBuilder.effectivePATH(
29
28
  purposes: [.tty],
30
29
  env: [:],
@@ -33,7 +32,7 @@ struct PathBuilderTests {
33
32
  }
34
33
 
35
34
  @Test
36
- func resolvesCodexFromEnvOverride() {
35
+ func `resolves codex from env override`() {
37
36
  let overridePath = "/custom/bin/codex"
38
37
  let fm = MockFileManager(executables: [overridePath])
39
38
 
@@ -46,7 +45,7 @@ struct PathBuilderTests {
46
45
  }
47
46
 
48
47
  @Test
49
- func resolvesCodexFromLoginPath() {
48
+ func `resolves codex from login path`() {
50
49
  let fm = MockFileManager(executables: ["/login/bin/codex"])
51
50
  let resolved = BinaryLocator.resolveCodexBinary(
52
51
  env: ["PATH": "/env/bin"],
@@ -57,7 +56,7 @@ struct PathBuilderTests {
57
56
  }
58
57
 
59
58
  @Test
60
- func resolvesCodexFromEnvPath() {
59
+ func `resolves codex from env path`() {
61
60
  let fm = MockFileManager(executables: ["/env/bin/codex"])
62
61
  let resolved = BinaryLocator.resolveCodexBinary(
63
62
  env: ["PATH": "/env/bin:/usr/bin"],
@@ -68,7 +67,7 @@ struct PathBuilderTests {
68
67
  }
69
68
 
70
69
  @Test
71
- func resolvesCodexFromInteractiveShell() {
70
+ func `resolves codex from interactive shell`() {
72
71
  let fm = MockFileManager(executables: ["/shell/bin/codex"])
73
72
  let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
74
73
  #expect(tool == "codex")
@@ -88,7 +87,7 @@ struct PathBuilderTests {
88
87
  }
89
88
 
90
89
  @Test
91
- func resolvesClaudeFromInteractiveShell() {
90
+ func `resolves claude from interactive shell`() {
92
91
  let fm = MockFileManager(executables: ["/shell/bin/claude"])
93
92
  let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
94
93
  #expect(tool == "claude")
@@ -108,7 +107,7 @@ struct PathBuilderTests {
108
107
  }
109
108
 
110
109
  @Test
111
- func resolvesGeminiFromInteractiveShell() {
110
+ func `resolves gemini from interactive shell`() {
112
111
  let fm = MockFileManager(executables: ["/shell/bin/gemini"])
113
112
  let commandV: (String, String?, TimeInterval, FileManager) -> String? = { tool, shell, timeout, fileManager in
114
113
  #expect(tool == "gemini")
@@ -128,7 +127,7 @@ struct PathBuilderTests {
128
127
  }
129
128
 
130
129
  @Test
131
- func resolvesClaudeFromLoginPath() {
130
+ func `resolves claude from login path`() {
132
131
  let fm = MockFileManager(executables: ["/login/bin/claude"])
133
132
  let resolved = BinaryLocator.resolveClaudeBinary(
134
133
  env: ["PATH": "/env/bin"],
@@ -139,7 +138,7 @@ struct PathBuilderTests {
139
138
  }
140
139
 
141
140
  @Test
142
- func resolvesClaudeFromAliasWhenOtherLookupsFail() {
141
+ func `resolves claude from alias when other lookups fail`() {
143
142
  let aliasPath = "/home/test/.claude/local/bin/claude"
144
143
  let fm = MockFileManager(executables: [aliasPath])
145
144
  var aliasCalled = false
@@ -169,7 +168,7 @@ struct PathBuilderTests {
169
168
  }
170
169
 
171
170
  @Test
172
- func resolvesCodexFromAliasWhenOtherLookupsFail() {
171
+ func `resolves codex from alias when other lookups fail`() {
173
172
  let aliasPath = "/home/test/.codex/bin/codex"
174
173
  let fm = MockFileManager(executables: [aliasPath])
175
174
  var aliasCalled = false
@@ -199,7 +198,7 @@ struct PathBuilderTests {
199
198
  }
200
199
 
201
200
  @Test
202
- func skipsAliasWhenCommandVResolves() {
201
+ func `skips alias when command V resolves`() {
203
202
  let path = "/shell/bin/claude"
204
203
  let fm = MockFileManager(executables: [path])
205
204
  var aliasCalled = false
@@ -2,7 +2,6 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct ProviderFetchPipelineTests {
7
6
  private struct SlowStrategy: ProviderFetchStrategy {
8
7
  let id: String
@@ -90,7 +89,7 @@ struct ProviderFetchPipelineTests {
90
89
  }
91
90
 
92
91
  @Test
93
- func hangsAreConvertedToStrategyTimeout() async {
92
+ func `hangs are converted to strategy timeout`() async {
94
93
  let pipeline = ProviderFetchPipeline(
95
94
  resolveStrategies: { _ in [SlowStrategy(id: "provider.fetchPipeline.hang", shouldFallbackValue: false)] },
96
95
  strategyTimeout: 0.05)
@@ -101,7 +100,7 @@ struct ProviderFetchPipelineTests {
101
100
  #expect(outcome.attempts[0].strategyID == "provider.fetchPipeline.hang")
102
101
  #expect(outcome.attempts[0].wasAvailable == true)
103
102
 
104
- guard case .failure(let error) = outcome.result else {
103
+ guard case let .failure(error) = outcome.result else {
105
104
  Issue.record("Expected fetch failure")
106
105
  return
107
106
  }
@@ -115,7 +114,7 @@ struct ProviderFetchPipelineTests {
115
114
  }
116
115
 
117
116
  @Test
118
- func strategyFallbackStillHonorsOrder() async {
117
+ func `strategy fallback still honors order`() async {
119
118
  let expected = Self.makeStubResult()
120
119
  let pipeline = ProviderFetchPipeline(
121
120
  resolveStrategies: { _ in [
@@ -3,10 +3,9 @@ import Foundation
3
3
  import Testing
4
4
 
5
5
  @MainActor
6
- @Suite
7
6
  struct ProviderIconResourcesTests {
8
7
  @Test
9
- func providerIconSVGsExist() throws {
8
+ func `provider icon SV gs exist`() throws {
10
9
  let root = try Self.repoRoot()
11
10
  let resources = root.appending(path: "Sources/Runic/Resources", directoryHint: .isDirectory)
12
11
 
@@ -1,10 +1,9 @@
1
1
  import Testing
2
2
  @testable import RunicCore
3
3
 
4
- @Suite
5
4
  struct ProviderMetadataStatusLinkTests {
6
5
  @Test
7
- func workspaceStatusLinkMatchesProductID() {
6
+ func `workspace status link matches product ID`() {
8
7
  for (provider, meta) in ProviderDefaults.metadata {
9
8
  guard let productID = meta.statusWorkspaceProductID else { continue }
10
9
  let expected = "https://www.google.com/appsstatus/dashboard/products/\(productID)/history"
@@ -1,17 +1,23 @@
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
7
  @MainActor
8
- @Suite
9
8
  struct ProviderSettingsDescriptorTests {
10
9
  @Test
11
- func toggleIDsAreUniqueAcrossProviders() {
12
- let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-unique")!
10
+ func `toggle I ds are unique across providers`() throws {
11
+ let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-unique"))
13
12
  defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-unique")
14
- let settings = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
13
+ let settings = SettingsStore(
14
+ userDefaults: defaults,
15
+ zaiTokenStore: NoopZaiTokenStore(),
16
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
17
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
18
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
19
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
20
+ groqTokenStore: NoopGroqTokenStore())
15
21
  let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
16
22
 
17
23
  var statusByID: [String: String] = [:]
@@ -52,7 +58,7 @@ struct ProviderSettingsDescriptorTests {
52
58
  },
53
59
  requestConfirmation: { _ in })
54
60
 
55
- let impl = ProviderCatalog.implementation(for: provider)!
61
+ let impl = try #require(ProviderCatalog.implementation(for: provider))
56
62
  let toggles = impl.settingsToggles(context: context)
57
63
  for toggle in toggles {
58
64
  #expect(!seenToggleIDs.contains(toggle.id))
@@ -67,10 +73,17 @@ struct ProviderSettingsDescriptorTests {
67
73
  }
68
74
 
69
75
  @Test
70
- func codexDoesNotExposeOpenAIWebToggle() {
71
- let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-codex")!
76
+ func `codex does not expose open AI web toggle`() throws {
77
+ let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-codex"))
72
78
  defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-codex")
73
- let settings = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
79
+ let settings = SettingsStore(
80
+ userDefaults: defaults,
81
+ zaiTokenStore: NoopZaiTokenStore(),
82
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
83
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
84
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
85
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
86
+ groqTokenStore: NoopGroqTokenStore())
74
87
  let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
75
88
 
76
89
  let context = ProviderSettingsContext(
@@ -98,10 +111,17 @@ struct ProviderSettingsDescriptorTests {
98
111
  }
99
112
 
100
113
  @Test
101
- func claudeDoesNotExposeSettingsToggles() {
102
- let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude")!
114
+ func `claude does not expose settings toggles`() throws {
115
+ let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude"))
103
116
  defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-claude")
104
- let settings = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
117
+ let settings = SettingsStore(
118
+ userDefaults: defaults,
119
+ zaiTokenStore: NoopZaiTokenStore(),
120
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
121
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
122
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
123
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
124
+ groqTokenStore: NoopGroqTokenStore())
105
125
  let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
106
126
 
107
127
  let context = ProviderSettingsContext(
@@ -128,10 +148,17 @@ struct ProviderSettingsDescriptorTests {
128
148
  }
129
149
 
130
150
  @Test
131
- func claudeWebExtrasAutoDisablesWhenLeavingCLI() {
132
- let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude-invariant")!
151
+ func `claude web extras auto disables when leaving CLI`() throws {
152
+ let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-claude-invariant"))
133
153
  defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-claude-invariant")
134
- let settings = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
154
+ let settings = SettingsStore(
155
+ userDefaults: defaults,
156
+ zaiTokenStore: NoopZaiTokenStore(),
157
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
158
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
159
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
160
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
161
+ groqTokenStore: NoopGroqTokenStore())
135
162
  settings.debugMenuEnabled = true
136
163
  settings.claudeUsageDataSource = .cli
137
164
  settings.claudeWebExtrasEnabled = true
@@ -141,10 +168,17 @@ struct ProviderSettingsDescriptorTests {
141
168
  }
142
169
 
143
170
  @Test
144
- func extraProvidersExposeExpectedSettingsFields() {
145
- let defaults = UserDefaults(suiteName: "ProviderSettingsDescriptorTests-extra-fields")!
171
+ func `extra providers expose expected settings fields`() throws {
172
+ let defaults = try #require(UserDefaults(suiteName: "ProviderSettingsDescriptorTests-extra-fields"))
146
173
  defaults.removePersistentDomain(forName: "ProviderSettingsDescriptorTests-extra-fields")
147
- let settings = SettingsStore(userDefaults: defaults, zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
174
+ let settings = SettingsStore(
175
+ userDefaults: defaults,
176
+ zaiTokenStore: NoopZaiTokenStore(),
177
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
178
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
179
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
180
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
181
+ groqTokenStore: NoopGroqTokenStore())
148
182
  let store = UsageStore(fetcher: UsageFetcher(environment: [:]), settings: settings)
149
183
 
150
184
  let expectedFields: [UsageProvider: Set<String>] = [
@@ -168,7 +202,7 @@ struct ProviderSettingsDescriptorTests {
168
202
  }
169
203
 
170
204
  @Test
171
- func allProvidersHaveAppSideImplementation() {
205
+ func `all providers have app side implementation`() {
172
206
  for provider in UsageProvider.allCases {
173
207
  #expect(ProviderCatalog.implementation(for: provider) != nil)
174
208
  }
@@ -3,41 +3,40 @@ import Testing
3
3
  @testable import Runic
4
4
 
5
5
  @MainActor
6
- @Suite
7
6
  struct ProviderToggleStoreTests {
8
7
  @Test
9
- func defaultsMatchMetadata() {
10
- let defaults = UserDefaults(suiteName: "ProviderToggleStoreTests-defaults")!
8
+ func `defaults match metadata`() throws {
9
+ let defaults = try #require(UserDefaults(suiteName: "ProviderToggleStoreTests-defaults"))
11
10
  defaults.removePersistentDomain(forName: "ProviderToggleStoreTests-defaults")
12
11
  let store = ProviderToggleStore(userDefaults: defaults)
13
12
  let registry = ProviderRegistry.shared
14
- let codexMeta = registry.metadata[.codex]!
15
- let claudeMeta = registry.metadata[.claude]!
13
+ let codexMeta = try #require(registry.metadata[.codex])
14
+ let claudeMeta = try #require(registry.metadata[.claude])
16
15
 
17
16
  #expect(store.isEnabled(metadata: codexMeta))
18
17
  #expect(!store.isEnabled(metadata: claudeMeta))
19
18
  }
20
19
 
21
20
  @Test
22
- func persistsChanges() {
21
+ func `persists changes`() throws {
23
22
  let suite = "ProviderToggleStoreTests-persist"
24
- let defaultsA = UserDefaults(suiteName: suite)!
23
+ let defaultsA = try #require(UserDefaults(suiteName: suite))
25
24
  defaultsA.removePersistentDomain(forName: suite)
26
25
  let storeA = ProviderToggleStore(userDefaults: defaultsA)
27
26
  let registry = ProviderRegistry.shared
28
- let claudeMeta = registry.metadata[.claude]!
27
+ let claudeMeta = try #require(registry.metadata[.claude])
29
28
 
30
29
  storeA.setEnabled(true, metadata: claudeMeta)
31
30
 
32
- let defaultsB = UserDefaults(suiteName: suite)!
31
+ let defaultsB = try #require(UserDefaults(suiteName: suite))
33
32
  let storeB = ProviderToggleStore(userDefaults: defaultsB)
34
33
  #expect(storeB.isEnabled(metadata: claudeMeta))
35
34
  }
36
35
 
37
36
  @Test
38
- func purgesLegacyKeys() {
37
+ func `purges legacy keys`() throws {
39
38
  let suite = "ProviderToggleStoreTests-purge"
40
- let defaults = UserDefaults(suiteName: suite)!
39
+ let defaults = try #require(UserDefaults(suiteName: suite))
41
40
  defaults.removePersistentDomain(forName: suite)
42
41
  defaults.set(false, forKey: "showCodexUsage")
43
42
  defaults.set(true, forKey: "showClaudeUsage")