Ctrl K
ComunidadGuías…

install/uninstall.ps1

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
param(
  [string]$InstallDir = "",
  [string]$ServiceName = "MetisAI",
  [switch]$KeepData,
  [switch]$RemoveData,
  [switch]$DryRun,
  [switch]$Yes
)
$ErrorActionPreference = "Stop"
if (-not $InstallDir) {
  try {
    $InstallDir = (Get-ItemProperty -LiteralPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$ServiceName" -ErrorAction Stop).InstallLocation
  } catch {}
}
if (-not $InstallDir) {
  $runKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
  foreach ($candidate in @($ServiceName, "MetisAI", "metis-ai")) {
    try {
      $existingRun = (Get-ItemProperty -LiteralPath $runKey -Name "$candidate-app" -ErrorAction Stop)."$candidate-app"
      if ($existingRun) {
        $cmdPath = [string]$existingRun.Trim().Trim('"')
        if (Test-Path -LiteralPath $cmdPath) {
          $InstallDir = Split-Path -Parent $cmdPath
          $ServiceName = $candidate
          break
        }
      }
    } catch {}
  }
}
if (-not $InstallDir) { throw "Metis AI is not installed (no startup entry for $ServiceName). Nothing to uninstall. Pass -InstallDir DIR if files are leftover." }
$manifestPath = Join-Path $InstallDir ".metis-ai-install.json"
$manifest = $null
if (Test-Path $manifestPath) {
  $manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
} else {
  $manifest = [pscustomobject]@{ serviceName = $ServiceName; dataDir = ""; installMethod = "native" }
}
if ([IO.Path]::GetFullPath($InstallDir).TrimEnd("\") -eq [IO.Path]::GetPathRoot([IO.Path]::GetFullPath($InstallDir)).TrimEnd("\")) {
  throw "Refusing to remove a filesystem root."
}
if (-not $Yes) {
  $answer = Read-Host "Remove Metis AI installation at $InstallDir? Type 'yes'"
  if ($answer -cne "yes") { Write-Host "Aborted."; exit 0 }
}
function Invoke-Step([scriptblock]$Action, [string]$Description) {
  if ($DryRun) { Write-Host "+ $Description" } else { & $Action }
}
function Test-InstallProcess($Process, [string[]]$Roots) {
  if ($Process.Name -notin @("node.exe", "esbuild.exe")) { return $false }
  foreach ($root in $Roots) {
    if (-not $root) { continue }
    $prefix = $root.TrimEnd('\') + '\'
    if ($Process.Name -eq "node.exe" -and $Process.CommandLine -and
        $Process.CommandLine.IndexOf($prefix, [StringComparison]::OrdinalIgnoreCase) -ge 0) { return $true }
    if ($Process.Name -eq "esbuild.exe" -and $Process.ExecutablePath -and
        $Process.ExecutablePath.StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase)) { return $true }
  }
  return $false
}

$runKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
$rootNorm = [IO.Path]::GetFullPath($InstallDir).TrimEnd("\")
$hostExe = Join-Path $InstallDir "MetisHost.exe"
$scriptHost = Join-Path $InstallDir "windows-script-host.mjs"
if (Test-Path -LiteralPath $scriptHost) {
  Invoke-Step {
    $node = (Get-Command node -ErrorAction Stop).Source
    & $node $scriptHost --stop
    if ($LASTEXITCODE -ne 0) { throw "Metis script host did not stop. Check host.log." }
  } "Stop Metis script background host"
}
$hostProcesses = @(Get-CimInstance Win32_Process -Filter "Name='MetisHost.exe'" -ErrorAction SilentlyContinue |
  Where-Object { $_.ExecutablePath -and [IO.Path]::GetFullPath($_.ExecutablePath) -eq [IO.Path]::GetFullPath($hostExe) })
if ($hostProcesses.Count) {
  Invoke-Step {
    Start-Process -FilePath $hostExe -ArgumentList "--stop" -Wait
    $deadline = [DateTime]::UtcNow.AddSeconds(15)
    do {
      $running = @(Get-CimInstance Win32_Process -Filter "Name='MetisHost.exe'" -ErrorAction SilentlyContinue |
        Where-Object { $_.ExecutablePath -and [IO.Path]::GetFullPath($_.ExecutablePath) -eq [IO.Path]::GetFullPath($hostExe) })
      if (-not $running.Count) { break }
      Start-Sleep -Milliseconds 250
    } while ([DateTime]::UtcNow -lt $deadline)
    foreach ($proc in $running) {
      Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
    }
    if (@(Get-CimInstance Win32_Process -Filter "Name='MetisHost.exe'" -ErrorAction SilentlyContinue |
      Where-Object { $_.ExecutablePath -and [IO.Path]::GetFullPath($_.ExecutablePath) -eq [IO.Path]::GetFullPath($hostExe) }).Count) {
      throw "Metis host did not stop. Check host.log."
    }
  } "Stop Metis background host"
}
Invoke-Step { Remove-ItemProperty -LiteralPath $runKey -Name "$($manifest.serviceName)-host" -ErrorAction SilentlyContinue } "Remove host startup entry"
Invoke-Step { Remove-Item -LiteralPath (Join-Path ([Environment]::GetFolderPath("Programs")) $manifest.serviceName) -Recurse -Force -ErrorAction SilentlyContinue } "Remove Metis Start menu entry"
Invoke-Step { Remove-Item -LiteralPath "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$($manifest.serviceName)" -Recurse -Force -ErrorAction SilentlyContinue } "Remove Metis application registration"
if ($manifest.installMethod -eq "docker") {
  Invoke-Step { Push-Location $InstallDir; docker compose down; Pop-Location } "docker compose down"
} else {
foreach ($suffix in @("app", "worker", "mcp")) {
  $task = "$($manifest.serviceName)-$suffix"
  Invoke-Step { Remove-ItemProperty -LiteralPath $runKey -Name $task -ErrorAction SilentlyContinue } "Remove startup entry $task"
  Invoke-Step { cmd.exe /c "schtasks /Delete /TN `"$task`" /F >nul 2>&1" } "Delete scheduled task $task"
}
$rootAliases = @($InstallDir, $rootNorm)
if ($manifest.installDir -and [IO.Path]::GetFullPath([string]$manifest.installDir).TrimEnd('\') -eq $rootNorm) {
  $rootAliases += [string]$manifest.installDir
}
Invoke-Step {
  Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
    Where-Object { Test-InstallProcess $_ $rootAliases } |
    ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
} "Stop running Metis node processes"
}
function Remove-Tree([string]$Path) {
  if (-not (Test-Path -LiteralPath $Path)) { return }
  $target = $Path
  if ($target -notlike "\\?\*") { $target = "\\?\$Path" }
  for ($i = 0; $i -lt 8; $i++) {
    cmd.exe /c "rmdir /s /q `"$target`"" | Out-Null
    if (-not (Test-Path -LiteralPath $Path)) { return }
    Start-Sleep -Seconds 2
  }
  throw "Failed to remove $Path"
}
$shouldKeepData = $KeepData -and -not $RemoveData
$keepStash = $null
$keepEnvStash = $null
if ($shouldKeepData) {
  $envFile = Join-Path $InstallDir ".env"
  if (Test-Path -LiteralPath $envFile) {
    $keepEnvStash = Join-Path (Split-Path -Parent $rootNorm) (".$(Split-Path -Leaf $rootNorm).metis-keep-env")
    Invoke-Step {
      Copy-Item -LiteralPath $envFile -Destination $keepEnvStash -Force
    } "Stash .env to $keepEnvStash"
  }
  $dataDir = [string]$manifest.dataDir
  if (-not $dataDir) {
    $envFile = Join-Path $InstallDir ".env"
    if (Test-Path -LiteralPath $envFile) {
      $line = Get-Content -LiteralPath $envFile | Where-Object { $_ -match '^CHAT_DATA_DIR=' } | Select-Object -First 1
      if ($line) { $dataDir = $line.Substring('CHAT_DATA_DIR='.Length).Trim().Trim('"') }
    }
  }
  if (-not $dataDir -and (Test-Path -LiteralPath (Join-Path $InstallDir "data"))) {
    $dataDir = Join-Path $InstallDir "data"
  }
  if ($dataDir -and (Test-Path -LiteralPath $dataDir)) {
    $dataFull = [IO.Path]::GetFullPath($dataDir).TrimEnd('\')
    if ($dataFull -ne $rootNorm -and $dataFull.StartsWith($rootNorm + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
      $keepStash = "$rootNorm.metis-keep-data"
      Invoke-Step {
        if (Test-Path -LiteralPath $keepStash) { Remove-Item -LiteralPath $keepStash -Recurse -Force }
        Move-Item -LiteralPath $dataDir -Destination $keepStash
      } "Stash nested data to $keepStash"
    }
  }
}
if (-not $shouldKeepData -and $manifest.dataDir -and ([IO.Path]::GetFullPath($manifest.dataDir) -ne [IO.Path]::GetFullPath($InstallDir))) {
  Invoke-Step { Remove-Tree ([IO.Path]::GetFullPath($manifest.dataDir)) } "Remove data directory"
}
Invoke-Step { Start-Sleep -Seconds 1; Remove-Tree $rootNorm } "Remove installation directory"
if ($keepStash -or $keepEnvStash) {
  Write-Host "Metis AI uninstalled. Data kept: $(if ($keepStash) { $keepStash } else { $shouldKeepData }); env kept: $(if ($keepEnvStash) { $keepEnvStash } else { 'none' })"
} else {
  Write-Host "Metis AI uninstalled. Data kept: $shouldKeepData"
}