lightman-agent 1.0.5 → 1.0.7

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,183 +1,183 @@
1
- import { spawn, execSync } from 'child_process';
2
- import type { ChildProcess } from 'child_process';
3
- import { resolve } from 'path';
4
- import type { Logger } from '../lib/logger.js';
5
-
6
- interface ManagedService {
7
- name: string;
8
- cwd: string;
9
- command: string;
10
- args: string[];
11
- port: number;
12
- process: ChildProcess | null;
13
- }
14
-
15
- export class ServiceLauncher {
16
- private services: ManagedService[] = [];
17
- private logger: Logger;
18
- private projectRoot: string;
19
-
20
- constructor(logger: Logger, projectRoot: string) {
21
- this.logger = logger;
22
- this.projectRoot = projectRoot;
23
- }
24
-
25
- /**
26
- * Kill any existing processes on managed ports, then start fresh.
27
- */
28
- async startAll(): Promise<void> {
29
- const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
30
-
31
- this.services = [
32
- {
33
- name: 'server',
34
- cwd: resolve(this.projectRoot, 'server'),
35
- command: npmCmd,
36
- args: ['run', 'dev'],
37
- port: 3401,
38
- process: null,
39
- },
40
- {
41
- name: 'display',
42
- cwd: resolve(this.projectRoot, 'display'),
43
- command: npmCmd,
44
- args: ['run', 'dev'],
45
- port: 3403,
46
- process: null,
47
- },
48
- ];
49
-
50
- // Kill anything already on these ports for a clean start
51
- for (const svc of this.services) {
52
- await this.killProcessOnPort(svc.port);
53
- }
54
-
55
- // Small delay to let ports free up
56
- await new Promise((r) => setTimeout(r, 1_000));
57
-
58
- for (const svc of this.services) {
59
- this.logger.info(`Starting ${svc.name} (port ${svc.port})...`);
60
- svc.process = spawn(svc.command, svc.args, {
61
- cwd: svc.cwd,
62
- stdio: 'pipe',
63
- detached: false,
64
- shell: true,
65
- });
66
-
67
- svc.process.stdout?.on('data', (data: Buffer) => {
68
- const lines = data.toString().trim();
69
- if (lines) this.logger.debug(`[${svc.name}] ${lines}`);
70
- });
71
-
72
- svc.process.stderr?.on('data', (data: Buffer) => {
73
- const lines = data.toString().trim();
74
- if (lines) this.logger.debug(`[${svc.name}:err] ${lines}`);
75
- });
76
-
77
- svc.process.on('exit', (code) => {
78
- this.logger.warn(`${svc.name} exited with code ${code}`);
79
- svc.process = null;
80
- });
81
-
82
- svc.process.on('error', (err) => {
83
- this.logger.error(`${svc.name} spawn error: ${err.message}`);
84
- svc.process = null;
85
- });
86
- }
87
-
88
- // Wait for each service to become reachable
89
- for (const svc of this.services) {
90
- await this.waitForPort(svc.name, svc.port, 60_000);
91
- }
92
-
93
- this.logger.info('All services started successfully');
94
- }
95
-
96
- /**
97
- * Stop all managed services.
98
- */
99
- stopAll(): void {
100
- for (const svc of this.services) {
101
- if (svc.process) {
102
- this.logger.info(`Stopping ${svc.name} (pid ${svc.process.pid})...`);
103
- try {
104
- if (process.platform === 'win32') {
105
- spawn('taskkill', ['/pid', String(svc.process.pid), '/T', '/F'], {
106
- stdio: 'ignore',
107
- shell: true,
108
- });
109
- } else {
110
- svc.process.kill('SIGTERM');
111
- }
112
- } catch {
113
- // already dead
114
- }
115
- svc.process = null;
116
- }
117
- }
118
- }
119
-
120
- isRunning(name: string): boolean {
121
- const svc = this.services.find((s) => s.name === name);
122
- return svc?.process !== null && svc?.process?.exitCode === null;
123
- }
124
-
125
- private async killProcessOnPort(port: number): Promise<void> {
126
- try {
127
- if (process.platform === 'win32') {
128
- // Find PID using the port on Windows
129
- const result = execSync(
130
- `netstat -ano | findstr :${port} | findstr LISTENING`,
131
- { encoding: 'utf-8', timeout: 5_000 }
132
- ).trim();
133
- const lines = result.split('\n');
134
- const pids = new Set<string>();
135
- for (const line of lines) {
136
- const parts = line.trim().split(/\s+/);
137
- const pid = parts[parts.length - 1];
138
- if (pid && pid !== '0') pids.add(pid);
139
- }
140
- for (const pid of pids) {
141
- this.logger.info(`Killing existing process on port ${port} (pid ${pid})`);
142
- try {
143
- execSync(`taskkill /pid ${pid} /T /F`, { timeout: 5_000 });
144
- } catch {
145
- // process may already be gone
146
- }
147
- }
148
- } else {
149
- // Unix: use fuser or lsof
150
- try {
151
- execSync(`fuser -k ${port}/tcp`, { timeout: 5_000 });
152
- this.logger.info(`Killed existing process on port ${port}`);
153
- } catch {
154
- // no process on port, that's fine
155
- }
156
- }
157
- } catch {
158
- // No process found on port — that's fine
159
- }
160
- }
161
-
162
- private async waitForPort(name: string, port: number, timeoutMs: number): Promise<void> {
163
- const start = Date.now();
164
- const interval = 1_500;
165
-
166
- while (Date.now() - start < timeoutMs) {
167
- try {
168
- const res = await fetch(`http://localhost:${port}/`, {
169
- signal: AbortSignal.timeout(2_000),
170
- });
171
- if (res.status > 0) {
172
- this.logger.info(`${name} is ready on port ${port}`);
173
- return;
174
- }
175
- } catch {
176
- // not ready yet
177
- }
178
- await new Promise((r) => setTimeout(r, interval));
179
- }
180
-
181
- this.logger.warn(`${name} did not become ready on port ${port} within ${timeoutMs}ms, continuing anyway`);
182
- }
183
- }
1
+ import { spawn, execSync } from 'child_process';
2
+ import type { ChildProcess } from 'child_process';
3
+ import { resolve } from 'path';
4
+ import type { Logger } from '../lib/logger.js';
5
+
6
+ interface ManagedService {
7
+ name: string;
8
+ cwd: string;
9
+ command: string;
10
+ args: string[];
11
+ port: number;
12
+ process: ChildProcess | null;
13
+ }
14
+
15
+ export class ServiceLauncher {
16
+ private services: ManagedService[] = [];
17
+ private logger: Logger;
18
+ private projectRoot: string;
19
+
20
+ constructor(logger: Logger, projectRoot: string) {
21
+ this.logger = logger;
22
+ this.projectRoot = projectRoot;
23
+ }
24
+
25
+ /**
26
+ * Kill any existing processes on managed ports, then start fresh.
27
+ */
28
+ async startAll(): Promise<void> {
29
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
30
+
31
+ this.services = [
32
+ {
33
+ name: 'server',
34
+ cwd: resolve(this.projectRoot, 'server'),
35
+ command: npmCmd,
36
+ args: ['run', 'dev'],
37
+ port: 3401,
38
+ process: null,
39
+ },
40
+ {
41
+ name: 'display',
42
+ cwd: resolve(this.projectRoot, 'display'),
43
+ command: npmCmd,
44
+ args: ['run', 'dev'],
45
+ port: 3403,
46
+ process: null,
47
+ },
48
+ ];
49
+
50
+ // Kill anything already on these ports for a clean start
51
+ for (const svc of this.services) {
52
+ await this.killProcessOnPort(svc.port);
53
+ }
54
+
55
+ // Small delay to let ports free up
56
+ await new Promise((r) => setTimeout(r, 1_000));
57
+
58
+ for (const svc of this.services) {
59
+ this.logger.info(`Starting ${svc.name} (port ${svc.port})...`);
60
+ svc.process = spawn(svc.command, svc.args, {
61
+ cwd: svc.cwd,
62
+ stdio: 'pipe',
63
+ detached: false,
64
+ shell: true,
65
+ });
66
+
67
+ svc.process.stdout?.on('data', (data: Buffer) => {
68
+ const lines = data.toString().trim();
69
+ if (lines) this.logger.debug(`[${svc.name}] ${lines}`);
70
+ });
71
+
72
+ svc.process.stderr?.on('data', (data: Buffer) => {
73
+ const lines = data.toString().trim();
74
+ if (lines) this.logger.debug(`[${svc.name}:err] ${lines}`);
75
+ });
76
+
77
+ svc.process.on('exit', (code) => {
78
+ this.logger.warn(`${svc.name} exited with code ${code}`);
79
+ svc.process = null;
80
+ });
81
+
82
+ svc.process.on('error', (err) => {
83
+ this.logger.error(`${svc.name} spawn error: ${err.message}`);
84
+ svc.process = null;
85
+ });
86
+ }
87
+
88
+ // Wait for each service to become reachable
89
+ for (const svc of this.services) {
90
+ await this.waitForPort(svc.name, svc.port, 60_000);
91
+ }
92
+
93
+ this.logger.info('All services started successfully');
94
+ }
95
+
96
+ /**
97
+ * Stop all managed services.
98
+ */
99
+ stopAll(): void {
100
+ for (const svc of this.services) {
101
+ if (svc.process) {
102
+ this.logger.info(`Stopping ${svc.name} (pid ${svc.process.pid})...`);
103
+ try {
104
+ if (process.platform === 'win32') {
105
+ spawn('taskkill', ['/pid', String(svc.process.pid), '/T', '/F'], {
106
+ stdio: 'ignore',
107
+ shell: true,
108
+ });
109
+ } else {
110
+ svc.process.kill('SIGTERM');
111
+ }
112
+ } catch {
113
+ // already dead
114
+ }
115
+ svc.process = null;
116
+ }
117
+ }
118
+ }
119
+
120
+ isRunning(name: string): boolean {
121
+ const svc = this.services.find((s) => s.name === name);
122
+ return svc?.process !== null && svc?.process?.exitCode === null;
123
+ }
124
+
125
+ private async killProcessOnPort(port: number): Promise<void> {
126
+ try {
127
+ if (process.platform === 'win32') {
128
+ // Find PID using the port on Windows
129
+ const result = execSync(
130
+ `netstat -ano | findstr :${port} | findstr LISTENING`,
131
+ { encoding: 'utf-8', timeout: 5_000 }
132
+ ).trim();
133
+ const lines = result.split('\n');
134
+ const pids = new Set<string>();
135
+ for (const line of lines) {
136
+ const parts = line.trim().split(/\s+/);
137
+ const pid = parts[parts.length - 1];
138
+ if (pid && pid !== '0') pids.add(pid);
139
+ }
140
+ for (const pid of pids) {
141
+ this.logger.info(`Killing existing process on port ${port} (pid ${pid})`);
142
+ try {
143
+ execSync(`taskkill /pid ${pid} /T /F`, { timeout: 5_000 });
144
+ } catch {
145
+ // process may already be gone
146
+ }
147
+ }
148
+ } else {
149
+ // Unix: use fuser or lsof
150
+ try {
151
+ execSync(`fuser -k ${port}/tcp`, { timeout: 5_000 });
152
+ this.logger.info(`Killed existing process on port ${port}`);
153
+ } catch {
154
+ // no process on port, that's fine
155
+ }
156
+ }
157
+ } catch {
158
+ // No process found on port — that's fine
159
+ }
160
+ }
161
+
162
+ private async waitForPort(name: string, port: number, timeoutMs: number): Promise<void> {
163
+ const start = Date.now();
164
+ const interval = 1_500;
165
+
166
+ while (Date.now() - start < timeoutMs) {
167
+ try {
168
+ const res = await fetch(`http://localhost:${port}/`, {
169
+ signal: AbortSignal.timeout(2_000),
170
+ });
171
+ if (res.status > 0) {
172
+ this.logger.info(`${name} is ready on port ${port}`);
173
+ return;
174
+ }
175
+ } catch {
176
+ // not ready yet
177
+ }
178
+ await new Promise((r) => setTimeout(r, interval));
179
+ }
180
+
181
+ this.logger.warn(`${name} did not become ready on port ${port} within ${timeoutMs}ms, continuing anyway`);
182
+ }
183
+ }