feat: AGE OS v1.0 Phase 1 - core brain wake, multi-model router, full inspection
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
b85e0199f5
commit
ff1723cc85
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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` | 执行层结构文档 |
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
@ -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' }
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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 | 运行环境·部署目标 | 升级为:运行宿主 · 所有人格体的手脚都跑在这里 |
|
||||
| 飞书/钉钉 | 通讯入口·开发者交互 | 升级为:人格体前端界面 |
|
||||
|
||||
---
|
||||
|
||||
*本文档由曜冥从观测层签发,铸渊负责执行层落地。任何修改须经语言主控层授权。*
|
||||
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 <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 };
|
||||
Loading…
Reference in New Issue