功能(ClaudeCode): 新增飞书联动工具链与会话管理增强

- 新增命令: lark-delegate(跨项目委派)、lark-inbox(收件箱)、lark-me(自发送)、stop(任务收尾)
- 新增工具: lark-consumer/consumer-ensure/inbox-check(飞书事件实时消费与会话隔离)、tmux-monitor(监控面板)
- runcc.sh 增加 --list/--kill/--send 操作模式与多会话自增命名
- CLAUDE.md 增加强制要求(汇报格式、收尾通知、上下文压缩)
This commit is contained in:
2026-07-03 14:49:44 +08:00
parent cd6a130b29
commit 911503d997
11 changed files with 1455 additions and 39 deletions
+144
View File
@@ -0,0 +1,144 @@
#!/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 退出),消费者结束"