Public/Invoke-KritOpenApiGenerate.ps1

function Invoke-KritOpenApiGenerate {
<#
.SYNOPSIS
    Generate a Krit.<Brand>OpenApi PowerShell module from an OpenAPI 3.x spec.

.DESCRIPTION
    v0.1.0 thin wrapper: shells out to the canonical Node generator at
    tools/Lens-OpenApiToPsModule-1507.mjs (bundled in this repo or located in
    the sibling KRTPax8ToShopifyConnector repo). Native PS port queued for
    0.2.0 (WAVE-5079 body).

    Emits a strongly-typed PowerShell module:
      - One PS function per operationId (or method+path).
      - Full IntelliSense via [Parameter()] + comment-based help.
      - ValidateSet enums extracted from spec schema enum / parameter enum.
      - Pipeline binding on path-param 'id'.
      - SupportsShouldProcess on DELETE / non-idempotent POST.
      - Common Invoke-<Brand>Api helper with retry / timeout / paging.

.PARAMETER Spec
    Path to OpenAPI 3.0/3.1 JSON or YAML document.

.PARAMETER Brand
    Pascal-cased brand for the generated module (Pax8, CrowdStrike, CrazyTel,
    Shopify, Supervisor, etc).

.PARAMETER OutModule
    Output .psm1 path. Defaults to ./Krit.<Brand>OpenApi.psm1.

.PARAMETER BaseUri
    Default base URI for the generated Invoke-<Brand>Api helper.

.PARAMETER AuthType
    bearer | basic | none. Default: bearer.

.PARAMETER WithAgenticSidecar
    Also emit Test-<Brand>Api + Resolve-<Brand>AgentTask companion functions
    per WAVE-5079 design.

.EXAMPLE
    Invoke-KritOpenApiGenerate -Spec ./pax8-openapi.json -Brand Pax8 `
        -OutModule ./Krit.Pax8OpenApi.psm1 -BaseUri 'https://api.pax8.com' `
        -AuthType bearer -WithAgenticSidecar
#>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)][string]$Spec,
        [Parameter(Mandatory)][string]$Brand,
        [string]$OutModule,
        [string]$BaseUri = 'http://127.0.0.1:80',
        [ValidateSet('bearer','basic','none')][string]$AuthType = 'bearer',
        [switch]$WithAgenticSidecar
    )

    if (-not (Test-Path -LiteralPath $Spec)) {
        throw "Spec not found: $Spec"
    }
    if (-not $OutModule) {
        $OutModule = Join-Path (Get-Location) "Krit.${Brand}OpenApi.psm1"
    }

    $moduleRoot = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
    $repoRoot   = Split-Path -Parent $moduleRoot
    $generator  = Join-Path $repoRoot 'tools/Lens-OpenApiToPsModule-1507.mjs'

    if (-not (Test-Path -LiteralPath $generator)) {
        # Sister-repo fallback (KRTPax8ToShopifyConnector ships the canonical generator).
        $sisterGen = Join-Path (Split-Path -Parent $repoRoot) 'KRTPax8ToShopifyConnector/scripts/tools/Lens-OpenApiToPsModule-1507.mjs'
        if (Test-Path -LiteralPath $sisterGen) { $generator = $sisterGen }
    }
    if (-not (Test-Path -LiteralPath $generator)) {
        throw "Generator not located. Looked for: $generator AND sister-repo fallback. Vendor tools/Lens-OpenApiToPsModule-1507.mjs into Krit.OpenApi/tools/ to make this module self-contained."
    }

    $node = (Get-Command node -ErrorAction SilentlyContinue)?.Source
    if (-not $node) { throw 'node.exe not on PATH — install Node.js to run the OpenAPI generator.' }

    $args = @(
        $generator
        '--Spec',     $Spec
        '--Brand',    $Brand
        '--OutModule',$OutModule
        '--BaseUri',  $BaseUri
        '--AuthType', $AuthType
    )

    if ($PSCmdlet.ShouldProcess($OutModule, "Generate Krit.${Brand}OpenApi module from $Spec")) {
        Write-Verbose "[Krit.OpenApi] Invoking: node $generator --Brand $Brand --OutModule $OutModule"
        & $node @args
        if ($LASTEXITCODE -ne 0) { throw "Generator failed (exit $LASTEXITCODE)." }

        if ($WithAgenticSidecar) {
            $sidecarOut = [System.IO.Path]::ChangeExtension($OutModule, '.AgenticSidecar.ps1')
            New-KritOpenApiAgenticSidecar -Spec $Spec -Brand $Brand -OutFile $sidecarOut
        }

        [pscustomobject]@{
            Spec       = (Resolve-Path -LiteralPath $Spec).Path
            Brand      = $Brand
            OutModule  = (Resolve-Path -LiteralPath $OutModule).Path
            Sidecar    = if ($WithAgenticSidecar) { (Resolve-Path -LiteralPath $sidecarOut).Path } else { $null }
            Generator  = $generator
            GeneratedAt = (Get-Date).ToUniversalTime().ToString('o')
        }
    }
}