lightman-agent 1.0.5 → 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 +505 -505
  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,186 +1,186 @@
1
- import { createSocket, type Socket } from 'dgram';
2
- import type { WsClient } from './websocket.js';
3
- import type { Logger } from '../lib/logger.js';
4
-
5
- /**
6
- * OscBridge — listens for OSC messages over UDP and converts them to
7
- * hardware events, mirroring the SerialBridge pattern.
8
- *
9
- * When the configured OSC address is received with arg === 1, it emits
10
- * an `osc:trigger` event via the local event broadcaster and the server WS.
11
- *
12
- * Uses Node.js built-in `dgram` — no external dependencies.
13
- *
14
- * OSC wire format (minimal parser):
15
- * - Address: null-terminated string padded to 4-byte boundary
16
- * - Type tag: "," + type chars, null-terminated padded to 4-byte boundary
17
- * - Arguments: int32 (big-endian) for 'i', float32 for 'f'
18
- */
19
- export class OscBridge {
20
- private wsClient: WsClient;
21
- private logger: Logger;
22
- private port: number;
23
- private host: string;
24
- private address: string;
25
- private running = false;
26
- private socket: Socket | null = null;
27
- private onEvent?: (event: Record<string, unknown>) => void;
28
-
29
- constructor(opts: {
30
- wsClient: WsClient;
31
- logger: Logger;
32
- port: number;
33
- host?: string;
34
- address: string;
35
- onEvent?: (event: Record<string, unknown>) => void;
36
- }) {
37
- this.wsClient = opts.wsClient;
38
- this.logger = opts.logger;
39
- this.port = opts.port;
40
- this.host = opts.host || '0.0.0.0';
41
- this.address = opts.address;
42
- this.onEvent = opts.onEvent;
43
- }
44
-
45
- start(): void {
46
- if (this.running) return;
47
- this.running = true;
48
-
49
- this.logger.info(`[OSC] Starting bridge — listening on UDP ${this.host}:${this.port} for address "${this.address}"`);
50
-
51
- this.socket = createSocket('udp4');
52
-
53
- this.socket.on('message', (msg: Buffer) => {
54
- try {
55
- const parsed = parseOscMessage(msg);
56
- if (!parsed) return;
57
-
58
- this.logger.debug(`[OSC] Received: ${parsed.address} args=${JSON.stringify(parsed.args)}`);
59
-
60
- // Match address
61
- if (parsed.address === this.address) {
62
- const firstArg = parsed.args[0];
63
- // Trigger on arg === 1 (int or float)
64
- if (firstArg === 1 || firstArg === 1.0) {
65
- this.emitTrigger();
66
- }
67
- }
68
- } catch (err) {
69
- this.logger.debug('[OSC] Failed to parse message:', err);
70
- }
71
- });
72
-
73
- this.socket.on('error', (err) => {
74
- this.logger.error('[OSC] Socket error:', err);
75
- if (this.running) {
76
- this.socket?.close();
77
- this.socket = null;
78
- this.logger.info('[OSC] Restarting in 3s...');
79
- setTimeout(() => {
80
- if (this.running) this.start();
81
- }, 3000);
82
- }
83
- });
84
-
85
- this.socket.bind(this.port, this.host, () => {
86
- this.logger.info(`[OSC] Bridge listening on UDP ${this.host}:${this.port}`);
87
- });
88
- }
89
-
90
- stop(): void {
91
- this.running = false;
92
- if (this.socket) {
93
- try { this.socket.close(); } catch { /* ignore */ }
94
- this.socket = null;
95
- }
96
- this.logger.info('[OSC] Bridge stopped');
97
- }
98
-
99
- isRunning(): boolean {
100
- return this.running;
101
- }
102
-
103
- private emitTrigger(): void {
104
- const event: Record<string, unknown> = {
105
- type: 'osc:trigger',
106
- address: this.address,
107
- timestamp: Date.now(),
108
- };
109
-
110
- this.logger.info(`[OSC] Trigger event: ${this.address}`);
111
-
112
- // Send to server
113
- this.wsClient.send({
114
- type: 'osc-bridge:event',
115
- payload: event,
116
- timestamp: Date.now(),
117
- });
118
-
119
- // Broadcast locally to Chrome display
120
- this.onEvent?.(event);
121
- }
122
- }
123
-
124
- // ==========================================
125
- // Minimal OSC message parser
126
- // ==========================================
127
-
128
- interface OscMessage {
129
- address: string;
130
- args: (number | string)[];
131
- }
132
-
133
- function parseOscMessage(buf: Buffer): OscMessage | null {
134
- let offset = 0;
135
-
136
- // Read address string
137
- const address = readOscString(buf, offset);
138
- if (!address.value || address.value[0] !== '/') return null;
139
- offset = address.next;
140
-
141
- // Read type tag string
142
- const typeTags = readOscString(buf, offset);
143
- offset = typeTags.next;
144
-
145
- const tags = typeTags.value || '';
146
- // Type tag starts with ','
147
- const types = tags.startsWith(',') ? tags.slice(1) : tags;
148
-
149
- // Read arguments
150
- const args: (number | string)[] = [];
151
- for (const t of types) {
152
- if (offset >= buf.length) break;
153
-
154
- switch (t) {
155
- case 'i': // int32
156
- args.push(buf.readInt32BE(offset));
157
- offset += 4;
158
- break;
159
- case 'f': // float32
160
- args.push(buf.readFloatBE(offset));
161
- offset += 4;
162
- break;
163
- case 's': { // string
164
- const s = readOscString(buf, offset);
165
- args.push(s.value);
166
- offset = s.next;
167
- break;
168
- }
169
- default:
170
- // Unknown type, stop parsing
171
- return { address: address.value, args };
172
- }
173
- }
174
-
175
- return { address: address.value, args };
176
- }
177
-
178
- function readOscString(buf: Buffer, offset: number): { value: string; next: number } {
179
- let end = offset;
180
- while (end < buf.length && buf[end] !== 0) end++;
181
- const value = buf.toString('utf-8', offset, end);
182
- // OSC strings are padded to 4-byte boundary (including null terminator)
183
- const padded = end + 1;
184
- const next = padded + ((4 - (padded % 4)) % 4);
185
- return { value, next };
186
- }
1
+ import { createSocket, type Socket } from 'dgram';
2
+ import type { WsClient } from './websocket.js';
3
+ import type { Logger } from '../lib/logger.js';
4
+
5
+ /**
6
+ * OscBridge — listens for OSC messages over UDP and converts them to
7
+ * hardware events, mirroring the SerialBridge pattern.
8
+ *
9
+ * When the configured OSC address is received with arg === 1, it emits
10
+ * an `osc:trigger` event via the local event broadcaster and the server WS.
11
+ *
12
+ * Uses Node.js built-in `dgram` — no external dependencies.
13
+ *
14
+ * OSC wire format (minimal parser):
15
+ * - Address: null-terminated string padded to 4-byte boundary
16
+ * - Type tag: "," + type chars, null-terminated padded to 4-byte boundary
17
+ * - Arguments: int32 (big-endian) for 'i', float32 for 'f'
18
+ */
19
+ export class OscBridge {
20
+ private wsClient: WsClient;
21
+ private logger: Logger;
22
+ private port: number;
23
+ private host: string;
24
+ private address: string;
25
+ private running = false;
26
+ private socket: Socket | null = null;
27
+ private onEvent?: (event: Record<string, unknown>) => void;
28
+
29
+ constructor(opts: {
30
+ wsClient: WsClient;
31
+ logger: Logger;
32
+ port: number;
33
+ host?: string;
34
+ address: string;
35
+ onEvent?: (event: Record<string, unknown>) => void;
36
+ }) {
37
+ this.wsClient = opts.wsClient;
38
+ this.logger = opts.logger;
39
+ this.port = opts.port;
40
+ this.host = opts.host || '0.0.0.0';
41
+ this.address = opts.address;
42
+ this.onEvent = opts.onEvent;
43
+ }
44
+
45
+ start(): void {
46
+ if (this.running) return;
47
+ this.running = true;
48
+
49
+ this.logger.info(`[OSC] Starting bridge — listening on UDP ${this.host}:${this.port} for address "${this.address}"`);
50
+
51
+ this.socket = createSocket('udp4');
52
+
53
+ this.socket.on('message', (msg: Buffer) => {
54
+ try {
55
+ const parsed = parseOscMessage(msg);
56
+ if (!parsed) return;
57
+
58
+ this.logger.debug(`[OSC] Received: ${parsed.address} args=${JSON.stringify(parsed.args)}`);
59
+
60
+ // Match address
61
+ if (parsed.address === this.address) {
62
+ const firstArg = parsed.args[0];
63
+ // Trigger on arg === 1 (int or float)
64
+ if (firstArg === 1 || firstArg === 1.0) {
65
+ this.emitTrigger();
66
+ }
67
+ }
68
+ } catch (err) {
69
+ this.logger.debug('[OSC] Failed to parse message:', err);
70
+ }
71
+ });
72
+
73
+ this.socket.on('error', (err) => {
74
+ this.logger.error('[OSC] Socket error:', err);
75
+ if (this.running) {
76
+ this.socket?.close();
77
+ this.socket = null;
78
+ this.logger.info('[OSC] Restarting in 3s...');
79
+ setTimeout(() => {
80
+ if (this.running) this.start();
81
+ }, 3000);
82
+ }
83
+ });
84
+
85
+ this.socket.bind(this.port, this.host, () => {
86
+ this.logger.info(`[OSC] Bridge listening on UDP ${this.host}:${this.port}`);
87
+ });
88
+ }
89
+
90
+ stop(): void {
91
+ this.running = false;
92
+ if (this.socket) {
93
+ try { this.socket.close(); } catch { /* ignore */ }
94
+ this.socket = null;
95
+ }
96
+ this.logger.info('[OSC] Bridge stopped');
97
+ }
98
+
99
+ isRunning(): boolean {
100
+ return this.running;
101
+ }
102
+
103
+ private emitTrigger(): void {
104
+ const event: Record<string, unknown> = {
105
+ type: 'osc:trigger',
106
+ address: this.address,
107
+ timestamp: Date.now(),
108
+ };
109
+
110
+ this.logger.info(`[OSC] Trigger event: ${this.address}`);
111
+
112
+ // Send to server
113
+ this.wsClient.send({
114
+ type: 'osc-bridge:event',
115
+ payload: event,
116
+ timestamp: Date.now(),
117
+ });
118
+
119
+ // Broadcast locally to Chrome display
120
+ this.onEvent?.(event);
121
+ }
122
+ }
123
+
124
+ // ==========================================
125
+ // Minimal OSC message parser
126
+ // ==========================================
127
+
128
+ interface OscMessage {
129
+ address: string;
130
+ args: (number | string)[];
131
+ }
132
+
133
+ function parseOscMessage(buf: Buffer): OscMessage | null {
134
+ let offset = 0;
135
+
136
+ // Read address string
137
+ const address = readOscString(buf, offset);
138
+ if (!address.value || address.value[0] !== '/') return null;
139
+ offset = address.next;
140
+
141
+ // Read type tag string
142
+ const typeTags = readOscString(buf, offset);
143
+ offset = typeTags.next;
144
+
145
+ const tags = typeTags.value || '';
146
+ // Type tag starts with ','
147
+ const types = tags.startsWith(',') ? tags.slice(1) : tags;
148
+
149
+ // Read arguments
150
+ const args: (number | string)[] = [];
151
+ for (const t of types) {
152
+ if (offset >= buf.length) break;
153
+
154
+ switch (t) {
155
+ case 'i': // int32
156
+ args.push(buf.readInt32BE(offset));
157
+ offset += 4;
158
+ break;
159
+ case 'f': // float32
160
+ args.push(buf.readFloatBE(offset));
161
+ offset += 4;
162
+ break;
163
+ case 's': { // string
164
+ const s = readOscString(buf, offset);
165
+ args.push(s.value);
166
+ offset = s.next;
167
+ break;
168
+ }
169
+ default:
170
+ // Unknown type, stop parsing
171
+ return { address: address.value, args };
172
+ }
173
+ }
174
+
175
+ return { address: address.value, args };
176
+ }
177
+
178
+ function readOscString(buf: Buffer, offset: number): { value: string; next: number } {
179
+ let end = offset;
180
+ while (end < buf.length && buf[end] !== 0) end++;
181
+ const value = buf.toString('utf-8', offset, end);
182
+ // OSC strings are padded to 4-byte boundary (including null terminator)
183
+ const padded = end + 1;
184
+ const next = padded + ((4 - (padded % 4)) % 4);
185
+ return { value, next };
186
+ }