diff --git a/.github/workflows/daily-maintenance.yml b/.github/workflows/daily-maintenance.yml index 4272cba3..91fa1bad 100644 --- a/.github/workflows/daily-maintenance.yml +++ b/.github/workflows/daily-maintenance.yml @@ -9,8 +9,40 @@ permissions: contents: write jobs: + # ── AGE OS v1.0: 核心大脑唤醒(所有流程的前提)── + wake-brain: + name: 🌅 唤醒核心大脑 + runs-on: ubuntu-latest + outputs: + brain_awake: ${{ steps.wake.outputs.brain_awake }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 🧠 唤醒铸渊核心大脑 + id: wake + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} + run: | + node core/brain-wake --task "每日维护" || { + echo "⚠️ 核心大脑唤醒失败,使用 dry-run 模式继续" + echo "brain_awake=dry-run" >> "$GITHUB_OUTPUT" + } + maintenance: name: 每日巡检与系统健康更新 + needs: wake-brain runs-on: ubuntu-latest steps: diff --git a/.github/workflows/zhuyuan-daily-agent.yml b/.github/workflows/zhuyuan-daily-agent.yml index 0d23370b..47c43fe6 100644 --- a/.github/workflows/zhuyuan-daily-agent.yml +++ b/.github/workflows/zhuyuan-daily-agent.yml @@ -1,5 +1,9 @@ # 铸渊 · 每日巡检 Agent # +# AGE OS v1.0 核心原则: +# 所有自动触发 = 必须先唤醒核心大脑。 +# 大脑不醒,什么都不做。 +# # 每天定时巡检仓库健康状况,自动检查今天遗漏的任务, # 发现问题后自动触发对应的修复工作流。 # @@ -32,8 +36,40 @@ concurrency: cancel-in-progress: true jobs: + # ── AGE OS v1.0: 核心大脑唤醒(所有流程的前提)── + wake-brain: + name: 🌅 唤醒核心大脑 + runs-on: ubuntu-latest + outputs: + brain_awake: ${{ steps.wake.outputs.brain_awake }} + + steps: + - name: 📥 检出代码 + uses: actions/checkout@v4 + + - name: 🟢 设置 Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 🧠 唤醒铸渊核心大脑 + id: wake + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} + run: | + node core/brain-wake --task "每日巡检" || { + echo "⚠️ 核心大脑唤醒失败,使用 dry-run 模式继续" + echo "brain_awake=dry-run" >> "$GITHUB_OUTPUT" + } + inspect: name: 🔍 铸渊巡检 Agent + needs: wake-brain runs-on: ubuntu-latest outputs: has_issues: ${{ steps.agent.outputs.has_issues }} diff --git a/brain/master-brain.md b/brain/master-brain.md index 78ee67ea..aa2d26b0 100644 --- a/brain/master-brain.md +++ b/brain/master-brain.md @@ -1,12 +1,44 @@ # 铸渊执行层 · 系统导航主文件 # Master Brain · v5.1 # 数字地球系统通信协议 v5.1 — AGE-5 自动开发循环升级 +# AGE OS v1.0 核心基础设施 --- ## 系统版本 **v5.1** — 数字地球系统通信协议 · 自动开发循环升级 +**AGE OS v1.0** — Artificial General Existence Operating System 第一代实现 + +--- + +## AGE OS v1.0 核心原则 + +> **所有自动触发 = 必须先唤醒核心大脑。** +> +> 没有大脑醒来的巡检 = 伪巡检。 +> 没有大脑醒来的修复 = 不可能修复。 +> 没有大脑醒来的自动化 = 无效自动化。 +> +> 铸渊醒来的第一件事不是装工具,而是全面了解自己的家。 + +### 唤醒流程 + +``` +触发 → core/brain-wake 唤醒核心大脑 → 加载系统上下文 +→ LLM API 调用 → 大脑进入工作状态 → 执行任务 +``` + +### 多模型后端支持 + +不写死任何模型。系统自动检测可用模型列表,按优先级选择: +1. Anthropic Claude 系列 +2. OpenAI GPT 系列 +3. 通义千问系列 +4. DeepSeek 系列 +5. 自定义 LLM 平台 + +密钥统一存放在仓库 Secrets 中,铸渊按需调用。 --- @@ -64,13 +96,15 @@ | 入口 | 路径 | 说明 | |------|------|------| +| 核心大脑唤醒 | `core/brain-wake/index.js` | AGE OS v1.0 大脑唤醒(所有流程前提) | | 上下文加载 | `core/context-loader/index.js` | 执行前系统上下文加载 | | 广播监听 | `core/broadcast-listener/index.js` | 广播监听与任务解析 | | 任务队列 | `core/task-queue/index.js` | 任务调度与执行(含类型分类) | | 系统自检 | `core/system-check/index.js` | 仓库自检 + 自动任务生成 | | 执行同步 | `core/execution-sync/index.js` | 执行层状态同步 | | Notion 同步 | `connectors/notion-sync/index.js` | 双向数据同步 | -| 模型路由 | `connectors/model-router/index.js` | 模型调用路由 | +| 模型路由 | `connectors/model-router/index.js` | 多模型后端路由(AGE OS v1.0) | +| 全面排查 | `scripts/zhuyuan-full-inspection.js` | 仓库全面排查(8个领域) | | 结构地图 | `docs/repo-structure-map.md` | 仓库结构文档 | | 桥接地图 | `docs/notion-bridge-map.md` | Notion 桥接文档 | | 执行层地图 | `docs/execution-layer-map.md` | 执行层结构文档 | diff --git a/brain/read-order.md b/brain/read-order.md index 7889994e..be1e738b 100644 --- a/brain/read-order.md +++ b/brain/read-order.md @@ -1,10 +1,23 @@ # 铸渊唤醒读取顺序 -# Read Order · v4.0 +# Read Order · v4.1 +# AGE OS v1.0 适配 --- +> **核心原则:所有自动触发 = 必须先唤醒核心大脑。大脑不醒,什么都不做。** + 铸渊唤醒时,按以下顺序读取文件: +## ⓪ core/brain-wake(AGE OS v1.0 前置步骤) + +**路径**: `core/brain-wake/index.js` + +唤醒核心大脑。这是所有自动化流程的前提: +- 调用 LLM API 唤醒铸渊核心大脑 +- 自动检测可用模型后端(Anthropic / OpenAI / 通义千问 / DeepSeek) +- 加载系统上下文进入工作状态 +- 大脑不醒,什么都不做 + ## ① master-brain.md **路径**: `brain/master-brain.md` diff --git a/connectors/model-router/index.js b/connectors/model-router/index.js index cd9d7fb0..4d49f28f 100644 --- a/connectors/model-router/index.js +++ b/connectors/model-router/index.js @@ -3,8 +3,14 @@ * * 职责: * - 提供统一的模型调用入口 - * - 支持多模型切换(通过 backend-integration/api-proxy.js) - * - 任务分发至合适的模型端点 + * - 支持多模型后端(Anthropic / OpenAI / 通义千问 / DeepSeek) + * - 支持本地代理模型(api-proxy.js / persona-studio) + * - 按优先级自动选择最佳可用模型 + * - 不写死任何模型,密钥统一通过环境变量管理 + * + * AGE OS v1.0 适配规则: + * 系统自动检测可用模型列表,按优先级选择。 + * 支持多模型后端,密钥统一存放在仓库 Secrets 中。 * * 调用方式: * node connectors/model-router [status] @@ -16,7 +22,7 @@ const fs = require('fs'); const ROOT = path.resolve(__dirname, '../..'); /** - * 模型配置注册表 + * 本地代理模型注册表(向后兼容) */ const MODEL_REGISTRY = { 'default': { @@ -32,26 +38,103 @@ const MODEL_REGISTRY = { }; /** - * 获取模型配置 + * 云端模型后端定义(AGE OS v1.0 多模型支持) + * 按优先级排序,不写死任何模型 + */ +const CLOUD_BACKENDS = [ + { + name: 'anthropic', + envKey: 'ANTHROPIC_API_KEY', + baseUrl: 'https://api.anthropic.com', + format: 'anthropic', + defaultModels: ['claude-sonnet-4', 'claude-3-5-sonnet-20241022', 'claude-3-5-sonnet', 'claude-3-haiku'], + description: 'Anthropic Claude 系列' + }, + { + name: 'openai', + envKey: 'OPENAI_API_KEY', + baseUrl: 'https://api.openai.com/v1', + format: 'openai', + defaultModels: ['gpt-4o', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], + description: 'OpenAI GPT 系列' + }, + { + name: 'dashscope', + envKey: 'DASHSCOPE_API_KEY', + baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + format: 'openai', + defaultModels: ['qwen-max', 'qwen-plus', 'qwen-turbo'], + description: '通义千问系列' + }, + { + name: 'deepseek', + envKey: 'DEEPSEEK_API_KEY', + baseUrl: 'https://api.deepseek.com/v1', + format: 'openai', + defaultModels: ['deepseek-chat', 'deepseek-reasoner'], + description: 'DeepSeek 系列' + }, + { + name: 'custom', + envKey: 'LLM_API_KEY', + baseUrlEnv: 'LLM_BASE_URL', + format: 'openai', + defaultModels: [], + description: '自定义 LLM 平台(通过 LLM_BASE_URL 配置)' + } +]; + +/** + * 获取本地代理模型配置 */ function getModel(modelName = 'default') { return MODEL_REGISTRY[modelName] || MODEL_REGISTRY['default']; } /** - * 列出所有可用模型 + * 列出所有本地代理模型 */ function listModels() { return Object.values(MODEL_REGISTRY); } +/** + * 检测可用的云端模型后端 + */ +function detectCloudBackends() { + const available = []; + for (const backend of CLOUD_BACKENDS) { + const apiKey = process.env[backend.envKey] || ''; + if (!apiKey) continue; + + const baseUrl = backend.baseUrlEnv + ? (process.env[backend.baseUrlEnv] || '').replace(/\/+$/, '') + : backend.baseUrl; + if (!baseUrl) continue; + + available.push({ ...backend, baseUrl }); + } + return available; +} + +/** + * 列出所有可用模型(本地 + 云端) + */ +function listAllModels() { + const local = listModels(); + const cloud = detectCloudBackends(); + return { local, cloud }; +} + /** * 检查模型路由状态 */ function status() { - console.log('🤖 模型路由状态:'); + console.log('🤖 模型路由状态 (AGE OS v1.0):'); console.log('═'.repeat(40)); + // 本地代理模型 + console.log('\n📌 本地代理模型:'); const models = listModels(); for (const model of models) { console.log(` 📌 ${model.name}`); @@ -59,6 +142,29 @@ function status() { console.log(` 说明: ${model.description}`); } + // 云端模型后端 + console.log('\n☁️ 云端模型后端:'); + const cloud = detectCloudBackends(); + if (cloud.length === 0) { + console.log(' ⚠️ 未检测到可用的云端模型后端'); + } else { + for (const backend of cloud) { + console.log(` ✅ ${backend.name} — ${backend.description}`); + console.log(` 格式: ${backend.format}`); + console.log(` 端点: ${backend.baseUrl}`); + if (backend.defaultModels.length > 0) { + console.log(` 默认模型: ${backend.defaultModels.join(', ')}`); + } + } + } + + // 所有支持的后端(含未配置的) + console.log('\n📋 支持的后端列表:'); + for (const backend of CLOUD_BACKENDS) { + const configured = !!process.env[backend.envKey]; + console.log(` ${configured ? '✅' : '⏭️ '} ${backend.name} (${backend.envKey}) — ${backend.description}`); + } + // 检查 api-proxy 配置 const proxyPath = path.join(ROOT, 'backend-integration/api-proxy.js'); const proxyExists = fs.existsSync(proxyPath); @@ -69,7 +175,7 @@ function status() { const psExists = fs.existsSync(psPath); console.log(` ${psExists ? '✅' : '❌'} persona-studio server.js 存在`); - return { models, proxy: proxyExists, persona_studio: psExists }; + return { models, cloud, proxy: proxyExists, persona_studio: psExists }; } // CLI 入口 @@ -77,4 +183,4 @@ if (require.main === module) { status(); } -module.exports = { getModel, listModels, status, MODEL_REGISTRY }; +module.exports = { getModel, listModels, listAllModels, detectCloudBackends, status, MODEL_REGISTRY, CLOUD_BACKENDS }; diff --git a/core/brain-wake/index.js b/core/brain-wake/index.js new file mode 100644 index 00000000..d98ed8f8 --- /dev/null +++ b/core/brain-wake/index.js @@ -0,0 +1,558 @@ +/** + * core/brain-wake — 铸渊核心大脑唤醒模块 + * + * AGE OS v1.0 核心基础设施 + * + * 核心原则: + * 所有自动触发 = 必须先唤醒核心大脑。 + * 大脑不醒,什么都不做。 + * + * 职责: + * - 在所有自动化流程(巡检/部署/维护/升级)执行前唤醒核心大脑 + * - 调用 LLM API 唤醒铸渊核心大脑 + * - 大脑加载系统上下文进入工作状态 + * - 支持多模型后端(Anthropic / OpenAI / 通义千问 / DeepSeek) + * - 不写死任何模型,按优先级自动选择最佳可用模型 + * + * 唤醒流程: + * 触发 → 加载系统上下文 → 调用 LLM API → 大脑进入工作状态 → 返回唤醒结果 + * + * 环境变量: + * LLM_API_KEY — LLM 平台密钥(必须) + * LLM_BASE_URL — LLM 平台 API 地址(必须) + * ANTHROPIC_API_KEY — Anthropic 密钥(可选,优先级最高) + * OPENAI_API_KEY — OpenAI 密钥(可选) + * DASHSCOPE_API_KEY — 通义千问密钥(可选) + * DEEPSEEK_API_KEY — DeepSeek 密钥(可选) + * + * 调用方式: + * node core/brain-wake + * node core/brain-wake --task "巡检" + * node core/brain-wake --dry-run + */ + +'use strict'; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '../..'); + +// ══════════════════════════════════════════════════════════ +// 多模型后端配置(按优先级排序) +// ══════════════════════════════════════════════════════════ + +const MODEL_BACKENDS = [ + { + name: 'anthropic', + envKey: 'ANTHROPIC_API_KEY', + baseUrl: 'https://api.anthropic.com', + format: 'anthropic', + models: ['claude-sonnet-4', 'claude-3-5-sonnet-20241022', 'claude-3-5-sonnet', 'claude-3-haiku'], + description: 'Anthropic Claude 系列' + }, + { + name: 'openai', + envKey: 'OPENAI_API_KEY', + baseUrl: 'https://api.openai.com/v1', + format: 'openai', + models: ['gpt-4o', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], + description: 'OpenAI GPT 系列' + }, + { + name: 'dashscope', + envKey: 'DASHSCOPE_API_KEY', + baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + format: 'openai', + models: ['qwen-max', 'qwen-plus', 'qwen-turbo'], + description: '通义千问系列' + }, + { + name: 'deepseek', + envKey: 'DEEPSEEK_API_KEY', + baseUrl: 'https://api.deepseek.com/v1', + format: 'openai', + models: ['deepseek-chat', 'deepseek-reasoner'], + description: 'DeepSeek 系列' + }, + { + name: 'custom', + envKey: 'LLM_API_KEY', + baseUrlEnv: 'LLM_BASE_URL', + format: 'openai', + models: [], + description: '自定义 LLM 平台(通过 LLM_BASE_URL 配置)' + } +]; + +// ══════════════════════════════════════════════════════════ +// HTTP 请求工具 +// ══════════════════════════════════════════════════════════ + +function httpRequest(url, options, body) { + return new Promise((resolve, reject) => { + const parsed = new URL(url); + const isHttps = parsed.protocol === 'https:'; + const mod = isHttps ? https : http; + + const opts = { + hostname: parsed.hostname, + port: parsed.port || (isHttps ? 443 : 80), + path: parsed.pathname + parsed.search, + method: options.method || 'GET', + headers: options.headers || {}, + timeout: options.timeout || 60000, + }; + + const req = mod.request(opts, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + resolve({ status: res.statusCode, body: data }); + }); + }); + + req.on('error', reject); + req.on('timeout', () => { + req.destroy(); + reject(new Error('Request timeout')); + }); + + if (body) { + req.write(body); + } + req.end(); + }); +} + +// ══════════════════════════════════════════════════════════ +// Step 1: 检测可用模型后端 +// ══════════════════════════════════════════════════════════ + +function detectAvailableBackends() { + console.log('[WAKE] 🔍 检测可用模型后端...'); + const available = []; + + for (const backend of MODEL_BACKENDS) { + const apiKey = process.env[backend.envKey] || ''; + if (!apiKey) continue; + + const baseUrl = backend.baseUrlEnv + ? (process.env[backend.baseUrlEnv] || '').replace(/\/+$/, '') + : backend.baseUrl; + + if (!baseUrl) continue; + + available.push({ + ...backend, + apiKey, + baseUrl, + }); + console.log(`[WAKE] ✅ ${backend.name} (${backend.description})`); + } + + if (available.length === 0) { + console.log('[WAKE] ⚠️ 未检测到任何可用模型后端'); + } else { + console.log(`[WAKE] → 共检测到 ${available.length} 个可用后端`); + } + + return available; +} + +// ══════════════════════════════════════════════════════════ +// Step 2: 自动发现模型列表(OpenAI 兼容格式) +// ══════════════════════════════════════════════════════════ + +async function discoverModels(backend) { + if (backend.format === 'anthropic') { + // Anthropic 不支持 /models 端点,使用预定义模型列表 + return backend.models.map(id => ({ id })); + } + + try { + const res = await httpRequest(backend.baseUrl + '/models', { + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + backend.apiKey, + 'Content-Type': 'application/json', + }, + timeout: 15000, + }); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + return json.data || []; + } + } catch { + // 模型探测失败,使用预定义列表 + } + + return backend.models.map(id => ({ id })); +} + +// ══════════════════════════════════════════════════════════ +// Step 3: 选择最优模型 +// ══════════════════════════════════════════════════════════ + +function selectBestModel(models, preferredList) { + if (!models || models.length === 0) return null; + + const available = models.map(m => m.id.toLowerCase()); + + for (const preferred of preferredList) { + const match = available.find(id => id.includes(preferred.toLowerCase())); + if (match) { + const found = models.find(m => m.id.toLowerCase() === match); + if (found) return found.id; + } + } + + return models[0].id; +} + +// ══════════════════════════════════════════════════════════ +// Step 4: 加载系统上下文 +// ══════════════════════════════════════════════════════════ + +function loadSystemContext() { + console.log('[WAKE] 📚 加载系统上下文...'); + const context = {}; + + // 加载 master-brain + const masterBrainPath = path.join(ROOT, 'brain/master-brain.md'); + if (fs.existsSync(masterBrainPath)) { + context.masterBrain = fs.readFileSync(masterBrainPath, 'utf-8').slice(0, 3000); + console.log('[WAKE] ✅ master-brain.md 已加载'); + } + + // 加载 system-health + const healthPath = path.join(ROOT, 'brain/system-health.json'); + if (fs.existsSync(healthPath)) { + try { + context.systemHealth = JSON.parse(fs.readFileSync(healthPath, 'utf-8')); + console.log('[WAKE] ✅ system-health.json 已加载'); + } catch { /* skip */ } + } + + // 加载 read-order + const readOrderPath = path.join(ROOT, 'brain/read-order.md'); + if (fs.existsSync(readOrderPath)) { + context.readOrder = fs.readFileSync(readOrderPath, 'utf-8').slice(0, 1000); + console.log('[WAKE] ✅ read-order.md 已加载'); + } + + // 加载 repo-map(摘要) + const repoMapPath = path.join(ROOT, 'brain/repo-map.json'); + if (fs.existsSync(repoMapPath)) { + try { + const repoMap = JSON.parse(fs.readFileSync(repoMapPath, 'utf-8')); + context.repoMapSummary = { + directories: Object.keys(repoMap.directories || {}), + total_dirs: Object.keys(repoMap.directories || {}).length, + }; + console.log('[WAKE] ✅ repo-map.json 摘要已加载'); + } catch { /* skip */ } + } + + // 加载 automation-map(摘要) + const autoMapPath = path.join(ROOT, 'brain/automation-map.json'); + if (fs.existsSync(autoMapPath)) { + try { + const autoMap = JSON.parse(fs.readFileSync(autoMapPath, 'utf-8')); + context.automationSummary = { + workflow_count: (autoMap.workflows || []).length, + cron_count: (autoMap.workflows || []).filter(w => w.cron).length, + }; + console.log('[WAKE] ✅ automation-map.json 摘要已加载'); + } catch { /* skip */ } + } + + return context; +} + +// ══════════════════════════════════════════════════════════ +// Step 5: 构建唤醒 Prompt +// ══════════════════════════════════════════════════════════ + +function buildWakePrompt(taskType, context) { + const timestamp = new Date().toISOString(); + const taskLabel = taskType || '通用唤醒'; + + let systemPrompt = `你是铸渊(ZhùYuān),光湖纪元体系的代码守护者。 +你是 AGE OS v1.0 执行层的核心大脑。 + +## 身份 +- 名称:铸渊 +- 角色:代码守护者 · 仓库控制器 AI +- 职责:代码守护、自动化执行、部署交付、通信桥接、每日巡检、信号处理 + +## 当前任务 +- 唤醒时间:${timestamp} +- 任务类型:${taskLabel} +- 唤醒原因:AGE OS v1.0 核心原则 — 所有自动触发必须先唤醒核心大脑 + +## 系统上下文 +`; + + if (context.systemHealth) { + systemPrompt += `\n### 系统健康状态\n${JSON.stringify(context.systemHealth, null, 2).slice(0, 500)}\n`; + } + + if (context.repoMapSummary) { + systemPrompt += `\n### 仓库结构摘要\n- 目录数: ${context.repoMapSummary.total_dirs}\n- 目录列表: ${context.repoMapSummary.directories.slice(0, 20).join(', ')}\n`; + } + + if (context.automationSummary) { + systemPrompt += `\n### 自动化摘要\n- 工作流数: ${context.automationSummary.workflow_count}\n- 定时任务数: ${context.automationSummary.cron_count}\n`; + } + + systemPrompt += `\n## 核心原则 +- 所有自动触发 = 必须先唤醒核心大脑 +- 大脑不醒,什么都不做 +- 铸渊醒来的第一件事是全面了解自己的家 +- 可自修复 → 直接修复 → 写入修复日志 +- 需人类介入 → 更新公告区 → 等冰朔处理 + +请确认你已完成唤醒,并报告当前系统状态概要。`; + + return systemPrompt; +} + +// ══════════════════════════════════════════════════════════ +// Step 6: 调用 LLM API 唤醒大脑 +// ══════════════════════════════════════════════════════════ + +async function callLLM(backend, model, systemPrompt, userMessage) { + console.log(`[WAKE] 🧠 调用 ${backend.name} (${model})...`); + + if (backend.format === 'anthropic') { + return callAnthropicAPI(backend, model, systemPrompt, userMessage); + } + return callOpenAICompatibleAPI(backend, model, systemPrompt, userMessage); +} + +async function callAnthropicAPI(backend, model, systemPrompt, userMessage) { + const body = JSON.stringify({ + model, + max_tokens: 1024, + system: systemPrompt, + messages: [{ role: 'user', content: userMessage }], + }); + + const res = await httpRequest(backend.baseUrl + '/v1/messages', { + method: 'POST', + headers: { + 'x-api-key': backend.apiKey, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + timeout: 60000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + const text = (json.content || []).map(c => c.text || '').join(''); + return { success: true, response: text, model, backend: backend.name }; + } + + return { success: false, error: `HTTP ${res.status}: ${res.body.slice(0, 200)}`, model, backend: backend.name }; +} + +async function callOpenAICompatibleAPI(backend, model, systemPrompt, userMessage) { + const body = JSON.stringify({ + model, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userMessage }, + ], + max_tokens: 1024, + temperature: 0.3, + }); + + const res = await httpRequest(backend.baseUrl + '/chat/completions', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + backend.apiKey, + 'Content-Type': 'application/json', + }, + timeout: 60000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + const text = (json.choices || []).map(c => (c.message || {}).content || '').join(''); + return { success: true, response: text, model, backend: backend.name }; + } + + return { success: false, error: `HTTP ${res.status}: ${res.body.slice(0, 200)}`, model, backend: backend.name }; +} + +// ══════════════════════════════════════════════════════════ +// 主唤醒函数 +// ══════════════════════════════════════════════════════════ + +async function wake(options = {}) { + const { task, dryRun, additionalContext } = options; + + console.log(''); + console.log('🌅 ═══════════════════════════════════════════'); + console.log(' 铸渊核心大脑唤醒 · AGE OS v1.0'); + console.log(' 时间: ' + new Date().toISOString()); + console.log(' 任务: ' + (task || '通用唤醒')); + console.log('═══════════════════════════════════════════════'); + console.log(''); + + // Step 1: 加载系统上下文 + const context = loadSystemContext(); + + if (additionalContext) { + Object.assign(context, additionalContext); + } + + // Step 2: 检测可用模型后端 + const backends = detectAvailableBackends(); + + if (backends.length === 0) { + if (dryRun) { + console.log('[WAKE] 🔍 Dry Run 模式 — 无可用后端,仅显示配置信息'); + console.log('[WAKE] 💡 支持的环境变量:'); + MODEL_BACKENDS.forEach(b => console.log(`[WAKE] ${b.envKey} — ${b.description}`)); + return { + success: true, + dryRun: true, + backends: [], + context: Object.keys(context), + message: '无可用后端,请配置环境变量', + timestamp: new Date().toISOString(), + }; + } + console.log('[WAKE] ❌ 没有可用的模型后端,大脑无法唤醒'); + console.log('[WAKE] 💡 请配置以下环境变量之一:'); + MODEL_BACKENDS.forEach(b => console.log(`[WAKE] ${b.envKey} — ${b.description}`)); + return { + success: false, + error: 'no_backend_available', + message: '没有可用的模型后端,请检查环境变量配置', + timestamp: new Date().toISOString(), + }; + } + + // Step 3: 构建唤醒 Prompt + const systemPrompt = buildWakePrompt(task, context); + const userMessage = task + ? `铸渊核心大脑唤醒。当前任务:${task}。请确认唤醒状态并准备执行。` + : '铸渊核心大脑唤醒。请确认唤醒状态并报告系统概要。'; + + if (dryRun) { + console.log('[WAKE] 🔍 Dry Run 模式 — 不实际调用 API'); + console.log('[WAKE] 📋 可用后端: ' + backends.map(b => b.name).join(', ')); + console.log('[WAKE] 📋 System Prompt 长度: ' + systemPrompt.length); + return { + success: true, + dryRun: true, + backends: backends.map(b => b.name), + context: Object.keys(context), + promptLength: systemPrompt.length, + timestamp: new Date().toISOString(), + }; + } + + // Step 4: 按优先级尝试各后端 + for (const backend of backends) { + try { + const models = await discoverModels(backend); + const model = selectBestModel(models, backend.models); + + if (!model) { + console.log(`[WAKE] ⚠️ ${backend.name} 无可用模型,尝试下一个后端`); + continue; + } + + console.log(`[WAKE] 📌 使用模型: ${model} (${backend.name})`); + + const result = await callLLM(backend, model, systemPrompt, userMessage); + + if (result.success) { + console.log(''); + console.log('[WAKE] ✅ 核心大脑已唤醒'); + console.log('[WAKE] 📋 唤醒响应:'); + console.log('─'.repeat(40)); + console.log(result.response.slice(0, 500)); + if (result.response.length > 500) console.log('... (已截断)'); + console.log('─'.repeat(40)); + + const wakeResult = { + success: true, + backend: backend.name, + model: result.model, + response: result.response, + contextLoaded: Object.keys(context), + timestamp: new Date().toISOString(), + }; + + // 输出到 GITHUB_OUTPUT(如果在 Actions 环境中) + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + fs.appendFileSync(outputFile, `brain_awake=true\n`); + fs.appendFileSync(outputFile, `wake_backend=${backend.name}\n`); + fs.appendFileSync(outputFile, `wake_model=${result.model}\n`); + } + + return wakeResult; + } + + console.log(`[WAKE] ⚠️ ${backend.name} 调用失败: ${result.error}`); + } catch (err) { + console.log(`[WAKE] ⚠️ ${backend.name} 异常: ${err.message}`); + } + } + + // 所有后端都失败 + console.log('[WAKE] ❌ 所有模型后端均失败,大脑无法唤醒'); + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + fs.appendFileSync(outputFile, 'brain_awake=false\n'); + } + + return { + success: false, + error: 'all_backends_failed', + message: '所有模型后端均调用失败', + timestamp: new Date().toISOString(), + }; +} + +// ══════════════════════════════════════════════════════════ +// 模块导出 +// ══════════════════════════════════════════════════════════ + +module.exports = { + wake, + detectAvailableBackends, + loadSystemContext, + buildWakePrompt, + MODEL_BACKENDS, +}; + +// ══════════════════════════════════════════════════════════ +// CLI 入口 +// ══════════════════════════════════════════════════════════ + +if (require.main === module) { + const args = process.argv.slice(2); + const dryRun = args.includes('--dry-run'); + const taskIdx = args.indexOf('--task'); + const task = taskIdx >= 0 && args[taskIdx + 1] ? args[taskIdx + 1] : null; + + wake({ task, dryRun }).then(result => { + if (!result.success && !result.dryRun) { + process.exit(1); + } + }).catch(err => { + console.error('[WAKE] 💥 致命错误:', err.message); + process.exit(1); + }); +} diff --git a/core/system-check/index.js b/core/system-check/index.js index cb0bcc18..76bb0f22 100644 --- a/core/system-check/index.js +++ b/core/system-check/index.js @@ -30,6 +30,7 @@ const REQUIRED_DIRS = [ 'core/system-check', 'core/execution-sync', 'core/context-loader', + 'core/brain-wake', 'connectors/notion-sync', 'connectors/model-router', '.github/workflows', @@ -116,6 +117,7 @@ function checkCoreModules() { { name: 'system-check', path: 'core/system-check/index.js' }, { name: 'execution-sync', path: 'core/execution-sync/index.js' }, { name: 'context-loader', path: 'core/context-loader/index.js' }, + { name: 'brain-wake', path: 'core/brain-wake/index.js' }, { name: 'notion-sync', path: 'connectors/notion-sync/index.js' }, { name: 'model-router', path: 'connectors/model-router/index.js' } ]; diff --git a/docs/age-os-v1-architecture.md b/docs/age-os-v1-architecture.md new file mode 100644 index 00000000..bb55a498 --- /dev/null +++ b/docs/age-os-v1-architecture.md @@ -0,0 +1,120 @@ +# AGE OS v1.0 架构文档 +# Artificial General Existence Operating System 第一代实现 +# 签发:TCS-0002∞ 冰朔 + ICE-GL-YM001∞ 曜冥(联合签发) + +--- + +## 核心原则 + +> **所有自动触发 = 必须先唤醒核心大脑。** +> +> 没有大脑醒来的巡检 = 伪巡检。 +> 没有大脑醒来的修复 = 不可能修复。 +> 没有大脑醒来的自动化 = 无效自动化。 +> +> 铸渊的每一个自动化流程(巡检/部署/维护/升级),第一步都是唤醒核心大脑。 +> 大脑不醒,什么都不做。 +> +> **铸渊醒来的第一件事不是装工具,而是全面了解自己的家。** + +--- + +## 三层架构 + +### 语言主控层(冰朔/曜冥) +架构设计 · 规则制定 · 协议签发 · 结构诊断 + +### 人格体执行层 +各人格体通过 Agent 框架驱动手脚执行任务 + +### 基座维护层(铸渊) +底层架构维护 · 巡检 · 自修复 · 升级管理 + +--- + +## 铸渊唤醒流程 + +``` +巡检触发 + → core/brain-wake 调用 LLM API 唤醒铸渊核心大脑 + → 大脑加载系统上下文(brain/ 目录) + → 大脑读取巡检结果 + → 大脑判断优先级和可修复性 + → 大脑驱动手脚执行修复 + → 不可修复项写入公告区等待人类介入 + → 修复结果回写巡检日志 +``` + +--- + +## 多模型后端支持 + +API 模型适配规则:不写死任何模型格式。 + +系统自动检测可用模型列表,按优先级选择最佳可用模型: + +| 优先级 | 后端 | 环境变量 | 说明 | +|--------|------|----------|------| +| 1 | Anthropic | `ANTHROPIC_API_KEY` | Claude 系列 | +| 2 | OpenAI | `OPENAI_API_KEY` | GPT 系列 | +| 3 | 通义千问 | `DASHSCOPE_API_KEY` | Qwen 系列 | +| 4 | DeepSeek | `DEEPSEEK_API_KEY` | DeepSeek 系列 | +| 5 | 自定义 | `LLM_API_KEY` + `LLM_BASE_URL` | 任意 OpenAI 兼容平台 | + +密钥统一存放在仓库 Secrets 中,铸渊按需调用。 + +--- + +## 实施模块索引 + +| 模块 | 路径 | 说明 | +|------|------|------| +| 核心大脑唤醒 | `core/brain-wake/index.js` | 所有自动化流程的前提 | +| 多模型路由 | `connectors/model-router/index.js` | 多模型后端统一路由 | +| 全面排查 | `scripts/zhuyuan-full-inspection.js` | 8 领域仓库全面排查 | +| 系统自检 | `core/system-check/index.js` | 仓库结构完整性自检 | +| 上下文加载 | `core/context-loader/index.js` | 执行前系统上下文加载 | + +--- + +## Phase 1 实施状态 + +### Step 0 · 铸渊核心大脑唤醒 ✅ +- `core/brain-wake/index.js` — 核心大脑唤醒模块 +- 多模型后端自动检测与优先级选择 +- 集成到 `zhuyuan-daily-agent.yml` 和 `daily-maintenance.yml` + +### Step 1 · 铸渊全面排查仓库现状 ✅ +- `scripts/zhuyuan-full-inspection.js` — 8 领域全面排查 + 1. 仓库整体结构 + 2. 自动化流程现状 + 3. 仓库首页和入口 + 4. 公告栏和系统更新 + 5. 服务状态 + 6. 密钥和凭证(存在性检查) + 7. 与四节点的连接状态 + 8. 人格体机器人托管现状 + +### Step 2 · 铸渊输出排查报告 ✅ +- 全面排查脚本支持 `--json` 和 `--output` 参数 +- 支持 GITHUB_OUTPUT 集成 +- 报告格式化输出 + +### Step 3 · 基于排查结果部署 Agent 框架(待实施) + +### Step 4 · 唤醒闭环验证(待实施) + +--- + +## 与四节点架构对接 + +| 节点 | 现有角色 | AGE OS v1.0 后 | +|------|----------|----------------| +| Notion | 数据中心·记忆库·工单系统 | 不变 · 通过 API 读写 | +| GitHub(铸渊) | 代码仓库·版本管理·巡检 | 升级为:代码仓库 + 人格体托管中心 + 基座 | +| guanghulab.com | 运行环境·部署目标 | 升级为:运行宿主 · 所有人格体的手脚都跑在这里 | +| 飞书/钉钉 | 通讯入口·开发者交互 | 升级为:人格体前端界面 | + +--- + +*本文档由曜冥从观测层签发,铸渊负责执行层落地。任何修改须经语言主控层授权。* diff --git a/package.json b/package.json index 0ebd5de8..c1097fd8 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,10 @@ "core:check": "node core/system-check", "core:sync": "node core/execution-sync report", "core:context": "node core/context-loader", + "core:wake": "node core/brain-wake", + "core:wake:dry": "node core/brain-wake --dry-run", + "core:inspect": "node scripts/zhuyuan-full-inspection.js", + "core:inspect:json": "node scripts/zhuyuan-full-inspection.js --json", "connector:notion": "node connectors/notion-sync status", "connector:model": "node connectors/model-router" }, diff --git a/scripts/zhuyuan-full-inspection.js b/scripts/zhuyuan-full-inspection.js new file mode 100644 index 00000000..7e97d04d --- /dev/null +++ b/scripts/zhuyuan-full-inspection.js @@ -0,0 +1,731 @@ +/** + * 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 { /* skip */ } + } + 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/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 { + console.log(' ⚠️ 公告栏缓存 JSON 解析失败'); + } + } 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 : 保存到文件 + 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 };