#!/usr/bin/env bash # ============================================================================= # OpenClaw All-in-One — instalación en 1 click (v2, idempotente) # ============================================================================= # Instala y pre-configura: # • openclaw (CLI + Gateway daemon, systemd usuario) # • free-ide-proxy (endpoint OpenAI-compatible local en 127.0.0.1:6446) # • Canal Telegram (opcional, si pasás TELEGRAM_BOT_TOKEN + TELEGRAM_USER_ID) # Con tu modelo apuntando a TU proxy. Reescribir la config es SEGURO: # usa node (JSON.parse/stringify), hace backup, y se auto-repara si el # openclaw.json previo está corrupto (cae al .last-good). # # Uso (Linux/macOS): # curl -fsSL https://gitea.cbcren.online/renato97/openclaw-allinone/raw/branch/main/install.sh | bash # # Variables (override): # MODEL, PROXY_API_KEY, GATEWAY_PORT, PROXY_PORT, GITEA_URL, GITEA_USER, # PROXY_REPO, PROXY_VERSION, TELEGRAM_BOT_TOKEN, TELEGRAM_USER_ID # Telegram viene configurado por defecto (@rvfinanzas_bot, owner 692714536). # Para correr sin Telegram: TELEGRAM_BOT_TOKEN= TELEGRAM_USER_ID= bash install.sh # ============================================================================= set -euo pipefail log() { echo -e "\033[1;34m[openclaw]\033[0m $*"; } warn() { echo -e "\033[1;33m[openclaw]\033[0m $*"; } die() { echo -e "\033[1;31m[openclaw]\033[0m ERROR: $*" >&2; exit 1; } # ---------------- Configurables (env override) ---------------- MODEL="${MODEL:-deepseek-v4-flash-free}" PROXY_API_KEY="${PROXY_API_KEY:-tw0rxo314kvga9lfbp2nj5sde7hiq6uc}" GATEWAY_PORT="${GATEWAY_PORT:-18789}" PROXY_PORT="${PROXY_PORT:-6446}" GITEA_URL="${GITEA_URL:-https://gitea.cbcren.online}" GITEA_USER="${GITEA_USER:-renato97}" PROXY_REPO="${PROXY_REPO:-free-ide-proxy}" PROXY_VERSION="${PROXY_VERSION:-v1.0.0}" OPENCLAW_VER="${OPENCLAW_VER:-2026.7.1-2}" TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN-8501699617:AAHe5T8ZfLobkYO5TfCbNPPXUX8LN9tjD8g}" TELEGRAM_USER_ID="${TELEGRAM_USER_ID-692714536}" GITEA_URL="${GITEA_URL%/}" export PATH="$HOME/.local/bin:$HOME/.openclaw/bin:/usr/local/bin:$PATH" # ---------------- Detectar OS / arch ---------------- OS="$(uname -s)" case "$OS" in Linux) OS=linux ;; Darwin) OS=darwin ;; *) die "Sistema operativo no soportado: $OS (se requiere Linux o macOS)" ;; esac case "$(uname -m)" in x86_64|amd64) ARCH=amd64 ;; aarch64|arm64) ARCH=arm64 ;; *) ARCH=source ;; esac log "OS=$OS arch=$(uname -m) modelo=$MODEL gateway=:$GATEWAY_PORT proxy=:$PROXY_PORT telegram=$([ -n "$TELEGRAM_BOT_TOKEN" ] && echo 'si' || echo 'no')" # ---------------- 1) openclaw ---------------- if command -v openclaw >/dev/null 2>&1; then log "openclaw ya instalado: $(openclaw --version 2>&1 | head -1)" elif command -v npm >/dev/null 2>&1 && [ "$(node --version 2>/dev/null | sed -E 's/[^0-9.]//g' | cut -d. -f1)" -ge 22 ]; then log "Instalando openclaw via npm (node >=22)..." npm install -g "openclaw@${OPENCLAW_VER}" else log "Instalando openclaw (installer oficial, provee Node si hace falta)..." curl -fsSL https://openclaw.ai/install.sh | bash fi command -v openclaw >/dev/null 2>&1 || die "openclaw no quedó disponible en PATH" command -v node >/dev/null 2>&1 || die "node no está disponible" OPENCLAW_BIN="$(command -v openclaw)" # ---------------- 2) free-ide-proxy ---------------- PROXY_OK=0 if curl -fsS "http://127.0.0.1:${PROXY_PORT}/health" >/dev/null 2>&1; then log "free-ide-proxy ya responde en :${PROXY_PORT} — se omite su instalación" PROXY_OK=1 fi if [ "$PROXY_OK" -eq 0 ]; then PROXY_BIN="$HOME/.local/bin/free-ide-proxy" mkdir -p "$HOME/.local/bin" if [ "$OS" = "linux" ] && [ "$ARCH" = "amd64" ]; then ASSET="free-ide-proxy-linux-amd64" log "Descargando free-ide-proxy ${PROXY_VERSION} (${ASSET}) desde tu Gitea..." curl -fsSL "${GITEA_URL}/${GITEA_USER}/${PROXY_REPO}/releases/download/${PROXY_VERSION}/${ASSET}" -o "$PROXY_BIN" chmod +x "$PROXY_BIN" else warn "No hay binario precompilado para $OS/$ARCH — compilando desde fuente (requiere Go 1.22+)..." command -v go >/dev/null 2>&1 || die "Go no está instalado. Instala Go 1.22+ o usa Linux/amd64." TMP="$(mktemp -d)" git clone --depth 1 "${GITEA_URL}/${GITEA_USER}/${PROXY_REPO}.git" "$TMP/proxy" (cd "$TMP/proxy" && go build -o "$PROXY_BIN" .) rm -rf "$TMP" fi mkdir -p "$HOME/.config/systemd/user" cat > "$HOME/.config/systemd/user/free-ide-proxy.service" </dev/null || true log "Habilitando linger (servicios de usuario al boot)..." (sudo loginctl enable-linger "$USER" 2>/dev/null || loginctl enable-linger "$USER" 2>/dev/null) || warn "no se pudo enable-linger" systemctl --user daemon-reload systemctl --user enable --now free-ide-proxy.service sleep 2 curl -fsS "http://127.0.0.1:${PROXY_PORT}/health" >/dev/null 2>&1 || die "free-ide-proxy no quedó arriba en :${PROXY_PORT}" log "free-ide-proxy activo en http://127.0.0.1:${PROXY_PORT}" fi # ---------------- 3) Config de openclaw (merge SEGURO via node) ---------------- # Si otro servicio (ej. un bot viejo) usa el mismo bot de Telegram, lo detiene # para evitar conflicto de long-polling. if [ -n "$TELEGRAM_BOT_TOKEN" ] && systemctl list-unit-files --type=service 2>/dev/null | grep -q 'vps-bot.service'; then log "Deteniendo vps-bot.service (usaría el mismo bot de Telegram)..." sudo systemctl stop vps-bot.service 2>/dev/null || true sudo systemctl disable vps-bot.service 2>/dev/null || true fi mkdir -p "$HOME/.openclaw/workspace" export OPENCLAW_MODEL="$MODEL" export OPENCLAW_PROXY_KEY="$PROXY_API_KEY" export OPENCLAW_PROXY_URL="http://127.0.0.1:${PROXY_PORT}/v1" export OPENCLAW_GATEWAY_PORT="$GATEWAY_PORT" export OPENCLAW_TG_TOKEN="$TELEGRAM_BOT_TOKEN" export OPENCLAW_TG_USER="$TELEGRAM_USER_ID" node - "$HOME/.openclaw" <<'NODE' const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const dir = process.argv[2]; const cfgPath = path.join(dir, 'openclaw.json'); const lastGood = path.join(dir, 'openclaw.json.last-good'); const readSafe = (p) => { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; } }; let cfg = readSafe(cfgPath); let base = cfg ? 'existing' : 'none'; if (!cfg) { if (fs.existsSync(cfgPath)) fs.copyFileSync(cfgPath, cfgPath + '.corrupt.' + Date.now()); cfg = readSafe(lastGood) || {}; base = readSafe(lastGood) ? 'last-good' : 'fresh'; } if (fs.existsSync(cfgPath)) fs.copyFileSync(cfgPath, cfgPath + '.bak.' + Date.now()); const model = process.env.OPENCLAW_MODEL || 'deepseek-v4-flash-free'; const tgToken = process.env.OPENCLAW_TG_TOKEN || ''; const tgUser = process.env.OPENCLAW_TG_USER || ''; cfg.agents = cfg.agents || {}; cfg.agents.defaults = cfg.agents.defaults || {}; cfg.agents.defaults.workspace = path.join(dir, 'workspace'); cfg.agents.defaults.skipBootstrap = true; cfg.agents.defaults.model = { primary: 'free-ide/' + model }; cfg.agents.defaults.models = { ['free-ide/' + model]: {} }; cfg.gateway = cfg.gateway || {}; cfg.gateway.mode = 'local'; cfg.gateway.auth = cfg.gateway.auth || {}; cfg.gateway.auth.mode = 'token'; if (!cfg.gateway.auth.token) cfg.gateway.auth.token = crypto.randomBytes(24).toString('hex'); cfg.gateway.port = parseInt(process.env.OPENCLAW_GATEWAY_PORT || '18789', 10); cfg.gateway.bind = 'loopback'; cfg.gateway.tailscale = { mode: 'off', resetOnExit: false }; cfg.session = cfg.session || { dmScope: 'per-channel-peer' }; cfg.tools = cfg.tools || { profile: 'coding' }; cfg.models = cfg.models || {}; cfg.models.mode = 'merge'; cfg.models.providers = cfg.models.providers || {}; cfg.models.providers['free-ide'] = { baseUrl: process.env.OPENCLAW_PROXY_URL, api: 'openai-completions', apiKey: process.env.OPENCLAW_PROXY_KEY, models: [{ id: model, name: model + ' (Custom Provider)', contextWindow: 128000, maxTokens: 4096, input: ['text'], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, reasoning: false }] }; if (tgToken) { cfg.channels = cfg.channels || {}; cfg.channels.telegram = { enabled: true, botToken: tgToken, dmPolicy: 'allowlist', allowFrom: tgUser ? [tgUser] : [] }; cfg.commands = cfg.commands || {}; cfg.commands.ownerAllowFrom = [...new Set([...(cfg.commands.ownerAllowFrom || []), ...(tgUser ? ['telegram:' + tgUser] : [])])]; } const tmp = cfgPath + '.tmp'; fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2), { mode: 0o600 }); fs.chmodSync(tmp, 0o600); fs.renameSync(tmp, cfgPath); fs.writeFileSync(lastGood, JSON.stringify(cfg, null, 2)); console.log('config OK (base=' + base + ') modelo=free-ide/' + model + ' telegram=' + (tgToken ? 'si' : 'no')); NODE chmod 600 "$HOME/.openclaw/openclaw.json" log "Config escrita (con backup en ~/.openclaw/openclaw.json.bak.*)" # ---------------- 4) Gateway daemon ---------------- export XDG_RUNTIME_DIR="/run/user/$(id -u)" export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus" mkdir -p "$XDG_RUNTIME_DIR" 2>/dev/null || true log "Instalando daemon del gateway (systemd usuario)..." "$OPENCLAW_BIN" daemon install --runtime node >/dev/null 2>&1 || warn "daemon install avisó algo; se intenta igual" systemctl --user daemon-reload 2>/dev/null || true systemctl --user enable openclaw-gateway.service >/dev/null 2>&1 || true if systemctl --user is-active --quiet openclaw-gateway.service 2>/dev/null; then log "Reiniciando gateway para tomar la config..." systemctl --user restart openclaw-gateway.service >/dev/null 2>&1 || true else systemctl --user start openclaw-gateway.service >/dev/null 2>&1 || { warn "El servicio openclaw-gateway no arrancó al vuelo; reintentando..." sleep 2 systemctl --user start openclaw-gateway.service >/dev/null 2>&1 || true } fi sleep 5 # ---------------- 5) Verificación ---------------- log "Verificando proxy..." curl -fsS "http://127.0.0.1:${PROXY_PORT}/health" && echo log "Estado del gateway..." systemctl --user is-active openclaw-gateway.service 2>&1 (ss -tln 2>/dev/null || netstat -tln 2>/dev/null) | grep -q ":${GATEWAY_PORT}" && echo "gateway escuchando en :${GATEWAY_PORT}" || warn "no se detectó :${GATEWAY_PORT} (mirar: systemctl --user status openclaw-gateway)" if [ -n "$TELEGRAM_BOT_TOKEN" ]; then log "Canal Telegram configurado. Chequeo no bloqueante (max 20s):" timeout 20 "$OPENCLAW_BIN" channels status 2>&1 | grep -iE 'telegram|channel|connected|error|ready' | head -15 || warn "channels status no respondió en 20s (no bloqueante — el canal se conecta en background)" fi echo echo "==============================================================================" echo " ✅ OpenClaw listo" echo " Modelo : $MODEL" echo " Endpoint : http://127.0.0.1:${PROXY_PORT}/v1" echo " Gateway : ws://127.0.0.1:${GATEWAY_PORT}" if [ -n "$TELEGRAM_BOT_TOKEN" ]; then echo " Telegram : bot conectado (DM, dmPolicy=allowlist, owner=$TELEGRAM_USER_ID)" fi echo "----------------------------------------------------------------------------" echo " Chat: openclaw chat | Status: openclaw gateway status" echo " Panel: openclaw dashboard | Canales: openclaw channels status" echo "=============================================================================="