lightman-agent 1.0.1 → 1.0.3

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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightman-agent",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "LIGHTMAN Agent - System-level daemon for museum display machines",
5
5
  "private": false,
6
6
  "type": "module",
@@ -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
 
@@ -68,11 +69,24 @@ foreach ($tn in @($AgentTask, $KioskTask, $GuardianTask)) {
68
69
  if ($t) { Stop-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue; Unregister-ScheduledTask -TaskName $tn -Confirm:$false -ErrorAction SilentlyContinue }
69
70
  }
70
71
 
71
- # Kill processes
72
- Write-Host "[0c] Killing node.exe and Chrome..." -ForegroundColor Yellow
73
- Get-Process -Name "node" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
74
- Get-Process -Name "chrome" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
75
- Start-Sleep -Seconds 2
72
+ # Kill processes
73
+ Write-Host "[0c] Killing node.exe and Chrome..." -ForegroundColor Yellow
74
+ # IMPORTANT:
75
+ # If install-windows.ps1 is launched from the npm CLI wrapper (node.exe),
76
+ # killing all node.exe would terminate the installer mid-run.
77
+ $parentPid = $null
78
+ try {
79
+ $parentPid = (Get-CimInstance Win32_Process -Filter "ProcessId=$PID" -ErrorAction SilentlyContinue).ParentProcessId
80
+ } catch { }
81
+ Get-Process -Name "node" -ErrorAction SilentlyContinue | ForEach-Object {
82
+ if ($parentPid -and $_.Id -eq $parentPid) {
83
+ Write-Host " Keeping installer parent node.exe (PID $($_.Id))" -ForegroundColor DarkGray
84
+ } else {
85
+ Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
86
+ }
87
+ }
88
+ Get-Process -Name "chrome" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
89
+ Start-Sleep -Seconds 2
76
90
 
77
91
  # Remove old files (keep NSSM and logs)
78
92
  Write-Host "[0d] Removing old agent files..." -ForegroundColor Yellow
@@ -287,11 +301,54 @@ for ($i = 0; $i -lt 10; $i++) {
287
301
  if ($n) { $portUp = $true; break }
288
302
  Start-Sleep -Seconds 2
289
303
  }
290
- if ($portUp) { Write-Host " Port 3403 LISTENING" -ForegroundColor Green }
291
- else { Write-Host " Port 3403 not yet up (may take a moment)" -ForegroundColor Yellow }
292
-
293
- # --- 12. Firewall ---
294
- Write-Host "[12/19] Configuring firewall..." -ForegroundColor Yellow
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
295
352
  $ErrorActionPreference = "Continue"
296
353
  if (-not (Get-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -ErrorAction SilentlyContinue)) {
297
354
  New-NetFirewallRule -DisplayName "LIGHTMAN Agent WebSocket" -Direction Outbound -Action Allow -Protocol TCP -RemotePort 3001 -Description "LIGHTMAN Agent" | Out-Null