@personaai/react 0.10.0 → 0.14.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/README.md +210 -210
- package/dist/docs/.nojekyll +1 -0
- package/dist/index.cjs +1176 -183
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +801 -57
- package/dist/index.d.ts +801 -57
- package/dist/index.js +1169 -180
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2453,20 +2453,38 @@ function useFiles(autoFetch = true) {
|
|
|
2453
2453
|
|
|
2454
2454
|
// src/hooks/useAgents.ts
|
|
2455
2455
|
import { useCallback as useCallback7, useEffect as useEffect7, useState as useState7 } from "react";
|
|
2456
|
-
function useAgents(
|
|
2456
|
+
function useAgents(options = true) {
|
|
2457
|
+
const opts = typeof options === "boolean" ? { autoFetch: options } : options;
|
|
2458
|
+
const { autoFetch = true, page, limit, search, category, scope } = opts;
|
|
2457
2459
|
const { fetchWithAuth } = usePersonaContext();
|
|
2458
2460
|
const [agents, setAgents] = useState7([]);
|
|
2461
|
+
const [pagination, setPagination] = useState7({
|
|
2462
|
+
total: 0,
|
|
2463
|
+
page: page ?? 1,
|
|
2464
|
+
limit: limit ?? 20,
|
|
2465
|
+
pages: 0
|
|
2466
|
+
});
|
|
2459
2467
|
const [isLoading, setIsLoading] = useState7(false);
|
|
2460
2468
|
const [error, setError] = useState7(null);
|
|
2461
2469
|
const fetchAgents = useCallback7(async () => {
|
|
2462
2470
|
setIsLoading(true);
|
|
2463
2471
|
setError(null);
|
|
2464
2472
|
try {
|
|
2465
|
-
const
|
|
2473
|
+
const query = new URLSearchParams();
|
|
2474
|
+
if (page) query.set("page", String(page));
|
|
2475
|
+
if (limit) query.set("limit", String(limit));
|
|
2476
|
+
if (search) query.set("search", search);
|
|
2477
|
+
if (category) query.set("category", category);
|
|
2478
|
+
if (scope) query.set("scope", scope);
|
|
2479
|
+
const queryStr = query.toString() ? `?${query.toString()}` : "";
|
|
2480
|
+
const res = await fetchWithAuth(`/agents${queryStr}`);
|
|
2466
2481
|
if (!res.ok) throw new Error(`Failed to list agents: ${res.statusText}`);
|
|
2467
2482
|
const data = await res.json();
|
|
2468
|
-
const items = Array.isArray(data) ? data : data?.items
|
|
2483
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
2469
2484
|
setAgents(items);
|
|
2485
|
+
setPagination(
|
|
2486
|
+
data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
|
|
2487
|
+
);
|
|
2470
2488
|
return items;
|
|
2471
2489
|
} catch (err) {
|
|
2472
2490
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
@@ -2475,83 +2493,929 @@ function useAgents(autoFetch = true) {
|
|
|
2475
2493
|
} finally {
|
|
2476
2494
|
setIsLoading(false);
|
|
2477
2495
|
}
|
|
2478
|
-
}, [fetchWithAuth]);
|
|
2496
|
+
}, [fetchWithAuth, page, limit, search, category, scope]);
|
|
2497
|
+
const getAgent = useCallback7(
|
|
2498
|
+
async (agentId) => {
|
|
2499
|
+
const res = await fetchWithAuth(`/agents/${agentId}`);
|
|
2500
|
+
if (!res.ok) throw new Error(`Failed to fetch agent: ${res.statusText}`);
|
|
2501
|
+
return res.json();
|
|
2502
|
+
},
|
|
2503
|
+
[fetchWithAuth]
|
|
2504
|
+
);
|
|
2505
|
+
const createAgent = useCallback7(
|
|
2506
|
+
async (input) => {
|
|
2507
|
+
setError(null);
|
|
2508
|
+
try {
|
|
2509
|
+
const res = await fetchWithAuth("/agents", {
|
|
2510
|
+
method: "POST",
|
|
2511
|
+
headers: { "Content-Type": "application/json" },
|
|
2512
|
+
body: JSON.stringify(input)
|
|
2513
|
+
});
|
|
2514
|
+
if (!res.ok) {
|
|
2515
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2516
|
+
throw new Error(`Failed to create agent (${res.status}): ${errText}`);
|
|
2517
|
+
}
|
|
2518
|
+
const created = await res.json();
|
|
2519
|
+
setAgents((prev) => [created, ...prev]);
|
|
2520
|
+
return created;
|
|
2521
|
+
} catch (err) {
|
|
2522
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2523
|
+
setError(errorObj);
|
|
2524
|
+
throw errorObj;
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
[fetchWithAuth]
|
|
2528
|
+
);
|
|
2529
|
+
const updateAgent = useCallback7(
|
|
2530
|
+
async (agentId, input) => {
|
|
2531
|
+
setError(null);
|
|
2532
|
+
try {
|
|
2533
|
+
const res = await fetchWithAuth(`/agents/${agentId}`, {
|
|
2534
|
+
method: "PATCH",
|
|
2535
|
+
headers: { "Content-Type": "application/json" },
|
|
2536
|
+
body: JSON.stringify(input)
|
|
2537
|
+
});
|
|
2538
|
+
if (!res.ok) {
|
|
2539
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2540
|
+
throw new Error(`Failed to update agent (${res.status}): ${errText}`);
|
|
2541
|
+
}
|
|
2542
|
+
const updated = await res.json();
|
|
2543
|
+
setAgents((prev) => prev.map((a) => a._id === agentId ? updated : a));
|
|
2544
|
+
return updated;
|
|
2545
|
+
} catch (err) {
|
|
2546
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2547
|
+
setError(errorObj);
|
|
2548
|
+
throw errorObj;
|
|
2549
|
+
}
|
|
2550
|
+
},
|
|
2551
|
+
[fetchWithAuth]
|
|
2552
|
+
);
|
|
2553
|
+
const deleteAgent = useCallback7(
|
|
2554
|
+
async (agentId) => {
|
|
2555
|
+
setError(null);
|
|
2556
|
+
try {
|
|
2557
|
+
const res = await fetchWithAuth(`/agents/${agentId}`, { method: "DELETE" });
|
|
2558
|
+
if (!res.ok) {
|
|
2559
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2560
|
+
throw new Error(`Failed to delete agent (${res.status}): ${errText}`);
|
|
2561
|
+
}
|
|
2562
|
+
setAgents((prev) => prev.filter((a) => a._id !== agentId));
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2565
|
+
setError(errorObj);
|
|
2566
|
+
throw errorObj;
|
|
2567
|
+
}
|
|
2568
|
+
},
|
|
2569
|
+
[fetchWithAuth]
|
|
2570
|
+
);
|
|
2571
|
+
const bulkDeleteAgents = useCallback7(
|
|
2572
|
+
async (ids) => {
|
|
2573
|
+
setError(null);
|
|
2574
|
+
try {
|
|
2575
|
+
const res = await fetchWithAuth("/agents/bulk-delete", {
|
|
2576
|
+
method: "POST",
|
|
2577
|
+
headers: { "Content-Type": "application/json" },
|
|
2578
|
+
body: JSON.stringify({ ids })
|
|
2579
|
+
});
|
|
2580
|
+
if (!res.ok) {
|
|
2581
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2582
|
+
throw new Error(`Failed to bulk-delete agents (${res.status}): ${errText}`);
|
|
2583
|
+
}
|
|
2584
|
+
const result = await res.json();
|
|
2585
|
+
const deletedSet = new Set(result.deleted);
|
|
2586
|
+
setAgents((prev) => prev.filter((a) => !deletedSet.has(a._id)));
|
|
2587
|
+
return result;
|
|
2588
|
+
} catch (err) {
|
|
2589
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2590
|
+
setError(errorObj);
|
|
2591
|
+
throw errorObj;
|
|
2592
|
+
}
|
|
2593
|
+
},
|
|
2594
|
+
[fetchWithAuth]
|
|
2595
|
+
);
|
|
2479
2596
|
useEffect7(() => {
|
|
2480
|
-
if (autoFetch)
|
|
2481
|
-
void fetchAgents();
|
|
2482
|
-
}
|
|
2597
|
+
if (autoFetch) void fetchAgents();
|
|
2483
2598
|
}, [autoFetch, fetchAgents]);
|
|
2484
2599
|
return {
|
|
2485
|
-
agents,
|
|
2600
|
+
agents,
|
|
2601
|
+
pagination,
|
|
2602
|
+
isLoading,
|
|
2603
|
+
error,
|
|
2604
|
+
refetch: fetchAgents,
|
|
2605
|
+
getAgent,
|
|
2606
|
+
createAgent,
|
|
2607
|
+
updateAgent,
|
|
2608
|
+
deleteAgent,
|
|
2609
|
+
bulkDeleteAgents
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// src/hooks/useConnection.ts
|
|
2614
|
+
import { useCallback as useCallback8, useEffect as useEffect8, useState as useState8 } from "react";
|
|
2615
|
+
function useConnection(autoCheck = true) {
|
|
2616
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2617
|
+
const [health, setHealth] = useState8(null);
|
|
2618
|
+
const [isConnected, setIsConnected] = useState8(false);
|
|
2619
|
+
const [isLoading, setIsLoading] = useState8(false);
|
|
2620
|
+
const checkHealth = useCallback8(async () => {
|
|
2621
|
+
setIsLoading(true);
|
|
2622
|
+
try {
|
|
2623
|
+
const res = await fetchWithAuth("/health");
|
|
2624
|
+
if (res.ok) {
|
|
2625
|
+
const data = await res.json();
|
|
2626
|
+
setHealth(data);
|
|
2627
|
+
setIsConnected(true);
|
|
2628
|
+
return data;
|
|
2629
|
+
}
|
|
2630
|
+
setIsConnected(false);
|
|
2631
|
+
return null;
|
|
2632
|
+
} catch {
|
|
2633
|
+
setIsConnected(false);
|
|
2634
|
+
return null;
|
|
2635
|
+
} finally {
|
|
2636
|
+
setIsLoading(false);
|
|
2637
|
+
}
|
|
2638
|
+
}, [fetchWithAuth]);
|
|
2639
|
+
useEffect8(() => {
|
|
2640
|
+
if (autoCheck) {
|
|
2641
|
+
void checkHealth();
|
|
2642
|
+
}
|
|
2643
|
+
}, [autoCheck, checkHealth]);
|
|
2644
|
+
return {
|
|
2645
|
+
isConnected,
|
|
2646
|
+
health,
|
|
2647
|
+
isLoading,
|
|
2648
|
+
checkHealth
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
// src/hooks/useMcpConnections.ts
|
|
2653
|
+
import { useCallback as useCallback9, useEffect as useEffect9, useState as useState9 } from "react";
|
|
2654
|
+
function useMcpConnections(options = {}) {
|
|
2655
|
+
const { defaultAgentId, fetchWithAuth } = usePersonaContext();
|
|
2656
|
+
const agentId = options.agentId ?? defaultAgentId;
|
|
2657
|
+
const autoFetch = options.autoFetch ?? true;
|
|
2658
|
+
const [connections, setConnections] = useState9([]);
|
|
2659
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
2660
|
+
const [error, setError] = useState9(null);
|
|
2661
|
+
const fetchConnections = useCallback9(async () => {
|
|
2662
|
+
if (!agentId) return [];
|
|
2663
|
+
setIsLoading(true);
|
|
2664
|
+
setError(null);
|
|
2665
|
+
try {
|
|
2666
|
+
const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
2667
|
+
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
|
|
2668
|
+
const res = await fetchWithAuth(
|
|
2669
|
+
`/agents/${agentId}/mcp-connections${query}`
|
|
2670
|
+
);
|
|
2671
|
+
if (!res.ok)
|
|
2672
|
+
throw new Error(`Failed to load MCP connections: ${res.statusText}`);
|
|
2673
|
+
const data = await res.json();
|
|
2674
|
+
const items = Array.isArray(data) ? data : data?.items || [];
|
|
2675
|
+
setConnections(items);
|
|
2676
|
+
return items;
|
|
2677
|
+
} catch (err) {
|
|
2678
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2679
|
+
setError(errorObj);
|
|
2680
|
+
return [];
|
|
2681
|
+
} finally {
|
|
2682
|
+
setIsLoading(false);
|
|
2683
|
+
}
|
|
2684
|
+
}, [agentId, fetchWithAuth, options.returnTo]);
|
|
2685
|
+
const [isDisconnecting, setIsDisconnecting] = useState9(false);
|
|
2686
|
+
const disconnect = useCallback9(
|
|
2687
|
+
async (mcpId) => {
|
|
2688
|
+
setIsDisconnecting(true);
|
|
2689
|
+
setError(null);
|
|
2690
|
+
try {
|
|
2691
|
+
const res = await fetchWithAuth(`/mcps/${mcpId}/oauth/user/connection`, {
|
|
2692
|
+
method: "DELETE"
|
|
2693
|
+
});
|
|
2694
|
+
if (!res.ok)
|
|
2695
|
+
throw new Error(`Failed to disconnect MCP: ${res.statusText}`);
|
|
2696
|
+
setConnections(
|
|
2697
|
+
(prev) => prev.map(
|
|
2698
|
+
(c) => c.mcpId === mcpId ? { ...c, connected: false, authorizeUrl: null } : c
|
|
2699
|
+
)
|
|
2700
|
+
);
|
|
2701
|
+
void fetchConnections();
|
|
2702
|
+
} catch (err) {
|
|
2703
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2704
|
+
setError(errorObj);
|
|
2705
|
+
throw errorObj;
|
|
2706
|
+
} finally {
|
|
2707
|
+
setIsDisconnecting(false);
|
|
2708
|
+
}
|
|
2709
|
+
},
|
|
2710
|
+
[fetchWithAuth, fetchConnections]
|
|
2711
|
+
);
|
|
2712
|
+
useEffect9(() => {
|
|
2713
|
+
if (autoFetch) void fetchConnections();
|
|
2714
|
+
}, [autoFetch, fetchConnections]);
|
|
2715
|
+
return {
|
|
2716
|
+
connections,
|
|
2717
|
+
/** Convenience filter for the common "show a banner for what's missing" case. */
|
|
2718
|
+
unconnected: connections.filter((c) => !c.connected),
|
|
2719
|
+
isLoading,
|
|
2720
|
+
error,
|
|
2721
|
+
refetch: fetchConnections,
|
|
2722
|
+
disconnect,
|
|
2723
|
+
isDisconnecting
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
// src/hooks/useMcp.ts
|
|
2728
|
+
import { useCallback as useCallback10 } from "react";
|
|
2729
|
+
function useMcp(options = {}) {
|
|
2730
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2731
|
+
const defaultMcpId = options.mcpId;
|
|
2732
|
+
const readResource = useCallback10(
|
|
2733
|
+
async (uri, mcpId) => {
|
|
2734
|
+
const id = mcpId ?? defaultMcpId;
|
|
2735
|
+
if (!id) throw new Error("MCP server ID is required to read resource");
|
|
2736
|
+
const res = await fetchWithAuth(
|
|
2737
|
+
`/mcps/${id}/resource?uri=${encodeURIComponent(uri)}`
|
|
2738
|
+
);
|
|
2739
|
+
if (!res.ok) {
|
|
2740
|
+
throw new Error(`Failed to read MCP resource: ${res.statusText}`);
|
|
2741
|
+
}
|
|
2742
|
+
return await res.json();
|
|
2743
|
+
},
|
|
2744
|
+
[fetchWithAuth, defaultMcpId]
|
|
2745
|
+
);
|
|
2746
|
+
const callTool = useCallback10(
|
|
2747
|
+
async (name, args, mcpId) => {
|
|
2748
|
+
const id = mcpId ?? defaultMcpId;
|
|
2749
|
+
if (!id) throw new Error("MCP server ID is required to call tool");
|
|
2750
|
+
const res = await fetchWithAuth(`/mcps/${id}/call-tool`, {
|
|
2751
|
+
method: "POST",
|
|
2752
|
+
headers: { "Content-Type": "application/json" },
|
|
2753
|
+
body: JSON.stringify({ name, arguments: args })
|
|
2754
|
+
});
|
|
2755
|
+
if (!res.ok) {
|
|
2756
|
+
throw new Error(`Failed to call MCP tool: ${res.statusText}`);
|
|
2757
|
+
}
|
|
2758
|
+
return await res.json();
|
|
2759
|
+
},
|
|
2760
|
+
[fetchWithAuth, defaultMcpId]
|
|
2761
|
+
);
|
|
2762
|
+
return {
|
|
2763
|
+
readResource,
|
|
2764
|
+
callTool
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// src/hooks/useSkills.ts
|
|
2769
|
+
import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
|
|
2770
|
+
function useSkills(options = {}) {
|
|
2771
|
+
const { autoFetch = true, page, limit, search, scope } = options;
|
|
2772
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2773
|
+
const [skills, setSkills] = useState10([]);
|
|
2774
|
+
const [pagination, setPagination] = useState10({
|
|
2775
|
+
total: 0,
|
|
2776
|
+
page: page ?? 1,
|
|
2777
|
+
limit: limit ?? 20,
|
|
2778
|
+
pages: 0
|
|
2779
|
+
});
|
|
2780
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
2781
|
+
const [error, setError] = useState10(null);
|
|
2782
|
+
const fetchSkills = useCallback11(async () => {
|
|
2783
|
+
setIsLoading(true);
|
|
2784
|
+
setError(null);
|
|
2785
|
+
try {
|
|
2786
|
+
const query = new URLSearchParams();
|
|
2787
|
+
if (page) query.set("page", String(page));
|
|
2788
|
+
if (limit) query.set("limit", String(limit));
|
|
2789
|
+
if (search) query.set("search", search);
|
|
2790
|
+
if (scope) query.set("scope", scope);
|
|
2791
|
+
const queryStr = query.toString() ? `?${query.toString()}` : "";
|
|
2792
|
+
const res = await fetchWithAuth(`/skills${queryStr}`);
|
|
2793
|
+
if (!res.ok) throw new Error(`Failed to list skills: ${res.statusText}`);
|
|
2794
|
+
const data = await res.json();
|
|
2795
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
2796
|
+
setSkills(items);
|
|
2797
|
+
setPagination(
|
|
2798
|
+
data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
|
|
2799
|
+
);
|
|
2800
|
+
return items;
|
|
2801
|
+
} catch (err) {
|
|
2802
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2803
|
+
setError(errorObj);
|
|
2804
|
+
return [];
|
|
2805
|
+
} finally {
|
|
2806
|
+
setIsLoading(false);
|
|
2807
|
+
}
|
|
2808
|
+
}, [fetchWithAuth, page, limit, search, scope]);
|
|
2809
|
+
const getSkill = useCallback11(
|
|
2810
|
+
async (skillId) => {
|
|
2811
|
+
const res = await fetchWithAuth(`/skills/${skillId}`);
|
|
2812
|
+
if (!res.ok) throw new Error(`Failed to fetch skill: ${res.statusText}`);
|
|
2813
|
+
return res.json();
|
|
2814
|
+
},
|
|
2815
|
+
[fetchWithAuth]
|
|
2816
|
+
);
|
|
2817
|
+
const createSkill = useCallback11(
|
|
2818
|
+
async (input) => {
|
|
2819
|
+
setError(null);
|
|
2820
|
+
try {
|
|
2821
|
+
const res = await fetchWithAuth("/skills", {
|
|
2822
|
+
method: "POST",
|
|
2823
|
+
headers: { "Content-Type": "application/json" },
|
|
2824
|
+
body: JSON.stringify(input)
|
|
2825
|
+
});
|
|
2826
|
+
if (!res.ok) {
|
|
2827
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2828
|
+
throw new Error(`Failed to create skill (${res.status}): ${errText}`);
|
|
2829
|
+
}
|
|
2830
|
+
const created = await res.json();
|
|
2831
|
+
setSkills((prev) => [created, ...prev]);
|
|
2832
|
+
return created;
|
|
2833
|
+
} catch (err) {
|
|
2834
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2835
|
+
setError(errorObj);
|
|
2836
|
+
throw errorObj;
|
|
2837
|
+
}
|
|
2838
|
+
},
|
|
2839
|
+
[fetchWithAuth]
|
|
2840
|
+
);
|
|
2841
|
+
const updateSkill = useCallback11(
|
|
2842
|
+
async (skillId, input) => {
|
|
2843
|
+
setError(null);
|
|
2844
|
+
try {
|
|
2845
|
+
const res = await fetchWithAuth(`/skills/${skillId}`, {
|
|
2846
|
+
method: "PATCH",
|
|
2847
|
+
headers: { "Content-Type": "application/json" },
|
|
2848
|
+
body: JSON.stringify(input)
|
|
2849
|
+
});
|
|
2850
|
+
if (!res.ok) {
|
|
2851
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2852
|
+
throw new Error(`Failed to update skill (${res.status}): ${errText}`);
|
|
2853
|
+
}
|
|
2854
|
+
const updated = await res.json();
|
|
2855
|
+
setSkills((prev) => prev.map((s) => s._id === skillId ? updated : s));
|
|
2856
|
+
return updated;
|
|
2857
|
+
} catch (err) {
|
|
2858
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2859
|
+
setError(errorObj);
|
|
2860
|
+
throw errorObj;
|
|
2861
|
+
}
|
|
2862
|
+
},
|
|
2863
|
+
[fetchWithAuth]
|
|
2864
|
+
);
|
|
2865
|
+
const deleteSkill = useCallback11(
|
|
2866
|
+
async (skillId) => {
|
|
2867
|
+
setError(null);
|
|
2868
|
+
try {
|
|
2869
|
+
const res = await fetchWithAuth(`/skills/${skillId}`, { method: "DELETE" });
|
|
2870
|
+
if (!res.ok) {
|
|
2871
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2872
|
+
throw new Error(`Failed to delete skill (${res.status}): ${errText}`);
|
|
2873
|
+
}
|
|
2874
|
+
setSkills((prev) => prev.filter((s) => s._id !== skillId));
|
|
2875
|
+
} catch (err) {
|
|
2876
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2877
|
+
setError(errorObj);
|
|
2878
|
+
throw errorObj;
|
|
2879
|
+
}
|
|
2880
|
+
},
|
|
2881
|
+
[fetchWithAuth]
|
|
2882
|
+
);
|
|
2883
|
+
const bulkDeleteSkills = useCallback11(
|
|
2884
|
+
async (ids) => {
|
|
2885
|
+
setError(null);
|
|
2886
|
+
try {
|
|
2887
|
+
const res = await fetchWithAuth("/skills/bulk-delete", {
|
|
2888
|
+
method: "POST",
|
|
2889
|
+
headers: { "Content-Type": "application/json" },
|
|
2890
|
+
body: JSON.stringify({ ids })
|
|
2891
|
+
});
|
|
2892
|
+
if (!res.ok) {
|
|
2893
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2894
|
+
throw new Error(`Failed to bulk-delete skills (${res.status}): ${errText}`);
|
|
2895
|
+
}
|
|
2896
|
+
const result = await res.json();
|
|
2897
|
+
const deletedSet = new Set(result.deleted);
|
|
2898
|
+
setSkills((prev) => prev.filter((s) => !deletedSet.has(s._id)));
|
|
2899
|
+
return result;
|
|
2900
|
+
} catch (err) {
|
|
2901
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2902
|
+
setError(errorObj);
|
|
2903
|
+
throw errorObj;
|
|
2904
|
+
}
|
|
2905
|
+
},
|
|
2906
|
+
[fetchWithAuth]
|
|
2907
|
+
);
|
|
2908
|
+
const getSkillUsage = useCallback11(
|
|
2909
|
+
async (skillId) => {
|
|
2910
|
+
const res = await fetchWithAuth(`/skills/${skillId}/usage`);
|
|
2911
|
+
if (!res.ok) throw new Error(`Failed to fetch skill usage: ${res.statusText}`);
|
|
2912
|
+
return res.json();
|
|
2913
|
+
},
|
|
2914
|
+
[fetchWithAuth]
|
|
2915
|
+
);
|
|
2916
|
+
useEffect10(() => {
|
|
2917
|
+
if (autoFetch) void fetchSkills();
|
|
2918
|
+
}, [autoFetch, fetchSkills]);
|
|
2919
|
+
return {
|
|
2920
|
+
skills,
|
|
2921
|
+
pagination,
|
|
2922
|
+
isLoading,
|
|
2923
|
+
error,
|
|
2924
|
+
refetch: fetchSkills,
|
|
2925
|
+
getSkill,
|
|
2926
|
+
createSkill,
|
|
2927
|
+
updateSkill,
|
|
2928
|
+
deleteSkill,
|
|
2929
|
+
bulkDeleteSkills,
|
|
2930
|
+
getSkillUsage
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// src/hooks/useRcpSources.ts
|
|
2935
|
+
import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
|
|
2936
|
+
function useRcpSources(options = {}) {
|
|
2937
|
+
const { autoFetch = true, page, limit, search } = options;
|
|
2938
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
2939
|
+
const [rcpSources, setRcpSources] = useState11([]);
|
|
2940
|
+
const [pagination, setPagination] = useState11({
|
|
2941
|
+
total: 0,
|
|
2942
|
+
page: page ?? 1,
|
|
2943
|
+
limit: limit ?? 20,
|
|
2944
|
+
pages: 0
|
|
2945
|
+
});
|
|
2946
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
2947
|
+
const [error, setError] = useState11(null);
|
|
2948
|
+
const fetchRcpSources = useCallback12(async () => {
|
|
2949
|
+
setIsLoading(true);
|
|
2950
|
+
setError(null);
|
|
2951
|
+
try {
|
|
2952
|
+
const query = new URLSearchParams();
|
|
2953
|
+
if (page) query.set("page", String(page));
|
|
2954
|
+
if (limit) query.set("limit", String(limit));
|
|
2955
|
+
if (search) query.set("search", search);
|
|
2956
|
+
const queryStr = query.toString() ? `?${query.toString()}` : "";
|
|
2957
|
+
const res = await fetchWithAuth(`/rcp-sources${queryStr}`);
|
|
2958
|
+
if (!res.ok) throw new Error(`Failed to list RCP sources: ${res.statusText}`);
|
|
2959
|
+
const data = await res.json();
|
|
2960
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
2961
|
+
setRcpSources(items);
|
|
2962
|
+
setPagination(
|
|
2963
|
+
data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
|
|
2964
|
+
);
|
|
2965
|
+
return items;
|
|
2966
|
+
} catch (err) {
|
|
2967
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
2968
|
+
setError(errorObj);
|
|
2969
|
+
return [];
|
|
2970
|
+
} finally {
|
|
2971
|
+
setIsLoading(false);
|
|
2972
|
+
}
|
|
2973
|
+
}, [fetchWithAuth, page, limit, search]);
|
|
2974
|
+
const getRcpSource = useCallback12(
|
|
2975
|
+
async (sourceId) => {
|
|
2976
|
+
const res = await fetchWithAuth(`/rcp-sources/${sourceId}`);
|
|
2977
|
+
if (!res.ok) throw new Error(`Failed to fetch RCP source: ${res.statusText}`);
|
|
2978
|
+
return res.json();
|
|
2979
|
+
},
|
|
2980
|
+
[fetchWithAuth]
|
|
2981
|
+
);
|
|
2982
|
+
const createRcpSource = useCallback12(
|
|
2983
|
+
async (input) => {
|
|
2984
|
+
setError(null);
|
|
2985
|
+
try {
|
|
2986
|
+
const res = await fetchWithAuth("/rcp-sources", {
|
|
2987
|
+
method: "POST",
|
|
2988
|
+
headers: { "Content-Type": "application/json" },
|
|
2989
|
+
body: JSON.stringify(input)
|
|
2990
|
+
});
|
|
2991
|
+
if (!res.ok) {
|
|
2992
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
2993
|
+
throw new Error(`Failed to create RCP source (${res.status}): ${errText}`);
|
|
2994
|
+
}
|
|
2995
|
+
const created = await res.json();
|
|
2996
|
+
setRcpSources((prev) => [created, ...prev]);
|
|
2997
|
+
return created;
|
|
2998
|
+
} catch (err) {
|
|
2999
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3000
|
+
setError(errorObj);
|
|
3001
|
+
throw errorObj;
|
|
3002
|
+
}
|
|
3003
|
+
},
|
|
3004
|
+
[fetchWithAuth]
|
|
3005
|
+
);
|
|
3006
|
+
const updateRcpSource = useCallback12(
|
|
3007
|
+
async (sourceId, input) => {
|
|
3008
|
+
setError(null);
|
|
3009
|
+
try {
|
|
3010
|
+
const res = await fetchWithAuth(`/rcp-sources/${sourceId}`, {
|
|
3011
|
+
method: "PATCH",
|
|
3012
|
+
headers: { "Content-Type": "application/json" },
|
|
3013
|
+
body: JSON.stringify(input)
|
|
3014
|
+
});
|
|
3015
|
+
if (!res.ok) {
|
|
3016
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3017
|
+
throw new Error(`Failed to update RCP source (${res.status}): ${errText}`);
|
|
3018
|
+
}
|
|
3019
|
+
const updated = await res.json();
|
|
3020
|
+
setRcpSources((prev) => prev.map((s) => s._id === sourceId ? updated : s));
|
|
3021
|
+
return updated;
|
|
3022
|
+
} catch (err) {
|
|
3023
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3024
|
+
setError(errorObj);
|
|
3025
|
+
throw errorObj;
|
|
3026
|
+
}
|
|
3027
|
+
},
|
|
3028
|
+
[fetchWithAuth]
|
|
3029
|
+
);
|
|
3030
|
+
const deleteRcpSource = useCallback12(
|
|
3031
|
+
async (sourceId) => {
|
|
3032
|
+
setError(null);
|
|
3033
|
+
try {
|
|
3034
|
+
const res = await fetchWithAuth(`/rcp-sources/${sourceId}`, { method: "DELETE" });
|
|
3035
|
+
if (!res.ok) {
|
|
3036
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3037
|
+
throw new Error(`Failed to delete RCP source (${res.status}): ${errText}`);
|
|
3038
|
+
}
|
|
3039
|
+
setRcpSources((prev) => prev.filter((s) => s._id !== sourceId));
|
|
3040
|
+
} catch (err) {
|
|
3041
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3042
|
+
setError(errorObj);
|
|
3043
|
+
throw errorObj;
|
|
3044
|
+
}
|
|
3045
|
+
},
|
|
3046
|
+
[fetchWithAuth]
|
|
3047
|
+
);
|
|
3048
|
+
const bulkDeleteRcpSources = useCallback12(
|
|
3049
|
+
async (ids) => {
|
|
3050
|
+
setError(null);
|
|
3051
|
+
try {
|
|
3052
|
+
const res = await fetchWithAuth("/rcp-sources/bulk-delete", {
|
|
3053
|
+
method: "POST",
|
|
3054
|
+
headers: { "Content-Type": "application/json" },
|
|
3055
|
+
body: JSON.stringify({ ids })
|
|
3056
|
+
});
|
|
3057
|
+
if (!res.ok) {
|
|
3058
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3059
|
+
throw new Error(`Failed to bulk-delete RCP sources (${res.status}): ${errText}`);
|
|
3060
|
+
}
|
|
3061
|
+
const result = await res.json();
|
|
3062
|
+
const deletedSet = new Set(result.deleted);
|
|
3063
|
+
setRcpSources((prev) => prev.filter((s) => !deletedSet.has(s._id)));
|
|
3064
|
+
return result;
|
|
3065
|
+
} catch (err) {
|
|
3066
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3067
|
+
setError(errorObj);
|
|
3068
|
+
throw errorObj;
|
|
3069
|
+
}
|
|
3070
|
+
},
|
|
3071
|
+
[fetchWithAuth]
|
|
3072
|
+
);
|
|
3073
|
+
const getRcpSourceUsage = useCallback12(
|
|
3074
|
+
async (sourceId) => {
|
|
3075
|
+
const res = await fetchWithAuth(`/rcp-sources/${sourceId}/usage`);
|
|
3076
|
+
if (!res.ok) throw new Error(`Failed to fetch RCP source usage: ${res.statusText}`);
|
|
3077
|
+
return res.json();
|
|
3078
|
+
},
|
|
3079
|
+
[fetchWithAuth]
|
|
3080
|
+
);
|
|
3081
|
+
const testRcpSourceConnection = useCallback12(
|
|
3082
|
+
async (sourceId) => {
|
|
3083
|
+
setError(null);
|
|
3084
|
+
try {
|
|
3085
|
+
const res = await fetchWithAuth(`/rcp-sources/${sourceId}/test`, { method: "POST" });
|
|
3086
|
+
if (!res.ok) {
|
|
3087
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3088
|
+
throw new Error(`Failed to test RCP source connection (${res.status}): ${errText}`);
|
|
3089
|
+
}
|
|
3090
|
+
const result = await res.json();
|
|
3091
|
+
setRcpSources(
|
|
3092
|
+
(prev) => prev.map(
|
|
3093
|
+
(s) => s._id === sourceId ? { ...s, tools: result.tools, lastTestedAt: (/* @__PURE__ */ new Date()).toISOString() } : s
|
|
3094
|
+
)
|
|
3095
|
+
);
|
|
3096
|
+
return result;
|
|
3097
|
+
} catch (err) {
|
|
3098
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3099
|
+
setError(errorObj);
|
|
3100
|
+
throw errorObj;
|
|
3101
|
+
}
|
|
3102
|
+
},
|
|
3103
|
+
[fetchWithAuth]
|
|
3104
|
+
);
|
|
3105
|
+
useEffect11(() => {
|
|
3106
|
+
if (autoFetch) void fetchRcpSources();
|
|
3107
|
+
}, [autoFetch, fetchRcpSources]);
|
|
3108
|
+
return {
|
|
3109
|
+
rcpSources,
|
|
3110
|
+
pagination,
|
|
3111
|
+
isLoading,
|
|
3112
|
+
error,
|
|
3113
|
+
refetch: fetchRcpSources,
|
|
3114
|
+
getRcpSource,
|
|
3115
|
+
createRcpSource,
|
|
3116
|
+
updateRcpSource,
|
|
3117
|
+
deleteRcpSource,
|
|
3118
|
+
bulkDeleteRcpSources,
|
|
3119
|
+
getRcpSourceUsage,
|
|
3120
|
+
testRcpSourceConnection
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// src/hooks/useKnowledgeBases.ts
|
|
3125
|
+
import { useCallback as useCallback13, useEffect as useEffect12, useState as useState12 } from "react";
|
|
3126
|
+
function useKnowledgeBases(options = {}) {
|
|
3127
|
+
const { autoFetch = true, page, limit, search, scope } = options;
|
|
3128
|
+
const { fetchWithAuth } = usePersonaContext();
|
|
3129
|
+
const [knowledgeBases, setKnowledgeBases] = useState12([]);
|
|
3130
|
+
const [pagination, setPagination] = useState12({
|
|
3131
|
+
total: 0,
|
|
3132
|
+
page: page ?? 1,
|
|
3133
|
+
limit: limit ?? 20,
|
|
3134
|
+
pages: 0
|
|
3135
|
+
});
|
|
3136
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
3137
|
+
const [isUploading, setIsUploading] = useState12(false);
|
|
3138
|
+
const [error, setError] = useState12(null);
|
|
3139
|
+
const fetchKnowledgeBases = useCallback13(async () => {
|
|
3140
|
+
setIsLoading(true);
|
|
3141
|
+
setError(null);
|
|
3142
|
+
try {
|
|
3143
|
+
const query = new URLSearchParams();
|
|
3144
|
+
if (page) query.set("page", String(page));
|
|
3145
|
+
if (limit) query.set("limit", String(limit));
|
|
3146
|
+
if (search) query.set("search", search);
|
|
3147
|
+
if (scope) query.set("scope", scope);
|
|
3148
|
+
const queryStr = query.toString() ? `?${query.toString()}` : "";
|
|
3149
|
+
const res = await fetchWithAuth(`/knowledge${queryStr}`);
|
|
3150
|
+
if (!res.ok) throw new Error(`Failed to list knowledge bases: ${res.statusText}`);
|
|
3151
|
+
const data = await res.json();
|
|
3152
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
3153
|
+
setKnowledgeBases(items);
|
|
3154
|
+
setPagination(
|
|
3155
|
+
data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
|
|
3156
|
+
);
|
|
3157
|
+
return items;
|
|
3158
|
+
} catch (err) {
|
|
3159
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3160
|
+
setError(errorObj);
|
|
3161
|
+
return [];
|
|
3162
|
+
} finally {
|
|
3163
|
+
setIsLoading(false);
|
|
3164
|
+
}
|
|
3165
|
+
}, [fetchWithAuth, page, limit, search, scope]);
|
|
3166
|
+
const getKnowledgeBase = useCallback13(
|
|
3167
|
+
async (kbId) => {
|
|
3168
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}`);
|
|
3169
|
+
if (!res.ok) throw new Error(`Failed to fetch knowledge base: ${res.statusText}`);
|
|
3170
|
+
return res.json();
|
|
3171
|
+
},
|
|
3172
|
+
[fetchWithAuth]
|
|
3173
|
+
);
|
|
3174
|
+
const createKnowledgeBase = useCallback13(
|
|
3175
|
+
async (input) => {
|
|
3176
|
+
setError(null);
|
|
3177
|
+
try {
|
|
3178
|
+
const res = await fetchWithAuth("/knowledge", {
|
|
3179
|
+
method: "POST",
|
|
3180
|
+
headers: { "Content-Type": "application/json" },
|
|
3181
|
+
body: JSON.stringify(input)
|
|
3182
|
+
});
|
|
3183
|
+
if (!res.ok) {
|
|
3184
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3185
|
+
throw new Error(`Failed to create knowledge base (${res.status}): ${errText}`);
|
|
3186
|
+
}
|
|
3187
|
+
const created = await res.json();
|
|
3188
|
+
setKnowledgeBases((prev) => [created, ...prev]);
|
|
3189
|
+
return created;
|
|
3190
|
+
} catch (err) {
|
|
3191
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3192
|
+
setError(errorObj);
|
|
3193
|
+
throw errorObj;
|
|
3194
|
+
}
|
|
3195
|
+
},
|
|
3196
|
+
[fetchWithAuth]
|
|
3197
|
+
);
|
|
3198
|
+
const updateKnowledgeBase = useCallback13(
|
|
3199
|
+
async (kbId, input) => {
|
|
3200
|
+
setError(null);
|
|
3201
|
+
try {
|
|
3202
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}`, {
|
|
3203
|
+
method: "PATCH",
|
|
3204
|
+
headers: { "Content-Type": "application/json" },
|
|
3205
|
+
body: JSON.stringify(input)
|
|
3206
|
+
});
|
|
3207
|
+
if (!res.ok) {
|
|
3208
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3209
|
+
throw new Error(`Failed to update knowledge base (${res.status}): ${errText}`);
|
|
3210
|
+
}
|
|
3211
|
+
const updated = await res.json();
|
|
3212
|
+
setKnowledgeBases((prev) => prev.map((kb) => kb._id === kbId ? updated : kb));
|
|
3213
|
+
return updated;
|
|
3214
|
+
} catch (err) {
|
|
3215
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3216
|
+
setError(errorObj);
|
|
3217
|
+
throw errorObj;
|
|
3218
|
+
}
|
|
3219
|
+
},
|
|
3220
|
+
[fetchWithAuth]
|
|
3221
|
+
);
|
|
3222
|
+
const deleteKnowledgeBase = useCallback13(
|
|
3223
|
+
async (kbId) => {
|
|
3224
|
+
setError(null);
|
|
3225
|
+
try {
|
|
3226
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}`, { method: "DELETE" });
|
|
3227
|
+
if (!res.ok) {
|
|
3228
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3229
|
+
throw new Error(`Failed to delete knowledge base (${res.status}): ${errText}`);
|
|
3230
|
+
}
|
|
3231
|
+
setKnowledgeBases((prev) => prev.filter((kb) => kb._id !== kbId));
|
|
3232
|
+
} catch (err) {
|
|
3233
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3234
|
+
setError(errorObj);
|
|
3235
|
+
throw errorObj;
|
|
3236
|
+
}
|
|
3237
|
+
},
|
|
3238
|
+
[fetchWithAuth]
|
|
3239
|
+
);
|
|
3240
|
+
const bulkDeleteKnowledgeBases = useCallback13(
|
|
3241
|
+
async (ids) => {
|
|
3242
|
+
setError(null);
|
|
3243
|
+
try {
|
|
3244
|
+
const res = await fetchWithAuth("/knowledge/bulk-delete", {
|
|
3245
|
+
method: "POST",
|
|
3246
|
+
headers: { "Content-Type": "application/json" },
|
|
3247
|
+
body: JSON.stringify({ ids })
|
|
3248
|
+
});
|
|
3249
|
+
if (!res.ok) {
|
|
3250
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3251
|
+
throw new Error(`Failed to bulk-delete knowledge bases (${res.status}): ${errText}`);
|
|
3252
|
+
}
|
|
3253
|
+
const result = await res.json();
|
|
3254
|
+
const deletedSet = new Set(result.deleted);
|
|
3255
|
+
setKnowledgeBases((prev) => prev.filter((kb) => !deletedSet.has(kb._id)));
|
|
3256
|
+
return result;
|
|
3257
|
+
} catch (err) {
|
|
3258
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3259
|
+
setError(errorObj);
|
|
3260
|
+
throw errorObj;
|
|
3261
|
+
}
|
|
3262
|
+
},
|
|
3263
|
+
[fetchWithAuth]
|
|
3264
|
+
);
|
|
3265
|
+
const getKnowledgeBaseUsage = useCallback13(
|
|
3266
|
+
async (kbId) => {
|
|
3267
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}/usage`);
|
|
3268
|
+
if (!res.ok) throw new Error(`Failed to fetch knowledge base usage: ${res.statusText}`);
|
|
3269
|
+
return res.json();
|
|
3270
|
+
},
|
|
3271
|
+
[fetchWithAuth]
|
|
3272
|
+
);
|
|
3273
|
+
const uploadDocuments = useCallback13(
|
|
3274
|
+
async (kbId, files) => {
|
|
3275
|
+
setIsUploading(true);
|
|
3276
|
+
setError(null);
|
|
3277
|
+
try {
|
|
3278
|
+
const body = new FormData();
|
|
3279
|
+
for (const file of files) {
|
|
3280
|
+
if (file.content instanceof Blob) {
|
|
3281
|
+
body.append("files", file.content, file.filename);
|
|
3282
|
+
} else {
|
|
3283
|
+
body.append("files", {
|
|
3284
|
+
uri: file.content.uri,
|
|
3285
|
+
name: file.content.name ?? file.filename,
|
|
3286
|
+
type: file.content.type ?? file.contentType
|
|
3287
|
+
});
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}/documents`, {
|
|
3291
|
+
method: "POST",
|
|
3292
|
+
body
|
|
3293
|
+
});
|
|
3294
|
+
if (!res.ok) {
|
|
3295
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3296
|
+
throw new Error(`Failed to upload documents (${res.status}): ${errText}`);
|
|
3297
|
+
}
|
|
3298
|
+
const result = await res.json();
|
|
3299
|
+
setKnowledgeBases(
|
|
3300
|
+
(prev) => prev.map(
|
|
3301
|
+
(kb) => kb._id === kbId ? { ...kb, documentCount: result.documentCount, chunkCount: result.chunkCount } : kb
|
|
3302
|
+
)
|
|
3303
|
+
);
|
|
3304
|
+
return result;
|
|
3305
|
+
} catch (err) {
|
|
3306
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3307
|
+
setError(errorObj);
|
|
3308
|
+
throw errorObj;
|
|
3309
|
+
} finally {
|
|
3310
|
+
setIsUploading(false);
|
|
3311
|
+
}
|
|
3312
|
+
},
|
|
3313
|
+
[fetchWithAuth]
|
|
3314
|
+
);
|
|
3315
|
+
const listDocuments = useCallback13(
|
|
3316
|
+
async (kbId) => {
|
|
3317
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}/documents`);
|
|
3318
|
+
if (!res.ok) throw new Error(`Failed to list documents: ${res.statusText}`);
|
|
3319
|
+
return res.json();
|
|
3320
|
+
},
|
|
3321
|
+
[fetchWithAuth]
|
|
3322
|
+
);
|
|
3323
|
+
const deleteDocument = useCallback13(
|
|
3324
|
+
async (kbId, sourceName) => {
|
|
3325
|
+
setError(null);
|
|
3326
|
+
try {
|
|
3327
|
+
const res = await fetchWithAuth(
|
|
3328
|
+
`/knowledge/${kbId}/documents/${encodeURIComponent(sourceName)}`,
|
|
3329
|
+
{ method: "DELETE" }
|
|
3330
|
+
);
|
|
3331
|
+
if (!res.ok) {
|
|
3332
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3333
|
+
throw new Error(`Failed to delete document (${res.status}): ${errText}`);
|
|
3334
|
+
}
|
|
3335
|
+
const result = await res.json();
|
|
3336
|
+
setKnowledgeBases(
|
|
3337
|
+
(prev) => prev.map(
|
|
3338
|
+
(kb) => kb._id === kbId ? { ...kb, documentCount: result.remainingDocuments, chunkCount: result.remainingChunks } : kb
|
|
3339
|
+
)
|
|
3340
|
+
);
|
|
3341
|
+
return result;
|
|
3342
|
+
} catch (err) {
|
|
3343
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3344
|
+
setError(errorObj);
|
|
3345
|
+
throw errorObj;
|
|
3346
|
+
}
|
|
3347
|
+
},
|
|
3348
|
+
[fetchWithAuth]
|
|
3349
|
+
);
|
|
3350
|
+
const searchKnowledgeBase = useCallback13(
|
|
3351
|
+
async (kbId, query, opts) => {
|
|
3352
|
+
const res = await fetchWithAuth(`/knowledge/${kbId}/search`, {
|
|
3353
|
+
method: "POST",
|
|
3354
|
+
headers: { "Content-Type": "application/json" },
|
|
3355
|
+
body: JSON.stringify({ query, topK: opts?.topK })
|
|
3356
|
+
});
|
|
3357
|
+
if (!res.ok) {
|
|
3358
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3359
|
+
throw new Error(`Failed to search knowledge base (${res.status}): ${errText}`);
|
|
3360
|
+
}
|
|
3361
|
+
return res.json();
|
|
3362
|
+
},
|
|
3363
|
+
[fetchWithAuth]
|
|
3364
|
+
);
|
|
3365
|
+
useEffect12(() => {
|
|
3366
|
+
if (autoFetch) void fetchKnowledgeBases();
|
|
3367
|
+
}, [autoFetch, fetchKnowledgeBases]);
|
|
3368
|
+
return {
|
|
3369
|
+
knowledgeBases,
|
|
3370
|
+
pagination,
|
|
2486
3371
|
isLoading,
|
|
3372
|
+
isUploading,
|
|
2487
3373
|
error,
|
|
2488
|
-
refetch:
|
|
3374
|
+
refetch: fetchKnowledgeBases,
|
|
3375
|
+
getKnowledgeBase,
|
|
3376
|
+
createKnowledgeBase,
|
|
3377
|
+
updateKnowledgeBase,
|
|
3378
|
+
deleteKnowledgeBase,
|
|
3379
|
+
bulkDeleteKnowledgeBases,
|
|
3380
|
+
getKnowledgeBaseUsage,
|
|
3381
|
+
uploadDocuments,
|
|
3382
|
+
listDocuments,
|
|
3383
|
+
deleteDocument,
|
|
3384
|
+
search: searchKnowledgeBase
|
|
2489
3385
|
};
|
|
2490
3386
|
}
|
|
2491
3387
|
|
|
2492
|
-
// src/hooks/
|
|
2493
|
-
import { useCallback as
|
|
2494
|
-
function
|
|
3388
|
+
// src/hooks/useMcpAdmin.ts
|
|
3389
|
+
import { useCallback as useCallback14, useEffect as useEffect13, useState as useState13 } from "react";
|
|
3390
|
+
function useMcpAdmin(options = {}) {
|
|
3391
|
+
const { autoFetch = true, page, limit, search } = options;
|
|
2495
3392
|
const { fetchWithAuth } = usePersonaContext();
|
|
2496
|
-
const [
|
|
2497
|
-
const [
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
setIsConnected(true);
|
|
2507
|
-
return data;
|
|
2508
|
-
}
|
|
2509
|
-
setIsConnected(false);
|
|
2510
|
-
return null;
|
|
2511
|
-
} catch {
|
|
2512
|
-
setIsConnected(false);
|
|
2513
|
-
return null;
|
|
2514
|
-
} finally {
|
|
2515
|
-
setIsLoading(false);
|
|
2516
|
-
}
|
|
2517
|
-
}, [fetchWithAuth]);
|
|
2518
|
-
useEffect8(() => {
|
|
2519
|
-
if (autoCheck) {
|
|
2520
|
-
void checkHealth();
|
|
2521
|
-
}
|
|
2522
|
-
}, [autoCheck, checkHealth]);
|
|
2523
|
-
return {
|
|
2524
|
-
isConnected,
|
|
2525
|
-
health,
|
|
2526
|
-
isLoading,
|
|
2527
|
-
checkHealth
|
|
2528
|
-
};
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
|
-
// src/hooks/useMcpConnections.ts
|
|
2532
|
-
import { useCallback as useCallback9, useEffect as useEffect9, useState as useState9 } from "react";
|
|
2533
|
-
function useMcpConnections(options = {}) {
|
|
2534
|
-
const { defaultAgentId, fetchWithAuth } = usePersonaContext();
|
|
2535
|
-
const agentId = options.agentId ?? defaultAgentId;
|
|
2536
|
-
const autoFetch = options.autoFetch ?? true;
|
|
2537
|
-
const [connections, setConnections] = useState9([]);
|
|
2538
|
-
const [isLoading, setIsLoading] = useState9(false);
|
|
2539
|
-
const [error, setError] = useState9(null);
|
|
2540
|
-
const fetchConnections = useCallback9(async () => {
|
|
2541
|
-
if (!agentId) return [];
|
|
3393
|
+
const [mcps, setMcps] = useState13([]);
|
|
3394
|
+
const [pagination, setPagination] = useState13({
|
|
3395
|
+
total: 0,
|
|
3396
|
+
page: page ?? 1,
|
|
3397
|
+
limit: limit ?? 20,
|
|
3398
|
+
pages: 0
|
|
3399
|
+
});
|
|
3400
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
3401
|
+
const [error, setError] = useState13(null);
|
|
3402
|
+
const fetchMcps = useCallback14(async () => {
|
|
2542
3403
|
setIsLoading(true);
|
|
2543
3404
|
setError(null);
|
|
2544
3405
|
try {
|
|
2545
|
-
const
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
);
|
|
2550
|
-
|
|
2551
|
-
|
|
3406
|
+
const query = new URLSearchParams();
|
|
3407
|
+
if (page) query.set("page", String(page));
|
|
3408
|
+
if (limit) query.set("limit", String(limit));
|
|
3409
|
+
if (search) query.set("search", search);
|
|
3410
|
+
query.set("scope", "mine");
|
|
3411
|
+
const res = await fetchWithAuth(`/mcps?${query.toString()}`);
|
|
3412
|
+
if (!res.ok) throw new Error(`Failed to list MCPs: ${res.statusText}`);
|
|
2552
3413
|
const data = await res.json();
|
|
2553
|
-
const items = Array.isArray(data) ? data : data?.items
|
|
2554
|
-
|
|
3414
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
3415
|
+
setMcps(items);
|
|
3416
|
+
setPagination(
|
|
3417
|
+
data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
|
|
3418
|
+
);
|
|
2555
3419
|
return items;
|
|
2556
3420
|
} catch (err) {
|
|
2557
3421
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
@@ -2560,83 +3424,172 @@ function useMcpConnections(options = {}) {
|
|
|
2560
3424
|
} finally {
|
|
2561
3425
|
setIsLoading(false);
|
|
2562
3426
|
}
|
|
2563
|
-
}, [
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
)
|
|
2589
|
-
|
|
2590
|
-
|
|
3427
|
+
}, [fetchWithAuth, page, limit, search]);
|
|
3428
|
+
const getMcp = useCallback14(
|
|
3429
|
+
async (mcpId) => {
|
|
3430
|
+
const res = await fetchWithAuth(`/mcps/${mcpId}`);
|
|
3431
|
+
if (!res.ok) throw new Error(`Failed to fetch MCP: ${res.statusText}`);
|
|
3432
|
+
return res.json();
|
|
3433
|
+
},
|
|
3434
|
+
[fetchWithAuth]
|
|
3435
|
+
);
|
|
3436
|
+
const createMcp = useCallback14(
|
|
3437
|
+
async (input) => {
|
|
3438
|
+
setError(null);
|
|
3439
|
+
try {
|
|
3440
|
+
const res = await fetchWithAuth("/mcps", {
|
|
3441
|
+
method: "POST",
|
|
3442
|
+
headers: { "Content-Type": "application/json" },
|
|
3443
|
+
body: JSON.stringify(input)
|
|
3444
|
+
});
|
|
3445
|
+
if (!res.ok) {
|
|
3446
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3447
|
+
throw new Error(`Failed to create MCP (${res.status}): ${errText}`);
|
|
3448
|
+
}
|
|
3449
|
+
const created = await res.json();
|
|
3450
|
+
setMcps((prev) => [created, ...prev]);
|
|
3451
|
+
return created;
|
|
3452
|
+
} catch (err) {
|
|
3453
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3454
|
+
setError(errorObj);
|
|
3455
|
+
throw errorObj;
|
|
2591
3456
|
}
|
|
2592
|
-
return await res.json();
|
|
2593
3457
|
},
|
|
2594
|
-
[fetchWithAuth
|
|
3458
|
+
[fetchWithAuth]
|
|
2595
3459
|
);
|
|
2596
|
-
const
|
|
2597
|
-
async (
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
3460
|
+
const updateMcp = useCallback14(
|
|
3461
|
+
async (mcpId, input) => {
|
|
3462
|
+
setError(null);
|
|
3463
|
+
try {
|
|
3464
|
+
const res = await fetchWithAuth(`/mcps/${mcpId}`, {
|
|
3465
|
+
method: "PATCH",
|
|
3466
|
+
headers: { "Content-Type": "application/json" },
|
|
3467
|
+
body: JSON.stringify(input)
|
|
3468
|
+
});
|
|
3469
|
+
if (!res.ok) {
|
|
3470
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3471
|
+
throw new Error(`Failed to update MCP (${res.status}): ${errText}`);
|
|
3472
|
+
}
|
|
3473
|
+
const updated = await res.json();
|
|
3474
|
+
setMcps((prev) => prev.map((m) => m._id === mcpId ? updated : m));
|
|
3475
|
+
return updated;
|
|
3476
|
+
} catch (err) {
|
|
3477
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3478
|
+
setError(errorObj);
|
|
3479
|
+
throw errorObj;
|
|
2607
3480
|
}
|
|
2608
|
-
return await res.json();
|
|
2609
3481
|
},
|
|
2610
|
-
[fetchWithAuth
|
|
3482
|
+
[fetchWithAuth]
|
|
3483
|
+
);
|
|
3484
|
+
const deleteMcp = useCallback14(
|
|
3485
|
+
async (mcpId) => {
|
|
3486
|
+
setError(null);
|
|
3487
|
+
try {
|
|
3488
|
+
const res = await fetchWithAuth(`/mcps/${mcpId}`, { method: "DELETE" });
|
|
3489
|
+
if (!res.ok) {
|
|
3490
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3491
|
+
throw new Error(`Failed to delete MCP (${res.status}): ${errText}`);
|
|
3492
|
+
}
|
|
3493
|
+
setMcps((prev) => prev.filter((m) => m._id !== mcpId));
|
|
3494
|
+
} catch (err) {
|
|
3495
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3496
|
+
setError(errorObj);
|
|
3497
|
+
throw errorObj;
|
|
3498
|
+
}
|
|
3499
|
+
},
|
|
3500
|
+
[fetchWithAuth]
|
|
3501
|
+
);
|
|
3502
|
+
const bulkDeleteMcps = useCallback14(
|
|
3503
|
+
async (ids) => {
|
|
3504
|
+
setError(null);
|
|
3505
|
+
try {
|
|
3506
|
+
const res = await fetchWithAuth("/mcps/bulk-delete", {
|
|
3507
|
+
method: "POST",
|
|
3508
|
+
headers: { "Content-Type": "application/json" },
|
|
3509
|
+
body: JSON.stringify({ ids })
|
|
3510
|
+
});
|
|
3511
|
+
if (!res.ok) {
|
|
3512
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3513
|
+
throw new Error(`Failed to bulk-delete MCPs (${res.status}): ${errText}`);
|
|
3514
|
+
}
|
|
3515
|
+
const result = await res.json();
|
|
3516
|
+
const deletedSet = new Set(result.deleted);
|
|
3517
|
+
setMcps((prev) => prev.filter((m) => !deletedSet.has(m._id)));
|
|
3518
|
+
return result;
|
|
3519
|
+
} catch (err) {
|
|
3520
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3521
|
+
setError(errorObj);
|
|
3522
|
+
throw errorObj;
|
|
3523
|
+
}
|
|
3524
|
+
},
|
|
3525
|
+
[fetchWithAuth]
|
|
3526
|
+
);
|
|
3527
|
+
const testConnection = useCallback14(
|
|
3528
|
+
async (mcpId) => {
|
|
3529
|
+
setError(null);
|
|
3530
|
+
try {
|
|
3531
|
+
const res = await fetchWithAuth(`/mcps/${mcpId}/test`, { method: "POST" });
|
|
3532
|
+
if (!res.ok) {
|
|
3533
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3534
|
+
throw new Error(`Failed to test MCP connection (${res.status}): ${errText}`);
|
|
3535
|
+
}
|
|
3536
|
+
const result = await res.json();
|
|
3537
|
+
setMcps(
|
|
3538
|
+
(prev) => prev.map(
|
|
3539
|
+
(m) => m._id === mcpId ? { ...m, tools: result.tools, resources: result.resources, resourceTemplates: result.resourceTemplates } : m
|
|
3540
|
+
)
|
|
3541
|
+
);
|
|
3542
|
+
return result;
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
3545
|
+
setError(errorObj);
|
|
3546
|
+
throw errorObj;
|
|
3547
|
+
}
|
|
3548
|
+
},
|
|
3549
|
+
[fetchWithAuth]
|
|
2611
3550
|
);
|
|
3551
|
+
useEffect13(() => {
|
|
3552
|
+
if (autoFetch) void fetchMcps();
|
|
3553
|
+
}, [autoFetch, fetchMcps]);
|
|
2612
3554
|
return {
|
|
2613
|
-
|
|
2614
|
-
|
|
3555
|
+
mcps,
|
|
3556
|
+
pagination,
|
|
3557
|
+
isLoading,
|
|
3558
|
+
error,
|
|
3559
|
+
refetch: fetchMcps,
|
|
3560
|
+
getMcp,
|
|
3561
|
+
createMcp,
|
|
3562
|
+
updateMcp,
|
|
3563
|
+
deleteMcp,
|
|
3564
|
+
bulkDeleteMcps,
|
|
3565
|
+
testConnection
|
|
2615
3566
|
};
|
|
2616
3567
|
}
|
|
2617
3568
|
|
|
2618
3569
|
// src/hooks/useWorkflows.ts
|
|
2619
|
-
import { useCallback as
|
|
3570
|
+
import { useCallback as useCallback15, useEffect as useEffect14, useState as useState14 } from "react";
|
|
2620
3571
|
function useWorkflows(options) {
|
|
2621
3572
|
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
2622
|
-
const { autoFetch = true, search,
|
|
3573
|
+
const { autoFetch = true, search, scope, visibility, isEnabled, page, limit } = opts;
|
|
2623
3574
|
const { fetchWithAuth } = usePersonaContext();
|
|
2624
|
-
const [workflows, setWorkflows] =
|
|
2625
|
-
const [pagination, setPagination] =
|
|
3575
|
+
const [workflows, setWorkflows] = useState14([]);
|
|
3576
|
+
const [pagination, setPagination] = useState14({
|
|
2626
3577
|
page: page ?? 1,
|
|
2627
3578
|
limit: limit ?? 20,
|
|
2628
3579
|
total: 0,
|
|
2629
3580
|
totalPages: 0
|
|
2630
3581
|
});
|
|
2631
|
-
const [isLoading, setIsLoading] =
|
|
2632
|
-
const [error, setError] =
|
|
2633
|
-
const fetchWorkflows =
|
|
3582
|
+
const [isLoading, setIsLoading] = useState14(false);
|
|
3583
|
+
const [error, setError] = useState14(null);
|
|
3584
|
+
const fetchWorkflows = useCallback15(async () => {
|
|
2634
3585
|
setIsLoading(true);
|
|
2635
3586
|
setError(null);
|
|
2636
3587
|
try {
|
|
2637
3588
|
const params = new URLSearchParams();
|
|
2638
3589
|
if (search) params.set("search", search);
|
|
2639
|
-
if (
|
|
3590
|
+
if (scope) params.set("scope", scope);
|
|
3591
|
+
if (visibility) params.set("visibility", visibility);
|
|
3592
|
+
if (isEnabled !== void 0) params.set("isEnabled", String(isEnabled));
|
|
2640
3593
|
if (page) params.set("page", String(page));
|
|
2641
3594
|
if (limit) params.set("limit", String(limit));
|
|
2642
3595
|
const queryStr = params.toString() ? `?${params.toString()}` : "";
|
|
@@ -2645,14 +3598,17 @@ function useWorkflows(options) {
|
|
|
2645
3598
|
throw new Error(`Failed to list workflows: ${res.statusText}`);
|
|
2646
3599
|
}
|
|
2647
3600
|
const data = await res.json();
|
|
2648
|
-
const items = Array.isArray(data) ? data : data?.items
|
|
3601
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
2649
3602
|
setWorkflows(items);
|
|
2650
3603
|
if (data?.pagination) {
|
|
2651
3604
|
setPagination({
|
|
2652
3605
|
page: data.pagination.page ?? 1,
|
|
2653
3606
|
limit: data.pagination.limit ?? items.length,
|
|
2654
3607
|
total: data.pagination.total ?? items.length,
|
|
2655
|
-
|
|
3608
|
+
// Backend's paginationEnvelope() names this field `pages`, not
|
|
3609
|
+
// `totalPages` — this used to always fall through to the `?? 1`
|
|
3610
|
+
// default.
|
|
3611
|
+
totalPages: data.pagination.pages ?? 1
|
|
2656
3612
|
});
|
|
2657
3613
|
} else {
|
|
2658
3614
|
setPagination({
|
|
@@ -2670,8 +3626,8 @@ function useWorkflows(options) {
|
|
|
2670
3626
|
} finally {
|
|
2671
3627
|
setIsLoading(false);
|
|
2672
3628
|
}
|
|
2673
|
-
}, [fetchWithAuth, search,
|
|
2674
|
-
const createWorkflow =
|
|
3629
|
+
}, [fetchWithAuth, search, scope, visibility, isEnabled, page, limit]);
|
|
3630
|
+
const createWorkflow = useCallback15(
|
|
2675
3631
|
async (input) => {
|
|
2676
3632
|
setError(null);
|
|
2677
3633
|
try {
|
|
@@ -2695,7 +3651,7 @@ function useWorkflows(options) {
|
|
|
2695
3651
|
},
|
|
2696
3652
|
[fetchWithAuth]
|
|
2697
3653
|
);
|
|
2698
|
-
|
|
3654
|
+
useEffect14(() => {
|
|
2699
3655
|
if (autoFetch) {
|
|
2700
3656
|
void fetchWorkflows();
|
|
2701
3657
|
}
|
|
@@ -2711,17 +3667,17 @@ function useWorkflows(options) {
|
|
|
2711
3667
|
}
|
|
2712
3668
|
|
|
2713
3669
|
// src/hooks/useWorkflow.ts
|
|
2714
|
-
import { useCallback as
|
|
3670
|
+
import { useCallback as useCallback16, useEffect as useEffect15, useState as useState15 } from "react";
|
|
2715
3671
|
function useWorkflow(workflowId, options) {
|
|
2716
3672
|
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
2717
3673
|
const { autoFetch = true } = opts;
|
|
2718
3674
|
const { fetchWithAuth } = usePersonaContext();
|
|
2719
|
-
const [workflow, setWorkflow] =
|
|
2720
|
-
const [versions, setVersions] =
|
|
2721
|
-
const [mermaid, setMermaid] =
|
|
2722
|
-
const [isLoading, setIsLoading] =
|
|
2723
|
-
const [error, setError] =
|
|
2724
|
-
const fetchWorkflow =
|
|
3675
|
+
const [workflow, setWorkflow] = useState15(null);
|
|
3676
|
+
const [versions, setVersions] = useState15([]);
|
|
3677
|
+
const [mermaid, setMermaid] = useState15(null);
|
|
3678
|
+
const [isLoading, setIsLoading] = useState15(false);
|
|
3679
|
+
const [error, setError] = useState15(null);
|
|
3680
|
+
const fetchWorkflow = useCallback16(async () => {
|
|
2725
3681
|
if (!workflowId) return null;
|
|
2726
3682
|
setIsLoading(true);
|
|
2727
3683
|
setError(null);
|
|
@@ -2741,20 +3697,31 @@ function useWorkflow(workflowId, options) {
|
|
|
2741
3697
|
setIsLoading(false);
|
|
2742
3698
|
}
|
|
2743
3699
|
}, [fetchWithAuth, workflowId]);
|
|
2744
|
-
const fetchVersions =
|
|
3700
|
+
const fetchVersions = useCallback16(async () => {
|
|
2745
3701
|
if (!workflowId) return [];
|
|
2746
3702
|
try {
|
|
2747
3703
|
const res = await fetchWithAuth(`/workflows/${workflowId}/versions`);
|
|
2748
3704
|
if (!res.ok) return [];
|
|
2749
3705
|
const data = await res.json();
|
|
2750
|
-
const items = Array.isArray(data) ? data : data?.items
|
|
3706
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
2751
3707
|
setVersions(items);
|
|
2752
3708
|
return items;
|
|
2753
3709
|
} catch {
|
|
2754
3710
|
return [];
|
|
2755
3711
|
}
|
|
2756
3712
|
}, [fetchWithAuth, workflowId]);
|
|
2757
|
-
const
|
|
3713
|
+
const getVersion = useCallback16(
|
|
3714
|
+
async (version) => {
|
|
3715
|
+
if (!workflowId) throw new Error("Workflow ID is required");
|
|
3716
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/versions/${version}`);
|
|
3717
|
+
if (!res.ok) {
|
|
3718
|
+
throw new Error(`Failed to fetch workflow version (${res.status}): ${res.statusText}`);
|
|
3719
|
+
}
|
|
3720
|
+
return await res.json();
|
|
3721
|
+
},
|
|
3722
|
+
[fetchWithAuth, workflowId]
|
|
3723
|
+
);
|
|
3724
|
+
const fetchMermaid = useCallback16(async () => {
|
|
2758
3725
|
if (!workflowId) return "";
|
|
2759
3726
|
try {
|
|
2760
3727
|
const res = await fetchWithAuth(`/workflows/${workflowId}/mermaid`);
|
|
@@ -2767,18 +3734,18 @@ function useWorkflow(workflowId, options) {
|
|
|
2767
3734
|
return "";
|
|
2768
3735
|
}
|
|
2769
3736
|
}, [fetchWithAuth, workflowId]);
|
|
2770
|
-
const
|
|
2771
|
-
async (
|
|
3737
|
+
const updateWorkflow = useCallback16(
|
|
3738
|
+
async (input) => {
|
|
2772
3739
|
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2773
3740
|
setError(null);
|
|
2774
|
-
const res = await fetchWithAuth(`/workflows/${workflowId}
|
|
2775
|
-
method: "
|
|
3741
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}`, {
|
|
3742
|
+
method: "PATCH",
|
|
2776
3743
|
headers: { "Content-Type": "application/json" },
|
|
2777
|
-
body: JSON.stringify(
|
|
3744
|
+
body: JSON.stringify(input)
|
|
2778
3745
|
});
|
|
2779
3746
|
if (!res.ok) {
|
|
2780
3747
|
const errText = await res.text().catch(() => res.statusText);
|
|
2781
|
-
throw new Error(`Failed to
|
|
3748
|
+
throw new Error(`Failed to update workflow (${res.status}): ${errText}`);
|
|
2782
3749
|
}
|
|
2783
3750
|
const updated = await res.json();
|
|
2784
3751
|
setWorkflow(updated);
|
|
@@ -2786,27 +3753,41 @@ function useWorkflow(workflowId, options) {
|
|
|
2786
3753
|
},
|
|
2787
3754
|
[fetchWithAuth, workflowId]
|
|
2788
3755
|
);
|
|
2789
|
-
const
|
|
2790
|
-
async (
|
|
3756
|
+
const saveDraft = useCallback16(
|
|
3757
|
+
async (draft) => {
|
|
2791
3758
|
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2792
3759
|
setError(null);
|
|
2793
|
-
const res = await fetchWithAuth(`/workflows/${workflowId}/
|
|
2794
|
-
method: "
|
|
3760
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
|
|
3761
|
+
method: "PUT",
|
|
2795
3762
|
headers: { "Content-Type": "application/json" },
|
|
2796
|
-
body: JSON.stringify(
|
|
3763
|
+
body: JSON.stringify(draft)
|
|
2797
3764
|
});
|
|
2798
3765
|
if (!res.ok) {
|
|
2799
3766
|
const errText = await res.text().catch(() => res.statusText);
|
|
2800
|
-
throw new Error(`Failed to
|
|
3767
|
+
throw new Error(`Failed to save draft (${res.status}): ${errText}`);
|
|
2801
3768
|
}
|
|
2802
|
-
const
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
return versionResult;
|
|
3769
|
+
const updated = await res.json();
|
|
3770
|
+
setWorkflow(updated);
|
|
3771
|
+
return updated;
|
|
2806
3772
|
},
|
|
2807
|
-
[fetchWithAuth, workflowId
|
|
3773
|
+
[fetchWithAuth, workflowId]
|
|
2808
3774
|
);
|
|
2809
|
-
const
|
|
3775
|
+
const publish = useCallback16(async () => {
|
|
3776
|
+
if (!workflowId) throw new Error("Workflow ID is required");
|
|
3777
|
+
setError(null);
|
|
3778
|
+
const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
|
|
3779
|
+
method: "POST"
|
|
3780
|
+
});
|
|
3781
|
+
if (!res.ok) {
|
|
3782
|
+
const errText = await res.text().catch(() => res.statusText);
|
|
3783
|
+
throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
|
|
3784
|
+
}
|
|
3785
|
+
const versionResult = await res.json();
|
|
3786
|
+
void fetchWorkflow();
|
|
3787
|
+
void fetchVersions();
|
|
3788
|
+
return versionResult;
|
|
3789
|
+
}, [fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]);
|
|
3790
|
+
const deleteWorkflow = useCallback16(async () => {
|
|
2810
3791
|
if (!workflowId) throw new Error("Workflow ID is required");
|
|
2811
3792
|
setError(null);
|
|
2812
3793
|
const res = await fetchWithAuth(`/workflows/${workflowId}`, {
|
|
@@ -2817,7 +3798,7 @@ function useWorkflow(workflowId, options) {
|
|
|
2817
3798
|
}
|
|
2818
3799
|
setWorkflow(null);
|
|
2819
3800
|
}, [fetchWithAuth, workflowId]);
|
|
2820
|
-
|
|
3801
|
+
useEffect15(() => {
|
|
2821
3802
|
if (autoFetch && workflowId) {
|
|
2822
3803
|
void fetchWorkflow();
|
|
2823
3804
|
}
|
|
@@ -2830,7 +3811,9 @@ function useWorkflow(workflowId, options) {
|
|
|
2830
3811
|
error,
|
|
2831
3812
|
refetch: fetchWorkflow,
|
|
2832
3813
|
fetchVersions,
|
|
3814
|
+
getVersion,
|
|
2833
3815
|
fetchMermaid,
|
|
3816
|
+
updateWorkflow,
|
|
2834
3817
|
saveDraft,
|
|
2835
3818
|
publish,
|
|
2836
3819
|
deleteWorkflow
|
|
@@ -2838,25 +3821,25 @@ function useWorkflow(workflowId, options) {
|
|
|
2838
3821
|
}
|
|
2839
3822
|
|
|
2840
3823
|
// src/hooks/useWorkflowStream.ts
|
|
2841
|
-
import { useCallback as
|
|
3824
|
+
import { useCallback as useCallback17, useEffect as useEffect16, useRef as useRef4, useState as useState16 } from "react";
|
|
2842
3825
|
function useWorkflowStream(workflowId, options) {
|
|
2843
3826
|
const { baseUrl, getAuthToken, fetchWithAuth, logger } = usePersonaContext();
|
|
2844
|
-
const [status, setStatus] =
|
|
2845
|
-
const [runId, setRunId] =
|
|
2846
|
-
const [activeNodeId, setActiveNodeId] =
|
|
2847
|
-
const [nodeRuns, setNodeRuns] =
|
|
3827
|
+
const [status, setStatus] = useState16("idle");
|
|
3828
|
+
const [runId, setRunId] = useState16(null);
|
|
3829
|
+
const [activeNodeId, setActiveNodeId] = useState16(null);
|
|
3830
|
+
const [nodeRuns, setNodeRuns] = useState16(
|
|
2848
3831
|
{}
|
|
2849
3832
|
);
|
|
2850
|
-
const [text, setText] =
|
|
2851
|
-
const [output, setOutput] =
|
|
2852
|
-
const [error, setError] =
|
|
2853
|
-
const [events, setEvents] =
|
|
3833
|
+
const [text, setText] = useState16("");
|
|
3834
|
+
const [output, setOutput] = useState16(null);
|
|
3835
|
+
const [error, setError] = useState16(null);
|
|
3836
|
+
const [events, setEvents] = useState16([]);
|
|
2854
3837
|
const abortControllerRef = useRef4(null);
|
|
2855
3838
|
const currentRunIdRef = useRef4(null);
|
|
2856
3839
|
currentRunIdRef.current = runId;
|
|
2857
3840
|
const optionsRef = useRef4(options);
|
|
2858
3841
|
optionsRef.current = options;
|
|
2859
|
-
const cancel =
|
|
3842
|
+
const cancel = useCallback17(async () => {
|
|
2860
3843
|
if (abortControllerRef.current) {
|
|
2861
3844
|
abortControllerRef.current.abort();
|
|
2862
3845
|
abortControllerRef.current = null;
|
|
@@ -2877,7 +3860,7 @@ function useWorkflowStream(workflowId, options) {
|
|
|
2877
3860
|
setStatus("cancelled");
|
|
2878
3861
|
setActiveNodeId(null);
|
|
2879
3862
|
}, [fetchWithAuth, logger]);
|
|
2880
|
-
const reset =
|
|
3863
|
+
const reset = useCallback17(() => {
|
|
2881
3864
|
if (abortControllerRef.current) {
|
|
2882
3865
|
abortControllerRef.current.abort();
|
|
2883
3866
|
abortControllerRef.current = null;
|
|
@@ -2891,7 +3874,7 @@ function useWorkflowStream(workflowId, options) {
|
|
|
2891
3874
|
setError(null);
|
|
2892
3875
|
setEvents([]);
|
|
2893
3876
|
}, []);
|
|
2894
|
-
const processStream =
|
|
3877
|
+
const processStream = useCallback17(
|
|
2895
3878
|
async (url, method, body, targetRunId) => {
|
|
2896
3879
|
if (abortControllerRef.current) {
|
|
2897
3880
|
abortControllerRef.current.abort();
|
|
@@ -3056,7 +4039,7 @@ function useWorkflowStream(workflowId, options) {
|
|
|
3056
4039
|
},
|
|
3057
4040
|
[getAuthToken]
|
|
3058
4041
|
);
|
|
3059
|
-
const start =
|
|
4042
|
+
const start = useCallback17(
|
|
3060
4043
|
async (input, opts) => {
|
|
3061
4044
|
const targetWfId = opts?.workflowId || workflowId;
|
|
3062
4045
|
if (!targetWfId) {
|
|
@@ -3077,7 +4060,7 @@ function useWorkflowStream(workflowId, options) {
|
|
|
3077
4060
|
},
|
|
3078
4061
|
[workflowId, baseUrl, processStream]
|
|
3079
4062
|
);
|
|
3080
|
-
const resume =
|
|
4063
|
+
const resume = useCallback17(
|
|
3081
4064
|
async (targetRunId, sinceSeq) => {
|
|
3082
4065
|
if (!targetRunId) {
|
|
3083
4066
|
throw new Error("Run ID is required to resume stream");
|
|
@@ -3090,7 +4073,7 @@ function useWorkflowStream(workflowId, options) {
|
|
|
3090
4073
|
},
|
|
3091
4074
|
[baseUrl, processStream]
|
|
3092
4075
|
);
|
|
3093
|
-
|
|
4076
|
+
useEffect16(() => {
|
|
3094
4077
|
return () => {
|
|
3095
4078
|
if (abortControllerRef.current) {
|
|
3096
4079
|
abortControllerRef.current.abort();
|
|
@@ -3115,21 +4098,21 @@ function useWorkflowStream(workflowId, options) {
|
|
|
3115
4098
|
}
|
|
3116
4099
|
|
|
3117
4100
|
// src/hooks/useWorkflowRuns.ts
|
|
3118
|
-
import { useCallback as
|
|
4101
|
+
import { useCallback as useCallback18, useEffect as useEffect17, useState as useState17 } from "react";
|
|
3119
4102
|
function useWorkflowRuns(workflowId, options) {
|
|
3120
4103
|
const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
|
|
3121
4104
|
const { autoFetch = true, page = 1, limit = 20 } = opts;
|
|
3122
4105
|
const { fetchWithAuth } = usePersonaContext();
|
|
3123
|
-
const [runs, setRuns] =
|
|
3124
|
-
const [pagination, setPagination] =
|
|
4106
|
+
const [runs, setRuns] = useState17([]);
|
|
4107
|
+
const [pagination, setPagination] = useState17({
|
|
3125
4108
|
page,
|
|
3126
4109
|
limit,
|
|
3127
4110
|
total: 0,
|
|
3128
4111
|
totalPages: 0
|
|
3129
4112
|
});
|
|
3130
|
-
const [isLoading, setIsLoading] =
|
|
3131
|
-
const [error, setError] =
|
|
3132
|
-
const fetchRuns =
|
|
4113
|
+
const [isLoading, setIsLoading] = useState17(false);
|
|
4114
|
+
const [error, setError] = useState17(null);
|
|
4115
|
+
const fetchRuns = useCallback18(async () => {
|
|
3133
4116
|
if (!workflowId) return [];
|
|
3134
4117
|
setIsLoading(true);
|
|
3135
4118
|
setError(null);
|
|
@@ -3140,14 +4123,16 @@ function useWorkflowRuns(workflowId, options) {
|
|
|
3140
4123
|
throw new Error(`Failed to list workflow runs: ${res.statusText}`);
|
|
3141
4124
|
}
|
|
3142
4125
|
const data = await res.json();
|
|
3143
|
-
const items = Array.isArray(data) ? data : data?.items
|
|
4126
|
+
const items = Array.isArray(data) ? data : data?.items ?? [];
|
|
3144
4127
|
setRuns(items);
|
|
3145
4128
|
if (data?.pagination) {
|
|
3146
4129
|
setPagination({
|
|
3147
4130
|
page: data.pagination.page ?? page,
|
|
3148
4131
|
limit: data.pagination.limit ?? limit,
|
|
3149
4132
|
total: data.pagination.total ?? items.length,
|
|
3150
|
-
|
|
4133
|
+
// Backend's paginationEnvelope() names this field `pages`, not
|
|
4134
|
+
// `totalPages`.
|
|
4135
|
+
totalPages: data.pagination.pages ?? 1
|
|
3151
4136
|
});
|
|
3152
4137
|
}
|
|
3153
4138
|
return items;
|
|
@@ -3159,7 +4144,7 @@ function useWorkflowRuns(workflowId, options) {
|
|
|
3159
4144
|
setIsLoading(false);
|
|
3160
4145
|
}
|
|
3161
4146
|
}, [fetchWithAuth, workflowId, page, limit]);
|
|
3162
|
-
const getRun =
|
|
4147
|
+
const getRun = useCallback18(
|
|
3163
4148
|
async (runId) => {
|
|
3164
4149
|
if (!runId) throw new Error("Run ID is required");
|
|
3165
4150
|
const res = await fetchWithAuth(`/workflows/runs/${runId}`);
|
|
@@ -3170,7 +4155,7 @@ function useWorkflowRuns(workflowId, options) {
|
|
|
3170
4155
|
},
|
|
3171
4156
|
[fetchWithAuth]
|
|
3172
4157
|
);
|
|
3173
|
-
const cancelRun =
|
|
4158
|
+
const cancelRun = useCallback18(
|
|
3174
4159
|
async (runId) => {
|
|
3175
4160
|
if (!runId) throw new Error("Run ID is required");
|
|
3176
4161
|
const res = await fetchWithAuth(`/workflows/runs/${runId}/cancel`, {
|
|
@@ -3185,7 +4170,7 @@ function useWorkflowRuns(workflowId, options) {
|
|
|
3185
4170
|
},
|
|
3186
4171
|
[fetchWithAuth]
|
|
3187
4172
|
);
|
|
3188
|
-
|
|
4173
|
+
useEffect17(() => {
|
|
3189
4174
|
if (autoFetch && workflowId) {
|
|
3190
4175
|
void fetchRuns();
|
|
3191
4176
|
}
|
|
@@ -3226,10 +4211,14 @@ export {
|
|
|
3226
4211
|
useChat,
|
|
3227
4212
|
useConnection,
|
|
3228
4213
|
useFiles,
|
|
4214
|
+
useKnowledgeBases,
|
|
3229
4215
|
useMcp,
|
|
4216
|
+
useMcpAdmin,
|
|
3230
4217
|
useMcpConnections,
|
|
3231
4218
|
useMemory,
|
|
3232
4219
|
usePersonaContext,
|
|
4220
|
+
useRcpSources,
|
|
4221
|
+
useSkills,
|
|
3233
4222
|
useThreads,
|
|
3234
4223
|
useVoice,
|
|
3235
4224
|
useWorkflow,
|