@matterbridge/core 3.6.1-dev-20260309-6341dee → 3.6.1-dev-20260310-7887380

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 CHANGED
@@ -175,6 +175,7 @@ function help() {
175
175
  --delay [seconds]: set a delay in seconds before starting Matterbridge in the first 5 minutes from a reboot (default 120)
176
176
  --fixed_delay [seconds]: set a fixed delay in seconds before starting Matterbridge (default 120)
177
177
  --no-ansi: disable ANSI color output in the logs
178
+ --no-reset-sessions: disable resetting of sessions and resumption records on shutdown
178
179
  `);
179
180
  process.exit(0);
180
181
  }
@@ -9,7 +9,7 @@ const ClosureControlSchema = ClusterElement({
9
9
  id: ClosureControl.Cluster.id,
10
10
  name: ClosureControl.Cluster.name,
11
11
  classification: 'application',
12
- }, AttributeElement({ id: 0xfffd, name: 'ClusterRevision', type: 'ClusterRevision', conformance: 'M', default: ClosureControl.Base.revision ?? 1 }), AttributeElement({ id: 0xfffc, name: 'FeatureMap', type: 'FeatureMap', conformance: 'M' }, FieldElement({ name: 'POS', constraint: '0', title: 'Positioning' }), FieldElement({ name: 'ML', constraint: '1', title: 'MotionLatching' }), FieldElement({ name: 'INS', constraint: '2', title: 'Instantaneous' }), FieldElement({ name: 'SPD', constraint: '3', title: 'Speed' }), FieldElement({ name: 'VNT', constraint: '4', title: 'Ventilation' }), FieldElement({ name: 'PED', constraint: '5', title: 'Pedestrian' }), FieldElement({ name: 'CAL', constraint: '6', title: 'Calibration' }), FieldElement({ name: 'PRT', constraint: '7', title: 'Protection' }), FieldElement({ name: 'MAN', constraint: '8', title: 'ManuallyOperable' })), AttributeElement({ name: 'CountdownTime', id: 0x0000, type: 'uint32', conformance: 'POS', default: null, quality: 'X' }), AttributeElement({ name: 'MainState', id: 0x0001, type: 'MainStateEnum', conformance: 'M', constraint: 'desc' }), AttributeElement({ name: 'CurrentErrorList', id: 0x0002, type: 'list', conformance: 'M', default: [] }, FieldElement({ name: 'entry', type: 'ClosureErrorEnum' })), AttributeElement({ name: 'OverallCurrentState', id: 0x0003, type: 'OverallCurrentStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'OverallTargetState', id: 0x0004, type: 'OverallTargetStateStruct', conformance: 'M', default: null, quality: 'X' }), CommandElement({ name: 'Stop', id: 0x0000, conformance: 'M', direction: 'request', response: 'status' }), CommandElement({ name: 'MoveTo', id: 0x0001, conformance: 'M', direction: 'request', response: 'status' }, FieldElement({ name: 'Position', id: 0, type: 'TargetPositionEnum', conformance: 'O' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), EventElement({ name: 'OperationalError', id: 0x0000, conformance: 'M', priority: 'critical' }, FieldElement({ name: 'ErrorState', id: 0, type: 'list', conformance: 'M', constraint: 'max 10' }, FieldElement({ name: 'entry', type: 'ClosureErrorEnum' }))), EventElement({ name: 'MovementCompleted', id: 0x0001, conformance: 'M', priority: 'info' }), EventElement({ name: 'SecureStateChanged', id: 0x0003, conformance: 'M', priority: 'info' }, FieldElement({ name: 'SecureValue', id: 0, type: 'bool', conformance: 'M' })), DatatypeElement({ name: 'ClosureErrorEnum', type: 'enum8' }, FieldElement({ name: 'PhysicallyBlocked', id: 0, conformance: 'M' }), FieldElement({ name: 'BlockedBySensor', id: 1, conformance: 'M' }), FieldElement({ name: 'TemperatureLimited', id: 2, conformance: 'M' }), FieldElement({ name: 'MaintenanceRequired', id: 3, conformance: 'M' }), FieldElement({ name: 'InternalInterference', id: 4, conformance: 'M' })), DatatypeElement({ name: 'CurrentPositionEnum', type: 'enum8' }, FieldElement({ name: 'FullyClosed', id: 0, conformance: 'M' }), FieldElement({ name: 'FullyOpened', id: 1, conformance: 'M' }), FieldElement({ name: 'PartiallyOpened', id: 2, conformance: 'M' }), FieldElement({ name: 'OpenedForPedestrian', id: 3, conformance: 'M' }), FieldElement({ name: 'OpenedForVentilation', id: 4, conformance: 'M' }), FieldElement({ name: 'OpenedAtSignature', id: 5, conformance: 'M' })), DatatypeElement({ name: 'MainStateEnum', type: 'enum8' }, FieldElement({ name: 'Stopped', id: 0, conformance: 'M' }), FieldElement({ name: 'Moving', id: 1, conformance: 'M' }), FieldElement({ name: 'WaitingForMotion', id: 2, conformance: 'M' }), FieldElement({ name: 'Error', id: 3, conformance: 'M' }), FieldElement({ name: 'Calibrating', id: 4, conformance: 'M' }), FieldElement({ name: 'Protected', id: 5, conformance: 'M' }), FieldElement({ name: 'Disengaged', id: 6, conformance: 'M' }), FieldElement({ name: 'SetupRequired', id: 7, conformance: 'M' })), DatatypeElement({ name: 'TargetPositionEnum', type: 'enum8' }, FieldElement({ name: 'MoveToFullyClosed', id: 0, conformance: 'M' }), FieldElement({ name: 'MoveToFullyOpen', id: 1, conformance: 'M' }), FieldElement({ name: 'MoveToPedestrianPosition', id: 2, conformance: 'M' }), FieldElement({ name: 'MoveToVentilationPosition', id: 3, conformance: 'M' }), FieldElement({ name: 'MoveToSignaturePosition', id: 4, conformance: 'M' })), DatatypeElement({ name: 'OverallCurrentStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'CurrentPositionEnum', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' }), FieldElement({ name: 'SecureState', id: 3, type: 'bool', conformance: 'O', default: null, quality: 'X' })), DatatypeElement({ name: 'OverallTargetStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'TargetPositionEnum', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })));
12
+ }, AttributeElement({ id: 0xfffd, name: 'ClusterRevision', type: 'ClusterRevision', conformance: 'M', default: ClosureControl.Base.revision }), AttributeElement({ id: 0xfffc, name: 'FeatureMap', type: 'FeatureMap', conformance: 'M' }, FieldElement({ name: 'POS', constraint: '0', title: 'Positioning' }), FieldElement({ name: 'ML', constraint: '1', title: 'MotionLatching' }), FieldElement({ name: 'INS', constraint: '2', title: 'Instantaneous' }), FieldElement({ name: 'SPD', constraint: '3', title: 'Speed' }), FieldElement({ name: 'VNT', constraint: '4', title: 'Ventilation' }), FieldElement({ name: 'PED', constraint: '5', title: 'Pedestrian' }), FieldElement({ name: 'CAL', constraint: '6', title: 'Calibration' }), FieldElement({ name: 'PRT', constraint: '7', title: 'Protection' }), FieldElement({ name: 'MAN', constraint: '8', title: 'ManuallyOperable' })), AttributeElement({ name: 'CountdownTime', id: 0x0000, type: 'uint32', conformance: 'POS', default: null, quality: 'X' }), AttributeElement({ name: 'MainState', id: 0x0001, type: 'MainStateEnum', conformance: 'M', constraint: 'desc' }), AttributeElement({ name: 'CurrentErrorList', id: 0x0002, type: 'list', conformance: 'M', default: [] }, FieldElement({ name: 'entry', type: 'ClosureErrorEnum' })), AttributeElement({ name: 'OverallCurrentState', id: 0x0003, type: 'OverallCurrentStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'OverallTargetState', id: 0x0004, type: 'OverallTargetStateStruct', conformance: 'M', default: null, quality: 'X' }), CommandElement({ name: 'Stop', id: 0x0000, conformance: 'M', direction: 'request', response: 'status' }), CommandElement({ name: 'MoveTo', id: 0x0001, conformance: 'M', direction: 'request', response: 'status' }, FieldElement({ name: 'Position', id: 0, type: 'TargetPositionEnum', conformance: 'O' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), EventElement({ name: 'OperationalError', id: 0x0000, conformance: 'M', priority: 'critical' }, FieldElement({ name: 'ErrorState', id: 0, type: 'list', conformance: 'M', constraint: 'max 10' }, FieldElement({ name: 'entry', type: 'ClosureErrorEnum' }))), EventElement({ name: 'MovementCompleted', id: 0x0001, conformance: 'M', priority: 'info' }), EventElement({ name: 'SecureStateChanged', id: 0x0003, conformance: 'M', priority: 'info' }, FieldElement({ name: 'SecureValue', id: 0, type: 'bool', conformance: 'M' })), DatatypeElement({ name: 'ClosureErrorEnum', type: 'enum8' }, FieldElement({ name: 'PhysicallyBlocked', id: 0, conformance: 'M' }), FieldElement({ name: 'BlockedBySensor', id: 1, conformance: 'M' }), FieldElement({ name: 'TemperatureLimited', id: 2, conformance: 'M' }), FieldElement({ name: 'MaintenanceRequired', id: 3, conformance: 'M' }), FieldElement({ name: 'InternalInterference', id: 4, conformance: 'M' })), DatatypeElement({ name: 'CurrentPositionEnum', type: 'enum8' }, FieldElement({ name: 'FullyClosed', id: 0, conformance: 'M' }), FieldElement({ name: 'FullyOpened', id: 1, conformance: 'M' }), FieldElement({ name: 'PartiallyOpened', id: 2, conformance: 'M' }), FieldElement({ name: 'OpenedForPedestrian', id: 3, conformance: 'M' }), FieldElement({ name: 'OpenedForVentilation', id: 4, conformance: 'M' }), FieldElement({ name: 'OpenedAtSignature', id: 5, conformance: 'M' })), DatatypeElement({ name: 'MainStateEnum', type: 'enum8' }, FieldElement({ name: 'Stopped', id: 0, conformance: 'M' }), FieldElement({ name: 'Moving', id: 1, conformance: 'M' }), FieldElement({ name: 'WaitingForMotion', id: 2, conformance: 'M' }), FieldElement({ name: 'Error', id: 3, conformance: 'M' }), FieldElement({ name: 'Calibrating', id: 4, conformance: 'M' }), FieldElement({ name: 'Protected', id: 5, conformance: 'M' }), FieldElement({ name: 'Disengaged', id: 6, conformance: 'M' }), FieldElement({ name: 'SetupRequired', id: 7, conformance: 'M' })), DatatypeElement({ name: 'TargetPositionEnum', type: 'enum8' }, FieldElement({ name: 'MoveToFullyClosed', id: 0, conformance: 'M' }), FieldElement({ name: 'MoveToFullyOpen', id: 1, conformance: 'M' }), FieldElement({ name: 'MoveToPedestrianPosition', id: 2, conformance: 'M' }), FieldElement({ name: 'MoveToVentilationPosition', id: 3, conformance: 'M' }), FieldElement({ name: 'MoveToSignaturePosition', id: 4, conformance: 'M' })), DatatypeElement({ name: 'OverallCurrentStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'CurrentPositionEnum', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' }), FieldElement({ name: 'SecureState', id: 3, type: 'bool', conformance: 'O', default: null, quality: 'X' })), DatatypeElement({ name: 'OverallTargetStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'TargetPositionEnum', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })));
13
13
  const ClosureControlBehavior = ClusterBehavior.for(ClusterType(ClosureControl.Base), new ClusterModel(ClosureControlSchema));
14
14
  export class ClosureControlServer extends ClosureControlBehavior.with(ClosureControl.Feature.Positioning) {
15
15
  moveTo = (request) => {
@@ -9,7 +9,7 @@ const ClosureDimensionSchema = ClusterElement({
9
9
  id: ClosureDimension.Cluster.id,
10
10
  name: ClosureDimension.Cluster.name,
11
11
  classification: 'application',
12
- }, AttributeElement({ id: 0xfffd, name: 'ClusterRevision', type: 'ClusterRevision', conformance: 'M', default: ClosureDimension.Base.revision ?? 1 }), AttributeElement({ id: 0xfffc, name: 'FeatureMap', type: 'FeatureMap', conformance: 'M' }, FieldElement({ name: 'POS', constraint: '0', title: 'Positioning' }), FieldElement({ name: 'ML', constraint: '1', title: 'MotionLatching' }), FieldElement({ name: 'UNI', constraint: '2', title: 'Unit' }), FieldElement({ name: 'LIM', constraint: '3', title: 'Limitation' }), FieldElement({ name: 'SPD', constraint: '4', title: 'Speed' }), FieldElement({ name: 'TRN', constraint: '5', title: 'Translation' }), FieldElement({ name: 'ROT', constraint: '6', title: 'Rotation' }), FieldElement({ name: 'MOD', constraint: '7', title: 'Modulation' })), AttributeElement({ name: 'CurrentState', id: 0x0000, type: 'DimensionStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'TargetState', id: 0x0001, type: 'DimensionStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'Resolution', id: 0x0002, type: 'percent100ths', conformance: 'POS', default: 1 }), AttributeElement({ name: 'StepValue', id: 0x0003, type: 'percent100ths', conformance: 'POS', default: 1 }), CommandElement({ name: 'SetTarget', id: 0x0000, conformance: 'M', direction: 'request', response: 'status' }, FieldElement({ name: 'Position', id: 0, type: 'percent100ths', conformance: 'O' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), CommandElement({ name: 'Step', id: 0x0001, conformance: 'POS', direction: 'request', response: 'status' }, FieldElement({ name: 'Direction', id: 0, type: 'StepDirectionEnum', conformance: 'M' }), FieldElement({ name: 'NumberOfSteps', id: 1, type: 'uint16', conformance: 'M', constraint: { min: 1 } }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), DatatypeElement({ name: 'StepDirectionEnum', type: 'enum8' }, FieldElement({ name: 'Decrease', id: 0, conformance: 'M' }), FieldElement({ name: 'Increase', id: 1, conformance: 'M' })), DatatypeElement({ name: 'DimensionStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'percent100ths', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })));
12
+ }, AttributeElement({ id: 0xfffd, name: 'ClusterRevision', type: 'ClusterRevision', conformance: 'M', default: ClosureDimension.Base.revision }), AttributeElement({ id: 0xfffc, name: 'FeatureMap', type: 'FeatureMap', conformance: 'M' }, FieldElement({ name: 'POS', constraint: '0', title: 'Positioning' }), FieldElement({ name: 'ML', constraint: '1', title: 'MotionLatching' }), FieldElement({ name: 'UNI', constraint: '2', title: 'Unit' }), FieldElement({ name: 'LIM', constraint: '3', title: 'Limitation' }), FieldElement({ name: 'SPD', constraint: '4', title: 'Speed' }), FieldElement({ name: 'TRN', constraint: '5', title: 'Translation' }), FieldElement({ name: 'ROT', constraint: '6', title: 'Rotation' }), FieldElement({ name: 'MOD', constraint: '7', title: 'Modulation' })), AttributeElement({ name: 'CurrentState', id: 0x0000, type: 'DimensionStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'TargetState', id: 0x0001, type: 'DimensionStateStruct', conformance: 'M', default: null, quality: 'X' }), AttributeElement({ name: 'Resolution', id: 0x0002, type: 'percent100ths', conformance: 'POS', default: 1 }), AttributeElement({ name: 'StepValue', id: 0x0003, type: 'percent100ths', conformance: 'POS', default: 1 }), CommandElement({ name: 'SetTarget', id: 0x0000, conformance: 'M', direction: 'request', response: 'status' }, FieldElement({ name: 'Position', id: 0, type: 'percent100ths', conformance: 'O' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), CommandElement({ name: 'Step', id: 0x0001, conformance: 'POS', direction: 'request', response: 'status' }, FieldElement({ name: 'Direction', id: 0, type: 'StepDirectionEnum', conformance: 'M' }), FieldElement({ name: 'NumberOfSteps', id: 1, type: 'uint16', conformance: 'M', constraint: { min: 1 } }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })), DatatypeElement({ name: 'StepDirectionEnum', type: 'enum8' }, FieldElement({ name: 'Decrease', id: 0, conformance: 'M' }), FieldElement({ name: 'Increase', id: 1, conformance: 'M' })), DatatypeElement({ name: 'DimensionStateStruct', type: 'struct' }, FieldElement({ name: 'Position', id: 0, type: 'percent100ths', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Latch', id: 1, type: 'bool', conformance: 'O', default: null, quality: 'X' }), FieldElement({ name: 'Speed', id: 2, type: 'ThreeLevelAutoEnum', conformance: 'O' })));
13
13
  const ClosureDimensionBehavior = ClusterBehavior.for(ClusterType(ClosureDimension.Base), new ClusterModel(ClosureDimensionSchema));
14
14
  export class ClosureDimensionServer extends ClosureDimensionBehavior.with(ClosureDimension.Feature.Positioning) {
15
15
  setTarget = (request) => {
package/dist/frontend.js CHANGED
@@ -108,30 +108,30 @@ export class Frontend extends EventEmitter {
108
108
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
109
109
  }
110
110
  }
111
- if (this.server.isWorkerResponse(msg) && msg.result) {
111
+ if (this.server.isWorkerResponse(msg) && (msg.dst === 'all' || msg.dst === 'frontend')) {
112
112
  if (this.verbose)
113
113
  this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
114
114
  switch (msg.type) {
115
- case 'plugins_install':
116
- this.wssSendCloseSnackbarMessage(`Installing package ${msg.result.packageName}...`);
117
- if (msg.result.success) {
118
- this.wssSendRestartRequired(true, true);
119
- this.wssSendRefreshRequired('plugins');
120
- this.wssSendSnackbarMessage(`Installed package ${msg.result.packageName}`, 5, 'success');
121
- }
122
- else {
123
- this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not installed`, 10, 'error');
124
- }
125
- break;
126
- case 'plugins_uninstall':
127
- this.wssSendCloseSnackbarMessage(`Uninstalling package ${msg.result.packageName}...`);
128
- if (msg.result.success) {
129
- this.wssSendRestartRequired(true, true);
130
- this.wssSendRefreshRequired('plugins');
131
- this.wssSendSnackbarMessage(`Uninstalled package ${msg.result.packageName}`, 5, 'success');
115
+ case 'manager_spawn_response':
116
+ if (msg.result && msg.result.packageCommand === 'install') {
117
+ this.wssSendCloseSnackbarMessage(`Installing package ${msg.result.packageName}...`);
118
+ if (msg.result.success) {
119
+ this.wssSendRestartRequired(true, true);
120
+ this.wssSendSnackbarMessage(`Installed package ${msg.result.packageName}`, 5, 'success');
121
+ }
122
+ else {
123
+ this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not installed`, 10, 'error');
124
+ }
132
125
  }
133
- else {
134
- this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not uninstalled`, 10, 'error');
126
+ if (msg.result && msg.result.packageCommand === 'uninstall') {
127
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${msg.result.packageName}...`);
128
+ if (msg.result.success) {
129
+ this.wssSendRestartRequired(true, true);
130
+ this.wssSendSnackbarMessage(`Uninstalled package ${msg.result.packageName}`, 5, 'success');
131
+ }
132
+ else {
133
+ this.wssSendSnackbarMessage(`Package ${msg.result.packageName} not uninstalled`, 10, 'error');
134
+ }
135
135
  }
136
136
  break;
137
137
  }
@@ -1266,7 +1266,21 @@ export class Frontend extends EventEmitter {
1266
1266
  else if (data.method === '/api/install') {
1267
1267
  if (isValidString(data.params.packageName, 12) && isValidBoolean(data.params.restart)) {
1268
1268
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
1269
- this.server.request({ type: 'plugins_install', src: this.server.name, dst: 'plugins', params: { packageName: data.params.packageName } });
1269
+ this.server.request({
1270
+ type: 'manager_run',
1271
+ src: 'frontend',
1272
+ dst: 'manager',
1273
+ params: {
1274
+ name: 'SpawnCommand',
1275
+ workerData: {
1276
+ threadName: 'SpawnCommand',
1277
+ command: 'npm',
1278
+ args: ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'],
1279
+ packageCommand: 'install',
1280
+ packageName: data.params.packageName,
1281
+ },
1282
+ },
1283
+ });
1270
1284
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1271
1285
  }
1272
1286
  else {
@@ -1276,7 +1290,21 @@ export class Frontend extends EventEmitter {
1276
1290
  else if (data.method === '/api/uninstall') {
1277
1291
  if (isValidString(data.params.packageName, 12)) {
1278
1292
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1279
- this.server.request({ type: 'plugins_uninstall', src: this.server.name, dst: 'plugins', params: { packageName: data.params.packageName } });
1293
+ this.server.request({
1294
+ type: 'manager_run',
1295
+ src: 'frontend',
1296
+ dst: 'manager',
1297
+ params: {
1298
+ name: 'SpawnCommand',
1299
+ workerData: {
1300
+ threadName: 'SpawnCommand',
1301
+ command: 'npm',
1302
+ args: ['uninstall', '-g', data.params.packageName, '--omit=dev', '--verbose'],
1303
+ packageCommand: 'uninstall',
1304
+ packageName: data.params.packageName,
1305
+ },
1306
+ },
1307
+ });
1280
1308
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1281
1309
  }
1282
1310
  else {
@@ -117,7 +117,6 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
117
117
  private msgHandler;
118
118
  static loadInstance(initialize?: boolean): Promise<Matterbridge>;
119
119
  private initialize;
120
- resolveWorkerDistFilePath(fileName: string): string;
121
120
  private parseCommandLine;
122
121
  private startPlugins;
123
122
  private registerProcessHandlers;
@@ -15,6 +15,7 @@ import { PaseClient, Read, Subscribe } from '@matter/protocol';
15
15
  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
+ import { wait } from '@matterbridge/utils';
18
19
  import { getIntParameter, getParameter, hasParameter } from '@matterbridge/utils/cli';
19
20
  import { copyDirectory } from '@matterbridge/utils/copy-dir';
20
21
  import { createDirectory } from '@matterbridge/utils/create-dir';
@@ -242,6 +243,23 @@ export class Matterbridge extends EventEmitter {
242
243
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
243
244
  }
244
245
  }
246
+ if (this.server.isWorkerResponse(msg) && (msg.dst === 'all' || msg.dst === 'matterbridge')) {
247
+ if (this.verbose)
248
+ this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
249
+ switch (msg.type) {
250
+ case 'manager_spawn_response':
251
+ if (msg.result && msg.result.success && msg.result.packageCommand === 'install') {
252
+ this.restartRequired = true;
253
+ this.fixedRestartRequired = true;
254
+ if (msg.result.packageName === 'matterbridge') {
255
+ this.log.info('Matterbridge has been updated. Full restart required.');
256
+ if (this.restartMode !== '')
257
+ await this.cleanup('updating...', false);
258
+ }
259
+ }
260
+ break;
261
+ }
262
+ }
245
263
  }
246
264
  static async loadInstance(initialize = false) {
247
265
  if (!Matterbridge.instance) {
@@ -601,19 +619,6 @@ export class Matterbridge extends EventEmitter {
601
619
  this.emit('initialize_completed');
602
620
  this.initialized = true;
603
621
  }
604
- resolveWorkerDistFilePath(fileName) {
605
- const currentModuleDirectory = path.dirname(fileURLToPath(import.meta.url));
606
- const candidates = [
607
- path.join(currentModuleDirectory, fileName),
608
- path.join(currentModuleDirectory, '..', 'dist', fileName),
609
- path.join(this.rootDirectory, 'node_modules', '@matterbridge', 'thread', 'dist', fileName),
610
- ];
611
- for (const candidate of candidates) {
612
- if (fs.existsSync(candidate))
613
- return candidate;
614
- }
615
- return candidates[0];
616
- }
617
622
  async parseCommandLine() {
618
623
  if (hasParameter('list')) {
619
624
  this.log.info(`│ Registered plugins (${this.plugins.length})`);
@@ -651,6 +656,7 @@ export class Matterbridge extends EventEmitter {
651
656
  if (hasParameter('systemcheck')) {
652
657
  const { systemCheck } = await import('@matterbridge/thread');
653
658
  await systemCheck();
659
+ await wait(1000);
654
660
  this.shutdown = true;
655
661
  return;
656
662
  }
@@ -730,15 +736,15 @@ export class Matterbridge extends EventEmitter {
730
736
  return;
731
737
  }
732
738
  clearTimeout(this.systemCheckTimeout);
733
- this.systemCheckTimeout = setTimeout(async () => {
739
+ this.systemCheckTimeout = setTimeout(() => {
734
740
  this.server.request({ type: 'manager_run', src: 'matterbridge', dst: 'manager', params: { name: 'SystemCheck' } });
735
741
  }, 120 * 1000).unref();
736
742
  clearTimeout(this.checkUpdateTimeout);
737
- this.checkUpdateTimeout = setTimeout(async () => {
743
+ this.checkUpdateTimeout = setTimeout(() => {
738
744
  this.server.request({ type: 'manager_run', src: 'matterbridge', dst: 'manager', params: { name: 'CheckUpdates' } });
739
745
  }, 300 * 1000).unref();
740
746
  clearInterval(this.checkUpdateInterval);
741
- this.checkUpdateInterval = setInterval(async () => {
747
+ this.checkUpdateInterval = setInterval(() => {
742
748
  this.server.request({ type: 'manager_run', src: 'matterbridge', dst: 'manager', params: { name: 'CheckUpdates' } });
743
749
  }, 12 * 60 * 60 * 1000).unref();
744
750
  if (hasParameter('test')) {
@@ -1026,15 +1032,21 @@ export class Matterbridge extends EventEmitter {
1026
1032
  }
1027
1033
  async updateProcess() {
1028
1034
  this.log.info('Updating matterbridge...');
1029
- const { spawnCommand } = await import('./spawn.js');
1030
- if (await spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'install', 'matterbridge')) {
1031
- this.log.info('Matterbridge has been updated. Full restart required.');
1032
- }
1033
- else {
1034
- this.log.error('Error updating matterbridge.');
1035
- }
1036
- this.frontend.wssSendRestartRequired();
1037
- await this.cleanup('updating...', false);
1035
+ this.server.request({
1036
+ type: 'manager_run',
1037
+ src: 'matterbridge',
1038
+ dst: 'manager',
1039
+ params: {
1040
+ name: 'SpawnCommand',
1041
+ workerData: {
1042
+ threadName: 'SpawnCommand',
1043
+ command: 'npm',
1044
+ args: ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'],
1045
+ packageCommand: 'install',
1046
+ packageName: 'matterbridge',
1047
+ },
1048
+ },
1049
+ });
1038
1050
  }
1039
1051
  async unregisterAndShutdownProcess(timeout = 1000) {
1040
1052
  const { wait } = await import('@matterbridge/utils/wait');
@@ -1176,7 +1188,7 @@ export class Matterbridge extends EventEmitter {
1176
1188
  catch {
1177
1189
  }
1178
1190
  }
1179
- if (getParameter('reset-sessions')) {
1191
+ if (!hasParameter('no-reset-sessions')) {
1180
1192
  this.log.debug(`Cleaning matter storage context for ${GREEN}Matterbridge${db}...`);
1181
1193
  unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'sessions.resumptionRecords'), this.log);
1182
1194
  unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'root.subscriptions.subscriptions'), this.log);
@@ -6,7 +6,7 @@ import type { ApiPlugin, PlatformConfig, PlatformSchema, PluginName, StoragePlug
6
6
  import { LogLevel } from 'node-ansi-logger';
7
7
  import type { NodeStorage } from 'node-persist-manager';
8
8
  import type { Matterbridge } from './matterbridge.js';
9
- import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
9
+ import type { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
10
10
  import { type MatterbridgePlatform } from './matterbridgePlatform.js';
11
11
  export interface Plugin extends ApiPlugin {
12
12
  nodeContext?: NodeStorage;
@@ -56,8 +56,8 @@ export declare class PluginManager extends EventEmitter<PluginManagerEvents> {
56
56
  loadFromStorage(): Promise<StoragePlugin[]>;
57
57
  saveToStorage(): Promise<number>;
58
58
  resolve(nameOrPath: string): Promise<string | null>;
59
- install(packageName: string): Promise<boolean>;
60
- uninstall(packageName: string): Promise<boolean>;
59
+ install(packageName: string): void;
60
+ uninstall(packageName: string): void;
61
61
  getAuthor(packageJson: Record<string, string | number | Record<string, string | number | object>>): string;
62
62
  getHomepage(packageJson: Record<string, string | number | Record<string, string | number | object>>): string | undefined;
63
63
  getHelp(packageJson: Record<string, string | number | Record<string, string | number | object>>): string | undefined;
@@ -72,10 +72,12 @@ export class PluginManager extends EventEmitter {
72
72
  this.server.respond({ ...msg, result: { plugins: this.apiPluginArray() } });
73
73
  break;
74
74
  case 'plugins_install':
75
- this.server.respond({ ...msg, result: { packageName: msg.params.packageName, success: await this.install(msg.params.packageName) } });
75
+ this.install(msg.params.packageName);
76
+ this.server.respond({ ...msg, result: { packageName: msg.params.packageName } });
76
77
  break;
77
78
  case 'plugins_uninstall':
78
- this.server.respond({ ...msg, result: { packageName: msg.params.packageName, success: await this.uninstall(msg.params.packageName) } });
79
+ this.uninstall(msg.params.packageName);
80
+ this.server.respond({ ...msg, result: { packageName: msg.params.packageName } });
79
81
  break;
80
82
  case 'plugins_add':
81
83
  {
@@ -229,6 +231,47 @@ export class PluginManager extends EventEmitter {
229
231
  this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
230
232
  }
231
233
  }
234
+ if (this.server.isWorkerResponse(msg) && (msg.dst === 'all' || msg.dst === 'plugins')) {
235
+ if (this.verbose)
236
+ this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
237
+ switch (msg.type) {
238
+ case 'manager_spawn_response':
239
+ if (msg.result && msg.result.packageCommand === 'install') {
240
+ 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);
246
+ if (plugin && !plugin.loaded) {
247
+ await this.load(plugin);
248
+ this.server.request({ type: 'frontend_refreshrequired', src: 'plugins', dst: 'frontend', params: { changed: 'plugins' } });
249
+ }
250
+ }
251
+ this.log.info(`Installed plugin ${plg}${msg.result.packageName}${db} successfully`);
252
+ }
253
+ else {
254
+ this.log.error(`Failed to install plugin ${plg}${msg.result.packageName}${er}`);
255
+ }
256
+ }
257
+ if (msg.result && msg.result.packageCommand === 'uninstall') {
258
+ if (msg.result.success) {
259
+ if (this.has(msg.result.packageName)) {
260
+ const plugin = this.get(msg.result.packageName);
261
+ if (plugin && plugin.loaded)
262
+ await this.shutdown(plugin, 'Matterbridge is uninstalling the plugin');
263
+ await this.remove(msg.result.packageName);
264
+ this.server.request({ type: 'frontend_refreshrequired', src: 'plugins', dst: 'frontend', params: { changed: 'plugins' } });
265
+ }
266
+ this.log.info(`Uninstalled plugin ${plg}${msg.result.packageName}${db} successfully`);
267
+ }
268
+ else {
269
+ this.log.error(`Failed to uninstall plugin ${plg}${msg.result.packageName}${er}`);
270
+ }
271
+ }
272
+ break;
273
+ }
274
+ }
232
275
  }
233
276
  get length() {
234
277
  return this._plugins.size;
@@ -484,53 +527,41 @@ export class PluginManager extends EventEmitter {
484
527
  return null;
485
528
  }
486
529
  }
487
- async install(packageName) {
530
+ install(packageName) {
488
531
  this.log.debug(`Installing plugin ${plg}${packageName}${db}...`);
489
- const { spawnCommand } = await import('./spawn.js');
490
- if (await spawnCommand('npm', ['install', '-g', packageName, '--omit=dev', '--verbose'], 'install', packageName)) {
491
- this.matterbridge.restartRequired = true;
492
- this.matterbridge.fixedRestartRequired = true;
493
- packageName = packageName.replace(/@.*$/, '');
494
- if (packageName !== 'matterbridge') {
495
- if (!this.has(packageName))
496
- await this.add(packageName);
497
- const plugin = this.get(packageName);
498
- if (plugin && !plugin.loaded)
499
- await this.load(plugin);
500
- }
501
- else {
502
- if (this.matterbridge.restartMode !== '') {
503
- await this.matterbridge.shutdownProcess();
504
- }
505
- }
506
- this.log.info(`Installed plugin ${plg}${packageName}${db} successfully`);
507
- return true;
508
- }
509
- else {
510
- this.log.error(`Failed to install plugin ${plg}${packageName}${er}`);
511
- return false;
512
- }
532
+ this.server.request({
533
+ type: 'manager_run',
534
+ src: 'plugins',
535
+ dst: 'manager',
536
+ params: {
537
+ name: 'SpawnCommand',
538
+ workerData: {
539
+ threadName: 'SpawnCommand',
540
+ command: 'npm',
541
+ args: ['install', '-g', packageName, '--omit=dev', '--verbose'],
542
+ packageCommand: 'install',
543
+ packageName: packageName,
544
+ },
545
+ },
546
+ });
513
547
  }
514
- async uninstall(packageName) {
548
+ uninstall(packageName) {
515
549
  this.log.debug(`Uninstalling plugin ${plg}${packageName}${db}...`);
516
- const { spawnCommand } = await import('./spawn.js');
517
- packageName = packageName.replace(/@.*$/, '');
518
- if (packageName === 'matterbridge')
519
- return false;
520
- if (this.has(packageName)) {
521
- const plugin = this.get(packageName);
522
- if (plugin && plugin.loaded)
523
- await this.shutdown(plugin, 'Matterbridge is uninstalling the plugin');
524
- await this.remove(packageName);
525
- }
526
- if (await spawnCommand('npm', ['uninstall', '-g', packageName, '--verbose'], 'uninstall', packageName)) {
527
- this.log.info(`Uninstalled plugin ${plg}${packageName}${db} successfully`);
528
- return true;
529
- }
530
- else {
531
- this.log.error(`Failed to uninstall plugin ${plg}${packageName}${er}`);
532
- return false;
533
- }
550
+ this.server.request({
551
+ type: 'manager_run',
552
+ src: 'plugins',
553
+ dst: 'manager',
554
+ params: {
555
+ name: 'SpawnCommand',
556
+ workerData: {
557
+ threadName: 'SpawnCommand',
558
+ command: 'npm',
559
+ args: ['uninstall', '-g', packageName, '--omit=dev', '--verbose'],
560
+ packageCommand: 'uninstall',
561
+ packageName: packageName,
562
+ },
563
+ },
564
+ });
534
565
  }
535
566
  getAuthor(packageJson) {
536
567
  if (packageJson.author && typeof packageJson.author === 'string')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.6.1-dev-20260309-6341dee",
3
+ "version": "3.6.1-dev-20260310-7887380",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -122,10 +122,10 @@
122
122
  ],
123
123
  "dependencies": {
124
124
  "@matter/main": "0.16.10",
125
- "@matterbridge/dgram": "3.6.1-dev-20260309-6341dee",
126
- "@matterbridge/thread": "3.6.1-dev-20260309-6341dee",
127
- "@matterbridge/types": "3.6.1-dev-20260309-6341dee",
128
- "@matterbridge/utils": "3.6.1-dev-20260309-6341dee",
125
+ "@matterbridge/dgram": "3.6.1-dev-20260310-7887380",
126
+ "@matterbridge/thread": "3.6.1-dev-20260310-7887380",
127
+ "@matterbridge/types": "3.6.1-dev-20260310-7887380",
128
+ "@matterbridge/utils": "3.6.1-dev-20260310-7887380",
129
129
  "archiver": "7.0.1",
130
130
  "express": "5.2.1",
131
131
  "glob": "13.0.6",