diff --git a/README.md b/README.md index fb201073..586d4ce1 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,18 @@ Spawn agents on [Hetzner Cloud](https://www.hetzner.com/cloud/) servers. No `hcl bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/claude.sh) ``` +#### OpenClaw + +```bash +bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/openclaw.sh) +``` + +#### NanoClaw + +```bash +bash <(curl -fsSL https://openrouter.ai/lab/spawn/hetzner/nanoclaw.sh) +``` + ### Non-Interactive Mode ```bash diff --git a/hetzner/nanoclaw.sh b/hetzner/nanoclaw.sh new file mode 100755 index 00000000..dfcbe9dc --- /dev/null +++ b/hetzner/nanoclaw.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -e + +# Source common functions - try local file first, fall back to remote +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)" +if [[ -f "$SCRIPT_DIR/lib/common.sh" ]]; then + source "$SCRIPT_DIR/lib/common.sh" +else + source <(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/hetzner/lib/common.sh) +fi + +log_info "NanoClaw on Hetzner Cloud" +echo "" + +# 1. Resolve Hetzner API token +ensure_hcloud_token + +# 2. Generate + register SSH key +ensure_ssh_key + +# 3. Get server name and create server +SERVER_NAME=$(get_server_name) +create_server "$SERVER_NAME" + +# 4. Wait for SSH and cloud-init +verify_server_connectivity "$HETZNER_SERVER_IP" +wait_for_cloud_init "$HETZNER_SERVER_IP" + +# 5. Install Node.js deps and clone nanoclaw +log_warn "Installing tsx..." +run_server "$HETZNER_SERVER_IP" "source ~/.bashrc && bun install -g tsx" + +log_warn "Cloning and building nanoclaw..." +run_server "$HETZNER_SERVER_IP" "git clone https://github.com/gavrielc/nanoclaw.git ~/nanoclaw && cd ~/nanoclaw && npm install && npm run build" +log_info "NanoClaw installed" + +# 6. Get OpenRouter API key +echo "" +if [[ -n "$OPENROUTER_API_KEY" ]]; then + log_info "Using OpenRouter API key from environment" +else + OPENROUTER_API_KEY=$(get_openrouter_api_key_oauth 5180) +fi + +# 7. Inject environment variables into ~/.zshrc +log_warn "Setting up environment variables..." + +ENV_TEMP=$(mktemp) +cat > "$ENV_TEMP" << EOF + +# [spawn:env] +export OPENROUTER_API_KEY="${OPENROUTER_API_KEY}" +export ANTHROPIC_API_KEY="${OPENROUTER_API_KEY}" +export ANTHROPIC_BASE_URL="https://openrouter.ai/api" +EOF + +upload_file "$HETZNER_SERVER_IP" "$ENV_TEMP" "/tmp/env_config" +run_server "$HETZNER_SERVER_IP" "cat /tmp/env_config >> ~/.zshrc && rm /tmp/env_config" +rm "$ENV_TEMP" + +# 8. Create nanoclaw .env file +log_warn "Configuring nanoclaw..." + +DOTENV_TEMP=$(mktemp) +cat > "$DOTENV_TEMP" << EOF +ANTHROPIC_API_KEY=${OPENROUTER_API_KEY} +EOF + +upload_file "$HETZNER_SERVER_IP" "$DOTENV_TEMP" "/root/nanoclaw/.env" +rm "$DOTENV_TEMP" + +echo "" +log_info "Hetzner server setup completed successfully!" +log_info "Server: $SERVER_NAME (ID: $HETZNER_SERVER_ID, IP: $HETZNER_SERVER_IP)" +echo "" + +# 9. Start nanoclaw +log_warn "Starting nanoclaw..." +log_warn "You will need to scan a WhatsApp QR code to authenticate." +echo "" +interactive_session "$HETZNER_SERVER_IP" "cd ~/nanoclaw && source ~/.zshrc && npm run dev" diff --git a/hetzner/openclaw.sh b/hetzner/openclaw.sh new file mode 100755 index 00000000..0749443b --- /dev/null +++ b/hetzner/openclaw.sh @@ -0,0 +1,107 @@ +#!/bin/bash +set -e + +# Source common functions - try local file first, fall back to remote +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)" +if [[ -f "$SCRIPT_DIR/lib/common.sh" ]]; then + source "$SCRIPT_DIR/lib/common.sh" +else + source <(curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/hetzner/lib/common.sh) +fi + +log_info "OpenClaw on Hetzner Cloud" +echo "" + +# 1. Resolve Hetzner API token +ensure_hcloud_token + +# 2. Generate + register SSH key +ensure_ssh_key + +# 3. Get server name and create server +SERVER_NAME=$(get_server_name) +create_server "$SERVER_NAME" + +# 4. Wait for SSH and cloud-init +verify_server_connectivity "$HETZNER_SERVER_IP" +wait_for_cloud_init "$HETZNER_SERVER_IP" + +# 5. Install openclaw via bun +log_warn "Installing openclaw..." +run_server "$HETZNER_SERVER_IP" "source ~/.bashrc && bun install -g openclaw" +log_info "OpenClaw installed" + +# 6. Get OpenRouter API key +echo "" +if [[ -n "$OPENROUTER_API_KEY" ]]; then + log_info "Using OpenRouter API key from environment" +else + OPENROUTER_API_KEY=$(get_openrouter_api_key_oauth 5180) +fi + +# 7. Get model preference +echo "" +log_warn "Browse models at: https://openrouter.ai/models" +log_warn "Which model would you like to use?" +MODEL_ID=$(safe_read "Enter model ID [openrouter/auto]: ") || MODEL_ID="" +MODEL_ID="${MODEL_ID:-openrouter/auto}" + +# 8. Inject environment variables into ~/.zshrc +log_warn "Setting up environment variables..." + +ENV_TEMP=$(mktemp) +cat > "$ENV_TEMP" << EOF + +# [spawn:env] +export OPENROUTER_API_KEY="${OPENROUTER_API_KEY}" +export ANTHROPIC_API_KEY="${OPENROUTER_API_KEY}" +export ANTHROPIC_BASE_URL="https://openrouter.ai/api" +EOF + +upload_file "$HETZNER_SERVER_IP" "$ENV_TEMP" "/tmp/env_config" +run_server "$HETZNER_SERVER_IP" "cat /tmp/env_config >> ~/.zshrc && rm /tmp/env_config" +rm "$ENV_TEMP" + +# 9. Configure openclaw +log_warn "Configuring openclaw..." + +run_server "$HETZNER_SERVER_IP" "rm -rf ~/.openclaw && mkdir -p ~/.openclaw" + +# Generate a random gateway token +GATEWAY_TOKEN=$(openssl rand -hex 16) + +OPENCLAW_CONFIG_TEMP=$(mktemp) +cat > "$OPENCLAW_CONFIG_TEMP" << EOF +{ + "env": { + "OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}" + }, + "gateway": { + "mode": "local", + "auth": { + "token": "${GATEWAY_TOKEN}" + } + }, + "agents": { + "defaults": { + "model": { + "primary": "openrouter/${MODEL_ID}" + } + } + } +} +EOF + +upload_file "$HETZNER_SERVER_IP" "$OPENCLAW_CONFIG_TEMP" "/root/.openclaw/openclaw.json" +rm "$OPENCLAW_CONFIG_TEMP" + +echo "" +log_info "Hetzner server setup completed successfully!" +log_info "Server: $SERVER_NAME (ID: $HETZNER_SERVER_ID, IP: $HETZNER_SERVER_IP)" +echo "" + +# 10. Start openclaw gateway in background and launch TUI +log_warn "Starting openclaw..." +run_server "$HETZNER_SERVER_IP" "source ~/.zshrc && nohup openclaw gateway > /tmp/openclaw-gateway.log 2>&1 &" +sleep 2 +interactive_session "$HETZNER_SERVER_IP" "source ~/.zshrc && openclaw tui" diff --git a/manifest.json b/manifest.json index bf8fcea3..d293dc81 100644 --- a/manifest.json +++ b/manifest.json @@ -99,7 +99,7 @@ "sprite/openclaw": "implemented", "sprite/nanoclaw": "implemented", "hetzner/claude": "implemented", - "hetzner/openclaw": "missing", - "hetzner/nanoclaw": "missing" + "hetzner/openclaw": "implemented", + "hetzner/nanoclaw": "implemented" } }