lightman-agent 1.0.28 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightman-agent",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "description": "LIGHTMAN Agent - System-level daemon for museum display machines",
5
5
  "private": false,
6
6
  "type": "module",
@@ -21,7 +21,7 @@ function Write-GuardianLog($msg) {
21
21
  } catch { }
22
22
  }
23
23
 
24
- function Get-BrowserProcessName {
24
+ function Get-BrowserProcessName {
25
25
  try {
26
26
  if (Test-Path $ConfigPath) {
27
27
  $config = Get-Content $ConfigPath -Raw | ConvertFrom-Json
@@ -35,10 +35,20 @@ function Get-BrowserProcessName {
35
35
  }
36
36
  } catch { }
37
37
 
38
- return "chrome"
39
- }
40
-
41
- try {
38
+ return "chrome"
39
+ }
40
+
41
+ function Get-ShellModeEnabled {
42
+ try {
43
+ if (Test-Path $ConfigPath) {
44
+ $config = Get-Content $ConfigPath -Raw | ConvertFrom-Json
45
+ return [bool]($config.kiosk -and $config.kiosk.shellMode)
46
+ }
47
+ } catch { }
48
+ return $false
49
+ }
50
+
51
+ try {
42
52
  # 1. Check LIGHTMAN service
43
53
  $svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
44
54
  if (-not $svc) {
@@ -75,13 +85,18 @@ try {
75
85
  }
76
86
  }
77
87
 
78
- # 2. Check kiosk browser
79
- $browserProcess = Get-BrowserProcessName
80
- $browser = Get-Process -Name $browserProcess -ErrorAction SilentlyContinue
81
- if (-not $browser) {
82
- $vbsPath = "C:\Program Files\Lightman\Agent\launch-kiosk.vbs"
83
- if (Test-Path $vbsPath) {
84
- Start-Sleep -Seconds 10
88
+ # 2. Check kiosk browser
89
+ $browserProcess = Get-BrowserProcessName
90
+ $shellMode = Get-ShellModeEnabled
91
+ $browser = Get-Process -Name $browserProcess -ErrorAction SilentlyContinue
92
+ if (-not $browser) {
93
+ if ($shellMode) {
94
+ Write-GuardianLog "Browser '$browserProcess' not running in shell mode. Skipping VBS launch; shell will recover."
95
+ exit 0
96
+ }
97
+ $vbsPath = "C:\Program Files\Lightman\Agent\launch-kiosk.vbs"
98
+ if (Test-Path $vbsPath) {
99
+ Start-Sleep -Seconds 10
85
100
  $browserRecheck = Get-Process -Name $browserProcess -ErrorAction SilentlyContinue
86
101
  if (-not $browserRecheck) {
87
102
  Write-GuardianLog "Browser '$browserProcess' not running. Launching via VBS..."
@@ -165,8 +165,9 @@ Start-Sleep -Seconds 2
165
165
 
166
166
  # Remove old files (keep NSSM and logs)
167
167
  Write-Host "[0d] Removing old agent files..." -ForegroundColor Yellow
168
- Remove-Item -Path $InstallDir -Recurse -Force -ErrorAction SilentlyContinue
169
- Remove-Item -Path "C:\ProgramData\Lightman\kiosk-url.txt" -Force -ErrorAction SilentlyContinue
168
+ Remove-Item -Path $InstallDir -Recurse -Force -ErrorAction SilentlyContinue
169
+ Remove-Item -Path "C:\ProgramData\Lightman\kiosk-url.txt" -Force -ErrorAction SilentlyContinue
170
+ Remove-Item -Path "C:\ProgramData\Lightman\kiosk-multi.json" -Force -ErrorAction SilentlyContinue
170
171
 
171
172
  # Remove firewall rule
172
173
  Remove-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue
@@ -535,16 +536,6 @@ powercfg /change monitor-timeout-ac 0 2>&1 | Out-Null
535
536
  powercfg /change standby-timeout-ac 0 2>&1 | Out-Null
536
537
  powercfg /change hibernate-timeout-ac 0 2>&1 | Out-Null
537
538
 
538
- # --- 15b. Display topology ---
539
- Write-Host "[15b/19] Enforcing display extend mode..." -ForegroundColor Yellow
540
- if (Test-Path "$env:SystemRoot\System32\DisplaySwitch.exe") {
541
- & "$env:SystemRoot\System32\DisplaySwitch.exe" /extend 2>&1 | Out-Null
542
- Start-Sleep -Seconds 2
543
- Write-Host " Display mode set to Extend"
544
- } else {
545
- Write-Host " DisplaySwitch.exe not found - skipped" -ForegroundColor DarkYellow
546
- }
547
-
548
539
  # --- 16. Harden ---
549
540
  Write-Host "[16/19] Hardening Windows..." -ForegroundColor Yellow
550
541
  $WU = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
@@ -25,8 +25,10 @@ jsonText = objFile.ReadAll
25
25
  objFile.Close
26
26
 
27
27
  browserPath = ExtractJsonValue(jsonText, "browserPath")
28
- defaultUrl = ExtractJsonValue(jsonText, "defaultUrl")
29
- browserExeName = LCase(ExtractFileName(browserPath))
28
+ defaultUrl = ExtractJsonValue(jsonText, "defaultUrl")
29
+ defaultUrl = Replace(defaultUrl, "http://localhost:3403", "http://127.0.0.1:3403")
30
+ defaultUrl = Replace(defaultUrl, "https://localhost:3403", "http://127.0.0.1:3403")
31
+ browserExeName = LCase(ExtractFileName(browserPath))
30
32
 
31
33
  If browserExeName = "" Then
32
34
  browserExeName = "chrome.exe"
@@ -77,7 +79,7 @@ Do While waitedMs < (maxWaitSeconds * 1000)
77
79
  Loop
78
80
 
79
81
  ' --- Build Chrome kiosk args ---
80
- 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"
82
+ 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 --proxy-server=direct:// --proxy-bypass-list=*"
81
83
 
82
84
  userDataDir = ExtractJsonValue(jsonText, "user-data-dir")
83
85
  If userDataDir = "" Then
@@ -84,6 +84,8 @@ if (-not (Test-Path $MultiConfigPath)) {
84
84
  "--no-default-browser-check",
85
85
  "--autoplay-policy=no-user-gesture-required",
86
86
  "--disable-features=TranslateUI",
87
+ "--proxy-server=direct://",
88
+ "--proxy-bypass-list=*",
87
89
  "--user-data-dir=C:\ProgramData\Lightman\chrome-kiosk",
88
90
  $FallbackUrl
89
91
  ) -PassThru
@@ -104,6 +106,8 @@ if ($entries.Count -le 1) {
104
106
  "--no-default-browser-check",
105
107
  "--autoplay-policy=no-user-gesture-required",
106
108
  "--disable-features=TranslateUI",
109
+ "--proxy-server=direct://",
110
+ "--proxy-bypass-list=*",
107
111
  "--user-data-dir=C:\ProgramData\Lightman\chrome-kiosk",
108
112
  $FallbackUrl
109
113
  ) -PassThru
@@ -151,6 +155,8 @@ for ($i = 0; $i -lt $entries.Count; $i++) {
151
155
  "--no-default-browser-check",
152
156
  "--autoplay-policy=no-user-gesture-required",
153
157
  "--disable-features=TranslateUI",
158
+ "--proxy-server=direct://",
159
+ "--proxy-bypass-list=*",
154
160
  "--window-position=$x,$y",
155
161
  "--window-size=$w,$h",
156
162
  "--user-data-dir=$userDataDir",
@@ -172,6 +178,8 @@ if ($processes.Count -eq 0) {
172
178
  "--no-default-browser-check",
173
179
  "--autoplay-policy=no-user-gesture-required",
174
180
  "--disable-features=TranslateUI",
181
+ "--proxy-server=direct://",
182
+ "--proxy-bypass-list=*",
175
183
  "--user-data-dir=C:\ProgramData\Lightman\chrome-kiosk",
176
184
  $FallbackUrl
177
185
  ) -PassThru
@@ -63,22 +63,29 @@ if exist "%CONFIG_FILE%" (
63
63
  :use_fallbacks
64
64
 
65
65
  REM Build URL from slug (ALWAYS from config, never from sidecar)
66
- if not "%DEVICE_SLUG%"=="" (
67
- set URL=http://localhost:3403/display/%DEVICE_SLUG%
68
- echo [%date% %time%] Slug: %DEVICE_SLUG% >> "%LOG_FILE%"
69
- ) else (
70
- set URL=http://localhost:3403/display
71
- echo [%date% %time%] WARNING: No slug in config! >> "%LOG_FILE%"
72
- )
73
-
74
- REM If agent wrote a URL sidecar (includes deviceId/apiKey), prefer it.
75
- if exist "%URL_SIDECAR%" (
76
- for /f "usebackq delims=" %%u in ("%URL_SIDECAR%") do set SIDE_URL=%%u
77
- if not "%SIDE_URL%"=="" (
78
- set URL=%SIDE_URL%
79
- echo [%date% %time%] Using sidecar URL >> "%LOG_FILE%"
80
- )
81
- )
66
+ if not "%DEVICE_SLUG%"=="" (
67
+ set URL=http://127.0.0.1:3403/display/%DEVICE_SLUG%
68
+ echo [%date% %time%] Slug: %DEVICE_SLUG% >> "%LOG_FILE%"
69
+ ) else (
70
+ set URL=http://127.0.0.1:3403/display
71
+ echo [%date% %time%] WARNING: No slug in config! >> "%LOG_FILE%"
72
+ )
73
+
74
+ REM If agent wrote a URL sidecar (includes deviceId/apiKey), prefer it.
75
+ if exist "%URL_SIDECAR%" (
76
+ for /f "usebackq delims=" %%u in ("%URL_SIDECAR%") do set SIDE_URL=%%u
77
+ set USE_SIDE_URL=0
78
+ if not "%SIDE_URL%"=="" (
79
+ echo %SIDE_URL% | find /I "127.0.0.1:3403" >nul && set USE_SIDE_URL=1
80
+ echo %SIDE_URL% | find /I "localhost:3403" >nul && set USE_SIDE_URL=1
81
+ )
82
+ if "%USE_SIDE_URL%"=="1" (
83
+ set URL=%SIDE_URL%
84
+ echo [%date% %time%] Using sidecar local URL >> "%LOG_FILE%"
85
+ ) else (
86
+ if not "%SIDE_URL%"=="" echo [%date% %time%] Ignoring non-local sidecar URL >> "%LOG_FILE%"
87
+ )
88
+ )
82
89
 
83
90
  REM Fallback browser
84
91
  if "%BROWSER%"=="" (
@@ -118,11 +125,19 @@ set WAIT_COUNT=0
118
125
  :agent_ready
119
126
  echo [%date% %time%] Agent ready >> "%LOG_FILE%"
120
127
 
121
- REM Force Windows into Extend mode so all connected displays are usable.
122
- if exist "%SystemRoot%\System32\DisplaySwitch.exe" (
123
- "%SystemRoot%\System32\DisplaySwitch.exe" /extend >nul 2>&1
124
- timeout /t 2 /nobreak >nul
125
- )
128
+ REM Wait until local display HTTP endpoint responds before opening browser.
129
+ set HTTP_WAIT=0
130
+ :wait_for_http
131
+ powershell -NoProfile -Command "try { $r=Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 -Uri 'http://127.0.0.1:3403/'; if ($r.StatusCode -ge 200) { exit 0 } else { exit 1 } } catch { exit 1 }" >nul 2>&1
132
+ if %errorlevel%==0 goto http_ready
133
+ set /a HTTP_WAIT+=1
134
+ set /a HTTP_WAIT_MOD=HTTP_WAIT %% 30
135
+ if %HTTP_WAIT_MOD%==0 echo [%date% %time%] Still waiting for HTTP on 127.0.0.1:3403... (%HTTP_WAIT%s) >> "%LOG_FILE%"
136
+ timeout /t 1 /nobreak >nul
137
+ goto wait_for_http
138
+
139
+ :http_ready
140
+ echo [%date% %time%] HTTP ready on 127.0.0.1:3403 >> "%LOG_FILE%"
126
141
 
127
142
  REM ----------------------------------------------------------------
128
143
  REM Infinite Chrome loop
@@ -133,16 +148,23 @@ REM ----------------------------------------------------------------
133
148
  if exist "%URL_SIDECAR%" (
134
149
  for /f "usebackq delims=" %%u in ("%URL_SIDECAR%") do set SIDE_URL=%%u
135
150
  )
136
- if not "%SIDE_URL%"=="" (
137
- set URL=%SIDE_URL%
138
- ) else (
139
- REM Re-read slug from config on every loop iteration.
140
- if exist "%CONFIG_FILE%" (
141
- for /f "delims=" %%a in ('node -e "try{console.log(JSON.parse(require('fs').readFileSync(String.raw`%CONFIG_FILE%`,'utf8')).deviceSlug)}catch(e){console.log('')}" 2^>nul') do (
142
- if not "%%a"=="" set URL=http://localhost:3403/display/%%a
143
- )
144
- )
145
- )
151
+ if not "%SIDE_URL%"=="" (
152
+ set USE_SIDE_URL=0
153
+ echo %SIDE_URL% | find /I "127.0.0.1:3403" >nul && set USE_SIDE_URL=1
154
+ echo %SIDE_URL% | find /I "localhost:3403" >nul && set USE_SIDE_URL=1
155
+ if "%USE_SIDE_URL%"=="1" (
156
+ set URL=%SIDE_URL%
157
+ ) else (
158
+ echo [%date% %time%] Ignoring non-local sidecar URL in loop >> "%LOG_FILE%"
159
+ )
160
+ ) else (
161
+ REM Re-read slug from config on every loop iteration.
162
+ if exist "%CONFIG_FILE%" (
163
+ for /f "delims=" %%a in ('node -e "try{console.log(JSON.parse(require('fs').readFileSync(String.raw`%CONFIG_FILE%`,'utf8')).deviceSlug)}catch(e){console.log('')}" 2^>nul') do (
164
+ if not "%%a"=="" set URL=http://127.0.0.1:3403/display/%%a
165
+ )
166
+ )
167
+ )
146
168
 
147
169
  set MULTI_COUNT=0
148
170
  if exist "%MULTI_SIDECAR%" (
@@ -154,7 +176,7 @@ REM ----------------------------------------------------------------
154
176
  powershell -ExecutionPolicy Bypass -NoProfile -File "%MULTI_LAUNCHER%" -BrowserPath "%BROWSER%" -MultiConfigPath "%MULTI_SIDECAR%" -FallbackUrl "%URL%" -LogFile "%LOG_FILE%"
155
177
  ) else (
156
178
  echo [%date% %time%] Launching browser: %URL% >> "%LOG_FILE%"
157
- start /wait "" "%BROWSER%" --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 --disable-features=TranslateUI --user-data-dir="%CHROME_DATA%" "%URL%"
179
+ start /wait "" "%BROWSER%" --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 --disable-features=TranslateUI --proxy-server=direct:// --proxy-bypass-list=* --user-data-dir="%CHROME_DATA%" "%URL%"
158
180
  )
159
181
 
160
182
  echo [%date% %time%] Browser exited (code: %errorlevel%). Restarting in 3s... >> "%LOG_FILE%"
package/scripts/setup.ps1 CHANGED
@@ -68,8 +68,8 @@ if (Test-Path $IdentityFile) {
68
68
  Write-Host "[OK] No existing identity cache found (clean install)" -ForegroundColor DarkGray
69
69
  }
70
70
 
71
- # 2. Kiosk display URL - always localhost since agent runs the static server locally on port 3403
72
- $KioskUrl = "http://localhost:3403/display/$Slug"
71
+ # 2. Kiosk display URL - use 127.0.0.1 to avoid localhost/proxy edge-cases
72
+ $KioskUrl = "http://127.0.0.1:3403/display/$Slug"
73
73
 
74
74
  # 3. Detect browser path
75
75
  try {
package/src/index.ts CHANGED
@@ -163,10 +163,16 @@ async function main(): Promise<void> {
163
163
  crashWindowMs: 300_000,
164
164
  };
165
165
  // Enforce unmuted autoplay in kiosk mode for video templates with audio tracks.
166
- const normalizedExtraArgs = (baseKioskConfig.extraArgs || []).filter((arg) => arg !== '--mute-audio');
167
- if (!normalizedExtraArgs.some((arg) => arg.startsWith('--autoplay-policy='))) {
168
- normalizedExtraArgs.push('--autoplay-policy=no-user-gesture-required');
169
- }
166
+ const normalizedExtraArgs = (baseKioskConfig.extraArgs || []).filter((arg) => arg !== '--mute-audio');
167
+ if (!normalizedExtraArgs.some((arg) => arg.startsWith('--autoplay-policy='))) {
168
+ normalizedExtraArgs.push('--autoplay-policy=no-user-gesture-required');
169
+ }
170
+ if (!normalizedExtraArgs.some((arg) => arg.startsWith('--proxy-server='))) {
171
+ normalizedExtraArgs.push('--proxy-server=direct://');
172
+ }
173
+ if (!normalizedExtraArgs.some((arg) => arg.startsWith('--proxy-bypass-list='))) {
174
+ normalizedExtraArgs.push('--proxy-bypass-list=*');
175
+ }
170
176
  // Inject credentials into the kiosk URL so Chrome auto-provisions without pairing
171
177
  const kioskUrl = new URL(baseKioskConfig.defaultUrl);
172
178
  kioskUrl.searchParams.set('deviceId', identity.deviceId);
@@ -222,12 +222,12 @@ export class MultiScreenKioskManager {
222
222
  /** Build the full URL with device credentials and screenIndex */
223
223
  private buildUrl(path: string, identity: { deviceId: string; apiKey: string }, screenIndex?: number): string {
224
224
  let fullUrl: string;
225
- if (path.startsWith('http://') || path.startsWith('https://')) {
226
- fullUrl = path;
227
- } else {
228
- const displayPath = path.startsWith('/display/') ? path : `/display/${path.replace(/^\//, '')}`;
229
- fullUrl = `http://localhost:3403${displayPath}`;
230
- }
225
+ if (path.startsWith('http://') || path.startsWith('https://')) {
226
+ fullUrl = path;
227
+ } else {
228
+ const displayPath = path.startsWith('/display/') ? path : `/display/${path.replace(/^\//, '')}`;
229
+ fullUrl = `http://127.0.0.1:3403${displayPath}`;
230
+ }
231
231
 
232
232
  const url = new URL(fullUrl);
233
233
  url.searchParams.set('deviceId', identity.deviceId);