diff options
| author | soryu <soryu@soryu.co> | 2026-02-22 16:39:15 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-22 16:39:15 +0000 |
| commit | a8dd432fd58a3036cf952eec691981dff43a7c7e (patch) | |
| tree | 925d67ce3f4a00b744a320c920be57f13f5b15a5 | |
| parent | 0c00b6aa26baf49ba02164cd44aab16f9c804bbd (diff) | |
| download | soryu-a8dd432fd58a3036cf952eec691981dff43a7c7e.tar.gz soryu-a8dd432fd58a3036cf952eec691981dff43a7c7e.zip | |
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
| -rw-r--r-- | .github/workflows/release.yml | 47 | ||||
| -rw-r--r-- | install.sh | 88 | ||||
| -rw-r--r-- | makima/frontend/src/components/NavStrip.tsx | 1 | ||||
| -rw-r--r-- | makima/frontend/src/main.tsx | 9 | ||||
| -rw-r--r-- | makima/frontend/src/routes/daemon.tsx | 746 |
5 files changed, 42 insertions, 849 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 42d4241..fbf5e13 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,12 +93,19 @@ jobs: name: makima-${{ github.ref_name }}-linux-arm64 path: daemon-binaries + - name: Download macOS ARM64 artifact + uses: actions/download-artifact@v4 + with: + name: makima-${{ github.ref_name }}-macos-arm64 + path: daemon-binaries + - name: Extract and repackage daemon binaries run: | - mkdir -p daemon-extracted/linux-x86_64 daemon-extracted/linux-arm64 + mkdir -p daemon-extracted/linux-x86_64 daemon-extracted/linux-arm64 daemon-extracted/macos-arm64 tar xzf daemon-binaries/makima-${{ github.ref_name }}-linux-x86_64.tar.gz -C daemon-extracted/linux-x86_64 tar xzf daemon-binaries/makima-${{ github.ref_name }}-linux-arm64.tar.gz -C daemon-extracted/linux-arm64 + tar xzf daemon-binaries/makima-${{ github.ref_name }}-macos-arm64.tar.gz -C daemon-extracted/macos-arm64 tar czvf daemon-binaries.tar.gz -C daemon-extracted . @@ -150,24 +157,27 @@ jobs: ```bash # Linux x86_64 - curl -LO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-linux-x86_64.tar.gz - tar xzf makima-${{ github.ref_name }}-linux-x86_64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/linux-x86_64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # Linux ARM64 - curl -LO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-linux-arm64.tar.gz - tar xzf makima-${{ github.ref_name }}-linux-arm64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/linux-arm64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # macOS Intel - curl -LO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-macos-x86_64.tar.gz - tar xzf makima-${{ github.ref_name }}-macos-x86_64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/macos-x86_64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # macOS Apple Silicon - curl -LO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-macos-arm64.tar.gz - tar xzf makima-${{ github.ref_name }}-macos-arm64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/macos-arm64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ + + # Or use the install script (auto-detects platform): + curl -fsSL https://raw.githubusercontent.com/soryu-co/soryu/master/install.sh | bash ``` ### Verification @@ -202,24 +212,27 @@ jobs: ```bash # Linux x86_64 - curl -LO https://github.com/soryu-co/makima/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-linux-x86_64.tar.gz - tar xzf makima-${{ github.ref_name }}-linux-x86_64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/linux-x86_64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # Linux ARM64 - curl -LO https://github.com/soryu-co/makima/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-linux-arm64.tar.gz - tar xzf makima-${{ github.ref_name }}-linux-arm64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/linux-arm64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # macOS Intel - curl -LO https://github.com/soryu-co/makima/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-macos-x86_64.tar.gz - tar xzf makima-${{ github.ref_name }}-macos-x86_64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/macos-x86_64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ # macOS Apple Silicon - curl -LO https://github.com/soryu-co/makima/releases/download/${{ github.ref_name }}/makima-${{ github.ref_name }}-macos-arm64.tar.gz - tar xzf makima-${{ github.ref_name }}-macos-arm64.tar.gz + curl -fsSL https://api.makima.jp/api/v1/daemon/download/macos-arm64 -o makima + chmod +x makima sudo mv makima /usr/local/bin/ + + # Or use the install script (auto-detects platform): + curl -fsSL https://raw.githubusercontent.com/soryu-co/soryu/master/install.sh | bash ``` ### Verification @@ -5,7 +5,6 @@ set -e # Usage: curl -fsSL https://raw.githubusercontent.com/soryu-co/soryu/master/install.sh | bash # curl -fsSL https://raw.githubusercontent.com/soryu-co/soryu/master/install.sh | INSTALL_DIR=/opt/bin bash -REPO="soryu-co/soryu" INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}" BINARY_NAME="makima" @@ -39,10 +38,6 @@ check_dependencies() { missing="$missing curl" fi - if ! command -v tar &> /dev/null; then - missing="$missing tar" - fi - if [ -n "$missing" ]; then print_error "Missing required tools:$missing" print_info "Please install the missing tools and try again." @@ -90,29 +85,11 @@ detect_arch() { esac } -# Get the latest release version from GitHub API -get_latest_version() { - local version - version=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - - if [ -z "$version" ]; then - print_error "Failed to fetch latest release version from GitHub" - exit 1 - fi - - echo "$version" -} - -# Construct the download URL for the release asset +# Construct the download URL for the binary get_download_url() { - local version=$1 - local os=$2 - local arch=$3 - - # Handle macOS arm64 vs Linux x86_64 naming - local asset_name="makima-${version}-${os}-${arch}.tar.gz" - - echo "https://github.com/$REPO/releases/download/${version}/${asset_name}" + local os=$1 + local arch=$2 + echo "https://api.makima.jp/api/v1/daemon/download/${os}-${arch}" } # Download and install the binary @@ -120,55 +97,26 @@ install_binary() { local url=$1 local tmpdir tmpdir=$(mktemp -d) - local tarball="$tmpdir/makima.tar.gz" + local binary="$tmpdir/$BINARY_NAME" print_info "Downloading from: $url" - # Download the tarball - if ! curl -fsSL "$url" -o "$tarball"; then - print_error "Failed to download release from: $url" - print_info "Please check if the release exists for your platform." + if ! curl -fsSL "$url" -o "$binary"; then + print_error "Failed to download from: $url" + print_info "Please check if the binary is available for your platform." rm -rf "$tmpdir" exit 1 fi - # Verify download was successful - if [ ! -f "$tarball" ] || [ ! -s "$tarball" ]; then + if [ ! -f "$binary" ] || [ ! -s "$binary" ]; then print_error "Downloaded file is empty or missing" rm -rf "$tmpdir" exit 1 fi - # Extract the tarball - print_info "Extracting archive..." - if ! tar -xzf "$tarball" -C "$tmpdir"; then - print_error "Failed to extract archive" - rm -rf "$tmpdir" - exit 1 - fi - - # Find the binary (it should be in the extracted files) - local binary - if [ -f "$tmpdir/$BINARY_NAME" ]; then - binary="$tmpdir/$BINARY_NAME" - elif [ -f "$tmpdir/makima/$BINARY_NAME" ]; then - binary="$tmpdir/makima/$BINARY_NAME" - else - # Search for the binary - binary=$(find "$tmpdir" -name "$BINARY_NAME" -type f | head -1) - if [ -z "$binary" ]; then - print_error "Could not find $BINARY_NAME binary in archive" - print_info "Archive contents:" - ls -la "$tmpdir" - rm -rf "$tmpdir" - exit 1 - fi - fi - - # Make binary executable chmod +x "$binary" - # Create install directory if it doesn't exist + # Create install directory if needed if [ ! -d "$INSTALL_DIR" ]; then print_info "Creating directory: $INSTALL_DIR" if ! mkdir -p "$INSTALL_DIR" 2>/dev/null; then @@ -177,14 +125,13 @@ install_binary() { fi fi - # Install binary + # Install print_info "Installing to: $INSTALL_DIR/$BINARY_NAME" if ! mv "$binary" "$INSTALL_DIR/$BINARY_NAME" 2>/dev/null; then print_warning "Cannot write to $INSTALL_DIR, trying with sudo..." sudo mv "$binary" "$INSTALL_DIR/$BINARY_NAME" fi - # Cleanup rm -rf "$tmpdir" } @@ -217,30 +164,19 @@ main() { print_info "====================" print_info "" - # Check dependencies check_dependencies - # Detect platform local os arch os=$(detect_os) arch=$(detect_arch) print_info "Detected platform: $os-$arch" - # Get latest version - print_info "Fetching latest release..." - local version - version=$(get_latest_version) - print_info "Latest version: $version" - - # Construct download URL local url - url=$(get_download_url "$version" "$os" "$arch") + url=$(get_download_url "$os" "$arch") - # Download and install print_info "" install_binary "$url" - # Verify print_info "" verify_installation diff --git a/makima/frontend/src/components/NavStrip.tsx b/makima/frontend/src/components/NavStrip.tsx index 1bd0891..9556458 100644 --- a/makima/frontend/src/components/NavStrip.tsx +++ b/makima/frontend/src/components/NavStrip.tsx @@ -17,7 +17,6 @@ const NAV_LINKS: NavLink[] = [ { label: "Mesh", href: "/mesh", requiresAuth: true }, { label: "Daemons", href: "/daemons", requiresAuth: true }, { label: "History", href: "/history", requiresAuth: true }, - { label: "Daemon", href: "/daemon", requiresAuth: true }, ]; export function NavStrip() { diff --git a/makima/frontend/src/main.tsx b/makima/frontend/src/main.tsx index a75d6a0..32c05ba 100644 --- a/makima/frontend/src/main.tsx +++ b/makima/frontend/src/main.tsx @@ -21,7 +21,6 @@ import SettingsPage from "./routes/settings"; import ContractFilePage from "./routes/contract-file"; import SpeakPage from "./routes/speak"; import DirectivesPage from "./routes/directives"; -import DaemonPage from "./routes/daemon"; createRoot(document.getElementById("root")!).render( <StrictMode> @@ -163,14 +162,6 @@ createRoot(document.getElementById("root")!).render( } /> <Route - path="/daemon" - element={ - <ProtectedRoute> - <DaemonPage /> - </ProtectedRoute> - } - /> - <Route path="/speak" element={ <ProtectedRoute> 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 ( - <h2 className="text-[11px] font-mono uppercase tracking-wide text-[#8899aa] mb-3 pb-2 border-b border-[rgba(117,170,252,0.15)]"> - {children} - </h2> - ); -} - -function ErrorAlert({ children }: { children: React.ReactNode }) { - return ( - <div className="border border-red-700/50 bg-red-900/20 text-red-400 px-3 py-2 mb-4 font-mono text-xs"> - {children} - </div> - ); -} - -function CodeBlock({ children }: { children: React.ReactNode }) { - return ( - <code className="block bg-black/50 px-3 py-2 text-[11px] font-mono text-green-400 mb-2 overflow-x-auto"> - {children} - </code> - ); -} - -function StepNumber({ n }: { n: number }) { - return ( - <span className="inline-flex items-center justify-center w-5 h-5 border border-[rgba(117,170,252,0.35)] text-[10px] font-mono text-[#75aafc] mr-2 shrink-0"> - {n} - </span> - ); -} - -// ============================================================================= -// Download Section -// ============================================================================= - -function DownloadSection() { - const [release, setRelease] = useState<GitHubRelease | null>(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState<string | null>(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 ( - <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4"> - <SectionHeader>Download Daemon</SectionHeader> - - {loading && ( - <p className="text-[#7788aa] font-mono text-xs"> - Fetching latest release... - </p> - )} - - {error && <ErrorAlert>Failed to load release: {error}</ErrorAlert>} - - {release && ( - <> - <div className="flex items-center justify-between mb-4"> - <div className="flex items-center gap-3"> - <span className="text-[#9bc3ff] font-mono text-sm"> - {release.tag_name} - </span> - <span className="text-[#556677] font-mono text-[10px]"> - {formatDate(release.published_at)} - </span> - </div> - <a - href="https://github.com/soryu-co/makima/releases" - target="_blank" - rel="noopener noreferrer" - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors" - > - All Releases → - </a> - </div> - - <div className="space-y-2"> - {sortedPlatforms.map((p) => ( - <div - key={p.arch} - className={`border p-3 flex items-center justify-between ${ - p.recommended - ? "border-[rgba(117,170,252,0.5)] bg-[#0a1628]" - : "border-[rgba(117,170,252,0.15)] bg-[#0a1525]" - }`} - > - <div className="flex items-center gap-3"> - <span className="font-mono text-xs text-[#9bc3ff]"> - {p.label} - </span> - {p.recommended && ( - <span className="text-[9px] font-mono uppercase px-1.5 py-0.5 border border-[rgba(117,170,252,0.4)] text-[#75aafc] bg-[rgba(117,170,252,0.08)]"> - Detected - </span> - )} - {p.asset && ( - <span className="text-[10px] font-mono text-[#556677]"> - {formatBytes(p.asset.size)} - </span> - )} - </div> - {p.asset ? ( - <a - href={p.asset.browser_download_url} - className={`text-[10px] font-mono uppercase px-3 py-1.5 border transition-colors ${ - p.recommended - ? "text-[#9bc3ff] border-[rgba(117,170,252,0.5)] bg-[rgba(117,170,252,0.1)] hover:bg-[rgba(117,170,252,0.2)]" - : "text-[#75aafc] border-[rgba(117,170,252,0.25)] hover:bg-[rgba(117,170,252,0.1)]" - }`} - download - > - Download - </a> - ) : ( - <span className="text-[10px] font-mono text-[#556677]"> - Not available - </span> - )} - </div> - ))} - </div> - </> - )} - </section> - ); -} - -// ============================================================================= -// Setup Instructions Section -// ============================================================================= - -function SetupSection() { - const [showConfig, setShowConfig] = useState(false); - - return ( - <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4"> - <SectionHeader>Setup Instructions</SectionHeader> - <div className="space-y-3"> - <div className="flex items-start"> - <StepNumber n={1} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Download the binary for your platform above - </p> - </div> - </div> - - <div className="flex items-start"> - <StepNumber n={2} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Extract the archive - </p> - <CodeBlock>tar xzf makima-*.tar.gz</CodeBlock> - </div> - </div> - - <div className="flex items-start"> - <StepNumber n={3} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Move to PATH - </p> - <CodeBlock>sudo mv makima /usr/local/bin/</CodeBlock> - </div> - </div> - - <div className="flex items-start"> - <StepNumber n={4} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Set your API key ( - <a - href="/settings" - className="text-[#75aafc] hover:text-[#9bc3ff] transition-colors" - > - generate one in Settings - </a> - ) - </p> - <CodeBlock>export MAKIMA_API_KEY="your-key"</CodeBlock> - </div> - </div> - - <div className="flex items-start"> - <StepNumber n={5} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Set server URL - </p> - <CodeBlock> - export MAKIMA_DAEMON_SERVER_URL="ws://your-server:8080" - </CodeBlock> - </div> - </div> - - <div className="flex items-start"> - <StepNumber n={6} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Run the daemon - </p> - <CodeBlock>makima daemon</CodeBlock> - </div> - </div> - </div> - - {/* Config file alternative */} - <div className="mt-4 pt-3 border-t border-[rgba(117,170,252,0.1)]"> - <button - onClick={() => setShowConfig(!showConfig)} - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors bg-transparent border-none cursor-pointer p-0" - > - {showConfig ? "- Hide" : "+"} Config file alternative - </button> - {showConfig && ( - <div className="mt-3"> - <p className="text-[#7788aa] font-mono text-[10px] mb-2"> - Create <code className="text-green-400">makima-daemon.toml</code>{" "} - in the working directory: - </p> - <code className="block bg-black/50 px-3 py-2 text-[11px] font-mono text-green-400 whitespace-pre overflow-x-auto"> - {`[daemon] -api_key = "your-key" -server_url = "ws://your-server:8080" -max_concurrent_tasks = 4`} - </code> - </div> - )} - </div> - </section> - ); -} - -// ============================================================================= -// 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 ( - <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4"> - <SectionHeader>Edge Deployment</SectionHeader> - - <p className="text-[#7788aa] font-mono text-[10px] mb-4 leading-relaxed"> - 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. - </p> - - {/* Benefits */} - <div className="space-y-2 mb-4"> - {benefits.map((b) => ( - <div - key={b.label} - className="flex items-start gap-2 text-[10px] font-mono" - > - <span className="text-[#75aafc] mt-px shrink-0">▸</span> - <div> - <span className="text-[#9bc3ff]">{b.label}</span> - <span className="text-[#556677] ml-1">— {b.desc}</span> - </div> - </div> - ))} - </div> - - {/* Quick Setup */} - <div className="border-t border-[rgba(117,170,252,0.1)] pt-3"> - <button - onClick={() => setShowSetup(!showSetup)} - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors bg-transparent border-none cursor-pointer p-0" - > - {showSetup ? "- Hide" : "+"} Quick setup - </button> - {showSetup && ( - <div className="mt-3 space-y-3"> - <div className="flex items-start"> - <StepNumber n={1} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Navigate to the Cloudflare agent directory - </p> - <CodeBlock>cd makima/cloudflare-agent</CodeBlock> - </div> - </div> - <div className="flex items-start"> - <StepNumber n={2} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Run the setup script - </p> - <CodeBlock>./setup.sh</CodeBlock> - </div> - </div> - <div className="flex items-start"> - <StepNumber n={3} /> - <div className="flex-1"> - <p className="text-[#7788aa] font-mono text-[10px] mb-1"> - Deploy to Cloudflare - </p> - <CodeBlock>npx wrangler deploy</CodeBlock> - </div> - </div> - </div> - )} - </div> - - {/* Link to repo */} - <div className="mt-4 pt-3 border-t border-[rgba(117,170,252,0.1)] flex items-center justify-between"> - <span className="text-[10px] font-mono text-[#556677]"> - Full documentation & source - </span> - <a - href="https://github.com/soryu-co/soryu/tree/master/makima/cloudflare-agent" - target="_blank" - rel="noopener noreferrer" - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] transition-colors px-3 py-1.5 border border-[rgba(117,170,252,0.25)] hover:bg-[rgba(117,170,252,0.1)]" - > - View on GitHub → - </a> - </div> - </section> - ); -} - -// ============================================================================= -// Connected Daemons Section -// ============================================================================= - -function ConnectedDaemonsSection() { - const [daemons, setDaemons] = useState<Daemon[]>([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState<string | null>(null); - const [restartingDaemonId, setRestartingDaemonId] = useState<string | null>( - 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 ( - <section className="border border-[rgba(117,170,252,0.25)] bg-[#0d1b2d] p-4"> - <div className="flex items-center justify-between mb-3 pb-2 border-b border-[rgba(117,170,252,0.15)]"> - <div className="flex items-center gap-2"> - <h2 className="text-[11px] font-mono uppercase tracking-wide text-[#8899aa]"> - Connected Daemons - </h2> - {daemons.length > 0 && ( - <span className="text-[10px] font-mono text-[#556677]"> - ({daemons.filter((d) => d.status === "connected").length}{" "} - connected / {daemons.length} total) - </span> - )} - </div> - <button - onClick={loadDaemons} - disabled={loading} - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] disabled:opacity-50 bg-transparent border-none cursor-pointer p-0" - title="Refresh" - > - {loading ? "..." : "\u21BB"} - </button> - </div> - - {error && <ErrorAlert>{error}</ErrorAlert>} - - {loading && daemons.length === 0 ? ( - <p className="text-[#7788aa] font-mono text-xs">Loading...</p> - ) : daemons.length === 0 ? ( - <div className="text-center py-6"> - <p className="text-[#7788aa] font-mono text-xs mb-2"> - No daemons connected - </p> - <p className="text-[#556677] font-mono text-[10px]"> - Follow the setup instructions above to connect a daemon - </p> - </div> - ) : ( - <div className="space-y-2"> - {daemons.map((daemon) => ( - <div - key={daemon.id} - className="border border-[rgba(117,170,252,0.15)] bg-[#0a1525] p-3" - > - <div className="flex items-center justify-between mb-2"> - <span className="font-mono text-xs text-[#9bc3ff]"> - {daemon.hostname || "Unknown Host"} - </span> - <div className="flex items-center gap-2"> - <span - className={`text-[10px] font-mono uppercase px-2 py-0.5 border ${ - daemon.status === "connected" - ? "text-green-400 border-green-700/50 bg-green-900/20" - : daemon.status === "unhealthy" - ? "text-yellow-400 border-yellow-700/50 bg-yellow-900/20" - : "text-[#8899aa] border-[rgba(117,170,252,0.25)]" - }`} - > - {daemon.status} - </span> - </div> - </div> - <div className="font-mono text-[10px] text-[#7788aa] space-y-1"> - <div className="flex justify-between"> - <span>Tasks</span> - <span className="text-[#9bc3ff]"> - {daemon.currentTaskCount} / {daemon.maxConcurrentTasks} - </span> - </div> - <div className="flex justify-between"> - <span>Connected</span> - <span className="text-[#75aafc]"> - {new Date(daemon.connectedAt).toLocaleString()} - </span> - </div> - {daemon.machineId && ( - <div className="flex justify-between"> - <span>Machine</span> - <span - className="text-[#556677] truncate ml-2" - title={daemon.machineId} - > - {daemon.machineId.substring(0, 16)}... - </span> - </div> - )} - </div> - {/* Restart Section */} - {daemon.status === "connected" && ( - <div className="mt-3 pt-2 border-t border-[rgba(117,170,252,0.1)]"> - {restartConfirmDaemonId === daemon.id ? ( - <div className="flex items-center justify-between gap-2"> - <span className="text-[10px] font-mono text-yellow-400"> - Restart daemon? Running tasks will be interrupted. - </span> - <div className="flex gap-2"> - <button - onClick={() => setRestartConfirmDaemonId(null)} - className="text-[10px] font-mono text-[#7788aa] hover:text-[#9bc3ff] px-2 py-1 bg-transparent border-none cursor-pointer" - disabled={restartingDaemonId === daemon.id} - > - Cancel - </button> - <button - onClick={() => handleRestartDaemon(daemon.id)} - disabled={restartingDaemonId === daemon.id} - className="text-[10px] font-mono text-red-400 hover:text-red-300 px-2 py-1 border border-red-700/50 bg-red-900/20 disabled:opacity-50 cursor-pointer" - > - {restartingDaemonId === daemon.id - ? "Restarting..." - : "Confirm"} - </button> - </div> - </div> - ) : ( - <button - onClick={() => setRestartConfirmDaemonId(daemon.id)} - className="text-[10px] font-mono text-[#75aafc] hover:text-[#9bc3ff] bg-transparent border-none cursor-pointer p-0" - > - ⟳ Restart Daemon - </button> - )} - </div> - )} - </div> - ))} - </div> - )} - </section> - ); -} - -// ============================================================================= -// 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 ( - <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]"> - <Masthead showNav /> - <main className="flex-1 flex items-center justify-center"> - <p className="text-[#7788aa] font-mono text-sm">Loading...</p> - </main> - </div> - ); - } - - return ( - <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]"> - <Masthead showNav /> - <main className="flex-1 px-4 py-6 max-w-4xl mx-auto w-full"> - {/* Page header */} - <div className="mb-6"> - <h1 className="text-sm font-mono uppercase tracking-wide text-[#9bc3ff] mb-1"> - Daemon Management - </h1> - <p className="text-[#556677] font-mono text-[10px]"> - Download, configure, and monitor Makima daemons - </p> - </div> - - <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> - {/* Left Column: Downloads & Setup */} - <div className="space-y-6"> - <DownloadSection /> - <SetupSection /> - </div> - - {/* Right Column: Edge Deployment & Connected Daemons */} - <div className="space-y-6"> - <ConnectedDaemonsSection /> - <CloudflareAgentSection /> - </div> - </div> - </main> - </div> - ); -} |
