273 lines
12 KiB
Bash
273 lines
12 KiB
Bash
#!/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
|
|
#
|
|
# 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
|
|
|
|
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:-}"
|
|
TELEGRAM_USER_ID="${TELEGRAM_USER_ID:-}"
|
|
|
|
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" <<UNIT
|
|
[Unit]
|
|
Description=Free IDE Proxy (OpenAI/Anthropic free models aggregator)
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=$HOME/.local/bin/free-ide-proxy -host 127.0.0.1 -port $PROXY_PORT
|
|
Environment=OPENCODE_PROXY_KEY=$PROXY_API_KEY
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
UNIT
|
|
|
|
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 "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 "=============================================================================="
|