"use client"; import { useState, useEffect } from "react"; import { Card, Button, Badge, Modal, Input, ModelSelectModal } from "@/shared/components"; import Image from "next/image"; export default function AntigravityToolCard({ tool, isExpanded, onToggle, baseUrl, apiKeys, activeProviders, hasActiveProviders, cloudEnabled, initialStatus, }) { const [status, setStatus] = useState(initialStatus || null); const [loading, setLoading] = useState(false); const [startingStep, setStartingStep] = useState(null); // "cert" | "server" | "dns" | null const [showPasswordModal, setShowPasswordModal] = useState(false); const [sudoPassword, setSudoPassword] = useState(""); const [selectedApiKey, setSelectedApiKey] = useState(""); const [message, setMessage] = useState(null); const [modelMappings, setModelMappings] = useState({}); const [modalOpen, setModalOpen] = useState(false); const [currentEditingAlias, setCurrentEditingAlias] = useState(null); const [modelAliases, setModelAliases] = useState({}); useEffect(() => { if (apiKeys?.length > 0 && !selectedApiKey) { setSelectedApiKey(apiKeys[0].key); } }, [apiKeys, selectedApiKey]); useEffect(() => { if (initialStatus) setStatus(initialStatus); }, [initialStatus]); useEffect(() => { if (isExpanded && !status) { fetchStatus(); loadSavedMappings(); fetchModelAliases(); } if (isExpanded) { loadSavedMappings(); fetchModelAliases(); } }, [isExpanded]); const loadSavedMappings = async () => { try { const res = await fetch("/api/cli-tools/antigravity-mitm/alias?tool=antigravity"); if (res.ok) { const data = await res.json(); const aliases = data.aliases || {}; if (Object.keys(aliases).length > 0) { setModelMappings(aliases); } } } catch (error) { console.log("Error loading saved mappings:", error); } }; const fetchModelAliases = async () => { try { const res = await fetch("/api/models/alias"); const data = await res.json(); if (res.ok) setModelAliases(data.aliases || {}); } catch (error) { console.log("Error fetching model aliases:", error); } }; const fetchStatus = async () => { try { const res = await fetch("/api/cli-tools/antigravity-mitm"); if (res.ok) { const data = await res.json(); setStatus(data); } } catch (error) { console.log("Error fetching status:", error); setStatus({ running: false }); } }; // MITM elevation is decided by the server OS, not by this browser's OS. const serverIsWindows = status?.isWin === true; const canRunWithoutPassword = serverIsWindows || status?.hasCachedPassword || status?.needsSudoPassword === false; const handleStart = () => { if (canRunWithoutPassword) { doStart(""); } else { setShowPasswordModal(true); setMessage(null); } }; const handleStop = () => { if (canRunWithoutPassword) { doStop(""); } else { setShowPasswordModal(true); setMessage(null); } }; const doStart = async (password) => { setLoading(true); setMessage(null); // Show steps progressing in order setStartingStep("cert"); try { const keyToUse = selectedApiKey?.trim() || (apiKeys?.length > 0 ? apiKeys[0].key : null) || (!cloudEnabled ? "sk_9router" : null); const res = await fetch("/api/cli-tools/antigravity-mitm", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ apiKey: keyToUse, sudoPassword: password }), }); const data = await res.json(); if (res.ok) { setStartingStep(null); setMessage({ type: "success", text: "MITM started" }); setShowPasswordModal(false); setSudoPassword(""); fetchStatus(); } else { setStartingStep(null); setMessage({ type: "error", text: data.error || "Failed to start" }); } } catch (error) { setStartingStep(null); setMessage({ type: "error", text: error.message }); } finally { setLoading(false); } }; const doStop = async (password) => { setLoading(true); setMessage(null); try { const res = await fetch("/api/cli-tools/antigravity-mitm", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sudoPassword: password }), }); const data = await res.json(); if (res.ok) { setMessage({ type: "success", text: "MITM stopped" }); setShowPasswordModal(false); setSudoPassword(""); fetchStatus(); } else { setMessage({ type: "error", text: data.error || "Failed to stop" }); } } catch (error) { setMessage({ type: "error", text: error.message }); } finally { setLoading(false); } }; const handleConfirmPassword = () => { if (!sudoPassword.trim()) { setMessage({ type: "error", text: "Sudo password is required" }); return; } if (status?.running) { doStop(sudoPassword); } else { doStart(sudoPassword); } }; const openModelSelector = (alias) => { setCurrentEditingAlias(alias); setModalOpen(true); }; const handleModelSelect = (model) => { if (currentEditingAlias) { setModelMappings(prev => ({ ...prev, [currentEditingAlias]: model.value, })); } }; const handleModelMappingChange = (alias, value) => { setModelMappings(prev => ({ ...prev, [alias]: value, })); }; const handleSaveMappings = async () => { setLoading(true); setMessage(null); try { const res = await fetch("/api/cli-tools/antigravity-mitm/alias", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ tool: "antigravity", mappings: modelMappings }), }); if (!res.ok) { const data = await res.json(); throw new Error(data.error || "Failed to save mappings"); } setMessage({ type: "success", text: "Mappings saved!" }); } catch (error) { setMessage({ type: "error", text: error.message }); } finally { setLoading(false); } }; const isRunning = status?.running; return (
{tool.name} { e.target.style.display = "none"; }} />

{tool.name}

{isRunning ? ( Active ) : ( Inactive )}

{tool.description}

expand_more
{isExpanded && (
{/* Status indicators — ordered: Cert → Server → DNS */}
{[ { key: "cert", label: "Cert", ok: status?.certExists }, { key: "server", label: "Server", ok: status?.running }, { key: "dns", label: "DNS", ok: status?.dnsConfigured }, ].map(({ key, label, ok }, i) => { const isLoading = startingStep === key; return (
{isLoading ? ( progress_activity ) : ( {ok ? "check_circle" : "radio_button_unchecked"} )} {label}
{i < 2 && arrow_forward}
); })}
{/* Start/Stop Button */}
{isRunning ? ( ) : ( )}
{message?.type === "error" && (
error {message.text}
)} {/* When running: API Key + Model Mappings */} {isRunning && ( <>
API Key arrow_forward {apiKeys.length > 0 ? ( ) : ( {cloudEnabled ? "No API keys - Create one in Keys page" : "sk_9router (default)"} )}
{tool.defaultModels.map((model) => (
{model.name} arrow_forward
handleModelMappingChange(model.alias, e.target.value)} placeholder="provider/model-id" className="w-full min-w-0 pl-2 pr-7 py-2 bg-surface rounded border border-border text-xs focus:outline-none focus:ring-1 focus:ring-primary/50 sm:py-1.5" /> {modelMappings[model.alias] && ( )}
))}
)} {/* Windows admin warning */} {!isRunning && serverIsWindows && (
warning Windows: Run terminal (9Router) as Administrator to enable MITM
)} {/* When stopped: how it works */} {!isRunning && (

How it works: Intercepts Antigravity traffic via DNS redirect, letting you reroute models through 9Router.

1. Generates SSL cert & adds to system keychain 2. Redirects daily-cloudcode-pa.googleapis.com → localhost 3. Maps Antigravity models to any provider via 9Router
)}
)} {/* Password Modal */} { setShowPasswordModal(false); setSudoPassword(""); setMessage(null); }} title="Sudo Password Required" size="sm" >
warning

Required for SSL certificate and DNS configuration

setSudoPassword(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && !loading) handleConfirmPassword(); }} /> {message && (
{message.type === "success" ? "check_circle" : "error"} {message.text}
)}
{/* Model Select Modal */} setModalOpen(false)} onSelect={handleModelSelect} selectedModel={currentEditingAlias ? modelMappings[currentEditingAlias] : null} activeProviders={activeProviders} modelAliases={modelAliases} title={`Select model for ${currentEditingAlias}`} />
); }