param([Parameter(Mandatory = $true)][string]$PackageRoot) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $root = [IO.Path]::GetFullPath($PackageRoot) $start = Join-Path $root 'start-sense.bat' $stop = Join-Path $root 'stop-sense.bat' $port = [int]$env:SENSE_PORT $launcher = Start-Process -FilePath 'cmd.exe' -ArgumentList @('/d', '/c', "`"$start`" -SkipMigration") -WorkingDirectory (Split-Path $root -Parent) -WindowStyle Hidden -PassThru try { $ready = $false for ($attempt = 0; $attempt -lt 60; $attempt++) { $client = New-Object Net.Sockets.TcpClient try { $task = $client.ConnectAsync('127.0.0.1', $port) if ($task.Wait(500) -and $client.Connected) { $ready = $true; break } } catch {} finally { $client.Dispose() } Start-Sleep -Milliseconds 500 } if (-not $ready) { throw 'start-sense.bat did not open the configured HTTP port.' } & $stop if ($LASTEXITCODE -ne 0) { throw 'stop-sense.bat failed.' } Start-Sleep -Seconds 1 $probe = New-Object Net.Sockets.TcpClient try { $task = $probe.ConnectAsync('127.0.0.1', $port) if ($task.Wait(500) -and $probe.Connected) { throw 'Sense port is still open after stop-sense.bat.' } } catch [Net.Sockets.SocketException] {} finally { $probe.Dispose() } Write-Host 'Sense start/stop wrapper smoke passed from an external working directory.' } finally { if (-not $launcher.HasExited) { & taskkill.exe /PID $launcher.Id /T /F | Out-Null } }