diff --git a/.env.example b/.env.example index b133765..d9f0e94 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,11 @@ SEARXNG_TOKEN= # Optional public URL reported by pi-agent-public.ps1 (e.g. your own tunnel domain). PI_AGENT_PUBLIC_URL= +# Optional SSH reverse-tunnel command for public access, run as a scheduled task +# by pi-agent-public.ps1 (registered only when set). Example for an "aliyun" host +# in ~/.ssh/config forwarding remote 8090 to local 30141: +# PI_AGENT_TUNNEL_COMMAND=C:\WINDOWS\System32\OpenSSH\ssh.exe -n -N -T -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -R 127.0.0.1:8090:127.0.0.1:30141 aliyun + # Optional higher Context7 quota. Context7 also works at its public unauthenticated limit. CONTEXT7_API_KEY= diff --git a/data/local-tools/pi-agent-public.ps1 b/data/local-tools/pi-agent-public.ps1 index 1083194..445baa6 100644 --- a/data/local-tools/pi-agent-public.ps1 +++ b/data/local-tools/pi-agent-public.ps1 @@ -1,4 +1,4 @@ -[CmdletBinding()] +[CmdletBinding()] param( [ValidateSet('status', 'start', 'restart', 'stop')] [string]$Action = 'status' @@ -8,12 +8,65 @@ $ErrorActionPreference = 'Stop' $webTaskName = 'PiAgentIntegratedWeb' $tunnelTaskName = 'PiAgentIntegratedTunnel' $localUrl = 'http://127.0.0.1:30141/' +# local-tools 位于 data/ 下,上两级才是项目根 +$projectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) + +# 加载项目 .env(独立运行的脚本不会自动加载,需手动解析) +$envFile = Join-Path $projectRoot '.env' +if (Test-Path $envFile) { + Get-Content $envFile -Encoding UTF8 | ForEach-Object { + if ($_ -match '^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$') { + $envName = $matches[1] + $envValue = $matches[2].Trim() + if (-not [string]::IsNullOrWhiteSpace($envValue) -and -not [Environment]::GetEnvironmentVariable($envName, 'Process')) { + Set-Item -Path "Env:$envName" -Value $envValue + } + } + } +} + +# 隧道命令从环境变量读取(.env 中设置 PI_AGENT_TUNNEL_COMMAND),例如: +# C:\WINDOWS\System32\OpenSSH\ssh.exe -n -N -T -o BatchMode=yes -R 127.0.0.1:8090:127.0.0.1:30141 aliyun +$tunnelCommand = $env:PI_AGENT_TUNNEL_COMMAND # 公网地址从环境变量读取(.env 中设置 PI_AGENT_PUBLIC_URL),不在代码里写死个人域名 $publicUrl = $env:PI_AGENT_PUBLIC_URL if ([string]::IsNullOrWhiteSpace($publicUrl)) { $publicUrl = '(未配置 PI_AGENT_PUBLIC_URL)' } +function Ensure-Tasks { + # Web supervisor 任务:必需,自动注册(相对路径,新机器可直接用) + if (-not (Get-ScheduledTask -TaskName $webTaskName -ErrorAction SilentlyContinue)) { + Write-Host " 注册计划任务 $webTaskName ..." + $supervisorPath = Join-Path $PSScriptRoot 'pi-agent-web-supervisor.ps1' + $action = New-ScheduledTaskAction -Execute 'powershell.exe' ` + -Argument "-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `\"$supervisorPath`\"" ` + -WorkingDirectory $projectRoot + $trigger = New-ScheduledTaskTrigger -AtLogOn + $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable ` + -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) ` + -ExecutionTimeLimit ([TimeSpan]::Zero) + Register-ScheduledTask -TaskName $webTaskName -Action $action -Trigger $trigger ` + -Settings $settings -Description 'Pi Agent Integrated Web supervisor' -Force | Out-Null + } + + # Tunnel 任务:可选,仅在配置了 PI_AGENT_TUNNEL_COMMAND 时注册 + if (Get-ScheduledTask -TaskName $tunnelTaskName -ErrorAction SilentlyContinue) { return } + if ([string]::IsNullOrWhiteSpace($tunnelCommand)) { + Write-Host " - 未配置 PI_AGENT_TUNNEL_COMMAND,跳过隧道任务" + return + } + Write-Host " 注册计划任务 $tunnelTaskName ..." + $parts = $tunnelCommand.Trim() -split '\s+', 2 + $action = New-ScheduledTaskAction -Execute $parts[0] -Argument $parts[1] + $trigger = New-ScheduledTaskTrigger -AtLogOn + $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable ` + -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) ` + -ExecutionTimeLimit ([TimeSpan]::Zero) + Register-ScheduledTask -TaskName $tunnelTaskName -Action $action -Trigger $trigger ` + -Settings $settings -Description 'Pi Agent Integrated public tunnel' -Force | Out-Null +} + function Get-PublicAccessStatus { $webTask = Get-ScheduledTask -TaskName $webTaskName -ErrorAction SilentlyContinue $tunnelTask = Get-ScheduledTask -TaskName $tunnelTaskName -ErrorAction SilentlyContinue @@ -44,7 +97,8 @@ switch ($Action) { Get-PublicAccessStatus } 'start' { - Start-ScheduledTask -TaskName $tunnelTaskName + Ensure-Tasks + Start-ScheduledTask -TaskName $tunnelTaskName -ErrorAction SilentlyContinue Start-ScheduledTask -TaskName $webTaskName Start-Sleep -Seconds 3 Get-PublicAccessStatus diff --git a/data/local-tools/pi-agent-web-supervisor.ps1 b/data/local-tools/pi-agent-web-supervisor.ps1 index c7db677..6a8b133 100644 --- a/data/local-tools/pi-agent-web-supervisor.ps1 +++ b/data/local-tools/pi-agent-web-supervisor.ps1 @@ -1,4 +1,4 @@ -[CmdletBinding()] +[CmdletBinding()] param() $ErrorActionPreference = 'Stop'