#!/usr/bin/env node
/**
* Patch electron-builder's NSIS extractUsing7za macro to extract directly into
* $INSTDIR instead of temp + CopyFiles.
*
* #1026 enlarged the packaged openclaw runtime; CopyFiles over thousands of
* files makes assisted installers look frozen (~50%) and often fails with the
* "app cannot be closed" retry dialog when AV or file locks are involved.
*
* Must run before makensis (package:win), not only in afterPack.
*/
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
export const EXTRACT_APP_PACKAGE_NSH = join(
ROOT,
'node_modules',
'app-builder-lib',
'templates',
'nsis',
'include',
'extractAppPackage.nsh',
);
const PATCHED_MACRO = [
'!macro extractUsing7za FILE',
' ; ClawX-patched: extract directly to $INSTDIR (skip temp + CopyFiles).',
' StrCpy $R9 0',
' clawx_extract_attempt:',
' IntOp $R9 $R9 + 1',
' DetailPrint "Extracting ClawX application files (attempt $R9, please wait)..."',
' SetOutPath $INSTDIR',
' ClearErrors',
' Nsis7z::Extract "${FILE}"',
' IfErrors 0 clawx_extract_done',
' ${if} $R9 < 3',
' DetailPrint "Releasing file locks before retry..."',
' nsExec::ExecToStack \'taskkill /F /T /IM "${APP_EXECUTABLE_FILENAME}"\'',
' Pop $0',
' Pop $1',
' nsExec::ExecToStack \'taskkill /F /IM openclaw-gateway.exe\'',
' Pop $0',
' Pop $1',
' Sleep 3000',
' Goto clawx_extract_attempt',
' ${endIf}',
' MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDRETRY IDRETRY clawx_extract_attempt IDCANCEL clawx_extract_abort',
' clawx_extract_abort:',
' Quit',
' clawx_extract_done:',
'!macroend',
].join('\n');
/**
* @param {string} [targetPath]
* @returns {boolean} true when template is patched (or already patched)
*/
export function patchNsisExtractTemplate(targetPath = EXTRACT_APP_PACKAGE_NSH) {
if (!existsSync(targetPath)) {
console.warn('[patch-nsis-extract] extractAppPackage.nsh not found, skipping.');
return false;
}
const original = readFileSync(targetPath, 'utf8');
if (original.includes('ClawX-patched')) {
return true;
}
if (!original.includes('CopyFiles')) {
console.warn('[patch-nsis-extract] CopyFiles not found — NSIS template may have changed.');
return false;
}
// Use a replacer function so NSIS `${if}` tokens are not treated as replace groups.
const patched = original.replace(
/(!macro extractUsing7za FILE[\s\S]*?!macroend)/,
() => PATCHED_MACRO,
);
if (patched === original) {
console.warn('[patch-nsis-extract] extractUsing7za macro regex did not match.');
return false;
}
writeFileSync(targetPath, patched, 'utf8');
console.log('[patch-nsis-extract] Patched extractAppPackage.nsh (direct Nsis7z::Extract to $INSTDIR).');
return true;
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const ok = patchNsisExtractTemplate();
process.exit(ok ? 0 : 1);
}