diff --git a/install.sh b/install.sh index 042a7c7..739b483 100644 --- a/install.sh +++ b/install.sh @@ -1,21 +1,25 @@ #!/usr/bin/env bash # ============================================================================= -# OpenClaw All-in-One — instalación en 1 click -# ============================================= +# 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) -# Con el modelo por defecto deepseek-v4-flash-free apuntando a TU proxy. +# • 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 de entorno opcionales (override): -# MODEL modelo a usar (default: deepseek-v4-flash-free) -# PROXY_API_KEY clave del proxy (default: embebida, local-only) -# GATEWAY_PORT puerto del gateway de openclaw (default: 18789) -# GITEA_URL base de tu Gitea (default: https://gitea.cbcren.online/) -# GITEA_USER usuario de tu Gitea (default: renato97) +# Con Telegram: +# curl -fsSL https://gitea.cbcren.online/renato97/openclaw-allinone/raw/branch/main/install.sh | \ +# TELEGRAM_BOT_TOKEN=123:abc TELEGRAM_USER_ID=123456789 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 # ============================================================================= set -euo pipefail @@ -33,10 +37,10 @@ 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:-}" +TELEGRAM_USER_ID="${TELEGRAM_USER_ID:-}" -# Normalizar GITEA_URL (sin barra final) GITEA_URL="${GITEA_URL%/}" - export PATH="$HOME/.local/bin:$HOME/.openclaw/bin:/usr/local/bin:$PATH" # ---------------- Detectar OS / arch ---------------- @@ -51,7 +55,7 @@ case "$(uname -m)" in aarch64|arm64) ARCH=arm64 ;; *) ARCH=source ;; esac -log "OS=$OS arch=$(uname -m) modelo=$MODEL gateway=:$GATEWAY_PORT proxy=:$PROXY_PORT" +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 @@ -64,6 +68,7 @@ else 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 ---------------- @@ -83,14 +88,13 @@ if [ "$PROXY_OK" -eq 0 ]; then 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 con el binario precompilado." + 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 - # systemd unit a nivel usuario (no requiere root) mkdir -p "$HOME/.config/systemd/user" cat > "$HOME/.config/systemd/user/free-ide-proxy.service" </dev/null || true - log "Habilitando linger para arrancar los 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 (los servicios de usuario no arrancarán solos al boot)" + 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} (${MODEL} y más)" + log "free-ide-proxy activo en http://127.0.0.1:${PROXY_PORT}" fi -# ---------------- 3) Config de openclaw (pre-cargada) ---------------- -# Reutiliza el token del gateway si ya existía una config (idempotencia). -GATEWAY_TOKEN="" -if [ -f "$HOME/.openclaw/openclaw.json" ]; then - GATEWAY_TOKEN="$(grep -o '"token"[[:space:]]*:[[:space:]]*"[^"]*"' "$HOME/.openclaw/openclaw.json" | head -1 | sed -E 's/.*:"([^"]*)"/\1/')" - cp "$HOME/.openclaw/openclaw.json" "$HOME/.openclaw/openclaw.json.bak.$(date +%s)" - log "Backup de config existente guardado en ~/.openclaw/openclaw.json.bak.*" +# ---------------- 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 -GATEWAY_TOKEN="${GATEWAY_TOKEN:-$(openssl rand -hex 24 2>/dev/null || cat /dev/urandom | tr -dc 'a-f0-9' | head -c 48)}" mkdir -p "$HOME/.openclaw/workspace" -cat > "$HOME/.openclaw/openclaw.json" < { 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'; } -JSON +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 en ~/.openclaw/openclaw.json (modelo: free-ide/$MODEL)" +log "Config escrita (con backup en ~/.openclaw/openclaw.json.bak.*)" # ---------------- 4) Gateway daemon ---------------- export XDG_RUNTIME_DIR="/run/user/$(id -u)" @@ -188,7 +234,7 @@ log "Instalando daemon del gateway (systemd usuario)..." 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 nueva..." + 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 || { @@ -197,19 +243,18 @@ else systemctl --user start openclaw-gateway.service >/dev/null 2>&1 || true } fi -sleep 4 +sleep 5 # ---------------- 5) Verificación ---------------- log "Verificando proxy..." curl -fsS "http://127.0.0.1:${PROXY_PORT}/health" && echo -log "Verificando gateway ($GATEWAY_PORT)..." -(ss -tln 2>/dev/null || netstat -tln 2>/dev/null) | grep -q ":$GATEWAY_PORT " && echo "gateway escuchando en :$GATEWAY_PORT" || warn "no se detectó el puerto $GATEWAY_PORT (revisa: systemctl --user status openclaw-gateway)" +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 (systemctl --user status openclaw-gateway)" -if timeout 60 "$OPENCLAW_BIN" agent -m "Responde SOLO con: OK" --agent main >/dev/null 2>&1; then - log "Inferencia verificada: el agente respondió OK con $MODEL." -else - warn "La verificación de inferencia no respondió en 60s. El gateway puede estar calentando; probá luego:" - warn " openclaw agent -m 'hola' --agent main" +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + log "Canal Telegram configurado. Estado:" + timeout 30 "$OPENCLAW_BIN" channels status 2>&1 | grep -iE 'telegram|channel|connected|error' | head -15 || true fi echo @@ -218,9 +263,10 @@ 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}" -echo " Servicios : free-ide-proxy + openclaw-gateway (systemd usuario)" +if [ -n "$TELEGRAM_BOT_TOKEN" ]; then + echo " Telegram : bot conectado (DM, dmPolicy=allowlist, owner=$TELEGRAM_USER_ID)" +fi echo "----------------------------------------------------------------------------" -echo " Chat: openclaw chat" -echo " Status: openclaw gateway status" -echo " Panel: openclaw dashboard" +echo " Chat: openclaw chat | Status: openclaw gateway status" +echo " Panel: openclaw dashboard | Canales: openclaw channels status" echo "=============================================================================="