222 lines
6.4 KiB
PowerShell
222 lines
6.4 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[ValidateNotNullOrEmpty()]
|
|
[string]$InstallRoot = (Join-Path $env:LOCALAPPDATA 'MercuryToolbox'),
|
|
[string]$CodexHome
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-StrictMode -Version Latest
|
|
|
|
. (Join-Path $PSScriptRoot 'toolbox-commands.ps1')
|
|
|
|
function Normalize-PathValue {
|
|
param(
|
|
[AllowNull()]
|
|
[string]$PathValue
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($PathValue)) {
|
|
return ''
|
|
}
|
|
|
|
return $PathValue.Replace('/', '\').Trim().TrimEnd('\').ToLowerInvariant()
|
|
}
|
|
|
|
function Split-PathEntries {
|
|
param(
|
|
[AllowNull()]
|
|
[string]$PathValue
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($PathValue)) {
|
|
return @()
|
|
}
|
|
|
|
return $PathValue.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries)
|
|
}
|
|
|
|
function Remove-PathEntry {
|
|
param(
|
|
[AllowNull()]
|
|
[string]$ExistingPath,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Entry
|
|
)
|
|
|
|
$normalizedEntry = Normalize-PathValue -PathValue $Entry
|
|
$remaining = [System.Collections.Generic.List[string]]::new()
|
|
$changed = $false
|
|
|
|
foreach ($existing in (Split-PathEntries -PathValue $ExistingPath)) {
|
|
if ((Normalize-PathValue -PathValue $existing) -eq $normalizedEntry) {
|
|
$changed = $true
|
|
continue
|
|
}
|
|
$remaining.Add($existing)
|
|
}
|
|
|
|
return @{
|
|
Changed = $changed
|
|
Value = ($remaining -join ';')
|
|
}
|
|
}
|
|
|
|
function Resolve-CodexHomePath {
|
|
param(
|
|
[AllowNull()]
|
|
[string]$ExplicitCodexHome
|
|
)
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($ExplicitCodexHome)) {
|
|
return $ExplicitCodexHome
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($env:CODEX_HOME)) {
|
|
return $env:CODEX_HOME
|
|
}
|
|
|
|
return (Join-Path $env:USERPROFILE '.codex')
|
|
}
|
|
|
|
function Test-CodexSkillOwnedByMercury {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$SkillRoot
|
|
)
|
|
|
|
$markerPath = Join-Path $SkillRoot '.mercury-toolbox-owner.json'
|
|
if (-not (Test-Path -LiteralPath $markerPath)) {
|
|
return $false
|
|
}
|
|
|
|
try {
|
|
$marker = Get-Content -LiteralPath $markerPath -Raw | ConvertFrom-Json
|
|
return [string]$marker.owner -eq 'mercury-toolbox'
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Assert-ExistingPathNotReparsePoint {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Description
|
|
)
|
|
|
|
$resolvedPath = [System.IO.Path]::GetFullPath($Path)
|
|
if (-not (Test-Path -LiteralPath $resolvedPath)) {
|
|
return
|
|
}
|
|
|
|
$item = Get-Item -LiteralPath $resolvedPath -Force
|
|
if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
|
|
throw "$Description must not be a reparse point: $resolvedPath"
|
|
}
|
|
}
|
|
|
|
function Assert-ExistingTreeHasNoReparsePoints {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Path,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Description
|
|
)
|
|
|
|
$resolvedPath = [System.IO.Path]::GetFullPath($Path)
|
|
if (-not (Test-Path -LiteralPath $resolvedPath)) {
|
|
return
|
|
}
|
|
|
|
$reparsePoints = @(Get-ChildItem -LiteralPath $resolvedPath -Force -Recurse -Attributes ReparsePoint)
|
|
if ($reparsePoints.Count -gt 0) {
|
|
throw "$Description must not contain reparse points: $($reparsePoints[0].FullName)"
|
|
}
|
|
}
|
|
|
|
$legacyBinDir = Join-Path $InstallRoot 'bin'
|
|
$currentRoot = Join-Path $InstallRoot 'current'
|
|
$activeBinDir = Join-Path $currentRoot 'bin'
|
|
$versionsRoot = Join-Path $InstallRoot 'versions'
|
|
$shareRoot = Join-Path $InstallRoot 'share\mercury-toolbox'
|
|
$codexSkillRoot = Join-Path (Resolve-CodexHomePath -ExplicitCodexHome $CodexHome) 'skills\mercury-toolbox'
|
|
$legacyCommandNames = @('context', 'waitfor')
|
|
|
|
Assert-ExistingPathNotReparsePoint -Path $InstallRoot -Description 'InstallRoot'
|
|
Assert-ExistingPathNotReparsePoint -Path $versionsRoot -Description 'versions root'
|
|
Assert-ExistingPathNotReparsePoint -Path $shareRoot -Description 'share root'
|
|
Assert-ExistingPathNotReparsePoint -Path $codexSkillRoot -Description 'Codex skill root'
|
|
Assert-ExistingTreeHasNoReparsePoints -Path $shareRoot -Description 'share root'
|
|
Assert-ExistingTreeHasNoReparsePoints -Path $codexSkillRoot -Description 'Codex skill root'
|
|
|
|
if (Test-Path -LiteralPath $legacyBinDir) {
|
|
foreach ($commandName in Get-ToolboxCommandNames) {
|
|
$commandPath = Join-Path $legacyBinDir "$commandName.exe"
|
|
if (Test-Path -LiteralPath $commandPath) {
|
|
Remove-Item -LiteralPath $commandPath -Force
|
|
}
|
|
}
|
|
|
|
foreach ($legacyName in $legacyCommandNames) {
|
|
$legacyPath = Join-Path $legacyBinDir "$legacyName.exe"
|
|
if (Test-Path -LiteralPath $legacyPath) {
|
|
Remove-Item -LiteralPath $legacyPath -Force
|
|
}
|
|
}
|
|
|
|
$remainingBinItems = @(Get-ChildItem -LiteralPath $legacyBinDir -Force)
|
|
if ($remainingBinItems.Count -eq 0) {
|
|
Remove-Item -LiteralPath $legacyBinDir -Force
|
|
}
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $currentRoot) {
|
|
Remove-Item -LiteralPath $currentRoot -Force
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $versionsRoot) {
|
|
Remove-Item -LiteralPath $versionsRoot -Recurse -Force
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $shareRoot) {
|
|
Remove-Item -LiteralPath $shareRoot -Recurse -Force
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $codexSkillRoot) {
|
|
if (Test-CodexSkillOwnedByMercury -SkillRoot $codexSkillRoot) {
|
|
Remove-Item -LiteralPath $codexSkillRoot -Recurse -Force
|
|
}
|
|
else {
|
|
Write-Warning "Skipping Codex skill removal because Mercury Toolbox does not own this skill copy: $codexSkillRoot"
|
|
}
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $InstallRoot) {
|
|
$remainingItems = @(Get-ChildItem -LiteralPath $InstallRoot -Force)
|
|
if ($remainingItems.Count -eq 0) {
|
|
Remove-Item -LiteralPath $InstallRoot -Force
|
|
}
|
|
}
|
|
|
|
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
foreach ($entry in @($legacyBinDir, $activeBinDir)) {
|
|
$pathUpdate = Remove-PathEntry -ExistingPath $userPath -Entry $entry
|
|
if ($pathUpdate.Changed) {
|
|
$userPath = $pathUpdate.Value
|
|
}
|
|
}
|
|
[Environment]::SetEnvironmentVariable('Path', $userPath, 'User')
|
|
|
|
foreach ($entry in @($legacyBinDir, $activeBinDir)) {
|
|
$sessionUpdate = Remove-PathEntry -ExistingPath $env:Path -Entry $entry
|
|
if ($sessionUpdate.Changed) {
|
|
$env:Path = $sessionUpdate.Value
|
|
}
|
|
}
|
|
|
|
Write-Host "Mercury Toolbox removed from $InstallRoot"
|
|
Write-Host "Removed Codex skill root: $codexSkillRoot"
|