← back to Yolo Agent
setup-yolo-remote.sh
219 lines
#!/bin/bash
# ============================================================================
# YOLO Runner (Yuri) — Remote Machine Setup Script
#
# Deploys a portable YOLO agent on any machine with Claude CLI installed.
# Run this on the TARGET machine after copying yolo-agent/ directory.
#
# Prerequisites on target machine:
# 1. Node.js 18+ (node, npm)
# 2. Claude CLI authenticated (`claude` in PATH)
# 3. PM2 globally installed (`npm i -g pm2`)
#
# Usage:
# chmod +x setup-yolo-remote.sh
# ./setup-yolo-remote.sh [PORT] [WORKING_DIR]
#
# PORT — dashboard port (default: 9670)
# WORKING_DIR — cwd for Claude tasks (default: $HOME)
# ============================================================================
set -euo pipefail
PORT="${1:-9670}"
WORK_DIR="${2:-$HOME}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=== YOLO Runner Setup ==="
echo " Directory: $SCRIPT_DIR"
echo " Port: $PORT"
echo " Work Dir: $WORK_DIR"
echo ""
# ── Step 1: Verify prerequisites ──────────────────────────────────────────
echo "[1/6] Checking prerequisites..."
if ! command -v node &>/dev/null; then
echo "ERROR: Node.js not found. Install with: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash - && sudo apt install -y nodejs"
exit 1
fi
NODE_VER=$(node -v)
echo " Node.js: $NODE_VER"
if ! command -v npm &>/dev/null; then
echo "ERROR: npm not found."
exit 1
fi
if ! command -v pm2 &>/dev/null; then
echo " PM2 not found — installing globally..."
npm install -g pm2
fi
echo " PM2: $(pm2 -v 2>/dev/null || echo 'installed')"
# Find claude binary
CLAUDE_BIN=""
for candidate in /usr/bin/claude /usr/local/bin/claude "$HOME/.claude/bin/claude" "$(which claude 2>/dev/null)"; do
if [ -x "$candidate" 2>/dev/null ]; then
CLAUDE_BIN="$candidate"
break
fi
done
if [ -z "$CLAUDE_BIN" ]; then
echo "ERROR: Claude CLI not found. Install from https://claude.ai/download"
exit 1
fi
echo " Claude CLI: $CLAUDE_BIN"
# ── Step 2: Install dependencies ──────────────────────────────────────────
echo ""
echo "[2/6] Installing npm dependencies..."
cd "$SCRIPT_DIR"
npm install --production 2>&1 | tail -3
# ── Step 3: Create directory structure ────────────────────────────────────
echo ""
echo "[3/6] Creating task directories..."
mkdir -p "$SCRIPT_DIR/tasks/done"
mkdir -p "$SCRIPT_DIR/tasks/failed"
mkdir -p "$SCRIPT_DIR/tasks/pending"
mkdir -p "$SCRIPT_DIR/tasks/deferred"
mkdir -p "$SCRIPT_DIR/logs"
mkdir -p "$SCRIPT_DIR/history"
echo " Created: tasks/{done,failed,pending,deferred}, logs, history"
# ── Step 4: Generate local config override ────────────────────────────────
echo ""
echo "[4/6] Generating local config..."
CONFIG_FILE="$SCRIPT_DIR/local-config.json"
cat > "$CONFIG_FILE" << JSONEOF
{
"port": $PORT,
"claudeBin": "$CLAUDE_BIN",
"workingDir": "$WORK_DIR",
"authUser": "admin",
"authPass": "DWSecure2024!",
"cooldownSec": 30,
"maxTurns": 50,
"maxConcurrent": 3,
"timeoutMinutes": 20,
"allowedTools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "WebFetch", "WebSearch"]
}
JSONEOF
echo " Written: $CONFIG_FILE"
# ── Step 5: Patch server.js to read local config ─────────────────────────
echo ""
echo "[5/6] Checking server.js for local-config support..."
if grep -q "local-config.json" "$SCRIPT_DIR/server.js" 2>/dev/null; then
echo " Already patched — skipping."
else
echo " Creating wrapper script (yolo-local.js) to override constants..."
cat > "$SCRIPT_DIR/yolo-local.js" << 'JSEOF'
#!/usr/bin/env node
// ============================================================================
// YOLO Local Wrapper — reads local-config.json and patches env before
// loading the main server.js. Allows portable deployment without editing
// the original server.js.
// ============================================================================
const fs = require('fs');
const path = require('path');
const configPath = path.join(__dirname, 'local-config.json');
if (fs.existsSync(configPath)) {
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
// Override via environment variables that server.js respects
if (cfg.port) process.env.YOLO_PORT = String(cfg.port);
if (cfg.authUser) process.env.YOLO_AUTH_USER = cfg.authUser;
if (cfg.authPass) process.env.YOLO_AUTH_PASS = cfg.authPass;
if (cfg.claudeBin) process.env.YOLO_CLAUDE_BIN = cfg.claudeBin;
if (cfg.workingDir) process.env.YOLO_WORK_DIR = cfg.workingDir;
if (cfg.maxTurns) process.env.YOLO_MAX_TURNS = String(cfg.maxTurns);
if (cfg.maxConcurrent) process.env.YOLO_MAX_CONCURRENT = String(cfg.maxConcurrent);
if (cfg.timeoutMinutes) process.env.YOLO_TIMEOUT_MS = String(cfg.timeoutMinutes * 60 * 1000);
if (cfg.cooldownSec) process.env.YOLO_COOLDOWN = String(cfg.cooldownSec);
if (cfg.allowedTools) process.env.YOLO_ALLOWED_TOOLS = cfg.allowedTools.join(',');
console.log(`[YOLO-Local] Config loaded: port=${cfg.port}, claude=${cfg.claudeBin}, cwd=${cfg.workingDir}`);
}
// Now load the real server
require('./server.js');
JSEOF
chmod +x "$SCRIPT_DIR/yolo-local.js"
echo " Created: yolo-local.js (wrapper with local-config.json support)"
echo ""
echo " NOTE: server.js hardcodes constants at lines 16-28."
echo " The wrapper sets env vars, but server.js must read them."
echo " You may need to edit server.js lines 16-28 to use:"
echo ""
echo " const PORT = parseInt(process.env.YOLO_PORT) || 9670;"
echo " const CLAUDE_BIN = process.env.YOLO_CLAUDE_BIN || '/usr/bin/claude';"
echo " const MAX_TURNS = parseInt(process.env.YOLO_MAX_TURNS) || 50;"
echo " // etc."
echo ""
echo " OR just edit the constants directly in server.js."
fi
# ── Step 6: Start with PM2 ───────────────────────────────────────────────
echo ""
echo "[6/6] Starting with PM2..."
# Stop existing if running
pm2 delete yolo-agent 2>/dev/null || true
# Determine which script to run
if [ -f "$SCRIPT_DIR/yolo-local.js" ]; then
ENTRY="yolo-local.js"
else
ENTRY="server.js"
fi
pm2 start "$SCRIPT_DIR/$ENTRY" \
--name yolo-agent \
--cwd "$SCRIPT_DIR" \
--max-memory-restart 512M \
--no-autorestart
pm2 save 2>/dev/null || true
echo ""
echo "============================================"
echo " YOLO Runner (Yuri) is LIVE"
echo " Dashboard: http://localhost:$PORT/"
echo " Remote access: ssh -L $PORT:127.0.0.1:$PORT <user>@$(hostname -I | awk '{print $1}')"
echo " Auth: admin / DWSecure2024!"
echo "============================================"
echo ""
echo "Quick start:"
echo " # Add a task"
echo " echo 'Check disk space and report' > $SCRIPT_DIR/tasks/01_health.md"
echo ""
echo " # Start the loop"
echo " curl -u admin:DWSecure2024! -X POST http://localhost:$PORT/api/start"
echo ""
echo " # Quick one-off run"
echo " curl -u admin:DWSecure2024! -X POST http://localhost:$PORT/api/run \\"
echo " -H 'Content-Type: application/json' \\"
echo " -d '{\"prompt\": \"Check system health and report\"}'"
echo ""
echo " # Monitor"
echo " curl -u admin:DWSecure2024! http://localhost:$PORT/api/status"
echo ""
echo " # View logs"
echo " pm2 logs yolo-agent --lines 50"
echo ""