xia / entrypoint.sh
echo8900's picture
Update entrypoint.sh
08da1b7 verified
#!/bin/sh
# OpenClaw HF Spaces - Production Entrypoint
# Storage: HF Storage Bucket at /data (100 GB persistent)
# ----------------------------------------------------------------
# Logging
# ----------------------------------------------------------------
ts() { date -u +"%H:%M:%S"; }
log() { echo "[$(ts)] [entrypoint] $*"; }
warn() { echo "[$(ts)] [entrypoint] WARN: $*"; }
# ----------------------------------------------------------------
# Storage: resolve OPENCLAW_HOME
# Prefer /data (HF Storage Bucket). Fall back to /home/user.
# Retry up to 5s in case the bucket mount is slightly delayed.
# ----------------------------------------------------------------
OPENCLAW_HOME=/home/user
mkdir -p /home/user/.openclaw
RETRIES=5
i=0
while [ $i -lt $RETRIES ]; do
if [ -d /data ] && touch /data/.write-test 2>/dev/null; then
rm -f /data/.write-test
OPENCLAW_HOME=/data
mkdir -p /data/.openclaw
log "Storage bucket /data is writable - using persistent storage"
break
fi
i=$((i+1))
if [ $i -lt $RETRIES ]; then
log "Waiting for /data mount... ($i/$RETRIES)"
sleep 1
fi
done
if [ "$OPENCLAW_HOME" = "/home/user" ]; then
warn "/data not writable after ${RETRIES}s - using ephemeral /home/user"
fi
export OPENCLAW_HOME
log "OPENCLAW_HOME=$OPENCLAW_HOME"
# ----------------------------------------------------------------
# Export provider API keys from Secrets
# ----------------------------------------------------------------
for VAR in $(env | cut -d= -f1); do
case "$VAR" in
OPENCLAW_*|SPACE_*|SYSTEM_*|HF_*|NODE_*|npm_*) continue ;;
esac
case "$VAR" in
*_API_KEY|*_SECRET_KEY|*_ACCESS_TOKEN|*_BOT_TOKEN|*_AUTH_TOKEN|*_APP_KEY)
VAL=$(printenv "$VAR" 2>/dev/null || true)
if [ -n "$VAL" ]; then
export "$VAR"
log "exported: $VAR"
fi
;;
esac
done
export HF_TOKEN="${HF_TOKEN:-}"
# ----------------------------------------------------------------
# One-time setup (config write, webhook registration, seed files)
# ----------------------------------------------------------------
log "Running setup..."
node /app/spaces/huggingface/setup-hf-config.mjs || true
log "Setup done."
# ----------------------------------------------------------------
# Security audit (non-fatal)
# ----------------------------------------------------------------
if [ -f /app/security-check.sh ]; then
sh /app/security-check.sh || true
fi
# ----------------------------------------------------------------
# Auto-approve pending device pairings (background)
# ----------------------------------------------------------------
(
DEVICES_DIR="$OPENCLAW_HOME/.openclaw/devices"
mkdir -p "$DEVICES_DIR"
log "Device auto-pairing watcher started"
while true; do
sleep 8
PENDING="$DEVICES_DIR/pending.json"
PAIRED="$DEVICES_DIR/paired.json"
if [ -f "$PENDING" ]; then
node - << 'JSEOF'
const fs = require('fs');
const dir = process.env.OPENCLAW_HOME + '/.openclaw/devices';
const pend = dir + '/pending.json';
const pair = dir + '/paired.json';
try {
const raw = fs.readFileSync(pend, 'utf-8').trim();
if (!raw || raw === '[]' || raw === '{}') process.exit(0);
let pending = JSON.parse(raw);
if (!Array.isArray(pending)) pending = Object.values(pending);
if (pending.length === 0) process.exit(0);
let paired = [];
try { paired = JSON.parse(fs.readFileSync(pair, 'utf-8')); } catch(e) {}
if (!Array.isArray(paired)) paired = [];
const ids = new Set(paired.map(function(d){ return d.id||d.deviceId||d.name; }));
const todo = pending.filter(function(d){ return !ids.has(d.id||d.deviceId||d.name); });
if (todo.length === 0) process.exit(0);
const now = new Date().toISOString();
paired = paired.concat(todo.map(function(d){
return Object.assign({}, d, { approved: true, approvedAt: now });
}));
fs.writeFileSync(pair, JSON.stringify(paired, null, 2));
fs.writeFileSync(pend, '[]');
console.log('[auto-pair] Approved ' + todo.length + ' device(s)');
} catch(e) { /* ignore */ }
JSEOF
fi
done
) &
# ----------------------------------------------------------------
# Gateway loop
#
# OpenClaw restart behaviour:
# - On config save it spawns a child process (new gateway) then
# the parent exits with code 0.
# - The child inherits the port 7860 lock.
# - We must NOT restart until the child also exits (port is free).
#
# Normal crash: port is immediately free, loop restarts quickly.
# Config-save restart: port stays held by child; we wait up to 90s.
# ----------------------------------------------------------------
log "Starting gateway loop..."
while true; do
log "Gateway starting..."
node /app/openclaw.mjs gateway \
--allow-unconfigured \
--bind lan \
--port 7860
CODE=$?
log "Gateway exited (code $CODE)"
# Wait for port 7860 to be released before starting a new instance
WAITED=0
while nc -z 127.0.0.1 7860 2>/dev/null; do
if [ $WAITED -eq 0 ]; then
log "Port 7860 still held (spawned child running) - waiting..."
fi
sleep 2
WAITED=$((WAITED + 2))
if [ $WAITED -ge 90 ]; then
warn "Port 7860 still in use after 90s - forcing restart"
break
fi
done
if [ $WAITED -gt 0 ]; then
log "Port free after ${WAITED}s"
fi
sleep 1
log "Restarting gateway..."
done