$ErrorActionPreference = 'Stop' $taskRoot = 'C:\kefu\wechat_rpa' $taskRecordPath = Join-Path $taskRoot 'dist\installer\v1.4.9-verification.json' $taskRecord = Get-Content -LiteralPath $taskRecordPath -Raw -Encoding UTF8 | ConvertFrom-Json if ($taskRecord.status -ne 'verified') { throw 'Installer has not passed final verification' } $taskInstaller = [string]$taskRecord.installer.path if ((Get-FileHash -LiteralPath $taskInstaller -Algorithm SHA256).Hash -ne $taskRecord.installer.sha256) { throw 'Installer hash mismatch' } $taskTarget = 'C:\Users\isard\AppData\Local\Programs\ZhenAIService' $taskResolved = (Resolve-Path -LiteralPath $taskTarget).Path if (-not [string]::Equals($taskResolved, $taskTarget, [StringComparison]::OrdinalIgnoreCase)) { throw 'Unexpected install target' } $taskInternal = [IO.Path]::GetFullPath((Join-Path $taskResolved '_internal')) if (-not $taskInternal.StartsWith($taskResolved + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Runtime directory escaped install target' } foreach ($taskPath in @($taskResolved, $taskInternal)) { if ((Get-Item -LiteralPath $taskPath).Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'Install target contains a directory link' } } $taskLinks = @(Get-ChildItem -LiteralPath $taskInternal -Directory -Recurse -Force | Where-Object { $_.Attributes -band [IO.FileAttributes]::ReparsePoint }) if ($taskLinks.Count) { throw 'Runtime directory contains links; refusing recursive replacement' } $taskProcesses = Get-CimInstance Win32_Process -Filter "Name LIKE 'ZhenAI-WeCom-Assistant-v%.exe'" foreach ($taskProcess in $taskProcesses) { if ($taskProcess.ExecutablePath -and [string]::Equals([IO.Path]::GetDirectoryName($taskProcess.ExecutablePath), $taskResolved, [StringComparison]::OrdinalIgnoreCase)) { Stop-Process -Id $taskProcess.ProcessId -ErrorAction Stop $taskExiting = Get-Process -Id $taskProcess.ProcessId -ErrorAction SilentlyContinue if ($taskExiting -and -not $taskExiting.WaitForExit(10000)) { throw 'Old assistant did not exit' } } } $taskData = 'C:\Users\isard\AppData\Local\ZhenYangTangRPA' $taskDataHashes = @{} foreach ($taskName in @('conversations.json','pending_replies.json','app_settings.json','ai_settings.json','wxwork_keys.json')) { $taskFile = Join-Path $taskData $taskName if (Test-Path -LiteralPath $taskFile -PathType Leaf) { $taskDataHashes[$taskName] = (Get-FileHash -LiteralPath $taskFile -Algorithm SHA256).Hash } } Write-Output 'Installing verified v1.4.9 into the existing assistant program directory.' $taskInstall = Start-Process -FilePath $taskInstaller -ArgumentList ('/S /D=' + $taskResolved) -WindowStyle Hidden -PassThru $taskInstall.WaitForExit() if ($taskInstall.ExitCode -ne 0) { throw ('Installer failed: ' + $taskInstall.ExitCode) } $taskExe = Join-Path $taskResolved 'ZhenAI-WeCom-Assistant-v1.4.9.exe' $taskExeHash = (Get-FileHash -LiteralPath $taskExe -Algorithm SHA256).Hash if ($taskExeHash -ne $taskRecord.app.sha256) { throw 'Installed EXE hash mismatch' } Write-Output 'Installed EXE matches the validated build; running local self-check.' $taskOldPath = $env:PATH try { $env:PATH = (Join-Path $env:SystemRoot 'System32') + ';' + $env:SystemRoot $taskCheck = Start-Process -FilePath $taskExe -ArgumentList '--packaging-self-check' -WorkingDirectory $taskResolved -WindowStyle Hidden -PassThru } finally { $env:PATH = $taskOldPath } if (-not $taskCheck.WaitForExit(90000)) { $taskCheck.Kill(); throw 'Installed self-check timed out' } if ($taskCheck.ExitCode -ne 0) { throw ('Installed self-check failed: ' + $taskCheck.ExitCode) } foreach ($taskName in $taskDataHashes.Keys) { if ((Get-FileHash -LiteralPath (Join-Path $taskData $taskName) -Algorithm SHA256).Hash -ne $taskDataHashes[$taskName]) { throw ('User data changed during install: ' + $taskName) } } $taskVersion = (Get-ItemProperty -LiteralPath 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\ZhenAIService').DisplayVersion if ($taskVersion -ne '1.4.9') { throw 'Installed version registration mismatch' } $taskResult = [ordered]@{version='1.4.9';path=$taskExe;sha256=$taskExeHash;installer_exit_code=$taskInstall.ExitCode;self_check_exit_code=$taskCheck.ExitCode;user_data_preserved=$true;user_data_files=@($taskDataHashes.Keys);listener_started=$false} $taskResult | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $taskRoot 'tmp\self-reply-local-install-verification.json') -Encoding UTF8 Write-Output 'Local installation verified; chat records and settings preserved; listener remains stopped.'