[CmdletBinding()] param( [string]$ProjectRoot, [ValidateSet('Official', 'China')][string]$Mirror, [switch]$Help, [switch]$Staged ) $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = New-Object Text.UTF8Encoding($false) $env:PYTHONUTF8 = '1' $env:PYTHONIOENCODING = 'utf-8' if ($Help) { Write-Host '用法:update.bat 或 update-cn.bat [-Help]' Write-Host '升级会终止本项目的 ARTEMIS 服务、MCP 和任务进程;保留配置、密钥与历史资料。' Write-Host '随后快进更新源码,同步依赖、网页构建和 Codex MCP;完成后请重新启动并重载 MCP。' exit 0 } if (-not $ProjectRoot) { $ProjectRoot = Split-Path $PSScriptRoot -Parent } $ProjectRoot = (Resolve-Path -LiteralPath $ProjectRoot).Path . (Join-Path $PSScriptRoot 'installation_lock.ps1') if (-not $Staged) { $lock = $null $stage = $null $stageCreated = $false $code = 1 try { $lock = Enter-ArtemisInstallationLock -ProjectRoot $ProjectRoot # Every program used before the merge is frozen outside the checkout. $stage = Join-Path ([IO.Path]::GetTempPath()) ('artemis-upgrade-' + [Guid]::NewGuid().ToString('N')) $fullStage = [IO.Path]::GetFullPath($stage) $rootPrefix = [IO.Path]::GetFullPath($ProjectRoot).TrimEnd('\') + '\' if ($fullStage.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase)) { throw 'TEMP must be outside the project directory.' } New-Item -ItemType Directory -Path $stage | Out-Null $stageCreated = $true foreach ($name in @('upgrade.ps1', 'installation_lock.ps1', 'upgrade_processes.ps1', 'update_installation.py')) { Copy-Item -LiteralPath (Join-Path $PSScriptRoot $name) -Destination (Join-Path $stage $name) } $forward = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', (Join-Path $stage 'upgrade.ps1'), '-Staged', '-ProjectRoot', $ProjectRoot) if ($Mirror) { $forward += @('-Mirror', $Mirror) } & powershell.exe @forward $code = $LASTEXITCODE } catch { Write-Host "[升级失败] $_" -ForegroundColor Red } finally { # Only remove the unique, validated staging directory created above. if ($stageCreated -and (Test-Path -LiteralPath $stage)) { Remove-Item -LiteralPath $stage -Recurse -Force } Exit-ArtemisInstallationLock -Mutex $lock } exit $code } try { Set-Location -LiteralPath $PSScriptRoot $python = Join-Path $ProjectRoot '.venv\Scripts\python.exe' if (-not (Test-Path -LiteralPath $python)) { throw '尚未安装项目 Python 环境;请先运行 start.bat 或 start-cn.bat。' } if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw '未安装 Git,无法升级。' } if (-not (Test-Path -LiteralPath (Join-Path $ProjectRoot '.git'))) { throw '此目录不含 Git 信息;请改用 Git 克隆或新版发布源码包。' } # Refuse dirty checkouts before terminating the user's running processes. $localChanges = & git -C $ProjectRoot status --porcelain 2>$null if ($LASTEXITCODE -ne 0) { throw '无法检查 Git 工作区。' } if ($localChanges) { throw '发现本地修改;请先提交或备份,未终止任何任务。' } Write-Host '======================================================' Write-Host 'ARTEMIS 独立升级:将终止当前项目的服务、MCP 及任务。' -ForegroundColor Yellow . (Join-Path $PSScriptRoot 'upgrade_processes.ps1') Stop-ArtemisUpgradeProcesses -ProjectRoot $ProjectRoot & $python (Join-Path $PSScriptRoot 'update_installation.py') source --root $ProjectRoot if ($LASTEXITCODE -notin @(0, 10)) { throw '源码升级未完成;配置和历史资料已保留。' } Stop-ArtemisUpgradeProcesses -ProjectRoot $ProjectRoot # Load the new launcher only AFTER the frozen Git updater has exited. $prepare = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', (Join-Path $ProjectRoot 'scripts\start.ps1'), '-PrepareOnly', '-SkipUpdate', '-NoOpen') if ($Mirror) { $prepare += @('-Mirror', $Mirror) } $previousChild = $env:ARTEMIS_UPGRADE_CHILD $env:ARTEMIS_UPGRADE_CHILD = '1' try { & powershell.exe @prepare; $prepareCode = $LASTEXITCODE } finally { $env:ARTEMIS_UPGRADE_CHILD = $previousChild } if ($prepareCode -ne 0) { throw '新版源码已保留,但环境同步失败;请处理上方错误后重新运行升级脚本。' } Write-Host '[升级完成] 源码、依赖、网页及 Codex MCP 已检查并同步。' -ForegroundColor Green Write-Host '[下一步] 运行启动脚本,并在 Codex 中重载 MCP 或重启 Codex。' exit 0 } catch { Write-Host "[升级失败] $_" -ForegroundColor Red exit 1 }