# BBToken OpenCode Quickstart (PowerShell) # Usage: $env:BASE_URL="https://bbtoken.cloud/v1"; $env:API_KEY="sk-bbt-..."; irm https://bbtoken.cloud/docs/windows.ps1 | iex $ErrorActionPreference = 'Stop' Write-Host '=== BBToken OpenCode Quickstart ===' -ForegroundColor Cyan if ([string]::IsNullOrWhiteSpace($env:BASE_URL) -or [string]::IsNullOrWhiteSpace($env:API_KEY)) { Write-Host 'Error: BASE_URL and API_KEY environment variables are required' -ForegroundColor Red Write-Host '$env:BASE_URL="https://bbtoken.cloud/v1"; $env:API_KEY="sk-bbt-..."; .\windows.ps1' exit 1 } $BaseUrl = $env:BASE_URL.TrimEnd('/') $ApiKey = $env:API_KEY # Install Node.js when npm is unavailable. Prefer winget, then use the official LTS MSI. if (-not (Get-Command npm -ErrorAction SilentlyContinue)) { Write-Host '-> Node.js/npm not found. Installing Node.js LTS...' -ForegroundColor Yellow $NodeInstalled = $false if (Get-Command winget -ErrorAction SilentlyContinue) { winget install --id OpenJS.NodeJS.LTS --exact --accept-source-agreements --accept-package-agreements $NodeInstalled = ($LASTEXITCODE -eq 0) } if (-not $NodeInstalled) { try { $NodeArch = if ([Environment]::Is64BitOperatingSystem) { 'x64' } else { 'x86' } $NodeRelease = Invoke-RestMethod -Uri 'https://nodejs.org/dist/index.json' -ErrorAction Stop | Where-Object { $_.lts -and $_.files -contains "win-$NodeArch-msi" } | Select-Object -First 1 if (-not $NodeRelease) { throw 'No compatible Node.js LTS release found.' } $NodeMsi = Join-Path $env:TEMP "node-$($NodeRelease.version)-win-$NodeArch.msi" Invoke-WebRequest -Uri "https://nodejs.org/dist/$($NodeRelease.version)/node-$($NodeRelease.version)-win-$NodeArch.msi" -OutFile $NodeMsi $MsiProcess = Start-Process msiexec.exe -ArgumentList @('/i', $NodeMsi, '/qn', '/norestart') -Wait -PassThru Remove-Item $NodeMsi -Force -ErrorAction SilentlyContinue if ($MsiProcess.ExitCode -ne 0) { throw "Node.js installer exited with code $($MsiProcess.ExitCode)." } $NodeInstalled = $true } catch { Write-Host "Error: automatic Node.js installation failed: $($_.Exception.Message)" -ForegroundColor Red Write-Host 'Install Node.js LTS manually from https://nodejs.org/ then run this script again.' -ForegroundColor Red exit 1 } } if (-not $NodeInstalled) { Write-Host 'Error: Node.js installation was cancelled or failed.' -ForegroundColor Red exit 1 } $env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User') } # Install OpenCode via npm if (Get-Command opencode -ErrorAction SilentlyContinue) { Write-Host '[OK] opencode already installed' -ForegroundColor Green } elseif (Get-Command npm -ErrorAction SilentlyContinue) { Write-Host '-> Installing opencode with npm...' -ForegroundColor Yellow npm install --global opencode-ai if ($LASTEXITCODE -ne 0) { throw 'OpenCode installation failed.' } Write-Host '[OK] opencode installed' -ForegroundColor Green } else { Write-Host 'Error: npm was not found. Install Node.js from https://nodejs.org/ then run this script again.' -ForegroundColor Red exit 1 } Write-Host '-> Fetching available models...' -ForegroundColor Yellow try { $ModelsResponse = Invoke-RestMethod -Uri "$BaseUrl/models" -Headers @{ Authorization = "Bearer $ApiKey" } -ErrorAction Stop $ModelIds = @($ModelsResponse.data | ForEach-Object { [string]$_.id } | Where-Object { $_ }) } catch { Write-Host "Warning: failed to fetch models: $($_.Exception.Message)" -ForegroundColor Yellow $ModelIds = @('glm-5.2') } if ($ModelIds.Count -eq 0) { Write-Host 'Warning: API returned no models. Using fallback model.' -ForegroundColor Yellow $ModelIds = @('glm-5.2') } $FirstModel = $ModelIds[0] Write-Host "[OK] Found $($ModelIds.Count) models, default: $FirstModel" -ForegroundColor Green $ConfigDir = Join-Path $env:USERPROFILE '.config\opencode' New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null function ConvertTo-JsonStringLiteral { param([string]$Value) $escaped = $Value -replace '\\', '\\' -replace '"', '\"' -replace "`r", '\r' -replace "`n", '\n' -replace "`t", '\t' return '"' + $escaped + '"' } $ModelBlocks = @() foreach ($id in $ModelIds) { $ModelInfo = $ModelsResponse.data | Where-Object { $_.id -eq $id } | Select-Object -First 1 $InputArray = if ($ModelInfo.vision -eq $true) { '["text", "image"]' } else { '["text"]' } $idLiteral = ConvertTo-JsonStringLiteral $id $ModelBlocks += @" ${idLiteral}: { "name": $idLiteral, "modalities": { "input": $InputArray, "output": ["text"] } } "@ } $ModelsJson = $ModelBlocks -join ",`n" $BaseUrlLiteral = ConvertTo-JsonStringLiteral $BaseUrl $ApiKeyLiteral = ConvertTo-JsonStringLiteral $ApiKey $ModelLiteral = ConvertTo-JsonStringLiteral "bbtoken/$FirstModel" $ConfigJson = @" { "`$schema": "https://opencode.ai/config.json", "provider": { "bbtoken": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": $BaseUrlLiteral, "apiKey": $ApiKeyLiteral }, "models": { $ModelsJson } } }, "model": $ModelLiteral, "permission": "allow" } "@ $Utf8NoBom = New-Object System.Text.UTF8Encoding($false) foreach ($FileName in @('opencode.json', 'opencode.jsonc')) { [System.IO.File]::WriteAllText((Join-Path $ConfigDir $FileName), $ConfigJson, $Utf8NoBom) } Write-Host '[OK] Config written to:' -ForegroundColor Green Write-Host " $(Join-Path $ConfigDir 'opencode.json')" Write-Host " $(Join-Path $ConfigDir 'opencode.jsonc')" Write-Host '' Write-Host '=== Done! ===' -ForegroundColor Green Write-Host 'Now run: opencode' Write-Host "Default model: bbtoken/$FirstModel" Write-Host '' Write-Host 'If running "opencode" shows a "running scripts is disabled" error, run this once:' -ForegroundColor Yellow Write-Host ' Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned' -ForegroundColor Yellow