#!/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