matterbridge 3.3.3-dev-20251019-a73923c → 3.3.4-dev-20251020-4d2dd49
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.
- package/CHANGELOG.md +21 -0
- package/dist/cli.js +109 -313
- package/dist/cliHistory.js +15 -51
- package/dist/matterbridge.js +6 -78
- package/dist/utils/commandLine.js +6 -1
- package/dist/utils/inspector.js +200 -0
- package/dist/utils/tracker.js +229 -0
- package/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_mdi.js +1 -1
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +44 -44
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -23,6 +23,27 @@ Advantages:
|
|
|
23
23
|
- isolation between threads;
|
|
24
24
|
- individual plugin isolation in childbridge mode;
|
|
25
25
|
|
|
26
|
+
## [3.3.4] - Not released
|
|
27
|
+
|
|
28
|
+
### Breaking Changes
|
|
29
|
+
|
|
30
|
+
- [nodejs]: Matterbridge will not start if the Node.js version is less then 20.x.x.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- [frontend]: Added debounce to MatterSettings.
|
|
35
|
+
- [cli]: Bumped `cli` version to 3.0.0 with backport of Traker and Inspector from thread module.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- [package]: Updated dependencies.
|
|
40
|
+
- [matter]: Bumped `matter.js` version to 0.15.6. Thanks matter.js!
|
|
41
|
+
- [frontend]: Bumped `frontend` version to 3.2.3.
|
|
42
|
+
|
|
43
|
+
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
44
|
+
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
|
45
|
+
</a>
|
|
46
|
+
|
|
26
47
|
## [3.3.3] - 2025-10-18
|
|
27
48
|
|
|
28
49
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -1,331 +1,66 @@
|
|
|
1
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
2
2
|
console.log('\u001B[32mCLI loaded.\u001B[40;0m');
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { cliEmitter, setLastOsCpuUsage, setLastProcessCpuUsage } from './cliEmitter.js';
|
|
7
|
-
import { history, historyIndex, historySize, setHistoryIndex } from './cliHistory.js';
|
|
8
|
-
import { getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
3
|
+
import { AnsiLogger } from 'node-ansi-logger';
|
|
4
|
+
import { cliEmitter } from './cliEmitter.js';
|
|
5
|
+
import { hasParameter, hasAnyParameter } from './utils/commandLine.js';
|
|
9
6
|
import { Matterbridge } from './matterbridge.js';
|
|
7
|
+
import { inspectError } from './utils/error.js';
|
|
8
|
+
import { Tracker } from './utils/tracker.js';
|
|
9
|
+
import { Inspector } from './utils/inspector.js';
|
|
10
10
|
export let instance;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const trace = hasParameter('trace');
|
|
14
|
-
let memoryCheckInterval;
|
|
15
|
-
const memoryCheckIntervalMs = getIntParameter('memoryinterval') ?? 10 * 1000;
|
|
16
|
-
let memoryPeakResetTimeout;
|
|
17
|
-
let prevCpus;
|
|
18
|
-
let prevProcessCpu;
|
|
19
|
-
let peakCpu = 0;
|
|
20
|
-
let peakProcessCpu = 0;
|
|
21
|
-
let peakRss = 0;
|
|
22
|
-
let peakHeapUsed = 0;
|
|
23
|
-
let peakHeapTotal = 0;
|
|
24
|
-
let peakExternal = 0;
|
|
25
|
-
let peakArrayBuffers = 0;
|
|
11
|
+
export const tracker = new Tracker('Cli', true, true);
|
|
12
|
+
export const inspector = new Inspector('Cli', true, true);
|
|
26
13
|
const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
const formatOsUpTime = (seconds) => {
|
|
42
|
-
if (seconds >= 86400) {
|
|
43
|
-
const days = Math.floor(seconds / 86400);
|
|
44
|
-
return `${days} day${days !== 1 ? 's' : ''}`;
|
|
45
|
-
}
|
|
46
|
-
if (seconds >= 3600) {
|
|
47
|
-
const hours = Math.floor(seconds / 3600);
|
|
48
|
-
return `${hours} hour${hours !== 1 ? 's' : ''}`;
|
|
49
|
-
}
|
|
50
|
-
if (seconds >= 60) {
|
|
51
|
-
const minutes = Math.floor(seconds / 60);
|
|
52
|
-
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
|
|
53
|
-
}
|
|
54
|
-
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
55
|
-
};
|
|
56
|
-
async function startCpuMemoryCheck() {
|
|
14
|
+
function startCpuMemoryCheck() {
|
|
15
|
+
log.debug(`Cpu memory check starting...`);
|
|
16
|
+
tracker.start();
|
|
17
|
+
tracker.on('uptime', (os, process) => {
|
|
18
|
+
cliEmitter.emit('uptime', tracker.formatOsUpTime(Math.floor(os)), tracker.formatOsUpTime(Math.floor(process)));
|
|
19
|
+
});
|
|
20
|
+
tracker.on('snapshot', (snapshot) => {
|
|
21
|
+
cliEmitter.emit('memory', tracker.formatBytes(snapshot.totalMemory), tracker.formatBytes(snapshot.freeMemory), tracker.formatBytes(snapshot.rss), tracker.formatBytes(snapshot.heapTotal), tracker.formatBytes(snapshot.heapUsed), tracker.formatBytes(snapshot.external), tracker.formatBytes(snapshot.arrayBuffers));
|
|
22
|
+
cliEmitter.emit('cpu', snapshot.osCpu, snapshot.processCpu);
|
|
23
|
+
});
|
|
57
24
|
log.debug(`Cpu memory check started`);
|
|
58
|
-
prevCpus = os.cpus();
|
|
59
|
-
prevProcessCpu = process.cpuUsage();
|
|
60
|
-
const interval = () => {
|
|
61
|
-
const systemUptime = formatOsUpTime(Math.floor(os.uptime()));
|
|
62
|
-
const processUptime = formatOsUpTime(Math.floor(process.uptime()));
|
|
63
|
-
cliEmitter.emit('uptime', systemUptime, processUptime);
|
|
64
|
-
const totalMemory = formatMemoryUsage(os.totalmem());
|
|
65
|
-
const freeMemory = formatMemoryUsage(os.freemem());
|
|
66
|
-
const memoryUsageRaw = process.memoryUsage();
|
|
67
|
-
const rss = formatMemoryUsage(memoryUsageRaw.rss);
|
|
68
|
-
const heapTotal = formatMemoryUsage(memoryUsageRaw.heapTotal);
|
|
69
|
-
const heapUsed = formatMemoryUsage(memoryUsageRaw.heapUsed);
|
|
70
|
-
const external = formatMemoryUsage(memoryUsageRaw.external);
|
|
71
|
-
const arrayBuffers = formatMemoryUsage(memoryUsageRaw.arrayBuffers);
|
|
72
|
-
if (memoryUsageRaw.rss > peakRss) {
|
|
73
|
-
if (peakRss && trace)
|
|
74
|
-
log.debug(`****${RED}${BRIGHT}Rss peak detected.${db} Peak rss from ${CYAN}${formatMemoryUsage(peakRss)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.rss)}${db}`);
|
|
75
|
-
peakRss = memoryUsageRaw.rss;
|
|
76
|
-
}
|
|
77
|
-
if (memoryUsageRaw.heapUsed > peakHeapUsed) {
|
|
78
|
-
if (peakHeapUsed && trace)
|
|
79
|
-
log.debug(`****${RED}${BRIGHT}HeapUsed peak detected.${db} Peak heapUsed from ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapUsed)}${db}`);
|
|
80
|
-
peakHeapUsed = memoryUsageRaw.heapUsed;
|
|
81
|
-
}
|
|
82
|
-
if (memoryUsageRaw.heapTotal > peakHeapTotal) {
|
|
83
|
-
if (peakHeapTotal && trace)
|
|
84
|
-
log.debug(`****${RED}${BRIGHT}HeapTotal peak detected.${db} Peak heapTotal from ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapTotal)}${db}`);
|
|
85
|
-
peakHeapTotal = memoryUsageRaw.heapTotal;
|
|
86
|
-
}
|
|
87
|
-
if (memoryUsageRaw.external > peakExternal) {
|
|
88
|
-
if (peakExternal && trace)
|
|
89
|
-
log.debug(`****${RED}${BRIGHT}External peak detected.${db} Peak external from ${CYAN}${formatMemoryUsage(peakExternal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.external)}${db}`);
|
|
90
|
-
peakExternal = memoryUsageRaw.external;
|
|
91
|
-
}
|
|
92
|
-
if (memoryUsageRaw.arrayBuffers > peakArrayBuffers) {
|
|
93
|
-
if (peakArrayBuffers && trace)
|
|
94
|
-
log.debug(`****${RED}${BRIGHT}ArrayBuffers peak detected.${db} Peak arrayBuffers from ${CYAN}${formatMemoryUsage(peakArrayBuffers)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.arrayBuffers)}${db}`);
|
|
95
|
-
peakArrayBuffers = memoryUsageRaw.arrayBuffers;
|
|
96
|
-
}
|
|
97
|
-
cliEmitter.emit('memory', totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers);
|
|
98
|
-
const currCpus = os.cpus();
|
|
99
|
-
if (currCpus.length !== prevCpus.length) {
|
|
100
|
-
prevCpus = currCpus;
|
|
101
|
-
log.debug(`Cpu check length failed, resetting previous cpus`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
let totalIdle = 0, totalTick = 0;
|
|
105
|
-
prevCpus.forEach((prevCpu, i) => {
|
|
106
|
-
const currCpu = currCpus[i];
|
|
107
|
-
const idleDiff = currCpu.times.idle - prevCpu.times.idle;
|
|
108
|
-
const totalDiff = Object.keys(currCpu.times).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);
|
|
109
|
-
totalIdle += idleDiff;
|
|
110
|
-
totalTick += totalDiff;
|
|
111
|
-
});
|
|
112
|
-
const osCpuUsage = 100 - (totalIdle / totalTick) * 100;
|
|
113
|
-
if (totalTick === 0 || isNaN(osCpuUsage) || !isFinite(osCpuUsage) || osCpuUsage <= 0) {
|
|
114
|
-
log.debug(`Cpu check failed, using previous cpus`);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
setLastOsCpuUsage(osCpuUsage);
|
|
118
|
-
if (osCpuUsage > peakCpu) {
|
|
119
|
-
peakCpu = osCpuUsage;
|
|
120
|
-
if (peakCpu && trace)
|
|
121
|
-
log.debug(`****${RED}${BRIGHT}Cpu peak detected.${db} Peak cpu from ${CYAN}${formatCpuUsage(peakCpu)}${db} to ${CYAN}${formatCpuUsage(osCpuUsage)}${db}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
prevCpus = currCpus;
|
|
125
|
-
const diff = process.cpuUsage(prevProcessCpu);
|
|
126
|
-
const userMs = diff.user / 1000;
|
|
127
|
-
const systemMs = diff.system / 1000;
|
|
128
|
-
const totalMs = userMs + systemMs;
|
|
129
|
-
const processCpuUsage = Number((((totalMs / memoryCheckIntervalMs) * 100) / currCpus.length).toFixed(2));
|
|
130
|
-
if (processCpuUsage > peakProcessCpu) {
|
|
131
|
-
peakProcessCpu = processCpuUsage;
|
|
132
|
-
if (peakProcessCpu && trace)
|
|
133
|
-
log.debug(`****${RED}${BRIGHT}Process cpu peak detected.${db} Peak process cpu from ${CYAN}${formatCpuUsage(peakProcessCpu)}${db} to ${CYAN}${formatCpuUsage(processCpuUsage)}${db}`);
|
|
134
|
-
}
|
|
135
|
-
prevProcessCpu = process.cpuUsage();
|
|
136
|
-
setLastProcessCpuUsage(processCpuUsage);
|
|
137
|
-
cliEmitter.emit('cpu', osCpuUsage, processCpuUsage);
|
|
138
|
-
const entry = history[historyIndex];
|
|
139
|
-
entry.timestamp = Date.now();
|
|
140
|
-
entry.cpu = osCpuUsage;
|
|
141
|
-
entry.peakCpu = peakCpu;
|
|
142
|
-
entry.processCpu = processCpuUsage;
|
|
143
|
-
entry.peakProcessCpu = peakProcessCpu;
|
|
144
|
-
entry.rss = memoryUsageRaw.rss;
|
|
145
|
-
entry.peakRss = peakRss;
|
|
146
|
-
entry.heapUsed = memoryUsageRaw.heapUsed;
|
|
147
|
-
entry.peakHeapUsed = peakHeapUsed;
|
|
148
|
-
entry.heapTotal = memoryUsageRaw.heapTotal;
|
|
149
|
-
entry.peakHeapTotal = peakHeapTotal;
|
|
150
|
-
entry.external = memoryUsageRaw.external;
|
|
151
|
-
entry.peakExternal = peakExternal;
|
|
152
|
-
entry.arrayBuffers = memoryUsageRaw.arrayBuffers;
|
|
153
|
-
entry.peakArrayBuffers = peakArrayBuffers;
|
|
154
|
-
setHistoryIndex((historyIndex + 1) % historySize);
|
|
155
|
-
if (trace)
|
|
156
|
-
log.debug(`***${YELLOW}${BRIGHT}Host cpu:${db} ` +
|
|
157
|
-
`${CYAN}${formatCpuUsage(osCpuUsage)}${db} (peak ${formatCpuUsage(peakCpu)}) ` +
|
|
158
|
-
`${YELLOW}${BRIGHT}Process cpu:${db} ` +
|
|
159
|
-
`${CYAN}${formatCpuUsage(processCpuUsage)}${db} (peak ${formatCpuUsage(peakProcessCpu)}) ` +
|
|
160
|
-
`${YELLOW}${BRIGHT}Process memory:${db} ` +
|
|
161
|
-
`rss ${CYAN}${rss}${db} (peak ${formatMemoryUsage(peakRss)}) ` +
|
|
162
|
-
`heapUsed ${CYAN}${heapUsed}${db} (peak ${formatMemoryUsage(peakHeapUsed)}) ` +
|
|
163
|
-
`heapTotal ${CYAN}${heapTotal}${db} (peak ${formatMemoryUsage(peakHeapTotal)}) ` +
|
|
164
|
-
`external ${CYAN}${external}${db} (peak ${formatMemoryUsage(peakExternal)}) ` +
|
|
165
|
-
`arrayBuffers ${CYAN}${arrayBuffers}${db} (peak ${formatMemoryUsage(peakArrayBuffers)})`);
|
|
166
|
-
};
|
|
167
|
-
clearInterval(memoryCheckInterval);
|
|
168
|
-
memoryCheckInterval = setInterval(interval, memoryCheckIntervalMs).unref();
|
|
169
|
-
clearTimeout(memoryPeakResetTimeout);
|
|
170
|
-
memoryPeakResetTimeout = setTimeout(() => {
|
|
171
|
-
if (trace)
|
|
172
|
-
log.debug(`****${RED}${BRIGHT}Cpu and memory peaks reset after first 5 minutes.${db}`);
|
|
173
|
-
peakCpu = 0;
|
|
174
|
-
peakProcessCpu = 0;
|
|
175
|
-
peakRss = 0;
|
|
176
|
-
peakHeapUsed = 0;
|
|
177
|
-
peakHeapTotal = 0;
|
|
178
|
-
}, 5 * 60 * 1000).unref();
|
|
179
25
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
`Peak heapUsed: ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db}. ` +
|
|
186
|
-
`Peak heapTotal: ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db}. ` +
|
|
187
|
-
`Peak external: ${CYAN}${formatMemoryUsage(peakExternal)}${db}. ` +
|
|
188
|
-
`Peak arrayBuffers: ${CYAN}${formatMemoryUsage(peakArrayBuffers)}${db}.`);
|
|
189
|
-
}
|
|
190
|
-
clearInterval(memoryCheckInterval);
|
|
191
|
-
clearTimeout(memoryPeakResetTimeout);
|
|
26
|
+
function stopCpuMemoryCheck() {
|
|
27
|
+
log.debug(`Cpu memory check stopping...`);
|
|
28
|
+
tracker.stop();
|
|
29
|
+
tracker.removeAllListeners();
|
|
30
|
+
log.debug(`Cpu memory check stopped`);
|
|
192
31
|
}
|
|
193
32
|
async function startInspector() {
|
|
194
|
-
|
|
195
|
-
const { mkdirSync } = await import('node:fs');
|
|
196
|
-
log.debug(`***Starting heap sampling...`);
|
|
197
|
-
mkdirSync('heap_profile', { recursive: true });
|
|
198
|
-
try {
|
|
199
|
-
session = new Session();
|
|
200
|
-
session.connect();
|
|
201
|
-
await new Promise((resolve, reject) => {
|
|
202
|
-
session?.post('HeapProfiler.startSampling', (err) => (err ? reject(err) : resolve()));
|
|
203
|
-
});
|
|
204
|
-
log.debug(`***Started heap sampling`);
|
|
205
|
-
const interval = getIntParameter('snapshotinterval');
|
|
206
|
-
if (interval && interval >= 30000) {
|
|
207
|
-
log.debug(`***Started heap snapshot interval of ${CYAN}${interval}${db} ms`);
|
|
208
|
-
clearInterval(snapshotInterval);
|
|
209
|
-
snapshotInterval = setInterval(async () => {
|
|
210
|
-
log.debug(`Run heap snapshot interval`);
|
|
211
|
-
await takeHeapSnapshot();
|
|
212
|
-
}, interval).unref();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
log.error(`***Failed to start heap sampling: ${err instanceof Error ? err.message : err}`);
|
|
217
|
-
session?.disconnect();
|
|
218
|
-
session = undefined;
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
33
|
+
await inspector.start();
|
|
221
34
|
}
|
|
222
35
|
async function stopInspector() {
|
|
223
|
-
|
|
224
|
-
const path = await import('node:path');
|
|
225
|
-
log.debug(`***Stopping heap sampling...`);
|
|
226
|
-
if (snapshotInterval) {
|
|
227
|
-
log.debug(`***Clearing heap snapshot interval...`);
|
|
228
|
-
clearInterval(snapshotInterval);
|
|
229
|
-
await takeHeapSnapshot();
|
|
230
|
-
}
|
|
231
|
-
if (!session) {
|
|
232
|
-
log.error('***No active inspector session.');
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
try {
|
|
236
|
-
const result = await new Promise((resolve, reject) => {
|
|
237
|
-
session?.post('HeapProfiler.stopSampling', (err, result) => (err ? reject(err) : resolve(result)));
|
|
238
|
-
});
|
|
239
|
-
const profile = JSON.stringify(result.profile);
|
|
240
|
-
const filename = path.join('heap_profile', `Heap-profile-${new Date().toISOString().replace(/[:]/g, '-')}.heapprofile`);
|
|
241
|
-
writeFileSync(filename, profile);
|
|
242
|
-
log.debug(`***Heap sampling profile saved to ${CYAN}${filename}${db}`);
|
|
243
|
-
}
|
|
244
|
-
catch (err) {
|
|
245
|
-
log.error(`***Failed to stop heap sampling: ${err instanceof Error ? err.message : err}`);
|
|
246
|
-
}
|
|
247
|
-
finally {
|
|
248
|
-
session.disconnect();
|
|
249
|
-
session = undefined;
|
|
250
|
-
log.debug(`***Stopped heap sampling`);
|
|
251
|
-
}
|
|
36
|
+
await inspector.stop();
|
|
252
37
|
}
|
|
253
38
|
async function takeHeapSnapshot() {
|
|
254
|
-
|
|
255
|
-
const path = await import('node:path');
|
|
256
|
-
const filename = path.join('heap_profile', `Heap-snapshot-${new Date().toISOString().replace(/[:]/g, '-')}.heapsnapshot`);
|
|
257
|
-
if (!session) {
|
|
258
|
-
log.error('No active inspector session.');
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
triggerGarbageCollection();
|
|
262
|
-
log.debug(`Taking heap snapshot...`);
|
|
263
|
-
const chunks = [];
|
|
264
|
-
const chunksListener = (notification) => {
|
|
265
|
-
chunks.push(Buffer.from(notification.params.chunk));
|
|
266
|
-
};
|
|
267
|
-
session.on('HeapProfiler.addHeapSnapshotChunk', chunksListener);
|
|
268
|
-
await new Promise((resolve) => {
|
|
269
|
-
session?.post('HeapProfiler.takeHeapSnapshot', (err) => {
|
|
270
|
-
if (!err) {
|
|
271
|
-
session?.off('HeapProfiler.addHeapSnapshotChunk', chunksListener);
|
|
272
|
-
writeFileSync(filename, Buffer.concat(chunks));
|
|
273
|
-
chunks.length = 0;
|
|
274
|
-
log.debug(`***Heap sampling snapshot saved to ${CYAN}${filename}${db}`);
|
|
275
|
-
triggerGarbageCollection();
|
|
276
|
-
resolve();
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
session?.off('HeapProfiler.addHeapSnapshotChunk', chunksListener);
|
|
280
|
-
chunks.length = 0;
|
|
281
|
-
log.error(`***Failed to take heap snapshot: ${err instanceof Error ? err.message : err}`);
|
|
282
|
-
triggerGarbageCollection();
|
|
283
|
-
resolve();
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
});
|
|
39
|
+
await inspector.takeHeapSnapshot();
|
|
287
40
|
}
|
|
288
41
|
function triggerGarbageCollection() {
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
global.gc({ type: 'major', execution: 'sync' });
|
|
292
|
-
}
|
|
293
|
-
catch {
|
|
294
|
-
global.gc();
|
|
295
|
-
}
|
|
296
|
-
log.debug('Manual garbage collection triggered via global.gc().');
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
log.debug('Garbage collection is not exposed. Start Node.js with --expose-gc to enable manual GC.');
|
|
300
|
-
}
|
|
42
|
+
inspector.runGarbageCollector();
|
|
301
43
|
}
|
|
302
44
|
function registerHandlers() {
|
|
303
45
|
log.debug('Registering event handlers...');
|
|
304
|
-
if (instance)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
instance.on('startinspector', async () => startInspector());
|
|
316
|
-
if (instance)
|
|
317
|
-
instance.on('stopinspector', async () => stopInspector());
|
|
318
|
-
if (instance)
|
|
319
|
-
instance.on('takeheapsnapshot', async () => takeHeapSnapshot());
|
|
320
|
-
if (instance)
|
|
321
|
-
instance.on('triggergarbagecollection', async () => triggerGarbageCollection());
|
|
46
|
+
if (!instance)
|
|
47
|
+
return;
|
|
48
|
+
instance.on('shutdown', () => shutdown());
|
|
49
|
+
instance.on('restart', () => restart());
|
|
50
|
+
instance.on('update', () => update());
|
|
51
|
+
instance.on('startmemorycheck', () => start());
|
|
52
|
+
instance.on('stopmemorycheck', () => stop());
|
|
53
|
+
instance.on('startinspector', () => startInspector());
|
|
54
|
+
instance.on('stopinspector', () => stopInspector());
|
|
55
|
+
instance.on('takeheapsnapshot', () => takeHeapSnapshot());
|
|
56
|
+
instance.on('triggergarbagecollection', () => triggerGarbageCollection());
|
|
322
57
|
log.debug('Registered event handlers');
|
|
323
58
|
}
|
|
324
59
|
async function shutdown() {
|
|
325
60
|
log.debug('Received shutdown event, exiting...');
|
|
326
61
|
if (hasParameter('inspect'))
|
|
327
62
|
await stopInspector();
|
|
328
|
-
|
|
63
|
+
stopCpuMemoryCheck();
|
|
329
64
|
cliEmitter.emit('shutdown');
|
|
330
65
|
process.exit(0);
|
|
331
66
|
}
|
|
@@ -339,17 +74,17 @@ async function update() {
|
|
|
339
74
|
instance = await Matterbridge.loadInstance(true);
|
|
340
75
|
registerHandlers();
|
|
341
76
|
}
|
|
342
|
-
|
|
77
|
+
function start() {
|
|
343
78
|
log.debug('Received start memory check event');
|
|
344
|
-
|
|
79
|
+
startCpuMemoryCheck();
|
|
345
80
|
}
|
|
346
|
-
|
|
81
|
+
function stop() {
|
|
347
82
|
log.debug('Received stop memory check event');
|
|
348
|
-
|
|
83
|
+
stopCpuMemoryCheck();
|
|
349
84
|
}
|
|
350
85
|
async function main() {
|
|
351
86
|
log.debug(`Cli main() started`);
|
|
352
|
-
|
|
87
|
+
startCpuMemoryCheck();
|
|
353
88
|
if (hasParameter('inspect'))
|
|
354
89
|
await startInspector();
|
|
355
90
|
log.debug(`***Matterbridge.loadInstance(true) called`);
|
|
@@ -361,11 +96,72 @@ async function main() {
|
|
|
361
96
|
else {
|
|
362
97
|
registerHandlers();
|
|
363
98
|
cliEmitter.emit('ready');
|
|
99
|
+
log.debug(`Cli main() ready`);
|
|
364
100
|
}
|
|
365
101
|
}
|
|
366
|
-
|
|
102
|
+
if (hasAnyParameter('help', 'h'))
|
|
103
|
+
help();
|
|
104
|
+
if (hasAnyParameter('version', 'v'))
|
|
105
|
+
await version();
|
|
367
106
|
main().catch((error) => {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
log.error(`Matterbridge.loadInstance() failed with error: ${errorMessage}\nstack: ${errorInspect}`);
|
|
107
|
+
inspectError(log, 'Matterbridge.loadInstance() failed with error', error);
|
|
108
|
+
shutdown();
|
|
371
109
|
});
|
|
110
|
+
async function version() {
|
|
111
|
+
const { default: pkg } = await import('../package.json', { with: { type: 'json' } });
|
|
112
|
+
console.log(`Matterbridge version ${pkg.version}`);
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
function help() {
|
|
116
|
+
console.log(`
|
|
117
|
+
Usage: matterbridge [options] [command]
|
|
118
|
+
|
|
119
|
+
Commands:
|
|
120
|
+
--help: show the help
|
|
121
|
+
--version: show the version
|
|
122
|
+
--add [plugin path]: register the plugin from the given absolute or relative path
|
|
123
|
+
--add [plugin name]: register the globally installed plugin with the given name
|
|
124
|
+
--remove [plugin path]: remove the plugin from the given absolute or relative path
|
|
125
|
+
--remove [plugin name]: remove the globally installed plugin with the given name
|
|
126
|
+
--enable [plugin path]: enable the plugin from the given absolute or relative path
|
|
127
|
+
--enable [plugin name]: enable the globally installed plugin with the given name
|
|
128
|
+
--disable [plugin path]: disable the plugin from the given absolute or relative path
|
|
129
|
+
--disable [plugin name]: disable the globally installed plugin with the given name
|
|
130
|
+
|
|
131
|
+
Reset Commands:
|
|
132
|
+
--reset: remove the commissioning for Matterbridge (bridge mode and childbridge mode). Shutdown Matterbridge before using it!
|
|
133
|
+
--reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
|
|
134
|
+
--reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!
|
|
135
|
+
--factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
136
|
+
|
|
137
|
+
Options:
|
|
138
|
+
--bridge: start Matterbridge in bridge mode
|
|
139
|
+
--childbridge: start Matterbridge in childbridge mode
|
|
140
|
+
--port [port]: start the matter commissioning server on the given port (default 5540)
|
|
141
|
+
--mdnsinterface [name]: set the interface to use for the matter server mdnsInterface (default all interfaces)
|
|
142
|
+
--ipv4address [address]: set the ipv4 interface address to use for the matter server listener (default all addresses)
|
|
143
|
+
--ipv6address [address]: set the ipv6 interface address to use for the matter server listener (default all addresses)
|
|
144
|
+
--frontend [port]: start the frontend on the given port (default 8283)
|
|
145
|
+
--logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
|
|
146
|
+
--filelogger: enable the matterbridge file logger (matterbridge.log)
|
|
147
|
+
--matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
|
|
148
|
+
--matterfilelogger: enable the matter.js file logger (matter.log)
|
|
149
|
+
--list: list the registered plugins
|
|
150
|
+
--loginterfaces: log the network interfaces (usefull for finding the name of the interface to use with -mdnsinterface option)
|
|
151
|
+
--logstorage: log the node storage
|
|
152
|
+
--sudo: force the use of sudo to install or update packages if the internal logic fails
|
|
153
|
+
--nosudo: force not to use sudo to install or update packages if the internal logic fails
|
|
154
|
+
--norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
|
|
155
|
+
--novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
|
|
156
|
+
--ssl: enable SSL for the frontend and the WebSocketServer (the server will use the certificates and switch to https)
|
|
157
|
+
--mtls: enable mTLS for the frontend and the WebSocketServer (both server and client will use and require the certificates and switch to https)
|
|
158
|
+
--vendorId: override the default vendorId 0xfff1
|
|
159
|
+
--vendorName: override the default vendorName "Matterbridge"
|
|
160
|
+
--productId: override the default productId 0x8000
|
|
161
|
+
--productName: override the default productName "Matterbridge aggregator"
|
|
162
|
+
--service: enable the service mode (used in the linux systemctl configuration file and macOS launchctl plist)
|
|
163
|
+
--docker: enable the docker mode (used in the docker image)
|
|
164
|
+
--homedir: override the home directory (default the os homedir)
|
|
165
|
+
`);
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
package/dist/cliHistory.js
CHANGED
|
@@ -3,47 +3,15 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
3
3
|
import { writeFileSync } from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import os from 'node:os';
|
|
6
|
-
|
|
7
|
-
export let historyIndex = 0;
|
|
8
|
-
export function setHistoryIndex(index) {
|
|
9
|
-
if (!Number.isFinite(index) || !Number.isSafeInteger(index)) {
|
|
10
|
-
throw new TypeError('historyIndex must be a finite, safe integer.');
|
|
11
|
-
}
|
|
12
|
-
if (index < 0 || index >= historySize) {
|
|
13
|
-
throw new RangeError(`historyIndex must be between 0 and ${historySize - 1}.`);
|
|
14
|
-
}
|
|
15
|
-
historyIndex = index;
|
|
16
|
-
}
|
|
17
|
-
export const history = Array.from({ length: historySize }, () => ({
|
|
18
|
-
timestamp: 0,
|
|
19
|
-
cpu: 0,
|
|
20
|
-
peakCpu: 0,
|
|
21
|
-
processCpu: 0,
|
|
22
|
-
peakProcessCpu: 0,
|
|
23
|
-
rss: 0,
|
|
24
|
-
peakRss: 0,
|
|
25
|
-
heapUsed: 0,
|
|
26
|
-
peakHeapUsed: 0,
|
|
27
|
-
heapTotal: 0,
|
|
28
|
-
peakHeapTotal: 0,
|
|
29
|
-
external: 0,
|
|
30
|
-
peakExternal: 0,
|
|
31
|
-
arrayBuffers: 0,
|
|
32
|
-
peakArrayBuffers: 0,
|
|
33
|
-
}));
|
|
6
|
+
import { Tracker } from './utils/tracker.js';
|
|
34
7
|
export function generateHistoryPage(options = {}) {
|
|
35
8
|
const pageTitle = options.pageTitle ?? 'Matterbridge CPU & Memory History';
|
|
36
9
|
const hostname = options.hostname ?? os.hostname();
|
|
37
10
|
const outputPath = path.resolve(options.outputPath ?? path.join(process.cwd(), 'history.html'));
|
|
38
|
-
const bufferLength = history.length;
|
|
39
|
-
if (bufferLength === 0) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const startIndex = ((Math.trunc(historyIndex) % bufferLength) + bufferLength) % bufferLength;
|
|
43
11
|
const normalizedHistory = [];
|
|
44
|
-
for (let offset = 0; offset <
|
|
45
|
-
const index = (
|
|
46
|
-
const entry = history[index];
|
|
12
|
+
for (let offset = 0; offset < Tracker.historySize; offset += 1) {
|
|
13
|
+
const index = (Tracker.historyIndex + offset) % Tracker.historySize;
|
|
14
|
+
const entry = Tracker.history[index];
|
|
47
15
|
if (!entry || entry.timestamp === 0)
|
|
48
16
|
continue;
|
|
49
17
|
normalizedHistory.push(entry);
|
|
@@ -51,7 +19,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
51
19
|
if (normalizedHistory.length === 0) {
|
|
52
20
|
return undefined;
|
|
53
21
|
}
|
|
54
|
-
const
|
|
22
|
+
const peakOsCpu = Math.max(...normalizedHistory.map((entry) => entry.peakOsCpu ?? entry.osCpu));
|
|
55
23
|
const peakProcessCpu = Math.max(...normalizedHistory.map((entry) => entry.peakProcessCpu ?? entry.processCpu));
|
|
56
24
|
const peakRss = Math.max(...normalizedHistory.map((entry) => entry.peakRss ?? entry.rss));
|
|
57
25
|
const peakHeapUsed = Math.max(...normalizedHistory.map((entry) => entry.peakHeapUsed ?? entry.heapUsed));
|
|
@@ -63,7 +31,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
63
31
|
const summary = {
|
|
64
32
|
entries: normalizedHistory.length,
|
|
65
33
|
timeRange: `${new Date(firstTimestamp).toLocaleString()} → ${new Date(lastTimestamp).toLocaleString()}`,
|
|
66
|
-
|
|
34
|
+
peakOsCpu,
|
|
67
35
|
peakProcessCpu,
|
|
68
36
|
peakRss,
|
|
69
37
|
peakHeapUsed,
|
|
@@ -295,7 +263,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
295
263
|
const summaryEntries = [
|
|
296
264
|
{ label: 'Samples', value: SUMMARY_DATA.entries.toLocaleString() },
|
|
297
265
|
{ label: 'Time Range', value: SUMMARY_DATA.timeRange },
|
|
298
|
-
{ label: 'Host CPU Peak', value: SUMMARY_DATA.
|
|
266
|
+
{ label: 'Host CPU Peak', value: SUMMARY_DATA.peakOsCpu.toFixed(2) + ' %' },
|
|
299
267
|
{ label: 'Process CPU Peak', value: SUMMARY_DATA.peakProcessCpu.toFixed(2) + ' %' },
|
|
300
268
|
{ label: 'RSS Peak', value: formatBytes(SUMMARY_DATA.peakRss) },
|
|
301
269
|
{ label: 'Heap Used Peak', value: formatBytes(SUMMARY_DATA.peakHeapUsed) },
|
|
@@ -316,8 +284,8 @@ export function generateHistoryPage(options = {}) {
|
|
|
316
284
|
const row = document.createElement('tr');
|
|
317
285
|
const cells = [
|
|
318
286
|
new Date(entry.timestamp).toLocaleString(),
|
|
319
|
-
entry.
|
|
320
|
-
entry.
|
|
287
|
+
entry.osCpu.toFixed(2),
|
|
288
|
+
entry.peakOsCpu.toFixed(2),
|
|
321
289
|
(Number.isFinite(entry.processCpu) ? entry.processCpu : 0).toFixed(2),
|
|
322
290
|
(
|
|
323
291
|
Number.isFinite(entry.peakProcessCpu)
|
|
@@ -342,16 +310,12 @@ export function generateHistoryPage(options = {}) {
|
|
|
342
310
|
});
|
|
343
311
|
|
|
344
312
|
const cpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
|
|
345
|
-
return Math.max(acc, Number.isFinite(entry.
|
|
313
|
+
return Math.max(acc, Number.isFinite(entry.peakOsCpu) ? entry.peakOsCpu : 0, Number.isFinite(entry.osCpu) ? entry.osCpu : 0);
|
|
346
314
|
}, 0);
|
|
347
315
|
const cpuMaxYAxis = cpuPeakValue > 0 ? cpuPeakValue * 1.05 : undefined;
|
|
348
316
|
|
|
349
317
|
const processCpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
|
|
350
|
-
return Math.max(
|
|
351
|
-
acc,
|
|
352
|
-
Number.isFinite(entry.peakProcessCpu) ? entry.peakProcessCpu : 0,
|
|
353
|
-
Number.isFinite(entry.processCpu) ? entry.processCpu : 0
|
|
354
|
-
);
|
|
318
|
+
return Math.max(acc, Number.isFinite(entry.peakProcessCpu) ? entry.peakProcessCpu : 0, Number.isFinite(entry.processCpu) ? entry.processCpu : 0);
|
|
355
319
|
}, 0);
|
|
356
320
|
const processCpuMaxYAxis = processCpuPeakValue > 0 ? processCpuPeakValue * 1.05 : undefined;
|
|
357
321
|
const useProcessCpuDecimals = (processCpuMaxYAxis ?? 0) <= 3;
|
|
@@ -418,14 +382,14 @@ export function generateHistoryPage(options = {}) {
|
|
|
418
382
|
{
|
|
419
383
|
label: 'Host CPU %',
|
|
420
384
|
values: HISTORY_DATA.map(function (entry) {
|
|
421
|
-
return Number.isFinite(entry.
|
|
385
|
+
return Number.isFinite(entry.osCpu) ? Number(entry.osCpu.toFixed(2)) : 0;
|
|
422
386
|
}),
|
|
423
387
|
color: '#38bdf8',
|
|
424
388
|
fill: 'rgba(56, 189, 248, 0.18)',
|
|
425
389
|
markPeaks: true,
|
|
426
390
|
markerPeakValues: HISTORY_DATA.map(function (entry) {
|
|
427
|
-
if (Number.isFinite(entry.
|
|
428
|
-
if (Number.isFinite(entry.
|
|
391
|
+
if (Number.isFinite(entry.peakOsCpu)) return entry.peakOsCpu;
|
|
392
|
+
if (Number.isFinite(entry.osCpu)) return entry.osCpu;
|
|
429
393
|
return Number.NEGATIVE_INFINITY;
|
|
430
394
|
}),
|
|
431
395
|
markerRadius: 2.5
|
|
@@ -433,7 +397,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
433
397
|
{
|
|
434
398
|
label: 'Host Peak CPU %',
|
|
435
399
|
values: HISTORY_DATA.map(function (entry) {
|
|
436
|
-
return Number.isFinite(entry.
|
|
400
|
+
return Number.isFinite(entry.peakOsCpu) ? Number(entry.peakOsCpu.toFixed(2)) : 0;
|
|
437
401
|
}),
|
|
438
402
|
color: '#facc15',
|
|
439
403
|
dashed: [6, 4]
|