@elevasis/ui 1.7.5 → 1.8.0
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/dist/api/index.js +2 -3
- package/dist/auth/index.js +3 -4
- package/dist/charts/index.js +6 -8
- package/dist/{chunk-SFF5MJEI.js → chunk-2YBPRE6H.js} +1 -2
- package/dist/{chunk-U34YGJQB.js → chunk-3I2LOKQU.js} +1 -1
- package/dist/{chunk-PVVQTENF.js → chunk-3PURTICE.js} +1 -1
- package/dist/{chunk-2JBWPFHF.js → chunk-6TMW6VQ2.js} +1 -1
- package/dist/{chunk-6HZAMY6T.js → chunk-ARZM3OTI.js} +6 -2
- package/dist/{chunk-OK3XFSJJ.js → chunk-BWZMI4KP.js} +963 -10
- package/dist/{chunk-4KPI7YCY.js → chunk-EHXOR5LA.js} +2 -3
- package/dist/{chunk-Y7UY3HI4.js → chunk-ESOQEOOX.js} +2 -2
- package/dist/{chunk-L2CM2CUA.js → chunk-GZVH423C.js} +26 -3
- package/dist/{chunk-Y6DNK5ZD.js → chunk-JRJW2H57.js} +35 -9
- package/dist/{chunk-LBPALY25.js → chunk-JUPCUF77.js} +2 -2
- package/dist/chunk-QWYJHM3S.js +1068 -0
- package/dist/{chunk-NEK6JKPW.js → chunk-WUQWCUCB.js} +1 -1
- package/dist/chunk-Z4TPHMRD.js +231 -0
- package/dist/components/index.css +65 -0
- package/dist/components/index.d.ts +261 -2
- package/dist/components/index.js +1136 -80
- package/dist/hooks/index.css +452 -0
- package/dist/hooks/index.d.ts +1831 -13
- package/dist/hooks/index.js +15 -6
- package/dist/hooks/published.css +452 -0
- package/dist/hooks/published.d.ts +866 -6
- package/dist/hooks/published.js +15 -370
- package/dist/index.css +3 -0
- package/dist/index.d.ts +1665 -2
- package/dist/index.js +14 -17
- package/dist/initialization/index.js +3 -4
- package/dist/organization/index.js +3 -4
- package/dist/profile/index.js +1 -2
- package/dist/provider/index.css +3 -0
- package/dist/provider/index.js +7 -10
- package/dist/provider/published.js +6 -9
- package/dist/utils/index.d.ts +92 -1
- package/dist/utils/index.js +1 -2
- package/package.json +7 -3
- package/dist/chunk-4VGWQ5AN.js +0 -91
- package/dist/chunk-KA7LO7U5.js +0 -28
- package/dist/chunk-LFYO3MDC.js +0 -58
- package/dist/chunk-TIRMFDM4.js +0 -33
- package/dist/chunk-TYV5NJV2.js +0 -1
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { getTimeRangeDates, observabilityKeys } from './chunk-
|
|
1
|
+
import { getTimeRangeDates, observabilityKeys } from './chunk-ARZM3OTI.js';
|
|
2
2
|
import { useSupabase } from './chunk-JGJSZ3UE.js';
|
|
3
|
-
import {
|
|
3
|
+
import { GRAPH_CONSTANTS } from './chunk-F6RBK7NJ.js';
|
|
4
|
+
import { useNotificationAdapter } from './chunk-JRJW2H57.js';
|
|
5
|
+
import { HTTP_HEADERS } from './chunk-JUPCUF77.js';
|
|
6
|
+
import { STALE_TIME_MONITORING, REFETCH_INTERVAL_DASHBOARD, REFETCH_INTERVAL_RUNNING, WS_MAX_RETRIES_BEFORE_ERROR, WS_RECONNECT_BASE_DELAY, WS_RECONNECT_MAX_DELAY, STALE_TIME_DEFAULT, STALE_TIME_ADMIN } from './chunk-Z4TPHMRD.js';
|
|
4
7
|
import { useStableAccessToken } from './chunk-FWZJH3TL.js';
|
|
5
|
-
import { useElevasisServices } from './chunk-
|
|
8
|
+
import { useElevasisServices } from './chunk-GZVH423C.js';
|
|
9
|
+
import { useOrganization } from './chunk-DD3CCMCZ.js';
|
|
10
|
+
import { useAuthContext } from './chunk-7PLEQFHO.js';
|
|
6
11
|
import { useQuery, useQueryClient, useMutation, useQueries } from '@tanstack/react-query';
|
|
7
12
|
import { z } from 'zod';
|
|
8
|
-
import { useCallback, useMemo, useState, useEffect, useId
|
|
13
|
+
import { useCallback, useMemo, useState, useRef, useEffect, useId } from 'react';
|
|
14
|
+
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
15
|
+
import { create } from 'zustand';
|
|
16
|
+
import { persist } from 'zustand/middleware';
|
|
17
|
+
import dagre from '@dagrejs/dagre';
|
|
9
18
|
|
|
10
19
|
function useCommandQueue({
|
|
11
20
|
status,
|
|
@@ -202,7 +211,7 @@ function useExecutions(resourceId, resourceStatus, limit) {
|
|
|
202
211
|
enabled: isReady && !!resourceId
|
|
203
212
|
});
|
|
204
213
|
}
|
|
205
|
-
var
|
|
214
|
+
var REFETCH_INTERVAL_RUNNING2 = 2e3;
|
|
206
215
|
function useExecution(resourceId, executionId) {
|
|
207
216
|
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
208
217
|
return useQuery({
|
|
@@ -211,7 +220,7 @@ function useExecution(resourceId, executionId) {
|
|
|
211
220
|
enabled: isReady && !!resourceId && !!executionId,
|
|
212
221
|
refetchInterval: (query) => {
|
|
213
222
|
const data = query.state.data;
|
|
214
|
-
return data?.status === "running" ?
|
|
223
|
+
return data?.status === "running" ? REFETCH_INTERVAL_RUNNING2 : false;
|
|
215
224
|
}
|
|
216
225
|
});
|
|
217
226
|
}
|
|
@@ -323,7 +332,7 @@ var UuidSchema = z.string().uuid();
|
|
|
323
332
|
var NonEmptyStringSchema = z.string().trim().min(1).max(1e3);
|
|
324
333
|
var ResourceTypeSchema = z.enum(["agent", "workflow"]);
|
|
325
334
|
var OriginResourceTypeSchema = z.enum(["agent", "workflow", "scheduler", "api"]);
|
|
326
|
-
z.string().trim().toLowerCase().min(1, "Credential name required").max(100, "Credential name too long (max 100 chars)").regex(
|
|
335
|
+
var CredentialNameSchema = z.string().trim().toLowerCase().min(1, "Credential name required").max(100, "Credential name too long (max 100 chars)").regex(
|
|
327
336
|
/^[a-z0-9]+(-[a-z0-9]+)+$/,
|
|
328
337
|
"Credential name must be lowercase letters, numbers, and hyphens in format: service-environment (e.g., gmail-prod, attio-dev)"
|
|
329
338
|
);
|
|
@@ -946,6 +955,24 @@ function useBatchDelete(tableName, invalidateQueryKeys) {
|
|
|
946
955
|
}
|
|
947
956
|
});
|
|
948
957
|
}
|
|
958
|
+
function useTestNotification() {
|
|
959
|
+
const queryClient = useQueryClient();
|
|
960
|
+
const { apiRequest } = useElevasisServices();
|
|
961
|
+
const adapter = useNotificationAdapter();
|
|
962
|
+
return useMutation({
|
|
963
|
+
mutationFn: async () => {
|
|
964
|
+
await apiRequest("/notifications/test", {
|
|
965
|
+
method: "POST"
|
|
966
|
+
});
|
|
967
|
+
},
|
|
968
|
+
onSuccess: () => {
|
|
969
|
+
queryClient.invalidateQueries({ queryKey: ["notifications"] });
|
|
970
|
+
},
|
|
971
|
+
onError: (error) => {
|
|
972
|
+
adapter.apiError(error);
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
}
|
|
949
976
|
function useErrorAnalysis(timeRange) {
|
|
950
977
|
const { apiRequest, isReady, organizationId } = useElevasisServices();
|
|
951
978
|
return useQuery({
|
|
@@ -1206,6 +1233,62 @@ function useBatchedResourcesHealth(params) {
|
|
|
1206
1233
|
}, [queryResults]);
|
|
1207
1234
|
return { healthLookup };
|
|
1208
1235
|
}
|
|
1236
|
+
function useUnresolvedErrors({ startDate, endDate }) {
|
|
1237
|
+
const { apiRequest, isReady, organizationId } = useElevasisServices();
|
|
1238
|
+
return useQuery({
|
|
1239
|
+
queryKey: observabilityKeys.unresolvedErrors(organizationId, startDate, endDate),
|
|
1240
|
+
queryFn: async () => {
|
|
1241
|
+
const params = new URLSearchParams({
|
|
1242
|
+
resolved: "false",
|
|
1243
|
+
limit: "5",
|
|
1244
|
+
page: "1",
|
|
1245
|
+
startDate,
|
|
1246
|
+
endDate
|
|
1247
|
+
});
|
|
1248
|
+
return apiRequest(`/observability/error-details?${params.toString()}`);
|
|
1249
|
+
},
|
|
1250
|
+
enabled: isReady,
|
|
1251
|
+
refetchInterval: REFETCH_INTERVAL_DASHBOARD,
|
|
1252
|
+
staleTime: STALE_TIME_MONITORING
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
function useRecentExecutionsByResource({ timeRange, limit }) {
|
|
1256
|
+
const { apiRequest, isReady, organizationId } = useElevasisServices();
|
|
1257
|
+
return useQuery({
|
|
1258
|
+
queryKey: observabilityKeys.recentExecutionsByResource(organizationId, timeRange, limit),
|
|
1259
|
+
queryFn: async () => {
|
|
1260
|
+
const { startDate, endDate } = getTimeRangeDates(timeRange);
|
|
1261
|
+
const params = new URLSearchParams({
|
|
1262
|
+
startDate,
|
|
1263
|
+
endDate,
|
|
1264
|
+
...limit !== void 0 && { limit: String(limit) }
|
|
1265
|
+
});
|
|
1266
|
+
return apiRequest(
|
|
1267
|
+
`/observability/recent-executions-by-resource?${params.toString()}`
|
|
1268
|
+
);
|
|
1269
|
+
},
|
|
1270
|
+
enabled: isReady,
|
|
1271
|
+
refetchInterval: REFETCH_INTERVAL_DASHBOARD
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
function useScheduledTasks(options = {}) {
|
|
1275
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
1276
|
+
return useQuery({
|
|
1277
|
+
queryKey: ["schedules", "dashboard", organizationId, options.status, options.targetResourceType],
|
|
1278
|
+
queryFn: async () => {
|
|
1279
|
+
const params = new URLSearchParams();
|
|
1280
|
+
if (options.status) params.append("status", options.status);
|
|
1281
|
+
if (options.targetResourceType) params.append("targetResourceType", options.targetResourceType);
|
|
1282
|
+
const queryString = params.toString();
|
|
1283
|
+
const response = await apiRequest(
|
|
1284
|
+
`/task-scheduler/schedules${queryString ? `?${queryString}` : ""}`
|
|
1285
|
+
);
|
|
1286
|
+
return response.schedules;
|
|
1287
|
+
},
|
|
1288
|
+
enabled: isReady,
|
|
1289
|
+
refetchInterval: REFETCH_INTERVAL_DASHBOARD
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1209
1292
|
|
|
1210
1293
|
// src/hooks/scheduling/queryKeys.ts
|
|
1211
1294
|
var scheduleKeys = {
|
|
@@ -1384,6 +1467,358 @@ function useDeleteSchedule() {
|
|
|
1384
1467
|
}
|
|
1385
1468
|
});
|
|
1386
1469
|
}
|
|
1470
|
+
|
|
1471
|
+
// src/hooks/sessions/queryKeys.ts
|
|
1472
|
+
var sessionsKeys = {
|
|
1473
|
+
all: ["sessions"],
|
|
1474
|
+
sessions: (org, params) => [...sessionsKeys.all, "list", org, params],
|
|
1475
|
+
session: (org, sessionId) => [...sessionsKeys.all, "detail", org, sessionId],
|
|
1476
|
+
executions: (org, sessionId) => [...sessionsKeys.all, org, sessionId, "executions"],
|
|
1477
|
+
execution: (org, sessionId, executionId) => [...sessionsKeys.all, org, sessionId, "executions", executionId],
|
|
1478
|
+
messages: (org, sessionId) => [...sessionsKeys.all, org, sessionId, "messages"]
|
|
1479
|
+
};
|
|
1480
|
+
function useSessions(params, options) {
|
|
1481
|
+
const { apiRequest, isReady } = useElevasisServices();
|
|
1482
|
+
const { currentMembership } = useOrganization();
|
|
1483
|
+
const organizationName = currentMembership?.organization?.name;
|
|
1484
|
+
return useQuery({
|
|
1485
|
+
queryKey: sessionsKeys.sessions(organizationName || "", params),
|
|
1486
|
+
queryFn: async () => {
|
|
1487
|
+
const service = new OperationsService(apiRequest);
|
|
1488
|
+
return service.listSessions(params);
|
|
1489
|
+
},
|
|
1490
|
+
enabled: isReady && (options?.enabled ?? true)
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
function useSession(sessionId) {
|
|
1494
|
+
const { apiRequest, isReady } = useElevasisServices();
|
|
1495
|
+
const { currentMembership } = useOrganization();
|
|
1496
|
+
const organizationName = currentMembership?.organization?.name;
|
|
1497
|
+
return useQuery({
|
|
1498
|
+
queryKey: sessionsKeys.session(organizationName || "", sessionId),
|
|
1499
|
+
queryFn: () => {
|
|
1500
|
+
const service = new OperationsService(apiRequest);
|
|
1501
|
+
return service.getSession(sessionId);
|
|
1502
|
+
},
|
|
1503
|
+
enabled: isReady && !!sessionId
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
function useCreateSession() {
|
|
1507
|
+
const { apiRequest } = useElevasisServices();
|
|
1508
|
+
const { currentMembership } = useOrganization();
|
|
1509
|
+
const organizationName = currentMembership?.organization?.name;
|
|
1510
|
+
const queryClient = useQueryClient();
|
|
1511
|
+
return useMutation({
|
|
1512
|
+
mutationFn: async (resourceId) => {
|
|
1513
|
+
const service = new OperationsService(apiRequest);
|
|
1514
|
+
return service.createSession(resourceId);
|
|
1515
|
+
},
|
|
1516
|
+
onSuccess: () => {
|
|
1517
|
+
queryClient.invalidateQueries({ queryKey: sessionsKeys.sessions(organizationName || "") });
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
function useDeleteSession() {
|
|
1522
|
+
const { apiRequest } = useElevasisServices();
|
|
1523
|
+
const { currentMembership } = useOrganization();
|
|
1524
|
+
const organizationName = currentMembership?.organization?.name;
|
|
1525
|
+
const queryClient = useQueryClient();
|
|
1526
|
+
return useMutation({
|
|
1527
|
+
mutationFn: async (sessionId) => {
|
|
1528
|
+
SessionIdParamSchema.parse({ sessionId });
|
|
1529
|
+
await apiRequest(`/sessions/${sessionId}`, {
|
|
1530
|
+
method: "DELETE"
|
|
1531
|
+
});
|
|
1532
|
+
},
|
|
1533
|
+
onSuccess: (_, sessionId) => {
|
|
1534
|
+
queryClient.removeQueries({
|
|
1535
|
+
queryKey: sessionsKeys.session(organizationName || "", sessionId)
|
|
1536
|
+
});
|
|
1537
|
+
queryClient.invalidateQueries({
|
|
1538
|
+
queryKey: sessionsKeys.sessions(organizationName || "")
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
function useArchiveSession() {
|
|
1544
|
+
const { apiRequest } = useElevasisServices();
|
|
1545
|
+
const { currentMembership } = useOrganization();
|
|
1546
|
+
const organizationName = currentMembership?.organization?.name;
|
|
1547
|
+
const queryClient = useQueryClient();
|
|
1548
|
+
return useMutation({
|
|
1549
|
+
mutationFn: (sessionId) => {
|
|
1550
|
+
const service = new OperationsService(apiRequest);
|
|
1551
|
+
return service.archiveSession(sessionId);
|
|
1552
|
+
},
|
|
1553
|
+
onSuccess: () => {
|
|
1554
|
+
queryClient.invalidateQueries({ queryKey: sessionsKeys.sessions(organizationName || "") });
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
function useSessionExecutions(sessionId) {
|
|
1559
|
+
const { apiRequest, isReady } = useElevasisServices();
|
|
1560
|
+
const { currentSupabaseOrganizationId } = useOrganization();
|
|
1561
|
+
return useQuery({
|
|
1562
|
+
queryKey: sessionsKeys.executions(currentSupabaseOrganizationId || "", sessionId),
|
|
1563
|
+
queryFn: async () => {
|
|
1564
|
+
SessionIdParamSchema.parse({ sessionId });
|
|
1565
|
+
return apiRequest(`/sessions/${sessionId}/executions`, {
|
|
1566
|
+
method: "GET"
|
|
1567
|
+
});
|
|
1568
|
+
},
|
|
1569
|
+
enabled: isReady && !!sessionId,
|
|
1570
|
+
staleTime: 0
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
function useSessionExecution(sessionId, executionId) {
|
|
1574
|
+
const { apiRequest, isReady } = useElevasisServices();
|
|
1575
|
+
const { currentSupabaseOrganizationId } = useOrganization();
|
|
1576
|
+
return useQuery({
|
|
1577
|
+
queryKey: sessionsKeys.execution(currentSupabaseOrganizationId || "", sessionId, executionId),
|
|
1578
|
+
queryFn: async () => {
|
|
1579
|
+
ExecutionIdParamsSchema.parse({ sessionId, executionId });
|
|
1580
|
+
return apiRequest(`/sessions/${sessionId}/executions/${executionId}`, {
|
|
1581
|
+
method: "GET"
|
|
1582
|
+
});
|
|
1583
|
+
},
|
|
1584
|
+
enabled: isReady && !!sessionId && !!executionId,
|
|
1585
|
+
refetchInterval: (query) => {
|
|
1586
|
+
const data = query.state.data;
|
|
1587
|
+
const shouldRefetch = data?.status === "running";
|
|
1588
|
+
return shouldRefetch ? REFETCH_INTERVAL_RUNNING : false;
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
function useSessionMessages(sessionId) {
|
|
1593
|
+
const { apiRequest, isReady } = useElevasisServices();
|
|
1594
|
+
const { currentSupabaseOrganizationId } = useOrganization();
|
|
1595
|
+
return useQuery({
|
|
1596
|
+
queryKey: sessionsKeys.messages(currentSupabaseOrganizationId || "", sessionId),
|
|
1597
|
+
queryFn: async () => {
|
|
1598
|
+
SessionIdParamSchema.parse({ sessionId });
|
|
1599
|
+
const response = await apiRequest(`/sessions/${sessionId}/messages`, {
|
|
1600
|
+
method: "GET"
|
|
1601
|
+
});
|
|
1602
|
+
const messages = response.messages.map((msg) => ({
|
|
1603
|
+
...msg,
|
|
1604
|
+
createdAt: new Date(msg.createdAt)
|
|
1605
|
+
}));
|
|
1606
|
+
return messages;
|
|
1607
|
+
},
|
|
1608
|
+
enabled: isReady && !!sessionId,
|
|
1609
|
+
staleTime: 0
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
function formatMessageEvent(event) {
|
|
1613
|
+
switch (event.type) {
|
|
1614
|
+
case "user_message":
|
|
1615
|
+
return event.text;
|
|
1616
|
+
case "assistant_message":
|
|
1617
|
+
return event.text;
|
|
1618
|
+
case "agent:started":
|
|
1619
|
+
return "\u{1F916} Agent started reasoning";
|
|
1620
|
+
case "agent:reasoning":
|
|
1621
|
+
return `\u{1F4AD} Reasoning (Iteration ${event.iteration})`;
|
|
1622
|
+
case "agent:tool_call":
|
|
1623
|
+
return `\u{1F527} Tool: ${event.toolName}`;
|
|
1624
|
+
case "agent:tool_result":
|
|
1625
|
+
return event.success ? "\u2705 Tool Result" : "\u26A0\uFE0F Tool Result (Failed)";
|
|
1626
|
+
case "agent:completed":
|
|
1627
|
+
return "\u2705 Agent completed";
|
|
1628
|
+
case "agent:error":
|
|
1629
|
+
return `\u274C Agent error: ${event.error}`;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
function getWsUrl(apiUrl) {
|
|
1633
|
+
const url = new URL(apiUrl);
|
|
1634
|
+
const protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
1635
|
+
return `${protocol}//${url.host}`;
|
|
1636
|
+
}
|
|
1637
|
+
function useSessionWebSocket(sessionId, apiUrl) {
|
|
1638
|
+
const [messages, setMessages] = useState([]);
|
|
1639
|
+
const [state, setState] = useState({
|
|
1640
|
+
isConnected: false,
|
|
1641
|
+
isProcessing: false,
|
|
1642
|
+
error: null
|
|
1643
|
+
});
|
|
1644
|
+
const [lastTokenUsage, setLastTokenUsage] = useState(null);
|
|
1645
|
+
const wsRef = useRef(null);
|
|
1646
|
+
const retryCountRef = useRef(0);
|
|
1647
|
+
const reconnectTimeoutRef = useRef(null);
|
|
1648
|
+
const { getAccessToken } = useAuthContext();
|
|
1649
|
+
const { currentWorkOSOrganizationId, currentSupabaseOrganizationId } = useOrganization();
|
|
1650
|
+
const queryClient = useQueryClient();
|
|
1651
|
+
const getAccessTokenRef = useRef(getAccessToken);
|
|
1652
|
+
getAccessTokenRef.current = getAccessToken;
|
|
1653
|
+
const currentWorkOSOrgIdRef = useRef(currentWorkOSOrganizationId);
|
|
1654
|
+
currentWorkOSOrgIdRef.current = currentWorkOSOrganizationId;
|
|
1655
|
+
const queryClientRef = useRef(queryClient);
|
|
1656
|
+
queryClientRef.current = queryClient;
|
|
1657
|
+
const currentSupabaseOrgIdRef = useRef(currentSupabaseOrganizationId);
|
|
1658
|
+
currentSupabaseOrgIdRef.current = currentSupabaseOrganizationId;
|
|
1659
|
+
const connect = useCallback(async () => {
|
|
1660
|
+
if (!currentWorkOSOrgIdRef.current) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (wsRef.current?.readyState === WebSocket.OPEN || wsRef.current?.readyState === WebSocket.CONNECTING) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const token = await getAccessTokenRef.current();
|
|
1667
|
+
if (!token) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const orgId = currentWorkOSOrgIdRef.current;
|
|
1671
|
+
const wsUrl = `${getWsUrl(apiUrl)}/ws/sessions/${sessionId}?token=${token}&orgId=${orgId}`;
|
|
1672
|
+
const ws = new WebSocket(wsUrl);
|
|
1673
|
+
ws.onopen = () => {
|
|
1674
|
+
setState((prev) => ({ ...prev, isConnected: true, error: null }));
|
|
1675
|
+
retryCountRef.current = 0;
|
|
1676
|
+
const supabaseOrgId = currentSupabaseOrgIdRef.current;
|
|
1677
|
+
if (supabaseOrgId) {
|
|
1678
|
+
queryClientRef.current.invalidateQueries({
|
|
1679
|
+
queryKey: sessionsKeys.messages(supabaseOrgId, sessionId)
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
ws.onmessage = (event) => {
|
|
1684
|
+
try {
|
|
1685
|
+
const data = JSON.parse(event.data);
|
|
1686
|
+
if (data.type && data.type !== "turn:complete" && data.type !== "error") {
|
|
1687
|
+
const tempMessage = {
|
|
1688
|
+
id: `temp-${Date.now()}`,
|
|
1689
|
+
role: data.type === "user_message" ? "user" : "assistant",
|
|
1690
|
+
messageType: data.type,
|
|
1691
|
+
text: formatMessageEvent(data),
|
|
1692
|
+
// Format based on type
|
|
1693
|
+
metadata: data.type.startsWith("agent:") ? data : void 0,
|
|
1694
|
+
turnNumber: 999999,
|
|
1695
|
+
// High placeholder so temp messages sort to bottom
|
|
1696
|
+
messageIndex: Date.now(),
|
|
1697
|
+
// Use timestamp for unique ordering
|
|
1698
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1699
|
+
};
|
|
1700
|
+
setMessages((prev) => [...prev, tempMessage]);
|
|
1701
|
+
} else if (data.type === "turn:complete") {
|
|
1702
|
+
if (data.tokenUsage) {
|
|
1703
|
+
setLastTokenUsage(data.tokenUsage);
|
|
1704
|
+
}
|
|
1705
|
+
setState((prev) => ({ ...prev, isProcessing: false }));
|
|
1706
|
+
const supabaseOrgId = currentSupabaseOrgIdRef.current;
|
|
1707
|
+
if (supabaseOrgId) {
|
|
1708
|
+
queryClientRef.current.invalidateQueries({
|
|
1709
|
+
queryKey: sessionsKeys.messages(supabaseOrgId, sessionId)
|
|
1710
|
+
});
|
|
1711
|
+
queryClientRef.current.invalidateQueries({
|
|
1712
|
+
queryKey: sessionsKeys.executions(supabaseOrgId, sessionId)
|
|
1713
|
+
});
|
|
1714
|
+
queryClientRef.current.invalidateQueries({
|
|
1715
|
+
queryKey: sessionsKeys.session(supabaseOrgId, sessionId)
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
} else if (data.type === "error") {
|
|
1719
|
+
setState((prev) => ({
|
|
1720
|
+
...prev,
|
|
1721
|
+
error: data.error,
|
|
1722
|
+
isProcessing: false
|
|
1723
|
+
}));
|
|
1724
|
+
}
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
ws.onerror = () => {
|
|
1730
|
+
};
|
|
1731
|
+
ws.onclose = (event) => {
|
|
1732
|
+
setState((prev) => ({ ...prev, isConnected: false }));
|
|
1733
|
+
if (!event.wasClean && retryCountRef.current >= WS_MAX_RETRIES_BEFORE_ERROR) {
|
|
1734
|
+
setState((prev) => ({
|
|
1735
|
+
...prev,
|
|
1736
|
+
error: "Unable to connect to session. Retrying..."
|
|
1737
|
+
}));
|
|
1738
|
+
}
|
|
1739
|
+
const terminalCodes = [1003, 1008];
|
|
1740
|
+
if (terminalCodes.includes(event.code) || event.code >= 4e3) {
|
|
1741
|
+
setState((prev) => ({
|
|
1742
|
+
...prev,
|
|
1743
|
+
error: `Connection rejected (code ${event.code})`
|
|
1744
|
+
}));
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
const delay = Math.min(WS_RECONNECT_BASE_DELAY * Math.pow(2, retryCountRef.current), WS_RECONNECT_MAX_DELAY);
|
|
1748
|
+
retryCountRef.current += 1;
|
|
1749
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
1750
|
+
connect();
|
|
1751
|
+
}, delay);
|
|
1752
|
+
};
|
|
1753
|
+
wsRef.current = ws;
|
|
1754
|
+
}, [sessionId, apiUrl]);
|
|
1755
|
+
useEffect(() => {
|
|
1756
|
+
retryCountRef.current = 0;
|
|
1757
|
+
if (currentWorkOSOrgIdRef.current) {
|
|
1758
|
+
connect();
|
|
1759
|
+
}
|
|
1760
|
+
return () => {
|
|
1761
|
+
if (reconnectTimeoutRef.current) {
|
|
1762
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
1763
|
+
}
|
|
1764
|
+
if (wsRef.current) {
|
|
1765
|
+
wsRef.current.close();
|
|
1766
|
+
wsRef.current = null;
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
}, [sessionId, currentWorkOSOrganizationId, connect]);
|
|
1770
|
+
const sendMessage = useCallback(
|
|
1771
|
+
(text, pageContext) => {
|
|
1772
|
+
const trimmed = text.trim();
|
|
1773
|
+
if (!trimmed) {
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (!wsRef.current || state.isProcessing) {
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
if (wsRef.current.readyState !== WebSocket.OPEN) {
|
|
1780
|
+
setState((prev) => ({
|
|
1781
|
+
...prev,
|
|
1782
|
+
error: "WebSocket not connected"
|
|
1783
|
+
}));
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
const userMessage = {
|
|
1787
|
+
id: `temp-${Date.now()}`,
|
|
1788
|
+
role: "user",
|
|
1789
|
+
messageType: "user_message",
|
|
1790
|
+
text: trimmed,
|
|
1791
|
+
turnNumber: 999999,
|
|
1792
|
+
// High placeholder so temp messages sort to bottom
|
|
1793
|
+
messageIndex: Date.now(),
|
|
1794
|
+
// Use timestamp for unique ordering
|
|
1795
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
1796
|
+
};
|
|
1797
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
1798
|
+
const input = { message: trimmed };
|
|
1799
|
+
if (pageContext) {
|
|
1800
|
+
input.pageContext = pageContext;
|
|
1801
|
+
}
|
|
1802
|
+
const message = WebSocketSessionTurnSchema.parse({
|
|
1803
|
+
type: "session:turn",
|
|
1804
|
+
input
|
|
1805
|
+
});
|
|
1806
|
+
wsRef.current.send(JSON.stringify(message));
|
|
1807
|
+
setState((prev) => ({ ...prev, isProcessing: true, error: null }));
|
|
1808
|
+
},
|
|
1809
|
+
[state.isProcessing, sessionId]
|
|
1810
|
+
);
|
|
1811
|
+
const clearError = useCallback(() => {
|
|
1812
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
1813
|
+
}, []);
|
|
1814
|
+
return {
|
|
1815
|
+
messages,
|
|
1816
|
+
state,
|
|
1817
|
+
sendMessage,
|
|
1818
|
+
clearError,
|
|
1819
|
+
lastTokenUsage
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1387
1822
|
function usePaginationState(pageSize, resetDeps, total) {
|
|
1388
1823
|
const [page, setPage] = useState(1);
|
|
1389
1824
|
useEffect(() => {
|
|
@@ -1491,12 +1926,12 @@ function useSortedData(data, defaultColumn, accessors, defaultDirection = "desc"
|
|
|
1491
1926
|
}
|
|
1492
1927
|
function createUseFeatureAccess({
|
|
1493
1928
|
useInitialization,
|
|
1494
|
-
useOrganization,
|
|
1929
|
+
useOrganization: useOrganization2,
|
|
1495
1930
|
optInFeatures = []
|
|
1496
1931
|
}) {
|
|
1497
1932
|
return function useFeatureAccess() {
|
|
1498
1933
|
const { profile, organizationReady } = useInitialization();
|
|
1499
|
-
const { currentMembership } =
|
|
1934
|
+
const { currentMembership } = useOrganization2();
|
|
1500
1935
|
const { orgConfig, membershipConfig } = useMemo(() => {
|
|
1501
1936
|
const organizationConfig = currentMembership?.organization?.config;
|
|
1502
1937
|
const memberConfig = currentMembership?.config;
|
|
@@ -1593,4 +2028,522 @@ function useSSEConnection({
|
|
|
1593
2028
|
return { connected, error };
|
|
1594
2029
|
}
|
|
1595
2030
|
|
|
1596
|
-
|
|
2031
|
+
// src/hooks/operations/shared/queryKeys.ts
|
|
2032
|
+
var operationsKeys = {
|
|
2033
|
+
all: ["operations"],
|
|
2034
|
+
// Individual resource types (kept for CC-specific features)
|
|
2035
|
+
workflows: (org) => [...operationsKeys.all, "workflows", org],
|
|
2036
|
+
workflowDetails: (org) => [...operationsKeys.workflows(org), "details"],
|
|
2037
|
+
workflow: (id, org) => [...operationsKeys.workflows(org), id],
|
|
2038
|
+
agents: (org) => [...operationsKeys.all, "agents", org],
|
|
2039
|
+
agentDetails: (org) => [...operationsKeys.agents(org), "details"],
|
|
2040
|
+
agent: (id, org) => [...operationsKeys.agents(org), id],
|
|
2041
|
+
sessions: (org, params) => [...operationsKeys.all, "sessions", org, params],
|
|
2042
|
+
session: (org, sessionId) => [...operationsKeys.all, "session", org, sessionId]
|
|
2043
|
+
};
|
|
2044
|
+
function useExecutionLogSSE(resourceId, manager) {
|
|
2045
|
+
const queryClient = useQueryClient();
|
|
2046
|
+
const [liveExecutions, setLiveExecutions] = useState(/* @__PURE__ */ new Set());
|
|
2047
|
+
const [streamingLogs, setStreamingLogs] = useState(/* @__PURE__ */ new Map());
|
|
2048
|
+
const { isReady: isOrganizationReady, organizationId } = useElevasisServices();
|
|
2049
|
+
const { currentMembership } = useOrganization();
|
|
2050
|
+
const currentWorkOSOrganizationId = currentMembership?.organization?.id;
|
|
2051
|
+
const apiUrl = import.meta.env?.VITE_API_SERVER ?? "http://localhost:5170";
|
|
2052
|
+
const sseUrl = `${apiUrl}/api/execution-engine/sse/${resourceId}`;
|
|
2053
|
+
const headers = useMemo(() => {
|
|
2054
|
+
const h = {};
|
|
2055
|
+
if (currentWorkOSOrganizationId) h[HTTP_HEADERS.WORKOS_ORGANIZATION_ID] = currentWorkOSOrganizationId;
|
|
2056
|
+
return h;
|
|
2057
|
+
}, [currentWorkOSOrganizationId]);
|
|
2058
|
+
const handleMessage = useCallback(
|
|
2059
|
+
(data) => {
|
|
2060
|
+
try {
|
|
2061
|
+
const event = JSON.parse(data);
|
|
2062
|
+
switch (event.type) {
|
|
2063
|
+
case "connected":
|
|
2064
|
+
break;
|
|
2065
|
+
case "new-execution":
|
|
2066
|
+
if (event.executionId) {
|
|
2067
|
+
setLiveExecutions((prev) => {
|
|
2068
|
+
const updated = new Set(prev);
|
|
2069
|
+
updated.add(event.executionId);
|
|
2070
|
+
return updated;
|
|
2071
|
+
});
|
|
2072
|
+
queryClient.invalidateQueries({
|
|
2073
|
+
queryKey: executionsKeys.executions(organizationId, resourceId)
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
break;
|
|
2077
|
+
case "execution-complete":
|
|
2078
|
+
if (event.executionId) {
|
|
2079
|
+
setLiveExecutions((prev) => {
|
|
2080
|
+
const updated = new Set(prev);
|
|
2081
|
+
updated.delete(event.executionId);
|
|
2082
|
+
return updated;
|
|
2083
|
+
});
|
|
2084
|
+
setStreamingLogs((prev) => {
|
|
2085
|
+
const updated = new Map(prev);
|
|
2086
|
+
updated.delete(event.executionId);
|
|
2087
|
+
return updated;
|
|
2088
|
+
});
|
|
2089
|
+
queryClient.invalidateQueries({
|
|
2090
|
+
queryKey: executionsKeys.executions(organizationId, resourceId)
|
|
2091
|
+
});
|
|
2092
|
+
queryClient.invalidateQueries({
|
|
2093
|
+
queryKey: executionsKeys.execution(organizationId, resourceId, event.executionId)
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
break;
|
|
2097
|
+
case "log":
|
|
2098
|
+
if (event.executionId) {
|
|
2099
|
+
if (event.data?.log) {
|
|
2100
|
+
setStreamingLogs((prev) => {
|
|
2101
|
+
const updated = new Map(prev);
|
|
2102
|
+
const existing = updated.get(event.executionId) || [];
|
|
2103
|
+
updated.set(event.executionId, [...existing, event.data.log]);
|
|
2104
|
+
return updated;
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
queryClient.invalidateQueries({
|
|
2108
|
+
queryKey: executionsKeys.execution(organizationId, resourceId, event.executionId)
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
break;
|
|
2112
|
+
}
|
|
2113
|
+
} catch {
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
[organizationId, resourceId, queryClient]
|
|
2117
|
+
);
|
|
2118
|
+
const { connected, error } = useSSEConnection({
|
|
2119
|
+
manager,
|
|
2120
|
+
connectionKey: `resource-${resourceId}`,
|
|
2121
|
+
url: sseUrl,
|
|
2122
|
+
enabled: !!resourceId && !!organizationId && isOrganizationReady,
|
|
2123
|
+
headers,
|
|
2124
|
+
onmessage: handleMessage,
|
|
2125
|
+
onopen: (response) => response.status === 403 ? "Organization access denied" : void 0
|
|
2126
|
+
});
|
|
2127
|
+
useEffect(() => {
|
|
2128
|
+
return () => {
|
|
2129
|
+
setLiveExecutions(/* @__PURE__ */ new Set());
|
|
2130
|
+
setStreamingLogs(/* @__PURE__ */ new Map());
|
|
2131
|
+
};
|
|
2132
|
+
}, [resourceId, organizationId, isOrganizationReady]);
|
|
2133
|
+
return {
|
|
2134
|
+
liveExecutions,
|
|
2135
|
+
connected,
|
|
2136
|
+
error,
|
|
2137
|
+
runningCount: liveExecutions.size,
|
|
2138
|
+
isLive: (executionId) => liveExecutions.has(executionId),
|
|
2139
|
+
streamingLogs
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
function useExecutionPanelState({
|
|
2143
|
+
resourceId,
|
|
2144
|
+
manager,
|
|
2145
|
+
limit,
|
|
2146
|
+
onConnectionStatus
|
|
2147
|
+
}) {
|
|
2148
|
+
const queryClient = useQueryClient();
|
|
2149
|
+
const navigate = useNavigate();
|
|
2150
|
+
const { organizationId } = useElevasisServices();
|
|
2151
|
+
const [resourceStatusFilter, setResourceStatusFilter] = useState("all");
|
|
2152
|
+
const { data: executionsData, isLoading, isFetched } = useExecutions(resourceId, resourceStatusFilter, limit);
|
|
2153
|
+
const { liveExecutions, connected, runningCount, streamingLogs } = useExecutionLogSSE(resourceId, manager);
|
|
2154
|
+
const search = useSearch({ strict: false });
|
|
2155
|
+
const selectedId = search.exec;
|
|
2156
|
+
const setSelectedId = useCallback(
|
|
2157
|
+
(id) => {
|
|
2158
|
+
void navigate({
|
|
2159
|
+
to: ".",
|
|
2160
|
+
search: { exec: id },
|
|
2161
|
+
replace: true
|
|
2162
|
+
});
|
|
2163
|
+
},
|
|
2164
|
+
[navigate]
|
|
2165
|
+
);
|
|
2166
|
+
const executions = executionsData?.executions || [];
|
|
2167
|
+
const prevExecutionCountRef = useRef(0);
|
|
2168
|
+
const prevNewestIdRef = useRef(void 0);
|
|
2169
|
+
const prevResourceIdRef = useRef(resourceId);
|
|
2170
|
+
const hasInitializedRef = useRef(false);
|
|
2171
|
+
useEffect(() => {
|
|
2172
|
+
if (prevResourceIdRef.current !== resourceId) {
|
|
2173
|
+
setSelectedId(void 0);
|
|
2174
|
+
prevResourceIdRef.current = resourceId;
|
|
2175
|
+
hasInitializedRef.current = false;
|
|
2176
|
+
}
|
|
2177
|
+
}, [resourceId, setSelectedId]);
|
|
2178
|
+
useEffect(() => {
|
|
2179
|
+
if (organizationId) {
|
|
2180
|
+
queryClient.invalidateQueries({
|
|
2181
|
+
queryKey: [...executionsKeys.all, "execution"]
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
}, [resourceId, queryClient, organizationId]);
|
|
2185
|
+
useEffect(() => {
|
|
2186
|
+
const sortedExecutions = [...executions].sort((a, b) => b.startTime - a.startTime);
|
|
2187
|
+
const newestExecution = sortedExecutions[0];
|
|
2188
|
+
if (!newestExecution) return;
|
|
2189
|
+
if (!hasInitializedRef.current) {
|
|
2190
|
+
hasInitializedRef.current = true;
|
|
2191
|
+
prevExecutionCountRef.current = executions.length;
|
|
2192
|
+
prevNewestIdRef.current = newestExecution.id;
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const executionCountIncreased = executions.length > prevExecutionCountRef.current;
|
|
2196
|
+
const newestIdChanged = newestExecution.id !== prevNewestIdRef.current;
|
|
2197
|
+
if (executionCountIncreased && newestIdChanged && newestExecution.id !== selectedId) {
|
|
2198
|
+
setSelectedId(newestExecution.id);
|
|
2199
|
+
}
|
|
2200
|
+
prevExecutionCountRef.current = executions.length;
|
|
2201
|
+
prevNewestIdRef.current = newestExecution.id;
|
|
2202
|
+
}, [executions, selectedId, setSelectedId]);
|
|
2203
|
+
useEffect(() => {
|
|
2204
|
+
if (!isFetched) return;
|
|
2205
|
+
if (executions.length === 0) {
|
|
2206
|
+
setSelectedId(void 0);
|
|
2207
|
+
} else if (!selectedId && executions.length > 0) {
|
|
2208
|
+
const latest = [...executions].sort((a, b) => b.startTime - a.startTime)[0];
|
|
2209
|
+
setSelectedId(latest.id);
|
|
2210
|
+
}
|
|
2211
|
+
}, [executions, selectedId, setSelectedId, isFetched]);
|
|
2212
|
+
useEffect(() => {
|
|
2213
|
+
if (onConnectionStatus) {
|
|
2214
|
+
onConnectionStatus(connected, runningCount);
|
|
2215
|
+
}
|
|
2216
|
+
}, [connected, runningCount, onConnectionStatus]);
|
|
2217
|
+
return {
|
|
2218
|
+
executions,
|
|
2219
|
+
isLoading,
|
|
2220
|
+
isFetched,
|
|
2221
|
+
selectedId,
|
|
2222
|
+
setSelectedId,
|
|
2223
|
+
resourceStatusFilter,
|
|
2224
|
+
setResourceStatusFilter,
|
|
2225
|
+
liveExecutions,
|
|
2226
|
+
connected,
|
|
2227
|
+
runningCount,
|
|
2228
|
+
streamingLogs
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
// src/hooks/operations/shared/resourceDefinitionUtils.ts
|
|
2233
|
+
function isSessionCapable(type, resourceDefinition) {
|
|
2234
|
+
if (type !== "agent" || !resourceDefinition) {
|
|
2235
|
+
return false;
|
|
2236
|
+
}
|
|
2237
|
+
const config = resourceDefinition.config;
|
|
2238
|
+
return config.sessionCapable === true;
|
|
2239
|
+
}
|
|
2240
|
+
function useDeploymentDocs(selectedDeploymentId) {
|
|
2241
|
+
const { apiRequest, isReady: isOrganizationReady, organizationId } = useElevasisServices();
|
|
2242
|
+
const deploymentsQuery = useQuery({
|
|
2243
|
+
queryKey: ["deployments", organizationId],
|
|
2244
|
+
queryFn: async () => {
|
|
2245
|
+
const response = await apiRequest("/deployments");
|
|
2246
|
+
return response.deployments;
|
|
2247
|
+
},
|
|
2248
|
+
enabled: isOrganizationReady,
|
|
2249
|
+
staleTime: STALE_TIME_DEFAULT
|
|
2250
|
+
});
|
|
2251
|
+
const activeDeployments = useMemo(() => {
|
|
2252
|
+
if (!deploymentsQuery.data) return [];
|
|
2253
|
+
return deploymentsQuery.data.filter((d) => d.status === "active").sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
2254
|
+
}, [deploymentsQuery.data]);
|
|
2255
|
+
const activeDeployment = useMemo(() => {
|
|
2256
|
+
if (selectedDeploymentId) {
|
|
2257
|
+
return activeDeployments.find((d) => d.id === selectedDeploymentId) ?? activeDeployments[0] ?? null;
|
|
2258
|
+
}
|
|
2259
|
+
return activeDeployments[0] ?? null;
|
|
2260
|
+
}, [activeDeployments, selectedDeploymentId]);
|
|
2261
|
+
const deploymentId = activeDeployment?.id;
|
|
2262
|
+
const docsQuery = useQuery({
|
|
2263
|
+
queryKey: ["deployment-docs", deploymentId],
|
|
2264
|
+
queryFn: async () => {
|
|
2265
|
+
const response = await apiRequest(`/deployments/${deploymentId}/docs`);
|
|
2266
|
+
return response.files;
|
|
2267
|
+
},
|
|
2268
|
+
enabled: isOrganizationReady && !!deploymentId,
|
|
2269
|
+
staleTime: STALE_TIME_DEFAULT
|
|
2270
|
+
});
|
|
2271
|
+
return {
|
|
2272
|
+
files: docsQuery.data ?? [],
|
|
2273
|
+
isLoading: deploymentsQuery.isLoading || !!deploymentId && docsQuery.isLoading,
|
|
2274
|
+
error: deploymentsQuery.error || docsQuery.error,
|
|
2275
|
+
activeDeployment,
|
|
2276
|
+
activeDeployments
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
function useCommandViewData() {
|
|
2280
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
2281
|
+
return useQuery({
|
|
2282
|
+
queryKey: ["command-view", organizationId],
|
|
2283
|
+
queryFn: async () => {
|
|
2284
|
+
return apiRequest("/command-view");
|
|
2285
|
+
},
|
|
2286
|
+
enabled: isReady,
|
|
2287
|
+
staleTime: STALE_TIME_DEFAULT
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
function useCommandViewStats(timeRange = "24h") {
|
|
2291
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
2292
|
+
return useQuery({
|
|
2293
|
+
queryKey: ["command-view-stats", organizationId, timeRange],
|
|
2294
|
+
queryFn: async () => {
|
|
2295
|
+
return apiRequest(`/command-view/stats?timeRange=${timeRange}`);
|
|
2296
|
+
},
|
|
2297
|
+
enabled: isReady,
|
|
2298
|
+
staleTime: STALE_TIME_MONITORING
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
var useCommandViewStore = create()(
|
|
2302
|
+
persist(
|
|
2303
|
+
(set) => ({
|
|
2304
|
+
statusFilter: "all",
|
|
2305
|
+
setStatusFilter: (statusFilter) => set({ statusFilter }),
|
|
2306
|
+
showIntegrations: true,
|
|
2307
|
+
setShowIntegrations: (showIntegrations) => set({ showIntegrations }),
|
|
2308
|
+
fitViewOnFilter: true,
|
|
2309
|
+
setFitViewOnFilter: (fitViewOnFilter) => set({ fitViewOnFilter }),
|
|
2310
|
+
selectedNodeId: null,
|
|
2311
|
+
setSelectedNodeId: (selectedNodeId) => set({ selectedNodeId })
|
|
2312
|
+
}),
|
|
2313
|
+
{
|
|
2314
|
+
name: "command-view-store",
|
|
2315
|
+
partialize: (s) => ({
|
|
2316
|
+
showIntegrations: s.showIntegrations,
|
|
2317
|
+
fitViewOnFilter: s.fitViewOnFilter
|
|
2318
|
+
})
|
|
2319
|
+
}
|
|
2320
|
+
)
|
|
2321
|
+
);
|
|
2322
|
+
function useCommandViewLayout(graph) {
|
|
2323
|
+
return useMemo(() => {
|
|
2324
|
+
const dagreGraph = new dagre.graphlib.Graph();
|
|
2325
|
+
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
2326
|
+
dagreGraph.setGraph({
|
|
2327
|
+
rankdir: "LR",
|
|
2328
|
+
ranksep: GRAPH_CONSTANTS.RANK_SEP,
|
|
2329
|
+
nodesep: GRAPH_CONSTANTS.NODE_SEP,
|
|
2330
|
+
marginx: 50,
|
|
2331
|
+
marginy: 50,
|
|
2332
|
+
ranker: "longest-path"
|
|
2333
|
+
});
|
|
2334
|
+
graph.nodes.forEach((node) => {
|
|
2335
|
+
dagreGraph.setNode(node.id, {
|
|
2336
|
+
width: GRAPH_CONSTANTS.NODE_WIDTH,
|
|
2337
|
+
height: GRAPH_CONSTANTS.NODE_HEIGHT
|
|
2338
|
+
});
|
|
2339
|
+
});
|
|
2340
|
+
graph.edges.forEach((edge) => {
|
|
2341
|
+
dagreGraph.setEdge(edge.source, edge.target);
|
|
2342
|
+
});
|
|
2343
|
+
dagre.layout(dagreGraph);
|
|
2344
|
+
const nodePositions = /* @__PURE__ */ new Map();
|
|
2345
|
+
const nodeNames = /* @__PURE__ */ new Map();
|
|
2346
|
+
graph.nodes.forEach((node) => {
|
|
2347
|
+
const dagreNode = dagreGraph.node(node.id);
|
|
2348
|
+
nodePositions.set(node.id, { x: dagreNode.x, y: dagreNode.y });
|
|
2349
|
+
nodeNames.set(node.id, node.name);
|
|
2350
|
+
});
|
|
2351
|
+
const parent = /* @__PURE__ */ new Map();
|
|
2352
|
+
graph.nodes.forEach((node) => parent.set(node.id, node.id));
|
|
2353
|
+
const find = (id) => {
|
|
2354
|
+
if (parent.get(id) !== id) {
|
|
2355
|
+
parent.set(id, find(parent.get(id)));
|
|
2356
|
+
}
|
|
2357
|
+
return parent.get(id);
|
|
2358
|
+
};
|
|
2359
|
+
const union = (a, b) => {
|
|
2360
|
+
const rootA = find(a);
|
|
2361
|
+
const rootB = find(b);
|
|
2362
|
+
if (rootA !== rootB) {
|
|
2363
|
+
parent.set(rootA, rootB);
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
graph.edges.forEach((edge) => {
|
|
2367
|
+
if (edge.relationship === "triggers" || edge.relationship === "approval") {
|
|
2368
|
+
union(edge.source, edge.target);
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
const components = /* @__PURE__ */ new Map();
|
|
2372
|
+
graph.nodes.forEach((node) => {
|
|
2373
|
+
const root = find(node.id);
|
|
2374
|
+
const existing = components.get(root) || [];
|
|
2375
|
+
existing.push(node.id);
|
|
2376
|
+
components.set(root, existing);
|
|
2377
|
+
});
|
|
2378
|
+
const getMinPrefix = (nodeIds) => {
|
|
2379
|
+
let minPrefix = "";
|
|
2380
|
+
for (const id of nodeIds) {
|
|
2381
|
+
const name = nodeNames.get(id) || "";
|
|
2382
|
+
const prefix = name.match(/^([A-Z]+-\d+)/)?.[1];
|
|
2383
|
+
if (prefix && (!minPrefix || prefix.localeCompare(minPrefix) < 0)) {
|
|
2384
|
+
minPrefix = prefix;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
return minPrefix || nodeNames.get(nodeIds[0]) || "";
|
|
2388
|
+
};
|
|
2389
|
+
const sortedComponents = Array.from(components.values()).sort((a, b) => {
|
|
2390
|
+
return getMinPrefix(a).localeCompare(getMinPrefix(b));
|
|
2391
|
+
});
|
|
2392
|
+
const chains = sortedComponents.filter((c) => c.length > 1);
|
|
2393
|
+
const orphans = sortedComponents.filter((c) => c.length === 1);
|
|
2394
|
+
const componentYOffsets = /* @__PURE__ */ new Map();
|
|
2395
|
+
let currentY = 0;
|
|
2396
|
+
for (const nodeIds of chains) {
|
|
2397
|
+
let minY = Infinity;
|
|
2398
|
+
let maxY = -Infinity;
|
|
2399
|
+
for (const id of nodeIds) {
|
|
2400
|
+
const pos = nodePositions.get(id);
|
|
2401
|
+
minY = Math.min(minY, pos.y);
|
|
2402
|
+
maxY = Math.max(maxY, pos.y);
|
|
2403
|
+
}
|
|
2404
|
+
const offset = currentY - minY + 50;
|
|
2405
|
+
for (const id of nodeIds) {
|
|
2406
|
+
componentYOffsets.set(id, offset);
|
|
2407
|
+
}
|
|
2408
|
+
currentY = currentY + (maxY - minY) + GRAPH_CONSTANTS.NODE_HEIGHT + 80;
|
|
2409
|
+
}
|
|
2410
|
+
if (orphans.length > 0) {
|
|
2411
|
+
const longestChainDepth = chains.length > 0 ? Math.max(
|
|
2412
|
+
...chains.map((chain) => {
|
|
2413
|
+
const xValues = new Set(chain.map((id) => Math.round(nodePositions.get(id).x)));
|
|
2414
|
+
return xValues.size;
|
|
2415
|
+
})
|
|
2416
|
+
) : 0;
|
|
2417
|
+
const ORPHAN_COLS = Math.max(5, longestChainDepth + Math.floor(longestChainDepth / 4));
|
|
2418
|
+
const cellW = GRAPH_CONSTANTS.NODE_WIDTH + GRAPH_CONSTANTS.NODE_SEP;
|
|
2419
|
+
const cellH = GRAPH_CONSTANTS.NODE_HEIGHT + GRAPH_CONSTANTS.NODE_SEP;
|
|
2420
|
+
const gridStartY = currentY + 50;
|
|
2421
|
+
orphans.forEach((nodeIds, i) => {
|
|
2422
|
+
const id = nodeIds[0];
|
|
2423
|
+
const col = i % ORPHAN_COLS;
|
|
2424
|
+
const row = Math.floor(i / ORPHAN_COLS);
|
|
2425
|
+
nodePositions.set(id, {
|
|
2426
|
+
x: col * cellW + GRAPH_CONSTANTS.NODE_WIDTH / 2,
|
|
2427
|
+
y: gridStartY + row * cellH + GRAPH_CONSTANTS.NODE_HEIGHT / 2
|
|
2428
|
+
});
|
|
2429
|
+
componentYOffsets.set(id, 0);
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
const nodes = graph.nodes.map((node) => {
|
|
2433
|
+
const pos = nodePositions.get(node.id);
|
|
2434
|
+
const yOffset = componentYOffsets.get(node.id) || 0;
|
|
2435
|
+
return {
|
|
2436
|
+
id: node.id,
|
|
2437
|
+
type: "commandView",
|
|
2438
|
+
position: {
|
|
2439
|
+
x: pos.x - GRAPH_CONSTANTS.NODE_WIDTH / 2,
|
|
2440
|
+
y: pos.y + yOffset - GRAPH_CONSTANTS.NODE_HEIGHT / 2
|
|
2441
|
+
},
|
|
2442
|
+
data: node
|
|
2443
|
+
};
|
|
2444
|
+
});
|
|
2445
|
+
const edgesBySource = /* @__PURE__ */ new Map();
|
|
2446
|
+
graph.edges.forEach((edge) => {
|
|
2447
|
+
const existing = edgesBySource.get(edge.source) || [];
|
|
2448
|
+
existing.push(edge);
|
|
2449
|
+
edgesBySource.set(edge.source, existing);
|
|
2450
|
+
});
|
|
2451
|
+
const edges = graph.edges.map((edge) => {
|
|
2452
|
+
const sourceEdges = edgesBySource.get(edge.source) || [];
|
|
2453
|
+
const edgeIndex = sourceEdges.indexOf(edge);
|
|
2454
|
+
const totalEdges = sourceEdges.length;
|
|
2455
|
+
return {
|
|
2456
|
+
id: edge.id,
|
|
2457
|
+
source: edge.source,
|
|
2458
|
+
target: edge.target,
|
|
2459
|
+
type: "commandView",
|
|
2460
|
+
data: {
|
|
2461
|
+
relationship: edge.relationship,
|
|
2462
|
+
label: edge.label,
|
|
2463
|
+
edgeIndex,
|
|
2464
|
+
totalEdges
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
});
|
|
2468
|
+
return { nodes, edges };
|
|
2469
|
+
}, [graph]);
|
|
2470
|
+
}
|
|
2471
|
+
function useGraphStats(graph) {
|
|
2472
|
+
return useMemo(() => {
|
|
2473
|
+
const stats = {
|
|
2474
|
+
agents: 0,
|
|
2475
|
+
workflows: 0,
|
|
2476
|
+
integrations: 0,
|
|
2477
|
+
triggers: 0,
|
|
2478
|
+
prodResources: 0,
|
|
2479
|
+
devResources: 0,
|
|
2480
|
+
connectedIntegrations: 0,
|
|
2481
|
+
errorIntegrations: 0
|
|
2482
|
+
};
|
|
2483
|
+
for (const node of graph.nodes) {
|
|
2484
|
+
switch (node.type) {
|
|
2485
|
+
case "agent":
|
|
2486
|
+
stats.agents++;
|
|
2487
|
+
break;
|
|
2488
|
+
case "workflow":
|
|
2489
|
+
stats.workflows++;
|
|
2490
|
+
break;
|
|
2491
|
+
case "integration":
|
|
2492
|
+
stats.integrations++;
|
|
2493
|
+
if (node.connectionStatus === "connected") stats.connectedIntegrations++;
|
|
2494
|
+
if (node.connectionStatus === "error") stats.errorIntegrations++;
|
|
2495
|
+
break;
|
|
2496
|
+
case "trigger":
|
|
2497
|
+
stats.triggers++;
|
|
2498
|
+
break;
|
|
2499
|
+
}
|
|
2500
|
+
if (node.status === "prod") stats.prodResources++;
|
|
2501
|
+
if (node.status === "dev") stats.devResources++;
|
|
2502
|
+
}
|
|
2503
|
+
return stats;
|
|
2504
|
+
}, [graph]);
|
|
2505
|
+
}
|
|
2506
|
+
function useCheckpointTasks({ checkpointId, enabled = true }) {
|
|
2507
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
2508
|
+
return useQuery({
|
|
2509
|
+
queryKey: ["checkpoint-tasks", organizationId, checkpointId],
|
|
2510
|
+
queryFn: async () => {
|
|
2511
|
+
return apiRequest(
|
|
2512
|
+
`/command-queue?humanCheckpoint=${checkpointId}&status=pending&limit=10`
|
|
2513
|
+
);
|
|
2514
|
+
},
|
|
2515
|
+
// Only fetch when: org ready, checkpoint selected, and enabled
|
|
2516
|
+
enabled: isReady && !!checkpointId && enabled,
|
|
2517
|
+
staleTime: STALE_TIME_MONITORING
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
function useResourceErrors({ resourceId, timeRange, hasFailures }) {
|
|
2521
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
2522
|
+
return useQuery({
|
|
2523
|
+
queryKey: ["resource-errors", organizationId, resourceId, timeRange],
|
|
2524
|
+
queryFn: async () => {
|
|
2525
|
+
return apiRequest(
|
|
2526
|
+
`/command-view/resource-errors?resourceId=${resourceId}&timeRange=${timeRange}`
|
|
2527
|
+
);
|
|
2528
|
+
},
|
|
2529
|
+
// Only fetch when: org ready, resource selected, and resource has failures
|
|
2530
|
+
enabled: isReady && !!resourceId && hasFailures,
|
|
2531
|
+
staleTime: STALE_TIME_ADMIN
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
function useResourceExecutions({ resourceId, timeRange, enabled = true }) {
|
|
2535
|
+
const { apiRequest, organizationId, isReady } = useElevasisServices();
|
|
2536
|
+
return useQuery({
|
|
2537
|
+
queryKey: ["resource-executions", organizationId, resourceId, timeRange],
|
|
2538
|
+
queryFn: async () => {
|
|
2539
|
+
return apiRequest(
|
|
2540
|
+
`/command-view/resource-executions?resourceId=${resourceId}&timeRange=${timeRange}`
|
|
2541
|
+
);
|
|
2542
|
+
},
|
|
2543
|
+
// Only fetch when: org ready, resource selected, and enabled
|
|
2544
|
+
enabled: isReady && !!resourceId && enabled,
|
|
2545
|
+
staleTime: STALE_TIME_MONITORING
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
export { CredentialNameSchema, OperationsService, UuidSchema, createUseFeatureAccess, executionsKeys, isSessionCapable, operationsKeys, scheduleKeys, sessionsKeys, sortData, useActivities, useActivityTrend, useArchiveSession, useBatchDelete, useBatchedResourcesHealth, useBulkDeleteExecutions, useBusinessImpact, useCancelExecution, useCancelSchedule, useCheckpointTasks, useCommandQueue, useCommandQueueTotals, useCommandViewData, useCommandViewLayout, useCommandViewStats, useCommandViewStore, useCostBreakdown, useCostByModel, useCostSummary, useCostTrends, useCreateSchedule, useCreateSession, useDashboardMetrics, useDeleteExecution, useDeleteSchedule, useDeleteSession, useDeleteTask, useDeploymentDocs, useErrorAnalysis, useErrorDetail, useErrorDetails, useErrorDistribution, useErrorNotification, useExecuteAsync, useExecution, useExecutionHealth, useExecutionLogSSE, useExecutionLogs, useExecutionPanelState, useExecutions, useGetExecutionHistory, useGetSchedule, useGraphStats, useListSchedules, useMarkAllAsRead, useMarkAsRead, useNotificationCount, useNotifications, usePaginationState, usePatchTask, usePauseSchedule, useRecentExecutionsByResource, useResolveAllErrors, useResolveError, useResolveErrorsByExecution, useResourceDefinition, useResourceErrors, useResourceExecutions, useResources, useResourcesHealth, useResumeSchedule, useRetryExecution, useSSEConnection, useScheduledTasks, useSession, useSessionExecution, useSessionExecutions, useSessionMessages, useSessionWebSocket, useSessions, useSortedData, useSubmitAction, useSuccessNotification, useTableSelection, useTableSort, useTestNotification, useTopFailingResources, useUnresolveError, useUnresolvedErrors, useUpdateAnchor, useUpdateSchedule, useWarningNotification };
|