Skip to content

Instantly share code, notes, and snippets.

@dgehriger
Last active July 12, 2025 18:46
Show Gist options
  • Save dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39 to your computer and use it in GitHub Desktop.
Save dgehriger/62e0284a5ed55f32bbe22f9a28ddcf39 to your computer and use it in GitHub Desktop.
Copilot API token for Aider
# 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