@trillboards/edge-sdk 0.2.4 → 0.2.6

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.
Files changed (200) hide show
  1. package/README.md +4 -3
  2. package/deploy/docker/Dockerfile.cloud-sense +117 -0
  3. package/deploy/docker/Dockerfile.cpu +30 -21
  4. package/deploy/docker/Dockerfile.cuda +30 -21
  5. package/deploy/docker/Dockerfile.openvino +30 -20
  6. package/deploy/docker/README.md +9 -4
  7. package/deploy/docker/connector-entrypoint.sh +122 -0
  8. package/deploy/helm/README.md +1 -1
  9. package/deploy/k8s/daemonset.yaml +1 -1
  10. package/deploy/macos/README.md +161 -0
  11. package/deploy/macos/TrillboardsSetupApp.m +603 -0
  12. package/deploy/macos/build-macos-pkg.sh +588 -0
  13. package/deploy/macos/com.trillboards.edge.plist +62 -0
  14. package/deploy/macos/com.trillboards.litertlm.plist +45 -0
  15. package/deploy/macos/pair.command +519 -0
  16. package/deploy/macos/scripts/ensure-litertlm.sh +62 -0
  17. package/deploy/macos/scripts/postinstall +153 -0
  18. package/deploy/macos/scripts/setup-litertlm.sh +272 -0
  19. package/deploy/macos/test-install-pair.sh +383 -0
  20. package/deploy/macos/test-setup-litertlm-reinstall.sh +109 -0
  21. package/deploy/systemd/install.sh +25 -15
  22. package/deploy/systemd/trillboards-edge.service +4 -1
  23. package/deploy/windows/README.md +70 -0
  24. package/deploy/windows/build-windows.ps1 +225 -0
  25. package/deploy/windows/bundle-windows-installer.ps1 +153 -0
  26. package/deploy/windows/check-feeds.js +26 -0
  27. package/deploy/windows/e2e/fixtures/person.jpg +0 -0
  28. package/deploy/windows/e2e/test-local-sense.ps1 +185 -0
  29. package/deploy/windows/install-update.ps1 +101 -0
  30. package/deploy/windows/installer.nsi +138 -0
  31. package/deploy/windows/register-litertlm-task.ps1 +42 -0
  32. package/deploy/windows/register-task.ps1 +44 -0
  33. package/deploy/windows/run-connector.ps1 +69 -0
  34. package/deploy/windows/setup-litertlm.ps1 +361 -0
  35. package/deploy/windows/stop-existing.ps1 +72 -0
  36. package/deploy/windows/trillboards-edge-task.vbs +25 -0
  37. package/deploy/windows/trillboards-edge.cmd +33 -0
  38. package/deploy/windows/trillboards-pair.cmd +276 -0
  39. package/deploy/windows/trillboards-setup.ps1 +456 -0
  40. package/deploy/windows/trillboards-setup.vbs +13 -0
  41. package/dist/AudienceAnalyzeClient.d.ts +136 -0
  42. package/dist/AudienceAnalyzeClient.d.ts.map +1 -0
  43. package/dist/AudienceAnalyzeClient.js +246 -0
  44. package/dist/AudienceAnalyzeClient.js.map +1 -0
  45. package/dist/ChannelProber.d.ts +150 -0
  46. package/dist/ChannelProber.d.ts.map +1 -0
  47. package/dist/ChannelProber.js +887 -0
  48. package/dist/ChannelProber.js.map +1 -0
  49. package/dist/CloudEscalationPolicy.d.ts +143 -0
  50. package/dist/CloudEscalationPolicy.d.ts.map +1 -0
  51. package/dist/CloudEscalationPolicy.js +137 -0
  52. package/dist/CloudEscalationPolicy.js.map +1 -0
  53. package/dist/CloudSensingManager.d.ts +533 -0
  54. package/dist/CloudSensingManager.d.ts.map +1 -0
  55. package/dist/CloudSensingManager.js +1254 -0
  56. package/dist/CloudSensingManager.js.map +1 -0
  57. package/dist/DiscoveryReportClient.d.ts +66 -0
  58. package/dist/DiscoveryReportClient.d.ts.map +1 -0
  59. package/dist/DiscoveryReportClient.js +85 -0
  60. package/dist/DiscoveryReportClient.js.map +1 -0
  61. package/dist/EdgeAgent.d.ts +12 -2
  62. package/dist/EdgeAgent.d.ts.map +1 -1
  63. package/dist/EdgeAgent.js +55 -13
  64. package/dist/EdgeAgent.js.map +1 -1
  65. package/dist/LiteRtVlmProvider.d.ts +193 -0
  66. package/dist/LiteRtVlmProvider.d.ts.map +1 -0
  67. package/dist/LiteRtVlmProvider.js +436 -0
  68. package/dist/LiteRtVlmProvider.js.map +1 -0
  69. package/dist/MosaicSource.d.ts +108 -0
  70. package/dist/MosaicSource.d.ts.map +1 -0
  71. package/dist/MosaicSource.js +274 -0
  72. package/dist/MosaicSource.js.map +1 -0
  73. package/dist/OnvifClient.d.ts +83 -0
  74. package/dist/OnvifClient.d.ts.map +1 -0
  75. package/dist/OnvifClient.js +209 -0
  76. package/dist/OnvifClient.js.map +1 -0
  77. package/dist/PairClient.d.ts +106 -0
  78. package/dist/PairClient.d.ts.map +1 -0
  79. package/dist/PairClient.js +186 -0
  80. package/dist/PairClient.js.map +1 -0
  81. package/dist/RecorderAddressRecovery.d.ts +53 -0
  82. package/dist/RecorderAddressRecovery.d.ts.map +1 -0
  83. package/dist/RecorderAddressRecovery.js +359 -0
  84. package/dist/RecorderAddressRecovery.js.map +1 -0
  85. package/dist/SceneChangeDetector.d.ts +291 -0
  86. package/dist/SceneChangeDetector.d.ts.map +1 -0
  87. package/dist/SceneChangeDetector.js +682 -0
  88. package/dist/SceneChangeDetector.js.map +1 -0
  89. package/dist/SensingProfileClient.d.ts +59 -0
  90. package/dist/SensingProfileClient.d.ts.map +1 -0
  91. package/dist/SensingProfileClient.js +78 -0
  92. package/dist/SensingProfileClient.js.map +1 -0
  93. package/dist/SetupSessionClient.d.ts +114 -0
  94. package/dist/SetupSessionClient.d.ts.map +1 -0
  95. package/dist/SetupSessionClient.js +95 -0
  96. package/dist/SetupSessionClient.js.map +1 -0
  97. package/dist/VlmAdmissionGate.d.ts +126 -0
  98. package/dist/VlmAdmissionGate.d.ts.map +1 -0
  99. package/dist/VlmAdmissionGate.js +166 -0
  100. package/dist/VlmAdmissionGate.js.map +1 -0
  101. package/dist/autoSense.d.ts +216 -0
  102. package/dist/autoSense.d.ts.map +1 -0
  103. package/dist/autoSense.js +521 -0
  104. package/dist/autoSense.js.map +1 -0
  105. package/dist/cli.d.ts +27 -1
  106. package/dist/cli.d.ts.map +1 -1
  107. package/dist/cli.js +511 -21
  108. package/dist/cli.js.map +1 -1
  109. package/dist/combinedSense.d.ts +128 -0
  110. package/dist/combinedSense.d.ts.map +1 -0
  111. package/dist/combinedSense.js +261 -0
  112. package/dist/combinedSense.js.map +1 -0
  113. package/dist/config.d.ts +351 -0
  114. package/dist/config.d.ts.map +1 -1
  115. package/dist/config.js +99 -2
  116. package/dist/config.js.map +1 -1
  117. package/dist/connectorCadence.d.ts +135 -0
  118. package/dist/connectorCadence.d.ts.map +1 -0
  119. package/dist/connectorCadence.js +151 -0
  120. package/dist/connectorCadence.js.map +1 -0
  121. package/dist/dataDir.d.ts +33 -0
  122. package/dist/dataDir.d.ts.map +1 -0
  123. package/dist/dataDir.js +76 -0
  124. package/dist/dataDir.js.map +1 -0
  125. package/dist/deployedCadence.d.ts +167 -0
  126. package/dist/deployedCadence.d.ts.map +1 -0
  127. package/dist/deployedCadence.js +209 -0
  128. package/dist/deployedCadence.js.map +1 -0
  129. package/dist/deployedCadenceSource.d.ts +48 -0
  130. package/dist/deployedCadenceSource.d.ts.map +1 -0
  131. package/dist/deployedCadenceSource.js +74 -0
  132. package/dist/deployedCadenceSource.js.map +1 -0
  133. package/dist/diagnose.d.ts +1 -1
  134. package/dist/diagnose.d.ts.map +1 -1
  135. package/dist/diagnose.js +13 -12
  136. package/dist/diagnose.js.map +1 -1
  137. package/dist/discovery.d.ts +131 -0
  138. package/dist/discovery.d.ts.map +1 -0
  139. package/dist/discovery.js +499 -0
  140. package/dist/discovery.js.map +1 -0
  141. package/dist/gemmaModelOta.d.ts +118 -0
  142. package/dist/gemmaModelOta.d.ts.map +1 -0
  143. package/dist/gemmaModelOta.js +291 -0
  144. package/dist/gemmaModelOta.js.map +1 -0
  145. package/dist/index.d.ts +16 -1
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +40 -1
  148. package/dist/index.js.map +1 -1
  149. package/dist/init.d.ts.map +1 -1
  150. package/dist/init.js +41 -18
  151. package/dist/init.js.map +1 -1
  152. package/dist/keepAwake.d.ts +33 -0
  153. package/dist/keepAwake.d.ts.map +1 -0
  154. package/dist/keepAwake.js +54 -0
  155. package/dist/keepAwake.js.map +1 -0
  156. package/dist/legacyRecorderProfile.d.ts +20 -0
  157. package/dist/legacyRecorderProfile.d.ts.map +1 -0
  158. package/dist/legacyRecorderProfile.js +27 -0
  159. package/dist/legacyRecorderProfile.js.map +1 -0
  160. package/dist/localInferenceSchema.d.ts +132 -0
  161. package/dist/localInferenceSchema.d.ts.map +1 -0
  162. package/dist/localInferenceSchema.js +274 -0
  163. package/dist/localInferenceSchema.js.map +1 -0
  164. package/dist/localSenseE2eHarness.d.ts +101 -0
  165. package/dist/localSenseE2eHarness.d.ts.map +1 -0
  166. package/dist/localSenseE2eHarness.js +279 -0
  167. package/dist/localSenseE2eHarness.js.map +1 -0
  168. package/dist/localSensing.d.ts +268 -0
  169. package/dist/localSensing.d.ts.map +1 -0
  170. package/dist/localSensing.js +492 -0
  171. package/dist/localSensing.js.map +1 -0
  172. package/dist/modelIntegrity.d.ts +59 -0
  173. package/dist/modelIntegrity.d.ts.map +1 -0
  174. package/dist/modelIntegrity.js +165 -0
  175. package/dist/modelIntegrity.js.map +1 -0
  176. package/dist/pairVerify.d.ts +100 -0
  177. package/dist/pairVerify.d.ts.map +1 -0
  178. package/dist/pairVerify.js +296 -0
  179. package/dist/pairVerify.js.map +1 -0
  180. package/dist/qrScanner.d.ts +33 -0
  181. package/dist/qrScanner.d.ts.map +1 -0
  182. package/dist/qrScanner.js +103 -0
  183. package/dist/qrScanner.js.map +1 -0
  184. package/dist/runtimePaths.d.ts +10 -0
  185. package/dist/runtimePaths.d.ts.map +1 -0
  186. package/dist/runtimePaths.js +79 -0
  187. package/dist/runtimePaths.js.map +1 -0
  188. package/dist/sensingRunMode.d.ts +68 -0
  189. package/dist/sensingRunMode.d.ts.map +1 -0
  190. package/dist/sensingRunMode.js +346 -0
  191. package/dist/sensingRunMode.js.map +1 -0
  192. package/dist/setupMode.d.ts +98 -0
  193. package/dist/setupMode.d.ts.map +1 -0
  194. package/dist/setupMode.js +828 -0
  195. package/dist/setupMode.js.map +1 -0
  196. package/dist/setupProgress.d.ts +31 -0
  197. package/dist/setupProgress.d.ts.map +1 -0
  198. package/dist/setupProgress.js +173 -0
  199. package/dist/setupProgress.js.map +1 -0
  200. package/package.json +13 -4
@@ -0,0 +1,456 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [switch]$SmokeTest
4
+ )
5
+
6
+ $ErrorActionPreference = 'Stop'
7
+ Set-StrictMode -Version Latest
8
+
9
+ Add-Type -AssemblyName System.Windows.Forms
10
+ Add-Type -AssemblyName System.Drawing
11
+ [System.Windows.Forms.Application]::EnableVisualStyles()
12
+
13
+ $BinDir = $PSScriptRoot
14
+ $Pairer = Join-Path $BinDir 'trillboards-pair.cmd'
15
+ $StateDir = Join-Path $env:LOCALAPPDATA 'Trillboards'
16
+ $StatePath = Join-Path $StateDir 'setup-progress.json'
17
+ $LogDir = Join-Path $StateDir 'logs'
18
+ $SetupLogPath = Join-Path $LogDir 'setup-app.log'
19
+ $LocalAiSetup = Join-Path $BinDir 'setup-litertlm.ps1'
20
+ $script:LastStage = 'welcome'
21
+ $script:SetupStarted = $false
22
+ $script:PortalUrl = 'https://trillboards.com/earner/screens'
23
+ $script:PairProcess = $null
24
+ $script:SetupMutex = $null
25
+ $StepNames = @('Welcome', 'Account', 'Recorder', 'Cameras', 'Local AI', 'Ready')
26
+ $StepLabels = New-Object System.Collections.Generic.List[System.Windows.Forms.Label]
27
+
28
+ function New-Label {
29
+ param([string]$Text, [float]$Size = 10, [System.Drawing.FontStyle]$Style = [System.Drawing.FontStyle]::Regular)
30
+ $label = New-Object System.Windows.Forms.Label
31
+ $label.Text = $Text
32
+ $label.Font = New-Object System.Drawing.Font('Segoe UI', $Size, $Style)
33
+ $label.AutoSize = $false
34
+ $label.BackColor = [System.Drawing.Color]::Transparent
35
+ return $label
36
+ }
37
+
38
+ function Get-StateValue {
39
+ param($State, [string]$Name)
40
+ if ($null -eq $State) { return $null }
41
+ $property = $State.PSObject.Properties[$Name]
42
+ if ($null -eq $property) { return $null }
43
+ return $property.Value
44
+ }
45
+
46
+ function Get-StepIndex([string]$Stage) {
47
+ switch ($Stage) {
48
+ { $_ -in @('waiting_for_account', 'account_attention') } { return 1 }
49
+ { $_ -in @('account_connected', 'discovering') } { return 2 }
50
+ 'devices_found' { return 3 }
51
+ 'validating' { return 3 }
52
+ 'cameras_created' { return 4 }
53
+ { $_ -in @('local_ai', 'local_ai_error', 'starting_service') } { return 4 }
54
+ 'ready' { return 5 }
55
+ default { return 0 }
56
+ }
57
+ }
58
+
59
+ function Get-ProgressValue([string]$Stage, $State) {
60
+ switch ($Stage) {
61
+ 'waiting_for_account' { return 18 }
62
+ 'account_attention' { return 18 }
63
+ 'account_connected' { return 30 }
64
+ 'discovering' { return 42 }
65
+ 'devices_found' { return 50 }
66
+ 'validating' { return 68 }
67
+ 'cameras_created' { return 80 }
68
+ 'local_ai' {
69
+ $localPercent = Get-StateValue $State 'localAiProgressPercent'
70
+ if ($null -ne $localPercent) {
71
+ return 84 + [Math]::Floor(([Math]::Max(0, [Math]::Min(100, [int]$localPercent))) * 0.14)
72
+ }
73
+ return 84
74
+ }
75
+ 'local_ai_error' { return 95 }
76
+ 'starting_service' { return 82 }
77
+ 'ready' { return 100 }
78
+ default { return 8 }
79
+ }
80
+ }
81
+
82
+ function Update-Steps([int]$Active, [bool]$CompleteActive = $false) {
83
+ for ($index = 0; $index -lt $StepLabels.Count; $index++) {
84
+ $label = $StepLabels[$index]
85
+ $completed = $index -lt $Active -or ($CompleteActive -and $index -eq $Active)
86
+ $prefix = if ($completed) { [string][char]0x2713 } else { [string]($index + 1) }
87
+ $label.Text = "$prefix $($StepNames[$index])"
88
+ $label.ForeColor = if ($index -le $Active) { [System.Drawing.Color]::FromArgb(30, 43, 49) } else { [System.Drawing.Color]::FromArgb(104, 119, 127) }
89
+ $style = if ($index -eq $Active) { [System.Drawing.FontStyle]::Bold } else { [System.Drawing.FontStyle]::Regular }
90
+ $label.Font = New-Object System.Drawing.Font('Segoe UI', 10, $style)
91
+ }
92
+ }
93
+
94
+ function Format-ByteCount($Value) {
95
+ if ($null -eq $Value) { return $null }
96
+ $bytes = [double]$Value
97
+ if ($bytes -ge 1GB) { return ('{0:N2} GB' -f ($bytes / 1GB)) }
98
+ if ($bytes -ge 1MB) { return ('{0:N0} MB' -f ($bytes / 1MB)) }
99
+ return ('{0:N0} KB' -f ($bytes / 1KB))
100
+ }
101
+
102
+ $Form = New-Object System.Windows.Forms.Form
103
+ $Form.Text = 'Trillboards CCTV Setup'
104
+ $Form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
105
+ $Form.ClientSize = New-Object System.Drawing.Size(760, 540)
106
+ $Form.MinimumSize = New-Object System.Drawing.Size(700, 520)
107
+ $Form.BackColor = [System.Drawing.Color]::White
108
+ $Form.MaximizeBox = $false
109
+
110
+ $Sidebar = New-Object System.Windows.Forms.Panel
111
+ $Sidebar.Dock = [System.Windows.Forms.DockStyle]::Left
112
+ $Sidebar.Width = 190
113
+ $Sidebar.BackColor = [System.Drawing.Color]::FromArgb(238, 243, 245)
114
+ $Form.Controls.Add($Sidebar)
115
+
116
+ $Brand = New-Label -Text 'Trillboards' -Size 12 -Style ([System.Drawing.FontStyle]::Bold)
117
+ $Brand.Location = New-Object System.Drawing.Point(24, 28)
118
+ $Brand.Size = New-Object System.Drawing.Size(145, 28)
119
+ $Sidebar.Controls.Add($Brand)
120
+
121
+ for ($index = 0; $index -lt $StepNames.Count; $index++) {
122
+ $step = New-Label -Text "$($index + 1) $($StepNames[$index])" -Size 10
123
+ $step.Location = New-Object System.Drawing.Point(24, (92 + ($index * 48)))
124
+ $step.Size = New-Object System.Drawing.Size(150, 28)
125
+ $Sidebar.Controls.Add($step)
126
+ $StepLabels.Add($step)
127
+ }
128
+
129
+ $Eyebrow = New-Label -Text 'CUSTOMER SETUP' -Size 9 -Style ([System.Drawing.FontStyle]::Bold)
130
+ $Eyebrow.ForeColor = [System.Drawing.Color]::FromArgb(0, 112, 153)
131
+ $Eyebrow.Location = New-Object System.Drawing.Point(236, 48)
132
+ $Eyebrow.Size = New-Object System.Drawing.Size(450, 22)
133
+ $Form.Controls.Add($Eyebrow)
134
+
135
+ $Headline = New-Label -Text '' -Size 21 -Style ([System.Drawing.FontStyle]::Bold)
136
+ $Headline.Location = New-Object System.Drawing.Point(232, 76)
137
+ $Headline.Size = New-Object System.Drawing.Size(475, 70)
138
+ $Form.Controls.Add($Headline)
139
+
140
+ $Detail = New-Label -Text '' -Size 11
141
+ $Detail.ForeColor = [System.Drawing.Color]::FromArgb(96, 113, 122)
142
+ $Detail.Location = New-Object System.Drawing.Point(236, 150)
143
+ $Detail.Size = New-Object System.Drawing.Size(465, 74)
144
+ $Form.Controls.Add($Detail)
145
+
146
+ $StatusBox = New-Object System.Windows.Forms.Panel
147
+ $StatusBox.Location = New-Object System.Drawing.Point(236, 242)
148
+ $StatusBox.Size = New-Object System.Drawing.Size(465, 70)
149
+ $StatusBox.BackColor = [System.Drawing.Color]::FromArgb(245, 248, 249)
150
+ $StatusBox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
151
+ $Form.Controls.Add($StatusBox)
152
+
153
+ $StatusValue = New-Label -Text '' -Size 17 -Style ([System.Drawing.FontStyle]::Bold)
154
+ $StatusValue.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
155
+ $StatusValue.Dock = [System.Windows.Forms.DockStyle]::Fill
156
+ $StatusBox.Controls.Add($StatusValue)
157
+
158
+ $Metric = New-Label -Text '' -Size 9
159
+ $Metric.ForeColor = [System.Drawing.Color]::FromArgb(96, 113, 122)
160
+ $Metric.Location = New-Object System.Drawing.Point(236, 330)
161
+ $Metric.Size = New-Object System.Drawing.Size(465, 24)
162
+ $Form.Controls.Add($Metric)
163
+
164
+ $Progress = New-Object System.Windows.Forms.ProgressBar
165
+ $Progress.Location = New-Object System.Drawing.Point(236, 364)
166
+ $Progress.Size = New-Object System.Drawing.Size(465, 10)
167
+ $Progress.Minimum = 0
168
+ $Progress.Maximum = 100
169
+ $Form.Controls.Add($Progress)
170
+
171
+ $Privacy = New-Label -Text 'Camera credentials stay on this computer. Cloud inference is off by default.' -Size 8.5
172
+ $Privacy.ForeColor = [System.Drawing.Color]::FromArgb(96, 113, 122)
173
+ $Privacy.Location = New-Object System.Drawing.Point(236, 468)
174
+ $Privacy.Size = New-Object System.Drawing.Size(300, 40)
175
+ $Form.Controls.Add($Privacy)
176
+
177
+ $Action = New-Object System.Windows.Forms.Button
178
+ $Action.Text = 'Start setup'
179
+ $Action.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold)
180
+ $Action.Location = New-Object System.Drawing.Point(562, 466)
181
+ $Action.Size = New-Object System.Drawing.Size(139, 42)
182
+ $Action.BackColor = [System.Drawing.Color]::FromArgb(0, 127, 174)
183
+ $Action.ForeColor = [System.Drawing.Color]::White
184
+ $Action.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
185
+ $Action.FlatAppearance.BorderSize = 0
186
+ $Form.Controls.Add($Action)
187
+
188
+ function Show-Welcome {
189
+ $script:LastStage = 'welcome'
190
+ $Headline.Text = 'Set up this computer for CCTV'
191
+ $Detail.Text = 'Connect your Trillboards account, find your recorder, and verify every camera before screens are created.'
192
+ $StatusValue.Text = 'About 5 minutes'
193
+ $Metric.Text = 'Keep this computer and the recorder on the same network.'
194
+ $Progress.Value = 0
195
+ $Action.Text = 'Start setup'
196
+ $Action.Enabled = $true
197
+ Update-Steps 0
198
+ }
199
+
200
+ function Show-LocalError([string]$Title, [string]$Message) {
201
+ $script:LastStage = 'error'
202
+ $Headline.Text = $Title
203
+ $Detail.Text = $Message
204
+ $StatusValue.Text = 'Setup stopped'
205
+ $Metric.Text = 'Details are saved in logs\setup-app.log.'
206
+ $Progress.Value = 0
207
+ $Action.Text = 'Try again'
208
+ $Action.Enabled = $true
209
+ }
210
+
211
+ function Start-Setup {
212
+ if (-not (Test-Path $Pairer)) {
213
+ Show-LocalError 'Setup app is incomplete' 'Reinstall the Trillboards CCTV connector, then try again.'
214
+ return
215
+ }
216
+ Stop-SetupProcess
217
+ New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
218
+ Remove-Item $StatePath -Force -ErrorAction SilentlyContinue
219
+ Add-Content -Path $SetupLogPath -Value "`r`n[$([DateTime]::Now.ToString('o'))] Starting Trillboards CCTV Setup"
220
+ $Headline.Text = 'Starting camera setup'
221
+ $Detail.Text = 'Connecting securely to Trillboards.'
222
+ $StatusValue.Text = 'Please wait'
223
+ $Metric.Text = 'No Terminal or command window is required.'
224
+ $Progress.Value = 8
225
+ $Action.Enabled = $false
226
+ $script:LastStage = 'starting'
227
+ $script:SetupStarted = $true
228
+
229
+ $startInfo = New-Object System.Diagnostics.ProcessStartInfo
230
+ $startInfo.FileName = $env:ComSpec
231
+ $startInfo.Arguments = ('/d /s /c ""{0}" --setup --status-file "{1}" >> "{2}" 2>&1"' -f $Pairer, $StatePath, $SetupLogPath)
232
+ $startInfo.UseShellExecute = $false
233
+ $startInfo.CreateNoWindow = $true
234
+ $startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
235
+ try {
236
+ $script:PairProcess = [System.Diagnostics.Process]::Start($startInfo)
237
+ } catch {
238
+ Show-LocalError 'Could not start setup' 'Reinstall the Trillboards CCTV connector, then try again.'
239
+ }
240
+ }
241
+
242
+ function Start-LocalAiSetup {
243
+ if (-not (Test-Path $LocalAiSetup)) {
244
+ Show-LocalError 'Local AI setup is incomplete' 'Reinstall the Trillboards CCTV connector, then try again.'
245
+ return
246
+ }
247
+ $Headline.Text = 'Preparing local AI'
248
+ $Detail.Text = 'Restarting the local AI installed with Trillboards. Your paired cameras will not be recreated.'
249
+ $StatusValue.Text = 'Starting local AI'
250
+ $Metric.Text = 'Cloud inference remains off.'
251
+ $Progress.Value = 82
252
+ $Action.Text = 'Working...'
253
+ $Action.Enabled = $false
254
+ $script:LastStage = 'local_ai'
255
+
256
+ $startInfo = New-Object System.Diagnostics.ProcessStartInfo
257
+ $startInfo.FileName = 'powershell.exe'
258
+ $startInfo.Arguments = ('-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "{0}" -RequireBundledModel -ProgressPath "{1}"' -f $LocalAiSetup, $StatePath)
259
+ $startInfo.UseShellExecute = $false
260
+ $startInfo.CreateNoWindow = $true
261
+ $startInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
262
+ try {
263
+ [void][System.Diagnostics.Process]::Start($startInfo)
264
+ } catch {
265
+ Show-LocalError 'Could not start local AI' 'Reinstall the Trillboards CCTV connector, then try again.'
266
+ }
267
+ }
268
+
269
+ function Stop-SetupProcess {
270
+ $process = $script:PairProcess
271
+ $script:PairProcess = $null
272
+ if ($null -eq $process) { return }
273
+ try {
274
+ if (-not $process.HasExited) {
275
+ $taskkill = Join-Path $env:SystemRoot 'System32\taskkill.exe'
276
+ & $taskkill /PID $process.Id /T /F | Out-Null
277
+ [void]$process.WaitForExit(5000)
278
+ }
279
+ } catch {
280
+ try {
281
+ if (-not $process.HasExited) { $process.Kill() }
282
+ } catch {
283
+ # The process may have exited between checks.
284
+ }
285
+ } finally {
286
+ $process.Dispose()
287
+ }
288
+ }
289
+
290
+ function Render-State($State) {
291
+ $stage = [string](Get-StateValue $State 'stage')
292
+ if (-not $stage) { return }
293
+ $script:LastStage = $stage
294
+ $url = Get-StateValue $State 'portalUrl'
295
+ if ($url) { $script:PortalUrl = [string]$url }
296
+ $Headline.Text = [string](Get-StateValue $State 'title')
297
+ $Detail.Text = [string](Get-StateValue $State 'message')
298
+ $cameraCount = Get-StateValue $State 'cameraCount'
299
+ $validated = Get-StateValue $State 'validatedFeeds'
300
+ $devices = Get-StateValue $State 'devicesFound'
301
+ $rejected = Get-StateValue $State 'rejectedFeeds'
302
+ $localAiPhase = [string](Get-StateValue $State 'localAiPhase')
303
+ $localAiPercent = Get-StateValue $State 'localAiProgressPercent'
304
+ $localAiDownloaded = Get-StateValue $State 'localAiDownloadedBytes'
305
+ $localAiTotal = Get-StateValue $State 'localAiTotalBytes'
306
+
307
+ if ($stage -in @('waiting_for_account', 'account_attention')) {
308
+ $StatusValue.Text = [string](Get-StateValue $State 'displayCode')
309
+ } elseif ($stage -eq 'ready') {
310
+ $StatusValue.Text = 'Setup complete'
311
+ } elseif ($stage -eq 'local_ai_error') {
312
+ $StatusValue.Text = 'Local AI needs attention'
313
+ } elseif ($stage -eq 'starting_service') {
314
+ $StatusValue.Text = 'Starting Trillboards'
315
+ } elseif ($stage -eq 'local_ai') {
316
+ switch ($localAiPhase) {
317
+ 'installing_model' {
318
+ $StatusValue.Text = if ($null -ne $localAiPercent) { "Installing Gemma - $localAiPercent%" } else { 'Installing Gemma' }
319
+ }
320
+ 'starting_service' { $StatusValue.Text = 'Starting local AI' }
321
+ 'verifying' { $StatusValue.Text = 'Checking local AI' }
322
+ default { $StatusValue.Text = 'Installing local AI' }
323
+ }
324
+ } elseif ($cameraCount -and [int]$cameraCount -gt 0) {
325
+ $StatusValue.Text = "$cameraCount camera$(if ([int]$cameraCount -eq 1) { '' } else { 's' })"
326
+ } elseif ($validated -and [int]$validated -gt 0) {
327
+ $StatusValue.Text = "$validated feed$(if ([int]$validated -eq 1) { '' } else { 's' }) ready"
328
+ } elseif ($null -ne $devices) {
329
+ $StatusValue.Text = "$devices device$(if ([int]$devices -eq 1) { '' } else { 's' }) found"
330
+ } elseif ($stage -eq 'error') {
331
+ $StatusValue.Text = 'Setup stopped'
332
+ } else {
333
+ $StatusValue.Text = 'In progress'
334
+ }
335
+
336
+ if ($stage -eq 'local_ai' -and $localAiPhase -eq 'installing_model' -and $null -ne $localAiTotal) {
337
+ $totalText = Format-ByteCount $localAiTotal
338
+ $Metric.Text = if ($totalText) { "Installing the $totalText model included with Trillboards. Cloud inference remains off." } else { 'Installing the included model. Cloud inference remains off.' }
339
+ } elseif ($stage -eq 'local_ai_error') {
340
+ $Metric.Text = 'Camera pairing is saved. Retry only the local AI step.'
341
+ } elseif ($rejected -and [int]$rejected -gt 0) {
342
+ $Metric.Text = "$rejected black or unavailable input$(if ([int]$rejected -eq 1) { ' was' } else { 's were' }) ignored."
343
+ } elseif ($stage -eq 'account_attention') {
344
+ $Metric.Text = 'No cameras were added. It is safe to switch accounts.'
345
+ } elseif ($stage -eq 'local_ai') {
346
+ $Metric.Text = 'The included model stays on this computer; cloud inference remains off.'
347
+ } elseif ($stage -eq 'ready') {
348
+ $Metric.Text = 'Trillboards starts automatically when this computer logs in.'
349
+ } elseif ($stage -eq 'error') {
350
+ $Metric.Text = 'Details are saved in logs\setup-app.log.'
351
+ } else {
352
+ $Metric.Text = 'Camera credentials stay encrypted on this computer.'
353
+ }
354
+
355
+ $Progress.Value = [Math]::Max(0, [Math]::Min(100, (Get-ProgressValue $stage $State)))
356
+ Update-Steps (Get-StepIndex $stage) ($stage -eq 'ready')
357
+ switch ($stage) {
358
+ 'waiting_for_account' { $Action.Text = 'Open Trillboards'; $Action.Enabled = $true }
359
+ 'account_attention' { $Action.Text = 'Switch account'; $Action.Enabled = $true }
360
+ 'local_ai_error' { $Action.Text = 'Retry local AI'; $Action.Enabled = $true }
361
+ 'ready' { $Action.Text = 'View cameras'; $Action.Enabled = $true }
362
+ 'error' { $Action.Text = 'Try again'; $Action.Enabled = $true }
363
+ default { $Action.Text = 'Working...'; $Action.Enabled = $false }
364
+ }
365
+ }
366
+
367
+ $Action.Add_Click({
368
+ switch ($script:LastStage) {
369
+ 'waiting_for_account' { Start-Process $script:PortalUrl }
370
+ 'account_attention' { Start-Process $script:PortalUrl }
371
+ 'local_ai_error' { Start-LocalAiSetup }
372
+ 'ready' {
373
+ Start-Process 'https://trillboards.com/earner/screens'
374
+ $Form.Close()
375
+ }
376
+ 'error' { Start-Setup }
377
+ 'welcome' { Start-Setup }
378
+ }
379
+ })
380
+
381
+ $Form.Add_FormClosing({
382
+ Stop-SetupProcess
383
+ if ($null -ne $script:SetupMutex) {
384
+ try { $script:SetupMutex.ReleaseMutex() } catch { }
385
+ $script:SetupMutex.Dispose()
386
+ $script:SetupMutex = $null
387
+ }
388
+ })
389
+
390
+ $Timer = New-Object System.Windows.Forms.Timer
391
+ $Timer.Interval = 500
392
+ $Timer.Add_Tick({
393
+ if ($script:SetupStarted -and (Test-Path $StatePath)) {
394
+ try {
395
+ $state = Get-Content -Raw -Path $StatePath | ConvertFrom-Json
396
+ Render-State $state
397
+ } catch {
398
+ # Atomic writer means this is uncommon; retry on the next tick.
399
+ }
400
+ }
401
+ if ($null -ne $script:PairProcess -and $script:PairProcess.HasExited) {
402
+ $exitCode = $script:PairProcess.ExitCode
403
+ $script:PairProcess.Dispose()
404
+ $script:PairProcess = $null
405
+ if ($exitCode -ne 0 -and $script:LastStage -notin @('local_ai', 'local_ai_error', 'ready', 'error')) {
406
+ Show-LocalError 'Setup stopped before finishing' "The camera setup helper exited with code $exitCode. Try again; if it repeats, send setup-app.log to Trillboards support."
407
+ }
408
+ }
409
+ })
410
+ Show-Welcome
411
+ if ($SmokeTest) {
412
+ if ($StepLabels.Count -ne 6 -or $Form.Controls.Count -lt 8 -or $Action.Text -ne 'Start setup') {
413
+ throw 'Windows setup UI did not construct its expected controls.'
414
+ }
415
+ $smokeStartInfo = New-Object System.Diagnostics.ProcessStartInfo
416
+ $smokeStartInfo.FileName = $env:ComSpec
417
+ $smokeStartInfo.Arguments = '/d /s /c "ping -n 120 127.0.0.1 >nul"'
418
+ $smokeStartInfo.UseShellExecute = $false
419
+ $smokeStartInfo.CreateNoWindow = $true
420
+ $script:PairProcess = [System.Diagnostics.Process]::Start($smokeStartInfo)
421
+ $smokeParentId = $script:PairProcess.Id
422
+ $smokeChildIds = @()
423
+ for ($attempt = 0; $attempt -lt 20 -and $smokeChildIds.Count -eq 0; $attempt++) {
424
+ Start-Sleep -Milliseconds 100
425
+ $smokeChildIds = @(Get-CimInstance Win32_Process -Filter "ParentProcessId = $smokeParentId" | Select-Object -ExpandProperty ProcessId)
426
+ }
427
+ Stop-SetupProcess
428
+ if ($smokeChildIds.Count -eq 0) {
429
+ throw 'Windows setup cancellation smoke test did not create its child process.'
430
+ }
431
+ $trackedIds = @($smokeParentId) + $smokeChildIds
432
+ $leftRunning = @($trackedIds)
433
+ for ($attempt = 0; $attempt -lt 20 -and $leftRunning.Count -gt 0; $attempt++) {
434
+ $leftRunning = @($trackedIds | Where-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue })
435
+ if ($leftRunning.Count -gt 0) { Start-Sleep -Milliseconds 100 }
436
+ }
437
+ if ($leftRunning.Count -gt 0) {
438
+ throw "Windows setup cancellation left process IDs running: $($leftRunning -join ', ')"
439
+ }
440
+ Write-Host 'Trillboards CCTV Setup Windows Forms smoke test passed.'
441
+ $Form.Dispose()
442
+ exit 0
443
+ }
444
+
445
+ $createdNew = $false
446
+ $script:SetupMutex = New-Object System.Threading.Mutex($true, 'Local\TrillboardsCctvSetup', [ref]$createdNew)
447
+ if (-not $createdNew) {
448
+ $script:SetupMutex.Dispose()
449
+ $script:SetupMutex = $null
450
+ $Form.Dispose()
451
+ exit 0
452
+ }
453
+
454
+ $Timer.Start()
455
+ [void]$Form.ShowDialog()
456
+ $Timer.Stop()
@@ -0,0 +1,13 @@
1
+ Option Explicit
2
+
3
+ Dim fso, shell, scriptDir, powerShellScript, command
4
+ Set fso = CreateObject("Scripting.FileSystemObject")
5
+ Set shell = CreateObject("WScript.Shell")
6
+
7
+ scriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
8
+ powerShellScript = fso.BuildPath(scriptDir, "trillboards-setup.ps1")
9
+ command = "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & powerShellScript & """"
10
+
11
+ ' Window style 0 prevents a console flash; the PowerShell process displays only
12
+ ' the Windows Forms customer setup application.
13
+ shell.Run command, 0, False
@@ -0,0 +1,136 @@
1
+ /**
2
+ * AudienceAnalyzeClient — POSTs a JPEG frame to the cloud VLM endpoint
3
+ * `POST {apiBaseUrl}/v2/earner/audience-analyze`.
4
+ *
5
+ * This is the ONE genuinely-new piece for cloud sensing: it replaces the
6
+ * edge-cloud Gemini-direct path. The cloud VLM does the inference; the edge
7
+ * only grabs a frame and ships it.
8
+ *
9
+ * Wire contract mirrors the Android agent-core `FrameCaptureManager.sendToApi`:
10
+ * - body: JSON `{ fingerprint?, screenId?, imageBase64, capturedAt, ... }`
11
+ * - headers required by `middleware/deviceRequestAuth.js`:
12
+ * X-Device-Token — the per-screen device token
13
+ * X-Trillboard-Device-Token — alias header (back-compat)
14
+ * X-Device-Timestamp — epoch ms (replay skew window)
15
+ * X-Device-Nonce — unique per request (replay guard)
16
+ *
17
+ * Uses Node's built-in http/https (no extra dependency) so it works inside a
18
+ * self-contained binary and is trivially mockable in tests.
19
+ */
20
+ export interface AudienceAnalyzeClientOptions {
21
+ /** Base URL, e.g. https://api.trillboards.com (no trailing slash needed). */
22
+ apiBaseUrl: string;
23
+ /** Device token for X-Device-Token (one feed = one token). */
24
+ deviceToken: string;
25
+ /** Screen this client posts for (one feed = one screen). Optional. */
26
+ screenId?: string;
27
+ /** Device fingerprint, sent in the body for legacy correlation. Optional. */
28
+ fingerprint?: string;
29
+ /** Request timeout in ms. Default: 30000. */
30
+ timeoutMs?: number;
31
+ /** Image width sent in the body metadata. Optional. */
32
+ imageWidth?: number;
33
+ /** Image height sent in the body metadata. Optional. */
34
+ imageHeight?: number;
35
+ }
36
+ export interface AudienceAnalyzeRequest {
37
+ /** Raw JPEG bytes for this frame. */
38
+ jpeg: Buffer;
39
+ /** Capture timestamp (epoch ms). Default: Date.now(). */
40
+ capturedAt?: number;
41
+ /** Capture mode hint forwarded to the cloud. Optional. */
42
+ captureMode?: string;
43
+ /** Extra body fields merged into the payload (e.g. per_face_observations). */
44
+ extra?: Record<string, unknown>;
45
+ }
46
+ export interface AudienceAnalyzeResponse {
47
+ /** HTTP status code. */
48
+ statusCode: number;
49
+ /** Whether the POST succeeded (2xx). */
50
+ ok: boolean;
51
+ /** Parsed JSON body (or null if the body was not valid JSON). */
52
+ body: Record<string, unknown> | null;
53
+ /** Raw response text. */
54
+ rawBody: string;
55
+ }
56
+ export type CameraHealthState = 'never_started' | 'starting' | 'open' | 'failed' | 'closed';
57
+ export type CameraHealthFailureClass = 'rtsp_auth' | 'rtsp_unreachable' | 'rtsp_timeout' | 'nvr_busy' | 'no_frames' | 'decode_error';
58
+ export type CameraSensingState = 'idle' | 'analyzing' | 'active' | 'waiting_for_capacity' | 'unavailable';
59
+ export interface CameraHealthReport {
60
+ state: CameraHealthState;
61
+ lastFrameAtMs?: number;
62
+ retryCount?: number;
63
+ lastFailureClass?: CameraHealthFailureClass;
64
+ lastFailureMessage?: string;
65
+ /** AI/inference state, deliberately separate from RTSP/video health. */
66
+ sensingState?: CameraSensingState;
67
+ /** Authenticated attestation that the connector decoded a local frame. */
68
+ captureProof?: 'decoded_frame';
69
+ }
70
+ /** Minimal injectable HTTP transport — lets tests stub the network entirely. */
71
+ export interface HttpPoster {
72
+ (url: string, options: {
73
+ method: string;
74
+ headers: Record<string, string>;
75
+ timeoutMs: number;
76
+ }, body: string): Promise<{
77
+ statusCode: number;
78
+ body: string;
79
+ }>;
80
+ }
81
+ export declare class AudienceAnalyzeClient {
82
+ private readonly endpoint;
83
+ private readonly deviceToken;
84
+ private readonly screenId?;
85
+ private readonly fingerprint?;
86
+ private readonly timeoutMs;
87
+ private readonly imageWidth?;
88
+ private readonly imageHeight?;
89
+ private readonly poster;
90
+ constructor(options: AudienceAnalyzeClientOptions, poster?: HttpPoster);
91
+ /** The resolved POST URL (exposed for assertions / logging). */
92
+ getEndpoint(): string;
93
+ private authHeaders;
94
+ /**
95
+ * POST a JPEG frame for cloud audience analysis. Returns the parsed response.
96
+ */
97
+ analyze(request: AudienceAnalyzeRequest): Promise<AudienceAnalyzeResponse>;
98
+ /**
99
+ * POST a PRE-ANALYZED structured result (computed ON-DEVICE by
100
+ * LiteRtVlmProvider) to the same audience-analyze endpoint. The server detects
101
+ * `preAnalyzed`, SKIPS the cloud VLM, and ingests the result directly —
102
+ * forcing provenance `local_connector` server-side so it drives the
103
+ * operator's Sense-Anything routes but is gated out of the sold bidstream.
104
+ * Same device-token auth + replay guard as analyze().
105
+ *
106
+ * EVIDENCE FRAME (optional): when `jpeg` (raw bytes) or `imageBase64` (already
107
+ * base64) is supplied, the SAME JPEG the on-device VLM analyzed is included as
108
+ * `imageBase64` ALONGSIDE `preAnalyzed`. The server uploads it and attaches it
109
+ * to any alert the result fires. The caller decides WHETHER to attach it — the
110
+ * connector only sends a frame for alert-worthy observations (see
111
+ * localSensing.ts), so this method just forwards what it's given. When neither
112
+ * is passed, NO `imageBase64` is sent (results-only, privacy-preserving
113
+ * default for empty/uninteresting scenes).
114
+ */
115
+ analyzePreAnalyzed(preAnalyzed: Record<string, unknown>, request?: {
116
+ capturedAt?: number;
117
+ captureMode?: string;
118
+ extra?: Record<string, unknown>;
119
+ /** Raw JPEG bytes of the analyzed frame — encoded to base64 and sent as `imageBase64`. */
120
+ jpeg?: Buffer;
121
+ /** Pre-encoded base64 JPEG of the analyzed frame (alternative to `jpeg`). */
122
+ imageBase64?: string;
123
+ }): Promise<AudienceAnalyzeResponse>;
124
+ /**
125
+ * POST a FRAMELESS camera-health beacon. This is used when the connector can
126
+ * pair but cannot open the RTSP stream, so the portal can show "Camera login
127
+ * failed" / "not reachable" instead of looking like the connector is absent.
128
+ */
129
+ reportCameraHealth(cameraHealth: CameraHealthReport): Promise<AudienceAnalyzeResponse>;
130
+ }
131
+ /**
132
+ * Default transport using Node's built-in http/https. No third-party deps so it
133
+ * survives bundling into a single self-contained binary.
134
+ */
135
+ export declare const defaultHttpPoster: HttpPoster;
136
+ //# sourceMappingURL=AudienceAnalyzeClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudienceAnalyzeClient.d.ts","sourceRoot":"","sources":["../src/AudienceAnalyzeClient.ts"],"names":[],"mappings":"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"}