203 lines
7.1 KiB
NSIS
203 lines
7.1 KiB
NSIS
; ClawX Custom NSIS Installer/Uninstaller Script
|
|
;
|
|
; Install: enables long paths, adds resources\cli to user PATH for openclaw CLI.
|
|
; Uninstall: removes the PATH entry and optionally deletes user data.
|
|
|
|
!ifndef nsProcess::FindProcess
|
|
!include "nsProcess.nsh"
|
|
!endif
|
|
|
|
!macro customCheckAppRunning
|
|
|
|
${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
|
|
${if} $R0 == 0
|
|
${if} ${isUpdated}
|
|
# allow app to exit without explicit kill
|
|
Sleep 1000
|
|
Goto doStopProcess
|
|
${endIf}
|
|
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "$(appRunning)" /SD IDOK IDOK doStopProcess
|
|
Quit
|
|
|
|
doStopProcess:
|
|
DetailPrint `Closing running "${PRODUCT_NAME}"...`
|
|
|
|
# Silently kill the process using nsProcess instead of taskkill / cmd.exe
|
|
${nsProcess::KillProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
|
|
# to ensure that files are not "in-use"
|
|
Sleep 300
|
|
|
|
# Retry counter
|
|
StrCpy $R1 0
|
|
|
|
loop:
|
|
IntOp $R1 $R1 + 1
|
|
|
|
${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
${if} $R0 == 0
|
|
# wait to give a chance to exit gracefully
|
|
Sleep 1000
|
|
${nsProcess::KillProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
|
|
${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
${If} $R0 == 0
|
|
DetailPrint `Waiting for "${PRODUCT_NAME}" to close.`
|
|
Sleep 2000
|
|
${else}
|
|
Goto not_running
|
|
${endIf}
|
|
${else}
|
|
Goto not_running
|
|
${endIf}
|
|
|
|
# App likely running with elevated permissions.
|
|
# Ask user to close it manually
|
|
${if} $R1 > 1
|
|
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDCANCEL IDRETRY loop
|
|
Quit
|
|
${else}
|
|
Goto loop
|
|
${endIf}
|
|
not_running:
|
|
${nsProcess::Unload}
|
|
${endIf}
|
|
!macroend
|
|
|
|
!macro customInstall
|
|
; Enable Windows long path support (Windows 10 1607+ / Windows 11).
|
|
; pnpm virtual store paths can exceed the default MAX_PATH limit of 260 chars.
|
|
; Writing to HKLM requires admin privileges; on per-user installs without
|
|
; elevation this call silently fails — no crash, just no key written.
|
|
WriteRegDWORD HKLM "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
|
|
|
|
; Use PowerShell to update the current user's PATH.
|
|
; This avoids NSIS string-buffer limits and preserves long PATH values.
|
|
InitPluginsDir
|
|
ClearErrors
|
|
File "/oname=$PLUGINSDIR\update-user-path.ps1" "${PROJECT_DIR}\resources\cli\win32\update-user-path.ps1"
|
|
nsExec::ExecToStack '"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$PLUGINSDIR\update-user-path.ps1" -Action add -CliDir "$INSTDIR\resources\cli"'
|
|
Pop $0
|
|
Pop $1
|
|
StrCmp $0 "error" 0 +2
|
|
DetailPrint "Warning: Failed to launch PowerShell while updating PATH."
|
|
StrCmp $0 "timeout" 0 +2
|
|
DetailPrint "Warning: PowerShell PATH update timed out."
|
|
StrCmp $0 "0" 0 +2
|
|
Goto _ci_done
|
|
DetailPrint "Warning: PowerShell PATH update exited with code $0."
|
|
|
|
_ci_done:
|
|
!macroend
|
|
|
|
!macro customUnInstall
|
|
; Remove resources\cli from user PATH via PowerShell so long PATH values are handled safely
|
|
InitPluginsDir
|
|
ClearErrors
|
|
File "/oname=$PLUGINSDIR\update-user-path.ps1" "${PROJECT_DIR}\resources\cli\win32\update-user-path.ps1"
|
|
nsExec::ExecToStack '"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$PLUGINSDIR\update-user-path.ps1" -Action remove -CliDir "$INSTDIR\resources\cli"'
|
|
Pop $0
|
|
Pop $1
|
|
StrCmp $0 "error" 0 +2
|
|
DetailPrint "Warning: Failed to launch PowerShell while removing PATH entry."
|
|
StrCmp $0 "timeout" 0 +2
|
|
DetailPrint "Warning: PowerShell PATH removal timed out."
|
|
StrCmp $0 "0" 0 +2
|
|
Goto _cu_pathDone
|
|
DetailPrint "Warning: PowerShell PATH removal exited with code $0."
|
|
|
|
_cu_pathDone:
|
|
|
|
; Ask user if they want to completely remove all user data
|
|
MessageBox MB_YESNO|MB_ICONQUESTION \
|
|
"Do you want to completely remove all ClawX user data?$\r$\n$\r$\nThis will delete:$\r$\n • .openclaw folder (configuration & skills)$\r$\n • AppData\Local\clawx (local app data)$\r$\n • AppData\Roaming\clawx (roaming app data)$\r$\n$\r$\nSelect 'No' to keep your data for future reinstallation." \
|
|
/SD IDNO IDYES _cu_removeData IDNO _cu_skipRemove
|
|
|
|
_cu_removeData:
|
|
; Kill any lingering ClawX processes to release file locks on electron-store
|
|
; JSON files (settings.json, clawx-providers.json, window-state.json, etc.)
|
|
${nsProcess::FindProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
${if} $R0 == 0
|
|
${nsProcess::KillProcess} "${APP_EXECUTABLE_FILENAME}" $R0
|
|
${endIf}
|
|
${nsProcess::Unload}
|
|
|
|
; Wait for processes to fully exit and release file handles
|
|
Sleep 2000
|
|
|
|
; --- Always remove current user's data first ---
|
|
RMDir /r "$PROFILE\.openclaw"
|
|
RMDir /r "$LOCALAPPDATA\clawx"
|
|
RMDir /r "$APPDATA\clawx"
|
|
|
|
; --- Retry: if directories still exist (locked files), wait and try again ---
|
|
; Check .openclaw
|
|
IfFileExists "$PROFILE\.openclaw\*.*" 0 _cu_openclawDone
|
|
Sleep 3000
|
|
RMDir /r "$PROFILE\.openclaw"
|
|
IfFileExists "$PROFILE\.openclaw\*.*" 0 _cu_openclawDone
|
|
nsExec::ExecToStack 'cmd.exe /c rd /s /q "$PROFILE\.openclaw"'
|
|
Pop $0
|
|
Pop $1
|
|
_cu_openclawDone:
|
|
|
|
; Check AppData\Local\clawx
|
|
IfFileExists "$LOCALAPPDATA\clawx\*.*" 0 _cu_localDone
|
|
Sleep 3000
|
|
RMDir /r "$LOCALAPPDATA\clawx"
|
|
IfFileExists "$LOCALAPPDATA\clawx\*.*" 0 _cu_localDone
|
|
nsExec::ExecToStack 'cmd.exe /c rd /s /q "$LOCALAPPDATA\clawx"'
|
|
Pop $0
|
|
Pop $1
|
|
_cu_localDone:
|
|
|
|
; Check AppData\Roaming\clawx
|
|
IfFileExists "$APPDATA\clawx\*.*" 0 _cu_roamingDone
|
|
Sleep 3000
|
|
RMDir /r "$APPDATA\clawx"
|
|
IfFileExists "$APPDATA\clawx\*.*" 0 _cu_roamingDone
|
|
nsExec::ExecToStack 'cmd.exe /c rd /s /q "$APPDATA\clawx"'
|
|
Pop $0
|
|
Pop $1
|
|
_cu_roamingDone:
|
|
|
|
; --- Final check: warn user if any directories could not be removed ---
|
|
StrCpy $R3 ""
|
|
IfFileExists "$PROFILE\.openclaw\*.*" 0 +2
|
|
StrCpy $R3 "$R3$\r$\n • $PROFILE\.openclaw"
|
|
IfFileExists "$LOCALAPPDATA\clawx\*.*" 0 +2
|
|
StrCpy $R3 "$R3$\r$\n • $LOCALAPPDATA\clawx"
|
|
IfFileExists "$APPDATA\clawx\*.*" 0 +2
|
|
StrCpy $R3 "$R3$\r$\n • $APPDATA\clawx"
|
|
StrCmp $R3 "" _cu_cleanupOk
|
|
MessageBox MB_OK|MB_ICONEXCLAMATION \
|
|
"Some data directories could not be removed (files may be in use):$\r$\n$R3$\r$\n$\r$\nPlease delete them manually after restarting your computer."
|
|
_cu_cleanupOk:
|
|
|
|
; --- For per-machine (all users) installs, enumerate all user profiles ---
|
|
StrCpy $R0 0
|
|
|
|
_cu_enumLoop:
|
|
EnumRegKey $R1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $R0
|
|
StrCmp $R1 "" _cu_enumDone
|
|
|
|
ReadRegStr $R2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$R1" "ProfileImagePath"
|
|
StrCmp $R2 "" _cu_enumNext
|
|
|
|
ExpandEnvStrings $R2 $R2
|
|
StrCmp $R2 $PROFILE _cu_enumNext
|
|
|
|
RMDir /r "$R2\.openclaw"
|
|
RMDir /r "$R2\AppData\Local\clawx"
|
|
RMDir /r "$R2\AppData\Roaming\clawx"
|
|
|
|
_cu_enumNext:
|
|
IntOp $R0 $R0 + 1
|
|
Goto _cu_enumLoop
|
|
|
|
_cu_enumDone:
|
|
_cu_skipRemove:
|
|
!macroend
|
|
|