@tencent-connect/openclaw-qqbot 1.6.5-alpha.5 → 1.6.5-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/gateway.js +30 -1
- package/dist/src/slash-commands.js +90 -22
- package/package.json +1 -1
- package/scripts/upgrade-via-npm.sh +38 -5
- package/scripts/upgrade-via-source.sh +21 -0
- package/src/gateway.ts +30 -1
- package/src/slash-commands.ts +97 -24
package/dist/src/gateway.js
CHANGED
|
@@ -87,6 +87,22 @@ export async function startGateway(ctx) {
|
|
|
87
87
|
log?.info(`[qqbot:${account.accountId}] ${w}`);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
+
// 预检 openclaw runtime 模块是否可正常解析(兼容性诊断)
|
|
91
|
+
// openclaw 3.23+ 存在 plugin-sdk/root-alias.cjs 回归 bug,
|
|
92
|
+
// 内置插件(qwen-portal-auth 等)全部加载失败,导致 AI agent 调用返回
|
|
93
|
+
// "Unable to resolve plugin runtime module"。提前检测并告警。
|
|
94
|
+
try {
|
|
95
|
+
const pluginRuntime = getQQBotRuntime();
|
|
96
|
+
if (pluginRuntime?.channel?.reply?.dispatchReplyWithBufferedBlockDispatcher) {
|
|
97
|
+
log?.info(`[qqbot:${account.accountId}] Runtime module preflight: OK`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ Runtime preflight: dispatchReply API 不可用,AI 消息处理可能失败。请检查 openclaw 版本兼容性`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (preflightErr) {
|
|
104
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ Runtime preflight failed: ${preflightErr}. AI 消息处理可能失败`);
|
|
105
|
+
}
|
|
90
106
|
// 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)
|
|
91
107
|
triggerUpdateCheck(log);
|
|
92
108
|
// 初始化 API 配置(markdown 支持)
|
|
@@ -883,8 +899,13 @@ export async function startGateway(ctx) {
|
|
|
883
899
|
clearTimeout(timeoutId);
|
|
884
900
|
timeoutId = null;
|
|
885
901
|
}
|
|
886
|
-
// 发送错误提示给用户,显示完整错误信息
|
|
887
902
|
const errMsg = String(err);
|
|
903
|
+
// 兼容 openclaw 3.23+ 的 plugin-sdk/root-alias.cjs 模块解析失败
|
|
904
|
+
if (errMsg.includes("Unable to resolve plugin runtime module") || errMsg.includes("root-alias.cjs")) {
|
|
905
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ openclaw 框架 runtime 模块解析失败,可能是 openclaw 版本与 plugin-sdk 不兼容。请尝试: npm install -g openclaw@latest && openclaw gateway restart`);
|
|
906
|
+
await sendErrorMessage("⚠️ AI 服务暂时不可用:openclaw 框架运行时模块加载失败。\n\n请管理员执行:\nnpm install -g openclaw@latest\nopenclaw gateway restart\n\n斜杠命令(如 /bot-ping)不受影响。");
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
888
909
|
if (errMsg.includes("401") || errMsg.includes("key") || errMsg.includes("auth")) {
|
|
889
910
|
log?.error(`[qqbot:${account.accountId}] AI auth error: ${errMsg}`);
|
|
890
911
|
}
|
|
@@ -929,7 +950,15 @@ export async function startGateway(ctx) {
|
|
|
929
950
|
}
|
|
930
951
|
}
|
|
931
952
|
catch (err) {
|
|
953
|
+
const errStr = String(err);
|
|
932
954
|
log?.error(`[qqbot:${account.accountId}] Message processing failed: ${err}`);
|
|
955
|
+
// 兼容 openclaw 3.23+ runtime 模块解析失败:给用户发可操作的提示
|
|
956
|
+
if (errStr.includes("Unable to resolve plugin runtime module") || errStr.includes("root-alias.cjs")) {
|
|
957
|
+
try {
|
|
958
|
+
await sendErrorMessage("⚠️ AI 服务暂时不可用:openclaw 框架运行时模块加载失败。\n\n请管理员执行:\nnpm install -g openclaw@latest\nopenclaw gateway restart\n\n斜杠命令(如 /bot-ping)不受影响。");
|
|
959
|
+
}
|
|
960
|
+
catch { /* best-effort */ }
|
|
961
|
+
}
|
|
933
962
|
}
|
|
934
963
|
finally {
|
|
935
964
|
// 无论成功/失败/超时,都停止输入状态续期
|
|
@@ -251,7 +251,7 @@ registerCommand({
|
|
|
251
251
|
return lines.join("\n");
|
|
252
252
|
},
|
|
253
253
|
});
|
|
254
|
-
const DEFAULT_UPGRADE_URL = "https://
|
|
254
|
+
const DEFAULT_UPGRADE_URL = "https://docs.qq.com/doc/DSGxOZk1oVnVKVkpq";
|
|
255
255
|
function saveUpgradeGreetingTarget(accountId, appId, openid) {
|
|
256
256
|
const safeAccountId = accountId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
257
257
|
const safeAppId = appId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -561,6 +561,38 @@ function findPowerShell() {
|
|
|
561
561
|
}
|
|
562
562
|
return null;
|
|
563
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* 将升级脚本复制到临时位置,避免升级过程中插件目录被清理后脚本丢失。
|
|
566
|
+
* 返回临时脚本路径,失败返回 null。
|
|
567
|
+
*/
|
|
568
|
+
function copyScriptToTemp(scriptPath) {
|
|
569
|
+
try {
|
|
570
|
+
const ext = path.extname(scriptPath);
|
|
571
|
+
const tmpDir = path.join(getHomeDir(), ".openclaw", ".qqbot-upgrade-tmp");
|
|
572
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
573
|
+
const tmpScript = path.join(tmpDir, `upgrade-via-npm${ext}`);
|
|
574
|
+
fs.copyFileSync(scriptPath, tmpScript);
|
|
575
|
+
if (!isWindows()) {
|
|
576
|
+
fs.chmodSync(tmpScript, 0o755);
|
|
577
|
+
}
|
|
578
|
+
return tmpScript;
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* 清理临时升级脚本目录
|
|
586
|
+
*/
|
|
587
|
+
function cleanupTempScript() {
|
|
588
|
+
try {
|
|
589
|
+
const tmpDir = path.join(getHomeDir(), ".openclaw", ".qqbot-upgrade-tmp");
|
|
590
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
// 非关键,静默忽略
|
|
594
|
+
}
|
|
595
|
+
}
|
|
564
596
|
/**
|
|
565
597
|
* 执行热更新:执行脚本(--no-restart) → 立即触发 gateway restart
|
|
566
598
|
*
|
|
@@ -570,11 +602,15 @@ function findPowerShell() {
|
|
|
570
602
|
* - 新进程启动时 getStartupGreeting() 检测到版本变更,自动通知管理员
|
|
571
603
|
*
|
|
572
604
|
* Windows 使用 PowerShell 执行 .ps1 脚本,Mac/Linux 使用 bash 执行 .sh 脚本。
|
|
605
|
+
*
|
|
606
|
+
* 安全机制:脚本会被复制到临时目录再执行,避免升级过程中插件目录被操作导致脚本自身丢失。
|
|
573
607
|
*/
|
|
574
608
|
function fireHotUpgrade(targetVersion) {
|
|
575
|
-
const
|
|
576
|
-
if (!
|
|
609
|
+
const originalScriptPath = getUpgradeScriptPath();
|
|
610
|
+
if (!originalScriptPath)
|
|
577
611
|
return { ok: false, reason: "no-script" };
|
|
612
|
+
// 将脚本复制到临时位置,避免升级过程中脚本被删除
|
|
613
|
+
const scriptPath = copyScriptToTemp(originalScriptPath) || originalScriptPath;
|
|
578
614
|
const cli = findCli();
|
|
579
615
|
if (!cli)
|
|
580
616
|
return { ok: false, reason: "no-cli" };
|
|
@@ -601,7 +637,7 @@ function fireHotUpgrade(targetVersion) {
|
|
|
601
637
|
shell = bash;
|
|
602
638
|
shellArgs = [scriptPath, "--no-restart", ...(targetVersion ? ["--version", targetVersion] : [])];
|
|
603
639
|
}
|
|
604
|
-
console.log(`[qqbot] fireHotUpgrade: shell=${shell}, script=${scriptPath}, cli=${cli}, target=${targetVersion || "latest"}`);
|
|
640
|
+
console.log(`[qqbot] fireHotUpgrade: shell=${shell}, script=${scriptPath} (original: ${originalScriptPath}), cli=${cli}, target=${targetVersion || "latest"}`);
|
|
605
641
|
// 异步执行升级脚本
|
|
606
642
|
execFile(shell, shellArgs, {
|
|
607
643
|
timeout: 120_000,
|
|
@@ -614,6 +650,7 @@ function fireHotUpgrade(targetVersion) {
|
|
|
614
650
|
console.error(`[qqbot] fireHotUpgrade: stdout: ${stdout.slice(0, 2000)}`);
|
|
615
651
|
if (_stderr)
|
|
616
652
|
console.error(`[qqbot] fireHotUpgrade: stderr: ${_stderr.slice(0, 2000)}`);
|
|
653
|
+
cleanupTempScript();
|
|
617
654
|
_upgrading = false;
|
|
618
655
|
return;
|
|
619
656
|
}
|
|
@@ -623,10 +660,13 @@ function fireHotUpgrade(targetVersion) {
|
|
|
623
660
|
const newVersion = versionMatch?.[1];
|
|
624
661
|
if (newVersion === "unknown") {
|
|
625
662
|
console.error(`[qqbot] fireHotUpgrade: script output QQBOT_NEW_VERSION=unknown, aborting restart`);
|
|
663
|
+
cleanupTempScript();
|
|
626
664
|
_upgrading = false;
|
|
627
665
|
return;
|
|
628
666
|
}
|
|
629
667
|
console.log(`[qqbot] fireHotUpgrade: new version=${newVersion || "(not detected)"}, triggering restart...`);
|
|
668
|
+
// 脚本执行成功,清理临时脚本副本
|
|
669
|
+
cleanupTempScript();
|
|
630
670
|
// 文件替换成功,在 restart 之前把 source 从 path 切换为 npm,
|
|
631
671
|
// 确保新进程启动时读到的是 npm source,不会被本地源码覆盖。
|
|
632
672
|
switchPluginSourceToNpm();
|
|
@@ -686,7 +726,11 @@ function fireHotUpgrade(targetVersion) {
|
|
|
686
726
|
/**
|
|
687
727
|
* /bot-upgrade — 统一升级入口
|
|
688
728
|
*
|
|
689
|
-
*
|
|
729
|
+
* upgradeMode 开关:
|
|
730
|
+
* - "doc"(默认):只展示升级指引文档,不执行热更新
|
|
731
|
+
* - "hot-reload":执行 npm 升级脚本进行热更新
|
|
732
|
+
*
|
|
733
|
+
* 热更新模式下的产品流程:
|
|
690
734
|
* /bot-upgrade — 展示版本信息+确认按钮(不直接升级)
|
|
691
735
|
* /bot-upgrade --latest — 确认升级到最新版本
|
|
692
736
|
* /bot-upgrade --version X — 升级到指定版本
|
|
@@ -695,21 +739,49 @@ function fireHotUpgrade(targetVersion) {
|
|
|
695
739
|
let _upgrading = false; // 升级锁
|
|
696
740
|
registerCommand({
|
|
697
741
|
name: "bot-upgrade",
|
|
698
|
-
description: "
|
|
742
|
+
description: "检查更新并查看升级指引",
|
|
699
743
|
usage: [
|
|
700
|
-
`/bot-upgrade
|
|
701
|
-
`/bot-upgrade --latest
|
|
702
|
-
`/bot-upgrade --version X
|
|
703
|
-
`/bot-upgrade --force
|
|
704
|
-
``,
|
|
705
|
-
`⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
|
|
706
|
-
``,
|
|
707
|
-
`环境要求:`,
|
|
708
|
-
` - 操作系统:macOS / Linux / Windows`,
|
|
709
|
-
` - OpenClaw 框架版本 ≥ ${UPGRADE_REQUIREMENTS.minFrameworkVersion}`,
|
|
710
|
-
` - Node.js ≥ v${UPGRADE_REQUIREMENTS.minNodeVersion}`,
|
|
744
|
+
`/bot-upgrade 检查是否有新版本`,
|
|
745
|
+
`/bot-upgrade --latest 确认升级到最新版本(需 upgradeMode=hot-reload)`,
|
|
746
|
+
`/bot-upgrade --version X 升级到指定版本(需 upgradeMode=hot-reload)`,
|
|
747
|
+
`/bot-upgrade --force 强制重新安装当前版本(需 upgradeMode=hot-reload)`,
|
|
711
748
|
].join("\n"),
|
|
712
749
|
handler: async (ctx) => {
|
|
750
|
+
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
751
|
+
const upgradeMode = ctx.accountConfig?.upgradeMode || "doc";
|
|
752
|
+
const args = ctx.args.trim();
|
|
753
|
+
const info = await getUpdateInfo();
|
|
754
|
+
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
755
|
+
// ── doc 模式(默认):只展示升级指引,不执行热更新 ──
|
|
756
|
+
if (upgradeMode !== "hot-reload") {
|
|
757
|
+
if (info.checkedAt === 0) {
|
|
758
|
+
return `⏳ 版本检查中,请稍后再试`;
|
|
759
|
+
}
|
|
760
|
+
if (info.error) {
|
|
761
|
+
return [
|
|
762
|
+
`❌ 主机网络访问异常,无法检查更新`,
|
|
763
|
+
``,
|
|
764
|
+
`查看升级指引:[点击查看](${url})`,
|
|
765
|
+
].join("\n");
|
|
766
|
+
}
|
|
767
|
+
if (!info.hasUpdate) {
|
|
768
|
+
return [
|
|
769
|
+
`✅ 当前已是最新版本 v${PLUGIN_VERSION}`,
|
|
770
|
+
``,
|
|
771
|
+
`项目地址:[GitHub](${GITHUB_URL})`,
|
|
772
|
+
].join("\n");
|
|
773
|
+
}
|
|
774
|
+
return [
|
|
775
|
+
`🆕 发现新版本`,
|
|
776
|
+
``,
|
|
777
|
+
`当前版本:**v${PLUGIN_VERSION}**`,
|
|
778
|
+
`最新版本:**v${info.latest}**`,
|
|
779
|
+
``,
|
|
780
|
+
`📖 升级指引:[点击查看](${url})`,
|
|
781
|
+
`🌟 官方 GitHub 仓库:[点击前往](${GITHUB_URL})`,
|
|
782
|
+
].join("\n");
|
|
783
|
+
}
|
|
784
|
+
// ── hot-reload 模式:执行热更新 ──
|
|
713
785
|
// 升级相关指令仅在私聊中可用
|
|
714
786
|
if (ctx.type !== "c2c") {
|
|
715
787
|
return `💡 请在私聊中使用此指令`;
|
|
@@ -718,9 +790,6 @@ registerCommand({
|
|
|
718
790
|
if (_upgrading) {
|
|
719
791
|
return `⏳ 正在升级中,请稍候...`;
|
|
720
792
|
}
|
|
721
|
-
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
722
|
-
const args = ctx.args.trim();
|
|
723
|
-
const info = await getUpdateInfo();
|
|
724
793
|
let isForce = false;
|
|
725
794
|
let isLatest = false;
|
|
726
795
|
let versionArg;
|
|
@@ -757,7 +826,6 @@ registerCommand({
|
|
|
757
826
|
continue;
|
|
758
827
|
}
|
|
759
828
|
}
|
|
760
|
-
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
761
829
|
// ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
|
|
762
830
|
if (!versionArg && !isLatest && !isForce) {
|
|
763
831
|
if (info.checkedAt === 0) {
|
|
@@ -778,7 +846,7 @@ registerCommand({
|
|
|
778
846
|
];
|
|
779
847
|
return lines.join("\n");
|
|
780
848
|
}
|
|
781
|
-
// 有新版本:展示信息 +
|
|
849
|
+
// 有新版本:展示信息 + 确认按钮
|
|
782
850
|
return [
|
|
783
851
|
`🆕 发现新版本`,
|
|
784
852
|
``,
|
package/package.json
CHANGED
|
@@ -230,20 +230,40 @@ if [ "$USE_UPDATE" = "true" ]; then
|
|
|
230
230
|
fi
|
|
231
231
|
|
|
232
232
|
if [ "$UPGRADE_OK" != "true" ]; then
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
# 备份旧目录(而非直接删除),install 失败时可回滚
|
|
234
|
+
BACKUP_DIR=""
|
|
235
|
+
if [ -d "$EXTENSIONS_DIR/$PLUGIN_ID" ]; then
|
|
236
|
+
BACKUP_DIR="$EXTENSIONS_DIR/.openclaw-qqbot-backup-$$"
|
|
237
|
+
mv "$EXTENSIONS_DIR/$PLUGIN_ID" "$BACKUP_DIR"
|
|
238
|
+
echo " 已备份旧目录: $BACKUP_DIR"
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
# 清理历史遗留名称(这些不需要回滚)
|
|
242
|
+
for dir_name in qqbot openclaw-qq; do
|
|
243
|
+
[ -d "$EXTENSIONS_DIR/$dir_name" ] && rm -rf "$EXTENSIONS_DIR/$dir_name" && echo " 已清理历史目录: $EXTENSIONS_DIR/$dir_name"
|
|
236
244
|
done
|
|
237
245
|
|
|
238
246
|
echo " 执行 install: $INSTALL_SRC"
|
|
239
247
|
if $CMD plugins install "$INSTALL_SRC" --pin 2>&1; then
|
|
240
248
|
UPGRADE_OK=true
|
|
241
249
|
echo " ✅ install 成功"
|
|
250
|
+
# install 成功,清理备份
|
|
251
|
+
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
252
|
+
rm -rf "$BACKUP_DIR"
|
|
253
|
+
echo " 已清理旧版备份"
|
|
254
|
+
fi
|
|
255
|
+
# 清理 openclaw CLI install 可能留下的额外 backup 目录
|
|
256
|
+
find "$EXTENSIONS_DIR" -maxdepth 1 -name ".openclaw-qqbot-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
242
257
|
else
|
|
243
|
-
echo "❌ install 失败"
|
|
258
|
+
echo " ❌ install 失败"
|
|
259
|
+
# 回滚:恢复旧目录
|
|
260
|
+
if [ -n "$BACKUP_DIR" ] && [ -d "$BACKUP_DIR" ]; then
|
|
261
|
+
mv "$BACKUP_DIR" "$EXTENSIONS_DIR/$PLUGIN_ID"
|
|
262
|
+
echo " ↩️ 已回滚到旧版本"
|
|
263
|
+
fi
|
|
244
264
|
restore_qqbot_channel
|
|
245
265
|
echo "QQBOT_NEW_VERSION=unknown"
|
|
246
|
-
echo "QQBOT_REPORT=❌ QQBot
|
|
266
|
+
echo "QQBOT_REPORT=❌ QQBot 安装失败(已回滚到旧版本),请检查网络和 npm registry"
|
|
247
267
|
exit 1
|
|
248
268
|
fi
|
|
249
269
|
fi
|
|
@@ -336,6 +356,19 @@ if [ "$PREFLIGHT_OK" != "true" ]; then
|
|
|
336
356
|
fi
|
|
337
357
|
echo " ✅ 验证全部通过"
|
|
338
358
|
|
|
359
|
+
# 确保 openclaw/plugin-sdk 可解析:
|
|
360
|
+
# openclaw plugins install 不会执行 npm lifecycle scripts,
|
|
361
|
+
# 需要手动调用 postinstall-link-sdk.js 创建 node_modules/openclaw → 全局 openclaw 的符号链接
|
|
362
|
+
POSTINSTALL_SCRIPT="$TARGET_DIR/scripts/postinstall-link-sdk.js"
|
|
363
|
+
if [ -f "$POSTINSTALL_SCRIPT" ]; then
|
|
364
|
+
echo " 执行 postinstall-link-sdk..."
|
|
365
|
+
if node "$POSTINSTALL_SCRIPT" 2>&1; then
|
|
366
|
+
echo " ✅ plugin-sdk 链接就绪"
|
|
367
|
+
else
|
|
368
|
+
echo " ⚠️ postinstall-link-sdk 失败,插件可能无法加载"
|
|
369
|
+
fi
|
|
370
|
+
fi
|
|
371
|
+
|
|
339
372
|
# [3/4] 输出结构化信息(供 TS handler 解析)
|
|
340
373
|
echo ""
|
|
341
374
|
echo "[3/4] 升级结果..."
|
|
@@ -444,6 +444,27 @@ else
|
|
|
444
444
|
# gateway 已在安装前 stop,此时不会有自动 restart 的问题
|
|
445
445
|
# 所有配置写入完成后,在 Step 6 统一启动
|
|
446
446
|
|
|
447
|
+
# 确保 openclaw/plugin-sdk 可解析:
|
|
448
|
+
# openclaw plugins install 不会执行 npm lifecycle scripts,
|
|
449
|
+
# 需要手动调用 postinstall-link-sdk.js 创建 node_modules/openclaw → 全局 openclaw 的符号链接。
|
|
450
|
+
# 必须在 peerDeps 清理之后执行,否则 symlink 会被清理逻辑删除。
|
|
451
|
+
for _candidate_name in openclaw-qqbot qqbot openclaw-qq; do
|
|
452
|
+
_postinstall="$HOME/.openclaw/extensions/$_candidate_name/scripts/postinstall-link-sdk.js"
|
|
453
|
+
if [ -f "$_postinstall" ]; then
|
|
454
|
+
echo " 执行 postinstall-link-sdk..."
|
|
455
|
+
if node "$_postinstall" 2>&1; then
|
|
456
|
+
echo " ✅ plugin-sdk 链接就绪"
|
|
457
|
+
else
|
|
458
|
+
echo " ⚠️ postinstall-link-sdk 失败,插件可能无法加载"
|
|
459
|
+
fi
|
|
460
|
+
break
|
|
461
|
+
fi
|
|
462
|
+
done
|
|
463
|
+
|
|
464
|
+
# 清理 openclaw CLI install 留下的 backup 目录,
|
|
465
|
+
# 避免 gateway 发现两个同 id 插件不断刷 duplicate plugin id 警告
|
|
466
|
+
find "$HOME/.openclaw/extensions/" -maxdepth 1 -name ".openclaw-qqbot-backup-*" -exec rm -rf {} + 2>/dev/null || true
|
|
467
|
+
|
|
447
468
|
# 记录更新后的 qqbot 插件版本
|
|
448
469
|
NEW_QQBOT_VERSION=$(node -e '
|
|
449
470
|
try {
|
package/src/gateway.ts
CHANGED
|
@@ -112,6 +112,21 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// 预检 openclaw runtime 模块是否可正常解析(兼容性诊断)
|
|
116
|
+
// openclaw 3.23+ 存在 plugin-sdk/root-alias.cjs 回归 bug,
|
|
117
|
+
// 内置插件(qwen-portal-auth 等)全部加载失败,导致 AI agent 调用返回
|
|
118
|
+
// "Unable to resolve plugin runtime module"。提前检测并告警。
|
|
119
|
+
try {
|
|
120
|
+
const pluginRuntime = getQQBotRuntime();
|
|
121
|
+
if (pluginRuntime?.channel?.reply?.dispatchReplyWithBufferedBlockDispatcher) {
|
|
122
|
+
log?.info(`[qqbot:${account.accountId}] Runtime module preflight: OK`);
|
|
123
|
+
} else {
|
|
124
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ Runtime preflight: dispatchReply API 不可用,AI 消息处理可能失败。请检查 openclaw 版本兼容性`);
|
|
125
|
+
}
|
|
126
|
+
} catch (preflightErr) {
|
|
127
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ Runtime preflight failed: ${preflightErr}. AI 消息处理可能失败`);
|
|
128
|
+
}
|
|
129
|
+
|
|
115
130
|
// 后台版本检查(供 /bot-version、/bot-upgrade 指令被动查询)
|
|
116
131
|
triggerUpdateCheck(log);
|
|
117
132
|
|
|
@@ -1019,8 +1034,15 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
1019
1034
|
timeoutId = null;
|
|
1020
1035
|
}
|
|
1021
1036
|
|
|
1022
|
-
// 发送错误提示给用户,显示完整错误信息
|
|
1023
1037
|
const errMsg = String(err);
|
|
1038
|
+
|
|
1039
|
+
// 兼容 openclaw 3.23+ 的 plugin-sdk/root-alias.cjs 模块解析失败
|
|
1040
|
+
if (errMsg.includes("Unable to resolve plugin runtime module") || errMsg.includes("root-alias.cjs")) {
|
|
1041
|
+
log?.error(`[qqbot:${account.accountId}] ⚠️ openclaw 框架 runtime 模块解析失败,可能是 openclaw 版本与 plugin-sdk 不兼容。请尝试: npm install -g openclaw@latest && openclaw gateway restart`);
|
|
1042
|
+
await sendErrorMessage("⚠️ AI 服务暂时不可用:openclaw 框架运行时模块加载失败。\n\n请管理员执行:\nnpm install -g openclaw@latest\nopenclaw gateway restart\n\n斜杠命令(如 /bot-ping)不受影响。");
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1024
1046
|
if (errMsg.includes("401") || errMsg.includes("key") || errMsg.includes("auth")) {
|
|
1025
1047
|
log?.error(`[qqbot:${account.accountId}] AI auth error: ${errMsg}`);
|
|
1026
1048
|
} else {
|
|
@@ -1062,7 +1084,14 @@ export async function startGateway(ctx: GatewayContext): Promise<void> {
|
|
|
1062
1084
|
}
|
|
1063
1085
|
}
|
|
1064
1086
|
} catch (err) {
|
|
1087
|
+
const errStr = String(err);
|
|
1065
1088
|
log?.error(`[qqbot:${account.accountId}] Message processing failed: ${err}`);
|
|
1089
|
+
// 兼容 openclaw 3.23+ runtime 模块解析失败:给用户发可操作的提示
|
|
1090
|
+
if (errStr.includes("Unable to resolve plugin runtime module") || errStr.includes("root-alias.cjs")) {
|
|
1091
|
+
try {
|
|
1092
|
+
await sendErrorMessage("⚠️ AI 服务暂时不可用:openclaw 框架运行时模块加载失败。\n\n请管理员执行:\nnpm install -g openclaw@latest\nopenclaw gateway restart\n\n斜杠命令(如 /bot-ping)不受影响。");
|
|
1093
|
+
} catch { /* best-effort */ }
|
|
1094
|
+
}
|
|
1066
1095
|
} finally {
|
|
1067
1096
|
// 无论成功/失败/超时,都停止输入状态续期
|
|
1068
1097
|
typing.keepAlive?.stop();
|
package/src/slash-commands.ts
CHANGED
|
@@ -337,7 +337,7 @@ registerCommand({
|
|
|
337
337
|
},
|
|
338
338
|
});
|
|
339
339
|
|
|
340
|
-
const DEFAULT_UPGRADE_URL = "https://
|
|
340
|
+
const DEFAULT_UPGRADE_URL = "https://docs.qq.com/doc/DSGxOZk1oVnVKVkpq";
|
|
341
341
|
|
|
342
342
|
function saveUpgradeGreetingTarget(accountId: string, appId: string, openid: string): void {
|
|
343
343
|
const safeAccountId = accountId.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -660,6 +660,38 @@ function findPowerShell(): string | null {
|
|
|
660
660
|
return null;
|
|
661
661
|
}
|
|
662
662
|
|
|
663
|
+
/**
|
|
664
|
+
* 将升级脚本复制到临时位置,避免升级过程中插件目录被清理后脚本丢失。
|
|
665
|
+
* 返回临时脚本路径,失败返回 null。
|
|
666
|
+
*/
|
|
667
|
+
function copyScriptToTemp(scriptPath: string): string | null {
|
|
668
|
+
try {
|
|
669
|
+
const ext = path.extname(scriptPath);
|
|
670
|
+
const tmpDir = path.join(getHomeDir(), ".openclaw", ".qqbot-upgrade-tmp");
|
|
671
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
672
|
+
const tmpScript = path.join(tmpDir, `upgrade-via-npm${ext}`);
|
|
673
|
+
fs.copyFileSync(scriptPath, tmpScript);
|
|
674
|
+
if (!isWindows()) {
|
|
675
|
+
fs.chmodSync(tmpScript, 0o755);
|
|
676
|
+
}
|
|
677
|
+
return tmpScript;
|
|
678
|
+
} catch {
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* 清理临时升级脚本目录
|
|
685
|
+
*/
|
|
686
|
+
function cleanupTempScript(): void {
|
|
687
|
+
try {
|
|
688
|
+
const tmpDir = path.join(getHomeDir(), ".openclaw", ".qqbot-upgrade-tmp");
|
|
689
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
690
|
+
} catch {
|
|
691
|
+
// 非关键,静默忽略
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
663
695
|
/**
|
|
664
696
|
* 执行热更新:执行脚本(--no-restart) → 立即触发 gateway restart
|
|
665
697
|
*
|
|
@@ -669,10 +701,15 @@ function findPowerShell(): string | null {
|
|
|
669
701
|
* - 新进程启动时 getStartupGreeting() 检测到版本变更,自动通知管理员
|
|
670
702
|
*
|
|
671
703
|
* Windows 使用 PowerShell 执行 .ps1 脚本,Mac/Linux 使用 bash 执行 .sh 脚本。
|
|
704
|
+
*
|
|
705
|
+
* 安全机制:脚本会被复制到临时目录再执行,避免升级过程中插件目录被操作导致脚本自身丢失。
|
|
672
706
|
*/
|
|
673
707
|
function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
674
|
-
const
|
|
675
|
-
if (!
|
|
708
|
+
const originalScriptPath = getUpgradeScriptPath();
|
|
709
|
+
if (!originalScriptPath) return { ok: false, reason: "no-script" };
|
|
710
|
+
|
|
711
|
+
// 将脚本复制到临时位置,避免升级过程中脚本被删除
|
|
712
|
+
const scriptPath = copyScriptToTemp(originalScriptPath) || originalScriptPath;
|
|
676
713
|
|
|
677
714
|
const cli = findCli();
|
|
678
715
|
if (!cli) return { ok: false, reason: "no-cli" };
|
|
@@ -699,7 +736,7 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
|
699
736
|
shellArgs = [scriptPath, "--no-restart", ...(targetVersion ? ["--version", targetVersion] : [])];
|
|
700
737
|
}
|
|
701
738
|
|
|
702
|
-
console.log(`[qqbot] fireHotUpgrade: shell=${shell}, script=${scriptPath}, cli=${cli}, target=${targetVersion || "latest"}`);
|
|
739
|
+
console.log(`[qqbot] fireHotUpgrade: shell=${shell}, script=${scriptPath} (original: ${originalScriptPath}), cli=${cli}, target=${targetVersion || "latest"}`);
|
|
703
740
|
|
|
704
741
|
// 异步执行升级脚本
|
|
705
742
|
execFile(shell, shellArgs, {
|
|
@@ -711,6 +748,7 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
|
711
748
|
console.error(`[qqbot] fireHotUpgrade: script failed: ${error.message}`);
|
|
712
749
|
if (stdout) console.error(`[qqbot] fireHotUpgrade: stdout: ${stdout.slice(0, 2000)}`);
|
|
713
750
|
if (_stderr) console.error(`[qqbot] fireHotUpgrade: stderr: ${_stderr.slice(0, 2000)}`);
|
|
751
|
+
cleanupTempScript();
|
|
714
752
|
_upgrading = false;
|
|
715
753
|
return;
|
|
716
754
|
}
|
|
@@ -722,12 +760,16 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
|
722
760
|
const newVersion = versionMatch?.[1];
|
|
723
761
|
if (newVersion === "unknown") {
|
|
724
762
|
console.error(`[qqbot] fireHotUpgrade: script output QQBOT_NEW_VERSION=unknown, aborting restart`);
|
|
763
|
+
cleanupTempScript();
|
|
725
764
|
_upgrading = false;
|
|
726
765
|
return;
|
|
727
766
|
}
|
|
728
767
|
|
|
729
768
|
console.log(`[qqbot] fireHotUpgrade: new version=${newVersion || "(not detected)"}, triggering restart...`);
|
|
730
769
|
|
|
770
|
+
// 脚本执行成功,清理临时脚本副本
|
|
771
|
+
cleanupTempScript();
|
|
772
|
+
|
|
731
773
|
// 文件替换成功,在 restart 之前把 source 从 path 切换为 npm,
|
|
732
774
|
// 确保新进程启动时读到的是 npm source,不会被本地源码覆盖。
|
|
733
775
|
switchPluginSourceToNpm();
|
|
@@ -788,7 +830,11 @@ function fireHotUpgrade(targetVersion?: string): HotUpgradeStartResult {
|
|
|
788
830
|
/**
|
|
789
831
|
* /bot-upgrade — 统一升级入口
|
|
790
832
|
*
|
|
791
|
-
*
|
|
833
|
+
* upgradeMode 开关:
|
|
834
|
+
* - "doc"(默认):只展示升级指引文档,不执行热更新
|
|
835
|
+
* - "hot-reload":执行 npm 升级脚本进行热更新
|
|
836
|
+
*
|
|
837
|
+
* 热更新模式下的产品流程:
|
|
792
838
|
* /bot-upgrade — 展示版本信息+确认按钮(不直接升级)
|
|
793
839
|
* /bot-upgrade --latest — 确认升级到最新版本
|
|
794
840
|
* /bot-upgrade --version X — 升级到指定版本
|
|
@@ -798,21 +844,54 @@ let _upgrading = false; // 升级锁
|
|
|
798
844
|
|
|
799
845
|
registerCommand({
|
|
800
846
|
name: "bot-upgrade",
|
|
801
|
-
description: "
|
|
847
|
+
description: "检查更新并查看升级指引",
|
|
802
848
|
usage: [
|
|
803
|
-
`/bot-upgrade
|
|
804
|
-
`/bot-upgrade --latest
|
|
805
|
-
`/bot-upgrade --version X
|
|
806
|
-
`/bot-upgrade --force
|
|
807
|
-
``,
|
|
808
|
-
`⚠️ 仅在私聊中可用。升级过程约 30~60 秒,期间服务短暂不可用。`,
|
|
809
|
-
``,
|
|
810
|
-
`环境要求:`,
|
|
811
|
-
` - 操作系统:macOS / Linux / Windows`,
|
|
812
|
-
` - OpenClaw 框架版本 ≥ ${UPGRADE_REQUIREMENTS.minFrameworkVersion}`,
|
|
813
|
-
` - Node.js ≥ v${UPGRADE_REQUIREMENTS.minNodeVersion}`,
|
|
849
|
+
`/bot-upgrade 检查是否有新版本`,
|
|
850
|
+
`/bot-upgrade --latest 确认升级到最新版本(需 upgradeMode=hot-reload)`,
|
|
851
|
+
`/bot-upgrade --version X 升级到指定版本(需 upgradeMode=hot-reload)`,
|
|
852
|
+
`/bot-upgrade --force 强制重新安装当前版本(需 upgradeMode=hot-reload)`,
|
|
814
853
|
].join("\n"),
|
|
815
854
|
handler: async (ctx) => {
|
|
855
|
+
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
856
|
+
const upgradeMode = ctx.accountConfig?.upgradeMode || "doc";
|
|
857
|
+
const args = ctx.args.trim();
|
|
858
|
+
const info = await getUpdateInfo();
|
|
859
|
+
|
|
860
|
+
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
861
|
+
|
|
862
|
+
// ── doc 模式(默认):只展示升级指引,不执行热更新 ──
|
|
863
|
+
if (upgradeMode !== "hot-reload") {
|
|
864
|
+
if (info.checkedAt === 0) {
|
|
865
|
+
return `⏳ 版本检查中,请稍后再试`;
|
|
866
|
+
}
|
|
867
|
+
if (info.error) {
|
|
868
|
+
return [
|
|
869
|
+
`❌ 主机网络访问异常,无法检查更新`,
|
|
870
|
+
``,
|
|
871
|
+
`查看升级指引:[点击查看](${url})`,
|
|
872
|
+
].join("\n");
|
|
873
|
+
}
|
|
874
|
+
if (!info.hasUpdate) {
|
|
875
|
+
return [
|
|
876
|
+
`✅ 当前已是最新版本 v${PLUGIN_VERSION}`,
|
|
877
|
+
``,
|
|
878
|
+
`项目地址:[GitHub](${GITHUB_URL})`,
|
|
879
|
+
].join("\n");
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return [
|
|
883
|
+
`🆕 发现新版本`,
|
|
884
|
+
``,
|
|
885
|
+
`当前版本:**v${PLUGIN_VERSION}**`,
|
|
886
|
+
`最新版本:**v${info.latest}**`,
|
|
887
|
+
``,
|
|
888
|
+
`📖 升级指引:[点击查看](${url})`,
|
|
889
|
+
`🌟 官方 GitHub 仓库:[点击前往](${GITHUB_URL})`,
|
|
890
|
+
].join("\n");
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// ── hot-reload 模式:执行热更新 ──
|
|
894
|
+
|
|
816
895
|
// 升级相关指令仅在私聊中可用
|
|
817
896
|
if (ctx.type !== "c2c") {
|
|
818
897
|
return `💡 请在私聊中使用此指令`;
|
|
@@ -823,10 +902,6 @@ registerCommand({
|
|
|
823
902
|
return `⏳ 正在升级中,请稍候...`;
|
|
824
903
|
}
|
|
825
904
|
|
|
826
|
-
const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
|
|
827
|
-
const args = ctx.args.trim();
|
|
828
|
-
const info = await getUpdateInfo();
|
|
829
|
-
|
|
830
905
|
let isForce = false;
|
|
831
906
|
let isLatest = false;
|
|
832
907
|
let versionArg: string | undefined;
|
|
@@ -864,8 +939,6 @@ registerCommand({
|
|
|
864
939
|
}
|
|
865
940
|
}
|
|
866
941
|
|
|
867
|
-
const GITHUB_URL = "https://github.com/tencent-connect/openclaw-qqbot/";
|
|
868
|
-
|
|
869
942
|
// ── 无参数(也没有 --latest / --version / --force):只展示信息+确认按钮 ──
|
|
870
943
|
if (!versionArg && !isLatest && !isForce) {
|
|
871
944
|
if (info.checkedAt === 0) {
|
|
@@ -887,7 +960,7 @@ registerCommand({
|
|
|
887
960
|
return lines.join("\n");
|
|
888
961
|
}
|
|
889
962
|
|
|
890
|
-
// 有新版本:展示信息 +
|
|
963
|
+
// 有新版本:展示信息 + 确认按钮
|
|
891
964
|
return [
|
|
892
965
|
`🆕 发现新版本`,
|
|
893
966
|
``,
|