@openclaw/qqbot 2026.5.14-beta.1 → 2026.5.16-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 +6 -6
- package/dist/channel-CsKOF-PJ.js +1102 -0
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-DWWV_GAa.js → channel.setup-D_8Ryfln.js} +1 -2
- package/dist/{config-DiBiPtf4.js → config-schema-BE5YP0NG.js} +316 -12
- package/dist/{gateway-DEJhmN-p.js → gateway-pvpMZ-Sn.js} +9 -15
- package/dist/{handler-runtime-COWL4RMq.js → handler-runtime-CsNYWx6E.js} +3 -3
- package/dist/{outbound-BdQEr78b.js → outbound-DekhyR4u.js} +2 -2
- package/dist/{request-context-Betw4nJr.js → request-context-FhYx1qjS.js} +2 -2
- package/dist/{sender-BeE_CBbN.js → runtime-BFcYWYuk.js} +17 -1
- package/dist/runtime-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/package.json +5 -5
- package/dist/channel-BYNY5Nwb.js +0 -606
- package/dist/config-schema-BeJQUheK.js +0 -308
- package/dist/exec-approvals-Deww_EGt.js +0 -238
- package/dist/narrowing-Bj4eMufj.js +0 -25
- package/dist/runtime-B-cqLImu.js +0 -18
- package/dist/target-parser-ByU3srDK.js +0 -245
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { c as getPlatformAdapter } from "./string-normalize-C46CS-F1.js";
|
|
2
|
-
import { C as debugWarn, L as formatErrorMessage, S as debugLog } from "./sender-BeE_CBbN.js";
|
|
3
|
-
import * as fs$1 from "node:fs";
|
|
4
|
-
import * as os$1 from "node:os";
|
|
5
|
-
import * as path$1 from "node:path";
|
|
6
|
-
//#region extensions/qqbot/src/engine/utils/platform.ts
|
|
7
|
-
/**
|
|
8
|
-
* Cross-platform path and detection helpers for core/ modules.
|
|
9
|
-
*
|
|
10
|
-
* Provides home/data/media directory helpers, platform detection,
|
|
11
|
-
* silk-wasm availability checks — all without importing `openclaw/plugin-sdk`.
|
|
12
|
-
* The temp-directory fallback is delegated to the PlatformAdapter.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Resolve the current user's home directory safely across platforms.
|
|
16
|
-
*
|
|
17
|
-
* Priority:
|
|
18
|
-
* 1. `os.homedir()`
|
|
19
|
-
* 2. `$HOME` or `%USERPROFILE%`
|
|
20
|
-
* 3. PlatformAdapter.getTempDir() as a last resort
|
|
21
|
-
*/
|
|
22
|
-
function getHomeDir() {
|
|
23
|
-
try {
|
|
24
|
-
const home = os$1.homedir();
|
|
25
|
-
if (home && fs$1.existsSync(home)) return home;
|
|
26
|
-
} catch {}
|
|
27
|
-
const envHome = process.env.HOME || process.env.USERPROFILE;
|
|
28
|
-
if (envHome && fs$1.existsSync(envHome)) return envHome;
|
|
29
|
-
return getPlatformAdapter().getTempDir();
|
|
30
|
-
}
|
|
31
|
-
/** Return a path under `~/.openclaw/qqbot` without creating it. */
|
|
32
|
-
function getQQBotDataPath(...subPaths) {
|
|
33
|
-
return path$1.join(getHomeDir(), ".openclaw", "qqbot", ...subPaths);
|
|
34
|
-
}
|
|
35
|
-
/** Return a path under `~/.openclaw/qqbot`, creating it on demand. */
|
|
36
|
-
function getQQBotDataDir(...subPaths) {
|
|
37
|
-
const dir = getQQBotDataPath(...subPaths);
|
|
38
|
-
if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
|
|
39
|
-
return dir;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Return a path under `~/.openclaw/media/qqbot` without creating it.
|
|
43
|
-
*
|
|
44
|
-
* Unlike `getQQBotDataPath`, this lives under OpenClaw's core media allowlist so
|
|
45
|
-
* downloaded images and audio can be accessed by framework media tooling.
|
|
46
|
-
*/
|
|
47
|
-
function getQQBotMediaPath(...subPaths) {
|
|
48
|
-
return path$1.join(getHomeDir(), ".openclaw", "media", "qqbot", ...subPaths);
|
|
49
|
-
}
|
|
50
|
-
/** Return a path under `~/.openclaw/media/qqbot`, creating it on demand. */
|
|
51
|
-
function getQQBotMediaDir(...subPaths) {
|
|
52
|
-
const dir = getQQBotMediaPath(...subPaths);
|
|
53
|
-
if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
|
|
54
|
-
return dir;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Return `~/.openclaw/media`, OpenClaw's shared media root.
|
|
58
|
-
*
|
|
59
|
-
* This mirrors the directory that core's `buildMediaLocalRoots` exposes as an
|
|
60
|
-
* allowlisted location (see `openclaw/src/media/local-roots.ts`). Using it as a
|
|
61
|
-
* QQ Bot payload root lets the plugin trust framework-produced files that live
|
|
62
|
-
* in sibling subdirectories such as `outbound/` (written by
|
|
63
|
-
* `saveMediaBuffer(..., "outbound", ...)`) or `inbound/`, while still keeping
|
|
64
|
-
* the check anchored to a single, well-known directory.
|
|
65
|
-
*/
|
|
66
|
-
function getOpenClawMediaDir() {
|
|
67
|
-
return path$1.join(getHomeDir(), ".openclaw", "media");
|
|
68
|
-
}
|
|
69
|
-
function isWindows() {
|
|
70
|
-
return process.platform === "win32";
|
|
71
|
-
}
|
|
72
|
-
/** Return the preferred temporary directory. */
|
|
73
|
-
function getTempDir() {
|
|
74
|
-
return getPlatformAdapter().getTempDir();
|
|
75
|
-
}
|
|
76
|
-
let _silkWasmAvailable = null;
|
|
77
|
-
/** Check whether silk-wasm can run in the current environment. */
|
|
78
|
-
async function checkSilkWasmAvailable() {
|
|
79
|
-
if (_silkWasmAvailable !== null) return _silkWasmAvailable;
|
|
80
|
-
try {
|
|
81
|
-
const { isSilk } = await import("silk-wasm");
|
|
82
|
-
isSilk(new Uint8Array(0));
|
|
83
|
-
_silkWasmAvailable = true;
|
|
84
|
-
debugLog("[platform] silk-wasm: available");
|
|
85
|
-
} catch (err) {
|
|
86
|
-
_silkWasmAvailable = false;
|
|
87
|
-
debugWarn(`[platform] silk-wasm: NOT available (${formatErrorMessage(err)})`);
|
|
88
|
-
}
|
|
89
|
-
return _silkWasmAvailable;
|
|
90
|
-
}
|
|
91
|
-
/** Expand `~` to the current user's home directory. */
|
|
92
|
-
function expandTilde(p) {
|
|
93
|
-
if (!p) return p;
|
|
94
|
-
if (p === "~") return getHomeDir();
|
|
95
|
-
if (p.startsWith("~/") || p.startsWith("~\\")) return path$1.join(getHomeDir(), p.slice(2));
|
|
96
|
-
return p;
|
|
97
|
-
}
|
|
98
|
-
/** Normalize a user-provided path by trimming, stripping `file://`, and expanding `~`. */
|
|
99
|
-
function normalizePath(p) {
|
|
100
|
-
let result = p.trim();
|
|
101
|
-
if (result.startsWith("file://")) {
|
|
102
|
-
result = result.slice(7);
|
|
103
|
-
try {
|
|
104
|
-
result = decodeURIComponent(result);
|
|
105
|
-
} catch {}
|
|
106
|
-
}
|
|
107
|
-
return expandTilde(result);
|
|
108
|
-
}
|
|
109
|
-
/** Return true when the string looks like a local filesystem path rather than a URL. */
|
|
110
|
-
function isLocalPath(p) {
|
|
111
|
-
if (!p) return false;
|
|
112
|
-
if (p.startsWith("file://")) return true;
|
|
113
|
-
if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) return true;
|
|
114
|
-
if (p.startsWith("/")) return true;
|
|
115
|
-
if (/^[a-zA-Z]:[\\/]/.test(p)) return true;
|
|
116
|
-
if (p.startsWith("\\\\")) return true;
|
|
117
|
-
if (p.startsWith("./") || p.startsWith("../")) return true;
|
|
118
|
-
if (p.startsWith(".\\") || p.startsWith("..\\")) return true;
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
function isPathWithinRoot(candidate, root) {
|
|
122
|
-
const relative = path$1.relative(root, candidate);
|
|
123
|
-
return relative === "" || !relative.startsWith("..") && !path$1.isAbsolute(relative);
|
|
124
|
-
}
|
|
125
|
-
/** Remap legacy or hallucinated QQ Bot local media paths to real files when possible. */
|
|
126
|
-
function resolveQQBotLocalMediaPath(p) {
|
|
127
|
-
const normalized = normalizePath(p);
|
|
128
|
-
if (!isLocalPath(normalized) || fs$1.existsSync(normalized)) return normalized;
|
|
129
|
-
const homeDir = getHomeDir();
|
|
130
|
-
const mediaRoot = getQQBotMediaPath();
|
|
131
|
-
const dataRoot = getQQBotDataPath();
|
|
132
|
-
const candidateRoots = [
|
|
133
|
-
{
|
|
134
|
-
from: path$1.join(homeDir, ".openclaw", "workspace", "qqbot"),
|
|
135
|
-
to: mediaRoot
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
from: dataRoot,
|
|
139
|
-
to: mediaRoot
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
from: mediaRoot,
|
|
143
|
-
to: dataRoot
|
|
144
|
-
}
|
|
145
|
-
];
|
|
146
|
-
for (const { from, to } of candidateRoots) {
|
|
147
|
-
if (!isPathWithinRoot(normalized, from)) continue;
|
|
148
|
-
const relative = path$1.relative(from, normalized);
|
|
149
|
-
const candidate = path$1.join(to, relative);
|
|
150
|
-
if (fs$1.existsSync(candidate)) {
|
|
151
|
-
debugWarn(`[platform] Remapped missing QQBot media path ${normalized} -> ${candidate}`);
|
|
152
|
-
return candidate;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return normalized;
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Resolve a structured-payload local file path and enforce that it stays within
|
|
159
|
-
* QQ Bot-owned storage roots.
|
|
160
|
-
*/
|
|
161
|
-
function resolveQQBotPayloadLocalFilePath(p) {
|
|
162
|
-
const candidate = resolveQQBotLocalMediaPath(p);
|
|
163
|
-
if (!candidate.trim()) return null;
|
|
164
|
-
const resolvedCandidate = path$1.resolve(candidate);
|
|
165
|
-
if (!fs$1.existsSync(resolvedCandidate)) return null;
|
|
166
|
-
const canonicalCandidate = fs$1.realpathSync(resolvedCandidate);
|
|
167
|
-
const allowedRoots = [getOpenClawMediaDir(), getQQBotMediaPath()];
|
|
168
|
-
for (const root of allowedRoots) {
|
|
169
|
-
const resolvedRoot = path$1.resolve(root);
|
|
170
|
-
if (isPathWithinRoot(canonicalCandidate, fs$1.existsSync(resolvedRoot) ? fs$1.realpathSync(resolvedRoot) : resolvedRoot)) return canonicalCandidate;
|
|
171
|
-
}
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region extensions/qqbot/src/engine/messaging/target-parser.ts
|
|
176
|
-
/**
|
|
177
|
-
* Parse a qqbot target string into a structured delivery target.
|
|
178
|
-
*
|
|
179
|
-
* Supported formats:
|
|
180
|
-
* - `qqbot:c2c:openid` → C2C direct message
|
|
181
|
-
* - `qqbot:group:groupid` → Group message
|
|
182
|
-
* - `qqbot:channel:channelid` → Channel message
|
|
183
|
-
* - `c2c:openid` → C2C (without qqbot: prefix)
|
|
184
|
-
* - `group:groupid` → Group (without qqbot: prefix)
|
|
185
|
-
* - `channel:channelid` → Channel (without qqbot: prefix)
|
|
186
|
-
* - `openid` → C2C (bare openid, default)
|
|
187
|
-
*
|
|
188
|
-
* @param to - Raw target string.
|
|
189
|
-
* @returns Parsed target with type and id.
|
|
190
|
-
* @throws {Error} When the target format is invalid.
|
|
191
|
-
*/
|
|
192
|
-
function parseTarget(to) {
|
|
193
|
-
let id = to.replace(/^qqbot:/i, "");
|
|
194
|
-
if (id.startsWith("c2c:")) {
|
|
195
|
-
const userId = id.slice(4);
|
|
196
|
-
if (!userId) throw new Error(`Invalid c2c target format: ${to} - missing user ID`);
|
|
197
|
-
return {
|
|
198
|
-
type: "c2c",
|
|
199
|
-
id: userId
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
if (id.startsWith("group:")) {
|
|
203
|
-
const groupId = id.slice(6);
|
|
204
|
-
if (!groupId) throw new Error(`Invalid group target format: ${to} - missing group ID`);
|
|
205
|
-
return {
|
|
206
|
-
type: "group",
|
|
207
|
-
id: groupId
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
if (id.startsWith("channel:")) {
|
|
211
|
-
const channelId = id.slice(8);
|
|
212
|
-
if (!channelId) throw new Error(`Invalid channel target format: ${to} - missing channel ID`);
|
|
213
|
-
return {
|
|
214
|
-
type: "channel",
|
|
215
|
-
id: channelId
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
if (!id) throw new Error(`Invalid target format: ${to} - empty ID after removing qqbot: prefix`);
|
|
219
|
-
return {
|
|
220
|
-
type: "c2c",
|
|
221
|
-
id
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Normalize a QQ Bot target string into the canonical `qqbot:...` form.
|
|
226
|
-
*
|
|
227
|
-
* Returns `undefined` when the target does not look like a QQ Bot address.
|
|
228
|
-
*/
|
|
229
|
-
function normalizeTarget(target) {
|
|
230
|
-
const id = target.replace(/^qqbot:/i, "");
|
|
231
|
-
if (id.startsWith("c2c:") || id.startsWith("group:") || id.startsWith("channel:")) return `qqbot:${id}`;
|
|
232
|
-
if (/^[0-9a-fA-F]{32}$/.test(id)) return `qqbot:c2c:${id}`;
|
|
233
|
-
if (/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id)) return `qqbot:c2c:${id}`;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Return true when the string looks like a QQ Bot target ID.
|
|
237
|
-
*/
|
|
238
|
-
function looksLikeQQBotTarget(id) {
|
|
239
|
-
if (/^qqbot:(c2c|group|channel):/i.test(id)) return true;
|
|
240
|
-
if (/^(c2c|group|channel):/i.test(id)) return true;
|
|
241
|
-
if (/^[0-9a-fA-F]{32}$/.test(id)) return true;
|
|
242
|
-
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
|
|
243
|
-
}
|
|
244
|
-
//#endregion
|
|
245
|
-
export { getHomeDir as a, getQQBotMediaDir as c, isLocalPath as d, isWindows as f, checkSilkWasmAvailable as i, getQQBotMediaPath as l, resolveQQBotPayloadLocalFilePath as m, normalizeTarget as n, getQQBotDataDir as o, normalizePath as p, parseTarget as r, getQQBotDataPath as s, looksLikeQQBotTarget as t, getTempDir as u };
|