File size: 7,492 Bytes
64b378e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490d7c8
 
83c2581
 
64b378e
 
104f18f
3074d8e
93b38bc
0bf8440
 
 
83c2581
93b38bc
0bf8440
 
 
 
 
 
 
91b07df
93b38bc
0bf8440
0f9dd17
 
 
 
 
b6222b3
490d7c8
0bf8440
 
 
 
 
 
93b38bc
0bf8440
 
93b38bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0bf8440
93b38bc
0bf8440
 
93b38bc
 
 
0bf8440
 
 
 
93b38bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0bf8440
93b38bc
 
0bf8440
 
 
 
93b38bc
 
 
 
 
 
 
 
 
 
 
 
 
0bf8440
 
 
64b378e
 
490d7c8
9d4129b
 
 
 
 
490d7c8
 
6fbefb9
490d7c8
83c2581
 
9d4129b
 
 
4595a4f
 
 
 
9d4129b
 
 
78cca53
cb5290a
f801149
9f5c21a
9d4129b
 
 
 
 
 
 
 
 
 
 
0839e2c
9d4129b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4595a4f
 
 
 
490d7c8
fbbb73f
 
490d7c8
 
 
 
 
fbbb73f
 
c014448
 
9d4129b
 
 
 
 
64b378e
 
3074d8e
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# 核心镜像选择
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. 构建环境与 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

# 5. 安装飞书频道插件(预装)
RUN npm install -g @openclaw/feishu --unsafe-perm

# 6. 环境变量预设
ENV PORT=7860 \
    OPENCLAW_GATEWAY_MODE=local \
    HOME=/root

# 7. Python 同步引擎 (sync.py) - 增强版(json配置+压缩数据,直接覆盖配置)
RUN echo 'import os, sys, tarfile\n\
from huggingface_hub import HfApi, hf_hub_download\n\
from datetime import datetime, timedelta\n\
import json\n\
import shutil\n\
\n\
api = HfApi()\n\
repo_id = os.getenv("HF_DATASET")\n\
token = os.getenv("HF_TOKEN")\n\
OPENCLAW_DIR = "/root/.openclaw"\n\
\n\
BACKUP_FILES = [\n\
    "agents",\n\
    "credentials",\n\    
    "openclaw.json",\n\
    "workspace/IDENTITY.md",\n\
    "workspace/USER.md",\n\
    "workspace/SOUL.md",\n\
    "workspace/AGENTS.md",\n\
    "workspace/TOOLS.md",\n\
    "workspace/MEMORY.md",\n\
    "workspace/skills",\n\
]\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\
        restored = False\n\
        for i in range(5):\n\
            day = (now - timedelta(days=i)).strftime("%Y-%m-%d")\n\
            config_name = f"backup_{day}_config.json"\n\
            data_name = f"backup_{day}_data.tar.gz"\n\
\n\
            # 1. 恢复数据包(如果存在)\n\
            if data_name in files:\n\
                data_path = hf_hub_download(repo_id=repo_id, filename=data_name, repo_type="dataset", token=token)\n\
                with tarfile.open(data_path, "r:gz") as tar:\n\
                    tar.extractall(path=OPENCLAW_DIR)\n\
                print(f"Restored data from {data_name}")\n\
                restored = True\n\
\n\
            # 2. 恢复配置文件(如果存在),直接覆盖现有配置\n\
            if config_name in files:\n\
                config_path = hf_hub_download(repo_id=repo_id, filename=config_name, repo_type="dataset", token=token)\n\
                target_config_path = os.path.join(OPENCLAW_DIR, "openclaw.json")\n\
                # 确保目标目录存在\n\
                os.makedirs(os.path.dirname(target_config_path), exist_ok=True)\n\
                shutil.copy2(config_path, target_config_path)\n\
                print(f"Restored config from {config_name} (overwritten)")\n\
                restored = True\n\
\n\
            if restored:\n\
                print("Restore completed.")\n\
                return True\n\
\n\
        print("No backup found in last 5 days")\n\
        return False\n\
    except Exception as e:\n\
        print(f"Restore Error: {e}")\n\
        return False\n\
\n\
def backup():\n\
    try:\n\
        day = datetime.now().strftime("%Y-%m-%d")\n\
        config_name = f"backup_{day}_config.json"\n\
        data_name = f"backup_{day}_data.tar.gz"\n\
\n\
        # 1. 备份配置文件(直接上传 JSON,不压缩)\n\
        config_path = os.path.join(OPENCLAW_DIR, "openclaw.json")\n\
        if os.path.exists(config_path):\n\
            with open(config_path, "rb") as f:\n\
                api.upload_file(\n\
                    path_or_fileobj=f,\n\
                    path_in_repo=config_name,\n\
                    repo_id=repo_id,\n\
                    repo_type="dataset",\n\
                    token=token\n\
                )\n\
            print(f"Config backup {config_name} uploaded.")\n\
\n\
        # 2. 备份其他文件(打包为 tar.gz)\n\
        with tarfile.open(data_name, "w:gz") as tar:\n\
            for f in BACKUP_FILES:\n\
                if f == "openclaw.json":\n\
                    continue  # 已单独处理\n\
                path = os.path.join(OPENCLAW_DIR, f)\n\
                if os.path.exists(path):\n\
                    tar.add(path, arcname=f)\n\
                    print(f"Backed up: {f}")\n\
        if os.path.exists(data_name):\n\
            api.upload_file(\n\
                path_or_fileobj=data_name,\n\
                path_in_repo=data_name,\n\
                repo_id=repo_id,\n\
                repo_type="dataset",\n\
                token=token\n\
            )\n\
            print(f"Data backup {data_name} uploaded.")\n\
\n\
        print(f"Backup {day} completed.")\n\
    except Exception as e:\n\
        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

#RK|# 8. 启动控制逻辑
RUN cat > /usr/local/bin/start-openclaw << 'SCRIPT'
#!/bin/bash
set -e
mkdir -p /root/.openclaw/sessions

# 阶段 1: 清理可能存在的重复插件(使用全局 npm 安装的版本)
rm -rf /root/.openclaw/extensions/feishu

# 阶段 2: 执行启动前恢复(会清理旧的 feishu 配置)
python3 /usr/local/bin/sync.py restore

# 处理地址逻辑
CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed "s|/chat/completions||g" | sed "s|/v1/|/v1|g" | sed "s|/v1$|/v1|g")

# 阶段 3: 仅当配置文件不存在时才生成默认配置
if [ ! -f /root/.openclaw/openclaw.json ]; then
    echo "No existing openclaw.json found, generating default configuration..."
    cat > /root/.openclaw/openclaw.json <<EOF
{
  "models": {
    "providers": {
      "$MODEL_PROVIDER": {
        "baseUrl": "$OPENAI_API_BASE", 
        "apiKey": "$OPENAI_API_KEY",
        "api": "openai-completions",
        "models": [{ 
          "id": "$MODEL", 
          "name": "$MODEL_NAME", 
          "contextWindow": $MODEL_CONTEXT 
        }]
      }
    }
  },
  "agents": { 
    "defaults": { 
      "model": { 
        "primary": "$MODEL_PROVIDER/$MODEL" 
      } 
    } 
  },
  "gateway": {
    "mode": "local", 
    "bind": "lan", 
    "port": $PORT,
    "trustedProxies": ["0.0.0.0/0", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
    "auth": { 
      "mode": "token", 
      "token": "$OPENCLAW_GATEWAY_TOKEN" 
    },
    "remote": { 
      "token": "$OPENCLAW_GATEWAY_TOKEN" 
    },
    "controlUi": { 
      "allowInsecureAuth": true,
      "dangerouslyAllowHostHeaderOriginFallback": true,
      "dangerouslyDisableDeviceAuth": true 
    }
  }
}
EOF
else
    echo "Existing openclaw.json found, skipping generation."
fi

# 阶段 4: doctor 自动检测并配置 feishu 插件
openclaw doctor --fix

# 阶段 5: 配置 feishu 频道(doctor 之后)
if [ -n "$FEISHU_APP_ID" ] && [ -n "$FEISHU_APP_SECRET" ]; then
    openclaw config set channels.feishu.enabled true
    openclaw config set channels.feishu.accounts.main.appId "$FEISHU_APP_ID"
    openclaw config set channels.feishu.accounts.main.appSecret "$FEISHU_APP_SECRET"
fi

# 增量备份循环 (每 3小时)
(while true; do sleep 10800; python3 /usr/local/bin/sync.py backup; done) &

exec openclaw gateway run --port $PORT
SCRIPT

RUN chmod +x /usr/local/bin/start-openclaw

EXPOSE 7860
CMD ["/usr/local/bin/start-openclaw"]