@openclaw/qqbot 2026.7.1 → 2026.7.2-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.
Files changed (41) hide show
  1. package/dist/api.js +8 -7
  2. package/dist/{channel-entry-C5YdhX3Y.js → channel-entry-CA2T2sf8.js} +4 -4
  3. package/dist/channel-entry-api.js +1 -1
  4. package/dist/{channel-D1UztsnG.js → channel-nz3Mkify.js} +99 -54
  5. package/dist/channel-plugin-api.js +1 -1
  6. package/dist/{channel.setup-2ItDYKhz.js → channel.setup-CNoBtKXQ.js} +1 -1
  7. package/dist/{config-C1qZbh0K.js → config-CpOXnoEc.js} +2 -2
  8. package/dist/{config-schema-JZEf1dvB.js → config-schema-D7MaH5X5.js} +61 -76
  9. package/dist/doctor-contract-api.js +4 -10
  10. package/dist/{gateway-pJQppxe4.js → gateway-D8uYPtoy.js} +286 -174
  11. package/dist/{group-o0GmovSf.js → group-BVHG8qUZ.js} +40 -23
  12. package/dist/{handler-runtime-zQvT6SrI.js → handler-runtime-S1_XF8on.js} +8 -12
  13. package/dist/{log-DEtcoDWe.js → log-Da4jz75I.js} +4 -8
  14. package/dist/{outbound-BIrfvvFJ.js → outbound-FOG4zNLY.js} +8 -142
  15. package/dist/{runtime-DodcT_fQ.js → runtime-CyjBiGD2.js} +2 -2
  16. package/dist/runtime-api.js +1 -1
  17. package/dist/secret-contract-api.js +6 -22
  18. package/dist/{sender-CjDuU-uz.js → sender-BAUHZqDW.js} +225 -66
  19. package/dist/setup-plugin-api.js +1 -1
  20. package/dist/state-keys-jLJ2SmJA.js +225 -0
  21. package/dist/{tools-UJJ-tLHP.js → tools-CC5CKQig.js} +71 -26
  22. package/dist/tools-api.js +1 -1
  23. package/node_modules/p-map/index.d.ts +155 -0
  24. package/node_modules/p-map/index.js +284 -0
  25. package/node_modules/p-map/license +9 -0
  26. package/node_modules/p-map/package.json +57 -0
  27. package/node_modules/p-map/readme.md +190 -0
  28. package/node_modules/parse-ms/index.d.ts +30 -0
  29. package/node_modules/parse-ms/index.js +45 -0
  30. package/node_modules/parse-ms/license +9 -0
  31. package/node_modules/parse-ms/package.json +47 -0
  32. package/node_modules/parse-ms/readme.md +46 -0
  33. package/node_modules/pretty-ms/index.d.ts +157 -0
  34. package/node_modules/pretty-ms/index.js +149 -0
  35. package/node_modules/pretty-ms/license +9 -0
  36. package/node_modules/pretty-ms/package.json +55 -0
  37. package/node_modules/pretty-ms/readme.md +179 -0
  38. package/npm-shrinkwrap.json +44 -3
  39. package/openclaw.plugin.json +105 -133
  40. package/package.json +8 -4
  41. package/dist/state-keys-CQKlAFyo.js +0 -141
package/dist/api.js CHANGED
@@ -1,8 +1,9 @@
1
- import { t as qqbotPlugin } from "./channel-D1UztsnG.js";
2
- import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-C1qZbh0K.js";
3
- import { t as qqbotSetupPlugin } from "./channel.setup-2ItDYKhz.js";
4
- import { o as getFrameworkCommands } from "./group-o0GmovSf.js";
5
- import { A as OUTBOUND_ERROR_CODES, C as resolveUserFacingMediaError, D as getMessageReplyStats, E as getMessageReplyConfig, O as recordMessageReply, T as checkMessageReplyLimit, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, j as setOutboundAudioPort, k as DEFAULT_MEDIA_SEND_ERROR, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as MESSAGE_REPLY_LIMIT } from "./outbound-BIrfvvFJ.js";
6
- import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-UJJ-tLHP.js";
7
- import { t as registerQQBotFull } from "./channel-entry-C5YdhX3Y.js";
1
+ import { C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, S as MESSAGE_REPLY_LIMIT, T as getMessageReplyConfig } from "./sender-BAUHZqDW.js";
2
+ import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
3
+ import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-CpOXnoEc.js";
4
+ import { t as qqbotSetupPlugin } from "./channel.setup-CNoBtKXQ.js";
5
+ import { o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
6
+ import { C as resolveUserFacingMediaError, E as setOutboundAudioPort, T as OUTBOUND_ERROR_CODES, _ as sendVideoMsg, a as sendText, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-FOG4zNLY.js";
7
+ import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-CC5CKQig.js";
8
+ import { t as registerQQBotFull } from "./channel-entry-CA2T2sf8.js";
8
9
  export { DEFAULT_ACCOUNT_ID, DEFAULT_MEDIA_SEND_ERROR, MESSAGE_REPLY_LIMIT, OUTBOUND_ERROR_CODES, applyQQBotAccountConfig, buildMediaTarget, checkMessageReplyLimit, getFrameworkCommands, getMessageReplyConfig, getMessageReplyStats, listQQBotAccountIds, parseTarget, qqbotPlugin, qqbotSetupPlugin, recordMessageReply, registerChannelTool, registerQQBotFull, registerQQBotTools, registerRemindTool, resolveDefaultQQBotAccountId, resolveOutboundMediaPath, resolveQQBotAccount, resolveUserFacingMediaError, sendCronMessage, sendDocument, sendMedia, sendPhoto, sendProactiveMessage, sendText, sendVideoMsg, sendVoice, setOutboundAudioPort };
@@ -1,7 +1,7 @@
1
- import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
2
- import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-o0GmovSf.js";
3
- import { h as sendDocument } from "./outbound-BIrfvvFJ.js";
4
- import { t as registerQQBotTools } from "./tools-UJJ-tLHP.js";
1
+ import { a as resolveQQBotAccount } from "./config-CpOXnoEc.js";
2
+ import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-BVHG8qUZ.js";
3
+ import { h as sendDocument } from "./outbound-FOG4zNLY.js";
4
+ import { t as registerQQBotTools } from "./tools-CC5CKQig.js";
5
5
  //#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
6
6
  /**
7
7
  * Default queue snapshot used for framework-registered commands.
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotFull } from "./channel-entry-C5YdhX3Y.js";
1
+ import { t as registerQQBotFull } from "./channel-entry-CA2T2sf8.js";
2
2
  export { registerQQBotFull };
@@ -1,10 +1,10 @@
1
1
  import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-JZEf1dvB.js";
3
- import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-C1qZbh0K.js";
4
- import { t as getQQBotRuntime } from "./runtime-DodcT_fQ.js";
5
- import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
2
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-D7MaH5X5.js";
3
+ import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-CpOXnoEc.js";
4
+ import { t as getQQBotRuntime } from "./runtime-CyjBiGD2.js";
5
+ import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
6
6
  import { n as normalizeTarget, r as parseTarget, t as looksLikeQQBotTarget } from "./target-parser-DagYRQkP.js";
7
- import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
7
+ import { getExecApprovalReplyMetadata, resolveExecApprovalCommandDisplay } from "openclaw/plugin-sdk/approval-runtime";
8
8
  import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/channel-core";
9
9
  import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
10
10
  import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
@@ -17,34 +17,66 @@ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
17
17
  import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
18
18
  import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
19
19
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
20
- import { resolveChannelGroupToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
20
+ import { buildChannelGroupsScopeTree, resolveScopeKeyCaseInsensitive, resolveScopeToolsPolicy } from "openclaw/plugin-sdk/channel-policy";
21
21
  //#region extensions/qqbot/src/engine/approval/index.ts
22
- /**
23
- * Approval helpers — pure functions, zero framework dependencies.
24
- *
25
- * - Build approval message text + inline keyboard
26
- * - Resolve delivery target from session metadata
27
- * - Parse INTERACTION_CREATE button data
28
- */
29
- function buildExecApprovalText(request) {
30
- const expiresIn = Math.max(0, Math.round((request.expiresAtMs - Date.now()) / 1e3));
22
+ const COMMAND_PREVIEW_MAX_LENGTH = 300;
23
+ const COMMAND_PREVIEW_GRAPHEMES_PER_LINE = 24;
24
+ const COMMAND_PREVIEW_WRAP_MARKER = "↩";
25
+ const commandPreviewSegmenter = typeof Intl !== "undefined" && "Segmenter" in Intl ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
26
+ function splitCommandPreviewGraphemes(commandText) {
27
+ return commandPreviewSegmenter ? Array.from(commandPreviewSegmenter.segment(commandText), ({ segment }) => segment) : Array.from(commandText);
28
+ }
29
+ function formatCommandPreview(commandText) {
30
+ const lines = [""];
31
+ const displayText = commandText.replaceAll(COMMAND_PREVIEW_WRAP_MARKER, "\\u{21A9}");
32
+ let previewLength = 0;
33
+ let lineGraphemes = 0;
34
+ let truncated = false;
35
+ let wrapped = false;
36
+ for (const grapheme of splitCommandPreviewGraphemes(displayText)) {
37
+ if (previewLength + grapheme.length > COMMAND_PREVIEW_MAX_LENGTH) {
38
+ if (previewLength === 0) lines[0] = truncateUtf16Safe(grapheme, COMMAND_PREVIEW_MAX_LENGTH);
39
+ truncated = true;
40
+ break;
41
+ }
42
+ previewLength += grapheme.length;
43
+ if (lineGraphemes === COMMAND_PREVIEW_GRAPHEMES_PER_LINE) {
44
+ lines[lines.length - 1] += COMMAND_PREVIEW_WRAP_MARKER;
45
+ lines.push("");
46
+ lineGraphemes = 0;
47
+ wrapped = true;
48
+ }
49
+ lines[lines.length - 1] += grapheme;
50
+ lineGraphemes += 1;
51
+ }
52
+ const preview = `${lines.join("\n")}${truncated ? "\n…[truncated]" : ""}`;
53
+ const longestBacktickRun = Math.max(0, ...preview.match(/`+/g)?.map((run) => run.length) ?? []);
54
+ const fence = "`".repeat(Math.max(3, longestBacktickRun + 1));
55
+ const block = `${fence}\n${preview}\n${fence}`;
56
+ return wrapped ? `${COMMAND_PREVIEW_WRAP_MARKER} = display wrap only; not command text\n${block}` : block;
57
+ }
58
+ function formatApprovalMetadata(value) {
59
+ const sanitized = resolveExecApprovalCommandDisplay({ command: value }).commandText;
60
+ return formatCommandPreview(sanitized);
61
+ }
62
+ function buildExecApprovalText(view, nowMs = Date.now()) {
63
+ const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1e3));
31
64
  const lines = ["🔐 命令执行审批", ""];
32
- const cmd = request.request.commandPreview ?? request.request.command ?? "";
33
- if (cmd) lines.push(`\`\`\`\n${truncateUtf16Safe(cmd, 300)}\n\`\`\``);
34
- if (request.request.cwd) lines.push(`\u{1f4c1} \u76ee\u5f55: ${request.request.cwd}`);
35
- if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
65
+ if (view.commandText) lines.push(formatCommandPreview(view.commandText));
66
+ if (view.cwd) lines.push(`\u{1f4c1} \u76ee\u5f55:\n${formatApprovalMetadata(view.cwd)}`);
67
+ if (view.agentId) lines.push(`\u{1f916} Agent:\n${formatApprovalMetadata(view.agentId)}`);
36
68
  lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
37
69
  return lines.join("\n");
38
70
  }
39
- function buildPluginApprovalText(request) {
40
- const timeoutSec = Math.round((request.request.timeoutMs ?? 12e4) / 1e3);
41
- const lines = [`${request.request.severity === "critical" ? "🔴" : request.request.severity === "info" ? "🔵" : "🟡"} \u5ba1\u6279\u8bf7\u6c42`, ""];
42
- lines.push(`\u{1f4cb} ${request.request.title}`);
43
- if (request.request.description) lines.push(`\u{1f4dd} ${request.request.description}`);
44
- if (request.request.toolName) lines.push(`\u{1f527} \u5de5\u5177: ${request.request.toolName}`);
45
- if (request.request.pluginId) lines.push(`\u{1f50c} \u63d2\u4ef6: ${request.request.pluginId}`);
46
- if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
47
- lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${timeoutSec} \u79d2`);
71
+ function buildPluginApprovalText(view, nowMs = Date.now()) {
72
+ const expiresIn = Math.max(0, Math.round((view.expiresAtMs - nowMs) / 1e3));
73
+ const lines = [`${view.severity === "critical" ? "🔴" : view.severity === "info" ? "🔵" : "🟡"} \u5ba1\u6279\u8bf7\u6c42`, ""];
74
+ lines.push(`\u{1f4cb} ${view.title}`);
75
+ if (view.description) lines.push(`\u{1f4dd} ${view.description}`);
76
+ if (view.toolName) lines.push(`\u{1f527} \u5de5\u5177: ${view.toolName}`);
77
+ if (view.pluginId) lines.push(`\u{1f50c} \u63d2\u4ef6: ${view.pluginId}`);
78
+ if (view.agentId) lines.push(`\u{1f916} Agent: ${view.agentId}`);
79
+ lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
48
80
  return lines.join("\n");
49
81
  }
50
82
  /**
@@ -55,11 +87,12 @@ function buildPluginApprovalText(request) {
55
87
  * click_limit=1: each user can only click once.
56
88
  * permission.type=2: all users can interact.
57
89
  */
58
- function buildApprovalKeyboard(approvalId, allowedDecisions = [
90
+ function buildApprovalKeyboard(approvalId, approvalKind, allowedDecisions = [
59
91
  "allow-once",
60
92
  "allow-always",
61
93
  "deny"
62
94
  ]) {
95
+ const actionPrefix = `approve:v2:${approvalKind}:${encodeURIComponent(approvalId)}`;
63
96
  const makeBtn = (id, label, visitedLabel, data, style) => ({
64
97
  id,
65
98
  render_data: {
@@ -76,9 +109,9 @@ function buildApprovalKeyboard(approvalId, allowedDecisions = [
76
109
  group_id: "approval"
77
110
  });
78
111
  const buttons = [];
79
- if (allowedDecisions.includes("allow-once")) buttons.push(makeBtn("allow", "✅ 允许一次", "已允许", `approve:${approvalId}:allow-once`, 1));
80
- if (allowedDecisions.includes("allow-always")) buttons.push(makeBtn("always", "⭐ 始终允许", "已始终允许", `approve:${approvalId}:allow-always`, 1));
81
- if (allowedDecisions.includes("deny")) buttons.push(makeBtn("deny", "❌ 拒绝", "已拒绝", `approve:${approvalId}:deny`, 0));
112
+ if (allowedDecisions.includes("allow-once")) buttons.push(makeBtn("allow", "✅ 允许一次", "已处理", `${actionPrefix}:allow-once`, 1));
113
+ if (allowedDecisions.includes("allow-always")) buttons.push(makeBtn("always", "⭐ 始终允许", "已处理", `${actionPrefix}:allow-always`, 1));
114
+ if (allowedDecisions.includes("deny")) buttons.push(makeBtn("deny", "❌ 拒绝", "已处理", `${actionPrefix}:deny`, 0));
82
115
  return { content: { rows: [{ buttons }] } };
83
116
  }
84
117
  /**
@@ -96,25 +129,39 @@ function resolveApprovalTarget(sessionKey, turnSourceTo) {
96
129
  if (!sk) return null;
97
130
  const m = sk.match(/qqbot:(c2c|direct|group):([A-F0-9]+)/i);
98
131
  if (!m) return null;
132
+ const scope = m[1];
133
+ const id = m[2];
134
+ if (scope === void 0 || id === void 0) return null;
99
135
  return {
100
- type: m[1].toLowerCase() === "group" ? "group" : "c2c",
101
- id: m[2]
136
+ type: scope.toLowerCase() === "group" ? "group" : "c2c",
137
+ id
102
138
  };
103
139
  }
104
140
  /**
105
141
  * Parse the button_data string from an INTERACTION_CREATE event.
106
142
  *
107
- * Expected format: `approve:<approvalId>:<decision>`
108
- * where approvalId may be prefixed with "exec:" or "plugin:".
143
+ * Expected format: `approve:v2:<approvalKind>:<encodedApprovalId>:<decision>`.
109
144
  *
110
145
  * Returns null if the data does not match the approval button format.
111
146
  */
112
147
  function parseApprovalButtonData(buttonData) {
113
- const m = buttonData.match(/^approve:((?:(?:exec|plugin):)?[0-9a-f-]+):(allow-once|allow-always|deny)$/i);
114
- if (!m) return null;
148
+ const m = buttonData.match(/^approve:v2:(exec|plugin):([^:]+):(allow-once|allow-always|deny)$/);
149
+ if (!m || m[0] !== buttonData) return null;
150
+ let approvalId;
151
+ const kind = m[1];
152
+ const encodedId = m[2];
153
+ const decision = m[3];
154
+ if (kind !== "exec" && kind !== "plugin" || encodedId === void 0 || decision !== "allow-once" && decision !== "allow-always" && decision !== "deny") return null;
155
+ try {
156
+ approvalId = decodeURIComponent(encodedId);
157
+ } catch {
158
+ return null;
159
+ }
160
+ if (!approvalId) return null;
115
161
  return {
116
- approvalId: m[1],
117
- decision: m[2]
162
+ approvalId,
163
+ approvalKind: kind,
164
+ decision
118
165
  };
119
166
  }
120
167
  //#endregion
@@ -375,7 +422,7 @@ function createQQBotApprovalCapability() {
375
422
  },
376
423
  load: async () => {
377
424
  ensurePlatformAdapter();
378
- return (await import("./handler-runtime-zQvT6SrI.js")).qqbotApprovalNativeRuntime;
425
+ return (await import("./handler-runtime-S1_XF8on.js")).qqbotApprovalNativeRuntime;
379
426
  }
380
427
  })
381
428
  });
@@ -746,7 +793,8 @@ var QQBotMarkdownChunkingState = class {
746
793
  const bodyLines = pendingFenceOpenLine ? [...this.textLines] : this.textLines.slice(1);
747
794
  this.textLines = [];
748
795
  this.pendingTextFenceOpenLine = null;
749
- if (bodyLines.length > 0 && isClosingFenceLine(bodyLines[bodyLines.length - 1], fence)) bodyLines.pop();
796
+ const lastBodyLine = bodyLines.at(-1);
797
+ if (lastBodyLine !== void 0 && isClosingFenceLine(lastBodyLine, fence)) bodyLines.pop();
750
798
  if (this.activeFence && bodyLines.length === 0) return true;
751
799
  pushFenceLineChunks({
752
800
  chunks,
@@ -994,22 +1042,19 @@ function isClosingFenceLine(line, fence) {
994
1042
  //#endregion
995
1043
  //#region extensions/qqbot/src/group-policy.ts
996
1044
  function resolveQQBotGroupToolPolicy(params) {
997
- return resolveChannelGroupToolsPolicy({
998
- cfg: params.cfg,
999
- channel: "qqbot",
1000
- groupId: params.groupId,
1001
- groupIdCaseInsensitive: true,
1002
- accountId: params.accountId,
1003
- senderId: params.senderId,
1004
- senderName: params.senderName,
1005
- senderUsername: params.senderUsername,
1006
- senderE164: params.senderE164
1045
+ const tree = buildChannelGroupsScopeTree(params.cfg, "qqbot", params.accountId);
1046
+ const scopeKey = resolveScopeKeyCaseInsensitive(tree, params.groupId);
1047
+ return resolveScopeToolsPolicy({
1048
+ ...params,
1049
+ tree,
1050
+ path: scopeKey ? [scopeKey] : [],
1051
+ messageProvider: "qqbot"
1007
1052
  });
1008
1053
  }
1009
1054
  //#endregion
1010
1055
  //#region extensions/qqbot/src/channel.ts
1011
- const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-pJQppxe4.js"));
1012
- const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-BIrfvvFJ.js").then((n) => n.t));
1056
+ const loadGatewayModule = createLazyRuntimeModule(() => import("./gateway-D8uYPtoy.js"));
1057
+ const loadOutboundMessagingModule = createLazyRuntimeModule(() => import("./outbound-FOG4zNLY.js").then((n) => n.t));
1013
1058
  function createQQBotSendReceipt(params) {
1014
1059
  const messageId = params.messageId?.trim();
1015
1060
  return createMessageReceiptFromOutboundResults({
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-D1UztsnG.js";
1
+ import { t as qqbotPlugin } from "./channel-nz3Mkify.js";
2
2
  export { qqbotPlugin };
@@ -1,4 +1,4 @@
1
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-JZEf1dvB.js";
1
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-D7MaH5X5.js";
2
2
  //#region extensions/qqbot/src/channel.setup.ts
3
3
  /**
4
4
  * Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`
@@ -56,8 +56,8 @@ function resolveDefaultAccountId(cfg) {
56
56
  if (configuredDefaultAccountId && (configuredDefaultAccountId === "default" || Boolean(qqbot?.accounts?.[configuredDefaultAccountId]?.appId))) return configuredDefaultAccountId;
57
57
  if (qqbot?.appId || process.env.QQBOT_APP_ID) return DEFAULT_ACCOUNT_ID$1;
58
58
  if (qqbot?.accounts) {
59
- const ids = Object.keys(qqbot.accounts);
60
- if (ids.length > 0) return ids[0];
59
+ const firstId = Object.keys(qqbot.accounts).at(0);
60
+ if (firstId !== void 0) return firstId;
61
61
  }
62
62
  return DEFAULT_ACCOUNT_ID$1;
63
63
  }
@@ -1,12 +1,13 @@
1
1
  import { d as registerPlatformAdapterFactory, l as hasPlatformAdapter, n as normalizeLowercaseStringOrEmpty, u as registerPlatformAdapter } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-C1qZbh0K.js";
3
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
2
+ import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-CpOXnoEc.js";
3
+ import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
4
4
  import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
5
5
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
6
6
  import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
7
7
  import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
8
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
8
9
  import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
9
- import { AllowFromListSchema, ToolPolicySchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
10
+ import { AllowFromListSchema, GroupPolicySchema, buildChannelConfigSchema, buildGroupEntrySchema, buildMultiAccountChannelSchema } from "openclaw/plugin-sdk/channel-config-schema";
10
11
  import { z } from "zod";
11
12
  //#region extensions/qqbot/src/bridge/logger.ts
12
13
  let loggerInstance = null;
@@ -24,31 +25,7 @@ function getBridgeLogger() {
24
25
  }
25
26
  //#endregion
26
27
  //#region extensions/qqbot/src/bridge/bootstrap.ts
27
- /**
28
- * Bootstrap the PlatformAdapter for the built-in version.
29
- *
30
- * ## Design
31
- *
32
- * The adapter is registered via two complementary mechanisms:
33
- *
34
- * 1. **Factory registration** (`registerPlatformAdapterFactory`) — a lightweight
35
- * callback stored in `adapter/index.ts` that is invoked lazily by
36
- * `getPlatformAdapter()` on first access. This guarantees the adapter is
37
- * available regardless of module evaluation order or bundler chunk splitting.
38
- *
39
- * 2. **Eager side-effect** (`ensurePlatformAdapter()`) — called at module
40
- * evaluation time when `channel.ts` imports this file. Provides the adapter
41
- * immediately for code that runs synchronously during startup.
42
- *
43
- * Heavy async-only dependencies (`media-runtime`, `config-runtime`,
44
- * `approval-gateway-runtime`) are lazy-imported inside each async method body
45
- * so that this module evaluates with minimal overhead.
46
- *
47
- * Synchronous dependencies (`secret-input`, `temp-path`) are imported
48
- * statically at the top level so they work reliably in both production and
49
- * vitest (which resolves bare specifiers via `resolve.alias`, not Node CJS).
50
- */
51
- const loadMediaRuntimeModule = createLazyRuntimeModule(() => import("openclaw/plugin-sdk/media-runtime"));
28
+ const loadReadRemoteMediaBuffer = createLazyRuntimeNamedExport(() => import("openclaw/plugin-sdk/media-runtime"), "readRemoteMediaBuffer");
52
29
  function createBuiltinAdapter() {
53
30
  return {
54
31
  async validateRemoteUrl(_url, _options) {},
@@ -56,8 +33,7 @@ function createBuiltinAdapter() {
56
33
  if (typeof value === "string") return value || void 0;
57
34
  },
58
35
  async downloadFile(url, destDir, filename) {
59
- const { readRemoteMediaBuffer } = await loadMediaRuntimeModule();
60
- const result = await readRemoteMediaBuffer({
36
+ const result = await (await loadReadRemoteMediaBuffer())({
61
37
  url,
62
38
  filePathHint: filename
63
39
  });
@@ -69,12 +45,13 @@ function createBuiltinAdapter() {
69
45
  return destPath;
70
46
  },
71
47
  async fetchMedia(options) {
72
- const { readRemoteMediaBuffer } = await loadMediaRuntimeModule();
73
- const result = await readRemoteMediaBuffer({
48
+ const result = await (await loadReadRemoteMediaBuffer())({
74
49
  url: options.url,
75
50
  filePathHint: options.filePathHint,
76
51
  maxBytes: options.maxBytes,
77
52
  maxRedirects: options.maxRedirects,
53
+ timeoutMs: options.timeoutMs,
54
+ responseHeaderTimeoutMs: options.responseHeaderTimeoutMs,
78
55
  ssrfPolicy: options.ssrfPolicy,
79
56
  requestInit: options.requestInit
80
57
  });
@@ -95,20 +72,20 @@ function createBuiltinAdapter() {
95
72
  resolveSecretInputString(params) {
96
73
  return normalizeResolvedSecretInputString(params) ?? void 0;
97
74
  },
98
- async resolveApproval(approvalId, decision) {
75
+ async resolveApproval(params) {
99
76
  try {
100
77
  const { getRuntimeConfig } = await import("openclaw/plugin-sdk/runtime-config-snapshot");
101
78
  const { resolveApprovalOverGateway } = await import("openclaw/plugin-sdk/approval-gateway-runtime");
102
- await resolveApprovalOverGateway({
79
+ return await resolveApprovalOverGateway({
103
80
  cfg: getRuntimeConfig(),
104
- approvalId,
105
- decision,
81
+ approvalId: params.approvalId,
82
+ approvalKind: params.approvalKind,
83
+ decision: params.decision,
106
84
  clientDisplayName: "QQBot Approval Handler"
107
85
  });
108
- return true;
109
86
  } catch (err) {
110
87
  getBridgeLogger().error(`[qqbot] resolveApproval failed: ${String(err)}`);
111
- return false;
88
+ throw err;
112
89
  }
113
90
  }
114
91
  };
@@ -247,32 +224,43 @@ function isQQBotAccountConfigured(cfg, accountId) {
247
224
  const account = resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true });
248
225
  return Boolean(account.appId && account.clientSecret);
249
226
  }
227
+ async function reportQQBotLinkFailure(params, error) {
228
+ params.runtime.error(`QQ Bot 绑定失败: ${String(error)}`);
229
+ await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
230
+ }
250
231
  async function linkViaQrCode(params) {
232
+ let connector;
251
233
  try {
252
- const { qrConnect } = await import("@tencent-connect/qqbot-connector");
253
- const accounts = await qrConnect({ source: "openclaw" });
234
+ connector = await import("@tencent-connect/qqbot-connector");
235
+ } catch (error) {
236
+ await reportQQBotLinkFailure(params, error);
237
+ return params.cfg;
238
+ }
239
+ await params.beforePersistentEffect?.();
240
+ try {
241
+ const accounts = await connector.qrConnect({ source: "openclaw" });
254
242
  if (accounts.length === 0) {
255
243
  await params.prompter.note("未获取到任何 QQ Bot 账号信息。", "QQ Bot");
256
244
  return params.cfg;
257
245
  }
258
246
  let next = params.cfg;
259
- for (let i = 0; i < accounts.length; i++) {
260
- const { appId, appSecret } = accounts[i];
247
+ for (const [i, { appId, appSecret }] of accounts.entries()) {
261
248
  const targetAccountId = i === 0 ? params.accountId : appId;
262
249
  next = applyQQBotAccountConfig(next, targetAccountId, {
263
250
  appId,
264
251
  clientSecret: appSecret
265
252
  });
266
253
  }
267
- if (accounts.length === 1) params.runtime.log(`✔ QQ Bot 绑定成功!(AppID: ${accounts[0].appId})`);
268
- else {
254
+ if (accounts.length === 1) {
255
+ const account = expectDefined(accounts.at(0), "single linked QQ Bot account");
256
+ params.runtime.log(`✔ QQ Bot 绑定成功!(AppID: ${account.appId})`);
257
+ } else {
269
258
  const idList = accounts.map((a) => a.appId).join(", ");
270
259
  params.runtime.log(`✔ ${accounts.length} 个 QQ Bot 绑定成功!(AppID: ${idList})`);
271
260
  }
272
261
  return next;
273
262
  } catch (error) {
274
- params.runtime.error(`QQ Bot 绑定失败: ${String(error)}`);
275
- await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
263
+ await reportQQBotLinkFailure(params, error);
276
264
  return params.cfg;
277
265
  }
278
266
  }
@@ -319,7 +307,8 @@ async function finalizeQQBotSetup(params) {
319
307
  cfg: next,
320
308
  accountId,
321
309
  prompter: params.prompter,
322
- runtime: params.runtime
310
+ runtime: params.runtime,
311
+ beforePersistentEffect: params.options?.beforePersistentEffect
323
312
  });
324
313
  else if (mode === "manual") next = await linkViaManualInput({
325
314
  cfg: next,
@@ -347,12 +336,13 @@ const qqbotSetupWizard = {
347
336
  })
348
337
  }),
349
338
  credentials: [],
350
- finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime }) => await finalizeQQBotSetup({
339
+ finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime, options }) => await finalizeQQBotSetup({
351
340
  cfg,
352
341
  accountId,
353
342
  forceAllowFrom,
354
343
  prompter,
355
- runtime
344
+ runtime,
345
+ options
356
346
  }),
357
347
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
358
348
  };
@@ -370,13 +360,12 @@ const QQBotSttSchema = z.object({
370
360
  apiKey: z.string().optional(),
371
361
  model: z.string().optional()
372
362
  }).strict().optional();
373
- /** When `true`, same as `mode: "partial"` and `c2cStreamApi: true` for C2C. Object form kept for legacy configs. */
374
- const QQBotStreamingSchema = z.union([z.boolean(), z.object({
363
+ const QQBotStreamingSchema = z.object({
375
364
  /** "partial" (default) enables block streaming; "off" disables it. */
376
365
  mode: z.enum(["off", "partial"]).default("partial"),
377
- /** @deprecated Prefer `streaming: true`. */
378
- c2cStreamApi: z.boolean().optional()
379
- }).passthrough()]).optional();
366
+ /** Use QQ's official C2C `stream_messages` API for DM replies. */
367
+ nativeTransport: z.boolean().optional()
368
+ }).strict().optional();
380
369
  const QQBotExecApprovalsSchema = z.object({
381
370
  enabled: z.union([z.boolean(), z.literal("auto")]).optional(),
382
371
  approvers: z.array(z.string()).optional(),
@@ -393,26 +382,23 @@ const QQBotDmPolicySchema = z.enum([
393
382
  "allowlist",
394
383
  "disabled"
395
384
  ]).optional();
396
- const QQBotGroupPolicySchema = z.enum([
397
- "open",
398
- "allowlist",
399
- "disabled"
400
- ]).optional();
401
- const QQBotGroupCommandLevelSchema = z.enum([
402
- "all",
403
- "safety",
404
- "strict"
405
- ]).optional();
406
- const QQBotGroupSchema = z.object({
407
- requireMention: z.boolean().optional(),
408
- commandLevel: QQBotGroupCommandLevelSchema,
385
+ const QQBotGroupPolicySchema = GroupPolicySchema.optional();
386
+ const QQBotGroupSchema = buildGroupEntrySchema({
387
+ commandLevel: z.enum([
388
+ "all",
389
+ "safety",
390
+ "strict"
391
+ ]).optional(),
409
392
  ignoreOtherMentions: z.boolean().optional(),
410
393
  historyLimit: z.number().optional(),
411
394
  name: z.string().optional(),
412
- prompt: z.string().optional(),
413
- tools: ToolPolicySchema,
414
- toolsBySender: z.record(z.string(), ToolPolicySchema).optional()
415
- }).strict();
395
+ prompt: z.string().optional()
396
+ }).omit({
397
+ skills: true,
398
+ enabled: true,
399
+ allowFrom: true,
400
+ systemPrompt: true
401
+ });
416
402
  const QQBotGroupsSchema = z.record(z.string(), QQBotGroupSchema).optional();
417
403
  const QQBotAccountSchema = z.object({
418
404
  enabled: z.boolean().optional(),
@@ -435,10 +421,9 @@ const QQBotAccountSchema = z.object({
435
421
  execApprovals: QQBotExecApprovalsSchema,
436
422
  groups: QQBotGroupsSchema
437
423
  }).passthrough();
438
- const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotAccountSchema.extend({
439
- stt: QQBotSttSchema,
440
- accounts: z.object({}).catchall(QQBotAccountSchema.passthrough()).optional(),
441
- defaultAccount: z.string().optional()
442
- }).passthrough());
424
+ const qqbotChannelConfigSchema = buildChannelConfigSchema(buildMultiAccountChannelSchema(QQBotAccountSchema.extend({ stt: QQBotSttSchema }).passthrough(), {
425
+ accountSchema: QQBotAccountSchema,
426
+ accountsMode: "catchall"
427
+ }));
443
428
  //#endregion
444
429
  export { qqbotSetupAdapterShared as a, setBridgeLogger as c, qqbotMeta as i, qqbotSetupWizard as n, ensurePlatformAdapter as o, qqbotConfigAdapter as r, getBridgeLogger as s, qqbotChannelConfigSchema as t };
@@ -1,4 +1,5 @@
1
- import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
1
+ import { n as legacyConfigRules, r as normalizeCompatibilityConfig, t as buildQQBotStateKey } from "./state-keys-jLJ2SmJA.js";
2
+ import { fileExists } from "openclaw/plugin-sdk/security-runtime";
2
3
  import path from "node:path";
3
4
  import fs from "node:fs/promises";
4
5
  //#region extensions/qqbot/src/state-migrations.ts
@@ -7,13 +8,6 @@ const MAX_CREDENTIAL_BACKUPS = 1e3;
7
8
  function safeName(id) {
8
9
  return id.replace(/[^a-zA-Z0-9._-]/g, "_");
9
10
  }
10
- async function fileExists(filePath) {
11
- try {
12
- return (await fs.lstat(filePath)).isFile();
13
- } catch {
14
- return false;
15
- }
16
- }
17
11
  async function readCredentialBackup(filePath) {
18
12
  try {
19
13
  const parsed = JSON.parse(await fs.readFile(filePath, "utf8"));
@@ -42,7 +36,7 @@ async function credentialBackupCandidates(stateDir) {
42
36
  } catch {}
43
37
  accountFiles.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
44
38
  const singlePath = path.join(dataDir, "credential-backup.json");
45
- return await fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
39
+ return fileExists(singlePath) ? [...accountFiles, { sourcePath: singlePath }] : accountFiles;
46
40
  }
47
41
  async function readLegacyCredentialBackups(stateDir) {
48
42
  const backups = [];
@@ -59,7 +53,7 @@ async function readLegacyCredentialBackups(stateDir) {
59
53
  }
60
54
  async function archiveLegacySource(params) {
61
55
  const archivedPath = `${params.sourcePath}.migrated`;
62
- if (await fileExists(archivedPath)) {
56
+ if (fileExists(archivedPath)) {
63
57
  params.warnings.push(`Left QQBot credential backup in place because ${archivedPath} already exists`);
64
58
  return;
65
59
  }