ek-station 0.3.0

Station for the ek ecosystem: headless plugin host bootstrap - assembles ek-core MainEngine from a compile-time plugin factory table, runs the svctl supervisor service (list/start/remove over bus ask) and a headless main (readiness marker + graceful close).
Documentation
# Package-content guard (run BEFORE `cargo publish`).
# Asserts required files (docs + sources + tests) and forbidden patterns (secrets).
# Usage: powershell -ExecutionPolicy Bypass -File scripts/check-package.ps1 [-AllowDirty]
# NOTE: ASCII-only on purpose (Windows PowerShell 5.1 decodes BOM-less .ps1 as ANSI).
[CmdletBinding()]
param(
    [switch]$AllowDirty
)

$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
Push-Location $root
try {
    # ---- required: the station crate must carry ----
    $required = @(
        'Cargo.toml',
        'README.md',
        'DESIGN.md',
        'CHANGELOG.md',
        'src/lib.rs',
        'src/wire.rs',
        'src/factory.rs',
        'src/supervisor.rs',
        'src/station.rs',
        'src/pickfile.rs',
        'src/main.rs',
        'tests/station.rs',
        'tests/headless_exit.rs',
        'tests/common/mod.rs',
        'tests/pickfile.rs',
        'tests/assembly.rs'
    )
    # 'tests/assembly.rs' 是 0.2.9 从 ek-converter 迁来的跨 crate 集成套(15 钉),
    # 靠 dev-dep ek-converter 编译 —— 它也是发布面的一部分,谁哪天删了它这道守卫要当场红。

    # ---- forbidden: must never reach the registry ----
    $forbiddenPatterns = @(
        '\.local\.env$',
        '(^|/)\.env$',
        '\.pfx$',
        '\.pem$',
        '\.key$',
        '(^|/)probe/',
        '\.py$',
        '\.toml\.local$'
    )

    $cargoArgs = @('package', '--list')
    if ($AllowDirty) { $cargoArgs += '--allow-dirty' }

    Write-Host "running: cargo $($cargoArgs -join ' ')"
    $list = & cargo @cargoArgs 2>$null
    if ($LASTEXITCODE -ne 0) {
        Write-Host "FAIL: 'cargo package --list' returned $LASTEXITCODE (dirty tree? pass -AllowDirty)" -ForegroundColor Red
        exit 1
    }

    $files = @($list | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' })
    Write-Host "package file count: $($files.Count)"

    $ok = $true

    # ---- required present ----
    foreach ($r in $required) {
        $norm = $r -replace '/', '\'
        $hit = @($files | Where-Object { ($_ -replace '/', '\') -eq $norm })
        if ($hit.Count -gt 0) {
            Write-Host ("  OK   " + $r)
        } else {
            Write-Host ("  MISS " + $r) -ForegroundColor Red
            $ok = $false
        }
    }

    # ---- forbidden absent ----
    foreach ($p in $forbiddenPatterns) {
        $bad = @($files | Where-Object { $_ -match $p })
        if ($bad.Count -gt 0) {
            Write-Host ("  LEAK (pattern " + $p + "): " + ($bad -join ', ')) -ForegroundColor Red
            $ok = $false
        } else {
            Write-Host ("  clean (pattern " + $p + ")")
        }
    }

    if ($ok) {
        Write-Host "PASS: package content looks correct" -ForegroundColor Green
        Write-Host "note: for the compressed size (crates.io limit 10 MiB) run 'cargo package' and check target/package/*.crate"
        exit 0
    } else {
        Write-Host "FAIL: package content is not publishable" -ForegroundColor Red
        exit 1
    }
} finally {
    Pop-Location
}