Public/Get-RunbookStatus.ps1

function Get-RunbookStatus {
    <#
    .SYNOPSIS
        Retrieves runbook execution status and history.
    .DESCRIPTION
        Queries the execution log directory ($env:USERPROFILE\.runbookengine\executions\) for
        past and current runbook executions. Supports filtering by execution ID, computer name,
        status, and time range. Returns execution summary objects.
    .PARAMETER ExecutionId
        Specific execution ID (GUID) to retrieve.
    .PARAMETER ComputerName
        Filter executions by target computer name.
    .PARAMETER Last
        Return only the last N executions.
    .PARAMETER Status
        Filter by execution status: All, Running, Completed, Failed, Escalated.
    .PARAMETER Since
        Return only executions since this datetime.
    .EXAMPLE
        Get-RunbookStatus -Last 10
        Get the 10 most recent runbook executions.
    .EXAMPLE
        Get-RunbookStatus -ComputerName 'SERVER01' -Status Failed
        Get all failed executions targeting SERVER01.
    .EXAMPLE
        Get-RunbookStatus -ExecutionId 'abc12345-...'
        Get details for a specific execution.
    .EXAMPLE
        Get-RunbookStatus -Since (Get-Date).AddHours(-8) -Status Escalated
        Get all escalated executions from the last 8 hours.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$ExecutionId,

        [Parameter()]
        [string]$ComputerName,

        [Parameter()]
        [int]$Last,

        [Parameter()]
        [ValidateSet('All', 'Running', 'Completed', 'Failed', 'Escalated')]
        [string]$Status = 'All',

        [Parameter()]
        [datetime]$Since
    )

    $executionsPath = Join-Path $env:USERPROFILE '.runbookengine\executions'

    if (-not (Test-Path $executionsPath)) {
        Write-Verbose "No executions directory found."
        return @()
    }

    # If specific execution ID requested
    if ($ExecutionId) {
        $execFile = Join-Path $executionsPath "$ExecutionId.json"
        if (Test-Path $execFile) {
            try {
                $exec = Get-Content -Path $execFile -Raw | ConvertFrom-Json
                return $exec
            }
            catch {
                Write-Warning "Failed to parse execution file: $_"
                return $null
            }
        }
        else {
            # Search by partial ID
            $matchingFiles = Get-ChildItem -Path $executionsPath -Filter "$ExecutionId*.json" -ErrorAction SilentlyContinue
            if ($matchingFiles) {
                $results = foreach ($file in $matchingFiles) {
                    try {
                        Get-Content -Path $file.FullName -Raw | ConvertFrom-Json
                    }
                    catch {
                        Write-Verbose "Failed to parse $($file.Name): $_"
                    }
                }
                return $results
            }

            Write-Warning "Execution not found: $ExecutionId"
            return $null
        }
    }

    # Load all execution files
    $executionFiles = Get-ChildItem -Path $executionsPath -Filter '*.json' -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending

    if (-not $executionFiles -or $executionFiles.Count -eq 0) {
        Write-Verbose "No execution records found."
        return @()
    }

    $executions = foreach ($file in $executionFiles) {
        try {
            $exec = Get-Content -Path $file.FullName -Raw | ConvertFrom-Json -ErrorAction Stop

            # Create summary object
            $stepCount = if ($exec.StepResults) { @($exec.StepResults).Count } else { 0 }
            $failedSteps = if ($exec.StepResults) {
                @($exec.StepResults | Where-Object { $_.Status -eq 'Failed' }).Count
            } else { 0 }

            $exec | Add-Member -NotePropertyName 'StepCount' -NotePropertyValue $stepCount -Force
            $exec | Add-Member -NotePropertyName 'FailedStepCount' -NotePropertyValue $failedSteps -Force
            $exec | Add-Member -NotePropertyName '_FileTime' -NotePropertyValue $file.LastWriteTime -Force

            $exec
        }
        catch {
            Write-Verbose "Failed to parse execution file $($file.Name): $_"
        }
    }

    # Apply filters
    if ($ComputerName) {
        $executions = $executions | Where-Object {
            $_.ComputerName -eq $ComputerName -or
            ($_.Parameters -and $_.Parameters.ComputerName -eq $ComputerName)
        }
    }

    if ($Status -ne 'All') {
        $executions = $executions | Where-Object { $_.Status -eq $Status }
    }

    if ($Since) {
        $executions = $executions | Where-Object {
            $execTime = if ($_.StartTime) {
                try { [DateTime]::Parse($_.StartTime) } catch { $null }
            }
            else { $_._FileTime }

            $execTime -and $execTime -ge $Since
        }
    }

    # Sort by start time descending
    $executions = $executions | Sort-Object {
        if ($_.StartTime) {
            try { [DateTime]::Parse($_.StartTime) } catch { [DateTime]::MinValue }
        }
        else { $_._FileTime }
    } -Descending

    # Apply Last limit
    if ($Last -and $Last -gt 0) {
        $executions = $executions | Select-Object -First $Last
    }

    # Return summary objects
    $executions | ForEach-Object {
        [PSCustomObject]@{
            ExecutionId    = $_.ExecutionId
            RunbookName    = $_.RunbookName
            ComputerName   = $_.ComputerName
            Status         = $_.Status
            StartTime      = $_.StartTime
            EndTime        = $_.EndTime
            Duration       = $_.Duration
            StepCount      = $_.StepCount
            FailedSteps    = $_.FailedStepCount
            WhatIf         = $_.WhatIf
            StepResults    = $_.StepResults
            ApprovalLog    = $_.ApprovalLog
            VerificationResults = $_.VerificationResults
            Correlations   = $_.Correlations
        }
    }
}