239 lines
10 KiB
YAML
239 lines
10 KiB
YAML
name: "🧬 铸渊 · 双端神经系统 · 每日汇总引擎"
|
||
|
||
on:
|
||
schedule:
|
||
- cron: '0 13 * * *' # UTC 13:00 = CST 21:00
|
||
workflow_dispatch: # 手动触发
|
||
|
||
permissions:
|
||
contents: write
|
||
actions: read
|
||
|
||
jobs:
|
||
daily-digest:
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 15
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
# ━━━ Phase 1: 唤醒核心大脑 ━━━
|
||
- name: "🧠 唤醒核心大脑"
|
||
run: |
|
||
echo "🧬 双端神经系统·汇总引擎启动"
|
||
echo "时间:$(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M CST')"
|
||
for f in memory.json routing-map.json; do
|
||
[ -f ".github/persona-brain/$f" ] && echo "✅ $f" || echo "❌ $f 缺失"
|
||
done
|
||
|
||
# ━━━ Phase 2: 收集全 Workflow 运行数据 ━━━
|
||
- name: "📊 收集 Workflow 运行数据"
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
run: |
|
||
echo "📊 收集最近24h所有Workflow运行数据..."
|
||
mkdir -p /tmp/neural-digest
|
||
|
||
gh run list --limit 100 \
|
||
--json name,status,conclusion,createdAt,updatedAt,headBranch,event \
|
||
> /tmp/neural-digest/all-runs.json
|
||
|
||
node -e "
|
||
const runs = require('/tmp/neural-digest/all-runs.json');
|
||
const now = Date.now();
|
||
const h24 = 24*60*60*1000;
|
||
const recent = runs.filter(r => now - new Date(r.createdAt).getTime() < h24);
|
||
const summary = {
|
||
total: recent.length,
|
||
success: recent.filter(r => r.conclusion === 'success').length,
|
||
failure: recent.filter(r => r.conclusion === 'failure').length,
|
||
cancelled: recent.filter(r => r.conclusion === 'cancelled').length,
|
||
in_progress: recent.filter(r => r.status === 'in_progress').length,
|
||
by_workflow: {}
|
||
};
|
||
recent.forEach(r => {
|
||
if (!summary.by_workflow[r.name]) {
|
||
summary.by_workflow[r.name] = { runs: 0, success: 0, failure: 0 };
|
||
}
|
||
summary.by_workflow[r.name].runs++;
|
||
if (r.conclusion === 'success') summary.by_workflow[r.name].success++;
|
||
if (r.conclusion === 'failure') summary.by_workflow[r.name].failure++;
|
||
});
|
||
require('fs').writeFileSync('/tmp/neural-digest/workflow-summary.json', JSON.stringify(summary, null, 2));
|
||
console.log('最近24h运行统计:', JSON.stringify(summary, null, 2));
|
||
"
|
||
|
||
# ━━━ Phase 3: 收集各 Workflow 自报告 ━━━
|
||
- name: "📋 收集各 Workflow 自报告"
|
||
run: |
|
||
echo "📋 读取各 Workflow 的神经报告..."
|
||
mkdir -p /tmp/neural-digest/reports
|
||
|
||
if [ -f "skyeye/neural-map.json" ]; then
|
||
node -e "
|
||
const map = require('./skyeye/neural-map.json');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const reports = {};
|
||
for (const [id, wf] of Object.entries(map.github_workflows)) {
|
||
const reportDir = wf.report_path;
|
||
if (fs.existsSync(reportDir)) {
|
||
const files = fs.readdirSync(reportDir)
|
||
.filter(f => f.endsWith('.json'))
|
||
.sort().reverse();
|
||
if (files.length > 0) {
|
||
try {
|
||
reports[id] = JSON.parse(fs.readFileSync(path.join(reportDir, files[0]), 'utf8'));
|
||
console.log('✅ ' + id + ': 读取 ' + files[0]);
|
||
} catch(e) {
|
||
reports[id] = { error: 'parse_failed', file: files[0] };
|
||
console.log('❌ ' + id + ': JSON解析失败');
|
||
}
|
||
} else {
|
||
reports[id] = { error: 'no_reports' };
|
||
console.log('⚠️ ' + id + ': 无报告文件');
|
||
}
|
||
} else {
|
||
reports[id] = { error: 'dir_not_found' };
|
||
console.log('⚠️ ' + id + ': 报告目录不存在');
|
||
}
|
||
}
|
||
fs.writeFileSync('/tmp/neural-digest/workflow-reports.json', JSON.stringify(reports, null, 2));
|
||
"
|
||
else
|
||
echo "⚠️ neural-map.json 不存在,跳过自报告收集"
|
||
fi
|
||
|
||
# ━━━ Phase 4: 收集天眼 + Guard + 配额 ━━━
|
||
- name: "🦅 收集天眼 + Guard + 配额"
|
||
run: |
|
||
# 天眼报告
|
||
if [ -d "data/skyeye-reports" ]; then
|
||
LATEST=$(ls -t data/skyeye-reports/*.json 2>/dev/null | head -1)
|
||
[ -n "$LATEST" ] && cp "$LATEST" /tmp/neural-digest/skyeye-latest.json && echo "✅ 天眼: $LATEST" || echo "⚠️ 无天眼报告"
|
||
fi
|
||
# Guard 状态
|
||
if [ -d "skyeye/guards" ]; then
|
||
node -e "
|
||
const fs = require('fs'); const path = require('path');
|
||
const guards = {};
|
||
fs.readdirSync('skyeye/guards')
|
||
.filter(f => f.endsWith('.json') && f !== 'guard-template.json')
|
||
.forEach(f => {
|
||
try { guards[f.replace('.json','')] = JSON.parse(fs.readFileSync(path.join('skyeye/guards', f), 'utf8')); }
|
||
catch(e) { guards[f.replace('.json','')] = { error: 'parse_failed' }; }
|
||
});
|
||
fs.writeFileSync('/tmp/neural-digest/guard-status.json', JSON.stringify(guards, null, 2));
|
||
"
|
||
fi
|
||
# 配额
|
||
[ -f "skyeye/quota-ledger.json" ] && cp skyeye/quota-ledger.json /tmp/neural-digest/quota-status.json && echo "✅ 配额" || echo "⚠️ 无配额"
|
||
|
||
# ━━━ Phase 5: 生成标准化日报 ━━━
|
||
- name: "📝 生成标准化日报"
|
||
run: node scripts/neural/generate-daily-digest.js
|
||
|
||
# ━━━ Phase 6: 保存日报到仓库 ━━━
|
||
- name: "💾 保存日报"
|
||
run: |
|
||
DATE=$(TZ=Asia/Shanghai date +%Y-%m-%d)
|
||
mkdir -p data/neural-reports/daily-digest
|
||
cp /tmp/neural-digest/daily-digest.json \
|
||
"data/neural-reports/daily-digest/digest-${DATE}.json"
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git add data/neural-reports/
|
||
git diff --cached --quiet || \
|
||
git commit -m "🧬 双端日报 · ${DATE} [skip ci]"
|
||
git push || echo "⚠️ push失败(不阻断)"
|
||
|
||
# ━━━ Phase 6.5: Notion 指令同步(方案B · 汇总引擎附带同步)━━━
|
||
- name: "🔄 同步 Notion 指令到 memory.json"
|
||
if: always()
|
||
env:
|
||
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
|
||
DIRECTIVES_DB_ID: ${{ secrets.DIRECTIVES_DB_ID }}
|
||
run: |
|
||
echo "🔄 执行 Notion 指令同步..."
|
||
node scripts/neural/sync-notion-directives.js || \
|
||
echo "⚠️ Notion 指令同步失败(不阻断主流程)"
|
||
# 如果 memory.json 有更新,追加提交
|
||
git add .github/persona-brain/memory.json
|
||
git diff --cached --quiet || \
|
||
git commit -m "🔄 Notion 指令同步 · $(TZ=Asia/Shanghai date +%Y-%m-%d) [skip ci]"
|
||
git push || echo "⚠️ 指令同步push失败(不阻断)"
|
||
|
||
# ━━━ Phase 7: 推送到 Notion ━━━
|
||
- name: "📡 推送日报到 Notion"
|
||
if: always()
|
||
env:
|
||
NOTION_TOKEN: ${{ secrets.NOTION_API_KEY }}
|
||
run: |
|
||
node scripts/neural/sync-digest-to-notion.js || \
|
||
echo "⚠️ Notion推送失败(不阻断)"
|
||
|
||
# ━━━ Phase 8: 自动分析日报 + 生成工单 ━━━
|
||
- name: "🧬 分析日报 · 生成工单"
|
||
if: always()
|
||
run: |
|
||
echo "🧬 自动分析日报..."
|
||
node scripts/neural/analyze-digest.js
|
||
if [ -d "data/neural-reports/work-orders" ]; then
|
||
git add data/neural-reports/work-orders/
|
||
git diff --cached --quiet || \
|
||
git commit -m "🧬 天眼工单 · $(TZ=Asia/Shanghai date +%Y-%m-%d) [skip ci]"
|
||
git push || echo "⚠️ 工单push失败"
|
||
fi
|
||
|
||
# ━━━ 🧬 双端神经系统·自报告 ━━━
|
||
- name: "🧬 写入神经自报告"
|
||
if: always()
|
||
run: |
|
||
WORKFLOW_ID="neural-daily-digest"
|
||
BRAIN="AG-TY-01"
|
||
REPORT_DIR="data/neural-reports/daily-digest"
|
||
TIMESTAMP=$(TZ=Asia/Shanghai date +%Y-%m-%d-%H%M)
|
||
STATUS="${{ job.status }}"
|
||
|
||
mkdir -p "$REPORT_DIR"
|
||
|
||
cat > "${REPORT_DIR}/${WORKFLOW_ID}-${TIMESTAMP}.json" << EOF
|
||
{
|
||
"workflow_id": "${WORKFLOW_ID}",
|
||
"run_id": "${{ github.run_id }}",
|
||
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||
"status": "${STATUS}",
|
||
"brain": "${BRAIN}",
|
||
"event": "${{ github.event_name }}",
|
||
"branch": "${{ github.ref_name }}",
|
||
"commit": "${{ github.sha }}"
|
||
}
|
||
EOF
|
||
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git add "$REPORT_DIR/"
|
||
git diff --cached --quiet || \
|
||
git commit -m "🧬 ${WORKFLOW_ID} 自报告 · ${TIMESTAMP} [skip ci]"
|
||
git push || echo "⚠️ 自报告push失败(不阻断主流程)"
|
||
# ━━━ 📡 指令回执·自动同步 ━━━
|
||
- name: "📡 同步回执到 Notion"
|
||
if: always()
|
||
env:
|
||
NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }}
|
||
RECEIPT_DB_ID: ${{ secrets.RECEIPT_DB_ID }}
|
||
run: |
|
||
node scripts/neural/write-receipt-to-notion.js \
|
||
--instruction-id "${{ github.event.inputs.instruction_id || 'AUTO' }}" \
|
||
--status "${{ job.status }}" \
|
||
--workflow "neural-daily-digest" \
|
||
--summary "neural-daily-digest 自动执行" \
|
||
--related-agent "AG-TY-01"
|
||
|