lightman-agent 1.0.0

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 (54) hide show
  1. package/agent.config.template.json +30 -0
  2. package/bin/cms-agent.js +233 -0
  3. package/nssm/nssm.exe +0 -0
  4. package/package.json +52 -0
  5. package/public/assets/index-CcBNCz6h.css +1 -0
  6. package/public/assets/index-H-8HDl46.js +1 -0
  7. package/public/index.html +19 -0
  8. package/scripts/guardian.ps1 +75 -0
  9. package/scripts/install-linux.sh +134 -0
  10. package/scripts/install-rpi.sh +117 -0
  11. package/scripts/install-windows.ps1 +529 -0
  12. package/scripts/launch-kiosk.vbs +101 -0
  13. package/scripts/lightman-agent.logrotate +12 -0
  14. package/scripts/lightman-agent.service +38 -0
  15. package/scripts/lightman-shell.bat +128 -0
  16. package/scripts/reinstall-windows.ps1 +26 -0
  17. package/scripts/restore-desktop.ps1 +32 -0
  18. package/scripts/setup.ps1 +116 -0
  19. package/scripts/setup.sh +115 -0
  20. package/scripts/uninstall-linux.sh +50 -0
  21. package/scripts/uninstall-windows.ps1 +54 -0
  22. package/src/commands/display.ts +177 -0
  23. package/src/commands/kiosk.ts +113 -0
  24. package/src/commands/maintenance.ts +106 -0
  25. package/src/commands/network.ts +129 -0
  26. package/src/commands/power.ts +163 -0
  27. package/src/commands/rpi.ts +45 -0
  28. package/src/commands/screenshot.ts +166 -0
  29. package/src/commands/serial.ts +17 -0
  30. package/src/commands/update.ts +124 -0
  31. package/src/index.ts +652 -0
  32. package/src/lib/config.ts +69 -0
  33. package/src/lib/identity.ts +40 -0
  34. package/src/lib/logger.ts +137 -0
  35. package/src/lib/platform.ts +10 -0
  36. package/src/lib/rpi.ts +180 -0
  37. package/src/lib/screens.ts +128 -0
  38. package/src/lib/types.ts +176 -0
  39. package/src/services/commands.ts +107 -0
  40. package/src/services/health.ts +161 -0
  41. package/src/services/kiosk.ts +395 -0
  42. package/src/services/localEvents.ts +60 -0
  43. package/src/services/logForwarder.ts +72 -0
  44. package/src/services/multiScreenKiosk.ts +324 -0
  45. package/src/services/oscBridge.ts +186 -0
  46. package/src/services/powerScheduler.ts +260 -0
  47. package/src/services/provisioning.ts +120 -0
  48. package/src/services/serialBridge.ts +230 -0
  49. package/src/services/serviceLauncher.ts +183 -0
  50. package/src/services/staticServer.ts +226 -0
  51. package/src/services/updater.ts +249 -0
  52. package/src/services/watchdog.ts +310 -0
  53. package/src/services/websocket.ts +152 -0
  54. package/tsconfig.json +28 -0
@@ -0,0 +1,529 @@
1
+ # LIGHTMAN Agent - Complete Windows Installer
2
+ # Uses NSSM for rock-solid Windows Service. Shell replacement for kiosk.
3
+ # Cleans up any previous installation automatically before installing.
4
+ #
5
+ # Run as Administrator:
6
+ # powershell -ExecutionPolicy Bypass -File install-windows.ps1 -Slug "F-AV01" -Server "http://192.168.1.180:3401"
7
+ #
8
+ # Shell Replacement mode (RECOMMENDED for kiosk machines):
9
+ # powershell -ExecutionPolicy Bypass -File install-windows.ps1 -Slug "F-AV01" -Server "http://..." -ShellReplace
10
+ #Requires -RunAsAdministrator
11
+
12
+ param(
13
+ [Parameter(Mandatory=$true)] [string]$Slug,
14
+ [Parameter(Mandatory=$true)] [string]$Server,
15
+ [string]$Timezone = "Asia/Kolkata",
16
+ [string]$Username = "",
17
+ [switch]$ShellReplace = $false
18
+ )
19
+
20
+ $ErrorActionPreference = "Stop"
21
+
22
+ $InstallDir = "C:\Program Files\Lightman\Agent"
23
+ $LogDir = "C:\ProgramData\Lightman\logs"
24
+ $ChromeData = "C:\ProgramData\Lightman\chrome-kiosk"
25
+ $NssmDir = "C:\ProgramData\Lightman\nssm"
26
+ $NssmExe = "$NssmDir\nssm.exe"
27
+ $ServiceName = "LightmanAgent"
28
+ $GuardianTask = "LIGHTMAN Guardian"
29
+ $KioskTask = "LIGHTMAN Kiosk Browser"
30
+ $AgentTask = "LIGHTMAN Agent"
31
+ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
32
+ $AgentDir = Split-Path -Parent $ScriptDir
33
+
34
+ if (-not $Username) { $Username = $env:USERNAME }
35
+
36
+ Write-Host ""
37
+ Write-Host "=============================================" -ForegroundColor Cyan
38
+ Write-Host " LIGHTMAN Agent - Complete Windows Installer" -ForegroundColor Cyan
39
+ Write-Host "=============================================" -ForegroundColor Cyan
40
+ Write-Host " Device slug : $Slug"
41
+ Write-Host " Server URL : $Server"
42
+ Write-Host " Username : $Username"
43
+ Write-Host " Mode : $(if ($ShellReplace) { 'Shell Replacement' } else { 'Standard' })"
44
+ Write-Host ""
45
+
46
+ # ============================================================
47
+ # PHASE 0: NUKE EVERYTHING FROM PREVIOUS INSTALLS
48
+ # ============================================================
49
+ Write-Host "--- Phase 0: Cleaning previous installation ---" -ForegroundColor Cyan
50
+ $ErrorActionPreference = "Continue"
51
+
52
+ # Stop and remove NSSM service
53
+ Write-Host "[0a] Removing old services..." -ForegroundColor Yellow
54
+ if (Test-Path $NssmExe) {
55
+ & $NssmExe stop $ServiceName 2>$null
56
+ & $NssmExe remove $ServiceName confirm 2>$null
57
+ }
58
+ foreach ($sn in @($ServiceName, "lightmanagent.exe", "LightmanAgent.exe")) {
59
+ sc.exe stop $sn 2>$null; sc.exe delete $sn 2>$null
60
+ }
61
+ $oldSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue
62
+ if ($oldSvc) { Stop-Service -Name $oldSvc.Name -Force -ErrorAction SilentlyContinue; sc.exe delete $oldSvc.Name 2>$null }
63
+
64
+ # Remove scheduled tasks (from previous task-scheduler-based installs)
65
+ Write-Host "[0b] Removing old scheduled tasks..." -ForegroundColor Yellow
66
+ foreach ($tn in @($AgentTask, $KioskTask, $GuardianTask)) {
67
+ $t = Get-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue
68
+ if ($t) { Stop-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue; Unregister-ScheduledTask -TaskName $tn -Confirm:$false -ErrorAction SilentlyContinue }
69
+ }
70
+
71
+ # Kill processes
72
+ Write-Host "[0c] Killing node.exe and Chrome..." -ForegroundColor Yellow
73
+ Get-Process -Name "node" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
74
+ Get-Process -Name "chrome" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
75
+ Start-Sleep -Seconds 2
76
+
77
+ # Remove old files (keep NSSM and logs)
78
+ Write-Host "[0d] Removing old agent files..." -ForegroundColor Yellow
79
+ Remove-Item -Path $InstallDir -Recurse -Force -ErrorAction SilentlyContinue
80
+ Remove-Item -Path "C:\ProgramData\Lightman\kiosk-url.txt" -Force -ErrorAction SilentlyContinue
81
+
82
+ # Remove firewall rule
83
+ Remove-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue
84
+
85
+ $ErrorActionPreference = "Stop"
86
+ Start-Sleep -Seconds 2
87
+ Write-Host " Clean slate" -ForegroundColor Green
88
+ Write-Host ""
89
+
90
+ # ============================================================
91
+ # PART 1: BUILD & INSTALL
92
+ # ============================================================
93
+
94
+ # --- 1. Node.js ---
95
+ Write-Host "[1/19] Checking Node.js..." -ForegroundColor Yellow
96
+ try {
97
+ $nodeVersion = (node -v) -replace 'v', ''
98
+ if ([int]($nodeVersion.Split('.')[0]) -lt 20) { throw "old" }
99
+ Write-Host " Found Node.js v$nodeVersion"
100
+ } catch {
101
+ Write-Host " Installing Node.js v20.18.0..." -ForegroundColor Yellow
102
+ $installer = "$env:TEMP\node-setup.msi"
103
+ Invoke-WebRequest -Uri "https://nodejs.org/dist/v20.18.0/node-v20.18.0-x64.msi" -OutFile $installer -UseBasicParsing
104
+ Start-Process msiexec.exe -ArgumentList "/i `"$installer`" /qn /norestart" -Wait -NoNewWindow
105
+ Remove-Item $installer -Force -ErrorAction SilentlyContinue
106
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
107
+ if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Host " FATAL: Node.js install failed!" -ForegroundColor Red; exit 1 }
108
+ Write-Host " Node.js installed" -ForegroundColor Green
109
+ }
110
+
111
+ # --- 2. Build ---
112
+ Write-Host "[2/19] Building agent..." -ForegroundColor Yellow
113
+ Push-Location $AgentDir
114
+ $ErrorActionPreference = "Continue"
115
+ & npm install 2>&1 | Out-Host
116
+ & npm run build 2>&1 | Out-Host
117
+ $ErrorActionPreference = "Stop"
118
+ if (-not (Test-Path "$AgentDir\dist\index.js")) { Write-Host " FATAL: Build failed!" -ForegroundColor Red; exit 1 }
119
+ Pop-Location
120
+ Write-Host " Build successful"
121
+
122
+ # --- 3. Directories ---
123
+ Write-Host "[3/19] Creating directories..." -ForegroundColor Yellow
124
+ foreach ($d in @($InstallDir, $LogDir, $ChromeData, $NssmDir)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
125
+
126
+ # --- 4. Copy files ---
127
+ Write-Host "[4/19] Copying agent files..." -ForegroundColor Yellow
128
+ Copy-Item "$AgentDir\dist" "$InstallDir\dist" -Recurse -Force
129
+ Copy-Item "$AgentDir\package.json" "$InstallDir\package.json" -Force
130
+ if (Test-Path "$AgentDir\package-lock.json") { Copy-Item "$AgentDir\package-lock.json" "$InstallDir\package-lock.json" -Force }
131
+ Copy-Item "$AgentDir\agent.config.template.json" "$InstallDir\agent.config.template.json" -Force
132
+ if (Test-Path "$AgentDir\public") { Copy-Item "$AgentDir\public" "$InstallDir\public" -Recurse -Force }
133
+
134
+ # --- 5. Install deps ---
135
+ Write-Host "[5/19] Installing dependencies..." -ForegroundColor Yellow
136
+ Push-Location $InstallDir
137
+ $ErrorActionPreference = "Continue"
138
+ & npm ci --omit=dev --ignore-scripts 2>&1 | Out-Host
139
+ if ($LASTEXITCODE -ne 0) { & npm install --omit=dev --ignore-scripts 2>&1 | Out-Host }
140
+ $ErrorActionPreference = "Stop"
141
+ Pop-Location
142
+
143
+ # --- 6. Generate config ---
144
+ Write-Host "[6/19] Generating config..." -ForegroundColor Yellow
145
+ if ($ShellReplace) {
146
+ & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir -ShellMode
147
+ } else {
148
+ & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir
149
+ }
150
+
151
+ # --- 7. Fix BOM ---
152
+ Write-Host "[7/19] Fixing config encoding..." -ForegroundColor Yellow
153
+ $configPath = Join-Path $InstallDir "agent.config.json"
154
+ if (-not (Test-Path $configPath)) { Write-Host " FATAL: config not created!" -ForegroundColor Red; exit 1 }
155
+ $raw = [System.IO.File]::ReadAllText($configPath)
156
+ [System.IO.File]::WriteAllText($configPath, $raw.TrimStart([char]0xFEFF), [System.Text.UTF8Encoding]::new($false))
157
+
158
+ # --- 8. Verify config ---
159
+ Write-Host "[8/19] Verifying config..." -ForegroundColor Yellow
160
+ Push-Location $InstallDir
161
+ $ErrorActionPreference = "Continue"
162
+ $jsonCheck = & node -e "try{const c=JSON.parse(require('fs').readFileSync('agent.config.json','utf8'));console.log('OK slug='+c.deviceSlug+' shellMode='+(c.kiosk&&c.kiosk.shellMode||false))}catch(e){console.log('FAIL: '+e.message);process.exit(1)}" 2>&1
163
+ $ErrorActionPreference = "Stop"
164
+ if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: invalid config: $jsonCheck" -ForegroundColor Red; Pop-Location; exit 1 }
165
+ Pop-Location
166
+ Write-Host " $jsonCheck"
167
+
168
+ # --- 9. Download NSSM ---
169
+ Write-Host "[9/19] Setting up NSSM..." -ForegroundColor Yellow
170
+ if (-not (Test-Path $NssmExe)) {
171
+ # Check bundled copy first (fastest, no internet needed)
172
+ $bundled = Join-Path $AgentDir "nssm\nssm.exe"
173
+ if (Test-Path $bundled) {
174
+ Copy-Item $bundled $NssmExe -Force
175
+ Write-Host " Using bundled NSSM"
176
+ } else {
177
+ # Download from multiple sources
178
+ $nssmZip = "$env:TEMP\nssm.zip"
179
+ $downloaded = $false
180
+ $urls = @(
181
+ "https://nssm.cc/release/nssm-2.24.zip",
182
+ "https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip"
183
+ )
184
+ foreach ($url in $urls) {
185
+ if ($downloaded) { break }
186
+ Write-Host " Downloading from $url ..."
187
+ try {
188
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
189
+ Invoke-WebRequest -Uri $url -OutFile $nssmZip -UseBasicParsing -TimeoutSec 60
190
+ if ((Test-Path $nssmZip) -and (Get-Item $nssmZip).Length -gt 10000) {
191
+ $downloaded = $true
192
+ }
193
+ } catch {
194
+ Write-Host " Failed: $_" -ForegroundColor DarkYellow
195
+ }
196
+ }
197
+ if ($downloaded) {
198
+ Expand-Archive -Path $nssmZip -DestinationPath "$env:TEMP\nssm-extract" -Force
199
+ # Find nssm.exe in extracted folder (handles different zip structures)
200
+ $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Where-Object { $_.DirectoryName -like "*win64*" } | Select-Object -First 1
201
+ if (-not $found) { $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Select-Object -First 1 }
202
+ if ($found) { Copy-Item $found.FullName $NssmExe -Force }
203
+ Remove-Item $nssmZip -Force -ErrorAction SilentlyContinue
204
+ Remove-Item "$env:TEMP\nssm-extract" -Recurse -Force -ErrorAction SilentlyContinue
205
+ }
206
+ }
207
+ }
208
+ if (-not (Test-Path $NssmExe)) {
209
+ Write-Host ""
210
+ Write-Host " NSSM download failed. Manual fix:" -ForegroundColor Red
211
+ Write-Host " 1. Download nssm-2.24.zip from https://nssm.cc/release/nssm-2.24.zip" -ForegroundColor Yellow
212
+ Write-Host " 2. Extract win64\nssm.exe to: $NssmExe" -ForegroundColor Yellow
213
+ Write-Host " 3. Re-run this script" -ForegroundColor Yellow
214
+ Write-Host ""
215
+ Write-Host " OR bundle it in the repo:" -ForegroundColor Yellow
216
+ Write-Host " Copy nssm.exe to: $AgentDir\nssm\nssm.exe" -ForegroundColor Yellow
217
+ exit 1
218
+ }
219
+ Write-Host " NSSM ready: $NssmExe"
220
+
221
+ # --- 10. Install Windows Service via NSSM ---
222
+ Write-Host "[10/19] Installing Windows Service..." -ForegroundColor Yellow
223
+
224
+ # Clean slate
225
+ $ErrorActionPreference = "Continue"
226
+ & $NssmExe stop $ServiceName 2>$null
227
+ & $NssmExe remove $ServiceName confirm 2>$null
228
+ sc.exe delete $ServiceName 2>$null
229
+ Start-Sleep -Seconds 2
230
+ $ErrorActionPreference = "Stop"
231
+
232
+ $nodePath = (Get-Command node).Source
233
+
234
+ # Install
235
+ & $NssmExe install $ServiceName $nodePath "dist\index.js"
236
+ if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: NSSM install failed!" -ForegroundColor Red; exit 1 }
237
+
238
+ # Configure
239
+ & $NssmExe set $ServiceName AppDirectory $InstallDir
240
+ & $NssmExe set $ServiceName DisplayName "LIGHTMAN Agent"
241
+ & $NssmExe set $ServiceName Description "LIGHTMAN kiosk agent - display management and monitoring"
242
+ & $NssmExe set $ServiceName Start SERVICE_AUTO_START
243
+ & $NssmExe set $ServiceName AppStdout "$LogDir\service-stdout.log"
244
+ & $NssmExe set $ServiceName AppStderr "$LogDir\service-stderr.log"
245
+ & $NssmExe set $ServiceName AppStdoutCreationDisposition 4
246
+ & $NssmExe set $ServiceName AppStderrCreationDisposition 4
247
+ & $NssmExe set $ServiceName AppRotateFiles 1
248
+ & $NssmExe set $ServiceName AppRotateBytes 5242880
249
+ & $NssmExe set $ServiceName AppRestartDelay 10000
250
+ & $NssmExe set $ServiceName AppExit Default Restart
251
+
252
+ # Verify service was created
253
+ Start-Sleep -Seconds 2
254
+ $svcCheck = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
255
+ if (-not $svcCheck) {
256
+ $svcCheck = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1
257
+ }
258
+ if (-not $svcCheck) {
259
+ Write-Host " FATAL: Service was not created!" -ForegroundColor Red
260
+ exit 1
261
+ }
262
+ Write-Host " Service installed: $($svcCheck.Name)" -ForegroundColor Green
263
+
264
+ # Recovery policy
265
+ sc.exe failure $svcCheck.Name reset= 86400 actions= restart/5000/restart/10000/restart/30000 2>$null
266
+
267
+ # --- 11. Start service ---
268
+ Write-Host "[11/19] Starting service..." -ForegroundColor Yellow
269
+ Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
270
+ Start-Sleep -Seconds 5
271
+ $svcCheck.Refresh()
272
+
273
+ if ($svcCheck.Status -eq 'Running') {
274
+ Write-Host " Service is RUNNING" -ForegroundColor Green
275
+ } else {
276
+ Write-Host " Service status: $($svcCheck.Status) - check $LogDir" -ForegroundColor Yellow
277
+ Start-Sleep -Seconds 3
278
+ Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
279
+ }
280
+
281
+ # Wait for port 3403
282
+ $portUp = $false
283
+ for ($i = 0; $i -lt 10; $i++) {
284
+ $ErrorActionPreference = "Continue"
285
+ $n = netstat -an 2>$null | findstr ":3403.*LISTENING" 2>$null
286
+ $ErrorActionPreference = "Stop"
287
+ if ($n) { $portUp = $true; break }
288
+ Start-Sleep -Seconds 2
289
+ }
290
+ if ($portUp) { Write-Host " Port 3403 LISTENING" -ForegroundColor Green }
291
+ else { Write-Host " Port 3403 not yet up (may take a moment)" -ForegroundColor Yellow }
292
+
293
+ # --- 12. Firewall ---
294
+ Write-Host "[12/19] Configuring firewall..." -ForegroundColor Yellow
295
+ $ErrorActionPreference = "Continue"
296
+ if (-not (Get-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue)) {
297
+ New-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -Direction Outbound -Action Allow -Protocol TCP -RemotePort 3001 -Description "LIGHTMAN Agent" | Out-Null
298
+ Write-Host " Created"
299
+ } else { Write-Host " Already exists" }
300
+
301
+ # ============================================================
302
+ # PART 2: KIOSK CONFIGURATION
303
+ # ============================================================
304
+ $ErrorActionPreference = "Continue"
305
+ Write-Host ""
306
+ Write-Host "--- Configuring Kiosk Mode ---" -ForegroundColor Cyan
307
+ Write-Host ""
308
+
309
+ # --- 13. Auto-login ---
310
+ Write-Host "[13/19] Enabling auto-login..." -ForegroundColor Yellow
311
+ $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
312
+ $targetUser = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue
313
+ $isMsAccount = $targetUser -and $targetUser.PrincipalSource -eq 'MicrosoftAccount'
314
+
315
+ if ($isMsAccount) {
316
+ $KioskUser = "kiosk"
317
+ $existingKiosk = Get-LocalUser -Name $KioskUser -ErrorAction SilentlyContinue
318
+ if (-not $existingKiosk) { net user $KioskUser "" /add 2>$null; net localgroup Administrators $KioskUser /add 2>$null }
319
+ else { net user $KioskUser "" 2>$null }
320
+ $HidePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
321
+ if (-not (Test-Path $HidePath)) { New-Item -Path $HidePath -Force | Out-Null }
322
+ Set-ItemProperty -Path $HidePath -Name $Username -Value 0
323
+ $Username = $KioskUser
324
+ Write-Host " Created kiosk account, auto-login: $Username" -ForegroundColor Green
325
+ } else {
326
+ net user $Username "" 2>$null
327
+ }
328
+
329
+ $PwdLess = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device"
330
+ if (Test-Path $PwdLess) { Set-ItemProperty -Path $PwdLess -Name "DevicePasswordLessBuildVersion" -Value 0 }
331
+
332
+ $Passport = "HKLM:\SOFTWARE\Policies\Microsoft\PassportForWork"
333
+ if (-not (Test-Path $Passport)) { New-Item -Path $Passport -Force | Out-Null }
334
+ Set-ItemProperty -Path $Passport -Name "Enabled" -Value 0
335
+
336
+ Set-ItemProperty -Path $RegPath -Name "AutoAdminLogon" -Value "1"
337
+ Set-ItemProperty -Path $RegPath -Name "DefaultUserName" -Value $Username
338
+ Set-ItemProperty -Path $RegPath -Name "DefaultPassword" -Value ""
339
+ Set-ItemProperty -Path $RegPath -Name "DefaultDomainName" -Value ""
340
+ Set-ItemProperty -Path $RegPath -Name "DisableCAD" -Value 1
341
+ Set-ItemProperty -Path $RegPath -Name "AutoRestartShell" -Value 1
342
+ Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DisableAutomaticRestartSignOn" -Value 0
343
+ $OOBE = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE"
344
+ if (-not (Test-Path $OOBE)) { New-Item -Path $OOBE -Force | Out-Null }
345
+ Set-ItemProperty -Path $OOBE -Name "DisablePrivacyExperience" -Value 1
346
+ Write-Host " Auto-login enabled for: $Username"
347
+
348
+ # --- 14. Lock screen ---
349
+ Write-Host "[14/19] Removing lock screen..." -ForegroundColor Yellow
350
+ $LP = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization"
351
+ if (-not (Test-Path $LP)) { New-Item -Path $LP -Force | Out-Null }
352
+ Set-ItemProperty -Path $LP -Name "NoLockScreen" -Value 1
353
+
354
+ $SD = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
355
+ if (Test-Path $SD) { Set-ItemProperty -Path $SD -Name "AllowLockScreen" -Value 0 -ErrorAction SilentlyContinue }
356
+
357
+ $CC = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
358
+ if (-not (Test-Path $CC)) { New-Item -Path $CC -Force | Out-Null }
359
+ Set-ItemProperty -Path $CC -Name "DisableWindowsConsumerFeatures" -Value 1
360
+ Set-ItemProperty -Path $CC -Name "DisableCloudOptimizedContent" -Value 1
361
+ $CCU = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
362
+ if (-not (Test-Path $CCU)) { New-Item -Path $CCU -Force | Out-Null }
363
+ Set-ItemProperty -Path $CCU -Name "DisableWindowsSpotlightFeatures" -Value 1
364
+ Set-ItemProperty -Path $CCU -Name "DisableTailoredExperiencesWithDiagnosticData" -Value 1
365
+
366
+ Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableFirstLogonAnimation" -Value 0
367
+ $SP = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
368
+ Set-ItemProperty -Path $SP -Name "DisableLockWorkstation" -Value 1
369
+ Set-ItemProperty -Path $SP -Name "HideFastUserSwitching" -Value 1
370
+
371
+ $DL = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
372
+ if (-not (Test-Path $DL)) { New-Item -Path $DL -Force | Out-Null }
373
+ Set-ItemProperty -Path $DL -Name "EnableGoodbye" -Value 0
374
+
375
+ $PS = "HKLM:\SOFTWARE\Policies\Microsoft\Power\PowerSettings\0e796bdb-100d-47d6-a2d5-f7d2daa51f51"
376
+ if (-not (Test-Path $PS)) { New-Item -Path $PS -Force | Out-Null }
377
+ Set-ItemProperty -Path $PS -Name "ACSettingIndex" -Value 0
378
+ Set-ItemProperty -Path $PS -Name "DCSettingIndex" -Value 0
379
+ powercfg /SETACVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
380
+ powercfg /SETDCVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
381
+ powercfg /SETACTIVE SCHEME_CURRENT 2>&1 | Out-Null
382
+
383
+ Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaverIsSecure" -Value "0"
384
+ Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaveActive" -Value "0"
385
+ Set-ItemProperty -Path $SP -Name "InactivityTimeoutSecs" -Value 0 -ErrorAction SilentlyContinue
386
+ try { Disable-ScheduledTask -TaskName "\Microsoft\Windows\Shell\CreateObjectTask" -ErrorAction SilentlyContinue | Out-Null } catch { }
387
+ Write-Host " Lock screen fully disabled"
388
+
389
+ # --- 15. Sleep ---
390
+ Write-Host "[15/19] Disabling sleep..." -ForegroundColor Yellow
391
+ powercfg /change monitor-timeout-ac 0 2>&1 | Out-Null
392
+ powercfg /change standby-timeout-ac 0 2>&1 | Out-Null
393
+ powercfg /change hibernate-timeout-ac 0 2>&1 | Out-Null
394
+
395
+ # --- 16. Harden ---
396
+ Write-Host "[16/19] Hardening Windows..." -ForegroundColor Yellow
397
+ $WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
398
+ if (-not (Test-Path $WU)) { New-Item -Path $WU -Force | Out-Null }
399
+ Set-ItemProperty -Path $WU -Name "NoAutoRebootWithLoggedOnUsers" -Value 1
400
+ Set-ItemProperty -Path $WU -Name "AUOptions" -Value 2
401
+ $WUM = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
402
+ if (-not (Test-Path $WUM)) { New-Item -Path $WUM -Force | Out-Null }
403
+ Set-ItemProperty -Path $WUM -Name "SetAutoRestartNotificationDisable" -Value 1
404
+ Set-ItemProperty -Path $WUM -Name "SetActiveHours" -Value 1
405
+ Set-ItemProperty -Path $WUM -Name "ActiveHoursStart" -Value 0
406
+ Set-ItemProperty -Path $WUM -Name "ActiveHoursEnd" -Value 23
407
+
408
+ $NP = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer"
409
+ if (-not (Test-Path $NP)) { New-Item -Path $NP -Force | Out-Null }
410
+ Set-ItemProperty -Path $NP -Name "DisableNotificationCenter" -Value 1
411
+ $TP = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications"
412
+ if (-not (Test-Path $TP)) { New-Item -Path $TP -Force | Out-Null }
413
+ Set-ItemProperty -Path $TP -Name "ToastEnabled" -Value 0
414
+
415
+ $WER = "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting"
416
+ if (-not (Test-Path $WER)) { New-Item -Path $WER -Force | Out-Null }
417
+ Set-ItemProperty -Path $WER -Name "DontShowUI" -Value 1
418
+ Set-ItemProperty -Path $WER -Name "Disabled" -Value 1
419
+ Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Windows" -Name "ErrorMode" -Value 2 -ErrorAction SilentlyContinue
420
+
421
+ $SR = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search"
422
+ if (-not (Test-Path $SR)) { New-Item -Path $SR -Force | Out-Null }
423
+ Set-ItemProperty -Path $SR -Name "AllowCortana" -Value 0
424
+ Write-Host " Done"
425
+
426
+ # --- 17. Kiosk Chrome ---
427
+ if ($ShellReplace) {
428
+ Write-Host "[17/19] SHELL REPLACEMENT..." -ForegroundColor Magenta
429
+
430
+ # Copy shell BAT (reads slug from agent.config.json - single source of truth)
431
+ $shellSource = Join-Path $ScriptDir "lightman-shell.bat"
432
+ $shellTarget = Join-Path $InstallDir "lightman-shell.bat"
433
+ if (Test-Path $shellSource) { Copy-Item $shellSource $shellTarget -Force }
434
+
435
+ # No sidecar file needed - shell BAT reads directly from agent.config.json
436
+
437
+ # Replace shell
438
+ $ShellReg = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
439
+ if (-not (Test-Path $ShellReg)) { New-Item -Path $ShellReg -Force | Out-Null }
440
+ Set-ItemProperty -Path $ShellReg -Name "Shell" -Value """$shellTarget"""
441
+ $HKLMShell = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
442
+ $orig = (Get-ItemProperty -Path $HKLMShell -Name "Shell" -ErrorAction SilentlyContinue).Shell
443
+ if ($orig -and $orig -notlike "*lightman*") { Set-ItemProperty -Path $HKLMShell -Name "Shell_Original" -Value $orig }
444
+ Set-ItemProperty -Path $HKLMShell -Name "Shell" -Value """$shellTarget"""
445
+
446
+ Write-Host " Shell replaced -> lightman-shell.bat" -ForegroundColor Green
447
+ Write-Host " Recovery: scripts\restore-desktop.ps1" -ForegroundColor Yellow
448
+
449
+ # Remove kiosk task if exists
450
+ $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
451
+ if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
452
+ } else {
453
+ Write-Host "[17/19] Standard mode - kiosk browser task..." -ForegroundColor Yellow
454
+ $vbs = Join-Path $ScriptDir "launch-kiosk.vbs"
455
+ $vbsT = Join-Path $InstallDir "launch-kiosk.vbs"
456
+ if (Test-Path $vbs) { Copy-Item $vbs $vbsT -Force }
457
+ $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
458
+ if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
459
+ $kA = New-ScheduledTaskAction -Execute "wscript.exe" -Argument """$vbsT""" -WorkingDirectory $InstallDir
460
+ $kT1 = New-ScheduledTaskTrigger -AtLogOn -User $Username
461
+ $kT2 = New-ScheduledTaskTrigger -AtStartup
462
+ $kS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
463
+ Register-ScheduledTask -TaskName $KioskTask -Action $kA -Trigger @($kT1,$kT2) -Settings $kS -RunLevel Highest -Description "Chrome kiosk at logon/startup" -Force | Out-Null
464
+ Write-Host " Kiosk browser task registered"
465
+ }
466
+
467
+ # --- 18. Guardian ---
468
+ Write-Host "[18/19] Registering Guardian..." -ForegroundColor Yellow
469
+ $gSrc = Join-Path $ScriptDir "guardian.ps1"
470
+ $gDst = Join-Path $InstallDir "guardian.ps1"
471
+ if (Test-Path $gSrc) { Copy-Item $gSrc $gDst -Force }
472
+ $gt = Get-ScheduledTask -TaskName $GuardianTask -ErrorAction SilentlyContinue
473
+ if ($gt) { Unregister-ScheduledTask -TaskName $GuardianTask -Confirm:$false }
474
+ $gA = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File ""$gDst""" -WorkingDirectory $InstallDir
475
+ $gT = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 365)
476
+ $gS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 2)
477
+ $gP = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
478
+ Register-ScheduledTask -TaskName $GuardianTask -Action $gA -Trigger $gT -Settings $gS -Principal $gP -Description "LIGHTMAN health check every 5 min" -Force | Out-Null
479
+
480
+ foreach ($task in @("\Microsoft\Windows\UpdateOrchestrator\Reboot","\Microsoft\Windows\UpdateOrchestrator\Schedule Retry Scan","\Microsoft\Windows\WindowsUpdate\Scheduled Start")) {
481
+ try { Disable-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue | Out-Null } catch { }
482
+ }
483
+ Write-Host " Guardian registered"
484
+
485
+ # --- 19. Final verification ---
486
+ Write-Host "[19/19] Verification..." -ForegroundColor Yellow
487
+ Start-Sleep -Seconds 3
488
+
489
+ $finalSvc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
490
+ if (-not $finalSvc) { $finalSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1 }
491
+ $svcStatus = if ($finalSvc) { "$($finalSvc.Status)" } else { "NOT FOUND" }
492
+
493
+ $cfgOk = $false
494
+ try {
495
+ Push-Location $InstallDir
496
+ $cfgResult = & node -e "const c=JSON.parse(require('fs').readFileSync('agent.config.json','utf8'));console.log(JSON.stringify({slug:c.deviceSlug,shell:c.kiosk&&c.kiosk.shellMode||false}))" 2>&1
497
+ $cfgData = $cfgResult | ConvertFrom-Json
498
+ Pop-Location
499
+ $cfgOk = $true
500
+ } catch { Pop-Location }
501
+
502
+ Write-Host ""
503
+ Write-Host "=============================================" -ForegroundColor Green
504
+ Write-Host " INSTALLATION COMPLETE" -ForegroundColor Green
505
+ Write-Host "=============================================" -ForegroundColor Green
506
+ Write-Host ""
507
+ Write-Host " Slug : $Slug"
508
+ Write-Host " Server : $Server"
509
+ Write-Host " Install : $InstallDir"
510
+ Write-Host " Logs : $LogDir"
511
+ Write-Host " User : $Username"
512
+ Write-Host ""
513
+ Write-Host " Service : $svcStatus" -ForegroundColor $(if ($svcStatus -eq 'Running') { 'Green' } else { 'Red' })
514
+ if ($cfgOk) {
515
+ Write-Host " Config slug: $($cfgData.slug)" -ForegroundColor $(if ($cfgData.slug -eq $Slug) { 'Green' } else { 'Red' })
516
+ Write-Host " Shell mode : $($cfgData.shell)" -ForegroundColor $(if ($cfgData.shell -eq $ShellReplace.IsPresent) { 'Green' } else { 'Red' })
517
+ }
518
+ Write-Host ""
519
+ Write-Host " Manage:" -ForegroundColor DarkGray
520
+ Write-Host " $NssmExe stop $ServiceName" -ForegroundColor DarkGray
521
+ Write-Host " $NssmExe start $ServiceName" -ForegroundColor DarkGray
522
+ Write-Host " $NssmExe restart $ServiceName" -ForegroundColor DarkGray
523
+ Write-Host ""
524
+ Write-Host " BIOS (manual):" -ForegroundColor Red
525
+ Write-Host " After Power Loss = Power On" -ForegroundColor Red
526
+ Write-Host " Wake-on-LAN = Enabled" -ForegroundColor Red
527
+ Write-Host ""
528
+ Write-Host " REBOOT NOW: Restart-Computer" -ForegroundColor Yellow
529
+ Write-Host ""
@@ -0,0 +1,101 @@
1
+ ' LIGHTMAN Kiosk Launcher
2
+ ' Runs at user logon AND at system startup to start the kiosk browser.
3
+ ' Waits for the agent service to be ready before launching Chrome.
4
+ ' If the agent is already managing Chrome, this script exits gracefully.
5
+
6
+ Set objShell = CreateObject("WScript.Shell")
7
+ Set objFSO = CreateObject("Scripting.FileSystemObject")
8
+
9
+ ' --- Configuration ---
10
+ configPath = "C:\Program Files\Lightman\Agent\agent.config.json"
11
+ maxWaitSeconds = 120 ' Max time to wait for agent service
12
+ checkIntervalMs = 5000 ' Check every 5 seconds
13
+
14
+ ' --- Wait for config file to exist (agent might still be installing) ---
15
+ If Not objFSO.FileExists(configPath) Then
16
+ WScript.Sleep 10000
17
+ If Not objFSO.FileExists(configPath) Then
18
+ WScript.Quit 1
19
+ End If
20
+ End If
21
+
22
+ ' --- Read config ---
23
+ Set objFile = objFSO.OpenTextFile(configPath, 1)
24
+ jsonText = objFile.ReadAll
25
+ objFile.Close
26
+
27
+ browserPath = ExtractJsonValue(jsonText, "browserPath")
28
+ defaultUrl = ExtractJsonValue(jsonText, "defaultUrl")
29
+
30
+ If browserPath = "" Or defaultUrl = "" Then
31
+ WScript.Quit 1
32
+ End If
33
+
34
+ ' --- Wait for network connectivity ---
35
+ ' Try to ping the server (extract hostname from URL)
36
+ waitedMs = 0
37
+ Do While waitedMs < (maxWaitSeconds * 1000)
38
+ ' Check if Chrome is already running (agent's KioskManager may have launched it)
39
+ Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
40
+ Set colProcesses = objWMI.ExecQuery("SELECT ProcessId FROM Win32_Process WHERE Name = 'chrome.exe'")
41
+ If colProcesses.Count > 0 Then
42
+ ' Chrome already running - agent is handling it. Exit gracefully.
43
+ WScript.Quit 0
44
+ End If
45
+
46
+ ' Check if LIGHTMAN service is running
47
+ Set colServices = objWMI.ExecQuery("SELECT State FROM Win32_Service WHERE DisplayName LIKE 'LIGHTMAN%'")
48
+ serviceRunning = False
49
+ For Each svc In colServices
50
+ If LCase(svc.State) = "running" Then
51
+ serviceRunning = True
52
+ End If
53
+ Next
54
+
55
+ If serviceRunning Then
56
+ ' Service is running - give it a few more seconds to launch Chrome itself
57
+ WScript.Sleep 15000
58
+
59
+ ' Re-check if Chrome appeared (agent launched it)
60
+ Set colProcesses2 = objWMI.ExecQuery("SELECT ProcessId FROM Win32_Process WHERE Name = 'chrome.exe'")
61
+ If colProcesses2.Count > 0 Then
62
+ ' Agent launched Chrome successfully. Exit.
63
+ WScript.Quit 0
64
+ End If
65
+
66
+ ' Agent is running but hasn't launched Chrome yet - we'll do it
67
+ Exit Do
68
+ End If
69
+
70
+ WScript.Sleep checkIntervalMs
71
+ waitedMs = waitedMs + checkIntervalMs
72
+ Loop
73
+
74
+ ' --- Build Chrome kiosk args ---
75
+ chromeArgs = "--kiosk --noerrdialogs --disable-infobars --disable-session-crashed-bubble --no-first-run --no-default-browser-check --start-fullscreen --disable-translate --disable-extensions --autoplay-policy=no-user-gesture-required"
76
+
77
+ userDataDir = ExtractJsonValue(jsonText, "user-data-dir")
78
+ If userDataDir = "" Then
79
+ userDataDir = "C:\ProgramData\Lightman\chrome-kiosk"
80
+ End If
81
+ chromeArgs = chromeArgs & " --user-data-dir=""" & userDataDir & """"
82
+
83
+ ' --- Launch Chrome ---
84
+ objShell.Run """" & browserPath & """ " & chromeArgs & " """ & defaultUrl & """", 1, False
85
+
86
+ ' ========================================================
87
+ ' Helper: extract a string value from JSON by key
88
+ ' ========================================================
89
+ Function ExtractJsonValue(json, key)
90
+ ExtractJsonValue = ""
91
+ pos = InStr(json, """" & key & """")
92
+ If pos = 0 Then Exit Function
93
+ pos = InStr(pos, json, ":")
94
+ If pos = 0 Then Exit Function
95
+ pos = InStr(pos, json, """")
96
+ If pos = 0 Then Exit Function
97
+ pos = pos + 1
98
+ endPos = InStr(pos, json, """")
99
+ If endPos = 0 Then Exit Function
100
+ ExtractJsonValue = Mid(json, pos, endPos - pos)
101
+ End Function
@@ -0,0 +1,12 @@
1
+ /var/log/lightman/*.log {
2
+ daily
3
+ rotate 7
4
+ compress
5
+ delaycompress
6
+ missingok
7
+ notifempty
8
+ create 0640 lightman lightman
9
+ postrotate
10
+ systemctl reload lightman-agent > /dev/null 2>&1 || true
11
+ endscript
12
+ }