@sriinnu/runic 2.0.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.
- package/CHANGELOG.md +256 -0
- package/LICENSE +373 -21
- package/README.md +96 -89
- package/THIRD-PARTY-NOTICES.md +651 -0
- package/package.json +14 -4
- package/.swiftformat +0 -50
- package/.swiftlint.yml +0 -160
- package/CONTRIBUTING.md +0 -456
- package/Icon.icns +0 -0
- package/Icon.icon/Assets/runic.png +0 -0
- package/Icon.icon/icon.json +0 -36
- package/Package.resolved +0 -51
- package/Package.swift +0 -136
- package/Packages/Helix/LICENSE +0 -21
- package/Packages/Helix/Package.swift +0 -29
- package/Packages/Helix/README.md +0 -976
- package/Packages/Helix/Sources/Helix/Binding.swift +0 -111
- package/Packages/Helix/Sources/Helix/CommandParser.swift +0 -160
- package/Packages/Helix/Sources/Helix/CommandSignature.swift +0 -117
- package/Packages/Helix/Sources/Helix/ExpressibleFromArgument.swift +0 -72
- package/Packages/Helix/Sources/Helix/HelpGenerator.swift +0 -175
- package/Packages/Helix/Sources/Helix/NameSpecification.swift +0 -98
- package/Packages/Helix/Sources/Helix/ParsableCommand.swift +0 -204
- package/Packages/Helix/Sources/Helix/Platform/DarwinPlatformContext.swift +0 -44
- package/Packages/Helix/Sources/Helix/Platform/LinuxPlatformContext.swift +0 -34
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext+Default.swift +0 -91
- package/Packages/Helix/Sources/Helix/Platform/PlatformContext.swift +0 -94
- package/Packages/Helix/Sources/Helix/Platform/PlatformPath.swift +0 -167
- package/Packages/Helix/Sources/Helix/Platform/Stdio.swift +0 -99
- package/Packages/Helix/Sources/Helix/Platform/WebPlatformContext.swift +0 -146
- package/Packages/Helix/Sources/Helix/Platform/WindowsPlatformContext.swift +0 -97
- package/Packages/Helix/Sources/Helix/Program.swift +0 -204
- package/Packages/Helix/Sources/Helix/PropertyWrappers.swift +0 -367
- package/Packages/Silo/LICENSE +0 -21
- package/Packages/Silo/Package.swift +0 -34
- package/Packages/Silo/README.md +0 -466
- package/Packages/Silo/Sources/Silo/BinaryCookiesReader.swift +0 -169
- package/Packages/Silo/Sources/Silo/BrowserCookieAnalytics.swift +0 -365
- package/Packages/Silo/Sources/Silo/BrowserCookieBackends.swift +0 -594
- package/Packages/Silo/Sources/Silo/BrowserCookieClient.swift +0 -326
- package/Packages/Silo/Sources/Silo/BrowserCookieExport.swift +0 -154
- package/Packages/Silo/Sources/Silo/BrowserCookieImport.swift +0 -119
- package/Packages/Silo/Sources/Silo/BrowserCookieMockStore.swift +0 -121
- package/Packages/Silo/Sources/Silo/BrowserCookieModels.swift +0 -391
- package/Packages/Silo/Sources/Silo/ChromiumCookieReader.swift +0 -240
- package/Packages/Silo/Sources/Silo/CryptoSupport.swift +0 -86
- package/Packages/Silo/Sources/Silo/FirefoxCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxChromiumCookieReader.swift +0 -172
- package/Packages/Silo/Sources/Silo/LinuxSecretService.swift +0 -105
- package/Packages/Silo/Sources/Silo/MacOSChromiumCookieReader.swift +0 -191
- package/Packages/Silo/Sources/Silo/SQLiteSnapshot.swift +0 -41
- package/Packages/Silo/Sources/Silo/WindowsChromiumCookieReader.swift +0 -110
- package/RuniciOS/App/RuniciOSApp.swift +0 -105
- package/RuniciOS/Core/RunicTypographyiOS.swift +0 -24
- package/RuniciOS/Core/iOSUsageStore.swift +0 -192
- package/RuniciOS/Views/ProviderDetailView.swift +0 -312
- package/RuniciOS/Views/ProviderListView.swift +0 -228
- package/RuniciOS/Widgets/LargeWidget.swift +0 -367
- package/RuniciOS/Widgets/LiveActivity.swift +0 -342
- package/RuniciOS/Widgets/LockScreenWidget.swift +0 -273
- package/RuniciOS/Widgets/MediumWidget.swift +0 -370
- package/RuniciOS/Widgets/RunicWidget.swift +0 -274
- package/RuniciOS/Widgets/SmallWidget.swift +0 -281
- package/RuniciOS/Widgets/WidgetModels.swift +0 -285
- package/RuniciOS/Widgets/WidgetTimeline.swift +0 -270
- package/Scripts/CREATION-SUMMARY.md +0 -367
- package/Scripts/DIRECTORY-STRUCTURE.md +0 -358
- package/Scripts/GETTING-STARTED-CHECKLIST.md +0 -364
- package/Scripts/INDEX.md +0 -365
- package/Scripts/QUICK-REFERENCE.md +0 -164
- package/Scripts/README-SCRIPTS.md +0 -926
- package/Scripts/build-android.sh +0 -526
- package/Scripts/build-ios.sh +0 -529
- package/Scripts/build-macos.sh +0 -603
- package/Scripts/build-windows.sh +0 -504
- package/Scripts/build_icon.sh +0 -49
- package/Scripts/changelog-to-html.sh +0 -89
- package/Scripts/check-release-assets.sh +0 -10
- package/Scripts/ci-config.yml +0 -474
- package/Scripts/compile_and_run.sh +0 -219
- package/Scripts/compile_and_run_adhoc.sh +0 -4
- package/Scripts/docs-list.mjs +0 -119
- package/Scripts/make_appcast.sh +0 -58
- package/Scripts/package_app.sh +0 -409
- package/Scripts/release-lib.sh +0 -214
- package/Scripts/release.sh +0 -76
- package/Scripts/setup-all.sh +0 -387
- package/Scripts/setup-ios.sh +0 -404
- package/Scripts/setup-react-native.sh +0 -523
- package/Scripts/sign-and-notarize.sh +0 -122
- package/Scripts/test_live_update.sh +0 -38
- package/Scripts/validate_changelog.sh +0 -23
- package/Scripts/verify_appcast.sh +0 -92
- package/Sources/Runic/About.swift +0 -93
- package/Sources/Runic/AppNotifications.swift +0 -117
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +0 -670
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +0 -519
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +0 -824
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +0 -90
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +0 -1412
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +0 -339
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +0 -423
- package/Sources/Runic/Controllers/StatusItemController.swift +0 -360
- package/Sources/Runic/Core/BudgetNotificationManager.swift +0 -81
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +0 -103
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +0 -154
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +0 -206
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +0 -92
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +0 -1222
- package/Sources/Runic/Core/Rendering/IconView.swift +0 -122
- package/Sources/Runic/Core/Rendering/ProviderBrandIcon.swift +0 -71
- package/Sources/Runic/Core/RunicAnimation.swift +0 -53
- package/Sources/Runic/Core/RunicColors.swift +0 -73
- package/Sources/Runic/Core/RunicCornerRadius.swift +0 -15
- package/Sources/Runic/Core/RunicSpacing.swift +0 -68
- package/Sources/Runic/Core/RunicTypography.swift +0 -206
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +0 -158
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +0 -30
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/SettingsStore.swift +0 -1749
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+Status.swift +0 -189
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +0 -33
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +0 -67
- package/Sources/Runic/Core/Stores/UsageStore.swift +0 -3631
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +0 -117
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +0 -117
- package/Sources/Runic/Core/UsageExporter.swift +0 -197
- package/Sources/Runic/Date+RelativeDescription.swift +0 -21
- package/Sources/Runic/HiddenWindowView.swift +0 -32
- package/Sources/Runic/InstallOrigin.swift +0 -9
- package/Sources/Runic/KeyboardShortcuts+Names.swift +0 -6
- package/Sources/Runic/MouseLocationReader.swift +0 -65
- package/Sources/Runic/Notifications+Runic.swift +0 -6
- package/Sources/Runic/OpenAICreditsPurchaseWindowController.swift +0 -503
- package/Sources/Runic/ProviderRegistry.swift +0 -100
- package/Sources/Runic/ProviderSwitcherButtons.swift +0 -267
- package/Sources/Runic/Providers/Antigravity/AntigravityLoginFlow.swift +0 -11
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +0 -52
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +0 -42
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Claude/ClaudeLoginFlow.swift +0 -26
- package/Sources/Runic/Providers/Claude/ClaudeProviderImplementation.swift +0 -14
- package/Sources/Runic/Providers/Codex/CodexLoginFlow.swift +0 -18
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Copilot/CopilotLoginFlow.swift +0 -193
- package/Sources/Runic/Providers/Copilot/CopilotProviderImplementation.swift +0 -110
- package/Sources/Runic/Providers/Cursor/CursorLoginFlow.swift +0 -28
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Factory/FactoryLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Gemini/GeminiLoginFlow.swift +0 -20
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +0 -15
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +0 -32
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Shared/ProviderCatalog.swift +0 -15
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +0 -51
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +0 -66
- package/Sources/Runic/Providers/Shared/ProviderLoginFlow.swift +0 -12
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +0 -99
- package/Sources/Runic/Providers/Shared/SystemSettingsLinks.swift +0 -18
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +0 -24
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +0 -24
- package/Sources/Runic/Resources/Fonts/Caveat-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Caveat-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Light.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/FiraCode-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/Inconsolata-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Medium.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Bold.ttf +0 -0
- package/Sources/Runic/Resources/Fonts/SpaceMono-Regular.ttf +0 -0
- package/Sources/Runic/Resources/ProviderIcon-antigravity.svg +0 -2
- package/Sources/Runic/Resources/ProviderIcon-auggie.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-azure.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-bedrock.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-cerebras.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-claude.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-codex.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cohere.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-copilot.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-cursor.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-deepseek.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-factory.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-fireworks.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-gemini.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-groq.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-kimi.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-minimax.svg +0 -1
- package/Sources/Runic/Resources/ProviderIcon-mistral.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-openrouter.svg +0 -7
- package/Sources/Runic/Resources/ProviderIcon-perplexity.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-qwen.svg +0 -9
- package/Sources/Runic/Resources/ProviderIcon-sambanova.svg +0 -4
- package/Sources/Runic/Resources/ProviderIcon-together.svg +0 -5
- package/Sources/Runic/Resources/ProviderIcon-vertexai.svg +0 -17
- package/Sources/Runic/Resources/ProviderIcon-xai.svg +0 -3
- package/Sources/Runic/Resources/ProviderIcon-zai.svg +0 -5
- package/Sources/Runic/Resources/RunicMenubarIcon.svg +0 -7
- package/Sources/Runic/Resources/provider-context-windows.json +0 -20
- package/Sources/Runic/RunicApp.swift +0 -367
- package/Sources/Runic/SessionQuotaNotifications.swift +0 -58
- package/Sources/Runic/UsagePaceText.swift +0 -52
- package/Sources/Runic/UsageProgressBar.swift +0 -228
- package/Sources/Runic/Utilities/DisplayLink.swift +0 -96
- package/Sources/Runic/Utilities/LoadingPattern.swift +0 -62
- package/Sources/Runic/Utilities/PerformanceConstants.swift +0 -85
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +0 -254
- package/Sources/Runic/Views/Menu/ChartPanelStyle.swift +0 -22
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +0 -275
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +0 -377
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +0 -312
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +0 -311
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +0 -228
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +0 -157
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +0 -63
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +0 -248
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +0 -104
- package/Sources/Runic/Views/Menu/MenuCardView.swift +0 -1889
- package/Sources/Runic/Views/Menu/MenuContent.swift +0 -153
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +0 -371
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +0 -35
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +0 -241
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +0 -281
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +0 -220
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +0 -86
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +0 -214
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +0 -398
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +0 -231
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +0 -369
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +0 -191
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +0 -132
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +0 -621
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +0 -306
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +0 -271
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +0 -268
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +0 -418
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +0 -637
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +0 -344
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +0 -230
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +0 -321
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +0 -469
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +0 -250
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +0 -50
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +0 -572
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +0 -291
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +0 -3480
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +0 -29
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +0 -61
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +0 -213
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +0 -238
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +0 -376
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +0 -175
- package/Sources/Runic/Views/Team/TeamManagementView.swift +0 -762
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +0 -181
- package/Sources/RunicCLI/CLIEntry.swift +0 -983
- package/Sources/RunicCLI/CLIRenderer.swift +0 -87
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +0 -588
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +0 -363
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +0 -453
- package/Sources/RunicCLI/Commands/ResetCommand.swift +0 -430
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +0 -167
- package/Sources/RunicClaudeWatchdog/main.swift +0 -122
- package/Sources/RunicClaudeWebProbe/main.swift +0 -62
- package/Sources/RunicCore/AlertRuleStore.swift +0 -187
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +0 -25
- package/Sources/RunicCore/CopilotUsageModels.swift +0 -84
- package/Sources/RunicCore/CostUsageFetcher.swift +0 -119
- package/Sources/RunicCore/CostUsageModels.swift +0 -479
- package/Sources/RunicCore/CreditsModels.swift +0 -50
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +0 -268
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +0 -631
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +0 -444
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +0 -679
- package/Sources/RunicCore/Host/Process/SubprocessRunner.swift +0 -135
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +0 -173
- package/Sources/RunicCore/Logging/JSONStderrLogHandler.swift +0 -79
- package/Sources/RunicCore/Logging/OSLogLogHandler.swift +0 -86
- package/Sources/RunicCore/Logging/RunicLog.swift +0 -123
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +0 -370
- package/Sources/RunicCore/OpenAIDashboardModels.swift +0 -210
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +0 -676
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +0 -492
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardNavigationDelegate.swift +0 -51
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardParser.swift +0 -483
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardScrapeScript.swift +0 -558
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebViewCache.swift +0 -204
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardWebsiteDataStore.swift +0 -78
- package/Sources/RunicCore/PathEnvironment.swift +0 -482
- package/Sources/RunicCore/Performance/PerformanceModels.swift +0 -371
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +0 -426
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +0 -178
- package/Sources/RunicCore/ProjectBudgetStore.swift +0 -102
- package/Sources/RunicCore/ProviderCostSnapshot.swift +0 -29
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +0 -68
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +0 -685
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +0 -142
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +0 -141
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +0 -188
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +0 -126
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +0 -164
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Claude/ClaudeCLISession.swift +0 -332
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift +0 -180
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +0 -137
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +0 -209
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +0 -654
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +0 -30
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageFetcher.swift +0 -480
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +0 -780
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthCredentials.swift +0 -147
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +0 -280
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexTokenRefresher.swift +0 -100
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +0 -245
- package/Sources/RunicCore/Providers/Codex/CodexStatusProbe.swift +0 -125
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +0 -26
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +0 -183
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +0 -85
- package/Sources/RunicCore/Providers/Copilot/CopilotDeviceFlow.swift +0 -123
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +0 -117
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +0 -100
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +0 -293
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +0 -385
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Cursor/CursorStatusProbe.swift +0 -595
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +0 -85
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +0 -108
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +0 -398
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +0 -1276
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +0 -67
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +0 -839
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +0 -71
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +0 -77
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +0 -97
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxSettingsReader.swift +0 -54
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +0 -168
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +0 -911
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +0 -87
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +0 -216
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/ProviderBranding.swift +0 -25
- package/Sources/RunicCore/Providers/ProviderCLIConfig.swift +0 -17
- package/Sources/RunicCore/Providers/ProviderDescriptor.swift +0 -129
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +0 -263
- package/Sources/RunicCore/Providers/ProviderSettingsSnapshot.swift +0 -98
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +0 -501
- package/Sources/RunicCore/Providers/ProviderVersionDetector.swift +0 -68
- package/Sources/RunicCore/Providers/Providers.swift +0 -184
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +0 -86
- package/Sources/RunicCore/Providers/Qwen/QwenSettingsReader.swift +0 -30
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +0 -328
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +0 -84
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +0 -123
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +0 -48
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +0 -144
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +0 -83
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +0 -75
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +0 -74
- package/Sources/RunicCore/Providers/Zai/ZaiSettingsReader.swift +0 -41
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +0 -745
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +0 -384
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +0 -307
- package/Sources/RunicCore/Sync/SyncProtocol.swift +0 -287
- package/Sources/RunicCore/Sync/SyncRecord.swift +0 -402
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +0 -288
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +0 -768
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +0 -338
- package/Sources/RunicCore/Team/TeamModels.swift +0 -584
- package/Sources/RunicCore/Team/TeamShowbackExporter.swift +0 -495
- package/Sources/RunicCore/Team/TeamStore.swift +0 -809
- package/Sources/RunicCore/TextParsing.swift +0 -55
- package/Sources/RunicCore/UsageFetcher.swift +0 -694
- package/Sources/RunicCore/UsageFormatter.swift +0 -410
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +0 -441
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +0 -502
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +0 -161
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +0 -550
- package/Sources/RunicCore/UsageLedger/UsageLedger.swift +0 -29
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +0 -675
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +0 -209
- package/Sources/RunicCore/UsageLedger/UsageLedgerModels.swift +0 -417
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +0 -284
- package/Sources/RunicCore/UsagePace.swift +0 -81
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +0 -73
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +0 -74
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +0 -208
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Claude.swift +0 -455
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner+Timestamp.swift +0 -132
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +0 -589
- package/Sources/RunicCore/WidgetSnapshot.swift +0 -183
- package/Sources/RunicMacroSupport/ProviderRegistrationMacros.swift +0 -14
- package/Sources/RunicMacros/ProviderRegistrationMacros.swift +0 -216
- package/Sources/RunicWidget/RunicWidgetBundle.swift +0 -79
- package/Sources/RunicWidget/RunicWidgetProvider.swift +0 -283
- package/Sources/RunicWidget/RunicWidgetViews.swift +0 -680
- package/Tests/RunicTests/AntigravityStatusProbeTests.swift +0 -50
- package/Tests/RunicTests/AppDelegateTests.swift +0 -48
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +0 -21
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +0 -69
- package/Tests/RunicTests/CLICostTests.swift +0 -64
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +0 -21
- package/Tests/RunicTests/CLISnapshotTests.swift +0 -71
- package/Tests/RunicTests/CLIWebFallbackTests.swift +0 -57
- package/Tests/RunicTests/ClaudeOAuthTests.swift +0 -163
- package/Tests/RunicTests/ClaudeResilienceTests.swift +0 -29
- package/Tests/RunicTests/ClaudeUsageTests.swift +0 -369
- package/Tests/RunicTests/CodexOAuthTests.swift +0 -121
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +0 -80
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +0 -74
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +0 -74
- package/Tests/RunicTests/CostUsageDecodingTests.swift +0 -392
- package/Tests/RunicTests/CostUsagePricingTests.swift +0 -48
- package/Tests/RunicTests/CostUsageScannerTests.swift +0 -698
- package/Tests/RunicTests/CursorStatusProbeTests.swift +0 -322
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +0 -67
- package/Tests/RunicTests/GeminiAPITestHelpers.swift +0 -94
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +0 -27
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +0 -283
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +0 -279
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +0 -232
- package/Tests/RunicTests/GeminiTestEnvironment.swift +0 -87
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +0 -72
- package/Tests/RunicTests/InstallOriginTests.swift +0 -17
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +0 -9
- package/Tests/RunicTests/LiveAccountTests.swift +0 -24
- package/Tests/RunicTests/LoadingPatternTests.swift +0 -34
- package/Tests/RunicTests/MenuCardModelTests.swift +0 -544
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/MiniMaxParsingTests.swift +0 -67
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +0 -11
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +0 -110
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +0 -16
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +0 -74
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +0 -26
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +0 -133
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +0 -64
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +0 -131
- package/Tests/RunicTests/PathBuilderTests.swift +0 -236
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +0 -159
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +0 -37
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +0 -15
- package/Tests/RunicTests/ProviderRegistryTests.swift +0 -29
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +0 -236
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +0 -50
- package/Tests/RunicTests/RunicTests.swift +0 -119
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +0 -35
- package/Tests/RunicTests/SettingsStoreTests.swift +0 -265
- package/Tests/RunicTests/StatusItemAnimationTests.swift +0 -54
- package/Tests/RunicTests/StatusMenuTests.swift +0 -475
- package/Tests/RunicTests/StatusProbeTests.swift +0 -376
- package/Tests/RunicTests/SubprocessRunnerTests.swift +0 -18
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +0 -64
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +0 -147
- package/Tests/RunicTests/TTYIntegrationTests.swift +0 -41
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +0 -157
- package/Tests/RunicTests/TextParsingTests.swift +0 -11
- package/Tests/RunicTests/UsageFormatterTests.swift +0 -111
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +0 -170
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +0 -211
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +0 -81
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +0 -168
- package/Tests/RunicTests/UsagePaceTests.swift +0 -76
- package/Tests/RunicTests/UsagePaceTextTests.swift +0 -108
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +0 -89
- package/Tests/RunicTests/WidgetSnapshotTests.swift +0 -43
- package/Tests/RunicTests/ZaiProviderTests.swift +0 -59
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +0 -9
- package/TestsLinux/PlatformGatingTests.swift +0 -46
- package/appcast.xml +0 -104
- package/assets/logo-concept-stack.svg +0 -24
- package/assets/logo-minimal-bars.svg +0 -15
- package/assets/logo.svg +0 -20
- package/assets/logo2.svg +0 -43
- package/bin/docs-list +0 -5
- package/build-and-run.sh +0 -369
- package/docs/.nojekyll +0 -1
- package/docs/CNAME +0 -1
- package/docs/antigravity.md +0 -75
- package/docs/architecture.md +0 -32
- package/docs/claude.md +0 -93
- package/docs/cli-commands/integration_guide.md +0 -371
- package/docs/cli-commands/quick_reference.md +0 -362
- package/docs/cli-commands/readme.md +0 -361
- package/docs/cli.md +0 -156
- package/docs/cloudkit_sync_guide.md +0 -495
- package/docs/codebase_review.md +0 -288
- package/docs/codex-oauth.md +0 -635
- package/docs/codex.md +0 -104
- package/docs/codexbar.png +0 -0
- package/docs/copilot.md +0 -44
- package/docs/cursor.md +0 -46
- package/docs/design/designcomparisonchart.md +0 -510
- package/docs/design/implementationguide.md +0 -792
- package/docs/design/menubaricondesignconcepts.md +0 -796
- package/docs/design/mockups/hybridiconconcept.svg +0 -62
- package/docs/design/mockups/infinityiconconcept.svg +0 -41
- package/docs/design/mockups/infinityiconstates.svg +0 -129
- package/docs/design/mockups/wavelogorefined.svg +0 -48
- package/docs/design/quick_summary.md +0 -426
- package/docs/design/readme.md +0 -478
- package/docs/design/sizespecifications.md +0 -575
- package/docs/design-system.md +0 -836
- package/docs/factory.md +0 -103
- package/docs/gemini.md +0 -73
- package/docs/icon.md +0 -37
- package/docs/icon.png +0 -0
- package/docs/index.html +0 -175
- package/docs/keychain_fix.md +0 -274
- package/docs/minimax.md +0 -71
- package/docs/packaging.md +0 -25
- package/docs/progress-bar-improvements.md +0 -240
- package/docs/progress-bar-quick-reference.md +0 -283
- package/docs/progress-bar-technical-details.md +0 -432
- package/docs/progress-bar-visual-test.swift +0 -375
- package/docs/provider.md +0 -202
- package/docs/providers.md +0 -86
- package/docs/refactor/macros.md +0 -35
- package/docs/refresh-loop.md +0 -22
- package/docs/releasing-homebrew.md +0 -35
- package/docs/releasing.md +0 -121
- package/docs/research/concrete_todo_2026-02-22.md +0 -47
- package/docs/research/core-feature-research.md +0 -248
- package/docs/research/feature_uiux_roadmap_2026-02-22.md +0 -202
- package/docs/research/parity_matrix.md +0 -49
- package/docs/research/provider_expansion_candidates_2026-02-22.md +0 -80
- package/docs/research/provider_model_token_project_matrix_2026-02-23.md +0 -46
- package/docs/research/release_gate_checklist.md +0 -57
- package/docs/site.css +0 -505
- package/docs/sparkle.md +0 -28
- package/docs/status.md +0 -28
- package/docs/sync/implementation.md +0 -393
- package/docs/sync/readme.md +0 -317
- package/docs/sync_files_summary.md +0 -391
- package/docs/sync_quick_reference.md +0 -225
- package/docs/todo.md +0 -11
- package/docs/ui.md +0 -29
- package/docs/web-integration.md +0 -9
- package/docs/widgets.md +0 -85
- package/docs/workflows/error-handling-improvements.md +0 -434
- package/docs/workflows/folder_structure.md +0 -299
- package/docs/workflows/loading_states.md +0 -446
- package/docs/workflows/loading_states_examples.md +0 -565
- package/docs/workflows/loading_states_flow.md +0 -430
- package/docs/workflows/performance_optimizations.md +0 -457
- package/docs/workflows/phase1-cli-testing-report.md +0 -313
- package/docs/workflows/quick_reference.md +0 -228
- package/docs/workflows/readme.md +0 -59
- package/docs/workflows/recent_improvements.md +0 -275
- package/docs/workflows/sync_enablement_summary.md +0 -400
- package/docs/zai.md +0 -43
- package/runic.png +0 -0
- package/runic.svg +0 -15
- package/version.env +0 -2
|
@@ -1,768 +0,0 @@
|
|
|
1
|
-
import CloudKit
|
|
2
|
-
import CryptoKit
|
|
3
|
-
import Foundation
|
|
4
|
-
import LocalAuthentication
|
|
5
|
-
|
|
6
|
-
// MARK: - CloudKit Record Type Constants
|
|
7
|
-
|
|
8
|
-
extension CloudKitRecordType {
|
|
9
|
-
public static let team = "Team"
|
|
10
|
-
public static let teamMembership = "TeamMembership"
|
|
11
|
-
public static let projectOwnership = "ProjectOwnership"
|
|
12
|
-
public static let teamUsageSnapshot = "TeamUsageSnapshot"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// MARK: - CloudKit Custom Zone Configuration
|
|
16
|
-
|
|
17
|
-
/// Custom zone for team collaboration data
|
|
18
|
-
///
|
|
19
|
-
/// Using a custom zone allows for atomic batch operations and better
|
|
20
|
-
/// change tracking for team-related records.
|
|
21
|
-
public enum TeamSyncZone {
|
|
22
|
-
public static let zoneName = "TeamCollaborationZone"
|
|
23
|
-
public static let ownerName = CKCurrentUserDefaultName
|
|
24
|
-
|
|
25
|
-
public static var zoneID: CKRecordZone.ID {
|
|
26
|
-
CKRecordZone.ID(zoneName: zoneName, ownerName: ownerName)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public static func createZone() -> CKRecordZone {
|
|
30
|
-
CKRecordZone(zoneID: self.zoneID)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// MARK: - Team Record
|
|
35
|
-
|
|
36
|
-
/// Team record for CloudKit synchronization
|
|
37
|
-
///
|
|
38
|
-
/// Represents a collaborative team that can share usage quotas and projects.
|
|
39
|
-
/// - Record Type: "Team"
|
|
40
|
-
/// - Queryable by: ownerUserID
|
|
41
|
-
/// - Zone: TeamCollaborationZone (custom zone)
|
|
42
|
-
public struct TeamRecord: SyncableRecord {
|
|
43
|
-
public let recordID: String
|
|
44
|
-
public let recordType: String = CloudKitRecordType.team
|
|
45
|
-
public let version: Int
|
|
46
|
-
public let modifiedAt: Date
|
|
47
|
-
public let lastModifiedDeviceID: String?
|
|
48
|
-
|
|
49
|
-
// Team data
|
|
50
|
-
public let teamID: String
|
|
51
|
-
public let name: String
|
|
52
|
-
public let ownerUserID: String
|
|
53
|
-
public let totalQuota: Int64
|
|
54
|
-
public let createdAt: Date
|
|
55
|
-
public let updatedAt: Date
|
|
56
|
-
|
|
57
|
-
// Optional metadata
|
|
58
|
-
public let description: String?
|
|
59
|
-
public let avatarURL: String?
|
|
60
|
-
public let isActive: Bool
|
|
61
|
-
|
|
62
|
-
public init(
|
|
63
|
-
recordID: String = UUID().uuidString,
|
|
64
|
-
version: Int = 1,
|
|
65
|
-
modifiedAt: Date = Date(),
|
|
66
|
-
lastModifiedDeviceID: String? = nil,
|
|
67
|
-
teamID: String = UUID().uuidString,
|
|
68
|
-
name: String,
|
|
69
|
-
ownerUserID: String,
|
|
70
|
-
totalQuota: Int64,
|
|
71
|
-
createdAt: Date = Date(),
|
|
72
|
-
updatedAt: Date = Date(),
|
|
73
|
-
description: String? = nil,
|
|
74
|
-
avatarURL: String? = nil,
|
|
75
|
-
isActive: Bool = true)
|
|
76
|
-
{
|
|
77
|
-
self.recordID = recordID
|
|
78
|
-
self.version = version
|
|
79
|
-
self.modifiedAt = modifiedAt
|
|
80
|
-
self.lastModifiedDeviceID = lastModifiedDeviceID
|
|
81
|
-
self.teamID = teamID
|
|
82
|
-
self.name = name
|
|
83
|
-
self.ownerUserID = ownerUserID
|
|
84
|
-
self.totalQuota = totalQuota
|
|
85
|
-
self.createdAt = createdAt
|
|
86
|
-
self.updatedAt = updatedAt
|
|
87
|
-
self.description = description
|
|
88
|
-
self.avatarURL = avatarURL
|
|
89
|
-
self.isActive = isActive
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public func toCKRecord() throws -> CKRecord {
|
|
93
|
-
let zoneID = TeamSyncZone.zoneID
|
|
94
|
-
let record = CKRecord(
|
|
95
|
-
recordType: recordType,
|
|
96
|
-
recordID: CKRecord.ID(recordName: self.recordID, zoneID: zoneID))
|
|
97
|
-
|
|
98
|
-
// Sync metadata
|
|
99
|
-
record["version"] = self.version as CKRecordValue
|
|
100
|
-
record["modifiedAt"] = self.modifiedAt as CKRecordValue
|
|
101
|
-
record["lastModifiedDeviceID"] = self.lastModifiedDeviceID as? CKRecordValue
|
|
102
|
-
|
|
103
|
-
// Team data
|
|
104
|
-
record["teamID"] = self.teamID as CKRecordValue
|
|
105
|
-
record["name"] = self.name as CKRecordValue
|
|
106
|
-
record["ownerUserID"] = self.ownerUserID as CKRecordValue
|
|
107
|
-
record["totalQuota"] = self.totalQuota as CKRecordValue
|
|
108
|
-
record["createdAt"] = self.createdAt as CKRecordValue
|
|
109
|
-
record["updatedAt"] = self.updatedAt as CKRecordValue
|
|
110
|
-
record["isActive"] = (self.isActive ? 1 : 0) as CKRecordValue
|
|
111
|
-
|
|
112
|
-
// Optional fields
|
|
113
|
-
record["description"] = self.description as? CKRecordValue
|
|
114
|
-
record["avatarURL"] = self.avatarURL as? CKRecordValue
|
|
115
|
-
|
|
116
|
-
return record
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public static func fromCKRecord(_ ckRecord: CKRecord) throws -> TeamRecord {
|
|
120
|
-
guard let version = ckRecord["version"] as? Int,
|
|
121
|
-
let modifiedAt = ckRecord["modifiedAt"] as? Date,
|
|
122
|
-
let teamID = ckRecord["teamID"] as? String,
|
|
123
|
-
let name = ckRecord["name"] as? String,
|
|
124
|
-
let ownerUserID = ckRecord["ownerUserID"] as? String,
|
|
125
|
-
let totalQuota = ckRecord["totalQuota"] as? Int64,
|
|
126
|
-
let createdAt = ckRecord["createdAt"] as? Date,
|
|
127
|
-
let updatedAt = ckRecord["updatedAt"] as? Date,
|
|
128
|
-
let isActiveInt = ckRecord["isActive"] as? Int
|
|
129
|
-
else {
|
|
130
|
-
throw SyncError.invalidRecordFormat("Missing required fields in Team record")
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return TeamRecord(
|
|
134
|
-
recordID: ckRecord.recordID.recordName,
|
|
135
|
-
version: version,
|
|
136
|
-
modifiedAt: modifiedAt,
|
|
137
|
-
lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
|
|
138
|
-
teamID: teamID,
|
|
139
|
-
name: name,
|
|
140
|
-
ownerUserID: ownerUserID,
|
|
141
|
-
totalQuota: totalQuota,
|
|
142
|
-
createdAt: createdAt,
|
|
143
|
-
updatedAt: updatedAt,
|
|
144
|
-
description: ckRecord["description"] as? String,
|
|
145
|
-
avatarURL: ckRecord["avatarURL"] as? String,
|
|
146
|
-
isActive: isActiveInt == 1)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// MARK: - Team Membership Record
|
|
151
|
-
|
|
152
|
-
/// Team membership record for CloudKit synchronization
|
|
153
|
-
///
|
|
154
|
-
/// Represents a user's membership in a team with role and quota information.
|
|
155
|
-
/// - Record Type: "TeamMembership"
|
|
156
|
-
/// - Queryable by: teamID, userID
|
|
157
|
-
/// - References: Team record (parent)
|
|
158
|
-
/// - Zone: TeamCollaborationZone (custom zone)
|
|
159
|
-
public struct TeamMembershipRecord: SyncableRecord {
|
|
160
|
-
public let recordID: String
|
|
161
|
-
public let recordType: String = CloudKitRecordType.teamMembership
|
|
162
|
-
public let version: Int
|
|
163
|
-
public let modifiedAt: Date
|
|
164
|
-
public let lastModifiedDeviceID: String?
|
|
165
|
-
|
|
166
|
-
// Membership data
|
|
167
|
-
public let membershipID: String
|
|
168
|
-
public let teamID: String
|
|
169
|
-
public let userID: String
|
|
170
|
-
public let role: TeamRole
|
|
171
|
-
public let quotaLimit: Int64?
|
|
172
|
-
public let joinedAt: Date
|
|
173
|
-
|
|
174
|
-
// Optional metadata
|
|
175
|
-
public let email: String?
|
|
176
|
-
public let displayName: String?
|
|
177
|
-
public let invitedBy: String?
|
|
178
|
-
public let acceptedAt: Date?
|
|
179
|
-
public let isActive: Bool
|
|
180
|
-
|
|
181
|
-
/// Reference to parent team
|
|
182
|
-
public let teamRecordName: String
|
|
183
|
-
|
|
184
|
-
public enum TeamRole: String, Codable, Sendable {
|
|
185
|
-
case owner
|
|
186
|
-
case admin
|
|
187
|
-
case member
|
|
188
|
-
case viewer
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
public init(
|
|
192
|
-
recordID: String = UUID().uuidString,
|
|
193
|
-
version: Int = 1,
|
|
194
|
-
modifiedAt: Date = Date(),
|
|
195
|
-
lastModifiedDeviceID: String? = nil,
|
|
196
|
-
membershipID: String = UUID().uuidString,
|
|
197
|
-
teamID: String,
|
|
198
|
-
userID: String,
|
|
199
|
-
role: TeamRole,
|
|
200
|
-
quotaLimit: Int64? = nil,
|
|
201
|
-
joinedAt: Date = Date(),
|
|
202
|
-
email: String? = nil,
|
|
203
|
-
displayName: String? = nil,
|
|
204
|
-
invitedBy: String? = nil,
|
|
205
|
-
acceptedAt: Date? = nil,
|
|
206
|
-
isActive: Bool = true,
|
|
207
|
-
teamRecordName: String)
|
|
208
|
-
{
|
|
209
|
-
self.recordID = recordID
|
|
210
|
-
self.version = version
|
|
211
|
-
self.modifiedAt = modifiedAt
|
|
212
|
-
self.lastModifiedDeviceID = lastModifiedDeviceID
|
|
213
|
-
self.membershipID = membershipID
|
|
214
|
-
self.teamID = teamID
|
|
215
|
-
self.userID = userID
|
|
216
|
-
self.role = role
|
|
217
|
-
self.quotaLimit = quotaLimit
|
|
218
|
-
self.joinedAt = joinedAt
|
|
219
|
-
self.email = email
|
|
220
|
-
self.displayName = displayName
|
|
221
|
-
self.invitedBy = invitedBy
|
|
222
|
-
self.acceptedAt = acceptedAt
|
|
223
|
-
self.isActive = isActive
|
|
224
|
-
self.teamRecordName = teamRecordName
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
public func toCKRecord() throws -> CKRecord {
|
|
228
|
-
let zoneID = TeamSyncZone.zoneID
|
|
229
|
-
let record = CKRecord(
|
|
230
|
-
recordType: recordType,
|
|
231
|
-
recordID: CKRecord.ID(recordName: self.recordID, zoneID: zoneID))
|
|
232
|
-
|
|
233
|
-
// Sync metadata
|
|
234
|
-
record["version"] = self.version as CKRecordValue
|
|
235
|
-
record["modifiedAt"] = self.modifiedAt as CKRecordValue
|
|
236
|
-
record["lastModifiedDeviceID"] = self.lastModifiedDeviceID as? CKRecordValue
|
|
237
|
-
|
|
238
|
-
// Membership data
|
|
239
|
-
record["membershipID"] = self.membershipID as CKRecordValue
|
|
240
|
-
record["teamID"] = self.teamID as CKRecordValue
|
|
241
|
-
record["userID"] = self.userID as CKRecordValue
|
|
242
|
-
record["role"] = self.role.rawValue as CKRecordValue
|
|
243
|
-
record["quotaLimit"] = self.quotaLimit as? CKRecordValue
|
|
244
|
-
record["joinedAt"] = self.joinedAt as CKRecordValue
|
|
245
|
-
record["isActive"] = (self.isActive ? 1 : 0) as CKRecordValue
|
|
246
|
-
|
|
247
|
-
// Optional encrypted fields
|
|
248
|
-
if let email {
|
|
249
|
-
let encrypted = try encryptString(email)
|
|
250
|
-
record["email"] = encrypted as CKRecordValue
|
|
251
|
-
}
|
|
252
|
-
record["displayName"] = self.displayName as? CKRecordValue
|
|
253
|
-
record["invitedBy"] = self.invitedBy as? CKRecordValue
|
|
254
|
-
record["acceptedAt"] = self.acceptedAt as? CKRecordValue
|
|
255
|
-
|
|
256
|
-
// Reference to parent team
|
|
257
|
-
let teamReference = CKRecord.Reference(
|
|
258
|
-
recordID: CKRecord.ID(recordName: self.teamRecordName, zoneID: zoneID),
|
|
259
|
-
action: .deleteSelf)
|
|
260
|
-
record["teamReference"] = teamReference as CKRecordValue
|
|
261
|
-
|
|
262
|
-
return record
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
public static func fromCKRecord(_ ckRecord: CKRecord) throws -> TeamMembershipRecord {
|
|
266
|
-
guard let version = ckRecord["version"] as? Int,
|
|
267
|
-
let modifiedAt = ckRecord["modifiedAt"] as? Date,
|
|
268
|
-
let membershipID = ckRecord["membershipID"] as? String,
|
|
269
|
-
let teamID = ckRecord["teamID"] as? String,
|
|
270
|
-
let userID = ckRecord["userID"] as? String,
|
|
271
|
-
let roleString = ckRecord["role"] as? String,
|
|
272
|
-
let role = TeamRole(rawValue: roleString),
|
|
273
|
-
let joinedAt = ckRecord["joinedAt"] as? Date,
|
|
274
|
-
let isActiveInt = ckRecord["isActive"] as? Int,
|
|
275
|
-
let teamReference = ckRecord["teamReference"] as? CKRecord.Reference
|
|
276
|
-
else {
|
|
277
|
-
throw SyncError.invalidRecordFormat("Missing required fields in TeamMembership record")
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
var email: String?
|
|
281
|
-
if let encryptedEmail = ckRecord["email"] as? String {
|
|
282
|
-
email = try? decryptString(encryptedEmail)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return TeamMembershipRecord(
|
|
286
|
-
recordID: ckRecord.recordID.recordName,
|
|
287
|
-
version: version,
|
|
288
|
-
modifiedAt: modifiedAt,
|
|
289
|
-
lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
|
|
290
|
-
membershipID: membershipID,
|
|
291
|
-
teamID: teamID,
|
|
292
|
-
userID: userID,
|
|
293
|
-
role: role,
|
|
294
|
-
quotaLimit: ckRecord["quotaLimit"] as? Int64,
|
|
295
|
-
joinedAt: joinedAt,
|
|
296
|
-
email: email,
|
|
297
|
-
displayName: ckRecord["displayName"] as? String,
|
|
298
|
-
invitedBy: ckRecord["invitedBy"] as? String,
|
|
299
|
-
acceptedAt: ckRecord["acceptedAt"] as? Date,
|
|
300
|
-
isActive: isActiveInt == 1,
|
|
301
|
-
teamRecordName: teamReference.recordID.recordName)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// MARK: - Project Ownership Record
|
|
306
|
-
|
|
307
|
-
/// Project ownership record for CloudKit synchronization
|
|
308
|
-
///
|
|
309
|
-
/// Represents ownership and sharing permissions for a project.
|
|
310
|
-
/// - Record Type: "ProjectOwnership"
|
|
311
|
-
/// - Queryable by: ownerUserID, teamID, projectID
|
|
312
|
-
/// - References: Team record (optional - if team-owned)
|
|
313
|
-
/// - Zone: TeamCollaborationZone (custom zone)
|
|
314
|
-
public struct ProjectOwnershipRecord: SyncableRecord {
|
|
315
|
-
public let recordID: String
|
|
316
|
-
public let recordType: String = CloudKitRecordType.projectOwnership
|
|
317
|
-
public let version: Int
|
|
318
|
-
public let modifiedAt: Date
|
|
319
|
-
public let lastModifiedDeviceID: String?
|
|
320
|
-
|
|
321
|
-
// Project ownership data
|
|
322
|
-
public let projectID: String
|
|
323
|
-
public let teamID: String?
|
|
324
|
-
public let ownerUserID: String
|
|
325
|
-
public let sharedWith: [String]
|
|
326
|
-
public let accessLevel: AccessLevel
|
|
327
|
-
public let createdAt: Date
|
|
328
|
-
|
|
329
|
-
// Optional metadata
|
|
330
|
-
public let projectName: String?
|
|
331
|
-
public let projectDescription: String?
|
|
332
|
-
public let tags: [String]
|
|
333
|
-
public let isArchived: Bool
|
|
334
|
-
|
|
335
|
-
/// Reference to team (if team-owned)
|
|
336
|
-
public let teamRecordName: String?
|
|
337
|
-
|
|
338
|
-
public enum AccessLevel: String, Codable, Sendable {
|
|
339
|
-
case `private`
|
|
340
|
-
case teamReadOnly = "team_read_only"
|
|
341
|
-
case teamReadWrite = "team_read_write"
|
|
342
|
-
case `public`
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
public init(
|
|
346
|
-
recordID: String = UUID().uuidString,
|
|
347
|
-
version: Int = 1,
|
|
348
|
-
modifiedAt: Date = Date(),
|
|
349
|
-
lastModifiedDeviceID: String? = nil,
|
|
350
|
-
projectID: String = UUID().uuidString,
|
|
351
|
-
teamID: String? = nil,
|
|
352
|
-
ownerUserID: String,
|
|
353
|
-
sharedWith: [String] = [],
|
|
354
|
-
accessLevel: AccessLevel = .private,
|
|
355
|
-
createdAt: Date = Date(),
|
|
356
|
-
projectName: String? = nil,
|
|
357
|
-
projectDescription: String? = nil,
|
|
358
|
-
tags: [String] = [],
|
|
359
|
-
isArchived: Bool = false,
|
|
360
|
-
teamRecordName: String? = nil)
|
|
361
|
-
{
|
|
362
|
-
self.recordID = recordID
|
|
363
|
-
self.version = version
|
|
364
|
-
self.modifiedAt = modifiedAt
|
|
365
|
-
self.lastModifiedDeviceID = lastModifiedDeviceID
|
|
366
|
-
self.projectID = projectID
|
|
367
|
-
self.teamID = teamID
|
|
368
|
-
self.ownerUserID = ownerUserID
|
|
369
|
-
self.sharedWith = sharedWith
|
|
370
|
-
self.accessLevel = accessLevel
|
|
371
|
-
self.createdAt = createdAt
|
|
372
|
-
self.projectName = projectName
|
|
373
|
-
self.projectDescription = projectDescription
|
|
374
|
-
self.tags = tags
|
|
375
|
-
self.isArchived = isArchived
|
|
376
|
-
self.teamRecordName = teamRecordName
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
public func toCKRecord() throws -> CKRecord {
|
|
380
|
-
let zoneID = TeamSyncZone.zoneID
|
|
381
|
-
let record = CKRecord(
|
|
382
|
-
recordType: recordType,
|
|
383
|
-
recordID: CKRecord.ID(recordName: self.recordID, zoneID: zoneID))
|
|
384
|
-
|
|
385
|
-
// Sync metadata
|
|
386
|
-
record["version"] = self.version as CKRecordValue
|
|
387
|
-
record["modifiedAt"] = self.modifiedAt as CKRecordValue
|
|
388
|
-
record["lastModifiedDeviceID"] = self.lastModifiedDeviceID as? CKRecordValue
|
|
389
|
-
|
|
390
|
-
// Project ownership data
|
|
391
|
-
record["projectID"] = self.projectID as CKRecordValue
|
|
392
|
-
record["teamID"] = self.teamID as? CKRecordValue
|
|
393
|
-
record["ownerUserID"] = self.ownerUserID as CKRecordValue
|
|
394
|
-
record["sharedWith"] = self.sharedWith as CKRecordValue
|
|
395
|
-
record["accessLevel"] = self.accessLevel.rawValue as CKRecordValue
|
|
396
|
-
record["createdAt"] = self.createdAt as CKRecordValue
|
|
397
|
-
record["isArchived"] = (self.isArchived ? 1 : 0) as CKRecordValue
|
|
398
|
-
|
|
399
|
-
// Optional metadata
|
|
400
|
-
record["projectName"] = self.projectName as? CKRecordValue
|
|
401
|
-
record["projectDescription"] = self.projectDescription as? CKRecordValue
|
|
402
|
-
record["tags"] = self.tags as CKRecordValue
|
|
403
|
-
|
|
404
|
-
// Reference to team (if team-owned)
|
|
405
|
-
if let teamRecordName {
|
|
406
|
-
let teamReference = CKRecord.Reference(
|
|
407
|
-
recordID: CKRecord.ID(recordName: teamRecordName, zoneID: zoneID),
|
|
408
|
-
action: .none)
|
|
409
|
-
record["teamReference"] = teamReference as CKRecordValue
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return record
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
public static func fromCKRecord(_ ckRecord: CKRecord) throws -> ProjectOwnershipRecord {
|
|
416
|
-
guard let version = ckRecord["version"] as? Int,
|
|
417
|
-
let modifiedAt = ckRecord["modifiedAt"] as? Date,
|
|
418
|
-
let projectID = ckRecord["projectID"] as? String,
|
|
419
|
-
let ownerUserID = ckRecord["ownerUserID"] as? String,
|
|
420
|
-
let sharedWith = ckRecord["sharedWith"] as? [String],
|
|
421
|
-
let accessLevelString = ckRecord["accessLevel"] as? String,
|
|
422
|
-
let accessLevel = AccessLevel(rawValue: accessLevelString),
|
|
423
|
-
let createdAt = ckRecord["createdAt"] as? Date,
|
|
424
|
-
let isArchivedInt = ckRecord["isArchived"] as? Int
|
|
425
|
-
else {
|
|
426
|
-
throw SyncError.invalidRecordFormat("Missing required fields in ProjectOwnership record")
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
let teamReference = ckRecord["teamReference"] as? CKRecord.Reference
|
|
430
|
-
|
|
431
|
-
return ProjectOwnershipRecord(
|
|
432
|
-
recordID: ckRecord.recordID.recordName,
|
|
433
|
-
version: version,
|
|
434
|
-
modifiedAt: modifiedAt,
|
|
435
|
-
lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
|
|
436
|
-
projectID: projectID,
|
|
437
|
-
teamID: ckRecord["teamID"] as? String,
|
|
438
|
-
ownerUserID: ownerUserID,
|
|
439
|
-
sharedWith: sharedWith,
|
|
440
|
-
accessLevel: accessLevel,
|
|
441
|
-
createdAt: createdAt,
|
|
442
|
-
projectName: ckRecord["projectName"] as? String,
|
|
443
|
-
projectDescription: ckRecord["projectDescription"] as? String,
|
|
444
|
-
tags: ckRecord["tags"] as? [String] ?? [],
|
|
445
|
-
isArchived: isArchivedInt == 1,
|
|
446
|
-
teamRecordName: teamReference?.recordID.recordName)
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// MARK: - Team Usage Snapshot Record
|
|
451
|
-
|
|
452
|
-
/// Team usage snapshot record for CloudKit synchronization
|
|
453
|
-
///
|
|
454
|
-
/// Aggregates usage data across team members for tracking and quota management.
|
|
455
|
-
/// - Record Type: "TeamUsageSnapshot"
|
|
456
|
-
/// - Queryable by: teamID, date
|
|
457
|
-
/// - References: Team record (parent)
|
|
458
|
-
/// - Zone: TeamCollaborationZone (custom zone)
|
|
459
|
-
public struct TeamUsageSnapshotRecord: SyncableRecord {
|
|
460
|
-
public let recordID: String
|
|
461
|
-
public let recordType: String = CloudKitRecordType.teamUsageSnapshot
|
|
462
|
-
public let version: Int
|
|
463
|
-
public let modifiedAt: Date
|
|
464
|
-
public let lastModifiedDeviceID: String?
|
|
465
|
-
|
|
466
|
-
// Team usage data
|
|
467
|
-
public let teamID: String
|
|
468
|
-
public let date: Date
|
|
469
|
-
public let totalTokens: Int64
|
|
470
|
-
public let totalCost: Double
|
|
471
|
-
public let memberUsage: [String: MemberUsageData]
|
|
472
|
-
public let quotaUsedPercent: Double
|
|
473
|
-
|
|
474
|
-
// Additional metrics
|
|
475
|
-
public let primaryProvider: String?
|
|
476
|
-
public let mostActiveUser: String?
|
|
477
|
-
public let averageTokensPerMember: Double
|
|
478
|
-
|
|
479
|
-
/// Reference to parent team
|
|
480
|
-
public let teamRecordName: String
|
|
481
|
-
|
|
482
|
-
public struct MemberUsageData: Codable, Sendable, Hashable {
|
|
483
|
-
public let userID: String
|
|
484
|
-
public let tokens: Int64
|
|
485
|
-
public let cost: Double
|
|
486
|
-
public let requestCount: Int
|
|
487
|
-
|
|
488
|
-
public init(userID: String, tokens: Int64, cost: Double, requestCount: Int) {
|
|
489
|
-
self.userID = userID
|
|
490
|
-
self.tokens = tokens
|
|
491
|
-
self.cost = cost
|
|
492
|
-
self.requestCount = requestCount
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
public init(
|
|
497
|
-
recordID: String = UUID().uuidString,
|
|
498
|
-
version: Int = 1,
|
|
499
|
-
modifiedAt: Date = Date(),
|
|
500
|
-
lastModifiedDeviceID: String? = nil,
|
|
501
|
-
teamID: String,
|
|
502
|
-
date: Date = Date(),
|
|
503
|
-
totalTokens: Int64,
|
|
504
|
-
totalCost: Double,
|
|
505
|
-
memberUsage: [String: MemberUsageData] = [:],
|
|
506
|
-
quotaUsedPercent: Double,
|
|
507
|
-
primaryProvider: String? = nil,
|
|
508
|
-
mostActiveUser: String? = nil,
|
|
509
|
-
averageTokensPerMember: Double = 0.0,
|
|
510
|
-
teamRecordName: String)
|
|
511
|
-
{
|
|
512
|
-
self.recordID = recordID
|
|
513
|
-
self.version = version
|
|
514
|
-
self.modifiedAt = modifiedAt
|
|
515
|
-
self.lastModifiedDeviceID = lastModifiedDeviceID
|
|
516
|
-
self.teamID = teamID
|
|
517
|
-
self.date = date
|
|
518
|
-
self.totalTokens = totalTokens
|
|
519
|
-
self.totalCost = totalCost
|
|
520
|
-
self.memberUsage = memberUsage
|
|
521
|
-
self.quotaUsedPercent = quotaUsedPercent
|
|
522
|
-
self.primaryProvider = primaryProvider
|
|
523
|
-
self.mostActiveUser = mostActiveUser
|
|
524
|
-
self.averageTokensPerMember = averageTokensPerMember
|
|
525
|
-
self.teamRecordName = teamRecordName
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
public func toCKRecord() throws -> CKRecord {
|
|
529
|
-
let zoneID = TeamSyncZone.zoneID
|
|
530
|
-
let record = CKRecord(
|
|
531
|
-
recordType: recordType,
|
|
532
|
-
recordID: CKRecord.ID(recordName: self.recordID, zoneID: zoneID))
|
|
533
|
-
|
|
534
|
-
// Sync metadata
|
|
535
|
-
record["version"] = self.version as CKRecordValue
|
|
536
|
-
record["modifiedAt"] = self.modifiedAt as CKRecordValue
|
|
537
|
-
record["lastModifiedDeviceID"] = self.lastModifiedDeviceID as? CKRecordValue
|
|
538
|
-
|
|
539
|
-
// Team usage data
|
|
540
|
-
record["teamID"] = self.teamID as CKRecordValue
|
|
541
|
-
record["date"] = self.date as CKRecordValue
|
|
542
|
-
record["totalTokens"] = self.totalTokens as CKRecordValue
|
|
543
|
-
record["totalCost"] = self.totalCost as CKRecordValue
|
|
544
|
-
record["quotaUsedPercent"] = self.quotaUsedPercent as CKRecordValue
|
|
545
|
-
|
|
546
|
-
// Encode member usage as JSON
|
|
547
|
-
let encoder = JSONEncoder()
|
|
548
|
-
let memberUsageData = try encoder.encode(self.memberUsage)
|
|
549
|
-
record["memberUsage"] = String(data: memberUsageData, encoding: .utf8) as? CKRecordValue
|
|
550
|
-
|
|
551
|
-
// Optional metrics
|
|
552
|
-
record["primaryProvider"] = self.primaryProvider as? CKRecordValue
|
|
553
|
-
record["mostActiveUser"] = self.mostActiveUser as? CKRecordValue
|
|
554
|
-
record["averageTokensPerMember"] = self.averageTokensPerMember as CKRecordValue
|
|
555
|
-
|
|
556
|
-
// Reference to parent team
|
|
557
|
-
let teamReference = CKRecord.Reference(
|
|
558
|
-
recordID: CKRecord.ID(recordName: self.teamRecordName, zoneID: zoneID),
|
|
559
|
-
action: .deleteSelf)
|
|
560
|
-
record["teamReference"] = teamReference as CKRecordValue
|
|
561
|
-
|
|
562
|
-
return record
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
public static func fromCKRecord(_ ckRecord: CKRecord) throws -> TeamUsageSnapshotRecord {
|
|
566
|
-
guard let version = ckRecord["version"] as? Int,
|
|
567
|
-
let modifiedAt = ckRecord["modifiedAt"] as? Date,
|
|
568
|
-
let teamID = ckRecord["teamID"] as? String,
|
|
569
|
-
let date = ckRecord["date"] as? Date,
|
|
570
|
-
let totalTokens = ckRecord["totalTokens"] as? Int64,
|
|
571
|
-
let totalCost = ckRecord["totalCost"] as? Double,
|
|
572
|
-
let quotaUsedPercent = ckRecord["quotaUsedPercent"] as? Double,
|
|
573
|
-
let teamReference = ckRecord["teamReference"] as? CKRecord.Reference
|
|
574
|
-
else {
|
|
575
|
-
throw SyncError.invalidRecordFormat("Missing required fields in TeamUsageSnapshot record")
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Decode member usage from JSON
|
|
579
|
-
var memberUsage: [String: MemberUsageData] = [:]
|
|
580
|
-
if let memberUsageJSON = ckRecord["memberUsage"] as? String,
|
|
581
|
-
let data = memberUsageJSON.data(using: .utf8)
|
|
582
|
-
{
|
|
583
|
-
let decoder = JSONDecoder()
|
|
584
|
-
memberUsage = (try? decoder.decode([String: MemberUsageData].self, from: data)) ?? [:]
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
return TeamUsageSnapshotRecord(
|
|
588
|
-
recordID: ckRecord.recordID.recordName,
|
|
589
|
-
version: version,
|
|
590
|
-
modifiedAt: modifiedAt,
|
|
591
|
-
lastModifiedDeviceID: ckRecord["lastModifiedDeviceID"] as? String,
|
|
592
|
-
teamID: teamID,
|
|
593
|
-
date: date,
|
|
594
|
-
totalTokens: totalTokens,
|
|
595
|
-
totalCost: totalCost,
|
|
596
|
-
memberUsage: memberUsage,
|
|
597
|
-
quotaUsedPercent: quotaUsedPercent,
|
|
598
|
-
primaryProvider: ckRecord["primaryProvider"] as? String,
|
|
599
|
-
mostActiveUser: ckRecord["mostActiveUser"] as? String,
|
|
600
|
-
averageTokensPerMember: ckRecord["averageTokensPerMember"] as? Double ?? 0.0,
|
|
601
|
-
teamRecordName: teamReference.recordID.recordName)
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// MARK: - CloudKit Query Helpers
|
|
606
|
-
|
|
607
|
-
/// Helper methods for querying team-related records
|
|
608
|
-
public enum TeamRecordQuery {
|
|
609
|
-
/// Creates a predicate to query teams by owner
|
|
610
|
-
public static func teamsByOwner(_ ownerUserID: String) -> NSPredicate {
|
|
611
|
-
NSPredicate(format: "ownerUserID == %@", ownerUserID)
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/// Creates a predicate to query memberships by team
|
|
615
|
-
public static func membershipsByTeam(_ teamID: String) -> NSPredicate {
|
|
616
|
-
NSPredicate(format: "teamID == %@ AND isActive == 1", teamID)
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/// Creates a predicate to query memberships by user
|
|
620
|
-
public static func membershipsByUser(_ userID: String) -> NSPredicate {
|
|
621
|
-
NSPredicate(format: "userID == %@ AND isActive == 1", userID)
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
/// Creates a predicate to query projects by owner
|
|
625
|
-
public static func projectsByOwner(_ ownerUserID: String) -> NSPredicate {
|
|
626
|
-
NSPredicate(format: "ownerUserID == %@ AND isArchived == 0", ownerUserID)
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/// Creates a predicate to query projects by team
|
|
630
|
-
public static func projectsByTeam(_ teamID: String) -> NSPredicate {
|
|
631
|
-
NSPredicate(format: "teamID == %@ AND isArchived == 0", teamID)
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
/// Creates a predicate to query team usage snapshots by date range
|
|
635
|
-
public static func teamUsageByDateRange(teamID: String, startDate: Date, endDate: Date) -> NSPredicate {
|
|
636
|
-
NSPredicate(
|
|
637
|
-
format: "teamID == %@ AND date >= %@ AND date <= %@",
|
|
638
|
-
teamID,
|
|
639
|
-
startDate as NSDate,
|
|
640
|
-
endDate as NSDate)
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/// Creates a predicate to query latest team usage
|
|
644
|
-
public static func latestTeamUsage(_ teamID: String) -> NSPredicate {
|
|
645
|
-
NSPredicate(format: "teamID == %@", teamID)
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// MARK: - CloudKit Index Configuration
|
|
650
|
-
|
|
651
|
-
/// Recommended CloudKit indexes for efficient querying
|
|
652
|
-
///
|
|
653
|
-
/// Configure these indexes in CloudKit Dashboard for optimal performance:
|
|
654
|
-
///
|
|
655
|
-
/// Team Record:
|
|
656
|
-
/// - ownerUserID (Queryable, Sortable)
|
|
657
|
-
/// - isActive (Queryable)
|
|
658
|
-
/// - createdAt (Sortable)
|
|
659
|
-
///
|
|
660
|
-
/// TeamMembership Record:
|
|
661
|
-
/// - teamID (Queryable, Sortable)
|
|
662
|
-
/// - userID (Queryable, Sortable)
|
|
663
|
-
/// - isActive (Queryable)
|
|
664
|
-
/// - role (Queryable)
|
|
665
|
-
/// - joinedAt (Sortable)
|
|
666
|
-
///
|
|
667
|
-
/// ProjectOwnership Record:
|
|
668
|
-
/// - ownerUserID (Queryable, Sortable)
|
|
669
|
-
/// - teamID (Queryable, Sortable)
|
|
670
|
-
/// - projectID (Queryable)
|
|
671
|
-
/// - isArchived (Queryable)
|
|
672
|
-
/// - accessLevel (Queryable)
|
|
673
|
-
/// - createdAt (Sortable)
|
|
674
|
-
///
|
|
675
|
-
/// TeamUsageSnapshot Record:
|
|
676
|
-
/// - teamID (Queryable, Sortable)
|
|
677
|
-
/// - date (Queryable, Sortable)
|
|
678
|
-
/// - quotaUsedPercent (Sortable)
|
|
679
|
-
public enum TeamRecordIndexes {
|
|
680
|
-
// This is a documentation enum - configure actual indexes in CloudKit Dashboard
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// MARK: - Encryption Helpers (Team-specific)
|
|
684
|
-
|
|
685
|
-
/// Encrypts a string using AES-GCM encryption (team-specific wrapper)
|
|
686
|
-
private func encryptString(_ plaintext: String) throws -> String {
|
|
687
|
-
guard let data = plaintext.data(using: .utf8) else {
|
|
688
|
-
throw SyncError.encryptionFailed("Failed to convert string to data")
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
let key = getOrCreateEncryptionKey()
|
|
692
|
-
let sealed = try AES.GCM.seal(data, using: key)
|
|
693
|
-
|
|
694
|
-
guard let combined = sealed.combined else {
|
|
695
|
-
throw SyncError.encryptionFailed("Failed to create sealed box")
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
return combined.base64EncodedString()
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
/// Decrypts a string that was encrypted with encryptString
|
|
702
|
-
private func decryptString(_ encrypted: String) throws -> String {
|
|
703
|
-
guard let combined = Data(base64Encoded: encrypted) else {
|
|
704
|
-
throw SyncError.encryptionFailed("Failed to decode base64")
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
let key = getOrCreateEncryptionKey()
|
|
708
|
-
let sealedBox = try AES.GCM.SealedBox(combined: combined)
|
|
709
|
-
let decrypted = try AES.GCM.open(sealedBox, using: key)
|
|
710
|
-
|
|
711
|
-
guard let plaintext = String(data: decrypted, encoding: .utf8) else {
|
|
712
|
-
throw SyncError.encryptionFailed("Failed to convert decrypted data to string")
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
return plaintext
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/// Retrieves or creates an encryption key for sensitive data
|
|
719
|
-
private func getOrCreateEncryptionKey() -> SymmetricKey {
|
|
720
|
-
let keychainKey = "com.runic.sync.team.encryption.key"
|
|
721
|
-
|
|
722
|
-
// Try to load existing key from Keychain
|
|
723
|
-
if let keyData = loadFromKeychain(key: keychainKey) {
|
|
724
|
-
return SymmetricKey(data: keyData)
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Generate new key
|
|
728
|
-
let key = SymmetricKey(size: .bits256)
|
|
729
|
-
let keyData = key.withUnsafeBytes { Data($0) }
|
|
730
|
-
|
|
731
|
-
// Store in Keychain
|
|
732
|
-
saveToKeychain(key: keychainKey, data: keyData)
|
|
733
|
-
|
|
734
|
-
return key
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
private func saveToKeychain(key: String, data: Data) {
|
|
738
|
-
let query: [String: Any] = [
|
|
739
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
740
|
-
kSecAttrService as String: "com.sriinnu.athena.Runic",
|
|
741
|
-
kSecAttrAccount as String: key,
|
|
742
|
-
kSecUseDataProtectionKeychain as String: true,
|
|
743
|
-
kSecValueData as String: data,
|
|
744
|
-
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
|
|
745
|
-
]
|
|
746
|
-
SecItemDelete(query as CFDictionary)
|
|
747
|
-
SecItemAdd(query as CFDictionary, nil)
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
private func loadFromKeychain(key: String) -> Data? {
|
|
751
|
-
var query: [String: Any] = [
|
|
752
|
-
kSecClass as String: kSecClassGenericPassword,
|
|
753
|
-
kSecAttrService as String: "com.sriinnu.athena.Runic",
|
|
754
|
-
kSecAttrAccount as String: key,
|
|
755
|
-
kSecUseDataProtectionKeychain as String: true,
|
|
756
|
-
kSecUseAuthenticationUI as String: "kSecUseAuthenticationUIFail" as CFString,
|
|
757
|
-
kSecReturnData as String: true,
|
|
758
|
-
]
|
|
759
|
-
let authContext = LAContext()
|
|
760
|
-
authContext.interactionNotAllowed = true
|
|
761
|
-
query[kSecUseAuthenticationContext as String] = authContext
|
|
762
|
-
var result: AnyObject?
|
|
763
|
-
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
764
|
-
if status == errSecInteractionNotAllowed {
|
|
765
|
-
return nil
|
|
766
|
-
}
|
|
767
|
-
return status == errSecSuccess ? result as? Data : nil
|
|
768
|
-
}
|