@oneuptime/common 14.0.9 → 14.0.10
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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +213 -0
- package/Models/DatabaseModels/DatabaseServer.ts +213 -0
- package/Models/DatabaseModels/DockerHost.ts +213 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Host.ts +213 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +144 -0
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Server/API/AutoRemediationAPI.ts +392 -1
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +15 -5
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- package/Server/Services/CephClusterService.ts +109 -1
- package/Server/Services/DatabaseServerService.ts +116 -1
- package/Server/Services/DockerHostService.ts +109 -1
- package/Server/Services/DockerSwarmClusterService.ts +113 -1
- package/Server/Services/HostService.ts +126 -4
- package/Server/Services/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- package/Server/Services/ResourceAiAccessService.ts +1439 -0
- package/Server/Services/ResourceAiAgentJobService.ts +202 -0
- package/Server/Services/ResourceAiAgentService.ts +2432 -0
- package/Server/Services/RunnerJobService.ts +611 -4
- package/Server/Services/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/VMwareVCenterService.ts +110 -1
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1434 -32
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +72 -2
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
- package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +366 -143
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +93 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
- package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
- package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
- package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
- package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
- package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
- package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
- package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
- package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
- package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
- package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
- package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
- package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
- package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
- package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
- package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
- package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
- package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
- package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
- package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
- package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
- package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
- package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
- package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
- package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
- package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
- package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
- package/Types/Monitor/RumAlertTemplates.ts +351 -4
- package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
- package/Utils/AI/InvestigationReport.ts +121 -15
- package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
- package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
- package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
- package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
- package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
- package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
- package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
- package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
- package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
- package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
- package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
- package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +219 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
- package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +104 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerService.js +99 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +104 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +114 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +104 -0
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
- package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/RunnerJobService.js +344 -5
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1029 -30
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +50 -4
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +263 -110
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +71 -11
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import OneUptimeDate from "../../Types/Date";
|
|
11
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
12
|
+
import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
|
|
13
|
+
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
14
|
+
import ObjectID from "../../Types/ObjectID";
|
|
15
|
+
import AiResourceType, { AI_RESOURCE_TYPE_INFO, ALL_AI_RESOURCE_TYPES, isAiResourceType, } from "../../Types/ResourceAiAgent/AiResourceType";
|
|
16
|
+
import { RESOURCE_AI_AGENT_IMAGE_REPOSITORY, RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV, RESOURCE_AI_ALLOW_WRITES_ENV, RESOURCE_AI_WRITE_TARGETS_ENV, ResourceAiRemediationMode, parseResourceAiRemediationMode, } from "../../Types/ResourceAiAgent/ResourceAiAccess";
|
|
17
|
+
import ModelPermission from "../Types/Database/Permissions/Index";
|
|
18
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
19
|
+
import MonitorResourceContextUtil from "../Utils/Monitor/MonitorResourceContext";
|
|
20
|
+
import logger from "../Utils/Logger";
|
|
21
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
22
|
+
import AlertService from "./AlertService";
|
|
23
|
+
import CephClusterService from "./CephClusterService";
|
|
24
|
+
import DatabaseServerService from "./DatabaseServerService";
|
|
25
|
+
import DockerHostService from "./DockerHostService";
|
|
26
|
+
import DockerSwarmClusterService from "./DockerSwarmClusterService";
|
|
27
|
+
import HostService from "./HostService";
|
|
28
|
+
import IncidentService from "./IncidentService";
|
|
29
|
+
import KubernetesClusterAiAccessService, { AI_BALANCE_INSUFFICIENT_NEXT_STEP, } from "./KubernetesClusterAiAccessService";
|
|
30
|
+
import MonitorService from "./MonitorService";
|
|
31
|
+
import PodmanHostService from "./PodmanHostService";
|
|
32
|
+
import ProxmoxClusterService from "./ProxmoxClusterService";
|
|
33
|
+
import ResourceAiAgentService from "./ResourceAiAgentService";
|
|
34
|
+
import VMwareVCenterService from "./VMwareVCenterService";
|
|
35
|
+
// The most resources one incident or alert offers an investigation.
|
|
36
|
+
export const MAX_RESOURCES_PER_SUBJECT = 10;
|
|
37
|
+
/*
|
|
38
|
+
* How many of a subject's linked resources are read per query while
|
|
39
|
+
* looking for its unarchived ones (getResourcesForSubject): one read covers
|
|
40
|
+
* any ordinary subject, and a subject linked to many archived resources is
|
|
41
|
+
* read on in batches until the cap is reached.
|
|
42
|
+
*/
|
|
43
|
+
const SUBJECT_RESOURCE_READ_BATCH_SIZE = MAX_RESOURCES_PER_SUBJECT * 5;
|
|
44
|
+
// The columns every resource model carries for AI access.
|
|
45
|
+
const AI_ACCESS_COLUMNS = {
|
|
46
|
+
_id: true,
|
|
47
|
+
projectId: true,
|
|
48
|
+
name: true,
|
|
49
|
+
isArchived: true,
|
|
50
|
+
isAiInvestigationEnabled: true,
|
|
51
|
+
aiRemediationMode: true,
|
|
52
|
+
aiCommandAllowlist: true,
|
|
53
|
+
aiAccessConfiguredAt: true,
|
|
54
|
+
aiAccessLastVerifiedAt: true,
|
|
55
|
+
aiAccessLastError: true,
|
|
56
|
+
};
|
|
57
|
+
const MODEL_SPECS = {
|
|
58
|
+
[AiResourceType.DockerHost]: {
|
|
59
|
+
service: DockerHostService,
|
|
60
|
+
identityColumns: { hostIdentifier: true },
|
|
61
|
+
subjectRelation: "dockerHosts",
|
|
62
|
+
resolvedIdsKey: "dockerHostIds",
|
|
63
|
+
},
|
|
64
|
+
[AiResourceType.PodmanHost]: {
|
|
65
|
+
service: PodmanHostService,
|
|
66
|
+
identityColumns: { hostIdentifier: true },
|
|
67
|
+
subjectRelation: "podmanHosts",
|
|
68
|
+
resolvedIdsKey: "podmanHostIds",
|
|
69
|
+
},
|
|
70
|
+
[AiResourceType.DockerSwarmCluster]: {
|
|
71
|
+
service: DockerSwarmClusterService,
|
|
72
|
+
identityColumns: {},
|
|
73
|
+
subjectRelation: "dockerSwarmClusters",
|
|
74
|
+
resolvedIdsKey: "dockerSwarmClusterIds",
|
|
75
|
+
},
|
|
76
|
+
[AiResourceType.ProxmoxCluster]: {
|
|
77
|
+
service: ProxmoxClusterService,
|
|
78
|
+
identityColumns: {},
|
|
79
|
+
subjectRelation: "proxmoxClusters",
|
|
80
|
+
resolvedIdsKey: "proxmoxClusterIds",
|
|
81
|
+
},
|
|
82
|
+
[AiResourceType.VMwareVCenter]: {
|
|
83
|
+
service: VMwareVCenterService,
|
|
84
|
+
identityColumns: {},
|
|
85
|
+
subjectRelation: "vmwareVCenters",
|
|
86
|
+
resolvedIdsKey: "vmwareVCenterIds",
|
|
87
|
+
},
|
|
88
|
+
[AiResourceType.CephCluster]: {
|
|
89
|
+
service: CephClusterService,
|
|
90
|
+
identityColumns: {},
|
|
91
|
+
subjectRelation: "cephClusters",
|
|
92
|
+
resolvedIdsKey: "cephClusterIds",
|
|
93
|
+
},
|
|
94
|
+
[AiResourceType.DatabaseServer]: {
|
|
95
|
+
service: DatabaseServerService,
|
|
96
|
+
identityColumns: {
|
|
97
|
+
dbSystem: true,
|
|
98
|
+
serverAddress: true,
|
|
99
|
+
serverPort: true,
|
|
100
|
+
},
|
|
101
|
+
subjectRelation: "databaseServers",
|
|
102
|
+
resolvedIdsKey: "databaseServerIds",
|
|
103
|
+
},
|
|
104
|
+
[AiResourceType.Host]: {
|
|
105
|
+
service: HostService,
|
|
106
|
+
identityColumns: { hostIdentifier: true },
|
|
107
|
+
subjectRelation: "hosts",
|
|
108
|
+
resolvedIdsKey: "hostIds",
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
/*
|
|
112
|
+
* What an operator checks when the agent is online but cannot reach its
|
|
113
|
+
* resource: the one thing each agent needs besides the network.
|
|
114
|
+
*/
|
|
115
|
+
const REACH_HINTS = {
|
|
116
|
+
[AiResourceType.DockerHost]: "that the Docker socket (/var/run/docker.sock) is mounted into the agent's container",
|
|
117
|
+
[AiResourceType.PodmanHost]: "that the Podman socket (/run/podman/podman.sock) is mounted into the agent's container",
|
|
118
|
+
[AiResourceType.DockerSwarmCluster]: "that the agent runs on a swarm MANAGER node with the Docker socket mounted",
|
|
119
|
+
[AiResourceType.ProxmoxCluster]: "PVE_HOST and the agent's Proxmox API token",
|
|
120
|
+
[AiResourceType.VMwareVCenter]: "the vCenter URL and the credentials in the agent's environment",
|
|
121
|
+
[AiResourceType.CephCluster]: "the Ceph config and keyring mounted into the agent's container",
|
|
122
|
+
[AiResourceType.DatabaseServer]: "the database address and the read-only credentials in the agent's environment",
|
|
123
|
+
[AiResourceType.Host]: "that the agent runs privileged with the host's PID namespace (pid: host)",
|
|
124
|
+
};
|
|
125
|
+
// "Docker host", but "host" and "database server" for the generic nouns.
|
|
126
|
+
export function describeResourceNoun(resourceType) {
|
|
127
|
+
if (resourceType === AiResourceType.Host) {
|
|
128
|
+
return "host";
|
|
129
|
+
}
|
|
130
|
+
if (resourceType === AiResourceType.DatabaseServer) {
|
|
131
|
+
return "database server";
|
|
132
|
+
}
|
|
133
|
+
return isAiResourceType(resourceType)
|
|
134
|
+
? AI_RESOURCE_TYPE_INFO[resourceType].displayName
|
|
135
|
+
: "resource";
|
|
136
|
+
}
|
|
137
|
+
/*
|
|
138
|
+
* Where every next step sends an operator: the resource's AI agent page
|
|
139
|
+
* (AI → AI agent in its side menu). Shown verbatim in the investigation's
|
|
140
|
+
* report and panel, so it always names the page.
|
|
141
|
+
*/
|
|
142
|
+
export function getResourceAiAgentPage(resourceType) {
|
|
143
|
+
return `the ${describeResourceNoun(resourceType)}'s AI agent page (AI → AI agent)`;
|
|
144
|
+
}
|
|
145
|
+
/*
|
|
146
|
+
* How to install the resource's agent, as one sentence: the image, the
|
|
147
|
+
* type, and the identity to register with — spelled with this resource's
|
|
148
|
+
* own identity when it is known, so the agent lands on THIS row.
|
|
149
|
+
*/
|
|
150
|
+
export function getResourceAiAgentInstallNextStep(data) {
|
|
151
|
+
const info = AI_RESOURCE_TYPE_INFO[data.resourceType];
|
|
152
|
+
const noun = describeResourceNoun(data.resourceType);
|
|
153
|
+
let identity;
|
|
154
|
+
if (data.resourceType === AiResourceType.DatabaseServer) {
|
|
155
|
+
identity = data.resourceId
|
|
156
|
+
? ` and DATABASE_SERVER_ID=${data.resourceId}`
|
|
157
|
+
: " and DATABASE_SERVER_ID set to this database server's id";
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const variable = info.identityEnvVars[0] || "the identity";
|
|
161
|
+
identity = data.identifier
|
|
162
|
+
? ` and ${variable}=${data.identifier}`
|
|
163
|
+
: ` and ${variable} set to the name its collector reports`;
|
|
164
|
+
}
|
|
165
|
+
return `Install the ${info.agentDisplayName} next to this ${noun}'s telemetry collector: run the ${RESOURCE_AI_AGENT_IMAGE_REPOSITORY} image with ${RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV}=${info.agentAlias}${identity}. The complete snippet is on ${getResourceAiAgentPage(data.resourceType)}.`;
|
|
166
|
+
}
|
|
167
|
+
function toDate(value) {
|
|
168
|
+
if (!value) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
172
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
173
|
+
}
|
|
174
|
+
function toIsoString(value) {
|
|
175
|
+
const date = toDate(value);
|
|
176
|
+
return date ? OneUptimeDate.toString(date) : undefined;
|
|
177
|
+
}
|
|
178
|
+
/*
|
|
179
|
+
* The allowlist column is jsonb; a client may save it as an array or as a
|
|
180
|
+
* JSON string. Anything unusable normalizes to empty — nothing risky
|
|
181
|
+
* auto-executes.
|
|
182
|
+
*/
|
|
183
|
+
export function normalizeResourceAllowlist(value) {
|
|
184
|
+
let raw = value;
|
|
185
|
+
if (typeof raw === "string") {
|
|
186
|
+
try {
|
|
187
|
+
raw = JSON.parse(raw);
|
|
188
|
+
}
|
|
189
|
+
catch (_a) {
|
|
190
|
+
raw = [value];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!Array.isArray(raw)) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
return raw
|
|
197
|
+
.filter((pattern) => {
|
|
198
|
+
return typeof pattern === "string" && pattern.trim().length > 0;
|
|
199
|
+
})
|
|
200
|
+
.map((pattern) => {
|
|
201
|
+
return pattern.trim();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function refKey(resourceType, resourceId) {
|
|
205
|
+
return `${resourceType}:${resourceId.toLowerCase()}`;
|
|
206
|
+
}
|
|
207
|
+
function readString(value) {
|
|
208
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
209
|
+
}
|
|
210
|
+
export class ResourceAiAccessServiceClass {
|
|
211
|
+
/*
|
|
212
|
+
* ------------------------------------------------------------------
|
|
213
|
+
* Readiness
|
|
214
|
+
* ------------------------------------------------------------------
|
|
215
|
+
*/
|
|
216
|
+
// The project-wide gates, read exactly as they are for clusters.
|
|
217
|
+
async getProjectGates(projectId) {
|
|
218
|
+
return await KubernetesClusterAiAccessService.getProjectGates(projectId);
|
|
219
|
+
}
|
|
220
|
+
async getStatusForResource(data) {
|
|
221
|
+
const statuses = await this.getStatusesForResources({
|
|
222
|
+
projectId: data.projectId,
|
|
223
|
+
resources: [
|
|
224
|
+
{
|
|
225
|
+
resourceType: data.resourceType,
|
|
226
|
+
resourceId: data.resourceId.toString(),
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
gates: data.gates,
|
|
230
|
+
});
|
|
231
|
+
return statuses[0] || null;
|
|
232
|
+
}
|
|
233
|
+
/*
|
|
234
|
+
* The status of several resources of one project, in the order asked
|
|
235
|
+
* for, with every resource's agent row read in ONE query. A resource
|
|
236
|
+
* that does not exist in the project (or, with excludeArchived, is
|
|
237
|
+
* archived) has no status. When the agent read fails every status reads
|
|
238
|
+
* as having no agent, which can only make it say less is reachable.
|
|
239
|
+
*/
|
|
240
|
+
async getStatusesForResources(data) {
|
|
241
|
+
const rows = await this.loadResources({
|
|
242
|
+
projectId: data.projectId,
|
|
243
|
+
resources: data.resources,
|
|
244
|
+
excludeArchived: data.excludeArchived,
|
|
245
|
+
});
|
|
246
|
+
if (rows.length === 0) {
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
const gates = data.gates || (await this.getProjectGates(data.projectId));
|
|
250
|
+
let agentRows = new Map();
|
|
251
|
+
try {
|
|
252
|
+
agentRows = await this.findAgentRows({
|
|
253
|
+
projectId: data.projectId,
|
|
254
|
+
resources: rows,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
logger.error(`ResourceAiAccess: could not read the resource AI agents of project ${data.projectId.toString()}; every resource reads as having none: ${error}`);
|
|
259
|
+
}
|
|
260
|
+
return rows.map((row) => {
|
|
261
|
+
return this.buildStatus({
|
|
262
|
+
resource: row,
|
|
263
|
+
agentRow: agentRows.get(refKey(row.resourceType, row.id.toString())) || null,
|
|
264
|
+
gates,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/*
|
|
269
|
+
* The readiness computation proper, for one resource row, its agent row
|
|
270
|
+
* (online or not; null when none ever registered) and the project gates.
|
|
271
|
+
* Pure: every read was done by the caller.
|
|
272
|
+
*/
|
|
273
|
+
buildStatus(data) {
|
|
274
|
+
const { resource, agentRow, gates } = data;
|
|
275
|
+
const type = resource.resourceType;
|
|
276
|
+
const info = AI_RESOURCE_TYPE_INFO[type];
|
|
277
|
+
const noun = describeResourceNoun(type);
|
|
278
|
+
const page = getResourceAiAgentPage(type);
|
|
279
|
+
const gaps = [];
|
|
280
|
+
/*
|
|
281
|
+
* The agent as ResourceAiAgentService sees it: one online rule for the
|
|
282
|
+
* status and for the chokepoint (findOnlineAgentForResource).
|
|
283
|
+
*/
|
|
284
|
+
const agent = agentRow
|
|
285
|
+
? ResourceAiAgentService.getAgentSummary(agentRow, data.now)
|
|
286
|
+
: null;
|
|
287
|
+
const posture = (agent === null || agent === void 0 ? void 0 : agent.posture) || null;
|
|
288
|
+
const remediationMode = resource.aiRemediationMode;
|
|
289
|
+
if (!agent) {
|
|
290
|
+
gaps.push({
|
|
291
|
+
code: "ai_agent_not_connected",
|
|
292
|
+
title: `No ${info.agentDisplayName} is connected`,
|
|
293
|
+
nextStep: getResourceAiAgentInstallNextStep({
|
|
294
|
+
resourceType: type,
|
|
295
|
+
resourceId: resource.id.toString(),
|
|
296
|
+
identifier: resource.identifier,
|
|
297
|
+
}),
|
|
298
|
+
blocksInvestigation: true,
|
|
299
|
+
blocksRemediation: true,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
else if (!agent.isOnline) {
|
|
303
|
+
const lastAliveAt = toDate(agentRow === null || agentRow === void 0 ? void 0 : agentRow.lastAliveAt);
|
|
304
|
+
const lastAliveText = lastAliveAt
|
|
305
|
+
? OneUptimeDate.getDateAsFormattedString(lastAliveAt)
|
|
306
|
+
: "an unknown time";
|
|
307
|
+
gaps.push({
|
|
308
|
+
code: "ai_agent_offline",
|
|
309
|
+
title: `The ${info.agentDisplayName} is offline`,
|
|
310
|
+
nextStep: `It last reported in at ${lastAliveText}${agent.connectionStatus === "disconnected"
|
|
311
|
+
? " and then signed off (it was stopped, restarted or reset)"
|
|
312
|
+
: ""}. Check that its container is running and can reach your OneUptime URL (its logs say why it cannot).`,
|
|
313
|
+
blocksInvestigation: true,
|
|
314
|
+
blocksRemediation: true,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
else if (!posture || posture.resourceType !== type) {
|
|
318
|
+
/*
|
|
319
|
+
* The agent always reports a posture for the type it serves; one
|
|
320
|
+
* that is missing, unreadable or for another type is not trusted
|
|
321
|
+
* with commands (the enqueue chokepoint refuses the same case).
|
|
322
|
+
*/
|
|
323
|
+
gaps.push({
|
|
324
|
+
code: "ai_agent_unreachable_resource",
|
|
325
|
+
title: `The ${info.agentDisplayName} has not reported that it can reach this ${noun}`,
|
|
326
|
+
nextStep: `Wait for its next report (within a minute), or reset the agent on ${page}.`,
|
|
327
|
+
blocksInvestigation: true,
|
|
328
|
+
blocksRemediation: true,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else if (posture.reachable !== true) {
|
|
332
|
+
gaps.push({
|
|
333
|
+
code: "ai_agent_unreachable_resource",
|
|
334
|
+
title: `The ${info.agentDisplayName} cannot reach this ${noun}`,
|
|
335
|
+
nextStep: `Its last check failed${posture.reachError ? `: ${posture.reachError}` : ""}. Check ${REACH_HINTS[type]}, then use "Test connection" on ${page}.`,
|
|
336
|
+
blocksInvestigation: true,
|
|
337
|
+
blocksRemediation: true,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (!resource.isAiInvestigationEnabled) {
|
|
341
|
+
gaps.push({
|
|
342
|
+
code: "investigation_disabled",
|
|
343
|
+
title: `AI investigation is turned off for this ${noun}`,
|
|
344
|
+
nextStep: `Turn on AI investigation on ${page}.`,
|
|
345
|
+
blocksInvestigation: true,
|
|
346
|
+
blocksRemediation: false,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (remediationMode === ResourceAiRemediationMode.Disabled) {
|
|
350
|
+
gaps.push({
|
|
351
|
+
code: "remediation_disabled",
|
|
352
|
+
title: `AI fixes are turned off for this ${noun}`,
|
|
353
|
+
nextStep: `Set "Fixes" to "Ask for approval", "Automatic" or "Bypass approval" on ${page}.`,
|
|
354
|
+
blocksInvestigation: false,
|
|
355
|
+
blocksRemediation: true,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
else if (agent && (posture === null || posture === void 0 ? void 0 : posture.allowWrites) !== true) {
|
|
359
|
+
gaps.push({
|
|
360
|
+
code: "remediation_write_access_missing",
|
|
361
|
+
title: `The ${info.agentDisplayName} is read-only`,
|
|
362
|
+
nextStep: `Set ${RESOURCE_AI_ALLOW_WRITES_ENV}=true on the ${info.agentDisplayName} and restart it (${RESOURCE_AI_WRITE_TARGETS_ENV} limits what it may change).`,
|
|
363
|
+
blocksInvestigation: false,
|
|
364
|
+
blocksRemediation: true,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (!gates.isAiEnabled) {
|
|
368
|
+
gaps.push({
|
|
369
|
+
code: "ai_disabled_for_project",
|
|
370
|
+
title: "AI is disabled for this project",
|
|
371
|
+
nextStep: "Enable AI under Project Settings → AI Features.",
|
|
372
|
+
blocksInvestigation: true,
|
|
373
|
+
blocksRemediation: true,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (!gates.hasLlmProvider) {
|
|
377
|
+
gaps.push({
|
|
378
|
+
code: "llm_provider_missing",
|
|
379
|
+
title: "No AI provider is configured",
|
|
380
|
+
nextStep: "Add a provider under Project Settings → AI → LLM Providers (or use OneUptime AI credits).",
|
|
381
|
+
blocksInvestigation: true,
|
|
382
|
+
blocksRemediation: true,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
if (gates.aiBalanceBlocker) {
|
|
386
|
+
gaps.push({
|
|
387
|
+
code: "ai_balance_insufficient",
|
|
388
|
+
title: "The project is out of AI credits",
|
|
389
|
+
nextStep: AI_BALANCE_INSUFFICIENT_NEXT_STEP,
|
|
390
|
+
blocksInvestigation: true,
|
|
391
|
+
blocksRemediation: true,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
if (!gates.isAutoRemediationEnabled) {
|
|
395
|
+
gaps.push({
|
|
396
|
+
code: "auto_remediation_disabled_for_project",
|
|
397
|
+
title: "Auto-remediation is disabled for this project",
|
|
398
|
+
nextStep: "Enable auto-remediation under Project Settings → AI Features.",
|
|
399
|
+
blocksInvestigation: false,
|
|
400
|
+
blocksRemediation: true,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
const blocksInvestigation = gaps.some((gap) => {
|
|
404
|
+
return gap.blocksInvestigation;
|
|
405
|
+
});
|
|
406
|
+
const blocksRemediation = gaps.some((gap) => {
|
|
407
|
+
return gap.blocksRemediation;
|
|
408
|
+
});
|
|
409
|
+
return {
|
|
410
|
+
resourceType: type,
|
|
411
|
+
resourceId: resource.id.toString(),
|
|
412
|
+
resourceName: resource.name,
|
|
413
|
+
isAiInvestigationEnabled: resource.isAiInvestigationEnabled,
|
|
414
|
+
aiRemediationMode: remediationMode,
|
|
415
|
+
aiCommandAllowlist: resource.aiCommandAllowlist,
|
|
416
|
+
aiAccessConfiguredAt: toIsoString(resource.aiAccessConfiguredAt) || null,
|
|
417
|
+
aiAccessLastVerifiedAt: toIsoString(resource.aiAccessLastVerifiedAt) || null,
|
|
418
|
+
aiAccessLastError: resource.aiAccessLastError || null,
|
|
419
|
+
agent,
|
|
420
|
+
gaps,
|
|
421
|
+
isInvestigationReady: resource.isAiInvestigationEnabled && !blocksInvestigation,
|
|
422
|
+
isRemediationReady: remediationMode !== ResourceAiRemediationMode.Disabled &&
|
|
423
|
+
!blocksRemediation,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
/*
|
|
427
|
+
* ------------------------------------------------------------------
|
|
428
|
+
* Rows
|
|
429
|
+
* ------------------------------------------------------------------
|
|
430
|
+
*/
|
|
431
|
+
/*
|
|
432
|
+
* One resource of the project (as root: the caller scopes by project),
|
|
433
|
+
* or null when it is not the project's. Lookup failures propagate: the
|
|
434
|
+
* enqueue chokepoint must fail closed on a resource it could not read.
|
|
435
|
+
*/
|
|
436
|
+
async loadResource(data) {
|
|
437
|
+
if (!isAiResourceType(data.resourceType)) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
const rows = await this.loadResources({
|
|
441
|
+
projectId: data.projectId,
|
|
442
|
+
resources: [
|
|
443
|
+
{
|
|
444
|
+
resourceType: data.resourceType,
|
|
445
|
+
resourceId: data.resourceId.toString(),
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
});
|
|
449
|
+
return rows[0] || null;
|
|
450
|
+
}
|
|
451
|
+
/*
|
|
452
|
+
* Several resources of one project, one query per resource type, in the
|
|
453
|
+
* order asked for (duplicates and unknown types dropped).
|
|
454
|
+
*/
|
|
455
|
+
async loadResources(data) {
|
|
456
|
+
const wanted = [];
|
|
457
|
+
const seen = new Set();
|
|
458
|
+
for (const ref of data.resources) {
|
|
459
|
+
if (!ref ||
|
|
460
|
+
!isAiResourceType(ref.resourceType) ||
|
|
461
|
+
typeof ref.resourceId !== "string" ||
|
|
462
|
+
!ObjectID.isValidUUID(ref.resourceId)) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const key = refKey(ref.resourceType, ref.resourceId);
|
|
466
|
+
if (seen.has(key)) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
seen.add(key);
|
|
470
|
+
wanted.push(ref);
|
|
471
|
+
}
|
|
472
|
+
const byKey = new Map();
|
|
473
|
+
for (const type of ALL_AI_RESOURCE_TYPES) {
|
|
474
|
+
const ids = wanted
|
|
475
|
+
.filter((ref) => {
|
|
476
|
+
return ref.resourceType === type;
|
|
477
|
+
})
|
|
478
|
+
.map((ref) => {
|
|
479
|
+
return ref.resourceId;
|
|
480
|
+
});
|
|
481
|
+
if (ids.length === 0) {
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
const spec = MODEL_SPECS[type];
|
|
485
|
+
const models = (await spec.service.findBy({
|
|
486
|
+
query: Object.assign({ projectId: data.projectId, _id: QueryHelper.any(ids.map((id) => {
|
|
487
|
+
return new ObjectID(id);
|
|
488
|
+
})) }, (data.excludeArchived ? { isArchived: false } : {})),
|
|
489
|
+
select: Object.assign(Object.assign({}, AI_ACCESS_COLUMNS), spec.identityColumns),
|
|
490
|
+
limit: ids.length,
|
|
491
|
+
skip: 0,
|
|
492
|
+
props: { isRoot: true },
|
|
493
|
+
}));
|
|
494
|
+
for (const model of models) {
|
|
495
|
+
const row = this.toRow(type, model, data);
|
|
496
|
+
if (row) {
|
|
497
|
+
byKey.set(refKey(type, row.id.toString()), row);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
const rows = [];
|
|
502
|
+
for (const ref of wanted) {
|
|
503
|
+
const row = byKey.get(refKey(ref.resourceType, ref.resourceId));
|
|
504
|
+
if (row) {
|
|
505
|
+
rows.push(row);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return rows;
|
|
509
|
+
}
|
|
510
|
+
// A resource model read through its service, as the status reads it.
|
|
511
|
+
toRow(resourceType, model, scope) {
|
|
512
|
+
const idValue = model["_id"] || model["id"];
|
|
513
|
+
const id = idValue instanceof ObjectID
|
|
514
|
+
? idValue.toString()
|
|
515
|
+
: readString(idValue);
|
|
516
|
+
if (!id || !ObjectID.isValidUUID(id)) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
const projectIdValue = model["projectId"];
|
|
520
|
+
const projectId = projectIdValue instanceof ObjectID
|
|
521
|
+
? projectIdValue.toString()
|
|
522
|
+
: readString(projectIdValue);
|
|
523
|
+
// The query was project-scoped; a row that says otherwise is not used.
|
|
524
|
+
if (projectId && projectId !== scope.projectId.toString()) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
const isArchived = model["isArchived"] === true;
|
|
528
|
+
if (scope.excludeArchived && isArchived) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
const identifier = this.readIdentifier(resourceType, model);
|
|
532
|
+
return {
|
|
533
|
+
resourceType,
|
|
534
|
+
id: new ObjectID(id),
|
|
535
|
+
projectId: scope.projectId,
|
|
536
|
+
name: readString(model["name"]) ||
|
|
537
|
+
identifier ||
|
|
538
|
+
AI_RESOURCE_TYPE_INFO[resourceType].displayName,
|
|
539
|
+
identifier,
|
|
540
|
+
isAiInvestigationEnabled: model["isAiInvestigationEnabled"] === true,
|
|
541
|
+
aiRemediationMode: parseResourceAiRemediationMode(model["aiRemediationMode"]),
|
|
542
|
+
aiCommandAllowlist: normalizeResourceAllowlist(model["aiCommandAllowlist"]),
|
|
543
|
+
aiAccessConfiguredAt: toDate(model["aiAccessConfiguredAt"]) || undefined,
|
|
544
|
+
aiAccessLastVerifiedAt: toDate(model["aiAccessLastVerifiedAt"]) || undefined,
|
|
545
|
+
aiAccessLastError: readString(model["aiAccessLastError"]),
|
|
546
|
+
isArchived,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
/*
|
|
550
|
+
* The identity the resource's collector reports: hostIdentifier for
|
|
551
|
+
* hosts, the name for clusters and vCenters, and system|host:port for a
|
|
552
|
+
* database server.
|
|
553
|
+
*/
|
|
554
|
+
readIdentifier(resourceType, model) {
|
|
555
|
+
if (resourceType === AiResourceType.DockerHost ||
|
|
556
|
+
resourceType === AiResourceType.PodmanHost ||
|
|
557
|
+
resourceType === AiResourceType.Host) {
|
|
558
|
+
return readString(model["hostIdentifier"]);
|
|
559
|
+
}
|
|
560
|
+
if (resourceType === AiResourceType.DatabaseServer) {
|
|
561
|
+
const address = readString(model["serverAddress"]);
|
|
562
|
+
if (!address) {
|
|
563
|
+
return undefined;
|
|
564
|
+
}
|
|
565
|
+
const port = model["serverPort"];
|
|
566
|
+
const system = readString(model["dbSystem"]);
|
|
567
|
+
return `${system ? `${system}|` : ""}${address}${typeof port === "number" && Number.isFinite(port) ? `:${port}` : ""}`;
|
|
568
|
+
}
|
|
569
|
+
return readString(model["name"]);
|
|
570
|
+
}
|
|
571
|
+
/*
|
|
572
|
+
* The agent rows of these resources (online or not), keyed by
|
|
573
|
+
* type:resourceId — one query per resource type present
|
|
574
|
+
* (ResourceAiAgentService.findAgentsForResources). Never reads the key
|
|
575
|
+
* hash.
|
|
576
|
+
*/
|
|
577
|
+
async findAgentRows(data) {
|
|
578
|
+
const byKey = new Map();
|
|
579
|
+
for (const type of ALL_AI_RESOURCE_TYPES) {
|
|
580
|
+
const ids = data.resources
|
|
581
|
+
.filter((resource) => {
|
|
582
|
+
return resource.resourceType === type;
|
|
583
|
+
})
|
|
584
|
+
.map((resource) => {
|
|
585
|
+
return resource.id;
|
|
586
|
+
});
|
|
587
|
+
if (ids.length === 0) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
const rows = await ResourceAiAgentService.findAgentsForResources({
|
|
591
|
+
projectId: data.projectId,
|
|
592
|
+
resourceType: type,
|
|
593
|
+
resourceIds: ids,
|
|
594
|
+
});
|
|
595
|
+
for (const [resourceId, row] of rows) {
|
|
596
|
+
byKey.set(refKey(type, resourceId), row);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return byKey;
|
|
600
|
+
}
|
|
601
|
+
/*
|
|
602
|
+
* ------------------------------------------------------------------
|
|
603
|
+
* The access target
|
|
604
|
+
* ------------------------------------------------------------------
|
|
605
|
+
*/
|
|
606
|
+
/*
|
|
607
|
+
* The resource and the agent a command for it goes to NOW: its online
|
|
608
|
+
* agent, or null. Lookup failures propagate: the enqueue chokepoint must
|
|
609
|
+
* fail closed on a target it could not check.
|
|
610
|
+
*/
|
|
611
|
+
async loadAccessTarget(data) {
|
|
612
|
+
const resource = await this.loadResource(data);
|
|
613
|
+
if (!resource) {
|
|
614
|
+
return { resource: null, agent: null };
|
|
615
|
+
}
|
|
616
|
+
const agent = await ResourceAiAgentService.findOnlineAgentForResource({
|
|
617
|
+
projectId: data.projectId,
|
|
618
|
+
resourceType: resource.resourceType,
|
|
619
|
+
resourceId: resource.id,
|
|
620
|
+
});
|
|
621
|
+
return { resource, agent: agent || null };
|
|
622
|
+
}
|
|
623
|
+
/*
|
|
624
|
+
* ------------------------------------------------------------------
|
|
625
|
+
* Subjects
|
|
626
|
+
* ------------------------------------------------------------------
|
|
627
|
+
*/
|
|
628
|
+
/*
|
|
629
|
+
* The unarchived resources of the project an incident or alert is linked
|
|
630
|
+
* to, in the order ALL_AI_RESOURCE_TYPES lists the types, at most
|
|
631
|
+
* MAX_RESOURCES_PER_SUBJECT. Linked means the subject's resource
|
|
632
|
+
* relations (written when it was created, from its series labels and its
|
|
633
|
+
* monitors' configuration); when none of those is an unarchived resource
|
|
634
|
+
* of the project, the subject's monitors' step configuration is resolved
|
|
635
|
+
* again now (the resource may have appeared after the subject was
|
|
636
|
+
* created, or replaced an archived one), matching names
|
|
637
|
+
* case-insensitively like the linker does.
|
|
638
|
+
*
|
|
639
|
+
* Archived and missing resources are dropped BEFORE the cap: capping
|
|
640
|
+
* first would let ten archived links hide an active one behind them.
|
|
641
|
+
*/
|
|
642
|
+
async getResourcesForSubject(data) {
|
|
643
|
+
const linked = new Map();
|
|
644
|
+
const monitorIds = [];
|
|
645
|
+
if (data.alertId || data.incidentId) {
|
|
646
|
+
const relations = await Promise.all(ALL_AI_RESOURCE_TYPES.map(async (type) => {
|
|
647
|
+
return {
|
|
648
|
+
type,
|
|
649
|
+
ids: await this.readSubjectRelation({
|
|
650
|
+
projectId: data.projectId,
|
|
651
|
+
incidentId: data.incidentId,
|
|
652
|
+
alertId: data.alertId,
|
|
653
|
+
relation: MODEL_SPECS[type].subjectRelation,
|
|
654
|
+
}),
|
|
655
|
+
};
|
|
656
|
+
}));
|
|
657
|
+
for (const relation of relations) {
|
|
658
|
+
linked.set(relation.type, relation.ids);
|
|
659
|
+
}
|
|
660
|
+
monitorIds.push(...(await this.readSubjectMonitorIds({
|
|
661
|
+
projectId: data.projectId,
|
|
662
|
+
incidentId: data.incidentId,
|
|
663
|
+
alertId: data.alertId,
|
|
664
|
+
})));
|
|
665
|
+
}
|
|
666
|
+
let refs = await this.keepUnarchivedRefs({
|
|
667
|
+
projectId: data.projectId,
|
|
668
|
+
refs: this.flattenRefs(linked),
|
|
669
|
+
});
|
|
670
|
+
if (refs.length === 0 && monitorIds.length > 0) {
|
|
671
|
+
refs = await this.keepUnarchivedRefs({
|
|
672
|
+
projectId: data.projectId,
|
|
673
|
+
refs: this.flattenRefs(await this.resolveMonitorResources({
|
|
674
|
+
projectId: data.projectId,
|
|
675
|
+
monitorIds,
|
|
676
|
+
})),
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
return refs;
|
|
680
|
+
}
|
|
681
|
+
/*
|
|
682
|
+
* The first MAX_RESOURCES_PER_SUBJECT of these refs (in their order) that
|
|
683
|
+
* are unarchived resources of the project, read in batches so a long
|
|
684
|
+
* list of archived links costs a few reads, not one per link.
|
|
685
|
+
*/
|
|
686
|
+
async keepUnarchivedRefs(data) {
|
|
687
|
+
const kept = [];
|
|
688
|
+
for (let start = 0; start < data.refs.length && kept.length < MAX_RESOURCES_PER_SUBJECT; start += SUBJECT_RESOURCE_READ_BATCH_SIZE) {
|
|
689
|
+
const rows = await this.loadResources({
|
|
690
|
+
projectId: data.projectId,
|
|
691
|
+
resources: data.refs.slice(start, start + SUBJECT_RESOURCE_READ_BATCH_SIZE),
|
|
692
|
+
excludeArchived: true,
|
|
693
|
+
});
|
|
694
|
+
for (const row of rows) {
|
|
695
|
+
kept.push({
|
|
696
|
+
resourceType: row.resourceType,
|
|
697
|
+
resourceId: row.id.toString(),
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return kept.slice(0, MAX_RESOURCES_PER_SUBJECT);
|
|
702
|
+
}
|
|
703
|
+
async getStatusesForSubject(data) {
|
|
704
|
+
const refs = await this.getResourcesForSubject(data);
|
|
705
|
+
if (refs.length === 0) {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
return await this.getStatusesForResources({
|
|
709
|
+
projectId: data.projectId,
|
|
710
|
+
resources: refs,
|
|
711
|
+
excludeArchived: true,
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
// Types in priority order, each type's ids in relation order, deduped.
|
|
715
|
+
flattenRefs(byType) {
|
|
716
|
+
const refs = [];
|
|
717
|
+
const seen = new Set();
|
|
718
|
+
for (const type of ALL_AI_RESOURCE_TYPES) {
|
|
719
|
+
for (const id of byType.get(type) || []) {
|
|
720
|
+
const key = refKey(type, id);
|
|
721
|
+
if (!ObjectID.isValidUUID(id) || seen.has(key)) {
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
seen.add(key);
|
|
725
|
+
refs.push({ resourceType: type, resourceId: id });
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return refs;
|
|
729
|
+
}
|
|
730
|
+
/*
|
|
731
|
+
* One of the subject's resource relations, read on its own: reading all
|
|
732
|
+
* eight many-to-many relations in one query would multiply their rows.
|
|
733
|
+
*/
|
|
734
|
+
async readSubjectRelation(data) {
|
|
735
|
+
var _a;
|
|
736
|
+
const select = {
|
|
737
|
+
_id: true,
|
|
738
|
+
[data.relation]: { _id: true },
|
|
739
|
+
};
|
|
740
|
+
let linked = [];
|
|
741
|
+
if (data.alertId) {
|
|
742
|
+
const alert = await AlertService.findOneBy({
|
|
743
|
+
query: { _id: data.alertId.toString(), projectId: data.projectId },
|
|
744
|
+
select: select,
|
|
745
|
+
props: { isRoot: true },
|
|
746
|
+
});
|
|
747
|
+
linked = ((alert === null || alert === void 0 ? void 0 : alert[data.relation]) || []);
|
|
748
|
+
}
|
|
749
|
+
else if (data.incidentId) {
|
|
750
|
+
const incident = await IncidentService.findOneBy({
|
|
751
|
+
query: { _id: data.incidentId.toString(), projectId: data.projectId },
|
|
752
|
+
select: select,
|
|
753
|
+
props: { isRoot: true },
|
|
754
|
+
});
|
|
755
|
+
linked = ((incident === null || incident === void 0 ? void 0 : incident[data.relation]) || []);
|
|
756
|
+
}
|
|
757
|
+
const ids = [];
|
|
758
|
+
for (const resource of Array.isArray(linked) ? linked : []) {
|
|
759
|
+
const id = (_a = resource === null || resource === void 0 ? void 0 : resource.id) === null || _a === void 0 ? void 0 : _a.toString();
|
|
760
|
+
if (id) {
|
|
761
|
+
ids.push(id);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return ids;
|
|
765
|
+
}
|
|
766
|
+
async readSubjectMonitorIds(data) {
|
|
767
|
+
if (data.alertId) {
|
|
768
|
+
const alert = await AlertService.findOneBy({
|
|
769
|
+
query: { _id: data.alertId.toString(), projectId: data.projectId },
|
|
770
|
+
select: { _id: true, monitorId: true },
|
|
771
|
+
props: { isRoot: true },
|
|
772
|
+
});
|
|
773
|
+
return (alert === null || alert === void 0 ? void 0 : alert.monitorId) ? [alert.monitorId] : [];
|
|
774
|
+
}
|
|
775
|
+
if (data.incidentId) {
|
|
776
|
+
const incident = await IncidentService.findOneBy({
|
|
777
|
+
query: { _id: data.incidentId.toString(), projectId: data.projectId },
|
|
778
|
+
select: { _id: true, monitors: { _id: true } },
|
|
779
|
+
props: { isRoot: true },
|
|
780
|
+
});
|
|
781
|
+
return ((incident === null || incident === void 0 ? void 0 : incident.monitors) || [])
|
|
782
|
+
.map((monitor) => {
|
|
783
|
+
return monitor.id || undefined;
|
|
784
|
+
})
|
|
785
|
+
.filter((id) => {
|
|
786
|
+
return Boolean(id);
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return [];
|
|
790
|
+
}
|
|
791
|
+
/*
|
|
792
|
+
* What the subject's monitors' step configuration names now, resolved
|
|
793
|
+
* the way the incident/alert creation path resolves it
|
|
794
|
+
* (MonitorResourceContext: MonitorStepResourceIdentity, then
|
|
795
|
+
* SeriesResourceLinker.resolveResourceRefs with case-insensitive
|
|
796
|
+
* names). Best effort per monitor: that resolution never throws.
|
|
797
|
+
*/
|
|
798
|
+
async resolveMonitorResources(data) {
|
|
799
|
+
const byType = new Map();
|
|
800
|
+
const monitors = await MonitorService.findBy({
|
|
801
|
+
query: {
|
|
802
|
+
_id: QueryHelper.any(data.monitorIds.slice(0, MAX_RESOURCES_PER_SUBJECT)),
|
|
803
|
+
projectId: data.projectId,
|
|
804
|
+
},
|
|
805
|
+
select: {
|
|
806
|
+
_id: true,
|
|
807
|
+
projectId: true,
|
|
808
|
+
monitorType: true,
|
|
809
|
+
monitorSteps: true,
|
|
810
|
+
},
|
|
811
|
+
limit: MAX_RESOURCES_PER_SUBJECT,
|
|
812
|
+
skip: 0,
|
|
813
|
+
props: { isRoot: true },
|
|
814
|
+
});
|
|
815
|
+
for (const monitor of monitors) {
|
|
816
|
+
const resolved = await MonitorResourceContextUtil.resolveResourceContextForMonitor({
|
|
817
|
+
monitor,
|
|
818
|
+
});
|
|
819
|
+
for (const type of ALL_AI_RESOURCE_TYPES) {
|
|
820
|
+
const ids = resolved[MODEL_SPECS[type].resolvedIdsKey] || [];
|
|
821
|
+
if (ids.length > 0) {
|
|
822
|
+
byType.set(type, [...(byType.get(type) || []), ...ids]);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return byType;
|
|
827
|
+
}
|
|
828
|
+
/*
|
|
829
|
+
* ------------------------------------------------------------------
|
|
830
|
+
* Who may change a resource
|
|
831
|
+
* ------------------------------------------------------------------
|
|
832
|
+
*/
|
|
833
|
+
/*
|
|
834
|
+
* Throws unless the caller may EDIT this resource: its update ACL, with
|
|
835
|
+
* label-scoped allow and block rows decided from the row's own labels,
|
|
836
|
+
* and the caller's Owned scope — exactly what a CRUD update of the row
|
|
837
|
+
* would check. Approving an AI plan runs its commands on the resource as
|
|
838
|
+
* root, so being allowed to start runbooks in the project is not enough
|
|
839
|
+
* on its own: someone blocked from editing (or reading) a resource must
|
|
840
|
+
* not be able to change it by approving a plan. Root and master admins
|
|
841
|
+
* are not gated. A resource that does not exist in the project is
|
|
842
|
+
* refused like one the caller may not edit.
|
|
843
|
+
*/
|
|
844
|
+
async assertCallerMayChangeResource(data) {
|
|
845
|
+
if (data.props.isRoot || data.props.isMasterAdmin) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
if (!isAiResourceType(data.resourceType)) {
|
|
849
|
+
throw new BadDataException(`"${String(data.resourceType)}" is not a resource type.`);
|
|
850
|
+
}
|
|
851
|
+
const noun = describeResourceNoun(data.resourceType);
|
|
852
|
+
const refusal = `You do not have permission to edit this ${noun}, so you cannot approve an AI plan that changes it. Ask someone who may edit the ${noun} to approve it, or dismiss the suggestion.`;
|
|
853
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
854
|
+
const service = MODEL_SPECS[data.resourceType].service;
|
|
855
|
+
const query = {
|
|
856
|
+
_id: data.resourceId.toString(),
|
|
857
|
+
projectId: data.projectId,
|
|
858
|
+
};
|
|
859
|
+
/*
|
|
860
|
+
* The caller's permissions are read for the resource's own project —
|
|
861
|
+
* the permission layer reads the rows of props.tenantId.
|
|
862
|
+
*/
|
|
863
|
+
const callerProps = Object.assign(Object.assign({}, data.props), { tenantId: data.projectId, isMultiTenantRequest: false });
|
|
864
|
+
/*
|
|
865
|
+
* The row's labels, read as root: label-scoped allow and block rows are
|
|
866
|
+
* decided per row, from them (DatabaseService.updateOneById's read).
|
|
867
|
+
*/
|
|
868
|
+
const select = { _id: true };
|
|
869
|
+
const accessControlColumn = service
|
|
870
|
+
.getModel()
|
|
871
|
+
.getAccessControlColumn();
|
|
872
|
+
if (accessControlColumn) {
|
|
873
|
+
select[accessControlColumn] = {
|
|
874
|
+
_id: true,
|
|
875
|
+
name: true,
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
const row = (await service.findOneBy({
|
|
879
|
+
query,
|
|
880
|
+
select,
|
|
881
|
+
props: { isRoot: true },
|
|
882
|
+
}));
|
|
883
|
+
if (!row) {
|
|
884
|
+
throw new NotAuthorizedException(refusal);
|
|
885
|
+
}
|
|
886
|
+
try {
|
|
887
|
+
// Readable to the caller at all (a label-scoped read block refuses).
|
|
888
|
+
const readable = (await service.findOneBy({
|
|
889
|
+
query,
|
|
890
|
+
select: { _id: true },
|
|
891
|
+
props: callerProps,
|
|
892
|
+
}));
|
|
893
|
+
if (!readable) {
|
|
894
|
+
throw new NotAuthorizedException(refusal);
|
|
895
|
+
}
|
|
896
|
+
await ModelPermission.checkUpdatePermissionByModel({
|
|
897
|
+
modelType: service.modelType,
|
|
898
|
+
fetchModelWithAccessControlIds: async () => {
|
|
899
|
+
return row;
|
|
900
|
+
},
|
|
901
|
+
props: callerProps,
|
|
902
|
+
});
|
|
903
|
+
/*
|
|
904
|
+
* The caller's whole update scope as a query (labels, Owned scope):
|
|
905
|
+
* the row must still be in it.
|
|
906
|
+
*/
|
|
907
|
+
const permittedQuery = (await ModelPermission.checkUpdateQueryPermissions(service.modelType, query, {}, callerProps));
|
|
908
|
+
const permitted = (await service.findOneBy({
|
|
909
|
+
query: permittedQuery,
|
|
910
|
+
select: { _id: true },
|
|
911
|
+
props: { isRoot: true },
|
|
912
|
+
}));
|
|
913
|
+
if (!permitted) {
|
|
914
|
+
throw new NotAuthorizedException(refusal);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
catch (error) {
|
|
918
|
+
if (error instanceof NotAuthorizedException ||
|
|
919
|
+
error instanceof NotAuthenticatedException) {
|
|
920
|
+
throw new NotAuthorizedException(refusal);
|
|
921
|
+
}
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
/*
|
|
926
|
+
* ------------------------------------------------------------------
|
|
927
|
+
* Bookkeeping
|
|
928
|
+
* ------------------------------------------------------------------
|
|
929
|
+
*/
|
|
930
|
+
/*
|
|
931
|
+
* Best effort: the resource's AI agent page shows "last verified" / "last
|
|
932
|
+
* error" from this. Written as root without the model's hooks — it is the
|
|
933
|
+
* server's own record of what a command did, not an operator's change to
|
|
934
|
+
* the resource's AI settings (the settings hooks stamp and police those).
|
|
935
|
+
*/
|
|
936
|
+
async recordCommandOutcome(data) {
|
|
937
|
+
var _a;
|
|
938
|
+
try {
|
|
939
|
+
if (!isAiResourceType(data.resourceType)) {
|
|
940
|
+
throw new BadDataException(`"${String(data.resourceType)}" is not a resource type.`);
|
|
941
|
+
}
|
|
942
|
+
await MODEL_SPECS[data.resourceType].service.updateOneById({
|
|
943
|
+
id: data.resourceId,
|
|
944
|
+
data: (data.succeeded
|
|
945
|
+
? {
|
|
946
|
+
aiAccessLastVerifiedAt: OneUptimeDate.getCurrentDate(),
|
|
947
|
+
aiAccessLastError: null,
|
|
948
|
+
}
|
|
949
|
+
: {
|
|
950
|
+
aiAccessLastError: (data.errorMessage || "Command failed.").slice(0, 2000),
|
|
951
|
+
}),
|
|
952
|
+
props: { isRoot: true, ignoreHooks: true },
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
catch (error) {
|
|
956
|
+
logger.error(`ResourceAiAccess: could not record a command outcome for ${String(data.resourceType)} ${(_a = data.resourceId) === null || _a === void 0 ? void 0 : _a.toString()}: ${error}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
__decorate([
|
|
961
|
+
CaptureSpan(),
|
|
962
|
+
__metadata("design:type", Function),
|
|
963
|
+
__metadata("design:paramtypes", [ObjectID]),
|
|
964
|
+
__metadata("design:returntype", Promise)
|
|
965
|
+
], ResourceAiAccessServiceClass.prototype, "getProjectGates", null);
|
|
966
|
+
__decorate([
|
|
967
|
+
CaptureSpan(),
|
|
968
|
+
__metadata("design:type", Function),
|
|
969
|
+
__metadata("design:paramtypes", [Object]),
|
|
970
|
+
__metadata("design:returntype", Promise)
|
|
971
|
+
], ResourceAiAccessServiceClass.prototype, "getStatusForResource", null);
|
|
972
|
+
__decorate([
|
|
973
|
+
CaptureSpan(),
|
|
974
|
+
__metadata("design:type", Function),
|
|
975
|
+
__metadata("design:paramtypes", [Object]),
|
|
976
|
+
__metadata("design:returntype", Promise)
|
|
977
|
+
], ResourceAiAccessServiceClass.prototype, "getStatusesForResources", null);
|
|
978
|
+
__decorate([
|
|
979
|
+
CaptureSpan(),
|
|
980
|
+
__metadata("design:type", Function),
|
|
981
|
+
__metadata("design:paramtypes", [Object]),
|
|
982
|
+
__metadata("design:returntype", Promise)
|
|
983
|
+
], ResourceAiAccessServiceClass.prototype, "loadResource", null);
|
|
984
|
+
__decorate([
|
|
985
|
+
CaptureSpan(),
|
|
986
|
+
__metadata("design:type", Function),
|
|
987
|
+
__metadata("design:paramtypes", [Object]),
|
|
988
|
+
__metadata("design:returntype", Promise)
|
|
989
|
+
], ResourceAiAccessServiceClass.prototype, "loadResources", null);
|
|
990
|
+
__decorate([
|
|
991
|
+
CaptureSpan(),
|
|
992
|
+
__metadata("design:type", Function),
|
|
993
|
+
__metadata("design:paramtypes", [Object]),
|
|
994
|
+
__metadata("design:returntype", Promise)
|
|
995
|
+
], ResourceAiAccessServiceClass.prototype, "findAgentRows", null);
|
|
996
|
+
__decorate([
|
|
997
|
+
CaptureSpan(),
|
|
998
|
+
__metadata("design:type", Function),
|
|
999
|
+
__metadata("design:paramtypes", [Object]),
|
|
1000
|
+
__metadata("design:returntype", Promise)
|
|
1001
|
+
], ResourceAiAccessServiceClass.prototype, "loadAccessTarget", null);
|
|
1002
|
+
__decorate([
|
|
1003
|
+
CaptureSpan(),
|
|
1004
|
+
__metadata("design:type", Function),
|
|
1005
|
+
__metadata("design:paramtypes", [Object]),
|
|
1006
|
+
__metadata("design:returntype", Promise)
|
|
1007
|
+
], ResourceAiAccessServiceClass.prototype, "getResourcesForSubject", null);
|
|
1008
|
+
__decorate([
|
|
1009
|
+
CaptureSpan(),
|
|
1010
|
+
__metadata("design:type", Function),
|
|
1011
|
+
__metadata("design:paramtypes", [Object]),
|
|
1012
|
+
__metadata("design:returntype", Promise)
|
|
1013
|
+
], ResourceAiAccessServiceClass.prototype, "getStatusesForSubject", null);
|
|
1014
|
+
__decorate([
|
|
1015
|
+
CaptureSpan(),
|
|
1016
|
+
__metadata("design:type", Function),
|
|
1017
|
+
__metadata("design:paramtypes", [Object]),
|
|
1018
|
+
__metadata("design:returntype", Promise)
|
|
1019
|
+
], ResourceAiAccessServiceClass.prototype, "assertCallerMayChangeResource", null);
|
|
1020
|
+
__decorate([
|
|
1021
|
+
CaptureSpan(),
|
|
1022
|
+
__metadata("design:type", Function),
|
|
1023
|
+
__metadata("design:paramtypes", [Object]),
|
|
1024
|
+
__metadata("design:returntype", Promise)
|
|
1025
|
+
], ResourceAiAccessServiceClass.prototype, "recordCommandOutcome", null);
|
|
1026
|
+
const ResourceAiAccessService = new ResourceAiAccessServiceClass();
|
|
1027
|
+
export default ResourceAiAccessService;
|
|
1028
|
+
//# sourceMappingURL=ResourceAiAccessService.js.map
|