upgrade openclaw to 3.23 (#652)

Co-authored-by: Felix <24791380+vcfgv@users.noreply.github.com>
This commit is contained in:
paisley
2026-03-26 16:58:04 +08:00
committed by GitHub
Unverified
parent b786b773f1
commit ba5947e2cb
22 changed files with 2927 additions and 4739 deletions

View File

@@ -20,7 +20,7 @@
*/
const { cpSync, existsSync, readdirSync, rmSync, statSync, mkdirSync, realpathSync } = require('fs');
const { join, dirname, basename } = require('path');
const { join, dirname, basename, relative } = require('path');
// On Windows, paths in pnpm's virtual store can exceed the default MAX_PATH
// limit (260 chars). Node.js 18.17+ respects the system LongPathsEnabled
@@ -287,6 +287,84 @@ function patchBrokenModules(nodeModulesDir) {
}
}
// lru-cache CJS/ESM interop fix (recursive):
// Multiple versions of lru-cache may exist in the output tree — not just
// at node_modules/lru-cache/ but also nested inside other packages.
// Older CJS versions (v5, v6) export the class via `module.exports = LRUCache`
// without a named `LRUCache` property, so `import { LRUCache } from 'lru-cache'`
// fails in Node.js 22+ ESM interop (used by Electron 40+).
// We recursively scan the entire output for ALL lru-cache installations and
// patch each CJS entry to ensure `exports.LRUCache` always exists.
function patchAllLruCacheInstances(rootDir) {
let lruCount = 0;
const stack = [rootDir];
while (stack.length > 0) {
const dir = stack.pop();
let entries;
try { entries = readdirSync(normWin(dir), { withFileTypes: true }); } catch { continue; }
for (const entry of entries) {
const fullPath = join(dir, entry.name);
let isDirectory = entry.isDirectory();
if (!isDirectory) {
// pnpm layout may contain symlink/junction directories on Windows.
try { isDirectory = statSync(normWin(fullPath)).isDirectory(); } catch { isDirectory = false; }
}
if (!isDirectory) continue;
if (entry.name === 'lru-cache') {
const pkgPath = join(fullPath, 'package.json');
if (!existsSync(normWin(pkgPath))) { stack.push(fullPath); continue; }
try {
const pkg = JSON.parse(readFileSync(normWin(pkgPath), 'utf8'));
if (pkg.type === 'module') continue; // ESM version — already has named exports
const mainFile = pkg.main || 'index.js';
const entryFile = join(fullPath, mainFile);
if (!existsSync(normWin(entryFile))) continue;
const original = readFileSync(normWin(entryFile), 'utf8');
if (!original.includes('exports.LRUCache')) {
const patched = [
original,
'',
'// ClawX patch: add LRUCache named export for Node.js 22+ ESM interop',
'if (typeof module.exports === "function" && !module.exports.LRUCache) {',
' module.exports.LRUCache = module.exports;',
'}',
'',
].join('\n');
writeFileSync(normWin(entryFile), patched, 'utf8');
lruCount++;
console.log(`[after-pack] 🩹 Patched lru-cache CJS (v${pkg.version}) at ${relative(rootDir, fullPath)}`);
}
// lru-cache v7 ESM entry exports default only; add named export.
const moduleFile = typeof pkg.module === 'string' ? pkg.module : null;
if (moduleFile) {
const esmEntry = join(fullPath, moduleFile);
if (existsSync(normWin(esmEntry))) {
const esmOriginal = readFileSync(normWin(esmEntry), 'utf8');
if (
esmOriginal.includes('export default LRUCache') &&
!esmOriginal.includes('export { LRUCache')
) {
const esmPatched = [esmOriginal, '', 'export { LRUCache }', ''].join('\n');
writeFileSync(normWin(esmEntry), esmPatched, 'utf8');
lruCount++;
console.log(`[after-pack] 🩹 Patched lru-cache ESM (v${pkg.version}) at ${relative(rootDir, fullPath)}`);
}
}
}
} catch (err) {
console.warn(`[after-pack] ⚠️ Failed to patch lru-cache at ${fullPath}:`, err.message);
}
} else {
stack.push(fullPath);
}
}
}
return lruCount;
}
const lruPatched = patchAllLruCacheInstances(nodeModulesDir);
count += lruPatched;
if (count > 0) {
console.log(`[after-pack] 🩹 Patched ${count} broken module(s) in ${nodeModulesDir}`);
}
@@ -497,7 +575,7 @@ exports.default = async function afterPack(context) {
const BUNDLED_PLUGINS = [
{ npmName: '@soimy/dingtalk', pluginId: 'dingtalk' },
{ npmName: '@wecom/wecom-openclaw-plugin', pluginId: 'wecom' },
{ npmName: '@sliverp/qqbot', pluginId: 'qqbot' },
{ npmName: '@tencent-connect/openclaw-qqbot', pluginId: 'qqbot' },
{ npmName: '@larksuite/openclaw-lark', pluginId: 'feishu-openclaw-plugin' },
{ npmName: '@tencent-weixin/openclaw-weixin', pluginId: 'openclaw-weixin' },
];
@@ -535,4 +613,86 @@ exports.default = async function afterPack(context) {
if (nativeRemoved > 0) {
console.log(`[after-pack] ✅ Removed ${nativeRemoved} non-target native platform packages.`);
}
// 5. Patch lru-cache in app.asar.unpacked
//
// Production dependencies (electron-updater → semver → lru-cache@6,
// posthog-node → proxy agents → lru-cache@7, etc.) end up inside app.asar.
// Older CJS versions lack the `LRUCache` named export, breaking
// `import { LRUCache }` in Electron 40+ (Node.js 22+ ESM interop).
//
// electron-builder.yml lists `**/node_modules/lru-cache/**` in asarUnpack,
// which extracts those files to app.asar.unpacked/. We patch them here so
// Electron's transparent asar fs layer serves the fixed version at runtime.
const asarUnpackedDir = join(resourcesDir, 'app.asar.unpacked');
if (existsSync(asarUnpackedDir)) {
const { readFileSync: readFS, writeFileSync: writeFS } = require('fs');
let asarLruCount = 0;
const lruStack = [asarUnpackedDir];
while (lruStack.length > 0) {
const dir = lruStack.pop();
let entries;
try { entries = readdirSync(normWin(dir), { withFileTypes: true }); } catch { continue; }
for (const entry of entries) {
const fullPath = join(dir, entry.name);
let isDirectory = entry.isDirectory();
if (!isDirectory) {
// pnpm layout may contain symlink/junction directories on Windows.
try { isDirectory = statSync(normWin(fullPath)).isDirectory(); } catch { isDirectory = false; }
}
if (!isDirectory) continue;
if (entry.name === 'lru-cache') {
const pkgPath = join(fullPath, 'package.json');
if (!existsSync(normWin(pkgPath))) { lruStack.push(fullPath); continue; }
try {
const pkg = JSON.parse(readFS(normWin(pkgPath), 'utf8'));
if (pkg.type === 'module') continue; // ESM — already exports LRUCache
const mainFile = pkg.main || 'index.js';
const entryFile = join(fullPath, mainFile);
if (!existsSync(normWin(entryFile))) continue;
const original = readFS(normWin(entryFile), 'utf8');
if (!original.includes('exports.LRUCache')) {
const patched = [
original,
'',
'// ClawX patch: add LRUCache named export for Node.js 22+ ESM interop',
'if (typeof module.exports === "function" && !module.exports.LRUCache) {',
' module.exports.LRUCache = module.exports;',
'}',
'',
].join('\n');
writeFS(normWin(entryFile), patched, 'utf8');
asarLruCount++;
console.log(`[after-pack] 🩹 Patched lru-cache CJS (v${pkg.version}) in app.asar.unpacked at ${relative(asarUnpackedDir, fullPath)}`);
}
// lru-cache v7 ESM entry exports default only; add named export.
const moduleFile = typeof pkg.module === 'string' ? pkg.module : null;
if (moduleFile) {
const esmEntry = join(fullPath, moduleFile);
if (existsSync(normWin(esmEntry))) {
const esmOriginal = readFS(normWin(esmEntry), 'utf8');
if (
esmOriginal.includes('export default LRUCache') &&
!esmOriginal.includes('export { LRUCache')
) {
const esmPatched = [esmOriginal, '', 'export { LRUCache }', ''].join('\n');
writeFS(normWin(esmEntry), esmPatched, 'utf8');
asarLruCount++;
console.log(`[after-pack] 🩹 Patched lru-cache ESM (v${pkg.version}) in app.asar.unpacked at ${relative(asarUnpackedDir, fullPath)}`);
}
}
}
} catch (err) {
console.warn(`[after-pack] ⚠️ Failed to patch lru-cache in asar.unpacked at ${fullPath}:`, err.message);
}
} else {
lruStack.push(fullPath);
}
}
}
if (asarLruCount > 0) {
console.log(`[after-pack] 🩹 Patched ${asarLruCount} lru-cache instance(s) in app.asar.unpacked`);
}
}
};