"use client"; import { useState, useEffect, useCallback } from "react"; import Link from "next/link"; import { CardSkeleton } from "@/shared/components"; import { CLI_TOOLS } from "@/shared/constants/cliTools"; import { getModelsByProviderId, PROVIDER_ID_TO_ALIAS } from "@/shared/constants/models"; import { ClaudeToolCard, CodexToolCard, DroidToolCard, OpenClawToolCard, HermesToolCard, DefaultToolCard, OpenCodeToolCard, CoworkToolCard, CopilotToolCard, ClineToolCard, KiloToolCard, DeepSeekTuiToolCard, JcodeToolCard, } from "../components"; const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL; export default function ToolDetailClient({ toolId, machineId }) { const tool = CLI_TOOLS[toolId]; const [connections, setConnections] = useState([]); const [loading, setLoading] = useState(true); const [modelMappings, setModelMappings] = useState({}); const [cloudEnabled, setCloudEnabled] = useState(false); const [tunnelEnabled, setTunnelEnabled] = useState(false); const [tunnelPublicUrl, setTunnelPublicUrl] = useState(""); const [tailscaleEnabled, setTailscaleEnabled] = useState(false); const [tailscaleUrl, setTailscaleUrl] = useState(""); const [apiKeys, setApiKeys] = useState([]); useEffect(() => { let mounted = true; (async () => { try { const [provRes, settingsRes, tunnelRes, keysRes] = await Promise.all([ fetch("/api/providers"), fetch("/api/settings"), fetch("/api/tunnel/status"), fetch("/api/keys"), ]); if (!mounted) return; if (provRes.ok) { const data = await provRes.json(); setConnections(data.connections || []); } if (settingsRes.ok) { const data = await settingsRes.json(); setCloudEnabled(data.cloudEnabled || false); } if (tunnelRes.ok) { const data = await tunnelRes.json(); setTunnelEnabled(!!(data.tunnel?.enabled || data.tunnel?.settingsEnabled)); setTunnelPublicUrl(data.tunnel?.publicUrl || ""); setTailscaleEnabled(!!(data.tailscale?.enabled || data.tailscale?.settingsEnabled)); setTailscaleUrl(data.tailscale?.tunnelUrl || ""); } if (keysRes.ok) { const data = await keysRes.json(); setApiKeys(data.keys || []); } } catch (error) { console.log("Error loading tool data:", error); } finally { if (mounted) setLoading(false); } })(); return () => { mounted = false; }; }, []); const getActiveProviders = () => connections.filter(c => c.isActive !== false); const getAllAvailableModels = () => { const activeProviders = getActiveProviders(); const models = []; const seenModels = new Set(); activeProviders.forEach(conn => { const alias = PROVIDER_ID_TO_ALIAS[conn.provider] || conn.provider; const providerModels = getModelsByProviderId(conn.provider); providerModels.forEach(m => { const modelValue = `${alias}/${m.id}`; if (!seenModels.has(modelValue)) { seenModels.add(modelValue); models.push({ value: modelValue, label: `${alias}/${m.id}`, provider: conn.provider, alias, connectionName: conn.name, modelId: m.id }); } }); }); return models; }; const handleModelMappingChange = useCallback((tId, alias, target) => { setModelMappings(prev => { if (prev[tId]?.[alias] === target) return prev; return { ...prev, [tId]: { ...prev[tId], [alias]: target } }; }); }, []); const getBaseUrl = () => { if (tunnelEnabled && tunnelPublicUrl) return tunnelPublicUrl; if (cloudEnabled && CLOUD_URL) return CLOUD_URL; if (typeof window !== "undefined") return window.location.origin; return "http://localhost:20128"; }; const renderToolCard = () => { const availableModels = getAllAvailableModels(); const hasActiveProviders = availableModels.length > 0; const commonProps = { tool, isExpanded: true, onToggle: () => {}, baseUrl: getBaseUrl(), apiKeys, tunnelEnabled, tunnelPublicUrl, tailscaleEnabled, tailscaleUrl, }; switch (toolId) { case "claude": return handleModelMappingChange(toolId, a, t)} hasActiveProviders={hasActiveProviders} cloudEnabled={cloudEnabled} />; case "codex": return ; case "opencode": return ; case "cowork": return ; case "droid": return ; case "openclaw": return ; case "hermes": return ; case "copilot": return ; case "cline": return ; case "kilo": return ; case "deepseek-tui": return ; case "jcode": return ; default: return ; } }; // Guard removed/unknown tools (e.g. disabled Cowork) to avoid crash on direct URL. if (!tool) { return (
arrow_back Back to CLI Tools

Tool not found or disabled.

); } return (
arrow_back Back to CLI Tools

{tool.name}

{tool.description}

{loading ? : renderToolCard()}
); }