@datalayer/core 1.1.60 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -18
- package/lib/api/DatalayerApi.d.ts +42 -1
- package/lib/api/DatalayerApi.js +90 -3
- package/lib/api/constants.d.ts +19 -1
- package/lib/api/constants.js +19 -1
- package/lib/api/contents/attachments.d.ts +9 -0
- package/lib/api/contents/attachments.js +39 -0
- package/lib/api/contents/bridges.d.ts +22 -0
- package/lib/api/contents/bridges.js +79 -0
- package/lib/api/contents/capabilities.d.ts +11 -0
- package/lib/api/contents/capabilities.js +21 -0
- package/lib/api/contents/cloudStorage.d.ts +31 -0
- package/lib/api/contents/cloudStorage.js +89 -0
- package/lib/api/contents/dataservers.d.ts +37 -0
- package/lib/api/contents/dataservers.js +121 -0
- package/lib/api/contents/datasets.d.ts +63 -0
- package/lib/api/contents/datasets.js +82 -0
- package/lib/api/contents/datasources.d.ts +70 -0
- package/lib/api/contents/datasources.js +149 -0
- package/lib/api/contents/generated.d.ts +1070 -0
- package/lib/api/contents/index.d.ts +16 -0
- package/lib/api/contents/index.js +20 -0
- package/lib/api/contents/mcp.d.ts +46 -0
- package/lib/api/contents/mcp.js +157 -0
- package/lib/api/contents/objects.d.ts +31 -0
- package/lib/api/contents/objects.js +88 -0
- package/lib/api/contents/operations.d.ts +3 -0
- package/lib/api/contents/operations.js +19 -0
- package/lib/api/contents/runtimeMounts.d.ts +40 -0
- package/lib/api/contents/runtimeMounts.js +59 -0
- package/lib/api/contents/sandboxUid.d.ts +21 -0
- package/lib/api/contents/sandboxUid.js +27 -0
- package/lib/api/contents/sources.d.ts +19 -0
- package/lib/api/contents/sources.js +119 -0
- package/lib/api/contents/sync.d.ts +18 -0
- package/lib/api/contents/sync.js +69 -0
- package/lib/api/contents/transfers.d.ts +38 -0
- package/lib/api/contents/transfers.js +123 -0
- package/lib/api/evals/client.d.ts +402 -0
- package/lib/api/evals/client.js +359 -0
- package/lib/api/evals/index.d.ts +10 -0
- package/lib/api/evals/index.js +18 -0
- package/lib/api/evals/request.d.ts +43 -0
- package/lib/api/evals/request.js +67 -0
- package/lib/api/evals/status.d.ts +27 -0
- package/lib/api/evals/status.js +122 -0
- package/lib/api/evals/types.d.ts +1335 -0
- package/lib/api/evals/types.js +5 -0
- package/lib/api/iam/connectedAgents.d.ts +68 -0
- package/lib/api/iam/connectedAgents.js +87 -0
- package/lib/api/iam/identityProviders.d.ts +76 -0
- package/lib/api/iam/identityProviders.js +187 -0
- package/lib/api/iam/index.d.ts +3 -1
- package/lib/api/iam/index.js +3 -1
- package/lib/api/iam/mcpAlertRules.d.ts +51 -0
- package/lib/api/iam/mcpAlertRules.js +198 -0
- package/lib/api/iam/mcpAuditSettings.d.ts +46 -0
- package/lib/api/iam/mcpAuditSettings.js +120 -0
- package/lib/api/iam/mcpPolicy.d.ts +181 -0
- package/lib/api/iam/mcpPolicy.js +168 -0
- package/lib/api/iam/organizationTeams.d.ts +13 -0
- package/lib/api/iam/organizationTeams.js +43 -0
- package/lib/api/iam/profile.d.ts +17 -1
- package/lib/api/iam/profile.js +27 -0
- package/lib/api/iam/scimTokens.d.ts +79 -0
- package/lib/api/iam/scimTokens.js +141 -0
- package/lib/api/iam/secrets.js +9 -21
- package/lib/api/iam/serviceAgents.d.ts +68 -0
- package/lib/api/iam/serviceAgents.js +119 -0
- package/lib/api/iam/trials.d.ts +38 -0
- package/lib/api/iam/trials.js +66 -0
- package/lib/api/index.d.ts +8 -1
- package/lib/api/index.js +7 -1
- package/lib/api/mcp/activity.d.ts +37 -0
- package/lib/api/mcp/activity.js +26 -0
- package/lib/api/mcp/alerts.d.ts +25 -0
- package/lib/api/mcp/alerts.js +51 -0
- package/lib/api/mcp/audit.d.ts +29 -0
- package/lib/api/mcp/audit.js +55 -0
- package/lib/api/mcp/bindings.d.ts +18 -0
- package/lib/api/mcp/bindings.js +42 -0
- package/lib/api/mcp/forwarding.d.ts +25 -0
- package/lib/api/mcp/forwarding.js +46 -0
- package/lib/api/mcp/gateway.d.ts +16 -0
- package/lib/api/mcp/gateway.js +70 -0
- package/lib/api/mcp/generated.d.ts +22 -0
- package/lib/api/mcp/generated.js +213 -0
- package/lib/api/mcp/index.d.ts +17 -0
- package/lib/api/mcp/index.js +25 -0
- package/lib/api/mcp/observability.d.ts +141 -0
- package/lib/api/mcp/observability.js +267 -0
- package/lib/api/mcp/operations.d.ts +83 -0
- package/lib/api/mcp/operations.js +50 -0
- package/lib/api/mcp/organizations.d.ts +102 -0
- package/lib/api/mcp/organizations.js +46 -0
- package/lib/api/mcp/policy.d.ts +9 -0
- package/lib/api/mcp/policy.js +23 -0
- package/lib/api/mcp/tasks.d.ts +49 -0
- package/lib/api/mcp/tasks.js +147 -0
- package/lib/api/orchestration/client.d.ts +53 -0
- package/lib/api/orchestration/client.js +105 -0
- package/lib/api/orchestration/events.d.ts +88 -0
- package/lib/api/orchestration/events.js +245 -0
- package/lib/api/orchestration/generated.d.ts +693 -0
- package/lib/api/orchestration/generated.js +687 -0
- package/lib/api/orchestration/index.d.ts +30 -0
- package/lib/api/orchestration/index.js +34 -0
- package/lib/api/orchestration/lifecycle.d.ts +39 -0
- package/lib/api/orchestration/lifecycle.js +57 -0
- package/lib/api/orchestration/measures.d.ts +55 -0
- package/lib/api/orchestration/measures.js +99 -0
- package/lib/api/otel/dashboards.d.ts +56 -0
- package/lib/api/otel/dashboards.js +50 -0
- package/lib/api/otel/index.d.ts +1 -0
- package/lib/api/otel/index.js +1 -0
- package/lib/api/otel/metrics.d.ts +16 -0
- package/lib/api/otel/metrics.js +15 -0
- package/lib/api/scheduler/client.d.ts +32 -0
- package/lib/api/scheduler/client.js +42 -0
- package/lib/api/scheduler/index.d.ts +8 -0
- package/lib/api/scheduler/index.js +16 -0
- package/lib/api/scheduler/request.d.ts +28 -0
- package/lib/api/scheduler/request.js +50 -0
- package/lib/api/scheduler/types.d.ts +88 -0
- package/lib/api/scheduler/types.js +5 -0
- package/lib/api/spacer/comments.d.ts +97 -0
- package/lib/api/spacer/comments.js +45 -0
- package/lib/api/spacer/index.d.ts +9 -0
- package/lib/api/spacer/index.js +17 -0
- package/lib/api/spacer/notebooks.d.ts +33 -0
- package/lib/api/spacer/notebooks.js +39 -0
- package/lib/api/spacer/request.d.ts +25 -0
- package/lib/api/spacer/request.js +46 -0
- package/lib/api/spacer/spaces.d.ts +68 -0
- package/lib/api/spacer/spaces.js +45 -0
- package/lib/api/utils/validation.d.ts +4 -4
- package/lib/api/utils/validation.js +10 -7
- package/lib/client/auth/index.js +0 -5
- package/lib/client/auth/storage.js +0 -5
- package/lib/client/auth/strategies.js +0 -5
- package/lib/client/base.d.ts +6 -0
- package/lib/client/base.js +8 -0
- package/lib/client/constants.d.ts +1 -0
- package/lib/client/constants.js +1 -0
- package/lib/client/index.d.ts +0 -10
- package/lib/client/index.js +2 -2
- package/lib/client/mixins/ContentsMixin.d.ts +71 -0
- package/lib/client/mixins/ContentsMixin.js +157 -0
- package/lib/client/mixins/IAMMixin.d.ts +0 -31
- package/lib/client/mixins/IAMMixin.js +0 -58
- package/lib/components/animation/AnimatedText.d.ts +2 -44
- package/lib/components/animation/AnimatedText.js +6 -126
- package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
- package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
- package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
- package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
- package/lib/components/anonymous/index.d.ts +7 -0
- package/lib/components/anonymous/index.js +15 -0
- package/lib/components/auth/SigInAPIKey.d.ts +1 -0
- package/lib/components/auth/SigInAPIKey.js +0 -6
- package/lib/components/auth/SignIn.d.ts +1 -0
- package/lib/components/auth/SignIn.js +0 -4
- package/lib/components/auth/SignIn.stories.d.ts +2 -2
- package/lib/components/avatars/BoringAvatar.d.ts +1 -1
- package/lib/components/avatars/BoringAvatar.stories.d.ts +1 -1
- package/lib/components/avatars/DLAvatar.d.ts +1 -1
- package/lib/components/avatars/DLAvatar.js +0 -4
- package/lib/components/avatars/UserAvatar.d.ts +16 -1
- package/lib/components/avatars/UserAvatar.js +15 -13
- package/lib/components/avatars/UserProfileAvatar.d.ts +1 -1
- package/lib/components/banners/NoAutomationBanner.d.ts +1 -1
- package/lib/components/billing/BillingEntitySelect.d.ts +16 -0
- package/lib/components/billing/BillingEntitySelect.js +49 -27
- package/lib/components/billing/eligibility.d.ts +22 -0
- package/lib/components/billing/eligibility.js +27 -0
- package/lib/components/billing/index.d.ts +1 -0
- package/lib/components/billing/index.js +1 -4
- package/lib/components/buttons/DownloadCSVButton.d.ts +1 -1
- package/lib/components/buttons/DownloadJsonButton.d.ts +1 -1
- package/lib/components/buttons/LongActionButton.d.ts +1 -0
- package/lib/components/buttons/LongActionButton.js +0 -4
- package/lib/components/buttons/UploadButton.d.ts +1 -0
- package/lib/components/buttons/UploadButton.js +0 -4
- package/lib/components/checkout/StripeCheckout.d.ts +1 -1
- package/lib/components/collaboration/LiveEditorCollaborators.d.ts +36 -0
- package/lib/components/collaboration/LiveEditorCollaborators.js +88 -53
- package/lib/components/collaboration/index.js +0 -4
- package/lib/components/confetti/ConfettiSuccess.d.ts +1 -1
- package/lib/components/contents/ContentSourceCard.d.ts +8 -0
- package/lib/components/contents/ContentSourceCard.js +23 -0
- package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +10 -0
- package/lib/components/contents/HomeFolderObjectsBrowser.js +148 -0
- package/lib/components/contents/index.d.ts +2 -0
- package/lib/components/contents/index.js +6 -0
- package/lib/components/context/OrganizationSelect.d.ts +1 -1
- package/lib/components/display/AvatarSkeleton.d.ts +1 -1
- package/lib/components/display/BusyDots.d.ts +1 -1
- package/lib/components/display/CenteredSpinner.d.ts +1 -1
- package/lib/components/display/CodePreview.d.ts +1 -1
- package/lib/components/display/DatalayerBox.d.ts +7 -3
- package/lib/components/display/DatalayerBox.js +7 -7
- package/lib/components/display/HorizontalCenter.d.ts +1 -1
- package/lib/components/display/JupyterDialog.d.ts +1 -0
- package/lib/components/display/JupyterDialog.js +1 -7
- package/lib/components/display/Markdown.d.ts +1 -0
- package/lib/components/display/NavLink.js +3 -2
- package/lib/components/display/NotebookSkeleton.d.ts +1 -1
- package/lib/components/display/Placeholder.d.ts +1 -1
- package/lib/components/display/ToTopBranded.d.ts +1 -1
- package/lib/components/echarts/EChartsReact.d.ts +1 -1
- package/lib/components/flashes/FlashClosable.d.ts +1 -0
- package/lib/components/flashes/FlashClosable.js +0 -4
- package/lib/components/flashes/FlashGuest.d.ts +1 -1
- package/lib/components/flashes/FlashSurveys.d.ts +1 -1
- package/lib/components/flashes/FlashUnauthorized.d.ts +1 -0
- package/lib/components/flashes/FlashUnauthorized.js +0 -6
- package/lib/components/flashes/surveys/Survey2025_1.d.ts +1 -1
- package/lib/components/iam/ExternalTokenSilentLogin.d.ts +1 -1
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/labels/StatusLabels.d.ts +2 -9
- package/lib/components/labels/StatusLabels.js +2 -26
- package/lib/components/labels/VisibilityLabel.d.ts +1 -1
- package/lib/components/landings/StepBlock.d.ts +1 -1
- package/lib/components/mcp/ClientBadge.d.ts +41 -0
- package/lib/components/mcp/ClientBadge.js +33 -0
- package/lib/components/mcp/McpErrorBlankslate.d.ts +21 -0
- package/lib/components/mcp/McpErrorBlankslate.js +17 -0
- package/lib/components/mcp/ScopeList.d.ts +28 -0
- package/lib/components/mcp/ScopeList.js +14 -0
- package/lib/components/mcp/index.d.ts +10 -0
- package/lib/components/mcp/index.js +18 -0
- package/lib/components/navbar/NavigationVisbilityObserver.d.ts +2 -1
- package/lib/components/navbar/SubdomainNavBar.d.ts +10 -5
- package/lib/components/navbar/SubdomainNavBar.js +2 -1
- package/lib/components/notebooks/JupyterNotebook.d.ts +1 -1
- package/lib/components/notebooks/JupyterNotebookToolbar.d.ts +1 -1
- package/lib/components/principal/PrincipalAppearance.d.ts +22 -42
- package/lib/components/principal/PrincipalAppearance.js +232 -182
- package/lib/components/principal/PrincipalAvatar.d.ts +9 -1
- package/lib/components/principal/PrincipalAvatar.js +18 -10
- package/lib/components/principal/PrincipalBadge.d.ts +1 -1
- package/lib/components/principal/PrincipalBanner.d.ts +1 -1
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +38 -1
- package/lib/components/principal/PrincipalDetailsOverlay.js +64 -33
- package/lib/components/principal/PrincipalHoverCard.d.ts +31 -0
- package/lib/components/principal/PrincipalHoverCard.js +52 -0
- package/lib/components/principal/PrincipalSwitcherMenu.d.ts +1 -0
- package/lib/components/principal/PrincipalSwitcherMenu.js +5 -14
- package/lib/components/principal/index.d.ts +2 -1
- package/lib/components/principal/index.js +2 -1
- package/lib/components/progress/ConsumptionBar.d.ts +1 -0
- package/lib/components/progress/ConsumptionBar.js +0 -4
- package/lib/components/progress/ProgressRing.d.ts +1 -0
- package/lib/components/progress/SpinnerBar.d.ts +1 -1
- package/lib/components/progress/Timer.d.ts +1 -0
- package/lib/components/progress/Timer.js +0 -4
- package/lib/components/screencapture/Screencapture.js +46 -3
- package/lib/components/screencapture/ScreencaptureButton.d.ts +1 -1
- package/lib/components/sharing/ShareAccessComponent.d.ts +58 -3
- package/lib/components/sharing/ShareAccessComponent.js +143 -98
- package/lib/components/sharing/SharingEditor.d.ts +21 -0
- package/lib/components/sharing/SharingEditor.js +9 -28
- package/lib/components/sharing/index.d.ts +1 -0
- package/lib/components/sharing/index.js +1 -0
- package/lib/components/sharing/sandboxSharing.d.ts +26 -0
- package/lib/components/sharing/sandboxSharing.js +41 -0
- package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
- package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
- package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
- package/lib/components/spaces/SpaceDisplay.js +92 -0
- package/lib/components/spaces/index.d.ts +4 -0
- package/lib/components/spaces/index.js +7 -0
- package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
- package/lib/components/spaces/spaceDisplayModel.js +170 -0
- package/lib/components/sparklines/Sparklines.js +3 -4
- package/lib/components/sparklines/index.js +0 -4
- package/lib/components/storage/ContentsBrowser.d.ts +25 -1
- package/lib/components/storage/ContentsBrowser.js +145 -52
- package/lib/components/storage/ContentsBrowserMock.d.ts +3 -0
- package/lib/components/storage/ContentsBrowserMock.js +75 -0
- package/lib/components/storage/ContentsItems.d.ts +2 -1
- package/lib/components/storage/ContentsItems.js +0 -4
- package/lib/components/storage/ContentsTable.d.ts +50 -0
- package/lib/components/storage/ContentsTable.js +134 -0
- package/lib/components/storage/HomeFolderDrive.d.ts +71 -0
- package/lib/components/storage/HomeFolderDrive.js +248 -0
- package/lib/components/storage/index.d.ts +2 -0
- package/lib/components/storage/index.js +2 -0
- package/lib/components/students/StudentItemStatus.d.ts +1 -1
- package/lib/components/students/StudentResultCircle.d.ts +1 -1
- package/lib/components/subnav/SubNav.d.ts +5 -5
- package/lib/components/subnav/SubNav.js +11 -9
- package/lib/components/tables/DataTable.d.ts +1 -1
- package/lib/components/text-reveal/TextRevealAnimation.d.ts +2 -2
- package/lib/components/text-reveal/useTextRevealAnimationLines.d.ts +1 -1
- package/lib/components/time/LiveRelativeTime.d.ts +1 -0
- package/lib/components/time/LiveRelativeTime.js +0 -8
- package/lib/components/time/index.js +0 -4
- package/lib/components/timeline/Timeline.d.ts +1 -1
- package/lib/components/timeline/index.js +0 -4
- package/lib/components/users/PeerIndicator.d.ts +1 -0
- package/lib/components/users/PeerIndicator.js +0 -4
- package/lib/config/Configuration.d.ts +15 -17
- package/lib/config/Configuration.js +7 -0
- package/lib/hooks/cacheConverters.d.ts +0 -7
- package/lib/hooks/cacheConverters.js +0 -25
- package/lib/hooks/index.d.ts +3 -0
- package/lib/hooks/index.js +3 -0
- package/lib/hooks/layouts/LayoutBackdrop.d.ts +1 -1
- package/lib/hooks/layouts/LayoutScreencapture.d.ts +1 -1
- package/lib/hooks/useBackdrop.d.ts +1 -1
- package/lib/hooks/useBackdropJupyterLab.d.ts +3 -2
- package/lib/hooks/useBackdropJupyterLab.js +0 -4
- package/lib/hooks/useBillingEntityStore.js +3 -0
- package/lib/hooks/useCache.d.ts +478 -130
- package/lib/hooks/useCache.js +1580 -676
- package/lib/hooks/useContainsFocus.d.ts +1 -1
- package/lib/hooks/useContents.d.ts +334 -0
- package/lib/hooks/useContents.js +1033 -0
- package/lib/hooks/useFocusTrap.d.ts +4 -4
- package/lib/hooks/useFocusTrap.js +1 -1
- package/lib/hooks/useMcp.d.ts +315 -0
- package/lib/hooks/useMcp.js +891 -0
- package/lib/hooks/useMobile.js +0 -5
- package/lib/hooks/useNavigate.d.ts +41 -3
- package/lib/hooks/useNavigate.js +106 -52
- package/lib/hooks/usePrincipalCacheStore.js +3 -0
- package/lib/hooks/usePrincipalStore.js +3 -0
- package/lib/hooks/useProjectStore.d.ts +3 -3
- package/lib/hooks/useProjectStore.js +4 -4
- package/lib/hooks/useRef.d.ts +1 -1
- package/lib/hooks/useRef.js +3 -0
- package/lib/hooks/useScreencapture.d.ts +2 -1
- package/lib/hooks/useScreencapture.js +0 -4
- package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
- package/lib/hooks/useSpaceCacheStore.js +85 -0
- package/lib/hooks/useToast.js +0 -4
- package/lib/hooks/useVisibilityObserver.d.ts +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +3 -1
- package/lib/mocks/components/FlashMock.d.ts +1 -1
- package/lib/mocks/views/ActionMenuMock.d.ts +1 -1
- package/lib/mocks/views/ChartMock.d.ts +1 -1
- package/lib/mocks/views/DashboardMock.d.ts +1 -1
- package/lib/mocks/views/FormMock.d.ts +1 -1
- package/lib/mocks/views/TableMock.d.ts +1 -1
- package/lib/mocks/views/WipMock.d.ts +1 -1
- package/lib/models/Credits.d.ts +16 -0
- package/lib/models/Credits.js +22 -0
- package/lib/models/CreditsDTO.d.ts +6 -5
- package/lib/models/CreditsDTO.js +7 -5
- package/lib/models/McpAuditEvent.d.ts +52 -0
- package/lib/models/McpAuditEvent.js +5 -0
- package/lib/models/McpBinding.d.ts +57 -0
- package/lib/models/McpBinding.js +5 -0
- package/lib/models/McpPolicy.d.ts +77 -0
- package/lib/models/McpPolicy.js +5 -0
- package/lib/models/McpTask.d.ts +94 -0
- package/lib/models/McpTask.js +7 -0
- package/lib/models/Organization.d.ts +69 -0
- package/lib/models/Organization.js +96 -0
- package/lib/models/Secret.d.ts +1 -2
- package/lib/models/Secret.js +2 -18
- package/lib/models/StartedBy.d.ts +91 -0
- package/lib/models/StartedBy.js +117 -0
- package/lib/models/Team.d.ts +2 -0
- package/lib/models/Team.js +2 -0
- package/lib/models/User.d.ts +4 -0
- package/lib/models/User.js +3 -0
- package/lib/models/UserDTO.js +4 -2
- package/lib/models/UserSettings.d.ts +13 -0
- package/lib/models/UserSettings.js +9 -0
- package/lib/models/contents/__fixtures__/solr-codec.json +57 -0
- package/lib/models/contents/__fixtures__/v1-contracts.json +194 -0
- package/lib/models/contents/fieldCase.d.ts +8 -0
- package/lib/models/contents/fieldCase.js +37 -0
- package/lib/models/contents/index.d.ts +2 -0
- package/lib/models/contents/index.js +6 -0
- package/lib/models/contents/permissions.d.ts +16 -0
- package/lib/models/contents/permissions.js +34 -0
- package/lib/models/index.d.ts +5 -1
- package/lib/models/index.js +5 -1
- package/lib/navigation/components.js +2 -3
- package/lib/routes/publicPaths.js +4 -0
- package/lib/state/index.d.ts +1 -0
- package/lib/state/index.js +1 -0
- package/lib/state/sessionEnd.d.ts +55 -0
- package/lib/state/sessionEnd.js +162 -0
- package/lib/state/substates/CoreState.d.ts +15 -7
- package/lib/state/substates/CoreState.js +21 -17
- package/lib/state/substates/IAMState.d.ts +1 -1
- package/lib/state/substates/IAMState.js +12 -5
- package/lib/state/substates/LayoutState.js +12 -0
- package/lib/state/substates/ProfileState.d.ts +1 -0
- package/lib/state/substates/ProfileState.js +1 -4
- package/lib/state/substates/index.d.ts +0 -1
- package/lib/state/substates/index.js +0 -1
- package/lib/utils/Jwt.d.ts +26 -0
- package/lib/utils/Jwt.js +37 -0
- package/lib/utils/Lazy.d.ts +20 -0
- package/lib/utils/Lazy.js +32 -6
- package/lib/utils/Screencapture.js +3 -0
- package/lib/utils/WithSuspense.d.ts +11 -1
- package/lib/utils/WithSuspense.js +11 -1
- package/lib/views/api-keys/APIKeyEdit.d.ts +1 -1
- package/lib/views/api-keys/APIKeyNew.d.ts +1 -1
- package/lib/views/api-keys/APIKeys.d.ts +1 -1
- package/lib/views/api-keys/APIKeys.js +30 -4
- package/lib/views/api-keys/APIKeysStandalone.d.ts +5 -1
- package/lib/views/api-keys/APIKeysStandalone.js +2 -2
- package/lib/views/datasources/DatasourceDetail.d.ts +12 -5
- package/lib/views/datasources/DatasourceDetail.js +80 -110
- package/lib/views/datasources/DatasourceNew.d.ts +14 -8
- package/lib/views/datasources/DatasourceNew.js +129 -118
- package/lib/views/datasources/Datasources.d.ts +39 -7
- package/lib/views/datasources/Datasources.js +95 -40
- package/lib/views/datasources/DatasourcesMock.d.ts +3 -0
- package/lib/views/datasources/DatasourcesMock.js +57 -0
- package/lib/views/iam/SignInSimple.d.ts +12 -0
- package/lib/views/iam/SignInSimple.js +4 -4
- package/lib/views/index.d.ts +1 -0
- package/lib/views/index.js +1 -0
- package/lib/views/mcp/AdmittedClients.d.ts +44 -0
- package/lib/views/mcp/AdmittedClients.js +59 -0
- package/lib/views/mcp/AlertDestinations.d.ts +34 -0
- package/lib/views/mcp/AlertDestinations.js +75 -0
- package/lib/views/mcp/AlertRules.d.ts +32 -0
- package/lib/views/mcp/AlertRules.js +245 -0
- package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
- package/lib/views/mcp/ApprovalQueue.js +95 -0
- package/lib/views/mcp/AuditLog.d.ts +42 -0
- package/lib/views/mcp/AuditLog.js +144 -0
- package/lib/views/mcp/ConnectedAgents.d.ts +34 -0
- package/lib/views/mcp/ConnectedAgents.js +135 -0
- package/lib/views/mcp/EnterpriseConsole.d.ts +74 -0
- package/lib/views/mcp/EnterpriseConsole.js +195 -0
- package/lib/views/mcp/IdentityProviders.d.ts +33 -0
- package/lib/views/mcp/IdentityProviders.js +299 -0
- package/lib/views/mcp/McpDashboard.d.ts +50 -0
- package/lib/views/mcp/McpDashboard.js +430 -0
- package/lib/views/mcp/McpHome.d.ts +92 -0
- package/lib/views/mcp/McpHome.js +153 -0
- package/lib/views/mcp/McpObservability.d.ts +79 -0
- package/lib/views/mcp/McpObservability.js +236 -0
- package/lib/views/mcp/NotebookRuns.d.ts +39 -0
- package/lib/views/mcp/NotebookRuns.js +44 -0
- package/lib/views/mcp/OrganizationPolicy.d.ts +45 -0
- package/lib/views/mcp/OrganizationPolicy.js +108 -0
- package/lib/views/mcp/OrganizationUsage.d.ts +69 -0
- package/lib/views/mcp/OrganizationUsage.js +113 -0
- package/lib/views/mcp/PersonalPolicy.d.ts +54 -0
- package/lib/views/mcp/PersonalPolicy.js +126 -0
- package/lib/views/mcp/Policies.d.ts +53 -0
- package/lib/views/mcp/Policies.js +210 -0
- package/lib/views/mcp/PolicyForm.d.ts +83 -0
- package/lib/views/mcp/PolicyForm.js +113 -0
- package/lib/views/mcp/PolicyHistory.d.ts +58 -0
- package/lib/views/mcp/PolicyHistory.js +75 -0
- package/lib/views/mcp/RunDetail.d.ts +87 -0
- package/lib/views/mcp/RunDetail.js +186 -0
- package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
- package/lib/views/mcp/ScimProvisioning.js +173 -0
- package/lib/views/mcp/ServiceAgents.d.ts +46 -0
- package/lib/views/mcp/ServiceAgents.js +194 -0
- package/lib/views/mcp/TeamPolicies.d.ts +33 -0
- package/lib/views/mcp/TeamPolicies.js +159 -0
- package/lib/views/mcp/ToolAccess.d.ts +60 -0
- package/lib/views/mcp/ToolAccess.js +101 -0
- package/lib/views/mcp/TraceTimeline.d.ts +81 -0
- package/lib/views/mcp/TraceTimeline.js +132 -0
- package/lib/views/mcp/format.d.ts +25 -0
- package/lib/views/mcp/format.js +77 -0
- package/lib/views/mcp/index.d.ts +34 -0
- package/lib/views/mcp/index.js +42 -0
- package/lib/views/mcp/types.d.ts +66 -0
- package/lib/views/mcp/types.js +5 -0
- package/lib/views/otel/simpleAuthStore.js +3 -0
- package/lib/views/secrets/SecretEdit.d.ts +5 -1
- package/lib/views/secrets/SecretEdit.js +5 -4
- package/lib/views/secrets/SecretNew.d.ts +11 -1
- package/lib/views/secrets/SecretNew.js +7 -3
- package/lib/views/secrets/Secrets.d.ts +12 -1
- package/lib/views/secrets/Secrets.js +2 -2
- package/package.json +269 -260
- package/scripts/generate-contents-types.py +169 -0
- package/scripts/generate-mcp-types.py +233 -0
- package/scripts/generate-orchestration-types.py +920 -0
- package/lib/api/iam/datasources.d.ts +0 -86
- package/lib/api/iam/datasources.js +0 -185
- package/lib/components/toolbars/index.d.ts +0 -1
- package/lib/models/Datasource.d.ts +0 -181
- package/lib/models/Datasource.js +0 -154
- package/lib/state/substates/DatasourceState.d.ts +0 -11
- package/lib/state/substates/DatasourceState.js +0 -16
- /package/lib/{components/toolbars/index.js → api/contents/generated.js} +0 -0
|
@@ -101,7 +101,7 @@ const SignInInputGlobalStyle = createGlobalStyle `
|
|
|
101
101
|
}
|
|
102
102
|
`;
|
|
103
103
|
// ── Component ────────────────────────────────────────────────────────
|
|
104
|
-
export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp, name, title = 'Datalayer OTEL', description = 'Sign in to access the observability dashboard.', icon, leadingIcon = _jsx(TelescopeIcon, { size: 24 }), github = false, google = false, linkedin = false, apiKey = false, signUp = true, socialSignInNavigationTarget, asDoc = false, hideHero = false, calloutTitle, calloutDescription, passwordToggleLabel = 'Sign in with a password', signUpTitle = "Don't have an account?", signUpDescription = 'Create a free Datalayer account with your email address.', signUpLabel = 'Sign up with email', signUpHref = 'https://datalayer.ai/signup', forgotPasswordHref = '/password', forgotPasswordLabel = 'Forgot password?', showForgotPassword = true, socialButtonMarginTop = 3, actionButtonMaxWidth = 320, }) => {
|
|
104
|
+
export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp, name, title = 'Datalayer OTEL', description = 'Sign in to access the observability dashboard.', icon, leadingIcon = _jsx(TelescopeIcon, { size: 24 }), github = false, google = false, linkedin = false, apiKey = false, signUp = true, socialSignInNavigationTarget, asDoc = false, fillHeight = true, hideHero = false, calloutTitle, calloutDescription, passwordToggleLabel = 'Sign in with a password', signUpTitle = "Don't have an account?", signUpDescription = 'Create a free Datalayer account with your email address.', signUpLabel = 'Sign up with email', signUpHref = 'https://datalayer.ai/signup', forgotPasswordHref = '/password', forgotPasswordLabel = 'Forgot password?', showForgotPassword = true, socialButtonMarginTop = 3, actionButtonMaxWidth = 320, }) => {
|
|
105
105
|
const compactDocMode = asDoc && !hideHero;
|
|
106
106
|
const headingText = name ?? (asDoc && title === 'Datalayer OTEL' ? 'Datalayer Sign In' : title);
|
|
107
107
|
const headingIcon = icon ?? (asDoc ? _jsx(SignInIcon, { size: 24 }) : leadingIcon);
|
|
@@ -316,9 +316,9 @@ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp,
|
|
|
316
316
|
const socialDisabled = socialLoading || loading || asDoc;
|
|
317
317
|
return (_jsxs(Box, { className: "signin-input-theme-scope", sx: {
|
|
318
318
|
display: 'flex',
|
|
319
|
-
alignItems: hideHero || compactDocMode ? 'flex-start' : 'center',
|
|
319
|
+
alignItems: hideHero || compactDocMode || !fillHeight ? 'flex-start' : 'center',
|
|
320
320
|
justifyContent: 'center',
|
|
321
|
-
minHeight: hideHero || compactDocMode ? 'auto' : '100vh',
|
|
321
|
+
minHeight: hideHero || compactDocMode || !fillHeight ? 'auto' : '100vh',
|
|
322
322
|
bg: hideHero ? 'transparent' : 'canvas.default',
|
|
323
323
|
color: 'fg.default',
|
|
324
324
|
py: hideHero ? 0 : compactDocMode ? 2 : 4,
|
|
@@ -407,7 +407,7 @@ export const SignInSimple = ({ onSignIn, onApiKeySignIn, loginUrl: loginUrlProp,
|
|
|
407
407
|
bg: 'canvas.overlay',
|
|
408
408
|
color: 'fg.default',
|
|
409
409
|
boxShadow: 'shadow.large',
|
|
410
|
-
}, children: [_jsx(Heading, { id: "signin-api-key-title", sx: { fontSize: 2, mb: 2 }, children: "Enter your API Key" }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "API Key" }), _jsx(Textarea, { block: true, required: true, autoFocus: true, placeholder: "Paste your API key here", value: apiKeyValue, onInput:
|
|
410
|
+
}, children: [_jsx(Heading, { id: "signin-api-key-title", sx: { fontSize: 2, mb: 2 }, children: "Enter your API Key" }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "API Key" }), _jsx(Textarea, { block: true, required: true, autoFocus: true, placeholder: "Paste your API key here", value: apiKeyValue, onInput: e => setApiKeyValue(e.currentTarget.value), ref: apiKeyRef })] }), _jsxs(Box, { sx: {
|
|
411
411
|
mt: 3,
|
|
412
412
|
display: 'flex',
|
|
413
413
|
justifyContent: 'flex-end',
|
package/lib/views/index.d.ts
CHANGED
package/lib/views/index.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the admitted-clients list actually admits.
|
|
3
|
+
*
|
|
4
|
+
* The field above it is a textarea of client ids, because that is what they
|
|
5
|
+
* are: a client id is a URL somebody pastes, and there is nothing to pick
|
|
6
|
+
* from. What a textarea cannot do is tell whoever typed it whether an entry
|
|
7
|
+
* resolves — and this is the one policy rule whose typo fails silently in
|
|
8
|
+
* the direction that matters. A misspelled entry does not refuse anything
|
|
9
|
+
* loudly; it never matches, and every agent from that vendor is turned away
|
|
10
|
+
* with "your organization does not admit this client", which reads to its
|
|
11
|
+
* owner as a decision somebody made on purpose.
|
|
12
|
+
*
|
|
13
|
+
* So each stored entry is shown with what it names. Three states, and the
|
|
14
|
+
* reason this is a component rather than a line of text is that collapsing
|
|
15
|
+
* any two of them misleads:
|
|
16
|
+
*
|
|
17
|
+
* * a **hostname** admits every client that host publishes. There is no
|
|
18
|
+
* document and none is expected — shown as unresolved it would send
|
|
19
|
+
* somebody chasing a fetch that was never going to happen.
|
|
20
|
+
* * a **document that resolves**, with the name and publishing host.
|
|
21
|
+
* * a **document that does not**, with the reason. The state the whole
|
|
22
|
+
* thing is for.
|
|
23
|
+
*
|
|
24
|
+
* **It describes what is stored, not the draft.** Resolving on every
|
|
25
|
+
* keystroke would make a textarea a source of outbound requests, so this
|
|
26
|
+
* goes stale while somebody edits and says so rather than pretending to
|
|
27
|
+
* describe a line that has not been saved.
|
|
28
|
+
*
|
|
29
|
+
* @module views/mcp/AdmittedClients
|
|
30
|
+
*/
|
|
31
|
+
import type { JSX } from 'react';
|
|
32
|
+
import type { McpPolicyScope } from '../../api/iam/mcpPolicy';
|
|
33
|
+
export interface AdmittedClientsProps {
|
|
34
|
+
scope: McpPolicyScope;
|
|
35
|
+
subjectUid: string;
|
|
36
|
+
/**
|
|
37
|
+
* Whether the textarea above differs from what is stored. The entries
|
|
38
|
+
* below describe the *stored* list, and saying so is the difference
|
|
39
|
+
* between a stale panel and a wrong one.
|
|
40
|
+
*/
|
|
41
|
+
edited?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare const AdmittedClients: ({ scope, subjectUid, edited, }: AdmittedClientsProps) => JSX.Element | null;
|
|
44
|
+
export default AdmittedClients;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Label, Link, Spinner, Text } from '@primer/react';
|
|
3
|
+
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { AlertIcon, CheckCircleIcon, GlobeIcon, QuestionIcon, } from '@primer/octicons-react';
|
|
5
|
+
import { useAdmittedClients } from '../../hooks/useMcp.js';
|
|
6
|
+
const Row = ({ client }) => {
|
|
7
|
+
const unreadable = client.kind === 'url' && client.resolved === false;
|
|
8
|
+
const unusable = client.kind === 'unusable';
|
|
9
|
+
const icon = unusable ? (_jsx(QuestionIcon, {})) : unreadable ? (_jsx(AlertIcon, {})) : client.kind === 'hostname' ? (_jsx(GlobeIcon, {})) : (_jsx(CheckCircleIcon, {}));
|
|
10
|
+
const tone = unusable || unreadable ? 'danger.fg' : 'fg.muted';
|
|
11
|
+
return (_jsxs(Box, { sx: {
|
|
12
|
+
display: 'grid',
|
|
13
|
+
gridTemplateColumns: 'auto 1fr',
|
|
14
|
+
gap: 2,
|
|
15
|
+
alignItems: 'start',
|
|
16
|
+
py: 2,
|
|
17
|
+
borderTop: '1px solid',
|
|
18
|
+
borderColor: 'border.muted',
|
|
19
|
+
}, children: [_jsx(Box, { sx: { color: tone, pt: '2px' }, children: icon }), _jsxs(Box, { sx: { display: 'grid', gap: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
gap: 2,
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
flexWrap: 'wrap',
|
|
24
|
+
}, children: [client.clientName && (_jsx(Text, { sx: { fontWeight: 'semibold' }, children: client.clientName })), client.kind === 'hostname' && (_jsx(Label, { variant: "accent", size: "small", children: "Whole host" })), client.localhostOnly && (
|
|
25
|
+
// Worth a label of its own: every redirect goes to the reader's
|
|
26
|
+
// own machine, and a document cannot prove which local program
|
|
27
|
+
// is listening on the port.
|
|
28
|
+
_jsx(Label, { variant: "attention", size: "small", children: "Redirects to localhost" })), unreadable && (_jsx(Label, { variant: "danger", size: "small", children: "Cannot be read" })), unusable && (_jsx(Label, { variant: "danger", size: "small", children: "Matches nothing" }))] }), _jsx(Text, { sx: {
|
|
29
|
+
fontSize: 0,
|
|
30
|
+
fontFamily: 'mono',
|
|
31
|
+
color: 'fg.muted',
|
|
32
|
+
overflowWrap: 'anywhere',
|
|
33
|
+
}, children: client.entry }), client.detail && (_jsx(Text, { sx: { fontSize: 0, color: tone }, children: client.detail })), client.clientUri && (_jsx(Link, { href: client.clientUri, target: "_blank", rel: "noreferrer noopener", sx: { fontSize: 0 }, children: client.clientHostname || client.clientUri }))] })] }));
|
|
34
|
+
};
|
|
35
|
+
export const AdmittedClients = ({ scope, subjectUid, edited = false, }) => {
|
|
36
|
+
const described = useAdmittedClients(scope, subjectUid);
|
|
37
|
+
if (described.isPending && !described.data) {
|
|
38
|
+
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 3 }, children: _jsx(Spinner, { size: "small" }) }));
|
|
39
|
+
}
|
|
40
|
+
// An error here is not worth a blankslate: the field above still works and
|
|
41
|
+
// the policy is unaffected. Saying nothing would be worse than saying it
|
|
42
|
+
// could not be checked, so it says that and stays out of the way.
|
|
43
|
+
if (described.isError) {
|
|
44
|
+
return (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "The admitted clients could not be described just now. The list above is still what is enforced." }));
|
|
45
|
+
}
|
|
46
|
+
const answer = described.data;
|
|
47
|
+
if (!answer) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (!answer.allowlisted) {
|
|
51
|
+
// Said, not rendered as an empty list. An empty allowlist is not an
|
|
52
|
+
// allowlist, and "no clients admitted" would be the opposite of what
|
|
53
|
+
// the gateway does with a blank one.
|
|
54
|
+
return (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: answer.detail ??
|
|
55
|
+
'This layer names no client allowlist, so every client is admitted by it.' }));
|
|
56
|
+
}
|
|
57
|
+
return (_jsxs(Box, { sx: { display: 'grid', gap: 1 }, children: [edited && (_jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'attention.fg', m: 0 }, children: ["Describing the ", _jsx("strong", { children: "saved" }), " list. Save to check what you have just typed."] })), answer.clients.map(client => (_jsx(Row, { client: client }, client.entry)))] }));
|
|
58
|
+
};
|
|
59
|
+
export default AdmittedClients;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a fired alert goes, besides the notice in the app.
|
|
3
|
+
*
|
|
4
|
+
* These sit beside the rules because they are half of the same decision: a
|
|
5
|
+
* rule with nowhere to go is a row somebody has to remember to look at, and
|
|
6
|
+
* a destination with no rules is nothing at all.
|
|
7
|
+
*
|
|
8
|
+
* They live on the organization rather than on each rule, and that is worth
|
|
9
|
+
* knowing while editing them: a URL repeated on twenty rules is nineteen
|
|
10
|
+
* places to forget when it rotates, and the rules would disagree about where
|
|
11
|
+
* the organization is reachable.
|
|
12
|
+
*
|
|
13
|
+
* **Slack has its own field.** Not fussiness — the transport is the same and
|
|
14
|
+
* the body is not. Slack renders a generic JSON POST as one grey line of
|
|
15
|
+
* text; given Block Kit it renders a coloured attachment with the reading,
|
|
16
|
+
* the severity and the scope as separate fields. IAM refuses a
|
|
17
|
+
* non-Slack URL here, because anything else sent Block Kit half-works and
|
|
18
|
+
* looks configured.
|
|
19
|
+
*
|
|
20
|
+
* Only what changed is sent. IAM merges these settings, and this form does
|
|
21
|
+
* not show retention or SIEM forwarding — sending them empty would clear
|
|
22
|
+
* decisions made on another page by somebody else.
|
|
23
|
+
*
|
|
24
|
+
* @module views/mcp/AlertDestinations
|
|
25
|
+
*/
|
|
26
|
+
import type { JSX } from 'react';
|
|
27
|
+
import type { McpErrorStateFn } from './types';
|
|
28
|
+
export interface AlertDestinationsProps {
|
|
29
|
+
errorState: McpErrorStateFn;
|
|
30
|
+
orgUid: string;
|
|
31
|
+
readOnly?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare const AlertDestinations: ({ errorState, orgUid, readOnly, }: AlertDestinationsProps) => JSX.Element;
|
|
34
|
+
export default AlertDestinations;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { Button, Flash, FormControl, Heading, Spinner, Text, TextInput, } from '@primer/react';
|
|
4
|
+
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
6
|
+
import { useAuditSettings, useSetAuditSettings } from '../../hooks/useMcp.js';
|
|
7
|
+
import { useToast } from '../../hooks/index.js';
|
|
8
|
+
import { AuditSettingInvalid } from '../../api/iam/mcpAuditSettings.js';
|
|
9
|
+
const EMPTY = {
|
|
10
|
+
alertEmails: '',
|
|
11
|
+
alertWebhookUrl: '',
|
|
12
|
+
alertSlackWebhookUrl: '',
|
|
13
|
+
};
|
|
14
|
+
export const AlertDestinations = ({ errorState, orgUid, readOnly = false, }) => {
|
|
15
|
+
const { enqueueToast } = useToast();
|
|
16
|
+
const settings = useAuditSettings(orgUid);
|
|
17
|
+
const save = useSetAuditSettings(orgUid);
|
|
18
|
+
const [draft, setDraft] = useState(EMPTY);
|
|
19
|
+
const [refusal, setRefusal] = useState('');
|
|
20
|
+
const stored = useMemo(() => ({
|
|
21
|
+
alertEmails: settings.data?.alertEmails ?? '',
|
|
22
|
+
alertWebhookUrl: settings.data?.alertWebhookUrl ?? '',
|
|
23
|
+
alertSlackWebhookUrl: settings.data?.alertSlackWebhookUrl ?? '',
|
|
24
|
+
}), [settings.data]);
|
|
25
|
+
useEffect(() => setDraft(stored), [stored]);
|
|
26
|
+
const changed = useMemo(() => Object.keys(EMPTY).some(key => draft[key] !== stored[key]), [draft, stored]);
|
|
27
|
+
const set = (key, value) => setDraft(current => ({ ...current, [key]: value }));
|
|
28
|
+
const apply = () => {
|
|
29
|
+
setRefusal('');
|
|
30
|
+
// Only the fields this form owns, and only those that changed. Sending
|
|
31
|
+
// an untouched retention as empty would clear a decision made on
|
|
32
|
+
// another page by somebody else.
|
|
33
|
+
const changes = {};
|
|
34
|
+
for (const key of Object.keys(EMPTY)) {
|
|
35
|
+
if (draft[key] !== stored[key]) {
|
|
36
|
+
changes[key] = draft[key].trim();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
save.mutate({ settings: changes, expectedVersion: settings.data?.version }, {
|
|
40
|
+
onSuccess: () => {
|
|
41
|
+
enqueueToast('Saved. The next firing goes there.', {
|
|
42
|
+
variant: 'success',
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
onError: error => {
|
|
46
|
+
if (error instanceof AuditSettingInvalid) {
|
|
47
|
+
// IAM's own words. "That is not a Slack incoming webhook" is the
|
|
48
|
+
// whole answer, and a generic failure would send somebody
|
|
49
|
+
// looking for a problem that is already named.
|
|
50
|
+
setRefusal(error.message);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
enqueueToast(`Could not save: ${error.message}`, {
|
|
54
|
+
variant: 'error',
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
if (settings.isError) {
|
|
60
|
+
return (_jsx(McpErrorBlankslate, { state: errorState(settings.error, 'Alert destinations'), onRetry: () => settings.refetch() }));
|
|
61
|
+
}
|
|
62
|
+
if (settings.isPending && settings.data === undefined) {
|
|
63
|
+
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 4 }, children: _jsx(Spinner, {}) }));
|
|
64
|
+
}
|
|
65
|
+
return (_jsxs(Box, { sx: {
|
|
66
|
+
display: 'grid',
|
|
67
|
+
gap: 3,
|
|
68
|
+
maxWidth: '42rem',
|
|
69
|
+
p: 3,
|
|
70
|
+
border: '1px solid',
|
|
71
|
+
borderColor: 'border.default',
|
|
72
|
+
borderRadius: 2,
|
|
73
|
+
}, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: "Where alerts go" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Set once for the organization, not per rule \u2014 a URL repeated on twenty rules is nineteen places to forget when it rotates. Every firing also appears in the app, and that copy is sent first so it is not lost when one of these is down." })] }), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Email" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertEmails, onChange: event => set('alertEmails', event.target.value), placeholder: "ops@example.co, oncall@example.co" }), _jsx(FormControl.Caption, { children: "Comma-separated, up to twenty. An alert list is the people who want to know, not a mailing list \u2014 a firing that emails two hundred people is a firing that gets the rule switched off." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Slack" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertSlackWebhookUrl, onChange: event => set('alertSlackWebhookUrl', event.target.value), placeholder: "https://hooks.slack.com/services/\u2026" }), _jsx(FormControl.Caption, { children: "A Slack incoming webhook, and only Slack\u2019s \u2014 anything else belongs in the field below. Through this one Slack gets a coloured attachment with the reading, the severity and the scope as separate fields; through the generic webhook it shows one grey line." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Webhook" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertWebhookUrl, onChange: event => set('alertWebhookUrl', event.target.value), placeholder: "https://events.pagerduty.com/\u2026" }), _jsxs(FormControl.Caption, { children: ["Anything that takes a JSON POST: PagerDuty, a chat relay, your own endpoint. ", _jsx("strong", { children: "https only" }), " \u2014 an alert names your organization and what its agents did. It carries no credential."] })] }), !readOnly && (_jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save destinations" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" }))] })), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Each copy is attempted on its own: if the webhook is down you still get the email. A copy that fails is recorded and", ' ', _jsx("strong", { children: "not retried" }), " \u2014 retrying a notice against an endpoint that is down turns its outage into a loop against it, and the alert is already in the app."] })] }));
|
|
74
|
+
};
|
|
75
|
+
export default AlertDestinations;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What this organization asked to be told about.
|
|
3
|
+
*
|
|
4
|
+
* A rule is a condition over a number the gateway already counts, checked
|
|
5
|
+
* once a minute, firing at most once per condition per window. The
|
|
6
|
+
* evaluator has been able to evaluate these since milestone 1; until this
|
|
7
|
+
* page they could only be written with `curl`.
|
|
8
|
+
*
|
|
9
|
+
* The form is built from the vocabulary the evaluator publishes, and that
|
|
10
|
+
* is the point rather than a convenience. A rule it cannot evaluate is
|
|
11
|
+
* refused at the write — and a rule that never fires because of a typo is
|
|
12
|
+
* indistinguishable from a condition that never happened, which is what
|
|
13
|
+
* somebody will believe. A dropdown cannot produce one.
|
|
14
|
+
*
|
|
15
|
+
* The same reasoning runs one level deeper. A condition the evaluator knows
|
|
16
|
+
* but has **no reader** for is stored, evaluated every minute, counted
|
|
17
|
+
* `unreadable`, and never fires — worse than a typo, because it looks
|
|
18
|
+
* configured. Those are shown as not yet measurable and cannot be chosen.
|
|
19
|
+
*
|
|
20
|
+
* @module views/mcp/AlertRules
|
|
21
|
+
*/
|
|
22
|
+
import type { JSX } from 'react';
|
|
23
|
+
import type { McpErrorStateFn } from './types';
|
|
24
|
+
export interface AlertRulesProps {
|
|
25
|
+
errorState: McpErrorStateFn;
|
|
26
|
+
orgUid: string;
|
|
27
|
+
/** Read-only for anybody who is not an owner. */
|
|
28
|
+
readOnly?: boolean;
|
|
29
|
+
showTitle?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare const AlertRules: ({ errorState, orgUid, readOnly, showTitle, }: AlertRulesProps) => JSX.Element;
|
|
32
|
+
export default AlertRules;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { ActionList, ActionMenu, Button, Flash, FormControl, Heading, Label, Select, Spinner, Text, TextInput, ToggleSwitch, } from '@primer/react';
|
|
4
|
+
import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
|
|
5
|
+
import { Box } from '@datalayer/primer-addons';
|
|
6
|
+
import { BellIcon, KebabHorizontalIcon } from '@primer/octicons-react';
|
|
7
|
+
import { McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
8
|
+
import { AlertDestinations } from './AlertDestinations.js';
|
|
9
|
+
import { useAlertRules, useTestAlertRule, useCreateAlertRule, useDeleteAlertRule, useUpdateAlertRule, } from '../../hooks/useMcp.js';
|
|
10
|
+
import { useToast } from '../../hooks/index.js';
|
|
11
|
+
import { ALERT_CONDITIONS, ALERT_OPERATORS, ALERT_SEVERITIES, AlertRuleInvalid, } from '../../api/iam/mcpAlertRules.js';
|
|
12
|
+
/** The windows worth offering, in seconds. */
|
|
13
|
+
const WINDOWS = [
|
|
14
|
+
{ seconds: 300, label: '5 minutes' },
|
|
15
|
+
{ seconds: 900, label: '15 minutes' },
|
|
16
|
+
{ seconds: 3600, label: 'an hour' },
|
|
17
|
+
{ seconds: 21600, label: '6 hours' },
|
|
18
|
+
{ seconds: 86400, label: 'a day' },
|
|
19
|
+
];
|
|
20
|
+
const MEASURABLE = ALERT_CONDITIONS.filter(condition => condition.measurable);
|
|
21
|
+
const BLANK = {
|
|
22
|
+
condition: MEASURABLE[0]?.name ?? 'tasks.open',
|
|
23
|
+
operator: 'gt',
|
|
24
|
+
threshold: 1,
|
|
25
|
+
severity: 'warning',
|
|
26
|
+
windowSeconds: 3600,
|
|
27
|
+
scopeKind: 'organization',
|
|
28
|
+
scopeUid: '',
|
|
29
|
+
enabled: true,
|
|
30
|
+
};
|
|
31
|
+
const labelOf = (condition) => ALERT_CONDITIONS.find(entry => entry.name === condition)?.label ?? condition;
|
|
32
|
+
const operatorOf = (operator) => ALERT_OPERATORS.find(entry => entry.name === operator)?.label ?? operator;
|
|
33
|
+
const windowOf = (seconds) => WINDOWS.find(entry => entry.seconds === seconds)?.label ?? `${seconds}s`;
|
|
34
|
+
export const AlertRules = ({ errorState, orgUid, readOnly = false, showTitle = true, }) => {
|
|
35
|
+
const { enqueueToast } = useToast();
|
|
36
|
+
const rules = useAlertRules(orgUid);
|
|
37
|
+
const create = useCreateAlertRule(orgUid);
|
|
38
|
+
const update = useUpdateAlertRule(orgUid);
|
|
39
|
+
const remove = useDeleteAlertRule(orgUid);
|
|
40
|
+
const test = useTestAlertRule();
|
|
41
|
+
const [editing, setEditing] = useState(null);
|
|
42
|
+
const [creating, setCreating] = useState(false);
|
|
43
|
+
const [draft, setDraft] = useState(BLANK);
|
|
44
|
+
const [refusal, setRefusal] = useState('');
|
|
45
|
+
const [trial, setTrial] = useState(null);
|
|
46
|
+
const [removing, setRemoving] = useState(null);
|
|
47
|
+
// Primer 37 types Dialog's focus refs with React 18's non-nullable
|
|
48
|
+
// `RefObject<HTMLElement>`. React 19's `useRef(null)` is nullable, and this
|
|
49
|
+
// ref is only handed to Dialog, so narrow it once here.
|
|
50
|
+
const returnFocusRef = useRef(null);
|
|
51
|
+
const rows = useMemo(() => (rules.data ?? []).map(rule => ({ ...rule, id: rule.uid })), [rules.data]);
|
|
52
|
+
const openCreate = () => {
|
|
53
|
+
setDraft(BLANK);
|
|
54
|
+
setRefusal('');
|
|
55
|
+
setTrial(null);
|
|
56
|
+
setCreating(true);
|
|
57
|
+
};
|
|
58
|
+
const openEdit = (rule) => {
|
|
59
|
+
const { uid, orgUid: _org, version: _version, ...rest } = rule;
|
|
60
|
+
setDraft(rest);
|
|
61
|
+
setRefusal('');
|
|
62
|
+
setTrial(null);
|
|
63
|
+
setEditing(rule);
|
|
64
|
+
};
|
|
65
|
+
const close = () => {
|
|
66
|
+
setCreating(false);
|
|
67
|
+
setEditing(null);
|
|
68
|
+
setRefusal('');
|
|
69
|
+
};
|
|
70
|
+
const onError = (error) => {
|
|
71
|
+
if (error instanceof AlertRuleInvalid) {
|
|
72
|
+
// The evaluator's own words, beside the form rather than in a toast:
|
|
73
|
+
// it names what is wrong, and the person can still fix it.
|
|
74
|
+
setRefusal(error.message);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
enqueueToast(`Could not save the rule: ${error.message}`, {
|
|
78
|
+
variant: 'error',
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const apply = () => {
|
|
82
|
+
setRefusal('');
|
|
83
|
+
if (draft.scopeKind !== 'organization' && !draft.scopeUid.trim()) {
|
|
84
|
+
setRefusal(`A ${draft.scopeKind} rule needs the ${draft.scopeKind} it is about. ` +
|
|
85
|
+
'Without one it would be measured over the whole organization ' +
|
|
86
|
+
'under a name saying otherwise.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!Number.isFinite(draft.threshold)) {
|
|
90
|
+
setRefusal('The threshold must be a number.');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (editing) {
|
|
94
|
+
update.mutate({ uid: editing.uid, rule: draft }, {
|
|
95
|
+
onSuccess: () => {
|
|
96
|
+
close();
|
|
97
|
+
enqueueToast('Rule saved.', { variant: 'success' });
|
|
98
|
+
},
|
|
99
|
+
onError,
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
create.mutate(draft, {
|
|
104
|
+
onSuccess: () => {
|
|
105
|
+
close();
|
|
106
|
+
enqueueToast('Rule created. It is evaluated on the next tick.', {
|
|
107
|
+
variant: 'success',
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
onError,
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
/** Switching a rule off leaves it here; it stops being evaluated. */
|
|
114
|
+
const toggle = (rule, enabled) => {
|
|
115
|
+
const { uid, orgUid: _org, version: _version, ...rest } = rule;
|
|
116
|
+
update.mutate({ uid, rule: { ...rest, enabled } }, {
|
|
117
|
+
onError: error => enqueueToast(`Could not change the rule: ${error.message}`, {
|
|
118
|
+
variant: 'error',
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
const confirmRemove = () => {
|
|
123
|
+
const rule = removing;
|
|
124
|
+
if (!rule) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
remove.mutate(rule.uid, {
|
|
128
|
+
onSuccess: () => {
|
|
129
|
+
setRemoving(null);
|
|
130
|
+
enqueueToast('Rule removed. What it watched is unwatched.', {
|
|
131
|
+
variant: 'success',
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
onError: error => {
|
|
135
|
+
setRemoving(null);
|
|
136
|
+
enqueueToast(`Could not remove: ${error.message}`, {
|
|
137
|
+
variant: 'error',
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const set = (key, value) => {
|
|
143
|
+
// A reading belongs to the rule that produced it. Left on screen after
|
|
144
|
+
// the condition changes, it would be somebody reading last question's
|
|
145
|
+
// answer as this question's.
|
|
146
|
+
setTrial(null);
|
|
147
|
+
setDraft(current => ({ ...current, [key]: value }));
|
|
148
|
+
};
|
|
149
|
+
const tryIt = () => {
|
|
150
|
+
setRefusal('');
|
|
151
|
+
test.mutate({
|
|
152
|
+
condition: draft.condition,
|
|
153
|
+
operator: draft.operator,
|
|
154
|
+
threshold: draft.threshold,
|
|
155
|
+
windowSeconds: draft.windowSeconds,
|
|
156
|
+
scopeKind: draft.scopeKind,
|
|
157
|
+
scopeUid: draft.scopeUid,
|
|
158
|
+
}, {
|
|
159
|
+
onSuccess: setTrial,
|
|
160
|
+
onError: error => setRefusal(error.message),
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
const columns = [
|
|
164
|
+
{
|
|
165
|
+
header: 'Tell me when',
|
|
166
|
+
id: 'condition',
|
|
167
|
+
rowHeader: true,
|
|
168
|
+
width: 'growCollapse',
|
|
169
|
+
renderCell: row => (_jsxs(Box, { sx: { display: 'grid' }, children: [_jsxs(Text, { sx: { fontSize: 1 }, children: [labelOf(row.condition), " ", operatorOf(row.operator), " ", row.threshold] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["over ", windowOf(row.windowSeconds), row.scopeKind !== 'organization' && ` · this ${row.scopeKind}`] })] })),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
header: 'Severity',
|
|
173
|
+
id: 'severity',
|
|
174
|
+
width: '110px',
|
|
175
|
+
renderCell: row => (_jsx(Label, { size: "small", variant: row.severity === 'critical'
|
|
176
|
+
? 'danger'
|
|
177
|
+
: row.severity === 'warning'
|
|
178
|
+
? 'attention'
|
|
179
|
+
: 'secondary', children: row.severity })),
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
header: 'On',
|
|
183
|
+
id: 'enabled',
|
|
184
|
+
width: '90px',
|
|
185
|
+
renderCell: row => (
|
|
186
|
+
// The label is drawn and hidden rather than passed as `aria-label`:
|
|
187
|
+
// Primer's switch names itself from an element, and a row of
|
|
188
|
+
// unlabelled switches is a row a screen reader reads as "on, on,
|
|
189
|
+
// off" with nothing said about what.
|
|
190
|
+
_jsxs(_Fragment, { children: [_jsx(Text, { id: `alert-toggle-${row.uid}`, sx: { display: 'none' }, children: `Evaluate ${labelOf(row.condition)}` }), _jsx(ToggleSwitch, { size: "small", checked: row.enabled, disabled: readOnly || update.isPending, "aria-labelledby": `alert-toggle-${row.uid}`, onClick: () => toggle(row, !row.enabled) })] })),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
header: '',
|
|
194
|
+
id: 'actions',
|
|
195
|
+
width: '48px',
|
|
196
|
+
align: 'end',
|
|
197
|
+
renderCell: row => readOnly ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "\u2014" })) : (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { variant: "invisible", size: "small", "aria-label": `Actions for ${labelOf(row.condition)}`, icon: KebabHorizontalIcon }) }), _jsx(ActionMenu.Overlay, { align: "end", children: _jsxs(ActionList, { children: [_jsx(ActionList.Item, { onSelect: () => openEdit(row), children: "Edit" }), _jsx(ActionList.Item, { variant: "danger", onSelect: () => setRemoving(row), children: "Remove" })] }) })] })),
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
if (rules.isError) {
|
|
201
|
+
return (_jsx(McpErrorBlankslate, { state: errorState(rules.error, 'Alert rules'), onRetry: () => rules.refetch() }));
|
|
202
|
+
}
|
|
203
|
+
const chosen = ALERT_CONDITIONS.find(entry => entry.name === draft.condition);
|
|
204
|
+
return (_jsxs(Box, { sx: { display: 'grid', gap: 3, minWidth: 0 }, children: [showTitle && (_jsxs(Box, { sx: {
|
|
205
|
+
display: 'flex',
|
|
206
|
+
alignItems: 'start',
|
|
207
|
+
justifyContent: 'space-between',
|
|
208
|
+
gap: 3,
|
|
209
|
+
}, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Alerts" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "What this organization asked to be told about. Each rule is checked once a minute and fires at most once per window." })] }), !readOnly && (_jsx(Button, { variant: "primary", onClick: openCreate, children: "New rule" }))] })), rules.isPending && !rules.data ? (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 5 }, children: _jsx(Spinner, {}) })) : rows.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "alert-rules", children: "Rules" }), _jsx(Table.Subtitle, { as: "p", id: "alert-rules-subtitle", children: "A rule switched off stays here and stops being evaluated." }), _jsx(DataTable, { "aria-labelledby": "alert-rules", "aria-describedby": "alert-rules-subtitle", data: rows, columns: columns })] })) : (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(BellIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "Nothing is watched" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Runs are recorded either way. A rule is what turns a number somebody would have to go and look at into something that reaches you." }) }), !readOnly && (_jsx(Button, { size: "small", onClick: openCreate, children: "New rule" }))] })), _jsx(AlertDestinations, { errorState: errorState, orgUid: orgUid, readOnly: readOnly }), (creating || editing) && (_jsx(Dialog, { title: editing ? 'Edit rule' : 'New alert rule', onClose: close, returnFocusRef: returnFocusRef, footerButtons: [
|
|
210
|
+
{ buttonType: 'default', content: 'Cancel', onClick: close },
|
|
211
|
+
{
|
|
212
|
+
buttonType: 'default',
|
|
213
|
+
content: test.isPending ? 'Testing…' : 'Test',
|
|
214
|
+
onClick: tryIt,
|
|
215
|
+
disabled: test.isPending || !draft.condition,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
buttonType: 'primary',
|
|
219
|
+
content: editing ? 'Save' : 'Create',
|
|
220
|
+
onClick: apply,
|
|
221
|
+
disabled: create.isPending || update.isPending,
|
|
222
|
+
},
|
|
223
|
+
], children: _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), trial && (_jsxs(Flash, { variant: trial.readable ? 'default' : 'warning', children: [_jsx(Text, { sx: { fontSize: 1 }, children: trial.readable
|
|
224
|
+
? `Right now it reads ${trial.value}, so this rule ${trial.wouldFire ? 'would fire' : 'would not fire'}.`
|
|
225
|
+
: 'This cannot be read at the moment, so the rule would ' +
|
|
226
|
+
'not fire — and a rule that never fires looks exactly ' +
|
|
227
|
+
'like a condition that never happens.' }), trial.detail && (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 1, mb: 0 }, children: trial.detail }))] })), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Tell me when" }), _jsx(Select, { value: draft.condition, onChange: event => set('condition', event.target.value), children: ALERT_CONDITIONS.map(condition => (_jsxs(Select.Option, { value: condition.name,
|
|
228
|
+
// A condition nothing reads would be stored, evaluated
|
|
229
|
+
// every minute and never fire. Offering it as though it
|
|
230
|
+
// worked is the silence rules exist to break.
|
|
231
|
+
disabled: !condition.measurable, children: [condition.label, condition.measurable ? '' : ' — not measurable yet'] }, condition.name))) }), chosen && (_jsx(FormControl.Caption, { children: chosen.help }))] }), _jsxs(Box, { sx: { display: 'grid', gap: 2, gridTemplateColumns: '1fr 1fr' }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Comparison" }), _jsx(Select, { value: draft.operator, onChange: event => set('operator', event.target.value), children: ALERT_OPERATORS.map(operator => (_jsx(Select.Option, { value: operator.name, children: operator.label }, operator.name))) })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Threshold" }), _jsx(TextInput, { block: true, type: "number", value: String(draft.threshold), onChange: event => set('threshold', Number(event.target.value)) })] })] }), _jsxs(Box, { sx: { display: 'grid', gap: 2, gridTemplateColumns: '1fr 1fr' }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Over" }), _jsx(Select, { value: String(draft.windowSeconds), onChange: event => set('windowSeconds', Number(event.target.value)), children: WINDOWS.map(window => (_jsx(Select.Option, { value: String(window.seconds), children: window.label }, window.seconds))) }), _jsx(FormControl.Caption, { children: "It fires once per window, not once per check." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Severity" }), _jsx(Select, { value: draft.severity, onChange: event => set('severity', event.target.value), children: ALERT_SEVERITIES.map(severity => (_jsx(Select.Option, { value: severity, children: severity }, severity))) }), _jsx(FormControl.Caption, { children: "Sets the colour in Slack and the wording of the notice." })] })] }), _jsxs(Box, { sx: { display: 'grid', gap: 2, gridTemplateColumns: '1fr 1fr' }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Measured over" }), _jsxs(Select, { value: draft.scopeKind, onChange: event => set('scopeKind', event.target.value), children: [_jsx(Select.Option, { value: "organization", children: "The whole organization" }), _jsx(Select.Option, { value: "team", children: "One team" }), _jsx(Select.Option, { value: "user", children: "One person" }), _jsx(Select.Option, { value: "agent", children: "One agent" })] })] }), draft.scopeKind !== 'organization' && (_jsxs(FormControl, { required: true, children: [_jsxs(FormControl.Label, { children: ["Which ", draft.scopeKind] }), _jsx(TextInput, { block: true, value: draft.scopeUid, onChange: event => set('scopeUid', event.target.value), placeholder: "uid" }), _jsx(FormControl.Caption, { children: "Without one the rule would be measured over the whole organization under a name saying otherwise." })] }))] })] }) })), removing && (_jsx(Dialog, { title: "Remove this rule?", onClose: () => setRemoving(null), returnFocusRef: returnFocusRef, footerButtons: [
|
|
232
|
+
{
|
|
233
|
+
buttonType: 'default',
|
|
234
|
+
content: 'Keep it',
|
|
235
|
+
onClick: () => setRemoving(null),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
buttonType: 'danger',
|
|
239
|
+
content: 'Remove',
|
|
240
|
+
onClick: confirmRemove,
|
|
241
|
+
disabled: remove.isPending,
|
|
242
|
+
},
|
|
243
|
+
], children: _jsx(Text, { sx: { fontSize: 1 }, children: "What it watched is unwatched from the next check, and nothing will say so again. If you only want it quiet for now, switch it off instead \u2014 it stays in the list." }) }))] }));
|
|
244
|
+
};
|
|
245
|
+
export default AlertRules;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calls an agent cannot make without somebody saying yes.
|
|
3
|
+
*
|
|
4
|
+
* A source set to `explicit` stops every call it does not already hold an
|
|
5
|
+
* approval for. The call waits as `pending-approval`, and the person who
|
|
6
|
+
* owns the source is the only one who can move it — which means that until
|
|
7
|
+
* this page existed, "approval required" was a setting that stopped agents
|
|
8
|
+
* and gave nobody a way to unstop them.
|
|
9
|
+
*
|
|
10
|
+
* **An approval is bound to the arguments it was asked about.** Contents
|
|
11
|
+
* hashes them and re-checks the hash when the call runs, so approving is
|
|
12
|
+
* approving *those* arguments and not that tool in general. That is why the
|
|
13
|
+
* redacted arguments are drawn in full rather than summarised: a reader who
|
|
14
|
+
* cannot see what they are allowing is a reader who approves everything.
|
|
15
|
+
*
|
|
16
|
+
* **A pending approval that has expired is not decidable**, and the buttons
|
|
17
|
+
* go rather than fail. Contents expires them lazily — the record still says
|
|
18
|
+
* `pending` until something looks at it — so a row can arrive here already
|
|
19
|
+
* over, and offering an Approve button on it would be offering a decision
|
|
20
|
+
* the server will refuse.
|
|
21
|
+
*
|
|
22
|
+
* @module views/mcp/ApprovalQueue
|
|
23
|
+
*/
|
|
24
|
+
import type { JSX } from 'react';
|
|
25
|
+
import type { McpApproval } from '../../api/contents/generated';
|
|
26
|
+
import { type McpErrorStateFn } from './types';
|
|
27
|
+
/** How each state of an approval is drawn. */
|
|
28
|
+
export declare const APPROVAL_LOOK: Record<McpApproval['status'], {
|
|
29
|
+
label: string;
|
|
30
|
+
variant: 'attention' | 'success' | 'danger' | 'secondary';
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Whether this approval can still be decided.
|
|
34
|
+
*
|
|
35
|
+
* Two conditions, and the second is the one that matters: the record says
|
|
36
|
+
* `pending`, *and* its deadline has not passed. Contents expires an approval
|
|
37
|
+
* when something next reads it rather than on a timer, so a row fetched a
|
|
38
|
+
* minute ago can be `pending` and over. Trusting the status alone would draw
|
|
39
|
+
* buttons whose only outcome is an error.
|
|
40
|
+
*
|
|
41
|
+
* An unparseable deadline is treated as passed. That is the safe direction:
|
|
42
|
+
* the worst case is somebody has to ask the agent to try again, where the
|
|
43
|
+
* other way round is a decision taken on a record nobody can date.
|
|
44
|
+
*/
|
|
45
|
+
export declare const isDecidable: (approval: Pick<McpApproval, "status" | "expiresAt">, now?: number) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* What a row says about its own deadline.
|
|
48
|
+
*
|
|
49
|
+
* A pending approval that has run out is drawn as expired even though the
|
|
50
|
+
* record still says pending — the reader's question is "can I still act on
|
|
51
|
+
* this", and the record's answer to that is stale by construction.
|
|
52
|
+
*/
|
|
53
|
+
export declare const approvalLook: (approval: Pick<McpApproval, "status" | "expiresAt">, now?: number) => {
|
|
54
|
+
label: string;
|
|
55
|
+
variant: "attention" | "success" | "danger" | "secondary";
|
|
56
|
+
};
|
|
57
|
+
/** The states this page can be filtered to. `pending` is what it opens on. */
|
|
58
|
+
export declare const APPROVAL_FILTERS: McpApproval['status'][];
|
|
59
|
+
export interface ApprovalQueueProps {
|
|
60
|
+
/** The application's words for a failed request. */
|
|
61
|
+
errorState: McpErrorStateFn;
|
|
62
|
+
/** Only the approvals of one source, when the caller has one in mind. */
|
|
63
|
+
sourceUid?: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const ApprovalQueue: ({ errorState, sourceUid, }: ApprovalQueueProps) => JSX.Element;
|
|
66
|
+
export default ApprovalQueue;
|