@openclaw/qqbot 2026.5.2-beta.2 → 2026.5.3-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.
Files changed (203) hide show
  1. package/dist/api.js +646 -0
  2. package/dist/approval-cg0SVahb.js +94 -0
  3. package/dist/channel-N6Y_Rcjp.js +551 -0
  4. package/dist/channel-plugin-api.js +2 -0
  5. package/dist/channel.setup-ByCRAe5H.js +25 -0
  6. package/dist/config-D6545NkC.js +94 -0
  7. package/dist/config-schema-DFcjQw73.js +309 -0
  8. package/dist/exec-approvals-COUsM6wZ.js +138 -0
  9. package/dist/gateway-CmSUJKSt.js +6726 -0
  10. package/dist/handler-runtime-Bqm6N0WG.js +120 -0
  11. package/dist/index.js +29 -0
  12. package/dist/narrowing-BoieBTIU.js +25 -0
  13. package/dist/outbound-BJfhwrPg.js +1357 -0
  14. package/dist/request-context-DXtpwWui.js +921 -0
  15. package/dist/resolve-D_06fV6-.js +284 -0
  16. package/dist/runtime-BJAS3eXW.js +18 -0
  17. package/dist/runtime-api.js +2 -0
  18. package/dist/secret-contract-api.js +56 -0
  19. package/dist/sender-p-B14eLG.js +1987 -0
  20. package/dist/setup-entry.js +15 -0
  21. package/dist/setup-plugin-api.js +2 -0
  22. package/dist/string-normalize-Ci6NM5DE.js +98 -0
  23. package/dist/target-parser-Y0prnrXD.js +245 -0
  24. package/package.json +15 -6
  25. package/api.ts +0 -56
  26. package/channel-plugin-api.ts +0 -1
  27. package/index.ts +0 -29
  28. package/runtime-api.ts +0 -9
  29. package/setup-entry.ts +0 -9
  30. package/setup-plugin-api.ts +0 -3
  31. package/src/bridge/approval/capability.ts +0 -237
  32. package/src/bridge/approval/handler-runtime.ts +0 -204
  33. package/src/bridge/bootstrap.ts +0 -135
  34. package/src/bridge/channel-entry.ts +0 -18
  35. package/src/bridge/commands/framework-context-adapter.ts +0 -60
  36. package/src/bridge/commands/framework-registration.ts +0 -47
  37. package/src/bridge/commands/from-parser.test.ts +0 -86
  38. package/src/bridge/commands/from-parser.ts +0 -60
  39. package/src/bridge/commands/result-dispatcher.ts +0 -76
  40. package/src/bridge/config-shared.ts +0 -132
  41. package/src/bridge/config.ts +0 -111
  42. package/src/bridge/gateway.ts +0 -174
  43. package/src/bridge/logger.ts +0 -31
  44. package/src/bridge/narrowing.ts +0 -31
  45. package/src/bridge/plugin-version.test.ts +0 -146
  46. package/src/bridge/plugin-version.ts +0 -102
  47. package/src/bridge/runtime.ts +0 -25
  48. package/src/bridge/sdk-adapter.ts +0 -131
  49. package/src/bridge/setup/finalize.ts +0 -144
  50. package/src/bridge/setup/surface.ts +0 -34
  51. package/src/bridge/tools/channel.ts +0 -58
  52. package/src/bridge/tools/index.ts +0 -15
  53. package/src/bridge/tools/remind.test.ts +0 -124
  54. package/src/bridge/tools/remind.ts +0 -91
  55. package/src/channel.setup.ts +0 -33
  56. package/src/channel.ts +0 -288
  57. package/src/command-auth.test.ts +0 -62
  58. package/src/config-schema.ts +0 -84
  59. package/src/config.test.ts +0 -364
  60. package/src/engine/access/access-control.test.ts +0 -198
  61. package/src/engine/access/access-control.ts +0 -226
  62. package/src/engine/access/index.ts +0 -16
  63. package/src/engine/access/resolve-policy.test.ts +0 -59
  64. package/src/engine/access/resolve-policy.ts +0 -57
  65. package/src/engine/access/sender-match.test.ts +0 -60
  66. package/src/engine/access/sender-match.ts +0 -55
  67. package/src/engine/access/types.ts +0 -53
  68. package/src/engine/adapter/audio.port.ts +0 -27
  69. package/src/engine/adapter/commands.port.ts +0 -22
  70. package/src/engine/adapter/history.port.ts +0 -52
  71. package/src/engine/adapter/index.ts +0 -139
  72. package/src/engine/adapter/mention-gate.port.ts +0 -50
  73. package/src/engine/adapter/types.ts +0 -38
  74. package/src/engine/api/api-client.ts +0 -212
  75. package/src/engine/api/media-chunked.test.ts +0 -336
  76. package/src/engine/api/media-chunked.ts +0 -622
  77. package/src/engine/api/media.ts +0 -218
  78. package/src/engine/api/messages.ts +0 -293
  79. package/src/engine/api/retry.ts +0 -217
  80. package/src/engine/api/routes.ts +0 -95
  81. package/src/engine/api/token.ts +0 -271
  82. package/src/engine/approval/index.test.ts +0 -22
  83. package/src/engine/approval/index.ts +0 -224
  84. package/src/engine/commands/builtin/log-helpers.ts +0 -319
  85. package/src/engine/commands/builtin/register-all.ts +0 -17
  86. package/src/engine/commands/builtin/register-approve.ts +0 -201
  87. package/src/engine/commands/builtin/register-basic.ts +0 -95
  88. package/src/engine/commands/builtin/register-clear-storage.ts +0 -187
  89. package/src/engine/commands/builtin/register-logs.ts +0 -20
  90. package/src/engine/commands/builtin/register-streaming.ts +0 -137
  91. package/src/engine/commands/builtin/state.ts +0 -31
  92. package/src/engine/commands/slash-command-auth.ts +0 -48
  93. package/src/engine/commands/slash-command-handler.ts +0 -146
  94. package/src/engine/commands/slash-commands-impl.test.ts +0 -8
  95. package/src/engine/commands/slash-commands-impl.ts +0 -61
  96. package/src/engine/commands/slash-commands.ts +0 -199
  97. package/src/engine/config/credential-backup.test.ts +0 -88
  98. package/src/engine/config/credential-backup.ts +0 -107
  99. package/src/engine/config/credentials.ts +0 -76
  100. package/src/engine/config/group.test.ts +0 -234
  101. package/src/engine/config/group.ts +0 -299
  102. package/src/engine/config/resolve.test.ts +0 -152
  103. package/src/engine/config/resolve.ts +0 -283
  104. package/src/engine/config/setup-logic.ts +0 -84
  105. package/src/engine/engine-import-boundary.test.ts +0 -73
  106. package/src/engine/gateway/codec.ts +0 -47
  107. package/src/engine/gateway/constants.ts +0 -117
  108. package/src/engine/gateway/event-dispatcher.ts +0 -177
  109. package/src/engine/gateway/gateway-connection.ts +0 -371
  110. package/src/engine/gateway/gateway.ts +0 -291
  111. package/src/engine/gateway/inbound-attachments.test.ts +0 -126
  112. package/src/engine/gateway/inbound-attachments.ts +0 -360
  113. package/src/engine/gateway/inbound-context.ts +0 -195
  114. package/src/engine/gateway/inbound-pipeline.self-echo.test.ts +0 -218
  115. package/src/engine/gateway/inbound-pipeline.ts +0 -235
  116. package/src/engine/gateway/interaction-handler.ts +0 -220
  117. package/src/engine/gateway/message-queue.test.ts +0 -282
  118. package/src/engine/gateway/message-queue.ts +0 -499
  119. package/src/engine/gateway/outbound-dispatch.test.ts +0 -231
  120. package/src/engine/gateway/outbound-dispatch.ts +0 -575
  121. package/src/engine/gateway/reconnect.ts +0 -199
  122. package/src/engine/gateway/stages/access-stage.ts +0 -132
  123. package/src/engine/gateway/stages/assembly-stage.ts +0 -156
  124. package/src/engine/gateway/stages/content-stage.test.ts +0 -77
  125. package/src/engine/gateway/stages/content-stage.ts +0 -77
  126. package/src/engine/gateway/stages/envelope-stage.test.ts +0 -152
  127. package/src/engine/gateway/stages/envelope-stage.ts +0 -144
  128. package/src/engine/gateway/stages/group-gate-stage.ts +0 -292
  129. package/src/engine/gateway/stages/index.ts +0 -18
  130. package/src/engine/gateway/stages/quote-stage.ts +0 -113
  131. package/src/engine/gateway/stages/refidx-stage.ts +0 -62
  132. package/src/engine/gateway/stages/stub-contexts.ts +0 -116
  133. package/src/engine/gateway/types.ts +0 -264
  134. package/src/engine/gateway/typing-keepalive.ts +0 -79
  135. package/src/engine/group/activation.test.ts +0 -114
  136. package/src/engine/group/activation.ts +0 -147
  137. package/src/engine/group/history.test.ts +0 -314
  138. package/src/engine/group/history.ts +0 -321
  139. package/src/engine/group/mention.test.ts +0 -141
  140. package/src/engine/group/mention.ts +0 -197
  141. package/src/engine/group/message-gating.test.ts +0 -188
  142. package/src/engine/group/message-gating.ts +0 -216
  143. package/src/engine/messaging/decode-media-path.ts +0 -82
  144. package/src/engine/messaging/media-source.ts +0 -215
  145. package/src/engine/messaging/media-type-detect.ts +0 -37
  146. package/src/engine/messaging/outbound-audio-port.ts +0 -38
  147. package/src/engine/messaging/outbound-deliver.ts +0 -810
  148. package/src/engine/messaging/outbound-media-send.ts +0 -702
  149. package/src/engine/messaging/outbound-reply.ts +0 -27
  150. package/src/engine/messaging/outbound-result-helpers.ts +0 -54
  151. package/src/engine/messaging/outbound-types.ts +0 -45
  152. package/src/engine/messaging/outbound.ts +0 -485
  153. package/src/engine/messaging/reply-dispatcher.ts +0 -597
  154. package/src/engine/messaging/reply-limiter.ts +0 -164
  155. package/src/engine/messaging/sender.ts +0 -729
  156. package/src/engine/messaging/streaming-c2c.ts +0 -1192
  157. package/src/engine/messaging/streaming-media-send.ts +0 -544
  158. package/src/engine/messaging/target-parser.ts +0 -104
  159. package/src/engine/ref/format-message-ref.ts +0 -142
  160. package/src/engine/ref/format-ref-entry.test.ts +0 -60
  161. package/src/engine/ref/format-ref-entry.ts +0 -27
  162. package/src/engine/ref/store.ts +0 -208
  163. package/src/engine/ref/types.ts +0 -27
  164. package/src/engine/session/known-users.ts +0 -137
  165. package/src/engine/session/session-store.ts +0 -204
  166. package/src/engine/tools/channel-api.ts +0 -244
  167. package/src/engine/tools/remind-logic.test.ts +0 -280
  168. package/src/engine/tools/remind-logic.ts +0 -377
  169. package/src/engine/types.ts +0 -313
  170. package/src/engine/utils/attachment-tags.test.ts +0 -186
  171. package/src/engine/utils/attachment-tags.ts +0 -174
  172. package/src/engine/utils/audio.test.ts +0 -250
  173. package/src/engine/utils/audio.ts +0 -585
  174. package/src/engine/utils/data-paths.ts +0 -38
  175. package/src/engine/utils/diagnostics.ts +0 -109
  176. package/src/engine/utils/file-utils.test.ts +0 -72
  177. package/src/engine/utils/file-utils.ts +0 -225
  178. package/src/engine/utils/format.test.ts +0 -68
  179. package/src/engine/utils/format.ts +0 -70
  180. package/src/engine/utils/image-size.test.ts +0 -158
  181. package/src/engine/utils/image-size.ts +0 -249
  182. package/src/engine/utils/log.test.ts +0 -28
  183. package/src/engine/utils/log.ts +0 -61
  184. package/src/engine/utils/media-tags.test.ts +0 -32
  185. package/src/engine/utils/media-tags.ts +0 -177
  186. package/src/engine/utils/payload.test.ts +0 -68
  187. package/src/engine/utils/payload.ts +0 -145
  188. package/src/engine/utils/platform-storage-laziness.test.ts +0 -63
  189. package/src/engine/utils/platform.test.ts +0 -148
  190. package/src/engine/utils/platform.ts +0 -343
  191. package/src/engine/utils/request-context.ts +0 -60
  192. package/src/engine/utils/string-normalize.ts +0 -91
  193. package/src/engine/utils/stt.test.ts +0 -104
  194. package/src/engine/utils/stt.ts +0 -100
  195. package/src/engine/utils/text-parsing.test.ts +0 -29
  196. package/src/engine/utils/text-parsing.ts +0 -155
  197. package/src/engine/utils/upload-cache.ts +0 -96
  198. package/src/engine/utils/voice-text.ts +0 -15
  199. package/src/exec-approvals.ts +0 -218
  200. package/src/manifest-schema.test.ts +0 -56
  201. package/src/qqbot-test-support.ts +0 -29
  202. package/src/types.ts +0 -210
  203. package/tsconfig.json +0 -16
@@ -0,0 +1,15 @@
1
+ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
2
+ //#region extensions/qqbot/setup-entry.ts
3
+ var setup_entry_default = defineBundledChannelSetupEntry({
4
+ importMetaUrl: import.meta.url,
5
+ plugin: {
6
+ specifier: "./setup-plugin-api.js",
7
+ exportName: "qqbotSetupPlugin"
8
+ },
9
+ secrets: {
10
+ specifier: "./secret-contract-api.js",
11
+ exportName: "channelSecrets"
12
+ }
13
+ });
14
+ //#endregion
15
+ export { setup_entry_default as default };
@@ -0,0 +1,2 @@
1
+ import { t as qqbotSetupPlugin } from "./channel.setup-ByCRAe5H.js";
2
+ export { qqbotSetupPlugin };
@@ -0,0 +1,98 @@
1
+ //#region extensions/qqbot/src/engine/adapter/index.ts
2
+ let _adapter = null;
3
+ let _adapterFactory = null;
4
+ /** Register the platform adapter. Called once during startup. */
5
+ function registerPlatformAdapter(adapter) {
6
+ _adapter = adapter;
7
+ }
8
+ /**
9
+ * Register a factory that creates the PlatformAdapter on first access.
10
+ *
11
+ * This decouples adapter availability from side-effect import ordering.
12
+ * The factory is invoked at most once — on the first `getPlatformAdapter()`
13
+ * call when no adapter has been explicitly registered yet.
14
+ */
15
+ function registerPlatformAdapterFactory(factory) {
16
+ _adapterFactory = factory;
17
+ }
18
+ /**
19
+ * Get the registered platform adapter.
20
+ *
21
+ * If no adapter has been explicitly registered yet but a factory was provided
22
+ * via `registerPlatformAdapterFactory()`, the factory is invoked to create
23
+ * and register the adapter automatically.
24
+ */
25
+ function getPlatformAdapter() {
26
+ if (!_adapter && _adapterFactory) _adapter = _adapterFactory();
27
+ if (!_adapter) throw new Error("PlatformAdapter not registered. Call registerPlatformAdapter() during bootstrap.");
28
+ return _adapter;
29
+ }
30
+ /** Check whether a platform adapter has been registered (or can be created from a factory). */
31
+ function hasPlatformAdapter() {
32
+ return _adapter !== null || _adapterFactory !== null;
33
+ }
34
+ //#endregion
35
+ //#region extensions/qqbot/src/engine/utils/string-normalize.ts
36
+ /**
37
+ * String normalization and record-coercion helpers.
38
+ *
39
+ * These are self-contained re-implementations of the functions that
40
+ * the plugin previously imported from `openclaw/plugin-sdk/text-runtime`
41
+ * and `openclaw/plugin-sdk/text-runtime` (via record-coerce / string-coerce).
42
+ *
43
+ * core/ modules use these instead of importing plugin-sdk, keeping the
44
+ * shared layer portable between the built-in and standalone versions.
45
+ */
46
+ /** Return the trimmed string or `null` when the value is not a non-empty string. */
47
+ function normalizeNullableString(value) {
48
+ if (typeof value !== "string") return null;
49
+ const trimmed = value.trim();
50
+ return trimmed ? trimmed : null;
51
+ }
52
+ /** Return the trimmed string or `undefined` when the value is not a non-empty string. */
53
+ function normalizeOptionalString(value) {
54
+ return normalizeNullableString(value) ?? void 0;
55
+ }
56
+ /**
57
+ * Stringify then normalize. Accepts `string | number | boolean | bigint`.
58
+ * Returns `undefined` for objects, arrays, null, and undefined.
59
+ */
60
+ function normalizeStringifiedOptionalString(value) {
61
+ if (typeof value === "string") return normalizeOptionalString(value);
62
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return normalizeOptionalString(String(value));
63
+ }
64
+ /** Return the trimmed lowercase string or `undefined`. */
65
+ function normalizeOptionalLowercaseString(value) {
66
+ return normalizeOptionalString(value)?.toLowerCase();
67
+ }
68
+ /** Return the trimmed lowercase string or `""`. */
69
+ function normalizeLowercaseStringOrEmpty(value) {
70
+ return normalizeOptionalLowercaseString(value) ?? "";
71
+ }
72
+ /** Coerce a value into a `Record<string, unknown>` or `undefined`. */
73
+ function asOptionalObjectRecord(value) {
74
+ return value && typeof value === "object" ? value : void 0;
75
+ }
76
+ /** Read a string field from a record. */
77
+ function readStringField(record, key) {
78
+ const v = record?.[key];
79
+ return typeof v === "string" ? v : void 0;
80
+ }
81
+ /**
82
+ * Normalize filenames into a UTF-8 form that the QQ Bot API accepts reliably.
83
+ *
84
+ * Decodes percent-escaped names, converts Unicode to NFC, and strips
85
+ * ASCII control characters.
86
+ */
87
+ function sanitizeFileName(name) {
88
+ if (!name) return name;
89
+ let result = name.trim();
90
+ if (result.includes("%")) try {
91
+ result = decodeURIComponent(result);
92
+ } catch {}
93
+ result = result.normalize("NFC");
94
+ result = result.replace(/\p{Cc}/gu, "");
95
+ return result;
96
+ }
97
+ //#endregion
98
+ export { normalizeStringifiedOptionalString as a, getPlatformAdapter as c, registerPlatformAdapterFactory as d, normalizeOptionalString as i, hasPlatformAdapter as l, normalizeLowercaseStringOrEmpty as n, readStringField as o, normalizeOptionalLowercaseString as r, sanitizeFileName as s, asOptionalObjectRecord as t, registerPlatformAdapter as u };
@@ -0,0 +1,245 @@
1
+ import { c as getPlatformAdapter } from "./string-normalize-Ci6NM5DE.js";
2
+ import { C as debugLog, L as formatErrorMessage, w as debugWarn } from "./sender-p-B14eLG.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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.5.2-beta.2",
3
+ "version": "2026.5.3-beta.1",
4
4
  "private": false,
5
5
  "description": "OpenClaw QQ Bot channel plugin",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "openclaw": "workspace:*"
22
22
  },
23
23
  "peerDependencies": {
24
- "openclaw": ">=2026.5.2-beta.2"
24
+ "openclaw": ">=2026.5.3-beta.1"
25
25
  },
26
26
  "peerDependenciesMeta": {
27
27
  "openclaw": {
@@ -50,14 +50,23 @@
50
50
  "minHostVersion": ">=2026.4.10"
51
51
  },
52
52
  "compat": {
53
- "pluginApi": ">=2026.5.2-beta.2"
53
+ "pluginApi": ">=2026.5.3-beta.1"
54
54
  },
55
55
  "build": {
56
- "openclawVersion": "2026.5.2-beta.2"
56
+ "openclawVersion": "2026.5.3-beta.1"
57
57
  },
58
58
  "release": {
59
59
  "publishToClawHub": true,
60
60
  "publishToNpm": true
61
- }
62
- }
61
+ },
62
+ "runtimeExtensions": [
63
+ "./dist/index.js"
64
+ ],
65
+ "runtimeSetupEntry": "./dist/setup-entry.js"
66
+ },
67
+ "files": [
68
+ "dist/**",
69
+ "openclaw.plugin.json",
70
+ "skills/**"
71
+ ]
63
72
  }
package/api.ts DELETED
@@ -1,56 +0,0 @@
1
- export { qqbotPlugin } from "./src/channel.js";
2
- export { qqbotSetupPlugin } from "./src/channel.setup.js";
3
- export { getFrameworkCommands } from "./src/engine/commands/slash-commands-impl.js";
4
- export { registerChannelTool } from "./src/bridge/tools/channel.js";
5
- export { registerRemindTool } from "./src/bridge/tools/remind.js";
6
- export { registerQQBotTools } from "./src/bridge/tools/index.js";
7
- export { registerQQBotFull } from "./src/bridge/channel-entry.js";
8
- export {
9
- type AudioFormatPolicy,
10
- type C2CMessageEvent,
11
- type GroupMessageEvent,
12
- type GuildMessageEvent,
13
- type MessageAttachment,
14
- type QQBotAccountConfig,
15
- type QQBotConfig,
16
- type QQBotDmPolicy,
17
- type QQBotExecApprovalConfig,
18
- type QQBotGroupPolicy,
19
- type ResolvedQQBotAccount,
20
- type WSPayload,
21
- } from "./src/types.js";
22
- export {
23
- applyQQBotAccountConfig,
24
- DEFAULT_ACCOUNT_ID,
25
- listQQBotAccountIds,
26
- resolveDefaultQQBotAccountId,
27
- resolveQQBotAccount,
28
- } from "./src/bridge/config.js";
29
- export {
30
- buildMediaTarget,
31
- checkMessageReplyLimit,
32
- DEFAULT_MEDIA_SEND_ERROR,
33
- getMessageReplyConfig,
34
- getMessageReplyStats,
35
- type MediaOutboundContext,
36
- type MediaTargetContext,
37
- MESSAGE_REPLY_LIMIT,
38
- OUTBOUND_ERROR_CODES,
39
- type OutboundContext,
40
- type OutboundErrorCode,
41
- type OutboundResult,
42
- parseTarget,
43
- recordMessageReply,
44
- type ReplyLimitResult,
45
- resolveOutboundMediaPath,
46
- resolveUserFacingMediaError,
47
- sendCronMessage,
48
- sendDocument,
49
- sendMedia,
50
- sendPhoto,
51
- sendProactiveMessage,
52
- sendText,
53
- sendVideoMsg,
54
- sendVoice,
55
- setOutboundAudioPort,
56
- } from "./src/engine/messaging/outbound.js";
@@ -1 +0,0 @@
1
- export { qqbotPlugin } from "./src/channel.js";
package/index.ts DELETED
@@ -1,29 +0,0 @@
1
- import {
2
- defineBundledChannelEntry,
3
- loadBundledEntryExportSync,
4
- type OpenClawPluginApi,
5
- } from "openclaw/plugin-sdk/channel-entry-contract";
6
-
7
- function registerQQBotFull(api: OpenClawPluginApi): void {
8
- const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
9
- specifier: "./api.js",
10
- exportName: "registerQQBotFull",
11
- });
12
- register(api);
13
- }
14
-
15
- export default defineBundledChannelEntry({
16
- id: "qqbot",
17
- name: "QQ Bot",
18
- description: "QQ Bot channel plugin",
19
- importMetaUrl: import.meta.url,
20
- plugin: {
21
- specifier: "./channel-plugin-api.js",
22
- exportName: "qqbotPlugin",
23
- },
24
- runtime: {
25
- specifier: "./runtime-api.js",
26
- exportName: "setQQBotRuntime",
27
- },
28
- registerFull: registerQQBotFull,
29
- });
package/runtime-api.ts DELETED
@@ -1,9 +0,0 @@
1
- export type { ChannelPlugin, OpenClawPluginApi, PluginRuntime } from "openclaw/plugin-sdk/core";
2
- export type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
3
- export type {
4
- OpenClawPluginService,
5
- OpenClawPluginServiceContext,
6
- PluginLogger,
7
- } from "openclaw/plugin-sdk/core";
8
- export type { ResolvedQQBotAccount, QQBotAccountConfig } from "./src/types.js";
9
- export { getQQBotRuntime, setQQBotRuntime } from "./src/bridge/runtime.js";
package/setup-entry.ts DELETED
@@ -1,9 +0,0 @@
1
- import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
2
-
3
- export default defineBundledChannelSetupEntry({
4
- importMetaUrl: import.meta.url,
5
- plugin: {
6
- specifier: "./setup-plugin-api.js",
7
- exportName: "qqbotSetupPlugin",
8
- },
9
- });
@@ -1,3 +0,0 @@
1
- // Keep bundled setup entry imports narrow so setup loads do not pull the
2
- // broader QQ Bot runtime plugin surface.
3
- export { qqbotSetupPlugin } from "./src/channel.setup.js";