@oneuptime/common 14.0.1 → 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 (1090) 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/KubernetesCluster.ts +429 -0
  7. package/Models/DatabaseModels/Monitor.ts +48 -0
  8. package/Models/DatabaseModels/Runner.ts +5 -4
  9. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  10. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  11. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  12. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  13. package/Server/API/AIInvestigationAPI.ts +341 -0
  14. package/Server/API/AutoRemediationAPI.ts +184 -2
  15. package/Server/API/CommonAPI.ts +70 -0
  16. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  17. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  18. package/Server/API/MonitorAPI.ts +140 -0
  19. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  20. package/Server/API/SlackAPI.ts +447 -99
  21. package/Server/API/StatusPageAPI.ts +146 -26
  22. package/Server/API/TelemetryAPI.ts +216 -5
  23. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  24. package/Server/API/WorkflowVariableAPI.ts +156 -0
  25. package/Server/Infrastructure/GlobalCache.ts +35 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  33. package/Server/Middleware/TelemetryIngest.ts +47 -2
  34. package/Server/Middleware/UserAuthorization.ts +56 -2
  35. package/Server/Services/AIAgentService.ts +1 -0
  36. package/Server/Services/AccessTokenService.ts +121 -48
  37. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  38. package/Server/Services/AlertEpisodeService.ts +14 -1
  39. package/Server/Services/AlertService.ts +134 -22
  40. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  41. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  42. package/Server/Services/BillingService.ts +1 -0
  43. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  44. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  45. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  46. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  47. package/Server/Services/IncidentEpisodeService.ts +14 -1
  48. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  49. package/Server/Services/IncidentMemberService.ts +21 -0
  50. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  51. package/Server/Services/IncidentService.ts +120 -20
  52. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  53. package/Server/Services/IncidentTemplateService.ts +83 -3
  54. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  55. package/Server/Services/KubernetesClusterService.ts +1570 -1
  56. package/Server/Services/LogAggregationService.ts +14 -6
  57. package/Server/Services/MonitorService.ts +651 -52
  58. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  59. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  60. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  61. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  62. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  63. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  64. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  65. package/Server/Services/ProbeService.ts +7 -1
  66. package/Server/Services/ProjectService.ts +11 -1
  67. package/Server/Services/RunbookCredentialService.ts +26 -1
  68. package/Server/Services/RunbookSecretService.ts +36 -0
  69. package/Server/Services/RunnerJobService.ts +941 -23
  70. package/Server/Services/RunnerService.ts +439 -6
  71. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  72. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  73. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  74. package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
  75. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  76. package/Server/Services/StatusPageService.ts +301 -74
  77. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  78. package/Server/Services/TeamMemberService.ts +309 -7
  79. package/Server/Services/TraceAggregationService.ts +10 -6
  80. package/Server/Services/UserEmailService.ts +1 -0
  81. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  82. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  83. package/Server/Services/UserNotificationRuleService.ts +4 -0
  84. package/Server/Services/UserService.ts +122 -1
  85. package/Server/Services/WorkflowVariableService.ts +593 -8
  86. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  87. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  88. package/Server/Types/Database/QueryHelper.ts +22 -0
  89. package/Server/Types/Markdown.ts +502 -27
  90. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  91. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  92. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  93. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  94. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  95. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  96. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  97. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  98. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  99. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  100. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  101. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  102. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  103. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  104. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  105. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  106. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  107. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  108. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  109. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  110. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  111. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  112. package/Server/Utils/PushNotificationUtil.ts +45 -0
  113. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  114. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  115. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  116. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  117. package/Server/Utils/StartServer.ts +4 -3
  118. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  119. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  120. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  121. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  122. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  123. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  124. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  125. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  126. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  127. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  128. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  129. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  130. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  131. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  132. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  133. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  134. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  135. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  136. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  137. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  138. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  139. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  140. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  141. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  142. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  143. package/Server/Utils/Workspace/Workspace.ts +171 -54
  144. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  145. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  146. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  147. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  148. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  149. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  150. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  151. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  152. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  153. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  154. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  155. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  156. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  157. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  158. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  159. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  160. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  161. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  162. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  163. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  164. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  165. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  166. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  167. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  168. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  169. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  170. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
  171. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  172. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  173. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  174. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  175. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  176. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  177. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  178. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  179. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  180. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  181. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  182. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  183. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  184. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  185. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  186. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  187. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  188. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  189. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  190. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  191. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  192. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  193. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  194. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  195. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  196. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  197. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  198. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  199. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  200. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  201. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  202. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  203. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  204. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  205. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  206. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  207. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  208. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  209. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  210. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  211. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  212. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  213. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  214. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  215. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  216. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  217. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  218. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  219. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  220. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  221. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  222. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  223. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  224. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  225. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  226. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  227. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  228. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  229. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  230. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  231. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  232. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  233. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  234. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  235. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  236. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  237. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  238. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  239. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  240. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  241. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  242. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  243. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  244. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  245. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  246. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  247. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  248. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  249. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  250. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  251. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  252. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  253. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  254. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  255. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  256. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  257. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  258. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  259. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  260. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  261. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  262. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  263. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  264. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  265. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  266. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  267. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  268. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  269. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  270. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  271. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  272. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  273. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  274. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  275. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  276. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  277. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  278. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  279. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  280. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  281. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  282. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  283. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  284. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  285. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  286. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  287. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  288. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  289. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  290. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  291. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  292. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  293. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  294. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  295. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  296. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  297. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  298. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  299. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  300. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  301. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  302. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  303. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  304. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  305. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  306. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  307. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  308. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  309. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  310. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  311. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  312. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  313. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  314. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  315. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  316. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  317. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
  318. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  319. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  320. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  321. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  322. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  323. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  324. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  325. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  326. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  327. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
  328. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
  329. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  330. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  331. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  332. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  333. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  334. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  335. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  336. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  337. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  338. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  339. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  340. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  341. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  342. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  343. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  344. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  345. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  346. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  347. package/Tests/Server/Types/Markdown.test.ts +1033 -0
  348. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  349. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  350. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  351. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  352. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  353. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  354. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  355. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  356. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  357. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  358. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  359. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  360. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  361. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  362. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  363. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  364. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  365. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  366. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  367. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  368. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  369. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  370. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  371. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  372. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
  373. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  374. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  375. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  376. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  377. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  378. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  379. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  380. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  381. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  382. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  383. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  384. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  385. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  386. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  387. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  388. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  389. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  390. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  391. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  392. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  393. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  394. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  395. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  396. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  397. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  398. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  399. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  400. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  401. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  402. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  403. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  404. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  405. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  406. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  407. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  408. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  409. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  410. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  411. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  412. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  413. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  414. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  415. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  416. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  417. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  418. package/Tests/Types/Timezone.test.ts +5 -0
  419. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  420. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  421. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  422. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  423. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  424. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  425. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  426. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  427. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  428. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  429. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  430. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  431. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  432. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  433. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  434. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  435. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  436. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  437. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  438. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  439. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  440. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  441. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  442. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  443. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  444. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  445. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  446. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  447. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  448. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  449. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  450. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  451. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  452. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  453. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  454. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  455. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  456. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  457. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  458. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  459. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  460. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  461. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  462. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  463. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  464. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  465. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  466. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  467. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  468. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  469. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  470. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  471. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  472. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  473. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  474. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  475. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  476. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  477. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  478. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  479. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  480. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  481. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  482. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  483. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  484. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  485. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  486. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  487. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  488. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  489. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  490. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  491. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  492. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  493. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  494. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  495. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  496. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  497. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  498. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  499. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  500. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  501. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  502. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  503. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  504. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  505. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  506. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  507. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  508. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  509. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  510. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  511. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  512. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  513. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  514. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  515. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  516. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  517. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  518. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  519. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  520. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  521. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  522. package/Types/Date.ts +121 -7
  523. package/Types/Email/EmailMessage.ts +8 -0
  524. package/Types/Email/EmailTemplateType.ts +1 -0
  525. package/Types/Exception/ExceptionMessages.ts +1 -0
  526. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  527. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  528. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  529. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  530. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  531. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  532. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  533. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  534. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  535. package/Types/Rum/SessionReplay.ts +55 -2
  536. package/Types/Rum/SessionReplayApi.ts +44 -1
  537. package/Types/Rum/UserFlow.ts +187 -0
  538. package/Types/Runbook/RunbookStepType.ts +10 -0
  539. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  540. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  541. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  542. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  543. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  544. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  545. package/Types/Timezone.ts +5 -0
  546. package/Types/TimezoneAlias.ts +385 -0
  547. package/Types/Workflow/Templates.ts +3041 -2
  548. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  549. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  550. package/Typings/Index.d.ts +24 -0
  551. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  552. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  553. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  554. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  555. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  556. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  557. package/UI/Components/Calendar/Calendar.css +83 -0
  558. package/UI/Components/Calendar/Calendar.tsx +136 -7
  559. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  560. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  561. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  562. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  563. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  564. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  565. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  566. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  567. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  568. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  569. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  570. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  571. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  572. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  573. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  574. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  575. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  576. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  577. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  578. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  579. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  580. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  581. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  582. package/UI/Components/Page/ModelPage.tsx +59 -3
  583. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  584. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  585. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  586. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  587. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  588. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  589. package/UI/Utils/Timezone.ts +69 -28
  590. package/Utils/AI/InvestigationReport.ts +195 -25
  591. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  592. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  593. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  594. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  595. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  596. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  597. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  598. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  599. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  600. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  601. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  602. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  603. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  604. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  605. package/Utils/Rum/SessionIdentity.ts +44 -0
  606. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  607. package/Utils/Rum/UserFlow.ts +1527 -0
  608. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  609. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  610. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  611. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  612. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  613. package/Utils/Uptime/UptimeUtil.ts +122 -38
  614. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  615. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  616. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  617. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  618. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  619. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  620. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  621. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  622. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  623. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  624. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  625. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  626. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  627. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  628. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  629. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  630. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  631. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  632. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  633. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  634. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  635. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  636. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  637. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  638. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  639. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  640. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  641. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  642. package/build/dist/Server/API/CommonAPI.js +49 -0
  643. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  644. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  645. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  646. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  647. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  648. package/build/dist/Server/API/MonitorAPI.js +107 -1
  649. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  650. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  651. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  652. package/build/dist/Server/API/SlackAPI.js +322 -78
  653. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  654. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  655. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  656. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  657. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  658. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  659. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  660. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  661. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  662. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  663. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  664. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  665. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  666. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  667. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  668. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  669. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  670. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  671. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  672. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  673. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  674. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  675. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  676. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  677. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  678. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  679. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  680. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  681. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  682. package/build/dist/Server/Services/AIAgentService.js +1 -0
  683. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  684. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  685. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  686. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  687. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  688. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  689. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  690. package/build/dist/Server/Services/AlertService.js +110 -23
  691. package/build/dist/Server/Services/AlertService.js.map +1 -1
  692. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  693. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  694. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  695. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  696. package/build/dist/Server/Services/BillingService.js +1 -0
  697. package/build/dist/Server/Services/BillingService.js.map +1 -1
  698. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  699. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  700. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  701. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  702. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  703. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  704. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  705. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  706. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  707. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  708. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  709. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  710. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  711. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  712. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  713. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  714. package/build/dist/Server/Services/IncidentService.js +99 -21
  715. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  716. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  717. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  718. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  719. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  720. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  721. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  722. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  723. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  724. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  725. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  726. package/build/dist/Server/Services/MonitorService.js +487 -48
  727. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  728. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  729. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  730. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  731. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  732. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  733. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  734. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  735. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  736. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  737. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  738. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  739. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  740. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  741. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  742. package/build/dist/Server/Services/ProbeService.js +7 -1
  743. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  744. package/build/dist/Server/Services/ProjectService.js +11 -1
  745. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  746. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  747. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  748. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  749. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  750. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  751. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  752. package/build/dist/Server/Services/RunnerService.js +344 -5
  753. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  754. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  755. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  756. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  757. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  758. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  759. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  760. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
  761. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  762. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  763. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  764. package/build/dist/Server/Services/StatusPageService.js +230 -32
  765. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  766. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  767. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  768. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  769. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  770. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  771. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  772. package/build/dist/Server/Services/UserEmailService.js +1 -0
  773. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  774. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  775. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  776. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  777. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  778. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  779. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  780. package/build/dist/Server/Services/UserService.js +107 -1
  781. package/build/dist/Server/Services/UserService.js.map +1 -1
  782. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  783. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  784. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  785. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  786. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  787. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  788. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  789. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  790. package/build/dist/Server/Types/Markdown.js +394 -27
  791. package/build/dist/Server/Types/Markdown.js.map +1 -1
  792. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  793. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  794. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  795. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  796. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  797. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  798. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  799. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  800. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  801. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  802. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  803. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  804. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  805. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  806. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  807. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  808. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  809. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  810. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  811. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  812. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  813. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  814. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  815. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  816. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  817. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  818. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  819. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  820. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  821. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  822. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  823. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  824. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  825. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  826. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  827. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  828. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  829. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  830. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  831. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  832. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  833. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  834. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  835. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  836. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  837. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  838. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  839. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  840. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  841. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  842. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  843. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  844. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  845. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  846. package/build/dist/Server/Utils/StartServer.js +4 -3
  847. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  848. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  849. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  850. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  851. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  852. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  853. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  854. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  855. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  856. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  857. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  858. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  859. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  860. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  861. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  862. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  863. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  864. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  865. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  866. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  867. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  868. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  869. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  870. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  871. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  872. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  873. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  874. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  875. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  876. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  877. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  878. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  879. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  880. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  881. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  882. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  883. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  884. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  885. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  886. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  887. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  888. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  889. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  890. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  891. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  892. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  893. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  894. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  895. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  896. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  897. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  898. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  899. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  900. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  901. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  902. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  903. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  904. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  905. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  906. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  907. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  908. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  909. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  910. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  911. package/build/dist/Types/Date.js +97 -7
  912. package/build/dist/Types/Date.js.map +1 -1
  913. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  914. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  915. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  916. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  917. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  918. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  919. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  920. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  921. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  922. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  923. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  924. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  925. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  926. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  927. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  928. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  929. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  930. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  931. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  932. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  933. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  934. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  935. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  936. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  937. package/build/dist/Types/Rum/UserFlow.js +115 -0
  938. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  939. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  940. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  941. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  942. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  943. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  944. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  945. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  946. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  947. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  948. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  949. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  950. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  951. package/build/dist/Types/Timezone.js +5 -0
  952. package/build/dist/Types/Timezone.js.map +1 -1
  953. package/build/dist/Types/TimezoneAlias.js +340 -0
  954. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  955. package/build/dist/Types/Workflow/Templates.js +2694 -2
  956. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  957. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  958. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  959. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  960. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  961. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  962. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  963. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  964. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  965. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  966. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  967. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  968. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  969. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  970. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  971. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  972. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  973. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  974. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  975. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  976. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  977. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  978. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  979. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  980. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  981. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  982. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  983. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  984. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  985. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  986. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  987. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  988. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  989. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  990. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  991. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  992. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  993. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  994. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  995. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  996. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  997. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  998. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  999. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1000. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1001. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1002. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1003. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1004. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1005. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1006. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1007. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1008. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1009. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1010. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1011. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1012. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1013. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1014. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1015. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1016. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1017. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1018. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1019. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1020. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1021. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1022. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1023. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1024. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1025. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1026. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1027. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1028. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1029. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1030. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1031. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1032. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1033. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1034. package/build/dist/UI/Utils/Timezone.js +52 -17
  1035. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1036. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1037. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1038. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1039. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1040. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1041. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1042. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1043. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1044. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1045. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1046. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1047. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1048. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1049. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1050. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1051. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1052. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1053. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1054. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1055. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1056. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1057. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1058. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1059. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1060. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1061. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1062. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1063. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1064. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1065. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1066. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1067. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1068. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1069. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1070. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1071. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1072. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1073. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1074. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1075. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1076. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1077. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1078. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1079. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1080. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1081. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1082. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1083. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1084. package/jest.config.json +2 -1
  1085. package/package.json +2 -1
  1086. package/tsconfig.tests.json +36 -0
  1087. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1088. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1089. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1090. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -1,13 +1,50 @@
1
1
  import DatabaseService from "./DatabaseService";
2
+ import AutoRemediationSuggestionService from "./AutoRemediationSuggestionService";
2
3
  import KubernetesClusterLabelRuleEngineService from "./KubernetesClusterLabelRuleEngineService";
3
4
  import KubernetesClusterOwnerRuleEngineService from "./KubernetesClusterOwnerRuleEngineService";
5
+ import RunbookCredentialService from "./RunbookCredentialService";
6
+ import RunnerService, { Service as RunnerServiceClass } from "./RunnerService";
7
+ import UserService from "./UserService";
8
+ import AutoRemediationSuggestion from "../../Models/DatabaseModels/AutoRemediationSuggestion";
4
9
  import Model from "../../Models/DatabaseModels/KubernetesCluster";
5
10
  import Label from "../../Models/DatabaseModels/Label";
6
- import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
11
+ import RunbookCredential from "../../Models/DatabaseModels/RunbookCredential";
12
+ import Runner from "../../Models/DatabaseModels/Runner";
13
+ import CreateBy from "../Types/Database/CreateBy";
14
+ import DeleteBy from "../Types/Database/DeleteBy";
15
+ import Select from "../Types/Database/Select";
16
+ import UpdateBy from "../Types/Database/UpdateBy";
17
+ import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
18
+ import AutoRemediationSuggestionStatus from "../../Types/AutoRemediation/AutoRemediationSuggestionStatus";
19
+ import {
20
+ AiRemediationCommand,
21
+ AiRemediationCommandExecutionStatus,
22
+ AiRemediationCommandPlan,
23
+ AiRemediationCommandPlanUtil,
24
+ } from "../../Types/AutoRemediation/AiRemediationCommandPlan";
25
+ import KubectlPolicy, {
26
+ KUBECTL_ALLOWLIST_MAX_PATTERNS,
27
+ } from "../../Utils/AiRemediation/KubectlPolicy";
28
+ import RelationIdUtil from "../Utils/Database/RelationIdUtil";
29
+ import { holdsAnyPermission } from "../Utils/Runbook/RunbookExecutePermission";
30
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
31
+ import BadDataException from "../../Types/Exception/BadDataException";
32
+ import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
33
+ import {
34
+ KubernetesAiRemediationMode,
35
+ isUnattendedRemediationMode,
36
+ } from "../../Types/Kubernetes/KubernetesClusterAiAccess";
37
+ import {
38
+ KUBERNETES_AI_ACCESS_ADMIN_PERMISSIONS,
39
+ KUBERNETES_AI_ACCESS_CREDENTIAL_PERMISSIONS,
40
+ } from "../../Types/Kubernetes/KubernetesClusterAiAccessPermissions";
41
+ import { PermissionHelper } from "../../Types/Permission";
42
+ import RunbookCredentialType from "../../Types/Runbook/RunbookCredentialType";
7
43
  import KubernetesClusterFeedService from "./KubernetesClusterFeedService";
8
44
  import { KubernetesClusterFeedEventType } from "../../Models/DatabaseModels/KubernetesClusterFeed";
9
45
  import ResourceFeedUtil from "../Utils/ResourceFeed/ResourceFeedUtil";
10
46
  import { Blue500, Gray500, Green500, Yellow500 } from "../../Types/BrandColors";
47
+ import Color from "../../Types/Color";
11
48
  import { JSONObject } from "../../Types/JSON";
12
49
  import URL from "../../Types/API/URL";
13
50
  import DatabaseConfig from "../DatabaseConfig";
@@ -27,16 +64,1067 @@ const LAST_SEEN_THROTTLE_SECONDS: number = 60;
27
64
  const LABELS_APPLIED_CACHE_NAMESPACE: string = "k8s-cluster-labels-applied";
28
65
  const LABELS_APPLIED_CACHE_TTL_SECONDS: number = 60;
29
66
 
67
+ /*
68
+ * The two spellings each AI-access relation can arrive under: the FK column
69
+ * server-side callers write, and the relation object the dashboard's forms
70
+ * post. See RelationIdUtil for why a hook must read both.
71
+ */
72
+ const AI_ACCESS_RUNNER_KEYS: Array<string> = [
73
+ "aiAccessRunnerId",
74
+ "aiAccessRunner",
75
+ ];
76
+ const AI_ACCESS_CREDENTIAL_KEYS: Array<string> = [
77
+ "aiAccessCredentialId",
78
+ "aiAccessCredential",
79
+ ];
80
+
81
+ /*
82
+ * Every AI access setting an operator can write — on the cluster's AI page,
83
+ * through the API or through Terraform. A non-root write of any of them is
84
+ * checked against who may loosen AI access, marks the cluster as
85
+ * AI-configured (aiAccessConfiguredAt) and is recorded on the cluster feed.
86
+ */
87
+ export const AI_ACCESS_SETTING_KEYS: Array<string> = [
88
+ ...AI_ACCESS_RUNNER_KEYS,
89
+ ...AI_ACCESS_CREDENTIAL_KEYS,
90
+ "isAiInvestigationEnabled",
91
+ "aiRemediationMode",
92
+ "aiKubectlCommandAllowlist",
93
+ ];
94
+
95
+ /*
96
+ * What the binding checks read off a Runner and a credential: whether the
97
+ * Runner is a kubernetes-agent row (and whose), and which Runners the
98
+ * credential is assigned to.
99
+ */
100
+ const AI_ACCESS_BINDING_RUNNER_SELECT: Select<Runner> = {
101
+ _id: true,
102
+ name: true,
103
+ hostInfo: true,
104
+ };
105
+
106
+ const AI_ACCESS_BINDING_CREDENTIAL_SELECT: Select<RunbookCredential> = {
107
+ _id: true,
108
+ credentialType: true,
109
+ runners: { _id: true },
110
+ };
111
+
112
+ // Runner and credential rows the binding checks already loaded, by id.
113
+ interface AiAccessBindingRows {
114
+ runners: Map<string, Runner>;
115
+ credentials: Map<string, RunbookCredential>;
116
+ }
117
+
118
+ const KUBECTL_ALLOWLIST_SHAPE_HINT: string =
119
+ 'The kubectl allowlist must be a JSON array of kubectl command patterns, for example ["kubectl set image deployment/web * -n web"].';
120
+
121
+ /*
122
+ * How much each remediation mode lets OneUptime AI do without a human,
123
+ * least first. Automatic runs a strict subset of what Bypass approval runs
124
+ * (safe changes and allowlisted shapes, never an unlisted riskier change),
125
+ * so moving from Bypass approval to Automatic is a tightening.
126
+ */
127
+ const REMEDIATION_MODES_BY_AUTONOMY: Array<KubernetesAiRemediationMode> = [
128
+ KubernetesAiRemediationMode.Disabled,
129
+ KubernetesAiRemediationMode.RequireApproval,
130
+ KubernetesAiRemediationMode.Automatic,
131
+ KubernetesAiRemediationMode.BypassApproval,
132
+ ];
133
+
134
+ // The AI page's short name for each mode, used on the cluster feed.
135
+ const REMEDIATION_MODE_FEED_LABELS: Record<
136
+ KubernetesAiRemediationMode,
137
+ string
138
+ > = {
139
+ [KubernetesAiRemediationMode.Disabled]: "Off",
140
+ [KubernetesAiRemediationMode.RequireApproval]: "Ask for approval",
141
+ [KubernetesAiRemediationMode.Automatic]: "Automatic",
142
+ [KubernetesAiRemediationMode.BypassApproval]: "Bypass approval",
143
+ };
144
+
145
+ /*
146
+ * One answer for "no such credential in this project" and "not a Kubernetes
147
+ * credential": a caller who may not read credentials must not be able to
148
+ * tell the two apart and so learn which ids exist and what type they are.
149
+ */
150
+ export const AI_ACCESS_CREDENTIAL_REFUSAL: string =
151
+ "Credential not found, or it is not a Kubernetes credential in this project. The AI access credential must be a Kubernetes credential (API server URL and ServiceAccount token) that belongs to this project.";
152
+
153
+ export function getAiAccessAdminRefusal(): string {
154
+ return `You need one of these permissions to let OneUptime AI do more on a Kubernetes cluster (switch AI remediation to Automatic or Bypass approval, add kubectl allowlist patterns, or bind a Runner or credential): ${PermissionHelper.getPermissionTitles(
155
+ KUBERNETES_AI_ACCESS_ADMIN_PERMISSIONS,
156
+ ).join(
157
+ ", ",
158
+ )}. Anyone who may edit the cluster can still turn AI remediation off or back to Ask for approval, remove allowlist patterns, and clear the Runner or credential.`;
159
+ }
160
+
161
+ export function getAiAccessCredentialRefusal(): string {
162
+ return `Binding a Kubernetes credential to a cluster also needs permission to read credentials. You need one of these permissions: ${PermissionHelper.getPermissionTitles(
163
+ KUBERNETES_AI_ACCESS_CREDENTIAL_PERMISSIONS,
164
+ ).join(", ")}.`;
165
+ }
166
+
167
+ /*
168
+ * A cluster's AI access settings as they stood before an operator's write:
169
+ * the baseline "does this write loosen anything?" is decided against, and
170
+ * the "before" half of the feed item that records the change. Normalized
171
+ * the way the readers normalize them (an unknown mode is Disabled, an
172
+ * unusable allowlist is empty), because that is what was in effect.
173
+ */
174
+ export interface AiAccessSettingsSnapshot {
175
+ projectId?: ObjectID | undefined;
176
+ /*
177
+ * Which cluster this is, for "is the chosen Runner THIS cluster's agent
178
+ * Runner?" (validateAiAccessBindingPairs). Not an AI setting itself.
179
+ */
180
+ clusterIdentifier?: string | undefined;
181
+ isAiInvestigationEnabled: boolean;
182
+ aiRemediationMode: KubernetesAiRemediationMode;
183
+ aiKubectlCommandAllowlist: Array<string>;
184
+ aiAccessRunnerId: string | null;
185
+ aiAccessCredentialId: string | null;
186
+ }
187
+
188
+ // What a cluster that was never AI-configured has: the column defaults.
189
+ const NEVER_CONFIGURED_AI_ACCESS: AiAccessSettingsSnapshot = {
190
+ isAiInvestigationEnabled: false,
191
+ aiRemediationMode: KubernetesAiRemediationMode.Disabled,
192
+ aiKubectlCommandAllowlist: [],
193
+ aiAccessRunnerId: null,
194
+ aiAccessCredentialId: null,
195
+ };
196
+
197
+ /*
198
+ * Handed from onBeforeUpdate to onUpdateSuccess for an operator's write of
199
+ * an AI access setting, keyed by cluster id. Its presence is the signal that
200
+ * the write must mark the cluster configured and be recorded on the feed —
201
+ * both happen only after the update succeeded.
202
+ */
203
+ export interface AiAccessWriteCarryForward {
204
+ previousAiAccessSettings: Record<string, AiAccessSettingsSnapshot>;
205
+ }
206
+
207
+ /*
208
+ * Handed from onBeforeDelete to onDeleteSuccess: the cluster-level AI
209
+ * remediation rounds still in flight on the clusters being deleted, read
210
+ * before the foreign key nulls their link, and the clusters' names.
211
+ */
212
+ interface ClusterDeleteCarryForward {
213
+ inFlightRounds: Array<AutoRemediationSuggestion>;
214
+ clusterNames: Record<string, string>;
215
+ }
216
+
217
+ function getClusterDeleteCarryForward(
218
+ carryForward: unknown,
219
+ ): ClusterDeleteCarryForward | null {
220
+ if (
221
+ carryForward &&
222
+ typeof carryForward === "object" &&
223
+ "inFlightRounds" in carryForward
224
+ ) {
225
+ return carryForward as ClusterDeleteCarryForward;
226
+ }
227
+
228
+ return null;
229
+ }
230
+
231
+ // What loosening an operator's write would do to one cluster.
232
+ interface AiAccessLoosening {
233
+ loosens: boolean;
234
+ bindsCredential: boolean;
235
+ }
236
+
30
237
  export class Service extends DatabaseService<Model> {
31
238
  public constructor() {
32
239
  super(Model);
33
240
  }
34
241
 
242
+ @CaptureSpan()
243
+ protected override async onBeforeCreate(
244
+ createBy: CreateBy<Model>,
245
+ ): Promise<OnCreate<Model>> {
246
+ const projectId: ObjectID | undefined =
247
+ createBy.data.projectId ||
248
+ createBy.data.project?.id ||
249
+ createBy.props.tenantId ||
250
+ undefined;
251
+
252
+ const data: JSONObject = createBy.data as unknown as JSONObject;
253
+
254
+ this.validateAiRemediationSettings(data);
255
+
256
+ /*
257
+ * The AI columns' create ACLs are empty, so a user's create cannot carry
258
+ * them today and this is defence in depth: were one ever opened, a new
259
+ * cluster starts from the never-configured defaults, and anything above
260
+ * them is a loosening like any other.
261
+ */
262
+ if (!createBy.props.isRoot && !createBy.props.isMasterAdmin) {
263
+ this.assertMayChangeAiAccess({
264
+ data,
265
+ props: createBy.props,
266
+ current: [{ ...NEVER_CONFIGURED_AI_ACCESS, projectId }],
267
+ });
268
+ }
269
+
270
+ const loaded: AiAccessBindingRows =
271
+ await this.validateAiAccessBindingsBelongToProject({
272
+ data,
273
+ projectId,
274
+ });
275
+
276
+ // An operator's binding must also be a pair that can work.
277
+ if (!createBy.props.isRoot) {
278
+ await this.validateAiAccessBindingPairs({
279
+ data,
280
+ clusters: [
281
+ {
282
+ ...NEVER_CONFIGURED_AI_ACCESS,
283
+ projectId,
284
+ clusterIdentifier:
285
+ typeof data["clusterIdentifier"] === "string"
286
+ ? (data["clusterIdentifier"] as string)
287
+ : undefined,
288
+ },
289
+ ],
290
+ loaded,
291
+ });
292
+ }
293
+
294
+ return {
295
+ createBy,
296
+ carryForward: {
297
+ writesAiAccessSettings:
298
+ !createBy.props.isRoot && this.isAiAccessSettingWritten(data),
299
+ },
300
+ };
301
+ }
302
+
303
+ @CaptureSpan()
304
+ protected override async onBeforeUpdate(
305
+ updateBy: UpdateBy<Model>,
306
+ ): Promise<OnUpdate<Model>> {
307
+ const data: JSONObject = (updateBy.data || {}) as unknown as JSONObject;
308
+
309
+ this.validateAiRemediationSettings(data);
310
+
311
+ /*
312
+ * An operator's write of an AI access setting is gated, marked and
313
+ * recorded. Root writes are the server's own — the in-cluster Runner's
314
+ * registration (which writes its own feed item and marker) and the
315
+ * outcome bookkeeping after each kubectl command — and are none of the
316
+ * three.
317
+ */
318
+ let carryForward: AiAccessWriteCarryForward | null = null;
319
+
320
+ if (!updateBy.props.isRoot && this.isAiAccessSettingWritten(data)) {
321
+ const previousAiAccessSettings: Record<string, AiAccessSettingsSnapshot> =
322
+ await this.getAiAccessSettingsForUpdateQuery(updateBy);
323
+
324
+ if (!updateBy.props.isMasterAdmin) {
325
+ this.assertMayChangeAiAccess({
326
+ data,
327
+ props: updateBy.props,
328
+ current: Object.values(previousAiAccessSettings),
329
+ });
330
+ }
331
+
332
+ carryForward = { previousAiAccessSettings };
333
+ }
334
+
335
+ if (
336
+ RelationIdUtil.isWritten(Object.keys(data), [
337
+ ...AI_ACCESS_RUNNER_KEYS,
338
+ ...AI_ACCESS_CREDENTIAL_KEYS,
339
+ ])
340
+ ) {
341
+ /*
342
+ * Root/API updates do not always carry a tenantId, so fall back to
343
+ * the project of each cluster the query actually matches — a Runner
344
+ * must belong to every one of them.
345
+ */
346
+ const projectIds: Array<ObjectID> = updateBy.props.tenantId
347
+ ? [updateBy.props.tenantId]
348
+ : await this.getProjectIdsForUpdateQuery(updateBy);
349
+
350
+ const loaded: AiAccessBindingRows = {
351
+ runners: new Map<string, Runner>(),
352
+ credentials: new Map<string, RunbookCredential>(),
353
+ };
354
+
355
+ for (const projectId of projectIds) {
356
+ await this.validateAiAccessBindingsBelongToProject({
357
+ data,
358
+ projectId,
359
+ loaded,
360
+ });
361
+ }
362
+
363
+ /*
364
+ * Nothing matched: there is no project to check against, and the
365
+ * update will write nothing — unless the query was empty, which
366
+ * would never happen through updateOneById. Fail closed on a
367
+ * binding that cannot be checked.
368
+ */
369
+ if (projectIds.length === 0) {
370
+ await this.validateAiAccessBindingsBelongToProject({
371
+ data,
372
+ projectId: undefined,
373
+ });
374
+ }
375
+
376
+ /*
377
+ * An operator's binding must also be a pair that can work, judged
378
+ * per cluster against what it is bound to now (read above). The
379
+ * server's own writes — registration binds this cluster's agent Runner
380
+ * and clears the credential — are not operator choices.
381
+ */
382
+ if (carryForward) {
383
+ await this.validateAiAccessBindingPairs({
384
+ data,
385
+ clusters: Object.values(carryForward.previousAiAccessSettings),
386
+ loaded,
387
+ });
388
+ }
389
+ }
390
+
391
+ return { updateBy, carryForward };
392
+ }
393
+
394
+ /*
395
+ * A cluster-level AI remediation round (a suggestion the cluster's mode
396
+ * produced, not a rule) still Planning or waiting in Suggested for the
397
+ * cluster being deleted can never finish: its kubectl names a cluster
398
+ * that is about to be gone, and the foreign key nulls its link. Read here,
399
+ * before the delete, which ones there are; settled in onDeleteSuccess for
400
+ * the clusters that were actually deleted (the delete may still be
401
+ * refused after this hook runs).
402
+ */
403
+ @CaptureSpan()
404
+ protected override async onBeforeDelete(
405
+ deleteBy: DeleteBy<Model>,
406
+ ): Promise<OnDelete<Model>> {
407
+ let inFlightRounds: Array<AutoRemediationSuggestion> = [];
408
+ const clusterNames: Record<string, string> = {};
409
+
410
+ try {
411
+ const clusters: Array<Model> = await this.findBy({
412
+ query: {
413
+ ...deleteBy.query,
414
+ ...(deleteBy.props.tenantId
415
+ ? { projectId: deleteBy.props.tenantId }
416
+ : {}),
417
+ },
418
+ select: { _id: true, name: true, clusterIdentifier: true },
419
+ limit: LIMIT_MAX,
420
+ skip: 0,
421
+ props: { isRoot: true },
422
+ });
423
+
424
+ const clusterIds: Array<ObjectID> = [];
425
+
426
+ for (const cluster of clusters) {
427
+ if (cluster.id) {
428
+ clusterIds.push(cluster.id);
429
+ clusterNames[cluster.id.toString()] =
430
+ cluster.name || cluster.clusterIdentifier || "this cluster";
431
+ }
432
+ }
433
+
434
+ if (clusterIds.length > 0) {
435
+ inFlightRounds = await AutoRemediationSuggestionService.findBy({
436
+ query: {
437
+ kubernetesClusterId: QueryHelper.any(clusterIds),
438
+ status: QueryHelper.any([
439
+ AutoRemediationSuggestionStatus.Planning,
440
+ AutoRemediationSuggestionStatus.Suggested,
441
+ ]),
442
+ },
443
+ select: {
444
+ _id: true,
445
+ status: true,
446
+ kubernetesClusterId: true,
447
+ rationaleMarkdown: true,
448
+ commandPlan: true,
449
+ },
450
+ limit: LIMIT_MAX,
451
+ skip: 0,
452
+ props: { isRoot: true },
453
+ });
454
+ }
455
+ } catch (error) {
456
+ /*
457
+ * Best effort: a failed read must not block deleting the cluster;
458
+ * the rounds then fail on their next command as the cluster is gone.
459
+ */
460
+ logger.error(
461
+ `KubernetesClusterService: could not read the in-flight AI remediation rounds of the cluster(s) being deleted: ${error}`,
462
+ );
463
+ }
464
+
465
+ const carryForward: ClusterDeleteCarryForward = {
466
+ inFlightRounds,
467
+ clusterNames,
468
+ };
469
+
470
+ return { deleteBy, carryForward };
471
+ }
472
+
473
+ @CaptureSpan()
474
+ protected override async onDeleteSuccess(
475
+ onDelete: OnDelete<Model>,
476
+ deletedItemIds: Array<ObjectID>,
477
+ ): Promise<OnDelete<Model>> {
478
+ const carryForward: ClusterDeleteCarryForward | null =
479
+ getClusterDeleteCarryForward(onDelete.carryForward);
480
+
481
+ if (carryForward && carryForward.inFlightRounds.length > 0) {
482
+ const deleted: Set<string> = new Set<string>(
483
+ deletedItemIds.map((id: ObjectID) => {
484
+ return id.toString();
485
+ }),
486
+ );
487
+
488
+ for (const suggestion of carryForward.inFlightRounds) {
489
+ const clusterId: string =
490
+ suggestion.kubernetesClusterId?.toString() || "";
491
+
492
+ if (!deleted.has(clusterId)) {
493
+ continue;
494
+ }
495
+
496
+ await this.settleRoundOfDeletedCluster({
497
+ suggestion,
498
+ clusterName: carryForward.clusterNames[clusterId] || "this cluster",
499
+ deletedByUserId: onDelete.deleteBy.props.userId,
500
+ });
501
+ }
502
+ }
503
+
504
+ return onDelete;
505
+ }
506
+
507
+ /*
508
+ * Settle one in-flight cluster-level round of a deleted cluster the way a
509
+ * human dismissal settles it (the conditional status transition, so a
510
+ * planner finishing at the same moment loses cleanly and writes nothing),
511
+ * with a note on the suggestion saying why — and, for a round that had
512
+ * already run commands, that those changes were made and nothing was
513
+ * rolled back. Best effort: a settle that fails must not fail the delete
514
+ * that already happened.
515
+ */
516
+ private async settleRoundOfDeletedCluster(data: {
517
+ suggestion: AutoRemediationSuggestion;
518
+ clusterName: string;
519
+ deletedByUserId?: ObjectID | undefined;
520
+ }): Promise<void> {
521
+ const { suggestion } = data;
522
+
523
+ try {
524
+ const plan: AiRemediationCommandPlan | null =
525
+ AiRemediationCommandPlanUtil.parse(suggestion.commandPlan);
526
+ // A Skipped command never ran; any other execution record may have.
527
+ const executedCount: number = (plan?.commands || []).filter(
528
+ (command: AiRemediationCommand) => {
529
+ return (
530
+ command.execution !== undefined &&
531
+ command.execution.status !==
532
+ AiRemediationCommandExecutionStatus.Skipped
533
+ );
534
+ },
535
+ ).length;
536
+
537
+ const note: string =
538
+ executedCount > 0
539
+ ? `**Dismissed: the Kubernetes cluster "${data.clusterName}" this remediation was for was deleted.** ${executedCount} command(s) had already run on it before; nothing was rolled back and no further command will run.`
540
+ : `**Dismissed: the Kubernetes cluster "${data.clusterName}" this remediation was for was deleted.** None of its kubectl commands ran, and none will.`;
541
+
542
+ await AutoRemediationSuggestionService.attemptStatusTransition({
543
+ suggestionId: suggestion.id!,
544
+ fromStatus: suggestion.status!,
545
+ set: {
546
+ status: AutoRemediationSuggestionStatus.Dismissed,
547
+ dismissedAt: OneUptimeDate.getCurrentDate(),
548
+ ...(data.deletedByUserId
549
+ ? { dismissedByUserId: data.deletedByUserId.toString() }
550
+ : {}),
551
+ rationaleMarkdown: suggestion.rationaleMarkdown
552
+ ? `${note}\n\n${suggestion.rationaleMarkdown}`
553
+ : note,
554
+ },
555
+ });
556
+ } catch (error) {
557
+ logger.error(
558
+ `KubernetesClusterService: could not settle AI remediation suggestion ${suggestion.id?.toString()} of a deleted cluster: ${error}`,
559
+ );
560
+ }
561
+ }
562
+
563
+ private isAiAccessSettingWritten(data: JSONObject): boolean {
564
+ return isAnyKeyWritten(data, AI_ACCESS_SETTING_KEYS);
565
+ }
566
+
567
+ /*
568
+ * aiRemediationMode is a plain text column and aiKubectlCommandAllowlist
569
+ * untyped JSON, and both of their readers fail safe: an unknown mode reads
570
+ * as Disabled and an unusable pattern as no pattern. Accepting such a value
571
+ * would answer an API or Terraform write with success for a setting that
572
+ * never takes effect (and shows no drift), so refuse it here — and store
573
+ * the allowlist in the one shape its readers expect.
574
+ */
575
+ private validateAiRemediationSettings(data: JSONObject): void {
576
+ const mode: unknown = data["aiRemediationMode"];
577
+
578
+ if (
579
+ mode !== undefined &&
580
+ !Object.values(KubernetesAiRemediationMode).includes(
581
+ mode as KubernetesAiRemediationMode,
582
+ )
583
+ ) {
584
+ throw new BadDataException(
585
+ `AI remediation mode must be one of ${Object.values(
586
+ KubernetesAiRemediationMode,
587
+ ).join(", ")} (got ${JSON.stringify(mode)}).`,
588
+ );
589
+ }
590
+
591
+ if (data["aiKubectlCommandAllowlist"] !== undefined) {
592
+ data["aiKubectlCommandAllowlist"] = normalizeKubectlAllowlistForWrite(
593
+ data["aiKubectlCommandAllowlist"],
594
+ );
595
+ }
596
+ }
597
+
598
+ /*
599
+ * A cluster's AI mode does the job of a FullAuto AutoRemediationRule with
600
+ * no rule row, so making AI do MORE on a cluster takes the same
601
+ * permissions as authoring such a rule (KUBERNETES_AI_ACCESS_ADMIN_
602
+ * PERMISSIONS), and binding a credential additionally takes the right to
603
+ * read credentials — the rule the dashboard's picker applies, now enforced
604
+ * where it cannot be skipped. The column ACLs stay open to every cluster
605
+ * editor on purpose: a column ACL cannot say "tightening is free", and
606
+ * turning AI remediation off must never need more than editing the
607
+ * cluster.
608
+ *
609
+ * `current` holds the settings of every cluster the write reaches. When it
610
+ * matched none, the write is judged against the never-configured defaults
611
+ * in the caller's tenant, so the answer does not depend on whether the id
612
+ * exists; with no project to check against at all it fails closed.
613
+ */
614
+ private assertMayChangeAiAccess(data: {
615
+ data: JSONObject;
616
+ props: DatabaseCommonInteractionProps;
617
+ current: Array<AiAccessSettingsSnapshot>;
618
+ }): void {
619
+ const baselines: Array<AiAccessSettingsSnapshot> =
620
+ data.current.length > 0
621
+ ? data.current
622
+ : [{ ...NEVER_CONFIGURED_AI_ACCESS, projectId: data.props.tenantId }];
623
+
624
+ for (const baseline of baselines) {
625
+ const loosening: AiAccessLoosening = this.getAiAccessLoosening(
626
+ data.data,
627
+ baseline,
628
+ );
629
+
630
+ if (!loosening.loosens) {
631
+ continue;
632
+ }
633
+
634
+ if (
635
+ !baseline.projectId ||
636
+ !holdsAnyPermission({
637
+ props: data.props,
638
+ projectId: baseline.projectId,
639
+ allowed: KUBERNETES_AI_ACCESS_ADMIN_PERMISSIONS,
640
+ })
641
+ ) {
642
+ throw new NotAuthorizedException(getAiAccessAdminRefusal());
643
+ }
644
+
645
+ if (
646
+ loosening.bindsCredential &&
647
+ !holdsAnyPermission({
648
+ props: data.props,
649
+ projectId: baseline.projectId,
650
+ allowed: KUBERNETES_AI_ACCESS_CREDENTIAL_PERMISSIONS,
651
+ })
652
+ ) {
653
+ throw new NotAuthorizedException(getAiAccessCredentialRefusal());
654
+ }
655
+ }
656
+ }
657
+
658
+ /*
659
+ * Does this write let AI do more on a cluster whose settings are
660
+ * `current`? Only a real change counts: the AI page posts every field of
661
+ * its form, so an editor who only flips the investigation switch re-posts
662
+ * an unchanged mode, allowlist and binding, and must not be refused for
663
+ * settings someone else chose.
664
+ *
665
+ * - mode: moving UP to Automatic or Bypass approval (Bypass approval ->
666
+ * Automatic is a tightening);
667
+ * - allowlist: adding a pattern the cluster did not already have
668
+ * (removing patterns, or clearing the list, tightens);
669
+ * - Runner or credential: binding one other than the current one
670
+ * (clearing tightens).
671
+ *
672
+ * `data` has been through validateAiRemediationSettings, so the mode is a
673
+ * known value and the allowlist a trimmed array or null.
674
+ */
675
+ private getAiAccessLoosening(
676
+ data: JSONObject,
677
+ current: AiAccessSettingsSnapshot,
678
+ ): AiAccessLoosening {
679
+ let loosens: boolean = false;
680
+ let bindsCredential: boolean = false;
681
+
682
+ const mode: KubernetesAiRemediationMode | undefined = data[
683
+ "aiRemediationMode"
684
+ ] as KubernetesAiRemediationMode | undefined;
685
+
686
+ if (
687
+ mode !== undefined &&
688
+ isUnattendedRemediationMode(mode) &&
689
+ REMEDIATION_MODES_BY_AUTONOMY.indexOf(mode) >
690
+ REMEDIATION_MODES_BY_AUTONOMY.indexOf(current.aiRemediationMode)
691
+ ) {
692
+ loosens = true;
693
+ }
694
+
695
+ if (data["aiKubectlCommandAllowlist"] !== undefined) {
696
+ const patterns: Array<string> =
697
+ (data["aiKubectlCommandAllowlist"] as Array<string> | null) || [];
698
+
699
+ if (
700
+ patterns.some((pattern: string) => {
701
+ return !current.aiKubectlCommandAllowlist.includes(pattern);
702
+ })
703
+ ) {
704
+ loosens = true;
705
+ }
706
+ }
707
+
708
+ const runnerId: ObjectID | null = RelationIdUtil.readConsistent(
709
+ data,
710
+ AI_ACCESS_RUNNER_KEYS,
711
+ "AI access Runner",
712
+ );
713
+
714
+ if (runnerId && runnerId.toString() !== current.aiAccessRunnerId) {
715
+ loosens = true;
716
+ }
717
+
718
+ const credentialId: ObjectID | null = RelationIdUtil.readConsistent(
719
+ data,
720
+ AI_ACCESS_CREDENTIAL_KEYS,
721
+ "AI access credential",
722
+ );
723
+
724
+ if (
725
+ credentialId &&
726
+ credentialId.toString() !== current.aiAccessCredentialId
727
+ ) {
728
+ loosens = true;
729
+ bindsCredential = true;
730
+ }
731
+
732
+ return { loosens, bindsCredential };
733
+ }
734
+
735
+ /*
736
+ * The AI access settings of every cluster an operator's update reaches,
737
+ * read before the write. onBeforeUpdate runs before the framework scopes
738
+ * the query to the caller's project, so scope it here: a caller must
739
+ * never learn anything about, or be judged against, another project's
740
+ * cluster.
741
+ */
742
+ @CaptureSpan()
743
+ private async getAiAccessSettingsForUpdateQuery(
744
+ updateBy: UpdateBy<Model>,
745
+ ): Promise<Record<string, AiAccessSettingsSnapshot>> {
746
+ const clusters: Array<Model> = await this.findBy({
747
+ query: {
748
+ ...updateBy.query,
749
+ ...(updateBy.props.tenantId
750
+ ? { projectId: updateBy.props.tenantId }
751
+ : {}),
752
+ },
753
+ select: {
754
+ _id: true,
755
+ projectId: true,
756
+ clusterIdentifier: true,
757
+ isAiInvestigationEnabled: true,
758
+ aiRemediationMode: true,
759
+ aiKubectlCommandAllowlist: true,
760
+ aiAccessRunnerId: true,
761
+ aiAccessCredentialId: true,
762
+ },
763
+ limit: LIMIT_MAX,
764
+ skip: 0,
765
+ props: {
766
+ isRoot: true,
767
+ },
768
+ });
769
+
770
+ const settings: Record<string, AiAccessSettingsSnapshot> = {};
771
+
772
+ for (const cluster of clusters) {
773
+ const clusterId: string | undefined =
774
+ cluster.id?.toString() || cluster._id?.toString();
775
+
776
+ if (!clusterId) {
777
+ continue;
778
+ }
779
+
780
+ settings[clusterId] = {
781
+ projectId: cluster.projectId,
782
+ clusterIdentifier: cluster.clusterIdentifier,
783
+ isAiInvestigationEnabled: cluster.isAiInvestigationEnabled === true,
784
+ aiRemediationMode: readStoredRemediationMode(cluster.aiRemediationMode),
785
+ aiKubectlCommandAllowlist: readStoredKubectlAllowlist(
786
+ cluster.aiKubectlCommandAllowlist,
787
+ ),
788
+ aiAccessRunnerId: cluster.aiAccessRunnerId?.toString() || null,
789
+ aiAccessCredentialId: cluster.aiAccessCredentialId?.toString() || null,
790
+ };
791
+ }
792
+
793
+ return settings;
794
+ }
795
+
796
+ /*
797
+ * aiAccessRunnerId / aiAccessCredentialId are editable through ordinary
798
+ * CRUD by every member who may edit a cluster, and nothing in the
799
+ * framework checks that a ManyToOne target belongs to the row's own
800
+ * project (only the tenant relation itself is checked). Unchecked, a
801
+ * member of project A could point a cluster at project B's Runner or
802
+ * credential — and then read that Runner's name, liveness and status,
803
+ * or that credential's name and type, through the relation join, and use
804
+ * the FK as an existence oracle for foreign ids. Same class of guard as
805
+ * MonitorProbeService ("Probe not found or it does not belong to this
806
+ * project.") and IoTDeviceCredentialService.
807
+ *
808
+ * WHO may bind is decided before this runs (assertMayChangeAiAccess); this
809
+ * only decides WHAT may be bound. Clearing a binding (null) is always
810
+ * allowed.
811
+ *
812
+ * Returns the rows it loaded (with what validateAiAccessBindingPairs
813
+ * reads off them), so the pair check does not look them up again.
814
+ */
815
+ @CaptureSpan()
816
+ private async validateAiAccessBindingsBelongToProject(data: {
817
+ data: JSONObject;
818
+ projectId: ObjectID | undefined;
819
+ // Filled with what this call loads, when given.
820
+ loaded?: AiAccessBindingRows | undefined;
821
+ }): Promise<AiAccessBindingRows> {
822
+ const loaded: AiAccessBindingRows = data.loaded || {
823
+ runners: new Map<string, Runner>(),
824
+ credentials: new Map<string, RunbookCredential>(),
825
+ };
826
+
827
+ const runnerId: ObjectID | null = RelationIdUtil.readConsistent(
828
+ data.data,
829
+ AI_ACCESS_RUNNER_KEYS,
830
+ "AI access Runner",
831
+ );
832
+
833
+ const credentialId: ObjectID | null = RelationIdUtil.readConsistent(
834
+ data.data,
835
+ AI_ACCESS_CREDENTIAL_KEYS,
836
+ "AI access credential",
837
+ );
838
+
839
+ if (!runnerId && !credentialId) {
840
+ return loaded;
841
+ }
842
+
843
+ if (!data.projectId) {
844
+ throw new BadDataException(
845
+ "The project of this Kubernetes cluster could not be resolved, so its AI access Runner or credential cannot be checked.",
846
+ );
847
+ }
848
+
849
+ if (runnerId) {
850
+ const runner: Runner | null = await RunnerService.findOneBy({
851
+ query: {
852
+ _id: runnerId.toString(),
853
+ projectId: data.projectId,
854
+ },
855
+ select: AI_ACCESS_BINDING_RUNNER_SELECT,
856
+ props: { isRoot: true },
857
+ });
858
+
859
+ if (!runner) {
860
+ throw new BadDataException(
861
+ "Runner not found or it does not belong to this project.",
862
+ );
863
+ }
864
+
865
+ loaded.runners.set(runnerId.toString(), runner);
866
+ }
867
+
868
+ if (credentialId) {
869
+ const credential: RunbookCredential | null =
870
+ await RunbookCredentialService.findOneBy({
871
+ query: {
872
+ _id: credentialId.toString(),
873
+ projectId: data.projectId,
874
+ },
875
+ select: AI_ACCESS_BINDING_CREDENTIAL_SELECT,
876
+ props: { isRoot: true },
877
+ });
878
+
879
+ if (
880
+ !credential ||
881
+ credential.credentialType !== RunbookCredentialType.Kubernetes
882
+ ) {
883
+ throw new BadDataException(AI_ACCESS_CREDENTIAL_REFUSAL);
884
+ }
885
+
886
+ loaded.credentials.set(credentialId.toString(), credential);
887
+ }
888
+
889
+ return loaded;
890
+ }
891
+
892
+ /*
893
+ * Whether the Runner and credential a write leaves a cluster with can
894
+ * work together — checked for an operator's write (the dashboard no
895
+ * longer offers these pairs, but the API and Terraform can still send
896
+ * them), per cluster the write reaches, and only where the pair actually
897
+ * changes (the AI page re-posts unchanged values):
898
+ *
899
+ * - a kubernetes-agent Runner of ANOTHER cluster is refused: its
900
+ * ServiceAccount reaches only the cluster its pod runs in, and it is
901
+ * never given a credential, so it can never reach this one. This
902
+ * cluster's own agent Runner (by its name for this cluster, or a
903
+ * posture naming it) is of course fine.
904
+ * - a credential together with a kubernetes-agent Runner of another
905
+ * cluster is refused — such a Runner is never handed credential
906
+ * material (this cluster's own agent ignores a credential: in-cluster
907
+ * access wins).
908
+ * - a credential the chosen Runner is not assigned is refused: the claim
909
+ * path resolves a credential only for a Runner it is assigned to, so
910
+ * every command would fail.
911
+ */
912
+ @CaptureSpan()
913
+ private async validateAiAccessBindingPairs(data: {
914
+ data: JSONObject;
915
+ clusters: Array<AiAccessSettingsSnapshot>;
916
+ loaded: AiAccessBindingRows;
917
+ }): Promise<void> {
918
+ const isRunnerWritten: boolean = isAnyKeyWritten(
919
+ data.data,
920
+ AI_ACCESS_RUNNER_KEYS,
921
+ );
922
+ const isCredentialWritten: boolean = isAnyKeyWritten(
923
+ data.data,
924
+ AI_ACCESS_CREDENTIAL_KEYS,
925
+ );
926
+
927
+ if (!isRunnerWritten && !isCredentialWritten) {
928
+ return;
929
+ }
930
+
931
+ const writtenRunnerId: string | null =
932
+ RelationIdUtil.readConsistent(
933
+ data.data,
934
+ AI_ACCESS_RUNNER_KEYS,
935
+ "AI access Runner",
936
+ )?.toString() || null;
937
+ const writtenCredentialId: string | null =
938
+ RelationIdUtil.readConsistent(
939
+ data.data,
940
+ AI_ACCESS_CREDENTIAL_KEYS,
941
+ "AI access credential",
942
+ )?.toString() || null;
943
+
944
+ for (const cluster of data.clusters) {
945
+ const runnerId: string | null = isRunnerWritten
946
+ ? writtenRunnerId
947
+ : cluster.aiAccessRunnerId;
948
+ const credentialId: string | null = isCredentialWritten
949
+ ? writtenCredentialId
950
+ : cluster.aiAccessCredentialId;
951
+ const isRunnerChanged: boolean = runnerId !== cluster.aiAccessRunnerId;
952
+ const isCredentialChanged: boolean =
953
+ credentialId !== cluster.aiAccessCredentialId;
954
+
955
+ if (!runnerId || (!isRunnerChanged && !isCredentialChanged)) {
956
+ continue;
957
+ }
958
+
959
+ const runner: Runner | null = await this.loadBindingRunner({
960
+ runnerId,
961
+ projectId: cluster.projectId,
962
+ loaded: data.loaded,
963
+ });
964
+
965
+ // A bound Runner that is gone is the Runner delete race: nothing to pair.
966
+ if (!runner) {
967
+ continue;
968
+ }
969
+
970
+ const isAgentRunner: boolean =
971
+ RunnerServiceClass.isKubernetesAgentRunnerRow(runner);
972
+ const isThisClustersAgent: boolean =
973
+ RunnerServiceClass.isKubernetesAgentRunnerOfCluster(
974
+ runner,
975
+ cluster.clusterIdentifier,
976
+ );
977
+
978
+ if (isAgentRunner && !isThisClustersAgent && isRunnerChanged) {
979
+ throw new BadDataException(
980
+ `Runner "${runner.name}" is the in-cluster Runner the Kubernetes agent chart installed on another cluster. Its ServiceAccount reaches only the cluster its pod runs in, and it is never given a credential, so it cannot run kubectl for this cluster. Install the in-cluster Runner on this cluster with --set aiAccess.enabled=true, or bind a Runner you created under Project Settings → Runners together with a Kubernetes credential for this cluster.`,
981
+ );
982
+ }
983
+
984
+ if (!credentialId || isThisClustersAgent) {
985
+ continue;
986
+ }
987
+
988
+ if (isAgentRunner) {
989
+ throw new BadDataException(
990
+ `Runner "${runner.name}" is an in-cluster Runner the Kubernetes agent chart installed: it runs kubectl with its own ServiceAccount only and is never given a credential. Clear the credential, or bind a Runner you created under Project Settings → Runners that the credential is assigned to.`,
991
+ );
992
+ }
993
+
994
+ const credential: RunbookCredential | null =
995
+ await this.loadBindingCredential({
996
+ credentialId,
997
+ projectId: cluster.projectId,
998
+ loaded: data.loaded,
999
+ });
1000
+
1001
+ // A bound credential that is gone is reported by the readiness check.
1002
+ if (!credential) {
1003
+ continue;
1004
+ }
1005
+
1006
+ const isAssigned: boolean = (credential.runners || []).some(
1007
+ (assigned: Runner) => {
1008
+ return (
1009
+ (assigned.id?.toString() || assigned._id?.toString()) === runnerId
1010
+ );
1011
+ },
1012
+ );
1013
+
1014
+ if (!isAssigned) {
1015
+ throw new BadDataException(
1016
+ `The Kubernetes credential is not assigned to Runner "${runner.name}", so that Runner could never use it for this cluster (a credential is handed only to the Runners it is assigned to). Assign it to the Runner under Project Settings → Runner Credentials first, or choose a credential that is assigned to it.`,
1017
+ );
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ private async loadBindingRunner(data: {
1023
+ runnerId: string;
1024
+ projectId: ObjectID | undefined;
1025
+ loaded: AiAccessBindingRows;
1026
+ }): Promise<Runner | null> {
1027
+ const cached: Runner | undefined = data.loaded.runners.get(data.runnerId);
1028
+
1029
+ if (cached) {
1030
+ return cached;
1031
+ }
1032
+
1033
+ const runner: Runner | null = await RunnerService.findOneBy({
1034
+ query: {
1035
+ _id: data.runnerId,
1036
+ ...(data.projectId ? { projectId: data.projectId } : {}),
1037
+ },
1038
+ select: AI_ACCESS_BINDING_RUNNER_SELECT,
1039
+ props: { isRoot: true },
1040
+ });
1041
+
1042
+ if (runner) {
1043
+ data.loaded.runners.set(data.runnerId, runner);
1044
+ }
1045
+
1046
+ return runner;
1047
+ }
1048
+
1049
+ private async loadBindingCredential(data: {
1050
+ credentialId: string;
1051
+ projectId: ObjectID | undefined;
1052
+ loaded: AiAccessBindingRows;
1053
+ }): Promise<RunbookCredential | null> {
1054
+ const cached: RunbookCredential | undefined = data.loaded.credentials.get(
1055
+ data.credentialId,
1056
+ );
1057
+
1058
+ if (cached) {
1059
+ return cached;
1060
+ }
1061
+
1062
+ const credential: RunbookCredential | null =
1063
+ await RunbookCredentialService.findOneBy({
1064
+ query: {
1065
+ _id: data.credentialId,
1066
+ ...(data.projectId ? { projectId: data.projectId } : {}),
1067
+ },
1068
+ select: AI_ACCESS_BINDING_CREDENTIAL_SELECT,
1069
+ props: { isRoot: true },
1070
+ });
1071
+
1072
+ if (credential) {
1073
+ data.loaded.credentials.set(data.credentialId, credential);
1074
+ }
1075
+
1076
+ return credential;
1077
+ }
1078
+
1079
+ @CaptureSpan()
1080
+ private async getProjectIdsForUpdateQuery(
1081
+ updateBy: UpdateBy<Model>,
1082
+ ): Promise<Array<ObjectID>> {
1083
+ const clusters: Array<Model> = await this.findBy({
1084
+ query: updateBy.query,
1085
+ select: {
1086
+ projectId: true,
1087
+ },
1088
+ limit: LIMIT_MAX,
1089
+ skip: 0,
1090
+ props: {
1091
+ isRoot: true,
1092
+ },
1093
+ });
1094
+
1095
+ const projectIds: Record<string, ObjectID> = {};
1096
+
1097
+ for (const cluster of clusters) {
1098
+ if (cluster.projectId) {
1099
+ projectIds[cluster.projectId.toString()] = cluster.projectId;
1100
+ }
1101
+ }
1102
+
1103
+ return Object.values(projectIds);
1104
+ }
1105
+
35
1106
  @CaptureSpan()
36
1107
  protected override async onCreateSuccess(
37
1108
  onCreate: OnCreate<Model>,
38
1109
  createdItem: Model,
39
1110
  ): Promise<Model> {
1111
+ /*
1112
+ * Decided in onBeforeCreate, from the payload as the caller sent it: by
1113
+ * now the saved row carries the column defaults, which would read as an
1114
+ * AI access setting on every create.
1115
+ */
1116
+ if (
1117
+ createdItem.id &&
1118
+ (onCreate.carryForward as { writesAiAccessSettings?: boolean } | null)
1119
+ ?.writesAiAccessSettings === true
1120
+ ) {
1121
+ await this.markAiAccessConfigured([createdItem.id]);
1122
+
1123
+ if (this.bindsRunner(onCreate.createBy.data as unknown as JSONObject)) {
1124
+ await this.markAiAccessRunnerBound([createdItem.id]);
1125
+ }
1126
+ }
1127
+
40
1128
  if (createdItem.projectId && createdItem.id) {
41
1129
  Promise.resolve()
42
1130
  .then(async () => {
@@ -436,15 +1524,339 @@ export class Service extends DatabaseService<Model> {
436
1524
  onUpdate: OnUpdate<Model>,
437
1525
  updatedItemIds: Array<ObjectID>,
438
1526
  ): Promise<OnUpdate<Model>> {
1527
+ const aiAccessWrite: AiAccessWriteCarryForward | null =
1528
+ getAiAccessWriteCarryForward(onUpdate.carryForward);
1529
+
1530
+ /*
1531
+ * The feed items first, fire and forget: the operator's write has
1532
+ * committed, so who changed what AI may do is recorded even when the
1533
+ * marker writes below fail (and the save reports that failure).
1534
+ */
439
1535
  this.writeKubernetesClusterUpdatedFeed(onUpdate, updatedItemIds).catch(
440
1536
  (error: Error) => {
441
1537
  logger.error(error);
442
1538
  },
443
1539
  );
444
1540
 
1541
+ if (aiAccessWrite) {
1542
+ this.writeAiAccessSettingsChangedFeed({
1543
+ onUpdate,
1544
+ updatedItemIds,
1545
+ previousAiAccessSettings: aiAccessWrite.previousAiAccessSettings,
1546
+ }).catch((error: Error) => {
1547
+ logger.error(error);
1548
+ });
1549
+ }
1550
+
1551
+ /*
1552
+ * Awaited, unlike the feed: registration reads the markers to tell an
1553
+ * operator's "off" (or a cleared binding) from a cluster nobody ever
1554
+ * configured, so a save that could not record them must not look like
1555
+ * one that did.
1556
+ */
1557
+ if (aiAccessWrite && updatedItemIds.length > 0) {
1558
+ await this.markAiAccessConfigured(updatedItemIds);
1559
+
1560
+ if (this.bindsRunner(onUpdate.updateBy.data as unknown as JSONObject)) {
1561
+ await this.markAiAccessRunnerBound(updatedItemIds);
1562
+ }
1563
+ }
1564
+
445
1565
  return onUpdate;
446
1566
  }
447
1567
 
1568
+ // The write binds a Runner (not merely clears the binding).
1569
+ private bindsRunner(data: JSONObject | undefined): boolean {
1570
+ return Boolean(
1571
+ data &&
1572
+ RelationIdUtil.readConsistent(
1573
+ data,
1574
+ AI_ACCESS_RUNNER_KEYS,
1575
+ "AI access Runner",
1576
+ ),
1577
+ );
1578
+ }
1579
+
1580
+ /*
1581
+ * Set aiAccessRunnerBoundAt on each cluster that does not have it yet —
1582
+ * "a Runner was bound here", never cleared. Registration reads it to leave
1583
+ * a cluster whose binding was later cleared (or whose Runner was deleted)
1584
+ * unbound, while still binding one an operator only configured before
1585
+ * installing the chart. Written as root after the operator's own write
1586
+ * succeeded; the column's update ACL is empty.
1587
+ */
1588
+ @CaptureSpan()
1589
+ private async markAiAccessRunnerBound(
1590
+ clusterIds: Array<ObjectID>,
1591
+ ): Promise<void> {
1592
+ await this.updateBy({
1593
+ query: {
1594
+ _id: QueryHelper.any(clusterIds),
1595
+ aiAccessRunnerBoundAt: QueryHelper.isNull(),
1596
+ },
1597
+ data: {
1598
+ aiAccessRunnerBoundAt: OneUptimeDate.getCurrentDate(),
1599
+ },
1600
+ limit: LIMIT_MAX,
1601
+ skip: 0,
1602
+ props: {
1603
+ isRoot: true,
1604
+ },
1605
+ });
1606
+ }
1607
+
1608
+ /*
1609
+ * Set aiAccessConfiguredAt on each cluster that does not have it yet, and
1610
+ * never touch one that does: the marker records when AI access was FIRST
1611
+ * configured. A server-only column (its update ACL is empty), so it is
1612
+ * written here, as root, after the operator's own write succeeded, rather
1613
+ * than slipped into that write.
1614
+ */
1615
+ @CaptureSpan()
1616
+ private async markAiAccessConfigured(
1617
+ clusterIds: Array<ObjectID>,
1618
+ ): Promise<void> {
1619
+ await this.updateBy({
1620
+ query: {
1621
+ _id: QueryHelper.any(clusterIds),
1622
+ aiAccessConfiguredAt: QueryHelper.isNull(),
1623
+ },
1624
+ data: {
1625
+ aiAccessConfiguredAt: OneUptimeDate.getCurrentDate(),
1626
+ },
1627
+ limit: LIMIT_MAX,
1628
+ skip: 0,
1629
+ props: {
1630
+ isRoot: true,
1631
+ },
1632
+ });
1633
+ }
1634
+
1635
+ /*
1636
+ * Who changed what OneUptime AI may do on a cluster, and when. The generic
1637
+ * "was updated" item only covers MEANINGFUL_UPDATE_COLUMNS (shared by ten
1638
+ * resource types and deliberately left alone), and the registration path
1639
+ * records only its own binds, so without this an operator switching a
1640
+ * production cluster to Bypass approval - or back off - left no trace.
1641
+ *
1642
+ * One item per cluster that actually changed; a save that re-posts
1643
+ * unchanged values (the AI page posts its whole form) records nothing.
1644
+ * API-key and Terraform writes carry no user and are attributed to an API
1645
+ * key rather than dropped.
1646
+ */
1647
+ private async writeAiAccessSettingsChangedFeed(data: {
1648
+ onUpdate: OnUpdate<Model>;
1649
+ updatedItemIds: Array<ObjectID>;
1650
+ previousAiAccessSettings: Record<string, AiAccessSettingsSnapshot>;
1651
+ }): Promise<void> {
1652
+ const updateData: JSONObject = (data.onUpdate.updateBy.data ||
1653
+ {}) as unknown as JSONObject;
1654
+ const updatedByUserId: ObjectID | undefined =
1655
+ data.onUpdate.updateBy.props.userId || undefined;
1656
+
1657
+ for (const kubernetesClusterId of data.updatedItemIds) {
1658
+ const previous: AiAccessSettingsSnapshot | undefined =
1659
+ data.previousAiAccessSettings[kubernetesClusterId.toString()];
1660
+
1661
+ const changes: Array<string> = await this.describeAiAccessChanges(
1662
+ updateData,
1663
+ previous,
1664
+ );
1665
+
1666
+ if (changes.length === 0) {
1667
+ continue;
1668
+ }
1669
+
1670
+ const projectId: ObjectID | undefined =
1671
+ previous?.projectId ||
1672
+ (
1673
+ await this.findOneById({
1674
+ id: kubernetesClusterId,
1675
+ select: { projectId: true },
1676
+ props: { isRoot: true },
1677
+ })
1678
+ )?.projectId;
1679
+
1680
+ if (!projectId) {
1681
+ continue;
1682
+ }
1683
+
1684
+ const userMarkdown: string = updatedByUserId
1685
+ ? (await UserService.getUserMarkdownString({
1686
+ userId: updatedByUserId,
1687
+ projectId,
1688
+ })) || "A user"
1689
+ : "";
1690
+
1691
+ const actor: string = userMarkdown ? `**${userMarkdown}**` : "An API key";
1692
+
1693
+ /*
1694
+ * Yellow when the change lets AI do more (the same test the permission
1695
+ * check applies), grey when it tightens or only flips investigation.
1696
+ */
1697
+ const displayColor: Color = this.getAiAccessLoosening(
1698
+ updateData,
1699
+ previous || NEVER_CONFIGURED_AI_ACCESS,
1700
+ ).loosens
1701
+ ? Yellow500
1702
+ : Gray500;
1703
+
1704
+ const moreInformation: Array<string> = [
1705
+ `**Changed by**: ${userMarkdown || "An API key (no user)"}`,
1706
+ ];
1707
+
1708
+ const allowlist: unknown = updateData["aiKubectlCommandAllowlist"];
1709
+
1710
+ if (Array.isArray(allowlist) && allowlist.length > 0) {
1711
+ moreInformation.push(
1712
+ `**kubectl allowlist**:\n\n${allowlist
1713
+ .map((pattern: string) => {
1714
+ return `- \`${pattern.replace(/`/g, "'")}\``;
1715
+ })
1716
+ .join("\n")}`,
1717
+ );
1718
+ }
1719
+
1720
+ await KubernetesClusterFeedService.createKubernetesClusterFeedItem({
1721
+ kubernetesClusterId,
1722
+ projectId,
1723
+ kubernetesClusterFeedEventType:
1724
+ KubernetesClusterFeedEventType.KubernetesClusterUpdated,
1725
+ displayColor,
1726
+ feedInfoInMarkdown: `🤖 ${actor} changed what OneUptime AI may do on ${await this.getKubernetesClusterMarkdownLink(
1727
+ projectId,
1728
+ kubernetesClusterId,
1729
+ )}:\n\n${changes
1730
+ .map((change: string) => {
1731
+ return `- ${change}`;
1732
+ })
1733
+ .join("\n")}`,
1734
+ moreInformationInMarkdown: moreInformation.join("\n\n"),
1735
+ userId: updatedByUserId,
1736
+ });
1737
+ }
1738
+ }
1739
+
1740
+ /*
1741
+ * One line per AI access setting this write actually changed, old -> new.
1742
+ * With no `previous` (the cluster was not among those read before the
1743
+ * write) every written setting is reported as set, without a "from".
1744
+ *
1745
+ * A credential is reported without its name: the feed is readable by
1746
+ * everyone who may read the cluster, credentials only by those who may
1747
+ * read credentials.
1748
+ */
1749
+ private async describeAiAccessChanges(
1750
+ updateData: JSONObject,
1751
+ previous: AiAccessSettingsSnapshot | undefined,
1752
+ ): Promise<Array<string>> {
1753
+ const changes: Array<string> = [];
1754
+
1755
+ if (updateData["isAiInvestigationEnabled"] !== undefined) {
1756
+ const isEnabled: boolean =
1757
+ updateData["isAiInvestigationEnabled"] === true;
1758
+
1759
+ if (!previous || previous.isAiInvestigationEnabled !== isEnabled) {
1760
+ changes.push(
1761
+ `AI investigation with kubectl turned **${isEnabled ? "on" : "off"}**`,
1762
+ );
1763
+ }
1764
+ }
1765
+
1766
+ if (updateData["aiRemediationMode"] !== undefined) {
1767
+ const mode: KubernetesAiRemediationMode = readStoredRemediationMode(
1768
+ updateData["aiRemediationMode"],
1769
+ );
1770
+
1771
+ if (!previous) {
1772
+ changes.push(
1773
+ `AI remediation set to **${REMEDIATION_MODE_FEED_LABELS[mode]}**`,
1774
+ );
1775
+ } else if (previous.aiRemediationMode !== mode) {
1776
+ changes.push(
1777
+ `AI remediation changed from **${
1778
+ REMEDIATION_MODE_FEED_LABELS[previous.aiRemediationMode]
1779
+ }** to **${REMEDIATION_MODE_FEED_LABELS[mode]}**`,
1780
+ );
1781
+ }
1782
+ }
1783
+
1784
+ if (updateData["aiKubectlCommandAllowlist"] !== undefined) {
1785
+ const patterns: Array<string> = readStoredKubectlAllowlist(
1786
+ updateData["aiKubectlCommandAllowlist"],
1787
+ );
1788
+
1789
+ if (
1790
+ !previous ||
1791
+ !isSameKubectlAllowlist(previous.aiKubectlCommandAllowlist, patterns)
1792
+ ) {
1793
+ changes.push(
1794
+ patterns.length === 0
1795
+ ? "kubectl allowlist cleared"
1796
+ : `kubectl allowlist changed to ${describePatternCount(
1797
+ patterns.length,
1798
+ )}${
1799
+ previous
1800
+ ? ` (was ${describePatternCount(
1801
+ previous.aiKubectlCommandAllowlist.length,
1802
+ )})`
1803
+ : ""
1804
+ }`,
1805
+ );
1806
+ }
1807
+ }
1808
+
1809
+ if (isAnyKeyWritten(updateData, AI_ACCESS_RUNNER_KEYS)) {
1810
+ const runnerId: ObjectID | null = RelationIdUtil.readConsistent(
1811
+ updateData,
1812
+ AI_ACCESS_RUNNER_KEYS,
1813
+ "AI access Runner",
1814
+ );
1815
+
1816
+ if (
1817
+ !previous ||
1818
+ previous.aiAccessRunnerId !== (runnerId?.toString() || null)
1819
+ ) {
1820
+ if (runnerId) {
1821
+ const runner: Runner | null = await RunnerService.findOneById({
1822
+ id: runnerId,
1823
+ select: { name: true },
1824
+ props: { isRoot: true },
1825
+ });
1826
+
1827
+ changes.push(
1828
+ `Runner **${runner?.name || runnerId.toString()}** bound to run kubectl`,
1829
+ );
1830
+ } else {
1831
+ changes.push("Runner cleared, so AI runs no kubectl on this cluster");
1832
+ }
1833
+ }
1834
+ }
1835
+
1836
+ if (isAnyKeyWritten(updateData, AI_ACCESS_CREDENTIAL_KEYS)) {
1837
+ const credentialId: ObjectID | null = RelationIdUtil.readConsistent(
1838
+ updateData,
1839
+ AI_ACCESS_CREDENTIAL_KEYS,
1840
+ "AI access credential",
1841
+ );
1842
+
1843
+ if (
1844
+ !previous ||
1845
+ previous.aiAccessCredentialId !== (credentialId?.toString() || null)
1846
+ ) {
1847
+ changes.push(
1848
+ credentialId
1849
+ ? previous?.aiAccessCredentialId
1850
+ ? "Kubernetes credential changed"
1851
+ : "Kubernetes credential bound"
1852
+ : "Kubernetes credential cleared",
1853
+ );
1854
+ }
1855
+ }
1856
+
1857
+ return changes;
1858
+ }
1859
+
448
1860
  private async writeKubernetesClusterUpdatedFeed(
449
1861
  onUpdate: OnUpdate<Model>,
450
1862
  updatedItemIds: Array<ObjectID>,
@@ -547,4 +1959,161 @@ function fingerprintLabelIds(labelIds: Array<ObjectID>): string {
547
1959
  return crypto.createHash("sha1").update(sorted.join(",")).digest("hex");
548
1960
  }
549
1961
 
1962
+ // Undefined is an omitted property; null is an explicit clear, so a write.
1963
+ function isAnyKeyWritten(data: JSONObject, keys: Array<string>): boolean {
1964
+ return keys.some((key: string) => {
1965
+ return data[key] !== undefined;
1966
+ });
1967
+ }
1968
+
1969
+ function getAiAccessWriteCarryForward(
1970
+ carryForward: unknown,
1971
+ ): AiAccessWriteCarryForward | null {
1972
+ if (
1973
+ carryForward &&
1974
+ typeof carryForward === "object" &&
1975
+ "previousAiAccessSettings" in carryForward
1976
+ ) {
1977
+ return carryForward as AiAccessWriteCarryForward;
1978
+ }
1979
+
1980
+ return null;
1981
+ }
1982
+
1983
+ // A stored mode as the readers see it: anything unknown is Disabled.
1984
+ function readStoredRemediationMode(
1985
+ value: unknown,
1986
+ ): KubernetesAiRemediationMode {
1987
+ return Object.values(KubernetesAiRemediationMode).includes(
1988
+ value as KubernetesAiRemediationMode,
1989
+ )
1990
+ ? (value as KubernetesAiRemediationMode)
1991
+ : KubernetesAiRemediationMode.Disabled;
1992
+ }
1993
+
1994
+ /*
1995
+ * A stored allowlist as the readers see it (KubernetesClusterAiAccessService
1996
+ * .normalizeAllowlist, which this module cannot import without a cycle):
1997
+ * trimmed non-empty strings, a JSON-encoded array accepted, anything else
1998
+ * empty. Rows written before the write-side validation existed may hold any
1999
+ * of those shapes.
2000
+ */
2001
+ function readStoredKubectlAllowlist(value: unknown): Array<string> {
2002
+ let raw: unknown = value;
2003
+
2004
+ if (typeof raw === "string") {
2005
+ try {
2006
+ raw = JSON.parse(raw);
2007
+ } catch {
2008
+ raw = [value];
2009
+ }
2010
+ }
2011
+
2012
+ if (!Array.isArray(raw)) {
2013
+ return [];
2014
+ }
2015
+
2016
+ return raw
2017
+ .filter((pattern: unknown) => {
2018
+ return typeof pattern === "string" && pattern.trim().length > 0;
2019
+ })
2020
+ .map((pattern: string) => {
2021
+ return pattern.trim();
2022
+ });
2023
+ }
2024
+
2025
+ /*
2026
+ * What an operator's allowlist write stores: null (no allowlist) or an array
2027
+ * of trimmed patterns the matcher can actually use. Refused, with the entry
2028
+ * and the rule it broke:
2029
+ *
2030
+ * - anything but an array (a JSON-encoded array in a string is accepted, the
2031
+ * form the readers already understand, and stored as the array);
2032
+ * - an entry that is not text;
2033
+ * - more than KUBECTL_ALLOWLIST_MAX_PATTERNS entries — the matcher skips
2034
+ * every pattern past that silently;
2035
+ * - an entry KubectlPolicy.describeAllowlistPatternProblem says the matcher
2036
+ * cannot use (blank, too long, not one kubectl command line, no verb).
2037
+ * That is the ONE definition of a valid entry, shared with the AI page:
2038
+ * KubectlPolicy.matchesAllowlist compares word by word, `*` stands for
2039
+ * exactly one word, and the leading "kubectl" is optional.
2040
+ */
2041
+ export function normalizeKubectlAllowlistForWrite(
2042
+ value: unknown,
2043
+ ): Array<string> | null {
2044
+ let raw: unknown = value;
2045
+
2046
+ if (raw === null) {
2047
+ return null;
2048
+ }
2049
+
2050
+ if (typeof raw === "string") {
2051
+ if (raw.trim().length === 0) {
2052
+ return null;
2053
+ }
2054
+
2055
+ try {
2056
+ raw = JSON.parse(raw);
2057
+ } catch {
2058
+ throw new BadDataException(KUBECTL_ALLOWLIST_SHAPE_HINT);
2059
+ }
2060
+ }
2061
+
2062
+ if (!Array.isArray(raw)) {
2063
+ throw new BadDataException(KUBECTL_ALLOWLIST_SHAPE_HINT);
2064
+ }
2065
+
2066
+ if (raw.length > KUBECTL_ALLOWLIST_MAX_PATTERNS) {
2067
+ throw new BadDataException(
2068
+ `The kubectl allowlist can hold at most ${KUBECTL_ALLOWLIST_MAX_PATTERNS} patterns (got ${raw.length}).`,
2069
+ );
2070
+ }
2071
+
2072
+ return raw.map((entry: unknown, index: number): string => {
2073
+ const position: string = `Pattern ${index + 1} of the kubectl allowlist`;
2074
+
2075
+ if (typeof entry !== "string") {
2076
+ throw new BadDataException(
2077
+ `${position} must be text (got ${entry === null ? "null" : typeof entry}). ${KUBECTL_ALLOWLIST_SHAPE_HINT}`,
2078
+ );
2079
+ }
2080
+
2081
+ // Stored trimmed, so it is judged exactly as it will be read.
2082
+ const pattern: string = entry.trim();
2083
+
2084
+ const problem: string | null =
2085
+ KubectlPolicy.describeAllowlistPatternProblem(pattern);
2086
+
2087
+ if (problem) {
2088
+ throw new BadDataException(
2089
+ `${position} cannot be used: ${
2090
+ problem.endsWith(".") ? problem : `${problem}.`
2091
+ } Patterns are compared with the command word by word; * stands for exactly one word, and the leading "kubectl" is optional.`,
2092
+ );
2093
+ }
2094
+
2095
+ return pattern;
2096
+ });
2097
+ }
2098
+
2099
+ function isSameKubectlAllowlist(
2100
+ left: Array<string>,
2101
+ right: Array<string>,
2102
+ ): boolean {
2103
+ return (
2104
+ left.length === right.length &&
2105
+ left.every((pattern: string, index: number) => {
2106
+ return pattern === right[index];
2107
+ })
2108
+ );
2109
+ }
2110
+
2111
+ function describePatternCount(count: number): string {
2112
+ if (count === 0) {
2113
+ return "none";
2114
+ }
2115
+
2116
+ return `${count} pattern${count === 1 ? "" : "s"}`;
2117
+ }
2118
+
550
2119
  export default new Service();