"use client"; import { useState, useEffect } from "react"; import { Card, Button, Badge, Input } from "@/shared/components"; /** * Shared MITM infrastructure card — manages SSL cert + server start/stop. * DNS per-tool is handled separately in MitmToolCard. */ export default function MitmServerCard({ apiKeys, cloudEnabled, onStatusChange }) { const [status, setStatus] = useState(null); const [loading, setLoading] = useState(false); const [showPasswordModal, setShowPasswordModal] = useState(false); const [sudoPassword, setSudoPassword] = useState(""); const [selectedApiKey, setSelectedApiKey] = useState(""); const [message, setMessage] = useState(null); const [pendingAction, setPendingAction] = useState(null); // "start" | "stop" const isWindows = typeof navigator !== "undefined" && navigator.userAgent?.includes("Windows"); useEffect(() => { if (apiKeys?.length > 0 && !selectedApiKey) { setSelectedApiKey(apiKeys[0].key); } }, [apiKeys, selectedApiKey]); useEffect(() => { fetchStatus(); }, []); const fetchStatus = async () => { try { const res = await fetch("/api/cli-tools/antigravity-mitm"); if (res.ok) { const data = await res.json(); setStatus(data); onStatusChange?.(data); } } catch { setStatus({ running: false, certExists: false, dnsStatus: {} }); } }; const handleAction = (action) => { if (isWindows || status?.hasCachedPassword) { doAction(action, ""); } else { setPendingAction(action); setShowPasswordModal(true); setMessage(null); } }; const doAction = async (action, password) => { setLoading(true); setMessage(null); try { if (action === "start") { 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) { setMessage({ type: "success", text: "Server started" }); } else { setMessage({ type: "error", text: data.error || "Failed to start server" }); } } else { 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: "Server stopped — all DNS cleared" }); } else { setMessage({ type: "error", text: data.error || "Failed to stop server" }); } } setShowPasswordModal(false); setSudoPassword(""); await fetchStatus(); } catch (error) { setMessage({ type: "error", text: error.message }); } finally { setLoading(false); setPendingAction(null); } }; const handleConfirmPassword = () => { if (!sudoPassword.trim()) { setMessage({ type: "error", text: "Sudo password is required" }); return; } doAction(pendingAction, sudoPassword); }; const isRunning = status?.running; return ( <>
{/* Header */}
security MITM Server {isRunning ? ( Running ) : ( Stopped )}
{[ { label: "Cert", ok: status?.certExists }, { label: "Server", ok: isRunning }, ].map(({ label, ok }) => ( {ok ? "check_circle" : "radio_button_unchecked"} {label} ))}
{/* Purpose & How it works */}

Purpose: Use Antigravity IDE & GitHub Copilot → with ANY provider/model from 9Router

How it works: Antigravity/Copilot IDE request → DNS redirect to localhost:443 → MITM proxy intercepts → 9Router → response to Antigravity/Copilot

{/* API Key selector (only when stopped, to pick key for start) */} {!isRunning && (
API Key {apiKeys?.length > 0 ? ( ) : ( {cloudEnabled ? "No API keys — create one in Keys page" : "sk_9router (default)"} )}
)} {message && (
{message.type === "success" ? "check_circle" : "error"} {message.text}
)} {/* Action button */}
{isRunning ? ( ) : ( )} {isRunning && (

Enable DNS per tool below to activate interception

)}
{/* Windows admin warning */} {!isRunning && isWindows && (
warning Windows: Run 9Router terminal as Administrator
)}
{/* Password Modal */} {showPasswordModal && (

Sudo Password Required

warning

Required for SSL certificate and server startup

setSudoPassword(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && !loading) handleConfirmPassword(); }} /> {message && (
error {message.text}
)}
)} ); }