Codex.psm1
function Enable-Codex { param ( [string]$Chord = "ctrl+g", [switch]$Overwrite ) foreach ($line in $defaultContext.Split("`n")) { $contextList.Add($line) } $handler = Get-PSReadLineKeyHandler -Chord $Chord if ($null -ne $handler -and -not $Overwrite) { throw "A key handler for '$Chord' already exists. Use `-Overwrite` to overwrite." } Set-PSReadLineKeyHandler -Key $Chord -BriefDescription 'Codex' -LongDescription 'Enable retriving Codex completions based on a comment' -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) $output = Get-CodexCompletion -Line $line if ($null -eq $output) { $output = @('# No completion found') } $emptyCompletion = $true foreach ($str in $output) { $str = $str.Trim() if (![string]::IsNullOrEmpty($str)) { [Microsoft.PowerShell.PSConsoleReadLine]::AddLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert($str) $emptyCompletion = $false } } if ($emptyCompletion) { [Microsoft.PowerShell.PSConsoleReadLine]::AddLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert('# An empty completion was returned') } else { $result = Test-Completion $output if ($null -ne $result) { [Microsoft.PowerShell.PSConsoleReadLine]::AddLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert($result) } } } } function Register-CodexOpenApiKey { param ( [Parameter(Mandatory)] [securestring]$ApiKey, [switch]$Overwrite ) if ($null -eq (Get-Module Microsoft.PowerShell.SecretManagement -ListAvailable)) { throw "Please install the Microsoft.PowerShell.SecretManagement module to use this cmdlet" } if ($null -ne (Get-Secret -Name $secretName -ErrorAction Ignore) -and -not $Overwrite) { throw "Secret '$secretName' already exists. Use `-Overwrite` to overwrite." } Test-OpenApiKey -ApiKey $ApiKey Set-Secret -Name CodexOpenApiKey -Secret $ApiKey } function Test-Completion { param ( $Lines ) $tokens = $null $err = $null $ast = [System.Management.Automation.Language.Parser]::ParseInput($Lines, [ref]$tokens, [ref]$err) if ($err.Length -gt 0) { return "# This is not a valid PowerShell script" } $commands = $ast.FindAll({$true},$true) | Where-Object { $_ -is [System.Management.Automation.Language.CommandAst] } | ForEach-Object { $_.CommandElements[0].Value } | Sort-Object -Unique foreach ($command in $commands) { $c = Get-Command $command -ErrorAction Ignore if ($null -eq $c) { return "# '$command' not found on this system" } } } function Test-OpenApiKey { param ( [Parameter(Mandatory)] [securestring]$ApiKey ) try { Write-Progress -Activity "OpenAI Key" -Status "Validating..." $null = Invoke-RestMethod -Uri 'https://api.openai.com/v1/engines' -Authentication Bearer -Token $ApiKey Write-Progress -Activity "OpenAI Key" -Completed } catch { $statusCode = $_.Exception.Response.StatusCode.value__ throw "Failed to access OpenAI api [$statusCode]. Please check your OpenAI API key (https://beta.openai.com/account/api-keys) and Organization ID (https://beta.openai.com/account/org-settings)." } } $engine = 'code-davinci-002' $secretName = 'CodexOpenApiKey' $contextList = [System.Collections.Generic.List[string]]::new() $defaultContext = @" # what processes are hogging the most cpu? Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 10 # stop the chrome processes Get-Process chrome | Stop-Process # what's my IP address? (Invoke-WebRequest -uri "http://ifconfig.me/ip").Content # what's the weather in New York? (Invoke-WebRequest -uri "wttr.in/NewYork").Content # make a git ignore with node modules and src in it "node_modules src" | Out-File .gitignore # open it in notepad notepad .gitignore # what's running on port 1018? Get-Process -Id (Get-NetTCPConnection -LocalPort 1018).OwningProcess # kill process 1584 Stop-Process -Id 1584 # what other devices are on my network? Get-NetIPAddress | Format-Table # how much storage is left on my pc? Get-WmiObject -Class Win32_LogicalDisk | Select-Object -Property DeviceID,FreeSpace,Size,DriveType | Format-Table -AutoSize # how many GB is 367247884288 B? (367247884288 / 1GB) "@ function Get-CodexCompletion { param ( [ValidateNotNullOrEmpty()] [string]$Line ) $ApiKey = Get-Secret -Name $secretName -ErrorAction Ignore if ($null -eq $ApiKey) { throw "OpenAI API key not found. Please use Register-CodexOpenApiKey to register your OpenAI API key" } $contextList.Add("\n" + $Line.Replace('"', '\"')) $trimList = $false do { if ($trimList) { $contextList.RemoveAt(0) } $context = [string]::Join("\n", $contextList.ToArray()).Replace('"', '\"') [int]$tokenCount = $context.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries).count + $contextList.Count - 1 $trimList = $true } while ($tokenCount -gt 4096) $body = "{`"prompt`": `"<# powershell #>\n\n$context`", `"temperature`": 0, `"max_tokens`": 300, `"stop`":`"#`"}" Write-Progress -Activity "Codex" -Status "Getting completion..." try { $completion = Invoke-RestMethod -Uri "https://api.openai.com/v1/engines/$engine/completions" -ContentType 'application/json' -Authentication Bearer -Token $ApiKey -Body $body -Method Post } catch { Write-Error $_ Write-Verbose -Verbose $body } Write-Progress -Activity "Codex" -Completed $response = $completion.Choices.Text $contextList.Add($response.Trim().Replace("`n", "\n")) return $response } |