mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-12-05 22:04:43 +00:00
906 lines
29 KiB
PowerShell
906 lines
29 KiB
PowerShell
# WelsonJS post-install script
|
||
# Namhyeon Go <gnh1201@catswords.re.kr>, and Catswords OSS contributors.
|
||
# https://github.com/gnh1201/welsonjs
|
||
|
||
# ================================
|
||
# PARAMETERS
|
||
# ================================
|
||
param(
|
||
[string]$TelemetryProvider = "",
|
||
[string]$TelemetryApiKey = "",
|
||
[string]$Version = "",
|
||
[string]$DistinctId = "",
|
||
[string]$Components = ""
|
||
)
|
||
|
||
# ================================
|
||
# LOGO
|
||
# ================================
|
||
$logo = @"
|
||
__ __ _ _ ____
|
||
\ \ / /__| |___ ___ _ __ | / ___|
|
||
\ \ /\ / / _ \ / __|/ _ \| '_ \ _ | \___ \
|
||
\ V V / __/ \__ \ (_) | | | | |_| |___) |
|
||
\_/\_/ \___|_|___/\___/|_| |_| \___/|____/
|
||
|
||
WelsonJS post-install script
|
||
https://github.com/gnh1201/welsonjs
|
||
|
||
"@
|
||
|
||
Write-Host $logo
|
||
|
||
# ================================
|
||
# SCRIPT ROOT RESOLUTION
|
||
# ================================
|
||
# Ensure $ScriptRoot is available even on older PowerShell
|
||
if (-not (Get-Variable -Name PSScriptRoot -ErrorAction SilentlyContinue)) {
|
||
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
}
|
||
else {
|
||
$ScriptRoot = $PSScriptRoot
|
||
}
|
||
|
||
# ================================
|
||
# LOAD DOWNLOAD URL TABLE (DownloadUrls.psd1 in /data folder)
|
||
# ================================
|
||
$DownloadUrls = @{}
|
||
$urlsFilePath = Join-Path $ScriptRoot "data/DownloadUrls.psd1"
|
||
|
||
if (Test-Path $urlsFilePath) {
|
||
try {
|
||
$DownloadUrls = Import-PowerShellDataFile -Path $urlsFilePath
|
||
}
|
||
catch {
|
||
Write-Host "[WARN] Failed to load DownloadUrls.psd1. Falling back to empty URL table."
|
||
$DownloadUrls = @{}
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[WARN] DownloadUrls.psd1 not found at: $urlsFilePath"
|
||
$DownloadUrls = @{}
|
||
}
|
||
|
||
function Get-DownloadUrl {
|
||
param(
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$Component,
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$Arch # x64, arm64, x86
|
||
)
|
||
|
||
$componentKey = $Component.ToLowerInvariant()
|
||
|
||
if (-not $DownloadUrls.ContainsKey($componentKey)) {
|
||
return $null
|
||
}
|
||
|
||
$entry = $DownloadUrls[$componentKey]
|
||
|
||
# Prefer arch-specific URL
|
||
if ($entry.ContainsKey($Arch)) {
|
||
return $entry[$Arch]
|
||
}
|
||
|
||
# Fallback to "any" (arch-independent)
|
||
if ($entry.ContainsKey("any")) {
|
||
return $entry["any"]
|
||
}
|
||
|
||
return $null
|
||
}
|
||
|
||
# ================================
|
||
# TELEMETRY
|
||
# ================================
|
||
if ($TelemetryProvider -and $TelemetryProvider.ToLower() -eq "posthog") {
|
||
|
||
# Skip telemetry if API key is missing
|
||
if (-not $TelemetryApiKey -or $TelemetryApiKey.Trim() -eq "") {
|
||
# No-op: continue script
|
||
}
|
||
else {
|
||
# Resolve distinct ID (fallback to machine name, then device UID)
|
||
$finalDistinctId = if ($DistinctId -and $DistinctId.Trim() -ne "") {
|
||
$DistinctId
|
||
} else {
|
||
# Attempt to get the machine name
|
||
$computerName = $env:COMPUTERNAME
|
||
|
||
if ($computerName -and $computerName.Trim() -ne "") {
|
||
$computerName
|
||
} else {
|
||
# Fall back to using the device UUID (if COMPUTERNAME is unavailable)
|
||
$deviceUid = (Get-WmiObject -Class Win32_ComputerSystemProduct).UUID
|
||
if ($deviceUid -and $deviceUid.Trim() -ne "") {
|
||
$deviceUid
|
||
} else {
|
||
# Optionally, generate a new UUID or use a predefined value if UUID is also unavailable
|
||
[guid]::NewGuid().ToString()
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($finalDistinctId -and $finalDistinctId.Trim() -ne "") {
|
||
|
||
# Build single event payload for PostHog /i/v0/e endpoint
|
||
$body = @{
|
||
api_key = $TelemetryApiKey
|
||
event = "app_installed"
|
||
distinct_id = $finalDistinctId
|
||
properties = @{
|
||
product = "welsonjs"
|
||
version = $Version
|
||
os = "windows"
|
||
source = "post-install.ps1"
|
||
components = $Components # Keep raw string here
|
||
}
|
||
timestamp = (Get-Date).ToString("o") # ISO 8601 format
|
||
} | ConvertTo-Json -Depth 5
|
||
|
||
try {
|
||
Invoke-RestMethod `
|
||
-Uri "https://us.i.posthog.com/i/v0/e/" `
|
||
-Method Post `
|
||
-ContentType "application/json" `
|
||
-Body $body | Out-Null
|
||
}
|
||
catch {
|
||
# Ignore telemetry failure (installer must not break)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
# ================================
|
||
# 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
|
||
|
||
# ================================
|
||
# COMPONENT SELECTION (SINGLE PARSE)
|
||
# ================================
|
||
# Convert Components (string) → array exactly once.
|
||
# Example: "python,curl,websocat"
|
||
# If empty → treat as "all selected" for backward compatibility.
|
||
|
||
$SelectedComponents = @()
|
||
$AllComponentsSelected = $true
|
||
|
||
if ($Components -and $Components.Trim() -ne "") {
|
||
$SelectedComponents =
|
||
$Components.Split(",") |
|
||
ForEach-Object { $_.Trim().ToLowerInvariant() }
|
||
|
||
$AllComponentsSelected = $false
|
||
}
|
||
|
||
function Test-ComponentSelected {
|
||
param(
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$Name
|
||
)
|
||
|
||
if ($AllComponentsSelected) {
|
||
return $true
|
||
}
|
||
|
||
return $SelectedComponents -contains $Name.ToLowerInvariant()
|
||
}
|
||
|
||
Write-Host "[*] Selected components (raw): $Components"
|
||
if ($AllComponentsSelected) {
|
||
Write-Host "[*] Component filter : <none> (treat as ALL selected)"
|
||
} else {
|
||
Write-Host "[*] Component filter : $($SelectedComponents -join ', ')"
|
||
}
|
||
Write-Host ""
|
||
|
||
# ================================
|
||
# ARCHITECTURE DETECTION
|
||
# ================================
|
||
function Get-NativeArchitecture {
|
||
# https://learn.microsoft.com/windows/win32/cimwin32prov/win32-processor
|
||
$arch = $null
|
||
|
||
try {
|
||
$proc = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop |
|
||
Select-Object -First 1
|
||
|
||
switch ($proc.Architecture) {
|
||
0 { $arch = "x86" } # 32-bit Intel/AMD
|
||
5 { $arch = "arm32" } # 32-bit ARM
|
||
12 { $arch = "arm64" } # treat ARM as arm64 target
|
||
9 { $arch = "x64" } # 64-bit Intel/AMD
|
||
6 { $arch = "ia64" } # Intel Itanium
|
||
default { $arch = "x86" } # fallback
|
||
}
|
||
}
|
||
catch {
|
||
# Fallback: only 32/64 bit detection if WMI/CIM is not available
|
||
if ([System.Environment]::Is64BitOperatingSystem) {
|
||
$arch = "x64"
|
||
}
|
||
else {
|
||
$arch = "x86"
|
||
}
|
||
}
|
||
|
||
return $arch
|
||
}
|
||
|
||
$arch = Get-NativeArchitecture
|
||
|
||
Write-Host "[*] Detected architecture: $arch"
|
||
Write-Host ""
|
||
|
||
# ================================
|
||
# HELPER FUNCTIONS
|
||
# ================================
|
||
function Ensure-EmptyDirectory {
|
||
param(
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$Path
|
||
)
|
||
|
||
# If a file exists at this path, delete it
|
||
if (Test-Path $Path -PathType Leaf) {
|
||
Write-Host "[WARN] File exists at '$Path'. Removing..."
|
||
Remove-Item -Path $Path -Force
|
||
}
|
||
|
||
# Ensure a directory exists at this path
|
||
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]$DestinationPath
|
||
)
|
||
|
||
Write-Host "[*] Downloading:"
|
||
Write-Host " $Url"
|
||
Write-Host " -> $DestinationPath"
|
||
|
||
# Ensure destination directory exists
|
||
$destDir = Split-Path -Parent $DestinationPath
|
||
if ($destDir -and -not (Test-Path $destDir)) {
|
||
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
|
||
}
|
||
|
||
$maxRetries = 3
|
||
$attempt = 0
|
||
$success = $false
|
||
|
||
while (-not $success -and $attempt -lt $maxRetries) {
|
||
$attempt++
|
||
try {
|
||
Invoke-WebRequest -Uri $Url -OutFile $DestinationPath -UseBasicParsing
|
||
$success = $true
|
||
}
|
||
catch {
|
||
Write-Host "[WARN] Download failed (attempt $attempt of $maxRetries): $($_.Exception.Message)"
|
||
if ($attempt -lt $maxRetries) {
|
||
Start-Sleep -Seconds 5
|
||
}
|
||
}
|
||
}
|
||
|
||
if (-not $success) {
|
||
throw "Failed to download $Url after $maxRetries attempts."
|
||
}
|
||
}
|
||
|
||
function Extract-CompressedFile {
|
||
param(
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$CompressedPath,
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$DestinationDirectory
|
||
)
|
||
|
||
Write-Host "[*] Extracting compressed file:"
|
||
Write-Host " $CompressedPath"
|
||
Write-Host " -> $DestinationDirectory"
|
||
|
||
# Ensure destination directory exists (clean)
|
||
Ensure-EmptyDirectory -Path $DestinationDirectory
|
||
|
||
# Temporary extraction workspace
|
||
$tmpExtractDir = Join-Path $DestinationDirectory "_tmp_extract"
|
||
Ensure-EmptyDirectory -Path $tmpExtractDir
|
||
|
||
# Extract all
|
||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||
[System.IO.Compression.ZipFile]::ExtractToDirectory($CompressedPath, $tmpExtractDir)
|
||
|
||
# Detect source root to move from
|
||
$entries = Get-ChildItem -Path $tmpExtractDir -Force
|
||
$SourceRoot = $tmpExtractDir
|
||
|
||
if ($entries.Count -eq 1 -and $entries[0].PSIsContainer) {
|
||
# ZIP contains exactly one top-level folder → unwrap that folder
|
||
$SourceRoot = $entries[0].FullName
|
||
Write-Host "[*] Detected single root folder inside zip: $($entries[0].Name)"
|
||
Write-Host "[*] Unwrapping folder content..."
|
||
}
|
||
else {
|
||
Write-Host "[*] Extracting multi-item archive (no root folder unwrapping needed)."
|
||
}
|
||
|
||
# Move all items from source root to final destination
|
||
Get-ChildItem -Path $SourceRoot -Force | ForEach-Object {
|
||
$targetPath = Join-Path $DestinationDirectory $_.Name
|
||
|
||
if (Test-Path $targetPath) {
|
||
Remove-Item -Path $targetPath -Recurse -Force
|
||
}
|
||
Move-Item -Path $_.FullName -Destination $targetPath
|
||
}
|
||
|
||
# Cleanup
|
||
Remove-Item -Path $tmpExtractDir -Recurse -Force
|
||
}
|
||
|
||
function Extract-TarGzArchive {
|
||
param(
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$ArchivePath,
|
||
[Parameter(Mandatory = $true)]
|
||
[string]$DestinationDirectory
|
||
)
|
||
|
||
Write-Host "[*] Extracting TAR.GZ archive:"
|
||
Write-Host " $ArchivePath"
|
||
Write-Host " -> $DestinationDirectory"
|
||
|
||
Ensure-EmptyDirectory -Path $DestinationDirectory
|
||
|
||
# Validate tar availability
|
||
$tarCommand = Get-Command tar -ErrorAction SilentlyContinue
|
||
if (-not $tarCommand) {
|
||
Write-Host "[ERROR] 'tar' command not found."
|
||
throw "tar not available on this system."
|
||
}
|
||
|
||
Write-Host "[DEBUG] tar command:"
|
||
Write-Host " tar -xzf `"$ArchivePath`" -C `"$DestinationDirectory`""
|
||
|
||
try {
|
||
& tar -xzf "$ArchivePath" -C "$DestinationDirectory"
|
||
if ($LASTEXITCODE -ne 0) {
|
||
throw "tar exited with code $LASTEXITCODE."
|
||
}
|
||
}
|
||
catch {
|
||
throw "Failed to extract TAR.GZ archive: $($_.Exception.Message)"
|
||
}
|
||
}
|
||
|
||
# ================================
|
||
# COMPRESSED / INSTALLER PATHS
|
||
# ================================
|
||
$PythonCompressed = Join-Path $TmpDir "python.zip"
|
||
$CurlCompressed = Join-Path $TmpDir "curl.zip"
|
||
$YaraCompressed = Join-Path $TmpDir "yara.zip"
|
||
$WamrArchive = Join-Path $TmpDir "wamr.tar.gz"
|
||
$WebsocatCompressed = Join-Path $TmpDir "websocat.zip"
|
||
$ArtifactsCompressed = Join-Path $TmpDir "artifacts.zip"
|
||
$GtkRuntimeInstaller = Join-Path $TmpDir "gtk-runtime.exe"
|
||
$TessdataCompressed = Join-Path $TmpDir "tessdata.zip"
|
||
$TessdataBestCompressed = Join-Path $TmpDir "tessdata_best.zip"
|
||
$TessdataFastCompressed = Join-Path $TmpDir "tessdata_fast.zip"
|
||
$NpcapInstaller = Join-Path $TmpDir "npcap-setup.exe"
|
||
$NmapInstaller = Join-Path $TmpDir "nmap-setup.exe"
|
||
$GtkServerCompressed = Join-Path $TmpDir "gtkserver.zip"
|
||
$WinDivertCompressed = Join-Path $TmpDir "windivert.zip"
|
||
$AndroidPlatformToolsCompressed = Join-Path $TmpDir "android-platform-tools.zip"
|
||
|
||
# ================================
|
||
# DOWNLOAD PHASE
|
||
# ================================
|
||
try {
|
||
# Python (component: python)
|
||
if (Test-ComponentSelected -Name "python") {
|
||
$url = Get-DownloadUrl -Component "python" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $PythonCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] Python URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] Python component not selected. Skipping download."
|
||
}
|
||
|
||
# curl (component: curl)
|
||
if (Test-ComponentSelected -Name "curl") {
|
||
$url = Get-DownloadUrl -Component "curl" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $CurlCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] curl URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] curl component not selected. Skipping download."
|
||
}
|
||
|
||
# YARA (component: yara)
|
||
if (Test-ComponentSelected -Name "yara") {
|
||
$url = Get-DownloadUrl -Component "yara" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $YaraCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] YARA URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] YARA component not selected. Skipping download."
|
||
}
|
||
|
||
# WAMR (component: wamr)
|
||
if (Test-ComponentSelected -Name "wamr") {
|
||
$url = Get-DownloadUrl -Component "wamr" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $WamrArchive
|
||
}
|
||
else {
|
||
Write-Host "[*] WAMR URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] WAMR component not selected. Skipping download."
|
||
}
|
||
|
||
# websocat (component: websocat)
|
||
if (Test-ComponentSelected -Name "websocat") {
|
||
$url = Get-DownloadUrl -Component "websocat" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $WebsocatCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] websocat URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] websocat component not selected. Skipping download."
|
||
}
|
||
|
||
# artifacts (component: artifacts)
|
||
if (Test-ComponentSelected -Name "artifacts") {
|
||
$url = Get-DownloadUrl -Component "artifacts" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $ArtifactsCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] artifacts URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] artifacts component not selected. Skipping download."
|
||
}
|
||
|
||
# GTK3 runtime (component: gtk3runtime)
|
||
if (Test-ComponentSelected -Name "gtk3runtime") {
|
||
$url = Get-DownloadUrl -Component "gtk3runtime" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $GtkRuntimeInstaller
|
||
}
|
||
else {
|
||
Write-Host "[*] gtk3runtime URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] gtk3runtime component not selected. Skipping download."
|
||
}
|
||
|
||
# GTK server (component: gtkserver)
|
||
if (Test-ComponentSelected -Name "gtkserver") {
|
||
$url = Get-DownloadUrl -Component "gtkserver" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $GtkServerCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] gtkserver URL not available for arch: $arch. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] gtkserver component not selected. Skipping download."
|
||
}
|
||
|
||
# tessdata (component: tessdata)
|
||
if (Test-ComponentSelected -Name "tessdata") {
|
||
$url = Get-DownloadUrl -Component "tessdata" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $TessdataCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata URL not available. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata component not selected. Skipping download."
|
||
}
|
||
|
||
# tessdata_best (component: tessdata_best)
|
||
if (Test-ComponentSelected -Name "tessdata_best") {
|
||
$url = Get-DownloadUrl -Component "tessdata_best" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $TessdataBestCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_best URL not available. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_best component not selected. Skipping download."
|
||
}
|
||
|
||
# tessdata_fast (component: tessdata_fast)
|
||
if (Test-ComponentSelected -Name "tessdata_fast") {
|
||
$url = Get-DownloadUrl -Component "tessdata_fast" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $TessdataFastCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_fast URL not available. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_fast component not selected. Skipping download."
|
||
}
|
||
|
||
# Nmap bundle (component: nmap) – includes Npcap + Nmap installer
|
||
if (Test-ComponentSelected -Name "nmap") {
|
||
# Npcap
|
||
$url = Get-DownloadUrl -Component "npcap" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $NpcapInstaller
|
||
}
|
||
else {
|
||
Write-Host "[*] npcap URL not available. Skipping npcap download."
|
||
}
|
||
|
||
# Nmap
|
||
$url = Get-DownloadUrl -Component "nmap" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $NmapInstaller
|
||
}
|
||
else {
|
||
Write-Host "[*] nmap URL not available. Skipping nmap download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] nmap component not selected. Skipping Npcap/Nmap download."
|
||
}
|
||
|
||
# windivert (component: windivert)
|
||
if (Test-ComponentSelected -Name "windivert") {
|
||
$url = Get-DownloadUrl -Component "windivert" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $WinDivertCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] WinDivert URL not available. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] WinDivert component not selected. Skipping download."
|
||
}
|
||
|
||
# Android Platform Tools (component: android_platform_tools)
|
||
if (Test-ComponentSelected -Name "android_platform_tools") {
|
||
$url = Get-DownloadUrl -Component "android_platform_tools" -Arch $arch
|
||
if ($url) {
|
||
Download-File -Url $url -DestinationPath $AndroidPlatformToolsCompressed
|
||
}
|
||
else {
|
||
Write-Host "[*] Android Platform Tools URL not available. Skipping download."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] Android Platform Tools component not selected. Skipping download."
|
||
}
|
||
}
|
||
catch {
|
||
Write-Host "[FATAL] Download phase failed."
|
||
if ($_ -is [System.Exception]) {
|
||
Write-Host $_.Exception.Message
|
||
} else {
|
||
Write-Host $_
|
||
}
|
||
exit 1
|
||
}
|
||
|
||
# ================================
|
||
# EXTRACT / INSTALL PHASE
|
||
# ================================
|
||
try {
|
||
# Python (component: python)
|
||
if (Test-ComponentSelected -Name "python") {
|
||
if (Test-Path $PythonCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $PythonCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "python")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] Python archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] Python component not selected. Skipping installation."
|
||
}
|
||
|
||
# curl (component: curl)
|
||
if (Test-ComponentSelected -Name "curl") {
|
||
if (Test-Path $CurlCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $CurlCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "curl")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] curl archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] curl component not selected. Skipping installation."
|
||
}
|
||
|
||
# YARA (component: yara)
|
||
if (Test-ComponentSelected -Name "yara") {
|
||
if (Test-Path $YaraCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $YaraCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "yara")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] YARA archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] YARA component not selected. Skipping installation."
|
||
}
|
||
|
||
# WAMR (component: wamr, TAR.GZ)
|
||
if (Test-ComponentSelected -Name "wamr") {
|
||
if (Test-Path $WamrArchive) {
|
||
Extract-TarGzArchive `
|
||
-ArchivePath $WamrArchive `
|
||
-DestinationDirectory (Join-Path $TargetDir "wamr")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] WAMR archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] WAMR component not selected. Skipping installation."
|
||
}
|
||
|
||
# websocat (component: websocat)
|
||
if (Test-ComponentSelected -Name "websocat") {
|
||
if (Test-Path $WebsocatCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $WebsocatCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "websocat")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] websocat archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] websocat component not selected. Skipping installation."
|
||
}
|
||
|
||
# artifacts (component: artifacts)
|
||
if (Test-ComponentSelected -Name "artifacts") {
|
||
if (Test-Path $ArtifactsCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $ArtifactsCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "bin")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] artifacts archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] artifacts component not selected. Skipping installation."
|
||
}
|
||
|
||
# GTK3 runtime (component: gtk3runtime) – run installer and wait
|
||
if (Test-ComponentSelected -Name "gtk3runtime") {
|
||
if (Test-Path $GtkRuntimeInstaller) {
|
||
Write-Host "[*] Running GTK runtime installer (wait): $GtkRuntimeInstaller"
|
||
Start-Process -FilePath $GtkRuntimeInstaller -Wait -ErrorAction Stop
|
||
}
|
||
else {
|
||
Write-Host "[WARN] GTK runtime installer not found. Skipping."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] gtk3runtime component not selected. Skipping installation."
|
||
}
|
||
|
||
# GTK server (component: gtkserver) – extract ZIP into AppData
|
||
if (Test-ComponentSelected -Name "gtkserver") {
|
||
if (Test-Path $GtkServerCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $GtkServerCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "gtkserver")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] gtkserver archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] gtkserver component not selected. Skipping installation."
|
||
}
|
||
|
||
# tessdata (component: tessdata)
|
||
if (Test-ComponentSelected -Name "tessdata") {
|
||
if (Test-Path $TessdataCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $TessdataCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "tessdata")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] tessdata archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata component not selected. Skipping installation."
|
||
}
|
||
|
||
# tessdata_best (component: tessdata_best)
|
||
if (Test-ComponentSelected -Name "tessdata_best") {
|
||
if (Test-Path $TessdataBestCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $TessdataBestCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "tessdata_best")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] tessdata_best archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_best component not selected. Skipping installation."
|
||
}
|
||
|
||
# tessdata_fast (component: tessdata_fast)
|
||
if (Test-ComponentSelected -Name "tessdata_fast") {
|
||
if (Test-Path $TessdataFastCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $TessdataFastCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "tessdata_fast")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] tessdata_fast archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] tessdata_fast component not selected. Skipping installation."
|
||
}
|
||
|
||
# Nmap bundle (component: nmap) – Npcap → Nmap → VC_redist.x86.exe
|
||
if (Test-ComponentSelected -Name "nmap") {
|
||
|
||
# Npcap
|
||
if (Test-Path $NpcapInstaller) {
|
||
Write-Host "[*] Running Npcap installer (wait): $NpcapInstaller"
|
||
Start-Process -FilePath $NpcapInstaller -Wait -ErrorAction Stop
|
||
}
|
||
else {
|
||
Write-Host "[WARN] Npcap installer not found. Skipping Npcap."
|
||
}
|
||
|
||
# Nmap
|
||
if (Test-Path $NmapInstaller) {
|
||
Write-Host "[*] Running Nmap installer (wait): $NmapInstaller"
|
||
Start-Process -FilePath $NmapInstaller -Wait -ErrorAction Stop
|
||
}
|
||
else {
|
||
Write-Host "[WARN] Nmap installer not found. Skipping Nmap."
|
||
}
|
||
|
||
# Find and run VC_redist.x86.exe inside Nmap installation directory
|
||
$searchDirs = @()
|
||
|
||
if (${env:ProgramFiles(x86)}) {
|
||
$searchDirs += (Join-Path ${env:ProgramFiles(x86)} "Nmap")
|
||
}
|
||
if ($env:ProgramFiles) {
|
||
$searchDirs += (Join-Path $env:ProgramFiles "Nmap")
|
||
}
|
||
|
||
$vcRedist = $null
|
||
foreach ($dir in $searchDirs) {
|
||
if (Test-Path $dir) {
|
||
$candidate = Get-ChildItem -Path $dir -Filter "vc_redist.x86.exe" -Recurse -ErrorAction SilentlyContinue |
|
||
Select-Object -First 1
|
||
if ($candidate) {
|
||
$vcRedist = $candidate
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($vcRedist) {
|
||
Write-Host "[*] Running VC_redist.x86 installer: $($vcRedist.FullName)"
|
||
Start-Process -FilePath $vcRedist.FullName -Wait -ErrorAction SilentlyContinue
|
||
}
|
||
else {
|
||
Write-Host "[WARN] VC_redist.x86.exe not found under expected Nmap directories."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] nmap component not selected. Skipping Npcap/Nmap installation."
|
||
}
|
||
|
||
# windivert (component: windivert)
|
||
if (Test-ComponentSelected -Name "windivert") {
|
||
if (Test-Path $WinDivertCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $WinDivertCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "windivert")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] WinDivert archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] WinDivert component not selected. Skipping installation."
|
||
}
|
||
|
||
# Android Platform Tools (component: android_platform_tools)
|
||
if (Test-ComponentSelected -Name "android_platform_tools") {
|
||
if (Test-Path $AndroidPlatformToolsCompressed) {
|
||
Extract-CompressedFile `
|
||
-CompressedPath $AndroidPlatformToolsCompressed `
|
||
-DestinationDirectory (Join-Path $TargetDir "android_platform_tools")
|
||
}
|
||
else {
|
||
Write-Host "[WARN] Android Platform Tools archive not found. Skipping installation."
|
||
}
|
||
}
|
||
else {
|
||
Write-Host "[*] Android Platform Tools component not selected. Skipping installation."
|
||
}
|
||
}
|
||
catch {
|
||
Write-Host "[FATAL] Extraction/installation phase failed."
|
||
if ($_ -is [System.Exception]) {
|
||
Write-Host $_.Exception.Message
|
||
} else {
|
||
Write-Host $_
|
||
}
|
||
exit 1
|
||
}
|
||
|
||
# ================================
|
||
# FINISH
|
||
# ================================
|
||
Write-Host "[*] Installation completed successfully."
|
||
Write-Host "[*] Installed into: $TargetDir"
|
||
Write-Host ""
|
||
exit 0
|