@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
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { Heading, Label, Link, Select, Spinner, Text } from '@primer/react';
|
|
4
|
+
import { Blankslate, Table } from '@primer/react/experimental';
|
|
5
|
+
import { Box } from '@datalayer/primer-addons';
|
|
6
|
+
import { ShieldCheckIcon } from '@primer/octicons-react';
|
|
7
|
+
import { McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
8
|
+
import { useConnectedAgents, useEffectivePolicy } from '../../hooks/useMcp.js';
|
|
9
|
+
import { useNavigate } from '../../hooks/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* The filter for a preview choice.
|
|
12
|
+
*
|
|
13
|
+
* A blank choice is *nobody*, not an agent whose client id is the empty
|
|
14
|
+
* string: sent as a filter that matches nothing, and a table that renders
|
|
15
|
+
* empty looks like a policy that grants nothing rather than a query that
|
|
16
|
+
* asked for no one.
|
|
17
|
+
*/
|
|
18
|
+
export const policyFiltersOf = (previewAs) => ({
|
|
19
|
+
agent: previewAs.trim() || undefined,
|
|
20
|
+
});
|
|
21
|
+
/** How each layer is named on a row, in the reader's terms. */
|
|
22
|
+
export const layerLabel = (layer, organizationName) => {
|
|
23
|
+
switch (layer) {
|
|
24
|
+
case 'platform':
|
|
25
|
+
return 'Datalayer default';
|
|
26
|
+
case 'organization':
|
|
27
|
+
return organizationName
|
|
28
|
+
? `required by ${organizationName} (organization)`
|
|
29
|
+
: 'required by your organization';
|
|
30
|
+
case 'team':
|
|
31
|
+
return 'required by your team';
|
|
32
|
+
default:
|
|
33
|
+
return 'you asked';
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const LAYER_VARIANT = {
|
|
37
|
+
platform: 'secondary',
|
|
38
|
+
organization: 'accent',
|
|
39
|
+
team: 'accent',
|
|
40
|
+
personal: 'attention',
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The rules this page names, in the order it names them, with the words a
|
|
44
|
+
* reader uses rather than the field names of the policy document.
|
|
45
|
+
*/
|
|
46
|
+
const ROW_ORDER = [
|
|
47
|
+
{
|
|
48
|
+
name: 'approval_policy',
|
|
49
|
+
label: 'Default approval',
|
|
50
|
+
description: 'What happens before a tool your agent calls actually runs.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'allowed_clients',
|
|
54
|
+
label: 'Clients admitted',
|
|
55
|
+
description: 'Which MCP clients may hold a grant at all.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'require_org_sso',
|
|
59
|
+
label: 'Sign-in required',
|
|
60
|
+
description: 'Whether an agent must be authorized through your organization.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'require_dpop',
|
|
64
|
+
label: 'Token binding',
|
|
65
|
+
description: 'Whether a token must be bound to the key of the client holding it.',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'max_scopes',
|
|
69
|
+
label: 'Scopes at most',
|
|
70
|
+
description: 'The ceiling on what any agent of yours may be granted.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'allowed_environments',
|
|
74
|
+
label: 'Compute: environments',
|
|
75
|
+
description: 'The environments an agent may launch a sandbox in.',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'allowed_providers',
|
|
79
|
+
label: 'Compute: providers',
|
|
80
|
+
description: 'Where those sandboxes may run.',
|
|
81
|
+
},
|
|
82
|
+
{ name: 'max_gpu', label: 'Compute: GPU at most', description: '' },
|
|
83
|
+
{
|
|
84
|
+
name: 'max_reservation_minutes',
|
|
85
|
+
label: 'Compute: reservation at most',
|
|
86
|
+
description: '',
|
|
87
|
+
},
|
|
88
|
+
{ name: 'credits_per_day', label: 'Spend: credits a day', description: '' },
|
|
89
|
+
{ name: 'calls_per_minute', label: 'Rate: calls a minute', description: '' },
|
|
90
|
+
{ name: 'session_max_hours', label: 'Session at most', description: '' },
|
|
91
|
+
];
|
|
92
|
+
/** A rule's value in the words of the page, whatever shape it arrived in. */
|
|
93
|
+
export const ruleValueLabel = (value) => {
|
|
94
|
+
if (value === null || value === undefined) {
|
|
95
|
+
return 'No limit';
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === 'boolean') {
|
|
98
|
+
return value ? 'Required' : 'Not required';
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
return value.length === 0 ? 'Nothing admitted' : value.join(', ');
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === 'object') {
|
|
104
|
+
return Object.entries(value)
|
|
105
|
+
.map(([key, item]) => `${key}: ${String(item)}`)
|
|
106
|
+
.join(' · ');
|
|
107
|
+
}
|
|
108
|
+
return String(value);
|
|
109
|
+
};
|
|
110
|
+
/** The tools of one access kind, and what happens before they run. */
|
|
111
|
+
const ToolGroup = ({ title, description, tools, organizationName, }) => (_jsxs(Box, { sx: { display: 'grid', gap: 2 }, children: [_jsxs(Box, { children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold', display: 'block' }, children: title }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: description })] }), tools.length === 0 ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "No tool of this kind is available to your agents." })) : (_jsx(Box, { sx: { display: 'flex', gap: 1, flexWrap: 'wrap' }, children: tools.map(tool => (_jsx(Label, { size: "small", variant: !tool.allowed
|
|
112
|
+
? 'danger'
|
|
113
|
+
: tool.approval === 'explicit'
|
|
114
|
+
? 'attention'
|
|
115
|
+
: 'secondary', title: `${tool.scope} · ${tool.approval ?? 'no approval rule'} · ${layerLabel(tool.decidedBy, organizationName)}`, children: tool.tool }, tool.tool))) }))] }));
|
|
116
|
+
export const Policies = ({ errorState, routes, organizationName, showTitle = true, }) => {
|
|
117
|
+
const navigate = useNavigate();
|
|
118
|
+
// Whose policy is being read. Empty is the reader's own.
|
|
119
|
+
//
|
|
120
|
+
// The layers differ per agent — a client admitted by one organization and
|
|
121
|
+
// not another, a scope one grant carries and the next does not — so "what
|
|
122
|
+
// applies" has no single answer once somebody has connected more than one
|
|
123
|
+
// agent. Without this the page answered for the reader's own token and
|
|
124
|
+
// said nothing about which, which is the answer to a question nobody
|
|
125
|
+
// asked.
|
|
126
|
+
const [previewAs, setPreviewAs] = useState('');
|
|
127
|
+
const agents = useConnectedAgents();
|
|
128
|
+
const policy = useEffectivePolicy(policyFiltersOf(previewAs).agent);
|
|
129
|
+
const rows = useMemo(() => {
|
|
130
|
+
const effective = policy.data;
|
|
131
|
+
if (!effective) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const byName = new Map(effective.rules.map(rule => [rule.name, rule]));
|
|
135
|
+
const known = ROW_ORDER.filter(row => byName.has(row.name)).map(row => ({
|
|
136
|
+
...row,
|
|
137
|
+
rule: byName.get(row.name),
|
|
138
|
+
}));
|
|
139
|
+
// A rule the gateway answers that this page has no wording for is still
|
|
140
|
+
// shown: hiding it would make the page a partial answer to "what applies".
|
|
141
|
+
const rest = effective.rules
|
|
142
|
+
.filter(rule => !ROW_ORDER.some(row => row.name === rule.name))
|
|
143
|
+
.map(rule => ({
|
|
144
|
+
name: rule.name,
|
|
145
|
+
label: rule.name,
|
|
146
|
+
description: '',
|
|
147
|
+
rule,
|
|
148
|
+
}));
|
|
149
|
+
return [...known, ...rest];
|
|
150
|
+
}, [policy.data]);
|
|
151
|
+
const tools = policy.data?.tools ?? [];
|
|
152
|
+
const readOnlyTools = tools.filter(tool => tool.access === 'read');
|
|
153
|
+
const writeTools = tools.filter(tool => tool.access === 'write');
|
|
154
|
+
const executeTools = tools.filter(tool => tool.access === 'execute' || !tool.access);
|
|
155
|
+
if (policy.isError) {
|
|
156
|
+
return (_jsx(McpErrorBlankslate, { state: errorState(policy.error, 'Policies'), onRetry: () => policy.refetch() }));
|
|
157
|
+
}
|
|
158
|
+
return (_jsxs(Box, { sx: { display: 'grid', gap: 4, minWidth: 0 }, children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Policies" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: previewAs
|
|
159
|
+
? 'What applies to this agent, and which layer decided it.'
|
|
160
|
+
: 'What applies to your agents, and which layer decided it.' })] })), (agents.data?.length ?? 0) > 1 && (_jsxs(Box, { sx: {
|
|
161
|
+
display: 'flex',
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
gap: 2,
|
|
164
|
+
flexWrap: 'wrap',
|
|
165
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Preview as" }), _jsx(Box, { sx: { minWidth: '18rem' }, children: _jsxs(Select, { value: previewAs, onChange: event => setPreviewAs(event.target.value), "aria-label": "Preview the policy as", children: [_jsx(Select.Option, { value: "", children: "You \u2014 your own token" }), (agents.data ?? []).map(agent => (_jsx(Select.Option, { value: agent.clientId, children: agent.clientName || agent.clientId }, agent.uid)))] }) }), previewAs && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "A grant carries its own scopes and its own admitted clients, so this can differ from what applies to you." }))] })), policy.isPending && !policy.data ? (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 6 }, children: _jsx(Spinner, {}) })) : rows.length === 0 && tools.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(ShieldCheckIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "The defaults apply" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "No agent is connected yet, so nothing narrows the Datalayer defaults. Connect a client and this page fills with what its calls are allowed to do." }) }), _jsx(Link, { as: "button", sx: { cursor: 'pointer' }, onClick: () => navigate(routes.access), children: "Set up a client" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: { display: 'grid', gap: 2 }, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "What applies to your agents" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Scope of this reading: ", policy.data?.scope ?? 'personal', policy.data?.evaluatedAt
|
|
166
|
+
? ` · evaluated ${policy.data.evaluatedAt}`
|
|
167
|
+
: ''] })] }), _jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h4", id: "effective-policy", children: "Effective policy" }), _jsx(Table.Subtitle, { as: "p", id: "effective-policy-subtitle", children: "Each layer only narrows the one above it, so the strictest rule is what applies." }), _jsxs(Box, { as: "table", "aria-labelledby": "effective-policy", "aria-describedby": "effective-policy-subtitle", sx: { width: '100%', borderCollapse: 'collapse' }, children: [_jsx(Box, { as: "thead", children: _jsx(Box, { as: "tr", children: ['Rule', 'What applies', 'Decided by'].map(header => (_jsx(Box, { as: "th", sx: {
|
|
168
|
+
textAlign: 'left',
|
|
169
|
+
fontSize: 0,
|
|
170
|
+
color: 'fg.muted',
|
|
171
|
+
fontWeight: 'semibold',
|
|
172
|
+
py: 2,
|
|
173
|
+
px: 2,
|
|
174
|
+
borderBottom: '1px solid',
|
|
175
|
+
borderColor: 'border.default',
|
|
176
|
+
}, children: header }, header))) }) }), _jsx(Box, { as: "tbody", children: rows.map(row => (_jsxs(Box, { as: "tr", children: [_jsxs(Box, { as: "td", sx: {
|
|
177
|
+
py: 2,
|
|
178
|
+
px: 2,
|
|
179
|
+
borderBottom: '1px solid',
|
|
180
|
+
borderColor: 'border.muted',
|
|
181
|
+
verticalAlign: 'top',
|
|
182
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, display: 'block' }, children: row.label }), row.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: row.description }))] }), _jsx(Box, { as: "td", sx: {
|
|
183
|
+
py: 2,
|
|
184
|
+
px: 2,
|
|
185
|
+
borderBottom: '1px solid',
|
|
186
|
+
borderColor: 'border.muted',
|
|
187
|
+
verticalAlign: 'top',
|
|
188
|
+
}, children: _jsx(Text, { sx: { fontSize: 1 }, children: ruleValueLabel(row.rule.value) }) }), _jsxs(Box, { as: "td", sx: {
|
|
189
|
+
py: 2,
|
|
190
|
+
px: 2,
|
|
191
|
+
borderBottom: '1px solid',
|
|
192
|
+
borderColor: 'border.muted',
|
|
193
|
+
verticalAlign: 'top',
|
|
194
|
+
whiteSpace: 'nowrap',
|
|
195
|
+
}, children: [_jsx(Label, { size: "small", variant: LAYER_VARIANT[row.rule.decidedBy], children: layerLabel(row.rule.decidedBy, organizationName) }), row.rule.reason && (_jsx(Text, { sx: {
|
|
196
|
+
fontSize: 0,
|
|
197
|
+
color: 'fg.muted',
|
|
198
|
+
display: 'block',
|
|
199
|
+
mt: 1,
|
|
200
|
+
}, children: row.rule.reason }))] })] }, row.name))) })] })] }), _jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [_jsx(ToolGroup, { title: "Read-only tools", description: "Tools that only look: listing, reading a cell, reading a source.", tools: readOnlyTools, organizationName: organizationName }), _jsx(ToolGroup, { title: "Tools that change things", description: "Tools that write to a notebook, a document or a source.", tools: writeTools, organizationName: organizationName }), _jsx(ToolGroup, { title: "Tools that launch or spend", description: "Tools that execute code, launch a sandbox or reserve compute.", tools: executeTools, organizationName: organizationName })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Your token carries ", policy.data?.scopes.length ?? 0, " scopes, which is the ceiling over every rule above:", ' ', (policy.data?.scopes ?? []).join(', ') || 'none', "."] })] })), _jsxs(Box, { sx: {
|
|
201
|
+
p: 3,
|
|
202
|
+
border: '1px solid',
|
|
203
|
+
borderColor: 'border.default',
|
|
204
|
+
borderRadius: 2,
|
|
205
|
+
bg: 'canvas.subtle',
|
|
206
|
+
display: 'grid',
|
|
207
|
+
gap: 1,
|
|
208
|
+
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: "Your own rules" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Narrowing the defaults for your own agents \u2014 approval by tool, a daily credits cap, a denylist, the environments they may use \u2014 is edited here once the policy layers ship. When your agents act inside an organization, that organization's policy applies instead of yours." }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold', mt: 2 }, children: "Organizations you own" }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "The policy form for each organization you own, with its history and a preview of what one agent would get, is the same form the organization's MCP tab carries. It joins this page with the layers." })] })] }));
|
|
209
|
+
};
|
|
210
|
+
export default Policies;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fields of a policy layer, and the reading of them a layer cannot get
|
|
3
|
+
* wrong twice.
|
|
4
|
+
*
|
|
5
|
+
* One component for the organization's layer and a person's own, because
|
|
6
|
+
* the rules are the same rules and two forms would be two chances to word
|
|
7
|
+
* "an empty allowlist is not an allowlist" differently — the reading that
|
|
8
|
+
* differs from the plain one is exactly the reading that must not drift.
|
|
9
|
+
*
|
|
10
|
+
* What the two callers do differ about is what a rule *means*. An
|
|
11
|
+
* organization's layer is the floor everyone in it stands on; a person's
|
|
12
|
+
* only narrows further, and cannot loosen. `notes` is how a caller says so
|
|
13
|
+
* per field, beside the field, at the moment somebody is typing into it.
|
|
14
|
+
*
|
|
15
|
+
* @module views/mcp/PolicyForm
|
|
16
|
+
*/
|
|
17
|
+
import type { JSX } from 'react';
|
|
18
|
+
import type { McpPolicyRules } from '../../api/iam/mcpPolicy';
|
|
19
|
+
/** The form's own shape: every field a string, as an input holds it. */
|
|
20
|
+
export type PolicyDraft = {
|
|
21
|
+
toolDenylist: string;
|
|
22
|
+
toolAllowlist: string;
|
|
23
|
+
allowedClients: string;
|
|
24
|
+
maxCallsPerMinute: string;
|
|
25
|
+
maxCreditsPerDay: string;
|
|
26
|
+
maxConcurrentSandboxes: string;
|
|
27
|
+
gpuHoursPerMonth: string;
|
|
28
|
+
storageBytes: string;
|
|
29
|
+
sessionMaxHours: string;
|
|
30
|
+
/** A checkbox, held as `'true'`/`''` so every field stays a string. */
|
|
31
|
+
ssoAdmitsWithoutConsent: string;
|
|
32
|
+
};
|
|
33
|
+
export declare const EMPTY_POLICY_DRAFT: PolicyDraft;
|
|
34
|
+
/** A stored list as the textarea shows it: one entry a line. */
|
|
35
|
+
export declare const linesOf: (value: string[] | undefined) => string;
|
|
36
|
+
/**
|
|
37
|
+
* A textarea back to a list, or `undefined` where nothing was typed.
|
|
38
|
+
*
|
|
39
|
+
* `undefined`, never `[]`. An empty array is a rule *set* to nothing, and
|
|
40
|
+
* for an allowlist that is the difference between "I have not set one" and
|
|
41
|
+
* a setting that refuses everything.
|
|
42
|
+
*/
|
|
43
|
+
export declare const listFrom: (value: string) => string[] | undefined;
|
|
44
|
+
/** A number field back to a number, or `undefined` where left blank. */
|
|
45
|
+
export declare const numberFrom: (value: string) => number | undefined;
|
|
46
|
+
export declare const textOf: (value: number | undefined) => string;
|
|
47
|
+
/** A stored layer as the form holds it. */
|
|
48
|
+
export declare const draftOf: (rules: McpPolicyRules | null | undefined) => PolicyDraft;
|
|
49
|
+
/**
|
|
50
|
+
* The draft as it will be stored, or the reason it will not be.
|
|
51
|
+
*
|
|
52
|
+
* A cap of zero is caught here rather than at the write, because IAM's
|
|
53
|
+
* refusal is correct and unhelpful: somebody who typed 0 meant "stop my
|
|
54
|
+
* agents", and the answer they need is what to do instead.
|
|
55
|
+
*/
|
|
56
|
+
export declare const rulesFrom: (draft: PolicyDraft) => McpPolicyRules | string;
|
|
57
|
+
export declare const policyChanged: (a: PolicyDraft, b: PolicyDraft) => boolean;
|
|
58
|
+
export interface PolicyFormProps {
|
|
59
|
+
draft: PolicyDraft;
|
|
60
|
+
onChange: (key: keyof PolicyDraft, value: string) => void;
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* A line drawn under one field, when this layer's reading of it needs
|
|
64
|
+
* something said — "your organization already caps this at 30", say.
|
|
65
|
+
*
|
|
66
|
+
* Beside the field rather than at the top: somebody typing 60 into a box
|
|
67
|
+
* needs to know while their hands are on it, not after they save.
|
|
68
|
+
*/
|
|
69
|
+
notes?: Partial<Record<keyof PolicyDraft, React.ReactNode>>;
|
|
70
|
+
/**
|
|
71
|
+
* Rendered under the admitted-clients field: what the stored entries
|
|
72
|
+
* actually name.
|
|
73
|
+
*
|
|
74
|
+
* Passed in rather than fetched here, because it needs the scope and
|
|
75
|
+
* subject this form is editing and the form is deliberately presentational
|
|
76
|
+
* — organization, team and personal all render it, and each knows its own
|
|
77
|
+
* layer. Not a `note`: those are one warning line in attention colour, and
|
|
78
|
+
* this is a list.
|
|
79
|
+
*/
|
|
80
|
+
belowAllowedClients?: React.ReactNode;
|
|
81
|
+
}
|
|
82
|
+
export declare const PolicyForm: ({ draft, onChange, disabled, notes, belowAllowedClients, }: PolicyFormProps) => JSX.Element;
|
|
83
|
+
export default PolicyForm;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FormControl, TextInput, Textarea, Text, ToggleSwitch, } from '@primer/react';
|
|
3
|
+
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
export const EMPTY_POLICY_DRAFT = {
|
|
5
|
+
toolDenylist: '',
|
|
6
|
+
toolAllowlist: '',
|
|
7
|
+
allowedClients: '',
|
|
8
|
+
maxCallsPerMinute: '',
|
|
9
|
+
maxCreditsPerDay: '',
|
|
10
|
+
maxConcurrentSandboxes: '',
|
|
11
|
+
gpuHoursPerMonth: '',
|
|
12
|
+
storageBytes: '',
|
|
13
|
+
sessionMaxHours: '',
|
|
14
|
+
ssoAdmitsWithoutConsent: '',
|
|
15
|
+
};
|
|
16
|
+
/** A stored list as the textarea shows it: one entry a line. */
|
|
17
|
+
export const linesOf = (value) => (value ?? []).join('\n');
|
|
18
|
+
/**
|
|
19
|
+
* A textarea back to a list, or `undefined` where nothing was typed.
|
|
20
|
+
*
|
|
21
|
+
* `undefined`, never `[]`. An empty array is a rule *set* to nothing, and
|
|
22
|
+
* for an allowlist that is the difference between "I have not set one" and
|
|
23
|
+
* a setting that refuses everything.
|
|
24
|
+
*/
|
|
25
|
+
export const listFrom = (value) => {
|
|
26
|
+
const entries = value
|
|
27
|
+
.split('\n')
|
|
28
|
+
.map(entry => entry.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
return entries.length > 0 ? entries : undefined;
|
|
31
|
+
};
|
|
32
|
+
/** A number field back to a number, or `undefined` where left blank. */
|
|
33
|
+
export const numberFrom = (value) => {
|
|
34
|
+
const trimmed = value.trim();
|
|
35
|
+
if (!trimmed) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const parsed = Number(trimmed);
|
|
39
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
40
|
+
};
|
|
41
|
+
export const textOf = (value) => value === undefined || value === null ? '' : String(value);
|
|
42
|
+
/** A stored layer as the form holds it. */
|
|
43
|
+
export const draftOf = (rules) => ({
|
|
44
|
+
toolDenylist: linesOf(rules?.toolDenylist),
|
|
45
|
+
toolAllowlist: linesOf(rules?.toolAllowlist),
|
|
46
|
+
allowedClients: linesOf(rules?.allowedClients),
|
|
47
|
+
maxCallsPerMinute: textOf(rules?.maxCallsPerMinute),
|
|
48
|
+
maxCreditsPerDay: textOf(rules?.maxCreditsPerDay),
|
|
49
|
+
maxConcurrentSandboxes: textOf(rules?.maxConcurrentSandboxes),
|
|
50
|
+
gpuHoursPerMonth: textOf(rules?.gpuHoursPerMonth),
|
|
51
|
+
storageBytes: textOf(rules?.storageBytes),
|
|
52
|
+
sessionMaxHours: textOf(rules?.sessionMaxHours),
|
|
53
|
+
ssoAdmitsWithoutConsent: rules?.ssoAdmitsWithoutConsent ? 'true' : '',
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* The draft as it will be stored, or the reason it will not be.
|
|
57
|
+
*
|
|
58
|
+
* A cap of zero is caught here rather than at the write, because IAM's
|
|
59
|
+
* refusal is correct and unhelpful: somebody who typed 0 meant "stop my
|
|
60
|
+
* agents", and the answer they need is what to do instead.
|
|
61
|
+
*/
|
|
62
|
+
export const rulesFrom = (draft) => {
|
|
63
|
+
const numbers = [
|
|
64
|
+
['maxCallsPerMinute', 'Calls per minute'],
|
|
65
|
+
['maxCreditsPerDay', 'Credits per day'],
|
|
66
|
+
['maxConcurrentSandboxes', 'Sandboxes at once'],
|
|
67
|
+
['gpuHoursPerMonth', 'GPU-hours per month'],
|
|
68
|
+
['storageBytes', 'Storage'],
|
|
69
|
+
['sessionMaxHours', 'Session at most'],
|
|
70
|
+
];
|
|
71
|
+
for (const [key, label] of numbers) {
|
|
72
|
+
const typed = draft[key].trim();
|
|
73
|
+
if (!typed) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const parsed = Number(typed);
|
|
77
|
+
if (!Number.isFinite(parsed)) {
|
|
78
|
+
return `${label} must be a number, or empty for no limit.`;
|
|
79
|
+
}
|
|
80
|
+
if (parsed <= 0) {
|
|
81
|
+
return (`${label} cannot be 0. A non-positive limit reads as *no limit*, so ` +
|
|
82
|
+
'a zero written to stop your agents would lift the limit instead. ' +
|
|
83
|
+
'To stop an agent, revoke its grant or deny the tools it uses.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
toolDenylist: listFrom(draft.toolDenylist),
|
|
88
|
+
toolAllowlist: listFrom(draft.toolAllowlist),
|
|
89
|
+
allowedClients: listFrom(draft.allowedClients),
|
|
90
|
+
maxCallsPerMinute: numberFrom(draft.maxCallsPerMinute),
|
|
91
|
+
maxCreditsPerDay: numberFrom(draft.maxCreditsPerDay),
|
|
92
|
+
maxConcurrentSandboxes: numberFrom(draft.maxConcurrentSandboxes),
|
|
93
|
+
gpuHoursPerMonth: numberFrom(draft.gpuHoursPerMonth),
|
|
94
|
+
storageBytes: numberFrom(draft.storageBytes),
|
|
95
|
+
sessionMaxHours: numberFrom(draft.sessionMaxHours),
|
|
96
|
+
// `undefined` rather than `false` when it is off, so an organization that
|
|
97
|
+
// never touched it stores no rule at all — the same distinction the lists
|
|
98
|
+
// draw between "not set" and "set to nothing".
|
|
99
|
+
ssoAdmitsWithoutConsent: draft.ssoAdmitsWithoutConsent === 'true' ? true : undefined,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
export const policyChanged = (a, b) => Object.keys(EMPTY_POLICY_DRAFT).some(key => a[key] !== b[key]);
|
|
103
|
+
const Note = ({ children, }) => children ? (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'attention.fg', mt: 1, mb: 0 }, children: children })) : null;
|
|
104
|
+
export const PolicyForm = ({ draft, onChange, disabled = false, notes = {}, belowAllowedClients, }) => (_jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Denied tools" }), _jsx(Textarea, { block: true, rows: 3, disabled: disabled, value: draft.toolDenylist, onChange: event => onChange('toolDenylist', event.target.value), placeholder: 'execute_cell\ndelete_file' }), _jsx(FormControl.Caption, { children: "One a line. A denial is added to every other layer\u2019s and can never be lifted by one." }), _jsx(Note, { children: notes.toolDenylist })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Permitted tools" }), _jsx(Textarea, { block: true, rows: 3, disabled: disabled, value: draft.toolAllowlist, onChange: event => onChange('toolAllowlist', event.target.value), placeholder: 'read_cell\nlist_notebooks' }), _jsxs(FormControl.Caption, { children: ["One a line, and everything else is refused.", ' ', _jsx("strong", { children: "Leave it empty to set no allowlist" }), " \u2014 empty means \u201CI have not set one\u201D, not \u201Cnothing is permitted\u201D, because the second would stop every agent at once."] }), _jsx(Note, { children: notes.toolAllowlist })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Admitted clients" }), _jsx(Textarea, { block: true, rows: 3, disabled: disabled, value: draft.allowedClients, onChange: event => onChange('allowedClients', event.target.value), placeholder: 'https://claude.ai/.well-known/mcp-client.json\ncursor.com' }), _jsx(FormControl.Caption, { children: "A client\u2019s document URL, or just its hostname. Empty is not an allowlist, for the same reason as above." }), _jsx(Note, { children: notes.allowedClients }), belowAllowedClients && _jsx(Box, { sx: { mt: 2 }, children: belowAllowedClients })] }), _jsxs(Box, { sx: {
|
|
105
|
+
display: 'grid',
|
|
106
|
+
gap: 3,
|
|
107
|
+
gridTemplateColumns: ['1fr', '1fr 1fr 1fr'],
|
|
108
|
+
}, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Calls per minute" }), _jsx(TextInput, { block: true, type: "number", min: 1, disabled: disabled, value: draft.maxCallsPerMinute, onChange: event => onChange('maxCallsPerMinute', event.target.value) }), _jsx(FormControl.Caption, { children: "Empty for no limit." }), _jsx(Note, { children: notes.maxCallsPerMinute })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Credits per day" }), _jsx(TextInput, { block: true, type: "number", min: 1, disabled: disabled, value: draft.maxCreditsPerDay, onChange: event => onChange('maxCreditsPerDay', event.target.value) }), _jsx(FormControl.Caption, { children: "Checked before a launch, never mid-session." }), _jsx(Note, { children: notes.maxCreditsPerDay })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Sandboxes at once" }), _jsx(TextInput, { block: true, type: "number", min: 1, disabled: disabled, value: draft.maxConcurrentSandboxes, onChange: event => onChange('maxConcurrentSandboxes', event.target.value) }), _jsx(FormControl.Caption, { children: "Counted across the layer that set it." }), _jsx(Note, { children: notes.maxConcurrentSandboxes })] })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "GPU-hours per month" }), _jsx(TextInput, { block: true, type: "number", min: 0, step: "any", disabled: disabled, value: draft.gpuHoursPerMonth, onChange: event => onChange('gpuHoursPerMonth', event.target.value) }), _jsxs(FormControl.Caption, { children: ["Empty for no limit. Refuses a launch on an environment that", ' ', _jsx("strong", { children: "has a GPU" }), " \u2014 a CPU sandbox is never affected, whatever this reads. Fractions are allowed: half an hour on one card is 0.5.", _jsx("br", {}), "Over the ", _jsx("strong", { children: "last 30 days" }), ", not the calendar month, so a limit cannot be spent twice across a reset \u2014 which means it will not line up with an invoice period.", ' ', _jsx("strong", { children: "Every GPU-hour billed to this scope counts, not only your agents\u2019" }), ' ', "\u2014 a notebook somebody ran by hand is in this number too."] }), _jsx(Note, { children: notes.gpuHoursPerMonth })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Storage" }), _jsx(TextInput, { block: true, type: "number", min: 0, step: 1, disabled: disabled, value: draft.storageBytes, onChange: event => onChange('storageBytes', event.target.value), trailingVisual: "bytes" }), _jsxs(FormControl.Caption, { children: ["Empty for no limit. In ", _jsx("strong", { children: "bytes" }), " \u2014 1 GB is 1073741824, 1 TB is 1099511627776. A number whose unit is implied is one somebody eventually reads as the other, and this is compared against a figure reported in bytes.", _jsx("br", {}), "Checked on ", _jsx("strong", { children: "every" }), " launch, not only one that attaches content: every sandbox can write.", ' ', _jsx("strong", { children: "Every kept version counts" }), " \u2014 an object\u2019s size is its newest version\u2019s, so a hundred versions of a 1\u00A0GB file are 100\u00A0GB, and deleting the newest frees none of the rest."] }), _jsx(Note, { children: notes.storageBytes })] }), _jsxs(Box, { sx: {
|
|
109
|
+
display: 'grid',
|
|
110
|
+
gap: 3,
|
|
111
|
+
gridTemplateColumns: ['1fr', '1fr 1fr'],
|
|
112
|
+
}, children: [_jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Session at most" }), _jsx(TextInput, { block: true, type: "number", min: 1, disabled: disabled, value: draft.sessionMaxHours, onChange: event => onChange('sessionMaxHours', event.target.value) }), _jsx(FormControl.Caption, { children: "Hours before a person signs in and consents again. It bites at the refresh: a token already minted lives out its own short life, and the connection ends when the client next tries to extend it. Empty for no limit." }), _jsx(Note, { children: notes.sessionMaxHours })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Your directory may stand in for the consent screen" }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mt: 1 }, children: [_jsx(ToggleSwitch, { size: "small", checked: draft.ssoAdmitsWithoutConsent === 'true', disabled: disabled, "aria-labelledby": "sso-admits-without-consent-label", onClick: () => onChange('ssoAdmitsWithoutConsent', draft.ssoAdmitsWithoutConsent === 'true' ? '' : 'true') }), _jsx(Text, { id: "sso-admits-without-consent-label", sx: { fontSize: 1 }, children: "Skip it for clients you already admit" })] }), _jsxs(FormControl.Caption, { children: ["Only for somebody who signed in through one of", ' ', _jsx("strong", { children: "your own" }), " enabled identity providers, and only for a client named in ", _jsx("strong", { children: "Admitted clients" }), " above \u2014 which must not be empty. Every other session still sees the screen. This does not require anyone to sign in through your directory; it only says the screen adds nothing when they did."] }), _jsx(Note, { children: notes.ssoAdmitsWithoutConsent })] })] })] }));
|
|
113
|
+
export default PolicyForm;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who changed this policy, and when.
|
|
3
|
+
*
|
|
4
|
+
* The one question a policy page cannot answer from the policy itself: it
|
|
5
|
+
* shows what is in force, and says nothing about how it got that way. An
|
|
6
|
+
* administrator who finds a denylist they did not write has, without this,
|
|
7
|
+
* nowhere to ask.
|
|
8
|
+
*
|
|
9
|
+
* The rows come from the audit — IAM records `mcp.policy.set` and
|
|
10
|
+
* `mcp.policy.remove` with who, when, the scope, the subject and the
|
|
11
|
+
* version. They are read with `?method=`, narrowed to the organization,
|
|
12
|
+
* which is why that filter exists: without it these rows are reachable only
|
|
13
|
+
* by reading past every tool call in the collection.
|
|
14
|
+
*
|
|
15
|
+
* **It can be empty for a real reason.** IAM's rows only reach the
|
|
16
|
+
* collection where a deployment has configured the Solr audit sink; with the
|
|
17
|
+
* default log sink they are in a log pipeline instead. An empty drawer that
|
|
18
|
+
* implied "nobody has ever changed this" would be a lie in exactly the
|
|
19
|
+
* deployment where somebody is trying to find out, so it says which of the
|
|
20
|
+
* two it is.
|
|
21
|
+
*
|
|
22
|
+
* @module views/mcp/PolicyHistory
|
|
23
|
+
*/
|
|
24
|
+
import type { JSX } from 'react';
|
|
25
|
+
import type { McpAuditEvent } from '../../models/McpAuditEvent';
|
|
26
|
+
/** The methods IAM writes when a policy layer is written or removed. */
|
|
27
|
+
export declare const POLICY_AUDIT_METHODS: readonly ["mcp.policy.set", "mcp.policy.remove"];
|
|
28
|
+
export interface PolicyHistoryProps {
|
|
29
|
+
/** The organization whose audit this is read from. */
|
|
30
|
+
orgUid: string;
|
|
31
|
+
/**
|
|
32
|
+
* The layer, when the drawer is about one.
|
|
33
|
+
*
|
|
34
|
+
* The audit is queried by organization and method — `subject_uid` lives in
|
|
35
|
+
* `clientInfo` and is not a Solr field — so one layer's rows are picked
|
|
36
|
+
* out of the answer here. That is honest for a handful of policy changes
|
|
37
|
+
* and would not be for a busy tool-call listing.
|
|
38
|
+
*/
|
|
39
|
+
subjectUid?: string;
|
|
40
|
+
limit?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What a change was made *through*, or `''` for somebody's own browser.
|
|
44
|
+
*
|
|
45
|
+
* The row names the person either way — an agent acts with exactly the
|
|
46
|
+
* access of whoever authorized it, so the person is accountable for what it
|
|
47
|
+
* did. What the row must not do is stop there: this drawer exists for the
|
|
48
|
+
* administrator who finds a denylist they did not write, and a row naming
|
|
49
|
+
* only them tells them they wrote it.
|
|
50
|
+
*
|
|
51
|
+
* The agent's own uid wins over the client id. A service agent is a named
|
|
52
|
+
* thing somebody can go and look at; a client id is the software it ran in.
|
|
53
|
+
*/
|
|
54
|
+
export declare const actedThrough: (event: McpAuditEvent) => string;
|
|
55
|
+
/** What one row says about a change, in the words of the page it is on. */
|
|
56
|
+
export declare const describeChange: (event: McpAuditEvent) => string;
|
|
57
|
+
export declare const PolicyHistory: ({ orgUid, subjectUid, limit, }: PolicyHistoryProps) => JSX.Element;
|
|
58
|
+
export default PolicyHistory;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { Label, RelativeTime, Spinner, Text } from '@primer/react';
|
|
4
|
+
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { useAuditEvents } from '../../hooks/useMcp.js';
|
|
6
|
+
/** The methods IAM writes when a policy layer is written or removed. */
|
|
7
|
+
export const POLICY_AUDIT_METHODS = [
|
|
8
|
+
'mcp.policy.set',
|
|
9
|
+
'mcp.policy.remove',
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* What a change was made *through*, or `''` for somebody's own browser.
|
|
13
|
+
*
|
|
14
|
+
* The row names the person either way — an agent acts with exactly the
|
|
15
|
+
* access of whoever authorized it, so the person is accountable for what it
|
|
16
|
+
* did. What the row must not do is stop there: this drawer exists for the
|
|
17
|
+
* administrator who finds a denylist they did not write, and a row naming
|
|
18
|
+
* only them tells them they wrote it.
|
|
19
|
+
*
|
|
20
|
+
* The agent's own uid wins over the client id. A service agent is a named
|
|
21
|
+
* thing somebody can go and look at; a client id is the software it ran in.
|
|
22
|
+
*/
|
|
23
|
+
export const actedThrough = (event) => String(event.agentUid || event.clientId || '');
|
|
24
|
+
/** What one row says about a change, in the words of the page it is on. */
|
|
25
|
+
export const describeChange = (event) => {
|
|
26
|
+
const info = (event.clientInfo ?? {});
|
|
27
|
+
const scope = String(info.scope ?? '');
|
|
28
|
+
const rules = String(info.rules ?? '');
|
|
29
|
+
if (event.method === 'mcp.policy.remove') {
|
|
30
|
+
return `removed the ${scope || 'policy'} layer`;
|
|
31
|
+
}
|
|
32
|
+
if (rules && rules !== '(none)') {
|
|
33
|
+
return `set ${rules.split(',').join(', ')}`;
|
|
34
|
+
}
|
|
35
|
+
// A layer written with no rules narrows nothing. Saying "set (none)" would
|
|
36
|
+
// read as a failure; saying what it means is the point of the row.
|
|
37
|
+
return 'set a layer that narrows nothing';
|
|
38
|
+
};
|
|
39
|
+
export const PolicyHistory = ({ orgUid, subjectUid, limit = 20, }) => {
|
|
40
|
+
// Both methods in one read: two queries would page independently and
|
|
41
|
+
// interleave wrongly, showing a removal before the set that preceded it.
|
|
42
|
+
const events = useAuditEvents({ org: orgUid, method: POLICY_AUDIT_METHODS[0], limit }, { enabled: Boolean(orgUid) });
|
|
43
|
+
const removals = useAuditEvents({ org: orgUid, method: POLICY_AUDIT_METHODS[1], limit }, { enabled: Boolean(orgUid) });
|
|
44
|
+
const rows = useMemo(() => {
|
|
45
|
+
const all = [
|
|
46
|
+
...(events.data?.items ?? []),
|
|
47
|
+
...(removals.data?.items ?? []),
|
|
48
|
+
];
|
|
49
|
+
const mine = subjectUid
|
|
50
|
+
? all.filter(event => String(event.clientInfo?.subject_uid ?? '') === subjectUid)
|
|
51
|
+
: all;
|
|
52
|
+
// Newest first, merged across the two reads.
|
|
53
|
+
return mine
|
|
54
|
+
.slice()
|
|
55
|
+
.sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0))
|
|
56
|
+
.slice(0, limit);
|
|
57
|
+
}, [events.data, removals.data, subjectUid, limit]);
|
|
58
|
+
if ((events.isPending || removals.isPending) && rows.length === 0) {
|
|
59
|
+
return (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 3 }, children: _jsx(Spinner, { size: "small" }) }));
|
|
60
|
+
}
|
|
61
|
+
if (events.isError || removals.isError) {
|
|
62
|
+
return (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "The history could not be read. The policy above is unaffected." }));
|
|
63
|
+
}
|
|
64
|
+
if (rows.length === 0) {
|
|
65
|
+
return (_jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', m: 0 }, children: "No recorded change. Either this layer has not been edited, or this deployment routes identity records to a log pipeline rather than to the audit \u2014 an administrator can tell you which." }));
|
|
66
|
+
}
|
|
67
|
+
return (_jsx(Box, { sx: { display: 'grid', gap: 2 }, children: rows.map(event => (_jsxs(Box, { sx: {
|
|
68
|
+
display: 'flex',
|
|
69
|
+
gap: 2,
|
|
70
|
+
alignItems: 'baseline',
|
|
71
|
+
flexWrap: 'wrap',
|
|
72
|
+
fontSize: 0,
|
|
73
|
+
}, children: [_jsx(Text, { sx: { color: 'fg.muted', whiteSpace: 'nowrap' }, children: _jsx(RelativeTime, { datetime: event.at }) }), _jsx(Text, { sx: { fontWeight: 'semibold' }, children: event.userUid || 'unknown' }), actedThrough(event) && (_jsxs(Label, { size: "small", variant: "attention", children: ["through ", actedThrough(event)] })), _jsx(Text, { children: describeChange(event) }), String(event.clientInfo?.version ?? '') && (_jsxs(Label, { size: "small", variant: "secondary", children: ["v", String(event.clientInfo?.version ?? '')] }))] }, event.uid))) }));
|
|
74
|
+
};
|
|
75
|
+
export default PolicyHistory;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One run: what it is doing, what it has printed, and the two things a
|
|
3
|
+
* person can do about it.
|
|
4
|
+
*
|
|
5
|
+
* A task is a tool call that outlived the request that asked for it, so the
|
|
6
|
+
* client that started it is usually gone. This is where somebody comes back
|
|
7
|
+
* to it — from another device, another session, or after closing the
|
|
8
|
+
* notebook — which is why it reads from the task record rather than from
|
|
9
|
+
* anything the caller held.
|
|
10
|
+
*
|
|
11
|
+
* **Cancelling stops the work, not just the record of it.** The gateway
|
|
12
|
+
* interrupts the kernel and then gives the tool a moment to hand back what
|
|
13
|
+
* the cell printed, so a run cancelled at minute nine of ten arrives here
|
|
14
|
+
* with its nine minutes of output and the `KeyboardInterrupt` that ended it.
|
|
15
|
+
* That is the reason the outputs are drawn for a cancelled run at all, and
|
|
16
|
+
* the reason the button says "Cancel" rather than "Stop watching".
|
|
17
|
+
*
|
|
18
|
+
* **`input_required` is a run asking, not a run idling.** It has stopped and
|
|
19
|
+
* will not move until somebody answers, so it is drawn as an open question
|
|
20
|
+
* with the answer box beside it rather than as another status label.
|
|
21
|
+
*
|
|
22
|
+
* Live while it runs: `useTaskEvents` patches the task's cache entry from
|
|
23
|
+
* the server's event stream, and `useTask` polls underneath as the fallback
|
|
24
|
+
* for a proxy that will not hold a stream open.
|
|
25
|
+
*
|
|
26
|
+
* @module views/mcp/RunDetail
|
|
27
|
+
*/
|
|
28
|
+
import type { JSX } from 'react';
|
|
29
|
+
import type { McpTask, McpTaskStatus } from '../../models/McpTask';
|
|
30
|
+
import type { McpSpanNode } from '../../api/mcp/observability';
|
|
31
|
+
import { type McpErrorStateFn, type McpRoutes } from './types';
|
|
32
|
+
/** How a run's status is drawn. Shared with the runs list. */
|
|
33
|
+
export declare const RUN_STATUS_LOOK: Record<McpTaskStatus, {
|
|
34
|
+
label: string;
|
|
35
|
+
variant: 'success' | 'attention' | 'secondary' | 'danger';
|
|
36
|
+
}>;
|
|
37
|
+
/** Whether the run has stopped for good. */
|
|
38
|
+
export declare const isRunOver: (status: McpTaskStatus) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Everything the run has printed, as text.
|
|
41
|
+
*
|
|
42
|
+
* Two shapes reach here and both are the run's output: the streamed
|
|
43
|
+
* `outputs`, and the `result` a finished tool returned. A cancelled run has
|
|
44
|
+
* the first and usually the second — the partial output is promoted to the
|
|
45
|
+
* result when it stops — so they are concatenated rather than chosen
|
|
46
|
+
* between, and de-duplicated by text so a promoted partial is not printed
|
|
47
|
+
* twice.
|
|
48
|
+
*/
|
|
49
|
+
export declare const outputTextOf: (task: McpTask) => string;
|
|
50
|
+
/**
|
|
51
|
+
* A run for the documentation to draw: cancelled mid-flight, holding the
|
|
52
|
+
* lines it printed before it stopped.
|
|
53
|
+
*
|
|
54
|
+
* Cancelled rather than completed on purpose. It is the state that shows
|
|
55
|
+
* both halves of what this view is for — the output a run keeps when it is
|
|
56
|
+
* stopped, and the reason it stopped — and it is the one a reader is most
|
|
57
|
+
* likely to be looking at when they open the documentation.
|
|
58
|
+
*/
|
|
59
|
+
export declare const SAMPLE_RUN: McpTask;
|
|
60
|
+
/**
|
|
61
|
+
* The spans that run left behind, for the documentation to draw.
|
|
62
|
+
*
|
|
63
|
+
* The shape is the one a reader needs to recognise: the gateway's twenty-two
|
|
64
|
+
* seconds are almost entirely the sandbox's, and the durable step between
|
|
65
|
+
* them explains the gap. A timeline drawn from an empty trace would teach
|
|
66
|
+
* nobody what the timeline is for.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SAMPLE_TRACE: McpSpanNode[];
|
|
69
|
+
export interface RunDetailProps {
|
|
70
|
+
/** The run to draw. */
|
|
71
|
+
taskUid: string;
|
|
72
|
+
/** The application's words for a failed request. */
|
|
73
|
+
errorState: McpErrorStateFn;
|
|
74
|
+
/** Where this application puts the surfaces this view links to. */
|
|
75
|
+
routes: McpRoutes;
|
|
76
|
+
/**
|
|
77
|
+
* Draw `SAMPLE_RUN` instead of asking the server, for documentation.
|
|
78
|
+
*
|
|
79
|
+
* The real component with fixed data, rather than a screenshot or a copy:
|
|
80
|
+
* a change to how a run is drawn reaches the documentation by itself, and
|
|
81
|
+
* a change that breaks it breaks the build rather than going unnoticed
|
|
82
|
+
* until somebody compares a picture.
|
|
83
|
+
*/
|
|
84
|
+
mock?: boolean;
|
|
85
|
+
}
|
|
86
|
+
export declare const RunDetail: ({ taskUid, errorState, routes, mock, }: RunDetailProps) => JSX.Element;
|
|
87
|
+
export default RunDetail;
|