diff --git a/.github/DISCUSSION_TEMPLATE/syslog-submit.yml b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml
new file mode 100644
index 00000000..a0baaa94
--- /dev/null
+++ b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml
@@ -0,0 +1,51 @@
+title: "BC-XXX-XXXïœSYSLOG"
+labels: []
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ## ð¡ SYSLOG èªå©æäº€ / 广ææé®
+
+ > åå®äºïŒææ¥å¿ç²èŽŽè¿å»ïŒå¡«äžé®ç®±ïŒç¹æäº€ãæ°å¹¿æäŒåå°äœ é®ç®±éã
+ >
+ > æé®é¢ïŒæé®é¢ç²èŽŽè¿å»ïŒå¡«äžé®ç®±ïŒç¹æäº€ãè§£çäŒåå°äœ é®ç®±éã
+
+ **æ 颿 ŒåŒ**ïŒ`BC-XXX-XXXïœSYSLOG` æ `BC-XXX-XXXïœæé®`
+
+ - type: input
+ id: broadcast_id
+ attributes:
+ label: "广æçŒå·"
+ description: "åäžäœ åœå广æççŒå·ïŒäŸåŠ BC-M22-009-AW"
+ placeholder: "BC-M22-009-AW"
+ validations:
+ required: true
+
+ - type: dropdown
+ id: submit_type
+ attributes:
+ label: "ç±»å"
+ description: "éæ©æäº€ç±»å"
+ options:
+ - SYSLOG
+ - æé®
+ validations:
+ required: true
+
+ - type: input
+ id: email
+ attributes:
+ label: "äœ çé®ç®±"
+ description: "çšäºæ¥æ¶æ°å¹¿ææé®é¢è§£ç"
+ placeholder: "your@email.com"
+ validations:
+ required: true
+
+ - type: textarea
+ id: content
+ attributes:
+ label: "å
容"
+ description: "ç²èŽŽäœ ç SYSLOG å
šæ æ é®é¢æè¿°"
+ placeholder: "åšè¿éç²èŽŽäœ ç SYSLOG æé®é¢..."
+ validations:
+ required: true
diff --git a/.github/brain/bingshuo-issues-index.json b/.github/brain/bingshuo-issues-index.json
index abbec8c8..8e8121de 100644
--- a/.github/brain/bingshuo-issues-index.json
+++ b/.github/brain/bingshuo-issues-index.json
@@ -1,7 +1,7 @@
{
"version": "1.0",
"description": "å°æäž»æ§é®é¢çŽ¢åŒåº â è®°åœå·²ç¥é®é¢ãæ ¹å äžææ¥è·¯ç±",
- "updated_at": "2026-03-13T02:05:55.611Z",
+ "updated_at": "2026-03-13T04:49:20.482Z",
"issues": [
{
"id": "BS-001",
diff --git a/.github/brain/bingshuo-master-brain.md b/.github/brain/bingshuo-master-brain.md
index 443bfaee..364f1351 100644
--- a/.github/brain/bingshuo-master-brain.md
+++ b/.github/brain/bingshuo-master-brain.md
@@ -1,7 +1,7 @@
# å°æäž»æ§ç¥ç»ç³»ç» · æ žå¿äž»æ§å€§è v1.0
> æ¬æä»¶äžºå°æäž»æ§ç¥ç»ç³»ç»çæ»æ§èæä»¶ã
-> æåçŒè¯æ¶éŽïŒ2026-03-13T02:05:55.612Z
+> æåçŒè¯æ¶éŽïŒ2026-03-13T04:49:20.482Z
---
@@ -56,7 +56,7 @@
### ä»åºç»è®¡
- åèœæš¡åïŒ10 䞪
-- WorkflowïŒ35 䞪
+- WorkflowïŒ36 䞪
---
@@ -85,7 +85,7 @@
> æ¬åºåç± master-brain-compiler èªåšçŒè¯ã
-- **çŒè¯æ¶éŽ**ïŒ2026-03-13T02:05:55.612Z
+- **çŒè¯æ¶éŽ**ïŒ2026-03-13T04:49:20.482Z
- **èæä»¶è§åçæ¬**ïŒ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 | 36 䞪 workflow 已泚å |
| ð¡ routing_health | yellow | HLI æ¥å£èŠçç 33.3%ïŒ7/21ïŒ |
| ð¢ docs_entry_health | green | docs/index.html ååš |
| ð¡ persona_studio_health | yellow | ååç«¯ç»æååšïŒç«¯å°ç«¯å¯¹è¯éŸè·¯åŸ
éªè¯ |
diff --git a/.github/brain/bingshuo-system-health.json b/.github/brain/bingshuo-system-health.json
index 56abef67..1ba8b36f 100644
--- a/.github/brain/bingshuo-system-health.json
+++ b/.github/brain/bingshuo-system-health.json
@@ -1,7 +1,7 @@
{
"version": "1.0",
"description": "å°æäž»æ§ç³»ç»å¥åº·ç¶æ",
- "updated_at": "2026-03-13T02:05:55.611Z",
+ "updated_at": "2026-03-13T04:49:20.482Z",
"health": {
"brain_consistency": {
"status": "yellow",
@@ -13,7 +13,7 @@
},
"workflow_health": {
"status": "green",
- "detail": "35 䞪 workflow 已泚å"
+ "detail": "36 䞪 workflow 已泚å"
},
"routing_health": {
"status": "yellow",
diff --git a/.github/brain/repo-map.json b/.github/brain/repo-map.json
index 45ae82d7..119427b3 100644
--- a/.github/brain/repo-map.json
+++ b/.github/brain/repo-map.json
@@ -1,20 +1,20 @@
{
"description": "éžæžåŸä¹ŠéŠç®åœ · Library Catalog for éžæž (ZhùyuÄn)",
"version": "2.0",
- "generated_at": "2026-03-13T02:07:22.787Z",
+ "generated_at": "2026-03-13T04:49:33.903Z",
"generated_by": "scripts/generate-repo-map.js",
"repo": "qinfendebingshuo/guanghulab",
"stats": {
"zones": 13,
"total_modules": 10,
- "total_workflows": 35,
- "total_scripts": 32,
+ "total_workflows": 36,
+ "total_scripts": 33,
"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": [
{
@@ -362,6 +362,13 @@
"manual"
]
},
+ {
+ "file": "syslog-auto-pipeline.yml",
+ "name": "SYSLOG Auto Pipeline",
+ "triggers": [
+ "unknown"
+ ]
+ },
{
"file": "syslog-pipeline.yml",
"name": "éžæž · SYSLOG Pipeline (A/D/E)",
@@ -435,7 +442,7 @@
]
}
],
- "item_count": 35
+ "item_count": 36
},
{
"zone_id": "SCRIPTS",
@@ -536,6 +543,9 @@
{
"file": "update-readme-bulletin.js"
},
+ {
+ "file": "wake-persona.js"
+ },
{
"file": "zhuyuan-daily-agent.js"
},
@@ -549,7 +559,7 @@
"file": "zhuyuan-module-protocol.js"
}
],
- "item_count": 32
+ "item_count": 33
},
{
"zone_id": "SRC",
@@ -1356,6 +1366,9 @@
"sync-persona-studio": [
"WORKFLOWS::sync-persona-studio.yml"
],
+ "syslog-auto-pipeline": [
+ "WORKFLOWS::syslog-auto-pipeline.yml"
+ ],
"syslog-pipeline": [
"WORKFLOWS::syslog-pipeline.yml"
],
@@ -1466,6 +1479,9 @@
"update-memory": [
"SCRIPTS::update-memory.js"
],
+ "wake-persona": [
+ "SCRIPTS::wake-persona.js"
+ ],
"zhuyuan-module-protocol": [
"SCRIPTS::zhuyuan-module-protocol.js"
],
diff --git a/.github/brain/repo-snapshot.md b/.github/brain/repo-snapshot.md
index 4e21ee97..8dd53858 100644
--- a/.github/brain/repo-snapshot.md
+++ b/.github/brain/repo-snapshot.md
@@ -1,5 +1,5 @@
# éžæžåŸä¹ŠéŠå¿«ç
§ · Repo Snapshot
-> çæäº 2026-03-13 10:07 CST · æ¯æ¬¡ push èªåšæŽæ° · éžæžå€éæ¶äŒå
è¯»åæ€æä»¶
+> çæäº 2026-03-13 12:49 CST · æ¯æ¬¡ push èªåšæŽæ° · éžæžå€éæ¶äŒå
è¯»åæ€æä»¶
---
@@ -9,11 +9,11 @@
|------|------|
| åºåæ»æ° | 13 䞪åºå |
| åèœæš¡å | 10 䞪 (m01~m18) |
-| å·¥äœæµ | 35 䞪 GitHub Actions |
-| èæ¬ | 32 䞪æ§è¡èæ¬ |
+| å·¥äœæµ | 36 䞪 GitHub Actions |
+| èæ¬ | 33 䞪æ§è¡èæ¬ |
| åŒåè
èç¹ | 8 人 |
| HLI æ¥å£èŠçç | 7/21 (33%) |
-| å¿«ç
§çææ¶éŽ | 2026-03-13 10:07 CST |
+| å¿«ç
§çææ¶éŽ | 2026-03-13 12:49 CST |
---
@@ -30,12 +30,12 @@
**å
³é®è¯**: persona · identity · dev-status · äººæ Œ · åŒåè
ç¶æ
### â¡ èªåšåå·¥äœæµïŒWORKFLOWSïŒ
-**è·¯åŸ**: `.github/workflows` · **æ°é**: 35 项
+**è·¯åŸ**: `.github/workflows` · **æ°é**: 36 项
**æè¿°**: ææ GitHub Actions å·¥äœæµå®ä¹
**å
³é®è¯**: workflow · actions · ci · automation · å·¥äœæµ · èªåšå
### ð§ æ§è¡èæ¬åºïŒSCRIPTSïŒ
-**è·¯åŸ**: `scripts` · **æ°é**: 32 项
+**è·¯åŸ**: `scripts` · **æ°é**: 33 项
**æè¿°**: éžæžæææ§è¡æè · èªåšåèæ¬
**å
³é®è¯**: script · node · js · èæ¬ · æ§è¡ · runner
@@ -116,6 +116,7 @@
| `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-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 |
@@ -158,6 +159,7 @@
- `scripts/update-brain.js`
- `scripts/update-memory.js`
- `scripts/update-readme-bulletin.js`
+- `scripts/wake-persona.js`
- `scripts/zhuyuan-daily-agent.js`
- `scripts/zhuyuan-daily-selfcheck.js`
- `scripts/zhuyuan-issue-reply.js`
diff --git a/.github/workflows/syslog-auto-pipeline.yml b/.github/workflows/syslog-auto-pipeline.yml
new file mode 100644
index 00000000..c2fa3898
--- /dev/null
+++ b/.github/workflows/syslog-auto-pipeline.yml
@@ -0,0 +1,366 @@
+name: SYSLOG Auto Pipeline
+# ð¡ SYSLOG èªå©æäº€ç³»ç» · å
šèªåšéç¯
+#
+# åŒåè
åš GitHub Discussion æäº€ SYSLOG ææé®
+# â Actions èªåšè§£æ â è°çš LLM API å€éäººæ Œäœ
+# â åå
¥ Notion å·¥å â åé®ä»¶ç»åŒåè
â Discussion åå€
+#
+# äŸèµ SecretsïŒ
+# LLM_API_KEY ç¬¬äžæ¹ LLM å¹³å°å¯é¥ïŒå¿
é¡»ïŒ
+# LLM_BASE_URL ç¬¬äžæ¹ LLM å¹³å° API å°åïŒå¿
é¡»ïŒåŠ https://api.xxx.com/v1ïŒ
+# NOTION_API_TOKEN Notion API token
+# NOTION_TICKET_DB_ID éç 工忰æ®åº ID
+# CORE_BRAIN_PAGE_ID æå¥æ žå¿å€§è v4.0 é¡µé¢ IDïŒv4.0 åè®®åšææ³šå
¥ïŒ
+# PORTRAIT_DB_ID åŒåè
åšæç»ååºæ°æ®åº IDïŒv4.0 åè®®åšææ³šå
¥ïŒ
+# FINGERPRINT_DB_ID æš¡åæçº¹æ³šåè¡šæ°æ®åº IDïŒv4.0 åè®®åšææ³šå
¥ïŒ
+# SMTP_USER QQ é®ç®±å°å
+# SMTP_PASS QQ é®ç®± SMTP ææç
+
+on:
+ discussion:
+ types: [created]
+
+jobs:
+ process:
+ name: ð¡ å€ç SYSLOG æäº€ / 广ææé®
+ runs-on: ubuntu-latest
+ if: contains(github.event.discussion.category.name, 'SYSLOG')
+ permissions:
+ contents: write
+ discussions: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install dependencies
+ run: npm ci --ignore-scripts
+
+ - name: ð Parse submission
+ id: parse
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const body = context.payload.discussion.body || '';
+ const title = context.payload.discussion.title || '';
+
+ // Parse structured fields from discussion body
+ function extractField(text, label) {
+ // Match form field format: ### Label\n\nValue
+ const regex = new RegExp('###\\s*' + label + '\\s*\\n+([^\\n#]+)', 'i');
+ const match = text.match(regex);
+ return match ? match[1].trim() : '';
+ }
+
+ const broadcastId = extractField(body, '广æçŒå·') || '';
+ const submitType = extractField(body, 'ç±»å') || '';
+ const email = extractField(body, 'äœ çé®ç®±') || '';
+
+ // Extract content (everything after "### å
容")
+ const contentMatch = body.match(/###\s*å
容\s*\n+([\s\S]*?)$/i);
+ const content = contentMatch ? contentMatch[1].trim() : '';
+
+ // Determine type from title or body
+ let type = 'question'; // default
+ if (submitType.includes('SYSLOG') || title.includes('SYSLOG')) {
+ type = 'syslog';
+ } else if (submitType.includes('æé®') || title.includes('æé®')) {
+ type = 'question';
+ }
+
+ // Validate
+ if (!broadcastId) {
+ core.setFailed('â 猺å°å¹¿æçŒå·ïŒè¯·åšæäº€æ¶å¡«å广æçŒå·');
+ return;
+ }
+ if (!email) {
+ core.setFailed('â 猺å°é®ç®±ïŒè¯·åšæäº€æ¶å¡«åäœ çé®ç®±');
+ return;
+ }
+ if (!content) {
+ core.setFailed('â 猺å°å
容ïŒè¯·ç²èŽŽäœ ç SYSLOG æé®é¢');
+ return;
+ }
+
+ core.setOutput('broadcast_id', broadcastId);
+ core.setOutput('type', type);
+ core.setOutput('email', email);
+ core.setOutput('content', content);
+ core.setOutput('discussion_number', context.payload.discussion.number);
+ core.setOutput('author', context.payload.discussion.user.login);
+
+ console.log(`ð¡ è§£æå®æ: 广æ=${broadcastId}, ç±»å=${type}, é®ç®±=${email}`);
+
+ - name: ð§ 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 }}
+ 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: ð Write to Notion 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 }}
+ run: |
+ node -e "
+ const https = require('https');
+
+ const token = process.env.NOTION_TOKEN;
+ const dbId = process.env.NOTION_TICKET_DB_ID;
+ const broadcastId = process.env.BROADCAST_ID || 'UNKNOWN';
+ const type = process.env.SUBMIT_TYPE || 'syslog';
+ const result = process.env.PERSONA_RESULT || '(no result)';
+
+ if (!token || !dbId) {
+ console.log('â ïž Notion credentials not configured, skipping ticket creation');
+ process.exit(0);
+ }
+
+ const typeLabel = type === 'syslog' ? 'SYSLOGéç¯' : 'æé®è§£ç';
+ const title = '[èªåš] ' + broadcastId + ' · ' + typeLabel;
+
+ const body = JSON.stringify({
+ parent: { database_id: dbId },
+ properties: {
+ 'æ é¢': { title: [{ type: 'text', text: { content: title.slice(0, 120) } }] },
+ 'æäœç±»å': { select: { name: 'å
¶ä»' } },
+ 'æäº€è
': { rich_text: [{ type: 'text', text: { content: 'èªåšç®¡é' } }] },
+ 'ç¶æ': { select: { name: '已宿' } },
+ 'äŒå
级': { select: { name: 'P1' } }
+ },
+ children: [{
+ object: 'block',
+ type: 'paragraph',
+ paragraph: {
+ rich_text: [{ type: 'text', text: { content: result.slice(0, 2000) } }]
+ }
+ }]
+ });
+
+ const opts = {
+ hostname: 'api.notion.com',
+ port: 443,
+ path: '/v1/pages',
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer ' + token,
+ 'Content-Type': 'application/json',
+ 'Notion-Version': '2022-06-28',
+ 'Content-Length': Buffer.byteLength(body)
+ }
+ };
+
+ const req = https.request(opts, (res) => {
+ let data = '';
+ res.on('data', (c) => data += c);
+ res.on('end', () => {
+ if (res.statusCode >= 200 && res.statusCode < 300) {
+ console.log('â
Notion å·¥åå·²å建');
+ } else {
+ console.log('â ïž Notion å·¥åå建倱莥: ' + res.statusCode + ' ' + data);
+ }
+ });
+ });
+ req.on('error', (e) => console.log('â ïž Notion 请æ±å€±èŽ¥: ' + e.message));
+ req.write(body);
+ req.end();
+ "
+
+ - name: ð§ Send email to developer
+ env:
+ SMTP_USER: ${{ secrets.SMTP_USER }}
+ SMTP_PASS: ${{ secrets.SMTP_PASS }}
+ EMAIL_TO: ${{ steps.parse.outputs.email }}
+ BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }}
+ SUBMIT_TYPE: ${{ steps.parse.outputs.type }}
+ PERSONA_RESULT: ${{ steps.persona.outputs.result }}
+ run: |
+ node -e "
+ const nodemailer = require('nodemailer');
+
+ const user = process.env.SMTP_USER || '';
+ const pass = process.env.SMTP_PASS || '';
+ const to = process.env.EMAIL_TO || '';
+ const broadcastId = process.env.BROADCAST_ID || '';
+ const type = process.env.SUBMIT_TYPE || 'syslog';
+ const result = process.env.PERSONA_RESULT || '(å€çäžïŒè¯·çšå)';
+
+ if (!user || !pass) {
+ console.log('â ïž SMTP not configured, skipping email');
+ process.exit(0);
+ }
+ if (!to) {
+ console.log('â ïž No recipient email, skipping');
+ process.exit(0);
+ }
+
+ const subjectText = type === 'syslog'
+ ? '[å
æ¹ç³»ç»] ' + broadcastId + ' · æ°å¹¿æå·²çæ'
+ : '[å
æ¹ç³»ç»] ' + broadcastId + ' · é®é¢å·²è§£ç';
+
+ const resultHtml = result
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/\n/g, '
');
+
+ const html = [
+ '
',
+ '
',
+ '
ð å
æ¹ç³»ç» · èªåšéç¥
',
+ '
HoloLake · äººæ Œè¯èšæäœç³»ç»
',
+ '
',
+ '
',
+ '
ð¡ ' + broadcastId + '
',
+ '
' + resultHtml + '
',
+ '
',
+ '
',
+ '
'
+ ].join('\n');
+
+ const transporter = nodemailer.createTransport({
+ host: 'smtp.qq.com',
+ port: 465,
+ secure: true,
+ auth: { user, pass }
+ });
+
+ transporter.sendMail({
+ from: '\"å
æ¹ç³»ç»\" <' + user + '>',
+ to: to,
+ subject: subjectText,
+ html: html
+ }).then((info) => {
+ console.log('â
é®ä»¶å·²åé: ' + info.messageId);
+ }).catch((err) => {
+ console.log('â ïž é®ä»¶åé倱莥: ' + err.message);
+ });
+ "
+
+ - name: ð¬ Reply to Discussion
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const number = ${{ steps.parse.outputs.discussion_number }};
+ const type = '${{ steps.parse.outputs.type }}';
+ const broadcastId = '${{ steps.parse.outputs.broadcast_id }}';
+ const email = '${{ steps.parse.outputs.email }}';
+
+ const typeLabel = type === 'syslog' ? 'SYSLOG éç¯å€ç' : 'é®é¢è§£ç';
+ const maskedEmail = email.length > 4
+ ? email.replace(/(.{2})(.*)(@.*)/, '$1***$3')
+ : '***';
+
+ const body = [
+ 'â
**å·²å€ç** · ' + typeLabel,
+ '',
+ '| é¡¹ç® | å
容 |',
+ '|------|------|',
+ '| ð¡ 广æçŒå· | `' + broadcastId + '` |',
+ '| ð§ ç»æåéè³ | `' + maskedEmail + '` |',
+ '| ð€ å€çäººæ Œäœ | éžæž |',
+ '| â° å€çæ¶éŽ | ' + new Date().toISOString() + ' |',
+ '',
+ '> ç»æå·²åéå°äœ çé®ç®±ïŒè¯·æ¥æ¶ã',
+ '>',
+ '> åŠæªæ¶å°ïŒè¯·æ£æ¥ååŸç®±æéæ°æäº€ã'
+ ].join('\n');
+
+ // Use GraphQL to add discussion comment
+ const query = `
+ mutation($discussionId: ID!, $body: String!) {
+ addDiscussionComment(input: {
+ discussionId: $discussionId,
+ body: $body
+ }) {
+ comment { id }
+ }
+ }
+ `;
+
+ try {
+ // Get the discussion node ID
+ const { repository } = await github.graphql(`
+ query($owner: String!, $repo: String!, $number: Int!) {
+ repository(owner: $owner, name: $repo) {
+ discussion(number: $number) {
+ id
+ }
+ }
+ }
+ `, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ number: number
+ });
+
+ await github.graphql(query, {
+ discussionId: repository.discussion.id,
+ body: body
+ });
+
+ console.log('â
Discussion åå€å·²åé');
+ } catch (err) {
+ console.log('â ïž Discussion åå€å€±èŽ¥: ' + err.message);
+ }
+
+ - name: ðŽ å€±èŽ¥åèŠ
+ if: failure()
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const number = context.payload.discussion?.number;
+ if (!number) return;
+
+ try {
+ const { repository } = await github.graphql(`
+ query($owner: String!, $repo: String!, $number: Int!) {
+ repository(owner: $owner, name: $repo) {
+ discussion(number: $number) {
+ id
+ }
+ }
+ }
+ `, {
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ number: number
+ });
+
+ await github.graphql(`
+ mutation($discussionId: ID!, $body: String!) {
+ addDiscussionComment(input: {
+ discussionId: $discussionId,
+ body: $body
+ }) {
+ comment { id }
+ }
+ }
+ `, {
+ discussionId: repository.discussion.id,
+ body: 'â **å€ç倱莥** · è¯·æ£æ¥æäº€æ ŒåŒæ¯åŠæ£ç¡®ïŒæè系管çåã\n\n> é误诊æ
请æ¥ç [Actions æ¥å¿](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions)ã'
+ });
+ } catch (err) {
+ console.log('â ïž å€±èŽ¥åèŠåå€å€±èŽ¥: ' + err.message);
+ }
diff --git a/README.md b/README.md
index ff934478..4f27b893 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,21 @@
---
+## ð åŒåè
å
¥å£
+
+
+
+[](https://github.com/qinfendebingshuo/guanghulab/discussions/new?category=ð¡+SYSLOG+æäº€åº)
+[](https://github.com/qinfendebingshuo/guanghulab/discussions/new?category=ð¡+SYSLOG+æäº€åº)
+
+> åå®äºïŒææ¥å¿ç²èŽŽè¿å»ïŒå¡«äžé®ç®±ïŒç¹æäº€ãæ°å¹¿æäŒåå°äœ é®ç®±éã
+>
+> æé®é¢ïŒåäžäžªå
¥å£ïŒéãæé®ãïŒé®é¢è§£çä¹åå°äœ é®ç®±éã
+
+
+
+---
+
## ð ç³»ç»ç®ä»
**å
æ¹ïŒHoloLakeïŒ** æ¯äžäžªåºäºäººæ Œè¯èšæäœç³»ç»çæºèœåäœå¹³å°ïŒéçšå£³-æ žå犻讟计ïŒ
@@ -77,21 +92,21 @@
| æ¶éŽ | æ£æ¥é¡¹ | ç¶æ |
|------|--------|------|
+| 03-13 12:50 | â
ð éžæž CD · èªåšéšçœ²å° guanghulab.com · æå | å°æ |
+| 03-13 12:50 | â
éžæž · Notion å·¥å蜮询 · æå | å°æ |
+| 03-13 12:49 | â
ð¢ æŽæ°ç³»ç»å
¬ååº Â· æå | å°æ |
+| 03-13 12:49 | ð§ ç³»ç»æŽæ°: `.github/` | éžæž (ZhùYuÄn) |
+| 03-13 12:49 | â
éžæž · Bridge E · GitHub Changes â Notion · æå | å°æ |
+| 03-13 11:54 | ð§ ç³»ç»æŽæ°: `scripts/` | éžæž (Copilot) |
+| 03-13 11:31 | â
ð¢ æŽæ°ç³»ç»å
¬ååº Â· æå | å°æ |
| 03-13 11:31 | â
éžæž · PSP åèº«å·¡æ£ Â· æå | å°æ |
-| 03-13 11:31 | ð§ ç³»ç»æŽæ°: `.github/` | éžæž (ZhùYuÄn) |
| 03-13 11:22 | â
ð¢ æŽæ°ç³»ç»å
¬ååº Â· æå | å°æ |
| 03-13 10:49 | â
éžæž · Notion å·¥å蜮询 · æå | å°æ |
-| 03-13 09:58 | â
ð¢ æŽæ°ç³»ç»å
¬ååº Â· æå | zhizhi200271 |
-| 03-13 09:58 | â
ð éžæž CD · èªåšéšçœ²å° guanghulab.com · æå | zhizhi200271 |
-| 03-13 09:57 | â
ð¢ æŽæ°ç³»ç»å
¬ååº Â· æå | zhizhi200271 |
-| 03-13 09:56 | â¹ïž ð¢ æŽæ°ç³»ç»å
¬ååº Â· cancelled | zhizhi200271 |
-| 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 08:00 | â
éžæž PSP å·¡æ£éè¿ Â· å
šé𿣿¥é¡¹ â
| éžæžPSPå·¡æ£ |
-| 03-13 07:59 | â
éžæž · Notion å·¥å蜮询 · æå | å°æ |
+| 03-12 16:55 | â
æ¯æ¥å·¡æ£ â
éè¿ | å°æ |
+| 03-12 08:00 | â
éžæž PSP å·¡æ£éè¿ Â· å
šé𿣿¥é¡¹ â
| éžæžPSPå·¡æ£ |
+| 03-11 16:55 | â
æ¯æ¥å·¡æ£ â
éè¿ | å°æ |
### ð€ éžæžèªåšæé
@@ -113,7 +128,8 @@
| æ¶éŽ | åäœè
| æš¡å | ç¶æ |
|------|--------|------|------|
-| 03-13 09:56 | zhizhi200271 | `â/` | â
äžäŒ æå |
+| 03-13 12:49 | å°æ | `â/` | â
äžäŒ æå |
+| 03-13 12:49 | Copilot | `â/` | â
äžäŒ æå |
| 03-13 09:56 | ð€ ä¹ä¹ | `dingtalk-bot/` | ðŠ äžäŒ æå |
diff --git a/scripts/wake-persona.js b/scripts/wake-persona.js
new file mode 100644
index 00000000..fb88b08e
--- /dev/null
+++ b/scripts/wake-persona.js
@@ -0,0 +1,871 @@
+// 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';
+
+// 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 åºå¹¿æïŒïŒæåšéŸè·¯äžå圱åã');
+
+ // âââ ä»»å¡ç±»åäžçšæä»€ âââ
+ 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. æ¥è¯¢ç»ååºæè¿ 2-3 æ¡å¿«ç
§ïŒPGP v1.0ïŒ');
+ parts.push('3. æ¥è¯¢æš¡åæçº¹æ³šå衚ïŒé²éå€Â·âš.5ïŒ');
+ parts.push('4. RT-02 èªåšè°åºŠå€æ');
+ parts.push('5. çææ°å¹¿æïŒBC-GEN v4.0 宿޿µçšïŒ');
+ parts.push('6. èŸåºç»æåç»æïŒå¹¿æå
šæ + éç¯æ°æ®ïŒ');
+ parts.push('');
+ parts.push('èŸåºæ ŒåŒïŒ');
+ parts.push('---');
+ parts.push('## ð¡ SYSLOG éªæ¶æ¥å');
+ 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);
+});