public/Set-AIToolCredential.ps1
|
function Set-AIToolCredential { <# .SYNOPSIS Stores credentials for an AI tool. .DESCRIPTION Saves credentials for AI tools to various locations: - PSF configuration (encrypted, persisted) - Environment variables (current process) - Optional file path (JSON format) Credentials stored via PSF configuration persist across PowerShell sessions. .PARAMETER Tool The AI tool to store credentials for (default: Claude). .PARAMETER Token The authentication token to store. .PARAMETER FilePath Optional file path to also save credentials to (JSON format). .PARAMETER EnvironmentOnly Only set the environment variable, don't persist to PSF configuration. .EXAMPLE Set-AIToolCredential -Tool Claude -Token "sk-ant-xxxxx" .EXAMPLE Set-AIToolCredential -Tool Claude -Token $token -FilePath "/config/claude.json" .EXAMPLE Set-AIToolCredential -Tool Gemini -Token $apiKey -EnvironmentOnly .OUTPUTS PSCustomObject with: - Success: Boolean indicating if save was successful - Locations: Array of locations where credential was saved - Message: Status message #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter()] [string]$Tool = 'Claude', [Parameter(Mandatory)] [string]$Token, [Parameter()] [string]$FilePath, [Parameter()] [switch]$EnvironmentOnly ) # Clean up token - remove whitespace, line breaks $Token = $Token -replace '[\r\n\s]', '' if ([string]::IsNullOrWhiteSpace($Token)) { throw "Token cannot be empty" } $savedLocations = @() $result = [PSCustomObject]@{ Success = $false Locations = @() Message = "" } # Tool-specific validation and storage switch ($Tool) { 'Claude' { # Validate Claude token format if ($Token -notmatch '^sk-ant-') { throw "Invalid Claude token format. Tokens should start with 'sk-ant-'" } $envVarName = 'CLAUDE_CODE_OAUTH_TOKEN' $configName = 'AITools.Claude.OAuthToken' } 'Aider' { # Aider uses various API keys - detect by format if ($Token -match '^sk-ant-') { $envVarName = 'ANTHROPIC_API_KEY' $configName = 'AITools.Aider.AnthropicApiKey' } elseif ($Token -match '^sk-') { $envVarName = 'OPENAI_API_KEY' $configName = 'AITools.Aider.OpenAiApiKey' } else { $envVarName = 'ANTHROPIC_API_KEY' $configName = 'AITools.Aider.ApiKey' } } 'Gemini' { $envVarName = 'GEMINI_API_KEY' $configName = 'AITools.Gemini.ApiKey' } 'Codex' { if ($Token -notmatch '^sk-') { Write-PSFMessage -Level Warning -Message "OpenAI tokens typically start with 'sk-'" } $envVarName = 'OPENAI_API_KEY' $configName = 'AITools.Codex.ApiKey' } default { throw "Credential storage not implemented for tool: $Tool" } } try { # 1. Set environment variable (always do this for immediate use) if ($PSCmdlet.ShouldProcess($envVarName, "Set environment variable")) { [Environment]::SetEnvironmentVariable($envVarName, $Token, 'Process') $savedLocations += "Environment: $envVarName" Write-PSFMessage -Level Verbose -Message "Set environment variable: $envVarName" } # 2. Save to PSF configuration (unless EnvironmentOnly) if (-not $EnvironmentOnly) { if ($PSCmdlet.ShouldProcess($configName, "Save to PSF configuration")) { Set-PSFConfig -FullName $configName -Value $Token -PassThru | Register-PSFConfig $savedLocations += "PSFConfig: $configName" Write-PSFMessage -Level Verbose -Message "Saved to PSF configuration: $configName" } } # 3. Save to file path if specified if ($FilePath) { if ($PSCmdlet.ShouldProcess($FilePath, "Save credential file")) { $fileDir = Split-Path $FilePath -Parent if ($fileDir -and -not (Test-Path $fileDir)) { New-Item -ItemType Directory -Path $fileDir -Force | Out-Null } $fileContent = @{ token = $Token tool = $Tool configuredAt = (Get-Date -Format "o") } # For Claude, also create the CLI credentials format if ($Tool -eq 'Claude') { # Check if this is the CLI credentials path if ($FilePath -match '\.credentials\.json$') { $fileContent = @{ claudeAiOauth = @{ accessToken = $Token refreshToken = "" expiresAt = [long]([DateTimeOffset]::UtcNow.AddDays(30).ToUnixTimeMilliseconds()) scopes = @("user:inference") subscriptionType = "max" rateLimitTier = "default_claude_max_20x" } } } } $fileContent | ConvertTo-Json -Depth 5 | Set-Content -Path $FilePath -Force $savedLocations += "File: $FilePath" Write-PSFMessage -Level Verbose -Message "Saved credential file: $FilePath" } } $result.Success = $true $result.Locations = $savedLocations $result.Message = "$Tool credentials saved to: $($savedLocations -join ', ')" } catch { $result.Success = $false $result.Locations = $savedLocations $result.Message = "Failed to save credentials: $($_.Exception.Message)" Write-PSFMessage -Level Error -Message $result.Message } return $result } |