| FROM node:22-slim |
|
|
| |
| RUN apt-get update && apt-get install -y --no-install-recommends \ |
| git openssh-client build-essential python3 python3-pip \ |
| g++ make ca-certificates \ |
| && rm -rf /var/lib/apt/lists/* |
|
|
| |
| RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages |
|
|
| |
| RUN update-ca-certificates && \ |
| git config --global http.sslVerify false && \ |
| git config --global url."https://github.com/".insteadOf ssh://git@github.com/ |
|
|
| |
| RUN npm install -g openclaw@latest --unsafe-perm |
|
|
| |
| ENV PORT=7860 \ |
| OPENCLAW_GATEWAY_MODE=local \ |
| HOME=/root |
|
|
| |
| RUN cat > /usr/local/bin/sync.py << 'EOF' |
| import os, sys, tarfile |
| from huggingface_hub import HfApi, hf_hub_download |
| from datetime import datetime, timedelta |
|
|
| api = HfApi() |
| repo_id = os.getenv("HF_DATASET") |
| token = os.getenv("HF_TOKEN") |
|
|
| def restore(): |
| try: |
| print("🔍 [Restore] Searching for latest backup...") |
| files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token) |
| now = datetime.now() |
| for i in range(7): |
| day = (now - timedelta(days=i)).strftime("%Y-%m-%d") |
| name = f"backup_{day}.tar.gz" |
| if name in files: |
| print(f"📦 [Restore] Found: {name}") |
| path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token) |
| with tarfile.open(path, "r:gz") as tar: |
| tar.extractall(path="/root/.openclaw/") |
| print(f"✅ [Restore] Success: Restored sessions + skills + scheduled tasks + agents from {name}") |
| return True |
| print("⚠️ [Restore] No recent backup found.") |
| except Exception as e: |
| print(f"❌ [Restore] Error: {e}") |
|
|
| def backup(): |
| try: |
| day = datetime.now().strftime("%Y-%m-%d") |
| name = f"backup_{day}.tar.gz" |
| print(f"📦 [Backup] Starting → {name}") |
| with tarfile.open(name, "w:gz") as tar: |
| |
| if os.path.exists("/root/.openclaw/agents"): |
| tar.add("/root/.openclaw/agents", arcname="agents") |
| session_count = len(os.listdir("/root/.openclaw/agents/main/sessions")) if os.path.exists("/root/.openclaw/agents/main/sessions") else 0 |
| print(f" → Backed up agents/sessions ({session_count} sessions)") |
| |
| if os.path.exists("/root/.openclaw/skills"): |
| tar.add("/root/.openclaw/skills", arcname="skills") |
| if os.path.exists("/root/.openclaw/workspace"): |
| tar.add("/root/.openclaw/workspace", arcname="workspace") |
| |
| if os.path.exists("/root/.openclaw/cron"): |
| tar.add("/root/.openclaw/cron", arcname="cron") |
| print(" → Backed up cron/ (scheduled tasks)") |
| if os.path.exists("/root/.openclaw/agents/main/cron"): |
| tar.add("/root/.openclaw/agents/main/cron", arcname="agents/main/cron") |
| print(" → Backed up agents/main/cron") |
| if os.path.exists("/root/.openclaw/workspace/cron"): |
| tar.add("/root/.openclaw/workspace/cron", arcname="workspace/cron") |
| print(" → Backed up workspace/cron") |
| |
| if os.path.exists("/root/.openclaw/openclaw.json"): |
| tar.add("/root/.openclaw/openclaw.json", arcname="openclaw.json") |
| if os.path.exists("/root/.openclaw/.clawhub"): |
| tar.add("/root/.openclaw/.clawhub", arcname=".clawhub") |
| api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token) |
| print(f"✅ [Backup] Success: {name} uploaded (includes scheduled tasks)") |
| except Exception as e: |
| print(f"❌ [Backup] Error: {e}") |
|
|
| if __name__ == "__main__": |
| if len(sys.argv) > 1 and sys.argv[1] == "backup": |
| backup() |
| else: |
| restore() |
| EOF |
|
|
| |
| RUN mkdir -p /root/.openclaw && \ |
| cat > /root/.openclaw/openclaw.json.template << 'EOT' |
| { |
| "models": { |
| "providers": { |
| "thirdparty": { |
| "baseUrl": "PLACEHOLDER_BASE_URL", |
| "apiKey": "PLACEHOLDER_API_KEY", |
| "api": "openai-completions", |
| "models": [ |
| { |
| "id": "PLACEHOLDER_MODEL_ID", |
| "name": "GLM-4.7-Flash", |
| "contextWindow": 131072 |
| } |
| ] |
| } |
| } |
| }, |
| "agents": { |
| "defaults": { |
| "timeoutSeconds": 300, // 整体任务超时,建议 300(5分钟)或更高 |
| "model": { |
| "primary": "thirdparty/PLACEHOLDER_MODEL_ID" |
| } |
| } |
| }, |
| "gateway": { |
| "mode": "local", |
| "bind": "lan", |
| "port": 7860, |
| "trustedProxies": ["0.0.0.0/0", "10.0.0.0/8", "10.16.0.0/12", "172.16.0.0/12", "192.168.0.0/16"], |
| "auth": { |
| "mode": "token", |
| "token": "PLACEHOLDER_GATEWAY_PASSWORD" |
| }, |
| "controlUi": { |
| "enabled": true, |
| "allowInsecureAuth": true, |
| "dangerouslyAllowHostHeaderOriginFallback": true, |
| "dangerouslyDisableDeviceAuth": true |
| } |
| } |
| } |
| EOT |
|
|
| |
| RUN cat > /usr/local/bin/start-openclaw << 'EOF' |
| |
| set -e |
|
|
| mkdir -p /root/.openclaw/sessions |
|
|
| echo "🚀 Starting OpenClaw with full persistence..." |
|
|
| |
| python3 /usr/local/bin/sync.py restore |
|
|
| |
| echo "=== Restored Sessions ===" |
| if [ -d "/root/.openclaw/agents/main/sessions" ]; then |
| count=$(ls -1 "/root/.openclaw/agents/main/sessions" 2>/dev/null | wc -l) |
| echo "agents/main/sessions: ${count} sessions" |
| else |
| echo "No agents/main/sessions yet" |
| fi |
| echo "=== Restored Scheduled Tasks (cron) ===" |
| ls -la /root/.openclaw/cron 2>/dev/null || echo "No global cron" |
| ls -la /root/.openclaw/agents/main/cron 2>/dev/null || echo "No agents/main/cron" |
| ls -la /root/.openclaw/workspace/cron 2>/dev/null || echo "No workspace/cron" |
| echo "==================================" |
|
|
| |
| echo "📤 Immediate backup after restore..." |
| python3 /usr/local/bin/sync.py backup |
|
|
| |
| CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|g') |
| sed -e "s|PLACEHOLDER_BASE_URL|$CLEAN_BASE|g" \ |
| -e "s|PLACEHOLDER_API_KEY|$OPENAI_API_KEY|g" \ |
| -e "s|PLACEHOLDER_MODEL_ID|$MODEL|g" \ |
| -e "s|PLACEHOLDER_GATEWAY_PASSWORD|$OPENCLAW_GATEWAY_PASSWORD|g" \ |
| /root/.openclaw/openclaw.json.template > /root/.openclaw/openclaw.json |
|
|
| echo "✅ OpenClaw fully started (sessions + skills + scheduled tasks restored)" |
|
|
| |
| (while true; do sleep 1200; python3 /usr/local/bin/sync.py backup; done) & |
|
|
| openclaw doctor --fix |
| exec openclaw gateway run --port $PORT |
| EOF |
|
|
| RUN chmod +x /usr/local/bin/start-openclaw |
|
|
| EXPOSE 7860 |
| CMD ["/usr/local/bin/start-openclaw"] |