#Requires -Version 5.1 [CmdletBinding()] param( [switch]$ConfigOnly, [switch]$SkipDesktopInstall, [switch]$SilentDesktopInstall, [string]$GitHubToken = $env:GITHUB_TOKEN, [string]$ConfigPath = (Join-Path $env:USERPROFILE ".config\opencode\opencode.json") ) $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $repo = "anomalyco/opencode" $windowsAssetName = "opencode-desktop-win-x64.exe" $releaseApiUrl = "https://api.github.com/repos/$repo/releases/latest" $baseUrl = "https://ai-openwebui.gesis.org/api" function Write-Step { param([string]$Message) Write-Host "" Write-Host "==> $Message" -ForegroundColor Cyan } function ConvertFrom-SecureStringToPlainText { param([Parameter(Mandatory = $true)][securestring]$SecureString) $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) try { [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { if ($bstr -ne [IntPtr]::Zero) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } } function Invoke-GitHubGet { param([Parameter(Mandatory = $true)][string]$Uri) $headers = @{ "Accept" = "application/vnd.github+json" "User-Agent" = "GESIS-OpenCode-Installer" } if (-not [string]::IsNullOrWhiteSpace($GitHubToken)) { $headers["Authorization"] = "Bearer $GitHubToken" } Invoke-RestMethod -Uri $Uri -Headers $headers -Method Get } function Install-OpenCodeDesktop { if ($SkipDesktopInstall -or $ConfigOnly) { Write-Host "Skipping OpenCode Desktop installation." return } if (-not [Environment]::Is64BitOperatingSystem) { throw "This installer expects 64-bit Windows because OpenCode publishes a Windows x64 desktop installer." } Write-Step "Finding latest OpenCode Desktop release" $release = Invoke-GitHubGet -Uri $releaseApiUrl $asset = $release.assets | Where-Object { $_.name -eq $windowsAssetName } | Select-Object -First 1 if ($null -eq $asset) { throw "Could not find $windowsAssetName in the latest $repo release ($($release.tag_name))." } Write-Host "Latest release: $($release.tag_name)" Write-Host "Downloading: $($asset.name)" $downloadPath = Join-Path $env:TEMP $asset.name Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $downloadPath -Headers @{ "User-Agent" = "GESIS-OpenCode-Installer" } Write-Step "Starting OpenCode Desktop installer" if ($SilentDesktopInstall) { $process = Start-Process -FilePath $downloadPath -ArgumentList "/S" -Wait -PassThru } else { $process = Start-Process -FilePath $downloadPath -Wait -PassThru } if ($process.ExitCode -ne 0) { throw "OpenCode Desktop installer exited with code $($process.ExitCode)." } } function New-GesisOpenCodeConfig { param([Parameter(Mandatory = $true)][string]$ApiKey) [ordered]@{ '$schema' = 'https://opencode.ai/config.json' enabled_providers = @('gesis') disabled_providers = @('zen', 'opencode') provider = [ordered]@{ gesis = [ordered]@{ npm = '@ai-sdk/openai-compatible' name = 'GESIS AI Server' options = [ordered]@{ baseURL = $baseUrl apiKey = $ApiKey } models = [ordered]@{ 'gemma3:27b' = @{ name = 'Gemma 3 27B' } 'gpt-4.1' = @{ name = 'GPT-4.1' } 'gpt-4.1-mini' = @{ name = 'GPT-4.1 Mini' } 'llama4:latest' = @{ name = 'Llama 4 Latest' } 'o4-mini' = @{ name = 'O4 Mini' } 'de-fr' = @{ name = 'De-Fr Translation' } 'de-ru' = @{ name = 'De-Ru Translation' } 'en-de' = @{ name = 'En-De Translation' } 'en-fr' = @{ name = 'En-Fr Translation' } 'en-ru' = @{ name = 'En-Ru Translation' } 'pdf-extractor' = @{ name = 'PDF Extractor' } 'gpt-5' = @{ name = 'GPT-5' } 'gpt-5-mini' = @{ name = 'GPT-5 Mini' } 'gpt-oss:120b' = @{ name = 'GPT-OSS 120B' } 'gpt-oss:latest' = @{ name = 'GPT-OSS Latest' } } } } } } function Write-OpenCodeConfig { Write-Step "Configuring GESIS AI Server" $secureApiKey = Read-Host "Enter your personal GESIS AI Server API key" -AsSecureString $apiKey = ConvertFrom-SecureStringToPlainText -SecureString $secureApiKey if ([string]::IsNullOrWhiteSpace($apiKey)) { throw "API key cannot be empty." } $configDirectory = Split-Path -Parent $ConfigPath New-Item -Path $configDirectory -ItemType Directory -Force | Out-Null if (Test-Path -LiteralPath $ConfigPath) { $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $backupPath = "$ConfigPath.backup-$timestamp" Copy-Item -LiteralPath $ConfigPath -Destination $backupPath -Force Write-Host "Existing config backed up to: $backupPath" } $config = New-GesisOpenCodeConfig -ApiKey $apiKey $json = $config | ConvertTo-Json -Depth 20 $utf8NoBom = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllText($ConfigPath, $json, $utf8NoBom) Write-Host "OpenCode config written to: $ConfigPath" } try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Install-OpenCodeDesktop Write-OpenCodeConfig Write-Step "Done" Write-Host "OpenCode Desktop is installed and configured for the GESIS AI Server." } catch { Write-Host "" Write-Host "Installation failed: $($_.Exception.Message)" -ForegroundColor Red exit 1 }