lightman-agent 1.0.18 → 1.0.21

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 (52) hide show
  1. package/agent.config.json +22 -23
  2. package/agent.config.template.json +30 -31
  3. package/bin/cms-agent.js +269 -248
  4. package/package.json +1 -1
  5. package/public/assets/index-CcBNCz6h.css +1 -1
  6. package/public/assets/index-D9QHMG8k.js +1 -1
  7. package/public/assets/index-H-8HDl46.js +1 -1
  8. package/public/assets/index-YodeiCia.css +1 -1
  9. package/public/assets/index-legacy-DWtNM8y7.js +41 -41
  10. package/public/assets/polyfills-legacy-DyVYWHbW.js +4 -4
  11. package/scripts/guardian.ps1 +50 -124
  12. package/scripts/install-windows.ps1 +60 -116
  13. package/scripts/lightman-agent.logrotate +12 -12
  14. package/scripts/lightman-agent.service +38 -38
  15. package/scripts/reinstall-windows.ps1 +26 -26
  16. package/scripts/restore-desktop.ps1 +32 -32
  17. package/scripts/setup.ps1 +17 -22
  18. package/scripts/sync-display.mjs +20 -20
  19. package/scripts/uninstall-windows.ps1 +54 -54
  20. package/src/commands/display.ts +177 -177
  21. package/src/commands/kiosk.ts +113 -113
  22. package/src/commands/maintenance.ts +106 -106
  23. package/src/commands/network.ts +129 -129
  24. package/src/commands/power.ts +163 -163
  25. package/src/commands/rpi.ts +45 -45
  26. package/src/commands/screenshot.ts +166 -166
  27. package/src/commands/serial.ts +17 -17
  28. package/src/commands/update.ts +124 -124
  29. package/src/index.ts +173 -90
  30. package/src/lib/config.ts +2 -3
  31. package/src/lib/identity.ts +40 -40
  32. package/src/lib/logger.ts +137 -137
  33. package/src/lib/platform.ts +10 -10
  34. package/src/lib/rpi.ts +180 -180
  35. package/src/lib/screenMap.ts +135 -0
  36. package/src/lib/screens.ts +128 -128
  37. package/src/lib/types.ts +176 -177
  38. package/src/services/commands.ts +107 -107
  39. package/src/services/health.ts +161 -161
  40. package/src/services/localEvents.ts +60 -60
  41. package/src/services/logForwarder.ts +72 -72
  42. package/src/services/multiScreenKiosk.ts +116 -83
  43. package/src/services/oscBridge.ts +186 -186
  44. package/src/services/powerScheduler.ts +260 -260
  45. package/src/services/provisioning.ts +120 -122
  46. package/src/services/serialBridge.ts +230 -230
  47. package/src/services/serviceLauncher.ts +183 -183
  48. package/src/services/staticServer.ts +226 -226
  49. package/src/services/updater.ts +249 -249
  50. package/src/services/watchdog.ts +310 -310
  51. package/src/services/websocket.ts +152 -152
  52. 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
+ }