@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,288 +0,0 @@
1
- import Foundation
2
- import CloudKit
3
-
4
- // MARK: - Sync Protocol Definitions
5
-
6
- /// Protocol defining the core synchronization engine interface
7
- ///
8
- /// This protocol abstracts the synchronization logic to allow for different
9
- /// implementations (CloudKit, custom server, etc.) while maintaining a
10
- /// consistent API for syncing data across devices.
11
- public protocol SyncEngine: Sendable {
12
- /// Synchronizes local data with remote storage
13
- ///
14
- /// - Parameter options: Configuration options for this sync operation
15
- /// - Returns: Result indicating success or failure with error details
16
- func sync(options: SyncOptions) async -> Result<SyncResult, SyncError>
17
-
18
- /// Pushes local changes to remote storage
19
- ///
20
- /// - Parameter records: Array of records to push
21
- /// - Returns: Result containing pushed record identifiers or error
22
- func push(records: [SyncableRecord]) async -> Result<[String], SyncError>
23
-
24
- /// Fetches changes from remote storage
25
- ///
26
- /// - Parameter recordTypes: Array of record type names to fetch
27
- /// - Returns: Result containing fetched records or error
28
- func fetch(recordTypes: [String]) async -> Result<[SyncableRecord], SyncError>
29
-
30
- /// Deletes records from remote storage
31
- ///
32
- /// - Parameter recordIDs: Array of record identifiers to delete
33
- /// - Returns: Result containing deleted record identifiers or error
34
- func delete(recordIDs: [String]) async -> Result<[String], SyncError>
35
-
36
- /// Resets sync state and clears all remote data
37
- ///
38
- /// - Warning: This operation is destructive and cannot be undone
39
- /// - Returns: Result indicating success or failure
40
- func resetSync() async -> Result<Void, SyncError>
41
- }
42
-
43
- // MARK: - Syncable Record Protocol
44
-
45
- /// Protocol for objects that can be synchronized across devices
46
- ///
47
- /// Conform to this protocol to enable CloudKit synchronization for your data types.
48
- public protocol SyncableRecord: Sendable, Codable {
49
- /// Unique identifier for the record
50
- var recordID: String { get }
51
-
52
- /// Type name used for CloudKit record type
53
- var recordType: String { get }
54
-
55
- /// Version number for conflict resolution
56
- var version: Int { get }
57
-
58
- /// Last modification timestamp
59
- var modifiedAt: Date { get }
60
-
61
- /// Device identifier that last modified this record
62
- var lastModifiedDeviceID: String? { get }
63
-
64
- /// Converts the record to a CloudKit CKRecord
65
- ///
66
- /// - Returns: CloudKit record representation
67
- func toCKRecord() throws -> CKRecord
68
-
69
- /// Creates a record from a CloudKit CKRecord
70
- ///
71
- /// - Parameter ckRecord: CloudKit record to convert
72
- /// - Returns: Initialized syncable record
73
- static func fromCKRecord(_ ckRecord: CKRecord) throws -> Self
74
- }
75
-
76
- // MARK: - Sync Conflict Resolver Protocol
77
-
78
- /// Protocol for resolving conflicts when multiple devices modify the same record
79
- ///
80
- /// Implement this protocol to define custom conflict resolution strategies
81
- /// beyond the default last-write-wins behavior.
82
- public protocol SyncConflictResolverProtocol: Sendable {
83
- /// Resolves a conflict between local and remote versions of a record
84
- ///
85
- /// - Parameters:
86
- /// - local: The local version of the conflicting record
87
- /// - remote: The remote version of the conflicting record
88
- /// - strategy: The conflict resolution strategy to apply
89
- /// - Returns: The resolved record that should be kept
90
- func resolve(
91
- local: SyncableRecord,
92
- remote: SyncableRecord,
93
- strategy: ConflictResolutionStrategy
94
- ) async -> SyncableRecord
95
- }
96
-
97
- // MARK: - Sync Observer Protocol
98
-
99
- /// Protocol for observing synchronization events
100
- ///
101
- /// Implement this protocol to receive notifications about sync state changes,
102
- /// progress updates, and completion events.
103
- public protocol SyncObserver: AnyObject, Sendable {
104
- /// Called when synchronization begins
105
- func syncDidStart()
106
-
107
- /// Called when synchronization progress updates
108
- ///
109
- /// - Parameters:
110
- /// - current: Number of records processed
111
- /// - total: Total number of records to process
112
- func syncDidProgress(current: Int, total: Int)
113
-
114
- /// Called when synchronization completes successfully
115
- ///
116
- /// - Parameter result: The sync result containing statistics
117
- func syncDidComplete(result: SyncResult)
118
-
119
- /// Called when synchronization fails
120
- ///
121
- /// - Parameter error: The error that caused the failure
122
- func syncDidFail(error: SyncError)
123
- }
124
-
125
- // MARK: - Supporting Types
126
-
127
- /// Configuration options for sync operations
128
- public struct SyncOptions: Sendable {
129
- /// Whether to force a full sync, ignoring change tracking
130
- public let forceFullSync: Bool
131
-
132
- /// Maximum number of records to sync in a single batch
133
- public let batchSize: Int
134
-
135
- /// Conflict resolution strategy to use
136
- public let conflictStrategy: ConflictResolutionStrategy
137
-
138
- /// Whether to sync in background mode
139
- public let backgroundMode: Bool
140
-
141
- /// Timeout interval for sync operations
142
- public let timeout: TimeInterval
143
-
144
- /// Whether to encrypt sensitive data
145
- public let encryptSensitiveData: Bool
146
-
147
- public init(
148
- forceFullSync: Bool = false,
149
- batchSize: Int = 100,
150
- conflictStrategy: ConflictResolutionStrategy = .lastWriteWins,
151
- backgroundMode: Bool = false,
152
- timeout: TimeInterval = 60.0,
153
- encryptSensitiveData: Bool = true
154
- ) {
155
- self.forceFullSync = forceFullSync
156
- self.batchSize = batchSize
157
- self.conflictStrategy = conflictStrategy
158
- self.backgroundMode = backgroundMode
159
- self.timeout = timeout
160
- self.encryptSensitiveData = encryptSensitiveData
161
- }
162
- }
163
-
164
- /// Result of a sync operation with statistics
165
- public struct SyncResult: Sendable {
166
- /// Number of records successfully pushed
167
- public let pushedCount: Int
168
-
169
- /// Number of records successfully fetched
170
- public let fetchedCount: Int
171
-
172
- /// Number of conflicts resolved
173
- public let conflictsResolved: Int
174
-
175
- /// Number of records deleted
176
- public let deletedCount: Int
177
-
178
- /// Timestamp when sync completed
179
- public let completedAt: Date
180
-
181
- /// Duration of the sync operation
182
- public let duration: TimeInterval
183
-
184
- /// Any non-fatal warnings encountered
185
- public let warnings: [String]
186
-
187
- public init(
188
- pushedCount: Int = 0,
189
- fetchedCount: Int = 0,
190
- conflictsResolved: Int = 0,
191
- deletedCount: Int = 0,
192
- completedAt: Date = Date(),
193
- duration: TimeInterval = 0,
194
- warnings: [String] = []
195
- ) {
196
- self.pushedCount = pushedCount
197
- self.fetchedCount = fetchedCount
198
- self.conflictsResolved = conflictsResolved
199
- self.deletedCount = deletedCount
200
- self.completedAt = completedAt
201
- self.duration = duration
202
- self.warnings = warnings
203
- }
204
- }
205
-
206
- /// Conflict resolution strategies
207
- public enum ConflictResolutionStrategy: String, Codable, Sendable {
208
- /// Keep the most recently modified record (default)
209
- case lastWriteWins
210
-
211
- /// Keep the local version
212
- case preferLocal
213
-
214
- /// Keep the remote version
215
- case preferRemote
216
-
217
- /// Keep the version with higher version number
218
- case highestVersion
219
-
220
- /// Merge both versions (requires custom merge logic)
221
- case merge
222
- }
223
-
224
- /// Errors that can occur during synchronization
225
- public enum SyncError: Error, Sendable {
226
- /// iCloud account is not available
227
- case iCloudAccountUnavailable
228
-
229
- /// Network connection is not available
230
- case networkUnavailable
231
-
232
- /// CloudKit quota exceeded
233
- case quotaExceeded
234
-
235
- /// Record format is invalid
236
- case invalidRecordFormat(String)
237
-
238
- /// Sync operation timed out
239
- case timeout
240
-
241
- /// Authentication failed
242
- case authenticationFailed
243
-
244
- /// Conflict could not be resolved
245
- case conflictResolutionFailed(String)
246
-
247
- /// Generic CloudKit error
248
- case cloudKitError(Error)
249
-
250
- /// Encryption/decryption failed
251
- case encryptionFailed(String)
252
-
253
- /// Unknown error occurred
254
- case unknown(Error)
255
-
256
- public var localizedDescription: String {
257
- switch self {
258
- case .iCloudAccountUnavailable:
259
- return "iCloud account is not available. Please sign in to iCloud in Settings."
260
- case .networkUnavailable:
261
- return "Network connection is not available. Sync will retry when online."
262
- case .quotaExceeded:
263
- return "iCloud storage quota exceeded. Please free up space or upgrade your plan."
264
- case .invalidRecordFormat(let details):
265
- return "Invalid record format: \(details)"
266
- case .timeout:
267
- return "Sync operation timed out. Please try again."
268
- case .authenticationFailed:
269
- return "Authentication failed. Please sign in again."
270
- case .conflictResolutionFailed(let details):
271
- return "Failed to resolve conflict: \(details)"
272
- case .cloudKitError(let error):
273
- return "CloudKit error: \(error.localizedDescription)"
274
- case .encryptionFailed(let details):
275
- return "Encryption failed: \(details)"
276
- case .unknown(let error):
277
- return "Unknown error: \(error.localizedDescription)"
278
- }
279
- }
280
- }
281
-
282
- /// Priority levels for sync operations
283
- public enum SyncPriority: Int, Sendable {
284
- case low = 0
285
- case normal = 1
286
- case high = 2
287
- case critical = 3
288
- }
@@ -1,405 +0,0 @@
1
- import Foundation
2
- import CloudKit
3
- import CryptoKit
4
- import LocalAuthentication
5
-
6
- // MARK: - CloudKit Record Type Constants
7
-
8
- /// CloudKit record type identifiers
9
- public enum CloudKitRecordType {
10
- public static let usageSnapshot = "UsageSnapshot"
11
- public static let userPreferences = "UserPreferences"
12
- public static let alertConfiguration = "AlertConfiguration"
13
- public static let syncMetadata = "SyncMetadata"
14
- }
15
-
16
- // MARK: - Usage Snapshot Sync Record
17
-
18
- /// Syncable usage snapshot record for cross-device synchronization
19
- ///
20
- /// This record encapsulates usage data that can be synchronized across
21
- /// devices using CloudKit, with support for encryption and versioning.
22
- public struct UsageSnapshotSyncRecord: SyncableRecord {
23
- public let recordID: String
24
- public let recordType: String = CloudKitRecordType.usageSnapshot
25
- public let version: Int
26
- public let modifiedAt: Date
27
- public let lastModifiedDeviceID: String?
28
-
29
- // Usage data
30
- public let providerID: String
31
- public let primaryUsed: Int
32
- public let primaryLimit: Int?
33
- public let secondaryUsed: Int?
34
- public let secondaryLimit: Int?
35
- public let costUSD: Double?
36
- public let accountEmail: String?
37
- public let updatedAt: Date
38
-
39
- // Metadata
40
- public let deviceName: String
41
- public let platform: String
42
-
43
- public init(
44
- recordID: String = UUID().uuidString,
45
- version: Int = 1,
46
- modifiedAt: Date = Date(),
47
- lastModifiedDeviceID: String? = nil,
48
- providerID: String,
49
- primaryUsed: Int,
50
- primaryLimit: Int?,
51
- secondaryUsed: Int? = nil,
52
- secondaryLimit: Int? = nil,
53
- costUSD: Double? = nil,
54
- accountEmail: String? = nil,
55
- updatedAt: Date = Date(),
56
- deviceName: String,
57
- platform: String
58
- ) {
59
- self.recordID = recordID
60
- self.version = version
61
- self.modifiedAt = modifiedAt
62
- self.lastModifiedDeviceID = lastModifiedDeviceID
63
- self.providerID = providerID
64
- self.primaryUsed = primaryUsed
65
- self.primaryLimit = primaryLimit
66
- self.secondaryUsed = secondaryUsed
67
- self.secondaryLimit = secondaryLimit
68
- self.costUSD = costUSD
69
- self.accountEmail = accountEmail
70
- self.updatedAt = updatedAt
71
- self.deviceName = deviceName
72
- self.platform = platform
73
- }
74
-
75
- public func toCKRecord() throws -> CKRecord {
76
- let record = CKRecord(recordType: recordType, recordID: CKRecord.ID(recordName: recordID))
77
-
78
- record["version"] = version as CKRecordValue
79
- record["modifiedAt"] = modifiedAt as CKRecordValue
80
- record["lastModifiedDeviceID"] = lastModifiedDeviceID as? CKRecordValue
81
- record["providerID"] = providerID as CKRecordValue
82
- record["primaryUsed"] = primaryUsed as CKRecordValue
83
- record["primaryLimit"] = primaryLimit as? CKRecordValue
84
- record["secondaryUsed"] = secondaryUsed as? CKRecordValue
85
- record["secondaryLimit"] = secondaryLimit as? CKRecordValue
86
- record["costUSD"] = costUSD as? CKRecordValue
87
- record["updatedAt"] = updatedAt as CKRecordValue
88
- record["deviceName"] = deviceName as CKRecordValue
89
- record["platform"] = platform as CKRecordValue
90
-
91
- // Encrypt sensitive data
92
- if let email = accountEmail {
93
- let encrypted = try encryptString(email)
94
- record["accountEmail"] = encrypted as CKRecordValue
95
- }
96
-
97
- return record
98
- }
99
-
100
- public static func fromCKRecord(_ ckRecord: CKRecord) throws -> UsageSnapshotSyncRecord {
101
- guard let version = ckRecord["version"] as? Int,
102
- let modifiedAt = ckRecord["modifiedAt"] as? Date,
103
- let providerID = ckRecord["providerID"] as? String,
104
- let primaryUsed = ckRecord["primaryUsed"] as? Int,
105
- let updatedAt = ckRecord["updatedAt"] as? Date,
106
- let deviceName = ckRecord["deviceName"] as? String,
107
- let platform = ckRecord["platform"] as? String
108
- else {
109
- throw SyncError.invalidRecordFormat("Missing required fields in UsageSnapshot")
110
- }
111
-
112
- var accountEmail: String?
113
- if let encryptedEmail = ckRecord["accountEmail"] as? String {
114
- accountEmail = try? decryptString(encryptedEmail)
115
- }
116
-
117
- return UsageSnapshotSyncRecord(
118
- recordID: ckRecord.recordID.recordName,
119
- version: version,
120
- modifiedAt: modifiedAt,
121
- lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
122
- providerID: providerID,
123
- primaryUsed: primaryUsed,
124
- primaryLimit: ckRecord["primaryLimit"] as? Int,
125
- secondaryUsed: ckRecord["secondaryUsed"] as? Int,
126
- secondaryLimit: ckRecord["secondaryLimit"] as? Int,
127
- costUSD: ckRecord["costUSD"] as? Double,
128
- accountEmail: accountEmail,
129
- updatedAt: updatedAt,
130
- deviceName: deviceName,
131
- platform: platform
132
- )
133
- }
134
- }
135
-
136
- // MARK: - User Preferences Sync Record
137
-
138
- /// Syncable user preferences record
139
- ///
140
- /// Stores user preferences and settings that should be synchronized
141
- /// across all devices where the user is signed in.
142
- public struct UserPreferencesSyncRecord: SyncableRecord {
143
- public let recordID: String
144
- public let recordType: String = CloudKitRecordType.userPreferences
145
- public let version: Int
146
- public let modifiedAt: Date
147
- public let lastModifiedDeviceID: String?
148
-
149
- // Preferences
150
- public let refreshInterval: TimeInterval
151
- public let enabledProviders: [String]
152
- public let notificationsEnabled: Bool
153
- public let autoRefreshEnabled: Bool
154
- public let theme: String
155
- public let displayFormat: String
156
-
157
- public init(
158
- recordID: String = "user-preferences",
159
- version: Int = 1,
160
- modifiedAt: Date = Date(),
161
- lastModifiedDeviceID: String? = nil,
162
- refreshInterval: TimeInterval = 300,
163
- enabledProviders: [String] = [],
164
- notificationsEnabled: Bool = true,
165
- autoRefreshEnabled: Bool = true,
166
- theme: String = "system",
167
- displayFormat: String = "compact"
168
- ) {
169
- self.recordID = recordID
170
- self.version = version
171
- self.modifiedAt = modifiedAt
172
- self.lastModifiedDeviceID = lastModifiedDeviceID
173
- self.refreshInterval = refreshInterval
174
- self.enabledProviders = enabledProviders
175
- self.notificationsEnabled = notificationsEnabled
176
- self.autoRefreshEnabled = autoRefreshEnabled
177
- self.theme = theme
178
- self.displayFormat = displayFormat
179
- }
180
-
181
- public func toCKRecord() throws -> CKRecord {
182
- let record = CKRecord(recordType: recordType, recordID: CKRecord.ID(recordName: recordID))
183
-
184
- record["version"] = version as CKRecordValue
185
- record["modifiedAt"] = modifiedAt as CKRecordValue
186
- record["lastModifiedDeviceID"] = lastModifiedDeviceID as? CKRecordValue
187
- record["refreshInterval"] = refreshInterval as CKRecordValue
188
- record["enabledProviders"] = enabledProviders as CKRecordValue
189
- record["notificationsEnabled"] = (notificationsEnabled ? 1 : 0) as CKRecordValue
190
- record["autoRefreshEnabled"] = (autoRefreshEnabled ? 1 : 0) as CKRecordValue
191
- record["theme"] = theme as CKRecordValue
192
- record["displayFormat"] = displayFormat as CKRecordValue
193
-
194
- return record
195
- }
196
-
197
- public static func fromCKRecord(_ ckRecord: CKRecord) throws -> UserPreferencesSyncRecord {
198
- guard let version = ckRecord["version"] as? Int,
199
- let modifiedAt = ckRecord["modifiedAt"] as? Date,
200
- let refreshInterval = ckRecord["refreshInterval"] as? Double,
201
- let enabledProviders = ckRecord["enabledProviders"] as? [String],
202
- let notificationsEnabled = ckRecord["notificationsEnabled"] as? Int,
203
- let autoRefreshEnabled = ckRecord["autoRefreshEnabled"] as? Int,
204
- let theme = ckRecord["theme"] as? String,
205
- let displayFormat = ckRecord["displayFormat"] as? String
206
- else {
207
- throw SyncError.invalidRecordFormat("Missing required fields in UserPreferences")
208
- }
209
-
210
- return UserPreferencesSyncRecord(
211
- recordID: ckRecord.recordID.recordName,
212
- version: version,
213
- modifiedAt: modifiedAt,
214
- lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
215
- refreshInterval: refreshInterval,
216
- enabledProviders: enabledProviders,
217
- notificationsEnabled: notificationsEnabled == 1,
218
- autoRefreshEnabled: autoRefreshEnabled == 1,
219
- theme: theme,
220
- displayFormat: displayFormat
221
- )
222
- }
223
- }
224
-
225
- // MARK: - Alert Configuration Sync Record
226
-
227
- /// Syncable alert configuration record
228
- ///
229
- /// Stores alert thresholds and notification settings that should be
230
- /// consistent across all user devices.
231
- public struct AlertConfigurationSyncRecord: SyncableRecord {
232
- public let recordID: String
233
- public let recordType: String = CloudKitRecordType.alertConfiguration
234
- public let version: Int
235
- public let modifiedAt: Date
236
- public let lastModifiedDeviceID: String?
237
-
238
- // Alert settings
239
- public let providerID: String
240
- public let warningThreshold: Double
241
- public let criticalThreshold: Double
242
- public let notificationChannels: [String]
243
- public let enabled: Bool
244
-
245
- public init(
246
- recordID: String = UUID().uuidString,
247
- version: Int = 1,
248
- modifiedAt: Date = Date(),
249
- lastModifiedDeviceID: String? = nil,
250
- providerID: String,
251
- warningThreshold: Double = 0.75,
252
- criticalThreshold: Double = 0.90,
253
- notificationChannels: [String] = ["system"],
254
- enabled: Bool = true
255
- ) {
256
- self.recordID = recordID
257
- self.version = version
258
- self.modifiedAt = modifiedAt
259
- self.lastModifiedDeviceID = lastModifiedDeviceID
260
- self.providerID = providerID
261
- self.warningThreshold = warningThreshold
262
- self.criticalThreshold = criticalThreshold
263
- self.notificationChannels = notificationChannels
264
- self.enabled = enabled
265
- }
266
-
267
- public func toCKRecord() throws -> CKRecord {
268
- let record = CKRecord(recordType: recordType, recordID: CKRecord.ID(recordName: recordID))
269
-
270
- record["version"] = version as CKRecordValue
271
- record["modifiedAt"] = modifiedAt as CKRecordValue
272
- record["lastModifiedDeviceID"] = lastModifiedDeviceID as? CKRecordValue
273
- record["providerID"] = providerID as CKRecordValue
274
- record["warningThreshold"] = warningThreshold as CKRecordValue
275
- record["criticalThreshold"] = criticalThreshold as CKRecordValue
276
- record["notificationChannels"] = notificationChannels as CKRecordValue
277
- record["enabled"] = (enabled ? 1 : 0) as CKRecordValue
278
-
279
- return record
280
- }
281
-
282
- public static func fromCKRecord(_ ckRecord: CKRecord) throws -> AlertConfigurationSyncRecord {
283
- guard let version = ckRecord["version"] as? Int,
284
- let modifiedAt = ckRecord["modifiedAt"] as? Date,
285
- let providerID = ckRecord["providerID"] as? String,
286
- let warningThreshold = ckRecord["warningThreshold"] as? Double,
287
- let criticalThreshold = ckRecord["criticalThreshold"] as? Double,
288
- let notificationChannels = ckRecord["notificationChannels"] as? [String],
289
- let enabled = ckRecord["enabled"] as? Int
290
- else {
291
- throw SyncError.invalidRecordFormat("Missing required fields in AlertConfiguration")
292
- }
293
-
294
- return AlertConfigurationSyncRecord(
295
- recordID: ckRecord.recordID.recordName,
296
- version: version,
297
- modifiedAt: modifiedAt,
298
- lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
299
- providerID: providerID,
300
- warningThreshold: warningThreshold,
301
- criticalThreshold: criticalThreshold,
302
- notificationChannels: notificationChannels,
303
- enabled: enabled == 1
304
- )
305
- }
306
- }
307
-
308
- // MARK: - Encryption Helpers
309
-
310
- /// Encrypts a string using AES-GCM encryption
311
- ///
312
- /// - Parameter plaintext: The string to encrypt
313
- /// - Returns: Base64-encoded encrypted string with nonce prepended
314
- /// - Throws: SyncError.encryptionFailed if encryption fails
315
- private func encryptString(_ plaintext: String) throws -> String {
316
- guard let data = plaintext.data(using: .utf8) else {
317
- throw SyncError.encryptionFailed("Failed to convert string to data")
318
- }
319
-
320
- let key = getOrCreateEncryptionKey()
321
- let sealed = try AES.GCM.seal(data, using: key)
322
-
323
- guard let combined = sealed.combined else {
324
- throw SyncError.encryptionFailed("Failed to create sealed box")
325
- }
326
-
327
- return combined.base64EncodedString()
328
- }
329
-
330
- /// Decrypts a string that was encrypted with encryptString
331
- ///
332
- /// - Parameter encrypted: Base64-encoded encrypted string
333
- /// - Returns: Decrypted plaintext string
334
- /// - Throws: SyncError.encryptionFailed if decryption fails
335
- private func decryptString(_ encrypted: String) throws -> String {
336
- guard let combined = Data(base64Encoded: encrypted) else {
337
- throw SyncError.encryptionFailed("Failed to decode base64")
338
- }
339
-
340
- let key = getOrCreateEncryptionKey()
341
- let sealedBox = try AES.GCM.SealedBox(combined: combined)
342
- let decrypted = try AES.GCM.open(sealedBox, using: key)
343
-
344
- guard let plaintext = String(data: decrypted, encoding: .utf8) else {
345
- throw SyncError.encryptionFailed("Failed to convert decrypted data to string")
346
- }
347
-
348
- return plaintext
349
- }
350
-
351
- /// Retrieves or creates an encryption key for sensitive data
352
- ///
353
- /// The key is stored securely in the Keychain and persists across app launches.
354
- ///
355
- /// - Returns: SymmetricKey for AES-GCM encryption
356
- private func getOrCreateEncryptionKey() -> SymmetricKey {
357
- let keychainKey = "com.runic.sync.encryption.key"
358
-
359
- // Try to load existing key from Keychain
360
- if let keyData = loadFromKeychain(key: keychainKey) {
361
- return SymmetricKey(data: keyData)
362
- }
363
-
364
- // Generate new key
365
- let key = SymmetricKey(size: .bits256)
366
- let keyData = key.withUnsafeBytes { Data($0) }
367
-
368
- // Store in Keychain
369
- saveToKeychain(key: keychainKey, data: keyData)
370
-
371
- return key
372
- }
373
-
374
- private func saveToKeychain(key: String, data: Data) {
375
- let query: [String: Any] = [
376
- kSecClass as String: kSecClassGenericPassword,
377
- kSecAttrService as String: "com.sriinnu.athena.Runic",
378
- kSecAttrAccount as String: key,
379
- kSecUseDataProtectionKeychain as String: true,
380
- kSecValueData as String: data,
381
- kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock
382
- ]
383
- SecItemDelete(query as CFDictionary)
384
- SecItemAdd(query as CFDictionary, nil)
385
- }
386
-
387
- private func loadFromKeychain(key: String) -> Data? {
388
- var query: [String: Any] = [
389
- kSecClass as String: kSecClassGenericPassword,
390
- kSecAttrService as String: "com.sriinnu.athena.Runic",
391
- kSecAttrAccount as String: key,
392
- kSecUseDataProtectionKeychain as String: true,
393
- kSecUseAuthenticationUI as String: "kSecUseAuthenticationUIFail" as CFString,
394
- kSecReturnData as String: true
395
- ]
396
- let authContext = LAContext()
397
- authContext.interactionNotAllowed = true
398
- query[kSecUseAuthenticationContext as String] = authContext
399
- var result: AnyObject?
400
- let status = SecItemCopyMatching(query as CFDictionary, &result)
401
- if status == errSecInteractionNotAllowed {
402
- return nil
403
- }
404
- return status == errSecSuccess ? result as? Data : nil
405
- }