param( [Parameter(Mandatory = $true)] [string]$Root ) $ErrorActionPreference = "Stop" $Root = $Root.Trim().Trim('"').TrimEnd("\") $Root = (Resolve-Path -LiteralPath $Root).Path $Venv = Join-Path $Root ".venv" $Python = Join-Path $Venv "Scripts\python.exe" $Pip = Join-Path $Venv "Scripts\pip.exe" $Frontend = Join-Path $Root "frontend" $Runtime = Join-Path $Root ".runtime" function Write-Step($Message) { Write-Host "" Write-Host $Message -ForegroundColor Cyan } function Test-Port([int]$Port) { $client = [Net.Sockets.TcpClient]::new() try { $client.Connect("127.0.0.1", $Port) return $true } catch { return $false } finally { $client.Close() } } function Find-FreePort([int]$StartPort) { $port = $StartPort while (Test-Port $port) { $port++ } return $port } function Get-LivePid([string]$PidFile) { if (!(Test-Path $PidFile)) { return $null } $raw = (Get-Content $PidFile -ErrorAction SilentlyContinue | Select-Object -First 1) if (!$raw) { return $null } $process = Get-Process -Id ([int]$raw) -ErrorAction SilentlyContinue if ($process) { return [int]$raw } Remove-Item $PidFile -Force -ErrorAction SilentlyContinue return $null } function Stop-Tree([int]$Id) { Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $Id } | ForEach-Object { Stop-Tree $_.ProcessId } Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue } function Invoke-Checked([scriptblock]$Command, [string]$ErrorMessage) { & $Command if ($LASTEXITCODE -ne 0) { throw $ErrorMessage } } Write-Host "" Write-Host "============================================" Write-Host " Knowledge Station - Plataforma completa" Write-Host "============================================" New-Item -ItemType Directory -Force -Path $Runtime | Out-Null $legacyPid = Join-Path $Runtime "server.pid" if (Test-Path $legacyPid) { $legacyRaw = Get-Content $legacyPid -ErrorAction SilentlyContinue | Select-Object -First 1 if ($legacyRaw) { Stop-Tree ([int]$legacyRaw) } Remove-Item $legacyPid -Force -ErrorAction SilentlyContinue } if (!(Test-Path $Python)) { Write-Step "Preparando Python por primera vez..." & py -m venv $Venv 2>$null if ($LASTEXITCODE -ne 0) { & python -m venv $Venv } if (!(Test-Path $Python)) { throw "No se pudo preparar Python. Instala Python 3.12 o superior." } } $EnvFile = Join-Path $Root ".env" $EnvExample = Join-Path $Root ".env.example" if (!(Test-Path $EnvFile)) { Write-Step "Creando configuracion local .env..." Copy-Item $EnvExample $EnvFile Write-Host "Agrega tus API keys en .env para audio, video u OCR." -ForegroundColor Yellow } Write-Step "Verificando dependencias de Python..." Invoke-Checked { & $Python -m pip install --upgrade pip --disable-pip-version-check | Out-Null } "No se pudo actualizar pip." Invoke-Checked { & $Pip install -r (Join-Path $Root "requirements.txt") } "No se pudieron instalar dependencias de Python." Write-Step "Verificando Node.js..." $node = Get-Command node -ErrorAction SilentlyContinue $npm = Get-Command npm.cmd -ErrorAction SilentlyContinue if (!$npm) { $npm = Get-Command npm -ErrorAction SilentlyContinue } if (!$node -or !$npm) { throw "Node.js LTS no esta instalado o npm no esta disponible. Instala Node.js desde https://nodejs.org/." } if (!(Test-Path (Join-Path $Frontend "node_modules"))) { Write-Step "Instalando dependencias del frontend. Esto puede tardar unos minutos..." Invoke-Checked { & $npm.Source install --prefix $Frontend } "No se pudieron instalar dependencias del frontend." } else { Write-Host "Dependencias del frontend encontradas." } $BackendPidFile = Join-Path $Runtime "backend.pid" $FrontendPidFile = Join-Path $Runtime "frontend.pid" $BackendPortFile = Join-Path $Runtime "backend.port" $FrontendPortFile = Join-Path $Runtime "frontend.port" Write-Step "Iniciando backend..." $backendPid = Get-LivePid $BackendPidFile if ($backendPid) { $backendPort = Get-Content $BackendPortFile -ErrorAction SilentlyContinue | Select-Object -First 1 if (!$backendPort) { $backendPort = 8000 } Write-Host "Backend ya estaba activo en puerto $backendPort." } else { $backendPort = Find-FreePort 8000 $backend = Start-Process -FilePath $Python -ArgumentList @("-m", "uvicorn", "backend.app.main:app", "--host", "127.0.0.1", "--port", [string]$backendPort, "--reload") -WorkingDirectory $Root -PassThru Set-Content $BackendPidFile $backend.Id Set-Content $BackendPortFile $backendPort Write-Host "Backend iniciado en puerto $backendPort." } $ApiUrl = "http://127.0.0.1:$backendPort" Set-Content (Join-Path $Frontend ".env.local") "NEXT_PUBLIC_API_URL=$ApiUrl" Write-Step "Iniciando frontend..." $frontendPid = Get-LivePid $FrontendPidFile if ($frontendPid) { $frontendPort = Get-Content $FrontendPortFile -ErrorAction SilentlyContinue | Select-Object -First 1 if (!$frontendPort) { $frontendPort = 3000 } Write-Host "Frontend ya estaba activo en puerto $frontendPort." } else { $frontendPort = Find-FreePort 3000 $nextDir = Join-Path $Frontend ".next" if (Test-Path $nextDir) { Get-ChildItem $nextDir -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { try { $_.Attributes = "Normal" } catch {} } Remove-Item $nextDir -Recurse -Force -ErrorAction SilentlyContinue } $frontend = Start-Process -FilePath $npm.Source -ArgumentList @("run", "dev", "--", "-p", [string]$frontendPort) -WorkingDirectory $Frontend -PassThru Set-Content $FrontendPidFile $frontend.Id Set-Content $FrontendPortFile $frontendPort Write-Host "Frontend iniciado en puerto $frontendPort." } $AppUrl = "http://127.0.0.1:$frontendPort" Set-Content (Join-Path $Runtime "last.url") $AppUrl Start-Sleep -Seconds 4 Write-Host "" Write-Host "Plataforma abierta:" -ForegroundColor Green Write-Host "Frontend: $AppUrl" Write-Host "API: $ApiUrl" Write-Host "" Start-Process $AppUrl