@ryantest/openclaw-qqbot 1.6.7-beta.2 → 1.6.7-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/src/api.js +1 -1
- package/dist/src/slash-commands.js +334 -26
- package/dist/src/types.d.ts +7 -0
- package/dist/src/update-checker.d.ts +3 -1
- package/dist/src/update-checker.js +13 -2
- package/package.json +1 -1
- package/scripts/upgrade-via-npm.ps1 +9 -0
- package/scripts/upgrade-via-npm.sh +144 -30
- package/scripts/upgrade-via-source.sh +37 -30
- package/src/api.ts +1 -1
- package/src/slash-commands.ts +328 -25
- package/src/types.ts +7 -0
- package/src/update-checker.ts +14 -2
|
@@ -18,15 +18,58 @@
|
|
|
18
18
|
|
|
19
19
|
set -eo pipefail
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# 确保 cwd 是一个存在的目录。
|
|
22
|
+
# 当从 gateway 进程 fork 时,继承的 cwd 可能已被删除(如旧插件目录被 mv/rm),
|
|
23
|
+
# 导致 openclaw CLI 启动时 process.cwd() 报 ENOENT: uv_cwd 错误。
|
|
24
|
+
cd "$HOME" 2>/dev/null || cd / 2>/dev/null || true
|
|
25
|
+
|
|
26
|
+
# 异常退出时清理临时文件并回滚(防止泄露或残留)
|
|
27
|
+
INSTALL_COMPLETED=false # 标记 install 是否已完成(用于区分正常退出和异常退出)
|
|
22
28
|
cleanup_on_exit() {
|
|
29
|
+
local exit_code=$?
|
|
30
|
+
|
|
31
|
+
# 异常退出时同步临时配置中的 install 记录回真实配置
|
|
23
32
|
if [ -n "$TEMP_CONFIG_FILE" ] && [ -f "$TEMP_CONFIG_FILE" ]; then
|
|
33
|
+
# 尝试同步 install 记录(即使异常退出也要保留)
|
|
34
|
+
node -e "
|
|
35
|
+
try {
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const tmp = JSON.parse(fs.readFileSync('$TEMP_CONFIG_FILE', 'utf8'));
|
|
38
|
+
const real = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
|
39
|
+
if (tmp.plugins && tmp.plugins.installs) {
|
|
40
|
+
if (!real.plugins) real.plugins = {};
|
|
41
|
+
real.plugins.installs = { ...(real.plugins.installs || {}), ...tmp.plugins.installs };
|
|
42
|
+
}
|
|
43
|
+
if (tmp.plugins && tmp.plugins.entries) {
|
|
44
|
+
if (!real.plugins) real.plugins = {};
|
|
45
|
+
real.plugins.entries = { ...(real.plugins.entries || {}), ...tmp.plugins.entries };
|
|
46
|
+
}
|
|
47
|
+
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(real, null, 4) + '\n');
|
|
48
|
+
} catch {}
|
|
49
|
+
" 2>/dev/null || true
|
|
24
50
|
rm -f "$TEMP_CONFIG_FILE" 2>/dev/null || true
|
|
25
51
|
fi
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
|
|
53
|
+
# 异常退出且 install 未完成时,回滚备份目录(而非删除)
|
|
54
|
+
if [ "$INSTALL_COMPLETED" != "true" ] && [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
55
|
+
if [ ! -d "$EXTENSIONS_DIR/$PLUGIN_ID" ] || [ ! -f "$EXTENSIONS_DIR/$PLUGIN_ID/package.json" ]; then
|
|
56
|
+
# 插件目录不存在或不完整,回滚
|
|
57
|
+
rm -rf "$EXTENSIONS_DIR/$PLUGIN_ID" 2>/dev/null || true
|
|
58
|
+
mv "$BACKUP_DIR/$PLUGIN_ID" "$EXTENSIONS_DIR/$PLUGIN_ID" 2>/dev/null || \
|
|
59
|
+
mv "$BACKUP_DIR"/* "$EXTENSIONS_DIR/$PLUGIN_ID" 2>/dev/null || true
|
|
60
|
+
echo " ↩️ [cleanup] 异常退出,已回滚到旧版本"
|
|
61
|
+
else
|
|
62
|
+
# 插件目录完整,清理备份
|
|
63
|
+
rm -rf "$BACKUP_DIR" 2>/dev/null || true
|
|
64
|
+
fi
|
|
65
|
+
elif [ "$INSTALL_COMPLETED" = "true" ] && [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
66
|
+
# 正常完成,清理备份
|
|
28
67
|
rm -rf "$BACKUP_DIR" 2>/dev/null || true
|
|
29
68
|
fi
|
|
69
|
+
|
|
70
|
+
# 清理 openclaw install 可能残留的暂存目录(extensions 和 /tmp 中都可能存在)
|
|
71
|
+
find "${EXTENSIONS_DIR:-/dev/null}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
72
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
30
73
|
}
|
|
31
74
|
trap cleanup_on_exit EXIT
|
|
32
75
|
|
|
@@ -63,6 +106,7 @@ print_usage() {
|
|
|
63
106
|
echo " upgrade-via-npm.sh --self-version # 升级到当前仓库版本"
|
|
64
107
|
fi
|
|
65
108
|
echo ""
|
|
109
|
+
echo " --pkg <scope/name> 指定 npm 包名(如 ryantest/openclaw-qqbot)"
|
|
66
110
|
echo " --appid <appid> QQ机器人 appid(首次安装时必填)"
|
|
67
111
|
echo " --secret <secret> QQ机器人 secret(首次安装时必填)"
|
|
68
112
|
echo ""
|
|
@@ -76,22 +120,17 @@ while [[ $# -gt 0 ]]; do
|
|
|
76
120
|
case "$1" in
|
|
77
121
|
--tag)
|
|
78
122
|
[ -z "$2" ] && echo "❌ --tag 需要参数" && exit 1
|
|
79
|
-
|
|
80
|
-
TARGET_VERSION="$_ver"
|
|
81
|
-
INSTALL_SRC="${PKG_NAME}@$_ver"
|
|
123
|
+
TARGET_VERSION="${2#v}" # 去掉 v 前缀(npm 版本号不带 v)
|
|
82
124
|
shift 2
|
|
83
125
|
;;
|
|
84
126
|
--version)
|
|
85
127
|
[ -z "$2" ] && echo "❌ --version 需要参数" && exit 1
|
|
86
|
-
|
|
87
|
-
TARGET_VERSION="$_ver"
|
|
88
|
-
INSTALL_SRC="${PKG_NAME}@$_ver"
|
|
128
|
+
TARGET_VERSION="${2#v}" # 去掉 v 前缀(npm 版本号不带 v)
|
|
89
129
|
shift 2
|
|
90
130
|
;;
|
|
91
131
|
--self-version)
|
|
92
132
|
[ -z "$LOCAL_VERSION" ] && echo "❌ 无法从 package.json 读取版本" && exit 1
|
|
93
133
|
TARGET_VERSION="$LOCAL_VERSION"
|
|
94
|
-
INSTALL_SRC="${PKG_NAME}@${LOCAL_VERSION}"
|
|
95
134
|
shift 1
|
|
96
135
|
;;
|
|
97
136
|
--appid)
|
|
@@ -104,6 +143,14 @@ while [[ $# -gt 0 ]]; do
|
|
|
104
143
|
SECRET="$2"
|
|
105
144
|
shift 2
|
|
106
145
|
;;
|
|
146
|
+
--pkg)
|
|
147
|
+
[ -z "$2" ] && echo "❌ --pkg 需要参数" && exit 1
|
|
148
|
+
_pkg="$2"
|
|
149
|
+
# 支持 "scope/name" 自动补 @
|
|
150
|
+
if [[ "$_pkg" != @* ]]; then _pkg="@$_pkg"; fi
|
|
151
|
+
PKG_NAME="$_pkg"
|
|
152
|
+
shift 2
|
|
153
|
+
;;
|
|
107
154
|
--no-restart)
|
|
108
155
|
NO_RESTART=true
|
|
109
156
|
shift 1
|
|
@@ -115,7 +162,12 @@ while [[ $# -gt 0 ]]; do
|
|
|
115
162
|
*) echo "未知选项: $1"; print_usage; exit 1 ;;
|
|
116
163
|
esac
|
|
117
164
|
done
|
|
118
|
-
INSTALL_SRC
|
|
165
|
+
# 参数解析完毕后统一拼接 INSTALL_SRC(确保 --pkg 无论在 --version 前后都能生效)
|
|
166
|
+
if [ -n "$TARGET_VERSION" ]; then
|
|
167
|
+
INSTALL_SRC="${PKG_NAME}@${TARGET_VERSION}"
|
|
168
|
+
else
|
|
169
|
+
INSTALL_SRC="${PKG_NAME}@latest"
|
|
170
|
+
fi
|
|
119
171
|
|
|
120
172
|
# 环境变量 fallback
|
|
121
173
|
APPID="${APPID:-$QQBOT_APPID}"
|
|
@@ -171,30 +223,46 @@ echo "[1/4] 安装/升级插件..."
|
|
|
171
223
|
# 环境变量让 plugins install/update 使用临时配置,真实配置文件不受影响。
|
|
172
224
|
CONFIG_FILE="$HOME/.$CMD/$CMD.json"
|
|
173
225
|
TEMP_CONFIG_FILE=""
|
|
174
|
-
|
|
226
|
+
NEEDS_TEMP_CONFIG=false
|
|
175
227
|
|
|
176
228
|
if [ -f "$CONFIG_FILE" ]; then
|
|
177
|
-
|
|
229
|
+
NEEDS_TEMP_CONFIG="$(node -e "
|
|
178
230
|
try {
|
|
179
231
|
const fs = require('fs');
|
|
180
232
|
const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
|
181
|
-
|
|
233
|
+
const hasChannel = !!(cfg.channels && cfg.channels.qqbot);
|
|
234
|
+
const hasAllow = Array.isArray(cfg.plugins?.allow) && cfg.plugins.allow.includes('$PLUGIN_ID');
|
|
235
|
+
const hasEntry = !!(cfg.plugins?.entries?.['$PLUGIN_ID']);
|
|
236
|
+
if (hasChannel || hasAllow || hasEntry) process.stdout.write('true');
|
|
182
237
|
} catch {}
|
|
183
238
|
" 2>/dev/null || true)"
|
|
184
239
|
|
|
185
|
-
if [ "$
|
|
240
|
+
if [ "$NEEDS_TEMP_CONFIG" = "true" ]; then
|
|
186
241
|
TEMP_CONFIG_FILE="$(mktemp)"
|
|
187
242
|
node -e "
|
|
188
243
|
try {
|
|
189
244
|
const fs = require('fs');
|
|
190
245
|
const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
|
191
|
-
|
|
192
|
-
if (
|
|
246
|
+
// 移除 channels.qqbot(插件自定义通道,校验时会 unknown channel id)
|
|
247
|
+
if (cfg.channels?.qqbot) {
|
|
248
|
+
delete cfg.channels.qqbot;
|
|
249
|
+
if (Object.keys(cfg.channels).length === 0) delete cfg.channels;
|
|
250
|
+
}
|
|
251
|
+
// 移除 plugins.allow 中的 openclaw-qqbot(插件目录被备份后校验找不到)
|
|
252
|
+
if (Array.isArray(cfg.plugins?.allow)) {
|
|
253
|
+
cfg.plugins.allow = cfg.plugins.allow.filter(p => p !== '$PLUGIN_ID');
|
|
254
|
+
if (cfg.plugins.allow.length === 0) delete cfg.plugins.allow;
|
|
255
|
+
}
|
|
256
|
+
// 移除 plugins.entries 中的 openclaw-qqbot(同理)
|
|
257
|
+
if (cfg.plugins?.entries?.['$PLUGIN_ID']) {
|
|
258
|
+
delete cfg.plugins.entries['$PLUGIN_ID'];
|
|
259
|
+
if (Object.keys(cfg.plugins.entries).length === 0) delete cfg.plugins.entries;
|
|
260
|
+
}
|
|
193
261
|
fs.writeFileSync('$TEMP_CONFIG_FILE', JSON.stringify(cfg, null, 4) + '\n');
|
|
194
262
|
} catch(e) { process.exit(1); }
|
|
195
263
|
" 2>/dev/null
|
|
196
264
|
if [ $? -eq 0 ]; then
|
|
197
|
-
echo " [兼容] 创建临时配置副本(不含 channels.qqbot)以通过配置校验"
|
|
265
|
+
echo " [兼容] 创建临时配置副本(不含 channels.qqbot / plugins.allow / plugins.entries)以通过配置校验"
|
|
198
266
|
export OPENCLAW_CONFIG_PATH="$TEMP_CONFIG_FILE"
|
|
199
267
|
else
|
|
200
268
|
echo " ⚠️ 创建临时配置失败,继续使用原配置"
|
|
@@ -207,22 +275,32 @@ fi
|
|
|
207
275
|
# plugins install/update 可能把 install 记录写入了临时配置,需要同步回真实配置
|
|
208
276
|
restore_qqbot_channel() {
|
|
209
277
|
if [ -n "$TEMP_CONFIG_FILE" ] && [ -f "$TEMP_CONFIG_FILE" ]; then
|
|
210
|
-
# 将临时配置中 plugins.installs 的变更同步回真实配置
|
|
278
|
+
# 将临时配置中 plugins.installs 和 plugins.entries 的变更同步回真实配置
|
|
211
279
|
node -e "
|
|
212
280
|
try {
|
|
213
281
|
const fs = require('fs');
|
|
214
282
|
const tmp = JSON.parse(fs.readFileSync('$TEMP_CONFIG_FILE', 'utf8'));
|
|
215
283
|
const real = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
|
|
284
|
+
let changed = false;
|
|
216
285
|
if (tmp.plugins && tmp.plugins.installs) {
|
|
217
286
|
if (!real.plugins) real.plugins = {};
|
|
218
287
|
real.plugins.installs = { ...(real.plugins.installs || {}), ...tmp.plugins.installs };
|
|
288
|
+
changed = true;
|
|
289
|
+
}
|
|
290
|
+
// 同步 plugins.entries(openclaw plugins install 会写入 entries)
|
|
291
|
+
if (tmp.plugins && tmp.plugins.entries) {
|
|
292
|
+
if (!real.plugins) real.plugins = {};
|
|
293
|
+
real.plugins.entries = { ...(real.plugins.entries || {}), ...tmp.plugins.entries };
|
|
294
|
+
changed = true;
|
|
295
|
+
}
|
|
296
|
+
if (changed) {
|
|
219
297
|
fs.writeFileSync('$CONFIG_FILE', JSON.stringify(real, null, 4) + '\n');
|
|
220
298
|
}
|
|
221
299
|
} catch {}
|
|
222
300
|
" 2>/dev/null || true
|
|
223
301
|
rm -f "$TEMP_CONFIG_FILE"
|
|
224
302
|
unset OPENCLAW_CONFIG_PATH
|
|
225
|
-
echo " [兼容] 已同步 install 记录并清理临时配置副本"
|
|
303
|
+
echo " [兼容] 已同步 install/entries 记录并清理临时配置副本"
|
|
226
304
|
fi
|
|
227
305
|
}
|
|
228
306
|
|
|
@@ -311,21 +389,57 @@ if [ "$UPGRADE_OK" != "true" ]; then
|
|
|
311
389
|
echo " 执行 install: $INSTALL_SRC"
|
|
312
390
|
|
|
313
391
|
if $CMD plugins install "$INSTALL_SRC" --pin 2>&1; then
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
392
|
+
# install 返回 0,但需要验证插件目录是否真的存在且完整
|
|
393
|
+
if [ -d "$EXTENSIONS_DIR/$PLUGIN_ID" ] && [ -f "$EXTENSIONS_DIR/$PLUGIN_ID/package.json" ]; then
|
|
394
|
+
UPGRADE_OK=true
|
|
395
|
+
INSTALL_COMPLETED=true
|
|
396
|
+
echo " ✅ install 成功"
|
|
397
|
+
# install 成功,清理备份
|
|
398
|
+
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
399
|
+
rm -rf "$BACKUP_DIR"
|
|
400
|
+
echo " 已清理旧版备份"
|
|
401
|
+
fi
|
|
402
|
+
# 清理 openclaw CLI install 可能留下的额外 backup 目录(extensions 内遗留 + 新路径)
|
|
403
|
+
find "$EXTENSIONS_DIR" -maxdepth 1 -name ".openclaw-qqbot-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
404
|
+
find "${EXTENSIONS_DIR:-/dev/null}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
405
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
406
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".qqbot-upgrade-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
407
|
+
else
|
|
408
|
+
echo " ❌ install 命令返回成功但插件目录不完整"
|
|
409
|
+
echo " [诊断] 目录存在: $([ -d "$EXTENSIONS_DIR/$PLUGIN_ID" ] && echo '是' || echo '否')"
|
|
410
|
+
echo " [诊断] package.json 存在: $([ -f "$EXTENSIONS_DIR/$PLUGIN_ID/package.json" ] && echo '是' || echo '否')"
|
|
411
|
+
# 清理可能残留的暂存目录(extensions 和 /tmp 中都可能存在)
|
|
412
|
+
find "${EXTENSIONS_DIR:-/dev/null}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
413
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
414
|
+
# 回滚
|
|
415
|
+
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
416
|
+
rm -rf "$EXTENSIONS_DIR/$PLUGIN_ID" 2>/dev/null || true
|
|
417
|
+
# 备份目录内可能是 PLUGIN_ID 子目录或直接是内容
|
|
418
|
+
if [ -d "$BACKUP_DIR/$PLUGIN_ID" ]; then
|
|
419
|
+
mv "$BACKUP_DIR/$PLUGIN_ID" "$EXTENSIONS_DIR/$PLUGIN_ID"
|
|
420
|
+
else
|
|
421
|
+
mv "$BACKUP_DIR" "$EXTENSIONS_DIR/$PLUGIN_ID"
|
|
422
|
+
fi
|
|
423
|
+
echo " ↩️ 已回滚到旧版本"
|
|
424
|
+
fi
|
|
425
|
+
restore_qqbot_channel
|
|
426
|
+
echo "QQBOT_NEW_VERSION=unknown"
|
|
427
|
+
echo "QQBOT_REPORT=❌ QQBot 安装异常(目录不完整,已回滚),请重试或手动安装"
|
|
428
|
+
exit 1
|
|
320
429
|
fi
|
|
321
|
-
# 清理 openclaw CLI install 可能留下的额外 backup 目录(extensions 内遗留 + 新路径)
|
|
322
|
-
find "$EXTENSIONS_DIR" -maxdepth 1 -name ".openclaw-qqbot-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
323
|
-
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".qqbot-upgrade-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
324
430
|
else
|
|
325
431
|
echo " ❌ install 失败"
|
|
432
|
+
# 清理可能残留的暂存目录(extensions 和 /tmp 中都可能存在)
|
|
433
|
+
find "${EXTENSIONS_DIR:-/dev/null}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
434
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
326
435
|
# 回滚:恢复旧目录
|
|
327
436
|
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
328
|
-
|
|
437
|
+
rm -rf "$EXTENSIONS_DIR/$PLUGIN_ID" 2>/dev/null || true
|
|
438
|
+
if [ -d "$BACKUP_DIR/$PLUGIN_ID" ]; then
|
|
439
|
+
mv "$BACKUP_DIR/$PLUGIN_ID" "$EXTENSIONS_DIR/$PLUGIN_ID"
|
|
440
|
+
else
|
|
441
|
+
mv "$BACKUP_DIR" "$EXTENSIONS_DIR/$PLUGIN_ID"
|
|
442
|
+
fi
|
|
329
443
|
echo " ↩️ 已回滚到旧版本"
|
|
330
444
|
fi
|
|
331
445
|
restore_qqbot_channel
|
|
@@ -262,8 +262,9 @@ if lsof -i :18789 -sTCP:LISTEN >/dev/null 2>&1; then
|
|
|
262
262
|
sleep 1
|
|
263
263
|
fi
|
|
264
264
|
|
|
265
|
-
# 清理之前可能残留的 staging
|
|
265
|
+
# 清理之前可能残留的 staging 目录(extensions 和 /tmp 中都可能存在)
|
|
266
266
|
find "$HOME/.openclaw/extensions/" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
267
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
267
268
|
|
|
268
269
|
# ── 清空并重新构建 dist/ ──
|
|
269
270
|
# openclaw plugins install . 只做文件复制,不执行 npm lifecycle scripts。
|
|
@@ -344,13 +345,13 @@ if [ ! -f "$_INSTALL_DIR/dist/index.js" ] || [ ! -f "$_INSTALL_DIR/preload.cjs"
|
|
|
344
345
|
echo "请先解决安装问题后再运行此脚本。"
|
|
345
346
|
# 恢复 channels.qqbot 后再退出
|
|
346
347
|
if [ -n "$_QQBOT_CHANNEL_STASH" ] && [ -n "$_STASH_CFG" ] && [ -f "$_STASH_CFG" ]; then
|
|
347
|
-
node -e
|
|
348
|
-
const fs = require(
|
|
349
|
-
const cfg = JSON.parse(fs.readFileSync(
|
|
348
|
+
_STASH="$_QQBOT_CHANNEL_STASH" _CFG="$_STASH_CFG" node -e '
|
|
349
|
+
const fs = require("fs");
|
|
350
|
+
const cfg = JSON.parse(fs.readFileSync(process.env._CFG, "utf8"));
|
|
350
351
|
if (!cfg.channels) cfg.channels = {};
|
|
351
|
-
cfg.channels.qqbot =
|
|
352
|
-
fs.writeFileSync(
|
|
353
|
-
|
|
352
|
+
cfg.channels.qqbot = JSON.parse(process.env._STASH);
|
|
353
|
+
fs.writeFileSync(process.env._CFG, JSON.stringify(cfg, null, 4) + "\n");
|
|
354
|
+
' 2>/dev/null || true
|
|
354
355
|
fi
|
|
355
356
|
exit 1
|
|
356
357
|
;;
|
|
@@ -398,6 +399,7 @@ else
|
|
|
398
399
|
echo ""
|
|
399
400
|
echo " 尝试自动修复: 清理残留并重试安装..."
|
|
400
401
|
find "$HOME/.openclaw/extensions/" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
402
|
+
find "${TMPDIR:-/tmp}" -maxdepth 1 -name ".openclaw-install-stage-*" -exec rm -rf {} + 2>/dev/null || true
|
|
401
403
|
if openclaw plugins install . 2>&1 | tee -a "$INSTALL_LOG"; then
|
|
402
404
|
for _candidate_name in openclaw-qqbot qqbot openclaw-qq; do
|
|
403
405
|
if [ -d "$HOME/.openclaw/extensions/$_candidate_name" ]; then
|
|
@@ -592,11 +594,11 @@ echo "[4/6] 准备机器人通道配置..."
|
|
|
592
594
|
# 注意:channels.qqbot 已被暂存移除,所以从 _QQBOT_CHANNEL_STASH 读取
|
|
593
595
|
CURRENT_QQBOT_TOKEN=""
|
|
594
596
|
if [ -n "$_QQBOT_CHANNEL_STASH" ]; then
|
|
595
|
-
CURRENT_QQBOT_TOKEN=$(node -e
|
|
596
|
-
const ch =
|
|
597
|
+
CURRENT_QQBOT_TOKEN=$(_STASH="$_QQBOT_CHANNEL_STASH" node -e '
|
|
598
|
+
const ch = JSON.parse(process.env._STASH);
|
|
597
599
|
if (ch.token) { process.stdout.write(ch.token); }
|
|
598
|
-
else if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId +
|
|
599
|
-
|
|
600
|
+
else if (ch.appId && ch.clientSecret) { process.stdout.write(ch.appId + ":" + ch.clientSecret); }
|
|
601
|
+
' 2>/dev/null || true)
|
|
600
602
|
fi
|
|
601
603
|
|
|
602
604
|
DESIRED_QQBOT_TOKEN=""
|
|
@@ -787,34 +789,39 @@ case "$start_choice" in
|
|
|
787
789
|
|
|
788
790
|
if [ -n "$_target_cfg" ]; then
|
|
789
791
|
# 构建完整的 channels.qqbot 对象(合并暂存配置 + 新 token + markdown)
|
|
790
|
-
node -e "
|
|
791
|
-
|
|
792
|
-
|
|
792
|
+
# 通过环境变量传递,避免 JSON 双引号在 node -e "..." 中被 shell 错误解析
|
|
793
|
+
_STASH="$_QQBOT_CHANNEL_STASH" \
|
|
794
|
+
_DESIRED="$DESIRED_QQBOT_TOKEN" \
|
|
795
|
+
_MD="$MARKDOWN_VALUE" \
|
|
796
|
+
_CFG="$_target_cfg" \
|
|
797
|
+
node -e '
|
|
798
|
+
const fs = require("fs");
|
|
799
|
+
const cfg = JSON.parse(fs.readFileSync(process.env._CFG, "utf8"));
|
|
793
800
|
if (!cfg.channels) cfg.channels = {};
|
|
794
801
|
|
|
795
802
|
// 从暂存恢复基础配置
|
|
796
|
-
const stash =
|
|
803
|
+
const stash = process.env._STASH;
|
|
797
804
|
if (stash) {
|
|
798
805
|
try { cfg.channels.qqbot = JSON.parse(stash); } catch {}
|
|
799
806
|
}
|
|
800
807
|
if (!cfg.channels.qqbot) cfg.channels.qqbot = {};
|
|
801
808
|
|
|
802
809
|
// 覆盖 token(如果有新值)
|
|
803
|
-
const desired =
|
|
804
|
-
if (desired && desired.includes(
|
|
805
|
-
const [appId, ...rest] = desired.split(
|
|
810
|
+
const desired = process.env._DESIRED;
|
|
811
|
+
if (desired && desired.includes(":")) {
|
|
812
|
+
const [appId, ...rest] = desired.split(":");
|
|
806
813
|
cfg.channels.qqbot.appId = appId;
|
|
807
|
-
cfg.channels.qqbot.clientSecret = rest.join(
|
|
814
|
+
cfg.channels.qqbot.clientSecret = rest.join(":");
|
|
808
815
|
delete cfg.channels.qqbot.token;
|
|
809
816
|
}
|
|
810
817
|
|
|
811
818
|
// 覆盖 markdown(如果有指定)
|
|
812
|
-
const md =
|
|
813
|
-
if (md ===
|
|
814
|
-
else if (md ===
|
|
819
|
+
const md = process.env._MD;
|
|
820
|
+
if (md === "true") cfg.channels.qqbot.markdownSupport = true;
|
|
821
|
+
else if (md === "false") cfg.channels.qqbot.markdownSupport = false;
|
|
815
822
|
|
|
816
|
-
fs.writeFileSync(
|
|
817
|
-
|
|
823
|
+
fs.writeFileSync(process.env._CFG, JSON.stringify(cfg, null, 4) + "\n");
|
|
824
|
+
' 2>&1 || echo " ⚠️ 配置写入失败"
|
|
818
825
|
echo " ✅ 已恢复 channels.qqbot 配置(含 token/markdown)"
|
|
819
826
|
_need_reload=1
|
|
820
827
|
fi
|
|
@@ -884,13 +891,13 @@ case "$start_choice" in
|
|
|
884
891
|
# 注意:下次 gateway 启动可能因 "unknown channel id" 失败,
|
|
885
892
|
# 需要用户手动 stop → 移除 channels.qqbot → start → 恢复
|
|
886
893
|
if [ -n "$_QQBOT_CHANNEL_STASH" ] && [ -n "$_STASH_CFG" ] && [ -f "$_STASH_CFG" ]; then
|
|
887
|
-
node -e
|
|
888
|
-
const fs = require(
|
|
889
|
-
const cfg = JSON.parse(fs.readFileSync(
|
|
894
|
+
_STASH="$_QQBOT_CHANNEL_STASH" _CFG="$_STASH_CFG" node -e '
|
|
895
|
+
const fs = require("fs");
|
|
896
|
+
const cfg = JSON.parse(fs.readFileSync(process.env._CFG, "utf8"));
|
|
890
897
|
if (!cfg.channels) cfg.channels = {};
|
|
891
|
-
cfg.channels.qqbot =
|
|
892
|
-
fs.writeFileSync(
|
|
893
|
-
|
|
898
|
+
cfg.channels.qqbot = JSON.parse(process.env._STASH);
|
|
899
|
+
fs.writeFileSync(process.env._CFG, JSON.stringify(cfg, null, 4) + "\n");
|
|
900
|
+
' 2>/dev/null || true
|
|
894
901
|
echo " 已恢复 channels.qqbot 配置"
|
|
895
902
|
fi
|
|
896
903
|
echo ""
|
package/src/api.ts
CHANGED
|
@@ -137,7 +137,7 @@ async function doFetchToken(appId: string, clientSecret: string): Promise<string
|
|
|
137
137
|
const requestHeaders = { "Content-Type": "application/json", "User-Agent": PLUGIN_USER_AGENT };
|
|
138
138
|
|
|
139
139
|
// 打印请求信息(隐藏敏感信息)
|
|
140
|
-
console.log(`[qqbot-api:${appId}] >>> POST ${TOKEN_URL}`);
|
|
140
|
+
console.log(`[qqbot-api:${appId}] >>> POST ${TOKEN_URL} [secret: ${clientSecret.slice(0, 6)}...len=${clientSecret.length}]`);
|
|
141
141
|
|
|
142
142
|
let response: Response;
|
|
143
143
|
try {
|