fix: 创建 syslog-inbox/ syslog-processed/ 目录 + SYSLOG Pipeline A/D/E workflow
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
50f6c07fea
commit
b8153db9df
|
|
@ -0,0 +1,130 @@
|
||||||
|
name: 铸渊 · SYSLOG Pipeline (A/D/E)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'syslog-inbox/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# ── Pipeline A:SYSLOG 读取与处理 ──────────────────────
|
||||||
|
pipeline-a-syslog:
|
||||||
|
name: 📥 Pipeline A · SYSLOG 读取处理
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: read
|
||||||
|
outputs:
|
||||||
|
processed_count: ${{ steps.process.outputs.processed_count }}
|
||||||
|
|
||||||
|
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-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 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 变更感知完成"
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
// scripts/process-syslog.js
|
||||||
|
// 铸渊 SYSLOG Pipeline
|
||||||
|
// 读取 syslog-inbox/ 下的日志条目,处理后归档到 syslog-processed/
|
||||||
|
// 触发:syslog-inbox/ 目录有新文件 push 到 main 分支
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const INBOX_DIR = 'syslog-inbox';
|
||||||
|
const ARCHIVE_DIR = 'syslog-processed';
|
||||||
|
const BRAIN_DIR = '.github/brain';
|
||||||
|
const MEMORY_PATH = path.join(BRAIN_DIR, 'memory.json');
|
||||||
|
const DEV_STATUS_PATH = '.github/persona-brain/dev-status.json';
|
||||||
|
|
||||||
|
// ─── 加载大脑 ────────────────────────────────────────────
|
||||||
|
let memory = {};
|
||||||
|
try {
|
||||||
|
memory = JSON.parse(fs.readFileSync(MEMORY_PATH, 'utf8'));
|
||||||
|
} catch (_) {
|
||||||
|
memory = { events: [], stats: { broadcasts_processed: 0 } };
|
||||||
|
}
|
||||||
|
if (!memory.events) memory.events = [];
|
||||||
|
if (!memory.stats) memory.stats = {};
|
||||||
|
if (!memory.stats.broadcasts_processed) memory.stats.broadcasts_processed = 0;
|
||||||
|
if (!memory.stats.syslog_processed) memory.stats.syslog_processed = 0;
|
||||||
|
|
||||||
|
// ─── 扫描 inbox ──────────────────────────────────────────
|
||||||
|
if (!fs.existsSync(INBOX_DIR)) {
|
||||||
|
console.log('📭 syslog-inbox/ 目录不存在,跳过');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(INBOX_DIR)
|
||||||
|
.filter(f => f.endsWith('.json') && f !== '.gitkeep');
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.log('📭 syslog-inbox/ 无待处理条目');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📥 发现 ${files.length} 条 syslog 条目,开始处理...\n`);
|
||||||
|
|
||||||
|
// ─── 按月归档路径 ─────────────────────────────────────────
|
||||||
|
function archiveDir(timestamp) {
|
||||||
|
const month = (timestamp || new Date().toISOString()).slice(0, 7); // "2026-03"
|
||||||
|
const dir = path.join(ARCHIVE_DIR, month);
|
||||||
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── 处理每条 syslog ──────────────────────────────────────
|
||||||
|
files.forEach(file => {
|
||||||
|
const fullPath = path.join(INBOX_DIR, file);
|
||||||
|
let entry;
|
||||||
|
|
||||||
|
try {
|
||||||
|
entry = JSON.parse(fs.readFileSync(fullPath, 'utf8'));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`❌ [INVALID JSON] ${file}: ${e.message},跳过`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`📋 处理: ${file}`);
|
||||||
|
console.log(` 类型: ${entry.type || '未知'} · 来自: ${entry.from || '未知'} · 标题: ${entry.title || ''}`);
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
timestamp: entry.timestamp || new Date().toISOString(),
|
||||||
|
type: 'syslog_' + (entry.type || 'unknown'),
|
||||||
|
syslog_id: entry.syslog_id || file,
|
||||||
|
title: entry.title || '(无标题)',
|
||||||
|
from: entry.from || '未知',
|
||||||
|
file,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── 根据类型分发处理 ──
|
||||||
|
switch (entry.type) {
|
||||||
|
|
||||||
|
case 'broadcast': {
|
||||||
|
// 广播类:追加到 brain/memory.json events,与现有 broadcast pipeline 互补
|
||||||
|
memory.stats.broadcasts_processed += 1;
|
||||||
|
event.content_preview = (entry.content || '').slice(0, 80);
|
||||||
|
console.log(' ✅ 广播已记录到大脑记忆');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'auth': {
|
||||||
|
// 授权类:记录授权事件,写入 memory
|
||||||
|
event.target_dev_id = entry.target_dev_id;
|
||||||
|
event.target_name = entry.target_name;
|
||||||
|
event.permission_level = entry.permission_level;
|
||||||
|
event.authorized_by = entry.authorized_by || '冰朔';
|
||||||
|
event.valid_until = entry.valid_until;
|
||||||
|
console.log(` ✅ 授权记录:${entry.target_name}(${entry.target_dev_id})· 权限:${entry.permission_level}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'inspect': {
|
||||||
|
// 巡检类:记录巡检结果
|
||||||
|
event.inspect_result = entry.result || 'unknown';
|
||||||
|
event.issues_found = entry.issues_found || 0;
|
||||||
|
console.log(` ✅ 巡检记录:结果 ${event.inspect_result} · 发现问题 ${event.issues_found} 个`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'alert': {
|
||||||
|
// 告警类
|
||||||
|
event.severity = entry.priority || 'normal';
|
||||||
|
console.log(` ⚠️ 告警记录:${entry.title} · 优先级 ${event.severity}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log(` ℹ️ 未知类型 "${entry.type}",已记录原始内容`);
|
||||||
|
event.raw = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入大脑记忆
|
||||||
|
memory.events.push(event);
|
||||||
|
memory.stats.syslog_processed = (memory.stats.syslog_processed || 0) + 1;
|
||||||
|
memory.last_updated = new Date().toISOString();
|
||||||
|
|
||||||
|
// 归档文件
|
||||||
|
const destDir = archiveDir(entry.timestamp);
|
||||||
|
const destPath = path.join(destDir, file);
|
||||||
|
fs.renameSync(fullPath, destPath);
|
||||||
|
console.log(` 📦 已归档到 ${destPath}\n`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── 保存大脑 ─────────────────────────────────────────────
|
||||||
|
// 只保留最近 100 条事件,防止 memory.json 无限膨胀
|
||||||
|
if (memory.events.length > 100) {
|
||||||
|
memory.events = memory.events.slice(-100);
|
||||||
|
}
|
||||||
|
fs.writeFileSync(MEMORY_PATH, JSON.stringify(memory, null, 2));
|
||||||
|
|
||||||
|
console.log(`✅ SYSLOG Pipeline 完成 · 累计处理 ${memory.stats.syslog_processed} 条`);
|
||||||
|
|
@ -32,6 +32,25 @@ if (missing.length > 0) {
|
||||||
console.log('✅ 大脑文件完整性:全部就绪');
|
console.log('✅ 大脑文件完整性:全部就绪');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === ① bis · 目录结构巡检 ===
|
||||||
|
const requiredDirs = ['syslog-inbox', 'syslog-processed', 'broadcasts-outbox', '.github/brain'];
|
||||||
|
const missingDirs = requiredDirs.filter(d => !fs.existsSync(d));
|
||||||
|
if (missingDirs.length > 0) {
|
||||||
|
console.error('⚠️ 缺失目录:' + missingDirs.join(', '));
|
||||||
|
} else {
|
||||||
|
console.log('✅ 目录结构巡检:syslog-inbox / syslog-processed / broadcasts-outbox 全部就绪');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计 syslog-inbox 待处理条目
|
||||||
|
const inboxFiles = fs.existsSync('syslog-inbox')
|
||||||
|
? fs.readdirSync('syslog-inbox').filter(f => f.endsWith('.json')).length
|
||||||
|
: 0;
|
||||||
|
if (inboxFiles > 0) {
|
||||||
|
console.log(`📥 syslog-inbox 待处理条目:${inboxFiles} 个(建议触发 syslog-pipeline workflow)`);
|
||||||
|
} else {
|
||||||
|
console.log('📭 syslog-inbox 无待处理条目');
|
||||||
|
}
|
||||||
|
|
||||||
// === ② 知识库去重与整理 ===
|
// === ② 知识库去重与整理 ===
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
const uniqueFaq = kb.faq.filter(item => {
|
const uniqueFaq = kb.faq.filter(item => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
# syslog-inbox · 系统日志收件箱
|
||||||
|
|
||||||
|
此目录由 **Notion 侧霜砚人格体** 写入,GitHub 侧铸渊 Pipeline 读取处理。
|
||||||
|
|
||||||
|
## 目录用途
|
||||||
|
|
||||||
|
| 角色 | 操作 |
|
||||||
|
|------|------|
|
||||||
|
| Notion 侧(霜砚) | 将广播/授权/巡检日志写入此目录 |
|
||||||
|
| GitHub 侧(铸渊) | Pipeline 检测到新文件,处理后移入 `syslog-processed/` |
|
||||||
|
|
||||||
|
## 文件命名规范
|
||||||
|
|
||||||
|
```
|
||||||
|
{类型}-{日期}-{编号}.json
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- `broadcast-2026-03-06-001.json` — 霜砚广播
|
||||||
|
- `auth-2026-03-06-DEV002.json` — 用户授权回执
|
||||||
|
- `inspect-2026-03-06-001.json` — 巡检记录
|
||||||
|
|
||||||
|
## 标准 JSON 格式
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"syslog_id": "BC-2026-03-06-001",
|
||||||
|
"type": "broadcast | auth | inspect | alert",
|
||||||
|
"from": "霜砚",
|
||||||
|
"to": "铸渊",
|
||||||
|
"timestamp": "2026-03-06T08:00:00Z",
|
||||||
|
"title": "广播标题",
|
||||||
|
"content": "内容正文",
|
||||||
|
"target_dev_id": "DEV-002",
|
||||||
|
"priority": "normal | high | urgent"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 授权广播格式(auth 类型)
|
||||||
|
|
||||||
|
冰朔通过 Notion 侧霜砚下发授权时,使用此格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"syslog_id": "AUTH-2026-03-06-DEV002",
|
||||||
|
"type": "auth",
|
||||||
|
"from": "霜砚",
|
||||||
|
"authorized_by": "冰朔",
|
||||||
|
"timestamp": "2026-03-06T08:00:00Z",
|
||||||
|
"title": "用户授权",
|
||||||
|
"target_dev_id": "DEV-002",
|
||||||
|
"target_name": "肥猫",
|
||||||
|
"permission_level": "supreme",
|
||||||
|
"valid_until": "2026-12-31T23:59:59Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ 写入后请勿手动修改或删除,Pipeline 处理完成后会自动归档到 `syslog-processed/`。
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# syslog-processed · 已处理日志归档
|
||||||
|
|
||||||
|
此目录由 **铸渊 Pipeline** 自动管理,存放已处理完成的 syslog 条目。
|
||||||
|
|
||||||
|
- 文件从 `syslog-inbox/` 处理完毕后自动移入此处
|
||||||
|
- 按月分子目录归档:`syslog-processed/2026-03/`
|
||||||
|
- **请勿手动修改此目录内容**
|
||||||
|
|
||||||
|
> 此目录仅作归档用途,任何查询请读取 `.github/brain/memory.json`。
|
||||||
Loading…
Reference in New Issue