@sriinnu/runic 1.2.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 (342) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +8 -1
  3. package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
  4. package/RuniciOS/Views/ProviderDetailView.swift +9 -9
  5. package/RuniciOS/Views/ProviderListView.swift +10 -10
  6. package/RuniciOS/Widgets/LargeWidget.swift +14 -14
  7. package/RuniciOS/Widgets/LiveActivity.swift +19 -19
  8. package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
  9. package/RuniciOS/Widgets/MediumWidget.swift +10 -10
  10. package/RuniciOS/Widgets/RunicWidget.swift +5 -5
  11. package/RuniciOS/Widgets/SmallWidget.swift +9 -9
  12. package/Sources/Runic/About.swift +3 -3
  13. package/Sources/Runic/AppNotifications.swift +5 -3
  14. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
  15. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
  16. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
  17. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
  18. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
  19. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
  20. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
  21. package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
  22. package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
  23. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
  24. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
  25. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
  26. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
  27. package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
  28. package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
  29. package/Sources/Runic/Core/RunicColors.swift +6 -6
  30. package/Sources/Runic/Core/RunicSpacing.swift +7 -7
  31. package/Sources/Runic/Core/RunicTypography.swift +206 -0
  32. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
  33. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
  34. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
  35. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
  36. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
  37. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
  38. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
  39. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
  40. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
  41. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
  42. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
  43. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
  44. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
  45. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
  46. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
  47. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
  48. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
  49. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
  50. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
  51. package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
  52. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
  53. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
  54. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
  55. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
  56. package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
  57. package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
  58. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
  59. package/Sources/Runic/Core/UsageExporter.swift +8 -4
  60. package/Sources/Runic/ProviderRegistry.swift +1 -1
  61. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
  62. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
  63. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
  64. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
  65. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
  66. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
  67. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
  68. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
  69. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
  70. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
  71. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
  72. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
  73. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
  74. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
  75. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
  76. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
  77. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
  78. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
  79. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
  80. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
  81. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
  82. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
  83. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
  84. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
  85. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
  86. package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
  87. package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
  88. package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
  89. package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
  90. package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
  91. package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
  92. package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
  93. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
  94. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
  95. package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
  96. package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
  97. package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
  98. package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
  99. package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
  100. package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
  101. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
  102. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
  103. package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
  104. package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
  105. package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
  106. package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
  107. package/Sources/Runic/RunicApp.swift +46 -36
  108. package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
  109. package/Sources/Runic/UsagePaceText.swift +1 -1
  110. package/Sources/Runic/UsageProgressBar.swift +13 -9
  111. package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
  112. package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
  113. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
  114. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
  115. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
  116. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
  117. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
  118. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
  119. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
  120. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
  121. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
  122. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
  123. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
  124. package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
  125. package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
  126. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
  127. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
  128. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
  129. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
  130. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
  131. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
  132. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
  133. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
  134. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
  135. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
  136. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
  137. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
  138. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
  139. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
  140. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
  141. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
  142. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
  143. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
  144. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
  145. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
  146. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
  147. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
  148. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
  149. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
  150. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
  151. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
  152. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
  153. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
  154. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
  155. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
  156. package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
  157. package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
  158. package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
  159. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
  160. package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
  161. package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
  162. package/Sources/RunicCLI/CLIEntry.swift +83 -42
  163. package/Sources/RunicCLI/CLIRenderer.swift +1 -1
  164. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
  165. package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
  166. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
  167. package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
  168. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
  169. package/Sources/RunicClaudeWebProbe/main.swift +1 -1
  170. package/Sources/RunicCore/AlertRuleStore.swift +20 -20
  171. package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
  172. package/Sources/RunicCore/CostUsageModels.swift +1 -1
  173. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
  174. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
  175. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
  176. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
  177. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
  178. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
  179. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
  180. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
  181. package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
  182. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
  183. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
  184. package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
  185. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
  186. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
  187. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
  188. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
  189. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
  190. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
  191. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
  192. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
  193. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
  194. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
  195. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
  196. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
  197. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
  198. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
  199. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
  200. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
  201. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
  202. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
  203. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
  204. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
  205. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
  206. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
  207. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
  208. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
  209. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
  210. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
  211. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
  212. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
  213. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
  214. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
  215. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
  216. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
  217. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
  218. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
  219. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
  220. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
  221. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
  222. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
  223. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
  224. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
  225. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
  226. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
  227. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
  228. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
  229. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
  230. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
  231. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
  232. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
  233. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
  234. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
  235. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
  236. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
  237. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
  238. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
  239. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
  240. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
  241. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
  242. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
  243. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
  244. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
  245. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
  246. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
  247. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
  248. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
  249. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
  250. package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
  251. package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
  252. package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
  253. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
  254. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
  255. package/Sources/RunicCore/Team/TeamModels.swift +111 -110
  256. package/Sources/RunicCore/Team/TeamStore.swift +126 -109
  257. package/Sources/RunicCore/UsageFetcher.swift +2 -2
  258. package/Sources/RunicCore/UsageFormatter.swift +25 -25
  259. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
  260. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
  261. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
  262. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
  263. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
  264. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
  265. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
  266. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
  267. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
  268. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
  269. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
  270. package/Tests/RunicTests/AppDelegateTests.swift +8 -3
  271. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
  272. package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
  273. package/Tests/RunicTests/CLICostTests.swift +6 -7
  274. package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
  275. package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
  276. package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
  277. package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
  278. package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
  279. package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
  280. package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
  281. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
  282. package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
  283. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
  284. package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
  285. package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
  286. package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
  287. package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
  288. package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
  289. package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
  290. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
  291. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
  292. package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
  293. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
  294. package/Tests/RunicTests/InstallOriginTests.swift +1 -2
  295. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
  296. package/Tests/RunicTests/LiveAccountTests.swift +3 -3
  297. package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
  298. package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
  299. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
  300. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
  301. package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
  302. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
  303. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
  304. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
  305. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
  306. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
  307. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
  308. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
  309. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
  310. package/Tests/RunicTests/PathBuilderTests.swift +14 -15
  311. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
  312. package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
  313. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
  314. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
  315. package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
  316. package/Tests/RunicTests/RunicTests.swift +6 -7
  317. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
  318. package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
  319. package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
  320. package/Tests/RunicTests/StatusMenuTests.swift +63 -22
  321. package/Tests/RunicTests/StatusProbeTests.swift +28 -29
  322. package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
  323. package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
  324. package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
  325. package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
  326. package/Tests/RunicTests/TextParsingTests.swift +1 -2
  327. package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
  328. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
  329. package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
  330. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
  331. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
  332. package/Tests/RunicTests/UsagePaceTests.swift +5 -6
  333. package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
  334. package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
  335. package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
  336. package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
  337. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
  338. package/appcast.xml +36 -76
  339. package/package.json +1 -1
  340. package/runic.png +0 -0
  341. package/runic.svg +15 -0
  342. package/version.env +2 -2
@@ -1,24 +1,23 @@
1
- /// ProjectsCommand.swift
2
- /// Runic CLI - Project Usage Tracking
3
- ///
4
- /// Lists projects and displays usage statistics for specific projects,
5
- /// helping you understand token consumption across different codebases.
6
- ///
7
- /// Usage:
8
- /// runic projects [--list | --stats <project-id>] [--json] [--pretty] [--no-color]
9
- ///
10
- /// Examples:
11
- /// runic projects --list # List all projects
12
- /// runic projects --stats my-project # Show stats for specific project
13
- /// runic projects --json --pretty # JSON output of all projects
1
+ // ProjectsCommand.swift
2
+ // Runic CLI - Project Usage Tracking
3
+ //
4
+ // Lists projects and displays usage statistics for specific projects,
5
+ // helping you understand token consumption across different codebases.
6
+ //
7
+ // Usage:
8
+ // runic projects [--list | --stats <project-id>] [--json] [--pretty] [--no-color]
9
+ //
10
+ // Examples:
11
+ // runic projects --list # List all projects
12
+ // runic projects --stats my-project # Show stats for specific project
13
+ // runic projects --json --pretty # JSON output of all projects
14
14
 
15
- import RunicCore
16
- import Helix
17
15
  import Foundation
16
+ import Helix
17
+ import RunicCore
18
18
 
19
19
  /// Main entry point for the projects command
20
20
  public enum ProjectsCommand {
21
-
22
21
  /// Command signature defining available options and flags
23
22
  public static var signature: CommandSignature {
24
23
  CommandSignature(
@@ -97,7 +96,7 @@ public enum ProjectsCommand {
97
96
 
98
97
  /// Execute the projects command
99
98
  public static func run(_ invocation: CommandInvocation) async {
100
- let config = parseConfiguration(invocation)
99
+ let config = self.parseConfiguration(invocation)
101
100
 
102
101
  do {
103
102
  let entries = try await loadUsageEntries(
@@ -105,16 +104,16 @@ public enum ProjectsCommand {
105
104
  maxAgeDays: config.days)
106
105
 
107
106
  guard !entries.isEmpty else {
108
- exitWithError("No project usage data found for the specified criteria.")
107
+ self.exitWithError("No project usage data found for the specified criteria.")
109
108
  }
110
109
 
111
110
  if let projectID = config.statsProjectID {
112
- await showProjectStats(projectID, entries: entries, config: config)
111
+ await self.showProjectStats(projectID, entries: entries, config: config)
113
112
  } else {
114
- await showProjectList(entries: entries, config: config)
113
+ await self.showProjectList(entries: entries, config: config)
115
114
  }
116
115
  } catch {
117
- exitWithError("Failed to load usage data: \(error.localizedDescription)")
116
+ self.exitWithError("Failed to load usage data: \(error.localizedDescription)")
118
117
  }
119
118
  }
120
119
 
@@ -143,7 +142,7 @@ public enum ProjectsCommand {
143
142
  let sortByName = invocation.parsedValues.flags.contains("sortByName")
144
143
 
145
144
  let days = daysArg.flatMap(Int.init) ?? 30
146
- let providers = resolveProviders(providerArg)
145
+ let providers = self.resolveProviders(providerArg)
147
146
 
148
147
  return Configuration(
149
148
  providers: providers,
@@ -165,7 +164,7 @@ public enum ProjectsCommand {
165
164
  }
166
165
 
167
166
  guard let provider = UsageProvider(rawValue: arg) else {
168
- exitWithError("Unknown provider: \(arg)")
167
+ self.exitWithError("Unknown provider: \(arg)")
169
168
  }
170
169
 
171
170
  return [provider]
@@ -175,8 +174,8 @@ public enum ProjectsCommand {
175
174
 
176
175
  private static func loadUsageEntries(
177
176
  providers: [UsageProvider],
178
- maxAgeDays: Int
179
- ) async throws -> [UsageLedgerEntry] {
177
+ maxAgeDays: Int) async throws -> [UsageLedgerEntry]
178
+ {
180
179
  let now = Date()
181
180
  var allEntries: [UsageLedgerEntry] = []
182
181
 
@@ -196,33 +195,33 @@ public enum ProjectsCommand {
196
195
 
197
196
  private static func showProjectList(
198
197
  entries: [UsageLedgerEntry],
199
- config: Configuration
200
- ) async {
198
+ config: Configuration) async
199
+ {
201
200
  var summaries = UsageLedgerAggregator.projectSummaries(entries: entries)
202
201
 
203
202
  if config.sortByName {
204
- summaries.sort { (lhs, rhs) in
203
+ summaries.sort { lhs, rhs in
205
204
  (lhs.projectID ?? "unknown") < (rhs.projectID ?? "unknown")
206
205
  }
207
206
  }
208
207
 
209
208
  if config.isJSON {
210
- outputJSON(summaries, pretty: config.isPretty)
209
+ self.outputJSON(summaries, pretty: config.isPretty)
211
210
  } else {
212
- outputProjectListText(summaries, useColor: config.useColor)
211
+ self.outputProjectListText(summaries, useColor: config.useColor)
213
212
  }
214
213
  }
215
214
 
216
215
  private static func outputProjectListText(
217
216
  _ summaries: [UsageLedgerProjectSummary],
218
- useColor: Bool
219
- ) {
217
+ useColor: Bool)
218
+ {
220
219
  if summaries.isEmpty {
221
220
  print("No projects found.")
222
221
  return
223
222
  }
224
223
 
225
- printHeader("Projects (\(summaries.count) total)", useColor: useColor)
224
+ self.printHeader("Projects (\(summaries.count) total)", useColor: useColor)
226
225
  print("")
227
226
 
228
227
  // Calculate column widths
@@ -230,10 +229,15 @@ public enum ProjectsCommand {
230
229
  let projectWidth = min(max(maxProjectWidth, 20), 50)
231
230
 
232
231
  // Print table header
233
- let header = String(format: "%-\(projectWidth)s %-12s %-15s %-12s %-10s",
234
- "PROJECT ID", "PROVIDER", "TOKENS", "REQUESTS", "COST")
232
+ let header = String(
233
+ format: "%-\(projectWidth)s %-12s %-15s %-12s %-10s",
234
+ "PROJECT ID",
235
+ "PROVIDER",
236
+ "TOKENS",
237
+ "REQUESTS",
238
+ "COST")
235
239
  if useColor {
236
- print(ansi("1;37", header))
240
+ print(self.ansi("1;37", header))
237
241
  print(String(repeating: "-", count: projectWidth + 55))
238
242
  } else {
239
243
  print(header)
@@ -245,17 +249,22 @@ public enum ProjectsCommand {
245
249
  var totalCost = 0.0
246
250
 
247
251
  for summary in summaries {
248
- let projectID = truncate(summary.projectID ?? "unknown", maxWidth: projectWidth)
252
+ let projectID = self.truncate(summary.projectID ?? "unknown", maxWidth: projectWidth)
249
253
  let provider = summary.provider.rawValue
250
- let tokens = formatNumber(summary.totals.totalTokens)
254
+ let tokens = self.formatNumber(summary.totals.totalTokens)
251
255
  let requests = "\(summary.entryCount)"
252
- let cost = formatCost(summary.totals.costUSD)
256
+ let cost = self.formatCost(summary.totals.costUSD)
253
257
 
254
- let line = String(format: "%-\(projectWidth)s %-12s %15s %12s %10s",
255
- projectID, provider, tokens, requests, cost)
258
+ let line = String(
259
+ format: "%-\(projectWidth)s %-12s %15s %12s %10s",
260
+ projectID,
261
+ provider,
262
+ tokens,
263
+ requests,
264
+ cost)
256
265
 
257
266
  if useColor {
258
- print(colorizeByUsage(line, tokens: summary.totals.totalTokens))
267
+ print(self.colorizeByUsage(line, tokens: summary.totals.totalTokens))
259
268
  } else {
260
269
  print(line)
261
270
  }
@@ -269,12 +278,16 @@ public enum ProjectsCommand {
269
278
 
270
279
  // Print totals
271
280
  print(String(repeating: "-", count: projectWidth + 55))
272
- let totalLine = String(format: "%-\(projectWidth)s %-12s %15s %12s %10s",
273
- "TOTAL", "", formatNumber(totalTokens), "\(totalRequests)",
274
- formatCost(totalCost > 0 ? totalCost : nil))
281
+ let totalLine = String(
282
+ format: "%-\(projectWidth)s %-12s %15s %12s %10s",
283
+ "TOTAL",
284
+ "",
285
+ formatNumber(totalTokens),
286
+ "\(totalRequests)",
287
+ formatCost(totalCost > 0 ? totalCost : nil))
275
288
 
276
289
  if useColor {
277
- print(ansi("1;36", totalLine))
290
+ print(self.ansi("1;36", totalLine))
278
291
  } else {
279
292
  print(totalLine)
280
293
  }
@@ -285,12 +298,12 @@ public enum ProjectsCommand {
285
298
  private static func showProjectStats(
286
299
  _ projectID: String,
287
300
  entries: [UsageLedgerEntry],
288
- config: Configuration
289
- ) async {
301
+ config: Configuration) async
302
+ {
290
303
  let projectEntries = entries.filter { $0.projectID == projectID }
291
304
 
292
305
  guard !projectEntries.isEmpty else {
293
- exitWithError("No usage data found for project: \(projectID)")
306
+ self.exitWithError("No usage data found for project: \(projectID)")
294
307
  }
295
308
 
296
309
  let summaries = UsageLedgerAggregator.projectSummaries(entries: projectEntries)
@@ -307,9 +320,9 @@ public enum ProjectsCommand {
307
320
  dailySummaries: dailySummaries)
308
321
 
309
322
  if config.isJSON {
310
- outputJSON(stats, pretty: config.isPretty)
323
+ self.outputJSON(stats, pretty: config.isPretty)
311
324
  } else {
312
- outputProjectStatsText(stats, useColor: config.useColor)
325
+ self.outputProjectStatsText(stats, useColor: config.useColor)
313
326
  }
314
327
  }
315
328
 
@@ -321,7 +334,7 @@ public enum ProjectsCommand {
321
334
  }
322
335
 
323
336
  private static func outputProjectStatsText(_ stats: ProjectStats, useColor: Bool) {
324
- printHeader("Project: \(stats.projectID)", useColor: useColor)
337
+ self.printHeader("Project: \(stats.projectID)", useColor: useColor)
325
338
  print("")
326
339
 
327
340
  // Overall stats
@@ -330,17 +343,17 @@ public enum ProjectsCommand {
330
343
  let totalCost = stats.summaries.reduce(0.0) { $0 + ($1.totals.costUSD ?? 0) }
331
344
 
332
345
  print("Overall Statistics:")
333
- print(" Total Tokens: \(formatNumber(totalTokens))")
346
+ print(" Total Tokens: \(self.formatNumber(totalTokens))")
334
347
  print(" Total Requests: \(totalRequests)")
335
- print(" Total Cost: \(formatCost(totalCost > 0 ? totalCost : nil))")
348
+ print(" Total Cost: \(self.formatCost(totalCost > 0 ? totalCost : nil))")
336
349
  print("")
337
350
 
338
351
  // Models used
339
352
  if !stats.modelSummaries.isEmpty {
340
353
  print("Models Used:")
341
354
  for model in stats.modelSummaries.prefix(10) {
342
- let tokens = formatNumber(model.totals.totalTokens)
343
- let cost = formatCost(model.totals.costUSD)
355
+ let tokens = self.formatNumber(model.totals.totalTokens)
356
+ let cost = self.formatCost(model.totals.costUSD)
344
357
  print(" \(model.model): \(tokens) tokens, \(cost)")
345
358
  }
346
359
  if stats.modelSummaries.count > 10 {
@@ -353,8 +366,8 @@ public enum ProjectsCommand {
353
366
  if !stats.dailySummaries.isEmpty {
354
367
  print("Recent Activity (last 10 days):")
355
368
  for daily in stats.dailySummaries.suffix(10) {
356
- let tokens = formatNumber(daily.totals.totalTokens)
357
- let cost = formatCost(daily.totals.costUSD)
369
+ let tokens = self.formatNumber(daily.totals.totalTokens)
370
+ let cost = self.formatCost(daily.totals.costUSD)
358
371
  print(" \(daily.dayKey): \(tokens) tokens, \(cost)")
359
372
  }
360
373
  print("")
@@ -363,7 +376,7 @@ public enum ProjectsCommand {
363
376
 
364
377
  // MARK: - Output Formatting
365
378
 
366
- private static func outputJSON<T: Encodable>(_ data: T, pretty: Bool) {
379
+ private static func outputJSON(_ data: some Encodable, pretty: Bool) {
367
380
  let encoder = JSONEncoder()
368
381
  encoder.dateEncodingStrategy = .iso8601
369
382
 
@@ -372,7 +385,8 @@ public enum ProjectsCommand {
372
385
  }
373
386
 
374
387
  guard let jsonData = try? encoder.encode(data),
375
- let text = String(data: jsonData, encoding: .utf8) else {
388
+ let text = String(data: jsonData, encoding: .utf8)
389
+ else {
376
390
  print("{}")
377
391
  return
378
392
  }
@@ -385,14 +399,14 @@ public enum ProjectsCommand {
385
399
  private static func formatNumber(_ n: Int) -> String {
386
400
  if n >= 1_000_000 {
387
401
  return String(format: "%.1fM", Double(n) / 1_000_000)
388
- } else if n >= 1_000 {
389
- return String(format: "%.1fK", Double(n) / 1_000)
402
+ } else if n >= 1000 {
403
+ return String(format: "%.1fK", Double(n) / 1000)
390
404
  }
391
405
  return "\(n)"
392
406
  }
393
407
 
394
408
  private static func formatCost(_ cost: Double?) -> String {
395
- guard let cost = cost, cost > 0 else {
409
+ guard let cost, cost > 0 else {
396
410
  return "n/a"
397
411
  }
398
412
  return String(format: "$%.4f", cost)
@@ -408,7 +422,7 @@ public enum ProjectsCommand {
408
422
 
409
423
  private static func printHeader(_ text: String, useColor: Bool) {
410
424
  if useColor {
411
- print(ansi("1;36", text))
425
+ print(self.ansi("1;36", text))
412
426
  } else {
413
427
  print(text)
414
428
  }
@@ -416,12 +430,12 @@ public enum ProjectsCommand {
416
430
 
417
431
  private static func colorizeByUsage(_ text: String, tokens: Int) -> String {
418
432
  let code = switch tokens {
419
- case 1_000_000...: "35" // magenta - very high
420
- case 500_000...: "33" // yellow - high
421
- case 100_000...: "32" // green - medium
422
- default: "0" // default
433
+ case 1_000_000...: "35" // magenta - very high
434
+ case 500_000...: "33" // yellow - high
435
+ case 100_000...: "32" // green - medium
436
+ default: "0" // default
423
437
  }
424
- return ansi(code, text)
438
+ return self.ansi(code, text)
425
439
  }
426
440
 
427
441
  private static func ansi(_ code: String, _ text: String) -> String {
@@ -1,24 +1,23 @@
1
- /// ResetCommand.swift
2
- /// Runic CLI - Usage Reset Timing Information
3
- ///
4
- /// Displays when usage limits reset for different providers,
5
- /// helping you plan usage and understand billing cycles.
6
- ///
7
- /// Usage:
8
- /// runic reset [--when <provider>] [--all] [--json] [--pretty] [--no-color]
9
- ///
10
- /// Examples:
11
- /// runic reset # Show reset times for all providers
12
- /// runic reset --when claude # Show Claude reset timings only
13
- /// runic reset --all --json # JSON output for all providers
1
+ // ResetCommand.swift
2
+ // Runic CLI - Usage Reset Timing Information
3
+ //
4
+ // Displays when usage limits reset for different providers,
5
+ // helping you plan usage and understand billing cycles.
6
+ //
7
+ // Usage:
8
+ // runic reset [--when <provider>] [--all] [--json] [--pretty] [--no-color]
9
+ //
10
+ // Examples:
11
+ // runic reset # Show reset times for all providers
12
+ // runic reset --when claude # Show Claude reset timings only
13
+ // runic reset --all --json # JSON output for all providers
14
14
 
15
- import RunicCore
16
- import Helix
17
15
  import Foundation
16
+ import Helix
17
+ import RunicCore
18
18
 
19
19
  /// Main entry point for the reset command
20
20
  public enum ResetCommand {
21
-
22
21
  /// Command signature defining available options and flags
23
22
  public static var signature: CommandSignature {
24
23
  CommandSignature(
@@ -86,22 +85,22 @@ public enum ResetCommand {
86
85
 
87
86
  /// Execute the reset command
88
87
  public static func run(_ invocation: CommandInvocation) async {
89
- let config = parseConfiguration(invocation)
88
+ let config = self.parseConfiguration(invocation)
90
89
 
91
90
  do {
92
91
  let resetInfos = try await loadResetInformation(providers: config.providers)
93
92
 
94
93
  if resetInfos.isEmpty {
95
- exitWithError("No reset information available for the specified providers.")
94
+ self.exitWithError("No reset information available for the specified providers.")
96
95
  }
97
96
 
98
97
  if config.isJSON {
99
- outputJSON(resetInfos, pretty: config.isPretty)
98
+ self.outputJSON(resetInfos, pretty: config.isPretty)
100
99
  } else {
101
- outputText(resetInfos, config: config)
100
+ self.outputText(resetInfos, config: config)
102
101
  }
103
102
  } catch {
104
- exitWithError("Failed to load reset information: \(error.localizedDescription)")
103
+ self.exitWithError("Failed to load reset information: \(error.localizedDescription)")
105
104
  }
106
105
  }
107
106
 
@@ -125,7 +124,7 @@ public enum ResetCommand {
125
124
  let noColor = invocation.parsedValues.flags.contains("noColor")
126
125
  let compact = invocation.parsedValues.flags.contains("compact")
127
126
 
128
- let providers = resolveProviders(whenArg)
127
+ let providers = self.resolveProviders(whenArg)
129
128
 
130
129
  return Configuration(
131
130
  providers: providers,
@@ -145,7 +144,7 @@ public enum ResetCommand {
145
144
  }
146
145
 
147
146
  guard let provider = UsageProvider(rawValue: arg) else {
148
- exitWithError("Unknown provider: \(arg)")
147
+ self.exitWithError("Unknown provider: \(arg)")
149
148
  }
150
149
 
151
150
  return [provider]
@@ -173,8 +172,8 @@ public enum ResetCommand {
173
172
  // MARK: - Data Loading
174
173
 
175
174
  private static func loadResetInformation(
176
- providers: [UsageProvider]
177
- ) async throws -> [ProviderResetInfo] {
175
+ providers: [UsageProvider]) async throws -> [ProviderResetInfo]
176
+ {
178
177
  let fetcher = UsageFetcher()
179
178
  var results: [ProviderResetInfo] = []
180
179
 
@@ -194,8 +193,8 @@ public enum ResetCommand {
194
193
 
195
194
  let outcome = await descriptor.fetchOutcome(context: context)
196
195
 
197
- if case .success(let result) = outcome.result {
198
- let info = buildResetInfo(
196
+ if case let .success(result) = outcome.result {
197
+ let info = self.buildResetInfo(
199
198
  provider: provider,
200
199
  snapshot: result.usage,
201
200
  metadata: descriptor.metadata)
@@ -209,30 +208,30 @@ public enum ResetCommand {
209
208
  private static func buildResetInfo(
210
209
  provider: UsageProvider,
211
210
  snapshot: UsageSnapshot,
212
- metadata: ProviderMetadata
213
- ) -> ProviderResetInfo {
211
+ metadata: ProviderMetadata) -> ProviderResetInfo
212
+ {
214
213
  var windows: [WindowResetInfo] = []
215
214
 
216
215
  // Primary window
217
- windows.append(buildWindowInfo(
216
+ windows.append(self.buildWindowInfo(
218
217
  windowName: metadata.sessionLabel,
219
218
  window: snapshot.primary))
220
219
 
221
220
  // Secondary window
222
221
  if let secondary = snapshot.secondary {
223
- windows.append(buildWindowInfo(
222
+ windows.append(self.buildWindowInfo(
224
223
  windowName: metadata.weeklyLabel,
225
224
  window: secondary))
226
225
  }
227
226
 
228
227
  // Tertiary window (Opus)
229
228
  if metadata.supportsOpus, let tertiary = snapshot.tertiary {
230
- windows.append(buildWindowInfo(
229
+ windows.append(self.buildWindowInfo(
231
230
  windowName: metadata.opusLabel ?? "Opus",
232
231
  window: tertiary))
233
232
  }
234
233
 
235
- let status = determineStatus(windows: windows)
234
+ let status = self.determineStatus(windows: windows)
236
235
 
237
236
  return ProviderResetInfo(
238
237
  provider: provider,
@@ -243,9 +242,9 @@ public enum ResetCommand {
243
242
 
244
243
  private static func buildWindowInfo(
245
244
  windowName: String,
246
- window: RateWindow
247
- ) -> WindowResetInfo {
248
- let resetAt = extractResetDate(from: window.resetDescription)
245
+ window: RateWindow) -> WindowResetInfo
246
+ {
247
+ let resetAt = self.extractResetDate(from: window.resetDescription)
249
248
  let timeUntilReset = resetAt?.timeIntervalSinceNow
250
249
 
251
250
  return WindowResetInfo(
@@ -261,14 +260,15 @@ public enum ResetCommand {
261
260
  private static func extractResetDate(from description: String?) -> Date? {
262
261
  // This is a simplified implementation
263
262
  // Real implementation would parse the reset description
264
- guard let description = description else { return nil }
263
+ guard let description else { return nil }
265
264
 
266
265
  // Look for patterns like "in Xh Ym"
267
266
  let pattern = #"in (\d+)h(?: (\d+)m)?"#
268
267
  guard let regex = try? NSRegularExpression(pattern: pattern),
269
268
  let match = regex.firstMatch(
270
269
  in: description,
271
- range: NSRange(description.startIndex..., in: description)) else {
270
+ range: NSRange(description.startIndex..., in: description))
271
+ else {
272
272
  return nil
273
273
  }
274
274
 
@@ -313,7 +313,8 @@ public enum ResetCommand {
313
313
  }
314
314
 
315
315
  guard let data = try? encoder.encode(infos),
316
- let text = String(data: data, encoding: .utf8) else {
316
+ let text = String(data: data, encoding: .utf8)
317
+ else {
317
318
  print("[]")
318
319
  return
319
320
  }
@@ -323,18 +324,18 @@ public enum ResetCommand {
323
324
 
324
325
  private static func outputText(_ infos: [ProviderResetInfo], config: Configuration) {
325
326
  if config.compact {
326
- outputCompactText(infos, useColor: config.useColor)
327
+ self.outputCompactText(infos, useColor: config.useColor)
327
328
  } else {
328
- outputDetailedText(infos, useColor: config.useColor)
329
+ self.outputDetailedText(infos, useColor: config.useColor)
329
330
  }
330
331
  }
331
332
 
332
333
  private static func outputCompactText(_ infos: [ProviderResetInfo], useColor: Bool) {
333
- printHeader("Usage Reset Times", useColor: useColor)
334
+ self.printHeader("Usage Reset Times", useColor: useColor)
334
335
  print("")
335
336
 
336
337
  for info in infos {
337
- let status = statusIndicator(info.status, useColor: useColor)
338
+ let status = self.statusIndicator(info.status, useColor: useColor)
338
339
  let provider = info.providerName.padding(toLength: 12, withPad: " ", startingAt: 0)
339
340
 
340
341
  var resetTimes: [String] = []
@@ -350,7 +351,7 @@ public enum ResetCommand {
350
351
  }
351
352
 
352
353
  private static func outputDetailedText(_ infos: [ProviderResetInfo], useColor: Bool) {
353
- printHeader("Usage Reset Information", useColor: useColor)
354
+ self.printHeader("Usage Reset Information", useColor: useColor)
354
355
  print("")
355
356
 
356
357
  for (index, info) in infos.enumerated() {
@@ -358,11 +359,11 @@ public enum ResetCommand {
358
359
  print("")
359
360
  }
360
361
 
361
- let status = statusIndicator(info.status, useColor: useColor)
362
+ let status = self.statusIndicator(info.status, useColor: useColor)
362
363
  let providerLine = "\(status) \(info.providerName) - \(info.status)"
363
364
 
364
365
  if useColor {
365
- print(ansi("1;37", providerLine))
366
+ print(self.ansi("1;37", providerLine))
366
367
  } else {
367
368
  print(providerLine)
368
369
  }
@@ -382,7 +383,7 @@ public enum ResetCommand {
382
383
  if window.isNearLimit {
383
384
  let warning = "⚠ High usage - consider monitoring closely"
384
385
  if useColor {
385
- print(" \(ansi("33", warning))")
386
+ print(" \(self.ansi("33", warning))")
386
387
  } else {
387
388
  print(" \(warning)")
388
389
  }
@@ -403,12 +404,12 @@ public enum ResetCommand {
403
404
  ("✓", "32")
404
405
  }
405
406
 
406
- return useColor ? ansi(color, icon) : icon
407
+ return useColor ? self.ansi(color, icon) : icon
407
408
  }
408
409
 
409
410
  private static func printHeader(_ text: String, useColor: Bool) {
410
411
  if useColor {
411
- print(ansi("1;36", text))
412
+ print(self.ansi("1;36", text))
412
413
  } else {
413
414
  print(text)
414
415
  }