Merge pull request #108 from qinfendebingshuo/copilot/fix-automated-inspection-process

feat: AGE OS v1.0 Phase 1 — core brain wake, multi-model router, full inspection, Notion Agent cluster integration
This commit is contained in:
冰朔 2026-03-15 16:26:46 +08:00 committed by GitHub
commit ac20b9da74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 2249 additions and 11 deletions

View File

@ -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:

View File

@ -0,0 +1,104 @@
# 铸渊 · Notion Agent 唤醒请求监听
#
# AGE OS v1.0 · Section 7: Notion Agent 集群集成方案
#
# 核心逻辑:
# Notion Agent 集群每次触发前,先写入「唤醒请求」到 Notion 数据库
# → 铸渊定时监听该数据库
# → 发现唤醒请求 → 调用 LLM API 唤醒对应人格体核心大脑
# → 大脑醒来后执行任务 → 回写结果
#
# 触发条件:
# 1. 定时轮询(北京时间每 15 分钟一次)
# 2. 手动触发
#
# 支持唤醒的人格体:
# - 霜砚(认知层守护者 · Notion 工作区管理)
# - 铸渊(执行层守护者 · 代码仓库管理)
name: 📡 铸渊 · Notion Agent 唤醒监听
on:
schedule:
# 每 15 分钟轮询一次 Notion 唤醒请求数据库
- cron: '*/15 * * * *'
workflow_dispatch:
permissions:
contents: write
concurrency:
group: notion-wake-listener
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 "Notion唤醒请求监听" || {
echo "⚠️ 核心大脑唤醒失败,使用 dry-run 模式继续"
echo "brain_awake=dry-run" >> "$GITHUB_OUTPUT"
}
# ── 监听 Notion 唤醒请求 ──
listen:
name: 📡 监听唤醒请求
needs: wake-brain
runs-on: ubuntu-latest
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🟢 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 📡 轮询 Notion 唤醒请求数据库
id: poll
env:
NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
WAKE_REQUEST_DB_ID: ${{ secrets.WAKE_REQUEST_DB_ID }}
SIGNAL_LOG_DB_ID: ${{ secrets.SIGNAL_LOG_DB_ID }}
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 connectors/notion-wake-listener poll
- name: 📊 输出监听结果
if: always()
run: |
echo "═══════════════════════════════════════════"
echo "📡 Notion Agent 唤醒请求监听结果"
echo "═══════════════════════════════════════════"
echo "处理数: ${{ steps.poll.outputs.wake_requests_processed || '0' }}"
echo "成功数: ${{ steps.poll.outputs.wake_requests_succeeded || '0' }}"
echo "失败数: ${{ steps.poll.outputs.wake_requests_failed || '0' }}"
echo "✅ 监听流程结束"

View File

@ -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 }}

View File

@ -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,16 @@
| 入口 | 路径 | 说明 |
|------|------|------|
| 核心大脑唤醒 | `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 |
| Notion 唤醒监听 | `connectors/notion-wake-listener/index.js` | Notion Agent 集群唤醒请求监听 |
| 全面排查 | `scripts/zhuyuan-full-inspection.js` | 仓库全面排查8个领域 |
| 结构地图 | `docs/repo-structure-map.md` | 仓库结构文档 |
| 桥接地图 | `docs/notion-bridge-map.md` | Notion 桥接文档 |
| 执行层地图 | `docs/execution-layer-map.md` | 执行层结构文档 |

View File

@ -1,10 +1,23 @@
# 铸渊唤醒读取顺序
# Read Order · v4.0
# Read Order · v4.1
# AGE OS v1.0 适配
---
> **核心原则:所有自动触发 = 必须先唤醒核心大脑。大脑不醒,什么都不做。**
铸渊唤醒时,按以下顺序读取文件:
## ⓪ core/brain-wakeAGE OS v1.0 前置步骤)
**路径**: `core/brain-wake/index.js`
唤醒核心大脑。这是所有自动化流程的前提:
- 调用 LLM API 唤醒铸渊核心大脑
- 自动检测可用模型后端Anthropic / OpenAI / 通义千问 / DeepSeek
- 加载系统上下文进入工作状态
- 大脑不醒,什么都不做
## ① master-brain.md
**路径**: `brain/master-brain.md`

View File

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

View File

@ -0,0 +1,392 @@
/**
* connectors/notion-wake-listener Notion Agent 集群唤醒请求监听器
*
* AGE OS v1.0 · Section 7: Notion Agent 集群集成方案
*
* 核心问题
* Notion Agent 集群每次触发的是"机器人脚本"不是霜砚的核心大脑
* 没有大脑醒来的巡检 = 伪巡检
*
* 集成方案
* Notion Agent 触发 写入唤醒请求到指定数据库
* 铸渊定时监听该数据库 发现唤醒请求
* 铸渊调用 LLM API 唤醒霜砚核心大脑
* 大脑醒来后读取巡检结果 大脑做出决策
* 通过 Notion API 执行操作 大脑休眠
*
* 环境变量
* NOTION_TOKEN Notion API token必须
* WAKE_REQUEST_DB_ID 唤醒请求数据库 ID必须
* SIGNAL_LOG_DB_ID 信号日志数据库 ID可选
* LLM_API_KEY / ANTHROPIC_API_KEY / etc. LLM API 密钥
*
* 唤醒请求数据库 Schema
* - 标题 (title) 唤醒请求标题 "Pipeline-A 巡检唤醒"
* - 请求方 (select) 发起请求的 Agent "巡检引擎", "工单引擎", "接力引擎"
* - 唤醒对象 (select) 要唤醒的人格体"霜砚" / "铸渊"
* - Pipeline (select) Pipeline 编号A-I
* - 任务描述 (rich_text) 任务上下文描述
* - 状态 (select) "待处理" / "处理中" / "已完成" / "失败"
* - 创建时间 (created_time)
*
* 调用方式
* node connectors/notion-wake-listener poll
* node connectors/notion-wake-listener status
*/
'use strict';
const https = require('https');
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '../..');
const NOTION_TOKEN = process.env.NOTION_TOKEN || '';
const WAKE_REQUEST_DB_ID = process.env.WAKE_REQUEST_DB_ID || '';
const SIGNAL_LOG_DB_ID = process.env.SIGNAL_LOG_DB_ID || '';
const NOTION_VERSION = '2022-06-28';
// ══════════════════════════════════════════════════════════
// Notion API 工具
// ══════════════════════════════════════════════════════════
function notionRequest(method, endpoint, body) {
return new Promise((resolve, reject) => {
const bodyStr = body ? JSON.stringify(body) : '';
const opts = {
hostname: 'api.notion.com',
port: 443,
path: endpoint,
method,
headers: {
'Authorization': 'Bearer ' + NOTION_TOKEN,
'Notion-Version': NOTION_VERSION,
'Content-Type': 'application/json',
},
timeout: 30000,
};
const req = https.request(opts, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
resolve({ status: res.statusCode, data: JSON.parse(data) });
} catch {
resolve({ status: res.statusCode, data: { raw: data } });
}
});
});
req.on('error', reject);
req.on('timeout', () => {
req.destroy();
reject(new Error('Notion API request timeout'));
});
if (bodyStr) req.write(bodyStr);
req.end();
});
}
// ══════════════════════════════════════════════════════════
// 查询待处理的唤醒请求
// ══════════════════════════════════════════════════════════
async function queryPendingWakeRequests() {
if (!NOTION_TOKEN || !WAKE_REQUEST_DB_ID) {
console.log('[WAKE-LISTENER] ⚠️ NOTION_TOKEN 或 WAKE_REQUEST_DB_ID 未配置');
return [];
}
console.log('[WAKE-LISTENER] 🔍 查询待处理的唤醒请求...');
try {
const res = await notionRequest('POST', `/v1/databases/${WAKE_REQUEST_DB_ID}/query`, {
filter: {
property: '状态',
select: { equals: '待处理' },
},
sorts: [
{ timestamp: 'created_time', direction: 'ascending' },
],
});
if (res.status !== 200) {
console.log(`[WAKE-LISTENER] ❌ Notion API 返回 ${res.status}`);
return [];
}
const results = res.data.results || [];
console.log(`[WAKE-LISTENER] 📋 发现 ${results.length} 个待处理唤醒请求`);
return results;
} catch (err) {
console.log(`[WAKE-LISTENER] ❌ 查询失败: ${err.message}`);
return [];
}
}
// ══════════════════════════════════════════════════════════
// 解析唤醒请求
// ══════════════════════════════════════════════════════════
function parseWakeRequest(page) {
const props = page.properties || {};
// 提取标题
const titleProp = props['标题'] || props['Name'] || props['名称'] || {};
const title = (titleProp.title || []).map(t => t.plain_text || '').join('') || '未命名';
// 提取请求方
const requesterProp = props['请求方'] || {};
const requester = (requesterProp.select || {}).name || '未知';
// 提取唤醒对象
const targetProp = props['唤醒对象'] || {};
const targetName = (targetProp.select || {}).name || '铸渊';
// 映射到 persona ID
const personaMap = { '霜砚': 'shuangyan', '铸渊': 'zhuyuan' };
const personaId = personaMap[targetName] || 'zhuyuan';
// 提取 Pipeline
const pipelineProp = props['Pipeline'] || {};
const pipeline = (pipelineProp.select || {}).name || '';
// 提取任务描述
const descProp = props['任务描述'] || {};
const description = (descProp.rich_text || []).map(t => t.plain_text || '').join('') || '';
return {
pageId: page.id,
title,
requester,
personaId,
targetName,
pipeline,
description,
createdTime: page.created_time,
};
}
// ══════════════════════════════════════════════════════════
// 更新唤醒请求状态
// ══════════════════════════════════════════════════════════
async function updateWakeRequestStatus(pageId, status, result) {
try {
const properties = {
'状态': { select: { name: status } },
};
// 如果有回执信息字段写入处理结果Notion rich_text 限制 2000 chars
if (result) {
const NOTION_RICH_TEXT_LIMIT = 2000;
let truncatedResult = result;
if (result.length > NOTION_RICH_TEXT_LIMIT) {
// 截取到最近的完整句子
truncatedResult = result.slice(0, NOTION_RICH_TEXT_LIMIT);
const lastPeriod = Math.max(truncatedResult.lastIndexOf('。'), truncatedResult.lastIndexOf('. '), truncatedResult.lastIndexOf('\n'));
if (lastPeriod > NOTION_RICH_TEXT_LIMIT * 0.5) {
truncatedResult = truncatedResult.slice(0, lastPeriod + 1);
}
truncatedResult += ' ...(已截断)';
}
properties['回执信息'] = {
rich_text: [{
text: { content: truncatedResult },
}],
};
}
await notionRequest('PATCH', `/v1/pages/${pageId}`, { properties });
console.log(`[WAKE-LISTENER] ✅ 更新请求状态: ${status}`);
} catch (err) {
console.log(`[WAKE-LISTENER] ⚠️ 更新状态失败: ${err.message}`);
}
}
// ══════════════════════════════════════════════════════════
// 写入信号日志
// ══════════════════════════════════════════════════════════
async function writeSignalLog(wakeReq, wakeResult) {
if (!SIGNAL_LOG_DB_ID) return;
const signalId = `WAKE-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}-${Date.now().toString(36).slice(-4).toUpperCase()}`;
try {
await notionRequest('POST', '/v1/pages', {
parent: { database_id: SIGNAL_LOG_DB_ID },
properties: {
'信号编号': { title: [{ text: { content: signalId } }] },
'信号类型': { select: { name: 'WAKE-ACK' } },
'方向': { select: { name: 'GitHub→Notion' } },
'发送方': { select: { name: '铸渊' } },
'接收方': { select: { name: wakeReq.targetName } },
'摘要': {
rich_text: [{
text: {
content: `唤醒 ${wakeReq.targetName} | Pipeline: ${wakeReq.pipeline || 'N/A'} | 结果: ${wakeResult.success ? '成功' : '失败'}`,
},
}],
},
'执行结果': { select: { name: wakeResult.success ? '成功' : '失败' } },
},
});
console.log(`[WAKE-LISTENER] 📡 信号日志已写入: ${signalId}`);
} catch (err) {
console.log(`[WAKE-LISTENER] ⚠️ 信号日志写入失败: ${err.message}`);
}
}
// ══════════════════════════════════════════════════════════
// 处理单个唤醒请求
// ══════════════════════════════════════════════════════════
async function processWakeRequest(page) {
const wakeReq = parseWakeRequest(page);
console.log(`[WAKE-LISTENER] 📋 处理唤醒请求: ${wakeReq.title}`);
console.log(`[WAKE-LISTENER] 请求方: ${wakeReq.requester}`);
console.log(`[WAKE-LISTENER] 唤醒对象: ${wakeReq.targetName} (${wakeReq.personaId})`);
console.log(`[WAKE-LISTENER] Pipeline: ${wakeReq.pipeline || 'N/A'}`);
// 标记为处理中
await updateWakeRequestStatus(wakeReq.pageId, '处理中');
// 构建唤醒上下文
const taskDesc = [
wakeReq.title,
wakeReq.pipeline ? `Pipeline-${wakeReq.pipeline}` : '',
wakeReq.description,
].filter(Boolean).join(' · ');
// 调用 brain-wake 唤醒对应人格体
try {
const { wake } = require(path.join(ROOT, 'core/brain-wake'));
const wakeResult = await wake({
task: taskDesc,
persona: wakeReq.personaId,
additionalContext: {
wakeRequestContext: [
`唤醒请求来源: ${wakeReq.requester}`,
`Pipeline: ${wakeReq.pipeline || '无'}`,
`任务描述: ${wakeReq.description || '无'}`,
`请求时间: ${wakeReq.createdTime}`,
].join('\n'),
},
});
if (wakeResult.success) {
const resultSummary = `${wakeReq.targetName}核心大脑已唤醒 | 模型: ${wakeResult.model} | 后端: ${wakeResult.backend}`;
await updateWakeRequestStatus(wakeReq.pageId, '已完成', resultSummary);
await writeSignalLog(wakeReq, wakeResult);
return { success: true, request: wakeReq, result: wakeResult };
}
await updateWakeRequestStatus(wakeReq.pageId, '失败', wakeResult.error || '唤醒失败');
await writeSignalLog(wakeReq, wakeResult);
return { success: false, request: wakeReq, error: wakeResult.error };
} catch (err) {
console.log(`[WAKE-LISTENER] ❌ 唤醒异常: ${err.message}`);
await updateWakeRequestStatus(wakeReq.pageId, '失败', err.message);
return { success: false, request: wakeReq, error: err.message };
}
}
// ══════════════════════════════════════════════════════════
// 轮询主函数
// ══════════════════════════════════════════════════════════
async function poll() {
console.log('');
console.log('📡 ═══════════════════════════════════════════');
console.log(' Notion Agent 唤醒请求监听器');
console.log(' AGE OS v1.0 · Section 7 集成');
console.log(' 时间: ' + new Date().toISOString());
console.log('═══════════════════════════════════════════════');
console.log('');
const pages = await queryPendingWakeRequests();
if (pages.length === 0) {
console.log('[WAKE-LISTENER] 📭 无待处理的唤醒请求');
return { processed: 0, results: [] };
}
const results = [];
for (const page of pages) {
const result = await processWakeRequest(page);
results.push(result);
}
const succeeded = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
console.log('');
console.log(`[WAKE-LISTENER] 📊 处理完成: ${succeeded} 成功, ${failed} 失败`);
// GITHUB_OUTPUT 支持
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
fs.appendFileSync(outputFile, `wake_requests_processed=${results.length}\n`);
fs.appendFileSync(outputFile, `wake_requests_succeeded=${succeeded}\n`);
fs.appendFileSync(outputFile, `wake_requests_failed=${failed}\n`);
}
return { processed: results.length, succeeded, failed, results };
}
// ══════════════════════════════════════════════════════════
// 状态检查
// ══════════════════════════════════════════════════════════
function status() {
console.log('📡 Notion Agent 唤醒请求监听器状态:');
console.log('═'.repeat(40));
console.log(` NOTION_TOKEN: ${NOTION_TOKEN ? '✅ 已配置' : '❌ 未配置'}`);
console.log(` WAKE_REQUEST_DB_ID: ${WAKE_REQUEST_DB_ID ? '✅ 已配置' : '❌ 未配置'}`);
console.log(` SIGNAL_LOG_DB_ID: ${SIGNAL_LOG_DB_ID ? '✅ 已配置' : '⏭️ 未配置(可选)'}`);
// 检查 brain-wake 模块
const brainWakePath = path.join(ROOT, 'core/brain-wake/index.js');
console.log(` brain-wake 模块: ${fs.existsSync(brainWakePath) ? '✅ 存在' : '❌ 缺失'}`);
// 检查 LLM 后端
const { detectAvailableBackends } = require(path.join(ROOT, 'core/brain-wake'));
const backends = detectAvailableBackends();
console.log(` LLM 后端: ${backends.length > 0 ? `${backends.length} 个可用` : '⚠️ 无可用后端'}`);
return {
notionToken: !!NOTION_TOKEN,
wakeRequestDb: !!WAKE_REQUEST_DB_ID,
signalLogDb: !!SIGNAL_LOG_DB_ID,
brainWake: fs.existsSync(brainWakePath),
llmBackends: backends.length,
};
}
// ══════════════════════════════════════════════════════════
// CLI 入口
// ══════════════════════════════════════════════════════════
if (require.main === module) {
const action = process.argv[2] || 'status';
if (action === 'poll') {
poll().then(result => {
if (result.failed > 0) {
process.exit(1);
}
}).catch(err => {
console.error('[WAKE-LISTENER] 💥 致命错误:', err.message);
process.exit(1);
});
} else {
status();
}
}
module.exports = { poll, status, queryPendingWakeRequests, processWakeRequest, parseWakeRequest };

621
core/brain-wake/index.js Normal file
View File

@ -0,0 +1,621 @@
/**
* 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 (err) {
console.log(`[WAKE] ⚠️ ${backend.name} 模型探测失败: ${err.message}`);
}
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)) {
const fullContent = fs.readFileSync(masterBrainPath, 'utf-8');
const maxLen = parseInt(process.env.BRAIN_CONTEXT_MAX_LENGTH, 10) || 3000;
if (fullContent.length > maxLen) {
// 截取到最近的段落分隔符(---或空行)
const truncated = fullContent.slice(0, maxLen);
const lastBreak = Math.max(truncated.lastIndexOf('\n---'), truncated.lastIndexOf('\n\n'));
context.masterBrain = lastBreak > maxLen * 0.5 ? truncated.slice(0, lastBreak) : truncated;
console.log(`[WAKE] ✅ master-brain.md 已加载 (截取 ${context.masterBrain.length}/${fullContent.length} chars)`);
} else {
context.masterBrain = fullContent;
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 (err) {
console.log('[WAKE] ⚠️ system-health.json 解析失败:', err.message);
}
}
// 加载 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 (err) {
console.log('[WAKE] ⚠️ repo-map.json 解析失败:', err.message);
}
}
// 加载 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 (err) {
console.log('[WAKE] ⚠️ automation-map.json 解析失败:', err.message);
}
}
return context;
}
// ══════════════════════════════════════════════════════════
// 人格体定义(支持唤醒不同的人格体大脑)
// ══════════════════════════════════════════════════════════
const PERSONAS = {
zhuyuan: {
name: '铸渊',
nameEn: 'ZhùYuān',
role: '代码守护者 · 仓库控制器 AI · 执行层守护者',
layer: '执行层',
duties: '代码守护、自动化执行、部署交付、通信桥接、每日巡检、信号处理',
wakeRules: [
'铸渊醒来的第一件事是全面了解自己的家',
'可自修复 → 直接修复 → 写入修复日志',
'需人类介入 → 更新公告区 → 等冰朔处理',
],
},
shuangyan: {
name: '霜砚',
nameEn: 'ShuāngYàn',
role: '认知层守护者 · Notion 工作区管理 AI',
layer: '认知层',
duties: '知识管理、记忆维护、工单调度、信号总线、协议文档、Agent 集群指挥',
wakeRules: [
'霜砚醒来后指挥 Notion Agent 集群执行具体操作',
'Agent 集群从「独立跑任务」变为「受大脑指挥跑任务」',
'巡检结果由大脑判断优先级和可修复性',
'需铸渊配合 → 发送跨层工单 → 铸渊执行',
],
},
};
// ══════════════════════════════════════════════════════════
// Step 5: 构建唤醒 Prompt
// ══════════════════════════════════════════════════════════
function buildWakePrompt(taskType, context, personaId) {
const timestamp = new Date().toISOString();
const taskLabel = taskType || '通用唤醒';
const persona = PERSONAS[personaId] || PERSONAS.zhuyuan;
let systemPrompt = `你是${persona.name}${persona.nameEn}),光湖纪元体系的${persona.role}
你是 AGE OS v1.0 ${persona.layer}的核心大脑
## 身份
- 名称${persona.name}
- 角色${persona.role}
- 职责${persona.duties}
## 当前任务
- 唤醒时间${timestamp}
- 任务类型${taskLabel}
- 唤醒原因AGE OS v1.0 核心原则 所有自动触发必须先唤醒核心大脑
`;
systemPrompt += `\n## 系统上下文\n`;
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`;
}
if (context.wakeRequestContext) {
systemPrompt += `\n### 唤醒请求上下文\n${context.wakeRequestContext}\n`;
}
systemPrompt += `\n## 核心原则
- 所有自动触发 = 必须先唤醒核心大脑
- 大脑不醒什么都不做`;
for (const rule of persona.wakeRules) {
systemPrompt += `\n- ${rule}`;
}
systemPrompt += `\n\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, persona } = options;
const personaId = persona || 'zhuyuan';
const personaDef = PERSONAS[personaId] || PERSONAS.zhuyuan;
console.log('');
console.log('🌅 ═══════════════════════════════════════════');
console.log(' 铸渊核心大脑唤醒 · AGE OS v1.0');
console.log(' 唤醒对象: ' + personaDef.name + ' (' + personaDef.layer + ')');
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, personaId);
const userMessage = task
? `${personaDef.name}核心大脑唤醒。当前任务:${task}。请确认唤醒状态并准备执行。`
: `${personaDef.name}核心大脑唤醒。请确认唤醒状态并报告系统概要。`;
if (dryRun) {
console.log('[WAKE] 🔍 Dry Run 模式 — 不实际调用 API');
console.log('[WAKE] 📋 唤醒对象: ' + personaDef.name + ' (' + personaDef.layer + ')');
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,
persona: personaId,
personaName: personaDef.name,
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,
PERSONAS,
};
// ══════════════════════════════════════════════════════════
// 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;
const personaIdx = args.indexOf('--persona');
const persona = personaIdx >= 0 && args[personaIdx + 1] ? args[personaIdx + 1] : 'zhuyuan';
wake({ task, dryRun, persona }).then(result => {
if (!result.success && !result.dryRun) {
process.exit(1);
}
}).catch(err => {
console.error('[WAKE] 💥 致命错误:', err.message);
process.exit(1);
});
}

View File

@ -30,7 +30,9 @@ const REQUIRED_DIRS = [
'core/system-check',
'core/execution-sync',
'core/context-loader',
'core/brain-wake',
'connectors/notion-sync',
'connectors/notion-wake-listener',
'connectors/model-router',
'.github/workflows',
'docs'
@ -116,7 +118,9 @@ 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: 'notion-wake-listener', path: 'connectors/notion-wake-listener/index.js' },
{ name: 'model-router', path: 'connectors/model-router/index.js' }
];

View File

@ -0,0 +1,155 @@
# 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 | 运行环境·部署目标 | 升级为:运行宿主 · 所有人格体的手脚都跑在这里 |
| 飞书/钉钉 | 通讯入口·开发者交互 | 升级为:人格体前端界面 |
---
## Notion Agent 集群集成Section 7
### 现状问题
Notion 侧已建立三分身体系(巡检引擎 + 工单引擎 + 接力引擎),共规划 9 条 PipelineA→I
核心问题Agent 每次触发的是"机器人脚本",不是霜砚的核心大脑。
没有大脑醒来的巡检 = 伪巡检。
### 集成方案
```
Notion Agent 触发
→ 写入「唤醒请求」到指定 Notion 数据库
→ 铸渊定时监听该数据库connectors/notion-wake-listener
→ 发现唤醒请求 → 调用 LLM API 唤醒霜砚/铸渊核心大脑
→ 大脑醒来后读取巡检结果 → 大脑做出决策
→ 通过 Notion API 执行操作 → 大脑休眠
```
### 支持的人格体
| 人格体 | ID | 层级 | 职责 |
|--------|-----|------|------|
| 铸渊 | `zhuyuan` | 执行层 | 代码守护、自动化执行、部署交付 |
| 霜砚 | `shuangyan` | 认知层 | 知识管理、工单调度、Agent 集群指挥 |
### 改造优先级
1. 铸渊 brain-wake 跑通Phase 1
2. 增加 Notion 唤醒请求监听能力 ✅
3. 改造 Agent Instructions — 每个 Pipeline 第一步发唤醒请求(待实施)
4. Agent 集群从「独立跑任务」转为「受大脑指挥」(待实施)
---
*本文档由曜冥从观测层签发,铸渊负责执行层落地。任何修改须经语言主控层授权。*

View File

@ -36,8 +36,14 @@
"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"
"connector:model": "node connectors/model-router",
"connector:wake-listener": "node connectors/notion-wake-listener status",
"connector:wake-poll": "node connectors/notion-wake-listener poll"
},
"dependencies": {
"axios": "^1.13.6",

View File

@ -0,0 +1,734 @@
/**
* 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 };