@sriinnu/runic 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +8 -1
- package/RuniciOS/Core/RunicTypographyiOS.swift +24 -0
- package/RuniciOS/Views/ProviderDetailView.swift +9 -9
- package/RuniciOS/Views/ProviderListView.swift +10 -10
- package/RuniciOS/Widgets/LargeWidget.swift +14 -14
- package/RuniciOS/Widgets/LiveActivity.swift +19 -19
- package/RuniciOS/Widgets/LockScreenWidget.swift +15 -15
- package/RuniciOS/Widgets/MediumWidget.swift +10 -10
- package/RuniciOS/Widgets/RunicWidget.swift +5 -5
- package/RuniciOS/Widgets/SmallWidget.swift +9 -9
- package/Sources/Runic/About.swift +3 -3
- package/Sources/Runic/AppNotifications.swift +5 -3
- package/Sources/Runic/Controllers/StatusItemController+Actions.swift +7 -3
- package/Sources/Runic/Controllers/StatusItemController+Animation.swift +5 -4
- package/Sources/Runic/Controllers/StatusItemController+ChartSubmenus.swift +30 -16
- package/Sources/Runic/Controllers/StatusItemController+CustomProviders.swift +6 -3
- package/Sources/Runic/Controllers/StatusItemController+Menu.swift +18 -13
- package/Sources/Runic/Controllers/StatusItemController+MenuCards.swift +6 -4
- package/Sources/Runic/Controllers/StatusItemController+MenuHelpers.swift +40 -12
- package/Sources/Runic/Controllers/StatusItemController.swift +43 -43
- package/Sources/Runic/Core/BudgetNotificationManager.swift +2 -1
- package/Sources/Runic/Core/Login/ClaudeLoginRunner.swift +2 -2
- package/Sources/Runic/Core/Login/CodexLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Login/CursorLoginRunner.swift +4 -4
- package/Sources/Runic/Core/Login/GeminiLoginRunner.swift +1 -1
- package/Sources/Runic/Core/Rendering/IconRenderer.swift +91 -85
- package/Sources/Runic/Core/Rendering/IconView.swift +3 -1
- package/Sources/Runic/Core/RunicColors.swift +6 -6
- package/Sources/Runic/Core/RunicSpacing.swift +7 -7
- package/Sources/Runic/Core/RunicTypography.swift +206 -0
- package/Sources/Runic/Core/Stores/AuggieTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/AzureOpenAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CerebrasTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CohereTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CopilotTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/CustomProviderStore.swift +10 -10
- package/Sources/Runic/Core/Stores/DeepSeekTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/FireworksTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/GroqTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/KimiTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxCookieHeaderStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxGroupIDStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MiniMaxTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/MistralTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/OpenRouterTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/PerplexityTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ProviderToggleStore.swift +1 -1
- package/Sources/Runic/Core/Stores/QwenTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SambaNovaTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/SettingsStore.swift +89 -51
- package/Sources/Runic/Core/Stores/TogetherTokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/UsageStore+Accessors.swift +19 -6
- package/Sources/Runic/Core/Stores/UsageStore+TokenCost.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore+WidgetSnapshot.swift +1 -1
- package/Sources/Runic/Core/Stores/UsageStore.swift +187 -161
- package/Sources/Runic/Core/Stores/XAITokenStore.swift +1 -3
- package/Sources/Runic/Core/Stores/ZaiTokenStore.swift +1 -2
- package/Sources/Runic/Core/UsageExporter.swift +8 -4
- package/Sources/Runic/ProviderRegistry.swift +1 -1
- package/Sources/Runic/Providers/Antigravity/AntigravityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Auggie/AuggieProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Azure/AzureProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Bedrock/BedrockProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cerebras/CerebrasProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Codex/CodexProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cohere/CohereProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Cursor/CursorProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/DeepSeek/DeepSeekProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Factory/FactoryProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Fireworks/FireworksProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Gemini/GeminiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Groq/GroqProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Kimi/KimiProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/MiniMax/MiniMaxProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Mistral/MistralProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/OpenRouter/OpenRouterProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Perplexity/PerplexityProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/SambaNova/SambaNovaProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderImplementation.swift +5 -2
- package/Sources/Runic/Providers/Shared/ProviderImplementationRegistry.swift +1 -1
- package/Sources/Runic/Providers/Shared/ProviderSettingsDescriptors.swift +1 -1
- package/Sources/Runic/Providers/Together/TogetherProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/XAI/XAIProviderImplementation.swift +1 -1
- package/Sources/Runic/Providers/Zai/ZaiProviderImplementation.swift +1 -1
- 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/RunicApp.swift +46 -36
- package/Sources/Runic/SessionQuotaNotifications.swift +2 -2
- package/Sources/Runic/UsagePaceText.swift +1 -1
- package/Sources/Runic/UsageProgressBar.swift +13 -9
- package/Sources/Runic/Utilities/LoadingPattern.swift +4 -2
- package/Sources/Runic/Utilities/PerformanceConstants.swift +28 -28
- package/Sources/Runic/Views/Menu/AlertsMenuView.swift +19 -21
- package/Sources/Runic/Views/Menu/CostComparativeChartMenuView.swift +19 -19
- package/Sources/Runic/Views/Menu/CostHistoryChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Menu/CreditsHistoryChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/EfficiencyMetricsMenuView.swift +29 -30
- package/Sources/Runic/Views/Menu/HeroStatCardsMenuView.swift +20 -17
- package/Sources/Runic/Views/Menu/HourlyActivityChartMenuView.swift +14 -14
- package/Sources/Runic/Views/Menu/InlineUsageChartView.swift +12 -10
- package/Sources/Runic/Views/Menu/MenuCardEntranceModifier.swift +3 -4
- package/Sources/Runic/Views/Menu/MenuCardSidebarView.swift +5 -25
- package/Sources/Runic/Views/Menu/MenuCardSkeletonView.swift +1 -1
- package/Sources/Runic/Views/Menu/MenuCardView.swift +120 -131
- package/Sources/Runic/Views/Menu/MenuContent.swift +11 -8
- package/Sources/Runic/Views/Menu/MenuDescriptor.swift +4 -2
- package/Sources/Runic/Views/Menu/MenuHighlightStyle.swift +1 -8
- package/Sources/Runic/Views/Menu/ModelBreakdownMenuView.swift +20 -14
- package/Sources/Runic/Views/Menu/OverviewMenuView.swift +18 -15
- package/Sources/Runic/Views/Menu/ProjectBreakdownMenuView.swift +16 -11
- package/Sources/Runic/Views/Menu/ProjectBudgetMenuView.swift +27 -27
- package/Sources/Runic/Views/Menu/ProviderTabBarView.swift +4 -6
- package/Sources/Runic/Views/Menu/SubscriptionUtilizationChartMenuView.swift +24 -11
- package/Sources/Runic/Views/Menu/UsageBreakdownChartMenuView.swift +5 -5
- package/Sources/Runic/Views/Menu/UsageHeatmapMenuView.swift +7 -8
- package/Sources/Runic/Views/Menu/UsageTimelineChartMenuView.swift +22 -17
- package/Sources/Runic/Views/Menu/UsageWindowComparisonChartMenuView.swift +8 -7
- package/Sources/Runic/Views/Menu/WeeklyActivityChartMenuView.swift +7 -7
- package/Sources/Runic/Views/Performance/PerformanceDashboardView.swift +50 -47
- package/Sources/Runic/Views/Preferences/LiquidComponents.swift +15 -14
- package/Sources/Runic/Views/Preferences/PreferencesAboutPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesAdvancedPane.swift +27 -27
- package/Sources/Runic/Views/Preferences/PreferencesAlertsPane.swift +38 -42
- package/Sources/Runic/Views/Preferences/PreferencesAnalyticsPane.swift +39 -43
- package/Sources/Runic/Views/Preferences/PreferencesBudgetsPane.swift +21 -21
- package/Sources/Runic/Views/Preferences/PreferencesComponents.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesCustomProvidersPane.swift +49 -50
- package/Sources/Runic/Views/Preferences/PreferencesDebugPane.swift +10 -10
- package/Sources/Runic/Views/Preferences/PreferencesGeneralPane.swift +40 -21
- package/Sources/Runic/Views/Preferences/PreferencesHelpPane.swift +7 -7
- package/Sources/Runic/Views/Preferences/PreferencesIntegrationsPane.swift +44 -39
- package/Sources/Runic/Views/Preferences/PreferencesPerformancePane.swift +26 -23
- package/Sources/Runic/Views/Preferences/PreferencesProvidersPane.swift +271 -226
- package/Sources/Runic/Views/Preferences/PreferencesSelection.swift +6 -2
- package/Sources/Runic/Views/Preferences/PreferencesSyncPane.swift +7 -5
- package/Sources/Runic/Views/Preferences/PreferencesView.swift +38 -31
- package/Sources/Runic/Views/Rating/QualityRatingView.swift +11 -11
- package/Sources/Runic/Views/Team/TeamDashboardView.swift +29 -24
- package/Sources/Runic/Views/Team/TeamInviteSheet.swift +16 -16
- package/Sources/Runic/Views/Team/TeamManagementView.swift +50 -49
- package/Sources/Runic/Views/Team/TeamMemberRow.swift +8 -8
- package/Sources/RunicCLI/CLIEntry.swift +83 -42
- package/Sources/RunicCLI/CLIRenderer.swift +1 -1
- package/Sources/RunicCLI/Commands/EnhancedUsageCommand.swift +60 -60
- package/Sources/RunicCLI/Commands/ModelsCommand.swift +45 -44
- package/Sources/RunicCLI/Commands/ProjectsCommand.swift +83 -69
- package/Sources/RunicCLI/Commands/ResetCommand.swift +50 -49
- package/Sources/RunicCLI/Commands/UsageLedgerSourceFactory.swift +31 -32
- package/Sources/RunicClaudeWebProbe/main.swift +1 -1
- package/Sources/RunicCore/AlertRuleStore.swift +20 -20
- package/Sources/RunicCore/BrowserCookieImportOrder.swift +3 -3
- package/Sources/RunicCore/CostUsageModels.swift +1 -1
- package/Sources/RunicCore/CustomProviders/CustomProviderModels.swift +29 -30
- package/Sources/RunicCore/CustomProviders/GenericProviderFetcher.swift +73 -75
- package/Sources/RunicCore/CustomProviders/ProviderTemplates.swift +53 -109
- package/Sources/RunicCore/Host/PTY/TTYCommandRunner.swift +3 -3
- package/Sources/RunicCore/Integrations/GitHubIntegration.swift +3 -3
- package/Sources/RunicCore/Models/EnhancedUsageModels.swift +47 -48
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardBrowserCookieImporter.swift +1 -1
- package/Sources/RunicCore/OpenAIWeb/OpenAIDashboardFetcher.swift +1 -1
- package/Sources/RunicCore/Performance/PerformanceModels.swift +4 -4
- package/Sources/RunicCore/Performance/PerformanceStorageImpl.swift +81 -64
- package/Sources/RunicCore/Policy/UsagePolicyEngine.swift +2 -2
- package/Sources/RunicCore/ProjectBudgetStore.swift +15 -12
- package/Sources/RunicCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Antigravity/AntigravityStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Auggie/AuggieUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Azure/AzureProviderDescriptor.swift +8 -8
- package/Sources/RunicCore/Providers/Azure/AzureUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Bedrock/BedrockProviderDescriptor.swift +6 -6
- package/Sources/RunicCore/Providers/Bedrock/BedrockUsageFetcher.swift +3 -7
- package/Sources/RunicCore/Providers/Cerebras/CerebrasProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cerebras/CerebrasUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Claude/ClaudeProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Claude/ClaudeStatusProbe.swift +4 -4
- package/Sources/RunicCore/Providers/Claude/ClaudeUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Claude/ClaudeWeb/ClaudeWebAPIFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexOAuth/CodexOAuthUsageFetcher.swift +1 -1
- package/Sources/RunicCore/Providers/Codex/CodexProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Codex/CodexUsageDataSource.swift +3 -1
- package/Sources/RunicCore/Providers/Codex/CodexWebDashboardStrategy.swift +5 -3
- package/Sources/RunicCore/Providers/Cohere/CohereProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Cohere/CohereUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotGitHubCLITokenReader.swift +7 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotProviderDescriptor.swift +4 -4
- package/Sources/RunicCore/Providers/Copilot/CopilotUsageFetcher.swift +5 -3
- package/Sources/RunicCore/Providers/Copilot/CopilotVSCodeTokenReader.swift +9 -10
- package/Sources/RunicCore/Providers/Cursor/CursorProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/DeepSeek/DeepSeekUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Factory/FactoryLocalStorageImporter.swift +4 -4
- package/Sources/RunicCore/Providers/Factory/FactoryProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Factory/FactoryStatusProbe.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Fireworks/FireworksUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Gemini/GeminiStatusProbe.swift +5 -2
- package/Sources/RunicCore/Providers/Groq/GroqProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Groq/GroqUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Kimi/KimiUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +10 -5
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxUsageFetcher.swift +7 -7
- package/Sources/RunicCore/Providers/MiniMax/MiniMaxWebUsageFetcher.swift +10 -10
- package/Sources/RunicCore/Providers/Mistral/MistralProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Mistral/MistralUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/OpenRouter/OpenRouterUsageFetcher.swift +23 -10
- package/Sources/RunicCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Perplexity/PerplexityUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderFetchPlan.swift +2 -2
- package/Sources/RunicCore/Providers/ProviderTokenResolver.swift +2 -2
- package/Sources/RunicCore/Providers/Qwen/QwenProviderDescriptor.swift +5 -3
- package/Sources/RunicCore/Providers/Qwen/QwenUsageStats.swift +26 -13
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/SambaNova/SambaNovaUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/Together/TogetherUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +5 -5
- package/Sources/RunicCore/Providers/VertexAI/VertexAISettingsReader.swift +1 -1
- package/Sources/RunicCore/Providers/VertexAI/VertexAIUsageFetcher.swift +3 -3
- package/Sources/RunicCore/Providers/XAI/XAIProviderDescriptor.swift +2 -2
- package/Sources/RunicCore/Providers/XAI/XAIUsageFetcher.swift +2 -2
- package/Sources/RunicCore/Providers/Zai/ZaiProviderDescriptor.swift +4 -2
- package/Sources/RunicCore/Providers/Zai/ZaiUsageStats.swift +48 -21
- package/Sources/RunicCore/Sync/BackgroundSyncManager.swift +76 -77
- package/Sources/RunicCore/Sync/SyncConflictResolver.swift +55 -58
- package/Sources/RunicCore/Sync/SyncProtocol.swift +21 -22
- package/Sources/RunicCore/Sync/SyncRecord.swift +46 -49
- package/Sources/RunicCore/Sync/SyncUsageExample.swift +44 -50
- package/Sources/RunicCore/Sync/TeamSyncRecords.swift +94 -101
- package/Sources/RunicCore/Sync/iCloudSyncEngine.swift +51 -46
- package/Sources/RunicCore/Team/TeamModels.swift +111 -110
- package/Sources/RunicCore/Team/TeamStore.swift +126 -109
- package/Sources/RunicCore/UsageFetcher.swift +2 -2
- package/Sources/RunicCore/UsageFormatter.swift +25 -25
- package/Sources/RunicCore/UsageLedger/ClaudeUsageLogSource.swift +27 -13
- package/Sources/RunicCore/UsageLedger/CodexUsageLogSource.swift +11 -3
- package/Sources/RunicCore/UsageLedger/LedgerCache.swift +5 -2
- package/Sources/RunicCore/UsageLedger/OTelGenAILedgerAdapter.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerAggregator.swift +40 -25
- package/Sources/RunicCore/UsageLedger/UsageLedgerInsightsAdvisor.swift +1 -1
- package/Sources/RunicCore/UsageLedger/UsageLedgerProjectIdentity.swift +55 -16
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageCache.swift +6 -6
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageJsonl.swift +1 -1
- package/Sources/RunicCore/Vendored/CostUsage/CostUsagePricing.swift +2 -2
- package/Sources/RunicCore/Vendored/CostUsage/CostUsageScanner.swift +5 -5
- package/Tests/RunicTests/AppDelegateTests.swift +8 -3
- package/Tests/RunicTests/BrowserCookieOrderLabelTests.swift +2 -3
- package/Tests/RunicTests/CLIArgumentParsingTests.swift +3 -4
- package/Tests/RunicTests/CLICostTests.swift +6 -7
- package/Tests/RunicTests/CLIProviderSelectionTests.swift +2 -3
- package/Tests/RunicTests/CLISnapshotTests.swift +3 -4
- package/Tests/RunicTests/CLIWebFallbackTests.swift +3 -4
- package/Tests/RunicTests/ClaudeOAuthTests.swift +11 -12
- package/Tests/RunicTests/ClaudeResilienceTests.swift +3 -4
- package/Tests/RunicTests/ClaudeUsageTests.swift +20 -21
- package/Tests/RunicTests/CodexOAuthTests.swift +7 -8
- package/Tests/RunicTests/CodexUsageLogSourceTests.swift +1 -3
- package/Tests/RunicTests/CopilotTokenResolverTests.swift +5 -6
- package/Tests/RunicTests/CopilotVSCodeTokenReaderTests.swift +4 -5
- package/Tests/RunicTests/CostUsageDecodingTests.swift +15 -17
- package/Tests/RunicTests/CostUsagePricingTests.swift +5 -7
- package/Tests/RunicTests/CostUsageScannerTests.swift +10 -12
- package/Tests/RunicTests/CursorStatusProbeTests.swift +15 -16
- package/Tests/RunicTests/FactoryStatusProbeTests.swift +3 -5
- package/Tests/RunicTests/GeminiLoginAlertTests.swift +3 -4
- package/Tests/RunicTests/GeminiStatusProbeAPITests.swift +11 -11
- package/Tests/RunicTests/GeminiStatusProbePlanTests.swift +8 -8
- package/Tests/RunicTests/GeminiStatusProbeTests.swift +20 -21
- package/Tests/RunicTests/GoogleWorkspaceStatusTests.swift +2 -3
- package/Tests/RunicTests/InstallOriginTests.swift +1 -2
- package/Tests/RunicTests/KeyboardShortcutsBundleTests.swift +2 -2
- package/Tests/RunicTests/LiveAccountTests.swift +3 -3
- package/Tests/RunicTests/LoadingPatternTests.swift +3 -4
- package/Tests/RunicTests/MenuCardModelTests.swift +33 -34
- package/Tests/RunicTests/MiniMaxCookieHeaderStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxGroupIDStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/MiniMaxParsingTests.swift +4 -5
- package/Tests/RunicTests/MiniMaxTokenStoreTestSupport.swift +4 -1
- package/Tests/RunicTests/OTelGenAILedgerAdapterTests.swift +4 -6
- package/Tests/RunicTests/OpenAIDashboardBrowserCookieImporterTests.swift +1 -2
- package/Tests/RunicTests/OpenAIDashboardFetcherCreditsWaitTests.swift +5 -6
- package/Tests/RunicTests/OpenAIDashboardOffscreenHostTests.swift +2 -3
- package/Tests/RunicTests/OpenAIDashboardParserTests.swift +12 -13
- package/Tests/RunicTests/OpenAIWebAccountSwitchTests.swift +17 -6
- package/Tests/RunicTests/OpenRouterTokenStoreTestSupport.swift +52 -13
- package/Tests/RunicTests/PathBuilderTests.swift +14 -15
- package/Tests/RunicTests/ProviderFetchPipelineTests.swift +3 -4
- package/Tests/RunicTests/ProviderIconResourcesTests.swift +1 -2
- package/Tests/RunicTests/ProviderMetadataStatusLinkTests.swift +1 -2
- package/Tests/RunicTests/ProviderSettingsDescriptorTests.swift +53 -19
- package/Tests/RunicTests/ProviderToggleStoreTests.swift +10 -11
- package/Tests/RunicTests/RunicTests.swift +6 -7
- package/Tests/RunicTests/SessionQuotaNotificationLogicTests.swift +5 -6
- package/Tests/RunicTests/SettingsStoreTests.swift +149 -45
- package/Tests/RunicTests/StatusItemAnimationTests.swift +8 -3
- package/Tests/RunicTests/StatusMenuTests.swift +63 -22
- package/Tests/RunicTests/StatusProbeTests.swift +28 -29
- package/Tests/RunicTests/SubprocessRunnerTests.swift +1 -3
- package/Tests/RunicTests/SubscriptionDetectionTests.swift +8 -9
- package/Tests/RunicTests/TTYCommandRunnerTests.swift +8 -9
- package/Tests/RunicTests/TeamShowbackExporterTests.swift +2 -4
- package/Tests/RunicTests/TextParsingTests.swift +1 -2
- package/Tests/RunicTests/UsageFormatterTests.swift +15 -16
- package/Tests/RunicTests/UsageLedgerAnomalyDetectionTests.swift +12 -14
- package/Tests/RunicTests/UsageLedgerForecastTests.swift +4 -6
- package/Tests/RunicTests/UsageLedgerInsightsAdvisorTests.swift +7 -9
- package/Tests/RunicTests/UsageLedgerProjectNameTests.swift +6 -8
- package/Tests/RunicTests/UsagePaceTests.swift +5 -6
- package/Tests/RunicTests/UsagePaceTextTests.swift +8 -9
- package/Tests/RunicTests/UsagePolicyEngineTests.swift +3 -5
- package/Tests/RunicTests/WidgetSnapshotTests.swift +1 -3
- package/Tests/RunicTests/ZaiProviderTests.swift +3 -5
- package/Tests/RunicTests/ZaiTokenStoreTestSupport.swift +4 -1
- package/appcast.xml +36 -76
- package/package.json +1 -1
- package/runic.png +0 -0
- package/runic.svg +15 -0
- package/version.env +2 -2
|
@@ -26,8 +26,8 @@ public struct Team: Codable, Sendable, Identifiable {
|
|
|
26
26
|
totalQuota: Int,
|
|
27
27
|
members: [TeamMembership] = [],
|
|
28
28
|
createdAt: Date = Date(),
|
|
29
|
-
updatedAt: Date = Date()
|
|
30
|
-
|
|
29
|
+
updatedAt: Date = Date())
|
|
30
|
+
{
|
|
31
31
|
self.id = id
|
|
32
32
|
self.name = name
|
|
33
33
|
self.ownerUserID = ownerUserID
|
|
@@ -41,22 +41,22 @@ public struct Team: Codable, Sendable, Identifiable {
|
|
|
41
41
|
extension Team {
|
|
42
42
|
/// Total number of team members including owner
|
|
43
43
|
public var memberCount: Int {
|
|
44
|
-
|
|
44
|
+
self.members.count
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/// Get all admin users (owner + admins)
|
|
48
48
|
public var adminUserIDs: [String] {
|
|
49
|
-
|
|
49
|
+
self.members.filter { $0.role == .owner || $0.role == .admin }.map(\.userID)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/// Check if a user is a member of this team
|
|
53
53
|
public func hasMember(userID: String) -> Bool {
|
|
54
|
-
|
|
54
|
+
self.members.contains { $0.userID == userID }
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/// Get membership for a specific user
|
|
58
58
|
public func membership(for userID: String) -> TeamMembership? {
|
|
59
|
-
|
|
59
|
+
self.members.first { $0.userID == userID }
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/// Check if user has admin privileges
|
|
@@ -67,12 +67,12 @@ extension Team {
|
|
|
67
67
|
|
|
68
68
|
/// Calculate total quota allocated to members with individual limits
|
|
69
69
|
public var allocatedQuota: Int {
|
|
70
|
-
|
|
70
|
+
self.members.compactMap(\.quotaLimit).reduce(0, +)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/// Remaining unallocated quota
|
|
74
74
|
public var unallocatedQuota: Int {
|
|
75
|
-
|
|
75
|
+
max(0, self.totalQuota - self.allocatedQuota)
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -93,8 +93,8 @@ public struct TeamMembership: Codable, Sendable, Identifiable {
|
|
|
93
93
|
teamID: String,
|
|
94
94
|
role: TeamRole,
|
|
95
95
|
quotaLimit: Int? = nil,
|
|
96
|
-
joinedAt: Date = Date()
|
|
97
|
-
|
|
96
|
+
joinedAt: Date = Date())
|
|
97
|
+
{
|
|
98
98
|
self.id = id
|
|
99
99
|
self.userID = userID
|
|
100
100
|
self.teamID = teamID
|
|
@@ -107,27 +107,27 @@ public struct TeamMembership: Codable, Sendable, Identifiable {
|
|
|
107
107
|
extension TeamMembership {
|
|
108
108
|
/// Check if this member can manage other members
|
|
109
109
|
public var canManageMembers: Bool {
|
|
110
|
-
|
|
110
|
+
self.role == .owner || self.role == .admin
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/// Check if this member can modify team settings
|
|
114
114
|
public var canModifyTeamSettings: Bool {
|
|
115
|
-
|
|
115
|
+
self.role == .owner || self.role == .admin
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/// Check if this member can invite others
|
|
119
119
|
public var canInviteMembers: Bool {
|
|
120
|
-
|
|
120
|
+
self.role == .owner || self.role == .admin
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/// Check if this member can view usage
|
|
124
124
|
public var canViewUsage: Bool {
|
|
125
|
-
|
|
125
|
+
true // All members can view usage
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/// Check if this member can create projects
|
|
129
129
|
public var canCreateProjects: Bool {
|
|
130
|
-
|
|
130
|
+
self.role != .viewer
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -135,18 +135,18 @@ extension TeamMembership {
|
|
|
135
135
|
|
|
136
136
|
/// Defines permission levels within a team
|
|
137
137
|
public enum TeamRole: String, Codable, Sendable, CaseIterable {
|
|
138
|
-
case owner
|
|
139
|
-
case admin
|
|
140
|
-
case member
|
|
141
|
-
case viewer
|
|
138
|
+
case owner
|
|
139
|
+
case admin
|
|
140
|
+
case member
|
|
141
|
+
case viewer
|
|
142
142
|
|
|
143
143
|
/// Display name for the role
|
|
144
144
|
public var displayName: String {
|
|
145
145
|
switch self {
|
|
146
|
-
case .owner:
|
|
147
|
-
case .admin:
|
|
148
|
-
case .member:
|
|
149
|
-
case .viewer:
|
|
146
|
+
case .owner: "Owner"
|
|
147
|
+
case .admin: "Admin"
|
|
148
|
+
case .member: "Member"
|
|
149
|
+
case .viewer: "Viewer"
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -154,29 +154,29 @@ public enum TeamRole: String, Codable, Sendable, CaseIterable {
|
|
|
154
154
|
public var description: String {
|
|
155
155
|
switch self {
|
|
156
156
|
case .owner:
|
|
157
|
-
|
|
157
|
+
"Full control over team, billing, and members"
|
|
158
158
|
case .admin:
|
|
159
|
-
|
|
159
|
+
"Can manage members and team settings"
|
|
160
160
|
case .member:
|
|
161
|
-
|
|
161
|
+
"Can create and share projects within team"
|
|
162
162
|
case .viewer:
|
|
163
|
-
|
|
163
|
+
"Read-only access to team projects"
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/// Permission level (higher = more permissions)
|
|
168
168
|
public var level: Int {
|
|
169
169
|
switch self {
|
|
170
|
-
case .owner:
|
|
171
|
-
case .admin:
|
|
172
|
-
case .member:
|
|
173
|
-
case .viewer:
|
|
170
|
+
case .owner: 4
|
|
171
|
+
case .admin: 3
|
|
172
|
+
case .member: 2
|
|
173
|
+
case .viewer: 1
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
/// Check if this role can modify another role
|
|
178
178
|
public func canModify(_ other: TeamRole) -> Bool {
|
|
179
|
-
|
|
179
|
+
self.level > other.level
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -199,8 +199,8 @@ public struct ProjectOwnership: Codable, Sendable, Identifiable {
|
|
|
199
199
|
ownerUserID: String,
|
|
200
200
|
sharedWith: [String] = [],
|
|
201
201
|
accessLevel: AccessLevel = .privateAccess,
|
|
202
|
-
createdAt: Date = Date()
|
|
203
|
-
|
|
202
|
+
createdAt: Date = Date())
|
|
203
|
+
{
|
|
204
204
|
self.id = id
|
|
205
205
|
self.projectID = projectID
|
|
206
206
|
self.teamID = teamID
|
|
@@ -215,17 +215,17 @@ extension ProjectOwnership {
|
|
|
215
215
|
/// Check if a user has access to this project
|
|
216
216
|
public func hasAccess(userID: String) -> Bool {
|
|
217
217
|
// Owner always has access
|
|
218
|
-
if userID == ownerUserID {
|
|
218
|
+
if userID == self.ownerUserID {
|
|
219
219
|
return true
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// Check access level
|
|
223
|
-
switch accessLevel {
|
|
223
|
+
switch self.accessLevel {
|
|
224
224
|
case .privateAccess:
|
|
225
|
-
return sharedWith.contains(userID)
|
|
225
|
+
return self.sharedWith.contains(userID)
|
|
226
226
|
case .team:
|
|
227
227
|
// User must be in the team
|
|
228
|
-
return teamID != nil
|
|
228
|
+
return self.teamID != nil
|
|
229
229
|
case .public:
|
|
230
230
|
return true
|
|
231
231
|
}
|
|
@@ -233,24 +233,24 @@ extension ProjectOwnership {
|
|
|
233
233
|
|
|
234
234
|
/// Check if a user can edit this project
|
|
235
235
|
public func canEdit(userID: String) -> Bool {
|
|
236
|
-
|
|
236
|
+
userID == self.ownerUserID || self.sharedWith.contains(userID)
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
/// Add a user to the shared list
|
|
240
240
|
public mutating func share(with userID: String) {
|
|
241
|
-
if !sharedWith.contains(userID) {
|
|
242
|
-
sharedWith.append(userID)
|
|
241
|
+
if !self.sharedWith.contains(userID) {
|
|
242
|
+
self.sharedWith.append(userID)
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/// Remove a user from the shared list
|
|
247
247
|
public mutating func unshare(with userID: String) {
|
|
248
|
-
sharedWith.removeAll { $0 == userID }
|
|
248
|
+
self.sharedWith.removeAll { $0 == userID }
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
/// Check if project is owned by a team
|
|
252
252
|
public var isTeamOwned: Bool {
|
|
253
|
-
|
|
253
|
+
self.teamID != nil
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
@@ -259,24 +259,24 @@ extension ProjectOwnership {
|
|
|
259
259
|
/// Defines who can access a project
|
|
260
260
|
public enum AccessLevel: String, Codable, Sendable, CaseIterable {
|
|
261
261
|
case privateAccess = "private" // Only owner and explicitly shared users
|
|
262
|
-
case team
|
|
263
|
-
case `public`
|
|
262
|
+
case team // All team members
|
|
263
|
+
case `public` // Anyone with link
|
|
264
264
|
|
|
265
265
|
/// Display name for the access level
|
|
266
266
|
public var displayName: String {
|
|
267
267
|
switch self {
|
|
268
|
-
case .privateAccess:
|
|
269
|
-
case .team:
|
|
270
|
-
case .public:
|
|
268
|
+
case .privateAccess: "Private"
|
|
269
|
+
case .team: "Team"
|
|
270
|
+
case .public: "Public"
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
/// Icon representing the access level
|
|
275
275
|
public var icon: String {
|
|
276
276
|
switch self {
|
|
277
|
-
case .privateAccess:
|
|
278
|
-
case .team:
|
|
279
|
-
case .public:
|
|
277
|
+
case .privateAccess: "lock.fill"
|
|
278
|
+
case .team: "person.2.fill"
|
|
279
|
+
case .public: "globe"
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -284,11 +284,11 @@ public enum AccessLevel: String, Codable, Sendable, CaseIterable {
|
|
|
284
284
|
public var description: String {
|
|
285
285
|
switch self {
|
|
286
286
|
case .privateAccess:
|
|
287
|
-
|
|
287
|
+
"Only you and people you share with"
|
|
288
288
|
case .team:
|
|
289
|
-
|
|
289
|
+
"All team members can access"
|
|
290
290
|
case .public:
|
|
291
|
-
|
|
291
|
+
"Anyone with the link can view"
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
}
|
|
@@ -310,8 +310,8 @@ public struct TeamUsageSummary: Codable, Sendable {
|
|
|
310
310
|
totalTokens: Int = 0,
|
|
311
311
|
totalCost: Double = 0.0,
|
|
312
312
|
memberUsage: [String: MemberUsage] = [:],
|
|
313
|
-
quotaUsedPercent: Double = 0.0
|
|
314
|
-
|
|
313
|
+
quotaUsedPercent: Double = 0.0)
|
|
314
|
+
{
|
|
315
315
|
self.teamID = teamID
|
|
316
316
|
self.period = period
|
|
317
317
|
self.totalTokens = totalTokens
|
|
@@ -324,12 +324,12 @@ public struct TeamUsageSummary: Codable, Sendable {
|
|
|
324
324
|
extension TeamUsageSummary {
|
|
325
325
|
/// Get usage for a specific member
|
|
326
326
|
public func usage(for userID: String) -> MemberUsage? {
|
|
327
|
-
|
|
327
|
+
self.memberUsage[userID]
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/// Top users by token consumption
|
|
331
331
|
public func topUsers(limit: Int = 5) -> [(userID: String, usage: MemberUsage)] {
|
|
332
|
-
|
|
332
|
+
self.memberUsage
|
|
333
333
|
.sorted { $0.value.tokens > $1.value.tokens }
|
|
334
334
|
.prefix(limit)
|
|
335
335
|
.map { ($0.key, $0.value) }
|
|
@@ -337,36 +337,36 @@ extension TeamUsageSummary {
|
|
|
337
337
|
|
|
338
338
|
/// Average tokens per member
|
|
339
339
|
public var averageTokensPerMember: Int {
|
|
340
|
-
guard !memberUsage.isEmpty else { return 0 }
|
|
341
|
-
return totalTokens / memberUsage.count
|
|
340
|
+
guard !self.memberUsage.isEmpty else { return 0 }
|
|
341
|
+
return self.totalTokens / self.memberUsage.count
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
/// Average cost per member
|
|
345
345
|
public var averageCostPerMember: Double {
|
|
346
|
-
guard !memberUsage.isEmpty else { return 0.0 }
|
|
347
|
-
return totalCost / Double(memberUsage.count)
|
|
346
|
+
guard !self.memberUsage.isEmpty else { return 0.0 }
|
|
347
|
+
return self.totalCost / Double(self.memberUsage.count)
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
/// Check if team is approaching quota limit
|
|
351
351
|
public var isApproachingQuota: Bool {
|
|
352
|
-
|
|
352
|
+
self.quotaUsedPercent >= 80.0
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
/// Check if team has exceeded quota
|
|
356
356
|
public var hasExceededQuota: Bool {
|
|
357
|
-
|
|
357
|
+
self.quotaUsedPercent >= 100.0
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
/// Add or update member usage
|
|
361
361
|
public mutating func updateUsage(for userID: String, usage: MemberUsage) {
|
|
362
|
-
memberUsage[userID] = usage
|
|
363
|
-
recalculateTotals()
|
|
362
|
+
self.memberUsage[userID] = usage
|
|
363
|
+
self.recalculateTotals()
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
/// Recalculate total tokens and cost from member usage
|
|
367
367
|
private mutating func recalculateTotals() {
|
|
368
|
-
totalTokens = memberUsage.values.reduce(0) { $0 + $1.tokens }
|
|
369
|
-
totalCost = memberUsage.values.reduce(0.0) { $0 + $1.cost }
|
|
368
|
+
self.totalTokens = self.memberUsage.values.reduce(0) { $0 + $1.tokens }
|
|
369
|
+
self.totalCost = self.memberUsage.values.reduce(0.0) { $0 + $1.cost }
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -383,8 +383,8 @@ public struct MemberUsage: Codable, Sendable {
|
|
|
383
383
|
userID: String,
|
|
384
384
|
tokens: Int = 0,
|
|
385
385
|
cost: Double = 0.0,
|
|
386
|
-
requests: Int = 0
|
|
387
|
-
|
|
386
|
+
requests: Int = 0)
|
|
387
|
+
{
|
|
388
388
|
self.userID = userID
|
|
389
389
|
self.tokens = tokens
|
|
390
390
|
self.cost = cost
|
|
@@ -395,20 +395,20 @@ public struct MemberUsage: Codable, Sendable {
|
|
|
395
395
|
extension MemberUsage {
|
|
396
396
|
/// Average cost per request
|
|
397
397
|
public var averageCostPerRequest: Double {
|
|
398
|
-
guard requests > 0 else { return 0.0 }
|
|
399
|
-
return cost / Double(requests)
|
|
398
|
+
guard self.requests > 0 else { return 0.0 }
|
|
399
|
+
return self.cost / Double(self.requests)
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
/// Average tokens per request
|
|
403
403
|
public var averageTokensPerRequest: Int {
|
|
404
|
-
guard requests > 0 else { return 0 }
|
|
405
|
-
return tokens / requests
|
|
404
|
+
guard self.requests > 0 else { return 0 }
|
|
405
|
+
return self.tokens / self.requests
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
/// Check if member has quota limit and percentage used
|
|
409
409
|
public func quotaUsedPercent(limit: Int?) -> Double? {
|
|
410
|
-
guard let limit
|
|
411
|
-
return (Double(tokens) / Double(limit)) * 100.0
|
|
410
|
+
guard let limit, limit > 0 else { return nil }
|
|
411
|
+
return (Double(self.tokens) / Double(limit)) * 100.0
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
/// Combine with another usage record
|
|
@@ -440,8 +440,8 @@ public struct TeamInvitation: Codable, Sendable, Identifiable {
|
|
|
440
440
|
role: TeamRole = .member,
|
|
441
441
|
expiresAt: Date = Date().addingTimeInterval(7 * 24 * 60 * 60), // 7 days
|
|
442
442
|
status: InvitationStatus = .pending,
|
|
443
|
-
createdAt: Date = Date()
|
|
444
|
-
|
|
443
|
+
createdAt: Date = Date())
|
|
444
|
+
{
|
|
445
445
|
self.id = id
|
|
446
446
|
self.teamID = teamID
|
|
447
447
|
self.invitedEmail = invitedEmail
|
|
@@ -456,36 +456,36 @@ public struct TeamInvitation: Codable, Sendable, Identifiable {
|
|
|
456
456
|
extension TeamInvitation {
|
|
457
457
|
/// Check if invitation is still valid
|
|
458
458
|
public var isValid: Bool {
|
|
459
|
-
|
|
459
|
+
self.status == .pending && !self.isExpired
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
/// Check if invitation has expired
|
|
463
463
|
public var isExpired: Bool {
|
|
464
|
-
|
|
464
|
+
Date() > self.expiresAt
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
/// Days until expiration
|
|
468
468
|
public var daysUntilExpiration: Int {
|
|
469
|
-
let timeInterval = expiresAt.timeIntervalSince(Date())
|
|
469
|
+
let timeInterval = self.expiresAt.timeIntervalSince(Date())
|
|
470
470
|
return max(0, Int(timeInterval / (24 * 60 * 60)))
|
|
471
471
|
}
|
|
472
472
|
|
|
473
473
|
/// Accept the invitation
|
|
474
474
|
public mutating func accept() {
|
|
475
|
-
guard isValid else { return }
|
|
476
|
-
status = .accepted
|
|
475
|
+
guard self.isValid else { return }
|
|
476
|
+
self.status = .accepted
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
/// Decline the invitation
|
|
480
480
|
public mutating func decline() {
|
|
481
|
-
guard isValid else { return }
|
|
482
|
-
status = .declined
|
|
481
|
+
guard self.isValid else { return }
|
|
482
|
+
self.status = .declined
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
/// Mark as expired
|
|
486
486
|
public mutating func expire() {
|
|
487
|
-
if status == .pending {
|
|
488
|
-
status = .expired
|
|
487
|
+
if self.status == .pending {
|
|
488
|
+
self.status = .expired
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
}
|
|
@@ -494,38 +494,38 @@ extension TeamInvitation {
|
|
|
494
494
|
|
|
495
495
|
/// Status of a team invitation
|
|
496
496
|
public enum InvitationStatus: String, Codable, Sendable, CaseIterable {
|
|
497
|
-
case pending
|
|
498
|
-
case accepted
|
|
499
|
-
case declined
|
|
500
|
-
case expired
|
|
497
|
+
case pending
|
|
498
|
+
case accepted
|
|
499
|
+
case declined
|
|
500
|
+
case expired
|
|
501
501
|
|
|
502
502
|
/// Display name for the status
|
|
503
503
|
public var displayName: String {
|
|
504
504
|
switch self {
|
|
505
|
-
case .pending:
|
|
506
|
-
case .accepted:
|
|
507
|
-
case .declined:
|
|
508
|
-
case .expired:
|
|
505
|
+
case .pending: "Pending"
|
|
506
|
+
case .accepted: "Accepted"
|
|
507
|
+
case .declined: "Declined"
|
|
508
|
+
case .expired: "Expired"
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
|
|
512
512
|
/// Icon representing the status
|
|
513
513
|
public var icon: String {
|
|
514
514
|
switch self {
|
|
515
|
-
case .pending:
|
|
516
|
-
case .accepted:
|
|
517
|
-
case .declined:
|
|
518
|
-
case .expired:
|
|
515
|
+
case .pending: "clock.fill"
|
|
516
|
+
case .accepted: "checkmark.circle.fill"
|
|
517
|
+
case .declined: "xmark.circle.fill"
|
|
518
|
+
case .expired: "hourglass.bottomhalf.filled"
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
521
|
|
|
522
522
|
/// Color associated with status
|
|
523
523
|
public var colorName: String {
|
|
524
524
|
switch self {
|
|
525
|
-
case .pending:
|
|
526
|
-
case .accepted:
|
|
527
|
-
case .declined:
|
|
528
|
-
case .expired:
|
|
525
|
+
case .pending: "orange"
|
|
526
|
+
case .accepted: "green"
|
|
527
|
+
case .declined: "red"
|
|
528
|
+
case .expired: "gray"
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
}
|
|
@@ -545,11 +545,12 @@ public struct TeamQuotaAllocation: Sendable {
|
|
|
545
545
|
/// Get remaining quota for a specific member
|
|
546
546
|
public func remainingQuota(for userID: String) -> Int? {
|
|
547
547
|
guard let membership = team.membership(for: userID),
|
|
548
|
-
let limit = membership.quotaLimit
|
|
548
|
+
let limit = membership.quotaLimit
|
|
549
|
+
else {
|
|
549
550
|
return nil
|
|
550
551
|
}
|
|
551
552
|
|
|
552
|
-
let used = usage.usage(for: userID)?.tokens ?? 0
|
|
553
|
+
let used = self.usage.usage(for: userID)?.tokens ?? 0
|
|
553
554
|
return max(0, limit - used)
|
|
554
555
|
}
|
|
555
556
|
|
|
@@ -557,25 +558,25 @@ public struct TeamQuotaAllocation: Sendable {
|
|
|
557
558
|
public func canUseTokens(_ tokens: Int, userID: String) -> Bool {
|
|
558
559
|
guard let remaining = remainingQuota(for: userID) else {
|
|
559
560
|
// No individual limit, check team limit
|
|
560
|
-
return usage.totalTokens + tokens <= team.totalQuota
|
|
561
|
+
return self.usage.totalTokens + tokens <= self.team.totalQuota
|
|
561
562
|
}
|
|
562
563
|
return remaining >= tokens
|
|
563
564
|
}
|
|
564
565
|
|
|
565
566
|
/// Get members who have exceeded their quota
|
|
566
567
|
public func membersExceedingQuota() -> [TeamMembership] {
|
|
567
|
-
|
|
568
|
+
self.team.members.filter { membership in
|
|
568
569
|
guard let limit = membership.quotaLimit else { return false }
|
|
569
|
-
let used = usage.usage(for: membership.userID)?.tokens ?? 0
|
|
570
|
+
let used = self.usage.usage(for: membership.userID)?.tokens ?? 0
|
|
570
571
|
return used > limit
|
|
571
572
|
}
|
|
572
573
|
}
|
|
573
574
|
|
|
574
575
|
/// Get members approaching their quota (>80%)
|
|
575
576
|
public func membersApproachingQuota() -> [TeamMembership] {
|
|
576
|
-
|
|
577
|
+
self.team.members.filter { membership in
|
|
577
578
|
guard let limit = membership.quotaLimit else { return false }
|
|
578
|
-
let used = usage.usage(for: membership.userID)?.tokens ?? 0
|
|
579
|
+
let used = self.usage.usage(for: membership.userID)?.tokens ?? 0
|
|
579
580
|
let percent = (Double(used) / Double(limit)) * 100.0
|
|
580
581
|
return percent >= 80.0 && percent < 100.0
|
|
581
582
|
}
|