@matterbridge/core 3.6.1-dev-20260311-7da5ff8 → 3.6.1-dev-20260312-a699c0e

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/frontend.js CHANGED
@@ -727,6 +727,7 @@ export class Frontend extends EventEmitter {
727
727
  });
728
728
  });
729
729
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
730
+ this.log.debug('The frontend sent /api/uploadpackage');
730
731
  if (!this.validateReq(req, res))
731
732
  return;
732
733
  const { filename } = req.body;
@@ -736,35 +737,34 @@ export class Frontend extends EventEmitter {
736
737
  res.status(400).send('Invalid request: file and filename are required');
737
738
  return;
738
739
  }
739
- this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
740
+ this.wssSendSnackbarMessage(`Installing package ${filename}...`, 0);
740
741
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
741
742
  try {
742
743
  const fs = await import('node:fs');
743
744
  await fs.promises.rename(file.path, filePath);
744
745
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
745
746
  if (filename.endsWith('.tgz')) {
746
- const { spawnCommand } = await import('./spawn.js');
747
- if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
748
- this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
749
- this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
750
- this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
751
- this.wssSendRestartRequired();
752
- res.send(`Plugin package ${filename} uploaded and installed successfully`);
753
- }
754
- else {
755
- this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}`);
756
- this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
757
- this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
758
- res.status(500).send(`Error uploading or installing plugin package ${filename}`);
759
- }
760
- }
761
- else {
762
- res.send(`File ${filename} uploaded successfully`);
747
+ this.server.request({
748
+ type: 'manager_run',
749
+ src: 'frontend',
750
+ dst: 'manager',
751
+ params: {
752
+ name: 'SpawnCommand',
753
+ workerData: {
754
+ threadName: 'SpawnCommand',
755
+ command: 'npm',
756
+ args: ['install', '-g', filePath, '--omit=dev', '--verbose'],
757
+ packageCommand: 'install',
758
+ packageName: filename,
759
+ },
760
+ },
761
+ });
763
762
  }
763
+ res.send(`File ${filename} uploaded successfully`);
764
764
  }
765
765
  catch (err) {
766
766
  this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}:`, err);
767
- this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
767
+ this.wssSendCloseSnackbarMessage(`Installing package ${filename}...`);
768
768
  this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
769
769
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
770
770
  }
@@ -333,7 +333,7 @@ export class MatterNode extends EventEmitter {
333
333
  await storageContext.set('productId', productId);
334
334
  await storageContext.set('productName', productName.slice(0, 32));
335
335
  await storageContext.set('nodeLabel', productName.slice(0, 32));
336
- await storageContext.set('productLabel', productName.slice(0, 32));
336
+ await storageContext.set('productLabel', productName.replace(vendorName, '').trim().slice(0, 32));
337
337
  await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : 'SN' + random));
338
338
  await storageContext.set('uniqueId', await storageContext.get('uniqueId', uniqueId ? uniqueId.slice(0, 32) : 'UI' + random));
339
339
  await storageContext.set('softwareVersion', isValidNumber(parseVersionString(this.matterbridge.matterbridgeVersion), 0, UINT32_MAX) ? parseVersionString(this.matterbridge.matterbridgeVersion) : 1);
@@ -16,7 +16,7 @@ import { DeviceTypeId, VendorId } from '@matter/types/datatype';
16
16
  import { BroadcastServer } from '@matterbridge/thread/server';
17
17
  import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from '@matterbridge/types';
18
18
  import { wait } from '@matterbridge/utils';
19
- import { getIntParameter, getParameter, hasParameter } from '@matterbridge/utils/cli';
19
+ import { getIntParameter, getParameter, hasAnyParameter, hasParameter } from '@matterbridge/utils/cli';
20
20
  import { copyDirectory } from '@matterbridge/utils/copy-dir';
21
21
  import { createDirectory } from '@matterbridge/utils/create-dir';
22
22
  import { formatBytes, formatPercent, formatUptime } from '@matterbridge/utils/format';
@@ -124,7 +124,7 @@ export class Matterbridge extends EventEmitter {
124
124
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
125
125
  aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
126
126
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
127
- aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
127
+ aggregatorProductName = getParameter('productName') ?? 'Matterbridge Aggregator';
128
128
  aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
129
129
  aggregatorSerialNumber = getParameter('serialNumber');
130
130
  aggregatorUniqueId = getParameter('uniqueId');
@@ -132,7 +132,7 @@ export class Matterbridge extends EventEmitter {
132
132
  controllerVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
133
133
  controllerVendorName = getParameter('vendorName') ?? 'Matterbridge';
134
134
  controllerProductId = getIntParameter('productId') ?? 0x8000;
135
- controllerProductName = getParameter('productName') ?? 'Matterbridge controller';
135
+ controllerProductName = getParameter('productName') ?? 'Matterbridge Controller';
136
136
  advertisingNodes = new Map();
137
137
  server;
138
138
  verbose = hasParameter('verbose');
@@ -251,7 +251,8 @@ export class Matterbridge extends EventEmitter {
251
251
  if (msg.result && msg.result.success && msg.result.packageCommand === 'install') {
252
252
  this.restartRequired = true;
253
253
  this.fixedRestartRequired = true;
254
- if (msg.result.packageName === 'matterbridge') {
254
+ const packageName = msg.result.packageName.replace(/@.*$/, '');
255
+ if (packageName === 'matterbridge') {
255
256
  this.log.info('Matterbridge has been updated. Full restart required.');
256
257
  if (this.restartMode !== '')
257
258
  await this.cleanup('updating...', false);
@@ -565,17 +566,11 @@ export class Matterbridge extends EventEmitter {
565
566
  this.devices.logLevel = this.log.logLevel;
566
567
  for (const plugin of this.plugins) {
567
568
  this.log.debug(`Parsing plugin ${plg}${plugin.name}${db} from path ${CYAN}${plugin.path}${db} with version ${CYAN}${plugin.version}${db} and type ${CYAN}${plugin.type}${db}.`);
568
- if (!fs.existsSync(plugin.path) &&
569
- !hasParameter('add') &&
570
- !hasParameter('remove') &&
571
- !hasParameter('enable') &&
572
- !hasParameter('disable') &&
573
- !hasParameter('reset') &&
574
- !hasParameter('factoryreset') &&
575
- !hasParameter('systemcheck')) {
569
+ if (!fs.existsSync(plugin.path) && !hasAnyParameter('add', 'remove', 'enable', 'disable', 'reset', 'factoryreset', 'systemcheck')) {
576
570
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
577
- const { spawnCommand } = await import('./spawn.js');
578
- if (await spawnCommand('npm', ['install', '-g', `${plugin.name}${plugin.version.includes('-dev-') ? '@dev' : ''}`, '--omit=dev', '--verbose'], 'install', plugin.name)) {
571
+ const { execSync } = await import('node:child_process');
572
+ const sudo = hasParameter('sudo') || (process.platform !== 'win32' && !hasParameter('docker') && !hasParameter('nosudo') && !process.env.PATH?.includes('/.nvm/versions/node/'));
573
+ if (execSync(`${sudo ? 'sudo ' : ''}npm install -g ${plugin.name}${plugin.version.includes('-dev-') ? '@dev' : ''} --omit=dev`)) {
579
574
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
580
575
  plugin.error = false;
581
576
  }
@@ -1515,7 +1510,7 @@ export class Matterbridge extends EventEmitter {
1515
1510
  await this.controllerContext.set('vendorName', this.controllerVendorName.slice(0, 32));
1516
1511
  await this.controllerContext.set('productId', this.controllerProductId);
1517
1512
  await this.controllerContext.set('productName', this.controllerProductName.slice(0, 32));
1518
- await this.controllerContext.set('productLabel', this.controllerProductName.slice(0, 32));
1513
+ await this.controllerContext.set('productLabel', this.controllerProductName.replace(this.controllerVendorName, '').trim().slice(0, 32));
1519
1514
  await this.controllerContext.set('nodeLabel', storeId.slice(0, 32));
1520
1515
  await this.controllerContext.set('serialNumber', await this.controllerContext.get('serialNumber', 'SN' + random));
1521
1516
  await this.controllerContext.set('uniqueId', await this.controllerContext.get('uniqueId', 'UI' + random));
@@ -1791,7 +1786,7 @@ export class Matterbridge extends EventEmitter {
1791
1786
  await storageContext.set('vendorName', vendorName.slice(0, 32));
1792
1787
  await storageContext.set('productId', productId);
1793
1788
  await storageContext.set('productName', productName.slice(0, 32));
1794
- await storageContext.set('productLabel', productName.slice(0, 32));
1789
+ await storageContext.set('productLabel', productName.replace(vendorName, '').trim().slice(0, 32));
1795
1790
  await storageContext.set('nodeLabel', deviceName.slice(0, 32));
1796
1791
  await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : 'SN' + random));
1797
1792
  await storageContext.set('uniqueId', await storageContext.get('uniqueId', uniqueId ? uniqueId.slice(0, 32) : 'UI' + random));
@@ -1860,7 +1855,7 @@ export class Matterbridge extends EventEmitter {
1860
1855
  vendorName: await storageContext.get('vendorName'),
1861
1856
  productId: await storageContext.get('productId'),
1862
1857
  productName: await storageContext.get('productName'),
1863
- productLabel: await storageContext.get('productName'),
1858
+ productLabel: await storageContext.get('productLabel'),
1864
1859
  serialNumber: await storageContext.get('serialNumber'),
1865
1860
  uniqueId: await storageContext.get('uniqueId'),
1866
1861
  softwareVersion: await storageContext.get('softwareVersion'),
@@ -1615,7 +1615,6 @@ declare const MatterbridgeThermostatServer_base: import("@matter/node").ClusterB
1615
1615
  }];
1616
1616
  }>, readonly [Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode]>, typeof ThermostatServer, import("@matter/node/behaviors/thermostat").ThermostatInterface>;
1617
1617
  export declare class MatterbridgeThermostatServer extends MatterbridgeThermostatServer_base {
1618
- initialize(): Promise<void>;
1619
1618
  setpointRaiseLower(request: Thermostat.SetpointRaiseLowerRequest): MaybePromise;
1620
1619
  }
1621
1620
  declare const MatterbridgePresetThermostatServer_base: import("@matter/node").ClusterBehavior.Type<import("@matter/types").ClusterComposer.WithFeatures<import("@matter/types").ClusterType.Of<{
@@ -321,17 +321,6 @@ export class MatterbridgeFanControlServer extends FanControlServer.with(FanContr
321
321
  }
322
322
  }
323
323
  export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) {
324
- async initialize() {
325
- await super.initialize();
326
- this.endpoint.construction.onSuccess(async () => {
327
- const device = this.endpoint.stateOf(MatterbridgeServer);
328
- device.log.debug(`Removing atomic commands (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
329
- await this.endpoint.setStateOf(ThermostatServer, {
330
- acceptedCommandList: [0],
331
- generatedCommandList: [],
332
- });
333
- });
334
- }
335
324
  setpointRaiseLower(request) {
336
325
  const device = this.endpoint.stateOf(MatterbridgeServer);
337
326
  device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
@@ -506,7 +506,7 @@ export class MatterbridgeEndpoint extends Endpoint {
506
506
  vendorName: vendorName.slice(0, 32),
507
507
  productName: productName.slice(0, 32),
508
508
  productUrl: this.productUrl.slice(0, 256),
509
- productLabel: productName.slice(0, 64),
509
+ productLabel: productName.replace(vendorName, '').trim().slice(0, 64),
510
510
  nodeLabel: deviceName.slice(0, 32),
511
511
  serialNumber: serialNumber.slice(0, 32),
512
512
  uniqueId: this.uniqueId.slice(0, 32),
@@ -29,6 +29,16 @@ interface PluginManagerEvents {
29
29
  configured: [name: string];
30
30
  shutdown: [name: string];
31
31
  }
32
+ type PackageJsonDependencies = Record<string, string> | string[];
33
+ type PackageJsonLike = Record<string, unknown> & {
34
+ name?: string;
35
+ dependencies?: PackageJsonDependencies;
36
+ devDependencies?: PackageJsonDependencies;
37
+ peerDependencies?: PackageJsonDependencies;
38
+ optionalDependencies?: PackageJsonDependencies;
39
+ bundledDependencies?: PackageJsonDependencies;
40
+ bundleDependencies?: PackageJsonDependencies;
41
+ };
32
42
  export declare class PluginManager extends EventEmitter<PluginManagerEvents> {
33
43
  private readonly matterbridge;
34
44
  private readonly _plugins;
@@ -36,8 +46,13 @@ export declare class PluginManager extends EventEmitter<PluginManagerEvents> {
36
46
  private readonly server;
37
47
  private readonly debug;
38
48
  private readonly verbose;
49
+ private readonly dependencyTypes;
39
50
  constructor(matterbridge: Matterbridge);
40
51
  destroy(): void;
52
+ private getDependencyNames;
53
+ private findInvalidDependencies;
54
+ private logInvalidDependencies;
55
+ checkDependencies(packageJson: PackageJsonLike): boolean;
41
56
  private msgHandler;
42
57
  get length(): number;
43
58
  get size(): number;
@@ -16,6 +16,7 @@ export class PluginManager extends EventEmitter {
16
16
  server;
17
17
  debug = hasParameter('debug') || hasParameter('verbose');
18
18
  verbose = hasParameter('verbose');
19
+ dependencyTypes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies', 'bundledDependencies', 'bundleDependencies'];
19
20
  constructor(matterbridge) {
20
21
  super();
21
22
  this.matterbridge = matterbridge;
@@ -29,6 +30,43 @@ export class PluginManager extends EventEmitter {
29
30
  this.server.off('broadcast_message', this.msgHandler.bind(this));
30
31
  this.server.close();
31
32
  }
33
+ getDependencyNames(dependencies) {
34
+ if (!dependencies)
35
+ return [];
36
+ return Array.isArray(dependencies) ? dependencies : Object.keys(dependencies);
37
+ }
38
+ findInvalidDependencies(packageJson) {
39
+ for (const dependencyType of this.dependencyTypes) {
40
+ const packages = this.getDependencyNames(packageJson[dependencyType]);
41
+ const matterbridgePackages = packages.filter((pkg) => pkg.startsWith('matterbridge'));
42
+ if (matterbridgePackages.length > 0) {
43
+ return { dependencyType, packages: matterbridgePackages, isMatterbridgePackage: true };
44
+ }
45
+ const scopedPackages = packages.filter((pkg) => pkg.startsWith('@project-chip') || pkg.startsWith('@matterbridge') || pkg.startsWith('@matter'));
46
+ if (scopedPackages.length > 0) {
47
+ return { dependencyType, packages: scopedPackages, isMatterbridgePackage: false };
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ logInvalidDependencies(packageJson, invalidDependencies, pluginName) {
53
+ if (invalidDependencies.isMatterbridgePackage) {
54
+ this.log.error(`Found matterbridge package in the plugin${pluginName ? ` ${plg}${pluginName}${er}` : ''} ${invalidDependencies.dependencyType}.`);
55
+ }
56
+ else {
57
+ this.log.error(`Found invalid packages "${invalidDependencies.packages.join(', ')}" in plugin${pluginName ? ` ${plg}${pluginName}${er}` : ''} ${invalidDependencies.dependencyType}.`);
58
+ }
59
+ this.log.error(`Please open an issue on the plugin repository to remove them.`);
60
+ this.server.request({
61
+ type: 'frontend_snackbarmessage',
62
+ src: 'plugins',
63
+ dst: 'frontend',
64
+ params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
65
+ });
66
+ }
67
+ checkDependencies(packageJson) {
68
+ return this.findInvalidDependencies(packageJson) === null;
69
+ }
32
70
  async msgHandler(msg) {
33
71
  if (this.server.isWorkerRequest(msg)) {
34
72
  if (this.verbose)
@@ -237,18 +275,20 @@ export class PluginManager extends EventEmitter {
237
275
  switch (msg.type) {
238
276
  case 'manager_spawn_response':
239
277
  if (msg.result && msg.result.packageCommand === 'install') {
278
+ if (msg.result.packageName.endsWith('.tgz'))
279
+ return;
240
280
  if (msg.result.success) {
241
- msg.result.packageName = msg.result.packageName.replace(/@.*$/, '');
242
- if (msg.result.packageName !== 'matterbridge') {
243
- if (!this.has(msg.result.packageName))
244
- await this.add(msg.result.packageName);
245
- const plugin = this.get(msg.result.packageName);
281
+ const packageName = msg.result.packageName.replace(/@.*$/, '');
282
+ if (packageName !== 'matterbridge') {
283
+ if (!this.has(packageName))
284
+ await this.add(packageName);
285
+ const plugin = this.get(packageName);
246
286
  if (plugin && !plugin.loaded) {
247
287
  await this.load(plugin);
248
288
  this.server.request({ type: 'frontend_refreshrequired', src: 'plugins', dst: 'frontend', params: { changed: 'plugins' } });
249
289
  }
250
290
  }
251
- this.log.info(`Installed plugin ${plg}${msg.result.packageName}${db} successfully`);
291
+ this.log.info(`Installed plugin ${plg}${packageName}${db} successfully`);
252
292
  }
253
293
  else {
254
294
  this.log.error(`Failed to install plugin ${plg}${msg.result.packageName}${er}`);
@@ -435,82 +475,9 @@ export class PluginManager extends EventEmitter {
435
475
  this.log.error(`Plugin at ${packageJsonPath} has no main entrypoint in package.json`);
436
476
  return null;
437
477
  }
438
- const checkForProjectChipPackages = (dependencies) => {
439
- return Object.keys(dependencies).filter((pkg) => pkg.startsWith('@project-chip') || pkg.startsWith('@matter'));
440
- };
441
- const projectChipDependencies = checkForProjectChipPackages(packageJson.dependencies || {});
442
- if (projectChipDependencies.length > 0) {
443
- this.log.error(`Found @project-chip packages "${projectChipDependencies.join(', ')}" in plugin dependencies.`);
444
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
445
- this.server.request({
446
- type: 'frontend_snackbarmessage',
447
- src: 'plugins',
448
- dst: 'frontend',
449
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
450
- });
451
- return null;
452
- }
453
- const projectChipDevDependencies = checkForProjectChipPackages(packageJson.devDependencies || {});
454
- if (projectChipDevDependencies.length > 0) {
455
- this.log.error(`Found @project-chip packages "${projectChipDevDependencies.join(', ')}" in plugin devDependencies.`);
456
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
457
- this.server.request({
458
- type: 'frontend_snackbarmessage',
459
- src: 'plugins',
460
- dst: 'frontend',
461
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
462
- });
463
- return null;
464
- }
465
- const projectChipPeerDependencies = checkForProjectChipPackages(packageJson.peerDependencies || {});
466
- if (projectChipPeerDependencies.length > 0) {
467
- this.log.error(`Found @project-chip packages "${projectChipPeerDependencies.join(', ')}" in plugin peerDependencies.`);
468
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
469
- this.server.request({
470
- type: 'frontend_snackbarmessage',
471
- src: 'plugins',
472
- dst: 'frontend',
473
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
474
- });
475
- return null;
476
- }
477
- const checkForMatterbridgePackage = (dependencies) => {
478
- return Object.keys(dependencies).filter((pkg) => pkg === 'matterbridge');
479
- };
480
- const matterbridgeDependencies = checkForMatterbridgePackage(packageJson.dependencies || {});
481
- if (matterbridgeDependencies.length > 0) {
482
- this.log.error(`Found matterbridge package in the plugin dependencies.`);
483
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
484
- this.server.request({
485
- type: 'frontend_snackbarmessage',
486
- src: 'plugins',
487
- dst: 'frontend',
488
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
489
- });
490
- return null;
491
- }
492
- const matterbridgeDevDependencies = checkForMatterbridgePackage(packageJson.devDependencies || {});
493
- if (matterbridgeDevDependencies.length > 0) {
494
- this.log.error(`Found matterbridge package in the plugin devDependencies.`);
495
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
496
- this.server.request({
497
- type: 'frontend_snackbarmessage',
498
- src: 'plugins',
499
- dst: 'frontend',
500
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
501
- });
502
- return null;
503
- }
504
- const matterbridgePeerDependencies = checkForMatterbridgePackage(packageJson.peerDependencies || {});
505
- if (matterbridgePeerDependencies.length > 0) {
506
- this.log.error(`Found matterbridge package in the plugin peerDependencies.`);
507
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
508
- this.server.request({
509
- type: 'frontend_snackbarmessage',
510
- src: 'plugins',
511
- dst: 'frontend',
512
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
513
- });
478
+ const invalidDependencies = this.findInvalidDependencies(packageJson);
479
+ if (invalidDependencies) {
480
+ this.logInvalidDependencies(packageJson, invalidDependencies);
514
481
  return null;
515
482
  }
516
483
  this.log.debug(`Resolved plugin path ${plg}${nameOrPath}${db}: ${packageJsonPath}`);
@@ -667,82 +634,9 @@ export class PluginManager extends EventEmitter {
667
634
  plugin.funding = this.getFunding(packageJson);
668
635
  if (!plugin.type)
669
636
  this.log.warn(`Plugin ${plg}${plugin.name}${wr} has no type`);
670
- const checkForProjectChipPackages = (dependencies) => {
671
- return Object.keys(dependencies).filter((pkg) => pkg.startsWith('@project-chip') || pkg.startsWith('@matter'));
672
- };
673
- const projectChipDependencies = checkForProjectChipPackages(packageJson.dependencies || {});
674
- if (projectChipDependencies.length > 0) {
675
- this.log.error(`Found @project-chip packages "${projectChipDependencies.join(', ')}" in plugin ${plg}${plugin.name}${er} dependencies.`);
676
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
677
- this.server.request({
678
- type: 'frontend_snackbarmessage',
679
- src: 'plugins',
680
- dst: 'frontend',
681
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
682
- });
683
- return null;
684
- }
685
- const projectChipDevDependencies = checkForProjectChipPackages(packageJson.devDependencies || {});
686
- if (projectChipDevDependencies.length > 0) {
687
- this.log.error(`Found @project-chip packages "${projectChipDevDependencies.join(', ')}" in plugin ${plg}${plugin.name}${er} devDependencies.`);
688
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
689
- this.server.request({
690
- type: 'frontend_snackbarmessage',
691
- src: 'plugins',
692
- dst: 'frontend',
693
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
694
- });
695
- return null;
696
- }
697
- const projectChipPeerDependencies = checkForProjectChipPackages(packageJson.peerDependencies || {});
698
- if (projectChipPeerDependencies.length > 0) {
699
- this.log.error(`Found @project-chip packages "${projectChipPeerDependencies.join(', ')}" in plugin ${plg}${plugin.name}${er} peerDependencies.`);
700
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
701
- this.server.request({
702
- type: 'frontend_snackbarmessage',
703
- src: 'plugins',
704
- dst: 'frontend',
705
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
706
- });
707
- return null;
708
- }
709
- const checkForMatterbridgePackage = (dependencies) => {
710
- return Object.keys(dependencies).filter((pkg) => pkg === 'matterbridge');
711
- };
712
- const matterbridgeDependencies = checkForMatterbridgePackage(packageJson.dependencies || {});
713
- if (matterbridgeDependencies.length > 0) {
714
- this.log.error(`Found matterbridge package in the plugin ${plg}${plugin.name}${er} dependencies.`);
715
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
716
- this.server.request({
717
- type: 'frontend_snackbarmessage',
718
- src: 'plugins',
719
- dst: 'frontend',
720
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
721
- });
722
- return null;
723
- }
724
- const matterbridgeDevDependencies = checkForMatterbridgePackage(packageJson.devDependencies || {});
725
- if (matterbridgeDevDependencies.length > 0) {
726
- this.log.error(`Found matterbridge package in the plugin ${plg}${plugin.name}${er} devDependencies.`);
727
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
728
- this.server.request({
729
- type: 'frontend_snackbarmessage',
730
- src: 'plugins',
731
- dst: 'frontend',
732
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
733
- });
734
- return null;
735
- }
736
- const matterbridgePeerDependencies = checkForMatterbridgePackage(packageJson.peerDependencies || {});
737
- if (matterbridgePeerDependencies.length > 0) {
738
- this.log.error(`Found matterbridge package in the plugin ${plg}${plugin.name}${er} peerDependencies.`);
739
- this.log.error(`Please open an issue on the plugin repository to remove them.`);
740
- this.server.request({
741
- type: 'frontend_snackbarmessage',
742
- src: 'plugins',
743
- dst: 'frontend',
744
- params: { message: `Found not allowed package in plugin ${packageJson.name} package.json`, timeout: 30, severity: 'error' },
745
- });
637
+ const invalidDependencies = this.findInvalidDependencies(packageJson);
638
+ if (invalidDependencies) {
639
+ this.logInvalidDependencies(packageJson, invalidDependencies, plugin.name);
746
640
  return null;
747
641
  }
748
642
  return packageJson;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.6.1-dev-20260311-7da5ff8",
3
+ "version": "3.6.1-dev-20260312-a699c0e",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -121,11 +121,11 @@
121
121
  "CHANGELOG.md"
122
122
  ],
123
123
  "dependencies": {
124
- "@matter/main": "0.17.0-alpha.0-20260309-33bcd9eab",
125
- "@matterbridge/dgram": "3.6.1-dev-20260311-7da5ff8",
126
- "@matterbridge/thread": "3.6.1-dev-20260311-7da5ff8",
127
- "@matterbridge/types": "3.6.1-dev-20260311-7da5ff8",
128
- "@matterbridge/utils": "3.6.1-dev-20260311-7da5ff8",
124
+ "@matter/main": "0.17.0-alpha.0-20260311-3dbb8a732",
125
+ "@matterbridge/dgram": "3.6.1-dev-20260312-a699c0e",
126
+ "@matterbridge/thread": "3.6.1-dev-20260312-a699c0e",
127
+ "@matterbridge/types": "3.6.1-dev-20260312-a699c0e",
128
+ "@matterbridge/utils": "3.6.1-dev-20260312-a699c0e",
129
129
  "archiver": "7.0.1",
130
130
  "express": "5.2.1",
131
131
  "glob": "13.0.6",
package/dist/spawn.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function spawnCommand(command: string, args: string[], packageCommand?: 'install' | 'uninstall', packageName?: string): Promise<boolean>;
package/dist/spawn.js DELETED
@@ -1,96 +0,0 @@
1
- import { BroadcastServer } from '@matterbridge/thread/server';
2
- import { hasParameter } from '@matterbridge/utils/cli';
3
- import { AnsiLogger } from 'node-ansi-logger';
4
- export async function spawnCommand(command, args, packageCommand, packageName) {
5
- const { spawn } = await import('node:child_process');
6
- const debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-spawn') || hasParameter('verbose-spawn');
7
- const verbose = hasParameter('verbose') || hasParameter('verbose-spawn');
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
- try {
12
- server.request({ type: 'frontend_logmessage', src: 'spawn', dst: 'frontend', params: { level: 'spawn', time: log.now(), name, message } });
13
- }
14
- catch (err) {
15
- log.debug(`Failed to send log message to frontend: ${err instanceof Error ? err.message : String(err)}`);
16
- }
17
- };
18
- if (verbose)
19
- log.debug(`Spawning command: ${command} with ${args.join(' ')} ${packageCommand} ${packageName}`);
20
- const cmdLine = command + ' ' + args.join(' ');
21
- if (process.platform === 'win32' && command === 'npm') {
22
- const argstring = 'npm ' + args.join(' ');
23
- args.splice(0, args.length, '/c', argstring);
24
- command = 'cmd.exe';
25
- }
26
- if (hasParameter('sudo') ||
27
- (process.platform !== 'win32' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo') && !process.env.PATH?.includes('/.nvm/versions/node/'))) {
28
- args.unshift(command);
29
- command = 'sudo';
30
- }
31
- log.debug(`Spawn command ${command} with ${args.join(' ')}`);
32
- const success = await new Promise((resolve) => {
33
- if (packageCommand === 'install')
34
- sendLog('Matterbridge:spawn-init', `Installing ${packageName}`);
35
- else if (packageCommand === 'uninstall')
36
- sendLog('Matterbridge:spawn-init', `Uninstalling ${packageName}`);
37
- const childProcess = spawn(command, args, {
38
- stdio: ['inherit', 'pipe', 'pipe'],
39
- });
40
- childProcess.on('error', (err) => {
41
- log.error(`Failed to start child process "${cmdLine}": ${err.message}`);
42
- sendLog('Matterbridge:spawn-exit-error', 'Spawn process error');
43
- resolve(false);
44
- });
45
- childProcess.on('close', (code, signal) => {
46
- if (code === 0) {
47
- log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
48
- sendLog('Matterbridge:spawn-exit-success', 'Child process closed');
49
- resolve(true);
50
- }
51
- else {
52
- log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
53
- sendLog('Matterbridge:spawn-exit-error', 'Child process closed');
54
- resolve(false);
55
- }
56
- });
57
- childProcess.on('exit', (code, signal) => {
58
- if (code === 0) {
59
- log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
60
- sendLog('Matterbridge:spawn-exit-success', 'Child process exited');
61
- resolve(true);
62
- }
63
- else {
64
- log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
65
- sendLog('Matterbridge:spawn-exit-error', 'Child process exited');
66
- resolve(false);
67
- }
68
- });
69
- childProcess.on('disconnect', () => {
70
- log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
71
- resolve(true);
72
- });
73
- if (childProcess.stdout) {
74
- childProcess.stdout.on('data', (data) => {
75
- const message = data.toString().trim();
76
- const lines = message.split('\n');
77
- for (const line of lines) {
78
- log.debug(`Spawn output (stdout): ${line}`);
79
- sendLog('Matterbridge:spawn', line);
80
- }
81
- });
82
- }
83
- if (childProcess.stderr) {
84
- childProcess.stderr.on('data', (data) => {
85
- const message = data.toString().trim();
86
- const lines = message.split('\n');
87
- for (const line of lines) {
88
- log.debug(`Spawn verbose (stderr): ${line}`);
89
- sendLog('Matterbridge:spawn', line);
90
- }
91
- });
92
- }
93
- });
94
- server.close();
95
- return success;
96
- }