$ErrorActionPreference="Stop"$clashRoot=Join-Path$env:USERPROFILE".config\clash"$profilesDir=Join-Path$clashRoot"profiles"$generalConfig=Join-Path$clashRoot"config.yaml"$profilesList=Join-Path$profilesDir"list.yml"$runtimeConfig=Join-Path$profilesDir"codex-active-runtime.yml"$utf8=[System.Text.UTF8Encoding]::new($false)$startMarker=" # codex-openai-rules-start"$endMarker=" # codex-openai-rules-end"functionGet-GeneralValue{param([string[]]$Lines,[string]$Key)foreach($linein$Lines){if($line-match"^\s*$([regex]::Escape($Key)):\s*(.+?)\s*$"){return$Matches[1]}}return$null}functionGet-MatchProxyName{param([System.Collections.Generic.List[string]]$Lines)foreach($linein$Lines){if($line-match'^\s*-\s*MATCH,(.+?)\s*$'){return$Matches[1]}}throw"Could not determine fallback proxy name from MATCH rule"}functionRead-Utf8Text{param([string]$Path)return[System.IO.File]::ReadAllText($Path,$utf8)}functionRead-Utf8Lines{param([string]$Path)return[System.IO.File]::ReadAllLines($Path,$utf8)}functionWrite-Utf8Lines{param([string]$Path,[string[]]$Lines)[System.IO.File]::WriteAllLines($Path,$Lines,$utf8)}functionUpdate-ProfileRules{param([string]$Path)$lines=[System.Collections.Generic.List[string]]::new()foreach($lineinRead-Utf8Lines-Path$Path){[void]$lines.Add($line)}$geoIndex=-1for($i=0;$i-lt$lines.Count;$i++){if($lines[$i]-match'^\s*-\s*GEOIP,CN,DIRECT\s*$'){$geoIndex=$ibreak}}if($geoIndex-lt0){return$false}$startIndex=$lines.IndexOf($startMarker)if($startIndex-ge0){$endIndex=$lines.IndexOf($endMarker)if($endIndex-ge$startIndex){$lines.RemoveRange($startIndex,$endIndex-$startIndex+1)if($startIndex-lt$geoIndex){$geoIndex-=($endIndex-$startIndex+1)}}}$proxyName=Get-MatchProxyName-Lines$lines$managedBlock=@($startMarker," - DOMAIN-SUFFIX,chatgpt.com,$proxyName"," - DOMAIN-SUFFIX,openai.com,$proxyName"," - DOMAIN-SUFFIX,oaistatic.com,$proxyName"," - DOMAIN-SUFFIX,oaiusercontent.com,$proxyName",$endMarker)for($i=0;$i-lt$managedBlock.Count;$i++){$lines.Insert($geoIndex+$i,$managedBlock[$i])}Write-Utf8Lines-Path$Path-Lines$linesreturn$true}functionGet-ActiveProfilePath{param([string]$ListPath,[string]$ProfilesRoot)$raw=Read-Utf8Text-Path$ListPath$indexMatch=[regex]::Match($raw,'(?m)^index:\s*(\d+)\s*$')if(-not$indexMatch.Success){throw"Could not find active profile index in $ListPath"}$selectedIndex=[int]$indexMatch.Groups[1].Value$entries=[regex]::Matches($raw,'(?m)^\s*(?:-\s*)?time:\s*(.+?)\s*$')|ForEach-Object{$_.Groups[1].Value}if($selectedIndex-ge$entries.Count){throw"Active profile index $selectedIndex is out of range"}returnJoin-Path$ProfilesRoot$entries[$selectedIndex]}functionBuild-RuntimeConfig{param([string]$SourcePath,[string]$DestinationPath,[string]$Controller,[string]$Secret)$lines=Read-Utf8Lines-Path$SourcePath$updated=foreach($linein$lines){if($line-match'^log-level:'){"log-level: info"}elseif($line-match'^external-controller:'){"external-controller: $Controller"}elseif($line-match'^secret:'){"secret: $Secret"}else{$line}}Write-Utf8Lines-Path$DestinationPath-Lines$updated}functionReload-ActiveConfig{param([string]$Controller,[string]$Secret,[string]$ConfigPath)$body=@{path=$ConfigPath}|ConvertTo-JsonInvoke-RestMethod`-Headers@{Authorization="Bearer $Secret"}`-Uri"http://$Controller/configs?force=true"`-MethodPut`-ContentType"application/json"`-Body$body|Out-Null}if(-not(Test-Path$generalConfig)){throw"Missing Clash general config: $generalConfig"}if(-not(Test-Path$profilesList)){throw"Missing Clash profile list: $profilesList"}$generalLines=Read-Utf8Lines-Path$generalConfig$controller=Get-GeneralValue-Lines$generalLines-Key"external-controller"$secret=Get-GeneralValue-Lines$generalLines-Key"secret"if(-not$controller){throw"Missing external-controller in $generalConfig"}if($null-eq$secret){$secret=""}$profileFiles=Get-ChildItem$profilesDir-Filter*.yml|Where-Object{$_.Name-notin@("list.yml","codex-active-runtime.yml")-and$_.Name-notlike"*.codex-merged.yml"}foreach($profilein$profileFiles){$raw=Read-Utf8Text-Path$profile.FullNameif($raw-match'(?m)^rules:\s*$'-and$raw-match'(?m)^\s*-\s*GEOIP,CN,DIRECT\s*$'){Update-ProfileRules-Path$profile.FullName|Out-Null}}$activeProfile=Get-ActiveProfilePath-ListPath$profilesList-ProfilesRoot$profilesDirBuild-RuntimeConfig-SourcePath$activeProfile-DestinationPath$runtimeConfig-Controller$controller-Secret$secretReload-ActiveConfig-Controller$controller-Secret$secret-ConfigPath$runtimeConfig