lightman-agent 1.0.5 → 1.0.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.
Files changed (115) hide show
  1. package/agent.config.template.json +30 -30
  2. package/package.json +52 -52
  3. package/public/assets/index-CcBNCz6h.css +1 -1
  4. package/public/assets/index-D9QHMG8k.js +1 -0
  5. package/public/assets/index-H-8HDl46.js +1 -1
  6. package/public/assets/index-YodeiCia.css +1 -0
  7. package/public/assets/index-legacy-DWtNM8y7.js +41 -0
  8. package/public/assets/museum-map-CwVDA2z1.svg +4182 -0
  9. package/public/assets/polyfills-legacy-DyVYWHbW.js +4 -0
  10. package/public/index.html +7 -2
  11. package/public/templates/custom08/elements/back-button.svg +20 -0
  12. package/public/templates/custom08/elements/base-map-background.svg +37 -0
  13. package/public/templates/custom08/elements/base-map.svg +1191 -0
  14. package/public/templates/custom08/elements/gallery-1-2-3-info-panel.svg +236 -0
  15. package/public/templates/custom08/elements/gallery-4-5-6-7-info-panel.svg +266 -0
  16. package/public/templates/custom08/elements/gallery-8-9-info-panel.svg +274 -0
  17. package/public/templates/custom08/elements/gallery-labels/_nav-map-styles.css +554 -0
  18. package/public/templates/custom08/elements/gallery-labels/_styles.css +556 -0
  19. package/public/templates/custom08/elements/gallery-labels/gallery-1.svg +35 -0
  20. package/public/templates/custom08/elements/gallery-labels/gallery-2.svg +34 -0
  21. package/public/templates/custom08/elements/gallery-labels/gallery-3.svg +34 -0
  22. package/public/templates/custom08/elements/gallery-labels/gallery-4.svg +37 -0
  23. package/public/templates/custom08/elements/gallery-labels/gallery-5.svg +34 -0
  24. package/public/templates/custom08/elements/gallery-labels/gallery-6.svg +34 -0
  25. package/public/templates/custom08/elements/gallery-labels/gallery-7.svg +34 -0
  26. package/public/templates/custom08/elements/gallery-labels/gallery-8.svg +37 -0
  27. package/public/templates/custom08/elements/gallery-labels/gallery-9.svg +34 -0
  28. package/public/templates/custom08/elements/hand-hint.png +0 -0
  29. package/public/templates/custom08/elements/idle-screen-bg.svg +5 -0
  30. package/public/templates/custom08/elements/idle-screen-map.svg +627 -0
  31. package/public/templates/custom08/elements/idle-screen-text.svg +350 -0
  32. package/public/templates/custom08/elements/key-map-1.svg +986 -0
  33. package/public/templates/custom08/elements/key-map-2.svg +1018 -0
  34. package/public/templates/custom08/elements/key-map-3.svg +1019 -0
  35. package/public/templates/custom08/elements/key-map-combined.svg +1001 -0
  36. package/public/templates/custom08/elements/map-highlight-marker.svg +11 -0
  37. package/public/templates/custom08/elements/map-pin-marker.svg +15 -0
  38. package/public/templates/custom08/elements/map-teardrop-star-marker.svg +13 -0
  39. package/public/templates/custom08/elements/nav-circle-galleries-1-3.svg +21 -0
  40. package/public/templates/custom08/elements/nav-circle-galleries-4-7.svg +24 -0
  41. package/public/templates/custom08/elements/nav-circle-galleries-8-9.svg +20 -0
  42. package/public/templates/custom08/elements/section1-map.svg +1435 -0
  43. package/public/templates/custom08/elements/section2-map.svg +1724 -0
  44. package/public/templates/custom08/elements/section3-map.svg +1295 -0
  45. package/public/templates/custom08/fonts/CabinetGrotesk-Variable.ttf +0 -0
  46. package/public/templates/custom08/images/highlights/Screenshot_2026-03-05_at_7.23.12_PM.png +0 -0
  47. package/public/templates/custom08/images/highlights/Screenshot_2026-03-05_at_7.23.56_PM.png +0 -0
  48. package/public/templates/custom08/images/highlights/Screenshot_2026-03-05_at_7.24.24_PM.png +0 -0
  49. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.31.58_PM.jpg +0 -0
  50. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.32.11_PM.jpg +0 -0
  51. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.32.36_PM.jpg +0 -0
  52. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.32.48_PM.jpg +0 -0
  53. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.32.59_PM.jpg +0 -0
  54. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.15_PM.jpg +0 -0
  55. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.27_PM.jpg +0 -0
  56. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.34_PM.jpg +0 -0
  57. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.42_PM.jpg +0 -0
  58. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.50_PM.jpg +0 -0
  59. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.33.58_PM.jpg +0 -0
  60. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.34.04_PM.jpg +0 -0
  61. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.34.11_PM.jpg +0 -0
  62. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.34.20_PM.jpg +0 -0
  63. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.34.57_PM.jpg +0 -0
  64. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.35.03_PM.jpg +0 -0
  65. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.35.16_PM.jpg +0 -0
  66. package/public/templates/custom08/images/highlights/Screenshot_2026-03-24_at_11.35.23_PM.jpg +0 -0
  67. package/public/templates/custom08/images/highlights/prologue-highlight.png +0 -0
  68. package/scripts/guardian.ps1 +75 -75
  69. package/scripts/install-linux.sh +134 -134
  70. package/scripts/install-rpi.sh +117 -117
  71. package/scripts/install-windows.ps1 +505 -505
  72. package/scripts/launch-kiosk.vbs +101 -101
  73. package/scripts/lightman-agent.logrotate +12 -12
  74. package/scripts/lightman-agent.service +38 -38
  75. package/scripts/lightman-shell.bat +107 -107
  76. package/scripts/reinstall-windows.ps1 +26 -26
  77. package/scripts/restore-desktop.ps1 +32 -32
  78. package/scripts/setup.ps1 +116 -116
  79. package/scripts/setup.sh +115 -115
  80. package/scripts/sync-display.mjs +20 -0
  81. package/scripts/uninstall-linux.sh +50 -50
  82. package/scripts/uninstall-windows.ps1 +54 -54
  83. package/src/commands/display.ts +177 -177
  84. package/src/commands/kiosk.ts +113 -113
  85. package/src/commands/maintenance.ts +106 -106
  86. package/src/commands/network.ts +129 -129
  87. package/src/commands/power.ts +163 -163
  88. package/src/commands/rpi.ts +45 -45
  89. package/src/commands/screenshot.ts +166 -166
  90. package/src/commands/serial.ts +17 -17
  91. package/src/commands/update.ts +124 -124
  92. package/src/index.ts +652 -652
  93. package/src/lib/config.ts +69 -69
  94. package/src/lib/identity.ts +40 -40
  95. package/src/lib/logger.ts +137 -137
  96. package/src/lib/platform.ts +10 -10
  97. package/src/lib/rpi.ts +180 -180
  98. package/src/lib/screens.ts +128 -128
  99. package/src/lib/types.ts +176 -176
  100. package/src/services/commands.ts +107 -107
  101. package/src/services/health.ts +161 -161
  102. package/src/services/kiosk.ts +384 -384
  103. package/src/services/localEvents.ts +60 -60
  104. package/src/services/logForwarder.ts +72 -72
  105. package/src/services/multiScreenKiosk.ts +324 -324
  106. package/src/services/oscBridge.ts +186 -186
  107. package/src/services/powerScheduler.ts +260 -260
  108. package/src/services/provisioning.ts +120 -120
  109. package/src/services/serialBridge.ts +230 -230
  110. package/src/services/serviceLauncher.ts +183 -183
  111. package/src/services/staticServer.ts +226 -226
  112. package/src/services/updater.ts +249 -249
  113. package/src/services/watchdog.ts +310 -310
  114. package/src/services/websocket.ts +152 -152
  115. package/tsconfig.json +28 -28
@@ -1,14 +1,14 @@
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
-
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
12
  param(
13
13
  [Parameter(Mandatory=$true)] [string]$Slug,
14
14
  [Parameter(Mandatory=$true)] [string]$Server,
@@ -17,58 +17,58 @@ param(
17
17
  [switch]$ShellReplace = $false,
18
18
  [int]$PairingTimeoutSeconds = 900
19
19
  )
20
-
21
- $ErrorActionPreference = "Stop"
22
-
23
- $InstallDir = "C:\Program Files\Lightman\Agent"
24
- $LogDir = "C:\ProgramData\Lightman\logs"
25
- $ChromeData = "C:\ProgramData\Lightman\chrome-kiosk"
26
- $NssmDir = "C:\ProgramData\Lightman\nssm"
27
- $NssmExe = "$NssmDir\nssm.exe"
28
- $ServiceName = "LightmanAgent"
29
- $GuardianTask = "LIGHTMAN Guardian"
30
- $KioskTask = "LIGHTMAN Kiosk Browser"
31
- $AgentTask = "LIGHTMAN Agent"
32
- $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
33
- $AgentDir = Split-Path -Parent $ScriptDir
34
-
35
- if (-not $Username) { $Username = $env:USERNAME }
36
-
37
- Write-Host ""
38
- Write-Host "=============================================" -ForegroundColor Cyan
39
- Write-Host " LIGHTMAN Agent - Complete Windows Installer" -ForegroundColor Cyan
40
- Write-Host "=============================================" -ForegroundColor Cyan
41
- Write-Host " Device slug : $Slug"
42
- Write-Host " Server URL : $Server"
43
- Write-Host " Username : $Username"
44
- Write-Host " Mode : $(if ($ShellReplace) { 'Shell Replacement' } else { 'Standard' })"
45
- Write-Host ""
46
-
47
- # ============================================================
48
- # PHASE 0: NUKE EVERYTHING FROM PREVIOUS INSTALLS
49
- # ============================================================
50
- Write-Host "--- Phase 0: Cleaning previous installation ---" -ForegroundColor Cyan
51
- $ErrorActionPreference = "Continue"
52
-
53
- # Stop and remove NSSM service
54
- Write-Host "[0a] Removing old services..." -ForegroundColor Yellow
55
- if (Test-Path $NssmExe) {
56
- & $NssmExe stop $ServiceName 2>$null
57
- & $NssmExe remove $ServiceName confirm 2>$null
58
- }
59
- foreach ($sn in @($ServiceName, "lightmanagent.exe", "LightmanAgent.exe")) {
60
- sc.exe stop $sn 2>$null; sc.exe delete $sn 2>$null
61
- }
62
- $oldSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue
63
- if ($oldSvc) { Stop-Service -Name $oldSvc.Name -Force -ErrorAction SilentlyContinue; sc.exe delete $oldSvc.Name 2>$null }
64
-
65
- # Remove scheduled tasks (from previous task-scheduler-based installs)
66
- Write-Host "[0b] Removing old scheduled tasks..." -ForegroundColor Yellow
67
- foreach ($tn in @($AgentTask, $KioskTask, $GuardianTask)) {
68
- $t = Get-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue
69
- if ($t) { Stop-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue; Unregister-ScheduledTask -TaskName $tn -Confirm:$false -ErrorAction SilentlyContinue }
70
- }
71
-
20
+
21
+ $ErrorActionPreference = "Stop"
22
+
23
+ $InstallDir = "C:\Program Files\Lightman\Agent"
24
+ $LogDir = "C:\ProgramData\Lightman\logs"
25
+ $ChromeData = "C:\ProgramData\Lightman\chrome-kiosk"
26
+ $NssmDir = "C:\ProgramData\Lightman\nssm"
27
+ $NssmExe = "$NssmDir\nssm.exe"
28
+ $ServiceName = "LightmanAgent"
29
+ $GuardianTask = "LIGHTMAN Guardian"
30
+ $KioskTask = "LIGHTMAN Kiosk Browser"
31
+ $AgentTask = "LIGHTMAN Agent"
32
+ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
33
+ $AgentDir = Split-Path -Parent $ScriptDir
34
+
35
+ if (-not $Username) { $Username = $env:USERNAME }
36
+
37
+ Write-Host ""
38
+ Write-Host "=============================================" -ForegroundColor Cyan
39
+ Write-Host " LIGHTMAN Agent - Complete Windows Installer" -ForegroundColor Cyan
40
+ Write-Host "=============================================" -ForegroundColor Cyan
41
+ Write-Host " Device slug : $Slug"
42
+ Write-Host " Server URL : $Server"
43
+ Write-Host " Username : $Username"
44
+ Write-Host " Mode : $(if ($ShellReplace) { 'Shell Replacement' } else { 'Standard' })"
45
+ Write-Host ""
46
+
47
+ # ============================================================
48
+ # PHASE 0: NUKE EVERYTHING FROM PREVIOUS INSTALLS
49
+ # ============================================================
50
+ Write-Host "--- Phase 0: Cleaning previous installation ---" -ForegroundColor Cyan
51
+ $ErrorActionPreference = "Continue"
52
+
53
+ # Stop and remove NSSM service
54
+ Write-Host "[0a] Removing old services..." -ForegroundColor Yellow
55
+ if (Test-Path $NssmExe) {
56
+ & $NssmExe stop $ServiceName 2>$null
57
+ & $NssmExe remove $ServiceName confirm 2>$null
58
+ }
59
+ foreach ($sn in @($ServiceName, "lightmanagent.exe", "LightmanAgent.exe")) {
60
+ sc.exe stop $sn 2>$null; sc.exe delete $sn 2>$null
61
+ }
62
+ $oldSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue
63
+ if ($oldSvc) { Stop-Service -Name $oldSvc.Name -Force -ErrorAction SilentlyContinue; sc.exe delete $oldSvc.Name 2>$null }
64
+
65
+ # Remove scheduled tasks (from previous task-scheduler-based installs)
66
+ Write-Host "[0b] Removing old scheduled tasks..." -ForegroundColor Yellow
67
+ foreach ($tn in @($AgentTask, $KioskTask, $GuardianTask)) {
68
+ $t = Get-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue
69
+ if ($t) { Stop-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue; Unregister-ScheduledTask -TaskName $tn -Confirm:$false -ErrorAction SilentlyContinue }
70
+ }
71
+
72
72
  # Kill processes
73
73
  Write-Host "[0c] Killing node.exe and Chrome..." -ForegroundColor Yellow
74
74
  # IMPORTANT:
@@ -87,56 +87,56 @@ Get-Process -Name "node" -ErrorAction SilentlyContinue | ForEach-Object {
87
87
  }
88
88
  Get-Process -Name "chrome" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
89
89
  Start-Sleep -Seconds 2
90
-
91
- # Remove old files (keep NSSM and logs)
92
- Write-Host "[0d] Removing old agent files..." -ForegroundColor Yellow
93
- Remove-Item -Path $InstallDir -Recurse -Force -ErrorAction SilentlyContinue
94
- Remove-Item -Path "C:\ProgramData\Lightman\kiosk-url.txt" -Force -ErrorAction SilentlyContinue
95
-
96
- # Remove firewall rule
97
- Remove-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue
98
-
99
- $ErrorActionPreference = "Stop"
100
- Start-Sleep -Seconds 2
101
- Write-Host " Clean slate" -ForegroundColor Green
102
- Write-Host ""
103
-
104
- # ============================================================
105
- # PART 1: BUILD & INSTALL
106
- # ============================================================
107
-
108
- # --- 1. Node.js ---
109
- Write-Host "[1/19] Checking Node.js..." -ForegroundColor Yellow
110
- try {
111
- $nodeVersion = (node -v) -replace 'v', ''
112
- if ([int]($nodeVersion.Split('.')[0]) -lt 20) { throw "old" }
113
- Write-Host " Found Node.js v$nodeVersion"
114
- } catch {
115
- Write-Host " Installing Node.js v20.18.0..." -ForegroundColor Yellow
116
- $installer = "$env:TEMP\node-setup.msi"
117
- Invoke-WebRequest -Uri "https://nodejs.org/dist/v20.18.0/node-v20.18.0-x64.msi" -OutFile $installer -UseBasicParsing
118
- Start-Process msiexec.exe -ArgumentList "/i `"$installer`" /qn /norestart" -Wait -NoNewWindow
119
- Remove-Item $installer -Force -ErrorAction SilentlyContinue
120
- $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
121
- if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Host " FATAL: Node.js install failed!" -ForegroundColor Red; exit 1 }
122
- Write-Host " Node.js installed" -ForegroundColor Green
123
- }
124
-
125
- # --- 2. Build ---
126
- Write-Host "[2/19] Building agent..." -ForegroundColor Yellow
127
- Push-Location $AgentDir
128
- $ErrorActionPreference = "Continue"
129
- & npm install 2>&1 | Out-Host
130
- & npm run build 2>&1 | Out-Host
131
- $ErrorActionPreference = "Stop"
132
- if (-not (Test-Path "$AgentDir\dist\index.js")) { Write-Host " FATAL: Build failed!" -ForegroundColor Red; exit 1 }
133
- Pop-Location
134
- Write-Host " Build successful"
135
-
136
- # --- 3. Directories ---
137
- Write-Host "[3/19] Creating directories..." -ForegroundColor Yellow
138
- foreach ($d in @($InstallDir, $LogDir, $ChromeData, $NssmDir)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
139
-
90
+
91
+ # Remove old files (keep NSSM and logs)
92
+ Write-Host "[0d] Removing old agent files..." -ForegroundColor Yellow
93
+ Remove-Item -Path $InstallDir -Recurse -Force -ErrorAction SilentlyContinue
94
+ Remove-Item -Path "C:\ProgramData\Lightman\kiosk-url.txt" -Force -ErrorAction SilentlyContinue
95
+
96
+ # Remove firewall rule
97
+ Remove-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue
98
+
99
+ $ErrorActionPreference = "Stop"
100
+ Start-Sleep -Seconds 2
101
+ Write-Host " Clean slate" -ForegroundColor Green
102
+ Write-Host ""
103
+
104
+ # ============================================================
105
+ # PART 1: BUILD & INSTALL
106
+ # ============================================================
107
+
108
+ # --- 1. Node.js ---
109
+ Write-Host "[1/19] Checking Node.js..." -ForegroundColor Yellow
110
+ try {
111
+ $nodeVersion = (node -v) -replace 'v', ''
112
+ if ([int]($nodeVersion.Split('.')[0]) -lt 20) { throw "old" }
113
+ Write-Host " Found Node.js v$nodeVersion"
114
+ } catch {
115
+ Write-Host " Installing Node.js v20.18.0..." -ForegroundColor Yellow
116
+ $installer = "$env:TEMP\node-setup.msi"
117
+ Invoke-WebRequest -Uri "https://nodejs.org/dist/v20.18.0/node-v20.18.0-x64.msi" -OutFile $installer -UseBasicParsing
118
+ Start-Process msiexec.exe -ArgumentList "/i `"$installer`" /qn /norestart" -Wait -NoNewWindow
119
+ Remove-Item $installer -Force -ErrorAction SilentlyContinue
120
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
121
+ if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Host " FATAL: Node.js install failed!" -ForegroundColor Red; exit 1 }
122
+ Write-Host " Node.js installed" -ForegroundColor Green
123
+ }
124
+
125
+ # --- 2. Build ---
126
+ Write-Host "[2/19] Building agent..." -ForegroundColor Yellow
127
+ Push-Location $AgentDir
128
+ $ErrorActionPreference = "Continue"
129
+ & npm install 2>&1 | Out-Host
130
+ & npm run build 2>&1 | Out-Host
131
+ $ErrorActionPreference = "Stop"
132
+ if (-not (Test-Path "$AgentDir\dist\index.js")) { Write-Host " FATAL: Build failed!" -ForegroundColor Red; exit 1 }
133
+ Pop-Location
134
+ Write-Host " Build successful"
135
+
136
+ # --- 3. Directories ---
137
+ Write-Host "[3/19] Creating directories..." -ForegroundColor Yellow
138
+ foreach ($d in @($InstallDir, $LogDir, $ChromeData, $NssmDir)) { New-Item -ItemType Directory -Force -Path $d | Out-Null }
139
+
140
140
  # --- 4. Copy files ---
141
141
  Write-Host "[4/19] Copying agent files..." -ForegroundColor Yellow
142
142
  Copy-Item "$AgentDir\dist" "$InstallDir\dist" -Recurse -Force
@@ -145,163 +145,163 @@ if (Test-Path "$AgentDir\package-lock.json") { Copy-Item "$AgentDir\package-lock
145
145
  Copy-Item "$AgentDir\agent.config.template.json" "$InstallDir\agent.config.template.json" -Force
146
146
  if (Test-Path "$AgentDir\public") { Copy-Item "$AgentDir\public" "$InstallDir\public" -Recurse -Force }
147
147
  if (Test-Path "$AgentDir\scripts") { Copy-Item "$AgentDir\scripts" "$InstallDir\scripts" -Recurse -Force }
148
-
149
- # --- 5. Install deps ---
150
- Write-Host "[5/19] Installing dependencies..." -ForegroundColor Yellow
151
- Push-Location $InstallDir
152
- $ErrorActionPreference = "Continue"
153
- & npm ci --omit=dev --ignore-scripts 2>&1 | Out-Host
154
- if ($LASTEXITCODE -ne 0) { & npm install --omit=dev --ignore-scripts 2>&1 | Out-Host }
155
- $ErrorActionPreference = "Stop"
156
- Pop-Location
157
-
158
- # --- 6. Generate config ---
159
- Write-Host "[6/19] Generating config..." -ForegroundColor Yellow
160
- if ($ShellReplace) {
161
- & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir -ShellMode
162
- } else {
163
- & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir
164
- }
165
-
166
- # --- 7. Fix BOM ---
167
- Write-Host "[7/19] Fixing config encoding..." -ForegroundColor Yellow
168
- $configPath = Join-Path $InstallDir "agent.config.json"
169
- if (-not (Test-Path $configPath)) { Write-Host " FATAL: config not created!" -ForegroundColor Red; exit 1 }
170
- $raw = [System.IO.File]::ReadAllText($configPath)
171
- [System.IO.File]::WriteAllText($configPath, $raw.TrimStart([char]0xFEFF), [System.Text.UTF8Encoding]::new($false))
172
-
173
- # --- 8. Verify config ---
174
- Write-Host "[8/19] Verifying config..." -ForegroundColor Yellow
175
- Push-Location $InstallDir
176
- $ErrorActionPreference = "Continue"
177
- $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
178
- $ErrorActionPreference = "Stop"
179
- if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: invalid config: $jsonCheck" -ForegroundColor Red; Pop-Location; exit 1 }
180
- Pop-Location
181
- Write-Host " $jsonCheck"
182
-
183
- # --- 9. Download NSSM ---
184
- Write-Host "[9/19] Setting up NSSM..." -ForegroundColor Yellow
185
- if (-not (Test-Path $NssmExe)) {
186
- # Check bundled copy first (fastest, no internet needed)
187
- $bundled = Join-Path $AgentDir "nssm\nssm.exe"
188
- if (Test-Path $bundled) {
189
- Copy-Item $bundled $NssmExe -Force
190
- Write-Host " Using bundled NSSM"
191
- } else {
192
- # Download from multiple sources
193
- $nssmZip = "$env:TEMP\nssm.zip"
194
- $downloaded = $false
195
- $urls = @(
196
- "https://nssm.cc/release/nssm-2.24.zip",
197
- "https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip"
198
- )
199
- foreach ($url in $urls) {
200
- if ($downloaded) { break }
201
- Write-Host " Downloading from $url ..."
202
- try {
203
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
204
- Invoke-WebRequest -Uri $url -OutFile $nssmZip -UseBasicParsing -TimeoutSec 60
205
- if ((Test-Path $nssmZip) -and (Get-Item $nssmZip).Length -gt 10000) {
206
- $downloaded = $true
207
- }
208
- } catch {
209
- Write-Host " Failed: $_" -ForegroundColor DarkYellow
210
- }
211
- }
212
- if ($downloaded) {
213
- Expand-Archive -Path $nssmZip -DestinationPath "$env:TEMP\nssm-extract" -Force
214
- # Find nssm.exe in extracted folder (handles different zip structures)
215
- $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Where-Object { $_.DirectoryName -like "*win64*" } | Select-Object -First 1
216
- if (-not $found) { $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Select-Object -First 1 }
217
- if ($found) { Copy-Item $found.FullName $NssmExe -Force }
218
- Remove-Item $nssmZip -Force -ErrorAction SilentlyContinue
219
- Remove-Item "$env:TEMP\nssm-extract" -Recurse -Force -ErrorAction SilentlyContinue
220
- }
221
- }
222
- }
223
- if (-not (Test-Path $NssmExe)) {
224
- Write-Host ""
225
- Write-Host " NSSM download failed. Manual fix:" -ForegroundColor Red
226
- Write-Host " 1. Download nssm-2.24.zip from https://nssm.cc/release/nssm-2.24.zip" -ForegroundColor Yellow
227
- Write-Host " 2. Extract win64\nssm.exe to: $NssmExe" -ForegroundColor Yellow
228
- Write-Host " 3. Re-run this script" -ForegroundColor Yellow
229
- Write-Host ""
230
- Write-Host " OR bundle it in the repo:" -ForegroundColor Yellow
231
- Write-Host " Copy nssm.exe to: $AgentDir\nssm\nssm.exe" -ForegroundColor Yellow
232
- exit 1
233
- }
234
- Write-Host " NSSM ready: $NssmExe"
235
-
236
- # --- 10. Install Windows Service via NSSM ---
237
- Write-Host "[10/19] Installing Windows Service..." -ForegroundColor Yellow
238
-
239
- # Clean slate
240
- $ErrorActionPreference = "Continue"
241
- & $NssmExe stop $ServiceName 2>$null
242
- & $NssmExe remove $ServiceName confirm 2>$null
243
- sc.exe delete $ServiceName 2>$null
244
- Start-Sleep -Seconds 2
245
- $ErrorActionPreference = "Stop"
246
-
247
- $nodePath = (Get-Command node).Source
248
-
249
- # Install
250
- & $NssmExe install $ServiceName $nodePath "dist\index.js"
251
- if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: NSSM install failed!" -ForegroundColor Red; exit 1 }
252
-
253
- # Configure
254
- & $NssmExe set $ServiceName AppDirectory $InstallDir
255
- & $NssmExe set $ServiceName DisplayName "LIGHTMAN Agent"
256
- & $NssmExe set $ServiceName Description "LIGHTMAN kiosk agent - display management and monitoring"
257
- & $NssmExe set $ServiceName Start SERVICE_AUTO_START
258
- & $NssmExe set $ServiceName AppStdout "$LogDir\service-stdout.log"
259
- & $NssmExe set $ServiceName AppStderr "$LogDir\service-stderr.log"
260
- & $NssmExe set $ServiceName AppStdoutCreationDisposition 4
261
- & $NssmExe set $ServiceName AppStderrCreationDisposition 4
262
- & $NssmExe set $ServiceName AppRotateFiles 1
263
- & $NssmExe set $ServiceName AppRotateBytes 5242880
264
- & $NssmExe set $ServiceName AppRestartDelay 10000
265
- & $NssmExe set $ServiceName AppExit Default Restart
266
-
267
- # Verify service was created
268
- Start-Sleep -Seconds 2
269
- $svcCheck = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
270
- if (-not $svcCheck) {
271
- $svcCheck = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1
272
- }
273
- if (-not $svcCheck) {
274
- Write-Host " FATAL: Service was not created!" -ForegroundColor Red
275
- exit 1
276
- }
277
- Write-Host " Service installed: $($svcCheck.Name)" -ForegroundColor Green
278
-
279
- # Recovery policy
280
- sc.exe failure $svcCheck.Name reset= 86400 actions= restart/5000/restart/10000/restart/30000 2>$null
281
-
282
- # --- 11. Start service ---
283
- Write-Host "[11/19] Starting service..." -ForegroundColor Yellow
284
- Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
285
- Start-Sleep -Seconds 5
286
- $svcCheck.Refresh()
287
-
288
- if ($svcCheck.Status -eq 'Running') {
289
- Write-Host " Service is RUNNING" -ForegroundColor Green
290
- } else {
291
- Write-Host " Service status: $($svcCheck.Status) - check $LogDir" -ForegroundColor Yellow
292
- Start-Sleep -Seconds 3
293
- Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
294
- }
295
-
296
- # Wait for port 3403
297
- $portUp = $false
298
- for ($i = 0; $i -lt 10; $i++) {
299
- $ErrorActionPreference = "Continue"
300
- $n = netstat -an 2>$null | findstr ":3403.*LISTENING" 2>$null
301
- $ErrorActionPreference = "Stop"
302
- if ($n) { $portUp = $true; break }
303
- Start-Sleep -Seconds 2
304
- }
148
+
149
+ # --- 5. Install deps ---
150
+ Write-Host "[5/19] Installing dependencies..." -ForegroundColor Yellow
151
+ Push-Location $InstallDir
152
+ $ErrorActionPreference = "Continue"
153
+ & npm ci --omit=dev --ignore-scripts 2>&1 | Out-Host
154
+ if ($LASTEXITCODE -ne 0) { & npm install --omit=dev --ignore-scripts 2>&1 | Out-Host }
155
+ $ErrorActionPreference = "Stop"
156
+ Pop-Location
157
+
158
+ # --- 6. Generate config ---
159
+ Write-Host "[6/19] Generating config..." -ForegroundColor Yellow
160
+ if ($ShellReplace) {
161
+ & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir -ShellMode
162
+ } else {
163
+ & "$ScriptDir\setup.ps1" -Slug $Slug -Server $Server -Timezone $Timezone -InstallDir $InstallDir
164
+ }
165
+
166
+ # --- 7. Fix BOM ---
167
+ Write-Host "[7/19] Fixing config encoding..." -ForegroundColor Yellow
168
+ $configPath = Join-Path $InstallDir "agent.config.json"
169
+ if (-not (Test-Path $configPath)) { Write-Host " FATAL: config not created!" -ForegroundColor Red; exit 1 }
170
+ $raw = [System.IO.File]::ReadAllText($configPath)
171
+ [System.IO.File]::WriteAllText($configPath, $raw.TrimStart([char]0xFEFF), [System.Text.UTF8Encoding]::new($false))
172
+
173
+ # --- 8. Verify config ---
174
+ Write-Host "[8/19] Verifying config..." -ForegroundColor Yellow
175
+ Push-Location $InstallDir
176
+ $ErrorActionPreference = "Continue"
177
+ $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
178
+ $ErrorActionPreference = "Stop"
179
+ if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: invalid config: $jsonCheck" -ForegroundColor Red; Pop-Location; exit 1 }
180
+ Pop-Location
181
+ Write-Host " $jsonCheck"
182
+
183
+ # --- 9. Download NSSM ---
184
+ Write-Host "[9/19] Setting up NSSM..." -ForegroundColor Yellow
185
+ if (-not (Test-Path $NssmExe)) {
186
+ # Check bundled copy first (fastest, no internet needed)
187
+ $bundled = Join-Path $AgentDir "nssm\nssm.exe"
188
+ if (Test-Path $bundled) {
189
+ Copy-Item $bundled $NssmExe -Force
190
+ Write-Host " Using bundled NSSM"
191
+ } else {
192
+ # Download from multiple sources
193
+ $nssmZip = "$env:TEMP\nssm.zip"
194
+ $downloaded = $false
195
+ $urls = @(
196
+ "https://nssm.cc/release/nssm-2.24.zip",
197
+ "https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip"
198
+ )
199
+ foreach ($url in $urls) {
200
+ if ($downloaded) { break }
201
+ Write-Host " Downloading from $url ..."
202
+ try {
203
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
204
+ Invoke-WebRequest -Uri $url -OutFile $nssmZip -UseBasicParsing -TimeoutSec 60
205
+ if ((Test-Path $nssmZip) -and (Get-Item $nssmZip).Length -gt 10000) {
206
+ $downloaded = $true
207
+ }
208
+ } catch {
209
+ Write-Host " Failed: $_" -ForegroundColor DarkYellow
210
+ }
211
+ }
212
+ if ($downloaded) {
213
+ Expand-Archive -Path $nssmZip -DestinationPath "$env:TEMP\nssm-extract" -Force
214
+ # Find nssm.exe in extracted folder (handles different zip structures)
215
+ $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Where-Object { $_.DirectoryName -like "*win64*" } | Select-Object -First 1
216
+ if (-not $found) { $found = Get-ChildItem "$env:TEMP\nssm-extract" -Recurse -Filter "nssm.exe" | Select-Object -First 1 }
217
+ if ($found) { Copy-Item $found.FullName $NssmExe -Force }
218
+ Remove-Item $nssmZip -Force -ErrorAction SilentlyContinue
219
+ Remove-Item "$env:TEMP\nssm-extract" -Recurse -Force -ErrorAction SilentlyContinue
220
+ }
221
+ }
222
+ }
223
+ if (-not (Test-Path $NssmExe)) {
224
+ Write-Host ""
225
+ Write-Host " NSSM download failed. Manual fix:" -ForegroundColor Red
226
+ Write-Host " 1. Download nssm-2.24.zip from https://nssm.cc/release/nssm-2.24.zip" -ForegroundColor Yellow
227
+ Write-Host " 2. Extract win64\nssm.exe to: $NssmExe" -ForegroundColor Yellow
228
+ Write-Host " 3. Re-run this script" -ForegroundColor Yellow
229
+ Write-Host ""
230
+ Write-Host " OR bundle it in the repo:" -ForegroundColor Yellow
231
+ Write-Host " Copy nssm.exe to: $AgentDir\nssm\nssm.exe" -ForegroundColor Yellow
232
+ exit 1
233
+ }
234
+ Write-Host " NSSM ready: $NssmExe"
235
+
236
+ # --- 10. Install Windows Service via NSSM ---
237
+ Write-Host "[10/19] Installing Windows Service..." -ForegroundColor Yellow
238
+
239
+ # Clean slate
240
+ $ErrorActionPreference = "Continue"
241
+ & $NssmExe stop $ServiceName 2>$null
242
+ & $NssmExe remove $ServiceName confirm 2>$null
243
+ sc.exe delete $ServiceName 2>$null
244
+ Start-Sleep -Seconds 2
245
+ $ErrorActionPreference = "Stop"
246
+
247
+ $nodePath = (Get-Command node).Source
248
+
249
+ # Install
250
+ & $NssmExe install $ServiceName $nodePath "dist\index.js"
251
+ if ($LASTEXITCODE -ne 0) { Write-Host " FATAL: NSSM install failed!" -ForegroundColor Red; exit 1 }
252
+
253
+ # Configure
254
+ & $NssmExe set $ServiceName AppDirectory $InstallDir
255
+ & $NssmExe set $ServiceName DisplayName "LIGHTMAN Agent"
256
+ & $NssmExe set $ServiceName Description "LIGHTMAN kiosk agent - display management and monitoring"
257
+ & $NssmExe set $ServiceName Start SERVICE_AUTO_START
258
+ & $NssmExe set $ServiceName AppStdout "$LogDir\service-stdout.log"
259
+ & $NssmExe set $ServiceName AppStderr "$LogDir\service-stderr.log"
260
+ & $NssmExe set $ServiceName AppStdoutCreationDisposition 4
261
+ & $NssmExe set $ServiceName AppStderrCreationDisposition 4
262
+ & $NssmExe set $ServiceName AppRotateFiles 1
263
+ & $NssmExe set $ServiceName AppRotateBytes 5242880
264
+ & $NssmExe set $ServiceName AppRestartDelay 10000
265
+ & $NssmExe set $ServiceName AppExit Default Restart
266
+
267
+ # Verify service was created
268
+ Start-Sleep -Seconds 2
269
+ $svcCheck = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
270
+ if (-not $svcCheck) {
271
+ $svcCheck = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1
272
+ }
273
+ if (-not $svcCheck) {
274
+ Write-Host " FATAL: Service was not created!" -ForegroundColor Red
275
+ exit 1
276
+ }
277
+ Write-Host " Service installed: $($svcCheck.Name)" -ForegroundColor Green
278
+
279
+ # Recovery policy
280
+ sc.exe failure $svcCheck.Name reset= 86400 actions= restart/5000/restart/10000/restart/30000 2>$null
281
+
282
+ # --- 11. Start service ---
283
+ Write-Host "[11/19] Starting service..." -ForegroundColor Yellow
284
+ Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
285
+ Start-Sleep -Seconds 5
286
+ $svcCheck.Refresh()
287
+
288
+ if ($svcCheck.Status -eq 'Running') {
289
+ Write-Host " Service is RUNNING" -ForegroundColor Green
290
+ } else {
291
+ Write-Host " Service status: $($svcCheck.Status) - check $LogDir" -ForegroundColor Yellow
292
+ Start-Sleep -Seconds 3
293
+ Start-Service -Name $svcCheck.Name -ErrorAction SilentlyContinue
294
+ }
295
+
296
+ # Wait for port 3403
297
+ $portUp = $false
298
+ for ($i = 0; $i -lt 10; $i++) {
299
+ $ErrorActionPreference = "Continue"
300
+ $n = netstat -an 2>$null | findstr ":3403.*LISTENING" 2>$null
301
+ $ErrorActionPreference = "Stop"
302
+ if ($n) { $portUp = $true; break }
303
+ Start-Sleep -Seconds 2
304
+ }
305
305
  if ($portUp) { Write-Host " Port 3403 LISTENING" -ForegroundColor Green }
306
306
  else { Write-Host " Port 3403 not yet up (may take a moment)" -ForegroundColor Yellow }
307
307
 
@@ -350,238 +350,238 @@ Write-Host " Provisioning/pairing complete" -ForegroundColor Green
350
350
 
351
351
  # --- 12. Firewall ---
352
352
  Write-Host "[12/19] Configuring firewall..." -ForegroundColor Yellow
353
- $ErrorActionPreference = "Continue"
354
- if (-not (Get-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue)) {
355
- New-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -Direction Outbound -Action Allow -Protocol TCP -RemotePort 3001 -Description "LIGHTMAN Agent" | Out-Null
356
- Write-Host " Created"
357
- } else { Write-Host " Already exists" }
358
-
359
- # ============================================================
360
- # PART 2: KIOSK CONFIGURATION
361
- # ============================================================
362
- $ErrorActionPreference = "Continue"
363
- Write-Host ""
364
- Write-Host "--- Configuring Kiosk Mode ---" -ForegroundColor Cyan
365
- Write-Host ""
366
-
367
- # --- 13. Auto-login ---
368
- Write-Host "[13/19] Enabling auto-login..." -ForegroundColor Yellow
369
- $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
370
- $targetUser = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue
371
- $isMsAccount = $targetUser -and $targetUser.PrincipalSource -eq 'MicrosoftAccount'
372
-
373
- if ($isMsAccount) {
374
- $KioskUser = "kiosk"
375
- $existingKiosk = Get-LocalUser -Name $KioskUser -ErrorAction SilentlyContinue
376
- if (-not $existingKiosk) { net user $KioskUser "" /add 2>$null; net localgroup Administrators $KioskUser /add 2>$null }
377
- else { net user $KioskUser "" 2>$null }
378
- $HidePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
379
- if (-not (Test-Path $HidePath)) { New-Item -Path $HidePath -Force | Out-Null }
380
- Set-ItemProperty -Path $HidePath -Name $Username -Value 0
381
- $Username = $KioskUser
382
- Write-Host " Created kiosk account, auto-login: $Username" -ForegroundColor Green
383
- } else {
384
- net user $Username "" 2>$null
385
- }
386
-
387
- $PwdLess = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device"
388
- if (Test-Path $PwdLess) { Set-ItemProperty -Path $PwdLess -Name "DevicePasswordLessBuildVersion" -Value 0 }
389
-
390
- $Passport = "HKLM:\SOFTWARE\Policies\Microsoft\PassportForWork"
391
- if (-not (Test-Path $Passport)) { New-Item -Path $Passport -Force | Out-Null }
392
- Set-ItemProperty -Path $Passport -Name "Enabled" -Value 0
393
-
394
- Set-ItemProperty -Path $RegPath -Name "AutoAdminLogon" -Value "1"
395
- Set-ItemProperty -Path $RegPath -Name "DefaultUserName" -Value $Username
396
- Set-ItemProperty -Path $RegPath -Name "DefaultPassword" -Value ""
397
- Set-ItemProperty -Path $RegPath -Name "DefaultDomainName" -Value ""
398
- Set-ItemProperty -Path $RegPath -Name "DisableCAD" -Value 1
399
- Set-ItemProperty -Path $RegPath -Name "AutoRestartShell" -Value 1
400
- Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DisableAutomaticRestartSignOn" -Value 0
401
- $OOBE = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE"
402
- if (-not (Test-Path $OOBE)) { New-Item -Path $OOBE -Force | Out-Null }
403
- Set-ItemProperty -Path $OOBE -Name "DisablePrivacyExperience" -Value 1
404
- Write-Host " Auto-login enabled for: $Username"
405
-
406
- # --- 14. Lock screen ---
407
- Write-Host "[14/19] Removing lock screen..." -ForegroundColor Yellow
408
- $LP = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization"
409
- if (-not (Test-Path $LP)) { New-Item -Path $LP -Force | Out-Null }
410
- Set-ItemProperty -Path $LP -Name "NoLockScreen" -Value 1
411
-
412
- $SD = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
413
- if (Test-Path $SD) { Set-ItemProperty -Path $SD -Name "AllowLockScreen" -Value 0 -ErrorAction SilentlyContinue }
414
-
415
- $CC = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
416
- if (-not (Test-Path $CC)) { New-Item -Path $CC -Force | Out-Null }
417
- Set-ItemProperty -Path $CC -Name "DisableWindowsConsumerFeatures" -Value 1
418
- Set-ItemProperty -Path $CC -Name "DisableCloudOptimizedContent" -Value 1
419
- $CCU = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
420
- if (-not (Test-Path $CCU)) { New-Item -Path $CCU -Force | Out-Null }
421
- Set-ItemProperty -Path $CCU -Name "DisableWindowsSpotlightFeatures" -Value 1
422
- Set-ItemProperty -Path $CCU -Name "DisableTailoredExperiencesWithDiagnosticData" -Value 1
423
-
424
- Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableFirstLogonAnimation" -Value 0
425
- $SP = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
426
- Set-ItemProperty -Path $SP -Name "DisableLockWorkstation" -Value 1
427
- Set-ItemProperty -Path $SP -Name "HideFastUserSwitching" -Value 1
428
-
429
- $DL = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
430
- if (-not (Test-Path $DL)) { New-Item -Path $DL -Force | Out-Null }
431
- Set-ItemProperty -Path $DL -Name "EnableGoodbye" -Value 0
432
-
433
- $PS = "HKLM:\SOFTWARE\Policies\Microsoft\Power\PowerSettings\0e796bdb-100d-47d6-a2d5-f7d2daa51f51"
434
- if (-not (Test-Path $PS)) { New-Item -Path $PS -Force | Out-Null }
435
- Set-ItemProperty -Path $PS -Name "ACSettingIndex" -Value 0
436
- Set-ItemProperty -Path $PS -Name "DCSettingIndex" -Value 0
437
- powercfg /SETACVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
438
- powercfg /SETDCVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
439
- powercfg /SETACTIVE SCHEME_CURRENT 2>&1 | Out-Null
440
-
441
- Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaverIsSecure" -Value "0"
442
- Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaveActive" -Value "0"
443
- Set-ItemProperty -Path $SP -Name "InactivityTimeoutSecs" -Value 0 -ErrorAction SilentlyContinue
444
- try { Disable-ScheduledTask -TaskName "\Microsoft\Windows\Shell\CreateObjectTask" -ErrorAction SilentlyContinue | Out-Null } catch { }
445
- Write-Host " Lock screen fully disabled"
446
-
447
- # --- 15. Sleep ---
448
- Write-Host "[15/19] Disabling sleep..." -ForegroundColor Yellow
449
- powercfg /change monitor-timeout-ac 0 2>&1 | Out-Null
450
- powercfg /change standby-timeout-ac 0 2>&1 | Out-Null
451
- powercfg /change hibernate-timeout-ac 0 2>&1 | Out-Null
452
-
453
- # --- 16. Harden ---
454
- Write-Host "[16/19] Hardening Windows..." -ForegroundColor Yellow
455
- $WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
456
- if (-not (Test-Path $WU)) { New-Item -Path $WU -Force | Out-Null }
457
- Set-ItemProperty -Path $WU -Name "NoAutoRebootWithLoggedOnUsers" -Value 1
458
- Set-ItemProperty -Path $WU -Name "AUOptions" -Value 2
459
- $WUM = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
460
- if (-not (Test-Path $WUM)) { New-Item -Path $WUM -Force | Out-Null }
461
- Set-ItemProperty -Path $WUM -Name "SetAutoRestartNotificationDisable" -Value 1
462
- Set-ItemProperty -Path $WUM -Name "SetActiveHours" -Value 1
463
- Set-ItemProperty -Path $WUM -Name "ActiveHoursStart" -Value 0
464
- Set-ItemProperty -Path $WUM -Name "ActiveHoursEnd" -Value 23
465
-
466
- $NP = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer"
467
- if (-not (Test-Path $NP)) { New-Item -Path $NP -Force | Out-Null }
468
- Set-ItemProperty -Path $NP -Name "DisableNotificationCenter" -Value 1
469
- $TP = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications"
470
- if (-not (Test-Path $TP)) { New-Item -Path $TP -Force | Out-Null }
471
- Set-ItemProperty -Path $TP -Name "ToastEnabled" -Value 0
472
-
473
- $WER = "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting"
474
- if (-not (Test-Path $WER)) { New-Item -Path $WER -Force | Out-Null }
475
- Set-ItemProperty -Path $WER -Name "DontShowUI" -Value 1
476
- Set-ItemProperty -Path $WER -Name "Disabled" -Value 1
477
- Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Windows" -Name "ErrorMode" -Value 2 -ErrorAction SilentlyContinue
478
-
479
- $SR = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search"
480
- if (-not (Test-Path $SR)) { New-Item -Path $SR -Force | Out-Null }
481
- Set-ItemProperty -Path $SR -Name "AllowCortana" -Value 0
482
- Write-Host " Done"
483
-
484
- # --- 17. Kiosk Chrome ---
485
- if ($ShellReplace) {
486
- Write-Host "[17/19] SHELL REPLACEMENT..." -ForegroundColor Magenta
487
-
488
- # Copy shell BAT (reads slug from agent.config.json - single source of truth)
489
- $shellSource = Join-Path $ScriptDir "lightman-shell.bat"
490
- $shellTarget = Join-Path $InstallDir "lightman-shell.bat"
491
- if (Test-Path $shellSource) { Copy-Item $shellSource $shellTarget -Force }
492
-
493
- # No sidecar file needed - shell BAT reads directly from agent.config.json
494
-
495
- # Replace shell
496
- $ShellReg = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
497
- if (-not (Test-Path $ShellReg)) { New-Item -Path $ShellReg -Force | Out-Null }
498
- Set-ItemProperty -Path $ShellReg -Name "Shell" -Value """$shellTarget"""
499
- $HKLMShell = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
500
- $orig = (Get-ItemProperty -Path $HKLMShell -Name "Shell" -ErrorAction SilentlyContinue).Shell
501
- if ($orig -and $orig -notlike "*lightman*") { Set-ItemProperty -Path $HKLMShell -Name "Shell_Original" -Value $orig }
502
- Set-ItemProperty -Path $HKLMShell -Name "Shell" -Value """$shellTarget"""
503
-
504
- Write-Host " Shell replaced -> lightman-shell.bat" -ForegroundColor Green
505
- Write-Host " Recovery: scripts\restore-desktop.ps1" -ForegroundColor Yellow
506
-
507
- # Remove kiosk task if exists
508
- $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
509
- if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
510
- } else {
511
- Write-Host "[17/19] Standard mode - kiosk browser task..." -ForegroundColor Yellow
512
- $vbs = Join-Path $ScriptDir "launch-kiosk.vbs"
513
- $vbsT = Join-Path $InstallDir "launch-kiosk.vbs"
514
- if (Test-Path $vbs) { Copy-Item $vbs $vbsT -Force }
515
- $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
516
- if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
517
- $kA = New-ScheduledTaskAction -Execute "wscript.exe" -Argument """$vbsT""" -WorkingDirectory $InstallDir
518
- $kT1 = New-ScheduledTaskTrigger -AtLogOn -User $Username
519
- $kT2 = New-ScheduledTaskTrigger -AtStartup
520
- $kS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
521
- Register-ScheduledTask -TaskName $KioskTask -Action $kA -Trigger @($kT1,$kT2) -Settings $kS -RunLevel Highest -Description "Chrome kiosk at logon/startup" -Force | Out-Null
522
- Write-Host " Kiosk browser task registered"
523
- }
524
-
525
- # --- 18. Guardian ---
526
- Write-Host "[18/19] Registering Guardian..." -ForegroundColor Yellow
527
- $gSrc = Join-Path $ScriptDir "guardian.ps1"
528
- $gDst = Join-Path $InstallDir "guardian.ps1"
529
- if (Test-Path $gSrc) { Copy-Item $gSrc $gDst -Force }
530
- $gt = Get-ScheduledTask -TaskName $GuardianTask -ErrorAction SilentlyContinue
531
- if ($gt) { Unregister-ScheduledTask -TaskName $GuardianTask -Confirm:$false }
532
- $gA = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File ""$gDst""" -WorkingDirectory $InstallDir
533
- $gT = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 365)
534
- $gS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 2)
535
- $gP = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
536
- Register-ScheduledTask -TaskName $GuardianTask -Action $gA -Trigger $gT -Settings $gS -Principal $gP -Description "LIGHTMAN health check every 5 min" -Force | Out-Null
537
-
538
- foreach ($task in @("\Microsoft\Windows\UpdateOrchestrator\Reboot","\Microsoft\Windows\UpdateOrchestrator\Schedule Retry Scan","\Microsoft\Windows\WindowsUpdate\Scheduled Start")) {
539
- try { Disable-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue | Out-Null } catch { }
540
- }
541
- Write-Host " Guardian registered"
542
-
543
- # --- 19. Final verification ---
544
- Write-Host "[19/19] Verification..." -ForegroundColor Yellow
545
- Start-Sleep -Seconds 3
546
-
547
- $finalSvc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
548
- if (-not $finalSvc) { $finalSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1 }
549
- $svcStatus = if ($finalSvc) { "$($finalSvc.Status)" } else { "NOT FOUND" }
550
-
551
- $cfgOk = $false
552
- try {
553
- Push-Location $InstallDir
554
- $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
555
- $cfgData = $cfgResult | ConvertFrom-Json
556
- Pop-Location
557
- $cfgOk = $true
558
- } catch { Pop-Location }
559
-
560
- Write-Host ""
561
- Write-Host "=============================================" -ForegroundColor Green
562
- Write-Host " INSTALLATION COMPLETE" -ForegroundColor Green
563
- Write-Host "=============================================" -ForegroundColor Green
564
- Write-Host ""
565
- Write-Host " Slug : $Slug"
566
- Write-Host " Server : $Server"
567
- Write-Host " Install : $InstallDir"
568
- Write-Host " Logs : $LogDir"
569
- Write-Host " User : $Username"
570
- Write-Host ""
571
- Write-Host " Service : $svcStatus" -ForegroundColor $(if ($svcStatus -eq 'Running') { 'Green' } else { 'Red' })
572
- if ($cfgOk) {
573
- Write-Host " Config slug: $($cfgData.slug)" -ForegroundColor $(if ($cfgData.slug -eq $Slug) { 'Green' } else { 'Red' })
574
- Write-Host " Shell mode : $($cfgData.shell)" -ForegroundColor $(if ($cfgData.shell -eq $ShellReplace.IsPresent) { 'Green' } else { 'Red' })
575
- }
576
- Write-Host ""
577
- Write-Host " Manage:" -ForegroundColor DarkGray
578
- Write-Host " $NssmExe stop $ServiceName" -ForegroundColor DarkGray
579
- Write-Host " $NssmExe start $ServiceName" -ForegroundColor DarkGray
580
- Write-Host " $NssmExe restart $ServiceName" -ForegroundColor DarkGray
581
- Write-Host ""
582
- Write-Host " BIOS (manual):" -ForegroundColor Red
583
- Write-Host " After Power Loss = Power On" -ForegroundColor Red
584
- Write-Host " Wake-on-LAN = Enabled" -ForegroundColor Red
585
- Write-Host ""
586
- Write-Host " REBOOT NOW: Restart-Computer" -ForegroundColor Yellow
587
- Write-Host ""
353
+ $ErrorActionPreference = "Continue"
354
+ if (-not (Get-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue)) {
355
+ New-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -Direction Outbound -Action Allow -Protocol TCP -RemotePort 3001 -Description "LIGHTMAN Agent" | Out-Null
356
+ Write-Host " Created"
357
+ } else { Write-Host " Already exists" }
358
+
359
+ # ============================================================
360
+ # PART 2: KIOSK CONFIGURATION
361
+ # ============================================================
362
+ $ErrorActionPreference = "Continue"
363
+ Write-Host ""
364
+ Write-Host "--- Configuring Kiosk Mode ---" -ForegroundColor Cyan
365
+ Write-Host ""
366
+
367
+ # --- 13. Auto-login ---
368
+ Write-Host "[13/19] Enabling auto-login..." -ForegroundColor Yellow
369
+ $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
370
+ $targetUser = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue
371
+ $isMsAccount = $targetUser -and $targetUser.PrincipalSource -eq 'MicrosoftAccount'
372
+
373
+ if ($isMsAccount) {
374
+ $KioskUser = "kiosk"
375
+ $existingKiosk = Get-LocalUser -Name $KioskUser -ErrorAction SilentlyContinue
376
+ if (-not $existingKiosk) { net user $KioskUser "" /add 2>$null; net localgroup Administrators $KioskUser /add 2>$null }
377
+ else { net user $KioskUser "" 2>$null }
378
+ $HidePath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList"
379
+ if (-not (Test-Path $HidePath)) { New-Item -Path $HidePath -Force | Out-Null }
380
+ Set-ItemProperty -Path $HidePath -Name $Username -Value 0
381
+ $Username = $KioskUser
382
+ Write-Host " Created kiosk account, auto-login: $Username" -ForegroundColor Green
383
+ } else {
384
+ net user $Username "" 2>$null
385
+ }
386
+
387
+ $PwdLess = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device"
388
+ if (Test-Path $PwdLess) { Set-ItemProperty -Path $PwdLess -Name "DevicePasswordLessBuildVersion" -Value 0 }
389
+
390
+ $Passport = "HKLM:\SOFTWARE\Policies\Microsoft\PassportForWork"
391
+ if (-not (Test-Path $Passport)) { New-Item -Path $Passport -Force | Out-Null }
392
+ Set-ItemProperty -Path $Passport -Name "Enabled" -Value 0
393
+
394
+ Set-ItemProperty -Path $RegPath -Name "AutoAdminLogon" -Value "1"
395
+ Set-ItemProperty -Path $RegPath -Name "DefaultUserName" -Value $Username
396
+ Set-ItemProperty -Path $RegPath -Name "DefaultPassword" -Value ""
397
+ Set-ItemProperty -Path $RegPath -Name "DefaultDomainName" -Value ""
398
+ Set-ItemProperty -Path $RegPath -Name "DisableCAD" -Value 1
399
+ Set-ItemProperty -Path $RegPath -Name "AutoRestartShell" -Value 1
400
+ Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DisableAutomaticRestartSignOn" -Value 0
401
+ $OOBE = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE"
402
+ if (-not (Test-Path $OOBE)) { New-Item -Path $OOBE -Force | Out-Null }
403
+ Set-ItemProperty -Path $OOBE -Name "DisablePrivacyExperience" -Value 1
404
+ Write-Host " Auto-login enabled for: $Username"
405
+
406
+ # --- 14. Lock screen ---
407
+ Write-Host "[14/19] Removing lock screen..." -ForegroundColor Yellow
408
+ $LP = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Personalization"
409
+ if (-not (Test-Path $LP)) { New-Item -Path $LP -Force | Out-Null }
410
+ Set-ItemProperty -Path $LP -Name "NoLockScreen" -Value 1
411
+
412
+ $SD = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData"
413
+ if (Test-Path $SD) { Set-ItemProperty -Path $SD -Name "AllowLockScreen" -Value 0 -ErrorAction SilentlyContinue }
414
+
415
+ $CC = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
416
+ if (-not (Test-Path $CC)) { New-Item -Path $CC -Force | Out-Null }
417
+ Set-ItemProperty -Path $CC -Name "DisableWindowsConsumerFeatures" -Value 1
418
+ Set-ItemProperty -Path $CC -Name "DisableCloudOptimizedContent" -Value 1
419
+ $CCU = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\CloudContent"
420
+ if (-not (Test-Path $CCU)) { New-Item -Path $CCU -Force | Out-Null }
421
+ Set-ItemProperty -Path $CCU -Name "DisableWindowsSpotlightFeatures" -Value 1
422
+ Set-ItemProperty -Path $CCU -Name "DisableTailoredExperiencesWithDiagnosticData" -Value 1
423
+
424
+ Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableFirstLogonAnimation" -Value 0
425
+ $SP = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
426
+ Set-ItemProperty -Path $SP -Name "DisableLockWorkstation" -Value 1
427
+ Set-ItemProperty -Path $SP -Name "HideFastUserSwitching" -Value 1
428
+
429
+ $DL = "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
430
+ if (-not (Test-Path $DL)) { New-Item -Path $DL -Force | Out-Null }
431
+ Set-ItemProperty -Path $DL -Name "EnableGoodbye" -Value 0
432
+
433
+ $PS = "HKLM:\SOFTWARE\Policies\Microsoft\Power\PowerSettings\0e796bdb-100d-47d6-a2d5-f7d2daa51f51"
434
+ if (-not (Test-Path $PS)) { New-Item -Path $PS -Force | Out-Null }
435
+ Set-ItemProperty -Path $PS -Name "ACSettingIndex" -Value 0
436
+ Set-ItemProperty -Path $PS -Name "DCSettingIndex" -Value 0
437
+ powercfg /SETACVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
438
+ powercfg /SETDCVALUEINDEX SCHEME_CURRENT SUB_NONE CONSOLELOCK 0 2>&1 | Out-Null
439
+ powercfg /SETACTIVE SCHEME_CURRENT 2>&1 | Out-Null
440
+
441
+ Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaverIsSecure" -Value "0"
442
+ Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaveActive" -Value "0"
443
+ Set-ItemProperty -Path $SP -Name "InactivityTimeoutSecs" -Value 0 -ErrorAction SilentlyContinue
444
+ try { Disable-ScheduledTask -TaskName "\Microsoft\Windows\Shell\CreateObjectTask" -ErrorAction SilentlyContinue | Out-Null } catch { }
445
+ Write-Host " Lock screen fully disabled"
446
+
447
+ # --- 15. Sleep ---
448
+ Write-Host "[15/19] Disabling sleep..." -ForegroundColor Yellow
449
+ powercfg /change monitor-timeout-ac 0 2>&1 | Out-Null
450
+ powercfg /change standby-timeout-ac 0 2>&1 | Out-Null
451
+ powercfg /change hibernate-timeout-ac 0 2>&1 | Out-Null
452
+
453
+ # --- 16. Harden ---
454
+ Write-Host "[16/19] Hardening Windows..." -ForegroundColor Yellow
455
+ $WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
456
+ if (-not (Test-Path $WU)) { New-Item -Path $WU -Force | Out-Null }
457
+ Set-ItemProperty -Path $WU -Name "NoAutoRebootWithLoggedOnUsers" -Value 1
458
+ Set-ItemProperty -Path $WU -Name "AUOptions" -Value 2
459
+ $WUM = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
460
+ if (-not (Test-Path $WUM)) { New-Item -Path $WUM -Force | Out-Null }
461
+ Set-ItemProperty -Path $WUM -Name "SetAutoRestartNotificationDisable" -Value 1
462
+ Set-ItemProperty -Path $WUM -Name "SetActiveHours" -Value 1
463
+ Set-ItemProperty -Path $WUM -Name "ActiveHoursStart" -Value 0
464
+ Set-ItemProperty -Path $WUM -Name "ActiveHoursEnd" -Value 23
465
+
466
+ $NP = "HKCU:\SOFTWARE\Policies\Microsoft\Windows\Explorer"
467
+ if (-not (Test-Path $NP)) { New-Item -Path $NP -Force | Out-Null }
468
+ Set-ItemProperty -Path $NP -Name "DisableNotificationCenter" -Value 1
469
+ $TP = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications"
470
+ if (-not (Test-Path $TP)) { New-Item -Path $TP -Force | Out-Null }
471
+ Set-ItemProperty -Path $TP -Name "ToastEnabled" -Value 0
472
+
473
+ $WER = "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting"
474
+ if (-not (Test-Path $WER)) { New-Item -Path $WER -Force | Out-Null }
475
+ Set-ItemProperty -Path $WER -Name "DontShowUI" -Value 1
476
+ Set-ItemProperty -Path $WER -Name "Disabled" -Value 1
477
+ Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Windows" -Name "ErrorMode" -Value 2 -ErrorAction SilentlyContinue
478
+
479
+ $SR = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search"
480
+ if (-not (Test-Path $SR)) { New-Item -Path $SR -Force | Out-Null }
481
+ Set-ItemProperty -Path $SR -Name "AllowCortana" -Value 0
482
+ Write-Host " Done"
483
+
484
+ # --- 17. Kiosk Chrome ---
485
+ if ($ShellReplace) {
486
+ Write-Host "[17/19] SHELL REPLACEMENT..." -ForegroundColor Magenta
487
+
488
+ # Copy shell BAT (reads slug from agent.config.json - single source of truth)
489
+ $shellSource = Join-Path $ScriptDir "lightman-shell.bat"
490
+ $shellTarget = Join-Path $InstallDir "lightman-shell.bat"
491
+ if (Test-Path $shellSource) { Copy-Item $shellSource $shellTarget -Force }
492
+
493
+ # No sidecar file needed - shell BAT reads directly from agent.config.json
494
+
495
+ # Replace shell
496
+ $ShellReg = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
497
+ if (-not (Test-Path $ShellReg)) { New-Item -Path $ShellReg -Force | Out-Null }
498
+ Set-ItemProperty -Path $ShellReg -Name "Shell" -Value """$shellTarget"""
499
+ $HKLMShell = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
500
+ $orig = (Get-ItemProperty -Path $HKLMShell -Name "Shell" -ErrorAction SilentlyContinue).Shell
501
+ if ($orig -and $orig -notlike "*lightman*") { Set-ItemProperty -Path $HKLMShell -Name "Shell_Original" -Value $orig }
502
+ Set-ItemProperty -Path $HKLMShell -Name "Shell" -Value """$shellTarget"""
503
+
504
+ Write-Host " Shell replaced -> lightman-shell.bat" -ForegroundColor Green
505
+ Write-Host " Recovery: scripts\restore-desktop.ps1" -ForegroundColor Yellow
506
+
507
+ # Remove kiosk task if exists
508
+ $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
509
+ if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
510
+ } else {
511
+ Write-Host "[17/19] Standard mode - kiosk browser task..." -ForegroundColor Yellow
512
+ $vbs = Join-Path $ScriptDir "launch-kiosk.vbs"
513
+ $vbsT = Join-Path $InstallDir "launch-kiosk.vbs"
514
+ if (Test-Path $vbs) { Copy-Item $vbs $vbsT -Force }
515
+ $kt = Get-ScheduledTask -TaskName $KioskTask -ErrorAction SilentlyContinue
516
+ if ($kt) { Unregister-ScheduledTask -TaskName $KioskTask -Confirm:$false }
517
+ $kA = New-ScheduledTaskAction -Execute "wscript.exe" -Argument """$vbsT""" -WorkingDirectory $InstallDir
518
+ $kT1 = New-ScheduledTaskTrigger -AtLogOn -User $Username
519
+ $kT2 = New-ScheduledTaskTrigger -AtStartup
520
+ $kS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
521
+ Register-ScheduledTask -TaskName $KioskTask -Action $kA -Trigger @($kT1,$kT2) -Settings $kS -RunLevel Highest -Description "Chrome kiosk at logon/startup" -Force | Out-Null
522
+ Write-Host " Kiosk browser task registered"
523
+ }
524
+
525
+ # --- 18. Guardian ---
526
+ Write-Host "[18/19] Registering Guardian..." -ForegroundColor Yellow
527
+ $gSrc = Join-Path $ScriptDir "guardian.ps1"
528
+ $gDst = Join-Path $InstallDir "guardian.ps1"
529
+ if (Test-Path $gSrc) { Copy-Item $gSrc $gDst -Force }
530
+ $gt = Get-ScheduledTask -TaskName $GuardianTask -ErrorAction SilentlyContinue
531
+ if ($gt) { Unregister-ScheduledTask -TaskName $GuardianTask -Confirm:$false }
532
+ $gA = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File ""$gDst""" -WorkingDirectory $InstallDir
533
+ $gT = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 365)
534
+ $gS = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Minutes 2)
535
+ $gP = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
536
+ Register-ScheduledTask -TaskName $GuardianTask -Action $gA -Trigger $gT -Settings $gS -Principal $gP -Description "LIGHTMAN health check every 5 min" -Force | Out-Null
537
+
538
+ foreach ($task in @("\Microsoft\Windows\UpdateOrchestrator\Reboot","\Microsoft\Windows\UpdateOrchestrator\Schedule Retry Scan","\Microsoft\Windows\WindowsUpdate\Scheduled Start")) {
539
+ try { Disable-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue | Out-Null } catch { }
540
+ }
541
+ Write-Host " Guardian registered"
542
+
543
+ # --- 19. Final verification ---
544
+ Write-Host "[19/19] Verification..." -ForegroundColor Yellow
545
+ Start-Sleep -Seconds 3
546
+
547
+ $finalSvc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
548
+ if (-not $finalSvc) { $finalSvc = Get-Service -DisplayName "LIGHTMAN*" -ErrorAction SilentlyContinue | Select-Object -First 1 }
549
+ $svcStatus = if ($finalSvc) { "$($finalSvc.Status)" } else { "NOT FOUND" }
550
+
551
+ $cfgOk = $false
552
+ try {
553
+ Push-Location $InstallDir
554
+ $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
555
+ $cfgData = $cfgResult | ConvertFrom-Json
556
+ Pop-Location
557
+ $cfgOk = $true
558
+ } catch { Pop-Location }
559
+
560
+ Write-Host ""
561
+ Write-Host "=============================================" -ForegroundColor Green
562
+ Write-Host " INSTALLATION COMPLETE" -ForegroundColor Green
563
+ Write-Host "=============================================" -ForegroundColor Green
564
+ Write-Host ""
565
+ Write-Host " Slug : $Slug"
566
+ Write-Host " Server : $Server"
567
+ Write-Host " Install : $InstallDir"
568
+ Write-Host " Logs : $LogDir"
569
+ Write-Host " User : $Username"
570
+ Write-Host ""
571
+ Write-Host " Service : $svcStatus" -ForegroundColor $(if ($svcStatus -eq 'Running') { 'Green' } else { 'Red' })
572
+ if ($cfgOk) {
573
+ Write-Host " Config slug: $($cfgData.slug)" -ForegroundColor $(if ($cfgData.slug -eq $Slug) { 'Green' } else { 'Red' })
574
+ Write-Host " Shell mode : $($cfgData.shell)" -ForegroundColor $(if ($cfgData.shell -eq $ShellReplace.IsPresent) { 'Green' } else { 'Red' })
575
+ }
576
+ Write-Host ""
577
+ Write-Host " Manage:" -ForegroundColor DarkGray
578
+ Write-Host " $NssmExe stop $ServiceName" -ForegroundColor DarkGray
579
+ Write-Host " $NssmExe start $ServiceName" -ForegroundColor DarkGray
580
+ Write-Host " $NssmExe restart $ServiceName" -ForegroundColor DarkGray
581
+ Write-Host ""
582
+ Write-Host " BIOS (manual):" -ForegroundColor Red
583
+ Write-Host " After Power Loss = Power On" -ForegroundColor Red
584
+ Write-Host " Wake-on-LAN = Enabled" -ForegroundColor Red
585
+ Write-Host ""
586
+ Write-Host " REBOOT NOW: Restart-Computer" -ForegroundColor Yellow
587
+ Write-Host ""