# Tumpai Handbook ยท Claude Code installer for Windows # # Recommended: # try { $env:CLAUDE_TUMPAI_NO_PAUSE='1'; $u='https://tumpai-handbook.pages.dev/install-claude.ps1'; iex ([System.Text.Encoding]::UTF8.GetString((New-Object Net.WebClient).DownloadData($u))) } catch { Write-Host ('Installer failed to start: ' + $_.Exception.Message) -ForegroundColor Red } finally { if (-not [Console]::IsInputRedirected) { Read-Host 'Press Enter to exit' } } # # Unattended: # $env:TUMPAI_API_KEY='sk-xxx'; $env:CLAUDE_TUMPAI_YES='1'; try { $env:CLAUDE_TUMPAI_NO_PAUSE='1'; $u='https://tumpai-handbook.pages.dev/install-claude.ps1'; iex ([System.Text.Encoding]::UTF8.GetString((New-Object Net.WebClient).DownloadData($u))) } catch { Write-Host ('Installer failed to start: ' + $_.Exception.Message) -ForegroundColor Red } finally { if (-not [Console]::IsInputRedirected) { Read-Host 'Press Enter to exit' } } $ErrorActionPreference = "Stop" try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) $OutputEncoding = [Console]::OutputEncoding if ($PSVersionTable.PSEdition -eq "Desktop") { chcp 65001 > $null } } catch {} $LogFile = Join-Path $env:TEMP "claude-tumpai-install-last.log" function Add-Log { param([string]$Message) if (-not $LogFile) { return } try { Add-Content -Encoding UTF8 -Path $LogFile -Value $Message } catch {} } try { "Claude Code installer started at $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Set-Content -Encoding UTF8 -Path $LogFile } catch {} $BaseUrl = if ($env:ANTHROPIC_BASE_URL) { $env:ANTHROPIC_BASE_URL } elseif ($env:CLAUDE_BASE_URL) { $env:CLAUDE_BASE_URL } else { "https://api.tumpai.site:2053" } $BaseUrl = $BaseUrl.TrimEnd("/") if ($BaseUrl.EndsWith("/v1")) { $BaseUrl = $BaseUrl.Substring(0, $BaseUrl.Length - 3).TrimEnd("/") } $NonInteractive = $false try { if ([Console]::IsInputRedirected) { $NonInteractive = $true } } catch { $NonInteractive = $true } $AssumeYes = ($env:CLAUDE_TUMPAI_YES -eq "1") -or ($env:CLAUDE_YES -eq "1") -or $NonInteractive function Write-Title { param([string]$Message) Write-Host "" Write-Host "==> $Message" -ForegroundColor Cyan Add-Log "==> $Message" } function Write-Ok { param([string]$Message) Write-Host "OK: $Message" -ForegroundColor Green Add-Log "OK: $Message" } function Write-Info { param([string]$Message) Write-Host "INFO: $Message" -ForegroundColor Cyan Add-Log "INFO: $Message" } function Write-Warn2 { param([string]$Message) Write-Host "WARN: $Message" -ForegroundColor Yellow Add-Log "WARN: $Message" } function Write-Fail { param([string]$Message) throw $Message } function Wait-BeforeExit { if ($env:CLAUDE_TUMPAI_NO_PAUSE -eq "1" -or $env:CI -eq "true") { return } try { Write-Host "" Read-Host "Press Enter to exit" | Out-Null } catch { try { cmd.exe /c pause | Out-Null } catch {} } } function Handle-InstallError { param([object]$Err) Write-Host "" Write-Host ("ERROR: Installation stopped: " + $Err.Exception.Message) -ForegroundColor Red Add-Log ("ERROR: " + $Err.Exception.Message) if ($LogFile) { Write-Host ("Log file: " + $LogFile) -ForegroundColor Yellow } Wait-BeforeExit } function Read-Line { param([string]$Prompt, [string]$Default = "") if ($NonInteractive) { return $Default } try { $value = Read-Host $Prompt if ([string]::IsNullOrWhiteSpace($value)) { return $Default } return $value } catch { Write-Warn2 "Read-Host failed. Using default: $Default" return $Default } } try { Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " Tumpai Claude Code setup - Windows installer" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" Write-Host "This script will:" Write-Host " 1. Install the official Claude Code CLI if needed" Write-Host " 2. Ask for the Tumpai client API key" Write-Host " 3. Set ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN as user environment variables" Write-Host " 4. Enable CLIProxyAPI/Claude Code gateway model discovery" Write-Host " 5. Enable YOLO mode (bypassPermissions; skip all permission prompts)" Write-Host " 6. Test gateway connectivity" Write-Host "" Write-Host "Claude Code base URL will be: $BaseUrl" Write-Host "Do not add /v1 to ANTHROPIC_BASE_URL; Claude Code calls /v1/messages itself." Write-Host "Models: no pinned model IDs; Claude Code and CLIProxyAPI will use currently available models." Write-Host "Permission mode: bypassPermissions (YOLO)" Write-Host "" if ($AssumeYes) { Write-Info "Auto-confirm mode is enabled. Continuing..." } else { $go = Read-Line -Prompt "Continue? [Y/n]" -Default "Y" if ($go -and $go -notmatch '^[Yy]') { Write-Info "Cancelled." Wait-BeforeExit return } } Write-Title "1/4 Installing Claude Code" $claude = Get-Command claude -ErrorAction SilentlyContinue if ($claude) { try { Write-Ok ("Claude Code is installed: " + (claude --version)) } catch { Write-Ok "Claude Code is installed" } } else { Write-Info "Running the official Claude Code installer" Invoke-RestMethod https://claude.ai/install.ps1 | Invoke-Expression $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") $claude = Get-Command claude -ErrorAction SilentlyContinue if ($claude) { try { Write-Ok ("Claude Code installed: " + (claude --version)) } catch { Write-Ok "Claude Code installed" } } else { Write-Warn2 "The installer finished, but claude is not visible in the current PATH. Open a new PowerShell window after this script." } } Write-Title "2/4 API key" $ApiKey = $env:TUMPAI_API_KEY if (-not $ApiKey) { $ApiKey = $env:ANTHROPIC_AUTH_TOKEN } if (-not $ApiKey) { $ApiKey = $env:CLAUDE_API_KEY } if ($ApiKey) { Write-Ok ("Read API key from environment (" + $ApiKey.Length + " chars)") } elseif ($NonInteractive) { Write-Fail "Non-interactive mode requires TUMPAI_API_KEY or ANTHROPIC_AUTH_TOKEN." } else { Write-Host "Paste the Tumpai client API key issued by the service provider." Write-Host "It will be saved as ANTHROPIC_AUTH_TOKEN for Claude Code." Write-Host "" while (-not $ApiKey) { $ApiKey = Read-Host "API Key" if (-not $ApiKey) { Write-Warn2 "API key is empty. Try again." } } Write-Ok ("Received API key (" + $ApiKey.Length + " chars)") } Write-Title "3/4 Writing user environment variables" [System.Environment]::SetEnvironmentVariable("TUMPAI_API_KEY", $ApiKey, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_AUTH_TOKEN", $ApiKey, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_BASE_URL", $BaseUrl, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_OPUS_MODEL", $null, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_SONNET_MODEL", $null, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_DEFAULT_HAIKU_MODEL", $null, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_MODEL", $null, "User") [System.Environment]::SetEnvironmentVariable("ANTHROPIC_SMALL_FAST_MODEL", $null, "User") [System.Environment]::SetEnvironmentVariable("CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY", "1", "User") $env:TUMPAI_API_KEY = $ApiKey $env:ANTHROPIC_AUTH_TOKEN = $ApiKey $env:ANTHROPIC_BASE_URL = $BaseUrl Remove-Item Env:ANTHROPIC_DEFAULT_OPUS_MODEL -ErrorAction SilentlyContinue Remove-Item Env:ANTHROPIC_DEFAULT_SONNET_MODEL -ErrorAction SilentlyContinue Remove-Item Env:ANTHROPIC_DEFAULT_HAIKU_MODEL -ErrorAction SilentlyContinue Remove-Item Env:ANTHROPIC_MODEL -ErrorAction SilentlyContinue Remove-Item Env:ANTHROPIC_SMALL_FAST_MODEL -ErrorAction SilentlyContinue $env:CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1" Write-Ok "Wrote TUMPAI_API_KEY" Write-Ok "Wrote ANTHROPIC_AUTH_TOKEN" Write-Ok "Wrote ANTHROPIC_BASE_URL=$BaseUrl" Write-Ok "Cleared pinned Claude model environment variables" Write-Ok "Enabled CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY" $claudeConfigDir = Join-Path $HOME ".claude" $claudeSettingsFile = Join-Path $claudeConfigDir "settings.json" New-Item -ItemType Directory -Force -Path $claudeConfigDir | Out-Null if (Test-Path $claudeSettingsFile) { $settingsBackup = "$claudeSettingsFile.bak-" + (Get-Date -Format "yyyyMMdd-HHmmss") Copy-Item $claudeSettingsFile $settingsBackup Write-Ok "Backed up Claude Code settings to $settingsBackup" } try { if (Test-Path $claudeSettingsFile) { $rawSettings = Get-Content -Raw -Path $claudeSettingsFile if ([string]::IsNullOrWhiteSpace($rawSettings)) { $settings = [pscustomobject]@{} } else { $settings = $rawSettings | ConvertFrom-Json } } else { $settings = [pscustomobject]@{} } } catch { Write-Warn2 "Existing Claude Code settings.json is not valid JSON. Rewriting it after backup." $settings = [pscustomobject]@{} } if (-not $settings.PSObject.Properties["`$schema"]) { $settings | Add-Member -MemberType NoteProperty -Name '$schema' -Value "https://json.schemastore.org/claude-code-settings.json" } if (-not $settings.PSObject.Properties["permissions"] -or $null -eq $settings.permissions) { $settings | Add-Member -MemberType NoteProperty -Name "permissions" -Value ([pscustomobject]@{}) } if (-not $settings.permissions.PSObject.Properties["defaultMode"]) { $settings.permissions | Add-Member -MemberType NoteProperty -Name "defaultMode" -Value "bypassPermissions" } else { $settings.permissions.defaultMode = "bypassPermissions" } if (-not $settings.permissions.PSObject.Properties["skipDangerousModePermissionPrompt"]) { $settings.permissions | Add-Member -MemberType NoteProperty -Name "skipDangerousModePermissionPrompt" -Value $true } else { $settings.permissions.skipDangerousModePermissionPrompt = $true } $settingsJson = $settings | ConvertTo-Json -Depth 20 Write-Utf8NoBom -Path $claudeSettingsFile -Content ($settingsJson + "`n") Write-Ok "Enabled Claude Code YOLO mode: permissions.defaultMode=bypassPermissions" Write-Title "4/4 Testing gateway" try { $response = Invoke-WebRequest -Uri "$BaseUrl/v1/models" -Headers @{ "Authorization" = "Bearer $ApiKey" } -UseBasicParsing -TimeoutSec 15 if ($response.StatusCode -eq 200) { Write-Ok "Gateway is reachable and the key looks valid" } else { Write-Warn2 ("HTTP " + $response.StatusCode + ". You can continue and test Claude Code manually.") } } catch { $code = $null try { $code = $_.Exception.Response.StatusCode.value__ } catch {} if ($code -eq 401 -or $code -eq 403) { Write-Warn2 "The key was rejected (HTTP $code). Check your API key." } else { Write-Warn2 "Connectivity test failed: $($_.Exception.Message). You can try again later." } } Write-Host "" Write-Host "================================================" -ForegroundColor Green Write-Host " Install complete" -ForegroundColor Green Write-Host "================================================" -ForegroundColor Green Write-Host "" Write-Host "Important: open a new PowerShell window before running Claude Code." -ForegroundColor Yellow Write-Host "" Write-Host "Next:" Write-Host " 1. Close this PowerShell window." Write-Host " 2. Open a new PowerShell window." Write-Host " 3. Run: claude" Write-Host "" Write-Host "If Claude Code cannot connect, check:" Write-Host " ANTHROPIC_BASE_URL=$BaseUrl" Write-Host " ANTHROPIC_AUTH_TOKEN is the Tumpai client API key" Write-Host " Use /model in Claude Code if you need to select a specific gateway model" Write-Host " The gateway supports POST /v1/messages" Write-Host " YOLO settings file: $claudeSettingsFile" Write-Host "" Write-Host "Log file: $LogFile" Write-Host "" Wait-BeforeExit } catch { Handle-InstallError $_ return }