Files
tech/.claude/tools/lark-consumer.sh
T
arno 911503d997 功能(ClaudeCode): 新增飞书联动工具链与会话管理增强
- 新增命令: lark-delegate(跨项目委派)、lark-inbox(收件箱)、lark-me(自发送)、stop(任务收尾)
- 新增工具: lark-consumer/consumer-ensure/inbox-check(飞书事件实时消费与会话隔离)、tmux-monitor(监控面板)
- runcc.sh 增加 --list/--kill/--send 操作模式与多会话自增命名
- CLAUDE.md 增加强制要求(汇报格式、收尾通知、上下文压缩)
2026-07-03 14:49:44 +08:00

145 lines
5.0 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ============================================================================
# 飞书事件消费者 (纯 bash) — 从 lark-cli event consume 管道读取 NDJSON
# 去重后写入 inbox ndjson 文件,并通过 tmux send-keys 转发到当前 CC pane。
#
# 用法: lark-consumer.sh --inbox-file <路径> --cc-pane <pane_id> \
# [--submit-delay <秒>] [--diag-log <路径>] [--scope <标识>]
#
# 由 lark-consumer-ensure.sh 经 setsid 启动,全局单例运行。
# 所有配置通过命令行参数传入,避免 shell 嵌入转义问题。
#
# 环境变量:
# LARK_SENDER_ALLOW - sender open_id 白名单(优先)
# LARK_ME_USER_ID - sender open_id 白名单(fallback
# ============================================================================
set -eo pipefail
# 不使用 set -u:空关联数组在 -u 下会触发"未绑定的变量"错误
# ── 参数解析 ──
INBOX_FILE=""
CC_PANE=""
SUBMIT_DELAY="0.5"
DIAG_LOG=""
SCOPE="singleton"
while [[ $# -gt 0 ]]; do
case "$1" in
--inbox-file) INBOX_FILE="$2"; shift 2 ;;
--cc-pane) CC_PANE="$2"; shift 2 ;;
--submit-delay) SUBMIT_DELAY="$2"; shift 2 ;;
--diag-log) DIAG_LOG="$2"; shift 2 ;;
--scope) SCOPE="$2"; shift 2 ;;
*) echo "未知参数: $1" >&2; exit 1 ;;
esac
done
# ── sender 白名单(三级 fallbackLARK_SENDER_ALLOW → LARK_ME_USER_ID → 默认值)──
SENDER_ALLOW="${LARK_SENDER_ALLOW:-${LARK_ME_USER_ID:-ou_2c245a54dbea4bc2197f6d63bd940bb0}}"
# ── 日志函数 ──
diag() {
[[ -n "${DIAG_LOG}" ]] || return 0
printf '[%s] [%s] [consumer] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "${SCOPE}" "$*" >> "${DIAG_LOG}"
}
# ── 检测目标 CC pane 是否存活 ──
pane_alive() {
[[ -z "${CC_PANE}" ]] && return 0 # 非 tmux 模式,永远 true
tmux display-message -t "${CC_PANE}" -p '#{pane_id}' &>/dev/null
}
# ── 从 ndjson 行提取 JSON 字段 ──
field() {
jq -r "$1 // empty" <<< "$2" 2>/dev/null
}
# ── 加载已有 message_id 到关联数组(启动去重)──
declare -A SEEN
load_seen() {
[[ -f "${INBOX_FILE}" ]] || return 0
local line mid
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
mid=$(field '.id' "${line}") || true
[[ -n "${mid}" ]] && SEEN["${mid}"]=1
done < "${INBOX_FILE}"
}
diag "消费者 bash 进程启动 (PID=$$)"
diag "inbox=${INBOX_FILE} cc_pane=${CC_PANE} delay=${SUBMIT_DELAY} sender=${SENDER_ALLOW}"
load_seen
diag "已加载 ${#SEEN[@]} 条历史 message_id"
# 就绪信号(写入 stderr,由 ensure.sh 经 CONSUMER_LOG 检测)
echo "consumer ready (pid=$$, seen=${#SEEN[@]})" >&2
# ── 主循环:逐行读取 lark-cli 输出 ──
LAST_CHECK=$(date +%s)
CHECK_INTERVAL=30
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
# ── 定期巡检 CC pane 存活 ──
_now=$(date +%s)
if (( _now - LAST_CHECK > CHECK_INTERVAL )); then
LAST_CHECK=${_now}
if ! pane_alive; then
diag "目标 pane 已消失(CC/session 退出),消费者主动退出"
exit 0
fi
fi
# ── sender 白名单过滤 ──
_sender=$(field '.sender' "${line}") || true
if [[ -n "${SENDER_ALLOW}" && "${_sender}" != "${SENDER_ALLOW}" ]]; then
diag "跳过非白名单 sender=${_sender}"
continue
fi
# ── 去重 ──
_mid=$(field '.id' "${line}") || true
if [[ -z "${_mid}" ]]; then
diag "警告: 缺少 message_id,跳过: ${line:0:80}"
continue
fi
if [[ -n "${SEEN[${_mid}]:-}" ]]; then
continue
fi
SEEN["${_mid}"]=1
# ── 写入 inbox(保留原始多行 msg)──
printf '%s\n' "${line}" >> "${INBOX_FILE}"
diag "新消息写入 inbox: id=${_mid}"
# ── send-keys 转发到 CC pane ──
if [[ -n "${CC_PANE}" ]]; then
if ! pane_alive; then
diag "转发前检测到 pane 已消失,消费者退出"
exit 0
fi
_msg=$(field '.msg' "${line}") || true
if [[ -n "${_msg}" ]]; then
# 多行压平:避免 tmux send-keys 把换行当多次回车,导致一条消息被拆成多次提交
_flat=$(printf '%s' "${_msg}" | tr '\n' ' ' | tr -s ' ')
diag "转发 → pane=${CC_PANE} len=${#_flat}"
if ! tmux send-keys -t "${CC_PANE}" -l "${_flat}" 2>/dev/null; then
diag "转发失败: send-keys -l 返回非零,pane=${CC_PANE} 可能已不存在"
continue
fi
sleep "${SUBMIT_DELAY}"
if ! tmux send-keys -t "${CC_PANE}" Enter 2>/dev/null; then
diag "转发失败: send-keys Enter 返回非零,pane=${CC_PANE} 可能已不存在"
continue
fi
diag "转发完成"
fi
else
diag "cc_pane 为空,仅写 inbox"
fi
done
diag "stdin 已关闭(lark-cli 退出),消费者结束"