@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
@@ -33,7 +33,7 @@ public actor GenericProviderFetcher {
33
33
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
34
34
 
35
35
  // Add auth headers
36
- try await addAuthHeaders(to: &request)
36
+ try await self.addAuthHeaders(to: &request)
37
37
 
38
38
  // 3. Make request
39
39
  let (data, response) = try await URLSession.shared.data(for: request)
@@ -52,7 +52,7 @@ public actor GenericProviderFetcher {
52
52
  }
53
53
 
54
54
  // 5. Extract fields using mapping
55
- return try extractUsageData(from: json, mapping: endpoint.mapping)
55
+ return try self.extractUsageData(from: json, mapping: endpoint.mapping)
56
56
  }
57
57
 
58
58
  /// Fetch balance data from the custom provider
@@ -70,7 +70,7 @@ public actor GenericProviderFetcher {
70
70
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
71
71
 
72
72
  // Add auth headers
73
- try await addAuthHeaders(to: &request)
73
+ try await self.addAuthHeaders(to: &request)
74
74
 
75
75
  // 3. Make request
76
76
  let (data, response) = try await URLSession.shared.data(for: request)
@@ -89,34 +89,31 @@ public actor GenericProviderFetcher {
89
89
  }
90
90
 
91
91
  // 5. Extract fields using mapping
92
- return try extractBalanceData(from: json, mapping: endpoint.mapping)
92
+ return try self.extractBalanceData(from: json, mapping: endpoint.mapping)
93
93
  }
94
94
 
95
95
  /// Convert usage data to Runic's UsageSnapshot format
96
96
  public func toUsageSnapshot(_ data: UsageData) -> UsageSnapshot {
97
- let usedPercent: Double
98
- if let quota = data.quota, let used = data.used, quota > 0 {
99
- usedPercent = (used / quota) * 100.0
97
+ let usedPercent: Double = if let quota = data.quota, let used = data.used, quota > 0 {
98
+ (used / quota) * 100.0
100
99
  } else if let remaining = data.remaining, let quota = data.quota, quota > 0 {
101
- usedPercent = ((quota - remaining) / quota) * 100.0
100
+ ((quota - remaining) / quota) * 100.0
102
101
  } else {
103
- usedPercent = 0
102
+ 0
104
103
  }
105
104
 
106
105
  let primary = RateWindow(
107
106
  usedPercent: max(0, min(100, usedPercent)),
108
107
  windowMinutes: nil,
109
108
  resetsAt: data.resetDate,
110
- resetDescription: data.resetDate.map { resetDescription(from: $0) }
111
- )
109
+ resetDescription: data.resetDate.map { self.resetDescription(from: $0) })
112
110
 
113
111
  return UsageSnapshot(
114
112
  primary: primary,
115
113
  secondary: nil,
116
114
  tertiary: nil,
117
115
  updatedAt: Date(),
118
- identity: nil
119
- )
116
+ identity: nil)
120
117
  }
121
118
 
122
119
  // MARK: - URL Building
@@ -126,7 +123,7 @@ public actor GenericProviderFetcher {
126
123
  var urlString = template
127
124
 
128
125
  // Replace date variables
129
- urlString = try substituteDateVariables(in: urlString)
126
+ urlString = try self.substituteDateVariables(in: urlString)
130
127
 
131
128
  // Build URL components
132
129
  guard var components = URLComponents(string: urlString) else {
@@ -134,7 +131,7 @@ public actor GenericProviderFetcher {
134
131
  }
135
132
 
136
133
  // Add query parameters
137
- if let params = params, !params.isEmpty {
134
+ if let params, !params.isEmpty {
138
135
  var queryItems = components.queryItems ?? []
139
136
  queryItems.append(contentsOf: params.map { URLQueryItem(name: $0.key, value: $0.value) })
140
137
  components.queryItems = queryItems
@@ -144,6 +141,10 @@ public actor GenericProviderFetcher {
144
141
  throw FetchError.invalidURL(urlString)
145
142
  }
146
143
 
144
+ guard url.scheme?.lowercased() == "https" else {
145
+ throw FetchError.insecureURL(urlString)
146
+ }
147
+
147
148
  return url
148
149
  }
149
150
 
@@ -201,10 +202,10 @@ public actor GenericProviderFetcher {
201
202
  let token = try await loadToken()
202
203
 
203
204
  // Add auth header based on type
204
- switch config.auth.type {
205
+ switch self.config.auth.type {
205
206
  case .apiKey:
206
207
  let value = (config.auth.headerPrefix ?? "") + token
207
- request.setValue(value, forHTTPHeaderField: config.auth.headerName)
208
+ request.setValue(value, forHTTPHeaderField: self.config.auth.headerName)
208
209
 
209
210
  case .bearer:
210
211
  request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
@@ -220,11 +221,11 @@ public actor GenericProviderFetcher {
220
221
  case .custom:
221
222
  // Custom auth uses the exact header name and prefix from config
222
223
  let value = (config.auth.headerPrefix ?? "") + token
223
- request.setValue(value, forHTTPHeaderField: config.auth.headerName)
224
+ request.setValue(value, forHTTPHeaderField: self.config.auth.headerName)
224
225
  }
225
226
 
226
227
  // Add additional headers
227
- config.auth.additionalHeaders?.forEach { key, value in
228
+ self.config.auth.additionalHeaders?.forEach { key, value in
228
229
  request.setValue(value, forHTTPHeaderField: key)
229
230
  }
230
231
  }
@@ -237,12 +238,12 @@ public actor GenericProviderFetcher {
237
238
  }
238
239
 
239
240
  // Try environment variable
240
- let envKey = config.auth.tokenKeychain.uppercased().replacingOccurrences(of: "-", with: "_")
241
+ let envKey = self.config.auth.tokenKeychain.uppercased().replacingOccurrences(of: "-", with: "_")
241
242
  if let token = ProcessInfo.processInfo.environment[envKey] {
242
243
  return token.trimmingCharacters(in: .whitespacesAndNewlines)
243
244
  }
244
245
 
245
- throw FetchError.missingToken(config.auth.tokenKeychain)
246
+ throw FetchError.missingToken(self.config.auth.tokenKeychain)
246
247
  }
247
248
 
248
249
  /// Read token from keychain
@@ -251,18 +252,18 @@ public actor GenericProviderFetcher {
251
252
  var result: CFTypeRef?
252
253
  var query: [String: Any] = [
253
254
  kSecClass as String: kSecClassGenericPassword,
254
- kSecAttrService as String: keychainService,
255
+ kSecAttrService as String: self.keychainService,
255
256
  kSecAttrAccount as String: account,
256
257
  kSecUseDataProtectionKeychain as String: true,
257
258
  kSecUseAuthenticationUI as String: "kSecUseAuthenticationUIFail" as CFString,
258
259
  kSecMatchLimit as String: kSecMatchLimitOne,
259
260
  kSecReturnData as String: true,
260
261
  ]
261
- #if canImport(LocalAuthentication)
262
+ #if canImport(LocalAuthentication)
262
263
  let authContext = LAContext()
263
264
  authContext.interactionNotAllowed = true
264
265
  query[kSecUseAuthenticationContext as String] = authContext
265
- #endif
266
+ #endif
266
267
 
267
268
  let status = SecItemCopyMatching(query as CFDictionary, &result)
268
269
  if status == errSecItemNotFound {
@@ -272,7 +273,7 @@ public actor GenericProviderFetcher {
272
273
  return nil
273
274
  }
274
275
  guard status == errSecSuccess else {
275
- log.error("Keychain read failed for \(account): \(status)")
276
+ self.log.error("Keychain read failed for \(account): \(status)")
276
277
  return nil
277
278
  }
278
279
 
@@ -299,32 +300,32 @@ public actor GenericProviderFetcher {
299
300
 
300
301
  // Extract quota
301
302
  if let quotaPath = mapping.quota {
302
- data.quota = extractDouble(from: json, path: quotaPath, nested: mapping.nestedPaths)
303
+ data.quota = self.extractDouble(from: json, path: quotaPath, nested: mapping.nestedPaths)
303
304
  }
304
305
 
305
306
  // Extract used amount
306
307
  if let usedPath = mapping.used {
307
- data.used = extractDouble(from: json, path: usedPath, nested: mapping.nestedPaths)
308
+ data.used = self.extractDouble(from: json, path: usedPath, nested: mapping.nestedPaths)
308
309
  }
309
310
 
310
311
  // Extract remaining amount
311
312
  if let remainingPath = mapping.remaining {
312
- data.remaining = extractDouble(from: json, path: remainingPath, nested: mapping.nestedPaths)
313
+ data.remaining = self.extractDouble(from: json, path: remainingPath, nested: mapping.nestedPaths)
313
314
  }
314
315
 
315
316
  // Extract cost
316
317
  if let costPath = mapping.cost {
317
- data.cost = extractDouble(from: json, path: costPath, nested: mapping.nestedPaths)
318
+ data.cost = self.extractDouble(from: json, path: costPath, nested: mapping.nestedPaths)
318
319
  }
319
320
 
320
321
  // Extract reset date
321
322
  if let resetPath = mapping.resetDate {
322
- data.resetDate = extractDate(from: json, path: resetPath, nested: mapping.nestedPaths)
323
+ data.resetDate = self.extractDate(from: json, path: resetPath, nested: mapping.nestedPaths)
323
324
  }
324
325
 
325
326
  // Extract tokens
326
327
  if let tokensPath = mapping.tokens {
327
- data.tokens = extractInt(from: json, path: tokensPath, nested: mapping.nestedPaths)
328
+ data.tokens = self.extractInt(from: json, path: tokensPath, nested: mapping.nestedPaths)
328
329
  }
329
330
 
330
331
  return data
@@ -336,11 +337,11 @@ public actor GenericProviderFetcher {
336
337
 
337
338
  // Extract balance (use 'remaining' or 'used' mapping)
338
339
  if let balancePath = mapping.remaining ?? mapping.used {
339
- data.balance = extractDouble(from: json, path: balancePath, nested: mapping.nestedPaths)
340
+ data.balance = self.extractDouble(from: json, path: balancePath, nested: mapping.nestedPaths)
340
341
  }
341
342
 
342
343
  // Try to detect currency from response
343
- data.currency = extractString(from: json, path: "currency", nested: true) ?? "USD"
344
+ data.currency = self.extractString(from: json, path: "currency", nested: true) ?? "USD"
344
345
 
345
346
  return data
346
347
  }
@@ -359,14 +360,16 @@ public actor GenericProviderFetcher {
359
360
  for component in components {
360
361
  // Handle array indexing: field[0]
361
362
  if let bracketIndex = component.firstIndex(of: "["),
362
- let closeBracket = component.firstIndex(of: "]") {
363
+ let closeBracket = component.firstIndex(of: "]")
364
+ {
363
365
  let fieldName = String(component[..<bracketIndex])
364
366
  let indexStr = String(component[component.index(after: bracketIndex)..<closeBracket])
365
367
 
366
368
  guard let dict = current as? [String: Any],
367
369
  let array = dict[fieldName] as? [Any],
368
370
  let index = Int(indexStr),
369
- index < array.count else {
371
+ index < array.count
372
+ else {
370
373
  return nil
371
374
  }
372
375
  current = array[index]
@@ -516,8 +519,8 @@ public struct UsageData: Sendable {
516
519
  remaining: Double? = nil,
517
520
  cost: Double? = nil,
518
521
  resetDate: Date? = nil,
519
- tokens: Int? = nil
520
- ) {
522
+ tokens: Int? = nil)
523
+ {
521
524
  self.quota = quota
522
525
  self.used = used
523
526
  self.remaining = remaining
@@ -527,14 +530,13 @@ public struct UsageData: Sendable {
527
530
  }
528
531
 
529
532
  public func toCustomUsageData() -> CustomUsageData {
530
- return CustomUsageData(
533
+ CustomUsageData(
531
534
  quota: self.quota,
532
535
  used: self.used,
533
536
  remaining: self.remaining,
534
537
  cost: self.cost,
535
538
  resetDate: self.resetDate,
536
- tokens: self.tokens
537
- )
539
+ tokens: self.tokens)
538
540
  }
539
541
  }
540
542
 
@@ -562,61 +564,68 @@ public enum FetchError: LocalizedError, Sendable {
562
564
  case missingToken(String)
563
565
  case invalidDateRange
564
566
  case extractionFailed(String)
567
+ case insecureURL(String)
565
568
 
566
569
  public var errorDescription: String? {
567
570
  switch self {
568
571
  case .noUsageEndpoint:
569
- return "No usage endpoint configured for this provider"
572
+ "No usage endpoint configured for this provider"
570
573
  case .noBalanceEndpoint:
571
- return "No balance endpoint configured for this provider"
572
- case .invalidURL(let url):
573
- return "Invalid URL: \(url)"
574
+ "No balance endpoint configured for this provider"
575
+ case let .invalidURL(url):
576
+ "Invalid URL: \(url)"
574
577
  case .invalidResponse:
575
- return "Invalid response from provider API"
576
- case .httpError(let code):
577
- return "HTTP error \(code) from provider API"
578
+ "Invalid response from provider API"
579
+ case let .httpError(code):
580
+ "HTTP error \(code) from provider API"
578
581
  case .invalidJSON:
579
- return "Failed to parse JSON response from provider"
580
- case .missingToken(let account):
581
- return "No API token found for '\(account)'. Store it in Keychain or set as environment variable."
582
+ "Failed to parse JSON response from provider"
583
+ case let .missingToken(account):
584
+ "No API token found for '\(account)'. Store it in Keychain or set as environment variable."
582
585
  case .invalidDateRange:
583
- return "Failed to calculate date range for URL template"
584
- case .extractionFailed(let field):
585
- return "Failed to extract field '\(field)' from response"
586
+ "Failed to calculate date range for URL template"
587
+ case let .extractionFailed(field):
588
+ "Failed to extract field '\(field)' from response"
589
+ case let .insecureURL(url):
590
+ "Only HTTPS URLs are allowed. Got: \(url)"
586
591
  }
587
592
  }
588
593
 
589
594
  public var failureReason: String? {
590
595
  switch self {
591
- case .missingToken(let account):
592
- return "The token for account '\(account)' is not configured in Keychain or environment variables"
593
- case .httpError(let code):
594
- return "The server returned HTTP status code \(code)"
596
+ case let .missingToken(account):
597
+ "The token for account '\(account)' is not configured in Keychain or environment variables"
598
+ case let .httpError(code):
599
+ "The server returned HTTP status code \(code)"
595
600
  case .invalidJSON:
596
- return "The response is not valid JSON or has unexpected structure"
597
- case .extractionFailed(let field):
598
- return "The field '\(field)' was not found in the response or has wrong type"
601
+ "The response is not valid JSON or has unexpected structure"
602
+ case let .extractionFailed(field):
603
+ "The field '\(field)' was not found in the response or has wrong type"
604
+ case .insecureURL:
605
+ "The URL scheme is not HTTPS"
599
606
  default:
600
- return nil
607
+ nil
601
608
  }
602
609
  }
603
610
 
604
611
  public var recoverySuggestion: String? {
605
612
  switch self {
606
- case .missingToken(let account):
607
- return "Store your API token in Keychain with account name '\(account)' or set it as environment variable"
613
+ case let .missingToken(account):
614
+ "Store your API token in Keychain with account name '\(account)' or set it as environment variable"
608
615
  case .noUsageEndpoint:
609
- return "Configure a usage endpoint in the provider settings"
616
+ "Configure a usage endpoint in the provider settings"
610
617
  case .noBalanceEndpoint:
611
- return "Configure a balance endpoint in the provider settings"
612
- case .httpError(let code) where code == 401:
613
- return "Check that your API token is valid and has not expired"
614
- case .httpError(let code) where code == 429:
615
- return "You have been rate limited. Try again later."
618
+ "Configure a balance endpoint in the provider settings"
619
+ case let .httpError(code) where code == 401:
620
+ "Check that your API token is valid and has not expired"
621
+ case let .httpError(code) where code == 429:
622
+ "You have been rate limited. Try again later."
616
623
  case .invalidJSON:
617
- return "Check the API endpoint URL and response mapping configuration"
624
+ "Check the API endpoint URL and response mapping configuration"
625
+ case .insecureURL:
626
+ "Change the provider URL to use HTTPS (e.g., https://api.example.com)"
618
627
  default:
619
- return nil
628
+ nil
620
629
  }
621
630
  }
622
631
  }