@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
@@ -8,7 +8,7 @@ struct GroqModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct GroqUsageFetcher {
11
+ enum GroqUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.groq.com/openai/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> GroqModelsResponse {
@@ -53,7 +53,7 @@ extension GroqModelsResponse {
53
53
  }
54
54
  }
55
55
 
56
- enum GroqAPIError: LocalizedError, Sendable {
56
+ enum GroqAPIError: LocalizedError {
57
57
  case invalidResponse
58
58
  case httpError(statusCode: Int)
59
59
  case decodingError
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -71,7 +71,7 @@ struct KimiAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum KimiSettingsError: LocalizedError, Sendable {
74
+ enum KimiSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct KimiModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct KimiUsageFetcher {
11
+ enum KimiUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.moonshot.ai/v1/models")!
13
13
  private static let requestTimeout: TimeInterval = 20
14
14
 
@@ -59,7 +59,7 @@ extension KimiModelsResponse {
59
59
  }
60
60
  }
61
61
 
62
- enum KimiAPIError: LocalizedError, Sendable {
62
+ enum KimiAPIError: LocalizedError {
63
63
  case invalidResponse
64
64
  case httpError(statusCode: Int, body: String?)
65
65
 
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -37,7 +37,10 @@ public enum MiniMaxProviderDescriptor {
37
37
  noDataMessage: { "MiniMax cost summary is not supported." }),
38
38
  fetchPlan: ProviderFetchPlan(
39
39
  sourceModes: [.auto, .api, .web],
40
- pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [MiniMaxAPIFetchStrategy(), MiniMaxWebFetchStrategy()] })),
40
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [
41
+ MiniMaxAPIFetchStrategy(),
42
+ MiniMaxWebFetchStrategy(),
43
+ ] })),
41
44
  cli: ProviderCLIConfig(
42
45
  name: "minimax",
43
46
  aliases: ["minimax-api"],
@@ -49,7 +52,9 @@ struct MiniMaxWebFetchStrategy: ProviderFetchStrategy {
49
52
  let id: String = "minimax.web"
50
53
  let kind: ProviderFetchKind = .web
51
54
 
52
- func isAvailable(_: ProviderFetchContext) async -> Bool { true }
55
+ func isAvailable(_: ProviderFetchContext) async -> Bool {
56
+ true
57
+ }
53
58
 
54
59
  func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
55
60
  let fetcher = MiniMaxWebUsageFetcher()
@@ -78,9 +83,9 @@ struct MiniMaxAPIFetchStrategy: ProviderFetchStrategy {
78
83
  guard let tokenRes = ProviderTokenResolver.minimaxApiKeyResolution(environment: context.env) else {
79
84
  throw ProviderFetchError.missingCredentials
80
85
  }
81
-
86
+
82
87
  let snapshot = try await MiniMaxUsageFetcher.fetchUsage(apiKey: tokenRes.token)
83
-
88
+
84
89
  return self.makeResult(
85
90
  usage: snapshot.toUsageSnapshot(),
86
91
  sourceLabel: "API")
@@ -18,7 +18,7 @@ public struct MiniMaxModelRemain: Decodable {
18
18
  public let modelName: String
19
19
  public let currentIntervalTotalCount: Int
20
20
  public let currentIntervalUsageCount: Int
21
-
21
+
22
22
  enum CodingKeys: String, CodingKey {
23
23
  case modelName = "model_name"
24
24
  case currentIntervalTotalCount = "current_interval_total_count"
@@ -62,9 +62,9 @@ extension MiniMaxUsageSnapshot {
62
62
  // But let's assume standard "usage" field naming means USED.
63
63
  // If the user reports it shows 100% when new, we invert it.
64
64
  // For now, let's map it as Used/Total.
65
-
65
+
66
66
  let percent = self.total > 0 ? (Double(self.used) / Double(self.total)) * 100.0 : 0.0
67
-
67
+
68
68
  let primary = RateWindow(
69
69
  usedPercent: percent,
70
70
  windowMinutes: 24 * 60,
@@ -77,7 +77,7 @@ extension MiniMaxUsageSnapshot {
77
77
  accountEmail: nil,
78
78
  accountOrganization: nil,
79
79
  loginMethod: "api-key")
80
-
80
+
81
81
  return UsageSnapshot(
82
82
  primary: primary,
83
83
  secondary: nil,
@@ -119,21 +119,18 @@ public struct MiniMaxUsageFetcher: Sendable {
119
119
  throw MiniMaxUsageError.apiError("HTTP \(httpResponse.statusCode): \(errorMessage)")
120
120
  }
121
121
 
122
- // Log raw response for debugging
123
- if let jsonString = String(data: data, encoding: .utf8) {
124
- Self.log.debug("MiniMax API response: \(jsonString)")
125
- }
122
+ Self.log.debug("MiniMax API response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
126
123
 
127
124
  let decoder = JSONDecoder()
128
125
  do {
129
126
  let apiResponse = try decoder.decode(MiniMaxQuotaResponse.self, from: data)
130
127
 
131
128
  if apiResponse.baseResponse.statusCode != 0 {
132
- throw MiniMaxUsageError.apiError(apiResponse.baseResponse.statusMsg)
129
+ throw MiniMaxUsageError.apiError(apiResponse.baseResponse.statusMsg)
133
130
  }
134
131
 
135
132
  guard let firstModel = apiResponse.modelRemains?.first else {
136
- throw MiniMaxUsageError.parseFailed("No model quota found")
133
+ throw MiniMaxUsageError.parseFailed("No model quota found")
137
134
  }
138
135
 
139
136
  return MiniMaxUsageSnapshot(
@@ -141,7 +138,7 @@ public struct MiniMaxUsageFetcher: Sendable {
141
138
  used: firstModel.currentIntervalUsageCount,
142
139
  modelName: firstModel.modelName,
143
140
  updatedAt: Date())
144
-
141
+
145
142
  } catch {
146
143
  Self.log.error("MiniMax parsing error: \(error.localizedDescription)")
147
144
  throw MiniMaxUsageError.parseFailed(error.localizedDescription)
@@ -225,13 +225,13 @@ public struct MiniMaxWebUsageFetcher: Sendable {
225
225
 
226
226
  // MARK: - Parsing helpers
227
227
 
228
- struct MiniMaxManualInput: Sendable {
228
+ struct MiniMaxManualInput {
229
229
  let cookieHeader: String
230
230
  let accessToken: String?
231
231
  let groupID: String?
232
232
  }
233
233
 
234
- struct MiniMaxParsedUsage: Sendable {
234
+ struct MiniMaxParsedUsage {
235
235
  let usedPercent: Double
236
236
  let windowMinutes: Int?
237
237
  let resetsAt: Date?
@@ -408,9 +408,9 @@ enum MiniMaxWebParsing {
408
408
  if let remaining = self.captureGroups(
409
409
  in: html,
410
410
  pattern: "(?i)Available usage[^0-9]*([0-9]+(?:\\.[0-9]+)?)")?.first,
411
- let total = self.captureGroups(
412
- in: html,
413
- pattern: "(?i)Total usage[^0-9]*([0-9]+(?:\\.[0-9]+)?)")?.first
411
+ let total = self.captureGroups(
412
+ in: html,
413
+ pattern: "(?i)Total usage[^0-9]*([0-9]+(?:\\.[0-9]+)?)")?.first
414
414
  {
415
415
  let remainingValue = self.doubleValue(remaining) ?? 0
416
416
  let totalValue = self.doubleValue(total) ?? 0
@@ -613,7 +613,7 @@ enum MiniMaxWebParsing {
613
613
  }
614
614
 
615
615
  #if os(macOS)
616
- private struct MiniMaxWebSession: Sendable {
616
+ private struct MiniMaxWebSession {
617
617
  let cookieHeader: String
618
618
  let accessToken: String?
619
619
  let groupID: String?
@@ -621,7 +621,7 @@ private struct MiniMaxWebSession: Sendable {
621
621
  let isManual: Bool
622
622
  }
623
623
 
624
- private struct MiniMaxCookieSession: Sendable {
624
+ private struct MiniMaxCookieSession {
625
625
  let cookieHeader: String
626
626
  let sourceLabel: String
627
627
  }
@@ -674,7 +674,7 @@ private enum MiniMaxCookieImporter {
674
674
  }
675
675
  }
676
676
 
677
- private struct MiniMaxLocalStorageToken: Sendable {
677
+ private struct MiniMaxLocalStorageToken {
678
678
  let accessToken: String
679
679
  let groupID: String?
680
680
  let sourceLabel: String
@@ -706,7 +706,7 @@ private enum MiniMaxLocalStorageImporter {
706
706
  return tokens
707
707
  }
708
708
 
709
- private struct LocalStorageCandidate: Sendable {
709
+ private struct LocalStorageCandidate {
710
710
  let label: String
711
711
  let levelDBURL: URL
712
712
  }
@@ -789,7 +789,7 @@ private enum MiniMaxLocalStorageImporter {
789
789
  }
790
790
  }
791
791
 
792
- private struct TokenMatch: Sendable {
792
+ private struct TokenMatch {
793
793
  let accessToken: String
794
794
  let groupID: String?
795
795
  }
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -71,7 +71,7 @@ struct MistralAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum MistralSettingsError: LocalizedError, Sendable {
74
+ enum MistralSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct MistralModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct MistralUsageFetcher {
11
+ enum MistralUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.mistral.ai/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> MistralModelsResponse {
@@ -57,7 +57,7 @@ extension MistralModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum MistralAPIError: LocalizedError, Sendable {
60
+ enum MistralAPIError: LocalizedError {
61
61
  case invalidResponse
62
62
  case httpError(statusCode: Int, body: String?)
63
63
 
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -74,7 +74,7 @@ struct OpenRouterAPIFetchStrategy: ProviderFetchStrategy {
74
74
 
75
75
  // MARK: - Settings Errors
76
76
 
77
- enum OpenRouterSettingsError: LocalizedError, Sendable {
77
+ enum OpenRouterSettingsError: LocalizedError {
78
78
  case missingToken
79
79
 
80
80
  var errorDescription: String? {
@@ -51,16 +51,30 @@ struct OpenRouterKeyInfoResponse: Decodable {
51
51
  }
52
52
  }
53
53
 
54
- var keyUsage: Double { self.data?.usage ?? 0 }
55
- var keyLimit: Double? { self.data?.limit }
56
- var isFreeTier: Bool { self.data?.is_free_tier ?? false }
57
- var rateLimitRequests: Int? { self.data?.rate_limit?.requests }
58
- var rateLimitInterval: String? { self.data?.rate_limit?.interval }
54
+ var keyUsage: Double {
55
+ self.data?.usage ?? 0
56
+ }
57
+
58
+ var keyLimit: Double? {
59
+ self.data?.limit
60
+ }
61
+
62
+ var isFreeTier: Bool {
63
+ self.data?.is_free_tier ?? false
64
+ }
65
+
66
+ var rateLimitRequests: Int? {
67
+ self.data?.rate_limit?.requests
68
+ }
69
+
70
+ var rateLimitInterval: String? {
71
+ self.data?.rate_limit?.interval
72
+ }
59
73
  }
60
74
 
61
75
  // MARK: - Fetcher
62
76
 
63
- struct OpenRouterUsageFetcher {
77
+ enum OpenRouterUsageFetcher {
64
78
  static let creditsURL = URL(string: "https://openrouter.ai/api/v1/credits")!
65
79
  static let keyInfoURL = URL(string: "https://openrouter.ai/api/v1/auth/key")!
66
80
  private static let requestTimeout: TimeInterval = 15
@@ -91,18 +105,16 @@ struct OpenRouterUsageFetcher {
91
105
  throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
92
106
  }
93
107
 
94
- if let json = String(data: data, encoding: .utf8) {
95
- Self.log.debug("OpenRouter credits response: \(json)")
96
- }
108
+ Self.log.debug("OpenRouter credits response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
97
109
 
98
110
  return try JSONDecoder().decode(OpenRouterCreditsResponse.self, from: data)
99
111
  }
100
112
 
101
113
  private static func fetchKeyInfoBestEffort(apiKey: String) async -> OpenRouterKeyInfoResponse? {
102
114
  do {
103
- return try await Self.fetchKeyInfo(apiKey: apiKey)
115
+ return try await self.fetchKeyInfo(apiKey: apiKey)
104
116
  } catch {
105
- Self.log.info("OpenRouter key info unavailable: \(error.localizedDescription)")
117
+ self.log.info("OpenRouter key info unavailable: \(error.localizedDescription)")
106
118
  return nil
107
119
  }
108
120
  }
@@ -122,13 +134,10 @@ struct OpenRouterUsageFetcher {
122
134
  throw OpenRouterAPIError.httpError(statusCode: httpResponse.statusCode)
123
135
  }
124
136
 
125
- if let json = String(data: data, encoding: .utf8) {
126
- Self.log.debug("OpenRouter key info response: \(json)")
127
- }
137
+ Self.log.debug("OpenRouter key info response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
128
138
 
129
139
  return try JSONDecoder().decode(OpenRouterKeyInfoResponse.self, from: data)
130
140
  }
131
-
132
141
  }
133
142
 
134
143
  // MARK: - Snapshot conversion
@@ -189,7 +198,7 @@ extension OpenRouterCreditsResponse {
189
198
 
190
199
  // MARK: - Errors
191
200
 
192
- enum OpenRouterAPIError: LocalizedError, Sendable {
201
+ enum OpenRouterAPIError: LocalizedError {
193
202
  case invalidResponse
194
203
  case httpError(statusCode: Int)
195
204
  case decodingError
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -71,7 +71,7 @@ struct PerplexityAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum PerplexitySettingsError: LocalizedError, Sendable {
74
+ enum PerplexitySettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct PerplexityModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct PerplexityUsageFetcher {
11
+ enum PerplexityUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.perplexity.ai/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> PerplexityModelsResponse {
@@ -57,7 +57,7 @@ extension PerplexityModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum PerplexityAPIError: LocalizedError, Sendable {
60
+ enum PerplexityAPIError: LocalizedError {
61
61
  case invalidResponse
62
62
  case httpError(statusCode: Int, body: String?)
63
63
 
@@ -170,8 +170,8 @@ public struct ProviderFetchPipeline: Sendable {
170
170
 
171
171
  public init(
172
172
  resolveStrategies: @escaping @Sendable (ProviderFetchContext) async -> [any ProviderFetchStrategy],
173
- strategyTimeout: TimeInterval = 45
174
- ) {
173
+ strategyTimeout: TimeInterval = 45)
174
+ {
175
175
  self.resolveStrategies = resolveStrategies
176
176
  self.strategyTimeout = strategyTimeout
177
177
  }
@@ -472,11 +472,11 @@ public enum ProviderTokenResolver {
472
472
  if dataProtection {
473
473
  query[kSecUseDataProtectionKeychain as String] = true
474
474
  }
475
- #if canImport(LocalAuthentication)
475
+ #if canImport(LocalAuthentication)
476
476
  let authContext = LAContext()
477
477
  authContext.interactionNotAllowed = true
478
478
  query[kSecUseAuthenticationContext as String] = authContext
479
- #endif
479
+ #endif
480
480
 
481
481
  let status = SecItemCopyMatching(query as CFDictionary, &result)
482
482
  if status == errSecItemNotFound || status == errSecInteractionNotAllowed {
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -33,7 +33,9 @@ public enum QwenProviderDescriptor {
33
33
  color: ProviderColor(red: 1.0, green: 0.416, blue: 0.0)),
34
34
  tokenCost: ProviderTokenCostConfig(
35
35
  supportsTokenCost: false,
36
- noDataMessage: { "Qwen cost estimates are shown per-model in the details submenu (based on public DashScope pricing)." }),
36
+ noDataMessage: {
37
+ "Qwen cost estimates are shown per-model in the details submenu (based on public DashScope pricing)."
38
+ }),
37
39
  fetchPlan: ProviderFetchPlan(
38
40
  sourceModes: [.auto, .cli],
39
41
  pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [QwenAPIFetchStrategy()] })),
@@ -71,7 +73,7 @@ struct QwenAPIFetchStrategy: ProviderFetchStrategy {
71
73
  }
72
74
  }
73
75
 
74
- enum QwenSettingsError: LocalizedError, Sendable {
76
+ enum QwenSettingsError: LocalizedError {
75
77
  case missingToken
76
78
 
77
79
  var errorDescription: String? {
@@ -46,10 +46,10 @@ public enum QwenModelPricing {
46
46
  "qwen-turbo-latest": Tier(inputPerMillion: 0.30, outputPerMillion: 0.60, contextWindow: 131_072),
47
47
  "qwen-plus": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
48
48
  "qwen-plus-latest": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
49
- "qwen-max": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32_768),
50
- "qwen-max-latest": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32_768),
51
- "qwen-vl-max": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32_768),
52
- "qwen-vl-max-latest": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32_768),
49
+ "qwen-max": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32768),
50
+ "qwen-max-latest": Tier(inputPerMillion: 2.40, outputPerMillion: 9.60, contextWindow: 32768),
51
+ "qwen-vl-max": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32768),
52
+ "qwen-vl-max-latest": Tier(inputPerMillion: 3.00, outputPerMillion: 9.60, contextWindow: 32768),
53
53
  "qwen-vl-plus": Tier(inputPerMillion: 0.80, outputPerMillion: 2.00, contextWindow: 131_072),
54
54
  "qwen-long": Tier(inputPerMillion: 0.50, outputPerMillion: 2.00, contextWindow: 10_000_000),
55
55
  "qwen2.5-72b-instruct": Tier(inputPerMillion: 0.56, outputPerMillion: 1.12, contextWindow: 131_072),
@@ -185,11 +185,25 @@ private struct QwenModelUsageRaw: Decodable {
185
185
  let requests: Int?
186
186
  let calls: Int?
187
187
 
188
- var resolvedModelID: String? { self.model_id ?? self.model }
189
- var resolvedInputTokens: Int { self.input_tokens ?? 0 }
190
- var resolvedOutputTokens: Int { self.output_tokens ?? 0 }
191
- var resolvedTotalTokens: Int { self.total_tokens ?? self.tokens ?? (self.resolvedInputTokens + self.resolvedOutputTokens) }
192
- var resolvedRequestCount: Int { self.request_count ?? self.requests ?? self.calls ?? 0 }
188
+ var resolvedModelID: String? {
189
+ self.model_id ?? self.model
190
+ }
191
+
192
+ var resolvedInputTokens: Int {
193
+ self.input_tokens ?? 0
194
+ }
195
+
196
+ var resolvedOutputTokens: Int {
197
+ self.output_tokens ?? 0
198
+ }
199
+
200
+ var resolvedTotalTokens: Int {
201
+ self.total_tokens ?? self.tokens ?? (self.resolvedInputTokens + self.resolvedOutputTokens)
202
+ }
203
+
204
+ var resolvedRequestCount: Int {
205
+ self.request_count ?? self.requests ?? self.calls ?? 0
206
+ }
193
207
  }
194
208
 
195
209
  // MARK: - Fetcher
@@ -218,9 +232,7 @@ public struct QwenUsageFetcher: Sendable {
218
232
  throw QwenUsageError.apiError("HTTP \(httpResponse.statusCode): \(errorMessage)")
219
233
  }
220
234
 
221
- if let jsonString = String(data: data, encoding: .utf8) {
222
- Self.log.debug("DashScope usage response: \(jsonString)")
223
- }
235
+ Self.log.debug("DashScope usage response: HTTP \(httpResponse.statusCode), \(data.count) bytes")
224
236
 
225
237
  let apiResponse = try JSONDecoder().decode(QwenUsageResponse.self, from: data)
226
238
  guard apiResponse.isSuccess else {
@@ -243,11 +255,10 @@ public struct QwenUsageFetcher: Sendable {
243
255
  let output = model.resolvedOutputTokens
244
256
  let tokens = model.resolvedTotalTokens
245
257
  let requests = model.resolvedRequestCount
246
- let cost: Double?
247
- if input > 0 || output > 0 {
248
- cost = QwenModelPricing.estimateCost(inputTokens: input, outputTokens: output, modelID: modelID)
258
+ let cost: Double? = if input > 0 || output > 0 {
259
+ QwenModelPricing.estimateCost(inputTokens: input, outputTokens: output, modelID: modelID)
249
260
  } else {
250
- cost = QwenModelPricing.estimateCost(tokens: tokens, modelID: modelID)
261
+ QwenModelPricing.estimateCost(tokens: tokens, modelID: modelID)
251
262
  }
252
263
  entries.append(QwenModelUsageEntry(
253
264
  modelID: modelID,
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -71,7 +71,7 @@ struct SambaNovaAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum SambaNovaSettingsError: LocalizedError, Sendable {
74
+ enum SambaNovaSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct SambaNovaModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct SambaNovaUsageFetcher {
11
+ enum SambaNovaUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.sambanova.ai/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> SambaNovaModelsResponse {
@@ -57,7 +57,7 @@ extension SambaNovaModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum SambaNovaAPIError: LocalizedError, Sendable {
60
+ enum SambaNovaAPIError: LocalizedError {
61
61
  case invalidResponse
62
62
  case httpError(statusCode: Int, body: String?)
63
63
 
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -71,7 +71,7 @@ struct TogetherAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum TogetherSettingsError: LocalizedError, Sendable {
74
+ enum TogetherSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct TogetherModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct TogetherUsageFetcher {
11
+ enum TogetherUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.together.xyz/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> TogetherModelsResponse {
@@ -57,7 +57,7 @@ extension TogetherModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum TogetherAPIError: LocalizedError, Sendable {
60
+ enum TogetherAPIError: LocalizedError {
61
61
  case invalidResponse
62
62
  case httpError(statusCode: Int, body: String?)
63
63
 
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -75,7 +75,7 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
75
75
  }
76
76
 
77
77
  private static func resolveProject(context: ProviderFetchContext) -> String? {
78
- if let project = Self.cleaned(context.settings?.vertexai?.project) {
78
+ if let project = cleaned(context.settings?.vertexai?.project) {
79
79
  return project
80
80
  }
81
81
  if let project = Self.cleaned(context.env["VERTEX_AI_PROJECT"]) {
@@ -91,7 +91,7 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
91
91
  }
92
92
 
93
93
  private static func resolveLocation(context: ProviderFetchContext) -> String? {
94
- if let location = Self.cleaned(context.settings?.vertexai?.location) {
94
+ if let location = cleaned(context.settings?.vertexai?.location) {
95
95
  return location
96
96
  }
97
97
  if let location = Self.cleaned(context.env["VERTEX_AI_LOCATION"]) {
@@ -110,13 +110,13 @@ struct VertexAICLIFetchStrategy: ProviderFetchStrategy {
110
110
  }
111
111
  }
112
112
 
113
- enum VertexAISettingsError: LocalizedError, Sendable {
113
+ enum VertexAISettingsError: LocalizedError {
114
114
  case missingProject
115
115
 
116
116
  var errorDescription: String? {
117
117
  switch self {
118
118
  case .missingProject:
119
- return "Google Cloud project not found. Set it in Preferences \u{2192} Providers \u{2192} Vertex AI or " +
119
+ "Google Cloud project not found. Set it in Preferences \u{2192} Providers \u{2192} Vertex AI or " +
120
120
  "export VERTEX_AI_PROJECT."
121
121
  }
122
122
  }
@@ -27,7 +27,7 @@ public struct VertexAISettingsReader: Sendable {
27
27
  public static func credentials(
28
28
  environment: [String: String] = ProcessInfo.processInfo.environment) -> String?
29
29
  {
30
- self.cleaned(environment[credentialsKey])
30
+ self.cleaned(environment[self.credentialsKey])
31
31
  }
32
32
 
33
33
  static func cleaned(_ raw: String?) -> String? {
@@ -1,7 +1,7 @@
1
1
  import Foundation
2
2
 
3
- struct VertexAIModelsResponse: Decodable, Sendable {
4
- struct Model: Decodable, Sendable {
3
+ struct VertexAIModelsResponse: Decodable {
4
+ struct Model: Decodable {
5
5
  let name: String?
6
6
  let displayName: String?
7
7
  }
@@ -117,7 +117,7 @@ extension VertexAIModelsResponse {
117
117
  }
118
118
  }
119
119
 
120
- enum VertexAICLIError: LocalizedError, Sendable {
120
+ enum VertexAICLIError: LocalizedError {
121
121
  case gcloudNotFound
122
122
  case commandFailed(statusCode: Int, detail: String?)
123
123
  case commandTimedOut