* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix * fix: track native source modules with root-anchored runtime ignores
211 lines
8.2 KiB
PowerShell
211 lines
8.2 KiB
PowerShell
param(
|
|
[int]$StartupTimeoutSeconds = 180,
|
|
[ValidateSet('auto','fast','balanced','lowvram')]
|
|
[string]$Profile = 'auto'
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version 2.0
|
|
|
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
if ((Split-Path -Leaf $ScriptDir) -eq 'local-ai') {
|
|
$VendooDir = Split-Path -Parent $ScriptDir
|
|
} else {
|
|
$VendooDir = Split-Path -Parent $ScriptDir
|
|
}
|
|
$LocalAiDir = Join-Path $VendooDir 'local-ai'
|
|
$PortableRoot = Join-Path $LocalAiDir 'comfyui\ComfyUI_windows_portable'
|
|
$Python = Join-Path $PortableRoot 'python_embeded\python.exe'
|
|
$Main = Join-Path $PortableRoot 'ComfyUI\main.py'
|
|
$LogDir = Join-Path $VendooDir 'logs'
|
|
$StdoutLog = Join-Path $LogDir 'local-flux-runtime.log'
|
|
$StderrLog = Join-Path $LogDir 'local-flux-runtime-error.log'
|
|
$StateFile = Join-Path $LogDir 'local-flux-runtime-state.json'
|
|
$PidFile = Join-Path $LogDir 'local-flux-runtime.pid'
|
|
$ComfyUrl = 'http://127.0.0.1:8188'
|
|
$ComfyPort = 8188
|
|
|
|
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
|
$env:PYTHONUTF8 = '1'
|
|
$env:PYTHONIOENCODING = 'utf-8'
|
|
$env:NO_COLOR = '1'
|
|
|
|
function Write-Utf8NoBom([string]$Path, [string]$Content) {
|
|
$encoding = New-Object System.Text.UTF8Encoding($false)
|
|
[System.IO.File]::WriteAllText($Path, $Content, $encoding)
|
|
}
|
|
|
|
function Remove-Ansi([string]$Text) {
|
|
if ([string]::IsNullOrEmpty($Text)) { return '' }
|
|
$pattern = ([string][char]27) + '\\[[0-?]*[ -/]*[@-~]'
|
|
return [regex]::Replace($Text, $pattern, '')
|
|
}
|
|
|
|
function Read-Tail([string]$Path, [int]$Count = 16) {
|
|
if (-not (Test-Path -LiteralPath $Path)) { return @() }
|
|
try {
|
|
return @(Get-Content -LiteralPath $Path -Tail $Count -ErrorAction SilentlyContinue | ForEach-Object { Remove-Ansi ([string]$_) })
|
|
} catch { return @() }
|
|
}
|
|
|
|
function Save-RuntimeState([string]$Status, [string]$Message, [int]$ProcessId = 0, [int]$Elapsed = 0, [string]$ErrorMessage = '') {
|
|
$state = [ordered]@{
|
|
status = $Status
|
|
message = $Message
|
|
pid = if ($ProcessId -gt 0) { $ProcessId } else { $null }
|
|
port = $ComfyPort
|
|
url = $ComfyUrl
|
|
elapsed_seconds = $Elapsed
|
|
updated_at = (Get-Date).ToString('o')
|
|
stdout_log = $StdoutLog
|
|
stderr_log = $StderrLog
|
|
error = $ErrorMessage
|
|
stdout_tail = @(Read-Tail $StdoutLog 12)
|
|
stderr_tail = @(Read-Tail $StderrLog 16)
|
|
}
|
|
Write-Utf8NoBom -Path $StateFile -Content ($state | ConvertTo-Json -Depth 5)
|
|
if ($ProcessId -gt 0) { Write-Utf8NoBom -Path $PidFile -Content ([string]$ProcessId) }
|
|
}
|
|
|
|
|
|
function Test-ComfyFlag([string]$Flag) {
|
|
try {
|
|
$help = & $Python -s $Main --help 2>&1 | Out-String
|
|
return $help -match [regex]::Escape($Flag)
|
|
} catch { return $false }
|
|
}
|
|
|
|
function Get-NvidiaVramGb {
|
|
try {
|
|
$line = & nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>$null | Select-Object -First 1
|
|
if ($line -match '^\s*(\d+)') { return [math]::Round(([double]$Matches[1] / 1024), 1) }
|
|
} catch {}
|
|
return 0
|
|
}
|
|
|
|
function Get-PerformanceArguments([string]$RequestedProfile) {
|
|
$effective = $RequestedProfile
|
|
$vramGb = Get-NvidiaVramGb
|
|
if ($effective -eq 'auto') {
|
|
if ($vramGb -ge 12) { $effective = 'fast' }
|
|
elseif ($vramGb -gt 0 -and $vramGb -lt 8) { $effective = 'lowvram' }
|
|
else { $effective = 'balanced' }
|
|
}
|
|
$flags = New-Object System.Collections.Generic.List[string]
|
|
if ($effective -eq 'fast') {
|
|
$flags.Add('--highvram')
|
|
$flags.Add('--cache-classic')
|
|
if (Test-ComfyFlag '--fast') {
|
|
$flags.Add('--fast'); $flags.Add('fp16_accumulation'); $flags.Add('cublas_ops')
|
|
}
|
|
} elseif ($effective -eq 'lowvram') {
|
|
$flags.Add('--lowvram')
|
|
$flags.Add('--cache-none')
|
|
} else {
|
|
$flags.Add('--cache-classic')
|
|
}
|
|
return [pscustomobject]@{ profile = $effective; requested = $RequestedProfile; vram_gb = $vramGb; flags = @($flags) }
|
|
}
|
|
|
|
function Test-ComfyReady {
|
|
try {
|
|
$response = Invoke-WebRequest -UseBasicParsing -Uri "$ComfyUrl/system_stats" -TimeoutSec 4
|
|
return $response.StatusCode -ge 200 -and $response.StatusCode -lt 300
|
|
} catch { return $false }
|
|
}
|
|
|
|
function Get-ComfyProcess {
|
|
try {
|
|
$items = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
|
|
$_.CommandLine -and
|
|
$_.CommandLine -match 'ComfyUI[\\/]main\.py' -and
|
|
$_.CommandLine -match "--port\s+$ComfyPort"
|
|
} | Select-Object -First 1)
|
|
if ($items.Count -gt 0) { return $items[0] }
|
|
return $null
|
|
} catch { return $null }
|
|
}
|
|
|
|
try {
|
|
if (-not (Test-Path -LiteralPath $Python)) { throw "Embedded Python wurde nicht gefunden: $Python" }
|
|
if (-not (Test-Path -LiteralPath $Main)) { throw "ComfyUI main.py wurde nicht gefunden: $Main" }
|
|
|
|
if (Test-ComfyReady) {
|
|
$existingReady = Get-ComfyProcess
|
|
$readyPid = if ($existingReady) { [int]$existingReady.ProcessId } else { 0 }
|
|
Save-RuntimeState 'ready' 'FLUX / ComfyUI ist bereits aktiv und erreichbar.' $readyPid 0
|
|
exit 0
|
|
}
|
|
|
|
$existing = Get-ComfyProcess
|
|
$comfyPid = 0
|
|
$process = $null
|
|
if ($existing) {
|
|
$comfyPid = [int]$existing.ProcessId
|
|
Save-RuntimeState 'starting' 'Ein vorhandener ComfyUI-Prozess initialisiert noch.' $comfyPid 0
|
|
} else {
|
|
Remove-Item -LiteralPath $StdoutLog,$StderrLog -Force -ErrorAction SilentlyContinue
|
|
$performance = Get-PerformanceArguments $Profile
|
|
$arguments = @(
|
|
'-s', $Main,
|
|
'--listen', '127.0.0.1',
|
|
'--port', [string]$ComfyPort,
|
|
'--disable-auto-launch',
|
|
'--preview-method', 'none',
|
|
'--windows-standalone-build',
|
|
'--disable-all-custom-nodes',
|
|
'--log-stdout',
|
|
'--verbose', 'INFO'
|
|
)
|
|
foreach ($flag in $performance.flags) { $arguments += $flag }
|
|
$process = Start-Process -FilePath $Python -ArgumentList $arguments -WorkingDirectory $PortableRoot -WindowStyle Hidden -RedirectStandardOutput $StdoutLog -RedirectStandardError $StderrLog -PassThru
|
|
if (-not $process) { throw 'Der ComfyUI-Prozess konnte nicht gestartet werden.' }
|
|
$comfyPid = [int]$process.Id
|
|
Save-RuntimeState 'starting' ("FLUX / ComfyUI wird initialisiert · Profil: {0} · VRAM: {1} GB." -f $performance.profile, $performance.vram_gb) $comfyPid 0
|
|
}
|
|
|
|
$startedAt = Get-Date
|
|
while (((Get-Date) - $startedAt).TotalSeconds -lt $StartupTimeoutSeconds) {
|
|
Start-Sleep -Seconds 2
|
|
$elapsed = [int][math]::Round(((Get-Date) - $startedAt).TotalSeconds)
|
|
|
|
if (Test-ComfyReady) {
|
|
Save-RuntimeState 'ready' "FLUX / ComfyUI ist nach ${elapsed}s erreichbar." $comfyPid $elapsed
|
|
exit 0
|
|
}
|
|
|
|
$alive = $false
|
|
try { $alive = $null -ne (Get-Process -Id $comfyPid -ErrorAction SilentlyContinue) } catch {}
|
|
if (-not $alive) {
|
|
$stderrTail = (Read-Tail $StderrLog 24) -join "`n"
|
|
$stdoutTail = (Read-Tail $StdoutLog 24) -join "`n"
|
|
$detail = if ($stderrTail) { $stderrTail } elseif ($stdoutTail) { $stdoutTail } else { 'Der Prozess wurde ohne verwertbare Ausgabe beendet.' }
|
|
Save-RuntimeState 'failed' 'ComfyUI wurde während des Starts beendet.' $comfyPid $elapsed $detail
|
|
throw "ComfyUI wurde während des Starts beendet. $detail"
|
|
}
|
|
|
|
$tail = @((Read-Tail $StdoutLog 3) + (Read-Tail $StderrLog 3)) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
|
$lastLine = if ($tail.Count -gt 0) { [string]$tail[-1] } else { 'ComfyUI initialisiert Python, Datenbank und Modelle.' }
|
|
Save-RuntimeState 'starting' "Start läuft seit ${elapsed}s: $lastLine" $comfyPid $elapsed
|
|
}
|
|
|
|
$timeoutDetail = @((Read-Tail $StderrLog 24) + (Read-Tail $StdoutLog 24)) -join "`n"
|
|
Stop-Process -Id $comfyPid -Force -ErrorAction SilentlyContinue
|
|
Save-RuntimeState 'failed' "ComfyUI war nach ${StartupTimeoutSeconds}s noch nicht erreichbar und wurde beendet." $comfyPid $StartupTimeoutSeconds $timeoutDetail
|
|
throw "ComfyUI war nach ${StartupTimeoutSeconds}s noch nicht erreichbar und wurde beendet. Prüfe $StderrLog und $StdoutLog."
|
|
} catch {
|
|
$message = Remove-Ansi ([string]$_.Exception.Message)
|
|
$currentPid = 0
|
|
try { if (Test-Path -LiteralPath $PidFile) { $currentPid = [int](Get-Content -LiteralPath $PidFile -Raw).Trim() } } catch {}
|
|
$elapsed = 0
|
|
try {
|
|
if (Test-Path -LiteralPath $StateFile) {
|
|
$oldState = Get-Content -LiteralPath $StateFile -Raw | ConvertFrom-Json
|
|
$elapsed = [int]$oldState.elapsed_seconds
|
|
}
|
|
} catch {}
|
|
Save-RuntimeState 'failed' 'FLUX / ComfyUI konnte nicht gestartet werden.' $currentPid $elapsed $message
|
|
Write-Error $message
|
|
exit 1
|
|
}
|