lightman-agent 1.0.23 → 1.0.24

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +122 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lightman-agent",
3
- "version": "1.0.23",
3
+ "version": "1.0.24",
4
4
  "description": "LIGHTMAN Agent - System-level daemon for museum display machines",
5
5
  "private": false,
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -111,10 +111,26 @@ async function main(): Promise<void> {
111
111
  serverUrl: config.serverUrl,
112
112
  identity,
113
113
  logger,
114
- onMessage: (msg: WsMessage) => {
115
- handleServerMessage(msg, commandExecutor, logger, powerScheduler, startSerialBridge, stopSerialBridge, startOscBridge, stopOscBridge, multiScreenKiosk, getIdentity, kioskManager, watchdog, startPresenceSensor, stopPresenceSensor);
116
- },
117
- });
114
+ onMessage: (msg: WsMessage) => {
115
+ handleServerMessage(
116
+ msg,
117
+ commandExecutor,
118
+ logger,
119
+ powerScheduler,
120
+ startSerialBridge,
121
+ stopSerialBridge,
122
+ startOscBridge,
123
+ stopOscBridge,
124
+ multiScreenKiosk,
125
+ getIdentity,
126
+ kioskManager,
127
+ watchdog,
128
+ startPresenceSensor,
129
+ stopPresenceSensor,
130
+ () => lastKnownTotalScreens
131
+ );
132
+ },
133
+ });
118
134
 
119
135
  // 4. Start health monitor
120
136
  const healthMonitor = new HealthMonitor(
@@ -163,14 +179,15 @@ async function main(): Promise<void> {
163
179
  const kioskManager = new KioskManager(kioskConfig, logger);
164
180
  registerKioskCommands(commandExecutor.register.bind(commandExecutor), kioskManager, logger);
165
181
 
166
- // Multi-screen kiosk manager — handles multiple Chrome instances on multi-display devices
167
- const multiScreenKiosk = new MultiScreenKioskManager(kioskConfig, logger);
168
- const getIdentity = () => identity!;
169
- registerMultiScreenKioskCommands(commandExecutor.register.bind(commandExecutor), multiScreenKiosk, getIdentity, logger);
170
-
171
- // Detect physical screens and keep them fresh (multi-display setups can change after boot).
172
- let detectedScreens = detectScreens(logger);
173
- multiScreenKiosk.setDetectedScreens(detectedScreens);
182
+ // Multi-screen kiosk manager — handles multiple Chrome instances on multi-display devices
183
+ const multiScreenKiosk = new MultiScreenKioskManager(kioskConfig, logger);
184
+ const getIdentity = () => identity!;
185
+ registerMultiScreenKioskCommands(commandExecutor.register.bind(commandExecutor), multiScreenKiosk, getIdentity, logger);
186
+
187
+ // Detect physical screens and keep them fresh (multi-display setups can change after boot).
188
+ let lastKnownTotalScreens = 0;
189
+ let detectedScreens = detectScreens(logger);
190
+ multiScreenKiosk.setDetectedScreens(detectedScreens);
174
191
 
175
192
  const toScreenPayload = (screens: DetectedScreen[]) => (
176
193
  screens.map((s) => ({
@@ -502,24 +519,23 @@ async function main(): Promise<void> {
502
519
  // Multi-screen handling:
503
520
  // 1) Use explicit screenMap when present.
504
521
  // 2) For multi-screen apps without a saved map, auto-create placeholders.
505
- const requestedScreenMap = deviceCfg?.screenMap || [];
506
- const isMultiScreenApp = !!deviceCfg && deviceCfg.totalScreens > 1;
507
- const effectiveRequestedMap = requestedScreenMap.length > 0
508
- ? requestedScreenMap
509
- : (isMultiScreenApp ? createPlaceholderScreenMap(deviceCfg.totalScreens) : []);
510
-
511
- if (effectiveRequestedMap.length > 0) {
512
- const resolved = resolveScreenMap({
513
- requestedScreenMap: effectiveRequestedMap,
514
- detectedScreens,
515
- totalScreens: Math.max(deviceCfg?.totalScreens || 0, effectiveRequestedMap.length),
516
- });
517
- logger.info(
518
- `[MultiKiosk] Effective map ready: requested=${effectiveRequestedMap.length}, mode=${resolved.mode}, totalScreens=${deviceCfg?.totalScreens || 0}`
519
- );
520
- watchdog.setMultiScreenActive(true);
521
- multiScreenKiosk.applyScreenMap(effectiveRequestedMap, identity).catch((err) => {
522
- logger.error('[MultiKiosk] Failed to apply effective screenMap from config:', err);
522
+ const requestedScreenMap = deviceCfg?.screenMap || [];
523
+ const totalScreens = Math.max(deviceCfg?.totalScreens || 0, requestedScreenMap.length);
524
+ lastKnownTotalScreens = totalScreens;
525
+ const effectiveRequestedMap = normalizeScreenMapForTotalScreens(requestedScreenMap, totalScreens);
526
+
527
+ if (effectiveRequestedMap.length > 0) {
528
+ const resolved = resolveScreenMap({
529
+ requestedScreenMap: effectiveRequestedMap,
530
+ detectedScreens,
531
+ totalScreens,
532
+ });
533
+ logger.info(
534
+ `[MultiKiosk] Effective map ready: requested=${requestedScreenMap.length}, effective=${effectiveRequestedMap.length}, mode=${resolved.mode}, totalScreens=${totalScreens}`
535
+ );
536
+ watchdog.setMultiScreenActive(true);
537
+ multiScreenKiosk.applyScreenMap(effectiveRequestedMap, identity).catch((err) => {
538
+ logger.error('[MultiKiosk] Failed to apply effective screenMap from config:', err);
523
539
  });
524
540
  return;
525
541
  }
@@ -606,10 +622,33 @@ async function main(): Promise<void> {
606
622
  logger.info('LIGHTMAN Agent running.');
607
623
  }
608
624
 
609
- function createPlaceholderScreenMap(totalScreens: number): ScreenMapping[] {
610
- const count = Math.max(0, Math.floor(totalScreens || 0));
611
- return Array.from({ length: count }, () => ({ hardwareId: '', url: '' }));
612
- }
625
+ function createPlaceholderScreenMap(totalScreens: number): ScreenMapping[] {
626
+ const count = Math.max(0, Math.floor(totalScreens || 0));
627
+ return Array.from({ length: count }, () => ({ hardwareId: '', url: '' }));
628
+ }
629
+
630
+ function normalizeScreenMapForTotalScreens(
631
+ screenMap: ScreenMapping[] | undefined,
632
+ totalScreens: number
633
+ ): ScreenMapping[] {
634
+ const requested = Array.isArray(screenMap)
635
+ ? screenMap.map((m) => ({
636
+ hardwareId: String(m.hardwareId || ''),
637
+ url: String(m.url || ''),
638
+ ...(m.label ? { label: String(m.label) } : {}),
639
+ }))
640
+ : [];
641
+
642
+ const targetCount = Math.max(requested.length, Math.max(0, Math.floor(totalScreens || 0)));
643
+ if (targetCount === 0) return [];
644
+
645
+ if (requested.length >= targetCount) return requested;
646
+
647
+ return [
648
+ ...requested,
649
+ ...createPlaceholderScreenMap(targetCount - requested.length),
650
+ ];
651
+ }
613
652
 
614
653
  function haveScreensChanged(prev: DetectedScreen[], next: DetectedScreen[]): boolean {
615
654
  if (prev.length !== next.length) return true;
@@ -631,22 +670,23 @@ function haveScreensChanged(prev: DetectedScreen[], next: DetectedScreen[]): boo
631
670
  return false;
632
671
  }
633
672
 
634
- function handleServerMessage(
635
- msg: WsMessage,
636
- commandExecutor: CommandExecutor,
673
+ function handleServerMessage(
674
+ msg: WsMessage,
675
+ commandExecutor: CommandExecutor,
637
676
  logger: Logger,
638
677
  powerScheduler?: PowerScheduler,
639
678
  startSerialBridge?: (comPort: string, controllerId: string, baudRate?: number) => void,
640
679
  stopSerialBridge?: () => void,
641
680
  startOscBridgeFn?: (oscPort: number, oscAddress: string, oscHost?: string) => void,
642
681
  stopOscBridgeFn?: () => void,
643
- multiScreenKiosk?: MultiScreenKioskManager,
644
- getIdentity?: () => Identity,
645
- kioskManager?: KioskManager,
646
- watchdog?: Watchdog,
647
- startPresenceSensorFn?: (port?: string) => void,
648
- stopPresenceSensorFn?: () => void
649
- ): void {
682
+ multiScreenKiosk?: MultiScreenKioskManager,
683
+ getIdentity?: () => Identity,
684
+ kioskManager?: KioskManager,
685
+ watchdog?: Watchdog,
686
+ startPresenceSensorFn?: (port?: string) => void,
687
+ stopPresenceSensorFn?: () => void,
688
+ getTotalScreensHint?: () => number
689
+ ): void {
650
690
  switch (msg.type) {
651
691
  case 'connected':
652
692
  logger.info('Server acknowledged connection');
@@ -698,16 +738,25 @@ function handleServerMessage(
698
738
  }
699
739
 
700
740
  // Admin pushed updated screenMap via device config save
701
- const screenMap = msg.payload.screenMap as ScreenMapping[] | undefined;
702
- if (screenMap && Array.isArray(screenMap) && multiScreenKiosk && getIdentity) {
703
- if (screenMap.length > 0) {
704
- logger.info(`[MultiKiosk] Received screenMap update: ${screenMap.length} mapping(s) — killing single kiosk`);
705
- if (kioskManager) kioskManager.kill().catch(() => {});
706
- if (watchdog) watchdog.setMultiScreenActive(true);
707
- multiScreenKiosk.applyScreenMap(screenMap, getIdentity()).catch((err) => {
708
- logger.error('[MultiKiosk] Failed to apply screenMap:', err);
709
- });
710
- } else {
741
+ const screenMap = msg.payload.screenMap as ScreenMapping[] | undefined;
742
+ if (screenMap && Array.isArray(screenMap) && multiScreenKiosk && getIdentity) {
743
+ const payloadTotalScreens = Number(msg.payload.totalScreens || 0);
744
+ const hintTotalScreens = Math.max(
745
+ Number.isFinite(payloadTotalScreens) ? payloadTotalScreens : 0,
746
+ getTotalScreensHint ? getTotalScreensHint() : 0
747
+ );
748
+ const effectiveScreenMap = screenMap.length > 0
749
+ ? normalizeScreenMapForTotalScreens(screenMap, hintTotalScreens)
750
+ : screenMap;
751
+
752
+ if (screenMap.length > 0) {
753
+ logger.info(`[MultiKiosk] Received screenMap update: requested=${screenMap.length}, effective=${effectiveScreenMap.length}, totalScreens=${hintTotalScreens} — killing single kiosk`);
754
+ if (kioskManager) kioskManager.kill().catch(() => {});
755
+ if (watchdog) watchdog.setMultiScreenActive(true);
756
+ multiScreenKiosk.applyScreenMap(effectiveScreenMap, getIdentity()).catch((err) => {
757
+ logger.error('[MultiKiosk] Failed to apply screenMap:', err);
758
+ });
759
+ } else {
711
760
  // Empty screenMap — deactivate multi-screen, resume single kiosk
712
761
  logger.info('[MultiKiosk] Empty screenMap received — deactivating multi-screen');
713
762
  multiScreenKiosk.killAll().catch(() => {});
@@ -719,16 +768,25 @@ function handleServerMessage(
719
768
  case 'agent:screenMap':
720
769
  // Direct screenMap push from server
721
770
  if (msg.payload && multiScreenKiosk && getIdentity) {
722
- const screenMap = msg.payload.screenMap as ScreenMapping[] | undefined;
723
- if (screenMap && Array.isArray(screenMap)) {
724
- if (screenMap.length > 0) {
725
- logger.info(`[MultiKiosk] Received agent:screenMap: ${screenMap.length} mapping(s) — killing single kiosk`);
726
- if (kioskManager) kioskManager.kill().catch(() => {});
727
- if (watchdog) watchdog.setMultiScreenActive(true);
728
- multiScreenKiosk.applyScreenMap(screenMap, getIdentity()).catch((err) => {
729
- logger.error('[MultiKiosk] Failed to apply screenMap:', err);
730
- });
731
- } else {
771
+ const screenMap = msg.payload.screenMap as ScreenMapping[] | undefined;
772
+ if (screenMap && Array.isArray(screenMap)) {
773
+ const payloadTotalScreens = Number(msg.payload.totalScreens || 0);
774
+ const hintTotalScreens = Math.max(
775
+ Number.isFinite(payloadTotalScreens) ? payloadTotalScreens : 0,
776
+ getTotalScreensHint ? getTotalScreensHint() : 0
777
+ );
778
+ const effectiveScreenMap = screenMap.length > 0
779
+ ? normalizeScreenMapForTotalScreens(screenMap, hintTotalScreens)
780
+ : screenMap;
781
+
782
+ if (screenMap.length > 0) {
783
+ logger.info(`[MultiKiosk] Received agent:screenMap: requested=${screenMap.length}, effective=${effectiveScreenMap.length}, totalScreens=${hintTotalScreens} — killing single kiosk`);
784
+ if (kioskManager) kioskManager.kill().catch(() => {});
785
+ if (watchdog) watchdog.setMultiScreenActive(true);
786
+ multiScreenKiosk.applyScreenMap(effectiveScreenMap, getIdentity()).catch((err) => {
787
+ logger.error('[MultiKiosk] Failed to apply screenMap:', err);
788
+ });
789
+ } else {
732
790
  logger.info('[MultiKiosk] Empty agent:screenMap — deactivating multi-screen');
733
791
  multiScreenKiosk.killAll().catch(() => {});
734
792
  if (watchdog) watchdog.setMultiScreenActive(false);