openclaw / Dockerfile
han145's picture
Update Dockerfile
0ace41e verified
Raw
History Blame Contribute Delete
5.87 kB
FROM node:22-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
git openssh-client build-essential python3 python3-pip g++ make ca-certificates sqlite3 \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
RUN update-ca-certificates && \
git config --global http.sslVerify false && \
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
RUN npm install -g openclaw@2026.6.9 --unsafe-perm
ENV PORT=7860 OPENCLAW_GATEWAY_MODE=local HOME=/root
# ==================== 同步引擎 - 最终修复版 ====================
RUN cat > /usr/local/bin/sync.py << 'EOF'
import os, sys, tarfile, shutil
from huggingface_hub import HfApi, hf_hub_download
from datetime import datetime, timedelta
api = HfApi()
repo_id = os.getenv("HF_DATASET")
token = os.getenv("HF_TOKEN")
BASE = "/root/.openclaw"
def restore():
print("🔍 [Restore] Searching latest backup...")
try:
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
now = datetime.now()
for i in range(5):
day = (now - timedelta(days=i)).strftime("%Y-%m-%d")
name = f"backup_{day}.tar.gz"
if name in files:
print(f"📦 Downloading {name}...")
path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)
print("📂 Tar contents preview:")
with tarfile.open(path, "r:gz") as tar:
for member in tar.getmembers()[:20]: # 只打印前20
print(" ", member.name)
for member in tar.getmembers():
# 关键修复:统一放到 .openclaw 下
if member.name.startswith('agents/') or member.name.startswith('skills/') or \
member.name.startswith('workspace/') or member.name.startswith('cron/') or \
member.name.startswith('state/'):
member.name = os.path.join('.openclaw', member.name)
elif member.name == 'openclaw.json' or member.name == 'backup_state.sqlite':
member.name = os.path.join('.openclaw', member.name)
tar.extract(member, "/root")
print("✅ Extraction completed")
# 处理 DB
if os.path.exists(f"{BASE}/backup_state.sqlite"):
os.makedirs(f"{BASE}/state", exist_ok=True)
shutil.move(f"{BASE}/backup_state.sqlite", f"{BASE}/state/openclaw.sqlite")
print("✅ SQLite (cron) restored")
return True
print("⚠️ No backup found")
except Exception as e:
print(f"❌ Restore error: {e}")
def backup():
print("📦 [Backup] Creating...")
try:
day = datetime.now().strftime("%Y-%m-%d")
name = f"backup_{day}.tar.gz"
with tarfile.open(name, "w:gz") as tar:
if os.path.exists(BASE):
tar.add(BASE, arcname=".openclaw")
api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)
print(f"✅ Backup uploaded: {name}")
except Exception as e:
print(f"❌ Backup error: {e}")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "backup":
backup()
else:
restore()
EOF
# 模板
RUN mkdir -p /root/.openclaw
RUN cat > /root/.openclaw/openclaw.json.template << 'EOT'
{"models":{"providers":{"thirdparty":{"baseUrl":"PLACEHOLDER_BASE_URL","apiKey":"PLACEHOLDER_API_KEY","api":"openai-completions","headers":{"User-Agent":"Mozilla/5.0"},"models":[{"id":"PLACEHOLDER_MODEL_ID","name":"GLM-4.7-Flash","contextWindow":131072}]}}},"agents":{"defaults":{"model":{"primary":"thirdparty/PLACEHOLDER_MODEL_ID"}}},"gateway":{"mode":"local","bind":"lan","port":7860,"trustedProxies":["0.0.0.0/0","10.0.0.0/8","10.16.0.0/12","172.16.0.0/12","192.168.0.0/16"],"auth":{"mode":"token","token":"PLACEHOLDER_GATEWAY_PASSWORD"},"controlUi":{"enabled":true,"allowInsecureAuth":true,"dangerouslyAllowHostHeaderOriginFallback":true,"dangerouslyDisableDeviceAuth":true}}}
EOT
# 启动脚本
RUN cat > /usr/local/bin/start-openclaw << 'EOF'
#!/bin/bash
set -e
mkdir -p /root/.openclaw/{sessions,state}
echo "🚀 Starting OpenClaw..."
python3 /usr/local/bin/sync.py restore
sleep 8
if [ ! -d "/root/.openclaw/agents" ]; then
echo "🔄 Retrying restore..."
python3 /usr/local/bin/sync.py restore
sleep 6
fi
echo "=== Final Status ==="
echo "Agents dir: $([ -d "/root/.openclaw/agents" ] && echo Yes || echo No)"
echo "Sessions: $(ls /root/.openclaw/agents/main/sessions 2>/dev/null | wc -l || echo 0)"
echo "Skills: $(ls /root/.openclaw/skills 2>/dev/null | wc -l || echo 0)"
[ -f "/root/.openclaw/state/openclaw.sqlite" ] && echo "Cron jobs: $(sqlite3 /root/.openclaw/state/openclaw.sqlite "SELECT COUNT(*) FROM cron_jobs WHERE enabled=1;" 2>/dev/null || echo 0)"
#python3 /usr/local/bin/sync.py backup
CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|g')
sed -e "s|PLACEHOLDER_BASE_URL|$CLEAN_BASE|g" \
-e "s|PLACEHOLDER_API_KEY|$OPENAI_API_KEY|g" \
-e "s|PLACEHOLDER_MODEL_ID|$MODEL|g" \
-e "s|PLACEHOLDER_GATEWAY_PASSWORD|$OPENCLAW_GATEWAY_PASSWORD|g" \
/root/.openclaw/openclaw.json.template > /root/.openclaw/openclaw.json
echo "✅ OpenClaw started"
(while true; do sleep 12000; python3 /usr/local/bin/sync.py backup; done) &
openclaw doctor --fix
exec openclaw gateway run --port $PORT
EOF
RUN chmod +x /usr/local/bin/start-openclaw
EXPOSE 7860
CMD ["/usr/local/bin/start-openclaw"]