import { startTransition, useCallback, useEffect, useState, type ReactNode } from "react"; import type { SourceInspectionToolDetail, } from "../../index"; import type { LocalScopePolicy } from "@executor/platform-sdk/schema"; import { useSourceInspection, useSourceToolDetail, } from "../../hooks/sources"; import { useCreatePolicy, usePolicies, useUpdatePolicy } from "../../hooks/policies"; import { resolveToolPermission, type ToolPermissionLevel } from "./tool-permission-badge"; import { Badge } from "./ui/badge"; import { LoadableBlock } from "./loadable"; import { SourceToolDetailPanel, SourceToolModelWorkbench, type ToolPermissionChangeRequest, } from "./source-tool-workbench"; import type { SourceToolExplorerSearch } from "./source-tool-explorer-search"; export const SourceToolExplorer = (props: { sourceId: string; title: string; kind: string; search: SourceToolExplorerSearch; selectedToolPath?: string | null; navigate?: (search: { tab: "model" | "discover"; tool?: string; query?: string; }) => void | Promise; actions?: ReactNode; summary?: ReactNode; policies?: ReadonlyArray; renderHeaderMeta?: (detail: SourceInspectionToolDetail) => ReactNode; renderSchemaExtras?: (detail: SourceInspectionToolDetail) => ReactNode; renderDetail?: (detail: SourceInspectionToolDetail) => ReactNode; }) => { const inspection = useSourceInspection(props.sourceId); const loadedPolicies = usePolicies(); const policies = props.policies ?? (loadedPolicies.status === "ready" ? loadedPolicies.data : []); const selectedToolPath = props.selectedToolPath ?? props.search.tool ?? null; const detail = useSourceToolDetail( props.sourceId, selectedToolPath, ); const navigate = props.navigate; const createPolicy = useCreatePolicy(); const updatePolicy = useUpdatePolicy(); const [permissionPending, setPermissionPending] = useState(false); const levelToPayload = (level: ToolPermissionLevel) => { switch (level) { case "auto-run": return { effect: "allow" as const, approvalMode: "auto" as const }; case "requires-approval": return { effect: "allow" as const, approvalMode: "required" as const }; case "denied": return { effect: "deny" as const, approvalMode: "auto" as const }; default: return { effect: "allow" as const, approvalMode: "auto" as const }; } }; const handleSetPermission = useCallback( async (request: ToolPermissionChangeRequest) => { setPermissionPending(true); try { const { matchedPolicy } = resolveToolPermission( request.toolPath, policies, ); const payload = levelToPayload(request.level); if (matchedPolicy && matchedPolicy.resourcePattern === request.toolPath) { await updatePolicy.mutateAsync({ policyId: matchedPolicy.id, payload, }); } else { await createPolicy.mutateAsync({ resourcePattern: request.toolPath, ...payload, priority: 10, enabled: true, }); } } catch { // policy state will refresh } finally { setPermissionPending(false); } }, [createPolicy, updatePolicy, policies], ); useEffect(() => { if (inspection.status !== "ready" || !navigate) { return; } const firstTool = inspection.data.tools[0]?.path; const nextToolPath = selectedToolPath ?? firstTool; if (!nextToolPath) { return; } if (props.search.tab !== "discover" && selectedToolPath) { return; } startTransition(() => { void navigate({ tab: "model", tool: nextToolPath, query: "", }); }); }, [inspection, navigate, props.search.tab, selectedToolPath]); return ( {(loadedInspection) => { const selectedTool = loadedInspection.tools.find((candidate) => candidate.path === selectedToolPath) ?? loadedInspection.tools[0] ?? null; return (

{props.title}

{props.kind} {loadedInspection.toolCount} {loadedInspection.toolCount === 1 ? "tool" : "tools"}
{props.actions}
{props.summary && (
{props.summary}
)}
{ if (!navigate) { return; } startTransition(() => { void navigate({ tab: "model", tool: nextToolPath, query: "", }); }); }} sourceId={props.sourceId} renderDetail={props.renderDetail ?? ( (props.renderHeaderMeta || props.renderSchemaExtras) ? (toolDetail) => ( ) : undefined )} />
); }}
); };