config-bridge.js
4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* 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 };