@datalayer/core 1.1.60 → 1.1.66
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 +2 -2
- package/lib/api/DatalayerApi.d.ts +42 -1
- package/lib/api/DatalayerApi.js +90 -3
- package/lib/api/constants.d.ts +18 -0
- package/lib/api/constants.js +18 -0
- package/lib/api/contents/attachments.d.ts +9 -0
- package/lib/api/contents/attachments.js +43 -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 +15 -0
- package/lib/api/contents/datasets.js +45 -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 +53 -0
- package/lib/api/contents/sandboxUid.js +78 -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/iam/connectedAgents.d.ts +39 -0
- package/lib/api/iam/connectedAgents.js +52 -0
- package/lib/api/iam/index.d.ts +2 -1
- package/lib/api/iam/index.js +2 -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 +80 -0
- package/lib/api/iam/mcpPolicy.js +132 -0
- package/lib/api/iam/organizationTeams.d.ts +13 -0
- package/lib/api/iam/organizationTeams.js +43 -0
- package/lib/api/iam/serviceAgents.d.ts +68 -0
- package/lib/api/iam/serviceAgents.js +119 -0
- package/lib/api/index.d.ts +4 -1
- package/lib/api/index.js +3 -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 +125 -0
- package/lib/api/mcp/observability.js +254 -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/utils/validation.d.ts +1 -1
- package/lib/api/utils/validation.js +1 -1
- 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/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 +1 -0
- package/lib/components/animation/AnimatedText.js +0 -4
- 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 +9 -0
- package/lib/components/avatars/UserAvatar.js +0 -12
- 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 +10 -0
- package/lib/components/billing/BillingEntitySelect.js +0 -17
- package/lib/components/billing/index.js +0 -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 +31 -0
- package/lib/components/collaboration/LiveEditorCollaborators.js +86 -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 -2
- package/lib/components/display/DatalayerBox.js +6 -6
- 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/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 +1 -0
- 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 +1 -0
- package/lib/components/principal/PrincipalAppearance.js +0 -8
- package/lib/components/principal/PrincipalAvatar.d.ts +1 -0
- package/lib/components/principal/PrincipalAvatar.js +17 -9
- 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 +31 -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/ScreencaptureButton.d.ts +1 -1
- package/lib/components/sharing/ShareAccessComponent.d.ts +15 -1
- package/lib/components/sharing/ShareAccessComponent.js +36 -23
- package/lib/components/sharing/SharingEditor.d.ts +17 -0
- package/lib/components/sharing/SharingEditor.js +0 -24
- 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/index.d.ts +2 -0
- package/lib/hooks/index.js +2 -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/useCache.d.ts +310 -105
- package/lib/hooks/useCache.js +142 -355
- package/lib/hooks/useContainsFocus.d.ts +1 -1
- package/lib/hooks/useContents.d.ts +313 -0
- package/lib/hooks/useContents.js +964 -0
- package/lib/hooks/useFocusTrap.d.ts +4 -4
- package/lib/hooks/useFocusTrap.js +1 -1
- package/lib/hooks/useMcp.d.ts +229 -0
- package/lib/hooks/useMcp.js +656 -0
- package/lib/hooks/useMobile.js +0 -5
- 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/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/McpAuditEvent.d.ts +52 -0
- package/lib/models/McpAuditEvent.js +5 -0
- package/lib/models/McpBinding.d.ts +51 -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/StartedBy.d.ts +57 -0
- package/lib/models/StartedBy.js +81 -0
- package/lib/models/UserDTO.js +4 -2
- package/lib/models/contents/__fixtures__/solr-codec.json +57 -0
- package/lib/models/contents/__fixtures__/v1-contracts.json +66 -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/state/substates/CoreState.d.ts +15 -7
- package/lib/state/substates/CoreState.js +18 -14
- package/lib/state/substates/IAMState.d.ts +1 -1
- package/lib/state/substates/IAMState.js +5 -5
- package/lib/state/substates/ProfileState.js +0 -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/WithSuspense.d.ts +1 -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/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/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 +100 -0
- package/lib/views/mcp/EnterpriseConsole.d.ts +74 -0
- package/lib/views/mcp/EnterpriseConsole.js +173 -0
- package/lib/views/mcp/McpDashboard.d.ts +50 -0
- package/lib/views/mcp/McpDashboard.js +347 -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 +50 -0
- package/lib/views/mcp/McpObservability.js +150 -0
- package/lib/views/mcp/OrganizationPolicy.d.ts +45 -0
- package/lib/views/mcp/OrganizationPolicy.js +107 -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 +125 -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 +67 -0
- package/lib/views/mcp/PolicyForm.js +91 -0
- package/lib/views/mcp/PolicyHistory.d.ts +45 -0
- package/lib/views/mcp/PolicyHistory.js +62 -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 +98 -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 +29 -0
- package/lib/views/mcp/index.js +37 -0
- package/lib/views/mcp/types.d.ts +66 -0
- package/lib/views/mcp/types.js +5 -0
- package/lib/views/secrets/SecretEdit.d.ts +5 -1
- package/lib/views/secrets/SecretEdit.js +2 -2
- package/lib/views/secrets/SecretNew.d.ts +11 -1
- package/lib/views/secrets/SecretNew.js +2 -2
- package/lib/views/secrets/Secrets.d.ts +12 -1
- package/lib/views/secrets/Secrets.js +2 -2
- package/package.json +28 -24
- package/scripts/generate-contents-types.py +169 -0
- package/scripts/generate-mcp-types.py +233 -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,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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who did what, and was it allowed.
|
|
3
|
+
*
|
|
4
|
+
* The audit log is the trail an auditor reads, so it is drawn as the
|
|
5
|
+
* activity timeline is — newest first, one mark per row, the decision on
|
|
6
|
+
* the mark — rather than as a grid of identifiers. The filters live in the
|
|
7
|
+
* address, which is what makes a row's page shareable, and the page is
|
|
8
|
+
* cursor-paged: an audit query is bounded at the service, and a page number
|
|
9
|
+
* over an append-only collection would be a different set of rows every
|
|
10
|
+
* time it was asked for.
|
|
11
|
+
*
|
|
12
|
+
* Rows are immutable. This view never offers to edit or delete one, and it
|
|
13
|
+
* never shows an argument in clear — arguments are hashed and redacted with
|
|
14
|
+
* the Contents rules before they reach the row.
|
|
15
|
+
*
|
|
16
|
+
* Audit is not telemetry: a row carries the trace id so one call can be
|
|
17
|
+
* followed into the traces, and the traces say so about themselves on the
|
|
18
|
+
* observability page.
|
|
19
|
+
*
|
|
20
|
+
* @module views/mcp/AuditLog
|
|
21
|
+
*/
|
|
22
|
+
import type { JSX } from 'react';
|
|
23
|
+
import type { McpAuditFilters } from '../../api/mcp';
|
|
24
|
+
import { type McpErrorStateFn, type McpRoutes } from './types';
|
|
25
|
+
export interface AuditLogProps {
|
|
26
|
+
/** The application's words for a failed request. */
|
|
27
|
+
errorState: McpErrorStateFn;
|
|
28
|
+
/** Where this application puts the surfaces this view links to. */
|
|
29
|
+
routes: McpRoutes;
|
|
30
|
+
/** The filters, exactly as the address holds them. */
|
|
31
|
+
filters: McpAuditFilters;
|
|
32
|
+
/** Asked to write a new set of filters into the address. */
|
|
33
|
+
onFiltersChange: (filters: McpAuditFilters) => void;
|
|
34
|
+
/** How long rows are kept on this account's plan, when it is known. */
|
|
35
|
+
retentionDays?: number;
|
|
36
|
+
/** Drawn without its heading, when the page around it carries one. */
|
|
37
|
+
showTitle?: boolean;
|
|
38
|
+
/** What this log is of: "your agents", "acme". */
|
|
39
|
+
subject?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare const AuditLog: ({ errorState, routes, filters, onFiltersChange, retentionDays, showTitle, subject, }: AuditLogProps) => JSX.Element;
|
|
42
|
+
export default AuditLog;
|