FROM node:22-slim # 1. 基础依赖补全 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/* # 2. 安装 HF 数据交互工具 RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages # 3. 构建环境优化 RUN update-ca-certificates && \ git config --global http.sslVerify false && \ git config --global url."https://github.com/".insteadOf ssh://git@github.com/ # 4. OpenClaw 核心安装 RUN npm install -g openclaw@latest --unsafe-perm # 5. 环境变量预设 ENV PORT=7860 \ OPENCLAW_GATEWAY_MODE=local \ HOME=/root # 6. Python 同步引擎(已新增定时任务完整备份) 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 # 7. openclaw.json 模板(你的第三方 API 配置) 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": "gemini-3.1-pro-high", "name": "Gemini 3.1 Pro (High)", "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 # 8. 启动脚本(显示定时任务恢复情况) RUN cat > /usr/local/bin/start-openclaw << 'EOF' #!/bin/bash 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)" # 每20分钟自动备份 (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"]