vignette/scripts/start-nas-preview-engine.ps1
Yun Chan 16e791e044 G0~G8 성과·동맹 측정 OS 작업 일괄 고정
8월 7일까지 워킹트리에만 남아 있던 미커밋 작업을 커밋한다. 여러 사본
폴더(worktree·clone)에 흩어져 있던 중간 스냅샷을 정리하기 전에 원본을
git 이력으로 고정하는 것이 목적이다.

- contracts/routes/services: measurement, outcome_trajectory, rupture_repair,
  deliberate_practice, calibration_transfer, supervision_research,
  multimodal_alliance, continuous_improvement 계열 신규 모듈과 테스트
- infra/db/init: 07~16 마이그레이션(측정 기반~calibration transfer 실행)
- apps/web: 세션 리뷰 카드·관리 화면·E2E 스펙 추가
- docs/ops: G0~G8 라이브 통합·배포·롤백 증거 문서와 evidence JSON/PNG
- scripts: smoke·ledger·릴리스 에이전트·NAS 프리뷰 운영 스크립트

engine.public 로그 .bak과 apps/web/test-results 산출물은 커밋에서 제외했다.
2026-08-08 01:30:53 +09:00

94 lines
3.1 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string]$EnvFile,
[string]$Workspace = "D:\workspace\vignette",
[string]$ListenAddress = "0.0.0.0",
[int]$Port = 9100,
[string]$Python = "$env:LOCALAPPDATA\Programs\Python\Python311\python.exe"
)
$ErrorActionPreference = "Stop"
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
function Get-EnvValue {
param([string]$Path, [string]$Key)
foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) {
if ($line -match "^\s*$([Regex]::Escape($Key))\s*=(.*)$") {
return $Matches[1].Trim().Trim('"').Trim("'")
}
}
return ""
}
if (!(Test-Path -LiteralPath $EnvFile)) {
throw "NAS preview env file not found: $EnvFile"
}
if (!(Test-Path -LiteralPath $Python)) {
throw "Python 3.11 not found: $Python"
}
$apiDir = Join-Path $Workspace "apps\api"
if (!(Test-Path -LiteralPath $apiDir)) {
throw "API directory not found: $apiDir"
}
$secret = Get-EnvValue -Path $EnvFile -Key "ENGINE_GATEWAY_SHARED_SECRET"
if ($secret.Length -lt 32 -or $secret.ToLowerInvariant().StartsWith("change-me")) {
throw "ENGINE_GATEWAY_SHARED_SECRET must be a non-placeholder value of at least 32 characters"
}
$existing = Get-CimInstance Win32_Process |
Where-Object {
$_.CommandLine -and
$_.CommandLine -like "*uvicorn engine_gateway.gateway:app*" -and
$_.CommandLine -like "*--port $Port*"
}
if ($existing) {
throw "An engine gateway is already running on the requested preview port $Port"
}
$logDir = Join-Path $env:LOCALAPPDATA "Temp\vignette-nas-preview"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
$stamp = [DateTime]::UtcNow.ToString("yyyyMMddHHmmss")
$outLog = Join-Path $logDir "engine-$Port-$stamp.out.log"
$errLog = Join-Path $logDir "engine-$Port-$stamp.err.log"
$env:ENGINE_GATEWAY_SHARED_SECRET = $secret
$env:PYTHONUTF8 = "1"
$process = Start-Process -WindowStyle Hidden -FilePath $Python `
-ArgumentList @("-X", "utf8", "-m", "uvicorn", "engine_gateway.gateway:app", "--host", $ListenAddress, "--port", "$Port") `
-WorkingDirectory $apiDir `
-RedirectStandardOutput $outLog `
-RedirectStandardError $errLog `
-PassThru
Remove-Item Env:ENGINE_GATEWAY_SHARED_SECRET
$deadline = (Get-Date).AddSeconds(30)
$health = $null
do {
Start-Sleep -Milliseconds 500
if ($process.HasExited) {
$detail = if (Test-Path -LiteralPath $errLog) {
(Get-Content -LiteralPath $errLog -Encoding UTF8 -Tail 40) -join "`n"
} else {
"no stderr log"
}
throw "Preview engine gateway exited with $($process.ExitCode): $detail"
}
try {
$health = Invoke-RestMethod -Uri "http://127.0.0.1:$Port/health" -TimeoutSec 2
} catch {
$health = $null
}
} while ($null -eq $health -and (Get-Date) -lt $deadline)
if ($null -eq $health -or !$health.ok) {
throw "Preview engine gateway health timed out on port $Port"
}
Write-Output "Preview engine gateway PID=$($process.Id) port=$Port"
Write-Output "Health=$($health | ConvertTo-Json -Compress)"
Write-Output "SecretValueEmitted=false"
Write-Output "StdoutLog=$outLog"
Write-Output "StderrLog=$errLog"