import { useState, useEffect, useCallback, useRef } from "react"; import { useAuth } from "../contexts/AuthContext"; import { useNavigate } from "react-router"; import { Masthead } from "../components/Masthead"; import { listDaemons, restartDaemon, triggerDaemonReauth, getDaemonReauthStatus, type Daemon, type DaemonListResponse, } from "../lib/api"; // ============================================================================= // Section Header Component // ============================================================================= function SectionHeader({ children }: { children: React.ReactNode }) { return (

{children}

); } // ============================================================================= // Alert Component // ============================================================================= function ErrorAlert({ children }: { children: React.ReactNode }) { return (
{children}
); } // ============================================================================= // Reauth Modal Component // ============================================================================= type ReauthState = | { phase: "initiating" } | { phase: "url_ready"; loginUrl: string; requestId: string } | { phase: "waiting_for_auth"; loginUrl: string; requestId: string } | { phase: "success" } | { phase: "error"; message: string }; function ReauthModal({ daemon, onClose, }: { daemon: Daemon; onClose: () => void; }) { const [state, setState] = useState({ phase: "initiating" }); const pollingRef = useRef | null>(null); // Cleanup polling on unmount useEffect(() => { return () => { if (pollingRef.current) { clearInterval(pollingRef.current); } }; }, []); // Start polling for status updates (URL ready, then completion) const startPolling = useCallback( (requestId: string) => { if (pollingRef.current) { clearInterval(pollingRef.current); } pollingRef.current = setInterval(async () => { try { const status = await getDaemonReauthStatus(daemon.id, requestId); if (status.status === "completed") { setState({ phase: "success" }); if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; } } else if (status.status === "url_ready" && status.loginUrl) { setState((prev) => { // Only update if we haven't already shown the URL if (prev.phase === "initiating") { return { phase: "url_ready", loginUrl: status.loginUrl!, requestId, }; } return prev; }); // Keep polling for completion - don't stop here } else if (status.status === "failed") { setState({ phase: "error", message: status.error || "Reauth failed", }); if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; } } } catch { // Polling errors are non-fatal, keep trying } }, 2000); }, [daemon.id], ); // Trigger reauth on mount useEffect(() => { let cancelled = false; const trigger = async () => { try { const res = await triggerDaemonReauth(daemon.id); if (cancelled) return; startPolling(res.requestId); } catch (err) { if (cancelled) return; setState({ phase: "error", message: err instanceof Error ? err.message : "Failed to trigger reauth", }); } }; trigger(); return () => { cancelled = true; }; }, [daemon.id, startPolling]); // When URL is shown, transition to waiting_for_auth after user clicks the link const handleOpenedLink = useCallback(() => { setState((prev) => { if (prev.phase === "url_ready") { return { phase: "waiting_for_auth", loginUrl: prev.loginUrl, requestId: prev.requestId, }; } return prev; }); }, []); const handleRetry = useCallback(() => { setState({ phase: "initiating" }); const trigger = async () => { try { const res = await triggerDaemonReauth(daemon.id); startPolling(res.requestId); } catch (err) { setState({ phase: "error", message: err instanceof Error ? err.message : "Failed to trigger reauth", }); } }; trigger(); }, [daemon.id, startPolling]); return (
{/* Header */}

Reauthorize Daemon

{daemon.hostname || "Unknown Host"}

{/* Initiating */} {state.phase === "initiating" && (
Initiating reauthorization...
)} {/* URL Ready - user needs to click the link */} {state.phase === "url_ready" && (

Click the button below to open the OAuth login page. Authentication will complete automatically.

Login to Claude
Waiting for authentication to complete...
)} {/* Waiting for auth - user has clicked the link, waiting for token */} {state.phase === "waiting_for_auth" && (
Waiting for authentication to complete...

Complete the login in your browser. The token will be saved automatically.

Open login page again
)} {/* Success */} {state.phase === "success" && (
Authentication successful

The daemon's OAuth token has been refreshed. Tasks can now run normally.

)} {/* Error */} {state.phase === "error" && (
{state.message}
)}
); } // ============================================================================= // Daemons Page // ============================================================================= export default function DaemonsPage() { const { isAuthenticated, isAuthConfigured } = useAuth(); const navigate = useNavigate(); // Daemon state const [daemons, setDaemons] = useState([]); const [daemonsLoading, setDaemonsLoading] = useState(true); const [daemonsError, setDaemonsError] = useState(null); const [restartingDaemonId, setRestartingDaemonId] = useState(null); const [restartConfirmDaemonId, setRestartConfirmDaemonId] = useState(null); const [reauthDaemon, setReauthDaemon] = useState(null); // Redirect if not authenticated useEffect(() => { if (isAuthConfigured && !isAuthenticated) { navigate("/login"); } }, [isAuthConfigured, isAuthenticated, navigate]); const loadDaemons = async () => { try { setDaemonsError(null); const response: DaemonListResponse = await listDaemons(); setDaemons(response.daemons); } catch (err) { setDaemonsError(err instanceof Error ? err.message : "Failed to load daemons"); } finally { setDaemonsLoading(false); } }; const handleRestartDaemon = async (id: string) => { try { setRestartingDaemonId(id); setDaemonsError(null); await restartDaemon(id); // Daemon will restart, so refresh the list after a short delay setTimeout(() => { loadDaemons(); }, 2000); } catch (err) { setDaemonsError(err instanceof Error ? err.message : "Failed to restart daemon"); } finally { setRestartingDaemonId(null); setRestartConfirmDaemonId(null); } }; // Static platform data for download links const downloadPlatforms = [ { key: "linux-x86_64", label: "Linux (Intel/AMD)", filename: "makima-vX.X.X-linux-x86_64.tar.gz" }, { key: "linux-arm64", label: "Linux (ARM64)", filename: "makima-vX.X.X-linux-arm64.tar.gz" }, { key: "macos-x86_64", label: "macOS (Intel)", filename: "makima-vX.X.X-macos-x86_64.tar.gz" }, { key: "macos-arm64", label: "macOS (Apple Silicon)", filename: "makima-vX.X.X-macos-arm64.tar.gz" }, ]; // Initial load useEffect(() => { loadDaemons(); }, []); // Auto-refresh daemons every 30 seconds useEffect(() => { const interval = setInterval(() => { loadDaemons(); }, 30000); return () => clearInterval(interval); }, []); return (
{/* Page Header */}

Daemons

Daemons are worker processes that connect to Makima and execute tasks on your machines.

{/* Left Column */}
{/* Download Section */}
Download Daemon

Download the pre-compiled daemon binary for your platform. The daemon connects to the Makima server and executes tasks.

{downloadPlatforms.map((p) => ( {p.label} {p.filename} ))}

Quick Install

curl -fsSL https://raw.githubusercontent.com/soryu-co/makima/master/install.sh | bash
{/* Daemon Setup */}
Daemon Setup

Set your API key as an environment variable:

export MAKIMA_API_KEY="your-key"

Then run: makima-daemon

{/* Kubernetes Section */}
Run in Kubernetes

Deploy daemons as containers in Kubernetes for scalable task execution.

Pull Container Image

docker pull ghcr.io/soryu-co/makima-daemon:latest

Environment Variables

MAKIMA_API_KEY="your-key"
MAKIMA_SERVER_URL="https://your-server"
GITHUB_TOKEN="ghp_..." # optional, for repo access

Kubernetes manifests available at{" "} github.com/soryu-co/makima

{/* Right Column */}
{/* Connected Daemons */}

Connected Daemons

{daemons.length > 0 && ( ({daemons.filter(d => d.status === "connected").length} connected / {daemons.length} total) )}
{daemonsError && {daemonsError}} {daemonsLoading && daemons.length === 0 ? (

Loading...

) : daemons.length === 0 ? (

No daemons connected

Start a daemon to enable task execution

) : (
{daemons.map((daemon) => (
{daemon.hostname || "Unknown Host"}
{daemon.status}
Tasks {daemon.currentTaskCount} / {daemon.maxConcurrentTasks}
Connected {new Date(daemon.connectedAt).toLocaleString()}
{daemon.machineId && (
Machine {daemon.machineId.substring(0, 16)}...
)}
{/* Actions Section */} {daemon.status === "connected" && (
{restartConfirmDaemonId === daemon.id ? (
Restart daemon? Running tasks will be interrupted.
) : (
)}
)}
))}
)}
{/* Reauth Modal */} {reauthDaemon && ( setReauthDaemon(null)} /> )}
); }