@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
@@ -155,8 +155,8 @@ public struct ErrorEvent: Codable, Sendable, Identifiable {
155
155
  public struct DailyPerformanceStats: Codable, Sendable, Identifiable {
156
156
  /// Computed identifier from provider-model-date
157
157
  public var id: String {
158
- let modelPart = model.map { "-\($0)" } ?? ""
159
- return "\(provider)\(modelPart)-\(date)"
158
+ let modelPart = self.model.map { "-\($0)" } ?? ""
159
+ return "\(self.provider)\(modelPart)-\(self.date)"
160
160
  }
161
161
 
162
162
  /// Provider for these statistics
@@ -298,7 +298,7 @@ extension QualityRating {
298
298
 
299
299
  /// Validates that the rating is within acceptable bounds (1-5)
300
300
  public var isValid: Bool {
301
- rating >= 1 && rating <= 5
301
+ self.rating >= 1 && self.rating <= 5
302
302
  }
303
303
  }
304
304
 
@@ -327,7 +327,7 @@ extension LatencyMetric {
327
327
 
328
328
  /// Computed duration in seconds
329
329
  public var durationSeconds: Double {
330
- Double(durationMs) / 1000.0
330
+ Double(self.durationMs) / 1000.0
331
331
  }
332
332
  }
333
333
 
@@ -3,7 +3,6 @@ import SQLite3
3
3
 
4
4
  /// Storage layer for performance metrics using SQLite
5
5
  public final class PerformanceStorageImpl {
6
-
7
6
  // MARK: - Properties
8
7
 
9
8
  private var db: OpaquePointer?
@@ -14,8 +13,7 @@ public final class PerformanceStorageImpl {
14
13
  public init() {
15
14
  let appSupport = FileManager.default.urls(
16
15
  for: .applicationSupportDirectory,
17
- in: .userDomainMask
18
- ).first!
16
+ in: .userDomainMask).first!
19
17
  let runicDir = appSupport.appendingPathComponent("Runic")
20
18
 
21
19
  // Ensure directory exists
@@ -25,7 +23,7 @@ public final class PerformanceStorageImpl {
25
23
  }
26
24
 
27
25
  deinit {
28
- if let db = db {
26
+ if let db {
29
27
  sqlite3_close(db)
30
28
  }
31
29
  }
@@ -33,20 +31,20 @@ public final class PerformanceStorageImpl {
33
31
  // MARK: - Database Connection
34
32
 
35
33
  private func open() throws {
36
- guard db == nil else { return }
34
+ guard self.db == nil else { return }
37
35
 
38
36
  let flags = SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX
39
- let result = sqlite3_open_v2(dbPath.path, &db, flags, nil)
37
+ let result = sqlite3_open_v2(dbPath.path, &self.db, flags, nil)
40
38
 
41
39
  guard result == SQLITE_OK else {
42
- throw StorageError.openFailed(message: String(cString: sqlite3_errmsg(db)))
40
+ throw StorageError.openFailed(message: String(cString: sqlite3_errmsg(self.db)))
43
41
  }
44
42
 
45
- try createTablesIfNeeded()
43
+ try self.createTablesIfNeeded()
46
44
  }
47
45
 
48
46
  private func close() {
49
- if let db = db {
47
+ if let db {
50
48
  sqlite3_close(db)
51
49
  self.db = nil
52
50
  }
@@ -120,18 +118,18 @@ public final class PerformanceStorageImpl {
120
118
  "CREATE INDEX IF NOT EXISTS idx_latency_created ON latency_metrics(created_at)",
121
119
  "CREATE INDEX IF NOT EXISTS idx_quality_timestamp ON quality_ratings(timestamp)",
122
120
  "CREATE INDEX IF NOT EXISTS idx_error_timestamp ON error_events(timestamp)",
123
- "CREATE INDEX IF NOT EXISTS idx_daily_stats_date ON daily_stats(date)"
121
+ "CREATE INDEX IF NOT EXISTS idx_daily_stats_date ON daily_stats(date)",
124
122
  ]
125
123
 
126
124
  for schema in schemas {
127
- try execute(schema)
125
+ try self.execute(schema)
128
126
  }
129
127
  }
130
128
 
131
129
  // MARK: - Execute SQL
132
130
 
133
131
  private func execute(_ sql: String) throws {
134
- guard let db = db else {
132
+ guard let db else {
135
133
  throw StorageError.notOpen
136
134
  }
137
135
 
@@ -148,24 +146,29 @@ public final class PerformanceStorageImpl {
148
146
  // MARK: - Save Metrics
149
147
 
150
148
  public func save(latency: LatencyMetric) async throws {
151
- try open()
149
+ try self.open()
152
150
 
153
151
  let sql = """
154
- INSERT OR REPLACE INTO latency_metrics
155
- (id, request_id, provider, model, start_time, end_time, duration_ms, success, created_at)
156
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
157
- """
152
+ INSERT OR REPLACE INTO latency_metrics
153
+ (id, request_id, provider, model, start_time, end_time, duration_ms, success, created_at)
154
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
155
+ """
158
156
 
159
157
  var statement: OpaquePointer?
160
158
  defer { sqlite3_finalize(statement) }
161
159
 
162
- guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
163
- throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
160
+ guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
161
+ throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
164
162
  }
165
163
 
166
164
  sqlite3_bind_text(statement, 1, latency.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
167
165
  sqlite3_bind_text(statement, 2, latency.requestID, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
168
- sqlite3_bind_text(statement, 3, latency.provider.rawValue, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
166
+ sqlite3_bind_text(
167
+ statement,
168
+ 3,
169
+ latency.provider.rawValue,
170
+ -1,
171
+ unsafeBitCast(-1, to: sqlite3_destructor_type.self))
169
172
 
170
173
  if let model = latency.model {
171
174
  sqlite3_bind_text(statement, 4, model, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
@@ -180,29 +183,34 @@ public final class PerformanceStorageImpl {
180
183
  sqlite3_bind_double(statement, 9, latency.createdAt.timeIntervalSince1970)
181
184
 
182
185
  guard sqlite3_step(statement) == SQLITE_DONE else {
183
- throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
186
+ throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
184
187
  }
185
188
  }
186
189
 
187
190
  public func save(rating: QualityRating) async throws {
188
- try open()
191
+ try self.open()
189
192
 
190
193
  let sql = """
191
- INSERT OR REPLACE INTO quality_ratings
192
- (id, request_id, provider, model, rating, comment, timestamp)
193
- VALUES (?, ?, ?, ?, ?, ?, ?)
194
- """
194
+ INSERT OR REPLACE INTO quality_ratings
195
+ (id, request_id, provider, model, rating, comment, timestamp)
196
+ VALUES (?, ?, ?, ?, ?, ?, ?)
197
+ """
195
198
 
196
199
  var statement: OpaquePointer?
197
200
  defer { sqlite3_finalize(statement) }
198
201
 
199
- guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
200
- throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
202
+ guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
203
+ throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
201
204
  }
202
205
 
203
206
  sqlite3_bind_text(statement, 1, rating.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
204
207
  sqlite3_bind_text(statement, 2, rating.requestID, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
205
- sqlite3_bind_text(statement, 3, rating.provider.rawValue, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
208
+ sqlite3_bind_text(
209
+ statement,
210
+ 3,
211
+ rating.provider.rawValue,
212
+ -1,
213
+ unsafeBitCast(-1, to: sqlite3_destructor_type.self))
206
214
 
207
215
  if let model = rating.model {
208
216
  sqlite3_bind_text(statement, 4, model, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
@@ -221,29 +229,39 @@ public final class PerformanceStorageImpl {
221
229
  sqlite3_bind_double(statement, 7, rating.timestamp.timeIntervalSince1970)
222
230
 
223
231
  guard sqlite3_step(statement) == SQLITE_DONE else {
224
- throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
232
+ throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
225
233
  }
226
234
  }
227
235
 
228
236
  public func save(error: ErrorEvent) async throws {
229
- try open()
237
+ try self.open()
230
238
 
231
239
  let sql = """
232
- INSERT OR REPLACE INTO error_events
233
- (id, provider, error_type, error_message, retry_count, timestamp)
234
- VALUES (?, ?, ?, ?, ?, ?)
235
- """
240
+ INSERT OR REPLACE INTO error_events
241
+ (id, provider, error_type, error_message, retry_count, timestamp)
242
+ VALUES (?, ?, ?, ?, ?, ?)
243
+ """
236
244
 
237
245
  var statement: OpaquePointer?
238
246
  defer { sqlite3_finalize(statement) }
239
247
 
240
- guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
241
- throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
248
+ guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
249
+ throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
242
250
  }
243
251
 
244
252
  sqlite3_bind_text(statement, 1, error.id, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
245
- sqlite3_bind_text(statement, 2, error.provider.rawValue, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
246
- sqlite3_bind_text(statement, 3, error.errorType.rawValue, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
253
+ sqlite3_bind_text(
254
+ statement,
255
+ 2,
256
+ error.provider.rawValue,
257
+ -1,
258
+ unsafeBitCast(-1, to: sqlite3_destructor_type.self))
259
+ sqlite3_bind_text(
260
+ statement,
261
+ 3,
262
+ error.errorType.rawValue,
263
+ -1,
264
+ unsafeBitCast(-1, to: sqlite3_destructor_type.self))
247
265
 
248
266
  if let message = error.errorMessage {
249
267
  sqlite3_bind_text(statement, 4, message, -1, unsafeBitCast(-1, to: sqlite3_destructor_type.self))
@@ -255,7 +273,7 @@ public final class PerformanceStorageImpl {
255
273
  sqlite3_bind_double(statement, 6, error.timestamp.timeIntervalSince1970)
256
274
 
257
275
  guard sqlite3_step(statement) == SQLITE_DONE else {
258
- throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(db)))
276
+ throw StorageError.insertFailed(message: String(cString: sqlite3_errmsg(self.db)))
259
277
  }
260
278
  }
261
279
 
@@ -264,17 +282,17 @@ public final class PerformanceStorageImpl {
264
282
  public func fetchDailyStats(
265
283
  timeRange: Int,
266
284
  provider: UsageProvider? = nil,
267
- model: String? = nil
268
- ) async throws -> [DailyPerformanceStats] {
269
- try open()
285
+ model: String? = nil) async throws -> [DailyPerformanceStats]
286
+ {
287
+ try self.open()
270
288
 
271
289
  var sql = "SELECT * FROM daily_stats WHERE date >= date('now', '-\(timeRange) days')"
272
290
 
273
- if let provider = provider {
291
+ if let provider {
274
292
  sql += " AND provider = '\(provider.rawValue)'"
275
293
  }
276
294
 
277
- if let model = model {
295
+ if let model {
278
296
  sql += " AND model = '\(model)'"
279
297
  }
280
298
 
@@ -283,8 +301,8 @@ public final class PerformanceStorageImpl {
283
301
  var statement: OpaquePointer?
284
302
  defer { sqlite3_finalize(statement) }
285
303
 
286
- guard sqlite3_prepare_v2(db, sql, -1, &statement, nil) == SQLITE_OK else {
287
- throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(db)))
304
+ guard sqlite3_prepare_v2(self.db, sql, -1, &statement, nil) == SQLITE_OK else {
305
+ throw StorageError.prepareFailed(message: String(cString: sqlite3_errmsg(self.db)))
288
306
  }
289
307
 
290
308
  var stats: [DailyPerformanceStats] = []
@@ -337,8 +355,7 @@ public final class PerformanceStorageImpl {
337
355
  quotaCount: quotaCount,
338
356
  networkCount: networkCount,
339
357
  apiErrorCount: apiErrorCount,
340
- createdAt: createdAt
341
- )
358
+ createdAt: createdAt)
342
359
 
343
360
  stats.append(stat)
344
361
  }
@@ -349,12 +366,12 @@ public final class PerformanceStorageImpl {
349
366
  // MARK: - Database Maintenance
350
367
 
351
368
  public func vacuum() async throws {
352
- try open()
353
- try execute("VACUUM")
369
+ try self.open()
370
+ try self.execute("VACUUM")
354
371
  }
355
372
 
356
373
  public func deleteOldData(olderThan days: Int) async throws {
357
- try open()
374
+ try self.open()
358
375
 
359
376
  let cutoffDate = Date().addingTimeInterval(-Double(days) * 24 * 60 * 60)
360
377
  let cutoffTimestamp = cutoffDate.timeIntervalSince1970
@@ -362,11 +379,11 @@ public final class PerformanceStorageImpl {
362
379
  let queries = [
363
380
  "DELETE FROM latency_metrics WHERE created_at < \(cutoffTimestamp)",
364
381
  "DELETE FROM quality_ratings WHERE timestamp < \(cutoffTimestamp)",
365
- "DELETE FROM error_events WHERE timestamp < \(cutoffTimestamp)"
382
+ "DELETE FROM error_events WHERE timestamp < \(cutoffTimestamp)",
366
383
  ]
367
384
 
368
385
  for query in queries {
369
- try execute(query)
386
+ try self.execute(query)
370
387
  }
371
388
  }
372
389
 
@@ -382,15 +399,15 @@ public final class PerformanceStorageImpl {
382
399
  public var errorDescription: String? {
383
400
  switch self {
384
401
  case .notOpen:
385
- return "Database not open"
386
- case .openFailed(let message):
387
- return "Failed to open database: \(message)"
388
- case .prepareFailed(let message):
389
- return "Failed to prepare statement: \(message)"
390
- case .executionFailed(let message):
391
- return "Failed to execute statement: \(message)"
392
- case .insertFailed(let message):
393
- return "Failed to insert data: \(message)"
402
+ "Database not open"
403
+ case let .openFailed(message):
404
+ "Failed to open database: \(message)"
405
+ case let .prepareFailed(message):
406
+ "Failed to prepare statement: \(message)"
407
+ case let .executionFailed(message):
408
+ "Failed to execute statement: \(message)"
409
+ case let .insertFailed(message):
410
+ "Failed to insert data: \(message)"
394
411
  }
395
412
  }
396
413
  }
@@ -400,7 +417,7 @@ public final class PerformanceStorageImpl {
400
417
 
401
418
  extension String {
402
419
  fileprivate init(cString pointer: UnsafePointer<UInt8>?) {
403
- guard let pointer = pointer else {
420
+ guard let pointer else {
404
421
  self = ""
405
422
  return
406
423
  }
@@ -145,7 +145,7 @@ public enum UsagePolicyEngine {
145
145
  context: UsagePolicyContext) -> String?
146
146
  {
147
147
  switch condition {
148
- case .projectedBudgetOverrun(let minimumPercent):
148
+ case let .projectedBudgetOverrun(minimumPercent):
149
149
  guard let budget = context.budgetLimitUSD, budget > 0 else { return nil }
150
150
  guard let projected = context.projectedSpendUSD, projected.isFinite else { return nil }
151
151
  let overrunUSD = projected - budget
@@ -161,7 +161,7 @@ public enum UsagePolicyEngine {
161
161
  guard observed >= budget else { return nil }
162
162
  return "Observed spend \(UsageFormatter.usdString(observed)) reached budget \(UsageFormatter.usdString(budget))."
163
163
 
164
- case .anomalySeverityAtLeast(let required):
164
+ case let .anomalySeverityAtLeast(required):
165
165
  guard let observed = context.anomalySeverity else { return nil }
166
166
  guard observed.rawValue >= required.rawValue else { return nil }
167
167
  return "Anomaly severity \(self.severityLabel(observed)) meets threshold \(self.severityLabel(required))."
@@ -2,11 +2,14 @@ import Foundation
2
2
  import RunicCore
3
3
 
4
4
  /// Storage for project budget limits and alert thresholds
5
- public struct ProjectBudgetStore {
5
+ public enum ProjectBudgetStore {
6
6
  // MARK: - Types
7
7
 
8
8
  public struct ProjectBudget: Codable, Sendable, Identifiable {
9
- public var id: String { projectID }
9
+ public var id: String {
10
+ self.projectID
11
+ }
12
+
10
13
  public let projectID: String
11
14
  public var projectName: String?
12
15
  public var monthlyLimit: Double
@@ -20,8 +23,8 @@ public struct ProjectBudgetStore {
20
23
  monthlyLimit: Double,
21
24
  alertThreshold: Double = 0.8,
22
25
  enabled: Bool = true,
23
- createdAt: Date = Date()
24
- ) {
26
+ createdAt: Date = Date())
27
+ {
25
28
  self.projectID = projectID
26
29
  self.projectName = projectName
27
30
  self.monthlyLimit = monthlyLimit
@@ -53,7 +56,7 @@ public struct ProjectBudgetStore {
53
56
  // MARK: - Public Methods
54
57
 
55
58
  public static func load() -> BudgetsData {
56
- guard FileManager.default.fileExists(atPath: storageURL.path) else {
59
+ guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
57
60
  return BudgetsData()
58
61
  }
59
62
 
@@ -74,26 +77,26 @@ public struct ProjectBudgetStore {
74
77
  encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
75
78
 
76
79
  let data = try encoder.encode(budgetsData)
77
- try data.write(to: storageURL, options: .atomic)
80
+ try data.write(to: self.storageURL, options: .atomic)
78
81
  }
79
82
 
80
83
  public static func getBudget(projectID: String) -> ProjectBudget? {
81
- load().budgets[projectID]
84
+ self.load().budgets[projectID]
82
85
  }
83
86
 
84
87
  public static func setBudget(_ budget: ProjectBudget) throws {
85
- var data = load()
88
+ var data = self.load()
86
89
  data.budgets[budget.projectID] = budget
87
- try save(data)
90
+ try self.save(data)
88
91
  }
89
92
 
90
93
  public static func removeBudget(projectID: String) throws {
91
- var data = load()
94
+ var data = self.load()
92
95
  data.budgets.removeValue(forKey: projectID)
93
- try save(data)
96
+ try self.save(data)
94
97
  }
95
98
 
96
99
  public static func getAllBudgets() -> [ProjectBudget] {
97
- Array(load().budgets.values).sorted { $0.createdAt < $1.createdAt }
100
+ Array(self.load().budgets.values).sorted { $0.createdAt < $1.createdAt }
98
101
  }
99
102
  }
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -49,7 +49,9 @@ struct AntigravityStatusFetchStrategy: ProviderFetchStrategy {
49
49
  let id: String = "antigravity.local"
50
50
  let kind: ProviderFetchKind = .localProbe
51
51
 
52
- func isAvailable(_: ProviderFetchContext) async -> Bool { true }
52
+ func isAvailable(_: ProviderFetchContext) async -> Bool {
53
+ true
54
+ }
53
55
 
54
56
  func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
55
57
  let probe = AntigravityStatusProbe()
@@ -309,7 +309,7 @@ public struct AntigravityStatusProbe: Sendable {
309
309
 
310
310
  // MARK: - Port detection
311
311
 
312
- private struct ProcessInfoResult: Sendable {
312
+ private struct ProcessInfoResult {
313
313
  let pid: Int
314
314
  let extensionPort: Int?
315
315
  let csrfToken: String
@@ -459,7 +459,7 @@ public struct AntigravityStatusProbe: Sendable {
459
459
  let body: [String: Any]
460
460
  }
461
461
 
462
- private struct RequestContext: Sendable {
462
+ private struct RequestContext {
463
463
  let httpsPort: Int
464
464
  let httpPort: Int?
465
465
  let csrfToken: String
@@ -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 AuggieAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum AuggieSettingsError: LocalizedError, Sendable {
74
+ enum AuggieSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -1,6 +1,6 @@
1
1
  import Foundation
2
2
 
3
- struct AuggieUsageMetrics: Sendable {
3
+ struct AuggieUsageMetrics {
4
4
  let requestCount: Int?
5
5
  let totalTokens: Int?
6
6
  let inputTokens: Int?
@@ -46,7 +46,7 @@ struct AuggieUsageFetcher {
46
46
  URLQueryItem(name: "window", value: "day"),
47
47
  URLQueryItem(name: "days", value: "1"),
48
48
  ]
49
- let url = components?.url ?? baseURL
49
+ let url = components?.url ?? self.baseURL
50
50
 
51
51
  var request = URLRequest(url: url)
52
52
  request.timeoutInterval = Self.requestTimeout
@@ -121,7 +121,7 @@ struct AuggieUsageFetcher {
121
121
  }
122
122
  }
123
123
 
124
- enum AuggieAPIError: LocalizedError, Sendable {
124
+ enum AuggieAPIError: LocalizedError {
125
125
  case invalidResponse
126
126
  case decodingError
127
127
  case httpError(statusCode: Int, body: String?)
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -78,14 +78,14 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
78
78
  }
79
79
 
80
80
  private static func resolveToken(context: ProviderFetchContext) -> ProviderTokenResolution? {
81
- if let token = Self.cleaned(context.settings?.azure?.apiToken) {
81
+ if let token = cleaned(context.settings?.azure?.apiToken) {
82
82
  return ProviderTokenResolution(token: token, source: .keychain)
83
83
  }
84
84
  return ProviderTokenResolver.azureOpenAIResolution(environment: context.env)
85
85
  }
86
86
 
87
87
  private static func resolveEndpoint(context: ProviderFetchContext) -> String? {
88
- if let endpoint = Self.cleaned(context.settings?.azure?.endpoint) {
88
+ if let endpoint = cleaned(context.settings?.azure?.endpoint) {
89
89
  return endpoint
90
90
  }
91
91
  if let endpoint = Self.cleaned(context.env["AZURE_OPENAI_ENDPOINT"]) {
@@ -98,7 +98,7 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
98
98
  }
99
99
 
100
100
  private static func resolveAPIVersion(context: ProviderFetchContext) -> String {
101
- if let version = Self.cleaned(context.settings?.azure?.apiVersion) {
101
+ if let version = cleaned(context.settings?.azure?.apiVersion) {
102
102
  return version
103
103
  }
104
104
  if let version = Self.cleaned(context.env["AZURE_OPENAI_API_VERSION"]) {
@@ -108,7 +108,7 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
108
108
  }
109
109
 
110
110
  private static func resolveDeployment(context: ProviderFetchContext) -> String? {
111
- if let deployment = Self.cleaned(context.settings?.azure?.deployment) {
111
+ if let deployment = cleaned(context.settings?.azure?.deployment) {
112
112
  return deployment
113
113
  }
114
114
  if let deployment = Self.cleaned(context.env["AZURE_OPENAI_DEPLOYMENT"]) {
@@ -124,17 +124,17 @@ struct AzureOpenAIAPIFetchStrategy: ProviderFetchStrategy {
124
124
  }
125
125
  }
126
126
 
127
- enum AzureSettingsError: LocalizedError, Sendable {
127
+ enum AzureSettingsError: LocalizedError {
128
128
  case missingEndpoint
129
129
  case missingToken
130
130
 
131
131
  var errorDescription: String? {
132
132
  switch self {
133
133
  case .missingEndpoint:
134
- return "Azure OpenAI endpoint not found. Set it in Preferences → Providers → Azure OpenAI or " +
134
+ "Azure OpenAI endpoint not found. Set it in Preferences → Providers → Azure OpenAI or " +
135
135
  "export AZURE_OPENAI_ENDPOINT."
136
136
  case .missingToken:
137
- return "Azure OpenAI API key not found. Set it in Preferences → Providers → Azure OpenAI or " +
137
+ "Azure OpenAI API key not found. Set it in Preferences → Providers → Azure OpenAI or " +
138
138
  "export AZURE_OPENAI_API_KEY."
139
139
  }
140
140
  }
@@ -1,7 +1,7 @@
1
1
  import Foundation
2
2
 
3
3
  struct AzureOpenAIDeploymentsResponse: Decodable {
4
- struct Deployment: Decodable, Sendable {
4
+ struct Deployment: Decodable {
5
5
  let id: String?
6
6
  let model: String?
7
7
  let status: String?
@@ -77,7 +77,7 @@ struct AzureUsageFetcher {
77
77
  throw AzureAPIError.invalidResponse
78
78
  }
79
79
 
80
- guard (200 ..< 300).contains(httpResponse.statusCode) else {
80
+ guard (200..<300).contains(httpResponse.statusCode) else {
81
81
  let body = String(data: data, encoding: .utf8)?
82
82
  .trimmingCharacters(in: .whitespacesAndNewlines)
83
83
  throw AzureAPIError.httpError(
@@ -94,6 +94,8 @@ struct AzureUsageFetcher {
94
94
  if !normalized.contains("://") {
95
95
  normalized = "https://\(normalized)"
96
96
  }
97
+ // Enforce HTTPS — reject http:// to prevent cleartext token leakage.
98
+ guard normalized.lowercased().hasPrefix("https://") else { return nil }
97
99
  guard let baseURL = URL(string: normalized),
98
100
  var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
99
101
  else {
@@ -165,7 +167,7 @@ extension AzureOpenAIDeploymentsResponse {
165
167
  }
166
168
  }
167
169
 
168
- enum AzureAPIError: LocalizedError, Sendable {
170
+ enum AzureAPIError: LocalizedError {
169
171
  case invalidEndpoint(String)
170
172
  case invalidResponse
171
173
  case httpError(statusCode: Int, body: String?)
@@ -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 BedrockCLIFetchStrategy: ProviderFetchStrategy {
74
74
  }
75
75
 
76
76
  private static func resolveRegion(context: ProviderFetchContext) -> String? {
77
- if let region = Self.cleaned(context.settings?.bedrock?.region) {
77
+ if let region = cleaned(context.settings?.bedrock?.region) {
78
78
  return region
79
79
  }
80
80
  if let region = Self.cleaned(context.env["AWS_REGION"]) {
@@ -87,7 +87,7 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
87
87
  }
88
88
 
89
89
  private static func resolveProfile(context: ProviderFetchContext) -> String? {
90
- if let profile = Self.cleaned(context.settings?.bedrock?.profile) {
90
+ if let profile = cleaned(context.settings?.bedrock?.profile) {
91
91
  return profile
92
92
  }
93
93
  if let profile = Self.cleaned(context.env["AWS_PROFILE"]) {
@@ -97,7 +97,7 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
97
97
  }
98
98
 
99
99
  private static func resolveModelFilter(context: ProviderFetchContext) -> String? {
100
- if let model = Self.cleaned(context.settings?.bedrock?.modelID) {
100
+ if let model = cleaned(context.settings?.bedrock?.modelID) {
101
101
  return model
102
102
  }
103
103
  if let model = Self.cleaned(context.env["BEDROCK_MODEL_ID"]) {
@@ -113,13 +113,13 @@ struct BedrockCLIFetchStrategy: ProviderFetchStrategy {
113
113
  }
114
114
  }
115
115
 
116
- enum BedrockSettingsError: LocalizedError, Sendable {
116
+ enum BedrockSettingsError: LocalizedError {
117
117
  case missingRegion
118
118
 
119
119
  var errorDescription: String? {
120
120
  switch self {
121
121
  case .missingRegion:
122
- return "AWS region not found. Set it in Preferences → Providers → Amazon Bedrock or " +
122
+ "AWS region not found. Set it in Preferences → Providers → Amazon Bedrock or " +
123
123
  "export AWS_REGION."
124
124
  }
125
125
  }