"use client"; import { useState, useEffect, useCallback } from "react"; import { Card, 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, MitmLinkCard } from "./components"; import { MITM_TOOLS } from "@/shared/constants/cliTools"; const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL; const ALL_STATUSES_URL = "/api/cli-tools/all-statuses"; 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 [tunnelEnabled, setTunnelEnabled] = useState(false); const [tunnelPublicUrl, setTunnelPublicUrl] = useState(""); const [tailscaleEnabled, setTailscaleEnabled] = useState(false); const [tailscaleUrl, setTailscaleUrl] = useState(""); const [apiKeys, setApiKeys] = useState([]); const [toolStatuses, setToolStatuses] = useState({}); useEffect(() => { fetchConnections(); loadCloudSettings(); fetchApiKeys(); fetchAllStatuses(); }, []); const fetchAllStatuses = async () => { try { const res = await fetch(ALL_STATUSES_URL); if (res.ok) setToolStatuses(await res.json()); } catch (error) { console.log("Error fetching tool statuses:", error); } }; const loadCloudSettings = async () => { try { const [settingsRes, tunnelRes] = await Promise.all([ fetch("/api/settings"), fetch("/api/tunnel/status"), ]); 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 || ""); } } catch (error) { console.log("Error loading 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 = () => 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((toolId, modelAlias, targetModel) => { setModelMappings(prev => { if (prev[toolId]?.[modelAlias] === targetModel) return prev; return { ...prev, [toolId]: { ...prev[toolId], [modelAlias]: targetModel } }; }); }, []); 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"; }; if (loading) { return (
Configure local coding tools to use your 9Router providers.