@ryantest/openclaw-qqbot 0.0.3 → 1.6.6-alpha.0

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 (89) hide show
  1. package/README.md +2 -15
  2. package/README.zh.md +3 -16
  3. package/dist/src/admin-resolver.d.ts +12 -6
  4. package/dist/src/admin-resolver.js +69 -34
  5. package/dist/src/api.d.ts +105 -1
  6. package/dist/src/api.js +164 -15
  7. package/dist/src/channel.js +13 -0
  8. package/dist/src/config.js +3 -10
  9. package/dist/src/deliver-debounce.d.ts +74 -0
  10. package/dist/src/deliver-debounce.js +174 -0
  11. package/dist/src/gateway.js +450 -248
  12. package/dist/src/image-server.d.ts +27 -8
  13. package/dist/src/image-server.js +179 -71
  14. package/dist/src/inbound-attachments.d.ts +3 -1
  15. package/dist/src/inbound-attachments.js +28 -14
  16. package/dist/src/outbound-deliver.js +77 -148
  17. package/dist/src/outbound.d.ts +6 -4
  18. package/dist/src/outbound.js +266 -442
  19. package/dist/src/reply-dispatcher.js +4 -4
  20. package/dist/src/request-context.d.ts +18 -0
  21. package/dist/src/request-context.js +30 -0
  22. package/dist/src/slash-commands.js +277 -32
  23. package/dist/src/startup-greeting.d.ts +5 -5
  24. package/dist/src/startup-greeting.js +32 -13
  25. package/dist/src/streaming.d.ts +244 -0
  26. package/dist/src/streaming.js +907 -0
  27. package/dist/src/tools/remind.js +11 -10
  28. package/dist/src/types.d.ts +101 -0
  29. package/dist/src/types.js +17 -1
  30. package/dist/src/update-checker.js +2 -8
  31. package/dist/src/utils/audio-convert.d.ts +9 -0
  32. package/dist/src/utils/audio-convert.js +51 -0
  33. package/dist/src/utils/chunked-upload.d.ts +59 -0
  34. package/dist/src/utils/chunked-upload.js +289 -0
  35. package/dist/src/utils/file-utils.d.ts +7 -1
  36. package/dist/src/utils/file-utils.js +24 -2
  37. package/dist/src/utils/media-send.d.ts +147 -0
  38. package/dist/src/utils/media-send.js +434 -0
  39. package/dist/src/utils/pkg-version.d.ts +5 -0
  40. package/dist/src/utils/pkg-version.js +51 -0
  41. package/dist/src/utils/ssrf-guard.d.ts +25 -0
  42. package/dist/src/utils/ssrf-guard.js +91 -0
  43. package/node_modules/ws/index.js +15 -6
  44. package/node_modules/ws/lib/permessage-deflate.js +6 -6
  45. package/node_modules/ws/lib/websocket-server.js +5 -5
  46. package/node_modules/ws/lib/websocket.js +6 -6
  47. package/node_modules/ws/package.json +4 -3
  48. package/node_modules/ws/wrapper.mjs +14 -1
  49. package/openclaw.plugin.json +1 -0
  50. package/package.json +11 -22
  51. package/scripts/postinstall-link-sdk.js +113 -0
  52. package/scripts/upgrade-via-npm.ps1 +161 -6
  53. package/scripts/upgrade-via-npm.sh +311 -104
  54. package/scripts/upgrade-via-source.sh +117 -0
  55. package/skills/qqbot-media/SKILL.md +9 -5
  56. package/skills/qqbot-remind/SKILL.md +3 -3
  57. package/src/admin-resolver.ts +76 -35
  58. package/src/api.ts +284 -12
  59. package/src/channel.ts +12 -0
  60. package/src/config.ts +3 -10
  61. package/src/deliver-debounce.ts +229 -0
  62. package/src/gateway.ts +277 -67
  63. package/src/image-server.ts +213 -77
  64. package/src/inbound-attachments.ts +32 -15
  65. package/src/outbound-deliver.ts +77 -157
  66. package/src/outbound.ts +304 -451
  67. package/src/reply-dispatcher.ts +4 -4
  68. package/src/request-context.ts +39 -0
  69. package/src/slash-commands.ts +303 -33
  70. package/src/startup-greeting.ts +35 -13
  71. package/src/streaming.ts +1096 -0
  72. package/src/tools/remind.ts +15 -11
  73. package/src/types.ts +111 -0
  74. package/src/update-checker.ts +2 -7
  75. package/src/utils/audio-convert.ts +56 -0
  76. package/src/utils/chunked-upload.ts +419 -0
  77. package/src/utils/file-utils.ts +28 -2
  78. package/src/utils/media-send.ts +563 -0
  79. package/src/utils/pkg-version.ts +54 -0
  80. package/src/utils/ssrf-guard.ts +102 -0
  81. package/clawdbot.plugin.json +0 -16
  82. package/dist/src/user-messages.d.ts +0 -8
  83. package/dist/src/user-messages.js +0 -8
  84. package/moltbot.plugin.json +0 -16
  85. package/scripts/upgrade-via-alt-pkg.sh +0 -307
  86. package/src/bot-logs-2026-03-21T11-21-47(2).txt +0 -46
  87. package/src/gateway.log +0 -43
  88. package/src/openclaw-2026-03-21.log +0 -3729
  89. package/src/user-messages.ts +0 -7
@@ -0,0 +1,102 @@
1
+ /**
2
+ * 远程 URL 安全校验
3
+ *
4
+ * 下载外部资源前,确保目标地址不会命中内部网络或云元数据端点,
5
+ * 避免模型输出的恶意链接触达内网服务。
6
+ */
7
+
8
+ import net from "node:net";
9
+ import dns from "node:dns/promises";
10
+
11
+ /* ---------- 内网 / 保留地址判定 ---------- */
12
+
13
+ /** IPv4 保留网段前缀(覆盖 RFC 1918、链路本地、回环等) */
14
+ const RESERVED_V4_PREFIXES = [
15
+ "127.", // loopback
16
+ "10.", // class-A private
17
+ "192.168.", // class-C private
18
+ "169.254.", // link-local / cloud metadata
19
+ ] as const;
20
+
21
+ /** 172.16.0.0 – 172.31.255.255 需要单独用正则匹配 */
22
+ const PRIVATE_172_RE = /^172\.(1[6-9]|2\d|3[01])\./;
23
+
24
+ /**
25
+ * 检查给定 IP 是否落在不可路由 / 私有网段内。
26
+ *
27
+ * 覆盖:
28
+ * - IPv4: 127/8, 10/8, 172.16/12, 192.168/16, 169.254/16, 0.0.0.0
29
+ * - IPv6: ::1, ::, fe80 (link-local), fc/fd (ULA)
30
+ */
31
+ export function isReservedAddr(ip: string): boolean {
32
+ // --- IPv4 ---
33
+ if (ip === "0.0.0.0") return true;
34
+ for (const pfx of RESERVED_V4_PREFIXES) {
35
+ if (ip.startsWith(pfx)) return true;
36
+ }
37
+ if (PRIVATE_172_RE.test(ip)) return true;
38
+
39
+ // --- IPv6 ---
40
+ const lower = ip.toLowerCase();
41
+ if (lower === "::1" || lower === "::") return true;
42
+ if (lower.startsWith("fe80:")) return true; // link-local
43
+ if (lower.startsWith("fc") || lower.startsWith("fd")) return true; // unique local
44
+ return false;
45
+ }
46
+
47
+ /* ---------- URL 合法性校验 ---------- */
48
+
49
+ const ALLOWED_SCHEMES = new Set(["http:", "https:"]);
50
+
51
+ /**
52
+ * 校验远程 URL 是否可安全请求。
53
+ *
54
+ * 规则:
55
+ * 1. 仅放行 http / https 协议
56
+ * 2. 若 URL 直接携带 IP 则即时判定
57
+ * 3. 若为域名则先做 DNS 解析,逐条检查解析结果
58
+ *
59
+ * @throws {Error} 当 URL 指向受限地址时
60
+ */
61
+ export async function validateRemoteUrl(raw: string): Promise<void> {
62
+ const url = new URL(raw);
63
+
64
+ if (!ALLOWED_SCHEMES.has(url.protocol)) {
65
+ throw new Error(
66
+ `不支持的协议 "${url.protocol}",仅允许 http/https(URL: ${raw})`,
67
+ );
68
+ }
69
+
70
+ // 去掉 IPv6 方括号
71
+ const host = url.hostname.replace(/^\[|\]$/g, "");
72
+
73
+ if (net.isIP(host)) {
74
+ assertPublicAddr(host, raw);
75
+ return;
76
+ }
77
+
78
+ // 域名 → 解析后逐条检查
79
+ try {
80
+ const ips = await dns.resolve(host);
81
+ for (const ip of ips) {
82
+ assertPublicAddr(ip, raw, host);
83
+ }
84
+ } catch (err) {
85
+ // 已经是我们自己抛的安全错误,继续向上传播
86
+ if (err instanceof Error && err.message.includes("内网")) throw err;
87
+ // DNS 查询失败不阻塞,后续 fetch 会产生网络错误
88
+ console.warn(`[url-check] DNS 解析 "${host}" 失败: ${err}`);
89
+ }
90
+ }
91
+
92
+ /* ---------- 内部辅助 ---------- */
93
+
94
+ /** 断言 IP 为公网地址,否则抛出错误 */
95
+ function assertPublicAddr(ip: string, originalUrl: string, domain?: string): void {
96
+ if (!isReservedAddr(ip)) return;
97
+
98
+ const target = domain ? `域名 "${domain}" 解析到内网地址 "${ip}"` : `内网地址 "${ip}"`;
99
+ throw new Error(
100
+ `禁止访问${target},已拦截潜在的 SSRF 请求(URL: ${originalUrl})`,
101
+ );
102
+ }
@@ -1,16 +0,0 @@
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
- "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
7
- "capabilities": {
8
- "proactiveMessaging": true,
9
- "cronJobs": true
10
- },
11
- "configSchema": {
12
- "type": "object",
13
- "additionalProperties": false,
14
- "properties": {}
15
- }
16
- }
@@ -1,8 +0,0 @@
1
- export {};
2
- /**
3
- * 用户面向的提示文案 — 已清空
4
- *
5
- * 设计原则(对齐飞书插件):
6
- * QQBot 插件层不生成额外的用户提示信息。
7
- * 所有运行时错误仅写日志,不面向用户展示。
8
- */
@@ -1,8 +0,0 @@
1
- export {};
2
- /**
3
- * 用户面向的提示文案 — 已清空
4
- *
5
- * 设计原则(对齐飞书插件):
6
- * QQBot 插件层不生成额外的用户提示信息。
7
- * 所有运行时错误仅写日志,不面向用户展示。
8
- */
@@ -1,16 +0,0 @@
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
- "skills": ["skills/qqbot-channel", "skills/qqbot-remind", "skills/qqbot-media"],
7
- "capabilities": {
8
- "proactiveMessaging": true,
9
- "cronJobs": true
10
- },
11
- "configSchema": {
12
- "type": "object",
13
- "additionalProperties": false,
14
- "properties": {}
15
- }
16
- }
@@ -1,307 +0,0 @@
1
- #!/bin/bash
2
-
3
- # qqbot 测试脚本:自由切换不同的 QQBot npm 包
4
- #
5
- # 支持从任意 npm 包安装指定版本到 openclaw extensions 目录。
6
- # 可用于在不同包之间切换测试,如 @sliverp/qqbot、@tencent-connect/openclaw-qqbot 等。
7
- #
8
- # 用法:
9
- # upgrade-via-alt-pkg.sh --pkg <包名> --version <version> # 指定包+版本
10
- # upgrade-via-alt-pkg.sh --pkg <包名> # 指定包,安装 latest
11
- # upgrade-via-alt-pkg.sh --appid <appid> --secret <secret> # 首次安装时配置
12
- # upgrade-via-alt-pkg.sh --no-restart # 只做文件替换,不重启
13
- #
14
- # 示例:
15
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.1
16
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4
17
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @tencent-connect/openclaw-qqbot --version 1.6.4
18
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot
19
- # bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4 --appid 12345 --secret abc123
20
-
21
- set -eo pipefail
22
-
23
- PKG_NAME=""
24
- VERSION=""
25
- INSTALL_SRC=""
26
- APPID=""
27
- SECRET=""
28
- NO_RESTART=false
29
-
30
- print_usage() {
31
- echo "用法:"
32
- echo " upgrade-via-alt-pkg.sh --pkg <包名> --version <版本号>"
33
- echo " upgrade-via-alt-pkg.sh --pkg <包名> # 安装 latest"
34
- echo ""
35
- echo "选项:"
36
- echo " --pkg <name> npm 包名(必填,如 @sliverp/qqbot、@tencent-connect/openclaw-qqbot)"
37
- echo " --version <version> 指定版本号(如 1.5.1, 1.5.4, 1.6.4)"
38
- echo " --appid <appid> QQ机器人 appid(首次安装时必填)"
39
- echo " --secret <secret> QQ机器人 secret(首次安装时必填)"
40
- echo " --no-restart 只做文件替换,不重启 gateway"
41
- echo " -h, --help 显示帮助信息"
42
- echo ""
43
- echo "环境变量:"
44
- echo " QQBOT_APPID QQ机器人 appid"
45
- echo " QQBOT_SECRET QQ机器人 secret"
46
- echo " QQBOT_TOKEN QQ机器人 token (appid:secret)"
47
- echo ""
48
- echo "示例:"
49
- echo " # 从 @sliverp/qqbot 包安装 v1.5.1"
50
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.1"
51
- echo ""
52
- echo " # 从 @sliverp/qqbot 包安装 v1.5.4"
53
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot --version 1.5.4"
54
- echo ""
55
- echo " # 从官方包安装 v1.6.4"
56
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @tencent-connect/openclaw-qqbot --version 1.6.4"
57
- echo ""
58
- echo " # 切回 @sliverp/qqbot 包的 latest"
59
- echo " bash scripts/upgrade-via-alt-pkg.sh --pkg @sliverp/qqbot"
60
- }
61
-
62
- while [[ $# -gt 0 ]]; do
63
- case "$1" in
64
- --pkg|--package)
65
- [ -z "$2" ] && echo "❌ --pkg 需要参数" && exit 1
66
- PKG_NAME="$2"
67
- shift 2
68
- ;;
69
- --version)
70
- [ -z "$2" ] && echo "❌ --version 需要参数" && exit 1
71
- VERSION="$2"
72
- shift 2
73
- ;;
74
- --tag)
75
- [ -z "$2" ] && echo "❌ --tag 需要参数" && exit 1
76
- VERSION="$2"
77
- shift 2
78
- ;;
79
- --appid)
80
- [ -z "$2" ] && echo "❌ --appid 需要参数" && exit 1
81
- APPID="$2"
82
- shift 2
83
- ;;
84
- --secret)
85
- [ -z "$2" ] && echo "❌ --secret 需要参数" && exit 1
86
- SECRET="$2"
87
- shift 2
88
- ;;
89
- --no-restart)
90
- NO_RESTART=true
91
- shift 1
92
- ;;
93
- -h|--help)
94
- print_usage
95
- exit 0
96
- ;;
97
- *) echo "未知选项: $1"; print_usage; exit 1 ;;
98
- esac
99
- done
100
-
101
- # --pkg 必填
102
- if [ -z "$PKG_NAME" ]; then
103
- echo "❌ 必须指定 --pkg 参数"
104
- echo ""
105
- print_usage
106
- exit 1
107
- fi
108
-
109
- if [ -n "$VERSION" ]; then
110
- INSTALL_SRC="${PKG_NAME}@${VERSION}"
111
- else
112
- INSTALL_SRC="${PKG_NAME}@latest"
113
- fi
114
-
115
- # 环境变量 fallback
116
- APPID="${APPID:-$QQBOT_APPID}"
117
- SECRET="${SECRET:-$QQBOT_SECRET}"
118
- if [ -z "$APPID" ] && [ -z "$SECRET" ] && [ -n "$QQBOT_TOKEN" ]; then
119
- APPID="${QQBOT_TOKEN%%:*}"
120
- SECRET="${QQBOT_TOKEN#*:}"
121
- fi
122
-
123
- # 检测 CLI(仅用于确定 extensions 目录路径)
124
- CMD=""
125
- for name in openclaw clawdbot moltbot; do
126
- command -v "$name" &>/dev/null && CMD="$name" && break
127
- done
128
- [ -z "$CMD" ] && echo "❌ 未找到 openclaw / clawdbot / moltbot" && exit 1
129
-
130
- EXTENSIONS_DIR="$HOME/.$CMD/extensions"
131
-
132
- echo "==========================================="
133
- echo " qqbot 测试升级: $INSTALL_SRC"
134
- echo "==========================================="
135
- echo ""
136
-
137
- # [1/3] 下载并安装新版本到临时目录
138
- echo "[1/3] 下载新版本..."
139
- TMPDIR_PACK=$(mktemp -d)
140
- EXTRACT_DIR=$(mktemp -d)
141
- trap "rm -rf '$TMPDIR_PACK' '$EXTRACT_DIR'" EXIT
142
-
143
- cd "$TMPDIR_PACK"
144
- # 多 registry fallback:npmjs.org → npmmirror(国内镜像)→ 默认 registry
145
- PACK_OK=false
146
- for _registry in "https://registry.npmjs.org/" "https://registry.npmmirror.com/" ""; do
147
- if [ -n "$_registry" ]; then
148
- echo " 尝试 registry: $_registry"
149
- npm pack "$INSTALL_SRC" --registry "$_registry" --quiet 2>&1 && PACK_OK=true && break
150
- else
151
- echo " 尝试默认 registry..."
152
- npm pack "$INSTALL_SRC" --quiet 2>&1 && PACK_OK=true && break
153
- fi
154
- done
155
- $PACK_OK || { echo "❌ npm pack 失败(所有 registry 均不可用)"; exit 1; }
156
- TGZ_FILE=$(ls -1 *.tgz 2>/dev/null | head -1)
157
- [ -z "$TGZ_FILE" ] && echo "❌ 未找到下载的 tgz 文件" && exit 1
158
- echo " 已下载: $TGZ_FILE"
159
-
160
- tar xzf "$TGZ_FILE" -C "$EXTRACT_DIR"
161
- PACKAGE_DIR="$EXTRACT_DIR/package"
162
- [ ! -d "$PACKAGE_DIR" ] && echo "❌ 解压失败,未找到 package 目录" && exit 1
163
-
164
- # 准备 staging 目录
165
- STAGING_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-staging"
166
- rm -rf "$STAGING_DIR"
167
- mkdir -p "$STAGING_DIR"
168
- cp -R "$PACKAGE_DIR/." "$STAGING_DIR/"
169
-
170
- # 依赖处理
171
- if [ -d "$STAGING_DIR/node_modules" ]; then
172
- BUNDLED_COUNT=$(find "$STAGING_DIR/node_modules" -mindepth 1 -maxdepth 2 -type d | wc -l | tr -d ' ')
173
- echo " bundled 依赖已就绪(${BUNDLED_COUNT} 个包)"
174
- else
175
- echo " ⚠️ 未找到 bundled node_modules,尝试安装依赖..."
176
- NPM_TMP_CACHE=$(mktemp -d)
177
- (cd "$STAGING_DIR" && npm install --omit=dev --omit=peer --ignore-scripts --cache="$NPM_TMP_CACHE" --quiet 2>&1) || echo " ⚠️ 依赖安装失败"
178
- rm -rf "$NPM_TMP_CACHE"
179
- fi
180
-
181
- # 清理下载临时文件
182
- rm -rf "$TMPDIR_PACK" "$EXTRACT_DIR"
183
- cd "$HOME"
184
-
185
- # [2/3] 原子替换插件目录
186
- echo ""
187
- echo "[2/3] 原子替换插件目录..."
188
- TARGET_DIR="$EXTENSIONS_DIR/openclaw-qqbot"
189
- OLD_DIR="$(dirname "$EXTENSIONS_DIR")/.qqbot-upgrade-old"
190
-
191
- rm -rf "$OLD_DIR"
192
-
193
- STAGING_IN_EXT="$EXTENSIONS_DIR/.openclaw-qqbot-new"
194
- rm -rf "$STAGING_IN_EXT"
195
- mv "$STAGING_DIR" "$STAGING_IN_EXT"
196
-
197
- if [ -d "$TARGET_DIR" ]; then
198
- mv "$TARGET_DIR" "$OLD_DIR" && mv "$STAGING_IN_EXT" "$TARGET_DIR"
199
- else
200
- mv "$STAGING_IN_EXT" "$TARGET_DIR"
201
- fi
202
- rm -rf "$OLD_DIR"
203
-
204
- # 清理可能残留的旧版 staging 目录
205
- rm -rf "$EXTENSIONS_DIR/openclaw-qqbot.staging"
206
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-staging"
207
- rm -rf "$EXTENSIONS_DIR/.qqbot-upgrade-old"
208
-
209
- # 清理历史遗留的其他目录名
210
- for dir_name in qqbot openclaw-qq; do
211
- [ -d "$EXTENSIONS_DIR/$dir_name" ] && rm -rf "$EXTENSIONS_DIR/$dir_name"
212
- done
213
- echo " 已安装到: $TARGET_DIR"
214
-
215
- # [3/3] 输出新版本号和升级报告
216
- echo ""
217
- echo "[3/3] 验证安装..."
218
- NEW_VERSION="$(node -e "
219
- try {
220
- const fs = require('fs');
221
- const path = require('path');
222
- const p = path.join('$EXTENSIONS_DIR', 'openclaw-qqbot', 'package.json');
223
- if (fs.existsSync(p)) {
224
- const v = JSON.parse(fs.readFileSync(p, 'utf8')).version;
225
- if (v) { process.stdout.write(v); process.exit(0); }
226
- }
227
- } catch {}
228
- " 2>/dev/null || true)"
229
- echo "QQBOT_NEW_VERSION=${NEW_VERSION:-unknown}"
230
-
231
- if [ -n "$NEW_VERSION" ] && [ "$NEW_VERSION" != "unknown" ]; then
232
- echo "QQBOT_REPORT=✅ QQBot 升级完成 ($PKG_NAME): v${NEW_VERSION}"
233
- else
234
- echo "QQBOT_REPORT=⚠️ QQBot 升级异常,无法确认新版本"
235
- fi
236
-
237
- echo ""
238
- echo "==========================================="
239
- echo " ✅ 文件安装完成"
240
- echo "==========================================="
241
-
242
- # --no-restart 模式
243
- if [ "$NO_RESTART" = "true" ]; then
244
- echo ""
245
- echo "[跳过重启] --no-restart 已指定,脚本立即退出以便调用方触发 gateway restart"
246
- exit 0
247
- fi
248
-
249
- # [4/4] 配置 appid/secret(仅在提供了参数时执行)
250
- if [ -n "$APPID" ] && [ -n "$SECRET" ]; then
251
- echo ""
252
- echo "[配置] 写入 qqbot 通道配置..."
253
- DESIRED_TOKEN="${APPID}:${SECRET}"
254
-
255
- CURRENT_TOKEN=""
256
- for _app in openclaw clawdbot moltbot; do
257
- _cfg="$HOME/.$_app/$_app.json"
258
- if [ -f "$_cfg" ]; then
259
- CURRENT_TOKEN=$(node -e "
260
- const cfg = JSON.parse(require('fs').readFileSync('$_cfg', 'utf8'));
261
- const keys = ['qqbot', 'openclaw-qqbot', 'openclaw-qq'];
262
- for (const key of keys) {
263
- const ch = cfg.channels && cfg.channels[key];
264
- if (!ch) continue;
265
- if (ch.token) { process.stdout.write(ch.token); process.exit(0); }
266
- if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ':' + ch.clientSecret); process.exit(0); }
267
- }
268
- " 2>/dev/null || true)
269
- [ -n "$CURRENT_TOKEN" ] && break
270
- fi
271
- done
272
-
273
- if [ "$CURRENT_TOKEN" = "$DESIRED_TOKEN" ]; then
274
- echo " ✅ 当前配置已是目标值,跳过写入"
275
- elif $CMD channels add --channel qqbot --token "$DESIRED_TOKEN" 2>&1; then
276
- echo " ✅ 通道配置写入成功"
277
- else
278
- echo " ⚠️ $CMD channels add 失败,尝试直接编辑配置文件..."
279
- CONFIG_FILE="$HOME/.$CMD/$CMD.json"
280
- if [ -f "$CONFIG_FILE" ] && node -e "
281
- const fs = require('fs');
282
- const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
283
- if (!cfg.channels) cfg.channels = {};
284
- if (!cfg.channels.qqbot) cfg.channels.qqbot = {};
285
- cfg.channels.qqbot.appId = '$APPID';
286
- cfg.channels.qqbot.clientSecret = '$SECRET';
287
- fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
288
- " 2>&1; then
289
- echo " ✅ 通道配置写入成功(直接编辑配置文件)"
290
- else
291
- echo " ❌ 配置写入失败,请手动配置:"
292
- echo " $CMD channels add --channel qqbot --token \"${APPID}:${SECRET}\""
293
- fi
294
- fi
295
- elif [ -n "$APPID" ] || [ -n "$SECRET" ]; then
296
- echo ""
297
- echo "⚠️ --appid 和 --secret 必须同时提供"
298
- fi
299
-
300
- # [5/5] 重启 gateway 使新版本生效
301
- echo ""
302
- echo "[重启] 重启 gateway 使新版本生效..."
303
- if $CMD gateway restart 2>&1; then
304
- echo " ✅ gateway 已重启"
305
- else
306
- echo " ⚠️ gateway 重启失败,请手动执行: $CMD gateway restart"
307
- fi
@@ -1,46 +0,0 @@
1
-
2
- ========== gateway.log (last 44 of 44 lines) ==========
3
- from: C:\Users\v_qachchen\.openclaw
4
- [2026-03-20T15:55:33.816Z] state: stopped → starting
5
- [2026-03-20T15:55:33.819Z] [extract] 检测到 runtime.tar.gz,开始解压...
6
- [2026-03-20T15:55:37.282Z] [extract] runtime.tar.gz 解压完成 (3.5s)
7
- [2026-03-20T15:55:37.286Z] [extract] 检测到 gateway.tar.gz,开始解压...
8
- [2026-03-20T15:56:05.916Z] [extract] gateway.tar.gz 解压完成 (28.6s)
9
- [2026-03-20T15:56:05.924Z] --- gateway start ---
10
- [2026-03-20T15:56:05.926Z] platform=win32 arch=x64 packaged=true
11
- [2026-03-20T15:56:05.927Z] resourcesPath=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources
12
- [2026-03-20T15:56:05.928Z] nodeBin=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\runtime\node.exe exists=true
13
- [2026-03-20T15:56:05.928Z] entry=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\gateway\node_modules\openclaw\openclaw.mjs exists=true
14
- [2026-03-20T15:56:05.929Z] cwd=C:\Users\v_qachchen\.openclaw\workspace exists=true
15
- [2026-03-20T15:56:05.929Z] token=a98d...06c4 port=19789
16
- [2026-03-20T15:56:05.978Z] TTS 未配置,使用默认 SiliconFlow URL
17
- [2026-03-20T15:56:05.979Z] spawn: C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\runtime\node.exe C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\gateway\node_modules\openclaw\openclaw.mjs gateway run --port 19789 --bind loopback
18
- [2026-03-20T15:56:14.259Z] stdout: |
19
- o Doctor warnings ------------------------------------------------------+
20
- | |
21
- | - channels.imessage.groupPolicy is "allowlist" but groupAllowFrom is |
22
- | empty — this channel does not fall back to allowFrom, so all group |
23
- | messages will be silently dropped. Add sender IDs to |
24
- | channels.imessage.groupAllowFrom, or set groupPolicy to "open". |
25
- | |
26
- +------------------------------------------------------------------------+
27
- [2026-03-20T15:56:44.457Z] stdout: 2026-03-20T15:56:44.457Z [canvas] host mounted at http://127.0.0.1:19789/__openclaw__/canvas/ (root C:\Users\v_qachchen\.openclaw\canvas)
28
- [2026-03-20T15:56:44.786Z] stdout: 2026-03-20T15:56:44.784Z [heartbeat] started
29
- [2026-03-20T15:56:44.791Z] stdout: 2026-03-20T15:56:44.791Z [health-monitor] started (interval: 300s, startup-grace: 60s, channel-connect-grace: 120s)
30
- [2026-03-20T15:56:44.804Z] stdout: 2026-03-20T15:56:44.802Z [gateway] agent model: custom/hy-hunyuan-instruct
31
- [2026-03-20T15:56:44.806Z] stdout: 2026-03-20T15:56:44.806Z [gateway] listening on ws://127.0.0.1:19789, ws://[::1]:19789 (PID 224544)
32
- [2026-03-20T15:56:44.812Z] stdout: 2026-03-20T15:56:44.812Z [gateway] log file: \tmp\openclaw\openclaw-2026-03-20.log
33
- [2026-03-20T15:56:44.929Z] stdout: 2026-03-20T15:56:44.929Z [browser/server] Browser control listening on http://127.0.0.1:19791/ (auth=token)
34
- [2026-03-20T15:56:45.150Z] health check passed, child alive
35
- [2026-03-20T15:56:45.151Z] state: starting → running
36
- [2026-03-20T15:56:46.272Z] stdout: 2026-03-20T15:56:46.112Z [hooks:loader] Registered hook: boot-md -> gateway:startup
37
- 2026-03-20T15:56:46.179Z [hooks:loader] Registered hook: bootstrap-extra-files -> agent:bootstrap
38
- 2026-03-20T15:56:46.247Z [hooks:loader] Registered hook: command-logger -> command
39
- [2026-03-20T15:56:46.273Z] stderr: 2026-03-20T15:56:46.166Z [ws] closed before connect conn=bd4ae208-a072-4475-8c6b-63daad0e7025 remote=127.0.0.1 fwd=n/a origin=file:// host=127.0.0.1:19789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HoldClaw/1.0.14 Chrome/144.0.7559.225 Electron/40.7.0 Safari/537.36 code=1006 reason=n/a
40
- 2026-03-20T15:56:46.235Z [ws] unauthorized conn=723433fc-d3d5-4065-b919-924e0992dcfe remote=127.0.0.1 client=openclaw-control-ui webchat vdev reason=token_mismatch
41
- 2026-03-20T15:56:46.252Z [ws] closed before connect conn=723433fc-d3d5-4065-b919-924e0992dcfe remote=127.0.0.1 fwd=n/a origin=file:// host=127.0.0.1:19789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HoldClaw/1.0.14 Chrome/144.0.7559.225 Electron/40.7.0 Safari/537.36 code=1008 reason=unauthorized: gateway token mismatch (open the dashboard URL and paste the token in Control UI settings)
42
- [2026-03-20T15:56:46.318Z] stdout: 2026-03-20T15:56:46.302Z [hooks:loader] Registered hook: session-memory -> command:new, command:reset
43
- 2026-03-20T15:56:46.308Z [hooks] loaded 4 internal hook handlers
44
- [2026-03-20T15:56:46.637Z] stdout: 2026-03-20T15:56:46.637Z [gateway] update available (latest): v2026.3.13 (current v2026.3.2). Run: openclaw update
45
- [2026-03-20T15:56:47.076Z] stdout: 2026-03-20T15:56:47.076Z [gateway] device pairing auto-approved device=bd0fa7fe507891dfe0875c53fd38d0582145228b1478b4e62ad102f07d9254df role=operator
46
- [2026-03-20T15:56:47.081Z] stdout: 2026-03-20T15:56:47.081Z [ws] webchat connected conn=200f754d-9791-4dcc-bfaa-93c839311f65 remote=127.0.0.1 client=openclaw-control-ui webchat vdev
package/src/gateway.log DELETED
@@ -1,43 +0,0 @@
1
- [2026-03-20T15:55:33.816Z] state: stopped → starting
2
- [2026-03-20T15:55:33.819Z] [extract] 检测到 runtime.tar.gz,开始解压...
3
- [2026-03-20T15:55:37.282Z] [extract] runtime.tar.gz 解压完成 (3.5s)
4
- [2026-03-20T15:55:37.286Z] [extract] 检测到 gateway.tar.gz,开始解压...
5
- [2026-03-20T15:56:05.916Z] [extract] gateway.tar.gz 解压完成 (28.6s)
6
- [2026-03-20T15:56:05.924Z] --- gateway start ---
7
- [2026-03-20T15:56:05.926Z] platform=win32 arch=x64 packaged=true
8
- [2026-03-20T15:56:05.927Z] resourcesPath=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources
9
- [2026-03-20T15:56:05.928Z] nodeBin=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\runtime\node.exe exists=true
10
- [2026-03-20T15:56:05.928Z] entry=C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\gateway\node_modules\openclaw\openclaw.mjs exists=true
11
- [2026-03-20T15:56:05.929Z] cwd=C:\Users\v_qachchen\.openclaw\workspace exists=true
12
- [2026-03-20T15:56:05.929Z] token=a98d...06c4 port=19789
13
- [2026-03-20T15:56:05.978Z] TTS 未配置,使用默认 SiliconFlow URL
14
- [2026-03-20T15:56:05.979Z] spawn: C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\runtime\node.exe C:\Users\v_qachchen\AppData\Local\Programs\HoldClaw\resources\resources\gateway\node_modules\openclaw\openclaw.mjs gateway run --port 19789 --bind loopback
15
- [2026-03-20T15:56:14.259Z] stdout: |
16
- o Doctor warnings ------------------------------------------------------+
17
- | |
18
- | - channels.imessage.groupPolicy is "allowlist" but groupAllowFrom is |
19
- | empty — this channel does not fall back to allowFrom, so all group |
20
- | messages will be silently dropped. Add sender IDs to |
21
- | channels.imessage.groupAllowFrom, or set groupPolicy to "open". |
22
- | |
23
- +------------------------------------------------------------------------+
24
- [2026-03-20T15:56:44.457Z] stdout: 2026-03-20T15:56:44.457Z [canvas] host mounted at http://127.0.0.1:19789/__openclaw__/canvas/ (root C:\Users\v_qachchen\.openclaw\canvas)
25
- [2026-03-20T15:56:44.786Z] stdout: 2026-03-20T15:56:44.784Z [heartbeat] started
26
- [2026-03-20T15:56:44.791Z] stdout: 2026-03-20T15:56:44.791Z [health-monitor] started (interval: 300s, startup-grace: 60s, channel-connect-grace: 120s)
27
- [2026-03-20T15:56:44.804Z] stdout: 2026-03-20T15:56:44.802Z [gateway] agent model: custom/hy-hunyuan-instruct
28
- [2026-03-20T15:56:44.806Z] stdout: 2026-03-20T15:56:44.806Z [gateway] listening on ws://127.0.0.1:19789, ws://[::1]:19789 (PID 224544)
29
- [2026-03-20T15:56:44.812Z] stdout: 2026-03-20T15:56:44.812Z [gateway] log file: \tmp\openclaw\openclaw-2026-03-20.log
30
- [2026-03-20T15:56:44.929Z] stdout: 2026-03-20T15:56:44.929Z [browser/server] Browser control listening on http://127.0.0.1:19791/ (auth=token)
31
- [2026-03-20T15:56:45.150Z] health check passed, child alive
32
- [2026-03-20T15:56:45.151Z] state: starting → running
33
- [2026-03-20T15:56:46.272Z] stdout: 2026-03-20T15:56:46.112Z [hooks:loader] Registered hook: boot-md -> gateway:startup
34
- 2026-03-20T15:56:46.179Z [hooks:loader] Registered hook: bootstrap-extra-files -> agent:bootstrap
35
- 2026-03-20T15:56:46.247Z [hooks:loader] Registered hook: command-logger -> command
36
- [2026-03-20T15:56:46.273Z] stderr: 2026-03-20T15:56:46.166Z [ws] closed before connect conn=bd4ae208-a072-4475-8c6b-63daad0e7025 remote=127.0.0.1 fwd=n/a origin=file:// host=127.0.0.1:19789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HoldClaw/1.0.14 Chrome/144.0.7559.225 Electron/40.7.0 Safari/537.36 code=1006 reason=n/a
37
- 2026-03-20T15:56:46.235Z [ws] unauthorized conn=723433fc-d3d5-4065-b919-924e0992dcfe remote=127.0.0.1 client=openclaw-control-ui webchat vdev reason=token_mismatch
38
- 2026-03-20T15:56:46.252Z [ws] closed before connect conn=723433fc-d3d5-4065-b919-924e0992dcfe remote=127.0.0.1 fwd=n/a origin=file:// host=127.0.0.1:19789 ua=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HoldClaw/1.0.14 Chrome/144.0.7559.225 Electron/40.7.0 Safari/537.36 code=1008 reason=unauthorized: gateway token mismatch (open the dashboard URL and paste the token in Control UI settings)
39
- [2026-03-20T15:56:46.318Z] stdout: 2026-03-20T15:56:46.302Z [hooks:loader] Registered hook: session-memory -> command:new, command:reset
40
- 2026-03-20T15:56:46.308Z [hooks] loaded 4 internal hook handlers
41
- [2026-03-20T15:56:46.637Z] stdout: 2026-03-20T15:56:46.637Z [gateway] update available (latest): v2026.3.13 (current v2026.3.2). Run: openclaw update
42
- [2026-03-20T15:56:47.076Z] stdout: 2026-03-20T15:56:47.076Z [gateway] device pairing auto-approved device=bd0fa7fe507891dfe0875c53fd38d0582145228b1478b4e62ad102f07d9254df role=operator
43
- [2026-03-20T15:56:47.081Z] stdout: 2026-03-20T15:56:47.081Z [ws] webchat connected conn=200f754d-9791-4dcc-bfaa-93c839311f65 remote=127.0.0.1 client=openclaw-control-ui webchat vdev