guanghulab/scripts/zhuyuan-full-inspection.js

735 lines
28 KiB
JavaScript
Raw Permalink Normal View History

/**
* scripts/zhuyuan-full-inspection.js
* 铸渊全面排查脚本 · AGE OS v1.0 Phase 1 Step 1
*
* 铸渊核心大脑醒来后全面排查仓库现状
* 排查覆盖 8 个领域
* 1. 仓库整体结构
* 2. 自动化流程现状
* 3. 仓库首页和入口
* 4. 公告栏和系统更新
* 5. 服务状态
* 6. 密钥和凭证仅检查存在性
* 7. 与四节点的连接状态
* 8. 人格体机器人托管现状
*
* 调用方式
* node scripts/zhuyuan-full-inspection.js
* node scripts/zhuyuan-full-inspection.js --json
* node scripts/zhuyuan-full-inspection.js --output report.json
*/
'use strict';
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
// ══════════════════════════════════════════════════════════
// Area 1: 仓库整体结构
// ══════════════════════════════════════════════════════════
function inspectRepoStructure() {
console.log('\n📂 ═══ Area 1: 仓库整体结构 ═══');
const result = {
area: '仓库整体结构',
directories: {},
config_files: {},
doc_files: {},
issues: [],
};
// 核心目录
const coreDirs = [
{ path: 'brain', category: '核心', desc: '系统大脑/知识库' },
{ path: 'core', category: '核心', desc: '核心系统模块' },
{ path: 'connectors', category: '核心', desc: '外部集成连接器' },
{ path: 'src', category: '核心', desc: '源代码' },
{ path: 'scripts', category: '核心', desc: '自动化脚本' },
{ path: '.github/workflows', category: '核心', desc: 'GitHub Actions 工作流' },
{ path: 'backend', category: '功能', desc: '后端服务' },
{ path: 'backend-integration', category: '功能', desc: 'API 代理和集成' },
{ path: 'persona-studio', category: '功能', desc: '人格工作室' },
{ path: 'persona-brain-db', category: '功能', desc: '人格知识数据库' },
{ path: 'dingtalk-bot', category: '功能', desc: '钉钉机器人' },
{ path: 'frontend', category: '功能', desc: '前端代码' },
{ path: 'docs', category: '文档', desc: '文档' },
{ path: 'tests', category: '测试', desc: '测试套件' },
{ path: 'broadcasts', category: '数据', desc: '广播数据' },
{ path: 'bulletin-board', category: '数据', desc: '公告栏系统' },
{ path: 'notification', category: '功能', desc: '通知系统' },
{ path: 'dashboard', category: '功能', desc: '仪表盘' },
{ path: 'modules', category: '功能', desc: '功能模块' },
{ path: 'cloud-drive', category: '功能', desc: '云存储' },
];
for (const dir of coreDirs) {
const fullPath = path.join(ROOT, dir.path);
const exists = fs.existsSync(fullPath);
let fileCount = 0;
if (exists) {
try {
fileCount = fs.readdirSync(fullPath).length;
} catch {
// Directory exists but is unreadable — use 0 as fallback
}
}
result.directories[dir.path] = {
exists,
category: dir.category,
description: dir.desc,
fileCount,
};
const icon = exists ? '✅' : '❌';
console.log(` ${icon} ${dir.path} (${dir.category}) — ${dir.desc}${exists ? ` [${fileCount} items]` : ''}`);
if (!exists && dir.category === '核心') {
result.issues.push(`缺少核心目录: ${dir.path}`);
}
}
// 配置文件
const configFiles = [
'package.json', 'ecosystem.config.js', 'config.js',
'.gitignore', 'tsconfig.json', 'next.config.ts',
];
for (const f of configFiles) {
const exists = fs.existsSync(path.join(ROOT, f));
result.config_files[f] = exists;
console.log(` ${exists ? '✅' : '❌'} 配置: ${f}`);
}
// 文档完整度
const docFiles = [
'README.md', 'docs/repo-structure-map.md', 'docs/notion-bridge-map.md',
'brain/master-brain.md', 'brain/read-order.md',
];
for (const f of docFiles) {
const exists = fs.existsSync(path.join(ROOT, f));
result.doc_files[f] = exists;
console.log(` ${exists ? '✅' : '❌'} 文档: ${f}`);
if (!exists) {
result.issues.push(`缺少文档: ${f}`);
}
}
return result;
}
// ══════════════════════════════════════════════════════════
// Area 2: 自动化流程现状
// ══════════════════════════════════════════════════════════
function inspectAutomation() {
console.log('\n⚙ ═══ Area 2: 自动化流程现状 ═══');
const result = {
area: '自动化流程现状',
workflows: [],
scripts: [],
issues: [],
};
// 工作流文件
const workflowDir = path.join(ROOT, '.github/workflows');
if (fs.existsSync(workflowDir)) {
const files = fs.readdirSync(workflowDir).filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
console.log(` ✅ 发现 ${files.length} 个工作流文件`);
for (const file of files) {
const content = fs.readFileSync(path.join(workflowDir, file), 'utf-8');
const nameMatch = content.match(/^name:\s*["']?(.+?)["']?\s*$/m);
const cronMatch = content.match(/cron:\s*'([^']+)'/);
const hasWorkflowDispatch = content.includes('workflow_dispatch');
const workflow = {
file,
name: nameMatch ? nameMatch[1] : file,
hasCron: !!cronMatch,
cron: cronMatch ? cronMatch[1] : null,
hasManualTrigger: hasWorkflowDispatch,
category: categorizeWorkflow(file),
};
result.workflows.push(workflow);
console.log(` 📋 ${file}${workflow.name}${workflow.hasCron ? ` [cron: ${workflow.cron}]` : ''}${workflow.hasManualTrigger ? ' [手动]' : ''}`);
}
} else {
result.issues.push('工作流目录不存在');
console.log(' ❌ 工作流目录不存在');
}
// 脚本文件
const scriptsDir = path.join(ROOT, 'scripts');
if (fs.existsSync(scriptsDir)) {
const files = fs.readdirSync(scriptsDir).filter(f => f.endsWith('.js'));
console.log(` ✅ 发现 ${files.length} 个自动化脚本`);
for (const file of files) {
result.scripts.push({
file,
category: categorizeScript(file),
});
}
}
// 核心模块
const coreModules = [
'core/broadcast-listener/index.js',
'core/task-queue/index.js',
'core/system-check/index.js',
'core/execution-sync/index.js',
'core/context-loader/index.js',
'core/brain-wake/index.js',
'connectors/notion-sync/index.js',
'connectors/notion-wake-listener/index.js',
'connectors/model-router/index.js',
];
console.log('\n 🔧 核心模块:');
for (const mod of coreModules) {
const exists = fs.existsSync(path.join(ROOT, mod));
console.log(` ${exists ? '✅' : '❌'} ${mod}`);
if (!exists) {
result.issues.push(`缺少核心模块: ${mod}`);
}
}
return result;
}
function categorizeWorkflow(filename) {
if (filename.includes('deploy')) return '部署';
if (filename.includes('maintenance') || filename.includes('selfcheck')) return '维护';
if (filename.includes('agent')) return 'Agent';
if (filename.includes('brain') || filename.includes('sync')) return '同步';
if (filename.includes('syslog') || filename.includes('pipeline')) return '管线';
if (filename.includes('notion')) return 'Notion';
if (filename.includes('ps-on')) return '人格体';
if (filename.includes('bridge')) return '桥接';
return '其他';
}
function categorizeScript(filename) {
if (filename.includes('generate-')) return '生成器';
if (filename.includes('zhuyuan-')) return '铸渊';
if (filename.includes('bingshuo-')) return '冰朔';
if (filename.includes('notion-') || filename.includes('sync')) return 'Notion';
if (filename.includes('wake-') || filename.includes('invoke-')) return '唤醒';
if (filename.includes('push-') || filename.includes('distribute')) return '广播';
if (filename.includes('syslog') || filename.includes('pipeline')) return '管线';
return '工具';
}
// ══════════════════════════════════════════════════════════
// Area 3: 仓库首页和入口
// ══════════════════════════════════════════════════════════
function inspectEntryPoints() {
console.log('\n🏠 ═══ Area 3: 仓库首页和入口 ═══');
const result = {
area: '仓库首页和入口',
readme: null,
entryPoints: {},
issues: [],
};
// README.md
const readmePath = path.join(ROOT, 'README.md');
if (fs.existsSync(readmePath)) {
const content = fs.readFileSync(readmePath, 'utf-8');
result.readme = {
exists: true,
length: content.length,
hasTitle: content.includes('#'),
hasBulletin: content.includes('公告') || content.includes('bulletin'),
hasDevGuide: content.includes('开发') || content.includes('developer') || content.includes('指南'),
};
console.log(` ✅ README.md (${content.length} chars)`);
console.log(` 标题: ${result.readme.hasTitle ? '✅' : '❌'}`);
console.log(` 公告栏: ${result.readme.hasBulletin ? '✅' : '❌'}`);
console.log(` 开发指南: ${result.readme.hasDevGuide ? '✅' : '❌'}`);
} else {
result.readme = { exists: false };
result.issues.push('README.md 不存在');
console.log(' ❌ README.md 不存在');
}
// 开发者入口
const entryPoints = [
{ path: 'frontend/index.html', desc: '前端入口' },
{ path: 'index.html', desc: '主页入口' },
{ path: 'index.js', desc: '主 JS 入口' },
{ path: 'src/index.js', desc: 'src 入口' },
{ path: 'homepage/index.html', desc: '首页入口' },
{ path: 'portal/index.html', desc: '门户入口' },
{ path: 'dashboard/index.html', desc: '仪表盘入口' },
];
for (const entry of entryPoints) {
const exists = fs.existsSync(path.join(ROOT, entry.path));
result.entryPoints[entry.path] = { exists, description: entry.desc };
console.log(` ${exists ? '✅' : '⏭️ '} ${entry.desc}: ${entry.path}`);
}
return result;
}
// ══════════════════════════════════════════════════════════
// Area 4: 公告栏和系统更新
// ══════════════════════════════════════════════════════════
function inspectBulletins() {
console.log('\n📢 ═══ Area 4: 公告栏和系统更新 ═══');
const result = {
area: '公告栏和系统更新',
bulletinBoard: {},
systemLogs: {},
issues: [],
};
// 公告栏目录
const bulletinDir = path.join(ROOT, 'bulletin-board');
if (fs.existsSync(bulletinDir)) {
const files = fs.readdirSync(bulletinDir);
result.bulletinBoard.exists = true;
result.bulletinBoard.fileCount = files.length;
console.log(` ✅ bulletin-board/ (${files.length} 文件)`);
} else {
result.bulletinBoard.exists = false;
console.log(' ❌ bulletin-board/ 不存在');
}
// brain 公告缓存
const cacheFile = path.join(ROOT, '.github/brain/bulletin-board-today.json');
if (fs.existsSync(cacheFile)) {
try {
const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
result.bulletinBoard.cacheDate = cache.date || 'unknown';
result.bulletinBoard.cacheRecords = (cache.records || []).length;
console.log(` ✅ 公告栏缓存日期: ${cache.date} (${(cache.records || []).length} 条记录)`);
} catch (err) {
console.log(' ⚠️ 公告栏缓存 JSON 解析失败:', err.message);
}
} else {
console.log(' ⏭️ 公告栏缓存文件不存在');
}
// 广播目录
const broadcastsDirs = ['broadcasts', 'broadcasts-outbox'];
for (const dir of broadcastsDirs) {
const fullPath = path.join(ROOT, dir);
if (fs.existsSync(fullPath)) {
const files = fs.readdirSync(fullPath);
result.systemLogs[dir] = { exists: true, fileCount: files.length };
console.log(`${dir}/ (${files.length} 文件)`);
} else {
result.systemLogs[dir] = { exists: false };
console.log(` ⏭️ ${dir}/ 不存在`);
}
}
// 公告相关工作流
const bulletinWorkflows = ['update-readme-bulletin.yml'];
for (const wf of bulletinWorkflows) {
const exists = fs.existsSync(path.join(ROOT, '.github/workflows', wf));
console.log(` ${exists ? '✅' : '❌'} 工作流: ${wf}`);
if (!exists) {
result.issues.push(`缺少公告工作流: ${wf}`);
}
}
return result;
}
// ══════════════════════════════════════════════════════════
// Area 5: 服务状态
// ══════════════════════════════════════════════════════════
function inspectServices() {
console.log('\n🖥 ═══ Area 5: 服务状态 ═══');
const result = {
area: '服务状态',
services: [],
configs: {},
issues: [],
};
// PM2 配置
const ecosystemPath = path.join(ROOT, 'ecosystem.config.js');
if (fs.existsSync(ecosystemPath)) {
try {
const content = fs.readFileSync(ecosystemPath, 'utf-8');
const appMatches = content.match(/name\s*:\s*['"]([^'"]+)['"]/g) || [];
const apps = appMatches.map(m => m.match(/['"]([^'"]+)['"]/)[1]);
result.services = apps.map(name => ({ name, source: 'ecosystem.config.js' }));
console.log(` ✅ ecosystem.config.js (${apps.length} 个应用)`);
apps.forEach(app => console.log(` 📋 ${app}`));
} catch {
console.log(' ⚠️ ecosystem.config.js 解析失败');
}
} else {
result.issues.push('ecosystem.config.js 不存在');
console.log(' ❌ ecosystem.config.js 不存在');
}
// Nginx 配置
const nginxPath = path.join(ROOT, 'backend-integration/nginx-api-proxy.conf');
if (fs.existsSync(nginxPath)) {
const content = fs.readFileSync(nginxPath, 'utf-8');
const locationMatches = content.match(/location\s+([^\s{]+)/g) || [];
result.configs.nginx = {
exists: true,
routes: locationMatches.map(m => m.replace('location ', '')),
};
console.log(` ✅ nginx-api-proxy.conf (${locationMatches.length} 路由)`);
locationMatches.forEach(m => console.log(` 📋 ${m}`));
} else {
result.configs.nginx = { exists: false };
console.log(' ⏭️ nginx-api-proxy.conf 不存在');
}
// 后端服务文件
const backendFiles = [
'backend/server.js',
'backend-integration/api-proxy.js',
'persona-studio/backend/server.js',
];
for (const f of backendFiles) {
const exists = fs.existsSync(path.join(ROOT, f));
result.configs[f] = exists;
console.log(` ${exists ? '✅' : '⏭️ '} ${f}`);
}
return result;
}
// ══════════════════════════════════════════════════════════
// Area 6: 密钥和凭证
// ══════════════════════════════════════════════════════════
function inspectCredentials() {
console.log('\n🔑 ═══ Area 6: 密钥和凭证(存在性检查)═══');
const result = {
area: '密钥和凭证',
envVars: {},
secretsUsed: [],
issues: [],
};
// 检查环境变量中可能的密钥配置
const envKeys = [
{ key: 'NOTION_TOKEN', purpose: 'Notion API 访问', category: 'Notion' },
{ key: 'LLM_API_KEY', purpose: 'LLM 平台通用密钥', category: 'LLM' },
{ key: 'LLM_BASE_URL', purpose: 'LLM 平台 API 地址', category: 'LLM' },
{ key: 'ANTHROPIC_API_KEY', purpose: 'Anthropic Claude API', category: 'LLM' },
{ key: 'OPENAI_API_KEY', purpose: 'OpenAI GPT API', category: 'LLM' },
{ key: 'DASHSCOPE_API_KEY', purpose: '通义千问 API', category: 'LLM' },
{ key: 'DEEPSEEK_API_KEY', purpose: 'DeepSeek API', category: 'LLM' },
{ key: 'GITHUB_TOKEN', purpose: 'GitHub API 访问', category: 'GitHub' },
{ key: 'FEISHU_APP_ID', purpose: '飞书应用 ID', category: '飞书' },
{ key: 'FEISHU_APP_SECRET', purpose: '飞书应用密钥', category: '飞书' },
{ key: 'DINGTALK_TOKEN', purpose: '钉钉机器人 Token', category: '钉钉' },
];
for (const env of envKeys) {
const isSet = !!process.env[env.key];
result.envVars[env.key] = {
isSet,
purpose: env.purpose,
category: env.category,
};
console.log(` ${isSet ? '✅' : '⏭️ '} ${env.key}${env.purpose} (${env.category})`);
}
// 扫描工作流文件中引用的 secrets
const workflowDir = path.join(ROOT, '.github/workflows');
if (fs.existsSync(workflowDir)) {
const files = fs.readdirSync(workflowDir).filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
const secretsSet = new Set();
for (const file of files) {
const content = fs.readFileSync(path.join(workflowDir, file), 'utf-8');
const matches = content.match(/secrets\.([A-Z_]+)/g) || [];
matches.forEach(m => secretsSet.add(m.replace('secrets.', '')));
}
result.secretsUsed = Array.from(secretsSet).sort();
console.log(`\n 📋 工作流中引用的 Secrets (${result.secretsUsed.length} 个):`);
result.secretsUsed.forEach(s => console.log(` 🔐 ${s}`));
}
return result;
}
// ══════════════════════════════════════════════════════════
// Area 7: 与四节点的连接状态
// ══════════════════════════════════════════════════════════
function inspectNodeConnections() {
console.log('\n🔗 ═══ Area 7: 与四节点的连接状态 ═══');
const result = {
area: '四节点连接状态',
nodes: {},
issues: [],
};
// GitHub ↔ Notion
const notionConnectors = [
'connectors/notion-sync/index.js',
'scripts/notion-signal-bridge.js',
'scripts/notion-heartbeat.js',
'scripts/write-notion-syslog.js',
];
const notionConnected = notionConnectors.filter(f => fs.existsSync(path.join(ROOT, f)));
result.nodes['Notion'] = {
connectors: notionConnected.length,
total: notionConnectors.length,
files: notionConnectors.map(f => ({ path: f, exists: fs.existsSync(path.join(ROOT, f)) })),
};
console.log(` 📋 GitHub ↔ Notion: ${notionConnected.length}/${notionConnectors.length} 连接器`);
notionConnectors.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
});
// GitHub ↔ 服务器 (guanghulab.com)
const serverConnectors = [
'backend/server.js',
'backend-integration/api-proxy.js',
'ecosystem.config.js',
'backend-integration/nginx-api-proxy.conf',
];
const serverConnected = serverConnectors.filter(f => fs.existsSync(path.join(ROOT, f)));
result.nodes['服务器'] = {
connectors: serverConnected.length,
total: serverConnectors.length,
files: serverConnectors.map(f => ({ path: f, exists: fs.existsSync(path.join(ROOT, f)) })),
};
console.log(` 📋 GitHub ↔ guanghulab.com: ${serverConnected.length}/${serverConnectors.length} 连接器`);
serverConnectors.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
});
// GitHub ↔ 飞书
const feishuConnectors = [
'scripts/send-feishu-alert.js',
'backend/routes/feishu-bot.js',
];
const feishuFiles = feishuConnectors.filter(f => fs.existsSync(path.join(ROOT, f)));
result.nodes['飞书'] = {
connectors: feishuFiles.length,
total: feishuConnectors.length,
files: feishuConnectors.map(f => ({ path: f, exists: fs.existsSync(path.join(ROOT, f)) })),
};
console.log(` 📋 GitHub ↔ 飞书: ${feishuFiles.length}/${feishuConnectors.length} 连接器`);
feishuConnectors.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
});
// GitHub ↔ 钉钉
const dingtalkConnectors = [
'dingtalk-bot/index.js',
'dingtalk-bot/package.json',
];
const dingtalkFiles = dingtalkConnectors.filter(f => fs.existsSync(path.join(ROOT, f)));
result.nodes['钉钉'] = {
connectors: dingtalkFiles.length,
total: dingtalkConnectors.length,
files: dingtalkConnectors.map(f => ({ path: f, exists: fs.existsSync(path.join(ROOT, f)) })),
};
console.log(` 📋 GitHub ↔ 钉钉: ${dingtalkFiles.length}/${dingtalkConnectors.length} 连接器`);
dingtalkConnectors.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
});
return result;
}
// ══════════════════════════════════════════════════════════
// Area 8: 人格体机器人托管现状
// ══════════════════════════════════════════════════════════
function inspectPersonaBots() {
console.log('\n🤖 ═══ Area 8: 人格体机器人托管现状 ═══');
const result = {
area: '人格体机器人托管现状',
personas: [],
issues: [],
};
// 扫描人格体相关目录和文件
const personaIndicators = [
{
name: '舒舒 (飞书)',
files: ['backend/routes/feishu-bot.js'],
config: [],
platform: '飞书',
},
{
name: '铸渊 (仓库守护)',
files: ['scripts/zhuyuan-daily-agent.js', 'scripts/zhuyuan-daily-selfcheck.js', 'scripts/zhuyuan-full-inspection.js'],
config: ['brain/master-brain.md', '.github/brain/wake-protocol.md'],
platform: 'GitHub',
},
{
name: '冰朔 (部署)',
files: ['scripts/bingshuo-deploy-agent.js', 'scripts/bingshuo-neural-sync.js'],
config: ['.github/brain/bingshuo-master-brain.md', '.github/brain/bingshuo-agent-registry.json'],
platform: 'GitHub',
},
];
// 人格工作室
const psDir = path.join(ROOT, 'persona-studio');
if (fs.existsSync(psDir)) {
console.log(' ✅ persona-studio/ 存在');
}
// 人格大脑数据库
const pbDir = path.join(ROOT, 'persona-brain-db');
if (fs.existsSync(pbDir)) {
console.log(' ✅ persona-brain-db/ 存在');
}
// 多人格模块
const mpDir = path.join(ROOT, 'multi-persona');
if (fs.existsSync(mpDir)) {
console.log(' ✅ multi-persona/ 存在');
}
for (const persona of personaIndicators) {
const existingFiles = persona.files.filter(f => fs.existsSync(path.join(ROOT, f)));
const existingConfig = persona.config.filter(f => fs.existsSync(path.join(ROOT, f)));
const status = {
name: persona.name,
platform: persona.platform,
codeFiles: existingFiles.length,
totalCodeFiles: persona.files.length,
configFiles: existingConfig.length,
totalConfigFiles: persona.config.length,
};
result.personas.push(status);
console.log(`\n 🤖 ${persona.name} (${persona.platform})`);
persona.files.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
});
persona.config.forEach(f => {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '⏭️ '} config: ${f}`);
});
}
// 唤醒脚本
const wakeScripts = ['scripts/wake-persona.js', 'scripts/invoke-persona.js'];
console.log('\n 🌅 唤醒脚本:');
for (const f of wakeScripts) {
const exists = fs.existsSync(path.join(ROOT, f));
console.log(` ${exists ? '✅' : '❌'} ${f}`);
}
return result;
}
// ══════════════════════════════════════════════════════════
// 生成排查报告
// ══════════════════════════════════════════════════════════
function generateFullReport(areas) {
const allIssues = areas.flatMap(a => a.issues || []);
const report = {
report_id: `INSPECT-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}`,
version: 'AGE-OS-v1.0',
generated_at: new Date().toISOString(),
generated_by: '铸渊全面排查 · Phase 1 Step 1',
summary: {
total_areas: areas.length,
total_issues: allIssues.length,
issues_by_priority: {
critical: allIssues.filter(i => i.includes('核心')).length,
warning: allIssues.length - allIssues.filter(i => i.includes('核心')).length,
},
},
areas: {},
all_issues: allIssues,
};
for (const area of areas) {
report.areas[area.area] = area;
}
return report;
}
// ══════════════════════════════════════════════════════════
// 主执行函数
// ══════════════════════════════════════════════════════════
function run() {
console.log('');
console.log('🔍 ═══════════════════════════════════════════');
console.log(' 铸渊全面排查 · AGE OS v1.0 Phase 1 Step 1');
console.log(' 时间: ' + new Date().toISOString());
console.log('═══════════════════════════════════════════════');
const areas = [
inspectRepoStructure(),
inspectAutomation(),
inspectEntryPoints(),
inspectBulletins(),
inspectServices(),
inspectCredentials(),
inspectNodeConnections(),
inspectPersonaBots(),
];
const report = generateFullReport(areas);
console.log('\n');
console.log('═══════════════════════════════════════════════');
console.log(`📊 排查报告: ${report.report_id}`);
console.log(` 总排查领域: ${report.summary.total_areas}`);
console.log(` 发现问题数: ${report.summary.total_issues}`);
if (report.summary.total_issues > 0) {
console.log(' 问题列表:');
report.all_issues.forEach((issue, i) => {
console.log(` ${i + 1}. ${issue}`);
});
} else {
console.log(' ✅ 未发现问题');
}
console.log('═══════════════════════════════════════════════');
return report;
}
// CLI 入口
if (require.main === module) {
const report = run();
const args = process.argv.slice(2);
// --json: 输出 JSON 到 stdout
if (args.includes('--json')) {
console.log('\n' + JSON.stringify(report, null, 2));
}
// --output <file>: 保存到文件
const outputIdx = args.indexOf('--output');
if (outputIdx >= 0 && args[outputIdx + 1]) {
const outputPath = path.resolve(args[outputIdx + 1]);
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2), 'utf-8');
console.log(`\n📝 报告已保存: ${outputPath}`);
}
// GITHUB_OUTPUT 支持
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
fs.appendFileSync(outputFile, `inspection_issues=${report.summary.total_issues}\n`);
fs.appendFileSync(outputFile, `inspection_report_id=${report.report_id}\n`);
}
}
module.exports = { run, inspectRepoStructure, inspectAutomation, inspectEntryPoints, inspectBulletins, inspectServices, inspectCredentials, inspectNodeConnections, inspectPersonaBots };