# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. # Meta VR CLI (metavr) installer for Windows # metavr is the new name for the CLI formerly known as hzdb; this also installs # a legacy hzdb.exe alias. # # KEEP IN SYNC with the canonical copy at arvr/apps/hzdb/scripts/install.ps1. # This nowdoc is the live served version (developers.meta.com/.../windows/, # fetched via `iwr | iex`); fixes must land in both, or the installers drift. $ErrorActionPreference = "Stop" # --- Configuration --- $CoreAppIdWin = "26548836238067195" $RcParam = "" $InfoRcParam = "" $DownloadBaseUrl = "https://www.oculus.com/download_app/" $InfoBaseUrl = "https://www.oculus.com/binary-info/" $DefaultHome = Join-Path $env:USERPROFILE ".metavr" # Install to ~/.metavr. METAVR_HOME overrides; the legacy HZDB_HOME is still # honored for anyone who set it before the rename. $HomeDir = if ($env:METAVR_HOME) { $env:METAVR_HOME } elseif ($env:HZDB_HOME) { $env:HZDB_HOME } else { $DefaultHome } $InstallDir = Join-Path $HomeDir "bin" # --- Download --- function Install-Metavr { $downloadUrl = "${DownloadBaseUrl}?id=${CoreAppIdWin}${RcParam}" $infoUrl = "${InfoBaseUrl}${CoreAppIdWin}/latest/${InfoRcParam}" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Fetch integrity metadata (best-effort). The binary-info feed publishes the # expected sha256 (and version) for the artifact served by download_app. We # keep downloading from download_app (a proven tokenless redirect) and only # use the feed to verify the bytes. If the feed is unreachable or omits # sha256 (older feed during rollout), proceed silently without verification. $expectedSha256 = $null $metavrVersion = $null try { $info = Invoke-RestMethod -Uri $infoUrl -UseBasicParsing $expectedSha256 = $info.sha256 $metavrVersion = $info.version } catch { $expectedSha256 = $null } if ($metavrVersion) { Write-Host "Downloading metavr $metavrVersion..." } else { Write-Host "Downloading metavr..." } New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null # Download to a neutral temp path: the Windows Core App stores the binary # inside a zip wrapper (mimeType=application/zip), exactly like the macOS and # Linux Core Apps, so the bytes download_app serves are normally a .zip, not # a bare .exe. (Older artifacts were a raw .exe; handled below.) $tempFile = Join-Path ([System.IO.Path]::GetTempPath()) "metavr_install.download" $extractDir = $null try { $webClient = New-Object System.Net.WebClient $webClient.DownloadFile($downloadUrl, $tempFile) if (-not (Test-Path $tempFile) -or (Get-Item $tempFile).Length -eq 0) { throw "Downloaded file is empty or missing" } # Verify the downloaded artifact against the feed's sha256 before # installing. The feed's sha256 is over the exact bytes download_app # serves (the zip wrapper), so verify before any extraction. Skip # silently when no sha256 is available so the install isn't blocked while # the feed rolls out. if ($expectedSha256) { $actualSha256 = (Get-FileHash -Algorithm SHA256 -Path $tempFile).Hash if ($actualSha256.ToLower() -ne $expectedSha256.ToLower()) { throw "Downloaded metavr failed integrity verification (expected sha256 $($expectedSha256.ToLower()), got $($actualSha256.ToLower()))" } Write-Host "Verified download integrity (sha256)." } $targetPath = Join-Path $InstallDir "metavr.exe" # The Core App artifact is a zip wrapping the executable, the same shape # the macOS/Linux installers extract. Detect the zip by its 'PK' magic # bytes and pull metavr.exe out of it; if the download is a raw .exe # instead (older artifact), install it directly. # Read the 2 magic bytes one at a time: Stream.Read may return fewer # bytes than requested, which would leave the second byte 0 and # misclassify a zip as a raw .exe. $stream = [System.IO.File]::OpenRead($tempFile) try { $b0 = $stream.ReadByte() $b1 = $stream.ReadByte() } finally { $stream.Dispose() } $isZip = ($b0 -eq 0x50 -and $b1 -eq 0x4B) # "PK" if ($isZip) { # Expand-Archive requires the source path to end in .zip. $zipFile = "$tempFile.zip" Move-Item -Force $tempFile $zipFile $tempFile = $zipFile $extractDir = Join-Path ([System.IO.Path]::GetTempPath()) ("metavr_extract_" + [System.Guid]::NewGuid().ToString("N")) Expand-Archive -Path $zipFile -DestinationPath $extractDir -Force # metavr.exe at the archive root; older artifacts used hzdb.exe. Only # those known names are accepted (matching the macOS/Linux # install.sh) — an unexpected archive shape fails loud rather than # silently installing some other bundled .exe. $extractedExe = Join-Path $extractDir "metavr.exe" if (-not (Test-Path $extractedExe)) { $extractedExe = Join-Path $extractDir "hzdb.exe" } if (-not (Test-Path $extractedExe)) { throw "Could not find metavr.exe or hzdb.exe at the root of the downloaded archive" } Move-Item -Force $extractedExe $targetPath } else { Move-Item -Force $tempFile $targetPath } # Provide the legacy `hzdb` command too. Windows has no reliable symlink # without admin/developer mode, so copy the binary to hzdb.exe. Copy-Item -Force $targetPath (Join-Path $InstallDir "hzdb.exe") Write-Host "Installed metavr to $targetPath" } finally { if (Test-Path $tempFile) { Remove-Item -Force $tempFile } if ($extractDir -and (Test-Path $extractDir)) { Remove-Item -Recurse -Force $extractDir } } } # --- PATH Setup --- function Update-Path { # Match the Unix precedence (${METAVR_NO_PATH:-${HZDB_NO_PATH:-0}}): the new # METAVR_NO_PATH wins when set to any non-empty value, so METAVR_NO_PATH=0 # re-enables PATH setup even if the legacy HZDB_NO_PATH=1. Only consult the # legacy variable when METAVR_NO_PATH is unset/empty. (In PowerShell a # non-empty string like "0" is truthy, mirroring the shell's :- default.) $noPath = if ($env:METAVR_NO_PATH) { $env:METAVR_NO_PATH -eq "1" } else { $env:HZDB_NO_PATH -eq "1" } if ($noPath) { return } $currentPath = [Environment]::GetEnvironmentVariable("Path", "User") if ($currentPath -and $currentPath.Split(";") -contains $InstallDir) { return } $newPath = if ($currentPath) { "$InstallDir;$currentPath" } else { $InstallDir } [Environment]::SetEnvironmentVariable("Path", $newPath, "User") $env:Path = "$InstallDir;$env:Path" if (-not ([System.Management.Automation.PSTypeName]"NativeMethods").Type) { Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @" [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessageTimeout( IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult); "@ } $HWND_BROADCAST = [IntPtr]0xffff $WM_SETTINGCHANGE = 0x1a $result = [UIntPtr]::Zero [Win32.NativeMethods]::SendMessageTimeout( $HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 5000, [ref]$result ) | Out-Null Write-Host "Added metavr to user PATH (restart terminal for changes to take effect)" } # --- Main --- function Main { Write-Host "Meta VR CLI (metavr) installer" Write-Host "" Install-Metavr Update-Path Write-Host "" Write-Host "metavr is installed. Open a new terminal, then run:" Write-Host " metavr init Set up the MCP server + skills for your AI agent" Write-Host "" Write-Host "Other commands:" Write-Host " metavr --help See all commands" Write-Host " metavr update Update metavr to the latest version" } Main