From 7ca5c8ee5c6db82880b5d52a6eeddcbbb7c026fe Mon Sep 17 00:00:00 2001 From: Frank Yang Date: Tue, 14 Apr 2026 15:04:42 +0800 Subject: [PATCH] fix: accept commented Baileys hotfix variant --- scripts/postinstall-bundled-plugins.mjs | 11 ++- .../stage-bundled-plugin-runtime-deps.test.ts | 70 ++++++++++++++++--- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/scripts/postinstall-bundled-plugins.mjs b/scripts/postinstall-bundled-plugins.mjs index 56b8fc4df89..2cf3b9acfa5 100644 --- a/scripts/postinstall-bundled-plugins.mjs +++ b/scripts/postinstall-bundled-plugins.mjs @@ -66,6 +66,12 @@ const BAILEYS_MEDIA_HOTFIX_SEQUENTIAL_REPLACEMENT = [ " await originalFinishPromise;", " logger?.debug('encrypted data successfully');", ].join("\n"); +const BAILEYS_MEDIA_HOTFIX_FINISH_PROMISES_RE = + /const\s+encFinishPromise\s*=\s*once\(encFileWriteStream,\s*'finish'\);\s*\n[\s\S]*const\s+originalFinishPromise\s*=\s*originalFileStream\s*\?\s*once\(originalFileStream,\s*'finish'\)\s*:\s*Promise\.resolve\(\);/u; +const BAILEYS_MEDIA_HOTFIX_PROMISE_ALL_RE = + /await\s+Promise\.all\(\[\s*encFinishPromise\s*,\s*originalFinishPromise\s*\]\);/u; +const BAILEYS_MEDIA_HOTFIX_SEQUENTIAL_AWAITS_RE = + /await\s+encFinishPromise;\s*(?:\/\/[^\n]*\n|\s)*await\s+originalFinishPromise;/u; const BAILEYS_MEDIA_DISPATCHER_NEEDLE = [ " const response = await fetch(url, {", " dispatcher: fetchAgent,", @@ -283,7 +289,10 @@ export function applyBaileysEncryptedStreamFinishHotfix(params = {}) { const encryptedStreamAlreadyPatched = patchedText.includes(BAILEYS_MEDIA_HOTFIX_REPLACEMENT) || - patchedText.includes(BAILEYS_MEDIA_HOTFIX_SEQUENTIAL_REPLACEMENT); + patchedText.includes(BAILEYS_MEDIA_HOTFIX_SEQUENTIAL_REPLACEMENT) || + (BAILEYS_MEDIA_HOTFIX_FINISH_PROMISES_RE.test(patchedText) && + (BAILEYS_MEDIA_HOTFIX_PROMISE_ALL_RE.test(patchedText) || + BAILEYS_MEDIA_HOTFIX_SEQUENTIAL_AWAITS_RE.test(patchedText))); if (!encryptedStreamAlreadyPatched) { if (!patchedText.includes(BAILEYS_MEDIA_HOTFIX_NEEDLE)) { diff --git a/src/plugins/stage-bundled-plugin-runtime-deps.test.ts b/src/plugins/stage-bundled-plugin-runtime-deps.test.ts index cd65a87cb03..fe570a03e7d 100644 --- a/src/plugins/stage-bundled-plugin-runtime-deps.test.ts +++ b/src/plugins/stage-bundled-plugin-runtime-deps.test.ts @@ -67,8 +67,9 @@ function createBaileysMessagesMediaSource(params?: { dispatcherPatched?: boolean; encryptedStreamPatched?: boolean; encryptedStreamPatchedSequentially?: boolean; + encryptedStreamPatchedSequentiallyWithComments?: boolean; }) { - const encryptedLines = params?.encryptedStreamPatchedSequentially + const encryptedLines = params?.encryptedStreamPatchedSequentiallyWithComments ? [ " encFileWriteStream.write(mac);", " const encFinishPromise = once(encFileWriteStream, 'finish');", @@ -76,11 +77,14 @@ function createBaileysMessagesMediaSource(params?: { " encFileWriteStream.end();", " originalFileStream?.end?.();", " stream.destroy();", + " // Wait for write streams to fully flush to disk before returning encFilePath.", + " // Without this await, the caller may open a read stream on the file before", + " // the OS has created it, causing a race-condition ENOENT crash.", " await encFinishPromise;", " await originalFinishPromise;", " logger?.debug('encrypted data successfully');", ] - : params?.encryptedStreamPatched + : params?.encryptedStreamPatchedSequentially ? [ " encFileWriteStream.write(mac);", " const encFinishPromise = once(encFileWriteStream, 'finish');", @@ -88,16 +92,28 @@ function createBaileysMessagesMediaSource(params?: { " encFileWriteStream.end();", " originalFileStream?.end?.();", " stream.destroy();", - " await Promise.all([encFinishPromise, originalFinishPromise]);", + " await encFinishPromise;", + " await originalFinishPromise;", " logger?.debug('encrypted data successfully');", ] - : [ - " encFileWriteStream.write(mac);", - " encFileWriteStream.end();", - " originalFileStream?.end?.();", - " stream.destroy();", - " logger?.debug('encrypted data successfully');", - ]; + : params?.encryptedStreamPatched + ? [ + " encFileWriteStream.write(mac);", + " const encFinishPromise = once(encFileWriteStream, 'finish');", + " const originalFinishPromise = originalFileStream ? once(originalFileStream, 'finish') : Promise.resolve();", + " encFileWriteStream.end();", + " originalFileStream?.end?.();", + " stream.destroy();", + " await Promise.all([encFinishPromise, originalFinishPromise]);", + " logger?.debug('encrypted data successfully');", + ] + : [ + " encFileWriteStream.write(mac);", + " encFileWriteStream.end();", + " originalFileStream?.end?.();", + " stream.destroy();", + " logger?.debug('encrypted data successfully');", + ]; const dispatcherLines = params?.dispatcherPatched ? [ " const response = await fetch(url, {", @@ -339,6 +355,40 @@ describe("stageBundledPluginRuntimeDeps", () => { ); }); + it("patches the Baileys dispatcher guard when sequential awaits include comments", async () => { + const repoRoot = makeRepoRoot( + "openclaw-stage-bundled-runtime-hotfix-dispatcher-sequential-comments-", + ); + const targetPath = path.join( + repoRoot, + "node_modules", + "@whiskeysockets", + "baileys", + "lib", + "Utils", + "messages-media.js", + ); + writeRepoFile( + repoRoot, + "node_modules/@whiskeysockets/baileys/lib/Utils/messages-media.js", + createBaileysMessagesMediaSource({ encryptedStreamPatchedSequentiallyWithComments: true }), + ); + + const { applyBaileysEncryptedStreamFinishHotfix } = await loadPostinstallBundledPluginsModule(); + const result = applyBaileysEncryptedStreamFinishHotfix({ packageRoot: repoRoot }); + + expect(result).toEqual({ + applied: true, + reason: "patched", + targetPath, + }); + expect(fs.readFileSync(targetPath, "utf8")).toContain( + "...(fetchAgent?.dispatch ? { dispatcher: fetchAgent } : {}),", + ); + expect(fs.readFileSync(targetPath, "utf8")).toContain("await encFinishPromise;"); + expect(fs.readFileSync(targetPath, "utf8")).toContain("await originalFinishPromise;"); + }); + it("patches the Baileys dispatcher guard when the flush hotfix uses sequential awaits", async () => { const repoRoot = makeRepoRoot("openclaw-stage-bundled-runtime-hotfix-dispatcher-sequential-"); const targetPath = path.join(