@oneuptime/common 14.0.0 → 14.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1270) hide show
  1. package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
  3. package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
  4. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
  6. package/Models/DatabaseModels/Index.ts +4 -0
  7. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  8. package/Models/DatabaseModels/Monitor.ts +48 -0
  9. package/Models/DatabaseModels/Runner.ts +5 -4
  10. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  11. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  12. package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
  13. package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
  14. package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
  15. package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
  16. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  17. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  18. package/Server/API/AIInvestigationAPI.ts +341 -0
  19. package/Server/API/AutoRemediationAPI.ts +184 -2
  20. package/Server/API/CommonAPI.ts +70 -0
  21. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  22. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  23. package/Server/API/MonitorAPI.ts +140 -0
  24. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  25. package/Server/API/SlackAPI.ts +447 -99
  26. package/Server/API/StatusPageAPI.ts +146 -26
  27. package/Server/API/TelemetryAPI.ts +216 -5
  28. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  29. package/Server/API/WorkflowVariableAPI.ts +156 -0
  30. package/Server/Infrastructure/GlobalCache.ts +35 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  39. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  40. package/Server/Middleware/TelemetryIngest.ts +47 -2
  41. package/Server/Middleware/UserAuthorization.ts +56 -2
  42. package/Server/Services/AIAgentService.ts +1 -0
  43. package/Server/Services/AccessTokenService.ts +121 -48
  44. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  45. package/Server/Services/AlertEpisodeService.ts +14 -1
  46. package/Server/Services/AlertService.ts +134 -22
  47. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  48. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  49. package/Server/Services/BillingService.ts +1 -0
  50. package/Server/Services/DatabaseService.ts +10 -0
  51. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  52. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  53. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  54. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  55. package/Server/Services/IncidentEpisodeService.ts +14 -1
  56. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  57. package/Server/Services/IncidentMemberService.ts +21 -0
  58. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  59. package/Server/Services/IncidentService.ts +120 -20
  60. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  61. package/Server/Services/IncidentTemplateService.ts +83 -3
  62. package/Server/Services/Index.ts +4 -0
  63. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  64. package/Server/Services/KubernetesClusterService.ts +1570 -1
  65. package/Server/Services/LogAggregationService.ts +14 -6
  66. package/Server/Services/MonitorService.ts +654 -54
  67. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  71. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  72. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  73. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  74. package/Server/Services/ProbeService.ts +7 -1
  75. package/Server/Services/ProjectService.ts +11 -1
  76. package/Server/Services/RunbookCredentialService.ts +26 -1
  77. package/Server/Services/RunbookSecretService.ts +36 -0
  78. package/Server/Services/RunnerJobService.ts +941 -23
  79. package/Server/Services/RunnerService.ts +439 -6
  80. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  81. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  82. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  83. package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
  84. package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
  85. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
  86. package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
  87. package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
  88. package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
  89. package/Server/Services/ServiceLevelObjectiveService.ts +42 -1
  90. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  91. package/Server/Services/StatusPageService.ts +301 -74
  92. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  93. package/Server/Services/TeamMemberService.ts +309 -7
  94. package/Server/Services/TraceAggregationService.ts +10 -6
  95. package/Server/Services/UserEmailService.ts +1 -0
  96. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  97. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  98. package/Server/Services/UserNotificationRuleService.ts +4 -0
  99. package/Server/Services/UserService.ts +122 -1
  100. package/Server/Services/WorkflowVariableService.ts +593 -8
  101. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  102. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  103. package/Server/Types/Database/QueryHelper.ts +22 -0
  104. package/Server/Types/Markdown.ts +502 -27
  105. package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
  106. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  107. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  108. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  109. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  110. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  111. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  112. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  113. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  114. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  115. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  116. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  117. package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
  118. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  119. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  120. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  121. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  122. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  123. package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
  124. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
  125. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
  126. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  127. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
  128. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  129. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  130. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  131. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  132. package/Server/Utils/Monitor/RootCauseList.ts +139 -0
  133. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  134. package/Server/Utils/PushNotificationUtil.ts +45 -0
  135. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  136. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
  137. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  138. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  139. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  140. package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
  141. package/Server/Utils/StartServer.ts +4 -3
  142. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  143. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  144. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  145. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  146. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  147. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  148. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  149. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  150. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  151. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  152. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  153. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  154. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  155. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  156. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  157. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  158. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  159. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  160. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  161. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  162. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  163. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  164. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  165. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  166. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  167. package/Server/Utils/Workspace/Workspace.ts +171 -54
  168. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  169. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  170. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  171. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  172. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  173. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  174. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  175. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  176. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  177. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  178. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  179. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  180. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  181. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  182. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  183. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  184. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  185. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  186. package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
  187. package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
  188. package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
  189. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  190. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  191. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  192. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  193. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  194. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  195. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  196. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  197. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
  198. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +606 -3
  199. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  200. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  201. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  202. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  203. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  204. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  205. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  206. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  207. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  208. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  209. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  210. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  211. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  212. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  213. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  214. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  215. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  216. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  217. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  218. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  219. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  220. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  221. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  222. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  223. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  224. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  225. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  226. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  227. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  228. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  229. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  230. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  231. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  232. package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
  233. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  234. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  235. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  236. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  237. package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
  238. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
  239. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
  240. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  241. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  242. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  243. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  244. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  245. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
  246. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  247. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  248. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  249. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  250. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  251. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  252. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  253. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  254. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  255. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  256. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  257. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  258. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  259. package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
  260. package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
  261. package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
  262. package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
  263. package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
  264. package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
  265. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  266. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  267. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  268. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  269. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  270. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  271. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  272. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  273. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  274. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  275. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  276. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  277. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  278. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  279. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  280. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  281. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  282. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  283. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  284. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
  285. package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
  286. package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
  287. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  288. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  289. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  290. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  291. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  292. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  293. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  294. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  295. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  296. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  297. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  298. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  299. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  300. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  301. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  302. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  303. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  304. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  305. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  306. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  307. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  308. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  309. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  310. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  311. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  312. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  313. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  314. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  315. package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
  316. package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
  317. package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
  318. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  319. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  320. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  321. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  322. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  323. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  324. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  325. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  326. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  327. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  328. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  329. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  330. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  331. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  332. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  333. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  334. package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
  335. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  336. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  337. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  338. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  339. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  340. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  341. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  342. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  343. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  344. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  345. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  346. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  347. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  348. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  349. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  350. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  351. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  352. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  353. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  354. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  355. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  356. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  357. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  358. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  359. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  360. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  361. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  362. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  363. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  364. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
  365. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  366. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +39 -1
  367. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  368. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  369. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  370. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  371. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  372. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  373. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  374. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  375. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  376. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1175 -0
  377. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
  378. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
  379. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
  380. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
  381. package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
  382. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +174 -0
  383. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
  384. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  385. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  386. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  387. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  388. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  389. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  390. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  391. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  392. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  393. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  394. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  395. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  396. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  397. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  398. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  399. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  400. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
  401. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  402. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  403. package/Tests/Server/Types/Markdown.test.ts +1035 -1
  404. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
  405. package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
  406. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  407. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  408. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  409. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  410. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  411. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  412. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  413. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  414. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  415. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  416. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  417. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  418. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  419. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  420. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  421. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  422. package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
  423. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  424. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  425. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  426. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  427. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  428. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  429. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
  430. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  431. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  432. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
  433. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1156 -0
  434. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
  435. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  436. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  437. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  438. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  439. package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
  440. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  441. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  442. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  443. package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
  444. package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
  445. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  446. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  447. package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
  448. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  449. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
  450. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  451. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  452. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  453. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  454. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  455. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  456. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  457. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  458. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  459. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  460. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  461. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  462. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  463. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  464. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  465. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  466. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  467. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  468. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  469. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  470. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  471. package/Tests/Types/API/Route.test.ts +56 -0
  472. package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
  473. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  474. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  475. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  476. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  477. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  478. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  479. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  480. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  481. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  482. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  483. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  484. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  485. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  486. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  487. package/Tests/Types/Timezone.test.ts +5 -0
  488. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  489. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  490. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  491. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  492. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  493. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  494. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  495. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  496. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  497. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  498. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  499. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  500. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  501. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  502. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  503. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  504. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  505. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  506. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  507. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  508. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  509. package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
  510. package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
  511. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  512. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  513. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  514. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  515. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  516. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  517. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  518. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  519. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  520. package/Tests/UI/Components/Icon.test.tsx +26 -0
  521. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  522. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  523. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  524. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  525. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  526. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  527. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  528. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  529. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  530. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  531. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  532. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  533. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  534. package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
  535. package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
  536. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  537. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  538. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  539. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  540. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  541. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  542. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  543. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  544. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  545. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  546. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  547. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  548. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  549. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  550. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  551. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  552. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  553. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  554. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  555. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  556. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  557. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  558. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  559. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  560. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  561. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  562. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  563. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  564. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  565. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  566. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  567. package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
  568. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  569. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  570. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  571. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  572. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  573. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  574. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  575. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  576. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  577. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  578. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  579. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  580. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  581. package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
  582. package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
  583. package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
  584. package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
  585. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  586. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  587. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  588. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  589. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  590. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  591. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  592. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  593. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  594. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  595. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  596. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  597. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  598. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  599. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  600. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  601. package/Types/API/Route.ts +19 -2
  602. package/Types/API/URL.ts +26 -2
  603. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  604. package/Types/Date.ts +121 -7
  605. package/Types/Email/EmailMessage.ts +8 -0
  606. package/Types/Email/EmailTemplateType.ts +1 -0
  607. package/Types/Exception/ExceptionMessages.ts +1 -0
  608. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  609. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  610. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  611. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  612. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  613. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  614. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
  615. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
  616. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  617. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  618. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  619. package/Types/Permission.ts +90 -0
  620. package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
  621. package/Types/Rules/RuleRun.ts +4 -0
  622. package/Types/Rum/SessionReplay.ts +55 -2
  623. package/Types/Rum/SessionReplayApi.ts +44 -1
  624. package/Types/Rum/UserFlow.ts +187 -0
  625. package/Types/Runbook/RunbookStepType.ts +10 -0
  626. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  627. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  628. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  629. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  630. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  631. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  632. package/Types/Timezone.ts +5 -0
  633. package/Types/TimezoneAlias.ts +385 -0
  634. package/Types/Workflow/Templates.ts +3041 -2
  635. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  636. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  637. package/Typings/Index.d.ts +24 -0
  638. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  639. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  640. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  641. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  642. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  643. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  644. package/UI/Components/Calendar/Calendar.css +83 -0
  645. package/UI/Components/Calendar/Calendar.tsx +136 -7
  646. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  647. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  648. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  649. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  650. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  651. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  652. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  653. package/UI/Components/Feed/FeedOptions.ts +427 -0
  654. package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
  655. package/UI/Components/Feed/useFeedItems.ts +28 -9
  656. package/UI/Components/Feed/useFeedOptions.ts +225 -0
  657. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  658. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  659. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  660. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  661. package/UI/Components/Icon/Icon.tsx +1 -1
  662. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  663. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  664. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  665. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  666. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  667. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  668. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  669. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  670. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  671. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  672. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  673. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  674. package/UI/Components/Page/ModelPage.tsx +59 -3
  675. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  676. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  677. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  678. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  679. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  680. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  681. package/UI/Utils/Timezone.ts +69 -28
  682. package/Utils/AI/InvestigationReport.ts +195 -25
  683. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  684. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  685. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  686. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  687. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  688. package/Utils/LabelRuleImportExport.ts +27 -9
  689. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  690. package/Utils/Monitor/MetricValueFormatter.ts +2 -2
  691. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  692. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  693. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  694. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  695. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  696. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  697. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  698. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  699. package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
  700. package/Utils/Rum/SessionIdentity.ts +44 -0
  701. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  702. package/Utils/Rum/UserFlow.ts +1527 -0
  703. package/Utils/Slo/SloDuration.ts +22 -6
  704. package/Utils/Slo/SloHealth.ts +8 -7
  705. package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
  706. package/Utils/Slo/SloOverviewText.ts +33 -0
  707. package/Utils/Slo/SloProjection.ts +4 -3
  708. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  709. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  710. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  711. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  712. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  713. package/Utils/Uptime/UptimeUtil.ts +122 -38
  714. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  715. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  716. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  717. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  718. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  719. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  720. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  721. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  722. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  723. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  724. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  725. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  726. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  727. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  728. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  729. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  730. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  731. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  732. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  733. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  734. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  735. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  736. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
  737. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
  738. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
  739. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
  740. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
  741. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
  742. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
  743. package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
  744. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  745. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  746. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  747. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  748. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  749. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  750. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  751. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  752. package/build/dist/Server/API/CommonAPI.js +49 -0
  753. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  754. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  755. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  756. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  757. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  758. package/build/dist/Server/API/MonitorAPI.js +107 -1
  759. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  760. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  761. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  762. package/build/dist/Server/API/SlackAPI.js +322 -78
  763. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  764. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  765. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  766. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  767. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  768. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  769. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  770. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  771. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  772. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  773. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  774. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
  775. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
  776. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
  777. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
  778. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  779. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  780. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  781. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  782. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  783. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  784. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  785. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  786. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  787. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  788. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  789. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  790. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  791. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  792. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  793. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  794. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  795. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  796. package/build/dist/Server/Services/AIAgentService.js +1 -0
  797. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  798. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  799. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  800. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  801. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  802. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  803. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  804. package/build/dist/Server/Services/AlertService.js +110 -23
  805. package/build/dist/Server/Services/AlertService.js.map +1 -1
  806. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  807. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  808. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  809. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  810. package/build/dist/Server/Services/BillingService.js +1 -0
  811. package/build/dist/Server/Services/BillingService.js.map +1 -1
  812. package/build/dist/Server/Services/DatabaseService.js +7 -0
  813. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  814. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  815. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  816. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  817. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  818. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  819. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  820. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  821. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  822. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  823. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  824. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  825. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  826. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  827. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  828. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  829. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  830. package/build/dist/Server/Services/IncidentService.js +99 -21
  831. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  832. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  833. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  834. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  835. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  836. package/build/dist/Server/Services/Index.js +4 -0
  837. package/build/dist/Server/Services/Index.js.map +1 -1
  838. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  839. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  840. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  841. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  842. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  843. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  844. package/build/dist/Server/Services/MonitorService.js +490 -50
  845. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  846. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  847. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  848. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  849. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  850. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  851. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  852. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  853. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  854. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  855. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  856. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  857. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  858. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  859. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  860. package/build/dist/Server/Services/ProbeService.js +7 -1
  861. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  862. package/build/dist/Server/Services/ProjectService.js +11 -1
  863. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  864. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  865. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  866. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  867. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  868. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  869. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  870. package/build/dist/Server/Services/RunnerService.js +344 -5
  871. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  872. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  873. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  874. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  875. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  876. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  877. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  878. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
  879. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
  880. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
  881. package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
  882. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
  883. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
  884. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
  885. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
  886. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
  887. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
  888. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
  889. package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
  890. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  891. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  892. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  893. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  894. package/build/dist/Server/Services/StatusPageService.js +230 -32
  895. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  896. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  897. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  898. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  899. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  900. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  901. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  902. package/build/dist/Server/Services/UserEmailService.js +1 -0
  903. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  904. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  905. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  906. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  907. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  908. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  909. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  910. package/build/dist/Server/Services/UserService.js +107 -1
  911. package/build/dist/Server/Services/UserService.js.map +1 -1
  912. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  913. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  914. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  915. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  916. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  917. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  918. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  919. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  920. package/build/dist/Server/Types/Markdown.js +394 -27
  921. package/build/dist/Server/Types/Markdown.js.map +1 -1
  922. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
  923. package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
  924. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  925. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  926. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  927. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  928. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  929. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  930. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  931. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  932. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  933. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  934. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  935. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  936. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  937. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  938. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  939. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  940. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  941. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  942. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  943. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  944. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  945. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  946. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
  947. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
  948. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  949. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  950. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  951. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  952. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  953. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  954. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  955. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  956. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  957. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  958. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
  959. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
  960. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
  961. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  962. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  963. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  964. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
  965. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  966. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  967. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  968. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  969. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  970. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  971. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  972. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  973. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  974. package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
  975. package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
  976. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  977. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  978. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  979. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  980. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  981. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  982. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
  983. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  984. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  985. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  986. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  987. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  988. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  989. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  990. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
  991. package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
  992. package/build/dist/Server/Utils/StartServer.js +4 -3
  993. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  994. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  995. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  996. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  997. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  998. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  999. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  1000. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  1001. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  1002. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  1003. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  1004. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  1005. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  1006. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  1007. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  1008. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  1009. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  1010. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  1011. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  1012. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  1013. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  1014. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  1015. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  1016. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  1017. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  1018. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  1019. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  1020. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  1021. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  1022. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  1023. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  1024. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  1025. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  1026. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  1027. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  1028. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  1029. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  1030. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  1031. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  1032. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  1033. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  1034. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  1035. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  1036. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  1037. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  1038. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  1039. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  1040. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  1041. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  1042. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  1043. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  1044. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  1045. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  1046. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  1047. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  1048. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  1049. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  1050. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  1051. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  1052. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  1053. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  1054. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  1055. package/build/dist/Types/API/Route.js +18 -2
  1056. package/build/dist/Types/API/Route.js.map +1 -1
  1057. package/build/dist/Types/API/URL.js +23 -2
  1058. package/build/dist/Types/API/URL.js.map +1 -1
  1059. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  1060. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  1061. package/build/dist/Types/Date.js +97 -7
  1062. package/build/dist/Types/Date.js.map +1 -1
  1063. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  1064. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  1065. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  1066. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  1067. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  1068. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  1069. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  1070. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  1071. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  1072. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  1073. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  1074. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  1075. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  1076. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  1077. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  1078. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  1079. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  1080. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  1081. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  1082. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  1083. package/build/dist/Types/Permission.js +84 -0
  1084. package/build/dist/Types/Permission.js.map +1 -1
  1085. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
  1086. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  1087. package/build/dist/Types/Rules/RuleRun.js +4 -0
  1088. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  1089. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  1090. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  1091. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  1092. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  1093. package/build/dist/Types/Rum/UserFlow.js +115 -0
  1094. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  1095. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  1096. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1097. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  1098. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  1099. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  1100. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  1101. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  1102. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  1103. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  1104. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  1105. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  1106. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1107. package/build/dist/Types/Timezone.js +5 -0
  1108. package/build/dist/Types/Timezone.js.map +1 -1
  1109. package/build/dist/Types/TimezoneAlias.js +340 -0
  1110. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  1111. package/build/dist/Types/Workflow/Templates.js +2694 -2
  1112. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  1113. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  1114. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  1115. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  1116. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  1117. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  1118. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  1119. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  1120. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  1121. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  1122. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  1123. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  1124. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  1125. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  1126. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  1127. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  1128. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  1129. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  1130. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  1131. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  1132. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  1133. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  1134. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  1135. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  1136. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  1137. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  1138. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  1139. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  1140. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  1141. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  1142. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  1143. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  1144. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  1145. package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
  1146. package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
  1147. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
  1148. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
  1149. package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
  1150. package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
  1151. package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
  1152. package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
  1153. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  1154. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1155. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  1156. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  1157. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  1158. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  1159. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  1160. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  1161. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  1162. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  1163. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1164. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  1165. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1166. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1167. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1168. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1169. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1170. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1171. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1172. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1173. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1174. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1175. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1176. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1177. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1178. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1179. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1180. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1181. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1182. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1183. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1184. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1185. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1186. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1187. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1188. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1189. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1190. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1191. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1192. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1193. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1194. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1195. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1196. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1197. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1198. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1199. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1200. package/build/dist/UI/Utils/Timezone.js +52 -17
  1201. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1202. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1203. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1204. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1205. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1206. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1207. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1208. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1209. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1210. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1211. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1212. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1213. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1214. package/build/dist/Utils/LabelRuleImportExport.js +22 -6
  1215. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  1216. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1217. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1218. package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
  1219. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1220. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1221. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1222. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1223. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1224. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1225. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1226. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1227. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1228. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1229. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1230. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1231. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1232. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1233. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1234. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1235. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
  1236. package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
  1237. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1238. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1239. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1240. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1241. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1242. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1243. package/build/dist/Utils/Slo/SloDuration.js +13 -5
  1244. package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
  1245. package/build/dist/Utils/Slo/SloHealth.js +8 -7
  1246. package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
  1247. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
  1248. package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
  1249. package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
  1250. package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
  1251. package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
  1252. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1253. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1254. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1255. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1256. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1257. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1258. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1259. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1260. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1261. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1262. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1263. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1264. package/jest.config.json +2 -1
  1265. package/package.json +2 -1
  1266. package/tsconfig.tests.json +36 -0
  1267. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1268. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1269. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1270. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,1876 @@
1
+ import KubernetesClusterService, {
2
+ AI_ACCESS_CREDENTIAL_REFUSAL,
3
+ getAiAccessCredentialRefusal,
4
+ } from "../../../Server/Services/KubernetesClusterService";
5
+ import RunbookCredentialService from "../../../Server/Services/RunbookCredentialService";
6
+ import RunnerService from "../../../Server/Services/RunnerService";
7
+ import DatabaseService from "../../../Server/Services/DatabaseService";
8
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
9
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
10
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
11
+ import KubernetesCluster from "../../../Models/DatabaseModels/KubernetesCluster";
12
+ import RunbookCredential from "../../../Models/DatabaseModels/RunbookCredential";
13
+ import Runner from "../../../Models/DatabaseModels/Runner";
14
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
15
+ import BadDataException from "../../../Types/Exception/BadDataException";
16
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
17
+ import { KubernetesAiRemediationMode } from "../../../Types/Kubernetes/KubernetesClusterAiAccess";
18
+ import ObjectID from "../../../Types/ObjectID";
19
+ import Permission, {
20
+ UserTenantAccessPermission,
21
+ } from "../../../Types/Permission";
22
+ import RunbookCredentialType from "../../../Types/Runbook/RunbookCredentialType";
23
+ import KubectlPolicy, {
24
+ KUBECTL_ALLOWLIST_MAX_PATTERNS,
25
+ KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH,
26
+ } from "../../../Utils/AiRemediation/KubectlPolicy";
27
+ import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
28
+
29
+ /*
30
+ * Contract under test — the tenant guard on a cluster's AI access binding,
31
+ * the shape of the AI remediation settings, and the "AI access was
32
+ * configured" marker.
33
+ *
34
+ * aiAccessRunnerId and aiAccessCredentialId are ordinary CRUD columns, and
35
+ * the framework checks only the tenant relation itself — never that a
36
+ * ManyToOne target belongs to the row's project. Without this guard a
37
+ * caller in project A could point their cluster at project B's Runner or
38
+ * credential and then read that Runner's name, liveness and connection
39
+ * status (or the credential's name and type) through the relation join,
40
+ * and probe foreign ids through the foreign key. So KubernetesClusterService's
41
+ * create and update hooks must:
42
+ *
43
+ * - resolve the Runner and the credential scoped to the cluster's project,
44
+ * under BOTH spellings the payload can use (FK column, relation object);
45
+ * - refuse a Runner or credential from another project, or one that does
46
+ * not exist, with the same message MonitorProbeService uses — and give ONE
47
+ * answer for a credential that is missing and one that is not a
48
+ * Kubernetes credential, so the check is not a type oracle;
49
+ * - let a clear (null) through, and do nothing for writes that do not
50
+ * touch the binding;
51
+ * - on update, use the caller's tenant when there is one and otherwise the
52
+ * project of every cluster the query matches, failing closed when a
53
+ * binding cannot be checked against any project.
54
+ *
55
+ * WHO may bind (only the roles that may author a FullAuto remediation rule,
56
+ * plus credential read for a credential) is covered in depth by
57
+ * KubernetesClusterAiSettingsPermission.test.ts; the binding tests here run
58
+ * as a project admin, who may, and this file pins the credential-read half.
59
+ */
60
+
61
+ const PROJECT_ID: ObjectID = new ObjectID(
62
+ "22222222-2222-4222-8222-222222222222",
63
+ );
64
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
65
+ "99999999-9999-4999-8999-999999999999",
66
+ );
67
+ const CLUSTER_ID: ObjectID = new ObjectID(
68
+ "33333333-3333-4333-8333-333333333333",
69
+ );
70
+ const RUNNER_ID: ObjectID = new ObjectID(
71
+ "44444444-4444-4444-8444-444444444444",
72
+ );
73
+ const CREDENTIAL_ID: ObjectID = new ObjectID(
74
+ "55555555-5555-4555-8555-555555555555",
75
+ );
76
+
77
+ type Hooks = {
78
+ onBeforeCreate: (
79
+ createBy: CreateBy<KubernetesCluster>,
80
+ ) => Promise<OnCreate<KubernetesCluster>>;
81
+ onBeforeUpdate: (
82
+ updateBy: UpdateBy<KubernetesCluster>,
83
+ ) => Promise<OnUpdate<KubernetesCluster>>;
84
+ onUpdateSuccess: (
85
+ onUpdate: OnUpdate<KubernetesCluster>,
86
+ updatedItemIds: Array<ObjectID>,
87
+ ) => Promise<OnUpdate<KubernetesCluster>>;
88
+ onCreateSuccess: (
89
+ onCreate: OnCreate<KubernetesCluster>,
90
+ createdItem: KubernetesCluster,
91
+ ) => Promise<KubernetesCluster>;
92
+ };
93
+
94
+ function hooks(): Hooks {
95
+ return KubernetesClusterService as unknown as Hooks;
96
+ }
97
+
98
+ /*
99
+ * A signed-in user holding exactly `permissions` (as grants) in `tenantId`.
100
+ * DatabaseCommonInteractionPropsUtil and holdsAnyPermission read only the
101
+ * tenant bucket keyed by project id, so the key must be right or every
102
+ * refusal below would pass for the wrong reason.
103
+ */
104
+ function userProps(
105
+ tenantId: ObjectID | undefined,
106
+ permissions: Array<Permission>,
107
+ ): DatabaseCommonInteractionProps {
108
+ const permissionProjectId: ObjectID = tenantId || PROJECT_ID;
109
+ const tenantPermission: UserTenantAccessPermission = {
110
+ _type: "UserTenantAccessPermission",
111
+ projectId: permissionProjectId,
112
+ permissions: permissions.map((permission: Permission) => {
113
+ return {
114
+ _type: "UserPermission",
115
+ permission,
116
+ labelIds: [],
117
+ isBlockPermission: false,
118
+ };
119
+ }),
120
+ };
121
+
122
+ return {
123
+ userId: ObjectID.generate(),
124
+ ...(tenantId ? { tenantId } : {}),
125
+ userTenantAccessPermission: {
126
+ [permissionProjectId.toString()]: tenantPermission,
127
+ },
128
+ } as DatabaseCommonInteractionProps;
129
+ }
130
+
131
+ // A project admin: may bind a Runner and read (so bind) credentials.
132
+ function adminProps(
133
+ tenantId: ObjectID | undefined = PROJECT_ID,
134
+ ): DatabaseCommonInteractionProps {
135
+ return userProps(tenantId, [Permission.ProjectAdmin]);
136
+ }
137
+
138
+ function rootProps(): DatabaseCommonInteractionProps {
139
+ return { isRoot: true } as DatabaseCommonInteractionProps;
140
+ }
141
+
142
+ function createBy(
143
+ data: Record<string, unknown>,
144
+ props: DatabaseCommonInteractionProps = adminProps(PROJECT_ID),
145
+ ): CreateBy<KubernetesCluster> {
146
+ return {
147
+ data: data as unknown as KubernetesCluster,
148
+ props,
149
+ } as CreateBy<KubernetesCluster>;
150
+ }
151
+
152
+ function updateBy(
153
+ data: Record<string, unknown>,
154
+ props: DatabaseCommonInteractionProps = adminProps(PROJECT_ID),
155
+ ): UpdateBy<KubernetesCluster> {
156
+ return {
157
+ query: { _id: CLUSTER_ID.toString() },
158
+ data,
159
+ limit: 1,
160
+ skip: 0,
161
+ props,
162
+ } as unknown as UpdateBy<KubernetesCluster>;
163
+ }
164
+
165
+ function lastQuery(spy: jest.SpyInstance): Record<string, unknown> {
166
+ return (
167
+ spy.mock.calls[spy.mock.calls.length - 1]![0] as {
168
+ query: Record<string, unknown>;
169
+ }
170
+ ).query;
171
+ }
172
+
173
+ type FindByArgs = {
174
+ query: Record<string, unknown>;
175
+ select: Record<string, unknown>;
176
+ };
177
+
178
+ /*
179
+ * KubernetesClusterService.findBy serves two readers in onBeforeUpdate: the
180
+ * binding guard's "which project does this root update reach?" lookup
181
+ * (selects only projectId) and the operator-write settings read (selects
182
+ * the AI columns too). The binding assertions are about the first.
183
+ */
184
+ function projectOnlyLookups(spy: jest.SpyInstance): Array<FindByArgs> {
185
+ return spy.mock.calls
186
+ .map((call: Array<unknown>) => {
187
+ return call[0] as FindByArgs;
188
+ })
189
+ .filter((args: FindByArgs) => {
190
+ return !("aiRemediationMode" in (args.select || {}));
191
+ });
192
+ }
193
+
194
+ function settingsReads(spy: jest.SpyInstance): Array<FindByArgs> {
195
+ return spy.mock.calls
196
+ .map((call: Array<unknown>) => {
197
+ return call[0] as FindByArgs;
198
+ })
199
+ .filter((args: FindByArgs) => {
200
+ return "aiRemediationMode" in (args.select || {});
201
+ });
202
+ }
203
+
204
+ describe("KubernetesClusterService AI access binding guard", () => {
205
+ let runnerLookup: jest.SpyInstance;
206
+ let credentialLookup: jest.SpyInstance;
207
+ let clusterLookup: jest.SpyInstance;
208
+
209
+ beforeEach(() => {
210
+ runnerLookup = jest
211
+ .spyOn(RunnerService, "findOneBy")
212
+ .mockResolvedValue({ id: RUNNER_ID } as unknown as Runner);
213
+ /*
214
+ * Assigned to RUNNER_ID: an operator's binding of a Runner and a
215
+ * credential together must be a pair that can work (the credential is
216
+ * assigned to the Runner) — see "the Runner and credential bound
217
+ * together must be able to work" below.
218
+ */
219
+ credentialLookup = jest
220
+ .spyOn(RunbookCredentialService, "findOneBy")
221
+ .mockResolvedValue({
222
+ id: CREDENTIAL_ID,
223
+ credentialType: RunbookCredentialType.Kubernetes,
224
+ runners: [{ id: RUNNER_ID, _id: RUNNER_ID.toString() }],
225
+ } as unknown as RunbookCredential);
226
+ /*
227
+ * The cluster lives in PROJECT_ID. A read scoped to another project
228
+ * (the operator-write settings read adds the caller's tenant) finds
229
+ * nothing, as Postgres would.
230
+ */
231
+ clusterLookup = jest
232
+ .spyOn(KubernetesClusterService, "findBy")
233
+ .mockImplementation(
234
+ async (args: unknown): Promise<Array<KubernetesCluster>> => {
235
+ const query: Record<string, unknown> = (args as FindByArgs).query;
236
+ if (
237
+ query["projectId"] &&
238
+ (query["projectId"] as ObjectID).toString() !==
239
+ PROJECT_ID.toString()
240
+ ) {
241
+ return [];
242
+ }
243
+ return [
244
+ {
245
+ id: CLUSTER_ID,
246
+ projectId: PROJECT_ID,
247
+ } as unknown as KubernetesCluster,
248
+ ];
249
+ },
250
+ );
251
+ });
252
+
253
+ afterEach(() => {
254
+ jest.restoreAllMocks();
255
+ });
256
+
257
+ it("installs non-stock create and update hooks", () => {
258
+ expect(hooks().onBeforeCreate).not.toBe(
259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
260
+ (DatabaseService.prototype as any).onBeforeCreate,
261
+ );
262
+ expect(hooks().onBeforeUpdate).not.toBe(
263
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
264
+ (DatabaseService.prototype as any).onBeforeUpdate,
265
+ );
266
+ });
267
+
268
+ describe("on update", () => {
269
+ it("checks the Runner against the caller's project and lets a same-project Runner through", async () => {
270
+ await hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: RUNNER_ID }));
271
+
272
+ expect(runnerLookup).toHaveBeenCalledTimes(1);
273
+ expect(lastQuery(runnerLookup)["_id"]).toBe(RUNNER_ID.toString());
274
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(PROJECT_ID);
275
+ // The tenant was on the props; no need to look the project up.
276
+ expect(projectOnlyLookups(clusterLookup)).toHaveLength(0);
277
+ });
278
+
279
+ it("refuses a Runner that is not in the caller's project", async () => {
280
+ runnerLookup.mockResolvedValue(null);
281
+
282
+ await expect(
283
+ hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: RUNNER_ID })),
284
+ ).rejects.toThrow(
285
+ "Runner not found or it does not belong to this project.",
286
+ );
287
+ });
288
+
289
+ it("reads the relation-object spelling the dashboard posts", async () => {
290
+ await hooks().onBeforeUpdate(
291
+ updateBy({ aiAccessRunner: { _id: RUNNER_ID.toString() } }),
292
+ );
293
+
294
+ expect(lastQuery(runnerLookup)["_id"]).toBe(RUNNER_ID.toString());
295
+
296
+ runnerLookup.mockResolvedValue(null);
297
+
298
+ await expect(
299
+ hooks().onBeforeUpdate(
300
+ updateBy({ aiAccessRunner: { _id: RUNNER_ID.toString() } }),
301
+ ),
302
+ ).rejects.toThrow(BadDataException);
303
+ });
304
+
305
+ it("reads a bare id string in the relation slot, and an ObjectID", async () => {
306
+ await hooks().onBeforeUpdate(
307
+ updateBy({ aiAccessRunner: RUNNER_ID.toString() }),
308
+ );
309
+ expect(lastQuery(runnerLookup)["_id"]).toBe(RUNNER_ID.toString());
310
+
311
+ await hooks().onBeforeUpdate(updateBy({ aiAccessRunner: RUNNER_ID }));
312
+ expect(lastQuery(runnerLookup)["_id"]).toBe(RUNNER_ID.toString());
313
+ });
314
+
315
+ it("refuses a payload whose two spellings point at different Runners", async () => {
316
+ const otherRunnerId: ObjectID = ObjectID.generate();
317
+
318
+ await expect(
319
+ hooks().onBeforeUpdate(
320
+ updateBy({
321
+ aiAccessRunnerId: RUNNER_ID,
322
+ aiAccessRunner: { _id: otherRunnerId.toString() },
323
+ }),
324
+ ),
325
+ ).rejects.toThrow(/Conflicting AI access Runner references/);
326
+
327
+ expect(runnerLookup).not.toHaveBeenCalled();
328
+ });
329
+
330
+ it("checks the credential against the caller's project and requires a Kubernetes credential", async () => {
331
+ await hooks().onBeforeUpdate(
332
+ updateBy({ aiAccessCredentialId: CREDENTIAL_ID }),
333
+ );
334
+
335
+ expect(credentialLookup).toHaveBeenCalledTimes(1);
336
+ expect(lastQuery(credentialLookup)["_id"]).toBe(CREDENTIAL_ID.toString());
337
+ expect(lastQuery(credentialLookup)["projectId"]).toBe(PROJECT_ID);
338
+
339
+ credentialLookup.mockResolvedValue(null);
340
+ await expect(
341
+ hooks().onBeforeUpdate(
342
+ updateBy({ aiAccessCredentialId: CREDENTIAL_ID }),
343
+ ),
344
+ ).rejects.toThrow(AI_ACCESS_CREDENTIAL_REFUSAL);
345
+
346
+ credentialLookup.mockResolvedValue({
347
+ id: CREDENTIAL_ID,
348
+ credentialType: RunbookCredentialType.SSH,
349
+ } as unknown as RunbookCredential);
350
+ await expect(
351
+ hooks().onBeforeUpdate(
352
+ updateBy({ aiAccessCredential: { _id: CREDENTIAL_ID.toString() } }),
353
+ ),
354
+ ).rejects.toThrow(/must be a Kubernetes credential/);
355
+ });
356
+
357
+ /*
358
+ * "Not found" and "wrong type" used to be two different messages, so a
359
+ * caller could learn from the refusal whether an id existed in the
360
+ * project and what kind of credential it was.
361
+ */
362
+ it("gives the same refusal for a missing credential and a credential of another type", async () => {
363
+ const refusalFor: (
364
+ credential: RunbookCredential | null,
365
+ ) => Promise<string> = async (
366
+ credential: RunbookCredential | null,
367
+ ): Promise<string> => {
368
+ credentialLookup.mockResolvedValue(credential);
369
+ try {
370
+ await hooks().onBeforeUpdate(
371
+ updateBy({ aiAccessCredentialId: CREDENTIAL_ID }),
372
+ );
373
+ } catch (error) {
374
+ expect(error).toBeInstanceOf(BadDataException);
375
+ return (error as Error).message;
376
+ }
377
+ throw new Error("expected the credential to be refused");
378
+ };
379
+
380
+ const missing: string = await refusalFor(null);
381
+ const ssh: string = await refusalFor({
382
+ id: CREDENTIAL_ID,
383
+ credentialType: RunbookCredentialType.SSH,
384
+ } as unknown as RunbookCredential);
385
+
386
+ expect(missing).toBe(AI_ACCESS_CREDENTIAL_REFUSAL);
387
+ expect(ssh).toBe(missing);
388
+ });
389
+
390
+ it("checks both when both are written, and reports the Runner first", async () => {
391
+ runnerLookup.mockResolvedValue(null);
392
+ credentialLookup.mockResolvedValue(null);
393
+
394
+ await expect(
395
+ hooks().onBeforeUpdate(
396
+ updateBy({
397
+ aiAccessRunnerId: RUNNER_ID,
398
+ aiAccessCredentialId: CREDENTIAL_ID,
399
+ }),
400
+ ),
401
+ ).rejects.toThrow(/Runner not found/);
402
+
403
+ runnerLookup.mockResolvedValue({ id: RUNNER_ID } as unknown as Runner);
404
+
405
+ await expect(
406
+ hooks().onBeforeUpdate(
407
+ updateBy({
408
+ aiAccessRunnerId: RUNNER_ID,
409
+ aiAccessCredentialId: CREDENTIAL_ID,
410
+ }),
411
+ ),
412
+ ).rejects.toThrow(/Credential not found/);
413
+ });
414
+
415
+ it("lets a clear (null) through without a Runner or credential lookup", async () => {
416
+ await hooks().onBeforeUpdate(
417
+ updateBy({ aiAccessRunnerId: null, aiAccessCredentialId: null }),
418
+ );
419
+ await hooks().onBeforeUpdate(
420
+ updateBy({ aiAccessRunner: null, aiAccessCredential: null }),
421
+ );
422
+
423
+ expect(runnerLookup).not.toHaveBeenCalled();
424
+ expect(credentialLookup).not.toHaveBeenCalled();
425
+ expect(projectOnlyLookups(clusterLookup)).toHaveLength(0);
426
+ });
427
+
428
+ /*
429
+ * Clearing only makes AI do less, so it needs no more than editing the
430
+ * cluster: a project member without any AI-access permission may do it.
431
+ */
432
+ it("lets a caller with no AI-access permission clear the binding", async () => {
433
+ await hooks().onBeforeUpdate(
434
+ updateBy(
435
+ { aiAccessRunnerId: null, aiAccessCredentialId: null },
436
+ userProps(PROJECT_ID, [Permission.ProjectMember]),
437
+ ),
438
+ );
439
+
440
+ expect(runnerLookup).not.toHaveBeenCalled();
441
+ expect(credentialLookup).not.toHaveBeenCalled();
442
+ });
443
+
444
+ /*
445
+ * Pinned before this change as "does nothing": a user's write of an AI
446
+ * setting now reads the cluster's current settings (the baseline the
447
+ * permission check compares against and the "before" half of the feed
448
+ * item). It still looks up no Runner, credential or project.
449
+ */
450
+ it("looks up no Runner, credential or project for an update that does not touch the binding", async () => {
451
+ const update: UpdateBy<KubernetesCluster> = updateBy({
452
+ name: "renamed",
453
+ isAiInvestigationEnabled: true,
454
+ });
455
+
456
+ const result: OnUpdate<KubernetesCluster> =
457
+ await hooks().onBeforeUpdate(update);
458
+
459
+ expect(result.updateBy).toBe(update);
460
+ expect(runnerLookup).not.toHaveBeenCalled();
461
+ expect(credentialLookup).not.toHaveBeenCalled();
462
+ expect(projectOnlyLookups(clusterLookup)).toHaveLength(0);
463
+ expect(settingsReads(clusterLookup)).toHaveLength(1);
464
+ });
465
+
466
+ it("reads nothing at all for an update that writes no AI access setting", async () => {
467
+ const update: UpdateBy<KubernetesCluster> = updateBy({
468
+ name: "renamed",
469
+ });
470
+
471
+ const result: OnUpdate<KubernetesCluster> =
472
+ await hooks().onBeforeUpdate(update);
473
+
474
+ expect(result.updateBy).toBe(update);
475
+ expect(result.carryForward).toBeNull();
476
+ expect(clusterLookup).not.toHaveBeenCalled();
477
+ });
478
+
479
+ /*
480
+ * Root/API updates (the in-cluster registration binds as root) carry
481
+ * no tenantId, so the project comes from the clusters the query
482
+ * matches — and the Runner must belong to every one of them.
483
+ */
484
+ it("falls back to the project of the matched cluster for a root update", async () => {
485
+ await hooks().onBeforeUpdate(
486
+ updateBy({ aiAccessRunnerId: RUNNER_ID }, rootProps()),
487
+ );
488
+
489
+ expect(clusterLookup).toHaveBeenCalledTimes(1);
490
+ expect(
491
+ (clusterLookup.mock.calls[0]![0] as { query: unknown }).query,
492
+ ).toEqual({ _id: CLUSTER_ID.toString() });
493
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(PROJECT_ID);
494
+ });
495
+
496
+ it("refuses a root update whose Runner belongs to a different project than the matched cluster", async () => {
497
+ runnerLookup.mockImplementation(
498
+ async (args: unknown): Promise<Runner | null> => {
499
+ const query: Record<string, unknown> = (
500
+ args as { query: Record<string, unknown> }
501
+ ).query;
502
+ return (query["projectId"] as ObjectID).toString() ===
503
+ OTHER_PROJECT_ID.toString()
504
+ ? ({ id: RUNNER_ID } as unknown as Runner)
505
+ : null;
506
+ },
507
+ );
508
+
509
+ await expect(
510
+ hooks().onBeforeUpdate(
511
+ updateBy({ aiAccessRunnerId: RUNNER_ID }, rootProps()),
512
+ ),
513
+ ).rejects.toThrow(/Runner not found/);
514
+ });
515
+
516
+ it("checks the Runner against every project a multi-row root update matches", async () => {
517
+ clusterLookup.mockResolvedValue([
518
+ { id: CLUSTER_ID, projectId: PROJECT_ID },
519
+ { id: ObjectID.generate(), projectId: OTHER_PROJECT_ID },
520
+ ] as unknown as Array<KubernetesCluster>);
521
+
522
+ await hooks().onBeforeUpdate(
523
+ updateBy({ aiAccessRunnerId: RUNNER_ID }, rootProps()),
524
+ );
525
+
526
+ const projectsChecked: Array<string> = runnerLookup.mock.calls.map(
527
+ (call: Array<unknown>) => {
528
+ return (
529
+ (call[0] as { query: Record<string, unknown> }).query[
530
+ "projectId"
531
+ ] as ObjectID
532
+ ).toString();
533
+ },
534
+ );
535
+
536
+ expect(projectsChecked.sort()).toEqual(
537
+ [PROJECT_ID.toString(), OTHER_PROJECT_ID.toString()].sort(),
538
+ );
539
+ });
540
+
541
+ it("fails closed when a binding is written but no project can be resolved", async () => {
542
+ clusterLookup.mockResolvedValue([]);
543
+
544
+ await expect(
545
+ hooks().onBeforeUpdate(
546
+ updateBy({ aiAccessRunnerId: RUNNER_ID }, rootProps()),
547
+ ),
548
+ ).rejects.toThrow(/could not be resolved/);
549
+
550
+ expect(runnerLookup).not.toHaveBeenCalled();
551
+ });
552
+
553
+ it("still lets a clear through when no project can be resolved", async () => {
554
+ clusterLookup.mockResolvedValue([]);
555
+
556
+ await hooks().onBeforeUpdate(
557
+ updateBy({ aiAccessRunnerId: null }, rootProps()),
558
+ );
559
+
560
+ expect(runnerLookup).not.toHaveBeenCalled();
561
+ });
562
+
563
+ it("prefers the caller's tenant over the matched rows when both are available", async () => {
564
+ await hooks().onBeforeUpdate(
565
+ updateBy({ aiAccessRunnerId: RUNNER_ID }, adminProps(OTHER_PROJECT_ID)),
566
+ );
567
+
568
+ expect(projectOnlyLookups(clusterLookup)).toHaveLength(0);
569
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(OTHER_PROJECT_ID);
570
+ });
571
+ });
572
+
573
+ /*
574
+ * The dashboard hides the credential picker from users who may not read
575
+ * credentials, but the CRUD API used to accept any credential id from
576
+ * anyone who may edit the cluster (an id is readable off another
577
+ * cluster's aiAccessCredentialId). The server now applies the picker's
578
+ * rule itself.
579
+ */
580
+ describe("binding a credential needs permission to read credentials", () => {
581
+ it("refuses a project member binding a same-project Kubernetes credential, before any credential lookup", async () => {
582
+ await expect(
583
+ hooks().onBeforeUpdate(
584
+ updateBy(
585
+ { aiAccessCredentialId: CREDENTIAL_ID },
586
+ userProps(PROJECT_ID, [Permission.ProjectMember]),
587
+ ),
588
+ ),
589
+ ).rejects.toThrow(NotAuthorizedException);
590
+
591
+ // Refused on who is asking, so the lookup is no oracle for them.
592
+ expect(credentialLookup).not.toHaveBeenCalled();
593
+ });
594
+
595
+ it("refuses a holder of Edit Auto Remediation Rule without credential read, under both spellings", async () => {
596
+ for (const data of [
597
+ { aiAccessCredentialId: CREDENTIAL_ID },
598
+ { aiAccessCredential: { _id: CREDENTIAL_ID.toString() } },
599
+ ]) {
600
+ await expect(
601
+ hooks().onBeforeUpdate(
602
+ updateBy(
603
+ data,
604
+ userProps(PROJECT_ID, [Permission.EditAutoRemediationRule]),
605
+ ),
606
+ ),
607
+ ).rejects.toThrow(getAiAccessCredentialRefusal());
608
+ }
609
+
610
+ expect(credentialLookup).not.toHaveBeenCalled();
611
+ });
612
+
613
+ it("names the permissions that would allow it", async () => {
614
+ await expect(
615
+ hooks().onBeforeUpdate(
616
+ updateBy(
617
+ { aiAccessCredentialId: CREDENTIAL_ID },
618
+ userProps(PROJECT_ID, [Permission.EditAutoRemediationRule]),
619
+ ),
620
+ ),
621
+ ).rejects.toThrow(
622
+ /Project Owner, Project Admin, Read Runbook Credential/,
623
+ );
624
+ });
625
+
626
+ it("accepts the same binding once the caller may read credentials", async () => {
627
+ await hooks().onBeforeUpdate(
628
+ updateBy(
629
+ { aiAccessCredentialId: CREDENTIAL_ID },
630
+ userProps(PROJECT_ID, [
631
+ Permission.EditAutoRemediationRule,
632
+ Permission.ReadRunbookCredential,
633
+ ]),
634
+ ),
635
+ );
636
+
637
+ expect(credentialLookup).toHaveBeenCalledTimes(1);
638
+ });
639
+
640
+ it("accepts a project admin and a project owner", async () => {
641
+ await hooks().onBeforeUpdate(
642
+ updateBy({ aiAccessCredentialId: CREDENTIAL_ID }, adminProps()),
643
+ );
644
+ await hooks().onBeforeUpdate(
645
+ updateBy(
646
+ { aiAccessCredential: { _id: CREDENTIAL_ID.toString() } },
647
+ userProps(PROJECT_ID, [Permission.ProjectOwner]),
648
+ ),
649
+ );
650
+
651
+ expect(credentialLookup).toHaveBeenCalledTimes(2);
652
+ });
653
+
654
+ it("does not read a block row for credential read as a grant", async () => {
655
+ const props: DatabaseCommonInteractionProps = userProps(PROJECT_ID, [
656
+ Permission.EditAutoRemediationRule,
657
+ ]);
658
+ props.userTenantAccessPermission![
659
+ PROJECT_ID.toString()
660
+ ]!.permissions.push({
661
+ _type: "UserPermission",
662
+ permission: Permission.ReadRunbookCredential,
663
+ labelIds: [],
664
+ isBlockPermission: true,
665
+ });
666
+
667
+ await expect(
668
+ hooks().onBeforeUpdate(
669
+ updateBy({ aiAccessCredentialId: CREDENTIAL_ID }, props),
670
+ ),
671
+ ).rejects.toThrow(NotAuthorizedException);
672
+ });
673
+
674
+ it("lets the in-cluster registration (root) bind without any permission rows", async () => {
675
+ await hooks().onBeforeUpdate(
676
+ updateBy(
677
+ {
678
+ aiAccessRunnerId: RUNNER_ID,
679
+ aiAccessCredentialId: CREDENTIAL_ID,
680
+ },
681
+ rootProps(),
682
+ ),
683
+ );
684
+
685
+ expect(credentialLookup).toHaveBeenCalledTimes(1);
686
+ });
687
+
688
+ /*
689
+ * The AI page posts every field of its form, so a member who only flips
690
+ * the investigation switch re-posts the credential an admin bound.
691
+ * Unchanged is not "binding".
692
+ */
693
+ it("lets a member re-post the credential that is already bound", async () => {
694
+ clusterLookup.mockResolvedValue([
695
+ {
696
+ id: CLUSTER_ID,
697
+ projectId: PROJECT_ID,
698
+ aiAccessCredentialId: CREDENTIAL_ID,
699
+ } as unknown as KubernetesCluster,
700
+ ]);
701
+
702
+ await hooks().onBeforeUpdate(
703
+ updateBy(
704
+ {
705
+ isAiInvestigationEnabled: false,
706
+ aiAccessCredential: { _id: CREDENTIAL_ID.toString() },
707
+ },
708
+ userProps(PROJECT_ID, [Permission.ProjectMember]),
709
+ ),
710
+ );
711
+ });
712
+ });
713
+
714
+ /*
715
+ * A Settings Member cannot approve a single kubectl plan, so it must not
716
+ * be able to remove the approval step either. The full role matrix lives
717
+ * in KubernetesClusterAiSettingsPermission.test.ts; this is the shortest
718
+ * statement of the escalation that was closed.
719
+ */
720
+ describe("only roles that may author a FullAuto rule can make a cluster unattended", () => {
721
+ it.each([
722
+ [{ aiRemediationMode: KubernetesAiRemediationMode.BypassApproval }],
723
+ [{ aiRemediationMode: KubernetesAiRemediationMode.Automatic }],
724
+ /*
725
+ * Was ["*"]. A * where the verb goes is no longer a valid entry
726
+ * (KubectlPolicy.describeAllowlistPatternProblem), and validity is
727
+ * checked before who may loosen, so "*" now reads as a BadDataException
728
+ * for every caller (pinned below). A valid entry that is as broad as
729
+ * one can be still exercises the escalation.
730
+ */
731
+ [{ aiKubectlCommandAllowlist: ["kubectl delete * * -n *"] }],
732
+ ])(
733
+ "refuses %p from a Settings Member and accepts it from a Project Admin",
734
+ async (data: Record<string, unknown>) => {
735
+ await expect(
736
+ hooks().onBeforeUpdate(
737
+ updateBy(data, userProps(PROJECT_ID, [Permission.SettingsMember])),
738
+ ),
739
+ ).rejects.toThrow(NotAuthorizedException);
740
+
741
+ await expect(
742
+ hooks().onBeforeUpdate(updateBy(data, adminProps())),
743
+ ).resolves.toBeDefined();
744
+ },
745
+ );
746
+
747
+ it.each([[["*"]], [["kubectl *"]], [["* * -n web"]]])(
748
+ "refuses the verb-glob allowlist %p as invalid for every caller, before who may loosen is judged",
749
+ async (allowlist: Array<string>) => {
750
+ for (const props of [
751
+ userProps(PROJECT_ID, [Permission.SettingsMember]),
752
+ adminProps(),
753
+ ]) {
754
+ await expect(
755
+ hooks().onBeforeUpdate(
756
+ updateBy({ aiKubectlCommandAllowlist: allowlist }, props),
757
+ ),
758
+ ).rejects.toThrow(BadDataException);
759
+ }
760
+ },
761
+ );
762
+ });
763
+
764
+ describe("on create", () => {
765
+ it("checks the binding against the row's own project", async () => {
766
+ await hooks().onBeforeCreate(
767
+ createBy({
768
+ projectId: PROJECT_ID,
769
+ name: "prod-us",
770
+ clusterIdentifier: "prod-us",
771
+ aiAccessRunnerId: RUNNER_ID,
772
+ aiAccessCredentialId: CREDENTIAL_ID,
773
+ }),
774
+ );
775
+
776
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(PROJECT_ID);
777
+ expect(lastQuery(credentialLookup)["projectId"]).toBe(PROJECT_ID);
778
+ });
779
+
780
+ it("refuses a foreign Runner and a foreign credential on create", async () => {
781
+ runnerLookup.mockResolvedValue(null);
782
+
783
+ await expect(
784
+ hooks().onBeforeCreate(
785
+ createBy({
786
+ projectId: PROJECT_ID,
787
+ name: "prod-us",
788
+ aiAccessRunner: { _id: RUNNER_ID.toString() },
789
+ }),
790
+ ),
791
+ ).rejects.toThrow(/Runner not found/);
792
+
793
+ credentialLookup.mockResolvedValue(null);
794
+
795
+ await expect(
796
+ hooks().onBeforeCreate(
797
+ createBy({
798
+ projectId: PROJECT_ID,
799
+ name: "prod-us",
800
+ aiAccessCredentialId: CREDENTIAL_ID,
801
+ }),
802
+ ),
803
+ ).rejects.toThrow(/Credential not found/);
804
+ });
805
+
806
+ it("falls back to the project relation and then to the caller's tenant", async () => {
807
+ await hooks().onBeforeCreate(
808
+ createBy({
809
+ project: { id: PROJECT_ID, _id: PROJECT_ID.toString() },
810
+ name: "prod-us",
811
+ aiAccessRunnerId: RUNNER_ID,
812
+ }),
813
+ );
814
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(PROJECT_ID);
815
+
816
+ await hooks().onBeforeCreate(
817
+ createBy(
818
+ { name: "prod-us", aiAccessRunnerId: RUNNER_ID },
819
+ adminProps(OTHER_PROJECT_ID),
820
+ ),
821
+ );
822
+ expect(lastQuery(runnerLookup)["projectId"]).toBe(OTHER_PROJECT_ID);
823
+ });
824
+
825
+ it("fails closed when a binding is given and no project is known", async () => {
826
+ await expect(
827
+ hooks().onBeforeCreate(
828
+ createBy(
829
+ { name: "prod-us", aiAccessRunnerId: RUNNER_ID },
830
+ rootProps(),
831
+ ),
832
+ ),
833
+ ).rejects.toThrow(/could not be resolved/);
834
+ });
835
+
836
+ it("does nothing for a create without a binding (the ingest path)", async () => {
837
+ const create: CreateBy<KubernetesCluster> = createBy(
838
+ {
839
+ projectId: PROJECT_ID,
840
+ name: "prod-us",
841
+ clusterIdentifier: "prod-us",
842
+ },
843
+ rootProps(),
844
+ );
845
+
846
+ const result: OnCreate<KubernetesCluster> =
847
+ await hooks().onBeforeCreate(create);
848
+
849
+ expect(result.createBy).toBe(create);
850
+ expect(runnerLookup).not.toHaveBeenCalled();
851
+ expect(credentialLookup).not.toHaveBeenCalled();
852
+ });
853
+
854
+ it("refuses a create that binds a credential for a caller without credential read", async () => {
855
+ await expect(
856
+ hooks().onBeforeCreate(
857
+ createBy(
858
+ {
859
+ projectId: PROJECT_ID,
860
+ name: "prod-us",
861
+ aiAccessCredentialId: CREDENTIAL_ID,
862
+ },
863
+ userProps(PROJECT_ID, [Permission.EditAutoRemediationRule]),
864
+ ),
865
+ ),
866
+ ).rejects.toThrow(NotAuthorizedException);
867
+ });
868
+ });
869
+ });
870
+
871
+ /*
872
+ * Known follow-up 9: the dashboard stopped offering these pairs, but the API
873
+ * and Terraform could still bind them. An operator's binding must be a pair
874
+ * that can work, per cluster, judged only where the pair changes (the AI
875
+ * page re-posts unchanged values):
876
+ * - another cluster's kubernetes-agent Runner cannot be bound (by its name
877
+ * for another cluster, or a posture naming another cluster);
878
+ * - a credential never goes with another cluster's agent Runner;
879
+ * - a credential must be assigned to the Runner it is bound with — except
880
+ * with THIS cluster's agent Runner, whose own ServiceAccount wins.
881
+ */
882
+ describe("KubernetesClusterService: the Runner and credential bound together must be able to work", () => {
883
+ const OTHER_AGENT_ID: ObjectID = new ObjectID(
884
+ "66666666-6666-4666-8666-666666666666",
885
+ );
886
+ const OWN_AGENT_ID: ObjectID = new ObjectID(
887
+ "77777777-7777-4777-8777-777777777777",
888
+ );
889
+ // Another cluster's agent row renamed by root: only its posture marks it.
890
+ const RENAMED_OTHER_AGENT_ID: ObjectID = new ObjectID(
891
+ "88888888-8888-4888-8888-888888888888",
892
+ );
893
+
894
+ const runners: Record<string, Runner> = {
895
+ [RENAMED_OTHER_AGENT_ID.toString()]: {
896
+ id: RENAMED_OTHER_AGENT_ID,
897
+ name: "prod-eu in-cluster runner",
898
+ hostInfo: {
899
+ kubernetes: { inCluster: true, clusterIdentifier: "prod-eu" },
900
+ },
901
+ } as unknown as Runner,
902
+ [RUNNER_ID.toString()]: {
903
+ id: RUNNER_ID,
904
+ name: "office-runner",
905
+ hostInfo: {},
906
+ } as unknown as Runner,
907
+ [OTHER_AGENT_ID.toString()]: {
908
+ id: OTHER_AGENT_ID,
909
+ name: "kubernetes-agent/prod-eu",
910
+ hostInfo: {
911
+ kubernetes: { inCluster: true, clusterIdentifier: "prod-eu" },
912
+ },
913
+ } as unknown as Runner,
914
+ [OWN_AGENT_ID.toString()]: {
915
+ id: OWN_AGENT_ID,
916
+ name: "kubernetes-agent/prod-us",
917
+ hostInfo: {
918
+ kubernetes: { inCluster: true, clusterIdentifier: "prod-us" },
919
+ },
920
+ } as unknown as Runner,
921
+ };
922
+
923
+ let runnerLookup: jest.SpyInstance;
924
+ let credentialLookup: jest.SpyInstance;
925
+ let current: Record<string, unknown>;
926
+
927
+ function assignedCredential(assignedTo: Array<ObjectID>): RunbookCredential {
928
+ return {
929
+ id: CREDENTIAL_ID,
930
+ credentialType: RunbookCredentialType.Kubernetes,
931
+ runners: assignedTo.map((id: ObjectID) => {
932
+ return { id, _id: id.toString() };
933
+ }),
934
+ } as unknown as RunbookCredential;
935
+ }
936
+
937
+ beforeEach(() => {
938
+ current = {
939
+ id: CLUSTER_ID,
940
+ projectId: PROJECT_ID,
941
+ clusterIdentifier: "prod-us",
942
+ };
943
+ runnerLookup = jest
944
+ .spyOn(RunnerService, "findOneBy")
945
+ .mockImplementation(async (args: unknown): Promise<Runner | null> => {
946
+ const query: Record<string, unknown> = (
947
+ args as { query: Record<string, unknown> }
948
+ ).query;
949
+ return runners[String(query["_id"])] || null;
950
+ });
951
+ credentialLookup = jest
952
+ .spyOn(RunbookCredentialService, "findOneBy")
953
+ .mockResolvedValue(assignedCredential([RUNNER_ID]));
954
+ jest
955
+ .spyOn(KubernetesClusterService, "findBy")
956
+ .mockImplementation(async (): Promise<Array<KubernetesCluster>> => {
957
+ return [current as unknown as KubernetesCluster];
958
+ });
959
+ });
960
+
961
+ afterEach(() => {
962
+ jest.restoreAllMocks();
963
+ });
964
+
965
+ async function refusalOf(data: Record<string, unknown>): Promise<string> {
966
+ try {
967
+ await hooks().onBeforeUpdate(updateBy(data));
968
+ } catch (error) {
969
+ expect(error).toBeInstanceOf(BadDataException);
970
+ return (error as Error).message;
971
+ }
972
+
973
+ throw new Error("expected the binding to be refused");
974
+ }
975
+
976
+ it("refuses binding another cluster's in-cluster Runner", async () => {
977
+ const message: string = await refusalOf({
978
+ aiAccessRunnerId: OTHER_AGENT_ID,
979
+ });
980
+
981
+ expect(message).toContain('Runner "kubernetes-agent/prod-eu"');
982
+ expect(message).toContain("another cluster");
983
+ });
984
+
985
+ it("refuses a renamed agent row of another cluster by its posture", async () => {
986
+ await refusalOf({
987
+ aiAccessRunner: { _id: RENAMED_OTHER_AGENT_ID.toString() },
988
+ });
989
+ });
990
+
991
+ it("negative control: THIS cluster's agent Runner and an ordinary Runner bind", async () => {
992
+ await expect(
993
+ hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: OWN_AGENT_ID })),
994
+ ).resolves.toBeDefined();
995
+ await expect(
996
+ hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: RUNNER_ID })),
997
+ ).resolves.toBeDefined();
998
+ });
999
+
1000
+ it("refuses a credential with another cluster's agent Runner that is already bound", async () => {
1001
+ current["aiAccessRunnerId"] = OTHER_AGENT_ID;
1002
+
1003
+ const message: string = await refusalOf({
1004
+ aiAccessCredentialId: CREDENTIAL_ID,
1005
+ });
1006
+
1007
+ expect(message).toContain("never given a credential");
1008
+ });
1009
+
1010
+ it("refuses a credential that is not assigned to the chosen Runner", async () => {
1011
+ credentialLookup.mockResolvedValue(assignedCredential([]));
1012
+
1013
+ const message: string = await refusalOf({
1014
+ aiAccessRunnerId: RUNNER_ID,
1015
+ aiAccessCredentialId: CREDENTIAL_ID,
1016
+ });
1017
+
1018
+ // Never names the credential: the refusal is readable to cluster editors.
1019
+ expect(message).toContain('is not assigned to Runner "office-runner"');
1020
+ expect(message).toContain("Runner Credentials");
1021
+ });
1022
+
1023
+ it("refuses switching the Runner while an unassigned credential stays bound", async () => {
1024
+ current["aiAccessRunnerId"] = OWN_AGENT_ID;
1025
+ current["aiAccessCredentialId"] = CREDENTIAL_ID;
1026
+ credentialLookup.mockResolvedValue(assignedCredential([OWN_AGENT_ID]));
1027
+
1028
+ await refusalOf({ aiAccessRunnerId: RUNNER_ID });
1029
+ });
1030
+
1031
+ it("negative control: the same credential assigned to the Runner binds", async () => {
1032
+ await expect(
1033
+ hooks().onBeforeUpdate(
1034
+ updateBy({
1035
+ aiAccessRunnerId: RUNNER_ID,
1036
+ aiAccessCredentialId: CREDENTIAL_ID,
1037
+ }),
1038
+ ),
1039
+ ).resolves.toBeDefined();
1040
+ });
1041
+
1042
+ it("negative control: with THIS cluster's agent a stale credential is no obstacle (in-cluster access wins)", async () => {
1043
+ current["aiAccessCredentialId"] = CREDENTIAL_ID;
1044
+ credentialLookup.mockResolvedValue(assignedCredential([]));
1045
+
1046
+ await expect(
1047
+ hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: OWN_AGENT_ID })),
1048
+ ).resolves.toBeDefined();
1049
+ });
1050
+
1051
+ it("negative control: re-posting an unchanged pair is not judged again", async () => {
1052
+ current["aiAccessRunnerId"] = OTHER_AGENT_ID;
1053
+ current["aiAccessCredentialId"] = CREDENTIAL_ID;
1054
+
1055
+ await expect(
1056
+ hooks().onBeforeUpdate(
1057
+ updateBy({
1058
+ isAiInvestigationEnabled: true,
1059
+ aiAccessRunnerId: OTHER_AGENT_ID,
1060
+ aiAccessCredentialId: CREDENTIAL_ID,
1061
+ }),
1062
+ ),
1063
+ ).resolves.toBeDefined();
1064
+ });
1065
+
1066
+ it("negative control: clearing the Runner never needs a pair check", async () => {
1067
+ current["aiAccessRunnerId"] = OTHER_AGENT_ID;
1068
+ current["aiAccessCredentialId"] = CREDENTIAL_ID;
1069
+
1070
+ await expect(
1071
+ hooks().onBeforeUpdate(updateBy({ aiAccessRunnerId: null })),
1072
+ ).resolves.toBeDefined();
1073
+ });
1074
+
1075
+ it("negative control: the server's own (root) writes are not operator choices", async () => {
1076
+ await expect(
1077
+ hooks().onBeforeUpdate(
1078
+ updateBy(
1079
+ { aiAccessRunnerId: OTHER_AGENT_ID, aiAccessCredentialId: null },
1080
+ rootProps(),
1081
+ ),
1082
+ ),
1083
+ ).resolves.toBeDefined();
1084
+ expect(runnerLookup).toHaveBeenCalledTimes(1);
1085
+ });
1086
+
1087
+ it("applies to an operator's create too", async () => {
1088
+ await expect(
1089
+ hooks().onBeforeCreate(
1090
+ createBy(
1091
+ {
1092
+ projectId: PROJECT_ID,
1093
+ name: "prod-us",
1094
+ clusterIdentifier: "prod-us",
1095
+ aiAccessRunnerId: OTHER_AGENT_ID,
1096
+ },
1097
+ { isMasterAdmin: true, userId: ObjectID.generate() },
1098
+ ),
1099
+ ),
1100
+ ).rejects.toThrow(/another cluster/);
1101
+ });
1102
+ });
1103
+
1104
+ /*
1105
+ * aiRemediationMode is a plain text column and aiKubectlCommandAllowlist
1106
+ * untyped JSON; their readers map anything unusable to Disabled / "no
1107
+ * pattern". Without write-side validation an API or Terraform write of
1108
+ * "automatic" or of a pattern that can never match answered 200, showed no
1109
+ * drift, and silently did nothing.
1110
+ */
1111
+ describe("KubernetesClusterService AI remediation settings validation", () => {
1112
+ let runnerLookup: jest.SpyInstance;
1113
+
1114
+ beforeEach(() => {
1115
+ runnerLookup = jest
1116
+ .spyOn(RunnerService, "findOneBy")
1117
+ .mockResolvedValue({ id: RUNNER_ID } as unknown as Runner);
1118
+ jest.spyOn(KubernetesClusterService, "findBy").mockResolvedValue([
1119
+ {
1120
+ id: CLUSTER_ID,
1121
+ projectId: PROJECT_ID,
1122
+ } as unknown as KubernetesCluster,
1123
+ ]);
1124
+ });
1125
+
1126
+ afterEach(() => {
1127
+ jest.restoreAllMocks();
1128
+ });
1129
+
1130
+ async function update(
1131
+ data: Record<string, unknown>,
1132
+ ): Promise<UpdateBy<KubernetesCluster>> {
1133
+ const update: UpdateBy<KubernetesCluster> = updateBy(data);
1134
+ await hooks().onBeforeUpdate(update);
1135
+ return update;
1136
+ }
1137
+
1138
+ function writtenAllowlist(update: UpdateBy<KubernetesCluster>): unknown {
1139
+ return (update.data as unknown as Record<string, unknown>)[
1140
+ "aiKubectlCommandAllowlist"
1141
+ ];
1142
+ }
1143
+
1144
+ describe("remediation mode", () => {
1145
+ it.each([
1146
+ ["automatic"],
1147
+ ["Automatic "],
1148
+ ["Bypass"],
1149
+ ["FullAuto"],
1150
+ [""],
1151
+ [null],
1152
+ [3],
1153
+ ])("refuses %p", async (mode: unknown) => {
1154
+ await expect(update({ aiRemediationMode: mode })).rejects.toThrow(
1155
+ BadDataException,
1156
+ );
1157
+ });
1158
+
1159
+ it("says which values are accepted and what it got", async () => {
1160
+ await expect(update({ aiRemediationMode: "automatic" })).rejects.toThrow(
1161
+ 'AI remediation mode must be one of Disabled, RequireApproval, Automatic, BypassApproval (got "automatic").',
1162
+ );
1163
+ });
1164
+
1165
+ it.each(Object.values(KubernetesAiRemediationMode))(
1166
+ "accepts %s",
1167
+ async (mode: KubernetesAiRemediationMode) => {
1168
+ const written: UpdateBy<KubernetesCluster> = await update({
1169
+ aiRemediationMode: mode,
1170
+ });
1171
+ expect(
1172
+ (written.data as unknown as Record<string, unknown>)[
1173
+ "aiRemediationMode"
1174
+ ],
1175
+ ).toBe(mode);
1176
+ },
1177
+ );
1178
+
1179
+ it("rejects on the mode even when a valid Runner is written alongside it (the check is not behind the binding guard)", async () => {
1180
+ await expect(
1181
+ update({ aiRemediationMode: "automatic", aiAccessRunnerId: RUNNER_ID }),
1182
+ ).rejects.toThrow(/AI remediation mode must be one of/);
1183
+
1184
+ expect(runnerLookup).not.toHaveBeenCalled();
1185
+ });
1186
+ });
1187
+
1188
+ describe("kubectl allowlist", () => {
1189
+ it.each([
1190
+ [{ a: 1 }, /must be a JSON array/],
1191
+ [42, /must be a JSON array/],
1192
+ [true, /must be a JSON array/],
1193
+ ["kubectl scale *", /must be a JSON array/],
1194
+ ['{"a": 1}', /must be a JSON array/],
1195
+ [[1], /Pattern 1 of the kubectl allowlist must be text \(got number\)/],
1196
+ [
1197
+ ["kubectl scale *", null],
1198
+ /Pattern 2 of the kubectl allowlist must be text \(got null\)/,
1199
+ ],
1200
+ [[""], /Pattern 1 of the kubectl allowlist cannot be used/],
1201
+ [[" "], /Pattern 1 of the kubectl allowlist cannot be used/],
1202
+ // No verb at all: the matcher can never use it.
1203
+ [["kubectl"], /Pattern 1 of the kubectl allowlist cannot be used/],
1204
+ // Unbalanced quotes: not one kubectl command line.
1205
+ [
1206
+ [
1207
+ "kubectl scale deployment/web --replicas=*",
1208
+ 'kubectl patch deployment/web -p \'{"spec":',
1209
+ ],
1210
+ /Pattern 2 of the kubectl allowlist cannot be used/,
1211
+ ],
1212
+ ])("refuses %p", async (value: unknown, message: RegExp) => {
1213
+ await expect(
1214
+ update({ aiKubectlCommandAllowlist: value }),
1215
+ ).rejects.toThrow(message);
1216
+ await expect(
1217
+ update({ aiKubectlCommandAllowlist: value }),
1218
+ ).rejects.toThrow(BadDataException);
1219
+ });
1220
+
1221
+ it("refuses a pattern that is not one kubectl command line, saying how patterns are matched", async () => {
1222
+ await expect(
1223
+ update({ aiKubectlCommandAllowlist: ["kubectl get\npods"] }),
1224
+ ).rejects.toThrow(/cannot be used/);
1225
+
1226
+ let message: string = "";
1227
+ try {
1228
+ await update({ aiKubectlCommandAllowlist: ["kubectl"] });
1229
+ } catch (error) {
1230
+ message = (error as Error).message;
1231
+ }
1232
+
1233
+ expect(message).toContain("word by word");
1234
+ // The stale rule from the whole-command matcher is gone for good.
1235
+ expect(message).not.toContain("whole command");
1236
+ expect(message).not.toContain('must start with "kubectl "');
1237
+ });
1238
+
1239
+ it(`refuses more than ${KUBECTL_ALLOWLIST_MAX_PATTERNS} patterns`, async () => {
1240
+ const patterns: Array<string> = [];
1241
+ for (let i: number = 0; i <= KUBECTL_ALLOWLIST_MAX_PATTERNS; i++) {
1242
+ patterns.push(`kubectl scale deployment/web-${i} --replicas=*`);
1243
+ }
1244
+
1245
+ await expect(
1246
+ update({ aiKubectlCommandAllowlist: patterns }),
1247
+ ).rejects.toThrow(
1248
+ `The kubectl allowlist can hold at most ${KUBECTL_ALLOWLIST_MAX_PATTERNS} patterns (got ${KUBECTL_ALLOWLIST_MAX_PATTERNS + 1}).`,
1249
+ );
1250
+
1251
+ // Negative control: exactly the limit is fine.
1252
+ const written: UpdateBy<KubernetesCluster> = await update({
1253
+ aiKubectlCommandAllowlist: patterns.slice(
1254
+ 0,
1255
+ KUBECTL_ALLOWLIST_MAX_PATTERNS,
1256
+ ),
1257
+ });
1258
+ expect(writtenAllowlist(written)).toHaveLength(
1259
+ KUBECTL_ALLOWLIST_MAX_PATTERNS,
1260
+ );
1261
+ });
1262
+
1263
+ it(`refuses a pattern longer than ${KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH} characters`, async () => {
1264
+ const prefix: string = "kubectl set image deployment/";
1265
+ const tooLong: string =
1266
+ prefix +
1267
+ "a".repeat(KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH + 1 - prefix.length);
1268
+ const atLimit: string = tooLong.slice(0, -1);
1269
+
1270
+ await expect(
1271
+ update({ aiKubectlCommandAllowlist: [tooLong] }),
1272
+ ).rejects.toThrow(
1273
+ new RegExp(
1274
+ `Pattern 1 of the kubectl allowlist cannot be used: .*${KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH} characters`,
1275
+ ),
1276
+ );
1277
+
1278
+ const written: UpdateBy<KubernetesCluster> = await update({
1279
+ aiKubectlCommandAllowlist: [atLimit],
1280
+ });
1281
+ expect(writtenAllowlist(written)).toEqual([atLimit]);
1282
+ });
1283
+
1284
+ /*
1285
+ * Round one refused these for not starting with "kubectl " (the old
1286
+ * whole-command glob). The matcher compares word by word and the
1287
+ * leading "kubectl" is optional in any case, so they are real,
1288
+ * matching patterns.
1289
+ */
1290
+ it("accepts patterns with or without a leading kubectl, in any case, trimmed", async () => {
1291
+ const written: UpdateBy<KubernetesCluster> = await update({
1292
+ aiKubectlCommandAllowlist: [
1293
+ " kubectl set image deployment/web * -n web ",
1294
+ "set image deployment/web * -n web",
1295
+ "Kubectl scale deployment/web --replicas=*",
1296
+ "kubectl\tscale deployment/web --replicas=*",
1297
+ ],
1298
+ });
1299
+
1300
+ expect(writtenAllowlist(written)).toEqual([
1301
+ "kubectl set image deployment/web * -n web",
1302
+ "set image deployment/web * -n web",
1303
+ "Kubectl scale deployment/web --replicas=*",
1304
+ "kubectl\tscale deployment/web --replicas=*",
1305
+ ]);
1306
+ });
1307
+
1308
+ it("stores a JSON-encoded array (a form the readers already accept) as the array", async () => {
1309
+ const written: UpdateBy<KubernetesCluster> = await update({
1310
+ aiKubectlCommandAllowlist:
1311
+ '["kubectl scale deployment/web --replicas=*"]',
1312
+ });
1313
+
1314
+ expect(writtenAllowlist(written)).toEqual([
1315
+ "kubectl scale deployment/web --replicas=*",
1316
+ ]);
1317
+ });
1318
+
1319
+ it("stores an empty text field and null as no allowlist, and keeps an empty list", async () => {
1320
+ expect(
1321
+ writtenAllowlist(await update({ aiKubectlCommandAllowlist: "" })),
1322
+ ).toBeNull();
1323
+ expect(
1324
+ writtenAllowlist(await update({ aiKubectlCommandAllowlist: " " })),
1325
+ ).toBeNull();
1326
+ expect(
1327
+ writtenAllowlist(await update({ aiKubectlCommandAllowlist: null })),
1328
+ ).toBeNull();
1329
+ expect(
1330
+ writtenAllowlist(await update({ aiKubectlCommandAllowlist: [] })),
1331
+ ).toEqual([]);
1332
+ });
1333
+
1334
+ it("skips validation entirely for an update that writes neither setting", async () => {
1335
+ const written: UpdateBy<KubernetesCluster> = await update({
1336
+ name: "renamed",
1337
+ });
1338
+
1339
+ expect(written.data).toEqual({ name: "renamed" });
1340
+ });
1341
+
1342
+ it("validates the create path too", async () => {
1343
+ await expect(
1344
+ hooks().onBeforeCreate(
1345
+ createBy(
1346
+ {
1347
+ projectId: PROJECT_ID,
1348
+ name: "prod-us",
1349
+ aiRemediationMode: "automatic",
1350
+ },
1351
+ rootProps(),
1352
+ ),
1353
+ ),
1354
+ ).rejects.toThrow(/AI remediation mode must be one of/);
1355
+
1356
+ await expect(
1357
+ hooks().onBeforeCreate(
1358
+ createBy(
1359
+ {
1360
+ projectId: PROJECT_ID,
1361
+ name: "prod-us",
1362
+ aiKubectlCommandAllowlist: ["kubectl"],
1363
+ },
1364
+ rootProps(),
1365
+ ),
1366
+ ),
1367
+ ).rejects.toThrow(/cannot be used/);
1368
+ });
1369
+
1370
+ /*
1371
+ * The validator and the matcher must not drift apart: every pattern the
1372
+ * validator accepts must be able to match the command it describes, and
1373
+ * the limits must be exactly where the matcher starts ignoring
1374
+ * patterns (past them it skips silently, which is what the validator
1375
+ * exists to prevent).
1376
+ */
1377
+ /*
1378
+ * The validator and the matcher must not drift apart: the server keeps
1379
+ * exactly the entries KubectlPolicy says the matcher can use, an
1380
+ * accepted pattern matches the command it describes, and the limits are
1381
+ * exactly where the matcher starts ignoring patterns (past them it skips
1382
+ * silently, which is what the validator exists to prevent). Round one
1383
+ * checked this against CommandPolicy — the Bash lane's glob — which is
1384
+ * not the matcher the cluster allowlist runs through.
1385
+ */
1386
+ describe("agrees with the kubectl matcher (KubectlPolicy)", () => {
1387
+ function argsOf(command: string): Array<string> {
1388
+ return KubectlPolicy.tokenize(command).args || [];
1389
+ }
1390
+
1391
+ const SET_IMAGE_ARGS: Array<string> = argsOf(
1392
+ "kubectl set image deployment/web web=nginx:2 -n web",
1393
+ );
1394
+
1395
+ /*
1396
+ * The one definition: every entry below is accepted by the server
1397
+ * exactly when describeAllowlistPatternProblem finds nothing wrong
1398
+ * with it — whatever that function decides, now or later.
1399
+ */
1400
+ it.each([
1401
+ [""],
1402
+ ["kubectl"],
1403
+ ["kubectl get\npods"],
1404
+ ['kubectl patch deployment/web -p \'{"spec":'],
1405
+ ["set image deployment/web * -n web"],
1406
+ ["Kubectl set image deployment/web * -n web"],
1407
+ ["kubectl set image deployment/web * -n web"],
1408
+ ["kubectl scale deployment/web --replicas=*"],
1409
+ ["* deployment/web -n web"],
1410
+ ["* * -n web"],
1411
+ ["*"],
1412
+ ["kubectl *"],
1413
+ ["kubectl delete * * -n *"],
1414
+ [`kubectl get ${"x ".repeat(70)}`],
1415
+ ])(
1416
+ "refuses %p exactly when KubectlPolicy says it cannot be used",
1417
+ async (pattern: string) => {
1418
+ const problem: string | null =
1419
+ KubectlPolicy.describeAllowlistPatternProblem(pattern.trim());
1420
+
1421
+ if (problem) {
1422
+ await expect(
1423
+ update({ aiKubectlCommandAllowlist: [pattern] }),
1424
+ ).rejects.toThrow(BadDataException);
1425
+ } else {
1426
+ const written: UpdateBy<KubernetesCluster> = await update({
1427
+ aiKubectlCommandAllowlist: [pattern],
1428
+ });
1429
+ expect(writtenAllowlist(written)).toEqual([pattern.trim()]);
1430
+
1431
+ // Whatever the server keeps, the matcher can read.
1432
+ expect(
1433
+ KubectlPolicy.tokenize(pattern.trim()).args?.length,
1434
+ ).toBeGreaterThan(0);
1435
+ }
1436
+ },
1437
+ );
1438
+
1439
+ it.each([
1440
+ ["kubectl set image deployment/web * -n web"],
1441
+ ["set image deployment/web * -n web"],
1442
+ [" kubectl set image deployment/web web=* -n web "],
1443
+ ])(
1444
+ "an accepted %p matches the command it describes",
1445
+ async (pattern: string) => {
1446
+ const written: UpdateBy<KubernetesCluster> = await update({
1447
+ aiKubectlCommandAllowlist: [pattern],
1448
+ });
1449
+
1450
+ expect(
1451
+ KubectlPolicy.matchesAllowlist({
1452
+ args: SET_IMAGE_ARGS,
1453
+ allowlistPatterns: writtenAllowlist(written) as Array<string>,
1454
+ }),
1455
+ ).toBe(true);
1456
+ },
1457
+ );
1458
+
1459
+ it("negative control: an accepted pattern for another object does not match", async () => {
1460
+ const written: UpdateBy<KubernetesCluster> = await update({
1461
+ aiKubectlCommandAllowlist: [
1462
+ "kubectl set image deployment/api * -n web",
1463
+ ],
1464
+ });
1465
+
1466
+ expect(
1467
+ KubectlPolicy.matchesAllowlist({
1468
+ args: SET_IMAGE_ARGS,
1469
+ allowlistPatterns: writtenAllowlist(written) as Array<string>,
1470
+ }),
1471
+ ).toBe(false);
1472
+ });
1473
+
1474
+ it(`the matcher ignores a pattern past the ${KUBECTL_ALLOWLIST_MAX_PATTERNS}th, which is why more are refused`, () => {
1475
+ const filler: Array<string> = [];
1476
+ for (let i: number = 0; i < KUBECTL_ALLOWLIST_MAX_PATTERNS; i++) {
1477
+ filler.push(`kubectl scale deployment/other-${i} --replicas=*`);
1478
+ }
1479
+
1480
+ const pattern: string = "kubectl set image deployment/web * -n web";
1481
+
1482
+ expect(
1483
+ KubectlPolicy.matchesAllowlist({
1484
+ args: SET_IMAGE_ARGS,
1485
+ allowlistPatterns: [...filler, pattern],
1486
+ }),
1487
+ ).toBe(false);
1488
+ expect(
1489
+ KubectlPolicy.matchesAllowlist({
1490
+ args: SET_IMAGE_ARGS,
1491
+ allowlistPatterns: [...filler.slice(1), pattern],
1492
+ }),
1493
+ ).toBe(true);
1494
+ });
1495
+
1496
+ it(`the matcher ignores a pattern longer than ${KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH} characters, which is why one is refused`, () => {
1497
+ const padded: (length: number) => string = (length: number): string => {
1498
+ const head: string = "kubectl set image deployment/web ";
1499
+ const tail: string = " -n web";
1500
+ return head + "*".repeat(length - head.length - tail.length) + tail;
1501
+ };
1502
+
1503
+ expect(
1504
+ KubectlPolicy.matchesAllowlist({
1505
+ args: SET_IMAGE_ARGS,
1506
+ allowlistPatterns: [
1507
+ padded(KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH + 1),
1508
+ ],
1509
+ }),
1510
+ ).toBe(false);
1511
+ expect(
1512
+ KubectlPolicy.matchesAllowlist({
1513
+ args: SET_IMAGE_ARGS,
1514
+ allowlistPatterns: [padded(KUBECTL_ALLOWLIST_MAX_PATTERN_LENGTH)],
1515
+ }),
1516
+ ).toBe(true);
1517
+ });
1518
+ });
1519
+ });
1520
+ });
1521
+
1522
+ /*
1523
+ * aiAccessConfiguredAt tells the in-cluster Runner's registration "an
1524
+ * operator has configured AI access here" apart from "nobody ever did", so
1525
+ * a Runner deleted and re-registered never re-applies the chart's defaults
1526
+ * over an operator's "off". This file pins the user-write side: every
1527
+ * operator write of an AI setting sets it once, nothing else does, and it
1528
+ * is never cleared.
1529
+ */
1530
+ describe("KubernetesClusterService AI access configured marker", () => {
1531
+ let updateBySpy: jest.SpyInstance;
1532
+
1533
+ beforeEach(() => {
1534
+ jest
1535
+ .spyOn(RunnerService, "findOneBy")
1536
+ .mockResolvedValue({ id: RUNNER_ID } as unknown as Runner);
1537
+ jest.spyOn(KubernetesClusterService, "findBy").mockResolvedValue([
1538
+ {
1539
+ id: CLUSTER_ID,
1540
+ projectId: PROJECT_ID,
1541
+ } as unknown as KubernetesCluster,
1542
+ ]);
1543
+ updateBySpy = jest
1544
+ .spyOn(KubernetesClusterService, "updateBy")
1545
+ .mockResolvedValue(1);
1546
+ // The feed writers are not what this block is about.
1547
+ jest
1548
+ .spyOn(
1549
+ KubernetesClusterService as unknown as {
1550
+ writeAiAccessSettingsChangedFeed: () => Promise<void>;
1551
+ },
1552
+ "writeAiAccessSettingsChangedFeed",
1553
+ )
1554
+ .mockResolvedValue(undefined);
1555
+ jest
1556
+ .spyOn(
1557
+ KubernetesClusterService as unknown as {
1558
+ writeKubernetesClusterUpdatedFeed: () => Promise<void>;
1559
+ },
1560
+ "writeKubernetesClusterUpdatedFeed",
1561
+ )
1562
+ .mockResolvedValue(undefined);
1563
+ });
1564
+
1565
+ afterEach(() => {
1566
+ jest.restoreAllMocks();
1567
+ });
1568
+
1569
+ async function runUpdate(
1570
+ data: Record<string, unknown>,
1571
+ props: DatabaseCommonInteractionProps,
1572
+ updatedItemIds: Array<ObjectID> = [CLUSTER_ID],
1573
+ ): Promise<UpdateBy<KubernetesCluster>> {
1574
+ const update: UpdateBy<KubernetesCluster> = updateBy(data, props);
1575
+ const onUpdate: OnUpdate<KubernetesCluster> =
1576
+ await hooks().onBeforeUpdate(update);
1577
+ await hooks().onUpdateSuccess(onUpdate, updatedItemIds);
1578
+ return update;
1579
+ }
1580
+
1581
+ function markerWrites(): Array<{
1582
+ query: Record<string, unknown>;
1583
+ data: Record<string, unknown>;
1584
+ props: DatabaseCommonInteractionProps;
1585
+ }> {
1586
+ return updateBySpy.mock.calls.map((call: Array<unknown>) => {
1587
+ return call[0] as {
1588
+ query: Record<string, unknown>;
1589
+ data: Record<string, unknown>;
1590
+ props: DatabaseCommonInteractionProps;
1591
+ };
1592
+ });
1593
+ }
1594
+
1595
+ it("marks the cluster when a user turns AI investigation off", async () => {
1596
+ await runUpdate(
1597
+ { isAiInvestigationEnabled: false },
1598
+ userProps(PROJECT_ID, [Permission.SettingsMember]),
1599
+ );
1600
+
1601
+ expect(markerWrites()).toHaveLength(1);
1602
+ const write: {
1603
+ query: Record<string, unknown>;
1604
+ data: Record<string, unknown>;
1605
+ props: DatabaseCommonInteractionProps;
1606
+ } = markerWrites()[0]!;
1607
+
1608
+ expect(write.data["aiAccessConfiguredAt"]).toBeInstanceOf(Date);
1609
+ expect(Object.keys(write.data)).toEqual(["aiAccessConfiguredAt"]);
1610
+ // Written by the server, as root — the column's update ACL is empty.
1611
+ expect(write.props.isRoot).toBe(true);
1612
+ });
1613
+
1614
+ it("only sets the marker where it is not set yet, so the first-configured time is kept", async () => {
1615
+ await runUpdate(
1616
+ { aiRemediationMode: KubernetesAiRemediationMode.Disabled },
1617
+ adminProps(),
1618
+ );
1619
+
1620
+ const query: Record<string, unknown> = markerWrites()[0]!.query;
1621
+
1622
+ expect(Object.keys(query).sort()).toEqual(
1623
+ ["_id", "aiAccessConfiguredAt"].sort(),
1624
+ );
1625
+ // A FindOperator, never a value: the write is conditional on IS NULL.
1626
+ expect(query["aiAccessConfiguredAt"]).not.toBeInstanceOf(Date);
1627
+ expect(query["aiAccessConfiguredAt"]).toBeTruthy();
1628
+ expect(JSON.stringify(query["_id"])).toContain(CLUSTER_ID.toString());
1629
+ });
1630
+
1631
+ it("does not slip the server-only column into the user's own write", async () => {
1632
+ const update: UpdateBy<KubernetesCluster> = await runUpdate(
1633
+ { aiRemediationMode: KubernetesAiRemediationMode.RequireApproval },
1634
+ adminProps(),
1635
+ );
1636
+
1637
+ expect(
1638
+ (update.data as unknown as Record<string, unknown>)[
1639
+ "aiAccessConfiguredAt"
1640
+ ],
1641
+ ).toBeUndefined();
1642
+ });
1643
+
1644
+ it.each([
1645
+ [{ aiAccessRunnerId: null }],
1646
+ [{ aiAccessCredential: null }],
1647
+ [{ aiKubectlCommandAllowlist: [] }],
1648
+ [{ aiRemediationMode: KubernetesAiRemediationMode.Disabled }],
1649
+ [{ isAiInvestigationEnabled: true }],
1650
+ ])(
1651
+ "marks the cluster for a user write of %p",
1652
+ async (data: Record<string, unknown>) => {
1653
+ await runUpdate(
1654
+ data,
1655
+ userProps(PROJECT_ID, [Permission.EditKubernetesCluster]),
1656
+ );
1657
+
1658
+ expect(markerWrites()).toHaveLength(1);
1659
+ },
1660
+ );
1661
+
1662
+ it("does not mark the cluster for a user write of an unrelated column", async () => {
1663
+ await runUpdate({ name: "x" }, adminProps());
1664
+
1665
+ expect(markerWrites()).toHaveLength(0);
1666
+ });
1667
+
1668
+ it("does not mark the cluster for a root heartbeat or outcome write", async () => {
1669
+ await runUpdate(
1670
+ { lastSeenAt: new Date(), otelCollectorStatus: "connected" },
1671
+ rootProps(),
1672
+ );
1673
+ await runUpdate({ aiAccessLastVerifiedAt: new Date() }, rootProps());
1674
+
1675
+ expect(markerWrites()).toHaveLength(0);
1676
+ });
1677
+
1678
+ /*
1679
+ * Registration writes as root and sets the marker on first bind itself;
1680
+ * the operator path must not double up on it.
1681
+ */
1682
+ it("does not mark the cluster for a root write of AI settings (registration marks its own)", async () => {
1683
+ await runUpdate(
1684
+ {
1685
+ aiAccessRunnerId: RUNNER_ID,
1686
+ isAiInvestigationEnabled: true,
1687
+ aiRemediationMode: KubernetesAiRemediationMode.RequireApproval,
1688
+ },
1689
+ rootProps(),
1690
+ );
1691
+
1692
+ expect(markerWrites()).toHaveLength(0);
1693
+ });
1694
+
1695
+ it("marks nothing when the update matched no row", async () => {
1696
+ await runUpdate({ isAiInvestigationEnabled: false }, adminProps(), []);
1697
+
1698
+ expect(markerWrites()).toHaveLength(0);
1699
+ });
1700
+
1701
+ it("marks nothing when the write is refused", async () => {
1702
+ await expect(
1703
+ runUpdate(
1704
+ { aiRemediationMode: KubernetesAiRemediationMode.BypassApproval },
1705
+ userProps(PROJECT_ID, [Permission.SettingsMember]),
1706
+ ),
1707
+ ).rejects.toThrow(NotAuthorizedException);
1708
+
1709
+ expect(markerWrites()).toHaveLength(0);
1710
+ });
1711
+
1712
+ it("never writes the marker as a clear", async () => {
1713
+ await runUpdate({ isAiInvestigationEnabled: false }, adminProps());
1714
+
1715
+ for (const write of markerWrites()) {
1716
+ expect(write.data["aiAccessConfiguredAt"]).not.toBeNull();
1717
+ }
1718
+ });
1719
+
1720
+ /*
1721
+ * Known follow-up 12: registration binds a cluster an operator configured
1722
+ * before installing the chart, and leaves one alone that had a Runner
1723
+ * bound before. "A Runner was bound here" is its own never-cleared
1724
+ * marker, stamped on every bind — here, an operator's.
1725
+ */
1726
+ describe("the Runner-bound marker", () => {
1727
+ function boundMarkerWrites(): Array<{
1728
+ query: Record<string, unknown>;
1729
+ data: Record<string, unknown>;
1730
+ props: DatabaseCommonInteractionProps;
1731
+ }> {
1732
+ return markerWrites().filter(
1733
+ (write: { data: Record<string, unknown> }) => {
1734
+ return "aiAccessRunnerBoundAt" in write.data;
1735
+ },
1736
+ );
1737
+ }
1738
+
1739
+ it.each([
1740
+ [{ aiAccessRunnerId: RUNNER_ID }],
1741
+ [{ aiAccessRunner: { _id: RUNNER_ID.toString() } }],
1742
+ ])(
1743
+ "is stamped, where it is not set yet, when an operator binds a Runner: %p",
1744
+ async (data: Record<string, unknown>) => {
1745
+ await runUpdate(data, adminProps());
1746
+
1747
+ const writes: Array<{
1748
+ query: Record<string, unknown>;
1749
+ data: Record<string, unknown>;
1750
+ props: DatabaseCommonInteractionProps;
1751
+ }> = boundMarkerWrites();
1752
+ expect(writes).toHaveLength(1);
1753
+ expect(writes[0]!.data["aiAccessRunnerBoundAt"]).toBeInstanceOf(Date);
1754
+ expect(Object.keys(writes[0]!.data)).toEqual(["aiAccessRunnerBoundAt"]);
1755
+ expect(Object.keys(writes[0]!.query).sort()).toEqual(
1756
+ ["_id", "aiAccessRunnerBoundAt"].sort(),
1757
+ );
1758
+ // Conditional on IS NULL: the first bind's time is kept.
1759
+ expect(writes[0]!.query["aiAccessRunnerBoundAt"]).not.toBeInstanceOf(
1760
+ Date,
1761
+ );
1762
+ expect(writes[0]!.props.isRoot).toBe(true);
1763
+ },
1764
+ );
1765
+
1766
+ it("negative control: clearing the Runner, or any other AI setting, does not stamp it", async () => {
1767
+ jest.spyOn(RunbookCredentialService, "findOneBy").mockResolvedValue({
1768
+ id: CREDENTIAL_ID,
1769
+ credentialType: RunbookCredentialType.Kubernetes,
1770
+ runners: [],
1771
+ } as unknown as RunbookCredential);
1772
+
1773
+ await runUpdate({ aiAccessRunnerId: null }, adminProps());
1774
+ await runUpdate(
1775
+ { aiRemediationMode: KubernetesAiRemediationMode.Automatic },
1776
+ adminProps(),
1777
+ );
1778
+ await runUpdate({ aiAccessCredentialId: CREDENTIAL_ID }, adminProps());
1779
+
1780
+ expect(boundMarkerWrites()).toHaveLength(0);
1781
+ });
1782
+
1783
+ it("negative control: a root write (registration stamps its own) does not stamp it", async () => {
1784
+ await runUpdate({ aiAccessRunnerId: RUNNER_ID }, rootProps());
1785
+
1786
+ expect(boundMarkerWrites()).toHaveLength(0);
1787
+ });
1788
+
1789
+ it("is stamped on an operator's create that binds a Runner", async () => {
1790
+ const create: CreateBy<KubernetesCluster> = createBy(
1791
+ {
1792
+ projectId: PROJECT_ID,
1793
+ name: "prod-us",
1794
+ aiAccessRunnerId: RUNNER_ID,
1795
+ },
1796
+ { isMasterAdmin: true, userId: ObjectID.generate() },
1797
+ );
1798
+ const onCreate: OnCreate<KubernetesCluster> =
1799
+ await hooks().onBeforeCreate(create);
1800
+
1801
+ jest
1802
+ .spyOn(
1803
+ KubernetesClusterService as unknown as {
1804
+ writeKubernetesClusterCreatedFeed: () => Promise<void>;
1805
+ },
1806
+ "writeKubernetesClusterCreatedFeed",
1807
+ )
1808
+ .mockResolvedValue(undefined);
1809
+
1810
+ await hooks().onCreateSuccess(onCreate, {
1811
+ id: CLUSTER_ID,
1812
+ } as unknown as KubernetesCluster);
1813
+
1814
+ expect(boundMarkerWrites()).toHaveLength(1);
1815
+ });
1816
+ });
1817
+
1818
+ describe("on create", () => {
1819
+ it("marks a cluster a master admin creates with AI settings", async () => {
1820
+ const create: CreateBy<KubernetesCluster> = createBy(
1821
+ {
1822
+ projectId: PROJECT_ID,
1823
+ name: "prod-us",
1824
+ isAiInvestigationEnabled: true,
1825
+ },
1826
+ { isMasterAdmin: true, userId: ObjectID.generate() },
1827
+ );
1828
+ const onCreate: OnCreate<KubernetesCluster> =
1829
+ await hooks().onBeforeCreate(create);
1830
+
1831
+ jest
1832
+ .spyOn(
1833
+ KubernetesClusterService as unknown as {
1834
+ writeKubernetesClusterCreatedFeed: () => Promise<void>;
1835
+ },
1836
+ "writeKubernetesClusterCreatedFeed",
1837
+ )
1838
+ .mockResolvedValue(undefined);
1839
+
1840
+ await hooks().onCreateSuccess(onCreate, {
1841
+ id: CLUSTER_ID,
1842
+ // The saved row carries column defaults; they must not count.
1843
+ isAiInvestigationEnabled: true,
1844
+ aiRemediationMode: KubernetesAiRemediationMode.Disabled,
1845
+ } as unknown as KubernetesCluster);
1846
+
1847
+ expect(markerWrites()).toHaveLength(1);
1848
+ });
1849
+
1850
+ it("does not mark a cluster created without AI settings, even though the saved row carries their defaults", async () => {
1851
+ const create: CreateBy<KubernetesCluster> = createBy({
1852
+ projectId: PROJECT_ID,
1853
+ name: "prod-us",
1854
+ });
1855
+ const onCreate: OnCreate<KubernetesCluster> =
1856
+ await hooks().onBeforeCreate(create);
1857
+
1858
+ jest
1859
+ .spyOn(
1860
+ KubernetesClusterService as unknown as {
1861
+ writeKubernetesClusterCreatedFeed: () => Promise<void>;
1862
+ },
1863
+ "writeKubernetesClusterCreatedFeed",
1864
+ )
1865
+ .mockResolvedValue(undefined);
1866
+
1867
+ await hooks().onCreateSuccess(onCreate, {
1868
+ id: CLUSTER_ID,
1869
+ isAiInvestigationEnabled: false,
1870
+ aiRemediationMode: KubernetesAiRemediationMode.Disabled,
1871
+ } as unknown as KubernetesCluster);
1872
+
1873
+ expect(markerWrites()).toHaveLength(0);
1874
+ });
1875
+ });
1876
+ });