File size: 7,656 Bytes
35e3473
 
 
 
 
89fb578
35e3473
 
89fb578
856c871
 
 
89fb578
 
35e3473
 
 
 
 
 
 
 
 
 
 
 
eb76166
3af9e5e
35e3473
 
 
856c871
 
35e3473
 
 
 
 
 
 
 
 
 
 
 
 
360f9c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35e3473
 
 
 
 
 
 
360f9c4
 
 
35e3473
eb76166
35e3473
 
360f9c4
35e3473
 
 
 
 
 
 
 
 
 
 
 
 
 
eb76166
3af9e5e
 
fb619df
3af9e5e
35e3473
 
 
 
3d980a7
 
 
35e3473
 
 
5aa59e4
 
 
 
87f529e
5aa59e4
87f529e
fb619df
5aa59e4
 
87f529e
 
ac84c85
 
 
87f529e
5aa59e4
 
 
87f529e
 
ac84c85
87f529e
 
 
5aa59e4
 
 
 
 
 
 
 
 
 
 
 
 
 
fb619df
5aa59e4
 
 
 
35e3473
ce3413f
 
35e3473
 
 
 
 
 
 
 
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
170
171
172
# 核心镜像:Node 22 slim 保证了环境的现代性与轻量化
FROM node:22-slim

# 1. 安装系统依赖
# 包含:git (拉取依赖), openssh-client (解决构建报错), build-essential/g++/make (编译原生模块), python3 (运行同步脚本)
# 新增:curl, chromium (浏览器工具支持), 以及运行 Chromium 所需的库, tzdata (设置时区)
RUN apt-get update && apt-get install -y --no-install-recommends \
    git openssh-client build-essential python3 python3-pip \
    g++ make ca-certificates curl chromium tzdata \
    libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
    libxcomposite1 libxdamage1 libxext6 libxfixes3 libxrandr2 \
    libgbm1 libasound2 libpangocairo-1.0-0 libpango-1.0-0 \
    && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone \
    && rm -rf /var/lib/apt/lists/*

# 2. 安装 Hugging Face 命令行工具
RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages

# 3. 构建环境优化
# 修复 Git 证书问题并将所有 SSH 协议重定向为 HTTPS
RUN update-ca-certificates && \
    git config --global http.sslVerify false && \
    git config --global url."https://github.com/".insteadOf ssh://git@github.com/

# 4. 全局安装 OpenClaw
ENV HOME=/root
RUN npm install -g openclaw@latest zod --unsafe-perm

# 5. 设置环境变量
ENV PORT=7860 \
    OPENCLAW_GATEWAY_MODE=local \
    OPENCLAW_BROWSER_PATH=/usr/bin/chromium

# 6. 核心同步引擎 (sync.py)
# 针对 OpenClaw 新版 MEMORY.md 机制进行了全路径覆盖
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\
        print(f"--- [SYNC] 启动恢复流程, 目标仓库: {repo_id} ---")\n\
        if repo_id and token:\n\
            files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)\n\
            now = datetime.now()\n\
            found = False\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\
                    print(f"--- [SYNC] 发现备份文件: {name}, 正在下载... ---")\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"--- [SYNC] 恢复成功! 数据已覆盖至 /root/.openclaw/ ---")\n\
                    found = True; break\n\
            if not found: print("--- [SYNC] 未找到最近 5 天的备份包 ---")\n\
        else: print("--- [SYNC] 跳过恢复: 未配置 HF_DATASET 或 HF_TOKEN ---")\n\
\n\
        # 强制清理所有残留的 .lock 文件,防止 session 锁定错误\n\
        count = 0\n\
        for root, _, fs in os.walk("/root/.openclaw/"):\n\
            for f in fs:\n\
                if f.endswith(".lock"):\n\
                    try:\n\
                        os.remove(os.path.join(root, f))\n\
                        count += 1\n\
                    except: pass\n\
        if count > 0: print(f"--- [SYNC] 已清理 {count} 个残留的锁定文件 ---")\n\
        return True\n\
    except Exception as e: print(f"--- [SYNC] 恢复异常: {e} ---")\n\
\n\
def backup():\n\
    try:\n\
        day = datetime.now().strftime("%Y-%m-%d")\n\
        name = f"backup_{day}.tar.gz"\n\
        print(f"--- [SYNC] 正在执行全量备份: {name} ---")\n\
        def lock_filter(tarinfo):\n\
            if tarinfo.name.endswith(".lock"): return None\n\
            return tarinfo\n\
        with tarfile.open(name, "w:gz") as tar:\n\
            for target in ["sessions", "workspace", "agents", "memory", "plugins", "openclaw.json"]:\n\
                full_path = f"/root/.openclaw/{target}"\n\
                if os.path.exists(full_path):\n\
                    tar.add(full_path, arcname=target, filter=lock_filter)\n\
        api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)\n\
        print(f"--- [SYNC] 备份上传成功! ---")\n\
    except Exception as e: print(f"--- [SYNC] 备份失败: {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. 容器入口脚本 (start-openclaw)
# 负责恢复数据 -> 生成配置 -> 启动网关 -> 定时备份
RUN echo "#!/bin/bash\n\
set -e\n\
mkdir -p /root/.openclaw/sessions\n\
mkdir -p /root/.openclaw/workspace\n\
mkdir -p /root/.openclaw/plugins\n\
mkdir -p /root/.openclaw/agents/main/sessions\n\
mkdir -p /root/.openclaw/credentials\n\
ln -s /root/.openclaw/workspace /root/.openclaw/memory\n\
chmod 700 /root/.openclaw\n\
\n\
# 启动前执行数据恢复\n\
python3 /usr/local/bin/sync.py restore\n\
\n\
# 设置 CLI 认证 Token\n\
export OPENCLAW_GATEWAY_TOKEN=\"\$OPENCLAW_GATEWAY_PASSWORD\"\n\
\n\
# 清理 API Base 地址\n\
CLEAN_BASE=\$(echo \"\$OPENAI_API_BASE\" | sed \"s|/chat/completions||g\" | sed \"s|/v1/|/v1|g\" | sed \"s|/v1\$|/v1|g\")\n\
\n\
# 生成 openclaw.json 配置文件\n\
cat > /root/.openclaw/openclaw.json <<EOF\n\
{\n\
  \"models\": {\n\
    \"mode\": \"merge\",\n\
    \"providers\": {\n\
      \"cliproxy\": {\n\
        \"baseUrl\": \"\$OPENAI_API_BASE\",\n\
        \"apiKey\": \"\$OPENAI_API_KEY\",\n\
        \"api\": \"openai-completions\",\n\
        \"models\": [\n\
          {\"id\": \"gemini-2.5-pro\", \"name\": \"gemini-2.5-pro\", \"contextWindow\": 200000, \"maxTokens\": 8192},\n\
          {\"id\": \"gemini-2.5-flash\", \"name\": \"gemini-2.5-flash\", \"contextWindow\": 200000, \"maxTokens\": 8192},\n\
          {\"id\": \"gemini-3-flash-preview\", \"name\": \"gemini-3-flash-preview\", \"contextWindow\": 200000, \"maxTokens\": 8192},\n\
          {\"id\": \"gemini-3-pro-preview\", \"name\": \"gemini-3-pro-preview\", \"contextWindow\": 200000, \"maxTokens\": 8192}\n\
        ]\n\
      }\n\
    }\n\
  },\n\
  \"agents\": {\n\
    \"defaults\": {\n\
      \"model\": {\"primary\": \"cliproxy/gemini-3-flash-preview\", \"fallbacks\": [\"cliproxy/gemini-3-pro-preview\"]},\n\
      \"workspace\": \"~/.openclaw/workspace\"\n\
    }\n\
  },\n\
  \"gateway\": {\n\
    \"mode\": \"local\", \"bind\": \"lan\", \"port\": \$PORT,\n\
    \"trustedProxies\": [\"0.0.0.0/0\", \"10.0.0.0/8\", \"172.16.0.0/12\", \"192.168.0.0/16\"],\n\
    \"auth\": { \"mode\": \"token\", \"token\": \"\$OPENCLAW_GATEWAY_PASSWORD\" },\n\
    \"controlUi\": { \"allowInsecureAuth\": true }\n\
  },\n\
  \"channels\": {\n\
    \"feishu\": {\n\
      \"enabled\": \${FEISHU_ENABLED:-false},\n\
      \"appId\": \"\$FEISHU_APP_ID\",\n\
      \"appSecret\": \"\$FEISHU_APP_SECRET\",\n\
      \"domain\": \"\${FEISHU_DOMAIN:-feishu}\",\n\
      \"connectionMode\": \"\${FEISHU_CONNECTION_MODE:-websocket}\",\n\
      \"dmPolicy\": \"open\",\n\
      \"ignoreEvents\": [\"im.message.message_read_v1\", \"im.chat.access_event.bot_p2p_chat_entered_v1\"]\n\
    }\n\
  }\n\
}\n\
EOF\n\
\n\
# 启动定时备份进程 (每 3 小时执行一次,增强安全性)\n\
(while true; do sleep 10800; python3 /usr/local/bin/sync.py backup; done) &\n\
\n\
# 启动 OpenClaw 网关\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"]