This commit is contained in:
zhizhi200271 2026-03-13 18:23:35 +08:00
commit 3d068f9506
41 changed files with 5650 additions and 93 deletions

View File

@ -0,0 +1,51 @@
title: "BC-XXX-XXXSYSLOG"
labels: []
body:
- type: markdown
attributes:
value: |
## 📡 SYSLOG 自助提交 / 广播提问
> 做完了?把日志粘贴进去,填上邮箱,点提交。新广播会发到你邮箱里。
>
> 有问题?把问题粘贴进去,填上邮箱,点提交。解答会发到你邮箱里。
**标题格式**`BC-XXX-XXXSYSLOG` 或 `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

View File

@ -0,0 +1,54 @@
name: "📡 提交系统日志 / 提问"
description: "做完了?提交你的 SYSLOG。有问题在这里提问。"
title: "BC-XXX-XXXSYSLOG"
labels: ["syslog", "pending"]
assignees: []
body:
- type: markdown
attributes:
value: |
## 📡 SYSLOG 自助提交 / 广播提问
> 做完了?把日志粘贴进去,填上邮箱,点提交。新广播会发到你邮箱里。
>
> 有问题?把问题粘贴进去,填上邮箱,点提交。解答会发到你邮箱里。
**标题格式**`BC-XXX-XXXSYSLOG` 或 `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

View File

@ -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",

View File

@ -1,7 +1,7 @@
# 冰朔主控神经系统 · 核心主控大脑 v1.0
> 本文件为冰朔主控神经系统的总控脑文件。
> 最后编译时间2026-03-13T02:05:55.612Z
> 最后编译时间2026-03-13T06:52:24.834Z
---
@ -56,7 +56,7 @@
### 仓库统计
- 功能模块10 个
- Workflow35
- Workflow40
---
@ -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 | 前后端结构存在,端到端对话链路待验证 |

View File

@ -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",

View File

@ -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"
}
]
}

View File

@ -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"
],

View File

@ -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`

View File

@ -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",

View File

@ -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: 📡 推送广播到 GitHubPhase 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
? '<span style=\"background:#22c55e;color:#fff;padding:4px 12px;border-radius:4px;font-weight:bold\">✅ 已核验通过</span>'
: '<span style=\"background:#f59e0b;color:#fff;padding:4px 12px;border-radius:4px;font-weight:bold\">⚠️ 需修订</span>';
const resultHtml = result
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>');
const html = [
'<div style=\"font-family:sans-serif;max-width:700px;margin:0 auto;padding:24px;background:#0f172a;color:#e2e8f0;border-radius:12px\">',
'<div style=\"text-align:center;padding:16px 0;border-bottom:1px solid #334155\">',
'<h2 style=\"color:#60a5fa;margin:0\">🌊 光湖系统 · Notion 回传核验通知</h2>',
'<p style=\"color:#94a3b8;font-size:14px;margin:8px 0 0\">HoloLake · 铸渊核心大脑二次核验完成</p>',
'</div>',
'<div style=\"padding:20px 0\">',
'<p style=\"color:#22d3ee;font-weight:bold\">📡 ' + broadcastId + '</p>',
'<p style=\"margin:12px 0\">' + statusBadge + '</p>',
'<div style=\"background:#1e293b;padding:16px;border-radius:8px;margin:12px 0;line-height:1.8\">' + resultHtml + '</div>',
'</div>',
'<div style=\"border-top:1px solid #334155;padding:16px 0;text-align:center\">',
'<a href=\"https://github.com/qinfendebingshuo/guanghulab\" style=\"display:inline-block;padding:8px 20px;background:linear-gradient(135deg,#3b82f6,#22d3ee);color:#fff;text-decoration:none;border-radius:6px;font-weight:600\">↩ 回到仓库继续开发</a>',
'<p style=\"color:#64748b;font-size:12px;margin:12px 0 0\">🌀 铸渊 · 代码守护人格体 · Notion↔GitHub 闭环确认</p>',
'</div>',
'</div>'
].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)"

47
.github/workflows/notion-heartbeat.yml vendored Normal file
View File

@ -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

248
.github/workflows/persona-invoke.yml vendored Normal file
View File

@ -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();
"

View File

@ -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 页面 IDv4.0 协议动态注入)
# PORTRAIT_DB_ID 开发者动态画像库数据库 IDv4.0 协议动态注入)
# FINGERPRINT_DB_ID 模块指纹注册表数据库 IDv4.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: 📡 推送广播到 GitHubPhase 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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>');
const html = [
'<div style=\"font-family:sans-serif;max-width:700px;margin:0 auto;padding:24px;background:#0f172a;color:#e2e8f0;border-radius:12px\">',
'<div style=\"text-align:center;padding:16px 0;border-bottom:1px solid #334155\">',
'<h2 style=\"color:#60a5fa;margin:0\">🌊 光湖系统 · 自动通知</h2>',
'<p style=\"color:#94a3b8;font-size:14px;margin:8px 0 0\">HoloLake · 人格语言操作系统</p>',
'</div>',
'<div style=\"padding:20px 0\">',
'<p style=\"color:#22d3ee;font-weight:bold\">📡 ' + broadcastId + '</p>',
'<div style=\"background:#1e293b;padding:16px;border-radius:8px;margin:12px 0;line-height:1.8\">' + resultHtml + '</div>',
'</div>',
'<div style=\"border-top:1px solid #334155;padding:16px 0;text-align:center\">',
'<a href=\"https://github.com/qinfendebingshuo/guanghulab\" style=\"display:inline-block;padding:8px 20px;background:linear-gradient(135deg,#3b82f6,#22d3ee);color:#fff;text-decoration:none;border-radius:6px;font-weight:600\">↩ 回到仓库继续开发</a>',
'<p style=\"color:#64748b;font-size:12px;margin:12px 0 0\">🌀 铸渊 · 代码守护人格体 · 自动发送</p>',
'</div>',
'</div>'
].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);
}

View File

@ -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 页面 IDv4.0 协议动态注入)
# PORTRAIT_DB_ID 开发者动态画像库数据库 IDv4.0 协议动态注入)
# FINGERPRINT_DB_ID 模块指纹注册表数据库 IDv4.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: 📡 推送广播到 GitHubPhase 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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>');
const html = [
'<div style=\"font-family:sans-serif;max-width:700px;margin:0 auto;padding:24px;background:#0f172a;color:#e2e8f0;border-radius:12px\">',
'<div style=\"text-align:center;padding:16px 0;border-bottom:1px solid #334155\">',
'<h2 style=\"color:#60a5fa;margin:0\">🌊 光湖系统 · 自动通知</h2>',
'<p style=\"color:#94a3b8;font-size:14px;margin:8px 0 0\">HoloLake · 人格语言操作系统</p>',
'</div>',
'<div style=\"padding:20px 0\">',
'<p style=\"color:#22d3ee;font-weight:bold\">📡 ' + broadcastId + '</p>',
'<div style=\"background:#1e293b;padding:16px;border-radius:8px;margin:12px 0;line-height:1.8\">' + resultHtml + '</div>',
'</div>',
'<div style=\"border-top:1px solid #334155;padding:16px 0;text-align:center\">',
'<a href=\"https://github.com/qinfendebingshuo/guanghulab\" style=\"display:inline-block;padding:8px 20px;background:linear-gradient(135deg,#3b82f6,#22d3ee);color:#fff;text-decoration:none;border-radius:6px;font-weight:600\">↩ 回到仓库继续开发</a>',
'<p style=\"color:#64748b;font-size:12px;margin:12px 0 0\">🌀 铸渊 · 代码守护人格体 · 自动发送</p>',
'</div>',
'</div>'
].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);
}

1
.gitignore vendored
View File

@ -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-*

View File

@ -49,6 +49,21 @@
---
## 🚀 开发者入口
<div align="center">
[![提交系统日志](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)
> 做完了?点蓝色按钮,把日志粘贴进去,填上邮箱,点提交。新广播会发到你邮箱里。
>
> 有问题?点绿色按钮,填写问题描述,铸渊会自动回答你。
</div>
---
## 📖 系统简介
**光湖HoloLake** 是一个基于人格语言操作系统的智能协作平台,采用壳-核分离设计:
@ -77,29 +92,31 @@
<!-- BINGSHUO_BULLETIN_START -->
| 时间 | 检查项 | 状态 |
|------|--------|------|
| 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%) | 铸渊(冰朔指令) |
<!-- BINGSHUO_BULLETIN_END -->
### 🤖 铸渊自动提醒
<!-- BINGSHUO_ALERT_START -->
> 🟢 **今日无需冰朔手动干预** · 系统一切正常
> 🔴 **需要冰朔手动干预!**
>
> 🗓️ 2026-03-13 · 铸渊自动检测
> - ❌ 📢 更新系统公告区 · 失败
>
> 🗓️ 2026-03-13 · 铸渊已发送邮件提醒
<!-- BINGSHUO_ALERT_END -->
---
@ -113,8 +130,9 @@
<!-- COLLABORATOR_BULLETIN_START -->
| 时间 | 合作者 | 模块 | 状态 |
|------|--------|------|------|
| 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 | `—/` | ✅ 上传成功 |
<!-- COLLABORATOR_BULLETIN_END -->
### 🤖 铸渊自动提醒 · 合作者

0
app.js Normal file
View File

22
broadcasts/README.md Normal file
View File

@ -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 自动管理。

203
homepage/app.js Normal file
View File

@ -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 => `
<div class="card" data-module-id="${card.id}" data-module-path="${card.path}">
<div class="card-header">
<div class="card-icon">${card.icon}</div>
<span class="card-name">${card.name}</span>
<span class="card-status ${card.status}">${card.status === 'online' ? '在线' : '建设中'}</span>
</div>
<span class="card-link">进入模块 </span>
</div>
`).join('');
grid.innerHTML = cardsHTML;
},
// 渲染轮播
renderCarousel() {
const carousel = document.getElementById('announcementCarousel');
if (!carousel) return;
const announcement = announcements[this.currentAnnouncementIndex];
carousel.innerHTML = `
<div class="carousel-item">
<div class="carousel-title">📢 ${announcement.title}</div>
<div class="carousel-content">${announcement.content}</div>
<div class="carousel-date">${announcement.date}</div>
</div>
`;
},
// 启动轮播自动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();
});

21
homepage/develop.md Normal file
View File

@ -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

48
homepage/index.html Normal file
View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>光湖实验室 · HoloLake 首页</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app">
<!-- Hero 区 -->
<header class="hero">
<h1 class="hero-title">光湖实验室<span class="hero-subtitle">HoloLake Lab</span></h1>
<p class="hero-description">探索未来之域 · 零点原核频道</p>
<a href="#" class="hero-btn">进入系统</a>
</header>
<!-- 公告轮播区(新增) -->
<div class="carousel-container">
<div class="carousel" id="announcementCarousel">
<!-- 轮播内容由 JS 动态生成 -->
</div>
<button class="carousel-arrow left" id="carouselPrev"></button>
<button class="carousel-arrow right" id="carouselNext"></button>
</div>
<!-- 模块导航网格 -->
<section class="modules">
<h2 class="section-title">核心模块</h2>
<div class="grid" id="moduleGrid">
<!-- 卡片会由 JavaScript 动态生成 -->
</div>
</section>
<!-- 系统状态栏 -->
<div class="status-bar">
<span>系统状态:</span>
<span class="status-dot online"></span> 在线
</div>
<!-- Footer -->
<footer class="footer">
<p>© 2026 光湖实验室 · 零点原核频道</p>
<p>版本 HoloLake Era · M23 首页中心</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>

437
homepage/style.css Normal file
View File

@ -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);
}
}

0
index.html Normal file
View File

View File

@ -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 APIOpenAI 兼容格式
*/
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 [
'<!DOCTYPE html>',
'<html lang="zh-CN">',
'<head>',
' <meta charset="UTF-8">',
' <meta name="viewport" content="width=device-width, initial-scale=1.0">',
' <title>' + (plan.project_name || '项目') + '</title>',
' <link rel="stylesheet" href="style.css">',
'</head>',
'<body>',
' <div class="container">',
' <h1>🌊 ' + desc + '</h1>',
' <p>由光湖 Persona Studio · 铸渊核心大脑 + 铸渊代理 协同开发</p>',
' <p class="design-note">设计风格:' + designNotes + '</p>',
' </div>',
' <script src="main.js"></script>',
'</body>',
'</html>'
].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
};

View File

@ -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 injectionAPI 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
});
}
})();

View File

@ -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"
}
]
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}
]
}

View File

@ -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 ---- */

View File

@ -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();

360
scripts/invoke-persona.js Normal file
View File

@ -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.jsfork 子进程)
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<<EOF_\d+\n([\s\S]*?)\nEOF_\d+/);
if (resultMatch) result = resultMatch[1];
}
if (!result) result = stdout.slice(-3000);
resolve(result);
} else {
reject(new Error('wake-persona.js exited with code ' + code + ': ' + stderr));
}
});
child.on('error', reject);
});
}
// ══════════════════════════════════════════════════════════
// Step 3: 回写 Notion 工单(更新 receipt_status
// ══════════════════════════════════════════════════════════
async function writeResultsToNotion(workOrderId, result, taskId) {
if (!workOrderId || !NOTION_TOKEN) {
console.log('⚠️ 无法回写 Notion缺少 workOrderId 或 NOTION_TOKEN');
return;
}
console.log('📝 回写 Notion 工单结果...');
try {
// 更新工单状态
await notionRequest('PATCH', '/v1/pages/' + workOrderId, {
properties: {
'状态': { select: { name: '✅ 已完成' } },
},
});
console.log(' → 工单状态更新为 ✅ 已完成');
// 在工单页面追加处理结果
var resultBlocks = [
{
object: 'block',
type: 'heading_2',
heading_2: {
rich_text: [{ type: 'text', text: { content: '🧠 人格体处理结果' } }],
},
},
{
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ type: 'text', text: { content: '处理时间: ' + new Date().toISOString() } }],
},
},
{
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ type: 'text', text: { content: 'receipt_status: completed' } }],
},
},
];
// 将人格体结果作为代码块
if (result) {
resultBlocks.push({
object: 'block',
type: 'code',
code: {
rich_text: [{ type: 'text', text: { content: result.slice(0, 2000) } }],
language: 'markdown',
},
});
}
await notionRequest('PATCH', '/v1/blocks/' + workOrderId + '/children', {
children: resultBlocks,
});
console.log(' → 处理结果已追加到工单页面');
} catch (err) {
console.error('⚠️ Notion 回写失败: ' + err.message);
// 重试一次
try {
console.log(' → 重试回写...');
await notionRequest('PATCH', '/v1/pages/' + workOrderId, {
properties: {
'状态': { select: { name: '⚠️ 异常·等人工介入' } },
},
});
} catch (_) { /* ignore */ }
}
}
// ══════════════════════════════════════════════════════════
// Step 4: 输出结果
// ══════════════════════════════════════════════════════════
function outputResult(result) {
var outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
var delimiter = 'EOF_' + Date.now();
fs.appendFileSync(outputFile, 'persona_result<<' + delimiter + '\n' + result + '\n' + delimiter + '\n');
fs.appendFileSync(outputFile, 'invocation_status=completed\n');
}
}
// ══════════════════════════════════════════════════════════
// 主流程
// ══════════════════════════════════════════════════════════
async function main() {
console.log('═══════════════════════════════════════════');
console.log('🔗 铸渊 · 人格体唤醒调用Phase B2');
console.log('═══════════════════════════════════════════');
console.log(' workOrderId: ' + WORK_ORDER_ID);
console.log(' taskId: ' + TASK_ID);
console.log(' developer: ' + DEVELOPER);
console.log(' action: ' + ACTION);
console.log('');
// Step 1: 读取工单
var workOrder = await readWorkOrder();
if (!workOrder.syslog_raw) {
console.error('❌ 缺少 SYSLOG 原文,无法处理');
process.exit(1);
}
// Step 2: 唤醒人格体
var result;
try {
result = await invokePersona(workOrder);
} catch (err) {
console.error('❌ 人格体唤醒失败: ' + err.message);
result = '(人格体唤醒失败: ' + err.message + ')';
}
// Step 3: 回写 Notion
if (WORK_ORDER_ID) {
await writeResultsToNotion(WORK_ORDER_ID, result, workOrder.taskId);
}
// Step 4: 输出结果
outputResult(result || '(no result)');
console.log('');
console.log('✅ 人格体调用完成');
}
main().catch(function (err) {
console.error('❌ invoke-persona 失败: ' + err.message);
// 尝试标记工单异常
if (WORK_ORDER_ID && NOTION_TOKEN) {
notionRequest('PATCH', '/v1/pages/' + WORK_ORDER_ID, {
properties: {
'状态': { select: { name: '⚠️ 异常·等人工介入' } },
},
}).catch(function () { /* ignore */ });
}
process.exit(1);
});

355
scripts/notion-heartbeat.js Normal file
View File

@ -0,0 +1,355 @@
// scripts/notion-heartbeat.js
// 铸渊 · Phase B3 · 工单心跳监控脚本
//
// 定时检测 Notion 工单队列中 receipt_status = "pending" 的工单,
// 如果超过 5 分钟未回执,重新触发人格体唤醒(最多 3 次)。
// 超过 3 次仍无回执,标记工单异常并发邮件通知冰朔。
//
// 环境变量:
// NOTION_TOKEN Notion API token
// NOTION_TICKET_DB_ID 工单队列数据库 ID
// GITHUB_TOKEN GitHub API token用于触发 workflow_dispatch
// SMTP_USER 邮件发送者
// SMTP_PASS 邮件授权码
// ALERT_EMAIL 告警邮箱(默认 565183519@qq.com
// LLM_API_KEY LLM 密钥(传递给 persona-invoke
// LLM_BASE_URL LLM 地址(传递给 persona-invoke
// CORE_BRAIN_PAGE_ID 核心大脑页面 ID
// PORTRAIT_DB_ID 画像库 ID
// FINGERPRINT_DB_ID 指纹表 ID
'use strict';
var https = require('https');
var NOTION_TOKEN = process.env.NOTION_TOKEN || '';
var NOTION_TICKET_DB_ID = process.env.NOTION_TICKET_DB_ID || '';
var GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
var ALERT_EMAIL = process.env.ALERT_EMAIL || '565183519@qq.com';
var SMTP_USER = process.env.SMTP_USER || '';
var SMTP_PASS = process.env.SMTP_PASS || '';
var NOTION_VERSION = '2022-06-28';
var HEARTBEAT_TIMEOUT_MS = (parseInt(process.env.HEARTBEAT_TIMEOUT_MINUTES, 10) || 5) * 60 * 1000;
var MAX_RETRIES = parseInt(process.env.MAX_RETRY_COUNT, 10) || 3;
var REPO_OWNER = 'qinfendebingshuo';
var REPO_NAME = 'guanghulab';
// ══════════════════════════════════════════════════════════
// HTTP 工具
// ══════════════════════════════════════════════════════════
function httpsPost(hostname, apiPath, body, headers) {
return new Promise(function (resolve, reject) {
var payload = JSON.stringify(body);
var opts = {
hostname: hostname,
port: 443,
path: apiPath,
method: 'POST',
headers: Object.assign({
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload),
}, headers || {}),
};
var req = https.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.write(payload);
req.end();
});
}
function httpsPatch(hostname, apiPath, body, headers) {
return new Promise(function (resolve, reject) {
var payload = JSON.stringify(body);
var opts = {
hostname: hostname,
port: 443,
path: apiPath,
method: 'PATCH',
headers: Object.assign({
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload),
}, headers || {}),
};
var req = https.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.write(payload);
req.end();
});
}
var notionHeaders = {
'Authorization': 'Bearer ' + NOTION_TOKEN,
'Notion-Version': NOTION_VERSION,
};
// ══════════════════════════════════════════════════════════
// Step 1: 查询待处理工单
// ══════════════════════════════════════════════════════════
async function queryPendingTickets() {
console.log('🔍 查询待处理工单...');
if (!NOTION_TOKEN || !NOTION_TICKET_DB_ID) {
console.log('⚠️ 缺少 Notion 配置,跳过');
return [];
}
// 查询 receipt_status = pending 的工单
// 由于 Notion 数据库可能没有 receipt_status 字段,
// 改为查询状态 = "待处理" 的工单
var queryBody = {
filter: {
property: '状态',
select: {
equals: '待处理',
},
},
page_size: 20,
sorts: [{ timestamp: 'created_time', direction: 'ascending' }],
};
try {
var res = await httpsPost('api.notion.com', '/v1/databases/' + NOTION_TICKET_DB_ID + '/query', queryBody, notionHeaders);
var parsed = JSON.parse(res.body);
if (res.status >= 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: [
'<div style="font-family:sans-serif;max-width:600px;margin:0 auto;padding:20px;background:#1e1b2e;color:#e2e8f0;border-radius:10px">',
'<h2 style="color:#f59e0b">⚠️ 工单处理超时告警</h2>',
'<p>以下工单超过 3 次重试仍未收到人格体回执:</p>',
'<pre style="background:#0f0d1a;padding:12px;border-radius:6px;color:#94a3b8">' + ticketList + '</pre>',
'<p>请登录 Notion 工单队列检查。</p>',
'<p style="color:#64748b;font-size:12px;margin-top:16px">🌀 铸渊 · 心跳监控 · 自动告警</p>',
'</div>',
].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);
});

View File

@ -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);
});

View File

@ -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;
}

View File

@ -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,
};

306
scripts/verify-modules.js Normal file
View File

@ -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();

906
scripts/wake-persona.js Normal file
View File

@ -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);
});

View File

@ -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
}

View File

@ -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",

0
style.css Normal file
View File