feat: publish codex retry gateway
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Get-GatewayRoot {
|
||||
return Split-Path -Parent $PSScriptRoot
|
||||
}
|
||||
|
||||
function Get-GatewayBaseUrl {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ListenHost,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$ListenPort
|
||||
)
|
||||
|
||||
return "http://{0}:{1}" -f $ListenHost, $ListenPort
|
||||
}
|
||||
|
||||
function Get-GatewayStatePaths {
|
||||
param(
|
||||
[string]$StateRoot = (Join-Path $HOME ".codex-retry-gateway")
|
||||
)
|
||||
|
||||
return [pscustomobject]@{
|
||||
StateRoot = $StateRoot
|
||||
ConfigDir = Join-Path $StateRoot "config"
|
||||
LogDir = Join-Path $StateRoot "logs"
|
||||
BackupDir = Join-Path $StateRoot "backups"
|
||||
ConfigPath = Join-Path $StateRoot "config\config.json"
|
||||
LogPath = Join-Path $StateRoot "logs\gateway.log"
|
||||
StatePath = Join-Path $StateRoot "state.json"
|
||||
PidPath = Join-Path $StateRoot "gateway.pid"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-GatewayBaseUrlFromConfig {
|
||||
param(
|
||||
$GatewayConfig
|
||||
)
|
||||
|
||||
if ($null -eq $GatewayConfig) {
|
||||
return $null
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace([string]$GatewayConfig.listen_host) -or $null -eq $GatewayConfig.listen_port) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return Get-GatewayBaseUrl `
|
||||
-ListenHost ([string]$GatewayConfig.listen_host) `
|
||||
-ListenPort ([int]$GatewayConfig.listen_port)
|
||||
}
|
||||
|
||||
function Ensure-Directory {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
New-Item -ItemType Directory -Path $Path -Force | Out-Null
|
||||
}
|
||||
|
||||
function Write-Utf8NoBomFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Content
|
||||
)
|
||||
|
||||
$parent = Split-Path -Parent $Path
|
||||
if ($parent) {
|
||||
Ensure-Directory -Path $parent
|
||||
}
|
||||
|
||||
$encoding = [System.Text.UTF8Encoding]::new($false)
|
||||
[System.IO.File]::WriteAllText($Path, $Content, $encoding)
|
||||
}
|
||||
|
||||
function Read-JsonFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$raw = Get-Content -LiteralPath $Path -Raw
|
||||
if ([string]::IsNullOrWhiteSpace($raw)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Write-JsonFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path,
|
||||
[Parameter(Mandatory = $true)]
|
||||
$Value
|
||||
)
|
||||
|
||||
$json = $Value | ConvertTo-Json -Depth 20
|
||||
Write-Utf8NoBomFile -Path $Path -Content ($json + "`n")
|
||||
}
|
||||
|
||||
function Get-CodexProviderContext {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$CodexConfigPath
|
||||
)
|
||||
|
||||
$content = Get-Content -LiteralPath $CodexConfigPath -Raw
|
||||
$providerMatch = [regex]::Match($content, '(?m)^\s*model_provider\s*=\s*"([^"]+)"\s*$')
|
||||
if (-not $providerMatch.Success) {
|
||||
throw "model_provider was not found in $CodexConfigPath"
|
||||
}
|
||||
|
||||
$providerName = $providerMatch.Groups[1].Value
|
||||
$sectionPattern = "(?ms)^\[model_providers\." + [regex]::Escape($providerName) + "\]\s*$.*?(?=^\[|\z)"
|
||||
$sectionMatch = [regex]::Match($content, $sectionPattern)
|
||||
if (-not $sectionMatch.Success) {
|
||||
throw "[model_providers.$providerName] was not found in $CodexConfigPath"
|
||||
}
|
||||
|
||||
$sectionText = $sectionMatch.Value
|
||||
$baseUrlMatch = [regex]::Match($sectionText, '(?m)^\s*base_url\s*=\s*"([^"]+)"\s*$')
|
||||
if (-not $baseUrlMatch.Success) {
|
||||
throw "base_url was not found in [model_providers.$providerName]"
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
Content = $content
|
||||
ProviderName = $providerName
|
||||
SectionText = $sectionText
|
||||
SectionIndex = $sectionMatch.Index
|
||||
SectionLength = $sectionMatch.Length
|
||||
CurrentBaseUrl = $baseUrlMatch.Groups[1].Value
|
||||
BaseUrlLineText = $baseUrlMatch.Value
|
||||
}
|
||||
}
|
||||
|
||||
function Set-CodexProviderBaseUrl {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$CodexConfigPath,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ProviderName,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$NewBaseUrl
|
||||
)
|
||||
|
||||
$context = Get-CodexProviderContext -CodexConfigPath $CodexConfigPath
|
||||
if ($context.ProviderName -ne $ProviderName) {
|
||||
throw "model_provider changed unexpectedly: expected $ProviderName, actual $($context.ProviderName)"
|
||||
}
|
||||
|
||||
$updatedSection = [regex]::Replace(
|
||||
$context.SectionText,
|
||||
'(?m)^(\s*base_url\s*=\s*")([^"]*)("\s*)$',
|
||||
{
|
||||
param($match)
|
||||
return $match.Groups[1].Value + $NewBaseUrl + $match.Groups[3].Value
|
||||
},
|
||||
1
|
||||
)
|
||||
|
||||
$updatedContent =
|
||||
$context.Content.Substring(0, $context.SectionIndex) +
|
||||
$updatedSection +
|
||||
$context.Content.Substring($context.SectionIndex + $context.SectionLength)
|
||||
|
||||
Write-Utf8NoBomFile -Path $CodexConfigPath -Content $updatedContent
|
||||
}
|
||||
|
||||
function Test-ProcessAlive {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$ProcessId
|
||||
)
|
||||
|
||||
try {
|
||||
$null = Get-Process -Id $ProcessId -ErrorAction Stop
|
||||
return $true
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Wait-GatewayHealth {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ListenHost,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]$ListenPort,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$HealthPath,
|
||||
[int]$TimeoutSeconds = 10
|
||||
)
|
||||
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||
$healthUrl = "http://{0}:{1}{2}" -f $ListenHost, $ListenPort, $HealthPath
|
||||
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 2
|
||||
if ($response.StatusCode -eq 200) {
|
||||
return $response
|
||||
}
|
||||
} catch {
|
||||
Start-Sleep -Milliseconds 200
|
||||
}
|
||||
}
|
||||
|
||||
throw "Gateway health check timed out: $healthUrl"
|
||||
}
|
||||
|
||||
function Normalize-IntArray {
|
||||
param(
|
||||
$Values,
|
||||
[int[]]$Default = @(516)
|
||||
)
|
||||
|
||||
if ($null -eq $Values) {
|
||||
return ,@($Default)
|
||||
}
|
||||
|
||||
$queue = New-Object System.Collections.Generic.List[object]
|
||||
foreach ($item in @($Values)) {
|
||||
$queue.Add($item)
|
||||
}
|
||||
|
||||
$normalized = @()
|
||||
foreach ($value in $queue) {
|
||||
if ($null -eq $value) {
|
||||
continue
|
||||
}
|
||||
if ($value -is [System.Collections.IEnumerable] -and -not ($value -is [string])) {
|
||||
foreach ($nestedValue in @($value)) {
|
||||
if ($null -eq $nestedValue) {
|
||||
continue
|
||||
}
|
||||
$normalized += [int]$nestedValue
|
||||
}
|
||||
continue
|
||||
}
|
||||
$normalized += [int]$value
|
||||
}
|
||||
|
||||
if ($normalized.Count -eq 0) {
|
||||
return ,@($Default)
|
||||
}
|
||||
|
||||
return ,@($normalized)
|
||||
}
|
||||
|
||||
function Normalize-StringArray {
|
||||
param(
|
||||
$Values,
|
||||
[string[]]$Default
|
||||
)
|
||||
|
||||
if ($null -eq $Values) {
|
||||
return ,@($Default)
|
||||
}
|
||||
|
||||
$normalized = @()
|
||||
foreach ($value in @($Values)) {
|
||||
if ($value -is [System.Collections.IEnumerable] -and -not ($value -is [string])) {
|
||||
foreach ($nestedValue in @($value)) {
|
||||
if ([string]::IsNullOrWhiteSpace([string]$nestedValue)) {
|
||||
continue
|
||||
}
|
||||
foreach ($part in ([string]$nestedValue).Split(@(" ", "`t", "`r", "`n"), [System.StringSplitOptions]::RemoveEmptyEntries)) {
|
||||
$normalized += $part
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace([string]$value)) {
|
||||
continue
|
||||
}
|
||||
foreach ($part in ([string]$value).Split(@(" ", "`t", "`r", "`n"), [System.StringSplitOptions]::RemoveEmptyEntries)) {
|
||||
$normalized += $part
|
||||
}
|
||||
}
|
||||
|
||||
if ($normalized.Count -eq 0) {
|
||||
return ,@($Default)
|
||||
}
|
||||
|
||||
return ,@($normalized)
|
||||
}
|
||||
Reference in New Issue
Block a user