Initial qiwei secondary development handoff

This commit is contained in:
2026-06-23 21:11:20 +08:00
commit 858cb68f4f
207 changed files with 52782 additions and 0 deletions

249
scripts/package-windows.ps1 Normal file
View File

@@ -0,0 +1,249 @@
param(
[switch]$SkipTests,
[switch]$SkipFrontendBuild,
[string]$WailsPath,
[string]$MakensisPath
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path
$binDir = Join-Path $repoRoot "build\bin"
$installerDir = Join-Path $repoRoot "build\windows\installer"
$runtimeDir = Join-Path $installerDir "runtime"
function Resolve-RequiredTool {
param(
[Parameter(Mandatory = $true)][string]$Name,
[string]$FallbackPath,
[string]$InstallHint
)
$command = Get-Command $Name -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
if ($FallbackPath -and (Test-Path -LiteralPath $FallbackPath)) {
return (Resolve-Path -LiteralPath $FallbackPath).Path
}
throw "Required tool not found: $Name. $InstallHint"
}
function Assert-UnderDirectory {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Parent
)
$parentPath = (Resolve-Path -LiteralPath $Parent).Path.TrimEnd('\')
if (Test-Path -LiteralPath $Path) {
$targetPath = (Resolve-Path -LiteralPath $Path).Path
} else {
$targetPath = [System.IO.Path]::GetFullPath($Path)
}
if (-not $targetPath.StartsWith($parentPath, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to operate on $targetPath because it is not under $parentPath"
}
}
function Reset-Directory {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Parent
)
Assert-UnderDirectory -Path $Path -Parent $Parent
if (Test-Path -LiteralPath $Path) {
Remove-Item -LiteralPath $Path -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $Path | Out-Null
}
function Write-Utf8NoBom {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Content
)
$encoding = New-Object System.Text.UTF8Encoding($false)
[System.IO.File]::WriteAllText($Path, $Content, $encoding)
}
function Copy-RequiredFile {
param(
[Parameter(Mandatory = $true)][string]$Source,
[Parameter(Mandatory = $true)][string]$Destination
)
if (-not (Test-Path -LiteralPath $Source)) {
throw "Missing release resource: $Source"
}
$destinationDir = Split-Path -Parent $Destination
if ($destinationDir -and -not (Test-Path -LiteralPath $destinationDir)) {
New-Item -ItemType Directory -Force -Path $destinationDir | Out-Null
}
try {
Copy-Item -LiteralPath $Source -Destination $Destination -Force -ErrorAction Stop
} catch {
if (Test-Path -LiteralPath $Destination) {
$sourceInfo = Get-Item -LiteralPath $Source
$destinationInfo = Get-Item -LiteralPath $Destination
if ($sourceInfo.Length -eq $destinationInfo.Length) {
Write-Warning "Could not overwrite ${Destination}; existing file has the same size and will be kept. $($_.Exception.Message)"
return
}
}
throw
}
}
function Stop-RunningReleaseProcesses {
$processes = Get-Process -Name "qiweimanager", "helper", "helper_auto_reply" -ErrorAction SilentlyContinue
foreach ($process in $processes) {
Write-Host "==> Stopping running release process: $($process.ProcessName) ($($process.Id))"
Stop-Process -Id $process.Id -Force -ErrorAction Stop
}
}
Set-Location $repoRoot
New-Item -ItemType Directory -Force -Path (Join-Path $repoRoot ".gocache") | Out-Null
$env:GOCACHE = (Resolve-Path (Join-Path $repoRoot ".gocache")).Path
$wailsFallback = if ($WailsPath) { $WailsPath } else { Join-Path $env:USERPROFILE "go\bin\wails.exe" }
$wails = Resolve-RequiredTool -Name "wails.exe" -FallbackPath $wailsFallback -InstallHint "Install Wails CLI on the build machine first."
$makensis = Resolve-RequiredTool -Name "makensis.exe" -FallbackPath $MakensisPath -InstallHint "Install NSIS on the build machine first, e.g. run as Administrator: choco install nsis -y; or pass -MakensisPath."
$npm = Resolve-RequiredTool -Name "npm.cmd" -InstallHint "Install Node.js on the build machine first."
$go = Resolve-RequiredTool -Name "go.exe" -InstallHint "Install Go on the build machine first."
$pdftoppm = Get-Command "pdftoppm.exe" -ErrorAction SilentlyContinue
$env:PATH = "$(Split-Path -Parent $makensis);$env:PATH"
Write-Host "==> Release root: $repoRoot"
Write-Host "==> Wails: $wails"
Write-Host "==> NSIS: $makensis"
if (-not $SkipTests) {
Write-Host "==> Running Go tests"
& $go test ./...
}
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
Stop-RunningReleaseProcesses
Write-Host "==> Building 32-bit helper.exe"
$helperOut = Join-Path $binDir "helper.exe"
$oldGoArch = $env:GOARCH
try {
$env:GOARCH = "386"
Push-Location (Join-Path $repoRoot "helper")
& $go build -trimpath -ldflags "-H windowsgui -s -w" -o $helperOut .
} finally {
Pop-Location
if ($null -eq $oldGoArch) {
Remove-Item Env:GOARCH -ErrorAction SilentlyContinue
} else {
$env:GOARCH = $oldGoArch
}
}
Write-Host "==> Building bundled silk decoder"
$silkDecoderOut = Join-Path $binDir "tools\audio\silkdecode.exe"
New-Item -ItemType Directory -Force -Path (Split-Path $silkDecoderOut) | Out-Null
Push-Location (Join-Path $repoRoot "tools\audio\silkdecode")
try {
& $go build -trimpath -ldflags "-s -w" -o $silkDecoderOut .
} finally {
Pop-Location
}
if ($pdftoppm) {
Write-Host "==> Copying PDF renderer"
Copy-RequiredFile -Source $pdftoppm.Source -Destination (Join-Path $binDir "tools\pdf\pdftoppm.exe")
} else {
Write-Warning "pdftoppm.exe not found; scanned PDF OCR fallback will be unavailable in this build."
}
Write-Host "==> Copying DLLs to build\bin"
$helperDll = Join-Path $repoRoot "Helper_4.1.33.6009.dll"
$loaderDll = Join-Path $repoRoot "Loader_4.1.33.6009.dll"
Copy-RequiredFile -Source $helperDll -Destination (Join-Path $binDir "Helper_4.1.33.6009.dll")
Copy-RequiredFile -Source $loaderDll -Destination (Join-Path $binDir "Loader_4.1.33.6009.dll")
foreach ($staleFile in @("helper_auto_reply.exe", "qiweimanager.exe~")) {
$path = Join-Path $binDir $staleFile
if (Test-Path -LiteralPath $path) {
try {
Remove-Item -LiteralPath $path -Force -ErrorAction Stop
} catch {
Write-Warning "Could not remove stale file ${path}: $($_.Exception.Message)"
}
}
}
if (-not $SkipFrontendBuild) {
Write-Host "==> Building frontend"
Push-Location (Join-Path $repoRoot "frontend")
try {
& $npm run build
} finally {
Pop-Location
}
}
Write-Host "==> Staging NSIS runtime resources"
Reset-Directory -Path $runtimeDir -Parent $installerDir
New-Item -ItemType Directory -Force -Path (Join-Path $runtimeDir "config\knowledge") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $runtimeDir "config\materials") | Out-Null
Copy-RequiredFile -Source $helperOut -Destination (Join-Path $runtimeDir "helper.exe")
Copy-RequiredFile -Source $silkDecoderOut -Destination (Join-Path $runtimeDir "tools\audio\silkdecode.exe")
if (Test-Path -LiteralPath (Join-Path $binDir "tools\pdf\pdftoppm.exe")) {
Copy-RequiredFile -Source (Join-Path $binDir "tools\pdf\pdftoppm.exe") -Destination (Join-Path $runtimeDir "tools\pdf\pdftoppm.exe")
}
Copy-RequiredFile -Source (Join-Path $binDir "Helper_4.1.33.6009.dll") -Destination (Join-Path $runtimeDir "Helper_4.1.33.6009.dll")
Copy-RequiredFile -Source (Join-Path $binDir "Loader_4.1.33.6009.dll") -Destination (Join-Path $runtimeDir "Loader_4.1.33.6009.dll")
Copy-Item -LiteralPath (Join-Path $repoRoot "requestdata") -Destination (Join-Path $runtimeDir "requestdata") -Recurse -Force
Copy-Item -LiteralPath (Join-Path $repoRoot "eventdata") -Destination (Join-Path $runtimeDir "eventdata") -Recurse -Force
$defaultConfig = @'
{
"callbackConfig": {
"callbackUrl": "",
"callbackToken": "",
"httpPort": "10001",
"enableCallback": false,
"enableCloudAuth": false,
"fileUploadUrl": "",
"deviceCode": ""
},
"lastUpdated": 0
}
'@
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\config.json") -Content $defaultConfig
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\client_status.json") -Content "{}"
$knowledgeKeep = Join-Path $repoRoot "config\knowledge\.keep"
if (Test-Path -LiteralPath $knowledgeKeep) {
Copy-Item -LiteralPath $knowledgeKeep -Destination (Join-Path $runtimeDir "config\knowledge\.keep") -Force
} else {
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\knowledge\.keep") -Content "placeholder for installer-created knowledge directory"
}
$materialsIndex = Join-Path $repoRoot "config\materials\materials.json"
if (Test-Path -LiteralPath $materialsIndex) {
Copy-RequiredFile -Source $materialsIndex -Destination (Join-Path $runtimeDir "config\materials\materials.json")
} else {
Write-Utf8NoBom -Path (Join-Path $runtimeDir "config\materials\materials.json") -Content "{`"materials`":[]}"
}
Write-Host "==> Building Wails NSIS installer"
& $wails build --nsis -webview2 embed -trimpath
$installer = Join-Path $binDir "qiweimanager-amd64-installer.exe"
if (-not (Test-Path -LiteralPath $installer)) {
throw "Installer was not generated: $installer"
}
Write-Host "==> Release complete: $installer"