ccv / entrypoint.sh
EmilyReed96989's picture
Update entrypoint.sh
323a601 verified
#!/bin/bash
set -e
# 强制使用北京时间(解决 HF Spaces 默认 UTC 导致汇报时间错误)
export TZ="Asia/Shanghai"
CONFIG="/root/.openclaw/openclaw.json"
IP_RECORD="/root/.openclaw/.last_outbound_ip"
ENV_FILE="/root/.openclaw/.env"
# ============================================================
# 从 Supabase Storage(Private Bucket)下载所有敏感部署文件
# DEPLOY_FILES_BASE_URL 和 SUPABASE_ANON_KEY 通过 HF Secrets 注入
# 格式示例:https://xxx.supabase.co/storage/v1/object/hf-deploy
# ============================================================
download_file() {
local src="$1"
local dest="$2"
local url="${DEPLOY_FILES_BASE_URL}/${src}"
mkdir -p "$(dirname "$dest")"
echo " Downloading: ${src}"
echo " From: ${url}"
echo " To: ${dest}"
if curl -fsSL \
-H "apikey: ${SUPABASE_ANON_KEY}" \
-H "Authorization: Bearer ${SUPABASE_ANON_KEY}" \
"${url}" -o "$dest"; then
echo "✅ Downloaded: $src"
return 0
else
echo "❌ Failed: $src (HTTP error or file not found)"
return 1
fi
}
if [ -n "$DEPLOY_FILES_BASE_URL" ] && [ -n "$SUPABASE_ANON_KEY" ]; then
echo "=== Downloading deploy files from Supabase ==="
FAIL_COUNT=0
# 下载 .env 文件(包含所有环境变量)
echo "=== Attempting to download .env file ==="
if download_file ".env" "$ENV_FILE"; then
echo "✅ .env file downloaded successfully"
# 加载 .env 文件中的环境变量
if [ -f "$ENV_FILE" ]; then
echo "=== Loading environment variables from .env ==="
set -a # 自动导出所有变量
source "$ENV_FILE"
set +a
echo "✅ Environment variables loaded from .env"
fi
else
echo "⚠️ .env file download failed or not found in Supabase"
echo "⚠️ Using HF Secrets as fallback for environment variables"
((FAIL_COUNT++))
fi
# 配置文件
download_file "openclaw.json" "/root/.openclaw/openclaw.json" || ((FAIL_COUNT++))
# workspace 文件(角色设定、任务文档等)
download_file "SOUL.md" "/root/.openclaw/workspace/SOUL.md" || ((FAIL_COUNT++))
download_file "IDENTITY.md" "/root/.openclaw/workspace/IDENTITY.md" || ((FAIL_COUNT++))
download_file "USER.md" "/root/.openclaw/workspace/USER.md" || ((FAIL_COUNT++))
download_file "AGENTS.md" "/root/.openclaw/workspace/AGENTS.md" || ((FAIL_COUNT++))
download_file "TOOLS.md" "/root/.openclaw/workspace/TOOLS.md" || ((FAIL_COUNT++))
download_file "BOOTSTRAP.md" "/root/.openclaw/workspace/BOOTSTRAP.md" || ((FAIL_COUNT++))
download_file "MEMORY.md" "/root/.openclaw/workspace/MEMORY.md" || ((FAIL_COUNT++))
download_file "HEARTBEAT.md" "/root/.openclaw/workspace/HEARTBEAT.md" || ((FAIL_COUNT++))
download_file "WORKFLOW_AUTO.md" "/root/.openclaw/workspace/WORKFLOW_AUTO.md" || ((FAIL_COUNT++))
download_file "task-dispatch.md" "/root/.openclaw/workspace/task-dispatch.md" || ((FAIL_COUNT++))
# cron 任务配置
download_file "cron/jobs.json" "/root/.openclaw/cron/jobs.json" || ((FAIL_COUNT++))
# 角色资源(Maggie)
download_file "characters/maggie/character-prompt.md" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/assets/characters/maggie/character-prompt.md" || ((FAIL_COUNT++))
download_file "characters/maggie/meta.json" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/assets/characters/maggie/meta.json" || ((FAIL_COUNT++))
download_file "characters/maggie/README.md" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/assets/characters/maggie/README.md" || ((FAIL_COUNT++))
download_file "characters/maggie/images/reference.png" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/assets/characters/maggie/images/reference.png" || ((FAIL_COUNT++))
# clawmate 补丁文件
download_file "patches/clawmate/types.ts" "/root/.openclaw/extensions/clawmate-companion/src/core/types.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/config.ts" "/root/.openclaw/extensions/clawmate-companion/src/core/config.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/prepare.ts" "/root/.openclaw/extensions/clawmate-companion/src/core/prepare.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/plugin.ts" "/root/.openclaw/extensions/clawmate-companion/src/plugin.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/video-pipeline.ts" "/root/.openclaw/extensions/clawmate-companion/src/video-pipeline.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/image-fast-pipeline.ts" "/root/.openclaw/extensions/clawmate-companion/src/image-fast-pipeline.ts" || ((FAIL_COUNT++))
download_file "patches/clawmate/SKILL.md" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/SKILL.md" || ((FAIL_COUNT++))
download_file "patches/clawmate/SKILL.zh.md" "/root/.openclaw/extensions/clawmate-companion/skills/clawmate-companion/SKILL.zh.md" || ((FAIL_COUNT++))
download_file "patches/clawmate/openclaw.plugin.json" "/root/.openclaw/extensions/clawmate-companion/openclaw.plugin.json" || ((FAIL_COUNT++))
echo "=== Deploy files download complete (failures: $FAIL_COUNT) ==="
if [ "$FAIL_COUNT" -gt 0 ]; then
echo "⚠️ WARNING: $FAIL_COUNT file(s) failed to download, service may not work correctly"
fi
else
echo "⚠️ DEPLOY_FILES_BASE_URL or SUPABASE_ANON_KEY not set, skipping Supabase file download"
fi
# ============================================================
# 调试:检查环境变量是否正确加载
# ============================================================
echo "=== Debug: Checking environment variables ==="
echo "GATEWAY_TOKEN length: ${#GATEWAY_TOKEN}"
echo "GATEWAY_TOKEN first 20 chars: ${GATEWAY_TOKEN:0:20}..."
echo "CLIPROXY_BASE_URL: ${CLIPROXY_BASE_URL}"
echo "WECOM_TOKEN length: ${#WECOM_TOKEN}"
echo ""
echo "=== Debug: Checking config file before replacement ==="
grep -n "GATEWAY_TOKEN" "$CONFIG" | head -5
echo ""
# ============================================================
# 替换 openclaw.json 中的占位符
# 环境变量已从 .env 文件加载,或使用 HF Secrets 作为后备
# ============================================================
echo "=== Replacing placeholders in openclaw.json ==="
sed -i "s|__GATEWAY_TOKEN__|${GATEWAY_TOKEN}|g" "$CONFIG"
sed -i "s|__CLIPROXY_BASE_URL__|${CLIPROXY_BASE_URL}|g" "$CONFIG"
sed -i "s|__CLIPROXY_API_KEY__|${CLIPROXY_API_KEY}|g" "$CONFIG"
sed -i "s|__WECOM_WS_BOT_ID__|${WECOM_WS_BOT_ID}|g" "$CONFIG"
sed -i "s|__WECOM_WS_SECRET__|${WECOM_WS_SECRET}|g" "$CONFIG"
sed -i "s|__WECOM_TOKEN__|${WECOM_TOKEN}|g" "$CONFIG"
sed -i "s|__WECOM_AES_KEY__|${WECOM_AES_KEY}|g" "$CONFIG"
sed -i "s|__WECOM_CORP_ID__|${WECOM_CORP_ID}|g" "$CONFIG"
sed -i "s|__WECOM_APP_TOKEN__|${WECOM_APP_TOKEN}|g" "$CONFIG"
sed -i "s|__WECOM_APP_AES_KEY__|${WECOM_APP_AES_KEY}|g" "$CONFIG"
sed -i "s|__WECOM_APP_SECRET__|${WECOM_APP_SECRET}|g" "$CONFIG"
sed -i "s|__WECOM_APP_ASR_APP_ID__|${WECOM_APP_ASR_APP_ID}|g" "$CONFIG"
sed -i "s|__WECOM_APP_ASR_SECRET_ID__|${WECOM_APP_ASR_SECRET_ID}|g" "$CONFIG"
sed -i "s|__WECOM_APP_ASR_SECRET_KEY__|${WECOM_APP_ASR_SECRET_KEY}|g" "$CONFIG"
sed -i "s|__QQBOT_APP_ID__|${QQBOT_APP_ID}|g" "$CONFIG"
sed -i "s|__QQBOT_CLIENT_SECRET__|${QQBOT_CLIENT_SECRET}|g" "$CONFIG"
sed -i "s|__QQBOT_ASR_APP_ID__|${QQBOT_ASR_APP_ID}|g" "$CONFIG"
sed -i "s|__QQBOT_ASR_SECRET_ID__|${QQBOT_ASR_SECRET_ID}|g" "$CONFIG"
sed -i "s|__QQBOT_ASR_SECRET_KEY__|${QQBOT_ASR_SECRET_KEY}|g" "$CONFIG"
sed -i "s|__FEISHU_APP_ID__|${FEISHU_APP_ID}|g" "$CONFIG"
sed -i "s|__FEISHU_APP_SECRET__|${FEISHU_APP_SECRET}|g" "$CONFIG"
sed -i "s|__FEISHU_VERIFICATION_TOKEN__|${FEISHU_VERIFICATION_TOKEN}|g" "$CONFIG"
sed -i "s|__FEISHU_ENCRYPT_KEY__|${FEISHU_ENCRYPT_KEY}|g" "$CONFIG"
sed -i "s|__VOLCENGINE_ARK_API_KEY__|${VOLCENGINE_ARK_API_KEY}|g" "$CONFIG"
sed -i "s|__VOLCENGINE_PROXY_TOKEN__|${VOLCENGINE_PROXY_TOKEN}|g" "$CONFIG"
sed -i "s|__VOLCENGINE_PROXY_URL__|${VOLCENGINE_PROXY_URL}|g" "$CONFIG"
sed -i "s|__HFOPCF_GROK2API_KEY__|${HFOPCF_GROK2API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPCF_GROK2API_BASE_URL__|${HFOPCF_GROK2API_BASE_URL}|g" "$CONFIG"
sed -i "s|__HFOPQQ_GROK2API_KEY__|${HFOPQQ_GROK2API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPQQ_GROK2API_BASE_URL__|${HFOPQQ_GROK2API_BASE_URL}|g" "$CONFIG"
sed -i "s|__HFOPKUAKE_GROK2API_KEY__|${HFOPKUAKE_GROK2API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPKUAKE_GROK2API_BASE_URL__|${HFOPKUAKE_GROK2API_BASE_URL}|g" "$CONFIG"
sed -i "s|__HFOPSOUGOU_GROK2API_KEY__|${HFOPSOUGOU_GROK2API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPSOUGOU_GROK2API_BASE_URL__|${HFOPSOUGOU_GROK2API_BASE_URL}|g" "$CONFIG"
sed -i "s|__HFOPIMAGINESOUGOU_GROK_API_KEY__|${HFOPIMAGINESOUGOU_GROK_API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPIMAGINESOUGOU_GROK_BASE_URL__|${HFOPIMAGINESOUGOU_GROK_BASE_URL}|g" "$CONFIG"
sed -i "s|__HFOPVIDEOSOUGOU_GROK_API_KEY__|${HFOPVIDEOSOUGOU_GROK_API_KEY}|g" "$CONFIG"
sed -i "s|__HFOPVIDEOSOUGOU_GROK_BASE_URL__|${HFOPVIDEOSOUGOU_GROK_BASE_URL}|g" "$CONFIG"
echo "✅ Placeholders replaced"
echo ""
echo "=== Debug: Checking config file after replacement ==="
grep -n '"token"' "$CONFIG" | head -10
echo ""
echo "=== Debug: Checking if any placeholders remain ==="
PLACEHOLDER_COUNT=$(grep -c "__.*__" "$CONFIG" || echo "0")
echo "Remaining placeholders: $PLACEHOLDER_COUNT"
if [ "$PLACEHOLDER_COUNT" -gt 0 ]; then
echo "⚠️ WARNING: Found unreplaced placeholders:"
grep -o "__[A-Z_]*__" "$CONFIG" | sort -u
fi
echo ""
# Fix config file permission (suppress security audit warning)
chmod 600 "$CONFIG"
# Check outbound IP and notify if changed
CURRENT_IP=$(curl -s --max-time 10 https://ifconfig.me || curl -s --max-time 10 https://api.ipify.org || echo "unknown")
echo "=== HF Space outbound IP: ${CURRENT_IP} ==="
LAST_IP=""
if [ -f "$IP_RECORD" ]; then
LAST_IP=$(cat "$IP_RECORD")
fi
if [ "$CURRENT_IP" != "unknown" ] && [ "$CURRENT_IP" != "$LAST_IP" ]; then
echo "$CURRENT_IP" > "$IP_RECORD"
if [ -n "$WECOM_WEBHOOK_KEY" ]; then
WEBHOOK_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_WEBHOOK_KEY}"
if [ -z "$LAST_IP" ]; then
MSG="🦞 OpenClaw HF Space 首次启动\n\n出口IP: ${CURRENT_IP}\n\n请确认该IP已添加到企业微信可信IP白名单中。"
else
MSG="⚠️ OpenClaw HF Space 出口IP已变更\n\n旧IP: ${LAST_IP}\n新IP: ${CURRENT_IP}\n\n请立即到企业微信后台更新可信IP白名单,否则 wecom-app 将无法主动发送消息。"
fi
curl -s -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"${MSG}\"}}" \
|| echo "Failed to send webhook notification"
fi
fi
# ============================================================
# 确保 @openclaw-china/channels 插件已安装(运行时再次检查)
# ============================================================
echo "=== Verifying @openclaw-china/channels plugin ==="
if ! node openclaw.mjs plugins list | grep -q "@openclaw-china/channels"; then
echo "⚠️ Plugin not found, attempting to install..."
node openclaw.mjs plugins install @openclaw-china/channels --force || \
echo "❌ Plugin installation failed, channels may not work"
else
echo "✅ @openclaw-china/channels plugin is installed"
fi
# ============================================================
# 自动修复配置文件(解决未知 channel ID 问题)
# ============================================================
echo "=== Running openclaw doctor to fix config ==="
node openclaw.mjs doctor --fix || echo "⚠️ Doctor fix failed, continuing anyway..."
echo "✅ Config fixed"
# Start gateway
exec node openclaw.mjs gateway --port 7860 --bind lan --allow-unconfigured