@sriinnu/runic 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +1 -1
  3. package/README.md +47 -29
  4. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  5. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  6. package/RuniciOS/Views/ProviderListView.swift +10 -10
  7. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  8. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  9. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  10. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  11. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  12. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  13. package/Scripts/package_app.sh +2 -2
  14. package/Sources/Runic/About.swift +3 -3
  15. package/Sources/Runic/AppNotifications.swift +5 -3
  16. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +17 -4
  17. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  18. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  19. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  20. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +47 -14
  21. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  22. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  23. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  24. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  25. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  26. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  28. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +3 -6
  29. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  30. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  31. package/Sources/Runic/Core/RunicColors.swift +6 -6
  32. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  33. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  34. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +10 -36
  35. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +10 -36
  36. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +10 -36
  37. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +10 -36
  38. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +10 -36
  39. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  40. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +10 -36
  41. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +10 -36
  42. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +10 -36
  43. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +10 -36
  44. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +10 -36
  45. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +10 -36
  46. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +10 -36
  47. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +10 -36
  48. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +10 -36
  49. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +10 -36
  50. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  51. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +10 -36
  52. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +10 -36
  53. package/Sources/Runic/Core/Stores/SettingsStore.swift +99 -52
  54. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +10 -36
  55. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  56. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  57. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  58. package/Sources/Runic/Core/Stores/UsageStore.swift +234 -167
  59. package/Sources/Runic/Core/Stores/XAITokenStore.swift +10 -36
  60. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +10 -36
  61. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  62. package/Sources/Runic/ProviderRegistry.swift +1 -1
  63. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  81. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  83. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  84. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  85. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  87. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  88. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  107. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  108. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  109. package/Sources/Runic/Resources/provider-context-windows.json +20 -0
  110. package/Sources/Runic/RunicApp.swift +46 -36
  111. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  112. package/Sources/Runic/UsagePaceText.swift +1 -1
  113. package/Sources/Runic/UsageProgressBar.swift +13 -9
  114. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  115. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  116. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  117. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  118. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  119. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  120. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  121. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  122. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  123. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +15 -20
  124. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  125. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  126. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  127. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  128. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  129. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  130. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  131. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  132. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +198 -58
  133. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  134. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  135. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  136. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  137. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  138. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  139. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +28 -76
  140. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  141. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  142. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  143. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  144. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  145. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  146. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  147. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  148. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  149. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  150. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  151. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  152. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  153. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  154. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +108 -33
  155. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  156. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  157. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  158. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  159. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  160. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  161. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  162. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  163. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  164. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  165. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  166. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  167. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  168. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  169. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  170. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  171. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  172. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  173. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  174. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  175. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  176. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  177. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +80 -71
  178. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  179. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  180. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  181. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +6 -6
  182. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  183. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  184. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  185. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  186. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  187. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  188. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  189. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  190. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  191. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  192. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  193. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  194. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +5 -3
  195. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  196. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  197. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  198. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  199. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  200. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  201. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  202. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  204. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +1 -0
  205. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +12 -1
  206. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  207. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  208. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  209. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  210. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  211. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  212. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  213. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  214. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  215. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  216. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +1 -0
  217. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  218. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  219. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  220. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  221. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +3 -2
  222. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  225. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +6 -2
  226. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  227. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  228. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  229. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  230. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  231. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +8 -11
  232. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  233. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  234. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  235. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  236. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +25 -16
  237. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  240. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  241. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  242. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +27 -16
  243. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  244. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  245. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  246. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  247. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  248. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  249. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  250. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  251. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  252. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  253. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +51 -30
  254. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  255. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  256. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  257. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  258. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  259. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  260. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  261. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  262. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  263. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  264. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  265. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +141 -29
  266. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +59 -5
  267. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +161 -0
  268. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  269. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  270. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  271. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  272. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  273. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  274. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  275. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  276. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  277. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  278. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  279. package/Tests/RunicTests/CLICostTests.swift +6 -7
  280. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  281. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  282. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  283. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  284. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  285. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  286. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  287. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  288. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  289. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  290. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  291. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  292. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  293. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  294. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  295. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  296. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  297. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  298. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  299. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  300. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  301. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  302. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  303. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  304. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  305. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  306. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  307. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  308. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  309. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  310. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  311. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  312. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  313. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  314. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  315. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  316. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  317. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  318. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  319. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  320. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  321. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  322. package/Tests/RunicTests/RunicTests.swift +6 -7
  323. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  324. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  325. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  326. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  327. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  328. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  329. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  330. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  331. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  332. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  333. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  334. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  335. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  336. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  337. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  338. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  339. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  340. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  341. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  342. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  343. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  344. package/appcast.xml +36 -37
  345. package/package.json +1 -1
  346. package/runic.png +0 -0
  347. package/runic.svg +15 -0
  348. package/version.env +2 -2
@@ -1,7 +1,7 @@
1
1
  import Foundation
2
2
 
3
- struct BedrockModelsResponse: Decodable, Sendable {
4
- struct ModelSummary: Decodable, Sendable {
3
+ struct BedrockModelsResponse: Decodable {
4
+ struct ModelSummary: Decodable {
5
5
  let modelID: String?
6
6
  let modelName: String?
7
7
  let providerName: String?
@@ -14,10 +14,6 @@ struct BedrockModelsResponse: Decodable, Sendable {
14
14
  }
15
15
 
16
16
  let modelSummaries: [ModelSummary]?
17
-
18
- init(modelSummaries: [ModelSummary]?) {
19
- self.modelSummaries = modelSummaries
20
- }
21
17
  }
22
18
 
23
19
  struct BedrockUsageFetcher {
@@ -141,7 +137,7 @@ extension BedrockModelsResponse {
141
137
  }
142
138
  }
143
139
 
144
- enum BedrockCLIError: LocalizedError, Sendable {
140
+ enum BedrockCLIError: LocalizedError {
145
141
  case awsCLINotFound
146
142
  case commandFailed(statusCode: Int, detail: String?)
147
143
  case commandTimedOut
@@ -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 CerebrasAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum CerebrasSettingsError: LocalizedError, Sendable {
74
+ enum CerebrasSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct CerebrasModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct CerebrasUsageFetcher {
11
+ enum CerebrasUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.cerebras.ai/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> CerebrasModelsResponse {
@@ -57,7 +57,7 @@ extension CerebrasModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum CerebrasAPIError: LocalizedError, Sendable {
60
+ enum CerebrasAPIError: LocalizedError {
61
61
  case invalidResponse
62
62
  case httpError(statusCode: Int, body: String?)
63
63
 
@@ -82,7 +82,7 @@ enum ClaudeOAuthUsageFetcher {
82
82
  }
83
83
  }
84
84
 
85
- struct OAuthUsageResponse: Decodable, Sendable {
85
+ struct OAuthUsageResponse: Decodable {
86
86
  let fiveHour: OAuthUsageWindow?
87
87
  let sevenDay: OAuthUsageWindow?
88
88
  let sevenDayOAuthApps: OAuthUsageWindow?
@@ -102,7 +102,7 @@ struct OAuthUsageResponse: Decodable, Sendable {
102
102
  }
103
103
  }
104
104
 
105
- struct OAuthUsageWindow: Decodable, Sendable {
105
+ struct OAuthUsageWindow: Decodable {
106
106
  let utilization: Double?
107
107
  let resetsAt: String?
108
108
 
@@ -112,7 +112,7 @@ struct OAuthUsageWindow: Decodable, Sendable {
112
112
  }
113
113
  }
114
114
 
115
- struct OAuthExtraUsage: Decodable, Sendable {
115
+ struct OAuthExtraUsage: Decodable {
116
116
  let isEnabled: Bool?
117
117
  let monthlyLimit: Double?
118
118
  let usedCredits: Double?
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -191,7 +191,9 @@ struct ClaudeCLIFetchStrategy: ProviderFetchStrategy {
191
191
  let kind: ProviderFetchKind = .cli
192
192
  let useWebExtras: Bool
193
193
 
194
- func isAvailable(_: ProviderFetchContext) async -> Bool { true }
194
+ func isAvailable(_: ProviderFetchContext) async -> Bool {
195
+ true
196
+ }
195
197
 
196
198
  func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
197
199
  let fetcher = ClaudeUsageFetcher(dataSource: .cli, useWebExtras: self.useWebExtras)
@@ -326,7 +326,7 @@ public struct ClaudeStatusProbe: Sendable {
326
326
  return nil
327
327
  }
328
328
 
329
- // Collect remaining percentages in the order they appear; used as a backup when labels move/rename.
329
+ /// Collect remaining percentages in the order they appear; used as a backup when labels move/rename.
330
330
  private static func allPercents(_ text: String) -> [Int] {
331
331
  let pat = #"([0-9]{1,3})\p{Zs}*%\s*(left|used)"#
332
332
  guard let regex = try? NSRegularExpression(pattern: pat, options: [.caseInsensitive]) else { return [] }
@@ -379,7 +379,7 @@ public struct ClaudeStatusProbe: Sendable {
379
379
  .joined(separator: " ")
380
380
  }
381
381
 
382
- // Capture all "Resets ..." strings to surface in the menu.
382
+ /// Capture all "Resets ..." strings to surface in the menu.
383
383
  private static func allResets(_ text: String) -> [String] {
384
384
  let pat = #"Resets[^\r\n]*"#
385
385
  guard let regex = try? NSRegularExpression(pattern: pat, options: [.caseInsensitive]) else { return [] }
@@ -500,7 +500,7 @@ public struct ClaudeStatusProbe: Sendable {
500
500
  return nil
501
501
  }
502
502
 
503
- // Extract login/plan string from CLI output.
503
+ /// Extract login/plan string from CLI output.
504
504
  private static func extractLoginMethod(text: String) -> String? {
505
505
  guard !text.isEmpty else { return nil }
506
506
  if let explicit = self.extractFirst(pattern: #"(?i)login\s+method:\s*(.+)"#, text: text) {
@@ -627,7 +627,7 @@ public struct ClaudeStatusProbe: Sendable {
627
627
  }
628
628
  }
629
629
 
630
- // Run claude CLI inside a PTY so we can respond to interactive permission prompts.
630
+ /// Run claude CLI inside a PTY so we can respond to interactive permission prompts.
631
631
  private static func capture(subcommand: String, binary: String, timeout: TimeInterval) async throws -> String {
632
632
  let stopOnSubstrings = subcommand == "/usage" ? ["Current session"] : []
633
633
  do {
@@ -5,7 +5,9 @@ public enum ClaudeUsageDataSource: String, CaseIterable, Identifiable, Sendable
5
5
  case web
6
6
  case cli
7
7
 
8
- public var id: String { self.rawValue }
8
+ public var id: String {
9
+ self.rawValue
10
+ }
9
11
 
10
12
  public var displayName: String {
11
13
  switch self {
@@ -633,7 +633,7 @@ public enum ClaudeWebAPIFetcher {
633
633
  return nil
634
634
  }
635
635
 
636
- private struct ProbeParseResult: Sendable {
636
+ private struct ProbeParseResult {
637
637
  let keys: [String]
638
638
  let emails: [String]
639
639
  let planHints: [String]
@@ -133,6 +133,7 @@ public enum CodexOAuthCredentialsStore {
133
133
  let directory = url.deletingLastPathComponent()
134
134
  try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
135
135
  try data.write(to: url, options: .atomic)
136
+ try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: url.path)
136
137
  }
137
138
 
138
139
  private static func parseLastRefresh(from raw: Any?) -> Date? {
@@ -192,12 +192,23 @@ public enum CodexOAuthUsageFetcher {
192
192
  self.resolveUsageURL(env: ProcessInfo.processInfo.environment, configContents: nil)
193
193
  }
194
194
 
195
+ private static let allowedHostSuffixes = ["openai.com", "chatgpt.com"]
196
+
195
197
  private static func resolveUsageURL(env: [String: String], configContents: String?) -> URL {
196
198
  let baseURL = self.resolveChatGPTBaseURL(env: env, configContents: configContents)
197
199
  let normalized = self.normalizeChatGPTBaseURL(baseURL)
198
200
  let path = normalized.contains("/backend-api") ? Self.chatGPTUsagePath : Self.codexUsagePath
199
201
  let full = normalized + path
200
- return URL(string: full) ?? URL(string: Self.defaultChatGPTBaseURL + Self.chatGPTUsagePath)!
202
+ guard let url = URL(string: full), self.isAllowedHost(url) else {
203
+ return URL(string: Self.defaultChatGPTBaseURL + Self.chatGPTUsagePath)!
204
+ }
205
+ return url
206
+ }
207
+
208
+ private static func isAllowedHost(_ url: URL) -> Bool {
209
+ guard url.scheme?.lowercased() == "https" else { return false }
210
+ guard let host = url.host?.lowercased() else { return false }
211
+ return self.allowedHostSuffixes.contains { host == $0 || host.hasSuffix("." + $0) }
201
212
  }
202
213
 
203
214
  private static func resolveChatGPTBaseURL(env: [String: String], configContents: String?) -> String {
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -119,7 +119,9 @@ struct CodexCLIUsageStrategy: ProviderFetchStrategy {
119
119
  let id: String = "codex.cli"
120
120
  let kind: ProviderFetchKind = .cli
121
121
 
122
- func isAvailable(_: ProviderFetchContext) async -> Bool { true }
122
+ func isAvailable(_: ProviderFetchContext) async -> Bool {
123
+ true
124
+ }
123
125
 
124
126
  func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
125
127
  let usage = try await context.fetcher.loadLatestUsage()
@@ -4,7 +4,9 @@ public enum CodexUsageDataSource: String, CaseIterable, Identifiable, Sendable {
4
4
  case oauth
5
5
  case cli
6
6
 
7
- public var id: String { self.rawValue }
7
+ public var id: String {
8
+ self.rawValue
9
+ }
8
10
 
9
11
  public var displayName: String {
10
12
  switch self {
@@ -39,7 +39,7 @@ public struct CodexWebDashboardStrategy: ProviderFetchStrategy {
39
39
  }
40
40
  }
41
41
 
42
- private struct OpenAIWebCodexResult: Sendable {
42
+ private struct OpenAIWebCodexResult {
43
43
  let usage: UsageSnapshot
44
44
  let credits: CreditsSnapshot?
45
45
  let dashboard: OpenAIDashboardSnapshot
@@ -56,7 +56,7 @@ private enum OpenAIWebCodexError: LocalizedError {
56
56
  }
57
57
  }
58
58
 
59
- private struct OpenAIWebOptions: Sendable {
59
+ private struct OpenAIWebOptions {
60
60
  let timeout: TimeInterval
61
61
  let debugDumpHTML: Bool
62
62
  let verbose: Bool
@@ -168,7 +168,9 @@ public struct CodexWebDashboardStrategy: ProviderFetchStrategy {
168
168
 
169
169
  public init() {}
170
170
 
171
- public func isAvailable(_: ProviderFetchContext) async -> Bool { false }
171
+ public func isAvailable(_: ProviderFetchContext) async -> Bool {
172
+ false
173
+ }
172
174
 
173
175
  public func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
174
176
  throw ProviderFetchError.noAvailableStrategy(.codex)
@@ -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 CohereAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum CohereSettingsError: LocalizedError, Sendable {
74
+ enum CohereSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -9,7 +9,7 @@ struct CohereModelsResponse: Decodable {
9
9
  let name: String?
10
10
 
11
11
  var modelID: String? {
12
- let candidate = id ?? name
12
+ let candidate = self.id ?? self.name
13
13
  guard let candidate else { return nil }
14
14
  let trimmed = candidate.trimmingCharacters(in: .whitespacesAndNewlines)
15
15
  return trimmed.isEmpty ? nil : trimmed
@@ -17,7 +17,7 @@ struct CohereModelsResponse: Decodable {
17
17
  }
18
18
  }
19
19
 
20
- struct CohereUsageFetcher {
20
+ enum CohereUsageFetcher {
21
21
  static let apiURL = URL(string: "https://api.cohere.ai/v1/models")!
22
22
 
23
23
  static func fetchModels(apiKey: String) async throws -> CohereModelsResponse {
@@ -67,7 +67,7 @@ extension CohereModelsResponse {
67
67
  }
68
68
  }
69
69
 
70
- enum CohereAPIError: LocalizedError, Sendable {
70
+ enum CohereAPIError: LocalizedError {
71
71
  case invalidResponse
72
72
  case httpError(statusCode: Int, body: String?)
73
73
 
@@ -67,7 +67,7 @@ public enum CopilotGitHubCLITokenReader {
67
67
 
68
68
  private static func isLikelyGitHubToken(_ token: String) -> Bool {
69
69
  guard !token.isEmpty else { return false }
70
- return tokenPrefixes.contains { token.hasPrefix($0) }
70
+ return self.tokenPrefixes.contains { token.hasPrefix($0) }
71
71
  }
72
72
 
73
73
  private static func candidateHostConfigPaths(environment: [String: String]) -> [URL] {
@@ -97,9 +97,12 @@ public enum CopilotGitHubCLITokenReader {
97
97
 
98
98
  for home in homes {
99
99
  let homeURL = URL(fileURLWithPath: home)
100
- addCandidate(homeURL.appendingPathComponent(".config").appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
101
- addCandidate(homeURL.appendingPathComponent("Library").appendingPathComponent("Application Support").appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
102
- addCandidate(homeURL.appendingPathComponent(".local").appendingPathComponent("share").appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
100
+ addCandidate(homeURL.appendingPathComponent(".config").appendingPathComponent("gh")
101
+ .appendingPathComponent("hosts.yml"))
102
+ addCandidate(homeURL.appendingPathComponent("Library").appendingPathComponent("Application Support")
103
+ .appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
104
+ addCandidate(homeURL.appendingPathComponent(".local").appendingPathComponent("share")
105
+ .appendingPathComponent("gh").appendingPathComponent("hosts.yml"))
103
106
  }
104
107
 
105
108
  return candidates
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -85,11 +85,11 @@ struct CopilotAPIFetchStrategy: ProviderFetchStrategy {
85
85
  private static func sourceLabel(from source: ProviderTokenSource, sourceKey: String?) -> String {
86
86
  switch source {
87
87
  case .keychain:
88
- return sourceKey ?? "keychain"
88
+ sourceKey ?? "keychain"
89
89
  case .environment:
90
- return sourceKey ?? "env"
90
+ sourceKey ?? "env"
91
91
  case .vscode:
92
- return sourceKey ?? "vscode"
92
+ sourceKey ?? "vscode"
93
93
  }
94
94
  }
95
95
 
@@ -158,7 +158,8 @@ public struct CopilotUsageFetcher: Sendable {
158
158
  guard httpResponse.statusCode == 200 else {
159
159
  let body = self.errorSnippet(from: data)
160
160
  if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
161
- throw CopilotUsageFetchError.unauthorized(details: "token exchange HTTP \(httpResponse.statusCode). \(body)")
161
+ throw CopilotUsageFetchError
162
+ .unauthorized(details: "token exchange HTTP \(httpResponse.statusCode). \(body)")
162
163
  }
163
164
  throw CopilotUsageFetchError.invalidResponse(
164
165
  details: "token exchange HTTP \(httpResponse.statusCode). \(body)")
@@ -253,7 +254,9 @@ public struct CopilotUsageFetcher: Sendable {
253
254
  .replacingOccurrences(of: "-", with: " ")
254
255
  }
255
256
 
256
- private func orderedQuotaSnapshots(_ snapshots: CopilotUsageResponse.QuotaSnapshots?) -> [CopilotUsageResponse.QuotaSnapshot] {
257
+ private func orderedQuotaSnapshots(_ snapshots: CopilotUsageResponse
258
+ .QuotaSnapshots?) -> [CopilotUsageResponse.QuotaSnapshot]
259
+ {
257
260
  guard let snapshots else { return [] }
258
261
  let candidates = [snapshots.premiumInteractions, snapshots.completions, snapshots.chat]
259
262
  var ordered: [CopilotUsageResponse.QuotaSnapshot] = []
@@ -287,5 +290,4 @@ public struct CopilotUsageFetcher: Sendable {
287
290
  let end = text.index(text.startIndex, offsetBy: 220)
288
291
  return "\(text[..<end])…"
289
292
  }
290
-
291
293
  }
@@ -10,7 +10,7 @@ import CryptoKit
10
10
  #endif
11
11
 
12
12
  public enum CopilotVSCodeTokenReader {
13
- private struct VSCodeStoreCandidate: Sendable {
13
+ private struct VSCodeStoreCandidate {
14
14
  let databaseURL: URL
15
15
  let safeStorageService: String
16
16
  }
@@ -36,9 +36,9 @@ public enum CopilotVSCodeTokenReader {
36
36
  guard let password = self.readSafeStoragePassword(
37
37
  service: candidate.safeStorageService,
38
38
  allowUserInteraction: allowUserInteraction),
39
- let legacyKey = self.deriveLegacyKey(from: password),
40
- let decryptedJSON = self.decryptSecretPayload(encryptedPayload, legacyKey: legacyKey),
41
- let token = self.extractToken(fromDecryptedJSON: decryptedJSON)
39
+ let legacyKey = self.deriveLegacyKey(from: password),
40
+ let decryptedJSON = self.decryptSecretPayload(encryptedPayload, legacyKey: legacyKey),
41
+ let token = self.extractToken(fromDecryptedJSON: decryptedJSON)
42
42
  else {
43
43
  continue
44
44
  }
@@ -217,14 +217,13 @@ public enum CopilotVSCodeTokenReader {
217
217
  }
218
218
 
219
219
  private static func decryptSecretPayload(_ payload: Data, legacyKey: Data) -> String? {
220
- let encryptedPayload: Data
221
- if payload.count >= 3,
222
- let prefix = String(data: payload.prefix(3), encoding: .utf8),
223
- prefix == "v10" || prefix == "v11"
220
+ let encryptedPayload: Data = if payload.count >= 3,
221
+ let prefix = String(data: payload.prefix(3), encoding: .utf8),
222
+ prefix == "v10" || prefix == "v11"
224
223
  {
225
- encryptedPayload = Data(payload.dropFirst(3))
224
+ Data(payload.dropFirst(3))
226
225
  } else {
227
- encryptedPayload = payload
226
+ payload
228
227
  }
229
228
 
230
229
  let iv = Data(repeating: 0x20, count: kCCBlockSizeAES128)
@@ -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 CursorStatusFetchStrategy: ProviderFetchStrategy {
49
49
  let id: String = "cursor.web"
50
50
  let kind: ProviderFetchKind = .web
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 = CursorStatusProbe()
@@ -375,6 +375,7 @@ public actor CursorSessionStore {
375
375
  return
376
376
  }
377
377
  try? data.write(to: self.fileURL)
378
+ try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: self.fileURL.path)
378
379
  }
379
380
 
380
381
  private func loadFromDisk() {
@@ -1,5 +1,5 @@
1
- import RunicMacroSupport
2
1
  import Foundation
2
+ import RunicMacroSupport
3
3
 
4
4
  @ProviderDescriptorRegistration
5
5
  @ProviderDescriptorDefinition
@@ -72,7 +72,7 @@ struct DeepSeekAPIFetchStrategy: ProviderFetchStrategy {
72
72
  }
73
73
  }
74
74
 
75
- enum DeepSeekSettingsError: LocalizedError, Sendable {
75
+ enum DeepSeekSettingsError: LocalizedError {
76
76
  case missingToken
77
77
 
78
78
  var errorDescription: String? {
@@ -25,7 +25,7 @@ struct DeepSeekBalanceResponse: Decodable {
25
25
  }
26
26
  }
27
27
 
28
- struct DeepSeekUsageFetcher {
28
+ enum DeepSeekUsageFetcher {
29
29
  static let apiURL = URL(string: "https://api.deepseek.com/user/balance")!
30
30
 
31
31
  static func fetchBalance(apiKey: String) async throws -> DeepSeekBalanceResponse {
@@ -90,7 +90,7 @@ extension DeepSeekBalanceResponse {
90
90
  }
91
91
  }
92
92
 
93
- enum DeepSeekAPIError: LocalizedError, Sendable {
93
+ enum DeepSeekAPIError: LocalizedError {
94
94
  case invalidResponse
95
95
  case httpError(statusCode: Int, body: String?)
96
96
 
@@ -5,7 +5,7 @@ import SQLite3
5
5
 
6
6
  #if os(macOS)
7
7
  enum FactoryLocalStorageImporter {
8
- struct TokenInfo: Sendable {
8
+ struct TokenInfo {
9
9
  let refreshToken: String
10
10
  let accessToken: String?
11
11
  let organizationID: String?
@@ -61,12 +61,12 @@ enum FactoryLocalStorageImporter {
61
61
 
62
62
  // MARK: - Chrome local storage discovery
63
63
 
64
- private enum LocalStorageSourceKind: Sendable {
64
+ private enum LocalStorageSourceKind {
65
65
  case chromeLevelDB(URL)
66
66
  case safariSQLite(URL)
67
67
  }
68
68
 
69
- private struct LocalStorageCandidate: Sendable {
69
+ private struct LocalStorageCandidate {
70
70
  let label: String
71
71
  let kind: LocalStorageSourceKind
72
72
  }
@@ -207,7 +207,7 @@ enum FactoryLocalStorageImporter {
207
207
 
208
208
  // MARK: - Token extraction
209
209
 
210
- private struct WorkOSTokenMatch: Sendable {
210
+ private struct WorkOSTokenMatch {
211
211
  let refreshToken: String
212
212
  let accessToken: String?
213
213
  let organizationID: String?
@@ -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 FactoryStatusFetchStrategy: ProviderFetchStrategy {
49
49
  let id: String = "factory.web"
50
50
  let kind: ProviderFetchKind = .web
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 = FactoryStatusProbe()
@@ -464,6 +464,7 @@ public actor FactorySessionStore {
464
464
  return
465
465
  }
466
466
  try? data.write(to: self.fileURL)
467
+ try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: self.fileURL.path)
467
468
  }
468
469
 
469
470
  private func loadFromDisk() {
@@ -535,7 +536,7 @@ public struct FactoryStatusProbe: Sendable {
535
536
  "client_01HNM792M5G5G1A2THWPXKFMXB",
536
537
  ]
537
538
 
538
- private struct WorkOSAuthResponse: Decodable, Sendable {
539
+ private struct WorkOSAuthResponse: Decodable {
539
540
  let access_token: String
540
541
  let refresh_token: String?
541
542
  let organization_id: String?
@@ -577,7 +578,7 @@ public struct FactoryStatusProbe: Sendable {
577
578
  throw FactoryStatusProbeError.noSessionCookie
578
579
  }
579
580
 
580
- private enum FetchAttemptResult: Sendable {
581
+ private enum FetchAttemptResult {
581
582
  case success(FactoryStatusSnapshot)
582
583
  case failure(Error)
583
584
  case skipped
@@ -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 FireworksAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum FireworksSettingsError: LocalizedError, Sendable {
74
+ enum FireworksSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {
@@ -8,7 +8,7 @@ struct FireworksModelsResponse: Decodable {
8
8
  }
9
9
  }
10
10
 
11
- struct FireworksUsageFetcher {
11
+ enum FireworksUsageFetcher {
12
12
  static let apiURL = URL(string: "https://api.fireworks.ai/inference/v1/models")!
13
13
 
14
14
  static func fetchModels(apiKey: String) async throws -> FireworksModelsResponse {
@@ -57,7 +57,7 @@ extension FireworksModelsResponse {
57
57
  }
58
58
  }
59
59
 
60
- enum FireworksAPIError: LocalizedError, Sendable {
60
+ enum FireworksAPIError: 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
@@ -49,7 +49,9 @@ struct GeminiStatusFetchStrategy: ProviderFetchStrategy {
49
49
  let id: String = "gemini.api"
50
50
  let kind: ProviderFetchKind = .apiToken
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 = GeminiStatusProbe()
@@ -21,8 +21,11 @@ public struct GeminiStatusSnapshot: Sendable {
21
21
  self.modelQuotas.min(by: { $0.percentLeft < $1.percentLeft })?.percentLeft
22
22
  }
23
23
 
24
- // Legacy compatibility
25
- public var dailyPercentLeft: Double? { self.lowestPercentLeft }
24
+ /// Legacy compatibility
25
+ public var dailyPercentLeft: Double? {
26
+ self.lowestPercentLeft
27
+ }
28
+
26
29
  public var resetDescription: String? {
27
30
  self.modelQuotas.min(by: { $0.percentLeft < $1.percentLeft })?.resetDescription
28
31
  }
@@ -619,6 +622,7 @@ public struct GeminiStatusProbe: Sendable {
619
622
 
620
623
  let updatedData = try JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted])
621
624
  try updatedData.write(to: credsURL, options: .atomic)
625
+ try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: credsURL.path)
622
626
  }
623
627
 
624
628
  private static func loadCredentials(homeDirectory: String) throws -> OAuthCredentials {
@@ -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 GroqAPIFetchStrategy: ProviderFetchStrategy {
71
71
  }
72
72
  }
73
73
 
74
- enum GroqSettingsError: LocalizedError, Sendable {
74
+ enum GroqSettingsError: LocalizedError {
75
75
  case missingToken
76
76
 
77
77
  var errorDescription: String? {