현재 작업 상태 저장

This commit is contained in:
Yun Chan 2026-06-27 11:20:24 +09:00
parent 07cc67761e
commit 6bd91b0d5e
674 changed files with 8726 additions and 298 deletions

View file

@ -1,10 +1,10 @@
<#
<#
.SYNOPSIS
Vignette 로컬 개발 스택을 깔끔하게 ()기동한다: 엔진 게이트웨이(9099) + API(8000) + (5173).
.DESCRIPTION
- 기존(고아 포함) 프로세스를 커맨드라인 기준으로 정확히 정리한 새로 띄운다.
API는 --reload(watchfiles) 핫리로드 app/ 수정 자동 재기동(수동 재시작 불필요).
API는 Windows reloader 고아 프로세스/포트 잔류를 피하려고 단일 프로세스로 띄운다.
게이트웨이는 no-reload 유지(상주 claude -p 세션 보존; Windows에선 reload 자식 claude 고아화).
(vite) npm run dev 기본 HMR.
- API는 DB 미가용 in-memory degraded로 기동된다(Postgres 불필요). dev-login + seed 페르소나 활성.
@ -18,6 +18,7 @@
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\dev-up.ps1
#>
param(
[int]$ApiPort = 8000,
[switch]$NoGateway,
[switch]$NoWeb,
[switch]$NoDb
@ -58,6 +59,16 @@ function Stop-Stale([string]$pattern, [string]$label) {
}
}
function Stop-ListenerByPort([int]$port, [string]$label) {
Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty OwningProcess -Unique |
Where-Object { $_ -and $_ -ne 0 } |
ForEach-Object {
Write-Host (" stop {0,-8} PID {1} (:${port})" -f $label, $_)
Stop-Process -Id $_ -Force -ErrorAction SilentlyContinue
}
}
function Wait-Health([string]$url, [string]$label, [int]$timeoutSec = 40) {
$deadline = (Get-Date).AddSeconds($timeoutSec)
while ((Get-Date) -lt $deadline) {
@ -70,70 +81,112 @@ function Wait-Health([string]$url, [string]$label, [int]$timeoutSec = 40) {
}
function Ensure-DevDb {
# Docker Postgres(pgvector)를 apps/api/.env 의 DATABASE_URL 자격증명에 맞춰 보장한다.
# DB가 있어야 페르소나 source=database 가 되어 UI 회기 시작이 열린다(무DB면 degraded로 막힘).
docker ps *> $null 2>&1
if ($LASTEXITCODE -ne 0) { Write-Host " WARN Docker 데몬 미응답 — DB 없이 degraded(UI 세션 시작 제한). Docker Desktop 실행 필요."; return }
$running = docker ps --filter name=vignette-dev-db --format "{{.Names}}" 2>$null
if ("$running" -match 'vignette-dev-db') { Write-Host " OK db (vignette-dev-db 실행 중)"; return }
docker rm -f vignette-dev-db *> $null 2>&1
$envLines = Get-Content (Join-Path $api '.env')
$dbUrl = (($envLines | Where-Object { $_ -match '^DATABASE_URL=' }) -replace '^DATABASE_URL=','').Trim()
if ($dbUrl -notmatch 'postgresql://([^:]+):([^@]+)@[^:]+:([0-9]+)/(\S+)') { Write-Host " WARN DATABASE_URL 파싱 실패 — DB 스킵"; return }
$u=$Matches[1]; $p=$Matches[2]; $port=$Matches[3]; $db=$Matches[4]
$initPath = Join-Path $repo 'infra\db\init'
docker run -d --name vignette-dev-db -p "$port`:5432" -e POSTGRES_USER=$u -e POSTGRES_PASSWORD=$p -e POSTGRES_DB=$db -e APP_DB_USER=vignette_app -e APP_DB_PASSWORD=vignette_app -v "$initPath`:/docker-entrypoint-initdb.d:ro" pgvector/pgvector:pg16 *> $null
for ($i=0; $i -lt 24; $i++) {
Start-Sleep -Seconds 2
docker exec vignette-dev-db pg_isready -U $u *> $null 2>&1
if ($LASTEXITCODE -eq 0) { Write-Host " OK db (Postgres 준비됨, init 스키마 적용)"; Start-Sleep -Seconds 1; return }
if (!(Get-Command docker -ErrorAction SilentlyContinue)) {
Write-Host ' WARN docker CLI 없음 - DB 없이 degraded(UI 세션 시작 제한).'
return
}
Write-Host " WARN db 준비 타임아웃"
if (!(Test-Path -LiteralPath '\\.\pipe\dockerDesktopLinuxEngine')) {
Write-Host ' WARN Docker Desktop Linux engine 미실행 - DB 없이 degraded(UI 세션 시작 제한).'
return
}
docker ps 1>$null 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Host ' WARN Docker 데몬 미응답 - DB 없이 degraded(UI 세션 시작 제한). Docker Desktop 실행 필요.'
return
}
$running = docker ps --filter name=vignette-dev-db --format '{{.Names}}' 2>$null
if ($running -match 'vignette-dev-db') {
Write-Host ' OK db (vignette-dev-db 실행 중)'
return
}
docker rm -f vignette-dev-db 1>$null 2>$null
$envLines = Get-Content -LiteralPath (Join-Path $api '.env')
$dbUrl = (($envLines | Where-Object { $_ -match '^DATABASE_URL=' }) -replace '^DATABASE_URL=', '').Trim()
if ($dbUrl -notmatch 'postgresql://([^:]+):([^@]+)@[^:]+:([0-9]+)/(\S+)') {
Write-Host ' WARN DATABASE_URL 파싱 실패 - DB 스킵'
return
}
$u = $Matches[1]
$p = $Matches[2]
$port = $Matches[3]
$db = $Matches[4]
$initPath = Join-Path $repo 'infra\db\init'
$dockerArgs = @(
'run', '-d',
'--name', 'vignette-dev-db',
'-p', ('{0}:5432' -f $port),
'-e', ('POSTGRES_USER={0}' -f $u),
'-e', ('POSTGRES_PASSWORD={0}' -f $p),
'-e', ('POSTGRES_DB={0}' -f $db),
'-e', 'APP_DB_USER=vignette_app',
'-e', 'APP_DB_PASSWORD=vignette_app',
'-v', ('{0}:/docker-entrypoint-initdb.d:ro' -f $initPath),
'pgvector/pgvector:pg16'
)
& docker @dockerArgs 1>$null
for ($i = 0; $i -lt 24; $i++) {
Start-Sleep -Seconds 2
docker exec vignette-dev-db pg_isready -U $u 1>$null 2>$null
if ($LASTEXITCODE -eq 0) {
Write-Host ' OK db (Postgres 준비됨, init 스키마 적용)'
Start-Sleep -Seconds 1
return
}
}
Write-Host ' WARN db 준비 타임아웃'
}
Write-Host "[1/4] 기존 스택 정리..."
Write-Host '[1/4] 기존 스택 정리...'
Stop-Stale 'engine_gateway\.gateway' 'gateway'
Stop-Stale 'app\.main:app' 'api'
Stop-Stale 'vite' 'web'
Stop-Stale 'app\.main:app' 'api'
Stop-Stale 'vite' 'web'
Stop-ListenerByPort 9099 'gateway'
Stop-ListenerByPort $ApiPort 'api'
Stop-ListenerByPort 5173 'web'
Start-Sleep -Seconds 2
if (-not $NoDb) {
Write-Host "[DB] Postgres(pgvector) 보장..."
Write-Host '[DB] Postgres(pgvector) 보장...'
Ensure-DevDb
}
if (-not $NoGateway) {
Write-Host "[2/4] 엔진 게이트웨이 :9099 (claude_cli)..."
Start-Process -FilePath $Python `
-ArgumentList (@($PyPre) + @('-m','uvicorn','engine_gateway.gateway:app','--host','127.0.0.1','--port','9099')) `
-WorkingDirectory $api -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'gateway.out.log') `
-RedirectStandardError (Join-Path $logs 'gateway.err.log')
} else { Write-Host "[2/4] (게이트웨이 건너뜀)" }
Write-Host '[2/4] 엔진 게이트웨이 :9099 (claude_cli)...'
$gatewayArgs = @($PyPre) + @('-m', 'uvicorn', 'engine_gateway.gateway:app', '--host', '127.0.0.1', '--port', '9099')
Start-Process -FilePath $Python -ArgumentList $gatewayArgs -WorkingDirectory $api -WindowStyle Hidden -RedirectStandardOutput (Join-Path $logs 'gateway.out.log') -RedirectStandardError (Join-Path $logs 'gateway.err.log')
} else {
Write-Host '[2/4] (게이트웨이 건너뜀)'
}
Write-Host "[3/4] API :8000 (핫리로드 --reload, degraded in-memory OK, seed 페르소나)..."
Write-Host ('[3/4] API :{0} (단일 프로세스, degraded in-memory OK, seed 페르소나)...' -f $ApiPort)
$env:AUTO_SEED_PERSONAS = 'true'
$env:ALLOW_SEED_PERSONA_FALLBACK = 'true'
# --reload + --reload-dir app: app/ 하위 .py 저장 시 watchfiles 가 감지해 자동 재기동.
Start-Process -FilePath $Python `
-ArgumentList (@($PyPre) + @('-m','uvicorn','app.main:app','--host','127.0.0.1','--port','8000','--reload','--reload-dir','app')) `
-WorkingDirectory $api -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'api.out.log') `
-RedirectStandardError (Join-Path $logs 'api.err.log')
$env:VITE_API_PROXY_TARGET = "http://127.0.0.1:$ApiPort"
$apiArgs = @($PyPre) + @('-m', 'uvicorn', 'app.main:app', '--host', '127.0.0.1', '--port', "$ApiPort")
Start-Process -FilePath $Python -ArgumentList $apiArgs -WorkingDirectory $api -WindowStyle Hidden -RedirectStandardOutput (Join-Path $logs 'api.out.log') -RedirectStandardError (Join-Path $logs 'api.err.log')
if (-not $NoWeb) {
Write-Host "[4/4] 웹 vite :5173..."
Start-Process -FilePath 'cmd.exe' -ArgumentList '/c','npm run dev' `
-WorkingDirectory $web -WindowStyle Hidden `
-RedirectStandardOutput (Join-Path $logs 'web.out.log') `
-RedirectStandardError (Join-Path $logs 'web.err.log')
} else { Write-Host "[4/4] (웹 건너뜀)" }
Write-Host ('[4/4] 웹 vite :5173... API proxy {0}' -f $env:VITE_API_PROXY_TARGET)
Start-Process -FilePath 'cmd.exe' -ArgumentList @('/c', 'npm run dev -- --host 127.0.0.1 --port 5173') -WorkingDirectory $web -WindowStyle Hidden -RedirectStandardOutput (Join-Path $logs 'web.out.log') -RedirectStandardError (Join-Path $logs 'web.err.log')
} else {
Write-Host '[4/4] (웹 건너뜀)'
}
Start-Sleep -Seconds 3
Write-Host "`n헬스 체크:"
if (-not $NoGateway) { Wait-Health 'http://127.0.0.1:9099/health' 'gateway' | Out-Null }
Wait-Health 'http://127.0.0.1:8000/health' 'api' | Out-Null
if (-not $NoWeb) { Wait-Health 'http://localhost:5173/' 'web' | Out-Null }
Write-Host ''
Write-Host '헬스 체크:'
if (-not $NoGateway) {
Wait-Health 'http://127.0.0.1:9099/health' 'gateway' | Out-Null
}
Wait-Health "http://127.0.0.1:$ApiPort/health" 'api' | Out-Null
if (-not $NoWeb) {
Wait-Health 'http://localhost:5173/' 'web' | Out-Null
}
Write-Host "`n준비 완료. 진입점: http://localhost:5173 (로그인 페이지에서 dev-login)"
Write-Host "종료: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\dev-down.ps1"
Write-Host ''
Write-Host '준비 완료. 진입점: http://localhost:5173 (로그인 페이지에서 dev-login)'
Write-Host '종료: powershell -NoProfile -ExecutionPolicy Bypass -File scripts\dev-down.ps1'