#!/usr/bin/env bash # quick-start.sh - v1.2.1 # 一键升级到 v1.2.0 auto-reply daemon 并启动,绕开 agent 的不稳定代码生成。 # # 用法(在 Linux 服务器上): # unzip -o openclaw-wechat-skill-v1.2.x.zip -d openclaw-wechat-skill # cd openclaw-wechat-skill # bash quick-start.sh # # 可选环境变量: # WECHAT_API_BASE=http://your-host:port # 跳过自动探测 # SKIP_DEPLOY=1 # 只启动 daemon,不重新部署 set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" OPENCLAW_DIR="$HOME/.openclaw" SKILLS_ROOT="$OPENCLAW_DIR/skills" DAEMON_DST="$OPENCLAW_DIR/auto-reply-daemon.js" CRED_FILE="$OPENCLAW_DIR/wechat-credentials.json" PID_FILE="$OPENCLAW_DIR/wechat-auto-reply.pid" LOG_FILE="$OPENCLAW_DIR/logs/auto-reply.log" GREEN=$'\e[32m'; YELLOW=$'\e[33m'; RED=$'\e[31m'; CYAN=$'\e[36m'; NC=$'\e[0m' section() { echo ""; echo "${CYAN}==== $* ====${NC}"; } ok() { echo " ${GREEN}[OK]${NC} $*"; } warn() { echo " ${YELLOW}[!]${NC} $*"; } err() { echo " ${RED}[ERR]${NC} $*"; } # ---------------- 1. Kill any running daemon (ours + agent's) ---------------- section "1. 停止任何正在运行的 auto-reply daemon" # our daemon via pid file if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE" 2>/dev/null || echo "") if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then kill "$PID" 2>/dev/null sleep 1 kill -0 "$PID" 2>/dev/null && kill -9 "$PID" 2>/dev/null ok "已停止旧 daemon (PID=$PID)" fi rm -f "$PID_FILE" fi # agent's hallucinated daemon: kill any node process running *auto-reply* # (pattern-match to avoid killing unrelated node processes) STALE=$(pgrep -f 'node.*auto.reply' 2>/dev/null || true) if [ -n "$STALE" ]; then echo "$STALE" | xargs -r kill 2>/dev/null || true sleep 1 STALE2=$(pgrep -f 'node.*auto.reply' 2>/dev/null || true) [ -n "$STALE2" ] && echo "$STALE2" | xargs -r kill -9 2>/dev/null || true ok "已停止 agent 遗留的 daemon 进程: $STALE" fi # ---------------- 2. Check prerequisites ---------------- section "2. 检查依赖" if ! command -v node >/dev/null 2>&1; then err "未找到 node, 请先安装 Node.js (>=14)" exit 1 fi ok "node $(node --version)" if ! command -v curl >/dev/null 2>&1; then err "未找到 curl" exit 1 fi ok "curl OK" # ---------------- 3. Deploy skills + daemon (unless SKIP_DEPLOY=1) ---------------- if [ "${SKIP_DEPLOY:-0}" != "1" ]; then section "3. 部署 skills 和 daemon" if [ -f "$SCRIPT_DIR/deploy-to-openclaw.js" ]; then node "$SCRIPT_DIR/deploy-to-openclaw.js" --source-root="$SCRIPT_DIR" || { err "部署脚本失败,请检查上面的输出" exit 1 } else err "找不到 $SCRIPT_DIR/deploy-to-openclaw.js, 确认你在解压目录里运行本脚本" exit 1 fi else warn "SKIP_DEPLOY=1, 跳过部署" fi # ---------------- 4. Probe / set wechatApiBase ---------------- section "4. 探测可用的 wechatApiBase" probe() { local base="$1" local resp resp=$(curl -s --max-time 5 -X POST "$base/login/check-online" \ -H "Content-Type: application/json" -d '{}' 2>/dev/null || echo "") if echo "$resp" | grep -q '"ret":200'; then local ONLINE="?" echo "$resp" | grep -q '"data":true' && ONLINE="true" echo "$resp" | grep -q '"data":false' && ONLINE="false" echo " probe $base -> ret=200 online=$ONLINE" return 0 fi echo " probe $base -> NO (resp=${resp:0:80})" return 1 } WORKING_BASE="" if [ -n "${WECHAT_API_BASE:-}" ]; then echo "使用环境变量 WECHAT_API_BASE=$WECHAT_API_BASE" if probe "$WECHAT_API_BASE"; then WORKING_BASE="$WECHAT_API_BASE"; fi else # Read current config first CURRENT="" if [ -f "$CRED_FILE" ]; then CURRENT=$(node -e "try{console.log(require('$CRED_FILE').wechatApiBase||'')}catch(e){}" 2>/dev/null || echo "") fi CANDIDATES=( "$CURRENT" "http://127.0.0.1:8138" "http://127.0.0.1" "http://localhost:8138" "http://localhost" "http://8.138.37.248" "http://8.138.37.248/api/wechat-agent" ) # de-dup + drop empty for base in "${CANDIDATES[@]}"; do [ -z "$base" ] && continue [ "$base" = "$WORKING_BASE" ] && continue # skip duplicates seen=0 for done_base in "${TRIED[@]:-}"; do [ "$done_base" = "$base" ] && seen=1; done [ "$seen" = "1" ] && continue TRIED+=("$base") if probe "$base"; then WORKING_BASE="$base" break fi done fi if [ -z "$WORKING_BASE" ]; then err "所有候选 URL 都不通, 请手动确认后端地址再运行: WECHAT_API_BASE=http://your-host bash quick-start.sh" exit 1 fi ok "使用 wechatApiBase = $WORKING_BASE" # Write into credentials file mkdir -p "$OPENCLAW_DIR" node -e " const fs=require('fs'),p='$CRED_FILE'; let c={}; try{c=JSON.parse(fs.readFileSync(p,'utf8'))}catch(e){}; c.wechatApiBase='$WORKING_BASE'; fs.writeFileSync(p,JSON.stringify(c,null,2)); " ok "已写入 $CRED_FILE" # ---------------- 5. Start our daemon ---------------- section "5. 启动 v1.2.0 daemon" if [ ! -f "$DAEMON_DST" ]; then err "daemon 未部署到 $DAEMON_DST, 请检查上一步" exit 1 fi mkdir -p "$(dirname "$LOG_FILE")" # Append separator to log echo "" >> "$LOG_FILE" echo "=== quick-start $(date -Iseconds) ===" >> "$LOG_FILE" nohup node "$DAEMON_DST" >> "$LOG_FILE" 2>&1 & NEW_PID=$! echo "$NEW_PID" > "$PID_FILE" sleep 2 if kill -0 "$NEW_PID" 2>/dev/null; then ok "daemon 已启动, PID=$NEW_PID" else err "daemon 启动后立即退出, 看日志排查:" tail -n 30 "$LOG_FILE" exit 1 fi # ---------------- 6. Watch log for 15 seconds ---------------- section "6. 观察日志 15 秒 (期望看到 '配置载入' + 至少一次 get-messages)" sleep 15 echo "" tail -n 30 "$LOG_FILE" echo "" if grep -q 'daemon start' "$LOG_FILE" && ! grep -q 'fatal\|uncaughtException' "$LOG_FILE"; then section "完成" ok "daemon 健康运行中, PID=$NEW_PID" echo "" echo "下一步:" echo " 1) 让测试微信号给这台机器登录的微信发条消息" echo " 2) 持续观察: tail -f $LOG_FILE" echo " 3) 改规则: vim $OPENCLAW_DIR/wechat-auto-reply-config.json (热加载)" echo " 4) 停服务: kill \$(cat $PID_FILE)" echo "" echo "如果测试消息发了但日志里没有 'received' -> 看后端 /messages 接口是否正常" echo "如果有 'received' 但没有 'replied' -> 看 'skip' / 'cooldown' / 'no-reply-rule' 原因" else warn "daemon 启动但日志异常, 请把上面的日志贴出来排查" fi