"use client";
import { useState, useEffect, useCallback } from "react";
import { Card, Button, Badge, Input } from "@/shared/components";
const DEFAULT_MITM_ROUTER_BASE = "http://localhost:20128";
/**
* 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(() => apiKeys?.[0]?.key || "");
const [pendingAction, setPendingAction] = useState(null);
const [modalError, setModalError] = useState(null);
const [actionError, setActionError] = useState(null);
const [mitmRouterBaseUrl, setMitmRouterBaseUrl] = useState(DEFAULT_MITM_ROUTER_BASE);
const serverIsWindows = status?.isWin === true;
const canRunWithoutPassword = serverIsWindows || status?.hasCachedPassword || status?.needsSudoPassword === false;
const isAdmin = status?.isAdmin !== false;
// No privilege: not admin/root AND (Win OR no cached sudo password)
const noPrivilege = !isAdmin && (serverIsWindows || (!status?.hasCachedPassword && status?.needsSudoPassword !== false));
const fetchStatus = useCallback(async () => {
try {
const res = await fetch("/api/cli-tools/antigravity-mitm");
if (res.ok) {
const data = await res.json();
setStatus(data);
if (data.mitmRouterBaseUrl) {
setMitmRouterBaseUrl(data.mitmRouterBaseUrl);
}
onStatusChange?.(data);
}
} catch {
setStatus({ running: false, certExists: false, dnsStatus: {} });
}
}, [onStatusChange]);
useEffect(() => {
queueMicrotask(() => {
fetchStatus();
});
}, [fetchStatus]);
const handleAction = (action) => {
setActionError(null);
if (canRunWithoutPassword) {
doAction(action, "");
} else {
setPendingAction(action);
setShowPasswordModal(true);
setModalError(null);
}
};
const doAction = async (action, password) => {
setLoading(true);
setActionError(null);
try {
let res;
if (action === "trust-cert") {
res = await fetch("/api/cli-tools/antigravity-mitm", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "trust-cert", sudoPassword: password }),
});
} else if (action === "start") {
const keyToUse = selectedApiKey?.trim()
|| (apiKeys?.length > 0 ? apiKeys[0].key : null)
|| (!cloudEnabled ? "sk_9router" : null);
res = await fetch("/api/cli-tools/antigravity-mitm", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
apiKey: keyToUse,
sudoPassword: password,
mitmRouterBaseUrl: mitmRouterBaseUrl.trim() || DEFAULT_MITM_ROUTER_BASE,
}),
});
} else {
res = await fetch("/api/cli-tools/antigravity-mitm", {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sudoPassword: password }),
});
}
if (!res.ok) {
const data = await res.json().catch(() => ({}));
setActionError(data.error || `Failed to ${action} MITM server`);
return;
}
setShowPasswordModal(false);
setSudoPassword("");
await fetchStatus();
} catch (e) {
setActionError(e.message || "Network error");
} finally {
setLoading(false);
setPendingAction(null);
}
};
const handleConfirmPassword = () => {
if (!sudoPassword.trim()) {
setModalError("Sudo password is required");
return;
}
doAction(pendingAction, sudoPassword);
};
const isRunning = status?.running;
return (
<>
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
Enable DNS per tool below to activate interception
Required for SSL certificate and server startup