config-bridge.js 4.31 KB
/**
 * 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 };