import { useState, useEffect, useCallback } from "react"; import { useNavigate } from "react-router"; import { Masthead } from "../components/Masthead"; import { useAuth } from "../contexts/AuthContext"; import { listDaemons, restartDaemon, type Daemon, } from "../lib/api"; // ============================================================================= // Types // ============================================================================= interface GitHubAsset { name: string; browser_download_url: string; size: number; } interface GitHubRelease { tag_name: string; name: string; published_at: string; html_url: string; assets: GitHubAsset[]; } interface PlatformDownload { label: string; arch: string; pattern: string; asset: GitHubAsset | null; recommended: boolean; } // ============================================================================= // Helpers // ============================================================================= function detectPlatform(): string { const ua = navigator.userAgent.toLowerCase(); const platform = navigator.platform?.toLowerCase() || ""; if (platform.includes("mac") || ua.includes("macintosh")) { // Check for Apple Silicon // navigator.platform is "MacIntel" even on ARM for some browsers, // but we can check userAgent for hints or default to arm64 for modern Macs if ( ua.includes("arm") || ua.includes("aarch64") || // Chrome 93+ on ARM Macs reports this (typeof navigator !== "undefined" && "userAgentData" in navigator && // @ts-expect-error -- userAgentData may not be typed navigator.userAgentData?.platform === "macOS") ) { return "macos-arm64"; } return "macos-arm64"; // Default to ARM64 for modern Macs } if (platform.includes("linux") || ua.includes("linux")) { return "linux-x86_64"; } return "linux-x86_64"; // fallback } function formatBytes(bytes: number): string { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`; } function formatDate(dateStr: string): string { return new Date(dateStr).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", }); } // ============================================================================= // Sub-components // ============================================================================= function SectionHeader({ children }: { children: React.ReactNode }) { return (

{children}

); } function ErrorAlert({ children }: { children: React.ReactNode }) { return (
{children}
); } function CodeBlock({ children }: { children: React.ReactNode }) { return ( {children} ); } function StepNumber({ n }: { n: number }) { return ( {n} ); } // ============================================================================= // Download Section // ============================================================================= function DownloadSection() { const [release, setRelease] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [userPlatform] = useState(detectPlatform); useEffect(() => { const fetchRelease = async () => { try { setLoading(true); setError(null); const res = await fetch( "https://api.github.com/repos/soryu-co/makima/releases/latest" ); if (!res.ok) { throw new Error(`GitHub API returned ${res.status}`); } const data: GitHubRelease = await res.json(); setRelease(data); } catch (err) { setError( err instanceof Error ? err.message : "Failed to fetch release info" ); } finally { setLoading(false); } }; fetchRelease(); }, []); const platforms: PlatformDownload[] = [ { label: "Linux x86_64", arch: "linux-x86_64", pattern: "linux-x86_64.tar.gz", asset: null, recommended: userPlatform === "linux-x86_64", }, { label: "macOS Intel (x86_64)", arch: "macos-x86_64", pattern: "macos-x86_64.tar.gz", asset: null, recommended: userPlatform === "macos-x86_64", }, { label: "macOS Apple Silicon (ARM64)", arch: "macos-arm64", pattern: "macos-arm64.tar.gz", asset: null, recommended: userPlatform === "macos-arm64", }, ]; // Match assets to platforms if (release) { for (const p of platforms) { p.asset = release.assets.find((a) => a.name.includes(p.pattern)) || null; } } // Sort recommended first const sortedPlatforms = [...platforms].sort( (a, b) => (b.recommended ? 1 : 0) - (a.recommended ? 1 : 0) ); return (
Download Daemon {loading && (

Fetching latest release...

)} {error && Failed to load release: {error}} {release && ( <>
{release.tag_name} {formatDate(release.published_at)}
All Releases →
{sortedPlatforms.map((p) => (
{p.label} {p.recommended && ( Detected )} {p.asset && ( {formatBytes(p.asset.size)} )}
{p.asset ? ( Download ) : ( Not available )}
))}
)}
); } // ============================================================================= // Setup Instructions Section // ============================================================================= function SetupSection() { const [showConfig, setShowConfig] = useState(false); return (
Setup Instructions

Download the binary for your platform above

Extract the archive

tar xzf makima-*.tar.gz

Move to PATH

sudo mv makima /usr/local/bin/

Set your API key ( generate one in Settings )

export MAKIMA_API_KEY="your-key"

Set server URL

export MAKIMA_DAEMON_SERVER_URL="ws://your-server:8080"

Run the daemon

makima daemon
{/* Config file alternative */}
{showConfig && (

Create makima-daemon.toml{" "} in the working directory:

{`[daemon] api_key = "your-key" server_url = "ws://your-server:8080" max_concurrent_tasks = 4`}
)}
); } // ============================================================================= // Cloudflare Edge Deployment Section // ============================================================================= function CloudflareAgentSection() { const [showSetup, setShowSetup] = useState(false); const benefits = [ { label: "Global edge presence", desc: "Lower latency from 300+ Cloudflare locations worldwide", }, { label: "Auto-scaling & hibernation", desc: "Cost-efficient — only runs when needed", }, { label: "WebSocket relay", desc: "Coordinate remote daemon instances through persistent connections", }, { label: "Durable Objects", desc: "Built on Cloudflare's stateful edge compute primitives", }, ]; return (
Edge Deployment

Deploy a lightweight Makima relay agent on Cloudflare's edge network for global, low-latency daemon coordination. Ideal for distributed teams or production deployments requiring high availability.

{/* Benefits */}
{benefits.map((b) => (
{b.label} — {b.desc}
))}
{/* Quick Setup */}
{showSetup && (

Navigate to the Cloudflare agent directory

cd makima/cloudflare-agent

Run the setup script

./setup.sh

Deploy to Cloudflare

npx wrangler deploy
)}
{/* Link to repo */}
Full documentation & source View on GitHub →
); } // ============================================================================= // Connected Daemons Section // ============================================================================= function ConnectedDaemonsSection() { const [daemons, setDaemons] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [restartingDaemonId, setRestartingDaemonId] = useState( null ); const [restartConfirmDaemonId, setRestartConfirmDaemonId] = useState< string | null >(null); const loadDaemons = useCallback(async () => { try { setError(null); const response = await listDaemons(); setDaemons(response.daemons); } catch (err) { setError( err instanceof Error ? err.message : "Failed to load daemons" ); } finally { setLoading(false); } }, []); useEffect(() => { loadDaemons(); }, [loadDaemons]); // Auto-refresh every 30 seconds useEffect(() => { const interval = setInterval(() => { loadDaemons(); }, 30000); return () => clearInterval(interval); }, [loadDaemons]); const handleRestartDaemon = async (id: string) => { try { setRestartingDaemonId(id); setError(null); await restartDaemon(id); setRestartConfirmDaemonId(null); // Daemon will restart, so refresh the list after a short delay setTimeout(() => { loadDaemons(); }, 2000); } catch (err) { setError( err instanceof Error ? err.message : "Failed to restart daemon" ); } finally { setRestartingDaemonId(null); } }; return (

Connected Daemons

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

Loading...

) : daemons.length === 0 ? (

No daemons connected

Follow the setup instructions above to connect a daemon

) : (
{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)}...
)}
{/* Restart Section */} {daemon.status === "connected" && (
{restartConfirmDaemonId === daemon.id ? (
Restart daemon? Running tasks will be interrupted.
) : ( )}
)}
))}
)}
); } // ============================================================================= // Main Page // ============================================================================= export default function DaemonPage() { const { isAuthenticated, isAuthConfigured, isLoading: authLoading, } = useAuth(); const navigate = useNavigate(); useEffect(() => { if (!authLoading && isAuthConfigured && !isAuthenticated) { navigate("/login"); } }, [authLoading, isAuthConfigured, isAuthenticated, navigate]); if (authLoading) { return (

Loading...

); } return (
{/* Page header */}

Daemon Management

Download, configure, and monitor Makima daemons

{/* Left Column: Downloads & Setup */}
{/* Right Column: Edge Deployment & Connected Daemons */}
); }