matterbridge 3.3.9-dev-20251118-930cfdb → 3.4.0-dev-20251120-1b65c89
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 +11 -1
- package/dist/broadcastServer.js +1 -1
- package/dist/deviceManager.js +33 -15
- package/dist/frontend.js +19 -7
- package/dist/matterbridge.js +8 -11
- package/dist/matterbridgeEndpoint.js +6 -0
- package/dist/matterbridgePlatform.js +15 -1
- package/dist/pluginManager.js +15 -19
- package/dist/utils/copyDirectory.js +3 -5
- package/dist/utils/spawn.js +36 -23
- package/npm-shrinkwrap.json +13 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,7 +28,7 @@ Advantages:
|
|
|
28
28
|
- individual plugin isolation in childbridge mode;
|
|
29
29
|
- ability to update the plugin in childbridge mode without restarting matterbridge;
|
|
30
30
|
|
|
31
|
-
## [3.
|
|
31
|
+
## [3.4.0] - Not published
|
|
32
32
|
|
|
33
33
|
### Development Breaking Changes
|
|
34
34
|
|
|
@@ -38,9 +38,19 @@ Removed the following long deprecated elements:
|
|
|
38
38
|
- [endpoint]: uniqueStorageKey instead of id in MatterbridgeEndpointOptions (deprecated since months).
|
|
39
39
|
- [endpoint]: endpointId instead of number in MatterbridgeEndpointOptions (deprecated since months).
|
|
40
40
|
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- [endpoint]: Added getChildEndpointById() and getChildEndpointByOriginalId().
|
|
44
|
+
- [endpoint]: Deprecated getChildEndpointByName(). Use getChildEndpointById() or getChildEndpointByOriginalId().
|
|
45
|
+
- [platform]: Added wssSendSnackbarMessage method to MatterbridgePlatform for sending snackbar notifications to the frontend.
|
|
46
|
+
|
|
41
47
|
### Changed
|
|
42
48
|
|
|
43
49
|
- [package]: Updated dependencies.
|
|
50
|
+
- [deviceManager]: Bumped DeviceManager v.1.1.1.
|
|
51
|
+
- [broadcastServer]: Bumped BroadcastServer v.1.0.3.
|
|
52
|
+
- [jest]: Bumped jestHelpers v.1.0.13.
|
|
53
|
+
- [spawn]: Bumped spawn module v.1.2.0.
|
|
44
54
|
|
|
45
55
|
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
46
56
|
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
package/dist/broadcastServer.js
CHANGED
|
@@ -70,7 +70,7 @@ export class BroadcastServer extends EventEmitter {
|
|
|
70
70
|
this.log.debug(`Broadcasting response message: ${debugStringify(message)}`);
|
|
71
71
|
this.broadcastChannel.postMessage(message);
|
|
72
72
|
}
|
|
73
|
-
async fetch(message, timeout =
|
|
73
|
+
async fetch(message, timeout = 250) {
|
|
74
74
|
if (message.id === undefined) {
|
|
75
75
|
message.id = this.getUniqueId();
|
|
76
76
|
}
|
package/dist/deviceManager.js
CHANGED
|
@@ -2,6 +2,31 @@ import { AnsiLogger, BLUE, CYAN, db, debugStringify, er } from 'node-ansi-logger
|
|
|
2
2
|
import { dev } from './matterbridgeTypes.js';
|
|
3
3
|
import { BroadcastServer } from './broadcastServer.js';
|
|
4
4
|
import { hasParameter } from './utils/commandLine.js';
|
|
5
|
+
export function toBaseDevice(device) {
|
|
6
|
+
return {
|
|
7
|
+
mode: device.mode,
|
|
8
|
+
plugin: device.plugin,
|
|
9
|
+
configUrl: device.configUrl,
|
|
10
|
+
deviceName: device.deviceName,
|
|
11
|
+
serialNumber: device.serialNumber,
|
|
12
|
+
uniqueId: device.uniqueId,
|
|
13
|
+
vendorId: device.vendorId,
|
|
14
|
+
vendorName: device.vendorName,
|
|
15
|
+
productId: device.productId,
|
|
16
|
+
productName: device.productName,
|
|
17
|
+
softwareVersion: device.softwareVersion,
|
|
18
|
+
softwareVersionString: device.softwareVersionString,
|
|
19
|
+
hardwareVersion: device.hardwareVersion,
|
|
20
|
+
hardwareVersionString: device.hardwareVersionString,
|
|
21
|
+
productUrl: device.productUrl,
|
|
22
|
+
tagList: device.tagList,
|
|
23
|
+
originalId: device.originalId,
|
|
24
|
+
name: device.name,
|
|
25
|
+
deviceType: device.deviceType,
|
|
26
|
+
number: device.number,
|
|
27
|
+
id: device.id,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
5
30
|
export class DeviceManager {
|
|
6
31
|
_devices = new Map();
|
|
7
32
|
log;
|
|
@@ -40,13 +65,16 @@ export class DeviceManager {
|
|
|
40
65
|
this.server.respond({ ...msg, response: { has: this.has(msg.params.uniqueId) } });
|
|
41
66
|
break;
|
|
42
67
|
case 'devices_get':
|
|
43
|
-
|
|
68
|
+
{
|
|
69
|
+
const endpoint = this.get(msg.params.uniqueId);
|
|
70
|
+
this.server.respond({ ...msg, response: { device: endpoint ? toBaseDevice(endpoint) : undefined } });
|
|
71
|
+
}
|
|
44
72
|
break;
|
|
45
73
|
case 'devices_set':
|
|
46
|
-
this.server.respond({ ...msg, response: { device: this.set(msg.params.device) } });
|
|
74
|
+
this.server.respond({ ...msg, response: { device: this.set(toBaseDevice(msg.params.device)) } });
|
|
47
75
|
break;
|
|
48
76
|
case 'devices_remove':
|
|
49
|
-
this.server.respond({ ...msg, response: { success: this.remove(msg.params.device) } });
|
|
77
|
+
this.server.respond({ ...msg, response: { success: this.remove(toBaseDevice(msg.params.device)) } });
|
|
50
78
|
break;
|
|
51
79
|
case 'devices_clear':
|
|
52
80
|
this.clear();
|
|
@@ -92,24 +120,14 @@ export class DeviceManager {
|
|
|
92
120
|
this._devices.clear();
|
|
93
121
|
}
|
|
94
122
|
toBaseDevice(device) {
|
|
95
|
-
return
|
|
96
|
-
pluginName: device.plugin,
|
|
97
|
-
deviceType: device.deviceType,
|
|
98
|
-
number: device.maybeNumber,
|
|
99
|
-
id: device.maybeId,
|
|
100
|
-
deviceName: device.deviceName,
|
|
101
|
-
serialNumber: device.serialNumber,
|
|
102
|
-
uniqueId: device.uniqueId,
|
|
103
|
-
productUrl: device.productUrl,
|
|
104
|
-
configUrl: device.configUrl,
|
|
105
|
-
};
|
|
123
|
+
return toBaseDevice(device);
|
|
106
124
|
}
|
|
107
125
|
array() {
|
|
108
126
|
return Array.from(this._devices.values());
|
|
109
127
|
}
|
|
110
128
|
baseArray(pluginName) {
|
|
111
129
|
const devices = [];
|
|
112
|
-
for (const device of this._devices.values()) {
|
|
130
|
+
for (const device of Array.from(this._devices.values())) {
|
|
113
131
|
if (pluginName && pluginName !== device.plugin)
|
|
114
132
|
continue;
|
|
115
133
|
devices.push(this.toBaseDevice(device));
|
package/dist/frontend.js
CHANGED
|
@@ -89,6 +89,10 @@ export class Frontend extends EventEmitter {
|
|
|
89
89
|
this.wssSendAttributeChangedMessage(msg.params.plugin, msg.params.serialNumber, msg.params.uniqueId, msg.params.number, msg.params.id, msg.params.cluster, msg.params.attribute, msg.params.value);
|
|
90
90
|
this.server.respond({ ...msg, response: { success: true } });
|
|
91
91
|
break;
|
|
92
|
+
case 'frontend_logmessage':
|
|
93
|
+
this.wssSendLogMessage(msg.params.level, msg.params.time, msg.params.name, msg.params.message);
|
|
94
|
+
this.server.respond({ ...msg, response: { success: true } });
|
|
95
|
+
break;
|
|
92
96
|
default:
|
|
93
97
|
if (this.verbose)
|
|
94
98
|
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
@@ -689,15 +693,23 @@ export class Frontend extends EventEmitter {
|
|
|
689
693
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
690
694
|
if (filename.endsWith('.tgz')) {
|
|
691
695
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
692
|
-
await spawnCommand(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
696
|
+
if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
|
|
697
|
+
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
698
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
699
|
+
this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
|
|
700
|
+
this.wssSendRestartRequired();
|
|
701
|
+
res.send(`Plugin package ${filename} uploaded and installed successfully`);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}`);
|
|
705
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
706
|
+
this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
|
|
707
|
+
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
708
|
+
}
|
|
698
709
|
}
|
|
699
|
-
else
|
|
710
|
+
else {
|
|
700
711
|
res.send(`File ${filename} uploaded successfully`);
|
|
712
|
+
}
|
|
701
713
|
}
|
|
702
714
|
catch (err) {
|
|
703
715
|
this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}:`, err);
|
package/dist/matterbridge.js
CHANGED
|
@@ -28,7 +28,6 @@ import { bridge } from './matterbridgeDeviceTypes.js';
|
|
|
28
28
|
import { Frontend } from './frontend.js';
|
|
29
29
|
import { addVirtualDevices } from './helpers.js';
|
|
30
30
|
import { BroadcastServer } from './broadcastServer.js';
|
|
31
|
-
import { inspectError } from './utils/error.js';
|
|
32
31
|
export class Matterbridge extends EventEmitter {
|
|
33
32
|
systemInformation = {
|
|
34
33
|
interfaceName: '',
|
|
@@ -462,14 +461,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
462
461
|
for (const plugin of this.plugins) {
|
|
463
462
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
464
463
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], 'install', plugin.name);
|
|
464
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
465
|
+
if (await spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], 'install', plugin.name)) {
|
|
468
466
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
469
467
|
plugin.error = false;
|
|
470
468
|
}
|
|
471
|
-
|
|
472
|
-
|
|
469
|
+
else {
|
|
470
|
+
this.log.error(`Error reinstalling plugin ${plg}${plugin.name}${nf}. The plugin is disabled.`);
|
|
473
471
|
plugin.error = true;
|
|
474
472
|
plugin.enabled = false;
|
|
475
473
|
continue;
|
|
@@ -905,13 +903,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
905
903
|
}
|
|
906
904
|
async updateProcess() {
|
|
907
905
|
this.log.info('Updating matterbridge...');
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'install', 'matterbridge');
|
|
906
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
907
|
+
if (await spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'install', 'matterbridge')) {
|
|
911
908
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
912
909
|
}
|
|
913
|
-
|
|
914
|
-
this.log.error(
|
|
910
|
+
else {
|
|
911
|
+
this.log.error('Error updating matterbridge.');
|
|
915
912
|
}
|
|
916
913
|
this.frontend.wssSendRestartRequired();
|
|
917
914
|
await this.cleanup('updating...', false);
|
|
@@ -368,6 +368,12 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
368
368
|
getChildEndpointByName(endpointName) {
|
|
369
369
|
return this.parts.find((part) => part.id === endpointName);
|
|
370
370
|
}
|
|
371
|
+
getChildEndpointById(id) {
|
|
372
|
+
return this.parts.find((part) => part.id === id);
|
|
373
|
+
}
|
|
374
|
+
getChildEndpointByOriginalId(originalId) {
|
|
375
|
+
return this.parts.find((part) => part.originalId === originalId);
|
|
376
|
+
}
|
|
371
377
|
getChildEndpoint(endpointNumber) {
|
|
372
378
|
return this.parts.find((part) => part.number === endpointNumber);
|
|
373
379
|
}
|
|
@@ -9,6 +9,8 @@ import { checkNotLatinCharacters } from './matterbridgeEndpointHelpers.js';
|
|
|
9
9
|
import { bridgedNode } from './matterbridgeDeviceTypes.js';
|
|
10
10
|
import { isValidArray, isValidObject, isValidString } from './utils/isvalid.js';
|
|
11
11
|
import { addVirtualDevice } from './helpers.js';
|
|
12
|
+
import { hasParameter } from './utils/commandLine.js';
|
|
13
|
+
import { BroadcastServer } from './broadcastServer.js';
|
|
12
14
|
export class MatterbridgePlatform {
|
|
13
15
|
matterbridge;
|
|
14
16
|
log;
|
|
@@ -26,10 +28,18 @@ export class MatterbridgePlatform {
|
|
|
26
28
|
ready;
|
|
27
29
|
registeredEndpointsByUniqueId = new Map();
|
|
28
30
|
registeredEndpointsByName = new Map();
|
|
31
|
+
#server;
|
|
32
|
+
#debug = hasParameter('debug') || hasParameter('verbose');
|
|
33
|
+
#verbose = hasParameter('verbose');
|
|
29
34
|
constructor(matterbridge, log, config) {
|
|
30
35
|
this.matterbridge = matterbridge;
|
|
31
36
|
this.log = log;
|
|
32
37
|
this.config = config;
|
|
38
|
+
this.#server = new BroadcastServer('platform', this.log);
|
|
39
|
+
if (this.#debug && !this.#verbose)
|
|
40
|
+
this.log.debug(`Creating MatterbridgePlatform for plugin ${this.config.name}`);
|
|
41
|
+
if (this.#verbose)
|
|
42
|
+
this.log.debug(`Creating MatterbridgePlatform for plugin ${this.config.name} with config:\n${JSON.stringify(this.config, null, 2)}\n`);
|
|
33
43
|
if (!isValidString(this.config.name, 1))
|
|
34
44
|
throw new Error('Platform: the plugin name is missing or invalid.');
|
|
35
45
|
this.log.debug(`Creating storage for plugin ${this.config.name} in ${path.join(this.matterbridge.matterbridgeDirectory, this.config.name)}`);
|
|
@@ -87,6 +97,7 @@ export class MatterbridgePlatform {
|
|
|
87
97
|
await this.context?.close();
|
|
88
98
|
this.context = undefined;
|
|
89
99
|
await this.storage?.close();
|
|
100
|
+
this.#server.close();
|
|
90
101
|
}
|
|
91
102
|
async onChangeLoggerLevel(logLevel) {
|
|
92
103
|
this.log.debug(`The plugin doesn't override onChangeLoggerLevel. Logger level set to: ${logLevel}`);
|
|
@@ -119,7 +130,10 @@ export class MatterbridgePlatform {
|
|
|
119
130
|
plugin.schemaJson = schema;
|
|
120
131
|
}
|
|
121
132
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
122
|
-
this.
|
|
133
|
+
this.#server.request({ type: 'frontend_restartrequired', src: 'platform', dst: 'frontend', params: { snackbar, fixed } });
|
|
134
|
+
}
|
|
135
|
+
wssSendSnackbarMessage(message, timeout, severity) {
|
|
136
|
+
this.#server.request({ type: 'frontend_snackbarmessage', src: 'platform', dst: 'frontend', params: { message, timeout, severity } });
|
|
123
137
|
}
|
|
124
138
|
getDevices() {
|
|
125
139
|
return Array.from(this.registeredEndpointsByUniqueId.values());
|
package/dist/pluginManager.js
CHANGED
|
@@ -370,8 +370,7 @@ export class PluginManager extends EventEmitter {
|
|
|
370
370
|
async install(packageName) {
|
|
371
371
|
this.log.debug(`Installing plugin ${plg}${packageName}${db}...`);
|
|
372
372
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
373
|
-
|
|
374
|
-
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', packageName, '--omit=dev', '--verbose'], 'install', packageName);
|
|
373
|
+
if (await spawnCommand('npm', ['install', '-g', packageName, '--omit=dev', '--verbose'], 'install', packageName)) {
|
|
375
374
|
this.matterbridge.restartRequired = true;
|
|
376
375
|
this.matterbridge.fixedRestartRequired = true;
|
|
377
376
|
packageName = packageName.replace(/@.*$/, '');
|
|
@@ -387,12 +386,11 @@ export class PluginManager extends EventEmitter {
|
|
|
387
386
|
await this.matterbridge.shutdownProcess();
|
|
388
387
|
}
|
|
389
388
|
}
|
|
390
|
-
this.log.
|
|
389
|
+
this.log.info(`Installed plugin ${plg}${packageName}${db} successfully`);
|
|
391
390
|
return true;
|
|
392
391
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
this.log.debug(`Failed to install plugin ${plg}${packageName}${db}`);
|
|
392
|
+
else {
|
|
393
|
+
this.log.error(`Failed to install plugin ${plg}${packageName}${er}`);
|
|
396
394
|
return false;
|
|
397
395
|
}
|
|
398
396
|
}
|
|
@@ -402,20 +400,18 @@ export class PluginManager extends EventEmitter {
|
|
|
402
400
|
packageName = packageName.replace(/@.*$/, '');
|
|
403
401
|
if (packageName === 'matterbridge')
|
|
404
402
|
return false;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.log.debug(`Uninstalled plugin ${plg}${packageName}${db} successfully`);
|
|
403
|
+
if (this.has(packageName)) {
|
|
404
|
+
const plugin = this.get(packageName);
|
|
405
|
+
if (plugin && plugin.loaded)
|
|
406
|
+
await this.shutdown(plugin, 'Matterbridge is uninstalling the plugin');
|
|
407
|
+
await this.remove(packageName);
|
|
408
|
+
}
|
|
409
|
+
if (await spawnCommand('npm', ['uninstall', '-g', packageName, '--verbose'], 'uninstall', packageName)) {
|
|
410
|
+
this.log.info(`Uninstalled plugin ${plg}${packageName}${db} successfully`);
|
|
414
411
|
return true;
|
|
415
412
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
this.log.debug(`Failed to uninstall plugin ${plg}${packageName}${db}`);
|
|
413
|
+
else {
|
|
414
|
+
this.log.error(`Failed to uninstall plugin ${plg}${packageName}${er}`);
|
|
419
415
|
return false;
|
|
420
416
|
}
|
|
421
417
|
}
|
|
@@ -741,7 +737,7 @@ export class PluginManager extends EventEmitter {
|
|
|
741
737
|
plugin.author = this.getAuthor(packageJson);
|
|
742
738
|
plugin.configJson = config;
|
|
743
739
|
plugin.schemaJson = await this.loadSchema(plugin);
|
|
744
|
-
config.name =
|
|
740
|
+
config.name = packageJson.name;
|
|
745
741
|
config.version = packageJson.version;
|
|
746
742
|
const log = new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4, logLevel: config.debug ? "debug" : this.matterbridge.log.logLevel });
|
|
747
743
|
const platform = pluginInstance.default(this.matterbridge, log, config);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export async function copyDirectory(srcDir, destDir) {
|
|
1
|
+
export async function copyDirectory(srcDir, destDir, log) {
|
|
3
2
|
if (srcDir === '') {
|
|
4
3
|
throw new Error('Source directory must be specified.');
|
|
5
4
|
}
|
|
@@ -15,10 +14,9 @@ export async function copyDirectory(srcDir, destDir) {
|
|
|
15
14
|
if (srcDir === destDir) {
|
|
16
15
|
throw new Error('Source and destination directories must be different.');
|
|
17
16
|
}
|
|
18
|
-
const log = new AnsiLogger({ logName: 'CopyDirectory', logTimestampFormat: 4, logLevel: "info" });
|
|
19
17
|
const fs = await import('node:fs').then((mod) => mod.promises);
|
|
20
18
|
const path = await import('node:path');
|
|
21
|
-
log
|
|
19
|
+
log?.debug(`copyDirectory: copying directory from ${srcDir} to ${destDir}`);
|
|
22
20
|
try {
|
|
23
21
|
await fs.mkdir(destDir, { recursive: true });
|
|
24
22
|
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
@@ -35,7 +33,7 @@ export async function copyDirectory(srcDir, destDir) {
|
|
|
35
33
|
return true;
|
|
36
34
|
}
|
|
37
35
|
catch (error) {
|
|
38
|
-
log
|
|
36
|
+
log?.error(`copyDirectory error copying from ${srcDir} to ${destDir}: ${error instanceof Error ? error.message : error}`);
|
|
39
37
|
return false;
|
|
40
38
|
}
|
|
41
39
|
}
|
package/dist/utils/spawn.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import { AnsiLogger } from 'node-ansi-logger';
|
|
2
|
+
import { BroadcastServer } from '../broadcastServer.js';
|
|
1
3
|
import { hasParameter } from './commandLine.js';
|
|
2
|
-
export async function spawnCommand(
|
|
4
|
+
export async function spawnCommand(command, args, packageCommand, packageName) {
|
|
3
5
|
const { spawn } = await import('node:child_process');
|
|
6
|
+
const debug = hasParameter('debug') || hasParameter('verbose');
|
|
7
|
+
const verbose = hasParameter('verbose');
|
|
8
|
+
const log = new AnsiLogger({ logName: 'Spawn', logTimestampFormat: 4, logLevel: debug ? "debug" : "info" });
|
|
9
|
+
const server = new BroadcastServer('spawn', log);
|
|
10
|
+
const sendLog = (name, message) => {
|
|
11
|
+
server.request({ type: 'frontend_logmessage', src: 'spawn', dst: 'frontend', params: { level: 'spawn', time: log.now(), name, message } });
|
|
12
|
+
};
|
|
13
|
+
if (verbose)
|
|
14
|
+
log.debug(`Spawning command: ${command} with ${args.join(' ')} ${packageCommand} ${packageName}`);
|
|
4
15
|
const cmdLine = command + ' ' + args.join(' ');
|
|
5
16
|
if (process.platform === 'win32' && command === 'npm') {
|
|
6
17
|
const argstring = 'npm ' + args.join(' ');
|
|
@@ -11,46 +22,46 @@ export async function spawnCommand(matterbridge, command, args, packageCommand,
|
|
|
11
22
|
args.unshift(command);
|
|
12
23
|
command = 'sudo';
|
|
13
24
|
}
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
log.debug(`Spawn command ${command} with ${args.join(' ')}`);
|
|
26
|
+
const success = await new Promise((resolve) => {
|
|
16
27
|
if (packageCommand === 'install')
|
|
17
|
-
|
|
28
|
+
sendLog('Matterbridge:spawn-init', `Installing ${packageName}`);
|
|
18
29
|
else if (packageCommand === 'uninstall')
|
|
19
|
-
|
|
30
|
+
sendLog('Matterbridge:spawn-init', `Uninstalling ${packageName}`);
|
|
20
31
|
const childProcess = spawn(command, args, {
|
|
21
32
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
22
33
|
});
|
|
23
34
|
childProcess.on('error', (err) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
log.error(`Failed to start child process "${cmdLine}": ${err.message}`);
|
|
36
|
+
sendLog('Matterbridge:spawn-exit-error', 'Spawn process error');
|
|
37
|
+
resolve(false);
|
|
27
38
|
});
|
|
28
39
|
childProcess.on('close', (code, signal) => {
|
|
29
40
|
if (code === 0) {
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
42
|
+
sendLog('Matterbridge:spawn-exit-success', 'Child process closed');
|
|
32
43
|
resolve(true);
|
|
33
44
|
}
|
|
34
45
|
else {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
47
|
+
sendLog('Matterbridge:spawn-exit-error', 'Child process closed');
|
|
48
|
+
resolve(false);
|
|
38
49
|
}
|
|
39
50
|
});
|
|
40
51
|
childProcess.on('exit', (code, signal) => {
|
|
41
52
|
if (code === 0) {
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
54
|
+
sendLog('Matterbridge:spawn-exit-success', 'Child process exited');
|
|
44
55
|
resolve(true);
|
|
45
56
|
}
|
|
46
57
|
else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
59
|
+
sendLog('Matterbridge:spawn-exit-error', 'Child process exited');
|
|
60
|
+
resolve(false);
|
|
50
61
|
}
|
|
51
62
|
});
|
|
52
63
|
childProcess.on('disconnect', () => {
|
|
53
|
-
|
|
64
|
+
log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
|
|
54
65
|
resolve(true);
|
|
55
66
|
});
|
|
56
67
|
if (childProcess.stdout) {
|
|
@@ -58,8 +69,8 @@ export async function spawnCommand(matterbridge, command, args, packageCommand,
|
|
|
58
69
|
const message = data.toString().trim();
|
|
59
70
|
const lines = message.split('\n');
|
|
60
71
|
for (const line of lines) {
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
log.debug(`Spawn output (stdout): ${line}`);
|
|
73
|
+
sendLog('Matterbridge:spawn', line);
|
|
63
74
|
}
|
|
64
75
|
});
|
|
65
76
|
}
|
|
@@ -68,10 +79,12 @@ export async function spawnCommand(matterbridge, command, args, packageCommand,
|
|
|
68
79
|
const message = data.toString().trim();
|
|
69
80
|
const lines = message.split('\n');
|
|
70
81
|
for (const line of lines) {
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
log.debug(`Spawn verbose (stderr): ${line}`);
|
|
83
|
+
sendLog('Matterbridge:spawn', line);
|
|
73
84
|
}
|
|
74
85
|
});
|
|
75
86
|
}
|
|
76
87
|
});
|
|
88
|
+
server.close();
|
|
89
|
+
return success;
|
|
77
90
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0-dev-20251120-1b65c89",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.4.0-dev-20251120-1b65c89",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.15.6",
|
|
@@ -284,9 +284,9 @@
|
|
|
284
284
|
}
|
|
285
285
|
},
|
|
286
286
|
"node_modules/archiver-utils/node_modules/glob": {
|
|
287
|
-
"version": "10.
|
|
288
|
-
"resolved": "https://registry.npmjs.org/glob/-/glob-10.
|
|
289
|
-
"integrity": "sha512-
|
|
287
|
+
"version": "10.5.0",
|
|
288
|
+
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
|
289
|
+
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
|
290
290
|
"license": "ISC",
|
|
291
291
|
"dependencies": {
|
|
292
292
|
"foreground-child": "^3.1.0",
|
|
@@ -581,15 +581,16 @@
|
|
|
581
581
|
}
|
|
582
582
|
},
|
|
583
583
|
"node_modules/content-disposition": {
|
|
584
|
-
"version": "1.0.
|
|
585
|
-
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.
|
|
586
|
-
"integrity": "sha512-
|
|
584
|
+
"version": "1.0.1",
|
|
585
|
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
|
586
|
+
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
|
587
587
|
"license": "MIT",
|
|
588
|
-
"dependencies": {
|
|
589
|
-
"safe-buffer": "5.2.1"
|
|
590
|
-
},
|
|
591
588
|
"engines": {
|
|
592
|
-
"node": ">=
|
|
589
|
+
"node": ">=18"
|
|
590
|
+
},
|
|
591
|
+
"funding": {
|
|
592
|
+
"type": "opencollective",
|
|
593
|
+
"url": "https://opencollective.com/express"
|
|
593
594
|
}
|
|
594
595
|
},
|
|
595
596
|
"node_modules/content-type": {
|
package/package.json
CHANGED