@openclaw/qqbot 2026.5.27 → 2026.5.28-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +10 -7
- package/dist/{channel-C_7BS_5A.js → channel-Cx3C1vCZ.js} +6 -13
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{gateway-BdWwKDtj.js → gateway-D1g0WRCr.js} +6 -6
- package/dist/{handler-runtime-CSVOet7I.js → handler-runtime-yjslhmCX.js} +5 -9
- package/dist/{outbound-DHc9KJCp.js → outbound-DsXljRHh.js} +2 -2
- package/dist/{request-context-Dge7ttkg.js → request-context-D4dkxrmN.js} +2 -2
- package/dist/{runtime-TzkQ0YbR.js → runtime-PZU9ND1Q.js} +109 -15
- 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 recordMessageReply, E as setOutboundAudioPort, S as getMessageReplyStats, T as OUTBOUND_ERROR_CODES, _ as sendVoice, a as sendText, b as checkMessageReplyLimit, d as buildMediaTarget, f as parseTarget, g as sendVideoMsg, h as sendPhoto, i as sendProactiveMessage, m as sendDocument, n as sendCronMessage, p as resolveOutboundMediaPath, r as sendMedia, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR, x as getMessageReplyConfig, y as MESSAGE_REPLY_LIMIT } from "./outbound-
|
|
1
|
+
import { C as recordMessageReply, E as setOutboundAudioPort, S as getMessageReplyStats, T as OUTBOUND_ERROR_CODES, _ as sendVoice, a as sendText, b as checkMessageReplyLimit, d as buildMediaTarget, f as parseTarget, g as sendVideoMsg, h as sendPhoto, i as sendProactiveMessage, m as sendDocument, n as sendCronMessage, p as resolveOutboundMediaPath, r as sendMedia, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR, x as getMessageReplyConfig, y as MESSAGE_REPLY_LIMIT } from "./outbound-DsXljRHh.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-Cx3C1vCZ.js";
|
|
4
|
+
import { C as debugError, l as getAccessToken, w as debugLog, z as formatErrorMessage } from "./runtime-PZU9ND1Q.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-D4dkxrmN.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
|
/**
|
|
@@ -269,14 +269,17 @@ const RemindSchema = {
|
|
|
269
269
|
* @returns Milliseconds or null if unparseable.
|
|
270
270
|
*/
|
|
271
271
|
function parseRelativeTime(timeStr) {
|
|
272
|
-
const s = timeStr.toLowerCase();
|
|
272
|
+
const s = timeStr.trim().toLowerCase();
|
|
273
273
|
if (/^\d+$/.test(s)) return Number.parseInt(s, 10) * 6e4;
|
|
274
274
|
let totalMs = 0;
|
|
275
275
|
let matched = false;
|
|
276
|
-
|
|
276
|
+
let consumed = 0;
|
|
277
|
+
const regex = /(\d+(?:\.\d+)?)\s*(d|h|m|s)\s*/g;
|
|
277
278
|
let match;
|
|
278
279
|
while ((match = regex.exec(s)) !== null) {
|
|
280
|
+
if (match.index !== consumed) return null;
|
|
279
281
|
matched = true;
|
|
282
|
+
consumed = regex.lastIndex;
|
|
280
283
|
const value = Number.parseFloat(match[1]);
|
|
281
284
|
switch (match[2]) {
|
|
282
285
|
case "d":
|
|
@@ -293,7 +296,7 @@ function parseRelativeTime(timeStr) {
|
|
|
293
296
|
break;
|
|
294
297
|
}
|
|
295
298
|
}
|
|
296
|
-
return matched ? Math.round(totalMs) : null;
|
|
299
|
+
return matched && consumed === s.length ? Math.round(totalMs) : null;
|
|
297
300
|
}
|
|
298
301
|
/**
|
|
299
302
|
* Check whether a time string is a cron expression (3–6 space-separated fields).
|
|
@@ -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-PZU9ND1Q.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";
|
|
@@ -28,13 +28,7 @@ function buildExecApprovalText(request) {
|
|
|
28
28
|
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
|
|
29
29
|
return lines.join("\n");
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
const commandActions = actions.filter((action) => (action.kind === "command" || action.decision == null) && typeof action.command === "string" && action.command.trim().length > 0);
|
|
33
|
-
if (commandActions.length === 0) return;
|
|
34
|
-
lines.push("", "🗨️ 命令:");
|
|
35
|
-
for (const action of commandActions) lines.push(`- ${action.label}: ${action.command.trim()}`);
|
|
36
|
-
}
|
|
37
|
-
function buildPluginApprovalText(request, actions = []) {
|
|
31
|
+
function buildPluginApprovalText(request) {
|
|
38
32
|
const timeoutSec = Math.round((request.request.timeoutMs ?? 12e4) / 1e3);
|
|
39
33
|
const lines = [`${request.request.severity === "critical" ? "🔴" : request.request.severity === "info" ? "🔵" : "🟡"} \u5ba1\u6279\u8bf7\u6c42`, ""];
|
|
40
34
|
lines.push(`\u{1f4cb} ${request.request.title}`);
|
|
@@ -42,7 +36,6 @@ function buildPluginApprovalText(request, actions = []) {
|
|
|
42
36
|
if (request.request.toolName) lines.push(`\u{1f527} \u5de5\u5177: ${request.request.toolName}`);
|
|
43
37
|
if (request.request.pluginId) lines.push(`\u{1f50c} \u63d2\u4ef6: ${request.request.pluginId}`);
|
|
44
38
|
if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
|
|
45
|
-
appendCommandActionLines(lines, actions);
|
|
46
39
|
lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${timeoutSec} \u79d2`);
|
|
47
40
|
return lines.join("\n");
|
|
48
41
|
}
|
|
@@ -381,7 +374,7 @@ function createQQBotApprovalCapability() {
|
|
|
381
374
|
},
|
|
382
375
|
load: async () => {
|
|
383
376
|
ensurePlatformAdapter();
|
|
384
|
-
return (await import("./handler-runtime-
|
|
377
|
+
return (await import("./handler-runtime-yjslhmCX.js")).qqbotApprovalNativeRuntime;
|
|
385
378
|
}
|
|
386
379
|
})
|
|
387
380
|
});
|
|
@@ -875,7 +868,7 @@ function looksLikeQQBotTarget(id) {
|
|
|
875
868
|
//#region extensions/qqbot/src/channel.ts
|
|
876
869
|
let gatewayModulePromise;
|
|
877
870
|
function loadGatewayModule() {
|
|
878
|
-
gatewayModulePromise ??= import("./gateway-
|
|
871
|
+
gatewayModulePromise ??= import("./gateway-D1g0WRCr.js");
|
|
879
872
|
return gatewayModulePromise;
|
|
880
873
|
}
|
|
881
874
|
function createQQBotSendReceipt(params) {
|
|
@@ -893,7 +886,7 @@ function createQQBotSendReceipt(params) {
|
|
|
893
886
|
async function sendQQBotText(params) {
|
|
894
887
|
await loadGatewayModule();
|
|
895
888
|
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
896
|
-
const { sendText } = await import("./outbound-
|
|
889
|
+
const { sendText } = await import("./outbound-DsXljRHh.js").then((n) => n.t);
|
|
897
890
|
const result = await sendText({
|
|
898
891
|
to: params.to,
|
|
899
892
|
text: params.text,
|
|
@@ -915,7 +908,7 @@ async function sendQQBotText(params) {
|
|
|
915
908
|
async function sendQQBotMedia(params) {
|
|
916
909
|
await loadGatewayModule();
|
|
917
910
|
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
918
|
-
const { sendMedia } = await import("./outbound-
|
|
911
|
+
const { sendMedia } = await import("./outbound-DsXljRHh.js").then((n) => n.t);
|
|
919
912
|
const result = await sendMedia({
|
|
920
913
|
to: params.to,
|
|
921
914
|
text: params.text ?? "",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-Cx3C1vCZ.js";
|
|
2
2
|
export { qqbotPlugin };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { E as setOutboundAudioPort, _ as sendVoice, c as isMediaPayload, g as sendVideoMsg, h as sendPhoto, l as parseQQBotPayload, m as sendDocument, o as encodePayloadForCron, r as sendMedia, s as isCronReminderPayload, u as normalizeMediaTags, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-
|
|
1
|
+
import { E as setOutboundAudioPort, _ as sendVoice, c as isMediaPayload, g as sendVideoMsg, h as sendPhoto, l as parseQQBotPayload, m as sendDocument, o as encodePayloadForCron, r as sendMedia, s as isCronReminderPayload, u as normalizeMediaTags, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-DsXljRHh.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$1, 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-
|
|
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$1, 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-Cx3C1vCZ.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-PZU9ND1Q.js";
|
|
6
|
+
import { a as getPluginVersion, i as getFrameworkVersion, n as runWithRequestContext, o as initCommands, s as matchSlashCommand } from "./request-context-D4dkxrmN.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";
|
|
@@ -5665,7 +5665,7 @@ function parseWavFallback(buf) {
|
|
|
5665
5665
|
* QQBot reply path abort at ~5 minutes with "LLM request timed out",
|
|
5666
5666
|
* despite the direct ollama call to the same model working. The
|
|
5667
5667
|
* embedded-runner / idle-timeout layer already honors longer
|
|
5668
|
-
* provider timeouts (see `src/agents/
|
|
5668
|
+
* provider timeouts (see `src/agents/embedded-agent-runner/run/llm-idle-timeout.ts`),
|
|
5669
5669
|
* but the QQBot outbound dispatcher held an independent hardcoded
|
|
5670
5670
|
* `RESPONSE_TIMEOUT = 300_000` watchdog that quietly undercut the
|
|
5671
5671
|
* configured ceiling.
|
|
@@ -5690,7 +5690,7 @@ const DEFAULT_RESPONSE_TIMEOUT_MS = 3e5;
|
|
|
5690
5690
|
/**
|
|
5691
5691
|
* Upper bound to keep the watchdog inside the safe `setTimeout` range
|
|
5692
5692
|
* (approximately 24.8 days). Mirrors `MAX_SAFE_TIMEOUT_MS` in
|
|
5693
|
-
* `src/agents/
|
|
5693
|
+
* `src/agents/embedded-agent-runner/run/llm-idle-timeout.ts`.
|
|
5694
5694
|
*/
|
|
5695
5695
|
const MAX_SAFE_TIMEOUT_MS = 2147e6;
|
|
5696
5696
|
function positiveSecondsToMs(value) {
|
|
@@ -1,14 +1,11 @@
|
|
|
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-Cx3C1vCZ.js";
|
|
3
|
+
import { d as getMessageApi, i as accountToCreds } from "./runtime-PZU9ND1Q.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
|
|
7
7
|
function isExecRequest(request) {
|
|
8
|
-
return "
|
|
9
|
-
}
|
|
10
|
-
function listDecisionActions(actions) {
|
|
11
|
-
return actions.flatMap((action) => action.kind === "decision" && action.decision ? [action.decision] : []);
|
|
8
|
+
return "expiresAtMs" in request;
|
|
12
9
|
}
|
|
13
10
|
function resolveQQTarget(request) {
|
|
14
11
|
const sessionConversation = resolveApprovalRequestSessionConversation({
|
|
@@ -74,9 +71,8 @@ const qqbotApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
|
|
|
74
71
|
presentation: {
|
|
75
72
|
buildPendingPayload: ({ request, view }) => {
|
|
76
73
|
const req = request;
|
|
77
|
-
const text = isExecRequest(req) ? buildExecApprovalText(req) : buildPluginApprovalText(req
|
|
78
|
-
const
|
|
79
|
-
const keyboard = decisionActions.length > 0 ? buildApprovalKeyboard(req.id, decisionActions) : void 0;
|
|
74
|
+
const text = isExecRequest(req) ? buildExecApprovalText(req) : buildPluginApprovalText(req);
|
|
75
|
+
const keyboard = buildApprovalKeyboard(req.id, view.actions.map((action) => action.decision));
|
|
80
76
|
getBridgeLogger().debug?.(`[qqbot:approval-runtime] buildPendingPayload requestId=${req.id} kind=${isExecRequest(req) ? "exec" : "plugin"}`);
|
|
81
77
|
return {
|
|
82
78
|
text,
|
|
@@ -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, n as parseTarget$1, p as resolveQQBotPayloadLocalFilePath, s as getQQBotMediaDir, u as isLocalPath } from "./channel-Cx3C1vCZ.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-PZU9ND1Q.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";
|
|
@@ -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-Cx3C1vCZ.js";
|
|
2
|
+
import { w as debugLog } from "./runtime-PZU9ND1Q.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";
|
|
@@ -4,10 +4,11 @@ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import * as crypto$1 from "node:crypto";
|
|
6
6
|
import crypto from "node:crypto";
|
|
7
|
-
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
7
|
+
import { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
8
8
|
import { FsSafeError, openLocalFileSafely, readRegularFile, statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
9
9
|
import * as path$1 from "node:path";
|
|
10
10
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
11
|
+
import { readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
|
|
11
12
|
//#region extensions/qqbot/src/engine/types.ts
|
|
12
13
|
/**
|
|
13
14
|
* Core API layer public types.
|
|
@@ -755,7 +756,8 @@ var ChunkedMediaApi = class {
|
|
|
755
756
|
this.logger?.info?.(`${prefix} Start: file=${displayName} size=${formatFileSize(fileSize)} type=${opts.fileType}`);
|
|
756
757
|
const hashes = await computeHashes(input);
|
|
757
758
|
this.logger?.debug?.(`${prefix} hashes: md5=${hashes.md5} sha1=${hashes.sha1} md5_10m=${hashes.md5_10m}`);
|
|
758
|
-
|
|
759
|
+
const canUseUploadCache = opts.fileType !== 4;
|
|
760
|
+
if (this.cache && canUseUploadCache) {
|
|
759
761
|
const cached = this.cache.get(hashes.md5, opts.scope, opts.targetId, opts.fileType);
|
|
760
762
|
if (cached) {
|
|
761
763
|
this.logger?.info?.(`${prefix} cache HIT (md5=${hashes.md5.slice(0, 8)}) — skipping chunked upload`);
|
|
@@ -798,7 +800,7 @@ var ChunkedMediaApi = class {
|
|
|
798
800
|
this.logger?.info?.(`${prefix} all parts uploaded, completing...`);
|
|
799
801
|
const result = await this.callCompleteUpload(opts, upload_id);
|
|
800
802
|
this.logger?.info?.(`${prefix} completed: file_uuid=${result.file_uuid} ttl=${result.ttl}s`);
|
|
801
|
-
if (this.cache && result.file_info && result.ttl > 0) this.cache.set(hashes.md5, opts.scope, opts.targetId, opts.fileType, result.file_info, result.file_uuid, result.ttl);
|
|
803
|
+
if (this.cache && canUseUploadCache && result.file_info && result.ttl > 0) this.cache.set(hashes.md5, opts.scope, opts.targetId, opts.fileType, result.file_info, result.file_uuid, result.ttl);
|
|
802
804
|
return result;
|
|
803
805
|
} finally {
|
|
804
806
|
if (input.kind === "localPath" && input.closeWhenDone) await input.opened.close().catch(() => void 0);
|
|
@@ -1186,6 +1188,82 @@ var TokenManager = class {
|
|
|
1186
1188
|
* {@link ./media-chunked.ts}; this module currently handles only the
|
|
1187
1189
|
* one-shot path.
|
|
1188
1190
|
*/
|
|
1191
|
+
const DIRECT_UPLOAD_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
1192
|
+
const DIRECT_UPLOAD_READ_IDLE_TIMEOUT_MS = 1e4;
|
|
1193
|
+
const DIRECT_UPLOAD_BODY_GRACE_TIMEOUT_MS = 3e4;
|
|
1194
|
+
const DIRECT_UPLOAD_MIN_DOWNLOAD_BYTES_PER_SECOND = 256 * 1024;
|
|
1195
|
+
const DIRECT_UPLOAD_MAX_BODY_TIMEOUT_MS = 8 * 6e4;
|
|
1196
|
+
function assertDirectUploadDownloadHostAllowed(hostname) {
|
|
1197
|
+
if (isBlockedHostnameOrIp(hostname)) throw new Error("Blocked hostname or private/internal/special-use IP address");
|
|
1198
|
+
}
|
|
1199
|
+
async function fetchDirectUploadDownload(url) {
|
|
1200
|
+
const controller = new AbortController();
|
|
1201
|
+
const timeoutError = /* @__PURE__ */ new Error("Direct-upload media URL fetch timed out");
|
|
1202
|
+
let timedOut = false;
|
|
1203
|
+
let timeout;
|
|
1204
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1205
|
+
timeout = setTimeout(() => {
|
|
1206
|
+
timedOut = true;
|
|
1207
|
+
controller.abort(timeoutError);
|
|
1208
|
+
reject(timeoutError);
|
|
1209
|
+
}, DIRECT_UPLOAD_DOWNLOAD_TIMEOUT_MS);
|
|
1210
|
+
unrefTimer(timeout);
|
|
1211
|
+
});
|
|
1212
|
+
const guardedFetch = fetchWithSsrFGuard({
|
|
1213
|
+
url,
|
|
1214
|
+
maxRedirects: 0,
|
|
1215
|
+
signal: controller.signal
|
|
1216
|
+
});
|
|
1217
|
+
guardedFetch.then((result) => {
|
|
1218
|
+
if (timedOut) result.release().catch(() => void 0);
|
|
1219
|
+
}, () => void 0);
|
|
1220
|
+
try {
|
|
1221
|
+
return await Promise.race([guardedFetch, timeoutPromise]);
|
|
1222
|
+
} finally {
|
|
1223
|
+
if (timeout) clearTimeout(timeout);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function unrefTimer(timeout) {
|
|
1227
|
+
if (typeof timeout === "object" && "unref" in timeout) timeout.unref();
|
|
1228
|
+
}
|
|
1229
|
+
function resolveDirectUploadBodyTimeoutMs(maxBytes) {
|
|
1230
|
+
const transferTimeoutMs = Math.ceil(maxBytes / DIRECT_UPLOAD_MIN_DOWNLOAD_BYTES_PER_SECOND * 1e3);
|
|
1231
|
+
return Math.min(DIRECT_UPLOAD_BODY_GRACE_TIMEOUT_MS + transferTimeoutMs, DIRECT_UPLOAD_MAX_BODY_TIMEOUT_MS);
|
|
1232
|
+
}
|
|
1233
|
+
async function readDirectUploadResponse(response, maxBytes) {
|
|
1234
|
+
const timeoutMs = resolveDirectUploadBodyTimeoutMs(maxBytes);
|
|
1235
|
+
const timeoutError = /* @__PURE__ */ new Error(`Direct-upload media URL body timed out after ${timeoutMs}ms`);
|
|
1236
|
+
let timeout;
|
|
1237
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1238
|
+
timeout = setTimeout(() => {
|
|
1239
|
+
response.body?.cancel(timeoutError).catch(() => void 0);
|
|
1240
|
+
reject(timeoutError);
|
|
1241
|
+
}, timeoutMs);
|
|
1242
|
+
unrefTimer(timeout);
|
|
1243
|
+
});
|
|
1244
|
+
try {
|
|
1245
|
+
return await Promise.race([readResponseWithLimit(response, maxBytes, { chunkTimeoutMs: DIRECT_UPLOAD_READ_IDLE_TIMEOUT_MS }), timeoutPromise]);
|
|
1246
|
+
} finally {
|
|
1247
|
+
if (timeout) clearTimeout(timeout);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
async function downloadDirectUploadUrl(url, opts = {}) {
|
|
1251
|
+
let parsed;
|
|
1252
|
+
try {
|
|
1253
|
+
parsed = new URL(url);
|
|
1254
|
+
} catch {
|
|
1255
|
+
throw new Error("Direct-upload media URL must be a valid URL");
|
|
1256
|
+
}
|
|
1257
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("Direct-upload media URL must use HTTP or HTTPS");
|
|
1258
|
+
assertDirectUploadDownloadHostAllowed(parsed.hostname);
|
|
1259
|
+
const { response, release } = await fetchDirectUploadDownload(parsed.toString());
|
|
1260
|
+
try {
|
|
1261
|
+
if (!response.ok) throw new Error(`Direct-upload media URL returned HTTP ${response.status}`);
|
|
1262
|
+
return await readDirectUploadResponse(response, opts.maxBytes ?? 20971520);
|
|
1263
|
+
} finally {
|
|
1264
|
+
await release?.();
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1189
1267
|
/**
|
|
1190
1268
|
* Small-file media upload module.
|
|
1191
1269
|
*
|
|
@@ -1227,9 +1305,11 @@ var MediaApi = class {
|
|
|
1227
1305
|
let fileData = opts.fileData;
|
|
1228
1306
|
if (opts.buffer) fileData = opts.buffer.toString("base64");
|
|
1229
1307
|
else if (opts.localPath) fileData = (await fs$1.promises.readFile(opts.localPath)).toString("base64");
|
|
1230
|
-
if (fileData
|
|
1231
|
-
|
|
1232
|
-
|
|
1308
|
+
else if (opts.url !== void 0) fileData = (await downloadDirectUploadUrl(opts.url)).toString("base64");
|
|
1309
|
+
const uploadCache = fileData !== void 0 && !(fileType === 4 && opts.fileName) ? this.cache : void 0;
|
|
1310
|
+
if (fileData !== void 0 && uploadCache) {
|
|
1311
|
+
const hash = uploadCache.computeHash(fileData);
|
|
1312
|
+
const cached = uploadCache.get(hash, scope, targetId, fileType);
|
|
1233
1313
|
if (cached) return {
|
|
1234
1314
|
file_uuid: "",
|
|
1235
1315
|
file_info: cached,
|
|
@@ -1240,8 +1320,7 @@ var MediaApi = class {
|
|
|
1240
1320
|
file_type: fileType,
|
|
1241
1321
|
srv_send_msg: opts.srvSendMsg ?? false
|
|
1242
1322
|
};
|
|
1243
|
-
if (
|
|
1244
|
-
else if (fileData) body.file_data = fileData;
|
|
1323
|
+
if (fileData !== void 0) body.file_data = fileData;
|
|
1245
1324
|
if (fileType === 4 && opts.fileName) body.file_name = this.sanitize(opts.fileName);
|
|
1246
1325
|
const token = await this.tokenManager.getAccessToken(creds.appId, creds.clientSecret);
|
|
1247
1326
|
const path = mediaUploadPath(scope, targetId);
|
|
@@ -1249,9 +1328,9 @@ var MediaApi = class {
|
|
|
1249
1328
|
redactBodyKeys: ["file_data"],
|
|
1250
1329
|
uploadRequest: true
|
|
1251
1330
|
}), UPLOAD_RETRY_POLICY, void 0, this.logger);
|
|
1252
|
-
if (fileData && result.file_info && result.ttl > 0
|
|
1253
|
-
const hash =
|
|
1254
|
-
|
|
1331
|
+
if (fileData !== void 0 && uploadCache && result.file_info && result.ttl > 0) {
|
|
1332
|
+
const hash = uploadCache.computeHash(fileData);
|
|
1333
|
+
uploadCache.set(hash, scope, targetId, fileType, result.file_info, result.file_uuid, result.ttl);
|
|
1255
1334
|
}
|
|
1256
1335
|
return result;
|
|
1257
1336
|
}
|
|
@@ -1900,10 +1979,25 @@ async function sendMediaInternal(ctx, opts) {
|
|
|
1900
1979
|
*/
|
|
1901
1980
|
async function dispatchUpload(ctx, scope, targetId, fileType, source, creds, fileName) {
|
|
1902
1981
|
switch (source.kind) {
|
|
1903
|
-
case "url":
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1982
|
+
case "url": {
|
|
1983
|
+
const buffer = await downloadDirectUploadUrl(source.url, { maxBytes: getMaxUploadSize(fileType) });
|
|
1984
|
+
if (buffer.length >= 5242880) return ctx.chunkedMediaApi.uploadChunked({
|
|
1985
|
+
scope,
|
|
1986
|
+
targetId,
|
|
1987
|
+
fileType,
|
|
1988
|
+
source: {
|
|
1989
|
+
kind: "buffer",
|
|
1990
|
+
buffer,
|
|
1991
|
+
fileName
|
|
1992
|
+
},
|
|
1993
|
+
creds,
|
|
1994
|
+
fileName
|
|
1995
|
+
});
|
|
1996
|
+
return ctx.mediaApi.uploadMedia(scope, targetId, fileType, creds, {
|
|
1997
|
+
buffer,
|
|
1998
|
+
fileName
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
1907
2001
|
case "base64": return ctx.mediaApi.uploadMedia(scope, targetId, fileType, creds, {
|
|
1908
2002
|
fileData: source.data,
|
|
1909
2003
|
fileName
|
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-PZU9ND1Q.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.
|
|
3
|
+
"version": "2026.5.28-beta.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/qqbot",
|
|
9
|
-
"version": "2026.5.
|
|
9
|
+
"version": "2026.5.28-beta.2",
|
|
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.
|
|
18
|
+
"openclaw": ">=2026.5.28-beta.2"
|
|
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.
|
|
3
|
+
"version": "2026.5.28-beta.2",
|
|
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.
|
|
19
|
+
"openclaw": ">=2026.5.28-beta.2"
|
|
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.
|
|
48
|
+
"pluginApi": ">=2026.5.28-beta.2"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.5.
|
|
51
|
+
"openclawVersion": "2026.5.28-beta.2"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|