chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:19:22 +08:00
commit 6366859bb3
222 changed files with 47313 additions and 0 deletions

199
start-host.ps1 Normal file
View File

@@ -0,0 +1,199 @@
# native process mode (called by start.ps1 when Docker is unavailable)
param(
[switch]$Rebuild,
[switch]$NoBrowser,
[switch]$ShowConsole # show service windows (debug); default: hidden
)
$ErrorActionPreference = "Stop"
$Root = $PSScriptRoot
Set-Location -LiteralPath $Root
$LogDir = Join-Path $Root ".runtime\logs"
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
# Always wipe previous native/docker leftovers before binding ports
Write-Host "==> Pre-clean before native start" -ForegroundColor DarkGray
& (Join-Path $Root "scripts\Stop-Stack.ps1") -Quiet
Start-Sleep -Seconds 1
Write-Host "==> Native mode: need Go / Python / Node / PostgreSQL" -ForegroundColor Yellow
if (-not $ShowConsole) {
Write-Host " services run hidden; logs -> .runtime\logs\" -ForegroundColor DarkGray
}
function Clear-Port([int]$port) {
$conns = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue
foreach ($c in $conns) {
$procId = $c.OwningProcess
if ($procId -gt 0) {
Write-Host " free port $port (pid=$procId)"
& taskkill.exe /F /T /PID $procId 2>$null | Out-Null
Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue
}
}
}
Write-Host "==> Clearing ports 8888/8001/8180/5173"
foreach ($p in 8888, 8001, 8180, 5173) { Clear-Port $p }
Start-Sleep -Seconds 1
function Reset-LogFile([string]$path) {
try {
if (Test-Path -LiteralPath $path) {
# 句柄未释放时 Set-Content 会失败:先挪走旧文件
$prev = "$path.prev"
Remove-Item -LiteralPath $prev -Force -ErrorAction SilentlyContinue
Move-Item -LiteralPath $path -Destination $prev -Force -ErrorAction Stop
}
Set-Content -LiteralPath $path -Value "" -Encoding UTF8
return $path
} catch {
$alt = ($path -replace '\.log$', ".$(Get-Date -Format 'HHmmss').log")
Set-Content -LiteralPath $alt -Value "" -Encoding UTF8
return $alt
}
}
function Start-Svc($name, $workDir, $filePath, $argumentList) {
$outLog = Reset-LogFile (Join-Path $LogDir "$name.out.log")
$errLog = Reset-LogFile (Join-Path $LogDir "$name.err.log")
$pidFile = Join-Path $LogDir "$name.pid"
if ($ShowConsole) {
$argStr = ""
if ($argumentList) {
$parts = @()
foreach ($a in $argumentList) {
if ($a -match '\s') { $parts += ('"' + $a + '"') } else { $parts += $a }
}
$argStr = $parts -join ' '
}
$cmd = "Set-Location -LiteralPath '$workDir'; Write-Host '[$name]' -ForegroundColor Cyan; & '$filePath' $argStr"
$p = Start-Process -FilePath "powershell.exe" -ArgumentList @(
"-NoExit", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", $cmd
) -WorkingDirectory $workDir -PassThru
} else {
$p = Start-Process -FilePath $filePath `
-ArgumentList $argumentList `
-WorkingDirectory $workDir `
-WindowStyle Hidden `
-RedirectStandardOutput $outLog `
-RedirectStandardError $errLog `
-PassThru
}
if ($p) { Set-Content -LiteralPath $pidFile -Value $p.Id -Encoding ASCII }
Write-Host " started $name (pid=$($p.Id))" -ForegroundColor DarkGray
}
$plat = Join-Path $Root "platform\platform.exe"
$gw = Join-Path $Root "gateway\gateway.exe"
# 默认重编译,避免改 Go 源码后 restart 仍跑旧 exe
$needPlat = $Rebuild -or -not (Test-Path $plat)
$needGw = $Rebuild -or -not (Test-Path $gw)
if (-not $needPlat -and (Test-Path $plat)) {
$exeTime = (Get-Item $plat).LastWriteTime
$newer = Get-ChildItem -Path (Join-Path $Root "platform") -Recurse -Include *.go -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt $exeTime } |
Select-Object -First 1
if ($newer) { $needPlat = $true }
}
if (-not $needGw -and (Test-Path $gw)) {
$exeTime = (Get-Item $gw).LastWriteTime
$newer = Get-ChildItem -Path (Join-Path $Root "gateway") -Recurse -Include *.go -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt $exeTime } |
Select-Object -First 1
if ($newer) { $needGw = $true }
}
if ($needPlat) {
Write-Host " building platform.exe ..." -ForegroundColor DarkGray
Push-Location (Join-Path $Root "platform")
go build -o platform.exe .
Pop-Location
}
if ($needGw) {
Write-Host " building gateway.exe ..." -ForegroundColor DarkGray
Push-Location (Join-Path $Root "gateway")
go build -o gateway.exe .
Pop-Location
}
Write-Host "==> Ensure web frontend (vite /#/preview)" -ForegroundColor DarkGray
& (Join-Path $Root "scripts\Ensure-Web.ps1")
# $? 判断 PS 脚本成败;勿单靠 $LASTEXITCODE会残留 docker/compose 等 native 失败码)
if (-not $?) { throw "Ensure-Web failed" }
$pyCmd = Get-Command python -ErrorAction SilentlyContinue
if (-not $pyCmd) { $pyCmd = Get-Command py -ErrorAction SilentlyContinue }
if (-not $pyCmd) { throw "python not found" }
$py = $pyCmd.Source
$npmCmd = Get-Command npm.cmd -ErrorAction SilentlyContinue
if (-not $npmCmd) { $npmCmd = Get-Command npm -ErrorAction SilentlyContinue }
if (-not $npmCmd) { throw "npm not found" }
# AI 截图直达本机 Vite
$env:WEB_BASE = "http://127.0.0.1:5173"
$env:PLATFORM_BASE = "http://127.0.0.1:8888"
$env:GATEWAY_BASE = "http://127.0.0.1:8180"
$env:GENERATE_LOG_DIR = Join-Path $Root ".runtime\logs\generate"
$env:FIDELITY_SHOT_DIR = Join-Path $Root ".runtime\fidelity_shots"
Start-Svc "platform" (Join-Path $Root "platform") $plat @("-f", "etc/platform.yaml")
Start-Sleep -Seconds 2
Start-Svc "ai-service" (Join-Path $Root "ai-service") $py @("-m", "uvicorn", "app:app", "--host", "127.0.0.1", "--port", "8001", "--reload")
Start-Sleep -Seconds 1
Start-Svc "gateway" (Join-Path $Root "gateway") $gw @("-f", "etc/gateway.yaml")
Start-Sleep -Seconds 1
Start-Svc "web" (Join-Path $Root "web") $npmCmd.Source @("run", "dev", "--", "--host", "127.0.0.1", "--port", "5173", "--strictPort")
Write-Host "==> Waiting for services..."
$deadline = (Get-Date).AddSeconds(45)
do {
$webOk = $false
$gwOk = $false
try {
$r = Invoke-WebRequest -Uri "http://127.0.0.1:5173/" -UseBasicParsing -TimeoutSec 2
if ($r.StatusCode -lt 500) { $webOk = $true }
} catch {}
try {
$r = Invoke-WebRequest -Uri "http://127.0.0.1:8180/gateway/health" -UseBasicParsing -TimeoutSec 2
if ($r.StatusCode -lt 500) { $gwOk = $true }
} catch {}
if ($webOk -and $gwOk) { break }
Start-Sleep -Seconds 1
} while ((Get-Date) -lt $deadline)
if ($webOk -and $gwOk) {
Write-Host "OK Web http://127.0.0.1:5173 Gateway http://127.0.0.1:8180 account demo/demo123" -ForegroundColor Green
Write-Host " logs $LogDir" -ForegroundColor DarkGray
} else {
Write-Host "WARN some services not ready yet. Check logs:" -ForegroundColor Yellow
Write-Host " $LogDir"
Write-Host " web=$webOk gateway=$gwOk"
Write-Host " (debug with visible windows: .\start-host.ps1 -ShowConsole)"
}
# 后台看门狗ai-service / gateway 挂掉自动拉起
$watchPidFile = Join-Path $LogDir "watchdog.pid"
if (Test-Path $watchPidFile) {
$oldWatch = Get-Content $watchPidFile -ErrorAction SilentlyContinue
if ($oldWatch) { Stop-Process -Id ([int]$oldWatch) -Force -ErrorAction SilentlyContinue }
}
$watchScript = Join-Path $Root "watch-services.ps1"
$watchOut = Join-Path $LogDir "watchdog.out.log"
$watchErr = Join-Path $LogDir "watchdog.err.log"
Set-Content -LiteralPath $watchOut -Value "" -Encoding UTF8
Set-Content -LiteralPath $watchErr -Value "" -Encoding UTF8
$wp = Start-Process -FilePath "powershell.exe" `
-ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $watchScript) `
-WorkingDirectory $Root `
-WindowStyle Hidden `
-RedirectStandardOutput $watchOut `
-RedirectStandardError $watchErr `
-PassThru
if ($wp) {
Set-Content -LiteralPath $watchPidFile -Value $wp.Id -Encoding ASCII
Write-Host " started watchdog (pid=$($wp.Id)) - auto-restarts ai-service if down" -ForegroundColor DarkGray
}
if (-not $NoBrowser) { Start-Process "http://127.0.0.1:5173" }