@oneuptime/common 14.0.8 → 14.0.9
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/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/RunnerJob.ts +75 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +207 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/TelemetryIngest.ts +41 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/RunnerJobService.ts +241 -104
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +178 -92
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1202 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import {
|
|
3
|
+
AI_AGENT_FIXES_OFF_HINT,
|
|
4
|
+
AI_AGENT_GONE_TEXT,
|
|
5
|
+
AI_AGENT_LEGACY_RUNNER_OFFLINE_TEXT,
|
|
6
|
+
AI_AGENT_LEGACY_RUNNER_TEXT,
|
|
7
|
+
AI_AGENT_NOT_INSTALLED_TEXT,
|
|
8
|
+
AI_AGENT_OTHER_RELEASE_TEXT,
|
|
9
|
+
AI_AGENT_PAGE_SUBTITLE,
|
|
10
|
+
AI_AGENT_PAGE_TITLE,
|
|
11
|
+
AI_AGENT_READY_TEXT,
|
|
12
|
+
AI_AGENT_SIGNED_OFF_TEXT,
|
|
13
|
+
AI_AGENT_SILENT_TEXT,
|
|
14
|
+
AI_AGENT_STATUS_POLL_INTERVAL_MS,
|
|
15
|
+
AI_AGENT_UPGRADE_CHART_TEXT,
|
|
16
|
+
ASK_PROJECT_ADMIN_TEXT,
|
|
17
|
+
AiAgentCardState,
|
|
18
|
+
AiAgentOfflineReason,
|
|
19
|
+
CHOICE_GAP_CODES,
|
|
20
|
+
REFUSED_REGISTRATION_WARNING_WINDOW_MS,
|
|
21
|
+
canSwitchToAiAgent,
|
|
22
|
+
describeAiAgentNodeOperations,
|
|
23
|
+
describeAiAgentWriteAccess,
|
|
24
|
+
getAiAgentCardCommand,
|
|
25
|
+
getAiAgentCardState,
|
|
26
|
+
getAiAgentGapAction,
|
|
27
|
+
getAiAgentMetaParts,
|
|
28
|
+
getAiAgentOfflineReason,
|
|
29
|
+
getAiAgentOverviewState,
|
|
30
|
+
getAiAgentPodNamespace,
|
|
31
|
+
getAiAgentStateSentence,
|
|
32
|
+
getAiAgentStatusPill,
|
|
33
|
+
getAiAgentSummary,
|
|
34
|
+
getAttentionGaps,
|
|
35
|
+
getAutomaticInvestigation,
|
|
36
|
+
getAutomaticInvestigationConfirmation,
|
|
37
|
+
getAutomaticInvestigationLine,
|
|
38
|
+
getAutomaticInvestigationTurnOnChanges,
|
|
39
|
+
getRefusedRegistrationWarning,
|
|
40
|
+
isAdvancedRunnerTarget,
|
|
41
|
+
isInClusterTarget,
|
|
42
|
+
isLegacyRunnerTarget,
|
|
43
|
+
parseStatus,
|
|
44
|
+
shouldShowWriteAccessCommands,
|
|
45
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/KubernetesAiAgentStatus";
|
|
46
|
+
import { KUBERNETES_AGENT_HELM_NAMESPACE } from "../../../../App/FeatureSet/Dashboard/src/Pages/Kubernetes/Utils/DocumentationMarkdown";
|
|
47
|
+
import {
|
|
48
|
+
KubernetesAiAccessGap,
|
|
49
|
+
KubernetesAiAccessGapCode,
|
|
50
|
+
KubernetesAiAgentSummary,
|
|
51
|
+
KubernetesAiRemediationMode,
|
|
52
|
+
KubernetesClusterAiAccessStatus,
|
|
53
|
+
KUBERNETES_AI_AGENT_DISPLAY_NAME,
|
|
54
|
+
} from "../../../Types/Kubernetes/KubernetesClusterAiAccess";
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* The pure reading of the server's access status behind the cluster's AI
|
|
58
|
+
* agent page and the Overview's "AI agent" card: which of its states the
|
|
59
|
+
* card is in (the agent connected or offline, not installed, the previous
|
|
60
|
+
* in-cluster Runner, an advanced Runner), the words and the one command for
|
|
61
|
+
* each, the meta line, the refused-registration warning, which server gaps
|
|
62
|
+
* the "Needs attention" card lists and what each row offers, when the
|
|
63
|
+
* write-access commands show, and the automatic-investigation footer.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
const AGENT_ID: string = "99999999-0000-4000-8000-000000000009";
|
|
67
|
+
const LEGACY_RUNNER_ID: string = "55555555-0000-4000-8000-000000000005";
|
|
68
|
+
const ADVANCED_RUNNER_ID: string = "55555555-0000-4000-8000-000000000006";
|
|
69
|
+
|
|
70
|
+
const NOW: Date = new Date();
|
|
71
|
+
|
|
72
|
+
function minutesAgo(minutes: number): string {
|
|
73
|
+
return new Date(NOW.getTime() - minutes * 60 * 1000).toISOString();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function makeAgent(
|
|
77
|
+
overrides: Partial<KubernetesAiAgentSummary> = {},
|
|
78
|
+
): KubernetesAiAgentSummary {
|
|
79
|
+
return {
|
|
80
|
+
id: AGENT_ID,
|
|
81
|
+
isOnline: true,
|
|
82
|
+
connectionStatus: "connected",
|
|
83
|
+
lastAliveAt: minutesAgo(1),
|
|
84
|
+
lastRegisteredAt: minutesAgo(60),
|
|
85
|
+
agentVersion: "14.1.0",
|
|
86
|
+
posture: {
|
|
87
|
+
clusterIdentifier: "prod-east",
|
|
88
|
+
inCluster: true,
|
|
89
|
+
allowWrites: false,
|
|
90
|
+
writeNamespaces: [],
|
|
91
|
+
podNamespace: "monitoring",
|
|
92
|
+
kubectlVersion: "v1.31.2",
|
|
93
|
+
allowNodeOperations: false,
|
|
94
|
+
},
|
|
95
|
+
...overrides,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* The three ways an agent goes offline, as the server's isOnline rule
|
|
101
|
+
* produces them: heartbeats stopped without a sign-off, a sign-off or reset
|
|
102
|
+
* moments ago (a helm upgrade, "Reset agent"), and a sign-off it never came
|
|
103
|
+
* back from.
|
|
104
|
+
*/
|
|
105
|
+
function silentAgent(
|
|
106
|
+
overrides: Partial<KubernetesAiAgentSummary> = {},
|
|
107
|
+
): KubernetesAiAgentSummary {
|
|
108
|
+
return makeAgent({
|
|
109
|
+
isOnline: false,
|
|
110
|
+
lastAliveAt: minutesAgo(12),
|
|
111
|
+
...overrides,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function signedOffAgent(
|
|
116
|
+
overrides: Partial<KubernetesAiAgentSummary> = {},
|
|
117
|
+
): KubernetesAiAgentSummary {
|
|
118
|
+
return makeAgent({
|
|
119
|
+
isOnline: false,
|
|
120
|
+
connectionStatus: "disconnected",
|
|
121
|
+
lastAliveAt: minutesAgo(0.25),
|
|
122
|
+
...overrides,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function goneAgent(
|
|
127
|
+
overrides: Partial<KubernetesAiAgentSummary> = {},
|
|
128
|
+
): KubernetesAiAgentSummary {
|
|
129
|
+
return makeAgent({
|
|
130
|
+
isOnline: false,
|
|
131
|
+
connectionStatus: "disconnected",
|
|
132
|
+
lastAliveAt: minutesAgo(45),
|
|
133
|
+
...overrides,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// A status whose resolved target is the cluster's Kubernetes AI agent.
|
|
138
|
+
function agentStatus(
|
|
139
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
140
|
+
agent: KubernetesAiAgentSummary = makeAgent(),
|
|
141
|
+
): KubernetesClusterAiAccessStatus {
|
|
142
|
+
return {
|
|
143
|
+
clusterId: "c1",
|
|
144
|
+
clusterName: "prod-east",
|
|
145
|
+
clusterIdentifier: "prod-east",
|
|
146
|
+
runner: {
|
|
147
|
+
id: agent.id,
|
|
148
|
+
name: KUBERNETES_AI_AGENT_DISPLAY_NAME,
|
|
149
|
+
kind: "ai_agent",
|
|
150
|
+
isOnline: agent.isOnline,
|
|
151
|
+
lastAliveAt: agent.lastAliveAt,
|
|
152
|
+
canRunAiCommands: true,
|
|
153
|
+
posture: agent.posture,
|
|
154
|
+
},
|
|
155
|
+
accessMethod: "in_cluster",
|
|
156
|
+
aiAgent: agent,
|
|
157
|
+
automaticInvestigation: { incidents: false, alerts: false },
|
|
158
|
+
kubectlAllowlist: [],
|
|
159
|
+
isInvestigationEnabled: true,
|
|
160
|
+
isInvestigationReady: true,
|
|
161
|
+
remediationMode: KubernetesAiRemediationMode.Disabled,
|
|
162
|
+
isRemediationReady: false,
|
|
163
|
+
gaps: [],
|
|
164
|
+
evaluatedAt: NOW.toISOString(),
|
|
165
|
+
...overrides,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Nothing reaches the cluster and no agent ever registered.
|
|
170
|
+
function notInstalledStatus(
|
|
171
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
172
|
+
): KubernetesClusterAiAccessStatus {
|
|
173
|
+
return agentStatus({
|
|
174
|
+
runner: null,
|
|
175
|
+
aiAgent: null,
|
|
176
|
+
accessMethod: "none",
|
|
177
|
+
isInvestigationReady: false,
|
|
178
|
+
gaps: [gap("ai_agent_not_connected", "both")],
|
|
179
|
+
...overrides,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Still on the chart's previous in-cluster Runner (a 14.0.x install).
|
|
184
|
+
function legacyStatus(
|
|
185
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
186
|
+
isOnline: boolean = true,
|
|
187
|
+
): KubernetesClusterAiAccessStatus {
|
|
188
|
+
return agentStatus({
|
|
189
|
+
runner: {
|
|
190
|
+
id: LEGACY_RUNNER_ID,
|
|
191
|
+
name: "kubernetes-agent/prod-east",
|
|
192
|
+
isOnline,
|
|
193
|
+
lastAliveAt: minutesAgo(2),
|
|
194
|
+
canRunAiCommands: true,
|
|
195
|
+
posture: {
|
|
196
|
+
clusterIdentifier: "prod-east",
|
|
197
|
+
inCluster: true,
|
|
198
|
+
allowWrites: false,
|
|
199
|
+
kubectlVersion: "v1.30.0",
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
aiAgent: null,
|
|
203
|
+
...overrides,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Bound to a Runner outside the chart, with a Kubernetes credential.
|
|
208
|
+
function advancedStatus(
|
|
209
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
210
|
+
isOnline: boolean = true,
|
|
211
|
+
): KubernetesClusterAiAccessStatus {
|
|
212
|
+
return agentStatus({
|
|
213
|
+
runner: {
|
|
214
|
+
id: ADVANCED_RUNNER_ID,
|
|
215
|
+
name: "ops-runner",
|
|
216
|
+
kind: "runner",
|
|
217
|
+
isOnline,
|
|
218
|
+
lastAliveAt: minutesAgo(3),
|
|
219
|
+
canRunAiCommands: true,
|
|
220
|
+
posture: { inCluster: false, kubectlVersion: "v1.29.1" },
|
|
221
|
+
},
|
|
222
|
+
accessMethod: "credential",
|
|
223
|
+
credentialId: "cred-1",
|
|
224
|
+
credentialName: "prod token",
|
|
225
|
+
...overrides,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function gap(
|
|
230
|
+
code: KubernetesAiAccessGapCode,
|
|
231
|
+
blocks: KubernetesAiAccessGap["blocks"] = "both",
|
|
232
|
+
): KubernetesAiAccessGap {
|
|
233
|
+
return {
|
|
234
|
+
code,
|
|
235
|
+
title: `title of ${code}`,
|
|
236
|
+
description: `description of ${code}`,
|
|
237
|
+
nextStep: `next step for ${code}`,
|
|
238
|
+
blocks,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
describe("parseStatus", () => {
|
|
243
|
+
test("accepts the status shape the API returns and nothing looser", () => {
|
|
244
|
+
expect(parseStatus(agentStatus())).toEqual(agentStatus());
|
|
245
|
+
expect(parseStatus(null)).toBeNull();
|
|
246
|
+
expect(parseStatus(undefined)).toBeNull();
|
|
247
|
+
expect(parseStatus("ready")).toBeNull();
|
|
248
|
+
expect(parseStatus([agentStatus()])).toBeNull();
|
|
249
|
+
expect(parseStatus({ ...agentStatus(), clusterId: 7 })).toBeNull();
|
|
250
|
+
expect(parseStatus({ ...agentStatus(), gaps: null })).toBeNull();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("a status from a server without the new fields still parses", () => {
|
|
254
|
+
const older: Partial<KubernetesClusterAiAccessStatus> = agentStatus();
|
|
255
|
+
delete older.aiAgent;
|
|
256
|
+
delete older.automaticInvestigation;
|
|
257
|
+
const parsed: KubernetesClusterAiAccessStatus | null = parseStatus(older);
|
|
258
|
+
expect(parsed).not.toBeNull();
|
|
259
|
+
// A missing aiAgent reads as null — never as a third state.
|
|
260
|
+
expect(getAiAgentSummary(parsed!)).toBeNull();
|
|
261
|
+
expect(getAutomaticInvestigation(parsed!)).toBeNull();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("polls every 30 seconds, like the page always did", () => {
|
|
265
|
+
expect(AI_AGENT_STATUS_POLL_INTERVAL_MS).toBe(30_000);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("the card's state", () => {
|
|
270
|
+
const CASES: Array<{
|
|
271
|
+
name: string;
|
|
272
|
+
status: KubernetesClusterAiAccessStatus;
|
|
273
|
+
state: AiAgentCardState;
|
|
274
|
+
}> = [
|
|
275
|
+
{ name: "the agent, online", status: agentStatus(), state: "connected" },
|
|
276
|
+
{
|
|
277
|
+
name: "the agent, offline",
|
|
278
|
+
status: agentStatus({}, makeAgent({ isOnline: false })),
|
|
279
|
+
state: "offline",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "nothing installed",
|
|
283
|
+
status: notInstalledStatus(),
|
|
284
|
+
state: "not_installed",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "the previous in-cluster Runner, online",
|
|
288
|
+
status: legacyStatus(),
|
|
289
|
+
state: "legacy_runner",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "the previous in-cluster Runner, offline",
|
|
293
|
+
status: legacyStatus({}, false),
|
|
294
|
+
state: "legacy_runner_offline",
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: "an advanced Runner, online",
|
|
298
|
+
status: advancedStatus(),
|
|
299
|
+
state: "advanced_runner",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "an advanced Runner, offline",
|
|
303
|
+
status: advancedStatus({}, false),
|
|
304
|
+
state: "advanced_runner_offline",
|
|
305
|
+
},
|
|
306
|
+
// Defensive: a row but no target reads as the agent's own state.
|
|
307
|
+
{
|
|
308
|
+
name: "an agent row without a target",
|
|
309
|
+
status: agentStatus({ runner: null }, makeAgent({ isOnline: false })),
|
|
310
|
+
state: "offline",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "an online agent row without a target",
|
|
314
|
+
status: agentStatus({ runner: null }),
|
|
315
|
+
state: "connected",
|
|
316
|
+
},
|
|
317
|
+
];
|
|
318
|
+
|
|
319
|
+
for (const testCase of CASES) {
|
|
320
|
+
test(testCase.name, () => {
|
|
321
|
+
expect(getAiAgentCardState(testCase.status)).toBe(testCase.state);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/*
|
|
326
|
+
* The legacy Runner is told apart from an advanced one the server's way:
|
|
327
|
+
* the name marker, an agent posture, or the in-cluster access method —
|
|
328
|
+
* never by the kind alone (both are "runner").
|
|
329
|
+
*/
|
|
330
|
+
test("the previous in-cluster Runner is recognised by name, posture or access method", () => {
|
|
331
|
+
expect(isLegacyRunnerTarget(legacyStatus())).toBe(true);
|
|
332
|
+
// Renamed row, agent posture: still the chart's Runner.
|
|
333
|
+
expect(
|
|
334
|
+
isLegacyRunnerTarget(
|
|
335
|
+
legacyStatus({
|
|
336
|
+
accessMethod: "none",
|
|
337
|
+
runner: {
|
|
338
|
+
...legacyStatus().runner!,
|
|
339
|
+
name: "east-kubectl",
|
|
340
|
+
},
|
|
341
|
+
}),
|
|
342
|
+
),
|
|
343
|
+
).toBe(true);
|
|
344
|
+
// Upper-case name marker, no posture.
|
|
345
|
+
expect(
|
|
346
|
+
isLegacyRunnerTarget(
|
|
347
|
+
legacyStatus({
|
|
348
|
+
accessMethod: "none",
|
|
349
|
+
runner: {
|
|
350
|
+
...legacyStatus().runner!,
|
|
351
|
+
name: "KUBERNETES-AGENT/prod-east",
|
|
352
|
+
posture: undefined,
|
|
353
|
+
},
|
|
354
|
+
}),
|
|
355
|
+
),
|
|
356
|
+
).toBe(true);
|
|
357
|
+
// Negative controls.
|
|
358
|
+
expect(isLegacyRunnerTarget(advancedStatus())).toBe(false);
|
|
359
|
+
expect(isLegacyRunnerTarget(agentStatus())).toBe(false);
|
|
360
|
+
expect(isLegacyRunnerTarget(notInstalledStatus())).toBe(false);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("an advanced Runner is a Runner target that is not the chart's", () => {
|
|
364
|
+
expect(isAdvancedRunnerTarget(advancedStatus())).toBe(true);
|
|
365
|
+
// A Runner with no credential (credential_missing) is still advanced.
|
|
366
|
+
expect(
|
|
367
|
+
isAdvancedRunnerTarget(
|
|
368
|
+
advancedStatus({ accessMethod: "none", credentialName: undefined }),
|
|
369
|
+
),
|
|
370
|
+
).toBe(true);
|
|
371
|
+
// A summary without `kind` is a Runner (the field's documented default).
|
|
372
|
+
const withoutKind: KubernetesClusterAiAccessStatus = advancedStatus();
|
|
373
|
+
delete withoutKind.runner!.kind;
|
|
374
|
+
expect(isAdvancedRunnerTarget(withoutKind)).toBe(true);
|
|
375
|
+
|
|
376
|
+
for (const status of [
|
|
377
|
+
agentStatus(),
|
|
378
|
+
legacyStatus(),
|
|
379
|
+
notInstalledStatus(),
|
|
380
|
+
]) {
|
|
381
|
+
expect(isAdvancedRunnerTarget(status)).toBe(false);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test("the agent and the previous Runner run kubectl in the cluster; an advanced Runner does not", () => {
|
|
386
|
+
expect(isInClusterTarget(agentStatus())).toBe(true);
|
|
387
|
+
expect(isInClusterTarget(legacyStatus())).toBe(true);
|
|
388
|
+
expect(isInClusterTarget(advancedStatus())).toBe(false);
|
|
389
|
+
expect(isInClusterTarget(notInstalledStatus())).toBe(false);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("the pill and the sentence", () => {
|
|
394
|
+
test("say each state plainly", () => {
|
|
395
|
+
expect(getAiAgentStatusPill(agentStatus())).toEqual({
|
|
396
|
+
text: "Connected",
|
|
397
|
+
tone: "success",
|
|
398
|
+
});
|
|
399
|
+
expect(
|
|
400
|
+
getAiAgentStatusPill(agentStatus({}, makeAgent({ isOnline: false }))),
|
|
401
|
+
).toEqual({ text: "Offline", tone: "danger" });
|
|
402
|
+
expect(getAiAgentStatusPill(notInstalledStatus())).toEqual({
|
|
403
|
+
text: "Not installed",
|
|
404
|
+
tone: "neutral",
|
|
405
|
+
});
|
|
406
|
+
expect(getAiAgentStatusPill(legacyStatus())).toEqual({
|
|
407
|
+
text: "Connected through the previous in-cluster Runner",
|
|
408
|
+
tone: "success",
|
|
409
|
+
});
|
|
410
|
+
expect(getAiAgentStatusPill(legacyStatus({}, false))).toEqual({
|
|
411
|
+
text: "Offline",
|
|
412
|
+
tone: "danger",
|
|
413
|
+
});
|
|
414
|
+
expect(getAiAgentStatusPill(advancedStatus())).toEqual({
|
|
415
|
+
text: "Connected through Runner ops-runner (advanced)",
|
|
416
|
+
tone: "success",
|
|
417
|
+
});
|
|
418
|
+
expect(getAiAgentStatusPill(advancedStatus({}, false))).toEqual({
|
|
419
|
+
text: "Offline",
|
|
420
|
+
tone: "danger",
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("the not-installed card says what installing does, in the product owner's words", () => {
|
|
425
|
+
expect(getAiAgentStateSentence(notInstalledStatus())).toBe(
|
|
426
|
+
"Install the AI agent — it runs in your cluster, read-only, using your Kubernetes agent's key. This page updates within a minute.",
|
|
427
|
+
);
|
|
428
|
+
expect(AI_AGENT_NOT_INSTALLED_TEXT).toBe(
|
|
429
|
+
getAiAgentStateSentence(notInstalledStatus()),
|
|
430
|
+
);
|
|
431
|
+
expect(AI_AGENT_OTHER_RELEASE_TEXT).toBe(
|
|
432
|
+
"Installed under another release or namespace? Use yours.",
|
|
433
|
+
);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test("the previous in-cluster Runner works today and upgrading carries the settings over", () => {
|
|
437
|
+
expect(getAiAgentStateSentence(legacyStatus())).toBe(
|
|
438
|
+
AI_AGENT_LEGACY_RUNNER_TEXT,
|
|
439
|
+
);
|
|
440
|
+
expect(AI_AGENT_LEGACY_RUNNER_TEXT).toBe(
|
|
441
|
+
"Works today. Upgrade the Kubernetes agent chart to switch to the new AI agent — your settings carry over.",
|
|
442
|
+
);
|
|
443
|
+
expect(AI_AGENT_LEGACY_RUNNER_TEXT).toBe(
|
|
444
|
+
`Works today. ${AI_AGENT_UPGRADE_CHART_TEXT}`,
|
|
445
|
+
);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
/*
|
|
449
|
+
* An offline Runner does not work today: the sentence said "is offline.
|
|
450
|
+
* Works today." in one breath. It keeps only the way forward.
|
|
451
|
+
*/
|
|
452
|
+
test("an offline previous Runner is not said to work today", () => {
|
|
453
|
+
const sentence: string = getAiAgentStateSentence(legacyStatus({}, false));
|
|
454
|
+
expect(sentence).toBe(
|
|
455
|
+
"The previous in-cluster Runner is offline. Upgrade the Kubernetes agent chart to switch to the new AI agent — your settings carry over.",
|
|
456
|
+
);
|
|
457
|
+
expect(sentence).toBe(AI_AGENT_LEGACY_RUNNER_OFFLINE_TEXT);
|
|
458
|
+
expect(sentence).not.toContain("Works today");
|
|
459
|
+
// Online, it still does.
|
|
460
|
+
expect(getAiAgentStateSentence(legacyStatus())).toContain("Works today");
|
|
461
|
+
// Offline or not, the same single command moves it to the agent.
|
|
462
|
+
expect(getAiAgentCardCommand(legacyStatus({}, false))).toBe("install");
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test("an advanced binding is display-only: which Runner, which credential", () => {
|
|
466
|
+
expect(getAiAgentStateSentence(advancedStatus())).toBe(
|
|
467
|
+
'Reached through Runner "ops-runner" with credential "prod token".',
|
|
468
|
+
);
|
|
469
|
+
expect(
|
|
470
|
+
getAiAgentStateSentence(advancedStatus({ credentialName: undefined })),
|
|
471
|
+
).toBe('Reached through Runner "ops-runner".');
|
|
472
|
+
expect(getAiAgentStateSentence(advancedStatus({}, false))).toBe(
|
|
473
|
+
'Reached through Runner "ops-runner" with credential "prod token". The Runner is offline.',
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test("the agent's own states", () => {
|
|
478
|
+
expect(getAiAgentStateSentence(agentStatus())).toBe(
|
|
479
|
+
"The AI agent is running in this cluster.",
|
|
480
|
+
);
|
|
481
|
+
expect(getAiAgentStateSentence(agentStatus({}, silentAgent()))).toBe(
|
|
482
|
+
"The AI agent has not checked in for over 5 minutes. Check its pod:",
|
|
483
|
+
);
|
|
484
|
+
expect(getAiAgentStateSentence(agentStatus({}, signedOffAgent()))).toBe(
|
|
485
|
+
"The AI agent signed off or was reset. It reconnects on its own within a few minutes. If it does not, check its pod:",
|
|
486
|
+
);
|
|
487
|
+
expect(getAiAgentStateSentence(agentStatus({}, goneAgent()))).toBe(
|
|
488
|
+
"The AI agent disconnected and has not come back. Check its pod:",
|
|
489
|
+
);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("the page's heading matches the AI Insights page's", () => {
|
|
493
|
+
expect(AI_AGENT_PAGE_TITLE).toBe("AI agent");
|
|
494
|
+
expect(AI_AGENT_PAGE_SUBTITLE).toBe(
|
|
495
|
+
"Whether OneUptime AI can reach this cluster, and what it may do there.",
|
|
496
|
+
);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test("the ready line is the product owner's words", () => {
|
|
500
|
+
expect(AI_AGENT_READY_TEXT).toBe(
|
|
501
|
+
"Ready — AI will inspect this cluster with read-only kubectl when it investigates an incident or alert here.",
|
|
502
|
+
);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
/*
|
|
507
|
+
* The server calls the agent offline for two different reasons
|
|
508
|
+
* (KubernetesAiAgentService.isOnline): it signed off or was reset
|
|
509
|
+
* (connectionStatus "disconnected"), or its heartbeats went quiet for over
|
|
510
|
+
* the alive window. The sentence used to blame the heartbeats every time,
|
|
511
|
+
* so right after "Reset agent" or a helm upgrade the card said "has not
|
|
512
|
+
* checked in for over 5 minutes" above a meta line reading "last seen a
|
|
513
|
+
* few seconds ago".
|
|
514
|
+
*/
|
|
515
|
+
describe("why the agent is offline", () => {
|
|
516
|
+
const NOW_DATE: Date = new Date(NOW.getTime());
|
|
517
|
+
|
|
518
|
+
const CASES: Array<{
|
|
519
|
+
name: string;
|
|
520
|
+
agent: KubernetesAiAgentSummary;
|
|
521
|
+
reason: AiAgentOfflineReason;
|
|
522
|
+
}> = [
|
|
523
|
+
{
|
|
524
|
+
name: "heartbeats stopped without a sign-off",
|
|
525
|
+
agent: silentAgent(),
|
|
526
|
+
reason: "silent",
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: "connected but never heard from",
|
|
530
|
+
agent: silentAgent({ lastAliveAt: undefined }),
|
|
531
|
+
reason: "silent",
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "a helm upgrade's pod signed off seconds ago",
|
|
535
|
+
agent: signedOffAgent(),
|
|
536
|
+
reason: "signed_off",
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: "an admin reset it a minute ago",
|
|
540
|
+
agent: signedOffAgent({ lastAliveAt: minutesAgo(1) }),
|
|
541
|
+
reason: "signed_off",
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
name: "signed off exactly at the edge of the alive window",
|
|
545
|
+
agent: signedOffAgent({ lastAliveAt: minutesAgo(5) }),
|
|
546
|
+
reason: "signed_off",
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "signed off just past the alive window",
|
|
550
|
+
agent: signedOffAgent({
|
|
551
|
+
lastAliveAt: new Date(
|
|
552
|
+
NOW.getTime() - 5 * 60 * 1000 - 1000,
|
|
553
|
+
).toISOString(),
|
|
554
|
+
}),
|
|
555
|
+
reason: "gone",
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
name: "a heartbeat stamped slightly ahead by another server's clock",
|
|
559
|
+
agent: signedOffAgent({
|
|
560
|
+
lastAliveAt: new Date(NOW.getTime() + 20 * 1000).toISOString(),
|
|
561
|
+
}),
|
|
562
|
+
reason: "signed_off",
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
name: "signed off long ago and never came back",
|
|
566
|
+
agent: goneAgent(),
|
|
567
|
+
reason: "gone",
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "disconnected without a last-seen time",
|
|
571
|
+
agent: goneAgent({ lastAliveAt: undefined }),
|
|
572
|
+
reason: "gone",
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "disconnected with an unreadable last-seen time",
|
|
576
|
+
agent: goneAgent({ lastAliveAt: "not a date" }),
|
|
577
|
+
reason: "gone",
|
|
578
|
+
},
|
|
579
|
+
];
|
|
580
|
+
|
|
581
|
+
for (const testCase of CASES) {
|
|
582
|
+
test(testCase.name, () => {
|
|
583
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus(
|
|
584
|
+
{},
|
|
585
|
+
testCase.agent,
|
|
586
|
+
);
|
|
587
|
+
expect(getAiAgentCardState(status)).toBe("offline");
|
|
588
|
+
expect(getAiAgentOfflineReason(status, NOW_DATE)).toBe(testCase.reason);
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
test("each reason has its own sentence, and every one hands over to the logs command", () => {
|
|
593
|
+
const sentences: Record<AiAgentOfflineReason, string> = {
|
|
594
|
+
silent: AI_AGENT_SILENT_TEXT,
|
|
595
|
+
signed_off: AI_AGENT_SIGNED_OFF_TEXT,
|
|
596
|
+
gone: AI_AGENT_GONE_TEXT,
|
|
597
|
+
};
|
|
598
|
+
for (const [agent, reason] of [
|
|
599
|
+
[silentAgent(), "silent"],
|
|
600
|
+
[signedOffAgent(), "signed_off"],
|
|
601
|
+
[goneAgent(), "gone"],
|
|
602
|
+
] as Array<[KubernetesAiAgentSummary, AiAgentOfflineReason]>) {
|
|
603
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus({}, agent);
|
|
604
|
+
expect(getAiAgentStateSentence(status, NOW_DATE)).toBe(sentences[reason]);
|
|
605
|
+
expect(getAiAgentStateSentence(status, NOW_DATE)).toMatch(
|
|
606
|
+
/check its pod:$/i,
|
|
607
|
+
);
|
|
608
|
+
expect(getAiAgentCardCommand(status)).toBe("logs");
|
|
609
|
+
}
|
|
610
|
+
expect(new Set(Object.values(sentences)).size).toBe(3);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
/*
|
|
614
|
+
* The regression itself: whenever the meta line can say the agent was
|
|
615
|
+
* seen within the alive window, the sentence must not say it has been
|
|
616
|
+
* silent for longer than that.
|
|
617
|
+
*/
|
|
618
|
+
test("a recent sign-off never claims five silent minutes above 'last seen seconds ago'", () => {
|
|
619
|
+
for (const agent of [
|
|
620
|
+
signedOffAgent(),
|
|
621
|
+
signedOffAgent({ lastAliveAt: minutesAgo(1) }),
|
|
622
|
+
signedOffAgent({ lastAliveAt: minutesAgo(4) }),
|
|
623
|
+
]) {
|
|
624
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus({}, agent);
|
|
625
|
+
expect(getAiAgentStateSentence(status, NOW_DATE)).not.toContain(
|
|
626
|
+
"has not checked in",
|
|
627
|
+
);
|
|
628
|
+
expect(getAiAgentMetaParts(status)[0]).toMatch(/^last seen /);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test("the same sign-off becomes 'has not come back' once the alive window passes", () => {
|
|
633
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus(
|
|
634
|
+
{},
|
|
635
|
+
signedOffAgent({ lastAliveAt: minutesAgo(1) }),
|
|
636
|
+
);
|
|
637
|
+
expect(getAiAgentOfflineReason(status, NOW_DATE)).toBe("signed_off");
|
|
638
|
+
expect(
|
|
639
|
+
getAiAgentOfflineReason(status, new Date(NOW.getTime() + 10 * 60 * 1000)),
|
|
640
|
+
).toBe("gone");
|
|
641
|
+
expect(
|
|
642
|
+
getAiAgentStateSentence(status, new Date(NOW.getTime() + 10 * 60 * 1000)),
|
|
643
|
+
).toBe(AI_AGENT_GONE_TEXT);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
test("reads the current time when none is given", () => {
|
|
647
|
+
expect(getAiAgentOfflineReason(agentStatus({}, signedOffAgent()))).toBe(
|
|
648
|
+
"signed_off",
|
|
649
|
+
);
|
|
650
|
+
expect(getAiAgentStateSentence(agentStatus({}, signedOffAgent()))).toBe(
|
|
651
|
+
AI_AGENT_SIGNED_OFF_TEXT,
|
|
652
|
+
);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
test("an agent row without a target still reads its own reason", () => {
|
|
656
|
+
expect(
|
|
657
|
+
getAiAgentOfflineReason(
|
|
658
|
+
agentStatus({ runner: null }, signedOffAgent()),
|
|
659
|
+
NOW_DATE,
|
|
660
|
+
),
|
|
661
|
+
).toBe("signed_off");
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
/*
|
|
665
|
+
* A server that predates the agent row gives no connection status to
|
|
666
|
+
* tell a sign-off apart; the heartbeat reading is the one that is true
|
|
667
|
+
* either way.
|
|
668
|
+
*/
|
|
669
|
+
test("without the agent row it falls back to the heartbeat reading", () => {
|
|
670
|
+
const payload: Partial<KubernetesClusterAiAccessStatus> = agentStatus(
|
|
671
|
+
{},
|
|
672
|
+
signedOffAgent(),
|
|
673
|
+
);
|
|
674
|
+
delete payload.aiAgent;
|
|
675
|
+
// An older server omits aiAgent, which the current type requires.
|
|
676
|
+
const older: KubernetesClusterAiAccessStatus =
|
|
677
|
+
payload as KubernetesClusterAiAccessStatus;
|
|
678
|
+
expect(getAiAgentCardState(older)).toBe("offline");
|
|
679
|
+
expect(getAiAgentOfflineReason(older, NOW_DATE)).toBe("silent");
|
|
680
|
+
expect(getAiAgentStateSentence(older, NOW_DATE)).toBe(AI_AGENT_SILENT_TEXT);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test("the silent sentence quotes the server's alive window", () => {
|
|
684
|
+
expect(AI_AGENT_SILENT_TEXT).toBe(
|
|
685
|
+
"The AI agent has not checked in for over 5 minutes. Check its pod:",
|
|
686
|
+
);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
test("no other state reads an offline reason into its sentence", () => {
|
|
690
|
+
for (const status of [
|
|
691
|
+
agentStatus(),
|
|
692
|
+
notInstalledStatus(),
|
|
693
|
+
legacyStatus(),
|
|
694
|
+
legacyStatus({}, false),
|
|
695
|
+
advancedStatus(),
|
|
696
|
+
advancedStatus({}, false),
|
|
697
|
+
]) {
|
|
698
|
+
const sentence: string = getAiAgentStateSentence(status, NOW_DATE);
|
|
699
|
+
for (const offline of [
|
|
700
|
+
AI_AGENT_SILENT_TEXT,
|
|
701
|
+
AI_AGENT_SIGNED_OFF_TEXT,
|
|
702
|
+
AI_AGENT_GONE_TEXT,
|
|
703
|
+
]) {
|
|
704
|
+
expect(sentence).not.toBe(offline);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
describe("the card's one command", () => {
|
|
711
|
+
test("install where installing the agent is the step, logs where its pod is", () => {
|
|
712
|
+
expect(getAiAgentCardCommand(notInstalledStatus())).toBe("install");
|
|
713
|
+
expect(getAiAgentCardCommand(legacyStatus())).toBe("install");
|
|
714
|
+
expect(getAiAgentCardCommand(legacyStatus({}, false))).toBe("install");
|
|
715
|
+
expect(
|
|
716
|
+
getAiAgentCardCommand(agentStatus({}, makeAgent({ isOnline: false }))),
|
|
717
|
+
).toBe("logs");
|
|
718
|
+
for (const status of [
|
|
719
|
+
agentStatus(),
|
|
720
|
+
advancedStatus(),
|
|
721
|
+
advancedStatus({}, false),
|
|
722
|
+
]) {
|
|
723
|
+
expect(getAiAgentCardCommand(status)).toBeNull();
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test("the logs go to the namespace the agent reported, else the install namespace", () => {
|
|
728
|
+
expect(getAiAgentPodNamespace(agentStatus())).toBe("monitoring");
|
|
729
|
+
expect(
|
|
730
|
+
getAiAgentPodNamespace(
|
|
731
|
+
agentStatus(
|
|
732
|
+
{},
|
|
733
|
+
makeAgent({
|
|
734
|
+
posture: { inCluster: true, podNamespace: undefined },
|
|
735
|
+
}),
|
|
736
|
+
),
|
|
737
|
+
),
|
|
738
|
+
).toBe(KUBERNETES_AGENT_HELM_NAMESPACE);
|
|
739
|
+
expect(getAiAgentPodNamespace(notInstalledStatus())).toBe(
|
|
740
|
+
KUBERNETES_AGENT_HELM_NAMESPACE,
|
|
741
|
+
);
|
|
742
|
+
// A legacy Runner's pod namespace is not the agent's.
|
|
743
|
+
expect(
|
|
744
|
+
getAiAgentPodNamespace(
|
|
745
|
+
legacyStatus({
|
|
746
|
+
runner: {
|
|
747
|
+
...legacyStatus().runner!,
|
|
748
|
+
posture: { inCluster: true, podNamespace: "old-ns" },
|
|
749
|
+
},
|
|
750
|
+
}),
|
|
751
|
+
),
|
|
752
|
+
).toBe(KUBERNETES_AGENT_HELM_NAMESPACE);
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
describe("what the target may change", () => {
|
|
757
|
+
test("read-only, a namespace list, or the whole cluster", () => {
|
|
758
|
+
expect(describeAiAgentWriteAccess(undefined)).toBeNull();
|
|
759
|
+
expect(describeAiAgentWriteAccess({ allowWrites: false })).toBe(
|
|
760
|
+
"Read-only",
|
|
761
|
+
);
|
|
762
|
+
expect(describeAiAgentWriteAccess({})).toBe("Read-only");
|
|
763
|
+
expect(
|
|
764
|
+
describeAiAgentWriteAccess({ allowWrites: true, writeNamespaces: [] }),
|
|
765
|
+
).toBe("Can change: whole cluster");
|
|
766
|
+
expect(
|
|
767
|
+
describeAiAgentWriteAccess({
|
|
768
|
+
allowWrites: true,
|
|
769
|
+
writeNamespaces: ["web", "api"],
|
|
770
|
+
}),
|
|
771
|
+
).toBe("Can change: web, api");
|
|
772
|
+
// An older Runner that never said where.
|
|
773
|
+
expect(describeAiAgentWriteAccess({ allowWrites: true })).toBe(
|
|
774
|
+
"Can change the cluster",
|
|
775
|
+
);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test("node operations are said only for a target that may write", () => {
|
|
779
|
+
expect(
|
|
780
|
+
describeAiAgentNodeOperations({
|
|
781
|
+
allowWrites: true,
|
|
782
|
+
allowNodeOperations: true,
|
|
783
|
+
}),
|
|
784
|
+
).toBe("node operations on");
|
|
785
|
+
expect(
|
|
786
|
+
describeAiAgentNodeOperations({
|
|
787
|
+
allowWrites: true,
|
|
788
|
+
allowNodeOperations: false,
|
|
789
|
+
}),
|
|
790
|
+
).toBe("node operations off");
|
|
791
|
+
expect(describeAiAgentNodeOperations({ allowWrites: true })).toBeNull();
|
|
792
|
+
expect(
|
|
793
|
+
describeAiAgentNodeOperations({
|
|
794
|
+
allowWrites: false,
|
|
795
|
+
allowNodeOperations: true,
|
|
796
|
+
}),
|
|
797
|
+
).toBeNull();
|
|
798
|
+
expect(describeAiAgentNodeOperations(undefined)).toBeNull();
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
describe("the meta line", () => {
|
|
803
|
+
test("the agent: last seen, its version, kubectl's, and read-only", () => {
|
|
804
|
+
expect(getAiAgentMetaParts(agentStatus())).toEqual([
|
|
805
|
+
"last seen a minute ago",
|
|
806
|
+
"agent v14.1.0",
|
|
807
|
+
"kubectl v1.31.2",
|
|
808
|
+
"Read-only",
|
|
809
|
+
]);
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
test("a writing agent names where it may change and whether nodes are included", () => {
|
|
813
|
+
expect(
|
|
814
|
+
getAiAgentMetaParts(
|
|
815
|
+
agentStatus(
|
|
816
|
+
{},
|
|
817
|
+
makeAgent({
|
|
818
|
+
agentVersion: "v14.2.0",
|
|
819
|
+
posture: {
|
|
820
|
+
inCluster: true,
|
|
821
|
+
allowWrites: true,
|
|
822
|
+
writeNamespaces: ["web", "api"],
|
|
823
|
+
allowNodeOperations: true,
|
|
824
|
+
kubectlVersion: "1.31.2",
|
|
825
|
+
},
|
|
826
|
+
}),
|
|
827
|
+
),
|
|
828
|
+
),
|
|
829
|
+
).toEqual([
|
|
830
|
+
"last seen a minute ago",
|
|
831
|
+
"agent v14.2.0",
|
|
832
|
+
"kubectl v1.31.2",
|
|
833
|
+
"Can change: web, api",
|
|
834
|
+
"node operations on",
|
|
835
|
+
]);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
test("the previous Runner has no agent version but the same scope reading", () => {
|
|
839
|
+
expect(getAiAgentMetaParts(legacyStatus())).toEqual([
|
|
840
|
+
"last seen 2 minutes ago",
|
|
841
|
+
"kubectl v1.30.0",
|
|
842
|
+
"Read-only",
|
|
843
|
+
]);
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
test("an advanced Runner shows no write scope: its credential's RBAC decides", () => {
|
|
847
|
+
expect(getAiAgentMetaParts(advancedStatus())).toEqual([
|
|
848
|
+
"last seen 3 minutes ago",
|
|
849
|
+
"kubectl v1.29.1",
|
|
850
|
+
]);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test("nothing installed, nothing to say", () => {
|
|
854
|
+
expect(getAiAgentMetaParts(notInstalledStatus())).toEqual([]);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
test("an agent that never heartbeated has no last-seen part", () => {
|
|
858
|
+
expect(
|
|
859
|
+
getAiAgentMetaParts(
|
|
860
|
+
agentStatus(
|
|
861
|
+
{},
|
|
862
|
+
makeAgent({
|
|
863
|
+
lastAliveAt: undefined,
|
|
864
|
+
agentVersion: undefined,
|
|
865
|
+
posture: undefined,
|
|
866
|
+
}),
|
|
867
|
+
),
|
|
868
|
+
),
|
|
869
|
+
).toEqual([]);
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
describe("the refused-registration warning", () => {
|
|
874
|
+
test("warns about another agent that tried to register while this one was online", () => {
|
|
875
|
+
const warning: string | null = getRefusedRegistrationWarning(
|
|
876
|
+
makeAgent({
|
|
877
|
+
lastRefusedRegistrationAt: minutesAgo(10),
|
|
878
|
+
lastRefusedRegistrationReason: "previous_instance_online",
|
|
879
|
+
}),
|
|
880
|
+
NOW,
|
|
881
|
+
);
|
|
882
|
+
expect(warning).toMatch(
|
|
883
|
+
/^Another agent tried to register for this cluster at .+ while this one was online\./,
|
|
884
|
+
);
|
|
885
|
+
expect(warning).toContain("clusterName");
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
test("an unknown reason still warns; the upgrade overlap with the previous Runner does not", () => {
|
|
889
|
+
expect(
|
|
890
|
+
getRefusedRegistrationWarning(
|
|
891
|
+
makeAgent({ lastRefusedRegistrationAt: minutesAgo(10) }),
|
|
892
|
+
NOW,
|
|
893
|
+
),
|
|
894
|
+
).not.toBeNull();
|
|
895
|
+
expect(
|
|
896
|
+
getRefusedRegistrationWarning(
|
|
897
|
+
makeAgent({
|
|
898
|
+
lastRefusedRegistrationAt: minutesAgo(10),
|
|
899
|
+
lastRefusedRegistrationReason: "legacy_runner_online",
|
|
900
|
+
}),
|
|
901
|
+
NOW,
|
|
902
|
+
),
|
|
903
|
+
).toBeNull();
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
test("ages out, and says nothing without a refusal or an agent", () => {
|
|
907
|
+
expect(
|
|
908
|
+
getRefusedRegistrationWarning(
|
|
909
|
+
makeAgent({
|
|
910
|
+
lastRefusedRegistrationAt: new Date(
|
|
911
|
+
NOW.getTime() - REFUSED_REGISTRATION_WARNING_WINDOW_MS - 1000,
|
|
912
|
+
).toISOString(),
|
|
913
|
+
}),
|
|
914
|
+
NOW,
|
|
915
|
+
),
|
|
916
|
+
).toBeNull();
|
|
917
|
+
expect(getRefusedRegistrationWarning(makeAgent(), NOW)).toBeNull();
|
|
918
|
+
expect(getRefusedRegistrationWarning(null, NOW)).toBeNull();
|
|
919
|
+
expect(
|
|
920
|
+
getRefusedRegistrationWarning(
|
|
921
|
+
makeAgent({ lastRefusedRegistrationAt: "not a date" }),
|
|
922
|
+
NOW,
|
|
923
|
+
),
|
|
924
|
+
).toBeNull();
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
describe("the Needs attention card", () => {
|
|
929
|
+
/*
|
|
930
|
+
* One row per server gap, and no client-side checklist. Fixes being off
|
|
931
|
+
* is a choice the "What AI may do" card shows with its own hint, so it is
|
|
932
|
+
* the one gap left out — otherwise a default install could never read
|
|
933
|
+
* "Ready".
|
|
934
|
+
*/
|
|
935
|
+
test("lists every server gap except the fixes-off choice", () => {
|
|
936
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus({
|
|
937
|
+
gaps: [
|
|
938
|
+
gap("remediation_disabled", "remediation"),
|
|
939
|
+
gap("investigation_disabled", "investigation"),
|
|
940
|
+
gap("ai_balance_insufficient"),
|
|
941
|
+
],
|
|
942
|
+
});
|
|
943
|
+
expect(
|
|
944
|
+
getAttentionGaps(status).map((item: KubernetesAiAccessGap): string => {
|
|
945
|
+
return item.code;
|
|
946
|
+
}),
|
|
947
|
+
).toEqual(["investigation_disabled", "ai_balance_insufficient"]);
|
|
948
|
+
expect(CHOICE_GAP_CODES).toEqual(["remediation_disabled"]);
|
|
949
|
+
expect(
|
|
950
|
+
getAttentionGaps(
|
|
951
|
+
agentStatus({ gaps: [gap("remediation_disabled", "remediation")] }),
|
|
952
|
+
),
|
|
953
|
+
).toEqual([]);
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
test("each row offers at most one action, the one that fixes it", () => {
|
|
957
|
+
const status: KubernetesClusterAiAccessStatus = agentStatus();
|
|
958
|
+
const expected: Array<[KubernetesAiAccessGapCode, string | null]> = [
|
|
959
|
+
["investigation_disabled", "turn_on_investigation"],
|
|
960
|
+
["project_ai_disabled", "open_ai_features"],
|
|
961
|
+
["project_auto_remediation_disabled", "open_ai_features"],
|
|
962
|
+
["project_ai_command_execution_disabled", "open_ai_features"],
|
|
963
|
+
["llm_provider_missing", "open_llm_providers"],
|
|
964
|
+
["ai_balance_insufficient", "open_ai_credits"],
|
|
965
|
+
["last_access_check_failed", "test_connection"],
|
|
966
|
+
// Already on the page as a command.
|
|
967
|
+
["ai_agent_not_connected", null],
|
|
968
|
+
["ai_agent_offline", null],
|
|
969
|
+
["remediation_write_access_missing", null],
|
|
970
|
+
["runner_missing", null],
|
|
971
|
+
["no_runner_bound", null],
|
|
972
|
+
];
|
|
973
|
+
for (const [code, action] of expected) {
|
|
974
|
+
expect({ code, action: getAiAgentGapAction(gap(code), status) }).toEqual({
|
|
975
|
+
code,
|
|
976
|
+
action,
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("Runner gaps link to the Runner only for an advanced binding", () => {
|
|
982
|
+
for (const code of [
|
|
983
|
+
"runner_offline",
|
|
984
|
+
"runner_ai_commands_disabled",
|
|
985
|
+
"runner_cluster_mismatch",
|
|
986
|
+
"credential_missing",
|
|
987
|
+
"credential_on_agent_runner",
|
|
988
|
+
] as Array<KubernetesAiAccessGapCode>) {
|
|
989
|
+
expect(getAiAgentGapAction(gap(code), advancedStatus())).toBe(
|
|
990
|
+
"view_runner",
|
|
991
|
+
);
|
|
992
|
+
// The previous in-cluster Runner is replaced by upgrading the chart.
|
|
993
|
+
expect(getAiAgentGapAction(gap(code), legacyStatus())).toBeNull();
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
test("nothing to test before anything can reach the cluster", () => {
|
|
998
|
+
expect(
|
|
999
|
+
getAiAgentGapAction(
|
|
1000
|
+
gap("last_access_check_failed"),
|
|
1001
|
+
notInstalledStatus(),
|
|
1002
|
+
),
|
|
1003
|
+
).toBeNull();
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
test("tells a user without permission who to ask", () => {
|
|
1007
|
+
expect(ASK_PROJECT_ADMIN_TEXT).toBe("Ask a project owner or admin.");
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
describe("the write-access commands", () => {
|
|
1012
|
+
test("show when fixes are on and the in-cluster target is read-only", () => {
|
|
1013
|
+
for (const mode of [
|
|
1014
|
+
KubernetesAiRemediationMode.RequireApproval,
|
|
1015
|
+
KubernetesAiRemediationMode.Automatic,
|
|
1016
|
+
KubernetesAiRemediationMode.BypassApproval,
|
|
1017
|
+
]) {
|
|
1018
|
+
expect(
|
|
1019
|
+
shouldShowWriteAccessCommands(agentStatus({ remediationMode: mode })),
|
|
1020
|
+
).toBe(true);
|
|
1021
|
+
// The previous Runner is replaced by the same aiAgent.* upgrade.
|
|
1022
|
+
expect(
|
|
1023
|
+
shouldShowWriteAccessCommands(legacyStatus({ remediationMode: mode })),
|
|
1024
|
+
).toBe(true);
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
test("never while fixes are off, the agent already writes, or the target is advanced or missing", () => {
|
|
1029
|
+
expect(shouldShowWriteAccessCommands(agentStatus())).toBe(false);
|
|
1030
|
+
expect(
|
|
1031
|
+
shouldShowWriteAccessCommands(
|
|
1032
|
+
agentStatus(
|
|
1033
|
+
{ remediationMode: KubernetesAiRemediationMode.RequireApproval },
|
|
1034
|
+
makeAgent({ posture: { inCluster: true, allowWrites: true } }),
|
|
1035
|
+
),
|
|
1036
|
+
),
|
|
1037
|
+
).toBe(false);
|
|
1038
|
+
expect(
|
|
1039
|
+
shouldShowWriteAccessCommands(
|
|
1040
|
+
advancedStatus({
|
|
1041
|
+
remediationMode: KubernetesAiRemediationMode.Automatic,
|
|
1042
|
+
}),
|
|
1043
|
+
),
|
|
1044
|
+
).toBe(false);
|
|
1045
|
+
expect(
|
|
1046
|
+
shouldShowWriteAccessCommands(
|
|
1047
|
+
notInstalledStatus({
|
|
1048
|
+
remediationMode: KubernetesAiRemediationMode.Automatic,
|
|
1049
|
+
}),
|
|
1050
|
+
),
|
|
1051
|
+
).toBe(false);
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
test("the fixes-off hint names the mode to choose", () => {
|
|
1055
|
+
expect(AI_AGENT_FIXES_OFF_HINT).toBe(
|
|
1056
|
+
"Want AI to propose fixes? Choose Ask for approval.",
|
|
1057
|
+
);
|
|
1058
|
+
});
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
describe("the automatic-investigation footer", () => {
|
|
1062
|
+
test("reads the project's two opt-ins, On or Off", () => {
|
|
1063
|
+
expect(
|
|
1064
|
+
getAutomaticInvestigationLine({ incidents: true, alerts: false }),
|
|
1065
|
+
).toBe(
|
|
1066
|
+
"Automatic investigation for new incidents in this project: On · alerts: Off",
|
|
1067
|
+
);
|
|
1068
|
+
expect(
|
|
1069
|
+
getAutomaticInvestigationLine({ incidents: false, alerts: true }),
|
|
1070
|
+
).toBe(
|
|
1071
|
+
"Automatic investigation for new incidents in this project: Off · alerts: On",
|
|
1072
|
+
);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
test("a malformed or missing field is not shown", () => {
|
|
1076
|
+
expect(
|
|
1077
|
+
getAutomaticInvestigation(
|
|
1078
|
+
agentStatus({
|
|
1079
|
+
automaticInvestigation: {
|
|
1080
|
+
incidents: "yes",
|
|
1081
|
+
alerts: false,
|
|
1082
|
+
} as unknown as KubernetesClusterAiAccessStatus["automaticInvestigation"],
|
|
1083
|
+
}),
|
|
1084
|
+
),
|
|
1085
|
+
).toBeNull();
|
|
1086
|
+
expect(
|
|
1087
|
+
getAutomaticInvestigation(
|
|
1088
|
+
agentStatus({
|
|
1089
|
+
// An older server sends no opt-ins, which the current type requires.
|
|
1090
|
+
automaticInvestigation:
|
|
1091
|
+
undefined as unknown as KubernetesClusterAiAccessStatus["automaticInvestigation"],
|
|
1092
|
+
}),
|
|
1093
|
+
),
|
|
1094
|
+
).toBeNull();
|
|
1095
|
+
expect(getAutomaticInvestigation(agentStatus())).toEqual({
|
|
1096
|
+
incidents: false,
|
|
1097
|
+
alerts: false,
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
test("Turn on writes only the flags that are off", () => {
|
|
1102
|
+
expect(
|
|
1103
|
+
getAutomaticInvestigationTurnOnChanges({
|
|
1104
|
+
incidents: false,
|
|
1105
|
+
alerts: false,
|
|
1106
|
+
}),
|
|
1107
|
+
).toEqual({
|
|
1108
|
+
enableAutomaticIncidentInvestigation: true,
|
|
1109
|
+
enableAutomaticAlertInvestigation: true,
|
|
1110
|
+
});
|
|
1111
|
+
expect(
|
|
1112
|
+
getAutomaticInvestigationTurnOnChanges({
|
|
1113
|
+
incidents: true,
|
|
1114
|
+
alerts: false,
|
|
1115
|
+
}),
|
|
1116
|
+
).toEqual({ enableAutomaticAlertInvestigation: true });
|
|
1117
|
+
expect(
|
|
1118
|
+
getAutomaticInvestigationTurnOnChanges({
|
|
1119
|
+
incidents: false,
|
|
1120
|
+
alerts: true,
|
|
1121
|
+
}),
|
|
1122
|
+
).toEqual({ enableAutomaticIncidentInvestigation: true });
|
|
1123
|
+
expect(
|
|
1124
|
+
getAutomaticInvestigationTurnOnChanges({ incidents: true, alerts: true }),
|
|
1125
|
+
).toEqual({});
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
/*
|
|
1129
|
+
* The opt-in is project-wide, so the dialog says so before anything is
|
|
1130
|
+
* turned on, and points at where its limits live.
|
|
1131
|
+
*/
|
|
1132
|
+
test("the confirmation says it applies to the whole project and where the limits are", () => {
|
|
1133
|
+
expect(
|
|
1134
|
+
getAutomaticInvestigationConfirmation({
|
|
1135
|
+
settings: { incidents: false, alerts: false },
|
|
1136
|
+
projectName: "Acme",
|
|
1137
|
+
}),
|
|
1138
|
+
).toBe(
|
|
1139
|
+
"This applies to every new incident and alert in Acme, not just this cluster. Limits live under Incidents → Settings → AI.",
|
|
1140
|
+
);
|
|
1141
|
+
expect(
|
|
1142
|
+
getAutomaticInvestigationConfirmation({
|
|
1143
|
+
settings: { incidents: false, alerts: true },
|
|
1144
|
+
projectName: "Acme",
|
|
1145
|
+
}),
|
|
1146
|
+
).toContain("every new incident in Acme");
|
|
1147
|
+
expect(
|
|
1148
|
+
getAutomaticInvestigationConfirmation({
|
|
1149
|
+
settings: { incidents: true, alerts: false },
|
|
1150
|
+
projectName: "Acme",
|
|
1151
|
+
}),
|
|
1152
|
+
).toContain("every new alert in Acme");
|
|
1153
|
+
});
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
describe("switching an advanced binding to the AI agent", () => {
|
|
1157
|
+
test("is offered only while the agent is online", () => {
|
|
1158
|
+
expect(canSwitchToAiAgent(advancedStatus())).toBe(true);
|
|
1159
|
+
expect(
|
|
1160
|
+
canSwitchToAiAgent(
|
|
1161
|
+
advancedStatus({ aiAgent: makeAgent({ isOnline: false }) }),
|
|
1162
|
+
),
|
|
1163
|
+
).toBe(false);
|
|
1164
|
+
expect(canSwitchToAiAgent(advancedStatus({ aiAgent: null }))).toBe(false);
|
|
1165
|
+
// Not an advanced binding: nothing to switch.
|
|
1166
|
+
expect(canSwitchToAiAgent(agentStatus())).toBe(false);
|
|
1167
|
+
expect(canSwitchToAiAgent(legacyStatus({ aiAgent: makeAgent() }))).toBe(
|
|
1168
|
+
false,
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
describe("the Overview's AI agent card", () => {
|
|
1174
|
+
test("Connected, Offline or Not installed", () => {
|
|
1175
|
+
expect(getAiAgentOverviewState(agentStatus())).toEqual({
|
|
1176
|
+
text: "Connected",
|
|
1177
|
+
tone: "success",
|
|
1178
|
+
});
|
|
1179
|
+
expect(getAiAgentOverviewState(legacyStatus())).toEqual({
|
|
1180
|
+
text: "Connected",
|
|
1181
|
+
tone: "success",
|
|
1182
|
+
});
|
|
1183
|
+
expect(getAiAgentOverviewState(advancedStatus())).toEqual({
|
|
1184
|
+
text: "Connected",
|
|
1185
|
+
tone: "success",
|
|
1186
|
+
});
|
|
1187
|
+
for (const status of [
|
|
1188
|
+
agentStatus({}, makeAgent({ isOnline: false })),
|
|
1189
|
+
legacyStatus({}, false),
|
|
1190
|
+
advancedStatus({}, false),
|
|
1191
|
+
]) {
|
|
1192
|
+
expect(getAiAgentOverviewState(status)).toEqual({
|
|
1193
|
+
text: "Offline",
|
|
1194
|
+
tone: "danger",
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
expect(getAiAgentOverviewState(notInstalledStatus())).toEqual({
|
|
1198
|
+
text: "Not installed",
|
|
1199
|
+
tone: "neutral",
|
|
1200
|
+
});
|
|
1201
|
+
});
|
|
1202
|
+
});
|