@trillboards/edge-sdk 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/deploy/docker/Dockerfile.cloud-sense +117 -0
- package/deploy/docker/Dockerfile.cpu +30 -21
- package/deploy/docker/Dockerfile.cuda +30 -21
- package/deploy/docker/Dockerfile.openvino +30 -20
- package/deploy/docker/README.md +9 -5
- package/deploy/docker/connector-entrypoint.sh +122 -0
- package/deploy/helm/README.md +1 -1
- package/deploy/k8s/daemonset.yaml +1 -1
- package/deploy/macos/README.md +161 -0
- package/deploy/macos/TrillboardsSetupApp.m +603 -0
- package/deploy/macos/build-macos-pkg.sh +588 -0
- package/deploy/macos/com.trillboards.edge.plist +62 -0
- package/deploy/macos/com.trillboards.litertlm.plist +45 -0
- package/deploy/macos/pair.command +519 -0
- package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
- package/deploy/macos/scripts/postinstall +153 -0
- package/deploy/macos/scripts/setup-litertlm.sh +272 -0
- package/deploy/macos/test-install-pair.sh +383 -0
- package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
- package/deploy/systemd/install.sh +25 -15
- package/deploy/systemd/trillboards-edge.service +4 -1
- package/deploy/windows/README.md +70 -0
- package/deploy/windows/build-windows.ps1 +225 -0
- package/deploy/windows/bundle-windows-installer.ps1 +153 -0
- package/deploy/windows/check-feeds.js +26 -0
- package/deploy/windows/e2e/fixtures/person.jpg +0 -0
- package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
- package/deploy/windows/install-service.ps1 +6 -1
- package/deploy/windows/install-update.ps1 +101 -0
- package/deploy/windows/installer.nsi +138 -0
- package/deploy/windows/register-litertlm-task.ps1 +42 -0
- package/deploy/windows/register-task.ps1 +44 -0
- package/deploy/windows/run-connector.ps1 +69 -0
- package/deploy/windows/setup-litertlm.ps1 +361 -0
- package/deploy/windows/stop-existing.ps1 +72 -0
- package/deploy/windows/trillboards-edge-task.vbs +25 -0
- package/deploy/windows/trillboards-edge.cmd +33 -0
- package/deploy/windows/trillboards-pair.cmd +276 -0
- package/deploy/windows/trillboards-setup.ps1 +456 -0
- package/deploy/windows/trillboards-setup.vbs +13 -0
- package/dist/AudienceAnalyzeClient.d.ts +136 -0
- package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
- package/dist/AudienceAnalyzeClient.js +246 -0
- package/dist/AudienceAnalyzeClient.js.map +1 -0
- package/dist/ChannelProber.d.ts +150 -0
- package/dist/ChannelProber.d.ts.map +1 -0
- package/dist/ChannelProber.js +887 -0
- package/dist/ChannelProber.js.map +1 -0
- package/dist/CloudEscalationPolicy.d.ts +143 -0
- package/dist/CloudEscalationPolicy.d.ts.map +1 -0
- package/dist/CloudEscalationPolicy.js +137 -0
- package/dist/CloudEscalationPolicy.js.map +1 -0
- package/dist/CloudSensingManager.d.ts +557 -0
- package/dist/CloudSensingManager.d.ts.map +1 -0
- package/dist/CloudSensingManager.js +1267 -0
- package/dist/CloudSensingManager.js.map +1 -0
- package/dist/DiscoveryReportClient.d.ts +66 -0
- package/dist/DiscoveryReportClient.d.ts.map +1 -0
- package/dist/DiscoveryReportClient.js +85 -0
- package/dist/DiscoveryReportClient.js.map +1 -0
- package/dist/EdgeAgent.d.ts +12 -2
- package/dist/EdgeAgent.d.ts.map +1 -1
- package/dist/EdgeAgent.js +55 -13
- package/dist/EdgeAgent.js.map +1 -1
- package/dist/LiteRtVlmProvider.d.ts +193 -0
- package/dist/LiteRtVlmProvider.d.ts.map +1 -0
- package/dist/LiteRtVlmProvider.js +436 -0
- package/dist/LiteRtVlmProvider.js.map +1 -0
- package/dist/MosaicSource.d.ts +108 -0
- package/dist/MosaicSource.d.ts.map +1 -0
- package/dist/MosaicSource.js +274 -0
- package/dist/MosaicSource.js.map +1 -0
- package/dist/OnvifClient.d.ts +83 -0
- package/dist/OnvifClient.d.ts.map +1 -0
- package/dist/OnvifClient.js +209 -0
- package/dist/OnvifClient.js.map +1 -0
- package/dist/PairClient.d.ts +106 -0
- package/dist/PairClient.d.ts.map +1 -0
- package/dist/PairClient.js +186 -0
- package/dist/PairClient.js.map +1 -0
- package/dist/RecorderAddressRecovery.d.ts +53 -0
- package/dist/RecorderAddressRecovery.d.ts.map +1 -0
- package/dist/RecorderAddressRecovery.js +359 -0
- package/dist/RecorderAddressRecovery.js.map +1 -0
- package/dist/SceneChangeDetector.d.ts +291 -0
- package/dist/SceneChangeDetector.d.ts.map +1 -0
- package/dist/SceneChangeDetector.js +682 -0
- package/dist/SceneChangeDetector.js.map +1 -0
- package/dist/SensingProfileClient.d.ts +59 -0
- package/dist/SensingProfileClient.d.ts.map +1 -0
- package/dist/SensingProfileClient.js +78 -0
- package/dist/SensingProfileClient.js.map +1 -0
- package/dist/SetupSessionClient.d.ts +114 -0
- package/dist/SetupSessionClient.d.ts.map +1 -0
- package/dist/SetupSessionClient.js +95 -0
- package/dist/SetupSessionClient.js.map +1 -0
- package/dist/VlmAdmissionGate.d.ts +126 -0
- package/dist/VlmAdmissionGate.d.ts.map +1 -0
- package/dist/VlmAdmissionGate.js +166 -0
- package/dist/VlmAdmissionGate.js.map +1 -0
- package/dist/autoSense.d.ts +266 -0
- package/dist/autoSense.d.ts.map +1 -0
- package/dist/autoSense.js +606 -0
- package/dist/autoSense.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +526 -27
- package/dist/cli.js.map +1 -1
- package/dist/combinedSense.d.ts +128 -0
- package/dist/combinedSense.d.ts.map +1 -0
- package/dist/combinedSense.js +261 -0
- package/dist/combinedSense.js.map +1 -0
- package/dist/config.d.ts +351 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +99 -2
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +135 -0
- package/dist/connectorCadence.d.ts.map +1 -0
- package/dist/connectorCadence.js +151 -0
- package/dist/connectorCadence.js.map +1 -0
- package/dist/dataDir.d.ts +33 -0
- package/dist/dataDir.d.ts.map +1 -0
- package/dist/dataDir.js +76 -0
- package/dist/dataDir.js.map +1 -0
- package/dist/deployedCadence.d.ts +167 -0
- package/dist/deployedCadence.d.ts.map +1 -0
- package/dist/deployedCadence.js +209 -0
- package/dist/deployedCadence.js.map +1 -0
- package/dist/deployedCadenceSource.d.ts +48 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -0
- package/dist/deployedCadenceSource.js +74 -0
- package/dist/deployedCadenceSource.js.map +1 -0
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.d.ts.map +1 -1
- package/dist/diagnose.js +15 -13
- package/dist/diagnose.js.map +1 -1
- package/dist/discovery.d.ts +131 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +499 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gemmaModelOta.d.ts +118 -0
- package/dist/gemmaModelOta.d.ts.map +1 -0
- package/dist/gemmaModelOta.js +291 -0
- package/dist/gemmaModelOta.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +25 -87
- package/dist/init.js.map +1 -1
- package/dist/keepAwake.d.ts +33 -0
- package/dist/keepAwake.d.ts.map +1 -0
- package/dist/keepAwake.js +54 -0
- package/dist/keepAwake.js.map +1 -0
- package/dist/legacyRecorderProfile.d.ts +20 -0
- package/dist/legacyRecorderProfile.d.ts.map +1 -0
- package/dist/legacyRecorderProfile.js +27 -0
- package/dist/legacyRecorderProfile.js.map +1 -0
- package/dist/localInferenceSchema.d.ts +132 -0
- package/dist/localInferenceSchema.d.ts.map +1 -0
- package/dist/localInferenceSchema.js +274 -0
- package/dist/localInferenceSchema.js.map +1 -0
- package/dist/localSenseE2eHarness.d.ts +101 -0
- package/dist/localSenseE2eHarness.d.ts.map +1 -0
- package/dist/localSenseE2eHarness.js +279 -0
- package/dist/localSenseE2eHarness.js.map +1 -0
- package/dist/localSensing.d.ts +284 -0
- package/dist/localSensing.d.ts.map +1 -0
- package/dist/localSensing.js +512 -0
- package/dist/localSensing.js.map +1 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +3 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/modelIntegrity.d.ts +80 -0
- package/dist/modelIntegrity.d.ts.map +1 -0
- package/dist/modelIntegrity.js +194 -0
- package/dist/modelIntegrity.js.map +1 -0
- package/dist/pairVerify.d.ts +100 -0
- package/dist/pairVerify.d.ts.map +1 -0
- package/dist/pairVerify.js +296 -0
- package/dist/pairVerify.js.map +1 -0
- package/dist/qrScanner.d.ts +33 -0
- package/dist/qrScanner.d.ts.map +1 -0
- package/dist/qrScanner.js +103 -0
- package/dist/qrScanner.js.map +1 -0
- package/dist/runtimePaths.d.ts +10 -0
- package/dist/runtimePaths.d.ts.map +1 -0
- package/dist/runtimePaths.js +79 -0
- package/dist/runtimePaths.js.map +1 -0
- package/dist/sensingRunMode.d.ts +86 -0
- package/dist/sensingRunMode.d.ts.map +1 -0
- package/dist/sensingRunMode.js +346 -0
- package/dist/sensingRunMode.js.map +1 -0
- package/dist/setupMode.d.ts +98 -0
- package/dist/setupMode.d.ts.map +1 -0
- package/dist/setupMode.js +828 -0
- package/dist/setupMode.js.map +1 -0
- package/dist/setupProgress.d.ts +31 -0
- package/dist/setupProgress.d.ts.map +1 -0
- package/dist/setupProgress.js +173 -0
- package/dist/setupProgress.js.map +1 -0
- package/package.json +13 -4
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
<#
|
|
2
|
+
Provision the LiteRT-LM on-device VLM runtime for the BYO-CCTV connector.
|
|
3
|
+
This script runs as the signed-in user, is safe to retry, and reports setup
|
|
4
|
+
progress without including camera credentials.
|
|
5
|
+
#>
|
|
6
|
+
[CmdletBinding()]
|
|
7
|
+
param(
|
|
8
|
+
[string]$Port = "9379",
|
|
9
|
+
[string]$ModelId = "gemma-4-E2B-it.litertlm",
|
|
10
|
+
[string]$HfRepo = "litert-community/gemma-4-E2B-it-litert-lm",
|
|
11
|
+
[string]$LitertPython = $(if ($env:LITERT_LM_PYTHON) { $env:LITERT_LM_PYTHON } else { "3.12" }),
|
|
12
|
+
[string]$ModelVersion = $(if ($env:GEMMA_VLM_VERSION) { $env:GEMMA_VLM_VERSION } else { "1.0.0" }),
|
|
13
|
+
[string]$BundledModelPath = "",
|
|
14
|
+
[string]$BundledModelSha256 = "181938105e0eefd105961417e8da75903eacda102c4fce9ce90f50b97139a63c",
|
|
15
|
+
[switch]$RequireBundledModel,
|
|
16
|
+
[switch]$AllowModelDownload,
|
|
17
|
+
[string]$ProgressPath = "",
|
|
18
|
+
[switch]$SmokeTest
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
$ErrorActionPreference = "Continue"
|
|
22
|
+
$Here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
23
|
+
$LocalBin = Join-Path $env:USERPROFILE ".local\bin"
|
|
24
|
+
$LogDir = Join-Path $env:LOCALAPPDATA "Trillboards\logs"
|
|
25
|
+
$LitertDir = if ($env:LITERT_LM_DIR) { $env:LITERT_LM_DIR } else { Join-Path $env:USERPROFILE ".litert-lm" }
|
|
26
|
+
$StoreDir = Join-Path (Join-Path $LitertDir "models") $ModelId
|
|
27
|
+
$VersionMarker = Join-Path $StoreDir "trillboards-bundled-version.json"
|
|
28
|
+
$ExpectedModelBytes = [long](2.4 * 1GB)
|
|
29
|
+
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
|
30
|
+
$LogPath = Join-Path $LogDir "litertlm-setup.log"
|
|
31
|
+
|
|
32
|
+
function Log([string]$Message) {
|
|
33
|
+
$line = "[trillboards setup-litertlm] {0} {1}" -f (Get-Date -Format "HH:mm:ss"), $Message
|
|
34
|
+
Add-Content -Path $LogPath -Value $line -ErrorAction SilentlyContinue
|
|
35
|
+
Write-Host $line
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Write-SetupProgress {
|
|
39
|
+
param(
|
|
40
|
+
[Parameter(Mandatory = $true)][string]$Stage,
|
|
41
|
+
[Parameter(Mandatory = $true)][string]$Title,
|
|
42
|
+
[Parameter(Mandatory = $true)][string]$Message,
|
|
43
|
+
[string]$Phase = "",
|
|
44
|
+
[object]$Percent = $null,
|
|
45
|
+
[object]$DownloadedBytes = $null,
|
|
46
|
+
[object]$TotalBytes = $null,
|
|
47
|
+
[bool]$Recoverable = $false
|
|
48
|
+
)
|
|
49
|
+
if ([string]::IsNullOrWhiteSpace($ProgressPath)) { return }
|
|
50
|
+
try {
|
|
51
|
+
$state = [ordered]@{
|
|
52
|
+
version = 1
|
|
53
|
+
stage = $Stage
|
|
54
|
+
title = $Title
|
|
55
|
+
message = $Message
|
|
56
|
+
updatedAt = [DateTime]::UtcNow.ToString("o")
|
|
57
|
+
}
|
|
58
|
+
if (Test-Path -LiteralPath $ProgressPath) {
|
|
59
|
+
try {
|
|
60
|
+
$previous = Get-Content -LiteralPath $ProgressPath -Raw | ConvertFrom-Json
|
|
61
|
+
foreach ($name in @("devicesFound", "validatedFeeds", "rejectedFeeds", "cameraCount")) {
|
|
62
|
+
$property = $previous.PSObject.Properties[$name]
|
|
63
|
+
if ($null -ne $property -and $null -ne $property.Value) { $state[$name] = [int]$property.Value }
|
|
64
|
+
}
|
|
65
|
+
} catch { }
|
|
66
|
+
}
|
|
67
|
+
if ($Phase) { $state.localAiPhase = $Phase }
|
|
68
|
+
if ($null -ne $Percent) { $state.localAiProgressPercent = [Math]::Max(0, [Math]::Min(100, [int]$Percent)) }
|
|
69
|
+
if ($null -ne $DownloadedBytes) { $state.localAiDownloadedBytes = [Math]::Max([long]0, [long]$DownloadedBytes) }
|
|
70
|
+
if ($null -ne $TotalBytes) { $state.localAiTotalBytes = [Math]::Max([long]0, [long]$TotalBytes) }
|
|
71
|
+
if ($Recoverable) { $state.recoverable = $true }
|
|
72
|
+
|
|
73
|
+
$parent = Split-Path -Parent $ProgressPath
|
|
74
|
+
New-Item -ItemType Directory -Force -Path $parent | Out-Null
|
|
75
|
+
$temporary = "$ProgressPath.$PID.tmp"
|
|
76
|
+
$json = ($state | ConvertTo-Json -Depth 4) + [Environment]::NewLine
|
|
77
|
+
[System.IO.File]::WriteAllText($temporary, $json, [System.Text.UTF8Encoding]::new($false))
|
|
78
|
+
Move-Item -LiteralPath $temporary -Destination $ProgressPath -Force
|
|
79
|
+
} catch {
|
|
80
|
+
Log "WARN: could not update setup progress: $_"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function Get-PartialModelBytes {
|
|
85
|
+
if (-not (Test-Path -LiteralPath $StoreDir)) { return [long]0 }
|
|
86
|
+
try {
|
|
87
|
+
$partial = Get-ChildItem -LiteralPath $StoreDir -Recurse -File -Filter "*.tmp" -ErrorAction SilentlyContinue |
|
|
88
|
+
Sort-Object Length -Descending |
|
|
89
|
+
Select-Object -First 1
|
|
90
|
+
if ($null -ne $partial) { return [long]$partial.Length }
|
|
91
|
+
} catch { }
|
|
92
|
+
return [long]0
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function Write-DownloadProgress {
|
|
96
|
+
$downloaded = Get-PartialModelBytes
|
|
97
|
+
$percent = [int][Math]::Min(99, [Math]::Floor(($downloaded * 100.0) / $ExpectedModelBytes))
|
|
98
|
+
Write-SetupProgress -Stage "local_ai" -Title "Downloading local AI" -Message "Gemma is downloading to this computer. Keep it online; cloud inference remains off." -Phase "downloading_model" -Percent $percent -DownloadedBytes $downloaded -TotalBytes $ExpectedModelBytes
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function Test-ServeHealthy {
|
|
102
|
+
try {
|
|
103
|
+
$response = Invoke-WebRequest -Uri "http://127.0.0.1:$Port/v1/models" -UseBasicParsing -TimeoutSec 3
|
|
104
|
+
return ($response.StatusCode -eq 200 -and ([string]$response.Content) -match [regex]::Escape($ModelId))
|
|
105
|
+
} catch {
|
|
106
|
+
return $false
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function Complete-Setup {
|
|
111
|
+
Log "local AI is healthy on :$Port"
|
|
112
|
+
Write-SetupProgress -Stage "ready" -Title "Camera setup is complete" -Message "Local AI is installed and healthy. Trillboards is running in the background." -Phase "verifying" -Percent 100
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function Fail-LocalAi([string]$Message) {
|
|
116
|
+
Log "ERROR: $Message"
|
|
117
|
+
Write-SetupProgress -Stage "local_ai_error" -Title "Local AI needs attention" -Message $Message -Recoverable $true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function Test-LitertLmModel([string]$ExecutablePath) {
|
|
121
|
+
try { return ((& $ExecutablePath list 2>$null | Out-String) -match [regex]::Escape($ModelId)) }
|
|
122
|
+
catch { return $false }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ($SmokeTest) {
|
|
126
|
+
if ([string]::IsNullOrWhiteSpace($ProgressPath)) { throw "SmokeTest requires ProgressPath" }
|
|
127
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "Smoke test" -Phase "installing_model" -Percent 62 -TotalBytes $ExpectedModelBytes
|
|
128
|
+
Write-SetupProgress -Stage "ready" -Title "Camera setup is complete" -Message "Smoke test complete" -Phase "verifying" -Percent 100
|
|
129
|
+
$smokeState = Get-Content -LiteralPath $ProgressPath -Raw | ConvertFrom-Json
|
|
130
|
+
if ($smokeState.stage -ne "ready" -or $smokeState.localAiProgressPercent -ne 100) {
|
|
131
|
+
throw "Local AI progress smoke test failed"
|
|
132
|
+
}
|
|
133
|
+
Write-Host "LiteRT-LM setup progress smoke test passed."
|
|
134
|
+
exit 0
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
$setupMutex = New-Object System.Threading.Mutex($false, "Local\TrillboardsLiteRtLmSetup")
|
|
138
|
+
$haveSetupMutex = $false
|
|
139
|
+
try {
|
|
140
|
+
$haveSetupMutex = $setupMutex.WaitOne(0)
|
|
141
|
+
} catch [System.Threading.AbandonedMutexException] {
|
|
142
|
+
$haveSetupMutex = $true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (-not $haveSetupMutex) {
|
|
146
|
+
Log "LiteRT-LM provisioning is already running; attaching setup progress"
|
|
147
|
+
if ([string]::IsNullOrWhiteSpace($ProgressPath) -and -not $RequireBundledModel) {
|
|
148
|
+
$setupMutex.Dispose()
|
|
149
|
+
exit 0
|
|
150
|
+
}
|
|
151
|
+
for ($attempt = 0; $attempt -lt 600 -and -not $haveSetupMutex; $attempt++) {
|
|
152
|
+
if (Test-ServeHealthy) {
|
|
153
|
+
Complete-Setup
|
|
154
|
+
$setupMutex.Dispose()
|
|
155
|
+
exit 0
|
|
156
|
+
}
|
|
157
|
+
if ((Get-PartialModelBytes) -gt 0) {
|
|
158
|
+
Write-DownloadProgress
|
|
159
|
+
} else {
|
|
160
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "The local AI setup is already running. Cloud inference remains off." -Phase "installing_runtime" -Percent 0
|
|
161
|
+
}
|
|
162
|
+
Start-Sleep -Seconds 3
|
|
163
|
+
try {
|
|
164
|
+
$haveSetupMutex = $setupMutex.WaitOne(0)
|
|
165
|
+
} catch [System.Threading.AbandonedMutexException] {
|
|
166
|
+
$haveSetupMutex = $true
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (-not $haveSetupMutex) {
|
|
170
|
+
Fail-LocalAi "The local AI installation did not finish. Reinstall Trillboards CCTV Setup."
|
|
171
|
+
$setupMutex.Dispose()
|
|
172
|
+
exit 1
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function Stop-SetupLock {
|
|
177
|
+
try { $setupMutex.ReleaseMutex() } catch { }
|
|
178
|
+
$setupMutex.Dispose()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
Log "starting LiteRT-LM provisioning (Windows x64, CPU backend)"
|
|
182
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "Preparing the private on-device AI runtime. Cloud inference remains off." -Phase "installing_runtime" -Percent 0
|
|
183
|
+
|
|
184
|
+
if (-not [Environment]::Is64BitOperatingSystem) {
|
|
185
|
+
Fail-LocalAi "Local AI requires 64-bit Windows. Camera pairing is saved."
|
|
186
|
+
Stop-SetupLock
|
|
187
|
+
exit 1
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (Test-ServeHealthy) {
|
|
191
|
+
Complete-Setup
|
|
192
|
+
Stop-SetupLock
|
|
193
|
+
exit 0
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
$env:Path = "$LocalBin;$env:Path"
|
|
197
|
+
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
|
|
198
|
+
Log "uv not found; installing the local package runtime (no admin required)"
|
|
199
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "Installing the local AI runtime. Cloud inference remains off." -Phase "installing_runtime" -Percent 5
|
|
200
|
+
try { Invoke-RestMethod -Uri "https://astral.sh/uv/install.ps1" | Invoke-Expression *>> $LogPath }
|
|
201
|
+
catch { Log "WARN: uv install failed: $_" }
|
|
202
|
+
$env:Path = "$LocalBin;$env:Path"
|
|
203
|
+
}
|
|
204
|
+
$uvCmd = Get-Command uv -ErrorAction SilentlyContinue
|
|
205
|
+
if (-not $uvCmd) {
|
|
206
|
+
Fail-LocalAi "The local AI runtime could not be installed. Check the internet connection, then retry Local AI."
|
|
207
|
+
Stop-SetupLock
|
|
208
|
+
exit 1
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
$litertlmCmd = Get-Command litert-lm -ErrorAction SilentlyContinue
|
|
212
|
+
$litertlmPath = if ($litertlmCmd) { $litertlmCmd.Source } else { Join-Path $LocalBin "litert-lm.exe" }
|
|
213
|
+
function Test-LitertLmCli {
|
|
214
|
+
if (-not (Test-Path -LiteralPath $litertlmPath)) { return $false }
|
|
215
|
+
try {
|
|
216
|
+
& $litertlmPath list *> $null
|
|
217
|
+
return ($LASTEXITCODE -eq 0)
|
|
218
|
+
} catch {
|
|
219
|
+
return $false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (Test-LitertLmCli) {
|
|
224
|
+
Log "litert-lm CLI is healthy; keeping current install"
|
|
225
|
+
} else {
|
|
226
|
+
Log "installing or repairing litert-lm with Python $LitertPython"
|
|
227
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "Installing the local AI engine. Cloud inference remains off." -Phase "installing_runtime" -Percent 10
|
|
228
|
+
& $uvCmd.Source tool install --python $LitertPython --managed-python --upgrade --force litert-lm *>> $LogPath
|
|
229
|
+
$litertlmCmd = Get-Command litert-lm -ErrorAction SilentlyContinue
|
|
230
|
+
$litertlmPath = if ($litertlmCmd) { $litertlmCmd.Source } else { Join-Path $LocalBin "litert-lm.exe" }
|
|
231
|
+
if (-not (Test-LitertLmCli)) {
|
|
232
|
+
Log "litert-lm remains unhealthy after repair"
|
|
233
|
+
Fail-LocalAi "The local AI engine could not be installed. Check the internet connection, then retry Local AI."
|
|
234
|
+
Stop-SetupLock
|
|
235
|
+
exit 1
|
|
236
|
+
}
|
|
237
|
+
Log "litert-lm CLI repaired successfully"
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
$bundledModelVerified = $false
|
|
241
|
+
if (-not [string]::IsNullOrWhiteSpace($BundledModelPath)) {
|
|
242
|
+
if (-not (Test-Path -LiteralPath $BundledModelPath -PathType Leaf)) {
|
|
243
|
+
Fail-LocalAi "The local AI model included with this installer is missing. Run the installer again."
|
|
244
|
+
Stop-SetupLock
|
|
245
|
+
exit 1
|
|
246
|
+
}
|
|
247
|
+
$actualModelSha256 = (Get-FileHash -LiteralPath $BundledModelPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
248
|
+
if ($actualModelSha256 -ne $BundledModelSha256.ToLowerInvariant()) {
|
|
249
|
+
Log "bundled model checksum mismatch (expected $BundledModelSha256, got $actualModelSha256)"
|
|
250
|
+
Fail-LocalAi "The included local AI model could not be verified. Download a fresh Trillboards installer."
|
|
251
|
+
Stop-SetupLock
|
|
252
|
+
exit 1
|
|
253
|
+
}
|
|
254
|
+
$bundledModelVerified = $true
|
|
255
|
+
Log "verified bundled model SHA-256"
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
$haveModel = Test-LitertLmModel $litertlmPath
|
|
259
|
+
if ($haveModel) {
|
|
260
|
+
Log "model $ModelId already imported; skipping import"
|
|
261
|
+
} elseif ($bundledModelVerified) {
|
|
262
|
+
Write-SetupProgress -Stage "local_ai" -Title "Installing local AI" -Message "Installing the AI model included with Trillboards. Cloud inference remains off." -Phase "installing_model" -Percent 25 -TotalBytes (Get-Item -LiteralPath $BundledModelPath).Length
|
|
263
|
+
& $litertlmPath import $BundledModelPath $ModelId *>> $LogPath
|
|
264
|
+
$haveModel = (($LASTEXITCODE -eq 0) -and (Test-LitertLmModel $litertlmPath))
|
|
265
|
+
if ($haveModel) {
|
|
266
|
+
Log "model imported from the installer bundle"
|
|
267
|
+
} else {
|
|
268
|
+
Log "bundled model import failed"
|
|
269
|
+
}
|
|
270
|
+
} elseif ($RequireBundledModel -or -not $AllowModelDownload) {
|
|
271
|
+
Fail-LocalAi "The included local AI model is unavailable. Run the Trillboards installer again."
|
|
272
|
+
Stop-SetupLock
|
|
273
|
+
exit 1
|
|
274
|
+
} else {
|
|
275
|
+
$maxAttempts = 3
|
|
276
|
+
for ($attempt = 1; $attempt -le $maxAttempts -and -not $haveModel; $attempt++) {
|
|
277
|
+
Log "importing $ModelId from $HfRepo (about 2.4 GB) [attempt $attempt/$maxAttempts]"
|
|
278
|
+
Write-DownloadProgress
|
|
279
|
+
$stdoutPath = Join-Path $LogDir "litertlm-import-$PID.out.log"
|
|
280
|
+
$stderrPath = Join-Path $LogDir "litertlm-import-$PID.err.log"
|
|
281
|
+
try {
|
|
282
|
+
$process = Start-Process -FilePath $litertlmPath -ArgumentList @("import", "--from-huggingface-repo", $HfRepo, $ModelId) -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -PassThru
|
|
283
|
+
while (-not $process.HasExited) {
|
|
284
|
+
Write-DownloadProgress
|
|
285
|
+
Start-Sleep -Seconds 2
|
|
286
|
+
$process.Refresh()
|
|
287
|
+
}
|
|
288
|
+
$process.WaitForExit()
|
|
289
|
+
Log "model import exited with code $($process.ExitCode)"
|
|
290
|
+
$process.Dispose()
|
|
291
|
+
} catch {
|
|
292
|
+
Log "WARN: model import process failed: $_"
|
|
293
|
+
}
|
|
294
|
+
foreach ($path in @($stdoutPath, $stderrPath)) {
|
|
295
|
+
if (Test-Path -LiteralPath $path) {
|
|
296
|
+
Get-Content -LiteralPath $path -ErrorAction SilentlyContinue | Add-Content -Path $LogPath -ErrorAction SilentlyContinue
|
|
297
|
+
Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
$haveModel = Test-LitertLmModel $litertlmPath
|
|
301
|
+
if ($haveModel) {
|
|
302
|
+
Log "model imported"
|
|
303
|
+
} elseif ($attempt -lt $maxAttempts) {
|
|
304
|
+
$backoff = 5 * $attempt
|
|
305
|
+
Log "WARN: model import attempt $attempt failed; retrying in ${backoff}s"
|
|
306
|
+
Start-Sleep -Seconds $backoff
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (-not $haveModel) {
|
|
312
|
+
$failureMessage = if ($RequireBundledModel) {
|
|
313
|
+
"The included local AI model could not be installed. Run the Trillboards installer again."
|
|
314
|
+
} else {
|
|
315
|
+
"Gemma did not finish downloading. Check the internet connection, then retry Local AI."
|
|
316
|
+
}
|
|
317
|
+
Fail-LocalAi $failureMessage
|
|
318
|
+
Stop-SetupLock
|
|
319
|
+
exit 1
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
New-Item -ItemType Directory -Force -Path $StoreDir | Out-Null
|
|
324
|
+
$markerJson = '{"version":"' + $ModelVersion + '","modelType":"gemma-vlm"}'
|
|
325
|
+
[System.IO.File]::WriteAllText($VersionMarker, $markerJson, [System.Text.UTF8Encoding]::new($false))
|
|
326
|
+
Log "recorded installed version $ModelVersion at $VersionMarker"
|
|
327
|
+
} catch {
|
|
328
|
+
Log "WARN: could not write version marker $VersionMarker : $_"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
Write-SetupProgress -Stage "local_ai" -Title "Starting local AI" -Message "Gemma is installed. Starting the private local AI service." -Phase "starting_service" -Percent 100
|
|
332
|
+
$registration = Join-Path $Here "register-litertlm-task.ps1"
|
|
333
|
+
if (-not (Test-Path -LiteralPath $registration)) {
|
|
334
|
+
Fail-LocalAi "The local AI service installer is missing. Reinstall the connector, then retry Local AI."
|
|
335
|
+
Stop-SetupLock
|
|
336
|
+
exit 1
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
Log "registering litert-lm serve Scheduled Task on :$Port"
|
|
340
|
+
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $registration -LitertLmPath $litertlmPath -Port $Port *>> $LogPath
|
|
341
|
+
if ($LASTEXITCODE -ne 0) {
|
|
342
|
+
Fail-LocalAi "The local AI service could not be started. Restart Windows, then retry Local AI."
|
|
343
|
+
Stop-SetupLock
|
|
344
|
+
exit 1
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
Write-SetupProgress -Stage "local_ai" -Title "Checking local AI" -Message "Verifying that Gemma is responding on this computer." -Phase "verifying" -Percent 100
|
|
348
|
+
$healthy = $false
|
|
349
|
+
for ($attempt = 0; $attempt -lt 60 -and -not $healthy; $attempt++) {
|
|
350
|
+
$healthy = Test-ServeHealthy
|
|
351
|
+
if (-not $healthy) { Start-Sleep -Seconds 2 }
|
|
352
|
+
}
|
|
353
|
+
if (-not $healthy) {
|
|
354
|
+
Fail-LocalAi "Gemma is installed, but the local AI service did not respond. Restart Windows, then retry Local AI."
|
|
355
|
+
Stop-SetupLock
|
|
356
|
+
exit 1
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
Complete-Setup
|
|
360
|
+
Stop-SetupLock
|
|
361
|
+
exit 0
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<#
|
|
2
|
+
Stops only processes belonging to this per-user Trillboards Edge install.
|
|
3
|
+
Used by upgrades and uninstall so bundled node.exe is never overwritten while
|
|
4
|
+
running, without killing unrelated Node or PowerShell applications.
|
|
5
|
+
#>
|
|
6
|
+
[CmdletBinding()]
|
|
7
|
+
param(
|
|
8
|
+
[Parameter(Mandatory = $true)][string]$InstallDir
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
$ErrorActionPreference = "Stop"
|
|
12
|
+
$root = [IO.Path]::GetFullPath($InstallDir).TrimEnd('\')
|
|
13
|
+
$rootPrefix = $root + '\'
|
|
14
|
+
|
|
15
|
+
Stop-ScheduledTask -TaskName "TrillboardsEdge" -ErrorAction SilentlyContinue
|
|
16
|
+
Start-Sleep -Milliseconds 750
|
|
17
|
+
|
|
18
|
+
function Get-TrillboardsEdgeProcesses {
|
|
19
|
+
$markers = @(
|
|
20
|
+
'trillboards-edge-task.vbs',
|
|
21
|
+
'run-connector.ps1',
|
|
22
|
+
'trillboards-edge.cmd',
|
|
23
|
+
'trillboards-setup.ps1',
|
|
24
|
+
'trillboards-pair.cmd'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
|
|
28
|
+
if ($_.ProcessId -eq $PID) { return $false }
|
|
29
|
+
$exe = if ($_.ExecutablePath) { [IO.Path]::GetFullPath($_.ExecutablePath) } else { '' }
|
|
30
|
+
$isInstalledExecutable = (
|
|
31
|
+
$exe.Equals($root, [StringComparison]::OrdinalIgnoreCase) -or
|
|
32
|
+
$exe.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase)
|
|
33
|
+
)
|
|
34
|
+
if ($isInstalledExecutable) {
|
|
35
|
+
return $true
|
|
36
|
+
}
|
|
37
|
+
$commandLine = [string]$_.CommandLine
|
|
38
|
+
if ($commandLine.IndexOf($root, [StringComparison]::OrdinalIgnoreCase) -lt 0) {
|
|
39
|
+
return $false
|
|
40
|
+
}
|
|
41
|
+
foreach ($marker in $markers) {
|
|
42
|
+
if ($commandLine.IndexOf($marker, [StringComparison]::OrdinalIgnoreCase) -ge 0) {
|
|
43
|
+
return $true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return $false
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$processes = Get-TrillboardsEdgeProcesses
|
|
51
|
+
foreach ($process in $processes) {
|
|
52
|
+
Stop-Process -Id $process.ProcessId -ErrorAction SilentlyContinue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
$deadline = [DateTime]::UtcNow.AddSeconds(5)
|
|
56
|
+
do {
|
|
57
|
+
Start-Sleep -Milliseconds 250
|
|
58
|
+
$remaining = Get-TrillboardsEdgeProcesses
|
|
59
|
+
} while ($remaining.Count -gt 0 -and [DateTime]::UtcNow -lt $deadline)
|
|
60
|
+
|
|
61
|
+
foreach ($process in $remaining) {
|
|
62
|
+
Stop-Process -Id $process.ProcessId -Force -ErrorAction SilentlyContinue
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Start-Sleep -Milliseconds 250
|
|
66
|
+
$remaining = Get-TrillboardsEdgeProcesses
|
|
67
|
+
if ($remaining.Count -gt 0) {
|
|
68
|
+
$ids = ($remaining | ForEach-Object { $_.ProcessId }) -join ', '
|
|
69
|
+
throw "Could not stop the existing Trillboards connector processes: $ids"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Write-Host "Stopped existing Trillboards Edge processes."
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Option Explicit
|
|
2
|
+
|
|
3
|
+
Dim fso, shell, scriptDir, installDir, supervisor
|
|
4
|
+
Dim mode, command, exitCode
|
|
5
|
+
|
|
6
|
+
Set fso = CreateObject("Scripting.FileSystemObject")
|
|
7
|
+
Set shell = CreateObject("WScript.Shell")
|
|
8
|
+
|
|
9
|
+
scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
|
|
10
|
+
installDir = fso.GetParentFolderName(scriptDir)
|
|
11
|
+
mode = "auto-sense"
|
|
12
|
+
If WScript.Arguments.Count > 0 Then mode = LCase(Trim(WScript.Arguments(0)))
|
|
13
|
+
If mode <> "auto-sense" And mode <> "local-sense" And mode <> "cloud-sense" Then
|
|
14
|
+
mode = "auto-sense"
|
|
15
|
+
End If
|
|
16
|
+
|
|
17
|
+
supervisor = fso.BuildPath(scriptDir, "run-connector.ps1")
|
|
18
|
+
|
|
19
|
+
' PowerShell owns the child output stream so it can rotate the log while the
|
|
20
|
+
' connector remains healthy. Window style 0 keeps the entire process tree hidden.
|
|
21
|
+
command = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File " _
|
|
22
|
+
& Chr(34) & supervisor & Chr(34) & " -InstallDir " & Chr(34) & installDir & Chr(34) _
|
|
23
|
+
& " -Mode " & Chr(34) & mode & Chr(34)
|
|
24
|
+
exitCode = shell.Run(command, 0, True)
|
|
25
|
+
WScript.Quit exitCode
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM Trillboards Edge connector launcher (bundled runtime — no system Node/ffmpeg).
|
|
3
|
+
REM bin\ goes first on PATH so the connector's default ffmpeg='ffmpeg' resolves to
|
|
4
|
+
REM OUR bundled ffmpeg.exe; NODE_PATH points at the bundled runtime subset.
|
|
5
|
+
setlocal EnableExtensions EnableDelayedExpansion
|
|
6
|
+
set "BIN=%~dp0"
|
|
7
|
+
set "PATH=%BIN%;%PATH%"
|
|
8
|
+
set "NODE_PATH=%BIN%..\lib\node_modules"
|
|
9
|
+
|
|
10
|
+
REM The scheduled task exists only after installation and starts after pairing.
|
|
11
|
+
REM Repair or restart the installer-provisioned local AI service on login. In
|
|
12
|
+
REM required-bundle mode this never downloads a missing model from the network.
|
|
13
|
+
if /I "%~1"=="auto-sense" (
|
|
14
|
+
set "LITERT_ROOT=%LITERT_LM_DIR%"
|
|
15
|
+
if not defined LITERT_ROOT set "LITERT_ROOT=%USERPROFILE%\.litert-lm"
|
|
16
|
+
set "NEEDS_LOCAL_AI=0"
|
|
17
|
+
if not exist "!LITERT_ROOT!\models\gemma-4-E2B-it.litertlm\trillboards-bundled-version.json" set "NEEDS_LOCAL_AI=1"
|
|
18
|
+
schtasks /Query /TN "TrillboardsLiteRtLm" >nul 2>&1
|
|
19
|
+
if errorlevel 1 set "NEEDS_LOCAL_AI=1"
|
|
20
|
+
curl.exe --fail --silent --max-time 2 http://127.0.0.1:9379/v1/models >nul 2>&1
|
|
21
|
+
if errorlevel 1 set "NEEDS_LOCAL_AI=1"
|
|
22
|
+
if "!NEEDS_LOCAL_AI!"=="1" if exist "%BIN%setup-litertlm.ps1" (
|
|
23
|
+
set "SETUP_PROGRESS=%LOCALAPPDATA%\Trillboards\setup-progress.json"
|
|
24
|
+
if exist "!SETUP_PROGRESS!" (
|
|
25
|
+
start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%BIN%setup-litertlm.ps1" -RequireBundledModel -ProgressPath "!SETUP_PROGRESS!"
|
|
26
|
+
) else (
|
|
27
|
+
start "" powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "%BIN%setup-litertlm.ps1" -RequireBundledModel
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
"%BIN%node.exe" "%BIN%..\lib\edge-sdk\dist\cli.js" %*
|
|
33
|
+
exit /b %ERRORLEVEL%
|