@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
3
  /// Storage for team management, memberships, project ownership, and invitations
4
- public struct TeamStore {
4
+ public enum TeamStore {
5
5
  // MARK: - Types
6
6
 
7
7
  public enum TeamRole: String, Codable, Sendable {
@@ -57,8 +57,8 @@ public struct TeamStore {
57
57
  quota: Int,
58
58
  usedQuota: Int = 0,
59
59
  createdAt: Date = Date(),
60
- updatedAt: Date = Date()
61
- ) {
60
+ updatedAt: Date = Date())
61
+ {
62
62
  self.id = id
63
63
  self.name = name
64
64
  self.ownerUserID = ownerUserID
@@ -87,8 +87,8 @@ public struct TeamStore {
87
87
  quota: Int? = nil,
88
88
  usedQuota: Int = 0,
89
89
  joinedAt: Date = Date(),
90
- updatedAt: Date = Date()
91
- ) {
90
+ updatedAt: Date = Date())
91
+ {
92
92
  self.id = id
93
93
  self.teamID = teamID
94
94
  self.userID = userID
@@ -101,7 +101,10 @@ public struct TeamStore {
101
101
  }
102
102
 
103
103
  public struct ProjectOwnership: Codable, Sendable, Identifiable {
104
- public var id: String { projectID }
104
+ public var id: String {
105
+ self.projectID
106
+ }
107
+
105
108
  public let projectID: String
106
109
  public var ownerUserID: String
107
110
  public var teamID: String?
@@ -117,8 +120,8 @@ public struct TeamStore {
117
120
  accessLevel: AccessLevel = .private,
118
121
  sharedWithUserIDs: [String] = [],
119
122
  createdAt: Date = Date(),
120
- updatedAt: Date = Date()
121
- ) {
123
+ updatedAt: Date = Date())
124
+ {
122
125
  self.projectID = projectID
123
126
  self.ownerUserID = ownerUserID
124
127
  self.teamID = teamID
@@ -149,8 +152,8 @@ public struct TeamStore {
149
152
  status: InvitationStatus = .pending,
150
153
  createdAt: Date = Date(),
151
154
  expiresAt: Date = Date().addingTimeInterval(7 * 24 * 60 * 60), // 7 days
152
- respondedAt: Date? = nil
153
- ) {
155
+ respondedAt: Date? = nil)
156
+ {
154
157
  self.id = id
155
158
  self.teamID = teamID
156
159
  self.email = email
@@ -163,7 +166,7 @@ public struct TeamStore {
163
166
  }
164
167
 
165
168
  public var isExpired: Bool {
166
- Date() > expiresAt && status == .pending
169
+ Date() > self.expiresAt && self.status == .pending
167
170
  }
168
171
  }
169
172
 
@@ -181,8 +184,8 @@ public struct TeamStore {
181
184
  teams: [String: Team] = [:],
182
185
  memberships: [String: TeamMembership] = [:],
183
186
  projectOwnership: [String: ProjectOwnership] = [:],
184
- invitations: [String: TeamInvitation] = [:]
185
- ) {
187
+ invitations: [String: TeamInvitation] = [:])
188
+ {
186
189
  self.version = version
187
190
  self.currentUserID = currentUserID
188
191
  self.teams = teams
@@ -212,31 +215,31 @@ public struct TeamStore {
212
215
  public var errorDescription: String? {
213
216
  switch self {
214
217
  case .teamNotFound:
215
- return "Team not found"
218
+ "Team not found"
216
219
  case .membershipNotFound:
217
- return "Membership not found"
220
+ "Membership not found"
218
221
  case .projectNotFound:
219
- return "Project not found"
222
+ "Project not found"
220
223
  case .invitationNotFound:
221
- return "Invitation not found"
224
+ "Invitation not found"
222
225
  case .invalidQuota:
223
- return "Invalid quota value"
226
+ "Invalid quota value"
224
227
  case .insufficientPermissions:
225
- return "Insufficient permissions to perform this action"
228
+ "Insufficient permissions to perform this action"
226
229
  case .duplicateMembership:
227
- return "User is already a member of this team"
230
+ "User is already a member of this team"
228
231
  case .duplicateInvitation:
229
- return "An invitation has already been sent to this email"
232
+ "An invitation has already been sent to this email"
230
233
  case .invitationExpired:
231
- return "This invitation has expired"
234
+ "This invitation has expired"
232
235
  case .ownerCannotLeave:
233
- return "Team owner cannot leave the team. Transfer ownership first."
236
+ "Team owner cannot leave the team. Transfer ownership first."
234
237
  case .invalidEmailFormat:
235
- return "Invalid email format"
238
+ "Invalid email format"
236
239
  case .teamQuotaExceeded:
237
- return "Team quota exceeded"
240
+ "Team quota exceeded"
238
241
  case .memberQuotaExceeded:
239
- return "Member quota exceeded"
242
+ "Member quota exceeded"
240
243
  }
241
244
  }
242
245
  }
@@ -253,7 +256,7 @@ public struct TeamStore {
253
256
  // MARK: - Storage Operations
254
257
 
255
258
  public static func load() -> TeamsData {
256
- guard FileManager.default.fileExists(atPath: storageURL.path) else {
259
+ guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
257
260
  return TeamsData()
258
261
  }
259
262
 
@@ -274,7 +277,7 @@ public struct TeamStore {
274
277
  encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
275
278
 
276
279
  let data = try encoder.encode(teamsData)
277
- try data.write(to: storageURL, options: .atomic)
280
+ try data.write(to: self.storageURL, options: .atomic)
278
281
  }
279
282
 
280
283
  // MARK: - Team Management
@@ -284,7 +287,7 @@ public struct TeamStore {
284
287
  throw TeamStoreError.invalidQuota
285
288
  }
286
289
 
287
- var data = load()
290
+ var data = self.load()
288
291
  let team = Team(name: name, ownerUserID: ownerUserID, quota: quota)
289
292
  data.teams[team.id] = team
290
293
 
@@ -292,20 +295,19 @@ public struct TeamStore {
292
295
  let ownerMembership = TeamMembership(
293
296
  teamID: team.id,
294
297
  userID: ownerUserID,
295
- role: .owner
296
- )
298
+ role: .owner)
297
299
  data.memberships[ownerMembership.id] = ownerMembership
298
300
 
299
- try save(data)
301
+ try self.save(data)
300
302
  return team
301
303
  }
302
304
 
303
305
  public static func getTeam(id: String) -> Team? {
304
- load().teams[id]
306
+ self.load().teams[id]
305
307
  }
306
308
 
307
309
  public static func updateTeam(_ team: Team) throws {
308
- var data = load()
310
+ var data = self.load()
309
311
  guard data.teams[team.id] != nil else {
310
312
  throw TeamStoreError.teamNotFound
311
313
  }
@@ -313,11 +315,11 @@ public struct TeamStore {
313
315
  var updatedTeam = team
314
316
  updatedTeam.updatedAt = Date()
315
317
  data.teams[team.id] = updatedTeam
316
- try save(data)
318
+ try self.save(data)
317
319
  }
318
320
 
319
321
  public static func deleteTeam(id: String) throws {
320
- var data = load()
322
+ var data = self.load()
321
323
  guard data.teams[id] != nil else {
322
324
  throw TeamStoreError.teamNotFound
323
325
  }
@@ -340,20 +342,25 @@ public struct TeamStore {
340
342
  data.projectOwnership[projectID] = updatedOwnership
341
343
  }
342
344
 
343
- try save(data)
345
+ try self.save(data)
344
346
  }
345
347
 
346
348
  public static func getUserTeams(userID: String) -> [Team] {
347
- let data = load()
349
+ let data = self.load()
348
350
  let userMemberships = data.memberships.values.filter { $0.userID == userID }
349
- let teamIDs = Set(userMemberships.map { $0.teamID })
351
+ let teamIDs = Set(userMemberships.map(\.teamID))
350
352
  return teamIDs.compactMap { data.teams[$0] }.sorted { $0.createdAt < $1.createdAt }
351
353
  }
352
354
 
353
355
  // MARK: - Membership Management
354
356
 
355
- public static func addMember(teamID: String, userID: String, role: TeamRole, quota: Int? = nil) throws -> TeamMembership {
356
- var data = load()
357
+ public static func addMember(
358
+ teamID: String,
359
+ userID: String,
360
+ role: TeamRole,
361
+ quota: Int? = nil) throws -> TeamMembership
362
+ {
363
+ var data = self.load()
357
364
 
358
365
  guard data.teams[teamID] != nil else {
359
366
  throw TeamStoreError.teamNotFound
@@ -366,7 +373,7 @@ public struct TeamStore {
366
373
  }
367
374
 
368
375
  // Validate quota if provided
369
- if let quota = quota, quota < 0 {
376
+ if let quota, quota < 0 {
370
377
  throw TeamStoreError.invalidQuota
371
378
  }
372
379
 
@@ -374,22 +381,21 @@ public struct TeamStore {
374
381
  teamID: teamID,
375
382
  userID: userID,
376
383
  role: role,
377
- quota: quota
378
- )
384
+ quota: quota)
379
385
  data.memberships[membership.id] = membership
380
386
 
381
- try save(data)
387
+ try self.save(data)
382
388
  return membership
383
389
  }
384
390
 
385
391
  public static func updateMemberRole(membershipID: String, newRole: TeamRole) throws {
386
- var data = load()
392
+ var data = self.load()
387
393
  guard var membership = data.memberships[membershipID] else {
388
394
  throw TeamStoreError.membershipNotFound
389
395
  }
390
396
 
391
397
  // Prevent changing owner role (should use transfer ownership instead)
392
- if membership.role == .owner && newRole != .owner {
398
+ if membership.role == .owner, newRole != .owner {
393
399
  throw TeamStoreError.insufficientPermissions
394
400
  }
395
401
 
@@ -397,11 +403,11 @@ public struct TeamStore {
397
403
  membership.updatedAt = Date()
398
404
  data.memberships[membershipID] = membership
399
405
 
400
- try save(data)
406
+ try self.save(data)
401
407
  }
402
408
 
403
409
  public static func updateMemberQuota(membershipID: String, newQuota: Int?) throws {
404
- var data = load()
410
+ var data = self.load()
405
411
  guard var membership = data.memberships[membershipID] else {
406
412
  throw TeamStoreError.membershipNotFound
407
413
  }
@@ -414,11 +420,11 @@ public struct TeamStore {
414
420
  membership.updatedAt = Date()
415
421
  data.memberships[membershipID] = membership
416
422
 
417
- try save(data)
423
+ try self.save(data)
418
424
  }
419
425
 
420
426
  public static func removeMember(membershipID: String) throws {
421
- var data = load()
427
+ var data = self.load()
422
428
  guard let membership = data.memberships[membershipID] else {
423
429
  throw TeamStoreError.membershipNotFound
424
430
  }
@@ -429,32 +435,36 @@ public struct TeamStore {
429
435
  }
430
436
 
431
437
  data.memberships.removeValue(forKey: membershipID)
432
- try save(data)
438
+ try self.save(data)
433
439
  }
434
440
 
435
441
  public static func getTeamMembers(teamID: String) -> [TeamMembership] {
436
- load().memberships.values
442
+ self.load().memberships.values
437
443
  .filter { $0.teamID == teamID }
438
444
  .sorted { $0.joinedAt < $1.joinedAt }
439
445
  }
440
446
 
441
447
  public static func getUserMembership(teamID: String, userID: String) -> TeamMembership? {
442
- load().memberships.values.first { $0.teamID == teamID && $0.userID == userID }
448
+ self.load().memberships.values.first { $0.teamID == teamID && $0.userID == userID }
443
449
  }
444
450
 
445
451
  public static func transferOwnership(teamID: String, newOwnerUserID: String) throws {
446
- var data = load()
452
+ var data = self.load()
447
453
  guard var team = data.teams[teamID] else {
448
454
  throw TeamStoreError.teamNotFound
449
455
  }
450
456
 
451
457
  // Get current owner membership
452
- guard let currentOwnerMembership = data.memberships.values.first(where: { $0.teamID == teamID && $0.userID == team.ownerUserID }) else {
458
+ guard let currentOwnerMembership = data.memberships.values
459
+ .first(where: { $0.teamID == teamID && $0.userID == team.ownerUserID })
460
+ else {
453
461
  throw TeamStoreError.membershipNotFound
454
462
  }
455
463
 
456
464
  // Get new owner membership
457
- guard let newOwnerMembership = data.memberships.values.first(where: { $0.teamID == teamID && $0.userID == newOwnerUserID }) else {
465
+ guard let newOwnerMembership = data.memberships.values
466
+ .first(where: { $0.teamID == teamID && $0.userID == newOwnerUserID })
467
+ else {
458
468
  throw TeamStoreError.membershipNotFound
459
469
  }
460
470
 
@@ -474,16 +484,21 @@ public struct TeamStore {
474
484
  updatedNewOwner.updatedAt = Date()
475
485
  data.memberships[newOwnerMembership.id] = updatedNewOwner
476
486
 
477
- try save(data)
487
+ try self.save(data)
478
488
  }
479
489
 
480
490
  // MARK: - Project Ownership Management
481
491
 
482
- public static func setProjectOwnership(projectID: String, ownerUserID: String, teamID: String? = nil, accessLevel: AccessLevel) throws {
483
- var data = load()
492
+ public static func setProjectOwnership(
493
+ projectID: String,
494
+ ownerUserID: String,
495
+ teamID: String? = nil,
496
+ accessLevel: AccessLevel) throws
497
+ {
498
+ var data = self.load()
484
499
 
485
500
  // Validate team exists if provided
486
- if let teamID = teamID {
501
+ if let teamID {
487
502
  guard data.teams[teamID] != nil else {
488
503
  throw TeamStoreError.teamNotFound
489
504
  }
@@ -501,16 +516,15 @@ public struct TeamStore {
501
516
  projectID: projectID,
502
517
  ownerUserID: ownerUserID,
503
518
  teamID: teamID,
504
- accessLevel: accessLevel
505
- )
519
+ accessLevel: accessLevel)
506
520
  data.projectOwnership[projectID] = ownership
507
521
  }
508
522
 
509
- try save(data)
523
+ try self.save(data)
510
524
  }
511
525
 
512
526
  public static func shareProject(projectID: String, withUserIDs: [String]) throws {
513
- var data = load()
527
+ var data = self.load()
514
528
  guard var ownership = data.projectOwnership[projectID] else {
515
529
  throw TeamStoreError.projectNotFound
516
530
  }
@@ -520,11 +534,11 @@ public struct TeamStore {
520
534
  ownership.updatedAt = Date()
521
535
  data.projectOwnership[projectID] = ownership
522
536
 
523
- try save(data)
537
+ try self.save(data)
524
538
  }
525
539
 
526
540
  public static func unshareProject(projectID: String, withUserIDs: [String]) throws {
527
- var data = load()
541
+ var data = self.load()
528
542
  guard var ownership = data.projectOwnership[projectID] else {
529
543
  throw TeamStoreError.projectNotFound
530
544
  }
@@ -533,52 +547,57 @@ public struct TeamStore {
533
547
  ownership.updatedAt = Date()
534
548
 
535
549
  // If no users are shared with, revert to private
536
- if ownership.sharedWithUserIDs.isEmpty && ownership.accessLevel == .shared {
550
+ if ownership.sharedWithUserIDs.isEmpty, ownership.accessLevel == .shared {
537
551
  ownership.accessLevel = .private
538
552
  }
539
553
 
540
554
  data.projectOwnership[projectID] = ownership
541
- try save(data)
555
+ try self.save(data)
542
556
  }
543
557
 
544
558
  public static func getProjectOwnership(projectID: String) -> ProjectOwnership? {
545
- load().projectOwnership[projectID]
559
+ self.load().projectOwnership[projectID]
546
560
  }
547
561
 
548
562
  public static func getUserProjects(userID: String) -> [ProjectOwnership] {
549
- let data = load()
563
+ let data = self.load()
550
564
  return data.projectOwnership.values
551
565
  .filter { $0.ownerUserID == userID || $0.sharedWithUserIDs.contains(userID) }
552
566
  .sorted { $0.createdAt < $1.createdAt }
553
567
  }
554
568
 
555
569
  public static func getTeamProjects(teamID: String) -> [ProjectOwnership] {
556
- load().projectOwnership.values
570
+ self.load().projectOwnership.values
557
571
  .filter { $0.teamID == teamID }
558
572
  .sorted { $0.createdAt < $1.createdAt }
559
573
  }
560
574
 
561
575
  public static func deleteProjectOwnership(projectID: String) throws {
562
- var data = load()
576
+ var data = self.load()
563
577
  guard data.projectOwnership[projectID] != nil else {
564
578
  throw TeamStoreError.projectNotFound
565
579
  }
566
580
 
567
581
  data.projectOwnership.removeValue(forKey: projectID)
568
- try save(data)
582
+ try self.save(data)
569
583
  }
570
584
 
571
585
  // MARK: - Invitation Management
572
586
 
573
- public static func createInvitation(teamID: String, email: String, role: TeamRole, invitedBy: String) throws -> TeamInvitation {
574
- var data = load()
587
+ public static func createInvitation(
588
+ teamID: String,
589
+ email: String,
590
+ role: TeamRole,
591
+ invitedBy: String) throws -> TeamInvitation
592
+ {
593
+ var data = self.load()
575
594
 
576
595
  guard data.teams[teamID] != nil else {
577
596
  throw TeamStoreError.teamNotFound
578
597
  }
579
598
 
580
599
  // Validate email format (basic validation)
581
- guard email.contains("@") && email.contains(".") else {
600
+ guard email.contains("@"), email.contains(".") else {
582
601
  throw TeamStoreError.invalidEmailFormat
583
602
  }
584
603
 
@@ -594,16 +613,15 @@ public struct TeamStore {
594
613
  teamID: teamID,
595
614
  email: email,
596
615
  role: role,
597
- invitedBy: invitedBy
598
- )
616
+ invitedBy: invitedBy)
599
617
  data.invitations[invitation.id] = invitation
600
618
 
601
- try save(data)
619
+ try self.save(data)
602
620
  return invitation
603
621
  }
604
622
 
605
623
  public static func acceptInvitation(id: String, userID: String) throws {
606
- var data = load()
624
+ var data = self.load()
607
625
  guard var invitation = data.invitations[id] else {
608
626
  throw TeamStoreError.invitationNotFound
609
627
  }
@@ -616,7 +634,7 @@ public struct TeamStore {
616
634
  invitation.status = .expired
617
635
  invitation.respondedAt = Date()
618
636
  data.invitations[id] = invitation
619
- try save(data)
637
+ try self.save(data)
620
638
  throw TeamStoreError.invitationExpired
621
639
  }
622
640
 
@@ -624,8 +642,7 @@ public struct TeamStore {
624
642
  let membership = TeamMembership(
625
643
  teamID: invitation.teamID,
626
644
  userID: userID,
627
- role: invitation.role
628
- )
645
+ role: invitation.role)
629
646
  data.memberships[membership.id] = membership
630
647
 
631
648
  // Update invitation status
@@ -633,11 +650,11 @@ public struct TeamStore {
633
650
  invitation.respondedAt = Date()
634
651
  data.invitations[id] = invitation
635
652
 
636
- try save(data)
653
+ try self.save(data)
637
654
  }
638
655
 
639
656
  public static func declineInvitation(id: String) throws {
640
- var data = load()
657
+ var data = self.load()
641
658
  guard var invitation = data.invitations[id] else {
642
659
  throw TeamStoreError.invitationNotFound
643
660
  }
@@ -646,53 +663,53 @@ public struct TeamStore {
646
663
  invitation.respondedAt = Date()
647
664
  data.invitations[id] = invitation
648
665
 
649
- try save(data)
666
+ try self.save(data)
650
667
  }
651
668
 
652
669
  public static func cancelInvitation(id: String) throws {
653
- var data = load()
670
+ var data = self.load()
654
671
  guard data.invitations[id] != nil else {
655
672
  throw TeamStoreError.invitationNotFound
656
673
  }
657
674
 
658
675
  data.invitations.removeValue(forKey: id)
659
- try save(data)
676
+ try self.save(data)
660
677
  }
661
678
 
662
679
  public static func getPendingInvitations(email: String) -> [TeamInvitation] {
663
- load().invitations.values
680
+ self.load().invitations.values
664
681
  .filter { $0.email.lowercased() == email.lowercased() && $0.status == .pending && !$0.isExpired }
665
682
  .sorted { $0.createdAt > $1.createdAt }
666
683
  }
667
684
 
668
685
  public static func getTeamInvitations(teamID: String) -> [TeamInvitation] {
669
- load().invitations.values
686
+ self.load().invitations.values
670
687
  .filter { $0.teamID == teamID }
671
688
  .sorted { $0.createdAt > $1.createdAt }
672
689
  }
673
690
 
674
691
  public static func cleanupExpiredInvitations() throws {
675
- var data = load()
676
- let expiredIDs = data.invitations.filter { $0.value.isExpired }.map { $0.key }
692
+ var data = self.load()
693
+ let expiredIDs = data.invitations.filter(\.value.isExpired).map(\.key)
677
694
 
678
695
  for id in expiredIDs {
679
696
  data.invitations[id]?.status = .expired
680
697
  data.invitations[id]?.respondedAt = Date()
681
698
  }
682
699
 
683
- try save(data)
700
+ try self.save(data)
684
701
  }
685
702
 
686
703
  // MARK: - Current User Management
687
704
 
688
705
  public static func setCurrentUser(userID: String) throws {
689
- var data = load()
706
+ var data = self.load()
690
707
  data.currentUserID = userID
691
- try save(data)
708
+ try self.save(data)
692
709
  }
693
710
 
694
711
  public static func getCurrentUser() -> String? {
695
- load().currentUserID
712
+ self.load().currentUserID
696
713
  }
697
714
 
698
715
  // MARK: - Usage Tracking
@@ -702,7 +719,7 @@ public struct TeamStore {
702
719
  throw TeamStoreError.invalidQuota
703
720
  }
704
721
 
705
- var data = load()
722
+ var data = self.load()
706
723
  guard var team = data.teams[teamID] else {
707
724
  throw TeamStoreError.teamNotFound
708
725
  }
@@ -712,12 +729,12 @@ public struct TeamStore {
712
729
 
713
730
  if team.usedQuota > team.quota {
714
731
  data.teams[teamID] = team
715
- try save(data)
732
+ try self.save(data)
716
733
  throw TeamStoreError.teamQuotaExceeded
717
734
  }
718
735
 
719
736
  data.teams[teamID] = team
720
- try save(data)
737
+ try self.save(data)
721
738
  }
722
739
 
723
740
  public static func incrementMemberUsage(membershipID: String, amount: Int) throws {
@@ -725,7 +742,7 @@ public struct TeamStore {
725
742
  throw TeamStoreError.invalidQuota
726
743
  }
727
744
 
728
- var data = load()
745
+ var data = self.load()
729
746
  guard var membership = data.memberships[membershipID] else {
730
747
  throw TeamStoreError.membershipNotFound
731
748
  }
@@ -736,16 +753,16 @@ public struct TeamStore {
736
753
  // Check individual quota if set
737
754
  if let quota = membership.quota, membership.usedQuota > quota {
738
755
  data.memberships[membershipID] = membership
739
- try save(data)
756
+ try self.save(data)
740
757
  throw TeamStoreError.memberQuotaExceeded
741
758
  }
742
759
 
743
760
  data.memberships[membershipID] = membership
744
- try save(data)
761
+ try self.save(data)
745
762
  }
746
763
 
747
764
  public static func resetTeamUsage(teamID: String) throws {
748
- var data = load()
765
+ var data = self.load()
749
766
  guard var team = data.teams[teamID] else {
750
767
  throw TeamStoreError.teamNotFound
751
768
  }
@@ -754,11 +771,11 @@ public struct TeamStore {
754
771
  team.updatedAt = Date()
755
772
  data.teams[teamID] = team
756
773
 
757
- try save(data)
774
+ try self.save(data)
758
775
  }
759
776
 
760
777
  public static func resetMemberUsage(membershipID: String) throws {
761
- var data = load()
778
+ var data = self.load()
762
779
  guard var membership = data.memberships[membershipID] else {
763
780
  throw TeamStoreError.membershipNotFound
764
781
  }
@@ -767,11 +784,11 @@ public struct TeamStore {
767
784
  membership.updatedAt = Date()
768
785
  data.memberships[membershipID] = membership
769
786
 
770
- try save(data)
787
+ try self.save(data)
771
788
  }
772
789
 
773
790
  public static func resetAllTeamUsages() throws {
774
- var data = load()
791
+ var data = self.load()
775
792
 
776
793
  // Reset all teams
777
794
  for (id, var team) in data.teams {
@@ -787,6 +804,6 @@ public struct TeamStore {
787
804
  data.memberships[id] = membership
788
805
  }
789
806
 
790
- try save(data)
807
+ try self.save(data)
791
808
  }
792
809
  }
@@ -277,7 +277,7 @@ private enum RPCWireError: Error, CustomStringConvertible {
277
277
  }
278
278
  }
279
279
 
280
- // RPC helper used on background tasks; safe because we confine it to the owning task.
280
+ /// RPC helper used on background tasks; safe because we confine it to the owning task.
281
281
  private final class CodexRPCClient: @unchecked Sendable {
282
282
  private let process = Process()
283
283
  private let stdinPipe = Pipe()
@@ -687,7 +687,7 @@ public struct UsageFetcher: Sendable {
687
687
  }
688
688
  }
689
689
 
690
- // Minimal auth.json struct preserved from previous implementation
690
+ /// Minimal auth.json struct preserved from previous implementation
691
691
  private struct AuthFile: Decodable {
692
692
  struct Tokens: Decodable { let idToken: String? }
693
693
  let tokens: Tokens?