Files
MercuryToolbox/scripts/check-ecosystem.ps1
T

1670 lines
83 KiB
PowerShell

[CmdletBinding()]
param(
[ValidateSet('Debug', 'Release', 'ReleaseFast', 'ReleaseSize')]
[string]$Configuration = 'ReleaseFast',
[switch]$SkipBuild,
[switch]$SkipPromptGeneration
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
. (Join-Path $PSScriptRoot 'toolbox-commands.ps1')
function Invoke-StrictNative {
param(
[Parameter(Mandatory = $true)]
[string]$FilePath,
[Parameter(Mandatory = $true)]
[string[]]$ArgumentList
)
& $FilePath @ArgumentList
if ($LASTEXITCODE -ne 0) {
throw "Command failed with exit code ${LASTEXITCODE}: $FilePath $($ArgumentList -join ' ')"
}
}
function Get-RequiredCommandPath {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
$command = Get-Command -Name $Name -ErrorAction SilentlyContinue
if ($null -eq $command) {
throw "Required command not found on PATH: $Name"
}
return $command.Source
}
function Assert-Condition {
param(
[Parameter(Mandatory = $true)]
[bool]$Condition,
[Parameter(Mandatory = $true)]
[string]$Message
)
if (-not $Condition) {
throw $Message
}
}
function Get-ToolboxBinaryPath {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
$path = Join-Path $script:BinaryRoot "$Name.exe"
if (-not (Test-Path -LiteralPath $path)) {
throw "Expected toolbox binary not found: $path"
}
return $path
}
function Get-FreeTcpPort {
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
$listener.Start()
try {
return ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
}
finally {
$listener.Stop()
}
}
function Start-LoopbackHttpServer {
param(
[Parameter(Mandatory = $true)]
[int]$Port
)
$readyFile = Join-Path ([System.IO.Path]::GetTempPath()) ("mercury-loopback-ready-" + [System.Guid]::NewGuid())
$job = Start-Job -ScriptBlock {
param($JobPort, $JobReadyFile)
$listener = [System.Net.HttpListener]::new()
$listener.Prefixes.Add("http://127.0.0.1:$JobPort/")
$listener.Start()
New-Item -ItemType File -Force -Path $JobReadyFile | Out-Null
try {
$context = $listener.GetContext()
$payload = [System.Text.Encoding]::UTF8.GetBytes('{"ok":true}')
$response = $context.Response
$response.StatusCode = 200
$response.ContentType = 'application/json'
$response.ContentLength64 = $payload.Length
$response.OutputStream.Write($payload, 0, $payload.Length)
$response.OutputStream.Close()
}
finally {
Remove-Item -LiteralPath $JobReadyFile -Force -ErrorAction SilentlyContinue
$listener.Stop()
$listener.Close()
}
} -ArgumentList $Port, $readyFile
Wait-Until -Description "loopback HTTP server on port $Port" -TimeoutMilliseconds 3000 -Probe {
if (Test-Path -LiteralPath $readyFile) {
return $true
}
if ($job.State -in @('Completed', 'Failed', 'Stopped')) {
return $true
}
return $false
}
if (-not (Test-Path -LiteralPath $readyFile)) {
Stop-JobSafe -Job $job
throw "loopback HTTP server on port $Port exited before becoming ready"
}
return [pscustomobject]@{
Job = $job
ReadyFile = $readyFile
}
}
function Stop-JobSafe {
param(
[Parameter(Mandatory = $true)]
[System.Management.Automation.Job]$Job
)
if ($Job.State -eq 'Running') {
Stop-Job -Job $Job | Out-Null
}
Receive-Job -Job $Job -ErrorAction SilentlyContinue | Out-Null
Remove-Job -Job $Job -Force -ErrorAction SilentlyContinue | Out-Null
}
function Stop-LoopbackHttpServer {
param(
[Parameter(Mandatory = $true)]
[psobject]$Server
)
if ($null -ne $Server.Job) {
Stop-JobSafe -Job $Server.Job
}
if ($Server.PSObject.Properties.Name -contains 'ReadyFile') {
Remove-Item -LiteralPath $Server.ReadyFile -Force -ErrorAction SilentlyContinue
}
}
function Wait-Until {
param(
[Parameter(Mandatory = $true)]
[string]$Description,
[Parameter(Mandatory = $true)]
[scriptblock]$Probe,
[int]$TimeoutMilliseconds = 3000,
[int]$IntervalMilliseconds = 50
)
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
while ($stopwatch.ElapsedMilliseconds -lt $TimeoutMilliseconds) {
if (& $Probe) {
return
}
Start-Sleep -Milliseconds $IntervalMilliseconds
}
throw "timed out waiting for $Description"
}
function Test-TcpPortListening {
param(
[Parameter(Mandatory = $true)]
[int]$Port
)
$client = [System.Net.Sockets.TcpClient]::new()
try {
$connect = $client.ConnectAsync([System.Net.IPAddress]::Loopback, $Port)
if (-not $connect.Wait(150)) {
return $false
}
return $client.Connected
}
catch {
return $false
}
finally {
$client.Dispose()
}
}
function Test-ExclusiveFileLock {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$stream = $null
try {
$stream = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
return $false
}
catch [System.IO.IOException] {
return $true
}
finally {
if ($null -ne $stream) {
$stream.Dispose()
}
}
}
function Invoke-Check {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[Parameter(Mandatory = $true)]
[scriptblock]$Script
)
try {
$detail = & $Script
$script:Results.Add([pscustomobject]@{
Check = $Name
Status = 'PASS'
Detail = [string]$detail
})
}
catch {
$script:Results.Add([pscustomobject]@{
Check = $Name
Status = 'FAIL'
Detail = $_.Exception.Message
})
$script:Failed = $true
}
}
function Invoke-PwshFile {
param(
[Parameter(Mandatory = $true)]
[string]$FilePath,
[string[]]$ArgumentList = @()
)
$output = & pwsh -NoProfile -ExecutionPolicy Bypass -File $FilePath @ArgumentList 2>&1
return [pscustomobject]@{
ExitCode = $LASTEXITCODE
Output = (($output | ForEach-Object { [string]$_ }) -join "`n")
}
}
function Invoke-NativeCapture {
param(
[Parameter(Mandatory = $true)]
[string]$FilePath,
[string[]]$ArgumentList = @(),
[int]$TimeoutMilliseconds = 5000
)
$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $FilePath
foreach ($argument in $ArgumentList) {
[void]$psi.ArgumentList.Add($argument)
}
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.CreateNoWindow = $true
$process = [System.Diagnostics.Process]::new()
$process.StartInfo = $psi
[void]$process.Start()
if (-not $process.WaitForExit($TimeoutMilliseconds)) {
try {
$process.Kill($true)
}
catch {
$process.Kill()
}
throw "timed out after ${TimeoutMilliseconds}ms: $FilePath $($ArgumentList -join ' ')"
}
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$exitCode = $process.ExitCode
$process.Dispose()
return [pscustomobject]@{
ExitCode = $exitCode
Stdout = $stdout
Stderr = $stderr
Output = ($stdout + $stderr)
}
}
function Test-ToolboxBinaryVersions {
foreach ($commandName in Get-ToolboxCommandNames) {
$binary = Get-ToolboxBinaryPath -Name $commandName
$result = Invoke-NativeCapture -FilePath $binary -ArgumentList @('--version')
Assert-Condition ($result.ExitCode -eq 0) "$commandName --version failed with exit code $($result.ExitCode): $($result.Output)"
Assert-Condition ($result.Output -match [regex]::Escape($commandName)) "$commandName --version did not include command name: $($result.Output)"
}
}
function Test-ToolboxNoArgsContracts {
$stdinWaitingCommands = @(
'ison',
'isonl',
'zon',
'tonl',
'jsonlgrep',
'ctxpack',
'diagpick',
'hitsnip',
'logshape',
'recent',
'codeshape',
'snip',
'sysshape',
'toon'
)
foreach ($commandName in Get-ToolboxCommandNames) {
if ($commandName -in $stdinWaitingCommands) {
continue
}
$binary = Get-ToolboxBinaryPath -Name $commandName
$result = Invoke-NativeCapture -FilePath $binary
Assert-Condition ($result.Output.Length -lt 20000) "$commandName no-args output was unexpectedly large"
Assert-Condition ($result.Output -notmatch '(?i)panic|backtrace') "$commandName no-args output contained panic/backtrace text"
}
}
function Test-ToolboxInvalidFlagContracts {
foreach ($commandName in Get-ToolboxCommandNames) {
$binary = Get-ToolboxBinaryPath -Name $commandName
$result = Invoke-NativeCapture -FilePath $binary -ArgumentList @('--mercury-invalid-flag')
Assert-Condition ($result.ExitCode -ne 0) "$commandName accepted an invalid flag"
Assert-Condition ($result.Output.Length -gt 0) "$commandName invalid-flag diagnostic was empty"
Assert-Condition ($result.Output -notmatch '(?i)panic|backtrace') "$commandName invalid-flag output contained panic/backtrace text"
}
}
function Test-ToolboxStructuredOutputHelpContracts {
foreach ($commandName in Get-ToolboxCommandNames) {
$binary = Get-ToolboxBinaryPath -Name $commandName
$result = Invoke-NativeCapture -FilePath $binary -ArgumentList @('--help')
Assert-Condition ($result.ExitCode -eq 0) "$commandName --help failed with exit code $($result.ExitCode)"
Assert-Condition ($result.Output -match '--json|--format|--toon|JSON|TOON') "$commandName --help does not mention structured output controls"
}
}
function Test-ToolboxMalformedJsonlStdinContracts {
foreach ($probe in @(
@{ Name = 'cjson'; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'jsonshape'; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'toon'; Args = @('--input-format', 'jsonl') }
)) {
$binary = Get-ToolboxBinaryPath -Name $probe.Name
$result = "'{not-json}'" | & $binary @($probe.Args) 2>&1
$exitCode = $LASTEXITCODE
$text = (($result | ForEach-Object { [string]$_ }) -join "`n")
Assert-Condition ($exitCode -ne 0) "$($probe.Name) accepted malformed JSONL stdin"
Assert-Condition ($text.Length -gt 0) "$($probe.Name) malformed JSONL diagnostic was empty"
Assert-Condition ($text -notmatch '(?i)panic|backtrace') "$($probe.Name) malformed JSONL diagnostic contained panic/backtrace text"
}
}
function Write-TestPackageSha256Sums {
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[switch]$UseWindowsSeparators
)
$hashPath = Join-Path $PackageRoot 'SHA256SUMS.txt'
$lines = [System.Collections.Generic.List[string]]::new()
foreach ($file in (Get-ChildItem -LiteralPath $PackageRoot -Recurse -File | Sort-Object FullName)) {
if ($file.FullName -eq $hashPath) {
continue
}
$relativePath = [System.IO.Path]::GetRelativePath($PackageRoot, $file.FullName)
if (-not $UseWindowsSeparators) {
$relativePath = $relativePath.Replace('\', '/')
}
$hash = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
$lines.Add("$hash $relativePath")
}
Set-Content -LiteralPath $hashPath -Value $lines -Encoding utf8NoBOM
}
function New-TestPortablePackage {
param(
[Parameter(Mandatory = $true)]
[string]$PackageRoot,
[switch]$UseWindowsSeparators
)
$scriptsRoot = Join-Path $PackageRoot 'scripts'
$binRoot = Join-Path $PackageRoot 'bin'
$docsRoot = Join-Path $PackageRoot 'docs\ai'
$skillRoot = Join-Path $PackageRoot 'skills\mercury-toolbox'
$catalogRoot = Join-Path $skillRoot 'references'
New-Item -ItemType Directory -Force -Path $scriptsRoot, $binRoot, $docsRoot, $catalogRoot | Out-Null
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'install-package-toolbox.ps1') -Destination (Join-Path $scriptsRoot 'install-package-toolbox.ps1') -Force
Copy-Item -LiteralPath (Join-Path $PSScriptRoot 'toolbox-commands.ps1') -Destination (Join-Path $scriptsRoot 'toolbox-commands.ps1') -Force
foreach ($commandName in Get-ToolboxCommandNames) {
Set-Content -LiteralPath (Join-Path $binRoot "$commandName.exe") -Value "fake $commandName" -Encoding utf8NoBOM
}
Set-Content -LiteralPath (Join-Path $docsRoot 'mercury-toolbox-ai-prompt.md') -Value 'test prompt' -Encoding utf8NoBOM
Set-Content -LiteralPath (Join-Path $skillRoot 'SKILL.md') -Value 'test skill' -Encoding utf8NoBOM
Set-Content -LiteralPath (Join-Path $catalogRoot 'command-catalog.md') -Value 'test catalog' -Encoding utf8NoBOM
[ordered]@{
name = 'Mercury Toolbox'
commands = @(Get-ToolboxCommandNames)
} | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (Join-Path $PackageRoot 'mercury-toolbox-package.json') -Encoding utf8NoBOM
Write-TestPackageSha256Sums -PackageRoot $PackageRoot -UseWindowsSeparators:$UseWindowsSeparators
}
$workspaceRoot = Split-Path -Parent $PSScriptRoot
$targetProfile = Resolve-ToolboxProfileName -Configuration $Configuration
$script:BinaryRoot = Join-Path $workspaceRoot (Join-Path 'target' $targetProfile)
$dependencyRoot = Join-Path $script:BinaryRoot 'deps'
if (Test-Path -LiteralPath $dependencyRoot -PathType Container) {
$env:PATH = "$dependencyRoot$([System.IO.Path]::PathSeparator)$env:PATH"
}
$fixtureRoot = Join-Path $workspaceRoot 'fixtures'
$cargoPath = Get-RequiredCommandPath -Name 'cargo'
$batPath = Get-RequiredCommandPath -Name 'bat'
$fdPath = Get-RequiredCommandPath -Name 'fd'
$rgPath = Get-RequiredCommandPath -Name 'rg'
$jqPath = Get-RequiredCommandPath -Name 'jq'
$script:Results = [System.Collections.Generic.List[object]]::new()
$script:Failed = $false
Write-Host "Mercury Toolbox ecosystem check"
Write-Host "workspace: $workspaceRoot"
Write-Host "configuration: $Configuration"
Write-Host "binary root: $script:BinaryRoot"
if (-not $SkipBuild) {
Invoke-ToolboxBuild -CargoPath $cargoPath -Configuration $Configuration
}
$jsonlgrep = Get-ToolboxBinaryPath -Name 'jsonlgrep'
$jsonshape = Get-ToolboxBinaryPath -Name 'jsonshape'
$cjson = Get-ToolboxBinaryPath -Name 'cjson'
$recent = Get-ToolboxBinaryPath -Name 'recent'
$pathshadow = Get-ToolboxBinaryPath -Name 'pathshadow'
$portping = Get-ToolboxBinaryPath -Name 'portping'
$portunlock = Get-ToolboxBinaryPath -Name 'portunlock'
$msudo = Get-ToolboxBinaryPath -Name 'msudo'
$asmtype = Get-ToolboxBinaryPath -Name 'asmtype'
$asmmember = Get-ToolboxBinaryPath -Name 'asmmember'
$asmref = Get-ToolboxBinaryPath -Name 'asmref'
$binmeta = Get-ToolboxBinaryPath -Name 'binmeta'
$fileprobe = Get-ToolboxBinaryPath -Name 'fileprobe'
$outline = Get-ToolboxBinaryPath -Name 'outline'
$codeshape = Get-ToolboxBinaryPath -Name 'codeshape'
$snip = Get-ToolboxBinaryPath -Name 'snip'
$defsnip = Get-ToolboxBinaryPath -Name 'defsnip'
$chunkcat = Get-ToolboxBinaryPath -Name 'chunkcat'
$hitsnip = Get-ToolboxBinaryPath -Name 'hitsnip'
$diagpick = Get-ToolboxBinaryPath -Name 'diagpick'
$logshape = Get-ToolboxBinaryPath -Name 'logshape'
$stringscan = Get-ToolboxBinaryPath -Name 'stringscan'
$toon = Get-ToolboxBinaryPath -Name 'toon'
$csvshape = Get-ToolboxBinaryPath -Name 'csvshape'
$sqliteshape = Get-ToolboxBinaryPath -Name 'sqliteshape'
$sqlshape = Get-ToolboxBinaryPath -Name 'sqlshape'
$envdiff = Get-ToolboxBinaryPath -Name 'envdiff'
$proctree = Get-ToolboxBinaryPath -Name 'proctree'
$sysshape = Get-ToolboxBinaryPath -Name 'sysshape'
$unlock = Get-ToolboxBinaryPath -Name 'unlock'
Invoke-Check -Name 'toolbox:all-binaries-report-version' -Script {
Test-ToolboxBinaryVersions
"verified --version output for $((Get-ToolboxCommandNames).Count) toolbox binaries"
}
Invoke-Check -Name 'toolbox:all-binaries-no-args-contract' -Script {
Test-ToolboxNoArgsContracts
"verified bounded no-args behavior for non-stdin-waiting toolbox binaries"
}
Invoke-Check -Name 'toolbox:all-binaries-invalid-flag-contract' -Script {
Test-ToolboxInvalidFlagContracts
"verified invalid-flag diagnostics for $((Get-ToolboxCommandNames).Count) toolbox binaries"
}
Invoke-Check -Name 'toolbox:all-binaries-structured-output-help' -Script {
Test-ToolboxStructuredOutputHelpContracts
"verified structured-output help surface for $((Get-ToolboxCommandNames).Count) toolbox binaries"
}
Invoke-Check -Name 'toolbox:malformed-jsonl-stdin-contract' -Script {
Test-ToolboxMalformedJsonlStdinContracts
'verified malformed JSONL stdin fails closed for representative input-format commands'
}
Invoke-Check -Name 'toolbox:functional-toon-smokes' -Script {
$outputs = @(
(& $fileprobe (Join-Path $fixtureRoot 'reading\sample.rs') --toon),
(& $jsonshape (Join-Path $fixtureRoot 'jsonshape\events.jsonl') --input-format jsonl --toon),
(& $csvshape (Join-Path $fixtureRoot 'csvshape\sample.csv') --toon)
)
foreach ($output in $outputs) {
$text = (($output | ForEach-Object { [string]$_ }) -join "`n")
Assert-Condition ($text.Trim().Length -gt 0) 'expected non-empty representative TOON smoke output'
Assert-Condition ($text -notmatch '(?i)panic|backtrace') "TOON smoke output contained panic/backtrace text: $text"
}
'verified representative functional TOON smokes across file, JSONL, and CSV families'
}
Invoke-Check -Name 'cjson:stdin-jsonl-wrapper' -Script {
$wrapper = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'cjson\records.jsonl') |
& $cjson --input-format jsonl --sort-keys --json |
ConvertFrom-Json
Assert-Condition ($wrapper.format -eq 'jsonl') "expected cjson wrapper format jsonl, got $($wrapper.format)"
Assert-Condition ($wrapper.documents -eq 2) "expected 2 compacted documents, got $($wrapper.documents)"
Assert-Condition ($wrapper.text -match '"event":"login","ok":true') 'expected compact wrapper text to contain a sorted login record'
"wrapped $($wrapper.documents) compacted JSONL document(s) for PowerShell consumers"
}
Invoke-Check -Name 'jsonlgrep:stdin-json' -Script {
$matchRows = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'jsonl\events.jsonl') |
& $jsonlgrep 'level=error' --pick ts,msg --json |
ConvertFrom-Json
Assert-Condition (@($matchRows).Count -eq 2) "expected 2 error rows, got $(@($matchRows).Count)"
Assert-Condition ((@($matchRows)[0].msg) -eq 'failed login') 'expected first error message to be "failed login"'
"matched $(@($matchRows).Count) error rows through stdin + ConvertFrom-Json"
}
Invoke-Check -Name 'jsonshape:stdin-jq' -Script {
$summaryJson = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'jsonshape\events.jsonl') |
& $jsonshape --input-format jsonl --json
$documents = [int]($summaryJson | & $jqPath '-r' '.documents')
$pathCount = [int]($summaryJson | & $jqPath '-r' '.paths | length')
Assert-Condition ($documents -eq 2) "expected 2 JSON documents, got $documents"
Assert-Condition ($pathCount -ge 3) "expected at least 3 summarized paths, got $pathCount"
"summarized $documents documents and $pathCount paths through jq"
}
Invoke-Check -Name 'recent:json-pipeline' -Script {
$recentJson = & $recent --root $fixtureRoot --ext rs --limit 3 --json
$firstPath = $recentJson | & $jqPath '-r' '.[0].path'
$count = [int]($recentJson | & $jqPath '-r' 'length')
Assert-Condition ($count -ge 1) "expected recent to return at least one Rust file, got $count"
Assert-Condition ($firstPath.EndsWith('.rs')) "expected first recent path to end with .rs, got $firstPath"
"returned $count recent Rust paths and projected the first path with jq"
}
Invoke-Check -Name 'pathshadow:piped-command' -Script {
$pathMatches = 'cargo' | & $pathshadow --json | ConvertFrom-Json
Assert-Condition (@($pathMatches).Count -ge 1) 'expected at least one cargo path match'
Assert-Condition ((@($pathMatches)[0].command) -eq 'cargo') "expected first pathshadow command to be cargo"
"resolved $(@($pathMatches).Count) PATH candidates for cargo from piped input"
}
Invoke-Check -Name 'pathshadow:summary-shell-json' -Script {
$summary = & $pathshadow cargo --shell powershell --summary --json | ConvertFrom-Json
$first = @($summary)[0]
Assert-Condition (@($summary).Count -ge 1) 'expected at least one pathshadow summary row'
Assert-Condition ($first.command -eq 'cargo') "expected cargo summary row, got $($first.command)"
Assert-Condition ($first.total_matches -ge 1) "expected at least one cargo match, got $($first.total_matches)"
Assert-Condition ($first.shell_mode -eq 'powershell') "expected powershell shell mode, got $($first.shell_mode)"
"summarized cargo resolution with shell-aware metadata"
}
Invoke-Check -Name 'portping:loopback-http' -Script {
$port = Get-FreeTcpPort
$server = Start-LoopbackHttpServer -Port $port
try {
$target = "http://127.0.0.1:$port/health"
$results = $target | & $portping --method GET --expect-status 200 --json | ConvertFrom-Json
$probe = @($results)[0]
Assert-Condition ($probe.ok) 'expected loopback HTTP probe to succeed'
Assert-Condition ($probe.status_code -eq 200) "expected HTTP 200, got $($probe.status_code)"
"probed $target successfully in $($probe.total_ms) ms from piped target input"
}
finally {
Stop-LoopbackHttpServer -Server $server
}
}
Invoke-Check -Name 'portunlock:who-and-free' -Script {
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("portunlock-check-" + [System.Guid]::NewGuid())
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
$listener = $null
try {
$port = Get-FreeTcpPort
$listenerScript = Join-Path $tempDir 'listen.ps1'
Set-Content -LiteralPath $listenerScript -Encoding utf8NoBOM -Value @'
param([int]$Port)
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, $Port)
$listener.Start()
try {
Start-Sleep -Seconds 30
}
finally {
$listener.Stop()
}
'@
$listener = Start-Process -FilePath pwsh -ArgumentList @('-NoProfile', '-File', $listenerScript, $port) -PassThru -WindowStyle Hidden
Wait-Until -Description "TCP listener on port $port" -TimeoutMilliseconds 3000 -Probe {
Test-TcpPortListening -Port $port
}
$who = & $portunlock who --json --protocol tcp $port | ConvertFrom-Json
$free = & $portunlock free --json --force --protocol tcp $port | ConvertFrom-Json
$whoResult = @($who)[0]
$freeResult = @($free)[0]
Assert-Condition ($whoResult.port -eq $port) "expected who result for port $port, got $($whoResult.port)"
Assert-Condition (@($whoResult.initial_owners).Count -ge 1) 'expected portunlock who to surface at least one owner'
Assert-Condition ($freeResult.ok) 'expected portunlock free --force to succeed'
Assert-Condition (@($freeResult.final_owners).Count -eq 0) 'expected portunlock free to leave no remaining owners'
"identified and freed a loopback TCP owner on port $port"
}
finally {
if ($null -ne $listener -and -not $listener.HasExited) {
Stop-Process -Id $listener.Id -Force -ErrorAction SilentlyContinue
}
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:status-json-stable-fields' -Script {
$status = & $msudo status --json | ConvertFrom-Json
$propertyNames = @($status.PSObject.Properties.Name)
$stableFields = @('ok', 'host', 'supports_runas', 'is_elevated', 'session_id', 'active_session_id', 'can_current_user')
$shellPresets = @($status.shells | ForEach-Object { [string]$_.preset })
Assert-Condition ($propertyNames.Count -ge 4) "expected msudo status to expose multiple stable fields, got $($propertyNames.Count)"
foreach ($stableField in $stableFields) {
Assert-Condition ($propertyNames -contains $stableField) "expected msudo status JSON to expose stable field $stableField"
}
Assert-Condition ($status.ok -is [bool]) "expected msudo status ok to be boolean, got $($status.ok.GetType().FullName)"
Assert-Condition ($status.is_elevated -is [bool]) "expected msudo status is_elevated to be boolean, got $($status.is_elevated.GetType().FullName)"
Assert-Condition ($status.supports_runas -is [bool]) "expected msudo status supports_runas to be boolean, got $($status.supports_runas.GetType().FullName)"
Assert-Condition ($status.can_current_user -is [bool]) "expected msudo status can_current_user to be boolean, got $($status.can_current_user.GetType().FullName)"
Assert-Condition (-not [string]::IsNullOrWhiteSpace([string]$status.host)) 'expected msudo status host to be a non-empty string'
Assert-Condition ($shellPresets -contains 'powershell') "expected msudo status to expose the CLI shell preset name 'powershell', got: $($shellPresets -join ', ')"
Assert-Condition ($shellPresets -contains 'git-bash') "expected msudo status to expose the CLI shell preset name 'git-bash', got: $($shellPresets -join ', ')"
"reported stable msudo discovery fields ok=$($status.ok) host=$($status.host) supports_runas=$($status.supports_runas) is_elevated=$($status.is_elevated) session=$($status.session_id) active_session=$($status.active_session_id) without launching a privileged child process"
}
Invoke-Check -Name 'msudo:help-surfaces-same-console-honestly' -Script {
$helpText = (& $msudo --help 2>&1 | Out-String)
Assert-Condition ($LASTEXITCODE -eq 0) 'expected msudo --help to succeed'
Assert-Condition (
$helpText -match '--same-console'
) "expected msudo help to expose the same-console flag, got: $helpText"
Assert-Condition (
$helpText -match 'Reuse the current console'
) "expected msudo help to describe same-console foreground behavior, got: $helpText"
'reported msudo help with the explicit same-console surface'
}
Invoke-Check -Name 'msudo:current-user-native-cli' -Script {
$tempDir = Join-Path $env:TEMP ("msudo-current-user-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
try {
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$currentProcessPath = Join-Path $tempDir 'current-process.json'
$currentUserPath = Join-Path $tempDir 'current-user.json'
function New-EncodedPayload([string]$outputPath, [bool]$includeGroups) {
$escapedPath = $outputPath.Replace("'", "''")
$groupsExpr = if ($includeGroups) { "(whoami /groups | Out-String)" } else { '$null' }
$script = @'
$result = @{
user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
session = (Get-Process -Id $PID).SessionId
groups = __GROUPS__
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$script = $script.Replace('__GROUPS__', $groupsExpr).Replace('__OUTPUT_PATH__', $escapedPath)
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($script))
}
& $msudo --user current-process --wait -- powershell.exe -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $currentProcessPath -includeGroups:$false)
Assert-Condition ($LASTEXITCODE -eq 0) "expected current-process launch to succeed, got exit code $LASTEXITCODE"
& $msudo --user current-user --wait -- powershell.exe -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $currentUserPath -includeGroups:$false)
Assert-Condition ($LASTEXITCODE -eq 0) "expected current-user launch to succeed, got exit code $LASTEXITCODE"
$currentProcess = Get-Content $currentProcessPath -Raw | ConvertFrom-Json
$currentUserResult = Get-Content $currentUserPath -Raw | ConvertFrom-Json
Assert-Condition ($currentProcess.user -eq $currentUser) "expected current-process user $currentUser, got $($currentProcess.user)"
Assert-Condition ($currentUserResult.user -eq $currentUser) "expected current-user user $currentUser, got $($currentUserResult.user)"
"verified native current-process/current-user identities"
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:admin-system-ti-current-session' -Script {
$tempDir = Join-Path $env:TEMP ("msudo-ecosystem-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
try {
$currentSession = (Get-Process -Id $PID).SessionId
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$adminPath = Join-Path $tempDir 'admin.json'
$systemPath = Join-Path $tempDir 'system.json'
$tiPath = Join-Path $tempDir 'ti.json'
function New-EncodedPayload([string]$outputPath, [bool]$includeGroups) {
$escapedPath = $outputPath.Replace("'", "''")
$groupsExpr = if ($includeGroups) { "(whoami /groups | Out-String)" } else { '$null' }
$script = @'
$result = @{
user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
session = (Get-Process -Id $PID).SessionId
groups = __GROUPS__
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$script = $script.Replace('__GROUPS__', $groupsExpr).Replace('__OUTPUT_PATH__', $escapedPath)
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($script))
}
& $msudo --user admin --wait -- powershell -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $adminPath -includeGroups:$false)
Assert-Condition ($LASTEXITCODE -eq 0) "expected admin msudo one-shot to succeed, got exit code $LASTEXITCODE"
& $msudo --user system --dangerous --wait -- powershell -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $systemPath -includeGroups:$true)
Assert-Condition ($LASTEXITCODE -eq 0) "expected system msudo one-shot to succeed, got exit code $LASTEXITCODE"
& $msudo --user trustedinstaller --dangerous --wait -- powershell -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $tiPath -includeGroups:$true)
Assert-Condition ($LASTEXITCODE -eq 0) "expected trustedinstaller msudo one-shot to succeed, got exit code $LASTEXITCODE"
$admin = Get-Content $adminPath -Raw | ConvertFrom-Json
$system = Get-Content $systemPath -Raw | ConvertFrom-Json
$ti = Get-Content $tiPath -Raw | ConvertFrom-Json
Assert-Condition ($admin.session -eq $currentSession) "expected admin session $currentSession, got $($admin.session)"
Assert-Condition ($system.session -eq $currentSession) "expected system session $currentSession, got $($system.session)"
Assert-Condition ($ti.session -eq $currentSession) "expected trustedinstaller session $currentSession, got $($ti.session)"
Assert-Condition ($admin.user -eq $currentUser) "expected admin user $currentUser, got $($admin.user)"
Assert-Condition ($system.user -eq 'NT AUTHORITY\SYSTEM') "expected system user NT AUTHORITY\\SYSTEM, got $($system.user)"
Assert-Condition ($ti.groups -match 'NT SERVICE\\TrustedInstaller') "expected trustedinstaller token groups to include TrustedInstaller SID, got: $($ti.groups)"
"verified admin/system/trustedinstaller one-shot launches in session $currentSession with SYSTEM user and TrustedInstaller service SID evidence"
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:admin-integrity-and-privileges' -Script {
$tempDir = Join-Path $env:TEMP ("msudo-shape-" + [guid]::NewGuid().ToString('N'))
try {
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
$disableAllPath = Join-Path $tempDir 'disable-all.json'
$enableAllPath = Join-Path $tempDir 'enable-all.json'
$escapedDisableAllPath = $disableAllPath.Replace("'", "''")
$disableAllPayload = @'
$result = @{
groups = (whoami /groups | Out-String)
privileges = (whoami /priv | Out-String)
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$disableAllPayload = $disableAllPayload.Replace('__OUTPUT_PATH__', $escapedDisableAllPath)
$disableAllEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($disableAllPayload))
& $msudo --user admin --wait --integrity medium --privileges disable-all -- powershell.exe -NoProfile -EncodedCommand $disableAllEncoded
Assert-Condition ($LASTEXITCODE -eq 0) "expected admin integrity/privilege shaping launch to succeed, got exit code $LASTEXITCODE"
Assert-Condition (Test-Path -LiteralPath $disableAllPath) "expected admin integrity/privilege shaping launch to write $disableAllPath"
$disableAll = Get-Content $disableAllPath -Raw | ConvertFrom-Json
Assert-Condition ($disableAll.groups -match 'S-1-16-8192') "expected shaped admin token to include medium integrity SID S-1-16-8192, got: $($disableAll.groups)"
Assert-Condition ($disableAll.privileges -notmatch '(?m)(Enabled|已启用)\s*$') "expected shaped admin token privileges to be fully disabled, got: $($disableAll.privileges)"
$escapedEnableAllPath = $enableAllPath.Replace("'", "''")
$enableAllPayload = @'
$result = @{
privileges = (whoami /priv | Out-String)
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$enableAllPayload = $enableAllPayload.Replace('__OUTPUT_PATH__', $escapedEnableAllPath)
$enableAllEncoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($enableAllPayload))
& $msudo --user admin --wait --privileges enable-all -- powershell.exe -NoProfile -EncodedCommand $enableAllEncoded
Assert-Condition ($LASTEXITCODE -eq 0) "expected admin enable-all privilege launch to succeed, got exit code $LASTEXITCODE"
Assert-Condition (Test-Path -LiteralPath $enableAllPath) "expected admin enable-all privilege launch to write $enableAllPath"
$enableAll = Get-Content $enableAllPath -Raw | ConvertFrom-Json
Assert-Condition ($enableAll.privileges -match '(?m)(Enabled|已启用)\s*$') "expected enable-all privilege shaping to leave at least one enabled privilege, got: $($enableAll.privileges)"
'verified admin token shaping for medium integrity plus real enable-all and disable-all privilege modes'
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:shell-wrapped-system-and-ti' -Script {
$status = & $msudo status --json | ConvertFrom-Json
$availableShells = @{}
foreach ($shell in @($status.shells)) {
$availableShells[[string]$shell.preset] = [bool]$shell.available
}
$tempDir = Join-Path $env:TEMP ("msudo-shells-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
function New-EncodedPayload([string]$outputPath, [bool]$includeGroups) {
$escapedPath = $outputPath.Replace("'", "''")
$groupsExpr = if ($includeGroups) { "(whoami /groups | Out-String)" } else { '$null' }
$script = @'
$result = @{
user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
session = (Get-Process -Id $PID).SessionId
groups = __GROUPS__
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$script = $script.Replace('__GROUPS__', $groupsExpr).Replace('__OUTPUT_PATH__', $escapedPath)
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($script))
}
$systemCases = @(
@{ preset = 'cmd' }
@{ preset = 'powershell' }
@{ preset = 'pwsh' }
@{ preset = 'git-bash' }
)
$verified = [System.Collections.Generic.List[string]]::new()
try {
foreach ($case in $systemCases) {
if (-not $availableShells.ContainsKey($case.preset) -or -not $availableShells[$case.preset]) {
continue
}
$outputPath = Join-Path $tempDir ("system-" + $case.preset + '.json')
$argv = @(
'--user', 'system',
'--dangerous',
'--wait',
'--shell', $case.preset,
'--',
'powershell.exe',
'-NoProfile',
'-EncodedCommand',
(New-EncodedPayload -outputPath $outputPath -includeGroups:$true)
)
$output = (& $msudo @argv 2>&1 | Out-String)
$exitCode = $LASTEXITCODE
Assert-Condition ($exitCode -eq 0) "expected system shell-wrapped launch via $($case.preset) to succeed, got exit code $exitCode with output: $output"
Assert-Condition (Test-Path -LiteralPath $outputPath) "expected system shell-wrapped launch via $($case.preset) to write $outputPath"
$result = Get-Content $outputPath -Raw | ConvertFrom-Json
Assert-Condition ($result.user -eq 'NT AUTHORITY\SYSTEM') "expected system shell-wrapped launch via $($case.preset) to run as SYSTEM, got $($result.user)"
[void]$verified.Add($case.preset)
}
Assert-Condition ($verified.Count -ge 3) "expected to verify at least 3 available system shell presets, got $($verified.Count): $($verified -join ', ')"
$tiPreset = @('powershell', 'pwsh', 'cmd', 'git-bash') | Where-Object {
$availableShells.ContainsKey($_) -and $availableShells[$_]
} | Select-Object -First 1
Assert-Condition ($null -ne $tiPreset) 'expected at least one available shell preset to verify trustedinstaller shell wrapping'
$tiPath = Join-Path $tempDir ("trustedinstaller-" + $tiPreset + '.json')
$tiArgv = @(
'--user', 'trustedinstaller',
'--dangerous',
'--wait',
'--shell', $tiPreset,
'--',
'powershell.exe',
'-NoProfile',
'-EncodedCommand',
(New-EncodedPayload -outputPath $tiPath -includeGroups:$true)
)
$tiOutput = (& $msudo @tiArgv 2>&1 | Out-String)
$tiExitCode = $LASTEXITCODE
Assert-Condition ($tiExitCode -eq 0) "expected trustedinstaller shell-wrapped launch via $tiPreset to succeed, got exit code $tiExitCode with output: $tiOutput"
Assert-Condition (Test-Path -LiteralPath $tiPath) "expected trustedinstaller shell-wrapped launch via $tiPreset to write $tiPath"
$tiResult = Get-Content $tiPath -Raw | ConvertFrom-Json
Assert-Condition ($tiResult.user -eq 'NT AUTHORITY\SYSTEM') "expected trustedinstaller shell-wrapped launch via $tiPreset to report SYSTEM user, got $($tiResult.user)"
Assert-Condition ($tiResult.groups -match 'NT SERVICE\\TrustedInstaller') "expected trustedinstaller shell-wrapped launch via $tiPreset to include TrustedInstaller group evidence, got: $($tiResult.groups)"
"verified shell-wrapped system launches through $($verified -join ', ') and trustedinstaller group evidence through $tiPreset"
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:same-console-system-and-ti' -Script {
$status = & $msudo status --json | ConvertFrom-Json
$availableShells = @{ }
foreach ($shell in @($status.shells)) {
$availableShells[[string]$shell.preset] = [bool]$shell.available
}
$tempDir = Join-Path $env:TEMP ("msudo-same-console-" + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
function New-EncodedPayload([string]$outputPath, [bool]$includeGroups) {
$escapedPath = $outputPath.Replace("'", "''")
$groupsExpr = if ($includeGroups) { "(whoami /groups | Out-String)" } else { '$null' }
$script = @'
$result = @{
user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
session = (Get-Process -Id $PID).SessionId
groups = __GROUPS__
} | ConvertTo-Json -Compress
[System.IO.File]::WriteAllText('__OUTPUT_PATH__', $result)
'@
$script = $script.Replace('__GROUPS__', $groupsExpr).Replace('__OUTPUT_PATH__', $escapedPath)
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($script))
}
try {
$systemPath = Join-Path $tempDir 'system-direct.json'
& $msudo --same-console --user system --dangerous -- powershell.exe -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $systemPath -includeGroups:$true)
Assert-Condition ($LASTEXITCODE -eq 0) "expected same-console system launch to succeed, got exit code $LASTEXITCODE"
Assert-Condition (Test-Path -LiteralPath $systemPath) "expected same-console system launch to write $systemPath"
$system = Get-Content $systemPath -Raw | ConvertFrom-Json
Assert-Condition ($system.user -eq 'NT AUTHORITY\SYSTEM') "expected same-console system launch to run as SYSTEM, got $($system.user)"
$tiPath = Join-Path $tempDir 'trustedinstaller-direct.json'
& $msudo --same-console --user trustedinstaller --dangerous -- powershell.exe -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $tiPath -includeGroups:$true)
Assert-Condition ($LASTEXITCODE -eq 0) "expected same-console trustedinstaller launch to succeed, got exit code $LASTEXITCODE"
Assert-Condition (Test-Path -LiteralPath $tiPath) "expected same-console trustedinstaller launch to write $tiPath"
$ti = Get-Content $tiPath -Raw | ConvertFrom-Json
Assert-Condition ($ti.groups -match 'NT SERVICE\\TrustedInstaller') "expected same-console trustedinstaller launch to include TrustedInstaller group evidence, got: $($ti.groups)"
$sameConsolePreset = @('powershell', 'pwsh', 'cmd', 'git-bash') | Where-Object {
$availableShells.ContainsKey($_) -and $availableShells[$_]
} | Select-Object -First 1
Assert-Condition ($null -ne $sameConsolePreset) 'expected at least one available preset for same-console shell wrapping'
$wrappedPath = Join-Path $tempDir ("system-wrapped-" + $sameConsolePreset + '.json')
& $msudo --same-console --user system --dangerous --shell $sameConsolePreset -- powershell.exe -NoProfile -EncodedCommand (New-EncodedPayload -outputPath $wrappedPath -includeGroups:$true)
Assert-Condition ($LASTEXITCODE -eq 0) "expected same-console system shell wrapping via $sameConsolePreset to succeed, got exit code $LASTEXITCODE"
Assert-Condition (Test-Path -LiteralPath $wrappedPath) "expected same-console system shell wrapping via $sameConsolePreset to write $wrappedPath"
$wrapped = Get-Content $wrappedPath -Raw | ConvertFrom-Json
Assert-Condition ($wrapped.user -eq 'NT AUTHORITY\SYSTEM') "expected same-console system shell wrapping via $sameConsolePreset to run as SYSTEM, got $($wrapped.user)"
"verified same-console direct SYSTEM/TI launches and shell wrapping through $sameConsolePreset"
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'msudo:privileged-pipeline-passthrough' -Script {
$systemOutput = (& $msudo --same-console --user system --dangerous --wait -- cmd /d /c whoami | Out-String)
$systemExit = $LASTEXITCODE
Assert-Condition ($systemExit -eq 0) "expected piped same-console SYSTEM launch to exit 0, got $systemExit"
Assert-Condition ($systemOutput -match 'nt authority\\system') "expected piped same-console SYSTEM stdout to include identity, got: $systemOutput"
$tiOutput = (& $msudo --same-console --user trustedinstaller --dangerous --wait -- cmd /d /c whoami /groups | Out-String)
$tiExit = $LASTEXITCODE
Assert-Condition ($tiExit -eq 0) "expected piped same-console TrustedInstaller launch to exit 0, got $tiExit"
Assert-Condition ($tiOutput -match 'NT SERVICE\\TrustedInstaller') "expected piped same-console TrustedInstaller stdout to include service SID, got: $tiOutput"
$stderrOutput = (& $msudo --same-console --user system --dangerous --wait -- cmd /d /c 'echo msudo-stderr 1>&2' 2>&1 | Out-String)
$stderrExit = $LASTEXITCODE
Assert-Condition ($stderrExit -eq 0) "expected piped same-console SYSTEM stderr launch to exit 0, got $stderrExit"
Assert-Condition ($stderrOutput -match 'msudo-stderr') "expected piped same-console SYSTEM stderr to be replayed, got: $stderrOutput"
'verified privileged stdout/stderr passthrough through non-elevated relay pipelines'
}
Invoke-Check -Name 'asmtype:managed-json' -Script {
$types = & $asmtype (Join-Path $fixtureRoot 'managed\bin\GameAssembly.dll') --with-member-match 'StartProject|QueueVehicle' --json |
ConvertFrom-Json
Assert-Condition (@($types).Count -ge 2) "expected at least 2 managed types, got $(@($types).Count)"
Assert-Condition ((@($types) | Where-Object full_name -eq 'Game.UI.Windows.Windows.SpaceCraftConstructionWindow').Count -ge 1) 'expected SpaceCraftConstructionWindow in asmtype output'
Assert-Condition ((@($types) | Where-Object full_name -eq 'Game.UI.Windows.Windows.SpaceCraftProjectWindow').Count -ge 1) 'expected SpaceCraftProjectWindow in asmtype output'
"listed $(@($types).Count) matching managed types"
}
Invoke-Check -Name 'asmmember:jsonl-types' -Script {
$members = & $asmtype (Join-Path $fixtureRoot 'managed\bin\GameAssembly.dll') --intent unity-spacecraft-workflow --json |
& $asmmember --assembly (Join-Path $fixtureRoot 'managed\bin\GameAssembly.dll') --input-format jsonl --intent unity-spacecraft-workflow --json |
ConvertFrom-Json
Assert-Condition ((@($members) | Where-Object kind -eq 'method' | Where-Object name -eq 'StartProject').Count -ge 1) 'expected StartProject method in asmmember output'
Assert-Condition ((@($members) | Where-Object kind -eq 'method' | Where-Object name -eq 'FinishProject').Count -ge 1) 'expected FinishProject method in asmmember output'
Assert-Condition ((@($members) | Where-Object kind -eq 'property' | Where-Object name -eq 'ProjectName').Count -ge 1) 'expected ProjectName property in asmmember output'
"listed $(@($members).Count) managed members from JSONL type handoff"
}
Invoke-Check -Name 'asmref:resolved-reference' -Script {
$report = & $asmref (Join-Path $fixtureRoot 'managed\bin\GameAssembly.dll') --resolve-dir (Join-Path $fixtureRoot 'managed\bin') --json |
ConvertFrom-Json
$fixtureSupport = @($report.references) | Where-Object name -eq 'FixtureSupport' | Select-Object -First 1
Assert-Condition ($null -ne $fixtureSupport) 'expected FixtureSupport reference in asmref output'
Assert-Condition ($fixtureSupport.resolved) 'expected FixtureSupport reference to resolve'
"resolved managed references for $($report.assembly.assembly_name)"
}
Invoke-Check -Name 'binmeta:fd-path-stream' -Script {
$reports = & $fdPath '-a' '--max-depth' '1' '^jsonlgrep\.exe$' $script:BinaryRoot |
& $binmeta --input-format lines --json |
ConvertFrom-Json
$report = @($reports)[0]
Assert-Condition (@($reports).Count -eq 1) "expected one binmeta report, got $(@($reports).Count)"
Assert-Condition ($report.path.EndsWith('jsonlgrep.exe')) "expected binmeta path to end with jsonlgrep.exe, got $($report.path)"
"inspected $($report.path) from fd path output"
}
Invoke-Check -Name 'fileprobe:fd-path-stream' -Script {
$reports = & $fdPath '-a' '^sample\.rs$' (Join-Path $fixtureRoot 'reading') |
& $fileprobe --input-format lines --json |
ConvertFrom-Json
$report = @($reports)[0]
Assert-Condition (@($reports).Count -eq 1) "expected one fileprobe report, got $(@($reports).Count)"
Assert-Condition ($report.language_hint -eq 'rust') "expected fileprobe language hint rust, got $($report.language_hint)"
"classified $($report.path) as $($report.family) with language hint $($report.language_hint)"
}
Invoke-Check -Name 'outline:recent-path-chain' -Script {
$reports = & $recent --root $fixtureRoot --ext rs --limit 1 --json |
ConvertFrom-Json |
Select-Object -ExpandProperty path |
& $outline --input-format lines --json |
ConvertFrom-Json
$report = @($reports)[0]
Assert-Condition (@($reports).Count -eq 1) "expected one outline report, got $(@($reports).Count)"
Assert-Condition (@($report.items).Count -ge 3) "expected outline to report at least 3 items, got $(@($report.items).Count)"
"outlined $($report.path) with $(@($report.items).Count) structural items"
}
Invoke-Check -Name 'codeshape:polyglot-json' -Script {
$report = & $codeshape (Join-Path $fixtureRoot 'polyglot\repo') --json | ConvertFrom-Json
$appTs = @($report.files) | Where-Object path -like '*web\app.ts' | Select-Object -First 1
Assert-Condition (@($report.files).Count -ge 6) "expected at least 6 indexed files, got $(@($report.files).Count)"
Assert-Condition ($null -ne $appTs) 'expected codeshape to include web\app.ts'
Assert-Condition ((@($appTs.items) | Where-Object name -eq 'helper').Count -ge 1) 'expected codeshape to capture helper in app.ts'
"indexed $(@($report.files).Count) files and surfaced AST-backed declarations"
}
Invoke-Check -Name 'snip:stdin-text' -Script {
$snippet = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'reading\sample.rs') |
& $snip --around helper --context 0
Assert-Condition (($snippet -join "`n") -match 'helper') 'expected snip output to include helper'
"extracted helper-focused context from piped text"
}
Invoke-Check -Name 'defsnip:polyglot-json' -Script {
$symbolMatches = & $defsnip helper (Join-Path $fixtureRoot 'polyglot\repo') --json | ConvertFrom-Json
Assert-Condition (@($symbolMatches).Count -ge 4) "expected at least 4 helper definitions, got $(@($symbolMatches).Count)"
Assert-Condition ((@($symbolMatches) | Where-Object path -like '*web\app.ts').Count -ge 1) 'expected defsnip to include the TypeScript helper'
Assert-Condition ((@($symbolMatches) | Where-Object text -match 'helper').Count -ge 4) 'expected emitted definition text to contain helper bodies'
"extracted $(@($symbolMatches).Count) full definition block(s) by exact symbol name"
}
Invoke-Check -Name 'chunkcat:recent-path-chain' -Script {
$report = & $recent --root $fixtureRoot --ext rs --limit 1 --json |
ConvertFrom-Json |
Select-Object -ExpandProperty path |
& $chunkcat --input-format lines --max-lines 8 --chunk 0 --json |
ConvertFrom-Json
Assert-Condition ($report.chunk_count -ge 1) "expected at least one chunk, got $($report.chunk_count)"
Assert-Condition ($null -ne $report.selected_chunk) 'expected chunkcat to materialize the selected chunk'
"selected chunk 0 from $($report.path) with $($report.selected_chunk.line_count) lines"
}
Invoke-Check -Name 'hitsnip:rg-context' -Script {
$snippets = & $rgPath '-n' '-H' 'Mode::' (Join-Path $fixtureRoot 'reading\sample.rs') |
& $hitsnip --context 1 --json |
ConvertFrom-Json
Assert-Condition (@($snippets).Count -ge 1) 'expected at least one hitsnip snippet'
Assert-Condition ((@($snippets)[0].path) -like '*sample.rs') "expected hitsnip path to end with sample.rs, got $((@($snippets)[0].path))"
"expanded rg hits into $(@($snippets).Count) merged snippet(s)"
}
Invoke-Check -Name 'diagpick:stdin-log' -Script {
$diagnostics = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'diag\rust-errors.txt') |
& $diagpick --with-source --json |
ConvertFrom-Json
$diagnostic = @($diagnostics)[0]
Assert-Condition (@($diagnostics).Count -ge 1) 'expected at least one diagnostic'
Assert-Condition ($diagnostic.severity -eq 'error') "expected first diagnostic severity error, got $($diagnostic.severity)"
"picked $(@($diagnostics).Count) diagnostic(s) with source context"
}
Invoke-Check -Name 'logshape:stdin-log' -Script {
$report = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'logs\repetitive.log') |
& $logshape --json |
ConvertFrom-Json
Assert-Condition ($report.summary.group_count -ge 2) "expected at least 2 grouped patterns, got $($report.summary.group_count)"
Assert-Condition ((@($report.groups)[0].count) -ge 2) "expected top log group count >= 2, got $((@($report.groups)[0].count))"
"grouped repetitive logs into $($report.summary.group_count) templates"
}
Invoke-Check -Name 'stringscan:fd-path-stream' -Script {
$reports = & $fdPath '-a' '^stringscan-sample\.bin$' (Join-Path $fixtureRoot 'binaries') |
& $stringscan --input-format lines --json |
ConvertFrom-Json
$report = @($reports)[0]
Assert-Condition (@($reports).Count -eq 1) "expected one stringscan report, got $(@($reports).Count)"
Assert-Condition ((@($report.categories) -contains 'url')) 'expected stringscan categories to include url'
Assert-Condition ((@($report.categories) -contains 'bepinex')) 'expected stringscan categories to include bepinex'
"extracted $($report.match_count) classified strings from $($report.path)"
}
Invoke-Check -Name 'toon:json-wrapper-and-jsonl' -Script {
$wrapper = & $toon (Join-Path $fixtureRoot 'toon\config.json') --json | ConvertFrom-Json
$text = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'toon\records.jsonl') |
& $toon
Assert-Condition ($wrapper.format -eq 'toon') "expected toon JSON wrapper format to be toon, got $($wrapper.format)"
Assert-Condition (($text -join "`n") -match 'event') 'expected TOON text converted from JSONL to include the event field'
"rendered a TOON JSON wrapper and converted JSONL stdin to TOON text"
}
Invoke-Check -Name 'csvshape:stdin-tsv' -Script {
$summary = & $batPath '--style=plain' '--paging=never' (Join-Path $fixtureRoot 'csvshape\sample.tsv') |
& $csvshape --delimiter tab --json |
ConvertFrom-Json
Assert-Condition ($summary.delimiter -eq 'tab') "expected detected delimiter tab, got $($summary.delimiter)"
Assert-Condition ($summary.column_count -eq 4) "expected 4 columns, got $($summary.column_count)"
Assert-Condition ((@($summary.columns) | Where-Object name -eq 'score').Count -eq 1) 'expected score column in csvshape output'
"summarized TSV schema with $($summary.column_count) columns and delimiter $($summary.delimiter)"
}
Invoke-Check -Name 'sqliteshape:fixture-db' -Script {
$summary = & $sqliteshape (Join-Path $fixtureRoot 'sqliteshape\sample.db') --include-indexes --json |
ConvertFrom-Json
Assert-Condition ((@($summary.tables) | Where-Object name -eq 'users').Count -eq 1) 'expected users table in sqliteshape output'
Assert-Condition ((@($summary.tables) | Where-Object name -eq 'events').Count -eq 1) 'expected events table in sqliteshape output'
Assert-Condition ((@($summary.tables)[0].columns).Count -ge 2) 'expected at least two columns in first table summary'
"inspected SQLite fixture with $(@($summary.tables).Count) tables"
}
Invoke-Check -Name 'sqlshape:sqlite-fixture-db' -Script {
$summary = & $sqlshape --engine sqlite --url (Join-Path $fixtureRoot 'sqliteshape\sample.db') --json |
ConvertFrom-Json
Assert-Condition ($summary.engine -eq 'sqlite') "expected sqlite engine, got $($summary.engine)"
Assert-Condition (@($summary.schemas | Where-Object { $_ -eq 'main' }).Count -eq 1) 'expected main schema in sqlshape output'
Assert-Condition (@($summary.tables | Where-Object name -eq 'users').Count -eq 1) 'expected users table in sqlshape output'
Assert-Condition (@($summary.tables | Where-Object name -eq 'events').Count -eq 1) 'expected events table in sqlshape output'
"inspected normalized SQLite fixture with $(@($summary.tables).Count) tables"
}
Invoke-Check -Name 'envdiff:cmd-wrapper-json' -Script {
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("envdiff-check-" + [System.Guid]::NewGuid())
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
try {
$scriptPath = Join-Path $tempDir 'mutate-env.cmd'
Set-Content -LiteralPath $scriptPath -Encoding ascii -Value @'
@echo off
set TOOLBOX_STAGE=after
set TOOLBOX_REMOVE=
set PATH=%PATH%;C:\MercuryToolbox\TestBin
'@
$report = & $envdiff run --json --shell cmd -- $scriptPath | ConvertFrom-Json
Assert-Condition ((@($report.added) | Where-Object name -eq 'TOOLBOX_STAGE').Count -eq 1) 'expected TOOLBOX_STAGE in added variables'
Assert-Condition ((@($report.path_like_changes) | Where-Object name -eq 'Path' | Where-Object { @($_.added_segments) -contains 'C:\MercuryToolbox\TestBin' }).Count -eq 1) 'expected PATH segment delta in envdiff output'
"captured environment mutations and PATH segment deltas from cmd wrapper"
}
finally {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'proctree:run-json' -Script {
$report = & $proctree run --json -- pwsh -NoProfile -Command "Start-Process -FilePath pwsh -ArgumentList '-NoProfile','-Command','Start-Sleep -Seconds 2' -WindowStyle Hidden; Start-Sleep -Milliseconds 400" |
ConvertFrom-Json
Assert-Condition ($report.root_pid -gt 0) "expected rooted report pid, got $($report.root_pid)"
Assert-Condition (@($report.nodes).Count -ge 1) 'expected at least one process node from proctree run'
Assert-Condition ((@($report.nodes) | Where-Object pid -eq $report.root_pid).Count -eq 1) 'expected proctree report to include the root node'
"captured process tree with $(@($report.nodes).Count) node(s)"
}
Invoke-Check -Name 'sysshape:shell-json' -Script {
$report = & $sysshape --json --env none --group shell | ConvertFrom-Json
Assert-Condition (-not [string]::IsNullOrWhiteSpace($report.system.architecture)) 'expected sysshape to report a machine architecture'
Assert-Condition (-not [string]::IsNullOrWhiteSpace($report.system.default_shell)) 'expected sysshape to report a default shell hint'
Assert-Condition (@($report.tools).Count -ge 1) 'expected sysshape to detect at least one shell tool'
Assert-Condition (
((@($report.tools) | Where-Object name -eq 'pwsh').Count -ge 1) -or
((@($report.tools) | Where-Object name -eq 'powershell').Count -ge 1)
) 'expected sysshape shell inventory to include pwsh or powershell'
Assert-Condition ((@($report.tools) | Where-Object probe_status -eq 'ok').Count -ge 1) 'expected sysshape shell inventory to report probe_status'
"captured shell inventory with $(@($report.tools).Count) detected tool(s)"
}
Invoke-Check -Name 'unlock:who-and-delete' -Script {
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("unlock-check-" + [System.Guid]::NewGuid())
New-Item -ItemType Directory -Force -Path $tempDir | Out-Null
$locker = $null
try {
$lockedPath = Join-Path $tempDir 'locked.txt'
$lockerScript = Join-Path $tempDir 'locker.ps1'
Set-Content -LiteralPath $lockedPath -Encoding utf8NoBOM -Value 'busy'
Set-Content -LiteralPath $lockerScript -Encoding utf8NoBOM -Value @'
$path = '__LOCKED_PATH__'
$stream = [System.IO.File]::Open($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
try {
Start-Sleep -Seconds 30
}
finally {
$stream.Dispose()
}
'@.Replace('__LOCKED_PATH__', $lockedPath.Replace("'", "''"))
$locker = Start-Process -FilePath pwsh -ArgumentList @('-NoProfile', '-File', $lockerScript) -PassThru -WindowStyle Hidden
Wait-Until -Description "exclusive lock on $lockedPath" -TimeoutMilliseconds 3000 -Probe {
Test-ExclusiveFileLock -Path $lockedPath
}
$who = & $unlock who --json $lockedPath | ConvertFrom-Json
$delete = & $unlock delete --json --force $lockedPath | ConvertFrom-Json
$whoResult = @($who.results)[0]
$deleteResult = @($delete.results)[0]
Assert-Condition ($who.summary.results -eq 1) "expected single unlock who result, got $($who.summary.results)"
Assert-Condition (@($whoResult.initial_blockers).Count -ge 1 -or @($whoResult.final_blockers).Count -ge 1) 'expected unlock who to surface at least one blocker'
Assert-Condition ($delete.summary.results -eq 1) "expected single unlock delete result, got $($delete.summary.results)"
Assert-Condition ($deleteResult.ok) 'expected unlock delete --force to succeed'
Assert-Condition (-not (Test-Path -LiteralPath $lockedPath)) 'expected locked file to be deleted'
"identified blockers and deleted a locked file with staged escalation"
}
finally {
if ($null -ne $locker -and -not $locker.HasExited) {
Stop-Process -Id $locker.Id -Force -ErrorAction SilentlyContinue
}
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
Invoke-Check -Name 'toolbox:valid-jsonl-path-stream-smokes' -Script {
'covered by per-command path-stream:* JSONL path-record checks below'
}
function Invoke-JsonlPathRecordSmoke {
param(
[Parameter(Mandatory = $true)]
[string]$Name,
[Parameter(Mandatory = $true)]
[string]$InputPath,
[string[]]$ArgumentList = @(),
[int[]]$AcceptExitCodes = @(0)
)
Assert-Condition (Test-Path -LiteralPath $InputPath) "expected JSONL path-record smoke input to exist for ${Name}: $InputPath"
$commandPath = Get-ToolboxBinaryPath -Name $Name
$jsonl = @{ path = $InputPath } | ConvertTo-Json -Compress
$output = $jsonl | & $commandPath @ArgumentList 2>&1
$exitCode = $LASTEXITCODE
$text = (($output | ForEach-Object { [string]$_ }) -join "`n")
Assert-Condition (@($AcceptExitCodes) -contains $exitCode) "expected ${Name} JSONL path-record exit code in $($AcceptExitCodes -join ', '), got $exitCode with output: $text"
Assert-Condition (-not [string]::IsNullOrWhiteSpace($text)) "expected ${Name} JSONL path-record smoke to emit output"
Assert-Condition ($text -notmatch "(?i)thread '.*' panicked|stack backtrace|RUST_BACKTRACE|panic at") "expected ${Name} JSONL path-record smoke to avoid panic/backtrace text, got: $text"
"verified ${Name} JSONL path-record stdin using $InputPath"
}
$sampleRust = Join-Path $fixtureRoot 'reading\sample.rs'
$readingRoot = Join-Path $fixtureRoot 'reading'
$polyglotRoot = Join-Path $fixtureRoot 'polyglot\repo'
$managedBinRoot = Join-Path $fixtureRoot 'managed\bin'
$managedAssembly = Join-Path $managedBinRoot 'GameAssembly.dll'
$csvSample = Join-Path $fixtureRoot 'csvshape\sample.csv'
$sqliteSample = Join-Path $fixtureRoot 'sqliteshape\sample.db'
$stringSample = Join-Path $fixtureRoot 'binaries\stringscan-sample.bin'
$peSample = Join-Path $script:BinaryRoot 'portping.exe'
$kernel32 = Join-Path $env:WINDIR 'System32\kernel32.dll'
$driverRoot = Join-Path $env:WINDIR 'System32\drivers'
$driverSmokeSample = @(
'ndis.sys',
'disk.sys',
'afd.sys',
'tcpip.sys',
'afunix.sys'
) |
ForEach-Object { Join-Path $driverRoot $_ } |
Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } |
Select-Object -First 1
if ([string]::IsNullOrWhiteSpace($driverSmokeSample) -and (Test-Path -LiteralPath $driverRoot -PathType Container)) {
$driverSmokeSample = Get-ChildItem -LiteralPath $driverRoot -Filter '*.sys' -File |
Sort-Object Name |
Select-Object -First 1 -ExpandProperty FullName
}
Assert-Condition (-not [string]::IsNullOrWhiteSpace($driverSmokeSample)) "expected at least one system driver sample under $driverRoot for path-stream smoke coverage"
$pathStreamSmokes = @(
@{ Name = 'asmflow'; Path = $managedAssembly; Args = @('--input-format', 'jsonl', '--json', 'find', '--match', 'StartProject') },
@{ Name = 'asmref'; Path = $managedAssembly; Args = @('--input-format', 'jsonl', '--resolve-dir', $managedBinRoot, '--json') },
@{ Name = 'asmtype'; Path = $managedAssembly; Args = @('--input-format', 'jsonl', '--match', 'SpaceCraft', '--json') },
@{ Name = 'binmeta'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'chunkcat'; Path = $sampleRust; Args = @('--input-format', 'jsonl', '--max-lines', '8', '--chunk', '0', '--json') },
@{ Name = 'codeshape'; Path = $polyglotRoot; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'csvshape'; Path = $csvSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'defsnip'; Path = $polyglotRoot; Args = @('helper', '--input-format', 'jsonl', '--json') },
@{ Name = 'mhash'; Path = $sampleRust; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'drvshape'; Path = $driverSmokeSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'fileprobe'; Path = $sampleRust; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'ioctlscan'; Path = $driverSmokeSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'llvmnm'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'llvmobjdump'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json', '--raw-output-limit', '65536') },
@{ Name = 'llvmreadobj'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'outline'; Path = $sampleRust; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'pecalls'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json', '--api', 'GetProcAddress'); AcceptExitCodes = @(0, 1) },
@{ Name = 'peexports'; Path = $kernel32; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'peimports'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'pesig'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json', '--min-confidence', 'low') },
@{ Name = 'pestrrefs'; Path = $peSample; Args = @('--input-format', 'jsonl', '--json', '--contains', 'json') },
@{ Name = 'recent'; Path = $readingRoot; Args = @('--input-format', 'jsonl', '--json', '--limit', '1') },
@{ Name = 'refs'; Path = $polyglotRoot; Args = @('helper', '--input-format', 'jsonl', '--json') },
@{ Name = 'sqliteshape'; Path = $sqliteSample; Args = @('--input-format', 'jsonl', '--include-indexes', '--json') },
@{ Name = 'stringscan'; Path = $stringSample; Args = @('--input-format', 'jsonl', '--json') },
@{ Name = 'unityasset'; Path = $readingRoot; Args = @('--input-format', 'jsonl', '--json', 'index', '--allow-empty') },
@{ Name = 'unlock'; Path = $sampleRust; Args = @('--input-format', 'jsonl', '--json', 'who') }
)
foreach ($smoke in $pathStreamSmokes) {
Invoke-Check -Name "path-stream:$($smoke.Name):jsonl-path-record" -Script {
$acceptExitCodes = if ($smoke.ContainsKey('AcceptExitCodes')) { $smoke.AcceptExitCodes } else { @(0) }
Invoke-JsonlPathRecordSmoke -Name $smoke.Name -InputPath $smoke.Path -ArgumentList $smoke.Args -AcceptExitCodes $acceptExitCodes
}.GetNewClosure()
}
Invoke-Check -Name 'installer:temp-root-roundtrip' -Script {
$installRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolbox-" + [System.Guid]::NewGuid())
$codexHome = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxCodex-" + [System.Guid]::NewGuid())
$activeBinDir = Join-Path $installRoot 'current\bin'
try {
& (Join-Path $PSScriptRoot 'install-toolbox.ps1') `
-Configuration $Configuration `
-InstallRoot $installRoot `
-CodexHome $codexHome `
-NoPathUpdate `
-SkipBuild
if ($LASTEXITCODE -ne 0) {
throw "install-toolbox.ps1 exited with $LASTEXITCODE"
}
foreach ($commandName in Get-ToolboxCommandNames) {
$commandPath = Join-Path $activeBinDir "$commandName.exe"
Assert-Condition (Test-Path -LiteralPath $commandPath) "expected installed binary not found: $commandPath"
}
Assert-Condition (Test-Path -LiteralPath (Join-Path $activeBinDir 'duckdb.dll')) 'expected installed DuckDB runtime'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox\docs\ai\mercury-toolbox-ai-prompt.md')
) 'expected installed AI prompt in share directory'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox\skills\mercury-toolbox\SKILL.md')
) 'expected installed shared skill copy'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox\SKILL.md')
) 'expected installed Codex skill copy'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox\references\command-catalog.md')
) 'expected installed Codex command catalog'
& (Join-Path $PSScriptRoot 'uninstall-toolbox.ps1') -InstallRoot $installRoot -CodexHome $codexHome
if ($LASTEXITCODE -ne 0) {
throw "uninstall-toolbox.ps1 exited with $LASTEXITCODE"
}
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'current'))) 'expected uninstall to remove the current install junction'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'versions'))) 'expected uninstall to remove staged versions'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox'))) 'expected uninstall to remove the share directory'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox'))) 'expected uninstall to remove the Codex skill'
"installed and removed $((Get-ToolboxCommandNames).Count) commands in a temp root"
}
finally {
foreach ($path in @($installRoot, $codexHome)) {
if (Test-Path -LiteralPath $path) {
Remove-Item -LiteralPath $path -Recurse -Force
}
}
}
}
Invoke-Check -Name 'installer:refuses-unmanaged-codex-skill' -Script {
$installRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxGuard-" + [System.Guid]::NewGuid())
$codexHome = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxGuardCodex-" + [System.Guid]::NewGuid())
$skillRoot = Join-Path $codexHome 'skills\mercury-toolbox'
$sentinel = Join-Path $skillRoot 'USER-SKILL.txt'
try {
New-Item -ItemType Directory -Force -Path $skillRoot | Out-Null
Set-Content -LiteralPath $sentinel -Value 'user managed' -Encoding utf8NoBOM
$result = Invoke-PwshFile -FilePath (Join-Path $PSScriptRoot 'install-toolbox.ps1') -ArgumentList @(
'-Configuration', $Configuration,
'-InstallRoot', $installRoot,
'-CodexHome', $codexHome,
'-NoPathUpdate',
'-SkipBuild'
)
Assert-Condition ($result.ExitCode -ne 0) 'expected workspace installer to refuse unmanaged Codex skill directory'
Assert-Condition (Test-Path -LiteralPath $sentinel) 'expected unmanaged Codex skill sentinel to be preserved'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $skillRoot '.mercury-toolbox-owner.json'))) 'expected unmanaged skill to remain unmarked'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'current'))) 'expected direct installer to fail before activating an install'
'refused unmanaged direct-install Codex skill directory without overwriting it'
}
finally {
foreach ($path in @($installRoot, $codexHome)) {
if (Test-Path -LiteralPath $path) {
Remove-Item -LiteralPath $path -Recurse -Force
}
}
}
}
Invoke-Check -Name 'package:portable-roundtrip' -Script {
$packageOutputRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxPkg-" + [System.Guid]::NewGuid())
$extractRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxExtract-" + [System.Guid]::NewGuid())
$installRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxPortable-" + [System.Guid]::NewGuid())
$codexHome = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxPortableCodex-" + [System.Guid]::NewGuid())
$activeBinDir = Join-Path $installRoot 'current\bin'
try {
if ($SkipPromptGeneration) {
& (Join-Path $PSScriptRoot 'package-toolbox.ps1') `
-Configuration $Configuration `
-OutputRoot $packageOutputRoot `
-SkipBuild `
-SkipPromptGeneration
} else {
& (Join-Path $PSScriptRoot 'package-toolbox.ps1') `
-Configuration $Configuration `
-OutputRoot $packageOutputRoot `
-SkipBuild
}
if ($LASTEXITCODE -ne 0) {
throw "package-toolbox.ps1 exited with $LASTEXITCODE"
}
$zip = @(Get-ChildItem -LiteralPath $packageOutputRoot -Filter '*.zip' -File)
Assert-Condition ($zip.Count -eq 1) 'expected exactly one portable package archive'
Expand-Archive -LiteralPath $zip[0].FullName -DestinationPath $extractRoot -Force
$packageDirs = @(Get-ChildItem -LiteralPath $extractRoot -Directory)
Assert-Condition ($packageDirs.Count -eq 1) 'expected extracted archive to contain one top-level package directory'
$packageRoot = $packageDirs[0].FullName
$installScript = Join-Path $packageRoot 'scripts\install-package-toolbox.ps1'
$uninstallScript = Join-Path $packageRoot 'scripts\uninstall-package-toolbox.ps1'
Assert-Condition (Test-Path -LiteralPath $installScript) "expected package install script at $installScript"
Assert-Condition (Test-Path -LiteralPath $uninstallScript) "expected package uninstall script at $uninstallScript"
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'docs\ai\mercury-toolbox-ai-prompt.md')) 'expected packaged AI prompt'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'skills\mercury-toolbox\SKILL.md')) 'expected packaged Codex skill'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'skills\mercury-toolbox\references\command-catalog.md')) 'expected packaged command catalog'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'mercury-toolbox-package.json')) 'expected package manifest'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'SHA256SUMS.txt')) 'expected package checksums'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'START-HERE.txt')) 'expected portable package quickstart'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'LICENSE')) 'expected packaged license'
Assert-Condition (Test-Path -LiteralPath (Join-Path $packageRoot 'bin\duckdb.dll')) 'expected packaged DuckDB runtime'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $packageRoot 'README.md'))) 'expected portable package to omit README.md'
& $installScript -InstallRoot $installRoot -CodexHome $codexHome -NoPathUpdate
if ($LASTEXITCODE -ne 0) {
throw "install-package-toolbox.ps1 exited with $LASTEXITCODE"
}
foreach ($commandName in Get-ToolboxCommandNames) {
$commandPath = Join-Path $activeBinDir "$commandName.exe"
Assert-Condition (Test-Path -LiteralPath $commandPath) "expected packaged install binary not found: $commandPath"
}
Assert-Condition (Test-Path -LiteralPath (Join-Path $activeBinDir 'duckdb.dll')) 'expected packaged install DuckDB runtime'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox\docs\ai\mercury-toolbox-ai-prompt.md')
) 'expected installed AI prompt in portable share directory'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox\skills\mercury-toolbox\SKILL.md')
) 'expected installed shared skill in portable share directory'
Assert-Condition (
-not (Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox\README.md'))
) 'expected portable share directory to omit README.md'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox\SKILL.md')
) 'expected installed portable Codex skill'
Assert-Condition (
Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox\references\command-catalog.md')
) 'expected installed portable Codex command catalog'
& $uninstallScript -InstallRoot $installRoot -CodexHome $codexHome
if ($LASTEXITCODE -ne 0) {
throw "uninstall-package-toolbox.ps1 exited with $LASTEXITCODE"
}
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'current'))) 'expected portable uninstall to remove the current install junction'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'versions'))) 'expected portable uninstall to remove staged versions'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $installRoot 'share\mercury-toolbox'))) 'expected portable uninstall to remove the share directory'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $codexHome 'skills\mercury-toolbox'))) 'expected portable uninstall to remove the Codex skill'
'packaged, extracted, installed, and removed the portable toolbox bundle'
}
finally {
foreach ($path in @($packageOutputRoot, $extractRoot, $installRoot, $codexHome)) {
if (Test-Path -LiteralPath $path) {
Remove-Item -LiteralPath $path -Recurse -Force
}
}
}
}
Invoke-Check -Name 'package:refuses-traversal-package-name' -Script {
$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxTraversal-" + [System.Guid]::NewGuid())
$outputRoot = Join-Path $tempRoot 'out'
$escapeRoot = Join-Path $tempRoot 'escape'
$sentinel = Join-Path $escapeRoot 'sentinel.txt'
try {
New-Item -ItemType Directory -Force -Path $outputRoot, $escapeRoot | Out-Null
Set-Content -LiteralPath $sentinel -Value 'do not delete' -Encoding utf8NoBOM
$result = Invoke-PwshFile -FilePath (Join-Path $PSScriptRoot 'package-toolbox.ps1') -ArgumentList @(
'-Configuration', $Configuration,
'-OutputRoot', $outputRoot,
'-PackageName', '..\escape',
'-SkipBuild',
'-SkipPromptGeneration'
)
Assert-Condition ($result.ExitCode -ne 0) 'expected packager to reject a package name containing a path separator'
Assert-Condition (Test-Path -LiteralPath $sentinel) 'expected path outside OutputRoot to be preserved'
'rejected traversal package name without deleting outside OutputRoot'
}
finally {
if (Test-Path -LiteralPath $tempRoot) {
Remove-Item -LiteralPath $tempRoot -Recurse -Force
}
}
}
Invoke-Check -Name 'package-installer:verifies-sha256-before-install' -Script {
$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxHash-" + [System.Guid]::NewGuid())
$packageRoot = Join-Path $tempRoot 'package'
$installRoot = Join-Path $tempRoot 'install'
$codexHome = Join-Path $tempRoot 'codex'
try {
New-TestPortablePackage -PackageRoot $packageRoot -UseWindowsSeparators
Set-Content -LiteralPath (Join-Path $packageRoot 'docs\ai\mercury-toolbox-ai-prompt.md') -Value 'tampered prompt' -Encoding utf8NoBOM
$result = Invoke-PwshFile -FilePath (Join-Path $packageRoot 'scripts\install-package-toolbox.ps1') -ArgumentList @(
'-InstallRoot', $installRoot,
'-CodexHome', $codexHome,
'-NoPathUpdate'
)
Assert-Condition ($result.ExitCode -ne 0) 'expected portable installer to reject a mismatched SHA256SUMS entry'
Assert-Condition (-not (Test-Path -LiteralPath $installRoot)) 'expected hash failure before writing install root'
Assert-Condition (-not (Test-Path -LiteralPath $codexHome)) 'expected hash failure before writing Codex skill root'
'rejected tampered package content before installing'
}
finally {
if (Test-Path -LiteralPath $tempRoot) {
Remove-Item -LiteralPath $tempRoot -Recurse -Force
}
}
}
Invoke-Check -Name 'package-installer:requires-listed-sha256-file' -Script {
$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxHashMissing-" + [System.Guid]::NewGuid())
$packageRoot = Join-Path $tempRoot 'package'
$installRoot = Join-Path $tempRoot 'install'
try {
New-TestPortablePackage -PackageRoot $packageRoot -UseWindowsSeparators
Add-Content -LiteralPath (Join-Path $packageRoot 'SHA256SUMS.txt') -Value ('0' * 64 + ' docs\ai\missing.txt')
$result = Invoke-PwshFile -FilePath (Join-Path $packageRoot 'scripts\install-package-toolbox.ps1') -ArgumentList @(
'-InstallRoot', $installRoot,
'-NoCodexSkillInstall',
'-NoPathUpdate'
)
Assert-Condition ($result.ExitCode -ne 0) 'expected portable installer to reject a missing SHA256SUMS entry'
Assert-Condition (-not (Test-Path -LiteralPath $installRoot)) 'expected missing-file failure before writing install root'
'rejected package with a missing listed checksum file before installing'
}
finally {
if (Test-Path -LiteralPath $tempRoot) {
Remove-Item -LiteralPath $tempRoot -Recurse -Force
}
}
}
Invoke-Check -Name 'package-installer:refuses-unmanaged-codex-skill' -Script {
$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("MercuryToolboxPackageGuard-" + [System.Guid]::NewGuid())
$packageRoot = Join-Path $tempRoot 'package'
$installRoot = Join-Path $tempRoot 'install'
$codexHome = Join-Path $tempRoot 'codex'
$skillRoot = Join-Path $codexHome 'skills\mercury-toolbox'
$sentinel = Join-Path $skillRoot 'USER-SKILL.txt'
try {
New-TestPortablePackage -PackageRoot $packageRoot -UseWindowsSeparators
New-Item -ItemType Directory -Force -Path $skillRoot | Out-Null
Set-Content -LiteralPath $sentinel -Value 'user managed' -Encoding utf8NoBOM
$result = Invoke-PwshFile -FilePath (Join-Path $packageRoot 'scripts\install-package-toolbox.ps1') -ArgumentList @(
'-InstallRoot', $installRoot,
'-CodexHome', $codexHome,
'-NoPathUpdate'
)
Assert-Condition ($result.ExitCode -ne 0) 'expected portable installer to refuse unmanaged Codex skill directory'
Assert-Condition (Test-Path -LiteralPath $sentinel) 'expected unmanaged portable Codex skill sentinel to be preserved'
Assert-Condition (-not (Test-Path -LiteralPath (Join-Path $skillRoot '.mercury-toolbox-owner.json'))) 'expected unmanaged portable skill to remain unmarked'
Assert-Condition (-not (Test-Path -LiteralPath $installRoot)) 'expected portable installer to fail before writing install root'
'refused unmanaged portable Codex skill directory without overwriting it'
}
finally {
if (Test-Path -LiteralPath $tempRoot) {
Remove-Item -LiteralPath $tempRoot -Recurse -Force
}
}
}
Write-Host ''
$script:Results | Format-Table -AutoSize
if ($script:Failed) {
throw 'One or more ecosystem checks failed.'
}