diff --git a/samy.ps1 b/samy.ps1 index 26f9e65..5d52425 100644 --- a/samy.ps1 +++ b/samy.ps1 @@ -77,47 +77,89 @@ function Import-SamyModule { Loads a SAMY subsystem script from local disk or from the Git repo. .DESCRIPTION - Modules are now expected under a "module" subfolder. + Local: + - Prefer .\module\ + - Fallback to .\ - 1. If running from a saved script (PSCommandPath) and the file exists under - .\module\, dot-sources that local file (dev mode). - 2. Otherwise, downloads the module from: - $Script:SamyRepoBase / $Script:SamyBranch / module / ?raw=1 - and Invoke-Expression on its content (remote iwr|iex mode). - - .PARAMETER Name - File name of the module, for example "Samy.Logging.ps1". + Remote (iwr | iex): + - Try .../module/?raw=1 first + - If that 404s, fallback to .../?raw=1 #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name ) - # 1) Local dev mode: script saved to disk, use .\module\ + # 1) Local dev mode: script saved to disk if ($PSCommandPath) { $moduleRoot = Join-Path -Path $PSScriptRoot -ChildPath 'module' - $localPath = Join-Path -Path $moduleRoot -ChildPath $Name + $localModulePath = Join-Path -Path $moduleRoot -ChildPath $Name + $localRootPath = Join-Path -Path $PSScriptRoot -ChildPath $Name - if (Test-Path $localPath) { - . $localPath + if (Test-Path $localModulePath) { + . $localModulePath + return + } + + if (Test-Path $localRootPath) { + . $localRootPath return } } - # 2) Remote mode (iwr | iex): pull module from repo/module - $url = "$Script:SamyRepoBase/$Script:SamyBranch/module/$Name?raw=1" + # 2) Remote mode (iwr | iex): pull module from repo + $base = "$Script:SamyRepoBase/$Script:SamyBranch" - try { - $resp = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop + $primaryUrl = "$base/module/$Name?raw=1" + $fallbackUrl = "$base/$Name?raw=1" + + # Helper to download and load a URL + function Invoke-LoadUrl { + param( + [Parameter(Mandatory)][string]$Url, + [Parameter(Mandatory)][string]$ModuleName + ) + + $resp = Invoke-WebRequest -Uri $Url -UseBasicParsing -ErrorAction Stop $content = $resp.Content if (-not $content) { - Write-Host ("[Error] Module {0} from {1} returned empty content." -f $Name, $url) -ForegroundColor Red + Write-Host ("[Error] Module {0} from {1} returned empty content." -f $ModuleName, $Url) -ForegroundColor Red throw "Empty module content." } Invoke-Expression $content } + + try { + # Try /module/?raw=1 first + Invoke-LoadUrl -Url $primaryUrl -ModuleName $Name + } + catch [System.Net.WebException] { + $response = $_.Exception.Response + $statusCode = $null + if ($response -and $response.StatusCode) { + $statusCode = [int]$response.StatusCode + } + + if ($statusCode -eq 404) { + # Fallback to root-level file + Write-Host ("[Info] Module {0} not found at {1} (404). Trying fallback {2}." -f $Name, $primaryUrl, $fallbackUrl) -ForegroundColor Yellow + try { + Invoke-LoadUrl -Url $fallbackUrl -ModuleName $Name + } + catch { + Write-Host ("[Error] Failed to load module {0} from fallback {1}: {2}" -f $Name, $fallbackUrl, $_.Exception.Message) -ForegroundColor Red + throw + } + } + else { + Write-Host ("[Error] Failed to load module {0} from {1}: {2}" -f $Name, $primaryUrl, $_.Exception.Message) -ForegroundColor Red + throw + } + } catch { - Write-Host ("[Error] Failed to load module {0} from {1}: {2}" -f $Name, $url, $_.Exception.Message) -ForegroundColor Red + if (-not ($_ -is [System.Net.WebException])) { + Write-Host ("[Error] Failed to load module {0} from {1}: {2}" -f $Name, $primaryUrl, $_.Exception.Message) -ForegroundColor Red + } throw } }