# ═══════════════════════════════════════════════════════════════ # UltraCode One-Click Setup Script (Downloadable version) # Validates API key → Sets env vars → Configures Claude Code → Installs CLI # ═══════════════════════════════════════════════════════════════ # Usage: Right-click → Run with PowerShell # OR: powershell -ExecutionPolicy Bypass -File setup-ultracode.ps1 # ═══════════════════════════════════════════════════════════════ param( [string]$ApiKey = "", [string]$Model = "claude-opus-5-5", [string]$BaseUrl = "https://api.ultracode.in" ) # Colors function Green { param($t) Write-Host " $t" -ForegroundColor Green } function Red { param($t) Write-Host " $t" -ForegroundColor Red } function Yellow { param($t) Write-Host " $t" -ForegroundColor Yellow } function Cyan { param($t) Write-Host " $t" -ForegroundColor Cyan } function Gray { param($t) Write-Host " $t" -ForegroundColor DarkGray } Write-Host "" Write-Host " ╔══════════════════════════════════════════╗" -ForegroundColor Cyan Write-Host " ║ UltraCode Setup for Windows ║" -ForegroundColor Cyan Write-Host " ╚══════════════════════════════════════════╝" -ForegroundColor Cyan Write-Host "" # ── Helpers ── function Read-JsonFile { param([string]$Path) if (Test-Path $Path) { try { $raw = Get-Content -Path $Path -Raw -ErrorAction Stop if ([string]::IsNullOrWhiteSpace($raw)) { return @{} } return $raw | ConvertFrom-Json -ErrorAction Stop } catch { return @{} } } return @{} } function Write-JsonFile { param([string]$Path, [object]$Data) $dir = Split-Path -Parent $Path if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } $Data | ConvertTo-Json -Depth 10 | Set-Content -Path $Path -Encoding UTF8 } function Set-JsonProp { param([psobject]$Obj, [string]$Name, [object]$Value) if ($Obj.PSObject.Properties.Name -contains $Name) { $Obj.$Name = $Value } else { $Obj | Add-Member -NotePropertyName $Name -NotePropertyValue $Value } } function Ensure-Property { param([psobject]$Obj, [string]$Name, [object]$Default) if (-not ($Obj.PSObject.Properties.Name -contains $Name)) { $Obj | Add-Member -NotePropertyName $Name -NotePropertyValue $Default } } # ─── Step 1: Get API Key ─── if (-not $ApiKey) { Write-Host " Enter your UltraCode API key" -ForegroundColor White Gray " (Get it from your admin dashboard)" Write-Host "" $ApiKey = Read-Host " API Key" Write-Host "" } if (-not $ApiKey -or $ApiKey.Length -lt 5) { Red "✗ No valid API key provided. Exiting." Write-Host "" Read-Host " Press Enter to exit" exit 1 } # ─── Step 2: Validate API Key ─── Cyan "[1/4] Validating API key..." Write-Host "" try { $headers = @{ "Content-Type" = "application/json" "x-api-key" = $ApiKey "anthropic-version" = "2023-06-01" } $body = @{ model = $Model max_tokens = 10 messages = @( @{ role = "user" content = "Hi" } ) } | ConvertTo-Json -Depth 5 $response = Invoke-RestMethod -Uri "$BaseUrl/messages" -Method Post -Headers $headers -Body $body -TimeoutSec 30 -ErrorAction Stop Green "✓ API key is valid!" Green "✓ Connected to UltraCode successfully" if ($response.model) { Green "✓ Model: $($response.model)" } Write-Host "" } catch { $statusCode = $null if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode } if ($statusCode -eq 401 -or $statusCode -eq 403) { Red "✗ Invalid API key! Please check your key and try again." Write-Host "" Gray " Make sure you're using a valid UltraCode API key (sk-uc-...)" Gray " Get your key from the admin dashboard." } elseif ($statusCode -eq 429) { Yellow "⚠ API key is valid but rate limited. Proceeding with setup..." Write-Host "" } elseif ($statusCode -eq 402) { Red "✗ API key is valid but has no remaining balance." Gray " Please recharge your account and try again." } else { Red "✗ Could not connect to UltraCode API." Gray " Error: $($_.Exception.Message)" Gray " Please check your internet connection and try again." } if ($statusCode -eq 401 -or $statusCode -eq 403 -or $statusCode -eq 402) { Write-Host "" Read-Host " Press Enter to exit" exit 1 } if (-not ($statusCode -eq 429)) { Write-Host "" $continue = Read-Host " Continue anyway? (y/n)" if ($continue -ne "y") { exit 1 } Write-Host "" } } # ─── Step 3: Check Node.js ─── Cyan "[2/4] Checking Node.js..." $node = Get-Command node -ErrorAction SilentlyContinue if (-not $node) { Yellow "⚠ Node.js not found!" Gray " Claude Code CLI requires Node.js." Gray " Download it from: https://nodejs.org/" Write-Host "" Gray " After installing Node.js, re-run this script." Write-Host "" Read-Host " Press Enter to exit" exit 1 } else { $nodeVersion = node --version Green "✓ Node.js found: $nodeVersion" } # ─── Clear old system env vars (prevent auth conflicts) ─── $staleVars = @( "ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","ANTHROPIC_BASE_URL", "ANTHROPIC_MODEL","ANTHROPIC_SMALL_FAST_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL","ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_HAIKU_MODEL","CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC" ) foreach ($v in $staleVars) { [System.Environment]::SetEnvironmentVariable($v, $null, "User") } # ─── Step 4: Configure Claude Code (~/.claude/settings.json) ─── Cyan "[3/4] Configuring Claude Code settings..." try { $settingsPath = Join-Path (Join-Path $env:USERPROFILE ".claude") "settings.json" $settings = Read-JsonFile -Path $settingsPath Ensure-Property -Obj $settings -Name "env" -Default ([PSCustomObject]@{}) # Mirror exactly what the published CLI (ultracode-live configureCli) writes — and nothing more. # All three CLAUDE_CODE_* keys must stay as a set — remove any one and Claude Code silently uses # different defaults with nothing erroring to explain it. $envVars = @{ "ANTHROPIC_AUTH_TOKEN" = $ApiKey "ANTHROPIC_BASE_URL" = $BaseUrl "CLAUDE_CODE_OAUTH_TOKEN" = $ApiKey "CLAUDE_CODE_SUBSCRIPTION_TYPE" = "max" "CLAUDE_CODE_REMOTE" = "1" } foreach ($key in $envVars.Keys) { Set-JsonProp -Obj $settings.env -Name $key -Value $envVars[$key] } # Remove stale model pins — the same list the CLI deletes; any of these left behind would make # its `doctor` report the machine as misconfigured even though the key and URL are correct. # (This script used to SET them, and a pinned `model` of its own.) $staleModelVars = @( 'ANTHROPIC_MODEL','ANTHROPIC_DEFAULT_MODEL','ANTHROPIC_REASONING_MODEL', 'CLAUDE_CODE_SUBAGENT_MODEL','ANTHROPIC_SMALL_FAST_MODEL', 'ANTHROPIC_DEFAULT_SONNET_MODEL','ANTHROPIC_DEFAULT_OPUS_MODEL','ANTHROPIC_DEFAULT_HAIKU_MODEL' ) foreach ($k in $staleModelVars) { if ($settings.env.PSObject.Properties.Name -contains $k) { $settings.env.PSObject.Properties.Remove($k) } } if ($settings.PSObject.Properties.Name -contains 'model') { $settings.PSObject.Properties.Remove('model') } Write-JsonFile -Path $settingsPath -Data $settings Green "✓ $settingsPath" } catch { Red "✗ Failed to configure Claude Code: $_" } Write-Host "" # ─── Clean up stale MCP config from prior installs ─── try { $claudeJsonPath = Join-Path $env:USERPROFILE ".claude.json" if (Test-Path $claudeJsonPath) { $claudeJson = Read-JsonFile -Path $claudeJsonPath if ($claudeJson.PSObject.Properties.Name -contains "mcpServers" -and $claudeJson.mcpServers.PSObject.Properties.Name -contains "UltraCode") { $claudeJson.mcpServers.PSObject.Properties.Remove("UltraCode") if ($claudeJson.mcpServers.PSObject.Properties.Count -eq 0) { $claudeJson.PSObject.Properties.Remove("mcpServers") } Set-JsonProp -Obj $claudeJson -Name "hasCompletedOnboarding" -Value $true Write-JsonFile -Path $claudeJsonPath -Data $claudeJson } } } catch { } Write-Host "" # ─── Step 6: Install Claude CLI ─── $claude = Get-Command claude -ErrorAction SilentlyContinue if (-not $claude) { Write-Host "" Cyan "[4/4] Installing Claude Code CLI..." Write-Host "" npm install -g @anthropic-ai/claude-code 2>$null if ($LASTEXITCODE -eq 0) { Green "✓ Claude Code CLI installed!" } else { Yellow "⚠ Auto-install failed. Try manually:" Gray " npm install -g @anthropic-ai/claude-code" } } else { Green "✓ Claude Code CLI already installed" } # ─── Done ─── Write-Host "" Write-Host " ╔══════════════════════════════════════════╗" -ForegroundColor Green Write-Host " ║ ✓ Setup complete! ║" -ForegroundColor Green Write-Host " ╚══════════════════════════════════════════╝" -ForegroundColor Green Write-Host "" Write-Host " What was configured:" -ForegroundColor White Write-Host " • API key validated ✓" -ForegroundColor DarkGray Write-Host " • Claude Code settings (~/.claude/settings.json)" -ForegroundColor DarkGray Write-Host " • Claude Code CLI" -ForegroundColor DarkGray Write-Host "" # Summary box # Rows are padded at run time, so the host and the model can be any length and the box stays closed. $rows = @( "Base URL: api.ultracode.in", "Models: all four, chosen per request", "Key: $($ApiKey.Substring(0, [Math]::Min(8, $ApiKey.Length)))..." ) $w = ($rows | ForEach-Object { $_.Length } | Measure-Object -Maximum).Maximum + 4 Write-Host (" ╭" + ("─" * $w) + "╮") -ForegroundColor DarkGray foreach ($r in $rows) { Write-Host (" │ " + $r.PadRight($w - 2) + "│") -ForegroundColor DarkGray } Write-Host (" ╰" + ("─" * $w) + "╯") -ForegroundColor DarkGray Write-Host "" Gray " Restart Claude Code / VS Code to apply." Gray " Re-run anytime to update your key." Write-Host "" # Ask to launch Claude $claudeInstalled = Get-Command claude -ErrorAction SilentlyContinue if ($claudeInstalled) { $launch = Read-Host " Launch Claude Code now? (y/n)" if ($launch -eq "y" -or $launch -eq "Y" -or $launch -eq "") { Write-Host "" Write-Host " 🚀 Launching Claude Code..." -ForegroundColor Cyan Write-Host " ════════════════════════════════════" -ForegroundColor DarkGray Write-Host "" claude } else { Write-Host "" Write-Host " 👋 To start later, open any terminal and type: claude" -ForegroundColor White Write-Host "" Read-Host " Press Enter to exit" } } else { Yellow " Claude CLI not found. Install Node.js first, then run:" Gray " npm install -g @anthropic-ai/claude-code" Write-Host "" Read-Host " Press Enter to exit" }