/** * config-bridge.js — 配置格式桥接 * * 把我们的简化配置格式转成 OpenClaw 的 ClawdbotConfig 格式。 * * OpenClaw 的 resolveFeishuAccount({ cfg }) 从 cfg.channels.feishu 读取凭证, * resolveAgentRoute() 从 cfg.agents 读取 Agent 路由配置。 * * 用法: * const { bridgeConfig, loadPluginConfig } = require('./plugins/config-bridge'); * const openclawConfig = bridgeConfig(ourConfig); */ const path = require('path'); const fs = require('fs'); /** * 从环境变量构建飞书 channel 配置。 * 环境变量优先,plugins.json 仅作为 fallback。 * * @returns {Object|null} 飞书配置,无环境变量时返回 null */ function feishuFromEnv() { const appId = process.env.FEISHU_APP_ID; const appSecret = process.env.FEISHU_APP_SECRET; if (!appId || !appSecret) return null; return { enabled: process.env.FEISHU_ENABLED !== 'false', appId, appSecret, verificationToken: process.env.FEISHU_VERIFICATION_TOKEN || '', encryptKey: process.env.FEISHU_ENCRYPT_KEY || '', mode: process.env.FEISHU_MODE || 'ws', allowFrom: process.env.FEISHU_ALLOW_FROM ? process.env.FEISHU_ALLOW_FROM.split(',').map(s => s.trim()) : [], }; } /** * 加载插件配置文件。 * * 优先级:环境变量 > plugins.json > 默认值 * * @param {string} [configPath] - 配置文件路径,默认 server/config/plugins.json * @returns {Object} 插件配置 */ function loadPluginConfig(configPath) { const defaultPath = path.resolve(__dirname, '../../config/plugins.json'); const filePath = configPath || defaultPath; // 1. 从 plugins.json 加载基础配置(fallback) let fileConfig = {}; if (fs.existsSync(filePath)) { const raw = fs.readFileSync(filePath, 'utf-8'); fileConfig = JSON.parse(raw); } // 2. 环境变量覆盖飞书配置 const envFeishu = feishuFromEnv(); if (envFeishu) { fileConfig.channels = fileConfig.channels || {}; // 合并:env 覆盖 json 中的同名字段 fileConfig.channels.feishu = { ...(fileConfig.channels.feishu || {}), ...envFeishu, }; } return fileConfig; } /** * 把我们的简化配置转成 OpenClaw 的 ClawdbotConfig 格式。 * * OpenClaw 的配置格式参考: * - feishu: extensions/feishu/src/channel.ts 的 configSchema * - agents: config/zod-schema.core.ts * * @param {Object} ourConfig - 我们的配置 * @param {Object} [ourConfig.channels] - 通道配置 * @param {Object} [ourConfig.channels.feishu] - 飞书配置 * @param {string} [ourConfig.channels.feishu.appId] - 飞书 App ID * @param {string} [ourConfig.channels.feishu.appSecret] - 飞书 App Secret * @param {string} [ourConfig.channels.feishu.verificationToken] - 验证 Token * @param {string} [ourConfig.channels.feishu.encryptKey] - 加密 Key * @param {string} [ourConfig.agentId] - 默认 Agent ID * @returns {Object} OpenClaw 兼容配置 */ function bridgeConfig(ourConfig) { const cfg = { // Agent 配置 agents: {}, // 通道配置 channels: {}, // 插件配置 plugins: { entries: {}, loadPaths: ourConfig.extensionPaths || [], }, }; // 飞书通道 if (ourConfig.channels?.feishu) { const feishu = ourConfig.channels.feishu; cfg.channels.feishu = { enabled: feishu.enabled !== false, appId: feishu.appId || '', appSecret: feishu.appSecret || '', verificationToken: feishu.verificationToken || '', encryptKey: feishu.encryptKey || '', // WebSocket 模式(推荐,无需公网 IP) connectionMode: feishu.connectionMode || feishu.mode || 'websocket', // Webhook 模式的 port(仅 webhook 模式需要) webhookPort: feishu.webhookPort || feishu.port || 9321, // 访问控制 —— 默认 pairing 模式(需审批) dmPolicy: feishu.dmPolicy || 'pairing', allowFrom: feishu.allowFrom || [], groupPolicy: feishu.groupPolicy || 'open', groupAllowFrom: feishu.groupAllowFrom || [], }; cfg.plugins.entries.feishu = { enabled: true }; } // 默认 Agent 路由 → 我们的编排器 cfg.agents.default = { agentId: ourConfig.agentId || 'orchestrator', }; // DM(私聊)默认路由 cfg.dm = { allowFrom: ['*'], // 允许所有用户 }; return cfg; } module.exports = { loadPluginConfig, bridgeConfig };