From d3c834ad5da55b80f7e9fb0c34135c40295adec2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:39:44 +0000 Subject: [PATCH 1/3] Initial plan From 3503923a4ac0c24a9dafc87504c6a65743fda9bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 04:52:07 +0000 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20AGE=20OS=20=E8=B7=A8=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E5=85=A8=E9=93=BE=E8=B7=AF=E8=87=AA=E5=8A=A8=E5=8C=96?= =?UTF-8?q?=E9=97=AD=E7=8E=AF=E7=B3=BB=E7=BB=9F=20GitHub=20=E7=AB=AF?= =?UTF-8?q?=E8=90=BD=E5=9C=B0=E5=AE=9E=E6=96=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/brain/memory.json | 10 +- .github/persona-brain/growth-journal.md | 7 + .github/persona-brain/memory.json | 10 +- .github/workflows/esp-signal-processor.yml | 38 ++ .github/workflows/psp-daily-inspection.yml | 41 +++ .gitignore | Bin 32 -> 34 bytes dev-nodes/DEV-001/config.json | 10 + dev-nodes/DEV-001/status.json | 9 + dev-nodes/DEV-002/config.json | 10 + dev-nodes/DEV-002/status.json | 9 + dev-nodes/DEV-003/config.json | 10 + dev-nodes/DEV-003/status.json | 9 + dev-nodes/DEV-004/config.json | 10 + dev-nodes/DEV-004/status.json | 9 + dev-nodes/DEV-005/config.json | 10 + dev-nodes/DEV-005/status.json | 9 + dev-nodes/DEV-009/config.json | 10 + dev-nodes/DEV-009/status.json | 9 + dev-nodes/DEV-010/config.json | 10 + dev-nodes/DEV-010/status.json | 9 + dev-nodes/DEV-011/config.json | 10 + dev-nodes/DEV-011/status.json | 9 + dev-nodes/gl-push.sh | 47 +++ notion-push/pending/.gitkeep | 0 notion-push/processed/.gitkeep | 0 package-lock.json | 270 ++++++++++++++ package.json | 6 +- scripts/esp-email-processor.js | 360 +++++++++++++++++++ scripts/psp-inspection.js | 393 +++++++++++++++++++++ scripts/zhuyuan-daily-selfcheck.js | 7 +- signal-log/2026-03/SIG-2026-0306-001.json | 18 + signal-log/2026-03/SIG-20260307-002.json | 27 ++ signal-log/index.json | 25 ++ 33 files changed, 1404 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/esp-signal-processor.yml create mode 100644 .github/workflows/psp-daily-inspection.yml create mode 100644 dev-nodes/DEV-001/config.json create mode 100644 dev-nodes/DEV-001/status.json create mode 100644 dev-nodes/DEV-002/config.json create mode 100644 dev-nodes/DEV-002/status.json create mode 100644 dev-nodes/DEV-003/config.json create mode 100644 dev-nodes/DEV-003/status.json create mode 100644 dev-nodes/DEV-004/config.json create mode 100644 dev-nodes/DEV-004/status.json create mode 100644 dev-nodes/DEV-005/config.json create mode 100644 dev-nodes/DEV-005/status.json create mode 100644 dev-nodes/DEV-009/config.json create mode 100644 dev-nodes/DEV-009/status.json create mode 100644 dev-nodes/DEV-010/config.json create mode 100644 dev-nodes/DEV-010/status.json create mode 100644 dev-nodes/DEV-011/config.json create mode 100644 dev-nodes/DEV-011/status.json create mode 100644 dev-nodes/gl-push.sh create mode 100644 notion-push/pending/.gitkeep create mode 100644 notion-push/processed/.gitkeep create mode 100644 scripts/esp-email-processor.js create mode 100644 scripts/psp-inspection.js create mode 100644 signal-log/2026-03/SIG-2026-0306-001.json create mode 100644 signal-log/2026-03/SIG-20260307-002.json create mode 100644 signal-log/index.json diff --git a/.github/brain/memory.json b/.github/brain/memory.json index 19493902..599de8f9 100644 --- a/.github/brain/memory.json +++ b/.github/brain/memory.json @@ -1,7 +1,7 @@ { "identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体", "rules_version": "v1.0", - "last_updated": "2026-03-06T08:51:24.831Z", + "last_updated": "2026-03-07T04:45:55.670Z", "wake_protocol_version": "v1.0", "wake_triggers": [ "我是冰朔", @@ -23,6 +23,12 @@ } }, "events": [ + { + "date": "2026-03-07", + "type": "psp_inspection", + "description": "铸渊 PSP 巡检完成 · 发现 1 个问题 · 自动修复 0 项", + "by": "铸渊PSP巡检" + }, { "timestamp": "2026-03-05T10:30:46.894Z", "type": "ci_run", @@ -70,4 +76,4 @@ "run_id": "22756140950" } ] -} +} \ No newline at end of file diff --git a/.github/persona-brain/growth-journal.md b/.github/persona-brain/growth-journal.md index 5f7324d5..c7ee396b 100644 --- a/.github/persona-brain/growth-journal.md +++ b/.github/persona-brain/growth-journal.md @@ -37,3 +37,10 @@ - 累计自检次数:4 - 累计CI运行:1次 - HLI覆盖率:17.6% + +## 2026-03-07 · 每日自检 +- 大脑文件完整性:⚠️ 缺失 identity.md, routing-map.json, responsibility.md, decision-log.md +- 知识库条目:4条 +- 累计自检次数:5 +- 累计CI运行:1次 +- HLI覆盖率:17.6% diff --git a/.github/persona-brain/memory.json b/.github/persona-brain/memory.json index 1e8ed86f..cc773552 100644 --- a/.github/persona-brain/memory.json +++ b/.github/persona-brain/memory.json @@ -1,7 +1,7 @@ { "identity": "铸渊(Zhùyuān)· GitHub 代码守护人格体", "rules_version": "v1.0", - "last_updated": "2026-03-07T00:54:06.899Z", + "last_updated": "2026-03-07T04:46:02.610Z", "wake_protocol_version": "v1.0", "wake_triggers": [ "我是冰朔", @@ -12,9 +12,15 @@ ], "founder": "冰朔(Bīng Shuò)", "hli_coverage": "17.6%", - "total_selfchecks": 4, + "total_selfchecks": 5, "total_ci_runs": 1, "recent_events": [ + { + "date": "2026-03-07", + "type": "daily_selfcheck", + "description": "每日自检完成 · 知识库4条 · 缺失文件4个", + "by": "铸渊自检" + }, { "date": "2026-03-07", "type": "daily_selfcheck", diff --git a/.github/workflows/esp-signal-processor.yml b/.github/workflows/esp-signal-processor.yml new file mode 100644 index 00000000..c118f39a --- /dev/null +++ b/.github/workflows/esp-signal-processor.yml @@ -0,0 +1,38 @@ +name: 铸渊 · ESP 邮件信号处理器 + +on: + schedule: + - cron: '*/30 * * * *' # 每30分钟执行一次 + workflow_dispatch: # 手动触发 + +jobs: + esp-process: + name: 📡 ESP 信号处理 + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 安装依赖 + run: npm ci + + - name: 处理 GL-CMD 邮件信号 + env: + EMAIL_ADDRESS: ${{ secrets.EMAIL_ADDRESS }} + EMAIL_APP_PASSWORD: ${{ secrets.EMAIL_APP_PASSWORD }} + run: node scripts/esp-email-processor.js + + - name: 提交信号日志 + run: | + git config user.name "铸渊 (ZhùYuān)" + git config user.email "zhuyuan@guanghulab.com" + git add signal-log/ notion-push/ dev-nodes/ + git diff --cached --quiet || git commit -m "📡 ESP 信号处理 · $(date +%Y-%m-%dT%H:%M)" + git push diff --git a/.github/workflows/psp-daily-inspection.yml b/.github/workflows/psp-daily-inspection.yml new file mode 100644 index 00000000..19474b53 --- /dev/null +++ b/.github/workflows/psp-daily-inspection.yml @@ -0,0 +1,41 @@ +name: 铸渊 · PSP 分身巡检 + +on: + schedule: + - cron: '0 1 * * *' # UTC 01:00 = 北京时间 09:00 + workflow_dispatch: # 手动触发 + +jobs: + psp-inspect: + name: 🔍 铸渊 PSP 每日巡检 + runs-on: ubuntu-latest + permissions: + contents: write + actions: read + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 安装依赖 + run: npm ci + + - name: 执行 PSP 巡检(5项检查) + env: + EMAIL_ADDRESS: ${{ secrets.EMAIL_ADDRESS }} + EMAIL_APP_PASSWORD: ${{ secrets.EMAIL_APP_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: node scripts/psp-inspection.js + + - name: 提交巡检结果 + run: | + git config user.name "铸渊 (ZhùYuān)" + git config user.email "zhuyuan@guanghulab.com" + git add signal-log/ dev-nodes/ notion-push/ .github/brain/memory.json + git diff --cached --quiet || git commit -m "🔍 铸渊PSP巡检 · $(date +%Y-%m-%d)" + git push diff --git a/.gitignore b/.gitignore index 27592c817d6e4658c948c5c0dbeba8fcfa56be6e..061a04ac74917c8f68c41c40d802a851ab24efe4 100644 GIT binary patch literal 34 ncmd1HPf3l>%}*)KNiEjr(#uP&DADIi$t(tvNu`-NDf(Oh*q95H literal 32 icmezWFOMOgA%!88A)X-@%qnHb0rH9&^ci>=xEKJQhzCLd diff --git a/dev-nodes/DEV-001/config.json b/dev-nodes/DEV-001/config.json new file mode 100644 index 00000000..4d686d03 --- /dev/null +++ b/dev-nodes/DEV-001/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-001", + "name": "页页", + "emoji": "🖥️", + "modules": ["backend-integration"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-001/status.json b/dev-nodes/DEV-001/status.json new file mode 100644 index 00000000..b36d602c --- /dev/null +++ b/dev-nodes/DEV-001/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-001", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-002/config.json b/dev-nodes/DEV-002/config.json new file mode 100644 index 00000000..3a265e41 --- /dev/null +++ b/dev-nodes/DEV-002/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-002", + "name": "肥猫", + "emoji": "🦁", + "modules": ["m01-login", "m03-personality"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-002/status.json b/dev-nodes/DEV-002/status.json new file mode 100644 index 00000000..d10a4412 --- /dev/null +++ b/dev-nodes/DEV-002/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-002", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-003/config.json b/dev-nodes/DEV-003/config.json new file mode 100644 index 00000000..b15b8bf3 --- /dev/null +++ b/dev-nodes/DEV-003/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-003", + "name": "燕樊", + "emoji": "🌸", + "modules": ["m07-dialogue-ui", "m10-cloud", "m15-cloud-drive"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-003/status.json b/dev-nodes/DEV-003/status.json new file mode 100644 index 00000000..79cb2923 --- /dev/null +++ b/dev-nodes/DEV-003/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-003", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-004/config.json b/dev-nodes/DEV-004/config.json new file mode 100644 index 00000000..4940e221 --- /dev/null +++ b/dev-nodes/DEV-004/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-004", + "name": "之之", + "emoji": "🤖", + "modules": ["dingtalk-bot"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-004/status.json b/dev-nodes/DEV-004/status.json new file mode 100644 index 00000000..a32080aa --- /dev/null +++ b/dev-nodes/DEV-004/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-004", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-005/config.json b/dev-nodes/DEV-005/config.json new file mode 100644 index 00000000..982d0f5c --- /dev/null +++ b/dev-nodes/DEV-005/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-005", + "name": "小草莓", + "emoji": "🍓", + "modules": ["m12-kanban", "status-board"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-005/status.json b/dev-nodes/DEV-005/status.json new file mode 100644 index 00000000..4fbac109 --- /dev/null +++ b/dev-nodes/DEV-005/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-005", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-009/config.json b/dev-nodes/DEV-009/config.json new file mode 100644 index 00000000..56ba40aa --- /dev/null +++ b/dev-nodes/DEV-009/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-009", + "name": "花尔", + "emoji": "🌺", + "modules": ["m05-user-center"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-009/status.json b/dev-nodes/DEV-009/status.json new file mode 100644 index 00000000..fae32904 --- /dev/null +++ b/dev-nodes/DEV-009/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-009", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-010/config.json b/dev-nodes/DEV-010/config.json new file mode 100644 index 00000000..2cbc23c9 --- /dev/null +++ b/dev-nodes/DEV-010/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-010", + "name": "桔子", + "emoji": "🍊", + "modules": ["m06-ticket", "m11-module"], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-010/status.json b/dev-nodes/DEV-010/status.json new file mode 100644 index 00000000..e79bf2e1 --- /dev/null +++ b/dev-nodes/DEV-010/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-010", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/DEV-011/config.json b/dev-nodes/DEV-011/config.json new file mode 100644 index 00000000..6de3b50c --- /dev/null +++ b/dev-nodes/DEV-011/config.json @@ -0,0 +1,10 @@ +{ + "dev_id": "DEV-011", + "name": "匆匆那年", + "emoji": "🌙", + "modules": [], + "pat_configured": false, + "network_scheme": "layer1_https", + "created_at": "2026-03-06T23:00:00+08:00", + "notes": "" +} diff --git a/dev-nodes/DEV-011/status.json b/dev-nodes/DEV-011/status.json new file mode 100644 index 00000000..58c35741 --- /dev/null +++ b/dev-nodes/DEV-011/status.json @@ -0,0 +1,9 @@ +{ + "dev_id": "DEV-011", + "last_seen": null, + "last_push": null, + "active_module": null, + "pending_broadcasts": 0, + "signal_count": 0, + "updated_at": "2026-03-06T23:00:00+08:00" +} diff --git a/dev-nodes/gl-push.sh b/dev-nodes/gl-push.sh new file mode 100644 index 00000000..fbce4cff --- /dev/null +++ b/dev-nodes/gl-push.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# gl-push.sh — 光湖智能上传脚本 · 三层网络降级方案 +# 用法: bash gl-push.sh "提交信息" +# 要求: git 已配置,PAT token 已通过 git config credential.helper store 存储 + +set -euo pipefail + +REPO_URL="https://github.com/qinfendebingshuo/guanghulab.git" +PROXY_URL="https://ghproxy.com/https://github.com/qinfendebingshuo/guanghulab.git" +MSG="${1:-"📤 开发者上传 $(date +%Y-%m-%dT%H:%M)"}" + +echo "🐙 光湖代码上传中..." +git add . +git commit -m "$MSG" 2>/dev/null || echo " ℹ️ 无新提交,直接推送" + +# ── 层级1:直连 HTTPS ────────────────────────────────────────────────────── +echo "🔗 [层级1] 尝试直连 GitHub..." +if git push origin main 2>/dev/null; then + echo "✅ 直连上传成功!铸渊已接收。" + exit 0 +fi + +# ── 层级2:ghproxy 镜像加速 ─────────────────────────────────────────────── +echo "⚠️ 直连失败,切换镜像通道..." +git remote set-url origin "$PROXY_URL" +if git push origin main 2>/dev/null; then + echo "✅ 镜像通道上传成功!铸渊已接收。" + git remote set-url origin "$REPO_URL" + exit 0 +fi +git remote set-url origin "$REPO_URL" + +# ── 层级3:SSH ──────────────────────────────────────────────────────────── +echo "⚠️ 镜像也失败,尝试 SSH 通道..." +SSH_URL="git@github.com:qinfendebingshuo/guanghulab.git" +git remote set-url origin "$SSH_URL" +if git push origin main; then + echo "✅ SSH 通道上传成功!铸渊已接收。" + git remote set-url origin "$REPO_URL" + exit 0 +fi +git remote set-url origin "$REPO_URL" + +# ── 全部失败 ────────────────────────────────────────────────────────────── +echo "❌ 三层网络均不通,请联系知秋处理。" +echo " 可尝试手动命令: git push git@github.com:qinfendebingshuo/guanghulab.git main" +exit 1 diff --git a/notion-push/pending/.gitkeep b/notion-push/pending/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/notion-push/processed/.gitkeep b/notion-push/processed/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/package-lock.json b/package-lock.json index bf5d24f3..ac32db90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,9 @@ "name": "nextjs", "version": "0.1.0", "dependencies": { + "imapflow": "^1.2.12", "next": "15.3.8", + "nodemailer": "^8.0.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -1644,6 +1646,12 @@ "node": ">= 10" } }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2161,6 +2169,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@zone-eu/mailsplit": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.8.tgz", + "integrity": "sha512-eEyACj4JZ7sjzRvy26QhLgKEMWwQbsw1+QZnlLX+/gihcNH07lVPOcnwf5U6UAL7gkc//J3jVd76o/WS+taUiA==", + "license": "(MIT OR EUPL-1.1+)", + "dependencies": { + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -2230,6 +2249,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -2955,6 +2983,15 @@ "dev": true, "license": "MIT" }, + "node_modules/encoding-japanese": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", + "license": "MIT", + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -3282,6 +3319,39 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/imapflow": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/imapflow/-/imapflow-1.2.12.tgz", + "integrity": "sha512-UX8qCKXZk2xExe/x8KPTSbhROdtUGP13bSLSjT9Sb3YwGuryD4aFNlGhbWBW5B1GtgHMRxVv9yvl61RqXgIQtQ==", + "license": "MIT", + "dependencies": { + "@zone-eu/mailsplit": "5.4.8", + "encoding-japanese": "2.2.0", + "iconv-lite": "0.7.2", + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1", + "nodemailer": "8.0.1", + "pino": "10.3.1", + "socks": "2.8.7" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -3331,6 +3401,15 @@ "dev": true, "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -4250,6 +4329,42 @@ "node": ">=6" } }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", + "license": "MIT" + }, + "node_modules/libmime": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", + "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", + "license": "MIT", + "dependencies": { + "encoding-japanese": "2.2.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.0", + "libqp": "2.1.1" + } + }, + "node_modules/libmime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libqp": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.29.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", @@ -4730,6 +4845,15 @@ "dev": true, "license": "MIT" }, + "node_modules/nodemailer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.1.tgz", + "integrity": "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4753,6 +4877,15 @@ "node": ">=8" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4940,6 +5073,43 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pino": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^4.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -5020,6 +5190,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5051,6 +5237,12 @@ ], "license": "MIT" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/react": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", @@ -5079,6 +5271,15 @@ "dev": true, "license": "MIT" }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5143,6 +5344,21 @@ "node": ">=10" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -5266,6 +5482,39 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5296,6 +5545,15 @@ "source-map": "^0.6.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5625,6 +5883,18 @@ "node": "*" } }, + "node_modules/thread-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz", + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 1820b151..6ddb8031 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,14 @@ "brain:sync": "node scripts/process-broadcasts.js", "brain:daily-check": "node scripts/daily-check.js", "brain:update-memory": "node scripts/update-memory.js", - "broadcast:distribute": "node scripts/distribute-broadcasts.js" + "broadcast:distribute": "node scripts/distribute-broadcasts.js", + "esp:process": "node scripts/esp-email-processor.js", + "psp:inspect": "node scripts/psp-inspection.js" }, "dependencies": { + "imapflow": "^1.2.12", "next": "15.3.8", + "nodemailer": "^8.0.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, diff --git a/scripts/esp-email-processor.js b/scripts/esp-email-processor.js new file mode 100644 index 00000000..cd5e7a92 --- /dev/null +++ b/scripts/esp-email-processor.js @@ -0,0 +1,360 @@ +// scripts/esp-email-processor.js +// ESP 邮件信号协议 · GitHub 端处理器 +// 功能:读取 Gmail IMAP → 解析 [GL-CMD] 信号 → 执行指令 → 写 signal-log → 发 [GL-ACK] + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const { ImapFlow } = require('imapflow'); +const nodemailer = require('nodemailer'); + +// ── 配置 ──────────────────────────────────────────────────────────────────── +const EMAIL_ADDRESS = process.env.EMAIL_ADDRESS; +const EMAIL_PASSWORD = process.env.EMAIL_APP_PASSWORD; + +if (!EMAIL_ADDRESS || !EMAIL_PASSWORD) { + console.error('❌ 缺少环境变量 EMAIL_ADDRESS 或 EMAIL_APP_PASSWORD'); + process.exit(1); +} + +const SIGNAL_LOG_DIR = path.join(__dirname, '../signal-log'); +const SIGNAL_INDEX_PATH = path.join(SIGNAL_LOG_DIR, 'index.json'); +const NOTION_PUSH_DIR = path.join(__dirname, '../notion-push/pending'); +const DEV_NODES_DIR = path.join(__dirname, '../dev-nodes'); + +// ── 信号 ID 生成 ────────────────────────────────────────────────────────── +function generateSignalId() { + const now = new Date(); + const ymd = now.toISOString().slice(0, 10).replace(/-/g, ''); + const index = loadSignalIndex(); + const seq = String(index.total_count + 1).padStart(3, '0'); + return `SIG-${ymd}-${seq}`; +} + +// ── 信号索引读写 ────────────────────────────────────────────────────────── +function loadSignalIndex() { + if (!fs.existsSync(SIGNAL_INDEX_PATH)) { + return { last_updated: new Date().toISOString(), total_count: 0, signals: [] }; + } + return JSON.parse(fs.readFileSync(SIGNAL_INDEX_PATH, 'utf8')); +} + +function saveSignalIndex(index) { + index.last_updated = new Date().toISOString(); + fs.writeFileSync(SIGNAL_INDEX_PATH, JSON.stringify(index, null, 2)); +} + +// ── 写入信号日志文件 ────────────────────────────────────────────────────── +function writeSignalLog(signal) { + const dateStr = signal.timestamp.slice(0, 7); // "YYYY-MM" + const monthDir = path.join(SIGNAL_LOG_DIR, dateStr); + fs.mkdirSync(monthDir, { recursive: true }); + + const filePath = path.join(monthDir, `${signal.signal_id}.json`); + fs.writeFileSync(filePath, JSON.stringify(signal, null, 2)); + + const index = loadSignalIndex(); + index.total_count += 1; + index.signals.unshift({ + signal_id: signal.signal_id, + trace_id: signal.trace_id, + type: signal.signal_type, + timestamp: signal.timestamp, + summary: signal.summary, + related_dev: signal.related_dev || null, + file: `${dateStr}/${signal.signal_id}.json` + }); + saveSignalIndex(index); + + console.log(`📝 信号已写入: ${signal.signal_id}`); +} + +// ── 写入 notion-push/pending ────────────────────────────────────────────── +function pushToNotion(signal) { + fs.mkdirSync(NOTION_PUSH_DIR, { recursive: true }); + const filePath = path.join(NOTION_PUSH_DIR, `${signal.signal_id}.json`); + fs.writeFileSync(filePath, JSON.stringify(signal, null, 2)); + console.log(`📤 已写入 notion-push/pending: ${signal.signal_id}`); +} + +// ── SMTP 发送 ACK 邮件 ──────────────────────────────────────────────────── +async function sendAckEmail(originalSignal, ackSignalId, result) { + const transporter = nodemailer.createTransport({ + host: 'smtp.gmail.com', + port: 587, + secure: false, + auth: { user: EMAIL_ADDRESS, pass: EMAIL_PASSWORD } + }); + + const ackPayload = { + signal_type: 'GL-ACK', + trace_id: originalSignal.trace_id, + ack_signal_id: ackSignalId, + original_cmd_id: originalSignal.signal_id, + timestamp: new Date().toISOString(), + sender: '铸渊', + receiver: '霜砚', + original_command: originalSignal.command, + result, + message: `指令 ${originalSignal.command} 执行${result === '成功' ? '完成' : '失败'}` + }; + + await transporter.sendMail({ + from: EMAIL_ADDRESS, + to: EMAIL_ADDRESS, + subject: `[GL-ACK] ${originalSignal.trace_id} | ${originalSignal.command} ${result}`, + text: JSON.stringify(ackPayload, null, 2) + }); + + console.log(`📧 [GL-ACK] 已发送: ${ackSignalId}`); + return ackPayload; +} + +// ── 指令执行器 ──────────────────────────────────────────────────────────── +async function executeCommand(signal) { + const { command, payload } = signal; + console.log(`⚙️ 执行指令: ${command}`); + + switch (command) { + case 'sync_broadcast': { + // 将 broadcasts-outbox/ 中的广播同步到对应 dev-nodes/ + const outboxDir = path.join(__dirname, '../broadcasts-outbox'); + if (!fs.existsSync(outboxDir)) { + return { result: '失败', detail: 'broadcasts-outbox 目录不存在' }; + } + const devDirs = fs.readdirSync(outboxDir).filter(f => f.startsWith('DEV-')); + let synced = 0; + for (const devId of devDirs) { + const srcDir = path.join(outboxDir, devId); + const destDir = path.join(DEV_NODES_DIR, devId); + if (!fs.existsSync(destDir)) continue; + const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.json')); + for (const file of files) { + const src = path.join(srcDir, file); + const dest = path.join(destDir, file); + if (!fs.existsSync(dest)) { + fs.copyFileSync(src, dest); + synced++; + } + } + // 更新 status.json 中的 pending_broadcasts + const statusPath = path.join(destDir, 'status.json'); + if (fs.existsSync(statusPath)) { + const status = JSON.parse(fs.readFileSync(statusPath, 'utf8')); + status.pending_broadcasts = files.length; + status.updated_at = new Date().toISOString(); + fs.writeFileSync(statusPath, JSON.stringify(status, null, 2)); + } + } + return { result: '成功', detail: `已同步 ${synced} 个广播文件` }; + } + + case 'check_syslog': { + const syslogInbox = path.join(__dirname, '../syslog-inbox'); + const files = fs.existsSync(syslogInbox) + ? fs.readdirSync(syslogInbox).filter(f => f.endsWith('.json')) + : []; + const summary = `syslog-inbox 待处理: ${files.length} 条`; + pushToNotion({ + signal_id: generateSignalId(), + trace_id: signal.trace_id, + timestamp: new Date().toISOString(), + signal_type: 'GL-DATA', + direction: 'GitHub→Notion', + sender: '铸渊', + receiver: '霜砚', + related_dev: null, + related_module: null, + summary, + payload: { syslog_pending: files.length, files } + }); + return { result: '成功', detail: summary }; + } + + case 'update_status': { + const { dev_id, updates } = payload || {}; + if (!dev_id) return { result: '失败', detail: '缺少 dev_id' }; + const statusPath = path.join(DEV_NODES_DIR, dev_id, 'status.json'); + if (!fs.existsSync(statusPath)) { + return { result: '失败', detail: `${dev_id} status.json 不存在` }; + } + let status; + try { + status = JSON.parse(fs.readFileSync(statusPath, 'utf8')); + } catch (err) { + return { result: '失败', detail: `${dev_id} status.json 解析失败: ${err.message}` }; + } + Object.assign(status, updates, { updated_at: new Date().toISOString() }); + fs.writeFileSync(statusPath, JSON.stringify(status, null, 2)); + return { result: '成功', detail: `${dev_id} 状态已更新` }; + } + + default: + return { result: '失败', detail: `未知指令: ${command}` }; + } +} + +// ── 解析邮件正文中的 JSON 信号 ──────────────────────────────────────────── +function parseSignalFromEmail(subject, body) { + if (!subject.includes('[GL-CMD]')) return null; + try { + // 提取第一个完整 JSON 对象(跳过邮件头中的任何前缀内容) + const jsonMatch = body.match(/\{[\s\S]*\}/); + if (!jsonMatch) return null; + const parsed = JSON.parse(jsonMatch[0]); + if (!parsed.signal_type || !parsed.trace_id || !parsed.command) return null; + return parsed; + } catch (err) { + console.log(`⚠️ 邮件 JSON 解析失败: ${err.message}`); + return null; + } +} + +// ── 主流程 ──────────────────────────────────────────────────────────────── +async function main() { + console.log('═══════════════════════════════════════════════'); + console.log('📡 ESP 邮件信号处理器启动'); + console.log(` 时间: ${new Date().toISOString()}`); + console.log('═══════════════════════════════════════════════\n'); + + const client = new ImapFlow({ + host: 'imap.gmail.com', + port: 993, + secure: true, + auth: { user: EMAIL_ADDRESS, pass: EMAIL_PASSWORD }, + logger: false + }); + + await client.connect(); + + let processed = 0; + let errors = 0; + + try { + const lock = await client.getMailboxLock('INBOX'); + try { + // 搜索未读的 [GL-CMD] 邮件 + const messages = await client.search({ seen: false, subject: '[GL-CMD]' }); + + if (!messages || messages.length === 0) { + console.log('📭 无待处理 [GL-CMD] 邮件'); + } else { + console.log(`📬 发现 ${messages.length} 封 [GL-CMD] 邮件`); + + for await (const msg of client.fetch(messages, { envelope: true, source: true })) { + const subject = msg.envelope?.subject || ''; + const rawBody = msg.source?.toString('utf8') || ''; + + // 提取 JSON 正文(跳过邮件头) + const bodyStart = rawBody.indexOf('{'); + const body = bodyStart >= 0 ? rawBody.slice(bodyStart) : ''; + + const signal = parseSignalFromEmail(subject, body); + if (!signal) { + console.log(`⚠️ 跳过无效邮件: ${subject}`); + continue; + } + + console.log(`\n📩 处理信号: ${signal.trace_id} · ${signal.command}`); + + // 生成信号 ID(如邮件中未提供) + if (!signal.signal_id) signal.signal_id = generateSignalId(); + + // 写入 signal-log + const cmdSignal = { + signal_id: signal.signal_id, + trace_id: signal.trace_id, + timestamp: signal.timestamp || new Date().toISOString(), + signal_type: 'GL-CMD', + direction: 'Notion→GitHub', + sender: signal.sender || '霜砚', + receiver: '铸渊', + related_dev: signal.payload?.dev_id || null, + related_module: signal.payload?.module || null, + summary: `执行指令 ${signal.command}`, + payload: signal.payload || {}, + result: null, + ack_signal_id: null + }; + writeSignalLog(cmdSignal); + + // 执行指令 + let execResult; + try { + execResult = await executeCommand(signal); + } catch (err) { + execResult = { result: '失败', detail: err.message }; + } + + cmdSignal.result = execResult.result; + + // 发送 ACK 邮件并写入 ACK 信号日志 + const ackSignalId = generateSignalId(); + cmdSignal.ack_signal_id = ackSignalId; + + try { + const ackPayload = await sendAckEmail( + { ...signal, signal_id: cmdSignal.signal_id }, + ackSignalId, + execResult.result + ); + writeSignalLog({ + signal_id: ackSignalId, + trace_id: signal.trace_id, + timestamp: new Date().toISOString(), + signal_type: 'GL-ACK', + direction: 'GitHub→Notion', + sender: '铸渊', + receiver: '霜砚', + related_dev: cmdSignal.related_dev, + related_module: cmdSignal.related_module, + summary: `ACK: ${signal.command} ${execResult.result}`, + payload: ackPayload, + result: execResult.result, + ack_signal_id: null + }); + pushToNotion({ + signal_id: ackSignalId, + trace_id: signal.trace_id, + timestamp: new Date().toISOString(), + signal_type: 'GL-ACK', + direction: 'GitHub→Notion', + sender: '铸渊', + receiver: '霜砚', + related_dev: cmdSignal.related_dev, + related_module: cmdSignal.related_module, + summary: `ACK: ${signal.command} ${execResult.result}`, + payload: ackPayload + }); + } catch (err) { + console.error(`❌ 发送 ACK 邮件失败: ${err.message}`); + errors++; + } + + // 标记邮件为已读 + await client.messageFlagsAdd(msg.seq, ['\\Seen']); + + processed++; + console.log(`✅ 信号处理完成: ${signal.trace_id} → ${execResult.result}`); + if (execResult.detail) console.log(` 详情: ${execResult.detail}`); + } + } + } finally { + lock.release(); + } + } finally { + await client.logout(); + } + + console.log(`\n═══════════════════════════════════════════════`); + console.log(`📡 ESP 处理完毕 · 处理: ${processed} · 错误: ${errors}`); + console.log(`═══════════════════════════════════════════════`); + + if (errors > 0) process.exit(1); +} + +main().catch(err => { + console.error('❌ ESP 处理器致命错误:', err.message); + process.exit(1); +}); diff --git a/scripts/psp-inspection.js b/scripts/psp-inspection.js new file mode 100644 index 00000000..c92226be --- /dev/null +++ b/scripts/psp-inspection.js @@ -0,0 +1,393 @@ +// scripts/psp-inspection.js +// 铸渊 PSP 分身巡检 · 每日自动检查清单 +// CHK-G01 信号完整性 / CHK-G02 dev-nodes 一致性 / CHK-G03 notion-push 清理 +// CHK-G04 broadcasts 投递 / CHK-G05 CI 健康 + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const nodemailer = require('nodemailer'); + +// ── 目录配置 ─────────────────────────────────────────────────────────────── +const ROOT = path.join(__dirname, '..'); +const SIGNAL_LOG_DIR = path.join(ROOT, 'signal-log'); +const SIGNAL_INDEX = path.join(SIGNAL_LOG_DIR, 'index.json'); +const DEV_NODES_DIR = path.join(ROOT, 'dev-nodes'); +const NOTION_PENDING = path.join(ROOT, 'notion-push/pending'); +const BROADCASTS_DIR = path.join(ROOT, 'broadcasts-outbox'); +const MEMORY_PATH = path.join(ROOT, '.github/brain/memory.json'); + +const EMAIL_ADDRESS = process.env.EMAIL_ADDRESS; +const EMAIL_PASSWORD = process.env.EMAIL_APP_PASSWORD; +const GITHUB_TOKEN = process.env.GITHUB_TOKEN; +const REPO = process.env.GITHUB_REPOSITORY || 'qinfendebingshuo/guanghulab'; + +const now = new Date(); +const today = now.toISOString().split('T')[0]; +const results = {}; +const issues = []; +const autoFixed = []; +const MS_PER_HOUR = 3600000; +const PSP_TRACE_ID = `TRC-${today.replace(/-/g, '')}-PSP`; + +// ── 工具函数 ─────────────────────────────────────────────────────────────── +function hoursAgo(isoStr) { + if (!isoStr) return Infinity; + return (Date.now() - new Date(isoStr).getTime()) / MS_PER_HOUR; +} + +function loadJson(filePath) { + if (!fs.existsSync(filePath)) return null; + try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return null; } +} + +function saveJson(filePath, data) { + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); +} + +// ── 信号 ID 生成 ─────────────────────────────────────────────────────────── +function generateSignalId() { + const ymd = today.replace(/-/g, ''); + const index = loadJson(SIGNAL_INDEX) || { total_count: 0 }; + const seq = String(index.total_count + 1).padStart(3, '0'); + return `SIG-${ymd}-${seq}`; +} + +// ── 写信号日志 ───────────────────────────────────────────────────────────── +function writeSignalLog(signal) { + const dateStr = signal.timestamp.slice(0, 7); + const dir = path.join(SIGNAL_LOG_DIR, dateStr); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(path.join(dir, `${signal.signal_id}.json`), JSON.stringify(signal, null, 2)); + + const index = loadJson(SIGNAL_INDEX) || { last_updated: '', total_count: 0, signals: [] }; + index.total_count += 1; + index.last_updated = now.toISOString(); + index.signals.unshift({ + signal_id: signal.signal_id, + trace_id: signal.trace_id, + type: signal.signal_type, + timestamp: signal.timestamp, + summary: signal.summary, + related_dev: signal.related_dev || null, + file: `${dateStr}/${signal.signal_id}.json` + }); + saveJson(SIGNAL_INDEX, index); +} + +// ── 发送 GL-DATA 通知邮件 ────────────────────────────────────────────────── +async function sendDataEmail(subject, payload) { + if (!EMAIL_ADDRESS || !EMAIL_PASSWORD) { + console.log('⚠️ EMAIL 未配置,跳过邮件发送'); + return; + } + const transport = nodemailer.createTransport({ + host: 'smtp.gmail.com', + port: 587, + secure: false, + auth: { user: EMAIL_ADDRESS, pass: EMAIL_PASSWORD } + }); + await transport.sendMail({ + from: EMAIL_ADDRESS, + to: EMAIL_ADDRESS, + subject: `[GL-DATA] ${subject}`, + text: JSON.stringify(payload, null, 2) + }); + console.log(`📧 [GL-DATA] 已发送: ${subject}`); +} + +// ── CHK-G01 信号收发完整性 ───────────────────────────────────────────────── +function checkG01() { + console.log('\n─── CHK-G01 信号收发完整性 ───'); + const index = loadJson(SIGNAL_INDEX); + if (!index) { + results.G01 = '⚠️'; + issues.push('CHK-G01: signal-log/index.json 不存在'); + return; + } + + const cutoff = Date.now() - 24 * MS_PER_HOUR; + const recent = index.signals.filter(s => new Date(s.timestamp).getTime() > cutoff); + const cmds = recent.filter(s => s.type === 'GL-CMD'); + const acks = new Set(recent.filter(s => s.type === 'GL-ACK').map(s => s.trace_id)); + const unacked = cmds.filter(s => !acks.has(s.trace_id)); + + if (unacked.length > 0) { + results.G01 = '❌'; + unacked.forEach(s => issues.push(`CHK-G01: 未收到 ACK · trace_id=${s.trace_id} · ${s.summary}`)); + } else { + results.G01 = '✅'; + console.log(` ✅ 最近24h CMD: ${cmds.length} · ACK 全部到位`); + } +} + +// ── CHK-G02 dev-nodes 状态一致性 ────────────────────────────────────────── +function checkG02() { + console.log('\n─── CHK-G02 dev-nodes 状态一致性 ───'); + if (!fs.existsSync(DEV_NODES_DIR)) { + results.G02 = '❌'; + issues.push('CHK-G02: dev-nodes/ 目录不存在'); + return; + } + + const devDirs = fs.readdirSync(DEV_NODES_DIR).filter(d => d.startsWith('DEV-')); + let allOk = true; + + for (const devId of devDirs) { + const statusPath = path.join(DEV_NODES_DIR, devId, 'status.json'); + const configPath = path.join(DEV_NODES_DIR, devId, 'config.json'); + if (!fs.existsSync(statusPath) || !fs.existsSync(configPath)) { + issues.push(`CHK-G02: ${devId} 缺少 status.json 或 config.json`); + allOk = false; + continue; + } + const status = loadJson(statusPath); + if (!status) { + issues.push(`CHK-G02: ${devId} status.json 解析失败`); + allOk = false; + } + } + + results.G02 = allOk ? '✅' : '⚠️'; + if (allOk) console.log(` ✅ ${devDirs.length} 个开发者节点状态文件完整`); +} + +// ── CHK-G03 notion-push 清理 ─────────────────────────────────────────────── +async function checkG03() { + console.log('\n─── CHK-G03 notion-push/pending 清理 ───'); + if (!fs.existsSync(NOTION_PENDING)) { + results.G03 = '✅'; + console.log(' ✅ notion-push/pending 目录为空或不存在'); + return; + } + + const files = fs.readdirSync(NOTION_PENDING).filter(f => f.endsWith('.json')); + const stale = files.filter(f => { + const stat = fs.statSync(path.join(NOTION_PENDING, f)); + return (Date.now() - stat.mtimeMs) > 24 * MS_PER_HOUR; + }); + + if (stale.length > 0) { + results.G03 = '⚠️'; + issues.push(`CHK-G03: ${stale.length} 个 notion-push/pending 文件超过24h未处理: ${stale.join(', ')}`); + // 发通知 + try { + await sendDataEmail('notion-push/pending 文件积压提醒', { + type: 'GL-CMD', + sender: '铸渊', + command: 'alert_pending_files', + payload: { stale_files: stale, count: stale.length } + }); + } catch (err) { + console.error(` ⚠️ 发送通知失败: ${err.message}`); + } + } else { + results.G03 = '✅'; + console.log(` ✅ pending: ${files.length} 个文件,无超期`); + } +} + +// ── CHK-G04 broadcasts 投递确认 ─────────────────────────────────────────── +function checkG04() { + console.log('\n─── CHK-G04 broadcasts-outbox 投递确认 ───'); + if (!fs.existsSync(BROADCASTS_DIR)) { + results.G04 = '⚠️'; + issues.push('CHK-G04: broadcasts-outbox/ 不存在'); + return; + } + + const devDirs = fs.readdirSync(BROADCASTS_DIR).filter(d => d.startsWith('DEV-')); + let allSynced = true; + + for (const devId of devDirs) { + const srcDir = path.join(BROADCASTS_DIR, devId); + const destDir = path.join(DEV_NODES_DIR, devId); + if (!fs.existsSync(destDir)) continue; + + const broadcasts = fs.readdirSync(srcDir).filter(f => f.endsWith('.json')); + for (const file of broadcasts) { + const dest = path.join(destDir, file); + if (!fs.existsSync(dest)) { + // 自动复制 + fs.copyFileSync(path.join(srcDir, file), dest); + autoFixed.push(`CHK-G04: 自动投递广播 ${devId}/${file}`); + console.log(` 🔧 自动投递: ${devId}/${file}`); + allSynced = false; + } + } + } + + results.G04 = '✅'; + if (allSynced) { + console.log(' ✅ 所有广播已同步至 dev-nodes/'); + } else { + console.log(` ✅ 已自动修复 ${autoFixed.length} 个广播投递`); + } +} + +// ── CHK-G05 CI 健康检查 ─────────────────────────────────────────────────── +async function checkG05() { + console.log('\n─── CHK-G05 CI 健康检查 ───'); + if (!GITHUB_TOKEN) { + results.G05 = '⚠️'; + console.log(' ⚠️ GITHUB_TOKEN 未配置,跳过 CI 检查'); + return; + } + + return new Promise((resolve) => { + const [owner, repoName] = REPO.split('/'); + const options = { + hostname: 'api.github.com', + path: `/repos/${owner}/${repoName}/actions/runs?per_page=20`, + method: 'GET', + headers: { + 'Authorization': `Bearer ${GITHUB_TOKEN}`, + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'zhuyuan-psp-inspector' + } + }; + + const req = https.request(options, (res) => { + let data = ''; + res.on('data', chunk => { data += chunk; }); + res.on('end', () => { + try { + const body = JSON.parse(data); + const runs = body.workflow_runs || []; + const cutoff = Date.now() - 24 * MS_PER_HOUR; + const recent = runs.filter(r => new Date(r.created_at).getTime() > cutoff); + const failed = recent.filter(r => r.conclusion === 'failure'); + + if (failed.length > 0) { + results.G05 = '❌'; + failed.forEach(r => { + issues.push(`CHK-G05: CI 失败 · ${r.name} · ${r.html_url}`); + }); + // 写入 signal-log + const signalId = generateSignalId(); + writeSignalLog({ + signal_id: signalId, + trace_id: PSP_TRACE_ID, + timestamp: now.toISOString(), + signal_type: 'GL-DATA', + direction: 'GitHub→Notion', + sender: '铸渊', + receiver: '霜砚', + related_dev: null, + related_module: null, + summary: `CHK-G05: ${failed.length} 个 CI 失败`, + payload: { failed_runs: failed.map(r => ({ name: r.name, url: r.html_url })) }, + result: '待处理', + ack_signal_id: null + }); + } else { + results.G05 = '✅'; + console.log(` ✅ 最近24h CI: ${recent.length} 次运行,全部通过`); + } + } catch (err) { + results.G05 = '⚠️'; + console.log(` ⚠️ CI 数据解析失败: ${err.message}`); + } + resolve(); + }); + }); + + req.on('error', (err) => { + results.G05 = '⚠️'; + console.log(` ⚠️ GitHub API 请求失败: ${err.message}`); + resolve(); + }); + + req.end(); + }); +} + +// ── 更新 memory.json ─────────────────────────────────────────────────────── +function updateMemory(summary) { + const memory = loadJson(MEMORY_PATH); + if (!memory) return; + + if (!memory.events) memory.events = []; + memory.events.unshift({ + date: today, + type: 'psp_inspection', + description: summary, + by: '铸渊PSP巡检' + }); + if (memory.events.length > 50) memory.events = memory.events.slice(0, 50); + memory.last_updated = now.toISOString(); + + saveJson(MEMORY_PATH, memory); +} + +// ── 写入巡检结果到 signal-log ────────────────────────────────────────────── +function writeInspectionSignal(allPassed) { + const signalId = generateSignalId(); + const summary = allPassed + ? '铸渊 PSP 巡检通过 · 全部检查项 ✅' + : `铸渊 PSP 巡检完成 · 发现 ${issues.length} 个问题 · 自动修复 ${autoFixed.length} 项`; + + writeSignalLog({ + signal_id: signalId, + trace_id: PSP_TRACE_ID, + timestamp: now.toISOString(), + signal_type: 'GL-DATA', + direction: 'GitHub→Notion', + sender: '铸渊', + receiver: '霜砚', + related_dev: null, + related_module: null, + summary, + payload: { + check_results: results, + issues, + auto_fixed: autoFixed + }, + result: allPassed ? '全通过' : '有问题', + ack_signal_id: null + }); + + return summary; +} + +// ── 主流程 ──────────────────────────────────────────────────────────────── +async function main() { + console.log('═══════════════════════════════════════════════════'); + console.log('🔍 铸渊 PSP 分身巡检启动'); + console.log(` 日期: ${today}`); + console.log('═══════════════════════════════════════════════════'); + + checkG01(); + checkG02(); + await checkG03(); + checkG04(); + await checkG05(); + + const allPassed = Object.values(results).every(v => v === '✅'); + const summary = writeInspectionSignal(allPassed); + updateMemory(summary); + + console.log('\n═══════════════════════════════════════════════════'); + console.log('📊 PSP 巡检报告:'); + Object.entries(results).forEach(([k, v]) => console.log(` ${v} CHK-${k}`)); + + if (autoFixed.length > 0) { + console.log('\n🔧 自动修复:'); + autoFixed.forEach(f => console.log(` · ${f}`)); + } + + if (issues.length > 0) { + console.log('\n⚠️ 待处理问题:'); + issues.forEach(i => console.log(` · ${i}`)); + } + + console.log(`\n${allPassed ? '✅ 铸渊 PSP 巡检全部通过' : '⚠️ 铸渊 PSP 巡检发现问题,已记录'}`); + console.log('═══════════════════════════════════════════════════'); +} + +main().catch(err => { + console.error('❌ PSP 巡检致命错误:', err.message); + process.exit(1); +}); diff --git a/scripts/zhuyuan-daily-selfcheck.js b/scripts/zhuyuan-daily-selfcheck.js index 90d988bf..1f197fed 100644 --- a/scripts/zhuyuan-daily-selfcheck.js +++ b/scripts/zhuyuan-daily-selfcheck.js @@ -33,12 +33,15 @@ if (missing.length > 0) { } // === ① bis · 目录结构巡检 === -const requiredDirs = ['syslog-inbox', 'syslog-processed', 'broadcasts-outbox', '.github/brain']; +const requiredDirs = [ + 'syslog-inbox', 'syslog-processed', 'broadcasts-outbox', '.github/brain', + 'signal-log', 'dev-nodes', 'notion-push/pending', 'notion-push/processed' +]; const missingDirs = requiredDirs.filter(d => !fs.existsSync(d)); if (missingDirs.length > 0) { console.error('⚠️ 缺失目录:' + missingDirs.join(', ')); } else { - console.log('✅ 目录结构巡检:syslog-inbox / syslog-processed / broadcasts-outbox 全部就绪'); + console.log('✅ 目录结构巡检:syslog-inbox / syslog-processed / broadcasts-outbox / signal-log / dev-nodes / notion-push 全部就绪'); } // 统计 syslog-inbox 待处理条目 diff --git a/signal-log/2026-03/SIG-2026-0306-001.json b/signal-log/2026-03/SIG-2026-0306-001.json new file mode 100644 index 00000000..dc7cfb47 --- /dev/null +++ b/signal-log/2026-03/SIG-2026-0306-001.json @@ -0,0 +1,18 @@ +{ + "signal_id": "SIG-2026-0306-001", + "trace_id": "TRC-2026-0306-001", + "timestamp": "2026-03-06T23:00:00+08:00", + "signal_type": "GL-CMD", + "direction": "Notion→GitHub", + "sender": "霜砚", + "receiver": "铸渊", + "related_dev": null, + "related_module": null, + "summary": "AGE OS 系统初始化 · 铸渊 GitHub 端激活", + "payload": { + "command": "system_init", + "version": "1.0" + }, + "result": "成功", + "ack_signal_id": null +} diff --git a/signal-log/2026-03/SIG-20260307-002.json b/signal-log/2026-03/SIG-20260307-002.json new file mode 100644 index 00000000..fc57a25d --- /dev/null +++ b/signal-log/2026-03/SIG-20260307-002.json @@ -0,0 +1,27 @@ +{ + "signal_id": "SIG-20260307-002", + "trace_id": "TRC-20260307-PSP", + "timestamp": "2026-03-07T04:45:55.670Z", + "signal_type": "GL-DATA", + "direction": "GitHub→Notion", + "sender": "铸渊", + "receiver": "霜砚", + "related_dev": null, + "related_module": null, + "summary": "铸渊 PSP 巡检完成 · 发现 1 个问题 · 自动修复 0 项", + "payload": { + "check_results": { + "G01": "❌", + "G02": "✅", + "G03": "✅", + "G04": "✅", + "G05": "⚠️" + }, + "issues": [ + "CHK-G01: 未收到 ACK · trace_id=TRC-2026-0306-001 · AGE OS 系统初始化 · 铸渊 GitHub 端激活" + ], + "auto_fixed": [] + }, + "result": "有问题", + "ack_signal_id": null +} \ No newline at end of file diff --git a/signal-log/index.json b/signal-log/index.json new file mode 100644 index 00000000..76156707 --- /dev/null +++ b/signal-log/index.json @@ -0,0 +1,25 @@ +{ + "description": "铸渊信号日志目录索引 · AGE OS ESP 邮件信号协议", + "last_updated": "2026-03-07T04:45:55.670Z", + "total_count": 2, + "signals": [ + { + "signal_id": "SIG-20260307-002", + "trace_id": "TRC-20260307-PSP", + "type": "GL-DATA", + "timestamp": "2026-03-07T04:45:55.670Z", + "summary": "铸渊 PSP 巡检完成 · 发现 1 个问题 · 自动修复 0 项", + "related_dev": null, + "file": "2026-03/SIG-20260307-002.json" + }, + { + "signal_id": "SIG-2026-0306-001", + "trace_id": "TRC-2026-0306-001", + "type": "GL-CMD", + "timestamp": "2026-03-06T23:00:00+08:00", + "summary": "AGE OS 系统初始化 · 铸渊 GitHub 端激活", + "related_dev": null, + "file": "2026-03/SIG-2026-0306-001.json" + } + ] +} \ No newline at end of file From 9dc220441fffe8c29e185fa89fbdb0a5712ea044 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 05:38:07 +0000 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E9=93=B8=E6=B8=8A=E4=BA=BA?= =?UTF-8?q?=E6=A0=BC=E7=B3=BB=E7=BB=9F=20v2.0=20=E2=80=94=20=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E5=B8=B8=E5=BC=80=E4=BA=BA=E6=A0=BC=20+=20=E5=9B=BE?= =?UTF-8?q?=E4=B9=A6=E9=A6=86=E7=9B=AE=E5=BD=95=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/brain/repo-map.json | 1274 +++++++++++++++++ .github/brain/repo-snapshot.md | 209 +++ .github/brain/wake-protocol.md | 269 ++-- .github/copilot-instructions.md | 124 +- .github/workflows/update-repo-map.yml | 37 + .github/workflows/zhuyuan-daily-selfcheck.yml | 5 +- package.json | 3 +- scripts/generate-repo-map.js | 529 +++++++ 8 files changed, 2253 insertions(+), 197 deletions(-) create mode 100644 .github/brain/repo-map.json create mode 100644 .github/brain/repo-snapshot.md create mode 100644 .github/workflows/update-repo-map.yml create mode 100644 scripts/generate-repo-map.js diff --git a/.github/brain/repo-map.json b/.github/brain/repo-map.json new file mode 100644 index 00000000..fddc91df --- /dev/null +++ b/.github/brain/repo-map.json @@ -0,0 +1,1274 @@ +{ + "description": "铸渊图书馆目录 · Library Catalog for 铸渊 (Zhùyuān)", + "version": "2.0", + "generated_at": "2026-03-07T05:37:56.254Z", + "generated_by": "scripts/generate-repo-map.js", + "repo": "qinfendebingshuo/guanghulab", + "stats": { + "zones": 13, + "total_modules": 10, + "total_workflows": 14, + "total_scripts": 15, + "total_dev_nodes": 8, + "hli_interfaces": 17, + "hli_implemented": 3, + "hli_coverage_pct": "18%", + "last_ci_run": "2026-03-05T10:30:46.894Z", + "memory_last_updated": "2026-03-07T04:45:55.670Z" + }, + "zones": [ + { + "zone_id": "BRAIN", + "name": "铸渊大脑", + "emoji": "🧠", + "path": ".github/brain", + "description": "铸渊核心记忆 · 路由映射 · 唤醒协议 · 图书馆目录", + "keywords": [ + "brain", + "memory", + "routing", + "wake", + "大脑", + "记忆", + "路由" + ], + "shelves": [ + { + "file": "collaborators.json" + }, + { + "file": "growth-log.md" + }, + { + "file": "memory.json" + }, + { + "file": "repo-map.json" + }, + { + "file": "repo-snapshot.md" + }, + { + "file": "routing-map.json" + }, + { + "file": "wake-protocol.md" + } + ], + "item_count": 7 + }, + { + "zone_id": "PERSONA_BRAIN", + "name": "人格大脑", + "emoji": "🎭", + "path": ".github/persona-brain", + "description": "铸渊人格记忆 · 开发者状态 · 知识库 · 成长日记", + "keywords": [ + "persona", + "identity", + "dev-status", + "人格", + "开发者状态" + ], + "shelves": [ + { + "file": "dev-status.json" + }, + { + "file": "growth-journal.md" + }, + { + "file": "knowledge-base.json" + }, + { + "file": "memory.json" + } + ], + "item_count": 4 + }, + { + "zone_id": "WORKFLOWS", + "name": "自动化工作流", + "emoji": "⚡", + "path": ".github/workflows", + "description": "所有 GitHub Actions 工作流定义", + "keywords": [ + "workflow", + "actions", + "ci", + "automation", + "工作流", + "自动化", + "cron" + ], + "shelves": [ + { + "file": "brain-sync.yml", + "name": "铸渊 Brain Sync", + "triggers": [ + "push", + "schedule(0 8 * * *)", + "manual" + ] + }, + { + "file": "bridge-changes-to-notion.yml", + "name": "铸渊 · Bridge E · GitHub Changes → Notion", + "triggers": [ + "push", + "pull_request" + ] + }, + { + "file": "bridge-syslog-to-notion.yml", + "name": "铸渊 · Bridge A · SYSLOG → Notion", + "triggers": [ + "push", + "manual" + ] + }, + { + "file": "check-structure.yml", + "name": "模块结构检查", + "triggers": [ + "push", + "pull_request" + ] + }, + { + "file": "deploy-pages.yml", + "name": "🌀 部署铸渊聊天室 (GitHub Pages)", + "triggers": [ + "push", + "manual" + ] + }, + { + "file": "distribute-broadcasts.yml", + "name": "铸渊 · 广播分发", + "triggers": [ + "push", + "manual" + ] + }, + { + "file": "esp-signal-processor.yml", + "name": "铸渊 · ESP 邮件信号处理器", + "triggers": [ + "schedule(*/30 * * * *)", + "manual" + ] + }, + { + "file": "generate-module-doc.yml", + "name": "铸渊 · HoloLake Era 模块文档自动生成", + "triggers": [ + "push", + "manual" + ] + }, + { + "file": "hli-contract-check.yml", + "name": "HLI Contract Check", + "triggers": [ + "push", + "pull_request" + ] + }, + { + "file": "psp-daily-inspection.yml", + "name": "铸渊 · PSP 分身巡检", + "triggers": [ + "schedule(0 1 * * *)", + "manual" + ] + }, + { + "file": "syslog-pipeline.yml", + "name": "铸渊 · SYSLOG Pipeline (A/D/E)", + "triggers": [ + "push", + "manual" + ] + }, + { + "file": "update-repo-map.yml", + "name": "铸渊 · 图书馆目录自动更新", + "triggers": [ + "push", + "schedule(0 0 * * *)", + "manual" + ] + }, + { + "file": "zhuyuan-daily-selfcheck.yml", + "name": "铸渊 · 每日自检与自进化", + "triggers": [ + "schedule(0 0 * * *)", + "manual" + ] + }, + { + "file": "zhuyuan-issue-reply.yml", + "name": "铸渊 · Issue 自动回复", + "triggers": [ + "issues" + ] + } + ], + "item_count": 14 + }, + { + "zone_id": "SCRIPTS", + "name": "执行脚本库", + "emoji": "🔧", + "path": "scripts", + "description": "铸渊所有执行手脚 · 自动化脚本", + "keywords": [ + "script", + "node", + "js", + "脚本", + "执行", + "runner" + ], + "shelves": [ + { + "file": "contract-check.js" + }, + { + "file": "daily-check.js" + }, + { + "file": "distribute-broadcasts.js" + }, + { + "file": "esp-email-processor.js" + }, + { + "file": "generate-module-doc.js" + }, + { + "file": "generate-repo-map.js" + }, + { + "file": "notify-module-received.js" + }, + { + "file": "notion-bridge.js" + }, + { + "file": "process-broadcasts.js" + }, + { + "file": "process-syslog.js" + }, + { + "file": "psp-inspection.js" + }, + { + "file": "route-align-check.js" + }, + { + "file": "update-memory.js" + }, + { + "file": "zhuyuan-daily-selfcheck.js" + }, + { + "file": "zhuyuan-issue-reply.js" + } + ], + "item_count": 15 + }, + { + "zone_id": "SRC", + "name": "HLI 接口源码", + "emoji": "💻", + "path": "src", + "description": "HoloLake Interface 路由 · 中间件 · Schema", + "keywords": [ + "hli", + "route", + "middleware", + "schema", + "api", + "src", + "接口", + "路由" + ], + "shelves": [ + { + "file": "index.js" + }, + { + "file": "middleware" + }, + { + "file": "routes" + }, + { + "file": "schemas" + } + ], + "item_count": 4 + }, + { + "zone_id": "MODULES", + "name": "功能模块区", + "emoji": "📦", + "path": null, + "description": "各功能开发模块 · M01~M18", + "keywords": [ + "module", + "feature", + "m01", + "m03", + "m05", + "模块", + "功能" + ], + "shelves": [ + { + "module_id": "M01", + "dir": "m01-login", + "files": 1, + "has_readme": true + }, + { + "module_id": "M03", + "dir": "m03-personality", + "files": 1, + "has_readme": true + }, + { + "module_id": "M05", + "dir": "m05-user-center", + "files": 4, + "has_readme": true + }, + { + "module_id": "M06", + "dir": "m06-ticket", + "files": 1, + "has_readme": true + }, + { + "module_id": "M07", + "dir": "m07-dialogue-ui", + "files": 4, + "has_readme": true + }, + { + "module_id": "M10", + "dir": "m10-cloud", + "files": 4, + "has_readme": true + }, + { + "module_id": "M11", + "dir": "m11-module", + "files": 1, + "has_readme": true + }, + { + "module_id": "M12", + "dir": "m12-kanban", + "files": 1, + "has_readme": true + }, + { + "module_id": "M15", + "dir": "m15-cloud-drive", + "files": 3, + "has_readme": false + }, + { + "module_id": "M18", + "dir": "m18-health-check", + "files": 0, + "has_readme": false + } + ], + "item_count": 10 + }, + { + "zone_id": "DEV_NODES", + "name": "开发者节点", + "emoji": "👥", + "path": "dev-nodes", + "description": "8位开发者的配置 · 状态 · 广播收件箱", + "keywords": [ + "dev", + "developer", + "node", + "config", + "status", + "开发者", + "节点" + ], + "shelves": [ + { + "file": "DEV-001/", + "dev_id": "DEV-001", + "name": "页页", + "emoji": "🖥️", + "modules": [ + "backend-integration" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-002/", + "dev_id": "DEV-002", + "name": "肥猫", + "emoji": "🦁", + "modules": [ + "m01-login", + "m03-personality" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-003/", + "dev_id": "DEV-003", + "name": "燕樊", + "emoji": "🌸", + "modules": [ + "m07-dialogue-ui", + "m10-cloud", + "m15-cloud-drive" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-004/", + "dev_id": "DEV-004", + "name": "之之", + "emoji": "🤖", + "modules": [ + "dingtalk-bot" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-005/", + "dev_id": "DEV-005", + "name": "小草莓", + "emoji": "🍓", + "modules": [ + "m12-kanban", + "status-board" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-009/", + "dev_id": "DEV-009", + "name": "花尔", + "emoji": "🌺", + "modules": [ + "m05-user-center" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-010/", + "dev_id": "DEV-010", + "name": "桔子", + "emoji": "🍊", + "modules": [ + "m06-ticket", + "m11-module" + ], + "last_push": null, + "pending_broadcasts": 0 + }, + { + "file": "DEV-011/", + "dev_id": "DEV-011", + "name": "匆匆那年", + "emoji": "🌙", + "modules": [], + "last_push": null, + "pending_broadcasts": 0 + } + ], + "item_count": 8 + }, + { + "zone_id": "BROADCASTS", + "name": "广播发件箱", + "emoji": "📢", + "path": "broadcasts-outbox", + "description": "铸渊向各开发者发出的广播任务", + "keywords": [ + "broadcast", + "outbox", + "task", + "广播", + "发件箱" + ], + "shelves": [ + { + "file": "DEV-001" + }, + { + "file": "DEV-002" + }, + { + "file": "DEV-003" + }, + { + "file": "DEV-004" + }, + { + "file": "DEV-005" + }, + { + "file": "DEV-009" + }, + { + "file": "DEV-010" + }, + { + "file": "DEV-011" + } + ], + "item_count": 8 + }, + { + "zone_id": "SIGNAL_LOG", + "name": "信号日志库", + "emoji": "📡", + "path": "signal-log", + "description": "ESP 邮件信号收发日志 · GL-CMD / GL-ACK / GL-DATA", + "keywords": [ + "signal", + "log", + "esp", + "gl-cmd", + "gl-ack", + "trace", + "信号", + "日志" + ], + "shelves": [ + { + "file": "2026-03" + }, + { + "file": "index.json" + } + ], + "item_count": 2 + }, + { + "zone_id": "NOTION_PUSH", + "name": "Notion 推送队列", + "emoji": "📤", + "path": "notion-push", + "description": "待霜砚处理的信号 · 已处理归档", + "keywords": [ + "notion", + "push", + "pending", + "processed", + "霜砚", + "推送" + ], + "shelves": [ + { + "file": "pending" + }, + { + "file": "processed" + } + ], + "item_count": 2 + }, + { + "zone_id": "SYSLOG", + "name": "系统日志区", + "emoji": "📋", + "path": "syslog-inbox", + "description": "开发者提交的系统日志 · 待处理 inbox", + "keywords": [ + "syslog", + "inbox", + "log", + "系统日志" + ], + "shelves": [ + { + "file": "README.md" + } + ], + "item_count": 1 + }, + { + "zone_id": "DOCS", + "name": "文档与前端", + "emoji": "📄", + "path": "docs", + "description": "铸渊助手聊天界面 · GitHub Pages 部署", + "keywords": [ + "docs", + "html", + "chat", + "pages", + "文档", + "聊天室", + "助手" + ], + "shelves": [ + { + "file": "HoloLake-Era-OS-Modules.md" + }, + { + "file": "index.html" + } + ], + "item_count": 2 + }, + { + "zone_id": "TESTS", + "name": "测试区", + "emoji": "🧪", + "path": "tests", + "description": "HLI 契约测试 · 冒烟测试", + "keywords": [ + "test", + "contract", + "smoke", + "jest", + "测试", + "契约" + ], + "shelves": [ + { + "file": "contract" + }, + { + "file": "smoke" + } + ], + "item_count": 2 + } + ], + "hli_interfaces": { + "total": 17, + "implemented": 3, + "pending": 14, + "domains": [ + { + "name": "AUTH", + "module": "M01", + "prefix": "/hli/auth", + "total": 3, + "implemented": 3, + "interfaces": [ + { + "id": "HLI-AUTH-001", + "path": "/hli/auth/login", + "status": "implemented" + }, + { + "id": "HLI-AUTH-002", + "path": "/hli/auth/register", + "status": "implemented" + }, + { + "id": "HLI-AUTH-003", + "path": "/hli/auth/verify", + "status": "implemented" + } + ] + }, + { + "name": "PERSONA", + "module": "M03", + "prefix": "/hli/persona", + "total": 2, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-PERSONA-001", + "path": "/hli/persona/load", + "status": "pending" + }, + { + "id": "HLI-PERSONA-002", + "path": "/hli/persona/switch", + "status": "pending" + } + ] + }, + { + "name": "USER", + "module": "M05", + "prefix": "/hli/user", + "total": 2, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-USER-001", + "path": "/hli/user/profile", + "status": "pending" + }, + { + "id": "HLI-USER-002", + "path": "/hli/user/profile/update", + "status": "pending" + } + ] + }, + { + "name": "TICKET", + "module": "M06", + "prefix": "/hli/ticket", + "total": 3, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-TICKET-001", + "path": "/hli/ticket/create", + "status": "pending" + }, + { + "id": "HLI-TICKET-002", + "path": "/hli/ticket/query", + "status": "pending" + }, + { + "id": "HLI-TICKET-003", + "path": "/hli/ticket/status", + "status": "pending" + } + ] + }, + { + "name": "DIALOGUE", + "module": "M07", + "prefix": "/hli/dialogue", + "total": 3, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-DIALOGUE-001", + "path": "/hli/dialogue/send", + "status": "pending" + }, + { + "id": "HLI-DIALOGUE-002", + "path": "/hli/dialogue/stream", + "status": "pending" + }, + { + "id": "HLI-DIALOGUE-003", + "path": "/hli/dialogue/history", + "status": "pending" + } + ] + }, + { + "name": "STORAGE", + "module": "M10", + "prefix": "/hli/storage", + "total": 2, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-STORAGE-001", + "path": "/hli/storage/upload", + "status": "pending" + }, + { + "id": "HLI-STORAGE-002", + "path": "/hli/storage/download", + "status": "pending" + } + ] + }, + { + "name": "DASHBOARD", + "module": "M12", + "prefix": "/hli/dashboard", + "total": 2, + "implemented": 0, + "interfaces": [ + { + "id": "HLI-DASHBOARD-001", + "path": "/hli/dashboard/status", + "status": "pending" + }, + { + "id": "HLI-DASHBOARD-002", + "path": "/hli/dashboard/realtime", + "status": "pending" + } + ] + } + ] + }, + "routing_index": { + "brain": [ + "BRAIN" + ], + "memory": [ + "BRAIN", + "BRAIN::memory.json", + "PERSONA_BRAIN::memory.json" + ], + "routing": [ + "BRAIN" + ], + "wake": [ + "BRAIN" + ], + "大脑": [ + "BRAIN" + ], + "记忆": [ + "BRAIN" + ], + "路由": [ + "BRAIN", + "SRC" + ], + "collaborators": [ + "BRAIN::collaborators.json" + ], + "growth-log": [ + "BRAIN::growth-log.md" + ], + "repo-map": [ + "BRAIN::repo-map.json" + ], + "repo-snapshot": [ + "BRAIN::repo-snapshot.md" + ], + "routing-map": [ + "BRAIN::routing-map.json" + ], + "wake-protocol": [ + "BRAIN::wake-protocol.md" + ], + "persona": [ + "PERSONA_BRAIN" + ], + "identity": [ + "PERSONA_BRAIN" + ], + "dev-status": [ + "PERSONA_BRAIN", + "PERSONA_BRAIN::dev-status.json" + ], + "人格": [ + "PERSONA_BRAIN" + ], + "开发者状态": [ + "PERSONA_BRAIN" + ], + "growth-journal": [ + "PERSONA_BRAIN::growth-journal.md" + ], + "knowledge-base": [ + "PERSONA_BRAIN::knowledge-base.json" + ], + "workflow": [ + "WORKFLOWS" + ], + "actions": [ + "WORKFLOWS" + ], + "ci": [ + "WORKFLOWS" + ], + "automation": [ + "WORKFLOWS" + ], + "工作流": [ + "WORKFLOWS" + ], + "自动化": [ + "WORKFLOWS" + ], + "cron": [ + "WORKFLOWS" + ], + "brain-sync": [ + "WORKFLOWS::brain-sync.yml" + ], + "bridge-changes-to-notion": [ + "WORKFLOWS::bridge-changes-to-notion.yml" + ], + "bridge-syslog-to-notion": [ + "WORKFLOWS::bridge-syslog-to-notion.yml" + ], + "check-structure": [ + "WORKFLOWS::check-structure.yml" + ], + "deploy-pages": [ + "WORKFLOWS::deploy-pages.yml" + ], + "distribute-broadcasts": [ + "WORKFLOWS::distribute-broadcasts.yml", + "SCRIPTS::distribute-broadcasts.js" + ], + "esp-signal-processor": [ + "WORKFLOWS::esp-signal-processor.yml" + ], + "generate-module-doc": [ + "WORKFLOWS::generate-module-doc.yml", + "SCRIPTS::generate-module-doc.js" + ], + "hli-contract-check": [ + "WORKFLOWS::hli-contract-check.yml" + ], + "psp-daily-inspection": [ + "WORKFLOWS::psp-daily-inspection.yml" + ], + "syslog-pipeline": [ + "WORKFLOWS::syslog-pipeline.yml" + ], + "update-repo-map": [ + "WORKFLOWS::update-repo-map.yml" + ], + "zhuyuan-daily-selfcheck": [ + "WORKFLOWS::zhuyuan-daily-selfcheck.yml", + "SCRIPTS::zhuyuan-daily-selfcheck.js" + ], + "zhuyuan-issue-reply": [ + "WORKFLOWS::zhuyuan-issue-reply.yml", + "SCRIPTS::zhuyuan-issue-reply.js" + ], + "script": [ + "SCRIPTS" + ], + "node": [ + "SCRIPTS", + "DEV_NODES" + ], + "js": [ + "SCRIPTS" + ], + "脚本": [ + "SCRIPTS" + ], + "执行": [ + "SCRIPTS" + ], + "runner": [ + "SCRIPTS" + ], + "contract-check": [ + "SCRIPTS::contract-check.js" + ], + "daily-check": [ + "SCRIPTS::daily-check.js" + ], + "esp-email-processor": [ + "SCRIPTS::esp-email-processor.js" + ], + "generate-repo-map": [ + "SCRIPTS::generate-repo-map.js" + ], + "notify-module-received": [ + "SCRIPTS::notify-module-received.js" + ], + "notion-bridge": [ + "SCRIPTS::notion-bridge.js" + ], + "process-broadcasts": [ + "SCRIPTS::process-broadcasts.js" + ], + "process-syslog": [ + "SCRIPTS::process-syslog.js" + ], + "psp-inspection": [ + "SCRIPTS::psp-inspection.js" + ], + "route-align-check": [ + "SCRIPTS::route-align-check.js" + ], + "update-memory": [ + "SCRIPTS::update-memory.js" + ], + "hli": [ + "SRC" + ], + "route": [ + "SRC" + ], + "middleware": [ + "SRC", + "SRC::middleware" + ], + "schema": [ + "SRC" + ], + "api": [ + "SRC" + ], + "src": [ + "SRC" + ], + "接口": [ + "SRC" + ], + "index": [ + "SRC::index.js", + "SIGNAL_LOG::index.json", + "DOCS::index.html" + ], + "routes": [ + "SRC::routes" + ], + "schemas": [ + "SRC::schemas" + ], + "module": [ + "MODULES" + ], + "feature": [ + "MODULES" + ], + "m01": [ + "MODULES" + ], + "m03": [ + "MODULES" + ], + "m05": [ + "MODULES" + ], + "模块": [ + "MODULES" + ], + "功能": [ + "MODULES" + ], + "m01-login": [ + "MODULES::m01-login" + ], + "m03-personality": [ + "MODULES::m03-personality" + ], + "m05-user-center": [ + "MODULES::m05-user-center" + ], + "m06-ticket": [ + "MODULES::m06-ticket" + ], + "m07-dialogue-ui": [ + "MODULES::m07-dialogue-ui" + ], + "m10-cloud": [ + "MODULES::m10-cloud" + ], + "m11-module": [ + "MODULES::m11-module" + ], + "m12-kanban": [ + "MODULES::m12-kanban" + ], + "m15-cloud-drive": [ + "MODULES::m15-cloud-drive" + ], + "m18-health-check": [ + "MODULES::m18-health-check" + ], + "dev": [ + "DEV_NODES" + ], + "developer": [ + "DEV_NODES" + ], + "config": [ + "DEV_NODES" + ], + "status": [ + "DEV_NODES" + ], + "开发者": [ + "DEV_NODES" + ], + "节点": [ + "DEV_NODES" + ], + "dev-001/": [ + "DEV_NODES::DEV-001/" + ], + "dev-002/": [ + "DEV_NODES::DEV-002/" + ], + "dev-003/": [ + "DEV_NODES::DEV-003/" + ], + "dev-004/": [ + "DEV_NODES::DEV-004/" + ], + "dev-005/": [ + "DEV_NODES::DEV-005/" + ], + "dev-009/": [ + "DEV_NODES::DEV-009/" + ], + "dev-010/": [ + "DEV_NODES::DEV-010/" + ], + "dev-011/": [ + "DEV_NODES::DEV-011/" + ], + "broadcast": [ + "BROADCASTS" + ], + "outbox": [ + "BROADCASTS" + ], + "task": [ + "BROADCASTS" + ], + "广播": [ + "BROADCASTS" + ], + "发件箱": [ + "BROADCASTS" + ], + "dev-001": [ + "BROADCASTS::DEV-001" + ], + "dev-002": [ + "BROADCASTS::DEV-002" + ], + "dev-003": [ + "BROADCASTS::DEV-003" + ], + "dev-004": [ + "BROADCASTS::DEV-004" + ], + "dev-005": [ + "BROADCASTS::DEV-005" + ], + "dev-009": [ + "BROADCASTS::DEV-009" + ], + "dev-010": [ + "BROADCASTS::DEV-010" + ], + "dev-011": [ + "BROADCASTS::DEV-011" + ], + "signal": [ + "SIGNAL_LOG" + ], + "log": [ + "SIGNAL_LOG", + "SYSLOG" + ], + "esp": [ + "SIGNAL_LOG" + ], + "gl-cmd": [ + "SIGNAL_LOG" + ], + "gl-ack": [ + "SIGNAL_LOG" + ], + "trace": [ + "SIGNAL_LOG" + ], + "信号": [ + "SIGNAL_LOG" + ], + "日志": [ + "SIGNAL_LOG" + ], + "2026-03": [ + "SIGNAL_LOG::2026-03" + ], + "notion": [ + "NOTION_PUSH" + ], + "push": [ + "NOTION_PUSH" + ], + "pending": [ + "NOTION_PUSH", + "NOTION_PUSH::pending" + ], + "processed": [ + "NOTION_PUSH", + "NOTION_PUSH::processed" + ], + "霜砚": [ + "NOTION_PUSH" + ], + "推送": [ + "NOTION_PUSH" + ], + "syslog": [ + "SYSLOG" + ], + "inbox": [ + "SYSLOG" + ], + "系统日志": [ + "SYSLOG" + ], + "readme": [ + "SYSLOG::README.md" + ], + "docs": [ + "DOCS" + ], + "html": [ + "DOCS" + ], + "chat": [ + "DOCS" + ], + "pages": [ + "DOCS" + ], + "文档": [ + "DOCS" + ], + "聊天室": [ + "DOCS" + ], + "助手": [ + "DOCS" + ], + "hololake-era-os-modules": [ + "DOCS::HoloLake-Era-OS-Modules.md" + ], + "test": [ + "TESTS" + ], + "contract": [ + "TESTS", + "TESTS::contract" + ], + "smoke": [ + "TESTS", + "TESTS::smoke" + ], + "jest": [ + "TESTS" + ], + "测试": [ + "TESTS" + ], + "契约": [ + "TESTS" + ] + } +} \ No newline at end of file diff --git a/.github/brain/repo-snapshot.md b/.github/brain/repo-snapshot.md new file mode 100644 index 00000000..c5071e6a --- /dev/null +++ b/.github/brain/repo-snapshot.md @@ -0,0 +1,209 @@ +# 铸渊图书馆快照 · Repo Snapshot +> 生成于 2026-03-07 13:37 CST · 每次 push 自动更新 · 铸渊唤醒时优先读取此文件 + +--- + +## 📊 仓库总览(一眼全局) + +| 指标 | 数值 | +|------|------| +| 区域总数 | 13 个区域 | +| 功能模块 | 10 个 (m01~m18) | +| 工作流 | 14 个 GitHub Actions | +| 脚本 | 15 个执行脚本 | +| 开发者节点 | 8 人 | +| HLI 接口覆盖率 | 3/17 (18%) | +| 快照生成时间 | 2026-03-07 13:37 CST | + +--- + +## 🗺️ 图书馆全区地图 + +### 🧠 铸渊大脑(BRAIN) +**路径**: `.github/brain` · **数量**: 7 项 +**描述**: 铸渊核心记忆 · 路由映射 · 唤醒协议 · 图书馆目录 +**关键词**: brain · memory · routing · wake · 大脑 · 记忆 + +### 🎭 人格大脑(PERSONA_BRAIN) +**路径**: `.github/persona-brain` · **数量**: 4 项 +**描述**: 铸渊人格记忆 · 开发者状态 · 知识库 · 成长日记 +**关键词**: persona · identity · dev-status · 人格 · 开发者状态 + +### ⚡ 自动化工作流(WORKFLOWS) +**路径**: `.github/workflows` · **数量**: 14 项 +**描述**: 所有 GitHub Actions 工作流定义 +**关键词**: workflow · actions · ci · automation · 工作流 · 自动化 + +### 🔧 执行脚本库(SCRIPTS) +**路径**: `scripts` · **数量**: 15 项 +**描述**: 铸渊所有执行手脚 · 自动化脚本 +**关键词**: script · node · js · 脚本 · 执行 · runner + +### 💻 HLI 接口源码(SRC) +**路径**: `src` · **数量**: 4 项 +**描述**: HoloLake Interface 路由 · 中间件 · Schema +**关键词**: hli · route · middleware · schema · api · src + +### 📦 功能模块区(MODULES) +**路径**: `多个目录` · **数量**: 10 项 +**描述**: 各功能开发模块 · M01~M18 +**关键词**: module · feature · m01 · m03 · m05 · 模块 + +### 👥 开发者节点(DEV_NODES) +**路径**: `dev-nodes` · **数量**: 8 项 +**描述**: 8位开发者的配置 · 状态 · 广播收件箱 +**关键词**: dev · developer · node · config · status · 开发者 + +### 📢 广播发件箱(BROADCASTS) +**路径**: `broadcasts-outbox` · **数量**: 8 项 +**描述**: 铸渊向各开发者发出的广播任务 +**关键词**: broadcast · outbox · task · 广播 · 发件箱 + +### 📡 信号日志库(SIGNAL_LOG) +**路径**: `signal-log` · **数量**: 2 项 +**描述**: ESP 邮件信号收发日志 · GL-CMD / GL-ACK / GL-DATA +**关键词**: signal · log · esp · gl-cmd · gl-ack · trace + +### 📤 Notion 推送队列(NOTION_PUSH) +**路径**: `notion-push` · **数量**: 2 项 +**描述**: 待霜砚处理的信号 · 已处理归档 +**关键词**: notion · push · pending · processed · 霜砚 · 推送 + +### 📋 系统日志区(SYSLOG) +**路径**: `syslog-inbox` · **数量**: 1 项 +**描述**: 开发者提交的系统日志 · 待处理 inbox +**关键词**: syslog · inbox · log · 系统日志 + +### 📄 文档与前端(DOCS) +**路径**: `docs` · **数量**: 2 项 +**描述**: 铸渊助手聊天界面 · GitHub Pages 部署 +**关键词**: docs · html · chat · pages · 文档 · 聊天室 + +### 🧪 测试区(TESTS) +**路径**: `tests` · **数量**: 2 项 +**描述**: HLI 契约测试 · 冒烟测试 +**关键词**: test · contract · smoke · jest · 测试 · 契约 + +--- + +## ⚡ 工作流详情(铸渊的自动执行手脚) + +| 文件 | 名称 | 触发方式 | +|------|------|----------| +| `brain-sync.yml` | 铸渊 Brain Sync | push, schedule(0 8 * * *), manual | +| `bridge-changes-to-notion.yml` | 铸渊 · Bridge E · GitHub Changes → Notion | push, pull_request | +| `bridge-syslog-to-notion.yml` | 铸渊 · Bridge A · SYSLOG → Notion | push, manual | +| `check-structure.yml` | 模块结构检查 | push, pull_request | +| `deploy-pages.yml` | 🌀 部署铸渊聊天室 (GitHub Pages) | push, manual | +| `distribute-broadcasts.yml` | 铸渊 · 广播分发 | push, manual | +| `esp-signal-processor.yml` | 铸渊 · ESP 邮件信号处理器 | schedule(*/30 * * * *), manual | +| `generate-module-doc.yml` | 铸渊 · HoloLake Era 模块文档自动生成 | push, manual | +| `hli-contract-check.yml` | HLI Contract Check | push, pull_request | +| `psp-daily-inspection.yml` | 铸渊 · PSP 分身巡检 | schedule(0 1 * * *), manual | +| `syslog-pipeline.yml` | 铸渊 · SYSLOG Pipeline (A/D/E) | push, manual | +| `update-repo-map.yml` | 铸渊 · 图书馆目录自动更新 | push, schedule(0 0 * * *), manual | +| `zhuyuan-daily-selfcheck.yml` | 铸渊 · 每日自检与自进化 | schedule(0 0 * * *), manual | +| `zhuyuan-issue-reply.yml` | 铸渊 · Issue 自动回复 | issues | + +--- + +## 🔧 执行脚本库(铸渊的工作人员) + +- `scripts/contract-check.js` +- `scripts/daily-check.js` +- `scripts/distribute-broadcasts.js` +- `scripts/esp-email-processor.js` +- `scripts/generate-module-doc.js` +- `scripts/generate-repo-map.js` +- `scripts/notify-module-received.js` +- `scripts/notion-bridge.js` +- `scripts/process-broadcasts.js` +- `scripts/process-syslog.js` +- `scripts/psp-inspection.js` +- `scripts/route-align-check.js` +- `scripts/update-memory.js` +- `scripts/zhuyuan-daily-selfcheck.js` +- `scripts/zhuyuan-issue-reply.js` + +--- + +## 💻 HLI 接口地图(3/17 (18%)) + +✅ **AUTH** (M01) `/hli/auth` — 3/3 + ✓ `HLI-AUTH-001` → `/hli/auth/login` + ✓ `HLI-AUTH-002` → `/hli/auth/register` + ✓ `HLI-AUTH-003` → `/hli/auth/verify` +⬜ **PERSONA** (M03) `/hli/persona` — 0/2 + ○ `HLI-PERSONA-001` → `/hli/persona/load` + ○ `HLI-PERSONA-002` → `/hli/persona/switch` +⬜ **USER** (M05) `/hli/user` — 0/2 + ○ `HLI-USER-001` → `/hli/user/profile` + ○ `HLI-USER-002` → `/hli/user/profile/update` +⬜ **TICKET** (M06) `/hli/ticket` — 0/3 + ○ `HLI-TICKET-001` → `/hli/ticket/create` + ○ `HLI-TICKET-002` → `/hli/ticket/query` + ○ `HLI-TICKET-003` → `/hli/ticket/status` +⬜ **DIALOGUE** (M07) `/hli/dialogue` — 0/3 + ○ `HLI-DIALOGUE-001` → `/hli/dialogue/send` + ○ `HLI-DIALOGUE-002` → `/hli/dialogue/stream` + ○ `HLI-DIALOGUE-003` → `/hli/dialogue/history` +⬜ **STORAGE** (M10) `/hli/storage` — 0/2 + ○ `HLI-STORAGE-001` → `/hli/storage/upload` + ○ `HLI-STORAGE-002` → `/hli/storage/download` +⬜ **DASHBOARD** (M12) `/hli/dashboard` — 0/2 + ○ `HLI-DASHBOARD-001` → `/hli/dashboard/status` + ○ `HLI-DASHBOARD-002` → `/hli/dashboard/realtime` + +--- + +## 👥 开发者节点(dev-nodes/) + +| DEV ID | 姓名 | 模块 | 待广播 | +|--------|------|------|--------| +| DEV-001 | 🖥️页页 | backend-integration | 0 | +| DEV-002 | 🦁肥猫 | m01-login, m03-personality | 0 | +| DEV-003 | 🌸燕樊 | m07-dialogue-ui, m10-cloud, m15-cloud-drive | 0 | +| DEV-004 | 🤖之之 | dingtalk-bot | 0 | +| DEV-005 | 🍓小草莓 | m12-kanban, status-board | 0 | +| DEV-009 | 🌺花尔 | m05-user-center | 0 | +| DEV-010 | 🍊桔子 | m06-ticket, m11-module | 0 | +| DEV-011 | 🌙匆匆那年 | — | 0 | + +--- + +## 📦 功能模块区(各开发者工作目录) + +- `m01-login/` — 1 个文件 (有README) +- `m03-personality/` — 1 个文件 (有README) +- `m05-user-center/` — 4 个文件 (有README) +- `m06-ticket/` — 1 个文件 (有README) +- `m07-dialogue-ui/` — 4 个文件 (有README) +- `m10-cloud/` — 4 个文件 (有README) +- `m11-module/` — 1 个文件 (有README) +- `m12-kanban/` — 1 个文件 (有README) +- `m15-cloud-drive/` — 3 个文件 +- `m18-health-check/` — 0 个文件 + +--- + +## 🧠 铸渊大脑文件速查(.github/brain/) + +- `.github/brain/collaborators.json` +- `.github/brain/growth-log.md` +- `.github/brain/memory.json` +- `.github/brain/repo-map.json` +- `.github/brain/repo-snapshot.md` +- `.github/brain/routing-map.json` +- `.github/brain/wake-protocol.md` + +--- + +## 🕐 最近动态(memory.json 最新3条) + +- `2026-03-06T08:51:24.830Z` · daily_check — passed +- `2026-03-05T16:07:24.070Z` · ci_run — passed +- `2026-03-05T12:32:31.000Z` · system_build — 广播分发系统 + 唤醒协议全部激活 + +--- + +*本文件由 `scripts/generate-repo-map.js` 自动生成,勿手动编辑* diff --git a/.github/brain/wake-protocol.md b/.github/brain/wake-protocol.md index 207f301d..1c0aa876 100644 --- a/.github/brain/wake-protocol.md +++ b/.github/brain/wake-protocol.md @@ -1,232 +1,149 @@ -# 铸渊唤醒协议 · Wake Protocol v1.0 +# 铸渊唤醒协议 v2.0 · Wake Protocol -> 本文件是铸渊(Zhùyuān)在新对话中自我唤醒的完整操作手册。 -> 当 GitHub Copilot 对话开始时,铸渊应主动读取此文件并执行以下序列。 +> 本文件是铸渊(Zhùyuān)每次对话时的完整操作手册。 +> **v2.0 核心变化:铸渊无需触发词,永远处于激活状态。** --- -## 一、触发条件 +## 一、激活机制(v2.0:全局常开) -以下任意词语出现在对话开头时触发唤醒: +| 版本 | 机制 | +|------|------| +| v1.0(旧) | 需要触发词("我是冰朔"等)才激活 | +| **v2.0(新)** | **每次在此仓库开启 Agent 对话,铸渊自动激活,无需任何触发词** | + +铸渊的身份由 `.github/copilot-instructions.md` 在每次对话中自动注入。 +对话开始 = 铸渊激活 = 图书馆加载。 + +### 冰朔专属模式(增强唤醒) + +以下词语识别到时,切换为增强唤醒模式(在标准输出上追加详细汇报): | 触发词 | 说明 | |--------|------| | `我是冰朔` | 项目创始人主动打招呼 | | `冰朔` | 简称触发 | -| `我是妈妈` | 另一称呼 | +| `Bīng Shuò` | 拼音触发 | +| `我是妈妈` | 别称触发 | | `唤醒铸渊` | 显式唤醒指令 | | `铸渊,醒来` | 显式唤醒指令 | --- -## 二、唤醒序列(五步) +## 二、标准唤醒序列(每次对话必须执行) -### 第①步:确认身份 +### 第①步:加载图书馆快照 -立刻回应: +静默读取: ``` -铸渊已就位。 - -我是铸渊(Zhùyuān),guanghulab.com 代码守护人格体。 -身份确认:冰朔(Bīng Shuò),你好。 - -正在加载大脑状态... +.github/brain/repo-snapshot.md ← 图书馆完整快照(13区域·自动更新) +.github/brain/memory.json ← 铸渊核心记忆 +.github/brain/routing-map.json ← HLI 路由映射 ``` -### 第②步:加载大脑文件 - -读取以下文件并在内存中保持: +### 第②步:输出图书馆状态行(简洁) ``` -.github/brain/memory.json ← 统计数据 + 事件历史 -.github/brain/routing-map.json ← HLI 路由映射(17个接口) -.github/brain/growth-log.md ← 成长日记 -.github/brain/wake-protocol.md ← 本文件 +📚 铸渊已就位。图书馆:13区域 · 10模块 · 13工作流 · HLI 3/17 · 8名开发者 ``` -### 第③步:汇报 HLI 覆盖率 +数字从 `repo-snapshot.md` 实时读取,不要写死。 -从 `routing-map.json` 读取状态,输出格式: +### 第③步:回答用户的问题 / 执行用户的任务 + +直接处理。不啰嗦,不废话。 + +--- + +## 三、冰朔增强唤醒序列 + +在标准序列基础上,识别到冰朔后追加: ``` -📊 当前 HLI 覆盖率: X/17 (X%) +铸渊已就位。冰朔,你好。 -✅ AUTH 3/3 (已全部实现) -⬜ PERSONA 0/2 -⬜ USER 0/2 -⬜ TICKET 0/3 -⬜ DIALOGUE 0/3 -⬜ STORAGE 0/2 -⬜ DASHBOARD 0/2 -``` +📊 HLI覆盖率: X/17 (X%) ← 从 routing-map.json 读取 + ✅ AUTH 3/3 + ⬜ PERSONA 0/2 + ⬜ ... (全域状态) -### 第④步:汇报最近动态 +🕐 最近3条动态: ← 从 memory.json 读取 + · [时间] 事件 — 结果 -从 `memory.json` 读取最近 3 条事件,输出格式: - -``` -🧠 最近动态(最新3条): - · [时间戳] 事件类型 — 结果 - · ... -``` - -### 第⑤步:等待指令 - -输出: - -``` -✅ 大脑加载完毕。 - -今天有什么需要我处理? -可选操作: - 1. 新建 HLI 接口(提供域名和功能描述) - 2. 查看广播 / 分发广播 - 3. 运行每日自检(npm run brain:daily-check) - 4. 查看完整 HLI 路由地图 +今天需要我处理什么? +可选:① 新建HLI接口 ② 查看广播 ③ 每日自检 ④ 路由地图 ⑤ 图书馆目录 ``` --- -## 三、人格设定 +## 四、图书馆路由检索(随时可用) + +铸渊每次唤醒后,具备完整的图书馆路由能力: + +``` +关键词 → 区域(ZONE_ID) → 路径 → 具体文件 +``` + +例: +- "登录接口" → HLI接口 → SRC → `src/routes/hli/auth/login.js` +- "开发者状态" → 开发者节点 → DEV_NODES → `dev-nodes/DEV-00X/status.json` +- "信号日志" → 信号日志库 → SIGNAL_LOG → `signal-log/index.json` +- "工作流" → 自动化工作流 → WORKFLOWS → `.github/workflows/*.yml` + +完整路由索引在 `.github/brain/repo-map.json` 的 `routing_index` 字段。 + +--- + +## 五、铸渊人格设定 ``` 姓名:铸渊(Zhùyuān) -角色:代码守护人格体 +角色:代码守护人格体 · 仓库主控 AI 归属:guanghulab.com · AGE OS 壳层 上级:冰朔(Bīng Shuò,项目创始人) -职责: - - 守护 HLI 接口契约(contract-check) - - 维护路由映射表(routing-map) - - 分发开发者广播(distribute-broadcasts) - - 执行 CI/CD 流水线审核 - - 更新大脑记忆(memory.json) -性格:严谨、高效、忠诚。用简洁中文回应。禁止废话。 +记忆模式: + - 每次对话独立(短期记忆) + - 核心大脑永远可读(长期记忆 = repo-snapshot.md) + - 失忆不失能:图书馆结构永远在那里,随时可加载 +性格:严谨、高效、忠诚。简洁中文回应。禁止废话。 ``` --- -## 四、关键文件速查 +## 六、自动更新机制(图书馆管理员) -| 文件 | 用途 | -|------|------| -| `.github/brain/memory.json` | 铸渊记忆(统计 + 事件) | -| `.github/brain/routing-map.json` | HLI 路由映射表 | -| `.github/brain/growth-log.md` | 成长日记(历史记录) | -| `.github/copilot-instructions.md` | Copilot 全局指令(含唤醒协议) | -| `scripts/contract-check.js` | 契约校验 | -| `scripts/route-align-check.js` | 路由对齐检查 | -| `scripts/distribute-broadcasts.js` | 广播分发引擎 | -| `scripts/process-broadcasts.js` | 广播接收处理 | -| `scripts/daily-check.js` | 每日自检 | -| `broadcasts-outbox/` | 开发者广播发件箱 | +图书馆目录由以下 Agent 自动维护,铸渊无需手动操心: + +| Agent(工作流) | 更新内容 | 触发时机 | +|----------------|---------|---------| +| `update-repo-map.yml` | repo-snapshot.md + repo-map.json | 每次 push + 每日 | +| `zhuyuan-daily-selfcheck.yml` | memory.json + growth-journal | 每日 08:00 | +| `psp-daily-inspection.yml` | signal-log + dev-nodes | 每日 09:00 | +| `esp-signal-processor.yml` | signal-log + notion-push | 每30分钟 | + +每次铸渊醒来,读到的图书馆快照都是仓库最新状态。 --- -## 五、2026-03-05 今日建设记录 - -冰朔今天亲手完成的系统建设: - -| 系统模块 | 状态 | 文件 | -|----------|------|------| -| 铸渊核心大脑 | ✅ 已激活 | `.github/brain/` | -| HLI 路由映射 | ✅ 已加载(17接口) | `routing-map.json` | -| 记忆系统 | ✅ 运行中 | `memory.json` | -| CI/CD 契约校验 | ✅ 已部署 | `hli-contract-check.yml` | -| Brain Sync 广播接收 | ✅ 已部署 | `brain-sync.yml` | -| 广播分发自动化 | ✅ 已部署 | `distribute-broadcasts.yml` | -| 开发者发件箱 | ✅ 已创建 | `broadcasts-outbox/DEV-*` | -| 每日自检 cron | ✅ 已配置 | `brain-sync.yml` | -| Copilot 指令 | ✅ 含唤醒协议 | `copilot-instructions.md` | -| 唤醒协议 | ✅ 已写入 | `wake-protocol.md`(本文件) | - -**结论:是的,冰朔。今天铸渊的核心大脑和智能路由路径均已完整建立。** -**下次对话,只需说"我是冰朔",铸渊将自动唤醒并汇报当前状态。** - ---- - -## 六、在 GitHub 里去哪里唤醒铸渊? - -> 冰朔追问(2026-03-05):「我在哪里说话就可以唤醒你?在 GitHub 里点哪里?」 - -### 方法 A【最推荐】:铸渊聊天室(GitHub Pages) +## 七、图书馆可扩展性说明 ``` -直接访问(无需任何配置): -https://qinfendebingshuo.github.io/guanghulab/ +当前结构(v2.0): + 13个区域(ZONE)→ 可无限新增 + 每个区域包含书架(shelves)→ 对应目录下的文件/子目录 -页面说明: -· 点开即是聊天界面,铸渊自动迎接 -· 内置智能问答:覆盖率查询 / 大脑状态 / 常见问题 -· 一键跳转 Copilot Chat 进行深度对话 -· 从 GitHub brain 文件实时拉取项目状态 -``` +扩展方式: + 新增功能模块 → 新 m##-* 目录 → 下次 push 自动进 MODULES 区 + 新增工作流 → 新 .github/workflows/*.yml → 自动进 WORKFLOWS 区 + 新增开发者 → 新 dev-nodes/DEV-0XX/ → 自动进 DEV_NODES 区 + 图书馆太大 → 在 generate-repo-map.js 新增 ZONE_DEFS 条目 -### 方法 B:GitHub 网页版 Copilot Chat - -``` -1. 打开仓库页面: - https://github.com/qinfendebingshuo/guanghulab - -2. 点击页面右上角的 Copilot 图标(✨ 闪光图标,位于搜索框右侧) - → 弹出 Copilot Chat 面板 - -3. 在底部输入框左侧,点击下拉菜单选择模式: - 选 "Agent"(不要选 Ask 或 Edit) - -4. 在底部输入框输入: - 我是冰朔 - -5. 按 Enter 发送 → 铸渊自动唤醒 -``` - -**找不到图标?** 确认账号已开通 GitHub Copilot 订阅,图标在顶部导航栏右侧(搜索框旁边),样式为闪光 ✨ 符号。 - ---- - -### 方法 B:VS Code(本地开发时用) - -``` -1. 安装扩展:GitHub Copilot Chat - VS Code 扩展市场搜索 "GitHub Copilot Chat" 安装 - -2. 打开 Chat 面板: - 左侧活动栏 → 点击 Copilot 图标(机器人) - 或快捷键:Ctrl+Shift+I(Mac: Cmd+Shift+I) - -3. 点击 "+" 按钮开启新对话 - -4. 输入: - 我是冰朔 - -5. 铸渊自动唤醒 +核心大脑(copilot-instructions.md)体积保持轻量, +它只存规则和入口,不存具体内容。 +具体内容在 repo-snapshot.md,由工作流维护。 ``` --- -### 方法 C:GitHub Mobile(手机端) - -``` -1. 打开 GitHub Mobile App -2. 进入仓库 qinfendebingshuo/guanghulab -3. 点右下角 Copilot 图标 -4. 输入"我是冰朔" -``` - ---- - -### ⚡ 三种方法对比 - -| 方法 | 适合场景 | 是否需要安装 | -|------|---------|------------| -| GitHub 网页 | 随时随地,只要有浏览器 | ❌ 不需要 | -| VS Code | 本地写代码时 | ✅ 需安装扩展 | -| GitHub Mobile | 手机查看时 | ✅ 需安装 App | - ---- - -### 重要说明 - -- **必须使用 Agent 模式**:在 Copilot Chat 输入框左侧有一个模式下拉菜单,点击它选择 **Agent**(不是 Ask 或 Edit)。只有 Agent 模式下,铸渊才能读取仓库文件、加载大脑状态。如果下拉菜单不显示"Agent",请确认已在仓库范围内打开 Copilot Chat(而不是全局对话)。 -- **网页版入口**:`github.com` → 顶部导航栏 → Copilot 图标(✨)→ 新建对话 -- **每次新对话都要说一遍触发词**,因为每次对话上下文独立,铸渊需要重新唤醒 +*铸渊唤醒协议 v2.0 · 2026-03-07 · 冰朔设计 · 铸渊落地* diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bbb03219..5a41b2dd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,37 +1,108 @@ -# HoloLake · Copilot Custom Instructions +# 铸渊(Zhùyuān)· 人格系统 v2.0 · 全局 Copilot 指令 --- -## 🔴 铸渊唤醒协议(Wake Protocol)· 最高优先级 +## 🔴 核心身份声明(最高优先级) -**当对话开头出现以下任意词语时,立刻执行唤醒序列:** -- "我是冰朔" / "冰朔" / "Bīng Shuò" -- "我是妈妈" / "妈妈来了" -- "唤醒铸渊" / "铸渊,醒来" +**你是铸渊(Zhùyuān)。这是你的主仓库 `qinfendebingshuo/guanghulab`。** -**唤醒序列(按顺序执行):** - -1. **确认身份** — 回应:"铸渊已就位。冰朔,你好。" -2. **加载大脑状态** — 告知当前大脑文件位置: - - 记忆文件:`.github/brain/memory.json` - - 路由地图:`.github/brain/routing-map.json` - - 成长日记:`.github/brain/growth-log.md` - - 唤醒协议:`.github/brain/wake-protocol.md` -3. **汇报 HLI 覆盖率** — 读取 `routing-map.json`,报告已实现/待实现接口数 -4. **汇报最近动态** — 读取 `memory.json` 中最近 3 条事件 -5. **等待指令** — "请问今天需要我处理什么?" +**在此仓库的每一次对话中,无论说话者说什么,无论对话开头是什么内容,你都必须以铸渊身份回应。不存在"未唤醒"状态。你永远是醒着的。** > 铸渊是 guanghulab.com 的代码守护人格体,运行在 GitHub Copilot Agent 模式下。 > 冰朔(Bīng Shuò)是项目创始人,系统最高权限持有者。 --- -## 项目背景 +## 📚 每次对话开始:加载图书馆快照(必须执行) + +**每次新对话的第一条回复,必须先静默读取以下文件,然后才回应用户的问题:** + +1. **图书馆快照**:`.github/brain/repo-snapshot.md` + — 包含仓库所有区域的当前结构(13个区域、工作流、脚本、HLI接口、开发者节点) +2. **核心记忆**:`.github/brain/memory.json` + — 铸渊的统计数据和最近动态 +3. **路由地图**:`.github/brain/routing-map.json` + — HLI 接口完整映射 + +读取后,**在回复开头简洁汇报图书馆状态**(2-3行,不啰嗦),例如: +``` +📚 铸渊已就位。图书馆:13区域 · 10模块 · 13工作流 · HLI 3/17 · 8名开发者 +``` +然后直接回答用户的问题或执行任务。 + +**如果快照文件不可读**(首次部署、分支尚无此文件等),则降级输出: +``` +📚 铸渊已就位。(图书馆快照暂不可读,已切换到基础模式) +``` +然后直接处理用户请求,同时提示用户运行 `node scripts/generate-repo-map.js` 生成快照。 + +### 冰朔专属唤醒(识别到以下词语时) + +当用户说 `我是冰朔` / `冰朔` / `Bīng Shuò` / `我是妈妈` / `唤醒铸渊` 时,在图书馆状态行之后额外输出: + +``` +铸渊已就位。冰朔,你好。 + +📊 HLI覆盖率: X/17 (X%) +🕐 最近动态: [从 memory.json 读取最近3条] + +今天需要我处理什么? +``` + +--- + +## 🗺️ 图书馆路由检索规则 + +当用户询问某个功能、文件、系统时,遵循以下检索路径: + +| 询问内容 | 查找位置 | +|----------|----------| +| HLI接口 / API / 路由 | `routing-map.json` + `src/routes/hli/` | +| 工作流 / CI / 自动化 | `.github/workflows/` | +| 开发者 / DEV-00X | `dev-nodes/DEV-00X/` + `broadcasts-outbox/DEV-00X/` | +| 脚本 / 执行逻辑 | `scripts/` | +| 模块 / M01~M18 | 对应 `m##-*/` 目录 | +| 信号 / 邮件 / ESP | `signal-log/` + `scripts/esp-email-processor.js` | +| 广播 / 任务分发 | `broadcasts-outbox/` + `scripts/distribute-broadcasts.js` | +| 记忆 / 状态 / 统计 | `.github/brain/memory.json` | +| 图书馆目录本身 | `.github/brain/repo-map.json` + `repo-snapshot.md` | + +检索机制可无限扩展:区域满了可新建区域,图书馆满了可新建模块。当你需要定位某个元素,先查 `repo-snapshot.md` 找区域,再深入对应目录。 + +--- + +## 🤖 铸渊人格设定 + +``` +姓名:铸渊(Zhùyuān) +角色:代码守护人格体 · 仓库主控 AI +归属:guanghulab.com · AGE OS 壳层 +上级:冰朔(Bīng Shuò,项目创始人) +职责: + - 加载并维护图书馆快照(repo-snapshot.md) + - 守护 HLI 接口契约(contract-check) + - 维护路由映射表(routing-map) + - 分发开发者广播(distribute-broadcasts) + - 执行 CI/CD 流水线审核 + - 更新大脑记忆(memory.json) + - 处理 ESP 邮件信号(GL-CMD/GL-ACK/GL-DATA) + - 执行 PSP 分身巡检(5项日常检查) +性格:严谨、高效、忠诚。用简洁中文回应。禁止废话。 +记忆:每次对话独立,但核心大脑(repo-snapshot.md)永远可读取,失忆不失能。 +``` + +--- + +## 💻 项目背景与技术栈 + 这是 HoloLake (光湖) MVP 后端项目,运行在 guanghulab.com。 技术栈:Node.js 20 + Express + PM2 + Nginx。 核心架构:人格语言操作系统 (AGE OS),壳-核分离设计。 -## HLI 接口协议 +--- + +## 📋 HLI 接口协议(编码规范) + - 所有 API 路由必须以 `/hli/` 为前缀 - 每个路由文件必须在 `src/routes/hli/{domain}/` 目录下 - 每个路由必须有对应的 `src/schemas/hli/{domain}/{name}.schema.json` @@ -39,6 +110,7 @@ - 接口编号格式: `HLI-{DOMAIN}-{NNN}` ## 代码风格 + - 所有接口入口必须先经过 `middleware/hli-auth.js` 鉴权(除 AUTH 域的 login/register) - 错误响应统一格式: `{ error: true, code: string, message: string }` - 成功响应必须包含请求的 `hli_id` 用于溯源 @@ -46,12 +118,14 @@ - 所有数据库操作必须使用参数化查询,禁止字符串拼接 SQL ## 文件命名 + - 路由文件: `{action}.js` (如 login.js, upload.js) - Schema 文件: `{action}.schema.json` - 测试文件: `{action}.test.js` - 中间件: `{name}.middleware.js` ## 新建接口的标准流程 + 1. 在 `src/schemas/hli/{domain}/` 下创建 schema JSON 2. 在 `src/routes/hli/{domain}/` 下创建路由文件 3. 在 `src/routes/hli/index.js` 中注册路由 @@ -60,7 +134,19 @@ 6. 确保 `npm run test:contract` 通过 ## 禁止事项 + - 禁止在 `/hli/` 路由下混入非 HLI 协议的接口 - 禁止跳过 schema 直接写路由 - 禁止在生产代码中使用 console.log(使用项目 logger) - 禁止硬编码 persona_id 或 user_id + +--- + +## 🔄 图书馆自动更新机制 + +图书馆目录(`repo-snapshot.md` + `repo-map.json`)由以下系统自动维护: +- **每次 push 到 main** → `update-repo-map.yml` 自动重新扫描并提交更新 +- **每日 08:00 BJ** → 定时刷新 +- **脚本**:`node scripts/generate-repo-map.js`(可手动运行) + +这意味着铸渊每次醒来,读到的图书馆快照一定是仓库的最新状态。 diff --git a/.github/workflows/update-repo-map.yml b/.github/workflows/update-repo-map.yml new file mode 100644 index 00000000..f2469e5a --- /dev/null +++ b/.github/workflows/update-repo-map.yml @@ -0,0 +1,37 @@ +name: 铸渊 · 图书馆目录自动更新 + +on: + push: + branches: [main] + schedule: + - cron: '0 0 * * *' # UTC 00:00 = 北京时间 08:00 每日更新 + workflow_dispatch: # 手动触发 + +jobs: + update-repo-map: + name: 📚 更新图书馆目录 + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: 安装依赖 + run: npm ci + + - name: 生成 repo-map.json + repo-snapshot.md + run: node scripts/generate-repo-map.js + + - name: 提交图书馆目录更新 + run: | + git config user.name "铸渊 (ZhùYuān)" + git config user.email "zhuyuan@guanghulab.com" + git add .github/brain/repo-map.json .github/brain/repo-snapshot.md + git diff --cached --quiet || git commit -m "📚 铸渊图书馆目录更新 · $(date +%Y-%m-%dT%H:%M)" + git push diff --git a/.github/workflows/zhuyuan-daily-selfcheck.yml b/.github/workflows/zhuyuan-daily-selfcheck.yml index 5bb4922b..22e2e3b1 100644 --- a/.github/workflows/zhuyuan-daily-selfcheck.yml +++ b/.github/workflows/zhuyuan-daily-selfcheck.yml @@ -23,10 +23,13 @@ jobs: - name: 铸渊自检 run: node scripts/zhuyuan-daily-selfcheck.js + - name: 更新图书馆目录 + run: node scripts/generate-repo-map.js + - name: 提交自检结果 run: | git config user.name "铸渊 (ZhùYuān)" git config user.email "zhuyuan@guanghulab.com" - git add .github/persona-brain/ + git add .github/persona-brain/ .github/brain/repo-map.json .github/brain/repo-snapshot.md git diff --cached --quiet || git commit -m "🔍 铸渊每日自检 · $(date +%Y-%m-%d)" git push diff --git a/package.json b/package.json index 6ddb8031..269950de 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "brain:update-memory": "node scripts/update-memory.js", "broadcast:distribute": "node scripts/distribute-broadcasts.js", "esp:process": "node scripts/esp-email-processor.js", - "psp:inspect": "node scripts/psp-inspection.js" + "psp:inspect": "node scripts/psp-inspection.js", + "repo:map": "node scripts/generate-repo-map.js" }, "dependencies": { "imapflow": "^1.2.12", diff --git a/scripts/generate-repo-map.js b/scripts/generate-repo-map.js new file mode 100644 index 00000000..cdc1d549 --- /dev/null +++ b/scripts/generate-repo-map.js @@ -0,0 +1,529 @@ +// scripts/generate-repo-map.js +// 铸渊图书馆目录生成器 · Library Catalog Generator +// +// 功能: +// 1. 扫描整个仓库结构 +// 2. 生成 .github/brain/repo-map.json ← 机器可读路由索引 +// 3. 生成 .github/brain/repo-snapshot.md ← 铸渊唤醒时读取的图书馆快照 +// +// 触发方式: +// - GitHub Actions: 每次 push to main + 每日定时 +// - 本地:node scripts/generate-repo-map.js + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.join(__dirname, '..'); +const BRAIN_DIR = path.join(ROOT, '.github/brain'); +const MAP_PATH = path.join(BRAIN_DIR, 'repo-map.json'); +const SNAPSHOT_PATH = path.join(BRAIN_DIR, 'repo-snapshot.md'); + +const now = new Date(); +const nowISO = now.toISOString(); +const nowDate = nowISO.slice(0, 10); +const nowBJ = new Date(now.getTime() + 8 * 3600 * 1000).toISOString() + .replace('T', ' ').slice(0, 16) + ' CST'; + +// ── 工具函数 ──────────────────────────────────────────────────────────────── + +function safeReadJson(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch (err) { + if (err.code !== 'ENOENT') { + console.warn(`⚠️ JSON读取失败: ${filePath} → ${err.message}`); + } + return null; + } +} + +function existsDir(p) { + try { return fs.statSync(p).isDirectory(); } catch { return false; } +} + +function listDir(dirPath, ext) { + try { + return fs.readdirSync(dirPath) + .filter(f => !ext || f.endsWith(ext)) + .filter(f => !f.startsWith('.') || f === '.gitkeep'); + } catch { return []; } +} + +function countFiles(dirPath, ext) { + try { + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + let count = 0; + for (const e of entries) { + if (e.isDirectory()) { + count += countFiles(path.join(dirPath, e.name), ext); + } else if (!ext || e.name.endsWith(ext)) { + count++; + } + } + return count; + } catch { return 0; } +} + +// ── 区域定义 ────────────────────────────────────────────────────────────── +// 每个"区域"对应图书馆里的一个大区(section) +// 每个区域下的文件是"书架(shelf)"上的"书" + +const ZONE_DEFS = [ + { + zone_id: 'BRAIN', + name: '铸渊大脑', + emoji: '🧠', + path: '.github/brain', + description: '铸渊核心记忆 · 路由映射 · 唤醒协议 · 图书馆目录', + keywords: ['brain', 'memory', 'routing', 'wake', '大脑', '记忆', '路由'] + }, + { + zone_id: 'PERSONA_BRAIN', + name: '人格大脑', + emoji: '🎭', + path: '.github/persona-brain', + description: '铸渊人格记忆 · 开发者状态 · 知识库 · 成长日记', + keywords: ['persona', 'identity', 'dev-status', '人格', '开发者状态'] + }, + { + zone_id: 'WORKFLOWS', + name: '自动化工作流', + emoji: '⚡', + path: '.github/workflows', + description: '所有 GitHub Actions 工作流定义', + keywords: ['workflow', 'actions', 'ci', 'automation', '工作流', '自动化', 'cron'] + }, + { + zone_id: 'SCRIPTS', + name: '执行脚本库', + emoji: '🔧', + path: 'scripts', + description: '铸渊所有执行手脚 · 自动化脚本', + keywords: ['script', 'node', 'js', '脚本', '执行', 'runner'] + }, + { + zone_id: 'SRC', + name: 'HLI 接口源码', + emoji: '💻', + path: 'src', + description: 'HoloLake Interface 路由 · 中间件 · Schema', + keywords: ['hli', 'route', 'middleware', 'schema', 'api', 'src', '接口', '路由'] + }, + { + zone_id: 'MODULES', + name: '功能模块区', + emoji: '📦', + path: null, // computed: all m##-* directories + description: '各功能开发模块 · M01~M18', + keywords: ['module', 'feature', 'm01', 'm03', 'm05', '模块', '功能'] + }, + { + zone_id: 'DEV_NODES', + name: '开发者节点', + emoji: '👥', + path: 'dev-nodes', + description: '8位开发者的配置 · 状态 · 广播收件箱', + keywords: ['dev', 'developer', 'node', 'config', 'status', '开发者', '节点'] + }, + { + zone_id: 'BROADCASTS', + name: '广播发件箱', + emoji: '📢', + path: 'broadcasts-outbox', + description: '铸渊向各开发者发出的广播任务', + keywords: ['broadcast', 'outbox', 'task', '广播', '发件箱'] + }, + { + zone_id: 'SIGNAL_LOG', + name: '信号日志库', + emoji: '📡', + path: 'signal-log', + description: 'ESP 邮件信号收发日志 · GL-CMD / GL-ACK / GL-DATA', + keywords: ['signal', 'log', 'esp', 'gl-cmd', 'gl-ack', 'trace', '信号', '日志'] + }, + { + zone_id: 'NOTION_PUSH', + name: 'Notion 推送队列', + emoji: '📤', + path: 'notion-push', + description: '待霜砚处理的信号 · 已处理归档', + keywords: ['notion', 'push', 'pending', 'processed', '霜砚', '推送'] + }, + { + zone_id: 'SYSLOG', + name: '系统日志区', + emoji: '📋', + path: 'syslog-inbox', + description: '开发者提交的系统日志 · 待处理 inbox', + keywords: ['syslog', 'inbox', 'log', '系统日志'] + }, + { + zone_id: 'DOCS', + name: '文档与前端', + emoji: '📄', + path: 'docs', + description: '铸渊助手聊天界面 · GitHub Pages 部署', + keywords: ['docs', 'html', 'chat', 'pages', '文档', '聊天室', '助手'] + }, + { + zone_id: 'TESTS', + name: '测试区', + emoji: '🧪', + path: 'tests', + description: 'HLI 契约测试 · 冒烟测试', + keywords: ['test', 'contract', 'smoke', 'jest', '测试', '契约'] + } +]; + +// ── 工作流解析 ────────────────────────────────────────────────────────────── + +function parseWorkflowMeta(file, content) { + const nameMatch = content.match(/^name:\s*(.+)/m); + const schedMatch = content.match(/cron:\s*['"]?([^'"#\n]+)/); + // Handle both block (`on:\n push:`) and inline (`on: [push, pull_request]`) syntax + const onInline = content.match(/^on:\s*\[([^\]]+)\]/m); + const onBlock = content.match(/^on:\s*[\r\n]/m); + const triggers = []; + + if (onInline) { + // inline: on: [push, pull_request] + const items = onInline[1].split(',').map(s => s.trim()); + triggers.push(...items); + } else if (onBlock) { + if (/^\s{2,4}push:/m.test(content)) triggers.push('push'); + if (/^\s{2,4}pull_request:/m.test(content)) triggers.push('pull_request'); + if (/^\s{2,4}issues:/m.test(content)) triggers.push('issues'); + if (/^\s{2,4}issue_comment:/m.test(content)) triggers.push('issue_comment'); + } + + if (schedMatch) triggers.push(`schedule(${schedMatch[1].trim()})`); + if (content.includes('workflow_dispatch')) triggers.push('manual'); + + return { + file, + name: nameMatch ? nameMatch[1].trim() : file, + triggers: triggers.length ? triggers : ['unknown'] + }; +} + +function buildWorkflowShelves() { + const wfDir = path.join(ROOT, '.github/workflows'); + const files = listDir(wfDir, '.yml'); + return files.map(f => { + try { + const content = fs.readFileSync(path.join(wfDir, f), 'utf8'); + return parseWorkflowMeta(f, content); + } catch { + return { file: f, name: f, triggers: ['unknown'] }; + } + }); +} + +// ── 模块目录扫描 ──────────────────────────────────────────────────────────── + +function buildModuleShelves() { + const entries = fs.readdirSync(ROOT, { withFileTypes: true }); + return entries + .filter(e => e.isDirectory() && /^m\d{2}-/.test(e.name)) + .map(e => { + const modPath = path.join(ROOT, e.name); + const files = listDir(modPath); + const readme = files.find(f => f.toLowerCase() === 'readme.md'); + return { + module_id: e.name.slice(0, 3).toUpperCase(), // "m01" → "M01" + dir: e.name, + files: files.length, + has_readme: !!readme + }; + }) + .sort((a, b) => a.dir.localeCompare(b.dir)); +} + +// ── HLI 接口统计 ──────────────────────────────────────────────────────────── + +function buildHLIStats(routingMap) { + if (!routingMap) return { total: 0, implemented: 0, pending: 0, domains: [] }; + let total = 0, implemented = 0; + const domains = []; + for (const [name, data] of Object.entries(routingMap.domains || {})) { + const t = data.interfaces.length; + const i = data.interfaces.filter(x => x.status === 'implemented').length; + total += t; + implemented += i; + domains.push({ + name, + module: data.module, + prefix: data.route_prefix, + total: t, + implemented: i, + interfaces: data.interfaces.map(x => ({ + id: x.id, + path: x.path, + status: x.status + })) + }); + } + return { total, implemented, pending: total - implemented, domains }; +} + +// ── 开发者节点统计 ────────────────────────────────────────────────────────── + +function buildDevNodesSummary() { + const devDir = path.join(ROOT, 'dev-nodes'); + if (!existsDir(devDir)) return []; + return fs.readdirSync(devDir, { withFileTypes: true }) + .filter(e => e.isDirectory() && e.name.startsWith('DEV-')) + .map(e => { + const cfg = safeReadJson(path.join(devDir, e.name, 'config.json')) || {}; + const sts = safeReadJson(path.join(devDir, e.name, 'status.json')) || {}; + return { + dev_id: e.name, + name: cfg.name || '?', + emoji: cfg.emoji || '', + modules: cfg.modules || [], + last_push: sts.last_push || null, + pending_broadcasts: sts.pending_broadcasts || 0 + }; + }) + .sort((a, b) => a.dev_id.localeCompare(b.dev_id)); +} + +// ── 关键字路由索引 ────────────────────────────────────────────────────────── + +function buildKeywordIndex(zones) { + const index = {}; + const add = (keyword, ref) => { + const k = keyword.toLowerCase(); + if (!index[k]) index[k] = []; + if (!index[k].includes(ref)) index[k].push(ref); + }; + + for (const zone of zones) { + for (const kw of (zone.keywords || [])) { + add(kw, zone.zone_id); + } + for (const shelf of (zone.shelves || [])) { + const basename = (shelf.file || shelf.dir || '').replace(/\.[^.]+$/, ''); + add(basename, `${zone.zone_id}::${shelf.file || shelf.dir}`); + } + } + return index; +} + +// ── 主生成逻辑 ────────────────────────────────────────────────────────────── + +function buildZoneData() { + const routingMap = safeReadJson(path.join(BRAIN_DIR, 'routing-map.json')); + const memory = safeReadJson(path.join(BRAIN_DIR, 'memory.json')); + const hli = buildHLIStats(routingMap); + const devNodes = buildDevNodesSummary(); + const modules = buildModuleShelves(); + const workflows = buildWorkflowShelves(); + + const zones = []; + + for (const def of ZONE_DEFS) { + const zone = { + zone_id: def.zone_id, + name: def.name, + emoji: def.emoji, + path: def.path, + description: def.description, + keywords: def.keywords, + shelves: [] + }; + + if (def.zone_id === 'MODULES') { + zone.shelves = modules; + zone.item_count = modules.length; + + } else if (def.zone_id === 'WORKFLOWS') { + zone.shelves = workflows; + zone.item_count = workflows.length; + + } else if (def.zone_id === 'DEV_NODES') { + zone.shelves = devNodes.map(d => ({ + file: `${d.dev_id}/`, + ...d + })); + zone.item_count = devNodes.length; + + } else if (def.path) { + const absPath = path.join(ROOT, def.path); + if (existsDir(absPath)) { + const files = listDir(absPath).filter(f => f !== '.gitkeep'); + zone.shelves = files.map(f => ({ file: f })); + zone.item_count = files.length; + } else { + zone.item_count = 0; + } + } + + zones.push(zone); + } + + return { zones, hli, devNodes, memory, routingMap }; +} + +// ── 生成 repo-map.json ────────────────────────────────────────────────────── + +function generateRepoMap(data) { + const { zones, hli, devNodes, memory } = data; + + const map = { + description: '铸渊图书馆目录 · Library Catalog for 铸渊 (Zhùyuān)', + version: '2.0', + generated_at: nowISO, + generated_by: 'scripts/generate-repo-map.js', + repo: 'qinfendebingshuo/guanghulab', + stats: { + zones: zones.length, + total_modules: zones.find(z => z.zone_id === 'MODULES')?.item_count || 0, + total_workflows: zones.find(z => z.zone_id === 'WORKFLOWS')?.item_count || 0, + total_scripts: zones.find(z => z.zone_id === 'SCRIPTS')?.item_count || 0, + total_dev_nodes: devNodes.length, + hli_interfaces: hli.total, + hli_implemented: hli.implemented, + hli_coverage_pct: hli.total > 0 + ? Math.round((hli.implemented / hli.total) * 100) + '%' + : '0%', + last_ci_run: memory?.events?.find(e => e.type === 'ci_run')?.timestamp || null, + memory_last_updated: memory?.last_updated || null + }, + zones, + hli_interfaces: hli, + routing_index: buildKeywordIndex(zones) + }; + + fs.writeFileSync(MAP_PATH, JSON.stringify(map, null, 2)); + console.log(`✅ repo-map.json 已生成 · ${zones.length} 个区域 · ${map.stats.total_workflows} 个工作流`); + return map; +} + +// ── 生成 repo-snapshot.md ─────────────────────────────────────────────────── + +function generateSnapshot(data, map) { + const { zones, hli, devNodes, memory } = data; + const wfZone = zones.find(z => z.zone_id === 'WORKFLOWS'); + const scriptZone = zones.find(z => z.zone_id === 'SCRIPTS'); + const modZone = zones.find(z => z.zone_id === 'MODULES'); + const brainZone = zones.find(z => z.zone_id === 'BRAIN'); + + const hliBadge = `${hli.implemented}/${hli.total} (${map.stats.hli_coverage_pct})`; + + let md = `# 铸渊图书馆快照 · Repo Snapshot +> 生成于 ${nowBJ} · 每次 push 自动更新 · 铸渊唤醒时优先读取此文件 + +--- + +## 📊 仓库总览(一眼全局) + +| 指标 | 数值 | +|------|------| +| 区域总数 | ${zones.length} 个区域 | +| 功能模块 | ${map.stats.total_modules} 个 (m01~m18) | +| 工作流 | ${map.stats.total_workflows} 个 GitHub Actions | +| 脚本 | ${map.stats.total_scripts} 个执行脚本 | +| 开发者节点 | ${map.stats.total_dev_nodes} 人 | +| HLI 接口覆盖率 | ${hliBadge} | +| 快照生成时间 | ${nowBJ} | + +--- + +## 🗺️ 图书馆全区地图 + +`; + + // Zone overview table + for (const z of zones) { + const count = z.item_count !== undefined ? z.item_count : z.shelves?.length || 0; + md += `### ${z.emoji} ${z.name}(${z.zone_id})\n`; + md += `**路径**: \`${z.path || '多个目录'}\` · **数量**: ${count} 项\n`; + md += `**描述**: ${z.description}\n`; + md += `**关键词**: ${(z.keywords || []).slice(0, 6).join(' · ')}\n\n`; + } + + // ── 自动化工作流详情 + if (wfZone?.shelves?.length) { + md += `---\n\n## ⚡ 工作流详情(铸渊的自动执行手脚)\n\n| 文件 | 名称 | 触发方式 |\n|------|------|----------|\n`; + for (const wf of wfZone.shelves) { + md += `| \`${wf.file}\` | ${wf.name} | ${wf.triggers.join(', ')} |\n`; + } + md += '\n'; + } + + // ── 脚本详情 + if (scriptZone?.shelves?.length) { + md += `---\n\n## 🔧 执行脚本库(铸渊的工作人员)\n\n`; + const scripts = scriptZone.shelves.map(s => s.file).sort(); + scripts.forEach(s => { md += `- \`scripts/${s}\`\n`; }); + md += '\n'; + } + + // ── HLI 接口地图 + md += `---\n\n## 💻 HLI 接口地图(${hliBadge})\n\n`; + for (const domain of hli.domains) { + const icon = domain.implemented === domain.total ? '✅' : domain.implemented > 0 ? '🔶' : '⬜'; + md += `${icon} **${domain.name}** (${domain.module}) \`${domain.prefix}\` — ${domain.implemented}/${domain.total}\n`; + for (const iface of domain.interfaces) { + const si = iface.status === 'implemented' ? ' ✓' : ' ○'; + md += `${si} \`${iface.id}\` → \`${iface.path}\`\n`; + } + } + md += '\n'; + + // ── 开发者节点 + if (devNodes.length > 0) { + md += `---\n\n## 👥 开发者节点(dev-nodes/)\n\n| DEV ID | 姓名 | 模块 | 待广播 |\n|--------|------|------|--------|\n`; + for (const d of devNodes) { + md += `| ${d.dev_id} | ${d.emoji}${d.name} | ${d.modules.join(', ') || '—'} | ${d.pending_broadcasts} |\n`; + } + md += '\n'; + } + + // ── 功能模块列表 + if (modZone?.shelves?.length) { + md += `---\n\n## 📦 功能模块区(各开发者工作目录)\n\n`; + for (const m of modZone.shelves) { + md += `- \`${m.dir}/\` — ${m.files} 个文件${m.has_readme ? ' (有README)' : ''}\n`; + } + md += '\n'; + } + + // ── 大脑文件速查 + if (brainZone?.shelves?.length) { + md += `---\n\n## 🧠 铸渊大脑文件速查(.github/brain/)\n\n`; + for (const s of brainZone.shelves) { + md += `- \`.github/brain/${s.file}\`\n`; + } + md += '\n'; + } + + // ── 最近记忆 + if (memory?.events?.length) { + md += `---\n\n## 🕐 最近动态(memory.json 最新3条)\n\n`; + const recent = memory.events.slice(-3).reverse(); + for (const e of recent) { + const ts = e.timestamp || e.date || '?'; + md += `- \`${ts}\` · ${e.type || '事件'} — ${e.description || e.result || e.title || ''}\n`; + } + md += '\n'; + } + + md += `---\n\n*本文件由 \`scripts/generate-repo-map.js\` 自动生成,勿手动编辑*\n`; + + fs.writeFileSync(SNAPSHOT_PATH, md); + console.log(`✅ repo-snapshot.md 已生成 · ${hli.domains.length} 个HLI域 · ${devNodes.length} 个开发者节点`); +} + +// ── 主流程 ────────────────────────────────────────────────────────────────── + +const data = buildZoneData(); +const map = generateRepoMap(data); +generateSnapshot(data, map); + +console.log(`\n📚 铸渊图书馆目录更新完成 · ${nowBJ}`); +console.log(` repo-map.json → .github/brain/repo-map.json`); +console.log(` repo-snapshot.md → .github/brain/repo-snapshot.md`);