summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-22 16:39:15 +0000
committerGitHub <noreply@github.com>2026-02-22 16:39:15 +0000
commita8dd432fd58a3036cf952eec691981dff43a7c7e (patch)
tree925d67ce3f4a00b744a320c920be57f13f5b15a5
parent0c00b6aa26baf49ba02164cd44aab16f9c804bbd (diff)
downloadsoryu-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.yml47
-rw-r--r--install.sh88
-rw-r--r--makima/frontend/src/components/NavStrip.tsx1
-rw-r--r--makima/frontend/src/main.tsx9
-rw-r--r--makima/frontend/src/routes/daemon.tsx746
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
diff --git a/install.sh b/install.sh
index b7a96fe..3dc76e1 100644
--- a/install.sh
+++ b/install.sh
@@ -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 &rarr;
- </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">&#x25B8;</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 &rarr;
- </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"
- >
- &#x27F3; 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>
- );
-}