# Claude Code Configuration Script for XCodeCLI (Windows) # This script configures Claude Code to use your XCodeCLI instance # Run with: powershell -ExecutionPolicy Bypass -File setup-claude-code.ps1 -ApiKey YOUR_KEY # Or via one-liner: & { $key='YOUR_KEY'; iwr -useb https://api.xcodecli.com/setup-claude-code.ps1 | iex } param( [string]$ApiKey, [switch]$Test, [switch]$Show, [switch]$Help ) # Check for pre-set variables from one-liner command (use different names to avoid conflict) if (-not $ApiKey -and (Test-Path Variable:key)) { $ApiKey = $key } # Configuration $DefaultBaseUrl = "https://api2.xcodecli.com" $ClaudeConfigDir = "$env:USERPROFILE\.claude" $ClaudeSettingsFile = "$ClaudeConfigDir\settings.json" $ToolCommand = "claude" $ToolPackage = "@anthropic-ai/claude-code" $ToolName = "Claude Code" # ========== 工具函数 ========== function Test-Command { param([string]$Name) return [bool](Get-Command $Name -ErrorAction SilentlyContinue) } function Refresh-Path { $env:Path = [Environment]::GetEnvironmentVariable("Path", "User") + ";" + [Environment]::GetEnvironmentVariable("Path", "Machine") } # Function to write environment variable (User level) function Set-EnvVariable { param( [string]$Name, [string]$Value ) # Set for current session [Environment]::SetEnvironmentVariable($Name, $Value, [System.EnvironmentVariableTarget]::Process) # Set permanently for user [Environment]::SetEnvironmentVariable($Name, $Value, [System.EnvironmentVariableTarget]::User) Write-Info "Environment variable set: $Name" } # Color functions for output function Write-Info { param([string]$Message) Write-Host "[INFO]" -ForegroundColor Blue -NoNewline Write-Host " $Message" } function Write-Success { param([string]$Message) Write-Host "[SUCCESS]" -ForegroundColor Green -NoNewline Write-Host " $Message" } function Write-Warning { param([string]$Message) Write-Host "[WARNING]" -ForegroundColor Yellow -NoNewline Write-Host " $Message" } function Write-Error { param([string]$Message) Write-Host "[ERROR]" -ForegroundColor Red -NoNewline Write-Host " $Message" } # ========== Node.js 环境检测 ========== function Get-NodeVersion { if (Test-Command "node") { try { $versionStr = (& node --version 2>$null) -replace 'v', '' $major = [int]($versionStr -split '\.')[0] return @{ Version = $versionStr; Major = $major } } catch { } } return $null } function Install-Fnm { Write-Host "" Write-Info "正在安装 fnm (Fast Node Manager)..." try { if (Test-Command "winget") { Write-Info "使用 winget 安装 fnm..." & winget install Schniz.fnm -e --accept-source-agreements --accept-package-agreements } else { Write-Info "使用 PowerShell 脚本安装 fnm..." Invoke-Expression "& { $(Invoke-RestMethod https://fnm.vercel.app/install.ps1) }" } Refresh-Path if (Test-Command "fnm") { Write-Success "fnm 安装成功!" $fnmEnv = & fnm env --use-on-cd 2>$null if ($fnmEnv) { $fnmEnv | Out-String | Invoke-Expression } return $true } else { Write-Warning "fnm 可能已安装,但需要重新打开终端才能生效" Write-Info "请重新打开 PowerShell 后再运行此脚本" return $false } } catch { Write-Error "fnm 安装失败: $($_.Exception.Message)" return $false } } function Install-NodeWithFnm { Write-Info "使用 fnm 安装 Node.js 24.x..." try { & fnm install 24 & fnm use 24 & fnm default 24 Refresh-Path if (Test-Command "node") { $nodeInfo = Get-NodeVersion Write-Success "Node.js v$($nodeInfo.Version) 安装成功!" return $true } else { Write-Warning "Node.js 可能已安装,但需要重新打开终端才能生效" return $false } } catch { Write-Error "Node.js 安装失败: $($_.Exception.Message)" return $false } } function Ensure-NodeEnvironment { $nodeInfo = Get-NodeVersion if ($nodeInfo) { Write-Info "检测到 Node.js v$($nodeInfo.Version)" if ($nodeInfo.Major -lt 20) { Write-Warning "Node.js 版本过低 (需要 >= 20.x)" $upgrade = Read-Host "是否使用 fnm 安装 Node.js 24.x? (Y/n)" if ($upgrade -eq "n" -or $upgrade -eq "N") { Write-Error "Node.js 版本不满足要求,请手动升级后重试" return $false } if (-not (Test-Command "fnm")) { if (-not (Install-Fnm)) { return $false } } return Install-NodeWithFnm } return $true } Write-Warning "未检测到 Node.js" Write-Info "将使用 fnm 安装 Node.js 24.x" $install = Read-Host "是否继续? (Y/n)" if ($install -eq "n" -or $install -eq "N") { return $false } if (-not (Test-Command "fnm")) { if (-not (Install-Fnm)) { return $false } } return Install-NodeWithFnm } function Install-Tool { if (-not (Ensure-NodeEnvironment)) { return $false } Write-Info "使用 npm 安装 $ToolName..." $installCmd = "npm install -g $ToolPackage" Write-Host " 执行: $installCmd" -ForegroundColor Gray try { Invoke-Expression $installCmd $exitCode = $LASTEXITCODE Refresh-Path if ($exitCode -ne 0) { Write-Error "安装命令返回错误码: $exitCode" return $false } if (Test-Command $ToolCommand) { Write-Success "$ToolName 安装成功!" return $true } else { Write-Warning "$ToolName 可能已安装,但需要重新打开终端才能生效" $continue = Read-Host "是否继续进行配置? (Y/n)" return ($continue -ne "n" -and $continue -ne "N") } } catch { Write-Error "安装失败: $($_.Exception.Message)" return $false } } # Function to show help function Show-Help { Write-Host @" Claude Code Configuration Script for XCodeCLI (Windows) Usage: powershell -ExecutionPolicy Bypass -File setup-claude-code.ps1 [OPTIONS] This script automatically tests multiple API endpoints and selects the working one: - https://api2.xcodecli.com - https://api.xcodecli.com Options: -ApiKey Set the API key -Test Test API connections only (requires -ApiKey) -Show Show current settings and exit -Help Show this help message Examples: .\setup-claude-code.ps1 -ApiKey your-api-key-here .\setup-claude-code.ps1 -Test -ApiKey your-api-key-here .\setup-claude-code.ps1 -Show Interactive mode (no arguments): .\setup-claude-code.ps1 PowerShell Execution Policy: If you get an execution policy error, run: powershell -ExecutionPolicy Bypass -File setup-claude-code.ps1 "@ exit 0 } # Function to backup existing settings function Backup-Settings { if (Test-Path $ClaudeSettingsFile) { $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $backupFile = "$ClaudeSettingsFile.backup.$timestamp" Copy-Item -Path $ClaudeSettingsFile -Destination $backupFile Write-Info "Backed up existing settings to: $backupFile" } $configJsonPath = "$ClaudeConfigDir\config.json" if (Test-Path $configJsonPath) { $timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $backupFile = "$configJsonPath.backup.$timestamp" Copy-Item -Path $configJsonPath -Destination $backupFile Write-Info "Backed up existing config to: $backupFile" } } # Function to create settings directory function New-SettingsDirectory { if (-not (Test-Path $ClaudeConfigDir)) { New-Item -ItemType Directory -Path $ClaudeConfigDir -Force | Out-Null Write-Info "Created Claude configuration directory: $ClaudeConfigDir" } } # Function to validate API key format function Test-ApiKey { param([string]$ApiKey) if ($ApiKey -match '^[A-Za-z0-9_-]+$') { return $true } else { Write-Error "Invalid API key format. API key should contain only alphanumeric characters, hyphens, and underscores." return $false } } # Function to extract model count from API response (supports both data[] and models[]) function Get-ModelCount { param([object]$Response) if ($null -eq $Response) { return 0 } if ($Response.data -is [Array] -and $Response.data.Count -gt 0) { return $Response.data.Count } if ($Response.models -is [Array] -and $Response.models.Count -gt 0) { return $Response.models.Count } return 0 } # Function to test API connection and return working base URL function Test-ApiConnection { param( [string]$ApiKey ) $testUrls = @( "https://api2.xcodecli.com", "https://api.xcodecli.com" ) Write-Info "Testing API connections..." foreach ($baseUrl in $testUrls) { Write-Info "Testing $baseUrl..." try { $headers = @{ "Content-Type" = "application/json" "Authorization" = "Bearer $ApiKey" } $uri = "$baseUrl/v1/models" $response = Invoke-RestMethod -Uri $uri -Method Get -Headers $headers -ErrorAction Stop $modelCount = Get-ModelCount -Response $response if ($modelCount -gt 0) { Write-Success "API connection successful! Found $modelCount models at $baseUrl" return $baseUrl } else { Write-Warning "API responded but no models found at $baseUrl" } } catch { if ($_.Exception.Response -and $_.Exception.Response.StatusCode -eq 401) { Write-Warning "API key authentication failed for $baseUrl" } elseif ($_.Exception.Message -like "*Unable to connect*" -or $_.Exception.Message -like "*could not be resolved*") { Write-Warning "Cannot connect to $baseUrl" } else { Write-Warning "API test failed for $baseUrl`: $($_.Exception.Message)" } } } Write-Error "All API connections failed. Please check your API key and internet connection." return $null } # Function to create Claude Code settings function New-Settings { param( [string]$BaseUrl, [string]$ApiKey ) $settings = @{ env = @{ ANTHROPIC_AUTH_TOKEN = $ApiKey ANTHROPIC_BASE_URL = $BaseUrl CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1" DISABLE_TELEMETRY = "1" DISABLE_ERROR_REPORTING = "1" API_TIMEOUT_MS = "600000" } permissions = @{ allow = @() deny = @() } model = "opus" alwaysThinkingEnabled = $true } try { $json = $settings | ConvertTo-Json -Depth 10 Set-Content -Path $ClaudeSettingsFile -Value $json -Encoding UTF8 Write-Success "Claude Code settings written to: $ClaudeSettingsFile" # Write config.json for VSCode Claude extension $configJsonPath = "$ClaudeConfigDir\config.json" $configJson = @{ primaryApiKey = "xcodecli" } $configJson | ConvertTo-Json | Set-Content -Path $configJsonPath -Encoding UTF8 Write-Success "VSCode Claude config written to: $configJsonPath" # Set environment variables Write-Info "Setting environment variables..." Set-EnvVariable -Name "ANTHROPIC_BASE_URL" -Value $BaseUrl Set-EnvVariable -Name "ANTHROPIC_AUTH_TOKEN" -Value $ApiKey Write-Success "Environment variables configured" return $true } catch { Write-Error "Failed to create settings file: $($_.Exception.Message)" return $false } } # Function to display current settings function Show-Settings { if (Test-Path $ClaudeSettingsFile) { Write-Info "Current Claude Code settings:" Write-Host "----------------------------------------" $settings = Get-Content $ClaudeSettingsFile -Raw | ConvertFrom-Json $settings | ConvertTo-Json -Depth 10 Write-Host "----------------------------------------" } else { Write-Info "No existing Claude Code settings found." } } # Main function function Main { Write-Info "Claude Code Configuration Script for XCodeCLI" Write-Host "=======================================================" Write-Host "" # Handle command line arguments if ($Help) { Show-Help } if ($Show) { Show-Settings exit 0 } # 检测工具是否已安装 if (-not (Test-Command $ToolCommand)) { Write-Host "" Write-Warning "$ToolName 未安装" $install = Read-Host "是否立即安装? (Y/n)" if ($install -eq "n" -or $install -eq "N") { Write-Info "已取消" exit 0 } if (-not (Install-Tool)) { exit 1 } } else { Write-Success "$ToolName 已安装" } # Interactive mode if no Key provided if (-not $ApiKey) { Write-Info "Interactive setup mode" Write-Host "" # Get API key while ([string]::IsNullOrWhiteSpace($ApiKey)) { $ApiKey = Read-Host "Enter your API key" if ([string]::IsNullOrWhiteSpace($ApiKey)) { Write-Warning "API key is required" } elseif (-not (Test-ApiKey $ApiKey)) { $ApiKey = "" } } } # Validate inputs if ([string]::IsNullOrWhiteSpace($ApiKey)) { Write-Error "API key is required" Write-Info "Use -Help for usage information" exit 1 } # Validate API key if (-not (Test-ApiKey $ApiKey)) { exit 1 } $maskedKey = if ($ApiKey.Length -gt 12) { "$($ApiKey.Substring(0, 8))...$($ApiKey.Substring($ApiKey.Length - 4))" } else { "$($ApiKey.Substring(0, [Math]::Min(4, $ApiKey.Length)))..." } Write-Info "API Key: $maskedKey" Write-Host "" # Test API connection and get working base URL $BaseUrl = Test-ApiConnection -ApiKey $ApiKey if ([string]::IsNullOrWhiteSpace($BaseUrl)) { if ($Test) { exit 1 } $continue = Read-Host "All API tests failed. Continue anyway? (y/N)" if ($continue -notmatch '^[Yy]$') { Write-Info "Setup cancelled" exit 1 } # If user chooses to continue anyway, use default URL $BaseUrl = $DefaultBaseUrl Write-Warning "Using default URL: $BaseUrl" } else { Write-Info "Selected working base URL: $BaseUrl" } # Exit if test only if ($Test) { Write-Success "API test completed successfully" exit 0 } # Create settings directory New-SettingsDirectory # Backup existing settings Backup-Settings # Create new settings if (New-Settings -BaseUrl $BaseUrl -ApiKey $ApiKey) { Write-Host "" Write-Success "Claude Code has been configured successfully!" Write-Info "You can now use Claude Code with your API router." Write-Info "" Write-Info "To verify the setup, run:" Write-Info " claude --version" Write-Info "" Write-Info "Configuration file location: $ClaudeSettingsFile" if (Test-Path $ClaudeSettingsFile) { Write-Host "" Write-Info "Current settings:" $settings = Get-Content $ClaudeSettingsFile -Raw | ConvertFrom-Json $settings | ConvertTo-Json -Depth 10 } Write-Host "" Write-Warning "Please restart your terminal for environment variables to take effect." } else { Write-Error "Failed to create Claude Code settings" exit 1 } } # Run main function Main