"use client"; import { useState, useEffect, useCallback } from "react"; import { Card, CardSkeleton } from "@/shared/components"; import { CLI_TOOLS } from "@/shared/constants/cliTools"; import { PROVIDER_MODELS, getModelsByProviderId, PROVIDER_ID_TO_ALIAS } from "@/shared/constants/models"; import { ClaudeToolCard, CodexToolCard, DroidToolCard, OpenClawToolCard, DefaultToolCard } from "./components"; const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL; export default function CLIToolsPageClient({ machineId }) { const [connections, setConnections] = useState([]); const [loading, setLoading] = useState(true); const [expandedTool, setExpandedTool] = useState(null); const [modelMappings, setModelMappings] = useState({}); const [cloudEnabled, setCloudEnabled] = useState(false); const [apiKeys, setApiKeys] = useState([]); useEffect(() => { fetchConnections(); loadCloudSettings(); fetchApiKeys(); }, []); const loadCloudSettings = async () => { try { const res = await fetch("/api/settings"); if (res.ok) { const data = await res.json(); setCloudEnabled(data.cloudEnabled || false); } } catch (error) { console.log("Error loading cloud settings:", error); } }; const fetchApiKeys = async () => { try { const res = await fetch("/api/keys"); if (res.ok) { const data = await res.json(); setApiKeys(data.keys || []); } } catch (error) { console.log("Error fetching API keys:", error); } }; const fetchConnections = async () => { try { const res = await fetch("/api/providers"); const data = await res.json(); if (res.ok) { setConnections(data.connections || []); } } catch (error) { console.log("Error fetching connections:", error); } finally { setLoading(false); } }; const getActiveProviders = () => { return 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: alias, connectionName: conn.name, modelId: m.id, }); } }); }); return models; }; const handleModelMappingChange = useCallback((toolId, modelAlias, targetModel) => { setModelMappings(prev => { // Prevent unnecessary updates if value hasn't changed if (prev[toolId]?.[modelAlias] === targetModel) { return prev; } return { ...prev, [toolId]: { ...prev[toolId], [modelAlias]: targetModel, }, }; }); }, []); const getBaseUrl = () => { if (cloudEnabled && CLOUD_URL) { return CLOUD_URL; } if (typeof window !== "undefined") { return window.location.origin; } return "http://localhost:20128"; }; if (loading) { return (
); } const availableModels = getAllAvailableModels(); const hasActiveProviders = availableModels.length > 0; const renderToolCard = (toolId, tool) => { const commonProps = { tool, isExpanded: expandedTool === toolId, onToggle: () => setExpandedTool(expandedTool === toolId ? null : toolId), baseUrl: getBaseUrl(), apiKeys, }; switch (toolId) { case "claude": return ( handleModelMappingChange(toolId, alias, target)} hasActiveProviders={hasActiveProviders} cloudEnabled={cloudEnabled} /> ); case "codex": return ; case "droid": return ; case "openclaw": return ; default: return ; } }; return (
{!hasActiveProviders && (
warning

No active providers

Please add and connect providers first to configure CLI tools.

)}
{Object.entries(CLI_TOOLS).map(([toolId, tool]) => renderToolCard(toolId, tool))}
); }