From a8dd432fd58a3036cf952eec691981dff43a7c7e Mon Sep 17 00:00:00 2001 From: soryu Date: Sun, 22 Feb 2026 16:39:15 +0000 Subject: fix: remove duplicate daemon page, add ARM64 binary, use makima.jp URLs (#78) * feat: soryu-co/soryu - makima: Add macOS ARM64 binary to daemon download bundle * feat: soryu-co/soryu - makima: Remove duplicate Daemon nav entry and route * feat: soryu-co/soryu - makima: Update download URLs to use makima.jp hosted instance --- makima/frontend/src/routes/daemon.tsx | 746 ---------------------------------- 1 file changed, 746 deletions(-) delete mode 100644 makima/frontend/src/routes/daemon.tsx (limited to 'makima/frontend/src/routes/daemon.tsx') diff --git a/makima/frontend/src/routes/daemon.tsx b/makima/frontend/src/routes/daemon.tsx deleted file mode 100644 index 66154ad..0000000 --- a/makima/frontend/src/routes/daemon.tsx +++ /dev/null @@ -1,746 +0,0 @@ -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 */} -
- - -
-
-
-
- ); -} -- cgit v1.2.3