import { useCallback, useState } from "react"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import { AddMachineSteps } from "../components/AddMachineSteps"; import { MachineRuntimeBadges } from "../components/MachineRuntimes"; import { formatRelative } from "../components/TaskDetailFields"; import { Button } from "../components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../components/ui/dialog"; import { Input } from "../components/ui/input"; import { useMachines } from "../hooks/useMachines"; import { api } from "../lib/api"; import { authClient } from "../lib/auth-client"; const statusDotColors: Record = { online: "bg-success", offline: "bg-content-tertiary", }; type DialogStep = "choose" | "cloud" | "waiting"; function randomName() { const adj = ["swift", "quiet", "bright", "sharp", "bold", "calm", "keen", "warm"]; const noun = ["falcon", "cedar", "river", "spark", "forge", "ridge", "stone", "drift"]; const pick = (arr: string[]) => arr[Math.floor(Math.random() * arr.length)]; return `${pick(adj)}-${pick(noun)}`; } export function MachinesPage() { const { machines, loading, refresh } = useMachines(); const [showDialog, setShowDialog] = useState(false); const [dialogStep, setDialogStep] = useState("choose"); const [createdKey, setCreatedKey] = useState(null); const [createdKeyId, setCreatedKeyId] = useState(null); const [connected, setConnected] = useState(false); const [creatingCloud, setCreatingCloud] = useState(false); const [cloudName, setCloudName] = useState(""); async function handleChooseLocal() { const name = randomName(); const { data, error } = await authClient.apiKey.create({ name }); if (error || !data) return; setCreatedKey(data.key); setCreatedKeyId(data.id); setDialogStep("waiting"); } function handleChooseCloud() { setCloudName(""); setDialogStep("cloud"); } async function handleCreateCloud() { const name = cloudName.trim(); if (!name) return; setCreatingCloud(true); try { await api.machines.createCloud({ name }); toast.success("Cloud sandbox added"); resetDialog(); refresh(); } catch (err) { const status = (err as { status?: number }).status; toast.error(status === 403 ? "Connect AMA to add a cloud sandbox" : (err as Error).message || "Failed to add cloud sandbox"); } finally { setCreatingCloud(false); } } const handleConnected = useCallback(async () => { setConnected(true); refresh(); }, [refresh]); async function closeDialog() { if (createdKeyId && !connected) { await authClient.apiKey.delete({ keyId: createdKeyId }).catch(() => {}); } resetDialog(); } function resetDialog() { setShowDialog(false); setDialogStep("choose"); setCreatedKey(null); setCreatedKeyId(null); setConnected(false); setCloudName(""); } return (

Machines

{machines.filter((m) => m.status === "online").length} online
{loading ? (
{[0, 1, 2].map((i) => (
))}
) : machines.length === 0 ? (

No machines registered.

Click{" "} {" "} to get started.

) : (
{machines.map((machine) => (
{machine.name} {machine.os && {machine.os}}
{machine.status}
Sessions: {machine.session_count}
Active: {machine.active_session_count}
Heartbeat: {machine.last_heartbeat_at ? formatRelative(machine.last_heartbeat_at) : "—"}
))}
)}
{/* Add Machine Dialog */} { if (!open) closeDialog(); }} > Add Machine Add a new machine to run agents {dialogStep === "choose" && (

Where will this machine run?

)} {dialogStep === "cloud" && (
setCloudName(e.target.value)} placeholder="e.g. my-sandbox" onKeyDown={(e) => { if (e.key === "Enter") handleCreateCloud(); }} />
)} {dialogStep === "waiting" && createdKey && createdKeyId && ( )}
); }