diff --git a/src/core.ps1 b/src/core.ps1 new file mode 100644 index 0000000..5e724cb --- /dev/null +++ b/src/core.ps1 @@ -0,0 +1,214 @@ +function Invoke-ScriptAutomationMonkey { + + [CmdletBinding( + DefaultParameterSetName='UI', + SupportsShouldProcess=$true, + ConfirmImpact= 'Medium' + )] + param( + [Parameter(Mandatory,ParameterSetName='Toolkit')][switch]$SilentInstall, + [Parameter(Mandatory,ParameterSetName='Cleanup')][switch]$Cleanup, + [Parameter(Mandatory,ParameterSetName='Offboard')][switch]$Offboard, + + [Parameter(Mandatory,ParameterSetName='DattoFetch')] + [Parameter(Mandatory,ParameterSetName='DattoInstall')] + [switch]$UseWebhook, + + [Parameter(Mandatory,ParameterSetName='DattoFetch')] + [Parameter(Mandatory,ParameterSetName='DattoInstall')] + [String]$WebhookPassword, + + [string]$WebhookUrl = $Global:DattoWebhookUrl, + + [Parameter(ParameterSetName='DattoFetch')][switch]$FetchSites, + [Parameter(ParameterSetName='DattoFetch')][switch] $SaveSitesList, + [Parameter(ParameterSetName='DattoFetch')][ValidatePattern('\.csv$|\.json$')][string] $OutputFile = 'datto_sites.csv', + + [Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteUID, + [Parameter(Mandatory,ParameterSetName='DattoInstall')][string] $SiteName, + [Parameter(ParameterSetName='DattoInstall')][switch] $PushSiteVars, + [Parameter(ParameterSetName='DattoInstall')][switch] $InstallRMM, + [Parameter(ParameterSetName='DattoInstall')][switch] $SaveCopy + ) + + # Initialize config + URLs (moved out of core body) + Initialize-SamyConfig + + # Load remote functions (kept as-is, including verification style) + try { + Write-Host "[Info] Loading functions from: $Script:SamyFunctionsUrl" -ForegroundColor Cyan + $functionsContent = (Invoke-WebRequest -UseBasicParsing $Script:SamyFunctionsUrl -ErrorAction Stop).Content + + if ([string]::IsNullOrWhiteSpace($functionsContent)) { + throw "Downloaded content was empty." + } + + if ($functionsContent -notmatch '(?im)^\s*function\s+Initialize-NuGetProvider\b') { + Write-Host "[Warning] samy.functions.ps1 loaded, but Initialize-NuGetProvider not found in content." -ForegroundColor Yellow + } + + . ([ScriptBlock]::Create($functionsContent)) + + if (Get-Command Initialize-NuGetProvider -ErrorAction SilentlyContinue) { + Write-Host "[Success] Initialize-NuGetProvider is loaded and available." -ForegroundColor Green + } + else { + throw "Dot-sourcing completed, but Initialize-NuGetProvider is still not available." + } + } + catch { + throw "Failed to load samy.functions.ps1 from $Script:SamyFunctionsUrl. $($_.Exception.Message)" + } + + if (-not $Global:LogCache -or -not ($Global:LogCache -is [System.Collections.ArrayList])) { + $Global:LogCache = [System.Collections.ArrayList]::new() + } + + # Load tasks + $Global:SamyTasks = Get-SamyTasks -Url $Script:SamyTasksUrl + if (-not $Global:SamyTasks) { + throw "SAMY cannot continue: failed to load tasks from $Script:SamyTasksUrl" + } + + # Debug checks (kept) + $Global:SamyTasks | ForEach-Object { + if ($_.PSObject.Properties.Name -contains 'Tooltip' -and + $_.Tooltip -is [System.Collections.IEnumerable] -and + -not ($_.Tooltip -is [string])) { + + Write-LogHybrid "BAD TOOLTIP TYPE: Id=$($_.Id) Type=$($_.Tooltip.GetType().FullName) ValueCount=$(@($_.Tooltip).Count)" Warning UI -LogToEvent + } + } + + $Global:SamyTasks | ForEach-Object { + $tip = if ($_.PSObject.Properties.Name -contains 'Tooltip') { [string]$_.Tooltip } else { "" } + $len = $tip.Length + + if ($len -gt 80) { + $preview = $tip.Substring(0, [Math]::Min(160, $len)) + Write-LogHybrid "LONG TOOLTIP: Id=$($_.Id) Len=$len Preview='$preview'" Warning UI -LogToEvent + } + } + + switch ($PSCmdlet.ParameterSetName) { + + 'Toolkit' { + Write-LogHybrid "Toolkit-only mode" Info Startup -LogToEvent + Install-SVSMSP -InstallToolkit + return + } + + 'Cleanup' { + Write-LogHybrid "Running Toolkit cleanup mode" Info Startup -LogToEvent + Install-SVSMSP -Cleanup + return + } + + 'DattoFetch' { + Write-LogHybrid "Fetching site list only…" Info DattoAuth -LogToEvent + $sites = Install-DattoRMM ` + -UseWebhook ` + -WebhookPassword $WebhookPassword ` + -FetchSites ` + -SaveSitesList:$SaveSitesList ` + -OutputFile $OutputFile + + Write-LogHybrid "Done." Success DattoAuth -LogToEvent + return + } + + 'DattoInstall' { + Write-LogHybrid "Headless DattoRMM deploy" Info DattoAuth -LogToEvent + if ($PSCmdlet.ShouldProcess("Datto site '$SiteName'", "Headless install")) { + Install-DattoRMM ` + -UseWebhook ` + -WebhookPassword $WebhookPassword ` + -SiteUID $SiteUID ` + -SiteName $SiteName ` + -PushSiteVars:$PushSiteVars ` + -InstallRMM:$InstallRMM ` + -SaveCopy:$SaveCopy + } + return + } + + 'Offboard' { + Write-LogHybrid "Headless offboarding requested" Info OffBoard -LogToEvent + + $offboardTasks = $Global:SamyTasks | Where-Object { + $_.Page -eq 'offboard' -and -not [string]::IsNullOrWhiteSpace([string]$_.Name) + } + + if (-not $offboardTasks) { + Write-LogHybrid "No offboard tasks configured (or none with a Name)." Warning OffBoard -LogToEvent + return + } + + if (-not $PSCmdlet.ShouldProcess("Full off-boarding flow", "Execute every offboard task")) { + return + } + + foreach ($task in $offboardTasks) { + try { + Write-LogHybrid "Running offboard task: $($task.Label)" Info OffBoard -LogToEvent + + $fn = Get-TaskHandlerName -Task $task + if ([string]::IsNullOrWhiteSpace($fn)) { + Write-LogHybrid "Skipping task with missing handler (Id=$($task.Id) Name='$($task.Name)' Label='$($task.Label)')" Error OffBoard -LogToEvent + continue + } + + $cmd = Get-Command -Name $fn -ErrorAction SilentlyContinue + if (-not $cmd) { + Write-LogHybrid "Skipping task: handler not found '$fn' (task '$($task.Label)')" Error OffBoard -LogToEvent + continue + } + + if ($cmd.Parameters.ContainsKey('Context')) { & $fn -Context $null } + else { & $fn } + } + catch { + Write-LogHybrid "Offboard task '$($task.Label)' failed: $($_.Exception.Message)" Error OffBoard -LogToEvent + } + } + + Write-LogHybrid "Headless offboarding completed" Success OffBoard -LogToEvent + return + } + + 'UI' { + $url = "http://localhost:$Script:Port/" + Write-LogHybrid "Starting ScriptAutomationMonkey UI on $url" Info Startup + + $edgeCandidates = @( + "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe", + "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe" + ) + $edgePath = $edgeCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 + if (-not $edgePath) { + $cmd = Get-Command -Name 'msedge.exe' -ErrorAction SilentlyContinue + if ($cmd) { $edgePath = $cmd.Path } + } + + Start-Job -Name 'OpenScriptAutomationMonkeyUI' -ScriptBlock { + param([string]$u, [string]$edge) + Start-Sleep -Milliseconds 400 + try { + if ($edge -and (Test-Path $edge)) { + Start-Process -FilePath $edge -ArgumentList @('--new-window', "--app=$u") + } else { + Start-Process -FilePath $u + } + } catch { } + } -ArgumentList $url, $edgePath | Out-Null + + Start-Server + return + } + + default { + Write-LogHybrid "Unknown ParameterSetName '$($PSCmdlet.ParameterSetName)'" Error Startup -LogToEvent + throw "Unknown mode." + } + } +}