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('✅ 大脑文件完整性:全部就绪');
|
||||
}
|
||||
|
||||
// === ① 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 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