matterbridge 3.3.1-dev-20251009-008da25 → 3.3.1-dev-20251012-b3546f8
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/dist/cli.js +48 -17
- package/dist/cliEmitter.js +3 -3
- package/dist/cliHistory.js +48 -13
- package/dist/frontend.js +41 -12
- package/dist/matterbridge.js +8 -11
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import { inspect } from 'node:util';
|
|
5
5
|
import { AnsiLogger, BRIGHT, CYAN, db, RED, YELLOW } from 'node-ansi-logger';
|
|
6
|
-
import { cliEmitter,
|
|
6
|
+
import { cliEmitter, setLastOsCpuUsage, setLastProcessCpuUsage } from './cliEmitter.js';
|
|
7
7
|
import { history, historyIndex, historySize, setHistoryIndex } from './cliHistory.js';
|
|
8
8
|
import { getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
9
9
|
import { Matterbridge } from './matterbridge.js';
|
|
@@ -21,6 +21,8 @@ let peakProcessCpu = 0;
|
|
|
21
21
|
let peakRss = 0;
|
|
22
22
|
let peakHeapUsed = 0;
|
|
23
23
|
let peakHeapTotal = 0;
|
|
24
|
+
let peakExternal = 0;
|
|
25
|
+
let peakArrayBuffers = 0;
|
|
24
26
|
const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
25
27
|
const formatCpuUsage = (percent) => {
|
|
26
28
|
return `${percent.toFixed(2).padStart(5, ' ')} %`;
|
|
@@ -55,12 +57,11 @@ async function startCpuMemoryCheck() {
|
|
|
55
57
|
log.debug(`Cpu memory check started`);
|
|
56
58
|
prevCpus = os.cpus();
|
|
57
59
|
prevProcessCpu = process.cpuUsage();
|
|
58
|
-
clearInterval(memoryCheckInterval);
|
|
59
60
|
const interval = () => {
|
|
60
61
|
const systemUptime = formatOsUpTime(Math.floor(os.uptime()));
|
|
61
62
|
const processUptime = formatOsUpTime(Math.floor(process.uptime()));
|
|
62
63
|
cliEmitter.emit('uptime', systemUptime, processUptime);
|
|
63
|
-
const
|
|
64
|
+
const totalMemory = formatMemoryUsage(os.totalmem());
|
|
64
65
|
const freeMemory = formatMemoryUsage(os.freemem());
|
|
65
66
|
const memoryUsageRaw = process.memoryUsage();
|
|
66
67
|
const rss = formatMemoryUsage(memoryUsageRaw.rss);
|
|
@@ -83,7 +84,17 @@ async function startCpuMemoryCheck() {
|
|
|
83
84
|
log.debug(`****${RED}${BRIGHT}HeapTotal peak detected.${db} Peak heapTotal from ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapTotal)}${db}`);
|
|
84
85
|
peakHeapTotal = memoryUsageRaw.heapTotal;
|
|
85
86
|
}
|
|
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);
|
|
87
98
|
const currCpus = os.cpus();
|
|
88
99
|
if (currCpus.length !== prevCpus.length) {
|
|
89
100
|
prevCpus = currCpus;
|
|
@@ -98,16 +109,16 @@ async function startCpuMemoryCheck() {
|
|
|
98
109
|
totalIdle += idleDiff;
|
|
99
110
|
totalTick += totalDiff;
|
|
100
111
|
});
|
|
101
|
-
const
|
|
102
|
-
if (totalTick === 0 || isNaN(
|
|
112
|
+
const osCpuUsage = 100 - (totalIdle / totalTick) * 100;
|
|
113
|
+
if (totalTick === 0 || isNaN(osCpuUsage) || !isFinite(osCpuUsage) || osCpuUsage <= 0) {
|
|
103
114
|
log.debug(`Cpu check failed, using previous cpus`);
|
|
104
115
|
}
|
|
105
116
|
else {
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
peakCpu =
|
|
117
|
+
setLastOsCpuUsage(osCpuUsage);
|
|
118
|
+
if (osCpuUsage > peakCpu) {
|
|
119
|
+
peakCpu = osCpuUsage;
|
|
109
120
|
if (peakCpu && trace)
|
|
110
|
-
log.debug(`****${RED}${BRIGHT}Cpu peak detected.${db} Peak cpu from ${CYAN}${formatCpuUsage(peakCpu)}${db} to ${CYAN}${formatCpuUsage(
|
|
121
|
+
log.debug(`****${RED}${BRIGHT}Cpu peak detected.${db} Peak cpu from ${CYAN}${formatCpuUsage(peakCpu)}${db} to ${CYAN}${formatCpuUsage(osCpuUsage)}${db}`);
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
prevCpus = currCpus;
|
|
@@ -115,7 +126,7 @@ async function startCpuMemoryCheck() {
|
|
|
115
126
|
const userMs = diff.user / 1000;
|
|
116
127
|
const systemMs = diff.system / 1000;
|
|
117
128
|
const totalMs = userMs + systemMs;
|
|
118
|
-
const processCpuUsage = Number(((totalMs / memoryCheckIntervalMs) * 100).toFixed(2));
|
|
129
|
+
const processCpuUsage = Number((((totalMs / memoryCheckIntervalMs) * 100) / currCpus.length).toFixed(2));
|
|
119
130
|
if (processCpuUsage > peakProcessCpu) {
|
|
120
131
|
peakProcessCpu = processCpuUsage;
|
|
121
132
|
if (peakProcessCpu && trace)
|
|
@@ -123,9 +134,10 @@ async function startCpuMemoryCheck() {
|
|
|
123
134
|
}
|
|
124
135
|
prevProcessCpu = process.cpuUsage();
|
|
125
136
|
setLastProcessCpuUsage(processCpuUsage);
|
|
126
|
-
cliEmitter.emit('cpu',
|
|
137
|
+
cliEmitter.emit('cpu', osCpuUsage, processCpuUsage);
|
|
127
138
|
const entry = history[historyIndex];
|
|
128
|
-
entry.
|
|
139
|
+
entry.timestamp = Date.now();
|
|
140
|
+
entry.cpu = osCpuUsage;
|
|
129
141
|
entry.peakCpu = peakCpu;
|
|
130
142
|
entry.processCpu = processCpuUsage;
|
|
131
143
|
entry.peakProcessCpu = peakProcessCpu;
|
|
@@ -135,16 +147,29 @@ async function startCpuMemoryCheck() {
|
|
|
135
147
|
entry.peakHeapUsed = peakHeapUsed;
|
|
136
148
|
entry.heapTotal = memoryUsageRaw.heapTotal;
|
|
137
149
|
entry.peakHeapTotal = peakHeapTotal;
|
|
138
|
-
entry.
|
|
150
|
+
entry.external = memoryUsageRaw.external;
|
|
151
|
+
entry.peakExternal = peakExternal;
|
|
152
|
+
entry.arrayBuffers = memoryUsageRaw.arrayBuffers;
|
|
153
|
+
entry.peakArrayBuffers = peakArrayBuffers;
|
|
139
154
|
setHistoryIndex((historyIndex + 1) % historySize);
|
|
140
155
|
if (trace)
|
|
141
|
-
log.debug(`***${YELLOW}${BRIGHT}Host cpu:${db}
|
|
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)})`);
|
|
142
166
|
};
|
|
143
167
|
clearInterval(memoryCheckInterval);
|
|
144
168
|
memoryCheckInterval = setInterval(interval, memoryCheckIntervalMs).unref();
|
|
145
169
|
clearTimeout(memoryPeakResetTimeout);
|
|
146
170
|
memoryPeakResetTimeout = setTimeout(() => {
|
|
147
|
-
|
|
171
|
+
if (trace)
|
|
172
|
+
log.debug(`****${RED}${BRIGHT}Cpu and memory peaks reset after first 5 minutes.${db}`);
|
|
148
173
|
peakCpu = 0;
|
|
149
174
|
peakProcessCpu = 0;
|
|
150
175
|
peakRss = 0;
|
|
@@ -154,7 +179,13 @@ async function startCpuMemoryCheck() {
|
|
|
154
179
|
}
|
|
155
180
|
async function stopCpuMemoryCheck() {
|
|
156
181
|
if (trace) {
|
|
157
|
-
log.debug(`***Cpu memory check stopped.
|
|
182
|
+
log.debug(`***Cpu memory check stopped. ` +
|
|
183
|
+
`Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. ` +
|
|
184
|
+
`Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}. ` +
|
|
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}.`);
|
|
158
189
|
}
|
|
159
190
|
clearInterval(memoryCheckInterval);
|
|
160
191
|
clearTimeout(memoryPeakResetTimeout);
|
package/dist/cliEmitter.js
CHANGED
|
@@ -2,10 +2,10 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
2
2
|
console.log('\u001B[32mCli emitter loaded.\u001B[40;0m');
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
export const cliEmitter = new EventEmitter();
|
|
5
|
-
export let
|
|
5
|
+
export let lastOsCpuUsage = 0;
|
|
6
6
|
export let lastProcessCpuUsage = 0;
|
|
7
|
-
export function
|
|
8
|
-
|
|
7
|
+
export function setLastOsCpuUsage(val) {
|
|
8
|
+
lastOsCpuUsage = val;
|
|
9
9
|
}
|
|
10
10
|
export function setLastProcessCpuUsage(val) {
|
|
11
11
|
lastProcessCpuUsage = val;
|
package/dist/cliHistory.js
CHANGED
|
@@ -2,7 +2,7 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
2
2
|
console.log('\u001B[32mCli history loaded.\u001B[40;0m');
|
|
3
3
|
import { writeFileSync } from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
export const historySize =
|
|
5
|
+
export const historySize = 4320;
|
|
6
6
|
export let historyIndex = 0;
|
|
7
7
|
export function setHistoryIndex(index) {
|
|
8
8
|
if (!Number.isFinite(index) || !Number.isSafeInteger(index)) {
|
|
@@ -14,6 +14,7 @@ export function setHistoryIndex(index) {
|
|
|
14
14
|
historyIndex = index;
|
|
15
15
|
}
|
|
16
16
|
export const history = Array.from({ length: historySize }, () => ({
|
|
17
|
+
timestamp: 0,
|
|
17
18
|
cpu: 0,
|
|
18
19
|
peakCpu: 0,
|
|
19
20
|
processCpu: 0,
|
|
@@ -24,7 +25,10 @@ export const history = Array.from({ length: historySize }, () => ({
|
|
|
24
25
|
peakHeapUsed: 0,
|
|
25
26
|
heapTotal: 0,
|
|
26
27
|
peakHeapTotal: 0,
|
|
27
|
-
|
|
28
|
+
external: 0,
|
|
29
|
+
peakExternal: 0,
|
|
30
|
+
arrayBuffers: 0,
|
|
31
|
+
peakArrayBuffers: 0,
|
|
28
32
|
}));
|
|
29
33
|
export function generateHistoryPage(options = {}) {
|
|
30
34
|
const pageTitle = options.pageTitle ?? 'Matterbridge CPU & Memory History';
|
|
@@ -50,9 +54,10 @@ export function generateHistoryPage(options = {}) {
|
|
|
50
54
|
const peakRss = Math.max(...normalizedHistory.map((entry) => entry.peakRss ?? entry.rss));
|
|
51
55
|
const peakHeapUsed = Math.max(...normalizedHistory.map((entry) => entry.peakHeapUsed ?? entry.heapUsed));
|
|
52
56
|
const peakHeapTotal = Math.max(...normalizedHistory.map((entry) => entry.peakHeapTotal ?? entry.heapTotal));
|
|
57
|
+
const peakExternal = Math.max(...normalizedHistory.map((entry) => entry.peakExternal ?? entry.external));
|
|
58
|
+
const peakArrayBuffers = Math.max(...normalizedHistory.map((entry) => entry.peakArrayBuffers ?? entry.arrayBuffers));
|
|
53
59
|
const firstTimestamp = normalizedHistory[0]?.timestamp ?? Date.now();
|
|
54
60
|
const lastTimestamp = normalizedHistory[normalizedHistory.length - 1]?.timestamp ?? Date.now();
|
|
55
|
-
const historySanitised = JSON.stringify(normalizedHistory).replace(/</g, '\\u003c').replace(/>/g, '\\u003e');
|
|
56
61
|
const summary = {
|
|
57
62
|
entries: normalizedHistory.length,
|
|
58
63
|
timeRange: `${new Date(firstTimestamp).toLocaleString()} → ${new Date(lastTimestamp).toLocaleString()}`,
|
|
@@ -61,8 +66,9 @@ export function generateHistoryPage(options = {}) {
|
|
|
61
66
|
peakRss,
|
|
62
67
|
peakHeapUsed,
|
|
63
68
|
peakHeapTotal,
|
|
69
|
+
peakExternal,
|
|
70
|
+
peakArrayBuffers,
|
|
64
71
|
};
|
|
65
|
-
const summarySanitised = JSON.stringify(summary).replace(/</g, '\\u003c').replace(/>/g, '\\u003e');
|
|
66
72
|
const html = `<!DOCTYPE html>
|
|
67
73
|
<html lang="en">
|
|
68
74
|
<head>
|
|
@@ -271,8 +277,8 @@ export function generateHistoryPage(options = {}) {
|
|
|
271
277
|
</div>
|
|
272
278
|
|
|
273
279
|
<script type="module">
|
|
274
|
-
const HISTORY_DATA = ${
|
|
275
|
-
const SUMMARY_DATA = ${
|
|
280
|
+
const HISTORY_DATA = ${JSON.stringify(normalizedHistory)};
|
|
281
|
+
const SUMMARY_DATA = ${JSON.stringify(summary)};
|
|
276
282
|
let cleanup = () => {};
|
|
277
283
|
|
|
278
284
|
const summaryContainer = document.getElementById('summary');
|
|
@@ -283,7 +289,9 @@ export function generateHistoryPage(options = {}) {
|
|
|
283
289
|
{ label: 'Process CPU Peak', value: SUMMARY_DATA.peakProcessCpu.toFixed(2) + ' %' },
|
|
284
290
|
{ label: 'RSS Peak', value: formatBytes(SUMMARY_DATA.peakRss) },
|
|
285
291
|
{ label: 'Heap Used Peak', value: formatBytes(SUMMARY_DATA.peakHeapUsed) },
|
|
286
|
-
{ label: 'Heap Total Peak', value: formatBytes(SUMMARY_DATA.peakHeapTotal) }
|
|
292
|
+
{ label: 'Heap Total Peak', value: formatBytes(SUMMARY_DATA.peakHeapTotal) },
|
|
293
|
+
{ label: 'External Peak', value: formatBytes(SUMMARY_DATA.peakExternal) },
|
|
294
|
+
{ label: 'Array Buffers Peak', value: formatBytes(SUMMARY_DATA.peakArrayBuffers) }
|
|
287
295
|
];
|
|
288
296
|
|
|
289
297
|
summaryEntries.forEach(function (itemData) {
|
|
@@ -326,7 +334,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
326
334
|
const cpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
|
|
327
335
|
return Math.max(acc, Number.isFinite(entry.peakCpu) ? entry.peakCpu : 0, Number.isFinite(entry.cpu) ? entry.cpu : 0);
|
|
328
336
|
}, 0);
|
|
329
|
-
const
|
|
337
|
+
const cpuMaxYAxis = cpuPeakValue > 0 ? cpuPeakValue * 1.05 : undefined;
|
|
330
338
|
|
|
331
339
|
const processCpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
|
|
332
340
|
return Math.max(
|
|
@@ -335,7 +343,33 @@ export function generateHistoryPage(options = {}) {
|
|
|
335
343
|
Number.isFinite(entry.processCpu) ? entry.processCpu : 0
|
|
336
344
|
);
|
|
337
345
|
}, 0);
|
|
338
|
-
const
|
|
346
|
+
const processCpuMaxYAxis = processCpuPeakValue > 0 ? processCpuPeakValue * 1.05 : undefined;
|
|
347
|
+
const useProcessCpuDecimals = (processCpuMaxYAxis ?? 0) <= 3;
|
|
348
|
+
|
|
349
|
+
// Compute memory chart dynamic minimum Y as (min observed MB) - 10%
|
|
350
|
+
const memoryMinMb = HISTORY_DATA.reduce(function (acc, entry) {
|
|
351
|
+
const values = [entry.rss, entry.heapTotal, entry.heapUsed].map(bytesToMb);
|
|
352
|
+
const finiteValues = values.filter(function (v) { return Number.isFinite(v); });
|
|
353
|
+
const minEntry = finiteValues.length ? Math.min.apply(Math, finiteValues) : acc;
|
|
354
|
+
return Math.min(acc, minEntry);
|
|
355
|
+
}, Number.POSITIVE_INFINITY);
|
|
356
|
+
const memoryMinYAxis = Number.isFinite(memoryMinMb) && memoryMinMb > 0 ? Math.max(0, memoryMinMb - memoryMinMb * 0.1) : 0;
|
|
357
|
+
|
|
358
|
+
// Compute memory chart dynamic maximum Y as (max observed MB including peaks) + 5%
|
|
359
|
+
const memoryMaxMb = HISTORY_DATA.reduce(function (acc, entry) {
|
|
360
|
+
const values = [
|
|
361
|
+
entry.rss,
|
|
362
|
+
entry.heapTotal,
|
|
363
|
+
entry.heapUsed,
|
|
364
|
+
entry.peakRss,
|
|
365
|
+
entry.peakHeapTotal,
|
|
366
|
+
entry.peakHeapUsed
|
|
367
|
+
].map(bytesToMb);
|
|
368
|
+
const finiteValues = values.filter(function (v) { return Number.isFinite(v); });
|
|
369
|
+
const maxEntry = finiteValues.length ? Math.max.apply(Math, finiteValues) : acc;
|
|
370
|
+
return Math.max(acc, maxEntry);
|
|
371
|
+
}, 0);
|
|
372
|
+
const memoryMaxYAxis = Number.isFinite(memoryMaxMb) && memoryMaxMb > 0 ? memoryMaxMb * 1.05 : undefined;
|
|
339
373
|
|
|
340
374
|
renderCharts();
|
|
341
375
|
|
|
@@ -364,7 +398,7 @@ export function generateHistoryPage(options = {}) {
|
|
|
364
398
|
}
|
|
365
399
|
],
|
|
366
400
|
minY: 0,
|
|
367
|
-
maxY:
|
|
401
|
+
maxY: cpuMaxYAxis,
|
|
368
402
|
yFormatter: function (value) {
|
|
369
403
|
return value.toFixed(0) + ' %';
|
|
370
404
|
}
|
|
@@ -397,9 +431,9 @@ export function generateHistoryPage(options = {}) {
|
|
|
397
431
|
}
|
|
398
432
|
],
|
|
399
433
|
minY: 0,
|
|
400
|
-
maxY:
|
|
434
|
+
maxY: processCpuMaxYAxis,
|
|
401
435
|
yFormatter: function (value) {
|
|
402
|
-
return value.toFixed(0) + ' %';
|
|
436
|
+
return value.toFixed(useProcessCpuDecimals ? 1 : 0) + ' %';
|
|
403
437
|
}
|
|
404
438
|
});
|
|
405
439
|
|
|
@@ -423,7 +457,8 @@ export function generateHistoryPage(options = {}) {
|
|
|
423
457
|
color: '#f472b6'
|
|
424
458
|
}
|
|
425
459
|
],
|
|
426
|
-
minY:
|
|
460
|
+
minY: memoryMinYAxis,
|
|
461
|
+
maxY: memoryMaxYAxis,
|
|
427
462
|
yFormatter: function (value) {
|
|
428
463
|
return value.toFixed(0) + ' MB';
|
|
429
464
|
}
|
package/dist/frontend.js
CHANGED
|
@@ -3,9 +3,6 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import EventEmitter from 'node:events';
|
|
6
|
-
import express from 'express';
|
|
7
|
-
import WebSocket, { WebSocketServer } from 'ws';
|
|
8
|
-
import multer from 'multer';
|
|
9
6
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt, wr } from 'node-ansi-logger';
|
|
10
7
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
|
|
11
8
|
import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
|
|
@@ -13,10 +10,14 @@ import { PowerSource } from '@matter/main/clusters/power-source';
|
|
|
13
10
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/main/protocol';
|
|
14
11
|
import { CommissioningOptions } from '@matter/main/types';
|
|
15
12
|
import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg } from './matterbridgeTypes.js';
|
|
16
|
-
import {
|
|
13
|
+
import { isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/isvalid.js';
|
|
14
|
+
import { createZip } from './utils/createZip.js';
|
|
15
|
+
import { hasParameter } from './utils/commandLine.js';
|
|
16
|
+
import { withTimeout, wait } from './utils/wait.js';
|
|
17
|
+
import { inspectError } from './utils/error.js';
|
|
17
18
|
import { formatMemoryUsage, formatOsUpTime } from './utils/network.js';
|
|
18
19
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
19
|
-
import { cliEmitter,
|
|
20
|
+
import { cliEmitter, lastOsCpuUsage, lastProcessCpuUsage } from './cliEmitter.js';
|
|
20
21
|
import { generateHistoryPage } from './cliHistory.js';
|
|
21
22
|
import { BroadcastServer } from './broadcastServer.js';
|
|
22
23
|
export class Frontend extends EventEmitter {
|
|
@@ -94,9 +95,11 @@ export class Frontend extends EventEmitter {
|
|
|
94
95
|
async start(port = 8283) {
|
|
95
96
|
this.port = port;
|
|
96
97
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
98
|
+
const multer = await import('multer');
|
|
97
99
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
98
|
-
const upload = multer({ dest: uploadDir });
|
|
99
|
-
|
|
100
|
+
const upload = multer.default({ dest: uploadDir });
|
|
101
|
+
const express = await import('express');
|
|
102
|
+
this.expressApp = express.default();
|
|
100
103
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
101
104
|
if (!hasParameter('ssl')) {
|
|
102
105
|
const http = await import('node:http');
|
|
@@ -245,8 +248,9 @@ export class Frontend extends EventEmitter {
|
|
|
245
248
|
return;
|
|
246
249
|
});
|
|
247
250
|
}
|
|
251
|
+
const ws = await import('ws');
|
|
248
252
|
this.log.debug(`Creating WebSocketServer...`);
|
|
249
|
-
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
253
|
+
this.webSocketServer = new ws.WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
250
254
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
251
255
|
const clientIp = request.socket.remoteAddress;
|
|
252
256
|
let callbackLogLevel = "notice";
|
|
@@ -625,6 +629,7 @@ export class Frontend extends EventEmitter {
|
|
|
625
629
|
}
|
|
626
630
|
async stop() {
|
|
627
631
|
this.log.debug('Stopping the frontend...');
|
|
632
|
+
const ws = await import('ws');
|
|
628
633
|
if (this.expressApp) {
|
|
629
634
|
this.expressApp.removeAllListeners();
|
|
630
635
|
this.expressApp = undefined;
|
|
@@ -633,7 +638,7 @@ export class Frontend extends EventEmitter {
|
|
|
633
638
|
if (this.webSocketServer) {
|
|
634
639
|
this.log.debug('Closing WebSocket server...');
|
|
635
640
|
this.webSocketServer.clients.forEach((client) => {
|
|
636
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
641
|
+
if (client.readyState === ws.WebSocket.OPEN) {
|
|
637
642
|
client.close();
|
|
638
643
|
}
|
|
639
644
|
});
|
|
@@ -679,7 +684,7 @@ export class Frontend extends EventEmitter {
|
|
|
679
684
|
this.matterbridge.systemInformation.freeMemory = formatMemoryUsage(os.freemem());
|
|
680
685
|
this.matterbridge.systemInformation.systemUptime = formatOsUpTime(os.uptime());
|
|
681
686
|
this.matterbridge.systemInformation.processUptime = formatOsUpTime(Math.floor(process.uptime()));
|
|
682
|
-
this.matterbridge.systemInformation.cpuUsage =
|
|
687
|
+
this.matterbridge.systemInformation.cpuUsage = lastOsCpuUsage.toFixed(2) + ' %';
|
|
683
688
|
this.matterbridge.systemInformation.processCpuUsage = lastProcessCpuUsage.toFixed(2) + ' %';
|
|
684
689
|
this.matterbridge.systemInformation.rss = formatMemoryUsage(process.memoryUsage().rss);
|
|
685
690
|
this.matterbridge.systemInformation.heapTotal = formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
@@ -999,7 +1004,7 @@ export class Frontend extends EventEmitter {
|
|
|
999
1004
|
async wsMessageHandler(client, message) {
|
|
1000
1005
|
let data;
|
|
1001
1006
|
const sendResponse = (data) => {
|
|
1002
|
-
if (client.readyState ===
|
|
1007
|
+
if (client.readyState === client.OPEN) {
|
|
1003
1008
|
if ('response' in data) {
|
|
1004
1009
|
const { response, ...rest } = data;
|
|
1005
1010
|
this.log.debug(`Sending api response message: ${debugStringify(rest)}`);
|
|
@@ -1721,6 +1726,8 @@ export class Frontend extends EventEmitter {
|
|
|
1721
1726
|
}
|
|
1722
1727
|
}
|
|
1723
1728
|
wssSendLogMessage(level, time, name, message) {
|
|
1729
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1730
|
+
return;
|
|
1724
1731
|
if (!level || !time || !name || !message)
|
|
1725
1732
|
return;
|
|
1726
1733
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
@@ -1745,10 +1752,14 @@ export class Frontend extends EventEmitter {
|
|
|
1745
1752
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
1746
1753
|
}
|
|
1747
1754
|
wssSendRefreshRequired(changed, params) {
|
|
1755
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1756
|
+
return;
|
|
1748
1757
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1749
1758
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
1750
1759
|
}
|
|
1751
1760
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
1761
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1762
|
+
return;
|
|
1752
1763
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1753
1764
|
this.matterbridge.restartRequired = true;
|
|
1754
1765
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
@@ -1757,6 +1768,8 @@ export class Frontend extends EventEmitter {
|
|
|
1757
1768
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
1758
1769
|
}
|
|
1759
1770
|
wssSendRestartNotRequired(snackbar = true) {
|
|
1771
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1772
|
+
return;
|
|
1760
1773
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
1761
1774
|
this.matterbridge.restartRequired = false;
|
|
1762
1775
|
if (snackbar === true)
|
|
@@ -1764,43 +1777,59 @@ export class Frontend extends EventEmitter {
|
|
|
1764
1777
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
1765
1778
|
}
|
|
1766
1779
|
wssSendUpdateRequired(devVersion = false) {
|
|
1780
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1781
|
+
return;
|
|
1767
1782
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1768
1783
|
this.matterbridge.updateRequired = true;
|
|
1769
1784
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
1770
1785
|
}
|
|
1771
1786
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
1787
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1788
|
+
return;
|
|
1772
1789
|
if (hasParameter('debug'))
|
|
1773
1790
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1774
1791
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 } });
|
|
1775
1792
|
}
|
|
1776
1793
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1794
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1795
|
+
return;
|
|
1777
1796
|
if (hasParameter('debug'))
|
|
1778
1797
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1779
1798
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
1780
1799
|
}
|
|
1781
1800
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1801
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1802
|
+
return;
|
|
1782
1803
|
if (hasParameter('debug'))
|
|
1783
1804
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1784
1805
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
1785
1806
|
}
|
|
1786
1807
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1808
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1809
|
+
return;
|
|
1787
1810
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1788
1811
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
1789
1812
|
}
|
|
1790
1813
|
wssSendCloseSnackbarMessage(message) {
|
|
1814
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1815
|
+
return;
|
|
1791
1816
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1792
1817
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
1793
1818
|
}
|
|
1794
1819
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
1820
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1821
|
+
return;
|
|
1795
1822
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1796
1823
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
1797
1824
|
}
|
|
1798
1825
|
wssBroadcastMessage(msg) {
|
|
1826
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
1827
|
+
return;
|
|
1799
1828
|
const stringifiedMsg = JSON.stringify(msg);
|
|
1800
1829
|
if (msg.method !== 'log')
|
|
1801
1830
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
1802
1831
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1803
|
-
if (client.readyState ===
|
|
1832
|
+
if (client.readyState === client.OPEN) {
|
|
1804
1833
|
client.send(stringifiedMsg);
|
|
1805
1834
|
}
|
|
1806
1835
|
});
|
package/dist/matterbridge.js
CHANGED
|
@@ -871,26 +871,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
871
871
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
872
872
|
}
|
|
873
873
|
async setLogLevel(logLevel) {
|
|
874
|
-
|
|
875
|
-
this.log.logLevel = logLevel;
|
|
874
|
+
this.log.logLevel = logLevel;
|
|
876
875
|
this.frontend.logLevel = logLevel;
|
|
877
876
|
MatterbridgeEndpoint.logLevel = logLevel;
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (this.plugins)
|
|
881
|
-
this.plugins.logLevel = logLevel;
|
|
877
|
+
this.devices.logLevel = logLevel;
|
|
878
|
+
this.plugins.logLevel = logLevel;
|
|
882
879
|
for (const plugin of this.plugins) {
|
|
883
880
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
884
881
|
continue;
|
|
885
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" :
|
|
886
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" :
|
|
882
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
883
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
887
884
|
}
|
|
888
885
|
let callbackLogLevel = "notice";
|
|
889
|
-
if (
|
|
886
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
890
887
|
callbackLogLevel = "info";
|
|
891
|
-
if (
|
|
888
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
892
889
|
callbackLogLevel = "debug";
|
|
893
|
-
AnsiLogger.
|
|
890
|
+
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
894
891
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
895
892
|
}
|
|
896
893
|
getLogLevel() {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.3.1-dev-
|
|
3
|
+
"version": "3.3.1-dev-20251012-b3546f8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "3.3.1-dev-
|
|
9
|
+
"version": "3.3.1-dev-20251012-b3546f8",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.15.5",
|
package/package.json
CHANGED