|
| 1 | +# Bailian — Pure Agent Setup (no Node.js / npx required) |
| 2 | +# |
| 3 | +# Directly writes config files for coding agents to use DashScope API. |
| 4 | +# |
| 5 | +# Usage (set env vars, then pipe): |
| 6 | +# $env:BL_AGENT="claude-code" |
| 7 | +# $env:BL_BASE_URL="https://dashscope.aliyuncs.com/apps/anthropic" |
| 8 | +# $env:BL_API_KEY="sk-xxxxx" |
| 9 | +# $env:BL_MODEL="qwen3.7-max" |
| 10 | +# irm https://xxx/agent-setup.ps1 | iex |
| 11 | + |
| 12 | +$ErrorActionPreference = "Stop" |
| 13 | + |
| 14 | +function Write-Err { Write-Host "ERROR: $args" -ForegroundColor Red } |
| 15 | +function Write-Info { Write-Host "INFO: $args" -ForegroundColor Blue } |
| 16 | +function Write-Ok { Write-Host "[OK] $args" -ForegroundColor Green } |
| 17 | +function Write-Warn { Write-Host "WARNING: $args" -ForegroundColor Yellow } |
| 18 | + |
| 19 | +function Test-AnthropicEndpoint([string]$Url) { |
| 20 | + return $Url -match '/apps/anthropic' |
| 21 | +} |
| 22 | + |
| 23 | +function Backup-File([string]$Path) { |
| 24 | + if (Test-Path $Path) { |
| 25 | + $ts = [int][double]::Parse((Get-Date -UFormat %s)) |
| 26 | + Copy-Item $Path "$Path.bak.$ts" |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +function Write-SafeFile([string]$Path, [string]$Content) { |
| 31 | + $dir = Split-Path $Path -Parent |
| 32 | + if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } |
| 33 | + $tmp = "$Path.tmp" |
| 34 | + Set-Content -Path $tmp -Value $Content -Encoding UTF8 -NoNewline |
| 35 | + Move-Item -Path $tmp -Destination $Path -Force |
| 36 | +} |
| 37 | + |
| 38 | +# ── Agent writers ───────────────────────────────────────────────────────────── |
| 39 | + |
| 40 | +function Setup-ClaudeCode([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 41 | + $settings = Join-Path $HOME ".claude" "settings.json" |
| 42 | + $onboarding = Join-Path $HOME ".claude.json" |
| 43 | + |
| 44 | + if ($DryRun) { Write-Info "[dry-run] Would write: $settings, $onboarding"; return } |
| 45 | + |
| 46 | + # settings.json — merge env if possible |
| 47 | + $data = @{} |
| 48 | + if (Test-Path $settings) { |
| 49 | + try { $data = Get-Content $settings -Raw | ConvertFrom-Json -AsHashtable } catch { $data = @{} } |
| 50 | + } |
| 51 | + if (-not $data.ContainsKey("env")) { $data["env"] = @{} } |
| 52 | + $data["env"]["ANTHROPIC_AUTH_TOKEN"] = $ApiKey |
| 53 | + $data["env"]["ANTHROPIC_BASE_URL"] = $BaseUrl |
| 54 | + $data["env"]["ANTHROPIC_MODEL"] = $Model |
| 55 | + $data["env"]["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = $Model |
| 56 | + $data["env"]["ANTHROPIC_DEFAULT_SONNET_MODEL"] = $Model |
| 57 | + $data["env"]["ANTHROPIC_DEFAULT_OPUS_MODEL"] = $Model |
| 58 | + $data["env"]["CLAUDE_CODE_SUBAGENT_MODEL"] = $Model |
| 59 | + |
| 60 | + Backup-File $settings |
| 61 | + Write-SafeFile $settings ($data | ConvertTo-Json -Depth 10) |
| 62 | + |
| 63 | + # .claude.json — ensure hasCompletedOnboarding |
| 64 | + $ob = @{} |
| 65 | + if (Test-Path $onboarding) { |
| 66 | + try { $ob = Get-Content $onboarding -Raw | ConvertFrom-Json -AsHashtable } catch { $ob = @{} } |
| 67 | + } |
| 68 | + $ob["hasCompletedOnboarding"] = $true |
| 69 | + |
| 70 | + Backup-File $onboarding |
| 71 | + Write-SafeFile $onboarding ($ob | ConvertTo-Json -Depth 10) |
| 72 | + |
| 73 | + Write-Ok "Claude Code configured." |
| 74 | + Write-Host " Written: $settings" |
| 75 | + Write-Host " Written: $onboarding" |
| 76 | + Write-Host "" |
| 77 | + Write-Host " Run ``claude`` to start using Claude Code with DashScope." |
| 78 | +} |
| 79 | + |
| 80 | +function Setup-QwenCode([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 81 | + $settings = Join-Path $HOME ".qwen" "settings.json" |
| 82 | + |
| 83 | + if ($DryRun) { Write-Info "[dry-run] Would write: $settings"; return } |
| 84 | + |
| 85 | + $data = @{ |
| 86 | + env = @{ BAILIAN_API_KEY = $ApiKey } |
| 87 | + modelProviders = @{ |
| 88 | + openai = @( |
| 89 | + @{ |
| 90 | + id = $Model |
| 91 | + name = "[Bailian] $Model" |
| 92 | + baseUrl = $BaseUrl |
| 93 | + envKey = "BAILIAN_API_KEY" |
| 94 | + } |
| 95 | + ) |
| 96 | + } |
| 97 | + security = @{ auth = @{ selectedType = "openai" } } |
| 98 | + model = @{ name = $Model } |
| 99 | + '$version' = 3 |
| 100 | + } |
| 101 | + |
| 102 | + Backup-File $settings |
| 103 | + Write-SafeFile $settings ($data | ConvertTo-Json -Depth 10) |
| 104 | + |
| 105 | + Write-Ok "Qwen Code configured." |
| 106 | + Write-Host " Written: $settings" |
| 107 | + Write-Host "" |
| 108 | + Write-Host " Run ``qwen`` to start using Qwen Code with DashScope." |
| 109 | +} |
| 110 | + |
| 111 | +function Setup-OpenCode([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 112 | + $config = Join-Path $HOME ".config" "opencode" "opencode.json" |
| 113 | + |
| 114 | + $npm = if (Test-AnthropicEndpoint $BaseUrl) { "@ai-sdk/anthropic" } else { "@ai-sdk/openai-compatible" } |
| 115 | + |
| 116 | + if ($DryRun) { Write-Info "[dry-run] Would write: $config (npm: $npm)"; return } |
| 117 | + |
| 118 | + $data = @{ |
| 119 | + '$schema' = "https://opencode.ai/config.json" |
| 120 | + provider = @{ |
| 121 | + bailian = @{ |
| 122 | + npm = $npm |
| 123 | + name = "Alibaba Cloud Model Studio" |
| 124 | + options = @{ baseURL = $BaseUrl; apiKey = $ApiKey } |
| 125 | + models = @{ $Model = @{ name = $Model } } |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + Backup-File $config |
| 131 | + Write-SafeFile $config ($data | ConvertTo-Json -Depth 10) |
| 132 | + |
| 133 | + Write-Ok "OpenCode configured." |
| 134 | + Write-Host " Written: $config" |
| 135 | + Write-Host "" |
| 136 | + Write-Host " Run ``opencode`` then type ``/models`` to select your model." |
| 137 | +} |
| 138 | + |
| 139 | +function Setup-OpenClaw([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 140 | + $config = Join-Path $HOME ".openclaw" "openclaw.json" |
| 141 | + |
| 142 | + $api = if (Test-AnthropicEndpoint $BaseUrl) { "anthropic-messages" } else { "openai-completions" } |
| 143 | + |
| 144 | + if ($DryRun) { Write-Info "[dry-run] Would write: $config (api: $api)"; return } |
| 145 | + |
| 146 | + $data = @{ |
| 147 | + models = @{ |
| 148 | + mode = "merge" |
| 149 | + providers = @{ |
| 150 | + bailian = @{ |
| 151 | + baseUrl = $BaseUrl |
| 152 | + apiKey = $ApiKey |
| 153 | + api = $api |
| 154 | + models = @( |
| 155 | + @{ |
| 156 | + id = $Model |
| 157 | + name = $Model |
| 158 | + reasoning = $false |
| 159 | + input = @("text", "image") |
| 160 | + contextWindow = 1000000 |
| 161 | + maxTokens = 65536 |
| 162 | + cost = @{ input = 0; output = 0; cacheRead = 0; cacheWrite = 0 } |
| 163 | + } |
| 164 | + ) |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + agents = @{ |
| 169 | + defaults = @{ |
| 170 | + model = @{ primary = "bailian/$Model" } |
| 171 | + } |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + Backup-File $config |
| 176 | + Write-SafeFile $config ($data | ConvertTo-Json -Depth 10) |
| 177 | + |
| 178 | + Write-Ok "OpenClaw configured." |
| 179 | + Write-Host " Written: $config" |
| 180 | + Write-Host "" |
| 181 | + Write-Host " Run ``openclaw`` to start using OpenClaw with DashScope." |
| 182 | +} |
| 183 | + |
| 184 | +function Setup-Hermes([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 185 | + $config = Join-Path $HOME ".hermes" "config.yaml" |
| 186 | + |
| 187 | + $apiMode = if (Test-AnthropicEndpoint $BaseUrl) { "anthropic_messages" } else { "chat_completions" } |
| 188 | + |
| 189 | + if ($DryRun) { Write-Info "[dry-run] Would write: $config (api_mode: $apiMode)"; return } |
| 190 | + |
| 191 | + Write-Warn "Hermes Agent does not support native Windows. This config is for WSL2." |
| 192 | + |
| 193 | + $yaml = @" |
| 194 | +model: |
| 195 | + default: $Model |
| 196 | + provider: custom |
| 197 | + base_url: $BaseUrl |
| 198 | + api_mode: $apiMode |
| 199 | + api_key: $ApiKey |
| 200 | +"@ |
| 201 | + |
| 202 | + Backup-File $config |
| 203 | + Write-SafeFile $config $yaml |
| 204 | + |
| 205 | + Write-Ok "Hermes Agent configured." |
| 206 | + Write-Host " Written: $config" |
| 207 | + Write-Host "" |
| 208 | + Write-Host " Run ``hermes chat -q `"hello`"`` to verify." |
| 209 | +} |
| 210 | + |
| 211 | +function Setup-Codex([string]$BaseUrl, [string]$ApiKey, [string]$Model, [bool]$DryRun) { |
| 212 | + $config = Join-Path $HOME ".codex" "config.toml" |
| 213 | + $auth = Join-Path $HOME ".codex" "auth.json" |
| 214 | + |
| 215 | + if ($DryRun) { Write-Info "[dry-run] Would write: $config, $auth"; return } |
| 216 | + |
| 217 | + $toml = @" |
| 218 | +model_provider = "Model_Studio" |
| 219 | +model = "$Model" |
| 220 | +
|
| 221 | +[model_providers.Model_Studio] |
| 222 | +name = "Model_Studio" |
| 223 | +base_url = "$BaseUrl" |
| 224 | +env_key = "OPENAI_API_KEY" |
| 225 | +wire_api = "responses" |
| 226 | +"@ |
| 227 | + |
| 228 | + Backup-File $config |
| 229 | + Write-SafeFile $config $toml |
| 230 | + |
| 231 | + Backup-File $auth |
| 232 | + Write-SafeFile $auth "{`n `"OPENAI_API_KEY`": `"$ApiKey`"`n}" |
| 233 | + |
| 234 | + Write-Ok "Codex configured." |
| 235 | + Write-Host " Written: $config" |
| 236 | + Write-Host " Written: $auth" |
| 237 | + Write-Host "" |
| 238 | + Write-Host " Run ``codex`` to start using Codex with DashScope." |
| 239 | +} |
| 240 | + |
| 241 | +# ── Read parameters from environment variables ──────────────────────────────── |
| 242 | + |
| 243 | +$Agent = $env:BL_AGENT |
| 244 | +$BaseUrl = $env:BL_BASE_URL |
| 245 | +$ApiKey = $env:BL_API_KEY |
| 246 | +$Model = $env:BL_MODEL |
| 247 | +$DryRun = $env:BL_DRY_RUN -eq "1" |
| 248 | + |
| 249 | +if (-not $Agent -or -not $BaseUrl -or -not $ApiKey -or -not $Model) { |
| 250 | + Write-Err "Missing required environment variables." |
| 251 | + Write-Host "" |
| 252 | + Write-Host "Set these before running:" |
| 253 | + Write-Host ' $env:BL_AGENT = "claude-code"' |
| 254 | + Write-Host ' $env:BL_BASE_URL = "https://dashscope.aliyuncs.com/apps/anthropic"' |
| 255 | + Write-Host ' $env:BL_API_KEY = "sk-xxxxx"' |
| 256 | + Write-Host ' $env:BL_MODEL = "qwen3.7-max"' |
| 257 | + Write-Host "" |
| 258 | + Write-Host "Then run:" |
| 259 | + Write-Host ' irm https://xxx/agent-setup.ps1 | iex' |
| 260 | + exit 1 |
| 261 | +} |
| 262 | + |
| 263 | +# ── Dispatch ────────────────────────────────────────────────────────────────── |
| 264 | + |
| 265 | +Write-Host "" |
| 266 | + |
| 267 | +switch ($Agent) { |
| 268 | + "claude-code" { Setup-ClaudeCode $BaseUrl $ApiKey $Model $DryRun } |
| 269 | + "qwen-code" { Setup-QwenCode $BaseUrl $ApiKey $Model $DryRun } |
| 270 | + "opencode" { Setup-OpenCode $BaseUrl $ApiKey $Model $DryRun } |
| 271 | + "openclaw" { Setup-OpenClaw $BaseUrl $ApiKey $Model $DryRun } |
| 272 | + "hermes" { Setup-Hermes $BaseUrl $ApiKey $Model $DryRun } |
| 273 | + "codex" { Setup-Codex $BaseUrl $ApiKey $Model $DryRun } |
| 274 | + default { |
| 275 | + Write-Err "Unknown agent `"$Agent`". Valid agents: claude-code, qwen-code, opencode, openclaw, hermes, codex" |
| 276 | + exit 1 |
| 277 | + } |
| 278 | +} |
| 279 | + |
| 280 | +Write-Host "" |
| 281 | + |
| 282 | +# Clean up env vars |
| 283 | +Remove-Item Env:BL_AGENT -ErrorAction SilentlyContinue |
| 284 | +Remove-Item Env:BL_BASE_URL -ErrorAction SilentlyContinue |
| 285 | +Remove-Item Env:BL_API_KEY -ErrorAction SilentlyContinue |
| 286 | +Remove-Item Env:BL_MODEL -ErrorAction SilentlyContinue |
| 287 | +Remove-Item Env:BL_DRY_RUN -ErrorAction SilentlyContinue |
0 commit comments