@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,85 @@
1
+ import RunicMacroSupport
2
+ import Foundation
3
+
4
+ @ProviderDescriptorRegistration
5
+ @ProviderDescriptorDefinition
6
+ public enum DeepSeekProviderDescriptor {
7
+ static func makeDescriptor() -> ProviderDescriptor {
8
+ ProviderDescriptor(
9
+ id: .deepseek,
10
+ metadata: ProviderMetadata(
11
+ id: .deepseek,
12
+ displayName: "DeepSeek",
13
+ sessionLabel: "Balance",
14
+ weeklyLabel: "",
15
+ opusLabel: nil,
16
+ supportsOpus: false,
17
+ supportsCredits: true,
18
+ creditsHint: "Shows remaining balance from DeepSeek API",
19
+ toggleTitle: "Show DeepSeek usage",
20
+ cliName: "deepseek",
21
+ defaultEnabled: false,
22
+ isPrimaryProvider: false,
23
+ usesAccountFallback: false,
24
+ dashboardURL: "https://platform.deepseek.com/usage",
25
+ statusPageURL: nil,
26
+ usageCoverage: ProviderUsageCoverage(
27
+ supportsModelBreakdown: false,
28
+ supportsTokenMetrics: false,
29
+ supportsProjectAttribution: false)),
30
+ branding: ProviderBranding(
31
+ iconStyle: .deepseek,
32
+ iconResourceName: "ProviderIcon-deepseek",
33
+ color: ProviderColor(red: 76 / 255, green: 110 / 255, blue: 245 / 255)),
34
+ tokenCost: ProviderTokenCostConfig(
35
+ supportsTokenCost: false,
36
+ noDataMessage: { "DeepSeek cost summary is not supported." }),
37
+ fetchPlan: ProviderFetchPlan(
38
+ sourceModes: [.auto, .cli],
39
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [DeepSeekAPIFetchStrategy()] })),
40
+ cli: ProviderCLIConfig(
41
+ name: "deepseek",
42
+ aliases: ["ds"],
43
+ versionDetector: nil))
44
+ }
45
+ }
46
+
47
+ struct DeepSeekAPIFetchStrategy: ProviderFetchStrategy {
48
+ let id: String = "deepseek.api"
49
+ let kind: ProviderFetchKind = .apiToken
50
+
51
+ func isAvailable(_ context: ProviderFetchContext) async -> Bool {
52
+ Self.resolveTokenResolution(environment: context.env) != nil
53
+ }
54
+
55
+ func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
56
+ guard let tokenRes = Self.resolveTokenResolution(environment: context.env) else {
57
+ throw DeepSeekSettingsError.missingToken
58
+ }
59
+ let usage = try await DeepSeekUsageFetcher.fetchBalance(apiKey: tokenRes.token)
60
+ return self.makeResult(
61
+ usage: usage.toUsageSnapshot(),
62
+ credits: usage.toCreditsSnapshot(),
63
+ sourceLabel: tokenRes.source.rawValue)
64
+ }
65
+
66
+ func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
67
+ false
68
+ }
69
+
70
+ private static func resolveTokenResolution(environment: [String: String]) -> ProviderTokenResolution? {
71
+ ProviderTokenResolver.deepSeekResolution(environment: environment)
72
+ }
73
+ }
74
+
75
+ enum DeepSeekSettingsError: LocalizedError, Sendable {
76
+ case missingToken
77
+
78
+ var errorDescription: String? {
79
+ switch self {
80
+ case .missingToken:
81
+ "DeepSeek API key not found. Set it in Preferences → Providers → DeepSeek or " +
82
+ "export DEEPSEEK_API_KEY."
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,108 @@
1
+ import Foundation
2
+
3
+ struct DeepSeekBalanceResponse: Decodable {
4
+ let is_available: Bool?
5
+ let balance_infos: [BalanceInfo]?
6
+
7
+ struct BalanceInfo: Decodable {
8
+ let currency: String?
9
+ let total_balance: String?
10
+ let granted_balance: String?
11
+ let topped_up_balance: String?
12
+
13
+ var totalBalanceValue: Double? {
14
+ Self.parse(self.total_balance) ??
15
+ Self.parse(self.topped_up_balance) ??
16
+ Self.parse(self.granted_balance)
17
+ }
18
+
19
+ private static func parse(_ raw: String?) -> Double? {
20
+ guard let raw = raw?.trimmingCharacters(in: .whitespacesAndNewlines), !raw.isEmpty else {
21
+ return nil
22
+ }
23
+ return Double(raw)
24
+ }
25
+ }
26
+ }
27
+
28
+ struct DeepSeekUsageFetcher {
29
+ static let apiURL = URL(string: "https://api.deepseek.com/user/balance")!
30
+
31
+ static func fetchBalance(apiKey: String) async throws -> DeepSeekBalanceResponse {
32
+ var request = URLRequest(url: apiURL)
33
+ request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
34
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
35
+
36
+ let (data, response) = try await URLSession.shared.data(for: request)
37
+
38
+ guard let httpResponse = response as? HTTPURLResponse else {
39
+ throw DeepSeekAPIError.invalidResponse
40
+ }
41
+
42
+ guard httpResponse.statusCode == 200 else {
43
+ let body = String(data: data, encoding: .utf8)?
44
+ .trimmingCharacters(in: .whitespacesAndNewlines)
45
+ throw DeepSeekAPIError.httpError(
46
+ statusCode: httpResponse.statusCode,
47
+ body: body?.isEmpty == false ? body : nil)
48
+ }
49
+
50
+ let decoder = JSONDecoder()
51
+ return try decoder.decode(DeepSeekBalanceResponse.self, from: data)
52
+ }
53
+ }
54
+
55
+ extension DeepSeekBalanceResponse {
56
+ private var preferredBalanceInfo: BalanceInfo? {
57
+ guard let infos = self.balance_infos, !infos.isEmpty else { return nil }
58
+ if let funded = infos.first(where: { ($0.totalBalanceValue ?? 0) > 0 }) {
59
+ return funded
60
+ }
61
+ return infos.first
62
+ }
63
+
64
+ private var remainingBalance: Double {
65
+ self.preferredBalanceInfo?.totalBalanceValue ?? 0
66
+ }
67
+
68
+ private var currencyCode: String {
69
+ self.preferredBalanceInfo?.currency?.uppercased() ?? "USD"
70
+ }
71
+
72
+ func toUsageSnapshot() -> UsageSnapshot {
73
+ UsageSnapshot(
74
+ primary: RateWindow(
75
+ usedPercent: 0,
76
+ windowMinutes: nil,
77
+ resetsAt: nil,
78
+ resetDescription: "Balance: \(String(format: "%.2f", self.remainingBalance)) \(self.currencyCode)"),
79
+ secondary: nil,
80
+ tertiary: nil,
81
+ updatedAt: Date(),
82
+ identity: nil)
83
+ }
84
+
85
+ func toCreditsSnapshot() -> CreditsSnapshot {
86
+ CreditsSnapshot(
87
+ remaining: self.remainingBalance,
88
+ events: [],
89
+ updatedAt: Date())
90
+ }
91
+ }
92
+
93
+ enum DeepSeekAPIError: LocalizedError, Sendable {
94
+ case invalidResponse
95
+ case httpError(statusCode: Int, body: String?)
96
+
97
+ var errorDescription: String? {
98
+ switch self {
99
+ case .invalidResponse:
100
+ return "Invalid response from DeepSeek API"
101
+ case let .httpError(statusCode, body):
102
+ if let body, !body.isEmpty {
103
+ return "DeepSeek API returned status code \(statusCode): \(body)"
104
+ }
105
+ return "DeepSeek API returned status code \(statusCode)"
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,398 @@
1
+ import Foundation
2
+ #if os(macOS)
3
+ import SQLite3
4
+ #endif
5
+
6
+ #if os(macOS)
7
+ enum FactoryLocalStorageImporter {
8
+ struct TokenInfo: Sendable {
9
+ let refreshToken: String
10
+ let accessToken: String?
11
+ let organizationID: String?
12
+ let sourceLabel: String
13
+ }
14
+
15
+ static func importWorkOSTokens(logger: ((String) -> Void)? = nil) -> [TokenInfo] {
16
+ let log: (String) -> Void = { msg in logger?("[factory-storage] \(msg)") }
17
+ var tokens: [TokenInfo] = []
18
+
19
+ let safariCandidates = self.safariLocalStorageCandidates()
20
+ let chromeCandidates = self.chromeLocalStorageCandidates()
21
+ if !safariCandidates.isEmpty {
22
+ log("Safari local storage candidates: \(safariCandidates.count)")
23
+ }
24
+ if !chromeCandidates.isEmpty {
25
+ log("Chrome local storage candidates: \(chromeCandidates.count)")
26
+ }
27
+
28
+ let candidates = safariCandidates + chromeCandidates
29
+ for candidate in candidates {
30
+ let match: WorkOSTokenMatch? = switch candidate.kind {
31
+ case let .chromeLevelDB(levelDBURL):
32
+ self.readWorkOSToken(from: levelDBURL)
33
+ case let .safariSQLite(dbURL):
34
+ self.readWorkOSTokenFromSafariSQLite(dbURL: dbURL, logger: log)
35
+ }
36
+ guard let token = match else { continue }
37
+ log("Found WorkOS refresh token in \(candidate.label)")
38
+ tokens.append(TokenInfo(
39
+ refreshToken: token.refreshToken,
40
+ accessToken: token.accessToken,
41
+ organizationID: token.organizationID,
42
+ sourceLabel: candidate.label))
43
+ }
44
+
45
+ if tokens.isEmpty {
46
+ log("No WorkOS refresh token found in browser local storage")
47
+ }
48
+
49
+ return tokens
50
+ }
51
+
52
+ static func hasSafariWorkOSRefreshToken() -> Bool {
53
+ for candidate in self.safariLocalStorageCandidates() {
54
+ guard case let .safariSQLite(dbURL) = candidate.kind else { continue }
55
+ if self.readWorkOSTokenFromSafariSQLite(dbURL: dbURL) != nil {
56
+ return true
57
+ }
58
+ }
59
+ return false
60
+ }
61
+
62
+ // MARK: - Chrome local storage discovery
63
+
64
+ private enum LocalStorageSourceKind: Sendable {
65
+ case chromeLevelDB(URL)
66
+ case safariSQLite(URL)
67
+ }
68
+
69
+ private struct LocalStorageCandidate: Sendable {
70
+ let label: String
71
+ let kind: LocalStorageSourceKind
72
+ }
73
+
74
+ private static func chromeLocalStorageCandidates() -> [LocalStorageCandidate] {
75
+ let roots: [(url: URL, labelPrefix: String)] = self.candidateHomes().flatMap { home in
76
+ let appSupport = home
77
+ .appendingPathComponent("Library")
78
+ .appendingPathComponent("Application Support")
79
+ return [
80
+ (appSupport.appendingPathComponent("Google").appendingPathComponent("Chrome"), "Chrome"),
81
+ (appSupport.appendingPathComponent("Google").appendingPathComponent("Chrome Beta"), "Chrome Beta"),
82
+ (appSupport.appendingPathComponent("Google").appendingPathComponent("Chrome Canary"), "Chrome Canary"),
83
+ (appSupport.appendingPathComponent("Arc").appendingPathComponent("User Data"), "Arc"),
84
+ (appSupport.appendingPathComponent("Arc Beta").appendingPathComponent("User Data"), "Arc Beta"),
85
+ (appSupport.appendingPathComponent("Arc Canary").appendingPathComponent("User Data"), "Arc Canary"),
86
+ (
87
+ appSupport
88
+ .appendingPathComponent("com.openai.atlas")
89
+ .appendingPathComponent("browser-data")
90
+ .appendingPathComponent("host"),
91
+ "ChatGPT Atlas"),
92
+ (appSupport.appendingPathComponent("Chromium"), "Chromium"),
93
+ ]
94
+ }
95
+
96
+ var candidates: [LocalStorageCandidate] = []
97
+ for root in roots {
98
+ candidates.append(contentsOf: self.chromeProfileLocalStorageDirs(
99
+ root: root.url,
100
+ labelPrefix: root.labelPrefix))
101
+ }
102
+ return candidates
103
+ }
104
+
105
+ private static func chromeProfileLocalStorageDirs(root: URL, labelPrefix: String) -> [LocalStorageCandidate] {
106
+ guard let entries = try? FileManager.default.contentsOfDirectory(
107
+ at: root,
108
+ includingPropertiesForKeys: [.isDirectoryKey],
109
+ options: [.skipsHiddenFiles])
110
+ else { return [] }
111
+
112
+ let profileDirs = entries.filter { url in
113
+ guard let isDir = (try? url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory), isDir else {
114
+ return false
115
+ }
116
+ let name = url.lastPathComponent
117
+ return name == "Default" || name.hasPrefix("Profile ") || name.hasPrefix("user-")
118
+ }
119
+ .sorted { $0.lastPathComponent < $1.lastPathComponent }
120
+
121
+ return profileDirs.compactMap { dir in
122
+ let levelDBURL = dir.appendingPathComponent("Local Storage").appendingPathComponent("leveldb")
123
+ guard FileManager.default.fileExists(atPath: levelDBURL.path) else { return nil }
124
+ let label = "\(labelPrefix) \(dir.lastPathComponent)"
125
+ return LocalStorageCandidate(label: label, kind: .chromeLevelDB(levelDBURL))
126
+ }
127
+ }
128
+
129
+ private static func safariLocalStorageCandidates() -> [LocalStorageCandidate] {
130
+ let root = FileManager.default.homeDirectoryForCurrentUser
131
+ .appendingPathComponent("Library")
132
+ .appendingPathComponent("Containers")
133
+ .appendingPathComponent("com.apple.Safari")
134
+ .appendingPathComponent("Data")
135
+ .appendingPathComponent("Library")
136
+ .appendingPathComponent("WebKit")
137
+ .appendingPathComponent("WebsiteData")
138
+ .appendingPathComponent("Default")
139
+
140
+ guard FileManager.default.fileExists(atPath: root.path) else { return [] }
141
+
142
+ let targets = ["app.factory.ai", "auth.factory.ai"]
143
+ var candidates: [LocalStorageCandidate] = []
144
+
145
+ guard let enumerator = FileManager.default.enumerator(
146
+ at: root,
147
+ includingPropertiesForKeys: [.isRegularFileKey],
148
+ options: [.skipsHiddenFiles])
149
+ else { return [] }
150
+
151
+ for case let fileURL as URL in enumerator {
152
+ guard fileURL.lastPathComponent == "origin" else { continue }
153
+ guard (try? fileURL.resourceValues(forKeys: [.isRegularFileKey]).isRegularFile) == true else {
154
+ continue
155
+ }
156
+ guard let data = try? Data(contentsOf: fileURL, options: [.mappedIfSafe]) else { continue }
157
+ let ascii = String(data: data, encoding: .utf8) ?? String(data: data, encoding: .isoLatin1) ?? ""
158
+ guard targets.contains(where: { ascii.contains($0) }) else { continue }
159
+
160
+ let storageURL = fileURL.deletingLastPathComponent()
161
+ .appendingPathComponent("LocalStorage")
162
+ .appendingPathComponent("localstorage.sqlite3")
163
+ guard FileManager.default.fileExists(atPath: storageURL.path) else { continue }
164
+ let host = self.extractSafariOriginHost(from: ascii) ?? "app.factory.ai"
165
+ candidates.append(LocalStorageCandidate(label: "Safari (\(host))", kind: .safariSQLite(storageURL)))
166
+ }
167
+
168
+ var seen = Set<String>()
169
+ return candidates.filter { candidate in
170
+ let key: String = switch candidate.kind {
171
+ case let .chromeLevelDB(url):
172
+ url.path
173
+ case let .safariSQLite(url):
174
+ url.path
175
+ }
176
+ if seen.contains(key) { return false }
177
+ seen.insert(key)
178
+ return true
179
+ }
180
+ }
181
+
182
+ private static func extractSafariOriginHost(from ascii: String) -> String? {
183
+ let targets = ["app.factory.ai", "auth.factory.ai", "factory.ai"]
184
+ for host in targets where ascii.contains(host) {
185
+ return host
186
+ }
187
+ return nil
188
+ }
189
+
190
+ private static func candidateHomes() -> [URL] {
191
+ var homes: [URL] = []
192
+ homes.append(FileManager.default.homeDirectoryForCurrentUser)
193
+ if let userHome = NSHomeDirectoryForUser(NSUserName()) {
194
+ homes.append(URL(fileURLWithPath: userHome))
195
+ }
196
+ if let envHome = ProcessInfo.processInfo.environment["HOME"], !envHome.isEmpty {
197
+ homes.append(URL(fileURLWithPath: envHome))
198
+ }
199
+ var seen = Set<String>()
200
+ return homes.filter { home in
201
+ let path = home.path
202
+ guard !seen.contains(path) else { return false }
203
+ seen.insert(path)
204
+ return true
205
+ }
206
+ }
207
+
208
+ // MARK: - Token extraction
209
+
210
+ private struct WorkOSTokenMatch: Sendable {
211
+ let refreshToken: String
212
+ let accessToken: String?
213
+ let organizationID: String?
214
+ }
215
+
216
+ private static func readWorkOSToken(from levelDBURL: URL) -> WorkOSTokenMatch? {
217
+ guard let entries = try? FileManager.default.contentsOfDirectory(
218
+ at: levelDBURL,
219
+ includingPropertiesForKeys: [.contentModificationDateKey],
220
+ options: [.skipsHiddenFiles])
221
+ else { return nil }
222
+
223
+ let files = entries.filter { url in
224
+ let ext = url.pathExtension.lowercased()
225
+ return ext == "ldb" || ext == "log"
226
+ }
227
+ .sorted { lhs, rhs in
228
+ let left = (try? lhs.resourceValues(forKeys: [.contentModificationDateKey]).contentModificationDate)
229
+ let right = (try? rhs.resourceValues(forKeys: [.contentModificationDateKey]).contentModificationDate)
230
+ return (left ?? .distantPast) > (right ?? .distantPast)
231
+ }
232
+
233
+ for file in files {
234
+ guard let data = try? Data(contentsOf: file, options: [.mappedIfSafe]) else { continue }
235
+ if let match = self.extractWorkOSToken(from: data) {
236
+ return match
237
+ }
238
+ }
239
+ return nil
240
+ }
241
+
242
+ private static func extractWorkOSToken(from data: Data) -> WorkOSTokenMatch? {
243
+ guard let contents = String(data: data, encoding: .utf8) ??
244
+ String(data: data, encoding: .isoLatin1)
245
+ else { return nil }
246
+ guard contents.contains("workos:refresh-token") else { return nil }
247
+
248
+ let refreshToken = self.matchToken(
249
+ in: contents,
250
+ pattern: "workos:refresh-token[^A-Za-z0-9_-]*([A-Za-z0-9_-]{20,})")
251
+ guard let refreshToken else { return nil }
252
+
253
+ let accessToken = self.matchToken(
254
+ in: contents,
255
+ pattern: "workos:access-token[^A-Za-z0-9_-]*([A-Za-z0-9_-]{20,})")
256
+
257
+ let organizationID = self.extractOrganizationID(from: accessToken)
258
+ return WorkOSTokenMatch(
259
+ refreshToken: refreshToken,
260
+ accessToken: accessToken,
261
+ organizationID: organizationID)
262
+ }
263
+
264
+ private static func readWorkOSTokenFromSafariSQLite(
265
+ dbURL: URL,
266
+ logger: ((String) -> Void)? = nil) -> WorkOSTokenMatch?
267
+ {
268
+ var db: OpaquePointer?
269
+ guard sqlite3_open_v2(dbURL.path, &db, SQLITE_OPEN_READONLY, nil) == SQLITE_OK else {
270
+ if let c = sqlite3_errmsg(db) {
271
+ logger?("Safari local storage open failed: \(String(cString: c))")
272
+ }
273
+ return nil
274
+ }
275
+ defer { sqlite3_close(db) }
276
+
277
+ sqlite3_busy_timeout(db, 250)
278
+ let tables = self.fetchTableNames(db: db, logger: logger)
279
+ if tables.isEmpty {
280
+ logger?("Safari local storage table lookup returned no tables")
281
+ }
282
+ let table = tables
283
+ .contains("ItemTable") ? "ItemTable" : (tables.contains("localstorage") ? "localstorage" : nil)
284
+ guard let table else {
285
+ logger?("Safari local storage missing ItemTable/localstorage tables (found: \(tables.sorted()))")
286
+ return nil
287
+ }
288
+
289
+ let refreshToken = self.fetchLocalStorageValue(db: db, table: table, key: "workos:refresh-token")
290
+ guard let refreshToken, !refreshToken.isEmpty else {
291
+ logger?("Safari local storage missing workos:refresh-token")
292
+ return nil
293
+ }
294
+ let accessToken = self.fetchLocalStorageValue(db: db, table: table, key: "workos:access-token")
295
+
296
+ let organizationID = self.extractOrganizationID(from: accessToken)
297
+ return WorkOSTokenMatch(
298
+ refreshToken: refreshToken,
299
+ accessToken: accessToken,
300
+ organizationID: organizationID)
301
+ }
302
+
303
+ private static func extractOrganizationID(from accessToken: String?) -> String? {
304
+ guard let accessToken, accessToken.contains(".") else { return nil }
305
+ let parts = accessToken.split(separator: ".")
306
+ guard parts.count == 3 else { return nil }
307
+ let payload = String(parts[1])
308
+ .replacingOccurrences(of: "-", with: "+")
309
+ .replacingOccurrences(of: "_", with: "/")
310
+ let padded = payload + String(repeating: "=", count: (4 - payload.count % 4) % 4)
311
+ guard let data = Data(base64Encoded: padded),
312
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
313
+ else {
314
+ return nil
315
+ }
316
+ return json["org_id"] as? String
317
+ }
318
+
319
+ private static func fetchTableNames(db: OpaquePointer?, logger: ((String) -> Void)? = nil) -> Set<String> {
320
+ let sql = "SELECT name FROM sqlite_master WHERE type='table'"
321
+ var stmt: OpaquePointer?
322
+ guard sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK else {
323
+ if let c = sqlite3_errmsg(db) {
324
+ logger?("Safari local storage table query failed: \(String(cString: c))")
325
+ }
326
+ return []
327
+ }
328
+ defer { sqlite3_finalize(stmt) }
329
+ var names = Set<String>()
330
+ while true {
331
+ let step = sqlite3_step(stmt)
332
+ if step == SQLITE_ROW {
333
+ if let c = sqlite3_column_text(stmt, 0) {
334
+ names.insert(String(cString: c))
335
+ }
336
+ } else {
337
+ if step != SQLITE_DONE, let c = sqlite3_errmsg(db) {
338
+ logger?("Safari local storage table query failed: \(String(cString: c))")
339
+ }
340
+ break
341
+ }
342
+ }
343
+ return names
344
+ }
345
+
346
+ private static func fetchLocalStorageValue(db: OpaquePointer?, table: String, key: String) -> String? {
347
+ let sql = "SELECT value FROM \(table) WHERE key = ? LIMIT 1"
348
+ var stmt: OpaquePointer?
349
+ guard sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK else { return nil }
350
+ defer { sqlite3_finalize(stmt) }
351
+ let transient = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
352
+ _ = key.withCString { cString in
353
+ sqlite3_bind_text(stmt, 1, cString, -1, transient)
354
+ }
355
+ guard sqlite3_step(stmt) == SQLITE_ROW else { return nil }
356
+ return self.decodeSQLiteValue(stmt: stmt, index: 0)
357
+ }
358
+
359
+ private static func decodeSQLiteValue(stmt: OpaquePointer?, index: Int32) -> String? {
360
+ let type = sqlite3_column_type(stmt, index)
361
+ switch type {
362
+ case SQLITE_TEXT:
363
+ guard let c = sqlite3_column_text(stmt, index) else { return nil }
364
+ return String(cString: c)
365
+ case SQLITE_BLOB:
366
+ guard let bytes = sqlite3_column_blob(stmt, index) else { return nil }
367
+ let count = Int(sqlite3_column_bytes(stmt, index))
368
+ let data = Data(bytes: bytes, count: count)
369
+ return self.decodeValueData(data)
370
+ default:
371
+ return nil
372
+ }
373
+ }
374
+
375
+ private static func decodeValueData(_ data: Data) -> String? {
376
+ if let decoded = String(data: data, encoding: .utf16LittleEndian) {
377
+ return decoded.trimmingCharacters(in: .controlCharacters)
378
+ }
379
+ if let decoded = String(data: data, encoding: .utf8) {
380
+ return decoded.trimmingCharacters(in: .controlCharacters)
381
+ }
382
+ if let decoded = String(data: data, encoding: .isoLatin1) {
383
+ return decoded.trimmingCharacters(in: .controlCharacters)
384
+ }
385
+ return nil
386
+ }
387
+
388
+ private static func matchToken(in contents: String, pattern: String) -> String? {
389
+ guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else { return nil }
390
+ let range = NSRange(contents.startIndex..<contents.endIndex, in: contents)
391
+ guard let match = regex.matches(in: contents, options: [], range: range).last else { return nil }
392
+ guard match.numberOfRanges > 1,
393
+ let tokenRange = Range(match.range(at: 1), in: contents)
394
+ else { return nil }
395
+ return String(contents[tokenRange])
396
+ }
397
+ }
398
+ #endif
@@ -0,0 +1,65 @@
1
+ import RunicMacroSupport
2
+ import Foundation
3
+
4
+ @ProviderDescriptorRegistration
5
+ @ProviderDescriptorDefinition
6
+ public enum FactoryProviderDescriptor {
7
+ static func makeDescriptor() -> ProviderDescriptor {
8
+ ProviderDescriptor(
9
+ id: .factory,
10
+ metadata: ProviderMetadata(
11
+ id: .factory,
12
+ displayName: "Droid",
13
+ sessionLabel: "Standard",
14
+ weeklyLabel: "Premium",
15
+ opusLabel: nil,
16
+ supportsOpus: false,
17
+ supportsCredits: false,
18
+ creditsHint: "",
19
+ toggleTitle: "Show Droid usage",
20
+ cliName: "factory",
21
+ defaultEnabled: false,
22
+ isPrimaryProvider: false,
23
+ usesAccountFallback: false,
24
+ browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder,
25
+ dashboardURL: "https://app.factory.ai/settings/billing",
26
+ statusPageURL: "https://status.factory.ai",
27
+ statusLinkURL: nil,
28
+ usageCoverage: ProviderUsageCoverage(
29
+ supportsModelBreakdown: false,
30
+ supportsTokenMetrics: false,
31
+ supportsProjectAttribution: false)),
32
+ branding: ProviderBranding(
33
+ iconStyle: .factory,
34
+ iconResourceName: "ProviderIcon-factory",
35
+ color: ProviderColor(red: 255 / 255, green: 107 / 255, blue: 53 / 255)),
36
+ tokenCost: ProviderTokenCostConfig(
37
+ supportsTokenCost: false,
38
+ noDataMessage: { "Droid cost summary is not supported." }),
39
+ fetchPlan: ProviderFetchPlan(
40
+ sourceModes: [.auto, .cli],
41
+ pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [FactoryStatusFetchStrategy()] })),
42
+ cli: ProviderCLIConfig(
43
+ name: "factory",
44
+ versionDetector: nil))
45
+ }
46
+ }
47
+
48
+ struct FactoryStatusFetchStrategy: ProviderFetchStrategy {
49
+ let id: String = "factory.web"
50
+ let kind: ProviderFetchKind = .web
51
+
52
+ func isAvailable(_: ProviderFetchContext) async -> Bool { true }
53
+
54
+ func fetch(_: ProviderFetchContext) async throws -> ProviderFetchResult {
55
+ let probe = FactoryStatusProbe()
56
+ let snap = try await probe.fetch()
57
+ return self.makeResult(
58
+ usage: snap.toUsageSnapshot(),
59
+ sourceLabel: "web")
60
+ }
61
+
62
+ func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
63
+ false
64
+ }
65
+ }