Commit aec9ecd5db8dce08fe488aaa4363c2b20457e4f6
1 parent
0e8f419d
fix: keepalive 单实例守护 + HERMES_TIMEOUT 180s
#1 HERMES_TIMEOUT 120s → 180s
Moonshot kimi-k2.6 在 turns=9 长 context 下 LLM call 可达 60s+,
旧的 2 分钟整体超时偶发不够,导致私聊 / 群聊回复直接 socket hang up。
keepalive.sh 内联 HERMES_TIMEOUT=180000 启动 node。
#2 keepalive 直起 node,绕过 start-ar.sh
phone 上某个 claude-run/hermes 会话有 shell snapshot hook,每次
start-ar.sh 通过 dash 启动时触发它写 /data/local/tmp/hermes-snap-*.sh,
Permission denied 错误被某种机制注入到 hermes-wechat/*.js 末尾,
下一次 node 加载就 SyntaxError 起不来。
修法:keepalive 用 `su -c 'cd X && env... nohup node auto-reply.js & echo $!'`
直接起 node,不经 dash 包装层。
#3 is_auto_reply_running 的两个 bug
- 第一版 pgrep -f node.*auto-reply.js: regex 在 su shell cmdline 里
自匹配('su -c "..."' 包含 pattern),永远返 true,永远不重启
- 第二版用 PID 文件 + grep /proc/PID/comm: 但 echo $! 写的是 sh
wrapper PID(comm=sh),不是 node 子进程 PID,永远返 false → 暴 spawn
- 终版:ps -ef + grep -E node.auto-reply.js + grep -v sh.-c
精确匹配 "node auto-reply.js" 实进程(含空格),排除 sh wrapper line。
pattern 里用 . 占位避开嵌套引号问题(双引号外/单引号内/转义都崩)
实测:keepalive 起 1 次后稳定 45s+,loop tick 不再误重启;
auto-reply.js 单实例(不再 4 个并行)。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Showing
1 changed file
with
34 additions
and
8 deletions
wechat_plain/scripts/local-testkit/keepalive.sh
| @@ -22,9 +22,13 @@ export HOME="/data/data/com.termux/files/home" | @@ -22,9 +22,13 @@ export HOME="/data/data/com.termux/files/home" | ||
| 22 | HERMES_DIR="${HERMES_DIR:-$HOME/hermes-wechat}" | 22 | HERMES_DIR="${HERMES_DIR:-$HOME/hermes-wechat}" |
| 23 | HERMES_PORT="${HERMES_PORT:-8642}" | 23 | HERMES_PORT="${HERMES_PORT:-8642}" |
| 24 | GATEWAY_START="${GATEWAY_START:-$HOME/.hermes/scripts/start-gateway.sh}" | 24 | GATEWAY_START="${GATEWAY_START:-$HOME/.hermes/scripts/start-gateway.sh}" |
| 25 | -AR_START="${AR_START:-$HERMES_DIR/start-ar.sh}" | ||
| 26 | A11Y_SVC="com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService" | 25 | A11Y_SVC="com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService" |
| 27 | CHECK_INTERVAL_SEC="${CHECK_INTERVAL_SEC:-15}" | 26 | CHECK_INTERVAL_SEC="${CHECK_INTERVAL_SEC:-15}" |
| 27 | +# auto-reply 启动用的 env(避开 start-ar.sh 的 shell hook 污染) | ||
| 28 | +MY_WXID="${MY_WXID:-wxid_51ap3m6p9xs722}" | ||
| 29 | +MY_NICKNAME="${MY_NICKNAME:-小火子}" | ||
| 30 | +HERMES_TIMEOUT_MS="${HERMES_TIMEOUT_MS:-180000}" | ||
| 31 | +HISTORY_CONTEXT_SIZE="${HISTORY_CONTEXT_SIZE:-8}" | ||
| 28 | 32 | ||
| 29 | log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } | 33 | log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } |
| 30 | 34 | ||
| @@ -81,23 +85,45 @@ start_gateway() { | @@ -81,23 +85,45 @@ start_gateway() { | ||
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | # ── 4. auto-reply.js ───────────────────────────────────────────── | 87 | # ── 4. auto-reply.js ───────────────────────────────────────────── |
| 88 | +AR_PID_FILE="$HERMES_DIR/.auto-reply.pid" | ||
| 89 | + | ||
| 84 | is_auto_reply_running() { | 90 | is_auto_reply_running() { |
| 85 | - pgrep -f "node.*auto-reply.js" >/dev/null 2>&1 | 91 | + # 不能用 pgrep -f node.*auto-reply.js — 它会自匹配 su shell 自己的 cmdline。 |
| 92 | + # 用 ps -ef + grep 精确匹配 'node auto-reply.js'(含空格,排除 sh wrapper)。 | ||
| 93 | + # su 提权因为 auto-reply 跑在 root 下,u0_a327 看不到 root 进程的 ps。 | ||
| 94 | + # 用 . 占位(任何单字符)避开嵌套引号问题 | ||
| 95 | + su -c "ps -ef 2>/dev/null | grep -E node.auto-reply.js | grep -v sh.-c | grep -v grep" \ | ||
| 96 | + 2>/dev/null | grep -q . | ||
| 86 | } | 97 | } |
| 87 | 98 | ||
| 88 | start_auto_reply() { | 99 | start_auto_reply() { |
| 89 | log "Starting auto-reply..." | 100 | log "Starting auto-reply..." |
| 90 | - if [ -x "$AR_START" ]; then | ||
| 91 | - # auto-reply 走 root 才能 input keyevent / am broadcast / 读微信 DB | ||
| 92 | - su -c "nohup bash $AR_START > /dev/null 2>&1 &" | ||
| 93 | - else | ||
| 94 | - log "[ERROR] $AR_START not found" | 101 | + # 不走 start-ar.sh wrapper(其 shebang 走 dash 后会触发 phone 上某个 |
| 102 | + # claude-run/hermes 会话的 shell snapshot hook,污染 hermes-wechat/*.js)。 | ||
| 103 | + # 直接以 root 起 node + 内联 env,跨过那个 shell session。 | ||
| 104 | + # 起完后用 pgrep + grep 严格匹配 cmdline 确切包含 'node auto-reply.js' | ||
| 105 | + # (不能用 pgrep -f 因为正则 .* 会自匹配 su shell 本身),写入 PID 文件 | ||
| 106 | + su -c "cd $HERMES_DIR && \ | ||
| 107 | + MY_WXID=${MY_WXID:-wxid_51ap3m6p9xs722} \ | ||
| 108 | + MY_NICKNAME=\"${MY_NICKNAME:-小火子}\" \ | ||
| 109 | + HERMES_HOST=127.0.0.1 \ | ||
| 110 | + HERMES_PORT=$HERMES_PORT \ | ||
| 111 | + HERMES_TIMEOUT=${HERMES_TIMEOUT_MS:-180000} \ | ||
| 112 | + NOTIFICATION_ENABLED=true \ | ||
| 113 | + HISTORY_CONTEXT_SIZE=${HISTORY_CONTEXT_SIZE:-8} \ | ||
| 114 | + nohup /data/data/com.termux/files/usr/bin/node auto-reply.js \ | ||
| 115 | + > $HERMES_DIR/auto-reply.log 2>&1 < /dev/null & | ||
| 116 | + echo \$! > $AR_PID_FILE" | ||
| 117 | + sleep 2 | ||
| 118 | + if [ -f "$AR_PID_FILE" ]; then | ||
| 119 | + log "Auto-reply started PID=$(cat $AR_PID_FILE 2>/dev/null)" | ||
| 95 | fi | 120 | fi |
| 96 | } | 121 | } |
| 97 | 122 | ||
| 98 | # ── 5. 主循环 ──────────────────────────────────────────────────── | 123 | # ── 5. 主循环 ──────────────────────────────────────────────────── |
| 99 | log "=== Keepalive started ===" | 124 | log "=== Keepalive started ===" |
| 100 | -log "HERMES_DIR=$HERMES_DIR GATEWAY_START=$GATEWAY_START AR_START=$AR_START" | 125 | +log "HERMES_DIR=$HERMES_DIR GATEWAY_START=$GATEWAY_START" |
| 126 | +log "MY_WXID=$MY_WXID MY_NICKNAME=$MY_NICKNAME HERMES_TIMEOUT=${HERMES_TIMEOUT_MS}ms" | ||
| 101 | log "Check interval: ${CHECK_INTERVAL_SEC}s" | 127 | log "Check interval: ${CHECK_INTERVAL_SEC}s" |
| 102 | 128 | ||
| 103 | # 首次进入:把所有依赖拉到位 | 129 | # 首次进入:把所有依赖拉到位 |