File size: 6,152 Bytes
017181f
 
 
 
 
 
 
895cba8
6836022
895cba8
d14221b
 
 
 
 
 
 
 
 
 
 
 
 
 
017181f
 
d14221b
 
895cba8
 
d14221b
 
 
 
 
 
895cba8
017181f
 
 
d14221b
017181f
 
 
 
59e6aed
debd0f0
 
 
 
 
 
 
 
 
 
0c78021
 
 
 
59e6aed
017181f
 
 
bd7650e
 
 
d14221b
 
 
 
 
 
 
 
 
7f62344
d14221b
 
017181f
10a30ae
017181f
 
 
 
 
 
10a30ae
017181f
 
 
 
 
 
 
 
 
 
10a30ae
017181f
 
10a30ae
017181f
 
10a30ae
017181f
10a30ae
 
 
 
 
 
 
 
 
 
 
 
 
 
017181f
10a30ae
017181f
 
 
 
556e697
80a33f8
017181f
df59f9b
b8cb32e
69b1a30
d14221b
017181f
80a33f8
017181f
556e697
 
80a33f8
 
 
556e697
 
017181f
 
 
556e697
80a33f8
556e697
80a33f8
556e697
80a33f8
 
 
 
 
b8cb32e
017181f
 
556e697
017181f
10a30ae
017181f
d14221b
80a33f8
 
017181f
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# 核心镜像选择
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"]