# 核心镜像选择 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 \ sqlite3 \ unzip \ curl \ vim \ rclone \ ca-certificates \ chromium \ libnss3 libnspr4 \ libatk1.0-0 libatk-bridge2.0-0 \ libcups2 \ libdrm2 \ libxkbcommon0 \ libxcomposite1 libxdamage1 libxext6 libxfixes3 libxrandr2 \ libgbm1 \ libpango-1.0-0 libcairo2 \ libasound2 \ fonts-noto-cjk fonts-noto-color-emoji \ && rm -rf /var/lib/apt/lists/* # 2. 安装 HF 数据交互工具 RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages # 2) 固定 Playwright 版本,并把浏览器缓存写入镜像层 ARG PLAYWRIGHT_VERSION=1.50.1 ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright ENV CHROME_PATH=/usr/bin/chromium WORKDIR /app # 3. 构建环境与 Git 协议优化 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 # 4.1 安装qmd 插件 # 安装 Bun(官方推荐方式) RUN curl -fsSL https://bun.sh/install | bash # 添加 Bun 到 PATH(关键步骤!) ENV BUN_INSTALL="/root/.bun" ENV PATH="${BUN_INSTALL}/bin:${PATH}" # 验证 Bun 安装 RUN bun --version # Install uv for running Python scripts RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ mv /root/.local/bin/uv /usr/local/bin/uv && \ mv /root/.local/bin/uvx /usr/local/bin/uvx 2>/dev/null || true # 5. 环境变量预设 ENV PORT=7860 \ OPENCLAW_GATEWAY_MODE=local \ HOME=/root \ OPENCLAW_GATEWAY_CONTROLUI_ALLOWINSECUREAUTH=true \ OPENCLAW_GATEWAY_TRUSTED_PROXIES="0.0.0.0/0,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,127.0.0.1/8" # 如果 /app 没有 package.json,补一个最小的 RUN test -f package.json || (printf '{\n "name": "openclaw-app",\n "private": true\n}\n' > package.json) # 3) 关键:不用 npm 安装 Playwright(会触发 npm 的奇怪报错),改用 pnpm # 4) 关键:/app 在这个镜像里被当成 workspace root,所以 pnpm add 需要加 -w RUN corepack enable \ && corepack prepare pnpm@9.15.3 --activate \ && pnpm add -D playwright@${PLAYWRIGHT_VERSION} \ && pnpm exec playwright install chromium \ && pnpm exec playwright install-deps chromium # 6. Python 同步引擎 (sync.py) - 修复热重启问题 RUN echo 'import os, sys, tarfile\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\ \n\ def restore():\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 = f"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="/root/.openclaw/")\n\ print(f"Success: Restored entire directory from {name}")\n\ return True\n\ except Exception as e: print(f"Restore Error: {e}")\n\ \n\ def backup():\n\ try:\n\ day = datetime.now().strftime("%Y-%m-%d_%H")\n\ name = f"backup_{day}.tar.gz"\n\ tmp_path = f"/tmp/{name}"\n\ base_dir = "/root/.openclaw"\n\ \n\ with tarfile.open(tmp_path, "w:gz") as tar:\n\ if os.path.exists(base_dir):\n\ for item in os.listdir(base_dir):\n\ tar.add(os.path.join(base_dir, item), arcname=item)\n\ \n\ api.upload_file(path_or_fileobj=tmp_path, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\ print(f"Backup {name} Success. All files in {base_dir} included.")\n\ \n\ # 上传完立刻清理临时文件,释放空间\n\ if os.path.exists(tmp_path):\n\ os.remove(tmp_path)\n\ except Exception as e: print(f"Backup Error: {e}")\n\ \n\ if __name__ == "__main__":\n\ if len(sys.argv) > 1 and sys.argv[1] == "backup": backup()\n\ else: restore()' > /usr/local/bin/sync.py # 7. 启动控制逻辑(使用 printf 避免 heredoc 问题) RUN echo '#!/bin/bash\n\ set -e\n\ mkdir -p /root/.openclaw\n\ chmod 700 /root/.openclaw\n\ \n\ python3 /usr/local/bin/sync.py restore\n\ \n\ CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed "s|/chat/completions||g" | sed "s|/v1/|/v1|g" | sed "s|/v1$|/v1|g")\n\ \n\ # 生成配置 - 使用 printf 避免引号问题\n\ printf '"'"'{\n\ "models": {\n\ "providers": {\n\ "nvidia": {\n\ "baseUrl": "%s", "apiKey": "%s", "api": "openai-completions",\n\ "models": [{ "id": "%s", "name": "z-ai/glm4.7", "contextWindow": 128000 }]\n\ }\n\ }\n\ },\n\ "agents": { "defaults": { "model": { "primary": "nvidia/%s" } } },\n\ "gateway": {\n\ "mode": "local", "bind": "lan", "port": %s,\n\ "trustedProxies": ["0.0.0.0/0", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "127.0.0.1/8"],\n\ "auth": { "mode": "token", "token": "%s" },\n\ "controlUi": { \n\ "enabled": true,\n\ "allowInsecureAuth": true,\n\ "dangerouslyAllowHostHeaderOriginFallback": true,\n\ "dangerouslyDisableDeviceAuth": true\n\ }\n\ }\n\ }\n\ '"'"' "$CLEAN_BASE" "$OPENAI_API_KEY" "$MODEL" "$MODEL" "$PORT" "$OPENCLAW_GATEWAY_PASSWORD" > /root/.openclaw/openclaw.json\n\ \n\ (while true; do sleep 86400; python3 /usr/local/bin/sync.py backup; done) &\n\ \n\ openclaw doctor --fix\n\ exec openclaw gateway run --port $PORT\n\ ' > /usr/local/bin/start-openclaw && chmod +x /usr/local/bin/start-openclaw EXPOSE 7860 CMD ["/usr/local/bin/start-openclaw"]