[CmdletBinding()] param() $LauncherVersion = "v2026-01-14-08" $BaseUri = "https://files.dayv.se/work/pwsh/" # MUST end with / try { $ProgressPreference = "SilentlyContinue" } catch {} # Trim incl NBSP $TrimChars = @([char]0x20,[char]0x09,[char]0x0D,[char]0x0A,[char]0xA0) function TrimX { param($s) if ($null -eq $s) { "" } else { ([string]$s).Trim($TrimChars) } } function Write-Banner { param( [string]$Title, [string]$Kind # MENU, RUN, END, WARN, ERROR ) $k = (TrimX $Kind).ToUpperInvariant() if (-not $k) { $k = "MENU" } $borderColor = "DarkGray" $titleColor = "Gray" switch ($k) { "MENU" { $borderColor = "Cyan"; $titleColor = "Cyan" } "RUN" { $borderColor = "Green"; $titleColor = "Green" } "END" { $borderColor = "Green"; $titleColor = "Green" } "WARN" { $borderColor = "Yellow"; $titleColor = "Yellow" } "ERROR" { $borderColor = "Red"; $titleColor = "Red" } default { $borderColor = "DarkGray"; $titleColor = "Gray" } } $line = "┌" + ("─" * 68) + "┐" $mid = "│ " + ($Title.PadRight(66)) + " │" $bot = "└" + ("─" * 68) + "┘" Write-Host "" Write-Host $line -ForegroundColor $borderColor Write-Host $mid -ForegroundColor $titleColor Write-Host $bot -ForegroundColor $borderColor } function Write-Box { param( [string[]]$Lines, [string]$Kind # MENU, RUN, END, WARN, ERROR ) $k = (TrimX $Kind).ToUpperInvariant() if (-not $k) { $k = "MENU" } $borderColor = "DarkGray" $textColor = "Gray" switch ($k) { "MENU" { $borderColor = "Cyan"; $textColor = "Cyan" } "RUN" { $borderColor = "Green"; $textColor = "Green" } "END" { $borderColor = "Green"; $textColor = "Green" } "WARN" { $borderColor = "Yellow"; $textColor = "Yellow" } "ERROR" { $borderColor = "Red"; $textColor = "Red" } default { $borderColor = "DarkGray"; $textColor = "Gray" } } $arr = @($Lines) if ($arr.Length -le 0) { $arr = @("") } $width = 68 $top = "┌" + ("─" * $width) + "┐" $bot = "└" + ("─" * $width) + "┘" Write-Host $top -ForegroundColor $borderColor for ($i = 0; $i -lt $arr.Length; $i++) { $t = [string]$arr[$i] if ($t.Length -gt $width) { $t = $t.Substring(0, $width) } $pad = $t.PadRight($width) Write-Host ("│" + $pad + "│") -ForegroundColor $textColor } Write-Host $bot -ForegroundColor $borderColor } function Get-ListingHtml { param($Uri) $headers = @{ "User-Agent" = "Mozilla/5.0" "Accept" = "text/html,*/*" "Cache-Control" = "no-cache" "Pragma" = "no-cache" } try { return [string](Invoke-WebRequest -UseBasicParsing -Uri $Uri -Headers $headers -Method Get -ErrorAction Stop).Content } catch { return [string](Invoke-WebRequest -Uri $Uri -Headers $headers -Method Get -ErrorAction Stop).Content } } function Normalize-Name { param($Name) $n = TrimX $Name if (-not $n) { return $null } if ($n -match '^(?

[^#?]+)') { $n = $Matches.p } $n = (TrimX $n).TrimEnd('/') if (-not $n) { return $null } if ($n -eq "." -or $n -eq ".." -or $n -eq "../") { return $null } if ($n -match '[\\/]' ) { return $null } if ($n -notmatch '^[a-zA-Z0-9._-]+$') { return $null } return $n } function Get-ScriptsFromHtml { param($Html) $names = @() foreach ($m in [regex]::Matches([string]$Html, '(?is)href\s*=\s*["'']([^"''#?]+)["'']')) { $norm = Normalize-Name $m.Groups[1].Value if ($norm) { $names += [string]$norm } } # Dedupe (case-insensitive) + sort $seen = @{} $out = @() foreach ($n in $names) { $k = ([string]$n).ToLowerInvariant() if (-not $seen.ContainsKey($k)) { $seen[$k] = $true $out += [string]$n } } return ($out | Sort-Object) } while ($true) { $html = $null try { $html = Get-ListingHtml -Uri $BaseUri } catch { Write-Banner -Title "ERROR: Failed to download listing" -Kind "ERROR" Write-Host $_ -ForegroundColor Red break } $scripts = @(Get-ScriptsFromHtml -Html $html) | Where-Object { $_ -and (TrimX $_) } $scripts = @($scripts) if ($scripts.Length -le 0) { Write-Banner -Title "WARN: No scripts found" -Kind "WARN" break } # ---- Boxed MENU ---- Write-Host "" Write-Banner -Title "MENU" -Kind "MENU" $menuLines = @() $menuLines += ("Scripts launcher {0}" -f $LauncherVersion) $menuLines += ("Source: {0}" -f $BaseUri) $menuLines += ("") for ($i = 0; $i -lt $scripts.Length; $i++) { $menuLines += ("[{0}] {1}" -f ($i + 1), $scripts[$i]) } $menuLines += ("") $menuLines += "Pick number or name. (r=refresh, q=quit)" Write-Box -Lines $menuLines -Kind "MENU" $choiceRaw = Read-Host "Run" $choice = TrimX $choiceRaw if (-not $choice) { continue } if ($choice.Equals("q", [System.StringComparison]::OrdinalIgnoreCase)) { break } if ($choice.Equals("r", [System.StringComparison]::OrdinalIgnoreCase)) { continue } # Selection logic inline $picked = $null if ($choice -match '^\d+$') { try { $num = [int]$choice if ($num -ge 1 -and $num -le $scripts.Length) { $picked = [string]$scripts[$num - 1] } } catch { } } else { for ($i = 0; $i -lt $scripts.Length; $i++) { $nm = [string]$scripts[$i] if ($nm -and $nm.Equals($choice, [System.StringComparison]::OrdinalIgnoreCase)) { $picked = $nm; break } } if (-not $picked) { $hits = @() for ($i = 0; $i -lt $scripts.Length; $i++) { $nm = [string]$scripts[$i] if ($nm -and $nm.StartsWith($choice, [System.StringComparison]::OrdinalIgnoreCase)) { $hits += $nm } } if (@($hits).Length -eq 1) { $picked = [string]$hits[0] } } } if (-not $picked) { Write-Banner -Title ("WARN: Invalid selection '{0}'" -f [string]$choiceRaw) -Kind "WARN" continue } if ($picked -notmatch '^[a-zA-Z0-9._-]+$') { Write-Banner -Title ("ERROR: Blocked unsafe name '{0}'" -f $picked) -Kind "ERROR" continue } $scriptUri = $BaseUri + $picked Write-Banner -Title ("RUN: {0}" -f $picked) -Kind "RUN" Write-Host ("URL: {0}" -f $scriptUri) -ForegroundColor DarkGray Write-Host "" try { Invoke-Expression (Invoke-RestMethod -Uri $scriptUri -Method Get -ErrorAction Stop) } catch { Write-Banner -Title ("ERROR running {0}" -f $picked) -Kind "ERROR" Write-Host $_ -ForegroundColor Red } Write-Banner -Title ("END: {0}" -f $picked) -Kind "END" Write-Host "Done. (q=quit, r=refresh, or pick another)" -ForegroundColor DarkGray }