@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,87 @@
1
+ import RunicCore
2
+ import Foundation
3
+
4
+ enum CLIRenderer {
5
+ static func renderText(
6
+ provider: UsageProvider,
7
+ snapshot: UsageSnapshot,
8
+ credits: CreditsSnapshot?,
9
+ context: RenderContext) -> String
10
+ {
11
+ let meta = ProviderDescriptorRegistry.descriptor(for: provider).metadata
12
+ var lines: [String] = []
13
+ lines.append(self.headerLine(context.header, useColor: context.useColor))
14
+
15
+ lines.append(self.rateLine(title: meta.sessionLabel, window: snapshot.primary, useColor: context.useColor))
16
+ if let reset = snapshot.primary.resetDescription {
17
+ lines.append(self.resetLine(reset))
18
+ }
19
+
20
+ if let weekly = snapshot.secondary {
21
+ lines.append(self.rateLine(title: meta.weeklyLabel, window: weekly, useColor: context.useColor))
22
+ if let reset = weekly.resetDescription {
23
+ lines.append(self.resetLine(reset))
24
+ }
25
+ }
26
+
27
+ if meta.supportsOpus, let opus = snapshot.tertiary {
28
+ lines.append(self.rateLine(title: meta.opusLabel ?? "Sonnet", window: opus, useColor: context.useColor))
29
+ if let reset = opus.resetDescription {
30
+ lines.append(self.resetLine(reset))
31
+ }
32
+ }
33
+
34
+ if provider == .codex, let credits {
35
+ lines.append("Credits: \(UsageFormatter.creditsString(from: credits.remaining))")
36
+ }
37
+
38
+ if let email = snapshot.accountEmail(for: provider), !email.isEmpty {
39
+ lines.append("Account: \(email)")
40
+ }
41
+ if let plan = snapshot.loginMethod(for: provider), !plan.isEmpty {
42
+ lines.append("Plan: \(plan.capitalized)")
43
+ }
44
+
45
+ return lines.joined(separator: "\n")
46
+ }
47
+
48
+ static func rateLine(title: String, window: RateWindow, useColor: Bool) -> String {
49
+ let text = UsageFormatter.usageLine(remaining: window.remainingPercent, used: window.usedPercent)
50
+ let colored = self.colorizeUsage(text, remainingPercent: window.remainingPercent, useColor: useColor)
51
+ return "\(title): \(colored)"
52
+ }
53
+
54
+ private static func resetLine(_ reset: String) -> String {
55
+ let trimmed = reset.trimmingCharacters(in: .whitespacesAndNewlines)
56
+ if trimmed.lowercased().hasPrefix("resets") { return trimmed }
57
+ return "Resets \(trimmed)"
58
+ }
59
+
60
+ private static func headerLine(_ header: String, useColor: Bool) -> String {
61
+ guard useColor else { return header }
62
+ return self.ansi("1;36", header)
63
+ }
64
+
65
+ private static func colorizeUsage(_ text: String, remainingPercent: Double, useColor: Bool) -> String {
66
+ guard useColor else { return text }
67
+
68
+ let code = switch remainingPercent {
69
+ case ..<10:
70
+ "31" // red
71
+ case ..<25:
72
+ "33" // yellow
73
+ default:
74
+ "32" // green
75
+ }
76
+ return self.ansi(code, text)
77
+ }
78
+
79
+ private static func ansi(_ code: String, _ text: String) -> String {
80
+ "\u{001B}[\(code)m\(text)\u{001B}[0m"
81
+ }
82
+ }
83
+
84
+ struct RenderContext {
85
+ let header: String
86
+ let useColor: Bool
87
+ }
@@ -0,0 +1,588 @@
1
+ /// EnhancedUsageCommand.swift
2
+ /// Runic CLI - Enhanced Usage Display
3
+ ///
4
+ /// Enhanced version of the usage command with additional features:
5
+ /// - Multiple display modes (summary, detailed, breakdown)
6
+ /// - Historical trending
7
+ /// - Cost projections
8
+ /// - Comparative analysis across providers
9
+ ///
10
+ /// Usage:
11
+ /// runic usage-enhanced [options]
12
+ ///
13
+ /// Examples:
14
+ /// runic usage-enhanced --mode summary # Quick overview
15
+ /// runic usage-enhanced --mode detailed # Detailed breakdown
16
+ /// runic usage-enhanced --mode trending # Usage trends
17
+
18
+ import RunicCore
19
+ import Helix
20
+ import Foundation
21
+
22
+ /// Enhanced usage command with additional analytics
23
+ public enum EnhancedUsageCommand {
24
+
25
+ /// Command signature defining available options and flags
26
+ public static var signature: CommandSignature {
27
+ CommandSignature(
28
+ options: [
29
+ OptionDefinition(
30
+ label: "provider",
31
+ names: [.long("provider"), .short("p")],
32
+ help: "Filter by provider (claude, codex, etc.)"),
33
+ OptionDefinition(
34
+ label: "mode",
35
+ names: [.long("mode"), .short("m")],
36
+ help: "Display mode: summary | detailed | breakdown | trending"),
37
+ OptionDefinition(
38
+ label: "days",
39
+ names: [.long("days"), .short("d")],
40
+ help: "Number of days for trending analysis (default: 7)"),
41
+ OptionDefinition(
42
+ label: "format",
43
+ names: [.long("format")],
44
+ help: "Output format: text | json"),
45
+ ],
46
+ flags: [
47
+ FlagDefinition(
48
+ label: "json",
49
+ names: [.long("json"), .short("j")],
50
+ help: "Output in JSON format"),
51
+ FlagDefinition(
52
+ label: "pretty",
53
+ names: [.long("pretty")],
54
+ help: "Pretty-print JSON output"),
55
+ FlagDefinition(
56
+ label: "noColor",
57
+ names: [.long("no-color")],
58
+ help: "Disable ANSI color codes"),
59
+ FlagDefinition(
60
+ label: "showCost",
61
+ names: [.long("show-cost")],
62
+ help: "Include cost information"),
63
+ FlagDefinition(
64
+ label: "showProjected",
65
+ names: [.long("projected")],
66
+ help: "Show projected usage at current rate"),
67
+ FlagDefinition(
68
+ label: "compare",
69
+ names: [.long("compare")],
70
+ help: "Compare usage across providers"),
71
+ ])
72
+ }
73
+
74
+ /// Command descriptor for registration
75
+ public static var descriptor: CommandDescriptor {
76
+ CommandDescriptor(
77
+ name: "usage-enhanced",
78
+ abstract: "Enhanced usage display with analytics",
79
+ discussion: """
80
+ Enhanced version of the usage command with additional features:
81
+
82
+ DISPLAY MODES:
83
+ summary - Quick overview of current usage (default)
84
+ detailed - Detailed breakdown with all windows
85
+ breakdown - Token breakdown by type (input/output/cache)
86
+ trending - Historical usage trends
87
+
88
+ EXAMPLES:
89
+ runic usage-enhanced
90
+ Show summary for all providers
91
+
92
+ runic usage-enhanced --mode detailed --show-cost
93
+ Detailed view with cost information
94
+
95
+ runic usage-enhanced --mode trending --days 30
96
+ Show 30-day usage trends
97
+
98
+ runic usage-enhanced --compare --json
99
+ Compare providers in JSON format
100
+
101
+ runic usage-enhanced --projected
102
+ Show projected usage based on current rate
103
+ """,
104
+ signature: signature)
105
+ }
106
+
107
+ /// Execute the enhanced usage command
108
+ public static func run(_ invocation: CommandInvocation) async {
109
+ let config = parseConfiguration(invocation)
110
+
111
+ do {
112
+ let data = try await loadUsageData(
113
+ providers: config.providers,
114
+ days: config.days,
115
+ includeCost: config.showCost)
116
+
117
+ if config.isJSON {
118
+ outputJSON(data, config: config)
119
+ } else {
120
+ outputText(data, config: config)
121
+ }
122
+ } catch {
123
+ exitWithError("Failed to load usage data: \(error.localizedDescription)")
124
+ }
125
+ }
126
+
127
+ // MARK: - Configuration
128
+
129
+ private struct Configuration {
130
+ let providers: [UsageProvider]
131
+ let mode: DisplayMode
132
+ let days: Int
133
+ let isJSON: Bool
134
+ let isPretty: Bool
135
+ let useColor: Bool
136
+ let showCost: Bool
137
+ let showProjected: Bool
138
+ let compare: Bool
139
+
140
+ enum DisplayMode: String {
141
+ case summary
142
+ case detailed
143
+ case breakdown
144
+ case trending
145
+ }
146
+ }
147
+
148
+ private static func parseConfiguration(_ invocation: CommandInvocation) -> Configuration {
149
+ let providerArg = invocation.parsedValues.options["provider"]?.first
150
+ let modeArg = invocation.parsedValues.options["mode"]?.first ?? "summary"
151
+ let daysArg = invocation.parsedValues.options["days"]?.first
152
+ let formatArg = invocation.parsedValues.options["format"]?.first ?? "text"
153
+
154
+ let isJSON = invocation.parsedValues.flags.contains("json")
155
+ || formatArg.lowercased() == "json"
156
+ let isPretty = invocation.parsedValues.flags.contains("pretty")
157
+ let noColor = invocation.parsedValues.flags.contains("noColor")
158
+ let showCost = invocation.parsedValues.flags.contains("showCost")
159
+ let showProjected = invocation.parsedValues.flags.contains("showProjected")
160
+ let compare = invocation.parsedValues.flags.contains("compare")
161
+
162
+ let mode = Configuration.DisplayMode(rawValue: modeArg.lowercased()) ?? .summary
163
+ let days = daysArg.flatMap(Int.init) ?? 7
164
+ let providers = resolveProviders(providerArg)
165
+
166
+ return Configuration(
167
+ providers: providers,
168
+ mode: mode,
169
+ days: days,
170
+ isJSON: isJSON,
171
+ isPretty: isPretty,
172
+ useColor: !noColor,
173
+ showCost: showCost,
174
+ showProjected: showProjected,
175
+ compare: compare)
176
+ }
177
+
178
+ private static func resolveProviders(_ arg: String?) -> [UsageProvider] {
179
+ guard let arg = arg?.lowercased().trimmingCharacters(in: .whitespaces) else {
180
+ return ProviderDescriptorRegistry.all.map(\.id)
181
+ }
182
+
183
+ if arg == "all" {
184
+ return ProviderDescriptorRegistry.all.map(\.id)
185
+ }
186
+
187
+ guard let provider = UsageProvider(rawValue: arg) else {
188
+ exitWithError("Unknown provider: \(arg)")
189
+ }
190
+
191
+ return [provider]
192
+ }
193
+
194
+ // MARK: - Data Models
195
+
196
+ private struct EnhancedUsageData: Encodable {
197
+ let providers: [ProviderUsageData]
198
+ let summary: UsageSummary?
199
+ let comparison: ProviderComparison?
200
+ }
201
+
202
+ private struct ProviderUsageData: Encodable {
203
+ let provider: UsageProvider
204
+ let providerName: String
205
+ let snapshot: UsageSnapshot
206
+ let credits: CreditsSnapshot?
207
+ let windows: [WindowData]
208
+ let breakdown: TokenBreakdown?
209
+ let trending: [DailyTrend]?
210
+ let projected: ProjectedUsage?
211
+ }
212
+
213
+ private struct WindowData: Encodable {
214
+ let name: String
215
+ let used: Int?
216
+ let limit: Int?
217
+ let usedPercent: Double
218
+ let remainingPercent: Double
219
+ let resetDescription: String?
220
+ }
221
+
222
+ private struct TokenBreakdown: Encodable {
223
+ let totalTokens: Int
224
+ let inputTokens: Int
225
+ let outputTokens: Int
226
+ let cacheCreationTokens: Int
227
+ let cacheReadTokens: Int
228
+ let inputPercent: Double
229
+ let outputPercent: Double
230
+ let cachePercent: Double
231
+ }
232
+
233
+ private struct DailyTrend: Encodable {
234
+ let date: String
235
+ let totalTokens: Int
236
+ let cost: Double?
237
+ }
238
+
239
+ private struct ProjectedUsage: Encodable {
240
+ let currentUsed: Int
241
+ let projectedTotal: Int
242
+ let projectedAtEndOfPeriod: Int
243
+ let riskLevel: String
244
+ }
245
+
246
+ private struct UsageSummary: Encodable {
247
+ let totalProviders: Int
248
+ let totalTokens: Int
249
+ let totalCost: Double?
250
+ let highestUsageProvider: String
251
+ let averageUsagePercent: Double
252
+ }
253
+
254
+ private struct ProviderComparison: Encodable {
255
+ let comparisons: [ProviderComparisonItem]
256
+ let mostEfficient: String?
257
+ let mostExpensive: String?
258
+ }
259
+
260
+ private struct ProviderComparisonItem: Encodable {
261
+ let provider: String
262
+ let totalTokens: Int
263
+ let cost: Double?
264
+ let efficiency: Double
265
+ }
266
+
267
+ // MARK: - Data Loading
268
+
269
+ private static func loadUsageData(
270
+ providers: [UsageProvider],
271
+ days: Int,
272
+ includeCost: Bool
273
+ ) async throws -> EnhancedUsageData {
274
+ let fetcher = UsageFetcher()
275
+ let _ = includeCost ? CostUsageFetcher() : nil
276
+ var providerData: [ProviderUsageData] = []
277
+
278
+ for provider in providers {
279
+ let descriptor = ProviderDescriptorRegistry.descriptor(for: provider)
280
+ let context = ProviderFetchContext(
281
+ runtime: .cli,
282
+ sourceMode: .cli,
283
+ includeCredits: true,
284
+ webTimeout: 60,
285
+ webDebugDumpHTML: false,
286
+ verbose: false,
287
+ env: ProcessInfo.processInfo.environment,
288
+ settings: nil,
289
+ fetcher: fetcher,
290
+ claudeFetcher: ClaudeUsageFetcher())
291
+
292
+ let outcome = await descriptor.fetchOutcome(context: context)
293
+
294
+ if case .success(let result) = outcome.result {
295
+ let trending = try? await loadTrendingData(
296
+ provider: provider,
297
+ days: days)
298
+
299
+ let data = buildProviderData(
300
+ provider: provider,
301
+ snapshot: result.usage,
302
+ credits: result.credits,
303
+ metadata: descriptor.metadata,
304
+ trending: trending)
305
+
306
+ providerData.append(data)
307
+ }
308
+ }
309
+
310
+ let summary = buildSummary(providerData: providerData)
311
+ let comparison = buildComparison(providerData: providerData)
312
+
313
+ return EnhancedUsageData(
314
+ providers: providerData,
315
+ summary: summary,
316
+ comparison: comparison)
317
+ }
318
+
319
+ private static func buildProviderData(
320
+ provider: UsageProvider,
321
+ snapshot: UsageSnapshot,
322
+ credits: CreditsSnapshot?,
323
+ metadata: ProviderMetadata,
324
+ trending: [DailyTrend]?
325
+ ) -> ProviderUsageData {
326
+ var windows: [WindowData] = []
327
+
328
+ windows.append(WindowData(
329
+ name: metadata.sessionLabel,
330
+ used: nil,
331
+ limit: nil,
332
+ usedPercent: snapshot.primary.usedPercent,
333
+ remainingPercent: snapshot.primary.remainingPercent,
334
+ resetDescription: snapshot.primary.resetDescription))
335
+
336
+ if let secondary = snapshot.secondary {
337
+ windows.append(WindowData(
338
+ name: metadata.weeklyLabel,
339
+ used: nil,
340
+ limit: nil,
341
+ usedPercent: secondary.usedPercent,
342
+ remainingPercent: secondary.remainingPercent,
343
+ resetDescription: secondary.resetDescription))
344
+ }
345
+
346
+ return ProviderUsageData(
347
+ provider: provider,
348
+ providerName: provider.rawValue.capitalized,
349
+ snapshot: snapshot,
350
+ credits: credits,
351
+ windows: windows,
352
+ breakdown: nil,
353
+ trending: trending,
354
+ projected: nil)
355
+ }
356
+
357
+ private static func loadTrendingData(
358
+ provider: UsageProvider,
359
+ days: Int
360
+ ) async throws -> [DailyTrend]? {
361
+ let source = UsageLedgerSourceFactory.source(for: provider, now: Date(), maxAgeDays: days)
362
+
363
+ guard let source else { return nil }
364
+
365
+ let entries = try await source.loadEntries()
366
+ let summaries = UsageLedgerAggregator.dailySummaries(
367
+ entries: entries,
368
+ timeZone: .current,
369
+ groupByProject: false)
370
+
371
+ return summaries.map { summary in
372
+ DailyTrend(
373
+ date: summary.dayKey,
374
+ totalTokens: summary.totals.totalTokens,
375
+ cost: summary.totals.costUSD)
376
+ }
377
+ }
378
+
379
+ private static func buildSummary(
380
+ providerData: [ProviderUsageData]
381
+ ) -> UsageSummary? {
382
+ guard !providerData.isEmpty else { return nil }
383
+
384
+ let totalTokens = providerData.reduce(0) { sum, data in
385
+ sum + data.windows.reduce(0) { $0 + ($1.used ?? 0) }
386
+ }
387
+
388
+ let averageUsage = providerData.reduce(0.0) { sum, data in
389
+ let providerAvg = data.windows.map(\.usedPercent).reduce(0.0, +) / Double(data.windows.count)
390
+ return sum + providerAvg
391
+ } / Double(providerData.count)
392
+
393
+ let highestProvider = providerData.max { lhs, rhs in
394
+ let lhsMax = lhs.windows.map(\.usedPercent).max() ?? 0
395
+ let rhsMax = rhs.windows.map(\.usedPercent).max() ?? 0
396
+ return lhsMax < rhsMax
397
+ }
398
+
399
+ return UsageSummary(
400
+ totalProviders: providerData.count,
401
+ totalTokens: totalTokens,
402
+ totalCost: nil,
403
+ highestUsageProvider: highestProvider?.providerName ?? "unknown",
404
+ averageUsagePercent: averageUsage)
405
+ }
406
+
407
+ private static func buildComparison(
408
+ providerData: [ProviderUsageData]
409
+ ) -> ProviderComparison? {
410
+ guard providerData.count > 1 else { return nil }
411
+
412
+ let comparisons = providerData.map { data in
413
+ let totalTokens = data.windows.reduce(0) { $0 + ($1.used ?? 0) }
414
+ let efficiency = data.windows.map(\.usedPercent).min() ?? 100
415
+
416
+ return ProviderComparisonItem(
417
+ provider: data.providerName,
418
+ totalTokens: totalTokens,
419
+ cost: nil,
420
+ efficiency: efficiency)
421
+ }
422
+
423
+ let mostEfficient = comparisons.min { $0.efficiency < $1.efficiency }?.provider
424
+
425
+ return ProviderComparison(
426
+ comparisons: comparisons,
427
+ mostEfficient: mostEfficient,
428
+ mostExpensive: nil)
429
+ }
430
+
431
+ // MARK: - Output Formatting
432
+
433
+ private static func outputJSON(_ data: EnhancedUsageData, config: Configuration) {
434
+ let encoder = JSONEncoder()
435
+ encoder.dateEncodingStrategy = .iso8601
436
+
437
+ if config.isPretty {
438
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
439
+ }
440
+
441
+ guard let jsonData = try? encoder.encode(data),
442
+ let text = String(data: jsonData, encoding: .utf8) else {
443
+ print("{}")
444
+ return
445
+ }
446
+
447
+ print(text)
448
+ }
449
+
450
+ private static func outputText(_ data: EnhancedUsageData, config: Configuration) {
451
+ switch config.mode {
452
+ case .summary:
453
+ outputSummaryMode(data, config: config)
454
+ case .detailed:
455
+ outputDetailedMode(data, config: config)
456
+ case .breakdown:
457
+ outputBreakdownMode(data, config: config)
458
+ case .trending:
459
+ outputTrendingMode(data, config: config)
460
+ }
461
+ }
462
+
463
+ private static func outputSummaryMode(_ data: EnhancedUsageData, config: Configuration) {
464
+ printHeader("Usage Summary", useColor: config.useColor)
465
+ print("")
466
+
467
+ if let summary = data.summary {
468
+ print("Providers: \(summary.totalProviders)")
469
+ print("Total Tokens: \(formatNumber(summary.totalTokens))")
470
+ print("Average Usage: \(String(format: "%.1f", summary.averageUsagePercent))%")
471
+ print("Highest Usage: \(summary.highestUsageProvider)")
472
+ print("")
473
+ }
474
+
475
+ for providerData in data.providers {
476
+ print(providerData.providerName)
477
+ for window in providerData.windows {
478
+ let bar = progressBar(
479
+ used: window.usedPercent,
480
+ width: 20,
481
+ useColor: config.useColor)
482
+ let percent = String(format: "%.1f", window.usedPercent)
483
+ print(" \(window.name): \(bar) \(percent)%")
484
+ }
485
+ print("")
486
+ }
487
+ }
488
+
489
+ private static func outputDetailedMode(_ data: EnhancedUsageData, config: Configuration) {
490
+ printHeader("Detailed Usage Information", useColor: config.useColor)
491
+ print("")
492
+
493
+ for (index, providerData) in data.providers.enumerated() {
494
+ if index > 0 { print("") }
495
+
496
+ if config.useColor {
497
+ print(ansi("1;36", providerData.providerName))
498
+ } else {
499
+ print(providerData.providerName)
500
+ }
501
+
502
+ for window in providerData.windows {
503
+ print("")
504
+ print(" \(window.name):")
505
+ if let used = window.used, let limit = window.limit {
506
+ print(" Used: \(formatNumber(used)) / \(formatNumber(limit))")
507
+ }
508
+ print(" Percent: \(String(format: "%.1f", window.usedPercent))%")
509
+ if let reset = window.resetDescription {
510
+ print(" Reset: \(reset)")
511
+ }
512
+ }
513
+ }
514
+ }
515
+
516
+ private static func outputBreakdownMode(_ data: EnhancedUsageData, config: Configuration) {
517
+ printHeader("Token Breakdown", useColor: config.useColor)
518
+ print("")
519
+ print("Note: Detailed token breakdown requires historical log data.")
520
+ print("Use 'runic insights --view models' for model-level breakdown.")
521
+ }
522
+
523
+ private static func outputTrendingMode(_ data: EnhancedUsageData, config: Configuration) {
524
+ printHeader("Usage Trends", useColor: config.useColor)
525
+ print("")
526
+
527
+ for providerData in data.providers {
528
+ print(providerData.providerName)
529
+
530
+ if let trends = providerData.trending, !trends.isEmpty {
531
+ for trend in trends.suffix(10) {
532
+ let tokens = formatNumber(trend.totalTokens)
533
+ let cost = trend.cost.map { String(format: "$%.4f", $0) } ?? "n/a"
534
+ print(" \(trend.date): \(tokens) tokens (\(cost))")
535
+ }
536
+ } else {
537
+ print(" No trending data available")
538
+ }
539
+ print("")
540
+ }
541
+ }
542
+
543
+ private static func progressBar(used: Double, width: Int, useColor: Bool) -> String {
544
+ let filled = Int((used / 100.0) * Double(width))
545
+ let empty = width - filled
546
+ let bar = String(repeating: "█", count: filled) + String(repeating: "░", count: empty)
547
+
548
+ guard useColor else { return "[\(bar)]" }
549
+
550
+ let color = switch used {
551
+ case 90...: "31" // red
552
+ case 75...: "33" // yellow
553
+ default: "32" // green
554
+ }
555
+
556
+ return "[\(ansi(color, bar))]"
557
+ }
558
+
559
+ private static func formatNumber(_ n: Int) -> String {
560
+ if n >= 1_000_000 {
561
+ return String(format: "%.1fM", Double(n) / 1_000_000)
562
+ } else if n >= 1_000 {
563
+ return String(format: "%.1fK", Double(n) / 1_000)
564
+ }
565
+ return "\(n)"
566
+ }
567
+
568
+ private static func printHeader(_ text: String, useColor: Bool) {
569
+ if useColor {
570
+ print(ansi("1;36", text))
571
+ } else {
572
+ print(text)
573
+ }
574
+ }
575
+
576
+ private static func ansi(_ code: String, _ text: String) -> String {
577
+ "\u{001B}[\(code)m\(text)\u{001B}[0m"
578
+ }
579
+
580
+ // MARK: - Error Handling
581
+
582
+ private static func exitWithError(_ message: String) -> Never {
583
+ if let data = ("Error: " + message + "\n").data(using: .utf8) {
584
+ FileHandle.standardError.write(data)
585
+ }
586
+ Foundation.exit(1)
587
+ }
588
+ }