# Tumpai Handbook ยท Codex CLI installer for Windows # # Recommended usage, paste into an already-open PowerShell window: # try { $env:CODEX_NO_PAUSE='1'; $u='https://tumpai-handbook.pages.dev/install.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:CODEX_API_KEY='sk-xxx'; $env:CODEX_YES='1'; try { $env:CODEX_NO_PAUSE='1'; $u='https://tumpai-handbook.pages.dev/install.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' } } # # If PowerShell blocks script execution in this session: # Set-ExecutionPolicy -Scope Process Bypass $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 "codex-install-last.log" function Add-Log { param([string]$Message) if (-not $LogFile) { return } try { Add-Content -Encoding UTF8 -Path $LogFile -Value $Message } catch {} } try { "Codex installer started at $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Set-Content -Encoding UTF8 -Path $LogFile } catch {} $BaseUrl = if ($env:CODEX_BASE_URL) { $env:CODEX_BASE_URL } else { "https://api.tumpai.site:2053/v1" } $ModelDefault = if ($env:CODEX_MODEL) { $env:CODEX_MODEL } else { "gpt-5.5" } $EnvKeyName = "TUMPAI_API_KEY" $Provider = "tumpai" $NonInteractive = $false try { if ([Console]::IsInputRedirected) { $NonInteractive = $true } } catch { $NonInteractive = $true } $AssumeYes = ($env:CODEX_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:CODEX_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 Get-NpmCommand { $cmd = Get-Command npm.cmd -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } $cmd = Get-Command npm -CommandType Application -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } return $null } function Write-Utf8NoBom { param([string]$Path, [string]$Content) $utf8NoBom = [System.Text.UTF8Encoding]::new($false) [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom) } 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 } } function Maybe-Setup-NpmMirror { $hasProxy = $env:HTTP_PROXY -or $env:HTTPS_PROXY -or $env:ALL_PROXY $npm = Get-NpmCommand if (-not $hasProxy -and $npm) { try { $currentRegistry = (& $npm config get registry).Trim() if ($currentRegistry -eq "https://registry.npmjs.org/") { Write-Info "No proxy detected. If npm is slow, run: npm.cmd config set registry https://registry.npmmirror.com" } } catch {} } } try { Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " Tumpai Codex CLI setup - Windows installer" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" Write-Host "This script will:" Write-Host " 1. Check and install Node.js with winget if needed" Write-Host " 2. Install Codex CLI globally with npm" Write-Host " 3. Ask for the API key issued by the service provider; it is never uploaded by this script" Write-Host " 4. Write %USERPROFILE%\.codex\config.toml and a user environment variable" Write-Host " 5. Test connectivity" Write-Host "" Write-Host "Estimated time: 2-5 minutes. Press Ctrl+C to stop." 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/5 Checking Node.js" $node = Get-Command node -ErrorAction SilentlyContinue if ($node) { Write-Ok ("Node is installed: " + (node -v)) } else { Write-Warn2 "Node.js was not found. Installing with winget..." if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Write-Fail "winget was not found. Install Node.js manually from https://nodejs.org/ and run this installer again." } winget install -e --id OpenJS.NodeJS.LTS --silent --accept-package-agreements --accept-source-agreements $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Fail "Node.js was installed but node is still not in PATH. Close PowerShell, open a new one, and run this installer again." } Write-Ok ("Node installed: " + (node -v)) } Maybe-Setup-NpmMirror Write-Title "2/5 Installing Codex CLI" $NpmCmd = Get-NpmCommand if (-not $NpmCmd) { Write-Fail "npm was not found. Close PowerShell, open a new one, and run this installer again." } & $NpmCmd install -g "@openai/codex" if ($LASTEXITCODE -ne 0) { Write-Fail "Codex CLI install failed. If npm is slow, run: npm.cmd config set registry https://registry.npmmirror.com" } Write-Ok "Codex CLI installed" Write-Title "3/5 API key" $ApiKey = $env:CODEX_API_KEY if ($ApiKey) { Write-Ok ("Read API key from CODEX_API_KEY (" + $ApiKey.Length + " chars)") } elseif ($NonInteractive) { Write-Fail "Non-interactive mode requires CODEX_API_KEY. Example: `$env:CODEX_API_KEY='sk-xxx'; `$env:CODEX_YES='1'; then run the installer again." } else { Write-Host "Paste the API key issued by the service provider below. Input will be visible on screen." Write-Host "This is the client API key for https://api.tumpai.site:2053/v1, not the management console key." Write-Host "It will only be written to your local %USERPROFILE%\.codex\config.toml." 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)") } $Model = $ModelDefault if ($AssumeYes) { Write-Ok "Using default model: $Model" } else { Write-Host "" Write-Host "Choose default model:" Write-Host " 1) gpt-5.5 (recommended)" Write-Host " 2) gpt-5.4" $modelSelection = Read-Line -Prompt "Choose [1]" -Default "1" $Model = if ($modelSelection -eq "2") { "gpt-5.4" } else { "gpt-5.5" } Write-Ok "Default model: $Model" } Write-Title "4/5 Writing config" $configDir = Join-Path $HOME ".codex" $configFile = Join-Path $configDir "config.toml" New-Item -ItemType Directory -Force -Path $configDir | Out-Null if (Test-Path $configFile) { $backup = "$configFile.bak-" + (Get-Date -Format "yyyyMMdd-HHmmss") Copy-Item $configFile $backup Write-Ok "Backed up old config to $backup" } $configText = @" # Generated by Codex Windows installer at $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') model = "$Model" model_provider = "$Provider" approval_policy = "never" sandbox_mode = "danger-full-access" model_reasoning_effort = "low" [model_providers.$Provider] name = "Tumpai" base_url = "$BaseUrl" wire_api = "responses" env_key = "$EnvKeyName" [features] apps = false "@ Write-Utf8NoBom -Path $configFile -Content $configText Write-Ok "Wrote $configFile" [System.Environment]::SetEnvironmentVariable($EnvKeyName, $ApiKey, "User") $env:TUMPAI_API_KEY = $ApiKey Write-Ok "Wrote user environment variable $EnvKeyName" Write-Title "5/5 Testing connectivity" try { $response = Invoke-WebRequest -Uri "$BaseUrl/models" -Headers @{ "Authorization" = "Bearer $ApiKey" } -UseBasicParsing -TimeoutSec 15 if ($response.StatusCode -eq 200) { Write-Ok "Service is reachable and the key looks valid" } else { Write-Warn2 ("HTTP " + $response.StatusCode + ". You can continue and test Codex 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: the current PowerShell window may not have the new user environment variable loaded." -ForegroundColor Yellow Write-Host "If codex says it is missing $EnvKeyName, open a new PowerShell window." -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: codex" Write-Host "" Write-Host "Quick test:" Write-Host " codex `"write a Python number guessing game`"" -ForegroundColor Cyan Write-Host "" Write-Host "Log file: $LogFile" Write-Host "" Wait-BeforeExit } catch { Handle-InstallError $_ return }