From 33c64d50c9e09a07503a9af0f507006f915ce473 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Thu, 20 Nov 2025 16:44:20 +0900 Subject: [PATCH] Add afterInstall.ps1 and update tool paths to APPDATA Introduces afterInstall.ps1 for post-install setup, downloading and extracting required tools to the APPDATA\WelsonJS directory. Updates code in http.js, python3.js, ovftool.js, wamr.js, and system.js to reference binaries from the new APPDATA location. Modifies setup.iss to run the PowerShell script after installation. --- afterInstall.ps1 | 297 +++++++++++++++++++++++++++++++++++++++++++++++ lib/http.js | 21 ++-- lib/ovftool.js | 7 +- lib/python3.js | 19 +-- lib/system.js | 7 +- lib/wamr.js | 11 +- setup.iss | 1 + 7 files changed, 341 insertions(+), 22 deletions(-) create mode 100644 afterInstall.ps1 diff --git a/afterInstall.ps1 b/afterInstall.ps1 new file mode 100644 index 0000000..fcaf232 --- /dev/null +++ b/afterInstall.ps1 @@ -0,0 +1,297 @@ +# ================================ +# CONFIGURATION +# ================================ +$AppName = "welsonjs" +$TargetDir = Join-Path $env:APPDATA $AppName +$TmpDir = Join-Path $env:TEMP "$AppName-downloads" + +Write-Host "" +Write-Host "[*] Target directory : $TargetDir" +Write-Host "[*] Temporary directory: $TmpDir" +Write-Host "" + +# Ensure base directories exist +New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null +New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null + + +# ================================ +# ARCHITECTURE DETECTION +# ================================ +$arch = "x86" +if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { $arch = "x64" } +elseif ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { $arch = "arm64" } +if ($env:PROCESSOR_ARCHITEW6432) { $arch = "x64" } + +Write-Host "[*] Detected architecture: $arch" +Write-Host "" + + +# ================================ +# HELPER FUNCTIONS +# ================================ + +function Ensure-EmptyDirectory { + param( + [Parameter(Mandatory=$true)] + [string]$Path + ) + + # If a file exists at the path, remove it + if (Test-Path $Path -PathType Leaf) { + Write-Host "[WARN] A file exists at path '$Path'. Deleting it..." + Remove-Item -Path $Path -Force + } + + # Ensure directory exists + if (-not (Test-Path $Path -PathType Container)) { + Write-Host "[*] Creating directory: $Path" + New-Item -ItemType Directory -Path $Path -Force | Out-Null + } +} + +function Download-File { + param( + [Parameter(Mandatory=$true)] + [string]$Url, + [Parameter(Mandatory=$true)] + [string]$Destination + ) + + Write-Host "[*] Downloading file..." + Write-Host " URL : $Url" + Write-Host " OUT : $Destination" + + try { + Invoke-WebRequest -Uri $Url -OutFile $Destination -UseBasicParsing -ErrorAction Stop + Write-Host "[OK] Download completed." + Write-Host "" + } + catch { + Write-Host "[ERROR] Failed to download: $Url" + Write-Host $_.Exception.Message + throw + } +} + +function Extract-Zip { + param( + [Parameter(Mandatory=$true)] + [string]$ZipPath, + [Parameter(Mandatory=$true)] + [string]$DestDir + ) + + Write-Host "[*] Extracting ZIP:" + Write-Host " $ZipPath" + Write-Host " -> $DestDir" + + # Make sure destination directory exists and is a directory + Ensure-EmptyDirectory -Path $DestDir + + # Temporary extraction folder inside destination + $TmpExtract = Join-Path $DestDir "__tmp_extract__" + Ensure-EmptyDirectory -Path $TmpExtract + + Write-Host "[DEBUG] PowerShell command:" + Write-Host " Expand-Archive -LiteralPath `"$ZipPath`" -DestinationPath `"$TmpExtract`" -Force" + + try { + Expand-Archive -LiteralPath $ZipPath -DestinationPath $TmpExtract -Force -ErrorAction Stop + } + catch { + Write-Host "[ERROR] Failed to extract ZIP with Expand-Archive: $ZipPath" + Write-Host $_.Exception.Message + throw + } + + # Check extracted entries + $entries = Get-ChildItem -LiteralPath $TmpExtract + + if (-not $entries -or $entries.Count -eq 0) { + Write-Host "[ERROR] No entries were extracted from ZIP: $ZipPath" + throw "ZIP appears to be empty or extraction failed." + } + + if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) { + Write-Host "[*] Detected single root directory inside ZIP. Flattening..." + + # Move children of the single root directory into destination + $innerItems = Get-ChildItem -LiteralPath $entries[0].FullName + foreach ($item in $innerItems) { + Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force + } + } + else { + Write-Host "[*] ZIP has multiple top-level entries. Copying all..." + + foreach ($item in $entries) { + Move-Item -LiteralPath $item.FullName -Destination $DestDir -Force + } + } + + # Clean up temp extraction folder + Remove-Item -LiteralPath $TmpExtract -Recurse -Force + + Write-Host "[OK] ZIP extraction completed." + Write-Host "" +} + +function Extract-TarGz { + param( + [Parameter(Mandatory=$true)] + [string]$TarGzPath, + [Parameter(Mandatory=$true)] + [string]$DestDir + ) + + Write-Host "[*] Extracting TAR.GZ:" + Write-Host " $TarGzPath" + Write-Host " -> $DestDir" + + Ensure-EmptyDirectory -Path $DestDir + + # Modern Windows ships with tar, but we validate its presence + $tarCmd = Get-Command tar -ErrorAction SilentlyContinue + if (-not $tarCmd) { + Write-Host "[ERROR] 'tar' command not found. Cannot extract TAR.GZ." + throw "tar command not found." + } + + Write-Host "[DEBUG] tar command:" + Write-Host " tar -xzf `"$TarGzPath`" -C `"$DestDir`"" + + try { + & tar -xzf "$TarGzPath" -C "$DestDir" + if ($LASTEXITCODE -ne 0) { + throw "tar exit code $LASTEXITCODE" + } + Write-Host "[OK] TAR.GZ extraction completed." + Write-Host "" + } + catch { + Write-Host "[ERROR] Failed to extract TAR.GZ: $TarGzPath" + Write-Host $_.Exception.Message + throw + } +} + + +# ================================ +# SET DOWNLOAD URLS BASED ON ARCH +# ================================ +$PythonUrl = $null +$CurlUrl = $null +$YaraUrl = $null +$WamrUrl = $null + +switch ($arch) { + "x64" { + # Python embeddable (x64) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-amd64.zip" + + # curl (x64, mingw) + $CurlUrl = "https://curl.se/windows/latest.cgi?p=win64-mingw.zip" + + # YARA (x64, GitHub — as you specified) + $YaraUrl = "https://github.com/VirusTotal/yara/releases/download/v4.5.5/yara-4.5.5-2368-win64.zip" + + # WAMR (x64 only) + $WamrUrl = "https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/WAMR-2.4.3/iwasm-2.4.3-x86_64-windows-2022.tar.gz" + } + + "arm64" { + # Python embeddable (ARM64) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-arm64.zip" + + # curl (ARM64) + $CurlUrl = "https://curl.se/windows/latest.cgi?p=win64a-mingw.zip" + + # DO NOT install YARA/WAMR on ARM64 + $YaraUrl = $null + $WamrUrl = $null + } + + default { + # Treat anything else as x86 + # Python embeddable (x86) + $PythonUrl = "https://www.python.org/ftp/python/3.13.9/python-3.13.9-embeddable-win32.zip" + + # curl (x86) + $CurlUrl = "https://downloads.sourceforge.net/project/muldersoft/cURL/curl-8.17.0-win-x86-full.2025-11-09.zip"; + + # Do NOT install YARA/WAMR on x86 (same policy as before) + $YaraUrl = $null + $WamrUrl = $null + } +} + +Write-Host "[*] Python URL: $PythonUrl" +Write-Host "[*] curl URL : $CurlUrl" +if ($YaraUrl) { + Write-Host "[*] YARA URL : $YaraUrl" +} else { + Write-Host "[*] YARA : skipped on this architecture" +} +if ($WamrUrl) { + Write-Host "[*] WAMR URL : $WamrUrl" +} else { + Write-Host "[*] WAMR : skipped on this architecture" +} +Write-Host "" + + +# ================================ +# DOWNLOAD FILES +# ================================ +$PythonZip = Join-Path $TmpDir "python.zip" +$CurlZip = Join-Path $TmpDir "curl.zip" +$YaraZip = Join-Path $TmpDir "yara.zip" +$WamrTgz = Join-Path $TmpDir "wamr.tar.gz" + +try { + Download-File -Url $PythonUrl -Destination $PythonZip + Download-File -Url $CurlUrl -Destination $CurlZip + + if ($YaraUrl) { + Download-File -Url $YaraUrl -Destination $YaraZip + } + + if ($WamrUrl) { + Download-File -Url $WamrUrl -Destination $WamrTgz + } +} +catch { + Write-Host "[FATAL] Download phase failed." + exit 1 +} + + +# ================================ +# EXTRACT FILES +# ================================ +try { + Extract-Zip -ZipPath $PythonZip -DestDir (Join-Path $TargetDir "python") + Extract-Zip -ZipPath $CurlZip -DestDir (Join-Path $TargetDir "curl") + + if ($YaraUrl) { + Extract-Zip -ZipPath $YaraZip -DestDir (Join-Path $TargetDir "yara") + } + + if ($WamrUrl) { + Extract-TarGz -TarGzPath $WamrTgz -DestDir (Join-Path $TargetDir "wamr") + } +} +catch { + Write-Host "[FATAL] Extraction phase failed." + exit 1 +} + + +# ================================ +# FINISH +# ================================ +Write-Host "[*] All tools installed successfully." +Write-Host "[*] Installed into: $TargetDir" +Write-Host "" +exit 0 diff --git a/lib/http.js b/lib/http.js index 47605e9..cdebb6b 100644 --- a/lib/http.js +++ b/lib/http.js @@ -119,16 +119,21 @@ var HTTPObject = function(engine) { } else if (this.engine == "CURL") { this._interface = SHELL.create(); - // the location of cURL binary - var arch = SYS.getArch(); - if (arch.toLowerCase().indexOf("arm") > -1) { - this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe"); - } else if (arch.indexOf("64") > -1) { - this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe"); + // set cURL executable file location + var default_curl_path = SYS.getAppDataDir() + "\\curl\\curl.exe"; + if (FILE.fileExists(default_curl_path)) { + this.setBinPath(default_curl_path); } else { - this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe"); + var arch = SYS.getArch(); + if (arch.toLowerCase().indexOf("arm") > -1) { + this.setBinPath("bin\\arm64\\curl-" + this.curlVersion + "-win64a-mingw\\bin\\curl.exe"); + } else if (arch.indexOf("64") > -1) { + this.setBinPath("bin\\x64\\curl-" + this.curlVersion + "-win64-mingw\\bin\\curl.exe"); + } else { + this.setBinPath("bin\\x86\\curl-" + this.curlVersion + "-win32-mingw\\bin\\curl.exe"); + } } - + // do not clear after calling the `exec` this._interface.setIsPreventClear(true); } else if (this.engine == "BITS") { diff --git a/lib/ovftool.js b/lib/ovftool.js index 311b6f0..679d542 100644 --- a/lib/ovftool.js +++ b/lib/ovftool.js @@ -1,16 +1,17 @@ // ovftool.js -// Copyright 2019-2025, Namhyeon Go and the WelsonJS contributors. +// Namhyeon Go and the WelsonJS contributors. // SPDX-License-Identifier: GPL-3.0-or-later // https://github.com/gnh1201/welsonjs // // Download OVFTool (Open Virtualization Format (OVF) Tool): // https://developer.broadcom.com/tools/open-virtualization-format-ovf-tool/latest // +var SYS = require("lib/system"); var SHELL = require("lib/shell"); var CRED = require("lib/credentials"); function OVFObject() { - this.binPath = "bin\\x64\\VMware-ovftool-4.6.3-24031167-win.x86_64\\ovftool\\ovftool.exe"; + this.binPath = SYS.getAppDataDir() + "\\ovftool\\ovftool.exe"; this.hostname = ""; this.port = 443; this.resourceName = ""; @@ -66,7 +67,7 @@ function create() { exports.setCredential = setCredential; exports.create = create; -exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.2"; +exports.VERSIONINFO = "Broadcom/VMware OVF Tool interface (ovftool.js) version 0.1.3"; exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.global = global; exports.require = global.require; diff --git a/lib/python3.js b/lib/python3.js index 37635c0..5231bfe 100644 --- a/lib/python3.js +++ b/lib/python3.js @@ -13,13 +13,18 @@ function PythonObject() { this.version = "3.13.2"; this.create = function() { - var arch = SYS.getArch(); - if (arch.toLowerCase().indexOf("arm") > -1) { - this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe"); - } else if (arch.indexOf("64") > -1) { - this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe"); + var default_python3_path = SYS.getAppDataDir() + "\\python\\python.exe"; + if (FILE.fileExists(default_python3_path)) { + this.setBinPath(default_python3_path); } else { - this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe"); + var arch = SYS.getArch(); + if (arch.toLowerCase().indexOf("arm") > -1) { + this.setBinPath("bin\\arm64\\python-" + this.version + "-embed-arm64\\python.exe"); + } else if (arch.indexOf("64") > -1) { + this.setBinPath("bin\\x64\\python-" + this.version + "-embed-amd64\\python.exe"); + } else { + this.setBinPath("bin\\x86\\python-" + this.version + "-embed-win32\\python.exe"); + } } }; @@ -57,6 +62,6 @@ exports.execScript = function(scriptName, args) { return (new PythonObject()).execScript(scriptName, args); }; -exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.1"; +exports.VERSIONINFO = "Python Interface (python3.js) version 0.2.2"; exports.global = global; exports.require = global.require; diff --git a/lib/system.js b/lib/system.js index 37f8a0f..cd4fb90 100644 --- a/lib/system.js +++ b/lib/system.js @@ -37,6 +37,10 @@ function getEnvString(envName) { })(envName.toUpperCase()); } +function getAppDataDir() { + return getEnvString("APPDATA") + "\\WelsonJS"; +} + function get32BitFolder() { var base = getEnvString("WINDIR"); var syswow64 = base + "\\SysWOW64\\"; @@ -198,6 +202,7 @@ function getProcessVersion() { exports.createProcess = createProcess; exports.getEnvString = getEnvString; +exports.getAppDataDir = getAppDataDir; exports.get32BitFolder = get32BitFolder; exports.isElevated = isElevated; exports.getOS = getOS; @@ -219,6 +224,6 @@ exports.createShortcut = createShortcut; exports.ping = ping; exports.getProcessVersion = getProcessVersion; -exports.VERSIONINFO = "System Module (system.js) version 0.1.5"; +exports.VERSIONINFO = "System Module (system.js) version 0.1.6"; exports.global = global; exports.require = global.require; diff --git a/lib/wamr.js b/lib/wamr.js index 83d2573..0e3b550 100644 --- a/lib/wamr.js +++ b/lib/wamr.js @@ -1,15 +1,16 @@ // wamr.js -// Copyright 2019-2025, Namhyeon Go and the WelsonJS contributors. +// Namhyeon Go and the WelsonJS contributors. // SPDX-License-Identifier: GPL-3.0-or-later // https://github.com/gnh1201/welsonjs // -// WAMR(WebAssembly Micro Runtime) integration for WelsonJS framework +// WAMR(WebAssembly Micro Runtime) integration // https://github.com/bytecodealliance/wasm-micro-runtime // +var SYS = require("lib/system"); var SHELL = require("lib/shell"); var WAMRObject = function() { - this.binPath = "bin\\iwasm"; + this.binPath = SYS.getAppDataDir() + "\\wamr\\iwasm.exe"; this.verbose = 0; this.stackSize = 0; @@ -60,3 +61,7 @@ var WAMRObject = function() { exports.create = function() { return new WAMRObject(); }; + +exports.VERSIONINFO = "WAMR(WebAssembly Micro Runtime) integration (wamr.js) version 0.1.1"; +exports.global = global; +exports.require = global.require; diff --git a/setup.iss b/setup.iss index 205ea96..51fc7e9 100644 --- a/setup.iss +++ b/setup.iss @@ -84,6 +84,7 @@ Name: "{group}\Uninstall {cm:AppName}"; Filename: "{uninstallexe}"; AfterInstall ; Filename: {app}\bin\gtk2-runtime-2.24.33-2021-01-30-ts-win64.exe; ; Filename: {app}\bin\nmap-7.92\VC_redist.x86.exe; ; Filename: {app}\bin\nmap-7.92\npcap-1.50.exe; +Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -NoProfile -File ""{app}\afterInstall.ps1"""; WorkingDir: "{app}"; Flags: nowait Filename: {app}\installService.bat; Flags: nowait Filename: {app}\bin\x86\WelsonJS.Launcher.exe; Flags: nowait