@sriinnu/runic 1.0.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 (348) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +1 -1
  3. package/README.md +47 -29
  4. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  5. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  6. package/RuniciOS/Views/ProviderListView.swift +10 -10
  7. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  8. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  9. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  10. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  11. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  12. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  13. package/Scripts/package_app.sh +2 -2
  14. package/Sources/Runic/About.swift +3 -3
  15. package/Sources/Runic/AppNotifications.swift +5 -3
  16. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
  17. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  18. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  19. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  20. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
  21. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  22. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  23. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  24. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  25. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  26. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  28. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
  29. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  30. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  31. package/Sources/Runic/Core/RunicColors.swift +6 -6
  32. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  33. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  34. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
  35. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
  36. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
  37. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
  38. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
  39. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  40. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
  41. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
  42. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
  43. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
  44. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
  45. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
  46. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
  47. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
  48. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
  49. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
  50. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  51. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
  52. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
  53. package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
  54. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
  55. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  56. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  57. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  58. package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
  59. package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
  60. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
  61. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  62. package/Sources/Runic/ProviderRegistry.swift +1 -1
  63. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  81. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  83. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  84. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  85. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  87. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  88. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  107. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  108. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  109. package/Sources/Runic/Resources/provider-context-windows.json +20 -0
  110. package/Sources/Runic/RunicApp.swift +46 -36
  111. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  112. package/Sources/Runic/UsagePaceText.swift +1 -1
  113. package/Sources/Runic/UsageProgressBar.swift +13 -9
  114. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  115. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  116. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  117. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  118. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  119. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  120. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  121. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  122. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  123. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
  124. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  125. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  126. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  127. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  128. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  129. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  130. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  131. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  132. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
  133. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  134. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  135. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  136. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  137. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  138. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  139. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
  140. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  141. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  142. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  143. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  144. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  145. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  146. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  147. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  148. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  149. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  150. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  151. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  152. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  153. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  154. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
  155. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  156. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  157. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  158. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  159. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  160. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  161. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  162. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  163. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  164. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  165. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  166. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  167. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  168. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  169. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  170. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  171. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  172. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  173. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  174. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  175. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  176. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  177. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
  178. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  179. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  180. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  181. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
  182. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  183. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  184. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  185. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  186. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  187. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  188. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  189. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  190. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  191. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  192. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  193. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  194. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
  195. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  196. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  197. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  198. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  199. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  200. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  201. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  202. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  204. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
  205. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
  206. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  207. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  208. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  209. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  210. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  211. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  212. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  213. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  214. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  215. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  216. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
  217. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  218. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  219. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  220. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  221. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
  222. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  225. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
  226. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  227. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  228. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  229. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  230. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  231. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
  232. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  233. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  234. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  235. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  236. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
  237. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  240. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  241. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  242. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
  243. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  244. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  245. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  246. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  247. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  248. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  249. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  250. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  251. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  252. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  253. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
  254. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  255. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  256. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  257. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  258. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  259. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  260. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  261. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  262. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  263. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  264. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  265. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
  266. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
  267. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
  268. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  269. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  270. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  271. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  272. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  273. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  274. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  275. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  276. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  277. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  278. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  279. package/Tests/RunicTests/CLICostTests.swift +6 -7
  280. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  281. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  282. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  283. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  284. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  285. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  286. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  287. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  288. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  289. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  290. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  291. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  292. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  293. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  294. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  295. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  296. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  297. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  298. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  299. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  300. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  301. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  302. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  303. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  304. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  305. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  306. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  307. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  308. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  309. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  310. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  311. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  312. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  313. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  314. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  315. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  316. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  317. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  318. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  319. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  320. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  321. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  322. package/Tests/RunicTests/RunicTests.swift +6 -7
  323. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  324. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  325. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  326. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  327. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  328. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  329. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  330. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  331. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  332. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  333. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  334. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  335. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  336. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  337. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  338. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  339. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  340. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  341. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  342. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  343. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  344. package/appcast.xml +36 -37
  345. package/package.json +1 -1
  346. package/runic.png +0 -0
  347. package/runic.svg +15 -0
  348. package/version.env +2 -2
@@ -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)
@@ -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)