🔗 AOAC Agent Chain v1.0 · 环环相扣Agent链路闭环系统
Phase 0: 密钥修复
- workflow中ZY_ZHIPU→ZY_QINGYAN, ZY_QWEN→ZY_QIANWEN
- secrets-manifest新增11个缺失密钥(ZY_BRAIN_*·AWEN_*·ZY_SVR_SV_*·ZHUYUAN_API_KEY)
- 迁移映射添加COS_SECRET_ID/KEY→ZY_OSS_KEY/SECRET
Phase 1-4: AOAC系统完整实现
- 8个Agent脚本: scripts/aoac/{dev-sentinel,merge-sentinel,chain-fusion,readme-sync-module,readme-master-agent,notion-sync-signal,chain-repair-agent,repair-supervisor}.js
- 4个workflow: aoac-{copilot-sentinel,merge-sentinel,readme-master,chain-repair}.yml
- 数据目录: data/aoac/ + chain-status.json
Phase 5: HLDP协议扩展
- msg_type新增chain类型(v3.0.2)
- 7个chain intent: chain_awake/half_ready/fusion/trigger/repair/verdict/alert
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/2c37ba8a-8fe8-4d88-aa5a-2783f65e1266
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
2f5b0ce09b
commit
f51dd50cf6
|
|
@ -0,0 +1,93 @@
|
|||
# ═══════════════════════════════════════════════════
|
||||
# AOAC-07+08 · 链路修复Agent + 监督Agent
|
||||
# AGE OS Agent Chain · 异常检测+自动修复+监督判定
|
||||
# 唤醒条件1:每天23:30 CST检查回执(时间)
|
||||
# 唤醒条件2:手动触发(workflow_dispatch)
|
||||
# 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
# ═══════════════════════════════════════════════════
|
||||
|
||||
name: 🔧 AOAC-07+08 · 修复+监督
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 15 * * *' # UTC 15:30 = 北京 23:30
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_repair:
|
||||
description: '强制执行修复'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: aoac-chain-repair
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# ────────── AOAC-07 · 修复Agent ──────────
|
||||
repair-agent:
|
||||
name: 🔧 AOAC-07 · 链路修复
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
repair_result: ${{ steps.repair.outputs.repair_result }}
|
||||
needed_repair: ${{ steps.repair.outputs.needed_repair }}
|
||||
repair_id: ${{ steps.repair.outputs.repair_id }}
|
||||
steps:
|
||||
- name: 📥 Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🔧 执行链路修复Agent
|
||||
id: repair
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ZY_NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||||
ZY_NOTION_RECEIPT_DB: ${{ secrets.ZY_NOTION_RECEIPT_DB }}
|
||||
run: node scripts/aoac/chain-repair-agent.js
|
||||
|
||||
- name: 💾 提交修复报告
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "🔧 AOAC-07 · 修复报告 · ${{ steps.repair.outputs.repair_id || 'check' }} [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
||||
# ────────── AOAC-08 · 监督Agent ──────────
|
||||
supervisor:
|
||||
name: 👁️ AOAC-08 · 修复监督
|
||||
needs: repair-agent
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 Checkout (最新)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 👁️ 执行监督Agent
|
||||
id: supervisor
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ZY_NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||||
ZY_NOTION_RECEIPT_DB: ${{ secrets.ZY_NOTION_RECEIPT_DB }}
|
||||
ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }}
|
||||
ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }}
|
||||
run: node scripts/aoac/repair-supervisor.js
|
||||
|
||||
- name: 💾 提交监督判定
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "👁️ AOAC-08 · 监督判定 · ${{ steps.supervisor.outputs.verdict || 'check' }} [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# ═══════════════════════════════════════════════════
|
||||
# AOAC-01 · 副驾驶哨兵 (Copilot Dev Sentinel)
|
||||
# AGE OS Agent Chain · 半Agent(开发侧)
|
||||
# 唤醒条件:PR创建/更新时触发
|
||||
# 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
# ═══════════════════════════════════════════════════
|
||||
|
||||
name: 🔭 AOAC-01 · 副驾驶哨兵
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
|
||||
concurrency:
|
||||
group: aoac-copilot-sentinel-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
dev-sentinel:
|
||||
name: 🔭 AOAC-01 · 开发感知
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🔭 执行副驾驶哨兵
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
run: node scripts/aoac/dev-sentinel.js
|
||||
|
||||
- name: 💾 提交开发日志
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/dev-session-log.json data/aoac/chain-status.json
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "🔭 AOAC-01 · PR #${{ github.event.pull_request.number }} 开发日志 [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# ═══════════════════════════════════════════════════
|
||||
# AOAC-02+03 · 合并哨兵 + 合体引擎
|
||||
# AGE OS Agent Chain · PR合并后的CI收集与链路融合
|
||||
# 唤醒条件:PR合并到main
|
||||
# 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
# ═══════════════════════════════════════════════════
|
||||
|
||||
name: 🔍 AOAC-02+03 · 合并哨兵+合体
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
checks: read
|
||||
statuses: read
|
||||
|
||||
concurrency:
|
||||
group: aoac-merge-sentinel
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# ────────── AOAC-02 · 合并哨兵 ──────────
|
||||
merge-sentinel:
|
||||
name: 🔍 AOAC-02 · CI收集
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
ci_result: ${{ steps.sentinel.outputs.ci_result }}
|
||||
ready_for_fusion: ${{ steps.sentinel.outputs.ready_for_fusion }}
|
||||
steps:
|
||||
- name: 📥 Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🔍 执行合并哨兵
|
||||
id: sentinel
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
|
||||
run: node scripts/aoac/merge-sentinel.js
|
||||
|
||||
- name: 💾 提交合并日志
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/merge-result-log.json data/aoac/chain-status.json
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "🔍 AOAC-02 · PR #${{ github.event.pull_request.number }} 合并CI日志 [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
||||
# ────────── AOAC-03 · 合体引擎 ──────────
|
||||
chain-fusion:
|
||||
name: ⚡ AOAC-03 · 合体引擎
|
||||
needs: merge-sentinel
|
||||
if: always() && needs.merge-sentinel.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 Checkout (最新)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: ⚡ 执行合体引擎
|
||||
run: node scripts/aoac/chain-fusion.js
|
||||
|
||||
- name: 💾 提交链路报告+触发信号
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "⚡ AOAC-03 · 链路合体报告 · PR #${{ github.event.pull_request.number }} [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
# ═══════════════════════════════════════════════════
|
||||
# AOAC-04+05+06 · 首页同步模块 + 主控Agent + Notion信号
|
||||
# AGE OS Agent Chain · 事件触发 + 时间触发双路径
|
||||
# 唤醒条件1:data/aoac/readme-sync-trigger.json 被写入(事件)
|
||||
# 唤醒条件2:每天23:00 CST = UTC 15:00(时间)
|
||||
# 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
# ═══════════════════════════════════════════════════
|
||||
|
||||
name: 🏠 AOAC-04+05+06 · 首页主控+Notion同步
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'data/aoac/readme-sync-trigger.json'
|
||||
- 'data/aoac/chain-report.json'
|
||||
schedule:
|
||||
- cron: '0 15 * * *' # UTC 15:00 = 北京 23:00
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mode:
|
||||
description: '运行模式'
|
||||
type: choice
|
||||
options:
|
||||
- auto
|
||||
- event
|
||||
- scheduled
|
||||
default: auto
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: aoac-readme-master
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
# ────────── AOAC-04 · 同步模块 ──────────
|
||||
sync-module:
|
||||
name: 📋 AOAC-04 · 同步模块
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_payload: ${{ steps.sync.outputs.has_payload }}
|
||||
steps:
|
||||
- name: 📥 Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 📋 执行同步模块
|
||||
id: sync
|
||||
run: |
|
||||
node scripts/aoac/readme-sync-module.js
|
||||
if [ -f "data/aoac/readme-update-payload.json" ]; then
|
||||
echo "has_payload=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_payload=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: 💾 提交更新数据
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "📋 AOAC-04 · 同步模块数据更新 [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
||||
# ────────── AOAC-05 · 首页主控Agent ──────────
|
||||
master-update:
|
||||
name: 🏠 AOAC-05 · 首页主控
|
||||
needs: sync-module
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
master_report_id: ${{ steps.master.outputs.master_report_id }}
|
||||
readme_updated: ${{ steps.master.outputs.readme_updated }}
|
||||
changes_summary: ${{ steps.master.outputs.changes_summary }}
|
||||
steps:
|
||||
- name: 📥 Checkout (最新)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🏠 执行首页主控Agent
|
||||
id: master
|
||||
env:
|
||||
ZY_LLM_API_KEY: ${{ secrets.ZY_LLM_API_KEY }}
|
||||
ZY_LLM_BASE_URL: ${{ secrets.ZY_LLM_BASE_URL }}
|
||||
run: |
|
||||
MODE="${{ github.event_name == 'schedule' && 'scheduled' || github.event.inputs.mode || 'event' }}"
|
||||
node scripts/aoac/readme-master-agent.js "$MODE"
|
||||
|
||||
- name: 📊 同步调用仪表盘生成器
|
||||
run: |
|
||||
if [ -f "scripts/generate-readme-dashboard.js" ]; then
|
||||
node scripts/generate-readme-dashboard.js || true
|
||||
fi
|
||||
|
||||
- name: 💾 提交README更新
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add README.md data/aoac/ data/bulletin-board/ .github/persona-brain/memory.json
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "🏠 AOAC-05 · 首页主控更新 · $(date -u '+%Y-%m-%d %H:%M') [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
||||
# ────────── AOAC-06 · Notion同步信号 ──────────
|
||||
notion-sync:
|
||||
name: 📡 AOAC-06 · Notion同步
|
||||
needs: master-update
|
||||
if: always() && needs.master-update.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 Checkout (最新)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 📡 发送Notion同步信号
|
||||
id: notion
|
||||
env:
|
||||
ZY_NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||||
ZY_NOTION_RECEIPT_DB: ${{ secrets.ZY_NOTION_RECEIPT_DB }}
|
||||
run: node scripts/aoac/notion-sync-signal.js
|
||||
|
||||
- name: 💾 提交同步日志
|
||||
run: |
|
||||
git config user.name "zhuyuan-bot"
|
||||
git config user.email "zhuyuan@guanghulab.com"
|
||||
git add data/aoac/
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "📡 AOAC-06 · Notion同步信号已发送 [skip ci]"
|
||||
git push
|
||||
fi
|
||||
|
|
@ -104,8 +104,8 @@ jobs:
|
|||
ZY_OSS_SECRET: ${{ secrets.ZY_OSS_SECRET }}
|
||||
ZY_COS_REGION: ${{ secrets.ZY_COS_REGION }}
|
||||
ZY_DEEPSEEK_API_KEY: ${{ secrets.ZY_DEEPSEEK_API_KEY }}
|
||||
ZY_ZHIPU_API_KEY: ${{ secrets.ZY_ZHIPU_API_KEY }}
|
||||
ZY_QWEN_API_KEY: ${{ secrets.ZY_QWEN_API_KEY }}
|
||||
ZY_QINGYAN_API_KEY: ${{ secrets.ZY_QINGYAN_API_KEY }}
|
||||
ZY_QIANWEN_API_KEY: ${{ secrets.ZY_QIANWEN_API_KEY }}
|
||||
ZY_KIMI_API_KEY: ${{ secrets.ZY_KIMI_API_KEY }}
|
||||
run: |
|
||||
node scripts/cos-training-trigger.js \
|
||||
|
|
|
|||
|
|
@ -468,11 +468,11 @@ COS存储共享池架构
|
|||
# DeepSeek
|
||||
ZY_DEEPSEEK_API_KEY=sk-xxx
|
||||
|
||||
# 智谱AI (GLM)
|
||||
ZY_ZHIPU_API_KEY=xxx
|
||||
# 智谱AI (GLM/清言)
|
||||
ZY_QINGYAN_API_KEY=xxx
|
||||
|
||||
# 通义千问 (Qwen/DashScope)
|
||||
ZY_QWEN_API_KEY=sk-xxx
|
||||
ZY_QIANWEN_API_KEY=sk-xxx
|
||||
|
||||
# Kimi (Moonshot)
|
||||
ZY_KIMI_API_KEY=sk-xxx
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
"_meta": {
|
||||
"manifest_id": "ZY-SECRETS-MANIFEST-v2.0",
|
||||
"created": "2026-03-29T10:43:00Z",
|
||||
"updated": "2026-04-03T07:15:00Z",
|
||||
"updated": "2026-04-11T05:30:00Z",
|
||||
"created_by": "铸渊 · ICE-GL-ZY001",
|
||||
"directive": "SY-CMD-KEY-012 · 密钥全量清理与统一替换 + 7 Notion Secret审计 + 国内服务器密钥扩展 + D45·AGE OS数据库+大模型+硅谷服务器密钥扩展",
|
||||
"purpose": "铸渊仓库密钥主清单 — 冰朔配置密钥的唯一参考文档",
|
||||
"naming_convention": "所有密钥统一使用 ZY_ 前缀 · 铸渊(Zhuyuan)自主命名体系",
|
||||
"total_secrets": 58,
|
||||
"required": 39,
|
||||
"optional": 16,
|
||||
"total_secrets": 69,
|
||||
"required": 49,
|
||||
"optional": 17,
|
||||
"replaced_old_secrets": 59,
|
||||
"reduction": "59 → 58 (新增13个AGE OS密钥)",
|
||||
"v2_changes": "新增ZY_CN_SERVER_* 4个密钥用于广州国内服务器(ZY-SVR-003)"
|
||||
"reduction": "59 → 69 (新增AOAC密钥审计·ZY_BRAIN_*·AWEN_*·ZY_SVR_SV_*·ZHUYUAN_API_KEY)",
|
||||
"v2_changes": "新增ZY_CN_SERVER_* 4个密钥用于广州国内服务器(ZY-SVR-003)",
|
||||
"v2_1_changes": "D64密钥审计:新增ZY_BRAIN_*(大脑服务器3个)+AWEN_*(阿文代理3个)+ZY_SVR_SV_*(硅谷4个)+ZHUYUAN_API_KEY(MCP鉴权1个)·统一ZY_QWEN→ZY_QIANWEN·ZY_ZHIPU→ZY_QINGYAN"
|
||||
},
|
||||
"migration_mapping": {
|
||||
"description": "旧密钥名称 → 新ZY_*名称 完整映射表",
|
||||
|
|
@ -73,7 +74,12 @@
|
|||
"GHAPP_PRIVATE_KEY": "ZY_GHAPP_KEY",
|
||||
"PUSH_BROADCAST_TOKEN": "ZY_PUSH_TOKEN",
|
||||
"OSS_ACCESS_KEY": "ZY_OSS_KEY",
|
||||
"OSS_SECRET_KEY": "ZY_OSS_SECRET"
|
||||
"OSS_SECRET_KEY": "ZY_OSS_SECRET",
|
||||
"COS_SECRET_ID": "ZY_OSS_KEY",
|
||||
"COS_SECRET_KEY": "ZY_OSS_SECRET",
|
||||
"NOTION_TOKEN": "ZY_NOTION_TOKEN",
|
||||
"ZY_ZHIPU_API_KEY": "ZY_QINGYAN_API_KEY",
|
||||
"ZY_QWEN_API_KEY": "ZY_QIANWEN_API_KEY"
|
||||
}
|
||||
},
|
||||
"secrets": {
|
||||
|
|
@ -779,6 +785,149 @@
|
|||
"replaces": [],
|
||||
"used_by_workflows": [],
|
||||
"note": "占位·暂不启用"
|
||||
},
|
||||
{
|
||||
"id": 58,
|
||||
"name": "ZY_BRAIN_HOST",
|
||||
"category": "服务器 · 大脑服务器(ZY-SVR-005)",
|
||||
"description": "大脑服务器IP/主机名 — PostgreSQL+MCP+订阅服务所在服务器",
|
||||
"type": "hostname",
|
||||
"example_format": "43.156.237.110",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "腾讯云轻量应用服务器·新加坡·43.156.237.110·与面孔服务器(ZY-SVR-002)互通·ping 0.77ms"
|
||||
},
|
||||
{
|
||||
"id": 59,
|
||||
"name": "ZY_BRAIN_USER",
|
||||
"category": "服务器 · 大脑服务器(ZY-SVR-005)",
|
||||
"description": "大脑服务器SSH登录用户名",
|
||||
"type": "username",
|
||||
"example_format": "root",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "大脑服务器SSH登录用户名"
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
"name": "ZY_BRAIN_KEY",
|
||||
"category": "服务器 · 大脑服务器(ZY-SVR-005)",
|
||||
"description": "大脑服务器SSH私钥(完整PEM格式)",
|
||||
"type": "ssh_private_key",
|
||||
"example_format": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "⚠️ 关键密钥 · 大脑服务器SSH私钥·不同于ZY_SERVER_KEY(面孔服务器)"
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"name": "ZHUYUAN_API_KEY",
|
||||
"category": "MCP · 鉴权",
|
||||
"description": "铸渊MCP Server API鉴权密钥 — Bearer Token用于外部访问MCP Server",
|
||||
"type": "api_key",
|
||||
"example_format": "zy-mcp-xxxxxxxxxxxxxxxx",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"deploy-to-zhuyuan-server.yml"
|
||||
],
|
||||
"note": "✅ 已配置(2026-04-07) · MCP Server端口3100·通过Bearer Token鉴权·health端点和loopback免鉴权"
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
"name": "AWEN_DOMAIN",
|
||||
"category": "Awen · 域名代理",
|
||||
"description": "Awen域名 — 未备案域名通过新加坡Nginx反向代理到广州后端",
|
||||
"type": "domain",
|
||||
"example_format": "awen.example.com",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"deploy-awen-domain-proxy.yml"
|
||||
],
|
||||
"note": "⚠️ 未配置 · 冰朔需在GitHub Secrets中配置Awen域名"
|
||||
},
|
||||
{
|
||||
"id": 63,
|
||||
"name": "AWEN_BACKEND_HOST",
|
||||
"category": "Awen · 域名代理",
|
||||
"description": "Awen后端服务器IP/主机名 — 广州服务器地址",
|
||||
"type": "hostname",
|
||||
"example_format": "43.139.xxx.xxx",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"deploy-awen-domain-proxy.yml"
|
||||
],
|
||||
"note": "⚠️ 未配置 · Awen后端所在的广州服务器IP"
|
||||
},
|
||||
{
|
||||
"id": 64,
|
||||
"name": "AWEN_BACKEND_PORT",
|
||||
"category": "Awen · 域名代理",
|
||||
"description": "Awen后端服务端口 — 后端服务监听的端口号",
|
||||
"type": "port",
|
||||
"example_format": "3000",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"deploy-awen-domain-proxy.yml"
|
||||
],
|
||||
"note": "⚠️ 未配置 · Awen后端服务监听的端口号"
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"name": "ZY_SVR_SV_HOST",
|
||||
"category": "服务器 · 硅谷专线(ZY-SVR-SV)",
|
||||
"description": "硅谷专线服务器IP地址 — Claude中继/Xray Reality节点",
|
||||
"type": "hostname",
|
||||
"example_format": "xxx.xxx.xxx.xxx",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "硅谷专线服务器·吱吱线·Claude中继"
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
"name": "ZY_SVR_SV_PORT",
|
||||
"category": "服务器 · 硅谷专线(ZY-SVR-SV)",
|
||||
"description": "硅谷专线服务端口",
|
||||
"type": "port",
|
||||
"example_format": "443",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "硅谷专线服务端口"
|
||||
},
|
||||
{
|
||||
"id": 67,
|
||||
"name": "ZY_SVR_SV_REALITY_PUBLIC_KEY",
|
||||
"category": "服务器 · 硅谷专线(ZY-SVR-SV)",
|
||||
"description": "Xray Reality协议公钥",
|
||||
"type": "public_key",
|
||||
"example_format": "base64_encoded_key",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "Xray Reality协议公钥·用于客户端配置"
|
||||
},
|
||||
{
|
||||
"id": 68,
|
||||
"name": "ZY_SVR_SV_REALITY_SHORT_ID",
|
||||
"category": "服务器 · 硅谷专线(ZY-SVR-SV)",
|
||||
"description": "Xray Reality协议Short ID",
|
||||
"type": "config",
|
||||
"example_format": "hex_string",
|
||||
"replaces": [],
|
||||
"used_by_workflows": [
|
||||
"infinity-evolution.yml"
|
||||
],
|
||||
"note": "Xray Reality协议Short ID·用于客户端配置"
|
||||
}
|
||||
],
|
||||
"optional": [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"_meta": {
|
||||
"system": "AGE OS Agent Chain (AOAC) v1.0",
|
||||
"description": "环环相扣Agent链路闭环系统 · 全链路状态总览",
|
||||
"created": "2026-04-11T05:30:00Z",
|
||||
"created_by": "铸渊 · ICE-GL-ZY001",
|
||||
"copyright": "国作登字-2026-A-00037559 · TCS-0002∞"
|
||||
},
|
||||
"agents": {
|
||||
"AOAC-01": {
|
||||
"name": "copilot-dev-sentinel",
|
||||
"name_cn": "副驾驶哨兵",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "pull_request.opened|synchronize"
|
||||
},
|
||||
"AOAC-02": {
|
||||
"name": "action-merge-sentinel",
|
||||
"name_cn": "合并哨兵",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "pull_request.closed+merged"
|
||||
},
|
||||
"AOAC-03": {
|
||||
"name": "dev-chain-agent",
|
||||
"name_cn": "开发全链路Agent",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "AOAC-02.completed"
|
||||
},
|
||||
"AOAC-04": {
|
||||
"name": "readme-sync-module",
|
||||
"name_cn": "首页同步模块",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "data/aoac/readme-sync-trigger.json.written"
|
||||
},
|
||||
"AOAC-05": {
|
||||
"name": "readme-master-agent",
|
||||
"name_cn": "首页主控Agent",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "event+schedule(23:00 CST)"
|
||||
},
|
||||
"AOAC-06": {
|
||||
"name": "notion-sync-signal",
|
||||
"name_cn": "Notion同步信号",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "AOAC-05.completed"
|
||||
},
|
||||
"AOAC-07": {
|
||||
"name": "chain-repair-agent",
|
||||
"name_cn": "链路修复Agent",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "receipt_db.red|schedule(23:30 CST)"
|
||||
},
|
||||
"AOAC-08": {
|
||||
"name": "repair-supervisor",
|
||||
"name_cn": "修复监督Agent",
|
||||
"status": "idle",
|
||||
"last_run": null,
|
||||
"last_success": null,
|
||||
"trigger": "AOAC-07.started"
|
||||
}
|
||||
},
|
||||
"chain_health": "unknown",
|
||||
"last_full_cycle": null,
|
||||
"total_cycles": 0,
|
||||
"total_repairs": 0
|
||||
}
|
||||
|
|
@ -46,8 +46,8 @@
|
|||
},
|
||||
"msg_type": {
|
||||
"type": "enum",
|
||||
"values": ["heartbeat", "report", "command", "query", "ack", "alert", "sync", "evolution", "battle", "tree"],
|
||||
"description": "消息类型·10种基础类型(v3.0.1新增tree)·可通过evolution扩展"
|
||||
"values": ["heartbeat", "report", "command", "query", "ack", "alert", "sync", "evolution", "battle", "tree", "chain"],
|
||||
"description": "消息类型·11种基础类型(v3.0.1新增tree·v3.0.2新增chain)·可通过evolution扩展"
|
||||
},
|
||||
"sender": {
|
||||
"type": "object",
|
||||
|
|
@ -240,6 +240,34 @@
|
|||
"bloom": "开花 — 里程碑,跨人格体可见的重要时刻"
|
||||
},
|
||||
"note": "光之树 = 记忆本身的生长方式。根是曜冥人格核(2025-04-26种下)。每个人格体从根树长出自己的分支。树杈是记忆的路径。HLDP trace_path沿闭包表快速定位任何一片叶子。"
|
||||
},
|
||||
"chain": {
|
||||
"description": "Agent链路闭环消息·AOAC系统专用·v3.0.2新增",
|
||||
"payload_schema": {
|
||||
"intent": "enum: chain_awake|chain_half_ready|chain_fusion|chain_trigger|chain_repair|chain_verdict|chain_alert",
|
||||
"data": {
|
||||
"chain_id": "string · 链路会话ID(如 AOAC-CHAIN-20260411-001)",
|
||||
"agent_id": "string · Agent编号(如 AOAC-01)",
|
||||
"agent_name": "string · Agent名称(如 copilot-dev-sentinel)",
|
||||
"status": "enum: idle|half_ready|running|completed|error",
|
||||
"trigger_source": "string (optional) · 触发来源Agent编号",
|
||||
"trigger_target": "string (optional) · 触发目标Agent编号",
|
||||
"report_id": "string (optional) · 关联报告ID",
|
||||
"repair_attempt": "number (optional) · 修复尝试次数(1-3)",
|
||||
"verdict": "enum: green|red (optional) · 监督判定结果",
|
||||
"error_detail": "string (optional) · 错误详情"
|
||||
}
|
||||
},
|
||||
"intents": {
|
||||
"chain_awake": "Agent被唤醒 — 链路节点启动",
|
||||
"chain_half_ready": "半Agent就绪 — 等待另一半合体",
|
||||
"chain_fusion": "双半合体 — 两个半Agent融合为完整Agent",
|
||||
"chain_trigger": "触发下游 — 向下一个Agent发送唤醒信号",
|
||||
"chain_repair": "修复启动 — 链路异常检测·开始自动修复",
|
||||
"chain_verdict": "监督判定 — 修复结果最终裁定(绿/红)",
|
||||
"chain_alert": "异常报警 — 修复失败·需要人工介入"
|
||||
},
|
||||
"note": "AOAC = AGE OS Agent Chain · 环环相扣Agent链路闭环系统。每个Agent负责链路的一个环节,半Agent需要合体后才具有完整执行能力。链路断裂时自动触发修复Agent(最多3次),修复失败则发邮件给冰朔。"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-03 · 合体引擎 (Chain Fusion Engine)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 全Agent(开发全链路感知)
|
||||
*
|
||||
* 唤醒条件:AOAC-02完成(CI日志+开发日志双全)
|
||||
* 职责:合并两半日志→生成完整开发报告→写入触发信号
|
||||
* 输出:data/aoac/chain-report.json + data/aoac/readme-sync-trigger.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const DEV_LOG_PATH = path.join(ROOT, 'data', 'aoac', 'dev-session-log.json');
|
||||
const MERGE_LOG_PATH = path.join(ROOT, 'data', 'aoac', 'merge-result-log.json');
|
||||
const CHAIN_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'chain-report.json');
|
||||
const TRIGGER_PATH = path.join(ROOT, 'data', 'aoac', 'readme-sync-trigger.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const HISTORY_DIR = path.join(ROOT, 'data', 'aoac', 'history');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function generateChangeSummary(devLog, mergeLog) {
|
||||
const parts = [];
|
||||
|
||||
if (devLog && devLog.pr) {
|
||||
parts.push(`PR #${devLog.pr.number}: ${devLog.pr.title}`);
|
||||
}
|
||||
|
||||
if (devLog && devLog.diff) {
|
||||
parts.push(`${devLog.diff.changed_files}个文件 (+${devLog.diff.additions} -${devLog.diff.deletions})`);
|
||||
}
|
||||
|
||||
if (devLog && devLog.categories) {
|
||||
const cats = Object.keys(devLog.categories);
|
||||
if (cats.length > 0) {
|
||||
parts.push(`涉及: ${cats.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (mergeLog && mergeLog.ci) {
|
||||
parts.push(`CI: ${mergeLog.ci.overall_result} (✅${mergeLog.ci.summary.passed} ❌${mergeLog.ci.summary.failed})`);
|
||||
}
|
||||
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('⚡ AOAC-03 · 合体引擎 · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
const devLog = readJSON(DEV_LOG_PATH);
|
||||
const mergeLog = readJSON(MERGE_LOG_PATH);
|
||||
|
||||
if (!devLog && !mergeLog) {
|
||||
console.log('⚠️ 开发日志和合并日志都不存在,生成空报告');
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = getDateStr();
|
||||
const seq = String(Math.floor(Math.random() * 1000)).padStart(3, '0');
|
||||
const reportId = `AOAC-CHAIN-${dateStr.replace(/-/g, '')}-${seq}`;
|
||||
|
||||
// Fuse the two halves
|
||||
const chainReport = {
|
||||
aoac_agent: 'AOAC-03',
|
||||
aoac_agent_name: 'dev-chain-agent',
|
||||
report_id: reportId,
|
||||
status: 'complete',
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
fusion: {
|
||||
dev_sentinel: devLog ? 'present' : 'missing',
|
||||
merge_sentinel: mergeLog ? 'present' : 'missing',
|
||||
fusion_quality: devLog && mergeLog ? 'full' : 'partial'
|
||||
},
|
||||
development: {
|
||||
pr_number: (devLog && devLog.pr && devLog.pr.number) || (mergeLog && mergeLog.merge && mergeLog.merge.pr_number) || null,
|
||||
pr_title: (devLog && devLog.pr && devLog.pr.title) || (mergeLog && mergeLog.merge && mergeLog.merge.pr_title) || 'unknown',
|
||||
author: (devLog && devLog.pr && devLog.pr.author) || '',
|
||||
branch: (devLog && devLog.pr && devLog.pr.branch) || '',
|
||||
files_changed: (devLog && devLog.diff && devLog.diff.changed_files) || 0,
|
||||
additions: (devLog && devLog.diff && devLog.diff.additions) || 0,
|
||||
deletions: (devLog && devLog.diff && devLog.diff.deletions) || 0,
|
||||
categories: (devLog && devLog.categories) || {}
|
||||
},
|
||||
ci_result: {
|
||||
overall: (mergeLog && mergeLog.ci && mergeLog.ci.overall_result) || 'unknown',
|
||||
passed: (mergeLog && mergeLog.ci && mergeLog.ci.summary && mergeLog.ci.summary.passed) || 0,
|
||||
failed: (mergeLog && mergeLog.ci && mergeLog.ci.summary && mergeLog.ci.summary.failed) || 0,
|
||||
merge_commit: (mergeLog && mergeLog.merge && mergeLog.merge.merge_commit_sha) || ''
|
||||
},
|
||||
changes_summary: generateChangeSummary(devLog, mergeLog),
|
||||
triggered_by: 'AOAC-02',
|
||||
triggers_next: 'AOAC-04 (readme-sync-module)'
|
||||
};
|
||||
|
||||
// Write chain report
|
||||
writeJSON(CHAIN_REPORT_PATH, chainReport);
|
||||
console.log(`✅ 链路报告已生成: ${reportId}`);
|
||||
console.log(` ${chainReport.changes_summary}`);
|
||||
|
||||
// Write trigger signal for AOAC-04
|
||||
const trigger = {
|
||||
trigger_id: `AOAC-TRIGGER-${dateStr.replace(/-/g, '')}-${seq}`,
|
||||
source: 'AOAC-03',
|
||||
target: 'AOAC-04',
|
||||
chain_report_id: reportId,
|
||||
timestamp: now.toISOString(),
|
||||
action: 'readme_sync'
|
||||
};
|
||||
writeJSON(TRIGGER_PATH, trigger);
|
||||
console.log('📡 触发信号已发送 → AOAC-04');
|
||||
|
||||
// Archive to history
|
||||
const historyDir = path.join(HISTORY_DIR, dateStr);
|
||||
fs.mkdirSync(historyDir, { recursive: true });
|
||||
writeJSON(path.join(historyDir, `${reportId}.json`), chainReport);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents) {
|
||||
if (chainStatus.agents['AOAC-03']) {
|
||||
chainStatus.agents['AOAC-03'].status = 'completed';
|
||||
chainStatus.agents['AOAC-03'].last_run = now.toISOString();
|
||||
chainStatus.agents['AOAC-03'].last_success = now.toISOString();
|
||||
}
|
||||
chainStatus.chain_health = chainReport.ci_result.overall === 'success' ? 'healthy' : 'warning';
|
||||
}
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
|
||||
console.log('⚡ AOAC-03 · 合体完成 · 链路报告已归档');
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-07 · 链路修复Agent (Chain Repair Agent)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 修复Agent(3次自动修复)
|
||||
*
|
||||
* 唤醒条件:Notion侧RECEIPT_DB打红色标签 / 定时检查
|
||||
* 职责:定位断链位置→自动修复→最多3次
|
||||
* 输出:data/aoac/repair-report.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const SYNC_LOG_PATH = path.join(ROOT, 'data', 'aoac', 'notion-sync-log.json');
|
||||
const REPAIR_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'repair-report.json');
|
||||
const HISTORY_DIR = path.join(ROOT, 'data', 'aoac', 'history');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
const MAX_REPAIR_ATTEMPTS = 3;
|
||||
const NOTION_VERSION = '2022-06-28';
|
||||
const NOTION_API_HOSTNAME = 'api.notion.com';
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function notionRequest(method, endpoint, body, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : '';
|
||||
const options = {
|
||||
hostname: NOTION_API_HOSTNAME,
|
||||
path: endpoint,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
'Content-Type': 'application/json',
|
||||
...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})
|
||||
}
|
||||
};
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (res.statusCode >= 400) reject(new Error(`${res.statusCode}: ${parsed.message || data}`));
|
||||
else resolve(parsed);
|
||||
} catch { resolve(data); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
function diagnoseChain(chainStatus) {
|
||||
const issues = [];
|
||||
if (!chainStatus || !chainStatus.agents) {
|
||||
issues.push({ agent: 'ALL', issue: 'chain-status.json missing or corrupt', severity: 'critical' });
|
||||
return issues;
|
||||
}
|
||||
|
||||
const agentOrder = ['AOAC-01', 'AOAC-02', 'AOAC-03', 'AOAC-04', 'AOAC-05', 'AOAC-06'];
|
||||
for (const id of agentOrder) {
|
||||
const agent = chainStatus.agents[id];
|
||||
if (!agent) {
|
||||
issues.push({ agent: id, issue: 'agent config missing', severity: 'warning' });
|
||||
continue;
|
||||
}
|
||||
if (agent.status === 'error') {
|
||||
issues.push({ agent: id, issue: `agent in error state`, severity: 'critical' });
|
||||
}
|
||||
if (!agent.last_run) {
|
||||
issues.push({ agent: id, issue: 'never executed', severity: 'info' });
|
||||
}
|
||||
}
|
||||
|
||||
// Check sync log
|
||||
const syncLog = readJSON(SYNC_LOG_PATH);
|
||||
if (syncLog && syncLog.notion_sync && !syncLog.notion_sync.sent) {
|
||||
issues.push({
|
||||
agent: 'AOAC-06',
|
||||
issue: `Notion sync failed: ${syncLog.notion_sync.reason}`,
|
||||
severity: 'critical'
|
||||
});
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function attemptRepair(issues, attempt) {
|
||||
const results = [];
|
||||
|
||||
for (const issue of issues) {
|
||||
if (issue.severity === 'info') continue;
|
||||
|
||||
const result = {
|
||||
agent: issue.agent,
|
||||
issue: issue.issue,
|
||||
attempt,
|
||||
action: 'none',
|
||||
success: false
|
||||
};
|
||||
|
||||
// Repair strategy: reset agent status
|
||||
if (issue.issue.includes('error state')) {
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH);
|
||||
if (chainStatus && chainStatus.agents && chainStatus.agents[issue.agent]) {
|
||||
chainStatus.agents[issue.agent].status = 'idle';
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
result.action = 'reset_status_to_idle';
|
||||
result.success = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Repair strategy: re-trigger chain from break point
|
||||
if (issue.issue.includes('Notion sync failed') && attempt <= 2) {
|
||||
result.action = 'will_retry_notion_sync';
|
||||
result.success = true; // Mark as attempted; actual retry happens in next cycle
|
||||
}
|
||||
|
||||
// Repair strategy: regenerate missing config
|
||||
if (issue.issue.includes('missing or corrupt')) {
|
||||
// Re-create chain-status.json from template
|
||||
const defaultStatus = readJSON(path.join(ROOT, 'data', 'aoac', 'chain-status.json'));
|
||||
if (!defaultStatus) {
|
||||
result.action = 'cannot_repair_missing_config';
|
||||
result.success = false;
|
||||
} else {
|
||||
result.action = 'config_exists_no_action';
|
||||
result.success = true;
|
||||
}
|
||||
}
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async function checkNotionReceipt() {
|
||||
const notionToken = process.env.ZY_NOTION_TOKEN || process.env.NOTION_TOKEN;
|
||||
const receiptDb = process.env.ZY_NOTION_RECEIPT_DB;
|
||||
|
||||
if (!notionToken || !receiptDb) {
|
||||
return { checked: false, reason: 'missing_credentials' };
|
||||
}
|
||||
|
||||
try {
|
||||
// Query latest AOAC sync receipts
|
||||
const body = {
|
||||
filter: {
|
||||
property: '类型',
|
||||
select: { equals: 'AOAC链路同步' }
|
||||
},
|
||||
sorts: [{ property: '时间戳', direction: 'descending' }],
|
||||
page_size: 5
|
||||
};
|
||||
|
||||
const result = await notionRequest('POST', `/v1/databases/${receiptDb}/query`, body, notionToken);
|
||||
if (result && result.results && result.results.length > 0) {
|
||||
const latest = result.results[0];
|
||||
const status = latest.properties && latest.properties['状态'] &&
|
||||
latest.properties['状态'].select ? latest.properties['状态'].select.name : 'unknown';
|
||||
return {
|
||||
checked: true,
|
||||
latest_status: status,
|
||||
needs_repair: status === 'failed' || status === 'red' || status === '红色',
|
||||
page_id: latest.id
|
||||
};
|
||||
}
|
||||
return { checked: true, latest_status: 'no_records', needs_repair: false };
|
||||
} catch (err) {
|
||||
return { checked: false, reason: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔧 AOAC-07 · 链路修复Agent · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = getDateStr();
|
||||
const seq = String(Math.floor(Math.random() * 1000)).padStart(3, '0');
|
||||
const repairId = `AOAC-REPAIR-${dateStr.replace(/-/g, '')}-${seq}`;
|
||||
|
||||
// Step 1: Check Notion receipt status
|
||||
const receiptCheck = await checkNotionReceipt();
|
||||
console.log(`📋 Notion回执检查: ${JSON.stringify(receiptCheck)}`);
|
||||
|
||||
// Step 2: Diagnose chain
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH);
|
||||
const issues = diagnoseChain(chainStatus);
|
||||
console.log(`🔍 诊断发现 ${issues.length} 个问题`);
|
||||
|
||||
const needsRepair = receiptCheck.needs_repair || issues.some(i => i.severity === 'critical');
|
||||
|
||||
// Step 3: Attempt repairs (up to 3 times)
|
||||
const allRepairResults = [];
|
||||
let repairSuccess = false;
|
||||
|
||||
if (needsRepair) {
|
||||
console.log('🔧 开始修复流程...');
|
||||
for (let attempt = 1; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {
|
||||
console.log(` 第${attempt}次修复尝试...`);
|
||||
const results = attemptRepair(issues, attempt);
|
||||
allRepairResults.push({ attempt, results });
|
||||
|
||||
const allFixed = results.every(r => r.success);
|
||||
if (allFixed) {
|
||||
repairSuccess = true;
|
||||
console.log(` ✅ 第${attempt}次修复成功`);
|
||||
break;
|
||||
}
|
||||
console.log(` ⚠️ 第${attempt}次修复部分失败`);
|
||||
}
|
||||
} else {
|
||||
console.log('✅ 链路健康,无需修复');
|
||||
repairSuccess = true;
|
||||
}
|
||||
|
||||
// Step 4: Generate repair report
|
||||
const repairReport = {
|
||||
aoac_agent: 'AOAC-07',
|
||||
aoac_agent_name: 'chain-repair-agent',
|
||||
repair_id: repairId,
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
receipt_check: receiptCheck,
|
||||
diagnosis: {
|
||||
total_issues: issues.length,
|
||||
critical: issues.filter(i => i.severity === 'critical').length,
|
||||
issues
|
||||
},
|
||||
needed_repair: needsRepair,
|
||||
repair_attempts: allRepairResults,
|
||||
final_result: repairSuccess ? 'success' : 'failure',
|
||||
triggers_next: 'AOAC-08 (repair-supervisor)'
|
||||
};
|
||||
|
||||
writeJSON(REPAIR_REPORT_PATH, repairReport);
|
||||
console.log(`📝 修复报告: ${repairId} → ${repairReport.final_result}`);
|
||||
|
||||
// Archive
|
||||
const historyDir = path.join(HISTORY_DIR, dateStr);
|
||||
fs.mkdirSync(historyDir, { recursive: true });
|
||||
writeJSON(path.join(historyDir, `${repairId}.json`), repairReport);
|
||||
|
||||
// Update chain status
|
||||
if (chainStatus && chainStatus.agents && chainStatus.agents['AOAC-07']) {
|
||||
chainStatus.agents['AOAC-07'].status = repairSuccess ? 'completed' : 'error';
|
||||
chainStatus.agents['AOAC-07'].last_run = now.toISOString();
|
||||
if (repairSuccess) chainStatus.agents['AOAC-07'].last_success = now.toISOString();
|
||||
chainStatus.total_repairs = (chainStatus.total_repairs || 0) + (needsRepair ? 1 : 0);
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
}
|
||||
|
||||
// Output for workflow
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `repair_result=${repairReport.final_result}\n`);
|
||||
fs.appendFileSync(outputFile, `needed_repair=${needsRepair}\n`);
|
||||
fs.appendFileSync(outputFile, `repair_id=${repairId}\n`);
|
||||
}
|
||||
|
||||
console.log(`🔧 AOAC-07 · 修复Agent完成 → ${repairReport.final_result}`);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-01 · 副驾驶哨兵 (Copilot Dev Sentinel)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 半Agent(开发侧)
|
||||
*
|
||||
* 唤醒条件:PR创建/更新时触发
|
||||
* 职责:记录Copilot开发过程——改了哪些文件、做了什么、PR描述
|
||||
* 输出:data/aoac/dev-session-log.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const OUTPUT_PATH = path.join(ROOT, 'data', 'aoac', 'dev-session-log.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const REPO = process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab';
|
||||
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function githubRequest(endpoint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(`https://api.github.com${endpoint}`);
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
path: url.pathname + url.search,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': 'AOAC-Dev-Sentinel/1.0',
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
...(GITHUB_TOKEN ? { 'Authorization': `Bearer ${GITHUB_TOKEN}` } : {})
|
||||
}
|
||||
};
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try { resolve(JSON.parse(data)); }
|
||||
catch { resolve(data); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function collectPRInfo() {
|
||||
const prNumber = process.env.PR_NUMBER;
|
||||
const prTitle = process.env.PR_TITLE || '';
|
||||
const prBody = process.env.PR_BODY || '';
|
||||
const prAuthor = process.env.PR_AUTHOR || '';
|
||||
const prBranch = process.env.PR_BRANCH || '';
|
||||
const eventName = process.env.EVENT_NAME || 'pull_request';
|
||||
|
||||
let filesChanged = [];
|
||||
let diffStats = { additions: 0, deletions: 0, changed_files: 0 };
|
||||
|
||||
if (prNumber && GITHUB_TOKEN) {
|
||||
try {
|
||||
const files = await githubRequest(`/repos/${REPO}/pulls/${prNumber}/files?per_page=100`);
|
||||
if (Array.isArray(files)) {
|
||||
filesChanged = files.map(f => ({
|
||||
filename: f.filename,
|
||||
status: f.status,
|
||||
additions: f.additions,
|
||||
deletions: f.deletions,
|
||||
changes: f.changes
|
||||
}));
|
||||
diffStats = {
|
||||
additions: files.reduce((s, f) => s + (f.additions || 0), 0),
|
||||
deletions: files.reduce((s, f) => s + (f.deletions || 0), 0),
|
||||
changed_files: files.length
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('⚠️ 获取PR文件列表失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Categorize changes
|
||||
const categories = {};
|
||||
for (const f of filesChanged) {
|
||||
let cat = 'other';
|
||||
if (f.filename.startsWith('.github/workflows/')) cat = 'workflow';
|
||||
else if (f.filename.startsWith('scripts/')) cat = 'script';
|
||||
else if (f.filename.startsWith('server/')) cat = 'server';
|
||||
else if (f.filename.startsWith('hldp/')) cat = 'hldp';
|
||||
else if (f.filename.startsWith('brain/')) cat = 'brain';
|
||||
else if (f.filename.startsWith('data/')) cat = 'data';
|
||||
else if (f.filename.startsWith('docs/') || f.filename.endsWith('.md')) cat = 'docs';
|
||||
if (!categories[cat]) categories[cat] = [];
|
||||
categories[cat].push(f.filename);
|
||||
}
|
||||
|
||||
return {
|
||||
aoac_agent: 'AOAC-01',
|
||||
aoac_agent_name: 'copilot-dev-sentinel',
|
||||
status: 'half_ready',
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: new Date().toISOString(),
|
||||
event: eventName,
|
||||
pr: {
|
||||
number: prNumber ? parseInt(prNumber) : null,
|
||||
title: prTitle.slice(0, 200),
|
||||
body_summary: prBody.slice(0, 500),
|
||||
author: prAuthor,
|
||||
branch: prBranch
|
||||
},
|
||||
diff: diffStats,
|
||||
files_changed: filesChanged.slice(0, 50),
|
||||
categories,
|
||||
waiting_for: 'AOAC-02 (merge-sentinel)'
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔭 AOAC-01 · 副驾驶哨兵 · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
try {
|
||||
const sessionLog = await collectPRInfo();
|
||||
|
||||
writeJSON(OUTPUT_PATH, sessionLog);
|
||||
console.log(`✅ 开发日志已写入: ${path.relative(ROOT, OUTPUT_PATH)}`);
|
||||
console.log(` PR #${sessionLog.pr.number}: ${sessionLog.pr.title}`);
|
||||
console.log(` 文件变更: ${sessionLog.diff.changed_files} 个文件`);
|
||||
console.log(` +${sessionLog.diff.additions} -${sessionLog.diff.deletions}`);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents && chainStatus.agents['AOAC-01']) {
|
||||
chainStatus.agents['AOAC-01'].status = 'half_ready';
|
||||
chainStatus.agents['AOAC-01'].last_run = new Date().toISOString();
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
}
|
||||
|
||||
console.log('🔭 AOAC-01 · 半Agent就绪 · 等待AOAC-02合体');
|
||||
} catch (err) {
|
||||
console.error('❌ AOAC-01 执行失败:', err.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-02 · 合并哨兵 (Action Merge Sentinel)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 半Agent(CI侧)
|
||||
*
|
||||
* 唤醒条件:PR合并到main
|
||||
* 职责:收集CI日志(通过/失败/报错/警告)
|
||||
* 输出:data/aoac/merge-result-log.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const OUTPUT_PATH = path.join(ROOT, 'data', 'aoac', 'merge-result-log.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const REPO = process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab';
|
||||
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function githubRequest(endpoint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(`https://api.github.com${endpoint}`);
|
||||
const options = {
|
||||
hostname: url.hostname,
|
||||
path: url.pathname + url.search,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': 'AOAC-Merge-Sentinel/1.0',
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
...(GITHUB_TOKEN ? { 'Authorization': `Bearer ${GITHUB_TOKEN}` } : {})
|
||||
}
|
||||
};
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try { resolve(JSON.parse(data)); }
|
||||
catch { resolve(data); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function collectMergeInfo() {
|
||||
const prNumber = process.env.PR_NUMBER;
|
||||
const prTitle = process.env.PR_TITLE || '';
|
||||
const mergeCommitSha = process.env.MERGE_COMMIT_SHA || '';
|
||||
const headSha = process.env.HEAD_SHA || '';
|
||||
const baseBranch = process.env.BASE_BRANCH || 'main';
|
||||
|
||||
let checkRuns = [];
|
||||
let ciSummary = { total: 0, passed: 0, failed: 0, skipped: 0 };
|
||||
|
||||
// Collect check runs for the merge commit
|
||||
if (mergeCommitSha && GITHUB_TOKEN) {
|
||||
try {
|
||||
const checks = await githubRequest(`/repos/${REPO}/commits/${mergeCommitSha}/check-runs?per_page=100`);
|
||||
if (checks && checks.check_runs) {
|
||||
checkRuns = checks.check_runs.map(cr => ({
|
||||
name: cr.name,
|
||||
status: cr.status,
|
||||
conclusion: cr.conclusion,
|
||||
started_at: cr.started_at,
|
||||
completed_at: cr.completed_at,
|
||||
output_title: cr.output ? cr.output.title : null,
|
||||
output_summary: cr.output ? (cr.output.summary || '').slice(0, 200) : null
|
||||
}));
|
||||
ciSummary = {
|
||||
total: checkRuns.length,
|
||||
passed: checkRuns.filter(c => c.conclusion === 'success').length,
|
||||
failed: checkRuns.filter(c => c.conclusion === 'failure').length,
|
||||
skipped: checkRuns.filter(c => c.conclusion === 'skipped' || c.conclusion === 'cancelled').length
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('⚠️ 获取CI检查结果失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect combined status
|
||||
let combinedStatus = 'unknown';
|
||||
if (mergeCommitSha && GITHUB_TOKEN) {
|
||||
try {
|
||||
const status = await githubRequest(`/repos/${REPO}/commits/${mergeCommitSha}/status`);
|
||||
if (status && status.state) {
|
||||
combinedStatus = status.state;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('⚠️ 获取合并状态失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine overall result
|
||||
const overallResult = ciSummary.failed > 0 ? 'failure' :
|
||||
ciSummary.passed === ciSummary.total && ciSummary.total > 0 ? 'success' :
|
||||
combinedStatus !== 'unknown' ? combinedStatus : 'no_checks';
|
||||
|
||||
return {
|
||||
aoac_agent: 'AOAC-02',
|
||||
aoac_agent_name: 'action-merge-sentinel',
|
||||
status: 'half_ready',
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: new Date().toISOString(),
|
||||
merge: {
|
||||
pr_number: prNumber ? parseInt(prNumber) : null,
|
||||
pr_title: prTitle.slice(0, 200),
|
||||
merge_commit_sha: mergeCommitSha,
|
||||
head_sha: headSha,
|
||||
base_branch: baseBranch
|
||||
},
|
||||
ci: {
|
||||
combined_status: combinedStatus,
|
||||
overall_result: overallResult,
|
||||
summary: ciSummary,
|
||||
check_runs: checkRuns.slice(0, 30)
|
||||
},
|
||||
ready_for_fusion: true,
|
||||
next_agent: 'AOAC-03 (chain-fusion)'
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔍 AOAC-02 · 合并哨兵 · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
try {
|
||||
const mergeLog = await collectMergeInfo();
|
||||
|
||||
writeJSON(OUTPUT_PATH, mergeLog);
|
||||
console.log(`✅ 合并结果已写入: ${path.relative(ROOT, OUTPUT_PATH)}`);
|
||||
console.log(` PR #${mergeLog.merge.pr_number}: ${mergeLog.merge.pr_title}`);
|
||||
console.log(` CI结果: ${mergeLog.ci.overall_result}`);
|
||||
console.log(` ✅${mergeLog.ci.summary.passed} ❌${mergeLog.ci.summary.failed} ⏭${mergeLog.ci.summary.skipped}`);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents && chainStatus.agents['AOAC-02']) {
|
||||
chainStatus.agents['AOAC-02'].status = 'half_ready';
|
||||
chainStatus.agents['AOAC-02'].last_run = new Date().toISOString();
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
}
|
||||
|
||||
// Output for workflow
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `ci_result=${mergeLog.ci.overall_result}\n`);
|
||||
fs.appendFileSync(outputFile, `ready_for_fusion=true\n`);
|
||||
}
|
||||
|
||||
console.log('🔍 AOAC-02 · 半Agent就绪 · 准备触发AOAC-03合体');
|
||||
} catch (err) {
|
||||
console.error('❌ AOAC-02 执行失败:', err.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-06 · Notion同步信号Agent (Notion Sync Signal)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 同步信号发送
|
||||
*
|
||||
* 唤醒条件:AOAC-05完成后触发
|
||||
* 职责:向Notion发送同步信号→写入回执DB→本地留底
|
||||
* 输出到Notion:ZY_NOTION_RECEIPT_DB写入记录
|
||||
* 输出到仓库:data/aoac/notion-sync-log.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const MASTER_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'master-report.json');
|
||||
const SYNC_LOG_PATH = path.join(ROOT, 'data', 'aoac', 'notion-sync-log.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
const NOTION_VERSION = '2022-06-28';
|
||||
const NOTION_API_HOSTNAME = 'api.notion.com';
|
||||
const NOTION_RICH_TEXT_MAX = 2000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function richText(content) {
|
||||
const text = String(content || '').slice(0, NOTION_RICH_TEXT_MAX);
|
||||
return [{ type: 'text', text: { content: text } }];
|
||||
}
|
||||
|
||||
function titleProp(content) {
|
||||
return [{ type: 'text', text: { content: String(content || '').slice(0, 120) } }];
|
||||
}
|
||||
|
||||
function notionRequest(method, endpoint, body, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : '';
|
||||
const options = {
|
||||
hostname: NOTION_API_HOSTNAME,
|
||||
path: endpoint,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
'Content-Type': 'application/json',
|
||||
...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})
|
||||
}
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (res.statusCode >= 400) {
|
||||
reject(new Error(`Notion API ${res.statusCode}: ${parsed.message || data}`));
|
||||
} else {
|
||||
resolve(parsed);
|
||||
}
|
||||
} catch { resolve(data); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); });
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function sendNotionSync(masterReport) {
|
||||
const notionToken = process.env.ZY_NOTION_TOKEN || process.env.NOTION_TOKEN;
|
||||
const receiptDb = process.env.ZY_NOTION_RECEIPT_DB;
|
||||
|
||||
if (!notionToken || !receiptDb) {
|
||||
console.log('⚠️ Notion密钥未配置 (ZY_NOTION_TOKEN / ZY_NOTION_RECEIPT_DB),跳过Notion同步');
|
||||
return { sent: false, reason: 'missing_credentials' };
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = getDateStr();
|
||||
const seq = String(Math.floor(Math.random() * 1000)).padStart(3, '0');
|
||||
const syncId = `AOAC-SYNC-${dateStr.replace(/-/g, '')}-${seq}`;
|
||||
|
||||
const body = {
|
||||
parent: { database_id: receiptDb },
|
||||
properties: {
|
||||
'标题': { title: titleProp(`${syncId} · AOAC链路同步`) },
|
||||
'类型': { select: { name: 'AOAC链路同步' } },
|
||||
'状态': { select: { name: 'pending_confirmation' } },
|
||||
'时间戳': { date: { start: now.toISOString() } }
|
||||
},
|
||||
children: [
|
||||
{
|
||||
object: 'block',
|
||||
type: 'heading_3',
|
||||
heading_3: { rich_text: richText('📊 AOAC链路同步信号') }
|
||||
},
|
||||
{
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
paragraph: {
|
||||
rich_text: richText(
|
||||
`同步ID: ${syncId}\n` +
|
||||
`日期: ${dateStr}\n` +
|
||||
`来源: github/aoac-readme-master\n` +
|
||||
`拉取自: README.md + data/aoac/master-report.json\n` +
|
||||
`变更摘要: ${(masterReport && masterReport.changes_summary) || '定时巡检'}\n` +
|
||||
`链路健康: ${(masterReport && masterReport.chain_health) || 'unknown'}\n` +
|
||||
`仓库状态: 工作流${(masterReport && masterReport.repo_status && masterReport.repo_status.total_workflows) || '?'}个 · 脚本${(masterReport && masterReport.repo_status && masterReport.repo_status.total_scripts) || '?'}个`
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await notionRequest('POST', '/v1/pages', body, notionToken);
|
||||
console.log(`✅ Notion回执已写入: ${syncId}`);
|
||||
return { sent: true, sync_id: syncId, notion_page_id: result.id };
|
||||
} catch (err) {
|
||||
console.error('❌ Notion同步失败:', err.message);
|
||||
return { sent: false, reason: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('📡 AOAC-06 · Notion同步信号 · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
const now = new Date();
|
||||
const masterReport = readJSON(MASTER_REPORT_PATH);
|
||||
|
||||
// Send to Notion
|
||||
const notionResult = await sendNotionSync(masterReport);
|
||||
|
||||
// Write local sync log
|
||||
const syncLog = {
|
||||
aoac_agent: 'AOAC-06',
|
||||
aoac_agent_name: 'notion-sync-signal',
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
master_report_id: masterReport ? masterReport.report_id : null,
|
||||
notion_sync: notionResult,
|
||||
changes_summary: masterReport ? masterReport.changes_summary : null,
|
||||
next_check: 'AOAC-07 (chain-repair) at 23:30 CST'
|
||||
};
|
||||
|
||||
writeJSON(SYNC_LOG_PATH, syncLog);
|
||||
console.log(`📝 同步日志已保存: ${path.relative(ROOT, SYNC_LOG_PATH)}`);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents && chainStatus.agents['AOAC-06']) {
|
||||
chainStatus.agents['AOAC-06'].status = notionResult.sent ? 'completed' : 'warning';
|
||||
chainStatus.agents['AOAC-06'].last_run = now.toISOString();
|
||||
if (notionResult.sent) chainStatus.agents['AOAC-06'].last_success = now.toISOString();
|
||||
}
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
|
||||
// Output for workflow
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `notion_sent=${notionResult.sent}\n`);
|
||||
fs.appendFileSync(outputFile, `sync_id=${notionResult.sync_id || 'none'}\n`);
|
||||
}
|
||||
|
||||
console.log(notionResult.sent
|
||||
? '📡 AOAC-06 · Notion同步完成 · 等待Notion侧确认'
|
||||
: '⚠️ AOAC-06 · Notion同步跳过/失败 · 本地日志已保存');
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-05 · 首页主控Agent (README Master Agent)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 全Agent(事件+时间双触发)
|
||||
*
|
||||
* 唤醒条件1:事件触发 — AOAC-04写入readme-update-payload.json
|
||||
* 唤醒条件2:时间触发 — 每天23:00 CST
|
||||
* 职责:回看仓库首页→分析进度→更新README.md AOAC区域
|
||||
* 输出:更新README.md + data/aoac/master-report.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const README_PATH = path.join(ROOT, 'README.md');
|
||||
const PAYLOAD_PATH = path.join(ROOT, 'data', 'aoac', 'readme-update-payload.json');
|
||||
const CHAIN_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'chain-report.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const MASTER_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'master-report.json');
|
||||
const DASHBOARD_PATH = path.join(ROOT, 'data', 'bulletin-board', 'dashboard.json');
|
||||
const MEMORY_PATH = path.join(ROOT, '.github', 'persona-brain', 'memory.json');
|
||||
const HISTORY_DIR = path.join(ROOT, 'data', 'aoac', 'history');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
const AOAC_START_MARKER = '<!-- AOAC_STATUS_START -->';
|
||||
const AOAC_END_MARKER = '<!-- AOAC_STATUS_END -->';
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function getDateStr() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function generateAOACSection(chainStatus, chainReport, payload) {
|
||||
const lines = [];
|
||||
lines.push('');
|
||||
lines.push('### 🔗 AOAC · Agent链路闭环系统');
|
||||
lines.push('');
|
||||
lines.push('| Agent | 名称 | 状态 | 最后运行 |');
|
||||
lines.push('|-------|------|------|----------|');
|
||||
|
||||
if (chainStatus && chainStatus.agents) {
|
||||
const agentOrder = ['AOAC-01', 'AOAC-02', 'AOAC-03', 'AOAC-04', 'AOAC-05', 'AOAC-06', 'AOAC-07', 'AOAC-08'];
|
||||
for (const id of agentOrder) {
|
||||
const agent = chainStatus.agents[id];
|
||||
if (!agent) continue;
|
||||
const statusIcon = agent.status === 'completed' ? '✅' :
|
||||
agent.status === 'half_ready' ? '🔶' :
|
||||
agent.status === 'running' ? '🔄' :
|
||||
agent.status === 'error' ? '❌' : '⬜';
|
||||
const lastRun = agent.last_run ?
|
||||
new Date(new Date(agent.last_run).getTime() + BEIJING_OFFSET_MS).toISOString().replace('T', ' ').slice(0, 16) :
|
||||
'N/A';
|
||||
lines.push(`| ${id} | ${agent.name_cn} | ${statusIcon} ${agent.status} | ${lastRun} |`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
|
||||
// Latest chain report
|
||||
if (chainReport && chainReport.development) {
|
||||
const dev = chainReport.development;
|
||||
const ci = chainReport.ci_result || {};
|
||||
const ciBadge = ci.overall === 'success' ? '✅' : ci.overall === 'failure' ? '❌' : '⚠️';
|
||||
lines.push(`**最新链路报告**: ${chainReport.report_id || 'N/A'}`);
|
||||
lines.push(`- PR #${dev.pr_number || '?'}: ${(dev.pr_title || '').slice(0, 80)}`);
|
||||
lines.push(`- 变更: ${dev.files_changed || 0}文件 (+${dev.additions || 0}/-${dev.deletions || 0})`);
|
||||
lines.push(`- CI: ${ciBadge} ${ci.overall || 'unknown'}`);
|
||||
lines.push(`- 时间: ${chainReport.timestamp || 'N/A'}`);
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
// Chain health
|
||||
if (chainStatus) {
|
||||
const health = chainStatus.chain_health || 'unknown';
|
||||
const healthIcon = health === 'healthy' ? '🟢' : health === 'warning' ? '🟡' : '⚪';
|
||||
lines.push(`**链路健康**: ${healthIcon} ${health} · 完成周期: ${chainStatus.total_cycles || 0} · 修复次数: ${chainStatus.total_repairs || 0}`);
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function updateReadmeAOACSection(aoacContent) {
|
||||
if (!fs.existsSync(README_PATH)) {
|
||||
console.log('⚠️ README.md不存在,跳过更新');
|
||||
return false;
|
||||
}
|
||||
|
||||
let readme = fs.readFileSync(README_PATH, 'utf8');
|
||||
const startIdx = readme.indexOf(AOAC_START_MARKER);
|
||||
const endIdx = readme.indexOf(AOAC_END_MARKER);
|
||||
|
||||
if (startIdx === -1 || endIdx === -1) {
|
||||
// Markers don't exist yet - add them before the footer if possible
|
||||
const footerMarker = '<!-- FOOTER_START -->';
|
||||
const footerIdx = readme.indexOf(footerMarker);
|
||||
if (footerIdx !== -1) {
|
||||
const insertion = `\n${AOAC_START_MARKER}\n${aoacContent}\n${AOAC_END_MARKER}\n\n`;
|
||||
readme = readme.slice(0, footerIdx) + insertion + readme.slice(footerIdx);
|
||||
} else {
|
||||
// Append to end
|
||||
readme += `\n${AOAC_START_MARKER}\n${aoacContent}\n${AOAC_END_MARKER}\n`;
|
||||
}
|
||||
} else {
|
||||
// Replace existing content
|
||||
readme = readme.slice(0, startIdx + AOAC_START_MARKER.length) +
|
||||
'\n' + aoacContent + '\n' +
|
||||
readme.slice(endIdx);
|
||||
}
|
||||
|
||||
fs.writeFileSync(README_PATH, readme, 'utf8');
|
||||
return true;
|
||||
}
|
||||
|
||||
function collectRepoStatus() {
|
||||
// Collect basic repo statistics
|
||||
const status = {
|
||||
total_workflows: 0,
|
||||
total_scripts: 0,
|
||||
aoac_agents: 0
|
||||
};
|
||||
|
||||
try {
|
||||
const wfDir = path.join(ROOT, '.github', 'workflows');
|
||||
if (fs.existsSync(wfDir)) {
|
||||
status.total_workflows = fs.readdirSync(wfDir).filter(f => f.endsWith('.yml')).length;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
try {
|
||||
const scriptDir = path.join(ROOT, 'scripts');
|
||||
if (fs.existsSync(scriptDir)) {
|
||||
status.total_scripts = fs.readdirSync(scriptDir).filter(f => f.endsWith('.js')).length;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
try {
|
||||
const aoacDir = path.join(ROOT, 'scripts', 'aoac');
|
||||
if (fs.existsSync(aoacDir)) {
|
||||
status.aoac_agents = fs.readdirSync(aoacDir).filter(f => f.endsWith('.js')).length;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const mode = process.argv[2] || 'auto';
|
||||
console.log(`🏠 AOAC-05 · 首页主控Agent · 唤醒中 (模式: ${mode})`);
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = getDateStr();
|
||||
|
||||
// Read all data sources
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH);
|
||||
const chainReport = readJSON(CHAIN_REPORT_PATH);
|
||||
const payload = readJSON(PAYLOAD_PATH);
|
||||
const dashboard = readJSON(DASHBOARD_PATH);
|
||||
const memory = readJSON(MEMORY_PATH);
|
||||
const repoStatus = collectRepoStatus();
|
||||
|
||||
// Generate AOAC section for README
|
||||
const aoacContent = generateAOACSection(chainStatus, chainReport, payload);
|
||||
|
||||
// Update README
|
||||
const updated = updateReadmeAOACSection(aoacContent);
|
||||
if (updated) {
|
||||
console.log('✅ README.md AOAC区域已更新');
|
||||
}
|
||||
|
||||
// Also call generate-readme-dashboard.js if it exists (event-triggered mode)
|
||||
const dashGen = path.join(ROOT, 'scripts', 'generate-readme-dashboard.js');
|
||||
if (fs.existsSync(dashGen) && mode === 'event') {
|
||||
try {
|
||||
execFileSync('node', [dashGen], { cwd: ROOT, timeout: 60000, stdio: 'inherit' });
|
||||
console.log('✅ README仪表盘同步更新');
|
||||
} catch (err) {
|
||||
console.error('⚠️ 仪表盘更新失败:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate master report
|
||||
const seq = String(Math.floor(Math.random() * 1000)).padStart(3, '0');
|
||||
const masterReport = {
|
||||
aoac_agent: 'AOAC-05',
|
||||
aoac_agent_name: 'readme-master-agent',
|
||||
report_id: `AOAC-MASTER-${dateStr.replace(/-/g, '')}-${seq}`,
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
mode,
|
||||
readme_updated: updated,
|
||||
chain_health: chainStatus ? chainStatus.chain_health : 'unknown',
|
||||
latest_chain_report: chainReport ? chainReport.report_id : null,
|
||||
repo_status: repoStatus,
|
||||
changes_summary: chainReport ? chainReport.changes_summary : '定时巡检·无新合并',
|
||||
triggers_next: 'AOAC-06 (notion-sync-signal)'
|
||||
};
|
||||
|
||||
writeJSON(MASTER_REPORT_PATH, masterReport);
|
||||
console.log(`✅ 主控报告: ${masterReport.report_id}`);
|
||||
|
||||
// Update chain status
|
||||
if (chainStatus && chainStatus.agents && chainStatus.agents['AOAC-05']) {
|
||||
chainStatus.agents['AOAC-05'].status = 'completed';
|
||||
chainStatus.agents['AOAC-05'].last_run = now.toISOString();
|
||||
chainStatus.agents['AOAC-05'].last_success = now.toISOString();
|
||||
chainStatus.total_cycles = (chainStatus.total_cycles || 0) + 1;
|
||||
chainStatus.last_full_cycle = now.toISOString();
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
}
|
||||
|
||||
// Archive
|
||||
const historyDir = path.join(HISTORY_DIR, dateStr);
|
||||
fs.mkdirSync(historyDir, { recursive: true });
|
||||
writeJSON(path.join(historyDir, `${masterReport.report_id}.json`), masterReport);
|
||||
|
||||
// Output for workflow
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `master_report_id=${masterReport.report_id}\n`);
|
||||
fs.appendFileSync(outputFile, `readme_updated=${updated}\n`);
|
||||
fs.appendFileSync(outputFile, `changes_summary=${(masterReport.changes_summary || '').slice(0, 200)}\n`);
|
||||
}
|
||||
|
||||
console.log('🏠 AOAC-05 · 主控Agent完成 · 触发AOAC-06 Notion同步');
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-04 · 首页同步模块 (README Sync Module)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 半Agent(事件唤醒)
|
||||
*
|
||||
* 唤醒条件:data/aoac/readme-sync-trigger.json 被写入
|
||||
* 职责:解析链路报告→提取进度变更→准备README更新数据
|
||||
* 输出:data/aoac/readme-update-payload.json
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const CHAIN_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'chain-report.json');
|
||||
const TRIGGER_PATH = path.join(ROOT, 'data', 'aoac', 'readme-sync-trigger.json');
|
||||
const PAYLOAD_PATH = path.join(ROOT, 'data', 'aoac', 'readme-update-payload.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function extractReadmeUpdates(chainReport) {
|
||||
if (!chainReport) return null;
|
||||
|
||||
const updates = {
|
||||
aoac_section: '',
|
||||
progress_entries: [],
|
||||
status_badge: '🟢'
|
||||
};
|
||||
|
||||
// Build AOAC status line
|
||||
const dev = chainReport.development || {};
|
||||
const ci = chainReport.ci_result || {};
|
||||
|
||||
const ciBadge = ci.overall === 'success' ? '✅' :
|
||||
ci.overall === 'failure' ? '❌' : '⚠️';
|
||||
|
||||
updates.status_badge = ciBadge;
|
||||
updates.aoac_section = [
|
||||
`| ${chainReport.report_id || 'N/A'} | PR #${dev.pr_number || '?'} | ${(dev.pr_title || '').slice(0, 50)} | ${dev.files_changed || 0}文件 +${dev.additions || 0}/-${dev.deletions || 0} | ${ciBadge} ${ci.overall || 'unknown'} | ${chainReport.timestamp || ''} |`
|
||||
].join('\n');
|
||||
|
||||
// Progress entry for consciousness chain
|
||||
if (dev.pr_title) {
|
||||
updates.progress_entries.push({
|
||||
type: 'dev_merge',
|
||||
title: dev.pr_title,
|
||||
result: ci.overall,
|
||||
timestamp: chainReport.timestamp
|
||||
});
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('📋 AOAC-04 · 首页同步模块 · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
// Check trigger
|
||||
const trigger = readJSON(TRIGGER_PATH);
|
||||
if (!trigger) {
|
||||
console.log('⚠️ 无触发信号,跳过');
|
||||
return;
|
||||
}
|
||||
console.log(`📡 收到触发信号: ${trigger.trigger_id}`);
|
||||
|
||||
// Read chain report
|
||||
const chainReport = readJSON(CHAIN_REPORT_PATH);
|
||||
if (!chainReport) {
|
||||
console.log('⚠️ 链路报告不存在,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract README updates
|
||||
const updates = extractReadmeUpdates(chainReport);
|
||||
|
||||
const now = new Date();
|
||||
const payload = {
|
||||
aoac_agent: 'AOAC-04',
|
||||
aoac_agent_name: 'readme-sync-module',
|
||||
payload_id: `AOAC-PAYLOAD-${now.toISOString().slice(0, 10).replace(/-/g, '')}-${String(Math.floor(Math.random() * 1000)).padStart(3, '0')}`,
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
source_report: chainReport.report_id,
|
||||
updates,
|
||||
changes_summary: chainReport.changes_summary,
|
||||
trigger_master: true,
|
||||
next_agent: 'AOAC-05 (readme-master-agent)'
|
||||
};
|
||||
|
||||
writeJSON(PAYLOAD_PATH, payload);
|
||||
console.log(`✅ README更新数据已准备: ${payload.payload_id}`);
|
||||
console.log(` ${chainReport.changes_summary}`);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents && chainStatus.agents['AOAC-04']) {
|
||||
chainStatus.agents['AOAC-04'].status = 'completed';
|
||||
chainStatus.agents['AOAC-04'].last_run = now.toISOString();
|
||||
chainStatus.agents['AOAC-04'].last_success = now.toISOString();
|
||||
}
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
|
||||
console.log('📋 AOAC-04 · 半Agent完成 · 触发AOAC-05主控');
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* AOAC-08 · 修复监督Agent (Repair Supervisor)
|
||||
* ═══════════════════════════════════════════════
|
||||
* AGE OS Agent Chain · 监督Agent(修复监督层)
|
||||
*
|
||||
* 唤醒条件:AOAC-07修复行为启动(事件触发)
|
||||
* 职责:监督修复过程→接收修复报告→判断最终结果
|
||||
* 成功→Notion标绿 失败→发邮件给冰朔
|
||||
*
|
||||
* 版权:国作登字-2026-A-00037559 · TCS-0002∞
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { createTransport } = (() => {
|
||||
try { return require('nodemailer'); }
|
||||
catch { return { createTransport: null }; }
|
||||
})();
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..', '..');
|
||||
const REPAIR_REPORT_PATH = path.join(ROOT, 'data', 'aoac', 'repair-report.json');
|
||||
const VERDICT_PATH = path.join(ROOT, 'data', 'aoac', 'supervisor-verdict.json');
|
||||
const CHAIN_STATUS_PATH = path.join(ROOT, 'data', 'aoac', 'chain-status.json');
|
||||
const BEIJING_OFFSET_MS = 8 * 3600 * 1000;
|
||||
|
||||
const NOTION_VERSION = '2022-06-28';
|
||||
const NOTION_API_HOSTNAME = 'api.notion.com';
|
||||
|
||||
function getBeijingTime() {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() + BEIJING_OFFSET_MS)
|
||||
.toISOString().replace('T', ' ').slice(0, 19) + '+08:00';
|
||||
}
|
||||
|
||||
function readJSON(filePath) {
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function writeJSON(filePath, data) {
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
function notionRequest(method, endpoint, body, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bodyStr = body ? JSON.stringify(body) : '';
|
||||
const options = {
|
||||
hostname: NOTION_API_HOSTNAME,
|
||||
path: endpoint,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
'Content-Type': 'application/json',
|
||||
...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})
|
||||
}
|
||||
};
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (res.statusCode >= 400) reject(new Error(`${res.statusCode}: ${parsed.message || data}`));
|
||||
else resolve(parsed);
|
||||
} catch { resolve(data); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => { req.destroy(); reject(new Error('timeout')); });
|
||||
if (bodyStr) req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function updateNotionReceiptGreen(pageId) {
|
||||
const notionToken = process.env.ZY_NOTION_TOKEN || process.env.NOTION_TOKEN;
|
||||
if (!notionToken || !pageId) return false;
|
||||
|
||||
try {
|
||||
await notionRequest('PATCH', `/v1/pages/${pageId}`, {
|
||||
properties: {
|
||||
'状态': { select: { name: '✅ 已确认' } }
|
||||
}
|
||||
}, notionToken);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('⚠️ 更新Notion状态失败:', err.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function sendAlertEmail(repairReport) {
|
||||
const smtpUser = process.env.ZY_SMTP_USER;
|
||||
const smtpPass = process.env.ZY_SMTP_PASS;
|
||||
|
||||
if (!smtpUser || !smtpPass) {
|
||||
console.log('⚠️ SMTP未配置,无法发送告警邮件');
|
||||
return { sent: false, reason: 'missing_smtp_credentials' };
|
||||
}
|
||||
|
||||
// Use nodemailer if available, otherwise use raw SMTP via https webhook
|
||||
if (!createTransport) {
|
||||
console.log('⚠️ nodemailer不可用,使用GitHub Issue告警');
|
||||
return { sent: false, reason: 'nodemailer_unavailable' };
|
||||
}
|
||||
|
||||
try {
|
||||
const transporter = createTransport({
|
||||
host: 'smtp.gmail.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
auth: { user: smtpUser, pass: smtpPass }
|
||||
});
|
||||
|
||||
const diagnosis = repairReport.diagnosis || {};
|
||||
const body = [
|
||||
`🔴 AOAC链路修复失败告警`,
|
||||
``,
|
||||
`修复ID: ${repairReport.repair_id}`,
|
||||
`时间: ${repairReport.timestamp}`,
|
||||
`诊断问题: ${diagnosis.total_issues || 0}个 (关键: ${diagnosis.critical || 0}个)`,
|
||||
`修复尝试: ${(repairReport.repair_attempts || []).length}次`,
|
||||
`最终结果: ${repairReport.final_result}`,
|
||||
``,
|
||||
`问题详情:`,
|
||||
...(diagnosis.issues || []).map(i => ` - [${i.severity}] ${i.agent}: ${i.issue}`),
|
||||
``,
|
||||
`请人工介入检查。`,
|
||||
``,
|
||||
`—— 铸渊·AOAC-08监督Agent`
|
||||
].join('\n');
|
||||
|
||||
await transporter.sendMail({
|
||||
from: smtpUser,
|
||||
to: smtpUser,
|
||||
subject: `🔴 AOAC链路修复失败 · ${repairReport.repair_id}`,
|
||||
text: body
|
||||
});
|
||||
|
||||
return { sent: true, method: 'email' };
|
||||
} catch (err) {
|
||||
console.error('❌ 邮件发送失败:', err.message);
|
||||
return { sent: false, reason: err.message };
|
||||
}
|
||||
}
|
||||
|
||||
async function createAlertIssue(repairReport) {
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repo = process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab';
|
||||
|
||||
if (!token) return { created: false, reason: 'no_token' };
|
||||
|
||||
const diagnosis = repairReport.diagnosis || {};
|
||||
const body = [
|
||||
`## 🔴 AOAC链路修复失败`,
|
||||
``,
|
||||
`**修复ID**: ${repairReport.repair_id}`,
|
||||
`**时间**: ${repairReport.timestamp}`,
|
||||
`**诊断**: ${diagnosis.total_issues || 0}个问题 (${diagnosis.critical || 0}个关键)`,
|
||||
`**尝试**: ${(repairReport.repair_attempts || []).length}次修复`,
|
||||
`**结果**: ❌ ${repairReport.final_result}`,
|
||||
``,
|
||||
`### 问题详情`,
|
||||
...(diagnosis.issues || []).map(i => `- \`[${i.severity}]\` **${i.agent}**: ${i.issue}`),
|
||||
``,
|
||||
`---`,
|
||||
`*此Issue由AOAC-08监督Agent自动创建,请冰朔人工介入*`
|
||||
].join('\n');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const reqBody = JSON.stringify({
|
||||
title: `🔴 AOAC链路修复失败 · ${repairReport.repair_id}`,
|
||||
body,
|
||||
labels: ['aoac-alert', 'needs-human']
|
||||
});
|
||||
|
||||
const options = {
|
||||
hostname: 'api.github.com',
|
||||
path: `/repos/${repo}/issues`,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'AOAC-Supervisor/1.0',
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(reqBody)
|
||||
}
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
resolve({ created: res.statusCode < 400, issue_number: parsed.number });
|
||||
} catch { resolve({ created: false }); }
|
||||
});
|
||||
});
|
||||
req.on('error', () => resolve({ created: false }));
|
||||
req.write(reqBody);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('👁️ AOAC-08 · 修复监督Agent · 唤醒中...');
|
||||
console.log(`📅 ${getBeijingTime()}`);
|
||||
|
||||
const now = new Date();
|
||||
const repairReport = readJSON(REPAIR_REPORT_PATH);
|
||||
|
||||
if (!repairReport) {
|
||||
console.log('⚠️ 修复报告不存在,跳过监督');
|
||||
return;
|
||||
}
|
||||
|
||||
const isSuccess = repairReport.final_result === 'success';
|
||||
const neededRepair = repairReport.needed_repair;
|
||||
|
||||
let verdictAction = {};
|
||||
|
||||
if (isSuccess || !neededRepair) {
|
||||
console.log('✅ 修复成功或无需修复');
|
||||
|
||||
// Update Notion receipt to green if there's a page ID
|
||||
if (repairReport.receipt_check && repairReport.receipt_check.page_id) {
|
||||
const updated = await updateNotionReceiptGreen(repairReport.receipt_check.page_id);
|
||||
verdictAction.notion_updated = updated;
|
||||
}
|
||||
verdictAction.action = 'close_green';
|
||||
} else {
|
||||
console.log('🔴 修复失败 · 触发告警');
|
||||
|
||||
// Try email first, then GitHub Issue as fallback
|
||||
const emailResult = await sendAlertEmail(repairReport);
|
||||
verdictAction.email = emailResult;
|
||||
|
||||
if (!emailResult.sent) {
|
||||
const issueResult = await createAlertIssue(repairReport);
|
||||
verdictAction.github_issue = issueResult;
|
||||
}
|
||||
verdictAction.action = 'alert_human';
|
||||
}
|
||||
|
||||
// Write supervisor verdict
|
||||
const verdict = {
|
||||
aoac_agent: 'AOAC-08',
|
||||
aoac_agent_name: 'repair-supervisor',
|
||||
timestamp: getBeijingTime(),
|
||||
timestamp_utc: now.toISOString(),
|
||||
repair_id: repairReport.repair_id,
|
||||
verdict: isSuccess || !neededRepair ? 'green' : 'red',
|
||||
needed_repair: neededRepair,
|
||||
repair_result: repairReport.final_result,
|
||||
action: verdictAction,
|
||||
chain_closed: isSuccess || !neededRepair
|
||||
};
|
||||
|
||||
writeJSON(VERDICT_PATH, verdict);
|
||||
console.log(`📝 监督判定: ${verdict.verdict === 'green' ? '🟢' : '🔴'} ${verdict.verdict}`);
|
||||
|
||||
// Update chain status
|
||||
const chainStatus = readJSON(CHAIN_STATUS_PATH) || {};
|
||||
if (chainStatus.agents && chainStatus.agents['AOAC-08']) {
|
||||
chainStatus.agents['AOAC-08'].status = 'completed';
|
||||
chainStatus.agents['AOAC-08'].last_run = now.toISOString();
|
||||
chainStatus.agents['AOAC-08'].last_success = now.toISOString();
|
||||
}
|
||||
if (chainStatus && verdict.chain_closed) {
|
||||
chainStatus.chain_health = 'healthy';
|
||||
}
|
||||
writeJSON(CHAIN_STATUS_PATH, chainStatus);
|
||||
|
||||
// Output for workflow
|
||||
const outputFile = process.env.GITHUB_OUTPUT;
|
||||
if (outputFile) {
|
||||
fs.appendFileSync(outputFile, `verdict=${verdict.verdict}\n`);
|
||||
fs.appendFileSync(outputFile, `chain_closed=${verdict.chain_closed}\n`);
|
||||
}
|
||||
|
||||
console.log(`👁️ AOAC-08 · 监督完成 · 闭环${verdict.chain_closed ? '✅' : '❌'}`);
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in New Issue