@datalayer/core 1.2.26 → 1.2.28
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/api/evals/__tests__/external-definition.golden.json +110 -0
- package/lib/api/evals/client.d.ts +13 -1
- package/lib/api/evals/client.js +13 -0
- package/lib/api/evals/types.d.ts +168 -1
- package/lib/api/mcp/agentAccess.d.ts +10 -0
- package/lib/api/mcp/agentAccess.js +28 -0
- package/lib/api/mcp/generated.js +20 -0
- package/lib/api/mcp/index.d.ts +1 -0
- package/lib/api/mcp/index.js +1 -0
- package/lib/api/mcp/tasks.d.ts +9 -2
- package/lib/api/mcp/tasks.js +12 -3
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/mcp/ScopeList.js +10 -1
- package/lib/components/toast/ToastPanel.d.ts +11 -0
- package/lib/components/toast/ToastPanel.js +31 -0
- package/lib/components/toast/index.d.ts +2 -0
- package/lib/components/toast/index.js +6 -0
- package/lib/components/toast/toastQueue.d.ts +33 -0
- package/lib/components/toast/toastQueue.js +47 -0
- package/lib/hooks/useDatalayer.js +12 -16
- package/lib/hooks/useMcp.d.ts +2 -2
- package/lib/hooks/useMcp.js +5 -2
- package/lib/hooks/useToast.d.ts +6 -0
- package/lib/hooks/useToast.js +40 -1
- package/lib/models/McpAgentAccess.d.ts +95 -0
- package/lib/models/McpAgentAccess.js +5 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.js +1 -0
- package/lib/otel/views/OtelLive.js +2 -2
- package/lib/otel/views/OtelLiveSpans.js +2 -2
- package/lib/otel/views/OtelLogsList.js +3 -3
- package/lib/otel/views/OtelMetricsChart.d.ts +13 -0
- package/lib/otel/views/OtelMetricsChart.js +109 -26
- package/lib/otel/views/OtelSpanDetail.js +3 -3
- package/lib/otel/views/OtelSqlView.js +4 -4
- package/lib/otel/views/OtelSystemView.js +2 -2
- package/lib/otel/views/OtelTimelineRangeSlider.js +2 -2
- package/lib/otel/views/OtelTracesList.js +2 -2
- package/lib/state/substates/NavigationState.js +7 -1
- package/lib/views/mcp/AuditLog.js +1 -1
- package/lib/views/mcp/ConnectedAgents.d.ts +1 -1
- package/lib/views/mcp/ConnectedAgents.js +13 -10
- package/lib/views/mcp/McpDashboard.d.ts +1 -1
- package/lib/views/mcp/McpDashboard.js +6 -6
- package/lib/views/mcp/McpHome.d.ts +3 -1
- package/lib/views/mcp/McpHome.js +19 -9
- package/lib/views/mcp/Policies.js +1 -1
- package/lib/views/mcp/types.d.ts +8 -4
- package/lib/views/otel/OtelHeader.js +2 -2
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
* @module otel/OtelSpanDetail
|
|
13
13
|
*/
|
|
14
14
|
import { useMemo, useState } from 'react';
|
|
15
|
-
import { Box } from '@datalayer/primer-addons';
|
|
15
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
16
16
|
import { Text, IconButton, UnderlineNav, CounterLabel, Label, } from '@primer/react';
|
|
17
17
|
import { XIcon, ChevronDownIcon, ChevronRightIcon, } from '@primer/octicons-react';
|
|
18
18
|
import { formatDuration, buildSpanTree } from '../utils.js';
|
|
@@ -39,7 +39,7 @@ const CollapsibleSection = ({ title, data, defaultOpen = false }) => {
|
|
|
39
39
|
const [open, setOpen] = useState(defaultOpen);
|
|
40
40
|
if (!data || Object.keys(data).length === 0)
|
|
41
41
|
return null;
|
|
42
|
-
return (_jsxs(Box, { mt: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, cursor: "pointer", py: 1, userSelect: "none", onClick: () => setOpen(!open), children: [open ? _jsx(ChevronDownIcon, { size: 16 }) : _jsx(ChevronRightIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: title }), _jsx(CounterLabel, { children: Object.keys(data).length })] }), open && (_jsx(
|
|
42
|
+
return (_jsxs(Box, { mt: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, cursor: "pointer", py: 1, userSelect: "none", onClick: () => setOpen(!open), children: [open ? _jsx(ChevronDownIcon, { size: 16 }) : _jsx(ChevronRightIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: title }), _jsx(CounterLabel, { children: Object.keys(data).length })] }), open && (_jsx(Card, { border: true, shadow: "none", variant: "subtle", display: "block", p: 2, mt: 1, overflowX: "auto", children: Object.entries(data).map(([key, val]) => (_jsx(AttributeRow, { attrKey: key, value: val, depth: 0 }, key))) }))] }));
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
45
|
* A value as the detail unfolds it: JSON text of an object or an array (the
|
|
@@ -107,7 +107,7 @@ export const OtelSpanDetail = ({ span, traceSpans, onClose, }) => {
|
|
|
107
107
|
}
|
|
108
108
|
// Build tree from traceSpans for tree tab
|
|
109
109
|
const tree = traceSpans && traceSpans.length > 0 ? buildSpanTree(traceSpans) : undefined;
|
|
110
|
-
return (_jsxs(Box, { height: "100%", overflow: "auto", borderLeft: "1px solid", borderColor: "border.default", bg: "canvas.default", display: "flex", flexDirection: "column", children: [_jsxs(Box, { px: 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", display: "flex", justifyContent: "space-between", alignItems: "center", bg: "canvas.subtle", flexShrink: 0, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: span.span_name }), onClose && (_jsx(IconButton, { icon: XIcon, "aria-label": "Close detail panel", variant: "invisible", size: "small", onClick: onClose }))] }), _jsxs(UnderlineNav, { "aria-label": "Span detail tabs", children: [_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'details' ? 'page' : undefined, onClick: () => setActiveTab('details'), children: "Details" }), tree && (_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'tree' ? 'page' : undefined, onClick: () => setActiveTab('tree'), children: "Trace Tree" })), _jsx(UnderlineNav.Item, { "aria-current": activeTab === 'raw' ? 'page' : undefined, onClick: () => setActiveTab('raw'), children: "Raw Data" })] }), _jsxs(Box, { flex: 1, overflow: "auto", children: [activeTab === 'details' && (_jsxs(Box, { p: 3, children: [_jsx(MetadataRow, { label: "span_name", value: span.span_name }), _jsx(MetadataRow, { label: "service_name", value: span.service_name }), _jsx(MetadataRow, { label: "otel_scope_name", value: span.otel_scope_name }), _jsx(MetadataRow, { label: "kind", value: span.kind }), _jsx(MetadataRow, { label: "trace_id", value: span.trace_id, mono: true }), _jsx(MetadataRow, { label: "span_id", value: span.span_id, mono: true }), _jsx(MetadataRow, { label: "parent_span_id", value: span.parent_span_id, mono: true }), _jsx(MetadataRow, { label: "duration", value: formatDuration(span.duration_ms) }), _jsx(MetadataRow, { label: "start_time", value: span.start_time, mono: true }), _jsx(MetadataRow, { label: "end_time", value: span.end_time, mono: true }), _jsx(MetadataRow, { label: "status_code", value: span.status_code }), _jsx(MetadataRow, { label: "status_message", value: span.status_message }), _jsx(CollapsibleSection, { title: "Arguments", data: genAiAttrs, defaultOpen: Object.keys(genAiAttrs).length > 0 }), _jsx(CollapsibleSection, { title: "Attributes", data: otherAttrs }), span.events && span.events.length > 0 && (_jsxs(Box, { mt: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "Events" }), _jsx(CounterLabel, { children: span.events.length })] }), span.events.map((ev, idx) => (_jsxs(
|
|
110
|
+
return (_jsxs(Box, { height: "100%", overflow: "auto", borderLeft: "1px solid", borderColor: "border.default", bg: "canvas.default", display: "flex", flexDirection: "column", children: [_jsxs(Box, { px: 3, py: 2, borderBottom: "1px solid", borderColor: "border.default", display: "flex", justifyContent: "space-between", alignItems: "center", bg: "canvas.subtle", flexShrink: 0, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: span.span_name }), onClose && (_jsx(IconButton, { icon: XIcon, "aria-label": "Close detail panel", variant: "invisible", size: "small", onClick: onClose }))] }), _jsxs(UnderlineNav, { "aria-label": "Span detail tabs", children: [_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'details' ? 'page' : undefined, onClick: () => setActiveTab('details'), children: "Details" }), tree && (_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'tree' ? 'page' : undefined, onClick: () => setActiveTab('tree'), children: "Trace Tree" })), _jsx(UnderlineNav.Item, { "aria-current": activeTab === 'raw' ? 'page' : undefined, onClick: () => setActiveTab('raw'), children: "Raw Data" })] }), _jsxs(Box, { flex: 1, overflow: "auto", children: [activeTab === 'details' && (_jsxs(Box, { p: 3, children: [_jsx(MetadataRow, { label: "span_name", value: span.span_name }), _jsx(MetadataRow, { label: "service_name", value: span.service_name }), _jsx(MetadataRow, { label: "otel_scope_name", value: span.otel_scope_name }), _jsx(MetadataRow, { label: "kind", value: span.kind }), _jsx(MetadataRow, { label: "trace_id", value: span.trace_id, mono: true }), _jsx(MetadataRow, { label: "span_id", value: span.span_id, mono: true }), _jsx(MetadataRow, { label: "parent_span_id", value: span.parent_span_id, mono: true }), _jsx(MetadataRow, { label: "duration", value: formatDuration(span.duration_ms) }), _jsx(MetadataRow, { label: "start_time", value: span.start_time, mono: true }), _jsx(MetadataRow, { label: "end_time", value: span.end_time, mono: true }), _jsx(MetadataRow, { label: "status_code", value: span.status_code }), _jsx(MetadataRow, { label: "status_message", value: span.status_message }), _jsx(CollapsibleSection, { title: "Arguments", data: genAiAttrs, defaultOpen: Object.keys(genAiAttrs).length > 0 }), _jsx(CollapsibleSection, { title: "Attributes", data: otherAttrs }), span.events && span.events.length > 0 && (_jsxs(Box, { mt: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "Events" }), _jsx(CounterLabel, { children: span.events.length })] }), span.events.map((ev, idx) => (_jsxs(Card, { border: true, shadow: "none", variant: "subtle", display: "block", p: 2, mt: 2, children: [_jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Label, { children: ev.name }), _jsx(Text, { sx: {
|
|
111
111
|
fontSize: 0,
|
|
112
112
|
color: 'fg.muted',
|
|
113
113
|
fontFamily: 'mono',
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
* Available tables: `spans`, `metrics`, `logs`.
|
|
15
15
|
*/
|
|
16
16
|
import { useState, useCallback } from 'react';
|
|
17
|
-
import { Box } from '@datalayer/primer-addons';
|
|
17
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
18
18
|
import { ActionList, ActionMenu, Button, Text, Textarea, Spinner, } from '@primer/react';
|
|
19
19
|
import { HistoryIcon, TableIcon, XCircleIcon, DownloadIcon, } from '@primer/octicons-react';
|
|
20
20
|
import { useOtelQuery } from '../hooks/index.js';
|
|
@@ -105,7 +105,7 @@ export const OtelSqlView = ({ token, baseUrl = '', }) => {
|
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
const columns = rows.length > 0 ? Object.keys(rows[0]) : [];
|
|
108
|
-
return (_jsxs(Box, { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, height: "100%", gap: 3, p: 4, bg: "canvas.default", color: "fg.default", overflow: "auto", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap", children: [_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { size: "small", children: "Preset queries" }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsx(ActionList, { children: PRESET_GROUPS.map(group => (_jsx(ActionList.Group, { title: group, children: PRESETS.filter(p => p.group === group).map(p => (_jsx(ActionList.Item, { onSelect: () => setSql(p.sql), children: p.label }, p.label))) }, group))) }) })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["\u2318 Enter to run \u00B7 Tables:", ' ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "spans" }), ', ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "metrics" }), ', ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "logs" })] })] }), _jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsx(Textarea, { "aria-label": "SQL query", value: sql, onChange: e => setSql(e.target.value), onKeyDown: handleKeyDown, placeholder: "SELECT \u2026 FROM spans LIMIT 20", rows: 5, resize: "vertical", sx: { fontFamily: 'mono', fontSize: 1, width: '100%' } }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Button, { variant: "primary", onClick: handleRun, disabled: loading, size: "small", children: loading ? 'Running…' : 'Run' }), loading && _jsx(Spinner, { size: "small" })] })] }), error && (_jsx(
|
|
108
|
+
return (_jsxs(Box, { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, height: "100%", gap: 3, p: 4, bg: "canvas.default", color: "fg.default", overflow: "auto", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, flexWrap: "wrap", children: [_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { size: "small", children: "Preset queries" }), _jsx(ActionMenu.Overlay, { width: "large", children: _jsx(ActionList, { children: PRESET_GROUPS.map(group => (_jsx(ActionList.Group, { title: group, children: PRESETS.filter(p => p.group === group).map(p => (_jsx(ActionList.Item, { onSelect: () => setSql(p.sql), children: p.label }, p.label))) }, group))) }) })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["\u2318 Enter to run \u00B7 Tables:", ' ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "spans" }), ', ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "metrics" }), ', ', _jsx(Box, { as: "code", fontFamily: "mono", fontSize: 0, children: "logs" })] })] }), _jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsx(Textarea, { "aria-label": "SQL query", value: sql, onChange: e => setSql(e.target.value), onKeyDown: handleKeyDown, placeholder: "SELECT \u2026 FROM spans LIMIT 20", rows: 5, resize: "vertical", sx: { fontFamily: 'mono', fontSize: 1, width: '100%' } }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Button, { variant: "primary", onClick: handleRun, disabled: loading, size: "small", children: loading ? 'Running…' : 'Run' }), loading && _jsx(Spinner, { size: "small" })] })] }), error && (_jsx(Card, { border: true, shadow: "none", display: "block", p: 3, bg: "danger.subtle", borderColor: "danger.muted", children: _jsx(Text, { sx: { color: 'danger.fg', fontFamily: 'mono', fontSize: 1 }, children: error }) })), !error && rows.length > 0 && (_jsxs(Box, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, mb: 1, children: [_jsx(TableIcon, { size: 14 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold', color: 'fg.default' }, children: "Results" }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted', ml: 1 }, children: ["(", rows.length, " row", rows.length !== 1 ? 's' : '', ")"] }), _jsxs(Box, { as: "button", onClick: clear, title: "Clear results", ml: "auto", display: "flex", alignItems: "center", gap: 1, px: 2, py: "2px", border: "none", borderRadius: 2, bg: "transparent", color: "fg.muted", cursor: "pointer", fontSize: 0, hover: { color: 'danger.fg', bg: 'danger.subtle' }, children: [_jsx(XCircleIcon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0 }, children: "Clear" })] }), _jsxs(Box, { as: "button", onClick: () => {
|
|
109
109
|
const header = columns.join(',');
|
|
110
110
|
const body = rows
|
|
111
111
|
.map(row => columns
|
|
@@ -128,12 +128,12 @@ export const OtelSqlView = ({ token, baseUrl = '', }) => {
|
|
|
128
128
|
a.download = 'otel-query-result.csv';
|
|
129
129
|
a.click();
|
|
130
130
|
URL.revokeObjectURL(url);
|
|
131
|
-
}, title: "Download as CSV", display: "flex", alignItems: "center", gap: 1, px: 2, py: "2px", border: "none", borderRadius: 2, bg: "transparent", color: "fg.muted", cursor: "pointer", fontSize: 0, hover: { color: 'accent.fg', bg: 'accent.subtle' }, children: [_jsx(DownloadIcon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0 }, children: "CSV" })] })] }), _jsx(
|
|
131
|
+
}, title: "Download as CSV", display: "flex", alignItems: "center", gap: 1, px: 2, py: "2px", border: "none", borderRadius: 2, bg: "transparent", color: "fg.muted", cursor: "pointer", fontSize: 0, hover: { color: 'accent.fg', bg: 'accent.subtle' }, children: [_jsx(DownloadIcon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0 }, children: "CSV" })] })] }), _jsx(Card, { border: true, shadow: "none", display: "block", flex: 1, minHeight: 200, overflow: "auto", children: _jsxs(Box, { as: "table", width: "100%", borderCollapse: "collapse", children: [_jsx(Box, { as: "thead", children: _jsx(Box, { as: "tr", bg: "canvas.subtle", children: columns.map(col => (_jsx(Box, { as: "th", px: 3, py: 2, textAlign: "left", fontWeight: "bold", fontSize: 0, color: "fg.muted", borderBottom: "1px solid", borderColor: "border.default", whiteSpace: "nowrap", children: col }, col))) }) }), _jsx(Box, { as: "tbody", children: rows.map((row, ri) => (_jsx(Box, { as: "tr", hover: { bg: 'canvas.subtle' }, sx: {
|
|
132
132
|
'&:not(:last-child) td': {
|
|
133
133
|
borderBottom: '1px solid',
|
|
134
134
|
borderColor: 'border.muted',
|
|
135
135
|
},
|
|
136
|
-
}, children: columns.map(col => (_jsx(Box, { as: "td", px: 3, py: 2, fontSize: 0, fontFamily: "mono", maxWidth: "300px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: String(row[col] ?? '') }, col))) }, ri))) })] }) })] })), !error && !loading && rows.length === 0 && (_jsx(Box, { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: "fg.muted", children: _jsx(Text, { sx: { fontSize: 1 }, children: "Run a query to see results." }) })), history.length > 0 && (_jsxs(Box, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, mb: 1, children: [_jsx(HistoryIcon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "History" })] }), _jsx(
|
|
136
|
+
}, children: columns.map(col => (_jsx(Box, { as: "td", px: 3, py: 2, fontSize: 0, fontFamily: "mono", maxWidth: "300px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", children: String(row[col] ?? '') }, col))) }, ri))) })] }) })] })), !error && !loading && rows.length === 0 && (_jsx(Box, { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", color: "fg.muted", children: _jsx(Text, { sx: { fontSize: 1 }, children: "Run a query to see results." }) })), history.length > 0 && (_jsxs(Box, { children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 1, mb: 1, children: [_jsx(HistoryIcon, { size: 12 }), _jsx(Text, { sx: { fontSize: 0, fontWeight: 'bold', color: 'fg.muted' }, children: "History" })] }), _jsx(Card, { border: true, shadow: "none", display: "flex", flexDirection: "column", overflow: "hidden", children: history.map((entry, i) => (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, px: 3, py: "6px", cursor: "pointer", bg: i % 2 === 0 ? 'canvas.default' : 'canvas.subtle', borderTop: i > 0 ? '1px solid' : 'none', borderColor: "border.muted", hover: { bg: 'accent.subtle' }, onClick: () => setSql(entry), children: [_jsx(Text, { sx: {
|
|
137
137
|
fontFamily: 'mono',
|
|
138
138
|
fontSize: 0,
|
|
139
139
|
color: 'fg.default',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from '@datalayer/primer-addons';
|
|
2
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
3
3
|
import { Button, Spinner, Text } from '@primer/react';
|
|
4
4
|
import { SyncIcon } from '@primer/octicons-react';
|
|
5
5
|
import { useOtelSystem } from '../hooks/index.js';
|
|
@@ -41,5 +41,5 @@ const SystemViewContent = ({ data }) => {
|
|
|
41
41
|
};
|
|
42
42
|
export const OtelSystemView = ({ baseUrl = '', token, }) => {
|
|
43
43
|
const { data, loading, error, refresh } = useOtelSystem({ baseUrl, token });
|
|
44
|
-
return (_jsxs(Box, { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, overflow: "auto", p: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 3, flexShrink: 0, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: "System Statistics" }), _jsx(Button, { size: "small", variant: "invisible", onClick: refresh, disabled: loading, leadingVisual: SyncIcon, children: "Refresh" })] }), loading && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading\u2026" })] })), !loading && error && (_jsx(
|
|
44
|
+
return (_jsxs(Box, { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, overflow: "auto", p: 3, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 3, flexShrink: 0, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: "System Statistics" }), _jsx(Button, { size: "small", variant: "invisible", onClick: refresh, disabled: loading, leadingVisual: SyncIcon, children: "Refresh" })] }), loading && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Loading\u2026" })] })), !loading && error && (_jsx(Card, { border: true, shadow: "none", display: "block", bg: "danger.subtle", borderColor: "danger.muted", p: 3, children: _jsx(Text, { sx: { color: 'danger.fg', fontSize: 1 }, children: error }) })), !loading && !error && data && _jsx(SystemViewContent, { data: data }), !loading && !error && !data && (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "No data available." }))] }));
|
|
45
45
|
};
|
|
@@ -17,7 +17,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
17
17
|
* @module otel/OtelTimelineRangeSlider
|
|
18
18
|
*/
|
|
19
19
|
import { useCallback, useRef, useState, useMemo } from 'react';
|
|
20
|
-
import { Box } from '@datalayer/primer-addons';
|
|
20
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
21
21
|
import { Text } from '@primer/react';
|
|
22
22
|
// ── Helpers ─────────────────────────────────────────────────────────
|
|
23
23
|
function clamp(v, min, max) {
|
|
@@ -180,7 +180,7 @@ export const OtelTimelineRangeSlider = ({ timelineStart, timelineEnd, selectedSt
|
|
|
180
180
|
const barWidth = 100 / (histogram.length || 1);
|
|
181
181
|
const barH = (h.count / histMax) * 100;
|
|
182
182
|
return (_jsx(Box, { position: "absolute", bottom: 0, left: `${pct}%`, width: `${barWidth}%`, height: `${barH}%`, bg: "accent.fg", opacity: 0.35, borderRadius: "1px 1px 0 0", pointerEvents: "none", zIndex: 4 }, i));
|
|
183
|
-
}), _jsx(Box, { onPointerDown: e => handlePointerDown(e, 'start'), position: "absolute", top: 0, left: `${startPct}%`, width: HANDLE_WIDTH, height: HANDLE_HEIGHT, ml: `-${HANDLE_WIDTH / 2}px`, bg: "accent.fg", borderRadius: 1, cursor: "ew-resize", zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", transition: isDragging ? 'none' : 'left 0.05s ease', boxShadow: "shadow.medium", hover: { bg: 'accent.emphasis' }, children: _jsx(Box, { width: "2px", height: "40%", bg: "fg.onEmphasis", borderRadius: 1 }) }), _jsx(Box, { onPointerDown: e => handlePointerDown(e, 'end'), position: "absolute", top: 0, left: `${endPct}%`, width: HANDLE_WIDTH, height: HANDLE_HEIGHT, ml: `-${HANDLE_WIDTH / 2}px`, bg: "accent.fg", borderRadius: 1, cursor: "ew-resize", zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", transition: isDragging ? 'none' : 'left 0.05s ease', boxShadow: "shadow.medium", hover: { bg: 'accent.emphasis' }, children: _jsx(Box, { width: "2px", height: "40%", bg: "fg.onEmphasis", borderRadius: 1 }) }), hoverPct !== null && !isDragging && (_jsxs(_Fragment, { children: [_jsx(Box, { position: "absolute", top: 0, left: `${hoverPct}%`, width: "1px", height: RAIL_HEIGHT, borderLeft: "1px dashed", borderColor: "fg.default", opacity: 0.55, pointerEvents: "none", zIndex: 5 }), _jsx(
|
|
183
|
+
}), _jsx(Box, { onPointerDown: e => handlePointerDown(e, 'start'), position: "absolute", top: 0, left: `${startPct}%`, width: HANDLE_WIDTH, height: HANDLE_HEIGHT, ml: `-${HANDLE_WIDTH / 2}px`, bg: "accent.fg", borderRadius: 1, cursor: "ew-resize", zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", transition: isDragging ? 'none' : 'left 0.05s ease', boxShadow: "shadow.medium", hover: { bg: 'accent.emphasis' }, children: _jsx(Box, { width: "2px", height: "40%", bg: "fg.onEmphasis", borderRadius: 1 }) }), _jsx(Box, { onPointerDown: e => handlePointerDown(e, 'end'), position: "absolute", top: 0, left: `${endPct}%`, width: HANDLE_WIDTH, height: HANDLE_HEIGHT, ml: `-${HANDLE_WIDTH / 2}px`, bg: "accent.fg", borderRadius: 1, cursor: "ew-resize", zIndex: 3, display: "flex", alignItems: "center", justifyContent: "center", transition: isDragging ? 'none' : 'left 0.05s ease', boxShadow: "shadow.medium", hover: { bg: 'accent.emphasis' }, children: _jsx(Box, { width: "2px", height: "40%", bg: "fg.onEmphasis", borderRadius: 1 }) }), hoverPct !== null && !isDragging && (_jsxs(_Fragment, { children: [_jsx(Box, { position: "absolute", top: 0, left: `${hoverPct}%`, width: "1px", height: RAIL_HEIGHT, borderLeft: "1px dashed", borderColor: "fg.default", opacity: 0.55, pointerEvents: "none", zIndex: 5 }), _jsx(Card, { border: true, shadow: "none", display: "block", position: "absolute", top: -36, left: `${hoverPct}%`, transform: "translateX(-50%)", bg: "canvas.overlay", px: 2, py: "2px", pointerEvents: "none", zIndex: 10, boxShadow: "shadow.medium", whiteSpace: "nowrap", children: _jsx(Text, { sx: {
|
|
184
184
|
fontSize: '11px',
|
|
185
185
|
color: 'fg.default',
|
|
186
186
|
fontFamily: 'mono',
|
|
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
* @module otel/OtelTracesList
|
|
15
15
|
*/
|
|
16
16
|
import { useMemo, useCallback, useState } from 'react';
|
|
17
|
-
import { Box } from '@datalayer/primer-addons';
|
|
17
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
18
18
|
import { Text, Label, Spinner } from '@primer/react';
|
|
19
19
|
import { Blankslate } from '@primer/react/experimental';
|
|
20
20
|
import { TelescopeIcon, ChevronRightIcon, ChevronDownIcon, } from '@primer/octicons-react';
|
|
@@ -123,7 +123,7 @@ export const OtelTracesList = ({ spans, loading, selectedSpanId, onSelectSpan, e
|
|
|
123
123
|
const usage = getTokenUsage(span);
|
|
124
124
|
if (!usage)
|
|
125
125
|
return null;
|
|
126
|
-
return (_jsxs(
|
|
126
|
+
return (_jsxs(Card, { border: true, shadow: "none", display: "inline-flex", alignItems: "center", gap: "2px", px: 1, fontSize: "10px", fontFamily: "mono", color: "fg.muted", flexShrink: 0, lineHeight: "16px", children: [usage.input != null && (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "1px", children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2197" }), _jsx(Text, { children: usage.input })] })), usage.output != null && (_jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "1px", children: [_jsx(Text, { sx: { fontSize: '9px', color: 'fg.subtle' }, children: "\u2199" }), _jsx(Text, { children: usage.output })] }))] }));
|
|
127
127
|
})(), description && (_jsx(Text, { sx: {
|
|
128
128
|
fontSize: 1,
|
|
129
129
|
color: 'fg.muted',
|
|
@@ -294,7 +294,13 @@ export function viewForRoute(route, current) {
|
|
|
294
294
|
if (path === '/research' || path.startsWith('/research/')) {
|
|
295
295
|
return 'research';
|
|
296
296
|
}
|
|
297
|
-
|
|
297
|
+
// The SVG gallery is the admin sidebar's, at its own address: filed under
|
|
298
|
+
// the Platform, the Platform button came back to it with the admin sidebar
|
|
299
|
+
// beside it (reported 28 October 2026).
|
|
300
|
+
if (path === '/admin' ||
|
|
301
|
+
path.startsWith('/admin/') ||
|
|
302
|
+
path === '/svg' ||
|
|
303
|
+
path.startsWith('/svg/')) {
|
|
298
304
|
return 'admin';
|
|
299
305
|
}
|
|
300
306
|
// The Apps shell, and the hosted applications at `/apps/<slug>` with it.
|
|
@@ -87,7 +87,7 @@ export const AuditLog = ({ errorState, routes, filters, onFiltersChange, retenti
|
|
|
87
87
|
}), children: [_jsx(Select.Option, { value: "", children: "Every decision" }), _jsx(Select.Option, { value: "allowed", children: "Allowed" }), _jsx(Select.Option, { value: "refused", children: "Refused" })] }), _jsxs(Select, { size: "small", "aria-label": "Outcome", value: filters.outcome ?? '', onChange: event => set({
|
|
88
88
|
outcome: (event.currentTarget.value ||
|
|
89
89
|
undefined),
|
|
90
|
-
}), children: [_jsx(Select.Option, { value: "", children: "Every outcome" }), _jsx(Select.Option, { value: "ok", children: "Succeeded" }), _jsx(Select.Option, { value: "error", children: "Failed" }), _jsx(Select.Option, { value: "is_error", children: "Tool error" })] }), _jsx(Box, { display: "flex", gap: 2, justifyContent: "flex-end", children: _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { size: "small", leadingVisual: DownloadIcon, disabled: exporting.isPending, children: "Export" }), _jsx(ActionMenu.Overlay, { align: "end", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Item, { onSelect: () => exportAs('jsonl'), children: ["JSONL", _jsx(ActionList.Description, { variant: "block", children: "One row per line, as a SIEM reads it." })] }), _jsxs(ActionList.Item, { onSelect: () => exportAs('csv'), children: ["CSV", _jsx(ActionList.Description, { variant: "block", children: "The same rows for a spreadsheet." })] })] }) })] }) })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Audit rows are written once and never changed or deleted by anyone, here or elsewhere. Arguments are redacted before they are written.", ' ', retention] }), page.isPending && !page.data ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 6, children: _jsx(Spinner, {}) })) : rows.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(HistoryIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "Nothing to audit yet" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Every call an agent makes is written down here with its decision and its outcome. Connect a client and ask it to list your notebooks." }) }), _jsx(Button, { size: "small", onClick: () => navigate(routes.
|
|
90
|
+
}), children: [_jsx(Select.Option, { value: "", children: "Every outcome" }), _jsx(Select.Option, { value: "ok", children: "Succeeded" }), _jsx(Select.Option, { value: "error", children: "Failed" }), _jsx(Select.Option, { value: "is_error", children: "Tool error" })] }), _jsx(Box, { display: "flex", gap: 2, justifyContent: "flex-end", children: _jsxs(ActionMenu, { children: [_jsx(ActionMenu.Button, { size: "small", leadingVisual: DownloadIcon, disabled: exporting.isPending, children: "Export" }), _jsx(ActionMenu.Overlay, { align: "end", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Item, { onSelect: () => exportAs('jsonl'), children: ["JSONL", _jsx(ActionList.Description, { variant: "block", children: "One row per line, as a SIEM reads it." })] }), _jsxs(ActionList.Item, { onSelect: () => exportAs('csv'), children: ["CSV", _jsx(ActionList.Description, { variant: "block", children: "The same rows for a spreadsheet." })] })] }) })] }) })] }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Audit rows are written once and never changed or deleted by anyone, here or elsewhere. Arguments are redacted before they are written.", ' ', retention] }), page.isPending && !page.data ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 6, children: _jsx(Spinner, {}) })) : rows.length === 0 ? (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(HistoryIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "Nothing to audit yet" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Every call an agent makes is written down here with its decision and its outcome. Connect a client and ask it to list your notebooks." }) }), _jsx(Button, { size: "small", onClick: () => navigate(routes.configuration), children: "Connect an agent" })] })) : (
|
|
91
91
|
/* The rail of the activity view: each mark on a tinted disc, the discs
|
|
92
92
|
linked by a line, so the rows carry no border of their own. */
|
|
93
93
|
_jsx(Box, { display: "grid", children: rows.map((event, index) => {
|
|
@@ -3,7 +3,7 @@ import { useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import { ActionList, ActionMenu, Button, Heading, Label, RelativeTime, Spinner, Text, } from '@primer/react';
|
|
4
4
|
import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
|
|
5
5
|
import { Box } from '@datalayer/primer-addons';
|
|
6
|
-
import { KebabHorizontalIcon,
|
|
6
|
+
import { KebabHorizontalIcon, McpIcon } from '@primer/octicons-react';
|
|
7
7
|
import { ClientBadge, McpErrorBlankslate, ScopeList, } from '../../components/mcp/index.js';
|
|
8
8
|
import { useConnectedAgents, useDisconnectAgent, useDisconnectEveryAgent, } from '../../hooks/useMcp.js';
|
|
9
9
|
import { useNavigate, useToast } from '../../hooks/index.js';
|
|
@@ -24,8 +24,8 @@ export const ConnectedAgents = ({ errorState, routes, showTitle = true, readOnly
|
|
|
24
24
|
disconnectEvery.mutate(undefined, {
|
|
25
25
|
onSuccess: answer => {
|
|
26
26
|
enqueueToast(answer.revoked === 1
|
|
27
|
-
? '1
|
|
28
|
-
: `${answer.revoked}
|
|
27
|
+
? '1 client disconnected.'
|
|
28
|
+
: `${answer.revoked} clients disconnected.`, { variant: 'success' });
|
|
29
29
|
setDisconnectingEvery(false);
|
|
30
30
|
},
|
|
31
31
|
onError: error => {
|
|
@@ -75,19 +75,22 @@ export const ConnectedAgents = ({ errorState, routes, showTitle = true, readOnly
|
|
|
75
75
|
{
|
|
76
76
|
header: 'Allowed to',
|
|
77
77
|
id: 'scopes',
|
|
78
|
-
|
|
78
|
+
// Room for its scopes, which wrap inside the cell rather than run
|
|
79
|
+
// over the dates beside it (30 October 2026).
|
|
80
|
+
width: 'grow',
|
|
81
|
+
minWidth: '240px',
|
|
79
82
|
renderCell: row => _jsx(ScopeList, { scopes: row.scopes }),
|
|
80
83
|
},
|
|
81
84
|
{
|
|
82
85
|
header: 'Connected',
|
|
83
86
|
id: 'created',
|
|
84
|
-
width: '
|
|
87
|
+
width: '140px',
|
|
85
88
|
renderCell: row => row.createdAt ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: _jsx(RelativeTime, { datetime: row.createdAt }) })) : (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "\u2014" })),
|
|
86
89
|
},
|
|
87
90
|
{
|
|
88
91
|
header: 'Last used',
|
|
89
92
|
id: 'last-used',
|
|
90
|
-
width: '
|
|
93
|
+
width: '140px',
|
|
91
94
|
renderCell: row => row.lastUsedAt ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: _jsx(RelativeTime, { datetime: row.lastUsedAt }) })) : (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: "Never" })),
|
|
92
95
|
},
|
|
93
96
|
{
|
|
@@ -99,9 +102,9 @@ export const ConnectedAgents = ({ errorState, routes, showTitle = true, readOnly
|
|
|
99
102
|
},
|
|
100
103
|
];
|
|
101
104
|
if (agents.isError) {
|
|
102
|
-
return (_jsx(McpErrorBlankslate, { state: errorState(agents.error, '
|
|
105
|
+
return (_jsx(McpErrorBlankslate, { state: errorState(agents.error, 'Authorized clients'), onRetry: () => agents.refetch() }));
|
|
103
106
|
}
|
|
104
|
-
return (_jsxs(Box, { display: "grid", gap: 3, minWidth: 0, children: [showTitle && (_jsxs(Box, { display: "flex", alignItems: "start", justifyContent: "space-between", gap: 3, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "
|
|
107
|
+
return (_jsxs(Box, { display: "grid", gap: 3, minWidth: 0, children: [showTitle && (_jsxs(Box, { display: "flex", alignItems: "start", justifyContent: "space-between", gap: 3, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "Authorized clients" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Every MCP client you have authorized, what it may do, and when it last did it." })] }), !readOnly && rows.length > 0 && (_jsx(Button, { variant: "danger", onClick: () => setDisconnectingEvery(true), disabled: disconnectEvery.isPending, children: "Disconnect all" }))] })), agents.isPending && !agents.data ? (_jsx(Box, { display: "flex", justifyContent: "center", py: 5, children: _jsx(Spinner, {}) })) : rows.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "authorized-clients", children: "Clients" }), _jsx(Table.Subtitle, { as: "p", id: "authorized-clients-subtitle", children: "A client acts with exactly the access of the person who authorized it, and no more." }), _jsx(DataTable, { "aria-labelledby": "authorized-clients", "aria-describedby": "authorized-clients-subtitle", data: rows, columns: columns })] })) : (_jsxs(Blankslate, { border: true, spacious: true, children: [_jsx(Blankslate.Visual, { children: _jsx(McpIcon, { size: "medium" }) }), _jsx(Blankslate.Heading, { children: "No client authorized" }), _jsx(Blankslate.Description, { children: _jsx(Text, { sx: { textAlign: 'center' }, children: "Connect an MCP client and approve the scopes it asks for; the grant appears here and can be revoked from here." }) }), _jsx(Button, { size: "small", onClick: () => navigate(routes.configuration), children: "Connect a client" })] })), _jsx(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: "Narrowing a grant to fewer scopes without disconnecting arrives with the organization policy." }), disconnecting && (_jsx(Dialog, { title: "Disconnect this client?", onClose: () => setDisconnecting(null), returnFocusRef: returnFocusRef, footerButtons: [
|
|
105
108
|
{
|
|
106
109
|
buttonType: 'default',
|
|
107
110
|
content: 'Keep it',
|
|
@@ -113,7 +116,7 @@ export const ConnectedAgents = ({ errorState, routes, showTitle = true, readOnly
|
|
|
113
116
|
onClick: confirmDisconnect,
|
|
114
117
|
disabled: disconnect.isPending,
|
|
115
118
|
},
|
|
116
|
-
], children: _jsxs(Text, { sx: { fontSize: 1 }, children: [disconnecting.clientName || disconnecting.clientId, " loses its grant at once and its next call is refused. An access token it still holds dies with its own short expiry. The other
|
|
119
|
+
], children: _jsxs(Text, { sx: { fontSize: 1 }, children: [disconnecting.clientName || disconnecting.clientId, " loses its grant at once and its next call is refused. An access token it still holds dies with its own short expiry. The other clients are untouched, and nothing this one already did is undone."] }) })), disconnectingEvery && (_jsxs(Dialog, { title: "Disconnect every client?", onClose: () => setDisconnectingEvery(false), returnFocusRef: returnFocusRef, footerButtons: [
|
|
117
120
|
{
|
|
118
121
|
buttonType: 'default',
|
|
119
122
|
content: 'Keep them',
|
|
@@ -125,6 +128,6 @@ export const ConnectedAgents = ({ errorState, routes, showTitle = true, readOnly
|
|
|
125
128
|
onClick: confirmDisconnectEvery,
|
|
126
129
|
disabled: disconnectEvery.isPending,
|
|
127
130
|
},
|
|
128
|
-
], children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Every
|
|
131
|
+
], children: [_jsx(Text, { sx: { fontSize: 1 }, children: "Every client you have authorized loses its grant at once \u2014 signing out everywhere. Each will ask again the next time you connect it, and this time it goes through the approval screen rather than being answered from what you agreed before, so this is also how you force a fresh approval." }), _jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', mt: 2, mb: 0 }, children: "Yours only. Nobody else\u2019s sessions end, in this organization or any other, and nothing any client already did is undone." })] }))] }));
|
|
129
132
|
};
|
|
130
133
|
export default ConnectedAgents;
|
|
@@ -45,6 +45,6 @@ export interface McpDashboardProps {
|
|
|
45
45
|
section: McpDashboardSection;
|
|
46
46
|
}
|
|
47
47
|
/** The parts of the dashboard, each its own tab. */
|
|
48
|
-
export type McpDashboardSection = 'overview' | '
|
|
48
|
+
export type McpDashboardSection = 'overview' | 'clients' | 'runs' | 'sandboxes' | 'calls';
|
|
49
49
|
export declare const McpDashboard: ({ errorState, routes, org, showTitle, section, }: McpDashboardProps) => JSX.Element;
|
|
50
50
|
export default McpDashboard;
|
|
@@ -3,7 +3,7 @@ import { useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import { ActionList, ActionMenu, Button, Heading, Label, Link, RelativeTime, Select, Spinner, Text, } from '@primer/react';
|
|
4
4
|
import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
|
|
5
5
|
import { Box, Card } from '@datalayer/primer-addons';
|
|
6
|
-
import { ChecklistIcon, KebabHorizontalIcon, PlayIcon,
|
|
6
|
+
import { ChecklistIcon, KebabHorizontalIcon, PlayIcon, McpIcon, PackageIcon, } from '@primer/octicons-react';
|
|
7
7
|
import { ShareAccessDialog, sandboxSharingUrl } from '../../components/sharing/index.js';
|
|
8
8
|
import { ClientBadge, McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
9
9
|
import { useDisconnectAgent, useMcpActivity, useTerminateBinding, } from '../../hooks/useMcp.js';
|
|
@@ -375,17 +375,17 @@ export const McpDashboard = ({ errorState, routes, org, showTitle = true, sectio
|
|
|
375
375
|
'repeat(2, 1fr)',
|
|
376
376
|
'repeat(3, 1fr)',
|
|
377
377
|
'repeat(6, 1fr)',
|
|
378
|
-
], children: [_jsx(Count, { label: "Clients connected", value: clients.length, to: routes.
|
|
378
|
+
], children: [_jsx(Count, { label: "Clients connected", value: clients.length, to: routes.clients, onOpen: navigate }), _jsx(Count, { label: "Sandboxes bound", to: routes.sandboxes, onOpen: navigate, value: sandboxes.filter(binding => (binding.state ?? 'active') === 'active').length }), _jsx(Count, { label: "Tasks running", value: data?.tasks.length ?? 0, to: routes.runs, onOpen: navigate }), _jsx(Count, { label: "Calls today", value: data?.today.calls ?? 0, to: routes.calls, onOpen: navigate }), _jsx(Count, { label: "Refusals today", value: data?.today.refusals ?? 0, to: routes.audit, onOpen: navigate, tone: (data?.today.refusals ?? 0) > 0 ? 'danger' : 'neutral' }), _jsx(Count, { label: "Credits by agents", value: data?.today.credits ?? 0, note: "today",
|
|
379
379
|
// Credits are summed from the day's calls, which the Calls tab
|
|
380
380
|
// lists.
|
|
381
|
-
to: routes.calls, onOpen: navigate })] }))), draws('
|
|
381
|
+
to: routes.calls, onOpen: navigate })] }))), draws('clients') &&
|
|
382
382
|
!loading &&
|
|
383
|
-
(clients.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-clients", children: "
|
|
383
|
+
(clients.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-clients", children: "Live connections" }), _jsxs(Table.Subtitle, { as: "p", id: "mcp-clients-subtitle", children: [plural(clients.length, 'client'), " connected to your workspace now, with its status and the last tool it called."] }), _jsx(DataTable, { "aria-labelledby": "mcp-clients", "aria-describedby": "mcp-clients-subtitle", data: clients, columns: clientColumns })] })) : (_jsx(PanelEmpty, { icon: McpIcon, heading: "No client connected", description: "Connect Claude Code, Codex, Cursor, VS Code or any MCP client to your workspace, and it appears here on its first call.", action: {
|
|
384
384
|
label: 'Set up a client',
|
|
385
|
-
onClick: () => navigate(routes.
|
|
385
|
+
onClick: () => navigate(routes.configuration),
|
|
386
386
|
} }))), draws('runs') && !loading && (_jsx(Box, { children: tasks.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-runs", children: "Running now" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-runs-subtitle", children: "A tool call that outlives its request. Open one to watch its output, cancel it, or answer it when it is waiting on you." }), _jsx(DataTable, { "aria-labelledby": "mcp-runs", "aria-describedby": "mcp-runs-subtitle", data: tasks, columns: taskColumns })] })) : (_jsx(PanelEmpty, { icon: PlayIcon, heading: "Nothing is running", description: "A tool call that outlives its request becomes a task you can watch, cancel and answer. Ask a client for one \u2014 an agent that runs a long cell \u2014 and it appears here while it runs." })) })), draws('sandboxes') &&
|
|
387
387
|
!loading &&
|
|
388
|
-
(sandboxes.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-sandboxes", children: "Sandboxes bound" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-sandboxes-subtitle", children: "The sessions your agents execute in. Terminating one ends its open work." }), _jsx(DataTable, { "aria-labelledby": "mcp-sandboxes", "aria-describedby": "mcp-sandboxes-subtitle", data: sandboxes, columns: sandboxColumns })] })) : (_jsx(PanelEmpty, { icon:
|
|
388
|
+
(sandboxes.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-sandboxes", children: "Sandboxes bound" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-sandboxes-subtitle", children: "The sessions your agents execute in. Terminating one ends its open work." }), _jsx(DataTable, { "aria-labelledby": "mcp-sandboxes", "aria-describedby": "mcp-sandboxes-subtitle", data: sandboxes, columns: sandboxColumns })] })) : (_jsx(PanelEmpty, { icon: PackageIcon, heading: "No Code Sandbox bound", description: "An agent that launches a sandbox keeps it for the session; it appears here with its provider, its capabilities and what it is doing." }))), draws('calls') &&
|
|
389
389
|
!loading &&
|
|
390
390
|
(calls.length > 0 ? (_jsxs(Table.Container, { children: [_jsx(Table.Title, { as: "h3", id: "mcp-calls", children: "Recent calls" }), _jsx(Table.Subtitle, { as: "p", id: "mcp-calls-subtitle", children: "The last calls as they landed. Every one of them has an audit row that outlives this page." }), _jsx(Table.Actions, { children: _jsxs(Box, { display: "flex", gap: 2, alignItems: "center", children: [_jsxs(Select, { size: "small", "aria-label": "Filter by client", value: callClient, onChange: event => setCallClient(event.currentTarget.value), children: [_jsx(Select.Option, { value: "", children: "Every client" }), clients.map(client => (_jsx(Select.Option, { value: client.clientId, children: client.clientName || client.clientId }, client.clientId)))] }), _jsxs(Select, { size: "small", "aria-label": "Filter by tool", value: callTool, onChange: event => setCallTool(event.currentTarget.value), children: [_jsx(Select.Option, { value: "", children: "Every tool" }), callTools.map(tool => (_jsx(Select.Option, { value: tool, children: tool }, tool)))] }), _jsxs(Select, { size: "small", "aria-label": "Filter by outcome", value: callOutcome, onChange: event => setCallOutcome(event.currentTarget.value), children: [_jsx(Select.Option, { value: "", children: "Every outcome" }), _jsx(Select.Option, { value: "ok", children: "Succeeded" }), _jsx(Select.Option, { value: "error", children: "Failed" }), _jsx(Select.Option, { value: "refused", children: "Refused" })] })] }) }), filteredCalls.length > 0 ? (_jsx(DataTable, { "aria-labelledby": "mcp-calls", "aria-describedby": "mcp-calls-subtitle", data: filteredCalls, columns: callColumns })) : (_jsx(Box, { p: 4, textAlign: "center", color: "fg.muted", fontSize: 1, children: "No call matches these filters." }))] })) : (_jsx(PanelEmpty, { icon: ChecklistIcon, heading: "No call yet", description: "Ask your agent to list your notebooks. The call, its decision and its outcome land here and in the audit log.", action: {
|
|
391
391
|
label: 'Open the audit log',
|
|
@@ -85,8 +85,10 @@ export declare const MCP_CLIENTS: {
|
|
|
85
85
|
* does: a reader who has seen one and then the other should not have to
|
|
86
86
|
* work out whether they are the same affordance.
|
|
87
87
|
*/
|
|
88
|
-
export declare const Copyable: ({ text }: {
|
|
88
|
+
export declare const Copyable: ({ text, compact, }: {
|
|
89
89
|
text: string;
|
|
90
|
+
/** As wide as the text, with the button right after it. */
|
|
91
|
+
compact?: boolean;
|
|
90
92
|
}) => JSX.Element;
|
|
91
93
|
export declare const McpHome: ({ routes, clientDocsBase, showTitle, showSummaries, showSetup, onOpenDocs, }: McpHomeProps) => JSX.Element;
|
|
92
94
|
export default McpHome;
|
package/lib/views/mcp/McpHome.js
CHANGED
|
@@ -39,12 +39,12 @@ export const MCP_CLIENTS = [
|
|
|
39
39
|
* does: a reader who has seen one and then the other should not have to
|
|
40
40
|
* work out whether they are the same affordance.
|
|
41
41
|
*/
|
|
42
|
-
export const Copyable = ({ text }) => {
|
|
42
|
+
export const Copyable = ({ text, compact = false, }) => {
|
|
43
43
|
const [copied, setCopied] = useState(false);
|
|
44
|
-
return (_jsxs(Box, { display:
|
|
44
|
+
return (_jsxs(Box, { display: compact ? 'inline-flex' : 'flex', justifySelf: compact ? 'start' : undefined, maxWidth: "100%", alignItems: "center", gap: 2, p: 2, bg: "canvas.inset", borderRadius: 2, minWidth: 0, children: [_jsx(Text, { sx: {
|
|
45
45
|
fontFamily: 'mono',
|
|
46
46
|
fontSize: 0,
|
|
47
|
-
flex: 1,
|
|
47
|
+
flex: compact ? undefined : 1,
|
|
48
48
|
minWidth: 0,
|
|
49
49
|
overflow: 'hidden',
|
|
50
50
|
textOverflow: 'ellipsis',
|
|
@@ -79,7 +79,7 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
79
79
|
const live = activity.data?.clients.length ?? 0;
|
|
80
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(Card, { border: true, shadow: "none", variant: "subtle", flexDirection: "row", display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap", p: 3, children: [_jsx(Text, { sx: { fontSize: 1 }, children: live > 0
|
|
81
81
|
? `${plural(live, 'client')} connected right now.`
|
|
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
|
|
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, { compact: true, 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
|
|
83
83
|
? 'You are signed in, so a client you set up from here authorizes through OAuth without a shared token.'
|
|
84
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(Card, { border: true, shadow: "none", p: 3, 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 => {
|
|
85
85
|
// A modified click is the reader asking for a new tab or
|
|
@@ -91,10 +91,10 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
91
91
|
}
|
|
92
92
|
event.preventDefault();
|
|
93
93
|
onOpenDocs(`${clientDocsBase}/${client.setup}`);
|
|
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: "
|
|
95
|
-
label: '
|
|
96
|
-
onClick: () => navigate(routes.
|
|
97
|
-
}, children: connected.length === 0 ? (_jsx(Text, { children: "No
|
|
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: "Clients", to: routes.clients, onOpen: navigate, action: {
|
|
95
|
+
label: 'Authorized clients',
|
|
96
|
+
onClick: () => navigate(routes.clients),
|
|
97
|
+
}, children: connected.length === 0 ? (_jsx(Text, { children: "No client 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, 'client'), " authorized."] }), connected.slice(0, 3).map(agent => (_jsxs(Text, { children: [agent.clientName || agent.clientId, " \u2014", ' ', agent.lastUsedAt
|
|
98
98
|
? `used ${timeAgo(agent.lastUsedAt)}`
|
|
99
99
|
: 'never used'] }, agent.uid)))] })) }), _jsx(Summary, { icon: PulseIcon, title: "Runs", to: routes.runs, onOpen: navigate, action: { label: 'All runs', onClick: () => navigate(routes.runs) }, children: runs.length === 0 ? (_jsx(Text, { children: "Nothing has run yet. A tool call that outlives its request becomes a run you can watch and cancel." })) : (runs.map(task => (_jsxs(Text, { children: [task.tool, " \u2014 ", task.status, " ", timeAgo(task.lastUpdatedAt)] }, task.uid)))) }), _jsxs(Summary, { icon: TelescopeIcon, title: "Observability", to: routes.observability, onOpen: navigate, action: {
|
|
100
100
|
label: 'Runs and metrics',
|
|
@@ -106,7 +106,17 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
106
106
|
: 'Latency not measured yet.' }), _jsx(Text, { children: metrics.data?.slis.availability === null ||
|
|
107
107
|
metrics.data?.slis.availability === undefined
|
|
108
108
|
? 'Error rate not measured yet.'
|
|
109
|
-
: `${((1 - metrics.data.slis.availability) * 100).toFixed(1)}% of calls failed.` })] }), _jsx(Summary, { icon: ToolsIcon, title: "Tools",
|
|
109
|
+
: `${((1 - metrics.data.slis.availability) * 100).toFixed(1)}% of calls failed.` })] }), _jsx(Summary, { icon: ToolsIcon, title: "Tools", to: routes.tools, onOpen: navigate, action: routes.tools
|
|
110
|
+
? {
|
|
111
|
+
label: 'What each agent may execute',
|
|
112
|
+
onClick: () => navigate(routes.tools),
|
|
113
|
+
}
|
|
114
|
+
: undefined, children: _jsx(Text, { children: "The toolsets each of your agents may execute, with their tools, their approval policy and their health." }) }), _jsx(Summary, { icon: RssIcon, title: "Reach", to: routes.reach, onOpen: navigate, action: routes.reach
|
|
115
|
+
? {
|
|
116
|
+
label: 'What each agent reaches',
|
|
117
|
+
onClick: () => navigate(routes.reach),
|
|
118
|
+
}
|
|
119
|
+
: undefined, children: _jsx(Text, { children: "The notebooks and sources each of your agents can actually reach, and whether it may read, edit or run them." }) }), _jsx(Summary, { icon: ShieldCheckIcon, title: "Policies", to: routes.policies, onOpen: navigate, action: {
|
|
110
120
|
label: 'What applies',
|
|
111
121
|
onClick: () => navigate(routes.policies),
|
|
112
122
|
}, children: _jsx(Text, { children: "What your agents are allowed to do, and which layer decided each rule." }) })] }))] }));
|
|
@@ -157,7 +157,7 @@ export const Policies = ({ errorState, routes, organizationName, showTitle = tru
|
|
|
157
157
|
}
|
|
158
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, { 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.
|
|
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.configuration), 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
|
|
161
161
|
? ` · evaluated ${policy.data.evaluatedAt}`
|
|
162
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: {
|
|
163
163
|
fontSize: 0,
|
package/lib/views/mcp/types.d.ts
CHANGED
|
@@ -51,20 +51,24 @@ export type McpErrorStateFn = (error: unknown, subject: string) => McpErrorState
|
|
|
51
51
|
export interface McpRoutes {
|
|
52
52
|
/** The dashboard: what the connected clients are doing right now. */
|
|
53
53
|
dashboard: string;
|
|
54
|
-
/**
|
|
55
|
-
|
|
54
|
+
/** Configuration: the endpoint and the per-client setup cards. */
|
|
55
|
+
configuration: string;
|
|
56
56
|
/** The audit log. */
|
|
57
57
|
audit: string;
|
|
58
58
|
/** The runs list; a task appends `/{uid}`. */
|
|
59
59
|
runs: string;
|
|
60
60
|
/** The observability panes. */
|
|
61
61
|
observability: string;
|
|
62
|
-
/** The
|
|
63
|
-
|
|
62
|
+
/** The MCP clients: live connections and authorized clients. */
|
|
63
|
+
clients: string;
|
|
64
64
|
/** The effective policy. */
|
|
65
65
|
policies: string;
|
|
66
66
|
/** The calls list, where an Overview count of calls or credits leads. */
|
|
67
67
|
calls?: string;
|
|
68
68
|
/** The bound code sandboxes, where the Overview's sandbox count leads. */
|
|
69
69
|
sandboxes?: string;
|
|
70
|
+
/** What each agent may execute, where the Tools summary leads. */
|
|
71
|
+
tools?: string;
|
|
72
|
+
/** What each agent can reach, where the Reach summary leads. */
|
|
73
|
+
reach?: string;
|
|
70
74
|
}
|
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
11
11
|
* pass `onSignOut` to wire up your own logout action.
|
|
12
12
|
*/
|
|
13
13
|
import { useState, useCallback, useRef } from 'react';
|
|
14
|
-
import { Box } from '@datalayer/primer-addons';
|
|
14
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
15
15
|
import { Text, Button, Dialog } from '@primer/react';
|
|
16
16
|
import { TelescopeIcon, SignOutIcon } from '@primer/octicons-react';
|
|
17
17
|
import { UserBadge } from '../profile/index.js';
|
|
@@ -67,5 +67,5 @@ export const OtelHeader = ({ baseUrl = '', token, trailing, onNavigate, onSignOu
|
|
|
67
67
|
onClick: () => setDialogOpen(false),
|
|
68
68
|
buttonType: 'default',
|
|
69
69
|
},
|
|
70
|
-
], children: [_jsxs(Box, { mb: 2, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1, color: 'fg.muted' }, children: "Request" }), _jsx(
|
|
70
|
+
], children: [_jsxs(Box, { mb: 2, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1, color: 'fg.muted' }, children: "Request" }), _jsx(Card, { border: true, shadow: "none", display: "block", as: "pre", fontFamily: "mono", fontSize: 1, p: 2, mt: 1, bg: "canvas.inset", overflow: "auto", color: "fg.default", whiteSpace: "pre-wrap", wordBreak: "break-all", children: dialogRequest })] }), _jsxs(Box, { children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1, color: 'fg.muted' }, children: "Response" }), _jsx(Box, { as: "pre", fontFamily: "mono", fontSize: 1, p: 2, mt: 1, bg: "canvas.subtle", borderRadius: 2, overflow: "auto", maxHeight: 300, color: dialogVariant === 'error' ? 'danger.fg' : 'fg.default', whiteSpace: "pre-wrap", wordBreak: "break-all", children: dialogBody })] })] }))] }));
|
|
71
71
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"@primer/behaviors": "^1.8.4",
|
|
138
138
|
"@primer/brand-primitives": "^0.51.0",
|
|
139
139
|
"@primer/css": "^21.5.1",
|
|
140
|
-
"@primer/octicons-react": "^19.
|
|
140
|
+
"@primer/octicons-react": "^19.42.0",
|
|
141
141
|
"@primer/primitives": "^10.7.0",
|
|
142
142
|
"@primer/react": "^37.31.0",
|
|
143
143
|
"@primer/react-brand": "^0.58.0",
|