@wecom/wecom-openclaw-plugin 2026.3.24-beta.3 → 2026.3.24-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -325,6 +325,36 @@ async function loadOutboundMediaFromUrl(mediaUrl, options = {}) {
325
325
  return loadOutboundMediaFromUrlFallback(mediaUrl, options);
326
326
  }
327
327
  // ============================================================================
328
+ // addWildcardAllowFrom —— 向 allowFrom 列表添加通配符 "*"
329
+ // ============================================================================
330
+ /** fallback 版 addWildcardAllowFrom:确保列表中包含 "*" 通配符 */
331
+ function addWildcardAllowFromFallback(allowFrom) {
332
+ if (allowFrom.includes("*")) {
333
+ return allowFrom;
334
+ }
335
+ return [...allowFrom, "*"];
336
+ }
337
+ /**
338
+ * 向 allowFrom 列表添加通配符 "*"(兼容入口)
339
+ *
340
+ * 当 dmPolicy 为 "open" 时,需要确保 allowFrom 中包含 "*" 以允许所有来源。
341
+ * 优先使用 SDK 版本(plugin-sdk/setup 或 plugin-sdk/core),不可用时使用 fallback。
342
+ *
343
+ * 注意:此函数为同步函数,与 SDK 原始签名一致。
344
+ * SDK 引用在模块加载时异步探测并缓存,调用时同步读取缓存。
345
+ */
346
+ function addWildcardAllowFrom(allowFrom) {
347
+ if (_cachedAddWildcardAllowFrom) {
348
+ try {
349
+ return _cachedAddWildcardAllowFrom(allowFrom);
350
+ }
351
+ catch {
352
+ // SDK 版本异常,降级到 fallback
353
+ }
354
+ }
355
+ return addWildcardAllowFromFallback(allowFrom);
356
+ }
357
+ // ============================================================================
328
358
  // getDefaultMediaLocalRoots —— 获取默认媒体本地路径白名单
329
359
  // ============================================================================
330
360
  /** 解析 openclaw 状态目录 */
@@ -2161,6 +2191,209 @@ function resolveWeComAccount(cfg) {
2161
2191
  config: wecomConfig,
2162
2192
  };
2163
2193
  }
2194
+ /**
2195
+ * 设置企业微信账户配置
2196
+ */
2197
+ function setWeComAccount(cfg, account) {
2198
+ const existing = (cfg.channels?.[CHANNEL_ID] ?? {});
2199
+ const merged = {
2200
+ enabled: account.enabled ?? existing?.enabled ?? true,
2201
+ botId: account.botId ?? existing?.botId ?? "",
2202
+ secret: account.secret ?? existing?.secret ?? "",
2203
+ allowFrom: account.allowFrom ?? existing?.allowFrom,
2204
+ dmPolicy: account.dmPolicy ?? existing?.dmPolicy,
2205
+ // 以下字段仅在已有配置值或显式传入时才写入,onboarding 时不主动生成
2206
+ ...(account.websocketUrl || existing?.websocketUrl
2207
+ ? { websocketUrl: account.websocketUrl ?? existing?.websocketUrl }
2208
+ : {}),
2209
+ ...(account.name || existing?.name
2210
+ ? { name: account.name ?? existing?.name }
2211
+ : {}),
2212
+ ...(account.sendThinkingMessage !== undefined || existing?.sendThinkingMessage !== undefined
2213
+ ? { sendThinkingMessage: account.sendThinkingMessage ?? existing?.sendThinkingMessage }
2214
+ : {}),
2215
+ };
2216
+ return {
2217
+ ...cfg,
2218
+ channels: {
2219
+ ...cfg.channels,
2220
+ [CHANNEL_ID]: merged,
2221
+ },
2222
+ };
2223
+ }
2224
+
2225
+ /**
2226
+ * 企业微信 setupWizard — 声明式 CLI setup wizard 配置。
2227
+ *
2228
+ * 框架通过 plugin.setupWizard 字段识别并驱动 channel 的引导配置流程。
2229
+ */
2230
+ // ============================================================================
2231
+ // ChannelSetupAdapter — 框架用于应用配置输入的适配器
2232
+ // ============================================================================
2233
+ const wecomSetupAdapter = {
2234
+ applyAccountConfig: ({ cfg, input }) => {
2235
+ const patch = {};
2236
+ if (input.token !== undefined) {
2237
+ patch.botId = String(input.token).trim();
2238
+ }
2239
+ if (input.privateKey !== undefined) {
2240
+ patch.secret = String(input.privateKey).trim();
2241
+ }
2242
+ // 如果是首次配置,默认启用
2243
+ const account = resolveWeComAccount(cfg);
2244
+ if (!account.botId && !account.secret) {
2245
+ patch.enabled = true;
2246
+ }
2247
+ return setWeComAccount(cfg, patch);
2248
+ },
2249
+ };
2250
+ // ============================================================================
2251
+ // DM Policy 配置
2252
+ // ============================================================================
2253
+ /**
2254
+ * 设置企业微信 dmPolicy
2255
+ */
2256
+ function setWeComDmPolicy(cfg, dmPolicy) {
2257
+ const account = resolveWeComAccount(cfg);
2258
+ const existingAllowFrom = account.config.allowFrom ?? [];
2259
+ const allowFrom = dmPolicy === "open"
2260
+ ? addWildcardAllowFrom(existingAllowFrom.map((x) => String(x)))
2261
+ : existingAllowFrom.map((x) => String(x));
2262
+ return setWeComAccount(cfg, {
2263
+ dmPolicy,
2264
+ allowFrom,
2265
+ });
2266
+ }
2267
+ const dmPolicy = {
2268
+ label: "企业微信",
2269
+ channel: CHANNEL_ID,
2270
+ policyKey: `channels.${CHANNEL_ID}.dmPolicy`,
2271
+ allowFromKey: `channels.${CHANNEL_ID}.allowFrom`,
2272
+ getCurrent: (cfg) => {
2273
+ const account = resolveWeComAccount(cfg);
2274
+ return account.config.dmPolicy ?? "open";
2275
+ },
2276
+ setPolicy: (cfg, policy) => {
2277
+ return setWeComDmPolicy(cfg, policy);
2278
+ },
2279
+ promptAllowFrom: async ({ cfg, prompter }) => {
2280
+ const account = resolveWeComAccount(cfg);
2281
+ const existingAllowFrom = account.config.allowFrom ?? [];
2282
+ const entry = await prompter.text({
2283
+ message: "企业微信允许来源(用户ID或群组ID,逗号分隔)",
2284
+ placeholder: "user123, group456",
2285
+ initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : undefined,
2286
+ });
2287
+ const allowFrom = String(entry ?? "")
2288
+ .split(/[\n,;]+/g)
2289
+ .map((s) => s.trim())
2290
+ .filter(Boolean);
2291
+ return setWeComAccount(cfg, { allowFrom });
2292
+ },
2293
+ };
2294
+ // ============================================================================
2295
+ // ChannelSetupWizard — 声明式 setup wizard 配置
2296
+ // ============================================================================
2297
+ const wecomSetupWizard = {
2298
+ channel: CHANNEL_ID,
2299
+ // ── 状态 ──────────────────────────────────────────────────────────────
2300
+ status: {
2301
+ configuredLabel: "已配置 ✓",
2302
+ unconfiguredLabel: "需要 Bot ID 和 Secret",
2303
+ configuredHint: "已配置",
2304
+ unconfiguredHint: "需要设置",
2305
+ resolveConfigured: ({ cfg }) => {
2306
+ const account = resolveWeComAccount(cfg);
2307
+ return Boolean(account.botId?.trim() && account.secret?.trim());
2308
+ },
2309
+ resolveStatusLines: ({ cfg, configured }) => {
2310
+ return [`企业微信: ${configured ? "已配置" : "需要 Bot ID 和 Secret"}`];
2311
+ },
2312
+ },
2313
+ // ── 引导说明 ──────────────────────────────────────────────────────────
2314
+ introNote: {
2315
+ title: "企业微信设置",
2316
+ lines: [
2317
+ "企业微信机器人需要以下配置信息:",
2318
+ "1. Bot ID: 企业微信机器人 ID",
2319
+ "2. Secret: 企业微信机器人密钥",
2320
+ ],
2321
+ shouldShow: ({ cfg }) => {
2322
+ const account = resolveWeComAccount(cfg);
2323
+ return !account.botId?.trim() || !account.secret?.trim();
2324
+ },
2325
+ },
2326
+ // ── 凭据输入 ──────────────────────────────────────────────────────────
2327
+ credentials: [
2328
+ {
2329
+ inputKey: "token",
2330
+ providerHint: "企业微信",
2331
+ credentialLabel: "Bot ID",
2332
+ envPrompt: "使用环境变量中的 Bot ID?",
2333
+ keepPrompt: "Bot ID 已配置,保留当前值?",
2334
+ inputPrompt: "企业微信机器人 Bot ID",
2335
+ inspect: ({ cfg }) => {
2336
+ const account = resolveWeComAccount(cfg);
2337
+ const hasValue = Boolean(account.botId?.trim());
2338
+ return {
2339
+ accountConfigured: hasValue,
2340
+ hasConfiguredValue: hasValue,
2341
+ resolvedValue: account.botId || undefined,
2342
+ };
2343
+ },
2344
+ applySet: ({ cfg, resolvedValue }) => {
2345
+ return setWeComAccount(cfg, { botId: resolvedValue });
2346
+ },
2347
+ },
2348
+ {
2349
+ inputKey: "privateKey",
2350
+ providerHint: "企业微信",
2351
+ credentialLabel: "Secret",
2352
+ envPrompt: "使用环境变量中的 Secret?",
2353
+ keepPrompt: "Secret 已配置,保留当前值?",
2354
+ inputPrompt: "企业微信机器人 Secret",
2355
+ inspect: ({ cfg }) => {
2356
+ const account = resolveWeComAccount(cfg);
2357
+ const hasValue = Boolean(account.secret?.trim());
2358
+ return {
2359
+ accountConfigured: hasValue,
2360
+ hasConfiguredValue: hasValue,
2361
+ resolvedValue: account.secret || undefined,
2362
+ };
2363
+ },
2364
+ applySet: ({ cfg, resolvedValue }) => {
2365
+ return setWeComAccount(cfg, { secret: resolvedValue });
2366
+ },
2367
+ },
2368
+ ],
2369
+ // ── 完成后的最终处理 ──────────────────────────────────────────────────
2370
+ finalize: async ({ cfg }) => {
2371
+ // 确保配置完成后 channel 处于启用状态
2372
+ const account = resolveWeComAccount(cfg);
2373
+ if (account.botId?.trim() && account.secret?.trim() && !account.enabled) {
2374
+ return { cfg: setWeComAccount(cfg, { enabled: true }) };
2375
+ }
2376
+ return undefined;
2377
+ },
2378
+ // ── 完成提示 ──────────────────────────────────────────────────────────
2379
+ completionNote: {
2380
+ title: "企业微信配置完成",
2381
+ lines: [
2382
+ "企业微信机器人已配置完成。",
2383
+ "运行 `openclaw start` 启动服务。",
2384
+ ],
2385
+ shouldShow: ({ cfg }) => {
2386
+ const account = resolveWeComAccount(cfg);
2387
+ return Boolean(account.botId?.trim() && account.secret?.trim());
2388
+ },
2389
+ },
2390
+ // ── DM 策略 ──────────────────────────────────────────────────────────
2391
+ dmPolicy,
2392
+ // ── 禁用 ─────────────────────────────────────────────────────────────
2393
+ disable: (cfg) => {
2394
+ return setWeComAccount(cfg, { enabled: false });
2395
+ },
2396
+ };
2164
2397
 
2165
2398
  /**
2166
2399
  * 使用 SDK 的 sendMessage 主动发送企业微信消息
@@ -2217,6 +2450,8 @@ const wecomPlugin = {
2217
2450
  // Pairing approved for user
2218
2451
  },
2219
2452
  },
2453
+ setupWizard: wecomSetupWizard,
2454
+ setup: wecomSetupAdapter,
2220
2455
  capabilities: {
2221
2456
  chatTypes: ["direct", "group"],
2222
2457
  reactions: false,