Last active
          July 12, 2025 18:46 
        
      - 
      
 - 
        
Save dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39 to your computer and use it in GitHub Desktop.  
    Copilot API token for Aider
  
        
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | # GitHub OAuth Token Generator for Aider with Copilot | |
| # This script generates the GitHub OAuth token needed to access Copilot API | |
| # | |
| # Run using `iex (Invoke-RestMethod -Uri ('https://gist.githubusercontent.com/dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39/raw')` | |
| Write-Host "`n====================================" -ForegroundColor Cyan | |
| Write-Host " Aider Config Script v2.21" -ForegroundColor White | |
| Write-Host "====================================" -ForegroundColor Cyan | |
| # Check if aider is installed | |
| $aiderInstalled = $false | |
| try { | |
| if (Get-Command aider -ErrorAction SilentlyContinue) { | |
| $aiderInstalled = $true | |
| Write-Host "Aider is already installed." -ForegroundColor Green | |
| } | |
| } | |
| catch { | |
| $aiderInstalled = $false | |
| } | |
| # Install aider if not already installed | |
| if (-not $aiderInstalled) { | |
| Write-Host "Aider is not installed. Installing aider..." -ForegroundColor Yellow | |
| try { | |
| Invoke-Expression "powershell -ExecutionPolicy ByPass -c `"irm https://aider.chat/install.ps1 | iex`"" | |
| Write-Host "Aider installed successfully." -ForegroundColor Green | |
| # Add aider to PATH | |
| $aiderPath = "$env:LOCALAPPDATA\Programs\aider" | |
| $currentPath = [Environment]::GetEnvironmentVariable("Path", "User") | |
| if ($currentPath -notlike "*$aiderPath*") { | |
| Write-Host "Adding aider to PATH..." -ForegroundColor Yellow | |
| [Environment]::SetEnvironmentVariable("Path", "$currentPath;$aiderPath", "User") | |
| $env:Path = "$env:Path;$aiderPath" | |
| Write-Host "Aider added to PATH. You may need to restart your terminal for changes to take effect." -ForegroundColor Green | |
| } | |
| } | |
| catch { | |
| Write-Host "Failed to install aider. Please install it manually using: powershell -ExecutionPolicy ByPass -c `"irm https://aider.chat/install.ps1 | iex`"" -ForegroundColor Red | |
| Write-Host "Error: $_" -ForegroundColor Red | |
| exit 1 | |
| } | |
| } | |
| # Check for existing configuration | |
| $userHome = [Environment]::GetFolderPath('UserProfile') | |
| $aiderConfigPath = Join-Path -Path $userHome -ChildPath ".aider.conf.yml" | |
| $skipTokenGeneration = $false | |
| $oauthToken = $null | |
| # Install commandlet function (moved here for reuse) | |
| function Install-UpdateAiderCopilot { | |
| # Function to install or update the Update-AiderCopilot commandlet | |
| Write-Host "`n📦 Installing Update-AiderCopilot commandlet..." -ForegroundColor Cyan | |
| try { | |
| # Create the commandlet file | |
| $cmdletDir = Split-Path $cmdletPath -Parent | |
| if (-not (Test-Path $cmdletDir)) { | |
| New-Item -ItemType Directory -Path $cmdletDir -Force | Out-Null | |
| } | |
| Set-Content -Path $cmdletPath -Value $cmdletContent -Encoding UTF8 -NoNewline | |
| # Add to PowerShell profile for auto-loading | |
| $profilePath = $PROFILE.CurrentUserAllHosts | |
| $profileDir = Split-Path $profilePath -Parent | |
| if (-not (Test-Path $profileDir)) { | |
| New-Item -ItemType Directory -Path $profileDir -Force | Out-Null | |
| } | |
| # Remove old import command format if exists | |
| if (Test-Path $profilePath) { | |
| $profileContent = Get-Content $profilePath -Raw | |
| # Ensure profileContent is a string | |
| if ($profileContent -is [array]) { | |
| $profileContent = $profileContent -join "`n" | |
| } | |
| if (-not $profileContent) { | |
| $profileContent = "" | |
| } | |
| # Escape the path for use in regex | |
| $escapedPath = [regex]::Escape($cmdletPath) | |
| $oldPattern = "\. `"$escapedPath`"" | |
| $profileContent = $profileContent -replace $oldPattern, "" | |
| # Also remove any duplicate entries | |
| $escapedImport = [regex]::Escape("Import-Module `"$cmdletPath`" -Force -Global") | |
| $profileContent = $profileContent -replace $escapedImport, "" | |
| # Clean up any multiple newlines | |
| $profileContent = $profileContent -replace "(\r?\n){3,}", "`n`n" | |
| Set-Content -Path $profilePath -Value $profileContent.TrimEnd() -Encoding UTF8 -NoNewline | |
| } | |
| # Use dot-sourcing since it's a regular script, not a module | |
| $importCommand = ". `"$cmdletPath`"" | |
| if (Test-Path $profilePath) { | |
| $profileContent = Get-Content $profilePath -Raw | |
| if ($profileContent -notlike "*$importCommand*") { | |
| Add-Content -Path $profilePath -Value "`n$importCommand" | |
| } | |
| } | |
| else { | |
| Set-Content -Path $profilePath -Value $importCommand -Encoding UTF8 -NoNewline | |
| } | |
| # Force reload for current session by dot-sourcing | |
| . $cmdletPath | |
| Write-Host "`n✅ Update-AiderCopilot commandlet installed!" -ForegroundColor Green | |
| Write-Host " 📌 You can now run 'Update-AiderCopilot' from any PowerShell" -ForegroundColor White | |
| Write-Host " session to reconfigure Aider or update this script." -ForegroundColor White | |
| Write-Host " 🔄 The commandlet auto-updates from the latest gist version." -ForegroundColor Gray | |
| } | |
| catch { | |
| Write-Host "⚠️ Failed to install Update-AiderCopilot commandlet: $_" -ForegroundColor Yellow | |
| Write-Host " You can still re-run the configuration using:" -ForegroundColor White | |
| Write-Host " iex (Invoke-RestMethod 'https://gist.githubusercontent.com/dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39/raw')" -ForegroundColor Gray | |
| } | |
| } | |
| # Function to patch aider's exceptions.py | |
| function Patch-AiderExceptions { | |
| Write-Host "`n🔧 Patching Aider Exceptions for GitHub Copilot Compatibility" -ForegroundColor Cyan | |
| # Find aider installation path | |
| $aiderPaths = @( | |
| "$env:APPDATA\uv\tools\aider-chat\Lib\site-packages\aider\exceptions.py", | |
| "$env:LOCALAPPDATA\Programs\Python\Python*\Lib\site-packages\aider\exceptions.py", | |
| "$env:ProgramFiles\Python*\Lib\site-packages\aider\exceptions.py" | |
| ) | |
| $exceptionsPath = $null | |
| foreach ($path in $aiderPaths) { | |
| $resolvedPaths = Resolve-Path $path -ErrorAction SilentlyContinue | |
| if ($resolvedPaths) { | |
| $exceptionsPath = $resolvedPaths[0].Path | |
| break | |
| } | |
| } | |
| if (-not $exceptionsPath -or -not (Test-Path $exceptionsPath)) { | |
| Write-Host "❌ Could not find aider's exceptions.py file." -ForegroundColor Red | |
| Write-Host " Expected locations checked:" -ForegroundColor Yellow | |
| foreach ($path in $aiderPaths) { | |
| Write-Host " - $path" -ForegroundColor Gray | |
| } | |
| return $false | |
| } | |
| Write-Host "Found exceptions.py at: $exceptionsPath" -ForegroundColor Green | |
| # Read the file | |
| $content = Get-Content $exceptionsPath -Raw | |
| # Check if already patched | |
| if ($content -match 'ExInfo\("BadRequestError",\s*True,\s*"Retrying due to known Copilot 400 error flakiness\."\)') { | |
| Write-Host "✅ File is already patched!" -ForegroundColor Green | |
| return $true | |
| } | |
| # Create backup | |
| $backupPath = "$exceptionsPath.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')" | |
| Copy-Item -Path $exceptionsPath -Destination $backupPath -Force | |
| Write-Host "Created backup at: $backupPath" -ForegroundColor Yellow | |
| # Apply patch | |
| $oldLine = 'ExInfo("BadRequestError", False, None),' | |
| $newLine = 'ExInfo("BadRequestError", True, "Retrying due to known Copilot 400 error flakiness."),' | |
| $patchedContent = $content -replace [regex]::Escape($oldLine), $newLine | |
| if ($patchedContent -eq $content) { | |
| Write-Host "❌ Could not find the line to patch. The file format may have changed." -ForegroundColor Red | |
| return $false | |
| } | |
| try { | |
| Set-Content -Path $exceptionsPath -Value $patchedContent -Encoding UTF8 -NoNewline | |
| Write-Host "✅ Successfully patched exceptions.py!" -ForegroundColor Green | |
| Write-Host " BadRequestError will now be retried automatically." -ForegroundColor Green | |
| return $true | |
| } | |
| catch { | |
| Write-Host "❌ Failed to write patched file: $_" -ForegroundColor Red | |
| Write-Host " You may need to run this script as Administrator." -ForegroundColor Yellow | |
| return $false | |
| } | |
| } | |
| # Check if Update-AiderCopilot is already installed | |
| $cmdletPath = "$env:LOCALAPPDATA\Programs\aider\Update-AiderCopilot.ps1" | |
| $cmdletInstalled = Test-Path $cmdletPath | |
| if (Test-Path $aiderConfigPath) { | |
| Write-Host "`nExisting aider configuration found at $aiderConfigPath" -ForegroundColor Yellow | |
| # Read existing config | |
| $existingConfig = Get-Content $aiderConfigPath -Raw | |
| if ($existingConfig -match 'openai-api-key:\s*"([^"]+)"') { | |
| $existingToken = $matches[1] | |
| Write-Host "Found existing GitHub OAuth token in config." -ForegroundColor Green | |
| Write-Host "`nOptions:" -ForegroundColor Cyan | |
| Write-Host " 1. Update model selection only (keep existing token)" -ForegroundColor White | |
| Write-Host " 2. Generate new token and update models" -ForegroundColor White | |
| Write-Host " 3. Patch aider exceptions for GitHub Copilot compatibility" -ForegroundColor White | |
| Write-Host " 4. Exit without changes" -ForegroundColor White | |
| $updateChoice = Read-Host "`nSelect option [1/2/3/4]" | |
| if ($updateChoice -eq "1") { | |
| # Skip to model selection with existing token | |
| $oauthToken = $existingToken | |
| $skipTokenGeneration = $true | |
| Write-Host "`nUsing existing token for model selection..." -ForegroundColor Green | |
| } | |
| elseif ($updateChoice -eq "3") { | |
| # Patch exceptions and exit | |
| Patch-AiderExceptions | |
| # Install/update commandlet | |
| if (-not $cmdletInstalled) { | |
| Write-Host "`nInstalling Update-AiderCopilot for future use..." -ForegroundColor Cyan | |
| Install-UpdateAiderCopilot | |
| } | |
| else { | |
| # Force reload if already installed to get latest version | |
| Write-Host "`nReloading Update-AiderCopilot commandlet..." -ForegroundColor Cyan | |
| . $cmdletPath | |
| } | |
| return | |
| } | |
| elseif ($updateChoice -eq "4") { | |
| Write-Host "`nExiting without changes..." -ForegroundColor Yellow | |
| # Install/update commandlet even when exiting | |
| if (-not $cmdletInstalled) { | |
| Write-Host "`nBefore you go - installing Update-AiderCopilot for future use..." -ForegroundColor Cyan | |
| Install-UpdateAiderCopilot | |
| } | |
| else { | |
| # Force reload if already installed to get latest version | |
| Write-Host "`nReloading Update-AiderCopilot commandlet..." -ForegroundColor Cyan | |
| . $cmdletPath | |
| } | |
| return | |
| } | |
| # else continue with new token generation | |
| } | |
| } | |
| # Token generation process | |
| if (-not $skipTokenGeneration) { | |
| # Step 1: Initiate the Device Flow Authentication | |
| Write-Host "`nInitiating GitHub device flow authentication..." -ForegroundColor Cyan | |
| $deviceCodeResponse = Invoke-RestMethod -Method Post -Uri "https://github.com/login/device/code" -Headers @{ | |
| "Accept" = "application/json" | |
| } -Body @{ | |
| "client_id" = "01ab8ac9400c4e429b23" | |
| "scope" = "user:email" | |
| } | |
| $deviceCode = $deviceCodeResponse.device_code | |
| $userCode = $deviceCodeResponse.user_code | |
| $verificationUri = $deviceCodeResponse.verification_uri | |
| $interval = $deviceCodeResponse.interval | |
| # Step 2: Prompt user to complete authentication | |
| Write-Host "`nPlease follow these steps to authenticate:" -ForegroundColor Yellow | |
| Write-Host "1. Copy this code: $userCode" -ForegroundColor Green | |
| Write-Host "2. Open this URL in your browser: $verificationUri" -ForegroundColor Yellow | |
| Write-Host "3. Paste the code and authorize GitHub Copilot access" -ForegroundColor Yellow | |
| Write-Host "4. Return to this terminal when completed" -ForegroundColor Yellow | |
| # Copy to clipboard if possible | |
| try { | |
| $userCode | Set-Clipboard | |
| Write-Host "`n✓ Code copied to clipboard!" -ForegroundColor Green | |
| } | |
| catch { | |
| # Clipboard not available | |
| } | |
| Write-Host "`nWaiting for authorization..." -ForegroundColor Cyan | |
| $completed = $false | |
| # Step 3: Poll for token | |
| while (-not $completed) { | |
| Start-Sleep -Seconds $interval | |
| try { | |
| $tokenResponse = Invoke-RestMethod -Method Post -Uri "https://github.com/login/oauth/access_token" -Headers @{ | |
| "Accept" = "application/json" | |
| } -Body @{ | |
| "client_id" = "01ab8ac9400c4e429b23" | |
| "device_code" = $deviceCode | |
| "grant_type" = "urn:ietf:params:oauth:grant-type:device_code" | |
| } -ErrorAction SilentlyContinue | |
| if ($tokenResponse.access_token) { | |
| $oauthToken = $tokenResponse.access_token | |
| $completed = $true | |
| } | |
| } | |
| catch { | |
| # Continue polling | |
| } | |
| } | |
| } | |
| if ($oauthToken) { | |
| Write-Host "`nSuccess! GitHub OAuth token obtained." -ForegroundColor Green | |
| # Validate token works | |
| Write-Host "`nValidating token..." -ForegroundColor Cyan | |
| try { | |
| $testResponse = Invoke-RestMethod -Uri "https://api.githubcopilot.com/models" -Headers @{ | |
| "Authorization" = "Bearer $oauthToken" | |
| "Content-Type" = "application/json" | |
| "Copilot-Integration-Id" = "vscode-chat" | |
| } -TimeoutSec 5 | |
| Write-Host "Token validated successfully!" -ForegroundColor Green | |
| } | |
| catch { | |
| Write-Host "Warning: Token validation failed. The token might not be working correctly." -ForegroundColor Red | |
| Write-Host "Error: $_" -ForegroundColor Red | |
| } | |
| # Default models | |
| $defaultModel = "claude-3.7-sonnet" | |
| $defaultWeakModel = "gpt-4o-mini" | |
| # Query available models with retry logic | |
| Write-Host "`nFetching available models from GitHub Copilot..." -ForegroundColor Cyan | |
| $maxRetries = 3 | |
| $retryCount = 0 | |
| $modelsResponse = $null | |
| while ($retryCount -lt $maxRetries -and -not $modelsResponse) { | |
| try { | |
| $modelsResponse = Invoke-RestMethod -Uri "https://api.githubcopilot.com/models" -Headers @{ | |
| "Authorization" = "Bearer $oauthToken" | |
| "Content-Type" = "application/json" | |
| "Copilot-Integration-Id" = "vscode-chat" | |
| } -TimeoutSec 10 | |
| } | |
| catch { | |
| $retryCount++ | |
| if ($retryCount -lt $maxRetries) { | |
| Write-Host " Retry $retryCount of $maxRetries..." -ForegroundColor Yellow | |
| Start-Sleep -Seconds 2 | |
| } | |
| } | |
| } | |
| if ($modelsResponse) { | |
| $availableModels = $modelsResponse.data.id | |
| # Display available models with better formatting | |
| Write-Host "`n╔════════════════════════════════════════╗" -ForegroundColor Cyan | |
| Write-Host "║ Available Models ║" -ForegroundColor Cyan | |
| Write-Host "╚════════════════════════════════════════╝" -ForegroundColor Cyan | |
| # Group models by provider for better readability | |
| $gptModels = $availableModels | Where-Object { $_ -like "gpt-*" -or $_ -like "o1*" -or $_ -like "o3*" -or $_ -like "o4*" } | |
| $claudeModels = $availableModels | Where-Object { $_ -like "claude-*" } | |
| $geminiModels = $availableModels | Where-Object { $_ -like "gemini-*" } | |
| $otherModels = $availableModels | Where-Object { $_ -notlike "gpt-*" -and $_ -notlike "o1*" -and $_ -notlike "o3*" -and $_ -notlike "o4*" -and $_ -notlike "claude-*" -and $_ -notlike "gemini-*" -and $_ -notlike "text-embedding-*" } | |
| $modelIndex = 1 | |
| if ($gptModels) { | |
| Write-Host "`n OpenAI Models:" -ForegroundColor Yellow | |
| foreach ($model in $gptModels) { | |
| $num = [array]::IndexOf($availableModels, $model) + 1 | |
| Write-Host " $num. openai/$model" -ForegroundColor White | |
| } | |
| } | |
| if ($claudeModels) { | |
| Write-Host "`n Claude Models:" -ForegroundColor Yellow | |
| foreach ($model in $claudeModels) { | |
| $num = [array]::IndexOf($availableModels, $model) + 1 | |
| Write-Host " $num. openai/$model" -ForegroundColor White | |
| } | |
| } | |
| if ($geminiModels) { | |
| Write-Host "`n Gemini Models:" -ForegroundColor Yellow | |
| foreach ($model in $geminiModels) { | |
| $num = [array]::IndexOf($availableModels, $model) + 1 | |
| Write-Host " $num. openai/$model" -ForegroundColor White | |
| } | |
| } | |
| if ($otherModels) { | |
| Write-Host "`n Other Models:" -ForegroundColor Yellow | |
| foreach ($model in $otherModels) { | |
| $num = [array]::IndexOf($availableModels, $model) + 1 | |
| Write-Host " $num. openai/$model" -ForegroundColor White | |
| } | |
| } | |
| # Check if default models are in the available models list | |
| $defaultModelIndex = [array]::IndexOf($availableModels, $defaultModel) | |
| $defaultWeakModelIndex = [array]::IndexOf($availableModels, $defaultWeakModel) | |
| # Prompt for primary model selection | |
| Write-Host "`n┌─ Select Primary Model ─────────────────┐" -ForegroundColor Cyan | |
| if ($defaultModelIndex -ge 0) { | |
| Write-Host " Press Enter for: $($defaultModelIndex + 1). $defaultModel" -ForegroundColor Cyan | |
| } | |
| else { | |
| Write-Host " Default '$defaultModel' not available" -ForegroundColor Yellow | |
| } | |
| Write-Host " Or enter number (1-$($availableModels.Count)) or model name:" -ForegroundColor Cyan | |
| Write-Host "└────────────────────────────────────────┘" -ForegroundColor Cyan | |
| Write-Host -NoNewline "> " | |
| $modelSelection = Read-Host | |
| # Process primary model selection | |
| if ([string]::IsNullOrWhiteSpace($modelSelection)) { | |
| $selectedModel = $defaultModel | |
| } | |
| elseif ($modelSelection -match '^\d+$' -and [int]$modelSelection -ge 1 -and [int]$modelSelection -le $availableModels.Count) { | |
| $selectedModel = $availableModels[[int]$modelSelection - 1] | |
| } | |
| elseif ($availableModels -contains $modelSelection) { | |
| $selectedModel = $modelSelection | |
| } | |
| else { | |
| Write-Host " Invalid selection, using default: $defaultModel" -ForegroundColor Yellow | |
| $selectedModel = $defaultModel | |
| } | |
| # Prompt for weak model selection | |
| Write-Host "`n┌─ Select Weak Model (for simple tasks) ─┐" -ForegroundColor Cyan | |
| if ($defaultWeakModelIndex -ge 0) { | |
| Write-Host " Press Enter for: $($defaultWeakModelIndex + 1). $defaultWeakModel" -ForegroundColor Cyan | |
| } | |
| else { | |
| Write-Host " Default '$defaultWeakModel' not available" -ForegroundColor Cyan | |
| } | |
| Write-Host " Or enter number (1-$($availableModels.Count)) or model name:" -ForegroundColor Cyan | |
| Write-Host "└────────────────────────────────────────┘" -ForegroundColor Cyan | |
| Write-Host -NoNewline "> " | |
| $weakModelSelection = Read-Host | |
| # Process weak model selection | |
| if ([string]::IsNullOrWhiteSpace($weakModelSelection)) { | |
| $selectedWeakModel = $defaultWeakModel | |
| } | |
| elseif ($weakModelSelection -match '^\d+$' -and [int]$weakModelSelection -ge 1 -and [int]$weakModelSelection -le $availableModels.Count) { | |
| $selectedWeakModel = $availableModels[[int]$weakModelSelection - 1] | |
| } | |
| elseif ($availableModels -contains $weakModelSelection) { | |
| $selectedWeakModel = $weakModelSelection | |
| } | |
| else { | |
| Write-Host " Invalid selection, using default: $defaultWeakModel" -ForegroundColor Yellow | |
| $selectedWeakModel = $defaultWeakModel | |
| } | |
| Write-Host "`n✅ Selected models:" -ForegroundColor Green | |
| Write-Host " Primary model: openai/$selectedModel" -ForegroundColor White | |
| Write-Host " Weak model: openai/$selectedWeakModel" -ForegroundColor White | |
| } | |
| else { | |
| Write-Host "`nFailed to fetch models from GitHub Copilot after $maxRetries attempts. Using default models." -ForegroundColor Red | |
| $selectedModel = $defaultModel | |
| $selectedWeakModel = $defaultWeakModel | |
| Write-Host "`nUsing default models:" -ForegroundColor Yellow | |
| Write-Host " Primary model: openai/$selectedModel" -ForegroundColor Yellow | |
| Write-Host " Weak model: openai/$selectedWeakModel" -ForegroundColor Yellow | |
| } | |
| # Prepare config content with selected models | |
| $configContent = @" | |
| openai-api-base: https://api.githubcopilot.com | |
| openai-api-key: "$oauthToken" | |
| model: openai/$selectedModel | |
| weak-model: openai/$selectedWeakModel | |
| show-model-warnings: false | |
| "@ | |
| # Option to directly write to ~/.aider.conf.yml | |
| Write-Host "`n┌─ Save Configuration? ──────────────────┐" -ForegroundColor Cyan | |
| Write-Host "│ Write config to ~/.aider.conf.yml? │" -ForegroundColor Cyan | |
| Write-Host "│ Enter 'yes' or 'y' to save │" -ForegroundColor Cyan | |
| Write-Host "│ Press Enter or 'n' to skip │" -ForegroundColor Cyan | |
| Write-Host "└────────────────────────────────────────┘" -ForegroundColor Cyan | |
| Write-Host -NoNewline "> " | |
| $writeToConfig = Read-Host | |
| if ($writeToConfig.ToLower() -eq 'yes' -or $writeToConfig.ToLower() -eq 'y') { | |
| try { | |
| # Check if file exists and offer to backup | |
| if (Test-Path $aiderConfigPath) { | |
| Write-Host "Existing config file found at $aiderConfigPath" -ForegroundColor Yellow | |
| $backupChoice = Read-Host "Would you like to create a backup? (yes/no)" | |
| if ($backupChoice.ToLower() -eq 'yes' -or $backupChoice.ToLower() -eq 'y') { | |
| $backupPath = "$aiderConfigPath.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')" | |
| Copy-Item -Path $aiderConfigPath -Destination $backupPath | |
| Write-Host " ✅ Backup created at:" -ForegroundColor Green | |
| Write-Host " $backupPath" -ForegroundColor Gray | |
| } | |
| # Read existing config to preserve user's custom settings | |
| $existingLines = Get-Content $aiderConfigPath | |
| $updatedConfig = @{} | |
| # Parse existing config into a hashtable | |
| foreach ($line in $existingLines) { | |
| if ($line -match '^([^#]+?):\s*(.*)$') { | |
| $key = $matches[1].Trim() | |
| $value = $matches[2].Trim() | |
| $updatedConfig[$key] = $value | |
| } | |
| } | |
| # Update only the specific keys we need to change | |
| $updatedConfig['openai-api-base'] = 'https://api.githubcopilot.com' | |
| $updatedConfig['openai-api-key'] = "`"$oauthToken`"" | |
| $updatedConfig['model'] = "openai/$selectedModel" | |
| $updatedConfig['weak-model'] = "openai/$selectedWeakModel" | |
| $updatedConfig['show-model-warnings'] = 'false' | |
| # Build the updated config content preserving all keys | |
| $finalConfig = @() | |
| foreach ($key in $updatedConfig.Keys) { | |
| $finalConfig += "${key}: $($updatedConfig[$key])" | |
| } | |
| # Write the merged config without BOM | |
| $utf8NoBom = New-Object System.Text.UTF8Encoding $false | |
| [System.IO.File]::WriteAllText($aiderConfigPath, ($finalConfig -join "`n"), $utf8NoBom) | |
| Write-Host "`n✅ Configuration successfully updated in $aiderConfigPath" -ForegroundColor Green | |
| Write-Host " Your existing settings have been preserved." -ForegroundColor Green | |
| } | |
| else { | |
| # No existing config, write new one without BOM | |
| $utf8NoBom = New-Object System.Text.UTF8Encoding $false | |
| [System.IO.File]::WriteAllText($aiderConfigPath, $configContent, $utf8NoBom) | |
| Write-Host "`n✅ Configuration successfully written to $aiderConfigPath" -ForegroundColor Green | |
| } | |
| } | |
| catch { | |
| Write-Host "`n❌ Failed to write to $aiderConfigPath" -ForegroundColor Red | |
| Write-Host " Error: $_" -ForegroundColor Red | |
| Write-Host " You may need to run this script with administrator privileges." -ForegroundColor Yellow | |
| } | |
| } | |
| else { | |
| Write-Host "`nSkipped writing to config file." -ForegroundColor Yellow | |
| } | |
| # Offer to patch exceptions after successful configuration | |
| Write-Host "`n┌─ Patch Aider for GitHub Copilot? ─────┐" -ForegroundColor Cyan | |
| Write-Host "│ GitHub models often fail with 400 │" -ForegroundColor Cyan | |
| Write-Host "│ BadRequestError. A patch can fix this.│" -ForegroundColor Cyan | |
| Write-Host "│ │" -ForegroundColor Cyan | |
| Write-Host "│ Apply patch? (yes/no) │" -ForegroundColor Cyan | |
| Write-Host "└───────────────────────────────────────┘" -ForegroundColor Cyan | |
| Write-Host -NoNewline "> " | |
| $patchChoice = Read-Host | |
| if ($patchChoice.ToLower() -eq 'yes' -or $patchChoice.ToLower() -eq 'y') { | |
| Patch-AiderExceptions | |
| } | |
| else { | |
| Write-Host "Skipped patching. You can run Update-AiderCopilot later and select option 3 to patch." -ForegroundColor Yellow | |
| } | |
| # Install commandlet function (moved here for reuse) | |
| function Install-UpdateAiderCopilot { | |
| $cmdletPath = "$env:LOCALAPPDATA\Programs\aider\Update-AiderCopilot.ps1" | |
| $cmdletContent = @' | |
| <# | |
| .SYNOPSIS | |
| Updates Aider configuration with GitHub Copilot OAuth token | |
| .DESCRIPTION | |
| Re-runs the Aider Copilot authentication script to update configuration or regenerate tokens | |
| .EXAMPLE | |
| Update-AiderCopilot | |
| #> | |
| function global:Update-AiderCopilot { | |
| [CmdletBinding()] | |
| param() | |
| Write-Host "Checking for updates..." -ForegroundColor Cyan | |
| try { | |
| $timestamp = [DateTime]::Now.Ticks | |
| $gistUrl = 'https://gist.githubusercontent.com/dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39/raw?t=' + $timestamp | |
| $localScript = "$env:TEMP\aider_copilot_auth_temp_$timestamp.ps1" | |
| # Download latest version | |
| $headers = @{ | |
| 'Cache-Control' = 'no-cache, no-store, must-revalidate' | |
| 'Pragma' = 'no-cache' | |
| 'Expires' = '0' | |
| } | |
| # Force fresh download | |
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | |
| $webClient = New-Object System.Net.WebClient | |
| $webClient.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate") | |
| $webClient.Headers.Add("Pragma", "no-cache") | |
| $webClient.DownloadFile($gistUrl, $localScript) | |
| # Execute the script in a new scope | |
| & $localScript | |
| # Clean up | |
| Remove-Item $localScript -Force -ErrorAction SilentlyContinue | |
| } | |
| catch { | |
| Write-Host "Failed to update. Running from fallback URL..." -ForegroundColor Yellow | |
| $timestamp = [DateTime]::Now.Ticks | |
| $fallbackUrl = "https://gist.githubusercontent.com/dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39/raw?t=$timestamp" | |
| $scriptContent = Invoke-RestMethod -Uri $fallbackUrl -Headers @{'Cache-Control'='no-cache'} | |
| Invoke-Expression $scriptContent | |
| } | |
| } | |
| '@ | |
| Write-Host "`n📦 Installing Update-AiderCopilot commandlet..." -ForegroundColor Cyan | |
| try { | |
| # Create the commandlet file | |
| $cmdletDir = Split-Path $cmdletPath -Parent | |
| if (-not (Test-Path $cmdletDir)) { | |
| New-Item -ItemType Directory -Path $cmdletDir -Force | Out-Null | |
| } | |
| Set-Content -Path $cmdletPath -Value $cmdletContent -Encoding UTF8 -NoNewline | |
| # Add to PowerShell profile for auto-loading | |
| $profilePath = $PROFILE.CurrentUserAllHosts | |
| $profileDir = Split-Path $profilePath -Parent | |
| if (-not (Test-Path $profileDir)) { | |
| New-Item -ItemType Directory -Path $profileDir -Force | Out-Null | |
| } | |
| # Remove old import command format if exists | |
| if (Test-Path $profilePath) { | |
| $profileContent = Get-Content $profilePath -Raw | |
| # Ensure profileContent is a string | |
| if ($profileContent -is [array]) { | |
| $profileContent = $profileContent -join "`n" | |
| } | |
| if (-not $profileContent) { | |
| $profileContent = "" | |
| } | |
| # Escape the path for use in regex | |
| $escapedPath = [regex]::Escape($cmdletPath) | |
| $oldPattern = "\. `"$escapedPath`"" | |
| $profileContent = $profileContent -replace $oldPattern, "" | |
| # Also remove any duplicate entries | |
| $escapedImport = [regex]::Escape("Import-Module `"$cmdletPath`" -Force -Global") | |
| $profileContent = $profileContent -replace $escapedImport, "" | |
| # Clean up any multiple newlines | |
| $profileContent = $profileContent -replace "(\r?\n){3,}", "`n`n" | |
| Set-Content -Path $profilePath -Value $profileContent.TrimEnd() -Encoding UTF8 -NoNewline | |
| } | |
| # Use dot-sourcing since it's a regular script, not a module | |
| $importCommand = ". `"$cmdletPath`"" | |
| if (Test-Path $profilePath) { | |
| $profileContent = Get-Content $profilePath -Raw | |
| if ($profileContent -notlike "*$importCommand*") { | |
| Add-Content -Path $profilePath -Value "`n$importCommand" | |
| } | |
| } | |
| else { | |
| Set-Content -Path $profilePath -Value $importCommand -Encoding UTF8 -NoNewline | |
| } | |
| # Force reload for current session by dot-sourcing | |
| . $cmdletPath | |
| Write-Host "`n✅ Update-AiderCopilot commandlet installed!" -ForegroundColor Green | |
| Write-Host " 📌 You can now run 'Update-AiderCopilot' from any PowerShell" -ForegroundColor White | |
| Write-Host " session to reconfigure Aider or update this script." -ForegroundColor White | |
| Write-Host " 🔄 The commandlet auto-updates from the latest gist version." -ForegroundColor Gray | |
| } | |
| catch { | |
| Write-Host "⚠️ Failed to install Update-AiderCopilot commandlet: $_" -ForegroundColor Yellow | |
| Write-Host " You can still re-run the configuration using:" -ForegroundColor White | |
| Write-Host " iex (Invoke-RestMethod 'https://gist.githubusercontent.com/dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39/raw')" -ForegroundColor Gray | |
| } | |
| } | |
| # Always install/update commandlet | |
| Install-UpdateAiderCopilot | |
| } | |
| else { | |
| Write-Host "`n❌ Failed to obtain a GitHub OAuth token. Authentication process did not complete." -ForegroundColor Red | |
| # Install commandlet even on failure | |
| if (-not $cmdletInstalled) { | |
| Write-Host "`nInstalling Update-AiderCopilot for future use..." -ForegroundColor Cyan | |
| Install-UpdateAiderCopilot | |
| } | |
| return | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment