@sriinnu/runic 1.2.0 → 2.11.1

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 (586) hide show
  1. package/CHANGELOG.md +267 -0
  2. package/LICENSE +373 -21
  3. package/README.md +98 -84
  4. package/THIRD-PARTY-NOTICES.md +651 -0
  5. package/package.json +14 -4
  6. package/.swiftformat +0 -50
  7. package/.swiftlint.yml +0 -160
  8. package/CONTRIBUTING.md +0 -456
  9. package/Icon.icns +0 -0
  10. package/Icon.icon/Assets/runic.png +0 -0
  11. package/Icon.icon/icon.json +0 -36
  12. package/Package.resolved +0 -51
  13. package/Package.swift +0 -136
  14. package/Packages/Helix/LICENSE +0 -21
  15. package/Packages/Helix/Package.swift +0 -29
  16. package/Packages/Helix/README.md +0 -976
  17. package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
  18. package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
  19. package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
  20. package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
  21. package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
  22. package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
  23. package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
  24. package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
  25. package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
  26. package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
  27. package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
  28. package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
  29. package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
  30. package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
  31. package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
  32. package/Packages/Helix/Sources/Helix/Program.swift +0 -204
  33. package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
  34. package/Packages/Silo/LICENSE +0 -21
  35. package/Packages/Silo/Package.swift +0 -34
  36. package/Packages/Silo/README.md +0 -466
  37. package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
  38. package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
  39. package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
  40. package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
  41. package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
  42. package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
  43. package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
  44. package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
  45. package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
  46. package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
  47. package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
  48. package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
  49. package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
  50. package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
  51. package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
  52. package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
  53. package/RuniciOS/App/RuniciOSApp.swift +0 -105
  54. package/RuniciOS/Core/iOSUsageStore.swift +0 -192
  55. package/RuniciOS/Views/ProviderDetailView.swift +0 -312
  56. package/RuniciOS/Views/ProviderListView.swift +0 -228
  57. package/RuniciOS/Widgets/LargeWidget.swift +0 -367
  58. package/RuniciOS/Widgets/LiveActivity.swift +0 -342
  59. package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
  60. package/RuniciOS/Widgets/MediumWidget.swift +0 -370
  61. package/RuniciOS/Widgets/RunicWidget.swift +0 -274
  62. package/RuniciOS/Widgets/SmallWidget.swift +0 -281
  63. package/RuniciOS/Widgets/WidgetModels.swift +0 -285
  64. package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
  65. package/Scripts/CREATION-SUMMARY.md +0 -367
  66. package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
  67. package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
  68. package/Scripts/INDEX.md +0 -365
  69. package/Scripts/QUICK-REFERENCE.md +0 -164
  70. package/Scripts/README-SCRIPTS.md +0 -926
  71. package/Scripts/build-android.sh +0 -526
  72. package/Scripts/build-ios.sh +0 -529
  73. package/Scripts/build-macos.sh +0 -603
  74. package/Scripts/build-windows.sh +0 -504
  75. package/Scripts/build_icon.sh +0 -49
  76. package/Scripts/changelog-to-html.sh +0 -89
  77. package/Scripts/check-release-assets.sh +0 -10
  78. package/Scripts/ci-config.yml +0 -474
  79. package/Scripts/compile_and_run.sh +0 -219
  80. package/Scripts/compile_and_run_adhoc.sh +0 -4
  81. package/Scripts/docs-list.mjs +0 -119
  82. package/Scripts/make_appcast.sh +0 -58
  83. package/Scripts/package_app.sh +0 -409
  84. package/Scripts/release-lib.sh +0 -214
  85. package/Scripts/release.sh +0 -76
  86. package/Scripts/setup-all.sh +0 -387
  87. package/Scripts/setup-ios.sh +0 -404
  88. package/Scripts/setup-react-native.sh +0 -523
  89. package/Scripts/sign-and-notarize.sh +0 -122
  90. package/Scripts/test_live_update.sh +0 -38
  91. package/Scripts/validate_changelog.sh +0 -23
  92. package/Scripts/verify_appcast.sh +0 -92
  93. package/Sources/Runic/About.swift +0 -93
  94. package/Sources/Runic/AppNotifications.swift +0 -115
  95. package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -666
  96. package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -518
  97. package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -810
  98. package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -87
  99. package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1407
  100. package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -337
  101. package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -395
  102. package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
  103. package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -80
  104. package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
  105. package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
  106. package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
  107. package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
  108. package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1216
  109. package/Sources/Runic/Core/Rendering/IconView.swift +0 -120
  110. package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
  111. package/Sources/Runic/Core/RunicAnimation.swift +0 -53
  112. package/Sources/Runic/Core/RunicColors.swift +0 -73
  113. package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
  114. package/Sources/Runic/Core/RunicSpacing.swift +0 -68
  115. package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -119
  116. package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -119
  117. package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -119
  118. package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -119
  119. package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -119
  120. package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
  121. package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -119
  122. package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -119
  123. package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -119
  124. package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -119
  125. package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -119
  126. package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -119
  127. package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -119
  128. package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -119
  129. package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -119
  130. package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -119
  131. package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
  132. package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -119
  133. package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -119
  134. package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1711
  135. package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -119
  136. package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -20
  137. package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
  138. package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
  139. package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
  140. package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3605
  141. package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -119
  142. package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -118
  143. package/Sources/Runic/Core/UsageExporter.swift +0 -193
  144. package/Sources/Runic/Date+RelativeDescription.swift +0 -21
  145. package/Sources/Runic/HiddenWindowView.swift +0 -32
  146. package/Sources/Runic/InstallOrigin.swift +0 -9
  147. package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
  148. package/Sources/Runic/MouseLocationReader.swift +0 -65
  149. package/Sources/Runic/Notifications+Runic.swift +0 -6
  150. package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
  151. package/Sources/Runic/ProviderRegistry.swift +0 -100
  152. package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
  153. package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
  154. package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
  155. package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
  156. package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
  157. package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
  158. package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
  159. package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
  160. package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
  161. package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
  162. package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
  163. package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
  164. package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
  165. package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
  166. package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
  167. package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
  168. package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
  169. package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
  170. package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
  171. package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
  172. package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
  173. package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
  174. package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
  175. package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
  176. package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
  177. package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
  178. package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
  179. package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
  180. package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
  181. package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
  182. package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -48
  183. package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
  184. package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
  185. package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
  186. package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
  187. package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
  188. package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
  189. package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
  190. package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
  191. package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
  192. package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
  193. package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
  194. package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
  195. package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
  196. package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
  197. package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
  198. package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
  199. package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
  200. package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
  201. package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
  202. package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
  203. package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
  204. package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
  205. package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
  206. package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
  207. package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
  208. package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
  209. package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
  210. package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
  211. package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
  212. package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
  213. package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
  214. package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
  215. package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
  216. package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
  217. package/Sources/Runic/Resources/provider-context-windows.json +0 -20
  218. package/Sources/Runic/RunicApp.swift +0 -357
  219. package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
  220. package/Sources/Runic/UsagePaceText.swift +0 -52
  221. package/Sources/Runic/UsageProgressBar.swift +0 -224
  222. package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
  223. package/Sources/Runic/Utilities/LoadingPattern.swift +0 -60
  224. package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
  225. package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -256
  226. package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
  227. package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
  228. package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
  229. package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
  230. package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -312
  231. package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -228
  232. package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
  233. package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -155
  234. package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -64
  235. package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -268
  236. package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
  237. package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1900
  238. package/Sources/Runic/Views/Menu/MenuContent.swift +0 -150
  239. package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -369
  240. package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -42
  241. package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -235
  242. package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -278
  243. package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -209
  244. package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
  245. package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -88
  246. package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -201
  247. package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
  248. package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -232
  249. package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -364
  250. package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -190
  251. package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
  252. package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -618
  253. package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -305
  254. package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
  255. package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
  256. package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -422
  257. package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -641
  258. package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
  259. package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
  260. package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -322
  261. package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
  262. package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -231
  263. package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
  264. package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -567
  265. package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -288
  266. package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3435
  267. package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -25
  268. package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -59
  269. package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -206
  270. package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
  271. package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -371
  272. package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
  273. package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -761
  274. package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
  275. package/Sources/RunicCLI/CLIEntry.swift +0 -942
  276. package/Sources/RunicCLI/CLIRenderer.swift +0 -87
  277. package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
  278. package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -362
  279. package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -439
  280. package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -429
  281. package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -168
  282. package/Sources/RunicClaudeWatchdog/main.swift +0 -122
  283. package/Sources/RunicClaudeWebProbe/main.swift +0 -62
  284. package/Sources/RunicCore/AlertRuleStore.swift +0 -187
  285. package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
  286. package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
  287. package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
  288. package/Sources/RunicCore/CostUsageModels.swift +0 -479
  289. package/Sources/RunicCore/CreditsModels.swift +0 -50
  290. package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -269
  291. package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -633
  292. package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -500
  293. package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
  294. package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
  295. package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
  296. package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
  297. package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
  298. package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
  299. package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -371
  300. package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
  301. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
  302. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
  303. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
  304. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
  305. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
  306. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
  307. package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
  308. package/Sources/RunicCore/PathEnvironment.swift +0 -482
  309. package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
  310. package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -409
  311. package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
  312. package/Sources/RunicCore/ProjectBudgetStore.swift +0 -99
  313. package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
  314. package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -66
  315. package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
  316. package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
  317. package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
  318. package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
  319. package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
  320. package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
  321. package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -168
  322. package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
  323. package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
  324. package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
  325. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
  326. package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
  327. package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -207
  328. package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
  329. package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -28
  330. package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
  331. package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
  332. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
  333. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
  334. package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
  335. package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -243
  336. package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
  337. package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -24
  338. package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -181
  339. package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
  340. package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
  341. package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
  342. package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -114
  343. package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
  344. package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -291
  345. package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -386
  346. package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -65
  347. package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
  348. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
  349. package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
  350. package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
  351. package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -65
  352. package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
  353. package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
  354. package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
  355. package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -65
  356. package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -836
  357. package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
  358. package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
  359. package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
  360. package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
  361. package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -92
  362. package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
  363. package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
  364. package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
  365. package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
  366. package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
  367. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
  368. package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -203
  369. package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
  370. package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
  371. package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
  372. package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
  373. package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
  374. package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
  375. package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
  376. package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
  377. package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
  378. package/Sources/RunicCore/Providers/Providers.swift +0 -184
  379. package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -84
  380. package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
  381. package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -315
  382. package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
  383. package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
  384. package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
  385. package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
  386. package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
  387. package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
  388. package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
  389. package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
  390. package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
  391. package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -72
  392. package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
  393. package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -718
  394. package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -385
  395. package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -310
  396. package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -288
  397. package/Sources/RunicCore/Sync/SyncRecord.swift +0 -405
  398. package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -294
  399. package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -775
  400. package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -333
  401. package/Sources/RunicCore/Team/TeamModels.swift +0 -583
  402. package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
  403. package/Sources/RunicCore/Team/TeamStore.swift +0 -792
  404. package/Sources/RunicCore/TextParsing.swift +0 -55
  405. package/Sources/RunicCore/UsageFetcher.swift +0 -694
  406. package/Sources/RunicCore/UsageFormatter.swift +0 -410
  407. package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -427
  408. package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -494
  409. package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -158
  410. package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
  411. package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
  412. package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -660
  413. package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
  414. package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
  415. package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -245
  416. package/Sources/RunicCore/UsagePace.swift +0 -81
  417. package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
  418. package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
  419. package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
  420. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
  421. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
  422. package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
  423. package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
  424. package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
  425. package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
  426. package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
  427. package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
  428. package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
  429. package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
  430. package/Tests/RunicTests/AppDelegateTests.swift +0 -43
  431. package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -22
  432. package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -70
  433. package/Tests/RunicTests/CLICostTests.swift +0 -65
  434. package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -22
  435. package/Tests/RunicTests/CLISnapshotTests.swift +0 -72
  436. package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -58
  437. package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -164
  438. package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -30
  439. package/Tests/RunicTests/ClaudeUsageTests.swift +0 -370
  440. package/Tests/RunicTests/CodexOAuthTests.swift +0 -122
  441. package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -82
  442. package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -75
  443. package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -75
  444. package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -394
  445. package/Tests/RunicTests/CostUsagePricingTests.swift +0 -50
  446. package/Tests/RunicTests/CostUsageScannerTests.swift +0 -700
  447. package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -323
  448. package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -69
  449. package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
  450. package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -28
  451. package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
  452. package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
  453. package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -233
  454. package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
  455. package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -73
  456. package/Tests/RunicTests/InstallOriginTests.swift +0 -18
  457. package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
  458. package/Tests/RunicTests/LiveAccountTests.swift +0 -24
  459. package/Tests/RunicTests/LoadingPatternTests.swift +0 -35
  460. package/Tests/RunicTests/MenuCardModelTests.swift +0 -545
  461. package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -8
  462. package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -8
  463. package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -68
  464. package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -8
  465. package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -112
  466. package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -17
  467. package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -75
  468. package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -27
  469. package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -134
  470. package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -53
  471. package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -92
  472. package/Tests/RunicTests/PathBuilderTests.swift +0 -237
  473. package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -160
  474. package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -38
  475. package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -16
  476. package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
  477. package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -202
  478. package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -51
  479. package/Tests/RunicTests/RunicTests.swift +0 -120
  480. package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -36
  481. package/Tests/RunicTests/SettingsStoreTests.swift +0 -161
  482. package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -49
  483. package/Tests/RunicTests/StatusMenuTests.swift +0 -434
  484. package/Tests/RunicTests/StatusProbeTests.swift +0 -377
  485. package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -20
  486. package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -65
  487. package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -148
  488. package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
  489. package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -159
  490. package/Tests/RunicTests/TextParsingTests.swift +0 -12
  491. package/Tests/RunicTests/UsageFormatterTests.swift +0 -112
  492. package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -172
  493. package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -213
  494. package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -83
  495. package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -170
  496. package/Tests/RunicTests/UsagePaceTests.swift +0 -77
  497. package/Tests/RunicTests/UsagePaceTextTests.swift +0 -109
  498. package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -91
  499. package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -45
  500. package/Tests/RunicTests/ZaiProviderTests.swift +0 -61
  501. package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -6
  502. package/TestsLinux/PlatformGatingTests.swift +0 -46
  503. package/appcast.xml +0 -144
  504. package/assets/logo-concept-stack.svg +0 -24
  505. package/assets/logo-minimal-bars.svg +0 -15
  506. package/assets/logo.svg +0 -20
  507. package/assets/logo2.svg +0 -43
  508. package/bin/docs-list +0 -5
  509. package/build-and-run.sh +0 -369
  510. package/docs/.nojekyll +0 -1
  511. package/docs/CNAME +0 -1
  512. package/docs/antigravity.md +0 -75
  513. package/docs/architecture.md +0 -32
  514. package/docs/claude.md +0 -93
  515. package/docs/cli-commands/integration_guide.md +0 -371
  516. package/docs/cli-commands/quick_reference.md +0 -362
  517. package/docs/cli-commands/readme.md +0 -361
  518. package/docs/cli.md +0 -156
  519. package/docs/cloudkit_sync_guide.md +0 -495
  520. package/docs/codebase_review.md +0 -288
  521. package/docs/codex-oauth.md +0 -635
  522. package/docs/codex.md +0 -104
  523. package/docs/codexbar.png +0 -0
  524. package/docs/copilot.md +0 -44
  525. package/docs/cursor.md +0 -46
  526. package/docs/design/designcomparisonchart.md +0 -510
  527. package/docs/design/implementationguide.md +0 -792
  528. package/docs/design/menubaricondesignconcepts.md +0 -796
  529. package/docs/design/mockups/hybridiconconcept.svg +0 -62
  530. package/docs/design/mockups/infinityiconconcept.svg +0 -41
  531. package/docs/design/mockups/infinityiconstates.svg +0 -129
  532. package/docs/design/mockups/wavelogorefined.svg +0 -48
  533. package/docs/design/quick_summary.md +0 -426
  534. package/docs/design/readme.md +0 -478
  535. package/docs/design/sizespecifications.md +0 -575
  536. package/docs/design-system.md +0 -836
  537. package/docs/factory.md +0 -103
  538. package/docs/gemini.md +0 -73
  539. package/docs/icon.md +0 -37
  540. package/docs/icon.png +0 -0
  541. package/docs/index.html +0 -175
  542. package/docs/keychain_fix.md +0 -274
  543. package/docs/minimax.md +0 -71
  544. package/docs/packaging.md +0 -25
  545. package/docs/progress-bar-improvements.md +0 -240
  546. package/docs/progress-bar-quick-reference.md +0 -283
  547. package/docs/progress-bar-technical-details.md +0 -432
  548. package/docs/progress-bar-visual-test.swift +0 -375
  549. package/docs/provider.md +0 -202
  550. package/docs/providers.md +0 -86
  551. package/docs/refactor/macros.md +0 -35
  552. package/docs/refresh-loop.md +0 -22
  553. package/docs/releasing-homebrew.md +0 -35
  554. package/docs/releasing.md +0 -121
  555. package/docs/research/concrete_todo_2026-02-22.md +0 -47
  556. package/docs/research/core-feature-research.md +0 -248
  557. package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
  558. package/docs/research/parity_matrix.md +0 -49
  559. package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
  560. package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
  561. package/docs/research/release_gate_checklist.md +0 -57
  562. package/docs/site.css +0 -505
  563. package/docs/sparkle.md +0 -28
  564. package/docs/status.md +0 -28
  565. package/docs/sync/implementation.md +0 -393
  566. package/docs/sync/readme.md +0 -317
  567. package/docs/sync_files_summary.md +0 -391
  568. package/docs/sync_quick_reference.md +0 -225
  569. package/docs/todo.md +0 -11
  570. package/docs/ui.md +0 -29
  571. package/docs/web-integration.md +0 -9
  572. package/docs/widgets.md +0 -85
  573. package/docs/workflows/error-handling-improvements.md +0 -434
  574. package/docs/workflows/folder_structure.md +0 -299
  575. package/docs/workflows/loading_states.md +0 -446
  576. package/docs/workflows/loading_states_examples.md +0 -565
  577. package/docs/workflows/loading_states_flow.md +0 -430
  578. package/docs/workflows/performance_optimizations.md +0 -457
  579. package/docs/workflows/phase1-cli-testing-report.md +0 -313
  580. package/docs/workflows/quick_reference.md +0 -228
  581. package/docs/workflows/readme.md +0 -59
  582. package/docs/workflows/recent_improvements.md +0 -275
  583. package/docs/workflows/sync_enablement_summary.md +0 -400
  584. package/docs/zai.md +0 -43
  585. package/runic.png +0 -0
  586. package/version.env +0 -2
@@ -1,1276 +0,0 @@
1
- import Foundation
2
- import Silo
3
-
4
- #if os(macOS)
5
-
6
- private let factoryCookieImportOrder: BrowserCookieImportOrder =
7
- ProviderDefaults.metadata[.factory]?.browserCookieOrder ?? Browser.defaultImportOrder
8
-
9
- // MARK: - Factory Cookie Importer
10
-
11
- /// Imports Factory session cookies from browser cookies.
12
- public enum FactoryCookieImporter {
13
- private static let cookieClient = BrowserCookieClient()
14
- private static let sessionCookieNames: Set<String> = [
15
- "wos-session",
16
- "__Secure-next-auth.session-token",
17
- "next-auth.session-token",
18
- "__Secure-authjs.session-token",
19
- "__Host-authjs.csrf-token",
20
- "authjs.session-token",
21
- "session",
22
- "access-token",
23
- ]
24
-
25
- private static let authSessionCookieNames: Set<String> = [
26
- "__Secure-next-auth.session-token",
27
- "next-auth.session-token",
28
- "__Secure-authjs.session-token",
29
- "authjs.session-token",
30
- ]
31
- private static let appBaseURL = URL(string: "https://app.factory.ai")!
32
- private static let authBaseURL = URL(string: "https://auth.factory.ai")!
33
- private static let apiBaseURL = URL(string: "https://api.factory.ai")!
34
-
35
- public struct SessionInfo: Sendable {
36
- public let cookies: [HTTPCookie]
37
- public let sourceLabel: String
38
-
39
- public init(cookies: [HTTPCookie], sourceLabel: String) {
40
- self.cookies = cookies
41
- self.sourceLabel = sourceLabel
42
- }
43
-
44
- public var cookieHeader: String {
45
- self.cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
46
- }
47
- }
48
-
49
- /// Returns all Factory sessions across supported browsers.
50
- public static func importSessions(logger: ((String) -> Void)? = nil) throws -> [SessionInfo] {
51
- let log: (String) -> Void = { msg in logger?("[factory-cookie] \(msg)") }
52
- var sessions: [SessionInfo] = []
53
-
54
- for browserSource in factoryCookieImportOrder {
55
- do {
56
- let perSource = try self.importSessions(from: browserSource, logger: logger)
57
- sessions.append(contentsOf: perSource)
58
- } catch {
59
- log("\(browserSource.displayName) cookie import failed: \(error.localizedDescription)")
60
- }
61
- }
62
-
63
- guard !sessions.isEmpty else {
64
- throw FactoryStatusProbeError.noSessionCookie
65
- }
66
- return sessions
67
- }
68
-
69
- public static func importSessions(
70
- from browserSource: Browser,
71
- logger: ((String) -> Void)? = nil) throws -> [SessionInfo]
72
- {
73
- let log: (String) -> Void = { msg in logger?("[factory-cookie] \(msg)") }
74
- let cookieDomains = ["factory.ai", "app.factory.ai", "auth.factory.ai"]
75
- let query = BrowserCookieQuery(domains: cookieDomains)
76
- let sources = try Self.cookieClient.records(
77
- matching: query,
78
- in: browserSource)
79
-
80
- var sessions: [SessionInfo] = []
81
- for source in sources where !source.records.isEmpty {
82
- let httpCookies = BrowserCookieClient.makeHTTPCookies(source.records, origin: query.origin)
83
- if httpCookies.contains(where: { Self.sessionCookieNames.contains($0.name) }) {
84
- log("Found \(httpCookies.count) Factory cookies in \(source.label)")
85
- log("\(source.label) cookie names: \(self.cookieNames(from: httpCookies))")
86
- if let token = httpCookies.first(where: { $0.name == "access-token" })?.value {
87
- let hint = token.contains(".") ? "jwt" : "opaque"
88
- log("\(source.label) access-token cookie: \(token.count) chars (\(hint))")
89
- }
90
- if let token = httpCookies.first(where: { self.authSessionCookieNames.contains($0.name) })?.value {
91
- let hint = token.contains(".") ? "jwt" : "opaque"
92
- log("\(source.label) session cookie: \(token.count) chars (\(hint))")
93
- }
94
- sessions.append(SessionInfo(cookies: httpCookies, sourceLabel: source.label))
95
- } else {
96
- log("\(source.label) cookies found, but no Factory session cookie present")
97
- }
98
- }
99
- return sessions
100
- }
101
-
102
- /// Attempts to import Factory cookies using the standard browser import order.
103
- public static func importSession(logger: ((String) -> Void)? = nil) throws -> SessionInfo {
104
- let sessions = try self.importSessions(logger: logger)
105
- guard let first = sessions.first else {
106
- throw FactoryStatusProbeError.noSessionCookie
107
- }
108
- return first
109
- }
110
-
111
- /// Check if Factory session cookies are available
112
- public static func hasSession(logger: ((String) -> Void)? = nil) -> Bool {
113
- do {
114
- return try !(self.importSessions(logger: logger)).isEmpty
115
- } catch {
116
- return false
117
- }
118
- }
119
-
120
- private static func cookieNames(from cookies: [HTTPCookie]) -> String {
121
- let names = Set(cookies.map { "\($0.name)@\($0.domain)" }).sorted()
122
- return names.joined(separator: ", ")
123
- }
124
- }
125
-
126
- // MARK: - Factory API Models
127
-
128
- public struct FactoryAuthResponse: Codable, Sendable {
129
- public let featureFlags: FactoryFeatureFlags?
130
- public let organization: FactoryOrganization?
131
- }
132
-
133
- public struct FactoryFeatureFlags: Codable, Sendable {
134
- public let flags: [String: Bool]?
135
- public let configs: [String: AnyCodable]?
136
- }
137
-
138
- public struct FactoryOrganization: Codable, Sendable {
139
- public let id: String?
140
- public let name: String?
141
- public let subscription: FactorySubscription?
142
-
143
- enum CodingKeys: String, CodingKey {
144
- case id
145
- case name
146
- case subscription
147
- }
148
- }
149
-
150
- public struct FactorySubscription: Codable, Sendable {
151
- public let factoryTier: String?
152
- public let orbSubscription: FactoryOrbSubscription?
153
- }
154
-
155
- public struct FactoryOrbSubscription: Codable, Sendable {
156
- public let plan: FactoryPlan?
157
- public let status: String?
158
- }
159
-
160
- public struct FactoryPlan: Codable, Sendable {
161
- public let name: String?
162
- public let id: String?
163
- }
164
-
165
- public struct FactoryUsageResponse: Codable, Sendable {
166
- public let usage: FactoryUsageData?
167
- public let source: String?
168
- public let userId: String?
169
- }
170
-
171
- public struct FactoryUsageData: Codable, Sendable {
172
- public let startDate: Int64?
173
- public let endDate: Int64?
174
- public let standard: FactoryTokenUsage?
175
- public let premium: FactoryTokenUsage?
176
- }
177
-
178
- public struct FactoryTokenUsage: Codable, Sendable {
179
- public let userTokens: Int64?
180
- public let orgTotalTokensUsed: Int64?
181
- public let totalAllowance: Int64?
182
- public let usedRatio: Double?
183
- public let orgOverageUsed: Int64?
184
- public let basicAllowance: Int64?
185
- public let orgOverageLimit: Int64?
186
- }
187
-
188
- /// Helper for encoding arbitrary JSON
189
- public struct AnyCodable: Codable, Sendable {
190
- public init(from decoder: Decoder) throws {
191
- let container = try decoder.singleValueContainer()
192
- if container.decodeNil() {
193
- return
194
- }
195
- _ = try? container.decode([String: AnyCodable].self)
196
- }
197
-
198
- public func encode(to encoder: Encoder) throws {
199
- var container = encoder.singleValueContainer()
200
- try container.encodeNil()
201
- }
202
- }
203
-
204
- // MARK: - Factory Status Snapshot
205
-
206
- public struct FactoryStatusSnapshot: Sendable {
207
- /// Standard token usage (user)
208
- public let standardUserTokens: Int64
209
- /// Standard token usage (org total)
210
- public let standardOrgTokens: Int64
211
- /// Standard token allowance
212
- public let standardAllowance: Int64
213
- /// Premium token usage (user)
214
- public let premiumUserTokens: Int64
215
- /// Premium token usage (org total)
216
- public let premiumOrgTokens: Int64
217
- /// Premium token allowance
218
- public let premiumAllowance: Int64
219
- /// Billing period start
220
- public let periodStart: Date?
221
- /// Billing period end
222
- public let periodEnd: Date?
223
- /// Plan name
224
- public let planName: String?
225
- /// Factory tier (enterprise, team, etc.)
226
- public let tier: String?
227
- /// Organization name
228
- public let organizationName: String?
229
- /// User email
230
- public let accountEmail: String?
231
- /// User ID
232
- public let userId: String?
233
- /// Raw JSON for debugging
234
- public let rawJSON: String?
235
-
236
- public init(
237
- standardUserTokens: Int64,
238
- standardOrgTokens: Int64,
239
- standardAllowance: Int64,
240
- premiumUserTokens: Int64,
241
- premiumOrgTokens: Int64,
242
- premiumAllowance: Int64,
243
- periodStart: Date?,
244
- periodEnd: Date?,
245
- planName: String?,
246
- tier: String?,
247
- organizationName: String?,
248
- accountEmail: String?,
249
- userId: String?,
250
- rawJSON: String?)
251
- {
252
- self.standardUserTokens = standardUserTokens
253
- self.standardOrgTokens = standardOrgTokens
254
- self.standardAllowance = standardAllowance
255
- self.premiumUserTokens = premiumUserTokens
256
- self.premiumOrgTokens = premiumOrgTokens
257
- self.premiumAllowance = premiumAllowance
258
- self.periodStart = periodStart
259
- self.periodEnd = periodEnd
260
- self.planName = planName
261
- self.tier = tier
262
- self.organizationName = organizationName
263
- self.accountEmail = accountEmail
264
- self.userId = userId
265
- self.rawJSON = rawJSON
266
- }
267
-
268
- /// Convert to UsageSnapshot for the common provider interface
269
- public func toUsageSnapshot() -> UsageSnapshot {
270
- // Primary: Standard tokens used (as percentage of allowance, capped reasonably)
271
- let standardPercent = self.calculateUsagePercent(
272
- used: self.standardUserTokens,
273
- allowance: self.standardAllowance)
274
-
275
- let primary = RateWindow(
276
- usedPercent: standardPercent,
277
- windowMinutes: nil,
278
- resetsAt: self.periodEnd,
279
- resetDescription: self.periodEnd.map { Self.formatResetDate($0) })
280
-
281
- // Secondary: Premium tokens used
282
- let premiumPercent = self.calculateUsagePercent(
283
- used: self.premiumUserTokens,
284
- allowance: self.premiumAllowance)
285
-
286
- let secondary = RateWindow(
287
- usedPercent: premiumPercent,
288
- windowMinutes: nil,
289
- resetsAt: self.periodEnd,
290
- resetDescription: self.periodEnd.map { Self.formatResetDate($0) })
291
-
292
- // Format login method as tier + plan
293
- let loginMethod: String? = {
294
- var parts: [String] = []
295
- if let tier = self.tier, !tier.isEmpty {
296
- parts.append("Factory \(tier.capitalized)")
297
- }
298
- if let plan = self.planName, !plan.isEmpty, !plan.lowercased().contains("factory") {
299
- parts.append(plan)
300
- }
301
- return parts.isEmpty ? nil : parts.joined(separator: " - ")
302
- }()
303
-
304
- let identity = ProviderIdentitySnapshot(
305
- providerID: .factory,
306
- accountEmail: self.accountEmail,
307
- accountOrganization: self.organizationName,
308
- loginMethod: loginMethod)
309
- return UsageSnapshot(
310
- primary: primary,
311
- secondary: secondary,
312
- tertiary: nil,
313
- providerCost: nil,
314
- updatedAt: Date(),
315
- identity: identity)
316
- }
317
-
318
- private func calculateUsagePercent(used: Int64, allowance: Int64) -> Double {
319
- // Treat very large allowances (> 1 trillion) as unlimited
320
- let unlimitedThreshold: Int64 = 1_000_000_000_000
321
- if allowance > unlimitedThreshold {
322
- // For unlimited, show a token count-based pseudo-percentage (capped at 100%)
323
- // Use 100M tokens as a reference point for "100%"
324
- let referenceTokens: Double = 100_000_000
325
- return min(100, Double(used) / referenceTokens * 100)
326
- }
327
- guard allowance > 0 else { return 0 }
328
- return min(100, Double(used) / Double(allowance) * 100)
329
- }
330
-
331
- private static func formatResetDate(_ date: Date) -> String {
332
- let formatter = DateFormatter()
333
- formatter.dateFormat = "MMM d 'at' h:mma"
334
- formatter.locale = Locale(identifier: "en_US_POSIX")
335
- return "Resets " + formatter.string(from: date)
336
- }
337
- }
338
-
339
- // MARK: - Factory Status Probe Error
340
-
341
- public enum FactoryStatusProbeError: LocalizedError, Sendable {
342
- case notLoggedIn
343
- case networkError(String)
344
- case parseFailed(String)
345
- case noSessionCookie
346
-
347
- public var errorDescription: String? {
348
- switch self {
349
- case .notLoggedIn:
350
- "Not logged in to Factory. Please log in via the Runic menu."
351
- case let .networkError(msg):
352
- "Factory API error: \(msg)"
353
- case let .parseFailed(msg):
354
- "Could not parse Factory usage: \(msg)"
355
- case .noSessionCookie:
356
- "No Factory session found. Please log in to app.factory.ai in \(factoryCookieImportOrder.loginHint)."
357
- }
358
- }
359
- }
360
-
361
- // MARK: - Factory Session Store
362
-
363
- public actor FactorySessionStore {
364
- public static let shared = FactorySessionStore()
365
-
366
- private var sessionCookies: [HTTPCookie] = []
367
- private var bearerToken: String?
368
- private var refreshToken: String?
369
- private let fileURL: URL
370
- private var didLoadFromDisk = false
371
-
372
- private init() {
373
- let fm = FileManager.default
374
- let appSupport = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
375
- ?? fm.temporaryDirectory
376
- let dir = appSupport.appendingPathComponent("Runic", isDirectory: true)
377
- try? fm.createDirectory(at: dir, withIntermediateDirectories: true)
378
- self.fileURL = dir.appendingPathComponent("factory-session.json")
379
- }
380
-
381
- public func setCookies(_ cookies: [HTTPCookie]) {
382
- self.didLoadFromDisk = true
383
- self.sessionCookies = cookies
384
- self.saveToDisk()
385
- }
386
-
387
- public func getCookies() -> [HTTPCookie] {
388
- self.loadFromDiskIfNeeded()
389
- return self.sessionCookies
390
- }
391
-
392
- public func setBearerToken(_ token: String?) {
393
- self.didLoadFromDisk = true
394
- self.bearerToken = token
395
- self.saveToDisk()
396
- }
397
-
398
- public func getBearerToken() -> String? {
399
- self.loadFromDiskIfNeeded()
400
- return self.bearerToken
401
- }
402
-
403
- public func setRefreshToken(_ token: String?) {
404
- self.didLoadFromDisk = true
405
- self.refreshToken = token
406
- self.saveToDisk()
407
- }
408
-
409
- public func getRefreshToken() -> String? {
410
- self.loadFromDiskIfNeeded()
411
- return self.refreshToken
412
- }
413
-
414
- public func clearSession() {
415
- self.didLoadFromDisk = true
416
- self.sessionCookies = []
417
- self.bearerToken = nil
418
- self.refreshToken = nil
419
- try? FileManager.default.removeItem(at: self.fileURL)
420
- }
421
-
422
- public func hasValidSession() -> Bool {
423
- self.loadFromDiskIfNeeded()
424
- return !self.sessionCookies.isEmpty || self.bearerToken != nil || self.refreshToken != nil
425
- }
426
-
427
- private func saveToDisk() {
428
- let cookieData = self.sessionCookies.compactMap { cookie -> [String: Any]? in
429
- guard let props = cookie.properties else { return nil }
430
- var serializable: [String: Any] = [:]
431
- for (key, value) in props {
432
- let keyString = key.rawValue
433
- if let date = value as? Date {
434
- serializable[keyString] = date.timeIntervalSince1970
435
- serializable[keyString + "_isDate"] = true
436
- } else if let url = value as? URL {
437
- serializable[keyString] = url.absoluteString
438
- serializable[keyString + "_isURL"] = true
439
- } else if JSONSerialization.isValidJSONObject([value]) ||
440
- value is String ||
441
- value is Bool ||
442
- value is NSNumber
443
- {
444
- serializable[keyString] = value
445
- }
446
- }
447
- return serializable
448
- }
449
-
450
- var payload: [String: Any] = [:]
451
- if !cookieData.isEmpty {
452
- payload["cookies"] = cookieData
453
- }
454
- if let bearerToken {
455
- payload["bearerToken"] = bearerToken
456
- }
457
- if let refreshToken {
458
- payload["refreshToken"] = refreshToken
459
- }
460
-
461
- guard !payload.isEmpty,
462
- let data = try? JSONSerialization.data(withJSONObject: payload, options: [.prettyPrinted])
463
- else {
464
- return
465
- }
466
- try? data.write(to: self.fileURL)
467
- try? FileManager.default.setAttributes([.posixPermissions: 0o600], ofItemAtPath: self.fileURL.path)
468
- }
469
-
470
- private func loadFromDisk() {
471
- guard let data = try? Data(contentsOf: self.fileURL),
472
- let json = try? JSONSerialization.jsonObject(with: data)
473
- else { return }
474
-
475
- var cookieArray: [[String: Any]] = []
476
- if let dict = json as? [String: Any] {
477
- if let stored = dict["cookies"] as? [[String: Any]] {
478
- cookieArray = stored
479
- }
480
- self.bearerToken = dict["bearerToken"] as? String
481
- self.refreshToken = dict["refreshToken"] as? String
482
- } else if let stored = json as? [[String: Any]] {
483
- cookieArray = stored
484
- }
485
-
486
- self.sessionCookies = cookieArray.compactMap { props in
487
- var cookieProps: [HTTPCookiePropertyKey: Any] = [:]
488
- for (key, value) in props {
489
- if key.hasSuffix("_isDate") || key.hasSuffix("_isURL") { continue }
490
-
491
- let propKey = HTTPCookiePropertyKey(key)
492
-
493
- if props[key + "_isDate"] as? Bool == true, let interval = value as? TimeInterval {
494
- cookieProps[propKey] = Date(timeIntervalSince1970: interval)
495
- } else if props[key + "_isURL"] as? Bool == true, let urlString = value as? String {
496
- cookieProps[propKey] = URL(string: urlString)
497
- } else {
498
- cookieProps[propKey] = value
499
- }
500
- }
501
- return HTTPCookie(properties: cookieProps)
502
- }
503
- }
504
-
505
- private func loadFromDiskIfNeeded() {
506
- guard !self.didLoadFromDisk else { return }
507
- self.didLoadFromDisk = true
508
- self.loadFromDisk()
509
- }
510
- }
511
-
512
- // MARK: - Factory Status Probe
513
-
514
- public struct FactoryStatusProbe: Sendable {
515
- public let baseURL: URL
516
- public var timeout: TimeInterval = 15.0
517
- private static let staleTokenCookieNames: Set<String> = [
518
- "access-token",
519
- "__recent_auth",
520
- ]
521
- private static let sessionCookieNames: Set<String> = [
522
- "session",
523
- "wos-session",
524
- ]
525
- private static let authSessionCookieNames: Set<String> = [
526
- "__Secure-next-auth.session-token",
527
- "next-auth.session-token",
528
- "__Secure-authjs.session-token",
529
- "authjs.session-token",
530
- ]
531
- static let appBaseURL = URL(string: "https://app.factory.ai")!
532
- static let authBaseURL = URL(string: "https://auth.factory.ai")!
533
- static let apiBaseURL = URL(string: "https://api.factory.ai")!
534
- private static let workosClientIDs = [
535
- "client_01HXRMBQ9BJ3E7QSTQ9X2PHVB7",
536
- "client_01HNM792M5G5G1A2THWPXKFMXB",
537
- ]
538
-
539
- private struct WorkOSAuthResponse: Decodable, Sendable {
540
- let access_token: String
541
- let refresh_token: String?
542
- let organization_id: String?
543
- }
544
-
545
- public init(baseURL: URL = URL(string: "https://app.factory.ai")!, timeout: TimeInterval = 15.0) {
546
- self.baseURL = baseURL
547
- self.timeout = timeout
548
- }
549
-
550
- /// Fetch Factory usage using browser cookies with fallback to stored session.
551
- public func fetch(logger: ((String) -> Void)? = nil) async throws -> FactoryStatusSnapshot {
552
- let log: (String) -> Void = { msg in logger?("[factory] \(msg)") }
553
- var lastError: Error?
554
-
555
- let attempts: [FetchAttemptResult] = await [
556
- self.attemptBrowserCookies(logger: log, sources: [.safari]),
557
- self.attemptStoredCookies(logger: log),
558
- self.attemptStoredBearer(logger: log),
559
- self.attemptStoredRefreshToken(logger: log),
560
- self.attemptLocalStorageTokens(logger: log),
561
- self.attemptWorkOSCookies(logger: log, sources: [.safari]),
562
- self.attemptBrowserCookies(logger: log, sources: [.chrome, .firefox]),
563
- self.attemptWorkOSCookies(logger: log, sources: [.chrome, .firefox]),
564
- ]
565
-
566
- for result in attempts {
567
- switch result {
568
- case let .success(snapshot):
569
- return snapshot
570
- case let .failure(error):
571
- lastError = error
572
- case .skipped:
573
- continue
574
- }
575
- }
576
-
577
- if let lastError { throw lastError }
578
- throw FactoryStatusProbeError.noSessionCookie
579
- }
580
-
581
- private enum FetchAttemptResult: Sendable {
582
- case success(FactoryStatusSnapshot)
583
- case failure(Error)
584
- case skipped
585
- }
586
-
587
- private func attemptBrowserCookies(
588
- logger: @escaping (String) -> Void,
589
- sources: [Browser]) async -> FetchAttemptResult
590
- {
591
- do {
592
- var lastError: Error?
593
- for browserSource in sources {
594
- let sessions = try FactoryCookieImporter.importSessions(from: browserSource, logger: logger)
595
- for session in sessions {
596
- logger("Using cookies from \(session.sourceLabel)")
597
- do {
598
- let snapshot = try await self.fetchWithCookies(session.cookies, logger: logger)
599
- await FactorySessionStore.shared.setCookies(session.cookies)
600
- return .success(snapshot)
601
- } catch {
602
- lastError = error
603
- logger("Browser session fetch failed for \(session.sourceLabel): \(error.localizedDescription)")
604
- }
605
- }
606
- }
607
- if let lastError { return .failure(lastError) }
608
- return .skipped
609
- } catch {
610
- logger("Browser cookie import failed: \(error.localizedDescription)")
611
- return .failure(error)
612
- }
613
- }
614
-
615
- private func attemptStoredCookies(logger: (String) -> Void) async -> FetchAttemptResult {
616
- let storedCookies = await FactorySessionStore.shared.getCookies()
617
- guard !storedCookies.isEmpty else { return .skipped }
618
- logger("Using stored session cookies")
619
- do {
620
- return try await .success(self.fetchWithCookies(storedCookies, logger: logger))
621
- } catch {
622
- if case FactoryStatusProbeError.notLoggedIn = error {
623
- await FactorySessionStore.shared.clearSession()
624
- logger("Stored session invalid, cleared")
625
- } else {
626
- logger("Stored session failed: \(error.localizedDescription)")
627
- }
628
- return .failure(error)
629
- }
630
- }
631
-
632
- private func attemptStoredBearer(logger: (String) -> Void) async -> FetchAttemptResult {
633
- guard let bearerToken = await FactorySessionStore.shared.getBearerToken() else { return .skipped }
634
- logger("Using stored Factory bearer token")
635
- do {
636
- return try await .success(self.fetchWithBearerToken(bearerToken, logger: logger))
637
- } catch {
638
- return .failure(error)
639
- }
640
- }
641
-
642
- private func attemptStoredRefreshToken(logger: (String) -> Void) async -> FetchAttemptResult {
643
- guard let refreshToken = await FactorySessionStore.shared.getRefreshToken(),
644
- !refreshToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
645
- else {
646
- return .skipped
647
- }
648
- logger("Using stored WorkOS refresh token")
649
- do {
650
- return try await .success(self.fetchWithWorkOSRefreshToken(
651
- refreshToken,
652
- organizationID: nil,
653
- logger: logger))
654
- } catch {
655
- if self.isInvalidGrant(error) {
656
- await FactorySessionStore.shared.setRefreshToken(nil)
657
- } else if case FactoryStatusProbeError.noSessionCookie = error {
658
- await FactorySessionStore.shared.setRefreshToken(nil)
659
- }
660
- return .failure(error)
661
- }
662
- }
663
-
664
- private func attemptLocalStorageTokens(logger: @escaping (String) -> Void) async -> FetchAttemptResult {
665
- let workosTokens = FactoryLocalStorageImporter.importWorkOSTokens(logger: logger)
666
- guard !workosTokens.isEmpty else { return .skipped }
667
- var lastError: Error?
668
- for token in workosTokens {
669
- guard !token.refreshToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
670
- continue
671
- }
672
- logger("Using WorkOS refresh token from \(token.sourceLabel)")
673
- if let accessToken = token.accessToken {
674
- do {
675
- await FactorySessionStore.shared.setBearerToken(accessToken)
676
- return try await .success(self.fetchWithBearerToken(accessToken, logger: logger))
677
- } catch {
678
- lastError = error
679
- }
680
- }
681
- do {
682
- return try await .success(self.fetchWithWorkOSRefreshToken(
683
- token.refreshToken,
684
- organizationID: token.organizationID,
685
- logger: logger))
686
- } catch {
687
- if self.isInvalidGrant(error) {
688
- await FactorySessionStore.shared.setRefreshToken(nil)
689
- }
690
- lastError = error
691
- }
692
- }
693
- if let lastError { return .failure(lastError) }
694
- return .skipped
695
- }
696
-
697
- private func attemptWorkOSCookies(
698
- logger: @escaping (String) -> Void,
699
- sources: [Browser]) async -> FetchAttemptResult
700
- {
701
- let log: (String) -> Void = { msg in logger("[factory-workos] \(msg)") }
702
- var lastError: Error?
703
-
704
- for browserSource in sources {
705
- do {
706
- let query = BrowserCookieQuery(domains: ["workos.com"])
707
- let sources = try BrowserCookieClient().records(
708
- matching: query,
709
- in: browserSource)
710
- for source in sources where !source.records.isEmpty {
711
- let cookies = BrowserCookieClient.makeHTTPCookies(source.records, origin: query.origin)
712
- guard !cookies.isEmpty else { continue }
713
- log("Using WorkOS cookies from \(source.label)")
714
- do {
715
- let auth = try await self.fetchWorkOSAccessTokenWithCookies(
716
- cookies: cookies,
717
- logger: logger)
718
- await FactorySessionStore.shared.setBearerToken(auth.access_token)
719
- if let refreshToken = auth.refresh_token {
720
- await FactorySessionStore.shared.setRefreshToken(refreshToken)
721
- }
722
- return try await .success(self.fetchWithBearerToken(auth.access_token, logger: logger))
723
- } catch {
724
- lastError = error
725
- log("WorkOS cookie auth failed for \(source.label): \(error.localizedDescription)")
726
- }
727
- }
728
- } catch {
729
- log("\(browserSource.displayName) WorkOS cookie import failed: \(error.localizedDescription)")
730
- lastError = error
731
- }
732
- }
733
-
734
- if let lastError { return .failure(lastError) }
735
- return .skipped
736
- }
737
-
738
- private func fetchWithWorkOSRefreshToken(
739
- _ refreshToken: String,
740
- organizationID: String?,
741
- logger: (String) -> Void) async throws -> FactoryStatusSnapshot
742
- {
743
- let auth = try await self.fetchWorkOSAccessToken(
744
- refreshToken: refreshToken,
745
- organizationID: organizationID)
746
- await FactorySessionStore.shared.setBearerToken(auth.access_token)
747
- if let newRefresh = auth.refresh_token {
748
- await FactorySessionStore.shared.setRefreshToken(newRefresh)
749
- }
750
- return try await self.fetchWithBearerToken(auth.access_token, logger: logger)
751
- }
752
-
753
- private func fetchWithCookies(
754
- _ cookies: [HTTPCookie],
755
- logger: (String) -> Void) async throws -> FactoryStatusSnapshot
756
- {
757
- let candidates = Self.baseURLCandidates(default: self.baseURL, cookies: cookies)
758
- var lastError: Error?
759
-
760
- for baseURL in candidates {
761
- if baseURL != self.baseURL {
762
- logger("Trying Factory base URL: \(baseURL.host ?? baseURL.absoluteString)")
763
- }
764
- do {
765
- return try await self.fetchWithCookies(cookies, baseURL: baseURL, logger: logger)
766
- } catch {
767
- lastError = error
768
- }
769
- }
770
-
771
- if let lastError { throw lastError }
772
- throw FactoryStatusProbeError.noSessionCookie
773
- }
774
-
775
- private func fetchWithCookies(
776
- _ cookies: [HTTPCookie],
777
- baseURL: URL,
778
- logger: (String) -> Void) async throws -> FactoryStatusSnapshot
779
- {
780
- let header = Self.cookieHeader(from: cookies)
781
- let bearerToken = Self.bearerToken(from: cookies)
782
- do {
783
- return try await self.fetchWithCookieHeader(header, bearerToken: bearerToken, baseURL: baseURL)
784
- } catch let error as FactoryStatusProbeError {
785
- if case .notLoggedIn = error, bearerToken != nil {
786
- logger("Retrying without Authorization header")
787
- return try await self.fetchWithCookieHeader(header, bearerToken: nil, baseURL: baseURL)
788
- }
789
- guard case let .networkError(message) = error,
790
- message.contains("HTTP 409")
791
- else {
792
- throw error
793
- }
794
-
795
- var lastError: Error? = error
796
- if bearerToken != nil {
797
- logger("Retrying without Authorization header (HTTP 409)")
798
- do {
799
- return try await self.fetchWithCookieHeader(header, bearerToken: nil, baseURL: baseURL)
800
- } catch {
801
- lastError = error
802
- }
803
- }
804
-
805
- let retries: [(String, (HTTPCookie) -> Bool)] = [
806
- ("Retrying without access-token cookies", { !Self.staleTokenCookieNames.contains($0.name) }),
807
- ("Retrying without session cookies", { !Self.sessionCookieNames.contains($0.name) }),
808
- ("Retrying without access-token/session cookies", {
809
- !Self.staleTokenCookieNames.contains($0.name) && !Self.sessionCookieNames.contains($0.name)
810
- }),
811
- ]
812
-
813
- for (label, predicate) in retries {
814
- let filtered = cookies.filter(predicate)
815
- guard filtered.count < cookies.count else { continue }
816
- logger(label)
817
- do {
818
- let filteredBearer = Self.bearerToken(from: filtered)
819
- return try await self.fetchWithCookieHeader(
820
- Self.cookieHeader(from: filtered),
821
- bearerToken: filteredBearer,
822
- baseURL: baseURL)
823
- } catch let retryError as FactoryStatusProbeError {
824
- switch retryError {
825
- case let .networkError(retryMessage)
826
- where retryMessage.contains("HTTP 409") &&
827
- retryMessage.localizedCaseInsensitiveContains("stale token"):
828
- lastError = retryError
829
- continue
830
- case .notLoggedIn:
831
- lastError = retryError
832
- continue
833
- default:
834
- throw retryError
835
- }
836
- }
837
- }
838
-
839
- let authOnly = cookies.filter {
840
- Self.authSessionCookieNames.contains($0.name) || $0.name == "__Host-authjs.csrf-token"
841
- }
842
- if !authOnly.isEmpty, authOnly.count < cookies.count {
843
- logger("Retrying with auth session cookies only")
844
- do {
845
- return try await self.fetchWithCookieHeader(
846
- Self.cookieHeader(from: authOnly),
847
- bearerToken: Self.bearerToken(from: authOnly),
848
- baseURL: baseURL)
849
- } catch let retryError as FactoryStatusProbeError {
850
- lastError = retryError
851
- }
852
- }
853
-
854
- if let lastError { throw lastError }
855
- throw error
856
- } catch {
857
- throw error
858
- }
859
- }
860
-
861
- private static func cookieHeader(from cookies: [HTTPCookie]) -> String {
862
- cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
863
- }
864
-
865
- private func fetchWithCookieHeader(
866
- _ cookieHeader: String,
867
- bearerToken: String?,
868
- baseURL: URL) async throws -> FactoryStatusSnapshot
869
- {
870
- // First fetch auth info to get user ID and org info
871
- let authInfo = try await self.fetchAuthInfo(
872
- cookieHeader: cookieHeader,
873
- bearerToken: bearerToken,
874
- baseURL: baseURL)
875
-
876
- // Extract user ID from JWT in the auth response or use a default endpoint
877
- let userId = self.extractUserIdFromAuth(authInfo)
878
-
879
- // Fetch usage data
880
- let usageData = try await self.fetchUsage(
881
- cookieHeader: cookieHeader,
882
- bearerToken: bearerToken,
883
- userId: userId,
884
- baseURL: baseURL)
885
-
886
- return self.buildSnapshot(authInfo: authInfo, usageData: usageData, userId: userId)
887
- }
888
-
889
- private func fetchAuthInfo(
890
- cookieHeader: String,
891
- bearerToken: String?,
892
- baseURL: URL) async throws -> FactoryAuthResponse
893
- {
894
- let url = baseURL.appendingPathComponent("/api/app/auth/me")
895
- var request = URLRequest(url: url)
896
- request.timeoutInterval = self.timeout
897
- request.httpMethod = "GET"
898
- request.setValue("application/json", forHTTPHeaderField: "Accept")
899
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
900
- request.setValue("https://app.factory.ai", forHTTPHeaderField: "Origin")
901
- request.setValue("https://app.factory.ai/", forHTTPHeaderField: "Referer")
902
- if !cookieHeader.isEmpty {
903
- request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
904
- }
905
- request.setValue("web-app", forHTTPHeaderField: "x-factory-client")
906
- if let bearerToken {
907
- request.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
908
- }
909
-
910
- let (data, response) = try await URLSession.shared.data(for: request)
911
-
912
- guard let httpResponse = response as? HTTPURLResponse else {
913
- throw FactoryStatusProbeError.networkError("Invalid response")
914
- }
915
-
916
- if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
917
- throw FactoryStatusProbeError.notLoggedIn
918
- }
919
-
920
- guard httpResponse.statusCode == 200 else {
921
- let body = String(data: data, encoding: .utf8)?
922
- .trimmingCharacters(in: .whitespacesAndNewlines) ?? "<binary>"
923
- let snippet = body.isEmpty ? "" : ": \(body.prefix(200))"
924
- throw FactoryStatusProbeError.networkError("HTTP \(httpResponse.statusCode)\(snippet)")
925
- }
926
-
927
- do {
928
- let decoder = JSONDecoder()
929
- return try decoder.decode(FactoryAuthResponse.self, from: data)
930
- } catch {
931
- let rawJSON = String(data: data, encoding: .utf8) ?? "<binary>"
932
- throw FactoryStatusProbeError
933
- .parseFailed("Auth decode failed: \(error.localizedDescription). Raw: \(rawJSON.prefix(200))")
934
- }
935
- }
936
-
937
- private func fetchUsage(
938
- cookieHeader: String,
939
- bearerToken: String?,
940
- userId: String?,
941
- baseURL: URL) async throws -> FactoryUsageResponse
942
- {
943
- let url = baseURL.appendingPathComponent("/api/organization/subscription/usage")
944
- var request = URLRequest(url: url)
945
- request.timeoutInterval = self.timeout
946
- request.httpMethod = "POST"
947
- request.setValue("application/json", forHTTPHeaderField: "Accept")
948
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
949
- request.setValue("https://app.factory.ai", forHTTPHeaderField: "Origin")
950
- request.setValue("https://app.factory.ai/", forHTTPHeaderField: "Referer")
951
- if !cookieHeader.isEmpty {
952
- request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
953
- }
954
- request.setValue("web-app", forHTTPHeaderField: "x-factory-client")
955
- if let bearerToken {
956
- request.setValue("Bearer \(bearerToken)", forHTTPHeaderField: "Authorization")
957
- }
958
-
959
- // Build request body
960
- var body: [String: Any] = ["useCache": true]
961
- if let userId {
962
- body["userId"] = userId
963
- }
964
- request.httpBody = try? JSONSerialization.data(withJSONObject: body)
965
-
966
- let (data, response) = try await URLSession.shared.data(for: request)
967
-
968
- guard let httpResponse = response as? HTTPURLResponse else {
969
- throw FactoryStatusProbeError.networkError("Invalid response")
970
- }
971
-
972
- if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 {
973
- throw FactoryStatusProbeError.notLoggedIn
974
- }
975
-
976
- guard httpResponse.statusCode == 200 else {
977
- let body = String(data: data, encoding: .utf8)?
978
- .trimmingCharacters(in: .whitespacesAndNewlines) ?? "<binary>"
979
- let snippet = body.isEmpty ? "" : ": \(body.prefix(200))"
980
- throw FactoryStatusProbeError.networkError("HTTP \(httpResponse.statusCode)\(snippet)")
981
- }
982
-
983
- do {
984
- let decoder = JSONDecoder()
985
- return try decoder.decode(FactoryUsageResponse.self, from: data)
986
- } catch {
987
- let rawJSON = String(data: data, encoding: .utf8) ?? "<binary>"
988
- throw FactoryStatusProbeError
989
- .parseFailed("Usage decode failed: \(error.localizedDescription). Raw: \(rawJSON.prefix(200))")
990
- }
991
- }
992
-
993
- private static func baseURLCandidates(default baseURL: URL, cookies: [HTTPCookie]) -> [URL] {
994
- let cookieDomains = Set(
995
- cookies.map {
996
- $0.domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
997
- })
998
-
999
- var candidates: [URL] = []
1000
- if cookieDomains.contains("auth.factory.ai") {
1001
- candidates.append(Self.authBaseURL)
1002
- }
1003
- candidates.append(Self.apiBaseURL)
1004
- candidates.append(Self.appBaseURL)
1005
- candidates.append(baseURL)
1006
-
1007
- var seen = Set<String>()
1008
- return candidates.filter { url in
1009
- let key = url.absoluteString
1010
- if seen.contains(key) { return false }
1011
- seen.insert(key)
1012
- return true
1013
- }
1014
- }
1015
-
1016
- private static func bearerToken(from cookies: [HTTPCookie]) -> String? {
1017
- let accessToken = cookies.first(where: { $0.name == "access-token" })?.value
1018
- let sessionToken = cookies.first(where: { Self.authSessionCookieNames.contains($0.name) })?.value
1019
- let legacySession = cookies.first(where: { $0.name == "session" })?.value
1020
-
1021
- if let accessToken, accessToken.contains(".") {
1022
- return accessToken
1023
- }
1024
- if let sessionToken, sessionToken.contains(".") {
1025
- return sessionToken
1026
- }
1027
- if let legacySession, legacySession.contains(".") {
1028
- return legacySession
1029
- }
1030
- return accessToken ?? sessionToken
1031
- }
1032
-
1033
- private func fetchWithBearerToken(
1034
- _ bearerToken: String,
1035
- logger: (String) -> Void) async throws -> FactoryStatusSnapshot
1036
- {
1037
- let candidates = [Self.apiBaseURL, self.baseURL]
1038
- var lastError: Error?
1039
- for baseURL in candidates {
1040
- if baseURL != Self.apiBaseURL {
1041
- logger("Trying Factory bearer base URL: \(baseURL.host ?? baseURL.absoluteString)")
1042
- }
1043
- do {
1044
- return try await self.fetchWithCookieHeader(
1045
- "",
1046
- bearerToken: bearerToken,
1047
- baseURL: baseURL)
1048
- } catch {
1049
- lastError = error
1050
- }
1051
- }
1052
- if let lastError { throw lastError }
1053
- throw FactoryStatusProbeError.notLoggedIn
1054
- }
1055
-
1056
- private func fetchWorkOSAccessToken(
1057
- refreshToken: String,
1058
- organizationID: String?) async throws -> WorkOSAuthResponse
1059
- {
1060
- var lastError: Error?
1061
- for clientID in Self.workosClientIDs {
1062
- do {
1063
- return try await self.fetchWorkOSAccessToken(
1064
- refreshToken: refreshToken,
1065
- organizationID: organizationID,
1066
- clientID: clientID)
1067
- } catch {
1068
- lastError = error
1069
- }
1070
- }
1071
- if let lastError { throw lastError }
1072
- throw FactoryStatusProbeError.networkError("WorkOS auth failed")
1073
- }
1074
-
1075
- private func fetchWorkOSAccessToken(
1076
- refreshToken: String,
1077
- organizationID: String?,
1078
- clientID: String) async throws -> WorkOSAuthResponse
1079
- {
1080
- guard let url = URL(string: "https://api.workos.com/user_management/authenticate") else {
1081
- throw FactoryStatusProbeError.networkError("WorkOS auth URL unavailable")
1082
- }
1083
-
1084
- var request = URLRequest(url: url)
1085
- request.timeoutInterval = self.timeout
1086
- request.httpMethod = "POST"
1087
- request.setValue("application/json", forHTTPHeaderField: "Accept")
1088
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
1089
-
1090
- var body: [String: Any] = [
1091
- "client_id": clientID,
1092
- "grant_type": "refresh_token",
1093
- "refresh_token": refreshToken,
1094
- ]
1095
- if let organizationID {
1096
- body["organization_id"] = organizationID
1097
- }
1098
- request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
1099
-
1100
- let (data, response) = try await URLSession.shared.data(for: request)
1101
- guard let httpResponse = response as? HTTPURLResponse else {
1102
- throw FactoryStatusProbeError.networkError("Invalid WorkOS response")
1103
- }
1104
-
1105
- guard httpResponse.statusCode == 200 else {
1106
- if httpResponse.statusCode == 400, Self.isMissingWorkOSRefreshToken(data) {
1107
- throw FactoryStatusProbeError.noSessionCookie
1108
- }
1109
- let body = String(data: data, encoding: .utf8)?
1110
- .trimmingCharacters(in: .whitespacesAndNewlines) ?? "<binary>"
1111
- let snippet = body.isEmpty ? "" : ": \(body.prefix(200))"
1112
- throw FactoryStatusProbeError.networkError("WorkOS HTTP \(httpResponse.statusCode)\(snippet)")
1113
- }
1114
-
1115
- let decoder = JSONDecoder()
1116
- return try decoder.decode(WorkOSAuthResponse.self, from: data)
1117
- }
1118
-
1119
- private func fetchWorkOSAccessTokenWithCookies(
1120
- cookies: [HTTPCookie],
1121
- logger: (String) -> Void) async throws -> WorkOSAuthResponse
1122
- {
1123
- let cookieHeader = Self.cookieHeader(from: cookies)
1124
- guard !cookieHeader.isEmpty else {
1125
- throw FactoryStatusProbeError.networkError("Missing WorkOS cookies")
1126
- }
1127
-
1128
- var lastError: Error?
1129
- for clientID in Self.workosClientIDs {
1130
- do {
1131
- return try await self.fetchWorkOSAccessTokenWithCookies(
1132
- cookieHeader: cookieHeader,
1133
- organizationID: nil,
1134
- clientID: clientID)
1135
- } catch {
1136
- lastError = error
1137
- logger("WorkOS cookie auth failed for client \(clientID): \(error.localizedDescription)")
1138
- }
1139
- }
1140
- if let lastError { throw lastError }
1141
- throw FactoryStatusProbeError.networkError("WorkOS cookie auth failed")
1142
- }
1143
-
1144
- private func fetchWorkOSAccessTokenWithCookies(
1145
- cookieHeader: String,
1146
- organizationID: String?,
1147
- clientID: String) async throws -> WorkOSAuthResponse
1148
- {
1149
- guard let url = URL(string: "https://api.workos.com/user_management/authenticate") else {
1150
- throw FactoryStatusProbeError.networkError("WorkOS auth URL unavailable")
1151
- }
1152
-
1153
- var request = URLRequest(url: url)
1154
- request.timeoutInterval = self.timeout
1155
- request.httpMethod = "POST"
1156
- request.setValue("application/json", forHTTPHeaderField: "Accept")
1157
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
1158
- request.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
1159
-
1160
- var body: [String: Any] = [
1161
- "client_id": clientID,
1162
- "grant_type": "refresh_token",
1163
- "useCookie": true,
1164
- ]
1165
- if let organizationID {
1166
- body["organization_id"] = organizationID
1167
- }
1168
- request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
1169
-
1170
- let (data, response) = try await URLSession.shared.data(for: request)
1171
- guard let httpResponse = response as? HTTPURLResponse else {
1172
- throw FactoryStatusProbeError.networkError("Invalid WorkOS response")
1173
- }
1174
-
1175
- guard httpResponse.statusCode == 200 else {
1176
- if httpResponse.statusCode == 400, Self.isMissingWorkOSRefreshToken(data) {
1177
- throw FactoryStatusProbeError.noSessionCookie
1178
- }
1179
- let bodyText = String(data: data, encoding: .utf8)?
1180
- .trimmingCharacters(in: .whitespacesAndNewlines) ?? "<binary>"
1181
- let snippet = bodyText.isEmpty ? "" : ": \(bodyText.prefix(200))"
1182
- throw FactoryStatusProbeError.networkError("WorkOS HTTP \(httpResponse.statusCode)\(snippet)")
1183
- }
1184
-
1185
- let decoder = JSONDecoder()
1186
- return try decoder.decode(WorkOSAuthResponse.self, from: data)
1187
- }
1188
-
1189
- private func isInvalidGrant(_ error: Error) -> Bool {
1190
- guard case let FactoryStatusProbeError.networkError(message) = error else {
1191
- return false
1192
- }
1193
- return message.localizedCaseInsensitiveContains("invalid_grant")
1194
- }
1195
-
1196
- static func isMissingWorkOSRefreshToken(_ data: Data) -> Bool {
1197
- guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
1198
- return false
1199
- }
1200
- guard let description = json["error_description"] as? String else { return false }
1201
- return description.localizedCaseInsensitiveContains("missing refresh token")
1202
- }
1203
-
1204
- private func extractUserIdFromAuth(_ auth: FactoryAuthResponse) -> String? {
1205
- // The user ID might be in the organization or we might need to parse JWT
1206
- // For now, return nil and let the API handle it
1207
- nil
1208
- }
1209
-
1210
- private func buildSnapshot(
1211
- authInfo: FactoryAuthResponse,
1212
- usageData: FactoryUsageResponse,
1213
- userId: String?) -> FactoryStatusSnapshot
1214
- {
1215
- let usage = usageData.usage
1216
-
1217
- let periodStart: Date? = usage?.startDate.map { Date(timeIntervalSince1970: TimeInterval($0) / 1000) }
1218
- let periodEnd: Date? = usage?.endDate.map { Date(timeIntervalSince1970: TimeInterval($0) / 1000) }
1219
-
1220
- return FactoryStatusSnapshot(
1221
- standardUserTokens: usage?.standard?.userTokens ?? 0,
1222
- standardOrgTokens: usage?.standard?.orgTotalTokensUsed ?? 0,
1223
- standardAllowance: usage?.standard?.totalAllowance ?? 0,
1224
- premiumUserTokens: usage?.premium?.userTokens ?? 0,
1225
- premiumOrgTokens: usage?.premium?.orgTotalTokensUsed ?? 0,
1226
- premiumAllowance: usage?.premium?.totalAllowance ?? 0,
1227
- periodStart: periodStart,
1228
- periodEnd: periodEnd,
1229
- planName: authInfo.organization?.subscription?.orbSubscription?.plan?.name,
1230
- tier: authInfo.organization?.subscription?.factoryTier,
1231
- organizationName: authInfo.organization?.name,
1232
- accountEmail: nil, // Email is in JWT, not in auth response body
1233
- userId: userId ?? usageData.userId,
1234
- rawJSON: nil)
1235
- }
1236
- }
1237
-
1238
- #else
1239
-
1240
- // MARK: - Factory (Unsupported)
1241
-
1242
- public enum FactoryStatusProbeError: LocalizedError, Sendable {
1243
- case notSupported
1244
-
1245
- public var errorDescription: String? {
1246
- "Factory is only supported on macOS."
1247
- }
1248
- }
1249
-
1250
- public struct FactoryStatusSnapshot: Sendable {
1251
- public init() {}
1252
-
1253
- public func toUsageSnapshot() -> UsageSnapshot {
1254
- UsageSnapshot(
1255
- primary: RateWindow(usedPercent: 0, windowMinutes: nil, resetsAt: nil, resetDescription: nil),
1256
- secondary: nil,
1257
- tertiary: nil,
1258
- providerCost: nil,
1259
- updatedAt: Date(),
1260
- identity: nil)
1261
- }
1262
- }
1263
-
1264
- public struct FactoryStatusProbe: Sendable {
1265
- public init(baseURL: URL = URL(string: "https://app.factory.ai")!, timeout: TimeInterval = 15.0) {
1266
- _ = baseURL
1267
- _ = timeout
1268
- }
1269
-
1270
- public func fetch(logger: ((String) -> Void)? = nil) async throws -> FactoryStatusSnapshot {
1271
- _ = logger
1272
- throw FactoryStatusProbeError.notSupported
1273
- }
1274
- }
1275
-
1276
- #endif