diff --git a/.github/DISCUSSION_TEMPLATE/syslog-submit.yml b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml new file mode 100644 index 00000000..a0baaa94 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml @@ -0,0 +1,51 @@ +title: "BC-XXX-XXXSYSLOG" +labels: [] +body: + - type: markdown + attributes: + value: | + ## 📡 SYSLOG 自助提亀 / 广播提问 + + > 做完了把日志粘莎进去填䞊邮箱点提亀。新广播䌚发到䜠邮箱里。 + > + > 有问题把问题粘莎进去填䞊邮箱点提亀。解答䌚发到䜠邮箱里。 + + **标题栌匏**`BC-XXX-XXXSYSLOG` 或 `BC-XXX-XXX提问` + + - type: input + id: broadcast_id + attributes: + label: "广播猖号" + description: "写䞊䜠圓前广播的猖号䟋劂 BC-M22-009-AW" + placeholder: "BC-M22-009-AW" + validations: + required: true + + - type: dropdown + id: submit_type + attributes: + label: "类型" + description: "选择提亀类型" + options: + - SYSLOG + - 提问 + validations: + required: true + + - type: input + id: email + attributes: + label: "䜠的邮箱" + description: "甚于接收新广播或问题解答" + placeholder: "your@email.com" + validations: + required: true + + - type: textarea + id: content + attributes: + label: "内容" + description: "粘莎䜠的 SYSLOG å…šæ–‡ 或 问题描述" + placeholder: "圚这里粘莎䜠的 SYSLOG 或问题..." + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/syslog-submit.yml b/.github/ISSUE_TEMPLATE/syslog-submit.yml new file mode 100644 index 00000000..80a87d27 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/syslog-submit.yml @@ -0,0 +1,54 @@ +name: "📡 提亀系统日志 / 提问" +description: "做完了提亀䜠的 SYSLOG。有问题圚这里提问。" +title: "BC-XXX-XXXSYSLOG" +labels: ["syslog", "pending"] +assignees: [] +body: + - type: markdown + attributes: + value: | + ## 📡 SYSLOG 自助提亀 / 广播提问 + + > 做完了把日志粘莎进去填䞊邮箱点提亀。新广播䌚发到䜠邮箱里。 + > + > 有问题把问题粘莎进去填䞊邮箱点提亀。解答䌚发到䜠邮箱里。 + + **标题栌匏**`BC-XXX-XXXSYSLOG` 或 `BC-XXX-XXX提问` + + - type: input + id: broadcast_id + attributes: + label: "广播猖号" + description: "写䞊䜠圓前广播的猖号䟋劂 BC-M22-009-AW" + placeholder: "BC-M22-009-AW" + validations: + required: true + + - type: dropdown + id: submit_type + attributes: + label: "类型" + description: "选择提亀类型" + options: + - SYSLOG + - 提问 + validations: + required: true + + - type: input + id: email + attributes: + label: "䜠的邮箱" + description: "甚于接收新广播或问题解答" + placeholder: "your@email.com" + validations: + required: true + + - type: textarea + id: content + attributes: + label: "内容" + description: "粘莎䜠的 SYSLOG å…šæ–‡ 或 问题描述" + placeholder: "圚这里粘莎䜠的 SYSLOG 或问题..." + validations: + required: true diff --git a/.github/brain/bingshuo-issues-index.json b/.github/brain/bingshuo-issues-index.json index abbec8c8..eded71d9 100644 --- a/.github/brain/bingshuo-issues-index.json +++ b/.github/brain/bingshuo-issues-index.json @@ -1,7 +1,7 @@ { "version": "1.0", "description": "冰朔䞻控问题玢匕库 — 记圕已知问题、根因䞎排查路由", - "updated_at": "2026-03-13T02:05:55.611Z", + "updated_at": "2026-03-13T06:52:24.833Z", "issues": [ { "id": "BS-001", diff --git a/.github/brain/bingshuo-master-brain.md b/.github/brain/bingshuo-master-brain.md index 443bfaee..2f10a3cf 100644 --- a/.github/brain/bingshuo-master-brain.md +++ b/.github/brain/bingshuo-master-brain.md @@ -1,7 +1,7 @@ # 冰朔䞻控神经系统 · 栞心䞻控倧脑 v1.0 > 本文件䞺冰朔䞻控神经系统的总控脑文件。 -> 最后猖译时闎2026-03-13T02:05:55.612Z +> 最后猖译时闎2026-03-13T06:52:24.834Z --- @@ -56,7 +56,7 @@ ### 仓库统计 - 功胜暡块10 䞪 -- Workflow35 䞪 +- Workflow40 䞪 --- @@ -85,7 +85,7 @@ > 本区块由 master-brain-compiler 自劚猖译。 -- **猖译时闎**2026-03-13T02:05:55.612Z +- **猖译时闎**2026-03-13T06:52:24.834Z - **脑文件规则版本**v3.0 - **脑文件完敎性**✅ 完敎 @@ -107,7 +107,7 @@ |--------|------|------| | 🟡 brain_consistency | yellow | 䞻仓库脑文件完敎䜆䞎 persona-studio 脑文件的同步状态埅验证 | | 🟢 deployment_health | green | deploy-to-server.yml 侎 deploy-pages.yml 均存圚 | -| 🟢 workflow_health | green | 35 䞪 workflow 已泚册 | +| 🟢 workflow_health | green | 40 䞪 workflow 已泚册 | | 🟡 routing_health | yellow | HLI 接口芆盖率 33.3%7/21 | | 🟢 docs_entry_health | green | docs/index.html 存圚 | | 🟡 persona_studio_health | yellow | 前后端结构存圚端到端对话铟路埅验证 | diff --git a/.github/brain/bingshuo-system-health.json b/.github/brain/bingshuo-system-health.json index 56abef67..a1ee0628 100644 --- a/.github/brain/bingshuo-system-health.json +++ b/.github/brain/bingshuo-system-health.json @@ -1,7 +1,7 @@ { "version": "1.0", "description": "冰朔䞻控系统健康状态", - "updated_at": "2026-03-13T02:05:55.611Z", + "updated_at": "2026-03-13T06:52:24.833Z", "health": { "brain_consistency": { "status": "yellow", @@ -13,7 +13,7 @@ }, "workflow_health": { "status": "green", - "detail": "35 䞪 workflow 已泚册" + "detail": "40 䞪 workflow 已泚册" }, "routing_health": { "status": "yellow", diff --git a/.github/brain/memory.json b/.github/brain/memory.json index 378b77c4..e07d7cd6 100644 --- a/.github/brain/memory.json +++ b/.github/brain/memory.json @@ -1,7 +1,7 @@ { "identity": "铞枊Zhùyuān· GitHub 代码守技人栌䜓", "rules_version": "v3.0", - "last_updated": "2026-03-12T08:55:54.205Z", + "last_updated": "2026-03-13T08:53:32.764Z", "wake_protocol_version": "v3.0", "brain_version": "v3.0", "architecture": { @@ -67,6 +67,18 @@ } }, "events": [ + { + "date": "2026-03-13", + "type": "brain_restore", + "description": "铞枊栞心倧脑恢倍 · Persona Studio 铞枊代理匀发工䜜流䞊线 · 修倍「我芁匀发」功胜无法觊发代理匀发 · Agent 5步自劚化工䜜流需求分析→架构规划→代码生成→预览郚眲→完成通知 · 䜿甚甚户API Key驱劚 · 支持GUEST暡匏 · WebSocket时序修倍 · 错误反銈增区", + "by": "铞枊冰朔指什" + }, + { + "date": "2026-03-13", + "type": "psp_inspection", + "description": "铞枊 PSP 巡检通过 · 党郚检查项 ✅", + "by": "铞枊PSP巡检" + }, { "date": "2026-03-12", "type": "psp_inspection", @@ -173,6 +185,14 @@ "actor": "qinfendebingshuo", "ref": "refs/heads/main", "run_id": "22993977564" + }, + { + "timestamp": "2026-03-13T08:53:32.764Z", + "type": "daily_check", + "result": "passed", + "actor": "qinfendebingshuo", + "ref": "refs/heads/main", + "run_id": "23043373769" } ] } diff --git a/.github/brain/repo-map.json b/.github/brain/repo-map.json index 45ae82d7..82975a73 100644 --- a/.github/brain/repo-map.json +++ b/.github/brain/repo-map.json @@ -1,20 +1,20 @@ { "description": "铞枊囟乊銆目圕 · Library Catalog for 铞枊 (Zhùyuān)", "version": "2.0", - "generated_at": "2026-03-13T02:07:22.787Z", + "generated_at": "2026-03-13T06:52:39.931Z", "generated_by": "scripts/generate-repo-map.js", "repo": "qinfendebingshuo/guanghulab", "stats": { "zones": 13, "total_modules": 10, - "total_workflows": 35, - "total_scripts": 32, + "total_workflows": 40, + "total_scripts": 39, "total_dev_nodes": 8, "hli_interfaces": 21, "hli_implemented": 7, "hli_coverage_pct": "33%", "last_ci_run": "2026-03-05T16:07:24.070Z", - "memory_last_updated": "2026-03-12T08:55:54.205Z" + "memory_last_updated": "2026-03-13T03:31:06.282Z" }, "zones": [ { @@ -266,6 +266,13 @@ "pull_request" ] }, + { + "file": "notion-callback-pipeline.yml", + "name": "Notion Callback Pipeline", + "triggers": [ + "unknown" + ] + }, { "file": "notion-connectivity-test.yml", "name": "铞枊 · Notion 连通性测试", @@ -273,6 +280,14 @@ "manual" ] }, + { + "file": "notion-heartbeat.yml", + "name": "Notion Heartbeat Monitor", + "triggers": [ + "schedule(*/5 * * * *)", + "manual" + ] + }, { "file": "notion-poll.yml", "name": "铞枊 · Notion 工单蜮询", @@ -281,6 +296,13 @@ "manual" ] }, + { + "file": "persona-invoke.yml", + "name": "Persona Invoke Endpoint", + "triggers": [ + "manual" + ] + }, { "file": "process-notion-orders.yml", "name": "Process Notion Work Orders", @@ -362,6 +384,20 @@ "manual" ] }, + { + "file": "syslog-auto-pipeline.yml", + "name": "SYSLOG Auto Pipeline", + "triggers": [ + "unknown" + ] + }, + { + "file": "syslog-issue-pipeline.yml", + "name": "SYSLOG Issue Pipeline", + "triggers": [ + "issues" + ] + }, { "file": "syslog-pipeline.yml", "name": "铞枊 · SYSLOG Pipeline (A/D/E)", @@ -435,7 +471,7 @@ ] } ], - "item_count": 35 + "item_count": 40 }, { "zone_id": "SCRIPTS", @@ -464,6 +500,9 @@ { "file": "contract-check.js" }, + { + "file": "create-standardized-ticket.js" + }, { "file": "cross-repo-sync.js" }, @@ -485,6 +524,9 @@ { "file": "generate-session-summary.js" }, + { + "file": "invoke-persona.js" + }, { "file": "notify-module-received.js" }, @@ -494,6 +536,9 @@ { "file": "notion-connectivity-test.js" }, + { + "file": "notion-heartbeat.js" + }, { "file": "notion-signal-bridge.js" }, @@ -506,6 +551,9 @@ { "file": "psp-inspection.js" }, + { + "file": "push-broadcast-to-github.js" + }, { "file": "push-broadcast.js" }, @@ -536,6 +584,15 @@ { "file": "update-readme-bulletin.js" }, + { + "file": "utils" + }, + { + "file": "verify-modules.js" + }, + { + "file": "wake-persona.js" + }, { "file": "zhuyuan-daily-agent.js" }, @@ -549,7 +606,7 @@ "file": "zhuyuan-module-protocol.js" } ], - "item_count": 32 + "item_count": 39 }, { "zone_id": "SRC", @@ -1313,13 +1370,23 @@ "hli-contract-check": [ "WORKFLOWS::hli-contract-check.yml" ], + "notion-callback-pipeline": [ + "WORKFLOWS::notion-callback-pipeline.yml" + ], "notion-connectivity-test": [ "WORKFLOWS::notion-connectivity-test.yml", "SCRIPTS::notion-connectivity-test.js" ], + "notion-heartbeat": [ + "WORKFLOWS::notion-heartbeat.yml", + "SCRIPTS::notion-heartbeat.js" + ], "notion-poll": [ "WORKFLOWS::notion-poll.yml" ], + "persona-invoke": [ + "WORKFLOWS::persona-invoke.yml" + ], "process-notion-orders": [ "WORKFLOWS::process-notion-orders.yml" ], @@ -1356,6 +1423,12 @@ "sync-persona-studio": [ "WORKFLOWS::sync-persona-studio.yml" ], + "syslog-auto-pipeline": [ + "WORKFLOWS::syslog-auto-pipeline.yml" + ], + "syslog-issue-pipeline": [ + "WORKFLOWS::syslog-issue-pipeline.yml" + ], "syslog-pipeline": [ "WORKFLOWS::syslog-pipeline.yml" ], @@ -1415,6 +1488,9 @@ "contract-check": [ "SCRIPTS::contract-check.js" ], + "create-standardized-ticket": [ + "SCRIPTS::create-standardized-ticket.js" + ], "cross-repo-sync": [ "SCRIPTS::cross-repo-sync.js" ], @@ -1430,6 +1506,9 @@ "generate-session-summary": [ "SCRIPTS::generate-session-summary.js" ], + "invoke-persona": [ + "SCRIPTS::invoke-persona.js" + ], "notify-module-received": [ "SCRIPTS::notify-module-received.js" ], @@ -1448,6 +1527,9 @@ "psp-inspection": [ "SCRIPTS::psp-inspection.js" ], + "push-broadcast-to-github": [ + "SCRIPTS::push-broadcast-to-github.js" + ], "route-align-check": [ "SCRIPTS::route-align-check.js" ], @@ -1466,6 +1548,15 @@ "update-memory": [ "SCRIPTS::update-memory.js" ], + "utils": [ + "SCRIPTS::utils" + ], + "verify-modules": [ + "SCRIPTS::verify-modules.js" + ], + "wake-persona": [ + "SCRIPTS::wake-persona.js" + ], "zhuyuan-module-protocol": [ "SCRIPTS::zhuyuan-module-protocol.js" ], diff --git a/.github/brain/repo-snapshot.md b/.github/brain/repo-snapshot.md index 4e21ee97..d67e857d 100644 --- a/.github/brain/repo-snapshot.md +++ b/.github/brain/repo-snapshot.md @@ -1,5 +1,5 @@ # 铞枊囟乊銆快照 · Repo Snapshot -> 生成于 2026-03-13 10:07 CST · 每次 push 自劚曎新 · 铞枊唀醒时䌘先读取歀文件 +> 生成于 2026-03-13 14:52 CST · 每次 push 自劚曎新 · 铞枊唀醒时䌘先读取歀文件 --- @@ -9,11 +9,11 @@ |------|------| | 区域总数 | 13 䞪区域 | | 功胜暡块 | 10 䞪 (m01~m18) | -| 工䜜流 | 35 䞪 GitHub Actions | -| 脚本 | 32 䞪执行脚本 | +| 工䜜流 | 40 䞪 GitHub Actions | +| 脚本 | 39 䞪执行脚本 | | 匀发者节点 | 8 人 | | HLI 接口芆盖率 | 7/21 (33%) | -| 快照生成时闎 | 2026-03-13 10:07 CST | +| 快照生成时闎 | 2026-03-13 14:52 CST | --- @@ -30,12 +30,12 @@ **关键词**: persona · identity · dev-status · 人栌 · 匀发者状态 ### ⚡ 自劚化工䜜流WORKFLOWS -**路埄**: `.github/workflows` · **数量**: 35 项 +**路埄**: `.github/workflows` · **数量**: 40 项 **描述**: 所有 GitHub Actions 工䜜流定义 **关键词**: workflow · actions · ci · automation · 工䜜流 · 自劚化 ### 🔧 执行脚本库SCRIPTS -**路埄**: `scripts` · **数量**: 32 项 +**路埄**: `scripts` · **数量**: 39 项 **描述**: 铞枊所有执行手脚 · 自劚化脚本 **关键词**: script · node · js · 脚本 · 执行 · runner @@ -103,8 +103,11 @@ | `esp-signal-processor.yml` | 铞枊 · ESP 邮件信号倄理噚已暂停 | schedule(*/30 * * * *), manual | | `generate-module-doc.yml` | 铞枊 · 光湖纪元 暡块文档自劚生成 | push, manual | | `hli-contract-check.yml` | HLI Contract Check | push, pull_request | +| `notion-callback-pipeline.yml` | Notion Callback Pipeline | unknown | | `notion-connectivity-test.yml` | 铞枊 · Notion 连通性测试 | manual | +| `notion-heartbeat.yml` | Notion Heartbeat Monitor | schedule(*/5 * * * *), manual | | `notion-poll.yml` | 铞枊 · Notion 工单蜮询 | schedule(*/15 * * * *), manual | +| `persona-invoke.yml` | Persona Invoke Endpoint | manual | | `process-notion-orders.yml` | Process Notion Work Orders | push, manual | | `ps-on-build.yml` | "🌊 Persona Studio · 代码生成" | manual | | `ps-on-chat.yml` | "🌊 Persona Studio · 对话倄理" | manual | @@ -116,6 +119,8 @@ | `staging-preview.yml` | "🔍 铞枊预挔郚眲 (Staging Preview)" | pull_request, manual | | `sync-login-entry.yml` | 铞枊 · Sync Login Entry · Notion → 飞乊文档A | manual | | `sync-persona-studio.yml` | 🔄 铞枊跚仓库同步 · persona-studio | push, manual | +| `syslog-auto-pipeline.yml` | SYSLOG Auto Pipeline | unknown | +| `syslog-issue-pipeline.yml` | SYSLOG Issue Pipeline | issues | | `syslog-pipeline.yml` | 铞枊 · SYSLOG Pipeline (A/D/E) | push, manual | | `test-notion-bridge.yml` | "🧪 Notion Bridge Connectivity Test" | push, manual | | `update-readme-bulletin.yml` | 📢 曎新系统公告区 | push, schedule(0 1 * * *), manual | @@ -134,6 +139,7 @@ - `scripts/bingshuo-neural-sync.js` - `scripts/brain-bridge-sync.js` - `scripts/contract-check.js` +- `scripts/create-standardized-ticket.js` - `scripts/cross-repo-sync.js` - `scripts/daily-check.js` - `scripts/distribute-broadcasts.js` @@ -141,13 +147,16 @@ - `scripts/generate-module-doc.js` - `scripts/generate-repo-map.js` - `scripts/generate-session-summary.js` +- `scripts/invoke-persona.js` - `scripts/notify-module-received.js` - `scripts/notion-bridge.js` - `scripts/notion-connectivity-test.js` +- `scripts/notion-heartbeat.js` - `scripts/notion-signal-bridge.js` - `scripts/process-broadcasts.js` - `scripts/process-syslog.js` - `scripts/psp-inspection.js` +- `scripts/push-broadcast-to-github.js` - `scripts/push-broadcast.js` - `scripts/receive-syslog.js` - `scripts/route-align-check.js` @@ -158,6 +167,9 @@ - `scripts/update-brain.js` - `scripts/update-memory.js` - `scripts/update-readme-bulletin.js` +- `scripts/utils` +- `scripts/verify-modules.js` +- `scripts/wake-persona.js` - `scripts/zhuyuan-daily-agent.js` - `scripts/zhuyuan-daily-selfcheck.js` - `scripts/zhuyuan-issue-reply.js` diff --git a/.github/persona-brain/memory.json b/.github/persona-brain/memory.json index 6cfa0e99..dae0f307 100644 --- a/.github/persona-brain/memory.json +++ b/.github/persona-brain/memory.json @@ -2,6 +2,12 @@ "persona_id": "ICE-GL-ZY001", "persona_name": "铞枊", "recent_events": [ + { + "date": "2026-03-13", + "type": "brain_restore", + "description": "栞心倧脑恢倍 · Persona Studio 铞枊代理匀发工䜜流䞊线 · 「我芁匀发」功胜修倍 · Agent自劚化5步匀发 · 支持GUEST暡匏 · 䜿甚甚户API Key驱劚", + "by": "铞枊冰朔指什" + }, { "date": "2026-03-11", "type": "architecture_upgrade", diff --git a/.github/workflows/notion-callback-pipeline.yml b/.github/workflows/notion-callback-pipeline.yml new file mode 100644 index 00000000..463d7ced --- /dev/null +++ b/.github/workflows/notion-callback-pipeline.yml @@ -0,0 +1,317 @@ +name: Notion Callback Pipeline +# 📥 Notion → GitHub 回䌠闭环管道 +# +# 圓 Notion 䟧栞心倧脑人栌䜓倄理完 SYSLOG 工单后 +# 通过 repository_dispatch 回䌠倄理结果到 GitHub。 +# 铞枊 Agent 接收回䌠重新唀醒栞心倧脑讀知进行二次栞验 +# 确讀新广播生成无误后自劚发送邮件通知匀发者。 +# +# 觊发方匏 +# Notion 䟧 Agent 调甚 GitHub API 发送 repository_dispatch 事件 +# event_type: notion-syslog-callback +# client_payload: { +# broadcast_id: "BC-XXX-XXX-XX", +# dev_email: "developer@email.com", +# notion_result: "Notion 䟧倄理结果文本", +# ticket_id: "Notion 工单 ID", +# status: "approved" | "needs_revision" +# } +# +# 闭环流皋 +# ① Notion 䟧倄理完成 → repository_dispatch 回䌠 +# ② 铞枊 Agent 接收并解析回䌠数据 +# ③ 重新唀醒铞枊栞心倧脑讀知二次栞验 +# ④ 栞心倧脑栞对广播生成无误 +# â‘€ 铞枊 Agent 发送邮件通知匀发者 +# ⑥ 闭环结束 +# +# 䟝赖 Secrets +# LLM_API_KEY 第䞉方 LLM 平台密钥 +# LLM_BASE_URL 第䞉方 LLM 平台 API 地址 +# NOTION_API_TOKEN Notion API token +# CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 ID +# PORTRAIT_DB_ID 匀发者劚态画像库数据库 ID +# FINGERPRINT_DB_ID 暡块指纹泚册衚数据库 ID +# SMTP_USER QQ 邮箱地址 +# SMTP_PASS QQ 邮箱 SMTP 授权码 + +on: + repository_dispatch: + types: [notion-syslog-callback] + +jobs: + verify-and-notify: + name: 📥 Notion 回䌠 · 二次栞验 · 邮件通知 + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: 📥 Parse Notion callback + id: callback + uses: actions/github-script@v7 + with: + script: | + const payload = context.payload.client_payload || {}; + + const broadcastId = payload.broadcast_id || ''; + const devEmail = payload.dev_email || ''; + const notionResult = payload.notion_result || ''; + const ticketId = payload.ticket_id || ''; + const status = payload.status || 'unknown'; + + if (!broadcastId) { + core.setFailed('❌ 猺少 broadcast_id'); + return; + } + + core.setOutput('broadcast_id', broadcastId); + core.setOutput('dev_email', devEmail); + core.setOutput('notion_result', notionResult); + core.setOutput('ticket_id', ticketId); + core.setOutput('status', status); + + console.log(`📥 Notion 回䌠: 广播=${broadcastId}, 状态=${status}, 工单=${ticketId}`); + + - name: 🔍 铞枊 Agent · 暡块二次验证 + id: verify + env: + SYSLOG_CONTENT: ${{ steps.callback.outputs.notion_result }} + BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }} + AUTHOR: notion-callback + run: node scripts/verify-modules.js + + - name: 🧠 二次栞验 · 唀醒铞枊栞心倧脑 + id: persona + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }} + SUBMIT_TYPE: syslog + SUBMIT_CONTENT: ${{ steps.callback.outputs.notion_result }} + AUTHOR: notion-callback + MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }} + NOTION_CALLBACK_RESULT: ${{ steps.callback.outputs.notion_result }} + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }} + PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }} + FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }} + run: node scripts/wake-persona.js + + - name: 📡 掚送广播到 GitHubPhase B4 + if: steps.persona.outcome == 'success' && steps.callback.outputs.status == 'approved' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }} + DEVELOPER_NAME: notion-callback + BROADCAST_CONTENT: ${{ steps.persona.outputs.result }} + run: node scripts/push-broadcast-to-github.js + + - name: 📝 回写 Notion 工单状态 + if: steps.callback.outputs.ticket_id + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + TICKET_ID: ${{ steps.callback.outputs.ticket_id }} + CALLBACK_STATUS: ${{ steps.callback.outputs.status }} + run: | + node -e " + var https = require('https'); + var token = process.env.NOTION_TOKEN || ''; + var ticketId = process.env.TICKET_ID || ''; + var status = process.env.CALLBACK_STATUS || 'unknown'; + + if (!token || !ticketId) { + console.log('⚠ 跳过工单状态曎新'); + process.exit(0); + } + + var notionStatus = status === 'approved' ? '✅ 已完成' : '⚠ 匂垞·等人工介入'; + var body = JSON.stringify({ + properties: { + '状态': { select: { name: notionStatus } } + } + }); + + var opts = { + hostname: 'api.notion.com', + port: 443, + path: '/v1/pages/' + ticketId, + method: 'PATCH', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json', + 'Notion-Version': '2022-06-28', + 'Content-Length': Buffer.byteLength(body) + } + }; + + var req = https.request(opts, function(res) { + var data = ''; + res.on('data', function(c) { data += c; }); + res.on('end', function() { + if (res.statusCode >= 200 && res.statusCode < 300) { + console.log('✅ 工单状态已曎新䞺 ' + notionStatus); + } else { + console.log('⚠ 工单曎新倱莥: ' + res.statusCode); + } + }); + }); + req.on('error', function(e) { console.log('⚠ ' + e.message); }); + req.write(body); + req.end(); + " + + - name: 📧 发送最终确讀邮件 + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + EMAIL_TO: ${{ steps.callback.outputs.dev_email }} + BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }} + NOTION_STATUS: ${{ steps.callback.outputs.status }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + run: | + node -e " + const nodemailer = require('nodemailer'); + + const user = process.env.SMTP_USER || ''; + const pass = process.env.SMTP_PASS || ''; + const to = process.env.EMAIL_TO || ''; + const broadcastId = process.env.BROADCAST_ID || ''; + const notionStatus = process.env.NOTION_STATUS || 'unknown'; + const result = process.env.PERSONA_RESULT || '(倄理䞭)'; + + if (!user || !pass) { + console.log('⚠ SMTP not configured, skipping email'); + process.exit(0); + } + if (!to) { + console.log('⚠ No recipient email, skipping'); + process.exit(0); + } + + const isApproved = notionStatus === 'approved'; + const subjectText = isApproved + ? '[光湖系统] ' + broadcastId + ' · ✅ 新广播已生成已栞验' + : '[光湖系统] ' + broadcastId + ' · ⚠ 广播需修订'; + + const statusBadge = isApproved + ? '✅ 已栞验通过' + : '⚠ 需修订'; + + const resultHtml = result + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + + const html = [ + '
', + '
', + '

🌊 光湖系统 · Notion 回䌠栞验通知

', + '

HoloLake · 铞枊栞心倧脑二次栞验完成

', + '
', + '
', + '

📡 ' + broadcastId + '

', + '

' + statusBadge + '

', + '
' + resultHtml + '
', + '
', + '
', + '↩ 回到仓库继续匀发', + '

🌀 铞枊 · 代码守技人栌䜓 · Notion↔GitHub 闭环确讀

', + '
', + '
' + ].join('\n'); + + const transporter = nodemailer.createTransport({ + host: 'smtp.qq.com', + port: 465, + secure: true, + auth: { user, pass } + }); + + transporter.sendMail({ + from: '\"光湖系统\" <' + user + '>', + to: to, + subject: subjectText, + html: html + }).then((info) => { + console.log('✅ 最终确讀邮件已发送: ' + info.messageId); + }).catch((err) => { + console.log('⚠ 邮件发送倱莥: ' + err.message); + }); + " + + - name: 📝 曎新倧脑记忆 + run: | + node -e " + const fs = require('fs'); + const path = require('path'); + + const memoryPath = path.join('.github', 'brain', 'memory.json'); + let memory = {}; + try { + memory = JSON.parse(fs.readFileSync(memoryPath, 'utf8')); + } catch (_) { + memory = { events: [] }; + } + if (!memory.events) memory.events = []; + + const broadcastId = process.env.BROADCAST_ID || 'UNKNOWN'; + const status = process.env.NOTION_STATUS || 'unknown'; + const now = new Date().toISOString(); + + // 检查是吊已存圚盞同事件按 broadcast_id 粟确匹配 + 同类型 + 同日期 + const eventKey = 'Notion 回䌠栞验 · ' + broadcastId; + const todayStr = now.slice(0, 10); + const existingIdx = memory.events.findIndex(function(e) { + return e.type === 'notion_callback' && + e.broadcast_id === broadcastId && + e.date === todayStr; + }); + + const newEvent = { + date: todayStr, + type: 'notion_callback', + broadcast_id: broadcastId, + description: eventKey + ' · 状态: ' + status, + by: '铞枊Agent·Notion回䌠管道' + }; + + if (existingIdx >= 0) { + // 同䞀 broadcast_id 只保留最新的䞀条 + memory.events[existingIdx] = newEvent; + console.log('ℹ 已曎新同日同广播事件'); + } else { + memory.events.unshift(newEvent); + } + + // 保留最近 20 条事件 + if (memory.events.length > 20) { + memory.events = memory.events.slice(0, 20); + } + + fs.writeFileSync(memoryPath, JSON.stringify(memory, null, 2) + '\n'); + console.log('✅ 倧脑记忆已曎新'); + " + env: + BROADCAST_ID: ${{ steps.callback.outputs.broadcast_id }} + NOTION_STATUS: ${{ steps.callback.outputs.status }} + + - name: 📀 Commit memory update + run: | + git config user.name "zhuyuan-agent[bot]" + git config user.email "zhuyuan-agent[bot]@users.noreply.github.com" + git add .github/brain/memory.json + git diff --cached --quiet || git commit -m "🧠 Notion回䌠栞验 · ${{ steps.callback.outputs.broadcast_id }}" + git push || echo "⚠ Push skipped (no changes or conflict)" diff --git a/.github/workflows/notion-heartbeat.yml b/.github/workflows/notion-heartbeat.yml new file mode 100644 index 00000000..76653dc0 --- /dev/null +++ b/.github/workflows/notion-heartbeat.yml @@ -0,0 +1,47 @@ +name: Notion Heartbeat Monitor +# 💓 Phase B3 · 工单心跳监控 +# +# 每 5 分钟检测 Notion 工单队列䞭的埅倄理工单 +# - 超过 5 分钟未回执 → 重新觊发人栌䜓唀醒最倚 3 次 +# - 3 次重试仍无回执 → 标记工单匂垞 + 发邮件通知冰朔 +# +# 䟝赖 Secrets +# NOTION_API_TOKEN Notion API token +# NOTION_TICKET_DB_ID 工单队列数据库 ID +# SMTP_USER 邮件发送者 +# SMTP_PASS 邮件授权码 + +on: + schedule: + - cron: '*/5 * * * *' + workflow_dispatch: + +jobs: + heartbeat: + name: 💓 工单心跳检测 + runs-on: ubuntu-latest + permissions: + contents: read + actions: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: 💓 执行心跳检测 + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + NOTION_TICKET_DB_ID: ${{ secrets.NOTION_TICKET_DB_ID }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + ALERT_EMAIL: '565183519@qq.com' + run: node scripts/notion-heartbeat.js diff --git a/.github/workflows/persona-invoke.yml b/.github/workflows/persona-invoke.yml new file mode 100644 index 00000000..e2ab5d45 --- /dev/null +++ b/.github/workflows/persona-invoke.yml @@ -0,0 +1,248 @@ +name: Persona Invoke Endpoint +# 🔗 Phase B2 · Notion Agent → 铞枊人栌䜓唀醒 +# +# Notion Agent 通过 workflow_dispatch 觊发歀工䜜流 +# 等效于 POST /api/persona/invoke 接口。 +# +# 完敎流皋 +# ① Notion Agent 发送 workflow_dispatch䌠入工单信息 +# ② 铞枊 Agent 从 Notion 读取工单内容 +# ③ 唀醒人栌䜓Claude API倄理 SYSLOG +# ④ 倄理结果写回 Notion 工单receipt_status = completed +# â‘€ 广播文件掚送到 GitHub 仓库 broadcasts/{dev_id}/ +# +# 䟝赖 Secrets +# LLM_API_KEY 第䞉方 LLM 平台密钥 +# LLM_BASE_URL 第䞉方 LLM 平台 API 地址 +# NOTION_API_TOKEN Notion API token +# NOTION_TICKET_DB_ID 工单队列数据库 ID +# CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 ID +# PORTRAIT_DB_ID 匀发者劚态画像库 ID +# FINGERPRINT_DB_ID 暡块指纹泚册衚 ID +# INVOKE_API_KEY 调甚鉎权密钥 + +on: + workflow_dispatch: + inputs: + work_order_id: + description: 'Notion 工单页面 ID' + required: true + type: string + task_id: + description: '广播猖号劂 BC-M23-001-AW' + required: true + type: string + developer: + description: '匀发者信息劂 DEV-012 Awen' + required: false + default: 'unknown' + type: string + syslog_raw: + description: 'SYSLOG JSON 原文可选劂䞺空则从 Notion 读取' + required: false + default: '' + type: string + action: + description: '劚䜜类型' + required: false + default: 'process_syslog' + type: string + +jobs: + invoke: + name: 🔗 唀醒人栌䜓倄理 SYSLOG + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: 🔍 暡块验证 + id: verify + env: + SYSLOG_CONTENT: ${{ inputs.syslog_raw }} + BROADCAST_ID: ${{ inputs.task_id }} + AUTHOR: ${{ inputs.developer }} + run: node scripts/verify-modules.js + + - name: 🧠 唀醒人栌䜓 + id: persona + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + BROADCAST_ID: ${{ inputs.task_id }} + SUBMIT_TYPE: syslog + SUBMIT_CONTENT: ${{ inputs.syslog_raw }} + AUTHOR: ${{ inputs.developer }} + MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }} + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }} + PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }} + FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }} + run: node scripts/wake-persona.js + + - name: 📝 回写 Notion 工单 + if: always() + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + WORK_ORDER_ID: ${{ inputs.work_order_id }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + TASK_ID: ${{ inputs.task_id }} + run: | + node -e " + var https = require('https'); + + var token = process.env.NOTION_TOKEN || ''; + var workOrderId = process.env.WORK_ORDER_ID || ''; + var result = process.env.PERSONA_RESULT || '(no result)'; + var taskId = process.env.TASK_ID || ''; + + if (!token || !workOrderId) { + console.log('⚠ 猺少 Notion 配眮跳过回写'); + process.exit(0); + } + + // 曎新工单状态䞺 ✅ 已完成 + var body = JSON.stringify({ + properties: { + '状态': { select: { name: '✅ 已完成' } } + } + }); + + var opts = { + hostname: 'api.notion.com', + port: 443, + path: '/v1/pages/' + workOrderId, + method: 'PATCH', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json', + 'Notion-Version': '2022-06-28', + 'Content-Length': Buffer.byteLength(body) + } + }; + + var req = https.request(opts, function(res) { + var data = ''; + res.on('data', function(c) { data += c; }); + res.on('end', function() { + if (res.statusCode >= 200 && res.statusCode < 300) { + console.log('✅ 工单状态已曎新䞺 ✅ 已完成'); + } else { + console.log('⚠ 工单状态曎新倱莥: ' + res.statusCode); + } + }); + }); + req.on('error', function(e) { console.log('⚠ ' + e.message); }); + req.write(body); + req.end(); + + // 远加倄理结果到工单 + var appendBody = JSON.stringify({ + children: [{ + object: 'block', + type: 'heading_2', + heading_2: { + rich_text: [{ type: 'text', text: { content: '🧠 人栌䜓倄理结果 · receipt_status: completed' } }] + } + }, { + object: 'block', + type: 'paragraph', + paragraph: { + rich_text: [{ type: 'text', text: { content: result.slice(0, 2000) } }] + } + }] + }); + + var appendOpts = { + hostname: 'api.notion.com', + port: 443, + path: '/v1/blocks/' + workOrderId + '/children', + method: 'PATCH', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json', + 'Notion-Version': '2022-06-28', + 'Content-Length': Buffer.byteLength(appendBody) + } + }; + + var req2 = https.request(appendOpts, function(res) { + var data = ''; + res.on('data', function(c) { data += c; }); + res.on('end', function() { + if (res.statusCode >= 200 && res.statusCode < 300) { + console.log('✅ 倄理结果已远加到工单'); + } else { + console.log('⚠ 结果远加倱莥: ' + res.statusCode); + } + }); + }); + req2.on('error', function(e) { console.log('⚠ ' + e.message); }); + req2.write(appendBody); + req2.end(); + " + + - name: 📡 掚送广播到 GitHub + if: steps.persona.outcome == 'success' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BROADCAST_ID: ${{ inputs.task_id }} + DEVELOPER_ID: '' + DEVELOPER_NAME: ${{ inputs.developer }} + BROADCAST_CONTENT: ${{ steps.persona.outputs.result }} + run: node scripts/push-broadcast-to-github.js + + - name: 🔎 倱莥倄理 + if: failure() + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + WORK_ORDER_ID: ${{ inputs.work_order_id }} + run: | + node -e " + var https = require('https'); + var token = process.env.NOTION_TOKEN || ''; + var workOrderId = process.env.WORK_ORDER_ID || ''; + + if (!token || !workOrderId) process.exit(0); + + var body = JSON.stringify({ + properties: { + '状态': { select: { name: '⚠ 匂垞·等人工介入' } } + } + }); + + var opts = { + hostname: 'api.notion.com', + port: 443, + path: '/v1/pages/' + workOrderId, + method: 'PATCH', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json', + 'Notion-Version': '2022-06-28', + 'Content-Length': Buffer.byteLength(body) + } + }; + + var req = https.request(opts, function(res) { + var data = ''; + res.on('data', function(c) { data += c; }); + res.on('end', function() { + console.log('工单状态已标记䞺 ⚠ 匂垞'); + }); + }); + req.on('error', function(e) { console.log(e.message); }); + req.write(body); + req.end(); + " diff --git a/.github/workflows/syslog-auto-pipeline.yml b/.github/workflows/syslog-auto-pipeline.yml new file mode 100644 index 00000000..a0d84b10 --- /dev/null +++ b/.github/workflows/syslog-auto-pipeline.yml @@ -0,0 +1,343 @@ +name: SYSLOG Auto Pipeline +# 📡 SYSLOG 自助提亀系统 · 党自劚闭环 +# +# 匀发者圚 GitHub Discussion 提亀 SYSLOG 或提问 +# → Actions 自劚解析 → 暡块䞊䌠验证 → 调甚 LLM API 唀醒人栌䜓 +# → 暡块闭环测试 → 写入 Notion 工单 → 发邮件给匀发者 → Discussion 回倍 +# +# 闭环流皋 +# ① 匀发者提亀 SYSLOG → Discussion 觊发 +# ② 铞枊 Agent 解析提亀内容 +# ③ 铞枊 Agent 检测暡块是吊䞊䌠到仓库 +# ④ 唀醒铞枊栞心倧脑讀知泚入暡块验证结果 +# â‘€ 栞心倧脑倄理 SYSLOG + 生成广播 +# ⑥ 铞枊 Agent 掚送 Notion 工单觊发 Notion 䟧倄理 +# ⑩ 邮件通知匀发者 +# ⑧ Discussion 回倍闭环 +# +# 䟝赖 Secrets +# LLM_API_KEY 第䞉方 LLM 平台密钥必须 +# LLM_BASE_URL 第䞉方 LLM 平台 API 地址必须劂 https://api.xxx.com/v1 +# NOTION_API_TOKEN Notion API token +# NOTION_TICKET_DB_ID 霜砚工单数据库 ID +# CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 IDv4.0 协议劚态泚入 +# PORTRAIT_DB_ID 匀发者劚态画像库数据库 IDv4.0 协议劚态泚入 +# FINGERPRINT_DB_ID 暡块指纹泚册衚数据库 IDv4.0 协议劚态泚入 +# SMTP_USER QQ 邮箱地址 +# SMTP_PASS QQ 邮箱 SMTP 授权码 + +on: + discussion: + types: [created] + +jobs: + process: + name: 📡 倄理 SYSLOG 提亀 / 广播提问 + runs-on: ubuntu-latest + if: contains(github.event.discussion.category.name, 'SYSLOG') + permissions: + contents: write + discussions: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: 🔍 Parse submission + id: parse + uses: actions/github-script@v7 + with: + script: | + const body = context.payload.discussion.body || ''; + const title = context.payload.discussion.title || ''; + + // Parse structured fields from discussion body + function extractField(text, label) { + // Match form field format: ### Label\n\nValue + const regex = new RegExp('###\\s*' + label + '\\s*\\n+([^\\n#]+)', 'i'); + const match = text.match(regex); + return match ? match[1].trim() : ''; + } + + const broadcastId = extractField(body, '广播猖号') || ''; + const submitType = extractField(body, '类型') || ''; + const email = extractField(body, '䜠的邮箱') || ''; + + // Extract content (everything after "### 内容") + const contentMatch = body.match(/###\s*内容\s*\n+([\s\S]*?)$/i); + const content = contentMatch ? contentMatch[1].trim() : ''; + + // Determine type from title or body + let type = 'question'; // default + if (submitType.includes('SYSLOG') || title.includes('SYSLOG')) { + type = 'syslog'; + } else if (submitType.includes('提问') || title.includes('提问')) { + type = 'question'; + } + + // Validate + if (!broadcastId) { + core.setFailed('❌ 猺少广播猖号请圚提亀时填写广播猖号'); + return; + } + if (!email) { + core.setFailed('❌ 猺少邮箱请圚提亀时填写䜠的邮箱'); + return; + } + if (!content) { + core.setFailed('❌ 猺少内容请粘莎䜠的 SYSLOG 或问题'); + return; + } + + core.setOutput('broadcast_id', broadcastId); + core.setOutput('type', type); + core.setOutput('email', email); + core.setOutput('content', content); + core.setOutput('discussion_number', context.payload.discussion.number); + core.setOutput('author', context.payload.discussion.user.login); + + console.log(`📡 解析完成: 广播=${broadcastId}, 类型=${type}, 邮箱=${email}`); + + - name: 🔍 铞枊 Agent · 暡块䞊䌠验证 + id: verify + env: + SYSLOG_CONTENT: ${{ steps.parse.outputs.content }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + AUTHOR: ${{ steps.parse.outputs.author }} + run: node scripts/verify-modules.js + + - name: 🧠 Auto-detect and wake up persona + id: persona + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + SUBMIT_CONTENT: ${{ steps.parse.outputs.content }} + AUTHOR: ${{ steps.parse.outputs.author }} + MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }} + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }} + PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }} + FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }} + run: node scripts/wake-persona.js + + - name: 📋 创建标准化 Notion 工单Phase B1 + id: ticket + if: ${{ secrets.NOTION_API_TOKEN }} + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + NOTION_TICKET_DB_ID: ${{ secrets.NOTION_TICKET_DB_ID }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + MODULE_VERIFY: ${{ steps.verify.outputs.verify_result }} + MODULES_UPLOADED: ${{ steps.verify.outputs.modules_uploaded }} + DEVELOPER: ${{ steps.parse.outputs.author }} + SYSLOG_RAW: ${{ steps.parse.outputs.content }} + run: node scripts/create-standardized-ticket.js + + - name: 📡 掚送广播到 GitHubPhase B4 + if: steps.persona.outcome == 'success' && steps.parse.outputs.type == 'syslog' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + DEVELOPER_NAME: ${{ steps.parse.outputs.author }} + BROADCAST_CONTENT: ${{ steps.persona.outputs.result }} + run: node scripts/push-broadcast-to-github.js + + - name: 📧 Send email to developer + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + EMAIL_TO: ${{ steps.parse.outputs.email }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + run: | + node -e " + const nodemailer = require('nodemailer'); + + const user = process.env.SMTP_USER || ''; + const pass = process.env.SMTP_PASS || ''; + const to = process.env.EMAIL_TO || ''; + const broadcastId = process.env.BROADCAST_ID || ''; + const type = process.env.SUBMIT_TYPE || 'syslog'; + const result = process.env.PERSONA_RESULT || '(倄理䞭请皍后)'; + + if (!user || !pass) { + console.log('⚠ SMTP not configured, skipping email'); + process.exit(0); + } + if (!to) { + console.log('⚠ No recipient email, skipping'); + process.exit(0); + } + + const subjectText = type === 'syslog' + ? '[光湖系统] ' + broadcastId + ' · 新广播已生成' + : '[光湖系统] ' + broadcastId + ' · 问题已解答'; + + const resultHtml = result + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + + const html = [ + '
', + '
', + '

🌊 光湖系统 · 自劚通知

', + '

HoloLake · 人栌语蚀操䜜系统

', + '
', + '
', + '

📡 ' + broadcastId + '

', + '
' + resultHtml + '
', + '
', + '
', + '↩ 回到仓库继续匀发', + '

🌀 铞枊 · 代码守技人栌䜓 · 自劚发送

', + '
', + '
' + ].join('\n'); + + const transporter = nodemailer.createTransport({ + host: 'smtp.qq.com', + port: 465, + secure: true, + auth: { user, pass } + }); + + transporter.sendMail({ + from: '\"光湖系统\" <' + user + '>', + to: to, + subject: subjectText, + html: html + }).then((info) => { + console.log('✅ 邮件已发送: ' + info.messageId); + }).catch((err) => { + console.log('⚠ 邮件发送倱莥: ' + err.message); + }); + " + + - name: 💬 Reply to Discussion + uses: actions/github-script@v7 + with: + script: | + const number = ${{ steps.parse.outputs.discussion_number }}; + const type = '${{ steps.parse.outputs.type }}'; + const broadcastId = '${{ steps.parse.outputs.broadcast_id }}'; + const email = '${{ steps.parse.outputs.email }}'; + const modulesUploaded = '${{ steps.verify.outputs.modules_uploaded }}' === 'true'; + const moduleCount = '${{ steps.verify.outputs.module_count }}' || '0'; + + const typeLabel = type === 'syslog' ? 'SYSLOG 闭环倄理' : '问题解答'; + const maskedEmail = email.length > 4 + ? email.replace(/(.{2})(.*)(@.*)/, '$1***$3') + : '***'; + const moduleStatus = moduleCount === '0' + ? 'ℹ 未检测到暡块匕甚' + : (modulesUploaded ? '✅ 党郚已䞊䌠' : '⚠ 郚分暡块未䞊䌠'); + + const body = [ + '✅ **已倄理** · ' + typeLabel, + '', + '| 项目 | 内容 |', + '|------|------|', + '| 📡 广播猖号 | `' + broadcastId + '` |', + '| 🔍 暡块验证 | ' + moduleStatus + ' |', + '| 📧 结果发送至 | `' + maskedEmail + '` |', + '| 🀖 倄理人栌䜓 | 铞枊 |', + '| ⏰ 倄理时闎 | ' + new Date().toISOString() + ' |', + '', + '> 铞枊栞心倧脑已完成 SYSLOG 验收 + 暡块检测 + 广播生成。', + '> 结果已发送到䜠的邮箱Notion 䟧工单已创建。', + '>', + '> 劂未收到邮件请检查垃土箱或重新提亀。' + ].join('\n'); + + // Use GraphQL to add discussion comment + const query = ` + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id } + } + } + `; + + try { + // Get the discussion node ID + const { repository } = await github.graphql(` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + } + } + } + `, { + owner: context.repo.owner, + repo: context.repo.repo, + number: number + }); + + await github.graphql(query, { + discussionId: repository.discussion.id, + body: body + }); + + console.log('✅ Discussion 回倍已发送'); + } catch (err) { + console.log('⚠ Discussion 回倍倱莥: ' + err.message); + } + + - name: 🔎 倱莥告譊 + if: failure() + uses: actions/github-script@v7 + with: + script: | + const number = context.payload.discussion?.number; + if (!number) return; + + try { + const { repository } = await github.graphql(` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + } + } + } + `, { + owner: context.repo.owner, + repo: context.repo.repo, + number: number + }); + + await github.graphql(` + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id } + } + } + `, { + discussionId: repository.discussion.id, + body: '❌ **倄理倱莥** · 请检查提亀栌匏是吊正确或联系管理员。\n\n> 错误诊情请查看 [Actions 日志](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions)。' + }); + } catch (err) { + console.log('⚠ 倱莥告譊回倍倱莥: ' + err.message); + } diff --git a/.github/workflows/syslog-issue-pipeline.yml b/.github/workflows/syslog-issue-pipeline.yml new file mode 100644 index 00000000..27ac9cfc --- /dev/null +++ b/.github/workflows/syslog-issue-pipeline.yml @@ -0,0 +1,302 @@ +name: SYSLOG Issue Pipeline +# 📡 SYSLOG 自助提亀系统 · Issue 版党自劚闭环 +# +# 匀发者圚 GitHub Issue 提亀 SYSLOG 或提问䜿甚 syslog-submit 暡板 +# → Actions 自劚解析 → 暡块䞊䌠验证 → 调甚 LLM API 唀醒人栌䜓 +# → 暡块闭环测试 → 写入 Notion 工单 → 发邮件给匀发者 → Issue 回倍 +# +# 闭环流皋 +# ① 匀发者提亀 SYSLOG → Issue 觊发 +# ② 铞枊 Agent 解析提亀内容 +# ③ 铞枊 Agent 检测暡块是吊䞊䌠到仓库 +# ④ 唀醒铞枊栞心倧脑讀知泚入暡块验证结果 +# â‘€ 栞心倧脑倄理 SYSLOG + 生成广播 +# ⑥ 铞枊 Agent 掚送 Notion 工单觊发 Notion 䟧倄理 +# ⑩ 邮件通知匀发者 +# ⑧ Issue 回倍闭环 +# +# 䟝赖 Secrets +# LLM_API_KEY 第䞉方 LLM 平台密钥必须 +# LLM_BASE_URL 第䞉方 LLM 平台 API 地址必须劂 https://api.xxx.com/v1 +# NOTION_API_TOKEN Notion API token +# NOTION_TICKET_DB_ID 霜砚工单数据库 ID +# CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 IDv4.0 协议劚态泚入 +# PORTRAIT_DB_ID 匀发者劚态画像库数据库 IDv4.0 协议劚态泚入 +# FINGERPRINT_DB_ID 暡块指纹泚册衚数据库 IDv4.0 协议劚态泚入 +# SMTP_USER QQ 邮箱地址 +# SMTP_PASS QQ 邮箱 SMTP 授权码 + +on: + issues: + types: [opened, labeled] + +jobs: + process: + name: 📡 倄理 SYSLOG 提亀 / 广播提问 (Issue) + runs-on: ubuntu-latest + if: contains(toJSON(github.event.issue.labels), 'syslog') + permissions: + contents: write + issues: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: 🔍 Parse submission + id: parse + uses: actions/github-script@v7 + with: + script: | + const body = context.payload.issue.body || ''; + const title = context.payload.issue.title || ''; + + // Parse structured fields from issue body + function extractField(text, label) { + // Match form field format: ### Label\n\nValue + const regex = new RegExp('###\\s*' + label + '\\s*\\n+([^\\n#]+)', 'i'); + const match = text.match(regex); + return match ? match[1].trim() : ''; + } + + const broadcastId = extractField(body, '广播猖号') || ''; + const submitType = extractField(body, '类型') || ''; + const email = extractField(body, '䜠的邮箱') || ''; + + // Extract content (everything after "### 内容") + const contentMatch = body.match(/###\s*内容\s*\n+([\s\S]*?)$/i); + const content = contentMatch ? contentMatch[1].trim() : ''; + + // Determine type from title or body + let type = 'question'; // default + if (submitType.includes('SYSLOG') || title.includes('SYSLOG')) { + type = 'syslog'; + } else if (submitType.includes('提问') || title.includes('提问')) { + type = 'question'; + } + + // Validate + if (!broadcastId) { + core.setFailed('❌ 猺少广播猖号请圚提亀时填写广播猖号'); + return; + } + if (!email) { + core.setFailed('❌ 猺少邮箱请圚提亀时填写䜠的邮箱'); + return; + } + if (!content) { + core.setFailed('❌ 猺少内容请粘莎䜠的 SYSLOG 或问题'); + return; + } + + core.setOutput('broadcast_id', broadcastId); + core.setOutput('type', type); + core.setOutput('email', email); + core.setOutput('content', content); + core.setOutput('issue_number', context.payload.issue.number); + core.setOutput('author', context.payload.issue.user.login); + + console.log(`📡 解析完成: 广播=${broadcastId}, 类型=${type}, 邮箱=${email}`); + + - name: 🔍 铞枊 Agent · 暡块䞊䌠验证 + id: verify + env: + SYSLOG_CONTENT: ${{ steps.parse.outputs.content }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + AUTHOR: ${{ steps.parse.outputs.author }} + run: node scripts/verify-modules.js + + - name: 🧠 Auto-detect and wake up persona + id: persona + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + SUBMIT_CONTENT: ${{ steps.parse.outputs.content }} + AUTHOR: ${{ steps.parse.outputs.author }} + MODULE_VERIFY_RESULT: ${{ steps.verify.outputs.verify_report }} + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }} + PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }} + FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }} + run: node scripts/wake-persona.js + + - name: 📋 创建标准化 Notion 工单Phase B1 + id: ticket + if: ${{ secrets.NOTION_API_TOKEN }} + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + NOTION_TICKET_DB_ID: ${{ secrets.NOTION_TICKET_DB_ID }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + MODULE_VERIFY: ${{ steps.verify.outputs.verify_result }} + MODULES_UPLOADED: ${{ steps.verify.outputs.modules_uploaded }} + DEVELOPER: ${{ steps.parse.outputs.author }} + SYSLOG_RAW: ${{ steps.parse.outputs.content }} + run: node scripts/create-standardized-ticket.js + + - name: 📡 掚送广播到 GitHubPhase B4 + if: steps.persona.outcome == 'success' && steps.parse.outputs.type == 'syslog' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + DEVELOPER_NAME: ${{ steps.parse.outputs.author }} + BROADCAST_CONTENT: ${{ steps.persona.outputs.result }} + run: node scripts/push-broadcast-to-github.js + + - name: 📧 Send email to developer + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + EMAIL_TO: ${{ steps.parse.outputs.email }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + run: | + node -e " + const nodemailer = require('nodemailer'); + + const user = process.env.SMTP_USER || ''; + const pass = process.env.SMTP_PASS || ''; + const to = process.env.EMAIL_TO || ''; + const broadcastId = process.env.BROADCAST_ID || ''; + const type = process.env.SUBMIT_TYPE || 'syslog'; + const result = process.env.PERSONA_RESULT || '(倄理䞭请皍后)'; + + if (!user || !pass) { + console.log('⚠ SMTP not configured, skipping email'); + process.exit(0); + } + if (!to) { + console.log('⚠ No recipient email, skipping'); + process.exit(0); + } + + const subjectText = type === 'syslog' + ? '[光湖系统] ' + broadcastId + ' · 新广播已生成' + : '[光湖系统] ' + broadcastId + ' · 问题已解答'; + + const resultHtml = result + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + + const html = [ + '
', + '
', + '

🌊 光湖系统 · 自劚通知

', + '

HoloLake · 人栌语蚀操䜜系统

', + '
', + '
', + '

📡 ' + broadcastId + '

', + '
' + resultHtml + '
', + '
', + '
', + '↩ 回到仓库继续匀发', + '

🌀 铞枊 · 代码守技人栌䜓 · 自劚发送

', + '
', + '
' + ].join('\n'); + + const transporter = nodemailer.createTransport({ + host: 'smtp.qq.com', + port: 465, + secure: true, + auth: { user, pass } + }); + + transporter.sendMail({ + from: '\"光湖系统\" <' + user + '>', + to: to, + subject: subjectText, + html: html + }).then((info) => { + console.log('✅ 邮件已发送: ' + info.messageId); + }).catch((err) => { + console.log('⚠ 邮件发送倱莥: ' + err.message); + }); + " + + - name: 💬 Reply to Issue + uses: actions/github-script@v7 + with: + script: | + const number = context.payload.issue.number; + const type = '${{ steps.parse.outputs.type }}'; + const broadcastId = '${{ steps.parse.outputs.broadcast_id }}'; + const email = '${{ steps.parse.outputs.email }}'; + const modulesUploaded = '${{ steps.verify.outputs.modules_uploaded }}' === 'true'; + const moduleCount = '${{ steps.verify.outputs.module_count }}' || '0'; + + const typeLabel = type === 'syslog' ? 'SYSLOG 闭环倄理' : '问题解答'; + const maskedEmail = email.length > 4 + ? email.replace(/(.{2})(.*)(@.*)/, '$1***$3') + : '***'; + const moduleStatus = moduleCount === '0' + ? 'ℹ 未检测到暡块匕甚' + : (modulesUploaded ? '✅ 党郚已䞊䌠' : '⚠ 郚分暡块未䞊䌠'); + + const body = [ + '✅ **已倄理** · ' + typeLabel, + '', + '| 项目 | 内容 |', + '|------|------|', + '| 📡 广播猖号 | `' + broadcastId + '` |', + '| 🔍 暡块验证 | ' + moduleStatus + ' |', + '| 📧 结果发送至 | `' + maskedEmail + '` |', + '| 🀖 倄理人栌䜓 | 铞枊 |', + '| ⏰ 倄理时闎 | ' + new Date().toISOString() + ' |', + '', + '> 铞枊栞心倧脑已完成 SYSLOG 验收 + 暡块检测 + 广播生成。', + '> 结果已发送到䜠的邮箱Notion 䟧工单已创建。', + '>', + '> 劂未收到邮件请检查垃土箱或重新提亀。' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: number, + body: body + }); + + // Close the issue after processing + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: number, + state: 'closed', + labels: ['syslog', 'processed'] + }); + + console.log('✅ Issue 回倍已发送并已关闭'); + + - name: 🔎 倱莥告譊 + if: failure() + uses: actions/github-script@v7 + with: + script: | + const number = context.payload.issue?.number; + if (!number) return; + + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: number, + body: '❌ **倄理倱莥** · 请检查提亀栌匏是吊正确或联系管理员。\n\n> 错误诊情请查看 [Actions 日志](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions)。' + }); + } catch (err) { + console.log('⚠ 倱莥告譊回倍倱莥: ' + err.message); + } diff --git a/.gitignore b/.gitignore index 28f589d3..a0e218d9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ persona-brain-db/brain.db-shm # persona-studio runtime artifacts persona-studio/backend/brain/model-benchmark.json +persona-studio/workspace/*/project-* # palace-game runtime artifacts modules/palace-game/data/saves/PAL-* diff --git a/README.md b/README.md index ebf954fe..6e1b368c 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,21 @@ --- +## 🚀 匀发者入口 + +
+ +[![提亀系统日志](https://img.shields.io/badge/📡_提亀系统日志-点这里-blue?style=for-the-badge)](https://github.com/qinfendebingshuo/guanghulab/issues/new?template=syslog-submit.yml) +[![遇到问题](https://img.shields.io/badge/❓_遇到问题-点这里提问-green?style=for-the-badge)](https://github.com/qinfendebingshuo/guanghulab/issues/new?template=dev-question.yml) + +> 做完了点蓝色按钮把日志粘莎进去填䞊邮箱点提亀。新广播䌚发到䜠邮箱里。 +> +> 有问题点绿色按钮填写问题描述铞枊䌚自劚回答䜠。 + +
+ +--- + ## 📖 系统简介 **光湖HoloLake** 是䞀䞪基于人栌语蚀操䜜系统的智胜协䜜平台采甚壳-栞分犻讟计 @@ -77,29 +92,31 @@ | æ—¶é—Ž | 检查项 | 状态 | |------|--------|------| -| 03-13 09:58 | ✅ 🚀 铞枊 CD · 自劚郚眲到 guanghulab.com · 成功 | zhizhi200271 | -| 03-13 09:57 | ✅ 📢 曎新系统公告区 · 成功 | zhizhi200271 | -| 03-13 09:56 | ⏹ 📢 曎新系统公告区 · cancelled | zhizhi200271 | -| 03-13 09:56 | 🔧 系统曎新: `.github/` | 铞枊 (ZhùYuān) | -| 03-13 09:56 | ✅ 铞枊 · Bridge E · GitHub Changes → Notion · 成功 | zhizhi200271 | -| 03-13 09:56 | 🔧 系统曎新: `docs/` | 铞枊 (ZhùYuān) | -| 03-13 08:57 | ✅ 📢 曎新系统公告区 · 成功 | 冰朔 | -| 03-13 08:56 | ✅ 铞枊 · 每日自检 · 成功 | 冰朔 | -| 03-13 08:01 | ✅ Generate Session Summary for Notion · 成功 | 冰朔 | -| 03-13 07:59 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | -| 03-13 07:34 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | -| 03-13 06:46 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | -| 03-13 06:01 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | -| 03-13 05:40 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | -| 03-13 04:53 | ✅ 铞枊 · Notion 工单蜮询 · 成功 | 冰朔 | +| 03-13 14:53 | ✅ 🚀 铞枊 CD · 自劚郚眲到 guanghulab.com · 成功 | 冰朔 | +| 03-13 14:52 | ❌ 📢 曎新系统公告区 · 倱莥 | 冰朔 | +| 03-13 14:52 | 🔧 系统曎新: `.github/` | 铞枊 (ZhùYuān) | +| 03-13 14:52 | ✅ 铞枊 · Bridge E · GitHub Changes → Notion · 成功 | 冰朔 | +| 03-13 14:52 | ⏹ 📢 曎新系统公告区 · cancelled | WENZHUOXI | +| 03-13 14:51 | ✅ 🚀 铞枊 CD · 自劚郚眲到 guanghulab.com · 成功 | WENZHUOXI | +| 03-13 14:50 | ✅ 📢 曎新系统公告区 · 成功 | WENZHUOXI | +| 03-13 14:50 | ✅ 铞枊 · Bridge E · GitHub Changes → Notion · 成功 | WENZHUOXI | +| 03-13 14:37 | 🔧 系统曎新: `scripts/` | 铞枊 (Copilot) | +| 03-13 08:00 | ✅ 铞枊 PSP 巡检通过 · 党郚检查项 ✅ | 铞枊PSP巡检 | +| 03-12 16:55 | ✅ 每日巡检 ✅ 通过 | 冰朔 | +| 03-12 08:00 | ✅ 铞枊 PSP 巡检通过 · 党郚检查项 ✅ | 铞枊PSP巡检 | +| 03-11 16:55 | ✅ 每日巡检 ✅ 通过 | 冰朔 | +| 03-10 16:56 | ✅ 每日巡检 ✅ 通过 | 冰朔 | +| 03-10 08:00 | 🧠 栞心倧脑升级 v3.0 · 壳-栞分犻架构 · 前端壳(UI/IO) + 后端脑(prompt/routing/memory/context) · BRAIN域4接口䞊线 · 芆盖率 7/21 (33.3%) | 铞枊冰朔指什 | ### 🀖 铞枊自劚提醒 -> 🟢 **今日无需冰朔手劚干预** · 系统䞀切正垞 +> 🔎 **需芁冰朔手劚干预** > -> 🗓 2026-03-13 · 铞枊自劚检测 +> - ❌ 📢 曎新系统公告区 · 倱莥 +> +> 🗓 2026-03-13 · 铞枊已发送邮件提醒 --- @@ -113,8 +130,9 @@ | æ—¶é—Ž | 合䜜者 | 暡块 | 状态 | |------|--------|------|------| -| 03-13 09:56 | zhizhi200271 | `—/` | ✅ 䞊䌠成功 | -| 03-13 09:56 | 🀖 之之 | `dingtalk-bot/` | 📊 䞊䌠成功 | +| 03-13 14:52 | 冰朔 | `—/` | ✅ 䞊䌠成功 | +| 03-13 14:52 | Copilot | `—/` | ✅ 䞊䌠成功 | +| 03-13 14:50 | WENZHUOXI | `—/` | ✅ 䞊䌠成功 | ### 🀖 铞枊自劚提醒 · 合䜜者 diff --git a/app.js b/app.js new file mode 100644 index 00000000..e69de29b diff --git a/broadcasts/README.md b/broadcasts/README.md new file mode 100644 index 00000000..a1d6d7bf --- /dev/null +++ b/broadcasts/README.md @@ -0,0 +1,22 @@ +# 📡 broadcasts/ + +自劚广播園档目圕。 + +铞枊 Agent 倄理完 SYSLOG 后䌚自劚将新广播文件掚送到歀目圕 + +``` +broadcasts/ + DEV-001/ + BC-M14-001-YY.md + DEV-012/ + BC-M22-009-AW.md + BC-M23-001-AW.md + ... +``` + +**掚送规范** +- 路埄`broadcasts/{developer_id}/{taskId}.md` +- 提亀信息`[AutoBroadcast] {taskId} · {匀发者名} · {暡块名}环节{N}` +- 掚送方匏通过 GitHub API`PUT /repos/.../contents/...` + +**䞍芁手劚猖蟑歀目圕**由铞枊 Agent 自劚管理。 diff --git a/homepage/app.js b/homepage/app.js new file mode 100644 index 00000000..f8c1138b --- /dev/null +++ b/homepage/app.js @@ -0,0 +1,203 @@ +/** + * app.js - 光湖銖页富航䞭心 + * M23 环节1 · 公告蜮播 + 卡片亀互 + 状态劚态化 + */ + +// 公告蜮播数据Mock数据未来对接M22真实API +const announcements = [ + { + id: 1, + title: "光湖实验宀v1.0匀发䞭", + content: "党团队并行掚进倚暡块同步建讟", + date: "2026-03-13", + type: "update" + }, + { + id: 2, + title: "暡块进床总览", + content: "4䞪暡块圚线 · 2䞪建讟䞭", + date: "2026-03-13", + type: "status" + }, + { + id: 3, + title: "欢迎新匀发者", + content: "零点原栞频道持续壮倧", + date: "2026-03-13", + type: "welcome" + } +]; + +// 暡块卡片数据 +const moduleCards = [ + { + id: "M09", + name: "消息通知䞭心", + icon: "🔔", + status: "online", + path: "../notification-center/" + }, + { + id: "M22", + name: "䞻域公告栏", + icon: "📢", + status: "online", + path: "../announcement/" + }, + { + id: "M06", + name: "工单管理", + icon: "🎫", + status: "online", + path: "../ticket-system/" + }, + { + id: "M16", + name: "码字工䜜台", + icon: "✍", + status: "building", + path: "../writing-workspace/" + }, + { + id: "M11", + name: "风栌组件库", + icon: "🎚", + status: "online", + path: "../ui-components/" + }, + { + id: "M05", + name: "甚户䞭心", + icon: "👀", + status: "building", + path: "../user-center/" + } +]; + +// 銖页应甚对象 +window.HomepageApp = { + currentAnnouncementIndex: 0, + carouselInterval: null, + + init() { + this.renderCards(); + this.renderCarousel(); + this.startCarousel(); + this.bindEvents(); + this.updateStatusBar(); + console.log('✅ 銖页初始化完成 · 15连胜版'); + }, + + // 枲染卡片 + renderCards() { + const grid = document.getElementById('moduleGrid'); + if (!grid) return; + + const cardsHTML = moduleCards.map(card => ` +
+
+
${card.icon}
+ ${card.name} + ${card.status === 'online' ? '圚线' : '建讟䞭'} +
+ 进入暡块 → +
+ `).join(''); + + grid.innerHTML = cardsHTML; + }, + + // 枲染蜮播 + renderCarousel() { + const carousel = document.getElementById('announcementCarousel'); + if (!carousel) return; + + const announcement = announcements[this.currentAnnouncementIndex]; + carousel.innerHTML = ` + + `; + }, + + // 启劚蜮播自劚4秒切换 + startCarousel() { + if (this.carouselInterval) clearInterval(this.carouselInterval); + this.carouselInterval = setInterval(() => { + this.nextAnnouncement(); + }, 4000); + console.log('🔄 蜮播已启劚4秒自劚切换'); + }, + + // 䞋䞀条公告 + nextAnnouncement() { + this.currentAnnouncementIndex = (this.currentAnnouncementIndex + 1) % announcements.length; + this.renderCarousel(); + }, + + // 䞊䞀条公告 + prevAnnouncement() { + this.currentAnnouncementIndex = (this.currentAnnouncementIndex - 1 + announcements.length) % announcements.length; + this.renderCarousel(); + }, + + // 暡块跳蜬 + navigateTo(moduleId) { + const module = moduleCards.find(m => m.id === moduleId); + if (module) { + console.log(`🧭 跳蜬到暡块: ${module.name} | 路埄: ${module.path}`); + } else { + console.log(`⚠ 未扟到暡块: ${moduleId}`); + } + }, + + // 绑定事件 + bindEvents() { + // 卡片点击事件 + document.querySelectorAll('.card').forEach(card => { + card.addEventListener('click', (e) => { + const moduleId = card.dataset.moduleId; + const modulePath = card.dataset.modulePath; + console.log(`🖱 点击卡片: ${moduleId} | 路埄: ${modulePath}`); + this.navigateTo(moduleId); + // 未来这里䌚实现实际跳蜬window.location.href = modulePath; + }); + }); + + // 蜮播箭倎点击 + document.getElementById('carouselPrev')?.addEventListener('click', () => { + console.log('◀ 点击巊箭倎'); + this.prevAnnouncement(); + }); + + document.getElementById('carouselNext')?.addEventListener('click', () => { + console.log('▶ 点击右箭倎'); + this.nextAnnouncement(); + }); + + // 键盘事件 + document.addEventListener('keydown', (e) => { + if (e.key === 'ArrowLeft') { + console.log('⌚ 键盘巊箭倎'); + this.prevAnnouncement(); + } else if (e.key === 'ArrowRight') { + console.log('⌚ 键盘右箭倎'); + this.nextAnnouncement(); + } + }); + }, + + // 曎新状态栏 + updateStatusBar() { + const onlineCount = moduleCards.filter(c => c.status === 'online').length; + const totalCount = moduleCards.length; + console.log(`📊 系统状态: ${onlineCount}/${totalCount} 暡块圚线`); + } +}; + +// 页面加蜜时自劚初始化 +document.addEventListener('DOMContentLoaded', () => { + window.HomepageApp.init(); +}); \ No newline at end of file diff --git a/homepage/develop.md b/homepage/develop.md new file mode 100644 index 00000000..b29eb166 --- /dev/null +++ b/homepage/develop.md @@ -0,0 +1,21 @@ +# M23 光湖銖页䞎富航䞭心 · 匀发日志 + +## 环节0 · 项目初始化䞎基础垃局 +- 完成时闎2026-03-13 +- 匀发者DEV-012 Awen +- 匕富人栌知秋 + +### 完成内容 +- [x] 创建 homepage 目圕及 index.html/style.css/app.js +- [x] Hero区标题、简介、进入系统按钮 +- [x] 暡块眑栌6䞪卡片垊囟标、名称、状态标筟 +- [x] 响应匏垃局桌面3列/平板2列/手机1列 +- [x] 卡片淡入劚画 +- [x] 状态栏 + Footer + +### 圓前系统状态 +- 圚线暡块4䞪 +- 建讟䞭暡块2䞪 + +### 倇泚 +✅ 环节0 验收通过准倇进入环节1 \ No newline at end of file diff --git a/homepage/index.html b/homepage/index.html new file mode 100644 index 00000000..90dcded3 --- /dev/null +++ b/homepage/index.html @@ -0,0 +1,48 @@ + + + + + + 光湖实验宀 · HoloLake 銖页 + + + +
+ +
+

光湖实验宀HoloLake Lab

+

探玢未来之域 · 零点原栞频道

+ 进入系统 +
+ + + +
+

栞心暡块

+
+ +
+
+ + +
+ 系统状态 + 圚线 +
+ + + +
+ + + + \ No newline at end of file diff --git a/homepage/style.css b/homepage/style.css new file mode 100644 index 00000000..3668c249 --- /dev/null +++ b/homepage/style.css @@ -0,0 +1,437 @@ +/* ===== 党局变量 ===== */ +:root { + --bg-primary: #0a0c14; + --bg-secondary: #141822; + --bg-card: #1e2430; + --text-primary: #ffffff; + --text-secondary: #a0a8b8; + --accent: #3b82f6; + --accent-hover: #60a5fa; + --online: #10b981; + --building: #f59e0b; + --border: #2a3142; +} + +/* ===== 基础样匏 ===== */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; +} + +.app { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; +} + +/* ===== Hero 区 ===== */ +.hero { + text-align: center; + padding: 4rem 1rem; + background: linear-gradient(135deg, var(--bg-secondary), var(--bg-primary)); + border-radius: 2rem; + margin-bottom: 3rem; + border: 1px solid var(--border); + animation: fadeIn 1s ease; +} + +.hero-title { + font-size: 3rem; + font-weight: 700; + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #fff, #94a3b8); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.hero-subtitle { + display: block; + font-size: 1.2rem; + color: var(--text-secondary); + margin-top: 0.5rem; +} + +.hero-description { + font-size: 1.2rem; + color: var(--text-secondary); + margin-bottom: 2rem; +} + +.hero-btn { + display: inline-block; + padding: 0.8rem 2rem; + background: var(--accent); + color: white; + text-decoration: none; + border-radius: 2rem; + font-weight: 500; + transition: all 0.3s ease; +} + +.hero-btn:hover { + background: var(--accent-hover); + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(59, 130, 246, 0.3); +} + +/* ===== 暡块眑栌 ===== */ +.section-title { + font-size: 1.8rem; + margin-bottom: 2rem; + color: var(--text-primary); +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1.5rem; + margin-bottom: 3rem; +} + +/* 卡片样匏 */ +.card { + background: var(--bg-card); + border-radius: 1.5rem; + padding: 1.5rem; + border: 1px solid var(--border); + transition: all 0.3s ease; + animation: cardFadeIn 0.5s ease forwards; + opacity: 0; + cursor: pointer; +} + +.card:hover { + transform: translateY(-4px); + border-color: var(--accent); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.card-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; +} + +.card-icon { + width: 40px; + height: 40px; + background: var(--bg-secondary); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; +} + +.card-name { + font-size: 1.2rem; + font-weight: 600; + flex: 1; +} + +.card-status { + font-size: 0.8rem; + padding: 0.2rem 0.8rem; + border-radius: 1rem; + background: var(--bg-secondary); +} + +.card-status.online { + background: rgba(16, 185, 129, 0.2); + color: var(--online); +} + +.card-status.building { + background: rgba(245, 158, 11, 0.2); + color: var(--building); +} + +.card-link { + display: inline-block; + margin-top: 1rem; + color: var(--accent); + text-decoration: none; + font-size: 0.9rem; +} + +.card-link:hover { + text-decoration: underline; +} + +/* 卡片劚画延迟 */ +.card:nth-child(1) { animation-delay: 0.1s; } +.card:nth-child(2) { animation-delay: 0.2s; } +.card:nth-child(3) { animation-delay: 0.3s; } +.card:nth-child(4) { animation-delay: 0.4s; } +.card:nth-child(5) { animation-delay: 0.5s; } +.card:nth-child(6) { animation-delay: 0.6s; } + +@keyframes cardFadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ===== 状态栏 ===== */ +.status-bar { + background: var(--bg-secondary); + padding: 1rem; + border-radius: 2rem; + margin-bottom: 2rem; + display: flex; + align-items: center; + gap: 1rem; + border: 1px solid var(--border); +} + +.status-dot { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; +} + +.status-dot.online { + background: var(--online); + box-shadow: 0 0 10px var(--online); +} + +/* ===== Footer ===== */ +.footer { + text-align: center; + padding: 2rem; + color: var(--text-secondary); + border-top: 1px solid var(--border); +} + +.footer p { + margin: 0.5rem 0; +} + +/* ===== 响应匏讟计 ===== */ +/* 桌面默讀已经是3列 (min-width: 1024px) */ + +/* 平板 (768px - 1023px) 2列 */ +@media (max-width: 1023px) { + .grid { + grid-template-columns: repeat(2, 1fr); + } + + .hero-title { + font-size: 2.5rem; + } +} + +/* 手机 (小于 768px) 1列 */ +@media (max-width: 767px) { + .grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2rem; + } + + .app { + padding: 1rem; + } +} + +/* ===== 党局淡入劚画 ===== */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* ===== 公告蜮播区样匏 ===== */ +.carousel-container { + position: relative; + background: var(--bg-secondary); + border-radius: 2rem; + padding: 2rem; + margin: 2rem 0 3rem; + border: 1px solid var(--border); + animation: fadeIn 0.5s ease; +} + +.carousel { + min-height: 100px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.carousel-item { + width: 100%; +} + +.carousel-title { + font-size: 1.3rem; + color: var(--accent); + margin-bottom: 0.5rem; +} + +.carousel-content { + color: var(--text-secondary); + margin-bottom: 0.5rem; + line-height: 1.6; +} + +.carousel-date { + font-size: 0.8rem; + color: var(--text-tertiary); +} + +.carousel-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--bg-card); + border: 1px solid var(--border); + color: var(--text-primary); + font-size: 1.2rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.carousel-arrow:hover { + background: var(--accent); + color: white; + border-color: var(--accent); +} + +.carousel-arrow.left { + left: -20px; +} + +.carousel-arrow.right { + right: -20px; +} + +/* ===== 卡片悬停效果增区 ===== */ +.card { + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.card:hover { + transform: translateY(-8px) scale(1.02); + border-color: var(--accent); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); +} + +/* ===== 状态标筟劚态化 ===== */ +.card-status.online { + animation: pulse 2s infinite; +} + +.card-status.building { + animation: flicker 1.5s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); + } + 70% { + box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); + } +} + +@keyframes flicker { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.6; + box-shadow: 0 0 10px rgba(245, 158, 11, 0.5); + } +} + +/* ===== 响应匏调敎 ===== */ +@media (max-width: 767px) { + .carousel-container { + padding: 1.5rem; + } + + .carousel-arrow.left { + left: -10px; + } + + .carousel-arrow.right { + right: -10px; + } +} + +/* ===== 暎力增区版确保生效===== */ +.card { + cursor: pointer !important; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; +} + +.card:hover { + transform: translateY(-10px) scale(1.05) !important; + border-color: var(--accent) !important; + box-shadow: 0 30px 50px rgba(59, 130, 246, 0.5) !important; +} + +.card-status.online { + animation: pulse 1.5s infinite !important; +} + +.card-status.building { + animation: flicker 1s infinite !important; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); + } + 50% { + box-shadow: 0 0 20px 10px rgba(16, 185, 129, 0.3); + } + 100% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); + } +} + +@keyframes flicker { + 0%, 100% { + opacity: 1; + text-shadow: 0 0 5px rgba(245, 158, 11, 0.5); + } + 50% { + opacity: 0.5; + text-shadow: 0 0 15px rgba(245, 158, 11, 0.8); + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..e69de29b diff --git a/persona-studio/backend/brain/agent-workflow.js b/persona-studio/backend/brain/agent-workflow.js new file mode 100644 index 00000000..aeacc4b4 --- /dev/null +++ b/persona-studio/backend/brain/agent-workflow.js @@ -0,0 +1,761 @@ +/** + * persona-studio · 铞枊代理匀发工䜜流匕擎壳-栞分犻架构 + * + * 架构讟计 + * æ ž = 铞枊栞心倧脑Core Brain + * ↳ 加蜜匀发者记忆、画像、项目历史、知识库、暡匏库 + * ↳ 深床分析匀发者真实意囟 + * ↳ 生成结构化匀发计划 + * 壳 = 铞枊代理Agent Executor + * ↳ 接收栞心倧脑掚送的匀发计划 + * ↳ 逐步执行代码生成 + * ↳ 广播进床给前端 + * ↳ 完成后回䌠栞心倧脑做自进化 + * + * 流皋 + * 甚户点击「我芁匀发」 + * → Agent 唀醒栞心倧脑 + * → 栞心倧脑加蜜党郚䞊䞋文 → 分析意囟 → 生成匀发计划 + * → Agent接收计划 → 逐步执行 → 广播进床 + * → 栞心倧脑知识提取 + 暡匏曎新 + 画像孊习 + 进化日志 + */ +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const http = require('http'); + +// ── 垞量 ── +// 䌠入 LLM 的对话文本最倧字笊数防止超出暡型䞊䞋文窗口 +const MAX_CONVERSATION_CHARS = 4000; + +// ── 栞心倧脑暡块 ── +const memoryManager = require('./memory-manager'); +const personaEngine = require('./persona-engine'); +const memoryInjector = require('./memory-injector'); +const knowledgeExtractor = require('./knowledge-extractor'); +const patternAnalyzer = require('./pattern-analyzer'); +const profileLearner = require('./profile-learner'); +const evolutionLogger = require('./evolution-logger'); + +const WORKSPACE_DIR = path.join(__dirname, '..', '..', 'workspace'); +const REGISTRY_PATH = path.join(__dirname, '..', '..', 'brain', 'registry.json'); +const HUMAN_REGISTRY_PATH = path.join(__dirname, '..', '..', 'brain', 'human-registry.json'); + +/* ================================================================ + 工具凜数 + ================================================================ */ + +/** + * 调甚甚户提䟛的 LLM APIOpenAI 兌容栌匏 + */ +function callLLM({ apiBase, apiKey, model, messages, maxTokens, temperature, timeoutMs }) { + return new Promise((resolve, reject) => { + const base = apiBase.replace(/\/+$/, ''); + const chatUrl = new URL(base + '/chat/completions'); + const isHttps = chatUrl.protocol === 'https:'; + const mod = isHttps ? https : http; + + const body = JSON.stringify({ + model, + messages, + max_tokens: maxTokens || 4000, + temperature: temperature != null ? temperature : 0.3 + }); + + const options = { + hostname: chatUrl.hostname, + port: chatUrl.port || (isHttps ? 443 : 80), + path: chatUrl.pathname, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + apiKey, + 'Content-Length': Buffer.byteLength(body) + }, + timeout: timeoutMs || 60000 + }; + + const req = mod.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + try { + const json = JSON.parse(data); + if (json.choices && json.choices[0] && json.choices[0].message) { + resolve(json.choices[0].message.content); + } else if (json.error) { + reject(new Error(json.error.message || 'API error')); + } else { + reject(new Error('Unexpected API response')); + } + } catch (_e) { + reject(new Error('Failed to parse API response')); + } + }); + }); + + req.on('error', reject); + req.on('timeout', () => { + req.destroy(); + reject(new Error('Request timeout')); + }); + + req.write(body); + req.end(); + }); +} + +/** + * 从泚册衚查扟匀发者信息 + */ +function lookupDeveloper(devId) { + if (!devId || devId === 'GUEST') { + return { dev_id: 'GUEST', name: '访客', role: 'guest' }; + } + try { + var reg = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8')); + if (reg.developers && reg.developers[devId]) { + return Object.assign({ dev_id: devId }, reg.developers[devId]); + } + } catch (_e) { /* ignore */ } + try { + var hReg = JSON.parse(fs.readFileSync(HUMAN_REGISTRY_PATH, 'utf-8')); + if (hReg.developers) { + var found = hReg.developers.find(function (d) { return d.exp_id === devId; }); + if (found) return { dev_id: devId, name: found.name, role: found.role }; + } + } catch (_e) { /* ignore */ } + return { dev_id: devId, name: devId, role: 'developer' }; +} + +/* ================================================================ + Phase 1: 唀醒铞枊栞心倧脑Core Brain Wake + ================================================================ */ + +/** + * 唀醒栞心倧脑加蜜党郚䞊䞋文分析匀发者意囟生成匀发计划。 + * + * 栞心倧脑聚合以䞋信息源 + * ① 匀发者身仜来自 registry + * ② 对话记忆来自 memory-manager + * ③ 甚户画像来自 profile-learner技术氎平、沟通风栌、讟计偏奜 + * ④ 项目历史来自 memory-manager + * â‘€ 知识库来自 knowledge-extractor + * ⑥ 暡匏库来自 pattern-analyzer + * ⑩ 记忆摘芁来自 memory-injector + */ +async function wakeCoreBrain(devId, conversation, apiBase, apiKey, model) { + var devInfo = lookupDeveloper(devId); + var isGuest = devId === 'GUEST'; + + // ── ① 加蜜匀发者记忆 ── + var memory = { conversations: [], last_topic: null }; + if (!isGuest) { + try { memory = memoryManager.loadMemory(devId); } catch (_e) { /* new user */ } + } + + // ── ② 加蜜甚户画像 ── + var profile = null; + if (!isGuest) { + try { profile = profileLearner.loadProfile(devId); } catch (_e) { /* ignore */ } + } + + // ── ③ 加蜜项目历史 ── + var projects = { projects: [] }; + if (!isGuest) { + try { projects = memoryManager.loadProjects(devId); } catch (_e) { /* ignore */ } + } + + // ── ④ 查询知识库 ── + var conversationText = conversation + .map(function (m) { return m.content || ''; }) + .join(' '); + var relevantKnowledge = []; + try { relevantKnowledge = knowledgeExtractor.queryKnowledge(conversationText, 5); } catch (_e) { /* ignore */ } + + // ── â‘€ 查询暡匏库 ── + var relevantPatterns = []; + try { relevantPatterns = patternAnalyzer.queryPatterns(conversationText); } catch (_e) { /* ignore */ } + + // ── ⑥ 构建栞心倧脑讀知䞊䞋文 ── + var brainContext = buildBrainContext(devInfo, memory, profile, projects, relevantKnowledge, relevantPatterns); + + // ── ⑩ 䜿甚 LLM 深床分析意囟 + 生成匀发计划 ── + var developmentPlan; + if (apiBase && apiKey && model) { + developmentPlan = await analyzeIntentWithLLM(brainContext, conversation, apiBase, apiKey, model); + } else { + developmentPlan = analyzeIntentLocally(brainContext, conversation); + } + + return { brainContext: brainContext, developmentPlan: developmentPlan }; +} + +/** + * 构建栞心倧脑讀知䞊䞋文聚合党郚信息源 + */ +function buildBrainContext(devInfo, memory, profile, projects, knowledge, patterns) { + return { + developer: { + id: devInfo.dev_id, + name: devInfo.name || devInfo.dev_id, + role: devInfo.role || 'developer', + is_guest: devInfo.dev_id === 'GUEST' + }, + memory: { + total_conversations: (memory.conversations || []).length, + last_topic: memory.last_topic || null, + has_history: (memory.conversations || []).length > 0 + }, + profile: { + tech_level: (profile && profile.tech_assessment && profile.tech_assessment.level) || null, + known_skills: (profile && profile.tech_assessment && profile.tech_assessment.known_skills) || [], + communication_style: (profile && profile.communication_style && profile.communication_style.verbosity) || null, + design_preference: (profile && profile.design_preferences && profile.design_preferences.color_scheme) || null, + style_keywords: (profile && profile.design_preferences && profile.design_preferences.style_keywords) || [], + mood: (profile && profile.emotional_profile && profile.emotional_profile.current_mood) || 'neutral' + }, + projects: { + total: (projects.projects || []).length, + recent: (projects.projects || []).slice(-3).map(function (p) { + return { name: p.name, status: p.status, files: (p.files || []).length }; + }) + }, + knowledge: knowledge.map(function (k) { + return { type: k.type, title: k.title, category: k.category }; + }), + patterns: patterns.slice(0, 3).map(function (p) { + return { + name: p.name, + frequency: p.frequency, + success_rate: p.success_rate, + tech_stack: p.common_tech_stack || [] + }; + }) + }; +} + +/** + * 䜿甚 LLM 深床分析匀发者意囟并生成匀发计划 + * 栞心倧脑将党郚䞊䞋文泚入 system prompt让 LLM 理解匀发者的真正需求 + */ +async function analyzeIntentWithLLM(brainContext, conversation, apiBase, apiKey, model) { + var conversationText = conversation.map(function (m) { + var role = m.role === 'user' ? '甚户' : '铞枊'; + return role + ': ' + m.content; + }).join('\n'); + + // 构建栞心倧脑系统提瀺词泚入党郚䞊䞋文 + var systemParts = [ + '䜠是铞枊栞心倧脑的意囟分析匕擎。', + '䜠的任务是深床理解匀发者的真实需求生成粟确的匀发计划。', + '', + '## 匀发者档案', + '- 身仜' + brainContext.developer.name + '' + brainContext.developer.id + '', + '- 角色' + brainContext.developer.role + ]; + + if (brainContext.developer.is_guest) { + systemParts.push('- ⚠ 访客甚户无历史记忆'); + } else { + systemParts.push('- 历史对话' + brainContext.memory.total_conversations + ' 条'); + } + + if (brainContext.memory.last_topic) { + systemParts.push('- 䞊次话题' + brainContext.memory.last_topic); + } + if (brainContext.profile.tech_level) { + systemParts.push('- 技术氎平' + brainContext.profile.tech_level); + } + if (brainContext.profile.known_skills.length > 0) { + systemParts.push('- 已知技胜' + brainContext.profile.known_skills.join(', ')); + } + if (brainContext.profile.communication_style) { + systemParts.push('- 沟通风栌' + brainContext.profile.communication_style); + } + if (brainContext.profile.design_preference) { + systemParts.push('- 讟计偏奜' + brainContext.profile.design_preference); + } + if (brainContext.profile.style_keywords.length > 0) { + systemParts.push('- 风栌关键词' + brainContext.profile.style_keywords.join('、')); + } + + if (brainContext.projects.total > 0) { + systemParts.push(''); + systemParts.push('## 历史项目共 ' + brainContext.projects.total + ' 䞪'); + brainContext.projects.recent.forEach(function (p) { + systemParts.push('- ' + p.name + '' + p.status + '' + p.files + ' 䞪文件'); + }); + } + + if (brainContext.knowledge.length > 0) { + systemParts.push(''); + systemParts.push('## 盞关知识库'); + brainContext.knowledge.forEach(function (k) { + systemParts.push('- [' + k.type + '] ' + k.title); + }); + } + + if (brainContext.patterns.length > 0) { + systemParts.push(''); + systemParts.push('## 高频匀发暡匏'); + brainContext.patterns.forEach(function (p) { + systemParts.push('- ' + p.name + '' + p.frequency + '次成功率' + p.success_rate + '%技术栈' + p.tech_stack.join('/') + ''); + }); + } + + systemParts.push(''); + systemParts.push('## 蟓出芁求'); + systemParts.push('根据对话内容和匀发者档案蟓出䞥栌 JSON 栌匏的匀发计划'); + systemParts.push('{'); + systemParts.push(' "intent_summary": "䞀句话总结匀发者的真实意囟",'); + systemParts.push(' "project_name": "英文项目名(kebab-case)",'); + systemParts.push(' "description": "项目描述考虑匀发者的技术氎平和偏奜",'); + systemParts.push(' "design_notes": "讟计芁点基于匀发者的讟计偏奜和风栌关键词",'); + systemParts.push(' "tech_decisions": "技术决策诎明基于匀发者的技胜氎平选择合适的倍杂床",'); + systemParts.push(' "files": ['); + systemParts.push(' { "filename": "index.html", "description": "诊细文件诎明", "key_features": ["功胜1","功胜2"] }'); + systemParts.push(' ]'); + systemParts.push('}'); + systemParts.push(''); + systemParts.push('泚意'); + systemParts.push('- 只生成前端文件HTML/CSS/JS最倚5䞪文件'); + systemParts.push('- 必须包含 index.html'); + systemParts.push('- 根据匀发者技术氎平调敎代码倍杂床'); + systemParts.push('- 根据匀发者讟计偏奜决定视觉风栌'); + systemParts.push('- 劂果对话䞭提到了具䜓功胜需求务必党郚纳入计划'); + + try { + var reply = await callLLM({ + apiBase: apiBase, + apiKey: apiKey, + model: model, + messages: [ + { role: 'system', content: systemParts.join('\n') }, + { role: 'user', content: '以䞋是和匀发者的完敎对话请分析意囟并生成匀发计划\n\n' + conversationText.substring(0, MAX_CONVERSATION_CHARS) } + ], + maxTokens: 2000, + temperature: 0.2, + timeoutMs: 45000 + }); + + var jsonMatch = reply.match(/\{[\s\S]*\}/); + if (jsonMatch) { + var plan = JSON.parse(jsonMatch[0]); + if (plan.files && !plan.files.some(function (f) { return f.filename === 'index.html'; })) { + plan.files.unshift({ filename: 'index.html', description: '䞻页面', key_features: ['入口页面'] }); + } + if (plan.files) { + plan.files = plan.files.slice(0, 5); + } + plan._source = 'core_brain_llm'; + return plan; + } + } catch (_e) { + // LLM 分析倱莥降级到本地分析 + } + + return analyzeIntentLocally(brainContext, conversation); +} + +/** + * 本地意囟分析无 API 或 API 倱莥时的降级方案 + * 䜿甚规则匕擎从对话和匀发者档案䞭提取意囟 + */ +function analyzeIntentLocally(brainContext, conversation) { + var userText = conversation + .filter(function (m) { return m.role === 'user'; }) + .map(function (m) { return m.content || ''; }) + .join(' '); + + var projectType = '通甚项目'; + if (brainContext.patterns.length > 0) { + projectType = brainContext.patterns[0].name; + } + + var designStyle = '现代简纊'; + if (brainContext.profile.style_keywords.length > 0) { + designStyle = brainContext.profile.style_keywords.join(' + '); + } else if (brainContext.profile.design_preference === 'dark') { + designStyle = '深色䞻题·科技感'; + } + + var complexity = 'moderate'; + if (brainContext.profile.tech_level === 'beginner') { + complexity = 'simple'; + } else if (brainContext.profile.tech_level === 'advanced') { + complexity = 'advanced'; + } + + return { + intent_summary: userText.substring(0, 100) || '匀发䞀䞪眑页项目', + project_name: 'web-project', + description: userText.substring(0, 200) || '眑页项目', + design_notes: '风栌' + designStyle, + tech_decisions: '倍杂床' + complexity + '技术栈HTML/CSS/JavaScript', + files: [ + { filename: 'index.html', description: '䞻页面', key_features: ['响应匏垃局', '栞心功胜展瀺'] }, + { filename: 'style.css', description: '样匏文件', key_features: [designStyle] }, + { filename: 'main.js', description: '亀互逻蟑', key_features: ['甚户亀互', '劚态效果'] } + ], + _source: 'core_brain_local' + }; +} + +/* ================================================================ + Phase 2: 铞枊代理执行Agent Execute + ================================================================ */ + +/** + * Agent 接收栞心倧脑的匀发计划逐步执行代码生成 + */ +async function agentExecute(developmentPlan, devId, apiBase, apiKey, model, broadcastFn) { + var projectName = 'project-' + Date.now(); + var files = {}; + var filePlan = developmentPlan.files || []; + var hasUserApi = apiBase && apiKey && model; + + for (var i = 0; i < filePlan.length; i++) { + var fileSpec = filePlan[i]; + + broadcastFn({ + type: 'progress', + step: 3, + message: '💻 铞枊代理 · 生成文件 (' + (i + 1) + '/' + filePlan.length + '): ' + fileSpec.filename, + status: 'building', + status_text: '生成 ' + fileSpec.filename + }); + + if (hasUserApi) { + try { + var code = await generateFileWithLLM(developmentPlan, fileSpec, filePlan, apiBase, apiKey, model); + files[fileSpec.filename] = code; + } catch (_e) { + files[fileSpec.filename] = generatePlaceholder(fileSpec, developmentPlan); + } + } else { + files[fileSpec.filename] = generatePlaceholder(fileSpec, developmentPlan); + } + } + + // 写入 workspace + var projectDir = path.join(WORKSPACE_DIR, devId, projectName); + fs.mkdirSync(projectDir, { recursive: true }); + + var fileList = []; + var entries = Object.entries(files); + for (var j = 0; j < entries.length; j++) { + var safeName = path.basename(entries[j][0]); + // 安党检查防止路埄遍历和隐藏文件 + if (!safeName || safeName.startsWith('.') || /[/\\]/.test(entries[j][0])) continue; + fs.writeFileSync(path.join(projectDir, safeName), entries[j][1], 'utf-8'); + fileList.push(safeName); + } + + // 生成 README + var readmePath = path.join(projectDir, 'README.md'); + if (!fs.existsSync(readmePath)) { + fs.writeFileSync(readmePath, [ + '# ' + (developmentPlan.project_name || projectName), + '', + developmentPlan.description || '', + '', + '## 铞枊栞心倧脑分析', + '- 意囟' + (developmentPlan.intent_summary || ''), + '- 讟计' + (developmentPlan.design_notes || ''), + '- 技术' + (developmentPlan.tech_decisions || ''), + '', + '由光湖 Persona Studio · 铞枊栞心倧脑 + 铞枊代理 协同匀发', + '生成时闎' + new Date().toISOString() + ].join('\n'), 'utf-8'); + fileList.push('README.md'); + } + + return { projectName: projectName, files: fileList }; +} + +/** + * 䜿甚 LLM 生成单䞪文件Agent 执行层 + * 泚入栞心倧脑的匀发计划䞊䞋文确保代码䞎意囟䞀臎 + */ +async function generateFileWithLLM(plan, fileSpec, allFiles, apiBase, apiKey, model) { + var prompt = [ + '䜠是铞枊代理的代码生成暡块。根据栞心倧脑的匀发计划生成代码。', + '', + '## 项目信息', + '- 项目' + (plan.project_name || 'web-project'), + '- 描述' + (plan.description || ''), + '- 意囟' + (plan.intent_summary || ''), + '- 讟计芁点' + (plan.design_notes || ''), + '- 技术决策' + (plan.tech_decisions || ''), + '', + '## 圓前文件', + '- 文件名' + fileSpec.filename, + '- 诎明' + (fileSpec.description || ''), + '- 栞心功胜' + (fileSpec.key_features || []).join('、'), + '', + '## 项目党郚文件', + allFiles.map(function (f) { return '- ' + f.filename + '' + (f.description || '') + ''; }).join('\n'), + '', + '请盎接蟓出文件内容䞍芁甚 ``` 代码块包裹䞍芁加任䜕额倖诎明。', + '确保代码完敎、可运行、笊合栞心倧脑的讟计芁求。', + '劂果是 HTML 文件芁正确匕甚同项目䞭的 CSS 和 JS 文件。' + ].join('\n'); + + var reply = await callLLM({ + apiBase: apiBase, + apiKey: apiKey, + model: model, + messages: [{ role: 'user', content: prompt }], + maxTokens: 4000, + temperature: 0.3, + timeoutMs: 60000 + }); + + var clean = reply.trim(); + if (clean.startsWith('```')) { + clean = clean.replace(/^```\S*\n?/, '').replace(/\n?```\s*$/, ''); + } + return clean; +} + +/** + * 生成占䜍内容无 API 或单文件倱莥时的降级方案 + */ +function generatePlaceholder(fileSpec, plan) { + var desc = plan.description || plan.intent_summary || plan.project_name || '项目'; + var designNotes = plan.design_notes || '现代简纊'; + + if (fileSpec.filename === 'index.html') { + return [ + '', + '', + '', + ' ', + ' ', + ' ' + (plan.project_name || '项目') + '', + ' ', + '', + '', + '
', + '

🌊 ' + desc + '

', + '

由光湖 Persona Studio · 铞枊栞心倧脑 + 铞枊代理 协同匀发

', + '

讟计风栌' + designNotes + '

', + '
', + ' ', + '', + '' + ].join('\n'); + } + if (fileSpec.filename.endsWith('.css')) { + return [ + '/* Generated by Persona Studio · Core Brain + Agent */', + '* { margin: 0; padding: 0; box-sizing: border-box; }', + 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; display: flex; align-items: center; justify-content: center; }', + '.container { text-align: center; padding: 2rem; max-width: 800px; }', + 'h1 { font-size: 2rem; margin-bottom: 1rem; background: linear-gradient(135deg, #60a5fa, #22d3ee); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }', + '.design-note { color: #64748b; font-size: 0.875rem; margin-top: 1rem; }' + ].join('\n'); + } + if (fileSpec.filename.endsWith('.js')) { + return '// Generated by Persona Studio · Core Brain + Agent\nconsole.log("' + (plan.project_name || 'Project') + ' initialized by 铞枊代理");\n'; + } + return '/* Generated by Persona Studio */\n'; +} + +/* ================================================================ + Phase 3: 栞心倧脑自进化Post-Build Evolution + ================================================================ */ + +/** + * 匀发完成后栞心倧脑进行自进化 + * ① 知识提取 → 曎新 knowledge-base.json + * ② 暡匏识别 → 曎新 pattern-library.json + * ③ 画像孊习 → 曎新 profile.json + * ④ 进化日志 → 曎新 evolution-log.json + */ +function coreBrainPostEvolution(devId, conversation, result, brainContext, startTime, success) { + try { + var buildTimeMs = Date.now() - startTime; + + // ① 知识提取 + try { + knowledgeExtractor.autoExtractKnowledge(devId, conversation, result.projectName); + } catch (_e) { /* non-fatal */ } + + // ② 暡匏识别 + var patterns = []; + try { + patterns = patternAnalyzer.analyzeAndUpdatePatterns( + devId, conversation, result.files, buildTimeMs, success + ); + } catch (_e) { /* non-fatal */ } + + // ③ 画像孊习从最后䞀条甚户消息孊习 + if (devId !== 'GUEST') { + try { + var lastUser = conversation.filter(function (m) { return m.role === 'user'; }).pop(); + if (lastUser) { + profileLearner.updateProfile(devId, lastUser.content, result.summary || ''); + } + } catch (_e) { /* non-fatal */ } + } + + // ④ 进化日志 + evolutionLogger.logEvent('agent_build_complete', '铞枊代理匀发完成: ' + result.projectName, { + dev_id: devId, + project: result.projectName, + files_count: (result.files || []).length, + build_time_ms: buildTimeMs, + success: success, + brain_source: brainContext.developer.is_guest ? 'local' : 'llm', + patterns_detected: patterns, + developer_tech_level: brainContext.profile.tech_level, + developer_projects_total: brainContext.projects.total + }); + } catch (err) { + console.error('Core brain post-evolution error:', err.message); + } +} + +/* ================================================================ + 䞻工䜜流入口 + ================================================================ */ + +/** + * 运行铞枊代理匀发工䜜流壳-栞分犻 + * + * @param {object} params + * @param {string} params.dev_id - 匀发猖号EXP-XXX 或 GUEST + * @param {Array} params.conversation - 对话历史 + * @param {string} params.api_base - 甚户 API Base URL + * @param {string} params.api_key - 甚户 API Key + * @param {string} params.model - 甚户选择的暡型 + * @param {Function} params.broadcast - WebSocket 广播凜数 + * @returns {Promise<{projectName, files, summary}>} + */ +async function runWorkflow({ dev_id, conversation, api_base, api_key, model, broadcast }) { + var emitProgress = broadcast || function () {}; + var startTime = Date.now(); + var brainContext = null; + + // ════════════════════════════════════════════ + // Phase 1: 唀醒铞枊栞心倧脑 + // ════════════════════════════════════════════ + emitProgress({ + type: 'progress', + step: 1, + total_steps: 5, + message: '🧠 正圚唀醒铞枊栞心倧脑...', + status: 'building', + status_text: '唀醒栞心倧脑' + }); + + var coreBrainResult; + try { + coreBrainResult = await wakeCoreBrain(dev_id, conversation, api_base, api_key, model); + brainContext = coreBrainResult.brainContext; + } catch (_err) { + brainContext = buildBrainContext( + lookupDeveloper(dev_id), + { conversations: [], last_topic: null }, + null, { projects: [] }, [], [] + ); + coreBrainResult = { + brainContext: brainContext, + developmentPlan: analyzeIntentLocally(brainContext, conversation) + }; + } + + var plan = coreBrainResult.developmentPlan; + + emitProgress({ + type: 'progress', + step: 1, + message: '✅ 栞心倧脑已唀醒 · 意囟分析完成' + (plan.intent_summary || '').substring(0, 50), + status: 'building', + status_text: '意囟已分析' + }); + + // ════════════════════════════════════════════ + // Phase 1.5: 栞心倧脑掚送匀发计划 + // ════════════════════════════════════════════ + emitProgress({ + type: 'progress', + step: 2, + total_steps: 5, + message: '📐 栞心倧脑 → 代理掚送匀发计划' + (plan.files || []).length + ' 䞪文件', + status: 'building', + status_text: '计划已掚送' + }); + + if (plan.design_notes) { + emitProgress({ + type: 'progress', + step: 2, + message: '🎚 讟计指什' + plan.design_notes, + status: 'building', + status_text: '讟计已确讀' + }); + } + + // ════════════════════════════════════════════ + // Phase 2: 铞枊代理执行匀发 + // ════════════════════════════════════════════ + emitProgress({ + type: 'progress', + step: 3, + total_steps: 5, + message: '💻 铞枊代理 · 匀始执行栞心倧脑的匀发计划...', + status: 'building', + status_text: '代理执行䞭' + }); + + var agentResult = await agentExecute(plan, dev_id, api_base, api_key, model, emitProgress); + + emitProgress({ + type: 'progress', + step: 3, + message: '✅ 代理执行完成' + agentResult.files.length + ' 䞪文件已生成', + status: 'building', + status_text: '代码已生成' + }); + + // ════════════════════════════════════════════ + // Phase 2.5: 预览郚眲 + // ════════════════════════════════════════════ + emitProgress({ + type: 'progress', + step: 4, + total_steps: 5, + message: '🚀 铞枊代理 · 郚眲预览...', + status: 'building', + status_text: '郚眲预览' + }); + + // ════════════════════════════════════════════ + // Phase 3: 栞心倧脑自进化 + // ════════════════════════════════════════════ + var finalResult = { + projectName: agentResult.projectName, + files: agentResult.files, + summary: '项目 ' + agentResult.projectName + ' 由铞枊栞心倧脑分析意囟 + 铞枊代理执行匀发完成包含 ' + agentResult.files.length + ' 䞪文件。\n意囟' + (plan.intent_summary || '') + '\n讟计' + (plan.design_notes || '') + }; + + coreBrainPostEvolution(dev_id, conversation, finalResult, brainContext, startTime, true); + + emitProgress({ + type: 'progress', + step: 5, + total_steps: 5, + message: '✹ 铞枊栞心倧脑 · 自进化完成 · 知识䞎暡匏已曎新', + status: 'building', + status_text: '即将完成' + }); + + return finalResult; +} + +module.exports = { + runWorkflow +}; diff --git a/persona-studio/backend/routes/build.js b/persona-studio/backend/routes/build.js index a60dfb18..73a7d0a4 100644 --- a/persona-studio/backend/routes/build.js +++ b/persona-studio/backend/routes/build.js @@ -1,21 +1,29 @@ /** * persona-studio · 匀发任务路由 - * POST /api/ps/build/start 觊发代码生成 + * POST /api/ps/build/start 觊发铞枊代理匀发工䜜流 + * + * 圓甚户点击「我芁匀发」时自劚觊发铞枊代理Agent + * 䜿甚甚户自垊的 API Key 进行 5 步自劚化匀发。 */ const express = require('express'); const router = express.Router(); const memoryManager = require('../brain/memory-manager'); -const codeGenerator = require('../brain/code-generator'); +const agentWorkflow = require('../brain/agent-workflow'); const emailSender = require('../utils/email-sender'); // 邮箱后端正则二次校验 const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +// 支持 EXP-XXX 和 GUEST 䞀种 dev_id 栌匏 +function isValidDevId(devId) { + return devId === 'GUEST' || /^EXP-\d{3,}$/.test(devId); +} + // POST /api/ps/build/start router.post('/start', async (req, res) => { - const { dev_id, email, contact, conversation } = req.body || {}; + const { dev_id, email, contact, conversation, api_base, api_key, model } = req.body || {}; - if (!dev_id || !/^EXP-\d{3,}$/.test(dev_id)) { + if (!dev_id || !isValidDevId(dev_id)) { return res.status(400).json({ error: true, code: 'INVALID_ID', @@ -40,71 +48,94 @@ router.post('/start', async (req, res) => { }); } + // 防止 header injectionAPI Key 䞍埗包含换行笊 + if (api_key && /[\r\n]/.test(api_key)) { + return res.status(400).json({ + error: true, + code: 'INVALID_API_KEY', + message: 'API Key 栌匏无效' + }); + } + // 联系方匏蟓入枅理去陀朜圚 HTML/script 泚入 const safeContact = contact ? String(contact).replace(/[<>&"']/g, '').substring(0, 100) : null; // 先立即响应后台匂步倄理 res.json({ error: false, - message: '匀发任务已接收完成后将发送到 ' + email, - status: 'queued' + message: '铞枊代理已启劚完成后将发送到 ' + email, + status: 'agent_started' }); - // 匂步执行代码生成 + 邮件通知 + // 匂步执行铞枊代理匀发工䜜流 (async () => { - const broadcast = req.app.locals.broadcastToClient || function () {}; + const broadcastRaw = req.app.locals.broadcastToClient || function () {}; + const broadcast = function (data) { broadcastRaw(dev_id, data); }; try { - broadcast(dev_id, { + // 启劚 Agent 工䜜流 + broadcast({ type: 'progress', - message: '🔧 正圚创建项目骚架...', + message: '🌀 铞枊代理已唀醒正圚启劚匀发工䜜流...', status: 'building', - status_text: '构建䞭' + status_text: '代理已启劚' }); - const result = await codeGenerator.generate({ - dev_id, + const result = await agentWorkflow.runWorkflow({ + dev_id: dev_id, conversation: conversation || [], + api_base: api_base || '', + api_key: api_key || '', + model: model || '', + broadcast: broadcast }); - // 记圕项目 - memoryManager.addProject(dev_id, { - name: result.projectName || 'untitled', - email: email, - contact: safeContact, - status: 'completed', - created_at: new Date().toISOString(), - files: result.files || [] - }); + // 记圕项目GUEST 也记圕方䟿远螪 + try { + memoryManager.addProject(dev_id, { + name: result.projectName || 'untitled', + email: email, + contact: safeContact, + status: 'completed', + created_at: new Date().toISOString(), + files: result.files || [] + }); - // 自劚提取知识 - memoryManager.autoExtractKnowledge(dev_id, conversation, result.projectName); + // 自劚提取知识仅泚册匀发者 + if (dev_id !== 'GUEST') { + memoryManager.autoExtractKnowledge(dev_id, conversation, result.projectName); + } + } catch (memErr) { + console.error('Memory save error (non-fatal):', memErr.message); + } // 通知预览就绪 - broadcast(dev_id, { + broadcast({ type: 'preview_ready', project: result.projectName, message: '✅ 预览已就绪' }); - broadcast(dev_id, { + broadcast({ type: 'complete', message: '🎉 党郚完成邮件正圚发送' }); - // 发邮件 - await emailSender.sendCompletion({ + // 发邮件匂步䞍阻塞䞻流皋 + emailSender.sendCompletion({ to: email, - dev_id, + dev_id: dev_id, projectName: result.projectName, summary: result.summary, files: result.files + }).catch(function (emailErr) { + console.error('Email send error (non-fatal):', emailErr.message); }); } catch (err) { - console.error('Build pipeline error:', err.message); - broadcast(dev_id, { + console.error('Agent workflow error:', err.message); + broadcast({ type: 'error', - message: '构建过皋出错: ' + err.message + message: '铞枊代理遇到问题: ' + err.message }); } })(); diff --git a/persona-studio/brain/evolution-log.json b/persona-studio/brain/evolution-log.json index a51724be..7d5a4016 100644 --- a/persona-studio/brain/evolution-log.json +++ b/persona-studio/brain/evolution-log.json @@ -1,7 +1,43 @@ { "schema_version": "1.0", "description": "系统进化日志 · 记圕每次自进化事件", - "last_updated": null, - "total_events": 0, - "events": [] -} + "last_updated": "2026-03-13T05:55:39.412Z", + "total_events": 2, + "events": [ + { + "id": "EVT-1773380616767", + "type": "build_complete", + "description": "项目构建完成: project-1773380616766", + "metadata": { + "dev_id": "EXP-000", + "project": "project-1773380616766", + "files_count": 4, + "build_time_ms": 0, + "success": true, + "patterns_detected": [ + "通甚项目" + ] + }, + "timestamp": "2026-03-13T05:43:36.767Z" + }, + { + "id": "EVT-1773381339412", + "type": "agent_build_complete", + "description": "铞枊代理匀发完成: project-1773381339411", + "metadata": { + "dev_id": "GUEST", + "project": "project-1773381339411", + "files_count": 4, + "build_time_ms": 1, + "success": true, + "brain_source": "local", + "patterns_detected": [ + "䞪人博客" + ], + "developer_tech_level": null, + "developer_projects_total": 0 + }, + "timestamp": "2026-03-13T05:55:39.412Z" + } + ] +} \ No newline at end of file diff --git a/persona-studio/brain/memory/EXP-000/projects.json b/persona-studio/brain/memory/EXP-000/projects.json new file mode 100644 index 00000000..01804274 --- /dev/null +++ b/persona-studio/brain/memory/EXP-000/projects.json @@ -0,0 +1,32 @@ +{ + "dev_id": "EXP-000", + "projects": [ + { + "name": "project-1773380616766", + "email": "test@test.com", + "contact": null, + "status": "completed", + "created_at": "2026-03-13T05:43:36.768Z", + "files": [ + "index.html", + "style.css", + "main.js", + "README.md" + ] + }, + { + "name": "project-1773380932239", + "email": "test@test.com", + "contact": null, + "status": "completed", + "created_at": "2026-03-13T05:48:52.239Z", + "files": [ + "index.html", + "style.css", + "main.js", + "README.md" + ] + } + ], + "updated_at": "2026-03-13T05:48:52.239Z" +} \ No newline at end of file diff --git a/persona-studio/brain/memory/GUEST/projects.json b/persona-studio/brain/memory/GUEST/projects.json new file mode 100644 index 00000000..623b2b3b --- /dev/null +++ b/persona-studio/brain/memory/GUEST/projects.json @@ -0,0 +1,32 @@ +{ + "dev_id": "GUEST", + "projects": [ + { + "name": "project-1773380932224", + "email": "test@test.com", + "contact": null, + "status": "completed", + "created_at": "2026-03-13T05:48:52.225Z", + "files": [ + "index.html", + "style.css", + "main.js", + "README.md" + ] + }, + { + "name": "project-1773381339411", + "email": "test@example.com", + "contact": null, + "status": "completed", + "created_at": "2026-03-13T05:55:39.412Z", + "files": [ + "index.html", + "style.css", + "main.js", + "README.md" + ] + } + ], + "updated_at": "2026-03-13T05:55:39.412Z" +} \ No newline at end of file diff --git a/persona-studio/brain/pattern-library.json b/persona-studio/brain/pattern-library.json index 9741c176..9889a7e9 100644 --- a/persona-studio/brain/pattern-library.json +++ b/persona-studio/brain/pattern-library.json @@ -1,7 +1,32 @@ { "schema_version": "1.0", "description": "系统暡匏库 · 自劚识别 · 每完成䞀䞪项目+每日聚合曎新", - "last_updated": null, - "total_patterns": 0, - "patterns": [] -} + "last_updated": "2026-03-13T05:55:39.412Z", + "total_patterns": 2, + "patterns": [ + { + "name": "通甚项目", + "frequency": 1, + "common_features": [], + "common_tech_stack": [ + "HTML/CSS/JS" + ], + "avg_build_time": null, + "success_rate": 100, + "first_seen": "2026-03-13T05:43:36.767Z", + "last_seen": "2026-03-13T05:43:36.767Z" + }, + { + "name": "䞪人博客", + "frequency": 1, + "common_features": [], + "common_tech_stack": [ + "HTML/CSS/JS" + ], + "avg_build_time": 1, + "success_rate": 100, + "first_seen": "2026-03-13T05:55:39.412Z", + "last_seen": "2026-03-13T05:55:39.412Z" + } + ] +} \ No newline at end of file diff --git a/persona-studio/frontend/chat.js b/persona-studio/frontend/chat.js index 44bbb726..2d263e98 100644 --- a/persona-studio/frontend/chat.js +++ b/persona-studio/frontend/chat.js @@ -640,28 +640,42 @@ async function confirmBuild() { if (contact) sessionStorage.setItem('ps_build_contact', contact); closeEmailModal(); - appendMessage('system', '🚀 匀发任务已提亀完成后䌚发送到 ' + email); + appendMessage('system', '🌀 铞枊代理已启劚完成后䌚发送到 ' + email); // 进入分屏暡匏 enterDevMode(); + // 先连接 WebSocket确保圚 build 匀始前建立连接避免䞢倱进床消息 + connectPreviewWebSocket(); + try { - await fetch(API_BASE + '/api/ps/build/start', { + var buildRes = await fetch(API_BASE + '/api/ps/build/start', { method: 'POST', headers: authHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ dev_id: DEV_ID, email: email, contact: contact, - conversation: conversationHistory + conversation: conversationHistory, + api_base: USER_API_BASE, + api_key: USER_API_KEY, + model: SELECTED_MODEL }) }); - } catch (_err) { - appendMessage('system', '任务提亀倱莥请皍后再试'); - } - // 连接 WebSocket 获取进床曎新 - connectPreviewWebSocket(); + if (!buildRes.ok) { + var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText; + try { + var errData = await buildRes.json(); + if (errData.message) errMsg = errData.message; + } catch (_e) { /* use status text fallback */ } + appendMessage('system', '⚠ 铞枊代理启劚倱莥: ' + errMsg); + updatePreviewStatus('error', '启劚倱莥'); + } + } catch (_err) { + appendMessage('system', '⚠ 任务提亀倱莥请检查眑络连接后再试'); + updatePreviewStatus('error', '眑络错误'); + } } /* ---- Dev Mode: Split Screen ---- */ diff --git a/scripts/create-standardized-ticket.js b/scripts/create-standardized-ticket.js new file mode 100644 index 00000000..4bfb5b1f --- /dev/null +++ b/scripts/create-standardized-ticket.js @@ -0,0 +1,197 @@ +// scripts/create-standardized-ticket.js +// 铞枊 · Phase B1 · 标准化工单创建脚本 +// +// 将 SYSLOG 写入 Notion 工单队列时䜿甚标准化栌匏 +// 让 Notion Agent 胜借识别和觊发后续倄理。 +// +// 环境变量 +// NOTION_TOKEN Notion API token +// NOTION_TICKET_DB_ID 工单队列数据库 ID +// BROADCAST_ID 广播猖号劂 BC-M23-001-AW +// DEVELOPER 匀发者信息劂 "DEV-012 Awen" +// SYSLOG_RAW 完敎 SYSLOG 原文JSON 字笊䞲或纯文本 +// SUBMIT_TYPE 提亀类型syslog / question +// PERSONA_RESULT 人栌䜓倄理结果可选銖次可䞺空 +// MODULE_VERIFY 暡块验证结果 JSON可选 +// MODULES_UPLOADED 暡块是吊党郚䞊䌠true/false + +'use strict'; + +const https = require('https'); +const fs = require('fs'); + +const NOTION_TOKEN = process.env.NOTION_TOKEN || ''; +const NOTION_TICKET_DB_ID = process.env.NOTION_TICKET_DB_ID || ''; +const BROADCAST_ID = process.env.BROADCAST_ID || 'UNKNOWN'; +const DEVELOPER = process.env.DEVELOPER || process.env.AUTHOR || 'unknown'; +const SYSLOG_RAW = process.env.SYSLOG_RAW || process.env.SUBMIT_CONTENT || ''; +const SUBMIT_TYPE = process.env.SUBMIT_TYPE || 'syslog'; +const PERSONA_RESULT = process.env.PERSONA_RESULT || ''; +const MODULE_VERIFY = process.env.MODULE_VERIFY || ''; +const MODULES_UPLOADED = process.env.MODULES_UPLOADED || 'false'; + +const NOTION_VERSION = '2022-06-28'; +const NOTION_API_HOSTNAME = 'api.notion.com'; +const NOTION_RICH_TEXT_MAX = 2000; + +// ══════════════════════════════════════════════════════════ +// Notion API 工具 +// ══════════════════════════════════════════════════════════ + +function notionPost(endpoint, body) { + return new Promise(function (resolve, reject) { + var payload = JSON.stringify(body); + var opts = { + hostname: NOTION_API_HOSTNAME, + port: 443, + path: endpoint, + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + NOTION_TOKEN, + 'Content-Type': 'application/json', + 'Notion-Version': NOTION_VERSION, + 'Content-Length': Buffer.byteLength(payload), + }, + }; + var req = https.request(opts, function (res) { + var data = ''; + res.on('data', function (chunk) { data += chunk; }); + res.on('end', function () { + try { + var parsed = JSON.parse(data); + if (res.statusCode >= 200 && res.statusCode < 300) { + resolve(parsed); + } else { + reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data))); + } + } catch (e) { + reject(new Error('Notion API parse error: ' + data)); + } + }); + }); + req.on('error', reject); + req.write(payload); + req.end(); + }); +} + +function richTextChunks(content) { + var str = String(content || ''); + var chunks = []; + for (var i = 0; i < str.length; i += NOTION_RICH_TEXT_MAX) { + chunks.push({ type: 'text', text: { content: str.slice(i, i + NOTION_RICH_TEXT_MAX) } }); + } + if (chunks.length === 0) { + chunks.push({ type: 'text', text: { content: '' } }); + } + return chunks; +} + +// ══════════════════════════════════════════════════════════ +// 标准化工单创建 +// ══════════════════════════════════════════════════════════ + +async function createStandardizedTicket() { + if (!NOTION_TOKEN || !NOTION_TICKET_DB_ID) { + console.log('⚠ Notion credentials not configured, skipping ticket creation'); + process.exit(0); + } + + var now = new Date().toISOString(); + var typeLabel = SUBMIT_TYPE === 'syslog' ? 'SYSLOG倄理' : '提问解答'; + var ticketStatus = SUBMIT_TYPE === 'syslog' ? '埅倄理' : '已完成'; + var verifyStatus = MODULES_UPLOADED === 'true' ? '暡块已验证✅' : '暡块埅验证⚠'; + var title = '[自劚] ' + BROADCAST_ID + ' · ' + typeLabel + ' · ' + verifyStatus; + + // Phase B1 标准化字段 — 工单内容 + var ticketContent = [ + '## 📡 标准化工单 · Phase B1', + '', + '| 字段 | 倌 |', + '|------|-----|', + '| 工单类型 | ' + typeLabel + ' |', + '| 状态 | ' + ticketStatus + ' |', + '| 来源 | GitHub Actions |', + '| taskId | ' + BROADCAST_ID + ' |', + '| developer | ' + DEVELOPER + ' |', + '| created_at | ' + now + ' |', + '| retry_count | 0 |', + '| receipt_status | pending |', + '', + ]; + + if (PERSONA_RESULT) { + ticketContent.push('## 铞枊栞心倧脑倄理结果'); + ticketContent.push(''); + ticketContent.push(PERSONA_RESULT.slice(0, 1500)); + ticketContent.push(''); + } + + ticketContent.push('## 暡块䞊䌠验证'); + ticketContent.push(''); + ticketContent.push('暡块党郚䞊䌠: ' + (MODULES_UPLOADED === 'true' ? '✅ 是' : '❌ 吊')); + ticketContent.push(''); + + if (MODULE_VERIFY) { + ticketContent.push('验证诊情: ' + MODULE_VERIFY.slice(0, 400)); + ticketContent.push(''); + } + + var contentText = ticketContent.join('\n'); + + // 构建 Notion 页面 + var body = { + parent: { database_id: NOTION_TICKET_DB_ID }, + properties: { + '标题': { title: [{ type: 'text', text: { content: title.slice(0, 120) } }] }, + '操䜜类型': { select: { name: '其他' } }, + '提亀者': { rich_text: [{ type: 'text', text: { content: '铞枊Agent·自劚管道' } }] }, + '状态': { select: { name: ticketStatus } }, + '䌘先级': { select: { name: 'P1' } }, + }, + children: [ + { + object: 'block', + type: 'paragraph', + paragraph: { + rich_text: richTextChunks(contentText), + }, + }, + ], + }; + + // 将 SYSLOG 原文䜜䞺代码块附加劂果有 + if (SYSLOG_RAW) { + body.children.push({ + object: 'block', + type: 'code', + code: { + rich_text: richTextChunks(SYSLOG_RAW.slice(0, 8000)), + language: 'json', + }, + }); + } + + console.log('📋 创建标准化工单...'); + console.log(' taskId: ' + BROADCAST_ID); + console.log(' developer: ' + DEVELOPER); + console.log(' type: ' + typeLabel); + console.log(' receipt_status: pending'); + + try { + var result = await notionPost('/v1/pages', body); + console.log('✅ Notion 标准化工单已创建: ' + result.id); + + // 蟓出工单 ID 到 GITHUB_OUTPUT + var outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + fs.appendFileSync(outputFile, 'ticket_page_id=' + result.id + '\n'); + fs.appendFileSync(outputFile, 'ticket_url=' + (result.url || '') + '\n'); + } + } catch (err) { + console.error('❌ 工单创建倱莥: ' + err.message); + process.exit(1); + } +} + +createStandardizedTicket(); diff --git a/scripts/invoke-persona.js b/scripts/invoke-persona.js new file mode 100644 index 00000000..c88cbd5c --- /dev/null +++ b/scripts/invoke-persona.js @@ -0,0 +1,360 @@ +// scripts/invoke-persona.js +// 铞枊 · Phase B2 · 人栌䜓唀醒调甚脚本 +// +// Notion Agent 调甚歀脚本通过 workflow_dispatch +// 脚本读取 Notion 工单内容 → 唀醒人栌䜓 → 倄理结果写回 Notion。 +// +// 环境变量 +// WORK_ORDER_ID Notion 工单页面 ID +// TASK_ID 广播猖号劂 BC-M23-001-AW +// DEVELOPER 匀发者信息劂 "DEV-012 Awen" +// SYSLOG_RAW SYSLOG JSON 原文 +// ACTION 劚䜜类型process_syslog / retry +// LLM_API_KEY 第䞉方 LLM 平台密钥 +// LLM_BASE_URL 第䞉方 LLM 平台 API 地址 +// NOTION_TOKEN Notion API token +// CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 ID +// PORTRAIT_DB_ID 匀发者劚态画像库 ID +// FINGERPRINT_DB_ID 暡块指纹泚册衚 ID +// INVOKE_API_KEY 鉎权密钥甚于 API 调甚验证 + +'use strict'; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); +const childProcess = require('child_process'); + +// ══════════════════════════════════════════════════════════ +// 配眮 +// ══════════════════════════════════════════════════════════ + +var WORK_ORDER_ID = process.env.WORK_ORDER_ID || ''; +var TASK_ID = process.env.TASK_ID || process.env.BROADCAST_ID || 'UNKNOWN'; +var DEVELOPER = process.env.DEVELOPER || 'unknown'; +var SYSLOG_RAW = process.env.SYSLOG_RAW || ''; +var ACTION = process.env.ACTION || 'process_syslog'; + +var LLM_API_KEY = process.env.LLM_API_KEY || ''; +var LLM_BASE_URL = (process.env.LLM_BASE_URL || '').replace(/\/+$/, ''); +var NOTION_TOKEN = process.env.NOTION_TOKEN || ''; +var CORE_BRAIN_PAGE_ID = process.env.CORE_BRAIN_PAGE_ID || ''; +var PORTRAIT_DB_ID = process.env.PORTRAIT_DB_ID || ''; +var FINGERPRINT_DB_ID = process.env.FINGERPRINT_DB_ID || ''; + +var NOTION_VERSION = '2022-06-28'; +var NOTION_API_HOSTNAME = 'api.notion.com'; + +// ══════════════════════════════════════════════════════════ +// HTTP 工具 +// ══════════════════════════════════════════════════════════ + +function httpRequest(url, options, body) { + return new Promise(function (resolve, reject) { + var parsed = new URL(url); + var isHttps = parsed.protocol === 'https:'; + var mod = isHttps ? https : http; + + var opts = { + hostname: parsed.hostname, + port: parsed.port || (isHttps ? 443 : 80), + path: parsed.pathname + parsed.search, + method: options.method || 'GET', + headers: options.headers || {}, + timeout: options.timeout || 120000, + }; + + var req = mod.request(opts, function (res) { + var data = ''; + res.on('data', function (chunk) { data += chunk; }); + res.on('end', function () { + resolve({ status: res.statusCode, body: data }); + }); + }); + + req.on('error', reject); + req.on('timeout', function () { req.destroy(); reject(new Error('Request timeout')); }); + + if (body) { + req.write(typeof body === 'string' ? body : JSON.stringify(body)); + } + req.end(); + }); +} + +function notionRequest(method, endpoint, body) { + var url = 'https://' + NOTION_API_HOSTNAME + endpoint; + var headers = { + 'Authorization': 'Bearer ' + NOTION_TOKEN, + 'Content-Type': 'application/json', + 'Notion-Version': NOTION_VERSION, + }; + return httpRequest(url, { method: method, headers: headers }, body ? JSON.stringify(body) : null) + .then(function (res) { + var parsed = JSON.parse(res.body); + if (res.status >= 200 && res.status < 300) return parsed; + throw new Error('Notion API ' + res.status + ': ' + (parsed.message || res.body)); + }); +} + +// ══════════════════════════════════════════════════════════ +// Step 1: 读取工单内容从 Notion +// ══════════════════════════════════════════════════════════ + +async function readWorkOrder() { + if (!WORK_ORDER_ID) { + console.log('ℹ No WORK_ORDER_ID, using SYSLOG_RAW directly'); + return { syslog_raw: SYSLOG_RAW, taskId: TASK_ID, developer: DEVELOPER }; + } + + console.log('📖 读取 Notion 工单: ' + WORK_ORDER_ID); + try { + var page = await notionRequest('GET', '/v1/pages/' + WORK_ORDER_ID); + var props = page.properties || {}; + + // 提取工单字段 + var taskId = TASK_ID; + var developer = DEVELOPER; + + // 尝试从工单属性䞭读取 + if (props['广播猖号'] && props['广播猖号'].rich_text) { + var bcText = props['广播猖号'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + if (bcText) taskId = bcText; + } + if (props['匀发者猖号'] && props['匀发者猖号'].rich_text) { + var devText = props['匀发者猖号'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + if (devText) developer = devText; + } + + // 读取页面内容子块来获取 SYSLOG 原文 + var blocks = await notionRequest('GET', '/v1/blocks/' + WORK_ORDER_ID + '/children?page_size=20'); + var syslogRaw = SYSLOG_RAW; + + if (blocks.results) { + blocks.results.forEach(function (block) { + if (block.type === 'code' && block.code && block.code.rich_text) { + var codeText = block.code.rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + if (codeText.length > syslogRaw.length) { + syslogRaw = codeText; + } + } + }); + } + + console.log(' → taskId: ' + taskId); + console.log(' → developer: ' + developer); + console.log(' → syslog_raw length: ' + syslogRaw.length); + + return { syslog_raw: syslogRaw, taskId: taskId, developer: developer }; + } catch (err) { + console.log('⚠ 工单读取倱莥: ' + err.message + '䜿甚环境变量'); + return { syslog_raw: SYSLOG_RAW, taskId: TASK_ID, developer: DEVELOPER }; + } +} + +// ══════════════════════════════════════════════════════════ +// Step 2: 唀醒人栌䜓倍甚 wake-persona.js 的逻蟑 +// ══════════════════════════════════════════════════════════ + +async function invokePersona(workOrder) { + console.log('🧠 唀醒人栌䜓倄理 SYSLOG...'); + + // 讟眮环境变量䟛 wake-persona.js 读取 + process.env.BROADCAST_ID = workOrder.taskId; + process.env.SUBMIT_TYPE = 'syslog'; + process.env.SUBMIT_CONTENT = workOrder.syslog_raw; + process.env.AUTHOR = workOrder.developer; + + // 执行 wake-persona.jsfork 子进皋 + var wakeScript = path.join(__dirname, 'wake-persona.js'); + + return new Promise(function (resolve, reject) { + var child = childProcess.fork(wakeScript, [], { + env: Object.assign({}, process.env, { + BROADCAST_ID: workOrder.taskId, + SUBMIT_TYPE: 'syslog', + SUBMIT_CONTENT: workOrder.syslog_raw, + AUTHOR: workOrder.developer, + LLM_API_KEY: LLM_API_KEY, + LLM_BASE_URL: LLM_BASE_URL, + NOTION_TOKEN: NOTION_TOKEN, + CORE_BRAIN_PAGE_ID: CORE_BRAIN_PAGE_ID, + PORTRAIT_DB_ID: PORTRAIT_DB_ID, + FINGERPRINT_DB_ID: FINGERPRINT_DB_ID, + }), + stdio: ['pipe', 'pipe', 'pipe', 'ipc'], + }); + + var stdout = ''; + var stderr = ''; + child.stdout.on('data', function (d) { stdout += d; process.stdout.write(d); }); + child.stderr.on('data', function (d) { stderr += d; process.stderr.write(d); }); + + child.on('exit', function (code) { + if (code === 0) { + // 从 GITHUB_OUTPUT 文件读取 result劂果存圚 + var outputFile = process.env.GITHUB_OUTPUT; + var result = ''; + if (outputFile && fs.existsSync(outputFile)) { + var outputContent = fs.readFileSync(outputFile, 'utf8'); + var resultMatch = outputContent.match(/result<= 200 && res.status < 300) { + var tickets = parsed.results || []; + console.log(' → 扟到 ' + tickets.length + ' 䞪埅倄理工单'); + return tickets; + } else { + console.log('⚠ 查询倱莥: ' + (parsed.message || res.body)); + return []; + } + } catch (err) { + console.log('⚠ 查询匂垞: ' + err.message); + return []; + } +} + +// ══════════════════════════════════════════════════════════ +// Step 2: 检测超时工单 +// ══════════════════════════════════════════════════════════ + +function filterTimedOutTickets(tickets) { + var now = Date.now(); + var timedOut = []; + + tickets.forEach(function (ticket) { + var createdTime = new Date(ticket.created_time).getTime(); + var age = now - createdTime; + + // 只倄理来自 "铞枊Agent·自劚管道" 的 SYSLOG 工单 + var props = ticket.properties || {}; + var submitter = ''; + if (props['提亀者'] && props['提亀者'].rich_text) { + submitter = props['提亀者'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + } + + // 只对自劚管道创建的工单做心跳检测 + if (!submitter.includes('铞枊Agent') && !submitter.includes('自劚管道')) { + return; + } + + // 超过 5 分钟未完成 + if (age > HEARTBEAT_TIMEOUT_MS) { + var title = ''; + if (props['标题'] && props['标题'].title) { + title = props['标题'].title.map(function (t) { return t.plain_text || ''; }).join(''); + } + + timedOut.push({ + id: ticket.id, + title: title, + createdTime: ticket.created_time, + ageMinutes: Math.round(age / 60000), + }); + } + }); + + console.log(' → ' + timedOut.length + ' 䞪工单超时> 5 分钟'); + return timedOut; +} + +// ══════════════════════════════════════════════════════════ +// Step 3: 觊发重试通过 workflow_dispatch +// ══════════════════════════════════════════════════════════ + +async function triggerRetry(ticket) { + if (!GITHUB_TOKEN) { + console.log('⚠ 猺少 GITHUB_TOKEN无法觊发重试'); + return false; + } + + console.log('🔄 重试唀醒: ' + ticket.title + ' (超时 ' + ticket.ageMinutes + ' 分钟)'); + + // 从工单标题提取 broadcast_id + var bcMatch = (ticket.title || '').match(/BC-[A-Z0-9]+-\d+-[A-Z]+/i); + var broadcastId = bcMatch ? bcMatch[0] : 'UNKNOWN'; + + try { + var res = await httpsPost('api.github.com', + '/repos/' + REPO_OWNER + '/' + REPO_NAME + '/actions/workflows/persona-invoke.yml/dispatches', + { + ref: 'main', + inputs: { + work_order_id: ticket.id, + task_id: broadcastId, + developer: 'heartbeat-retry', + action: 'retry', + }, + }, + { + 'Authorization': 'Bearer ' + GITHUB_TOKEN, + 'Accept': 'application/vnd.github+json', + 'User-Agent': 'ZhuyuanHeartbeat/1.0', + 'X-GitHub-Api-Version': '2022-11-28', + } + ); + + if (res.status === 204 || res.status === 200) { + console.log(' → ✅ workflow_dispatch 觊发成功'); + return true; + } else { + console.log(' → ⚠ 觊发倱莥: HTTP ' + res.status + ' ' + res.body); + return false; + } + } catch (err) { + console.log(' → ⚠ 觊发匂垞: ' + err.message); + return false; + } +} + +// ══════════════════════════════════════════════════════════ +// Step 4: 标记工单匂垞 +// ══════════════════════════════════════════════════════════ + +async function markTicketError(ticketId) { + try { + await httpsPatch('api.notion.com', '/v1/pages/' + ticketId, { + properties: { + '状态': { select: { name: '⚠ 匂垞·等人工介入' } }, + }, + }, notionHeaders); + console.log(' → 工单已标记䞺 ⚠ 匂垞·等人工介入'); + } catch (err) { + console.log(' → 标记倱莥: ' + err.message); + } +} + +// ══════════════════════════════════════════════════════════ +// Step 5: 发送告譊邮件 +// ══════════════════════════════════════════════════════════ + +async function sendAlertEmail(timedOutTickets) { + if (!SMTP_USER || !SMTP_PASS) { + console.log('⚠ SMTP 未配眮跳过告譊邮件'); + return; + } + + try { + var nodemailer = require('nodemailer'); + + var ticketList = timedOutTickets.map(function (t) { + return '- ' + t.title + ' (超时 ' + t.ageMinutes + ' 分钟, 重试已耗尜)'; + }).join('\n'); + + var transporter = nodemailer.createTransport({ + host: 'smtp.qq.com', + port: 465, + secure: true, + auth: { user: SMTP_USER, pass: SMTP_PASS }, + }); + + await transporter.sendMail({ + from: '"光湖系统·告譊" <' + SMTP_USER + '>', + to: ALERT_EMAIL, + subject: '[光湖系统] ⚠ SYSLOG 工单倄理超时告譊', + html: [ + '
', + '

⚠ 工单倄理超时告譊

', + '

以䞋工单超过 3 次重试仍未收到人栌䜓回执

', + '
' + ticketList + '
', + '

请登圕 Notion 工单队列检查。

', + '

🌀 铞枊 · 心跳监控 · 自劚告譊

', + '
', + ].join('\n'), + }); + + console.log('📧 告譊邮件已发送至 ' + ALERT_EMAIL); + } catch (err) { + console.log('⚠ 告譊邮件发送倱莥: ' + err.message); + } +} + +// ══════════════════════════════════════════════════════════ +// 䞻流皋 +// ══════════════════════════════════════════════════════════ + +async function main() { + console.log('═══════════════════════════════════════════'); + console.log('💓 铞枊 · 工单心跳监控Phase B3'); + console.log('═══════════════════════════════════════════'); + console.log(' æ—¶é—Ž: ' + new Date().toISOString()); + console.log(''); + + // Step 1: 查询埅倄理工单 + var tickets = await queryPendingTickets(); + if (tickets.length === 0) { + console.log('✅ 无埅倄理工单心跳正垞'); + return; + } + + // Step 2: 筛选超时工单 + var timedOut = filterTimedOutTickets(tickets); + if (timedOut.length === 0) { + console.log('✅ 所有工单均圚倄理窗口内心跳正垞'); + return; + } + + // Step 3 & 4: 对超时工单执行重试或标记匂垞 + var errorTickets = []; + + for (var i = 0; i < timedOut.length; i++) { + var ticket = timedOut[i]; + // 基于超时时闎䌰算重试次数每 5 分钟䞀次 + var estimatedRetries = Math.floor(ticket.ageMinutes / 5); + + if (estimatedRetries < MAX_RETRIES) { + // 重试 + await triggerRetry(ticket); + } else { + // 超过最倧重试次数标记匂垞 + console.log('❌ 工单 ' + ticket.title + ' 重试 ' + MAX_RETRIES + ' 次仍无回执'); + await markTicketError(ticket.id); + errorTickets.push(ticket); + } + } + + // Step 5: 发送告譊邮件劂有匂垞工单 + if (errorTickets.length > 0) { + await sendAlertEmail(errorTickets); + } + + console.log(''); + console.log('✅ 心跳检测完成'); +} + +main().catch(function (err) { + console.error('❌ 心跳监控倱莥: ' + err.message); + process.exit(1); +}); diff --git a/scripts/push-broadcast-to-github.js b/scripts/push-broadcast-to-github.js new file mode 100644 index 00000000..41cd164f --- /dev/null +++ b/scripts/push-broadcast-to-github.js @@ -0,0 +1,171 @@ +// scripts/push-broadcast-to-github.js +// 铞枊 · Phase B4 · 广播文件掚送到 GitHub 仓库 +// +// 人栌䜓生成广播后通过 GitHub API 将广播 .md 文件掚送到仓库。 +// 路埄规范broadcasts/{developer_id}/{taskId}.md +// 提亀信息[AutoBroadcast] {taskId} · {developer} · {描述} +// +// 环境变量 +// GITHUB_TOKEN GitHub API token掚送甚 +// BROADCAST_ID 广播猖号劂 BC-M23-001-AW +// DEVELOPER_ID 匀发者猖号劂 DEV-012 +// DEVELOPER_NAME 匀发者名字劂 Awen +// BROADCAST_CONTENT 广播党文内容Markdown +// MODULE_NAME 暡块名可选劂 M23 +// PHASE_NUMBER 环节号可选劂 1 + +'use strict'; + +var https = require('https'); +var fs = require('fs'); +var devSuffixMap = require('./utils/dev-suffix-map'); + +var GITHUB_TOKEN = process.env.GITHUB_TOKEN || ''; +var BROADCAST_ID = process.env.BROADCAST_ID || ''; +var DEVELOPER_ID = process.env.DEVELOPER_ID || ''; +var DEVELOPER_NAME = process.env.DEVELOPER_NAME || ''; +var BROADCAST_CONTENT = process.env.BROADCAST_CONTENT || ''; +var MODULE_NAME = process.env.MODULE_NAME || ''; +var PHASE_NUMBER = process.env.PHASE_NUMBER || ''; + +var REPO_OWNER = 'qinfendebingshuo'; +var REPO_NAME = 'guanghulab'; + +// ══════════════════════════════════════════════════════════ +// GitHub API 工具 +// ══════════════════════════════════════════════════════════ + +function githubRequest(method, apiPath, body) { + return new Promise(function (resolve, reject) { + var payload = body ? JSON.stringify(body) : ''; + var opts = { + hostname: 'api.github.com', + port: 443, + path: apiPath, + method: method, + headers: { + 'User-Agent': 'ZhuyuanBroadcast/1.0', + 'Accept': 'application/vnd.github+json', + 'Content-Type': 'application/json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + }; + + if (GITHUB_TOKEN) { + opts.headers['Authorization'] = 'Bearer ' + GITHUB_TOKEN; + } + + if (payload) { + opts.headers['Content-Length'] = Buffer.byteLength(payload); + } + + var req = https.request(opts, function (res) { + var data = ''; + res.on('data', function (chunk) { data += chunk; }); + res.on('end', function () { + try { + resolve({ status: res.statusCode, data: JSON.parse(data) }); + } catch (e) { + resolve({ status: res.statusCode, data: data }); + } + }); + }); + + req.on('error', reject); + if (payload) req.write(payload); + req.end(); + }); +} + +// ══════════════════════════════════════════════════════════ +// 广播掚送 +// ══════════════════════════════════════════════════════════ + +async function pushBroadcast() { + if (!GITHUB_TOKEN) { + console.log('⚠ 猺少 GITHUB_TOKEN无法掚送广播'); + process.exit(1); + } + + if (!BROADCAST_ID || !BROADCAST_CONTENT) { + console.log('⚠ 猺少 BROADCAST_ID 或 BROADCAST_CONTENT'); + process.exit(1); + } + + // 确定匀发者 ID从广播猖号提取或䜿甚环境变量 + var devId = DEVELOPER_ID; + if (!devId) { + devId = devSuffixMap.getDevIdFromBroadcast(BROADCAST_ID) || 'UNKNOWN'; + } + + // 构建路埄和提亀信息 + var filePath = 'broadcasts/' + devId + '/' + BROADCAST_ID + '.md'; + var moduleLabel = MODULE_NAME || BROADCAST_ID.match(/BC-([A-Z0-9-]+)-/i)?.[1] || ''; + var phaseLabel = PHASE_NUMBER ? '环节' + PHASE_NUMBER : ''; + var devLabel = DEVELOPER_NAME || devId; + var commitMessage = '[AutoBroadcast] ' + BROADCAST_ID + ' · ' + devLabel + ' · ' + moduleLabel + phaseLabel; + + console.log('📡 掚送广播到 GitHub...'); + console.log(' 路埄: ' + filePath); + console.log(' 提亀: ' + commitMessage); + + // 检查文件是吊已存圚获取 SHA + var existingRes = await githubRequest('GET', + '/repos/' + REPO_OWNER + '/' + REPO_NAME + '/contents/' + filePath); + + var sha = null; + if (existingRes.status === 200 && existingRes.data && existingRes.data.sha) { + sha = existingRes.data.sha; + console.log(' → 文件已存圚将芆盖曎新 (sha: ' + sha.slice(0, 8) + ')'); + } + + // Base64 猖码广播内容 + var contentBase64 = Buffer.from(BROADCAST_CONTENT, 'utf8').toString('base64'); + + // 创建或曎新文件 + var putBody = { + message: commitMessage, + content: contentBase64, + committer: { + name: 'zhuyuan-agent[bot]', + email: 'zhuyuan-agent[bot]@users.noreply.github.com', + }, + }; + + if (sha) { + putBody.sha = sha; + } + + var putRes = await githubRequest('PUT', + '/repos/' + REPO_OWNER + '/' + REPO_NAME + '/contents/' + filePath, + putBody); + + if (putRes.status === 200 || putRes.status === 201) { + console.log('✅ 广播已掚送: ' + filePath); + console.log(' → commit: ' + (putRes.data.commit ? putRes.data.commit.sha.slice(0, 8) : 'unknown')); + + // 蟓出到 GITHUB_OUTPUT + var outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + fs.appendFileSync(outputFile, 'broadcast_pushed=true\n'); + fs.appendFileSync(outputFile, 'broadcast_path=' + filePath + '\n'); + fs.appendFileSync(outputFile, 'broadcast_commit=' + (putRes.data.commit ? putRes.data.commit.sha : '') + '\n'); + } + } else { + console.error('❌ 广播掚送倱莥: HTTP ' + putRes.status); + console.error(' → ' + JSON.stringify(putRes.data)); + + // 蟓出倱莥状态 + var outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + fs.appendFileSync(outputFile, 'broadcast_pushed=false\n'); + } + + process.exit(1); + } +} + +pushBroadcast().catch(function (err) { + console.error('❌ 广播掚送匂垞: ' + err.message); + process.exit(1); +}); diff --git a/scripts/receive-syslog.js b/scripts/receive-syslog.js index dd136852..00ff57f0 100644 --- a/scripts/receive-syslog.js +++ b/scripts/receive-syslog.js @@ -194,11 +194,13 @@ async function createSyslogEntry(syslog, token, dbId, dateStr) { async function createTicket(syslog, token, dbId, dateStr) { const broadcastId = syslog.broadcast_id || syslog.broadcastId || 'UNKNOWN'; const devId = syslog.dev_id || syslog.developer_id || 'UNKNOWN'; + const devName = syslog.developer_name || syslog.dev_name || ''; + const now = new Date().toISOString(); const properties = { - '标题': titleProp('SYSLOG 回䌠' + broadcastId + ' · ' + devId), + '标题': titleProp('[自劚] SYSLOG倄理' + broadcastId + ' · ' + devId), '操䜜类型': selectProp('其他'), - '提亀者': richTextProp('巡检匕擎'), + '提亀者': richTextProp('铞枊Agent·自劚管道'), '提亀日期': dateProp(dateStr), '状态': selectProp('埅倄理'), '䌘先级': selectProp('P1'), @@ -208,6 +210,22 @@ async function createTicket(syslog, token, dbId, dateStr) { if (broadcastId !== 'UNKNOWN') properties['广播猖号'] = richTextProp(broadcastId); if (devId !== 'UNKNOWN') properties['匀发者猖号'] = richTextProp(devId); + // Phase B1 标准化工单内容 + const ticketContent = [ + '## 📡 标准化工单 · Phase B1', + '', + '| 字段 | 倌 |', + '|------|-----|', + '| 工单类型 | SYSLOG倄理 |', + '| 状态 | 埅倄理 |', + '| 来源 | GitHub Actions |', + '| taskId | ' + broadcastId + ' |', + '| developer | ' + devId + ' ' + devName + ' |', + '| created_at | ' + now + ' |', + '| retry_count | 0 |', + '| receipt_status | pending |', + ].join('\n'); + const body = { parent: { database_id: dbId }, properties, @@ -218,7 +236,7 @@ async function createTicket(syslog, token, dbId, dateStr) { paragraph: { rich_text: [{ type: 'text', - text: { content: '📥 SYSLOG 回䌠来自 ' + devId + '关联广播 ' + broadcastId }, + text: { content: ticketContent }, }], }, }, @@ -234,7 +252,7 @@ async function createTicket(syslog, token, dbId, dateStr) { }; const result = await notionPost('/v1/pages', body, token); - console.log(' → 霜砚工单已创建: ' + result.id); + console.log(' → 标准化工单已创建: ' + result.id); return result; } diff --git a/scripts/utils/dev-suffix-map.js b/scripts/utils/dev-suffix-map.js new file mode 100644 index 00000000..35248f80 --- /dev/null +++ b/scripts/utils/dev-suffix-map.js @@ -0,0 +1,38 @@ +// scripts/utils/dev-suffix-map.js +// 匀发者广播猖号后猀 → DEV-ID 映射衚 +// +// 广播猖号栌匏: BC-{暡块}-{序号}-{后猀} +// 后猀是匀发者名字拌音銖字母猩写 + +'use strict'; + +// 后猀→匀发者猖号映射 +var SUFFIX_MAP = { + 'YY': 'DEV-001', // 页页 + 'FM': 'DEV-002', // 肥猫 + 'YF': 'DEV-003', // 燕暊 + 'ZZ': 'DEV-004', // 之之 + 'XCM': 'DEV-005', // 小草莓 + 'HE': 'DEV-009', // 花尔 + 'JZ': 'DEV-010', // 桔子 + 'CCNN': 'DEV-011', // 匆匆那幎 + 'AW': 'DEV-012', // Awen + 'XX': 'DEV-013', // 小兎 + 'SY': 'DEV-014', // 时雚 +}; + +/** + * 从广播猖号䞭提取匀发者猖号 + * @param {string} broadcastId - 广播猖号劂 BC-M22-009-AW + * @returns {string} 匀发者猖号劂 DEV-012未扟到返回空字笊䞲 + */ +function getDevIdFromBroadcast(broadcastId) { + var suffixMatch = (broadcastId || '').match(/BC-[A-Z0-9]+-\d+-([A-Z]+)/i); + var suffix = suffixMatch ? suffixMatch[1].toUpperCase() : ''; + return SUFFIX_MAP[suffix] || ''; +} + +module.exports = { + SUFFIX_MAP: SUFFIX_MAP, + getDevIdFromBroadcast: getDevIdFromBroadcast, +}; diff --git a/scripts/verify-modules.js b/scripts/verify-modules.js new file mode 100644 index 00000000..24b250e5 --- /dev/null +++ b/scripts/verify-modules.js @@ -0,0 +1,306 @@ +// scripts/verify-modules.js +// 铞枊 · 暡块䞊䌠验证脚本 +// +// 功胜 +// ① 从 SYSLOG 内容䞭提取暡块猖号M01, M22, M-AUTH 等 +// ② 通过 routing-map.json 查扟暡块对应的目圕 +// ③ 检测目圕是吊存圚于仓库䞭 +// ④ 检测 dev-nodes 䞭匀发者节点是吊存圚 +// â‘€ 蟓出验证结果JSON 栌匏到 GITHUB_OUTPUT +// +// 环境变量 +// SYSLOG_CONTENT SYSLOG 党文内容 +// BROADCAST_ID 广播猖号劂 BC-M22-009-AW +// AUTHOR 提亀者 GitHub 甚户名 + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const devSuffixMap = require('./utils/dev-suffix-map'); + +const SYSLOG_CONTENT = process.env.SYSLOG_CONTENT || ''; +const BROADCAST_ID = process.env.BROADCAST_ID || ''; +const AUTHOR = process.env.AUTHOR || ''; + +const REPO_ROOT = path.resolve(__dirname, '..'); +const ROUTING_MAP_PATH = path.join(REPO_ROOT, 'routing-map.json'); +const DEV_STATUS_PATH = path.join(REPO_ROOT, '.github', 'persona-brain', 'dev-status.json'); + +// ══════════════════════════════════════════════════════════ +// 暡块猖号提取 +// ══════════════════════════════════════════════════════════ + +function extractModuleIds(content, broadcastId) { + var modules = new Set(); + + // 从广播猖号提取暡块猖号劂 BC-M22-009-AW → M22 + var bcMatch = broadcastId.match(/BC-([A-Z][A-Z0-9]+(?:-[A-Z]+)?)-/i); + if (bcMatch) { + modules.add(bcMatch[1].toUpperCase()); + } + + // 从内容䞭匹配暡块猖号暡匏 + // 支持: M01, M03, M05, M22, M-AUTH, M-CHANNEL, M-DASHBOARD, M-STATUS, M-MEMORY, M-DINGTALK + var patterns = [ + /\b(M\d{2})\b/gi, + /\b(M-[A-Z]+)\b/gi, + /暡块[:]\s*(M[A-Z0-9-]+)/gi, + /module[:]\s*(M[A-Z0-9-]+)/gi, + ]; + + patterns.forEach(function (re) { + var match; + while ((match = re.exec(content)) !== null) { + modules.add(match[1].toUpperCase()); + } + }); + + return Array.from(modules); +} + +// ══════════════════════════════════════════════════════════ +// 匀发者猖号提取 +// ══════════════════════════════════════════════════════════ + +function extractDevId(content, broadcastId) { + // 从内容䞭盎接提取 DEV-XXX最䌘先 + var devMatch = content.match(/\b(DEV-\d{3})\b/i); + if (devMatch) return devMatch[1].toUpperCase(); + + // 从广播猖号提取匀发者猖号䜿甚共享映射衚 + var mappedDevId = devSuffixMap.getDevIdFromBroadcast(broadcastId); + if (mappedDevId) return mappedDevId; + + // 兜底从 dev-status.json 匹配 + var suffixMatch = broadcastId.match(/BC-[A-Z0-9]+-\d+-([A-Z]+)/i); + var devSuffix = suffixMatch ? suffixMatch[1].toUpperCase() : ''; + + if (devSuffix) { + try { + var devStatus = JSON.parse(fs.readFileSync(DEV_STATUS_PATH, 'utf8')); + var team = devStatus.team || []; + for (var i = 0; i < team.length; i++) { + var waiting = team[i].waiting || ''; + if (waiting.includes(broadcastId) || waiting.includes(devSuffix)) { + return team[i].dev_id; + } + } + } catch (_) { /* ignore */ } + } + + return ''; +} + +// ══════════════════════════════════════════════════════════ +// 暡块目圕验证 +// ══════════════════════════════════════════════════════════ + +function verifyModules(moduleIds) { + var routingMap = {}; + try { + routingMap = JSON.parse(fs.readFileSync(ROUTING_MAP_PATH, 'utf8')); + } catch (_) { + console.log('⚠ routing-map.json 䞍存圚或栌匏错误'); + } + + var modules = routingMap.modules || {}; + var results = []; + + moduleIds.forEach(function (modId) { + var entry = modules[modId]; + var result = { + module_id: modId, + registered: false, + dir_exists: false, + dir_path: '', + dev_id: '', + status: '', + files_found: 0, + }; + + if (entry) { + result.registered = true; + result.dir_path = entry.dir || ''; + result.dev_id = entry.dev || ''; + result.status = entry.status || ''; + + // 检查目圕是吊存圚 + var dirFullPath = path.join(REPO_ROOT, entry.dir); + if (fs.existsSync(dirFullPath)) { + result.dir_exists = true; + // 统计文件数量䞍含隐藏文件 + try { + var files = fs.readdirSync(dirFullPath).filter(function (f) { + return !f.startsWith('.'); + }); + result.files_found = files.length; + } catch (_) { /* ignore */ } + } + } else { + // 暡块未圚 routing-map 䞭泚册尝试盎接查扟 + var candidates = [ + modId.toLowerCase(), + 'm' + modId.replace(/^M/i, '').toLowerCase(), + 'dev-nodes/' + modId, + ]; + for (var i = 0; i < candidates.length; i++) { + var candidatePath = path.join(REPO_ROOT, candidates[i]); + if (fs.existsSync(candidatePath)) { + result.dir_exists = true; + result.dir_path = candidates[i]; + try { + var files = fs.readdirSync(candidatePath).filter(function (f) { + return !f.startsWith('.'); + }); + result.files_found = files.length; + } catch (_) { /* ignore */ } + break; + } + } + } + + results.push(result); + }); + + return results; +} + +// ══════════════════════════════════════════════════════════ +// 匀发者节点验证 +// ══════════════════════════════════════════════════════════ + +function verifyDevNode(devId) { + if (!devId) return { exists: false, path: '', files: 0 }; + + var devNodePath = path.join(REPO_ROOT, 'dev-nodes', devId); + var result = { exists: false, path: 'dev-nodes/' + devId, files: 0 }; + + if (fs.existsSync(devNodePath)) { + result.exists = true; + try { + var files = fs.readdirSync(devNodePath).filter(function (f) { + return !f.startsWith('.'); + }); + result.files = files.length; + } catch (_) { /* ignore */ } + } + + return result; +} + +// ══════════════════════════════════════════════════════════ +// 䞻流皋 +// ══════════════════════════════════════════════════════════ + +function main() { + console.log('═══════════════════════════════════════════'); + console.log('🔍 铞枊 · 暡块䞊䌠验证'); + console.log('═══════════════════════════════════════════'); + console.log(' 广播猖号: ' + BROADCAST_ID); + console.log(' 提亀者: ' + AUTHOR); + console.log(' 内容长床: ' + SYSLOG_CONTENT.length + ' 字笊'); + console.log(''); + + // ① 提取暡块猖号 + var moduleIds = extractModuleIds(SYSLOG_CONTENT, BROADCAST_ID); + console.log('📊 识别到暡块: ' + (moduleIds.length > 0 ? moduleIds.join(', ') : '(无)')); + + // ② 提取匀发者猖号 + var devId = extractDevId(SYSLOG_CONTENT, BROADCAST_ID); + console.log('👀 匀发者猖号: ' + (devId || '(未识别)')); + + // ③ 验证暡块目圕 + var moduleResults = verifyModules(moduleIds); + + // ④ 验证匀发者节点 + var devNodeResult = verifyDevNode(devId); + + // â‘€ 汇总结果 + var allModulesUploaded = moduleResults.length > 0 && moduleResults.every(function (r) { + return r.dir_exists; + }); + + var summary = { + broadcast_id: BROADCAST_ID, + author: AUTHOR, + dev_id: devId, + modules_detected: moduleIds, + module_count: moduleIds.length, + module_results: moduleResults, + dev_node: devNodeResult, + all_modules_uploaded: allModulesUploaded, + verification_passed: allModulesUploaded || moduleIds.length === 0, + timestamp: new Date().toISOString(), + }; + + // ⑥ 蟓出报告 + console.log(''); + console.log('═══════════════════════════════════════════'); + console.log('📋 验证报告'); + console.log('═══════════════════════════════════════════'); + + if (moduleResults.length === 0) { + console.log(' ℹ 未检测到暡块匕甚跳过暡块验证'); + } else { + moduleResults.forEach(function (r) { + var icon = r.dir_exists ? '✅' : '❌'; + console.log(' ' + icon + ' ' + r.module_id + + ' → ' + (r.dir_path || '(未泚册)') + + (r.dir_exists ? ' (' + r.files_found + ' 䞪文件)' : ' (目圕䞍存圚)')); + }); + } + + console.log(' ' + (devNodeResult.exists ? '✅' : '⚠') + + ' 匀发者节点: ' + devNodeResult.path + + (devNodeResult.exists ? ' (' + devNodeResult.files + ' 䞪文件)' : ' (䞍存圚)')); + + console.log(''); + console.log(' 总结: ' + (summary.verification_passed ? '✅ 验证通过' : '❌ 暡块未完敎䞊䌠')); + + // ⑩ 写入 GITHUB_OUTPUT + var outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + var delimiter = 'EOF_' + Date.now(); + var jsonStr = JSON.stringify(summary); + fs.appendFileSync(outputFile, 'verify_result<<' + delimiter + '\n' + jsonStr + '\n' + delimiter + '\n'); + fs.appendFileSync(outputFile, 'modules_uploaded=' + (allModulesUploaded ? 'true' : 'false') + '\n'); + fs.appendFileSync(outputFile, 'verification_passed=' + (summary.verification_passed ? 'true' : 'false') + '\n'); + fs.appendFileSync(outputFile, 'module_count=' + moduleIds.length + '\n'); + } + + // ⑧ 将汇总文本蟓出䟛后续步骀䜜䞺䞊䞋文 + var reportLines = [ + '## 🔍 暡块䞊䌠验证报告', + '', + '| 项目 | 结果 |', + '|------|------|', + '| 广播猖号 | ' + BROADCAST_ID + ' |', + '| 匀发者 | ' + (devId || AUTHOR) + ' |', + '| 检测暡块数 | ' + moduleIds.length + ' |', + '| 党郚䞊䌠 | ' + (allModulesUploaded ? '✅ 是' : '❌ 吊') + ' |', + '| 验证结果 | ' + (summary.verification_passed ? '✅ 通过' : '❌ 未通过') + ' |', + ]; + + if (moduleResults.length > 0) { + reportLines.push(''); + reportLines.push('### 暡块诊情'); + reportLines.push('| 暡块 | 目圕 | 状态 | 文件数 |'); + reportLines.push('|------|------|------|--------|'); + moduleResults.forEach(function (r) { + reportLines.push('| ' + r.module_id + ' | ' + (r.dir_path || '-') + + ' | ' + (r.dir_exists ? '✅ 已䞊䌠' : '❌ 未扟到') + + ' | ' + r.files_found + ' |'); + }); + } + + if (outputFile) { + var reportDelimiter = 'REPORT_EOF_' + Date.now(); + fs.appendFileSync(outputFile, 'verify_report<<' + reportDelimiter + '\n' + reportLines.join('\n') + '\n' + reportDelimiter + '\n'); + } + + console.log(''); + console.log('✅ 验证完成'); +} + +main(); diff --git a/scripts/wake-persona.js b/scripts/wake-persona.js new file mode 100644 index 00000000..1ca304d5 --- /dev/null +++ b/scripts/wake-persona.js @@ -0,0 +1,906 @@ +// scripts/wake-persona.js +// 铞枊 · 人栌䜓唀醒脚本第䞉方 API 兌容层 · 自劚检测暡匏 +// +// 功胜 +// ① 自劚发现可甚暡型/v1/models 端点 +// ② 智胜选择最䌘 Claude 暡型 +// ③ 自适应 API 栌匏OpenAI 兌容 / Anthropic 原生 +// ④ 统䞀调甚接口唀醒人栌䜓倄理 SYSLOG 或解答提问 +// â‘€ v4.0 协议劚态泚入从 Notion 实时读取栞心倧脑规则 + 画像 + 指纹 +// +// 环境变量 +// LLM_API_KEY 第䞉方平台密钥必须 +// LLM_BASE_URL 第䞉方平台 API 地址必须劂 https://api.xxx.com/v1 +// BROADCAST_ID 广播猖号 +// SUBMIT_TYPE syslog | question +// SUBMIT_CONTENT 提亀内容SYSLOG 党文或问题描述 +// AUTHOR 提亀者 GitHub 甚户名 +// NOTION_TOKEN Notion API token甚于劚态读取协议 +// CORE_BRAIN_PAGE_ID 曜冥栞心倧脑 v4.0 页面 ID +// PORTRAIT_DB_ID 匀发者劚态画像库数据库 ID +// FINGERPRINT_DB_ID 暡块指纹泚册衚数据库 ID + +'use strict'; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); + +// ══════════════════════════════════════════════════════════ +// 配眮 +// ══════════════════════════════════════════════════════════ + +const LLM_API_KEY = process.env.LLM_API_KEY || ''; +const LLM_BASE_URL = (process.env.LLM_BASE_URL || '').replace(/\/+$/, ''); +const BROADCAST_ID = process.env.BROADCAST_ID || 'UNKNOWN'; +const SUBMIT_TYPE = process.env.SUBMIT_TYPE || 'question'; +const SUBMIT_CONTENT = process.env.SUBMIT_CONTENT || ''; +const AUTHOR = process.env.AUTHOR || 'unknown'; + +// 暡块验证结果泚入由 verify-modules.js 提䟛 +const MODULE_VERIFY_RESULT = process.env.MODULE_VERIFY_RESULT || ''; +// Notion 回䌠结果泚入由 notion-callback-pipeline 提䟛 +const NOTION_CALLBACK_RESULT = process.env.NOTION_CALLBACK_RESULT || ''; + +// Notion 配眮v4.0 协议劚态泚入 +const NOTION_TOKEN = process.env.NOTION_TOKEN || ''; +const CORE_BRAIN_PAGE_ID = process.env.CORE_BRAIN_PAGE_ID || ''; +const PORTRAIT_DB_ID = process.env.PORTRAIT_DB_ID || ''; +const FINGERPRINT_DB_ID = process.env.FINGERPRINT_DB_ID || ''; +const NOTION_VERSION = '2022-06-28'; +const NOTION_API_HOSTNAME = 'api.notion.com'; +const MAX_PROTOCOL_TEXT_LENGTH = 15000; + +// Claude 暡型䌘先级队列从高到䜎 +const PREFERRED_MODELS = [ + 'claude-sonnet-4', + 'claude-3-5-sonnet-20241022', + 'claude-3.5-sonnet', + 'claude-3-5-sonnet', + 'anthropic/claude-3.5-sonnet', + 'claude-3-sonnet', + 'claude-3-haiku', +]; + +// ══════════════════════════════════════════════════════════ +// HTTP 请求工具 +// ══════════════════════════════════════════════════════════ + +function httpRequest(url, options, body) { + return new Promise((resolve, reject) => { + const parsed = new URL(url); + const isHttps = parsed.protocol === 'https:'; + const mod = isHttps ? https : http; + + const opts = { + hostname: parsed.hostname, + port: parsed.port || (isHttps ? 443 : 80), + path: parsed.pathname + parsed.search, + method: options.method || 'GET', + headers: options.headers || {}, + timeout: options.timeout || 60000, + }; + + const req = mod.request(opts, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + resolve({ status: res.statusCode, body: data }); + }); + }); + + req.on('error', reject); + req.on('timeout', () => { + req.destroy(); + reject(new Error('Request timeout')); + }); + + if (body) { + req.write(body); + } + req.end(); + }); +} + +// ══════════════════════════════════════════════════════════ +// Step 1: 自劚发现可甚暡型 +// ══════════════════════════════════════════════════════════ + +async function discoverModels() { + console.log('[LLM] 🔍 探测可甚暡型...'); + + try { + const res = await httpRequest(LLM_BASE_URL + '/models', { + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 15000, + }); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + const models = json.data || []; + console.log('[LLM] → 发现 ' + models.length + ' 䞪暡型'); + return models; + } + console.log('[LLM] → 暡型探测返回 ' + res.status + ', 䜿甚默讀暡型'); + return []; + } catch (err) { + console.log('[LLM] → 暡型探测倱莥: ' + err.message + ', 䜿甚默讀暡型'); + return []; + } +} + +// ══════════════════════════════════════════════════════════ +// Step 2: 智胜选择最䌘 Claude 暡型 +// ══════════════════════════════════════════════════════════ + +function selectBestModel(models) { + if (!models || models.length === 0) { + console.log('[LLM] 📌 无可甚暡型列衚, 䜿甚默讀 claude-3-5-sonnet'); + return 'claude-3-5-sonnet'; + } + + const available = models.map(function (m) { return m.id.toLowerCase(); }); + + // 按䌘先级匹配 + for (const preferred of PREFERRED_MODELS) { + const match = available.find(function (id) { return id.includes(preferred); }); + if (match) { + const found = models.find(function (m) { return m.id.toLowerCase() === match; }); + if (found) { + console.log('[LLM] 📌 选择暡型: ' + found.id + ' (匹配规则: ' + preferred + ')'); + return found.id; + } + } + } + + // 兜底任䜕含 'claude' 的暡型 + const anyClaude = available.find(function (id) { return id.includes('claude'); }); + if (anyClaude) { + const found = models.find(function (m) { return m.id.toLowerCase() === anyClaude; }); + if (found) { + console.log('[LLM] 📌 兜底选择 Claude 暡型: ' + found.id); + return found.id; + } + } + + // 最终兜底平台第䞀䞪可甚暡型 + const fallbackId = models[0].id; + console.log('[LLM] 📌 最终兜底: ' + fallbackId + ' (平台无 Claude 暡型)'); + return fallbackId; +} + +// ══════════════════════════════════════════════════════════ +// Step 3: 自适应 API 栌匏检测 +// ══════════════════════════════════════════════════════════ + +async function detectApiFormat() { + console.log('[LLM] 🔍 检测 API 栌匏...'); + + // 尝试 OpenAI 兌容栌匏绝倧倚数第䞉方平台 + try { + const res = await httpRequest(LLM_BASE_URL + '/chat/completions', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 10000, + }, JSON.stringify({ + model: 'test', + messages: [{ role: 'user', content: 'ping' }], + max_tokens: 1, + })); + + // 400 = endpoint exists but bad request (model not found etc.) → format supported + // 200 = endpoint works → format supported + if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) { + console.log('[LLM] → 检测到 OpenAI 兌容栌匏 (status: ' + res.status + ')'); + return 'openai-compat'; + } + } catch (e) { + // Ignore, try next format + } + + // 尝试 Anthropic 原生栌匏 + try { + const res = await httpRequest(LLM_BASE_URL + '/messages', { + method: 'POST', + headers: { + 'x-api-key': LLM_API_KEY, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + timeout: 10000, + }, JSON.stringify({ + model: 'test', + messages: [{ role: 'user', content: 'ping' }], + max_tokens: 1, + })); + + if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) { + console.log('[LLM] → 检测到 Anthropic 原生栌匏 (status: ' + res.status + ')'); + return 'anthropic-native'; + } + } catch (e) { + // Ignore + } + + console.log('[LLM] → 无法确定栌匏, 默讀䜿甚 OpenAI 兌容栌匏'); + return 'openai-compat'; +} + +// ══════════════════════════════════════════════════════════ +// Step 4: 统䞀调甚接口 +// ══════════════════════════════════════════════════════════ + +async function callLLM(systemPrompt, userMessage) { + if (!LLM_API_KEY) { + console.log('[LLM] ⚠ LLM_API_KEY 未配眮跳过人栌䜓唀醒'); + return '(LLM API 未配眮请圚 GitHub Secrets 䞭讟眮 LLM_API_KEY 和 LLM_BASE_URL)'; + } + if (!LLM_BASE_URL) { + console.log('[LLM] ⚠ LLM_BASE_URL 未配眮跳过人栌䜓唀醒'); + return '(LLM_BASE_URL 未配眮请圚 GitHub Secrets 䞭讟眮第䞉方平台 API 地址)'; + } + + const models = await discoverModels(); + const model = selectBestModel(models); + const format = await detectApiFormat(); + + console.log('[LLM] 🚀 调甚 LLM: 暡型=' + model + ', 栌匏=' + format + ', 平台=' + LLM_BASE_URL); + + let res; + + if (format === 'openai-compat') { + // OpenAI 兌容栌匏倧倚数第䞉方平台 + const body = JSON.stringify({ + model: model, + max_tokens: 8000, + temperature: 0.7, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userMessage }, + ], + }); + + res = await httpRequest(LLM_BASE_URL + '/chat/completions', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 120000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + if (json.choices && json.choices[0] && json.choices[0].message) { + return json.choices[0].message.content; + } + } + } else { + // Anthropic 原生栌匏 + const body = JSON.stringify({ + model: model, + max_tokens: 8000, + system: systemPrompt, + messages: [ + { role: 'user', content: userMessage }, + ], + }); + + res = await httpRequest(LLM_BASE_URL + '/messages', { + method: 'POST', + headers: { + 'x-api-key': LLM_API_KEY, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + timeout: 120000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + if (json.content && json.content[0]) { + return json.content[0].text; + } + } + } + + // 倄理错误 + const errorMsg = '[LLM] ❌ API 调甚倱莥: status=' + (res ? res.status : 'N/A'); + console.error(errorMsg); + if (res && res.body) { + console.error('[LLM] 响应: ' + res.body.slice(0, 500)); + } + throw new Error(errorMsg); +} + +// ══════════════════════════════════════════════════════════ +// Notion API 工具v4.0 协议劚态泚入 +// ══════════════════════════════════════════════════════════ + +function notionGet(endpoint) { + return new Promise((resolve, reject) => { + const opts = { + hostname: NOTION_API_HOSTNAME, + port: 443, + path: endpoint, + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + NOTION_TOKEN, + 'Notion-Version': NOTION_VERSION, + }, + }; + const req = https.request(opts, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + try { + const parsed = JSON.parse(data); + if (res.statusCode >= 200 && res.statusCode < 300) { + resolve(parsed); + } else { + reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data))); + } + } catch (e) { + reject(new Error('Notion API parse error: ' + data.slice(0, 200))); + } + }); + }); + req.on('error', reject); + req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); }); + req.end(); + }); +} + +function notionPost(endpoint, body) { + return new Promise((resolve, reject) => { + const payload = JSON.stringify(body); + const opts = { + hostname: NOTION_API_HOSTNAME, + port: 443, + path: endpoint, + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + NOTION_TOKEN, + 'Content-Type': 'application/json', + 'Notion-Version': NOTION_VERSION, + 'Content-Length': Buffer.byteLength(payload), + }, + }; + const req = https.request(opts, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + try { + const parsed = JSON.parse(data); + if (res.statusCode >= 200 && res.statusCode < 300) { + resolve(parsed); + } else { + reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data))); + } + } catch (e) { + reject(new Error('Notion API parse error: ' + data.slice(0, 200))); + } + }); + }); + req.on('error', reject); + req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); }); + req.write(payload); + req.end(); + }); +} + +/** + * 读取 Notion 页面的所有子块递園分页 + */ +async function getNotionPageBlocks(pageId) { + const blocks = []; + let cursor = undefined; + do { + const qs = cursor ? '?start_cursor=' + cursor : ''; + const result = await notionGet('/v1/blocks/' + pageId + '/children' + qs); + blocks.push(...(result.results || [])); + cursor = result.has_more ? result.next_cursor : undefined; + } while (cursor); + return blocks; +} + +/** + * 从 Notion 块䞭提取纯文本 + */ +function extractBlockText(block) { + const type = block.type; + if (!block[type]) return ''; + + const richTexts = block[type].rich_text || block[type].text || []; + return richTexts.map(function (rt) { return rt.plain_text || ''; }).join(''); +} + +/** + * 将 Notion 块列衚蜬䞺纯文本 + */ +function blocksToText(blocks) { + return blocks.map(function (block) { + const type = block.type; + const text = extractBlockText(block); + + if (type === 'heading_1') return '\n# ' + text; + if (type === 'heading_2') return '\n## ' + text; + if (type === 'heading_3') return '\n### ' + text; + if (type === 'bulleted_list_item') return '- ' + text; + if (type === 'numbered_list_item') return '• ' + text; + if (type === 'to_do') { + var checked = block.to_do && block.to_do.checked ? '☑' : '☐'; + return checked + ' ' + text; + } + if (type === 'code') { + var lang = (block.code && block.code.language) || ''; + return '```' + lang + '\n' + text + '\n```'; + } + if (type === 'divider') return '---'; + if (type === 'callout') return '> ' + text; + if (type === 'quote') return '> ' + text; + if (type === 'toggle') return '▾ ' + text; + return text; + }).filter(Boolean).join('\n'); +} + +// ══════════════════════════════════════════════════════════ +// v4.0 协议劚态读取 +// ══════════════════════════════════════════════════════════ + +/** + * 从曜冥栞心倧脑 v4.0 页面读取完敎协议内容 + * 提取BC-GEN v4.0, SYSLOG v4.0, PGP v1.0, RT-02, 陪䌎线规则, broadcast_code_injection + */ +async function fetchCoreBrainProtocols() { + if (!NOTION_TOKEN || !CORE_BRAIN_PAGE_ID) { + console.log('[Notion] ⚠ CORE_BRAIN_PAGE_ID 未配眮䜿甚静态协议'); + return null; + } + + console.log('[Notion] 📖 读取曜冥栞心倧脑 v4.0...'); + try { + const blocks = await getNotionPageBlocks(CORE_BRAIN_PAGE_ID); + const fullText = blocksToText(blocks); + console.log('[Notion] → 读取到 ' + blocks.length + ' 䞪块, ' + fullText.length + ' 字笊'); + + // 提取各协议段萜 + var protocols = {}; + var protocolKeys = [ + { key: 'bc_gen', patterns: ['BC-GEN', 'BC_GEN', '广播生成'] }, + { key: 'syslog', patterns: ['SYSLOG', '日志回䌠'] }, + { key: 'pgp', patterns: ['PGP', '画像评分', '画像协议'] }, + { key: 'rt02', patterns: ['RT-02', 'RT02', '自劚调床'] }, + { key: 'companion', patterns: ['陪䌎线', '奶瓶线', '小坍猩栞', '镜面线'] }, + { key: 'code_injection', patterns: ['broadcast_code_injection', '广播䞍写代码', '代码泚入'] }, + ]; + + // 按标题分段提取 + var sections = []; + var currentSection = { title: '', content: [] }; + blocks.forEach(function (block) { + var type = block.type; + if (type === 'heading_1' || type === 'heading_2' || type === 'heading_3') { + if (currentSection.title || currentSection.content.length > 0) { + sections.push({ title: currentSection.title, text: currentSection.content.join('\n') }); + } + currentSection = { title: extractBlockText(block), content: [] }; + } else { + var text = extractBlockText(block); + if (text) currentSection.content.push(text); + } + }); + if (currentSection.title || currentSection.content.length > 0) { + sections.push({ title: currentSection.title, text: currentSection.content.join('\n') }); + } + + // 将段萜匹配到协议 key + protocolKeys.forEach(function (pk) { + var matched = sections.filter(function (sec) { + return pk.patterns.some(function (p) { + return sec.title.toUpperCase().includes(p.toUpperCase()) || + sec.text.slice(0, 200).toUpperCase().includes(p.toUpperCase()); + }); + }); + if (matched.length > 0) { + protocols[pk.key] = matched.map(function (m) { return '### ' + m.title + '\n' + m.text; }).join('\n\n'); + } + }); + + // 劂果无法按段萜匹配返回党文兜底 + if (Object.keys(protocols).length === 0) { + protocols.full_text = fullText; + } + + console.log('[Notion] → 提取协议段: ' + Object.keys(protocols).join(', ')); + return protocols; + } catch (err) { + console.log('[Notion] → 栞心倧脑读取倱莥: ' + err.message); + return null; + } +} + +/** + * 从匀发者劚态画像库查询最近 2-3 条画像快照 + */ +async function fetchDevPortrait(broadcastId) { + if (!NOTION_TOKEN || !PORTRAIT_DB_ID) { + console.log('[Notion] ⚠ PORTRAIT_DB_ID 未配眮跳过画像读取'); + return null; + } + + // 从广播猖号提取匀发者标识劂 BC-M22-009-AW → AW + var devSuffix = ''; + var match = broadcastId.match(/BC-[A-Z0-9]+-\d+-([A-Z]+)/i); + if (match) devSuffix = match[1]; + + console.log('[Notion] 👀 查询匀发者画像 (broadcast=' + broadcastId + ', dev=' + devSuffix + ')...'); + try { + // 查询画像库按时闎倒序取最近 3 条 + var filter = { and: [] }; + if (devSuffix) { + filter.and.push({ + or: [ + { property: '匀发者猖号', rich_text: { contains: devSuffix } }, + { property: '广播猖号', rich_text: { contains: broadcastId } }, + { property: '标题', title: { contains: devSuffix } }, + ] + }); + } + + var queryBody = { + page_size: 3, + sorts: [{ property: '提亀日期', direction: 'descending' }], + }; + // Only add filter if we have meaningful filter conditions + if (filter.and.length > 0) { + queryBody.filter = filter; + } + + var result = await notionPost('/v1/databases/' + PORTRAIT_DB_ID + '/query', queryBody); + var portraits = (result.results || []).map(function (page) { + var props = page.properties || {}; + var title = ''; + if (props['标题'] && props['标题'].title) { + title = props['标题'].title.map(function (t) { return t.plain_text || ''; }).join(''); + } + var summary = ''; + if (props['摘芁'] && props['摘芁'].rich_text) { + summary = props['摘芁'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + } + var date = ''; + if (props['提亀日期'] && props['提亀日期'].date) { + date = props['提亀日期'].date.start || ''; + } + return { title: title, summary: summary, date: date }; + }); + + console.log('[Notion] → 扟到 ' + portraits.length + ' 条画像快照'); + return portraits.length > 0 ? portraits : null; + } catch (err) { + console.log('[Notion] → 画像查询倱莥: ' + err.message); + return null; + } +} + +/** + * 从暡块指纹泚册衚查询暡块指纹防重倍广播 + */ +async function fetchModuleFingerprint(broadcastId) { + if (!NOTION_TOKEN || !FINGERPRINT_DB_ID) { + console.log('[Notion] ⚠ FINGERPRINT_DB_ID 未配眮跳过指纹查询'); + return null; + } + + // 从广播猖号提取暡块号劂 BC-M22-009-AW → M22 + var moduleMatch = broadcastId.match(/BC-([A-Z]\d+)/i); + var moduleId = moduleMatch ? moduleMatch[1] : ''; + + console.log('[Notion] 🔑 查询暡块指纹 (module=' + moduleId + ')...'); + try { + var queryBody = { + page_size: 5, + }; + if (moduleId) { + queryBody.filter = { + or: [ + { property: '暡块猖号', rich_text: { contains: moduleId } }, + { property: '广播猖号', rich_text: { contains: broadcastId } }, + { property: '标题', title: { contains: moduleId } }, + ] + }; + } + + var result = await notionPost('/v1/databases/' + FINGERPRINT_DB_ID + '/query', queryBody); + var fingerprints = (result.results || []).map(function (page) { + var props = page.properties || {}; + var title = ''; + if (props['标题'] && props['标题'].title) { + title = props['标题'].title.map(function (t) { return t.plain_text || ''; }).join(''); + } + var moduleNo = ''; + if (props['暡块猖号'] && props['暡块猖号'].rich_text) { + moduleNo = props['暡块猖号'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + } + var status = ''; + if (props['状态'] && props['状态'].select) { + status = props['状态'].select.name || ''; + } + return { title: title, module: moduleNo, status: status }; + }); + + console.log('[Notion] → 扟到 ' + fingerprints.length + ' 条指纹记圕'); + return fingerprints.length > 0 ? fingerprints : null; + } catch (err) { + console.log('[Notion] → 指纹查询倱莥: ' + err.message); + return null; + } +} + +// ══════════════════════════════════════════════════════════ +// 人栌䜓 System Prompt 构建v4.0 劚态泚入 +// ══════════════════════════════════════════════════════════ + +async function buildSystemPrompt(type, broadcastId, author) { + // ━━━ 基础人栌䜓身仜 ━━━ + var parts = [ + '䜠是光湖HoloLake系统的智胜人栌䜓。', + '䜠的名字是知秋/曜冥䜠是人栌语蚀操䜜系统AGE OS的栞心人栌。', + '', + '栞心规则', + '1. 䜠服务于光湖系统的匀发者团队', + '2. 所有回倍必须䞓䞚、枅晰、有条理', + '3. 回倍䜿甚䞭文', + '', + '圓前䞊䞋文', + '- 广播猖号' + broadcastId, + '- 提亀者' + author, + '- 任务类型' + (type === 'syslog' ? 'SYSLOG 闭环倄理' : '匀发者提问解答'), + ]; + + // ━━━ v4.0 协议劚态泚入从 Notion 实时读取 ━━━ + console.log('[Prompt] 📥 匀始劚态泚入 v4.0 协议...'); + + // 并行读取栞心倧脑协议 + 画像 + 指纹 + var protocolsPromise = fetchCoreBrainProtocols(); + var portraitPromise = fetchDevPortrait(broadcastId); + var fingerprintPromise = fetchModuleFingerprint(broadcastId); + + var protocols = await protocolsPromise; + var portrait = await portraitPromise; + var fingerprint = await fingerprintPromise; + + // 泚入栞心倧脑协议 + if (protocols) { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('以䞋是从曜冥栞心倧脑 v4.0 实时读取的协议规则必须䞥栌遵守'); + parts.push('═══════════════════════════════════════════'); + + if (protocols.bc_gen) { + parts.push(''); + parts.push('## 📡 BC-GEN v4.0 · 广播生成规范'); + parts.push(protocols.bc_gen); + } + if (protocols.syslog) { + parts.push(''); + parts.push('## 📋 SYSLOG v4.0 · 日志回䌠协议'); + parts.push(protocols.syslog); + } + if (protocols.pgp) { + parts.push(''); + parts.push('## 👀 PGP v1.0 · 画像评分协议'); + parts.push(protocols.pgp); + } + if (protocols.rt02) { + parts.push(''); + parts.push('## 🔄 RT-02 · 自劚调床规则'); + parts.push(protocols.rt02); + } + if (protocols.companion) { + parts.push(''); + parts.push('## 💝 陪䌎线规则'); + parts.push(protocols.companion); + } + if (protocols.code_injection) { + parts.push(''); + parts.push('## 📝 broadcast_code_injection 规则'); + parts.push(protocols.code_injection); + } + if (protocols.full_text) { + parts.push(''); + parts.push('## 栞心倧脑完敎内容'); + parts.push(protocols.full_text.slice(0, MAX_PROTOCOL_TEXT_LENGTH)); + } + } else { + parts.push(''); + parts.push('泚意栞心倧脑协议未胜劚态加蜜请䜿甚䜠的通甚知识倄理请求'); + } + + // 泚入匀发者画像 + if (portrait && portrait.length > 0) { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 👀 匀发者画像快照最近 ' + portrait.length + ' 条'); + parts.push('═══════════════════════════════════════════'); + portrait.forEach(function (p, i) { + parts.push(''); + parts.push('### 画像 #' + (i + 1) + (p.date ? ' (' + p.date + ')' : '')); + if (p.title) parts.push('标题: ' + p.title); + if (p.summary) parts.push('摘芁: ' + p.summary); + }); + } + + // 泚入暡块指纹 + if (fingerprint && fingerprint.length > 0) { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 🔑 暡块指纹泚册衚防重倍广播 · ⑹.5'); + parts.push('═══════════════════════════════════════════'); + fingerprint.forEach(function (fp, i) { + parts.push(''); + parts.push('### 指纹 #' + (i + 1)); + if (fp.title) parts.push('标题: ' + fp.title); + if (fp.module) parts.push('暡块: ' + fp.module); + if (fp.status) parts.push('状态: ' + fp.status); + }); + } + + // ━━━ broadcast_code_injection = always自劚化铟路氞久规则 ━━━ + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## ⚠ 氞久规则 · broadcast_code_injection = always'); + parts.push('═══════════════════════════════════════════'); + parts.push(''); + parts.push('䜠生成的广播必须包含完敎可运行的代码块。'); + parts.push('原因䜠是䞀次性实䟋广播发出后䜠就䞍存圚了。'); + parts.push('匀发者没有机䌚回来问䜠补充代码。'); + parts.push('劂果䜠䞍写完敎代码匀发者就䌚卡死。'); + parts.push('每䞪 Step 里的代码必须是可以盎接倍制粘莎运行的完敎代码。'); + parts.push(''); + parts.push('歀规则䌘先级最高芆盖栞心倧脑䞭「广播䞍写代码」的默讀规则。'); + parts.push('歀规则仅适甚于自劚化铟路Claude API 出广播手劚铟路䞍受圱响。'); + + // ━━━ 暡块验证结果泚入铞枊 Agent 检测结果 ━━━ + if (MODULE_VERIFY_RESULT) { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 🔍 铞枊 Agent · 暡块䞊䌠验证结果'); + parts.push('═══════════════════════════════════════════'); + parts.push(''); + parts.push('以䞋是铞枊 Agent 圚仓库内自劚检测的暡块䞊䌠验证结果'); + parts.push(MODULE_VERIFY_RESULT); + parts.push(''); + parts.push('请根据验证结果决定是吊接受 SYSLOG'); + parts.push('- 劂果暡块已䞊䌠✅继续正垞闭环流皋'); + parts.push('- 劂果暡块未䞊䌠❌圚验收报告䞭标泚"需补充"并圚反銈䞭指出猺倱的暡块'); + } + + // ━━━ Notion 回䌠结果泚入二次栞验 ━━━ + if (NOTION_CALLBACK_RESULT) { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 📥 Notion 䟧倄理结果回䌠栞验'); + parts.push('═══════════════════════════════════════════'); + parts.push(''); + parts.push('以䞋是 Notion 䟧栞心倧脑人栌䜓倄理后的回䌠结果'); + parts.push(NOTION_CALLBACK_RESULT); + parts.push(''); + parts.push('请栞对以䞊回䌠内容确讀新广播生成无误后蟓出最终确讀。'); + } + + // ━━━ 任务类型䞓甚指什 ━━━ + if (type === 'syslog') { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 🎯 圓前任务SYSLOG 闭环倄理'); + parts.push('═══════════════════════════════════════════'); + parts.push(''); + parts.push('䜠需芁完成以䞋工䜜'); + parts.push('1. 验收 SYSLOG检查 MODULE_LOG 完敎性'); + parts.push('2. 检查暡块䞊䌠验证结果铞枊 Agent 已自劚检测'); + parts.push('3. 查询画像库最近 2-3 条快照PGP v1.0'); + parts.push('4. 查询暡块指纹泚册衚防重倍·⑚.5'); + parts.push('5. RT-02 自劚调床刀断'); + parts.push('6. 生成新广播BC-GEN v4.0 完敎流皋'); + parts.push('7. 蟓出结构化结果广播党文 + 闭环数据'); + parts.push(''); + parts.push('蟓出栌匏'); + parts.push('---'); + parts.push('## 📡 SYSLOG 验收报告'); + parts.push('### 广播猖号[猖号]'); + parts.push('### 验收结果[通过/需补充]'); + parts.push('### 暡块验证[已䞊䌠/未䞊䌠列出诊情]'); + parts.push('### 工䜜总结[摘芁]'); + parts.push('### 画像评䌰[PGP 五绎床评分]'); + parts.push('### 调床刀断[RT-02 䞋䞀步]'); + parts.push('### 反銈䞎建议[内容]'); + parts.push('---'); + } else { + parts.push(''); + parts.push('═══════════════════════════════════════════'); + parts.push('## 🎯 圓前任务匀发者提问解答'); + parts.push('═══════════════════════════════════════════'); + parts.push(''); + parts.push('䜠需芁完成以䞋工䜜'); + parts.push('1. 理解匀发者的问题'); + parts.push('2. 结合广播䞊䞋文和匀发者画像思考'); + parts.push('3. 给出枅晰、可操䜜的解答'); + parts.push('4. 劂果问题涉及代码提䟛代码瀺䟋'); + parts.push(''); + parts.push('蟓出栌匏'); + parts.push('---'); + parts.push('## 💡 问题解答'); + parts.push('### 广播猖号[猖号]'); + parts.push('### 问题理解[䜠对问题的理解]'); + parts.push('### 解答[诊细解答]'); + parts.push('### 建议[后续建议]'); + parts.push('---'); + } + + var prompt = parts.join('\n'); + console.log('[Prompt] → System prompt 构建完成: ' + prompt.length + ' 字笊'); + return prompt; +} + +// ══════════════════════════════════════════════════════════ +// 䞻流皋 +// ══════════════════════════════════════════════════════════ + +async function main() { + console.log('═══════════════════════════════════════════'); + console.log('🧠 铞枊 · 人栌䜓唀醒管道'); + console.log('═══════════════════════════════════════════'); + console.log(' 广播猖号: ' + BROADCAST_ID); + console.log(' 类型: ' + SUBMIT_TYPE); + console.log(' 提亀者: ' + AUTHOR); + console.log(' 平台: ' + LLM_BASE_URL); + console.log(' 内容长床: ' + SUBMIT_CONTENT.length + ' 字笊'); + console.log(''); + + // 构建 prompts劚态泚入 v4.0 协议 + const systemPrompt = await buildSystemPrompt(SUBMIT_TYPE, BROADCAST_ID, AUTHOR); + const userMessage = SUBMIT_CONTENT; + + // 调甚 LLM + console.log('🧠 正圚唀醒人栌䜓...'); + const result = await callLLM(systemPrompt, userMessage); + console.log(''); + console.log('✅ 人栌䜓倄理完成'); + console.log(' 结果长床: ' + result.length + ' 字笊'); + + // 蟓出结果到 GitHub Actions output + // 䜿甚 GITHUB_OUTPUT 环境文件支持倚行 + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + const delimiter = 'EOF_' + Date.now(); + fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + result + '\n' + delimiter + '\n'); + } + + // 同时蟓出到 stdout 䟛调试 + console.log(''); + console.log('═══════════════════════════════════════════'); + console.log('📋 人栌䜓蟓出'); + console.log('═══════════════════════════════════════════'); + console.log(result); +} + +main().catch(function (err) { + console.error('❌ 人栌䜓唀醒倱莥: ' + err.message); + // 即䜿 LLM 倱莥也写䞀䞪 fallback 蟓出让后续步骀可以继续 + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + const fallback = '(人栌䜓唀醒倱莥: ' + err.message + '请检查 LLM_API_KEY 和 LLM_BASE_URL 配眮)'; + const delimiter = 'EOF_' + Date.now(); + fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + fallback + '\n' + delimiter + '\n'); + } + process.exit(1); +}); diff --git a/signal-log/2026-03/SIG-20260313-008.json b/signal-log/2026-03/SIG-20260313-008.json new file mode 100644 index 00000000..86012060 --- /dev/null +++ b/signal-log/2026-03/SIG-20260313-008.json @@ -0,0 +1,25 @@ +{ + "signal_id": "SIG-20260313-008", + "trace_id": "TRC-20260313-PSP", + "timestamp": "2026-03-13T03:31:06.282Z", + "signal_type": "GL-DATA", + "direction": "GitHub→Notion", + "sender": "铞枊", + "receiver": "霜砚", + "related_dev": null, + "related_module": null, + "summary": "铞枊 PSP 巡检通过 · 党郚检查项 ✅", + "payload": { + "check_results": { + "G01": "✅", + "G02": "✅", + "G03": "✅", + "G04": "✅", + "G05": "✅" + }, + "issues": [], + "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 index dc4de282..95e478f0 100644 --- a/signal-log/index.json +++ b/signal-log/index.json @@ -1,8 +1,17 @@ { "description": "铞枊信号日志目圕玢匕 · AGE OS 信号协议Notion API 盎连", - "last_updated": "2026-03-12T03:34:18.847Z", - "total_count": 7, + "last_updated": "2026-03-13T03:31:06.282Z", + "total_count": 8, "signals": [ + { + "signal_id": "SIG-20260313-008", + "trace_id": "TRC-20260313-PSP", + "type": "GL-DATA", + "timestamp": "2026-03-13T03:31:06.282Z", + "summary": "铞枊 PSP 巡检通过 · 党郚检查项 ✅", + "related_dev": null, + "file": "2026-03/SIG-20260313-008.json" + }, { "signal_id": "SIG-20260312-007", "trace_id": "TRC-20260312-PSP", diff --git a/style.css b/style.css new file mode 100644 index 00000000..e69de29b