// ═══════════════════════════════════════ // 铸渊开发Agent · Notion 桥接模块 // 读取规划数据库 · 写入执行进度 // ═══════════════════════════════════════ const https = require("https"); const NOTION_API = "https://api.notion.com/v1"; const NOTION_VERSION = "2022-06-28"; function getHeaders() { const token = process.env.NOTION_TOKEN; if (!token) { console.warn("[NotionBridge] NOTION_TOKEN 未配置"); return null; } return { "Authorization": "Bearer " + token, "Content-Type": "application/json", "Notion-Version": NOTION_VERSION }; } function notionRequest(endpoint, method, body) { return new Promise((resolve, reject) => { const headers = getHeaders(); if (!headers) return reject(new Error("NOTION_TOKEN 未配置")); const url = new URL(NOTION_API + endpoint); const options = { hostname: url.hostname, path: url.pathname + url.search, method: method, headers: headers }; const req = https.request(options, (res) => { let data = ""; res.on("data", (chunk) => data += chunk); res.on("end", () => { try { const json = JSON.parse(data); if (res.statusCode >= 400) { reject(new Error("Notion API " + res.statusCode + ": " + (json.message || data))); } else { resolve(json); } } catch (e) { reject(new Error("解析失败: " + data.slice(0, 200))); } }); }); req.on("error", reject); if (body) req.write(JSON.stringify(body)); req.end(); }); } /** * 从 Notion 数据库读取规划 * @param {string} databaseId - Notion 数据库 ID * @returns {Promise} 规划条目列表 */ async function fetchPlans(databaseId) { const result = await notionRequest("/databases/" + databaseId + "/query", "POST", { sorts: [{ property: "Created", direction: "descending" }], page_size: 10 }); return (result.results || []).map((page) => { const props = page.properties || {}; return { notion_page_id: page.id, title: props.Name?.title?.[0]?.plain_text || "无标题", status: props.Status?.status?.name || "unknown", plan_id: props.PlanID?.rich_text?.[0]?.plain_text || "", created: page.created_time }; }); } /** * 更新 Notion 中某条规划的状态 * @param {string} pageId - Notion 页面 ID * @param {string} status - 新状态 * @param {object} details - 额外详情 */ async function updatePlanStatus(pageId, status, details = {}) { const properties = { Status: { status: { name: status } } }; if (details.log_url) { properties.LogURL = { url: details.log_url }; } return notionRequest("/pages/" + pageId, "PATCH", { properties }); } /** * 向 Notion 数据库追加执行日志 * @param {string} databaseId - Notion 数据库 ID * @param {object} entry - 日志条目 { plan_id, task_id, action, result, time } */ async function appendLog(databaseId, entry) { return notionRequest("/pages", "POST", { parent: { database_id: databaseId }, properties: { Name: { title: [{ text: { content: entry.plan_id + " · " + entry.task_id } }] }, Status: { status: { name: entry.result === "ok" ? "Done" : "Failed" } }, LogTime: { date: { start: entry.time || new Date().toISOString() } }, Detail: { rich_text: [{ text: { content: entry.detail || entry.action } }] } } }); } module.exports = { fetchPlans, updatePlanStatus, appendLog };