@leoqlin/openclaw-qqbot 1.6.7-alpha1

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 (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +484 -0
  3. package/README.zh.md +479 -0
  4. package/bin/qqbot-cli.js +243 -0
  5. package/dist/index.d.ts +17 -0
  6. package/dist/index.js +26 -0
  7. package/dist/src/admin-resolver.d.ts +33 -0
  8. package/dist/src/admin-resolver.js +157 -0
  9. package/dist/src/api.d.ts +301 -0
  10. package/dist/src/api.js +890 -0
  11. package/dist/src/channel.d.ts +29 -0
  12. package/dist/src/channel.js +452 -0
  13. package/dist/src/config.d.ts +56 -0
  14. package/dist/src/config.js +278 -0
  15. package/dist/src/credential-backup.d.ts +31 -0
  16. package/dist/src/credential-backup.js +66 -0
  17. package/dist/src/deliver-debounce.d.ts +74 -0
  18. package/dist/src/deliver-debounce.js +174 -0
  19. package/dist/src/gateway.d.ts +18 -0
  20. package/dist/src/gateway.js +2005 -0
  21. package/dist/src/group-history.d.ts +136 -0
  22. package/dist/src/group-history.js +226 -0
  23. package/dist/src/image-server.d.ts +87 -0
  24. package/dist/src/image-server.js +570 -0
  25. package/dist/src/inbound-attachments.d.ts +60 -0
  26. package/dist/src/inbound-attachments.js +248 -0
  27. package/dist/src/known-users.d.ts +100 -0
  28. package/dist/src/known-users.js +263 -0
  29. package/dist/src/message-gating.d.ts +53 -0
  30. package/dist/src/message-gating.js +107 -0
  31. package/dist/src/message-queue.d.ts +89 -0
  32. package/dist/src/message-queue.js +257 -0
  33. package/dist/src/onboarding.d.ts +10 -0
  34. package/dist/src/onboarding.js +203 -0
  35. package/dist/src/outbound-deliver.d.ts +48 -0
  36. package/dist/src/outbound-deliver.js +392 -0
  37. package/dist/src/outbound.d.ts +205 -0
  38. package/dist/src/outbound.js +938 -0
  39. package/dist/src/proactive.d.ts +170 -0
  40. package/dist/src/proactive.js +399 -0
  41. package/dist/src/ref-index-store.d.ts +101 -0
  42. package/dist/src/ref-index-store.js +298 -0
  43. package/dist/src/reply-dispatcher.d.ts +35 -0
  44. package/dist/src/reply-dispatcher.js +311 -0
  45. package/dist/src/request-context.d.ts +25 -0
  46. package/dist/src/request-context.js +37 -0
  47. package/dist/src/runtime.d.ts +3 -0
  48. package/dist/src/runtime.js +10 -0
  49. package/dist/src/session-store.d.ts +52 -0
  50. package/dist/src/session-store.js +254 -0
  51. package/dist/src/slash-commands.d.ts +77 -0
  52. package/dist/src/slash-commands.js +1866 -0
  53. package/dist/src/startup-greeting.d.ts +30 -0
  54. package/dist/src/startup-greeting.js +97 -0
  55. package/dist/src/streaming.d.ts +247 -0
  56. package/dist/src/streaming.js +899 -0
  57. package/dist/src/stt.d.ts +21 -0
  58. package/dist/src/stt.js +70 -0
  59. package/dist/src/tools/channel.d.ts +16 -0
  60. package/dist/src/tools/channel.js +234 -0
  61. package/dist/src/tools/remind.d.ts +2 -0
  62. package/dist/src/tools/remind.js +256 -0
  63. package/dist/src/types.d.ts +367 -0
  64. package/dist/src/types.js +17 -0
  65. package/dist/src/typing-keepalive.d.ts +27 -0
  66. package/dist/src/typing-keepalive.js +64 -0
  67. package/dist/src/update-checker.d.ts +36 -0
  68. package/dist/src/update-checker.js +171 -0
  69. package/dist/src/utils/audio-convert.d.ts +98 -0
  70. package/dist/src/utils/audio-convert.js +755 -0
  71. package/dist/src/utils/chunked-upload.d.ts +68 -0
  72. package/dist/src/utils/chunked-upload.js +341 -0
  73. package/dist/src/utils/file-utils.d.ts +61 -0
  74. package/dist/src/utils/file-utils.js +172 -0
  75. package/dist/src/utils/image-size.d.ts +51 -0
  76. package/dist/src/utils/image-size.js +234 -0
  77. package/dist/src/utils/media-send.d.ts +158 -0
  78. package/dist/src/utils/media-send.js +499 -0
  79. package/dist/src/utils/media-tags.d.ts +14 -0
  80. package/dist/src/utils/media-tags.js +165 -0
  81. package/dist/src/utils/payload.d.ts +112 -0
  82. package/dist/src/utils/payload.js +186 -0
  83. package/dist/src/utils/pkg-version.d.ts +5 -0
  84. package/dist/src/utils/pkg-version.js +61 -0
  85. package/dist/src/utils/platform.d.ts +137 -0
  86. package/dist/src/utils/platform.js +390 -0
  87. package/dist/src/utils/ssrf-guard.d.ts +25 -0
  88. package/dist/src/utils/ssrf-guard.js +91 -0
  89. package/dist/src/utils/text-parsing.d.ts +36 -0
  90. package/dist/src/utils/text-parsing.js +75 -0
  91. package/dist/src/utils/upload-cache.d.ts +34 -0
  92. package/dist/src/utils/upload-cache.js +93 -0
  93. package/index.ts +31 -0
  94. package/node_modules/@eshaz/web-worker/LICENSE +201 -0
  95. package/node_modules/@eshaz/web-worker/README.md +134 -0
  96. package/node_modules/@eshaz/web-worker/browser.js +17 -0
  97. package/node_modules/@eshaz/web-worker/cjs/browser.js +16 -0
  98. package/node_modules/@eshaz/web-worker/cjs/node.js +219 -0
  99. package/node_modules/@eshaz/web-worker/index.d.ts +4 -0
  100. package/node_modules/@eshaz/web-worker/node.js +223 -0
  101. package/node_modules/@eshaz/web-worker/package.json +54 -0
  102. package/node_modules/@wasm-audio-decoders/common/index.js +5 -0
  103. package/node_modules/@wasm-audio-decoders/common/package.json +36 -0
  104. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderCommon.js +231 -0
  105. package/node_modules/@wasm-audio-decoders/common/src/WASMAudioDecoderWorker.js +129 -0
  106. package/node_modules/@wasm-audio-decoders/common/src/puff/README +67 -0
  107. package/node_modules/@wasm-audio-decoders/common/src/puff/build_puff.js +31 -0
  108. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.c +863 -0
  109. package/node_modules/@wasm-audio-decoders/common/src/puff/puff.h +35 -0
  110. package/node_modules/@wasm-audio-decoders/common/src/utilities.js +3 -0
  111. package/node_modules/@wasm-audio-decoders/common/types.d.ts +7 -0
  112. package/node_modules/mpg123-decoder/README.md +265 -0
  113. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js +185 -0
  114. package/node_modules/mpg123-decoder/dist/mpg123-decoder.min.js.map +1 -0
  115. package/node_modules/mpg123-decoder/index.js +8 -0
  116. package/node_modules/mpg123-decoder/package.json +58 -0
  117. package/node_modules/mpg123-decoder/src/EmscriptenWasm.js +464 -0
  118. package/node_modules/mpg123-decoder/src/MPEGDecoder.js +200 -0
  119. package/node_modules/mpg123-decoder/src/MPEGDecoderWebWorker.js +21 -0
  120. package/node_modules/mpg123-decoder/types.d.ts +30 -0
  121. package/node_modules/silk-wasm/LICENSE +21 -0
  122. package/node_modules/silk-wasm/README.md +85 -0
  123. package/node_modules/silk-wasm/lib/index.cjs +16 -0
  124. package/node_modules/silk-wasm/lib/index.d.ts +70 -0
  125. package/node_modules/silk-wasm/lib/index.mjs +16 -0
  126. package/node_modules/silk-wasm/lib/silk.wasm +0 -0
  127. package/node_modules/silk-wasm/lib/utils.d.ts +4 -0
  128. package/node_modules/silk-wasm/package.json +39 -0
  129. package/node_modules/simple-yenc/.github/FUNDING.yml +1 -0
  130. package/node_modules/simple-yenc/.prettierignore +1 -0
  131. package/node_modules/simple-yenc/LICENSE +7 -0
  132. package/node_modules/simple-yenc/README.md +163 -0
  133. package/node_modules/simple-yenc/dist/esm.js +1 -0
  134. package/node_modules/simple-yenc/dist/index.js +1 -0
  135. package/node_modules/simple-yenc/package.json +50 -0
  136. package/node_modules/simple-yenc/rollup.config.js +27 -0
  137. package/node_modules/simple-yenc/src/simple-yenc.js +302 -0
  138. package/node_modules/ws/LICENSE +20 -0
  139. package/node_modules/ws/README.md +548 -0
  140. package/node_modules/ws/browser.js +8 -0
  141. package/node_modules/ws/index.js +22 -0
  142. package/node_modules/ws/lib/buffer-util.js +131 -0
  143. package/node_modules/ws/lib/constants.js +19 -0
  144. package/node_modules/ws/lib/event-target.js +292 -0
  145. package/node_modules/ws/lib/extension.js +203 -0
  146. package/node_modules/ws/lib/limiter.js +55 -0
  147. package/node_modules/ws/lib/permessage-deflate.js +528 -0
  148. package/node_modules/ws/lib/receiver.js +706 -0
  149. package/node_modules/ws/lib/sender.js +602 -0
  150. package/node_modules/ws/lib/stream.js +161 -0
  151. package/node_modules/ws/lib/subprotocol.js +62 -0
  152. package/node_modules/ws/lib/validation.js +152 -0
  153. package/node_modules/ws/lib/websocket-server.js +554 -0
  154. package/node_modules/ws/lib/websocket.js +1393 -0
  155. package/node_modules/ws/package.json +70 -0
  156. package/node_modules/ws/wrapper.mjs +21 -0
  157. package/openclaw.plugin.json +17 -0
  158. package/package.json +70 -0
  159. package/preload.cjs +33 -0
  160. package/scripts/cleanup-legacy-plugins.sh +124 -0
  161. package/scripts/link-sdk-core.cjs +185 -0
  162. package/scripts/postinstall-link-sdk.js +126 -0
  163. package/scripts/proactive-api-server.ts +369 -0
  164. package/scripts/send-proactive.ts +293 -0
  165. package/scripts/set-markdown.sh +156 -0
  166. package/scripts/test-sendmedia.ts +116 -0
  167. package/scripts/upgrade-via-npm.ps1 +460 -0
  168. package/scripts/upgrade-via-npm.sh +652 -0
  169. package/scripts/upgrade-via-source.sh +1026 -0
  170. package/skills/qqbot-channel/SKILL.md +263 -0
  171. package/skills/qqbot-channel/references/api_references.md +521 -0
  172. package/skills/qqbot-media/SKILL.md +60 -0
  173. package/skills/qqbot-remind/SKILL.md +159 -0
  174. package/src/admin-resolver.ts +181 -0
  175. package/src/api.ts +1284 -0
  176. package/src/channel.ts +477 -0
  177. package/src/config.ts +347 -0
  178. package/src/credential-backup.ts +72 -0
  179. package/src/deliver-debounce.ts +229 -0
  180. package/src/gateway.ts +2245 -0
  181. package/src/group-history.ts +328 -0
  182. package/src/image-server.ts +675 -0
  183. package/src/inbound-attachments.ts +321 -0
  184. package/src/known-users.ts +353 -0
  185. package/src/message-gating.ts +190 -0
  186. package/src/message-queue.ts +352 -0
  187. package/src/onboarding.ts +274 -0
  188. package/src/openclaw-plugin-sdk.d.ts +587 -0
  189. package/src/outbound-deliver.ts +473 -0
  190. package/src/outbound.ts +1131 -0
  191. package/src/proactive.ts +530 -0
  192. package/src/ref-index-store.ts +412 -0
  193. package/src/reply-dispatcher.ts +334 -0
  194. package/src/request-context.ts +49 -0
  195. package/src/runtime.ts +14 -0
  196. package/src/session-store.ts +303 -0
  197. package/src/slash-commands.ts +2030 -0
  198. package/src/startup-greeting.ts +120 -0
  199. package/src/streaming.ts +1077 -0
  200. package/src/stt.ts +86 -0
  201. package/src/tools/channel.ts +281 -0
  202. package/src/tools/remind.ts +308 -0
  203. package/src/types.ts +391 -0
  204. package/src/typing-keepalive.ts +59 -0
  205. package/src/update-checker.ts +186 -0
  206. package/src/utils/audio-convert.ts +859 -0
  207. package/src/utils/chunked-upload.ts +483 -0
  208. package/src/utils/file-utils.ts +193 -0
  209. package/src/utils/image-size.ts +266 -0
  210. package/src/utils/media-send.ts +631 -0
  211. package/src/utils/media-tags.ts +183 -0
  212. package/src/utils/payload.ts +265 -0
  213. package/src/utils/pkg-version.ts +64 -0
  214. package/src/utils/platform.ts +435 -0
  215. package/src/utils/ssrf-guard.ts +102 -0
  216. package/src/utils/text-parsing.ts +85 -0
  217. package/src/utils/upload-cache.ts +128 -0
  218. package/tsconfig.json +16 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "ws",
3
+ "version": "8.20.0",
4
+ "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
5
+ "keywords": [
6
+ "HyBi",
7
+ "Push",
8
+ "RFC-6455",
9
+ "WebSocket",
10
+ "WebSockets",
11
+ "real-time"
12
+ ],
13
+ "homepage": "https://github.com/websockets/ws",
14
+ "bugs": "https://github.com/websockets/ws/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/websockets/ws.git"
18
+ },
19
+ "author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
20
+ "license": "MIT",
21
+ "main": "index.js",
22
+ "exports": {
23
+ ".": {
24
+ "browser": "./browser.js",
25
+ "import": "./wrapper.mjs",
26
+ "require": "./index.js"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "browser": "browser.js",
31
+ "engines": {
32
+ "node": ">=10.0.0"
33
+ },
34
+ "files": [
35
+ "browser.js",
36
+ "index.js",
37
+ "lib/*.js",
38
+ "wrapper.mjs"
39
+ ],
40
+ "scripts": {
41
+ "test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js",
42
+ "integration": "mocha --throw-deprecation test/*.integration.js",
43
+ "lint": "eslint . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\""
44
+ },
45
+ "peerDependencies": {
46
+ "bufferutil": "^4.0.1",
47
+ "utf-8-validate": ">=5.0.2"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "bufferutil": {
51
+ "optional": true
52
+ },
53
+ "utf-8-validate": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "devDependencies": {
58
+ "@eslint/js": "^10.0.1",
59
+ "benchmark": "^2.1.4",
60
+ "bufferutil": "^4.0.1",
61
+ "eslint": "^10.0.1",
62
+ "eslint-config-prettier": "^10.0.1",
63
+ "eslint-plugin-prettier": "^5.0.0",
64
+ "globals": "^17.0.0",
65
+ "mocha": "^8.4.0",
66
+ "nyc": "^15.0.0",
67
+ "prettier": "^3.0.0",
68
+ "utf-8-validate": "^6.0.0"
69
+ }
70
+ }
@@ -0,0 +1,21 @@
1
+ import createWebSocketStream from './lib/stream.js';
2
+ import extension from './lib/extension.js';
3
+ import PerMessageDeflate from './lib/permessage-deflate.js';
4
+ import Receiver from './lib/receiver.js';
5
+ import Sender from './lib/sender.js';
6
+ import subprotocol from './lib/subprotocol.js';
7
+ import WebSocket from './lib/websocket.js';
8
+ import WebSocketServer from './lib/websocket-server.js';
9
+
10
+ export {
11
+ createWebSocketStream,
12
+ extension,
13
+ PerMessageDeflate,
14
+ Receiver,
15
+ Sender,
16
+ subprotocol,
17
+ WebSocket,
18
+ WebSocketServer
19
+ };
20
+
21
+ export default WebSocket;
@@ -0,0 +1,17 @@
1
+ {
2
+ "id": "openclaw-qqbot",
3
+ "name": "OpenClaw QQ Bot",
4
+ "description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
5
+ "channels": ["qqbot"],
6
+ "extensions": ["./preload.cjs"],
7
+ "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
8
+ "capabilities": {
9
+ "proactiveMessaging": true,
10
+ "cronJobs": true
11
+ },
12
+ "configSchema": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "properties": {}
16
+ }
17
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@leoqlin/openclaw-qqbot",
3
+ "version": "1.6.7-alpha1",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "bin": {
11
+ "openclaw-qqbot": "./bin/qqbot-cli.js",
12
+ "qqbot": "./bin/qqbot-cli.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "bin",
17
+ "src",
18
+ "skills",
19
+ "scripts",
20
+ "index.ts",
21
+ "preload.cjs",
22
+ "tsconfig.json",
23
+ "openclaw.plugin.json"
24
+ ],
25
+ "openclaw": {
26
+ "id": "openclaw-qqbot",
27
+ "extensions": [
28
+ "./preload.cjs"
29
+ ],
30
+ "channel": {
31
+ "id": "qqbot",
32
+ "label": "QQ Bot"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "build": "tsc || true",
37
+ "postbuild": "node -e \"const fs=require('fs'),p=require('path'),ext=p.join(require('os').homedir(),'.openclaw/extensions/openclaw-qqbot');if(fs.existsSync(ext)&&!fs.lstatSync(ext).isSymbolicLink()){const d=p.join(ext,'dist'),pr=p.join(ext,'preload.cjs');fs.cpSync('dist',d,{recursive:true});fs.copyFileSync('preload.cjs',pr);console.log('[postbuild] synced to',ext)}\"",
38
+ "dev": "tsc --watch",
39
+ "prepack": "npm install --omit=dev",
40
+ "postinstall": ""
41
+ },
42
+ "dependencies": {
43
+ "mpg123-decoder": "^1.0.3",
44
+ "silk-wasm": "^3.7.1",
45
+ "ws": "^8.18.0"
46
+ },
47
+ "bundledDependencies": [
48
+ "mpg123-decoder",
49
+ "silk-wasm",
50
+ "ws"
51
+ ],
52
+ "devDependencies": {
53
+ "@types/node": "^20.0.0",
54
+ "@types/ws": "^8.5.0",
55
+ "typescript": "^5.9.3"
56
+ },
57
+ "peerDependencies": {
58
+ "openclaw": "*"
59
+ },
60
+ "homepage": "https://github.com/tencent-connect/openclaw-qqbot",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/tencent-connect/openclaw-qqbot.git"
64
+ },
65
+ "bundleDependencies": [
66
+ "mpg123-decoder",
67
+ "silk-wasm",
68
+ "ws"
69
+ ]
70
+ }
package/preload.cjs ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 插件预加载入口(CJS 格式)。
3
+ *
4
+ * openclaw 框架通过 require() 加载插件,因此需要 .cjs 后缀
5
+ * 确保在 "type": "module" 的 package 中也能被正确 require()。
6
+ *
7
+ * 在 require 真正的插件代码(依赖 openclaw/plugin-sdk)之前,
8
+ * 先同步确保 node_modules/openclaw symlink 存在。
9
+ */
10
+ "use strict";
11
+
12
+ const { ensurePluginSdkSymlink } = require("./scripts/link-sdk-core.cjs");
13
+
14
+ // 1) 同步创建 symlink
15
+ ensurePluginSdkSymlink(__dirname, "[preload]");
16
+
17
+ // 2) Node 22 原生支持 CJS require() 加载 ESM 模块
18
+ // 同步加载插件入口,确保框架同步检查 register/activate 时能找到
19
+ const _pluginModule = require("./dist/index.js");
20
+
21
+ // 3) 展平 default export:框架检查 register/activate 在顶级属性
22
+ // ESM 的 export default 在 require() 后变成 { default: plugin, ... }
23
+ const _default = _pluginModule.default;
24
+ const merged = Object.assign({}, _pluginModule);
25
+ if (_default && typeof _default === "object") {
26
+ for (const key of Object.keys(_default)) {
27
+ if (!(key in merged)) {
28
+ merged[key] = _default[key];
29
+ }
30
+ }
31
+ }
32
+
33
+ module.exports = merged;
@@ -0,0 +1,124 @@
1
+ #!/bin/bash
2
+ # qqbot 插件升级脚本
3
+ # 用于清理旧版本插件并重新安装
4
+ # 兼容 clawdbot 和 openclaw 两种安装
5
+
6
+ set -e
7
+
8
+ echo "=== qqbot 插件升级脚本 ==="
9
+
10
+ # 检测使用的是 clawdbot 还是 openclaw
11
+ detect_installation() {
12
+ if [ -d "$HOME/.clawdbot" ]; then
13
+ echo "clawdbot"
14
+ elif [ -d "$HOME/.openclaw" ]; then
15
+ echo "openclaw"
16
+ else
17
+ echo ""
18
+ fi
19
+ }
20
+
21
+ # 可能的扩展目录名(原仓库 qqbot + 本仓库框架推断名 openclaw-qq)
22
+ EXTENSION_DIRS=("qqbot" "openclaw-qq" "openclaw-qqbot")
23
+
24
+ # 清理指定目录的函数
25
+ cleanup_installation() {
26
+ local APP_NAME="$1"
27
+ local APP_DIR="$HOME/.$APP_NAME"
28
+ local CONFIG_FILE="$APP_DIR/$APP_NAME.json"
29
+
30
+ echo ""
31
+ echo ">>> 处理 $APP_NAME 安装..."
32
+
33
+ # 1. 删除所有可能的旧扩展目录
34
+ for dir_name in "${EXTENSION_DIRS[@]}"; do
35
+ local ext_dir="$APP_DIR/extensions/$dir_name"
36
+ if [ -d "$ext_dir" ]; then
37
+ echo "删除旧版本插件: $ext_dir"
38
+ rm -rf "$ext_dir"
39
+ fi
40
+ done
41
+
42
+ # 2. 清理配置文件中所有可能的插件 ID 相关字段
43
+ if [ -f "$CONFIG_FILE" ]; then
44
+ echo "清理配置文件中的插件字段..."
45
+
46
+ # 使用 node 处理 JSON(比 jq 更可靠处理复杂结构)
47
+ node -e "
48
+ const fs = require('fs');
49
+ const config = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
50
+ const ids = ['qqbot', 'openclaw-qq', '@sliverp/qqbot', '@tencent-connect/qqbot', '@tencent-connect/openclaw-qq', '@tencent-connect/openclaw-qqbot', 'openclaw-qqbot'];
51
+
52
+ for (const id of ids) {
53
+ // 注意: 不删除 channels.<id>,因为里面保存了用户的 appid/secret 凭证
54
+ // 凭证与插件版本无关,清理插件时不应清除凭证
55
+
56
+ // 删除 plugins.entries.<id>
57
+ if (config.plugins && config.plugins.entries && config.plugins.entries[id]) {
58
+ delete config.plugins.entries[id];
59
+ console.log(' - 已删除 plugins.entries.' + id);
60
+ }
61
+
62
+ // 删除 plugins.installs.<id>
63
+ if (config.plugins && config.plugins.installs && config.plugins.installs[id]) {
64
+ delete config.plugins.installs[id];
65
+ console.log(' - 已删除 plugins.installs.' + id);
66
+ }
67
+
68
+ // 删除 plugins.allow 中的 <id>
69
+ if (config.plugins && Array.isArray(config.plugins.allow)) {
70
+ const before = config.plugins.allow.length;
71
+ config.plugins.allow = config.plugins.allow.filter((x) => x !== id);
72
+ if (config.plugins.allow.length !== before) {
73
+ console.log(' - 已删除 plugins.allow 项: ' + id);
74
+ }
75
+ }
76
+ }
77
+
78
+ fs.writeFileSync('$CONFIG_FILE', JSON.stringify(config, null, 2));
79
+ console.log('配置文件已更新');
80
+ "
81
+ else
82
+ echo "未找到配置文件: $CONFIG_FILE"
83
+ fi
84
+ }
85
+
86
+ # 检测并处理所有可能的安装
87
+ FOUND_INSTALLATION=""
88
+
89
+ # 检查 clawdbot
90
+ if [ -d "$HOME/.clawdbot" ]; then
91
+ cleanup_installation "clawdbot"
92
+ FOUND_INSTALLATION="clawdbot"
93
+ fi
94
+
95
+ # 检查 openclaw
96
+ if [ -d "$HOME/.openclaw" ]; then
97
+ cleanup_installation "openclaw"
98
+ FOUND_INSTALLATION="openclaw"
99
+ fi
100
+
101
+ # 检查 moltbot
102
+ if [ -d "$HOME/.moltbot" ]; then
103
+ cleanup_installation "moltbot"
104
+ FOUND_INSTALLATION="moltbot"
105
+ fi
106
+
107
+ # 如果都没找到
108
+ if [ -z "$FOUND_INSTALLATION" ]; then
109
+ echo "未找到 clawdbot / openclaw / moltbot 安装目录"
110
+ echo "请确认已安装其中之一"
111
+ exit 1
112
+ fi
113
+
114
+ # 使用检测到的安装类型作为命令
115
+ CMD="$FOUND_INSTALLATION"
116
+
117
+ echo ""
118
+ echo "=== 清理完成 ==="
119
+ echo ""
120
+ echo "接下来将执行以下命令重新安装插件:"
121
+ echo " cd /path/to/openclaw-qqbot"
122
+ echo " $CMD plugins install ."
123
+ echo " $CMD channels add --channel qqbot --token \"appid:appsecret\""
124
+ echo " $CMD gateway restart"
@@ -0,0 +1,185 @@
1
+ /**
2
+ * 公共模块:openclaw plugin-sdk symlink 创建逻辑。
3
+ *
4
+ * 被 preload.cjs 和 postinstall-link-sdk.js 共同使用,避免代码重复。
5
+ * 必须是 CJS 格式,因为 preload.cjs 需要同步 require()。
6
+ */
7
+ "use strict";
8
+
9
+ const path = require("node:path");
10
+ const fs = require("node:fs");
11
+ const { execSync } = require("node:child_process");
12
+
13
+ const CLI_NAMES = ["openclaw", "clawdbot", "moltbot"];
14
+
15
+ /**
16
+ * 比较版本号是否 >= target
17
+ * Strip pre-release suffix (e.g. "2026.3.23-2" → "2026.3.23")
18
+ */
19
+ function compareVersionGte(version, target) {
20
+ const parts = version.replace(/-.*$/, "").split(".").map(Number);
21
+ for (let i = 0; i < target.length; i++) {
22
+ const v = parts[i] || 0;
23
+ const t = target[i];
24
+ if (v > t) return true;
25
+ if (v < t) return false;
26
+ }
27
+ return true;
28
+ }
29
+
30
+ /**
31
+ * 检查 openclaw 版本是否 >= 2026.3.22(需要 symlink 的最低版本)。
32
+ * 如果无法检测版本,返回 true(保守策略:宁可多创建也不遗漏)。
33
+ */
34
+ function isOpenclawVersionRequiresSymlink() {
35
+ const REQUIRED = [2026, 3, 22];
36
+
37
+ // Strategy 1: 从全局 openclaw 的 package.json 读取版本
38
+ try {
39
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
40
+ for (const name of CLI_NAMES) {
41
+ const pkgPath = path.join(globalRoot, name, "package.json");
42
+ if (fs.existsSync(pkgPath)) {
43
+ const v = JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
44
+ if (v) return compareVersionGte(v, REQUIRED);
45
+ }
46
+ }
47
+ } catch {}
48
+
49
+ // Strategy 2: 从 CLI 命令获取版本
50
+ for (const name of CLI_NAMES) {
51
+ try {
52
+ const out = execSync(`${name} --version`, {
53
+ encoding: "utf-8",
54
+ timeout: 5000,
55
+ stdio: ["pipe", "pipe", "pipe"],
56
+ }).trim();
57
+ const m = out.match(/(\d+\.\d+\.\d+)/);
58
+ if (m) return compareVersionGte(m[1], REQUIRED);
59
+ } catch {}
60
+ }
61
+
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * 查找全局 openclaw 安装路径。
67
+ * 三种策略依次尝试:npm root -g、which <cli>、从 extensions 目录推断。
68
+ */
69
+ function findOpenclawRoot(pluginRoot) {
70
+ // Strategy 1: npm root -g
71
+ try {
72
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
73
+ for (const name of CLI_NAMES) {
74
+ const candidate = path.join(globalRoot, name);
75
+ if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
76
+ }
77
+ } catch {}
78
+
79
+ // Strategy 2: which <cli>
80
+ const whichCmd = process.platform === "win32" ? "where" : "which";
81
+ for (const name of CLI_NAMES) {
82
+ try {
83
+ const bin = execSync(`${whichCmd} ${name}`, {
84
+ encoding: "utf-8",
85
+ timeout: 5000,
86
+ stdio: ["pipe", "pipe", "pipe"],
87
+ }).trim().split("\n")[0];
88
+ if (!bin) continue;
89
+ const realBin = fs.realpathSync(bin);
90
+ const c1 = path.resolve(path.dirname(realBin), "..", "lib", "node_modules", name);
91
+ if (fs.existsSync(path.join(c1, "package.json"))) return c1;
92
+ const c2 = path.resolve(path.dirname(realBin), "..");
93
+ if (fs.existsSync(path.join(c2, "package.json")) && fs.existsSync(path.join(c2, "plugin-sdk"))) return c2;
94
+ } catch {}
95
+ }
96
+
97
+ // Strategy 3: 从 extensions 目录推断
98
+ const extensionsDir = path.dirname(pluginRoot);
99
+ const dataDir = path.dirname(extensionsDir);
100
+ const dataDirName = path.basename(dataDir);
101
+ const cliName = dataDirName.replace(/^\./, "");
102
+ if (cliName) {
103
+ try {
104
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
105
+ const candidate = path.join(globalRoot, cliName);
106
+ if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
107
+ } catch {}
108
+ }
109
+
110
+ return null;
111
+ }
112
+
113
+ /**
114
+ * 验证现有 node_modules/openclaw 是否完整可用。
115
+ *
116
+ * openclaw plugins install 可能安装了不完整的 peerDep 副本
117
+ * (只有 dist/plugin-sdk/index.js,缺少 core.js 等子模块),覆盖了之前的 symlink。
118
+ *
119
+ * 判断标准:
120
+ * - symlink → 只需确认 dist/plugin-sdk 目录存在(target 有完整文件树)
121
+ * - 真实目录 → 必须检查 dist/plugin-sdk/core.js 是否存在
122
+ */
123
+ function isLinkValid(linkTarget) {
124
+ try {
125
+ const stat = fs.lstatSync(linkTarget);
126
+ if (stat.isSymbolicLink()) {
127
+ return fs.existsSync(path.join(linkTarget, "dist", "plugin-sdk"))
128
+ || fs.existsSync(path.join(linkTarget, "plugin-sdk"));
129
+ }
130
+ // 真实目录
131
+ return fs.existsSync(path.join(linkTarget, "dist", "plugin-sdk", "core.js"));
132
+ } catch {
133
+ return false;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * 确保 plugin-sdk symlink 存在。
139
+ *
140
+ * @param {string} pluginRoot - 插件根目录路径
141
+ * @param {string} [tag="[link-sdk]"] - 日志前缀
142
+ * @returns {boolean} true 如果 symlink 已存在或成功创建
143
+ */
144
+ function ensurePluginSdkSymlink(pluginRoot, tag) {
145
+ tag = tag || "[link-sdk]";
146
+ try {
147
+ if (!pluginRoot.includes("extensions")) return true;
148
+
149
+ const linkTarget = path.join(pluginRoot, "node_modules", "openclaw");
150
+
151
+ if (fs.existsSync(linkTarget)) {
152
+ if (isLinkValid(linkTarget)) return true;
153
+ // 无效/不完整 → 删除后重建
154
+ try {
155
+ fs.rmSync(linkTarget, { recursive: true, force: true });
156
+ console.log(`${tag} removed incomplete node_modules/openclaw`);
157
+ } catch {}
158
+ }
159
+
160
+ if (!isOpenclawVersionRequiresSymlink()) return true;
161
+
162
+ const openclawRoot = findOpenclawRoot(pluginRoot);
163
+ if (!openclawRoot) {
164
+ console.error(`${tag} WARNING: could not find openclaw global installation, symlink not created`);
165
+ return false;
166
+ }
167
+
168
+ fs.mkdirSync(path.join(pluginRoot, "node_modules"), { recursive: true });
169
+ fs.symlinkSync(openclawRoot, linkTarget, "junction");
170
+ console.log(`${tag} symlink created: node_modules/openclaw -> ${openclawRoot}`);
171
+ return true;
172
+ } catch (e) {
173
+ console.error(`${tag} WARNING: symlink check failed: ${e.message || e}`);
174
+ return false;
175
+ }
176
+ }
177
+
178
+ module.exports = {
179
+ CLI_NAMES,
180
+ compareVersionGte,
181
+ isOpenclawVersionRequiresSymlink,
182
+ findOpenclawRoot,
183
+ isLinkValid,
184
+ ensurePluginSdkSymlink,
185
+ };
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ // When installed as an openclaw extension under ~/.openclaw/extensions/,
4
+ // the plugin needs access to `openclaw/plugin-sdk` at runtime.
5
+ // openclaw's jiti loader resolves this via alias by walking up from the plugin
6
+ // path to find the openclaw package root — but ~/.openclaw/extensions/ is not
7
+ // under the openclaw package tree, so the alias lookup fails.
8
+ //
9
+ // This script creates a symlink from the plugin's node_modules/openclaw to the
10
+ // globally installed openclaw package, allowing Node's native ESM resolver
11
+ // (used by jiti with tryNative:true for .js files) to find `openclaw/plugin-sdk`.
12
+
13
+ import { existsSync, lstatSync, symlinkSync, unlinkSync, rmSync, mkdirSync, realpathSync } from "node:fs";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+ import { execSync } from "node:child_process";
17
+
18
+ const __dirname = dirname(fileURLToPath(import.meta.url));
19
+ const pluginRoot = resolve(__dirname, "..");
20
+
21
+ const linkTarget = join(pluginRoot, "node_modules", "openclaw");
22
+
23
+ // Check if already a valid symlink pointing to a directory with plugin-sdk/core
24
+ if (existsSync(linkTarget)) {
25
+ try {
26
+ const stat = lstatSync(linkTarget);
27
+ if (stat.isSymbolicLink()) {
28
+ // Symlink exists — verify it has plugin-sdk/core
29
+ if (existsSync(join(linkTarget, "plugin-sdk", "core.js"))) {
30
+ process.exit(0);
31
+ }
32
+ // Symlink is stale or points to wrong target, remove and re-create
33
+ unlinkSync(linkTarget);
34
+ } else if (existsSync(join(linkTarget, "plugin-sdk", "core.js"))) {
35
+ // Real directory with correct structure (e.g. npm installed a good version)
36
+ process.exit(0);
37
+ } else {
38
+ // Real directory from npm install but missing plugin-sdk/core — replace with symlink
39
+ rmSync(linkTarget, { recursive: true, force: true });
40
+ }
41
+ } catch {
42
+ // If stat fails, try to remove and re-create
43
+ try { rmSync(linkTarget, { recursive: true, force: true }); } catch {}
44
+ }
45
+ }
46
+
47
+ // CLI names to try (openclaw and its aliases)
48
+ const CLI_NAMES = ["openclaw", "clawdbot", "moltbot"];
49
+
50
+ // Find the global openclaw installation
51
+ let openclawRoot = null;
52
+
53
+ // Strategy 1: npm root -g → look for any known CLI package name
54
+ if (!openclawRoot) {
55
+ try {
56
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
57
+ for (const name of CLI_NAMES) {
58
+ const candidate = join(globalRoot, name);
59
+ if (existsSync(join(candidate, "package.json"))) {
60
+ openclawRoot = candidate;
61
+ break;
62
+ }
63
+ }
64
+ } catch {}
65
+ }
66
+
67
+ // Strategy 2: resolve from the CLI binary (which openclaw / clawdbot / moltbot)
68
+ if (!openclawRoot) {
69
+ const whichCmd = process.platform === "win32" ? "where" : "which";
70
+ for (const name of CLI_NAMES) {
71
+ try {
72
+ const bin = execSync(`${whichCmd} ${name}`, { encoding: "utf-8" }).trim().split("\n")[0];
73
+ if (!bin) continue;
74
+ // Resolve symlinks to get actual binary location
75
+ const realBin = realpathSync(bin);
76
+ // bin is typically <prefix>/bin/<name> -> ../lib/node_modules/<name>/...
77
+ const candidate = resolve(dirname(realBin), "..", "lib", "node_modules", name);
78
+ if (existsSync(join(candidate, "package.json"))) {
79
+ openclawRoot = candidate;
80
+ break;
81
+ }
82
+ // Also try: binary might be inside the package itself (e.g. .../node_modules/<name>/bin/<name>)
83
+ const candidate2 = resolve(dirname(realBin), "..");
84
+ if (existsSync(join(candidate2, "package.json")) && existsSync(join(candidate2, "plugin-sdk"))) {
85
+ openclawRoot = candidate2;
86
+ break;
87
+ }
88
+ } catch {}
89
+ }
90
+ }
91
+
92
+ // Strategy 3: walk up from the extensions directory to find the CLI's data root,
93
+ // then look for a global node_modules sibling
94
+ if (!openclawRoot) {
95
+ // pluginRoot is like /home/user/.openclaw/extensions/openclaw-qqbot
96
+ // The CLI data dir is /home/user/.openclaw (or .clawdbot, .moltbot)
97
+ const extensionsDir = dirname(pluginRoot);
98
+ const dataDir = dirname(extensionsDir);
99
+ const dataDirName = dataDir.split("/").pop() || dataDir.split("\\").pop() || "";
100
+ // dataDirName is like ".openclaw" → strip the dot to get "openclaw"
101
+ const cliName = dataDirName.replace(/^\./, "");
102
+ if (cliName) {
103
+ try {
104
+ const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
105
+ const candidate = join(globalRoot, cliName);
106
+ if (existsSync(join(candidate, "package.json"))) {
107
+ openclawRoot = candidate;
108
+ }
109
+ } catch {}
110
+ }
111
+ }
112
+
113
+ if (!openclawRoot) {
114
+ // Not fatal — plugin may work if openclaw loads it with proper alias resolution
115
+ // But log a warning so upgrade scripts can detect the failure
116
+ console.error("[postinstall-link-sdk] WARNING: could not find openclaw/clawdbot/moltbot global installation, symlink not created");
117
+ process.exit(0);
118
+ }
119
+
120
+ try {
121
+ mkdirSync(join(pluginRoot, "node_modules"), { recursive: true });
122
+ symlinkSync(openclawRoot, linkTarget, "junction");
123
+ console.log(`[postinstall-link-sdk] symlink created: node_modules/openclaw -> ${openclawRoot}`);
124
+ } catch (e) {
125
+ console.error(`[postinstall-link-sdk] WARNING: symlink creation failed: ${e.message}`);
126
+ }