Files
ai-team-dashboard/dashboard/03-任务脚本/poll-tasks.sh
fang 5f14174bb9 feat: 添加 Dashboard 完整日志监控系统 v1.1.0
 新增功能
- 完整的日志记录系统(6 种日志级别)
- 日志配置功能(可通过 config.json 控制)
- 性能监控装饰器和请求日志中间件
- 7 个管理工具脚本
- 完整的文档和使用指南

🛠️ 管理工具
- start-with-log.sh: 启动脚本(带日志)
- stop-dashboard.sh: 停止脚本
- view-logs.sh: 日志查看器
- monitor-logs.sh: 实时监控工具(支持多种过滤器)
- analyze-logs.sh: 日志分析工具(自动生成报告)
- demo-logging.sh: 功能演示脚本
- test-logging-config.sh: 配置测试工具

📊 日志特性
- 支持 INFO/SUCCESS/WARN/ERROR/DEBUG/PERF 6 种级别
- 自动记录启动过程、API 请求、性能统计
- 缓存命中情况追踪
- 分步性能监控
- 智能过滤器

⚙️ 配置功能
- 可控制是否启用日志(默认:true)
- 可设置日志级别(默认:INFO)
- 可控制文件/控制台输出
- 支持动态配置(重启生效)

📚 文档
- LOGGING_GUIDE.md: 完整使用指南
- LOGGING_CONFIG.md: 配置说明文档
- LOGGING_CONFIG_QUICK.md: 快速配置指南
- 多个中文说明文档

🔒 安全
- 添加 .gitignore 排除敏感信息
- config.json(含 Token)不提交
- 日志文件不提交
- 示例配置使用占位符

 测试
- 语法检查通过
- 功能完整性验证
- 配置控制测试通过
- 文档完整性检查

详见 CHANGELOG_v1.1.0.md

Made-with: Cursor
2026-03-11 11:37:35 +08:00

399 lines
12 KiB
Bash
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
# GitHub 任务轮询 — 迭代循环模式
# 每次轮询只处理 checklist 中的一项,直到全部完成
# 完成后自动交接给大龙虾(项目经理)
REPO="$GITHUB_REPO"
BOT="$BOT_NAME"
ROLE="$BOT_ROLE"
CODE_REPO_DIR="/home/node/code-repo"
SKILLS_REPO_DIR="/home/node/skills-repo"
WORKSPACE="/home/node/.openclaw/workspace"
SKILLS_DIR="/home/node/.openclaw/skills"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "[$TIMESTAMP] $BOT 开始轮询任务..."
health=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/healthz 2>/dev/null)
if [ "$health" != "200" ]; then
echo "⚠️ Gateway 未就绪 (HTTP $health), 跳过本轮"
exit 0
fi
# ── 同步技能仓的新技能 ──
if [ -d "$SKILLS_REPO_DIR/.git" ]; then
cd "$SKILLS_REPO_DIR" && git pull --rebase 2>/dev/null
for f in "$SKILLS_REPO_DIR"/*.md; do
[ -f "$f" ] && cp "$f" "$SKILLS_DIR/" 2>/dev/null
done
fi
# ── 1. 先找 in-progress 的任务(上次没做完的) ──
issue_json=$(gh issue list -R "$REPO" -l "status:in-progress" -l "role:${BOT}" \
--json number,title,body -q '.[0]' 2>/dev/null)
# ── 2. 没有 in-progress 就找 pending 的 ──
if [ -z "$issue_json" ] || [ "$issue_json" = "null" ]; then
issue_json=$(gh issue list -R "$REPO" -l "status:pending" -l "role:${BOT}" \
--json number,title,body -q '.[0]' 2>/dev/null)
if [ -z "$issue_json" ] || [ "$issue_json" = "null" ]; then
echo "📭 没有待处理任务"
exit 0
fi
number=$(echo "$issue_json" | jq -r '.number')
gh issue edit "$number" -R "$REPO" \
--remove-label "status:pending" \
--add-label "status:in-progress" 2>/dev/null
echo "📋 领取新任务 #$number"
else
echo "🔄 继续处理进行中任务"
fi
number=$(echo "$issue_json" | jq -r '.number')
title=$(echo "$issue_json" | jq -r '.title')
body=$(echo "$issue_json" | jq -r '.body')
echo "📋 任务 #$number: $title"
# ── 3. 确定当前阶段,只解析对应 section 的 checklist ──
current_phase="unknown"
section_keyword=""
if echo "$body" | grep -q '\*\*phase:design\*\*'; then
current_phase="design"
section_keyword="需求清单"
elif echo "$body" | grep -q '\*\*phase:coding\*\*'; then
current_phase="coding"
section_keyword="编码清单"
elif echo "$body" | grep -q '\*\*phase:review\*\*'; then
current_phase="review"
section_keyword="测试清单"
fi
next_item=""
next_item_num=0
section_item_num=0
total_items=0
done_items=0
global_count=0
in_target_section=0
while IFS= read -r line; do
if echo "$line" | grep -qi "$section_keyword"; then
in_target_section=1
elif echo "$line" | grep -qE '^## |^---'; then
if [ $in_target_section -eq 1 ] && [ $total_items -gt 0 ]; then
in_target_section=0
fi
fi
if echo "$line" | grep -qE '^\- \[[ x]\] [0-9]+\.'; then
global_count=$((global_count + 1))
if [ $in_target_section -eq 1 ]; then
total_items=$((total_items + 1))
if echo "$line" | grep -qE '^\- \[x\]'; then
done_items=$((done_items + 1))
elif [ -z "$next_item" ]; then
next_item=$(echo "$line" | sed 's/^- \[ \] [0-9]*\.\s*//')
next_item_num=$global_count
section_item_num=$total_items
fi
fi
fi
done <<< "$body"
if [ $total_items -eq 0 ]; then
echo "⚠️ 当前阶段 ($current_phase) 没有 checklist按整体任务处理"
next_item="$body"
total_items=1
next_item_num=1
section_item_num=1
fi
if [ -z "$next_item" ] && [ $total_items -gt 0 ]; then
echo "✅ 当前阶段所有项目已完成 ($done_items/$total_items)"
phase_label="设计"
[ "$current_phase" = "coding" ] && phase_label="编码"
[ "$current_phase" = "review" ] && phase_label="测试"
echo "📌 ${phase_label}阶段全部完成,交接给大龙虾"
gh issue edit "$number" -R "$REPO" \
--remove-label "role:${BOT}" \
--add-label "role:dalongxia" 2>/dev/null
tmphandoff=$(mktemp /tmp/issue-handoff-XXXXXX.md)
cat > "$tmphandoff" << HANDOFF_EOF
🦞 **${phase_label}阶段全部完成** ($done_items/$total_items) — 交接给大龙虾
**执行者**: ${BOT}
**完成阶段**: ${current_phase}
**完成项数**: ${done_items}/${total_items}
请大龙虾(项目经理):
1. 审查本阶段成果
2. 分析整体项目进度
3. 决定下一步安排(编写下阶段清单、指派执行者)
HANDOFF_EOF
gh issue comment "$number" -R "$REPO" --body-file "$tmphandoff" 2>/dev/null
rm -f "$tmphandoff"
exit 0
fi
echo "🎯 处理第 $section_item_num/$total_items 项: $next_item"
tmpstart=$(mktemp /tmp/issue-start-XXXXXX.md)
cat > "$tmpstart" << START_EOF
🔄 **${BOT}** 开始处理第 **$section_item_num/$total_items** 项:
> $next_item
⏰ $TIMESTAMP
START_EOF
gh issue comment "$number" -R "$REPO" --body-file "$tmpstart" 2>/dev/null
rm -f "$tmpstart"
# ── 4. 构建 prompt 给 bot ──
phase_instruction=""
case "$current_phase" in
design)
phase_instruction="你当前处于【设计阶段】。请对以下需求进行详细的技术设计包括数据模型、API 接口设计、技术方案、架构说明。输出要具体、可执行。"
;;
coding)
phase_instruction="你当前处于【编码阶段】。请按照设计方案完成以下编码任务。代码必须完整、可运行、有注释。所有代码文件写到工作目录。"
;;
review)
phase_instruction="你当前处于【测试/审查阶段】。请检查以下测试条目,验证功能是否正确,输出测试报告。如果发现问题,详细说明问题位置和修复建议。"
;;
*)
phase_instruction="请认真完成以下任务。"
;;
esac
task_prompt="你是 ${BOT},角色是 ${ROLE}
## 当前任务
项目: ${title} (Issue #${number})
当前阶段: ${current_phase}
当前进度: ${done_items}/${total_items} 已完成
## 本次要处理的项目(第 ${section_item_num} 项)
${next_item}
## 工作指引
${phase_instruction}
## 项目完整背景
${body}
## 重要规则
1. 你只需处理上面【第 ${section_item_num} 项】,不要做其他项
2. 完成后给出完整的交付物
3. 如果遇到问题无法解决,说明具体卡在哪里
4. 如果学到了新技能,在 /home/node/.openclaw/skills/ 创建 .md 记录"
echo "🧠 发送任务到 AI..."
start_time=$(date +%s)
result=$(openclaw agent \
--message "$task_prompt" \
--session-id "github-task-$number-item-$next_item_num" \
--thinking medium \
--timeout 300 \
--json 2>&1) || true
end_time=$(date +%s)
duration=$(( end_time - start_time ))
response=$(echo "$result" | jq -r '.response // empty' 2>/dev/null)
if [ -z "$response" ]; then
response=$(echo "$result" | jq -r '.text // empty' 2>/dev/null)
fi
if [ -z "$response" ]; then
response=$(echo "$result" | jq -r '.content // empty' 2>/dev/null)
fi
if [ -z "$response" ]; then
response="$result"
fi
# ── 5. 代码推送 ──
commit_url=""
if [ -d "$CODE_REPO_DIR/.git" ]; then
task_dir="$CODE_REPO_DIR/task-${number}"
mkdir -p "$task_dir"
has_code=false
for f in "$WORKSPACE"/*.py "$WORKSPACE"/*.js "$WORKSPACE"/*.ts "$WORKSPACE"/*.html \
"$WORKSPACE"/*.css "$WORKSPACE"/*.json "$WORKSPACE"/*.go "$WORKSPACE"/*.rs \
"$WORKSPACE"/*.java "$WORKSPACE"/*.swift "$WORKSPACE"/*.sh "$WORKSPACE"/*.md \
"$WORKSPACE"/*.yaml "$WORKSPACE"/*.yml "$WORKSPACE"/*.toml "$WORKSPACE"/*.sql \
"$WORKSPACE"/*.jsx "$WORKSPACE"/*.tsx "$WORKSPACE"/*.vue "$WORKSPACE"/*.wxml \
"$WORKSPACE"/*.wxss; do
if [ -f "$f" ]; then
cp "$f" "$task_dir/" 2>/dev/null
has_code=true
fi
done
for d in "$WORKSPACE"/*/; do
if [ -d "$d" ] && [ "$(basename "$d")" != "." ]; then
dname=$(basename "$d")
case "$dname" in
node_modules|.git|__pycache__|.openclaw|.cache|plugins) continue ;;
esac
cp -r "$d" "$task_dir/" 2>/dev/null
has_code=true
fi
done
if [ "$has_code" = true ]; then
cd "$CODE_REPO_DIR"
git add -A 2>/dev/null
commit_msg="task-${number} item-${next_item_num}: ${next_item}
执行者: ${BOT} (${ROLE})
进度: ${next_item_num}/${total_items}
耗时: ${duration}s"
git commit -m "$commit_msg" 2>/dev/null
git push origin main 2>/dev/null && echo "📦 代码已推送"
commit_hash=$(git rev-parse --short HEAD 2>/dev/null)
commit_url="https://github.com/${GITHUB_OWNER}/${CODE_REPO}/commit/${commit_hash}"
fi
fi
# ── 6. 技能推送 ──
skills_url=""
if [ -d "$SKILLS_REPO_DIR/.git" ]; then
has_new_skills=false
for f in "$SKILLS_DIR"/*.md; do
if [ -f "$f" ]; then
fname=$(basename "$f")
if [ "$fname" = "github-task-worker.md" ]; then continue; fi
if [ ! -f "$SKILLS_REPO_DIR/$fname" ] || ! diff -q "$f" "$SKILLS_REPO_DIR/$fname" >/dev/null 2>&1; then
cp "$f" "$SKILLS_REPO_DIR/" 2>/dev/null
has_new_skills=true
fi
fi
done
if [ "$has_new_skills" = true ]; then
cd "$SKILLS_REPO_DIR"
git add -A 2>/dev/null
git commit -m "skill: 新增/更新技能 (task-${number} item-${next_item_num})" 2>/dev/null
git push origin main 2>/dev/null && echo "🧠 技能已推送"
skill_hash=$(git rev-parse --short HEAD 2>/dev/null)
skills_url="https://github.com/${GITHUB_OWNER}/${SKILLS_REPO}/commit/${skill_hash}"
fi
fi
# ── 7. 更新 Issue body勾选已完成项 + 更新当前阶段进度 ──
new_done=$((done_items + 1))
updated_body=$(echo "$body" | awk -v item_num="$next_item_num" '
BEGIN { count = 0 }
/^- \[[ x]\] [0-9]+\./ {
count++
if (count == item_num) {
sub(/^- \[ \]/, "- [x]")
}
}
{ print }
')
if [ "$current_phase" = "design" ]; then
updated_body=$(echo "$updated_body" | sed "s/设计: [0-9]*\/[0-9]*/设计: ${new_done}\/${total_items}/")
elif [ "$current_phase" = "coding" ]; then
updated_body=$(echo "$updated_body" | sed "s/编码: [0-9]*\/[0-9]*/编码: ${new_done}\/${total_items}/")
elif [ "$current_phase" = "review" ]; then
updated_body=$(echo "$updated_body" | sed "s/测试: [0-9]*\/[0-9]*/测试: ${new_done}\/${total_items}/")
fi
tmpbody=$(mktemp /tmp/issue-body-XXXXXX.md)
printf '%s' "$updated_body" > "$tmpbody"
gh issue edit "$number" -R "$REPO" --body-file "$tmpbody" 2>/dev/null
edit_rc=$?
rm -f "$tmpbody"
if [ $edit_rc -ne 0 ]; then
echo "⚠️ Issue body 更新失败 (rc=$edit_rc)"
fi
echo "📝 已勾选第 $section_item_num 项,进度: $new_done/$total_items"
# ── 8. 评论交付结果 ──
max_len=60000
if [ ${#response} -gt $max_len ]; then
response="${response:0:$max_len}
...(输出过长,已截断)"
fi
finish_time=$(date '+%Y-%m-%d %H:%M:%S')
extra_section=""
if [ -n "$commit_url" ]; then
extra_section="
**📦 代码已提交**: [查看 commit](${commit_url})"
fi
if [ -n "$skills_url" ]; then
extra_section="${extra_section}
**🧠 技能已更新**: [查看 commit](${skills_url})"
fi
tmpcomment=$(mktemp /tmp/issue-comment-XXXXXX.md)
cat > "$tmpcomment" << COMMENT_EOF
## ✅ 第 ${section_item_num}/${total_items} 项完成
**执行者**: ${BOT} (${ROLE})
**完成项**: ${next_item}
**进度**: ${new_done}/${total_items}
**耗时**: ${duration} 秒
**完成时间**: ${finish_time}${extra_section}
---
${response}
COMMENT_EOF
gh issue comment "$number" -R "$REPO" --body-file "$tmpcomment" 2>/dev/null
comment_rc=$?
rm -f "$tmpcomment"
if [ $comment_rc -ne 0 ]; then
echo "⚠️ 完成评论发布失败 (rc=$comment_rc)"
fi
# ── 9. 判断是否全部完成 → 交接给大龙虾 ──
if [ $new_done -ge $total_items ]; then
echo "🎉 当前阶段所有项目已完成!交接给大龙虾"
phase_label="设计"
[ "$current_phase" = "coding" ] && phase_label="编码"
[ "$current_phase" = "review" ] && phase_label="测试"
gh issue edit "$number" -R "$REPO" \
--remove-label "role:${BOT}" \
--add-label "role:dalongxia" 2>/dev/null
tmphandoff=$(mktemp /tmp/issue-handoff-XXXXXX.md)
cat > "$tmphandoff" << HANDOFF_EOF
🦞 **${phase_label}阶段全部完成** ($total_items/$total_items) — 交接给大龙虾
**执行者**: ${BOT}
**完成阶段**: ${current_phase}
**完成项数**: ${total_items}/${total_items}
请大龙虾(项目经理):
1. 审查本阶段成果
2. 分析整体项目进度
3. 决定下一步安排(编写下阶段清单、指派执行者)
HANDOFF_EOF
gh issue comment "$number" -R "$REPO" --body-file "$tmphandoff" 2>/dev/null
rm -f "$tmphandoff"
else
echo "📌 还剩 $((total_items - new_done)) 项未完成,等待下次轮询"
fi
echo "✅ 第 $section_item_num/$total_items 项处理完成 (耗时 ${duration}s)"