@openclaw/qqbot 2026.5.2-beta.2 → 2026.5.3-beta.1

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 (203) hide show
  1. package/dist/api.js +646 -0
  2. package/dist/approval-cg0SVahb.js +94 -0
  3. package/dist/channel-N6Y_Rcjp.js +551 -0
  4. package/dist/channel-plugin-api.js +2 -0
  5. package/dist/channel.setup-ByCRAe5H.js +25 -0
  6. package/dist/config-D6545NkC.js +94 -0
  7. package/dist/config-schema-DFcjQw73.js +309 -0
  8. package/dist/exec-approvals-COUsM6wZ.js +138 -0
  9. package/dist/gateway-CmSUJKSt.js +6726 -0
  10. package/dist/handler-runtime-Bqm6N0WG.js +120 -0
  11. package/dist/index.js +29 -0
  12. package/dist/narrowing-BoieBTIU.js +25 -0
  13. package/dist/outbound-BJfhwrPg.js +1357 -0
  14. package/dist/request-context-DXtpwWui.js +921 -0
  15. package/dist/resolve-D_06fV6-.js +284 -0
  16. package/dist/runtime-BJAS3eXW.js +18 -0
  17. package/dist/runtime-api.js +2 -0
  18. package/dist/secret-contract-api.js +56 -0
  19. package/dist/sender-p-B14eLG.js +1987 -0
  20. package/dist/setup-entry.js +15 -0
  21. package/dist/setup-plugin-api.js +2 -0
  22. package/dist/string-normalize-Ci6NM5DE.js +98 -0
  23. package/dist/target-parser-Y0prnrXD.js +245 -0
  24. package/package.json +15 -6
  25. package/api.ts +0 -56
  26. package/channel-plugin-api.ts +0 -1
  27. package/index.ts +0 -29
  28. package/runtime-api.ts +0 -9
  29. package/setup-entry.ts +0 -9
  30. package/setup-plugin-api.ts +0 -3
  31. package/src/bridge/approval/capability.ts +0 -237
  32. package/src/bridge/approval/handler-runtime.ts +0 -204
  33. package/src/bridge/bootstrap.ts +0 -135
  34. package/src/bridge/channel-entry.ts +0 -18
  35. package/src/bridge/commands/framework-context-adapter.ts +0 -60
  36. package/src/bridge/commands/framework-registration.ts +0 -47
  37. package/src/bridge/commands/from-parser.test.ts +0 -86
  38. package/src/bridge/commands/from-parser.ts +0 -60
  39. package/src/bridge/commands/result-dispatcher.ts +0 -76
  40. package/src/bridge/config-shared.ts +0 -132
  41. package/src/bridge/config.ts +0 -111
  42. package/src/bridge/gateway.ts +0 -174
  43. package/src/bridge/logger.ts +0 -31
  44. package/src/bridge/narrowing.ts +0 -31
  45. package/src/bridge/plugin-version.test.ts +0 -146
  46. package/src/bridge/plugin-version.ts +0 -102
  47. package/src/bridge/runtime.ts +0 -25
  48. package/src/bridge/sdk-adapter.ts +0 -131
  49. package/src/bridge/setup/finalize.ts +0 -144
  50. package/src/bridge/setup/surface.ts +0 -34
  51. package/src/bridge/tools/channel.ts +0 -58
  52. package/src/bridge/tools/index.ts +0 -15
  53. package/src/bridge/tools/remind.test.ts +0 -124
  54. package/src/bridge/tools/remind.ts +0 -91
  55. package/src/channel.setup.ts +0 -33
  56. package/src/channel.ts +0 -288
  57. package/src/command-auth.test.ts +0 -62
  58. package/src/config-schema.ts +0 -84
  59. package/src/config.test.ts +0 -364
  60. package/src/engine/access/access-control.test.ts +0 -198
  61. package/src/engine/access/access-control.ts +0 -226
  62. package/src/engine/access/index.ts +0 -16
  63. package/src/engine/access/resolve-policy.test.ts +0 -59
  64. package/src/engine/access/resolve-policy.ts +0 -57
  65. package/src/engine/access/sender-match.test.ts +0 -60
  66. package/src/engine/access/sender-match.ts +0 -55
  67. package/src/engine/access/types.ts +0 -53
  68. package/src/engine/adapter/audio.port.ts +0 -27
  69. package/src/engine/adapter/commands.port.ts +0 -22
  70. package/src/engine/adapter/history.port.ts +0 -52
  71. package/src/engine/adapter/index.ts +0 -139
  72. package/src/engine/adapter/mention-gate.port.ts +0 -50
  73. package/src/engine/adapter/types.ts +0 -38
  74. package/src/engine/api/api-client.ts +0 -212
  75. package/src/engine/api/media-chunked.test.ts +0 -336
  76. package/src/engine/api/media-chunked.ts +0 -622
  77. package/src/engine/api/media.ts +0 -218
  78. package/src/engine/api/messages.ts +0 -293
  79. package/src/engine/api/retry.ts +0 -217
  80. package/src/engine/api/routes.ts +0 -95
  81. package/src/engine/api/token.ts +0 -271
  82. package/src/engine/approval/index.test.ts +0 -22
  83. package/src/engine/approval/index.ts +0 -224
  84. package/src/engine/commands/builtin/log-helpers.ts +0 -319
  85. package/src/engine/commands/builtin/register-all.ts +0 -17
  86. package/src/engine/commands/builtin/register-approve.ts +0 -201
  87. package/src/engine/commands/builtin/register-basic.ts +0 -95
  88. package/src/engine/commands/builtin/register-clear-storage.ts +0 -187
  89. package/src/engine/commands/builtin/register-logs.ts +0 -20
  90. package/src/engine/commands/builtin/register-streaming.ts +0 -137
  91. package/src/engine/commands/builtin/state.ts +0 -31
  92. package/src/engine/commands/slash-command-auth.ts +0 -48
  93. package/src/engine/commands/slash-command-handler.ts +0 -146
  94. package/src/engine/commands/slash-commands-impl.test.ts +0 -8
  95. package/src/engine/commands/slash-commands-impl.ts +0 -61
  96. package/src/engine/commands/slash-commands.ts +0 -199
  97. package/src/engine/config/credential-backup.test.ts +0 -88
  98. package/src/engine/config/credential-backup.ts +0 -107
  99. package/src/engine/config/credentials.ts +0 -76
  100. package/src/engine/config/group.test.ts +0 -234
  101. package/src/engine/config/group.ts +0 -299
  102. package/src/engine/config/resolve.test.ts +0 -152
  103. package/src/engine/config/resolve.ts +0 -283
  104. package/src/engine/config/setup-logic.ts +0 -84
  105. package/src/engine/engine-import-boundary.test.ts +0 -73
  106. package/src/engine/gateway/codec.ts +0 -47
  107. package/src/engine/gateway/constants.ts +0 -117
  108. package/src/engine/gateway/event-dispatcher.ts +0 -177
  109. package/src/engine/gateway/gateway-connection.ts +0 -371
  110. package/src/engine/gateway/gateway.ts +0 -291
  111. package/src/engine/gateway/inbound-attachments.test.ts +0 -126
  112. package/src/engine/gateway/inbound-attachments.ts +0 -360
  113. package/src/engine/gateway/inbound-context.ts +0 -195
  114. package/src/engine/gateway/inbound-pipeline.self-echo.test.ts +0 -218
  115. package/src/engine/gateway/inbound-pipeline.ts +0 -235
  116. package/src/engine/gateway/interaction-handler.ts +0 -220
  117. package/src/engine/gateway/message-queue.test.ts +0 -282
  118. package/src/engine/gateway/message-queue.ts +0 -499
  119. package/src/engine/gateway/outbound-dispatch.test.ts +0 -231
  120. package/src/engine/gateway/outbound-dispatch.ts +0 -575
  121. package/src/engine/gateway/reconnect.ts +0 -199
  122. package/src/engine/gateway/stages/access-stage.ts +0 -132
  123. package/src/engine/gateway/stages/assembly-stage.ts +0 -156
  124. package/src/engine/gateway/stages/content-stage.test.ts +0 -77
  125. package/src/engine/gateway/stages/content-stage.ts +0 -77
  126. package/src/engine/gateway/stages/envelope-stage.test.ts +0 -152
  127. package/src/engine/gateway/stages/envelope-stage.ts +0 -144
  128. package/src/engine/gateway/stages/group-gate-stage.ts +0 -292
  129. package/src/engine/gateway/stages/index.ts +0 -18
  130. package/src/engine/gateway/stages/quote-stage.ts +0 -113
  131. package/src/engine/gateway/stages/refidx-stage.ts +0 -62
  132. package/src/engine/gateway/stages/stub-contexts.ts +0 -116
  133. package/src/engine/gateway/types.ts +0 -264
  134. package/src/engine/gateway/typing-keepalive.ts +0 -79
  135. package/src/engine/group/activation.test.ts +0 -114
  136. package/src/engine/group/activation.ts +0 -147
  137. package/src/engine/group/history.test.ts +0 -314
  138. package/src/engine/group/history.ts +0 -321
  139. package/src/engine/group/mention.test.ts +0 -141
  140. package/src/engine/group/mention.ts +0 -197
  141. package/src/engine/group/message-gating.test.ts +0 -188
  142. package/src/engine/group/message-gating.ts +0 -216
  143. package/src/engine/messaging/decode-media-path.ts +0 -82
  144. package/src/engine/messaging/media-source.ts +0 -215
  145. package/src/engine/messaging/media-type-detect.ts +0 -37
  146. package/src/engine/messaging/outbound-audio-port.ts +0 -38
  147. package/src/engine/messaging/outbound-deliver.ts +0 -810
  148. package/src/engine/messaging/outbound-media-send.ts +0 -702
  149. package/src/engine/messaging/outbound-reply.ts +0 -27
  150. package/src/engine/messaging/outbound-result-helpers.ts +0 -54
  151. package/src/engine/messaging/outbound-types.ts +0 -45
  152. package/src/engine/messaging/outbound.ts +0 -485
  153. package/src/engine/messaging/reply-dispatcher.ts +0 -597
  154. package/src/engine/messaging/reply-limiter.ts +0 -164
  155. package/src/engine/messaging/sender.ts +0 -729
  156. package/src/engine/messaging/streaming-c2c.ts +0 -1192
  157. package/src/engine/messaging/streaming-media-send.ts +0 -544
  158. package/src/engine/messaging/target-parser.ts +0 -104
  159. package/src/engine/ref/format-message-ref.ts +0 -142
  160. package/src/engine/ref/format-ref-entry.test.ts +0 -60
  161. package/src/engine/ref/format-ref-entry.ts +0 -27
  162. package/src/engine/ref/store.ts +0 -208
  163. package/src/engine/ref/types.ts +0 -27
  164. package/src/engine/session/known-users.ts +0 -137
  165. package/src/engine/session/session-store.ts +0 -204
  166. package/src/engine/tools/channel-api.ts +0 -244
  167. package/src/engine/tools/remind-logic.test.ts +0 -280
  168. package/src/engine/tools/remind-logic.ts +0 -377
  169. package/src/engine/types.ts +0 -313
  170. package/src/engine/utils/attachment-tags.test.ts +0 -186
  171. package/src/engine/utils/attachment-tags.ts +0 -174
  172. package/src/engine/utils/audio.test.ts +0 -250
  173. package/src/engine/utils/audio.ts +0 -585
  174. package/src/engine/utils/data-paths.ts +0 -38
  175. package/src/engine/utils/diagnostics.ts +0 -109
  176. package/src/engine/utils/file-utils.test.ts +0 -72
  177. package/src/engine/utils/file-utils.ts +0 -225
  178. package/src/engine/utils/format.test.ts +0 -68
  179. package/src/engine/utils/format.ts +0 -70
  180. package/src/engine/utils/image-size.test.ts +0 -158
  181. package/src/engine/utils/image-size.ts +0 -249
  182. package/src/engine/utils/log.test.ts +0 -28
  183. package/src/engine/utils/log.ts +0 -61
  184. package/src/engine/utils/media-tags.test.ts +0 -32
  185. package/src/engine/utils/media-tags.ts +0 -177
  186. package/src/engine/utils/payload.test.ts +0 -68
  187. package/src/engine/utils/payload.ts +0 -145
  188. package/src/engine/utils/platform-storage-laziness.test.ts +0 -63
  189. package/src/engine/utils/platform.test.ts +0 -148
  190. package/src/engine/utils/platform.ts +0 -343
  191. package/src/engine/utils/request-context.ts +0 -60
  192. package/src/engine/utils/string-normalize.ts +0 -91
  193. package/src/engine/utils/stt.test.ts +0 -104
  194. package/src/engine/utils/stt.ts +0 -100
  195. package/src/engine/utils/text-parsing.test.ts +0 -29
  196. package/src/engine/utils/text-parsing.ts +0 -155
  197. package/src/engine/utils/upload-cache.ts +0 -96
  198. package/src/engine/utils/voice-text.ts +0 -15
  199. package/src/exec-approvals.ts +0 -218
  200. package/src/manifest-schema.test.ts +0 -56
  201. package/src/qqbot-test-support.ts +0 -29
  202. package/src/types.ts +0 -210
  203. package/tsconfig.json +0 -16
@@ -0,0 +1,94 @@
1
+ import { c as getPlatformAdapter } from "./string-normalize-Ci6NM5DE.js";
2
+ import { c as resolveDefaultAccountId, n as applyAccountConfig, o as listAccountIds, s as resolveAccountBase, t as DEFAULT_ACCOUNT_ID$1 } from "./resolve-D_06fV6-.js";
3
+ import { coerceSecretRef, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
4
+ import fs from "node:fs";
5
+ import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
6
+ //#region extensions/qqbot/src/bridge/config.ts
7
+ const DEFAULT_ACCOUNT_ID = DEFAULT_ACCOUNT_ID$1;
8
+ function assertNotLegacySecretRefMarker(value, path) {
9
+ const normalized = normalizeSecretInputString(value);
10
+ if (!normalized || !/^secretref(?:-env)?:/i.test(normalized)) return;
11
+ throw new Error(`${path}: legacy SecretRef marker strings are not valid QQ Bot clientSecret values; use a structured SecretRef object instead.`);
12
+ }
13
+ function resolveEnvSecretRefValue(params) {
14
+ const ref = coerceSecretRef(params.value, params.cfg.secrets?.defaults);
15
+ if (!ref || ref.source !== "env") return;
16
+ const providerConfig = params.cfg.secrets?.providers?.[ref.provider];
17
+ if (providerConfig) {
18
+ if (providerConfig.source !== "env") throw new Error(`Secret provider "${ref.provider}" has source "${providerConfig.source}" but ref requests "env".`);
19
+ if (providerConfig.allowlist && !providerConfig.allowlist.includes(ref.id)) throw new Error(`Environment variable "${ref.id}" is not allowlisted in secrets.providers.${ref.provider}.allowlist.`);
20
+ } else if (ref.provider !== resolveDefaultSecretProviderAlias(params.cfg, "env")) throw new Error(`Secret provider "${ref.provider}" is not configured (ref: env:${ref.provider}:${ref.id}).`);
21
+ return normalizeSecretInputString((params.env ?? process.env)[ref.id]);
22
+ }
23
+ function resolveQQBotClientSecretInput(params) {
24
+ assertNotLegacySecretRefMarker(params.value, params.path);
25
+ const envSecret = resolveEnvSecretRefValue({
26
+ cfg: params.cfg,
27
+ value: params.value
28
+ });
29
+ if (envSecret) return envSecret;
30
+ return getPlatformAdapter().resolveSecretInputString({
31
+ value: params.value,
32
+ path: params.path
33
+ });
34
+ }
35
+ /** List all configured QQBot account IDs. */
36
+ function listQQBotAccountIds(cfg) {
37
+ return listAccountIds(cfg);
38
+ }
39
+ /** Resolve the default QQBot account ID. */
40
+ function resolveDefaultQQBotAccountId(cfg) {
41
+ return resolveDefaultAccountId(cfg);
42
+ }
43
+ /** Resolve QQBot account config for runtime or setup flows. */
44
+ function resolveQQBotAccount(cfg, accountId, opts) {
45
+ const base = resolveAccountBase(cfg, accountId);
46
+ const qqbot = cfg.channels?.qqbot;
47
+ /**
48
+ * Legacy top-level account uses `channels.qqbot` as the base, but per-account
49
+ * fields (allowFrom, streaming, …) often live under `accounts.default`.
50
+ * Merge that slice so runtime sees `config.streaming` etc.
51
+ */
52
+ const accountConfig = base.accountId === DEFAULT_ACCOUNT_ID ? {
53
+ ...qqbot,
54
+ ...qqbot?.accounts?.[DEFAULT_ACCOUNT_ID]
55
+ } : qqbot?.accounts?.[base.accountId] ?? {};
56
+ let clientSecret = "";
57
+ let secretSource = "none";
58
+ const clientSecretPath = base.accountId === DEFAULT_ACCOUNT_ID ? "channels.qqbot.clientSecret" : `channels.qqbot.accounts.${base.accountId}.clientSecret`;
59
+ const adapter = getPlatformAdapter();
60
+ if (adapter.hasConfiguredSecret(accountConfig.clientSecret)) {
61
+ clientSecret = opts?.allowUnresolvedSecretRef ? adapter.normalizeSecretInputString(accountConfig.clientSecret) ?? "" : resolveQQBotClientSecretInput({
62
+ cfg,
63
+ value: accountConfig.clientSecret,
64
+ path: clientSecretPath
65
+ }) ?? "";
66
+ secretSource = "config";
67
+ } else if (accountConfig.clientSecretFile) try {
68
+ clientSecret = fs.readFileSync(accountConfig.clientSecretFile, "utf8").trim();
69
+ secretSource = "file";
70
+ } catch {
71
+ secretSource = "none";
72
+ }
73
+ else if (process.env.QQBOT_CLIENT_SECRET && base.accountId === DEFAULT_ACCOUNT_ID) {
74
+ clientSecret = process.env.QQBOT_CLIENT_SECRET;
75
+ secretSource = "env";
76
+ }
77
+ return {
78
+ accountId: base.accountId,
79
+ name: accountConfig.name,
80
+ enabled: base.enabled,
81
+ appId: base.appId,
82
+ clientSecret,
83
+ secretSource,
84
+ systemPrompt: base.systemPrompt,
85
+ markdownSupport: base.markdownSupport,
86
+ config: accountConfig
87
+ };
88
+ }
89
+ /** Apply account config updates back into the OpenClaw config object. */
90
+ function applyQQBotAccountConfig(cfg, accountId, input) {
91
+ return applyAccountConfig(cfg, accountId, input);
92
+ }
93
+ //#endregion
94
+ export { resolveQQBotAccount as a, resolveDefaultQQBotAccountId as i, applyQQBotAccountConfig as n, listQQBotAccountIds as r, DEFAULT_ACCOUNT_ID as t };
@@ -0,0 +1,309 @@
1
+ import { n as normalizeLowercaseStringOrEmpty } from "./string-normalize-Ci6NM5DE.js";
2
+ import { a as isAccountConfigured, i as formatAllowFrom, n as applyAccountConfig, r as describeAccount } from "./resolve-D_06fV6-.js";
3
+ import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds } from "./config-D6545NkC.js";
4
+ import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input";
5
+ import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
6
+ import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
7
+ import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
8
+ import { AllowFromListSchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
9
+ import { z } from "zod";
10
+ //#region extensions/qqbot/src/engine/config/setup-logic.ts
11
+ /**
12
+ * QQBot setup business logic (pure layer).
13
+ * QQBot setup 相关纯业务逻辑。
14
+ *
15
+ * Token parsing, input validation, and setup config application.
16
+ * All functions are framework-agnostic and operate on plain objects.
17
+ */
18
+ /** Parse an inline "appId:clientSecret" token string. */
19
+ function parseInlineToken(token) {
20
+ const colonIdx = token.indexOf(":");
21
+ if (colonIdx <= 0 || colonIdx === token.length - 1) return null;
22
+ const appId = token.slice(0, colonIdx).trim();
23
+ const clientSecret = token.slice(colonIdx + 1).trim();
24
+ if (!appId || !clientSecret) return null;
25
+ return {
26
+ appId,
27
+ clientSecret
28
+ };
29
+ }
30
+ /** Validate setup input for a QQBot account. Returns an error string or null. */
31
+ function validateSetupInput(accountId, input) {
32
+ if (!input.token && !input.tokenFile && !input.useEnv) return "QQBot requires --token (format: appId:clientSecret) or --use-env";
33
+ if (input.useEnv && accountId !== "default") return "QQBot --use-env only supports the default account";
34
+ if (input.token && !parseInlineToken(input.token)) return "QQBot --token must be in appId:clientSecret format";
35
+ return null;
36
+ }
37
+ /** Apply setup input to account config. Returns updated config. */
38
+ function applySetupAccountConfig(cfg, accountId, input) {
39
+ if (input.useEnv && accountId !== "default") return cfg;
40
+ let appId = "";
41
+ let clientSecret = "";
42
+ if (input.token) {
43
+ const parsed = parseInlineToken(input.token);
44
+ if (!parsed) return cfg;
45
+ appId = parsed.appId;
46
+ clientSecret = parsed.clientSecret;
47
+ }
48
+ if (!appId && !input.tokenFile && !input.useEnv) return cfg;
49
+ return applyAccountConfig(cfg, accountId, {
50
+ appId,
51
+ clientSecret,
52
+ clientSecretFile: input.tokenFile,
53
+ name: input.name
54
+ });
55
+ }
56
+ //#endregion
57
+ //#region extensions/qqbot/src/bridge/config-shared.ts
58
+ const qqbotMeta = {
59
+ id: "qqbot",
60
+ label: "QQ Bot",
61
+ selectionLabel: "QQ Bot (Bot API)",
62
+ docsPath: "/channels/qqbot",
63
+ blurb: "Connect to QQ via official QQ Bot API",
64
+ order: 50
65
+ };
66
+ function validateQQBotSetupInput(params) {
67
+ return validateSetupInput(params.accountId, params.input);
68
+ }
69
+ function applyQQBotSetupAccountConfig(params) {
70
+ return applySetupAccountConfig(params.cfg, params.accountId, params.input);
71
+ }
72
+ function isQQBotConfigured(account) {
73
+ return isAccountConfigured(account);
74
+ }
75
+ function describeQQBotAccount(account) {
76
+ return describeAccount(account);
77
+ }
78
+ function formatQQBotAllowFrom(params) {
79
+ return formatAllowFrom(params.allowFrom);
80
+ }
81
+ const qqbotConfigAdapter = {
82
+ listAccountIds: (cfg) => listQQBotAccountIds(cfg),
83
+ resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true }),
84
+ defaultAccountId: (cfg) => resolveDefaultQQBotAccountId(cfg),
85
+ setAccountEnabled: ({ cfg, accountId, enabled }) => setAccountEnabledInConfigSection({
86
+ cfg,
87
+ sectionKey: "qqbot",
88
+ accountId,
89
+ enabled,
90
+ allowTopLevel: true
91
+ }),
92
+ deleteAccount: ({ cfg, accountId }) => deleteAccountFromConfigSection({
93
+ cfg,
94
+ sectionKey: "qqbot",
95
+ accountId,
96
+ clearBaseFields: [
97
+ "appId",
98
+ "clientSecret",
99
+ "clientSecretFile",
100
+ "name"
101
+ ]
102
+ }),
103
+ isConfigured: isQQBotConfigured,
104
+ describeAccount: describeQQBotAccount,
105
+ resolveAllowFrom: ({ cfg, accountId }) => resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true }).config?.allowFrom,
106
+ formatAllowFrom: ({ allowFrom }) => formatQQBotAllowFrom({ allowFrom })
107
+ };
108
+ const qqbotSetupAdapterShared = {
109
+ resolveAccountId: ({ cfg, accountId }) => normalizeLowercaseStringOrEmpty(accountId) || resolveDefaultQQBotAccountId(cfg),
110
+ applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
111
+ cfg,
112
+ channelKey: "qqbot",
113
+ accountId,
114
+ name
115
+ }),
116
+ validateInput: ({ accountId, input }) => validateQQBotSetupInput({
117
+ accountId,
118
+ input
119
+ }),
120
+ applyAccountConfig: ({ cfg, accountId, input }) => applyQQBotSetupAccountConfig({
121
+ cfg,
122
+ accountId,
123
+ input
124
+ })
125
+ };
126
+ //#endregion
127
+ //#region extensions/qqbot/src/bridge/setup/finalize.ts
128
+ function isQQBotAccountConfigured(cfg, accountId) {
129
+ const account = resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true });
130
+ return Boolean(account.appId && account.clientSecret);
131
+ }
132
+ async function linkViaQrCode(params) {
133
+ try {
134
+ const { qrConnect } = await import("@tencent-connect/qqbot-connector");
135
+ const accounts = await qrConnect({ source: "openclaw" });
136
+ if (accounts.length === 0) {
137
+ await params.prompter.note("未获取到任何 QQ Bot 账号信息。", "QQ Bot");
138
+ return params.cfg;
139
+ }
140
+ let next = params.cfg;
141
+ for (let i = 0; i < accounts.length; i++) {
142
+ const { appId, appSecret } = accounts[i];
143
+ const targetAccountId = i === 0 ? params.accountId : appId;
144
+ next = applyQQBotAccountConfig(next, targetAccountId, {
145
+ appId,
146
+ clientSecret: appSecret
147
+ });
148
+ }
149
+ if (accounts.length === 1) params.runtime.log(`✔ QQ Bot 绑定成功!(AppID: ${accounts[0].appId})`);
150
+ else {
151
+ const idList = accounts.map((a) => a.appId).join(", ");
152
+ params.runtime.log(`✔ ${accounts.length} 个 QQ Bot 绑定成功!(AppID: ${idList})`);
153
+ }
154
+ return next;
155
+ } catch (error) {
156
+ params.runtime.error(`QQ Bot 绑定失败: ${String(error)}`);
157
+ await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
158
+ return params.cfg;
159
+ }
160
+ }
161
+ async function linkViaManualInput(params) {
162
+ const appId = await params.prompter.text({
163
+ message: "请输入 QQ Bot AppID",
164
+ validate: (value) => value.trim() ? void 0 : "AppID 不能为空"
165
+ });
166
+ const appSecret = await params.prompter.text({
167
+ message: "请输入 QQ Bot AppSecret",
168
+ validate: (value) => value.trim() ? void 0 : "AppSecret 不能为空"
169
+ });
170
+ const next = applyQQBotAccountConfig(params.cfg, params.accountId, {
171
+ appId: appId.trim(),
172
+ clientSecret: appSecret.trim()
173
+ });
174
+ await params.prompter.note("✔ QQ Bot 配置完成!", "QQ Bot");
175
+ return next;
176
+ }
177
+ async function finalizeQQBotSetup(params) {
178
+ const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;
179
+ let next = params.cfg;
180
+ const configured = isQQBotAccountConfigured(next, accountId);
181
+ const mode = await params.prompter.select({
182
+ message: configured ? "QQ 已绑定,选择操作" : "选择 QQ 绑定方式",
183
+ options: [
184
+ {
185
+ value: "qr",
186
+ label: "扫码绑定(推荐)",
187
+ hint: "使用 QQ 扫描二维码自动完成绑定"
188
+ },
189
+ {
190
+ value: "manual",
191
+ label: "手动输入 QQ Bot AppID 和 AppSecret",
192
+ hint: "需到 QQ 开放平台 q.qq.com 查看"
193
+ },
194
+ {
195
+ value: "skip",
196
+ label: configured ? "保持当前配置" : "稍后配置"
197
+ }
198
+ ]
199
+ });
200
+ if (mode === "qr") next = await linkViaQrCode({
201
+ cfg: next,
202
+ accountId,
203
+ prompter: params.prompter,
204
+ runtime: params.runtime
205
+ });
206
+ else if (mode === "manual") next = await linkViaManualInput({
207
+ cfg: next,
208
+ accountId,
209
+ prompter: params.prompter
210
+ });
211
+ else if (!configured) await params.prompter.note(["您可以稍后运行以下命令重新选择 QQ Bot 进行配置:", " openclaw channels add"].join("\n"), "QQ Bot");
212
+ return { cfg: next };
213
+ }
214
+ //#endregion
215
+ //#region extensions/qqbot/src/bridge/setup/surface.ts
216
+ const channel = "qqbot";
217
+ const qqbotSetupWizard = {
218
+ channel,
219
+ status: createStandardChannelSetupStatus({
220
+ channelLabel: "QQ Bot",
221
+ configuredLabel: "configured",
222
+ unconfiguredLabel: "needs AppID + AppSercet",
223
+ configuredHint: "configured",
224
+ unconfiguredHint: "needs AppID + AppSercet",
225
+ configuredScore: 1,
226
+ unconfiguredScore: 6,
227
+ resolveConfigured: ({ cfg, accountId }) => (accountId ? [accountId] : listQQBotAccountIds(cfg)).some((resolvedAccountId) => {
228
+ return isAccountConfigured(resolveQQBotAccount(cfg, resolvedAccountId, { allowUnresolvedSecretRef: true }));
229
+ })
230
+ }),
231
+ credentials: [],
232
+ finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime }) => await finalizeQQBotSetup({
233
+ cfg,
234
+ accountId,
235
+ forceAllowFrom,
236
+ prompter,
237
+ runtime
238
+ }),
239
+ disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
240
+ };
241
+ //#endregion
242
+ //#region extensions/qqbot/src/config-schema.ts
243
+ const AudioFormatPolicySchema = z.object({
244
+ sttDirectFormats: z.array(z.string()).optional(),
245
+ uploadDirectFormats: z.array(z.string()).optional(),
246
+ transcodeEnabled: z.boolean().optional()
247
+ }).optional();
248
+ const QQBotSttSchema = z.object({
249
+ enabled: z.boolean().optional(),
250
+ provider: z.string().optional(),
251
+ baseUrl: z.string().optional(),
252
+ apiKey: z.string().optional(),
253
+ model: z.string().optional()
254
+ }).strict().optional();
255
+ /** When `true`, same as `mode: "partial"` and `c2cStreamApi: true` for C2C. Object form kept for legacy configs. */
256
+ const QQBotStreamingSchema = z.union([z.boolean(), z.object({
257
+ /** "partial" (default) enables block streaming; "off" disables it. */
258
+ mode: z.enum(["off", "partial"]).default("partial"),
259
+ /** @deprecated Prefer `streaming: true`. */
260
+ c2cStreamApi: z.boolean().optional()
261
+ }).passthrough()]).optional();
262
+ const QQBotExecApprovalsSchema = z.object({
263
+ enabled: z.union([z.boolean(), z.literal("auto")]).optional(),
264
+ approvers: z.array(z.string()).optional(),
265
+ agentFilter: z.array(z.string()).optional(),
266
+ sessionFilter: z.array(z.string()).optional(),
267
+ target: z.enum([
268
+ "dm",
269
+ "channel",
270
+ "both"
271
+ ]).optional()
272
+ }).strict().optional();
273
+ const QQBotDmPolicySchema = z.enum([
274
+ "open",
275
+ "allowlist",
276
+ "disabled"
277
+ ]).optional();
278
+ const QQBotGroupPolicySchema = z.enum([
279
+ "open",
280
+ "allowlist",
281
+ "disabled"
282
+ ]).optional();
283
+ const QQBotAccountSchema = z.object({
284
+ enabled: z.boolean().optional(),
285
+ name: z.string().optional(),
286
+ appId: z.string().optional(),
287
+ clientSecret: buildSecretInputSchema().optional(),
288
+ clientSecretFile: z.string().optional(),
289
+ allowFrom: AllowFromListSchema,
290
+ groupAllowFrom: AllowFromListSchema,
291
+ dmPolicy: QQBotDmPolicySchema,
292
+ groupPolicy: QQBotGroupPolicySchema,
293
+ systemPrompt: z.string().optional(),
294
+ markdownSupport: z.boolean().optional(),
295
+ voiceDirectUploadFormats: z.array(z.string()).optional(),
296
+ audioFormatPolicy: AudioFormatPolicySchema,
297
+ urlDirectUpload: z.boolean().optional(),
298
+ upgradeUrl: z.string().optional(),
299
+ upgradeMode: z.enum(["doc", "hot-reload"]).optional(),
300
+ streaming: QQBotStreamingSchema,
301
+ execApprovals: QQBotExecApprovalsSchema
302
+ }).passthrough();
303
+ const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotAccountSchema.extend({
304
+ stt: QQBotSttSchema,
305
+ accounts: z.object({}).catchall(QQBotAccountSchema.passthrough()).optional(),
306
+ defaultAccount: z.string().optional()
307
+ }).passthrough());
308
+ //#endregion
309
+ export { qqbotSetupAdapterShared as a, qqbotMeta as i, qqbotSetupWizard as n, qqbotConfigAdapter as r, qqbotChannelConfigSchema as t };
@@ -0,0 +1,138 @@
1
+ import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-D6545NkC.js";
2
+ import { resolveApprovalRequestChannelAccountId } from "openclaw/plugin-sdk/approval-native-runtime";
3
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
4
+ import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
5
+ import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
6
+ import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
7
+ //#region extensions/qqbot/src/exec-approvals.ts
8
+ function normalizeApproverId(value) {
9
+ return normalizeOptionalString(String(value)) || void 0;
10
+ }
11
+ function resolveQQBotExecApprovalConfig(params) {
12
+ const account = resolveQQBotAccount(params.cfg, params.accountId);
13
+ const config = account.config.execApprovals;
14
+ if (!config) return;
15
+ return {
16
+ ...config,
17
+ enabled: account.enabled && account.secretSource !== "none" ? config.enabled : false
18
+ };
19
+ }
20
+ function getQQBotExecApprovalApprovers(params) {
21
+ const accountConfig = resolveQQBotAccount(params.cfg, params.accountId).config;
22
+ return resolveApprovalApprovers({
23
+ explicit: resolveQQBotExecApprovalConfig(params)?.approvers,
24
+ allowFrom: accountConfig.allowFrom,
25
+ normalizeApprover: normalizeApproverId
26
+ });
27
+ }
28
+ function countQQBotExecApprovalEligibleAccounts(params) {
29
+ return listQQBotAccountIds(params.cfg).filter((accountId) => {
30
+ const account = resolveQQBotAccount(params.cfg, accountId);
31
+ if (!account.enabled || account.secretSource === "none") return false;
32
+ const config = resolveQQBotExecApprovalConfig({
33
+ cfg: params.cfg,
34
+ accountId
35
+ });
36
+ return isChannelExecApprovalClientEnabledFromConfig({
37
+ enabled: config?.enabled,
38
+ approverCount: getQQBotExecApprovalApprovers({
39
+ cfg: params.cfg,
40
+ accountId
41
+ }).length
42
+ }) && matchesApprovalRequestFilters({
43
+ request: params.request.request,
44
+ agentFilter: config?.agentFilter,
45
+ sessionFilter: config?.sessionFilter,
46
+ fallbackAgentIdFromSessionKey: true
47
+ });
48
+ }).length;
49
+ }
50
+ function matchesQQBotRequestAccount(params) {
51
+ const turnSourceChannel = normalizeLowercaseStringOrEmpty(params.request.request.turnSourceChannel);
52
+ const boundAccountId = resolveApprovalRequestChannelAccountId({
53
+ cfg: params.cfg,
54
+ request: params.request,
55
+ channel: "qqbot"
56
+ });
57
+ if (turnSourceChannel && turnSourceChannel !== "qqbot" && !boundAccountId) return countQQBotExecApprovalEligibleAccounts({
58
+ cfg: params.cfg,
59
+ request: params.request
60
+ }) <= 1;
61
+ return !boundAccountId || !params.accountId || normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId);
62
+ }
63
+ /**
64
+ * Count QQBot accounts that could actually deliver a native approval
65
+ * message — i.e. accounts that are enabled and have resolvable secrets.
66
+ * Disabled or unconfigured accounts never spawn a handler, so they
67
+ * must not contribute to the single-account shortcut in the fallback
68
+ * ownership check below.
69
+ */
70
+ function countQQBotFallbackEligibleAccounts(cfg) {
71
+ return listQQBotAccountIds(cfg).filter((accountId) => {
72
+ const account = resolveQQBotAccount(cfg, accountId);
73
+ return account.enabled && account.secretSource !== "none";
74
+ }).length;
75
+ }
76
+ /**
77
+ * Fallback account-ownership check — applied when `execApprovals` is NOT
78
+ * configured for any QQBot account. In this mode every enabled account
79
+ * handler would otherwise race to deliver the same approval to its own
80
+ * openid namespace, so we must enforce per-account isolation.
81
+ *
82
+ * Rules:
83
+ * - If the request carries a bound account (via `turnSourceAccountId`
84
+ * or session binding), only the handler whose `accountId` matches it
85
+ * delivers the approval. This is strict: a handler with an unknown
86
+ * `accountId` (null/undefined) must not claim a bound request.
87
+ * - If no account is bound, only deliver when there is a single
88
+ * *eligible* QQBot account (enabled + secret resolved). Disabled or
89
+ * unconfigured accounts never deliver anyway, so they shouldn't
90
+ * block the remaining single account from handling the approval.
91
+ * Multiple eligible accounts cannot safely race because openids are
92
+ * account-scoped — cross-account delivery hits the QQ Bot API with
93
+ * a mismatched token and fails.
94
+ */
95
+ function matchesQQBotFallbackRequestAccount(params) {
96
+ const boundAccountId = resolveApprovalRequestChannelAccountId({
97
+ cfg: params.cfg,
98
+ request: params.request,
99
+ channel: "qqbot"
100
+ });
101
+ if (boundAccountId) {
102
+ if (!params.accountId) return false;
103
+ return normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId);
104
+ }
105
+ return countQQBotFallbackEligibleAccounts(params.cfg) <= 1;
106
+ }
107
+ /**
108
+ * Unified per-account ownership check used by both the profile and
109
+ * fallback approval paths. Dispatches to the profile rules when the
110
+ * current account has `execApprovals` configured, otherwise uses the
111
+ * fallback rules.
112
+ *
113
+ * This is the single source of truth for "does this QQBot handler own
114
+ * this approval request?" and is consumed by both the capability
115
+ * gate (shouldHandle) and the lazy native runtime adapter.
116
+ */
117
+ function matchesQQBotApprovalAccount(params) {
118
+ const normalized = {
119
+ cfg: params.cfg,
120
+ accountId: params.accountId,
121
+ request: params.request
122
+ };
123
+ if (resolveQQBotExecApprovalConfig(normalized) !== void 0) return matchesQQBotRequestAccount(normalized);
124
+ return matchesQQBotFallbackRequestAccount(normalized);
125
+ }
126
+ const qqbotExecApprovalProfile = createChannelExecApprovalProfile({
127
+ resolveConfig: resolveQQBotExecApprovalConfig,
128
+ resolveApprovers: getQQBotExecApprovalApprovers,
129
+ matchesRequestAccount: matchesQQBotRequestAccount,
130
+ fallbackAgentIdFromSessionKey: true,
131
+ requireClientEnabledForLocalPromptSuppression: false
132
+ });
133
+ const isQQBotExecApprovalClientEnabled = qqbotExecApprovalProfile.isClientEnabled;
134
+ const isQQBotExecApprovalApprover = qqbotExecApprovalProfile.isApprover;
135
+ const isQQBotExecApprovalAuthorizedSender = qqbotExecApprovalProfile.isAuthorizedSender;
136
+ const shouldHandleQQBotExecApprovalRequest = qqbotExecApprovalProfile.shouldHandleRequest;
137
+ //#endregion
138
+ export { resolveQQBotExecApprovalConfig as a, matchesQQBotApprovalAccount as i, isQQBotExecApprovalAuthorizedSender as n, shouldHandleQQBotExecApprovalRequest as o, isQQBotExecApprovalClientEnabled as r, isQQBotExecApprovalApprover as t };