@cxyhhhhh/openclaw-qqbot 2.0.0-dev.202607101321 → 2.0.0-dev.202607101939

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
@@ -31,6 +34,272 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
34
  ));
32
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
36
 
37
+ // src/config.ts
38
+ var config_exports = {};
39
+ __export(config_exports, {
40
+ DEFAULT_ACCOUNT_ID: () => DEFAULT_ACCOUNT_ID,
41
+ applyQQBotAccountConfig: () => applyQQBotAccountConfig,
42
+ isGroupAllowed: () => isGroupAllowed,
43
+ listQQBotAccountIds: () => listQQBotAccountIds,
44
+ resolveDefaultQQBotAccountId: () => resolveDefaultQQBotAccountId,
45
+ resolveGroupAllowFrom: () => resolveGroupAllowFrom,
46
+ resolveGroupConfig: () => resolveGroupConfig,
47
+ resolveGroupConfigFromAccount: () => resolveGroupConfigFromAccount,
48
+ resolveGroupName: () => resolveGroupName,
49
+ resolveGroupPolicy: () => resolveGroupPolicy,
50
+ resolveGroupPrompt: () => resolveGroupPrompt,
51
+ resolveHistoryLimit: () => resolveHistoryLimit,
52
+ resolveIgnoreOtherMentions: () => resolveIgnoreOtherMentions,
53
+ resolveMentionPatterns: () => resolveMentionPatterns,
54
+ resolveProcessingTimeoutMs: () => resolveProcessingTimeoutMs,
55
+ resolveQQBotAccount: () => resolveQQBotAccount,
56
+ resolveRequireMention: () => resolveRequireMention,
57
+ resolveToolPolicy: () => resolveToolPolicy,
58
+ resolveUserAgentSuffix: () => resolveUserAgentSuffix
59
+ });
60
+ function resolveMentionPatterns(cfg, agentId) {
61
+ if (agentId) {
62
+ const agents = cfg.agents;
63
+ const entry = agents?.list?.find((a) => a.id?.trim().toLowerCase() === agentId.trim().toLowerCase());
64
+ const agentGroupChat = entry?.groupChat;
65
+ if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) {
66
+ return agentGroupChat.mentionPatterns ?? [];
67
+ }
68
+ }
69
+ const globalGroupChat = cfg?.messages?.groupChat;
70
+ if (globalGroupChat && typeof globalGroupChat === "object" && Object.hasOwn(globalGroupChat, "mentionPatterns")) {
71
+ return globalGroupChat.mentionPatterns ?? [];
72
+ }
73
+ return [];
74
+ }
75
+ function evaluateMatchedGroupAccessForPolicy(params) {
76
+ if (params.groupPolicy === "disabled") {
77
+ return { allowed: false, groupPolicy: params.groupPolicy, reason: "disabled" };
78
+ }
79
+ if (params.groupPolicy === "allowlist") {
80
+ if (params.requireMatchInput && !params.hasMatchInput) {
81
+ return { allowed: false, groupPolicy: params.groupPolicy, reason: "missing_match_input" };
82
+ }
83
+ if (!params.allowlistConfigured) {
84
+ return { allowed: false, groupPolicy: params.groupPolicy, reason: "empty_allowlist" };
85
+ }
86
+ if (!params.allowlistMatched) {
87
+ return { allowed: false, groupPolicy: params.groupPolicy, reason: "not_allowlisted" };
88
+ }
89
+ }
90
+ return { allowed: true, groupPolicy: params.groupPolicy, reason: "allowed" };
91
+ }
92
+ function resolveGroupPolicy(cfg, accountId) {
93
+ const account = resolveQQBotAccount(cfg, accountId);
94
+ return account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;
95
+ }
96
+ function resolveGroupAllowFrom(cfg, accountId) {
97
+ const account = resolveQQBotAccount(cfg, accountId);
98
+ return (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());
99
+ }
100
+ function isGroupAllowed(cfg, groupOpenid, accountId) {
101
+ const account = resolveQQBotAccount(cfg, accountId);
102
+ const policy = account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;
103
+ const allowList = (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());
104
+ const allowlistConfigured = allowList.length > 0;
105
+ const allowlistMatched = allowList.some((id) => id === "*" || id === groupOpenid.toUpperCase());
106
+ return evaluateMatchedGroupAccessForPolicy({
107
+ groupPolicy: policy,
108
+ allowlistConfigured,
109
+ allowlistMatched
110
+ }).allowed;
111
+ }
112
+ function resolveGroupConfigFromAccount(account, groupOpenid) {
113
+ const groups = account.config?.groups ?? {};
114
+ const wildcardCfg = groups["*"] ?? {};
115
+ const specificCfg = groups[groupOpenid] ?? {};
116
+ const accountDefaultRequireMention = account.config?.defaultRequireMention ?? DEFAULT_GROUP_CONFIG.requireMention;
117
+ return {
118
+ requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ?? accountDefaultRequireMention,
119
+ ignoreOtherMentions: specificCfg.ignoreOtherMentions ?? wildcardCfg.ignoreOtherMentions ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
120
+ toolPolicy: specificCfg.toolPolicy ?? wildcardCfg.toolPolicy ?? DEFAULT_GROUP_CONFIG.toolPolicy,
121
+ name: specificCfg.name ?? wildcardCfg.name ?? DEFAULT_GROUP_CONFIG.name,
122
+ prompt: specificCfg.prompt ?? wildcardCfg.prompt ?? DEFAULT_GROUP_PROMPT,
123
+ historyLimit: specificCfg.historyLimit ?? wildcardCfg.historyLimit ?? DEFAULT_GROUP_CONFIG.historyLimit
124
+ };
125
+ }
126
+ function resolveGroupConfig(cfg, groupOpenid, accountId) {
127
+ return resolveGroupConfigFromAccount(resolveQQBotAccount(cfg, accountId), groupOpenid);
128
+ }
129
+ function resolveHistoryLimit(cfg, groupOpenid, accountId) {
130
+ return Math.max(0, resolveGroupConfig(cfg, groupOpenid, accountId).historyLimit);
131
+ }
132
+ function resolveGroupPrompt(cfg, groupOpenid, accountId) {
133
+ return resolveGroupConfig(cfg, groupOpenid, accountId).prompt;
134
+ }
135
+ function resolveRequireMention(cfg, groupOpenid, accountId) {
136
+ return resolveGroupConfig(cfg, groupOpenid, accountId).requireMention;
137
+ }
138
+ function resolveIgnoreOtherMentions(cfg, groupOpenid, accountId) {
139
+ return resolveGroupConfig(cfg, groupOpenid, accountId).ignoreOtherMentions;
140
+ }
141
+ function resolveToolPolicy(cfg, groupOpenid, accountId) {
142
+ return resolveGroupConfig(cfg, groupOpenid, accountId).toolPolicy;
143
+ }
144
+ function resolveGroupName(cfg, groupOpenid, accountId) {
145
+ const name = resolveGroupConfig(cfg, groupOpenid, accountId).name;
146
+ return name || groupOpenid.slice(0, 8);
147
+ }
148
+ function normalizeAppId(raw) {
149
+ if (raw === null || raw === void 0) return "";
150
+ return String(raw).trim();
151
+ }
152
+ function listQQBotAccountIds(cfg) {
153
+ const ids = /* @__PURE__ */ new Set();
154
+ const qqbot = cfg.channels?.qqbot;
155
+ if (qqbot?.appId) {
156
+ ids.add(DEFAULT_ACCOUNT_ID);
157
+ }
158
+ if (qqbot?.accounts) {
159
+ for (const accountId of Object.keys(qqbot.accounts)) {
160
+ if (qqbot.accounts[accountId]?.appId) {
161
+ ids.add(accountId);
162
+ }
163
+ }
164
+ }
165
+ return Array.from(ids);
166
+ }
167
+ function resolveDefaultQQBotAccountId(cfg) {
168
+ const qqbot = cfg.channels?.qqbot;
169
+ if (qqbot?.appId) {
170
+ return DEFAULT_ACCOUNT_ID;
171
+ }
172
+ if (qqbot?.accounts) {
173
+ const ids = Object.keys(qqbot.accounts);
174
+ if (ids.length > 0) {
175
+ return ids[0];
176
+ }
177
+ }
178
+ return DEFAULT_ACCOUNT_ID;
179
+ }
180
+ function resolveUserAgentSuffix(cfg) {
181
+ const qqbot = cfg.channels?.qqbot;
182
+ return qqbot?.userAgentSuffix ? String(qqbot.userAgentSuffix).trim() : "";
183
+ }
184
+ function resolveProcessingTimeoutMs(accountConfig) {
185
+ if (accountConfig?.processingTimeoutMs !== void 0) {
186
+ return accountConfig.processingTimeoutMs;
187
+ }
188
+ const env = process.env.OPENCLAW_PROCESSING_TIMEOUT_MS;
189
+ if (env) {
190
+ const v = Number(env);
191
+ if (!Number.isNaN(v) && v >= 0) return v;
192
+ }
193
+ return DEFAULT_PROCESSING_TIMEOUT_MS;
194
+ }
195
+ function resolveQQBotAccount(cfg, accountId) {
196
+ const resolvedAccountId = accountId ?? resolveDefaultQQBotAccountId(cfg);
197
+ const qqbot = cfg.channels?.qqbot;
198
+ let accountConfig = {};
199
+ let appId = "";
200
+ let clientSecret = "";
201
+ let secretSource = "none";
202
+ if (resolvedAccountId === DEFAULT_ACCOUNT_ID) {
203
+ const { accounts: _accounts, ...topLevelConfig } = qqbot ?? {};
204
+ accountConfig = {
205
+ ...topLevelConfig,
206
+ markdownSupport: qqbot?.markdownSupport ?? true
207
+ };
208
+ appId = normalizeAppId(qqbot?.appId);
209
+ } else {
210
+ const account = qqbot?.accounts?.[resolvedAccountId];
211
+ accountConfig = account ?? {};
212
+ appId = normalizeAppId(account?.appId);
213
+ }
214
+ if (accountConfig.clientSecret) {
215
+ clientSecret = accountConfig.clientSecret;
216
+ secretSource = "config";
217
+ } else if (accountConfig.clientSecretFile) {
218
+ secretSource = "file";
219
+ } else if (process.env.QQBOT_CLIENT_SECRET && resolvedAccountId === DEFAULT_ACCOUNT_ID) {
220
+ clientSecret = process.env.QQBOT_CLIENT_SECRET;
221
+ secretSource = "env";
222
+ }
223
+ if (!appId && process.env.QQBOT_APP_ID && resolvedAccountId === DEFAULT_ACCOUNT_ID) {
224
+ appId = normalizeAppId(process.env.QQBOT_APP_ID);
225
+ }
226
+ return {
227
+ accountId: resolvedAccountId,
228
+ name: accountConfig.name,
229
+ enabled: accountConfig.enabled !== false,
230
+ appId,
231
+ clientSecret,
232
+ secretSource,
233
+ systemPrompt: accountConfig.systemPrompt,
234
+ markdownSupport: accountConfig.markdownSupport !== false,
235
+ userAgentSuffix: resolveUserAgentSuffix(cfg),
236
+ processingTimeoutMs: resolveProcessingTimeoutMs(accountConfig),
237
+ config: accountConfig
238
+ };
239
+ }
240
+ function applyQQBotAccountConfig(cfg, accountId, input) {
241
+ const next = { ...cfg };
242
+ if (accountId === DEFAULT_ACCOUNT_ID) {
243
+ const existingConfig = next.channels?.qqbot || {};
244
+ const allowFrom = existingConfig.allowFrom ?? ["*"];
245
+ next.channels = {
246
+ ...next.channels,
247
+ qqbot: {
248
+ ...next.channels?.qqbot || {},
249
+ enabled: true,
250
+ allowFrom,
251
+ ...input.appId ? { appId: input.appId } : {},
252
+ ...input.clientSecret ? { clientSecret: input.clientSecret } : input.clientSecretFile ? { clientSecretFile: input.clientSecretFile } : {},
253
+ ...input.name ? { name: input.name } : {}
254
+ }
255
+ };
256
+ } else {
257
+ const existingAccountConfig = next.channels?.qqbot?.accounts?.[accountId] || {};
258
+ const allowFrom = existingAccountConfig.allowFrom ?? ["*"];
259
+ next.channels = {
260
+ ...next.channels,
261
+ qqbot: {
262
+ ...next.channels?.qqbot || {},
263
+ enabled: true,
264
+ accounts: {
265
+ ...next.channels?.qqbot?.accounts || {},
266
+ [accountId]: {
267
+ ...next.channels?.qqbot?.accounts?.[accountId] || {},
268
+ enabled: true,
269
+ allowFrom,
270
+ ...input.appId ? { appId: input.appId } : {},
271
+ ...input.clientSecret ? { clientSecret: input.clientSecret } : input.clientSecretFile ? { clientSecretFile: input.clientSecretFile } : {},
272
+ ...input.name ? { name: input.name } : {}
273
+ }
274
+ }
275
+ }
276
+ };
277
+ }
278
+ return next;
279
+ }
280
+ var DEFAULT_ACCOUNT_ID, DEFAULT_GROUP_POLICY, DEFAULT_GROUP_HISTORY_LIMIT, DEFAULT_PROCESSING_TIMEOUT_MS, DEFAULT_GROUP_CONFIG, DEFAULT_GROUP_PROMPT;
281
+ var init_config = __esm({
282
+ "src/config.ts"() {
283
+ "use strict";
284
+ DEFAULT_ACCOUNT_ID = "default";
285
+ DEFAULT_GROUP_POLICY = "open";
286
+ DEFAULT_GROUP_HISTORY_LIMIT = 20;
287
+ DEFAULT_PROCESSING_TIMEOUT_MS = 0;
288
+ DEFAULT_GROUP_CONFIG = {
289
+ requireMention: true,
290
+ ignoreOtherMentions: false,
291
+ toolPolicy: "restricted",
292
+ name: "",
293
+ historyLimit: DEFAULT_GROUP_HISTORY_LIMIT
294
+ };
295
+ DEFAULT_GROUP_PROMPT = [
296
+ "\u82E5\u53D1\u9001\u8005\u4E3A\u673A\u5668\u4EBA\uFF0C\u4EC5\u5728\u5BF9\u65B9\u660E\u786E@\u4F60\u63D0\u95EE\u6216\u8BF7\u6C42\u534F\u52A9\u5177\u4F53\u4EFB\u52A1\u65F6\uFF0C\u4EE5\u7B80\u6D01\u660E\u4E86\u7684\u5185\u5BB9\u56DE\u590D\uFF0C",
297
+ "\u907F\u514D\u4E0E\u5176\u4ED6\u673A\u5668\u4EBA\u4EA7\u751F\u62A2\u7B54\u6216\u591A\u8F6E\u65E0\u610F\u4E49\u5BF9\u8BDD\u3002",
298
+ "\u5728\u7FA4\u804A\u4E2D\u4F18\u5148\u8BA9\u4EBA\u7C7B\u7528\u6237\u7684\u6D88\u606F\u5F97\u5230\u54CD\u5E94\uFF0C\u673A\u5668\u4EBA\u4E4B\u95F4\u4FDD\u6301\u534F\u4F5C\u800C\u975E\u7ADE\u4E89\uFF0C\u786E\u4FDD\u5BF9\u8BDD\u6709\u5E8F\u4E0D\u5237\u5C4F\u3002"
299
+ ].join("");
300
+ }
301
+ });
302
+
34
303
  // node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/constants.js
35
304
  var require_constants = __commonJS({
36
305
  "node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/constants.js"(exports2, module2) {
@@ -3710,16 +3979,248 @@ var require_websocket_server = __commonJS({
3710
3979
  }
3711
3980
  });
3712
3981
 
3713
- // node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
3714
- var require_QRMode = __commonJS({
3715
- "node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js"(exports2, module2) {
3716
- "use strict";
3717
- module2.exports = {
3718
- MODE_NUMBER: 1 << 0,
3719
- MODE_ALPHA_NUM: 1 << 1,
3720
- MODE_8BIT_BYTE: 1 << 2,
3721
- MODE_KANJI: 1 << 3
3722
- };
3982
+ // src/adapter/resolve.ts
3983
+ var resolve_exports = {};
3984
+ __export(resolve_exports, {
3985
+ _resetAdaptersCache: () => _resetAdaptersCache,
3986
+ getAdapters: () => getAdapters,
3987
+ persistAuthConfig: () => persistAuthConfig,
3988
+ resolveRuntimeAdapters: () => resolveRuntimeAdapters
3989
+ });
3990
+ function probeFunction(rt, paths) {
3991
+ for (const path22 of paths) {
3992
+ let target = rt;
3993
+ let parent = rt;
3994
+ for (let i = 0; i < path22.length; i++) {
3995
+ parent = target;
3996
+ target = target?.[path22[i]];
3997
+ if (target === void 0 || target === null) break;
3998
+ }
3999
+ if (typeof target === "function") {
4000
+ return target.bind(parent);
4001
+ }
4002
+ }
4003
+ return null;
4004
+ }
4005
+ function resolveRuntimeAdapters(rt, log4) {
4006
+ const version = rt.version ?? "unknown";
4007
+ const inboundRun = probeFunction(rt, [
4008
+ ["channel", "inbound", "run"],
4009
+ // current (2026-05+)
4010
+ ["channel", "turn", "run"]
4011
+ // legacy (removed 2026-05-27)
4012
+ ]);
4013
+ const dispatchReply = probeFunction(rt, [
4014
+ ["channel", "reply", "dispatchReplyWithBufferedBlockDispatcher"]
4015
+ ]);
4016
+ const resolveAgentRoute = probeFunction(rt, [
4017
+ ["channel", "routing", "resolveAgentRoute"]
4018
+ ]);
4019
+ const rawBuildContext = probeFunction(rt, [
4020
+ ["channel", "inbound", "buildContext"]
4021
+ ]);
4022
+ const rawFinalizeContext = !rawBuildContext ? probeFunction(rt, [["channel", "reply", "finalizeInboundContext"]]) : null;
4023
+ const buildInboundContext = rawBuildContext ? (params) => rawBuildContext(params) : rawFinalizeContext ? (params) => {
4024
+ const isCommand = params.access?.commands?.authorized ?? false;
4025
+ const rawCtx = {
4026
+ Body: params.message.body,
4027
+ BodyForAgent: params.message.bodyForAgent,
4028
+ RawBody: params.message.rawBody,
4029
+ CommandBody: params.message.commandBody ?? params.message.rawBody,
4030
+ CommandSource: isCommand ? "text" : void 0,
4031
+ CommandTurn: params.command ?? void 0,
4032
+ CommandAuthorized: isCommand,
4033
+ From: params.from,
4034
+ To: params.reply.to,
4035
+ SessionKey: params.route.routeSessionKey,
4036
+ AccountId: params.route.accountId ?? params.accountId,
4037
+ ChatType: params.conversation.kind,
4038
+ GroupSystemPrompt: params.conversation.label,
4039
+ SenderId: params.sender.id,
4040
+ SenderName: params.sender.name,
4041
+ Provider: params.provider ?? params.channel,
4042
+ Surface: params.surface ?? params.channel,
4043
+ MessageSid: params.messageId,
4044
+ Timestamp: params.timestamp ?? Date.now(),
4045
+ OriginatingChannel: params.channel,
4046
+ OriginatingTo: params.reply.originatingTo ?? params.reply.to,
4047
+ ...params.extra
4048
+ };
4049
+ return rawFinalizeContext(rawCtx);
4050
+ } : null;
4051
+ const resolveStorePath = probeFunction(rt, [
4052
+ ["channel", "session", "resolveStorePath"]
4053
+ ]);
4054
+ const recordInboundSession = probeFunction(rt, [
4055
+ ["channel", "session", "recordInboundSession"]
4056
+ ]);
4057
+ const formatEnvelope = probeFunction(rt, [
4058
+ ["channel", "reply", "formatAgentEnvelope"],
4059
+ // current (2026-06+)
4060
+ ["channel", "reply", "formatInboundEnvelope"]
4061
+ // deprecated,低版本兼容
4062
+ ]);
4063
+ const resolveEnvelopeFormatOptions = probeFunction(rt, [
4064
+ ["channel", "reply", "resolveEnvelopeFormatOptions"]
4065
+ ]);
4066
+ const chunkMarkdownText = probeFunction(rt, [
4067
+ ["channel", "text", "chunkMarkdownText"]
4068
+ ]);
4069
+ const saveRemoteMedia = probeFunction(rt, [
4070
+ ["channel", "media", "saveRemoteMedia"]
4071
+ ]);
4072
+ const getConfig = probeFunction(rt, [
4073
+ ["config", "current"]
4074
+ ]) ?? probeFunction(rt, [
4075
+ ["getConfig"]
4076
+ ]) ?? probeFunction(rt, [
4077
+ ["config", "loadConfig"]
4078
+ ]);
4079
+ const rawMutateConfig = probeFunction(rt, [["config", "mutateConfigFile"]]);
4080
+ const rawWriteConfig = probeFunction(rt, [["config", "writeConfigFile"]]);
4081
+ const persistConfig = rawMutateConfig ? async (mutator) => {
4082
+ await rawMutateConfig({
4083
+ afterWrite: "hot-reload",
4084
+ mutate: mutator
4085
+ });
4086
+ } : rawWriteConfig && getConfig ? async (mutator) => {
4087
+ const current = JSON.parse(JSON.stringify(getConfig()));
4088
+ mutator(current);
4089
+ await rawWriteConfig(current);
4090
+ } : null;
4091
+ const resolved = [
4092
+ inboundRun && "inboundRun",
4093
+ dispatchReply && "dispatchReply",
4094
+ resolveAgentRoute && "resolveAgentRoute",
4095
+ buildInboundContext && "buildInboundContext",
4096
+ resolveStorePath && "resolveStorePath",
4097
+ recordInboundSession && "recordInboundSession",
4098
+ formatEnvelope && "formatEnvelope",
4099
+ chunkMarkdownText && "chunkMarkdownText",
4100
+ saveRemoteMedia && "saveRemoteMedia",
4101
+ getConfig && "getConfig",
4102
+ persistConfig && `persistConfig(${rawMutateConfig ? "mutate" : "write"})`
4103
+ ].filter(Boolean);
4104
+ log4?.info(
4105
+ `[qqbot:adapter] openclaw=${version} resolved ${resolved.length} adapters: ${resolved.join(", ")}`
4106
+ );
4107
+ return {
4108
+ inboundRun,
4109
+ dispatchReply,
4110
+ resolveAgentRoute,
4111
+ buildInboundContext,
4112
+ resolveStorePath,
4113
+ recordInboundSession,
4114
+ formatEnvelope,
4115
+ resolveEnvelopeFormatOptions,
4116
+ chunkMarkdownText,
4117
+ saveRemoteMedia,
4118
+ getConfig,
4119
+ persistConfig,
4120
+ version
4121
+ };
4122
+ }
4123
+ function getAdapters(rt, log4) {
4124
+ const cached = _cachedRuntimeRef?.deref();
4125
+ if (cached === rt && _cachedAdapters) {
4126
+ return _cachedAdapters;
4127
+ }
4128
+ _cachedAdapters = resolveRuntimeAdapters(rt, log4);
4129
+ _cachedRuntimeRef = new WeakRef(rt);
4130
+ return _cachedAdapters;
4131
+ }
4132
+ async function persistAuthConfig(runtime2, cfg, afterWrite = "restart") {
4133
+ const config = runtime2.config;
4134
+ if (typeof config?.mutateConfigFile === "function") {
4135
+ await config.mutateConfigFile({
4136
+ mutate: () => cfg,
4137
+ afterWrite
4138
+ });
4139
+ return;
4140
+ }
4141
+ if (typeof config?.writeConfigFile === "function") {
4142
+ await config.writeConfigFile(cfg);
4143
+ return;
4144
+ }
4145
+ if (typeof runtime2.writeConfigFile === "function") {
4146
+ await runtime2.writeConfigFile(cfg);
4147
+ return;
4148
+ }
4149
+ const { homedir: homedir5 } = await import("os");
4150
+ const { join: join8 } = await import("path");
4151
+ const { writeFileSync: writeFileSync5 } = await import("fs");
4152
+ writeFileSync5(join8(homedir5(), ".openclaw", "openclaw.json"), JSON.stringify(cfg, null, 2) + "\n", "utf-8");
4153
+ }
4154
+ function _resetAdaptersCache() {
4155
+ _cachedAdapters = null;
4156
+ _cachedRuntimeRef = null;
4157
+ }
4158
+ var _cachedAdapters, _cachedRuntimeRef;
4159
+ var init_resolve = __esm({
4160
+ "src/adapter/resolve.ts"() {
4161
+ "use strict";
4162
+ _cachedAdapters = null;
4163
+ _cachedRuntimeRef = null;
4164
+ }
4165
+ });
4166
+
4167
+ // src/adapter/setup.ts
4168
+ function loadSetup() {
4169
+ if (_setup !== void 0) return _setup;
4170
+ try {
4171
+ _setup = req2("openclaw/plugin-sdk/setup");
4172
+ } catch {
4173
+ _setup = null;
4174
+ }
4175
+ return _setup;
4176
+ }
4177
+ function loadTools() {
4178
+ if (_tools !== void 0) return _tools;
4179
+ try {
4180
+ _tools = req2("openclaw/plugin-sdk/setup-tools");
4181
+ } catch {
4182
+ _tools = null;
4183
+ }
4184
+ return _tools;
4185
+ }
4186
+ function createStandardChannelSetupStatus(...args) {
4187
+ const mod = loadSetup();
4188
+ if (mod) return mod.createStandardChannelSetupStatus(...args);
4189
+ return {
4190
+ channelLabel: args[0]?.channelLabel ?? "QQ Bot",
4191
+ configuredLabel: "Configured",
4192
+ unconfiguredLabel: "Not configured",
4193
+ resolveConfigured: () => false
4194
+ };
4195
+ }
4196
+ function setSetupChannelEnabled(...args) {
4197
+ loadSetup()?.setSetupChannelEnabled?.(...args);
4198
+ }
4199
+ function formatDocsLink(...args) {
4200
+ const mod = loadTools();
4201
+ if (mod) return mod.formatDocsLink(...args);
4202
+ return args[1] ? `${args[1]}: ${args[0]}` : args[0];
4203
+ }
4204
+ var import_node_module2, req2, _setup, _tools, DEFAULT_ACCOUNT_ID2;
4205
+ var init_setup = __esm({
4206
+ "src/adapter/setup.ts"() {
4207
+ "use strict";
4208
+ import_node_module2 = require("module");
4209
+ req2 = (0, import_node_module2.createRequire)(__filename);
4210
+ DEFAULT_ACCOUNT_ID2 = "default";
4211
+ }
4212
+ });
4213
+
4214
+ // node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
4215
+ var require_QRMode = __commonJS({
4216
+ "node_modules/.pnpm/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js"(exports2, module2) {
4217
+ "use strict";
4218
+ module2.exports = {
4219
+ MODE_NUMBER: 1 << 0,
4220
+ MODE_ALPHA_NUM: 1 << 1,
4221
+ MODE_8BIT_BYTE: 1 << 2,
4222
+ MODE_KANJI: 1 << 3
4223
+ };
3723
4224
  }
3724
4225
  });
3725
4226
 
@@ -4791,295 +5292,323 @@ var require_main = __commonJS({
4791
5292
  }
4792
5293
  });
4793
5294
 
4794
- // index.ts
4795
- var index_exports = {};
4796
- __export(index_exports, {
4797
- DEFAULT_ACCOUNT_ID: () => DEFAULT_ACCOUNT_ID,
4798
- MSG_TYPE_QUOTE: () => MSG_TYPE_QUOTE2,
4799
- MSG_TYPE_TEXT: () => MSG_TYPE_TEXT,
4800
- PersistedRefIndexStore: () => PersistedRefIndexStore,
4801
- QQBotGateway: () => QQBotGateway,
4802
- StreamContentType: () => StreamContentType2,
4803
- StreamInputMode: () => StreamInputMode2,
4804
- StreamInputState: () => StreamInputState2,
4805
- StreamingController: () => StreamingController,
4806
- applyQQBotAccountConfig: () => applyQQBotAccountConfig,
4807
- buildUserAgent: () => buildUserAgent,
4808
- default: () => index_default,
4809
- dispatchToOpenClaw: () => dispatchToOpenClaw,
4810
- flushAllRefIndexStores: () => flushAllRefIndexStores,
4811
- getBotForAccount: () => getBotForAccount,
4812
- getPersistedRefIndexStore: () => getPersistedRefIndexStore,
4813
- getQQBotRuntime: () => getQQBotRuntime,
4814
- isGroupAllowed: () => isGroupAllowed,
4815
- listQQBotAccountIds: () => listQQBotAccountIds,
4816
- parseTarget: () => parseTarget,
4817
- qqbotOnboardingAdapter: () => qqbotOnboardingAdapter,
4818
- qqbotPlugin: () => qqbotPlugin,
4819
- resolveDefaultQQBotAccountId: () => resolveDefaultQQBotAccountId,
4820
- resolveGroupAllowFrom: () => resolveGroupAllowFrom,
4821
- resolveGroupConfig: () => resolveGroupConfig,
4822
- resolveGroupConfigFromAccount: () => resolveGroupConfigFromAccount,
4823
- resolveGroupName: () => resolveGroupName,
4824
- resolveGroupPolicy: () => resolveGroupPolicy,
4825
- resolveGroupPrompt: () => resolveGroupPrompt,
4826
- resolveHistoryLimit: () => resolveHistoryLimit,
4827
- resolveIgnoreOtherMentions: () => resolveIgnoreOtherMentions,
4828
- resolveMentionPatterns: () => resolveMentionPatterns,
4829
- resolveProcessingTimeoutMs: () => resolveProcessingTimeoutMs,
4830
- resolveQQBotAccount: () => resolveQQBotAccount,
4831
- resolveRequireMention: () => resolveRequireMention,
4832
- resolveToolPolicy: () => resolveToolPolicy,
4833
- resolveUserAgentSuffix: () => resolveUserAgentSuffix,
4834
- sendMedia: () => sendMedia,
4835
- sendText: () => sendText,
4836
- setQQBotRuntime: () => setQQBotRuntime,
4837
- shouldUseStreaming: () => shouldUseStreaming,
4838
- tryGetBotForAccount: () => tryGetBotForAccount
4839
- });
4840
- module.exports = __toCommonJS(index_exports);
4841
- var import_plugin_sdk = require("openclaw/plugin-sdk");
4842
-
4843
- // src/channel.ts
4844
- var import_core = require("openclaw/plugin-sdk/core");
4845
-
4846
- // src/config.ts
4847
- function resolveMentionPatterns(cfg, agentId) {
4848
- if (agentId) {
4849
- const agents = cfg.agents;
4850
- const entry = agents?.list?.find((a) => a.id?.trim().toLowerCase() === agentId.trim().toLowerCase());
4851
- const agentGroupChat = entry?.groupChat;
4852
- if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) {
4853
- return agentGroupChat.mentionPatterns ?? [];
4854
- }
4855
- }
4856
- const globalGroupChat = cfg?.messages?.groupChat;
4857
- if (globalGroupChat && typeof globalGroupChat === "object" && Object.hasOwn(globalGroupChat, "mentionPatterns")) {
4858
- return globalGroupChat.mentionPatterns ?? [];
4859
- }
4860
- return [];
4861
- }
4862
- var DEFAULT_ACCOUNT_ID = "default";
4863
- function evaluateMatchedGroupAccessForPolicy(params) {
4864
- if (params.groupPolicy === "disabled") {
4865
- return { allowed: false, groupPolicy: params.groupPolicy, reason: "disabled" };
4866
- }
4867
- if (params.groupPolicy === "allowlist") {
4868
- if (params.requireMatchInput && !params.hasMatchInput) {
4869
- return { allowed: false, groupPolicy: params.groupPolicy, reason: "missing_match_input" };
4870
- }
4871
- if (!params.allowlistConfigured) {
4872
- return { allowed: false, groupPolicy: params.groupPolicy, reason: "empty_allowlist" };
4873
- }
4874
- if (!params.allowlistMatched) {
4875
- return { allowed: false, groupPolicy: params.groupPolicy, reason: "not_allowlisted" };
4876
- }
4877
- }
4878
- return { allowed: true, groupPolicy: params.groupPolicy, reason: "allowed" };
4879
- }
4880
- var DEFAULT_GROUP_POLICY = "open";
4881
- var DEFAULT_GROUP_HISTORY_LIMIT = 20;
4882
- var DEFAULT_PROCESSING_TIMEOUT_MS = 0;
4883
- var DEFAULT_GROUP_CONFIG = {
4884
- requireMention: true,
4885
- ignoreOtherMentions: false,
4886
- toolPolicy: "restricted",
4887
- name: "",
4888
- historyLimit: DEFAULT_GROUP_HISTORY_LIMIT
4889
- };
4890
- var DEFAULT_GROUP_PROMPT = [
4891
- "\u82E5\u53D1\u9001\u8005\u4E3A\u673A\u5668\u4EBA\uFF0C\u4EC5\u5728\u5BF9\u65B9\u660E\u786E@\u4F60\u63D0\u95EE\u6216\u8BF7\u6C42\u534F\u52A9\u5177\u4F53\u4EFB\u52A1\u65F6\uFF0C\u4EE5\u7B80\u6D01\u660E\u4E86\u7684\u5185\u5BB9\u56DE\u590D\uFF0C",
4892
- "\u907F\u514D\u4E0E\u5176\u4ED6\u673A\u5668\u4EBA\u4EA7\u751F\u62A2\u7B54\u6216\u591A\u8F6E\u65E0\u610F\u4E49\u5BF9\u8BDD\u3002",
4893
- "\u5728\u7FA4\u804A\u4E2D\u4F18\u5148\u8BA9\u4EBA\u7C7B\u7528\u6237\u7684\u6D88\u606F\u5F97\u5230\u54CD\u5E94\uFF0C\u673A\u5668\u4EBA\u4E4B\u95F4\u4FDD\u6301\u534F\u4F5C\u800C\u975E\u7ADE\u4E89\uFF0C\u786E\u4FDD\u5BF9\u8BDD\u6709\u5E8F\u4E0D\u5237\u5C4F\u3002"
4894
- ].join("");
4895
- function resolveGroupPolicy(cfg, accountId) {
4896
- const account = resolveQQBotAccount(cfg, accountId);
4897
- return account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;
5295
+ // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qqbot-session.js
5296
+ function d(t = "production") {
5297
+ return E[t];
4898
5298
  }
4899
- function resolveGroupAllowFrom(cfg, accountId) {
4900
- const account = resolveQQBotAccount(cfg, accountId);
4901
- return (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());
5299
+ function l() {
5300
+ return import_node_crypto.default.randomBytes(32).toString("base64");
4902
5301
  }
4903
- function isGroupAllowed(cfg, groupOpenid, accountId) {
4904
- const account = resolveQQBotAccount(cfg, accountId);
4905
- const policy = account.config?.groupPolicy ?? DEFAULT_GROUP_POLICY;
4906
- const allowList = (account.config?.groupAllowFrom ?? []).map((id) => String(id).trim().toUpperCase());
4907
- const allowlistConfigured = allowList.length > 0;
4908
- const allowlistMatched = allowList.some((id) => id === "*" || id === groupOpenid.toUpperCase());
4909
- return evaluateMatchedGroupAccessForPolicy({
4910
- groupPolicy: policy,
4911
- allowlistConfigured,
4912
- allowlistMatched
4913
- }).allowed;
5302
+ function b(t, r) {
5303
+ const a = Buffer.from(r, "base64"), n = Buffer.from(t, "base64"), e = n.subarray(0, 12), c = n.subarray(n.length - 16), s = n.subarray(12, n.length - 16), o = import_node_crypto.default.createDecipheriv("aes-256-gcm", a, e);
5304
+ return o.setAuthTag(c), Buffer.concat([o.update(s), o.final()]).toString("utf8");
4914
5305
  }
4915
- function resolveGroupConfigFromAccount(account, groupOpenid) {
4916
- const groups = account.config?.groups ?? {};
4917
- const wildcardCfg = groups["*"] ?? {};
4918
- const specificCfg = groups[groupOpenid] ?? {};
4919
- const accountDefaultRequireMention = account.config?.defaultRequireMention ?? DEFAULT_GROUP_CONFIG.requireMention;
4920
- return {
4921
- requireMention: specificCfg.requireMention ?? wildcardCfg.requireMention ?? accountDefaultRequireMention,
4922
- ignoreOtherMentions: specificCfg.ignoreOtherMentions ?? wildcardCfg.ignoreOtherMentions ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
4923
- toolPolicy: specificCfg.toolPolicy ?? wildcardCfg.toolPolicy ?? DEFAULT_GROUP_CONFIG.toolPolicy,
4924
- name: specificCfg.name ?? wildcardCfg.name ?? DEFAULT_GROUP_CONFIG.name,
4925
- prompt: specificCfg.prompt ?? wildcardCfg.prompt ?? DEFAULT_GROUP_PROMPT,
4926
- historyLimit: specificCfg.historyLimit ?? wildcardCfg.historyLimit ?? DEFAULT_GROUP_CONFIG.historyLimit
4927
- };
5306
+ function h(t, r, a) {
5307
+ return new Promise((n, e) => {
5308
+ const c = JSON.stringify(r), s = new URL(t), o = import_node_https.default.request({ hostname: s.hostname, path: s.pathname + s.search, method: "POST", timeout: a, headers: { "Content-Type": "application/json", Accept: "application/json", "Content-Length": Buffer.byteLength(c) } }, (i) => {
5309
+ if (i.statusCode !== 200) {
5310
+ i.resume(), e(new Error(`HTTP ${i.statusCode} from ${t}`));
5311
+ return;
5312
+ }
5313
+ let f = "";
5314
+ i.on("data", (p2) => {
5315
+ f += p2;
5316
+ }), i.on("end", () => {
5317
+ try {
5318
+ n(JSON.parse(f));
5319
+ } catch (p2) {
5320
+ e(p2);
5321
+ }
5322
+ });
5323
+ });
5324
+ o.on("error", e), o.on("timeout", () => {
5325
+ o.destroy(), e(new Error(`timeout fetching ${t}`));
5326
+ }), o.end(c);
5327
+ });
4928
5328
  }
4929
- function resolveGroupConfig(cfg, groupOpenid, accountId) {
4930
- return resolveGroupConfigFromAccount(resolveQQBotAccount(cfg, accountId), groupOpenid);
5329
+ async function y(t = "production", r = 1e4) {
5330
+ const a = `https://${d(t)}/lite/create_bind_task`, n = l(), e = await h(a, { key: n }, r);
5331
+ if (e.retcode !== 0) throw new Error(e.msg ?? "create_bind_task failed");
5332
+ if (!e.data?.task_id) throw new Error("create_bind_task: missing task_id");
5333
+ return { taskId: e.data.task_id, key: n };
4931
5334
  }
4932
- function resolveHistoryLimit(cfg, groupOpenid, accountId) {
4933
- return Math.max(0, resolveGroupConfig(cfg, groupOpenid, accountId).historyLimit);
5335
+ async function g(t, r = "production", a = 1e4) {
5336
+ const n = `https://${d(r)}/lite/poll_bind_result`, e = await h(n, { task_id: t }, a);
5337
+ if (e.retcode !== 0) throw new Error(e.msg ?? "poll_bind_result failed");
5338
+ return { status: e.data?.status ?? u.NONE, botAppId: String(e.data?.bot_appid ?? ""), botEncryptSecret: e.data?.bot_encrypt_secret ?? "", userOpenid: e.data?.user_openid || void 0 };
4934
5339
  }
4935
- function resolveGroupPrompt(cfg, groupOpenid, accountId) {
4936
- return resolveGroupConfig(cfg, groupOpenid, accountId).prompt;
5340
+ function w(t, r = "") {
5341
+ return `https://${d("production")}/qqbot/openclaw/connect.html?task_id=${encodeURIComponent(t)}&source=${encodeURIComponent(r)}&_wv=2`;
4937
5342
  }
4938
- function resolveRequireMention(cfg, groupOpenid, accountId) {
4939
- return resolveGroupConfig(cfg, groupOpenid, accountId).requireMention;
5343
+ var import_node_crypto, import_node_https, E, u;
5344
+ var init_qqbot_session = __esm({
5345
+ "node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qqbot-session.js"() {
5346
+ "use strict";
5347
+ import_node_crypto = __toESM(require("crypto"), 1);
5348
+ import_node_https = __toESM(require("https"), 1);
5349
+ E = { production: "q.qq.com", test: "test.q.qq.com" };
5350
+ (function(t) {
5351
+ t[t.NONE = 0] = "NONE", t[t.PENDING = 1] = "PENDING", t[t.COMPLETED = 2] = "COMPLETED", t[t.EXPIRED = 3] = "EXPIRED";
5352
+ })(u || (u = {}));
5353
+ }
5354
+ });
5355
+
5356
+ // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qr-connect.js
5357
+ function F(r) {
5358
+ return new Promise((o) => {
5359
+ import_qrcode_terminal.default.generate(r, { small: true }, (e) => {
5360
+ o(e);
5361
+ });
5362
+ });
4940
5363
  }
4941
- function resolveIgnoreOtherMentions(cfg, groupOpenid, accountId) {
4942
- return resolveGroupConfig(cfg, groupOpenid, accountId).ignoreOtherMentions;
5364
+ function d2(r, o) {
5365
+ return new Promise((e, t) => {
5366
+ if (o?.aborted) {
5367
+ t(new DOMException("Aborted", "AbortError"));
5368
+ return;
5369
+ }
5370
+ const n = setTimeout(e, r);
5371
+ o?.addEventListener("abort", () => {
5372
+ clearTimeout(n), t(new DOMException("Aborted", "AbortError"));
5373
+ }, { once: true });
5374
+ });
4943
5375
  }
4944
- function resolveToolPolicy(cfg, groupOpenid, accountId) {
4945
- return resolveGroupConfig(cfg, groupOpenid, accountId).toolPolicy;
5376
+ async function C(r, o, e) {
5377
+ for (; !e?.aborted; ) {
5378
+ let t;
5379
+ try {
5380
+ t = await g(r);
5381
+ } catch {
5382
+ await d2(p, e);
5383
+ continue;
5384
+ }
5385
+ if (t.status === u.COMPLETED) {
5386
+ const n = b(t.botEncryptSecret, o);
5387
+ return { outcome: "scanned", appId: t.botAppId, appSecret: n, userOpenid: t.userOpenid };
5388
+ }
5389
+ if (t.status === u.EXPIRED) return { outcome: "expired" };
5390
+ await d2(p, e);
5391
+ }
5392
+ throw new DOMException("Aborted", "AbortError");
4946
5393
  }
4947
- function resolveGroupName(cfg, groupOpenid, accountId) {
4948
- const name = resolveGroupConfig(cfg, groupOpenid, accountId).name;
4949
- return name || groupOpenid.slice(0, 8);
5394
+ function l2(r, o) {
5395
+ const e = new AbortController(), t = o?.signal ? AbortSignal.any([e.signal, o.signal]) : e.signal;
5396
+ return (async () => {
5397
+ const n = o?.displayQrCodeToConsole ?? true;
5398
+ for (; ; ) {
5399
+ if (t.aborted) throw new DOMException("Aborted", "AbortError");
5400
+ let a;
5401
+ try {
5402
+ a = await y();
5403
+ } catch (u2) {
5404
+ throw new Error(`\u83B7\u53D6\u7ED1\u5B9A\u4EFB\u52A1\u5931\u8D25: ${u2 instanceof Error ? u2.message : String(u2)}`, { cause: u2 });
5405
+ }
5406
+ const i = w(a.taskId, o?.source);
5407
+ if (n) {
5408
+ const u2 = await F(i);
5409
+ console.log(u2), console.log(`\u8BF7\u4F7F\u7528\u624B\u673A QQ \u626B\u63CF\u4E0A\u65B9\u4E8C\u7EF4\u7801\uFF0C\u5B8C\u6210\u673A\u5668\u4EBA\u7ED1\u5B9A\u3002
5410
+ `);
5411
+ }
5412
+ r.onQrDisplayed?.(i);
5413
+ const s = await C(a.taskId, a.key, t);
5414
+ if (s.outcome === "scanned") {
5415
+ r.onSuccess([{ appId: s.appId, appSecret: s.appSecret, userOpenid: s.userOpenid }]);
5416
+ return;
5417
+ }
5418
+ r.onQrExpired?.(), n && console.log(`\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u5237\u65B0\u2026
5419
+ `);
5420
+ }
5421
+ })().catch((n) => {
5422
+ if (n instanceof DOMException && n.name === "AbortError") {
5423
+ r.onFailure(new Error("\u5DF2\u53D6\u6D88"));
5424
+ return;
5425
+ }
5426
+ r.onFailure(n instanceof Error ? n : new Error(String(n)));
5427
+ }), () => e.abort();
4950
5428
  }
4951
- function normalizeAppId(raw) {
4952
- if (raw === null || raw === void 0) return "";
4953
- return String(raw).trim();
5429
+ function m2(r) {
5430
+ return new Promise((o, e) => {
5431
+ l2({ onSuccess: o, onFailure: e }, { ...r, displayQrCodeToConsole: true });
5432
+ });
4954
5433
  }
4955
- function listQQBotAccountIds(cfg) {
4956
- const ids = /* @__PURE__ */ new Set();
4957
- const qqbot = cfg.channels?.qqbot;
4958
- if (qqbot?.appId) {
4959
- ids.add(DEFAULT_ACCOUNT_ID);
5434
+ var import_qrcode_terminal, p;
5435
+ var init_qr_connect = __esm({
5436
+ "node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qr-connect.js"() {
5437
+ "use strict";
5438
+ import_qrcode_terminal = __toESM(require_main(), 1);
5439
+ init_qqbot_session();
5440
+ p = 2e3;
4960
5441
  }
4961
- if (qqbot?.accounts) {
4962
- for (const accountId of Object.keys(qqbot.accounts)) {
4963
- if (qqbot.accounts[accountId]?.appId) {
4964
- ids.add(accountId);
4965
- }
4966
- }
5442
+ });
5443
+
5444
+ // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/index.js
5445
+ var init_esm = __esm({
5446
+ "node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/index.js"() {
5447
+ "use strict";
5448
+ init_qr_connect();
4967
5449
  }
4968
- return Array.from(ids);
5450
+ });
5451
+
5452
+ // src/setup/account-key.ts
5453
+ var account_key_exports = {};
5454
+ __export(account_key_exports, {
5455
+ resolveAccountKey: () => resolveAccountKey
5456
+ });
5457
+ function resolveAccountKey(cfg, appId, resolvedId) {
5458
+ if (resolvedId) return resolvedId;
5459
+ for (const id of listQQBotAccountIds(cfg)) {
5460
+ if (resolveQQBotAccount(cfg, id).appId === appId) return id;
5461
+ }
5462
+ if (listQQBotAccountIds(cfg).length === 0) return "default";
5463
+ return appId;
4969
5464
  }
4970
- function resolveDefaultQQBotAccountId(cfg) {
4971
- const qqbot = cfg.channels?.qqbot;
4972
- if (qqbot?.appId) {
4973
- return DEFAULT_ACCOUNT_ID;
5465
+ var init_account_key = __esm({
5466
+ "src/setup/account-key.ts"() {
5467
+ "use strict";
5468
+ init_config();
4974
5469
  }
4975
- if (qqbot?.accounts) {
4976
- const ids = Object.keys(qqbot.accounts);
4977
- if (ids.length > 0) {
4978
- return ids[0];
5470
+ });
5471
+
5472
+ // src/setup/finalize.ts
5473
+ var finalize_exports = {};
5474
+ __export(finalize_exports, {
5475
+ applyAccountDefaults: () => applyAccountDefaults,
5476
+ finalizeQQBotSetup: () => finalizeQQBotSetup
5477
+ });
5478
+ function isConfigured(cfg, accountId) {
5479
+ const account = resolveQQBotAccount(cfg, accountId);
5480
+ return Boolean(account.appId && account.clientSecret);
5481
+ }
5482
+ async function linkViaQrCode(cfg, _accountId, prompter, rt) {
5483
+ try {
5484
+ const accounts = await m2({ source: "openclaw" });
5485
+ if (accounts.length === 0) {
5486
+ await prompter.note("\u672A\u83B7\u53D6\u5230\u4EFB\u4F55 QQ Bot \u8D26\u53F7\u4FE1\u606F\u3002", "QQ Bot");
5487
+ return cfg;
5488
+ }
5489
+ let next = cfg;
5490
+ for (const { appId, appSecret, userOpenid } of accounts) {
5491
+ const key = resolveAccountKey(cfg, appId);
5492
+ next = applyQQBotAccountConfig(next, key, { appId, clientSecret: appSecret });
5493
+ next = applyAccountDefaults(next, key, userOpenid);
5494
+ rt.log(`\u7ED1\u5B9A\u6210\u529F\uFF01\u8D26\u6237: ${key} (AppID: ${appId})`);
4979
5495
  }
5496
+ return next;
5497
+ } catch (err) {
5498
+ rt.error(`QQ Bot \u7ED1\u5B9A\u5931\u8D25: ${String(err)}`);
5499
+ await prompter.note(`\u7ED1\u5B9A\u5931\u8D25\uFF0C\u60A8\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u914D\u7F6E\u3002
5500
+ \u6587\u6863: ${formatDocsLink("/channels/qqbot", "qqbot")}`, "QQ Bot");
5501
+ return cfg;
4980
5502
  }
4981
- return DEFAULT_ACCOUNT_ID;
4982
5503
  }
4983
- function resolveUserAgentSuffix(cfg) {
4984
- const qqbot = cfg.channels?.qqbot;
4985
- return qqbot?.userAgentSuffix ? String(qqbot.userAgentSuffix).trim() : "";
4986
- }
4987
- function resolveProcessingTimeoutMs(accountConfig) {
4988
- if (accountConfig?.processingTimeoutMs !== void 0) {
4989
- return accountConfig.processingTimeoutMs;
4990
- }
4991
- const env = process.env.OPENCLAW_PROCESSING_TIMEOUT_MS;
4992
- if (env) {
4993
- const v = Number(env);
4994
- if (!Number.isNaN(v) && v >= 0) return v;
4995
- }
4996
- return DEFAULT_PROCESSING_TIMEOUT_MS;
5504
+ async function linkViaManual(cfg, _accountId, prompter) {
5505
+ const appIdInput = await prompter.text({ message: "\u8BF7\u8F93\u5165 QQ Bot AppID", validate: (v) => v.trim() ? void 0 : "AppID \u4E0D\u80FD\u4E3A\u7A7A" });
5506
+ const secret = await prompter.text({ message: "\u8BF7\u8F93\u5165 QQ Bot AppSecret", validate: (v) => v.trim() ? void 0 : "AppSecret \u4E0D\u80FD\u4E3A\u7A7A" });
5507
+ const appId = appIdInput.trim();
5508
+ const key = resolveAccountKey(cfg, appId);
5509
+ let next = applyQQBotAccountConfig(cfg, key, { appId, clientSecret: secret.trim() });
5510
+ next = applyAccountDefaults(next, key);
5511
+ await prompter.note("\u2714 QQ Bot \u914D\u7F6E\u5B8C\u6210\uFF01", "QQ Bot");
5512
+ return next;
4997
5513
  }
4998
- function resolveQQBotAccount(cfg, accountId) {
4999
- const resolvedAccountId = accountId ?? resolveDefaultQQBotAccountId(cfg);
5000
- const qqbot = cfg.channels?.qqbot;
5001
- let accountConfig = {};
5002
- let appId = "";
5003
- let clientSecret = "";
5004
- let secretSource = "none";
5005
- if (resolvedAccountId === DEFAULT_ACCOUNT_ID) {
5006
- const { accounts: _accounts, ...topLevelConfig } = qqbot ?? {};
5007
- accountConfig = {
5008
- ...topLevelConfig,
5009
- markdownSupport: qqbot?.markdownSupport ?? true
5010
- };
5011
- appId = normalizeAppId(qqbot?.appId);
5012
- } else {
5013
- const account = qqbot?.accounts?.[resolvedAccountId];
5014
- accountConfig = account ?? {};
5015
- appId = normalizeAppId(account?.appId);
5016
- }
5017
- if (accountConfig.clientSecret) {
5018
- clientSecret = accountConfig.clientSecret;
5019
- secretSource = "config";
5020
- } else if (accountConfig.clientSecretFile) {
5021
- secretSource = "file";
5022
- } else if (process.env.QQBOT_CLIENT_SECRET && resolvedAccountId === DEFAULT_ACCOUNT_ID) {
5023
- clientSecret = process.env.QQBOT_CLIENT_SECRET;
5024
- secretSource = "env";
5025
- }
5026
- if (!appId && process.env.QQBOT_APP_ID && resolvedAccountId === DEFAULT_ACCOUNT_ID) {
5027
- appId = normalizeAppId(process.env.QQBOT_APP_ID);
5514
+ async function finalizeQQBotSetup(params) {
5515
+ const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID2;
5516
+ const configured = isConfigured(params.cfg, accountId);
5517
+ const mode = await params.prompter.select({
5518
+ message: configured ? "QQ \u5DF2\u7ED1\u5B9A\uFF0C\u9009\u62E9\u64CD\u4F5C" : "\u9009\u62E9 QQ \u7ED1\u5B9A\u65B9\u5F0F",
5519
+ options: [
5520
+ { value: "qr", label: "\u626B\u7801\u7ED1\u5B9A\uFF08\u63A8\u8350\uFF09", hint: "\u4F7F\u7528 QQ \u626B\u63CF\u4E8C\u7EF4\u7801\u81EA\u52A8\u5B8C\u6210\u7ED1\u5B9A" },
5521
+ { value: "manual", label: "\u624B\u52A8\u8F93\u5165 QQ Bot AppID \u548C AppSecret", hint: "\u9700\u5230 QQ \u5F00\u653E\u5E73\u53F0 q.qq.com \u67E5\u770B" },
5522
+ { value: "skip", label: configured ? "\u4FDD\u6301\u5F53\u524D\u914D\u7F6E" : "\u7A0D\u540E\u914D\u7F6E" }
5523
+ ]
5524
+ });
5525
+ let next = params.cfg;
5526
+ if (mode === "qr") {
5527
+ next = await linkViaQrCode(next, accountId, params.prompter, params.runtime);
5528
+ } else if (mode === "manual") {
5529
+ next = await linkViaManual(next, accountId, params.prompter);
5530
+ } else if (!configured) {
5531
+ await params.prompter.note("\u60A8\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u91CD\u65B0\u914D\u7F6E\uFF1A\n openclaw channels add", "QQ Bot");
5028
5532
  }
5029
- return {
5030
- accountId: resolvedAccountId,
5031
- name: accountConfig.name,
5032
- enabled: accountConfig.enabled !== false,
5033
- appId,
5034
- clientSecret,
5035
- secretSource,
5036
- systemPrompt: accountConfig.systemPrompt,
5037
- markdownSupport: accountConfig.markdownSupport !== false,
5038
- userAgentSuffix: resolveUserAgentSuffix(cfg),
5039
- processingTimeoutMs: resolveProcessingTimeoutMs(accountConfig),
5040
- config: accountConfig
5041
- };
5533
+ return { cfg: next };
5042
5534
  }
5043
- function applyQQBotAccountConfig(cfg, accountId, input) {
5044
- const next = { ...cfg };
5045
- if (accountId === DEFAULT_ACCOUNT_ID) {
5046
- const existingConfig = next.channels?.qqbot || {};
5047
- const allowFrom = existingConfig.allowFrom ?? ["*"];
5048
- next.channels = {
5049
- ...next.channels,
5050
- qqbot: {
5051
- ...next.channels?.qqbot || {},
5052
- enabled: true,
5053
- allowFrom,
5054
- ...input.appId ? { appId: input.appId } : {},
5055
- ...input.clientSecret ? { clientSecret: input.clientSecret } : input.clientSecretFile ? { clientSecretFile: input.clientSecretFile } : {},
5056
- ...input.name ? { name: input.name } : {}
5057
- }
5058
- };
5535
+ function applyAccountDefaults(cfg, accountId, userOpenid) {
5536
+ const next = { ...cfg, channels: { ...cfg.channels } };
5537
+ const qqbot = { ...next.channels?.qqbot ?? {} };
5538
+ const defaults = { streaming: true, dmPolicy: "allowlist" };
5539
+ if (userOpenid) defaults.allowFrom = [userOpenid];
5540
+ if (accountId === DEFAULT_ACCOUNT_ID2) {
5541
+ Object.assign(qqbot, defaults);
5059
5542
  } else {
5060
- const existingAccountConfig = next.channels?.qqbot?.accounts?.[accountId] || {};
5061
- const allowFrom = existingAccountConfig.allowFrom ?? ["*"];
5062
- next.channels = {
5063
- ...next.channels,
5064
- qqbot: {
5065
- ...next.channels?.qqbot || {},
5066
- enabled: true,
5067
- accounts: {
5068
- ...next.channels?.qqbot?.accounts || {},
5069
- [accountId]: {
5070
- ...next.channels?.qqbot?.accounts?.[accountId] || {},
5071
- enabled: true,
5072
- allowFrom,
5073
- ...input.appId ? { appId: input.appId } : {},
5074
- ...input.clientSecret ? { clientSecret: input.clientSecret } : input.clientSecretFile ? { clientSecretFile: input.clientSecretFile } : {},
5075
- ...input.name ? { name: input.name } : {}
5076
- }
5077
- }
5078
- }
5079
- };
5543
+ const accounts = { ...qqbot.accounts ?? {} };
5544
+ accounts[accountId] = { ...accounts[accountId] ?? {}, ...defaults };
5545
+ qqbot.accounts = accounts;
5080
5546
  }
5547
+ next.channels = { ...next.channels, qqbot };
5081
5548
  return next;
5082
5549
  }
5550
+ var init_finalize = __esm({
5551
+ "src/setup/finalize.ts"() {
5552
+ "use strict";
5553
+ init_setup();
5554
+ init_esm();
5555
+ init_config();
5556
+ init_account_key();
5557
+ }
5558
+ });
5559
+
5560
+ // index.ts
5561
+ var index_exports = {};
5562
+ __export(index_exports, {
5563
+ DEFAULT_ACCOUNT_ID: () => DEFAULT_ACCOUNT_ID,
5564
+ MSG_TYPE_QUOTE: () => MSG_TYPE_QUOTE2,
5565
+ MSG_TYPE_TEXT: () => MSG_TYPE_TEXT,
5566
+ PersistedRefIndexStore: () => PersistedRefIndexStore,
5567
+ QQBotGateway: () => QQBotGateway,
5568
+ StreamContentType: () => StreamContentType2,
5569
+ StreamInputMode: () => StreamInputMode2,
5570
+ StreamInputState: () => StreamInputState2,
5571
+ StreamingController: () => StreamingController,
5572
+ applyQQBotAccountConfig: () => applyQQBotAccountConfig,
5573
+ buildUserAgent: () => buildUserAgent,
5574
+ default: () => index_default,
5575
+ dispatchToOpenClaw: () => dispatchToOpenClaw,
5576
+ flushAllRefIndexStores: () => flushAllRefIndexStores,
5577
+ getBotForAccount: () => getBotForAccount,
5578
+ getPersistedRefIndexStore: () => getPersistedRefIndexStore,
5579
+ getQQBotRuntime: () => getQQBotRuntime,
5580
+ isGroupAllowed: () => isGroupAllowed,
5581
+ listQQBotAccountIds: () => listQQBotAccountIds,
5582
+ parseTarget: () => parseTarget,
5583
+ qqbotOnboardingAdapter: () => qqbotOnboardingAdapter,
5584
+ qqbotPlugin: () => qqbotPlugin,
5585
+ resolveDefaultQQBotAccountId: () => resolveDefaultQQBotAccountId,
5586
+ resolveGroupAllowFrom: () => resolveGroupAllowFrom,
5587
+ resolveGroupConfig: () => resolveGroupConfig,
5588
+ resolveGroupConfigFromAccount: () => resolveGroupConfigFromAccount,
5589
+ resolveGroupName: () => resolveGroupName,
5590
+ resolveGroupPolicy: () => resolveGroupPolicy,
5591
+ resolveGroupPrompt: () => resolveGroupPrompt,
5592
+ resolveHistoryLimit: () => resolveHistoryLimit,
5593
+ resolveIgnoreOtherMentions: () => resolveIgnoreOtherMentions,
5594
+ resolveMentionPatterns: () => resolveMentionPatterns,
5595
+ resolveProcessingTimeoutMs: () => resolveProcessingTimeoutMs,
5596
+ resolveQQBotAccount: () => resolveQQBotAccount,
5597
+ resolveRequireMention: () => resolveRequireMention,
5598
+ resolveToolPolicy: () => resolveToolPolicy,
5599
+ resolveUserAgentSuffix: () => resolveUserAgentSuffix,
5600
+ sendMedia: () => sendMedia,
5601
+ sendText: () => sendText,
5602
+ setQQBotRuntime: () => setQQBotRuntime,
5603
+ shouldUseStreaming: () => shouldUseStreaming,
5604
+ tryGetBotForAccount: () => tryGetBotForAccount
5605
+ });
5606
+ module.exports = __toCommonJS(index_exports);
5607
+ var import_plugin_sdk = require("openclaw/plugin-sdk");
5608
+
5609
+ // src/channel.ts
5610
+ var import_core = require("openclaw/plugin-sdk/core");
5611
+ init_config();
5083
5612
 
5084
5613
  // src/bot-instance.ts
5085
5614
  var import_node_os = __toESM(require("os"), 1);
@@ -5087,11 +5616,11 @@ var import_node_os = __toESM(require("os"), 1);
5087
5616
  // src/outbound/outbound-service.ts
5088
5617
  var path3 = __toESM(require("path"), 1);
5089
5618
 
5090
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/QQBot.js
5619
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/QQBot.js
5091
5620
  var fs3 = __toESM(require("fs"), 1);
5092
5621
  var path = __toESM(require("path"), 1);
5093
5622
 
5094
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/types.js
5623
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/types.js
5095
5624
  function resolvePolicy(ctx, path22, explicit, defaultValue) {
5096
5625
  if (explicit !== void 0 && explicit !== null) {
5097
5626
  return explicit;
@@ -5167,7 +5696,7 @@ function createMiddlewareContext(params) {
5167
5696
  return ctx;
5168
5697
  }
5169
5698
 
5170
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/types.js
5699
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/types.js
5171
5700
  var ApiError = class extends Error {
5172
5701
  httpStatus;
5173
5702
  path;
@@ -5200,7 +5729,7 @@ var StreamContentType = {
5200
5729
  MARKDOWN: "markdown"
5201
5730
  };
5202
5731
 
5203
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/format.js
5732
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/format.js
5204
5733
  function formatErrorMessage(err) {
5205
5734
  if (err instanceof Error) {
5206
5735
  let formatted = err.message || err.name || "Error";
@@ -5247,7 +5776,7 @@ function formatFileSize(bytes) {
5247
5776
  return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
5248
5777
  }
5249
5778
 
5250
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/api-client.js
5779
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/api-client.js
5251
5780
  var DEFAULT_BASE_URL = "https://api.sgroup.qq.com";
5252
5781
  var DEFAULT_TIMEOUT_MS = 3e4;
5253
5782
  var FILE_UPLOAD_TIMEOUT_MS = 12e4;
@@ -5342,12 +5871,12 @@ var ApiClient = class {
5342
5871
  }
5343
5872
  };
5344
5873
 
5345
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media-chunked.js
5874
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media-chunked.js
5346
5875
  var crypto = __toESM(require("crypto"), 1);
5347
5876
  var fs = __toESM(require("fs"), 1);
5348
5877
  var https = __toESM(require("https"), 1);
5349
5878
 
5350
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/retry.js
5879
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/retry.js
5351
5880
  async function withRetry(fn, policy, persistentPolicy, logger) {
5352
5881
  let lastError = null;
5353
5882
  for (let attempt = 0; attempt <= policy.maxRetries; attempt++) {
@@ -5439,7 +5968,7 @@ function buildPartFinishPersistentPolicy(retryTimeoutMs, retryableCodes = PART_F
5439
5968
  var PART_FINISH_RETRYABLE_CODES = /* @__PURE__ */ new Set([40093001]);
5440
5969
  var UPLOAD_PREPARE_FALLBACK_CODE = 40093002;
5441
5970
 
5442
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/routes.js
5971
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/routes.js
5443
5972
  function messagePath(scope, targetId) {
5444
5973
  return scope === "c2c" ? `/v2/users/${targetId}/messages` : `/v2/groups/${targetId}/messages`;
5445
5974
  }
@@ -5476,7 +6005,7 @@ function getNextMsgSeq(_msgId) {
5476
6005
  return (timePart ^ random) % 65536;
5477
6006
  }
5478
6007
 
5479
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media-chunked.js
6008
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media-chunked.js
5480
6009
  var UploadDailyLimitExceededError = class extends Error {
5481
6010
  filePath;
5482
6011
  fileSize;
@@ -5738,8 +6267,32 @@ function sleep2(ms) {
5738
6267
  return new Promise((resolve2) => setTimeout(resolve2, ms));
5739
6268
  }
5740
6269
 
5741
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media.js
6270
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media.js
5742
6271
  var fs2 = __toESM(require("fs"), 1);
6272
+
6273
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/file-utils.js
6274
+ var MAX_UPLOAD_SIZE = 20 * 1024 * 1024;
6275
+ var CHUNKED_UPLOAD_MAX_SIZE = 100 * 1024 * 1024;
6276
+ var LARGE_FILE_THRESHOLD = 5 * 1024 * 1024;
6277
+ var MEDIA_FILE_TYPE_INFO = {
6278
+ [MediaFileType.IMAGE]: { maxSize: 30 * 1024 * 1024, name: "image" },
6279
+ [MediaFileType.VIDEO]: { maxSize: 100 * 1024 * 1024, name: "video" },
6280
+ [MediaFileType.VOICE]: { maxSize: 20 * 1024 * 1024, name: "voice" },
6281
+ [MediaFileType.FILE]: { maxSize: 100 * 1024 * 1024, name: "file" }
6282
+ };
6283
+ function sanitizeFileName(name) {
6284
+ if (!name) {
6285
+ return "file";
6286
+ }
6287
+ const cleaned = name.replace(/[\\/:*?"<>|]/g, "_").replace(/[\u0000-\u001f\u007f]/g, "").replace(/\s+/g, " ").trim();
6288
+ return cleaned || "file";
6289
+ }
6290
+
6291
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/media.js
6292
+ var MAX_BASE64_CHECK_SIZE = Math.ceil(MAX_UPLOAD_SIZE * 1.4);
6293
+ function formatUploadSize() {
6294
+ return formatFileSize(MAX_UPLOAD_SIZE);
6295
+ }
5743
6296
  var MediaApi = class {
5744
6297
  client;
5745
6298
  tokenManager;
@@ -5771,6 +6324,10 @@ var MediaApi = class {
5771
6324
  const buf = await fs2.promises.readFile(opts.localPath);
5772
6325
  fileData = buf.toString("base64");
5773
6326
  }
6327
+ if (fileData && fileData.length > MAX_BASE64_CHECK_SIZE) {
6328
+ const sizeMB = (fileData.length / (1024 * 1024)).toFixed(1);
6329
+ throw new Error(`fileData too large (${sizeMB}MB decoded); QQ Bot single upload limit is ${formatUploadSize()}`);
6330
+ }
5774
6331
  if (fileData && this.cache) {
5775
6332
  const hash = this.cache.computeHash(fileData);
5776
6333
  const cached = this.cache.get(hash, scope, targetId, fileType);
@@ -5819,7 +6376,7 @@ var MediaApi = class {
5819
6376
  }
5820
6377
  };
5821
6378
 
5822
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/messages.js
6379
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/messages.js
5823
6380
  var MessageApi = class {
5824
6381
  client;
5825
6382
  tokenManager;
@@ -6007,7 +6564,7 @@ var MessageApi = class {
6007
6564
  }
6008
6565
  };
6009
6566
 
6010
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/token.js
6567
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/api/token.js
6011
6568
  var DEFAULT_TOKEN_BASE_URL = "https://bots.qq.com";
6012
6569
  var TOKEN_PATH = "/app/getAppAccessToken";
6013
6570
  var TokenManager = class {
@@ -6194,7 +6751,7 @@ var import_websocket = __toESM(require_websocket(), 1);
6194
6751
  var import_websocket_server = __toESM(require_websocket_server(), 1);
6195
6752
  var wrapper_default = import_websocket.default;
6196
6753
 
6197
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/codec.js
6754
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/codec.js
6198
6755
  function decodeGatewayMessageData(data) {
6199
6756
  if (typeof data === "string") {
6200
6757
  return data;
@@ -6221,7 +6778,7 @@ function readOptionalMessageSceneExt(event) {
6221
6778
  return scene?.ext;
6222
6779
  }
6223
6780
 
6224
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/constants.js
6781
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/constants.js
6225
6782
  var INTENTS = {
6226
6783
  GUILDS: 1 << 0,
6227
6784
  GUILD_MEMBERS: 1 << 1,
@@ -6294,7 +6851,7 @@ var GatewayEvent = {
6294
6851
  MESSAGE_REACTION_REMOVE: "MESSAGE_REACTION_REMOVE"
6295
6852
  };
6296
6853
 
6297
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/event-dispatcher.js
6854
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/event-dispatcher.js
6298
6855
  var REF_INDEX_KEY = "msg_idx";
6299
6856
  function parseRefIndices(ext, msgType, msgElements) {
6300
6857
  let refMsgIdx;
@@ -6435,7 +6992,7 @@ function dispatchEvent(eventType, data, _accountId, _log) {
6435
6992
  return { action: "raw", type: eventType, data };
6436
6993
  }
6437
6994
 
6438
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/reconnect.js
6995
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/reconnect.js
6439
6996
  var ReconnectState = class {
6440
6997
  accountId;
6441
6998
  log;
@@ -6546,7 +7103,7 @@ var ReconnectState = class {
6546
7103
  }
6547
7104
  };
6548
7105
 
6549
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/gateway-connection.js
7106
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/gateway/gateway-connection.js
6550
7107
  var GatewayConnection = class {
6551
7108
  isAborted = false;
6552
7109
  currentWs = null;
@@ -6804,7 +7361,7 @@ function previewPayload(data) {
6804
7361
  }
6805
7362
  }
6806
7363
 
6807
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook-verify.js
7364
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook-verify.js
6808
7365
  var crypto2 = __toESM(require("crypto"), 1);
6809
7366
  function deriveSeed(botSecret) {
6810
7367
  let seed = botSecret;
@@ -6856,7 +7413,7 @@ function signValidationResponse(params) {
6856
7413
  };
6857
7414
  }
6858
7415
 
6859
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook-server-node.js
7416
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook-server-node.js
6860
7417
  var http = __toESM(require("http"), 1);
6861
7418
  var NodeHttpWebhookServer = class {
6862
7419
  server = null;
@@ -6904,7 +7461,7 @@ var NodeHttpWebhookServer = class {
6904
7461
  }
6905
7462
  };
6906
7463
 
6907
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook.js
7464
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/transport/webhook.js
6908
7465
  var OP_DISPATCH = 0;
6909
7466
  var OP_HTTP_CALLBACK_ACK = 12;
6910
7467
  var OP_VALIDATION = 13;
@@ -7048,25 +7605,7 @@ function getHeader(headers, key) {
7048
7605
  return val;
7049
7606
  }
7050
7607
 
7051
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/file-utils.js
7052
- var MAX_UPLOAD_SIZE = 20 * 1024 * 1024;
7053
- var CHUNKED_UPLOAD_MAX_SIZE = 100 * 1024 * 1024;
7054
- var LARGE_FILE_THRESHOLD = 5 * 1024 * 1024;
7055
- var MEDIA_FILE_TYPE_INFO = {
7056
- [MediaFileType.IMAGE]: { maxSize: 30 * 1024 * 1024, name: "image" },
7057
- [MediaFileType.VIDEO]: { maxSize: 100 * 1024 * 1024, name: "video" },
7058
- [MediaFileType.VOICE]: { maxSize: 20 * 1024 * 1024, name: "voice" },
7059
- [MediaFileType.FILE]: { maxSize: 100 * 1024 * 1024, name: "file" }
7060
- };
7061
- function sanitizeFileName(name) {
7062
- if (!name) {
7063
- return "file";
7064
- }
7065
- const cleaned = name.replace(/[\\/:*?"<>|]/g, "_").replace(/[\u0000-\u001f\u007f]/g, "").replace(/\s+/g, " ").trim();
7066
- return cleaned || "file";
7067
- }
7068
-
7069
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/upload-cache.js
7608
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/upload-cache.js
7070
7609
  var crypto3 = __toESM(require("crypto"), 1);
7071
7610
  var MAX_CACHE_SIZE = 500;
7072
7611
  function computeFileHash(data) {
@@ -7131,7 +7670,7 @@ var UploadCache = class {
7131
7670
  }
7132
7671
  };
7133
7672
 
7134
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/streaming.js
7673
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/streaming.js
7135
7674
  var DEFAULT_THROTTLE_MS = 500;
7136
7675
  var MIN_THROTTLE_MS = 300;
7137
7676
  var MAX_FLUSH_RETRIES = 3;
@@ -7299,7 +7838,7 @@ var StreamSession = class {
7299
7838
  }
7300
7839
  };
7301
7840
 
7302
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/QQBot.js
7841
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/QQBot.js
7303
7842
  var MsgType = {
7304
7843
  /** Plain text. */
7305
7844
  TEXT: 0,
@@ -7987,7 +8526,7 @@ var QQBot = class {
7987
8526
  }
7988
8527
  };
7989
8528
 
7990
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/message-filter.js
8529
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/message-filter.js
7991
8530
  function messageFilter(options = {}) {
7992
8531
  const skipSelfEcho = options.skipSelfEcho ?? true;
7993
8532
  const dedupOpts = options.dedup !== false ? { windowMs: 5e3, maxSize: 1e3, ...options.dedup ?? {} } : null;
@@ -8025,7 +8564,7 @@ function messageFilter(options = {}) {
8025
8564
  };
8026
8565
  }
8027
8566
 
8028
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/content-sanitizer.js
8567
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/content-sanitizer.js
8029
8568
  function contentSanitizer(options = {}) {
8030
8569
  const { stripBotMention = true, stripAllMentions = false, collapseWhitespace = false, parseFaceTags: parseFaceTags2 = false, transform } = options;
8031
8570
  return async (ctx, next) => {
@@ -8120,7 +8659,7 @@ function faceToEmoji(id) {
8120
8659
  return map[id];
8121
8660
  }
8122
8661
 
8123
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/rate-limiter.js
8662
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/rate-limiter.js
8124
8663
  var SlidingWindow = class {
8125
8664
  buckets = /* @__PURE__ */ new Map();
8126
8665
  max;
@@ -8174,7 +8713,7 @@ function rateLimiter(options = {}) {
8174
8713
  };
8175
8714
  }
8176
8715
 
8177
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/concurrency-guard.js
8716
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/concurrency-guard.js
8178
8717
  function concurrencyGuard(options = {}) {
8179
8718
  const strategy = options.strategy ?? "queue";
8180
8719
  const maxQueue = options.maxQueue ?? 3;
@@ -8409,7 +8948,7 @@ function concurrencyGuard(options = {}) {
8409
8948
  return guard;
8410
8949
  }
8411
8950
 
8412
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/mention-gate.js
8951
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/mention-gate.js
8413
8952
  function detectMentionInContent(content, appId) {
8414
8953
  if (!content || !appId)
8415
8954
  return false;
@@ -8465,7 +9004,7 @@ function mentionGate(options = {}) {
8465
9004
  };
8466
9005
  }
8467
9006
 
8468
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/quote-ref.js
9007
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/quote-ref.js
8469
9008
  var MemoryRefIndexStore = class {
8470
9009
  map = /* @__PURE__ */ new Map();
8471
9010
  maxSize;
@@ -8583,7 +9122,7 @@ function buildText(content, attachments) {
8583
9122
  return parts.join("\n") || "[empty message]";
8584
9123
  }
8585
9124
 
8586
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/envelope-formatter.js
9125
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/envelope-formatter.js
8587
9126
  function envelopeFormatter(options = {}) {
8588
9127
  const { historyLimit = 5, includeQuote = true, includeSender = true, format } = options;
8589
9128
  return async (ctx, next) => {
@@ -8655,7 +9194,7 @@ ${parts.join("\n")}
8655
9194
  return sections.join("\n\n");
8656
9195
  }
8657
9196
 
8658
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/slash-command.js
9197
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/slash-command.js
8659
9198
  function slashCommand(options = {}) {
8660
9199
  const prefixes = options.prefixes ?? ["/"];
8661
9200
  const catchErrors = options.catchErrors ?? true;
@@ -8718,8 +9257,9 @@ function slashCommand(options = {}) {
8718
9257
  }
8719
9258
  const cleaned = content.replace(/<@!?[^>]+>\s*/g, "").trim();
8720
9259
  if (ctx.message.kind === "group") {
8721
- const mentions = ctx.message.mentions;
8722
- if (!mentions?.some((m3) => m3.is_you)) {
9260
+ const msg = ctx.message;
9261
+ const wasMentioned = msg.rawEventType === "GROUP_AT_MESSAGE_CREATE" || msg.mentions?.some((m3) => m3.is_you);
9262
+ if (!wasMentioned) {
8723
9263
  await next();
8724
9264
  return;
8725
9265
  }
@@ -8807,7 +9347,7 @@ async function sendCommandResult(ctx, result) {
8807
9347
  }
8808
9348
  }
8809
9349
 
8810
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/history-buffer.js
9350
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/history-buffer.js
8811
9351
  var MemoryHistoryStore = class {
8812
9352
  buffers = /* @__PURE__ */ new Map();
8813
9353
  append(groupKey, entry, limit) {
@@ -8872,7 +9412,7 @@ function historyBuffer(options = {}) {
8872
9412
  return m3;
8873
9413
  }
8874
9414
 
8875
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/typing-indicator.js
9415
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/typing-indicator.js
8876
9416
  var DEFAULT_DURATION_SEC = 60;
8877
9417
  var DEFAULT_KEEPALIVE_INTERVAL_MS = 5e4;
8878
9418
  function typingIndicator(options = {}) {
@@ -8909,7 +9449,7 @@ function typingIndicator(options = {}) {
8909
9449
  };
8910
9450
  }
8911
9451
 
8912
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/error-handler.js
9452
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/middleware/error-handler.js
8913
9453
  var DEFAULT_FORMAT = (err) => {
8914
9454
  if (err instanceof ApiError) {
8915
9455
  if (err.bizMessage)
@@ -8947,7 +9487,7 @@ function errorHandler(options = {}) {
8947
9487
  };
8948
9488
  }
8949
9489
 
8950
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/storage/kv-store.js
9490
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/storage/kv-store.js
8951
9491
  var import_node_fs = __toESM(require("fs"), 1);
8952
9492
  var import_node_path = __toESM(require("path"), 1);
8953
9493
  var FileKVStore = class {
@@ -9057,7 +9597,7 @@ var FileKVStore = class {
9057
9597
  }
9058
9598
  };
9059
9599
 
9060
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/storage/session-adapter.js
9600
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/storage/session-adapter.js
9061
9601
  function kvSessionPersistence(opts) {
9062
9602
  const prefix = opts.prefix ?? "qqbot:session:";
9063
9603
  const key = `${prefix}${opts.accountId}`;
@@ -9284,7 +9824,7 @@ var import_node_path2 = __toESM(require("path"), 1);
9284
9824
  var import_node_fs2 = __toESM(require("fs"), 1);
9285
9825
  var _cachedOpenclawVersion;
9286
9826
  function getPackageVersion() {
9287
- return true ? "2.0.0-dev.202607101321" : "unknown";
9827
+ return true ? "2.0.0-dev.202607101939" : "unknown";
9288
9828
  }
9289
9829
  function getOpenclawVersion(runtimeVersion) {
9290
9830
  if (_cachedOpenclawVersion) return _cachedOpenclawVersion;
@@ -9615,856 +10155,485 @@ var PersistedRefIndexStore = class {
9615
10155
  import_node_fs3.default.renameSync(tmpPath, this.filePath);
9616
10156
  this.diskLineCount = lines.length;
9617
10157
  } catch (err) {
9618
- log.error(
9619
- `compactSync failed: ${err instanceof Error ? err.message : String(err)}`
9620
- );
9621
- }
9622
- }
9623
- // ── 诊断 ──
9624
- /** 当前内存中的条目数 */
9625
- get size() {
9626
- return this.memory.size;
9627
- }
9628
- /** 是否已完成初始化(磁盘回放) */
9629
- get isInitialized() {
9630
- return this.initialized;
9631
- }
9632
- /** 诊断快照 */
9633
- stats() {
9634
- return {
9635
- memoryEntries: this.memory.size,
9636
- diskLines: this.diskLineCount,
9637
- maxEntries: this.maxEntries,
9638
- filePath: this.filePath
9639
- };
9640
- }
9641
- /**
9642
- * 强制将当前内存状态持久化到磁盘(进程退出前调用)
9643
- */
9644
- flush() {
9645
- this.compactSync();
9646
- }
9647
- };
9648
- var stores = /* @__PURE__ */ new Map();
9649
- function getPersistedRefIndexStore(accountId) {
9650
- let store = stores.get(accountId);
9651
- if (!store) {
9652
- const filePath = import_node_path3.default.join(getQQBotDataDir("data", accountId), DEFAULT_FILENAME);
9653
- store = new PersistedRefIndexStore({ filePath });
9654
- stores.set(accountId, store);
9655
- }
9656
- return store;
9657
- }
9658
- function flushAllRefIndexStores() {
9659
- for (const store of stores.values()) {
9660
- store.flush();
9661
- }
9662
- }
9663
-
9664
- // src/runtime.ts
9665
- var runtime = null;
9666
- var exitHooksInstalled = false;
9667
- function setQQBotRuntime(next) {
9668
- runtime = next;
9669
- const version = getOpenclawVersion(next.version);
9670
- setOpenClawVersion(version);
9671
- installExitHooksOnce();
9672
- }
9673
- function installExitHooksOnce() {
9674
- if (exitHooksInstalled) return;
9675
- exitHooksInstalled = true;
9676
- const flush = () => {
9677
- try {
9678
- flushAllRefIndexStores();
9679
- } catch {
9680
- }
9681
- };
9682
- process.on("beforeExit", flush);
9683
- process.on("SIGINT", () => {
9684
- flush();
9685
- process.exit(0);
9686
- });
9687
- process.on("SIGTERM", () => {
9688
- flush();
9689
- process.exit(0);
9690
- });
9691
- }
9692
- function getQQBotRuntime() {
9693
- if (!runtime) throw new Error("QQBot runtime not initialized");
9694
- return runtime;
9695
- }
9696
- function tryGetQQBotRuntime() {
9697
- return runtime;
9698
- }
9699
-
9700
- // src/adapter/resolve.ts
9701
- function probeFunction(rt, paths) {
9702
- for (const path22 of paths) {
9703
- let target = rt;
9704
- let parent = rt;
9705
- for (let i = 0; i < path22.length; i++) {
9706
- parent = target;
9707
- target = target?.[path22[i]];
9708
- if (target === void 0 || target === null) break;
9709
- }
9710
- if (typeof target === "function") {
9711
- return target.bind(parent);
9712
- }
9713
- }
9714
- return null;
9715
- }
9716
- function resolveRuntimeAdapters(rt, log4) {
9717
- const version = rt.version ?? "unknown";
9718
- const inboundRun = probeFunction(rt, [
9719
- ["channel", "inbound", "run"],
9720
- // current (2026-05+)
9721
- ["channel", "turn", "run"]
9722
- // legacy (removed 2026-05-27)
9723
- ]);
9724
- const dispatchReply = probeFunction(rt, [
9725
- ["channel", "reply", "dispatchReplyWithBufferedBlockDispatcher"]
9726
- ]);
9727
- const resolveAgentRoute = probeFunction(rt, [
9728
- ["channel", "routing", "resolveAgentRoute"]
9729
- ]);
9730
- const rawBuildContext = probeFunction(rt, [
9731
- ["channel", "inbound", "buildContext"]
9732
- ]);
9733
- const rawFinalizeContext = !rawBuildContext ? probeFunction(rt, [["channel", "reply", "finalizeInboundContext"]]) : null;
9734
- const buildInboundContext = rawBuildContext ? (params) => rawBuildContext(params) : rawFinalizeContext ? (params) => {
9735
- const isCommand = params.access?.commands?.authorized ?? false;
9736
- const rawCtx = {
9737
- Body: params.message.body,
9738
- BodyForAgent: params.message.bodyForAgent,
9739
- RawBody: params.message.rawBody,
9740
- CommandBody: params.message.commandBody ?? params.message.rawBody,
9741
- CommandSource: isCommand ? "text" : void 0,
9742
- CommandTurn: params.command ?? void 0,
9743
- CommandAuthorized: isCommand,
9744
- From: params.from,
9745
- To: params.reply.to,
9746
- SessionKey: params.route.routeSessionKey,
9747
- AccountId: params.route.accountId ?? params.accountId,
9748
- ChatType: params.conversation.kind,
9749
- GroupSystemPrompt: params.conversation.label,
9750
- SenderId: params.sender.id,
9751
- SenderName: params.sender.name,
9752
- Provider: params.provider ?? params.channel,
9753
- Surface: params.surface ?? params.channel,
9754
- MessageSid: params.messageId,
9755
- Timestamp: params.timestamp ?? Date.now(),
9756
- OriginatingChannel: params.channel,
9757
- OriginatingTo: params.reply.originatingTo ?? params.reply.to,
9758
- ...params.extra
9759
- };
9760
- return rawFinalizeContext(rawCtx);
9761
- } : null;
9762
- const resolveStorePath = probeFunction(rt, [
9763
- ["channel", "session", "resolveStorePath"]
9764
- ]);
9765
- const recordInboundSession = probeFunction(rt, [
9766
- ["channel", "session", "recordInboundSession"]
9767
- ]);
9768
- const formatEnvelope = probeFunction(rt, [
9769
- ["channel", "reply", "formatAgentEnvelope"],
9770
- // current (2026-06+)
9771
- ["channel", "reply", "formatInboundEnvelope"]
9772
- // deprecated,低版本兼容
9773
- ]);
9774
- const resolveEnvelopeFormatOptions = probeFunction(rt, [
9775
- ["channel", "reply", "resolveEnvelopeFormatOptions"]
9776
- ]);
9777
- const chunkMarkdownText = probeFunction(rt, [
9778
- ["channel", "text", "chunkMarkdownText"]
9779
- ]);
9780
- const saveRemoteMedia = probeFunction(rt, [
9781
- ["channel", "media", "saveRemoteMedia"]
9782
- ]);
9783
- const getConfig = probeFunction(rt, [
9784
- ["config", "current"]
9785
- ]) ?? probeFunction(rt, [
9786
- ["getConfig"]
9787
- ]) ?? probeFunction(rt, [
9788
- ["config", "loadConfig"]
9789
- ]);
9790
- const rawMutateConfig = probeFunction(rt, [["config", "mutateConfigFile"]]);
9791
- const rawWriteConfig = probeFunction(rt, [["config", "writeConfigFile"]]);
9792
- const persistConfig = rawMutateConfig ? async (mutator) => {
9793
- await rawMutateConfig({
9794
- afterWrite: "hot-reload",
9795
- mutate: mutator
9796
- });
9797
- } : rawWriteConfig && getConfig ? async (mutator) => {
9798
- const current = JSON.parse(JSON.stringify(getConfig()));
9799
- mutator(current);
9800
- await rawWriteConfig(current);
9801
- } : null;
9802
- const resolved = [
9803
- inboundRun && "inboundRun",
9804
- dispatchReply && "dispatchReply",
9805
- resolveAgentRoute && "resolveAgentRoute",
9806
- buildInboundContext && "buildInboundContext",
9807
- resolveStorePath && "resolveStorePath",
9808
- recordInboundSession && "recordInboundSession",
9809
- formatEnvelope && "formatEnvelope",
9810
- chunkMarkdownText && "chunkMarkdownText",
9811
- saveRemoteMedia && "saveRemoteMedia",
9812
- getConfig && "getConfig",
9813
- persistConfig && `persistConfig(${rawMutateConfig ? "mutate" : "write"})`
9814
- ].filter(Boolean);
9815
- log4?.info(
9816
- `[qqbot:adapter] openclaw=${version} resolved ${resolved.length} adapters: ${resolved.join(", ")}`
9817
- );
9818
- return {
9819
- inboundRun,
9820
- dispatchReply,
9821
- resolveAgentRoute,
9822
- buildInboundContext,
9823
- resolveStorePath,
9824
- recordInboundSession,
9825
- formatEnvelope,
9826
- resolveEnvelopeFormatOptions,
9827
- chunkMarkdownText,
9828
- saveRemoteMedia,
9829
- getConfig,
9830
- persistConfig,
9831
- version
9832
- };
9833
- }
9834
- var _cachedAdapters = null;
9835
- var _cachedRuntimeRef = null;
9836
- function getAdapters(rt, log4) {
9837
- const cached = _cachedRuntimeRef?.deref();
9838
- if (cached === rt && _cachedAdapters) {
9839
- return _cachedAdapters;
9840
- }
9841
- _cachedAdapters = resolveRuntimeAdapters(rt, log4);
9842
- _cachedRuntimeRef = new WeakRef(rt);
9843
- return _cachedAdapters;
9844
- }
9845
-
9846
- // src/outbound/media-send.ts
9847
- var path8 = __toESM(require("path"), 1);
9848
- var fs9 = __toESM(require("fs"), 1);
9849
- var os4 = __toESM(require("os"), 1);
9850
-
9851
- // src/adapter/workspace.ts
9852
- var import_node_module = require("module");
9853
- var req = (0, import_node_module.createRequire)(__filename);
9854
- var health = null;
9855
- function resolveAgentWorkspace(cfg, agentId) {
9856
- if (!health) {
9857
- try {
9858
- health = req("openclaw/plugin-sdk/health");
9859
- } catch {
9860
- health = null;
9861
- }
9862
- }
9863
- if (health) {
9864
- return health.resolveAgentWorkspaceDir(cfg, agentId ?? health.resolveDefaultAgentId(cfg));
9865
- }
9866
- return getQQBotMediaDir();
9867
- }
9868
-
9869
- // src/utils/ssrf-guard.ts
9870
- var import_node_net = __toESM(require("net"), 1);
9871
- var import_promises = __toESM(require("dns/promises"), 1);
9872
- var RESERVED_V4_PREFIXES = [
9873
- "127.",
9874
- // loopback
9875
- "10.",
9876
- // class-A private
9877
- "192.168.",
9878
- // class-C private
9879
- "169.254."
9880
- // link-local / cloud metadata
9881
- ];
9882
- var PRIVATE_172_RE = /^172\.(1[6-9]|2\d|3[01])\./;
9883
- function isReservedAddr(ip) {
9884
- if (ip === "0.0.0.0") return true;
9885
- for (const pfx of RESERVED_V4_PREFIXES) {
9886
- if (ip.startsWith(pfx)) return true;
9887
- }
9888
- if (PRIVATE_172_RE.test(ip)) return true;
9889
- const lower = ip.toLowerCase();
9890
- if (lower === "::1" || lower === "::") return true;
9891
- if (lower.startsWith("fe80:")) return true;
9892
- if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
9893
- return false;
9894
- }
9895
- var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:"]);
9896
- var QQ_TRUSTED_DOMAINS = /* @__PURE__ */ new Set([
9897
- // QQ Bot API
9898
- "api.sgroup.qq.com",
9899
- "sandbox.api.sgroup.qq.com",
9900
- // QQ Bot Token
9901
- "bots.qq.com",
9902
- // QQ 多媒体上传/下载
9903
- "multimedia.nt.qq.com.cn",
9904
- "multimedia.nt.qq.com",
9905
- // QQ 群文件
9906
- "grouppro.grouppro.qq.com"
9907
- ]);
9908
- function isQQTrustedDomain(hostname) {
9909
- if (QQ_TRUSTED_DOMAINS.has(hostname)) return true;
9910
- const dot = hostname.indexOf(".");
9911
- if (dot > 0) {
9912
- const parent = hostname.slice(dot + 1);
9913
- return QQ_TRUSTED_DOMAINS.has(parent);
9914
- }
9915
- return false;
9916
- }
9917
- async function validateRemoteUrl(raw) {
9918
- const url = new URL(raw);
9919
- if (!ALLOWED_SCHEMES.has(url.protocol)) {
9920
- throw new Error(
9921
- `\u4E0D\u652F\u6301\u7684\u534F\u8BAE "${url.protocol}"\uFF0C\u4EC5\u5141\u8BB8 http/https\uFF08URL: ${raw}\uFF09`
9922
- );
9923
- }
9924
- const host = url.hostname.replace(/^\[|\]$/g, "");
9925
- if (isQQTrustedDomain(host)) return;
9926
- if (import_node_net.default.isIP(host)) {
9927
- assertPublicAddr(host, raw);
9928
- return;
9929
- }
9930
- try {
9931
- const ips = await import_promises.default.resolve(host);
9932
- for (const ip of ips) {
9933
- assertPublicAddr(ip, raw, host);
9934
- }
9935
- } catch (err) {
9936
- if (err instanceof Error && err.message.includes("\u5185\u7F51")) throw err;
9937
- console.warn(`[url-check] DNS \u89E3\u6790 "${host}" \u5931\u8D25: ${err}`);
9938
- }
9939
- }
9940
- function assertPublicAddr(ip, originalUrl, domain) {
9941
- if (!isReservedAddr(ip)) return;
9942
- const target = domain ? `\u57DF\u540D "${domain}" \u89E3\u6790\u5230\u5185\u7F51\u5730\u5740 "${ip}"` : `\u5185\u7F51\u5730\u5740 "${ip}"`;
9943
- throw new Error(
9944
- `\u7981\u6B62\u8BBF\u95EE${target}\uFF0C\u5DF2\u62E6\u622A\u6F5C\u5728\u7684 SSRF \u8BF7\u6C42\uFF08URL: ${originalUrl}\uFF09`
9945
- );
9946
- }
9947
-
9948
- // src/outbound/local-file-router.ts
9949
- var path7 = __toESM(require("path"), 1);
9950
- var fs8 = __toESM(require("fs"), 1);
9951
- var os3 = __toESM(require("os"), 1);
9952
- function normalizePath(p2) {
9953
- let result = p2;
9954
- if (result.startsWith("file://")) {
9955
- result = result.slice("file://".length);
9956
- if (/^\/[a-zA-Z]:[\\/]/.test(result)) {
9957
- result = result.slice(1);
9958
- }
9959
- }
9960
- try {
9961
- result = decodeURIComponent(result);
9962
- } catch {
9963
- }
9964
- if (result === "~" || result.startsWith("~/") || result.startsWith("~\\")) {
9965
- result = result.replace(/^~/, os3.homedir());
9966
- }
9967
- return result;
9968
- }
9969
- function isLocalFilePath(source) {
9970
- if (!source) return false;
9971
- if (source.startsWith("http://") || source.startsWith("https://")) return false;
9972
- if (source.startsWith("data:")) return false;
9973
- if (source.startsWith("file://")) return true;
9974
- if (source === "~" || source.startsWith("~/") || source.startsWith("~\\")) return true;
9975
- if (source.startsWith("/")) return true;
9976
- if (/^[a-zA-Z]:[\\/]/.test(source)) return true;
9977
- if (source.startsWith("\\\\")) return true;
9978
- if (source.startsWith("./") || source.startsWith("../")) return true;
9979
- if (source.startsWith(".\\") || source.startsWith("..\\")) return true;
9980
- return false;
9981
- }
9982
- function isDataUrl(source) {
9983
- return source.startsWith("data:");
9984
- }
9985
- var IMAGE_EXTS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".ico"]);
9986
- var VOICE_EXTS = /* @__PURE__ */ new Set([".wav", ".mp3", ".silk", ".amr", ".ogg", ".flac", ".aac", ".m4a"]);
9987
- var VIDEO_EXTS = /* @__PURE__ */ new Set([".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv"]);
9988
- function inferMediaKind(filePath) {
9989
- const ext = path7.extname(filePath).toLowerCase();
9990
- if (IMAGE_EXTS.has(ext)) return "image";
9991
- if (VOICE_EXTS.has(ext)) return "voice";
9992
- if (VIDEO_EXTS.has(ext)) return "video";
9993
- return "file";
9994
- }
9995
- function inferMediaKindFromMime(mime) {
9996
- const lower = mime.toLowerCase();
9997
- if (lower.startsWith("image/")) return "image";
9998
- if (lower.startsWith("audio/") || lower === "voice") return "voice";
9999
- if (lower.startsWith("video/")) return "video";
10000
- return "file";
10001
- }
10002
- function isPathInAllowedRoots(absPath, allowedRoots) {
10003
- if (!allowedRoots.length) return false;
10004
- try {
10005
- const real = fs8.realpathSync(absPath);
10006
- return allowedRoots.some((root) => {
10007
- try {
10008
- const rootReal = fs8.existsSync(root) ? fs8.realpathSync(root) : root;
10009
- return real.startsWith(rootReal + path7.sep) || real === rootReal;
10010
- } catch {
10011
- return false;
10012
- }
10013
- });
10014
- } catch {
10015
- return false;
10016
- }
10017
- }
10018
-
10019
- // src/outbound/media-send.ts
10020
- function resolveTempRoots() {
10021
- const roots = /* @__PURE__ */ new Set();
10022
- try {
10023
- const tmp = os4.tmpdir();
10024
- roots.add(fs9.existsSync(tmp) ? fs9.realpathSync(tmp) : tmp);
10025
- } catch {
10026
- }
10027
- if (process.platform !== "win32") {
10028
- try {
10029
- roots.add(fs9.realpathSync("/tmp"));
10030
- } catch {
10031
- }
10032
- }
10033
- return [...roots];
10034
- }
10035
- var ALLOWED_MEDIA_ROOTS = [
10036
- path8.join(os4.homedir(), ".openclaw", "media"),
10037
- path8.join(os4.homedir(), ".openclaw", "workspace"),
10038
- // 框架 outbound 目录(saveMediaBuffer 写入)
10039
- path8.join(os4.homedir(), ".openclaw", "outbound"),
10040
- // TTS 语音临时目录(deliver-pipeline 已通过 isTtsPathSafe 预检)
10041
- ...resolveTempRoots()
10042
- ];
10043
- var MAX_DATA_URL_BYTES = 10 * 1024 * 1024;
10044
- async function sendMedia2(params) {
10045
- const { source, accountId, log: log4 } = params;
10046
- const mlog = log4?.child("media");
10047
- if (!source) {
10048
- mlog?.error("source is empty");
10049
- return { error: "sendMedia: source is required" };
10050
- }
10051
- const wsDir = resolveWorkspaceFromAgent(params.agentId);
10052
- mlog?.debug(`resolveMediaPath source=${source} agentId=${params.agentId ?? "none"} workspaceDir=${wsDir ?? "none"}`);
10053
- const resolved = await resolveMediaPath(source, mlog, wsDir);
10054
- if (!resolved.ok) {
10055
- mlog?.error(`resolveMediaPath failed: ${resolved.error}`);
10056
- return { error: resolved.error };
10057
- }
10058
- const kind = params.mediaKind ?? (params.mimeType ? inferMediaKindFromMime(params.mimeType) : void 0) ?? inferMediaKind(resolved.path);
10059
- const gw = getGateway(accountId);
10060
- if (!gw) {
10061
- return { error: `Bot "${accountId}" not running` };
10062
- }
10063
- const target = parseTarget(params.to);
10064
- switch (kind) {
10065
- case "voice":
10066
- return sendVoiceMedia(gw, target, resolved.path, params);
10067
- case "video":
10068
- return sendVideoMedia(gw, target, resolved.path, params);
10069
- case "file":
10070
- return sendFileMedia(gw, target, resolved.path, params);
10071
- case "image":
10072
- default:
10073
- return sendImageMedia(gw, target, resolved.path, params);
10074
- }
10075
- }
10076
- async function resolveMediaPath(source, log4, workspaceDir) {
10077
- const normalized = normalizePath(source);
10078
- if (isDataUrl(normalized)) {
10079
- if (normalized.length > MAX_DATA_URL_BYTES) {
10080
- const sizeMB = (normalized.length / (1024 * 1024)).toFixed(1);
10081
- return { ok: false, error: `Data URL \u8FC7\u5927\uFF08${sizeMB}MB\uFF0C\u6700\u5927 10MB\uFF09` };
10082
- }
10083
- return { ok: true, path: normalized, isLocal: false };
10084
- }
10085
- if (normalized.startsWith("http://") || normalized.startsWith("https://")) {
10086
- try {
10087
- await validateRemoteUrl(normalized);
10088
- } catch (err) {
10089
- log4?.warn(`SSRF blocked for media URL: ${normalized}`);
10090
- return { ok: false, error: `\u5A92\u4F53 URL \u88AB SSRF \u9632\u62A4\u62E6\u622A: ${err instanceof Error ? err.message : String(err)}` };
10158
+ log.error(
10159
+ `compactSync failed: ${err instanceof Error ? err.message : String(err)}`
10160
+ );
10091
10161
  }
10092
- return { ok: true, path: normalized, isLocal: false };
10093
10162
  }
10094
- if (!isLocalFilePath(normalized)) {
10095
- const resolved2 = resolveWorkingFile(normalized, workspaceDir);
10096
- if (resolved2) {
10097
- return { ok: true, path: resolved2, isLocal: true };
10098
- }
10099
- return { ok: true, path: normalized, isLocal: false };
10163
+ // ── 诊断 ──
10164
+ /** 当前内存中的条目数 */
10165
+ get size() {
10166
+ return this.memory.size;
10100
10167
  }
10101
- const resolved = path8.resolve(normalized);
10102
- if (!fs9.existsSync(resolved)) {
10103
- return { ok: false, error: `File not found: ${resolved}` };
10168
+ /** 是否已完成初始化(磁盘回放) */
10169
+ get isInitialized() {
10170
+ return this.initialized;
10104
10171
  }
10105
- let real;
10106
- try {
10107
- real = fs9.realpathSync(resolved);
10108
- } catch {
10109
- return { ok: false, error: `Cannot resolve path: ${resolved}` };
10172
+ /** 诊断快照 */
10173
+ stats() {
10174
+ return {
10175
+ memoryEntries: this.memory.size,
10176
+ diskLines: this.diskLineCount,
10177
+ maxEntries: this.maxEntries,
10178
+ filePath: this.filePath
10179
+ };
10110
10180
  }
10111
- const allowed = isPathInAllowedRoots(real, ALLOWED_MEDIA_ROOTS);
10112
- if (!allowed) {
10113
- log4?.warn(`path blocked \u2014 not in allowed directory: ${real}`);
10114
- return { ok: false, error: `\u6587\u4EF6\u8DEF\u5F84\u4E0D\u5728\u5141\u8BB8\u7684\u76EE\u5F55\u4E2D` };
10181
+ /**
10182
+ * 强制将当前内存状态持久化到磁盘(进程退出前调用)
10183
+ */
10184
+ flush() {
10185
+ this.compactSync();
10115
10186
  }
10116
- return { ok: true, path: real, isLocal: true };
10117
- }
10118
- function resolveWorkspaceFromAgent(agentId) {
10119
- const cfg = resolveConfigViaAdapter();
10120
- if (!cfg) return void 0;
10121
- return resolveAgentWorkspace(cfg, agentId);
10122
- }
10123
- function resolveConfigViaAdapter() {
10124
- try {
10125
- const rt = tryGetQQBotRuntime();
10126
- if (!rt) return void 0;
10127
- return getAdapters(rt).getConfig?.();
10128
- } catch {
10129
- return void 0;
10187
+ };
10188
+ var stores = /* @__PURE__ */ new Map();
10189
+ function getPersistedRefIndexStore(accountId) {
10190
+ let store = stores.get(accountId);
10191
+ if (!store) {
10192
+ const filePath = import_node_path3.default.join(getQQBotDataDir("data", accountId), DEFAULT_FILENAME);
10193
+ store = new PersistedRefIndexStore({ filePath });
10194
+ stores.set(accountId, store);
10130
10195
  }
10196
+ return store;
10131
10197
  }
10132
- function resolveWorkingFile(name, workspaceDir) {
10133
- for (const p2 of [path8.resolve(name), workspaceDir ? path8.join(workspaceDir, name) : null]) {
10134
- if (p2 && fs9.existsSync(p2)) return p2;
10198
+ function flushAllRefIndexStores() {
10199
+ for (const store of stores.values()) {
10200
+ store.flush();
10135
10201
  }
10136
- return null;
10137
10202
  }
10138
- async function sendImageMedia(gw, target, source, params) {
10139
- try {
10140
- const result = await gw.sendMedia(target, source, {
10141
- text: params.text,
10142
- msgId: params.replyToId
10143
- });
10144
- return { messageId: result.id };
10145
- } catch (err) {
10146
- return { error: formatErr(err) };
10147
- }
10203
+
10204
+ // src/runtime.ts
10205
+ var runtime = null;
10206
+ var exitHooksInstalled = false;
10207
+ function setQQBotRuntime(next) {
10208
+ runtime = next;
10209
+ const version = getOpenclawVersion(next.version);
10210
+ setOpenClawVersion(version);
10211
+ installExitHooksOnce();
10148
10212
  }
10149
- async function sendVoiceMedia(gw, target, source, params) {
10150
- const voiceSource = resolveVoiceSource2(source);
10151
- try {
10152
- const result = await gw.sendVoice(target, voiceSource, {
10153
- msgId: params.replyToId
10154
- });
10155
- return { messageId: result.id };
10156
- } catch (err) {
10157
- params.log?.child("media")?.warn(`sendVoice failed (${formatErr(err)}), falling back to sendFile`);
10213
+ function installExitHooksOnce() {
10214
+ if (exitHooksInstalled) return;
10215
+ exitHooksInstalled = true;
10216
+ const flush = () => {
10158
10217
  try {
10159
- const fileName = path8.basename(source);
10160
- const fallback = await gw.sendFile(target, source, {
10161
- text: params.text,
10162
- msgId: params.replyToId,
10163
- fileName
10164
- });
10165
- return { messageId: fallback.id, fallback: true };
10166
- } catch (fallbackErr) {
10167
- return { error: `voice: ${formatErr(err)} | fallback file: ${formatErr(fallbackErr)}` };
10218
+ flushAllRefIndexStores();
10219
+ } catch {
10220
+ }
10221
+ };
10222
+ process.on("beforeExit", flush);
10223
+ process.on("SIGINT", () => {
10224
+ flush();
10225
+ process.exit(0);
10226
+ });
10227
+ process.on("SIGTERM", () => {
10228
+ flush();
10229
+ process.exit(0);
10230
+ });
10231
+ }
10232
+ function getQQBotRuntime() {
10233
+ if (!runtime) throw new Error("QQBot runtime not initialized");
10234
+ return runtime;
10235
+ }
10236
+ function tryGetQQBotRuntime() {
10237
+ return runtime;
10238
+ }
10239
+
10240
+ // src/channel.ts
10241
+ init_resolve();
10242
+
10243
+ // src/outbound/media-send.ts
10244
+ var path8 = __toESM(require("path"), 1);
10245
+ var fs9 = __toESM(require("fs"), 1);
10246
+ var os4 = __toESM(require("os"), 1);
10247
+
10248
+ // src/adapter/workspace.ts
10249
+ var import_node_module = require("module");
10250
+ var req = (0, import_node_module.createRequire)(__filename);
10251
+ var health = null;
10252
+ function resolveAgentWorkspace(cfg, agentId) {
10253
+ if (!health) {
10254
+ try {
10255
+ health = req("openclaw/plugin-sdk/health");
10256
+ } catch {
10257
+ health = null;
10168
10258
  }
10169
10259
  }
10260
+ if (health) {
10261
+ return health.resolveAgentWorkspaceDir(cfg, agentId ?? health.resolveDefaultAgentId(cfg));
10262
+ }
10263
+ return getQQBotMediaDir();
10170
10264
  }
10171
- async function sendVideoMedia(gw, target, source, params) {
10172
- try {
10173
- const result = await gw.sendVideo(target, source, {
10174
- text: params.text,
10175
- msgId: params.replyToId
10176
- });
10177
- return { messageId: result.id };
10178
- } catch (err) {
10179
- return { error: formatErr(err) };
10265
+
10266
+ // src/outbound/media-send.ts
10267
+ init_resolve();
10268
+
10269
+ // src/utils/ssrf-guard.ts
10270
+ var import_node_net = __toESM(require("net"), 1);
10271
+ var import_promises = __toESM(require("dns/promises"), 1);
10272
+ var RESERVED_V4_PREFIXES = [
10273
+ "127.",
10274
+ // loopback
10275
+ "10.",
10276
+ // class-A private
10277
+ "192.168.",
10278
+ // class-C private
10279
+ "169.254."
10280
+ // link-local / cloud metadata
10281
+ ];
10282
+ var PRIVATE_172_RE = /^172\.(1[6-9]|2\d|3[01])\./;
10283
+ function isReservedAddr(ip) {
10284
+ if (ip === "0.0.0.0") return true;
10285
+ for (const pfx of RESERVED_V4_PREFIXES) {
10286
+ if (ip.startsWith(pfx)) return true;
10180
10287
  }
10288
+ if (PRIVATE_172_RE.test(ip)) return true;
10289
+ const lower = ip.toLowerCase();
10290
+ if (lower === "::1" || lower === "::") return true;
10291
+ if (lower.startsWith("fe80:")) return true;
10292
+ if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
10293
+ return false;
10181
10294
  }
10182
- async function sendFileMedia(gw, target, source, params) {
10183
- try {
10184
- const fileName = path8.basename(source);
10185
- const result = await gw.sendFile(target, source, {
10186
- text: params.text,
10187
- msgId: params.replyToId,
10188
- fileName
10189
- });
10190
- return { messageId: result.id };
10191
- } catch (err) {
10192
- return { error: formatErr(err) };
10295
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:"]);
10296
+ var QQ_TRUSTED_DOMAINS = /* @__PURE__ */ new Set([
10297
+ // QQ Bot API
10298
+ "api.sgroup.qq.com",
10299
+ "sandbox.api.sgroup.qq.com",
10300
+ // QQ Bot Token
10301
+ "bots.qq.com",
10302
+ // QQ 多媒体上传/下载
10303
+ "multimedia.nt.qq.com.cn",
10304
+ "multimedia.nt.qq.com",
10305
+ // QQ 群文件
10306
+ "grouppro.grouppro.qq.com"
10307
+ ]);
10308
+ function isQQTrustedDomain(hostname) {
10309
+ if (QQ_TRUSTED_DOMAINS.has(hostname)) return true;
10310
+ const dot = hostname.indexOf(".");
10311
+ if (dot > 0) {
10312
+ const parent = hostname.slice(dot + 1);
10313
+ return QQ_TRUSTED_DOMAINS.has(parent);
10193
10314
  }
10315
+ return false;
10194
10316
  }
10195
- function resolveVoiceSource2(source) {
10196
- if (source.startsWith("http://") || source.startsWith("https://")) {
10197
- return { url: source };
10317
+ async function validateRemoteUrl(raw) {
10318
+ const url = new URL(raw);
10319
+ if (!ALLOWED_SCHEMES.has(url.protocol)) {
10320
+ throw new Error(
10321
+ `\u4E0D\u652F\u6301\u7684\u534F\u8BAE "${url.protocol}"\uFF0C\u4EC5\u5141\u8BB8 http/https\uFF08URL: ${raw}\uFF09`
10322
+ );
10198
10323
  }
10199
- if (source.startsWith("data:") || !source.startsWith("/") && !source.startsWith("./") && !source.startsWith("../") && !source.startsWith("~")) {
10200
- const commaIdx = source.indexOf(",");
10201
- return { base64: commaIdx > 0 ? source.slice(commaIdx + 1) : source };
10324
+ const host = url.hostname.replace(/^\[|\]$/g, "");
10325
+ if (isQQTrustedDomain(host)) return;
10326
+ if (import_node_net.default.isIP(host)) {
10327
+ assertPublicAddr(host, raw);
10328
+ return;
10329
+ }
10330
+ try {
10331
+ const ips = await import_promises.default.resolve(host);
10332
+ for (const ip of ips) {
10333
+ assertPublicAddr(ip, raw, host);
10334
+ }
10335
+ } catch (err) {
10336
+ if (err instanceof Error && err.message.includes("\u5185\u7F51")) throw err;
10337
+ console.warn(`[url-check] DNS \u89E3\u6790 "${host}" \u5931\u8D25: ${err}`);
10202
10338
  }
10203
- return { localPath: source };
10204
10339
  }
10205
- function formatErr(err) {
10206
- if (err instanceof Error) return err.message;
10207
- return String(err);
10340
+ function assertPublicAddr(ip, originalUrl, domain) {
10341
+ if (!isReservedAddr(ip)) return;
10342
+ const target = domain ? `\u57DF\u540D "${domain}" \u89E3\u6790\u5230\u5185\u7F51\u5730\u5740 "${ip}"` : `\u5185\u7F51\u5730\u5740 "${ip}"`;
10343
+ throw new Error(
10344
+ `\u7981\u6B62\u8BBF\u95EE${target}\uFF0C\u5DF2\u62E6\u622A\u6F5C\u5728\u7684 SSRF \u8BF7\u6C42\uFF08URL: ${originalUrl}\uFF09`
10345
+ );
10208
10346
  }
10209
10347
 
10210
- // src/adapter/setup.ts
10211
- var import_node_module2 = require("module");
10212
- var req2 = (0, import_node_module2.createRequire)(__filename);
10213
- var _setup;
10214
- var _tools;
10215
- function loadSetup() {
10216
- if (_setup !== void 0) return _setup;
10217
- try {
10218
- _setup = req2("openclaw/plugin-sdk/setup");
10219
- } catch {
10220
- _setup = null;
10348
+ // src/outbound/local-file-router.ts
10349
+ var path7 = __toESM(require("path"), 1);
10350
+ var fs8 = __toESM(require("fs"), 1);
10351
+ var os3 = __toESM(require("os"), 1);
10352
+ function normalizePath(p2) {
10353
+ let result = p2;
10354
+ if (result.startsWith("file://")) {
10355
+ result = result.slice("file://".length);
10356
+ if (/^\/[a-zA-Z]:[\\/]/.test(result)) {
10357
+ result = result.slice(1);
10358
+ }
10221
10359
  }
10222
- return _setup;
10223
- }
10224
- function loadTools() {
10225
- if (_tools !== void 0) return _tools;
10226
10360
  try {
10227
- _tools = req2("openclaw/plugin-sdk/setup-tools");
10361
+ result = decodeURIComponent(result);
10228
10362
  } catch {
10229
- _tools = null;
10230
10363
  }
10231
- return _tools;
10232
- }
10233
- var DEFAULT_ACCOUNT_ID2 = "default";
10234
- function createStandardChannelSetupStatus(...args) {
10235
- const mod = loadSetup();
10236
- if (mod) return mod.createStandardChannelSetupStatus(...args);
10237
- return {
10238
- channelLabel: args[0]?.channelLabel ?? "QQ Bot",
10239
- configuredLabel: "Configured",
10240
- unconfiguredLabel: "Not configured",
10241
- resolveConfigured: () => false
10242
- };
10243
- }
10244
- function setSetupChannelEnabled(...args) {
10245
- loadSetup()?.setSetupChannelEnabled?.(...args);
10364
+ if (result === "~" || result.startsWith("~/") || result.startsWith("~\\")) {
10365
+ result = result.replace(/^~/, os3.homedir());
10366
+ }
10367
+ return result;
10246
10368
  }
10247
- function formatDocsLink(...args) {
10248
- const mod = loadTools();
10249
- if (mod) return mod.formatDocsLink(...args);
10250
- return args[1] ? `${args[1]}: ${args[0]}` : args[0];
10369
+ function isLocalFilePath(source) {
10370
+ if (!source) return false;
10371
+ if (source.startsWith("http://") || source.startsWith("https://")) return false;
10372
+ if (source.startsWith("data:")) return false;
10373
+ if (source.startsWith("file://")) return true;
10374
+ if (source === "~" || source.startsWith("~/") || source.startsWith("~\\")) return true;
10375
+ if (source.startsWith("/")) return true;
10376
+ if (/^[a-zA-Z]:[\\/]/.test(source)) return true;
10377
+ if (source.startsWith("\\\\")) return true;
10378
+ if (source.startsWith("./") || source.startsWith("../")) return true;
10379
+ if (source.startsWith(".\\") || source.startsWith("..\\")) return true;
10380
+ return false;
10251
10381
  }
10252
-
10253
- // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qr-connect.js
10254
- var import_qrcode_terminal = __toESM(require_main(), 1);
10255
-
10256
- // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qqbot-session.js
10257
- var import_node_crypto = __toESM(require("crypto"), 1);
10258
- var import_node_https = __toESM(require("https"), 1);
10259
- var E = { production: "q.qq.com", test: "test.q.qq.com" };
10260
- function d(t = "production") {
10261
- return E[t];
10382
+ function isDataUrl(source) {
10383
+ return source.startsWith("data:");
10262
10384
  }
10263
- function l() {
10264
- return import_node_crypto.default.randomBytes(32).toString("base64");
10385
+ var IMAGE_EXTS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".ico"]);
10386
+ var VOICE_EXTS = /* @__PURE__ */ new Set([".wav", ".mp3", ".silk", ".amr", ".ogg", ".flac", ".aac", ".m4a"]);
10387
+ var VIDEO_EXTS = /* @__PURE__ */ new Set([".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv"]);
10388
+ function inferMediaKind(filePath) {
10389
+ const ext = path7.extname(filePath).toLowerCase();
10390
+ if (IMAGE_EXTS.has(ext)) return "image";
10391
+ if (VOICE_EXTS.has(ext)) return "voice";
10392
+ if (VIDEO_EXTS.has(ext)) return "video";
10393
+ return "file";
10265
10394
  }
10266
- var u;
10267
- (function(t) {
10268
- t[t.NONE = 0] = "NONE", t[t.PENDING = 1] = "PENDING", t[t.COMPLETED = 2] = "COMPLETED", t[t.EXPIRED = 3] = "EXPIRED";
10269
- })(u || (u = {}));
10270
- function b(t, r) {
10271
- const a = Buffer.from(r, "base64"), n = Buffer.from(t, "base64"), e = n.subarray(0, 12), c = n.subarray(n.length - 16), s = n.subarray(12, n.length - 16), o = import_node_crypto.default.createDecipheriv("aes-256-gcm", a, e);
10272
- return o.setAuthTag(c), Buffer.concat([o.update(s), o.final()]).toString("utf8");
10395
+ function inferMediaKindFromMime(mime) {
10396
+ const lower = mime.toLowerCase();
10397
+ if (lower.startsWith("image/")) return "image";
10398
+ if (lower.startsWith("audio/") || lower === "voice") return "voice";
10399
+ if (lower.startsWith("video/")) return "video";
10400
+ return "file";
10273
10401
  }
10274
- function h(t, r, a) {
10275
- return new Promise((n, e) => {
10276
- const c = JSON.stringify(r), s = new URL(t), o = import_node_https.default.request({ hostname: s.hostname, path: s.pathname + s.search, method: "POST", timeout: a, headers: { "Content-Type": "application/json", Accept: "application/json", "Content-Length": Buffer.byteLength(c) } }, (i) => {
10277
- if (i.statusCode !== 200) {
10278
- i.resume(), e(new Error(`HTTP ${i.statusCode} from ${t}`));
10279
- return;
10402
+ function isPathInAllowedRoots(absPath, allowedRoots) {
10403
+ if (!allowedRoots.length) return false;
10404
+ try {
10405
+ const real = fs8.realpathSync(absPath);
10406
+ return allowedRoots.some((root) => {
10407
+ try {
10408
+ const rootReal = fs8.existsSync(root) ? fs8.realpathSync(root) : root;
10409
+ return real.startsWith(rootReal + path7.sep) || real === rootReal;
10410
+ } catch {
10411
+ return false;
10280
10412
  }
10281
- let f = "";
10282
- i.on("data", (p2) => {
10283
- f += p2;
10284
- }), i.on("end", () => {
10285
- try {
10286
- n(JSON.parse(f));
10287
- } catch (p2) {
10288
- e(p2);
10289
- }
10290
- });
10291
10413
  });
10292
- o.on("error", e), o.on("timeout", () => {
10293
- o.destroy(), e(new Error(`timeout fetching ${t}`));
10294
- }), o.end(c);
10295
- });
10296
- }
10297
- async function y(t = "production", r = 1e4) {
10298
- const a = `https://${d(t)}/lite/create_bind_task`, n = l(), e = await h(a, { key: n }, r);
10299
- if (e.retcode !== 0) throw new Error(e.msg ?? "create_bind_task failed");
10300
- if (!e.data?.task_id) throw new Error("create_bind_task: missing task_id");
10301
- return { taskId: e.data.task_id, key: n };
10302
- }
10303
- async function g(t, r = "production", a = 1e4) {
10304
- const n = `https://${d(r)}/lite/poll_bind_result`, e = await h(n, { task_id: t }, a);
10305
- if (e.retcode !== 0) throw new Error(e.msg ?? "poll_bind_result failed");
10306
- return { status: e.data?.status ?? u.NONE, botAppId: String(e.data?.bot_appid ?? ""), botEncryptSecret: e.data?.bot_encrypt_secret ?? "", userOpenid: e.data?.user_openid || void 0 };
10307
- }
10308
- function w(t, r = "") {
10309
- return `https://${d("production")}/qqbot/openclaw/connect.html?task_id=${encodeURIComponent(t)}&source=${encodeURIComponent(r)}&_wv=2`;
10414
+ } catch {
10415
+ return false;
10416
+ }
10310
10417
  }
10311
10418
 
10312
- // node_modules/.pnpm/@tencent-connect+qqbot-connector@1.2.0/node_modules/@tencent-connect/qqbot-connector/dist/esm/qr-connect.js
10313
- var p = 2e3;
10314
- function F(r) {
10315
- return new Promise((o) => {
10316
- import_qrcode_terminal.default.generate(r, { small: true }, (e) => {
10317
- o(e);
10318
- });
10319
- });
10320
- }
10321
- function d2(r, o) {
10322
- return new Promise((e, t) => {
10323
- if (o?.aborted) {
10324
- t(new DOMException("Aborted", "AbortError"));
10325
- return;
10419
+ // src/outbound/media-send.ts
10420
+ function resolveTempRoots() {
10421
+ const roots = /* @__PURE__ */ new Set();
10422
+ try {
10423
+ const tmp = os4.tmpdir();
10424
+ roots.add(fs9.existsSync(tmp) ? fs9.realpathSync(tmp) : tmp);
10425
+ } catch {
10426
+ }
10427
+ if (process.platform !== "win32") {
10428
+ try {
10429
+ roots.add(fs9.realpathSync("/tmp"));
10430
+ } catch {
10326
10431
  }
10327
- const n = setTimeout(e, r);
10328
- o?.addEventListener("abort", () => {
10329
- clearTimeout(n), t(new DOMException("Aborted", "AbortError"));
10330
- }, { once: true });
10331
- });
10432
+ }
10433
+ return [...roots];
10332
10434
  }
10333
- async function C(r, o, e) {
10334
- for (; !e?.aborted; ) {
10335
- let t;
10435
+ function buildDynamicAllowedRoots(workspaceDir) {
10436
+ const home = os4.homedir();
10437
+ const derivedBase = workspaceDir ? path8.dirname(workspaceDir) : path8.join(home, ".openclaw");
10438
+ const roots = [];
10439
+ const added = /* @__PURE__ */ new Set();
10440
+ const addRoot = (p2) => {
10336
10441
  try {
10337
- t = await g(r);
10442
+ const real = fs9.existsSync(p2) ? fs9.realpathSync(p2) : p2;
10443
+ if (!added.has(real)) {
10444
+ added.add(real);
10445
+ roots.push(real);
10446
+ }
10338
10447
  } catch {
10339
- await d2(p, e);
10340
- continue;
10341
- }
10342
- if (t.status === u.COMPLETED) {
10343
- const n = b(t.botEncryptSecret, o);
10344
- return { outcome: "scanned", appId: t.botAppId, appSecret: n, userOpenid: t.userOpenid };
10345
10448
  }
10346
- if (t.status === u.EXPIRED) return { outcome: "expired" };
10347
- await d2(p, e);
10449
+ };
10450
+ const knownBases = [path8.join(home, ".openclaw"), path8.join(home, ".openclaw-dev")];
10451
+ if (workspaceDir) {
10452
+ addRoot(path8.join(derivedBase, "media"));
10453
+ addRoot(path8.join(derivedBase, "workspace"));
10454
+ addRoot(path8.join(derivedBase, "outbound"));
10455
+ addRoot(workspaceDir);
10348
10456
  }
10349
- throw new DOMException("Aborted", "AbortError");
10457
+ for (const base of knownBases) {
10458
+ addRoot(path8.join(base, "media"));
10459
+ addRoot(path8.join(base, "workspace"));
10460
+ addRoot(path8.join(base, "outbound"));
10461
+ }
10462
+ for (const t of resolveTempRoots()) addRoot(t);
10463
+ return roots;
10350
10464
  }
10351
- function l2(r, o) {
10352
- const e = new AbortController(), t = o?.signal ? AbortSignal.any([e.signal, o.signal]) : e.signal;
10353
- return (async () => {
10354
- const n = o?.displayQrCodeToConsole ?? true;
10355
- for (; ; ) {
10356
- if (t.aborted) throw new DOMException("Aborted", "AbortError");
10357
- let a;
10358
- try {
10359
- a = await y();
10360
- } catch (u2) {
10361
- throw new Error(`\u83B7\u53D6\u7ED1\u5B9A\u4EFB\u52A1\u5931\u8D25: ${u2 instanceof Error ? u2.message : String(u2)}`, { cause: u2 });
10362
- }
10363
- const i = w(a.taskId, o?.source);
10364
- if (n) {
10365
- const u2 = await F(i);
10366
- console.log(u2), console.log(`\u8BF7\u4F7F\u7528\u624B\u673A QQ \u626B\u63CF\u4E0A\u65B9\u4E8C\u7EF4\u7801\uFF0C\u5B8C\u6210\u673A\u5668\u4EBA\u7ED1\u5B9A\u3002
10367
- `);
10368
- }
10369
- r.onQrDisplayed?.(i);
10370
- const s = await C(a.taskId, a.key, t);
10371
- if (s.outcome === "scanned") {
10372
- r.onSuccess([{ appId: s.appId, appSecret: s.appSecret, userOpenid: s.userOpenid }]);
10373
- return;
10374
- }
10375
- r.onQrExpired?.(), n && console.log(`\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u6B63\u5728\u5237\u65B0\u2026
10376
- `);
10465
+ var MAX_DATA_URL_BYTES = 10 * 1024 * 1024;
10466
+ async function sendMedia2(params) {
10467
+ const { source, accountId, log: log4 } = params;
10468
+ const mlog = log4?.child("media");
10469
+ if (!source) {
10470
+ mlog?.error("source is empty");
10471
+ return { error: "sendMedia: source is required" };
10472
+ }
10473
+ const wsDir = resolveWorkspaceFromAgent(params.agentId);
10474
+ mlog?.debug(`resolveMediaPath source=${source} agentId=${params.agentId ?? "none"} workspaceDir=${wsDir ?? "none"}`);
10475
+ const resolved = await resolveMediaPath(source, mlog, wsDir);
10476
+ if (!resolved.ok) {
10477
+ mlog?.error(`resolveMediaPath failed: ${resolved.error}`);
10478
+ return { error: resolved.error };
10479
+ }
10480
+ const kind = params.mediaKind ?? (params.mimeType ? inferMediaKindFromMime(params.mimeType) : void 0) ?? inferMediaKind(resolved.path);
10481
+ const gw = getGateway(accountId);
10482
+ if (!gw) {
10483
+ return { error: `Bot "${accountId}" not running` };
10484
+ }
10485
+ const target = parseTarget(params.to);
10486
+ switch (kind) {
10487
+ case "voice":
10488
+ return sendVoiceMedia(gw, target, resolved.path, params);
10489
+ case "video":
10490
+ return sendVideoMedia(gw, target, resolved.path, params);
10491
+ case "file":
10492
+ return sendFileMedia(gw, target, resolved.path, params);
10493
+ case "image":
10494
+ default:
10495
+ return sendImageMedia(gw, target, resolved.path, params);
10496
+ }
10497
+ }
10498
+ async function resolveMediaPath(source, log4, workspaceDir) {
10499
+ const normalized = normalizePath(source);
10500
+ if (isDataUrl(normalized)) {
10501
+ if (normalized.length > MAX_DATA_URL_BYTES) {
10502
+ const sizeMB = (normalized.length / (1024 * 1024)).toFixed(1);
10503
+ return { ok: false, error: `Data URL \u8FC7\u5927\uFF08${sizeMB}MB\uFF0C\u6700\u5927 10MB\uFF09` };
10504
+ }
10505
+ return { ok: true, path: normalized, isLocal: false };
10506
+ }
10507
+ if (normalized.startsWith("http://") || normalized.startsWith("https://")) {
10508
+ try {
10509
+ await validateRemoteUrl(normalized);
10510
+ } catch (err) {
10511
+ log4?.warn(`SSRF blocked for media URL: ${normalized}`);
10512
+ return { ok: false, error: `\u5A92\u4F53 URL \u88AB SSRF \u9632\u62A4\u62E6\u622A: ${err instanceof Error ? err.message : String(err)}` };
10377
10513
  }
10378
- })().catch((n) => {
10379
- if (n instanceof DOMException && n.name === "AbortError") {
10380
- r.onFailure(new Error("\u5DF2\u53D6\u6D88"));
10381
- return;
10514
+ return { ok: true, path: normalized, isLocal: false };
10515
+ }
10516
+ if (!isLocalFilePath(normalized)) {
10517
+ const resolved2 = resolveWorkingFile(normalized, workspaceDir);
10518
+ if (resolved2) {
10519
+ return { ok: true, path: resolved2, isLocal: true };
10382
10520
  }
10383
- r.onFailure(n instanceof Error ? n : new Error(String(n)));
10384
- }), () => e.abort();
10521
+ return { ok: true, path: normalized, isLocal: false };
10522
+ }
10523
+ const resolved = path8.resolve(normalized);
10524
+ if (!fs9.existsSync(resolved)) {
10525
+ return { ok: false, error: `File not found: ${resolved}` };
10526
+ }
10527
+ let real;
10528
+ try {
10529
+ real = fs9.realpathSync(resolved);
10530
+ } catch {
10531
+ return { ok: false, error: `Cannot resolve path: ${resolved}` };
10532
+ }
10533
+ const dynamicRoots = buildDynamicAllowedRoots(workspaceDir);
10534
+ const allowed = isPathInAllowedRoots(real, dynamicRoots);
10535
+ if (!allowed) {
10536
+ log4?.warn(`path blocked \u2014 not in allowed directory: ${real}`);
10537
+ return { ok: false, error: `\u6587\u4EF6\u8DEF\u5F84\u4E0D\u5728\u5141\u8BB8\u7684\u76EE\u5F55\u4E2D` };
10538
+ }
10539
+ return { ok: true, path: real, isLocal: true };
10385
10540
  }
10386
- function m2(r) {
10387
- return new Promise((o, e) => {
10388
- l2({ onSuccess: o, onFailure: e }, { ...r, displayQrCodeToConsole: true });
10389
- });
10541
+ function resolveWorkspaceFromAgent(agentId) {
10542
+ const cfg = resolveConfigViaAdapter();
10543
+ if (!cfg) return void 0;
10544
+ return resolveAgentWorkspace(cfg, agentId);
10390
10545
  }
10391
-
10392
- // src/setup/finalize.ts
10393
- function isConfigured(cfg, accountId) {
10394
- const account = resolveQQBotAccount(cfg, accountId);
10395
- return Boolean(account.appId && account.clientSecret);
10546
+ function resolveConfigViaAdapter() {
10547
+ try {
10548
+ const rt = tryGetQQBotRuntime();
10549
+ if (!rt) return void 0;
10550
+ return getAdapters(rt).getConfig?.();
10551
+ } catch {
10552
+ return void 0;
10553
+ }
10554
+ }
10555
+ function resolveWorkingFile(name, workspaceDir) {
10556
+ for (const p2 of [path8.resolve(name), workspaceDir ? path8.join(workspaceDir, name) : null]) {
10557
+ if (p2 && fs9.existsSync(p2)) return p2;
10558
+ }
10559
+ return null;
10396
10560
  }
10397
- async function linkViaQrCode(cfg, accountId, prompter, rt) {
10561
+ async function sendImageMedia(gw, target, source, params) {
10398
10562
  try {
10399
- const accounts = await m2({ source: "openclaw" });
10400
- if (accounts.length === 0) {
10401
- await prompter.note("\u672A\u83B7\u53D6\u5230\u4EFB\u4F55 QQ Bot \u8D26\u53F7\u4FE1\u606F\u3002", "QQ Bot");
10402
- return cfg;
10403
- }
10404
- let next = cfg;
10405
- for (const { appId, appSecret, userOpenid } of accounts) {
10406
- next = applyQQBotAccountConfig(next, appId, { appId, clientSecret: appSecret });
10407
- next = applyAccountDefaults(next, appId, userOpenid);
10408
- }
10409
- if (accounts.length === 1) {
10410
- rt.log(`\u2714 QQ Bot \u7ED1\u5B9A\u6210\u529F\uFF01(AppID: ${accounts[0].appId})`);
10411
- } else {
10412
- rt.log(`\u2714 ${accounts.length} \u4E2A QQ Bot \u7ED1\u5B9A\u6210\u529F\uFF01(AppID: ${accounts.map((a) => a.appId).join(", ")})`);
10413
- }
10414
- return next;
10563
+ const result = await gw.sendMedia(target, source, {
10564
+ text: params.text,
10565
+ msgId: params.replyToId
10566
+ });
10567
+ return { messageId: result.id };
10415
10568
  } catch (err) {
10416
- rt.error(`QQ Bot \u7ED1\u5B9A\u5931\u8D25: ${String(err)}`);
10417
- await prompter.note(`\u7ED1\u5B9A\u5931\u8D25\uFF0C\u60A8\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u914D\u7F6E\u3002
10418
- \u6587\u6863: ${formatDocsLink("/channels/qqbot", "qqbot")}`, "QQ Bot");
10419
- return cfg;
10569
+ return { error: formatErr(err) };
10420
10570
  }
10421
10571
  }
10422
- async function linkViaManual(cfg, prompter) {
10423
- const appId = await prompter.text({ message: "\u8BF7\u8F93\u5165 QQ Bot AppID", validate: (v) => v.trim() ? void 0 : "AppID \u4E0D\u80FD\u4E3A\u7A7A" });
10424
- const secret = await prompter.text({ message: "\u8BF7\u8F93\u5165 QQ Bot AppSecret", validate: (v) => v.trim() ? void 0 : "AppSecret \u4E0D\u80FD\u4E3A\u7A7A" });
10425
- let next = applyQQBotAccountConfig(cfg, appId.trim(), { appId: appId.trim(), clientSecret: secret.trim() });
10426
- next = applyAccountDefaults(next, appId.trim());
10427
- await prompter.note("\u2714 QQ Bot \u914D\u7F6E\u5B8C\u6210\uFF01", "QQ Bot");
10428
- return next;
10572
+ async function sendVoiceMedia(gw, target, source, params) {
10573
+ const voiceSource = resolveVoiceSource2(source);
10574
+ try {
10575
+ const result = await gw.sendVoice(target, voiceSource, {
10576
+ msgId: params.replyToId
10577
+ });
10578
+ return { messageId: result.id };
10579
+ } catch (err) {
10580
+ params.log?.child("media")?.warn(`sendVoice failed (${formatErr(err)}), falling back to sendFile`);
10581
+ try {
10582
+ const fileName = path8.basename(source);
10583
+ const fallback = await gw.sendFile(target, source, {
10584
+ text: params.text,
10585
+ msgId: params.replyToId,
10586
+ fileName
10587
+ });
10588
+ return { messageId: fallback.id, fallback: true };
10589
+ } catch (fallbackErr) {
10590
+ return { error: `voice: ${formatErr(err)} | fallback file: ${formatErr(fallbackErr)}` };
10591
+ }
10592
+ }
10429
10593
  }
10430
- async function finalizeQQBotSetup(params) {
10431
- const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID2;
10432
- const configured = isConfigured(params.cfg, accountId);
10433
- const mode = await params.prompter.select({
10434
- message: configured ? "QQ \u5DF2\u7ED1\u5B9A\uFF0C\u9009\u62E9\u64CD\u4F5C" : "\u9009\u62E9 QQ \u7ED1\u5B9A\u65B9\u5F0F",
10435
- options: [
10436
- { value: "qr", label: "\u626B\u7801\u7ED1\u5B9A\uFF08\u63A8\u8350\uFF09", hint: "\u4F7F\u7528 QQ \u626B\u63CF\u4E8C\u7EF4\u7801\u81EA\u52A8\u5B8C\u6210\u7ED1\u5B9A" },
10437
- { value: "manual", label: "\u624B\u52A8\u8F93\u5165 QQ Bot AppID \u548C AppSecret", hint: "\u9700\u5230 QQ \u5F00\u653E\u5E73\u53F0 q.qq.com \u67E5\u770B" },
10438
- { value: "skip", label: configured ? "\u4FDD\u6301\u5F53\u524D\u914D\u7F6E" : "\u7A0D\u540E\u914D\u7F6E" }
10439
- ]
10440
- });
10441
- let next = params.cfg;
10442
- if (mode === "qr") {
10443
- next = await linkViaQrCode(next, accountId, params.prompter, params.runtime);
10444
- } else if (mode === "manual") {
10445
- next = await linkViaManual(next, params.prompter);
10446
- } else if (!configured) {
10447
- await params.prompter.note("\u60A8\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u91CD\u65B0\u914D\u7F6E\uFF1A\n openclaw channels add", "QQ Bot");
10594
+ async function sendVideoMedia(gw, target, source, params) {
10595
+ try {
10596
+ const result = await gw.sendVideo(target, source, {
10597
+ text: params.text,
10598
+ msgId: params.replyToId
10599
+ });
10600
+ return { messageId: result.id };
10601
+ } catch (err) {
10602
+ return { error: formatErr(err) };
10448
10603
  }
10449
- return { cfg: next };
10450
10604
  }
10451
- function applyAccountDefaults(cfg, accountId, userOpenid) {
10452
- const next = { ...cfg, channels: { ...cfg.channels } };
10453
- const qqbot = { ...next.channels?.qqbot ?? {} };
10454
- const defaults = { streaming: true, dmPolicy: "allowlist" };
10455
- if (userOpenid) defaults.allowFrom = [userOpenid];
10456
- if (accountId === DEFAULT_ACCOUNT_ID2) {
10457
- Object.assign(qqbot, defaults);
10458
- } else {
10459
- const accounts = { ...qqbot.accounts ?? {} };
10460
- accounts[accountId] = { ...accounts[accountId] ?? {}, ...defaults };
10461
- qqbot.accounts = accounts;
10605
+ async function sendFileMedia(gw, target, source, params) {
10606
+ try {
10607
+ const fileName = path8.basename(source);
10608
+ const result = await gw.sendFile(target, source, {
10609
+ text: params.text,
10610
+ msgId: params.replyToId,
10611
+ fileName
10612
+ });
10613
+ return { messageId: result.id };
10614
+ } catch (err) {
10615
+ return { error: formatErr(err) };
10462
10616
  }
10463
- next.channels = { ...next.channels, qqbot };
10464
- return next;
10617
+ }
10618
+ function resolveVoiceSource2(source) {
10619
+ if (source.startsWith("http://") || source.startsWith("https://")) {
10620
+ return { url: source };
10621
+ }
10622
+ if (source.startsWith("data:") || !source.startsWith("/") && !source.startsWith("./") && !source.startsWith("../") && !source.startsWith("~")) {
10623
+ const commaIdx = source.indexOf(",");
10624
+ return { base64: commaIdx > 0 ? source.slice(commaIdx + 1) : source };
10625
+ }
10626
+ return { localPath: source };
10627
+ }
10628
+ function formatErr(err) {
10629
+ if (err instanceof Error) return err.message;
10630
+ return String(err);
10465
10631
  }
10466
10632
 
10467
10633
  // src/setup/surface.ts
10634
+ init_setup();
10635
+ init_config();
10636
+ init_finalize();
10468
10637
  var CHANNEL = "qqbot";
10469
10638
  var qqbotSetupWizard = {
10470
10639
  channel: CHANNEL,
@@ -10481,6 +10650,14 @@ var qqbotSetupWizard = {
10481
10650
  return Boolean(account.appId && account.clientSecret);
10482
10651
  })
10483
10652
  }),
10653
+ // 未配置时默认使用 default 账号,有账户时框架会提示选择
10654
+ resolveAccountIdForConfigure: async ({ cfg, shouldPromptAccountIds, accountOverride, defaultAccountId }) => {
10655
+ if (accountOverride) return accountOverride;
10656
+ const ids = listQQBotAccountIds(cfg);
10657
+ if (ids.length === 0) return "default";
10658
+ if (!shouldPromptAccountIds) return ids[0];
10659
+ return defaultAccountId || resolveDefaultQQBotAccountId(cfg);
10660
+ },
10484
10661
  credentials: [],
10485
10662
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10486
10663
  finalize: (async ({ cfg, accountId, prompter, runtime: runtime2 }) => finalizeQQBotSetup({ cfg, accountId, prompter, runtime: runtime2 })),
@@ -10490,6 +10667,118 @@ var qqbotSetupWizard = {
10490
10667
  }
10491
10668
  };
10492
10669
 
10670
+ // src/setup/login.ts
10671
+ init_esm();
10672
+ var pendingSessions = /* @__PURE__ */ new Map();
10673
+ function startQrLogin(accountId, source = "openclaw") {
10674
+ const key = accountId ?? "default";
10675
+ pendingSessions.get(key)?.dispose();
10676
+ pendingSessions.delete(key);
10677
+ return new Promise((resolve2) => {
10678
+ let credentialsResolve;
10679
+ let credentialsReject;
10680
+ const credentialsPromise = new Promise((res, rej) => {
10681
+ credentialsResolve = res;
10682
+ credentialsReject = rej;
10683
+ });
10684
+ const dispose = l2(
10685
+ {
10686
+ onQrDisplayed(url) {
10687
+ resolve2({
10688
+ qrDataUrl: url,
10689
+ message: "\u8BF7\u4F7F\u7528\u624B\u673A QQ \u626B\u63CF\u4E8C\u7EF4\u7801\u5B8C\u6210\u7ED1\u5B9A"
10690
+ });
10691
+ },
10692
+ onSuccess: (creds) => credentialsResolve(creds),
10693
+ onFailure: (err) => credentialsReject(err)
10694
+ },
10695
+ { displayQrCodeToConsole: true, source }
10696
+ );
10697
+ pendingSessions.set(key, { dispose, credentialsPromise });
10698
+ });
10699
+ }
10700
+ async function waitQrLogin(accountId) {
10701
+ const key = accountId ?? "default";
10702
+ const session = pendingSessions.get(key);
10703
+ if (!session) {
10704
+ return { connected: false, message: "\u6CA1\u6709\u6B63\u5728\u8FDB\u884C\u7684\u767B\u5F55\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u8FD0\u884C login \u547D\u4EE4\u3002" };
10705
+ }
10706
+ try {
10707
+ const credentials = await session.credentialsPromise;
10708
+ pendingSessions.delete(key);
10709
+ if (credentials.length === 0) {
10710
+ return { connected: false, message: "\u672A\u83B7\u53D6\u5230 QQ Bot \u51ED\u636E\u3002" };
10711
+ }
10712
+ return {
10713
+ connected: true,
10714
+ message: `\u7ED1\u5B9A\u6210\u529F\uFF01AppID: ${credentials.map((c) => c.appId).join(", ")}`,
10715
+ credentials
10716
+ };
10717
+ } catch (err) {
10718
+ pendingSessions.delete(key);
10719
+ return {
10720
+ connected: false,
10721
+ message: `\u7ED1\u5B9A\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
10722
+ };
10723
+ }
10724
+ }
10725
+ function parseChannelInput(channelInput) {
10726
+ if (!channelInput) return null;
10727
+ const parts = channelInput.trim().split(":");
10728
+ if (parts.length === 2 && parts[0] && parts[1]) {
10729
+ return { appId: parts[0], clientSecret: parts[1] };
10730
+ }
10731
+ return null;
10732
+ }
10733
+ async function qqbotLogin({
10734
+ cfg,
10735
+ accountId,
10736
+ channelInput,
10737
+ verbose,
10738
+ ...rest
10739
+ }) {
10740
+ const { applyQQBotAccountConfig: applyQQBotAccountConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
10741
+ const { persistAuthConfig: persistAuthConfig2 } = await Promise.resolve().then(() => (init_resolve(), resolve_exports));
10742
+ const { applyAccountDefaults: applyAccountDefaults2 } = await Promise.resolve().then(() => (init_finalize(), finalize_exports));
10743
+ const { resolveAccountKey: resolveAccountKey2 } = await Promise.resolve().then(() => (init_account_key(), account_key_exports));
10744
+ const runtime2 = rest;
10745
+ const resolvedId = accountId ? accountId.trim().toLowerCase() : null;
10746
+ const parsed = parseChannelInput(channelInput);
10747
+ if (parsed) {
10748
+ const key = resolveAccountKey2(cfg, parsed.appId, resolvedId);
10749
+ const result2 = applyQQBotAccountConfig2(cfg, key, {
10750
+ appId: parsed.appId,
10751
+ clientSecret: parsed.clientSecret
10752
+ });
10753
+ Object.assign(cfg, result2);
10754
+ const withDefaults = applyAccountDefaults2(cfg, key);
10755
+ Object.assign(cfg, withDefaults);
10756
+ await persistAuthConfig2(runtime2, cfg, "restart");
10757
+ if (verbose) console.log(`QQ Bot \u5DF2\u914D\u7F6E (${key}) AppID: ${parsed.appId}`);
10758
+ return;
10759
+ }
10760
+ const { qrDataUrl, message } = await startQrLogin(resolvedId || "pending");
10761
+ console.log(`
10762
+ ${message}`);
10763
+ if (qrDataUrl) console.log(`QR \u94FE\u63A5: ${qrDataUrl}`);
10764
+ const result = await waitQrLogin(resolvedId || "pending");
10765
+ if (!result.connected || !result.credentials) {
10766
+ throw new Error(result.message);
10767
+ }
10768
+ for (const cred of result.credentials) {
10769
+ const key = resolveAccountKey2(cfg, cred.appId, resolvedId);
10770
+ const next = applyQQBotAccountConfig2(cfg, key, {
10771
+ appId: cred.appId,
10772
+ clientSecret: cred.appSecret
10773
+ });
10774
+ Object.assign(cfg, next);
10775
+ const withDefaults = applyAccountDefaults2(cfg, key, cred.userOpenid);
10776
+ Object.assign(cfg, withDefaults);
10777
+ }
10778
+ await persistAuthConfig2(runtime2, cfg, "restart");
10779
+ console.log(`QQ Bot \u767B\u5F55\u6210\u529F\uFF01`);
10780
+ }
10781
+
10493
10782
  // src/outbound/sanitize.ts
10494
10783
  var INTERNAL_TAGS = [
10495
10784
  // 框架脚手架标签
@@ -10515,6 +10804,10 @@ function sanitizeQQBotText(text) {
10515
10804
  return result.trim();
10516
10805
  }
10517
10806
 
10807
+ // src/gateway/lifecycle.ts
10808
+ init_config();
10809
+ init_resolve();
10810
+
10518
10811
  // src/gateway/qqbot-gateway.ts
10519
10812
  var import_node_os4 = __toESM(require("os"), 1);
10520
10813
 
@@ -10751,6 +11044,7 @@ function botMe() {
10751
11044
  }
10752
11045
 
10753
11046
  // src/commands/config-util.ts
11047
+ init_resolve();
10754
11048
  function checkCommandAuth(ctx) {
10755
11049
  const p2 = ctx.state.policy;
10756
11050
  const mode = p2?.c2cMode ?? "allowlist";
@@ -10998,6 +11292,7 @@ var import_node_fs5 = __toESM(require("fs"), 1);
10998
11292
  var import_node_path5 = __toESM(require("path"), 1);
10999
11293
  var import_node_os3 = __toESM(require("os"), 1);
11000
11294
  var import_node_crypto2 = __toESM(require("crypto"), 1);
11295
+ init_resolve();
11001
11296
  var MAX_LINES_PER_FILE = 1e3;
11002
11297
  var MAX_FILES = 4;
11003
11298
  var LOG_KEYWORDS = ["gateway", "openclaw", "clawdbot", "moltbot"];
@@ -11202,6 +11497,7 @@ function botLogs(runtime2) {
11202
11497
  }
11203
11498
 
11204
11499
  // src/commands/bot-approve.ts
11500
+ init_resolve();
11205
11501
  var PRESETS = {
11206
11502
  on: { security: "allowlist", ask: "on-miss", desc: "\u5F00\u542F\u5BA1\u6279\uFF08\u767D\u540D\u5355\u6A21\u5F0F\uFF09" },
11207
11503
  off: { security: "full", ask: "off", desc: "\u5173\u95ED\u5BA1\u6279" },
@@ -11505,10 +11801,10 @@ function buildCommandList(account, opts) {
11505
11801
  // src/middleware/attachment.ts
11506
11802
  var path17 = __toESM(require("path"), 1);
11507
11803
 
11508
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/reply-limiter.js
11804
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/reply-limiter.js
11509
11805
  var DEFAULT_TTL_MS = 60 * 60 * 1e3;
11510
11806
 
11511
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/media-tags.js
11807
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/media-tags.js
11512
11808
  var VALID_TAGS = ["qqimg", "qqvoice", "qqvideo", "qqfile", "qqmedia"];
11513
11809
  var TAG_ALIASES = {
11514
11810
  qq_img: "qqimg",
@@ -11557,30 +11853,30 @@ var SELF_CLOSING_TAG_REGEX = new RegExp("`?" + LEFT_BRACKET + "\\s*(" + TAG_NAME
11557
11853
  var FUZZY_MEDIA_TAG_REGEX = new RegExp("`?" + LEFT_BRACKET + "\\s*(" + TAG_NAME_PATTERN + ")\\s*" + RIGHT_BRACKET + `["']?\\s*([^<\uFF1C<\uFF1E>"'\`]+?)\\s*["']?` + LEFT_BRACKET + "\\s*/?\\s*(?:" + TAG_NAME_PATTERN + ")\\s*" + RIGHT_BRACKET + "`?", "gi");
11558
11854
  var MULTILINE_TAG_CLEANUP = new RegExp("(" + LEFT_BRACKET + "\\s*(?:" + TAG_NAME_PATTERN + ")\\s*" + RIGHT_BRACKET + ")([\\s\\S]*?)(" + LEFT_BRACKET + "\\s*/?\\s*(?:" + TAG_NAME_PATTERN + ")\\s*" + RIGHT_BRACKET + ")", "gi");
11559
11855
 
11560
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/ref-index-store.js
11856
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/ref-index-store.js
11561
11857
  var import_node_fs6 = __toESM(require("fs"), 1);
11562
11858
  var DEFAULT_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
11563
11859
 
11564
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/session-store.js
11860
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/session-store.js
11565
11861
  var import_node_fs7 = __toESM(require("fs"), 1);
11566
11862
  var import_node_path7 = __toESM(require("path"), 1);
11567
11863
  var DEFAULT_EXPIRE_MS = 5 * 60 * 1e3;
11568
11864
 
11569
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/text-parsing.js
11865
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/text-parsing.js
11570
11866
  var MAX_FACE_EXT_BYTES = 64 * 1024;
11571
11867
 
11572
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/media-source.js
11868
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/media-source.js
11573
11869
  var fs14 = __toESM(require("fs"), 1);
11574
11870
 
11575
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/image-size.js
11871
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/image-size.js
11576
11872
  var import_node_buffer = require("buffer");
11577
11873
 
11578
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/ffmpeg.js
11874
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/ffmpeg.js
11579
11875
  var import_node_child_process = require("child_process");
11580
11876
  var fs15 = __toESM(require("fs"), 1);
11581
11877
  var path13 = __toESM(require("path"), 1);
11582
11878
 
11583
- // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101142/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/audio.js
11879
+ // node_modules/.pnpm/@cxyhhhhh+qqbot-nodejs@1.0.2-dev.202607101530/node_modules/@cxyhhhhh/qqbot-nodejs/dist/protocol/utils/audio.js
11584
11880
  var import_node_child_process2 = require("child_process");
11585
11881
  var fs16 = __toESM(require("fs"), 1);
11586
11882
  var path14 = __toESM(require("path"), 1);
@@ -11854,6 +12150,7 @@ async function downloadViaFetch(opts) {
11854
12150
  }
11855
12151
 
11856
12152
  // src/middleware/attachment.ts
12153
+ init_resolve();
11857
12154
  function attachmentProcessor(opts) {
11858
12155
  return async (ctx, next) => {
11859
12156
  const msg = ctx.message;
@@ -12057,6 +12354,7 @@ async function downloadMediaFile(url, filename, log4) {
12057
12354
  }
12058
12355
 
12059
12356
  // src/dispatch/body-assembler.ts
12357
+ init_resolve();
12060
12358
  var QUOTE_BEGIN = "[Quoted message begins]";
12061
12359
  var QUOTE_END = "[Quoted message ends]";
12062
12360
  var REF_BEGIN = "[Reference message begins]";
@@ -12219,7 +12517,7 @@ function isNonEmpty(x) {
12219
12517
  }
12220
12518
  function buildAgentBody(input) {
12221
12519
  const { userContent, base, isGroup, wasMentioned, history } = input;
12222
- if (userContent.startsWith("/")) {
12520
+ if (userContent.trim().startsWith("/")) {
12223
12521
  return userContent;
12224
12522
  }
12225
12523
  if (isGroup && wasMentioned && history && history.length > 0) {
@@ -12260,6 +12558,7 @@ function normalizeTimestamp(ts) {
12260
12558
  }
12261
12559
 
12262
12560
  // src/middleware/policy-injector.ts
12561
+ init_config();
12263
12562
  function createPolicyInjector(account) {
12264
12563
  return async (ctx, next) => {
12265
12564
  const msg = ctx.message;
@@ -12386,6 +12685,25 @@ async function checkPairingMode(ctx, next, opts) {
12386
12685
  }
12387
12686
  }
12388
12687
 
12688
+ // src/utils/mention.ts
12689
+ function stripMentionText(text, mentions) {
12690
+ if (!text || !mentions?.length) return text;
12691
+ let cleaned = text;
12692
+ for (const m3 of mentions) {
12693
+ const openid = m3.member_openid ?? m3.id ?? m3.user_openid;
12694
+ if (!openid) continue;
12695
+ if (m3.is_you) {
12696
+ cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, "g"), "").trim();
12697
+ } else {
12698
+ const displayName = m3.nickname ?? m3.username;
12699
+ if (displayName) {
12700
+ cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, "g"), `@${displayName}`);
12701
+ }
12702
+ }
12703
+ }
12704
+ return cleaned;
12705
+ }
12706
+
12389
12707
  // src/gateway/middleware-setup.ts
12390
12708
  function setupMiddlewares(bot, account, opts) {
12391
12709
  bot.use(errorHandler());
@@ -12404,8 +12722,13 @@ function setupMiddlewares(bot, account, opts) {
12404
12722
  getRuntime: opts.getRuntime
12405
12723
  }));
12406
12724
  bot.use(mentionGate());
12407
- bot.use(contentSanitizer({ parseFaceTags: true }));
12725
+ bot.use(contentSanitizer({
12726
+ parseFaceTags: true,
12727
+ transform: (content, ctx) => stripMentionText(content, ctx.message.mentions)
12728
+ }));
12408
12729
  bot.use(rateLimiter());
12730
+ const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });
12731
+ bot.use(slash.middleware);
12409
12732
  bot.use(concurrencyGuard({
12410
12733
  strategy: "merge",
12411
12734
  maxQueue: 50,
@@ -12438,8 +12761,6 @@ function setupMiddlewares(bot, account, opts) {
12438
12761
  return assembled.agentBody;
12439
12762
  }
12440
12763
  }));
12441
- const slash = slashCommand({ commands: buildCommandList(account, { getRuntime: opts.getRuntime }) });
12442
- bot.use(slash.middleware);
12443
12764
  }
12444
12765
 
12445
12766
  // src/dispatch/envelope-builder.ts
@@ -12908,6 +13229,7 @@ function shouldUseStreaming(account, targetScope) {
12908
13229
  }
12909
13230
 
12910
13231
  // src/dispatch/dispatch.ts
13232
+ init_resolve();
12911
13233
  async function dispatchToOpenClaw(ctx, msg, account, runtime2, log4) {
12912
13234
  const dlog = log4?.child("dispatch");
12913
13235
  const adapters = getAdapters(runtime2, dlog);
@@ -13469,6 +13791,7 @@ function getApprovalHandler(accountId) {
13469
13791
  // src/features/proactive.ts
13470
13792
  var fs19 = __toESM(require("fs"), 1);
13471
13793
  var path20 = __toESM(require("path"), 1);
13794
+ init_config();
13472
13795
  var log2 = createPluginLogger({ prefix: "[proactive]" });
13473
13796
  var STORAGE_DIR = getQQBotDataDir("data");
13474
13797
  var KNOWN_USERS_FILE = path20.join(STORAGE_DIR, "known-users.json");
@@ -13575,6 +13898,8 @@ function getCachedMsgId(scope, targetId) {
13575
13898
  }
13576
13899
 
13577
13900
  // src/gateway/event-handlers.ts
13901
+ init_resolve();
13902
+ init_config();
13578
13903
  async function handleMessage(ctx, msg, account, runtime2, log4) {
13579
13904
  const hlog = log4.child("handle");
13580
13905
  const scope = msg.replyTarget.scope;
@@ -14419,10 +14744,6 @@ var approvalStubs = {
14419
14744
  buildPendingPayload: () => null,
14420
14745
  buildResolvedPayload: () => null
14421
14746
  },
14422
- auth: {
14423
- authorizeActorAction: () => ({ authorized: true }),
14424
- getActionAvailabilityState: () => ({ kind: "enabled" })
14425
- },
14426
14747
  approvals: {
14427
14748
  delivery: {
14428
14749
  hasConfiguredDmRoute: () => true,
@@ -14436,6 +14757,7 @@ var approvalStubs = {
14436
14757
  };
14437
14758
 
14438
14759
  // src/features/onboarding.ts
14760
+ init_config();
14439
14761
  var qqbotOnboardingAdapter = {
14440
14762
  getStatus: (ctx) => {
14441
14763
  const cfg = ctx.config;
@@ -14455,25 +14777,6 @@ var qqbotOnboardingAdapter = {
14455
14777
  }
14456
14778
  };
14457
14779
 
14458
- // src/utils/mention.ts
14459
- function stripMentionText(text, mentions) {
14460
- if (!text || !mentions?.length) return text;
14461
- let cleaned = text;
14462
- for (const m3 of mentions) {
14463
- const openid = m3.member_openid ?? m3.id ?? m3.user_openid;
14464
- if (!openid) continue;
14465
- if (m3.is_you) {
14466
- cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, "g"), "").trim();
14467
- } else {
14468
- const displayName = m3.nickname ?? m3.username;
14469
- if (displayName) {
14470
- cleaned = cleaned.replace(new RegExp(`<@!?${openid}>`, "g"), `@${displayName}`);
14471
- }
14472
- }
14473
- }
14474
- return cleaned;
14475
- }
14476
-
14477
14780
  // src/channel.ts
14478
14781
  var TEXT_CHUNK_LIMIT2 = 5e3;
14479
14782
  var GFM_TABLE_DATA_RE = /^\|.+\|.*\|/;
@@ -14498,6 +14801,7 @@ var qqbotPlugin = {
14498
14801
  threads: false,
14499
14802
  blockStreaming: false
14500
14803
  },
14804
+ gatewayMethods: ["web.login.start", "web.login.wait"],
14501
14805
  reload: { configPrefixes: ["channels.qqbot"] },
14502
14806
  // ── 群消息策略 ──
14503
14807
  groups: {
@@ -14676,7 +14980,19 @@ ${line}` : line;
14676
14980
  log: ctx.log
14677
14981
  });
14678
14982
  },
14679
- logoutAccount: (params) => logoutAndClearCredentials(params)
14983
+ logoutAccount: (params) => logoutAndClearCredentials(params),
14984
+ loginWithQrStart: async ({ accountId }) => startQrLogin(accountId),
14985
+ loginWithQrWait: async ({ accountId }) => {
14986
+ const result = await waitQrLogin(accountId);
14987
+ return { connected: result.connected, message: result.message };
14988
+ }
14989
+ },
14990
+ // ── 登录认证 ──
14991
+ auth: {
14992
+ login: qqbotLogin,
14993
+ // 审批权限(从 approvalStubs 迁移)
14994
+ authorizeActorAction: () => ({ authorized: true }),
14995
+ getActionAvailabilityState: () => ({ kind: "enabled" })
14680
14996
  },
14681
14997
  // ── 状态 ──
14682
14998
  status: {
@@ -14740,6 +15056,7 @@ function createOutLog(accountId) {
14740
15056
  }
14741
15057
 
14742
15058
  // src/tools/platform.ts
15059
+ init_config();
14743
15060
  var PlatformApiSchema = {
14744
15061
  type: "object",
14745
15062
  properties: {
@@ -15049,7 +15366,7 @@ function registerRemindTool(api) {
15049
15366
  }
15050
15367
 
15051
15368
  // src/adapter/contract.ts
15052
- var log3 = createPluginLogger({ prefix: "[contract]", forceConsole: true });
15369
+ var log3 = createPluginLogger({ prefix: "[contract]", forceConsole: false });
15053
15370
  var REQUIRED = [
15054
15371
  {
15055
15372
  name: "channel.reply.dispatchReplyWithBufferedBlockDispatcher",
@@ -15109,6 +15426,7 @@ var StreamContentType2 = {
15109
15426
  };
15110
15427
 
15111
15428
  // index.ts
15429
+ init_config();
15112
15430
  var registered = false;
15113
15431
  var plugin = {
15114
15432
  id: "openclaw-qqbot",