Upload files to "/"
This commit is contained in:
@@ -0,0 +1,581 @@
|
||||
#requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Idempotent Windows setup for Claude Code + CLIProxyAPI + claudex aliases.
|
||||
|
||||
.DESCRIPTION
|
||||
- Installs Claude Code only when the `claude` command is missing.
|
||||
- Downloads CLIProxyAPI v7.2.71 only when the expected executable is missing
|
||||
(or when -ForceReinstall is supplied).
|
||||
- Creates/updates config.yaml, preserving a timestamped backup when replacing it.
|
||||
- Adds a managed `claudex` function to Windows PowerShell and PowerShell 7 profiles.
|
||||
- Runs Codex OAuth login only when no Codex auth JSON is detected.
|
||||
- Creates/updates a Task Scheduler task that starts at boot and restarts the proxy.
|
||||
- Starts the task and verifies that TCP port 8317 is listening.
|
||||
|
||||
Run from PowerShell:
|
||||
powershell -ExecutionPolicy Bypass -File .\setup-claudex-proxy.ps1
|
||||
|
||||
Optional:
|
||||
.\setup-claudex-proxy.ps1 -SkipCodexLogin
|
||||
.\setup-claudex-proxy.ps1 -ForceReinstall
|
||||
.\setup-claudex-proxy.ps1 -NoStart
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$ForceReinstall,
|
||||
[switch]$SkipCodexLogin,
|
||||
[switch]$NoStart
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ProxyVersion = '7.2.71'
|
||||
$ProxyArchiveName = "CLIProxyAPI_${ProxyVersion}_windows_amd64.zip"
|
||||
$ProxyDownloadUrl = "https://github.com/router-for-me/CLIProxyAPI/releases/download/v$ProxyVersion/$ProxyArchiveName"
|
||||
$InstallDir = Join-Path $env:ProgramFiles "CLIProxyAPI_${ProxyVersion}_windows_amd64"
|
||||
$ProxyExe = Join-Path $InstallDir 'cli-proxy-api.exe'
|
||||
$ConfigPath = Join-Path $InstallDir 'config.yaml'
|
||||
$RunnerPath = Join-Path $InstallDir 'run-proxy.ps1'
|
||||
$TaskName = 'CLIProxyAPI'
|
||||
$ProxyPort = 8317
|
||||
$ProxyApiKey = 'sk-dummy'
|
||||
$AuthDir = Join-Path $env:USERPROFILE '.cli-proxy-api'
|
||||
|
||||
function Write-Step {
|
||||
param([Parameter(Mandatory)][string]$Message)
|
||||
Write-Host "`n==> $Message" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
function Write-Skip {
|
||||
param([Parameter(Mandatory)][string]$Message)
|
||||
Write-Host " SKIP: $Message" -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
function Test-Administrator {
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Restart-Elevated {
|
||||
Write-Step 'Administrator rights are required; reopening this script with UAC'
|
||||
|
||||
$forward = @(
|
||||
'-NoLogo',
|
||||
'-NoProfile',
|
||||
'-ExecutionPolicy', 'Bypass',
|
||||
'-File', ('"{0}"' -f $PSCommandPath)
|
||||
)
|
||||
|
||||
if ($ForceReinstall) { $forward += '-ForceReinstall' }
|
||||
if ($SkipCodexLogin) { $forward += '-SkipCodexLogin' }
|
||||
if ($NoStart) { $forward += '-NoStart' }
|
||||
|
||||
Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList ($forward -join ' ')
|
||||
exit
|
||||
}
|
||||
|
||||
function Refresh-ProcessPath {
|
||||
$machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine')
|
||||
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||
$env:Path = @($machinePath, $userPath) -join ';'
|
||||
}
|
||||
|
||||
function Write-Utf8NoBom {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Path,
|
||||
[Parameter(Mandatory)][string]$Content
|
||||
)
|
||||
|
||||
$parent = Split-Path -Parent $Path
|
||||
if ($parent) {
|
||||
[IO.Directory]::CreateDirectory($parent) | Out-Null
|
||||
}
|
||||
|
||||
$encoding = New-Object Text.UTF8Encoding($false)
|
||||
[IO.File]::WriteAllText($Path, $Content, $encoding)
|
||||
}
|
||||
|
||||
function Set-ManagedFile {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Path,
|
||||
[Parameter(Mandatory)][string]$Content,
|
||||
[switch]$BackupExisting
|
||||
)
|
||||
|
||||
$normalized = $Content.TrimEnd() + [Environment]::NewLine
|
||||
|
||||
if (Test-Path -LiteralPath $Path) {
|
||||
$existing = [IO.File]::ReadAllText($Path)
|
||||
if ($existing -eq $normalized) {
|
||||
Write-Skip "$Path is already correct"
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($BackupExisting) {
|
||||
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$backup = "$Path.bak-$stamp"
|
||||
Copy-Item -LiteralPath $Path -Destination $backup -Force
|
||||
Write-Host " Backed up existing file to: $backup"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Utf8NoBom -Path $Path -Content $normalized
|
||||
Write-Host " Wrote: $Path"
|
||||
return $true
|
||||
}
|
||||
|
||||
function Install-ClaudeCodeIfMissing {
|
||||
Write-Step 'Checking Claude Code'
|
||||
|
||||
if (Get-Command 'claude' -ErrorAction SilentlyContinue) {
|
||||
$version = try { (& claude --version 2>$null | Select-Object -First 1) } catch { $null }
|
||||
Write-Skip ("Claude Code is already installed" + $(if ($version) { ": $version" } else { '' }))
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host ' Claude Code was not found. Running the official Windows installer...'
|
||||
$installer = Invoke-RestMethod -Uri 'https://claude.ai/install.ps1'
|
||||
Invoke-Expression $installer
|
||||
Refresh-ProcessPath
|
||||
|
||||
if (-not (Get-Command 'claude' -ErrorAction SilentlyContinue)) {
|
||||
throw 'Claude Code installation finished, but `claude` is still unavailable in PATH. Open a new PowerShell window and rerun this script.'
|
||||
}
|
||||
|
||||
Write-Host ' Claude Code installed.'
|
||||
}
|
||||
|
||||
function Install-ProxyIfMissing {
|
||||
Write-Step "Checking CLIProxyAPI v$ProxyVersion"
|
||||
|
||||
if ((Test-Path -LiteralPath $ProxyExe) -and -not $ForceReinstall) {
|
||||
Write-Skip "$ProxyExe already exists"
|
||||
return
|
||||
}
|
||||
|
||||
$tempRoot = Join-Path ([IO.Path]::GetTempPath()) ("cliproxy-setup-" + [guid]::NewGuid().ToString('N'))
|
||||
$zipPath = Join-Path $tempRoot $ProxyArchiveName
|
||||
$extractPath = Join-Path $tempRoot 'extract'
|
||||
|
||||
try {
|
||||
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
||||
New-Item -ItemType Directory -Path $extractPath -Force | Out-Null
|
||||
|
||||
Write-Host " Downloading: $ProxyDownloadUrl"
|
||||
Invoke-WebRequest -Uri $ProxyDownloadUrl -OutFile $zipPath -UseBasicParsing
|
||||
|
||||
Write-Host ' Extracting archive...'
|
||||
Expand-Archive -LiteralPath $zipPath -DestinationPath $extractPath -Force
|
||||
|
||||
$foundExe = Get-ChildItem -LiteralPath $extractPath -Filter 'cli-proxy-api.exe' -File -Recurse |
|
||||
Select-Object -First 1
|
||||
|
||||
if (-not $foundExe) {
|
||||
throw 'The downloaded archive did not contain cli-proxy-api.exe.'
|
||||
}
|
||||
|
||||
$sourceRoot = $foundExe.Directory.FullName
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $sourceRoot '*') -Destination $InstallDir -Recurse -Force
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ProxyExe)) {
|
||||
throw "Extraction completed, but $ProxyExe was not created."
|
||||
}
|
||||
|
||||
Write-Host " Installed to: $InstallDir"
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
function Configure-Proxy {
|
||||
Write-Step 'Configuring CLIProxyAPI'
|
||||
|
||||
New-Item -ItemType Directory -Path $AuthDir -Force | Out-Null
|
||||
$authDirYaml = $AuthDir.Replace('\', '/')
|
||||
|
||||
$config = @"
|
||||
host: "127.0.0.1"
|
||||
port: $ProxyPort
|
||||
|
||||
auth-dir: "$authDirYaml"
|
||||
|
||||
api-keys:
|
||||
- "$ProxyApiKey"
|
||||
|
||||
debug: false
|
||||
logging-to-file: true
|
||||
logs-max-total-size-mb: 256
|
||||
usage-statistics-enabled: false
|
||||
"@
|
||||
|
||||
Set-ManagedFile -Path $ConfigPath -Content $config -BackupExisting | Out-Null
|
||||
}
|
||||
|
||||
function Configure-Runner {
|
||||
Write-Step 'Configuring the proxy watchdog script'
|
||||
|
||||
$escapedInstallDir = $InstallDir.Replace("'", "''")
|
||||
$escapedProxyExe = $ProxyExe.Replace("'", "''")
|
||||
$escapedConfigPath = $ConfigPath.Replace("'", "''")
|
||||
$wrapperLog = (Join-Path $InstallDir 'proxy-watchdog.log').Replace("'", "''")
|
||||
|
||||
$runner = @"
|
||||
`$ErrorActionPreference = 'Continue'
|
||||
`$installDir = '$escapedInstallDir'
|
||||
`$proxyExe = '$escapedProxyExe'
|
||||
`$configPath = '$escapedConfigPath'
|
||||
`$wrapperLog = '$wrapperLog'
|
||||
|
||||
Set-Location -LiteralPath `$installDir
|
||||
|
||||
while (`$true) {
|
||||
`$started = Get-Date
|
||||
|
||||
try {
|
||||
& `$proxyExe -config `$configPath
|
||||
`$exitCode = `$LASTEXITCODE
|
||||
Add-Content -LiteralPath `$wrapperLog -Value ("{0:u} Proxy exited with code {1}; restarting in 5 seconds." -f (Get-Date), `$exitCode)
|
||||
}
|
||||
catch {
|
||||
Add-Content -LiteralPath `$wrapperLog -Value ("{0:u} Proxy crashed: {1}; restarting in 5 seconds." -f (Get-Date), `$_.Exception.Message)
|
||||
}
|
||||
|
||||
# Avoid a hot restart loop if startup fails instantly.
|
||||
`$runtime = (Get-Date) - `$started
|
||||
if (`$runtime.TotalSeconds -lt 5) {
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
Set-ManagedFile -Path $RunnerPath -Content $runner | Out-Null
|
||||
}
|
||||
|
||||
function Upsert-ManagedProfileBlock {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Path,
|
||||
[Parameter(Mandatory)][string]$Block
|
||||
)
|
||||
|
||||
$begin = '# >>> CLIProxyAPI claudex BEGIN >>>'
|
||||
$end = '# <<< CLIProxyAPI claudex END <<<'
|
||||
$pattern = '(?ms)^[ \t]*' + [regex]::Escape($begin) + '.*?^[ \t]*' + [regex]::Escape($end) + '[ \t]*(?:\r?\n)?'
|
||||
|
||||
$existing = if (Test-Path -LiteralPath $Path) {
|
||||
[IO.File]::ReadAllText($Path)
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
|
||||
$withoutManagedBlock = [regex]::Replace($existing, $pattern, '').TrimEnd()
|
||||
$newContent = if ([string]::IsNullOrWhiteSpace($withoutManagedBlock)) {
|
||||
$Block.TrimEnd() + [Environment]::NewLine
|
||||
}
|
||||
else {
|
||||
$withoutManagedBlock + [Environment]::NewLine + [Environment]::NewLine + $Block.TrimEnd() + [Environment]::NewLine
|
||||
}
|
||||
|
||||
if ($existing -eq $newContent) {
|
||||
Write-Skip "$Path already contains the current claudex block"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Utf8NoBom -Path $Path -Content $newContent
|
||||
Write-Host " Updated: $Path"
|
||||
}
|
||||
|
||||
function Configure-PowerShellProfiles {
|
||||
Write-Step 'Configuring the claudex model aliases'
|
||||
|
||||
$profileBlock = @'
|
||||
# >>> CLIProxyAPI claudex BEGIN >>>
|
||||
$global:ClaudeModelAliases = @{
|
||||
sol = 'gpt-5.6-sol'
|
||||
terra = 'gpt-5.6-terra'
|
||||
luna = 'gpt-5.6-luna'
|
||||
}
|
||||
|
||||
function claudex {
|
||||
param(
|
||||
[Parameter(Position = 0)]
|
||||
[string]$Model = 'sol',
|
||||
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[string[]]$ClaudeArgs
|
||||
)
|
||||
|
||||
$resolvedModel = if ($global:ClaudeModelAliases.ContainsKey($Model)) {
|
||||
$global:ClaudeModelAliases[$Model]
|
||||
}
|
||||
else {
|
||||
$Model
|
||||
}
|
||||
|
||||
$names = @(
|
||||
'ANTHROPIC_BASE_URL',
|
||||
'ANTHROPIC_AUTH_TOKEN',
|
||||
'CLAUDE_CODE_SUBAGENT_MODEL'
|
||||
)
|
||||
|
||||
$previous = @{}
|
||||
foreach ($name in $names) {
|
||||
$previous[$name] = [Environment]::GetEnvironmentVariable($name, 'Process')
|
||||
}
|
||||
|
||||
[Environment]::SetEnvironmentVariable('ANTHROPIC_BASE_URL', 'http://127.0.0.1:8317', 'Process')
|
||||
[Environment]::SetEnvironmentVariable('ANTHROPIC_AUTH_TOKEN', 'sk-dummy', 'Process')
|
||||
[Environment]::SetEnvironmentVariable('CLAUDE_CODE_SUBAGENT_MODEL', $resolvedModel, 'Process')
|
||||
|
||||
try {
|
||||
& claude --model $resolvedModel @ClaudeArgs
|
||||
}
|
||||
finally {
|
||||
foreach ($name in $names) {
|
||||
[Environment]::SetEnvironmentVariable($name, $previous[$name], 'Process')
|
||||
}
|
||||
}
|
||||
}
|
||||
# <<< CLIProxyAPI claudex END <<<
|
||||
'@
|
||||
|
||||
$documents = [Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments)
|
||||
|
||||
# All-host and ConsoleHost profiles for Windows PowerShell 5.1 and PowerShell 7.
|
||||
# Writing the managed block last also overrides an older unmarked `claudex`
|
||||
# function that may already exist earlier in a profile.
|
||||
$profilePaths = @(
|
||||
(Join-Path $documents 'WindowsPowerShell\profile.ps1'),
|
||||
(Join-Path $documents 'WindowsPowerShell\Microsoft.PowerShell_profile.ps1'),
|
||||
(Join-Path $documents 'PowerShell\profile.ps1'),
|
||||
(Join-Path $documents 'PowerShell\Microsoft.PowerShell_profile.ps1')
|
||||
) | Select-Object -Unique
|
||||
|
||||
foreach ($profilePath in $profilePaths) {
|
||||
Upsert-ManagedProfileBlock -Path $profilePath -Block $profileBlock
|
||||
}
|
||||
}
|
||||
|
||||
function Test-CodexAuthentication {
|
||||
if (-not (Test-Path -LiteralPath $AuthDir)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
foreach ($file in Get-ChildItem -LiteralPath $AuthDir -Filter '*.json' -File -ErrorAction SilentlyContinue) {
|
||||
try {
|
||||
$data = Get-Content -LiteralPath $file.FullName -Raw | ConvertFrom-Json
|
||||
if (($data.type -eq 'codex') -and ($data.refresh_token -or $data.access_token)) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# Ignore unrelated or malformed JSON files.
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
function Ensure-CodexAuthentication {
|
||||
Write-Step 'Checking Codex OAuth authentication'
|
||||
|
||||
if (Test-CodexAuthentication) {
|
||||
Write-Skip 'A Codex OAuth credential already exists'
|
||||
return
|
||||
}
|
||||
|
||||
if ($SkipCodexLogin) {
|
||||
Write-Warning 'No Codex credential was found, and -SkipCodexLogin was supplied.'
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host ' No Codex credential found. A browser login will open now.'
|
||||
& $ProxyExe -config $ConfigPath -codex-login
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Codex login exited with code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
if (-not (Test-CodexAuthentication)) {
|
||||
throw "Codex login completed, but no Codex credential was detected in $AuthDir."
|
||||
}
|
||||
|
||||
Write-Host ' Codex OAuth credential saved.'
|
||||
}
|
||||
|
||||
function Get-ExpectedTaskActionArguments {
|
||||
$escapedRunner = $RunnerPath.Replace('"', '\"')
|
||||
return "-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$escapedRunner`""
|
||||
}
|
||||
|
||||
function Test-ScheduledTaskMatches {
|
||||
param([Parameter(Mandatory)]$Task)
|
||||
|
||||
$expectedExe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$expectedArgs = Get-ExpectedTaskActionArguments
|
||||
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
|
||||
$action = @($Task.Actions) | Select-Object -First 1
|
||||
$trigger = @($Task.Triggers) | Select-Object -First 1
|
||||
|
||||
if (-not $action -or -not $trigger) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$executeMatches = ([IO.Path]::GetFullPath($action.Execute) -ieq [IO.Path]::GetFullPath($expectedExe))
|
||||
$argumentsMatch = ($action.Arguments -eq $expectedArgs)
|
||||
$userMatches = ($Task.Principal.UserId -ieq $currentUser)
|
||||
$logonMatches = ($Task.Principal.LogonType -eq 'S4U')
|
||||
$startupTrigger = ($trigger.CimClass.CimClassName -eq 'MSFT_TaskBootTrigger')
|
||||
|
||||
return ($executeMatches -and $argumentsMatch -and $userMatches -and $logonMatches -and $startupTrigger)
|
||||
}
|
||||
|
||||
function Configure-ScheduledTask {
|
||||
Write-Step 'Configuring Task Scheduler'
|
||||
|
||||
Import-Module ScheduledTasks
|
||||
|
||||
$existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
|
||||
if ($existingTask -and (Test-ScheduledTaskMatches -Task $existingTask)) {
|
||||
Write-Skip "Scheduled task '$TaskName' is already correct"
|
||||
return
|
||||
}
|
||||
|
||||
$powershellExe = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute $powershellExe `
|
||||
-Argument (Get-ExpectedTaskActionArguments) `
|
||||
-WorkingDirectory $InstallDir
|
||||
|
||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||
|
||||
# S4U runs under the current Windows user without storing a password.
|
||||
# The proxy config uses an explicit auth directory, so it does not depend on
|
||||
# the task receiving a normal interactive user profile.
|
||||
$principal = New-ScheduledTaskPrincipal `
|
||||
-UserId ([Security.Principal.WindowsIdentity]::GetCurrent().Name) `
|
||||
-LogonType S4U `
|
||||
-RunLevel Highest
|
||||
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries `
|
||||
-DontStopIfGoingOnBatteries `
|
||||
-StartWhenAvailable `
|
||||
-RestartCount 999 `
|
||||
-RestartInterval (New-TimeSpan -Minutes 1) `
|
||||
-ExecutionTimeLimit ([TimeSpan]::Zero) `
|
||||
-MultipleInstances IgnoreNew
|
||||
|
||||
$task = New-ScheduledTask `
|
||||
-Action $action `
|
||||
-Trigger $trigger `
|
||||
-Principal $principal `
|
||||
-Settings $settings `
|
||||
-Description 'Runs CLIProxyAPI for Claude Code through the local Codex-compatible proxy.'
|
||||
|
||||
Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force | Out-Null
|
||||
Write-Host " Registered scheduled task: $TaskName"
|
||||
}
|
||||
|
||||
function Test-TcpPort {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$HostName,
|
||||
[Parameter(Mandatory)][int]$Port,
|
||||
[int]$TimeoutMs = 1500
|
||||
)
|
||||
|
||||
$client = New-Object Net.Sockets.TcpClient
|
||||
try {
|
||||
$result = $client.BeginConnect($HostName, $Port, $null, $null)
|
||||
if (-not $result.AsyncWaitHandle.WaitOne($TimeoutMs, $false)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$client.EndConnect($result)
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
return $false
|
||||
}
|
||||
finally {
|
||||
$client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
function Start-ProxyTask {
|
||||
if ($NoStart) {
|
||||
Write-Step 'Skipping task startup because -NoStart was supplied'
|
||||
return
|
||||
}
|
||||
|
||||
Write-Step 'Starting CLIProxyAPI'
|
||||
|
||||
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction Stop
|
||||
if ($task.State -eq 'Running') {
|
||||
Write-Skip "Scheduled task '$TaskName' is already running"
|
||||
}
|
||||
else {
|
||||
# Stop a manually launched copy from this exact installation before
|
||||
# starting the managed task, preventing a port-conflict restart loop.
|
||||
Get-CimInstance Win32_Process -Filter "Name = 'cli-proxy-api.exe'" -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.ExecutablePath -and ($_.ExecutablePath -ieq $ProxyExe) } |
|
||||
ForEach-Object {
|
||||
Write-Host " Stopping unmanaged process PID $($_.ProcessId)..."
|
||||
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Start-ScheduledTask -TaskName $TaskName
|
||||
}
|
||||
|
||||
$listening = $false
|
||||
foreach ($attempt in 1..12) {
|
||||
Start-Sleep -Seconds 1
|
||||
if (Test-TcpPort -HostName '127.0.0.1' -Port $ProxyPort) {
|
||||
$listening = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($listening) {
|
||||
Write-Host " Proxy is listening on http://127.0.0.1:$ProxyPort" -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
$info = Get-ScheduledTaskInfo -TaskName $TaskName
|
||||
Write-Warning "Task started, but port $ProxyPort is not listening yet. LastTaskResult: $($info.LastTaskResult)"
|
||||
Write-Warning "Check: $InstallDir\proxy-watchdog.log and $InstallDir\logs"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Administrator)) {
|
||||
Restart-Elevated
|
||||
}
|
||||
|
||||
try {
|
||||
Install-ClaudeCodeIfMissing
|
||||
Install-ProxyIfMissing
|
||||
Configure-Proxy
|
||||
Configure-Runner
|
||||
Configure-PowerShellProfiles
|
||||
Ensure-CodexAuthentication
|
||||
Configure-ScheduledTask
|
||||
Start-ProxyTask
|
||||
|
||||
Write-Host "`nSetup complete." -ForegroundColor Green
|
||||
Write-Host 'Open a new PowerShell window, then use:'
|
||||
Write-Host ' claudex sol'
|
||||
Write-Host ' claudex terra'
|
||||
Write-Host ' claudex luna'
|
||||
Write-Host ' claudex gpt-5.6-sol --dangerously-skip-permissions'
|
||||
}
|
||||
catch {
|
||||
Write-Host "`nSetup failed: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host $_.ScriptStackTrace -ForegroundColor DarkGray
|
||||
exit 1
|
||||
}
|
||||
Reference in New Issue
Block a user