@datalayer/core 1.2.18 → 1.2.19
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/lib/cli-login.js +5 -51
- package/lib/components/anonymous/AnonymousKeyExpired.js +1 -15
- package/lib/components/anonymous/AnonymousKeyTimer.js +5 -15
- package/lib/components/auth/SignIn.js +2 -2
- package/lib/components/avatars/UserAvatar.js +3 -27
- package/lib/components/billing/BillingEntitySelect.js +6 -48
- package/lib/components/checkout/StripeCheckout.js +33 -217
- package/lib/components/contents/ContentSourceCard.js +2 -11
- package/lib/components/contents/HomeFolderObjectsBrowser.d.ts +1 -1
- package/lib/components/contents/HomeFolderObjectsBrowser.js +11 -35
- package/lib/components/display/CenteredSpinner.js +1 -6
- package/lib/components/display/DatalayerBox.js +2 -14
- package/lib/components/flashes/FlashClosable.js +2 -17
- package/lib/components/mcp/ClientBadge.js +1 -1
- package/lib/components/mcp/McpErrorBlankslate.js +1 -1
- package/lib/components/mcp/ScopeList.js +1 -1
- package/lib/components/notebooks/JupyterNotebook.js +1 -2
- package/lib/components/principal/Principal.js +2 -10
- package/lib/components/principal/PrincipalAppearance.js +19 -63
- package/lib/components/principal/PrincipalAvatar.js +5 -25
- package/lib/components/principal/PrincipalBadge.js +3 -13
- package/lib/components/principal/PrincipalBanner.js +4 -32
- package/lib/components/principal/PrincipalDetailsOverlay.js +7 -23
- package/lib/components/principal/PrincipalHoverCard.js +15 -18
- package/lib/components/principal/PrincipalSwitcherMenu.js +15 -83
- package/lib/components/progress/ConsumptionBar.js +1 -1
- package/lib/components/sharing/ShareAccessComponent.js +21 -229
- package/lib/components/sharing/SharingEditor.js +1 -1
- package/lib/components/spaces/SpaceDetailsOverlay.js +4 -35
- package/lib/components/spaces/SpaceDisplay.js +1 -8
- package/lib/components/storage/ContentsBrowser.js +3 -26
- package/lib/components/storage/ContentsTable.js +4 -23
- package/lib/components/students/StudentResultCircle.js +7 -29
- package/lib/components/timeline/Timeline.js +4 -36
- package/lib/hooks/layouts/LayoutBackdrop.js +2 -1
- package/lib/hooks/layouts/LayoutScreencapture.js +1 -1
- package/lib/hooks/libraryApp.d.ts +6 -1
- package/lib/hooks/libraryApp.js +10 -1
- package/lib/hooks/useAuthorization.js +6 -1
- package/lib/mocks/components/FlashMock.js +3 -8
- package/lib/mocks/views/DashboardMock.js +6 -72
- package/lib/mocks/views/FormMock.js +3 -2
- package/lib/mocks/views/TableMock.js +3 -2
- package/lib/mocks/views/WipMock.js +2 -1
- package/lib/models/Person.d.ts +9 -0
- package/lib/models/Person.js +23 -0
- package/lib/otel/views/OtelLive.js +4 -62
- package/lib/otel/views/OtelLiveSpans.js +4 -29
- package/lib/otel/views/OtelLogsList.js +13 -64
- package/lib/otel/views/OtelMetricsChart.js +4 -19
- package/lib/otel/views/OtelMetricsList.js +8 -77
- package/lib/otel/views/OtelSearchBar.js +3 -12
- package/lib/otel/views/OtelSpanDetail.js +9 -79
- package/lib/otel/views/OtelSpanTree.js +7 -57
- package/lib/otel/views/OtelSqlView.js +5 -99
- package/lib/otel/views/OtelSystemView.js +4 -34
- package/lib/otel/views/OtelTimeline.js +7 -37
- package/lib/otel/views/OtelTimelineRangeSlider.js +7 -140
- package/lib/otel/views/OtelTracesList.js +12 -76
- package/lib/portable/index.d.ts +8 -0
- package/lib/portable/index.js +12 -0
- package/lib/state/substates/NavigationState.d.ts +6 -4
- package/lib/state/substates/NavigationState.js +6 -5
- package/lib/views/api-keys/APIKeyEdit.js +2 -11
- package/lib/views/api-keys/APIKeyNew.js +3 -19
- package/lib/views/api-keys/APIKeys.js +6 -11
- package/lib/views/api-keys/APIKeysStandalone.js +1 -1
- package/lib/views/datasources/DatasourceDetail.d.ts +1 -1
- package/lib/views/datasources/DatasourceDetail.js +24 -17
- package/lib/views/datasources/DatasourceNew.js +2 -6
- package/lib/views/datasources/Datasources.js +2 -14
- package/lib/views/iam/SignInSimple.js +8 -64
- package/lib/views/mcp/AdmittedClients.js +3 -16
- package/lib/views/mcp/AlertDestinations.js +2 -10
- package/lib/views/mcp/AlertRules.js +4 -9
- package/lib/views/mcp/ApprovalQueue.js +4 -21
- package/lib/views/mcp/AuditLog.js +7 -44
- package/lib/views/mcp/ConnectedAgents.js +1 -6
- package/lib/views/mcp/EnterpriseConsole.js +10 -23
- package/lib/views/mcp/IdentityProviders.js +7 -31
- package/lib/views/mcp/McpDashboard.js +14 -30
- package/lib/views/mcp/McpHome.js +7 -54
- package/lib/views/mcp/McpObservability.js +13 -85
- package/lib/views/mcp/NotebookRuns.js +5 -12
- package/lib/views/mcp/OrganizationPolicy.js +3 -9
- package/lib/views/mcp/OrganizationUsage.js +4 -11
- package/lib/views/mcp/PersonalPolicy.js +3 -3
- package/lib/views/mcp/Policies.js +5 -46
- package/lib/views/mcp/PolicyForm.js +1 -9
- package/lib/views/mcp/PolicyHistory.js +2 -8
- package/lib/views/mcp/RunDetail.js +3 -25
- package/lib/views/mcp/ScimProvisioning.js +3 -15
- package/lib/views/mcp/ServiceAgents.js +4 -17
- package/lib/views/mcp/TeamPolicies.js +7 -19
- package/lib/views/mcp/ToolAccess.js +15 -22
- package/lib/views/mcp/TraceTimeline.js +8 -23
- package/lib/views/otel/DashboardView.js +2 -2
- package/lib/views/otel/LogsView.js +2 -2
- package/lib/views/otel/MetricsView.js +2 -2
- package/lib/views/otel/OtelHeader.js +6 -40
- package/lib/views/otel/SqlView.js +2 -2
- package/lib/views/otel/SystemView.js +2 -2
- package/lib/views/otel/TracesView.js +2 -2
- package/lib/views/profile/UserBadge.js +6 -98
- package/lib/views/secrets/SecretEdit.js +2 -11
- package/lib/views/secrets/SecretNew.js +1 -1
- package/package.json +1 -1
package/lib/views/mcp/McpHome.js
CHANGED
|
@@ -41,15 +41,7 @@ export const MCP_CLIENTS = [
|
|
|
41
41
|
*/
|
|
42
42
|
export const Copyable = ({ text }) => {
|
|
43
43
|
const [copied, setCopied] = useState(false);
|
|
44
|
-
return (_jsxs(Box, { sx: {
|
|
45
|
-
display: 'flex',
|
|
46
|
-
alignItems: 'center',
|
|
47
|
-
gap: 2,
|
|
48
|
-
p: 2,
|
|
49
|
-
bg: 'canvas.inset',
|
|
50
|
-
borderRadius: 2,
|
|
51
|
-
minWidth: 0,
|
|
52
|
-
}, children: [_jsx(Text, { sx: {
|
|
44
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, p: 2, bg: "canvas.inset", borderRadius: 2, minWidth: 0, children: [_jsx(Text, { sx: {
|
|
53
45
|
fontFamily: 'mono',
|
|
54
46
|
fontSize: 0,
|
|
55
47
|
flex: 1,
|
|
@@ -64,23 +56,14 @@ export const Copyable = ({ text }) => {
|
|
|
64
56
|
} })] }));
|
|
65
57
|
};
|
|
66
58
|
/** One of the five summaries: a heading, what it holds, and the way in. */
|
|
67
|
-
const Summary = ({ icon: Icon, title, children, action, to, onOpen, }) => (_jsxs(Box, {
|
|
68
|
-
p: 3,
|
|
69
|
-
border: '1px solid',
|
|
70
|
-
borderColor: 'border.default',
|
|
71
|
-
borderRadius: 2,
|
|
72
|
-
display: 'grid',
|
|
73
|
-
gap: 2,
|
|
74
|
-
alignContent: 'start',
|
|
75
|
-
minWidth: 0,
|
|
76
|
-
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Icon, { size: 16 }), to ? (_jsx(Link, { href: to, onClick: event => {
|
|
59
|
+
const Summary = ({ icon: Icon, title, children, action, to, onOpen, }) => (_jsxs(Box, { p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, display: "grid", gap: 2, alignContent: "start", minWidth: 0, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Icon, { size: 16 }), to ? (_jsx(Link, { href: to, onClick: event => {
|
|
77
60
|
// A modified click asks for a new tab; `href` is real, so let it.
|
|
78
61
|
if (event.metaKey || event.ctrlKey || event.shiftKey) {
|
|
79
62
|
return;
|
|
80
63
|
}
|
|
81
64
|
event.preventDefault();
|
|
82
65
|
onOpen?.(to);
|
|
83
|
-
}, sx: { fontSize: 1, fontWeight: 'semibold' }, children: title })) : (_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: title }))] }), _jsx(Box, {
|
|
66
|
+
}, sx: { fontSize: 1, fontWeight: 'semibold' }, children: title })) : (_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: title }))] }), _jsx(Box, { fontSize: 0, color: "fg.muted", display: "grid", gap: 1, children: children }), action && (_jsx(Box, { children: _jsx(Link, { as: "button", sx: { fontSize: 0, cursor: 'pointer' }, onClick: action.onClick, children: action.label }) }))] }));
|
|
84
67
|
export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitle = true, showSummaries = true, showSetup = true, onOpenDocs, }) => {
|
|
85
68
|
const navigate = useNavigate();
|
|
86
69
|
const endpoint = useCoreStore(state => state.configuration.datalayerMcpServerUrl);
|
|
@@ -94,37 +77,11 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
94
77
|
const connected = agents.data ?? [];
|
|
95
78
|
const runs = tasks.data?.items ?? [];
|
|
96
79
|
const live = activity.data?.clients.length ?? 0;
|
|
97
|
-
return (_jsxs(Box, {
|
|
98
|
-
display: 'flex',
|
|
99
|
-
alignItems: 'center',
|
|
100
|
-
gap: 3,
|
|
101
|
-
flexWrap: 'wrap',
|
|
102
|
-
p: 3,
|
|
103
|
-
borderRadius: 2,
|
|
104
|
-
bg: 'canvas.subtle',
|
|
105
|
-
border: '1px solid',
|
|
106
|
-
borderColor: 'border.default',
|
|
107
|
-
}, children: [_jsx(Text, { sx: { fontSize: 1 }, children: live > 0
|
|
80
|
+
return (_jsxs(Box, { display: "grid", gap: 4, minWidth: 0, children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "MCP" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Connect your agents to your notebooks, data and sandboxes through the Model Context Protocol." })] })), showSetup && (_jsxs(_Fragment, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap", p: 3, borderRadius: 2, bg: "canvas.subtle", border: "1px solid", borderColor: "border.default", children: [_jsx(Text, { sx: { fontSize: 1 }, children: live > 0
|
|
108
81
|
? `${plural(live, 'client')} connected right now.`
|
|
109
|
-
: 'No client is connected right now.' }), _jsx(Button, { size: "small", leadingVisual: PulseIcon, onClick: () => navigate(routes.dashboard), children: "What is going on" })] }), _jsxs(Box, {
|
|
82
|
+
: 'No client is connected right now.' }), _jsx(Button, { size: "small", leadingVisual: PulseIcon, onClick: () => navigate(routes.dashboard), children: "What is going on" })] }), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "The endpoint" }), _jsx(Copyable, { text: endpoint || 'https://mcp.datalayer.run/mcp' }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(KeyIcon, { size: 14 }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: signedIn
|
|
110
83
|
? 'You are signed in, so a client you set up from here authorizes through OAuth without a shared token.'
|
|
111
|
-
: 'Sign in to authorize a client through OAuth; without it a client can only use a personal access token.' })] })] }), _jsxs(Box, {
|
|
112
|
-
display: 'grid',
|
|
113
|
-
gap: 3,
|
|
114
|
-
gridTemplateColumns: [
|
|
115
|
-
'1fr',
|
|
116
|
-
'repeat(2, 1fr)',
|
|
117
|
-
'repeat(3, 1fr)',
|
|
118
|
-
],
|
|
119
|
-
}, children: MCP_CLIENTS.map(client => (_jsxs(Box, { sx: {
|
|
120
|
-
p: 3,
|
|
121
|
-
border: '1px solid',
|
|
122
|
-
borderColor: 'border.default',
|
|
123
|
-
borderRadius: 2,
|
|
124
|
-
display: 'grid',
|
|
125
|
-
gap: 2,
|
|
126
|
-
minWidth: 0,
|
|
127
|
-
}, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: client.name }), _jsx(Copyable, { text: mcpSetupCommand(client.setup) }), _jsx(Link, { href: `${clientDocsBase}/${client.setup}`, sx: { fontSize: 0, cursor: 'pointer', textAlign: 'left' }, onClick: event => {
|
|
84
|
+
: 'Sign in to authorize a client through OAuth; without it a client can only use a personal access token.' })] })] }), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Set up a client" }), _jsx(Box, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)'], children: MCP_CLIENTS.map(client => (_jsxs(Box, { p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, display: "grid", gap: 2, minWidth: 0, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: client.name }), _jsx(Copyable, { text: mcpSetupCommand(client.setup) }), _jsx(Link, { href: `${clientDocsBase}/${client.setup}`, sx: { fontSize: 0, cursor: 'pointer', textAlign: 'left' }, onClick: event => {
|
|
128
85
|
// A modified click is the reader asking for a new tab or
|
|
129
86
|
// window, and `href` is real so it still leads to the
|
|
130
87
|
// full page — for that, for the status bar and for the
|
|
@@ -134,11 +91,7 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
134
91
|
}
|
|
135
92
|
event.preventDefault();
|
|
136
93
|
onOpenDocs(`${clientDocsBase}/${client.setup}`);
|
|
137
|
-
}, children: "Configuration and authorization" })] }, client.setup))) })] })] })), showSummaries && (_jsxs(Box, {
|
|
138
|
-
display: 'grid',
|
|
139
|
-
gap: 3,
|
|
140
|
-
gridTemplateColumns: ['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)'],
|
|
141
|
-
}, children: [_jsx(Summary, { icon: AiAgentIcon, title: "Agents", to: routes.agents, onOpen: navigate, action: {
|
|
94
|
+
}, children: "Configuration and authorization" })] }, client.setup))) })] })] })), showSummaries && (_jsxs(Box, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)'], children: [_jsx(Summary, { icon: AiAgentIcon, title: "Agents", to: routes.agents, onOpen: navigate, action: {
|
|
142
95
|
label: 'Connected agents',
|
|
143
96
|
onClick: () => navigate(routes.agents),
|
|
144
97
|
}, children: connected.length === 0 ? (_jsx(Text, { children: "No agent has been authorized yet. Set up a client above and approve the scopes it asks for." })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [plural(connected.length, 'agent'), " authorized."] }), connected.slice(0, 3).map(agent => (_jsxs(Text, { children: [agent.clientName || agent.clientId, " \u2014", ' ', agent.lastUsedAt
|
|
@@ -43,16 +43,7 @@ export const stageOf = (name) => {
|
|
|
43
43
|
const SpanRow = ({ node, depth, }) => {
|
|
44
44
|
const stage = stageOf(node.span.span_name);
|
|
45
45
|
const failed = (node.span.status_code ?? '').toUpperCase() === 'ERROR';
|
|
46
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Box, { sx: {
|
|
47
|
-
display: 'flex',
|
|
48
|
-
alignItems: 'center',
|
|
49
|
-
gap: 2,
|
|
50
|
-
py: 1,
|
|
51
|
-
pl: depth * 3,
|
|
52
|
-
borderBottom: '1px solid',
|
|
53
|
-
borderColor: 'border.muted',
|
|
54
|
-
minWidth: 0,
|
|
55
|
-
}, children: [_jsx(Text, { sx: {
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, py: 1, pl: depth * 3, borderBottom: "1px solid", borderColor: "border.muted", minWidth: 0, children: [_jsx(Text, { sx: {
|
|
56
47
|
fontSize: 0,
|
|
57
48
|
fontFamily: 'mono',
|
|
58
49
|
flex: 1,
|
|
@@ -69,13 +60,7 @@ const SpanRow = ({ node, depth, }) => {
|
|
|
69
60
|
}, children: durationLabel(node.span.duration_ms) })] }), node.children.map(child => (_jsx(SpanRow, { node: child, depth: depth + 1 }, child.span.span_id)))] }));
|
|
70
61
|
};
|
|
71
62
|
/** A number the reader can act on, or a plain statement that it is not measured. */
|
|
72
|
-
const Sli = ({ label, value, note, }) => (_jsxs(Box, { sx: {
|
|
73
|
-
p: 3,
|
|
74
|
-
border: '1px solid',
|
|
75
|
-
borderColor: 'border.default',
|
|
76
|
-
borderRadius: 2,
|
|
77
|
-
minWidth: 0,
|
|
78
|
-
}, children: [_jsx(Text, { sx: { display: 'block', fontSize: 3, fontWeight: 'bold' }, children: value }), _jsx(Text, { sx: { display: 'block', fontSize: 0, color: 'fg.muted' }, children: label }), note && (_jsx(Text, { sx: { display: 'block', fontSize: 0, color: 'fg.subtle' }, children: note }))] }));
|
|
63
|
+
const Sli = ({ label, value, note, }) => (_jsxs(Box, { p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, minWidth: 0, children: [_jsx(Text, { sx: { display: 'block', fontSize: 3, fontWeight: 'bold' }, children: value }), _jsx(Text, { sx: { display: 'block', fontSize: 0, color: 'fg.muted' }, children: label }), note && (_jsx(Text, { sx: { display: 'block', fontSize: 0, color: 'fg.subtle' }, children: note }))] }));
|
|
79
64
|
/**
|
|
80
65
|
* What the durable engine's own report amounts to, in one line.
|
|
81
66
|
*
|
|
@@ -135,12 +120,7 @@ export const McpObservability = ({ errorState, routes, pane, onPaneChange, taskU
|
|
|
135
120
|
const engine = useMcpWorkflowsHealth({ enabled: pane === 'live' });
|
|
136
121
|
const live = useTasks({ status: 'working' }, { enabled: pane === 'live' });
|
|
137
122
|
const telemetryNotice = (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["This is telemetry: spans and metrics are sampled, kept for a short time and meant for understanding how a call ran. It is not the record of what was allowed \u2014", ' ', _jsx(Text, { as: "span", sx: { color: 'accent.fg', cursor: 'pointer' }, onClick: () => navigate(routes.audit), children: "the audit log" }), ' ', "is, and it is kept per plan and never changed."] }));
|
|
138
|
-
return (_jsxs(Box, {
|
|
139
|
-
display: 'flex',
|
|
140
|
-
gap: 2,
|
|
141
|
-
alignItems: 'center',
|
|
142
|
-
flexWrap: 'wrap',
|
|
143
|
-
}, children: [_jsx(TextInput, { size: "small", leadingVisual: SearchIcon, "aria-label": "Task or trace id", placeholder: "Task uid or trace id", value: find, sx: { minWidth: 320 }, onChange: event => setFind(event.currentTarget.value) }), _jsx(Button, { size: "small", onClick: () => {
|
|
123
|
+
return (_jsxs(Box, { display: "grid", gap: 3, minWidth: 0, children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Observability" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "How the calls of your agents ran: where the time went, and how the service is behaving." })] })), _jsxs(SegmentedControl, { "aria-label": "Observability panes", size: "small", children: [_jsx(SegmentedControl.Button, { selected: pane === 'runs', onClick: () => onPaneChange('runs'), children: "Runs" }), _jsx(SegmentedControl.Button, { selected: pane === 'live', onClick: () => onPaneChange('live'), children: "Live" }), _jsx(SegmentedControl.Button, { selected: pane === 'metrics', onClick: () => onPaneChange('metrics'), children: "Metrics" })] }), telemetryNotice, pane === 'runs' && (_jsxs(Box, { display: "grid", gap: 3, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(TextInput, { size: "small", leadingVisual: SearchIcon, "aria-label": "Task or trace id", placeholder: "Task uid or trace id", value: find, sx: { minWidth: 320 }, onChange: event => setFind(event.currentTarget.value) }), _jsx(Button, { size: "small", onClick: () => {
|
|
144
124
|
const value = find.trim();
|
|
145
125
|
if (!value) {
|
|
146
126
|
onSelect({});
|
|
@@ -151,81 +131,29 @@ export const McpObservability = ({ errorState, routes, pane, onPaneChange, taskU
|
|
|
151
131
|
onSelect(/^[0-9a-f]{32}$/i.test(value)
|
|
152
132
|
? { trace: value }
|
|
153
133
|
: { task: value });
|
|
154
|
-
}, children: "Show the run" })] }), !taskUid && !traceId ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(TelescopeIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "Pick a run" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Open a call from the audit log or the dashboard, or paste its task uid or trace id here, and its spans across the gateway, the policy check and the worker appear with the time each took." }) }), _jsx(Button, { size: "small", onClick: () => navigate(routes.audit), children: "Open the audit log" })] })) : run.isError ? (_jsx(McpErrorBlankslate, { state: errorState(run.error, 'This run'), onRetry: () => run.refetch() })) : run.isPending ? (_jsx(Box, {
|
|
155
|
-
display: 'flex',
|
|
156
|
-
gap: 3,
|
|
157
|
-
alignItems: 'center',
|
|
158
|
-
flexWrap: 'wrap',
|
|
159
|
-
}, children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontFamily: 'mono' }, children: run.data?.traceId }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [run.data?.spans.length, " spans"] }), _jsx(Button, { size: "small", onClick: () => navigate(`${routes.audit}?trace=${encodeURIComponent(run.data?.traceId ?? '')}`), children: "The audit row" })] }), _jsx(TraceTimeline, { tree: run.data?.tree ?? [] }), _jsx(Box, { sx: {
|
|
160
|
-
border: '1px solid',
|
|
161
|
-
borderColor: 'border.default',
|
|
162
|
-
borderRadius: 2,
|
|
163
|
-
overflowX: 'auto',
|
|
164
|
-
}, children: (run.data?.tree ?? []).map(node => (_jsx(SpanRow, { node: node, depth: 0 }, node.span.span_id))) })] }))] })), pane === 'live' && (_jsxs(Box, { sx: { display: 'grid', gap: 3 }, children: [(() => {
|
|
134
|
+
}, children: "Show the run" })] }), !taskUid && !traceId ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(TelescopeIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "Pick a run" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Open a call from the audit log or the dashboard, or paste its task uid or trace id here, and its spans across the gateway, the policy check and the worker appear with the time each took." }) }), _jsx(Button, { size: "small", onClick: () => navigate(routes.audit), children: "Open the audit log" })] })) : run.isError ? (_jsx(McpErrorBlankslate, { state: errorState(run.error, 'This run'), onRetry: () => run.refetch() })) : run.isPending ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 5, children: _jsx(Spinner, {}) })) : (run.data?.spans.length ?? 0) === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(TelescopeIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "No span for this call" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Spans reach the collector a few seconds after the call and are kept for a short time. A call older than the retention window has an audit row but no trace." }) })] })) : (_jsxs(Box, { display: "grid", gap: 2, children: [_jsxs(Box, { display: "flex", gap: 3, alignItems: "center", flexWrap: "wrap", children: [_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontFamily: 'mono' }, children: run.data?.traceId }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: [run.data?.spans.length, " spans"] }), _jsx(Button, { size: "small", onClick: () => navigate(`${routes.audit}?trace=${encodeURIComponent(run.data?.traceId ?? '')}`), children: "The audit row" })] }), _jsx(TraceTimeline, { tree: run.data?.tree ?? [] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, overflowX: "auto", children: (run.data?.tree ?? []).map(node => (_jsx(SpanRow, { node: node, depth: 0 }, node.span.span_id))) })] }))] })), pane === 'live' && (_jsxs(Box, { display: "grid", gap: 3, children: [(() => {
|
|
165
135
|
const look = engineLook(engine.data, engine.isError);
|
|
166
136
|
const queues = engine.data?.queues ?? [];
|
|
167
|
-
return (_jsxs(Box, { sx: {
|
|
168
|
-
|
|
169
|
-
borderColor: 'border.default',
|
|
170
|
-
borderRadius: 2,
|
|
171
|
-
p: 3,
|
|
172
|
-
display: 'grid',
|
|
173
|
-
gap: 2,
|
|
174
|
-
}, children: [_jsxs(Box, { sx: {
|
|
175
|
-
display: 'flex',
|
|
176
|
-
gap: 2,
|
|
177
|
-
alignItems: 'center',
|
|
178
|
-
flexWrap: 'wrap',
|
|
179
|
-
}, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1 }, children: "The durable engine" }), _jsx(Label, { size: "small", variant: look.variant, children: look.label }), engine.data?.engine && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: engine.data.engine }))] }), look.note && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: look.note })), queues.length > 0 && (_jsx(Box, { sx: { display: 'grid', gap: 1 }, children: queues.map(queue => (_jsxs(Box, { sx: {
|
|
180
|
-
display: 'flex',
|
|
181
|
-
gap: 2,
|
|
182
|
-
alignItems: 'center',
|
|
183
|
-
fontSize: 0,
|
|
184
|
-
}, children: [_jsx(Text, { sx: { fontFamily: 'mono' }, children: queue.name }), _jsxs(Text, { sx: { color: 'fg.muted' }, children: [queue.backlog, " waiting"] }), typeof queue.oldestWaitSeconds === 'number' && (_jsxs(Text, { sx: { color: 'fg.muted' }, children: ["oldest", ' ', durationLabel(queue.oldestWaitSeconds * 1000)] }))] }, queue.name))) }))] }));
|
|
185
|
-
})(), _jsxs(Box, { sx: { display: 'grid', gap: 2 }, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1 }, children: "Running now" }), live.isError ? (_jsx(McpErrorBlankslate, { state: errorState(live.error, 'The running calls'), onRetry: () => live.refetch() })) : live.isPending ? (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', py: 4 }, children: _jsx(Spinner, {}) })) : (live.data?.items.length ?? 0) === 0 ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Nothing of yours is running. A call that finishes inside its own request never appears here \u2014 it is over before this pane could draw it \u2014 and is found by its trace in Runs." })) : ((live.data?.items ?? []).map(task => (_jsxs(Box, { sx: {
|
|
186
|
-
display: 'flex',
|
|
187
|
-
gap: 2,
|
|
188
|
-
alignItems: 'center',
|
|
189
|
-
flexWrap: 'wrap',
|
|
190
|
-
py: 1,
|
|
191
|
-
borderBottom: '1px solid',
|
|
192
|
-
borderColor: 'border.muted',
|
|
193
|
-
}, children: [_jsx(Label, { size: "small", variant: "success", children: "Working" }), _jsx(Text, { sx: { fontSize: 0 }, children: task.tool || 'Run' }), _jsx(Text, { sx: {
|
|
137
|
+
return (_jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, p: 3, display: "grid", gap: 2, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1 }, children: "The durable engine" }), _jsx(Label, { size: "small", variant: look.variant, children: look.label }), engine.data?.engine && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: engine.data.engine }))] }), look.note && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: look.note })), queues.length > 0 && (_jsx(Box, { display: "grid", gap: 1, children: queues.map(queue => (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", fontSize: 0, children: [_jsx(Text, { sx: { fontFamily: 'mono' }, children: queue.name }), _jsxs(Text, { sx: { color: 'fg.muted' }, children: [queue.backlog, " waiting"] }), typeof queue.oldestWaitSeconds === 'number' && (_jsxs(Text, { sx: { color: 'fg.muted' }, children: ["oldest", ' ', durationLabel(queue.oldestWaitSeconds * 1000)] }))] }, queue.name))) }))] }));
|
|
138
|
+
})(), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1 }, children: "Running now" }), live.isError ? (_jsx(McpErrorBlankslate, { state: errorState(live.error, 'The running calls'), onRetry: () => live.refetch() })) : live.isPending ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 4, children: _jsx(Spinner, {}) })) : (live.data?.items.length ?? 0) === 0 ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Nothing of yours is running. A call that finishes inside its own request never appears here \u2014 it is over before this pane could draw it \u2014 and is found by its trace in Runs." })) : ((live.data?.items ?? []).map(task => (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", py: 1, borderBottom: "1px solid", borderColor: "border.muted", children: [_jsx(Label, { size: "small", variant: "success", children: "Working" }), _jsx(Text, { sx: { fontSize: 0 }, children: task.tool || 'Run' }), _jsx(Text, { sx: {
|
|
194
139
|
fontSize: 0,
|
|
195
140
|
fontFamily: 'mono',
|
|
196
141
|
color: 'accent.fg',
|
|
197
142
|
cursor: 'pointer',
|
|
198
|
-
}, onClick: () => onSelect({ task: task.uid }), children: task.uid }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["started ", timeAgo(task.createdAt)] })] }, task.uid))))] })] })), pane === 'metrics' && (_jsx(Box, {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
'repeat(2, 1fr)',
|
|
204
|
-
'repeat(4, 1fr)',
|
|
205
|
-
],
|
|
206
|
-
}, children: [_jsx(Sli, { label: "Availability", value: percentLabel(metrics.data?.slis.availability), note: `${metrics.data?.slis.samples.calls ?? 0} calls` }), _jsx(Sli, { label: "Call latency, p95", value: metrics.data?.slis.p95CallDurationMs === null ||
|
|
143
|
+
}, onClick: () => onSelect({ task: task.uid }), children: task.uid }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["started ", timeAgo(task.createdAt)] })] }, task.uid))))] })] })), pane === 'metrics' && (_jsx(Box, { display: "grid", gap: 3, children: metrics.isError ? (_jsx(McpErrorBlankslate, { state: errorState(metrics.error, 'Metrics'), onRetry: () => metrics.refetch() })) : metrics.isPending && !metrics.data ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 5, children: _jsx(Spinner, {}) })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { display: "grid", gap: 3, gridTemplateColumns: [
|
|
144
|
+
'1fr',
|
|
145
|
+
'repeat(2, 1fr)',
|
|
146
|
+
'repeat(4, 1fr)',
|
|
147
|
+
], children: [_jsx(Sli, { label: "Availability", value: percentLabel(metrics.data?.slis.availability), note: `${metrics.data?.slis.samples.calls ?? 0} calls` }), _jsx(Sli, { label: "Call latency, p95", value: metrics.data?.slis.p95CallDurationMs === null ||
|
|
207
148
|
metrics.data?.slis.p95CallDurationMs === undefined
|
|
208
149
|
? 'Not measured'
|
|
209
150
|
: durationLabel(metrics.data.slis.p95CallDurationMs) }), _jsx(Sli, { label: "Task success", value: percentLabel(metrics.data?.slis.taskSuccessRate), note: `${metrics.data?.slis.samples.tasks ?? 0} tasks` }), _jsx(Sli, { label: "Sandbox launch, p95", value: Object.keys(metrics.data?.slis.p95SandboxLaunchSeconds ?? {}).length === 0
|
|
210
151
|
? 'Not measured'
|
|
211
152
|
: Object.entries(metrics.data?.slis.p95SandboxLaunchSeconds ?? {})
|
|
212
153
|
.map(([provider, seconds]) => `${provider} ${seconds.toFixed(1)}s`)
|
|
213
|
-
.join(' · '), note: `${metrics.data?.slis.samples.launches ?? 0} launches` })] }), _jsx(Box, {
|
|
214
|
-
border: '1px solid',
|
|
215
|
-
borderColor: 'border.default',
|
|
216
|
-
borderRadius: 2,
|
|
217
|
-
overflowX: 'auto',
|
|
218
|
-
}, children: MCP_METRIC_CATALOG.map(name => {
|
|
154
|
+
.join(' · '), note: `${metrics.data?.slis.samples.launches ?? 0} launches` })] }), _jsx(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, overflowX: "auto", children: MCP_METRIC_CATALOG.map(name => {
|
|
219
155
|
const reporting = metrics.data?.reporting.includes(name) ?? false;
|
|
220
|
-
return (_jsxs(Box, { sx: {
|
|
221
|
-
display: 'flex',
|
|
222
|
-
alignItems: 'center',
|
|
223
|
-
gap: 3,
|
|
224
|
-
py: 1,
|
|
225
|
-
px: 2,
|
|
226
|
-
borderBottom: '1px solid',
|
|
227
|
-
borderColor: 'border.muted',
|
|
228
|
-
}, children: [_jsx(Text, { sx: { fontSize: 0, fontFamily: 'mono', flex: 1 }, children: name }), _jsx(Text, { sx: {
|
|
156
|
+
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: 3, py: 1, px: 2, borderBottom: "1px solid", borderColor: "border.muted", children: [_jsx(Text, { sx: { fontSize: 0, fontFamily: 'mono', flex: 1 }, children: name }), _jsx(Text, { sx: {
|
|
229
157
|
fontSize: 0,
|
|
230
158
|
color: reporting ? 'success.fg' : 'fg.muted',
|
|
231
159
|
}, children: reporting ? 'reporting' : 'no point' })] }, name));
|
|
@@ -25,20 +25,13 @@ export const NotebookRuns = ({ notebookUid, routes, limit = 20, }) => {
|
|
|
25
25
|
const runs = useNotebookTasks(notebookUid);
|
|
26
26
|
const tasks = orderedRuns(runs.data?.items ?? []).slice(0, limit);
|
|
27
27
|
if (runs.isLoading) {
|
|
28
|
-
return (_jsx(Box, {
|
|
28
|
+
return (_jsx(Box, { display: "flex", justifyContent: "center", p: 3, children: _jsx(Spinner, { size: "small" }) }));
|
|
29
29
|
}
|
|
30
|
-
return (_jsxs(Box, {
|
|
30
|
+
return (_jsxs(Box, { display: "grid", gap: 2, minWidth: 0, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 1 }, children: "Runs" }), tasks.length === 0 ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Nothing has run on this notebook yet. A tool call that outlives its request appears here while it runs." })) : (tasks.map(task => {
|
|
31
31
|
const look = RUN_STATUS_LOOK[task.status];
|
|
32
|
-
return (_jsxs(Box, {
|
|
33
|
-
|
|
34
|
-
gap:
|
|
35
|
-
p: 2,
|
|
36
|
-
borderRadius: 2,
|
|
37
|
-
border: '1px solid',
|
|
38
|
-
borderColor: task.status === 'input_required'
|
|
39
|
-
? 'attention.emphasis'
|
|
40
|
-
: 'border.default',
|
|
41
|
-
}, children: [_jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center' }, children: [_jsx(Label, { size: "small", variant: look.variant, children: look.label }), _jsx(Text, { sx: { fontSize: 0 }, children: task.tool || 'Run' })] }), _jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center' }, children: [_jsxs(Link, { href: `${routes.runs}/${encodeURIComponent(task.uid)}`, sx: { fontFamily: 'mono', fontSize: 0 }, children: [task.uid.slice(0, 16), "\u2026"] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: _jsx(RelativeTime, { datetime: task.lastUpdatedAt }) })] })] }, task.uid));
|
|
32
|
+
return (_jsxs(Box, { display: "grid", gap: 1, p: 2, borderRadius: 2, border: "1px solid", borderColor: task.status === 'input_required'
|
|
33
|
+
? 'attention.emphasis'
|
|
34
|
+
: 'border.default', children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(Label, { size: "small", variant: look.variant, children: look.label }), _jsx(Text, { sx: { fontSize: 0 }, children: task.tool || 'Run' })] }), _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsxs(Link, { href: `${routes.runs}/${encodeURIComponent(task.uid)}`, sx: { fontFamily: 'mono', fontSize: 0 }, children: [task.uid.slice(0, 16), "\u2026"] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: _jsx(RelativeTime, { datetime: task.lastUpdatedAt }) })] })] }, task.uid));
|
|
42
35
|
}))] }));
|
|
43
36
|
};
|
|
44
37
|
export default NotebookRuns;
|
|
@@ -80,18 +80,12 @@ export const OrganizationPolicy = ({ errorState, orgUid, readOnly = false, showT
|
|
|
80
80
|
return (_jsx(McpErrorBlankslate, { state: errorState(layer.error, "The organization's MCP policy"), onRetry: () => layer.refetch() }));
|
|
81
81
|
}
|
|
82
82
|
if (layer.isPending && layer.data === undefined) {
|
|
83
|
-
return (_jsx(Box, {
|
|
83
|
+
return (_jsx(Box, { display: "flex", justifyContent: "center", py: 5, children: _jsx(Spinner, {}) }));
|
|
84
84
|
}
|
|
85
|
-
return (_jsxs(Box, {
|
|
85
|
+
return (_jsxs(Box, { display: "grid", gap: 3, minWidth: 0, maxWidth: "52rem", children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Organization Policy" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "What this organization narrows for every agent acting in it. Each layer only narrows \u2014 a team, or a person, can be stricter than this and never looser." })] })), conflict && (_jsxs(Flash, { variant: "warning", children: [_jsx(Text, { sx: { fontSize: 1 }, children: conflict }), _jsx(Box, { mt: 2, children: _jsx(Button, { size: "small", onClick: () => {
|
|
86
86
|
setConflict('');
|
|
87
87
|
layer.refetch();
|
|
88
|
-
}, children: "Read it again" }) })] })), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsx(PolicyForm, { draft: draft, onChange: set, disabled: readOnly, belowAllowedClients: _jsx(AdmittedClients, { scope: "organization", subjectUid: orgUid, edited: draft.allowedClients !== stored.allowedClients }) }), !readOnly && (_jsxs(Box, {
|
|
89
|
-
borderTop: '1px solid',
|
|
90
|
-
borderColor: 'border.muted',
|
|
91
|
-
pt: 3,
|
|
92
|
-
display: 'grid',
|
|
93
|
-
gap: 2,
|
|
94
|
-
}, children: [_jsx(Text, { as: "h3", sx: { fontSize: 1, fontWeight: 'semibold', m: 0 }, children: "History" }), _jsx(PolicyHistory, { orgUid: orgUid, subjectUid: orgUid })] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "Only these rules can be stored. A rule the gateway does not enforce is refused rather than kept, so this page cannot promise something that never happens." }), removing && (_jsx(Dialog, { title: "Remove this policy?", onClose: () => setRemoving(false), footerButtons: [
|
|
88
|
+
}, children: "Read it again" }) })] })), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsx(PolicyForm, { draft: draft, onChange: set, disabled: readOnly, belowAllowedClients: _jsx(AdmittedClients, { scope: "organization", subjectUid: orgUid, edited: draft.allowedClients !== stored.allowedClients }) }), !readOnly && (_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save policy" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" })), layer.data && (_jsx(Box, { marginLeft: "auto", children: _jsx(Button, { variant: "danger", onClick: () => setRemoving(true), children: "Remove policy" }) }))] })), _jsxs(Box, { borderTop: "1px solid", borderColor: "border.muted", pt: 3, display: "grid", gap: 2, children: [_jsx(Text, { as: "h3", sx: { fontSize: 1, fontWeight: 'semibold', m: 0 }, children: "History" }), _jsx(PolicyHistory, { orgUid: orgUid, subjectUid: orgUid })] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "Only these rules can be stored. A rule the gateway does not enforce is refused rather than kept, so this page cannot promise something that never happens." }), removing && (_jsx(Dialog, { title: "Remove this policy?", onClose: () => setRemoving(false), footerButtons: [
|
|
95
89
|
{
|
|
96
90
|
buttonType: 'default',
|
|
97
91
|
content: 'Keep it',
|
|
@@ -69,29 +69,22 @@ const QuotaCard = ({ label, hint, quota, }) => {
|
|
|
69
69
|
const limit = quota?.limit;
|
|
70
70
|
const used = quota?.used;
|
|
71
71
|
const fraction = quota?.fraction;
|
|
72
|
-
return (_jsxs(Box, { sx: {
|
|
73
|
-
border: '1px solid',
|
|
74
|
-
borderColor: 'border.default',
|
|
75
|
-
borderRadius: 2,
|
|
76
|
-
p: 3,
|
|
77
|
-
flex: '1 1 220px',
|
|
78
|
-
minWidth: 0,
|
|
79
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: label }), _jsx(Box, { sx: { mt: 2, display: 'flex', alignItems: 'baseline', gap: 2 }, children: quota?.unknown ? (_jsx(Text, { sx: { fontSize: 2, color: 'fg.muted' }, children: "Not available" })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 4, fontWeight: 'bold' }, children: typeof used === 'number' ? amount(used) : '—' }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: typeof limit === 'number'
|
|
72
|
+
return (_jsxs(Box, { border: "1px solid", borderColor: "border.default", borderRadius: 2, p: 3, flex: "1 1 220px", minWidth: 0, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: label }), _jsx(Box, { mt: 2, display: "flex", alignItems: "baseline", gap: 2, children: quota?.unknown ? (_jsx(Text, { sx: { fontSize: 2, color: 'fg.muted' }, children: "Not available" })) : (_jsxs(_Fragment, { children: [_jsx(Text, { sx: { fontSize: 4, fontWeight: 'bold' }, children: typeof used === 'number' ? amount(used) : '—' }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: typeof limit === 'number'
|
|
80
73
|
? `of ${amount(limit)}`
|
|
81
|
-
: 'no limit set' })] })) }), typeof fraction === 'number' && (_jsx(Box, {
|
|
74
|
+
: 'no limit set' })] })) }), typeof fraction === 'number' && (_jsx(Box, { mt: 2, children: _jsx(ProgressBar, { progress: Math.min(100, fraction * 100), barSize: "small", bg: `${quotaTone(fraction)}.emphasis`, "aria-label": `${label}: ${Math.round(fraction * 100)}% of the limit` }) })), _jsx(Text, { as: "p", sx: { mt: 2, mb: 0, fontSize: 0, color: 'fg.muted' }, children: quota?.unknown ? quota.unknown : hint })] }));
|
|
82
75
|
};
|
|
83
76
|
export const OrganizationUsage = ({ errorState, orgUid, teamUid, showTitle = true, }) => {
|
|
84
77
|
const usage = useOrgMcpUsage(orgUid, teamUid ? { team: teamUid } : {});
|
|
85
78
|
const rows = useMemo(() => spendRowsOf(usage.data?.byAgent ?? []), [usage.data]);
|
|
86
79
|
if (usage.isLoading) {
|
|
87
|
-
return (_jsx(Box, {
|
|
80
|
+
return (_jsx(Box, { display: "flex", justifyContent: "center", p: 6, children: _jsx(Spinner, {}) }));
|
|
88
81
|
}
|
|
89
82
|
if (usage.isError) {
|
|
90
83
|
return (_jsx(McpErrorBlankslate, { state: errorState(usage.error, 'The usage'), onRetry: () => void usage.refetch() }));
|
|
91
84
|
}
|
|
92
85
|
const quotas = usage.data?.quotas ?? {};
|
|
93
86
|
const breakdown = breakdownState(usage.data);
|
|
94
|
-
return (_jsxs(Box, { children: [showTitle && (_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Usage" })), _jsx(Text, { as: "p", sx: { color: 'fg.muted', mt: 0, mb: 3 }, children: "What this organization's agents used over the last day, against what they may use. Limits are set in the organization's MCP policy, not here." }), _jsx(Box, {
|
|
87
|
+
return (_jsxs(Box, { children: [showTitle && (_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Usage" })), _jsx(Text, { as: "p", sx: { color: 'fg.muted', mt: 0, mb: 3 }, children: "What this organization's agents used over the last day, against what they may use. Limits are set in the organization's MCP policy, not here." }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 3, children: QUOTAS.map(quota => (_jsx(QuotaCard, { label: quota.label, hint: quota.hint, quota: quotas[quota.key] }, quota.key))) }), _jsx(Heading, { as: "h3", sx: { fontSize: 2, mt: 4, mb: 2 }, children: "Credits by agent" }), breakdown === 'unreadable' ? (_jsxs(Blankslate, { border: true, children: [_jsx(Blankslate.Visual, { children: _jsx(GraphIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "The day's spend could not be read" }), _jsx(Blankslate.Description, { children: _jsx(Text, { as: "p", sx: { textAlign: 'center' }, children: "A breakdown of a total nobody could read would make every agent look cheap. Try again in a moment." }) })] })) : breakdown === 'empty' ? (_jsxs(Blankslate, { border: true, children: [_jsx(Blankslate.Visual, { children: _jsx(GraphIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "No agent spent credits today" }), _jsx(Blankslate.Description, { children: _jsx(Text, { as: "p", sx: { textAlign: 'center' }, children: "Work a person ran themselves is in the total above, and is not an agent's." }) })] })) : (_jsx(Table.Container, { children: _jsx(DataTable, { "aria-labelledby": "usage-by-agent", data: rows, columns: [
|
|
95
88
|
{
|
|
96
89
|
header: 'Agent',
|
|
97
90
|
field: 'clientId',
|
|
@@ -109,12 +109,12 @@ export const PersonalPolicy = ({ errorState, userUid, organizations = [], showTi
|
|
|
109
109
|
return (_jsx(McpErrorBlankslate, { state: errorState(layer.error, 'Your MCP policy'), onRetry: () => layer.refetch() }));
|
|
110
110
|
}
|
|
111
111
|
if (layer.isPending && layer.data === undefined) {
|
|
112
|
-
return (_jsx(Box, {
|
|
112
|
+
return (_jsx(Box, { display: "flex", justifyContent: "center", py: 5, children: _jsx(Spinner, {}) }));
|
|
113
113
|
}
|
|
114
|
-
return (_jsxs(Box, {
|
|
114
|
+
return (_jsxs(Box, { display: "grid", gap: 3, minWidth: 0, maxWidth: "52rem", children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Your MCP policy" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: ["Rules for your own agents. These can only make things", ' ', _jsx("strong", { children: "stricter" }), " \u2014 where you belong to an organization, its rules apply underneath and nothing here can loosen them."] })] })), conflict && (_jsxs(Flash, { variant: "warning", children: [_jsx(Text, { sx: { fontSize: 1 }, children: conflict }), _jsx(Box, { mt: 2, children: _jsx(Button, { size: "small", onClick: () => {
|
|
115
115
|
setConflict('');
|
|
116
116
|
layer.refetch();
|
|
117
|
-
}, children: "Read it again" }) })] })), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsx(PolicyForm, { draft: draft, onChange: set, notes: notes, belowAllowedClients: _jsx(AdmittedClients, { scope: "personal", subjectUid: userUid, edited: draft.allowedClients !== stored.allowedClients }) }), _jsxs(Box, {
|
|
117
|
+
}, children: "Read it again" }) })] })), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsx(PolicyForm, { draft: draft, onChange: set, notes: notes, belowAllowedClients: _jsx(AdmittedClients, { scope: "personal", subjectUid: userUid, edited: draft.allowedClients !== stored.allowedClients }) }), _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" })), layer.data && (_jsx(Box, { marginLeft: "auto", children: _jsx(Button, { variant: "danger", disabled: remove.isPending, onClick: () => remove.mutate(undefined, {
|
|
118
118
|
onSuccess: () => enqueueToast('Removed. You narrow nothing of your own.', {
|
|
119
119
|
variant: 'success',
|
|
120
120
|
}),
|
|
@@ -108,7 +108,7 @@ export const ruleValueLabel = (value) => {
|
|
|
108
108
|
return String(value);
|
|
109
109
|
};
|
|
110
110
|
/** The tools of one access kind, and what happens before they run. */
|
|
111
|
-
const ToolGroup = ({ title, description, tools, organizationName, }) => (_jsxs(Box, {
|
|
111
|
+
const ToolGroup = ({ title, description, tools, organizationName, }) => (_jsxs(Box, { 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, { display: "flex", gap: 1, flexWrap: "wrap", children: tools.map(tool => (_jsx(Label, { size: "small", variant: !tool.allowed
|
|
112
112
|
? 'danger'
|
|
113
113
|
: tool.approval === 'explicit'
|
|
114
114
|
? 'attention'
|
|
@@ -155,56 +155,15 @@ export const Policies = ({ errorState, routes, organizationName, showTitle = tru
|
|
|
155
155
|
if (policy.isError) {
|
|
156
156
|
return (_jsx(McpErrorBlankslate, { state: errorState(policy.error, 'Policies'), onRetry: () => policy.refetch() }));
|
|
157
157
|
}
|
|
158
|
-
return (_jsxs(Box, {
|
|
158
|
+
return (_jsxs(Box, { 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
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
|
|
160
|
+
: 'What applies to your agents, and which layer decided it.' })] })), (agents.data?.length ?? 0) > 1 && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap", children: [_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Preview as" }), _jsx(Box, { 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, { 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, { 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
161
|
? ` · 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",
|
|
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: {
|
|
162
|
+
: ''] })] }), _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", 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", textAlign: "left", fontSize: 0, color: "fg.muted", fontWeight: "semibold", py: 2, px: 2, borderBottom: "1px solid", borderColor: "border.default", children: header }, header))) }) }), _jsx(Box, { as: "tbody", children: rows.map(row => (_jsxs(Box, { as: "tr", children: [_jsxs(Box, { as: "td", py: 2, px: 2, borderBottom: "1px solid", borderColor: "border.muted", verticalAlign: "top", 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", py: 2, px: 2, borderBottom: "1px solid", borderColor: "border.muted", verticalAlign: "top", children: _jsx(Text, { sx: { fontSize: 1 }, children: ruleValueLabel(row.rule.value) }) }), _jsxs(Box, { as: "td", py: 2, px: 2, borderBottom: "1px solid", borderColor: "border.muted", verticalAlign: "top", whiteSpace: "nowrap", children: [_jsx(Label, { size: "small", variant: LAYER_VARIANT[row.rule.decidedBy], children: layerLabel(row.rule.decidedBy, organizationName) }), row.rule.reason && (_jsx(Text, { sx: {
|
|
196
163
|
fontSize: 0,
|
|
197
164
|
color: 'fg.muted',
|
|
198
165
|
display: 'block',
|
|
199
166
|
mt: 1,
|
|
200
|
-
}, children: row.rule.reason }))] })] }, row.name))) })] })] }), _jsxs(Box, {
|
|
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." })] })] }));
|
|
167
|
+
}, children: row.rule.reason }))] })] }, row.name))) })] })] }), _jsxs(Box, { 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, { p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.subtle", display: "grid", gap: 1, 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
168
|
};
|
|
210
169
|
export default Policies;
|
|
@@ -101,13 +101,5 @@ export const rulesFrom = (draft) => {
|
|
|
101
101
|
};
|
|
102
102
|
export const policyChanged = (a, b) => Object.keys(EMPTY_POLICY_DRAFT).some(key => a[key] !== b[key]);
|
|
103
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, {
|
|
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 })] })] })] }));
|
|
104
|
+
export const PolicyForm = ({ draft, onChange, disabled = false, notes = {}, belowAllowedClients, }) => (_jsxs(Box, { 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, { mt: 2, children: belowAllowedClients })] }), _jsxs(Box, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', '1fr 1fr 1fr'], 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, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', '1fr 1fr'], 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, { 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
105
|
export default PolicyForm;
|
|
@@ -56,7 +56,7 @@ export const PolicyHistory = ({ orgUid, subjectUid, limit = 20, }) => {
|
|
|
56
56
|
.slice(0, limit);
|
|
57
57
|
}, [events.data, removals.data, subjectUid, limit]);
|
|
58
58
|
if ((events.isPending || removals.isPending) && rows.length === 0) {
|
|
59
|
-
return (_jsx(Box, {
|
|
59
|
+
return (_jsx(Box, { display: "flex", justifyContent: "center", py: 3, children: _jsx(Spinner, { size: "small" }) }));
|
|
60
60
|
}
|
|
61
61
|
if (events.isError || removals.isError) {
|
|
62
62
|
return (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "The history could not be read. The policy above is unaffected." }));
|
|
@@ -64,12 +64,6 @@ export const PolicyHistory = ({ orgUid, subjectUid, limit = 20, }) => {
|
|
|
64
64
|
if (rows.length === 0) {
|
|
65
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
66
|
}
|
|
67
|
-
return (_jsx(Box, {
|
|
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))) }));
|
|
67
|
+
return (_jsx(Box, { display: "grid", gap: 2, children: rows.map(event => (_jsxs(Box, { display: "flex", gap: 2, alignItems: "baseline", flexWrap: "wrap", fontSize: 0, 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
68
|
};
|
|
75
69
|
export default PolicyHistory;
|