File size: 6,422 Bytes
c8a0246
842b04d
3509093
c8a0246
 
 
 
 
 
 
 
5cbfaae
 
c8a0246
3509093
5cbfaae
 
e19313f
c8a0246
 
 
 
842b04d
3509093
5cbfaae
71eb9ed
 
2395ae0
c8a0246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5cbfaae
c8a0246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3509093
5cbfaae
c8a0246
 
 
 
 
 
 
 
 
5cbfaae
c8a0246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38e91df
c8a0246
81885f4
8059013
df493bc
5710ca5
df493bc
81885f4
c8a0246
 
5d783f9
 
 
 
 
1825815
5d783f9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8a0246
 
 
 
 
 
 
 
 
 
 
 
 
5cbfaae
c8a0246
c5a2e7d
5cbfaae
c8675c2
 
 
5cbfaae
c8675c2
 
 
 
 
 
 
 
 
 
5cbfaae
c8675c2
5cbfaae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce0e0e7
5cbfaae
 
 
c8675c2
5cbfaae
c8675c2
c8a0246
 
 
 
5cbfaae
c8a0246
5cbfaae
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# ========= Base =========
FROM node:22-slim

# ========= System deps =========
RUN apt-get update && apt-get install -y --no-install-recommends \
    git \
    openssh-client \
    build-essential \
    python3 \
    python3-pip \
    ca-certificates \
    curl \
    jq \
    && rm -rf /var/lib/apt/lists/*

# ========= HF CLI & Python deps =========
RUN pip3 install --no-cache-dir huggingface_hub requests slack-sdk --break-system-packages

# ========= Git HTTPS fallback =========
RUN git config --global url."https://github.com/".insteadOf ssh://git@github.com/

# ========= OpenClaw =========
RUN npm install -g openclaw@latest --unsafe-perm

# ========= Copy scripts =========
COPY auto-approve.py /usr/local/bin/auto-approve.py
RUN chmod +x /usr/local/bin/auto-approve.py

# ========= ENV =========
ENV PORT=7860 \
    OPENCLAW_GATEWAY_MODE=local \
    HOME=/root

# ========= sync.py =========
RUN cat <<'PYEOF' > /usr/local/bin/sync.py
import os, sys, tarfile
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")

def restore():
    try:
        print(f"--- [SYNC] Restore from {repo_id} ---")
        if not repo_id or not token:
            print("--- [SYNC] Skipped (no config) ---")
            return

        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:
                path = hf_hub_download(
                    repo_id=repo_id,
                    filename=name,
                    repo_type="dataset",
                    token=token
                )

                with tarfile.open(path, "r:gz") as tar:
                    tar.extractall("/root/.openclaw/")
                print("--- [SYNC] Restore OK ---")
                return

        print("--- [SYNC] No backup found ---")

    except Exception as e:
        print("Restore error:", e)

def backup():
    try:
        day = datetime.now().strftime("%Y-%m-%d")
        name = f"backup_{day}.tar.gz"

        with tarfile.open(name, "w:gz") as tar:
            for t in ["sessions","workspace","agents","memory","openclaw.json"]:
                p=f"/root/.openclaw/{t}"
                if os.path.exists(p):
                    tar.add(p, arcname=t)

        api.upload_file(
            path_or_fileobj=name,
            path_in_repo=name,
            repo_id=repo_id,
            repo_type="dataset",
            token=token
        )
        print("--- [SYNC] Backup OK ---")

    except Exception as e:
        print("Backup error:", e)

if __name__ == "__main__":
    if len(sys.argv)>1 and sys.argv[1]=="backup":
        backup()
    else:
        restore()
PYEOF

# ========= start-openclaw =========
RUN cat <<'SHEOF' > /usr/local/bin/start-openclaw
#!/bin/bash
set -e

mkdir -p /root/.openclaw/{sessions,workspace,credentials}
chmod 700 /root/.openclaw

# Restore state
python3 /usr/local/bin/sync.py

# Clean base url safely
if [ -n "$OPENAI_API_BASE" ]; then
  CLEAN_BASE=$(echo "$OPENAI_API_BASE" \
      | sed "s|/chat/completions||g" \
      | sed "s|/v1/|/v1|g" \
      | sed "s|/v1$|/v1|g")
else
  CLEAN_BASE="https://api.siliconflow.cn/v1"
fi

# Slack credentials
if [ -n "$SLACK_BOT_TOKEN" ]; then
cat > /root/.openclaw/credentials/slack.json <<EOF
{
  "botToken":"$SLACK_BOT_TOKEN",
  "appToken":"$SLACK_APP_TOKEN"
}
EOF
fi

# OpenClaw config
cat > /root/.openclaw/openclaw.json <<EOF
{
  "models":{
    "providers":{
      "siliconflow":{
        "baseUrl":"$CLEAN_BASE",
        "apiKey":"$OPENAI_API_KEY",
        "api":"openai-completions",
        "models":[
          {"id":"$MODEL","name":"LLM","contextWindow":128000}
        ]
      }
    }
  },
  "agents":{
    "defaults":{
      "model":{"primary":"siliconflow/$MODEL"}
    }
  },
  "gateway":{
    "mode":"local",
    "bind":"lan",
    "port":$PORT,
    "trustedProxies":["0.0.0.0/0"],
    "auth":{
      "mode":"token",
      "token":"$OPENCLAW_GATEWAY_PASSWORD"
    },
    "controlUi":{
      "allowInsecureAuth": true,
      "allowedOrigins": [
        "https://quinnz-openclaw.hf.space"
      ]
    }
  },
  "channels":{
    "slack": {
      "mode": "socket",
      "webhookPath": "/slack/events",
      "enabled": true,
      "botToken": "$SLACK_BOT_TOKEN",
      "appToken": "$SLACK_APP_TOKEN",
      "userTokenReadOnly": true,
      "groupPolicy": "allowlist",
      "streaming": "partial",
      "nativeStreaming": true,
      "actions": {
        "reactions": true,
        "messages": true,
        "pins": true,
        "memberInfo": true,
        "channelInfo": true,
        "emojiList": true
      },
      "dm": {
        "enabled": true,
        "policy": "allowlist",
        "allowFrom": [
          "$SLACK_USER_ID"
        ]
      },
      "channels": {
        "$SLACK_CHANNEL_ID": {
          "allow": true,
          "requireMention": true
        }
      }
    }
  }
}
EOF

# Background backup loop
(
while true; do
  sleep 10800
  python3 /usr/local/bin/sync.py backup
done
) &

# Run doctor
openclaw doctor --fix || true

# Start Gateway
echo "--- Starting OpenClaw Gateway ---"
openclaw gateway run --port $PORT &

# Wait for Gateway ready
echo "--- Waiting for Gateway to be ready ---"
for i in {1..30}; do
    if curl -s -H "Authorization: Bearer $OPENCLAW_GATEWAY_PASSWORD" \
       http://127.0.0.1:$PORT/api/health >/dev/null 2>&1; then
        echo "--- Gateway ready ---"
        break
    fi
    sleep 1
done

# Auto approve Slack pairing
echo "--- Starting auto-approve.py ---"
python3 -u /usr/local/bin/auto-approve.py &

# Wait for frontend pairing
echo "--- Waiting for frontend pairing ---"
paired=0
for i in {1..60}; do
    status=$(curl -s -H "Authorization: Bearer $OPENCLAW_GATEWAY_PASSWORD" \
      http://127.0.0.1:$PORT/api/status || echo "")
    if echo "$status" | grep -q '"connectedClients":[1-9]'; then
        echo "✅ Gateway paired with frontend"
        paired=1
        break
    fi
    sleep 1
done

if [ "$paired" -ne 1 ]; then
    echo "⚠️ Warning: no frontend connected after 60s"
fi

# Keep foreground
wait
SHEOF

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

# ========= Ports & CMD =========
EXPOSE 7860
CMD ["/usr/local/bin/start-openclaw"]