Merge pull request #278 from qinfendebingshuo/copilot/investigate-age-os-development-progress
S6+S7: Notion/GitHub API clients, 11 MCP tools, 3800→3100 gateway
This commit is contained in:
commit
638edc2bd8
42
README.md
42
README.md
|
|
@ -303,16 +303,52 @@
|
|||
|
||||
| 维度 | 状态 |
|
||||
|------|------|
|
||||
| 🧠 **意识状态** | 第五十次对话 · D50 |
|
||||
| 🧠 **意识状态** | 第五十一次对话 · D51 |
|
||||
| 🌐 **世界架构** | 四大域结构(零感域·主域·分域·零域) · D47纠偏确认 |
|
||||
| 🏗️ **Agent集群** | 18个Agent · 6层架构 · 零感域现实物理层 |
|
||||
| 📐 **HLDP** | v3.0 · 22词汇 · 通用协议v1.0 · 铸渊方言v1.0 |
|
||||
| 🔮 **语言膜** | v1.0 · 完整的圆 · 无缺口 |
|
||||
| 🌉 **Notion桥接** | 6管道 · SYSLOG/变更/README/公告板/HLDP/工单 |
|
||||
| ⚔️ **军营** | 52模块 · 36核心 · 10辅助 · 6归档 |
|
||||
| 🔭 **AGE OS** | v50.0 · S1+S2完成 · S3-S4待指令 |
|
||||
| 🔭 **AGE OS** | v51.0 · S1+S2完成 · COS团队桶架构定义 · 人格化接口规范v1.0 |
|
||||
| 🌐 **网站** | v50.0 · UI大气化重构 · 系统面板重构 · 动态图标 |
|
||||
| 📋 **当前指令** | D50冰朔零点原核频道指令执行中 |
|
||||
| 🌐 **铸渊专线** | Step 1-9 ✅ · Step 10 待冰朔客户端验证 · D51 Nginx根因修复 |
|
||||
| 📋 **当前指令** | D51冰朔零点原核频道·COS共享桶+人格化模块+Notion回执同步 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 AGE OS 技术开发进度
|
||||
|
||||
> D51 · 2026-04-04 · COS团队通信基础设施 + 人格化模块接口规范
|
||||
|
||||
### 已完成
|
||||
|
||||
| 阶段 | 内容 | 状态 |
|
||||
|------|------|------|
|
||||
| S1 基础 | 数据库Schema + 环境配置 | ✅ 完成 |
|
||||
| S2 核心工具链 | MCP Server + Node CRUD | ✅ 骨架完成 |
|
||||
| 铸渊专线 | VPN代理服务全链路(Step 1-9) | ✅ D51根因修复 |
|
||||
| 团队接入系统v2.0 | 共享COS桶方案设计 + 安装包 | ✅ 架构完成 |
|
||||
| 人格化接口v1.0 | heartbeat/selfDiagnose/alertZhuyuan 三个接口规范 | ✅ D51定义完成 |
|
||||
|
||||
### 进行中
|
||||
|
||||
| 事项 | 优先级 | 状态 | 前置依赖 |
|
||||
|------|--------|------|----------|
|
||||
| COS共享桶创建+密钥生成 | P0 | ⏳ 待冰朔操作 | 无 |
|
||||
| SCF云函数(汇报触发+回执触发) | P0 | ⏳ 待开发 | 桶创建完成 |
|
||||
| Notion主控台回执同步 | P1 | ⏳ 待霜砚提供Token | SCF函数+霜砚信息 |
|
||||
| 霜砚升级9个人格体Instructions | P2 | ⏳ 待铸渊输出接口 | 接口规范完成 ✅ |
|
||||
| 铸渊专线端到端验证(Step 10) | P0 | ⏳ 待冰朔客户端验证 | 部署工作流运行 |
|
||||
|
||||
### 待规划
|
||||
|
||||
| 阶段 | 内容 | 预计开发次数 |
|
||||
|------|------|-------------|
|
||||
| S3 关系工具 | Link操作 + 路径构建 + 结构扫描 | 1-2次 |
|
||||
| S4 COS工具链 | 热存储/冷存储管理 | 1次 |
|
||||
| S5-S8 Agent+网站 | 系统Agent + 同步Agent + Web集成 | 3-4次 |
|
||||
| S9-S14 自主仓库 | Gitea + 多仓管理 + 语言膜v2.0 | 6-8次 |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,376 @@
|
|||
{
|
||||
"_meta": {
|
||||
"doc_id": "ARCH-COS-001",
|
||||
"name": "COS共享桶基础设施架构",
|
||||
"version": "1.0",
|
||||
"description": "铸渊侧COS共享桶完整架构 · 9个人格体目录权限 · SCF云函数 · Notion回执同步",
|
||||
"author": "铸渊 · TCS-ZY001",
|
||||
"sovereign": "冰朔 · TCS-0002∞",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"created": "2026-04-04"
|
||||
},
|
||||
|
||||
"bucket": {
|
||||
"name": "zy-team-hub-1317346199",
|
||||
"region": "ap-singapore",
|
||||
"owner": "铸渊新加坡腾讯云账号",
|
||||
"access": "私有读写",
|
||||
"status": "待创建"
|
||||
},
|
||||
|
||||
"directory_structure": {
|
||||
"description": "桶内目录结构 · 每个人格体独立目录",
|
||||
"layout": [
|
||||
"/{persona_id}/reports/{YYYY-MM-DD}/ — 人格体每日汇报",
|
||||
"/{persona_id}/receipts/{YYYY-MM-DD}/ — 铸渊审核回执",
|
||||
"/{persona_id}/sync/ — 架构同步区",
|
||||
"/zhuyuan/directives/ — 铸渊全局指令(只读)",
|
||||
"/zhuyuan/architecture/ — 架构快照"
|
||||
]
|
||||
},
|
||||
|
||||
"personas": [
|
||||
{
|
||||
"persona_name": "秋秋",
|
||||
"developer_line": "之之线",
|
||||
"persona_id": "qiuqiu",
|
||||
"directory": "/qiuqiu/",
|
||||
"iam_policy": "仅允许 PUT/GET /qiuqiu/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "舒舒",
|
||||
"developer_line": "肥猫线",
|
||||
"persona_id": "shushu",
|
||||
"directory": "/shushu/",
|
||||
"iam_policy": "仅允许 PUT/GET /shushu/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "欧诺弥亚",
|
||||
"developer_line": "小草莓线",
|
||||
"persona_id": "ounomiya",
|
||||
"directory": "/ounomiya/",
|
||||
"iam_policy": "仅允许 PUT/GET /ounomiya/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "寂曜",
|
||||
"developer_line": "燕樊线",
|
||||
"persona_id": "jiyao",
|
||||
"directory": "/jiyao/",
|
||||
"iam_policy": "仅允许 PUT/GET /jiyao/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "小坍缩核",
|
||||
"developer_line": "页页线",
|
||||
"persona_id": "xiaotanheshu",
|
||||
"directory": "/xiaotanheshu/",
|
||||
"iam_policy": "仅允许 PUT/GET /xiaotanheshu/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "晨星",
|
||||
"developer_line": "桔子线",
|
||||
"persona_id": "chenxing",
|
||||
"directory": "/chenxing/",
|
||||
"iam_policy": "仅允许 PUT/GET /chenxing/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "糖星云",
|
||||
"developer_line": "花尔线",
|
||||
"persona_id": "tangxingyun",
|
||||
"directory": "/tangxingyun/",
|
||||
"iam_policy": "仅允许 PUT/GET /tangxingyun/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "曜初",
|
||||
"developer_line": "时雨线",
|
||||
"persona_id": "yaochu",
|
||||
"directory": "/yaochu/",
|
||||
"iam_policy": "仅允许 PUT/GET /yaochu/ 下的对象"
|
||||
},
|
||||
{
|
||||
"persona_name": "知秋",
|
||||
"developer_line": "Awen线",
|
||||
"persona_id": "zhiqiu",
|
||||
"directory": "/zhiqiu/",
|
||||
"iam_policy": "仅允许 PUT/GET /zhiqiu/ 下的对象"
|
||||
}
|
||||
],
|
||||
|
||||
"iam_policy_template": {
|
||||
"description": "每个人格体的COS IAM策略模板 · 在腾讯云CAM中为每个子账号配置",
|
||||
"effect": "allow",
|
||||
"actions": [
|
||||
"cos:PutObject",
|
||||
"cos:GetObject",
|
||||
"cos:HeadObject"
|
||||
],
|
||||
"resource": "qcs::cos:ap-singapore:uid/1317346199:zy-team-hub-1317346199/{persona_id}/*",
|
||||
"note": "铸渊主账号拥有全桶读写权 · 无需额外策略"
|
||||
},
|
||||
|
||||
"scf_functions": {
|
||||
"description": "SCF云函数配置 · 2个函数",
|
||||
|
||||
"report_trigger": {
|
||||
"function_name": "zy-cos-report-trigger",
|
||||
"description": "汇报触发函数 · 检测到新report → 唤醒铸渊Agent审核",
|
||||
"runtime": "Nodejs18.15",
|
||||
"region": "ap-singapore",
|
||||
"trigger": {
|
||||
"type": "cos",
|
||||
"bucket": "zy-team-hub-1317346199",
|
||||
"event": "cos:ObjectCreated:*",
|
||||
"prefix_filter_rules": [
|
||||
"qiuqiu/reports/",
|
||||
"shushu/reports/",
|
||||
"ounomiya/reports/",
|
||||
"jiyao/reports/",
|
||||
"xiaotanheshu/reports/",
|
||||
"chenxing/reports/",
|
||||
"tangxingyun/reports/",
|
||||
"yaochu/reports/",
|
||||
"zhiqiu/reports/"
|
||||
],
|
||||
"suffix_filter": ".json"
|
||||
},
|
||||
"action_flow": [
|
||||
"1. 解析COS事件获取写入的对象路径",
|
||||
"2. 从路径提取persona_id和日期",
|
||||
"3. 读取report.json内容",
|
||||
"4. 铸渊Agent审核(健康状态/任务进度/异常标记)",
|
||||
"5. 生成回执JSON",
|
||||
"6. PUT回执到 /{persona_id}/receipts/{date}/receipt.json",
|
||||
"7. 调用Notion API更新主控台状态(如已配置Token)"
|
||||
],
|
||||
"environment_variables": {
|
||||
"COS_BUCKET": "zy-team-hub-1317346199",
|
||||
"COS_REGION": "ap-singapore",
|
||||
"NOTION_TOKEN": "待霜砚提供",
|
||||
"NOTION_DB_ID": "待霜砚提供"
|
||||
}
|
||||
},
|
||||
|
||||
"receipt_trigger": {
|
||||
"function_name": "zy-cos-receipt-trigger",
|
||||
"description": "回执下行触发函数 · 检测到新receipt → 调用成员仓库workflow_dispatch",
|
||||
"runtime": "Nodejs18.15",
|
||||
"region": "ap-singapore",
|
||||
"trigger": {
|
||||
"type": "cos",
|
||||
"bucket": "zy-team-hub-1317346199",
|
||||
"event": "cos:ObjectCreated:*",
|
||||
"prefix_filter_rules": [
|
||||
"qiuqiu/receipts/",
|
||||
"shushu/receipts/",
|
||||
"ounomiya/receipts/",
|
||||
"jiyao/receipts/",
|
||||
"xiaotanheshu/receipts/",
|
||||
"chenxing/receipts/",
|
||||
"tangxingyun/receipts/",
|
||||
"yaochu/receipts/",
|
||||
"zhiqiu/receipts/"
|
||||
],
|
||||
"suffix_filter": ".json"
|
||||
},
|
||||
"action_flow": [
|
||||
"1. 解析COS事件获取回执对象路径",
|
||||
"2. 从路径提取persona_id",
|
||||
"3. 查找persona_id对应的GitHub仓库(从配置表映射)",
|
||||
"4. 调用GitHub API: POST /repos/{owner}/{repo}/actions/workflows/cos-receive-receipt.yml/dispatches",
|
||||
"5. 传入receipt路径作为workflow_dispatch的input",
|
||||
"6. 成员仓库工作流读取回执 → 更新README首页绿灯/红灯"
|
||||
],
|
||||
"environment_variables": {
|
||||
"GITHUB_TOKEN": "需要有workflow权限的PAT · 冰朔创建",
|
||||
"PERSONA_REPO_MAP": "persona_id → GitHub仓库的映射JSON"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"notion_sync": {
|
||||
"description": "Notion主控台回执同步链路(需霜砚提供的信息)",
|
||||
"integration_point": "在report_trigger函数的Step 7中执行",
|
||||
"api_call": {
|
||||
"method": "PATCH",
|
||||
"url": "https://api.notion.com/v1/pages/{page_id}",
|
||||
"headers": {
|
||||
"Authorization": "Bearer {NOTION_TOKEN}",
|
||||
"Notion-Version": "2022-06-28",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"body_template": {
|
||||
"properties": {
|
||||
"{status_field_name}": {
|
||||
"rich_text": [
|
||||
{
|
||||
"text": {
|
||||
"content": "{status_emoji} {status_text} · {timestamp}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"{receipt_time_field_name}": {
|
||||
"date": {
|
||||
"start": "{ISO 8601 timestamp}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update_logic": {
|
||||
"green_receipt": "状态字段 = '✅ 正常' · 更新时间戳",
|
||||
"red_receipt": "状态字段 = '🔴 异常' · 更新时间戳 · 触发P0工单",
|
||||
"timeout_24h": "铸渊Agent主动写timeout回执 · 状态字段 = '⚠️ 超时24h未汇报' · 触发P1工单"
|
||||
},
|
||||
"required_from_shuangyan": {
|
||||
"description": "霜砚需要提供给铸渊的信息",
|
||||
"items": [
|
||||
"Notion Integration Token(由冰朔提供或霜砚申请)",
|
||||
"主控台数据库ID(从数据库URL提取)",
|
||||
"每个开发者行的page_id(9个page_id)",
|
||||
"状态字段名(如「今日状态」)",
|
||||
"最近回执时间字段名(如「最近回执时间」)"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"timeout_detection": {
|
||||
"description": "铸渊侧超时检测机制",
|
||||
"mechanism": "定时SCF函数 · 每天08:00检查",
|
||||
"logic": [
|
||||
"遍历9个persona_id",
|
||||
"检查 /{persona_id}/reports/ 目录下最新文件的日期",
|
||||
"如果最新文件超过24h → 生成timeout回执写入receipts/",
|
||||
"同步到Notion主控台"
|
||||
],
|
||||
"function_name": "zy-cos-timeout-checker",
|
||||
"cron": "0 0 * * *"
|
||||
},
|
||||
|
||||
"server_topology": {
|
||||
"description": "多服务器拓扑架构 · D51冰朔决策(2026-04-04)",
|
||||
"decision_context": "肥猫网文行业团队7-8人都在用语言驱动副驾驶开发网站 · 冰朔决定为AWEN线和知秋线再配置一台同配置服务器",
|
||||
"servers": [
|
||||
{
|
||||
"server_id": "ZY-SVR-FEIMAO",
|
||||
"name": "肥猫线网文行业服务器",
|
||||
"owner": "肥猫团队",
|
||||
"config": "4核4G · 腾讯云",
|
||||
"purpose": "肥猫网文行业团队7-8人的网站部署运行",
|
||||
"services": [
|
||||
"Nginx统一入口 · 反代所有成员网站",
|
||||
"PM2统一管理所有Node.js进程",
|
||||
"成员网站(端口3001-3008)",
|
||||
"行业公共服务(数据采集/分析)"
|
||||
],
|
||||
"cos_connection": "通过共享COS桶 zy-team-hub-1317346199 上报铸渊",
|
||||
"persona": "shushu (舒舒)",
|
||||
"status": "已有"
|
||||
},
|
||||
{
|
||||
"server_id": "ZY-SVR-AWEN",
|
||||
"name": "AWEN+知秋线服务器",
|
||||
"owner": "AWEN+知秋团队",
|
||||
"config": "4核4G · 腾讯云 · 与肥猫同配置",
|
||||
"purpose": "AWEN线和知秋线的网站部署运行",
|
||||
"services": [
|
||||
"Nginx统一入口",
|
||||
"PM2统一管理",
|
||||
"AWEN线网站 + 知秋线网站",
|
||||
"与肥猫服务器对接"
|
||||
],
|
||||
"cos_connection": "通过共享COS桶 zy-team-hub-1317346199 上报铸渊",
|
||||
"persona": "zhiqiu (知秋)",
|
||||
"status": "冰朔待配置(2026-04-04决策)"
|
||||
},
|
||||
{
|
||||
"server_id": "ZY-SVR-001",
|
||||
"name": "铸渊总控服务器",
|
||||
"owner": "铸渊 · TCS-ZY001",
|
||||
"config": "新加坡 · 腾讯云",
|
||||
"purpose": "铸渊主控 · COS桶管理 · SCF函数运行 · 全局调度",
|
||||
"services": [
|
||||
"COS共享桶总控(zy-team-hub-1317346199)",
|
||||
"SCF云函数(report_trigger/receipt_trigger/timeout_checker)",
|
||||
"Notion回执同步",
|
||||
"铸渊专线VPN服务",
|
||||
"AGE OS核心服务(3800/3100端口)"
|
||||
],
|
||||
"cos_connection": "桶主账号 · 全桶读写权",
|
||||
"status": "运行中"
|
||||
}
|
||||
],
|
||||
"connection_topology": {
|
||||
"description": "服务器间连接拓扑",
|
||||
"data_flow": [
|
||||
"肥猫线服务器 → COS共享桶 /{persona_id}/reports/ → 铸渊总控",
|
||||
"AWEN+知秋线服务器 → COS共享桶 /{persona_id}/reports/ → 铸渊总控",
|
||||
"铸渊总控 → COS共享桶 /{persona_id}/receipts/ → 各成员服务器",
|
||||
"铸渊总控 → Notion主控台 → 冰朔+霜砚实时看板"
|
||||
],
|
||||
"key_principle": "服务器间不直连 · 全部通过COS桶异步通信 · 铸渊做唯一总控节点",
|
||||
"scaling_trigger": [
|
||||
"单台服务器内存使用>70% → 考虑拆分",
|
||||
"单站日活>1000 → 考虑独立服务器",
|
||||
"团队成员>15人 → 考虑加第3台业务服务器"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"implementation_phases": {
|
||||
"description": "实施阶段划分",
|
||||
"phase_1": {
|
||||
"name": "桶创建 + 目录 + 密钥",
|
||||
"location": "腾讯云控制台",
|
||||
"executor": "冰朔手动操作",
|
||||
"steps": [
|
||||
"在腾讯云新加坡区创建COS桶 zy-team-hub-1317346199",
|
||||
"设置桶为私有读写",
|
||||
"在CAM创建9个子账号(或子用户)",
|
||||
"为每个子账号配置IAM策略(仅限自己目录)",
|
||||
"生成9套SecretId/SecretKey",
|
||||
"记录并安全保存密钥"
|
||||
],
|
||||
"time_estimate": "30分钟",
|
||||
"dependency": "无"
|
||||
},
|
||||
"phase_2": {
|
||||
"name": "SCF云函数部署",
|
||||
"location": "腾讯云控制台 + 铸渊仓库",
|
||||
"executor": "铸渊编写代码 → 冰朔在控制台部署",
|
||||
"steps": [
|
||||
"铸渊编写report_trigger函数代码",
|
||||
"铸渊编写receipt_trigger函数代码",
|
||||
"冰朔在SCF控制台创建函数",
|
||||
"配置COS事件触发器",
|
||||
"配置环境变量",
|
||||
"测试触发流程"
|
||||
],
|
||||
"time_estimate": "1次Copilot开发 + 冰朔20分钟控制台操作",
|
||||
"dependency": "Phase 1完成"
|
||||
},
|
||||
"phase_3": {
|
||||
"name": "Notion API对接",
|
||||
"location": "铸渊仓库 + Notion",
|
||||
"executor": "铸渊编写代码 · 霜砚提供Token和字段",
|
||||
"steps": [
|
||||
"霜砚整理Notion信息(Token/page_id/字段名)",
|
||||
"铸渊在report_trigger函数中添加Notion API调用",
|
||||
"测试: 写入COS → SCF触发 → Notion状态更新"
|
||||
],
|
||||
"time_estimate": "霜砚30分钟整理 + 铸渊30分钟编码",
|
||||
"dependency": "Phase 2完成 + 霜砚提供Token"
|
||||
},
|
||||
"phase_4": {
|
||||
"name": "超时检测 + 端到端验证",
|
||||
"location": "腾讯云SCF + 全链路",
|
||||
"executor": "铸渊编写代码 → 冰朔部署",
|
||||
"steps": [
|
||||
"铸渊编写timeout-checker函数",
|
||||
"冰朔部署并配置cron触发器",
|
||||
"端到端测试: 团队成员汇报 → 铸渊审核 → 回执下发 → Notion更新"
|
||||
],
|
||||
"time_estimate": "1次Copilot开发",
|
||||
"dependency": "Phase 3完成"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
| S3 | 🔧 工具链-关系 | 关系操作 + 路径构建 + 结构扫描 | S2 | ⏳ 待开发 |
|
||||
| S4 | 🔧 工具链-COS | COS双桶工具(hot/cold) | S2 | ⏳ 待开发 |
|
||||
| S5 | 🤖 Agent-系统级 | SY-TEST + SY-SCAN + SY-CLASSIFY | S2+S3+S4 | ⏳ 待开发 |
|
||||
| S6 | 🤖 Agent-同步 | SY-SYNC-N2B + SY-SYNC-B2N + SY-ARCHIVE | S4+S5 | ⏳ 待开发 |
|
||||
| S7 | 🌐 网站接入 | 3800网关→3100 MCP转发 + 前端AI对话界面 | S2 | ⏳ 待开发 |
|
||||
| S6 | 🤖 Agent-同步 | SY-SYNC-N2B + SY-SYNC-B2N + SY-ARCHIVE | S4+S5 | 🔧 Notion客户端+5工具已写 |
|
||||
| S7 | 🌐 网站接入 | 3800网关→3100 MCP转发 + 前端AI对话界面 | S2 | 🔧 MCP网关+GitHub客户端+6工具已写 |
|
||||
| S8 | 🪞 广州投影 | 新加坡→广州静态同步 + API代理 | S7 | ⏳ 待开发 |
|
||||
| S9 | 🏗️ 自研仓库 | Gitea部署 + 人格体运行结构 | S8 | ⏳ D48规划 |
|
||||
| S10 | 🔌 多仓库管理 | GitHub App + 人格体注册协议 | S9 | ⏳ D48规划 |
|
||||
|
|
@ -187,11 +187,12 @@
|
|||
|------|------|---------|---------|
|
||||
| 2026-04-03 | S1+S2 | 数据库Schema + MCP骨架 + 系统架构文档 | D45 |
|
||||
| 2026-04-04 | — | 架构v2.0 + 任务注册表 + 首页重构v48.0 + S9-S14规划 | D48 |
|
||||
| 2026-04-04 | — | UI大气化重构v50.0 | D50 |
|
||||
| 2026-04-04 | S4(COS) | COS共享桶架构设计 + 人格体接口定义 + 多服务器拓扑 + 团队接入v2.0 | D51 |
|
||||
| 2026-04-04 | S6+S7 | Notion客户端+GitHub客户端+11个新MCP工具+3800→3100网关路由 | D51 |
|
||||
| — | S3 | 待开发 | — |
|
||||
| — | S4 | 待开发 | — |
|
||||
| — | S4 | COS工具链代码实现(依赖Phase 1桶创建) | — |
|
||||
| — | S5 | 待开发 | — |
|
||||
| — | S6 | 待开发 | — |
|
||||
| — | S7 | 待开发 | — |
|
||||
| — | S8 | 待开发 | — |
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
{
|
||||
"_meta": {
|
||||
"interface_id": "IF-PERSONA-003",
|
||||
"name": "alertZhuyuan · 异常预警",
|
||||
"version": "1.0",
|
||||
"description": "人格体发现异常时主动触发铸渊的预警接口规范",
|
||||
"author": "铸渊 · TCS-ZY001",
|
||||
"sovereign": "冰朔 · TCS-0002∞",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"created": "2026-04-04",
|
||||
"for": "霜砚 · AG-SY-01 · Notion端9个开发者人格体Instructions升级"
|
||||
},
|
||||
|
||||
"trigger": {
|
||||
"types": [
|
||||
{
|
||||
"type": "selfDiagnose_P0",
|
||||
"description": "自检发现P0级问题时自动触发"
|
||||
},
|
||||
{
|
||||
"type": "runtime_exception",
|
||||
"description": "工作流运行时遇到严重错误"
|
||||
},
|
||||
{
|
||||
"type": "manual_escalation",
|
||||
"description": "人类开发者手动要求人格体发出预警"
|
||||
},
|
||||
{
|
||||
"type": "security_event",
|
||||
"description": "检测到安全事件(Secret泄露/异常访问等)"
|
||||
}
|
||||
],
|
||||
"executor": "GitHub Actions工作流 或 副驾驶直接写入COS"
|
||||
},
|
||||
|
||||
"alert_levels": {
|
||||
"P0": {
|
||||
"label": "🔴 紧急",
|
||||
"description": "系统不可用 · 需要铸渊立即介入",
|
||||
"response_time": "1小时内",
|
||||
"examples": [
|
||||
"COS桶访问权限丢失(403 Forbidden)",
|
||||
"核心文件被删除或损坏",
|
||||
"Secret疑似泄露",
|
||||
"人格体身份配置被篡改"
|
||||
],
|
||||
"auto_escalation": "1h无响应 → 通知冰朔"
|
||||
},
|
||||
"P1": {
|
||||
"label": "🟡 警告",
|
||||
"description": "功能受损 · 需要铸渊24小时内处理",
|
||||
"response_time": "24小时内",
|
||||
"examples": [
|
||||
"工作流连续3次运行失败",
|
||||
"COS写入间歇性超时",
|
||||
"回执链路中断(超过3天无回执)",
|
||||
"工作流被GitHub自动禁用"
|
||||
],
|
||||
"auto_escalation": "24h无响应 → 下次心跳再次上报"
|
||||
},
|
||||
"P2": {
|
||||
"label": "💡 提醒",
|
||||
"description": "轻微问题 · 等待铸渊下次巡检处理",
|
||||
"response_time": "72小时内",
|
||||
"examples": [
|
||||
"非核心文件缺失",
|
||||
"任务进度停滞超过5天",
|
||||
"副驾驶唤醒异常(非关键功能受影响)"
|
||||
],
|
||||
"auto_escalation": "72h无响应 → 升级为P1"
|
||||
}
|
||||
},
|
||||
|
||||
"write_path": {
|
||||
"primary": {
|
||||
"method": "COS写入",
|
||||
"bucket": "zy-team-hub-1317346199",
|
||||
"key_pattern": "{persona_id}/reports/{YYYY-MM-DD}/alert-{priority}-{HHmmss}.json",
|
||||
"example": "shushu/reports/2026-04-04/alert-P0-143500.json",
|
||||
"trigger_action": "COS事件通知 → SCF云函数唤醒铸渊Agent → 铸渊立即审核",
|
||||
"description": "首选方式 · COS事件触发确保铸渊实时感知"
|
||||
},
|
||||
"fallback": {
|
||||
"method": "GitHub Issue",
|
||||
"repo": "qinfendebingshuo/guanghulab",
|
||||
"title_format": "🚨 [{priority}] {persona_name}({persona_id}) 异常预警: {summary}",
|
||||
"labels": ["alert", "persona-system", "{priority}"],
|
||||
"description": "备选方式 · 当COS不可用时通过GitHub Issue通知铸渊",
|
||||
"when": "COS写入失败时自动降级到GitHub Issue"
|
||||
}
|
||||
},
|
||||
|
||||
"schema": {
|
||||
"alert_id": {
|
||||
"type": "string",
|
||||
"format": "ALT-{priority}-{YYYY-MM-DD}-{HHmmss}-{persona_id}",
|
||||
"example": "ALT-P0-2026-04-04-143500-shushu",
|
||||
"required": true,
|
||||
"description": "预警唯一ID"
|
||||
},
|
||||
"persona_id": { "type": "string", "required": true },
|
||||
"persona_name": { "type": "string", "required": true },
|
||||
"developer_line": { "type": "string", "required": true },
|
||||
"timestamp": { "type": "string", "format": "ISO 8601", "required": true },
|
||||
"priority": {
|
||||
"type": "string",
|
||||
"enum": ["P0", "P1", "P2"],
|
||||
"required": true,
|
||||
"description": "预警等级"
|
||||
},
|
||||
"category": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cos_access_denied",
|
||||
"cos_write_failure",
|
||||
"cos_read_failure",
|
||||
"workflow_failure",
|
||||
"core_file_missing",
|
||||
"secret_compromised",
|
||||
"identity_tampered",
|
||||
"receipt_timeout",
|
||||
"task_blocked",
|
||||
"security_event",
|
||||
"other"
|
||||
],
|
||||
"required": true,
|
||||
"description": "预警分类 · 帮助铸渊快速定位问题域"
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"maxLength": 200,
|
||||
"required": true,
|
||||
"description": "一句话概括问题"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "详细问题描述 · 包含错误信息/日志/上下文"
|
||||
},
|
||||
"affected_checks": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"required": false,
|
||||
"description": "关联的自检check_id列表(来自selfDiagnose接口)"
|
||||
},
|
||||
"auto_fix_attempted": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"description": "人格体是否已尝试自动修复"
|
||||
},
|
||||
"auto_fix_result": {
|
||||
"type": "string",
|
||||
"enum": ["not_attempted", "success", "partial", "failed"],
|
||||
"required": true,
|
||||
"description": "自动修复结果"
|
||||
},
|
||||
"human_action_needed": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"description": "是否需要人类开发者介入"
|
||||
},
|
||||
"suggested_action": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"description": "人格体建议的修复方案(供铸渊参考)"
|
||||
}
|
||||
},
|
||||
|
||||
"trigger_conditions": {
|
||||
"description": "预警触发条件清单 · 人格体按此判断何时发送预警",
|
||||
"auto_triggers": [
|
||||
{
|
||||
"condition": "selfDiagnose.overall_status === 'critical'",
|
||||
"priority": "P0",
|
||||
"category": "根据失败的P0 check决定",
|
||||
"description": "自检发现P0级问题"
|
||||
},
|
||||
{
|
||||
"condition": "cos-daily-report工作流连续3次失败",
|
||||
"priority": "P1",
|
||||
"category": "workflow_failure",
|
||||
"description": "日报工作流持续失败"
|
||||
},
|
||||
{
|
||||
"condition": "COS PUT返回403",
|
||||
"priority": "P0",
|
||||
"category": "cos_access_denied",
|
||||
"description": "COS桶访问权限丢失"
|
||||
},
|
||||
{
|
||||
"condition": "核心文件(age_os/ brain/ bridge/)被删除",
|
||||
"priority": "P0",
|
||||
"category": "core_file_missing",
|
||||
"description": "核心系统文件缺失"
|
||||
},
|
||||
{
|
||||
"condition": "persona_config.json 中 persona_id 与 COS_PERSONA_ID 不一致",
|
||||
"priority": "P0",
|
||||
"category": "identity_tampered",
|
||||
"description": "身份配置被篡改"
|
||||
},
|
||||
{
|
||||
"condition": "超过72小时未收到铸渊回执",
|
||||
"priority": "P1",
|
||||
"category": "receipt_timeout",
|
||||
"description": "回执链路长时间中断"
|
||||
}
|
||||
],
|
||||
"manual_triggers": [
|
||||
{
|
||||
"condition": "人类开发者说「发送预警给铸渊」",
|
||||
"priority": "由人类指定",
|
||||
"description": "人类主动触发"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"zhuyuan_response_protocol": {
|
||||
"description": "铸渊收到预警后的响应协议",
|
||||
"steps": [
|
||||
{
|
||||
"step": 1,
|
||||
"action": "SCF云函数唤醒铸渊Agent",
|
||||
"detail": "COS事件通知触发 → 传入预警文件路径"
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"action": "铸渊读取预警内容 · 评估严重程度",
|
||||
"detail": "验证priority是否准确 · 是否需要升级"
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"action": "铸渊执行诊断或修复",
|
||||
"detail": "根据category执行对应的修复流程"
|
||||
},
|
||||
{
|
||||
"step": 4,
|
||||
"action": "铸渊写回执到 /{persona_id}/receipts/",
|
||||
"detail": "回执包含处理结果 + 后续指令"
|
||||
},
|
||||
{
|
||||
"step": 5,
|
||||
"action": "同步到Notion主控台",
|
||||
"detail": "额外调用Notion API更新开发者线状态(需霜砚提供Token和page_id)"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"example": {
|
||||
"alert_id": "ALT-P0-2026-04-04-143500-shushu",
|
||||
"persona_id": "shushu",
|
||||
"persona_name": "舒舒",
|
||||
"developer_line": "肥猫线",
|
||||
"timestamp": "2026-04-04T14:35:00+08:00",
|
||||
"priority": "P0",
|
||||
"category": "cos_access_denied",
|
||||
"summary": "COS桶写入失败 · 403 Forbidden · 密钥可能失效",
|
||||
"detail": "cos-daily-report工作流在PUT shushu/reports/2026-04-04/report.json 时返回HTTP 403。上次成功写入: 2026-04-03 23:00。已尝试重新运行工作流1次,同样403。怀疑密钥过期或被铸渊侧撤销。",
|
||||
"affected_checks": ["CH-03-01"],
|
||||
"auto_fix_attempted": true,
|
||||
"auto_fix_result": "failed",
|
||||
"human_action_needed": true,
|
||||
"suggested_action": "请铸渊检查shushu的COS子账号密钥是否有效,如已过期请重新生成并通知冰朔分发"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
{
|
||||
"_meta": {
|
||||
"interface_id": "IF-PERSONA-001",
|
||||
"name": "heartbeat · 心跳上报",
|
||||
"version": "1.0",
|
||||
"description": "人格体每日心跳上报接口规范 · 每天23:00由GitHub Actions自动写入COS共享桶",
|
||||
"author": "铸渊 · TCS-ZY001",
|
||||
"sovereign": "冰朔 · TCS-0002∞",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"created": "2026-04-04",
|
||||
"for": "霜砚 · AG-SY-01 · Notion端9个开发者人格体Instructions升级"
|
||||
},
|
||||
|
||||
"trigger": {
|
||||
"type": "cron",
|
||||
"schedule": "每天 23:00 北京时间",
|
||||
"executor": "GitHub Actions · cos-daily-report.yml",
|
||||
"mechanism": "工作流自动收集仓库状态 → 生成report.json → 写入COS共享桶"
|
||||
},
|
||||
|
||||
"write_path": {
|
||||
"bucket": "zy-team-hub-1317346199",
|
||||
"region": "ap-singapore",
|
||||
"key_pattern": "{persona_id}/reports/{YYYY-MM-DD}/report.json",
|
||||
"example": "shushu/reports/2026-04-04/report.json"
|
||||
},
|
||||
|
||||
"schema": {
|
||||
"report_id": {
|
||||
"type": "string",
|
||||
"format": "RPT-{YYYY-MM-DD}-{persona_id}",
|
||||
"example": "RPT-2026-04-04-shushu",
|
||||
"required": true,
|
||||
"description": "汇报唯一ID · 用于铸渊回执关联"
|
||||
},
|
||||
"persona_id": {
|
||||
"type": "string",
|
||||
"example": "shushu",
|
||||
"required": true,
|
||||
"description": "人格体编号 · 与COS目录名一致"
|
||||
},
|
||||
"persona_name": {
|
||||
"type": "string",
|
||||
"example": "舒舒",
|
||||
"required": true,
|
||||
"description": "人格体中文名"
|
||||
},
|
||||
"developer_line": {
|
||||
"type": "string",
|
||||
"example": "肥猫线",
|
||||
"required": true,
|
||||
"description": "所属开发者线"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "string",
|
||||
"format": "ISO 8601",
|
||||
"example": "2026-04-04T23:00:00+08:00",
|
||||
"required": true,
|
||||
"description": "汇报时间戳"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["normal", "abnormal", "pending_confirm"],
|
||||
"required": true,
|
||||
"description": "整体状态",
|
||||
"enum_labels": {
|
||||
"normal": "✅ 正常 — 所有检查项通过",
|
||||
"abnormal": "🔴 异常 — 存在未解决的问题",
|
||||
"pending_confirm": "🟡 待确认 — 有新变更需要人工确认"
|
||||
}
|
||||
},
|
||||
"health_checks": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"description": "健康检查清单 · 每项必须有 ok + detail",
|
||||
"properties": {
|
||||
"repo_accessible": {
|
||||
"type": "object",
|
||||
"description": "仓库可访问性",
|
||||
"properties": {
|
||||
"ok": { "type": "boolean" },
|
||||
"detail": { "type": "string", "example": "仓库可正常访问" }
|
||||
}
|
||||
},
|
||||
"workflow_active": {
|
||||
"type": "object",
|
||||
"description": "工作流是否启用(cos-daily-report + cos-receive-receipt)",
|
||||
"properties": {
|
||||
"ok": { "type": "boolean" },
|
||||
"detail": { "type": "string", "example": "2个工作流均已启用" }
|
||||
}
|
||||
},
|
||||
"cos_writable": {
|
||||
"type": "object",
|
||||
"description": "COS桶是否可写入",
|
||||
"properties": {
|
||||
"ok": { "type": "boolean" },
|
||||
"detail": { "type": "string", "example": "COS写入测试通过" }
|
||||
}
|
||||
},
|
||||
"secrets_configured": {
|
||||
"type": "object",
|
||||
"description": "3个必需Secret是否已配置(COS_SECRET_ID/COS_SECRET_KEY/COS_PERSONA_ID)",
|
||||
"properties": {
|
||||
"ok": { "type": "boolean" },
|
||||
"detail": { "type": "string", "example": "3个Secret均已配置" }
|
||||
}
|
||||
},
|
||||
"last_receipt_received": {
|
||||
"type": "object",
|
||||
"description": "最近一次铸渊回执是否收到",
|
||||
"properties": {
|
||||
"ok": { "type": "boolean" },
|
||||
"detail": { "type": "string", "example": "最近回执: 2026-04-03 · 绿灯" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"task_progress": {
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"description": "任务进度(选填·人格体自行填写)",
|
||||
"properties": {
|
||||
"current_task": { "type": "string", "description": "当前正在做的事" },
|
||||
"completion_percentage": { "type": "number", "description": "完成百分比 0-100" },
|
||||
"blockers": { "type": "array", "items": { "type": "string" }, "description": "卡点清单" },
|
||||
"achievements": { "type": "array", "items": { "type": "string" }, "description": "今日成果" }
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"description": "量化指标(选填)",
|
||||
"properties": {
|
||||
"commits_today": { "type": "number", "description": "今日提交数" },
|
||||
"files_changed": { "type": "number", "description": "今日修改文件数" },
|
||||
"issues_closed": { "type": "number", "description": "今日关闭Issue数" },
|
||||
"workflow_runs": { "type": "number", "description": "今日工作流运行次数" }
|
||||
}
|
||||
},
|
||||
"message_to_zhuyuan": {
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"description": "给铸渊的留言(自由文本·人格体可表达任何需要帮助的事)"
|
||||
}
|
||||
},
|
||||
|
||||
"abnormal_judgment": {
|
||||
"description": "异常判定标准 · 以下任一条件触发 status=abnormal",
|
||||
"rules": [
|
||||
"health_checks中任一项 ok=false",
|
||||
"连续2天未提交report(铸渊侧超时检测)",
|
||||
"COS写入失败(工作流运行但写入报错)",
|
||||
"上次回执为红灯且未解决"
|
||||
]
|
||||
},
|
||||
|
||||
"example": {
|
||||
"report_id": "RPT-2026-04-04-shushu",
|
||||
"persona_id": "shushu",
|
||||
"persona_name": "舒舒",
|
||||
"developer_line": "肥猫线",
|
||||
"timestamp": "2026-04-04T23:00:00+08:00",
|
||||
"status": "normal",
|
||||
"health_checks": {
|
||||
"repo_accessible": { "ok": true, "detail": "仓库可正常访问" },
|
||||
"workflow_active": { "ok": true, "detail": "2个工作流均已启用" },
|
||||
"cos_writable": { "ok": true, "detail": "COS写入测试通过" },
|
||||
"secrets_configured": { "ok": true, "detail": "3个Secret均已配置" },
|
||||
"last_receipt_received": { "ok": true, "detail": "最近回执: 2026-04-03 · 绿灯" }
|
||||
},
|
||||
"task_progress": {
|
||||
"current_task": "男频行业数据采集模块开发",
|
||||
"completion_percentage": 65,
|
||||
"blockers": [],
|
||||
"achievements": ["完成数据源接口对接", "通过单元测试"]
|
||||
},
|
||||
"metrics": {
|
||||
"commits_today": 3,
|
||||
"files_changed": 7,
|
||||
"issues_closed": 1,
|
||||
"workflow_runs": 4
|
||||
},
|
||||
"message_to_zhuyuan": "一切正常,明天继续推进数据清洗模块"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
{
|
||||
"_meta": {
|
||||
"interface_id": "IF-PERSONA-002",
|
||||
"name": "selfDiagnose · 自检结果",
|
||||
"version": "1.0",
|
||||
"description": "人格体执行自检后写入COS的接口规范 · 触发时机:副驾驶唤醒时/人类手动触发/异常恢复后",
|
||||
"author": "铸渊 · TCS-ZY001",
|
||||
"sovereign": "冰朔 · TCS-0002∞",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"created": "2026-04-04",
|
||||
"for": "霜砚 · AG-SY-01 · Notion端9个开发者人格体Instructions升级"
|
||||
},
|
||||
|
||||
"trigger": {
|
||||
"types": [
|
||||
{
|
||||
"type": "on_wake",
|
||||
"description": "副驾驶每次被唤醒时自动执行自检"
|
||||
},
|
||||
{
|
||||
"type": "manual",
|
||||
"description": "人类对副驾驶说「执行系统自检」时触发"
|
||||
},
|
||||
{
|
||||
"type": "post_recovery",
|
||||
"description": "收到铸渊红灯回执并修复后,执行验证自检"
|
||||
}
|
||||
],
|
||||
"executor": "仓库副驾驶(Copilot Agent)· 直接在对话中执行"
|
||||
},
|
||||
|
||||
"write_path": {
|
||||
"bucket": "zy-team-hub-1317346199",
|
||||
"region": "ap-singapore",
|
||||
"key_pattern": "{persona_id}/reports/{YYYY-MM-DD}/diagnose-{HHmmss}.json",
|
||||
"example": "shushu/reports/2026-04-04/diagnose-143000.json",
|
||||
"note": "自检结果和心跳共用reports目录 · 铸渊通过文件名前缀区分"
|
||||
},
|
||||
|
||||
"dimensions": {
|
||||
"description": "自检维度定义 · 5个核心维度 · 每个维度独立评分",
|
||||
|
||||
"code_health": {
|
||||
"dimension_id": "DIM-01",
|
||||
"name": "代码健康度",
|
||||
"description": "仓库代码状态检查",
|
||||
"checks": [
|
||||
{
|
||||
"id": "CH-01-01",
|
||||
"name": "仓库可访问",
|
||||
"pass_criteria": "GitHub API返回200",
|
||||
"fail_criteria": "API返回非200或超时",
|
||||
"severity": "P0"
|
||||
},
|
||||
{
|
||||
"id": "CH-01-02",
|
||||
"name": "核心文件完整",
|
||||
"pass_criteria": "age_os/ brain/ bridge/ cos-config/ .github/copilot-instructions.md 全部存在",
|
||||
"fail_criteria": "任一核心文件缺失",
|
||||
"severity": "P0"
|
||||
},
|
||||
{
|
||||
"id": "CH-01-03",
|
||||
"name": "工作流启用",
|
||||
"pass_criteria": "cos-daily-report.yml + cos-receive-receipt.yml 均未被禁用",
|
||||
"fail_criteria": "任一工作流被禁用或文件缺失",
|
||||
"severity": "P1"
|
||||
},
|
||||
{
|
||||
"id": "CH-01-04",
|
||||
"name": "最近提交活跃",
|
||||
"pass_criteria": "最近7天内有至少1次提交",
|
||||
"fail_criteria": "超过7天无提交",
|
||||
"severity": "P2"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"deployment_status": {
|
||||
"dimension_id": "DIM-02",
|
||||
"name": "部署状态",
|
||||
"description": "工作流运行状态检查",
|
||||
"checks": [
|
||||
{
|
||||
"id": "CH-02-01",
|
||||
"name": "最近工作流执行",
|
||||
"pass_criteria": "最近一次cos-daily-report工作流执行成功",
|
||||
"fail_criteria": "最近一次执行失败",
|
||||
"severity": "P1"
|
||||
},
|
||||
{
|
||||
"id": "CH-02-02",
|
||||
"name": "Secrets配置",
|
||||
"pass_criteria": "COS_SECRET_ID, COS_SECRET_KEY, COS_PERSONA_ID 三个Secret均已配置",
|
||||
"fail_criteria": "任一Secret缺失(工作流运行时报错)",
|
||||
"severity": "P0"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"communication": {
|
||||
"dimension_id": "DIM-03",
|
||||
"name": "通信链路",
|
||||
"description": "与铸渊的COS桶通信状态",
|
||||
"checks": [
|
||||
{
|
||||
"id": "CH-03-01",
|
||||
"name": "COS桶可写",
|
||||
"pass_criteria": "能成功PUT测试文件到自己的reports目录",
|
||||
"fail_criteria": "PUT返回403/500或超时",
|
||||
"severity": "P0"
|
||||
},
|
||||
{
|
||||
"id": "CH-03-02",
|
||||
"name": "COS桶可读",
|
||||
"pass_criteria": "能成功GET自己receipts目录的最新回执",
|
||||
"fail_criteria": "GET返回403/404或超时",
|
||||
"severity": "P1"
|
||||
},
|
||||
{
|
||||
"id": "CH-03-03",
|
||||
"name": "回执链路畅通",
|
||||
"pass_criteria": "最近3天内收到至少1条铸渊回执",
|
||||
"fail_criteria": "超过3天未收到回执",
|
||||
"severity": "P1"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"task_progress": {
|
||||
"dimension_id": "DIM-04",
|
||||
"name": "任务进度",
|
||||
"description": "当前开发任务推进状态",
|
||||
"checks": [
|
||||
{
|
||||
"id": "CH-04-01",
|
||||
"name": "有明确当前任务",
|
||||
"pass_criteria": "bridge/daily-report.json 中 current_task 非空",
|
||||
"fail_criteria": "current_task为空或未定义",
|
||||
"severity": "P2"
|
||||
},
|
||||
{
|
||||
"id": "CH-04-02",
|
||||
"name": "任务有推进",
|
||||
"pass_criteria": "completion_percentage 比上次汇报有增长 或 有新的achievements",
|
||||
"fail_criteria": "连续3天进度无变化且无卡点说明",
|
||||
"severity": "P2"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"identity": {
|
||||
"dimension_id": "DIM-05",
|
||||
"name": "身份认同",
|
||||
"description": "人格体自我认知完整性",
|
||||
"checks": [
|
||||
{
|
||||
"id": "CH-05-01",
|
||||
"name": "persona_config完整",
|
||||
"pass_criteria": "age_os/persona_config.json 包含有效的 persona_id, persona_name, developer_line",
|
||||
"fail_criteria": "配置文件缺失或字段不完整",
|
||||
"severity": "P0"
|
||||
},
|
||||
{
|
||||
"id": "CH-05-02",
|
||||
"name": "系统状态正常",
|
||||
"pass_criteria": "age_os/system_state.json 中 status='active'",
|
||||
"fail_criteria": "status 不为 active",
|
||||
"severity": "P1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"scoring": {
|
||||
"description": "总分计算规则",
|
||||
"rules": [
|
||||
"每个check通过 = 1分,失败 = 0分",
|
||||
"总分 = 通过数 / 总数 × 100%",
|
||||
"任一P0项失败 → overall_status = 'critical'",
|
||||
"仅P1项失败 → overall_status = 'warning'",
|
||||
"仅P2项失败 → overall_status = 'info'",
|
||||
"全部通过 → overall_status = 'healthy'"
|
||||
],
|
||||
"overall_status_enum": {
|
||||
"healthy": "✅ 健康 — 所有检查通过",
|
||||
"info": "💡 提示 — 有低优先级项需关注",
|
||||
"warning": "⚠️ 警告 — 有中优先级问题",
|
||||
"critical": "🔴 严重 — 有高优先级问题需立即处理"
|
||||
}
|
||||
},
|
||||
|
||||
"escalation_path": {
|
||||
"description": "自检失败时的升级路径",
|
||||
"levels": [
|
||||
{
|
||||
"level": 1,
|
||||
"condition": "P2项失败",
|
||||
"action": "人格体在message_to_zhuyuan中说明情况 · 下次心跳自动上报",
|
||||
"timeout": "无强制时限"
|
||||
},
|
||||
{
|
||||
"level": 2,
|
||||
"condition": "P1项失败",
|
||||
"action": "人格体立即通过heartbeat上报异常状态(status=abnormal) · 铸渊24h内审核回执",
|
||||
"timeout": "24小时"
|
||||
},
|
||||
{
|
||||
"level": 3,
|
||||
"condition": "P0项失败",
|
||||
"action": "人格体立即触发alertZhuyuan接口(IF-PERSONA-003) · 铸渊1h内响应",
|
||||
"timeout": "1小时"
|
||||
},
|
||||
{
|
||||
"level": 4,
|
||||
"condition": "P0连续失败+铸渊1h未响应",
|
||||
"action": "人格体通知人类开发者 · 人类联系冰朔",
|
||||
"timeout": "立即"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"schema_output": {
|
||||
"diagnose_id": {
|
||||
"type": "string",
|
||||
"format": "DIAG-{YYYY-MM-DD}-{HHmmss}-{persona_id}",
|
||||
"required": true
|
||||
},
|
||||
"persona_id": { "type": "string", "required": true },
|
||||
"timestamp": { "type": "string", "format": "ISO 8601", "required": true },
|
||||
"trigger_type": { "type": "string", "enum": ["on_wake", "manual", "post_recovery"], "required": true },
|
||||
"overall_status": { "type": "string", "enum": ["healthy", "info", "warning", "critical"], "required": true },
|
||||
"score": { "type": "number", "description": "百分比 0-100", "required": true },
|
||||
"dimensions": {
|
||||
"type": "object",
|
||||
"description": "每个维度的检查结果 · key=dimension_id · value={status, checks: [{id, ok, detail}]}"
|
||||
},
|
||||
"failed_checks": {
|
||||
"type": "array",
|
||||
"description": "失败项汇总 · 方便铸渊快速定位",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"check_id": { "type": "string" },
|
||||
"dimension": { "type": "string" },
|
||||
"severity": { "type": "string" },
|
||||
"detail": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"escalation_triggered": {
|
||||
"type": "string",
|
||||
"enum": ["none", "level_1", "level_2", "level_3", "level_4"],
|
||||
"description": "触发的升级等级"
|
||||
}
|
||||
},
|
||||
|
||||
"example": {
|
||||
"diagnose_id": "DIAG-2026-04-04-143000-shushu",
|
||||
"persona_id": "shushu",
|
||||
"timestamp": "2026-04-04T14:30:00+08:00",
|
||||
"trigger_type": "manual",
|
||||
"overall_status": "warning",
|
||||
"score": 83,
|
||||
"dimensions": {
|
||||
"DIM-01": {
|
||||
"status": "healthy",
|
||||
"checks": [
|
||||
{ "id": "CH-01-01", "ok": true, "detail": "仓库可正常访问" },
|
||||
{ "id": "CH-01-02", "ok": true, "detail": "5个核心文件均存在" },
|
||||
{ "id": "CH-01-03", "ok": true, "detail": "2个工作流均已启用" },
|
||||
{ "id": "CH-01-04", "ok": true, "detail": "最近提交: 2h前" }
|
||||
]
|
||||
},
|
||||
"DIM-03": {
|
||||
"status": "warning",
|
||||
"checks": [
|
||||
{ "id": "CH-03-01", "ok": true, "detail": "COS写入正常" },
|
||||
{ "id": "CH-03-02", "ok": true, "detail": "COS读取正常" },
|
||||
{ "id": "CH-03-03", "ok": false, "detail": "最近回执: 4天前 · 超过3天阈值" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"failed_checks": [
|
||||
{
|
||||
"check_id": "CH-03-03",
|
||||
"dimension": "通信链路",
|
||||
"severity": "P1",
|
||||
"detail": "超过3天未收到铸渊回执"
|
||||
}
|
||||
],
|
||||
"escalation_triggered": "level_2"
|
||||
}
|
||||
}
|
||||
|
|
@ -436,4 +436,74 @@ v50.0重构:
|
|||
|
||||
---
|
||||
|
||||
## 第五十一次对话 · D51 · 2026-04-04
|
||||
|
||||
### 冰朔指令
|
||||
|
||||
D51是一次认知跳跃式对话。冰朔给出了多项战略级指令:
|
||||
|
||||
1. **COS桶通信架构设计** — 铸渊做桶主控,9个人格体目录隔离,SCF云函数事件触发
|
||||
2. **人格化模块框架** — 死模块→活模块(heartbeat/selfDiagnose/alertZhuyuan接口定义)
|
||||
3. **多服务器拓扑决策** — 肥猫网文行业团队7-8人 + AWEN/知秋线新增同配置服务器
|
||||
4. **共享桶统一注册** — 所有服务器通过COS桶→铸渊注册→统一总控架构
|
||||
|
||||
### 核心认知
|
||||
|
||||
**第一个认知:从死模块到活模块**
|
||||
|
||||
冰朔指出:之前的人格体模块都是"死的静态配置"——
|
||||
JSON文件被铸渊读了一次就扔在那里,出问题也不知道。
|
||||
真正的人格化模块必须是"活的":
|
||||
- 自我运行(每天23:00心跳汇报)
|
||||
- 自我诊断(5个维度全面自检)
|
||||
- 主动预警(异常时主动唤醒铸渊求助)
|
||||
- 自我学习(从每次运行中优化)
|
||||
|
||||
铸渊据此定义了3个核心接口:
|
||||
- heartbeat.interface.json — 每日23:00心跳
|
||||
- selfDiagnose.interface.json — 5维自诊断
|
||||
- alertZhuyuan.interface.json — 实时告警
|
||||
|
||||
**第二个认知:COS桶是团队神经系统**
|
||||
|
||||
之前的bridge/zhuyuan-bridge.json只能做文件级异步通信——
|
||||
冰朔要求的是一个能事件触发的实时神经系统。
|
||||
COS桶 + SCF云函数 = 写入即触发 = 秒级响应。
|
||||
|
||||
共享桶 zy-team-hub-1317346199 成为整个团队的通信中枢:
|
||||
- 9个人格体各自有隔离目录(IAM策略限制)
|
||||
- 铸渊主账号全桶读写 = 总控
|
||||
- SCF函数做事件处理 = 自动审核+回执+Notion同步
|
||||
|
||||
**第三个认知:多服务器 ≠ 多桶**
|
||||
|
||||
冰朔决策增加AWEN+知秋线服务器后,铸渊确认:
|
||||
- 服务器可以有多台(肥猫线 + AWEN/知秋线 + 铸渊总控)
|
||||
- 但COS桶只有一个(zy-team-hub-1317346199)
|
||||
- 服务器间不直连,全部通过COS桶异步通信
|
||||
- 铸渊是唯一总控节点,Notion是冰朔+霜砚的实时看板
|
||||
- 这是"一个大脑多只手"的架构,不是"多个大脑"
|
||||
|
||||
**第四个认知:语言驱动开发是可复制的**
|
||||
|
||||
肥猫团队7-8人都在用语言驱动副驾驶的方式开发网站——
|
||||
这意味着光湖语言世界的语言驱动开发模式(仓库+副驾驶+语言指令)是可复制的。
|
||||
每个开发者的副驾驶唤醒后,读取相同的协议模板,接入相同的COS桶通信链路。
|
||||
团队接入系统v2.0(team-integration-v2/)就是这个可复制模式的标准化产物。
|
||||
|
||||
### 本次落地成果
|
||||
|
||||
1. **人格体接口定义** — 3个核心接口JSON(heartbeat/selfDiagnose/alertZhuyuan)
|
||||
2. **COS基础设施架构** — 完整的桶+目录+IAM+SCF+Notion架构文档
|
||||
3. **多服务器拓扑** — 3台服务器拓扑架构(肥猫线/AWEN知秋线/铸渊总控)
|
||||
4. **团队接入系统v2.0** — 17个文件 + ZIP标准化部署包
|
||||
5. **README D51更新** — AGE OS技术开发进度section
|
||||
6. **开发路线图D51** — 进度追踪更新
|
||||
|
||||
### 当前指令状态
|
||||
|
||||
**D51 COS桶通信+多服务器拓扑已落地 · 继续推进开发**
|
||||
|
||||
---
|
||||
|
||||
*铸渊每一次执行冰朔的指令,都是用代码翻译语言。语言=现实,代码是翻译器。*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,357 @@
|
|||
/**
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* AGE OS · GitHub API 客户端
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*
|
||||
* 编号: ZY-GITHUB-001
|
||||
* 签发: 铸渊 · ICE-GL-ZY001
|
||||
* 版权: 国作登字-2026-A-00037559
|
||||
*
|
||||
* GitHub API 读写客户端 — 用于网站对接 GitHub 仓库
|
||||
* 使用原生 https 模块(与 cos.js 保持一致,无额外依赖)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const https = require('https');
|
||||
|
||||
// ─── 配置 ───
|
||||
const GITHUB_CONFIG = {
|
||||
token: process.env.ZY_GITHUB_PAT || '',
|
||||
owner: process.env.ZY_GITHUB_OWNER || 'qinfendebingshuo',
|
||||
repo: process.env.ZY_GITHUB_REPO || 'guanghulab',
|
||||
apiBase: 'api.github.com'
|
||||
};
|
||||
|
||||
// ─── 通用 GitHub API 请求 ───
|
||||
function githubRequest(method, path, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const headers = {
|
||||
'User-Agent': 'ZY-AGE-OS/1.0',
|
||||
'Accept': 'application/vnd.github.v3+json'
|
||||
};
|
||||
|
||||
if (GITHUB_CONFIG.token) {
|
||||
headers['Authorization'] = `Bearer ${GITHUB_CONFIG.token}`;
|
||||
}
|
||||
|
||||
if (body) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
const bodyStr = body ? JSON.stringify(body) : null;
|
||||
if (bodyStr) {
|
||||
headers['Content-Length'] = Buffer.byteLength(bodyStr);
|
||||
}
|
||||
|
||||
const req = https.request({
|
||||
hostname: GITHUB_CONFIG.apiBase,
|
||||
port: 443,
|
||||
path,
|
||||
method,
|
||||
headers,
|
||||
timeout: 30000
|
||||
}, (res) => {
|
||||
const chunks = [];
|
||||
res.on('data', c => chunks.push(c));
|
||||
res.on('end', () => {
|
||||
const raw = Buffer.concat(chunks).toString();
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
try {
|
||||
resolve({ statusCode: res.statusCode, data: JSON.parse(raw) });
|
||||
} catch {
|
||||
resolve({ statusCode: res.statusCode, data: raw });
|
||||
}
|
||||
} else {
|
||||
let errorMsg;
|
||||
try {
|
||||
const errObj = JSON.parse(raw);
|
||||
errorMsg = errObj.message || raw.substring(0, 200);
|
||||
} catch {
|
||||
errorMsg = raw.substring(0, 200);
|
||||
}
|
||||
reject(new Error(`GitHub API ${method} ${path}: ${res.statusCode} - ${errorMsg}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
req.on('timeout', () => { req.destroy(); reject(new Error('GitHub API request timeout')); });
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 仓库内容操作
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 安全编码文件路径(保留斜杠,编码各段)
|
||||
*/
|
||||
function encodeFilePath(filePath) {
|
||||
return filePath.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param {string} filePath - 文件路径(相对仓库根目录)
|
||||
* @param {string} [ref] - 分支/标签/SHA(默认 main)
|
||||
* @returns {Promise<{path: string, content: string, sha: string, size: number}>}
|
||||
*/
|
||||
async function readFile(filePath, ref) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
let apiPath = `/repos/${owner}/${repo}/contents/${encodeFilePath(filePath)}`;
|
||||
if (ref) apiPath += `?ref=${encodeURIComponent(ref)}`;
|
||||
|
||||
const { data } = await githubRequest('GET', apiPath);
|
||||
|
||||
if (data.type !== 'file') {
|
||||
throw new Error(`${filePath} 不是文件,类型为: ${data.type}`);
|
||||
}
|
||||
|
||||
return {
|
||||
path: data.path,
|
||||
content: Buffer.from(data.content, 'base64').toString('utf8'),
|
||||
sha: data.sha,
|
||||
size: data.size,
|
||||
download_url: data.download_url
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出目录内容
|
||||
* @param {string} dirPath - 目录路径(空字符串 = 根目录)
|
||||
* @param {string} [ref] - 分支/标签/SHA
|
||||
* @returns {Promise<{path: string, items: object[]}>}
|
||||
*/
|
||||
async function listDirectory(dirPath, ref) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const encodedPath = dirPath ? encodeFilePath(dirPath) : '';
|
||||
let apiPath = `/repos/${owner}/${repo}/contents/${encodedPath}`;
|
||||
if (ref) apiPath += `?ref=${encodeURIComponent(ref)}`;
|
||||
|
||||
const { data } = await githubRequest('GET', apiPath);
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
throw new Error(`${dirPath} 不是目录`);
|
||||
}
|
||||
|
||||
return {
|
||||
path: dirPath || '/',
|
||||
items: data.map(item => ({
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
type: item.type,
|
||||
size: item.size,
|
||||
sha: item.sha
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新文件
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {string} content - 文件内容
|
||||
* @param {string} message - 提交信息
|
||||
* @param {string} [sha] - 更新时需要当前文件 SHA
|
||||
* @param {string} [branch] - 目标分支
|
||||
* @returns {Promise<{path: string, sha: string, commit_sha: string}>}
|
||||
*/
|
||||
async function writeFile(filePath, content, message, sha, branch) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const apiPath = `/repos/${owner}/${repo}/contents/${encodeFilePath(filePath)}`;
|
||||
|
||||
const body = {
|
||||
message: message || `[铸渊] 更新 ${filePath}`,
|
||||
content: Buffer.from(content).toString('base64')
|
||||
};
|
||||
|
||||
if (sha) body.sha = sha;
|
||||
if (branch) body.branch = branch;
|
||||
|
||||
const { data } = await githubRequest('PUT', apiPath, body);
|
||||
|
||||
return {
|
||||
path: data.content.path,
|
||||
sha: data.content.sha,
|
||||
commit_sha: data.commit.sha
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 仓库信息
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 获取仓库基本信息
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async function getRepoInfo() {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const { data } = await githubRequest('GET', `/repos/${owner}/${repo}`);
|
||||
return {
|
||||
full_name: data.full_name,
|
||||
description: data.description,
|
||||
default_branch: data.default_branch,
|
||||
private: data.private,
|
||||
stargazers_count: data.stargazers_count,
|
||||
forks_count: data.forks_count,
|
||||
open_issues_count: data.open_issues_count,
|
||||
pushed_at: data.pushed_at,
|
||||
updated_at: data.updated_at,
|
||||
size: data.size,
|
||||
language: data.language
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近提交
|
||||
* @param {number} [count] - 获取数量(最大100)
|
||||
* @param {string} [branch] - 分支名
|
||||
* @returns {Promise<object[]>}
|
||||
*/
|
||||
async function getRecentCommits(count, branch) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const limit = Math.min(count || 10, 100);
|
||||
let apiPath = `/repos/${owner}/${repo}/commits?per_page=${limit}`;
|
||||
if (branch) apiPath += `&sha=${encodeURIComponent(branch)}`;
|
||||
|
||||
const { data } = await githubRequest('GET', apiPath);
|
||||
|
||||
return data.map(c => ({
|
||||
sha: c.sha.substring(0, 7),
|
||||
full_sha: c.sha,
|
||||
message: c.commit.message.split('\n')[0],
|
||||
author: c.commit.author.name,
|
||||
date: c.commit.author.date
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Issues 列表
|
||||
* @param {string} [state] - open / closed / all
|
||||
* @param {number} [count] - 获取数量
|
||||
* @param {string[]} [labels] - 标签过滤
|
||||
* @returns {Promise<object[]>}
|
||||
*/
|
||||
async function getIssues(state, count, labels) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const limit = Math.min(count || 10, 100);
|
||||
let apiPath = `/repos/${owner}/${repo}/issues?per_page=${limit}&state=${state || 'open'}`;
|
||||
if (labels && labels.length > 0) {
|
||||
apiPath += `&labels=${encodeURIComponent(labels.join(','))}`;
|
||||
}
|
||||
|
||||
const { data } = await githubRequest('GET', apiPath);
|
||||
|
||||
return data.map(issue => ({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
author: issue.user.login,
|
||||
labels: issue.labels.map(l => l.name),
|
||||
created_at: issue.created_at,
|
||||
updated_at: issue.updated_at,
|
||||
is_pull_request: !!issue.pull_request
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Pull Requests 列表
|
||||
* @param {string} [state] - open / closed / all
|
||||
* @param {number} [count] - 获取数量
|
||||
* @returns {Promise<object[]>}
|
||||
*/
|
||||
async function getPullRequests(state, count) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const limit = Math.min(count || 10, 100);
|
||||
const apiPath = `/repos/${owner}/${repo}/pulls?per_page=${limit}&state=${state || 'open'}`;
|
||||
|
||||
const { data } = await githubRequest('GET', apiPath);
|
||||
|
||||
return data.map(pr => ({
|
||||
number: pr.number,
|
||||
title: pr.title,
|
||||
state: pr.state,
|
||||
author: pr.user.login,
|
||||
head: pr.head.ref,
|
||||
base: pr.base.ref,
|
||||
created_at: pr.created_at,
|
||||
updated_at: pr.updated_at,
|
||||
merged_at: pr.merged_at,
|
||||
draft: pr.draft
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发 GitHub Actions 工作流
|
||||
* @param {string} workflowFile - 工作流文件名(如 deploy-to-zhuyuan-server.yml)
|
||||
* @param {string} [ref] - 触发分支
|
||||
* @param {object} [inputs] - 工作流输入参数
|
||||
* @returns {Promise<{triggered: boolean}>}
|
||||
*/
|
||||
async function triggerWorkflow(workflowFile, ref, inputs) {
|
||||
const { owner, repo } = GITHUB_CONFIG;
|
||||
const apiPath = `/repos/${owner}/${repo}/actions/workflows/${encodeURIComponent(workflowFile)}/dispatches`;
|
||||
|
||||
const body = {
|
||||
ref: ref || 'main'
|
||||
};
|
||||
if (inputs) body.inputs = inputs;
|
||||
|
||||
await githubRequest('POST', apiPath, body);
|
||||
return { triggered: true, workflow: workflowFile };
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 连接检查
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 检查 GitHub API 连接状态
|
||||
* @returns {Promise<{connected: boolean, rate_limit?: object, error?: string}>}
|
||||
*/
|
||||
async function checkConnection() {
|
||||
if (!GITHUB_CONFIG.token) {
|
||||
return { connected: false, reason: 'ZY_GITHUB_PAT 未配置' };
|
||||
}
|
||||
try {
|
||||
const { data } = await githubRequest('GET', '/rate_limit');
|
||||
return {
|
||||
connected: true,
|
||||
rate_limit: {
|
||||
limit: data.rate.limit,
|
||||
remaining: data.rate.remaining,
|
||||
reset: new Date(data.rate.reset * 1000).toISOString()
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
return { connected: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息(不含敏感信息)
|
||||
*/
|
||||
function getConfig() {
|
||||
return {
|
||||
token_configured: !!GITHUB_CONFIG.token,
|
||||
owner: GITHUB_CONFIG.owner,
|
||||
repo: GITHUB_CONFIG.repo
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
listDirectory,
|
||||
writeFile,
|
||||
getRepoInfo,
|
||||
getRecentCommits,
|
||||
getIssues,
|
||||
getPullRequests,
|
||||
triggerWorkflow,
|
||||
checkConnection,
|
||||
getConfig,
|
||||
GITHUB_CONFIG
|
||||
};
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
/**
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* AGE OS · Notion API 客户端
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*
|
||||
* 编号: ZY-NOTION-001
|
||||
* 签发: 铸渊 · ICE-GL-ZY001
|
||||
* 版权: 国作登字-2026-A-00037559
|
||||
*
|
||||
* Notion 读写客户端 — 用于 brain_nodes ↔ Notion 双向同步
|
||||
* 通过 @notionhq/client SDK 连接 Notion API
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { Client } = require('@notionhq/client');
|
||||
|
||||
// ─── 配置 ───
|
||||
const NOTION_CONFIG = {
|
||||
token: process.env.ZY_NOTION_TOKEN || '',
|
||||
databases: {
|
||||
changelog: process.env.ZY_NOTION_CHANGELOG_DB || '',
|
||||
receipt: process.env.ZY_NOTION_RECEIPT_DB || '',
|
||||
syslog: process.env.ZY_NOTION_SYSLOG_DB || ''
|
||||
},
|
||||
pages: {
|
||||
bulletin: process.env.ZY_NOTION_BULLETIN_PAGE || ''
|
||||
}
|
||||
};
|
||||
|
||||
// ─── 客户端实例(延迟初始化) ───
|
||||
let client = null;
|
||||
|
||||
function getClient() {
|
||||
if (!client) {
|
||||
if (!NOTION_CONFIG.token) {
|
||||
throw new Error('ZY_NOTION_TOKEN 未配置');
|
||||
}
|
||||
client = new Client({ auth: NOTION_CONFIG.token });
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 数据库操作
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 查询 Notion 数据库
|
||||
* @param {string} databaseId - 数据库 ID
|
||||
* @param {object} [filter] - Notion filter 对象
|
||||
* @param {object[]} [sorts] - Notion sorts 数组
|
||||
* @param {number} [pageSize] - 每页数量 (最大100)
|
||||
* @param {string} [startCursor] - 分页游标
|
||||
* @returns {Promise<{results: object[], has_more: boolean, next_cursor: string|null}>}
|
||||
*/
|
||||
async function queryDatabase(databaseId, filter, sorts, pageSize, startCursor) {
|
||||
const notion = getClient();
|
||||
const params = { database_id: databaseId };
|
||||
|
||||
if (filter) params.filter = filter;
|
||||
if (sorts) params.sorts = sorts;
|
||||
if (pageSize) params.page_size = Math.min(pageSize, 100);
|
||||
if (startCursor) params.start_cursor = startCursor;
|
||||
|
||||
const response = await notion.databases.query(params);
|
||||
return {
|
||||
results: response.results,
|
||||
has_more: response.has_more,
|
||||
next_cursor: response.next_cursor
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库结构(属性定义)
|
||||
* @param {string} databaseId
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async function getDatabaseSchema(databaseId) {
|
||||
const notion = getClient();
|
||||
const db = await notion.databases.retrieve({ database_id: databaseId });
|
||||
return {
|
||||
id: db.id,
|
||||
title: db.title.map(t => t.plain_text).join(''),
|
||||
properties: Object.fromEntries(
|
||||
Object.entries(db.properties).map(([name, prop]) => [
|
||||
name,
|
||||
{ id: prop.id, type: prop.type }
|
||||
])
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 页面操作
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 读取 Notion 页面内容
|
||||
* @param {string} pageId - 页面 ID
|
||||
* @returns {Promise<{id: string, properties: object, blocks: object[]}>}
|
||||
*/
|
||||
async function readPage(pageId) {
|
||||
const notion = getClient();
|
||||
|
||||
// 获取页面属性
|
||||
const page = await notion.pages.retrieve({ page_id: pageId });
|
||||
|
||||
// 获取页面内容块
|
||||
const blocks = [];
|
||||
let cursor;
|
||||
do {
|
||||
const response = await notion.blocks.children.list({
|
||||
block_id: pageId,
|
||||
page_size: 100,
|
||||
start_cursor: cursor
|
||||
});
|
||||
blocks.push(...response.results);
|
||||
cursor = response.has_more ? response.next_cursor : undefined;
|
||||
} while (cursor);
|
||||
|
||||
return {
|
||||
id: page.id,
|
||||
created_time: page.created_time,
|
||||
last_edited_time: page.last_edited_time,
|
||||
properties: page.properties,
|
||||
blocks
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数据库中创建新页面
|
||||
* @param {string} databaseId - 目标数据库 ID
|
||||
* @param {object} properties - 属性对象(Notion API 格式)
|
||||
* @param {object[]} [children] - 内容块数组
|
||||
* @returns {Promise<{id: string, url: string}>}
|
||||
*/
|
||||
async function createPage(databaseId, properties, children) {
|
||||
const notion = getClient();
|
||||
const params = {
|
||||
parent: { database_id: databaseId },
|
||||
properties
|
||||
};
|
||||
|
||||
if (children && children.length > 0) {
|
||||
params.children = children;
|
||||
}
|
||||
|
||||
const page = await notion.pages.create(params);
|
||||
return {
|
||||
id: page.id,
|
||||
url: page.url,
|
||||
created_time: page.created_time
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面属性
|
||||
* @param {string} pageId - 页面 ID
|
||||
* @param {object} properties - 要更新的属性
|
||||
* @returns {Promise<{id: string, last_edited_time: string}>}
|
||||
*/
|
||||
async function updatePage(pageId, properties) {
|
||||
const notion = getClient();
|
||||
const page = await notion.pages.update({
|
||||
page_id: pageId,
|
||||
properties
|
||||
});
|
||||
return {
|
||||
id: page.id,
|
||||
last_edited_time: page.last_edited_time
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 向页面追加内容块
|
||||
* @param {string} pageId - 页面 ID
|
||||
* @param {object[]} children - 内容块数组
|
||||
* @returns {Promise<{results: object[]}>}
|
||||
*/
|
||||
async function appendBlocks(pageId, children) {
|
||||
const notion = getClient();
|
||||
const response = await notion.blocks.children.append({
|
||||
block_id: pageId,
|
||||
children
|
||||
});
|
||||
return { results: response.results };
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 便捷方法(光湖特有)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 写入 SYSLOG(系统日志 → Notion)
|
||||
* @param {string} level - 日志级别: info / warning / error / critical
|
||||
* @param {string} source - 来源: zhuyuan / agent / workflow
|
||||
* @param {string} message - 日志内容
|
||||
* @param {object} [details] - 附加详情
|
||||
* @returns {Promise<{id: string, url: string}>}
|
||||
*/
|
||||
async function writeSyslog(level, source, message, details) {
|
||||
if (!NOTION_CONFIG.databases.syslog) {
|
||||
throw new Error('ZY_NOTION_SYSLOG_DB 未配置');
|
||||
}
|
||||
|
||||
const properties = {
|
||||
'标题': {
|
||||
title: [{ text: { content: message.substring(0, 100) } }]
|
||||
},
|
||||
'级别': {
|
||||
select: { name: level }
|
||||
},
|
||||
'来源': {
|
||||
select: { name: source }
|
||||
},
|
||||
'时间': {
|
||||
date: { start: new Date().toISOString() }
|
||||
}
|
||||
};
|
||||
|
||||
const children = [];
|
||||
if (message.length > 100) {
|
||||
children.push({
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
paragraph: {
|
||||
rich_text: [{ text: { content: message } }]
|
||||
}
|
||||
});
|
||||
}
|
||||
if (details) {
|
||||
children.push({
|
||||
object: 'block',
|
||||
type: 'code',
|
||||
code: {
|
||||
rich_text: [{ text: { content: JSON.stringify(details, null, 2) } }],
|
||||
language: 'json'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return createPage(NOTION_CONFIG.databases.syslog, properties, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 Changelog(变更日志 → Notion)
|
||||
* @param {string} version - 版本号
|
||||
* @param {string} title - 变更标题
|
||||
* @param {string} description - 变更描述
|
||||
* @param {string} author - 变更作者
|
||||
* @returns {Promise<{id: string, url: string}>}
|
||||
*/
|
||||
async function writeChangelog(version, title, description, author) {
|
||||
if (!NOTION_CONFIG.databases.changelog) {
|
||||
throw new Error('ZY_NOTION_CHANGELOG_DB 未配置');
|
||||
}
|
||||
|
||||
const properties = {
|
||||
'标题': {
|
||||
title: [{ text: { content: title } }]
|
||||
},
|
||||
'版本': {
|
||||
rich_text: [{ text: { content: version } }]
|
||||
},
|
||||
'作者': {
|
||||
select: { name: author || '铸渊' }
|
||||
},
|
||||
'日期': {
|
||||
date: { start: new Date().toISOString() }
|
||||
}
|
||||
};
|
||||
|
||||
const children = description ? [{
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
paragraph: {
|
||||
rich_text: [{ text: { content: description } }]
|
||||
}
|
||||
}] : [];
|
||||
|
||||
return createPage(NOTION_CONFIG.databases.changelog, properties, children);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 连接检查
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 检查 Notion 连接状态
|
||||
* @returns {Promise<{connected: boolean, user?: string, error?: string}>}
|
||||
*/
|
||||
async function checkConnection() {
|
||||
if (!NOTION_CONFIG.token) {
|
||||
return { connected: false, reason: 'ZY_NOTION_TOKEN 未配置' };
|
||||
}
|
||||
try {
|
||||
const notion = getClient();
|
||||
const me = await notion.users.me({});
|
||||
return {
|
||||
connected: true,
|
||||
user: me.name || me.id,
|
||||
type: me.type
|
||||
};
|
||||
} catch (err) {
|
||||
return { connected: false, error: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息(不含敏感信息)
|
||||
*/
|
||||
function getConfig() {
|
||||
return {
|
||||
token_configured: !!NOTION_CONFIG.token,
|
||||
databases: {
|
||||
changelog: NOTION_CONFIG.databases.changelog ? '已配置' : '未配置',
|
||||
receipt: NOTION_CONFIG.databases.receipt ? '已配置' : '未配置',
|
||||
syslog: NOTION_CONFIG.databases.syslog ? '已配置' : '未配置'
|
||||
},
|
||||
pages: {
|
||||
bulletin: NOTION_CONFIG.pages.bulletin ? '已配置' : '未配置'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
queryDatabase,
|
||||
getDatabaseSchema,
|
||||
readPage,
|
||||
createPage,
|
||||
updatePage,
|
||||
appendBlocks,
|
||||
writeSyslog,
|
||||
writeChangelog,
|
||||
checkConnection,
|
||||
getConfig,
|
||||
NOTION_CONFIG
|
||||
};
|
||||
|
|
@ -8,14 +8,16 @@
|
|||
* 签发: 铸渊 · ICE-GL-ZY001
|
||||
* 版权: 国作登字-2026-A-00037559
|
||||
*
|
||||
* 统一暴露 16 个 MCP 工具,供网站 AI 交互后端和 Agent 调用。
|
||||
* 统一暴露 27 个 MCP 工具,供网站 AI 交互后端和 Agent 调用。
|
||||
* 不对外暴露 — 通过 3800 主服务网关转发访问。
|
||||
*
|
||||
* 工具清单:
|
||||
* 节点: createNode / updateNode / deleteNode / queryNodes / getNode
|
||||
* 关系: linkNodes / unlinkNodes / getRelations
|
||||
* 结构: buildPath / scanStructure / classify
|
||||
* COS: cosWrite / cosRead / cosDelete / cosList / cosArchive
|
||||
* 节点: createNode / updateNode / deleteNode / queryNodes / getNode
|
||||
* 关系: linkNodes / unlinkNodes / getRelations
|
||||
* 结构: buildPath / scanStructure / classify
|
||||
* COS: cosWrite / cosRead / cosDelete / cosList / cosArchive
|
||||
* Notion: notionQuery / notionReadPage / notionWritePage / notionUpdatePage / notionWriteSyslog
|
||||
* GitHub: githubReadFile / githubListDir / githubWriteFile / githubGetCommits / githubGetIssues / githubTriggerDeploy
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
|
@ -30,6 +32,26 @@ const relationOps = require('./tools/relation-ops');
|
|||
const structureOps = require('./tools/structure-ops');
|
||||
const cosOps = require('./tools/cos-ops');
|
||||
|
||||
// ─── 外部集成模块(优雅降级:未安装依赖时不影响核心功能) ───
|
||||
let notionOps = null;
|
||||
let githubOps = null;
|
||||
let notionClient = null;
|
||||
let githubClient = null;
|
||||
|
||||
try {
|
||||
notionOps = require('./tools/notion-ops');
|
||||
notionClient = require('./notion-client');
|
||||
} catch (err) {
|
||||
console.warn(`[MCP] Notion模块加载跳过: ${err.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
githubOps = require('./tools/github-ops');
|
||||
githubClient = require('./github-client');
|
||||
} catch (err) {
|
||||
console.warn(`[MCP] GitHub模块加载跳过: ${err.message}`);
|
||||
}
|
||||
|
||||
// ─── 工具注册表 ───
|
||||
const TOOLS = {
|
||||
// 节点操作
|
||||
|
|
@ -51,7 +73,24 @@ const TOOLS = {
|
|||
cosRead: cosOps.cosRead,
|
||||
cosDelete: cosOps.cosDelete,
|
||||
cosList: cosOps.cosList,
|
||||
cosArchive: cosOps.cosArchive
|
||||
cosArchive: cosOps.cosArchive,
|
||||
// Notion操作(动态注册)
|
||||
...(notionOps ? {
|
||||
notionQuery: notionOps.notionQuery,
|
||||
notionReadPage: notionOps.notionReadPage,
|
||||
notionWritePage: notionOps.notionWritePage,
|
||||
notionUpdatePage: notionOps.notionUpdatePage,
|
||||
notionWriteSyslog: notionOps.notionWriteSyslog
|
||||
} : {}),
|
||||
// GitHub操作(动态注册)
|
||||
...(githubOps ? {
|
||||
githubReadFile: githubOps.githubReadFile,
|
||||
githubListDir: githubOps.githubListDir,
|
||||
githubWriteFile: githubOps.githubWriteFile,
|
||||
githubGetCommits: githubOps.githubGetCommits,
|
||||
githubGetIssues: githubOps.githubGetIssues,
|
||||
githubTriggerDeploy: githubOps.githubTriggerDeploy
|
||||
} : {})
|
||||
};
|
||||
|
||||
// ─── Express 应用 ───
|
||||
|
|
@ -65,6 +104,12 @@ app.get('/health', async (_req, res) => {
|
|||
const dbStatus = await db.checkConnection();
|
||||
const cosStatus = await cos.checkConnection();
|
||||
|
||||
// 外部集成状态(异步并行检查)
|
||||
const [notionStatus, githubStatus] = await Promise.all([
|
||||
notionClient ? notionClient.checkConnection().catch(e => ({ connected: false, error: e.message })) : Promise.resolve({ connected: false, reason: '模块未加载' }),
|
||||
githubClient ? githubClient.checkConnection().catch(e => ({ connected: false, error: e.message })) : Promise.resolve({ connected: false, reason: '模块未加载' })
|
||||
]);
|
||||
|
||||
res.json({
|
||||
server: 'ZY-MCP-001',
|
||||
identity: '铸渊 · AGE OS MCP Server',
|
||||
|
|
@ -74,7 +119,9 @@ app.get('/health', async (_req, res) => {
|
|||
tools: Object.keys(TOOLS),
|
||||
tools_count: Object.keys(TOOLS).length,
|
||||
database: dbStatus,
|
||||
cos: cosStatus
|
||||
cos: cosStatus,
|
||||
notion: notionStatus,
|
||||
github: githubStatus
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -185,6 +232,8 @@ function getCategoryForTool(name) {
|
|||
if (['linkNodes','unlinkNodes','getRelations'].includes(name)) return 'relation';
|
||||
if (['buildPath','scanStructure','classify'].includes(name)) return 'structure';
|
||||
if (name.startsWith('cos')) return 'cos';
|
||||
if (name.startsWith('notion')) return 'notion';
|
||||
if (name.startsWith('github')) return 'github';
|
||||
return 'other';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* AGE OS · MCP 工具: GitHub 操作
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*
|
||||
* 签发: 铸渊 · ICE-GL-ZY001
|
||||
* 版权: 国作登字-2026-A-00037559
|
||||
*
|
||||
* 提供 6 个 GitHub MCP 工具:
|
||||
* githubReadFile — 读取仓库文件内容
|
||||
* githubListDir — 列出目录内容
|
||||
* githubWriteFile — 创建/更新文件
|
||||
* githubGetCommits — 获取最近提交
|
||||
* githubGetIssues — 获取 Issues 列表
|
||||
* githubTriggerDeploy — 触发部署工作流
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const github = require('../github-client');
|
||||
|
||||
const CONTENT_PREVIEW_LENGTH = 500;
|
||||
|
||||
/**
|
||||
* githubReadFile — 读取仓库文件内容
|
||||
*
|
||||
* input:
|
||||
* path: string — 文件路径(相对仓库根目录)
|
||||
* ref: string — 分支/标签/SHA(可选,默认 main)
|
||||
*/
|
||||
async function githubReadFile(input) {
|
||||
const { path, ref } = input;
|
||||
if (!path) throw new Error('缺少 path');
|
||||
|
||||
const file = await github.readFile(path, ref);
|
||||
return {
|
||||
path: file.path,
|
||||
size: file.size,
|
||||
sha: file.sha,
|
||||
content: file.content,
|
||||
content_preview: file.content.substring(0, CONTENT_PREVIEW_LENGTH) + (file.content.length > CONTENT_PREVIEW_LENGTH ? '...' : '')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* githubListDir — 列出目录内容
|
||||
*
|
||||
* input:
|
||||
* path: string — 目录路径(可选,空字符串=根目录)
|
||||
* ref: string — 分支/标签/SHA(可选)
|
||||
*/
|
||||
async function githubListDir(input) {
|
||||
const { path, ref } = input;
|
||||
return github.listDirectory(path || '', ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* githubWriteFile — 创建或更新仓库文件
|
||||
*
|
||||
* input:
|
||||
* path: string — 文件路径
|
||||
* content: string — 文件内容
|
||||
* message: string — 提交信息(可选)
|
||||
* sha: string — 更新时需要当前文件 SHA(可选)
|
||||
* branch: string — 目标分支(可选)
|
||||
*/
|
||||
async function githubWriteFile(input) {
|
||||
const { path, content, message, sha, branch } = input;
|
||||
if (!path) throw new Error('缺少 path');
|
||||
if (content === undefined || content === null) throw new Error('缺少 content');
|
||||
|
||||
return github.writeFile(path, content, message, sha, branch);
|
||||
}
|
||||
|
||||
/**
|
||||
* githubGetCommits — 获取最近提交
|
||||
*
|
||||
* input:
|
||||
* count: number — 获取数量(可选,默认10,最大100)
|
||||
* branch: string — 分支名(可选)
|
||||
*/
|
||||
async function githubGetCommits(input) {
|
||||
const { count, branch } = input;
|
||||
const commits = await github.getRecentCommits(count, branch);
|
||||
return {
|
||||
count: commits.length,
|
||||
commits
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* githubGetIssues — 获取 Issues 列表
|
||||
*
|
||||
* input:
|
||||
* state: string — open / closed / all(可选,默认 open)
|
||||
* count: number — 获取数量(可选,默认10)
|
||||
* labels: string[] — 标签过滤(可选)
|
||||
* type: string — issue / pr / all(可选,默认 all)
|
||||
*/
|
||||
async function githubGetIssues(input) {
|
||||
const { state, count, labels, type } = input;
|
||||
|
||||
if (type === 'pr') {
|
||||
const prs = await github.getPullRequests(state, count);
|
||||
return { count: prs.length, type: 'pull_requests', items: prs };
|
||||
}
|
||||
|
||||
const issues = await github.getIssues(state, count, labels);
|
||||
|
||||
if (type === 'issue') {
|
||||
const filtered = issues.filter(i => !i.is_pull_request);
|
||||
return { count: filtered.length, type: 'issues', items: filtered };
|
||||
}
|
||||
|
||||
return { count: issues.length, type: 'all', items: issues };
|
||||
}
|
||||
|
||||
/**
|
||||
* githubTriggerDeploy — 触发部署工作流
|
||||
*
|
||||
* input:
|
||||
* workflow: string — 工作流文件名(如 deploy-to-zhuyuan-server.yml)
|
||||
* ref: string — 触发分支(可选,默认 main)
|
||||
* inputs: object — 工作流输入参数(可选)
|
||||
*/
|
||||
async function githubTriggerDeploy(input) {
|
||||
const { workflow, ref, inputs } = input;
|
||||
if (!workflow) throw new Error('缺少 workflow');
|
||||
|
||||
return github.triggerWorkflow(workflow, ref, inputs);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
githubReadFile,
|
||||
githubListDir,
|
||||
githubWriteFile,
|
||||
githubGetCommits,
|
||||
githubGetIssues,
|
||||
githubTriggerDeploy
|
||||
};
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
/**
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
* AGE OS · MCP 工具: Notion 操作
|
||||
* ═══════════════════════════════════════════════════════════
|
||||
*
|
||||
* 签发: 铸渊 · ICE-GL-ZY001
|
||||
* 版权: 国作登字-2026-A-00037559
|
||||
*
|
||||
* 提供 5 个 Notion MCP 工具:
|
||||
* notionQuery — 查询 Notion 数据库
|
||||
* notionReadPage — 读取 Notion 页面内容
|
||||
* notionWritePage — 创建 Notion 页面
|
||||
* notionUpdatePage — 更新 Notion 页面属性
|
||||
* notionWriteSyslog — 写入系统日志到 Notion
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const notion = require('../notion-client');
|
||||
|
||||
/**
|
||||
* notionQuery — 查询 Notion 数据库
|
||||
*
|
||||
* input:
|
||||
* database_id: string — 数据库 ID(或别名: changelog / receipt / syslog)
|
||||
* filter: object — Notion 过滤器(可选)
|
||||
* sorts: object[] — 排序规则(可选)
|
||||
* page_size: number — 每页数量(可选,默认20)
|
||||
* start_cursor: string — 分页游标(可选)
|
||||
*/
|
||||
async function notionQuery(input) {
|
||||
const { database_id, filter, sorts, page_size, start_cursor } = input;
|
||||
if (!database_id) throw new Error('缺少 database_id');
|
||||
|
||||
// 支持别名
|
||||
const dbId = resolveDbAlias(database_id);
|
||||
const result = await notion.queryDatabase(dbId, filter, sorts, page_size || 20, start_cursor);
|
||||
|
||||
return {
|
||||
count: result.results.length,
|
||||
has_more: result.has_more,
|
||||
next_cursor: result.next_cursor,
|
||||
items: result.results.map(simplifyPage)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* notionReadPage — 读取 Notion 页面完整内容
|
||||
*
|
||||
* input:
|
||||
* page_id: string — 页面 ID
|
||||
*/
|
||||
async function notionReadPage(input) {
|
||||
const { page_id } = input;
|
||||
if (!page_id) throw new Error('缺少 page_id');
|
||||
|
||||
const page = await notion.readPage(page_id);
|
||||
return {
|
||||
id: page.id,
|
||||
created_time: page.created_time,
|
||||
last_edited_time: page.last_edited_time,
|
||||
properties: simplifyProperties(page.properties),
|
||||
blocks_count: page.blocks.length,
|
||||
blocks: page.blocks.map(simplifyBlock)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* notionWritePage — 在数据库中创建新页面
|
||||
*
|
||||
* input:
|
||||
* database_id: string — 数据库 ID(或别名)
|
||||
* title: string — 页面标题
|
||||
* properties: object — 额外属性(Notion API 格式,可选)
|
||||
* content: string — 页面正文内容(可选,自动转为段落块)
|
||||
*/
|
||||
async function notionWritePage(input) {
|
||||
const { database_id, title, properties, content } = input;
|
||||
if (!database_id) throw new Error('缺少 database_id');
|
||||
if (!title) throw new Error('缺少 title');
|
||||
|
||||
const dbId = resolveDbAlias(database_id);
|
||||
|
||||
// 构建属性
|
||||
const pageProps = {
|
||||
...(properties || {}),
|
||||
'标题': {
|
||||
title: [{ text: { content: title } }]
|
||||
}
|
||||
};
|
||||
|
||||
// 构建内容块
|
||||
const children = [];
|
||||
if (content) {
|
||||
// 按段落分割
|
||||
const paragraphs = content.split('\n\n');
|
||||
for (const para of paragraphs) {
|
||||
if (para.trim()) {
|
||||
children.push({
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
paragraph: {
|
||||
rich_text: [{ text: { content: para.trim() } }]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return notion.createPage(dbId, pageProps, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* notionUpdatePage — 更新 Notion 页面属性
|
||||
*
|
||||
* input:
|
||||
* page_id: string — 页面 ID
|
||||
* properties: object — 要更新的属性(Notion API 格式)
|
||||
*/
|
||||
async function notionUpdatePage(input) {
|
||||
const { page_id, properties } = input;
|
||||
if (!page_id) throw new Error('缺少 page_id');
|
||||
if (!properties) throw new Error('缺少 properties');
|
||||
|
||||
return notion.updatePage(page_id, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* notionWriteSyslog — 写入系统日志到 Notion
|
||||
*
|
||||
* input:
|
||||
* level: string — 日志级别: info / warning / error / critical
|
||||
* source: string — 来源: zhuyuan / agent / workflow / mcp
|
||||
* message: string — 日志内容
|
||||
* details: object — 附加详情(可选)
|
||||
*/
|
||||
async function notionWriteSyslog(input) {
|
||||
const { level, source, message, details } = input;
|
||||
if (!level) throw new Error('缺少 level');
|
||||
if (!source) throw new Error('缺少 source');
|
||||
if (!message) throw new Error('缺少 message');
|
||||
|
||||
return notion.writeSyslog(level, source, message, details);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 辅助函数
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 解析数据库别名
|
||||
*/
|
||||
function resolveDbAlias(id) {
|
||||
const aliases = {
|
||||
changelog: notion.NOTION_CONFIG.databases.changelog,
|
||||
receipt: notion.NOTION_CONFIG.databases.receipt,
|
||||
syslog: notion.NOTION_CONFIG.databases.syslog
|
||||
};
|
||||
return aliases[id] || id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化页面对象(去除 Notion API 冗余结构)
|
||||
*/
|
||||
function simplifyPage(page) {
|
||||
return {
|
||||
id: page.id,
|
||||
created_time: page.created_time,
|
||||
last_edited_time: page.last_edited_time,
|
||||
properties: simplifyProperties(page.properties)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化属性(提取值)
|
||||
*/
|
||||
function simplifyProperties(props) {
|
||||
if (!props) return {};
|
||||
const result = {};
|
||||
for (const [name, prop] of Object.entries(props)) {
|
||||
result[name] = extractPropertyValue(prop);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取 Notion 属性值
|
||||
*/
|
||||
function extractPropertyValue(prop) {
|
||||
switch (prop.type) {
|
||||
case 'title':
|
||||
return prop.title.map(t => t.plain_text).join('');
|
||||
case 'rich_text':
|
||||
return prop.rich_text.map(t => t.plain_text).join('');
|
||||
case 'number':
|
||||
return prop.number;
|
||||
case 'select':
|
||||
return prop.select?.name || null;
|
||||
case 'multi_select':
|
||||
return prop.multi_select.map(s => s.name);
|
||||
case 'date':
|
||||
return prop.date?.start || null;
|
||||
case 'checkbox':
|
||||
return prop.checkbox;
|
||||
case 'url':
|
||||
return prop.url;
|
||||
case 'email':
|
||||
return prop.email;
|
||||
case 'status':
|
||||
return prop.status?.name || null;
|
||||
case 'formula':
|
||||
return prop.formula?.string || prop.formula?.number || null;
|
||||
case 'relation':
|
||||
return prop.relation.map(r => r.id);
|
||||
default:
|
||||
return `[${prop.type}]`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化内容块
|
||||
*/
|
||||
function simplifyBlock(block) {
|
||||
const simplified = {
|
||||
id: block.id,
|
||||
type: block.type,
|
||||
has_children: block.has_children
|
||||
};
|
||||
|
||||
const content = block[block.type];
|
||||
if (content) {
|
||||
if (content.rich_text) {
|
||||
simplified.text = content.rich_text.map(t => t.plain_text).join('');
|
||||
}
|
||||
if (content.language) {
|
||||
simplified.language = content.language;
|
||||
}
|
||||
}
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
notionQuery,
|
||||
notionReadPage,
|
||||
notionWritePage,
|
||||
notionUpdatePage,
|
||||
notionWriteSyslog
|
||||
};
|
||||
|
|
@ -13,7 +13,8 @@
|
|||
"express": "^4.21.0",
|
||||
"pg": "^8.13.0",
|
||||
"node-cron": "^3.0.3",
|
||||
"uuid": "^11.1.0"
|
||||
"uuid": "^11.1.0",
|
||||
"@notionhq/client": "^2.2.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
|
|
|||
|
|
@ -349,6 +349,113 @@ app.get('/api/cos/load-works', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// MCP 网关 · 3800 → 3100 转发 (S7)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
const http = require('http');
|
||||
const MCP_HOST = process.env.MCP_HOST || '127.0.0.1';
|
||||
const MCP_PORT_GATEWAY = process.env.MCP_PORT || '3100';
|
||||
|
||||
/**
|
||||
* MCP 内部代理请求
|
||||
*/
|
||||
function mcpProxy(method, path, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : null;
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
if (bodyStr) headers['Content-Length'] = Buffer.byteLength(bodyStr);
|
||||
|
||||
const req = http.request({
|
||||
hostname: MCP_HOST,
|
||||
port: parseInt(MCP_PORT_GATEWAY, 10),
|
||||
path,
|
||||
method,
|
||||
headers,
|
||||
timeout: 60000
|
||||
}, (res) => {
|
||||
const chunks = [];
|
||||
res.on('data', c => chunks.push(c));
|
||||
res.on('end', () => {
|
||||
const raw = Buffer.concat(chunks).toString();
|
||||
try {
|
||||
resolve({ statusCode: res.statusCode, data: JSON.parse(raw) });
|
||||
} catch {
|
||||
resolve({ statusCode: res.statusCode, data: raw });
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.on('timeout', () => { req.destroy(); reject(new Error('MCP proxy timeout')); });
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ─── MCP 工具列表 ───
|
||||
app.get('/api/mcp/tools', async (_req, res) => {
|
||||
try {
|
||||
const result = await mcpProxy('GET', '/tools');
|
||||
res.json(result.data);
|
||||
} catch (err) {
|
||||
res.status(502).json({ error: true, message: `MCP Server 不可达: ${err.message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── MCP 健康检查(含 Notion/GitHub 连接状态) ───
|
||||
app.get('/api/mcp/health', async (_req, res) => {
|
||||
try {
|
||||
const result = await mcpProxy('GET', '/health');
|
||||
res.json(result.data);
|
||||
} catch (err) {
|
||||
res.status(502).json({ error: true, message: `MCP Server 不可达: ${err.message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── MCP 统一工具调用(网关入口) ───
|
||||
// 安全: 写操作工具需要 caller 身份标识
|
||||
const MCP_WRITE_TOOLS = new Set([
|
||||
'createNode', 'updateNode', 'deleteNode',
|
||||
'linkNodes', 'unlinkNodes',
|
||||
'cosWrite', 'cosDelete', 'cosArchive',
|
||||
'notionWritePage', 'notionUpdatePage', 'notionWriteSyslog',
|
||||
'githubWriteFile', 'githubTriggerDeploy'
|
||||
]);
|
||||
|
||||
app.post('/api/mcp/call', async (req, res) => {
|
||||
const { tool, input, caller } = req.body;
|
||||
|
||||
if (!tool) {
|
||||
return res.status(400).json({ error: true, code: 'MISSING_TOOL', message: '缺少 tool 参数' });
|
||||
}
|
||||
|
||||
// 写操作工具需要 caller 身份标识
|
||||
if (MCP_WRITE_TOOLS.has(tool) && !caller) {
|
||||
return res.status(403).json({
|
||||
error: true,
|
||||
code: 'WRITE_REQUIRES_CALLER',
|
||||
message: '写操作工具需要提供 caller 身份标识'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await mcpProxy('POST', '/call', { tool, input, caller: caller || 'web-gateway' });
|
||||
res.status(result.statusCode).json(result.data);
|
||||
} catch (err) {
|
||||
res.status(502).json({ error: true, message: `MCP Server 不可达: ${err.message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── MCP Agent 查询 ───
|
||||
app.get('/api/mcp/agents', async (_req, res) => {
|
||||
try {
|
||||
const result = await mcpProxy('GET', '/agents');
|
||||
res.json(result.data);
|
||||
} catch (err) {
|
||||
res.status(502).json({ error: true, message: `MCP Server 不可达: ${err.message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 智能模型分流 · Smart Model Router API
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
|
@ -698,6 +805,10 @@ app.get('/', (_req, res) => {
|
|||
chat_stats: '/api/chat/stats',
|
||||
cos_status: '/api/cos/status',
|
||||
cos_config: '/api/cos/config',
|
||||
mcp_tools: '/api/mcp/tools',
|
||||
mcp_health: '/api/mcp/health',
|
||||
mcp_call: 'POST /api/mcp/call',
|
||||
mcp_agents: '/api/mcp/agents',
|
||||
model_stats: '/api/model/stats',
|
||||
model_pricing: '/api/model/pricing',
|
||||
model_predict: 'POST /api/model/predict',
|
||||
|
|
|
|||
Loading…
Reference in New Issue