#!/usr/bin/env bash # bootstrap.sh — one-shot setup for the Agent0 box. # # Run once after cloning. Handles everything that can be automated: # - Ollama install + model pulls # - SSH key generation (tunnel key + Gitea key) # - gutasktool clone + install # - Docker containers # # After this script: add two SSH keys (printed at the end), then open # https://agent0.glitch.university and enter your API keys in Settings. set -euo pipefail REPO_DIR="$(cd "$(dirname "$0")" && pwd)" GUTASK_DIR="${HOME}/gutasktool" GITEA_HOST="ramanujan.glitch.university" GITEA_PORT=2222 VPS_HOST="glitch.university" VPS_TUNNEL_USER="tunnel" TUNNEL_KEY="${REPO_DIR}/tunnel/id_ed25519" GITEA_KEY="${HOME}/.ssh/gitea_ed25519" GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${GREEN}✓${NC} $*"; } prompt() { echo -e "${CYAN}?${NC} $*"; } section() { echo ""; echo -e "${YELLOW}── $* ──────────────────────────────────────────${NC}"; } # ── Prerequisites ───────────────────────────────────────────────────────────── section "Checking prerequisites" command -v docker >/dev/null 2>&1 || { echo "docker not found. Install Docker first."; exit 1; } command -v ssh-keygen >/dev/null 2>&1 || { echo "ssh-keygen not found."; exit 1; } command -v ssh-keyscan >/dev/null 2>&1 || { echo "ssh-keyscan not found."; exit 1; } docker compose version >/dev/null 2>&1 || { echo "docker compose not found."; exit 1; } if ! command -v python3 >/dev/null 2>&1; then echo "python3 not found. Install Python 3.9+ first." exit 1 fi PY_VERSION=$(python3 -c "import sys; print(sys.version_info.minor)") if [[ "$PY_VERSION" -lt 9 ]]; then echo "Python 3.9+ required (found 3.${PY_VERSION})." exit 1 fi info "Prerequisites OK" # ── Ollama ──────────────────────────────────────────────────────────────────── section "Ollama" if command -v ollama >/dev/null 2>&1; then info "Ollama already installed" else info "Installing Ollama..." curl -fsSL https://ollama.com/install.sh | sh fi # Ensure the Ollama server is running if [[ "$OS" == "Linux" ]]; then if systemctl is-active --quiet ollama 2>/dev/null; then info "Ollama service running" else info "Starting Ollama service..." sudo systemctl enable --now ollama fi else # macOS — check if server is already answering, otherwise start in background if curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then info "Ollama server already running" else info "Starting Ollama server..." ollama serve >/tmp/ollama.log 2>&1 & sleep 2 curl -sf http://localhost:11434/api/tags >/dev/null 2>&1 \ && info "Ollama server started" \ || echo " Warning: Ollama server didn't respond — start it manually with: ollama serve" fi fi echo "" echo " Default models to pull (you can skip any):" echo " [1] qwen2.5-coder:7b (~5 GB) — fast coding model" echo " [2] qwen2.5-coder:32b (~20 GB) — strong coding model" echo " [3] qwen2.5:72b (~45 GB) — general reasoning" echo " [4] deepseek-r1:70b (~43 GB) — chain-of-thought" echo "" prompt "Which models to pull? (e.g. 1 2 or Enter to skip all): " read -r MODEL_CHOICES for choice in $MODEL_CHOICES; do case "$choice" in 1) info "Pulling qwen2.5-coder:7b..."; ollama pull qwen2.5-coder:7b ;; 2) info "Pulling qwen2.5-coder:32b..."; ollama pull qwen2.5-coder:32b ;; 3) info "Pulling qwen2.5:72b..."; ollama pull qwen2.5:72b ;; 4) info "Pulling deepseek-r1:70b..."; ollama pull deepseek-r1:70b ;; esac done # ── SSH keys ────────────────────────────────────────────────────────────────── section "SSH key: glitch-tunnel → VPS" mkdir -p "${REPO_DIR}/tunnel" chmod 700 "${REPO_DIR}/tunnel" if [[ -f "$TUNNEL_KEY" ]]; then info "Tunnel key already exists" else ssh-keygen -t ed25519 -f "$TUNNEL_KEY" -C "glitch-tunnel@$(hostname)" -N "" info "Tunnel key generated at ./tunnel/id_ed25519" fi info "Scanning VPS host key..." ssh-keyscan -p 22 "$VPS_HOST" > "${REPO_DIR}/tunnel/known_hosts" 2>/dev/null \ || { echo "Could not reach ${VPS_HOST} — check network. Re-run when online."; exit 1; } section "SSH key: Gitea (git operations)" mkdir -p "${HOME}/.ssh" chmod 700 "${HOME}/.ssh" if [[ -f "$GITEA_KEY" ]]; then info "Gitea key already exists" else ssh-keygen -t ed25519 -f "$GITEA_KEY" -C "gunnar@$(hostname)" -N "" info "Gitea key generated at ${GITEA_KEY}" fi # Add Gitea to known_hosts to avoid interactive prompt during git operations if ! ssh-keygen -F "[${GITEA_HOST}]:${GITEA_PORT}" -f "${HOME}/.ssh/known_hosts" >/dev/null 2>&1; then ssh-keyscan -p "$GITEA_PORT" "$GITEA_HOST" >> "${HOME}/.ssh/known_hosts" 2>/dev/null || true fi # Write SSH config entry for Gitea if ! grep -q "Host ${GITEA_HOST}" "${HOME}/.ssh/config" 2>/dev/null; then cat >> "${HOME}/.ssh/config" << EOF Host ${GITEA_HOST} HostName ${GITEA_HOST} Port ${GITEA_PORT} User git IdentityFile ${GITEA_KEY} EOF info "SSH config entry added for ${GITEA_HOST}" fi # ── gutasktool ──────────────────────────────────────────────────────────────── section "gutasktool" if [[ -d "$GUTASK_DIR" ]]; then info "gutasktool already at ${GUTASK_DIR}" else info "Cloning gutasktool..." git clone "ssh://git@${GITEA_HOST}:${GITEA_PORT}/glitch-university/gutasktool.git" "$GUTASK_DIR" \ || git clone "https://${GITEA_HOST}/glitch-university/gutasktool.git" "$GUTASK_DIR" fi info "Installing gutasktool..." (cd "$GUTASK_DIR" && pip3 install -e . --quiet) info "gutask installed ($(gutask --version 2>/dev/null || echo 'ok'))" if [[ ! -f "${GUTASK_DIR}/.env" ]]; then echo "" prompt "Enter CONTENT_API_KEY for glitch.university (find it in the VPS .env): " read -r -s API_KEY echo "" cat > "${GUTASK_DIR}/.env" << EOF API_URL=https://glitch.university CONTENT_API_KEY=${API_KEY} GITEA_URL=https://${GITEA_HOST} GITEA_OWNER=glitch-university # AGENT_ID, AGENT_NAME, AGENT_PASSWORD, and GITEA_TOKEN are per-agent. # They are passed through each agent's thread context, not set here. EOF info "gutasktool .env created at ${GUTASK_DIR}/.env" else info "gutasktool .env already exists" fi # ── Docker containers ───────────────────────────────────────────────────────── section "Docker containers" cd "$REPO_DIR" info "Building glitch-tunnel image..." docker compose build --quiet info "Starting containers..." docker compose up -d echo "" docker compose ps # ── Manual steps summary ────────────────────────────────────────────────────── TUNNEL_PUBKEY=$(cat "${TUNNEL_KEY}.pub") GITEA_PUBKEY=$(cat "${GITEA_KEY}.pub") echo "" echo -e "${YELLOW}════════════════════════════════════════════════════════${NC}" echo -e "${YELLOW} Two manual steps remaining:${NC}" echo -e "${YELLOW}════════════════════════════════════════════════════════${NC}" echo "" echo -e "${CYAN}1. Add tunnel key to VPS${NC}" echo " Run on the VPS:" echo "" echo " echo '${TUNNEL_PUBKEY}' >> /home/tunnel/.ssh/authorized_keys" echo "" echo -e "${CYAN}2. Add Gitea key to Gitea${NC}" echo " Log into https://${GITEA_HOST} as gunnar" echo " Settings → SSH Keys → Add key:" echo "" echo " ${GITEA_PUBKEY}" echo "" echo -e "${YELLOW}════════════════════════════════════════════════════════${NC}" echo "" echo " After adding both keys:" echo " • Tunnel connects automatically (check: docker logs glitch-tunnel)" echo " • Open https://agent0.glitch.university" echo " • Enter basic auth password" echo " • Settings → add Anthropic / OpenAI API keys" echo " • Ollama is pre-configured at http://host.docker.internal:11434" echo "" echo -e "${GREEN}Bootstrap complete.${NC}"