✨ 新增功能 - 完整的日志记录系统(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
399 lines
12 KiB
Bash
399 lines
12 KiB
Bash
#!/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)"
|