Update entrypoint.sh
Browse files- entrypoint.sh +43 -271
entrypoint.sh
CHANGED
|
@@ -1,245 +1,59 @@
|
|
| 1 |
#!/bin/sh
|
| 2 |
-
# OpenClaw HF Spaces -
|
| 3 |
umask 022
|
| 4 |
|
| 5 |
-
|
| 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 /
|
| 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 |
-
|
| 66 |
-
if [ -n "$VAL" ]; then
|
| 67 |
-
export "$VAR"
|
| 68 |
-
log "exported: $VAR"
|
| 69 |
-
fi
|
| 70 |
-
;;
|
| 71 |
esac
|
| 72 |
done
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
# ----------------------------------------------------------------
|
| 77 |
-
# One-time setup
|
| 78 |
-
# ----------------------------------------------------------------
|
| 79 |
log "Running setup..."
|
| 80 |
-
node /app/spaces/huggingface/setup-hf-config.mjs ||
|
| 81 |
-
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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>/
|
| 269 |
-
|| openclaw devices list --json </dev/null >/tmp/devices.json 2>/
|
| 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 |
-
|
| 308 |
-
|
| 309 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 340 |
-
|
| 341 |
-
|
| 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
|