param( [switch]$Help ) $ErrorActionPreference = 'Stop' $OmniRouterBaseUrl = 'https://api.omnirouter.ru' $UseAuthToken = $false function Show-Usage { @' Установщик OmniRouter для Claude Code на Windows. Использование: irm "https://api.omnirouter.ru/install/claude.ps1" | iex Для omni-ключа, созданного через Claude OAuth: irm "https://api.omnirouter.ru/install/claude.ps1?auth=oauth" | iex Что делает скрипт: - Без отображения запрашивает действующий omni-ключ - Сохраняет ANTHROPIC_BASE_URL и ключ для текущего пользователя Windows - Заранее подтверждает ключ в пользовательской конфигурации Claude Code - Не изменяет другие настройки Claude Code '@ } function Write-Utf8NoBom([string]$Path, [string]$Value) { $encoding = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllText($Path, $Value, $encoding) } function Resolve-ClaudeConfigPath { $configRoot = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { $env:USERPROFILE } $configCandidate = Join-Path $configRoot '.config.json' if ($env:CLAUDE_CONFIG_DIR -and (Test-Path -LiteralPath $configCandidate)) { return $configCandidate } $suffix = '' if ($env:CLAUDE_CODE_CUSTOM_OAUTH_URL) { $suffix = '-custom-oauth' } elseif ($env:USER_TYPE -eq 'ant') { if ($env:USE_LOCAL_OAUTH -match '^(?:1|true|yes|on)$') { $suffix = '-local-oauth' } elseif ($env:USE_STAGING_OAUTH -match '^(?:1|true|yes|on)$') { $suffix = '-staging-oauth' } } return Join-Path $configRoot ".claude$suffix.json" } function Approve-OmniKey([string]$Key) { $configPath = Resolve-ClaudeConfigPath $configDir = Split-Path -Parent $configPath [System.IO.Directory]::CreateDirectory($configDir) | Out-Null if (Test-Path -LiteralPath $configPath) { try { $config = [System.IO.File]::ReadAllText($configPath) | ConvertFrom-Json } catch { throw "Не удалось прочитать конфигурацию Claude Code $configPath. Исправьте JSON или временно переместите файл и повторите установку." } } else { $config = [PSCustomObject]@{} } if ($null -eq $config.PSObject.Properties['customApiKeyResponses']) { $config | Add-Member -NotePropertyName 'customApiKeyResponses' -NotePropertyValue ([PSCustomObject]@{}) } $responses = $config.customApiKeyResponses if ($null -eq $responses.PSObject.Properties['approved']) { $responses | Add-Member -NotePropertyName 'approved' -NotePropertyValue @() } if ($null -eq $responses.PSObject.Properties['rejected']) { $responses | Add-Member -NotePropertyName 'rejected' -NotePropertyValue @() } $suffix = $Key.Substring([Math]::Max(0, $Key.Length - 20)) $responses.approved = @($responses.approved | Where-Object { $_ -ne $suffix }) + @($suffix) $responses.rejected = @($responses.rejected | Where-Object { $_ -ne $suffix }) Write-Utf8NoBom $configPath (($config | ConvertTo-Json -Depth 100) + [Environment]::NewLine) return $configPath } function Remove-LegacyOmniVariable([string]$Name) { $current = [Environment]::GetEnvironmentVariable($Name, 'User') if ($current -is [string] -and $current.StartsWith('omni_')) { [Environment]::SetEnvironmentVariable($Name, $null, 'User') } } if ($Help) { Show-Usage return } $secureKey = Read-Host 'Введите omni-ключ' -AsSecureString $keyPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureKey) $omniApiKey = $null try { $omniApiKey = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($keyPointer) if ($omniApiKey -cnotmatch '^omni_[0-9a-f]{32}$') { throw 'Некорректный omni-ключ. Ожидается префикс omni_ и 32 строчные шестнадцатеричные цифры.' } $configPath = Approve-OmniKey $omniApiKey [Environment]::SetEnvironmentVariable('ANTHROPIC_BASE_URL', $OmniRouterBaseUrl, 'User') $env:ANTHROPIC_BASE_URL = $OmniRouterBaseUrl if ($UseAuthToken) { [Environment]::SetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN', $omniApiKey, 'User') $env:ANTHROPIC_AUTH_TOKEN = $omniApiKey Remove-LegacyOmniVariable 'ANTHROPIC_API_KEY' Remove-Item Env:ANTHROPIC_API_KEY -ErrorAction SilentlyContinue $credentialName = 'ANTHROPIC_AUTH_TOKEN' } else { [Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY', $omniApiKey, 'User') $env:ANTHROPIC_API_KEY = $omniApiKey Remove-LegacyOmniVariable 'ANTHROPIC_AUTH_TOKEN' Remove-Item Env:ANTHROPIC_AUTH_TOKEN -ErrorAction SilentlyContinue $credentialName = 'ANTHROPIC_API_KEY' } Write-Host 'Claude Code настроен для OmniRouter.' Write-Host "Сохранены ANTHROPIC_BASE_URL и $credentialName для текущего пользователя Windows." Write-Host "Обновлена конфигурация: $configPath" Write-Host 'Закройте Claude Code и PowerShell, затем откройте новый терминал и запустите claude.' } finally { if ($keyPointer -ne [IntPtr]::Zero) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($keyPointer) } $omniApiKey = $null $secureKey = $null }