feat(dev): 本地开发工具集 v0.5.6-dev-tooling

包含本地 dev 链路完整跑通的工具集(不进生产):

backend:
- dev_auth.py: /api/dev/login Mock 企微 OAuth(/dev/* 路由)
- messages.py: dev 模式短路企微推送,避免 invalid corpid 噪音
- main.py: dev 模式启动时建 5 条 demo conversation,让前端有数据可测

frontend:
- PortalSelect.vue: dev 模式 enterRole 跳完整 URL(5173/5174/5175 端口),生产仍走相对路径

infrastructure:
- docker-compose.dev.yml: dev compose(包含 backend/postgres/redis)

scripts(Windows PowerShell):
- dev-frontend-install.ps1: 一次性装 4 个前端依赖
- dev-frontend-start.ps1: 后台起 4 个前端 dev server
- dev-check-schema-drift.ps1: 对比 SQLAlchemy 模型 vs Postgres schema,漂移 exit 1

docs:
- CURRENT-FOCUS.md: 项目状态看板(每次 session 维护)
This commit is contained in:
Simon
2026-06-16 19:24:02 +08:00
parent cec5607c45
commit eee2bcc071
9 changed files with 963 additions and 22 deletions
+161
View File
@@ -0,0 +1,161 @@
# =============================================================================
# 企微IT智能服务台 — dev 模式 schema 漂移检测 (PowerShell 版)
# =============================================================================
# 作用:对比 SQLAlchemy 模型 vs 实际 DB 字段,防止"模型加了字段忘写 migration"
# 历史踩坑:
# - 010: agents.otp_secret (修了一次)
# - 011: conversations.impact_scope/is_blocking/emotion_state (又踩一次)
# 用法:.\scripts\dev-check-schema-drift.ps1
# 前置:dev 后端跑着(docker compose -f docker-compose.dev.yml up)
# 实现:
# 1. 在 backend 容器里跑 Python 一行反射模型 metadata
# 2. 在 db 容器里跑 psql 拿 information_schema
# 3. PowerShell 做差集对比输出
# =============================================================================
$ErrorActionPreference = 'Stop'
$ProjectRoot = $PSScriptRoot | Split-Path -Parent
Set-Location $ProjectRoot
function Write-Step($msg) { Write-Host "`n$msg" -ForegroundColor Cyan }
function Write-OK($msg) { Write-Host "$msg" -ForegroundColor Green }
function Write-Warn($msg) { Write-Host "$msg" -ForegroundColor Yellow }
function Write-Err($msg) { Write-Host "$msg" -ForegroundColor Red }
# --------------------------------------------------------------------------
# 拿 backend 容器 ID
# --------------------------------------------------------------------------
$BkId = docker compose --env-file .env.dev -f docker-compose.dev.yml ps -q backend
$DbId = docker compose --env-file .env.dev -f docker-compose.dev.yml ps -q postgres
if (-not $BkId -or -not $DbId) {
Write-Err "dev 后端或 db 容器没跑。先跑 dev-start.ps1"
exit 1
}
Write-Step "═══ Schema 漂移检测 ═══"
# --------------------------------------------------------------------------
# 1) 拿模型字段(在 backend 容器里跑 Python)
# --------------------------------------------------------------------------
$ModelJson = docker exec $BkId python -c @'
import json
from app.database import Base
import app.models # 触发模型注册到 Base.metadata
out = {}
for tname, tbl in Base.metadata.tables.items():
out[tname] = sorted([c.name for c in tbl.columns])
print(json.dumps(out, ensure_ascii=False))
'@ 2>&1
# docker exec 输出会带 status warning,过滤掉
$ModelJson = ($ModelJson | Where-Object { $_ -notmatch '^\s*$' -and $_ -notmatch 'WARN|INFO' } | Select-Object -Last 1).Trim()
if (-not $ModelJson) {
Write-Err "拿不到模型字段。backend 容器里 Python 跑挂了"
docker logs $BkId --tail 10
exit 1
}
try {
$ModelTables = $ModelJson | ConvertFrom-Json
} catch {
Write-Err "模型 JSON 解析失败: $ModelJson"
exit 1
}
Write-OK "模型字段: $($ModelTables.PSObject.Properties.Name.Count) 张表"
# --------------------------------------------------------------------------
# 2) 拿 DB 字段(在 db 容器里跑 psql)
# --------------------------------------------------------------------------
$DbSql = "SELECT table_name, column_name FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position;"
$DbRaw = @(docker exec $DbId psql -U wecom -d wecom_it_desk_dev -A -t -F "`t" -c $DbSql 2>&1 | Where-Object { $_ -is [string] })
$DbColumns = @{}
foreach ($line in $DbRaw) {
if ($line -isnot [string]) { continue }
$line = $line.Trim()
if (-not $line) { continue }
$parts = $line -split "`t"
if ($parts.Count -lt 2) { continue }
$tname = $parts[0]
$cname = $parts[1]
if (-not $DbColumns.ContainsKey($tname)) {
$DbColumns[$tname] = New-Object System.Collections.Generic.List[string]
}
$DbColumns[$tname].Add($cname)
}
# 拿 alembic version
$VersionRaw = @(docker exec $DbId psql -U wecom -d wecom_it_desk_dev -A -t -c "SELECT version_num FROM alembic_version;" 2>&1 | Where-Object { $_ -is [string] })
$Version = ($VersionRaw | Where-Object { $_ -match '^\d|_' } | Select-Object -First 1)
if ($Version) { $Version = $Version.Trim() }
Write-OK "DB 字段: $($DbColumns.Keys.Count) 张表 | alembic = $Version"
# --------------------------------------------------------------------------
# 3) 差集对比
# --------------------------------------------------------------------------
$Drift = $false
$OnlyInModel = @()
$OnlyInDb = @()
foreach ($tname in $ModelTables.PSObject.Properties.Name) {
$mcols = $ModelTables.$tname
$dcols = if ($DbColumns.ContainsKey($tname)) { $DbColumns[$tname] } else { @() }
foreach ($c in $mcols) {
if ($null -eq $dcols -or -not $dcols.Contains($c)) {
$OnlyInModel += [PSCustomObject]@{ Table = $tname; Column = $c }
}
}
foreach ($c in $dcols) {
if ($null -ne $c -and -not $mcols.Contains($c) -and $tname -ne 'alembic_version') {
$OnlyInDb += [PSCustomObject]@{ Table = $tname; Column = $c }
}
}
}
# --------------------------------------------------------------------------
# 4) 输出报告
# --------------------------------------------------------------------------
Write-Host ""
Write-Host "════════════════════════════════════════════════════════════════════" -ForegroundColor White
Write-Host " alembic current = $Version" -ForegroundColor Gray
Write-Host " 模型表数 = $($ModelTables.PSObject.Properties.Name.Count) DB 表数 = $($DbColumns.Keys.Count)" -ForegroundColor Gray
Write-Host "════════════════════════════════════════════════════════════════════" -ForegroundColor White
if ($OnlyInModel.Count -gt 0) {
$Drift = $true
Write-Host ""
Write-Host " ❌ 模型有,DB 缺($($OnlyInModel.Count) 个):" -ForegroundColor Red
foreach ($x in $OnlyInModel) {
Write-Host " - $($x.Table).$($x.Column)" -ForegroundColor Red
}
}
if ($OnlyInDb.Count -gt 0) {
$Drift = $true
Write-Host ""
Write-Host " ⚠️ DB 有,模型没($($OnlyInDb.Count) 个,可能遗留):" -ForegroundColor Yellow
foreach ($x in $OnlyInDb) {
Write-Host " - $($x.Table).$($x.Column)" -ForegroundColor Yellow
}
}
if ($Drift) {
Write-Host ""
Write-Host " 💡 修法:" -ForegroundColor Cyan
Write-Host " 1. cd backend" -ForegroundColor Gray
Write-Host " 2. alembic revision --autogenerate -m 'sync xxx fields'" -ForegroundColor Gray
Write-Host " 3. 检查生成的 migration 文件(review 一下,不要直接用)" -ForegroundColor Gray
Write-Host " 4. docker exec $BkId alembic upgrade head" -ForegroundColor Gray
Write-Host ""
exit 1
}
Write-Host ""
Write-Host " ✅ schema 一致,无漂移" -ForegroundColor Green
Write-Host " $($ModelTables.PSObject.Properties.Name.Count) 张表全部对齐" -ForegroundColor Green
Write-Host ""
Write-Host " 💡 建议:加到 Git pre-commit / dev 启动检查里" -ForegroundColor Gray
Write-Host ""
exit 0
+106
View File
@@ -0,0 +1,106 @@
# =============================================================================
# 企微IT智能服务台 — 前端 dev 环境一键安装脚本
# =============================================================================
# 作用:一次性给 4 个前端项目装 pnpm 依赖
# 用法:.\scripts\dev-frontend-install.ps1
# 前置:已装 pnpm (npm install -g pnpm)
# 输出:每个前端在它自己的目录下创建 node_modules
# 注意:首次安装 3-8 分钟,后续 pnpm install 会很快(增量)
# =============================================================================
$ErrorActionPreference = 'Stop'
$ProjectRoot = $PSScriptRoot | Split-Path -Parent
Set-Location $ProjectRoot
# --------------------------------------------------------------------------
# 自动把 npm 全局 bin 加进 PATH(只对本脚本生效,不污染系统)
# 原因:某些用户(如 NVM 用户)系统 PATH 缺 npm global bin,pnpm 找不到
# --------------------------------------------------------------------------
$NpmGlobalBin = npm config get prefix 2>$null
if ($NpmGlobalBin -and (Test-Path $NpmGlobalBin)) {
if ($env:PATH -notlike "*$NpmGlobalBin*") {
$env:PATH = "$env:PATH;$NpmGlobalBin"
Write-Host " 已临时把 $NpmGlobalBin 加进 PATH(只对本次脚本生效)" -ForegroundColor DarkGray
}
}
$Frontends = @('frontend-h5', 'frontend-agent', 'frontend-admin', 'frontend-portal')
# --------------------------------------------------------------------------
# 颜色函数
# --------------------------------------------------------------------------
function Write-Step($msg) { Write-Host "`n$msg" -ForegroundColor Cyan }
function Write-OK($msg) { Write-Host "$msg" -ForegroundColor Green }
function Write-Warn($msg) { Write-Host "$msg" -ForegroundColor Yellow }
function Write-Err($msg) { Write-Host "$msg" -ForegroundColor Red }
# --------------------------------------------------------------------------
# 前置检查
# --------------------------------------------------------------------------
Write-Step "═══ 企微IT智能服务台 — 前端依赖安装 ═══"
# 检查 pnpm(包含 npm global bin 路径)
$pnpmCmd = Get-Command pnpm -ErrorAction SilentlyContinue
if (-not $pnpmCmd) {
Write-Err "pnpm 没装,请先跑: npm install -g pnpm"
exit 1
}
$pnpmVer = & $pnpmCmd.Source --version 2>&1
Write-OK "pnpm 已装: $pnpmVer"
# 检查 Node 版本(警告,不阻塞)
$nodeVer = node --version
Write-Host " Node 版本: $nodeVer"
if ($nodeVer -notmatch '^v(20\.|21\.|22\.)') {
Write-Warn "前端要求 Node 20.x,你是 $nodeVer。多数情况能跑,出问题再装 Node 20。"
}
# 检查后端 dev 是否在跑(可选)
try {
$null = Invoke-WebRequest -Uri 'http://localhost:8000/health' -UseBasicParsing -TimeoutSec 3
Write-OK "后端 dev 在跑 (http://localhost:8000)"
} catch {
Write-Warn "后端 dev 没跑,Vite 反代会连不上后端。先跑 dev-start.ps1 起后端。"
}
# --------------------------------------------------------------------------
# 顺序装 4 个前端
# --------------------------------------------------------------------------
foreach ($name in $Frontends) {
Write-Step "─── $name ───"
$dir = Join-Path $ProjectRoot $name
if (-not (Test-Path "$dir/package.json")) {
Write-Err "找不到 $dir/package.json,跳过"
continue
}
Set-Location $dir
$sw = [System.Diagnostics.Stopwatch]::StartNew()
try {
pnpm install 2>&1 | Tee-Object -FilePath "$ProjectRoot\.pnpm-install-$name.log" | Out-Host
$sw.Stop()
Write-OK "$name 装完 ($([math]::Round($sw.Elapsed.TotalSeconds))s)"
} catch {
$sw.Stop()
Write-Err "$name 装失败 ($([math]::Round($sw.Elapsed.TotalSeconds))s)"
Write-Host " 详细日志: $ProjectRoot\.pnpm-install-$name.log" -ForegroundColor Yellow
Write-Host " 常见原因:网络抖动 / 镜像源不通 / Node 版本太新" -ForegroundColor Yellow
}
}
# --------------------------------------------------------------------------
# 汇总
# --------------------------------------------------------------------------
Write-Step "═══ 装完 ═══"
foreach ($name in $Frontends) {
$log = "$ProjectRoot\.pnpm-install-$name.log"
if ((Test-Path "$ProjectRoot/$name/node_modules") -and (Test-Path "$ProjectRoot/$name/package.json")) {
Write-OK "$name 已就绪 (node_modules 存在)"
} else {
Write-Err "$name 没装好,日志: $log"
}
}
Write-Host "`n下一步:启动前端 dev server" -ForegroundColor Cyan
Write-Host " 单独启动: cd frontend-h5 ; pnpm dev" -ForegroundColor Gray
Write-Host " 一起启动: d:/资料/03-项目开发/wecom_it_smart_desk-claude/scripts/dev-frontend-start.ps1" -ForegroundColor Gray
Write-Host " 全部停止: ...\scripts\dev-frontend-start.ps1 -Stop" -ForegroundColor Gray
+302
View File
@@ -0,0 +1,302 @@
# =============================================================================
# 企微IT智能服务台 — 前端 dev server 一键启动脚本
# =============================================================================
# 作用:一次性后台启动 4 个前端 dev server(每个独立窗口 + 独立日志)
# 用法:.\scripts\dev-frontend-start.ps1
# .\scripts\dev-frontend-start.ps1 -Frontend h5 # 只起一个
# .\scripts\dev-frontend-start.ps1 -Frontend h5,admin # 起多个
# .\scripts\dev-frontend-start.ps1 -Stop # 停掉所有
# 前置:已跑过 dev-frontend-install.ps1(每个前端有 node_modules)
# 输出:日志写到 .pnpm-dev-<name>.log;进程名 pnpm.exe(用 Get-Process pnpm 查)
# 注意:启动顺序 h5 → agent → admin → portal,每个隔 4 秒
# =============================================================================
[CmdletBinding()]
param(
[string]$Frontend = '', # 空 = 全部 4 个;逗号分隔可多选: h5,agent,admin,portal
[switch]$Stop # 切到停止模式
)
$ErrorActionPreference = 'Stop'
$ProjectRoot = $PSScriptRoot | Split-Path -Parent
Set-Location $ProjectRoot
# --------------------------------------------------------------------------
# 自动把 npm 全局 bin 加进 PATH(只对本脚本生效)
# 原因:某些用户(如 NVM 用户)系统 PATH 缺 npm global bin,pnpm 找不到
# --------------------------------------------------------------------------
$NpmGlobalBin = npm config get prefix 2>$null
if ($NpmGlobalBin -and (Test-Path $NpmGlobalBin)) {
if ($env:PATH -notlike "*$NpmGlobalBin*") {
$env:PATH = "$env:PATH;$NpmGlobalBin"
Write-Host " 已临时把 $NpmGlobalBin 加进 PATH(只对本次脚本生效)" -ForegroundColor DarkGray
}
}
# --------------------------------------------------------------------------
# 前置检查 pnpm
# --------------------------------------------------------------------------
$pnpmCmd = Get-Command pnpm -ErrorAction SilentlyContinue
if (-not $pnpmCmd) {
Write-Host " ✗ pnpm 没装或不在 PATH,请先跑 scripts\dev-frontend-install.ps1" -ForegroundColor Red
exit 1
}
# --------------------------------------------------------------------------
# 4 个前端清单(name -> port)
# 端口必须跟每个前端 vite.config.ts 的 server.port 一致
# --------------------------------------------------------------------------
$AllFrontends = @(
@{ Name = 'frontend-h5'; Port = 5174; Label = 'h5' },
@{ Name = 'frontend-agent'; Port = 5173; Label = 'agent' },
@{ Name = 'frontend-admin'; Port = 5175; Label = 'admin' },
@{ Name = 'frontend-portal'; Port = 5176; Label = 'portal' }
)
# --------------------------------------------------------------------------
# 颜色函数(跟 dev-frontend-install.ps1 一致)
# --------------------------------------------------------------------------
function Write-Step($msg) { Write-Host "`n$msg" -ForegroundColor Cyan }
function Write-OK($msg) { Write-Host "$msg" -ForegroundColor Green }
function Write-Warn($msg) { Write-Host "$msg" -ForegroundColor Yellow }
function Write-Err($msg) { Write-Host "$msg" -ForegroundColor Red }
# --------------------------------------------------------------------------
# 工具函数
# --------------------------------------------------------------------------
function Get-FrontendByLabel($label) {
foreach ($f in $AllFrontends) {
if ($f.Label -eq $label -or $f.Name -eq $label) { return $f }
}
return $null
}
function Test-PortListening($port) {
# 优先用 Get-NetTCPConnection(快);失败回退 Test-NetConnection
try {
$conn = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction Stop
return ($null -ne $conn)
} catch {
try {
$tnc = Test-NetConnection -ComputerName 'localhost' -Port $port -InformationLevel Quiet -WarningAction SilentlyContinue
return $tnc
} catch {
return $false
}
}
}
function Get-PnpmPids() {
# 拿到所有 pnpm 进程 PID(用作"停止所有")
$procs = Get-Process -Name 'pnpm' -ErrorAction SilentlyContinue
if ($null -eq $procs) { return @() }
return @($procs | Select-Object -ExpandProperty Id)
}
function Get-NodePidsByCwd($dir) {
# 拿到工作目录在 $dir 下的 node.exe 进程(Vite dev server)
$procs = Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" -ErrorAction SilentlyContinue
$pids = @()
foreach ($p in $procs) {
if ($p.CommandLine -and $p.CommandLine.Contains($dir)) {
$pids += $p.ProcessId
}
}
return $pids
}
# --------------------------------------------------------------------------
# 模式:停掉所有前端
# --------------------------------------------------------------------------
if ($Stop) {
Write-Step "═══ 停掉所有前端 dev server ═══"
# 先停 node.exe(它们是 Vite dev server 真正的工作进程)
$killed = 0
foreach ($f in $AllFrontends) {
$dir = Join-Path $ProjectRoot $f.Name
$pids = Get-NodePidsByCwd $dir
foreach ($pid in $pids) {
try {
Stop-Process -Id $pid -Force -ErrorAction Stop
$killed++
} catch {}
}
}
# 再停 pnpm 父进程(它会再起 node,所以得反过来先杀子再杀父?实际先父后子都行,这里杀剩的 pnpm)
$pnpmPids = Get-PnpmPids
foreach ($pid in $pnpmPids) {
try {
Stop-Process -Id $pid -Force -ErrorAction Stop
$killed++
} catch {}
}
Write-OK "已停掉 $killed 个进程"
# 兜底:再扫一遍端口确认空
Start-Sleep -Seconds 1
foreach ($f in $AllFrontends) {
if (Test-PortListening $f.Port) {
Write-Warn "$($f.Label) 端口 $($f.Port) 还占着,可能不是 pnpm 起的"
} else {
Write-OK "$($f.Label) 端口 $($f.Port) 已释放"
}
}
Write-Host "`n下一步: .\scripts\dev-frontend-start.ps1 重启" -ForegroundColor Gray
exit 0
}
# --------------------------------------------------------------------------
# 解析 -Frontend 参数
# --------------------------------------------------------------------------
if ([string]::IsNullOrWhiteSpace($Frontend)) {
$Selected = $AllFrontends
} else {
$Selected = @()
foreach ($label in ($Frontend -split ',')) {
$label = $label.Trim()
$f = Get-FrontendByLabel $label
if ($null -eq $f) {
Write-Err "未知前端: $label(可选: h5, agent, admin, portal)"
exit 1
}
$Selected += $f
}
}
# --------------------------------------------------------------------------
# 前置检查
# --------------------------------------------------------------------------
Write-Step "═══ 企微IT智能服务台 — 前端 dev server 启动 ═══"
# 检查 pnpm
try {
$pnpmVer = pnpm --version
Write-OK "pnpm 已装: $pnpmVer"
} catch {
Write-Err "pnpm 没装,请先跑: npm install -g pnpm"
exit 1
}
# 检查 Node 版本(警告,不阻塞)
$nodeVer = node --version
Write-Host " Node 版本: $nodeVer"
if ($nodeVer -notmatch '^v(20\.|21\.|22\.)') {
Write-Warn "前端要求 Node 20.x,你是 $nodeVer。多数情况能跑,出问题再装 Node 20。"
}
# 检查每个选中的前端是否有 node_modules
foreach ($f in $Selected) {
$dir = Join-Path $ProjectRoot $f.Name
if (-not (Test-Path "$dir/package.json")) {
Write-Err "找不到 $dir/package.json,跳过 $($f.Label)"
# 用占位标记一下,在后面的循环里跳过
$f | Add-Member -NotePropertyName 'Skip' -NotePropertyValue $true -Force
continue
}
if (-not (Test-Path "$dir/node_modules")) {
Write-Warn "$($f.Label) 没有 node_modules,先跑 dev-frontend-install.ps1"
$f | Add-Member -NotePropertyName 'Skip' -NotePropertyValue $true -Force
}
}
# --------------------------------------------------------------------------
# 逐个后台启动
# --------------------------------------------------------------------------
$Started = @()
foreach ($f in $Selected) {
if ($f.Skip) { continue }
$dir = Join-Path $ProjectRoot $f.Name
$log = Join-Path $ProjectRoot ".pnpm-dev-$($f.Label).log"
$port = $f.Port
$label = $f.Label
Write-Step "─── $label (port $port) ───"
# 检查端口是否已被占(被占就跳过,不报错)
if (Test-PortListening $port) {
Write-Warn "端口 $port 已被占,$label 跳过(可能别人/上次没退干净在跑)"
Write-Host " 看进程: Get-NetTCPConnection -LocalPort $port -State Listen" -ForegroundColor Gray
Write-Host " 强制停: .\scripts\dev-frontend-start.ps1 -Stop" -ForegroundColor Gray
continue
}
# 清空旧日志(每次启动都是干净的一页)
if (Test-Path $log) { Remove-Item $log -Force }
# 后台启动 Start-Process,WindowStyle Hidden(不弹黑窗)
# 关键:必须用 cmd.exe /c 调 pnpm,因为 pnpm 是 .cmd 不是 .exe
# 直接 Start-Process 'pnpm' 会报 "%1 不是有效的 Win32 应用程序"
try {
$proc = Start-Process -FilePath 'cmd.exe' `
-ArgumentList '/c', 'pnpm', 'dev' `
-WorkingDirectory $dir `
-WindowStyle Hidden `
-RedirectStandardOutput $log `
-RedirectStandardError "$log.err" `
-PassThru `
-ErrorAction Stop
Write-OK "已起进程 PID=$($proc.Id),日志: $log"
} catch {
Write-Err "启动失败: $_"
continue
}
# 等 3-5 秒,看端口起来没
$up = $false
for ($i = 1; $i -le 8; $i++) {
Start-Sleep -Seconds 1
if (Test-PortListening $port) {
$up = $true
break
}
}
if ($up) {
Write-OK "$label 端口 $port 已监听"
$Started += $f
} else {
Write-Warn "$label 8 秒内未监听,看日志: $log"
Write-Host " 常见原因: 依赖缺/Node 版本/端口冲突/语法错" -ForegroundColor Yellow
}
}
# --------------------------------------------------------------------------
# 汇总表格
# --------------------------------------------------------------------------
Write-Step "═══ 启动汇总 ═══"
if ($Started.Count -eq 0) {
Write-Warn "没前端成功起来"
} else {
# 算每行最大宽度
$maxLabel = ($Started | ForEach-Object { $_.Label.Length } | Measure-Object -Maximum).Maximum
$maxLabel = [Math]::Max($maxLabel, 5)
Write-Host (" {0,-$maxLabel} {1,-8} {2}" -f 'NAME', 'PORT', 'URL') -ForegroundColor White
Write-Host (" {0,-$maxLabel} {1,-8} {2}" -f ($maxLabel | ForEach-Object { '-' * $_ }), '----', '---') -ForegroundColor DarkGray
foreach ($f in $Started) {
$url = "http://localhost:$($f.Port)"
Write-Host (" {0,-$maxLabel} {1,-8} {2}" -f $f.Label, $f.Port, $url) -ForegroundColor Green
}
Write-Host "`n 日志位置:" -ForegroundColor Cyan
foreach ($f in $Started) {
$log = Join-Path $ProjectRoot ".pnpm-dev-$($f.Label).log"
Write-Host " $log" -ForegroundColor Gray
}
Write-Host "`n 实时跟日志(另开窗口跑):" -ForegroundColor Cyan
Write-Host " Get-Content .pnpm-dev-h5.log -Wait" -ForegroundColor Gray
Write-Host " Get-Content .pnpm-dev-agent.log -Wait" -ForegroundColor Gray
Write-Host "`n 停掉所有前端:" -ForegroundColor Cyan
Write-Host " .\scripts\dev-frontend-start.ps1 -Stop" -ForegroundColor Gray
}
Write-Host "`n下一步:浏览器开上面的 URL 就能用了" -ForegroundColor Cyan