🦅 [skyeye] 天眼周报增加系统架构汇总 · 自动更新 README + Notion 同步
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/6bdf5c3c-1a3e-4689-bcfc-2aa7367615a7
This commit is contained in:
parent
e2d7b766c0
commit
b0c77832ef
|
|
@ -33,10 +33,35 @@ jobs:
|
|||
- name: "📝 Phase 5 - Generate Report"
|
||||
run: node skyeye/scripts/weekly-scan.js --report
|
||||
|
||||
- name: "📊 Phase 6 - Architecture Summary → README"
|
||||
run: node scripts/skyeye/generate-arch-summary.js
|
||||
|
||||
- name: "Commit scan results"
|
||||
run: |
|
||||
git config user.name "天眼 · SkyEye Core"
|
||||
git config user.email "skyeye@guanghu.system"
|
||||
git add skyeye/ buffer/config/
|
||||
git diff --cached --quiet || git commit -m "[skyeye-weekly] $(date -u +%Y%m%d) full scan complete [skip ci]"
|
||||
git add skyeye/ buffer/config/ README.md
|
||||
git diff --cached --quiet || git commit -m "🦅 [skyeye-weekly] $(date -u +%Y%m%d) full scan + arch summary [skip ci]"
|
||||
git push
|
||||
|
||||
# Notion 同步 · 架构汇总推送到 Notion(通过 Bridge E)
|
||||
notify-notion:
|
||||
needs: weekly-scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main # 确保拉到刚 push 的最新版
|
||||
|
||||
- name: "📡 Sync arch summary to Notion via Bridge E"
|
||||
env:
|
||||
NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }}
|
||||
CHANGES_DB_ID: ${{ secrets.NOTION_CHANGE_LOG_DB_ID }}
|
||||
EVENT_TYPE: commit
|
||||
COMMIT_SHA: ${{ github.sha }}
|
||||
COMMIT_MSG: "🦅 天眼周报 · 系统架构汇总自动同步"
|
||||
COMMITTER: "天眼 · SkyEye Core"
|
||||
COMMIT_TIMESTAMP: ${{ github.event.repository.updated_at }}
|
||||
BRANCH: main
|
||||
CHANGED_FILES: "README.md\nskyeye/scan-report/"
|
||||
run: node scripts/notion-bridge.js changes
|
||||
|
|
|
|||
32
README.md
32
README.md
|
|
@ -99,7 +99,37 @@
|
|||
└─ L2 频道层(Channels)─ 11 个开发者自治频道 ── 自己管自己
|
||||
```
|
||||
|
||||
频道总数:11 | 活跃频道:8 | GitHub模块:17 | Workflow:67
|
||||
<!-- ARCH_SUMMARY_START -->
|
||||
> 🦅 **天眼自动汇总** · 每周六 20:00 CST 自动更新 · 最后更新: 2026-03-24
|
||||
|
||||
| 维度 | 当前状态 |
|
||||
|------|----------|
|
||||
| 🏥 系统健康 | 🟢 全部正常 |
|
||||
| ⚙️ Workflow 总数 | 79 个 |
|
||||
| 🤖 Agent 总数 | 62 个(14 个需日签到) |
|
||||
| 🎭 人格体 | 17 个 |
|
||||
| 📡 开发者频道 | 11 个(活跃 8) |
|
||||
| 🛡️ Guard 守卫 | 5 个 |
|
||||
| 📦 功能模块 | 10 个 |
|
||||
| 🌍 子仓库联邦 | 6 个模板(签到 1 个) |
|
||||
| 📜 本体论 | v1.0 · GLM-ONTOLOGY-v1.0 |
|
||||
| 📮 Buffer 待处理 | 8 条 |
|
||||
|
||||
**基础设施服务:**
|
||||
|
||||
| 服务 | 计划 | 状态 |
|
||||
|------|------|------|
|
||||
| GitHub | Free | ✅ |
|
||||
| Google Drive | Google One / Free 15GB | ✅ |
|
||||
| Notion | Plus | ✅ |
|
||||
| Google Gemini | Free / Pro | ✅ |
|
||||
| GitHub Actions | Free | ✅ |
|
||||
|
||||
**最近一次天眼扫描:** SKYEYE-SCAN-20260323
|
||||
- Guard: 5/5 活跃
|
||||
- 自愈: 清理 0 文件 · 修复 0 配置 · 重启 0 Guard
|
||||
- 下次扫描: 2026-03-28
|
||||
<!-- ARCH_SUMMARY_END -->
|
||||
|
||||
📢 [2026-03-22] 分布式主权共生架构落地 · 三层隔离+频道映射+CEP机制 · Notion双端同构
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,334 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* scripts/skyeye/generate-arch-summary.js
|
||||
* 天眼 · 系统架构汇总生成器
|
||||
*
|
||||
* 扫描仓库当前状态,生成 Markdown 格式的系统架构汇总,
|
||||
* 写入 README.md 的 <!-- ARCH_SUMMARY_START/END --> 标记区域。
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/skyeye/generate-arch-summary.js
|
||||
*
|
||||
* 数据来源:
|
||||
* - .github/workflows/ → Workflow 总数
|
||||
* - .github/persona-brain/agent-registry.json → Agent 总数
|
||||
* - .github/persona-brain/persona-registry.json → 人格体总数
|
||||
* - .github/brain/architecture/channel-map.json → 开发者频道
|
||||
* - skyeye/infra-manifest.json → 基础设施服务
|
||||
* - skyeye/guards/ → Guard 数量
|
||||
* - spoke-deployments/ → 子仓库(联邦)
|
||||
* - skyeye/scan-report/ → 最新扫描报告
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '../..');
|
||||
const README_PATH = path.join(ROOT, 'README.md');
|
||||
|
||||
const MARKER_START = '<!-- ARCH_SUMMARY_START -->';
|
||||
const MARKER_END = '<!-- ARCH_SUMMARY_END -->';
|
||||
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 16);
|
||||
}
|
||||
|
||||
function getBeijingDate() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function loadJSON(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━ 数据采集 ━━━
|
||||
|
||||
function countWorkflows() {
|
||||
const dir = path.join(ROOT, '.github/workflows');
|
||||
try {
|
||||
return fs.readdirSync(dir).filter(f => f.endsWith('.yml') || f.endsWith('.yaml')).length;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getAgentStats() {
|
||||
const registry = loadJSON(path.join(ROOT, '.github/persona-brain/agent-registry.json'));
|
||||
if (!registry) return { total: 0, checkinRequired: 0 };
|
||||
const agents = registry.agents || [];
|
||||
return {
|
||||
total: agents.length,
|
||||
checkinRequired: agents.filter(a => a.daily_checkin_required).length
|
||||
};
|
||||
}
|
||||
|
||||
function getPersonaStats() {
|
||||
const registry = loadJSON(path.join(ROOT, '.github/persona-brain/persona-registry.json'));
|
||||
if (!registry) return { total: 0 };
|
||||
const personas = registry.personas || [];
|
||||
return {
|
||||
total: personas.length,
|
||||
active: personas.filter(p => p.status === 'active' || !p.status).length
|
||||
};
|
||||
}
|
||||
|
||||
function getChannelStats() {
|
||||
const channelMap = loadJSON(path.join(ROOT, '.github/brain/architecture/channel-map.json'));
|
||||
if (!channelMap || !channelMap.channels) return { total: 0, active: 0 };
|
||||
const channels = channelMap.channels;
|
||||
const entries = Object.values(channels);
|
||||
return {
|
||||
total: entries.length,
|
||||
active: entries.filter(c => c.status === 'active').length
|
||||
};
|
||||
}
|
||||
|
||||
function getInfraServices() {
|
||||
const manifest = loadJSON(path.join(ROOT, 'skyeye/infra-manifest.json'));
|
||||
if (!manifest || !manifest.infrastructure) return [];
|
||||
return Object.entries(manifest.infrastructure).map(([key, svc]) => ({
|
||||
key,
|
||||
name: svc.service || key,
|
||||
plan: svc.plan || '—'
|
||||
}));
|
||||
}
|
||||
|
||||
function countGuards() {
|
||||
const dir = path.join(ROOT, 'skyeye/guards');
|
||||
try {
|
||||
return fs.readdirSync(dir).filter(f => f.endsWith('.json') && f !== 'guard-template.json').length;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getSpokeDeployments() {
|
||||
const dir = path.join(ROOT, 'spoke-deployments');
|
||||
try {
|
||||
return fs.readdirSync(dir, { withFileTypes: true })
|
||||
.filter(d => d.isDirectory())
|
||||
.map(d => d.name);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function getFederationStats() {
|
||||
const registry = loadJSON(path.join(ROOT, '.github/persona-brain/agent-registry.json'));
|
||||
if (!registry || !registry.federation) return { total: 0, members: [] };
|
||||
const fed = registry.federation;
|
||||
return {
|
||||
total: Object.keys(fed).length,
|
||||
members: Object.entries(fed).map(([devId, info]) => ({
|
||||
devId,
|
||||
persona: info.persona || '—',
|
||||
repo: info.repo || '—'
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
function getLatestScanReport() {
|
||||
const dir = path.join(ROOT, 'skyeye/scan-report');
|
||||
try {
|
||||
const files = fs.readdirSync(dir)
|
||||
.filter(f => f.endsWith('.json'))
|
||||
.sort()
|
||||
.reverse();
|
||||
if (files.length === 0) return null;
|
||||
return loadJSON(path.join(dir, files[0]));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function countModuleDirs() {
|
||||
try {
|
||||
return fs.readdirSync(ROOT, { withFileTypes: true }).filter(d => {
|
||||
return /^m\d+/.test(d.name) && d.isDirectory();
|
||||
}).length;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function getOntologyVersion() {
|
||||
const onto = loadJSON(path.join(ROOT, '.github/persona-brain/ontology.json'));
|
||||
if (!onto) return '—';
|
||||
return `v${onto.version || '?'} · ${onto.document_id || ''}`;
|
||||
}
|
||||
|
||||
function getBufferStatus() {
|
||||
const inboxDir = path.join(ROOT, 'buffer/inbox');
|
||||
try {
|
||||
let count = 0;
|
||||
const devDirs = fs.readdirSync(inboxDir);
|
||||
for (const d of devDirs) {
|
||||
const fullPath = path.join(inboxDir, d);
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
count += fs.readdirSync(fullPath).length;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ━━━ Markdown 生成 ━━━
|
||||
|
||||
function generateMarkdown() {
|
||||
const date = getBeijingDate();
|
||||
const time = getBeijingTime();
|
||||
const workflows = countWorkflows();
|
||||
const agentStats = getAgentStats();
|
||||
const personaStats = getPersonaStats();
|
||||
const channelStats = getChannelStats();
|
||||
const infraServices = getInfraServices();
|
||||
const guards = countGuards();
|
||||
const spokes = getSpokeDeployments();
|
||||
const federation = getFederationStats();
|
||||
const latestScan = getLatestScanReport();
|
||||
const modules = countModuleDirs();
|
||||
const ontology = getOntologyVersion();
|
||||
const bufferPending = getBufferStatus();
|
||||
|
||||
// Health indicator from latest scan
|
||||
let healthIcon = '🟢';
|
||||
let healthText = '全部正常';
|
||||
if (latestScan) {
|
||||
const alerts = latestScan.alerts || [];
|
||||
if (alerts.length > 0) {
|
||||
healthIcon = '🟡';
|
||||
healthText = `${alerts.length} 条告警`;
|
||||
}
|
||||
const status = latestScan.infrastructure_status || {};
|
||||
if ((status.down || 0) > 0) {
|
||||
healthIcon = '🔴';
|
||||
healthText = `${status.down} 服务宕机`;
|
||||
}
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
lines.push(`> 🦅 **天眼自动汇总** · 每周六 20:00 CST 自动更新 · 最后更新: ${date}`);
|
||||
lines.push('');
|
||||
lines.push(`| 维度 | 当前状态 |`);
|
||||
lines.push(`|------|----------|`);
|
||||
lines.push(`| 🏥 系统健康 | ${healthIcon} ${healthText} |`);
|
||||
lines.push(`| ⚙️ Workflow 总数 | ${workflows} 个 |`);
|
||||
lines.push(`| 🤖 Agent 总数 | ${agentStats.total} 个(${agentStats.checkinRequired} 个需日签到) |`);
|
||||
lines.push(`| 🎭 人格体 | ${personaStats.total} 个 |`);
|
||||
lines.push(`| 📡 开发者频道 | ${channelStats.total} 个(活跃 ${channelStats.active}) |`);
|
||||
lines.push(`| 🛡️ Guard 守卫 | ${guards} 个 |`);
|
||||
lines.push(`| 📦 功能模块 | ${modules} 个 |`);
|
||||
lines.push(`| 🌍 子仓库联邦 | ${spokes.length} 个模板(签到 ${federation.total} 个) |`);
|
||||
lines.push(`| 📜 本体论 | ${ontology} |`);
|
||||
lines.push(`| 📮 Buffer 待处理 | ${bufferPending} 条 |`);
|
||||
lines.push('');
|
||||
|
||||
// Infrastructure services table
|
||||
lines.push(`**基础设施服务:**`);
|
||||
lines.push('');
|
||||
lines.push(`| 服务 | 计划 | 状态 |`);
|
||||
lines.push(`|------|------|------|`);
|
||||
for (const svc of infraServices) {
|
||||
lines.push(`| ${svc.name} | ${svc.plan} | ✅ |`);
|
||||
}
|
||||
lines.push('');
|
||||
|
||||
// Scan report highlight
|
||||
if (latestScan) {
|
||||
const guardStatus = latestScan.guard_status || {};
|
||||
const healActions = latestScan.self_heal_actions || {};
|
||||
lines.push(`**最近一次天眼扫描:** ${latestScan.report_id || '—'}`);
|
||||
lines.push(`- Guard: ${guardStatus.active || 0}/${guardStatus.total_guards || 0} 活跃`);
|
||||
lines.push(`- 自愈: 清理 ${healActions.files_cleaned || 0} 文件 · 修复 ${healActions.configs_repaired || 0} 配置 · 重启 ${healActions.guards_restarted || 0} Guard`);
|
||||
const nextScan = latestScan.next_scan ? latestScan.next_scan.slice(0, 10) : '—';
|
||||
lines.push(`- 下次扫描: ${nextScan}`);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
// ━━━ README 更新 ━━━
|
||||
|
||||
function updateReadme(markdown) {
|
||||
if (!fs.existsSync(README_PATH)) {
|
||||
console.error('❌ README.md 不存在');
|
||||
return false;
|
||||
}
|
||||
|
||||
let readme = fs.readFileSync(README_PATH, 'utf8');
|
||||
|
||||
const startIdx = readme.indexOf(MARKER_START);
|
||||
const endIdx = readme.indexOf(MARKER_END);
|
||||
|
||||
if (startIdx === -1 || endIdx === -1) {
|
||||
console.error('❌ README.md 中未找到 ARCH_SUMMARY 标记');
|
||||
return false;
|
||||
}
|
||||
|
||||
const before = readme.slice(0, startIdx + MARKER_START.length);
|
||||
const after = readme.slice(endIdx);
|
||||
|
||||
readme = before + '\n' + markdown + '\n' + after;
|
||||
fs.writeFileSync(README_PATH, readme, 'utf8');
|
||||
console.log('✅ README.md 架构汇总已更新');
|
||||
return true;
|
||||
}
|
||||
|
||||
// ━━━ JSON 报告输出 ━━━
|
||||
|
||||
function generateJsonReport() {
|
||||
return {
|
||||
generated_at: new Date().toISOString(),
|
||||
generated_at_beijing: getBeijingTime(),
|
||||
workflows: countWorkflows(),
|
||||
agents: getAgentStats(),
|
||||
personas: getPersonaStats(),
|
||||
channels: getChannelStats(),
|
||||
infrastructure: getInfraServices(),
|
||||
guards: countGuards(),
|
||||
spoke_deployments: getSpokeDeployments(),
|
||||
federation: getFederationStats(),
|
||||
modules: countModuleDirs(),
|
||||
ontology: getOntologyVersion(),
|
||||
buffer_pending: getBufferStatus()
|
||||
};
|
||||
}
|
||||
|
||||
// ━━━ 入口 ━━━
|
||||
|
||||
function main() {
|
||||
console.log('[天眼 · 架构汇总] 开始生成系统架构汇总...');
|
||||
|
||||
const markdown = generateMarkdown();
|
||||
const updated = updateReadme(markdown);
|
||||
|
||||
// Also output JSON report for Notion bridge consumption
|
||||
const jsonReport = generateJsonReport();
|
||||
const reportDir = path.join(ROOT, 'skyeye/scan-report');
|
||||
if (!fs.existsSync(reportDir)) fs.mkdirSync(reportDir, { recursive: true });
|
||||
const reportPath = path.join(reportDir, `${getBeijingDate().replace(/-/g, '')}-arch-summary.json`);
|
||||
fs.writeFileSync(reportPath, JSON.stringify(jsonReport, null, 2) + '\n', 'utf8');
|
||||
console.log(`📋 架构汇总 JSON: ${reportPath}`);
|
||||
|
||||
if (updated) {
|
||||
console.log('[天眼 · 架构汇总] ✅ 完成');
|
||||
} else {
|
||||
console.log('[天眼 · 架构汇总] ⚠️ README 未更新(缺少标记)');
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in New Issue