@sriinnu/runic 2.0.0 → 2.11.2
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 +261 -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,809 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/// Storage for team management, memberships, project ownership, and invitations
|
|
4
|
-
public enum TeamStore {
|
|
5
|
-
// MARK: - Types
|
|
6
|
-
|
|
7
|
-
public enum TeamRole: String, Codable, Sendable {
|
|
8
|
-
case owner
|
|
9
|
-
case admin
|
|
10
|
-
case member
|
|
11
|
-
case viewer
|
|
12
|
-
|
|
13
|
-
public var canManageMembers: Bool {
|
|
14
|
-
self == .owner || self == .admin
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public var canManageProjects: Bool {
|
|
18
|
-
self == .owner || self == .admin
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public var canEditTeam: Bool {
|
|
22
|
-
self == .owner || self == .admin
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public var canDeleteTeam: Bool {
|
|
26
|
-
self == .owner
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public enum AccessLevel: String, Codable, Sendable {
|
|
31
|
-
case `private`
|
|
32
|
-
case team
|
|
33
|
-
case shared
|
|
34
|
-
case `public`
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public enum InvitationStatus: String, Codable, Sendable {
|
|
38
|
-
case pending
|
|
39
|
-
case accepted
|
|
40
|
-
case declined
|
|
41
|
-
case expired
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public struct Team: Codable, Sendable, Identifiable {
|
|
45
|
-
public let id: String
|
|
46
|
-
public var name: String
|
|
47
|
-
public var ownerUserID: String
|
|
48
|
-
public var quota: Int // Monthly quota in tokens or cost units
|
|
49
|
-
public var usedQuota: Int
|
|
50
|
-
public let createdAt: Date
|
|
51
|
-
public var updatedAt: Date
|
|
52
|
-
|
|
53
|
-
public init(
|
|
54
|
-
id: String = UUID().uuidString,
|
|
55
|
-
name: String,
|
|
56
|
-
ownerUserID: String,
|
|
57
|
-
quota: Int,
|
|
58
|
-
usedQuota: Int = 0,
|
|
59
|
-
createdAt: Date = Date(),
|
|
60
|
-
updatedAt: Date = Date())
|
|
61
|
-
{
|
|
62
|
-
self.id = id
|
|
63
|
-
self.name = name
|
|
64
|
-
self.ownerUserID = ownerUserID
|
|
65
|
-
self.quota = quota
|
|
66
|
-
self.usedQuota = usedQuota
|
|
67
|
-
self.createdAt = createdAt
|
|
68
|
-
self.updatedAt = updatedAt
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public struct TeamMembership: Codable, Sendable, Identifiable {
|
|
73
|
-
public let id: String
|
|
74
|
-
public let teamID: String
|
|
75
|
-
public let userID: String
|
|
76
|
-
public var role: TeamRole
|
|
77
|
-
public var quota: Int? // Individual quota override, nil means use team quota
|
|
78
|
-
public var usedQuota: Int
|
|
79
|
-
public let joinedAt: Date
|
|
80
|
-
public var updatedAt: Date
|
|
81
|
-
|
|
82
|
-
public init(
|
|
83
|
-
id: String = UUID().uuidString,
|
|
84
|
-
teamID: String,
|
|
85
|
-
userID: String,
|
|
86
|
-
role: TeamRole,
|
|
87
|
-
quota: Int? = nil,
|
|
88
|
-
usedQuota: Int = 0,
|
|
89
|
-
joinedAt: Date = Date(),
|
|
90
|
-
updatedAt: Date = Date())
|
|
91
|
-
{
|
|
92
|
-
self.id = id
|
|
93
|
-
self.teamID = teamID
|
|
94
|
-
self.userID = userID
|
|
95
|
-
self.role = role
|
|
96
|
-
self.quota = quota
|
|
97
|
-
self.usedQuota = usedQuota
|
|
98
|
-
self.joinedAt = joinedAt
|
|
99
|
-
self.updatedAt = updatedAt
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public struct ProjectOwnership: Codable, Sendable, Identifiable {
|
|
104
|
-
public var id: String {
|
|
105
|
-
self.projectID
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
public let projectID: String
|
|
109
|
-
public var ownerUserID: String
|
|
110
|
-
public var teamID: String?
|
|
111
|
-
public var accessLevel: AccessLevel
|
|
112
|
-
public var sharedWithUserIDs: [String]
|
|
113
|
-
public let createdAt: Date
|
|
114
|
-
public var updatedAt: Date
|
|
115
|
-
|
|
116
|
-
public init(
|
|
117
|
-
projectID: String,
|
|
118
|
-
ownerUserID: String,
|
|
119
|
-
teamID: String? = nil,
|
|
120
|
-
accessLevel: AccessLevel = .private,
|
|
121
|
-
sharedWithUserIDs: [String] = [],
|
|
122
|
-
createdAt: Date = Date(),
|
|
123
|
-
updatedAt: Date = Date())
|
|
124
|
-
{
|
|
125
|
-
self.projectID = projectID
|
|
126
|
-
self.ownerUserID = ownerUserID
|
|
127
|
-
self.teamID = teamID
|
|
128
|
-
self.accessLevel = accessLevel
|
|
129
|
-
self.sharedWithUserIDs = sharedWithUserIDs
|
|
130
|
-
self.createdAt = createdAt
|
|
131
|
-
self.updatedAt = updatedAt
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
public struct TeamInvitation: Codable, Sendable, Identifiable {
|
|
136
|
-
public let id: String
|
|
137
|
-
public let teamID: String
|
|
138
|
-
public let email: String
|
|
139
|
-
public var role: TeamRole
|
|
140
|
-
public let invitedBy: String // userID
|
|
141
|
-
public var status: InvitationStatus
|
|
142
|
-
public let createdAt: Date
|
|
143
|
-
public var expiresAt: Date
|
|
144
|
-
public var respondedAt: Date?
|
|
145
|
-
|
|
146
|
-
public init(
|
|
147
|
-
id: String = UUID().uuidString,
|
|
148
|
-
teamID: String,
|
|
149
|
-
email: String,
|
|
150
|
-
role: TeamRole,
|
|
151
|
-
invitedBy: String,
|
|
152
|
-
status: InvitationStatus = .pending,
|
|
153
|
-
createdAt: Date = Date(),
|
|
154
|
-
expiresAt: Date = Date().addingTimeInterval(7 * 24 * 60 * 60), // 7 days
|
|
155
|
-
respondedAt: Date? = nil)
|
|
156
|
-
{
|
|
157
|
-
self.id = id
|
|
158
|
-
self.teamID = teamID
|
|
159
|
-
self.email = email
|
|
160
|
-
self.role = role
|
|
161
|
-
self.invitedBy = invitedBy
|
|
162
|
-
self.status = status
|
|
163
|
-
self.createdAt = createdAt
|
|
164
|
-
self.expiresAt = expiresAt
|
|
165
|
-
self.respondedAt = respondedAt
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public var isExpired: Bool {
|
|
169
|
-
Date() > self.expiresAt && self.status == .pending
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
public struct TeamsData: Codable {
|
|
174
|
-
public let version: Int
|
|
175
|
-
public var currentUserID: String?
|
|
176
|
-
public var teams: [String: Team] // teamID -> Team
|
|
177
|
-
public var memberships: [String: TeamMembership] // membershipID -> TeamMembership
|
|
178
|
-
public var projectOwnership: [String: ProjectOwnership] // projectID -> ProjectOwnership
|
|
179
|
-
public var invitations: [String: TeamInvitation] // invitationID -> TeamInvitation
|
|
180
|
-
|
|
181
|
-
public init(
|
|
182
|
-
version: Int = 1,
|
|
183
|
-
currentUserID: String? = nil,
|
|
184
|
-
teams: [String: Team] = [:],
|
|
185
|
-
memberships: [String: TeamMembership] = [:],
|
|
186
|
-
projectOwnership: [String: ProjectOwnership] = [:],
|
|
187
|
-
invitations: [String: TeamInvitation] = [:])
|
|
188
|
-
{
|
|
189
|
-
self.version = version
|
|
190
|
-
self.currentUserID = currentUserID
|
|
191
|
-
self.teams = teams
|
|
192
|
-
self.memberships = memberships
|
|
193
|
-
self.projectOwnership = projectOwnership
|
|
194
|
-
self.invitations = invitations
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// MARK: - Errors
|
|
199
|
-
|
|
200
|
-
public enum TeamStoreError: LocalizedError {
|
|
201
|
-
case teamNotFound
|
|
202
|
-
case membershipNotFound
|
|
203
|
-
case projectNotFound
|
|
204
|
-
case invitationNotFound
|
|
205
|
-
case invalidQuota
|
|
206
|
-
case insufficientPermissions
|
|
207
|
-
case duplicateMembership
|
|
208
|
-
case duplicateInvitation
|
|
209
|
-
case invitationExpired
|
|
210
|
-
case ownerCannotLeave
|
|
211
|
-
case invalidEmailFormat
|
|
212
|
-
case teamQuotaExceeded
|
|
213
|
-
case memberQuotaExceeded
|
|
214
|
-
|
|
215
|
-
public var errorDescription: String? {
|
|
216
|
-
switch self {
|
|
217
|
-
case .teamNotFound:
|
|
218
|
-
"Team not found"
|
|
219
|
-
case .membershipNotFound:
|
|
220
|
-
"Membership not found"
|
|
221
|
-
case .projectNotFound:
|
|
222
|
-
"Project not found"
|
|
223
|
-
case .invitationNotFound:
|
|
224
|
-
"Invitation not found"
|
|
225
|
-
case .invalidQuota:
|
|
226
|
-
"Invalid quota value"
|
|
227
|
-
case .insufficientPermissions:
|
|
228
|
-
"Insufficient permissions to perform this action"
|
|
229
|
-
case .duplicateMembership:
|
|
230
|
-
"User is already a member of this team"
|
|
231
|
-
case .duplicateInvitation:
|
|
232
|
-
"An invitation has already been sent to this email"
|
|
233
|
-
case .invitationExpired:
|
|
234
|
-
"This invitation has expired"
|
|
235
|
-
case .ownerCannotLeave:
|
|
236
|
-
"Team owner cannot leave the team. Transfer ownership first."
|
|
237
|
-
case .invalidEmailFormat:
|
|
238
|
-
"Invalid email format"
|
|
239
|
-
case .teamQuotaExceeded:
|
|
240
|
-
"Team quota exceeded"
|
|
241
|
-
case .memberQuotaExceeded:
|
|
242
|
-
"Member quota exceeded"
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// MARK: - Storage Location
|
|
248
|
-
|
|
249
|
-
private static var storageURL: URL {
|
|
250
|
-
let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
251
|
-
let runicDir = appSupport.appendingPathComponent("Runic", isDirectory: true)
|
|
252
|
-
try? FileManager.default.createDirectory(at: runicDir, withIntermediateDirectories: true)
|
|
253
|
-
return runicDir.appendingPathComponent("teams.json")
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// MARK: - Storage Operations
|
|
257
|
-
|
|
258
|
-
public static func load() -> TeamsData {
|
|
259
|
-
guard FileManager.default.fileExists(atPath: self.storageURL.path) else {
|
|
260
|
-
return TeamsData()
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
do {
|
|
264
|
-
let data = try Data(contentsOf: storageURL)
|
|
265
|
-
let decoder = JSONDecoder()
|
|
266
|
-
decoder.dateDecodingStrategy = .iso8601
|
|
267
|
-
return try decoder.decode(TeamsData.self, from: data)
|
|
268
|
-
} catch {
|
|
269
|
-
print("[TeamStore] Failed to load teams: \(error)")
|
|
270
|
-
return TeamsData()
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
public static func save(_ teamsData: TeamsData) throws {
|
|
275
|
-
let encoder = JSONEncoder()
|
|
276
|
-
encoder.dateEncodingStrategy = .iso8601
|
|
277
|
-
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
278
|
-
|
|
279
|
-
let data = try encoder.encode(teamsData)
|
|
280
|
-
try data.write(to: self.storageURL, options: .atomic)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// MARK: - Team Management
|
|
284
|
-
|
|
285
|
-
public static func createTeam(name: String, ownerUserID: String, quota: Int) throws -> Team {
|
|
286
|
-
guard quota > 0 else {
|
|
287
|
-
throw TeamStoreError.invalidQuota
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
var data = self.load()
|
|
291
|
-
let team = Team(name: name, ownerUserID: ownerUserID, quota: quota)
|
|
292
|
-
data.teams[team.id] = team
|
|
293
|
-
|
|
294
|
-
// Automatically add owner as a member with owner role
|
|
295
|
-
let ownerMembership = TeamMembership(
|
|
296
|
-
teamID: team.id,
|
|
297
|
-
userID: ownerUserID,
|
|
298
|
-
role: .owner)
|
|
299
|
-
data.memberships[ownerMembership.id] = ownerMembership
|
|
300
|
-
|
|
301
|
-
try self.save(data)
|
|
302
|
-
return team
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
public static func getTeam(id: String) -> Team? {
|
|
306
|
-
self.load().teams[id]
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
public static func updateTeam(_ team: Team) throws {
|
|
310
|
-
var data = self.load()
|
|
311
|
-
guard data.teams[team.id] != nil else {
|
|
312
|
-
throw TeamStoreError.teamNotFound
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
var updatedTeam = team
|
|
316
|
-
updatedTeam.updatedAt = Date()
|
|
317
|
-
data.teams[team.id] = updatedTeam
|
|
318
|
-
try self.save(data)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
public static func deleteTeam(id: String) throws {
|
|
322
|
-
var data = self.load()
|
|
323
|
-
guard data.teams[id] != nil else {
|
|
324
|
-
throw TeamStoreError.teamNotFound
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Remove team
|
|
328
|
-
data.teams.removeValue(forKey: id)
|
|
329
|
-
|
|
330
|
-
// Remove all memberships
|
|
331
|
-
data.memberships = data.memberships.filter { $0.value.teamID != id }
|
|
332
|
-
|
|
333
|
-
// Remove all invitations
|
|
334
|
-
data.invitations = data.invitations.filter { $0.value.teamID != id }
|
|
335
|
-
|
|
336
|
-
// Update project ownership (set teamID to nil for projects owned by this team)
|
|
337
|
-
for (projectID, ownership) in data.projectOwnership where ownership.teamID == id {
|
|
338
|
-
var updatedOwnership = ownership
|
|
339
|
-
updatedOwnership.teamID = nil
|
|
340
|
-
updatedOwnership.accessLevel = .private
|
|
341
|
-
updatedOwnership.updatedAt = Date()
|
|
342
|
-
data.projectOwnership[projectID] = updatedOwnership
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
try self.save(data)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
public static func getUserTeams(userID: String) -> [Team] {
|
|
349
|
-
let data = self.load()
|
|
350
|
-
let userMemberships = data.memberships.values.filter { $0.userID == userID }
|
|
351
|
-
let teamIDs = Set(userMemberships.map(\.teamID))
|
|
352
|
-
return teamIDs.compactMap { data.teams[$0] }.sorted { $0.createdAt < $1.createdAt }
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// MARK: - Membership Management
|
|
356
|
-
|
|
357
|
-
public static func addMember(
|
|
358
|
-
teamID: String,
|
|
359
|
-
userID: String,
|
|
360
|
-
role: TeamRole,
|
|
361
|
-
quota: Int? = nil) throws -> TeamMembership
|
|
362
|
-
{
|
|
363
|
-
var data = self.load()
|
|
364
|
-
|
|
365
|
-
guard data.teams[teamID] != nil else {
|
|
366
|
-
throw TeamStoreError.teamNotFound
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Check for duplicate membership
|
|
370
|
-
let existingMembership = data.memberships.values.first { $0.teamID == teamID && $0.userID == userID }
|
|
371
|
-
if existingMembership != nil {
|
|
372
|
-
throw TeamStoreError.duplicateMembership
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Validate quota if provided
|
|
376
|
-
if let quota, quota < 0 {
|
|
377
|
-
throw TeamStoreError.invalidQuota
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
let membership = TeamMembership(
|
|
381
|
-
teamID: teamID,
|
|
382
|
-
userID: userID,
|
|
383
|
-
role: role,
|
|
384
|
-
quota: quota)
|
|
385
|
-
data.memberships[membership.id] = membership
|
|
386
|
-
|
|
387
|
-
try self.save(data)
|
|
388
|
-
return membership
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
public static func updateMemberRole(membershipID: String, newRole: TeamRole) throws {
|
|
392
|
-
var data = self.load()
|
|
393
|
-
guard var membership = data.memberships[membershipID] else {
|
|
394
|
-
throw TeamStoreError.membershipNotFound
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Prevent changing owner role (should use transfer ownership instead)
|
|
398
|
-
if membership.role == .owner, newRole != .owner {
|
|
399
|
-
throw TeamStoreError.insufficientPermissions
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
membership.role = newRole
|
|
403
|
-
membership.updatedAt = Date()
|
|
404
|
-
data.memberships[membershipID] = membership
|
|
405
|
-
|
|
406
|
-
try self.save(data)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
public static func updateMemberQuota(membershipID: String, newQuota: Int?) throws {
|
|
410
|
-
var data = self.load()
|
|
411
|
-
guard var membership = data.memberships[membershipID] else {
|
|
412
|
-
throw TeamStoreError.membershipNotFound
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if let quota = newQuota, quota < 0 {
|
|
416
|
-
throw TeamStoreError.invalidQuota
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
membership.quota = newQuota
|
|
420
|
-
membership.updatedAt = Date()
|
|
421
|
-
data.memberships[membershipID] = membership
|
|
422
|
-
|
|
423
|
-
try self.save(data)
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
public static func removeMember(membershipID: String) throws {
|
|
427
|
-
var data = self.load()
|
|
428
|
-
guard let membership = data.memberships[membershipID] else {
|
|
429
|
-
throw TeamStoreError.membershipNotFound
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Prevent removing the owner
|
|
433
|
-
if membership.role == .owner {
|
|
434
|
-
throw TeamStoreError.ownerCannotLeave
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
data.memberships.removeValue(forKey: membershipID)
|
|
438
|
-
try self.save(data)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
public static func getTeamMembers(teamID: String) -> [TeamMembership] {
|
|
442
|
-
self.load().memberships.values
|
|
443
|
-
.filter { $0.teamID == teamID }
|
|
444
|
-
.sorted { $0.joinedAt < $1.joinedAt }
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
public static func getUserMembership(teamID: String, userID: String) -> TeamMembership? {
|
|
448
|
-
self.load().memberships.values.first { $0.teamID == teamID && $0.userID == userID }
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
public static func transferOwnership(teamID: String, newOwnerUserID: String) throws {
|
|
452
|
-
var data = self.load()
|
|
453
|
-
guard var team = data.teams[teamID] else {
|
|
454
|
-
throw TeamStoreError.teamNotFound
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Get current owner membership
|
|
458
|
-
guard let currentOwnerMembership = data.memberships.values
|
|
459
|
-
.first(where: { $0.teamID == teamID && $0.userID == team.ownerUserID })
|
|
460
|
-
else {
|
|
461
|
-
throw TeamStoreError.membershipNotFound
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Get new owner membership
|
|
465
|
-
guard let newOwnerMembership = data.memberships.values
|
|
466
|
-
.first(where: { $0.teamID == teamID && $0.userID == newOwnerUserID })
|
|
467
|
-
else {
|
|
468
|
-
throw TeamStoreError.membershipNotFound
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Update team owner
|
|
472
|
-
team.ownerUserID = newOwnerUserID
|
|
473
|
-
team.updatedAt = Date()
|
|
474
|
-
data.teams[teamID] = team
|
|
475
|
-
|
|
476
|
-
// Update memberships
|
|
477
|
-
var updatedCurrentOwner = currentOwnerMembership
|
|
478
|
-
updatedCurrentOwner.role = .admin
|
|
479
|
-
updatedCurrentOwner.updatedAt = Date()
|
|
480
|
-
data.memberships[currentOwnerMembership.id] = updatedCurrentOwner
|
|
481
|
-
|
|
482
|
-
var updatedNewOwner = newOwnerMembership
|
|
483
|
-
updatedNewOwner.role = .owner
|
|
484
|
-
updatedNewOwner.updatedAt = Date()
|
|
485
|
-
data.memberships[newOwnerMembership.id] = updatedNewOwner
|
|
486
|
-
|
|
487
|
-
try self.save(data)
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// MARK: - Project Ownership Management
|
|
491
|
-
|
|
492
|
-
public static func setProjectOwnership(
|
|
493
|
-
projectID: String,
|
|
494
|
-
ownerUserID: String,
|
|
495
|
-
teamID: String? = nil,
|
|
496
|
-
accessLevel: AccessLevel) throws
|
|
497
|
-
{
|
|
498
|
-
var data = self.load()
|
|
499
|
-
|
|
500
|
-
// Validate team exists if provided
|
|
501
|
-
if let teamID {
|
|
502
|
-
guard data.teams[teamID] != nil else {
|
|
503
|
-
throw TeamStoreError.teamNotFound
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if let existing = data.projectOwnership[projectID] {
|
|
508
|
-
var updated = existing
|
|
509
|
-
updated.ownerUserID = ownerUserID
|
|
510
|
-
updated.teamID = teamID
|
|
511
|
-
updated.accessLevel = accessLevel
|
|
512
|
-
updated.updatedAt = Date()
|
|
513
|
-
data.projectOwnership[projectID] = updated
|
|
514
|
-
} else {
|
|
515
|
-
let ownership = ProjectOwnership(
|
|
516
|
-
projectID: projectID,
|
|
517
|
-
ownerUserID: ownerUserID,
|
|
518
|
-
teamID: teamID,
|
|
519
|
-
accessLevel: accessLevel)
|
|
520
|
-
data.projectOwnership[projectID] = ownership
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
try self.save(data)
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
public static func shareProject(projectID: String, withUserIDs: [String]) throws {
|
|
527
|
-
var data = self.load()
|
|
528
|
-
guard var ownership = data.projectOwnership[projectID] else {
|
|
529
|
-
throw TeamStoreError.projectNotFound
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
ownership.sharedWithUserIDs = Array(Set(ownership.sharedWithUserIDs + withUserIDs))
|
|
533
|
-
ownership.accessLevel = .shared
|
|
534
|
-
ownership.updatedAt = Date()
|
|
535
|
-
data.projectOwnership[projectID] = ownership
|
|
536
|
-
|
|
537
|
-
try self.save(data)
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
public static func unshareProject(projectID: String, withUserIDs: [String]) throws {
|
|
541
|
-
var data = self.load()
|
|
542
|
-
guard var ownership = data.projectOwnership[projectID] else {
|
|
543
|
-
throw TeamStoreError.projectNotFound
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
ownership.sharedWithUserIDs = ownership.sharedWithUserIDs.filter { !withUserIDs.contains($0) }
|
|
547
|
-
ownership.updatedAt = Date()
|
|
548
|
-
|
|
549
|
-
// If no users are shared with, revert to private
|
|
550
|
-
if ownership.sharedWithUserIDs.isEmpty, ownership.accessLevel == .shared {
|
|
551
|
-
ownership.accessLevel = .private
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
data.projectOwnership[projectID] = ownership
|
|
555
|
-
try self.save(data)
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
public static func getProjectOwnership(projectID: String) -> ProjectOwnership? {
|
|
559
|
-
self.load().projectOwnership[projectID]
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
public static func getUserProjects(userID: String) -> [ProjectOwnership] {
|
|
563
|
-
let data = self.load()
|
|
564
|
-
return data.projectOwnership.values
|
|
565
|
-
.filter { $0.ownerUserID == userID || $0.sharedWithUserIDs.contains(userID) }
|
|
566
|
-
.sorted { $0.createdAt < $1.createdAt }
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
public static func getTeamProjects(teamID: String) -> [ProjectOwnership] {
|
|
570
|
-
self.load().projectOwnership.values
|
|
571
|
-
.filter { $0.teamID == teamID }
|
|
572
|
-
.sorted { $0.createdAt < $1.createdAt }
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
public static func deleteProjectOwnership(projectID: String) throws {
|
|
576
|
-
var data = self.load()
|
|
577
|
-
guard data.projectOwnership[projectID] != nil else {
|
|
578
|
-
throw TeamStoreError.projectNotFound
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
data.projectOwnership.removeValue(forKey: projectID)
|
|
582
|
-
try self.save(data)
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// MARK: - Invitation Management
|
|
586
|
-
|
|
587
|
-
public static func createInvitation(
|
|
588
|
-
teamID: String,
|
|
589
|
-
email: String,
|
|
590
|
-
role: TeamRole,
|
|
591
|
-
invitedBy: String) throws -> TeamInvitation
|
|
592
|
-
{
|
|
593
|
-
var data = self.load()
|
|
594
|
-
|
|
595
|
-
guard data.teams[teamID] != nil else {
|
|
596
|
-
throw TeamStoreError.teamNotFound
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// Validate email format (basic validation)
|
|
600
|
-
guard email.contains("@"), email.contains(".") else {
|
|
601
|
-
throw TeamStoreError.invalidEmailFormat
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// Check for duplicate pending invitation
|
|
605
|
-
let existingInvitation = data.invitations.values.first {
|
|
606
|
-
$0.teamID == teamID && $0.email.lowercased() == email.lowercased() && $0.status == .pending
|
|
607
|
-
}
|
|
608
|
-
if existingInvitation != nil {
|
|
609
|
-
throw TeamStoreError.duplicateInvitation
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
let invitation = TeamInvitation(
|
|
613
|
-
teamID: teamID,
|
|
614
|
-
email: email,
|
|
615
|
-
role: role,
|
|
616
|
-
invitedBy: invitedBy)
|
|
617
|
-
data.invitations[invitation.id] = invitation
|
|
618
|
-
|
|
619
|
-
try self.save(data)
|
|
620
|
-
return invitation
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
public static func acceptInvitation(id: String, userID: String) throws {
|
|
624
|
-
var data = self.load()
|
|
625
|
-
guard var invitation = data.invitations[id] else {
|
|
626
|
-
throw TeamStoreError.invitationNotFound
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
guard invitation.status == .pending else {
|
|
630
|
-
throw TeamStoreError.invitationExpired
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if invitation.isExpired {
|
|
634
|
-
invitation.status = .expired
|
|
635
|
-
invitation.respondedAt = Date()
|
|
636
|
-
data.invitations[id] = invitation
|
|
637
|
-
try self.save(data)
|
|
638
|
-
throw TeamStoreError.invitationExpired
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// Create membership
|
|
642
|
-
let membership = TeamMembership(
|
|
643
|
-
teamID: invitation.teamID,
|
|
644
|
-
userID: userID,
|
|
645
|
-
role: invitation.role)
|
|
646
|
-
data.memberships[membership.id] = membership
|
|
647
|
-
|
|
648
|
-
// Update invitation status
|
|
649
|
-
invitation.status = .accepted
|
|
650
|
-
invitation.respondedAt = Date()
|
|
651
|
-
data.invitations[id] = invitation
|
|
652
|
-
|
|
653
|
-
try self.save(data)
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
public static func declineInvitation(id: String) throws {
|
|
657
|
-
var data = self.load()
|
|
658
|
-
guard var invitation = data.invitations[id] else {
|
|
659
|
-
throw TeamStoreError.invitationNotFound
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
invitation.status = .declined
|
|
663
|
-
invitation.respondedAt = Date()
|
|
664
|
-
data.invitations[id] = invitation
|
|
665
|
-
|
|
666
|
-
try self.save(data)
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
public static func cancelInvitation(id: String) throws {
|
|
670
|
-
var data = self.load()
|
|
671
|
-
guard data.invitations[id] != nil else {
|
|
672
|
-
throw TeamStoreError.invitationNotFound
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
data.invitations.removeValue(forKey: id)
|
|
676
|
-
try self.save(data)
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
public static func getPendingInvitations(email: String) -> [TeamInvitation] {
|
|
680
|
-
self.load().invitations.values
|
|
681
|
-
.filter { $0.email.lowercased() == email.lowercased() && $0.status == .pending && !$0.isExpired }
|
|
682
|
-
.sorted { $0.createdAt > $1.createdAt }
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
public static func getTeamInvitations(teamID: String) -> [TeamInvitation] {
|
|
686
|
-
self.load().invitations.values
|
|
687
|
-
.filter { $0.teamID == teamID }
|
|
688
|
-
.sorted { $0.createdAt > $1.createdAt }
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
public static func cleanupExpiredInvitations() throws {
|
|
692
|
-
var data = self.load()
|
|
693
|
-
let expiredIDs = data.invitations.filter(\.value.isExpired).map(\.key)
|
|
694
|
-
|
|
695
|
-
for id in expiredIDs {
|
|
696
|
-
data.invitations[id]?.status = .expired
|
|
697
|
-
data.invitations[id]?.respondedAt = Date()
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
try self.save(data)
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// MARK: - Current User Management
|
|
704
|
-
|
|
705
|
-
public static func setCurrentUser(userID: String) throws {
|
|
706
|
-
var data = self.load()
|
|
707
|
-
data.currentUserID = userID
|
|
708
|
-
try self.save(data)
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
public static func getCurrentUser() -> String? {
|
|
712
|
-
self.load().currentUserID
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// MARK: - Usage Tracking
|
|
716
|
-
|
|
717
|
-
public static func incrementTeamUsage(teamID: String, amount: Int) throws {
|
|
718
|
-
guard amount >= 0 else {
|
|
719
|
-
throw TeamStoreError.invalidQuota
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
var data = self.load()
|
|
723
|
-
guard var team = data.teams[teamID] else {
|
|
724
|
-
throw TeamStoreError.teamNotFound
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
team.usedQuota += amount
|
|
728
|
-
team.updatedAt = Date()
|
|
729
|
-
|
|
730
|
-
if team.usedQuota > team.quota {
|
|
731
|
-
data.teams[teamID] = team
|
|
732
|
-
try self.save(data)
|
|
733
|
-
throw TeamStoreError.teamQuotaExceeded
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
data.teams[teamID] = team
|
|
737
|
-
try self.save(data)
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
public static func incrementMemberUsage(membershipID: String, amount: Int) throws {
|
|
741
|
-
guard amount >= 0 else {
|
|
742
|
-
throw TeamStoreError.invalidQuota
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
var data = self.load()
|
|
746
|
-
guard var membership = data.memberships[membershipID] else {
|
|
747
|
-
throw TeamStoreError.membershipNotFound
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
membership.usedQuota += amount
|
|
751
|
-
membership.updatedAt = Date()
|
|
752
|
-
|
|
753
|
-
// Check individual quota if set
|
|
754
|
-
if let quota = membership.quota, membership.usedQuota > quota {
|
|
755
|
-
data.memberships[membershipID] = membership
|
|
756
|
-
try self.save(data)
|
|
757
|
-
throw TeamStoreError.memberQuotaExceeded
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
data.memberships[membershipID] = membership
|
|
761
|
-
try self.save(data)
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
public static func resetTeamUsage(teamID: String) throws {
|
|
765
|
-
var data = self.load()
|
|
766
|
-
guard var team = data.teams[teamID] else {
|
|
767
|
-
throw TeamStoreError.teamNotFound
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
team.usedQuota = 0
|
|
771
|
-
team.updatedAt = Date()
|
|
772
|
-
data.teams[teamID] = team
|
|
773
|
-
|
|
774
|
-
try self.save(data)
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
public static func resetMemberUsage(membershipID: String) throws {
|
|
778
|
-
var data = self.load()
|
|
779
|
-
guard var membership = data.memberships[membershipID] else {
|
|
780
|
-
throw TeamStoreError.membershipNotFound
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
membership.usedQuota = 0
|
|
784
|
-
membership.updatedAt = Date()
|
|
785
|
-
data.memberships[membershipID] = membership
|
|
786
|
-
|
|
787
|
-
try self.save(data)
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
public static func resetAllTeamUsages() throws {
|
|
791
|
-
var data = self.load()
|
|
792
|
-
|
|
793
|
-
// Reset all teams
|
|
794
|
-
for (id, var team) in data.teams {
|
|
795
|
-
team.usedQuota = 0
|
|
796
|
-
team.updatedAt = Date()
|
|
797
|
-
data.teams[id] = team
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
// Reset all memberships
|
|
801
|
-
for (id, var membership) in data.memberships {
|
|
802
|
-
membership.usedQuota = 0
|
|
803
|
-
membership.updatedAt = Date()
|
|
804
|
-
data.memberships[id] = membership
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
try self.save(data)
|
|
808
|
-
}
|
|
809
|
-
}
|