@sriinnu/runic 1.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 (584) hide show
  1. package/.swiftformat +50 -0
  2. package/.swiftlint.yml +160 -0
  3. package/CHANGELOG.md +307 -0
  4. package/CONTRIBUTING.md +456 -0
  5. package/Icon.icns +0 -0
  6. package/Icon.icon/Assets/runic.png +0 -0
  7. package/Icon.icon/icon.json +36 -0
  8. package/LICENSE +21 -0
  9. package/Package.resolved +51 -0
  10. package/Package.swift +136 -0
  11. package/Packages/Helix/LICENSE +21 -0
  12. package/Packages/Helix/Package.swift +29 -0
  13. package/Packages/Helix/README.md +976 -0
  14. package/Packages/Helix/Sources/Helix/Binding.swift +111 -0
  15. package/Packages/Helix/Sources/Helix/CommandParser.swift +160 -0
  16. package/Packages/Helix/Sources/Helix/CommandSignature.swift +117 -0
  17. package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +72 -0
  18. package/Packages/Helix/Sources/Helix/HelpGenerator.swift +175 -0
  19. package/Packages/Helix/Sources/Helix/NameSpecification.swift +98 -0
  20. package/Packages/Helix/Sources/Helix/ParsableCommand.swift +204 -0
  21. package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +44 -0
  22. package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +34 -0
  23. package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +91 -0
  24. package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +94 -0
  25. package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +167 -0
  26. package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +99 -0
  27. package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +146 -0
  28. package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +97 -0
  29. package/Packages/Helix/Sources/Helix/Program.swift +204 -0
  30. package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +367 -0
  31. package/Packages/Silo/LICENSE +21 -0
  32. package/Packages/Silo/Package.swift +34 -0
  33. package/Packages/Silo/README.md +466 -0
  34. package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +169 -0
  35. package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +365 -0
  36. package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +594 -0
  37. package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +326 -0
  38. package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +154 -0
  39. package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +119 -0
  40. package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +121 -0
  41. package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +391 -0
  42. package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +240 -0
  43. package/Packages/Silo/Sources/Silo/CryptoSupport.swift +86 -0
  44. package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +172 -0
  45. package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +172 -0
  46. package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +105 -0
  47. package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +191 -0
  48. package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +41 -0
  49. package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +110 -0
  50. package/README.md +109 -0
  51. package/RuniciOS/App/RuniciOSApp.swift +105 -0
  52. package/RuniciOS/Core/iOSUsageStore.swift +192 -0
  53. package/RuniciOS/Views/ProviderDetailView.swift +312 -0
  54. package/RuniciOS/Views/ProviderListView.swift +228 -0
  55. package/RuniciOS/Widgets/LargeWidget.swift +367 -0
  56. package/RuniciOS/Widgets/LiveActivity.swift +342 -0
  57. package/RuniciOS/Widgets/LockScreenWidget.swift +273 -0
  58. package/RuniciOS/Widgets/MediumWidget.swift +370 -0
  59. package/RuniciOS/Widgets/RunicWidget.swift +274 -0
  60. package/RuniciOS/Widgets/SmallWidget.swift +281 -0
  61. package/RuniciOS/Widgets/WidgetModels.swift +285 -0
  62. package/RuniciOS/Widgets/WidgetTimeline.swift +270 -0
  63. package/Scripts/CREATION-SUMMARY.md +367 -0
  64. package/Scripts/DIRECTORY-STRUCTURE.md +358 -0
  65. package/Scripts/GETTING-STARTED-CHECKLIST.md +364 -0
  66. package/Scripts/INDEX.md +365 -0
  67. package/Scripts/QUICK-REFERENCE.md +164 -0
  68. package/Scripts/README-SCRIPTS.md +926 -0
  69. package/Scripts/build-android.sh +526 -0
  70. package/Scripts/build-ios.sh +529 -0
  71. package/Scripts/build-macos.sh +603 -0
  72. package/Scripts/build-windows.sh +504 -0
  73. package/Scripts/build_icon.sh +49 -0
  74. package/Scripts/changelog-to-html.sh +89 -0
  75. package/Scripts/check-release-assets.sh +10 -0
  76. package/Scripts/ci-config.yml +474 -0
  77. package/Scripts/compile_and_run.sh +219 -0
  78. package/Scripts/compile_and_run_adhoc.sh +4 -0
  79. package/Scripts/docs-list.mjs +119 -0
  80. package/Scripts/make_appcast.sh +58 -0
  81. package/Scripts/package_app.sh +409 -0
  82. package/Scripts/release-lib.sh +214 -0
  83. package/Scripts/release.sh +76 -0
  84. package/Scripts/setup-all.sh +387 -0
  85. package/Scripts/setup-ios.sh +404 -0
  86. package/Scripts/setup-react-native.sh +523 -0
  87. package/Scripts/sign-and-notarize.sh +122 -0
  88. package/Scripts/test_live_update.sh +38 -0
  89. package/Scripts/validate_changelog.sh +23 -0
  90. package/Scripts/verify_appcast.sh +92 -0
  91. package/Sources/Runic/About.swift +93 -0
  92. package/Sources/Runic/AppNotifications.swift +115 -0
  93. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +657 -0
  94. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +518 -0
  95. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +810 -0
  96. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +87 -0
  97. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +1379 -0
  98. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +337 -0
  99. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +395 -0
  100. package/Sources/Runic/Controllers/StatusItemController.swift +360 -0
  101. package/Sources/Runic/Core/BudgetNotificationManager.swift +80 -0
  102. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +103 -0
  103. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +154 -0
  104. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +206 -0
  105. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +95 -0
  106. package/Sources/Runic/Core/Rendering/IconRenderer.swift +1216 -0
  107. package/Sources/Runic/Core/Rendering/IconView.swift +120 -0
  108. package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +71 -0
  109. package/Sources/Runic/Core/RunicAnimation.swift +53 -0
  110. package/Sources/Runic/Core/RunicColors.swift +73 -0
  111. package/Sources/Runic/Core/RunicCornerRadius.swift +15 -0
  112. package/Sources/Runic/Core/RunicSpacing.swift +68 -0
  113. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +143 -0
  114. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +143 -0
  115. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +143 -0
  116. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +143 -0
  117. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +143 -0
  118. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +158 -0
  119. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +143 -0
  120. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +143 -0
  121. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +143 -0
  122. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +143 -0
  123. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +143 -0
  124. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +143 -0
  125. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +143 -0
  126. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +143 -0
  127. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +143 -0
  128. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +143 -0
  129. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +30 -0
  130. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +143 -0
  131. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +143 -0
  132. package/Sources/Runic/Core/Stores/SettingsStore.swift +1702 -0
  133. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +143 -0
  134. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +20 -0
  135. package/Sources/Runic/Core/Stores/UsageStore+Status.swift +189 -0
  136. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +33 -0
  137. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +67 -0
  138. package/Sources/Runic/Core/Stores/UsageStore.swift +3564 -0
  139. package/Sources/Runic/Core/Stores/XAITokenStore.swift +143 -0
  140. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +143 -0
  141. package/Sources/Runic/Core/UsageExporter.swift +193 -0
  142. package/Sources/Runic/Date+RelativeDescription.swift +21 -0
  143. package/Sources/Runic/HiddenWindowView.swift +32 -0
  144. package/Sources/Runic/InstallOrigin.swift +9 -0
  145. package/Sources/Runic/KeyboardShortcuts+Names.swift +6 -0
  146. package/Sources/Runic/MouseLocationReader.swift +65 -0
  147. package/Sources/Runic/Notifications+Runic.swift +6 -0
  148. package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +503 -0
  149. package/Sources/Runic/ProviderRegistry.swift +100 -0
  150. package/Sources/Runic/ProviderSwitcherButtons.swift +267 -0
  151. package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +11 -0
  152. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +14 -0
  153. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +24 -0
  154. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +52 -0
  155. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +42 -0
  156. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +24 -0
  157. package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +26 -0
  158. package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +14 -0
  159. package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +18 -0
  160. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +15 -0
  161. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +24 -0
  162. package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +193 -0
  163. package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +110 -0
  164. package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +28 -0
  165. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +15 -0
  166. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +24 -0
  167. package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +12 -0
  168. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +15 -0
  169. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +24 -0
  170. package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +20 -0
  171. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +15 -0
  172. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +24 -0
  173. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +24 -0
  174. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +32 -0
  175. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +24 -0
  176. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +24 -0
  177. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +24 -0
  178. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +24 -0
  179. package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +15 -0
  180. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +48 -0
  181. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +66 -0
  182. package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +12 -0
  183. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +99 -0
  184. package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +18 -0
  185. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +24 -0
  186. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +24 -0
  187. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +24 -0
  188. package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +2 -0
  189. package/Sources/Runic/Resources/ProviderIcon-auggie.svg +3 -0
  190. package/Sources/Runic/Resources/ProviderIcon-azure.svg +5 -0
  191. package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +7 -0
  192. package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +3 -0
  193. package/Sources/Runic/Resources/ProviderIcon-claude.svg +1 -0
  194. package/Sources/Runic/Resources/ProviderIcon-codex.svg +1 -0
  195. package/Sources/Runic/Resources/ProviderIcon-cohere.svg +3 -0
  196. package/Sources/Runic/Resources/ProviderIcon-copilot.svg +1 -0
  197. package/Sources/Runic/Resources/ProviderIcon-cursor.svg +3 -0
  198. package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +4 -0
  199. package/Sources/Runic/Resources/ProviderIcon-factory.svg +3 -0
  200. package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +3 -0
  201. package/Sources/Runic/Resources/ProviderIcon-gemini.svg +1 -0
  202. package/Sources/Runic/Resources/ProviderIcon-groq.svg +1 -0
  203. package/Sources/Runic/Resources/ProviderIcon-kimi.svg +4 -0
  204. package/Sources/Runic/Resources/ProviderIcon-minimax.svg +1 -0
  205. package/Sources/Runic/Resources/ProviderIcon-mistral.svg +5 -0
  206. package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +7 -0
  207. package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +4 -0
  208. package/Sources/Runic/Resources/ProviderIcon-qwen.svg +9 -0
  209. package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +4 -0
  210. package/Sources/Runic/Resources/ProviderIcon-together.svg +5 -0
  211. package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +17 -0
  212. package/Sources/Runic/Resources/ProviderIcon-xai.svg +3 -0
  213. package/Sources/Runic/Resources/ProviderIcon-zai.svg +5 -0
  214. package/Sources/Runic/Resources/RunicMenubarIcon.svg +7 -0
  215. package/Sources/Runic/RunicApp.swift +357 -0
  216. package/Sources/Runic/SessionQuotaNotifications.swift +58 -0
  217. package/Sources/Runic/UsagePaceText.swift +52 -0
  218. package/Sources/Runic/UsageProgressBar.swift +224 -0
  219. package/Sources/Runic/Utilities/DisplayLink.swift +96 -0
  220. package/Sources/Runic/Utilities/LoadingPattern.swift +60 -0
  221. package/Sources/Runic/Utilities/PerformanceConstants.swift +85 -0
  222. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +256 -0
  223. package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +22 -0
  224. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +275 -0
  225. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +377 -0
  226. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +312 -0
  227. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +312 -0
  228. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +228 -0
  229. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +228 -0
  230. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +162 -0
  231. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +64 -0
  232. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +268 -0
  233. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +104 -0
  234. package/Sources/Runic/Views/Menu/MenuCardView.swift +1900 -0
  235. package/Sources/Runic/Views/Menu/MenuContent.swift +150 -0
  236. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +369 -0
  237. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +42 -0
  238. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +235 -0
  239. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +141 -0
  240. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +209 -0
  241. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +220 -0
  242. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +88 -0
  243. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +201 -0
  244. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +398 -0
  245. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +232 -0
  246. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +417 -0
  247. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +190 -0
  248. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +132 -0
  249. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +618 -0
  250. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +305 -0
  251. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +271 -0
  252. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +268 -0
  253. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +422 -0
  254. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +641 -0
  255. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +344 -0
  256. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +230 -0
  257. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +322 -0
  258. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +469 -0
  259. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +231 -0
  260. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +50 -0
  261. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +497 -0
  262. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +288 -0
  263. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +3435 -0
  264. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +25 -0
  265. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +59 -0
  266. package/Sources/Runic/Views/Preferences/PreferencesView.swift +206 -0
  267. package/Sources/Runic/Views/Rating/QualityRatingView.swift +238 -0
  268. package/Sources/Runic/Views/Team/TeamDashboardView.swift +371 -0
  269. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +175 -0
  270. package/Sources/Runic/Views/Team/TeamManagementView.swift +761 -0
  271. package/Sources/Runic/Views/Team/TeamMemberRow.swift +181 -0
  272. package/Sources/RunicCLI/CLIEntry.swift +942 -0
  273. package/Sources/RunicCLI/CLIRenderer.swift +87 -0
  274. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +588 -0
  275. package/Sources/RunicCLI/Commands/ModelsCommand.swift +362 -0
  276. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +439 -0
  277. package/Sources/RunicCLI/Commands/ResetCommand.swift +429 -0
  278. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +168 -0
  279. package/Sources/RunicClaudeWatchdog/main.swift +122 -0
  280. package/Sources/RunicClaudeWebProbe/main.swift +62 -0
  281. package/Sources/RunicCore/AlertRuleStore.swift +187 -0
  282. package/Sources/RunicCore/BrowserCookieImportOrder.swift +25 -0
  283. package/Sources/RunicCore/CopilotUsageModels.swift +84 -0
  284. package/Sources/RunicCore/CostUsageFetcher.swift +119 -0
  285. package/Sources/RunicCore/CostUsageModels.swift +479 -0
  286. package/Sources/RunicCore/CreditsModels.swift +50 -0
  287. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +269 -0
  288. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +622 -0
  289. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +500 -0
  290. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +679 -0
  291. package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +135 -0
  292. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +173 -0
  293. package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +79 -0
  294. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +86 -0
  295. package/Sources/RunicCore/Logging/RunicLog.swift +123 -0
  296. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +371 -0
  297. package/Sources/RunicCore/OpenAIDashboardModels.swift +210 -0
  298. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +676 -0
  299. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +492 -0
  300. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +51 -0
  301. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +483 -0
  302. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +558 -0
  303. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +204 -0
  304. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +78 -0
  305. package/Sources/RunicCore/PathEnvironment.swift +482 -0
  306. package/Sources/RunicCore/Performance/PerformanceModels.swift +371 -0
  307. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +409 -0
  308. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +178 -0
  309. package/Sources/RunicCore/ProjectBudgetStore.swift +99 -0
  310. package/Sources/RunicCore/ProviderCostSnapshot.swift +29 -0
  311. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +66 -0
  312. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +685 -0
  313. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +83 -0
  314. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +142 -0
  315. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +141 -0
  316. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +186 -0
  317. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +126 -0
  318. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +168 -0
  319. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +84 -0
  320. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +75 -0
  321. package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +332 -0
  322. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +180 -0
  323. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +137 -0
  324. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +207 -0
  325. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +654 -0
  326. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +28 -0
  327. package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +480 -0
  328. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +780 -0
  329. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +146 -0
  330. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +269 -0
  331. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +100 -0
  332. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +243 -0
  333. package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +125 -0
  334. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +24 -0
  335. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +181 -0
  336. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +83 -0
  337. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +85 -0
  338. package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +123 -0
  339. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +114 -0
  340. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +100 -0
  341. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +291 -0
  342. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +386 -0
  343. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +65 -0
  344. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +594 -0
  345. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +85 -0
  346. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +108 -0
  347. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +398 -0
  348. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +65 -0
  349. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +1275 -0
  350. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +84 -0
  351. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +75 -0
  352. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +65 -0
  353. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +835 -0
  354. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +83 -0
  355. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +71 -0
  356. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +83 -0
  357. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +77 -0
  358. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +92 -0
  359. package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +54 -0
  360. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +171 -0
  361. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +911 -0
  362. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +84 -0
  363. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +75 -0
  364. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +87 -0
  365. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +207 -0
  366. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +84 -0
  367. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +75 -0
  368. package/Sources/RunicCore/Providers/ProviderBranding.swift +25 -0
  369. package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +17 -0
  370. package/Sources/RunicCore/Providers/ProviderDescriptor.swift +129 -0
  371. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +263 -0
  372. package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +98 -0
  373. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +501 -0
  374. package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +68 -0
  375. package/Sources/RunicCore/Providers/Providers.swift +184 -0
  376. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +84 -0
  377. package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +30 -0
  378. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +317 -0
  379. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +84 -0
  380. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +75 -0
  381. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +83 -0
  382. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +75 -0
  383. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +123 -0
  384. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +48 -0
  385. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +144 -0
  386. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +83 -0
  387. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +75 -0
  388. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +72 -0
  389. package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +41 -0
  390. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +724 -0
  391. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +385 -0
  392. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +310 -0
  393. package/Sources/RunicCore/Sync/SyncProtocol.swift +288 -0
  394. package/Sources/RunicCore/Sync/SyncRecord.swift +405 -0
  395. package/Sources/RunicCore/Sync/SyncUsageExample.swift +294 -0
  396. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +775 -0
  397. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +333 -0
  398. package/Sources/RunicCore/Team/TeamModels.swift +583 -0
  399. package/Sources/RunicCore/Team/TeamShowbackExporter.swift +495 -0
  400. package/Sources/RunicCore/Team/TeamStore.swift +792 -0
  401. package/Sources/RunicCore/TextParsing.swift +55 -0
  402. package/Sources/RunicCore/UsageFetcher.swift +694 -0
  403. package/Sources/RunicCore/UsageFormatter.swift +410 -0
  404. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +329 -0
  405. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +448 -0
  406. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +550 -0
  407. package/Sources/RunicCore/UsageLedger/UsageLedger.swift +29 -0
  408. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +660 -0
  409. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +209 -0
  410. package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +417 -0
  411. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +245 -0
  412. package/Sources/RunicCore/UsagePace.swift +81 -0
  413. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +73 -0
  414. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +74 -0
  415. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +208 -0
  416. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +455 -0
  417. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +132 -0
  418. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +589 -0
  419. package/Sources/RunicCore/WidgetSnapshot.swift +183 -0
  420. package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +14 -0
  421. package/Sources/RunicMacros/ProviderRegistrationMacros.swift +216 -0
  422. package/Sources/RunicWidget/RunicWidgetBundle.swift +79 -0
  423. package/Sources/RunicWidget/RunicWidgetProvider.swift +283 -0
  424. package/Sources/RunicWidget/RunicWidgetViews.swift +680 -0
  425. package/Tests/RunicTests/AntigravityStatusProbeTests.swift +50 -0
  426. package/Tests/RunicTests/AppDelegateTests.swift +43 -0
  427. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +22 -0
  428. package/Tests/RunicTests/CLIArgumentParsingTests.swift +70 -0
  429. package/Tests/RunicTests/CLICostTests.swift +65 -0
  430. package/Tests/RunicTests/CLIProviderSelectionTests.swift +22 -0
  431. package/Tests/RunicTests/CLISnapshotTests.swift +72 -0
  432. package/Tests/RunicTests/CLIWebFallbackTests.swift +58 -0
  433. package/Tests/RunicTests/ClaudeOAuthTests.swift +164 -0
  434. package/Tests/RunicTests/ClaudeResilienceTests.swift +30 -0
  435. package/Tests/RunicTests/ClaudeUsageTests.swift +370 -0
  436. package/Tests/RunicTests/CodexOAuthTests.swift +122 -0
  437. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +82 -0
  438. package/Tests/RunicTests/CopilotTokenResolverTests.swift +75 -0
  439. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +75 -0
  440. package/Tests/RunicTests/CostUsageDecodingTests.swift +394 -0
  441. package/Tests/RunicTests/CostUsagePricingTests.swift +50 -0
  442. package/Tests/RunicTests/CostUsageScannerTests.swift +700 -0
  443. package/Tests/RunicTests/CursorStatusProbeTests.swift +323 -0
  444. package/Tests/RunicTests/FactoryStatusProbeTests.swift +69 -0
  445. package/Tests/RunicTests/GeminiAPITestHelpers.swift +94 -0
  446. package/Tests/RunicTests/GeminiLoginAlertTests.swift +28 -0
  447. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +283 -0
  448. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +279 -0
  449. package/Tests/RunicTests/GeminiStatusProbeTests.swift +233 -0
  450. package/Tests/RunicTests/GeminiTestEnvironment.swift +87 -0
  451. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +73 -0
  452. package/Tests/RunicTests/InstallOriginTests.swift +18 -0
  453. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +9 -0
  454. package/Tests/RunicTests/LiveAccountTests.swift +24 -0
  455. package/Tests/RunicTests/LoadingPatternTests.swift +35 -0
  456. package/Tests/RunicTests/MenuCardModelTests.swift +545 -0
  457. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +8 -0
  458. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +8 -0
  459. package/Tests/RunicTests/MiniMaxParsingTests.swift +68 -0
  460. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +8 -0
  461. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +112 -0
  462. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +17 -0
  463. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +75 -0
  464. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +27 -0
  465. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +134 -0
  466. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +53 -0
  467. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +92 -0
  468. package/Tests/RunicTests/PathBuilderTests.swift +237 -0
  469. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +160 -0
  470. package/Tests/RunicTests/ProviderIconResourcesTests.swift +38 -0
  471. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +16 -0
  472. package/Tests/RunicTests/ProviderRegistryTests.swift +29 -0
  473. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +202 -0
  474. package/Tests/RunicTests/ProviderToggleStoreTests.swift +51 -0
  475. package/Tests/RunicTests/RunicTests.swift +120 -0
  476. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +36 -0
  477. package/Tests/RunicTests/SettingsStoreTests.swift +161 -0
  478. package/Tests/RunicTests/StatusItemAnimationTests.swift +49 -0
  479. package/Tests/RunicTests/StatusMenuTests.swift +434 -0
  480. package/Tests/RunicTests/StatusProbeTests.swift +377 -0
  481. package/Tests/RunicTests/SubprocessRunnerTests.swift +20 -0
  482. package/Tests/RunicTests/SubscriptionDetectionTests.swift +65 -0
  483. package/Tests/RunicTests/TTYCommandRunnerTests.swift +148 -0
  484. package/Tests/RunicTests/TTYIntegrationTests.swift +41 -0
  485. package/Tests/RunicTests/TeamShowbackExporterTests.swift +159 -0
  486. package/Tests/RunicTests/TextParsingTests.swift +12 -0
  487. package/Tests/RunicTests/UsageFormatterTests.swift +112 -0
  488. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +172 -0
  489. package/Tests/RunicTests/UsageLedgerForecastTests.swift +213 -0
  490. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +83 -0
  491. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +170 -0
  492. package/Tests/RunicTests/UsagePaceTests.swift +77 -0
  493. package/Tests/RunicTests/UsagePaceTextTests.swift +109 -0
  494. package/Tests/RunicTests/UsagePolicyEngineTests.swift +91 -0
  495. package/Tests/RunicTests/WidgetSnapshotTests.swift +45 -0
  496. package/Tests/RunicTests/ZaiProviderTests.swift +61 -0
  497. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +6 -0
  498. package/TestsLinux/PlatformGatingTests.swift +46 -0
  499. package/appcast.xml +105 -0
  500. package/assets/logo-concept-stack.svg +24 -0
  501. package/assets/logo-minimal-bars.svg +15 -0
  502. package/assets/logo.svg +20 -0
  503. package/assets/logo2.svg +43 -0
  504. package/bin/docs-list +5 -0
  505. package/bin/install-runic-cli.sh +32 -0
  506. package/build-and-run.sh +369 -0
  507. package/docs/.nojekyll +1 -0
  508. package/docs/CNAME +1 -0
  509. package/docs/antigravity.md +75 -0
  510. package/docs/architecture.md +32 -0
  511. package/docs/claude.md +93 -0
  512. package/docs/cli-commands/integration_guide.md +371 -0
  513. package/docs/cli-commands/quick_reference.md +362 -0
  514. package/docs/cli-commands/readme.md +361 -0
  515. package/docs/cli.md +156 -0
  516. package/docs/cloudkit_sync_guide.md +495 -0
  517. package/docs/codebase_review.md +288 -0
  518. package/docs/codex-oauth.md +635 -0
  519. package/docs/codex.md +104 -0
  520. package/docs/codexbar.png +0 -0
  521. package/docs/copilot.md +44 -0
  522. package/docs/cursor.md +46 -0
  523. package/docs/design/designcomparisonchart.md +510 -0
  524. package/docs/design/implementationguide.md +792 -0
  525. package/docs/design/menubaricondesignconcepts.md +796 -0
  526. package/docs/design/mockups/hybridiconconcept.svg +62 -0
  527. package/docs/design/mockups/infinityiconconcept.svg +41 -0
  528. package/docs/design/mockups/infinityiconstates.svg +129 -0
  529. package/docs/design/mockups/wavelogorefined.svg +48 -0
  530. package/docs/design/quick_summary.md +426 -0
  531. package/docs/design/readme.md +478 -0
  532. package/docs/design/sizespecifications.md +575 -0
  533. package/docs/design-system.md +836 -0
  534. package/docs/factory.md +103 -0
  535. package/docs/gemini.md +73 -0
  536. package/docs/icon.md +37 -0
  537. package/docs/icon.png +0 -0
  538. package/docs/index.html +175 -0
  539. package/docs/keychain_fix.md +274 -0
  540. package/docs/minimax.md +71 -0
  541. package/docs/packaging.md +25 -0
  542. package/docs/progress-bar-improvements.md +240 -0
  543. package/docs/progress-bar-quick-reference.md +283 -0
  544. package/docs/progress-bar-technical-details.md +432 -0
  545. package/docs/progress-bar-visual-test.swift +375 -0
  546. package/docs/provider.md +202 -0
  547. package/docs/providers.md +86 -0
  548. package/docs/refactor/macros.md +35 -0
  549. package/docs/refresh-loop.md +22 -0
  550. package/docs/releasing-homebrew.md +35 -0
  551. package/docs/releasing.md +121 -0
  552. package/docs/research/concrete_todo_2026-02-22.md +47 -0
  553. package/docs/research/core-feature-research.md +248 -0
  554. package/docs/research/feature_uiux_roadmap_2026-02-22.md +202 -0
  555. package/docs/research/parity_matrix.md +49 -0
  556. package/docs/research/provider_expansion_candidates_2026-02-22.md +80 -0
  557. package/docs/research/provider_model_token_project_matrix_2026-02-23.md +46 -0
  558. package/docs/research/release_gate_checklist.md +57 -0
  559. package/docs/site.css +505 -0
  560. package/docs/sparkle.md +28 -0
  561. package/docs/status.md +28 -0
  562. package/docs/sync/implementation.md +393 -0
  563. package/docs/sync/readme.md +317 -0
  564. package/docs/sync_files_summary.md +391 -0
  565. package/docs/sync_quick_reference.md +225 -0
  566. package/docs/todo.md +11 -0
  567. package/docs/ui.md +29 -0
  568. package/docs/web-integration.md +9 -0
  569. package/docs/widgets.md +85 -0
  570. package/docs/workflows/error-handling-improvements.md +434 -0
  571. package/docs/workflows/folder_structure.md +299 -0
  572. package/docs/workflows/loading_states.md +446 -0
  573. package/docs/workflows/loading_states_examples.md +565 -0
  574. package/docs/workflows/loading_states_flow.md +430 -0
  575. package/docs/workflows/performance_optimizations.md +457 -0
  576. package/docs/workflows/phase1-cli-testing-report.md +313 -0
  577. package/docs/workflows/quick_reference.md +228 -0
  578. package/docs/workflows/readme.md +59 -0
  579. package/docs/workflows/recent_improvements.md +275 -0
  580. package/docs/workflows/sync_enablement_summary.md +400 -0
  581. package/docs/zai.md +43 -0
  582. package/package.json +36 -0
  583. package/runic.png +0 -0
  584. package/version.env +2 -0
@@ -0,0 +1,622 @@
1
+ import Foundation
2
+ #if canImport(Security)
3
+ import Security
4
+ #endif
5
+ #if canImport(LocalAuthentication)
6
+ import LocalAuthentication
7
+ #endif
8
+
9
+ /// Generic fetcher for custom API provider usage and balance data
10
+ public actor GenericProviderFetcher {
11
+ private let config: CustomProviderConfig
12
+ private let keychainService = "com.sriinnu.athena.Runic"
13
+ private let log = RunicLog.logger("generic-provider-fetcher")
14
+
15
+ public init(config: CustomProviderConfig) {
16
+ self.config = config
17
+ }
18
+
19
+ // MARK: - Public Fetch Methods
20
+
21
+ /// Fetch usage data from the custom provider
22
+ public func fetchUsage() async throws -> UsageData {
23
+ guard let endpoint = config.endpoints.usage else {
24
+ throw FetchError.noUsageEndpoint
25
+ }
26
+
27
+ // 1. Build URL with variable substitution
28
+ let url = try buildURL(from: endpoint.url, with: endpoint.queryParams)
29
+
30
+ // 2. Create request with auth headers
31
+ var request = URLRequest(url: url)
32
+ request.httpMethod = endpoint.method.rawValue
33
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
34
+
35
+ // Add auth headers
36
+ try await addAuthHeaders(to: &request)
37
+
38
+ // 3. Make request
39
+ let (data, response) = try await URLSession.shared.data(for: request)
40
+
41
+ guard let httpResponse = response as? HTTPURLResponse else {
42
+ throw FetchError.invalidResponse
43
+ }
44
+
45
+ guard (200...299).contains(httpResponse.statusCode) else {
46
+ throw FetchError.httpError(httpResponse.statusCode)
47
+ }
48
+
49
+ // 4. Parse JSON response
50
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
51
+ throw FetchError.invalidJSON
52
+ }
53
+
54
+ // 5. Extract fields using mapping
55
+ return try extractUsageData(from: json, mapping: endpoint.mapping)
56
+ }
57
+
58
+ /// Fetch balance data from the custom provider
59
+ public func fetchBalance() async throws -> BalanceData {
60
+ guard let endpoint = config.endpoints.balance else {
61
+ throw FetchError.noBalanceEndpoint
62
+ }
63
+
64
+ // 1. Build URL with variable substitution
65
+ let url = try buildURL(from: endpoint.url, with: nil)
66
+
67
+ // 2. Create request with auth headers
68
+ var request = URLRequest(url: url)
69
+ request.httpMethod = endpoint.method.rawValue
70
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
71
+
72
+ // Add auth headers
73
+ try await addAuthHeaders(to: &request)
74
+
75
+ // 3. Make request
76
+ let (data, response) = try await URLSession.shared.data(for: request)
77
+
78
+ guard let httpResponse = response as? HTTPURLResponse else {
79
+ throw FetchError.invalidResponse
80
+ }
81
+
82
+ guard (200...299).contains(httpResponse.statusCode) else {
83
+ throw FetchError.httpError(httpResponse.statusCode)
84
+ }
85
+
86
+ // 4. Parse JSON response
87
+ guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
88
+ throw FetchError.invalidJSON
89
+ }
90
+
91
+ // 5. Extract fields using mapping
92
+ return try extractBalanceData(from: json, mapping: endpoint.mapping)
93
+ }
94
+
95
+ /// Convert usage data to Runic's UsageSnapshot format
96
+ public func toUsageSnapshot(_ data: UsageData) -> UsageSnapshot {
97
+ let usedPercent: Double
98
+ if let quota = data.quota, let used = data.used, quota > 0 {
99
+ usedPercent = (used / quota) * 100.0
100
+ } else if let remaining = data.remaining, let quota = data.quota, quota > 0 {
101
+ usedPercent = ((quota - remaining) / quota) * 100.0
102
+ } else {
103
+ usedPercent = 0
104
+ }
105
+
106
+ let primary = RateWindow(
107
+ usedPercent: max(0, min(100, usedPercent)),
108
+ windowMinutes: nil,
109
+ resetsAt: data.resetDate,
110
+ resetDescription: data.resetDate.map { resetDescription(from: $0) }
111
+ )
112
+
113
+ return UsageSnapshot(
114
+ primary: primary,
115
+ secondary: nil,
116
+ tertiary: nil,
117
+ updatedAt: Date(),
118
+ identity: nil
119
+ )
120
+ }
121
+
122
+ // MARK: - URL Building
123
+
124
+ /// Build URL with variable substitution and query parameters
125
+ private func buildURL(from template: String, with params: [String: String]?) throws -> URL {
126
+ var urlString = template
127
+
128
+ // Replace date variables
129
+ urlString = try substituteDateVariables(in: urlString)
130
+
131
+ // Build URL components
132
+ guard var components = URLComponents(string: urlString) else {
133
+ throw FetchError.invalidURL(urlString)
134
+ }
135
+
136
+ // Add query parameters
137
+ if let params = params, !params.isEmpty {
138
+ var queryItems = components.queryItems ?? []
139
+ queryItems.append(contentsOf: params.map { URLQueryItem(name: $0.key, value: $0.value) })
140
+ components.queryItems = queryItems
141
+ }
142
+
143
+ guard let url = components.url else {
144
+ throw FetchError.invalidURL(urlString)
145
+ }
146
+
147
+ return url
148
+ }
149
+
150
+ /// Replace date variables in URL template
151
+ private func substituteDateVariables(in template: String) throws -> String {
152
+ var result = template
153
+ let now = Date()
154
+
155
+ // Date formatters
156
+ let iso8601Formatter = ISO8601DateFormatter()
157
+ let dateFormatter = DateFormatter()
158
+ dateFormatter.dateFormat = "yyyy-MM-dd"
159
+
160
+ // {{date}} - Today's date in ISO8601 format
161
+ if result.contains("{{date}}") {
162
+ result = result.replacingOccurrences(of: "{{date}}", with: iso8601Formatter.string(from: now))
163
+ }
164
+
165
+ // {{date:yyyy-MM-dd}} - Custom date format
166
+ if let customMatch = result.range(of: #"\{\{date:([^}]+)\}\}"#, options: .regularExpression) {
167
+ let pattern = String(result[customMatch])
168
+ if let formatRange = pattern.range(of: "(?<=:)[^}]+", options: .regularExpression) {
169
+ let format = String(pattern[formatRange])
170
+ dateFormatter.dateFormat = format
171
+ result = result.replacingOccurrences(of: pattern, with: dateFormatter.string(from: now))
172
+ }
173
+ }
174
+
175
+ // {{start}} and {{end}} - Date range (default: current month)
176
+ if result.contains("{{start}}") || result.contains("{{end}}") {
177
+ let calendar = Calendar.current
178
+ let components = calendar.dateComponents([.year, .month], from: now)
179
+ guard let startOfMonth = calendar.date(from: components) else {
180
+ throw FetchError.invalidDateRange
181
+ }
182
+ let endOfMonth = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) ?? now
183
+
184
+ result = result.replacingOccurrences(of: "{{start}}", with: dateFormatter.string(from: startOfMonth))
185
+ result = result.replacingOccurrences(of: "{{end}}", with: dateFormatter.string(from: endOfMonth))
186
+ }
187
+
188
+ // {{timestamp}} - Unix timestamp
189
+ if result.contains("{{timestamp}}") {
190
+ result = result.replacingOccurrences(of: "{{timestamp}}", with: "\(Int(now.timeIntervalSince1970))")
191
+ }
192
+
193
+ return result
194
+ }
195
+
196
+ // MARK: - Authentication
197
+
198
+ /// Add authentication headers to the request
199
+ private func addAuthHeaders(to request: inout URLRequest) async throws {
200
+ // Load token from keychain
201
+ let token = try await loadToken()
202
+
203
+ // Add auth header based on type
204
+ switch config.auth.type {
205
+ case .apiKey:
206
+ let value = (config.auth.headerPrefix ?? "") + token
207
+ request.setValue(value, forHTTPHeaderField: config.auth.headerName)
208
+
209
+ case .bearer:
210
+ request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
211
+
212
+ case .basic:
213
+ let encoded = Data("\(token):".utf8).base64EncodedString()
214
+ request.setValue("Basic \(encoded)", forHTTPHeaderField: "Authorization")
215
+
216
+ case .oauth:
217
+ // OAuth typically uses Bearer token
218
+ request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
219
+
220
+ case .custom:
221
+ // Custom auth uses the exact header name and prefix from config
222
+ let value = (config.auth.headerPrefix ?? "") + token
223
+ request.setValue(value, forHTTPHeaderField: config.auth.headerName)
224
+ }
225
+
226
+ // Add additional headers
227
+ config.auth.additionalHeaders?.forEach { key, value in
228
+ request.setValue(value, forHTTPHeaderField: key)
229
+ }
230
+ }
231
+
232
+ /// Load token from keychain or environment
233
+ private func loadToken() async throws -> String {
234
+ // Try keychain first
235
+ if let token = keychainToken(account: config.auth.tokenKeychain) {
236
+ return token
237
+ }
238
+
239
+ // Try environment variable
240
+ let envKey = config.auth.tokenKeychain.uppercased().replacingOccurrences(of: "-", with: "_")
241
+ if let token = ProcessInfo.processInfo.environment[envKey] {
242
+ return token.trimmingCharacters(in: .whitespacesAndNewlines)
243
+ }
244
+
245
+ throw FetchError.missingToken(config.auth.tokenKeychain)
246
+ }
247
+
248
+ /// Read token from keychain
249
+ private func keychainToken(account: String) -> String? {
250
+ #if canImport(Security)
251
+ var result: CFTypeRef?
252
+ var query: [String: Any] = [
253
+ kSecClass as String: kSecClassGenericPassword,
254
+ kSecAttrService as String: keychainService,
255
+ kSecAttrAccount as String: account,
256
+ kSecUseDataProtectionKeychain as String: true,
257
+ kSecUseAuthenticationUI as String: "kSecUseAuthenticationUIFail" as CFString,
258
+ kSecMatchLimit as String: kSecMatchLimitOne,
259
+ kSecReturnData as String: true,
260
+ ]
261
+ #if canImport(LocalAuthentication)
262
+ let authContext = LAContext()
263
+ authContext.interactionNotAllowed = true
264
+ query[kSecUseAuthenticationContext as String] = authContext
265
+ #endif
266
+
267
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
268
+ if status == errSecItemNotFound {
269
+ return nil
270
+ }
271
+ if status == errSecInteractionNotAllowed {
272
+ return nil
273
+ }
274
+ guard status == errSecSuccess else {
275
+ log.error("Keychain read failed for \(account): \(status)")
276
+ return nil
277
+ }
278
+
279
+ guard let data = result as? Data,
280
+ let token = String(data: data, encoding: .utf8)?
281
+ .trimmingCharacters(in: .whitespacesAndNewlines),
282
+ !token.isEmpty
283
+ else {
284
+ return nil
285
+ }
286
+
287
+ return token
288
+ #else
289
+ _ = account
290
+ return nil
291
+ #endif
292
+ }
293
+
294
+ // MARK: - Data Extraction
295
+
296
+ /// Extract usage data using JSONPath-like mapping
297
+ private func extractUsageData(from json: [String: Any], mapping: ResponseMapping) throws -> UsageData {
298
+ var data = UsageData()
299
+
300
+ // Extract quota
301
+ if let quotaPath = mapping.quota {
302
+ data.quota = extractDouble(from: json, path: quotaPath, nested: mapping.nestedPaths)
303
+ }
304
+
305
+ // Extract used amount
306
+ if let usedPath = mapping.used {
307
+ data.used = extractDouble(from: json, path: usedPath, nested: mapping.nestedPaths)
308
+ }
309
+
310
+ // Extract remaining amount
311
+ if let remainingPath = mapping.remaining {
312
+ data.remaining = extractDouble(from: json, path: remainingPath, nested: mapping.nestedPaths)
313
+ }
314
+
315
+ // Extract cost
316
+ if let costPath = mapping.cost {
317
+ data.cost = extractDouble(from: json, path: costPath, nested: mapping.nestedPaths)
318
+ }
319
+
320
+ // Extract reset date
321
+ if let resetPath = mapping.resetDate {
322
+ data.resetDate = extractDate(from: json, path: resetPath, nested: mapping.nestedPaths)
323
+ }
324
+
325
+ // Extract tokens
326
+ if let tokensPath = mapping.tokens {
327
+ data.tokens = extractInt(from: json, path: tokensPath, nested: mapping.nestedPaths)
328
+ }
329
+
330
+ return data
331
+ }
332
+
333
+ /// Extract balance data using JSONPath-like mapping
334
+ private func extractBalanceData(from json: [String: Any], mapping: ResponseMapping) throws -> BalanceData {
335
+ var data = BalanceData()
336
+
337
+ // Extract balance (use 'remaining' or 'used' mapping)
338
+ if let balancePath = mapping.remaining ?? mapping.used {
339
+ data.balance = extractDouble(from: json, path: balancePath, nested: mapping.nestedPaths)
340
+ }
341
+
342
+ // Try to detect currency from response
343
+ data.currency = extractString(from: json, path: "currency", nested: true) ?? "USD"
344
+
345
+ return data
346
+ }
347
+
348
+ // MARK: - JSONPath Extraction
349
+
350
+ /// Extract nested value using dot notation path
351
+ private func extractValue(from json: [String: Any], path: String, nested: Bool) -> Any? {
352
+ if !nested {
353
+ return json[path]
354
+ }
355
+
356
+ let components = path.split(separator: ".").map(String.init)
357
+ var current: Any? = json
358
+
359
+ for component in components {
360
+ // Handle array indexing: field[0]
361
+ if let bracketIndex = component.firstIndex(of: "["),
362
+ let closeBracket = component.firstIndex(of: "]") {
363
+ let fieldName = String(component[..<bracketIndex])
364
+ let indexStr = String(component[component.index(after: bracketIndex)..<closeBracket])
365
+
366
+ guard let dict = current as? [String: Any],
367
+ let array = dict[fieldName] as? [Any],
368
+ let index = Int(indexStr),
369
+ index < array.count else {
370
+ return nil
371
+ }
372
+ current = array[index]
373
+ } else {
374
+ // Regular field access
375
+ guard let dict = current as? [String: Any] else {
376
+ return nil
377
+ }
378
+ current = dict[component]
379
+ }
380
+ }
381
+
382
+ return current
383
+ }
384
+
385
+ /// Extract Double value from JSON path
386
+ private func extractDouble(from json: [String: Any], path: String, nested: Bool) -> Double? {
387
+ guard let value = extractValue(from: json, path: path, nested: nested) else {
388
+ return nil
389
+ }
390
+
391
+ // Handle different numeric types
392
+ switch value {
393
+ case let double as Double:
394
+ return double
395
+ case let int as Int:
396
+ return Double(int)
397
+ case let string as String:
398
+ return Double(string)
399
+ case let number as NSNumber:
400
+ return number.doubleValue
401
+ default:
402
+ return nil
403
+ }
404
+ }
405
+
406
+ /// Extract Int value from JSON path
407
+ private func extractInt(from json: [String: Any], path: String, nested: Bool) -> Int? {
408
+ guard let value = extractValue(from: json, path: path, nested: nested) else {
409
+ return nil
410
+ }
411
+
412
+ switch value {
413
+ case let int as Int:
414
+ return int
415
+ case let double as Double:
416
+ return Int(double)
417
+ case let string as String:
418
+ return Int(string)
419
+ case let number as NSNumber:
420
+ return number.intValue
421
+ default:
422
+ return nil
423
+ }
424
+ }
425
+
426
+ /// Extract String value from JSON path
427
+ private func extractString(from json: [String: Any], path: String, nested: Bool) -> String? {
428
+ guard let value = extractValue(from: json, path: path, nested: nested) else {
429
+ return nil
430
+ }
431
+
432
+ switch value {
433
+ case let string as String:
434
+ return string
435
+ case let number as NSNumber:
436
+ return number.stringValue
437
+ default:
438
+ return String(describing: value)
439
+ }
440
+ }
441
+
442
+ /// Extract Date value from JSON path (supports Unix timestamp and ISO8601)
443
+ private func extractDate(from json: [String: Any], path: String, nested: Bool) -> Date? {
444
+ guard let value = extractValue(from: json, path: path, nested: nested) else {
445
+ return nil
446
+ }
447
+
448
+ // Try Unix timestamp (seconds or milliseconds)
449
+ if let timestamp = value as? Double {
450
+ // If timestamp > 10 billion, assume milliseconds
451
+ let interval = timestamp > 10_000_000_000 ? timestamp / 1000.0 : timestamp
452
+ return Date(timeIntervalSince1970: interval)
453
+ }
454
+
455
+ if let timestamp = value as? Int {
456
+ let interval = timestamp > 10_000_000_000 ? Double(timestamp) / 1000.0 : Double(timestamp)
457
+ return Date(timeIntervalSince1970: interval)
458
+ }
459
+
460
+ // Try ISO8601 string
461
+ if let dateString = value as? String {
462
+ let formatter = ISO8601DateFormatter()
463
+ if let date = formatter.date(from: dateString) {
464
+ return date
465
+ }
466
+
467
+ // Try RFC3339 with fractional seconds
468
+ formatter.formatOptions.insert(.withFractionalSeconds)
469
+ if let date = formatter.date(from: dateString) {
470
+ return date
471
+ }
472
+ }
473
+
474
+ return nil
475
+ }
476
+
477
+ // MARK: - Helpers
478
+
479
+ /// Format reset date into human-readable description
480
+ private func resetDescription(from date: Date) -> String {
481
+ let now = Date()
482
+ let interval = date.timeIntervalSince(now)
483
+
484
+ if interval <= 0 {
485
+ return "Reset now"
486
+ }
487
+
488
+ let hours = Int(interval / 3600)
489
+ let minutes = Int((interval.truncatingRemainder(dividingBy: 3600)) / 60)
490
+
491
+ if hours > 24 {
492
+ let days = hours / 24
493
+ return "Resets in \(days)d"
494
+ } else if hours > 0 {
495
+ return "Resets in \(hours)h \(minutes)m"
496
+ } else {
497
+ return "Resets in \(minutes)m"
498
+ }
499
+ }
500
+ }
501
+
502
+ // MARK: - Data Structures
503
+
504
+ /// Usage data fetched from custom provider
505
+ public struct UsageData: Sendable {
506
+ public var quota: Double?
507
+ public var used: Double?
508
+ public var remaining: Double?
509
+ public var cost: Double?
510
+ public var resetDate: Date?
511
+ public var tokens: Int?
512
+
513
+ public init(
514
+ quota: Double? = nil,
515
+ used: Double? = nil,
516
+ remaining: Double? = nil,
517
+ cost: Double? = nil,
518
+ resetDate: Date? = nil,
519
+ tokens: Int? = nil
520
+ ) {
521
+ self.quota = quota
522
+ self.used = used
523
+ self.remaining = remaining
524
+ self.cost = cost
525
+ self.resetDate = resetDate
526
+ self.tokens = tokens
527
+ }
528
+
529
+ public func toCustomUsageData() -> CustomUsageData {
530
+ return CustomUsageData(
531
+ quota: self.quota,
532
+ used: self.used,
533
+ remaining: self.remaining,
534
+ cost: self.cost,
535
+ resetDate: self.resetDate,
536
+ tokens: self.tokens
537
+ )
538
+ }
539
+ }
540
+
541
+ /// Balance data fetched from custom provider
542
+ public struct BalanceData: Sendable {
543
+ public var balance: Double?
544
+ public var currency: String?
545
+
546
+ public init(balance: Double? = nil, currency: String? = nil) {
547
+ self.balance = balance
548
+ self.currency = currency
549
+ }
550
+ }
551
+
552
+ // MARK: - Errors
553
+
554
+ /// Errors that can occur during fetch operations
555
+ public enum FetchError: LocalizedError, Sendable {
556
+ case noUsageEndpoint
557
+ case noBalanceEndpoint
558
+ case invalidURL(String)
559
+ case invalidResponse
560
+ case httpError(Int)
561
+ case invalidJSON
562
+ case missingToken(String)
563
+ case invalidDateRange
564
+ case extractionFailed(String)
565
+
566
+ public var errorDescription: String? {
567
+ switch self {
568
+ case .noUsageEndpoint:
569
+ return "No usage endpoint configured for this provider"
570
+ case .noBalanceEndpoint:
571
+ return "No balance endpoint configured for this provider"
572
+ case .invalidURL(let url):
573
+ return "Invalid URL: \(url)"
574
+ case .invalidResponse:
575
+ return "Invalid response from provider API"
576
+ case .httpError(let code):
577
+ return "HTTP error \(code) from provider API"
578
+ case .invalidJSON:
579
+ return "Failed to parse JSON response from provider"
580
+ case .missingToken(let account):
581
+ return "No API token found for '\(account)'. Store it in Keychain or set as environment variable."
582
+ case .invalidDateRange:
583
+ return "Failed to calculate date range for URL template"
584
+ case .extractionFailed(let field):
585
+ return "Failed to extract field '\(field)' from response"
586
+ }
587
+ }
588
+
589
+ public var failureReason: String? {
590
+ switch self {
591
+ case .missingToken(let account):
592
+ return "The token for account '\(account)' is not configured in Keychain or environment variables"
593
+ case .httpError(let code):
594
+ return "The server returned HTTP status code \(code)"
595
+ case .invalidJSON:
596
+ return "The response is not valid JSON or has unexpected structure"
597
+ case .extractionFailed(let field):
598
+ return "The field '\(field)' was not found in the response or has wrong type"
599
+ default:
600
+ return nil
601
+ }
602
+ }
603
+
604
+ public var recoverySuggestion: String? {
605
+ switch self {
606
+ case .missingToken(let account):
607
+ return "Store your API token in Keychain with account name '\(account)' or set it as environment variable"
608
+ case .noUsageEndpoint:
609
+ return "Configure a usage endpoint in the provider settings"
610
+ case .noBalanceEndpoint:
611
+ return "Configure a balance endpoint in the provider settings"
612
+ case .httpError(let code) where code == 401:
613
+ return "Check that your API token is valid and has not expired"
614
+ case .httpError(let code) where code == 429:
615
+ return "You have been rate limited. Try again later."
616
+ case .invalidJSON:
617
+ return "Check the API endpoint URL and response mapping configuration"
618
+ default:
619
+ return nil
620
+ }
621
+ }
622
+ }