290 lines
8.4 KiB
Bash
Executable File
290 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# tmux-msg-log.sh — 消息日志查询工具
|
||
#
|
||
# 用法:
|
||
# tmux-msg-log.sh recent [--count 20]
|
||
# tmux-msg-log.sh by-pane <pane_id>
|
||
# tmux-msg-log.sh show <msg_id>
|
||
# tmux-msg-log.sh stats
|
||
# tmux-msg-log.sh log <msg_id> "<status_note>"
|
||
#
|
||
# 示例:
|
||
# tmux-msg-log.sh recent
|
||
# tmux-msg-log.sh by-pane %2
|
||
# tmux-msg-log.sh show msg-20260419-142357-a1b2
|
||
# tmux-msg-log.sh stats
|
||
|
||
set -euo pipefail
|
||
|
||
MSG_DIR="${TMUX_MSG_DIR:-/workspace/.tmux-messages}"
|
||
INDEX_FILE="${MSG_DIR}/index.jsonl"
|
||
|
||
# ─── 颜色 ───
|
||
GREEN='\033[32m'
|
||
RED='\033[31m'
|
||
YELLOW='\033[33m'
|
||
CYAN='\033[36m'
|
||
BOLD='\033[1m'
|
||
RESET='\033[0m'
|
||
|
||
read_envelope() {
|
||
local msg_id="$1"
|
||
find "$MSG_DIR" -name "${msg_id}.json" -type f 2>/dev/null | head -1
|
||
}
|
||
|
||
# ─── recent 子命令 ───
|
||
|
||
cmd_recent() {
|
||
local count="${1:-20}"
|
||
|
||
if [[ ! -f "$INDEX_FILE" ]]; then
|
||
echo "暂无消息记录"
|
||
return
|
||
fi
|
||
|
||
echo -e "${BOLD}══ 最近 ${count} 条消息 ══${RESET}"
|
||
echo ""
|
||
|
||
tail -n "$count" "$INDEX_FILE" | while IFS= read -r line; do
|
||
[[ -z "$line" ]] && continue
|
||
echo "$line" | python3 -c "
|
||
import sys, json
|
||
d = json.loads(sys.stdin.read())
|
||
direction = d.get('direction', '?')
|
||
status = d.get('status', '?')
|
||
msg_id = d.get('msg_id', '?')
|
||
from_p = d.get('from', '?')
|
||
to_p = d.get('to', '?')
|
||
summary = d.get('summary', '')[:50]
|
||
ts = d.get('ts', '?')
|
||
|
||
# 状态颜色
|
||
if status in ('completed', 'delivered', 'replied'):
|
||
color = '\033[32m'
|
||
elif status in ('failed', 'send_failed', 'reply_failed'):
|
||
color = '\033[31m'
|
||
else:
|
||
color = '\033[33m'
|
||
|
||
direction_icon = '→' if direction == 'request' else '←'
|
||
print(f' {color}{status:12}\033[0m {msg_id} {direction_icon} {from_p}→{to_p}')
|
||
print(f' {summary}')
|
||
print(f' {ts}')
|
||
print()
|
||
" 2>/dev/null
|
||
done
|
||
}
|
||
|
||
# ─── by-pane 子命令 ───
|
||
|
||
cmd_by_pane() {
|
||
local pane_id="${1:?用法: tmux-msg-log.sh by-pane <pane_id>}"
|
||
|
||
if [[ ! -f "$INDEX_FILE" ]]; then
|
||
echo "暂无消息记录"
|
||
return
|
||
fi
|
||
|
||
echo -e "${BOLD}══ Pane ${pane_id} 的消息 ══${RESET}"
|
||
local count=0
|
||
while IFS= read -r line; do
|
||
[[ -z "$line" ]] && continue
|
||
local match
|
||
match=$(echo "$line" | python3 -c "
|
||
import sys, json
|
||
d = json.loads(sys.stdin.read())
|
||
if d.get('from') == '${pane_id}' or d.get('to') == '${pane_id}':
|
||
print('yes')
|
||
else:
|
||
print('no')
|
||
" 2>/dev/null)
|
||
[[ "$match" != "yes" ]] && continue
|
||
|
||
count=$((count + 1))
|
||
echo "$line" | python3 -c "
|
||
import sys, json
|
||
d = json.loads(sys.stdin.read())
|
||
direction = d.get('direction', '?')
|
||
status = d.get('status', '?')
|
||
msg_id = d.get('msg_id', '?')
|
||
from_p = d.get('from', '?')
|
||
to_p = d.get('to', '?')
|
||
summary = d.get('summary', '')[:50]
|
||
role = '发送' if d.get('from') == '${pane_id}' else '接收'
|
||
print(f' [{role}] {status:12} {msg_id} {summary}')
|
||
" 2>/dev/null
|
||
done < "$INDEX_FILE"
|
||
|
||
echo ""
|
||
echo "共 ${count} 条消息"
|
||
}
|
||
|
||
# ─── show 子命令 ───
|
||
|
||
cmd_show() {
|
||
local msg_id="${1:?用法: tmux-msg-log.sh show <msg_id>}"
|
||
|
||
local envelope_path
|
||
envelope_path=$(read_envelope "$msg_id")
|
||
if [[ -z "$envelope_path" ]]; then
|
||
echo "错误: 未找到消息 ${msg_id}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
python3 -c "
|
||
import sys, json
|
||
with open('${envelope_path}') as f:
|
||
d = json.load(f)
|
||
|
||
print('══ 消息详情 ══')
|
||
print(f'消息ID: {d[\"msg_id\"]}')
|
||
print(f'方向: {d[\"direction\"]}')
|
||
if d.get('in_reply_to'):
|
||
print(f'回复消息: {d[\"in_reply_to\"]}')
|
||
print(f'来源: {d[\"from_title\"]} ({d[\"from_pane\"]})')
|
||
print(f'目标: {d[\"to_title\"]} ({d[\"to_pane\"]})')
|
||
print(f'状态: {d[\"status\"]}')
|
||
if d.get('task_summary'):
|
||
print(f'任务摘要: {d[\"task_summary\"]}')
|
||
if d.get('task_file'):
|
||
print(f'任务文件: {d[\"task_file\"]}')
|
||
print(f'创建时间: {d[\"created_at\"]}')
|
||
if d.get('delivered_at'):
|
||
print(f'送达时间: {d[\"delivered_at\"]}')
|
||
if d.get('replied_at'):
|
||
print(f'回复时间: {d[\"replied_at\"]}')
|
||
print(f'回复摘要: {d.get(\"reply_summary\", \"\")}')
|
||
if d.get('error'):
|
||
print(f'错误: {d[\"error\"]}')
|
||
" 2>/dev/null
|
||
}
|
||
|
||
# ─── stats 子命令 ───
|
||
|
||
cmd_stats() {
|
||
if [[ ! -f "$INDEX_FILE" ]]; then
|
||
echo "暂无消息记录"
|
||
return
|
||
fi
|
||
|
||
local total=0 requests=0 replies=0
|
||
local completed=0 pending=0 failed=0
|
||
declare -A pane_counts
|
||
|
||
while IFS= read -r line; do
|
||
[[ -z "$line" ]] && continue
|
||
total=$((total + 1))
|
||
|
||
local direction status from to
|
||
direction=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('direction','?'))" 2>/dev/null)
|
||
status=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('status','?'))" 2>/dev/null)
|
||
from=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('from','?'))" 2>/dev/null)
|
||
to=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('to','?'))" 2>/dev/null)
|
||
|
||
[[ "$direction" == "request" ]] && requests=$((requests + 1))
|
||
[[ "$direction" == "reply" ]] && replies=$((replies + 1))
|
||
|
||
case "$status" in
|
||
completed|delivered|replied) completed=$((completed + 1)) ;;
|
||
created|sent|pending) pending=$((pending + 1)) ;;
|
||
failed|send_failed|reply_failed) failed=$((failed + 1)) ;;
|
||
esac
|
||
|
||
pane_counts["$from"]=$(( ${pane_counts["$from"]:-0} + 1 ))
|
||
pane_counts["$to"]=$(( ${pane_counts["$to"]:-0} + 1 ))
|
||
done < "$INDEX_FILE"
|
||
|
||
echo -e "${BOLD}══ 消息统计 ══${RESET}"
|
||
echo ""
|
||
echo " 总计: ${total}"
|
||
echo " 请求: ${requests}"
|
||
echo " 回复: ${replies}"
|
||
echo ""
|
||
echo -e " ${GREEN}完成: ${completed}${RESET} ${YELLOW}待处理: ${pending}${RESET} ${RED}失败: ${failed}${RESET}"
|
||
echo ""
|
||
|
||
echo "各面板活跃度:"
|
||
for pane in "${!pane_counts[@]}"; do
|
||
printf " %-6s %d 条\n" "$pane" "${pane_counts[$pane]}"
|
||
done
|
||
}
|
||
|
||
# ─── 主入口 ───
|
||
|
||
# ─── list 子命令 ───
|
||
|
||
cmd_list() {
|
||
local filter_from='' filter_to='' filter_status=''
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--from) filter_from="$2"; shift 2 ;;
|
||
--to) filter_to="$2"; shift 2 ;;
|
||
--status) filter_status="$2"; shift 2 ;;
|
||
*) shift ;;
|
||
esac
|
||
done
|
||
|
||
if [[ ! -f "$INDEX_FILE" ]]; then
|
||
echo "暂无消息记录"
|
||
return
|
||
fi
|
||
|
||
local count=0
|
||
while IFS= read -r line; do
|
||
[[ -z "$line" ]] && continue
|
||
if [[ -n "$filter_from" ]]; then
|
||
local from
|
||
from=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['from'])" 2>/dev/null)
|
||
[[ "$from" != "$filter_from" ]] && continue
|
||
fi
|
||
if [[ -n "$filter_to" ]]; then
|
||
local to
|
||
to=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['to'])" 2>/dev/null)
|
||
[[ "$to" != "$filter_to" ]] && continue
|
||
fi
|
||
if [[ -n "$filter_status" ]]; then
|
||
local st
|
||
st=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['status'])" 2>/dev/null)
|
||
[[ "$st" != "$filter_status" ]] && continue
|
||
fi
|
||
count=$((count + 1))
|
||
echo "$line" | python3 -c "
|
||
import sys, json
|
||
d = json.loads(sys.stdin.read())
|
||
print(f'{d[\"msg_id\"]} {d[\"status\"]:12} {d[\"from\"]}→{d[\"to\"]} {d.get(\"summary\",\"\")[:50]}')
|
||
" 2>/dev/null
|
||
done < "$INDEX_FILE"
|
||
|
||
echo ""
|
||
echo "共 ${count} 条消息"
|
||
}
|
||
|
||
# ─── 主入口 ───
|
||
|
||
cmd="${1:-help}"
|
||
shift || true
|
||
|
||
case "$cmd" in
|
||
recent) cmd_recent "$@" ;;
|
||
by-pane) cmd_by_pane "$@" ;;
|
||
show) cmd_show "$@" ;;
|
||
stats) cmd_stats ;;
|
||
list) cmd_list "$@" ;;
|
||
help|--help|-h)
|
||
echo "用法: tmux-msg-log.sh <command> [options]"
|
||
echo ""
|
||
echo "命令:"
|
||
echo " recent [--count N] 最近 N 条消息(默认 20)"
|
||
echo " by-pane <pane_id> 指定面板的消息"
|
||
echo " show <msg_id> 消息详情"
|
||
echo " list [--from <p>] [--to <p>] [--status <s>]"
|
||
echo " 列出消息(可过滤)"
|
||
echo " stats 统计摘要"
|
||
;;
|
||
*)
|
||
echo "错误: 未知命令 '$cmd'" >&2
|
||
exit 1
|
||
;;
|
||
esac
|