@openclaw/qqbot 2026.6.11 → 2026.7.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +5 -5
- package/dist/{channel-DvWrqcZB.js → channel-Bh0MrYvU.js} +34 -100
- package/dist/{channel-entry-BGXFRmbg.js → channel-entry-LMtNY-J6.js} +3 -3
- package/dist/channel-entry-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +168 -2
- package/dist/{gateway-CCkrdgaK.js → gateway-BgfQAYxB.js} +11 -162
- package/dist/{group-CvVXke_k.js → group-DQQsZ6Y0.js} +2 -2
- package/dist/{handler-runtime--OTMkFWa.js → handler-runtime-BQ72YPsH.js} +2 -2
- package/dist/{outbound-BQcPCo-z.js → outbound-Dfa2uOEp.js} +216 -4
- package/dist/{runtime-D1hn91-s.js → runtime-BL0xZzWe.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/{sender-CRZ2af7P.js → sender-BHWpE_yH.js} +2 -2
- package/dist/{doctor-contract-BYTS8-ia.js → state-keys-CQKlAFyo.js} +7 -1
- package/dist/target-parser-DagYRQkP.js +71 -0
- package/dist/{tools-CeUI9pG-.js → tools-BvXe7bTq.js} +58 -13
- package/dist/tools-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/skills/qqbot-channel/SKILL.md +54 -41
- package/skills/qqbot-channel/references/api_references.md +16 -8
- package/skills/qqbot-media/SKILL.md +6 -3
- package/skills/qqbot-remind/SKILL.md +15 -14
- package/dist/target-parser-BdCUmxK7.js +0 -285
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { C as getNextMsgSeq, D as downloadFile, F as StreamInputMode, I as StreamInputState, M as getMaxUploadSize, P as StreamContentType, T as openLocalFile, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as getImageMimeType, k as formatFileSize, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-
|
|
1
|
+
import { C as getNextMsgSeq, D as downloadFile, F as StreamInputMode, I as StreamInputState, M as getMaxUploadSize, P as StreamContentType, T as openLocalFile, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as getImageMimeType, k as formatFileSize, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-BHWpE_yH.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 { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-gMW9zKvQ.js";
|
|
4
|
-
import { a as
|
|
4
|
+
import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as createQQBotMarkdownChunker, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-Bh0MrYvU.js";
|
|
5
5
|
import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
|
|
6
6
|
import { a as formatErrorMessage, i as formatDuration, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
|
|
7
|
-
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-
|
|
8
|
-
import {
|
|
9
|
-
import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-
|
|
7
|
+
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-BL0xZzWe.js";
|
|
8
|
+
import { t as buildQQBotStateKey } from "./state-keys-CQKlAFyo.js";
|
|
9
|
+
import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-DQQsZ6Y0.js";
|
|
10
|
+
import { A as getHomeDir, E as DEFAULT_MEDIA_SEND_ERROR, F as isLocalPath, I as isWindows, L as normalizePath, M as getQQBotMediaDir, O as setOutboundAudioPort, P as getTempDir, _ as sendVideoMsg, b as resolveUserFacingMediaError, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, j as getQQBotDataDir, k as checkSilkWasmAvailable, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, y as resolveTrustedOutboundMediaPath } from "./outbound-Dfa2uOEp.js";
|
|
10
11
|
import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
11
|
-
import { E as DEFAULT_MEDIA_SEND_ERROR, O as setOutboundAudioPort, _ as sendVideoMsg, b as resolveUserFacingMediaError, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, y as resolveTrustedOutboundMediaPath } from "./outbound-BQcPCo-z.js";
|
|
12
12
|
import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
13
|
import { isImplicitSameChatApprovalAuthorization } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
14
14
|
import * as fs$1 from "node:fs";
|
|
15
15
|
import fs from "node:fs";
|
|
16
16
|
import * as os$1 from "node:os";
|
|
17
|
-
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
17
|
+
import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
18
18
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
19
19
|
import crypto from "node:crypto";
|
|
20
|
-
import {
|
|
20
|
+
import { readRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
21
21
|
import * as path$1 from "node:path";
|
|
22
22
|
import path from "node:path";
|
|
23
23
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
@@ -179,19 +179,10 @@ function formatRefEntryForAgent(entry) {
|
|
|
179
179
|
//#region extensions/qqbot/src/engine/ref/store.ts
|
|
180
180
|
/**
|
|
181
181
|
* Ref-index store — SQLite KV-backed store for message reference index.
|
|
182
|
-
*
|
|
183
|
-
* Legacy JSONL entries are imported once, then deleted after SQLite has the
|
|
184
|
-
* canonical ref-index rows.
|
|
185
182
|
*/
|
|
186
183
|
const MAX_ENTRIES = 5e4;
|
|
187
184
|
const TTL_MS = 10080 * 60 * 1e3;
|
|
188
185
|
const REF_INDEX_NAMESPACE = "ref-index";
|
|
189
|
-
const REF_INDEX_MIGRATIONS_NAMESPACE = "ref-index-migrations";
|
|
190
|
-
const LEGACY_REF_INDEX_MIGRATION_KEY = "ref-index-jsonl-v1";
|
|
191
|
-
let legacyImported$1 = false;
|
|
192
|
-
function getRefIndexFile() {
|
|
193
|
-
return path.join(getQQBotDataPath("data"), "ref-index.jsonl");
|
|
194
|
-
}
|
|
195
186
|
function createRefIndexStore() {
|
|
196
187
|
return openQQBotSyncKeyedStore({
|
|
197
188
|
namespace: REF_INDEX_NAMESPACE,
|
|
@@ -199,12 +190,6 @@ function createRefIndexStore() {
|
|
|
199
190
|
defaultTtlMs: TTL_MS
|
|
200
191
|
});
|
|
201
192
|
}
|
|
202
|
-
function createRefIndexMigrationStore() {
|
|
203
|
-
return openQQBotSyncKeyedStore({
|
|
204
|
-
namespace: REF_INDEX_MIGRATIONS_NAMESPACE,
|
|
205
|
-
maxEntries: 100
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
193
|
function refIndexStateKey(refIdx) {
|
|
209
194
|
return buildQQBotStateKey("ref-index", refIdx);
|
|
210
195
|
}
|
|
@@ -240,51 +225,9 @@ function toRefIndexEntry(entry) {
|
|
|
240
225
|
...entry.attachments ? { attachments: entry.attachments.map(toStoredAttachment) } : {}
|
|
241
226
|
};
|
|
242
227
|
}
|
|
243
|
-
function ensureLegacyRefIndexImported() {
|
|
244
|
-
if (legacyImported$1) return;
|
|
245
|
-
const migrationStore = createRefIndexMigrationStore();
|
|
246
|
-
if (migrationStore.lookup(LEGACY_REF_INDEX_MIGRATION_KEY)) {
|
|
247
|
-
legacyImported$1 = true;
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
try {
|
|
251
|
-
const refIndexFile = getRefIndexFile();
|
|
252
|
-
if (!fs.existsSync(refIndexFile)) {
|
|
253
|
-
migrationStore.register(LEGACY_REF_INDEX_MIGRATION_KEY, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
254
|
-
legacyImported$1 = true;
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const lines = fs.readFileSync(refIndexFile, "utf-8").split("\n");
|
|
258
|
-
const now = Date.now();
|
|
259
|
-
let expired = 0;
|
|
260
|
-
let imported = 0;
|
|
261
|
-
const store = createRefIndexStore();
|
|
262
|
-
for (const line of lines) {
|
|
263
|
-
const trimmed = line.trim();
|
|
264
|
-
if (!trimmed) continue;
|
|
265
|
-
try {
|
|
266
|
-
const entry = JSON.parse(trimmed);
|
|
267
|
-
if (!entry.k || !entry.v || !entry.t) continue;
|
|
268
|
-
if (now - entry.t > TTL_MS) {
|
|
269
|
-
expired++;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
store.register(refIndexStateKey(entry.k), toStoredRefIndexEntry(entry.v, entry.t), { ttlMs: Math.max(1, TTL_MS - (now - entry.t)) });
|
|
273
|
-
imported++;
|
|
274
|
-
} catch {}
|
|
275
|
-
}
|
|
276
|
-
migrationStore.register(LEGACY_REF_INDEX_MIGRATION_KEY, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
277
|
-
legacyImported$1 = true;
|
|
278
|
-
fs.rmSync(refIndexFile, { force: true });
|
|
279
|
-
debugLog(`[ref-index-store] Migrated ${imported} entries to SQLite (${expired} expired)`);
|
|
280
|
-
} catch (err) {
|
|
281
|
-
debugError(`[ref-index-store] Failed to import legacy JSONL: ${formatErrorMessage(err)}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
228
|
/** Persist a refIdx mapping for one message. */
|
|
285
229
|
function setRefIndex(refIdx, entry) {
|
|
286
230
|
try {
|
|
287
|
-
ensureLegacyRefIndexImported();
|
|
288
231
|
const now = Date.now();
|
|
289
232
|
createRefIndexStore().register(refIndexStateKey(refIdx), toStoredRefIndexEntry(entry, now), { ttlMs: TTL_MS });
|
|
290
233
|
} catch (err) {
|
|
@@ -294,7 +237,6 @@ function setRefIndex(refIdx, entry) {
|
|
|
294
237
|
/** Look up one quoted message by refIdx. */
|
|
295
238
|
function getRefIndex(refIdx) {
|
|
296
239
|
try {
|
|
297
|
-
ensureLegacyRefIndexImported();
|
|
298
240
|
const store = createRefIndexStore();
|
|
299
241
|
const key = refIndexStateKey(refIdx);
|
|
300
242
|
const entry = store.lookup(key);
|
|
@@ -567,34 +509,19 @@ async function trySlashCommand(msg, ctx) {
|
|
|
567
509
|
//#region extensions/qqbot/src/engine/session/known-users.ts
|
|
568
510
|
/**
|
|
569
511
|
* Known user tracking — SQLite KV-backed store.
|
|
570
|
-
*
|
|
571
|
-
* Legacy `known-users.json` data is imported once, then deleted after SQLite
|
|
572
|
-
* has the canonical copy.
|
|
573
512
|
*/
|
|
574
|
-
function getKnownUsersFile() {
|
|
575
|
-
return path.join(getQQBotDataPath("data"), "known-users.json");
|
|
576
|
-
}
|
|
577
513
|
function makeUserKey(user) {
|
|
578
514
|
const base = `${user.accountId}:${user.type}:${user.openid}`;
|
|
579
515
|
return user.type === "group" && user.groupOpenid ? `${base}:${user.groupOpenid}` : base;
|
|
580
516
|
}
|
|
581
517
|
const KNOWN_USERS_NAMESPACE = "known-users";
|
|
582
|
-
const KNOWN_USERS_MIGRATIONS_NAMESPACE = "known-users-migrations";
|
|
583
|
-
const LEGACY_KNOWN_USERS_MIGRATION_KEY = "known-users-json-v1";
|
|
584
518
|
const MAX_KNOWN_USERS = 1e5;
|
|
585
|
-
let legacyImported = false;
|
|
586
519
|
function createKnownUsersStore() {
|
|
587
520
|
return openQQBotSyncKeyedStore({
|
|
588
521
|
namespace: KNOWN_USERS_NAMESPACE,
|
|
589
522
|
maxEntries: MAX_KNOWN_USERS
|
|
590
523
|
});
|
|
591
524
|
}
|
|
592
|
-
function createKnownUsersMigrationStore() {
|
|
593
|
-
return openQQBotSyncKeyedStore({
|
|
594
|
-
namespace: KNOWN_USERS_MIGRATIONS_NAMESPACE,
|
|
595
|
-
maxEntries: 100
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
525
|
function knownUserStateKey(key) {
|
|
599
526
|
return crypto.createHash("sha256").update(key).digest("hex");
|
|
600
527
|
}
|
|
@@ -610,32 +537,9 @@ function toStoredKnownUser(user) {
|
|
|
610
537
|
interactionCount: user.interactionCount
|
|
611
538
|
};
|
|
612
539
|
}
|
|
613
|
-
function ensureLegacyKnownUsersImported() {
|
|
614
|
-
if (legacyImported) return;
|
|
615
|
-
const migrationStore = createKnownUsersMigrationStore();
|
|
616
|
-
if (migrationStore.lookup(LEGACY_KNOWN_USERS_MIGRATION_KEY)) {
|
|
617
|
-
legacyImported = true;
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
try {
|
|
621
|
-
const knownUsersFile = getKnownUsersFile();
|
|
622
|
-
const users = privateFileStoreSync(path.dirname(knownUsersFile)).readJsonIfExists(path.basename(knownUsersFile));
|
|
623
|
-
if (Array.isArray(users)) {
|
|
624
|
-
const store = createKnownUsersStore();
|
|
625
|
-
for (const user of users) store.registerIfAbsent(knownUserStateKey(makeUserKey(user)), toStoredKnownUser(user));
|
|
626
|
-
debugLog(`[known-users] Migrated ${users.length} users to SQLite`);
|
|
627
|
-
fs.rmSync(knownUsersFile, { force: true });
|
|
628
|
-
}
|
|
629
|
-
migrationStore.register(LEGACY_KNOWN_USERS_MIGRATION_KEY, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
630
|
-
legacyImported = true;
|
|
631
|
-
} catch (err) {
|
|
632
|
-
debugError(`[known-users] Failed to import legacy users: ${formatErrorMessage(err)}`);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
540
|
/** Record a known user whenever a message is received. */
|
|
636
541
|
function recordKnownUser(user) {
|
|
637
542
|
try {
|
|
638
|
-
ensureLegacyKnownUsersImported();
|
|
639
543
|
const store = createKnownUsersStore();
|
|
640
544
|
const stateKey = knownUserStateKey(makeUserKey(user));
|
|
641
545
|
const now = Date.now();
|
|
@@ -669,34 +573,12 @@ function recordKnownUser(user) {
|
|
|
669
573
|
//#region extensions/qqbot/src/engine/session/session-store.ts
|
|
670
574
|
/**
|
|
671
575
|
* Gateway session persistence — SQLite KV-backed store.
|
|
672
|
-
*
|
|
673
|
-
* Legacy JSON session files are imported on first account access, then
|
|
674
|
-
* removed after SQLite has the canonical short-lived session entry.
|
|
675
576
|
*/
|
|
676
577
|
const SESSION_EXPIRE_TIME = 300 * 1e3;
|
|
677
578
|
const SAVE_THROTTLE_MS = 1e3;
|
|
678
579
|
const SESSION_NAMESPACE = "gateway-sessions";
|
|
679
580
|
const MAX_SESSIONS = 1e3;
|
|
680
581
|
const throttleState = /* @__PURE__ */ new Map();
|
|
681
|
-
function getSessionDir() {
|
|
682
|
-
return getQQBotDataPath("sessions");
|
|
683
|
-
}
|
|
684
|
-
function encodeAccountIdForFileName(accountId) {
|
|
685
|
-
return Buffer.from(accountId, "utf8").toString("base64url");
|
|
686
|
-
}
|
|
687
|
-
function getLegacySessionPath(accountId) {
|
|
688
|
-
const safeId = accountId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
689
|
-
return path.join(getSessionDir(), `session-${safeId}.json`);
|
|
690
|
-
}
|
|
691
|
-
function getSessionPath(accountId) {
|
|
692
|
-
const encodedId = encodeAccountIdForFileName(accountId);
|
|
693
|
-
return path.join(getSessionDir(), `session-${encodedId}.json`);
|
|
694
|
-
}
|
|
695
|
-
function getCandidateSessionPaths(accountId) {
|
|
696
|
-
const primaryPath = getSessionPath(accountId);
|
|
697
|
-
const legacyPath = getLegacySessionPath(accountId);
|
|
698
|
-
return primaryPath === legacyPath ? [primaryPath] : [primaryPath, legacyPath];
|
|
699
|
-
}
|
|
700
582
|
function createSessionStore() {
|
|
701
583
|
return openQQBotSyncKeyedStore({
|
|
702
584
|
namespace: SESSION_NAMESPACE,
|
|
@@ -707,9 +589,6 @@ function createSessionStore() {
|
|
|
707
589
|
function sessionKey(accountId) {
|
|
708
590
|
return buildQQBotStateKey("gateway-session", accountId);
|
|
709
591
|
}
|
|
710
|
-
function remainingSessionTtlMs(state, now = Date.now()) {
|
|
711
|
-
return Math.max(1, SESSION_EXPIRE_TIME - (now - state.savedAt));
|
|
712
|
-
}
|
|
713
592
|
function toStoredSessionState(state) {
|
|
714
593
|
return {
|
|
715
594
|
sessionId: state.sessionId,
|
|
@@ -721,35 +600,11 @@ function toStoredSessionState(state) {
|
|
|
721
600
|
...state.appId ? { appId: state.appId } : {}
|
|
722
601
|
};
|
|
723
602
|
}
|
|
724
|
-
function removeFileQuietly(filePath) {
|
|
725
|
-
try {
|
|
726
|
-
fs.unlinkSync(filePath);
|
|
727
|
-
} catch {}
|
|
728
|
-
}
|
|
729
|
-
function loadLegacySession(accountId) {
|
|
730
|
-
for (const candidatePath of getCandidateSessionPaths(accountId)) {
|
|
731
|
-
const state = privateFileStoreSync(path.dirname(candidatePath)).readJsonIfExists(path.basename(candidatePath));
|
|
732
|
-
if (state) return {
|
|
733
|
-
state,
|
|
734
|
-
filePath: candidatePath
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
return null;
|
|
738
|
-
}
|
|
739
|
-
function migrateLegacySession(accountId) {
|
|
740
|
-
const legacy = loadLegacySession(accountId);
|
|
741
|
-
if (!legacy) return null;
|
|
742
|
-
const now = Date.now();
|
|
743
|
-
if (now - legacy.state.savedAt <= SESSION_EXPIRE_TIME) createSessionStore().register(sessionKey(accountId), toStoredSessionState(legacy.state), { ttlMs: remainingSessionTtlMs(legacy.state, now) });
|
|
744
|
-
for (const filePath of getCandidateSessionPaths(accountId)) removeFileQuietly(filePath);
|
|
745
|
-
return legacy.state;
|
|
746
|
-
}
|
|
747
603
|
/** Load a saved session, rejecting expired or mismatched appId entries. */
|
|
748
604
|
function loadSession(accountId, expectedAppId) {
|
|
749
605
|
try {
|
|
750
606
|
const store = createSessionStore();
|
|
751
|
-
|
|
752
|
-
if (!state) state = migrateLegacySession(accountId) ?? void 0;
|
|
607
|
+
const state = store.lookup(sessionKey(accountId));
|
|
753
608
|
if (!state) return null;
|
|
754
609
|
const now = Date.now();
|
|
755
610
|
if (now - state.savedAt > SESSION_EXPIRE_TIME) {
|
|
@@ -813,16 +668,12 @@ function saveSession(state) {
|
|
|
813
668
|
}
|
|
814
669
|
}
|
|
815
670
|
function doSaveSession(state) {
|
|
816
|
-
const filePath = getSessionPath(state.accountId);
|
|
817
|
-
const legacyPath = getLegacySessionPath(state.accountId);
|
|
818
671
|
try {
|
|
819
672
|
const stateToSave = {
|
|
820
673
|
...state,
|
|
821
674
|
savedAt: Date.now()
|
|
822
675
|
};
|
|
823
676
|
createSessionStore().register(sessionKey(state.accountId), toStoredSessionState(stateToSave), { ttlMs: SESSION_EXPIRE_TIME });
|
|
824
|
-
removeFileQuietly(filePath);
|
|
825
|
-
removeFileQuietly(legacyPath);
|
|
826
677
|
debugLog(`[session-store] Saved session for ${state.accountId}: sessionId=${state.sessionId}, lastSeq=${state.lastSeq}`);
|
|
827
678
|
} catch (err) {
|
|
828
679
|
debugError(`[session-store] Failed to save session for ${state.accountId}: ${formatErrorMessage(err)}`);
|
|
@@ -836,9 +687,7 @@ function clearSession(accountId) {
|
|
|
836
687
|
throttleState.delete(accountId);
|
|
837
688
|
}
|
|
838
689
|
try {
|
|
839
|
-
|
|
840
|
-
for (const filePath of getCandidateSessionPaths(accountId)) removeFileQuietly(filePath);
|
|
841
|
-
if (cleared) debugLog(`[session-store] Cleared session for ${accountId}`);
|
|
690
|
+
if (createSessionStore().delete(sessionKey(accountId))) debugLog(`[session-store] Cleared session for ${accountId}`);
|
|
842
691
|
} catch (err) {
|
|
843
692
|
debugError(`[session-store] Failed to clear session for ${accountId}: ${formatErrorMessage(err)}`);
|
|
844
693
|
}
|
|
@@ -1869,7 +1718,7 @@ async function transcribeAudio(audioPath, cfg) {
|
|
|
1869
1718
|
const detail = await readResponseTextLimited(resp, STT_ERROR_BODY_LIMIT_BYTES).catch(() => "");
|
|
1870
1719
|
throw new Error(`STT failed (HTTP ${resp.status}): ${detail.slice(0, 300)}`);
|
|
1871
1720
|
}
|
|
1872
|
-
return normalizeOptionalString$1((await resp.
|
|
1721
|
+
return normalizeOptionalString$1((await readProviderJsonResponse(resp, "qqbot.stt")).text) ?? null;
|
|
1873
1722
|
} finally {
|
|
1874
1723
|
await release();
|
|
1875
1724
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
2
|
import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
|
|
3
3
|
import { n as debugLog } from "./log-SDfMMBWe.js";
|
|
4
|
-
import {
|
|
4
|
+
import { A as getHomeDir, I as isWindows, N as getQQBotMediaPath, j as getQQBotDataDir } from "./outbound-Dfa2uOEp.js";
|
|
5
5
|
import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
|
|
9
8
|
import { clearRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
9
|
+
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
|
|
10
10
|
//#region extensions/qqbot/src/engine/commands/builtin/state.ts
|
|
11
11
|
let resolveVersionGetter = () => "unknown";
|
|
12
12
|
let approveRuntimeGetter = null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as getMessageApi, t as accountToCreds } from "./sender-
|
|
1
|
+
import { c as getMessageApi, t as accountToCreds } from "./sender-BHWpE_yH.js";
|
|
2
2
|
import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-gMW9zKvQ.js";
|
|
3
|
-
import { c as
|
|
3
|
+
import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-Bh0MrYvU.js";
|
|
4
4
|
import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
|
|
5
5
|
import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
6
6
|
import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
@@ -1,11 +1,223 @@
|
|
|
1
|
-
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-
|
|
2
|
-
import { i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
1
|
+
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-BHWpE_yH.js";
|
|
2
|
+
import { c as getPlatformAdapter, i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
3
3
|
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
|
|
4
|
-
import {
|
|
4
|
+
import { r as parseTarget$1 } from "./target-parser-DagYRQkP.js";
|
|
5
|
+
import * as fs$1 from "node:fs";
|
|
6
|
+
import * as os$1 from "node:os";
|
|
5
7
|
import { pathExistsSync, resolveLocalPathFromRootsSync } from "openclaw/plugin-sdk/security-runtime";
|
|
8
|
+
import * as path$1 from "node:path";
|
|
6
9
|
import path from "node:path";
|
|
7
10
|
import { getFileExtension } from "openclaw/plugin-sdk/media-mime";
|
|
8
11
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/sandbox";
|
|
12
|
+
//#region extensions/qqbot/src/engine/utils/platform.ts
|
|
13
|
+
/**
|
|
14
|
+
* Cross-platform path and detection helpers for core/ modules.
|
|
15
|
+
*
|
|
16
|
+
* Provides home/data/media directory helpers, platform detection,
|
|
17
|
+
* silk-wasm availability checks — all without importing `openclaw/plugin-sdk`.
|
|
18
|
+
* The temp-directory fallback is delegated to the PlatformAdapter.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the current user's OS home directory safely across platforms.
|
|
22
|
+
*
|
|
23
|
+
* Priority:
|
|
24
|
+
* 1. `os.homedir()`
|
|
25
|
+
* 2. `$HOME` or `%USERPROFILE%`
|
|
26
|
+
* 3. PlatformAdapter.getTempDir() as a last resort
|
|
27
|
+
*
|
|
28
|
+
* This is the *operating-system* home and intentionally ignores
|
|
29
|
+
* `OPENCLAW_HOME`. QQ Bot still checks this tree for legacy state imports and
|
|
30
|
+
* media-path remaps from older releases.
|
|
31
|
+
*/
|
|
32
|
+
function getHomeDir() {
|
|
33
|
+
try {
|
|
34
|
+
const home = os$1.homedir();
|
|
35
|
+
if (home && fs$1.existsSync(home)) return home;
|
|
36
|
+
} catch {}
|
|
37
|
+
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
38
|
+
if (envHome && fs$1.existsSync(envHome)) return envHome;
|
|
39
|
+
return getPlatformAdapter().getTempDir();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the effective OpenClaw home directory.
|
|
43
|
+
*
|
|
44
|
+
* Mirrors the contract from core (`src/infra/home-dir.ts::resolveEffectiveHomeDir`)
|
|
45
|
+
* so QQ Bot media roots live under the same tree the rest of OpenClaw treats as
|
|
46
|
+
* `~`. The extension cannot import the core helper directly (it is a separate
|
|
47
|
+
* package with `openclaw` as a peer dependency), so this re-implements the
|
|
48
|
+
* minimal contract:
|
|
49
|
+
*
|
|
50
|
+
* 1. `OPENCLAW_HOME` when set (with `~` / `~/...` expanded against the OS home).
|
|
51
|
+
* 2. Otherwise fall back to {@link getHomeDir} so existing single-home
|
|
52
|
+
* deployments are unaffected.
|
|
53
|
+
*
|
|
54
|
+
* Empty / `"undefined"` / `"null"` strings are treated as unset to match how
|
|
55
|
+
* core normalizes the variable.
|
|
56
|
+
*/
|
|
57
|
+
function resolveOpenClawHome() {
|
|
58
|
+
const raw = process.env.OPENCLAW_HOME?.trim();
|
|
59
|
+
if (!raw || raw === "undefined" || raw === "null") return getHomeDir();
|
|
60
|
+
if (raw === "~" || raw.startsWith("~/") || raw.startsWith("~\\")) {
|
|
61
|
+
const osHome = getHomeDir();
|
|
62
|
+
if (raw === "~") return osHome;
|
|
63
|
+
return path$1.join(osHome, raw.slice(2));
|
|
64
|
+
}
|
|
65
|
+
return raw;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Return a legacy path under `~/.openclaw/qqbot` without creating it.
|
|
69
|
+
*
|
|
70
|
+
* Current QQ Bot runtime state lives in plugin SQLite KV. This path remains for
|
|
71
|
+
* legacy imports and media-path remaps from older releases.
|
|
72
|
+
*/
|
|
73
|
+
function getQQBotDataPath(...subPaths) {
|
|
74
|
+
return path$1.join(getHomeDir(), ".openclaw", "qqbot", ...subPaths);
|
|
75
|
+
}
|
|
76
|
+
/** Return a path under `~/.openclaw/qqbot`, creating it on demand. */
|
|
77
|
+
function getQQBotDataDir(...subPaths) {
|
|
78
|
+
const dir = getQQBotDataPath(...subPaths);
|
|
79
|
+
if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
|
|
80
|
+
return dir;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Return a path under `<openclaw-home>/.openclaw/media/qqbot` without creating it.
|
|
84
|
+
*
|
|
85
|
+
* Unlike `getQQBotDataPath`, this lives under OpenClaw's core media allowlist
|
|
86
|
+
* so downloaded images and audio can be accessed by framework media tooling.
|
|
87
|
+
* The base honors `OPENCLAW_HOME` (when set) so files written by agents into
|
|
88
|
+
* the OpenClaw-managed media tree are reachable by this plugin even when
|
|
89
|
+
* `HOME` and `OPENCLAW_HOME` differ (Docker, multi-user hosts). Fixes #83562.
|
|
90
|
+
*/
|
|
91
|
+
function getQQBotMediaPath(...subPaths) {
|
|
92
|
+
return path$1.join(resolveOpenClawHome(), ".openclaw", "media", "qqbot", ...subPaths);
|
|
93
|
+
}
|
|
94
|
+
/** Return a path under `<openclaw-home>/.openclaw/media/qqbot`, creating it on demand. */
|
|
95
|
+
function getQQBotMediaDir(...subPaths) {
|
|
96
|
+
const dir = getQQBotMediaPath(...subPaths);
|
|
97
|
+
if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
|
|
98
|
+
return dir;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Return `<openclaw-home>/.openclaw/media`, OpenClaw's shared media root.
|
|
102
|
+
*
|
|
103
|
+
* This mirrors the directory that core's `buildMediaLocalRoots` exposes as an
|
|
104
|
+
* allowlisted location (see `openclaw/src/media/local-roots.ts`). Using it as a
|
|
105
|
+
* QQ Bot payload root lets the plugin trust framework-produced files that live
|
|
106
|
+
* in sibling subdirectories such as `outbound/` (written by
|
|
107
|
+
* `saveMediaBuffer(..., "outbound", ...)`) or `inbound/`, while still keeping
|
|
108
|
+
* the check anchored to a single, well-known directory. Like
|
|
109
|
+
* {@link getQQBotMediaPath}, the base honors `OPENCLAW_HOME`.
|
|
110
|
+
*/
|
|
111
|
+
function getOpenClawMediaDir() {
|
|
112
|
+
return path$1.join(resolveOpenClawHome(), ".openclaw", "media");
|
|
113
|
+
}
|
|
114
|
+
function isWindows() {
|
|
115
|
+
return process.platform === "win32";
|
|
116
|
+
}
|
|
117
|
+
/** Return the preferred temporary directory. */
|
|
118
|
+
function getTempDir() {
|
|
119
|
+
return getPlatformAdapter().getTempDir();
|
|
120
|
+
}
|
|
121
|
+
let silkWasmAvailable = null;
|
|
122
|
+
/** Check whether silk-wasm can run in the current environment. */
|
|
123
|
+
async function checkSilkWasmAvailable() {
|
|
124
|
+
if (silkWasmAvailable !== null) return silkWasmAvailable;
|
|
125
|
+
try {
|
|
126
|
+
const { isSilk } = await import("silk-wasm");
|
|
127
|
+
isSilk(new Uint8Array(0));
|
|
128
|
+
silkWasmAvailable = true;
|
|
129
|
+
debugLog("[platform] silk-wasm: available");
|
|
130
|
+
} catch (err) {
|
|
131
|
+
silkWasmAvailable = false;
|
|
132
|
+
debugWarn(`[platform] silk-wasm: NOT available (${formatErrorMessage(err)})`);
|
|
133
|
+
}
|
|
134
|
+
return silkWasmAvailable;
|
|
135
|
+
}
|
|
136
|
+
/** Expand `~` to the current user's home directory. */
|
|
137
|
+
function expandTilde$1(p) {
|
|
138
|
+
if (!p) return p;
|
|
139
|
+
if (p === "~") return getHomeDir();
|
|
140
|
+
if (p.startsWith("~/") || p.startsWith("~\\")) return path$1.join(getHomeDir(), p.slice(2));
|
|
141
|
+
return p;
|
|
142
|
+
}
|
|
143
|
+
/** Normalize a user-provided path by trimming, stripping `file://`, and expanding `~`. */
|
|
144
|
+
function normalizePath$1(p) {
|
|
145
|
+
let result = p.trim();
|
|
146
|
+
if (result.startsWith("file://")) {
|
|
147
|
+
result = result.slice(7);
|
|
148
|
+
try {
|
|
149
|
+
result = decodeURIComponent(result);
|
|
150
|
+
} catch {}
|
|
151
|
+
}
|
|
152
|
+
return expandTilde$1(result);
|
|
153
|
+
}
|
|
154
|
+
/** Return true when the string looks like a local filesystem path rather than a URL. */
|
|
155
|
+
function isLocalPath(p) {
|
|
156
|
+
if (!p) return false;
|
|
157
|
+
if (p.startsWith("file://")) return true;
|
|
158
|
+
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) return true;
|
|
159
|
+
if (p.startsWith("/")) return true;
|
|
160
|
+
if (/^[a-zA-Z]:[\\/]/.test(p)) return true;
|
|
161
|
+
if (p.startsWith("\\\\")) return true;
|
|
162
|
+
if (p.startsWith("./") || p.startsWith("../")) return true;
|
|
163
|
+
if (p.startsWith(".\\") || p.startsWith("..\\")) return true;
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
function isPathWithinRoot(candidate, root) {
|
|
167
|
+
const relative = path$1.relative(root, candidate);
|
|
168
|
+
return relative === "" || !relative.startsWith("..") && !path$1.isAbsolute(relative);
|
|
169
|
+
}
|
|
170
|
+
/** Remap legacy or hallucinated QQ Bot local media paths to real files when possible. */
|
|
171
|
+
function resolveQQBotLocalMediaPath(p) {
|
|
172
|
+
const normalized = normalizePath$1(p);
|
|
173
|
+
if (!isLocalPath(normalized) || fs$1.existsSync(normalized)) return normalized;
|
|
174
|
+
const osHomeDir = getHomeDir();
|
|
175
|
+
const openclawHomeDir = resolveOpenClawHome();
|
|
176
|
+
const mediaRoot = getQQBotMediaPath();
|
|
177
|
+
const dataRoot = getQQBotDataPath();
|
|
178
|
+
const candidateRoots = [
|
|
179
|
+
...Array.from(new Set([path$1.join(osHomeDir, ".openclaw", "workspace", "qqbot"), path$1.join(openclawHomeDir, ".openclaw", "workspace", "qqbot")])).map((from) => ({
|
|
180
|
+
from,
|
|
181
|
+
to: mediaRoot
|
|
182
|
+
})),
|
|
183
|
+
{
|
|
184
|
+
from: dataRoot,
|
|
185
|
+
to: mediaRoot
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
from: mediaRoot,
|
|
189
|
+
to: dataRoot
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
for (const { from, to } of candidateRoots) {
|
|
193
|
+
if (!isPathWithinRoot(normalized, from)) continue;
|
|
194
|
+
const relative = path$1.relative(from, normalized);
|
|
195
|
+
const candidate = path$1.join(to, relative);
|
|
196
|
+
if (fs$1.existsSync(candidate)) {
|
|
197
|
+
debugWarn(`[platform] Remapped missing QQBot media path ${normalized} -> ${candidate}`);
|
|
198
|
+
return candidate;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return normalized;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolve a structured-payload local file path and enforce that it stays within
|
|
205
|
+
* QQ Bot-owned storage roots.
|
|
206
|
+
*/
|
|
207
|
+
function resolveQQBotPayloadLocalFilePath(p) {
|
|
208
|
+
const candidate = resolveQQBotLocalMediaPath(p);
|
|
209
|
+
if (!candidate.trim()) return null;
|
|
210
|
+
const resolvedCandidate = path$1.resolve(candidate);
|
|
211
|
+
if (!fs$1.existsSync(resolvedCandidate)) return null;
|
|
212
|
+
const canonicalCandidate = fs$1.realpathSync(resolvedCandidate);
|
|
213
|
+
const allowedRoots = [getOpenClawMediaDir(), getQQBotMediaPath()];
|
|
214
|
+
for (const root of allowedRoots) {
|
|
215
|
+
const resolvedRoot = path$1.resolve(root);
|
|
216
|
+
if (isPathWithinRoot(canonicalCandidate, fs$1.existsSync(resolvedRoot) ? fs$1.realpathSync(resolvedRoot) : resolvedRoot)) return canonicalCandidate;
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
9
221
|
//#region extensions/qqbot/src/engine/messaging/outbound-audio-port.ts
|
|
10
222
|
let outboundAudioPort = null;
|
|
11
223
|
/**
|
|
@@ -1410,4 +1622,4 @@ async function sendCronMessage(account, to, message) {
|
|
|
1410
1622
|
});
|
|
1411
1623
|
}
|
|
1412
1624
|
//#endregion
|
|
1413
|
-
export { getMessageReplyConfig as C, OUTBOUND_ERROR_CODES as D, DEFAULT_MEDIA_SEND_ERROR as E, setOutboundAudioPort as O, checkMessageReplyLimit as S, recordMessageReply as T, sendVideoMsg as _, sendText as a, resolveUserFacingMediaError 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, getMessageReplyStats as w, MESSAGE_REPLY_LIMIT as x, resolveTrustedOutboundMediaPath as y };
|
|
1625
|
+
export { getHomeDir as A, getMessageReplyConfig as C, OUTBOUND_ERROR_CODES as D, DEFAULT_MEDIA_SEND_ERROR as E, isLocalPath as F, isWindows as I, normalizePath$1 as L, getQQBotMediaDir as M, getQQBotMediaPath as N, setOutboundAudioPort as O, getTempDir as P, checkMessageReplyLimit as S, recordMessageReply as T, sendVideoMsg as _, sendText as a, resolveUserFacingMediaError as b, isCronReminderPayload as c, normalizeMediaTags as d, buildMediaTarget as f, sendPhoto as g, sendDocument as h, sendProactiveMessage as i, getQQBotDataDir as j, checkSilkWasmAvailable as k, 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, getMessageReplyStats as w, MESSAGE_REPLY_LIMIT as x, resolveTrustedOutboundMediaPath as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { v as setOpenClawVersion } from "./sender-
|
|
1
|
+
import { v as setOpenClawVersion } from "./sender-BHWpE_yH.js";
|
|
2
2
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
3
3
|
//#region extensions/qqbot/src/bridge/runtime.ts
|
|
4
4
|
const { setRuntime: _setRuntime, clearRuntime: resetQQBotRuntimeForTest, getRuntime: getQQBotRuntime } = createPluginRuntimeStore({
|
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-BL0xZzWe.js";
|
|
2
2
|
export { getQQBotRuntime, setQQBotRuntime };
|
|
@@ -2,7 +2,7 @@ import { c as getPlatformAdapter, i as normalizeOptionalString, s as sanitizeFil
|
|
|
2
2
|
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
|
-
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
5
|
+
import { readProviderTextResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
6
6
|
import { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
7
7
|
import * as crypto$1 from "node:crypto";
|
|
8
8
|
import crypto from "node:crypto";
|
|
@@ -159,7 +159,7 @@ var ApiClient = class {
|
|
|
159
159
|
this.logger?.info?.(`[qqbot:api] <<< Status: ${res.status} ${res.statusText}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
160
160
|
const readBody = async (limitBytes) => {
|
|
161
161
|
try {
|
|
162
|
-
return limitBytes === void 0 ? await res
|
|
162
|
+
return limitBytes === void 0 ? await readProviderTextResponse(res, "QQBot API response") : await readResponseTextLimited(res, limitBytes);
|
|
163
163
|
} catch (err) {
|
|
164
164
|
throw new ApiError(`Failed to read response [${path}]: ${formatErrorMessage(err)}`, res.status, path);
|
|
165
165
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import { asObjectRecord } from "openclaw/plugin-sdk/runtime-doctor";
|
|
2
3
|
//#region extensions/qqbot/src/doctor-contract.ts
|
|
3
4
|
const RESTRICTED_GROUP_TOOLS = { deny: [
|
|
@@ -132,4 +133,9 @@ function normalizeCompatibilityConfig({ cfg }) {
|
|
|
132
133
|
};
|
|
133
134
|
}
|
|
134
135
|
//#endregion
|
|
135
|
-
|
|
136
|
+
//#region extensions/qqbot/src/engine/utils/state-keys.ts
|
|
137
|
+
function buildQQBotStateKey(...parts) {
|
|
138
|
+
return crypto.createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
export { legacyConfigRules as n, normalizeCompatibilityConfig as r, buildQQBotStateKey as t };
|