Five components that ship as one product:
- behavision/ the recognition engine. RTSP ingest, YuNet detection, IoU
tracking, ArcFace embeddings, a FAISS/SQLite gallery, and a
FastAPI dashboard. Identity is decided once per TRACK from an
average of at least three embeddings, never per frame.
- agent/ the Go edge agent: supervises the engine, holds a durable
spool, and drains it to MQTT. Nothing is acked before the
broker confirms.
- desktop/ the shop PC application (Wails + React + tray).
- server/ the cloud API, MQTT consumer, reports and assistant.
- web/ platform.loyaly.ai, the head-office app, embedded in the
server binary.
The gallery stores 512-float embeddings and timestamps - no images unless
`app.store_faces` is switched on. Those embeddings are biometric personal
data under GDPR and India's DPDP: template inversion reconstructs a
recognisable face from an ArcFace vector, so data/behavision.db is treated
as a biometric database and DELETE /api/visitors/{id} is a real erasure.
CLAUDE.md carries the reasoning behind every non-obvious decision here,
including the ones that were measured and the ones that were wrong first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HViLj9gYNRtSr7YVZmW5sn
135 lines
5.2 KiB
PowerShell
135 lines
5.2 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Builds the Behavision Windows package: engine, app, agent, installer.
|
|
|
|
.DESCRIPTION
|
|
This script must run ON WINDOWS. Everything else in this repository
|
|
cross-compiles from a Mac - the Go binaries with GOOS=windows, the web bundle
|
|
with npm - but the ENGINE cannot. PyInstaller freezes the interpreter and the
|
|
native wheels (onnxruntime, OpenCV) of the machine it runs on; there is no
|
|
cross-target flag, and there never has been. So the engine .exe is built here
|
|
or it is not built at all.
|
|
|
|
Output: dist\Behavision-Setup-<version>.exe, plus dist\Behavision\ which is
|
|
the unpacked tree the installer copies (useful for testing without
|
|
installing).
|
|
|
|
.PARAMETER Version
|
|
Stamped into the installer and shown in Add/Remove Programs.
|
|
|
|
.PARAMETER SkipInstaller
|
|
Build the payload but not the setup .exe. Use when Inno Setup is absent.
|
|
#>
|
|
param(
|
|
[string]$Version = "0.1.0",
|
|
[switch]$SkipInstaller
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$dist = Join-Path $root "dist"
|
|
$stage = Join-Path $dist "Behavision"
|
|
|
|
function Step($msg) { Write-Host "`n=== $msg ===" -ForegroundColor Cyan }
|
|
function Need($exe, $hint) {
|
|
if (-not (Get-Command $exe -ErrorAction SilentlyContinue)) {
|
|
throw "$exe not found on PATH. $hint"
|
|
}
|
|
}
|
|
|
|
Need python "Install Python 3.11+ and tick 'Add to PATH'."
|
|
Need go "Install Go 1.21+ from https://go.dev/dl/."
|
|
Need npm "Install Node.js LTS from https://nodejs.org/."
|
|
|
|
Step "Python environment"
|
|
Push-Location $root
|
|
if (-not (Test-Path ".venv")) { python -m venv .venv }
|
|
& .\.venv\Scripts\python -m pip install --upgrade pip | Out-Null
|
|
& .\.venv\Scripts\python -m pip install -r requirements.txt pyinstaller | Out-Null
|
|
|
|
Step "Engine tests"
|
|
# The package is not worth building if the engine is broken, and finding that
|
|
# out after the installer is signed is the expensive order to do it in.
|
|
& .\.venv\Scripts\python -m pytest tests -q
|
|
if ($LASTEXITCODE -ne 0) { throw "engine tests failed" }
|
|
|
|
Step "Engine (PyInstaller, one-folder)"
|
|
if (Test-Path (Join-Path $root "build")) { Remove-Item -Recurse -Force (Join-Path $root "build") }
|
|
& .\.venv\Scripts\pyinstaller behavision.spec --noconfirm --distpath (Join-Path $dist "engine-build")
|
|
if ($LASTEXITCODE -ne 0) { throw "pyinstaller failed" }
|
|
|
|
Step "Desktop app (Wails)"
|
|
Push-Location (Join-Path $root "desktop\frontend")
|
|
npm ci
|
|
npm run build
|
|
Pop-Location
|
|
Push-Location (Join-Path $root "desktop")
|
|
# Wails v2 talks to WebView2 through pure-Go bindings, so no cgo and no
|
|
# toolchain beyond Go itself. Verified by cross-compiling the same package from
|
|
# a Mac with CGO_ENABLED=0.
|
|
$env:CGO_ENABLED = "0"
|
|
if (Get-Command wails -ErrorAction SilentlyContinue) {
|
|
wails build -platform windows/amd64 -clean -ldflags "-X main.version=$Version"
|
|
} else {
|
|
Write-Warning "wails CLI not found - falling back to a plain go build (no icon, no manifest)."
|
|
go build -ldflags "-H windowsgui -X main.version=$Version" -o (Join-Path $root "desktop\build\bin\Behavision.exe") .
|
|
}
|
|
Pop-Location
|
|
|
|
Step "Headless agent"
|
|
Push-Location (Join-Path $root "agent")
|
|
$env:CGO_ENABLED = "0" # the cgo resolver forces external linking
|
|
go build -o (Join-Path $root "dist\behavision-agent.exe") .
|
|
Pop-Location
|
|
|
|
Step "WebView2 bootstrapper"
|
|
# Bundled rather than downloaded at install time: a shop PC being set up often
|
|
# has no working internet yet, and the app is a blank white window without it.
|
|
$vendor = Join-Path $root "installer\vendor"
|
|
New-Item -ItemType Directory -Force -Path $vendor | Out-Null
|
|
$wv2 = Join-Path $vendor "MicrosoftEdgeWebview2Setup.exe"
|
|
if (-not (Test-Path $wv2)) {
|
|
Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -OutFile $wv2
|
|
}
|
|
|
|
Step "Staging"
|
|
if (Test-Path $stage) { Remove-Item -Recurse -Force $stage }
|
|
New-Item -ItemType Directory -Force -Path $stage | Out-Null
|
|
# The engine keeps its own folder: it is a one-folder PyInstaller build with
|
|
# ~150 native DLLs beside it, and `behavision.exe` would otherwise collide with
|
|
# `Behavision.exe` on a case-insensitive filesystem.
|
|
Copy-Item -Recurse (Join-Path $dist "engine-build\behavision") (Join-Path $stage "engine")
|
|
Copy-Item (Join-Path $root "desktop\build\bin\Behavision.exe") $stage
|
|
Copy-Item (Join-Path $dist "behavision-agent.exe") $stage
|
|
Copy-Item (Join-Path $root "LICENSE") $stage -ErrorAction SilentlyContinue
|
|
|
|
$engineExe = Join-Path $stage "engine\behavision.exe"
|
|
if (-not (Test-Path $engineExe)) { throw "engine exe missing at $engineExe" }
|
|
& $engineExe paths
|
|
if ($LASTEXITCODE -ne 0) { throw "the frozen engine cannot start - `paths` failed" }
|
|
|
|
Pop-Location
|
|
|
|
if ($SkipInstaller) {
|
|
Step "Done (payload only)"
|
|
Write-Host "Unpacked tree: $stage"
|
|
exit 0
|
|
}
|
|
|
|
Step "Installer (Inno Setup)"
|
|
$iscc = @(
|
|
"$env:ProgramFiles\Inno Setup 6\ISCC.exe",
|
|
"${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
|
|
) | Where-Object { Test-Path $_ } | Select-Object -First 1
|
|
if (-not $iscc) {
|
|
throw "Inno Setup 6 not found. Install it from https://jrsoftware.org/isdl.php, or re-run with -SkipInstaller."
|
|
}
|
|
& $iscc "/DMyAppVersion=$Version" (Join-Path $root "installer\behavision.iss")
|
|
if ($LASTEXITCODE -ne 0) { throw "ISCC failed" }
|
|
|
|
Step "Done"
|
|
Get-ChildItem (Join-Path $dist "Behavision-Setup-*.exe") | ForEach-Object {
|
|
Write-Host ("{0} ({1:N1} MB)" -f $_.FullName, ($_.Length / 1MB))
|
|
}
|