lightman-agent 1.0.4 → 1.0.6

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 +513 -512
  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,152 +1,152 @@
1
- import WebSocket from 'ws';
2
- import type { WsMessage, Identity } from '../lib/types.js';
3
- import type { Logger } from '../lib/logger.js';
4
-
5
- interface WsClientOptions {
6
- serverUrl: string;
7
- identity: Identity;
8
- logger: Logger;
9
- onMessage: (msg: WsMessage) => void;
10
- }
11
-
12
- export class WsClient {
13
- private ws: WebSocket | null = null;
14
- private serverUrl: string;
15
- private identity: Identity;
16
- private logger: Logger;
17
- private onMessage: (msg: WsMessage) => void;
18
-
19
- private reconnectAttempts = 0;
20
- private maxReconnectDelay = 60_000;
21
- private baseDelay = 1_000;
22
- private reconnectTimer: NodeJS.Timeout | null = null;
23
- private closed = false;
24
- private messageQueue: WsMessage[] = [];
25
-
26
- constructor(options: WsClientOptions) {
27
- this.serverUrl = options.serverUrl;
28
- this.identity = options.identity;
29
- this.logger = options.logger;
30
- this.onMessage = options.onMessage;
31
- }
32
-
33
- connect(): void {
34
- if (this.closed) return;
35
-
36
- const wsUrl = this.serverUrl.replace(/^http/, 'ws') +
37
- '/ws/agent?apiKey=' + encodeURIComponent(this.identity.apiKey);
38
-
39
- this.logger.debug('Connecting to', wsUrl.replace(/apiKey=.*/, 'apiKey=***'));
40
-
41
- try {
42
- this.ws = new WebSocket(wsUrl);
43
- } catch (err) {
44
- this.logger.error('Failed to create WebSocket:', err);
45
- this.scheduleReconnect();
46
- return;
47
- }
48
-
49
- this.ws.on('open', () => {
50
- this.logger.info('WebSocket connected');
51
- this.reconnectAttempts = 0;
52
- this.flushQueue();
53
- });
54
-
55
- this.ws.on('message', (data) => {
56
- try {
57
- const msg: WsMessage = JSON.parse(data.toString());
58
- this.onMessage(msg);
59
- } catch (err) {
60
- this.logger.error('Failed to parse WS message:', err);
61
- }
62
- });
63
-
64
- this.ws.on('close', (code, reason) => {
65
- this.logger.warn(`WebSocket closed: ${code} ${reason.toString()}`);
66
- this.ws = null;
67
- if (!this.closed) {
68
- this.scheduleReconnect();
69
- }
70
- });
71
-
72
- this.ws.on('error', (err) => {
73
- this.logger.error('WebSocket error:', err);
74
- // 'close' event will fire after this, triggering reconnect
75
- });
76
-
77
- this.ws.on('ping', () => {
78
- // ws library auto-responds with pong
79
- });
80
- }
81
-
82
- send(msg: WsMessage): void {
83
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
84
- this.ws.send(JSON.stringify(msg));
85
- } else {
86
- // Queue message for when connection is restored
87
- this.messageQueue.push(msg);
88
- // Keep queue bounded
89
- if (this.messageQueue.length > 100) {
90
- this.messageQueue.shift();
91
- }
92
- }
93
- }
94
-
95
- close(): void {
96
- this.closed = true;
97
- if (this.reconnectTimer) {
98
- clearTimeout(this.reconnectTimer);
99
- this.reconnectTimer = null;
100
- }
101
- if (this.ws) {
102
- this.ws.close(1000, 'Agent shutting down');
103
- this.ws = null;
104
- }
105
- this.messageQueue = [];
106
- }
107
-
108
- isConnected(): boolean {
109
- return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
110
- }
111
-
112
- /** Calculate delay with exponential backoff + jitter */
113
- getReconnectDelay(): number {
114
- const exponential = this.baseDelay * Math.pow(2, this.reconnectAttempts);
115
- const capped = Math.min(exponential, this.maxReconnectDelay);
116
- // Add jitter: 0.5x to 1.5x
117
- const jitter = capped * (0.5 + Math.random());
118
- return Math.round(jitter);
119
- }
120
-
121
- private scheduleReconnect(): void {
122
- if (this.closed) return;
123
-
124
- const delay = this.getReconnectDelay();
125
- this.reconnectAttempts++;
126
-
127
- this.logger.info(
128
- `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`
129
- );
130
-
131
- this.reconnectTimer = setTimeout(() => {
132
- this.reconnectTimer = null;
133
- this.connect();
134
- }, delay);
135
- }
136
-
137
- private flushQueue(): void {
138
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
139
-
140
- const now = Date.now();
141
- const MAX_AGE = 300_000; // 5 minutes
142
-
143
- while (this.messageQueue.length > 0) {
144
- const msg = this.messageQueue.shift()!;
145
- if (now - msg.timestamp > MAX_AGE) {
146
- this.logger.warn(`Discarding stale queued message: ${msg.type}`);
147
- continue;
148
- }
149
- this.ws.send(JSON.stringify(msg));
150
- }
151
- }
152
- }
1
+ import WebSocket from 'ws';
2
+ import type { WsMessage, Identity } from '../lib/types.js';
3
+ import type { Logger } from '../lib/logger.js';
4
+
5
+ interface WsClientOptions {
6
+ serverUrl: string;
7
+ identity: Identity;
8
+ logger: Logger;
9
+ onMessage: (msg: WsMessage) => void;
10
+ }
11
+
12
+ export class WsClient {
13
+ private ws: WebSocket | null = null;
14
+ private serverUrl: string;
15
+ private identity: Identity;
16
+ private logger: Logger;
17
+ private onMessage: (msg: WsMessage) => void;
18
+
19
+ private reconnectAttempts = 0;
20
+ private maxReconnectDelay = 60_000;
21
+ private baseDelay = 1_000;
22
+ private reconnectTimer: NodeJS.Timeout | null = null;
23
+ private closed = false;
24
+ private messageQueue: WsMessage[] = [];
25
+
26
+ constructor(options: WsClientOptions) {
27
+ this.serverUrl = options.serverUrl;
28
+ this.identity = options.identity;
29
+ this.logger = options.logger;
30
+ this.onMessage = options.onMessage;
31
+ }
32
+
33
+ connect(): void {
34
+ if (this.closed) return;
35
+
36
+ const wsUrl = this.serverUrl.replace(/^http/, 'ws') +
37
+ '/ws/agent?apiKey=' + encodeURIComponent(this.identity.apiKey);
38
+
39
+ this.logger.debug('Connecting to', wsUrl.replace(/apiKey=.*/, 'apiKey=***'));
40
+
41
+ try {
42
+ this.ws = new WebSocket(wsUrl);
43
+ } catch (err) {
44
+ this.logger.error('Failed to create WebSocket:', err);
45
+ this.scheduleReconnect();
46
+ return;
47
+ }
48
+
49
+ this.ws.on('open', () => {
50
+ this.logger.info('WebSocket connected');
51
+ this.reconnectAttempts = 0;
52
+ this.flushQueue();
53
+ });
54
+
55
+ this.ws.on('message', (data) => {
56
+ try {
57
+ const msg: WsMessage = JSON.parse(data.toString());
58
+ this.onMessage(msg);
59
+ } catch (err) {
60
+ this.logger.error('Failed to parse WS message:', err);
61
+ }
62
+ });
63
+
64
+ this.ws.on('close', (code, reason) => {
65
+ this.logger.warn(`WebSocket closed: ${code} ${reason.toString()}`);
66
+ this.ws = null;
67
+ if (!this.closed) {
68
+ this.scheduleReconnect();
69
+ }
70
+ });
71
+
72
+ this.ws.on('error', (err) => {
73
+ this.logger.error('WebSocket error:', err);
74
+ // 'close' event will fire after this, triggering reconnect
75
+ });
76
+
77
+ this.ws.on('ping', () => {
78
+ // ws library auto-responds with pong
79
+ });
80
+ }
81
+
82
+ send(msg: WsMessage): void {
83
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
84
+ this.ws.send(JSON.stringify(msg));
85
+ } else {
86
+ // Queue message for when connection is restored
87
+ this.messageQueue.push(msg);
88
+ // Keep queue bounded
89
+ if (this.messageQueue.length > 100) {
90
+ this.messageQueue.shift();
91
+ }
92
+ }
93
+ }
94
+
95
+ close(): void {
96
+ this.closed = true;
97
+ if (this.reconnectTimer) {
98
+ clearTimeout(this.reconnectTimer);
99
+ this.reconnectTimer = null;
100
+ }
101
+ if (this.ws) {
102
+ this.ws.close(1000, 'Agent shutting down');
103
+ this.ws = null;
104
+ }
105
+ this.messageQueue = [];
106
+ }
107
+
108
+ isConnected(): boolean {
109
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
110
+ }
111
+
112
+ /** Calculate delay with exponential backoff + jitter */
113
+ getReconnectDelay(): number {
114
+ const exponential = this.baseDelay * Math.pow(2, this.reconnectAttempts);
115
+ const capped = Math.min(exponential, this.maxReconnectDelay);
116
+ // Add jitter: 0.5x to 1.5x
117
+ const jitter = capped * (0.5 + Math.random());
118
+ return Math.round(jitter);
119
+ }
120
+
121
+ private scheduleReconnect(): void {
122
+ if (this.closed) return;
123
+
124
+ const delay = this.getReconnectDelay();
125
+ this.reconnectAttempts++;
126
+
127
+ this.logger.info(
128
+ `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`
129
+ );
130
+
131
+ this.reconnectTimer = setTimeout(() => {
132
+ this.reconnectTimer = null;
133
+ this.connect();
134
+ }, delay);
135
+ }
136
+
137
+ private flushQueue(): void {
138
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
139
+
140
+ const now = Date.now();
141
+ const MAX_AGE = 300_000; // 5 minutes
142
+
143
+ while (this.messageQueue.length > 0) {
144
+ const msg = this.messageQueue.shift()!;
145
+ if (now - msg.timestamp > MAX_AGE) {
146
+ this.logger.warn(`Discarding stale queued message: ${msg.type}`);
147
+ continue;
148
+ }
149
+ this.ws.send(JSON.stringify(msg));
150
+ }
151
+ }
152
+ }
package/tsconfig.json CHANGED
@@ -1,28 +1,28 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022"],
7
- "outDir": "dist",
8
- "rootDir": "src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "resolveJsonModule": true,
14
- "declaration": true,
15
- "sourceMap": true,
16
- "noUnusedLocals": false,
17
- "noUnusedParameters": false,
18
- "noFallthroughCasesInSwitch": true,
19
- "isolatedModules": true,
20
- "paths": {
21
- "@/*": ["./src/*"]
22
- },
23
- "baseUrl": ".",
24
- "types": ["node"]
25
- },
26
- "include": ["src/**/*"],
27
- "exclude": ["node_modules", "dist", "src/__tests__"]
28
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "outDir": "dist",
8
+ "rootDir": "src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "sourceMap": true,
16
+ "noUnusedLocals": false,
17
+ "noUnusedParameters": false,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "isolatedModules": true,
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ },
23
+ "baseUrl": ".",
24
+ "types": ["node"]
25
+ },
26
+ "include": ["src/**/*"],
27
+ "exclude": ["node_modules", "dist", "src/__tests__"]
28
+ }