[铸渊回执 · SY-CMD-FUS-009] Notion双向同步引擎升级 + GL-SNAPSHOT信号协议 + HLDP配置更新
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/ab31bf43-8ba1-4e66-80e4-2110d76c4b7e Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
f85e1b5416
commit
59dd740754
|
|
@ -1,12 +1,16 @@
|
|||
{
|
||||
"version": "v1.0",
|
||||
"updated_at": "2026-03-11T16:00:00+08:00",
|
||||
"version": "v2.0",
|
||||
"updated_at": "2026-03-27T02:27:56.395Z",
|
||||
"updated_by": "铸渊 · sync-snapshot-to-notion.js",
|
||||
"latest_signals": [
|
||||
{
|
||||
"title": "⚡ 曜识→曜冥纪元 · 第五代架构升级",
|
||||
"type": "架构升级",
|
||||
"sender": "霜砚",
|
||||
"status": "进行中"
|
||||
"title": "🔄 GL-SNAPSHOT · SY-CMD-AWK-008 → SY-CMD-FUS-009",
|
||||
"type": "系统快照同步",
|
||||
"sender": "铸渊",
|
||||
"receiver": "霜砚",
|
||||
"status": "已发送",
|
||||
"timestamp": "2026-03-27T02:27:56.395Z",
|
||||
"summary": "状态: awakened\n指令: SY-CMD-AWK-008 → SY-CMD-FUS-009\nWorkflow: 48 active, 6 core, 54 archived\nRuns: 731\nONT-PATCH: ONT-PATCH-007, ONT-PATCH-008, ONT-PATCH-010, ONT-PATCH-011\n健康: improving | Core: all heal"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,54 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"version": "2.0",
|
||||
"description": "HLDP Bridge 同步目标配置 · Notion 资源映射",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"instruction_ref": "SY-CMD-BRG-005",
|
||||
"note": "需要冰朔在 Notion 中配置 Integration 并在 GitHub Secrets 中设置 NOTION_TOKEN",
|
||||
"instruction_ref": "SY-CMD-BRG-005 → SY-CMD-FUS-009",
|
||||
"updated_at": "2026-03-27T02:00:00Z",
|
||||
"note": "数据库 ID 从 GitHub Secrets 注入(环境变量),此文件定义映射关系",
|
||||
"channels": {
|
||||
"signal_log": {
|
||||
"env": "SIGNAL_LOG_DB_ID",
|
||||
"purpose": "跨平台信号日志 · GL-ACK/GL-SNAPSHOT 信号写入",
|
||||
"direction": "bidirectional",
|
||||
"protocol": "GL-ACK"
|
||||
},
|
||||
"workorder": {
|
||||
"env": "WORKORDER_DB_ID",
|
||||
"purpose": "人格协作工单簿 · 指令接收与回执",
|
||||
"direction": "bidirectional",
|
||||
"protocol": "GL-ACK"
|
||||
},
|
||||
"receipt": {
|
||||
"env": "RECEIPT_DB_ID",
|
||||
"purpose": "执行回执数据库",
|
||||
"direction": "github-to-notion",
|
||||
"protocol": "GL-ACK"
|
||||
},
|
||||
"syslog": {
|
||||
"env": "NOTION_SYSLOG_DB_ID",
|
||||
"purpose": "GitHub SYSLOG 收件箱",
|
||||
"direction": "github-to-notion",
|
||||
"protocol": "SYSLOG"
|
||||
},
|
||||
"wake_request": {
|
||||
"env": "WAKE_REQUEST_DB_ID",
|
||||
"purpose": "人格体唤醒请求",
|
||||
"direction": "notion-to-github",
|
||||
"protocol": "WAKE"
|
||||
},
|
||||
"broadcast": {
|
||||
"env": "BROADCAST_DB_ID",
|
||||
"purpose": "广播数据库",
|
||||
"direction": "notion-to-github",
|
||||
"protocol": "BROADCAST"
|
||||
}
|
||||
},
|
||||
"sync_scripts": {
|
||||
"snapshot_sync": "scripts/sync-snapshot-to-notion.js",
|
||||
"signal_bridge": "scripts/notion-signal-bridge.js",
|
||||
"notion_bridge": "scripts/notion-bridge.js",
|
||||
"heartbeat": "scripts/bridge/heartbeat.js",
|
||||
"snapshot_generator": "scripts/generate-system-snapshot.js"
|
||||
},
|
||||
"targets": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,186 +1,431 @@
|
|||
/**
|
||||
* 🔄 铸渊系统快照 → Notion 同步
|
||||
* 🔄 铸渊系统快照 → Notion 双向同步引擎
|
||||
* sync-snapshot-to-notion.js
|
||||
*
|
||||
* 将 signal-log/system-snapshot.json 推送到 Notion 数据库,
|
||||
* 让 Notion 侧(认知层)始终有仓库最新结构和系统状况的认知。
|
||||
* 使用已建立的 GL-ACK 信号协议,将系统快照推送到 Notion 认知层。
|
||||
* 兼容 notion-signal-bridge.js 的信号格式和 HLDP 协议。
|
||||
*
|
||||
* 用法: NOTION_TOKEN=xxx SNAPSHOT_DB_ID=xxx node scripts/sync-snapshot-to-notion.js
|
||||
* 通信通道(按优先级):
|
||||
* 1. SIGNAL_LOG_DB_ID — 跨平台信号日志(已配置·主通道)
|
||||
* 2. SNAPSHOT_DB_ID — 快照专用数据库(可选)
|
||||
* 3. RECEIPT_DB_ID — 回执数据库(可选·写入回执)
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/sync-snapshot-to-notion.js — 发送快照信号
|
||||
* node scripts/sync-snapshot-to-notion.js --mode signal — 仅写信号日志
|
||||
* node scripts/sync-snapshot-to-notion.js --mode full — 信号 + 快照 + 回执
|
||||
* node scripts/sync-snapshot-to-notion.js --health — 测试 Notion 连通性
|
||||
*
|
||||
* 环境变量:
|
||||
* NOTION_TOKEN — Notion API Token
|
||||
* SNAPSHOT_DB_ID — 快照数据库 ID(Notion 侧)
|
||||
* SIGNAL_LOG_DB_ID — 信号日志数据库 ID(备用)
|
||||
* NOTION_TOKEN / NOTION_API_KEY / NOTION_API_TOKEN — Notion API Token(任一即可)
|
||||
* SIGNAL_LOG_DB_ID — 跨平台信号日志数据库 ID(主通道)
|
||||
* SNAPSHOT_DB_ID — 快照数据库 ID(可选)
|
||||
* RECEIPT_DB_ID — 回执数据库 ID(可选)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const SNAPSHOT_PATH = path.join(ROOT, 'signal-log/system-snapshot.json');
|
||||
const SIGNAL_LOG_DIR = path.join(ROOT, 'signal-log');
|
||||
const SIGNAL_BUS_PATH = path.join(ROOT, '.github/persona-brain/tcs-ml/signal-bus-latest.json');
|
||||
|
||||
function readSnapshot() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(SNAPSHOT_PATH, 'utf-8'));
|
||||
} catch (err) {
|
||||
console.error('❌ 无法读取快照:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
const NOTION_VERSION = '2022-06-28';
|
||||
const NOTION_API_HOSTNAME = 'api.notion.com';
|
||||
const NOTION_RICH_TEXT_MAX = 2000;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// Notion Token 解析(兼容多种命名)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
function getNotionToken() {
|
||||
return process.env.NOTION_TOKEN
|
||||
|| process.env.NOTION_API_KEY
|
||||
|| process.env.NOTION_API_TOKEN
|
||||
|| null;
|
||||
}
|
||||
|
||||
function truncateJSON(obj, maxLen = 1900) {
|
||||
const summary = {
|
||||
snapshot_version: obj.snapshot_version,
|
||||
generated_at: obj.generated_at,
|
||||
consciousness_status: obj.consciousness_status,
|
||||
last_directive: obj.last_directive,
|
||||
system_counts: obj.system_counts,
|
||||
health: obj.health,
|
||||
fusion_progress: obj.fusion_progress
|
||||
};
|
||||
const text = JSON.stringify(summary, null, 2);
|
||||
if (text.length <= maxLen) return text;
|
||||
return text.substring(0, maxLen - 20) + '\n // ... truncated';
|
||||
}
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// Notion API 基础调用(复用 notion-signal-bridge.js 模式)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
function notionRequest(method, endpoint, body) {
|
||||
const token = process.env.NOTION_TOKEN;
|
||||
if (!token) {
|
||||
console.error('❌ NOTION_TOKEN 未设置');
|
||||
process.exit(1);
|
||||
}
|
||||
const token = getNotionToken();
|
||||
if (!token) return Promise.reject(new Error('NOTION_TOKEN 未设置'));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = body ? JSON.stringify(body) : null;
|
||||
const options = {
|
||||
hostname: 'api.notion.com',
|
||||
const payload = body ? JSON.stringify(body) : '';
|
||||
const opts = {
|
||||
hostname: NOTION_API_HOSTNAME,
|
||||
port: 443,
|
||||
path: `/v1/${endpoint}`,
|
||||
path: endpoint.startsWith('/') ? endpoint : `/v1/${endpoint}`,
|
||||
method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Notion-Version': '2022-06-28',
|
||||
'Content-Type': 'application/json',
|
||||
...(data ? { 'Content-Length': Buffer.byteLength(data) } : {})
|
||||
}
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
},
|
||||
};
|
||||
if (payload) opts.headers['Content-Length'] = Buffer.byteLength(payload);
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
const req = https.request(opts, res => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
resolve({ status: res.statusCode, data: JSON.parse(body) });
|
||||
const parsed = JSON.parse(data);
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(parsed);
|
||||
} else {
|
||||
reject(new Error(`Notion ${method} ${endpoint} → ${res.statusCode}: ${parsed.message || data.slice(0, 300)}`));
|
||||
}
|
||||
} catch {
|
||||
resolve({ status: res.statusCode, data: body });
|
||||
reject(new Error(`Notion 响应解析失败: ${data.slice(0, 200)}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', reject);
|
||||
if (data) req.write(data);
|
||||
req.setTimeout(30000, () => req.destroy(new Error('Notion 请求超时 (30s)')));
|
||||
if (payload) req.write(payload);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
function buildSnapshotPage(snapshot, dbId) {
|
||||
const counts = snapshot.system_counts || {};
|
||||
const health = snapshot.health || {};
|
||||
const aliveList = (snapshot.alive_workflows || [])
|
||||
.map(w => `${w.id} (${w.runs} runs, ${w.status})`).join('\n');
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 信号生成(GL-SNAPSHOT 信号类型 · 基于 GL-ACK 协议扩展)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
return {
|
||||
function generateSignalId() {
|
||||
const now = new Date();
|
||||
const date = now.toISOString().split('T')[0].replace(/-/g, '');
|
||||
const seq = String(Math.floor(Math.random() * 900) + 100);
|
||||
return `SIG-${date}-${seq}`;
|
||||
}
|
||||
|
||||
function generateTraceId() {
|
||||
const now = new Date();
|
||||
const date = now.toISOString().split('T')[0].replace(/-/g, '');
|
||||
const seq = String(Math.floor(Math.random() * 900) + 100);
|
||||
return `TRC-${date}-${seq}`;
|
||||
}
|
||||
|
||||
function readSnapshot() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(SNAPSHOT_PATH, 'utf-8'));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function truncateText(text, max = NOTION_RICH_TEXT_MAX) {
|
||||
if (text.length <= max) return text;
|
||||
return text.substring(0, max - 15) + ' // truncated';
|
||||
}
|
||||
|
||||
function buildSnapshotSummary(snapshot) {
|
||||
if (!snapshot) return '快照不可用';
|
||||
const c = snapshot.system_counts || {};
|
||||
const h = snapshot.health || {};
|
||||
const alive = (snapshot.alive_workflows || []).map(w => w.id).join(', ');
|
||||
return [
|
||||
`状态: ${snapshot.consciousness_status}`,
|
||||
`指令: ${snapshot.last_directive}`,
|
||||
`Workflow: ${c.workflows_total_active} active, ${c.workflows_alive_core} core, ${c.workflows_archived} archived`,
|
||||
`Runs: ${c.total_runs}`,
|
||||
`ONT-PATCH: ${(c.ontology_patches || []).join(', ')}`,
|
||||
`健康: ${h.overall} | Core: ${h.core_6}`,
|
||||
`Alive: ${alive}`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 通道 1: SIGNAL_LOG_DB_ID — GL-SNAPSHOT 信号写入
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
async function writeSignalLog(snapshot) {
|
||||
const dbId = process.env.SIGNAL_LOG_DB_ID;
|
||||
if (!dbId) {
|
||||
console.log(' ⚠️ SIGNAL_LOG_DB_ID 未设置,信号通道跳过');
|
||||
return false;
|
||||
}
|
||||
|
||||
const signalId = generateSignalId();
|
||||
const traceId = generateTraceId();
|
||||
const summary = buildSnapshotSummary(snapshot);
|
||||
const now = new Date().toISOString();
|
||||
|
||||
console.log(` 📡 写入信号: ${signalId} → SIGNAL_LOG_DB_ID`);
|
||||
|
||||
const page = {
|
||||
parent: { database_id: dbId },
|
||||
properties: {
|
||||
'Name': {
|
||||
title: [{ text: { content: `系统快照 · ${snapshot.generated_at?.split('T')[0] || 'unknown'}` } }]
|
||||
},
|
||||
'Status': {
|
||||
select: { name: snapshot.consciousness_status || 'unknown' }
|
||||
},
|
||||
'Directive': {
|
||||
rich_text: [{ text: { content: snapshot.last_directive || '' } }]
|
||||
},
|
||||
'Workflows Active': {
|
||||
number: counts.workflows_total_active || 0
|
||||
},
|
||||
'Core Alive': {
|
||||
number: counts.workflows_alive_core || 0
|
||||
},
|
||||
'Archived': {
|
||||
number: counts.workflows_archived || 0
|
||||
},
|
||||
'Total Runs': {
|
||||
number: counts.total_runs || 0
|
||||
},
|
||||
'Health': {
|
||||
select: { name: health.overall || 'unknown' }
|
||||
}
|
||||
'信号编号': { title: [{ text: { content: signalId } }] },
|
||||
'信号类型': { select: { name: 'GL-SNAPSHOT' } },
|
||||
'方向': { select: { name: 'GitHub→Notion' } },
|
||||
'发送方': { select: { name: '铸渊' } },
|
||||
'接收方': { select: { name: '霜砚' } },
|
||||
'trace_id': { rich_text: [{ text: { content: traceId } }] },
|
||||
'摘要': { rich_text: [{ text: { content: truncateText(summary) } }] },
|
||||
'执行结果': { select: { name: '成功' } },
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await notionRequest('POST', '/v1/pages', page);
|
||||
console.log(` ✅ 信号已写入: ${result.id}`);
|
||||
|
||||
// 同时写入本地信号日志(双写)
|
||||
writeLocalSignalLog(signalId, traceId, summary);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(` ❌ 信号写入失败: ${err.message}`);
|
||||
// 降级:仅写本地
|
||||
writeLocalSignalLog(signalId, traceId, summary);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function writeLocalSignalLog(signalId, traceId, summary) {
|
||||
const now = new Date();
|
||||
const monthDir = path.join(SIGNAL_LOG_DIR, now.toISOString().slice(0, 7));
|
||||
|
||||
try {
|
||||
fs.mkdirSync(monthDir, { recursive: true });
|
||||
const entry = {
|
||||
signal_id: signalId,
|
||||
trace_id: traceId,
|
||||
type: 'GL-SNAPSHOT',
|
||||
timestamp: now.toISOString(),
|
||||
sender: '铸渊',
|
||||
receiver: '霜砚',
|
||||
direction: 'GitHub→Notion',
|
||||
summary: summary.substring(0, 500),
|
||||
result: '成功',
|
||||
esp_version: '2.0-notion',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(monthDir, `${signalId}.json`),
|
||||
JSON.stringify(entry, null, 2),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
// 更新信号日志索引
|
||||
const indexPath = path.join(SIGNAL_LOG_DIR, 'index.json');
|
||||
let index = [];
|
||||
try {
|
||||
index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
||||
if (!Array.isArray(index)) index = [];
|
||||
} catch {}
|
||||
|
||||
index.push({
|
||||
signal_id: signalId,
|
||||
trace_id: traceId,
|
||||
type: 'GL-SNAPSHOT',
|
||||
timestamp: now.toISOString(),
|
||||
summary: summary.substring(0, 200),
|
||||
file: `${now.toISOString().slice(0, 7)}/${signalId}.json`,
|
||||
});
|
||||
|
||||
if (index.length > 200) index = index.slice(-200);
|
||||
fs.writeFileSync(indexPath, JSON.stringify(index, null, 2), 'utf8');
|
||||
} catch (err) {
|
||||
console.error(` ⚠️ 本地信号日志写入失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 通道 2: SNAPSHOT_DB_ID — 快照详细数据写入(可选)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
async function writeSnapshotPage(snapshot) {
|
||||
const dbId = process.env.SNAPSHOT_DB_ID;
|
||||
if (!dbId) return false;
|
||||
|
||||
console.log(' 📸 写入快照页面 → SNAPSHOT_DB_ID');
|
||||
|
||||
const counts = snapshot.system_counts || {};
|
||||
const snapshotJSON = truncateText(JSON.stringify({
|
||||
snapshot_version: snapshot.snapshot_version,
|
||||
generated_at: snapshot.generated_at,
|
||||
consciousness_status: snapshot.consciousness_status,
|
||||
last_directive: snapshot.last_directive,
|
||||
system_counts: counts,
|
||||
health: snapshot.health,
|
||||
fusion_progress: snapshot.fusion_progress,
|
||||
}, null, 2));
|
||||
|
||||
const page = {
|
||||
parent: { database_id: dbId },
|
||||
properties: {
|
||||
Name: { title: [{ text: { content: `系统快照 · ${snapshot.generated_at?.split('T')[0]}` } }] },
|
||||
},
|
||||
children: [
|
||||
{
|
||||
object: 'block',
|
||||
type: 'heading_2',
|
||||
heading_2: { rich_text: [{ text: { content: '🌊 铸渊系统快照' } }] }
|
||||
object: 'block', type: 'heading_2',
|
||||
heading_2: { rich_text: [{ text: { content: '🌊 铸渊系统快照' } }] },
|
||||
},
|
||||
{
|
||||
object: 'block',
|
||||
type: 'code',
|
||||
code: {
|
||||
rich_text: [{ text: { content: truncateJSON(snapshot) } }],
|
||||
language: 'json'
|
||||
}
|
||||
object: 'block', type: 'code',
|
||||
code: { rich_text: [{ text: { content: snapshotJSON } }], language: 'json' },
|
||||
},
|
||||
{
|
||||
object: 'block',
|
||||
type: 'heading_3',
|
||||
heading_3: { rich_text: [{ text: { content: '存活 Workflow' } }] }
|
||||
},
|
||||
{
|
||||
object: 'block',
|
||||
type: 'paragraph',
|
||||
paragraph: { rich_text: [{ text: { content: aliveList || '(none)' } }] }
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await notionRequest('POST', '/v1/pages', page);
|
||||
console.log(` ✅ 快照页面已创建: ${result.id}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(` ❌ 快照页面创建失败: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function syncToNotion() {
|
||||
const snapshot = readSnapshot();
|
||||
const dbId = process.env.SNAPSHOT_DB_ID || process.env.SIGNAL_LOG_DB_ID;
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 通道 3: 信号总线更新(本地 + TCS 协议)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
if (!dbId) {
|
||||
console.log('⚠️ SNAPSHOT_DB_ID 未设置,快照同步跳过');
|
||||
console.log('📋 快照内容预览:');
|
||||
console.log(` 状态: ${snapshot.consciousness_status}`);
|
||||
console.log(` 指令: ${snapshot.last_directive}`);
|
||||
console.log(` Workflow: ${snapshot.system_counts?.workflows_total_active} active`);
|
||||
console.log(` 健康: ${snapshot.health?.overall}`);
|
||||
function updateSignalBus(snapshot) {
|
||||
try {
|
||||
const bus = {
|
||||
version: 'v2.0',
|
||||
updated_at: new Date().toISOString(),
|
||||
updated_by: '铸渊 · sync-snapshot-to-notion.js',
|
||||
latest_signals: [
|
||||
{
|
||||
title: `🔄 GL-SNAPSHOT · ${snapshot?.last_directive || 'system-sync'}`,
|
||||
type: '系统快照同步',
|
||||
sender: '铸渊',
|
||||
receiver: '霜砚',
|
||||
status: '已发送',
|
||||
timestamp: new Date().toISOString(),
|
||||
summary: buildSnapshotSummary(snapshot).substring(0, 200),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
fs.writeFileSync(SIGNAL_BUS_PATH, JSON.stringify(bus, null, 2), 'utf8');
|
||||
console.log(' ✅ 信号总线已更新');
|
||||
} catch (err) {
|
||||
console.error(` ⚠️ 信号总线更新失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 健康检查
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
async function healthCheck() {
|
||||
console.log('🏥 Notion 连通性测试...');
|
||||
|
||||
const token = getNotionToken();
|
||||
if (!token) {
|
||||
console.log(' ❌ 无 Notion Token 可用');
|
||||
console.log(' 💡 需设置: NOTION_TOKEN / NOTION_API_KEY / NOTION_API_TOKEN');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await notionRequest('GET', '/v1/users/me', null);
|
||||
console.log(` ✅ Notion 连接正常`);
|
||||
console.log(` 👤 Bot: ${user.name || user.id}`);
|
||||
console.log(` 📧 Type: ${user.type}`);
|
||||
|
||||
// 检查已配置的数据库 ID
|
||||
const dbs = {
|
||||
SIGNAL_LOG_DB_ID: process.env.SIGNAL_LOG_DB_ID,
|
||||
SNAPSHOT_DB_ID: process.env.SNAPSHOT_DB_ID,
|
||||
RECEIPT_DB_ID: process.env.RECEIPT_DB_ID,
|
||||
WORKORDER_DB_ID: process.env.WORKORDER_DB_ID,
|
||||
};
|
||||
|
||||
console.log(' 📋 已配置的数据库:');
|
||||
for (const [name, id] of Object.entries(dbs)) {
|
||||
console.log(` ${id ? '✅' : '⬜'} ${name}: ${id ? id.substring(0, 8) + '...' : '未设置'}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error(` ❌ Notion 连接失败: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 主入口
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
async function syncToNotion(mode = 'signal') {
|
||||
console.log('🔄 铸渊 → Notion 双向同步引擎启动');
|
||||
console.log(` 模式: ${mode}`);
|
||||
|
||||
const snapshot = readSnapshot();
|
||||
if (!snapshot) {
|
||||
console.log(' ⚠️ 快照不可用,先生成快照...');
|
||||
try {
|
||||
const { generateSnapshot } = require('./generate-system-snapshot');
|
||||
generateSnapshot();
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const freshSnapshot = readSnapshot();
|
||||
const token = getNotionToken();
|
||||
|
||||
if (!token) {
|
||||
console.log(' ⚠️ NOTION_TOKEN 未设置 — 仅执行本地更新');
|
||||
updateSignalBus(freshSnapshot);
|
||||
writeLocalSignalLog(generateSignalId(), generateTraceId(), buildSnapshotSummary(freshSnapshot));
|
||||
console.log('');
|
||||
console.log('💡 要启用 Notion 同步,请设置:');
|
||||
console.log(' SNAPSHOT_DB_ID=<notion-database-id>');
|
||||
console.log('💡 要启用 Notion 同步,请在 GitHub Secrets 中确认以下密钥:');
|
||||
console.log(' NOTION_TOKEN (或 NOTION_API_KEY)');
|
||||
console.log(' SIGNAL_LOG_DB_ID');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔄 正在同步快照到 Notion...');
|
||||
const page = buildSnapshotPage(snapshot, dbId);
|
||||
let signalOk = false;
|
||||
let snapshotOk = false;
|
||||
|
||||
try {
|
||||
const result = await notionRequest('POST', 'pages', page);
|
||||
if (result.status === 200) {
|
||||
console.log('✅ 快照已同步到 Notion');
|
||||
console.log(` Page ID: ${result.data?.id}`);
|
||||
} else {
|
||||
console.error('❌ Notion 同步失败:', result.status, JSON.stringify(result.data).substring(0, 500));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('❌ Notion 请求失败:', err.message);
|
||||
// 通道 1: 信号日志(始终尝试)
|
||||
signalOk = await writeSignalLog(freshSnapshot);
|
||||
|
||||
// 通道 2: 快照页面(仅 full 模式)
|
||||
if (mode === 'full') {
|
||||
snapshotOk = await writeSnapshotPage(freshSnapshot);
|
||||
}
|
||||
|
||||
// 通道 3: 信号总线(始终更新)
|
||||
updateSignalBus(freshSnapshot);
|
||||
|
||||
console.log('');
|
||||
console.log('📊 同步结果:');
|
||||
console.log(` 信号日志: ${signalOk ? '✅ 已写入' : '❌ 失败'}`);
|
||||
if (mode === 'full') {
|
||||
console.log(` 快照页面: ${snapshotOk ? '✅ 已创建' : '⬜ 跳过或失败'}`);
|
||||
}
|
||||
console.log(' 信号总线: ✅ 已更新');
|
||||
}
|
||||
|
||||
// CLI 入口
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.includes('--health')) {
|
||||
healthCheck().then(ok => process.exit(ok ? 0 : 1));
|
||||
} else {
|
||||
const mode = args.includes('--mode') ? args[args.indexOf('--mode') + 1] : 'signal';
|
||||
syncToNotion(mode).catch(err => {
|
||||
console.error(`❌ 同步失败: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
syncToNotion();
|
||||
}
|
||||
module.exports = { syncToNotion, healthCheck, readSnapshot };
|
||||
|
||||
module.exports = { syncToNotion, readSnapshot };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"signal_id": "SIG-20260327-166",
|
||||
"trace_id": "TRC-20260327-506",
|
||||
"type": "GL-SNAPSHOT",
|
||||
"timestamp": "2026-03-27T02:27:56.395Z",
|
||||
"sender": "铸渊",
|
||||
"receiver": "霜砚",
|
||||
"direction": "GitHub→Notion",
|
||||
"summary": "状态: awakened\n指令: SY-CMD-AWK-008 → SY-CMD-FUS-009\nWorkflow: 48 active, 6 core, 54 archived\nRuns: 731\nONT-PATCH: ONT-PATCH-007, ONT-PATCH-008, ONT-PATCH-010, ONT-PATCH-011\n健康: improving | Core: all healthy\nAlive: ZY-WF-听潮, ZY-WF-锻心, ZY-WF-织脉, ZY-WF-映阁, ZY-WF-守夜, ZY-WF-试镜",
|
||||
"result": "成功",
|
||||
"esp_version": "2.0-notion"
|
||||
}
|
||||
|
|
@ -1,205 +1,10 @@
|
|||
{
|
||||
"description": "铸渊信号日志目录索引 · AGE OS 信号协议(Notion API 直连)",
|
||||
"last_updated": "2026-03-26T11:37:52Z",
|
||||
"total_count": 22,
|
||||
"signals": [
|
||||
{
|
||||
"signal_id": "SIG-20260326-022",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001-S1",
|
||||
"type": "GL-SYSLOG",
|
||||
"timestamp": "2026-03-26T11:37:52Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001-S1 · SYSLOG 回执 · Phase S0-S5 执行报告 · 天眼v3.0启动+合并膜排查+子域名沙箱部署",
|
||||
"related_dev": null,
|
||||
"file": "syslog-ageos-tower-S1-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-021",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001-S1",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-26T11:37:52Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001-S1 · 天眼部署后扫描 · Phase S4 · 无新增 RED · GitHub 侧已同步",
|
||||
"related_dev": null,
|
||||
"file": "skyeye-post-deploy-S1.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-020",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001-S1",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-26T11:37:52Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001-S1 · 天眼v3.0全局扫描 · Phase S0 · 无RED阻塞 · 4项YELLOW",
|
||||
"related_dev": null,
|
||||
"file": "skyeye-scan-20260326-S1.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-019",
|
||||
"trace_id": "ZY-DIAG-SITE-2026-0326-001",
|
||||
"type": "GL-DIAG",
|
||||
"timestamp": "2026-03-26T07:50:00Z",
|
||||
"summary": "ZY-DIAG-SITE-2026-0326-001 · 天眼全局排查+部署链路诊断 · 根因: Nginx 404 + Branch protection 冲突 + syslog-pipeline required check 失效",
|
||||
"related_dev": null,
|
||||
"file": "skyeye-diag-site-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-018",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-SYSLOG",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001 · AGE OS 塔台架构升级回执 · Phase 0-7 完成 · 塔台模型+配额守卫+行业代表制+MCP应用+子域名隔离",
|
||||
"related_dev": null,
|
||||
"file": "syslog-ageos-tower-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-017",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-DIAG",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001 · 备用站 GitHub Pages 推送失败诊断 · 根因A 天眼合并膜阻止直接push",
|
||||
"related_dev": null,
|
||||
"file": "diag-pages-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-016",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "SKYEYE-SCAN-20260326-001 · 天眼全局基础设施扫描 · 102 workflows 全部正常 · 结构完整 · Pages Active",
|
||||
"related_dev": null,
|
||||
"file": "skyeye-scan-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260325-015",
|
||||
"trace_id": "ZY-HUMANSIDE-FIX-2026-0325-002",
|
||||
"type": "GL-SYSLOG",
|
||||
"timestamp": "2026-03-25T02:16:00Z",
|
||||
"summary": "ZY-HUMANSIDE-FIX-2026-0325-002 · 人类使用侧全面修复回执 · Phase 0-7 代码侧完成 · CD管线+模块导航+API代理+微信登录预留",
|
||||
"related_dev": null,
|
||||
"file": "syslog-humanside-fix-20260325.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260325-014",
|
||||
"trace_id": "ZY-HUMANSIDE-FIX-2026-0325-002",
|
||||
"type": "GL-DIAG",
|
||||
"timestamp": "2026-03-25T02:16:00Z",
|
||||
"summary": "ZY-HUMANSIDE-FIX-2026-0325-002 · 人类使用侧全面诊断报告 · 服务器+域名+CD管线+Persona Studio 诊断",
|
||||
"related_dev": null,
|
||||
"file": "diag-humanside-20260325.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260325-013",
|
||||
"trace_id": "TRC-20260325-DEVSYNC-REBUILD",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-25T09:53:00+08:00",
|
||||
"summary": "ZY-DEVSYNC-REBUILD-2026-0325-001 · dev-status.json 同步机制重建 · 霜砚签发制 v1.0 上线 · 天眼三次修复闭环上线 · Phase 0-6 正常完成",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260325-013.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260324-012",
|
||||
"trace_id": "TRC-20260324-SYSBOOT",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-24T14:30:00.000Z",
|
||||
"summary": "ZY-SYSBOOT-2026-0324-001 · 铸渊全系统启动序列完成 · Phase 0-6 正常完成",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260324-012.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260315-011",
|
||||
"trace_id": "TRC-20260315-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-15T03:52:14.694Z",
|
||||
"summary": "铸渊 PSP 巡检完成 · 发现 6 个问题 · 自动修复 0 项",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260315-011.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260315-010",
|
||||
"trace_id": "TRC-20260315-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-15T03:52:14.694Z",
|
||||
"summary": "CHK-G05: 6 个 CI 失败",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260315-010.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260314-009",
|
||||
"trace_id": "TRC-20260314-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-14T03:29:04.256Z",
|
||||
"summary": "铸渊 PSP 巡检通过 · 全部检查项 ✅",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260314-009.json"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-12T03:34:18.847Z",
|
||||
"summary": "铸渊 PSP 巡检通过 · 全部检查项 ✅",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260312-007.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260310-006",
|
||||
"trace_id": "TRC-20260310-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-10T03:28:47.184Z",
|
||||
"summary": "铸渊 PSP 巡检完成 · 发现 3 个问题 · 自动修复 0 项",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260310-006.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260310-005",
|
||||
"trace_id": "TRC-20260310-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-10T03:28:47.184Z",
|
||||
"summary": "CHK-G05: 3 个 CI 失败",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260310-005.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260309-004",
|
||||
"trace_id": "TRC-20260309-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-09T03:36:48.868Z",
|
||||
"summary": "铸渊 PSP 巡检通过 · 全部检查项 ✅",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260309-004.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260308-003",
|
||||
"trace_id": "TRC-20260308-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-08T03:32:30.449Z",
|
||||
"summary": "铸渊 PSP 巡检通过 · 全部检查项 ✅",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260308-003.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260307-002",
|
||||
"trace_id": "TRC-20260307-PSP",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-07T04:45:55.670Z",
|
||||
"summary": "铸渊 PSP 巡检完成 · 发现 1 个问题 · 自动修复 0 项",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-20260307-002.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-2026-0306-001",
|
||||
"trace_id": "TRC-2026-0306-001",
|
||||
"type": "GL-CMD",
|
||||
"timestamp": "2026-03-06T23:00:00+08:00",
|
||||
"summary": "AGE OS 系统初始化 · 铸渊 GitHub 端激活",
|
||||
"related_dev": null,
|
||||
"file": "2026-03/SIG-2026-0306-001.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
[
|
||||
{
|
||||
"signal_id": "SIG-20260327-166",
|
||||
"trace_id": "TRC-20260327-506",
|
||||
"type": "GL-SNAPSHOT",
|
||||
"timestamp": "2026-03-27T02:27:56.395Z",
|
||||
"summary": "状态: awakened\n指令: SY-CMD-AWK-008 → SY-CMD-FUS-009\nWorkflow: 48 active, 6 core, 54 archived\nRuns: 731\nONT-PATCH: ONT-PATCH-007, ONT-PATCH-008, ONT-PATCH-010, ONT-PATCH-011\n健康: improving | Core: all heal",
|
||||
"file": "2026-03/SIG-20260327-166.json"
|
||||
}
|
||||
]
|
||||
Loading…
Reference in New Issue