| #!/usr/bin/env bash |
| set -euo pipefail |
|
|
| export PROXY_PORT="${PROXY_PORT:-7860}" |
| export OPENCLAW_PORT="${OPENCLAW_PORT:-8080}" |
| export PORT="${OPENCLAW_PORT}" |
| export HOST="0.0.0.0" |
| export OPENCLAW_HOST="0.0.0.0" |
| export OPENCLAW_PORT |
| export SYNC_INTERVAL="${SYNC_INTERVAL:-300}" |
| export PLAYWRIGHT_BROWSERS_PATH="${PLAYWRIGHT_BROWSERS_PATH:-/ms-playwright}" |
| export TELEGRAM_API_ROOT="${TELEGRAM_API_ROOT:-https://tg-relay.markdevil11.workers.dev}" |
| export OPENCLAW_TELEGRAM_API_ROOT="${OPENCLAW_TELEGRAM_API_ROOT:-${TELEGRAM_API_ROOT}}" |
|
|
| configure_dns() { |
| if [ -w /etc/resolv.conf ]; then |
| cat > /etc/resolv.conf <<'EOF' |
| nameserver 1.1.1.1 |
| nameserver 1.0.0.1 |
| nameserver 8.8.8.8 |
| nameserver 8.8.4.4 |
| options timeout:2 attempts:3 rotate |
| EOF |
| echo "DNS configured with Cloudflare and Google resolvers." |
| else |
| echo "WARN: /etc/resolv.conf is not writable; keeping existing DNS." |
| fi |
| } |
|
|
| mkdir -p /root/workspace |
| configure_dns |
|
|
| /app/sync-root-data.sh restore |
| mkdir -p /root/.openclaw |
|
|
| generate_openclaw_config() { |
| local clean_base |
| clean_base="$(printf '%s' "${OPENAI_API_BASE:-}" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|g')" |
| local allow_id |
| allow_id="${TELEGRAM_ALLOW_ID:-0}" |
|
|
| cat > /root/.openclaw/openclaw.json <<JSONEOF |
| { |
| "models": { |
| "providers": { |
| "kilo_gateway": { |
| "baseUrl": "https://api.kilo.ai/api/gateway", |
| "apiKey": "anonymous", |
| "api": "openai-completions", |
| "models": [ |
| { "id": "kilo-auto/free", "name": "Kilo Auto Free", "contextWindow": 128000, "maxTokens": 16000 } |
| ] |
| }, |
| "nvidia": { |
| "baseUrl": "${clean_base}", |
| "apiKey": "${OPENAI_API_KEY:-}", |
| "api": "openai-completions", |
| "models": [ |
| { "id": "${MODEL:-gpt-5.5}", "name": "${MODEL:-gpt-5.5}", "contextWindow": 128000 }, |
| { "id": "${VISION_MODEL:-${MODEL:-gpt-5.5}}", "name": "Nvidia Vision", "contextWindow": 128000, "input": ["text", "image"] } |
| ] |
| } |
| } |
| }, |
| "agents": { |
| "defaults": { |
| "model": { |
| "primary": "nvidia/${MODEL:-gpt-5.5}", |
| "fallbacks": ["kilo_gateway/kilo-auto/free"] |
| }, |
| "imageModel": { |
| "primary": "nvidia/${VISION_MODEL:-${MODEL:-gpt-5.5}}" |
| } |
| } |
| }, |
| "commands": { "restart": true }, |
| "browser": { |
| "enabled": true, |
| "headless": true, |
| "noSandbox": true, |
| "defaultProfile": "openclaw", |
| "ssrfPolicy": { "dangerouslyAllowPrivateNetwork": true }, |
| "profiles": { |
| "openclaw": { |
| "cdpPort": 18800, |
| "color": "0088FF" |
| } |
| } |
| }, |
| "channels": { |
| "telegram": { |
| "enabled": true, |
| "botToken": "${TELEGRAM_BOT_TOKEN:-}", |
| "dmPolicy": "allowlist", |
| "allowFrom": [${allow_id}], |
| "apiRoot": "${TELEGRAM_API_ROOT}", |
| "webhookUrl": "https://elysiadev11-openclaw.hf.space/tg-webhook", |
| "webhookSecret": "${OPENCLAW_GATEWAY_PASSWORD:-}", |
| "webhookPath": "/tg-webhook", |
| "webhookHost": "0.0.0.0", |
| "webhookPort": 8787, |
| "streaming": { |
| "mode": "partial" |
| } |
| } |
| }, |
| "gateway": { |
| "mode": "local", |
| "bind": "lan", |
| "port": ${OPENCLAW_PORT}, |
| "trustedProxies": ["0.0.0.0/0"], |
| "auth": { "mode": "token", "token": "${OPENCLAW_GATEWAY_PASSWORD:-}" }, |
| "http": { |
| "endpoints": { |
| "chatCompletions": { "enabled": true } |
| } |
| }, |
| "controlUi": { |
| "enabled": true, |
| "allowInsecureAuth": true, |
| "dangerouslyDisableDeviceAuth": true, |
| "dangerouslyAllowHostHeaderOriginFallback": true |
| } |
| } |
| } |
| JSONEOF |
| } |
|
|
| config_is_complete() { |
| python3 - <<'PY' |
| import json |
| from pathlib import Path |
|
|
| path = Path('/root/.openclaw/openclaw.json') |
| if not path.exists(): |
| raise SystemExit(1) |
|
|
| try: |
| data = json.loads(path.read_text()) |
| except Exception: |
| raise SystemExit(1) |
|
|
| required_paths = [ |
| ('models', 'providers'), |
| ('models', 'providers', 'kilo_gateway'), |
| ('models', 'providers', 'nvidia'), |
| ('agents', 'defaults'), |
| ('browser',), |
| ('gateway',), |
| ('gateway', 'controlUi'), |
| ('channels', 'telegram'), |
| ] |
|
|
| for parts in required_paths: |
| cur = data |
| for part in parts: |
| if not isinstance(cur, dict) or part not in cur: |
| raise SystemExit(1) |
| cur = cur[part] |
|
|
| raise SystemExit(0) |
| PY |
| } |
|
|
| if ! config_is_complete; then |
| echo "Generating full /root/.openclaw/openclaw.json before OpenClaw startup..." |
| generate_openclaw_config |
| else |
| echo "Complete /root/.openclaw/openclaw.json found; keeping it." |
| fi |
|
|
| python3 - <<'PY' |
| import json |
| import os |
| from pathlib import Path |
|
|
| path = Path('/root/.openclaw/openclaw.json') |
| api_root = os.environ.get('TELEGRAM_API_ROOT', 'https://tg-relay.markdevil11.workers.dev') |
| if not path.exists(): |
| raise SystemExit(0) |
|
|
| try: |
| data = json.loads(path.read_text()) |
| except Exception as exc: |
| print(f'WARN: cannot update Telegram apiRoot in openclaw.json: {exc}') |
| raise SystemExit(0) |
|
|
| channels = data.setdefault('channels', {}) |
| telegram = channels.setdefault('telegram', {}) |
| old = telegram.get('apiRoot') |
| browser = data.get('browser') |
| if isinstance(browser, dict): |
| browser.pop('requirePairing', None) |
| agents = data.get('agents') |
| defaults = agents.get('defaults') if isinstance(agents, dict) else None |
| if isinstance(defaults, dict): |
| defaults.pop('llm', None) |
| if old != api_root: |
| telegram['apiRoot'] = api_root |
| print(f'Updated Telegram apiRoot: {old!r} -> {api_root!r}') |
| else: |
| print(f'Telegram apiRoot already set to {api_root}') |
| path.write_text(json.dumps(data, indent=2) + '\n') |
| PY |
|
|
| /app/sync-root-data.sh reconcile |
|
|
| /app/sync-root-data.sh loop & |
|
|
| run_openclaw() { |
| while true; do |
| echo "Starting OpenClaw on 127.0.0.1:${OPENCLAW_PORT}..." |
| if [ -n "${OPENCLAW_CMD:-}" ]; then |
| sh -lc "$OPENCLAW_CMD" |
| else |
| openclaw gateway --port "${OPENCLAW_PORT}" --allow-unconfigured |
| fi |
| echo "OpenClaw exited; restarting in 2 seconds..." |
| sleep 2 |
| done |
| } |
|
|
| run_nginx() { |
| while true; do |
| envsubst '${PROXY_PORT} ${OPENCLAW_PORT}' < /app/nginx.conf.template > /tmp/nginx.conf |
| nginx -c /tmp/nginx.conf -g 'daemon off;' |
| echo "Nginx exited; restarting in 2 seconds..." |
| sleep 2 |
| done |
| } |
|
|
| run_openclaw & |
| run_nginx & |
|
|
| wait -n |
| exit 1 |
|
|