@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
@@ -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
  ]
@@ -2,10 +2,9 @@ import Foundation
2
2
  import Testing
3
3
  @testable import RunicCore
4
4
 
5
- @Suite
6
5
  struct CopilotVSCodeTokenReaderTests {
7
6
  @Test
8
- func extractsTokenFromSessionArrayPayload() {
7
+ func `extracts token from session array payload`() {
9
8
  let token = "ghu_abcdefghijklmnopqrstuvwxyz1234567890"
10
9
  let json = """
11
10
  [
@@ -26,7 +25,7 @@ struct CopilotVSCodeTokenReaderTests {
26
25
  }
27
26
 
28
27
  @Test
29
- func extractsTokenFromNestedObjectPayload() {
28
+ func `extracts token from nested object payload`() {
30
29
  let token = "github_pat_abcdefghijklmnopqrstuvwxyz1234567890"
31
30
  let json = """
32
31
  {
@@ -45,7 +44,7 @@ struct CopilotVSCodeTokenReaderTests {
45
44
  }
46
45
 
47
46
  @Test
48
- func ignoresShortTokenValues() {
47
+ func `ignores short token values`() {
49
48
  let json = """
50
49
  {
51
50
  "sessions": [
@@ -61,7 +60,7 @@ struct CopilotVSCodeTokenReaderTests {
61
60
  }
62
61
 
63
62
  @Test
64
- func normalizesNodeBufferSecretWrapper() {
63
+ func `normalizes node buffer secret wrapper`() {
65
64
  let raw = Data("""
66
65
  {
67
66
  "type": "Buffer",
@@ -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 CostUsageDecodingTests {
8
6
  @Test
9
- func decodesDailyReportTypeFormat() throws {
7
+ func `decodes daily report type format`() throws {
10
8
  let json = """
11
9
  {
12
10
  "type": "daily",
@@ -45,7 +43,7 @@ struct CostUsageDecodingTests {
45
43
  }
46
44
 
47
45
  @Test
48
- func decodesDailyReportLegacyFormat() throws {
46
+ func `decodes daily report legacy format`() throws {
49
47
  let json = """
50
48
  {
51
49
  "daily": [
@@ -81,7 +79,7 @@ struct CostUsageDecodingTests {
81
79
  }
82
80
 
83
81
  @Test
84
- func decodesLegacyCacheTokenKeys() throws {
82
+ func `decodes legacy cache token keys`() throws {
85
83
  let json = """
86
84
  {
87
85
  "type": "daily",
@@ -109,7 +107,7 @@ struct CostUsageDecodingTests {
109
107
  }
110
108
 
111
109
  @Test
112
- func decodesDailyReportLegacyFormatWithModelMap() throws {
110
+ func `decodes daily report legacy format with model map`() throws {
113
111
  let json = """
114
112
  {
115
113
  "daily": [
@@ -143,7 +141,7 @@ struct CostUsageDecodingTests {
143
141
  }
144
142
 
145
143
  @Test
146
- func decodesDailyReportLegacyFormatWithModelMapSorted() throws {
144
+ func `decodes daily report legacy format with model map sorted`() throws {
147
145
  let json = """
148
146
  {
149
147
  "daily": [
@@ -166,7 +164,7 @@ struct CostUsageDecodingTests {
166
164
  }
167
165
 
168
166
  @Test
169
- func decodesDailyReportLegacyFormatWithEmptyModelMapAsNil() throws {
167
+ func `decodes daily report legacy format with empty model map as nil`() throws {
170
168
  let json = """
171
169
  {
172
170
  "daily": [
@@ -185,7 +183,7 @@ struct CostUsageDecodingTests {
185
183
  }
186
184
 
187
185
  @Test
188
- func decodesDailyReportLegacyFormatPrefersModelsUsedListOverModelsMap() throws {
186
+ func `decodes daily report legacy format prefers models used list over models map`() throws {
189
187
  let json = """
190
188
  {
191
189
  "daily": [
@@ -207,7 +205,7 @@ struct CostUsageDecodingTests {
207
205
  }
208
206
 
209
207
  @Test
210
- func decodesDailyReportLegacyFormatWithModelsList() throws {
208
+ func `decodes daily report legacy format with models list`() throws {
211
209
  let json = """
212
210
  {
213
211
  "daily": [
@@ -226,7 +224,7 @@ struct CostUsageDecodingTests {
226
224
  }
227
225
 
228
226
  @Test
229
- func decodesDailyReportLegacyFormatWithInvalidModelsField() throws {
227
+ func `decodes daily report legacy format with invalid models field`() throws {
230
228
  let json = """
231
229
  {
232
230
  "daily": [
@@ -245,7 +243,7 @@ struct CostUsageDecodingTests {
245
243
  }
246
244
 
247
245
  @Test
248
- func decodesMonthlyReportLegacyFormat() throws {
246
+ func `decodes monthly report legacy format`() throws {
249
247
  let json = """
250
248
  {
251
249
  "monthly": [
@@ -270,7 +268,7 @@ struct CostUsageDecodingTests {
270
268
  }
271
269
 
272
270
  @Test
273
- func selectsMostRecentSession() throws {
271
+ func `selects most recent session`() throws {
274
272
  let json = """
275
273
  {
276
274
  "type": "session",
@@ -306,7 +304,7 @@ struct CostUsageDecodingTests {
306
304
  }
307
305
 
308
306
  @Test
309
- func tokenSnapshotSelectsMostRecentDay() throws {
307
+ func `token snapshot selects most recent day`() throws {
310
308
  let json = """
311
309
  {
312
310
  "type": "daily",
@@ -339,7 +337,7 @@ struct CostUsageDecodingTests {
339
337
  }
340
338
 
341
339
  @Test
342
- func tokenSnapshotUsesSummaryTotalCostWhenAvailable() throws {
340
+ func `token snapshot uses summary total cost when available`() throws {
343
341
  let json = """
344
342
  {
345
343
  "type": "daily",
@@ -359,7 +357,7 @@ struct CostUsageDecodingTests {
359
357
  }
360
358
 
361
359
  @Test
362
- func tokenSnapshotFallsBackToSummedEntriesWhenSummaryMissing() throws {
360
+ func `token snapshot falls back to summed entries when summary missing`() throws {
363
361
  let json = """
364
362
  {
365
363
  "type": "daily",
@@ -376,7 +374,7 @@ struct CostUsageDecodingTests {
376
374
  }
377
375
 
378
376
  @Test
379
- func tokenSnapshotReturnsNilTotalWhenNoCostsPresent() throws {
377
+ func `token snapshot returns nil total when no costs present`() throws {
380
378
  let json = """
381
379
  {
382
380
  "type": "daily",
@@ -1,18 +1,16 @@
1
1
  import Testing
2
-
3
2
  @testable import RunicCore
4
3
 
5
- @Suite
6
4
  struct CostUsagePricingTests {
7
5
  @Test
8
- func normalizesCodexModelVariants() {
6
+ func `normalizes codex model variants`() {
9
7
  #expect(CostUsagePricing.normalizeCodexModel("openai/gpt-5-codex") == "gpt-5")
10
8
  #expect(CostUsagePricing.normalizeCodexModel("gpt-5.2-codex") == "gpt-5.2")
11
9
  #expect(CostUsagePricing.normalizeCodexModel("gpt-5.1-codex-max") == "gpt-5.1")
12
10
  }
13
11
 
14
12
  @Test
15
- func codexCostSupportsGpt51CodexMax() {
13
+ func `codex cost supports gpt51 codex max`() {
16
14
  let cost = CostUsagePricing.codexCostUSD(
17
15
  model: "gpt-5.1-codex-max",
18
16
  inputTokens: 100,
@@ -22,12 +20,12 @@ struct CostUsagePricingTests {
22
20
  }
23
21
 
24
22
  @Test
25
- func normalizesClaudeOpus41DatedVariants() {
23
+ func `normalizes claude opus41 dated variants`() {
26
24
  #expect(CostUsagePricing.normalizeClaudeModel("claude-opus-4-1-20250805") == "claude-opus-4-1")
27
25
  }
28
26
 
29
27
  @Test
30
- func claudeCostSupportsOpus41DatedVariant() {
28
+ func `claude cost supports opus41 dated variant`() {
31
29
  let cost = CostUsagePricing.claudeCostUSD(
32
30
  model: "claude-opus-4-1-20250805",
33
31
  inputTokens: 10,
@@ -38,7 +36,7 @@ struct CostUsagePricingTests {
38
36
  }
39
37
 
40
38
  @Test
41
- func claudeCostReturnsNilForUnknownModels() {
39
+ func `claude cost returns nil for unknown models`() {
42
40
  let cost = CostUsagePricing.claudeCostUSD(
43
41
  model: "glm-4.6",
44
42
  inputTokens: 100,