@sriinnu/runic 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +8 -1
  3. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  4. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  5. package/RuniciOS/Views/ProviderListView.swift +10 -10
  6. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  7. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  8. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  9. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  10. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  11. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  12. package/Sources/Runic/About.swift +3 -3
  13. package/Sources/Runic/AppNotifications.swift +5 -3
  14. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
  15. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  16. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  17. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  18. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
  19. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  20. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  21. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  22. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  23. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  24. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  25. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  26. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  28. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  29. package/Sources/Runic/Core/RunicColors.swift +6 -6
  30. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  31. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  32. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
  33. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
  34. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
  35. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
  36. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
  37. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  38. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
  39. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
  40. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
  41. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
  42. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
  43. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
  44. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
  45. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
  46. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
  47. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
  48. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  49. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
  50. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
  51. package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
  52. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
  53. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  54. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  55. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  56. package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
  57. package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
  58. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
  59. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  60. package/Sources/Runic/ProviderRegistry.swift +1 -1
  61. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  62. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  63. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  81. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  83. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  84. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  85. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  87. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  88. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  107. package/Sources/Runic/RunicApp.swift +46 -36
  108. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  109. package/Sources/Runic/UsagePaceText.swift +1 -1
  110. package/Sources/Runic/UsageProgressBar.swift +13 -9
  111. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  112. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  113. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  114. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  115. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  116. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  117. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  118. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  119. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  120. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
  121. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  122. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  123. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  124. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  125. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  126. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  127. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  128. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  129. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
  130. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  131. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  132. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  133. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  134. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  135. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  136. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
  137. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  138. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  139. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  140. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  141. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  142. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  143. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  144. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  145. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  146. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  147. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  148. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  149. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  150. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  151. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
  152. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  153. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  154. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  155. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  156. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  157. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  158. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  159. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  160. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  161. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  162. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  163. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  164. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  165. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  166. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  167. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  168. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  169. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  170. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  171. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  172. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  173. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  174. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
  175. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  176. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  177. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  178. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  179. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  180. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  181. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  182. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  183. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  184. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  185. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  186. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  187. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  188. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  189. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  190. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
  191. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  192. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  193. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  194. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  195. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  196. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  197. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  198. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  199. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  200. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
  201. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  202. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  204. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  205. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  206. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  207. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  208. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  209. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  210. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  211. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  212. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  213. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  214. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  215. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
  216. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  217. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  218. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  219. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
  220. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  221. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  222. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  225. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
  226. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  227. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  228. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  229. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  230. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
  231. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  232. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  233. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  234. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  235. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  236. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
  237. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  240. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  241. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  242. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  243. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  244. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  245. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  246. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  247. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
  248. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  249. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  250. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  251. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  252. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  253. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  254. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  255. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  256. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  257. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  258. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  259. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
  260. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
  261. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
  262. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  263. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  264. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  265. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  266. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  267. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  268. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  269. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  270. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  271. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  272. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  273. package/Tests/RunicTests/CLICostTests.swift +6 -7
  274. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  275. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  276. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  277. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  278. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  279. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  280. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  281. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  282. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  283. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  284. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  285. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  286. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  287. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  288. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  289. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  290. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  291. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  292. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  293. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  294. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  295. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  296. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  297. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  298. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  299. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  300. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  301. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  302. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  303. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  304. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  305. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  306. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  307. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  308. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  309. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  310. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  311. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  312. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  313. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  314. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  315. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  316. package/Tests/RunicTests/RunicTests.swift +6 -7
  317. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  318. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  319. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  320. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  321. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  322. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  323. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  324. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  325. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  326. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  327. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  328. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  329. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  330. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  331. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  332. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  333. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  334. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  335. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  336. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  337. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  338. package/appcast.xml +36 -76
  339. package/package.json +1 -1
  340. package/runic.png +0 -0
  341. package/runic.svg +15 -0
  342. package/version.env +2 -2
@@ -1,13 +1,13 @@
1
1
  import Foundation
2
2
 
3
3
  enum CostUsagePricing {
4
- struct CodexPricing: Sendable {
4
+ struct CodexPricing {
5
5
  let inputCostPerToken: Double
6
6
  let outputCostPerToken: Double
7
7
  let cacheReadInputCostPerToken: Double
8
8
  }
9
9
 
10
- struct ClaudePricing: Sendable {
10
+ struct ClaudePricing {
11
11
  let inputCostPerToken: Double
12
12
  let outputCostPerToken: Double
13
13
  let cacheCreationInputCostPerToken: Double
@@ -1,12 +1,12 @@
1
1
  import Foundation
2
2
 
3
3
  enum CostUsageScanner {
4
- struct Options: Sendable {
4
+ struct Options {
5
5
  var codexSessionsRoot: URL?
6
6
  var claudeProjectsRoots: [URL]?
7
7
  var cacheRoot: URL?
8
8
  var refreshMinIntervalSeconds: TimeInterval = 60
9
- // Force a full rescan, ignoring per-file cache and incremental offsets.
9
+ /// Force a full rescan, ignoring per-file cache and incremental offsets.
10
10
  var forceRescan: Bool = false
11
11
 
12
12
  init(
@@ -22,14 +22,14 @@ enum CostUsageScanner {
22
22
  }
23
23
  }
24
24
 
25
- struct CodexParseResult: Sendable {
25
+ struct CodexParseResult {
26
26
  let days: [String: [String: [Int]]]
27
27
  let parsedBytes: Int64
28
28
  let lastModel: String?
29
29
  let lastTotals: CostUsageCodexTotals?
30
30
  }
31
31
 
32
- struct ClaudeParseResult: Sendable {
32
+ struct ClaudeParseResult {
33
33
  let days: [String: [String: [Int]]]
34
34
  let parsedBytes: Int64
35
35
  }
@@ -101,7 +101,7 @@ enum CostUsageScanner {
101
101
 
102
102
  // MARK: - Day keys
103
103
 
104
- struct CostUsageDayRange: Sendable {
104
+ struct CostUsageDayRange {
105
105
  let sinceKey: String
106
106
  let untilKey: String
107
107
  let scanSinceKey: String
@@ -4,10 +4,9 @@ import Testing
4
4
  @testable import Runic
5
5
 
6
6
  @MainActor
7
- @Suite
8
7
  struct AppDelegateTests {
9
8
  @Test
10
- func buildsStatusControllerAfterLaunch() {
9
+ func `builds status controller after launch`() {
11
10
  let appDelegate = AppDelegate()
12
11
  var factoryCalls = 0
13
12
 
@@ -18,7 +17,13 @@ struct AppDelegateTests {
18
17
  }
19
18
  defer { StatusItemController.factory = StatusItemController.defaultFactory }
20
19
 
21
- let settings = SettingsStore(zaiTokenStore: NoopZaiTokenStore(), minimaxTokenStore: NoopMiniMaxTokenStore(), minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(), minimaxGroupIDStore: NoopMiniMaxGroupIDStore(), openRouterTokenStore: NoopOpenRouterTokenStore(), groqTokenStore: NoopGroqTokenStore())
20
+ let settings = SettingsStore(
21
+ zaiTokenStore: NoopZaiTokenStore(),
22
+ minimaxTokenStore: NoopMiniMaxTokenStore(),
23
+ minimaxCookieHeaderStore: NoopMiniMaxCookieHeaderStore(),
24
+ minimaxGroupIDStore: NoopMiniMaxGroupIDStore(),
25
+ openRouterTokenStore: NoopOpenRouterTokenStore(),
26
+ groqTokenStore: NoopGroqTokenStore())
22
27
  let fetcher = UsageFetcher()
23
28
  let store = UsageStore(fetcher: fetcher, settings: settings)
24
29
  let account = fetcher.loadAccountInfo()
@@ -2,18 +2,17 @@ import RunicCore
2
2
  import Silo
3
3
  import Testing
4
4
 
5
- @Suite
6
5
  struct BrowserCookieOrderStatusStringTests {
7
6
  #if os(macOS)
8
7
  @Test
9
- func cursorNoSessionIncludesBrowserLoginHint() {
8
+ func `cursor no session includes browser login hint`() {
10
9
  let order = ProviderDefaults.metadata[.cursor]?.browserCookieOrder ?? Browser.defaultImportOrder
11
10
  let message = CursorStatusProbeError.noSessionCookie.errorDescription ?? ""
12
11
  #expect(message.contains(order.loginHint))
13
12
  }
14
13
 
15
14
  @Test
16
- func factoryNoSessionIncludesBrowserLoginHint() {
15
+ func `factory no session includes browser login hint`() {
17
16
  let order = ProviderDefaults.metadata[.factory]?.browserCookieOrder ?? Browser.defaultImportOrder
18
17
  let message = FactoryStatusProbeError.noSessionCookie.errorDescription ?? ""
19
18
  #expect(message.contains(order.loginHint))
@@ -1,10 +1,9 @@
1
1
  import Helix
2
2
  import Testing
3
3
 
4
- @Suite
5
4
  struct CLIArgumentParsingTests {
6
5
  @Test
7
- func usageCommandParsesProviderAndJsonFlags() throws {
6
+ func `usage command parses provider and json flags`() throws {
8
7
  let signature = CommandSignature(
9
8
  options: [
10
9
  OptionDefinition(label: "provider", names: [.long("provider")], help: nil),
@@ -25,7 +24,7 @@ struct CLIArgumentParsingTests {
25
24
  }
26
25
 
27
26
  @Test
28
- func costCommandParsesRefreshFlag() throws {
27
+ func `cost command parses refresh flag`() throws {
29
28
  let signature = CommandSignature(
30
29
  options: [
31
30
  OptionDefinition(label: "provider", names: [.long("provider")], help: nil),
@@ -41,7 +40,7 @@ struct CLIArgumentParsingTests {
41
40
  }
42
41
 
43
42
  @Test
44
- func insightsCommandParsesViewAndBudgetFlags() throws {
43
+ func `insights command parses view and budget flags`() throws {
45
44
  let signature = CommandSignature(
46
45
  options: [
47
46
  OptionDefinition(label: "view", names: [.long("view")], help: nil),
@@ -2,26 +2,25 @@ import Foundation
2
2
  import RunicCore
3
3
  import Testing
4
4
 
5
- @Suite
6
5
  struct CLICostTests {
7
6
  @Test
8
- func costSnapshotStoresSessionAndMonthFields() {
7
+ func `cost snapshot stores session and month fields`() {
9
8
  let snapshot = CostUsageTokenSnapshot(
10
- sessionTokens: 1_200,
9
+ sessionTokens: 1200,
11
10
  sessionCostUSD: 1.25,
12
- last30DaysTokens: 9_000,
11
+ last30DaysTokens: 9000,
13
12
  last30DaysCostUSD: 9.99,
14
13
  daily: [],
15
14
  updatedAt: Date(timeIntervalSince1970: 1_700_000_000))
16
15
 
17
- #expect(snapshot.sessionTokens == 1_200)
16
+ #expect(snapshot.sessionTokens == 1200)
18
17
  #expect(snapshot.sessionCostUSD == 1.25)
19
- #expect(snapshot.last30DaysTokens == 9_000)
18
+ #expect(snapshot.last30DaysTokens == 9000)
20
19
  #expect(snapshot.last30DaysCostUSD == 9.99)
21
20
  }
22
21
 
23
22
  @Test
24
- func dailyReportDecodesLegacyCacheTokenKeys() throws {
23
+ func `daily report decodes legacy cache token keys`() throws {
25
24
  let json = """
26
25
  {
27
26
  "daily": [
@@ -1,10 +1,9 @@
1
1
  import RunicCore
2
2
  import Testing
3
3
 
4
- @Suite
5
4
  struct CLIProviderSelectionTests {
6
5
  @Test
7
- func providerRegistryIncludesCoreProviders() {
6
+ func `provider registry includes core providers`() {
8
7
  let providers = Set(ProviderDescriptorRegistry.all.map(\.id))
9
8
 
10
9
  #expect(providers.contains(.codex))
@@ -15,7 +14,7 @@ struct CLIProviderSelectionTests {
15
14
  }
16
15
 
17
16
  @Test
18
- func providerEnumResolvesInsightsSupportedValues() {
17
+ func `provider enum resolves insights supported values`() {
19
18
  #expect(UsageProvider(rawValue: "codex") == .codex)
20
19
  #expect(UsageProvider(rawValue: "claude") == .claude)
21
20
  }
@@ -3,10 +3,9 @@ import RunicCore
3
3
  import Testing
4
4
  @testable import RunicCLI
5
5
 
6
- @Suite
7
6
  struct CLISnapshotTests {
8
7
  @Test
9
- func rendersCodexTextSnapshot() {
8
+ func `renders codex text snapshot`() {
10
9
  let identity = ProviderIdentitySnapshot(
11
10
  providerID: .codex,
12
11
  accountEmail: "user@example.com",
@@ -34,7 +33,7 @@ struct CLISnapshotTests {
34
33
  }
35
34
 
36
35
  @Test
37
- func rendersClaudeSnapshotWithoutWeeklyWhenMissing() {
36
+ func `renders claude snapshot without weekly when missing`() {
38
37
  let snapshot = UsageSnapshot(
39
38
  primary: .init(usedPercent: 2, windowMinutes: nil, resetsAt: nil, resetDescription: "3pm (Europe/Vienna)"),
40
39
  secondary: nil,
@@ -52,7 +51,7 @@ struct CLISnapshotTests {
52
51
  }
53
52
 
54
53
  @Test
55
- func appliesAnsiColorsWhenEnabled() {
54
+ func `applies ansi colors when enabled`() {
56
55
  let snapshot = UsageSnapshot(
57
56
  primary: .init(usedPercent: 95, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
58
57
  secondary: .init(usedPercent: 80, windowMinutes: 10080, resetsAt: nil, resetDescription: nil),
@@ -2,7 +2,6 @@ import Testing
2
2
  @testable import RunicCLI
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct CLIWebFallbackTests {
7
6
  private func makeContext(sourceMode: ProviderSourceMode = .auto) -> ProviderFetchContext {
8
7
  ProviderFetchContext(
@@ -19,7 +18,7 @@ struct CLIWebFallbackTests {
19
18
  }
20
19
 
21
20
  @Test
22
- func codexFallsBackWhenCookiesMissing() {
21
+ func `codex falls back when cookies missing`() {
23
22
  let context = self.makeContext()
24
23
  let strategy = CodexWebDashboardStrategy()
25
24
  #expect(strategy.shouldFallback(
@@ -40,7 +39,7 @@ struct CLIWebFallbackTests {
40
39
  }
41
40
 
42
41
  @Test
43
- func codexDoesNotFallbackForDashboardDataErrors() {
42
+ func `codex does not fallback for dashboard data errors`() {
44
43
  let context = self.makeContext()
45
44
  let strategy = CodexWebDashboardStrategy()
46
45
  #expect(!strategy.shouldFallback(
@@ -49,7 +48,7 @@ struct CLIWebFallbackTests {
49
48
  }
50
49
 
51
50
  @Test
52
- func claudeFallsBackWhenNoSessionKey() {
51
+ func `claude falls back when no session key`() {
53
52
  let context = self.makeContext()
54
53
  let strategy = ClaudeWebFetchStrategy()
55
54
  #expect(strategy.shouldFallback(on: ClaudeWebAPIFetcher.FetchError.noSessionKeyFound, context: context))
@@ -1,11 +1,10 @@
1
- import RunicCore
2
1
  import Foundation
2
+ import RunicCore
3
3
  import Testing
4
4
 
5
- @Suite
6
5
  struct ClaudeOAuthTests {
7
6
  @Test
8
- func parsesOAuthCredentials() throws {
7
+ func `parses O auth credentials`() throws {
9
8
  let json = """
10
9
  {
11
10
  "claudeAiOauth": {
@@ -26,7 +25,7 @@ struct ClaudeOAuthTests {
26
25
  }
27
26
 
28
27
  @Test
29
- func missingAccessTokenThrows() {
28
+ func `missing access token throws`() {
30
29
  let json = """
31
30
  {
32
31
  "claudeAiOauth": {
@@ -42,7 +41,7 @@ struct ClaudeOAuthTests {
42
41
  }
43
42
 
44
43
  @Test
45
- func missingOAuthBlockThrows() {
44
+ func `missing O auth block throws`() {
46
45
  let json = """
47
46
  { "other": { "accessToken": "nope" } }
48
47
  """
@@ -52,7 +51,7 @@ struct ClaudeOAuthTests {
52
51
  }
53
52
 
54
53
  @Test
55
- func treatsMissingExpiryAsExpired() {
54
+ func `treats missing expiry as expired`() {
56
55
  let creds = ClaudeOAuthCredentials(
57
56
  accessToken: "token",
58
57
  refreshToken: nil,
@@ -63,7 +62,7 @@ struct ClaudeOAuthTests {
63
62
  }
64
63
 
65
64
  @Test
66
- func mapsOAuthUsageToSnapshot() throws {
65
+ func `maps O auth usage to snapshot`() throws {
67
66
  let json = """
68
67
  {
69
68
  "five_hour": { "utilization": 12.5, "resets_at": "2025-12-25T12:00:00.000Z" },
@@ -83,7 +82,7 @@ struct ClaudeOAuthTests {
83
82
  }
84
83
 
85
84
  @Test
86
- func mapsOAuthExtraUsage() throws {
85
+ func `maps O auth extra usage`() throws {
87
86
  let json = """
88
87
  {
89
88
  "five_hour": { "utilization": 1, "resets_at": "2025-12-25T12:00:00.000Z" },
@@ -101,7 +100,7 @@ struct ClaudeOAuthTests {
101
100
  }
102
101
 
103
102
  @Test
104
- func prefersOpusWhenSonnetMissing() throws {
103
+ func `prefers opus when sonnet missing`() throws {
105
104
  let json = """
106
105
  {
107
106
  "five_hour": { "utilization": 10, "resets_at": "2025-12-25T12:00:00.000Z" },
@@ -113,7 +112,7 @@ struct ClaudeOAuthTests {
113
112
  }
114
113
 
115
114
  @Test
116
- func skipsExtraUsageWhenDisabled() throws {
115
+ func `skips extra usage when disabled`() throws {
117
116
  let json = """
118
117
  {
119
118
  "five_hour": { "utilization": 1, "resets_at": "2025-12-25T12:00:00.000Z" },
@@ -131,7 +130,7 @@ struct ClaudeOAuthTests {
131
130
  // MARK: - Scope-based strategy resolution
132
131
 
133
132
  @Test
134
- func oauthRequiresUserProfileScope() {
133
+ func `oauth requires user profile scope`() {
135
134
  // With user:profile scope, OAuth should be selected.
136
135
  let withProfile = ClaudeProviderDescriptor.resolveUsageStrategy(
137
136
  debugMenuEnabled: false,
@@ -152,7 +151,7 @@ struct ClaudeOAuthTests {
152
151
  }
153
152
 
154
153
  @Test
155
- func fallsBackToCLIWhenNoWebOrOAuth() {
154
+ func `falls back to CLI when no web or O auth`() {
156
155
  let strategy = ClaudeProviderDescriptor.resolveUsageStrategy(
157
156
  debugMenuEnabled: false,
158
157
  selectedDataSource: .oauth,
@@ -1,10 +1,9 @@
1
1
  import Testing
2
2
  @testable import Runic
3
3
 
4
- @Suite
5
4
  struct ClaudeResilienceTests {
6
5
  @Test
7
- func suppressesSingleFlakeWhenPriorDataExists() {
6
+ func `suppresses single flake when prior data exists`() {
8
7
  var gate = ConsecutiveFailureGate()
9
8
  let firstFailure = gate.shouldSurfaceError(onFailureWithPriorData: true)
10
9
  let secondFailure = gate.shouldSurfaceError(onFailureWithPriorData: true)
@@ -13,14 +12,14 @@ struct ClaudeResilienceTests {
13
12
  }
14
13
 
15
14
  @Test
16
- func surfacesFailureWithoutPriorData() {
15
+ func `surfaces failure without prior data`() {
17
16
  var gate = ConsecutiveFailureGate()
18
17
  let shouldSurface = gate.shouldSurfaceError(onFailureWithPriorData: false)
19
18
  #expect(shouldSurface)
20
19
  }
21
20
 
22
21
  @Test
23
- func resetsAfterSuccess() {
22
+ func `resets after success`() {
24
23
  var gate = ConsecutiveFailureGate()
25
24
  _ = gate.shouldSurfaceError(onFailureWithPriorData: true)
26
25
  gate.recordSuccess()
@@ -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 ClaudeUsageTests {
8
7
  @Test
9
- func parsesUsageJSONWithSonnetLimit() {
8
+ func `parses usage JSON with sonnet limit`() {
10
9
  let json = """
11
10
  {
12
11
  "ok": true,
@@ -24,7 +23,7 @@ struct ClaudeUsageTests {
24
23
  }
25
24
 
26
25
  @Test
27
- func parsesUsageJSONWhenWeeklyMissing() {
26
+ func `parses usage JSON when weekly missing`() {
28
27
  let json = """
29
28
  {
30
29
  "ok": true,
@@ -39,7 +38,7 @@ struct ClaudeUsageTests {
39
38
  }
40
39
 
41
40
  @Test
42
- func parsesLegacyOpusAndAccount() {
41
+ func `parses legacy opus and account`() {
43
42
  let json = """
44
43
  {
45
44
  "ok": true,
@@ -59,7 +58,7 @@ struct ClaudeUsageTests {
59
58
  }
60
59
 
61
60
  @Test
62
- func parsesUsageJSONWhenOnlySonnetLimitIsPresent() {
61
+ func `parses usage JSON when only sonnet limit is present`() {
63
62
  let json = """
64
63
  {
65
64
  "ok": true,
@@ -76,7 +75,7 @@ struct ClaudeUsageTests {
76
75
  }
77
76
 
78
77
  @Test
79
- func trimsAccountFields() throws {
78
+ func `trims account fields`() throws {
80
79
  let cases: [[String: String?]] = [
81
80
  ["email": " sriinnu@icloud.com ", "org": " Org "],
82
81
  ["email": "", "org": " Claude Max Account "],
@@ -93,11 +92,11 @@ struct ClaudeUsageTests {
93
92
  if let org = entry["org"] { payload["account_org"] = org }
94
93
  let data = try JSONSerialization.data(withJSONObject: payload)
95
94
  let snap = ClaudeUsageFetcher.parse(json: data)
96
- let emailRaw: String? = entry["email"] ?? Optional<String>.none
95
+ let emailRaw: String? = entry["email"] ?? String?.none
97
96
  let expectedEmail = emailRaw?.trimmingCharacters(in: .whitespacesAndNewlines)
98
97
  let normalizedEmail = (expectedEmail?.isEmpty ?? true) ? nil : expectedEmail
99
98
  #expect(snap?.accountEmail == normalizedEmail)
100
- let orgRaw: String? = entry["org"] ?? Optional<String>.none
99
+ let orgRaw: String? = entry["org"] ?? String?.none
101
100
  let expectedOrg = orgRaw?.trimmingCharacters(in: .whitespacesAndNewlines)
102
101
  let normalizedOrg = (expectedOrg?.isEmpty ?? true) ? nil : expectedOrg
103
102
  #expect(snap?.accountOrganization == normalizedOrg)
@@ -105,7 +104,7 @@ struct ClaudeUsageTests {
105
104
  }
106
105
 
107
106
  @Test
108
- func liveClaudeFetchPTY() async throws {
107
+ func `live claude fetch PTY`() async throws {
109
108
  guard ProcessInfo.processInfo.environment["LIVE_CLAUDE_FETCH"] == "1" else {
110
109
  return
111
110
  }
@@ -162,7 +161,7 @@ struct ClaudeUsageTests {
162
161
  // MARK: - Web API tests
163
162
 
164
163
  @Test
165
- func liveClaudeFetchWebAPI() async throws {
164
+ func `live claude fetch web API`() async throws {
166
165
  // Set LIVE_CLAUDE_WEB_FETCH=1 to run this test with real browser cookies
167
166
  guard ProcessInfo.processInfo.environment["LIVE_CLAUDE_WEB_FETCH"] == "1" else {
168
167
  return
@@ -183,7 +182,7 @@ struct ClaudeUsageTests {
183
182
  }
184
183
 
185
184
  @Test
186
- func claudeWebAPIHasSessionKeyCheck() {
185
+ func `claude web API has session key check`() {
187
186
  // Quick check that hasSessionKey returns a boolean (doesn't crash)
188
187
  let hasKey = ClaudeWebAPIFetcher.hasSessionKey()
189
188
  // We can't assert the value since it depends on the test environment
@@ -191,7 +190,7 @@ struct ClaudeUsageTests {
191
190
  }
192
191
 
193
192
  @Test
194
- func parsesClaudeWebAPIUsageResponse() throws {
193
+ func `parses claude web API usage response`() throws {
195
194
  let json = """
196
195
  {
197
196
  "five_hour": { "utilization": 9, "resets_at": "2025-12-23T16:00:00.000Z" },
@@ -209,7 +208,7 @@ struct ClaudeUsageTests {
209
208
  }
210
209
 
211
210
  @Test
212
- func parsesClaudeWebAPIUsageResponseWhenWeeklyMissing() throws {
211
+ func `parses claude web API usage response when weekly missing`() throws {
213
212
  let json = """
214
213
  {
215
214
  "five_hour": { "utilization": 9, "resets_at": "2025-12-23T16:00:00.000Z" }
@@ -222,7 +221,7 @@ struct ClaudeUsageTests {
222
221
  }
223
222
 
224
223
  @Test
225
- func parsesClaudeWebAPIOverageSpendLimit() {
224
+ func `parses claude web API overage spend limit`() {
226
225
  let json = """
227
226
  {
228
227
  "monthly_credit_limit": 2000,
@@ -241,7 +240,7 @@ struct ClaudeUsageTests {
241
240
  }
242
241
 
243
242
  @Test
244
- func parsesClaudeWebAPIOverageSpendLimitCents() {
243
+ func `parses claude web API overage spend limit cents`() {
245
244
  let json = """
246
245
  {
247
246
  "monthly_credit_limit": 12345,
@@ -258,7 +257,7 @@ struct ClaudeUsageTests {
258
257
  }
259
258
 
260
259
  @Test
261
- func parsesClaudeWebAPIOrganizationsResponse() throws {
260
+ func `parses claude web API organizations response`() throws {
262
261
  let json = """
263
262
  [
264
263
  { "uuid": "org-123", "name": "Example Org", "capabilities": [] }
@@ -271,7 +270,7 @@ struct ClaudeUsageTests {
271
270
  }
272
271
 
273
272
  @Test
274
- func parsesClaudeWebAPIAccountInfo() {
273
+ func `parses claude web API account info`() {
275
274
  let json = """
276
275
  {
277
276
  "email_address": "sriinnu@icloud.com",
@@ -294,7 +293,7 @@ struct ClaudeUsageTests {
294
293
  }
295
294
 
296
295
  @Test
297
- func parsesClaudeWebAPIAccountInfoSelectsMatchingOrg() {
296
+ func `parses claude web API account info selects matching org`() {
298
297
  let json = """
299
298
  {
300
299
  "email_address": "sriinnu@icloud.com",
@@ -324,7 +323,7 @@ struct ClaudeUsageTests {
324
323
  }
325
324
 
326
325
  @Test
327
- func parsesClaudeWebAPIAccountInfoFallsBackToFirstMembership() {
326
+ func `parses claude web API account info falls back to first membership`() {
328
327
  let json = """
329
328
  {
330
329
  "email_address": "sriinnu@icloud.com",
@@ -354,7 +353,7 @@ struct ClaudeUsageTests {
354
353
  }
355
354
 
356
355
  @Test
357
- func claudeUsageFetcherInitWithDataSources() {
356
+ func `claude usage fetcher init with data sources`() {
358
357
  // Verify we can create fetchers with both configurations
359
358
  let defaultFetcher = ClaudeUsageFetcher()
360
359
  let webFetcher = ClaudeUsageFetcher(dataSource: .web)
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct CodexOAuthTests {
7
6
  @Test
8
- func parsesOAuthCredentials() throws {
7
+ func `parses O auth credentials`() throws {
9
8
  let json = """
10
9
  {
11
10
  "OPENAI_API_KEY": null,
@@ -27,7 +26,7 @@ struct CodexOAuthTests {
27
26
  }
28
27
 
29
28
  @Test
30
- func parsesAPIKeyCredentials() throws {
29
+ func `parses API key credentials`() throws {
31
30
  let json = """
32
31
  {
33
32
  "OPENAI_API_KEY": "sk-test"
@@ -41,7 +40,7 @@ struct CodexOAuthTests {
41
40
  }
42
41
 
43
42
  @Test
44
- func decodesCreditsBalanceString() throws {
43
+ func `decodes credits balance string`() throws {
45
44
  let json = """
46
45
  {
47
46
  "plan_type": "pro",
@@ -67,7 +66,7 @@ struct CodexOAuthTests {
67
66
  }
68
67
 
69
68
  @Test
70
- func mapsUsageWindowsFromOAuth() throws {
69
+ func `maps usage windows from O auth`() throws {
71
70
  let json = """
72
71
  {
73
72
  "rate_limit": {
@@ -100,21 +99,21 @@ struct CodexOAuthTests {
100
99
  }
101
100
 
102
101
  @Test
103
- func resolvesChatGPTUsageURLFromConfig() {
102
+ func `resolves chat GPT usage URL from config`() {
104
103
  let config = "chatgpt_base_url = \"https://chatgpt.com/backend-api/\"\n"
105
104
  let url = CodexOAuthUsageFetcher._resolveUsageURLForTesting(configContents: config)
106
105
  #expect(url.absoluteString == "https://chatgpt.com/backend-api/wham/usage")
107
106
  }
108
107
 
109
108
  @Test
110
- func resolvesCodexUsageURLFromConfig() {
109
+ func `resolves codex usage URL from config`() {
111
110
  let config = "chatgpt_base_url = \"https://api.openai.com\"\n"
112
111
  let url = CodexOAuthUsageFetcher._resolveUsageURLForTesting(configContents: config)
113
112
  #expect(url.absoluteString == "https://api.openai.com/api/codex/usage")
114
113
  }
115
114
 
116
115
  @Test
117
- func normalizesChatGPTBaseURLWithoutBackendAPI() {
116
+ func `normalizes chat GPT base URL without backend API`() {
118
117
  let config = "chatgpt_base_url = \"https://chat.openai.com\"\n"
119
118
  let url = CodexOAuthUsageFetcher._resolveUsageURLForTesting(configContents: config)
120
119
  #expect(url.absoluteString == "https://chat.openai.com/backend-api/wham/usage")
@@ -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 CodexUsageLogSourceTests {
8
6
  @Test
9
- func codexUsageFallsBackToWorkspacePathFromTurnContext() async throws {
7
+ func `codex usage falls back to workspace path from turn context`() async throws {
10
8
  let fm = FileManager.default
11
9
  let root = fm.temporaryDirectory
12
10
  .appendingPathComponent("runic-codex-usage-tests", isDirectory: true)
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct CopilotTokenResolverTests {
7
6
  @Test
8
- func copilotTokenPrefersCopilotApiEnvironmentVariable() {
7
+ func `copilot token prefers copilot api environment variable`() {
9
8
  let env = [
10
9
  "COPILOT_API_TOKEN": "copilot-env-token",
11
10
  "GITHUB_TOKEN": "github-token",
@@ -18,7 +17,7 @@ struct CopilotTokenResolverTests {
18
17
  }
19
18
 
20
19
  @Test
21
- func copilotTokenFallsBackToGithubToken() {
20
+ func `copilot token falls back to github token`() {
22
21
  let env = [
23
22
  "GITHUB_TOKEN": "github-token",
24
23
  "GH_TOKEN": "gh-fallback-token",
@@ -29,7 +28,7 @@ struct CopilotTokenResolverTests {
29
28
  }
30
29
 
31
30
  @Test
32
- func copilotTokenFallsBackToGhToken() {
31
+ func `copilot token falls back to gh token`() {
33
32
  let env = [
34
33
  "GH_TOKEN": "gh-fallback-token",
35
34
  "GITHUB_TOKEN": "",
@@ -40,7 +39,7 @@ struct CopilotTokenResolverTests {
40
39
  }
41
40
 
42
41
  @Test
43
- func copilotTokenFallsBackToGithubCLIToken() throws {
42
+ func `copilot token falls back to github CLI token`() throws {
44
43
  let token = "gho_abcdefghijklmnopqrstuvwxyz1234"
45
44
  let tempDir = FileManager.default.temporaryDirectory
46
45
  .appendingPathComponent("runic-copilot-gh-cli-\(UUID().uuidString)", isDirectory: true)
@@ -65,7 +64,7 @@ struct CopilotTokenResolverTests {
65
64
  }
66
65
 
67
66
  @Test
68
- func copilotTokenStripsQuotedValues() {
67
+ func `copilot token strips quoted values`() {
69
68
  let env = [
70
69
  "COPILOT_API_TOKEN": "\"quoted-token\"",
71
70
  ]