/** * ๐Ÿ”„ ้“ธๆธŠ็ณป็ปŸๅฟซ็…ง โ†’ Notion ๅŒๅ‘ๅŒๆญฅๅผ•ๆ“Ž * sync-snapshot-to-notion.js * * ไฝฟ็”จๅทฒๅปบ็ซ‹็š„ GL-ACK ไฟกๅทๅ่ฎฎ๏ผŒๅฐ†็ณป็ปŸๅฟซ็…งๆŽจ้€ๅˆฐ Notion ่ฎค็Ÿฅๅฑ‚ใ€‚ * ๅ…ผๅฎน notion-signal-bridge.js ็š„ไฟกๅทๆ ผๅผๅ’Œ HLDP ๅ่ฎฎใ€‚ * * ้€šไฟก้€š้“๏ผˆๆŒ‰ไผ˜ๅ…ˆ็บง๏ผ‰๏ผš * 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_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'); 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; } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // Notion API ๅŸบ็ก€่ฐƒ็”จ๏ผˆๅค็”จ notion-signal-bridge.js ๆจกๅผ๏ผ‰ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• function notionRequest(method, endpoint, body) { const token = getNotionToken(); if (!token) return Promise.reject(new Error('NOTION_TOKEN ๆœช่ฎพ็ฝฎ')); return new Promise((resolve, reject) => { const payload = body ? JSON.stringify(body) : ''; const opts = { hostname: NOTION_API_HOSTNAME, port: 443, path: endpoint.startsWith('/') ? endpoint : `/v1/${endpoint}`, method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', 'Notion-Version': NOTION_VERSION, }, }; if (payload) opts.headers['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 ${method} ${endpoint} โ†’ ${res.statusCode}: ${parsed.message || data.slice(0, 300)}`)); } } catch { reject(new Error(`Notion ๅ“ๅบ”่งฃๆžๅคฑ่ดฅ: ${data.slice(0, 200)}`)); } }); }); req.on('error', reject); req.setTimeout(30000, () => req.destroy(new Error('Notion ่ฏทๆฑ‚่ถ…ๆ—ถ (30s)'))); if (payload) req.write(payload); req.end(); }); } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ไฟกๅท็”Ÿๆˆ๏ผˆGL-SNAPSHOT ไฟกๅท็ฑปๅž‹ ยท ๅŸบไบŽ GL-ACK ๅ่ฎฎๆ‰ฉๅฑ•๏ผ‰ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• function generateSignalId() { const now = new Date(); const date = now.toISOString().split('T')[0].replace(/-/g, ''); // ่ฏปๅ–็Žฐๆœ‰ index.json๏ผŒไป…็ปŸ่ฎกๅฝ“ๆ—ฅไฟกๅท็š„ๆœ€ๅคง้กบๅบๅท let nextSeq = 1; try { const indexPath = path.join(SIGNAL_LOG_DIR, 'index.json'); const raw = JSON.parse(fs.readFileSync(indexPath, 'utf8')); const signals = Array.isArray(raw) ? raw : (raw.signals || []); const pattern = new RegExp(`^SIG-${date}-(\\d{3})$`); for (const sig of signals) { const match = (sig.signal_id || '').match(pattern); if (match) { const seq = parseInt(match[1], 10); if (seq >= nextSeq) nextSeq = seq + 1; } } } catch {} return `SIG-${date}-${String(nextSeq).padStart(3, '0')}`; } function generateTraceId() { const now = new Date(); const date = now.toISOString().split('T')[0].replace(/-/g, ''); // ่ฏปๅ–็Žฐๆœ‰ index.json๏ผŒไป…็ปŸ่ฎกๅฝ“ๆ—ฅ trace ็š„ๆœ€ๅคง้กบๅบๅท let nextSeq = 1; try { const indexPath = path.join(SIGNAL_LOG_DIR, 'index.json'); const raw = JSON.parse(fs.readFileSync(indexPath, 'utf8')); const signals = Array.isArray(raw) ? raw : (raw.signals || []); const pattern = new RegExp(`^TRC-${date}-(\\d{3})$`); for (const sig of signals) { const match = (sig.trace_id || '').match(pattern); if (match) { const seq = parseInt(match[1], 10); if (seq >= nextSeq) nextSeq = seq + 1; } } } catch {} return `TRC-${date}-${String(nextSeq).padStart(3, '0')}`; } 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: { 'ไฟกๅท็ผ–ๅท': { 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 indexData = { description: '้“ธๆธŠไฟกๅทๆ—ฅๅฟ—็›ฎๅฝ•็ดขๅผ• ยท AGE OS ไฟกๅทๅ่ฎฎ', last_updated: null, total_count: 0, signals: [] }; try { const raw = JSON.parse(fs.readFileSync(indexPath, 'utf8')); if (Array.isArray(raw)) { // ๅ…ผๅฎน๏ผšๅฆ‚ๆžœๅทฒ่ขซๆ”นไธบๆ•ฐ็ป„ๆ ผๅผ๏ผŒๆขๅคไธบๅฏน่ฑกๆ ผๅผ indexData.signals = raw; indexData.total_count = raw.length; } else { indexData = raw; if (!Array.isArray(indexData.signals)) indexData.signals = []; } } catch {} indexData.signals.unshift({ 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 (indexData.signals.length > 200) indexData.signals = indexData.signals.slice(0, 200); indexData.last_updated = now.toISOString(); indexData.total_count = indexData.signals.length; fs.writeFileSync(indexPath, JSON.stringify(indexData, 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: 'code', code: { rich_text: [{ text: { content: snapshotJSON } }], language: 'json' }, }, ], }; try { const result = await notionRequest('POST', '/v1/pages', page); console.log(` โœ… ๅฟซ็…ง้กต้ขๅทฒๅˆ›ๅปบ: ${result.id}`); return true; } catch (err) { console.error(` โŒ ๅฟซ็…ง้กต้ขๅˆ›ๅปบๅคฑ่ดฅ: ${err.message}`); return false; } } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ้€š้“ 3: ไฟกๅทๆ€ป็บฟๆ›ดๆ–ฐ๏ผˆๆœฌๅœฐ + TCS ๅ่ฎฎ๏ผ‰ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• 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 ๅŒๆญฅ๏ผŒ่ฏทๅœจ GitHub Secrets ไธญ็กฎ่ฎคไปฅไธ‹ๅฏ†้’ฅ:'); console.log(' NOTION_TOKEN (ๆˆ– NOTION_API_KEY)'); console.log(' SIGNAL_LOG_DB_ID'); return; } let signalOk = false; let snapshotOk = false; // ้€š้“ 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); }); } } module.exports = { syncToNotion, healthCheck, readSnapshot };