364 lines
14 KiB
YAML
364 lines
14 KiB
YAML
# ═══════════════════════════════════════════════
|
||
# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
|
||
# 📜 Copyright: 国作登字-2026-A-00037559
|
||
# ═══════════════════════════════════════════════
|
||
name: 铸渊 · SYSLOG Pipeline (A/D/E)
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- 'syslog-inbox/**'
|
||
workflow_run:
|
||
workflows: ["铸渊 · 飞书SYSLOG桥接"]
|
||
types: [completed]
|
||
workflow_dispatch:
|
||
|
||
jobs:
|
||
# ── Pipeline A:SYSLOG 读取与处理 ──────────────────────
|
||
pipeline-a-syslog:
|
||
name: 📥 Pipeline A · SYSLOG 读取处理
|
||
if: >-
|
||
github.event_name != 'workflow_run' ||
|
||
github.event.workflow_run.conclusion == 'success'
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write
|
||
issues: read
|
||
outputs:
|
||
processed_count: ${{ steps.process.outputs.processed_count }}
|
||
has_syslog: ${{ steps.parse.outputs.has_syslog }}
|
||
sender_open_id: ${{ steps.parse.outputs.sender_open_id }}
|
||
chat_id: ${{ steps.parse.outputs.chat_id }}
|
||
sender_name: ${{ steps.parse.outputs.sender_name }}
|
||
syslog_file: ${{ steps.parse.outputs.syslog_file }}
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
ref: main
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
cache: 'npm'
|
||
|
||
- name: Install dependencies
|
||
run: npm ci
|
||
|
||
# Step 0: 解析 sender 信息(在 process-syslog.js 归档之前)
|
||
- name: 📋 解析 SYSLOG sender 信息
|
||
id: parse
|
||
run: |
|
||
LATEST=$(ls -t syslog-inbox/*.json 2>/dev/null | grep -v '.gitkeep' | head -1 || true)
|
||
if [ -z "$LATEST" ]; then
|
||
echo "📭 syslog-inbox/ 无待处理文件"
|
||
echo "has_syslog=false" >> "$GITHUB_OUTPUT"
|
||
exit 0
|
||
fi
|
||
echo "has_syslog=true" >> "$GITHUB_OUTPUT"
|
||
echo "syslog_file=$LATEST" >> "$GITHUB_OUTPUT"
|
||
|
||
SENDER_OPEN_ID=$(jq -r '.sender_open_id // .sender_id // empty' "$LATEST" 2>/dev/null || true)
|
||
CHAT_ID=$(jq -r '.chat_id // empty' "$LATEST" 2>/dev/null || true)
|
||
SENDER_NAME=$(jq -r '.sender_name // empty' "$LATEST" 2>/dev/null || true)
|
||
|
||
echo "sender_open_id=$SENDER_OPEN_ID" >> "$GITHUB_OUTPUT"
|
||
echo "chat_id=$CHAT_ID" >> "$GITHUB_OUTPUT"
|
||
echo "sender_name=$SENDER_NAME" >> "$GITHUB_OUTPUT"
|
||
|
||
echo "📋 SYSLOG 文件: $LATEST"
|
||
echo " sender_open_id: ${SENDER_OPEN_ID:-无}"
|
||
echo " chat_id: ${CHAT_ID:-无}"
|
||
echo " sender_name: ${SENDER_NAME:-无}"
|
||
|
||
# Step 1: 写入 Notion SYSLOG 收件箱
|
||
- name: 📝 写入 Notion SYSLOG 收件箱
|
||
if: steps.parse.outputs.has_syslog == 'true'
|
||
env:
|
||
NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||
NOTION_DB_SYSLOG: ${{ secrets.ZY_NOTION_SYSLOG_DB }}
|
||
run: node scripts/write-notion-syslog.js "${{ steps.parse.outputs.syslog_file }}"
|
||
|
||
# Step 2: 处理 syslog-inbox 条目(归档到 syslog-processed/)
|
||
- name: 📥 处理 syslog-inbox 条目
|
||
id: process
|
||
run: |
|
||
node scripts/process-syslog.js
|
||
COUNT=$(ls syslog-processed/ -R 2>/dev/null | grep -c '\.json' || echo 0)
|
||
echo "processed_count=$COUNT" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: 提交处理结果
|
||
run: |
|
||
git config user.name "铸渊[bot]"
|
||
git config user.email "zhu-yuan-bot@guanghulab.com"
|
||
git add syslog-inbox/ syslog-processed/ .github/brain/memory.json
|
||
git diff --staged --quiet || git commit -m "📥 syslog: 处理 inbox 条目,更新大脑记忆 [skip ci]"
|
||
git push
|
||
|
||
# ── Pipeline B:Claude API 唤醒人格体 ────────────────────
|
||
pipeline-b-persona:
|
||
name: 🧠 Pipeline B · 人格体唤醒
|
||
runs-on: ubuntu-latest
|
||
needs: pipeline-a-syslog
|
||
if: needs.pipeline-a-syslog.outputs.has_syslog == 'true'
|
||
permissions:
|
||
contents: read
|
||
outputs:
|
||
broadcast_title: ${{ steps.persona.outputs.broadcast_title }}
|
||
broadcast_url: ${{ steps.persona.outputs.broadcast_url }}
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
ref: main
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
cache: 'npm'
|
||
|
||
- name: Install dependencies
|
||
run: npm ci
|
||
|
||
- name: 🧠 唤醒人格体处理 SYSLOG
|
||
id: persona
|
||
env:
|
||
LLM_API_KEY: ${{ secrets.ZY_LLM_API_KEY }}
|
||
LLM_BASE_URL: ${{ secrets.ZY_LLM_BASE_URL }}
|
||
NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||
CORE_BRAIN_PAGE_ID: ${{ secrets.ZY_NOTION_BRAIN_PAGE }}
|
||
PORTRAIT_DB_ID: ${{ secrets.ZY_NOTION_PORTRAIT_DB }}
|
||
FINGERPRINT_DB_ID: ${{ secrets.ZY_NOTION_FINGERPRINT_DB }}
|
||
SUBMIT_TYPE: syslog
|
||
SYSLOG_FILE: ${{ needs.pipeline-a-syslog.outputs.syslog_file }}
|
||
AUTHOR: ${{ needs.pipeline-a-syslog.outputs.sender_name }}
|
||
run: |
|
||
if [ -z "$LLM_API_KEY" ] || [ -z "$LLM_BASE_URL" ]; then
|
||
echo "⚠️ LLM_API_KEY 或 LLM_BASE_URL 未配置,跳过人格体唤醒"
|
||
echo "broadcast_title=" >> "$GITHUB_OUTPUT"
|
||
echo "broadcast_url=" >> "$GITHUB_OUTPUT"
|
||
exit 0
|
||
fi
|
||
|
||
# 读取 SYSLOG 文件内容传递给 wake-persona.js
|
||
# 注意:Pipeline A 可能已将文件从 syslog-inbox/ 归档到 syslog-processed/
|
||
if [ -f "$SYSLOG_FILE" ]; then
|
||
export SUBMIT_CONTENT=$(cat "$SYSLOG_FILE")
|
||
else
|
||
BASENAME=$(basename "$SYSLOG_FILE")
|
||
PROCESSED_FILE=$(find syslog-processed/ -name "$BASENAME" -type f 2>/dev/null | head -1)
|
||
if [ -n "$PROCESSED_FILE" ]; then
|
||
echo "📂 文件已归档,从 $PROCESSED_FILE 读取"
|
||
export SUBMIT_CONTENT=$(cat "$PROCESSED_FILE")
|
||
else
|
||
echo "⚠️ SYSLOG 文件未找到: $SYSLOG_FILE(inbox 和 processed 均不存在)"
|
||
fi
|
||
fi
|
||
|
||
if [ -z "$SUBMIT_CONTENT" ]; then
|
||
echo "⚠️ SUBMIT_CONTENT 为空,跳过人格体唤醒"
|
||
echo "broadcast_title=" >> "$GITHUB_OUTPUT"
|
||
echo "broadcast_url=" >> "$GITHUB_OUTPUT"
|
||
exit 0
|
||
fi
|
||
|
||
if node scripts/wake-persona.js; then
|
||
echo "✅ 人格体唤醒成功"
|
||
else
|
||
echo "⚠️ 人格体唤醒失败(exit code: $?),继续后续步骤"
|
||
fi
|
||
|
||
# 读取人格体输出的广播信息(如果有)
|
||
if [ -f /tmp/broadcast-result.json ]; then
|
||
BROADCAST_TITLE=$(jq -r '.title // empty' /tmp/broadcast-result.json)
|
||
BROADCAST_URL=$(jq -r '.notion_url // empty' /tmp/broadcast-result.json)
|
||
echo "broadcast_title=$BROADCAST_TITLE" >> "$GITHUB_OUTPUT"
|
||
echo "broadcast_url=$BROADCAST_URL" >> "$GITHUB_OUTPUT"
|
||
echo "✅ 人格体处理完成,广播标题: $BROADCAST_TITLE"
|
||
else
|
||
echo "⚠️ /tmp/broadcast-result.json 未生成(人格体可能未产出广播)"
|
||
echo "broadcast_title=" >> "$GITHUB_OUTPUT"
|
||
echo "broadcast_url=" >> "$GITHUB_OUTPUT"
|
||
fi
|
||
|
||
# ── Pipeline C:推送广播到飞书 ───────────────────────────
|
||
pipeline-c-push:
|
||
name: 📢 Pipeline C · 飞书广播推送
|
||
runs-on: ubuntu-latest
|
||
needs: [pipeline-a-syslog, pipeline-b-persona]
|
||
permissions: {}
|
||
if: >-
|
||
needs.pipeline-b-persona.outputs.broadcast_title != '' &&
|
||
(needs.pipeline-a-syslog.outputs.chat_id != '' || needs.pipeline-a-syslog.outputs.sender_open_id != '')
|
||
steps:
|
||
- name: 📢 推送广播到飞书
|
||
env:
|
||
PUSH_URL: https://guanghulab.com/webhook/push-broadcast
|
||
PUSH_TOKEN: ${{ secrets.ZY_PUSH_TOKEN }}
|
||
CHAT_ID: ${{ needs.pipeline-a-syslog.outputs.chat_id }}
|
||
SENDER_OPEN_ID: ${{ needs.pipeline-a-syslog.outputs.sender_open_id }}
|
||
BROADCAST_TITLE: ${{ needs.pipeline-b-persona.outputs.broadcast_title }}
|
||
BROADCAST_URL: ${{ needs.pipeline-b-persona.outputs.broadcast_url }}
|
||
run: |
|
||
if [ -z "$PUSH_TOKEN" ]; then
|
||
echo "⚠️ PUSH_BROADCAST_TOKEN 未配置,跳过飞书推送"
|
||
exit 0
|
||
fi
|
||
echo "📢 推送广播到飞书..."
|
||
echo " 标题: $BROADCAST_TITLE"
|
||
echo " 目标: ${CHAT_ID:-$SENDER_OPEN_ID}"
|
||
|
||
HTTP_CODE=$(curl -s -o /tmp/push-response.txt -w "%{http_code}" \
|
||
-X POST "$PUSH_URL" \
|
||
-H "Content-Type: application/json" \
|
||
-H "x-push-token: $PUSH_TOKEN" \
|
||
-d "$(jq -n \
|
||
--arg chat_id "$CHAT_ID" \
|
||
--arg sender_open_id "$SENDER_OPEN_ID" \
|
||
--arg broadcast_title "$BROADCAST_TITLE" \
|
||
--arg broadcast_content "人格体已验收,请查看完整广播。" \
|
||
--arg broadcast_url "$BROADCAST_URL" \
|
||
'{chat_id: $chat_id, sender_open_id: $sender_open_id, broadcast_title: $broadcast_title, broadcast_content: $broadcast_content, broadcast_url: $broadcast_url}')")
|
||
|
||
echo " HTTP 响应: $HTTP_CODE"
|
||
cat /tmp/push-response.txt 2>/dev/null || true
|
||
echo ""
|
||
|
||
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
|
||
echo "✅ 广播已推送到飞书"
|
||
else
|
||
echo "❌ 推送失败 (HTTP $HTTP_CODE),可通过 push-broadcast-feishu workflow 手动补推"
|
||
fi
|
||
|
||
# ── Pipeline D:Issues 巡检 ─────────────────────────────
|
||
pipeline-d-issues:
|
||
name: 🔍 Pipeline D · Issues 巡检
|
||
runs-on: ubuntu-latest
|
||
needs: pipeline-a-syslog
|
||
permissions:
|
||
contents: write
|
||
issues: read
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
ref: main
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
cache: 'npm'
|
||
|
||
- name: Install dependencies
|
||
run: npm ci
|
||
|
||
- name: 🔍 巡检 Issues
|
||
env:
|
||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
REPO: ${{ github.repository }}
|
||
run: |
|
||
echo "🔍 Pipeline D · Issues 巡检开始"
|
||
node -e "
|
||
const https = require('https');
|
||
const token = process.env.GITHUB_TOKEN;
|
||
const repo = process.env.REPO;
|
||
const opts = {
|
||
hostname: 'api.github.com',
|
||
path: '/repos/' + repo + '/issues?state=open&per_page=20',
|
||
headers: { 'Authorization': 'Bearer ' + token, 'User-Agent': 'zhuyuan-bot' }
|
||
};
|
||
https.get(opts, res => {
|
||
let body = '';
|
||
res.on('data', c => body += c);
|
||
res.on('end', () => {
|
||
const issues = JSON.parse(body);
|
||
console.log('📋 开放 Issues 数量:' + (Array.isArray(issues) ? issues.length : '读取失败'));
|
||
if (Array.isArray(issues) && issues.length > 0) {
|
||
issues.slice(0, 5).forEach(i => console.log(' #' + i.number + ' ' + i.title));
|
||
}
|
||
console.log('✅ Pipeline D 巡检完成');
|
||
});
|
||
}).on('error', e => { console.error('❌ 巡检失败:', e.message); process.exit(1); });
|
||
"
|
||
|
||
# ── Pipeline E:变更感知 ────────────────────────────────
|
||
pipeline-e-changes:
|
||
name: 📡 Pipeline E · 变更感知
|
||
runs-on: ubuntu-latest
|
||
needs: pipeline-a-syslog
|
||
permissions:
|
||
contents: write
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
token: ${{ secrets.GITHUB_TOKEN }}
|
||
ref: main
|
||
fetch-depth: 10
|
||
|
||
- name: 📡 感知近期变更
|
||
run: |
|
||
echo "📡 Pipeline E · 变更感知开始"
|
||
echo "=== 近期 10 次提交 ==="
|
||
git --no-pager log --oneline -10
|
||
echo ""
|
||
echo "=== syslog-inbox 文件状态 ==="
|
||
ls -la syslog-inbox/ || echo "(空)"
|
||
echo ""
|
||
echo "=== syslog-processed 文件状态 ==="
|
||
ls -laR syslog-processed/ || echo "(空)"
|
||
echo "✅ Pipeline E 变更感知完成"
|
||
|
||
# ━━━ 🧬 双端神经系统·自报告 ━━━
|
||
- name: "🧬 写入神经自报告"
|
||
if: always()
|
||
run: |
|
||
WORKFLOW_ID="syslog"
|
||
BRAIN="SY-03"
|
||
REPORT_DIR="data/neural-reports/syslog"
|
||
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.ZY_NOTION_TOKEN }}
|
||
RECEIPT_DB_ID: ${{ secrets.ZY_NOTION_RECEIPT_DB }}
|
||
run: |
|
||
node scripts/neural/write-receipt-to-notion.js \
|
||
--instruction-id "${{ github.event.inputs.instruction_id || 'AUTO' }}" \
|
||
--status "${{ job.status }}" \
|
||
--workflow "syslog" \
|
||
--summary "syslog 自动执行" \
|
||
--related-agent "SY-03"
|
||
|