@oneuptime/common 12.0.8 → 12.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (769) hide show
  1. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
  2. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
  3. package/Models/DatabaseModels/AIConversation.ts +32 -0
  4. package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
  5. package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
  6. package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
  7. package/Models/DatabaseModels/Index.ts +12 -4
  8. package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +139 -9
  9. package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
  10. package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +8 -8
  11. package/Models/DatabaseModels/NetworkDevice.ts +141 -0
  12. package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
  13. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
  14. package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
  16. package/Models/DatabaseModels/Project.ts +78 -0
  17. package/Models/DatabaseModels/UserCall.ts +42 -0
  18. package/Models/DatabaseModels/UserEmail.ts +44 -0
  19. package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
  20. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
  21. package/Models/DatabaseModels/UserPush.ts +49 -0
  22. package/Models/DatabaseModels/UserSMS.ts +43 -0
  23. package/Models/DatabaseModels/UserTelegram.ts +59 -0
  24. package/Models/DatabaseModels/UserWebhook.ts +52 -0
  25. package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
  26. package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
  27. package/Server/API/AIChatAPI.ts +315 -1
  28. package/Server/API/DashboardAPI.ts +217 -1
  29. package/Server/API/OnCallReadinessAPI.ts +841 -0
  30. package/Server/API/TeamComplianceAPI.ts +69 -17
  31. package/Server/API/TelemetryAPI.ts +220 -12
  32. package/Server/EnvironmentConfig.ts +52 -0
  33. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
  47. package/Server/Infrastructure/Queue.ts +78 -13
  48. package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
  49. package/Server/Services/AIService.ts +7 -0
  50. package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
  51. package/Server/Services/AlertSeverityService.ts +63 -0
  52. package/Server/Services/DashboardService.ts +9 -10
  53. package/Server/Services/DatabaseService.ts +32 -2
  54. package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
  55. package/Server/Services/IncidentSeverityService.ts +76 -0
  56. package/Server/Services/Index.ts +12 -4
  57. package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
  58. package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +4 -4
  59. package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +89 -20
  60. package/Server/Services/LogAggregationService.ts +45 -8
  61. package/Server/Services/MetricAggregationService.ts +121 -0
  62. package/Server/Services/MetricService.ts +7 -7
  63. package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
  64. package/Server/Services/NetworkDeviceLinkService.ts +84 -0
  65. package/Server/Services/NetworkDeviceService.ts +140 -0
  66. package/Server/Services/NetworkSiteService.ts +77 -25
  67. package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
  69. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
  70. package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
  71. package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
  72. package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
  73. package/Server/Services/OnCallReadinessService.ts +2803 -0
  74. package/Server/Services/OnCallSetupReminderService.ts +955 -0
  75. package/Server/Services/ProfileAggregationService.ts +123 -0
  76. package/Server/Services/StatusPageService.ts +9 -10
  77. package/Server/Services/TeamComplianceService.ts +429 -252
  78. package/Server/Services/UserCallService.ts +26 -1
  79. package/Server/Services/UserEmailService.ts +26 -1
  80. package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
  81. package/Server/Services/UserNotificationRuleService.ts +3812 -333
  82. package/Server/Services/UserOnCallLogService.ts +561 -48
  83. package/Server/Services/UserPushService.ts +29 -0
  84. package/Server/Services/UserService.ts +11 -0
  85. package/Server/Services/UserSmsService.ts +26 -1
  86. package/Server/Services/UserTelegramService.ts +24 -1
  87. package/Server/Services/UserWebhookService.ts +28 -1
  88. package/Server/Services/UserWhatsAppService.ts +24 -1
  89. package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
  90. package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
  91. package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
  92. package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
  93. package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
  94. package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
  95. package/Server/Types/Workflow/Components/API/Get.ts +1 -1
  96. package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
  97. package/Server/Types/Workflow/Components/API/Post.ts +1 -1
  98. package/Server/Types/Workflow/Components/API/Put.ts +1 -1
  99. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
  100. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
  101. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
  102. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
  103. package/Server/Types/Workflow/Components/Email.ts +25 -7
  104. package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
  105. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
  106. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
  107. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
  108. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
  109. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
  110. package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
  111. package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
  112. package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
  113. package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
  114. package/Server/Utils/AI/Toolbox/Index.ts +48 -0
  115. package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
  116. package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
  117. package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
  118. package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
  119. package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
  120. package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
  121. package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
  122. package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
  123. package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
  124. package/Server/Utils/ClientIp.ts +221 -0
  125. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
  126. package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
  127. package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
  128. package/Server/Utils/Express.ts +12 -17
  129. package/Server/Utils/LLM/LLMService.ts +85 -8
  130. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
  131. package/Server/Utils/SSRFProtection.ts +98 -23
  132. package/Server/Utils/StartServer.ts +12 -3
  133. package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
  134. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +25 -25
  135. package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
  136. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
  137. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
  138. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
  139. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
  140. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
  141. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
  142. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
  143. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
  144. package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
  145. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
  146. package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
  147. package/Tests/Models/InventoryItemModel.test.ts +174 -0
  148. package/Tests/Models/InventoryItemNaming.test.ts +302 -0
  149. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
  150. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
  151. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
  152. package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
  153. package/Tests/Server/API/Helpers.ts +24 -15
  154. package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
  155. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
  156. package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
  157. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
  158. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
  159. package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
  160. package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
  161. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
  162. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
  163. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
  164. package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
  165. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
  166. package/Tests/Server/Services/{TelemetryEntityManualCreate.test.ts → InventoryItemManualCreate.test.ts} +27 -27
  167. package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
  168. package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
  169. package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
  170. package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
  171. package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
  172. package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
  173. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
  174. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
  175. package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
  176. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
  177. package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
  178. package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
  179. package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
  180. package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
  181. package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
  182. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
  183. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
  184. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
  185. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
  186. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
  187. package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
  188. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
  189. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
  190. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
  191. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
  192. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
  193. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
  194. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
  195. package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
  196. package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
  197. package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
  198. package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
  199. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
  200. package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
  201. package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
  202. package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
  203. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
  204. package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
  205. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
  206. package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
  207. package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
  208. package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
  209. package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
  210. package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
  211. package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
  212. package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
  213. package/Tests/Server/Utils/ClientIp.test.ts +438 -0
  214. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
  215. package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
  216. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
  217. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
  218. package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
  219. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
  220. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
  221. package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
  222. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
  223. package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
  224. package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
  225. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +15 -15
  226. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
  227. package/Tests/Types/IP/IP.test.ts +263 -0
  228. package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
  229. package/Tests/Types/IP/IPv6.test.ts +12 -1
  230. package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
  231. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
  232. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
  233. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
  234. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
  235. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
  236. package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
  237. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +6 -6
  238. package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
  239. package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
  240. package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
  241. package/Tests/Types/Workflow/TemplateSyntax.test.ts +112 -0
  242. package/Tests/Types/Workflow/Templates.test.ts +916 -101
  243. package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
  244. package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
  245. package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
  246. package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
  247. package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
  248. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
  249. package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
  250. package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
  251. package/Tests/UI/Components/Workflow/GraphLint.test.ts +72 -0
  252. package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
  253. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +242 -7
  254. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +15 -7
  255. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +242 -28
  256. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +59 -1
  257. package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
  258. package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
  259. package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
  260. package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
  261. package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
  262. package/Tests/UI/EsbuildConfig.test.ts +607 -0
  263. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  264. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
  265. package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
  266. package/Tests/UI/Utils/Platform.test.ts +147 -0
  267. package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
  268. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
  269. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +487 -0
  270. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
  271. package/Tests/__mocks__/bullmq.js +55 -0
  272. package/Types/AI/AIChatMessageStatus.ts +8 -1
  273. package/Types/AI/AIChatTypes.ts +12 -0
  274. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
  275. package/Types/Exception/ExceptionCode.ts +2 -0
  276. package/Types/Exception/ServiceUnavailableException.ts +8 -0
  277. package/Types/Exception/TooManyRequestsException.ts +8 -0
  278. package/Types/IP/IP.ts +93 -47
  279. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +49 -3
  280. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
  281. package/Types/OnCallDutyPolicy/Layer.ts +203 -149
  282. package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
  283. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
  284. package/Types/Permission.ts +193 -0
  285. package/Types/Telemetry/EntityRelationshipType.ts +1 -1
  286. package/Types/Telemetry/EntitySource.ts +1 -1
  287. package/Types/Telemetry/EntityType.ts +1 -1
  288. package/Types/Telemetry/EntityTypeGroups.ts +1 -1
  289. package/Types/Workflow/Components/BaseModel.ts +75 -29
  290. package/Types/Workflow/Components/Discord.ts +1 -0
  291. package/Types/Workflow/Components/Email.ts +12 -3
  292. package/Types/Workflow/Components/JavaScript.ts +7 -0
  293. package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
  294. package/Types/Workflow/Components/Slack.ts +1 -0
  295. package/Types/Workflow/Components/Telegram.ts +1 -0
  296. package/Types/Workflow/TemplateSyntax.ts +44 -0
  297. package/Types/Workflow/Templates.ts +2097 -45
  298. package/UI/Components/Calendar/Calendar.css +43 -0
  299. package/UI/Components/Calendar/Calendar.tsx +8 -0
  300. package/UI/Components/Card/Card.tsx +2 -2
  301. package/UI/Components/Checkbox/Checkbox.tsx +16 -0
  302. package/UI/Components/Dictionary/Dictionary.tsx +48 -9
  303. package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
  304. package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
  305. package/UI/Components/Input/Input.tsx +1 -0
  306. package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
  307. package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
  310. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
  311. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
  312. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
  313. package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
  314. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
  315. package/UI/Components/LogsViewer/types.ts +23 -0
  316. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
  317. package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
  318. package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
  319. package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
  320. package/UI/Components/Table/Table.tsx +49 -16
  321. package/UI/Components/Table/TableBody.tsx +53 -28
  322. package/UI/Components/Table/TableHeader.tsx +13 -0
  323. package/UI/Components/Table/TableRow.tsx +58 -26
  324. package/UI/Components/Workflow/ArgumentsForm.tsx +155 -9
  325. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
  326. package/UI/Components/Workflow/ComponentSettingsModal.tsx +10 -1
  327. package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
  328. package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
  329. package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
  330. package/UI/Components/Workflow/GraphLint.ts +42 -5
  331. package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
  332. package/UI/Components/Workflow/ModelColumnEditor.tsx +154 -22
  333. package/UI/Components/Workflow/ModelSchema.ts +115 -33
  334. package/UI/Components/Workflow/RunStatusWatcher.ts +1 -1
  335. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  336. package/UI/Components/Workflow/UseRunWatch.ts +212 -0
  337. package/UI/Components/Workflow/VariableModal.tsx +6 -2
  338. package/UI/Components/Workflow/Workflow.tsx +49 -3
  339. package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
  340. package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
  341. package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
  342. package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
  343. package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
  344. package/UI/Utils/Platform.ts +149 -0
  345. package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
  346. package/UI/esbuild-config.js +22 -1
  347. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
  348. package/Utils/Monitor/NetworkTopologyUtil.ts +888 -164
  349. package/Utils/Telemetry/CrossSignalScope.ts +502 -0
  350. package/Utils/Telemetry/EntityKey.ts +5 -5
  351. package/Utils/Telemetry/EntityRelationship.ts +1 -1
  352. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
  353. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
  354. package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
  355. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  356. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
  357. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
  358. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
  359. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  360. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
  361. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  362. package/build/dist/Models/DatabaseModels/Index.js +12 -4
  363. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  364. package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +167 -31
  365. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
  366. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
  367. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
  368. package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +27 -27
  369. package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
  370. package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
  371. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  372. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
  373. package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
  374. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
  375. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
  376. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
  377. package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
  378. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
  379. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
  380. package/build/dist/Models/DatabaseModels/Project.js +80 -0
  381. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  382. package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
  383. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  384. package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
  385. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  386. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
  387. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  388. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
  389. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  390. package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
  391. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
  393. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
  395. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
  397. package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
  398. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
  399. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  400. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
  401. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  402. package/build/dist/Server/API/AIChatAPI.js +237 -1
  403. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  404. package/build/dist/Server/API/DashboardAPI.js +165 -13
  405. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  406. package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
  407. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
  408. package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
  409. package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
  410. package/build/dist/Server/API/TelemetryAPI.js +129 -18
  411. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  412. package/build/dist/Server/EnvironmentConfig.js +45 -0
  413. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  414. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
  415. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  416. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
  417. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
  418. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
  419. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
  420. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
  421. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
  422. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
  423. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
  424. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
  425. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
  426. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
  427. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
  428. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
  429. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
  430. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
  431. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
  432. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
  433. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
  434. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
  435. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
  436. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
  437. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
  438. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  439. package/build/dist/Server/Infrastructure/Queue.js +72 -13
  440. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  441. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
  442. package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
  443. package/build/dist/Server/Services/AIService.js +1 -0
  444. package/build/dist/Server/Services/AIService.js.map +1 -1
  445. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
  446. package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
  447. package/build/dist/Server/Services/AlertSeverityService.js +54 -0
  448. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  449. package/build/dist/Server/Services/DashboardService.js +10 -9
  450. package/build/dist/Server/Services/DashboardService.js.map +1 -1
  451. package/build/dist/Server/Services/DatabaseService.js +24 -2
  452. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  453. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
  454. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  455. package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
  456. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  457. package/build/dist/Server/Services/Index.js +12 -4
  458. package/build/dist/Server/Services/Index.js.map +1 -1
  459. package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
  460. package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
  461. package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +6 -6
  462. package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
  463. package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +68 -23
  464. package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
  465. package/build/dist/Server/Services/LogAggregationService.js +27 -8
  466. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  467. package/build/dist/Server/Services/MetricAggregationService.js +80 -0
  468. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  469. package/build/dist/Server/Services/MetricService.js +6 -6
  470. package/build/dist/Server/Services/MetricService.js.map +1 -1
  471. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
  472. package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
  473. package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
  474. package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
  475. package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
  476. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  477. package/build/dist/Server/Services/NetworkSiteService.js +53 -13
  478. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  479. package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
  480. package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
  481. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
  482. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  483. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
  484. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  485. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
  486. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
  487. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
  488. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  489. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
  490. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
  491. package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
  492. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
  493. package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
  494. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
  495. package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
  496. package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
  497. package/build/dist/Server/Services/StatusPageService.js +11 -10
  498. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  499. package/build/dist/Server/Services/TeamComplianceService.js +312 -160
  500. package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
  501. package/build/dist/Server/Services/UserCallService.js +24 -1
  502. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  503. package/build/dist/Server/Services/UserEmailService.js +24 -1
  504. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  505. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
  506. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
  507. package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
  508. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  509. package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
  510. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  511. package/build/dist/Server/Services/UserPushService.js +26 -0
  512. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  513. package/build/dist/Server/Services/UserService.js +10 -0
  514. package/build/dist/Server/Services/UserService.js.map +1 -1
  515. package/build/dist/Server/Services/UserSmsService.js +24 -1
  516. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  517. package/build/dist/Server/Services/UserTelegramService.js +22 -1
  518. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  519. package/build/dist/Server/Services/UserWebhookService.js +25 -1
  520. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  521. package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
  522. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  523. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
  524. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  525. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
  526. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  527. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
  528. package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
  529. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
  530. package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
  531. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
  532. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  533. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
  534. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  535. package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
  536. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  537. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
  538. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  539. package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
  540. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  541. package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
  542. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  543. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
  544. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  545. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
  546. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  547. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
  548. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
  549. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
  550. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  551. package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
  552. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  553. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
  554. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  555. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
  556. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  557. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
  558. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  559. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
  560. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  561. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
  562. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  563. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
  564. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  565. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
  566. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  567. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
  568. package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
  569. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
  570. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  571. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
  572. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  573. package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
  574. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  575. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
  576. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
  577. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
  578. package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
  579. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
  580. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
  581. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
  582. package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
  583. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
  584. package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
  585. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
  586. package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
  587. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
  588. package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
  589. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
  590. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
  591. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
  592. package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
  593. package/build/dist/Server/Utils/ClientIp.js +137 -0
  594. package/build/dist/Server/Utils/ClientIp.js.map +1 -0
  595. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
  596. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  597. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
  598. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
  599. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
  600. package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
  601. package/build/dist/Server/Utils/Express.js +12 -12
  602. package/build/dist/Server/Utils/Express.js.map +1 -1
  603. package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
  604. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  605. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
  606. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  607. package/build/dist/Server/Utils/SSRFProtection.js +82 -21
  608. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  609. package/build/dist/Server/Utils/StartServer.js +12 -4
  610. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  611. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
  612. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  613. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +11 -11
  614. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  615. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
  616. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  617. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
  618. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  619. package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
  620. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
  621. package/build/dist/Types/AI/AIChatTypes.js +12 -0
  622. package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
  623. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
  624. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
  625. package/build/dist/Types/Exception/ExceptionCode.js +2 -0
  626. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  627. package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
  628. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
  629. package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
  630. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
  631. package/build/dist/Types/IP/IP.js +87 -43
  632. package/build/dist/Types/IP/IP.js.map +1 -1
  633. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
  634. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
  635. package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
  636. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  637. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
  638. package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
  639. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
  640. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  641. package/build/dist/Types/Permission.js +174 -0
  642. package/build/dist/Types/Permission.js.map +1 -1
  643. package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
  644. package/build/dist/Types/Telemetry/EntitySource.js +1 -1
  645. package/build/dist/Types/Telemetry/EntityType.js +1 -1
  646. package/build/dist/Types/Telemetry/EntityTypeGroups.js +1 -1
  647. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -1
  648. package/build/dist/Types/Workflow/Components/BaseModel.js +66 -28
  649. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  650. package/build/dist/Types/Workflow/Components/Discord.js +1 -0
  651. package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
  652. package/build/dist/Types/Workflow/Components/Email.js +12 -3
  653. package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
  654. package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
  655. package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
  656. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
  657. package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
  658. package/build/dist/Types/Workflow/Components/Slack.js +1 -0
  659. package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
  660. package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
  661. package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
  662. package/build/dist/Types/Workflow/TemplateSyntax.js +12 -0
  663. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -1
  664. package/build/dist/Types/Workflow/Templates.js +1931 -38
  665. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  666. package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
  667. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  668. package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
  669. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  670. package/build/dist/UI/Components/Dictionary/Dictionary.js +25 -11
  671. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  672. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  673. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
  674. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  675. package/build/dist/UI/Components/Input/Input.js +1 -0
  676. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  677. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
  678. package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
  679. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
  680. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
  681. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
  682. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  683. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
  684. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  685. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
  686. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  687. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
  688. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  689. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
  690. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  691. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
  692. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  693. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
  694. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  695. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  696. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
  697. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  698. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
  699. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  700. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
  701. package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
  702. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
  703. package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
  704. package/build/dist/UI/Components/Table/Table.js +27 -15
  705. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  706. package/build/dist/UI/Components/Table/TableBody.js +24 -18
  707. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  708. package/build/dist/UI/Components/Table/TableHeader.js +9 -1
  709. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  710. package/build/dist/UI/Components/Table/TableRow.js +29 -21
  711. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  712. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +130 -12
  713. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  714. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
  715. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  716. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +4 -4
  717. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  718. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
  719. package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
  720. package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
  721. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  722. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
  723. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  724. package/build/dist/UI/Components/Workflow/GraphLint.js +33 -4
  725. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -1
  726. package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
  727. package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
  728. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +116 -21
  729. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
  730. package/build/dist/UI/Components/Workflow/ModelSchema.js +72 -34
  731. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -1
  732. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +1 -1
  733. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -1
  734. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  735. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  736. package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
  737. package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
  738. package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
  739. package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
  740. package/build/dist/UI/Components/Workflow/Workflow.js +41 -1
  741. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  742. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
  743. package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
  744. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
  745. package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
  746. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
  747. package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
  748. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
  749. package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
  750. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
  751. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  752. package/build/dist/UI/Utils/Platform.js +118 -0
  753. package/build/dist/UI/Utils/Platform.js.map +1 -0
  754. package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
  755. package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
  756. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
  757. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
  758. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +642 -136
  759. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  760. package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
  761. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
  762. package/build/dist/Utils/Telemetry/EntityKey.js +5 -5
  763. package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
  764. package/jest.config.json +1 -0
  765. package/package.json +1 -1
  766. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
  767. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
  768. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
  769. package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
@@ -29,6 +29,33 @@ export interface TopologyDeviceInput {
29
29
  */
30
30
  sysDescr?: string | undefined;
31
31
  sysObjectId?: string | undefined;
32
+ /*
33
+ * ENTITY-MIB chassis serial. Not rendered — it is here because LLDP
34
+ * chassis-id subtype 7 ("locally assigned") is very often exactly this
35
+ * string, so without it a peer we already manage is drawn as a stranger.
36
+ */
37
+ serialNumber?: string | undefined;
38
+ /*
39
+ * Health as reported by a Monitor watching this device, already reduced to
40
+ * the map's three states by the caller. Load-bearing for monitor-backed
41
+ * devices (monitoringMethod "Monitor"): nothing ever polls them, so their
42
+ * lastSeenAt is permanently absent and freshness alone would draw every
43
+ * one of them as "unknown" forever.
44
+ */
45
+ monitorStatus?: NetworkTopologyNodeStatus | undefined;
46
+ /*
47
+ * Whether the device's interface walk is on. LLDP and CDP ride along with
48
+ * it, so a device with it off can never report a neighbour — which is one
49
+ * of the ways a device ends up drawn floating, and one the map has no
50
+ * other way to explain.
51
+ */
52
+ isNeighborDiscoveryEnabled?: boolean | undefined;
53
+ /*
54
+ * Every interface MAC this device reports. Not rendered either: LLDP
55
+ * chassis-id subtype 4 IS a MAC, and it is frequently the only thing a
56
+ * peer advertises about itself.
57
+ */
58
+ macAddresses?: Array<string> | undefined;
32
59
  lldpNeighbors?: Array<LldpNeighbor> | undefined;
33
60
  cdpNeighbors?: Array<CdpNeighbor> | undefined;
34
61
  }
@@ -68,6 +95,22 @@ export interface TopologyEndpointInput {
68
95
  lastSeenAt?: Date | undefined;
69
96
  }
70
97
 
98
+ /*
99
+ * One operator-declared link (NetworkDeviceLink), reduced to what the graph
100
+ * needs. These exist for cables neither discovery protocol can see: a device
101
+ * with LLDP/CDP disabled, one that speaks neither, or a monitor-backed device
102
+ * that is never walked at all.
103
+ */
104
+ export interface TopologyManualLinkInput {
105
+ fromDeviceId: string;
106
+ toDeviceId: string;
107
+ name?: string | undefined;
108
+ fromPortName?: string | undefined;
109
+ toPortName?: string | undefined;
110
+ // Health from a bound Monitor, already reduced by the caller.
111
+ monitorStatus?: "up" | "down" | undefined;
112
+ }
113
+
71
114
  /*
72
115
  * buildTopology's result: the wire-format NetworkTopology plus endpoint
73
116
  * bookkeeping. The extra fields are optional on the wire type, so older
@@ -78,6 +121,13 @@ export interface TopologyBuildResult extends NetworkTopology {
78
121
  droppedEndpointCount?: number | undefined;
79
122
  // True when more attachable endpoints existed than the render cap allows.
80
123
  endpointsTruncated?: boolean | undefined;
124
+ /*
125
+ * Nodes removed because the project hid them. Always reported, even when
126
+ * zero: a map that quietly drops things is the same failure as a map that
127
+ * quietly invents them, and this is what the "N hidden — show" affordance
128
+ * counts.
129
+ */
130
+ suppressedNodeCount?: number | undefined;
81
131
  }
82
132
 
83
133
  /*
@@ -86,14 +136,86 @@ export interface TopologyBuildResult extends NetworkTopology {
86
136
  */
87
137
  interface NeighborClaim {
88
138
  protocol: NetworkTopologyLinkProtocol;
89
- matchKey: string | undefined;
139
+ /*
140
+ * Every identifier this report offers for the peer, strongest source
141
+ * first — LLDP's remoteSysName ahead of its remoteChassisId, CDP's
142
+ * remoteDeviceId on its own. Plural because a chassis id is frequently
143
+ * the ONLY thing a peer advertises, and it names a device we already
144
+ * manage exactly as well as a sysName does.
145
+ */
146
+ identifiers: Array<string>;
90
147
  displayName: string | undefined;
148
+ // True when displayName came from an advertised name, not a chassis id.
149
+ hasAdvertisedName: boolean;
91
150
  localInterfaceIndex: number | undefined;
92
151
  remotePortId: string | undefined;
93
152
  // CDP-only: the platform string the neighbor advertises about itself.
94
153
  remotePlatform: string | undefined;
95
154
  }
96
155
 
156
+ /*
157
+ * How a device can be recognized, strongest evidence first.
158
+ *
159
+ * "name" — something the device answers to (sysName, hostname, or the
160
+ * row's own name); what both discovery protocols advertise.
161
+ * "serial" — the ENTITY-MIB chassis serial, which is what LLDP
162
+ * chassis-id subtype 7 ("locally assigned") usually carries.
163
+ * "mac" — an interface MAC; LLDP chassis-id subtype 4 IS a MAC.
164
+ * "shortHost" — the first label of an FQDN, so `sw01.corp.local` and
165
+ * `sw01` are read as one device. Deliberately weakest: it
166
+ * throws information away, so it may only decide a match no
167
+ * stronger key could.
168
+ */
169
+ type MatchKeyKind = "name" | "serial" | "mac" | "shortHost";
170
+
171
+ const MATCH_KEY_KINDS_STRONGEST_FIRST: ReadonlyArray<MatchKeyKind> = [
172
+ "name",
173
+ "serial",
174
+ "mac",
175
+ "shortHost",
176
+ ];
177
+
178
+ /*
179
+ * The kinds strong enough to say "these two reports describe one peer".
180
+ *
181
+ * shortHost is excluded on purpose: `sw01.site-a` and `sw01.site-b` share
182
+ * one, and merging those would invent a device nobody owns. Against a
183
+ * MANAGED device the same key is safe, because a key two devices both
184
+ * claim is dropped from the index entirely rather than guessed at.
185
+ */
186
+ const PEER_IDENTITY_KINDS: ReadonlySet<MatchKeyKind> = new Set<MatchKeyKind>([
187
+ "name",
188
+ "serial",
189
+ "mac",
190
+ ]);
191
+
192
+ interface MatchKey {
193
+ kind: MatchKeyKind;
194
+ value: string;
195
+ }
196
+
197
+ /*
198
+ * One neighbor report, with its peer already resolved. Claims are resolved
199
+ * in a pass of their own because the identity of an UNMANAGED peer is not
200
+ * knowable from a single report: two switches can describe the same
201
+ * stranger by different identifiers, and only the whole set says they meant
202
+ * one box.
203
+ */
204
+ interface ResolvedClaim {
205
+ device: TopologyDeviceInput;
206
+ claim: NeighborClaim;
207
+ // Set when the claim named a device in this graph.
208
+ matchedDeviceId: string | undefined;
209
+ // Populated otherwise: the keys this unmanaged peer is known by.
210
+ peerAliases: Array<MatchKey>;
211
+ }
212
+
213
+ /*
214
+ * Hoisted so the literal is not the object of a member expression, which
215
+ * `wrap-regex` and Prettier cannot agree on.
216
+ */
217
+ const IPV4_PATTERN: RegExp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
218
+
97
219
  // A device not seen within this window is treated as not currently up.
98
220
  const FRESH_WINDOW_MS: number = 15 * 60 * 1000;
99
221
 
@@ -103,9 +225,11 @@ const ENDPOINT_RENDER_CAP: number = 2000;
103
225
  export default class NetworkTopologyUtil {
104
226
  /*
105
227
  * Builds the topology graph from every device in a project. Nodes are the
106
- * devices; LLDP and CDP neighbors that match another device (by sysName,
107
- * hostname or name) become edges, and neighbors that match nothing become
108
- * lightweight "unmanaged" nodes so links don't dead-end. Edges are
228
+ * devices; LLDP and CDP neighbors that match another device (by name,
229
+ * serial, interface MAC or FQDN short form see MatchKeyKind) become
230
+ * edges, and neighbors that match nothing become lightweight "unmanaged"
231
+ * nodes so links don't dead-end. Reports that share an identity-grade
232
+ * alias are one unmanaged node, however many switches saw it. Edges are
109
233
  * undirected and deduplicated — a link reported from both ends, or by
110
234
  * both protocols, appears once with the union of what each report knew
111
235
  * (ports, protocols, per-end interface state). When `interfaces` rows are
@@ -120,16 +244,35 @@ export default class NetworkTopologyUtil {
120
244
  now: Date,
121
245
  interfaces: Array<TopologyInterfaceInput> = [],
122
246
  endpoints: Array<TopologyEndpointInput> = [],
247
+ manualLinks: Array<TopologyManualLinkInput> = [],
248
+ suppressedNodeKeys: ReadonlySet<string> = new Set<string>(),
123
249
  ): TopologyBuildResult {
124
250
  const nodes: Array<NetworkTopologyNode> = [];
125
251
 
126
- // Index managed devices by the identifiers neighbor entries reference.
252
+ /*
253
+ * Index managed devices by every identifier a neighbor entry could
254
+ * reference. A key TWO devices both claim is deleted rather than
255
+ * resolved: last-writer-wins would quietly draw the link to whichever
256
+ * device happened to be indexed second, and a wrong cable on a network
257
+ * map is worse than a missing one.
258
+ */
127
259
  const deviceByMatchKey: Map<string, TopologyDeviceInput> = new Map();
260
+ const ambiguousMatchKeyIds: Set<string> = new Set<string>();
128
261
  for (const device of devices) {
129
262
  for (const key of NetworkTopologyUtil.matchKeysForDevice(device)) {
130
- deviceByMatchKey.set(key, device);
263
+ const keyId: string = NetworkTopologyUtil.matchKeyId(key);
264
+ const existing: TopologyDeviceInput | undefined =
265
+ deviceByMatchKey.get(keyId);
266
+ if (existing && existing.id !== device.id) {
267
+ ambiguousMatchKeyIds.add(keyId);
268
+ continue;
269
+ }
270
+ deviceByMatchKey.set(keyId, device);
131
271
  }
132
272
  }
273
+ for (const keyId of ambiguousMatchKeyIds) {
274
+ deviceByMatchKey.delete(keyId);
275
+ }
133
276
 
134
277
  // Index interface rows by (device, index) and by (device, name).
135
278
  const interfaceByDeviceAndIndex: Map<string, TopologyInterfaceInput> =
@@ -149,7 +292,15 @@ export default class NetworkTopologyUtil {
149
292
  }
150
293
  }
151
294
 
295
+ // Kept aside so the diagnostics pass below can read it back per node.
296
+ const isNeighborDiscoveryEnabledById: Map<string, boolean | undefined> =
297
+ new Map<string, boolean | undefined>();
298
+
152
299
  for (const device of devices) {
300
+ isNeighborDiscoveryEnabledById.set(
301
+ device.id,
302
+ device.isNeighborDiscoveryEnabled,
303
+ );
153
304
  nodes.push({
154
305
  id: device.id,
155
306
  name: device.name,
@@ -172,171 +323,174 @@ export default class NetworkTopologyUtil {
172
323
  });
173
324
  }
174
325
 
175
- const unmanagedNodeById: Map<string, NetworkTopologyNode> = new Map();
176
326
  const edgeByKey: Map<string, NetworkTopologyEdge> = new Map();
177
327
 
328
+ /*
329
+ * Resolve every neighbor report before drawing anything.
330
+ *
331
+ * A managed match is decidable from one report, but the IDENTITY of an
332
+ * unmanaged peer is not: the switch at one end may know its neighbor's
333
+ * sysName while the switch at the other end knows only its chassis MAC,
334
+ * and only the whole set of reports says those two describe one box.
335
+ */
336
+ const resolvedClaims: Array<ResolvedClaim> = [];
178
337
  for (const device of devices) {
179
338
  for (const claim of NetworkTopologyUtil.neighborClaimsForDevice(device)) {
180
- if (!claim.matchKey) {
339
+ const candidates: Array<MatchKey> =
340
+ NetworkTopologyUtil.candidateKeysForClaim(claim);
341
+ if (candidates.length === 0) {
181
342
  continue;
182
343
  }
183
344
 
184
- const matched: TopologyDeviceInput | undefined = deviceByMatchKey.get(
185
- claim.matchKey,
186
- );
187
-
188
- let toNodeId: string;
345
+ // Candidates arrive strongest-first, so the first hit is the best one.
346
+ let matched: TopologyDeviceInput | undefined = undefined;
347
+ for (const candidate of candidates) {
348
+ matched = deviceByMatchKey.get(
349
+ NetworkTopologyUtil.matchKeyId(candidate),
350
+ );
351
+ if (matched) {
352
+ break;
353
+ }
354
+ }
189
355
 
190
356
  if (matched) {
191
357
  if (matched.id === device.id) {
192
358
  // Self-referential neighbor entry; skip.
193
359
  continue;
194
360
  }
195
- toNodeId = matched.id;
196
- } else {
197
- toNodeId = `unmanaged:${claim.matchKey}`;
198
- const existingUnmanaged: NetworkTopologyNode | undefined =
199
- unmanagedNodeById.get(toNodeId);
200
- if (!existingUnmanaged) {
201
- const unmanagedName: string = claim.displayName || "Unknown device";
202
- const unmanagedNode: NetworkTopologyNode = {
203
- id: toNodeId,
204
- name: unmanagedName,
205
- isManaged: false,
206
- kind: "unmanaged",
207
- role: classifyDeviceRole({
208
- platform: claim.remotePlatform,
209
- name: unmanagedName,
210
- }),
211
- status: "unknown",
212
- deviceModel: claim.remotePlatform,
213
- };
214
- unmanagedNodeById.set(toNodeId, unmanagedNode);
215
- nodes.push(unmanagedNode);
216
- } else if (!existingUnmanaged.deviceModel && claim.remotePlatform) {
217
- /*
218
- * A later CDP claim may know the platform the first one didn't
219
- * — which is usually the only evidence a peer's role rests on,
220
- * so the role is re-derived rather than left at whatever the
221
- * name alone produced.
222
- */
223
- existingUnmanaged.deviceModel = claim.remotePlatform;
224
- existingUnmanaged.role = classifyDeviceRole({
225
- platform: claim.remotePlatform,
226
- name: existingUnmanaged.name,
227
- });
228
- }
229
- }
230
-
231
- // The reporting device's own end, enriched from its interface row.
232
- let localEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
233
- let localPortLabel: string | undefined = undefined;
234
- if (claim.localInterfaceIndex !== undefined) {
235
- const localInterface: TopologyInterfaceInput | undefined =
236
- interfaceByDeviceAndIndex.get(
237
- `${device.id}::${claim.localInterfaceIndex}`,
238
- );
239
- localEndpoint = {
240
- interfaceIndex: claim.localInterfaceIndex,
241
- interfaceName: localInterface?.name,
242
- isOperationallyUp: localInterface?.isOperationallyUp,
243
- isAdministrativelyUp: localInterface?.isAdministrativelyUp,
244
- utilizationPercent: localInterface?.utilizationPercent,
245
- inRateMbps: localInterface?.inRateMbps,
246
- outRateMbps: localInterface?.outRateMbps,
247
- errorsPerSecond: localInterface?.errorsPerSecond,
248
- };
249
- localPortLabel =
250
- localInterface?.name || `if${claim.localInterfaceIndex}`;
251
- }
252
-
253
- /*
254
- * The far end: best-effort — the advertised remote port id often
255
- * matches the remote device's interface name (e.g. "GigabitEthernet
256
- * 0/1"). Only attach data when it does; a bare port id string stays
257
- * in toPort.
258
- */
259
- let remoteEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
260
- if (matched && claim.remotePortId) {
261
- const remotePortKey: string | undefined =
262
- NetworkTopologyUtil.normalizeKey(claim.remotePortId);
263
- const remoteInterface: TopologyInterfaceInput | undefined =
264
- remotePortKey
265
- ? interfaceByDeviceAndName.get(`${matched.id}::${remotePortKey}`)
266
- : undefined;
267
- if (remoteInterface) {
268
- remoteEndpoint = {
269
- interfaceIndex: remoteInterface.interfaceIndex,
270
- interfaceName: remoteInterface.name,
271
- isOperationallyUp: remoteInterface.isOperationallyUp,
272
- isAdministrativelyUp: remoteInterface.isAdministrativelyUp,
273
- utilizationPercent: remoteInterface.utilizationPercent,
274
- inRateMbps: remoteInterface.inRateMbps,
275
- outRateMbps: remoteInterface.outRateMbps,
276
- errorsPerSecond: remoteInterface.errorsPerSecond,
277
- };
278
- }
279
- }
280
-
281
- // Undirected dedupe: canonicalize the endpoint pair.
282
- const edgeKey: string = [device.id, toNodeId].sort().join("::");
283
- const existing: NetworkTopologyEdge | undefined =
284
- edgeByKey.get(edgeKey);
285
-
286
- if (!existing) {
287
- edgeByKey.set(edgeKey, {
288
- fromNodeId: device.id,
289
- toNodeId: toNodeId,
290
- fromPort: localPortLabel,
291
- toPort: claim.remotePortId,
292
- protocols: [claim.protocol],
293
- fromInterface: localEndpoint,
294
- toInterface: remoteEndpoint,
361
+ resolvedClaims.push({
362
+ device: device,
363
+ claim: claim,
364
+ matchedDeviceId: matched.id,
365
+ peerAliases: [],
295
366
  });
296
367
  continue;
297
368
  }
298
369
 
299
- /*
300
- * Same link reported again — from the other end, or by the other
301
- * protocol. Merge instead of dropping so both ends keep the best
302
- * data anyone reported about them.
303
- */
304
- if (
305
- existing.protocols &&
306
- !existing.protocols.includes(claim.protocol)
307
- ) {
308
- existing.protocols.push(claim.protocol);
309
- }
370
+ resolvedClaims.push({
371
+ device: device,
372
+ claim: claim,
373
+ matchedDeviceId: undefined,
374
+ peerAliases: candidates.filter((candidate: MatchKey) => {
375
+ return PEER_IDENTITY_KINDS.has(candidate.kind);
376
+ }),
377
+ });
378
+ }
379
+ }
310
380
 
311
- if (existing.fromNodeId === device.id) {
312
- existing.fromPort = existing.fromPort || localPortLabel;
313
- existing.toPort = existing.toPort || claim.remotePortId;
314
- existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
315
- existing.fromInterface,
316
- localEndpoint,
317
- );
318
- existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
319
- existing.toInterface,
320
- remoteEndpoint,
321
- );
322
- } else {
323
- existing.toPort = existing.toPort || localPortLabel;
324
- existing.fromPort = existing.fromPort || claim.remotePortId;
325
- existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
326
- existing.toInterface,
327
- localEndpoint,
328
- );
329
- existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
330
- existing.fromInterface,
331
- remoteEndpoint,
381
+ const peerNodeIdByClaim: Map<ResolvedClaim, string> =
382
+ NetworkTopologyUtil.buildUnmanagedPeerNodes(resolvedClaims, nodes);
383
+
384
+ for (const resolved of resolvedClaims) {
385
+ const device: TopologyDeviceInput = resolved.device;
386
+ const claim: NeighborClaim = resolved.claim;
387
+ const matchedId: string | undefined = resolved.matchedDeviceId;
388
+ const toNodeId: string | undefined =
389
+ matchedId || peerNodeIdByClaim.get(resolved);
390
+ if (!toNodeId) {
391
+ continue;
392
+ }
393
+
394
+ // The reporting device's own end, enriched from its interface row.
395
+ let localEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
396
+ let localPortLabel: string | undefined = undefined;
397
+ if (claim.localInterfaceIndex !== undefined) {
398
+ const localInterface: TopologyInterfaceInput | undefined =
399
+ interfaceByDeviceAndIndex.get(
400
+ `${device.id}::${claim.localInterfaceIndex}`,
332
401
  );
402
+ localEndpoint = {
403
+ interfaceIndex: claim.localInterfaceIndex,
404
+ interfaceName: localInterface?.name,
405
+ isOperationallyUp: localInterface?.isOperationallyUp,
406
+ isAdministrativelyUp: localInterface?.isAdministrativelyUp,
407
+ utilizationPercent: localInterface?.utilizationPercent,
408
+ inRateMbps: localInterface?.inRateMbps,
409
+ outRateMbps: localInterface?.outRateMbps,
410
+ errorsPerSecond: localInterface?.errorsPerSecond,
411
+ };
412
+ localPortLabel =
413
+ localInterface?.name || `if${claim.localInterfaceIndex}`;
414
+ }
415
+
416
+ /*
417
+ * The far end: best-effort — the advertised remote port id often
418
+ * matches the remote device's interface name (e.g. "GigabitEthernet
419
+ * 0/1"). Only attach data when it does; a bare port id string stays
420
+ * in toPort.
421
+ */
422
+ let remoteEndpoint: NetworkTopologyEdgeEndpoint | undefined = undefined;
423
+ if (matchedId && claim.remotePortId) {
424
+ const remotePortKey: string | undefined =
425
+ NetworkTopologyUtil.normalizeKey(claim.remotePortId);
426
+ const remoteInterface: TopologyInterfaceInput | undefined =
427
+ remotePortKey
428
+ ? interfaceByDeviceAndName.get(`${matchedId}::${remotePortKey}`)
429
+ : undefined;
430
+ if (remoteInterface) {
431
+ remoteEndpoint = {
432
+ interfaceIndex: remoteInterface.interfaceIndex,
433
+ interfaceName: remoteInterface.name,
434
+ isOperationallyUp: remoteInterface.isOperationallyUp,
435
+ isAdministrativelyUp: remoteInterface.isAdministrativelyUp,
436
+ utilizationPercent: remoteInterface.utilizationPercent,
437
+ inRateMbps: remoteInterface.inRateMbps,
438
+ outRateMbps: remoteInterface.outRateMbps,
439
+ errorsPerSecond: remoteInterface.errorsPerSecond,
440
+ };
333
441
  }
334
442
  }
335
- }
336
443
 
337
- const edges: Array<NetworkTopologyEdge> = Array.from(edgeByKey.values());
444
+ // Undirected dedupe: canonicalize the endpoint pair.
445
+ const edgeKey: string = [device.id, toNodeId].sort().join("::");
446
+ const existing: NetworkTopologyEdge | undefined = edgeByKey.get(edgeKey);
338
447
 
339
- // --- Endpoint nodes + fdb edges ---
448
+ if (!existing) {
449
+ edgeByKey.set(edgeKey, {
450
+ fromNodeId: device.id,
451
+ toNodeId: toNodeId,
452
+ fromPort: localPortLabel,
453
+ toPort: claim.remotePortId,
454
+ protocols: [claim.protocol],
455
+ fromInterface: localEndpoint,
456
+ toInterface: remoteEndpoint,
457
+ });
458
+ continue;
459
+ }
460
+
461
+ /*
462
+ * Same link reported again — from the other end, or by the other
463
+ * protocol. Merge instead of dropping so both ends keep the best
464
+ * data anyone reported about them.
465
+ */
466
+ if (existing.protocols && !existing.protocols.includes(claim.protocol)) {
467
+ existing.protocols.push(claim.protocol);
468
+ }
469
+
470
+ if (existing.fromNodeId === device.id) {
471
+ existing.fromPort = existing.fromPort || localPortLabel;
472
+ existing.toPort = existing.toPort || claim.remotePortId;
473
+ existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
474
+ existing.fromInterface,
475
+ localEndpoint,
476
+ );
477
+ existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
478
+ existing.toInterface,
479
+ remoteEndpoint,
480
+ );
481
+ } else {
482
+ existing.toPort = existing.toPort || localPortLabel;
483
+ existing.fromPort = existing.fromPort || claim.remotePortId;
484
+ existing.toInterface = NetworkTopologyUtil.mergeEndpoints(
485
+ existing.toInterface,
486
+ localEndpoint,
487
+ );
488
+ existing.fromInterface = NetworkTopologyUtil.mergeEndpoints(
489
+ existing.fromInterface,
490
+ remoteEndpoint,
491
+ );
492
+ }
493
+ }
340
494
 
341
495
  const deviceNodeIds: Set<string> = new Set(
342
496
  devices.map((device: TopologyDeviceInput) => {
@@ -344,6 +498,129 @@ export default class NetworkTopologyUtil {
344
498
  }),
345
499
  );
346
500
 
501
+ /*
502
+ * --- Operator-declared links ---
503
+ *
504
+ * Merged into the SAME edge map the discovery protocols write to, so a
505
+ * cable somebody drew by hand and the same cable LLDP later reports are
506
+ * one line carrying both protocols — not two lines between one pair of
507
+ * boxes, which is exactly the duplicate an operator would read as a
508
+ * second physical link.
509
+ *
510
+ * Ends outside this graph are skipped rather than drawn: a link to a
511
+ * device in another site (or one since archived) has nothing to attach
512
+ * to here.
513
+ */
514
+ for (const link of manualLinks) {
515
+ if (
516
+ !deviceNodeIds.has(link.fromDeviceId) ||
517
+ !deviceNodeIds.has(link.toDeviceId) ||
518
+ link.fromDeviceId === link.toDeviceId
519
+ ) {
520
+ continue;
521
+ }
522
+
523
+ const edgeKey: string = [link.fromDeviceId, link.toDeviceId]
524
+ .sort()
525
+ .join("::");
526
+ const existing: NetworkTopologyEdge | undefined = edgeByKey.get(edgeKey);
527
+
528
+ if (!existing) {
529
+ edgeByKey.set(edgeKey, {
530
+ fromNodeId: link.fromDeviceId,
531
+ toNodeId: link.toDeviceId,
532
+ fromPort: link.fromPortName,
533
+ toPort: link.toPortName,
534
+ protocols: ["manual"],
535
+ monitorState: link.monitorStatus,
536
+ name: link.name,
537
+ });
538
+ continue;
539
+ }
540
+
541
+ if (existing.protocols && !existing.protocols.includes("manual")) {
542
+ existing.protocols.push("manual");
543
+ }
544
+
545
+ /*
546
+ * Discovered data wins on the shared fields — it is measured, and the
547
+ * hand-typed port is a best recollection. The operator's name and the
548
+ * monitor's verdict have no discovered counterpart, so they always
549
+ * apply.
550
+ */
551
+ if (existing.fromNodeId === link.fromDeviceId) {
552
+ existing.fromPort = existing.fromPort || link.fromPortName;
553
+ existing.toPort = existing.toPort || link.toPortName;
554
+ } else {
555
+ existing.toPort = existing.toPort || link.fromPortName;
556
+ existing.fromPort = existing.fromPort || link.toPortName;
557
+ }
558
+ existing.name = existing.name || link.name;
559
+ existing.monitorState = existing.monitorState ?? link.monitorStatus;
560
+ }
561
+
562
+ const edges: Array<NetworkTopologyEdge> = Array.from(edgeByKey.values());
563
+
564
+ /*
565
+ * --- Why a device has no links ---
566
+ *
567
+ * Attached to every managed node, so the detail panel can answer "the
568
+ * router is not linked to anything" with a cause instead of a shrug.
569
+ * Built from the claims that were just resolved: the unmatched ones are
570
+ * the actionable evidence — a renamed peer, a device nobody has added
571
+ * yet — and they are otherwise thrown away here.
572
+ */
573
+ const reportedNeighborCountByDeviceId: Map<string, number> = new Map<
574
+ string,
575
+ number
576
+ >();
577
+ const unmatchedIdentifiersByDeviceId: Map<string, Array<string>> = new Map<
578
+ string,
579
+ Array<string>
580
+ >();
581
+
582
+ for (const resolved of resolvedClaims) {
583
+ const deviceId: string = resolved.device.id;
584
+ reportedNeighborCountByDeviceId.set(
585
+ deviceId,
586
+ (reportedNeighborCountByDeviceId.get(deviceId) || 0) + 1,
587
+ );
588
+
589
+ if (resolved.matchedDeviceId) {
590
+ continue;
591
+ }
592
+
593
+ const identifier: string | undefined =
594
+ resolved.claim.displayName || resolved.claim.identifiers[0];
595
+ if (!identifier) {
596
+ continue;
597
+ }
598
+ const bucket: Array<string> | undefined =
599
+ unmatchedIdentifiersByDeviceId.get(deviceId);
600
+ if (bucket) {
601
+ if (!bucket.includes(identifier)) {
602
+ bucket.push(identifier);
603
+ }
604
+ } else {
605
+ unmatchedIdentifiersByDeviceId.set(deviceId, [identifier]);
606
+ }
607
+ }
608
+
609
+ for (const node of nodes) {
610
+ if (node.kind !== "device") {
611
+ continue;
612
+ }
613
+ node.diagnostics = {
614
+ isNeighborDiscoveryEnabled: isNeighborDiscoveryEnabledById.get(node.id),
615
+ reportedNeighborCount:
616
+ reportedNeighborCountByDeviceId.get(node.id) || 0,
617
+ unmatchedNeighborIdentifiers:
618
+ unmatchedIdentifiersByDeviceId.get(node.id) || [],
619
+ };
620
+ }
621
+
622
+ // --- Endpoint nodes + fdb edges ---
623
+
347
624
  /*
348
625
  * Deterministic render order: by normalized MAC, then id, so the capped
349
626
  * slice is stable across rebuilds regardless of query order.
@@ -439,11 +716,47 @@ export default class NetworkTopologyUtil {
439
716
  });
440
717
  }
441
718
 
719
+ /*
720
+ * --- Hidden nodes ---
721
+ *
722
+ * Applied last, on the finished graph, so a suppressed node cannot
723
+ * change how anything else resolved. Hiding a switch must not silently
724
+ * re-route its neighbours' links or turn its peers into strangers — it
725
+ * takes the box off the map and nothing more. Its edges go with it,
726
+ * because a line to a node that is not drawn is a line into empty space.
727
+ */
728
+ if (suppressedNodeKeys.size === 0) {
729
+ return {
730
+ nodes,
731
+ edges,
732
+ droppedEndpointCount,
733
+ endpointsTruncated,
734
+ suppressedNodeCount: 0,
735
+ };
736
+ }
737
+
738
+ const visibleNodes: Array<NetworkTopologyNode> = nodes.filter(
739
+ (node: NetworkTopologyNode) => {
740
+ return !suppressedNodeKeys.has(node.id);
741
+ },
742
+ );
743
+ const visibleNodeIds: Set<string> = new Set<string>(
744
+ visibleNodes.map((node: NetworkTopologyNode) => {
745
+ return node.id;
746
+ }),
747
+ );
748
+
442
749
  return {
443
- nodes,
444
- edges,
750
+ nodes: visibleNodes,
751
+ edges: edges.filter((edge: NetworkTopologyEdge) => {
752
+ return (
753
+ visibleNodeIds.has(edge.fromNodeId) &&
754
+ visibleNodeIds.has(edge.toNodeId)
755
+ );
756
+ }),
445
757
  droppedEndpointCount,
446
758
  endpointsTruncated,
759
+ suppressedNodeCount: nodes.length - visibleNodes.length,
447
760
  };
448
761
  }
449
762
 
@@ -458,12 +771,26 @@ export default class NetworkTopologyUtil {
458
771
  const claims: Array<NeighborClaim> = [];
459
772
 
460
773
  for (const neighbor of device.lldpNeighbors || []) {
774
+ /*
775
+ * Both identifiers are carried, not just the first non-empty one.
776
+ * The chassis id used to be a fallback that only applied when the
777
+ * peer advertised no sysName; it is in fact an identifier in its own
778
+ * right — a MAC or a serial — and the device it names is very often
779
+ * one we already manage.
780
+ */
781
+ const identifiers: Array<string> = [];
782
+ if (neighbor.remoteSysName) {
783
+ identifiers.push(neighbor.remoteSysName);
784
+ }
785
+ if (neighbor.remoteChassisId) {
786
+ identifiers.push(neighbor.remoteChassisId);
787
+ }
788
+
461
789
  claims.push({
462
790
  protocol: "lldp",
463
- matchKey:
464
- NetworkTopologyUtil.normalizeKey(neighbor.remoteSysName) ||
465
- NetworkTopologyUtil.normalizeKey(neighbor.remoteChassisId),
791
+ identifiers: identifiers,
466
792
  displayName: neighbor.remoteSysName || neighbor.remoteChassisId,
793
+ hasAdvertisedName: Boolean(neighbor.remoteSysName),
467
794
  localInterfaceIndex: neighbor.localInterfaceIndex,
468
795
  remotePortId: neighbor.remotePortId,
469
796
  remotePlatform: undefined,
@@ -473,8 +800,9 @@ export default class NetworkTopologyUtil {
473
800
  for (const neighbor of device.cdpNeighbors || []) {
474
801
  claims.push({
475
802
  protocol: "cdp",
476
- matchKey: NetworkTopologyUtil.normalizeKey(neighbor.remoteDeviceId),
803
+ identifiers: neighbor.remoteDeviceId ? [neighbor.remoteDeviceId] : [],
477
804
  displayName: neighbor.remoteDeviceId,
805
+ hasAdvertisedName: Boolean(neighbor.remoteDeviceId),
478
806
  localInterfaceIndex: neighbor.localInterfaceIndex,
479
807
  remotePortId: neighbor.remotePortId,
480
808
  remotePlatform: neighbor.remotePlatform,
@@ -484,6 +812,102 @@ export default class NetworkTopologyUtil {
484
812
  return claims;
485
813
  }
486
814
 
815
+ /*
816
+ * Every key a claim could be resolved by, strongest kind first and, within
817
+ * a kind, strongest SOURCE first (an advertised sysName ahead of a chassis
818
+ * id). Sorting by kind before source is deliberate: a MAC read off a
819
+ * chassis id identifies a box far more precisely than the first label of
820
+ * somebody's FQDN, whichever field it arrived in.
821
+ */
822
+ private static candidateKeysForClaim(claim: NeighborClaim): Array<MatchKey> {
823
+ const byKind: Map<MatchKeyKind, Array<MatchKey>> = new Map<
824
+ MatchKeyKind,
825
+ Array<MatchKey>
826
+ >();
827
+ const seen: Set<string> = new Set<string>();
828
+
829
+ for (const identifier of claim.identifiers) {
830
+ for (const key of NetworkTopologyUtil.candidateKeysForIdentifier(
831
+ identifier,
832
+ )) {
833
+ const keyId: string = NetworkTopologyUtil.matchKeyId(key);
834
+ if (seen.has(keyId)) {
835
+ continue;
836
+ }
837
+ seen.add(keyId);
838
+ const bucket: Array<MatchKey> | undefined = byKind.get(key.kind);
839
+ if (bucket) {
840
+ bucket.push(key);
841
+ } else {
842
+ byKind.set(key.kind, [key]);
843
+ }
844
+ }
845
+ }
846
+
847
+ const ordered: Array<MatchKey> = [];
848
+ for (const kind of MATCH_KEY_KINDS_STRONGEST_FIRST) {
849
+ ordered.push(...(byKind.get(kind) || []));
850
+ }
851
+ return ordered;
852
+ }
853
+
854
+ /*
855
+ * One identifier string, offered as every kind it could be.
856
+ *
857
+ * A chassis id is just bytes on the wire — nothing in the protocol says
858
+ * whether it is a name, a serial or a MAC — so it is offered as all of
859
+ * them, and only a key some device actually published can match.
860
+ */
861
+ private static candidateKeysForIdentifier(
862
+ raw: string | undefined,
863
+ ): Array<MatchKey> {
864
+ const normalized: string | undefined =
865
+ NetworkTopologyUtil.normalizeKey(raw);
866
+ if (!normalized) {
867
+ return [];
868
+ }
869
+
870
+ const keys: Array<MatchKey> = [
871
+ { kind: "name", value: normalized },
872
+ { kind: "serial", value: normalized },
873
+ ];
874
+
875
+ const mac: string | undefined = normalizeMac(normalized);
876
+ if (mac) {
877
+ keys.push({ kind: "mac", value: mac });
878
+ }
879
+
880
+ const shortHost: string | undefined =
881
+ NetworkTopologyUtil.shortHostKey(normalized);
882
+ if (shortHost) {
883
+ keys.push({ kind: "shortHost", value: shortHost });
884
+ }
885
+
886
+ return keys;
887
+ }
888
+
889
+ /*
890
+ * The first label of a hostname, or undefined when taking one would be
891
+ * nonsense. An IPv4 literal would shorten to "10" and a MAC to its first
892
+ * octet pair — both of which would collide half the estate into one node,
893
+ * so both are refused outright.
894
+ */
895
+ private static shortHostKey(normalized: string): string | undefined {
896
+ if (IPV4_PATTERN.test(normalized) || normalizeMac(normalized)) {
897
+ return undefined;
898
+ }
899
+ const firstLabel: string = normalized.split(".")[0] || "";
900
+ return firstLabel.length > 0 ? firstLabel : undefined;
901
+ }
902
+
903
+ private static matchKeyId(key: MatchKey): string {
904
+ /*
905
+ * A NUL separator, because every kind shares one namespace and a
906
+ * hostname may legitimately contain a colon-ish character.
907
+ */
908
+ return `${key.kind}\u0000${key.value}`;
909
+ }
910
+
487
911
  // Existing fields win; the incoming report only fills what's missing.
488
912
  private static mergeEndpoints(
489
913
  existing: NetworkTopologyEdgeEndpoint | undefined,
@@ -510,25 +934,316 @@ export default class NetworkTopologyUtil {
510
934
  };
511
935
  }
512
936
 
937
+ /*
938
+ * Every key this device answers to.
939
+ *
940
+ * Unlike a claim's candidates, these are NOT speculative: we know which
941
+ * column each value came from, so a serial is only ever registered as a
942
+ * serial. That asymmetry is the whole point — a claim may offer its
943
+ * chassis id as a name, a serial and a MAC, but only the kind a device
944
+ * actually published can be the one that matches.
945
+ */
513
946
  private static matchKeysForDevice(
514
947
  device: TopologyDeviceInput,
515
- ): Array<string> {
516
- /*
517
- * sysName / hostname / name — the identifiers LLDP remoteSysName and
518
- * CDP remoteDeviceId advertise. Order matters only across devices
519
- * (last writer wins on a collision), not within one device.
520
- */
521
- const keys: Array<string> = [];
948
+ ): Array<MatchKey> {
949
+ const keys: Array<MatchKey> = [];
950
+ const seen: Set<string> = new Set<string>();
951
+
952
+ const add: (key: MatchKey) => void = (key: MatchKey): void => {
953
+ const keyId: string = NetworkTopologyUtil.matchKeyId(key);
954
+ if (seen.has(keyId)) {
955
+ return;
956
+ }
957
+ seen.add(keyId);
958
+ keys.push(key);
959
+ };
960
+
961
+ // sysName / hostname / name — what both protocols advertise.
522
962
  for (const candidate of [device.sysName, device.hostname, device.name]) {
523
- const key: string | undefined =
963
+ const normalized: string | undefined =
524
964
  NetworkTopologyUtil.normalizeKey(candidate);
525
- if (key && !keys.includes(key)) {
526
- keys.push(key);
965
+ if (!normalized) {
966
+ continue;
967
+ }
968
+ add({ kind: "name", value: normalized });
969
+ /*
970
+ * The short form is registered for EVERY name, dotted or not, so the
971
+ * two halves of the comparison meet: an FQDN on the wire has to be
972
+ * able to find a bare sysName here, and a bare name on the wire has
973
+ * to be able to find an FQDN.
974
+ */
975
+ const shortHost: string | undefined =
976
+ NetworkTopologyUtil.shortHostKey(normalized);
977
+ if (shortHost) {
978
+ add({ kind: "shortHost", value: shortHost });
979
+ }
980
+ }
981
+
982
+ const serial: string | undefined = NetworkTopologyUtil.normalizeKey(
983
+ device.serialNumber,
984
+ );
985
+ if (serial) {
986
+ add({ kind: "serial", value: serial });
987
+ }
988
+
989
+ for (const rawMac of device.macAddresses || []) {
990
+ const mac: string | undefined = normalizeMac(rawMac);
991
+ if (mac) {
992
+ add({ kind: "mac", value: mac });
527
993
  }
528
994
  }
995
+
529
996
  return keys;
530
997
  }
531
998
 
999
+ /*
1000
+ * Turns the unmatched claims into unmanaged peer NODES, one per box.
1001
+ *
1002
+ * Reports are merged when they share any identity-grade alias, so the
1003
+ * neighbor one switch knows as "ap-lobby" and another knows only by its
1004
+ * chassis MAC lands on a single node instead of the two strangers the map
1005
+ * used to draw. Appends the nodes it mints to `nodes` and returns the node
1006
+ * id each claim should draw its edge to.
1007
+ */
1008
+ private static buildUnmanagedPeerNodes(
1009
+ resolvedClaims: Array<ResolvedClaim>,
1010
+ nodes: Array<NetworkTopologyNode>,
1011
+ ): Map<ResolvedClaim, string> {
1012
+ const unmanagedClaims: Array<ResolvedClaim> = resolvedClaims.filter(
1013
+ (resolved: ResolvedClaim) => {
1014
+ return !resolved.matchedDeviceId && resolved.peerAliases.length > 0;
1015
+ },
1016
+ );
1017
+
1018
+ const rootByAliasId: Map<string, string> =
1019
+ NetworkTopologyUtil.groupPeerAliases(unmanagedClaims);
1020
+
1021
+ // Claims per peer, in first-encounter order so the output is stable.
1022
+ const claimsByRoot: Map<string, Array<ResolvedClaim>> = new Map<
1023
+ string,
1024
+ Array<ResolvedClaim>
1025
+ >();
1026
+ for (const resolved of unmanagedClaims) {
1027
+ const root: string | undefined = rootByAliasId.get(
1028
+ NetworkTopologyUtil.matchKeyId(resolved.peerAliases[0]!),
1029
+ );
1030
+ if (!root) {
1031
+ continue;
1032
+ }
1033
+ const bucket: Array<ResolvedClaim> | undefined = claimsByRoot.get(root);
1034
+ if (bucket) {
1035
+ bucket.push(resolved);
1036
+ } else {
1037
+ claimsByRoot.set(root, [resolved]);
1038
+ }
1039
+ }
1040
+
1041
+ const nodeIdByClaim: Map<ResolvedClaim, string> = new Map<
1042
+ ResolvedClaim,
1043
+ string
1044
+ >();
1045
+ const usedNodeIds: Set<string> = new Set<string>();
1046
+
1047
+ for (const groupClaims of claimsByRoot.values()) {
1048
+ /*
1049
+ * The nicest label anyone reported: a name the peer advertises about
1050
+ * itself beats a bare chassis id, whichever report arrived first.
1051
+ */
1052
+ let displayName: string | undefined = undefined;
1053
+ let platform: string | undefined = undefined;
1054
+ for (const resolved of groupClaims) {
1055
+ if (!displayName && resolved.claim.hasAdvertisedName) {
1056
+ displayName = resolved.claim.displayName;
1057
+ }
1058
+ if (!platform && resolved.claim.remotePlatform) {
1059
+ platform = resolved.claim.remotePlatform;
1060
+ }
1061
+ }
1062
+ if (!displayName) {
1063
+ for (const resolved of groupClaims) {
1064
+ if (resolved.claim.displayName) {
1065
+ displayName = resolved.claim.displayName;
1066
+ break;
1067
+ }
1068
+ }
1069
+ }
1070
+
1071
+ /*
1072
+ * The id keeps its historic `unmanaged:<value>` shape — it is the key
1073
+ * saved arrangements are stored under, so changing it would scatter
1074
+ * every pinned peer on the map. It is named after the label wherever
1075
+ * that label is one of the peer's own aliases, so merging a report
1076
+ * that knew a sysName with one that knew only a chassis MAC still
1077
+ * produces `unmanaged:ap-lobby` rather than an id made of hex.
1078
+ */
1079
+ const nodeId: string = NetworkTopologyUtil.uniqueNodeId(
1080
+ `unmanaged:${NetworkTopologyUtil.canonicalPeerValue(
1081
+ groupClaims,
1082
+ displayName,
1083
+ )}`,
1084
+ usedNodeIds,
1085
+ );
1086
+ usedNodeIds.add(nodeId);
1087
+
1088
+ const unmanagedName: string = displayName || "Unknown device";
1089
+ nodes.push({
1090
+ id: nodeId,
1091
+ name: unmanagedName,
1092
+ isManaged: false,
1093
+ kind: "unmanaged",
1094
+ /*
1095
+ * Classified from the whole group rather than from whichever
1096
+ * report happened to be first: the platform string is usually the
1097
+ * only real evidence about a peer, and it may well arrive on the
1098
+ * second report of it.
1099
+ */
1100
+ role: classifyDeviceRole({
1101
+ platform: platform,
1102
+ name: unmanagedName,
1103
+ }),
1104
+ status: "unknown",
1105
+ deviceModel: platform,
1106
+ });
1107
+
1108
+ for (const resolved of groupClaims) {
1109
+ nodeIdByClaim.set(resolved, nodeId);
1110
+ }
1111
+ }
1112
+
1113
+ return nodeIdByClaim;
1114
+ }
1115
+
1116
+ /*
1117
+ * The alias a merged peer is named after.
1118
+ *
1119
+ * The chosen label wins whenever it is genuinely one of the peer's own
1120
+ * aliases; otherwise the strongest kind present decides, and within it
1121
+ * the lexicographically smallest value, so the id never depends on which
1122
+ * switch happened to be polled first.
1123
+ */
1124
+ private static canonicalPeerValue(
1125
+ groupClaims: Array<ResolvedClaim>,
1126
+ displayName: string | undefined,
1127
+ ): string {
1128
+ const valuesByKind: Map<MatchKeyKind, Array<string>> = new Map<
1129
+ MatchKeyKind,
1130
+ Array<string>
1131
+ >();
1132
+ for (const resolved of groupClaims) {
1133
+ for (const alias of resolved.peerAliases) {
1134
+ const bucket: Array<string> | undefined = valuesByKind.get(alias.kind);
1135
+ if (bucket) {
1136
+ if (!bucket.includes(alias.value)) {
1137
+ bucket.push(alias.value);
1138
+ }
1139
+ } else {
1140
+ valuesByKind.set(alias.kind, [alias.value]);
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ const preferred: string | undefined =
1146
+ NetworkTopologyUtil.normalizeKey(displayName);
1147
+ if (preferred && (valuesByKind.get("name") || []).includes(preferred)) {
1148
+ return preferred;
1149
+ }
1150
+
1151
+ for (const kind of MATCH_KEY_KINDS_STRONGEST_FIRST) {
1152
+ const values: Array<string> | undefined = valuesByKind.get(kind);
1153
+ if (values && values.length > 0) {
1154
+ return [...values].sort()[0]!;
1155
+ }
1156
+ }
1157
+
1158
+ return "unknown-peer";
1159
+ }
1160
+
1161
+ /*
1162
+ * Two peers could in principle canonicalize to the same string (one by
1163
+ * name, one by serial). Suffixing rather than merging keeps them the
1164
+ * distinct boxes they are.
1165
+ */
1166
+ private static uniqueNodeId(
1167
+ preferred: string,
1168
+ used: ReadonlySet<string>,
1169
+ ): string {
1170
+ if (!used.has(preferred)) {
1171
+ return preferred;
1172
+ }
1173
+ let suffix: number = 2;
1174
+ while (used.has(`${preferred}#${suffix}`)) {
1175
+ suffix++;
1176
+ }
1177
+ return `${preferred}#${suffix}`;
1178
+ }
1179
+
1180
+ /*
1181
+ * Union-find over peer aliases: two reports that share ANY identity-grade
1182
+ * alias describe one box. Returns the representative alias id for every
1183
+ * alias seen.
1184
+ */
1185
+ private static groupPeerAliases(
1186
+ unmanagedClaims: Array<ResolvedClaim>,
1187
+ ): Map<string, string> {
1188
+ const parent: Map<string, string> = new Map<string, string>();
1189
+
1190
+ const find: (key: string) => string = (key: string): string => {
1191
+ let root: string = key;
1192
+ let next: string | undefined = parent.get(root);
1193
+ while (next !== undefined && next !== root) {
1194
+ root = next;
1195
+ next = parent.get(root);
1196
+ }
1197
+ // Path compression, so repeated lookups stay flat.
1198
+ let cursor: string = key;
1199
+ while (cursor !== root) {
1200
+ const following: string = parent.get(cursor) || root;
1201
+ parent.set(cursor, root);
1202
+ cursor = following;
1203
+ }
1204
+ return root;
1205
+ };
1206
+
1207
+ const union: (a: string, b: string) => void = (
1208
+ a: string,
1209
+ b: string,
1210
+ ): void => {
1211
+ const rootA: string = find(a);
1212
+ const rootB: string = find(b);
1213
+ if (rootA === rootB) {
1214
+ return;
1215
+ }
1216
+ // Lexicographic tie-break, so the result ignores input order.
1217
+ if (rootA < rootB) {
1218
+ parent.set(rootB, rootA);
1219
+ } else {
1220
+ parent.set(rootA, rootB);
1221
+ }
1222
+ };
1223
+
1224
+ for (const resolved of unmanagedClaims) {
1225
+ const aliasIds: Array<string> = resolved.peerAliases.map(
1226
+ (alias: MatchKey) => {
1227
+ return NetworkTopologyUtil.matchKeyId(alias);
1228
+ },
1229
+ );
1230
+ for (const aliasId of aliasIds) {
1231
+ if (!parent.has(aliasId)) {
1232
+ parent.set(aliasId, aliasId);
1233
+ }
1234
+ }
1235
+ for (let index: number = 1; index < aliasIds.length; index++) {
1236
+ union(aliasIds[0]!, aliasIds[index]!);
1237
+ }
1238
+ }
1239
+
1240
+ const rootByAliasId: Map<string, string> = new Map<string, string>();
1241
+ for (const aliasId of parent.keys()) {
1242
+ rootByAliasId.set(aliasId, find(aliasId));
1243
+ }
1244
+ return rootByAliasId;
1245
+ }
1246
+
532
1247
  private static normalizeKey(value: string | undefined): string | undefined {
533
1248
  if (!value) {
534
1249
  return undefined;
@@ -537,10 +1252,19 @@ export default class NetworkTopologyUtil {
537
1252
  return trimmed.length > 0 ? trimmed : undefined;
538
1253
  }
539
1254
 
1255
+ /*
1256
+ * A stamped monitor status beats SNMP freshness, and freshness is the
1257
+ * fallback — the same precedence SiteStatusRollupUtil already applies when
1258
+ * it rolls devices up into a site, so a device cannot read "up" on the map
1259
+ * and "down" on the site card above it.
1260
+ */
540
1261
  private static deviceStatus(
541
1262
  device: TopologyDeviceInput,
542
1263
  now: Date,
543
1264
  ): NetworkTopologyNodeStatus {
1265
+ if (device.monitorStatus) {
1266
+ return device.monitorStatus;
1267
+ }
544
1268
  return NetworkTopologyUtil.statusFromLastSeen(device.lastSeenAt, now);
545
1269
  }
546
1270