@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,717 @@
|
|
|
1
|
+
import CommandPlanExecutor, {
|
|
2
|
+
CommandPlanRollbackOutcome,
|
|
3
|
+
} from "../../../../Server/Utils/AutoRemediation/CommandPlanExecutor";
|
|
4
|
+
import AutoRemediationSuggestionService from "../../../../Server/Services/AutoRemediationSuggestionService";
|
|
5
|
+
import IncidentFeedService from "../../../../Server/Services/IncidentFeedService";
|
|
6
|
+
import KubernetesClusterAiAccessService from "../../../../Server/Services/KubernetesClusterAiAccessService";
|
|
7
|
+
import ResourceAiAccessService from "../../../../Server/Services/ResourceAiAccessService";
|
|
8
|
+
import RunnerJobService from "../../../../Server/Services/RunnerJobService";
|
|
9
|
+
import Semaphore, {
|
|
10
|
+
SemaphoreMutex,
|
|
11
|
+
} from "../../../../Server/Infrastructure/Semaphore";
|
|
12
|
+
import logger from "../../../../Server/Utils/Logger";
|
|
13
|
+
import AutoRemediationSuggestion from "../../../../Models/DatabaseModels/AutoRemediationSuggestion";
|
|
14
|
+
import RunnerJob from "../../../../Models/DatabaseModels/RunnerJob";
|
|
15
|
+
import AutoRemediationSuggestionStatus from "../../../../Types/AutoRemediation/AutoRemediationSuggestionStatus";
|
|
16
|
+
import AutoRemediationSuggestionType from "../../../../Types/AutoRemediation/AutoRemediationSuggestionType";
|
|
17
|
+
import AutoRemediationVerificationStatus from "../../../../Types/AutoRemediation/AutoRemediationVerificationStatus";
|
|
18
|
+
import { AiRemediationRollbackStatus } from "../../../../Types/AutoRemediation/AiRemediationCommandPlan";
|
|
19
|
+
import AiResourceType from "../../../../Types/ResourceAiAgent/AiResourceType";
|
|
20
|
+
import {
|
|
21
|
+
ResourceAiAccessStatus,
|
|
22
|
+
ResourceAiAgentPosture,
|
|
23
|
+
ResourceAiRemediationMode,
|
|
24
|
+
} from "../../../../Types/ResourceAiAgent/ResourceAiAccess";
|
|
25
|
+
import RunnerJobOrigin from "../../../../Types/Runbook/RunnerJobOrigin";
|
|
26
|
+
import RunnerJobStatus from "../../../../Types/Runbook/RunnerJobStatus";
|
|
27
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
28
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
* Contract under test — the approved-plan executor and the rollback arm for
|
|
32
|
+
* ResourceCommand steps (a command on a Docker or Podman host, a Docker
|
|
33
|
+
* Swarm, Proxmox, VMware or Ceph cluster, a database server or a host,
|
|
34
|
+
* through its resource AI agent), mirroring the kubectl branches:
|
|
35
|
+
*
|
|
36
|
+
* - a command is enqueued through enqueueAiResourceCommand — never a Runner
|
|
37
|
+
* lane — for the plan's resource and the agent it was composed for, with
|
|
38
|
+
* the approved (or rollback) step id;
|
|
39
|
+
* - right before it is enqueued the resource's AI page is read again: a
|
|
40
|
+
* resource deleted, not ready, reached through another agent, or whose
|
|
41
|
+
* agent's write scope refuses the command stops it (Failed, never sent);
|
|
42
|
+
* a status that cannot be read stops it too; the policy's Denied tier is
|
|
43
|
+
* the execution-time floor (never the bash denylist);
|
|
44
|
+
* - the resource's "Last error" records a success, or a failure about its
|
|
45
|
+
* access only;
|
|
46
|
+
* - rollbacks run in REVERSE order and must be allowed unattended under the
|
|
47
|
+
* resource's CURRENT mode and allowlist, else they are left for a human;
|
|
48
|
+
* - a resource command recorded as failed that may nonetheless have run is
|
|
49
|
+
* read back from its job: succeeded-after-all is rolled back, unknown is
|
|
50
|
+
* left for a human, never-ran owes nothing.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
const SUGGESTION_ID: ObjectID = new ObjectID(
|
|
54
|
+
"77777777-7777-4777-8777-777777777777",
|
|
55
|
+
);
|
|
56
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
57
|
+
"22222222-2222-4222-8222-222222222222",
|
|
58
|
+
);
|
|
59
|
+
const INCIDENT_ID: ObjectID = new ObjectID(
|
|
60
|
+
"cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
61
|
+
);
|
|
62
|
+
const AI_RUN_ID: ObjectID = new ObjectID(
|
|
63
|
+
"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
64
|
+
);
|
|
65
|
+
const RESOURCE_ID: string = "33333333-3333-4333-8333-333333333333";
|
|
66
|
+
const AGENT_ID: string = "44444444-4444-4444-8444-444444444444";
|
|
67
|
+
const OTHER_AGENT_ID: string = "45454545-4545-4545-8545-454545454545";
|
|
68
|
+
const JOB_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
|
|
69
|
+
const FORWARD_JOB_ID: ObjectID = new ObjectID(
|
|
70
|
+
"11111111-1111-4111-8111-111111111111",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
function posture(
|
|
74
|
+
overrides: Partial<ResourceAiAgentPosture> = {},
|
|
75
|
+
): ResourceAiAgentPosture {
|
|
76
|
+
return {
|
|
77
|
+
resourceType: AiResourceType.DockerHost,
|
|
78
|
+
resourceIdentifier: "web-1",
|
|
79
|
+
allowWrites: true,
|
|
80
|
+
writeTargets: [],
|
|
81
|
+
protectedTargets: [],
|
|
82
|
+
reachable: true,
|
|
83
|
+
...overrides,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resource(
|
|
88
|
+
overrides: Partial<ResourceAiAccessStatus> = {},
|
|
89
|
+
postureOverrides: Partial<ResourceAiAgentPosture> = {},
|
|
90
|
+
): ResourceAiAccessStatus {
|
|
91
|
+
return {
|
|
92
|
+
resourceType: AiResourceType.DockerHost,
|
|
93
|
+
resourceId: RESOURCE_ID,
|
|
94
|
+
resourceName: "web-1",
|
|
95
|
+
isAiInvestigationEnabled: true,
|
|
96
|
+
aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
|
|
97
|
+
aiCommandAllowlist: [],
|
|
98
|
+
agent: {
|
|
99
|
+
agentId: AGENT_ID,
|
|
100
|
+
connectionStatus: "connected",
|
|
101
|
+
isOnline: true,
|
|
102
|
+
posture: posture(postureOverrides),
|
|
103
|
+
},
|
|
104
|
+
gaps: [],
|
|
105
|
+
isInvestigationReady: true,
|
|
106
|
+
isRemediationReady: true,
|
|
107
|
+
...overrides,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function resourceCommand(
|
|
112
|
+
overrides: Partial<Record<string, unknown>> = {},
|
|
113
|
+
): Record<string, unknown> {
|
|
114
|
+
return {
|
|
115
|
+
sequence: 1,
|
|
116
|
+
stepType: "ResourceCommand",
|
|
117
|
+
runnerId: AGENT_ID,
|
|
118
|
+
runnerNameSnapshot: "Docker AI agent",
|
|
119
|
+
resourceType: AiResourceType.DockerHost,
|
|
120
|
+
resourceId: RESOURCE_ID,
|
|
121
|
+
resourceNameSnapshot: "web-1",
|
|
122
|
+
resourceCommandTier: "SafeWrite",
|
|
123
|
+
command: "docker restart web",
|
|
124
|
+
rollbackCommand: "docker start web",
|
|
125
|
+
timeoutInMs: 30000,
|
|
126
|
+
rationale: "the web container is wedged",
|
|
127
|
+
expectedEffect: "web serves again",
|
|
128
|
+
policyVerdict: "RequiresApproval",
|
|
129
|
+
...overrides,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function approvedSuggestion(
|
|
134
|
+
commands: Array<Record<string, unknown>>,
|
|
135
|
+
): AutoRemediationSuggestion {
|
|
136
|
+
return {
|
|
137
|
+
id: SUGGESTION_ID,
|
|
138
|
+
_id: SUGGESTION_ID.toString(),
|
|
139
|
+
projectId: PROJECT_ID,
|
|
140
|
+
incidentId: INCIDENT_ID,
|
|
141
|
+
aiRunId: AI_RUN_ID,
|
|
142
|
+
status: AutoRemediationSuggestionStatus.Approved,
|
|
143
|
+
suggestionType: AutoRemediationSuggestionType.CommandPlan,
|
|
144
|
+
verificationStatus: AutoRemediationVerificationStatus.Pending,
|
|
145
|
+
verificationDeadlineAt: new Date(Date.now() + 15 * 60 * 1000),
|
|
146
|
+
ruleNameSnapshot: 'AI remediation for Docker host "web-1"',
|
|
147
|
+
resourceType: AiResourceType.DockerHost,
|
|
148
|
+
resourceId: new ObjectID(RESOURCE_ID),
|
|
149
|
+
commandPlan: { commands, executionStatus: "NotStarted" },
|
|
150
|
+
} as unknown as AutoRemediationSuggestion;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function failedSuggestion(
|
|
154
|
+
commands: Array<Record<string, unknown>>,
|
|
155
|
+
): AutoRemediationSuggestion {
|
|
156
|
+
return {
|
|
157
|
+
...(approvedSuggestion(commands) as unknown as Record<string, unknown>),
|
|
158
|
+
status: AutoRemediationSuggestionStatus.AutoExecuted,
|
|
159
|
+
verificationStatus: AutoRemediationVerificationStatus.Failed,
|
|
160
|
+
commandPlan: { commands, executionStatus: "Completed" },
|
|
161
|
+
} as unknown as AutoRemediationSuggestion;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let update: jest.SpyInstance;
|
|
165
|
+
let resourceEnqueue: jest.SpyInstance;
|
|
166
|
+
let kubectlEnqueue: jest.SpyInstance;
|
|
167
|
+
let bashEnqueue: jest.SpyInstance;
|
|
168
|
+
let poll: jest.SpyInstance;
|
|
169
|
+
let liveStatus: jest.SpyInstance;
|
|
170
|
+
let recordOutcome: jest.SpyInstance;
|
|
171
|
+
let jobRead: jest.SpyInstance;
|
|
172
|
+
let feed: jest.SpyInstance;
|
|
173
|
+
|
|
174
|
+
beforeEach(() => {
|
|
175
|
+
jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
176
|
+
return undefined;
|
|
177
|
+
});
|
|
178
|
+
jest.spyOn(logger, "warn").mockImplementation((): void => {
|
|
179
|
+
return undefined;
|
|
180
|
+
});
|
|
181
|
+
jest
|
|
182
|
+
.spyOn(Semaphore, "lock")
|
|
183
|
+
.mockResolvedValue({ id: "plan-lock" } as unknown as SemaphoreMutex);
|
|
184
|
+
jest.spyOn(Semaphore, "release").mockResolvedValue(undefined);
|
|
185
|
+
jest
|
|
186
|
+
.spyOn(AutoRemediationSuggestionService, "findOneBy")
|
|
187
|
+
.mockResolvedValue(null);
|
|
188
|
+
update = jest
|
|
189
|
+
.spyOn(AutoRemediationSuggestionService, "updateOneById")
|
|
190
|
+
.mockResolvedValue(undefined as never);
|
|
191
|
+
resourceEnqueue = jest
|
|
192
|
+
.spyOn(RunnerJobService, "enqueueAiResourceCommand")
|
|
193
|
+
.mockResolvedValue({
|
|
194
|
+
id: JOB_ID,
|
|
195
|
+
_id: JOB_ID.toString(),
|
|
196
|
+
status: RunnerJobStatus.Pending,
|
|
197
|
+
} as unknown as RunnerJob);
|
|
198
|
+
kubectlEnqueue = jest.spyOn(RunnerJobService, "enqueueAiKubectlCommand");
|
|
199
|
+
bashEnqueue = jest.spyOn(RunnerJobService, "enqueueAiCommand");
|
|
200
|
+
poll = jest.spyOn(RunnerJobService, "pollUntilTerminal").mockResolvedValue({
|
|
201
|
+
id: JOB_ID,
|
|
202
|
+
_id: JOB_ID.toString(),
|
|
203
|
+
status: RunnerJobStatus.Succeeded,
|
|
204
|
+
exitCode: 0,
|
|
205
|
+
output: "web",
|
|
206
|
+
} as unknown as RunnerJob);
|
|
207
|
+
liveStatus = jest
|
|
208
|
+
.spyOn(ResourceAiAccessService, "getStatusForResource")
|
|
209
|
+
.mockResolvedValue(resource());
|
|
210
|
+
recordOutcome = jest
|
|
211
|
+
.spyOn(ResourceAiAccessService, "recordCommandOutcome")
|
|
212
|
+
.mockResolvedValue(undefined);
|
|
213
|
+
jobRead = jest.spyOn(RunnerJobService, "findOneById");
|
|
214
|
+
feed = jest
|
|
215
|
+
.spyOn(IncidentFeedService, "createIncidentFeedItem")
|
|
216
|
+
.mockResolvedValue(undefined as never);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
afterEach(() => {
|
|
220
|
+
jest.restoreAllMocks();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
function lastPlan(): Record<string, unknown> {
|
|
224
|
+
const call: Record<string, unknown> = update.mock.calls[
|
|
225
|
+
update.mock.calls.length - 1
|
|
226
|
+
]![0] as Record<string, unknown>;
|
|
227
|
+
return (call["data"] as Record<string, unknown>)["commandPlan"] as Record<
|
|
228
|
+
string,
|
|
229
|
+
unknown
|
|
230
|
+
>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function commandAt(index: number): Record<string, unknown> {
|
|
234
|
+
return (lastPlan()["commands"] as Array<Record<string, unknown>>)[index]!;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function lastFeedMarkdown(): string {
|
|
238
|
+
return (
|
|
239
|
+
feed.mock.calls[feed.mock.calls.length - 1]![0] as {
|
|
240
|
+
feedInfoInMarkdown: string;
|
|
241
|
+
}
|
|
242
|
+
).feedInfoInMarkdown;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function executeApproved(
|
|
246
|
+
commands: Array<Record<string, unknown>>,
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
jest
|
|
249
|
+
.spyOn(AutoRemediationSuggestionService, "findOneById")
|
|
250
|
+
.mockResolvedValue(approvedSuggestion(commands));
|
|
251
|
+
|
|
252
|
+
await CommandPlanExecutor.executeApprovedPlan({
|
|
253
|
+
suggestionId: SUGGESTION_ID,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
describe("CommandPlanExecutor.executeApprovedPlan — resource commands", () => {
|
|
258
|
+
it("enqueues each command through the resource chokepoint, for the plan's resource and agent", async () => {
|
|
259
|
+
await executeApproved([
|
|
260
|
+
resourceCommand(),
|
|
261
|
+
resourceCommand({ sequence: 2, command: "docker restart api" }),
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
expect(bashEnqueue).not.toHaveBeenCalled();
|
|
265
|
+
expect(kubectlEnqueue).not.toHaveBeenCalled();
|
|
266
|
+
expect(resourceEnqueue).toHaveBeenCalledTimes(2);
|
|
267
|
+
expect(resourceEnqueue.mock.calls[0]![0]).toEqual({
|
|
268
|
+
projectId: PROJECT_ID,
|
|
269
|
+
aiRunId: AI_RUN_ID,
|
|
270
|
+
origin: RunnerJobOrigin.AiRemediation,
|
|
271
|
+
autoRemediationSuggestionId: SUGGESTION_ID,
|
|
272
|
+
resourceType: AiResourceType.DockerHost,
|
|
273
|
+
resourceId: new ObjectID(RESOURCE_ID),
|
|
274
|
+
stepId: "ai-approved-1",
|
|
275
|
+
targetResourceAiAgentId: new ObjectID(AGENT_ID),
|
|
276
|
+
command: "docker restart web",
|
|
277
|
+
timeoutInMs: 30000,
|
|
278
|
+
claimTimeoutInMs: 60000,
|
|
279
|
+
});
|
|
280
|
+
expect(resourceEnqueue.mock.calls[1]![0]).toMatchObject({
|
|
281
|
+
stepId: "ai-approved-2",
|
|
282
|
+
command: "docker restart api",
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
expect(commandAt(0)["execution"]).toMatchObject({
|
|
286
|
+
status: "Succeeded",
|
|
287
|
+
runnerJobId: JOB_ID.toString(),
|
|
288
|
+
});
|
|
289
|
+
expect(lastPlan()["executionStatus"]).toBe("Completed");
|
|
290
|
+
expect(recordOutcome).toHaveBeenCalledWith({
|
|
291
|
+
resourceType: AiResourceType.DockerHost,
|
|
292
|
+
resourceId: new ObjectID(RESOURCE_ID),
|
|
293
|
+
succeeded: true,
|
|
294
|
+
errorMessage: undefined,
|
|
295
|
+
});
|
|
296
|
+
expect(lastFeedMarkdown()).toContain("Approved AI command plan executed");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("stores the output through the resource redaction", async () => {
|
|
300
|
+
poll.mockResolvedValue({
|
|
301
|
+
id: JOB_ID,
|
|
302
|
+
status: RunnerJobStatus.Succeeded,
|
|
303
|
+
exitCode: 0,
|
|
304
|
+
output:
|
|
305
|
+
'[{"Config":{"Env":["DB_PASSWORD=hunter2hunter2","PATH=/usr/bin"]}}]',
|
|
306
|
+
} as unknown as RunnerJob);
|
|
307
|
+
|
|
308
|
+
await executeApproved([
|
|
309
|
+
resourceCommand({ command: "docker container inspect web" }),
|
|
310
|
+
]);
|
|
311
|
+
|
|
312
|
+
const output: string = String(
|
|
313
|
+
(commandAt(0)["execution"] as Record<string, unknown>)["output"],
|
|
314
|
+
);
|
|
315
|
+
expect(output).not.toContain("hunter2hunter2");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it.each([
|
|
319
|
+
[
|
|
320
|
+
"deleted",
|
|
321
|
+
null,
|
|
322
|
+
'Refused at execution time: Docker host "web-1" no longer exists in this project.',
|
|
323
|
+
],
|
|
324
|
+
[
|
|
325
|
+
"no longer ready",
|
|
326
|
+
resource({
|
|
327
|
+
isRemediationReady: false,
|
|
328
|
+
gaps: [
|
|
329
|
+
{
|
|
330
|
+
code: "ai_agent_offline",
|
|
331
|
+
title: "The Docker AI agent is offline",
|
|
332
|
+
nextStep: "Check it.",
|
|
333
|
+
blocksInvestigation: true,
|
|
334
|
+
blocksRemediation: true,
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
}),
|
|
338
|
+
'Docker host "web-1" no longer allows AI remediation (The Docker AI agent is offline)',
|
|
339
|
+
],
|
|
340
|
+
[
|
|
341
|
+
"reached through another agent",
|
|
342
|
+
resource({
|
|
343
|
+
agent: {
|
|
344
|
+
agentId: OTHER_AGENT_ID,
|
|
345
|
+
connectionStatus: "connected",
|
|
346
|
+
isOnline: true,
|
|
347
|
+
posture: posture(),
|
|
348
|
+
},
|
|
349
|
+
}),
|
|
350
|
+
"is no longer reached through the Docker AI agent this plan was composed for",
|
|
351
|
+
],
|
|
352
|
+
[
|
|
353
|
+
"guarded by an agent whose write scope refuses the command",
|
|
354
|
+
resource({}, { protectedTargets: ["web"] }),
|
|
355
|
+
"the Docker AI agent would refuse it:",
|
|
356
|
+
],
|
|
357
|
+
])(
|
|
358
|
+
"refuses a command whose resource is %s — never sent, the rest skipped",
|
|
359
|
+
async (
|
|
360
|
+
_label: string,
|
|
361
|
+
status: ResourceAiAccessStatus | null,
|
|
362
|
+
expected: string,
|
|
363
|
+
) => {
|
|
364
|
+
liveStatus.mockResolvedValue(status);
|
|
365
|
+
|
|
366
|
+
await executeApproved([
|
|
367
|
+
resourceCommand(),
|
|
368
|
+
resourceCommand({ sequence: 2, command: "docker restart api" }),
|
|
369
|
+
]);
|
|
370
|
+
|
|
371
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
372
|
+
const execution: Record<string, unknown> = commandAt(0)[
|
|
373
|
+
"execution"
|
|
374
|
+
] as Record<string, unknown>;
|
|
375
|
+
expect(execution["status"]).toBe("Failed");
|
|
376
|
+
expect(execution["errorMessage"]).toContain(expected);
|
|
377
|
+
expect(
|
|
378
|
+
(commandAt(1)["execution"] as Record<string, unknown>)["status"],
|
|
379
|
+
).toBe("Skipped");
|
|
380
|
+
expect(lastPlan()["executionStatus"]).toBe("Failed");
|
|
381
|
+
// A refusal before the enqueue never becomes the resource's last error.
|
|
382
|
+
expect(recordOutcome).not.toHaveBeenCalled();
|
|
383
|
+
},
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
it("refuses when the resource's status cannot be read (fail closed)", async () => {
|
|
387
|
+
liveStatus.mockRejectedValue(new Error("db down"));
|
|
388
|
+
|
|
389
|
+
await executeApproved([resourceCommand()]);
|
|
390
|
+
|
|
391
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
392
|
+
expect(
|
|
393
|
+
(commandAt(0)["execution"] as Record<string, unknown>)["errorMessage"],
|
|
394
|
+
).toContain(
|
|
395
|
+
'could not confirm that Docker host "web-1" still allows AI remediation',
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("refuses a command the resource policy denies now — the Denied tier is the floor, never the bash denylist", async () => {
|
|
400
|
+
await executeApproved([resourceCommand({ command: "docker exec web sh" })]);
|
|
401
|
+
|
|
402
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
403
|
+
expect(
|
|
404
|
+
(commandAt(0)["execution"] as Record<string, unknown>)["errorMessage"],
|
|
405
|
+
).toContain("Refused by the remediation command policy at execution time");
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("records only an ACCESS failure as the resource's last error", async () => {
|
|
409
|
+
poll.mockResolvedValue({
|
|
410
|
+
id: JOB_ID,
|
|
411
|
+
status: RunnerJobStatus.Failed,
|
|
412
|
+
exitCode: 1,
|
|
413
|
+
output:
|
|
414
|
+
"[stderr]\nCannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?",
|
|
415
|
+
errorMessage: "Exit code 1",
|
|
416
|
+
} as unknown as RunnerJob);
|
|
417
|
+
|
|
418
|
+
await executeApproved([resourceCommand()]);
|
|
419
|
+
|
|
420
|
+
expect(recordOutcome).toHaveBeenCalledWith(
|
|
421
|
+
expect.objectContaining({ succeeded: false }),
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
recordOutcome.mockClear();
|
|
425
|
+
poll.mockResolvedValue({
|
|
426
|
+
id: JOB_ID,
|
|
427
|
+
status: RunnerJobStatus.Failed,
|
|
428
|
+
exitCode: 1,
|
|
429
|
+
output: "[stderr]\nError response from daemon: No such container: web",
|
|
430
|
+
errorMessage: "Exit code 1",
|
|
431
|
+
} as unknown as RunnerJob);
|
|
432
|
+
|
|
433
|
+
await executeApproved([resourceCommand()]);
|
|
434
|
+
|
|
435
|
+
expect(recordOutcome).not.toHaveBeenCalled();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("a Kubernetes plan never reads a resource status", async () => {
|
|
439
|
+
jest
|
|
440
|
+
.spyOn(KubernetesClusterAiAccessService, "getStatusForCluster")
|
|
441
|
+
.mockResolvedValue(null);
|
|
442
|
+
|
|
443
|
+
await executeApproved([
|
|
444
|
+
{
|
|
445
|
+
sequence: 1,
|
|
446
|
+
stepType: "Kubectl",
|
|
447
|
+
runnerId: AGENT_ID,
|
|
448
|
+
runnerNameSnapshot: "Kubernetes AI agent",
|
|
449
|
+
kubernetesClusterId: RESOURCE_ID,
|
|
450
|
+
kubernetesClusterNameSnapshot: "prod-us",
|
|
451
|
+
command: "kubectl rollout restart deployment/web -n web",
|
|
452
|
+
timeoutInMs: 30000,
|
|
453
|
+
rationale: "r",
|
|
454
|
+
expectedEffect: "e",
|
|
455
|
+
policyVerdict: "RequiresApproval",
|
|
456
|
+
},
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
expect(liveStatus).not.toHaveBeenCalled();
|
|
460
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
describe("CommandPlanExecutor.executeRollback — resource commands", () => {
|
|
465
|
+
function executed(
|
|
466
|
+
overrides: Partial<Record<string, unknown>> = {},
|
|
467
|
+
): Record<string, unknown> {
|
|
468
|
+
return resourceCommand({
|
|
469
|
+
wasAutoExecuted: true,
|
|
470
|
+
policyVerdict: "AutoApproved",
|
|
471
|
+
execution: {
|
|
472
|
+
status: "Succeeded",
|
|
473
|
+
runnerJobId: FORWARD_JOB_ID.toString(),
|
|
474
|
+
exitCode: 0,
|
|
475
|
+
},
|
|
476
|
+
...overrides,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function rollBack(
|
|
481
|
+
commands: Array<Record<string, unknown>>,
|
|
482
|
+
): Promise<CommandPlanRollbackOutcome> {
|
|
483
|
+
return CommandPlanExecutor.executeRollback({
|
|
484
|
+
suggestion: failedSuggestion(commands),
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
it("runs the undos in REVERSE order through the resource chokepoint with rollback step ids", async () => {
|
|
489
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
490
|
+
executed({
|
|
491
|
+
command: "docker stop web",
|
|
492
|
+
rollbackCommand: "docker start web",
|
|
493
|
+
}),
|
|
494
|
+
executed({
|
|
495
|
+
sequence: 2,
|
|
496
|
+
command: "docker pause api",
|
|
497
|
+
rollbackCommand: "docker unpause api",
|
|
498
|
+
}),
|
|
499
|
+
]);
|
|
500
|
+
|
|
501
|
+
expect(resourceEnqueue).toHaveBeenCalledTimes(2);
|
|
502
|
+
expect(resourceEnqueue.mock.calls[0]![0]).toMatchObject({
|
|
503
|
+
stepId: "ai-rollback-2",
|
|
504
|
+
command: "docker unpause api",
|
|
505
|
+
resourceType: AiResourceType.DockerHost,
|
|
506
|
+
targetResourceAiAgentId: new ObjectID(AGENT_ID),
|
|
507
|
+
});
|
|
508
|
+
expect(resourceEnqueue.mock.calls[1]![0]).toMatchObject({
|
|
509
|
+
stepId: "ai-rollback-1",
|
|
510
|
+
command: "docker start web",
|
|
511
|
+
});
|
|
512
|
+
expect(outcome.rollbackStatus).toBe(AiRemediationRollbackStatus.Completed);
|
|
513
|
+
expect(outcome.rolledBack).toBe(2);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it("leaves a riskier undo for a human once the resource no longer bypasses approvals", async () => {
|
|
517
|
+
// The plan ran under Bypass approval; the operator has since moved to Automatic.
|
|
518
|
+
liveStatus.mockResolvedValue(
|
|
519
|
+
resource({ aiRemediationMode: ResourceAiRemediationMode.Automatic }),
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
523
|
+
executed({
|
|
524
|
+
command: "docker start web",
|
|
525
|
+
rollbackCommand: "docker stop web",
|
|
526
|
+
}),
|
|
527
|
+
]);
|
|
528
|
+
|
|
529
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
530
|
+
const rollback: Record<string, unknown> = commandAt(0)[
|
|
531
|
+
"rollbackExecution"
|
|
532
|
+
] as Record<string, unknown>;
|
|
533
|
+
expect(rollback["status"]).toBe("Skipped");
|
|
534
|
+
expect(rollback["errorMessage"]).toContain(
|
|
535
|
+
'Docker host "web-1" no longer allows this change unattended (its AI remediation mode is now Automatic)',
|
|
536
|
+
);
|
|
537
|
+
expect(rollback["errorMessage"]).toContain(
|
|
538
|
+
"Undo it manually: docker stop web",
|
|
539
|
+
);
|
|
540
|
+
expect(outcome.rollbackStatus).toBe(AiRemediationRollbackStatus.Failed);
|
|
541
|
+
expect(outcome.leftForHuman).toBe(1);
|
|
542
|
+
expect(lastFeedMarkdown()).toContain(
|
|
543
|
+
"the Docker host no longer allows them unattended",
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it("runs a riskier undo the resource still allows unattended (Bypass approval)", async () => {
|
|
548
|
+
liveStatus.mockResolvedValue(
|
|
549
|
+
resource({ aiRemediationMode: ResourceAiRemediationMode.BypassApproval }),
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
553
|
+
executed({
|
|
554
|
+
command: "docker start web",
|
|
555
|
+
rollbackCommand: "docker stop web",
|
|
556
|
+
}),
|
|
557
|
+
]);
|
|
558
|
+
|
|
559
|
+
expect(resourceEnqueue).toHaveBeenCalledTimes(1);
|
|
560
|
+
expect(outcome.rollbackStatus).toBe(AiRemediationRollbackStatus.Completed);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("leaves every undo for a human when the resource stopped allowing remediation", async () => {
|
|
564
|
+
liveStatus.mockResolvedValue(resource({ isRemediationReady: false }));
|
|
565
|
+
|
|
566
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([executed()]);
|
|
567
|
+
|
|
568
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
569
|
+
expect(
|
|
570
|
+
(commandAt(0)["rollbackExecution"] as Record<string, unknown>)[
|
|
571
|
+
"errorMessage"
|
|
572
|
+
],
|
|
573
|
+
).toContain('Docker host "web-1" no longer allows AI remediation');
|
|
574
|
+
expect(outcome.leftForHuman).toBe(1);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it("refuses an undo the resource policy denies", async () => {
|
|
578
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
579
|
+
executed({ rollbackCommand: "docker rm -f web" }),
|
|
580
|
+
]);
|
|
581
|
+
|
|
582
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
583
|
+
expect(
|
|
584
|
+
commandAt(0)["rollbackExecution"] as Record<string, unknown>,
|
|
585
|
+
).toMatchObject({ status: "Failed" });
|
|
586
|
+
expect(outcome.failed).toBe(1);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
describe("a failed resource command that may have run", () => {
|
|
590
|
+
function failedForward(
|
|
591
|
+
overrides: Partial<Record<string, unknown>> = {},
|
|
592
|
+
): Record<string, unknown> {
|
|
593
|
+
return executed({
|
|
594
|
+
execution: {
|
|
595
|
+
status: "Failed",
|
|
596
|
+
runnerJobId: FORWARD_JOB_ID.toString(),
|
|
597
|
+
errorMessage:
|
|
598
|
+
"The Docker AI agent took this command but did not report a result in time.",
|
|
599
|
+
},
|
|
600
|
+
...overrides,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
it("leaves the undo of a job the agent claimed and went silent on for a human", async () => {
|
|
605
|
+
jobRead.mockResolvedValue({
|
|
606
|
+
id: FORWARD_JOB_ID,
|
|
607
|
+
status: RunnerJobStatus.TimedOut,
|
|
608
|
+
claimedAt: new Date(),
|
|
609
|
+
} as unknown as RunnerJob);
|
|
610
|
+
|
|
611
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
612
|
+
failedForward(),
|
|
613
|
+
]);
|
|
614
|
+
|
|
615
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
616
|
+
const rollback: Record<string, unknown> = commandAt(0)[
|
|
617
|
+
"rollbackExecution"
|
|
618
|
+
] as Record<string, unknown>;
|
|
619
|
+
expect(rollback["status"]).toBe("Skipped");
|
|
620
|
+
expect(rollback["errorMessage"]).toContain(
|
|
621
|
+
"the command reached the resource's AI agent but it did not report a finished result, so whether it changed the Docker host is unknown",
|
|
622
|
+
);
|
|
623
|
+
expect(rollback["errorMessage"]).toContain(
|
|
624
|
+
"undo it manually: docker start web",
|
|
625
|
+
);
|
|
626
|
+
expect(outcome.rollbackStatus).toBe(AiRemediationRollbackStatus.Failed);
|
|
627
|
+
// Read with the claim columns.
|
|
628
|
+
expect(
|
|
629
|
+
(jobRead.mock.calls[0]![0] as { select: Record<string, unknown> })
|
|
630
|
+
.select,
|
|
631
|
+
).toEqual(
|
|
632
|
+
expect.objectContaining({
|
|
633
|
+
claimedAt: true,
|
|
634
|
+
startedAt: true,
|
|
635
|
+
assignedAgentId: true,
|
|
636
|
+
}),
|
|
637
|
+
);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it("leaves the undo of a program the agent killed at its timeout for a human", async () => {
|
|
641
|
+
jobRead.mockResolvedValue({
|
|
642
|
+
id: FORWARD_JOB_ID,
|
|
643
|
+
status: RunnerJobStatus.Failed,
|
|
644
|
+
errorMessage: "Killed (timeout 30s)",
|
|
645
|
+
claimedAt: new Date(),
|
|
646
|
+
} as unknown as RunnerJob);
|
|
647
|
+
|
|
648
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
649
|
+
failedForward(),
|
|
650
|
+
]);
|
|
651
|
+
|
|
652
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
653
|
+
expect(outcome.leftForHuman).toBe(1);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it("rolls back a job that succeeded after all", async () => {
|
|
657
|
+
jobRead.mockResolvedValue({
|
|
658
|
+
id: FORWARD_JOB_ID,
|
|
659
|
+
status: RunnerJobStatus.Succeeded,
|
|
660
|
+
exitCode: 0,
|
|
661
|
+
output: "web",
|
|
662
|
+
} as unknown as RunnerJob);
|
|
663
|
+
|
|
664
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
665
|
+
failedForward(),
|
|
666
|
+
]);
|
|
667
|
+
|
|
668
|
+
expect(resourceEnqueue).toHaveBeenCalledTimes(1);
|
|
669
|
+
expect(resourceEnqueue.mock.calls[0]![0]).toMatchObject({
|
|
670
|
+
stepId: "ai-rollback-1",
|
|
671
|
+
command: "docker start web",
|
|
672
|
+
});
|
|
673
|
+
expect(outcome.rollbackStatus).toBe(
|
|
674
|
+
AiRemediationRollbackStatus.Completed,
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it.each([
|
|
679
|
+
["never claimed", { status: RunnerJobStatus.TimedOut }],
|
|
680
|
+
[
|
|
681
|
+
"refused before the program started",
|
|
682
|
+
{
|
|
683
|
+
status: RunnerJobStatus.Failed,
|
|
684
|
+
errorMessage: "Refused: the agent is read-only.",
|
|
685
|
+
claimedAt: new Date(),
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
[
|
|
689
|
+
"finished with an exit code",
|
|
690
|
+
{
|
|
691
|
+
status: RunnerJobStatus.Failed,
|
|
692
|
+
exitCode: 1,
|
|
693
|
+
output: "[stderr]\nNo such container: web",
|
|
694
|
+
claimedAt: new Date(),
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
])(
|
|
698
|
+
"owes no undo for a job that %s",
|
|
699
|
+
async (_label: string, job: Record<string, unknown>) => {
|
|
700
|
+
jobRead.mockResolvedValue({
|
|
701
|
+
id: FORWARD_JOB_ID,
|
|
702
|
+
...job,
|
|
703
|
+
} as unknown as RunnerJob);
|
|
704
|
+
|
|
705
|
+
const outcome: CommandPlanRollbackOutcome = await rollBack([
|
|
706
|
+
failedForward(),
|
|
707
|
+
]);
|
|
708
|
+
|
|
709
|
+
expect(resourceEnqueue).not.toHaveBeenCalled();
|
|
710
|
+
expect(commandAt(0)["rollbackExecution"]).toBeUndefined();
|
|
711
|
+
expect(outcome.rollbackStatus).toBe(
|
|
712
|
+
AiRemediationRollbackStatus.NotApplicable,
|
|
713
|
+
);
|
|
714
|
+
},
|
|
715
|
+
);
|
|
716
|
+
});
|
|
717
|
+
});
|