# 核心镜像:使用 node-slim 保持轻量 FROM node:22-slim # 1. 整合系统依赖安装 RUN apt-get update && apt-get install -y --no-install-recommends \ git python3 python3-pip ca-certificates procps tzdata \ build-essential curl \ # 依赖 chromium fonts-noto-cjk-extra fonts-noto-color-emoji \ && rm -rf /var/lib/apt/lists/* # 2. 安装 Python 依赖 RUN pip3 install --no-cache-dir huggingface_hub scrapling curl_cffi browserforge html2text ddgs --break-system-packages # 3. 安装 OpenClaw RUN npm install -g openclaw@latest @marvae24/weibo-cli --registry=https://registry.npmjs.org/ \ --unsafe-perm=true --foreground-scripts && npm cache clean --force # 5. 环境变量预设 (✅ 新增 OPENCLAW_SKIP_CANVAS_HOST=1 禁用画板防卡顿) ENV TZ=Asia/Shanghai \ PORT=7860 \ HOME=/root \ OPENCLAW_TRUST_LOCAL_WS=1 \ OPENCLAW_SECURITY_STRICT=false \ NODE_TLS_REJECT_UNAUTHORIZED=0 \ OPENCLAW_TRUST_PROXY=true \ NODE_ENV=production \ OPENCLAW_SKIP_CANVAS_HOST=1 # 6. 同步引擎 (✅ 优化:去掉了对 MODEL 变量的自动覆盖) RUN echo 'import os, sys, tarfile, time\n\ from huggingface_hub import HfApi, hf_hub_download\n\ from datetime import datetime, timedelta\n\ api = HfApi()\n\ repo_id = os.getenv("HF_DATASET")\n\ token = os.getenv("HF_TOKEN")\n\ base_dir = "/root"\n\ \n\ def restore():\n\ if not repo_id or not token: return\n\ try:\n\ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\ now = datetime.now()\n\ for i in range(5):\n\ day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\ name = "backup_" + day + ".tar.gz"\n\ if name in files:\n\ path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)\n\ with tarfile.open(path, "r:gz") as tar: tar.extractall(path=base_dir)\n\ print("--- [Sync] ✅ 恢复成功: " + day + " ---")\n\ return True\n\ except Exception as e: print("--- [Sync] ❌ 恢复失败: " + str(e))\n\ \n\ def backup():\n\ if not repo_id or not token: return\n\ try:\n\ try:\n\ files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\ cutoff = datetime.now() - timedelta(days=14)\n\ for f in files:\n\ if f.startswith("backup_") and f.endswith(".tar.gz"):\n\ try:\n\ if datetime.strptime(f[7:17], "%Y-%m-%d") < cutoff:\n\ api.delete_file(path_in_repo=f, repo_id=repo_id, repo_type="dataset", token=token)\n\ print("--- [Sync] 🗑️ 已清理过期备份: " + f + " ---")\n\ except: pass\n\ except Exception as e: print("--- [Sync] ⚠️ 清理检查失败: " + str(e))\n\ \n\ target_dir = "/root/.openclaw"\n\ if not os.path.exists(target_dir): return\n\ day_str = datetime.now().strftime("%Y-%m-%d")\n\ name = "backup_" + day_str + ".tar.gz"\n\ \n\ def exclude_modules(tarinfo):\n\ if "node_modules" in tarinfo.name: return None\n\ return tarinfo\n\ \n\ with tarfile.open(name, "w:gz") as tar: tar.add(target_dir, arcname=".openclaw", filter=exclude_modules)\n\ api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\ if os.path.exists(name): os.remove(name)\n\ print("--- [Sync] ✨ 备份完成 (已跳过依赖): " + name + " ---")\n\ except Exception as e: print("--- [Sync] ❌ 备份失败: " + str(e))\n\ \n\ def update_env():\n\ import json\n\ path = "/root/.openclaw/openclaw.json"\n\ if not os.path.exists(path): return\n\ try:\n\ with open(path, "r") as f: c = json.load(f)\n\ b = os.getenv("OPENAI_API_BASE", "").replace("/chat/completions", "").replace("/v1/", "/v1")\n\ k = os.getenv("OPENAI_API_KEY", "")\n\ p = os.getenv("OPENCLAW_GATEWAY_PASSWORD", "")\n\ if "models" in c and "providers" in c["models"] and "custom_provider" in c["models"]["providers"]:\n\ if b: c["models"]["providers"]["custom_provider"]["baseUrl"] = b\n\ if k: c["models"]["providers"]["custom_provider"]["apiKey"] = k\n\ if "gateway" in c and "auth" in c["gateway"]:\n\ if p: c["gateway"]["auth"]["token"] = p\n\ with open(path, "w") as f: json.dump(c, f, indent=2)\n\ print("--- [Sync] 🔄 环境变量已热更新至配置文件 ---")\n\ except Exception as e: print("--- [Sync] ⚠️ 配置文件热更新失败: " + str(e))\n\ \n\ if __name__ == "__main__":\n\ if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\ elif len(sys.argv) > 1 and sys.argv[1] == "update": update_env()\n\ else: restore()' > /usr/local/bin/sync.py # 7. 最终启动脚本 RUN echo "#!/bin/bash\n\ set -e\n\ mkdir -p /root/.openclaw\n\ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime\n\ \n\ python3 /usr/local/bin/sync.py restore\n\ \n\ if ls /root/.openclaw/extensions/*/package.json 1> /dev/null 2>&1; then\n\ for pkg in /root/.openclaw/extensions/*/package.json; do\n\ ext_dir=\$(dirname \"\$pkg\")\n\ echo \"--- [System] 📦 正在重建插件依赖: \$(basename \"\$ext_dir\") ---\"\n\ npm install --prefix \"\$ext_dir\" --production --no-audit --no-fund\n\ done\n\ fi\n\ \n\ find /root/.openclaw -name \"*.lock\" -delete\n\ chmod 700 /root/.openclaw\n\ \n\ if [ ! -f /root/.openclaw/openclaw.json ]; then\n\ echo \"--- [System] 📝 初次运行,生成默认配置... ---\"\n\ CLEAN_BASE=\$(echo \"\$OPENAI_API_BASE\" | sed \"s|/chat/completions||g\" | sed \"s|/v1/|/v1|g\")\n\ cat > /root/.openclaw/openclaw.json < /usr/local/bin/start-openclaw && chmod +x /usr/local/bin/start-openclaw EXPOSE 7860 CMD ["/usr/local/bin/start-openclaw"]