@openclaw-china/wecom 2026.3.18 → 2026.3.20
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.d.ts +36 -0
- package/dist/index.js +248 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -414,6 +414,38 @@ declare const wecomPlugin: {
|
|
|
414
414
|
error?: Error;
|
|
415
415
|
}>;
|
|
416
416
|
};
|
|
417
|
+
status: {
|
|
418
|
+
defaultRuntime: {
|
|
419
|
+
accountId: string;
|
|
420
|
+
running: boolean;
|
|
421
|
+
lastStartAt: null;
|
|
422
|
+
lastStopAt: null;
|
|
423
|
+
lastError: null;
|
|
424
|
+
};
|
|
425
|
+
buildAccountSnapshot: ({ account, runtime, probe }: {
|
|
426
|
+
account: ResolvedWecomAccount;
|
|
427
|
+
runtime?: Record<string, unknown>;
|
|
428
|
+
probe?: unknown;
|
|
429
|
+
}) => {
|
|
430
|
+
accountId: string;
|
|
431
|
+
name: string | undefined;
|
|
432
|
+
enabled: boolean;
|
|
433
|
+
configured: boolean;
|
|
434
|
+
linked: boolean;
|
|
435
|
+
connected: boolean;
|
|
436
|
+
running: boolean;
|
|
437
|
+
lastStartAt: number | null;
|
|
438
|
+
lastStopAt: number | null;
|
|
439
|
+
lastError: string | null;
|
|
440
|
+
lastInboundAt: number | null;
|
|
441
|
+
lastOutboundAt: number | null;
|
|
442
|
+
mode: string;
|
|
443
|
+
webhookPath: string | undefined;
|
|
444
|
+
dmPolicy: WecomDmPolicy;
|
|
445
|
+
allowFrom: string[] | undefined;
|
|
446
|
+
probe: unknown;
|
|
447
|
+
};
|
|
448
|
+
};
|
|
417
449
|
gateway: {
|
|
418
450
|
startAccount: (ctx: {
|
|
419
451
|
cfg: PluginConfig;
|
|
@@ -476,6 +508,10 @@ interface PluginRuntime {
|
|
|
476
508
|
mediaUrl?: string;
|
|
477
509
|
mediaUrls?: string[];
|
|
478
510
|
}) => Promise<void>;
|
|
511
|
+
onSkip?: (payload: unknown, info: {
|
|
512
|
+
kind: string;
|
|
513
|
+
reason: string;
|
|
514
|
+
}) => void;
|
|
479
515
|
onError?: (err: unknown, info: {
|
|
480
516
|
kind: string;
|
|
481
517
|
}) => void;
|
package/dist/index.js
CHANGED
|
@@ -1729,6 +1729,7 @@ var CHANNEL_ORDER = [
|
|
|
1729
1729
|
"wecom",
|
|
1730
1730
|
"wecom-app",
|
|
1731
1731
|
"wecom-kf",
|
|
1732
|
+
"wechat-mp",
|
|
1732
1733
|
"feishu-china"
|
|
1733
1734
|
];
|
|
1734
1735
|
var CHANNEL_DISPLAY_LABELS = {
|
|
@@ -1737,6 +1738,7 @@ var CHANNEL_DISPLAY_LABELS = {
|
|
|
1737
1738
|
wecom: "WeCom\uFF08\u4F01\u4E1A\u5FAE\u4FE1-\u667A\u80FD\u673A\u5668\u4EBA\uFF09",
|
|
1738
1739
|
"wecom-app": "WeCom App\uFF08\u81EA\u5EFA\u5E94\u7528-\u53EF\u63A5\u5165\u5FAE\u4FE1\uFF09",
|
|
1739
1740
|
"wecom-kf": "WeCom KF\uFF08\u5FAE\u4FE1\u5BA2\u670D\uFF09",
|
|
1741
|
+
"wechat-mp": "WeChat MP\uFF08\u5FAE\u4FE1\u516C\u4F17\u53F7\uFF09",
|
|
1740
1742
|
qqbot: "QQBot\uFF08QQ \u673A\u5668\u4EBA\uFF09"
|
|
1741
1743
|
};
|
|
1742
1744
|
var CHANNEL_GUIDE_LINKS = {
|
|
@@ -1745,6 +1747,7 @@ var CHANNEL_GUIDE_LINKS = {
|
|
|
1745
1747
|
wecom: `${GUIDES_BASE}/wecom/configuration.md`,
|
|
1746
1748
|
"wecom-app": `${GUIDES_BASE}/wecom-app/configuration.md`,
|
|
1747
1749
|
"wecom-kf": "https://github.com/BytePioneer-AI/openclaw-china/blob/main/extensions/wecom-kf/README.md",
|
|
1750
|
+
"wechat-mp": `${GUIDES_BASE}/wechat-mp/configuration.md`,
|
|
1748
1751
|
qqbot: `${GUIDES_BASE}/qqbot/configuration.md`
|
|
1749
1752
|
};
|
|
1750
1753
|
var CHINA_CLI_STATE_KEY = /* @__PURE__ */ Symbol.for("@openclaw-china/china-cli-state");
|
|
@@ -1954,7 +1957,9 @@ function isChannelConfigured(cfg, channelId) {
|
|
|
1954
1957
|
case "wecom-app":
|
|
1955
1958
|
return hasTokenPair(channelCfg);
|
|
1956
1959
|
case "wecom-kf":
|
|
1957
|
-
return hasNonEmptyString(channelCfg.corpId) && hasNonEmptyString(channelCfg.
|
|
1960
|
+
return hasNonEmptyString(channelCfg.corpId) && hasNonEmptyString(channelCfg.token) && hasNonEmptyString(channelCfg.encodingAESKey);
|
|
1961
|
+
case "wechat-mp":
|
|
1962
|
+
return hasNonEmptyString(channelCfg.appId) && hasNonEmptyString(channelCfg.token);
|
|
1958
1963
|
default:
|
|
1959
1964
|
return false;
|
|
1960
1965
|
}
|
|
@@ -2215,6 +2220,15 @@ async function configureWecomKf(prompter, cfg) {
|
|
|
2215
2220
|
section("\u914D\u7F6E WeCom KF\uFF08\u5FAE\u4FE1\u5BA2\u670D\uFF09");
|
|
2216
2221
|
showGuideLink("wecom-kf");
|
|
2217
2222
|
const existing = getChannelConfig(cfg, "wecom-kf");
|
|
2223
|
+
Ve(
|
|
2224
|
+
[
|
|
2225
|
+
"\u5411\u5BFC\u987A\u5E8F\uFF1AwebhookPath / token / encodingAESKey / corpId / open_kfid / corpSecret",
|
|
2226
|
+
"\u57FA\u7840\u5FC5\u586B\uFF1AcorpId / token / encodingAESKey / open_kfid",
|
|
2227
|
+
"corpSecret \u4F1A\u4F5C\u4E3A\u6700\u540E\u4E00\u4E2A\u53C2\u6570\u8BE2\u95EE\uFF1B\u9996\u6B21\u63A5\u5165\u53EF\u5148\u7559\u7A7A\uFF0C\u5F85\u56DE\u8C03 URL \u6821\u9A8C\u901A\u8FC7\u5E76\u70B9\u51FB\u201C\u5F00\u59CB\u4F7F\u7528\u201D\u540E\u518D\u8865",
|
|
2228
|
+
"webhookPath \u9ED8\u8BA4\u503C\uFF1A/wecom-kf"
|
|
2229
|
+
].join("\n"),
|
|
2230
|
+
"\u53C2\u6570\u8BF4\u660E"
|
|
2231
|
+
);
|
|
2218
2232
|
const webhookPath = await prompter.askText({
|
|
2219
2233
|
label: "Webhook \u8DEF\u5F84\uFF08\u9ED8\u8BA4 /wecom-kf\uFF09",
|
|
2220
2234
|
defaultValue: toTrimmedString(existing.webhookPath) ?? "/wecom-kf",
|
|
@@ -2235,19 +2249,14 @@ async function configureWecomKf(prompter, cfg) {
|
|
|
2235
2249
|
defaultValue: toTrimmedString(existing.corpId),
|
|
2236
2250
|
required: true
|
|
2237
2251
|
});
|
|
2238
|
-
const corpSecret = await prompter.askSecret({
|
|
2239
|
-
label: "\u5FAE\u4FE1\u5BA2\u670D Secret",
|
|
2240
|
-
existingValue: toTrimmedString(existing.corpSecret),
|
|
2241
|
-
required: true
|
|
2242
|
-
});
|
|
2243
2252
|
const openKfId = await prompter.askText({
|
|
2244
2253
|
label: "open_kfid",
|
|
2245
2254
|
defaultValue: toTrimmedString(existing.openKfId),
|
|
2246
2255
|
required: true
|
|
2247
2256
|
});
|
|
2248
|
-
const
|
|
2249
|
-
label: "\
|
|
2250
|
-
|
|
2257
|
+
const corpSecret = await prompter.askSecret({
|
|
2258
|
+
label: "\u5FAE\u4FE1\u5BA2\u670D Secret\uFF08\u6700\u540E\u586B\u5199\uFF1B\u9996\u6B21\u63A5\u5165\u53EF\u5148\u7559\u7A7A\uFF09",
|
|
2259
|
+
existingValue: toTrimmedString(existing.corpSecret),
|
|
2251
2260
|
required: false
|
|
2252
2261
|
});
|
|
2253
2262
|
return mergeChannelConfig(cfg, "wecom-kf", {
|
|
@@ -2255,8 +2264,89 @@ async function configureWecomKf(prompter, cfg) {
|
|
|
2255
2264
|
token,
|
|
2256
2265
|
encodingAESKey,
|
|
2257
2266
|
corpId,
|
|
2258
|
-
corpSecret,
|
|
2259
2267
|
openKfId,
|
|
2268
|
+
corpSecret: corpSecret || void 0
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
async function configureWechatMp(prompter, cfg) {
|
|
2272
|
+
section("\u914D\u7F6E WeChat MP\uFF08\u5FAE\u4FE1\u516C\u4F17\u53F7\uFF09");
|
|
2273
|
+
showGuideLink("wechat-mp");
|
|
2274
|
+
const existing = getChannelConfig(cfg, "wechat-mp");
|
|
2275
|
+
const webhookPath = await prompter.askText({
|
|
2276
|
+
label: "Webhook \u8DEF\u5F84\uFF08\u9ED8\u8BA4 /wechat-mp\uFF09",
|
|
2277
|
+
defaultValue: toTrimmedString(existing.webhookPath) ?? "/wechat-mp",
|
|
2278
|
+
required: true
|
|
2279
|
+
});
|
|
2280
|
+
const appId = await prompter.askText({
|
|
2281
|
+
label: "\u516C\u4F17\u53F7 appId",
|
|
2282
|
+
defaultValue: toTrimmedString(existing.appId),
|
|
2283
|
+
required: true
|
|
2284
|
+
});
|
|
2285
|
+
const appSecret = await prompter.askSecret({
|
|
2286
|
+
label: "\u516C\u4F17\u53F7 appSecret\uFF08\u4E3B\u52A8\u53D1\u9001\u9700\u8981\uFF09",
|
|
2287
|
+
existingValue: toTrimmedString(existing.appSecret),
|
|
2288
|
+
required: false
|
|
2289
|
+
});
|
|
2290
|
+
const token = await prompter.askSecret({
|
|
2291
|
+
label: "\u670D\u52A1\u5668\u914D\u7F6E token",
|
|
2292
|
+
existingValue: toTrimmedString(existing.token),
|
|
2293
|
+
required: true
|
|
2294
|
+
});
|
|
2295
|
+
const messageMode = await prompter.askSelect(
|
|
2296
|
+
"\u6D88\u606F\u52A0\u89E3\u5BC6\u6A21\u5F0F",
|
|
2297
|
+
[
|
|
2298
|
+
{ value: "plain", label: "plain\uFF08\u660E\u6587\uFF09" },
|
|
2299
|
+
{ value: "safe", label: "safe\uFF08\u5B89\u5168\u6A21\u5F0F\uFF09" },
|
|
2300
|
+
{ value: "compat", label: "compat\uFF08\u517C\u5BB9\u6A21\u5F0F\uFF09" }
|
|
2301
|
+
],
|
|
2302
|
+
toTrimmedString(existing.messageMode) ?? "safe"
|
|
2303
|
+
);
|
|
2304
|
+
let encodingAESKey = toTrimmedString(existing.encodingAESKey);
|
|
2305
|
+
if (messageMode !== "plain") {
|
|
2306
|
+
encodingAESKey = await prompter.askSecret({
|
|
2307
|
+
label: "EncodingAESKey\uFF08safe/compat \u5FC5\u586B\uFF09",
|
|
2308
|
+
existingValue: encodingAESKey,
|
|
2309
|
+
required: true
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
const replyMode = await prompter.askSelect(
|
|
2313
|
+
"\u56DE\u590D\u6A21\u5F0F",
|
|
2314
|
+
[
|
|
2315
|
+
{ value: "passive", label: "passive\uFF085 \u79D2\u5185\u88AB\u52A8\u56DE\u590D\uFF09" },
|
|
2316
|
+
{ value: "active", label: "active\uFF08\u5BA2\u670D\u6D88\u606F\u4E3B\u52A8\u53D1\u9001\uFF09" }
|
|
2317
|
+
],
|
|
2318
|
+
toTrimmedString(existing.replyMode) ?? "passive"
|
|
2319
|
+
);
|
|
2320
|
+
let activeDeliveryMode;
|
|
2321
|
+
if (replyMode === "active") {
|
|
2322
|
+
activeDeliveryMode = await prompter.askSelect(
|
|
2323
|
+
"\u4E3B\u52A8\u53D1\u9001\u6A21\u5F0F\uFF08activeDeliveryMode\uFF09",
|
|
2324
|
+
[
|
|
2325
|
+
{ value: "split", label: "split\uFF08\u9010\u5757\u53D1\u9001\uFF0C\u63A8\u8350\uFF09" },
|
|
2326
|
+
{ value: "merged", label: "merged\uFF08\u5408\u5E76\u540E\u5355\u6B21\u53D1\u9001\uFF09" }
|
|
2327
|
+
],
|
|
2328
|
+
toTrimmedString(existing.activeDeliveryMode) ?? "split"
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
const renderMarkdown = await prompter.askConfirm(
|
|
2332
|
+
"\u542F\u7528 Markdown \u6E32\u67D3\uFF08\u63A8\u8350\u5F00\u542F\uFF09",
|
|
2333
|
+
toBoolean(existing.renderMarkdown, true)
|
|
2334
|
+
);
|
|
2335
|
+
const welcomeText = await prompter.askText({
|
|
2336
|
+
label: "\u6B22\u8FCE\u8BED\uFF08\u53EF\u9009\uFF09",
|
|
2337
|
+
defaultValue: toTrimmedString(existing.welcomeText),
|
|
2338
|
+
required: false
|
|
2339
|
+
});
|
|
2340
|
+
return mergeChannelConfig(cfg, "wechat-mp", {
|
|
2341
|
+
webhookPath,
|
|
2342
|
+
appId,
|
|
2343
|
+
appSecret: appSecret || void 0,
|
|
2344
|
+
token,
|
|
2345
|
+
encodingAESKey: messageMode === "plain" ? void 0 : encodingAESKey,
|
|
2346
|
+
messageMode,
|
|
2347
|
+
replyMode,
|
|
2348
|
+
activeDeliveryMode,
|
|
2349
|
+
renderMarkdown,
|
|
2260
2350
|
welcomeText: welcomeText || void 0
|
|
2261
2351
|
});
|
|
2262
2352
|
}
|
|
@@ -2318,6 +2408,8 @@ async function configureSingleChannel(channel, prompter, cfg) {
|
|
|
2318
2408
|
return configureWecomApp(prompter, cfg);
|
|
2319
2409
|
case "wecom-kf":
|
|
2320
2410
|
return configureWecomKf(prompter, cfg);
|
|
2411
|
+
case "wechat-mp":
|
|
2412
|
+
return configureWechatMp(prompter, cfg);
|
|
2321
2413
|
case "qqbot":
|
|
2322
2414
|
return configureQQBot(prompter, cfg);
|
|
2323
2415
|
default:
|
|
@@ -2459,6 +2551,7 @@ var SUPPORTED_CHANNELS = [
|
|
|
2459
2551
|
"wecom",
|
|
2460
2552
|
"wecom-app",
|
|
2461
2553
|
"wecom-kf",
|
|
2554
|
+
"wechat-mp",
|
|
2462
2555
|
"qqbot"
|
|
2463
2556
|
];
|
|
2464
2557
|
var CHINA_INSTALL_HINT_SHOWN_KEY = /* @__PURE__ */ Symbol.for("@openclaw-china/china-install-hint-shown");
|
|
@@ -7181,6 +7274,8 @@ function normalizeWecomWsCallback(frame) {
|
|
|
7181
7274
|
var MESSAGE_CONTEXT_TTL_MS = 6 * 60 * 1e3;
|
|
7182
7275
|
var EVENT_CONTEXT_TTL_MS = 10 * 1e3;
|
|
7183
7276
|
var STREAM_FINISH_GRACE_MS = 2500;
|
|
7277
|
+
var WECOM_WS_THINKING_MESSAGE = "<think></think>";
|
|
7278
|
+
var WECOM_WS_FINISH_FALLBACK_MESSAGE = "\u2705 \u5904\u7406\u5B8C\u6210\u3002";
|
|
7184
7279
|
var messageContexts = /* @__PURE__ */ new Map();
|
|
7185
7280
|
var eventContexts = /* @__PURE__ */ new Map();
|
|
7186
7281
|
var messageBySessionKey = /* @__PURE__ */ new Map();
|
|
@@ -7342,6 +7437,8 @@ function registerWecomWsMessageContext(params) {
|
|
|
7342
7437
|
pendingAutoImagePaths: [],
|
|
7343
7438
|
createdAt: now2(),
|
|
7344
7439
|
updatedAt: now2(),
|
|
7440
|
+
placeholderContent: "",
|
|
7441
|
+
suppressVisibleFallback: false,
|
|
7345
7442
|
started: false,
|
|
7346
7443
|
finished: false,
|
|
7347
7444
|
queue: Promise.resolve(),
|
|
@@ -7367,6 +7464,7 @@ async function sendWecomWsMessagePlaceholder(params) {
|
|
|
7367
7464
|
finish: false
|
|
7368
7465
|
})
|
|
7369
7466
|
);
|
|
7467
|
+
context.placeholderContent = content;
|
|
7370
7468
|
context.started = true;
|
|
7371
7469
|
context.updatedAt = now2();
|
|
7372
7470
|
});
|
|
@@ -7405,6 +7503,16 @@ function bindWecomWsRouteContext(params) {
|
|
|
7405
7503
|
}
|
|
7406
7504
|
context.updatedAt = now2();
|
|
7407
7505
|
}
|
|
7506
|
+
function markWecomWsMessageContextSkipped(params) {
|
|
7507
|
+
pruneMessageContexts();
|
|
7508
|
+
const key = messageKey(params.accountId.trim(), params.reqId.trim());
|
|
7509
|
+
const context = messageContexts.get(key);
|
|
7510
|
+
if (!context || context.finished) return;
|
|
7511
|
+
const reason = String(params.reason ?? "").trim();
|
|
7512
|
+
if (!reason) return;
|
|
7513
|
+
context.suppressVisibleFallback = true;
|
|
7514
|
+
context.updatedAt = now2();
|
|
7515
|
+
}
|
|
7408
7516
|
async function appendWecomWsActiveStreamChunk(params) {
|
|
7409
7517
|
const result = await appendWecomWsActiveStreamReply({
|
|
7410
7518
|
accountId: params.accountId,
|
|
@@ -7465,6 +7573,7 @@ async function appendWecomWsActiveStreamReply(params) {
|
|
|
7465
7573
|
context.msgItems.push(...acceptedMsgItems);
|
|
7466
7574
|
}
|
|
7467
7575
|
if (chunk.trim()) {
|
|
7576
|
+
context.placeholderContent = "";
|
|
7468
7577
|
context.content = appendStreamSnapshotContent(context.content, chunk);
|
|
7469
7578
|
await context.send(
|
|
7470
7579
|
buildWecomWsRespondMessageCommand({
|
|
@@ -7520,13 +7629,15 @@ async function finishWecomWsMessageContext(params) {
|
|
|
7520
7629
|
const finalContent = errorMessage ? context.content ? `${context.content}
|
|
7521
7630
|
|
|
7522
7631
|
${errorMessage}` : errorMessage : context.content;
|
|
7523
|
-
const
|
|
7632
|
+
const fallbackContent = !finalContent && !context.suppressVisibleFallback && context.placeholderContent === WECOM_WS_THINKING_MESSAGE ? WECOM_WS_FINISH_FALLBACK_MESSAGE : void 0;
|
|
7633
|
+
const finishContent = finalContent || fallbackContent;
|
|
7634
|
+
const sendFinish = context.started || Boolean(finishContent) || context.msgItems.length > 0;
|
|
7524
7635
|
if (sendFinish) {
|
|
7525
7636
|
await context.send(
|
|
7526
7637
|
buildWecomWsRespondMessageCommand({
|
|
7527
7638
|
reqId: context.reqId,
|
|
7528
7639
|
streamId: context.streamId,
|
|
7529
|
-
content:
|
|
7640
|
+
content: finishContent,
|
|
7530
7641
|
finish: true,
|
|
7531
7642
|
msgItems: context.msgItems
|
|
7532
7643
|
})
|
|
@@ -8056,6 +8167,9 @@ async function dispatchWecomMessage(params) {
|
|
|
8056
8167
|
if (!normalized.trim()) return;
|
|
8057
8168
|
await hooks.onChunk(normalized);
|
|
8058
8169
|
},
|
|
8170
|
+
onSkip: (_payload, info) => {
|
|
8171
|
+
hooks.onSkip?.(info);
|
|
8172
|
+
},
|
|
8059
8173
|
onError: (err, info) => {
|
|
8060
8174
|
hooks.onError?.(err);
|
|
8061
8175
|
logger.error(`${info.kind} reply failed: ${String(err)}`);
|
|
@@ -9214,6 +9328,52 @@ async function handleWecomWebhookRequest(req, res) {
|
|
|
9214
9328
|
jsonOk(res, encReply);
|
|
9215
9329
|
return true;
|
|
9216
9330
|
}
|
|
9331
|
+
|
|
9332
|
+
// src/status.ts
|
|
9333
|
+
function resolveWsLinked(runtime2) {
|
|
9334
|
+
if (typeof runtime2?.linked === "boolean") return runtime2.linked;
|
|
9335
|
+
return runtime2?.connectionState === "ready";
|
|
9336
|
+
}
|
|
9337
|
+
function resolveWsConnected(runtime2) {
|
|
9338
|
+
if (typeof runtime2?.connected === "boolean") return runtime2.connected;
|
|
9339
|
+
return runtime2?.connectionState === "ready";
|
|
9340
|
+
}
|
|
9341
|
+
function normalizeStringArray(value) {
|
|
9342
|
+
if (!Array.isArray(value)) return void 0;
|
|
9343
|
+
const normalized = value.map((entry) => entry.trim()).filter(Boolean);
|
|
9344
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
9345
|
+
}
|
|
9346
|
+
function createDefaultWecomRuntime(accountId) {
|
|
9347
|
+
return {
|
|
9348
|
+
accountId,
|
|
9349
|
+
running: false,
|
|
9350
|
+
lastStartAt: null,
|
|
9351
|
+
lastStopAt: null,
|
|
9352
|
+
lastError: null
|
|
9353
|
+
};
|
|
9354
|
+
}
|
|
9355
|
+
function buildWecomAccountSnapshot(params) {
|
|
9356
|
+
const { account, runtime: runtime2, probe } = params;
|
|
9357
|
+
return {
|
|
9358
|
+
accountId: account.accountId,
|
|
9359
|
+
name: account.name,
|
|
9360
|
+
enabled: account.enabled,
|
|
9361
|
+
configured: account.configured,
|
|
9362
|
+
linked: resolveWsLinked(runtime2),
|
|
9363
|
+
connected: resolveWsConnected(runtime2),
|
|
9364
|
+
running: runtime2?.running ?? false,
|
|
9365
|
+
lastStartAt: runtime2?.lastStartAt ?? null,
|
|
9366
|
+
lastStopAt: runtime2?.lastStopAt ?? null,
|
|
9367
|
+
lastError: runtime2?.lastError ?? null,
|
|
9368
|
+
lastInboundAt: runtime2?.lastInboundAt ?? null,
|
|
9369
|
+
lastOutboundAt: runtime2?.lastOutboundAt ?? null,
|
|
9370
|
+
mode: runtime2?.mode ?? account.mode,
|
|
9371
|
+
webhookPath: runtime2?.webhookPath ?? (account.mode === "webhook" ? account.config.webhookPath?.trim() || "/wecom" : void 0),
|
|
9372
|
+
dmPolicy: account.config.dmPolicy ?? "pairing",
|
|
9373
|
+
allowFrom: normalizeStringArray(account.config.allowFrom),
|
|
9374
|
+
probe
|
|
9375
|
+
};
|
|
9376
|
+
}
|
|
9217
9377
|
var DOC_BIZ_TYPE = "doc";
|
|
9218
9378
|
var DEFAULT_DOC_MCP_TYPE = "streamable-http";
|
|
9219
9379
|
var MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
|
|
@@ -9378,6 +9538,7 @@ async function fetchAndSaveWecomDocMcpConfig(params) {
|
|
|
9378
9538
|
var activeConnections = /* @__PURE__ */ new Map();
|
|
9379
9539
|
var processedMessageIds = /* @__PURE__ */ new Map();
|
|
9380
9540
|
var PROCESSED_MESSAGE_TTL_MS = 10 * 60 * 1e3;
|
|
9541
|
+
var WECOM_WS_SHUTDOWN_GRACE_MS = 1e3;
|
|
9381
9542
|
var activatedTargets = /* @__PURE__ */ new Map();
|
|
9382
9543
|
function getOrCreateConnection(accountId) {
|
|
9383
9544
|
let conn = activeConnections.get(accountId);
|
|
@@ -9493,7 +9654,11 @@ function summarizeWecomReplyFrame(frame) {
|
|
|
9493
9654
|
}
|
|
9494
9655
|
return JSON.stringify(summary);
|
|
9495
9656
|
}
|
|
9496
|
-
function
|
|
9657
|
+
function isExpectedShutdownWsLog(message) {
|
|
9658
|
+
const lowered = message.toLowerCase();
|
|
9659
|
+
return lowered.includes("invalid websocket frame") || lowered.includes("invalid opcode") || lowered.includes("websocket connection closed: code: 1006");
|
|
9660
|
+
}
|
|
9661
|
+
function createSdkLogger(logger, opts) {
|
|
9497
9662
|
return {
|
|
9498
9663
|
debug(message, ...args) {
|
|
9499
9664
|
logger.debug(formatLogMessage(message, args));
|
|
@@ -9502,13 +9667,27 @@ function createSdkLogger(logger) {
|
|
|
9502
9667
|
logger.info(formatLogMessage(message, args));
|
|
9503
9668
|
},
|
|
9504
9669
|
warn(message, ...args) {
|
|
9505
|
-
|
|
9670
|
+
const formatted = formatLogMessage(message, args);
|
|
9671
|
+
if (opts?.isShuttingDown?.() && isExpectedShutdownWsLog(formatted)) {
|
|
9672
|
+
logger.debug(`wecom ws shutdown noise suppressed: ${formatted}`);
|
|
9673
|
+
return;
|
|
9674
|
+
}
|
|
9675
|
+
logger.warn(formatted);
|
|
9506
9676
|
},
|
|
9507
9677
|
error(message, ...args) {
|
|
9508
|
-
|
|
9678
|
+
const formatted = formatLogMessage(message, args);
|
|
9679
|
+
if (opts?.isShuttingDown?.() && isExpectedShutdownWsLog(formatted)) {
|
|
9680
|
+
logger.debug(`wecom ws shutdown noise suppressed: ${formatted}`);
|
|
9681
|
+
return;
|
|
9682
|
+
}
|
|
9683
|
+
logger.error(formatted);
|
|
9509
9684
|
}
|
|
9510
9685
|
};
|
|
9511
9686
|
}
|
|
9687
|
+
function isExpectedShutdownWsError(error) {
|
|
9688
|
+
const message = error.message.toLowerCase();
|
|
9689
|
+
return message.includes("invalid websocket frame") || message.includes("invalid opcode");
|
|
9690
|
+
}
|
|
9512
9691
|
function requireActiveClient(accountId) {
|
|
9513
9692
|
const conn = activeConnections.get(accountId);
|
|
9514
9693
|
if (!conn?.client) {
|
|
@@ -9634,6 +9813,8 @@ async function startWecomWsGateway(opts) {
|
|
|
9634
9813
|
}
|
|
9635
9814
|
conn.promise = new Promise((resolve4, reject) => {
|
|
9636
9815
|
let finished = false;
|
|
9816
|
+
let shuttingDown = false;
|
|
9817
|
+
let shutdownTimer = null;
|
|
9637
9818
|
const client = new WSClient({
|
|
9638
9819
|
botId: account.botId ?? "",
|
|
9639
9820
|
secret: account.secret ?? "",
|
|
@@ -9641,18 +9822,18 @@ async function startWecomWsGateway(opts) {
|
|
|
9641
9822
|
heartbeatInterval: account.heartbeatIntervalMs,
|
|
9642
9823
|
reconnectInterval: account.reconnectInitialDelayMs,
|
|
9643
9824
|
maxReconnectAttempts: -1,
|
|
9644
|
-
logger: createSdkLogger(logger)
|
|
9825
|
+
logger: createSdkLogger(logger, { isShuttingDown: () => shuttingDown })
|
|
9645
9826
|
});
|
|
9646
9827
|
conn.client = client;
|
|
9647
|
-
const
|
|
9828
|
+
const cleanup = (err) => {
|
|
9648
9829
|
if (finished) return;
|
|
9649
9830
|
finished = true;
|
|
9831
|
+
if (shutdownTimer) {
|
|
9832
|
+
clearTimeout(shutdownTimer);
|
|
9833
|
+
shutdownTimer = null;
|
|
9834
|
+
}
|
|
9650
9835
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
9651
9836
|
client.removeAllListeners();
|
|
9652
|
-
try {
|
|
9653
|
-
client.disconnect();
|
|
9654
|
-
} catch {
|
|
9655
|
-
}
|
|
9656
9837
|
clearWecomWsReplyContextsForAccount(account.accountId);
|
|
9657
9838
|
clearActivatedTargetsForAccount(account.accountId);
|
|
9658
9839
|
conn.client = null;
|
|
@@ -9668,9 +9849,27 @@ async function startWecomWsGateway(opts) {
|
|
|
9668
9849
|
if (err) reject(err);
|
|
9669
9850
|
else resolve4();
|
|
9670
9851
|
};
|
|
9852
|
+
const beginShutdown = (err) => {
|
|
9853
|
+
if (finished) return;
|
|
9854
|
+
if (shuttingDown) {
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
shuttingDown = true;
|
|
9858
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
9859
|
+
shutdownTimer = setTimeout(() => {
|
|
9860
|
+
logger.warn(`wecom ws shutdown timed out for account ${account.accountId}; forcing cleanup`);
|
|
9861
|
+
cleanup(err);
|
|
9862
|
+
}, WECOM_WS_SHUTDOWN_GRACE_MS);
|
|
9863
|
+
shutdownTimer.unref?.();
|
|
9864
|
+
try {
|
|
9865
|
+
client.disconnect();
|
|
9866
|
+
} catch (disconnectErr) {
|
|
9867
|
+
cleanup(disconnectErr);
|
|
9868
|
+
}
|
|
9869
|
+
};
|
|
9671
9870
|
const onAbort = () => {
|
|
9672
9871
|
logger.info("abort signal received, stopping wecom ws gateway");
|
|
9673
|
-
|
|
9872
|
+
beginShutdown();
|
|
9674
9873
|
};
|
|
9675
9874
|
const handleMessageCallback = (frame) => {
|
|
9676
9875
|
const callback = normalizeWecomWsCallback(toWecomWsFrame(frame));
|
|
@@ -9717,7 +9916,7 @@ async function startWecomWsGateway(opts) {
|
|
|
9717
9916
|
void sendWecomWsMessagePlaceholder({
|
|
9718
9917
|
accountId: account.accountId,
|
|
9719
9918
|
reqId: callback.reqId,
|
|
9720
|
-
content:
|
|
9919
|
+
content: WECOM_WS_THINKING_MESSAGE
|
|
9721
9920
|
}).catch((err) => {
|
|
9722
9921
|
logger.warn(`wecom ws placeholder ack failed: ${String(err)}`);
|
|
9723
9922
|
});
|
|
@@ -9730,6 +9929,13 @@ async function startWecomWsGateway(opts) {
|
|
|
9730
9929
|
runId: context.runId
|
|
9731
9930
|
});
|
|
9732
9931
|
},
|
|
9932
|
+
onSkip: (info) => {
|
|
9933
|
+
markWecomWsMessageContextSkipped({
|
|
9934
|
+
accountId: account.accountId,
|
|
9935
|
+
reqId: callback.reqId,
|
|
9936
|
+
reason: info.reason
|
|
9937
|
+
});
|
|
9938
|
+
},
|
|
9733
9939
|
onChunk: async (text) => {
|
|
9734
9940
|
await appendWecomWsActiveStreamChunk({
|
|
9735
9941
|
accountId: account.accountId,
|
|
@@ -9919,13 +10125,20 @@ async function startWecomWsGateway(opts) {
|
|
|
9919
10125
|
setStatus?.({
|
|
9920
10126
|
accountId: account.accountId,
|
|
9921
10127
|
mode: "ws",
|
|
9922
|
-
running:
|
|
10128
|
+
running: !shuttingDown,
|
|
9923
10129
|
connectionState: "disconnected",
|
|
9924
10130
|
lastDisconnectAt: Date.now(),
|
|
9925
10131
|
lastDisconnectReason: reason
|
|
9926
10132
|
});
|
|
10133
|
+
if (shuttingDown) {
|
|
10134
|
+
cleanup();
|
|
10135
|
+
}
|
|
9927
10136
|
});
|
|
9928
10137
|
client.on("error", (error) => {
|
|
10138
|
+
if (shuttingDown && isExpectedShutdownWsError(error)) {
|
|
10139
|
+
logger.debug(`wecom ws shutdown noise suppressed: ${error.message}`);
|
|
10140
|
+
return;
|
|
10141
|
+
}
|
|
9929
10142
|
logger.error(`wecom ws sdk error: ${error.message}`);
|
|
9930
10143
|
setStatus?.({
|
|
9931
10144
|
accountId: account.accountId,
|
|
@@ -9935,17 +10148,17 @@ async function startWecomWsGateway(opts) {
|
|
|
9935
10148
|
});
|
|
9936
10149
|
});
|
|
9937
10150
|
conn.stop = () => {
|
|
9938
|
-
|
|
10151
|
+
beginShutdown();
|
|
9939
10152
|
};
|
|
9940
10153
|
if (abortSignal?.aborted) {
|
|
9941
|
-
|
|
10154
|
+
beginShutdown();
|
|
9942
10155
|
return;
|
|
9943
10156
|
}
|
|
9944
10157
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
9945
10158
|
try {
|
|
9946
10159
|
client.connect();
|
|
9947
10160
|
} catch (err) {
|
|
9948
|
-
|
|
10161
|
+
cleanup(err);
|
|
9949
10162
|
}
|
|
9950
10163
|
});
|
|
9951
10164
|
return conn.promise;
|
|
@@ -10626,6 +10839,14 @@ var wecomPlugin = {
|
|
|
10626
10839
|
}
|
|
10627
10840
|
}
|
|
10628
10841
|
},
|
|
10842
|
+
status: {
|
|
10843
|
+
defaultRuntime: createDefaultWecomRuntime(DEFAULT_ACCOUNT_ID),
|
|
10844
|
+
buildAccountSnapshot: ({ account, runtime: runtime2, probe }) => buildWecomAccountSnapshot({
|
|
10845
|
+
account,
|
|
10846
|
+
runtime: runtime2,
|
|
10847
|
+
probe
|
|
10848
|
+
})
|
|
10849
|
+
},
|
|
10629
10850
|
gateway: {
|
|
10630
10851
|
startAccount: async (ctx) => {
|
|
10631
10852
|
ctx.setStatus?.({ accountId: ctx.accountId });
|