@datalayer/core 1.0.3 → 1.0.11
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/constants.d.ts +3 -0
- package/lib/api/constants.js +3 -0
- package/lib/api/runtimes/checkpoints.d.ts +122 -0
- package/lib/api/runtimes/checkpoints.js +118 -0
- package/lib/api/runtimes/index.d.ts +1 -0
- package/lib/api/runtimes/index.js +1 -0
- package/lib/api/runtimes/runtimes.d.ts +84 -0
- package/lib/api/runtimes/runtimes.js +50 -0
- package/lib/components/auth/Login.js +1 -1
- package/lib/components/display/BusyDots.d.ts +9 -0
- package/lib/components/display/BusyDots.js +31 -0
- package/lib/components/display/LiveRelativeTime.d.ts +10 -0
- package/lib/components/display/LiveRelativeTime.js +21 -0
- package/lib/components/display/index.d.ts +2 -0
- package/lib/components/display/index.js +2 -0
- package/lib/components/flashes/FlashSurveys.js +1 -1
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/navbar/SubdomainNavBar.js +1 -1
- package/lib/components/progress/ConsumptionBar.js +6 -7
- package/lib/components/progress/CreditsIndicator.js +2 -2
- package/lib/components/progress/consumption.d.ts +12 -0
- package/lib/components/progress/consumption.js +31 -0
- package/lib/components/progress/index.d.ts +1 -0
- package/lib/components/progress/index.js +1 -0
- package/lib/components/sparklines/Sparklines.d.ts +16 -0
- package/lib/components/sparklines/Sparklines.js +65 -0
- package/lib/components/sparklines/SparklinesLine.d.ts +8 -0
- package/lib/components/sparklines/SparklinesLine.js +37 -0
- package/lib/components/sparklines/dataProcessing.d.ts +25 -0
- package/lib/components/sparklines/dataProcessing.js +35 -0
- package/lib/components/sparklines/index.d.ts +4 -0
- package/lib/components/sparklines/index.js +7 -0
- package/lib/components/sparklines/types.d.ts +36 -0
- package/lib/components/sparklines/types.js +5 -0
- package/lib/components/storage/ContentsBrowser.js +17 -1
- package/lib/components/subnav/SubNav.js +1 -1
- package/lib/hooks/useCache.d.ts +5 -67
- package/lib/hooks/useCache.js +15 -213
- package/lib/hooks/useProjects.d.ts +1 -1
- package/lib/models/ItemDTO.js +1 -1
- package/lib/models/RolesPlatform.js +2 -2
- package/lib/models/User.d.ts +2 -0
- package/lib/models/User.js +4 -1
- package/lib/otel/client/OtelClient.d.ts +93 -0
- package/lib/otel/client/OtelClient.js +232 -0
- package/lib/otel/client/index.d.ts +2 -0
- package/lib/otel/client/index.js +5 -0
- package/lib/otel/{hooks.d.ts → hooks/index.d.ts} +15 -1
- package/lib/otel/{hooks.js → hooks/index.js} +58 -16
- package/lib/otel/index.d.ts +29 -20
- package/lib/otel/index.js +19 -15
- package/lib/otel/{OtelLive.d.ts → views/OtelLive.d.ts} +1 -1
- package/lib/otel/{OtelLive.js → views/OtelLive.js} +22 -4
- package/lib/otel/{OtelLogsList.d.ts → views/OtelLogsList.d.ts} +1 -1
- package/lib/otel/{OtelLogsList.js → views/OtelLogsList.js} +1 -1
- package/lib/otel/{OtelMetricsChart.d.ts → views/OtelMetricsChart.d.ts} +1 -1
- package/lib/otel/{OtelMetricsList.d.ts → views/OtelMetricsList.d.ts} +1 -1
- package/lib/otel/{OtelMetricsList.js → views/OtelMetricsList.js} +1 -1
- package/lib/otel/{OtelSearchBar.d.ts → views/OtelSearchBar.d.ts} +1 -1
- package/lib/otel/{OtelSpanDetail.d.ts → views/OtelSpanDetail.d.ts} +1 -1
- package/lib/otel/{OtelSpanDetail.js → views/OtelSpanDetail.js} +1 -1
- package/lib/otel/{OtelSpanTree.d.ts → views/OtelSpanTree.d.ts} +1 -1
- package/lib/otel/{OtelSpanTree.js → views/OtelSpanTree.js} +1 -1
- package/lib/otel/{OtelSqlView.js → views/OtelSqlView.js} +1 -1
- package/lib/otel/{OtelSystemView.js → views/OtelSystemView.js} +1 -1
- package/lib/otel/{OtelTimeline.d.ts → views/OtelTimeline.d.ts} +1 -1
- package/lib/otel/{OtelTimeline.js → views/OtelTimeline.js} +1 -1
- package/lib/otel/{OtelTimelineRangeSlider.d.ts → views/OtelTimelineRangeSlider.d.ts} +1 -1
- package/lib/otel/{OtelTracesList.d.ts → views/OtelTracesList.d.ts} +1 -1
- package/lib/otel/{OtelTracesList.js → views/OtelTracesList.js} +1 -1
- package/lib/otel/views/index.d.ts +20 -0
- package/lib/otel/views/index.js +21 -0
- package/lib/state/substates/CoreState.js +6 -6
- package/lib/utils/Date.d.ts +6 -0
- package/lib/utils/Date.js +37 -0
- package/lib/views/iam/SignInSimple.d.ts +5 -0
- package/lib/views/iam/SignInSimple.js +39 -6
- package/lib/views/iam-tokens/IAMTokenEdit.d.ts +5 -1
- package/lib/views/iam-tokens/IAMTokenEdit.js +3 -3
- package/lib/views/iam-tokens/IAMTokenNew.js +2 -2
- package/lib/views/iam-tokens/IAMTokens.d.ts +4 -2
- package/lib/views/iam-tokens/IAMTokens.js +4 -4
- package/lib/views/index.d.ts +1 -0
- package/lib/views/index.js +1 -0
- package/lib/views/otel/DashboardView.d.ts +16 -0
- package/lib/views/otel/DashboardView.js +4 -0
- package/lib/views/otel/LogsView.d.ts +12 -0
- package/lib/views/otel/LogsView.js +4 -0
- package/lib/views/otel/MetricsView.d.ts +12 -0
- package/lib/views/otel/MetricsView.js +4 -0
- package/lib/views/otel/OtelHeader.d.ts +33 -0
- package/lib/views/otel/OtelHeader.js +105 -0
- package/lib/views/otel/SqlView.d.ts +9 -0
- package/lib/views/otel/SqlView.js +4 -0
- package/lib/views/otel/SystemView.d.ts +9 -0
- package/lib/views/otel/SystemView.js +4 -0
- package/lib/views/otel/TracesView.d.ts +12 -0
- package/lib/views/otel/TracesView.js +4 -0
- package/lib/views/otel/index.d.ts +16 -0
- package/lib/views/otel/index.js +12 -0
- package/lib/views/otel/simpleAuthStore.d.ts +21 -0
- package/lib/views/otel/simpleAuthStore.js +22 -0
- package/lib/views/profile/UserBadge.d.ts +2 -0
- package/lib/views/profile/UserBadge.js +3 -3
- package/package.json +1 -26
- /package/lib/otel/{OtelMetricsChart.js → views/OtelMetricsChart.js} +0 -0
- /package/lib/otel/{OtelSearchBar.js → views/OtelSearchBar.js} +0 -0
- /package/lib/otel/{OtelSqlView.d.ts → views/OtelSqlView.d.ts} +0 -0
- /package/lib/otel/{OtelSystemView.d.ts → views/OtelSystemView.d.ts} +0 -0
- /package/lib/otel/{OtelTimelineRangeSlider.js → views/OtelTimelineRangeSlider.js} +0 -0
package/lib/hooks/useCache.js
CHANGED
|
@@ -272,13 +272,6 @@ export const queryKeys = {
|
|
|
272
272
|
bySpace: (spaceId) => [...queryKeys.items.all(), 'space', spaceId],
|
|
273
273
|
search: (opts) => [...queryKeys.items.all(), 'search', opts],
|
|
274
274
|
},
|
|
275
|
-
// Agent Runtimes (runtimes with ai-agents environment)
|
|
276
|
-
agentRuntimes: {
|
|
277
|
-
all: () => ['agentRuntimes'],
|
|
278
|
-
lists: () => [...queryKeys.agentRuntimes.all(), 'list'],
|
|
279
|
-
details: () => [...queryKeys.agentRuntimes.all(), 'detail'],
|
|
280
|
-
detail: (podName) => [...queryKeys.agentRuntimes.details(), podName],
|
|
281
|
-
},
|
|
282
275
|
// Layout
|
|
283
276
|
layout: {
|
|
284
277
|
byAccount: (accountHandle, spaceHandle) => spaceHandle
|
|
@@ -1362,206 +1355,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
1362
1355
|
},
|
|
1363
1356
|
});
|
|
1364
1357
|
};
|
|
1365
|
-
/**
|
|
1366
|
-
* List agent runtimes (runtimes with ai-agents-env environment)
|
|
1367
|
-
*/
|
|
1368
|
-
/**
|
|
1369
|
-
* Get all agent runtimes for the current user.
|
|
1370
|
-
*
|
|
1371
|
-
* Note on phase/status mapping:
|
|
1372
|
-
* The backend (operator) RuntimePod model does not include a 'phase' field.
|
|
1373
|
-
* The operator only keeps active/assigned runtimes in its cache (OperatorCache.USER_RUNTIMES),
|
|
1374
|
-
* so any runtime returned by this endpoint is inherently running or starting.
|
|
1375
|
-
* Therefore, if rt.phase is undefined (which it will be), we default to 'running'.
|
|
1376
|
-
* A 'paused' state would require explicit backend support to track paused runtimes.
|
|
1377
|
-
*/
|
|
1378
|
-
const useAgentRuntimes = () => {
|
|
1379
|
-
return useQuery({
|
|
1380
|
-
queryKey: queryKeys.agentRuntimes.lists(),
|
|
1381
|
-
queryFn: async () => {
|
|
1382
|
-
const resp = await requestDatalayer({
|
|
1383
|
-
url: `${configuration.runtimesRunUrl}/api/runtimes/v1/runtimes`,
|
|
1384
|
-
method: 'GET',
|
|
1385
|
-
});
|
|
1386
|
-
if (resp.success && resp.runtimes) {
|
|
1387
|
-
// Filter to only include ai-agents-env runtimes
|
|
1388
|
-
const agentRuntimes = resp.runtimes
|
|
1389
|
-
.filter((rt) => rt.environment_name === 'ai-agents-env')
|
|
1390
|
-
.map((rt) => ({
|
|
1391
|
-
...rt,
|
|
1392
|
-
// Phase/status mapping: see hook JSDoc for details.
|
|
1393
|
-
// Backend returns only active runtimes, so default to 'running'.
|
|
1394
|
-
status: rt.phase === 'Pending'
|
|
1395
|
-
? 'starting'
|
|
1396
|
-
: rt.phase === 'Terminated'
|
|
1397
|
-
? 'terminated'
|
|
1398
|
-
: rt.phase === 'Paused'
|
|
1399
|
-
? 'paused'
|
|
1400
|
-
: rt.phase === 'Archived'
|
|
1401
|
-
? 'archived'
|
|
1402
|
-
: 'running',
|
|
1403
|
-
name: rt.given_name || rt.pod_name,
|
|
1404
|
-
id: rt.pod_name,
|
|
1405
|
-
// Map ingress URL to url for UI consistency
|
|
1406
|
-
url: rt.ingress,
|
|
1407
|
-
messageCount: 0, // Default for UI compatibility
|
|
1408
|
-
agent_spec_id: rt.agent_spec_id || undefined,
|
|
1409
|
-
}));
|
|
1410
|
-
// Set detail cache for each runtime
|
|
1411
|
-
agentRuntimes.forEach((runtime) => {
|
|
1412
|
-
queryClient.setQueryData(queryKeys.agentRuntimes.detail(runtime.pod_name), runtime);
|
|
1413
|
-
});
|
|
1414
|
-
return agentRuntimes;
|
|
1415
|
-
}
|
|
1416
|
-
return [];
|
|
1417
|
-
},
|
|
1418
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
1419
|
-
refetchInterval: 10000, // Refetch every 10 seconds for status updates
|
|
1420
|
-
enabled: !!user,
|
|
1421
|
-
});
|
|
1422
|
-
};
|
|
1423
|
-
/**
|
|
1424
|
-
* Get a single agent runtime by pod name.
|
|
1425
|
-
*
|
|
1426
|
-
* Note on phase/status mapping:
|
|
1427
|
-
* Same as useAgentRuntimes - the backend RuntimePod model has no 'phase' field,
|
|
1428
|
-
* and only active runtimes exist in the operator cache. Default to 'running'.
|
|
1429
|
-
*/
|
|
1430
|
-
const useAgentRuntime = (podName) => {
|
|
1431
|
-
return useQuery({
|
|
1432
|
-
queryKey: queryKeys.agentRuntimes.detail(podName ?? ''),
|
|
1433
|
-
queryFn: async () => {
|
|
1434
|
-
const resp = await requestDatalayer({
|
|
1435
|
-
url: `${configuration.runtimesRunUrl}/api/runtimes/v1/runtimes/${podName}`,
|
|
1436
|
-
method: 'GET',
|
|
1437
|
-
});
|
|
1438
|
-
if (resp.runtime) {
|
|
1439
|
-
const rt = resp.runtime;
|
|
1440
|
-
return {
|
|
1441
|
-
...rt,
|
|
1442
|
-
// Phase/status mapping: see useAgentRuntimes JSDoc for details.
|
|
1443
|
-
status: rt.phase === 'Pending'
|
|
1444
|
-
? 'starting'
|
|
1445
|
-
: rt.phase === 'Terminated'
|
|
1446
|
-
? 'terminated'
|
|
1447
|
-
: rt.phase === 'Paused'
|
|
1448
|
-
? 'paused'
|
|
1449
|
-
: rt.phase === 'Archived'
|
|
1450
|
-
? 'archived'
|
|
1451
|
-
: 'running',
|
|
1452
|
-
name: rt.given_name || rt.pod_name,
|
|
1453
|
-
id: rt.pod_name,
|
|
1454
|
-
// Map ingress URL to url for UI consistency
|
|
1455
|
-
url: rt.ingress,
|
|
1456
|
-
messageCount: 0,
|
|
1457
|
-
agent_spec_id: rt.agent_spec_id || undefined,
|
|
1458
|
-
};
|
|
1459
|
-
}
|
|
1460
|
-
throw new Error('Failed to fetch agent runtime');
|
|
1461
|
-
},
|
|
1462
|
-
...DEFAULT_QUERY_OPTIONS,
|
|
1463
|
-
// Poll every 5 seconds while the runtime exists. Stop polling on error
|
|
1464
|
-
// (e.g. 404 — runtime deleted, 500 — broken state) to avoid hammering the server.
|
|
1465
|
-
refetchInterval: query => {
|
|
1466
|
-
if (query.state.error)
|
|
1467
|
-
return false;
|
|
1468
|
-
return 5000;
|
|
1469
|
-
},
|
|
1470
|
-
// Don't retry failed detail requests. The refetchInterval handles
|
|
1471
|
-
// periodic re-checks, so retrying only generates duplicate failing requests.
|
|
1472
|
-
retry: false,
|
|
1473
|
-
enabled: !!podName,
|
|
1474
|
-
});
|
|
1475
|
-
};
|
|
1476
|
-
/**
|
|
1477
|
-
* Create a new agent runtime.
|
|
1478
|
-
*
|
|
1479
|
-
* Note on phase/status mapping:
|
|
1480
|
-
* Newly created runtimes are immediately active (the operator assigns a pod from the pool).
|
|
1481
|
-
* The response won't have a 'phase' field, so we default to 'running'.
|
|
1482
|
-
* See useAgentRuntimes JSDoc for full explanation.
|
|
1483
|
-
*/
|
|
1484
|
-
const useCreateAgentRuntime = () => {
|
|
1485
|
-
return useMutation({
|
|
1486
|
-
mutationFn: async (data) => {
|
|
1487
|
-
return requestDatalayer({
|
|
1488
|
-
url: `${configuration.runtimesRunUrl}/api/runtimes/v1/runtimes`,
|
|
1489
|
-
method: 'POST',
|
|
1490
|
-
body: {
|
|
1491
|
-
environment_name: data.environmentName || 'ai-agents-env',
|
|
1492
|
-
given_name: data.givenName || 'Agent',
|
|
1493
|
-
credits_limit: data.creditsLimit || 10,
|
|
1494
|
-
type: data.type || 'notebook',
|
|
1495
|
-
editor_variant: data.editorVariant || 'none',
|
|
1496
|
-
enable_codemode: data.enableCodemode ?? false,
|
|
1497
|
-
agent_spec_id: data.agentSpecId || undefined,
|
|
1498
|
-
},
|
|
1499
|
-
});
|
|
1500
|
-
},
|
|
1501
|
-
onSuccess: resp => {
|
|
1502
|
-
if (resp.success && resp.runtime) {
|
|
1503
|
-
const rt = resp.runtime;
|
|
1504
|
-
// Phase/status mapping: see useAgentRuntimes JSDoc for details.
|
|
1505
|
-
queryClient.setQueryData(queryKeys.agentRuntimes.detail(rt.pod_name), {
|
|
1506
|
-
...rt,
|
|
1507
|
-
status: rt.phase === 'Pending'
|
|
1508
|
-
? 'starting'
|
|
1509
|
-
: 'running',
|
|
1510
|
-
name: rt.given_name || rt.pod_name,
|
|
1511
|
-
id: rt.pod_name,
|
|
1512
|
-
// Map ingress URL to url for UI consistency
|
|
1513
|
-
url: rt.ingress,
|
|
1514
|
-
messageCount: 0,
|
|
1515
|
-
agent_spec_id: rt.agent_spec_id || undefined,
|
|
1516
|
-
});
|
|
1517
|
-
// Invalidate list
|
|
1518
|
-
queryClient.invalidateQueries({
|
|
1519
|
-
queryKey: queryKeys.agentRuntimes.all(),
|
|
1520
|
-
});
|
|
1521
|
-
}
|
|
1522
|
-
},
|
|
1523
|
-
});
|
|
1524
|
-
};
|
|
1525
|
-
/**
|
|
1526
|
-
* Delete agent runtime
|
|
1527
|
-
*/
|
|
1528
|
-
const useDeleteAgentRuntime = () => {
|
|
1529
|
-
return useMutation({
|
|
1530
|
-
mutationFn: async (podName) => {
|
|
1531
|
-
return requestDatalayer({
|
|
1532
|
-
url: `${configuration.runtimesRunUrl}/api/runtimes/v1/runtimes/${podName}`,
|
|
1533
|
-
method: 'DELETE',
|
|
1534
|
-
});
|
|
1535
|
-
},
|
|
1536
|
-
onSuccess: (_data, podName) => {
|
|
1537
|
-
// Cancel any in-flight queries for the deleted runtime so they
|
|
1538
|
-
// don't re-fetch a resource that no longer exists.
|
|
1539
|
-
queryClient.cancelQueries({
|
|
1540
|
-
queryKey: queryKeys.agentRuntimes.detail(podName),
|
|
1541
|
-
});
|
|
1542
|
-
// Remove the detail cache entry immediately — prevents React
|
|
1543
|
-
// Query from triggering a stale re-fetch while the component
|
|
1544
|
-
// unmounts.
|
|
1545
|
-
queryClient.removeQueries({
|
|
1546
|
-
queryKey: queryKeys.agentRuntimes.detail(podName),
|
|
1547
|
-
});
|
|
1548
|
-
// Invalidate the list so the sidebar refreshes.
|
|
1549
|
-
queryClient.invalidateQueries({
|
|
1550
|
-
queryKey: queryKeys.agentRuntimes.lists(),
|
|
1551
|
-
});
|
|
1552
|
-
},
|
|
1553
|
-
});
|
|
1554
|
-
};
|
|
1555
|
-
/**
|
|
1556
|
-
* Refresh agent runtimes list
|
|
1557
|
-
*/
|
|
1558
|
-
const useRefreshAgentRuntimes = () => {
|
|
1559
|
-
return () => {
|
|
1560
|
-
queryClient.invalidateQueries({
|
|
1561
|
-
queryKey: queryKeys.agentRuntimes.all(),
|
|
1562
|
-
});
|
|
1563
|
-
};
|
|
1564
|
-
};
|
|
1565
1358
|
// ============================================================================
|
|
1566
1359
|
// Notebook Hooks
|
|
1567
1360
|
// ============================================================================
|
|
@@ -4712,6 +4505,20 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
4712
4505
|
},
|
|
4713
4506
|
});
|
|
4714
4507
|
};
|
|
4508
|
+
/**
|
|
4509
|
+
* Create URL for password change (sends clickable link via email)
|
|
4510
|
+
*/
|
|
4511
|
+
const useCreateUrlForPasswordChange = () => {
|
|
4512
|
+
return useMutation({
|
|
4513
|
+
mutationFn: async ({ handle, password, passwordConfirm, }) => {
|
|
4514
|
+
return requestDatalayer({
|
|
4515
|
+
url: `${configuration.iamRunUrl}/api/iam/v1/password`,
|
|
4516
|
+
method: 'PUT',
|
|
4517
|
+
body: { handle, password, passwordConfirm },
|
|
4518
|
+
});
|
|
4519
|
+
},
|
|
4520
|
+
});
|
|
4521
|
+
};
|
|
4715
4522
|
/**
|
|
4716
4523
|
* Confirm password change with token
|
|
4717
4524
|
*/
|
|
@@ -6264,6 +6071,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6264
6071
|
useJoinWithInvite,
|
|
6265
6072
|
useConfirmJoinWithToken,
|
|
6266
6073
|
useCreateTokenForPasswordChange,
|
|
6074
|
+
useCreateUrlForPasswordChange,
|
|
6267
6075
|
useConfirmPasswordWithToken,
|
|
6268
6076
|
useOAuth2AuthorizationURL,
|
|
6269
6077
|
useOAuth2AuthorizationLinkURL,
|
|
@@ -6344,12 +6152,6 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
|
|
|
6344
6152
|
useRefreshUserSpace,
|
|
6345
6153
|
useRefreshLayout,
|
|
6346
6154
|
useExportSpace,
|
|
6347
|
-
// Agent Runtimes (runtimes with ai-agents environment)
|
|
6348
|
-
useAgentRuntime,
|
|
6349
|
-
useAgentRuntimes,
|
|
6350
|
-
useCreateAgentRuntime,
|
|
6351
|
-
useDeleteAgentRuntime,
|
|
6352
|
-
useRefreshAgentRuntimes,
|
|
6353
6155
|
// Courses
|
|
6354
6156
|
useCourse,
|
|
6355
6157
|
useUpdateCourse,
|
|
@@ -20,7 +20,7 @@ export type ProjectData = {
|
|
|
20
20
|
isPublic: boolean;
|
|
21
21
|
/** Attached agent (runtime) pod name, if any */
|
|
22
22
|
attachedAgentPodName?: string;
|
|
23
|
-
/** Attached agent spec ID (e.g. '
|
|
23
|
+
/** Attached agent spec ID (e.g. 'data-acquisition'), if any */
|
|
24
24
|
attachedAgentSpecId?: string;
|
|
25
25
|
};
|
|
26
26
|
/**
|
package/lib/models/ItemDTO.js
CHANGED
|
@@ -59,7 +59,7 @@ export class ItemDTO {
|
|
|
59
59
|
if (this._data.space_id) {
|
|
60
60
|
return this._data.space_id;
|
|
61
61
|
}
|
|
62
|
-
// Extract from s3_path_s if available: "datalayer.
|
|
62
|
+
// Extract from s3_path_s if available: "datalayer.ai/SPACE_ID/..."
|
|
63
63
|
const s3Path = this._data.s3_path_s;
|
|
64
64
|
if (s3Path && typeof s3Path === 'string') {
|
|
65
65
|
const match = s3Path.match(/^datalayer\.app\/([^/]+)\//);
|
|
@@ -43,8 +43,8 @@ export class PlatformRoles {
|
|
|
43
43
|
static IAMToken = {
|
|
44
44
|
id: 'platform_iam_token',
|
|
45
45
|
handle: 'platform_iam_token',
|
|
46
|
-
displayName: 'Platform
|
|
47
|
-
description: 'A Platform
|
|
46
|
+
displayName: 'Platform API Key',
|
|
47
|
+
description: 'A Platform API Key.',
|
|
48
48
|
permissions: PLATFORM_IAM_TOKEN_PERMSSIONS,
|
|
49
49
|
icon: UserIcon,
|
|
50
50
|
};
|
package/lib/models/User.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class User implements IUser {
|
|
|
18
18
|
lastName: string;
|
|
19
19
|
initials: string;
|
|
20
20
|
displayName: string;
|
|
21
|
+
friendlyName?: string;
|
|
21
22
|
joinDate?: Date;
|
|
22
23
|
roles: string[];
|
|
23
24
|
credits?: number;
|
|
@@ -48,6 +49,7 @@ export type IBaseUser = {
|
|
|
48
49
|
lastName: string;
|
|
49
50
|
initials: string;
|
|
50
51
|
displayName: string;
|
|
52
|
+
friendlyName?: string;
|
|
51
53
|
joinDate?: Date;
|
|
52
54
|
roles: string[];
|
|
53
55
|
setRoles: (roles: string[]) => void;
|
package/lib/models/User.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
import { asDisplayName, namesAsInitials } from '../utils';
|
|
5
|
+
import { asDisplayName, namesAsInitials, toFriendlyName } from '../utils';
|
|
6
6
|
import { asIAMProviderLinked } from './IAMProviderLinked';
|
|
7
7
|
import { BOOTSTRAP_USER_ONBOARDING } from './UserOnboarding';
|
|
8
8
|
import { UserSettings } from './UserSettings';
|
|
@@ -34,6 +34,7 @@ export class User {
|
|
|
34
34
|
lastName;
|
|
35
35
|
initials;
|
|
36
36
|
displayName;
|
|
37
|
+
friendlyName;
|
|
37
38
|
joinDate;
|
|
38
39
|
roles;
|
|
39
40
|
credits;
|
|
@@ -57,6 +58,8 @@ export class User {
|
|
|
57
58
|
this.initials = namesAsInitials(u.first_name_t, u.last_name_t);
|
|
58
59
|
const displayName = asDisplayName(u.first_name_t, u.last_name_t);
|
|
59
60
|
this.displayName = displayName === '' ? u.handle_s : displayName;
|
|
61
|
+
this.friendlyName =
|
|
62
|
+
toFriendlyName(u.first_name_t, u.last_name_t) || u.handle_s;
|
|
60
63
|
this.avatarUrl = u.avatar_url_s;
|
|
61
64
|
this.origin = u.origin_s;
|
|
62
65
|
this.joinDate = u.join_ts_dt ? new Date(u.join_ts_dt) : undefined;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { OtelSpan, OtelLog, OtelMetric, OtelQueryResult } from '../types';
|
|
2
|
+
import type { OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable } from '../hooks';
|
|
3
|
+
export interface OtelClientOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Base URL of the OTEL service.
|
|
6
|
+
* Defaults to `configuration.otelRunUrl` from the Datalayer core config
|
|
7
|
+
* (i.e. `https://prod1.datalayer.run`).
|
|
8
|
+
*/
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/** JWT bearer token or API key for authentication. */
|
|
11
|
+
token?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FetchTracesOptions {
|
|
14
|
+
serviceName?: string;
|
|
15
|
+
limit?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface FetchLogsOptions {
|
|
18
|
+
serviceName?: string;
|
|
19
|
+
severity?: string;
|
|
20
|
+
traceId?: string;
|
|
21
|
+
limit?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface FetchMetricsOptions {
|
|
24
|
+
serviceName?: string;
|
|
25
|
+
metricName?: string;
|
|
26
|
+
limit?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface FetchMetricTotalOptions {
|
|
29
|
+
serviceName?: string;
|
|
30
|
+
limit?: number;
|
|
31
|
+
fallbackWithoutService?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Typed HTTP client for all OTEL service endpoints.
|
|
35
|
+
*
|
|
36
|
+
* Construct via `createOtelClient()` or `new OtelClient(options)`.
|
|
37
|
+
*/
|
|
38
|
+
export declare class OtelClient {
|
|
39
|
+
private readonly baseUrl;
|
|
40
|
+
private readonly token?;
|
|
41
|
+
constructor(options?: OtelClientOptions);
|
|
42
|
+
/** List recent spans / traces. */
|
|
43
|
+
fetchTraces(options?: FetchTracesOptions): Promise<{
|
|
44
|
+
data: OtelSpan[];
|
|
45
|
+
count: number;
|
|
46
|
+
}>;
|
|
47
|
+
/** Fetch all spans for a single trace by `traceId`. */
|
|
48
|
+
fetchTrace(traceId: string): Promise<{
|
|
49
|
+
data: OtelSpan[];
|
|
50
|
+
}>;
|
|
51
|
+
/** List recent log records. */
|
|
52
|
+
fetchLogs(options?: FetchLogsOptions): Promise<{
|
|
53
|
+
data: OtelLog[];
|
|
54
|
+
count: number;
|
|
55
|
+
}>;
|
|
56
|
+
/** List recent metric data points. */
|
|
57
|
+
fetchMetrics(options?: FetchMetricsOptions): Promise<{
|
|
58
|
+
data: OtelMetric[];
|
|
59
|
+
count: number;
|
|
60
|
+
}>;
|
|
61
|
+
/** Sum all values for one metric name, with optional service filter. */
|
|
62
|
+
fetchMetricTotal(metricName: string, options?: FetchMetricTotalOptions): Promise<number>;
|
|
63
|
+
/** List known service names observed in traces. */
|
|
64
|
+
fetchServices(): Promise<string[]>;
|
|
65
|
+
/** Fetch storage / ingestion statistics. */
|
|
66
|
+
fetchStats(): Promise<Record<string, unknown>>;
|
|
67
|
+
/**
|
|
68
|
+
* Execute an ad-hoc DataFusion SQL query against the OTEL service.
|
|
69
|
+
*
|
|
70
|
+
* Available tables: `spans`, `logs`, `metrics`.
|
|
71
|
+
*/
|
|
72
|
+
executeQuery(sql: string): Promise<OtelQueryResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Fetch system statistics (platform_admin only).
|
|
75
|
+
*
|
|
76
|
+
* Returns process memory/CPU, disk usage, and per-table row counts.
|
|
77
|
+
*/
|
|
78
|
+
fetchSystem(): Promise<OtelSystemData>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create a new `OtelClient`.
|
|
82
|
+
*
|
|
83
|
+
* The `baseUrl` defaults to `configuration.otelRunUrl` from the Datalayer core
|
|
84
|
+
* configuration store, so only pass it when you want to override the default.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* const client = createOtelClient({ token: jwt });
|
|
89
|
+
* const services = await client.fetchServices();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function createOtelClient(options?: OtelClientOptions): OtelClient;
|
|
93
|
+
export type { OtelSystemData, OtelSystemProcess, OtelSystemDisk, OtelSystemTable, };
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* OtelClient – Typed HTTP client for the Datalayer OTEL service.
|
|
7
|
+
*
|
|
8
|
+
* Reads the OTEL service URL from the Datalayer core configuration by default,
|
|
9
|
+
* so callers don't need to pass `baseUrl` unless they want to override it.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createOtelClient } from '@datalayer/core/otel';
|
|
14
|
+
*
|
|
15
|
+
* const client = createOtelClient({ token: myJwt });
|
|
16
|
+
* const { data: spans } = await client.fetchTraces({ limit: 50 });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import { coreStore } from '../../state/substates/CoreState';
|
|
20
|
+
// ── Fetch helper ────────────────────────────────────────────────────────────
|
|
21
|
+
async function otelFetch(url, token, options) {
|
|
22
|
+
const headers = {
|
|
23
|
+
Accept: 'application/json',
|
|
24
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
|
25
|
+
};
|
|
26
|
+
if (token) {
|
|
27
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
28
|
+
}
|
|
29
|
+
if (options?.body) {
|
|
30
|
+
headers['Content-Type'] = 'application/json';
|
|
31
|
+
}
|
|
32
|
+
const resp = await fetch(url, {
|
|
33
|
+
method: options?.method ?? 'GET',
|
|
34
|
+
headers,
|
|
35
|
+
body: options?.body != null ? JSON.stringify(options.body) : undefined,
|
|
36
|
+
});
|
|
37
|
+
if (!resp.ok) {
|
|
38
|
+
const text = await resp.text().catch(() => '');
|
|
39
|
+
throw new Error(`OTEL API error ${resp.status}: ${text}`);
|
|
40
|
+
}
|
|
41
|
+
return resp.json();
|
|
42
|
+
}
|
|
43
|
+
function parseMetricValue(raw) {
|
|
44
|
+
const candidates = [raw.value, raw.value_double, raw.value_int];
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
if (typeof candidate === 'number' && Number.isFinite(candidate)) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
if (typeof candidate === 'string') {
|
|
50
|
+
const parsed = Number(candidate);
|
|
51
|
+
if (Number.isFinite(parsed)) {
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
function parseMetricTimestamp(raw) {
|
|
59
|
+
const candidate = raw.timestamp ??
|
|
60
|
+
raw.Timestamp ??
|
|
61
|
+
raw.timestamp_unix_nano ??
|
|
62
|
+
raw.start_time_unix_nano;
|
|
63
|
+
if (typeof candidate === 'string' && candidate.trim().length > 0) {
|
|
64
|
+
return candidate;
|
|
65
|
+
}
|
|
66
|
+
const numeric = Number(candidate);
|
|
67
|
+
if (!Number.isFinite(numeric)) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
if (numeric > 1e15) {
|
|
71
|
+
return new Date(numeric / 1e6).toISOString();
|
|
72
|
+
}
|
|
73
|
+
if (numeric > 1e12) {
|
|
74
|
+
return new Date(numeric / 1e3).toISOString();
|
|
75
|
+
}
|
|
76
|
+
return new Date(numeric).toISOString();
|
|
77
|
+
}
|
|
78
|
+
function normalizeMetric(raw) {
|
|
79
|
+
return {
|
|
80
|
+
metric_name: String(raw.metric_name ?? raw.MetricName ?? raw.name ?? raw.Name ?? ''),
|
|
81
|
+
service_name: String(raw.service_name ?? raw.ServiceName ?? ''),
|
|
82
|
+
value: parseMetricValue(raw),
|
|
83
|
+
unit: raw.metric_unit != null
|
|
84
|
+
? String(raw.metric_unit)
|
|
85
|
+
: raw.MetricUnit != null
|
|
86
|
+
? String(raw.MetricUnit)
|
|
87
|
+
: raw.unit != null
|
|
88
|
+
? String(raw.unit)
|
|
89
|
+
: raw.Unit != null
|
|
90
|
+
? String(raw.Unit)
|
|
91
|
+
: undefined,
|
|
92
|
+
timestamp: parseMetricTimestamp(raw),
|
|
93
|
+
metric_type: raw.metric_type != null
|
|
94
|
+
? String(raw.metric_type)
|
|
95
|
+
: raw.MetricType != null
|
|
96
|
+
? String(raw.MetricType)
|
|
97
|
+
: undefined,
|
|
98
|
+
attributes: typeof raw.attributes === 'object' && raw.attributes !== null
|
|
99
|
+
? raw.attributes
|
|
100
|
+
: undefined,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ── OtelClient class ─────────────────────────────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* Typed HTTP client for all OTEL service endpoints.
|
|
106
|
+
*
|
|
107
|
+
* Construct via `createOtelClient()` or `new OtelClient(options)`.
|
|
108
|
+
*/
|
|
109
|
+
export class OtelClient {
|
|
110
|
+
baseUrl;
|
|
111
|
+
token;
|
|
112
|
+
constructor(options = {}) {
|
|
113
|
+
this.baseUrl =
|
|
114
|
+
options.baseUrl ?? coreStore.getState().configuration.otelRunUrl;
|
|
115
|
+
this.token = options.token;
|
|
116
|
+
}
|
|
117
|
+
// ── Traces ────────────────────────────────────────────────────────────────
|
|
118
|
+
/** List recent spans / traces. */
|
|
119
|
+
async fetchTraces(options = {}) {
|
|
120
|
+
const params = new URLSearchParams({ limit: String(options.limit ?? 50) });
|
|
121
|
+
if (options.serviceName)
|
|
122
|
+
params.set('service_name', options.serviceName);
|
|
123
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/traces/?${params}`, this.token);
|
|
124
|
+
const rows = (Array.isArray(resp) ? resp : resp.data) ?? [];
|
|
125
|
+
return { data: rows, count: rows.length };
|
|
126
|
+
}
|
|
127
|
+
/** Fetch all spans for a single trace by `traceId`. */
|
|
128
|
+
async fetchTrace(traceId) {
|
|
129
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/traces/${traceId}`, this.token);
|
|
130
|
+
const rows = (Array.isArray(resp) ? resp : resp.data) ?? [];
|
|
131
|
+
return { data: rows };
|
|
132
|
+
}
|
|
133
|
+
// ── Logs ──────────────────────────────────────────────────────────────────
|
|
134
|
+
/** List recent log records. */
|
|
135
|
+
async fetchLogs(options = {}) {
|
|
136
|
+
const params = new URLSearchParams({ limit: String(options.limit ?? 100) });
|
|
137
|
+
if (options.serviceName)
|
|
138
|
+
params.set('service_name', options.serviceName);
|
|
139
|
+
if (options.severity)
|
|
140
|
+
params.set('severity', options.severity);
|
|
141
|
+
if (options.traceId)
|
|
142
|
+
params.set('trace_id', options.traceId);
|
|
143
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/logs/?${params}`, this.token);
|
|
144
|
+
const rows = (Array.isArray(resp) ? resp : resp.data) ?? [];
|
|
145
|
+
return { data: rows, count: rows.length };
|
|
146
|
+
}
|
|
147
|
+
// ── Metrics ───────────────────────────────────────────────────────────────
|
|
148
|
+
/** List recent metric data points. */
|
|
149
|
+
async fetchMetrics(options = {}) {
|
|
150
|
+
const params = new URLSearchParams({ limit: String(options.limit ?? 50) });
|
|
151
|
+
if (options.serviceName)
|
|
152
|
+
params.set('service_name', options.serviceName);
|
|
153
|
+
if (options.metricName)
|
|
154
|
+
params.set('metric_name', options.metricName);
|
|
155
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/metrics/?${params}`, this.token);
|
|
156
|
+
const rows = (Array.isArray(resp) ? resp : resp.data) ?? [];
|
|
157
|
+
const normalized = Array.isArray(rows)
|
|
158
|
+
? rows.map(row => normalizeMetric(row))
|
|
159
|
+
: [];
|
|
160
|
+
return { data: normalized, count: normalized.length };
|
|
161
|
+
}
|
|
162
|
+
/** Sum all values for one metric name, with optional service filter. */
|
|
163
|
+
async fetchMetricTotal(metricName, options = {}) {
|
|
164
|
+
const { serviceName, limit = 500, fallbackWithoutService = true } = options;
|
|
165
|
+
const filtered = await this.fetchMetrics({
|
|
166
|
+
metricName,
|
|
167
|
+
serviceName,
|
|
168
|
+
limit,
|
|
169
|
+
});
|
|
170
|
+
const filteredTotal = filtered.data.reduce((sum, row) => sum + Number(row.value || 0), 0);
|
|
171
|
+
if (filteredTotal > 0 || !serviceName || !fallbackWithoutService) {
|
|
172
|
+
return filteredTotal;
|
|
173
|
+
}
|
|
174
|
+
const unfiltered = await this.fetchMetrics({ metricName, limit });
|
|
175
|
+
return unfiltered.data.reduce((sum, row) => sum + Number(row.value || 0), 0);
|
|
176
|
+
}
|
|
177
|
+
// ── Services ──────────────────────────────────────────────────────────────
|
|
178
|
+
/** List known service names observed in traces. */
|
|
179
|
+
async fetchServices() {
|
|
180
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/traces/services/list`, this.token);
|
|
181
|
+
if (Array.isArray(resp))
|
|
182
|
+
return resp;
|
|
183
|
+
const byServices = resp.services;
|
|
184
|
+
if (Array.isArray(byServices))
|
|
185
|
+
return byServices;
|
|
186
|
+
const byData = resp.data;
|
|
187
|
+
if (Array.isArray(byData)) {
|
|
188
|
+
return byData.map((r) => String(r.service_name ?? r.name ?? ''));
|
|
189
|
+
}
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
// ── Stats ─────────────────────────────────────────────────────────────────
|
|
193
|
+
/** Fetch storage / ingestion statistics. */
|
|
194
|
+
async fetchStats() {
|
|
195
|
+
return otelFetch(`${this.baseUrl}/api/otel/v1/stats/`, this.token);
|
|
196
|
+
}
|
|
197
|
+
// ── SQL query ─────────────────────────────────────────────────────────────
|
|
198
|
+
/**
|
|
199
|
+
* Execute an ad-hoc DataFusion SQL query against the OTEL service.
|
|
200
|
+
*
|
|
201
|
+
* Available tables: `spans`, `logs`, `metrics`.
|
|
202
|
+
*/
|
|
203
|
+
async executeQuery(sql) {
|
|
204
|
+
return otelFetch(`${this.baseUrl}/api/otel/v1/query/`, this.token, { method: 'POST', body: { sql } });
|
|
205
|
+
}
|
|
206
|
+
// ── System ────────────────────────────────────────────────────────────────
|
|
207
|
+
/**
|
|
208
|
+
* Fetch system statistics (platform_admin only).
|
|
209
|
+
*
|
|
210
|
+
* Returns process memory/CPU, disk usage, and per-table row counts.
|
|
211
|
+
*/
|
|
212
|
+
async fetchSystem() {
|
|
213
|
+
const resp = await otelFetch(`${this.baseUrl}/api/otel/v1/system/`, this.token);
|
|
214
|
+
return (resp.data ?? resp);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// ── Factory ──────────────────────────────────────────────────────────────────
|
|
218
|
+
/**
|
|
219
|
+
* Create a new `OtelClient`.
|
|
220
|
+
*
|
|
221
|
+
* The `baseUrl` defaults to `configuration.otelRunUrl` from the Datalayer core
|
|
222
|
+
* configuration store, so only pass it when you want to override the default.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* const client = createOtelClient({ token: jwt });
|
|
227
|
+
* const services = await client.fetchServices();
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
export function createOtelClient(options = {}) {
|
|
231
|
+
return new OtelClient(options);
|
|
232
|
+
}
|