#Requires -Version 5.1 [CmdletBinding()]param([string]$OutputDirectory,[string]$Version='0.0.1') Set-StrictMode -Version Latest;$ErrorActionPreference='Stop' if($Version-notmatch'^\d+\.\d+\.\d+$'){throw 'Version must use major.minor.patch.'} $root=[IO.Path]::GetFullPath($PSScriptRoot);if(-not$OutputDirectory){$OutputDirectory=Join-Path $root ('dist\ms-private-'+$Version+'-final-'+(Get-Date -Format yyyyMMdd))};$output=[IO.Path]::GetFullPath($OutputDirectory);[IO.Directory]::CreateDirectory($output)|Out-Null $target=Join-Path $output ("ms-private-$Version.exe");if(Test-Path -LiteralPath $target){throw 'Executable already exists. Use an empty output directory.'} $dotnet=(Get-Command dotnet.exe -CommandType Application -ErrorAction Stop).Source $temp=Join-Path ([IO.Path]::GetTempPath()) ('ms-private-native-'+[guid]::NewGuid().ToString('N'));[IO.Directory]::CreateDirectory($temp)|Out-Null try{ $payload=Join-Path $temp 'ms-private.payload.zip';$entries=[Collections.Generic.List[object]]::new() $rootScripts=@('Accounts.ps1','Cleanup.ps1','Hardware.ps1','Home.ps1','Install-Apps.ps1','Lock-Event.ps1','Lock.ps1','Manage-Apps.ps1','ms-private-GUI.ps1','ms-private.ps1','Network.ps1','Offline-Updates.ps1','Preset.ps1','Recall.ps1','Security.ps1','Services.ps1') $files=@($rootScripts|ForEach-Object{Get-Item -LiteralPath (Join-Path $root $_)}) foreach($folder in @('src','config')){$files+=@(Get-ChildItem -LiteralPath (Join-Path $root $folder) -Recurse -File -Force)} foreach($doc in @('docs\HOME-GUIDE.md','docs\PRIVACY.md','docs\ANONYMITY.md','docs\TOOL-BREAKDOWN.md','docs\OFFLINE-UPDATES.md','docs\NATIVE-API.md','docs\ROADMAP.md')){$files+=Get-Item -LiteralPath (Join-Path $root $doc)} Add-Type -AssemblyName System.IO.Compression;Add-Type -AssemblyName System.IO.Compression.FileSystem $zip=[IO.Compression.ZipFile]::Open($payload,[IO.Compression.ZipArchiveMode]::Create) try{ foreach($file in @($files|Sort-Object FullName)){ if(-not$file.FullName.StartsWith($root+'\',[StringComparison]::OrdinalIgnoreCase)-or($file.Attributes-band[IO.FileAttributes]::ReparsePoint)){throw 'Unsafe executable payload input.'} $relative=$file.FullName.Substring($root.Length+1).Replace('\','/');$hash=(Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash.ToLowerInvariant();$entries.Add([ordered]@{Path=$relative;Length=[long]$file.Length;Sha256=$hash}) [IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,$file.FullName,$relative,[IO.Compression.CompressionLevel]::Optimal)|Out-Null } $manifest=[ordered]@{Schema=1;Version=$Version;EntryPoint='ms-private-GUI.ps1';Files=@($entries)}|ConvertTo-Json -Depth 5 -Compress $entry=$zip.CreateEntry('payload.manifest.json',[IO.Compression.CompressionLevel]::Optimal);$stream=$entry.Open();try{$writer=[IO.StreamWriter]::new($stream,[Text.UTF8Encoding]::new($false));try{$writer.Write($manifest)}finally{$writer.Dispose()}}finally{$stream.Dispose()} }finally{$zip.Dispose()} $appManifest=Join-Path $temp 'app.manifest';$appManifestText=[IO.File]::ReadAllText((Join-Path $root 'native\MsPrivate.Launcher\app.manifest'));$appManifestText=[regex]::Replace($appManifestText,'(?<=