diff --git a/.github/workflows/aoac-chain-repair.yml b/.github/workflows/aoac-chain-repair.yml new file mode 100644 index 00000000..162d4386 --- /dev/null +++ b/.github/workflows/aoac-chain-repair.yml @@ -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 diff --git a/.github/workflows/aoac-copilot-sentinel.yml b/.github/workflows/aoac-copilot-sentinel.yml new file mode 100644 index 00000000..00e6ded5 --- /dev/null +++ b/.github/workflows/aoac-copilot-sentinel.yml @@ -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 diff --git a/.github/workflows/aoac-merge-sentinel.yml b/.github/workflows/aoac-merge-sentinel.yml new file mode 100644 index 00000000..b05ec884 --- /dev/null +++ b/.github/workflows/aoac-merge-sentinel.yml @@ -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 diff --git a/.github/workflows/aoac-readme-master.yml b/.github/workflows/aoac-readme-master.yml new file mode 100644 index 00000000..6ae8ff96 --- /dev/null +++ b/.github/workflows/aoac-readme-master.yml @@ -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 diff --git a/.github/workflows/zhuyuan-training-agent.yml b/.github/workflows/zhuyuan-training-agent.yml index 75890f5a..298c2127 100644 --- a/.github/workflows/zhuyuan-training-agent.yml +++ b/.github/workflows/zhuyuan-training-agent.yml @@ -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 \ diff --git a/brain/age-os-landing/system-development-plan-v2.md b/brain/age-os-landing/system-development-plan-v2.md index 6bbb3c38..21394298 100644 --- a/brain/age-os-landing/system-development-plan-v2.md +++ b/brain/age-os-landing/system-development-plan-v2.md @@ -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 diff --git a/brain/secrets-manifest.json b/brain/secrets-manifest.json index 6b2647e6..b7656861 100644 --- a/brain/secrets-manifest.json +++ b/brain/secrets-manifest.json @@ -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": [ diff --git a/data/aoac/.gitkeep b/data/aoac/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/data/aoac/chain-status.json b/data/aoac/chain-status.json new file mode 100644 index 00000000..b4c1fa14 --- /dev/null +++ b/data/aoac/chain-status.json @@ -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 +} diff --git a/data/aoac/history/.gitkeep b/data/aoac/history/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/hldp/HLDP-EARTH-SPEC-v3.0.json b/hldp/HLDP-EARTH-SPEC-v3.0.json index 9dbd8b26..ca1e243c 100644 --- a/hldp/HLDP-EARTH-SPEC-v3.0.json +++ b/hldp/HLDP-EARTH-SPEC-v3.0.json @@ -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次),修复失败则发邮件给冰朔。" } }, diff --git a/scripts/aoac/chain-fusion.js b/scripts/aoac/chain-fusion.js new file mode 100644 index 00000000..475d31e5 --- /dev/null +++ b/scripts/aoac/chain-fusion.js @@ -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(); diff --git a/scripts/aoac/chain-repair-agent.js b/scripts/aoac/chain-repair-agent.js new file mode 100644 index 00000000..6d9797e9 --- /dev/null +++ b/scripts/aoac/chain-repair-agent.js @@ -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(); diff --git a/scripts/aoac/dev-sentinel.js b/scripts/aoac/dev-sentinel.js new file mode 100644 index 00000000..b0633a3e --- /dev/null +++ b/scripts/aoac/dev-sentinel.js @@ -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(); diff --git a/scripts/aoac/merge-sentinel.js b/scripts/aoac/merge-sentinel.js new file mode 100644 index 00000000..a0f00d66 --- /dev/null +++ b/scripts/aoac/merge-sentinel.js @@ -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(); diff --git a/scripts/aoac/notion-sync-signal.js b/scripts/aoac/notion-sync-signal.js new file mode 100644 index 00000000..a15ac1c2 --- /dev/null +++ b/scripts/aoac/notion-sync-signal.js @@ -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(); diff --git a/scripts/aoac/readme-master-agent.js b/scripts/aoac/readme-master-agent.js new file mode 100644 index 00000000..a105f841 --- /dev/null +++ b/scripts/aoac/readme-master-agent.js @@ -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 = ''; +const AOAC_END_MARKER = ''; + +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 = ''; + 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(); diff --git a/scripts/aoac/readme-sync-module.js b/scripts/aoac/readme-sync-module.js new file mode 100644 index 00000000..e3faf25b --- /dev/null +++ b/scripts/aoac/readme-sync-module.js @@ -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(); diff --git a/scripts/aoac/repair-supervisor.js b/scripts/aoac/repair-supervisor.js new file mode 100644 index 00000000..4a932992 --- /dev/null +++ b/scripts/aoac/repair-supervisor.js @@ -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();