lightman-agent 1.0.2 → 1.0.4
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/bin/cms-agent.js +12 -3
- package/package.json +1 -1
- package/scripts/install-windows.ps1 +56 -12
- package/scripts/lightman-shell.bat +38 -21
- package/src/services/kiosk.ts +13 -11
package/bin/cms-agent.js
CHANGED
|
@@ -24,6 +24,7 @@ Options:
|
|
|
24
24
|
--slug <value> Device slug (example: C-AV01)
|
|
25
25
|
--server <url> Server URL (example: http://192.168.1.100:3401)
|
|
26
26
|
--timezone <tz> Timezone override (default: Asia/Kolkata)
|
|
27
|
+
--pair-timeout <s> Wait time for pairing in seconds (default: 900, 0 = no timeout)
|
|
27
28
|
--no-restart Skip reboot after successful install/update
|
|
28
29
|
-h, --help Show help
|
|
29
30
|
`);
|
|
@@ -57,6 +58,9 @@ function parseArgs(argv) {
|
|
|
57
58
|
} else if (part === '--timezone') {
|
|
58
59
|
args.timezone = next.trim();
|
|
59
60
|
i += 1;
|
|
61
|
+
} else if (part === '--pair-timeout') {
|
|
62
|
+
args.pairTimeout = next.trim();
|
|
63
|
+
i += 1;
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
return args;
|
|
@@ -134,12 +138,15 @@ function resolveInstallScript() {
|
|
|
134
138
|
throw new Error('install-windows.ps1 not found. Expected in package scripts/ or current folder scripts/.');
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
function installUsingPowerShell({ scriptPath, slug, server, timezone, noRestart }) {
|
|
141
|
+
function installUsingPowerShell({ scriptPath, slug, server, timezone, pairingTimeoutSeconds, noRestart }) {
|
|
138
142
|
console.log(`powershell -ExecutionPolicy Bypass -File scripts\\install-windows.ps1 -Slug "${slug}" -Server "${server}" -ShellReplace`);
|
|
139
143
|
const args = ['-ExecutionPolicy', 'Bypass', '-File', scriptPath, '-Slug', slug, '-Server', server, '-ShellReplace'];
|
|
140
144
|
if (timezone) {
|
|
141
145
|
args.push('-Timezone', timezone);
|
|
142
146
|
}
|
|
147
|
+
if (Number.isInteger(pairingTimeoutSeconds) && pairingTimeoutSeconds >= 0) {
|
|
148
|
+
args.push('-PairingTimeoutSeconds', String(pairingTimeoutSeconds));
|
|
149
|
+
}
|
|
143
150
|
runOrFail('powershell.exe', args);
|
|
144
151
|
|
|
145
152
|
if (!noRestart) {
|
|
@@ -161,6 +168,7 @@ async function runInstall(opts) {
|
|
|
161
168
|
const defaultSlug = opts.slug || localConfig.deviceSlug || installedConfig.deviceSlug || '';
|
|
162
169
|
const server = opts.server || DEFAULT_SERVER;
|
|
163
170
|
const timezone = opts.timezone || localConfig?.powerSchedule?.timezone || installedConfig?.powerSchedule?.timezone || 'Asia/Kolkata';
|
|
171
|
+
const pairingTimeoutSeconds = Number.isFinite(Number(opts.pairTimeout)) ? Number.parseInt(String(opts.pairTimeout), 10) : 900;
|
|
164
172
|
const noRestart = Boolean(opts.noRestart);
|
|
165
173
|
|
|
166
174
|
console.log('Detected MAC addresses:');
|
|
@@ -178,7 +186,7 @@ async function runInstall(opts) {
|
|
|
178
186
|
const scriptPath = resolveInstallScript();
|
|
179
187
|
|
|
180
188
|
console.log(`Installing with slug=${slug}, server=${server}, shellReplace=true`);
|
|
181
|
-
installUsingPowerShell({ scriptPath, slug, server, timezone, noRestart });
|
|
189
|
+
installUsingPowerShell({ scriptPath, slug, server, timezone, pairingTimeoutSeconds, noRestart });
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
async function runUpdate(opts) {
|
|
@@ -193,6 +201,7 @@ async function runUpdate(opts) {
|
|
|
193
201
|
const slug = opts.slug || installedConfig.deviceSlug;
|
|
194
202
|
const server = opts.server || DEFAULT_SERVER;
|
|
195
203
|
const timezone = opts.timezone || installedConfig?.powerSchedule?.timezone || 'Asia/Kolkata';
|
|
204
|
+
const pairingTimeoutSeconds = Number.isFinite(Number(opts.pairTimeout)) ? Number.parseInt(String(opts.pairTimeout), 10) : 900;
|
|
196
205
|
const noRestart = Boolean(opts.noRestart);
|
|
197
206
|
const scriptPath = resolveInstallScript();
|
|
198
207
|
|
|
@@ -201,7 +210,7 @@ async function runUpdate(opts) {
|
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
console.log(`Updating with slug=${slug}, server=${server}, shellReplace=true`);
|
|
204
|
-
installUsingPowerShell({ scriptPath, slug, server, timezone, noRestart });
|
|
213
|
+
installUsingPowerShell({ scriptPath, slug, server, timezone, pairingTimeoutSeconds, noRestart });
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
async function main() {
|
package/package.json
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
# powershell -ExecutionPolicy Bypass -File install-windows.ps1 -Slug "F-AV01" -Server "http://..." -ShellReplace
|
|
10
10
|
#Requires -RunAsAdministrator
|
|
11
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
|
-
|
|
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
|
+
[int]$PairingTimeoutSeconds = 900
|
|
19
|
+
)
|
|
19
20
|
|
|
20
21
|
$ErrorActionPreference = "Stop"
|
|
21
22
|
|
|
@@ -300,11 +301,54 @@ for ($i = 0; $i -lt 10; $i++) {
|
|
|
300
301
|
if ($n) { $portUp = $true; break }
|
|
301
302
|
Start-Sleep -Seconds 2
|
|
302
303
|
}
|
|
303
|
-
if ($portUp) { Write-Host " Port 3403 LISTENING" -ForegroundColor Green }
|
|
304
|
-
else { Write-Host " Port 3403 not yet up (may take a moment)" -ForegroundColor Yellow }
|
|
305
|
-
|
|
306
|
-
#
|
|
307
|
-
Write-Host "[
|
|
304
|
+
if ($portUp) { Write-Host " Port 3403 LISTENING" -ForegroundColor Green }
|
|
305
|
+
else { Write-Host " Port 3403 not yet up (may take a moment)" -ForegroundColor Yellow }
|
|
306
|
+
|
|
307
|
+
# Wait until provisioning is complete (auto-provision or manual pairing)
|
|
308
|
+
Write-Host "[11b/19] Waiting for device provisioning/pairing..." -ForegroundColor Yellow
|
|
309
|
+
$identityPath = Join-Path $InstallDir ".lightman-identity.json"
|
|
310
|
+
$deadline = if ($PairingTimeoutSeconds -gt 0) { (Get-Date).AddSeconds($PairingTimeoutSeconds) } else { $null }
|
|
311
|
+
$paired = $false
|
|
312
|
+
$lastHint = ""
|
|
313
|
+
|
|
314
|
+
while (-not $paired) {
|
|
315
|
+
if (Test-Path $identityPath) {
|
|
316
|
+
try {
|
|
317
|
+
$identity = Get-Content $identityPath -Raw | ConvertFrom-Json
|
|
318
|
+
if ($identity.deviceId -and $identity.apiKey) {
|
|
319
|
+
$paired = $true
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
# File may be mid-write, retry
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
$logPath = Join-Path $LogDir "service-stdout.log"
|
|
328
|
+
if (Test-Path $logPath) {
|
|
329
|
+
try {
|
|
330
|
+
$hint = Get-Content $logPath -Tail 40 | Where-Object {
|
|
331
|
+
$_ -match "Pairing required|Waiting for admin to approve pairing|Auto-provisioned|Pairing complete"
|
|
332
|
+
} | Select-Object -Last 1
|
|
333
|
+
if ($hint -and $hint -ne $lastHint) {
|
|
334
|
+
Write-Host " Agent: $hint" -ForegroundColor DarkGray
|
|
335
|
+
$lastHint = $hint
|
|
336
|
+
}
|
|
337
|
+
} catch { }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if ($deadline -and (Get-Date) -ge $deadline) {
|
|
341
|
+
Write-Host " FATAL: Pairing timed out after $PairingTimeoutSeconds seconds." -ForegroundColor Red
|
|
342
|
+
Write-Host " Check server pairing UI, then re-run installer (or increase -PairingTimeoutSeconds)." -ForegroundColor Yellow
|
|
343
|
+
exit 1
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
Start-Sleep -Seconds 5
|
|
347
|
+
}
|
|
348
|
+
Write-Host " Provisioning/pairing complete" -ForegroundColor Green
|
|
349
|
+
|
|
350
|
+
# --- 12. Firewall ---
|
|
351
|
+
Write-Host "[12/19] Configuring firewall..." -ForegroundColor Yellow
|
|
308
352
|
$ErrorActionPreference = "Continue"
|
|
309
353
|
if (-not (Get-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue)) {
|
|
310
354
|
New-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -Direction Outbound -Action Allow -Protocol TCP -RemotePort 3001 -Description "LIGHTMAN Agent" | Out-Null
|
|
@@ -13,10 +13,11 @@ REM 5. Launches Chrome fullscreen
|
|
|
13
13
|
REM 6. If Chrome crashes, relaunches in 3 seconds (infinite loop)
|
|
14
14
|
REM ================================================================
|
|
15
15
|
|
|
16
|
-
set INSTALL_DIR=C:\Program Files\Lightman\Agent
|
|
17
|
-
set CONFIG_FILE=%INSTALL_DIR%\agent.config.json
|
|
18
|
-
set
|
|
19
|
-
set
|
|
16
|
+
set INSTALL_DIR=C:\Program Files\Lightman\Agent
|
|
17
|
+
set CONFIG_FILE=%INSTALL_DIR%\agent.config.json
|
|
18
|
+
set URL_SIDECAR=C:\ProgramData\Lightman\kiosk-url.txt
|
|
19
|
+
set CHROME_DATA=C:\ProgramData\Lightman\chrome-kiosk
|
|
20
|
+
set LOG_FILE=C:\ProgramData\Lightman\logs\shell.log
|
|
20
21
|
|
|
21
22
|
REM Ensure directories exist
|
|
22
23
|
if not exist "C:\ProgramData\Lightman\logs" mkdir "C:\ProgramData\Lightman\logs"
|
|
@@ -59,14 +60,23 @@ if exist "%CONFIG_FILE%" (
|
|
|
59
60
|
|
|
60
61
|
:use_fallbacks
|
|
61
62
|
|
|
62
|
-
REM Build URL from slug (ALWAYS from config, never from sidecar)
|
|
63
|
-
if not "%DEVICE_SLUG%"=="" (
|
|
64
|
-
set URL=http://localhost:3403/display/%DEVICE_SLUG%
|
|
65
|
-
echo [%date% %time%] Slug: %DEVICE_SLUG% >> "%LOG_FILE%"
|
|
66
|
-
) else (
|
|
67
|
-
set URL=http://localhost:3403/display
|
|
68
|
-
echo [%date% %time%] WARNING: No slug in config! >> "%LOG_FILE%"
|
|
69
|
-
)
|
|
63
|
+
REM Build URL from slug (ALWAYS from config, never from sidecar)
|
|
64
|
+
if not "%DEVICE_SLUG%"=="" (
|
|
65
|
+
set URL=http://localhost:3403/display/%DEVICE_SLUG%
|
|
66
|
+
echo [%date% %time%] Slug: %DEVICE_SLUG% >> "%LOG_FILE%"
|
|
67
|
+
) else (
|
|
68
|
+
set URL=http://localhost:3403/display
|
|
69
|
+
echo [%date% %time%] WARNING: No slug in config! >> "%LOG_FILE%"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
REM If agent wrote a URL sidecar (includes deviceId/apiKey), prefer it.
|
|
73
|
+
if exist "%URL_SIDECAR%" (
|
|
74
|
+
for /f "usebackq delims=" %%u in ("%URL_SIDECAR%") do set SIDE_URL=%%u
|
|
75
|
+
if not "%SIDE_URL%"=="" (
|
|
76
|
+
set URL=%SIDE_URL%
|
|
77
|
+
echo [%date% %time%] Using sidecar URL >> "%LOG_FILE%"
|
|
78
|
+
)
|
|
79
|
+
)
|
|
70
80
|
|
|
71
81
|
REM Fallback browser
|
|
72
82
|
if "%BROWSER%"=="" (
|
|
@@ -108,15 +118,22 @@ echo [%date% %time%] Agent ready >> "%LOG_FILE%"
|
|
|
108
118
|
REM ----------------------------------------------------------------
|
|
109
119
|
REM Infinite Chrome loop
|
|
110
120
|
REM ----------------------------------------------------------------
|
|
111
|
-
:loop
|
|
112
|
-
REM
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
121
|
+
:loop
|
|
122
|
+
REM Prefer sidecar URL for auth params/device routing; fallback to slug URL.
|
|
123
|
+
set SIDE_URL=
|
|
124
|
+
if exist "%URL_SIDECAR%" (
|
|
125
|
+
for /f "usebackq delims=" %%u in ("%URL_SIDECAR%") do set SIDE_URL=%%u
|
|
126
|
+
)
|
|
127
|
+
if not "%SIDE_URL%"=="" (
|
|
128
|
+
set URL=%SIDE_URL%
|
|
129
|
+
) else (
|
|
130
|
+
REM Re-read slug from config on every loop iteration.
|
|
131
|
+
if exist "%CONFIG_FILE%" (
|
|
132
|
+
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 (
|
|
133
|
+
if not "%%a"=="" set URL=http://localhost:3403/display/%%a
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
120
137
|
|
|
121
138
|
echo [%date% %time%] Launching Chrome: %URL% >> "%LOG_FILE%"
|
|
122
139
|
|
package/src/services/kiosk.ts
CHANGED
|
@@ -169,17 +169,19 @@ export class KioskManager {
|
|
|
169
169
|
// Shell Mode Methods
|
|
170
170
|
// =====================================================================
|
|
171
171
|
|
|
172
|
-
private async shellLaunch(targetUrl: string): Promise<KioskStatus> {
|
|
173
|
-
this.currentUrl = targetUrl;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
if
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.
|
|
182
|
-
}
|
|
172
|
+
private async shellLaunch(targetUrl: string): Promise<KioskStatus> {
|
|
173
|
+
this.currentUrl = targetUrl;
|
|
174
|
+
// Keep shell sidecar updated so shell BAT can launch with auth query params.
|
|
175
|
+
this.writeUrlSidecar(targetUrl);
|
|
176
|
+
|
|
177
|
+
// Shell mode: Chrome is managed by lightman-shell.bat.
|
|
178
|
+
// Shell prefers sidecar URL (if present), then falls back to slug in config.
|
|
179
|
+
if (this.isChromeRunning()) {
|
|
180
|
+
this.logger.info('Shell mode: Chrome already running. Restarting once to apply sidecar URL.');
|
|
181
|
+
this.killAllChrome();
|
|
182
|
+
} else {
|
|
183
|
+
this.logger.info('Shell mode: Chrome not running. Shell BAT will launch it.');
|
|
184
|
+
}
|
|
183
185
|
|
|
184
186
|
this.startedAt = this.startedAt || Date.now();
|
|
185
187
|
return this.getStatus();
|