@openclaw/qqbot 2026.5.28-beta.3 → 2026.5.28
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/api.js +4 -4
- package/dist/{channel-Cx3C1vCZ.js → channel-8Efx0wKu.js} +5 -5
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{gateway-D1g0WRCr.js → gateway-CuTCxSqs.js} +18 -93
- package/dist/{handler-runtime-yjslhmCX.js → handler-runtime-C-5nL3Cl.js} +2 -2
- package/dist/{outbound-DsXljRHh.js → outbound-Ba5ryGDm.js} +79 -35
- package/dist/{request-context-D4dkxrmN.js → request-context-BCsRtyph.js} +2 -2
- package/dist/{runtime-PZU9ND1Q.js → runtime-DWfbz21q.js} +60 -33
- package/dist/runtime-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as getMessageReplyStats, D as setOutboundAudioPort, E as OUTBOUND_ERROR_CODES, S as getMessageReplyConfig, T as DEFAULT_MEDIA_SEND_ERROR, _ as sendVideoMsg, a as sendText, b as MESSAGE_REPLY_LIMIT, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as recordMessageReply, x as checkMessageReplyLimit, y as resolveUserFacingMediaError } from "./outbound-Ba5ryGDm.js";
|
|
2
2
|
import { c as listQQBotAccountIds, l as resolveDefaultQQBotAccountId, o as DEFAULT_ACCOUNT_ID, s as applyQQBotAccountConfig, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
|
|
3
|
-
import { t as qqbotPlugin } from "./channel-
|
|
4
|
-
import { C as debugError, l as getAccessToken, w as debugLog, z as formatErrorMessage } from "./runtime-
|
|
3
|
+
import { t as qqbotPlugin } from "./channel-8Efx0wKu.js";
|
|
4
|
+
import { C as debugError, l as getAccessToken, w as debugLog, z as formatErrorMessage } from "./runtime-DWfbz21q.js";
|
|
5
5
|
import { t as qqbotSetupPlugin } from "./channel.setup-BxGpVkdx.js";
|
|
6
|
-
import { r as getFrameworkCommands, t as getRequestContext } from "./request-context-
|
|
6
|
+
import { r as getFrameworkCommands, t as getRequestContext } from "./request-context-BCsRtyph.js";
|
|
7
7
|
import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
8
8
|
//#region extensions/qqbot/src/engine/tools/channel-api.ts
|
|
9
9
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as getPlatformAdapter, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
2
|
import { a as qqbotSetupAdapterShared, c as listQQBotAccountIds, i as qqbotMeta, m as getBridgeLogger, n as qqbotSetupWizard, o as DEFAULT_ACCOUNT_ID$1, p as ensurePlatformAdapter, r as qqbotConfigAdapter, s as applyQQBotAccountConfig, t as qqbotChannelConfigSchema, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
|
|
3
|
-
import { T as debugWarn, t as getQQBotRuntime, w as debugLog, z as formatErrorMessage } from "./runtime-
|
|
3
|
+
import { T as debugWarn, t as getQQBotRuntime, w as debugLog, z as formatErrorMessage } from "./runtime-DWfbz21q.js";
|
|
4
4
|
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
|
5
5
|
import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
|
|
6
6
|
import { createChannelApprovalCapability } from "openclaw/plugin-sdk/approval-delivery-runtime";
|
|
@@ -374,7 +374,7 @@ function createQQBotApprovalCapability() {
|
|
|
374
374
|
},
|
|
375
375
|
load: async () => {
|
|
376
376
|
ensurePlatformAdapter();
|
|
377
|
-
return (await import("./handler-runtime-
|
|
377
|
+
return (await import("./handler-runtime-C-5nL3Cl.js")).qqbotApprovalNativeRuntime;
|
|
378
378
|
}
|
|
379
379
|
})
|
|
380
380
|
});
|
|
@@ -868,7 +868,7 @@ function looksLikeQQBotTarget(id) {
|
|
|
868
868
|
//#region extensions/qqbot/src/channel.ts
|
|
869
869
|
let gatewayModulePromise;
|
|
870
870
|
function loadGatewayModule() {
|
|
871
|
-
gatewayModulePromise ??= import("./gateway-
|
|
871
|
+
gatewayModulePromise ??= import("./gateway-CuTCxSqs.js");
|
|
872
872
|
return gatewayModulePromise;
|
|
873
873
|
}
|
|
874
874
|
function createQQBotSendReceipt(params) {
|
|
@@ -886,7 +886,7 @@ function createQQBotSendReceipt(params) {
|
|
|
886
886
|
async function sendQQBotText(params) {
|
|
887
887
|
await loadGatewayModule();
|
|
888
888
|
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
889
|
-
const { sendText } = await import("./outbound-
|
|
889
|
+
const { sendText } = await import("./outbound-Ba5ryGDm.js").then((n) => n.t);
|
|
890
890
|
const result = await sendText({
|
|
891
891
|
to: params.to,
|
|
892
892
|
text: params.text,
|
|
@@ -908,7 +908,7 @@ async function sendQQBotText(params) {
|
|
|
908
908
|
async function sendQQBotMedia(params) {
|
|
909
909
|
await loadGatewayModule();
|
|
910
910
|
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
911
|
-
const { sendMedia } = await import("./outbound-
|
|
911
|
+
const { sendMedia } = await import("./outbound-Ba5ryGDm.js").then((n) => n.t);
|
|
912
912
|
const result = await sendMedia({
|
|
913
913
|
to: params.to,
|
|
914
914
|
text: params.text ?? "",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-8Efx0wKu.js";
|
|
2
2
|
export { qqbotPlugin };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as setOutboundAudioPort, T as DEFAULT_MEDIA_SEND_ERROR, _ as sendVideoMsg, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, l as isMediaPayload, o as decodeMediaPath, r as sendMedia, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, y as resolveUserFacingMediaError } from "./outbound-Ba5ryGDm.js";
|
|
2
2
|
import { c as getPlatformAdapter, i as normalizeOptionalString$1, n as normalizeLowercaseStringOrEmpty$1, o as readStringField, s as sanitizeFileName, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
3
3
|
import { f as resolveAccountBase, h as setBridgeLogger, p as ensurePlatformAdapter } from "./config-schema-iX2iJzKm.js";
|
|
4
|
-
import { C as parseApprovalButtonData, a as getQQBotDataDir, d as isWindows, f as normalizePath
|
|
5
|
-
import { B as StreamContentType, C as debugError, D as getNextMsgSeq, F as getImageMimeType, H as StreamInputState, I as getMaxUploadSize, N as formatFileSize, R as formatDuration, S as withTokenRetry, T as debugWarn, V as StreamInputMode, _ as sendInputNotify, a as acknowledgeInteraction, b as startBackgroundTokenRefresh, c as createRawInputNotifyFn, d as getMessageApi, f as getPluginUserAgent, g as registerAccount, h as onMessageSent, i as accountToCreds, j as downloadFile, k as openLocalFile, l as getAccessToken, m as initSender, n as getQQBotRuntimeForEngine, o as buildDeliveryTarget, p as initApiConfig, s as clearTokenCache, t as getQQBotRuntime, u as getGatewayUrl, v as sendMedia$1, w as debugLog, x as stopBackgroundTokenRefresh, y as sendText, z as formatErrorMessage } from "./runtime-
|
|
6
|
-
import { a as getPluginVersion, i as getFrameworkVersion, n as runWithRequestContext, o as initCommands, s as matchSlashCommand } from "./request-context-
|
|
4
|
+
import { C as parseApprovalButtonData, a as getQQBotDataDir, d as isWindows, f as normalizePath, h as authorizeQQBotApprovalAction, i as getHomeDir, l as getTempDir, m as toGatewayAccount, o as getQQBotDataPath, p as resolveQQBotPayloadLocalFilePath, r as checkSilkWasmAvailable, s as getQQBotMediaDir, u as isLocalPath } from "./channel-8Efx0wKu.js";
|
|
5
|
+
import { B as StreamContentType, C as debugError, D as getNextMsgSeq, F as getImageMimeType, H as StreamInputState, I as getMaxUploadSize, N as formatFileSize, R as formatDuration, S as withTokenRetry, T as debugWarn, V as StreamInputMode, _ as sendInputNotify, a as acknowledgeInteraction, b as startBackgroundTokenRefresh, c as createRawInputNotifyFn, d as getMessageApi, f as getPluginUserAgent, g as registerAccount, h as onMessageSent, i as accountToCreds, j as downloadFile, k as openLocalFile, l as getAccessToken, m as initSender, n as getQQBotRuntimeForEngine, o as buildDeliveryTarget, p as initApiConfig, s as clearTokenCache, t as getQQBotRuntime, u as getGatewayUrl, v as sendMedia$1, w as debugLog, x as stopBackgroundTokenRefresh, y as sendText, z as formatErrorMessage } from "./runtime-DWfbz21q.js";
|
|
6
|
+
import { a as getPluginVersion, i as getFrameworkVersion, n as runWithRequestContext, o as initCommands, s as matchSlashCommand } from "./request-context-BCsRtyph.js";
|
|
7
7
|
import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
8
8
|
import { isImplicitSameChatApprovalAuthorization } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
9
9
|
import * as fs$1 from "node:fs";
|
|
@@ -15,6 +15,7 @@ import { appendRegularFileSync, privateFileStoreSync, readRegularFileSync, repla
|
|
|
15
15
|
import * as path$1 from "node:path";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
18
|
+
import { MAX_TIMER_TIMEOUT_MS, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
18
19
|
import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
|
|
19
20
|
import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
20
21
|
import WebSocket from "ws";
|
|
@@ -3465,64 +3466,6 @@ function hasQQBotImageSize(markdownImage) {
|
|
|
3465
3466
|
return /!\[#\d+px\s+#\d+px\]/.test(markdownImage);
|
|
3466
3467
|
}
|
|
3467
3468
|
//#endregion
|
|
3468
|
-
//#region extensions/qqbot/src/engine/messaging/decode-media-path.ts
|
|
3469
|
-
/**
|
|
3470
|
-
* Normalize a file path by expanding `~` to the home directory and trimming.
|
|
3471
|
-
*
|
|
3472
|
-
* This is a minimal re-implementation of `utils/platform.ts#normalizePath`
|
|
3473
|
-
* so that `core/` remains self-contained.
|
|
3474
|
-
*/
|
|
3475
|
-
function normalizePath(p) {
|
|
3476
|
-
let result = p.trim();
|
|
3477
|
-
if (result.startsWith("~/") || result === "~") {
|
|
3478
|
-
const home = typeof process !== "undefined" ? process.env.HOME ?? process.env.USERPROFILE : void 0;
|
|
3479
|
-
if (home) result = result === "~" ? home : `${home}${result.slice(1)}`;
|
|
3480
|
-
}
|
|
3481
|
-
return result;
|
|
3482
|
-
}
|
|
3483
|
-
/**
|
|
3484
|
-
* Decode a media path by stripping `MEDIA:`, expanding `~`, and unescaping
|
|
3485
|
-
* octal/UTF-8 byte sequences.
|
|
3486
|
-
*
|
|
3487
|
-
* @param raw - Raw path string from a media tag.
|
|
3488
|
-
* @param log - Optional logger for decode diagnostics.
|
|
3489
|
-
* @returns The decoded, normalized media path.
|
|
3490
|
-
*/
|
|
3491
|
-
function decodeMediaPath(raw, log) {
|
|
3492
|
-
let mediaPath = raw;
|
|
3493
|
-
if (mediaPath.startsWith("MEDIA:")) mediaPath = mediaPath.slice(6);
|
|
3494
|
-
mediaPath = normalizePath(mediaPath);
|
|
3495
|
-
mediaPath = mediaPath.replace(/\\\\/g, "\\");
|
|
3496
|
-
const isWinLocal = /^[a-zA-Z]:[\\/]/.test(mediaPath) || mediaPath.startsWith("\\\\");
|
|
3497
|
-
try {
|
|
3498
|
-
const hasOctal = /\\[0-7]{1,3}/.test(mediaPath);
|
|
3499
|
-
const hasNonASCII = /[\u0080-\u00FF]/.test(mediaPath);
|
|
3500
|
-
if (!isWinLocal && (hasOctal || hasNonASCII)) {
|
|
3501
|
-
log?.debug?.(`Decoding path with mixed encoding: ${mediaPath}`);
|
|
3502
|
-
const decoded = mediaPath.replace(/\\([0-7]{1,3})/g, (_, octal) => {
|
|
3503
|
-
return String.fromCharCode(Number.parseInt(octal, 8));
|
|
3504
|
-
});
|
|
3505
|
-
const bytes = [];
|
|
3506
|
-
for (let i = 0; i < decoded.length; i++) {
|
|
3507
|
-
const code = decoded.charCodeAt(i);
|
|
3508
|
-
if (code <= 255) bytes.push(code);
|
|
3509
|
-
else {
|
|
3510
|
-
const charBytes = Buffer.from(decoded[i], "utf8");
|
|
3511
|
-
bytes.push(...charBytes);
|
|
3512
|
-
}
|
|
3513
|
-
}
|
|
3514
|
-
const utf8Decoded = Buffer.from(bytes).toString("utf8");
|
|
3515
|
-
if (!utf8Decoded.includes("�") || utf8Decoded.length < decoded.length) {
|
|
3516
|
-
mediaPath = utf8Decoded;
|
|
3517
|
-
log?.debug?.(`Successfully decoded path: ${mediaPath}`);
|
|
3518
|
-
}
|
|
3519
|
-
}
|
|
3520
|
-
} catch (decodeErr) {
|
|
3521
|
-
log?.error(`Path decode error: ${String(decodeErr)}`);
|
|
3522
|
-
}
|
|
3523
|
-
return mediaPath;
|
|
3524
|
-
}
|
|
3525
|
-
//#endregion
|
|
3526
3469
|
//#region extensions/qqbot/src/engine/messaging/outbound-deliver.ts
|
|
3527
3470
|
/** Maximum text length for a single QQ Bot message. */
|
|
3528
3471
|
const TEXT_CHUNK_LIMIT = 5e3;
|
|
@@ -4018,7 +3961,7 @@ function isInlineImageDataUrl(p) {
|
|
|
4018
3961
|
}
|
|
4019
3962
|
function resolveStructuredPayloadPath(ctx, payload, mediaType) {
|
|
4020
3963
|
const originalPath = payload.path ?? "";
|
|
4021
|
-
const normalizedPath = normalizePath
|
|
3964
|
+
const normalizedPath = normalizePath(originalPath);
|
|
4022
3965
|
const isHttpUrl = isRemoteHttpUrl(normalizedPath);
|
|
4023
3966
|
const resolvedPath = isHttpUrl ? normalizedPath : validateStructuredPayloadLocalPath(ctx, originalPath, mediaType);
|
|
4024
3967
|
if (!resolvedPath) return null;
|
|
@@ -4080,7 +4023,7 @@ async function assertLocalFileWithinTypeLimit(filePath, fileType) {
|
|
|
4080
4023
|
}
|
|
4081
4024
|
async function handleImagePayload(ctx, payload) {
|
|
4082
4025
|
const { target, account, log } = ctx;
|
|
4083
|
-
const normalizedPath = normalizePath
|
|
4026
|
+
const normalizedPath = normalizePath(payload.path);
|
|
4084
4027
|
let imageUrl;
|
|
4085
4028
|
if (payload.source === "file") imageUrl = validateStructuredPayloadLocalPath(ctx, normalizedPath, "image");
|
|
4086
4029
|
else if (isRemoteHttpUrl(normalizedPath) || isInlineImageDataUrl(normalizedPath)) imageUrl = normalizedPath;
|
|
@@ -4368,7 +4311,7 @@ function findFirstClosedMediaTag(text, log) {
|
|
|
4368
4311
|
const tagName = match[1].toLowerCase();
|
|
4369
4312
|
let mediaPath = match[2]?.trim() ?? "";
|
|
4370
4313
|
if (mediaPath.startsWith("MEDIA:")) mediaPath = mediaPath.slice(6);
|
|
4371
|
-
mediaPath = normalizePath
|
|
4314
|
+
mediaPath = normalizePath(mediaPath);
|
|
4372
4315
|
mediaPath = fixPathEncoding(mediaPath, log);
|
|
4373
4316
|
return {
|
|
4374
4317
|
textBefore,
|
|
@@ -5676,7 +5619,7 @@ function parseWavFallback(buf) {
|
|
|
5676
5619
|
* already configured:
|
|
5677
5620
|
* - `agents.defaults.timeoutSeconds`
|
|
5678
5621
|
* - `models.providers.<id>.timeoutSeconds` (max across configured providers)
|
|
5679
|
-
* Take the maximum and clamp to `[DEFAULT_RESPONSE_TIMEOUT_MS,
|
|
5622
|
+
* Take the maximum and clamp to `[DEFAULT_RESPONSE_TIMEOUT_MS, MAX_TIMER_TIMEOUT_MS]`.
|
|
5680
5623
|
* The default floor preserves the existing 5-minute guard for users
|
|
5681
5624
|
* that have not configured any longer ceiling — i.e. a no-op for
|
|
5682
5625
|
* typical cloud-model deployments.
|
|
@@ -5688,16 +5631,6 @@ function parseWavFallback(buf) {
|
|
|
5688
5631
|
*/
|
|
5689
5632
|
const DEFAULT_RESPONSE_TIMEOUT_MS = 3e5;
|
|
5690
5633
|
/**
|
|
5691
|
-
* Upper bound to keep the watchdog inside the safe `setTimeout` range
|
|
5692
|
-
* (approximately 24.8 days). Mirrors `MAX_SAFE_TIMEOUT_MS` in
|
|
5693
|
-
* `src/agents/embedded-agent-runner/run/llm-idle-timeout.ts`.
|
|
5694
|
-
*/
|
|
5695
|
-
const MAX_SAFE_TIMEOUT_MS = 2147e6;
|
|
5696
|
-
function positiveSecondsToMs(value) {
|
|
5697
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return;
|
|
5698
|
-
return Math.floor(value * 1e3);
|
|
5699
|
-
}
|
|
5700
|
-
/**
|
|
5701
5634
|
* Resolve the QQBot outbound response watchdog (ms).
|
|
5702
5635
|
*
|
|
5703
5636
|
* The watchdog is the longest of:
|
|
@@ -5706,21 +5639,21 @@ function positiveSecondsToMs(value) {
|
|
|
5706
5639
|
* - the maximum `cfg.models.providers.<id>.timeoutSeconds` across
|
|
5707
5640
|
* configured providers, converted to ms
|
|
5708
5641
|
*
|
|
5709
|
-
* Returns at most `
|
|
5642
|
+
* Returns at most `MAX_TIMER_TIMEOUT_MS` so the chosen value is always
|
|
5710
5643
|
* a safe `setTimeout` argument.
|
|
5711
5644
|
*/
|
|
5712
5645
|
function resolveResponseTimeoutMs(cfg) {
|
|
5713
5646
|
const candidates = [DEFAULT_RESPONSE_TIMEOUT_MS];
|
|
5714
5647
|
const typed = cfg ?? {};
|
|
5715
|
-
const agentDefaultMs =
|
|
5648
|
+
const agentDefaultMs = finiteSecondsToTimerSafeMilliseconds(typed.agents?.defaults?.timeoutSeconds);
|
|
5716
5649
|
if (agentDefaultMs !== void 0) candidates.push(agentDefaultMs);
|
|
5717
5650
|
const providers = typed.models?.providers;
|
|
5718
5651
|
if (providers && typeof providers === "object") for (const entry of Object.values(providers)) {
|
|
5719
|
-
const providerMs =
|
|
5652
|
+
const providerMs = finiteSecondsToTimerSafeMilliseconds(entry?.timeoutSeconds);
|
|
5720
5653
|
if (providerMs !== void 0) candidates.push(providerMs);
|
|
5721
5654
|
}
|
|
5722
5655
|
const chosen = Math.max(...candidates);
|
|
5723
|
-
return Math.min(chosen,
|
|
5656
|
+
return Math.min(chosen, MAX_TIMER_TIMEOUT_MS);
|
|
5724
5657
|
}
|
|
5725
5658
|
//#endregion
|
|
5726
5659
|
//#region extensions/qqbot/src/engine/gateway/outbound-dispatch.ts
|
|
@@ -6341,7 +6274,7 @@ async function startTypingForEvent(event, account, log) {
|
|
|
6341
6274
|
try {
|
|
6342
6275
|
const creds = accountToCreds(account);
|
|
6343
6276
|
const rawNotifyFn = createRawInputNotifyFn(account.appId);
|
|
6344
|
-
|
|
6277
|
+
const sendNotifyAndStartKeepAlive = async () => {
|
|
6345
6278
|
const resp = await sendInputNotify({
|
|
6346
6279
|
openid: event.senderId,
|
|
6347
6280
|
creds,
|
|
@@ -6354,22 +6287,14 @@ async function startTypingForEvent(event, account, log) {
|
|
|
6354
6287
|
refIdx: resp.refIdx,
|
|
6355
6288
|
keepAlive
|
|
6356
6289
|
};
|
|
6290
|
+
};
|
|
6291
|
+
try {
|
|
6292
|
+
return await sendNotifyAndStartKeepAlive();
|
|
6357
6293
|
} catch (notifyErr) {
|
|
6358
6294
|
const errMsg = String(notifyErr);
|
|
6359
6295
|
if (errMsg.includes("token") || errMsg.includes("401") || errMsg.includes("11244")) {
|
|
6360
6296
|
clearTokenCache(account.appId);
|
|
6361
|
-
|
|
6362
|
-
openid: event.senderId,
|
|
6363
|
-
creds,
|
|
6364
|
-
msgId: event.messageId,
|
|
6365
|
-
inputSecond: 10
|
|
6366
|
-
});
|
|
6367
|
-
const keepAlive = new TypingKeepAlive(() => getAccessToken(account.appId, account.clientSecret), () => clearTokenCache(account.appId), rawNotifyFn, event.senderId, event.messageId, log);
|
|
6368
|
-
keepAlive.start();
|
|
6369
|
-
return {
|
|
6370
|
-
refIdx: resp.refIdx,
|
|
6371
|
-
keepAlive
|
|
6372
|
-
};
|
|
6297
|
+
return await sendNotifyAndStartKeepAlive();
|
|
6373
6298
|
}
|
|
6374
6299
|
throw notifyErr;
|
|
6375
6300
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { m as getBridgeLogger, p as ensurePlatformAdapter, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
|
|
2
|
-
import { S as buildPluginApprovalText, _ as matchesQQBotApprovalAccount, b as buildApprovalKeyboard, g as isQQBotExecApprovalClientEnabled, v as resolveQQBotExecApprovalConfig, w as resolveApprovalTarget, x as buildExecApprovalText, y as shouldHandleQQBotExecApprovalRequest } from "./channel-
|
|
3
|
-
import { d as getMessageApi, i as accountToCreds } from "./runtime-
|
|
2
|
+
import { S as buildPluginApprovalText, _ as matchesQQBotApprovalAccount, b as buildApprovalKeyboard, g as isQQBotExecApprovalClientEnabled, v as resolveQQBotExecApprovalConfig, w as resolveApprovalTarget, x as buildExecApprovalText, y as shouldHandleQQBotExecApprovalRequest } from "./channel-8Efx0wKu.js";
|
|
3
|
+
import { d as getMessageApi, i as accountToCreds } from "./runtime-DWfbz21q.js";
|
|
4
4
|
import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
5
5
|
import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
6
6
|
//#region extensions/qqbot/src/bridge/approval/handler-runtime.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
2
|
-
import { a as getQQBotDataDir, f as normalizePath, n as parseTarget$1, p as resolveQQBotPayloadLocalFilePath, s as getQQBotMediaDir, u as isLocalPath } from "./channel-
|
|
3
|
-
import { A as checkFileSize, C as debugError, E as UploadDailyLimitExceededError, F as getImageMimeType, I as getMaxUploadSize, L as readFileAsync, M as fileExistsAsync, N as formatFileSize, O as UPLOAD_PREPARE_FALLBACK_CODE, P as getFileTypeName, T as debugWarn, i as accountToCreds, j as downloadFile, p as initApiConfig, v as sendMedia$1, w as debugLog, y as sendText$1, z as formatErrorMessage } from "./runtime-
|
|
2
|
+
import { a as getQQBotDataDir, f as normalizePath$1, n as parseTarget$1, p as resolveQQBotPayloadLocalFilePath, s as getQQBotMediaDir, u as isLocalPath } from "./channel-8Efx0wKu.js";
|
|
3
|
+
import { A as checkFileSize, C as debugError, E as UploadDailyLimitExceededError, F as getImageMimeType, I as getMaxUploadSize, L as readFileAsync, M as fileExistsAsync, N as formatFileSize, O as UPLOAD_PREPARE_FALLBACK_CODE, P as getFileTypeName, T as debugWarn, i as accountToCreds, j as downloadFile, p as initApiConfig, v as sendMedia$1, w as debugLog, y as sendText$1, z as formatErrorMessage } from "./runtime-DWfbz21q.js";
|
|
4
4
|
import { pathExistsSync, resolveLocalPathFromRootsSync } from "openclaw/plugin-sdk/security-runtime";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { getFileExtension } from "openclaw/plugin-sdk/media-mime";
|
|
@@ -258,7 +258,7 @@ function resolveExistingPathWithinRoots(normalizedPath, allowedRoots) {
|
|
|
258
258
|
})?.path ?? null;
|
|
259
259
|
}
|
|
260
260
|
function resolveOutboundMediaPath(rawPath, mediaKind, options = {}) {
|
|
261
|
-
const normalizedPath = normalizePath(rawPath);
|
|
261
|
+
const normalizedPath = normalizePath$1(rawPath);
|
|
262
262
|
if (isHttpOrDataSource(normalizedPath)) return {
|
|
263
263
|
ok: true,
|
|
264
264
|
mediaPath: normalizedPath
|
|
@@ -970,6 +970,75 @@ function isMediaPayload(payload) {
|
|
|
970
970
|
return payload.type === "media";
|
|
971
971
|
}
|
|
972
972
|
//#endregion
|
|
973
|
+
//#region extensions/qqbot/src/engine/messaging/decode-media-path.ts
|
|
974
|
+
function getHomeForTildePath(windowsStyle) {
|
|
975
|
+
if (windowsStyle && process.env.USERPROFILE) return process.env.USERPROFILE;
|
|
976
|
+
return process.env.HOME ?? process.env.USERPROFILE;
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Normalize a file path by expanding `~` to the home directory and trimming.
|
|
980
|
+
*
|
|
981
|
+
* This is a minimal re-implementation of `utils/platform.ts#normalizePath`
|
|
982
|
+
* so that `core/` remains self-contained.
|
|
983
|
+
*/
|
|
984
|
+
function normalizePath(p) {
|
|
985
|
+
let result = p.trim();
|
|
986
|
+
if (result.startsWith("file://")) {
|
|
987
|
+
result = result.slice(7);
|
|
988
|
+
try {
|
|
989
|
+
result = decodeURIComponent(result);
|
|
990
|
+
} catch {}
|
|
991
|
+
}
|
|
992
|
+
const windowsStyleHomePath = result.startsWith("~\\");
|
|
993
|
+
if (result === "~" || result.startsWith("~/") || windowsStyleHomePath) {
|
|
994
|
+
const home = typeof process !== "undefined" ? getHomeForTildePath(windowsStyleHomePath) : void 0;
|
|
995
|
+
if (home) result = result === "~" ? home : `${home}${result.slice(1)}`;
|
|
996
|
+
}
|
|
997
|
+
return result;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Decode a media path by stripping `MEDIA:`, expanding `~`, and unescaping
|
|
1001
|
+
* octal/UTF-8 byte sequences.
|
|
1002
|
+
*
|
|
1003
|
+
* @param raw - Raw path string from a media tag.
|
|
1004
|
+
* @param log - Optional logger for decode diagnostics.
|
|
1005
|
+
* @returns The decoded, normalized media path.
|
|
1006
|
+
*/
|
|
1007
|
+
function decodeMediaPath(raw, log) {
|
|
1008
|
+
let mediaPath = raw;
|
|
1009
|
+
if (mediaPath.startsWith("MEDIA:")) mediaPath = mediaPath.slice(6);
|
|
1010
|
+
mediaPath = normalizePath(mediaPath);
|
|
1011
|
+
mediaPath = mediaPath.replace(/\\\\/g, "\\");
|
|
1012
|
+
const isWinLocal = /^[a-zA-Z]:[\\/]/.test(mediaPath) || mediaPath.startsWith("\\\\");
|
|
1013
|
+
try {
|
|
1014
|
+
const hasOctal = /\\[0-7]{1,3}/.test(mediaPath);
|
|
1015
|
+
const hasNonASCII = /[\u0080-\u00FF]/.test(mediaPath);
|
|
1016
|
+
if (!isWinLocal && (hasOctal || hasNonASCII)) {
|
|
1017
|
+
log?.debug?.(`Decoding path with mixed encoding: ${mediaPath}`);
|
|
1018
|
+
const decoded = mediaPath.replace(/\\([0-7]{1,3})/g, (_, octal) => {
|
|
1019
|
+
return String.fromCharCode(Number.parseInt(octal, 8));
|
|
1020
|
+
});
|
|
1021
|
+
const bytes = [];
|
|
1022
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
1023
|
+
const code = decoded.charCodeAt(i);
|
|
1024
|
+
if (code <= 255) bytes.push(code);
|
|
1025
|
+
else {
|
|
1026
|
+
const charBytes = Buffer.from(decoded[i], "utf8");
|
|
1027
|
+
bytes.push(...charBytes);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
const utf8Decoded = Buffer.from(bytes).toString("utf8");
|
|
1031
|
+
if (!utf8Decoded.includes("�") || utf8Decoded.length < decoded.length) {
|
|
1032
|
+
mediaPath = utf8Decoded;
|
|
1033
|
+
log?.debug?.(`Successfully decoded path: ${mediaPath}`);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
} catch (decodeErr) {
|
|
1037
|
+
log?.error(`Path decode error: ${String(decodeErr)}`);
|
|
1038
|
+
}
|
|
1039
|
+
return mediaPath;
|
|
1040
|
+
}
|
|
1041
|
+
//#endregion
|
|
973
1042
|
//#region extensions/qqbot/src/engine/messaging/media-type-detect.ts
|
|
974
1043
|
/**
|
|
975
1044
|
* Media type detection — pure functions for classifying files by MIME or extension.
|
|
@@ -1016,6 +1085,11 @@ var outbound_exports = /* @__PURE__ */ __exportAll({
|
|
|
1016
1085
|
});
|
|
1017
1086
|
const isImageFile = isImageFile$1;
|
|
1018
1087
|
const isVideoFile = isVideoFile$1;
|
|
1088
|
+
const mediaPathDecodeLog = {
|
|
1089
|
+
info: (message) => debugLog(`[qqbot] sendText: ${message}`),
|
|
1090
|
+
error: (message) => debugError(`[qqbot] sendText: ${message}`),
|
|
1091
|
+
debug: (message) => debugLog(`[qqbot] sendText: ${message}`)
|
|
1092
|
+
};
|
|
1019
1093
|
/**
|
|
1020
1094
|
* Send text, optionally falling back from passive reply mode to proactive mode.
|
|
1021
1095
|
*
|
|
@@ -1062,37 +1136,7 @@ async function sendText(ctx) {
|
|
|
1062
1136
|
content: textBefore
|
|
1063
1137
|
});
|
|
1064
1138
|
const tagName = normalizeLowercaseStringOrEmpty(match[1]);
|
|
1065
|
-
|
|
1066
|
-
if (mediaPath.startsWith("MEDIA:")) mediaPath = mediaPath.slice(6);
|
|
1067
|
-
mediaPath = normalizePath(mediaPath);
|
|
1068
|
-
mediaPath = mediaPath.replace(/\\\\/g, "\\");
|
|
1069
|
-
const isWinLocal = /^[a-zA-Z]:[\\/]/.test(mediaPath) || mediaPath.startsWith("\\\\");
|
|
1070
|
-
try {
|
|
1071
|
-
const hasOctal = /\\[0-7]{1,3}/.test(mediaPath);
|
|
1072
|
-
const hasNonASCII = /[\u0080-\u00FF]/.test(mediaPath);
|
|
1073
|
-
if (!isWinLocal && (hasOctal || hasNonASCII)) {
|
|
1074
|
-
debugLog(`[qqbot] sendText: Decoding path with mixed encoding: ${mediaPath}`);
|
|
1075
|
-
let decoded = mediaPath.replace(/\\([0-7]{1,3})/g, (_, octal) => {
|
|
1076
|
-
return String.fromCharCode(Number.parseInt(octal, 8));
|
|
1077
|
-
});
|
|
1078
|
-
const bytes = [];
|
|
1079
|
-
for (let i = 0; i < decoded.length; i++) {
|
|
1080
|
-
const code = decoded.charCodeAt(i);
|
|
1081
|
-
if (code <= 255) bytes.push(code);
|
|
1082
|
-
else {
|
|
1083
|
-
const charBytes = Buffer.from(decoded[i], "utf8");
|
|
1084
|
-
bytes.push(...charBytes);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
const utf8Decoded = Buffer.from(bytes).toString("utf8");
|
|
1088
|
-
if (!utf8Decoded.includes("�") || utf8Decoded.length < decoded.length) {
|
|
1089
|
-
mediaPath = utf8Decoded;
|
|
1090
|
-
debugLog(`[qqbot] sendText: Successfully decoded path: ${mediaPath}`);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
} catch (decodeErr) {
|
|
1094
|
-
debugError(`[qqbot] sendText: Path decode error: ${decodeErr instanceof Error ? decodeErr.message : JSON.stringify(decodeErr)}`);
|
|
1095
|
-
}
|
|
1139
|
+
const mediaPath = decodeMediaPath(normalizeOptionalString(match[2]) ?? "", mediaPathDecodeLog);
|
|
1096
1140
|
if (mediaPath) if (tagName === "qqmedia") {
|
|
1097
1141
|
sendQueue.push({
|
|
1098
1142
|
type: "media",
|
|
@@ -1322,4 +1366,4 @@ async function sendCronMessage(account, to, message) {
|
|
|
1322
1366
|
});
|
|
1323
1367
|
}
|
|
1324
1368
|
//#endregion
|
|
1325
|
-
export {
|
|
1369
|
+
export { getMessageReplyStats as C, setOutboundAudioPort as D, OUTBOUND_ERROR_CODES as E, getMessageReplyConfig as S, DEFAULT_MEDIA_SEND_ERROR as T, sendVideoMsg as _, sendText as a, MESSAGE_REPLY_LIMIT as b, isCronReminderPayload as c, normalizeMediaTags as d, buildMediaTarget as f, sendPhoto as g, sendDocument as h, sendProactiveMessage as i, isMediaPayload as l, resolveOutboundMediaPath as m, sendCronMessage as n, decodeMediaPath as o, parseTarget as p, sendMedia as r, encodePayloadForCron as s, outbound_exports as t, parseQQBotPayload as u, sendVoice as v, recordMessageReply as w, checkMessageReplyLimit as x, resolveUserFacingMediaError as y };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as getQQBotDataDir, c as getQQBotMediaPath, d as isWindows, i as getHomeDir } from "./channel-
|
|
2
|
-
import { w as debugLog } from "./runtime-
|
|
1
|
+
import { a as getQQBotDataDir, c as getQQBotMediaPath, d as isWindows, i as getHomeDir } from "./channel-8Efx0wKu.js";
|
|
2
|
+
import { w as debugLog } from "./runtime-DWfbz21q.js";
|
|
3
3
|
import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
@@ -9,6 +9,7 @@ import { FsSafeError, openLocalFileSafely, readRegularFile, statRegularFileSync
|
|
|
9
9
|
import * as path$1 from "node:path";
|
|
10
10
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
11
11
|
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
12
|
+
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
12
13
|
//#region extensions/qqbot/src/engine/types.ts
|
|
13
14
|
/**
|
|
14
15
|
* Core API layer public types.
|
|
@@ -988,7 +989,21 @@ function sleep(ms) {
|
|
|
988
989
|
}
|
|
989
990
|
//#endregion
|
|
990
991
|
//#region extensions/qqbot/src/engine/api/token.ts
|
|
992
|
+
/**
|
|
993
|
+
* Token management for the QQ Open Platform.
|
|
994
|
+
*
|
|
995
|
+
* All state (cache, singleflight promises, background refresh controllers)
|
|
996
|
+
* is encapsulated in the `TokenManager` class instance — no module-level
|
|
997
|
+
* globals, fully supporting multi-account concurrent operation.
|
|
998
|
+
*/
|
|
991
999
|
const TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
|
|
1000
|
+
const DEFAULT_TOKEN_EXPIRES_IN_SECONDS = 7200;
|
|
1001
|
+
function resolveTokenExpiresInSeconds(value) {
|
|
1002
|
+
const parsed = parseStrictPositiveInteger(value);
|
|
1003
|
+
if (parsed !== void 0) return parsed;
|
|
1004
|
+
if (value == null || typeof value === "number" && !Number.isFinite(value)) return DEFAULT_TOKEN_EXPIRES_IN_SECONDS;
|
|
1005
|
+
return 0;
|
|
1006
|
+
}
|
|
992
1007
|
/**
|
|
993
1008
|
* Per-appId token manager with caching, singleflight, and background refresh.
|
|
994
1009
|
*
|
|
@@ -1116,47 +1131,59 @@ var TokenManager = class {
|
|
|
1116
1131
|
async doFetchToken(appId, clientSecret) {
|
|
1117
1132
|
this.logger?.debug?.(`[qqbot:token:${appId}] >>> POST ${TOKEN_URL}`);
|
|
1118
1133
|
let response;
|
|
1134
|
+
let release;
|
|
1119
1135
|
try {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1136
|
+
const guarded = await fetchWithSsrFGuard({
|
|
1137
|
+
url: TOKEN_URL,
|
|
1138
|
+
auditContext: "qqbot-token",
|
|
1139
|
+
capture: false,
|
|
1140
|
+
init: {
|
|
1141
|
+
method: "POST",
|
|
1142
|
+
headers: {
|
|
1143
|
+
"Content-Type": "application/json",
|
|
1144
|
+
"User-Agent": this.resolveUserAgent()
|
|
1145
|
+
},
|
|
1146
|
+
body: JSON.stringify({
|
|
1147
|
+
appId,
|
|
1148
|
+
clientSecret
|
|
1149
|
+
})
|
|
1150
|
+
}
|
|
1130
1151
|
});
|
|
1152
|
+
response = guarded.response;
|
|
1153
|
+
release = guarded.release;
|
|
1131
1154
|
} catch (err) {
|
|
1132
1155
|
this.logger?.error?.(`[qqbot:token:${appId}] Network error: ${formatErrorMessage(err)}`);
|
|
1133
1156
|
throw new Error(`Network error getting access_token: ${formatErrorMessage(err)}`, { cause: err });
|
|
1134
1157
|
}
|
|
1135
|
-
const traceId = response.headers.get("x-tps-trace-id") ?? "";
|
|
1136
|
-
this.logger?.debug?.(`[qqbot:token:${appId}] <<< ${response.status}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
1137
|
-
let rawBody;
|
|
1138
|
-
try {
|
|
1139
|
-
rawBody = await response.text();
|
|
1140
|
-
} catch (err) {
|
|
1141
|
-
throw new Error(`Failed to read access_token response: ${formatErrorMessage(err)}`, { cause: err });
|
|
1142
|
-
}
|
|
1143
|
-
const logBody = rawBody.replace(/"access_token"\s*:\s*"[^"]+"/g, "\"access_token\": \"***\"");
|
|
1144
|
-
this.logger?.debug?.(`[qqbot:token:${appId}] <<< Body: ${logBody}`);
|
|
1145
|
-
let data;
|
|
1146
1158
|
try {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1159
|
+
const traceId = response.headers.get("x-tps-trace-id") ?? "";
|
|
1160
|
+
this.logger?.debug?.(`[qqbot:token:${appId}] <<< ${response.status}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
1161
|
+
let rawBody;
|
|
1162
|
+
try {
|
|
1163
|
+
rawBody = await response.text();
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
throw new Error(`Failed to read access_token response: ${formatErrorMessage(err)}`, { cause: err });
|
|
1166
|
+
}
|
|
1167
|
+
const logBody = rawBody.replace(/"access_token"\s*:\s*"[^"]+"/g, "\"access_token\": \"***\"");
|
|
1168
|
+
this.logger?.debug?.(`[qqbot:token:${appId}] <<< Body: ${logBody}`);
|
|
1169
|
+
let data;
|
|
1170
|
+
try {
|
|
1171
|
+
data = JSON.parse(rawBody);
|
|
1172
|
+
} catch {
|
|
1173
|
+
throw new Error("QQBot access_token response was malformed JSON");
|
|
1174
|
+
}
|
|
1175
|
+
if (!data.access_token) throw new Error(`Failed to get access_token: ${JSON.stringify(data)}`);
|
|
1176
|
+
const expiresAt = Date.now() + resolveTokenExpiresInSeconds(data.expires_in) * 1e3;
|
|
1177
|
+
this.cache.set(appId, {
|
|
1178
|
+
token: data.access_token,
|
|
1179
|
+
expiresAt,
|
|
1180
|
+
appId
|
|
1181
|
+
});
|
|
1182
|
+
this.logger?.debug?.(`[qqbot:token:${appId}] Cached, expires at: ${new Date(expiresAt).toISOString()}`);
|
|
1183
|
+
return data.access_token;
|
|
1184
|
+
} finally {
|
|
1185
|
+
await release?.();
|
|
1150
1186
|
}
|
|
1151
|
-
if (!data.access_token) throw new Error(`Failed to get access_token: ${JSON.stringify(data)}`);
|
|
1152
|
-
const expiresAt = Date.now() + (data.expires_in ?? 7200) * 1e3;
|
|
1153
|
-
this.cache.set(appId, {
|
|
1154
|
-
token: data.access_token,
|
|
1155
|
-
expiresAt,
|
|
1156
|
-
appId
|
|
1157
|
-
});
|
|
1158
|
-
this.logger?.debug?.(`[qqbot:token:${appId}] Cached, expires at: ${new Date(expiresAt).toISOString()}`);
|
|
1159
|
-
return data.access_token;
|
|
1160
1187
|
}
|
|
1161
1188
|
abortableSleep(ms, signal) {
|
|
1162
1189
|
return new Promise((resolve, reject) => {
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-
|
|
1
|
+
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-DWfbz21q.js";
|
|
2
2
|
export { getQQBotRuntime, setQQBotRuntime };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.5.28
|
|
3
|
+
"version": "2026.5.28",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/qqbot",
|
|
9
|
-
"version": "2026.5.28
|
|
9
|
+
"version": "2026.5.28",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@tencent-connect/qqbot-connector": "1.1.0",
|
|
12
12
|
"mpg123-decoder": "1.0.3",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zod": "4.4.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.5.28
|
|
18
|
+
"openclaw": ">=2026.5.28"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.5.28
|
|
3
|
+
"version": "2026.5.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"zod": "4.4.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.5.28
|
|
19
|
+
"openclaw": ">=2026.5.28"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"minHostVersion": ">=2026.4.10"
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.5.28
|
|
48
|
+
"pluginApi": ">=2026.5.28"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.5.28
|
|
51
|
+
"openclawVersion": "2026.5.28"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|