# Foxora Studio for Windows (x64): a verified, per-user install or update. # # irm https://foxora.ai/install-desktop.ps1 | iex # # Downloads the pinned Foxora Studio setup, checks its SHA-256 and its # Authenticode signature (publisher and timestamp) before running anything, then # opens Foxora Setup. It never changes Defender, system settings or your Foxora # data: accounts, device keys, permissions and projects are kept. It needs no # administrator rights; Setup asks Windows for permission only if it has to # install the Microsoft Visual C++ runtime. # # Options, when the file is run directly or as a script block: # -PrintTarget print the pinned version, file, SHA-256 and publisher; install nothing # -Update accepted for parity with install.sh; install and update are the same # & ([scriptblock]::Create((irm https://foxora.ai/install-desktop.ps1))) -PrintTarget # Environment: # FOXORA_INSTALLER_PATH use this local copy of the setup file instead of # downloading it; it must still match the pinned SHA-256 # and signature # # Pure ASCII on purpose: Windows PowerShell 5.1 reads a saved .ps1 without a BOM # as ANSI, and one typographic dash inside a string is enough to break parsing. # # "irm | iex" runs this text in your own scope, where a parameter named $Update # would replace a variable of yours; hence the prefixed names, removed again below. param([Alias('Update')][switch]$FoxoraUpdate, [Alias('PrintTarget')][switch]$FoxoraPrintTarget) # Everything below runs in a child scope, so "irm | iex" leaves no variables, # functions or preference changes behind in your PowerShell window. Failures are # thrown: PowerShell prints the reason and the command fails (exit code 1 when # scripted), and an interactive window stays open. try { & { param([switch]$Update, [switch]$PrintTarget) # Release pins. foxora-web's sync-desktop-release.mjs and check-downloads.mjs read # and rewrite these three lines by pattern: keep them at column 0, in this form. $Version = '5.0.10' $Asset = "Foxora-Studio_${Version}_x64-setup.exe" # Final Authenticode-signed Windows installer. $ExpectedSha256 = '5b6615fe83ed9367609f227bc236312f8d8058269784fe30c6ff98aa21b99a83' $Publisher = 'xBesh Labs, LLC' $Url = "https://github.com/foxora-ai/releases/releases/download/foxora-studio-v$Version/$Asset" # A Set-StrictMode in your profile would otherwise apply in here too. Set-StrictMode -Off $ErrorActionPreference = 'Stop' # Windows PowerShell 5.1 downloads many times slower while it draws a progress bar. $ProgressPreference = 'SilentlyContinue' if ($Update) { Write-Verbose 'foxora: -Update and a fresh install run the same steps.' } if ($PrintTarget) { [pscustomobject]@{ Version = $Version; Asset = $Asset; Sha256 = $ExpectedSha256; Publisher = $Publisher; Url = $Url } return } if ($ExpectedSha256 -notmatch '^[0-9a-f]{64}$') { throw 'foxora: the signed Windows release is still being verified. See https://foxora.ai/download for availability.' } if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage') { throw "foxora: this PowerShell window runs in $($ExecutionContext.SessionState.LanguageMode) mode (set by your organisation), which cannot verify the installer. Download Foxora Studio from https://foxora.ai/download instead." } if ($env:OS -ne 'Windows_NT') { throw 'foxora: this installer is for Windows. On macOS or Linux run: curl -fsSL https://foxora.ai/install.sh | sh' } # The machine's own processor, not this process's: x64 PowerShell running under # emulation on an ARM PC reports AMD64 in its environment. function Get-FoxoraMachineArch { try { $value = (Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' -Name PROCESSOR_ARCHITECTURE -ErrorAction Stop).PROCESSOR_ARCHITECTURE if ($value) { return ([string]$value).ToUpperInvariant() } } catch { Write-Verbose "registry architecture unavailable: $($_.Exception.Message)" } foreach ($value in @($env:PROCESSOR_ARCHITEW6432, $env:PROCESSOR_ARCHITECTURE)) { if ($value) { return $value.ToUpperInvariant() } } return 'UNKNOWN' } function Get-FoxoraWindowsBuild { try { return [int](Get-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name CurrentBuildNumber -ErrorAction Stop).CurrentBuildNumber } catch { return [Environment]::OSVersion.Version.Build } } $MachineArch = Get-FoxoraMachineArch if ($MachineArch -eq 'ARM64') { throw 'foxora: Foxora Studio for Windows is built for x64 (Intel and AMD) processors only. This PC has an ARM64 processor, and there is no Windows on ARM build of Foxora Studio yet. Nothing was installed.' } if ($MachineArch -ne 'AMD64') { throw "foxora: Foxora Studio for Windows needs a 64-bit Intel or AMD (x64) PC; this one reports $MachineArch. Nothing was installed." } if ((Get-FoxoraWindowsBuild) -lt 19045) { throw 'foxora: Foxora Studio needs Windows 10 22H2 or Windows 11 (or Windows Server 2022 or newer). Nothing was installed.' } # Advisory only: a failure here never blocks the install. try { $Identity = [Security.Principal.WindowsIdentity]::GetCurrent() if (([Security.Principal.WindowsPrincipal]$Identity).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "This PowerShell window is running as Administrator. Foxora Studio installs for the account $($Identity.Name) only; if you use Foxora from a different account, close this window and run the command in a normal PowerShell window." } } catch { Write-Verbose "administrator check unavailable: $($_.Exception.Message)" } function Assert-FoxoraIdle { $names = @('foxora-desktop', 'Foxora Setup', 'foxora-setup', [IO.Path]::GetFileNameWithoutExtension($Asset), "Foxora_${Version}_x64-setup") $running = @(Get-Process -Name $names -ErrorAction SilentlyContinue) if ($running.Count -gt 0) { $list = ($running | ForEach-Object { $_.ProcessName } | Sort-Object -Unique) -join ', ' throw "foxora: quit Foxora and any open Foxora Setup window ($list), then run this command again. Running tasks will not be interrupted." } } function Save-FoxoraDownload([string]$Uri, [string]$OutFile) { for ($attempt = 1; ; $attempt++) { try { Invoke-WebRequest -Uri $Uri -OutFile $OutFile -UseBasicParsing -TimeoutSec 900 return } catch { $status = 0 try { $status = [int]$_.Exception.Response.StatusCode } catch { $status = 0 } # A missing file stays missing: report it now instead of after two more tries. if ($status -eq 404) { throw "foxora: could not download $Uri ($($_.Exception.Message)). That file is not published; download Foxora Studio from https://foxora.ai/download instead. Nothing was installed." } if ($attempt -ge 3) { throw "foxora: could not download $Uri ($($_.Exception.Message)). Check your connection and run the command again. Nothing was installed." } Start-Sleep -Seconds (3 * $attempt) } } } Assert-FoxoraIdle # GitHub requires TLS 1.2; older Windows PowerShell 5.1 setups still offer only TLS 1.0. $Protocols = [int][Net.ServicePointManager]::SecurityProtocol if ($Protocols -ne 0 -and ($Protocols -band 3072) -eq 0) { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]($Protocols -bor 3072) } $TempDir = Join-Path ([IO.Path]::GetTempPath()) ('foxora-studio-' + [guid]::NewGuid().ToString('N').Substring(0, 12)) New-Item -ItemType Directory -Path $TempDir | Out-Null try { $Installer = Join-Path $TempDir $Asset if ($env:FOXORA_INSTALLER_PATH) { Write-Host "foxora: using $env:FOXORA_INSTALLER_PATH" Copy-Item -LiteralPath $env:FOXORA_INSTALLER_PATH -Destination $Installer } else { Write-Host "foxora: downloading Foxora Studio $Version for Windows x64..." Save-FoxoraDownload $Url $Installer } $ActualSha256 = (Get-FileHash -LiteralPath $Installer -Algorithm SHA256).Hash.ToLowerInvariant() if ($ActualSha256 -ne $ExpectedSha256) { throw "foxora: checksum mismatch (expected $ExpectedSha256, got $ActualSha256). Nothing was installed." } $Signature = Get-AuthenticodeSignature -LiteralPath $Installer if ($Signature.Status -ne 'Valid') { throw "foxora: Windows could not verify the installer's signature ($($Signature.Status): $($Signature.StatusMessage)). Nothing was installed." } $SignerName = $Signature.SignerCertificate.GetNameInfo([Security.Cryptography.X509Certificates.X509NameType]::SimpleName, $false) if ($SignerName -ne $Publisher) { throw "foxora: the installer is signed by '$SignerName', not '$Publisher'. Nothing was installed." } if (-not $Signature.TimeStamperCertificate) { throw 'foxora: the installer signature carries no timestamp. Nothing was installed.' } Assert-FoxoraIdle Write-Host "foxora: verified Foxora Studio $Version, signed by $Publisher." Write-Host 'foxora: unpacking Foxora Setup. Keep this window open; Windows may ask to install the Microsoft Visual C++ runtime.' # Wait only for the outer extractor, not the setup window it opens: when it # exits, everything Setup needs is in %LOCALAPPDATA%\Foxora\Setup\. $Process = Start-Process -FilePath $Installer -PassThru # Hold the process handle now, or ExitCode can read as empty after it exits. $null = $Process.Handle $Process.WaitForExit() if ($Process.ExitCode -ne 0) { throw "foxora: Foxora Setup could not unpack or start (exit code $($Process.ExitCode)). Run the command again, or download Setup from https://foxora.ai/download" } Write-Host 'foxora: Foxora Setup is open in its own window. Finish the installation there; this command does not wait for it. Your accounts, device keys, permissions and projects are kept.' } finally { Remove-Item -LiteralPath $TempDir -Recurse -Force -ErrorAction SilentlyContinue } } -Update:$FoxoraUpdate -PrintTarget:$FoxoraPrintTarget } finally { Remove-Variable -Name FoxoraUpdate, FoxoraPrintTarget -Scope Local -ErrorAction SilentlyContinue }