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:
copilot-swe-agent[bot] 2026-03-06 04:58:35 +00:00
parent 50f6c07fea
commit b8153db9df
5 changed files with 354 additions and 0 deletions

130
.github/workflows/syslog-pipeline.yml vendored Normal file
View File

@ -0,0 +1,130 @@
name: 铸渊 · SYSLOG Pipeline (A/D/E)
on:
push:
branches: [main]
paths:
- 'syslog-inbox/**'
workflow_dispatch:
jobs:
# ── Pipeline ASYSLOG 读取与处理 ──────────────────────
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 DIssues 巡检 ─────────────────────────────
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 变更感知完成"

138
scripts/process-syslog.js Normal file
View File

@ -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}`);

View File

@ -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 => {

58
syslog-inbox/README.md Normal file
View File

@ -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/`

View File

@ -0,0 +1,9 @@
# syslog-processed · 已处理日志归档
此目录由 **铸渊 Pipeline** 自动管理,存放已处理完成的 syslog 条目。
- 文件从 `syslog-inbox/` 处理完毕后自动移入此处
- 按月分子目录归档:`syslog-processed/2026-03/`
- **请勿手动修改此目录内容**
> 此目录仅作归档用途,任何查询请读取 `.github/brain/memory.json`