Subsequently commited on
Commit
6f71136
·
verified ·
1 Parent(s): f6c484b

Update entrypoint.sh

Browse files
Files changed (1) hide show
  1. entrypoint.sh +43 -271
entrypoint.sh CHANGED
@@ -1,245 +1,59 @@
1
  #!/bin/sh
2
- # OpenClaw HF Spaces - Production Entrypoint
3
  umask 022
4
 
5
- # ── Force IPv4 for Telegram (IPv6 route broken on HF nodes) ──
6
- sysctl -w net.ipv6.conf.all.disable_ipv6=1 2>/dev/null || true
7
- sysctl -w net.ipv6.conf.default.disable_ipv6=1 2>/dev/null || true
8
- export NODE_OPTIONS="--dns-result-order=ipv4first ${NODE_OPTIONS:-}"
9
- {
10
- echo "149.154.167.51 api.telegram.org"
11
- echo "149.154.167.220 api.telegram.org"
12
- echo "91.108.56.130 api.telegram.org"
13
- } >> /etc/hosts 2>/dev/null || true
14
- echo "[entrypoint] IPv6 disabled, IPv4 forced for Telegram"
15
-
16
- # ----------------------------------------------------------------
17
- # Logging
18
- # ----------------------------------------------------------------
19
- ts() { date -u +"%H:%M:%S"; }
20
  log() { echo "[$(ts)] [entrypoint] $*"; }
21
  warn() { echo "[$(ts)] [entrypoint] WARN: $*"; }
22
 
23
- # ----------------------------------------------------------------
24
- # Storage: resolve OPENCLAW_HOME
25
- # ----------------------------------------------------------------
26
  OPENCLAW_HOME=/home/user
27
- mkdir -p /home/user/.openclaw
28
-
29
- RETRIES=5
30
- i=0
31
- USE_LOCAL_STATE="${USE_LOCAL_STATE:-1}"
32
- while [ "$USE_LOCAL_STATE" != "1" ] && [ $i -lt $RETRIES ]; do
33
- if [ -d /data ] && touch /data/.write-test 2>/dev/null; then
34
- rm -f /data/.write-test
35
- OPENCLAW_HOME=/data
36
- mkdir -p /data/.openclaw
37
- log "Storage bucket /data is writable - using persistent storage"
38
- break
39
- fi
40
- i=$((i+1))
41
- if [ $i -lt $RETRIES ]; then
42
- log "Waiting for /data mount... ($i/$RETRIES)"
43
- sleep 1
44
- fi
45
- done
46
-
47
- if [ "$USE_LOCAL_STATE" = "1" ]; then
48
- log "USE_LOCAL_STATE=1 - live state on local disk (/home/user); /data is left untouched"
49
- elif [ "$OPENCLAW_HOME" = "/home/user" ]; then
50
- warn "/data not writable after ${RETRIES}s - using ephemeral /home/user"
51
- fi
52
-
53
  export OPENCLAW_HOME
54
- log "OPENCLAW_HOME=$OPENCLAW_HOME"
55
 
56
- # ----------------------------------------------------------------
57
- # Export provider API keys from Secrets
58
- # ----------------------------------------------------------------
59
  for VAR in $(env | cut -d= -f1); do
60
- case "$VAR" in
61
- OPENCLAW_*|SPACE_*|SYSTEM_*|HF_*|NODE_*|npm_*) continue ;;
62
- esac
63
  case "$VAR" in
64
  *_API_KEY|*_SECRET_KEY|*_ACCESS_TOKEN|*_BOT_TOKEN|*_AUTH_TOKEN|*_APP_KEY)
65
- VAL=$(printenv "$VAR" 2>/dev/null || true)
66
- if [ -n "$VAL" ]; then
67
- export "$VAR"
68
- log "exported: $VAR"
69
- fi
70
- ;;
71
  esac
72
  done
73
 
74
- export HF_TOKEN="${HF_TOKEN:-}"
75
-
76
- # ----------------------------------------------------------------
77
- # One-time setup
78
- # ----------------------------------------------------------------
79
  log "Running setup..."
80
- node /app/spaces/huggingface/setup-hf-config.mjs || true
81
- log "Setup done."
82
-
83
- if [ -f /app/security-check.sh ]; then
84
- sh /app/security-check.sh || true
85
- fi
86
-
87
- # ----------------------------------------------------------------
88
- # Helpers
89
- # ----------------------------------------------------------------
90
- STATE_DB="$OPENCLAW_HOME/.openclaw/state/openclaw.sqlite"
91
-
92
- check_db() {
93
- DBPATH="$1" node - <<'JSEOF'
94
- const { DatabaseSync } = require('node:sqlite');
95
- try {
96
- const db = new DatabaseSync(process.env.DBPATH, { readOnly: true });
97
- const row = db.prepare('PRAGMA integrity_check').get();
98
- db.close();
99
- process.exit(Object.values(row)[0] === 'ok' ? 0 : 1);
100
- } catch (e) {
101
- console.error('[db-check] ' + e.message);
102
- process.exit(1);
103
- }
104
- JSEOF
105
- }
106
-
107
- dump_owner() {
108
- DBPATH="$1" node - <<'JSEOF'
109
- const { DatabaseSync } = require('node:sqlite');
110
- try {
111
- const db = new DatabaseSync(process.env.DBPATH, { readOnly: true });
112
- const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND (name LIKE '%owner%' OR name LIKE '%lock%' OR name LIKE '%lease%' OR name LIKE '%heartbeat%')").all();
113
- for (const t of tables) {
114
- console.log('[owner-dump] table ' + t.name);
115
- for (const r of db.prepare('SELECT * FROM "' + t.name + '" LIMIT 5').all()) {
116
- console.log('[owner-dump] ' + JSON.stringify(r).slice(0, 400));
117
- }
118
- }
119
- db.close();
120
- } catch (e) { console.log('[owner-dump] ' + e.message); }
121
- JSEOF
122
- }
123
-
124
- # Stale lock files left on the persistent volume by a crashed gateway
125
- clear_stale_locks() {
126
- for d in "$OPENCLAW_HOME"/.openclaw/tmp/openclaw-*; do
127
- [ -d "$d" ] || continue
128
- for l in "$d"/*.lock; do
129
- [ -e "$l" ] || continue
130
- INFO=$(head -c 160 "$l" 2>/dev/null | tr '\n' ' ')
131
- rm -rf "$l" && log "Removed stale lock: $l [$INFO]"
132
- done
133
- done
134
- }
135
-
136
- run_doctor() {
137
- timeout 300 openclaw doctor --fix --non-interactive </dev/null >/tmp/doctor.out 2>&1
138
- cat /tmp/doctor.out
139
- }
140
- doctor_locked() {
141
- grep -q "could not enter maintenance" /tmp/doctor.out
142
- }
143
-
144
- # Move retired Telegram JSON state aside (blocks doctor migration; kept as backup)
145
- LEGACY_BAK="$OPENCLAW_HOME/legacy-telegram-backup"
146
- mkdir -p "$LEGACY_BAK"
147
- for f in "$OPENCLAW_HOME"/.openclaw/telegram/update-offset-*.json \
148
- "$OPENCLAW_HOME"/.openclaw/agents/*/sessions/sessions.json.telegram-sent-messages.json; do
149
- if [ -f "$f" ]; then
150
- mv "$f" "$LEGACY_BAK/$(echo "$f" | tr '/' '_')" \
151
- && log "Moved legacy telegram state: $f"
152
- fi
153
- done
154
-
155
- # ----------------------------------------------------------------
156
- # Restore a state DB that an earlier boot moved aside
157
- # ----------------------------------------------------------------
158
- LOCKED=$(ls -t "$STATE_DB".locked-* 2>/dev/null | head -n1)
159
- if [ -n "$LOCKED" ]; then
160
- warn "Found a state DB moved aside by an earlier boot - restoring: $LOCKED"
161
- if [ -e "$STATE_DB" ]; then
162
- mv "$STATE_DB" "$STATE_DB.fresh-$(date +%s)"
163
- fi
164
- rm -f "$STATE_DB-wal" "$STATE_DB-shm"
165
- mv "$LOCKED" "$STATE_DB" && log "Restored state DB from $LOCKED"
166
- fi
167
-
168
- # If the state DB is corrupt, restore from doctor's own pre-migration backup
169
- if [ -f "$STATE_DB" ] && ! check_db "$STATE_DB" 2>/dev/null; then
170
- warn "State DB failed integrity check - looking for an intact backup..."
171
- GOOD=""
172
- for B in $(ls -t "$STATE_DB".pre-startup-migration-*.bak 2>/dev/null); do
173
- if check_db "$B" 2>/dev/null; then GOOD="$B"; break; fi
174
- done
175
- if [ -n "$GOOD" ]; then
176
- mv "$STATE_DB" "$STATE_DB.corrupt-$(date +%s)"
177
- rm -f "$STATE_DB-wal" "$STATE_DB-shm"
178
- cp "$GOOD" "$STATE_DB" && log "Restored state DB from $GOOD"
179
- else
180
- warn "No intact backup found - leaving DB untouched"
181
- fi
182
- fi
183
-
184
- # ----------------------------------------------------------------
185
- # Doctor (state migration)
186
- # ----------------------------------------------------------------
187
- log "Running openclaw doctor --fix (state migration)..."
188
- clear_stale_locks
189
- run_doctor
190
- if doctor_locked; then
191
- warn "doctor blocked by state ownership lock - diagnostics follow"
192
- log "now (UTC): $(date -u +%Y-%m-%dT%H:%M:%S)"
193
- log "processes in this container:"
194
- for p in /proc/[0-9]*; do
195
- c=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null)
196
- [ -n "$c" ] && echo "[proc] ${p#/proc/}: $(echo "$c" | cut -c1-160)"
197
- done
198
- dump_owner "$STATE_DB" 2>/dev/null
199
- find "$OPENCLAW_HOME/.openclaw/tmp" -maxdepth 3 2>/dev/null | head -n 30
200
- warn "retrying doctor in 30s..."
201
- sleep 30
202
- clear_stale_locks
203
- run_doctor
204
- fi
205
- if doctor_locked; then
206
- warn "doctor still blocked by the state lock - not touching any data"
207
- fi
208
- log "Doctor step done."
209
 
210
- # Plugins must not be world-writable or OpenClaw blocks them
 
 
 
211
  chmod -R go-w "$OPENCLAW_HOME/.openclaw/npm" "$OPENCLAW_HOME/.openclaw/extensions" 2>/dev/null || true
212
 
213
- # ----------------------------------------------------------------
214
- # Make the CLI default to the same gateway we start below
215
- # ----------------------------------------------------------------
216
  GW_URL="ws://127.0.0.1:7860"
217
  GW_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-}"
 
 
 
218
  for kv in "gateway.mode local" "gateway.port 7860"; do
219
  set -- $kv
220
- if openclaw config set "$1" "$2" </dev/null >/tmp/cfg.out 2>&1; then
221
- log "config set $1 = $2"
222
- else
223
- warn "config set $1 failed: $(head -c 200 /tmp/cfg.out | tr '\n' ' ')"
224
- fi
225
  done
226
 
227
- if [ "${#GW_TOKEN}" -lt 24 ]; then
228
- warn "OPENCLAW_GATEWAY_TOKEN is only ${#GW_TOKEN} chars - with password-only access it should be 32+ letters/digits"
 
 
 
 
 
 
229
  fi
230
 
231
- # ----------------------------------------------------------------
232
- # Password-only access (no manual device approval).
233
- # A pairing request exists only AFTER the gateway password/token was accepted,
234
- # so approving pending requests == "correct password gets in".
235
- # Security therefore depends entirely on OPENCLAW_GATEWAY_TOKEN being long.
236
- # Set AUTO_APPROVE_PAIRING=0 (Space variable) to switch this off.
237
- # Request IDs are collected from two places:
238
- # 1) `openclaw devices list --json`
239
- # 2) the gateway log ("pairing required ... requestId: <uuid>")
240
- # ----------------------------------------------------------------
241
  UUID_RE='[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}'
242
-
243
  try_approve() {
244
  ID="$1"
245
  grep -qx "$ID" /tmp/approved.ids 2>/dev/null && return 0
@@ -257,20 +71,12 @@ try_approve() {
257
 
258
  if [ "${AUTO_APPROVE_PAIRING:-1}" = "1" ]; then
259
  (
260
- log "Pairing auto-approver started (only requests that already passed gateway auth)"
261
- FAILS=0
262
- SAMPLED=0
263
  while true; do
264
  sleep 4
265
-
266
- # source 1: CLI device list
267
  IDS=""
268
- if openclaw devices list --json --url "$GW_URL" --token "$GW_TOKEN" </dev/null >/tmp/devices.json 2>/tmp/devices.err \
269
- || openclaw devices list --json </dev/null >/tmp/devices.json 2>/tmp/devices.err; then
270
- if [ $SAMPLED -eq 0 ]; then
271
- log "devices list sample: $(head -c 300 /tmp/devices.json | tr '\n' ' ')"
272
- SAMPLED=1
273
- fi
274
  IDS=$(node - <<'JSEOF'
275
  const fs = require('fs');
276
  let data;
@@ -283,9 +89,7 @@ const out = [];
283
  if (pending && x && typeof x === 'object') {
284
  const id = x.requestId || x.id;
285
  if (typeof id === 'string') out.push(id);
286
- } else {
287
- walk(x, key);
288
- }
289
  }
290
  } else if (v && typeof v === 'object') {
291
  for (const k of Object.keys(v)) walk(v[k], k);
@@ -294,63 +98,31 @@ const out = [];
294
  console.log(out.join('\n'));
295
  JSEOF
296
  )
297
- else
298
- FAILS=$((FAILS+1))
299
- if [ $FAILS -eq 8 ] || [ $((FAILS % 30)) -eq 0 ]; then
300
- warn "devices list keeps failing: $(head -c 300 /tmp/devices.err | tr '\n' ' ') | $(head -c 200 /tmp/devices.json | tr '\n' ' ')"
301
- fi
302
  fi
303
-
304
- # source 2: request IDs printed by the gateway when a browser is waiting for approval
305
  LOGIDS=""
306
  LATEST=$(ls -t /tmp/openclaw/openclaw-*.log 2>/dev/null | head -n 1)
307
- if [ -n "$LATEST" ]; then
308
- LOGIDS=$(tail -n 300 "$LATEST" 2>/dev/null \
309
- | grep -o "requestId[^0-9a-f]\{0,4\}$UUID_RE" \
310
- | grep -o "$UUID_RE" | sort -u)
311
- fi
312
-
313
- for ID in $IDS $LOGIDS; do
314
- try_approve "$ID"
315
- done
316
  done
317
  ) &
318
  fi
319
 
320
- # ----------------------------------------------------------------
321
- # Gateway loop
322
- # ----------------------------------------------------------------
323
  log "Starting gateway loop..."
324
-
325
  while true; do
326
- clear_stale_locks
 
 
327
  log "Gateway starting..."
328
-
329
- openclaw gateway \
330
- --allow-unconfigured \
331
- --bind lan \
332
- --port 7860
333
  CODE=$?
334
-
335
  log "Gateway exited (code $CODE)"
336
-
337
  WAITED=0
338
  while nc -z 127.0.0.1 7860 2>/dev/null; do
339
- if [ $WAITED -eq 0 ]; then
340
- log "Port 7860 still held (spawned child running) - waiting..."
341
- fi
342
- sleep 2
343
- WAITED=$((WAITED + 2))
344
- if [ $WAITED -ge 90 ]; then
345
- warn "Port 7860 still in use after 90s - forcing restart"
346
- break
347
- fi
348
  done
349
-
350
- if [ $WAITED -gt 0 ]; then
351
- log "Port free after ${WAITED}s"
352
- fi
353
-
354
  sleep 1
355
- log "Restarting gateway..."
356
  done
 
1
  #!/bin/sh
2
+ # OpenClaw on HF Spaces - 实时状态在本地磁盘, /data 仅只读导入旧配置/工作区
3
  umask 022
4
 
5
+ ts() { date -u +"%H:%M:%S"; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  log() { echo "[$(ts)] [entrypoint] $*"; }
7
  warn() { echo "[$(ts)] [entrypoint] WARN: $*"; }
8
 
 
 
 
9
  OPENCLAW_HOME=/home/user
10
+ mkdir -p "$OPENCLAW_HOME/.openclaw"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  export OPENCLAW_HOME
12
+ log "OPENCLAW_HOME=$OPENCLAW_HOME (live state on local disk)"
13
 
14
+ # ── 从 Secrets 导出各家 Key ──
 
 
15
  for VAR in $(env | cut -d= -f1); do
16
+ case "$VAR" in OPENCLAW_*|SPACE_*|SYSTEM_*|HF_*|NODE_*|npm_*) continue ;; esac
 
 
17
  case "$VAR" in
18
  *_API_KEY|*_SECRET_KEY|*_ACCESS_TOKEN|*_BOT_TOKEN|*_AUTH_TOKEN|*_APP_KEY)
19
+ [ -n "$(printenv "$VAR" 2>/dev/null)" ] && { export "$VAR"; log "exported: $VAR"; } ;;
 
 
 
 
 
20
  esac
21
  done
22
 
23
+ # ── 生成配置 + 安全检查 ──
 
 
 
 
24
  log "Running setup..."
25
+ node /app/spaces/huggingface/setup-hf-config.mjs || warn "setup failed"
26
+ sh /app/security-check.sh || true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # ── doctor (本地新状态, 做一次迁移/校验) ──
29
+ log "Running openclaw doctor --fix..."
30
+ timeout 300 openclaw doctor --fix --non-interactive </dev/null >/tmp/doctor.out 2>&1
31
+ tail -n 30 /tmp/doctor.out
32
  chmod -R go-w "$OPENCLAW_HOME/.openclaw/npm" "$OPENCLAW_HOME/.openclaw/extensions" 2>/dev/null || true
33
 
 
 
 
34
  GW_URL="ws://127.0.0.1:7860"
35
  GW_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-}"
36
+ if [ -z "$GW_TOKEN" ]; then
37
+ GW_TOKEN=$(node -e 'try{console.log(JSON.parse(require("fs").readFileSync(process.env.OPENCLAW_HOME+"/.openclaw/openclaw.json","utf8")).gateway.auth.token||"")}catch(e){}' 2>/dev/null)
38
+ fi
39
  for kv in "gateway.mode local" "gateway.port 7860"; do
40
  set -- $kv
41
+ openclaw config set "$1" "$2" </dev/null >/tmp/cfg.out 2>&1 \
42
+ && log "config set $1 = $2" || warn "config set $1 failed: $(head -c 200 /tmp/cfg.out | tr '\n' ' ')"
 
 
 
43
  done
44
 
45
+ # ── Telegram 网络: 启动先探测一次, 之后后台持续重试 ──
46
+ export TG_IP_CACHE=/tmp/tg-ips.json
47
+ GW_NODE_OPTIONS="--dns-result-order=ipv4first"
48
+ if [ "${TG_NET_FIX:-1}" = "1" ]; then
49
+ log "Probing api.telegram.org..."
50
+ timeout 30 node /app/tg-net-fix.cjs probe || true
51
+ GW_NODE_OPTIONS="$GW_NODE_OPTIONS --require /app/tg-net-fix.cjs"
52
+ ( while true; do sleep 45; timeout 40 node /app/tg-net-fix.cjs probe quiet; done ) &
53
  fi
54
 
55
+ # ── 只靠密码登录: 通过网关认证后才会出现配对请求, 自动批准 ──
 
 
 
 
 
 
 
 
 
56
  UUID_RE='[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}'
 
57
  try_approve() {
58
  ID="$1"
59
  grep -qx "$ID" /tmp/approved.ids 2>/dev/null && return 0
 
71
 
72
  if [ "${AUTO_APPROVE_PAIRING:-1}" = "1" ]; then
73
  (
74
+ log "Pairing auto-approver started"
 
 
75
  while true; do
76
  sleep 4
 
 
77
  IDS=""
78
+ if openclaw devices list --json --url "$GW_URL" --token "$GW_TOKEN" </dev/null >/tmp/devices.json 2>/dev/null \
79
+ || openclaw devices list --json </dev/null >/tmp/devices.json 2>/dev/null; then
 
 
 
 
80
  IDS=$(node - <<'JSEOF'
81
  const fs = require('fs');
82
  let data;
 
89
  if (pending && x && typeof x === 'object') {
90
  const id = x.requestId || x.id;
91
  if (typeof id === 'string') out.push(id);
92
+ } else walk(x, key);
 
 
93
  }
94
  } else if (v && typeof v === 'object') {
95
  for (const k of Object.keys(v)) walk(v[k], k);
 
98
  console.log(out.join('\n'));
99
  JSEOF
100
  )
 
 
 
 
 
101
  fi
 
 
102
  LOGIDS=""
103
  LATEST=$(ls -t /tmp/openclaw/openclaw-*.log 2>/dev/null | head -n 1)
104
+ [ -n "$LATEST" ] && LOGIDS=$(tail -n 300 "$LATEST" 2>/dev/null \
105
+ | grep -o "requestId[^0-9a-f]\{0,4\}$UUID_RE" | grep -o "$UUID_RE" | sort -u)
106
+ for ID in $IDS $LOGIDS; do try_approve "$ID"; done
 
 
 
 
 
 
107
  done
108
  ) &
109
  fi
110
 
111
+ # ── 网关循环 ──
 
 
112
  log "Starting gateway loop..."
 
113
  while true; do
114
+ for d in "$OPENCLAW_HOME"/.openclaw/tmp/openclaw-*; do
115
+ [ -d "$d" ] && rm -f "$d"/*.lock 2>/dev/null
116
+ done
117
  log "Gateway starting..."
118
+ NODE_OPTIONS="$GW_NODE_OPTIONS ${NODE_OPTIONS:-}" openclaw gateway --allow-unconfigured --bind lan --port 7860
 
 
 
 
119
  CODE=$?
 
120
  log "Gateway exited (code $CODE)"
 
121
  WAITED=0
122
  while nc -z 127.0.0.1 7860 2>/dev/null; do
123
+ [ $WAITED -eq 0 ] && log "Port 7860 still held - waiting..."
124
+ sleep 2; WAITED=$((WAITED + 2))
125
+ [ $WAITED -ge 90 ] && { warn "Port still in use after 90s - forcing restart"; break; }
 
 
 
 
 
 
126
  done
 
 
 
 
 
127
  sleep 1
 
128
  done