@wecom/wecom-openclaw-plugin 2026.4.3 → 2026.4.7-beta.2

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.
@@ -38,6 +38,8 @@ export declare const MESSAGE_STATE_TTL_MS: number;
38
38
  export declare const MESSAGE_STATE_CLEANUP_INTERVAL_MS = 60000;
39
39
  /** messageStates Map 最大条目数 */
40
40
  export declare const MESSAGE_STATE_MAX_SIZE = 500;
41
+ /** WebSocket 全局实例键 */
42
+ export declare const GLOBAL_WS_CLIENT_KEY: "__wecom_openclaw_ws_client_instances";
41
43
  /** "思考中"流式消息占位内容 */
42
44
  export declare const THINKING_MESSAGE = "<think></think>";
43
45
  /** 仅包含图片时的消息占位符 */
package/dist/src/const.js CHANGED
@@ -45,6 +45,8 @@ export const MESSAGE_STATE_TTL_MS = 10 * 60 * 1000;
45
45
  export const MESSAGE_STATE_CLEANUP_INTERVAL_MS = 60_000;
46
46
  /** messageStates Map 最大条目数 */
47
47
  export const MESSAGE_STATE_MAX_SIZE = 500;
48
+ /** WebSocket 全局实例键 */
49
+ export const GLOBAL_WS_CLIENT_KEY = "__wecom_openclaw_ws_client_instances";
48
50
  // ============================================================================
49
51
  // 消息模板
50
52
  // ============================================================================
@@ -15,6 +15,8 @@ import { getWeComWebSocket } from "../state-manager.js";
15
15
  import { MCP_GET_CONFIG_CMD, MCP_CONFIG_FETCH_TIMEOUT_MS } from "../const.js";
16
16
  import { withTimeout } from "../timeout.js";
17
17
  import { PLUGIN_VERSION } from "../version.js";
18
+ import { getWeComRuntime } from "../runtime.js";
19
+ import { resolveDefaultWeComAccountId, listWeComAccountIds, resolveWeComAccountMulti, } from "../accounts.js";
18
20
  // ============================================================================
19
21
  // 内部状态
20
22
  // ============================================================================
@@ -77,16 +79,75 @@ const inflightInitRequests = new Map();
77
79
  // ============================================================================
78
80
  // MCP 配置拉取与缓存
79
81
  // ============================================================================
82
+ /**
83
+ * 判断账户是否为长连接机器人(通过 botId + secret 配置)
84
+ */
85
+ function isWebSocketBot(cfg, accountId) {
86
+ const resolved = resolveWeComAccountMulti({ cfg, accountId });
87
+ return Boolean(resolved.botId?.trim() && resolved.secret?.trim());
88
+ }
89
+ /**
90
+ * 解析用于 MCP 调用的账户 ID
91
+ *
92
+ * 优先级:
93
+ * 1. 默认账户(如果是长连接机器人)
94
+ * 2. 第一个长连接机器人账户
95
+ * 3. 无可用账户时返回 null
96
+ */
97
+ function resolveMcpAccountId(cfg) {
98
+ const defaultAccountId = resolveDefaultWeComAccountId(cfg);
99
+ // 优先使用默认账户
100
+ if (isWebSocketBot(cfg, defaultAccountId)) {
101
+ return defaultAccountId;
102
+ }
103
+ // 默认账户非长连接,查找第一个长连接账户
104
+ const allIds = listWeComAccountIds(cfg);
105
+ for (const id of allIds) {
106
+ if (id !== defaultAccountId && isWebSocketBot(cfg, id)) {
107
+ console.log(`${LOG_TAG} 默认账户 "${defaultAccountId}" 非长连接模式,使用账户 "${id}" 获取 MCP 配置`);
108
+ return id;
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ /**
114
+ * 根据配置解析当前用于 MCP 的账户 ID
115
+ *
116
+ * 通过 PluginRuntime 读取全局配置,按以下优先级选择账户:
117
+ * 1. 默认账户(如果配置了 botId + secret)
118
+ * 2. 第一个配置了 botId + secret 的长连接账户
119
+ * 3. 无可用长连接账户时回退到默认账户(会导致后续 WSClient 获取失败)
120
+ */
121
+ function resolveCurrentAccountId() {
122
+ try {
123
+ const core = getWeComRuntime();
124
+ const cfg = core.config.loadConfig();
125
+ const mcpAccountId = resolveMcpAccountId(cfg);
126
+ if (mcpAccountId) {
127
+ return mcpAccountId;
128
+ }
129
+ // 无长连接账户,给出友好提示
130
+ console.warn(`${LOG_TAG} 未找到长连接模式的机器人账户(需配置 botId + secret),MCP 功能不可用`);
131
+ return resolveDefaultWeComAccountId(cfg);
132
+ }
133
+ catch {
134
+ // runtime 未初始化时回退到 "default"
135
+ return DEFAULT_ACCOUNT_ID;
136
+ }
137
+ }
80
138
  /**
81
139
  * 通过 WSClient 拉取指定 category 的 MCP 完整配置
82
140
  *
141
+ * 自动根据 defaultAccount 配置获取对应账户的 WSClient,无需手动传入 accountId。
142
+ *
83
143
  * @param category - MCP 品类名称,如 doc、contact
84
144
  * @returns 完整的 response.body 配置对象(至少包含 url 字段)
85
145
  */
86
146
  async function fetchMcpConfig(category) {
87
- const wsClient = getWeComWebSocket(DEFAULT_ACCOUNT_ID);
147
+ const accountId = resolveCurrentAccountId();
148
+ const wsClient = getWeComWebSocket(accountId);
88
149
  if (!wsClient) {
89
- throw new Error("WSClient 未连接,无法拉取 MCP 配置");
150
+ throw new Error(`WSClient 未连接 (accountId="${accountId}"),无法拉取 MCP 配置`);
90
151
  }
91
152
  const reqId = generateReqId("mcp_config");
92
153
  const response = await withTimeout(wsClient.reply({ headers: { req_id: reqId } }, { biz_type: category, plugin_version: PLUGIN_VERSION }, MCP_GET_CONFIG_CMD), MCP_CONFIG_FETCH_TIMEOUT_MS, `MCP config fetch for "${category}" timed out after ${MCP_CONFIG_FETCH_TIMEOUT_MS}ms`);
@@ -99,7 +160,7 @@ async function fetchMcpConfig(category) {
99
160
  if (!body?.url) {
100
161
  throw new Error(`MCP 配置响应缺少 url 字段 (category="${category}")`);
101
162
  }
102
- console.log(`${LOG_TAG} 配置拉取成功 (category="${category}")`);
163
+ console.log(`${LOG_TAG} 配置拉取成功 (accountId="${accountId}", category="${category}")`);
103
164
  return body;
104
165
  }
105
166
  /**
@@ -353,7 +353,7 @@ async function routeAndDispatchMessage(params) {
353
353
  }
354
354
  },
355
355
  deliver: async (payload, info) => {
356
- runtime.log?.(`[openclaw -> plugin] kind=${info.kind}, text=${payload.text ?? ''}, mediaUrl=${payload.mediaUrl ?? ''}, mediaUrls=${JSON.stringify(payload.mediaUrls ?? [])}`);
356
+ runtime.log?.(`[openclaw -> plugin] kind=${info.kind}, text=${JSON.stringify(payload.text ?? '')}, mediaUrl=${payload.mediaUrl ?? ''}, mediaUrls=${JSON.stringify(payload.mediaUrls ?? [])}`);
357
357
  // 累积文本
358
358
  if (payload.text) {
359
359
  state.accumulatedText += `${payload.text || ''}`;
@@ -1,3 +1,3 @@
1
1
  import type { PluginRuntime } from "openclaw/plugin-sdk/core";
2
- export declare function setWeComRuntime(r: PluginRuntime): void;
3
- export declare function getWeComRuntime(): PluginRuntime;
2
+ declare const setWeComRuntime: (next: PluginRuntime) => void, getWeComRuntime: () => PluginRuntime;
3
+ export { setWeComRuntime, getWeComRuntime };
@@ -1,10 +1,3 @@
1
- let runtime = null;
2
- export function setWeComRuntime(r) {
3
- runtime = r;
4
- }
5
- export function getWeComRuntime() {
6
- if (!runtime) {
7
- throw new Error("WeCom runtime not initialized - plugin not registered");
8
- }
9
- return runtime;
10
- }
1
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
2
+ const { setRuntime: setWeComRuntime, getRuntime: getWeComRuntime } = createPluginRuntimeStore("WeCom runtime not initialized");
3
+ export { setWeComRuntime, getWeComRuntime };
@@ -5,12 +5,13 @@
5
5
  * 解决全局 Map 的内存泄漏问题
6
6
  */
7
7
  import { createPersistentReqIdStore } from "./reqid-store.js";
8
- import { MESSAGE_STATE_TTL_MS, MESSAGE_STATE_CLEANUP_INTERVAL_MS, MESSAGE_STATE_MAX_SIZE, } from "./const.js";
8
+ import { MESSAGE_STATE_TTL_MS, MESSAGE_STATE_CLEANUP_INTERVAL_MS, MESSAGE_STATE_MAX_SIZE, GLOBAL_WS_CLIENT_KEY, } from "./const.js";
9
9
  // ============================================================================
10
10
  // WSClient 实例管理
11
11
  // ============================================================================
12
12
  /** WSClient 实例管理 */
13
- const wsClientInstances = new Map();
13
+ const wsClientInstances = (globalThis[GLOBAL_WS_CLIENT_KEY]
14
+ ?? (globalThis[GLOBAL_WS_CLIENT_KEY] = new Map()));
14
15
  /**
15
16
  * 获取指定账户的 WSClient 实例
16
17
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wecom/wecom-openclaw-plugin",
3
- "version": "2026.4.3",
3
+ "version": "2026.4.7-beta.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",