@matterbridge/thread 3.9.1-dev-20260614-18d1a2e → 3.9.1-dev-20260617-b1e1b99
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/README.md +5 -3
- package/dist/broadcastServer.js +49 -52
- package/dist/checkUpdates.js +32 -17
- package/dist/dockerVersion.js +5 -2
- package/dist/spawnCommand.js +5 -2
- package/dist/threadsManager.js +9 -8
- package/dist/workerArchiveCommand.js +2 -2
- package/dist/workerCheckUpdates.js +2 -2
- package/dist/workerDockerVersion.js +2 -2
- package/dist/workerGlobalPrefix.js +2 -2
- package/dist/workerSpawnCommand.js +2 -2
- package/dist/workerSystemCheck.js +3 -3
- package/dist/workerWrapper.js +20 -18
- package/dist/zipjs.js +13 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
[](https://hub.docker.com/r/luligu/matterbridge)
|
|
7
7
|

|
|
8
8
|

|
|
9
|
-
[](https://codecov.io/gh/Luligu/matterbridge)
|
|
10
|
+
[](https://vitest.dev)
|
|
11
|
+
[](https://oxc.rs/docs/guide/usage/formatter.html)
|
|
12
|
+
[](https://oxc.rs/docs/guide/usage/linter.html)
|
|
12
13
|
[](https://www.typescriptlang.org/)
|
|
14
|
+
[](https://github.com/microsoft/typescript-go)
|
|
13
15
|
[](https://nodejs.org/)
|
|
14
16
|
[](https://matterbridge.io)
|
|
15
17
|
|
package/dist/broadcastServer.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[32m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] BroadcastServer loaded.\u001B[40;0m');
|
|
3
1
|
import { EventEmitter } from 'node:events';
|
|
4
2
|
import { BroadcastChannel } from 'node:worker_threads';
|
|
5
3
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
6
4
|
import { logError } from '@matterbridge/utils/error';
|
|
5
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
7
6
|
import { CYAN, db, debugStringify, er } from 'node-ansi-logger';
|
|
7
|
+
logModuleLoaded('BroadcastServer');
|
|
8
8
|
export class BroadcastServer extends EventEmitter {
|
|
9
9
|
name;
|
|
10
10
|
log;
|
|
@@ -88,20 +88,19 @@ export class BroadcastServer extends EventEmitter {
|
|
|
88
88
|
this.log.error('Broadcast channel is closed');
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
message
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
message.src = this.name;
|
|
91
|
+
const broadcastMessage = {
|
|
92
|
+
...message,
|
|
93
|
+
id: message.id ?? this.getUniqueId(),
|
|
94
|
+
timestamp: message.timestamp ?? Date.now(),
|
|
95
|
+
src: this.name,
|
|
96
|
+
};
|
|
98
97
|
if (this.verbose)
|
|
99
|
-
this.log.debug(`Broadcasting message: ${debugStringify(
|
|
98
|
+
this.log.debug(`Broadcasting message: ${debugStringify(broadcastMessage)}`);
|
|
100
99
|
try {
|
|
101
|
-
this.broadcastChannel.postMessage(
|
|
100
|
+
this.broadcastChannel.postMessage(broadcastMessage);
|
|
102
101
|
}
|
|
103
102
|
catch (error) {
|
|
104
|
-
logError(this.log, `Failed to broadcast message ${debugStringify(
|
|
103
|
+
logError(this.log, `Failed to broadcast message ${debugStringify(broadcastMessage)}${er}`, error);
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
request(message) {
|
|
@@ -109,24 +108,23 @@ export class BroadcastServer extends EventEmitter {
|
|
|
109
108
|
this.log.error('Broadcast channel is closed');
|
|
110
109
|
return;
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
message
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.log.error(`Invalid request message format: ${debugStringify(message)}`);
|
|
111
|
+
const requestMessage = {
|
|
112
|
+
...message,
|
|
113
|
+
id: message.id ?? this.getUniqueId(),
|
|
114
|
+
timestamp: message.timestamp ?? Date.now(),
|
|
115
|
+
src: this.name,
|
|
116
|
+
};
|
|
117
|
+
if (!this.isWorkerRequest(requestMessage)) {
|
|
118
|
+
this.log.error(`Invalid request message format: ${debugStringify(requestMessage)}`);
|
|
121
119
|
return;
|
|
122
120
|
}
|
|
123
121
|
if (this.verbose)
|
|
124
|
-
this.log.debug(`Broadcasting request message: ${debugStringify(
|
|
122
|
+
this.log.debug(`Broadcasting request message: ${debugStringify(requestMessage)}`);
|
|
125
123
|
try {
|
|
126
|
-
this.broadcastChannel.postMessage(
|
|
124
|
+
this.broadcastChannel.postMessage(requestMessage);
|
|
127
125
|
}
|
|
128
126
|
catch (error) {
|
|
129
|
-
logError(this.log, `Failed to broadcast request message ${debugStringify(
|
|
127
|
+
logError(this.log, `Failed to broadcast request message ${debugStringify(requestMessage)}${er}`, error);
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
respond(message) {
|
|
@@ -134,65 +132,64 @@ export class BroadcastServer extends EventEmitter {
|
|
|
134
132
|
this.log.error('Broadcast channel is closed');
|
|
135
133
|
return;
|
|
136
134
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
message.timestamp
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (!this.isWorkerResponse(message)) {
|
|
148
|
-
this.log.error(`Invalid response message format: ${debugStringify(message)}`);
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
const responseMessage = {
|
|
137
|
+
...message,
|
|
138
|
+
...(typeof message.timestamp === 'number' ? { elapsed: now - message.timestamp } : {}),
|
|
139
|
+
timestamp: message.timestamp ?? now,
|
|
140
|
+
dst: message.dst === this.name ? message.src : message.dst,
|
|
141
|
+
src: this.name,
|
|
142
|
+
};
|
|
143
|
+
if (!this.isWorkerResponse(responseMessage)) {
|
|
144
|
+
this.log.error(`Invalid response message format: ${debugStringify(responseMessage)}`);
|
|
149
145
|
return;
|
|
150
146
|
}
|
|
151
147
|
if (this.verbose)
|
|
152
|
-
this.log.debug(`Broadcasting response message: ${debugStringify(
|
|
148
|
+
this.log.debug(`Broadcasting response message: ${debugStringify(responseMessage)}`);
|
|
153
149
|
try {
|
|
154
|
-
this.broadcastChannel.postMessage(
|
|
150
|
+
this.broadcastChannel.postMessage(responseMessage);
|
|
155
151
|
}
|
|
156
152
|
catch (error) {
|
|
157
|
-
logError(this.log, `Failed to broadcast response message ${debugStringify(
|
|
153
|
+
logError(this.log, `Failed to broadcast response message ${debugStringify(responseMessage)}${er}`, error);
|
|
158
154
|
}
|
|
159
155
|
}
|
|
160
156
|
async fetch(message, timeout = 250) {
|
|
161
157
|
if (this.closed) {
|
|
162
158
|
return Promise.reject(new Error('Broadcast channel is closed'));
|
|
163
159
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
message.timestamp
|
|
169
|
-
|
|
160
|
+
let timeoutId;
|
|
161
|
+
const requestMessage = {
|
|
162
|
+
...message,
|
|
163
|
+
id: message.id ?? this.getUniqueId(),
|
|
164
|
+
timestamp: message.timestamp ?? Date.now(),
|
|
165
|
+
src: this.name,
|
|
166
|
+
};
|
|
170
167
|
if (this.verbose)
|
|
171
|
-
this.log.debug(`Fetching message: ${debugStringify(
|
|
168
|
+
this.log.debug(`Fetching message: ${debugStringify(requestMessage)}`);
|
|
172
169
|
return new Promise((resolve, reject) => {
|
|
173
170
|
const responseHandler = (msg) => {
|
|
174
|
-
if (this.isWorkerResponseOfType(msg,
|
|
171
|
+
if (this.isWorkerResponseOfType(msg, requestMessage.type) && msg.id === requestMessage.id) {
|
|
175
172
|
clearTimeout(timeoutId);
|
|
176
173
|
this.off('broadcast_message', responseHandler);
|
|
177
174
|
if (this.verbose)
|
|
178
175
|
this.log.debug(`Fetch response: ${debugStringify(msg)}`);
|
|
179
176
|
if ('error' in msg && typeof msg.error === 'string') {
|
|
180
|
-
reject(new Error(`Fetch received error response ${msg.error} to message type ${
|
|
177
|
+
reject(new Error(`Fetch received error response ${msg.error} to message type ${requestMessage.type} id ${requestMessage.id} from ${requestMessage.src} to ${requestMessage.dst}`));
|
|
181
178
|
}
|
|
182
179
|
else if ('result' in msg) {
|
|
183
180
|
resolve(msg);
|
|
184
181
|
}
|
|
185
182
|
else {
|
|
186
|
-
reject(new Error(`Fetch received malformed response for message type ${
|
|
183
|
+
reject(new Error(`Fetch received malformed response for message type ${requestMessage.type} id ${requestMessage.id} from ${requestMessage.src} to ${requestMessage.dst}`));
|
|
187
184
|
}
|
|
188
185
|
return;
|
|
189
186
|
}
|
|
190
187
|
};
|
|
191
188
|
this.on('broadcast_message', responseHandler);
|
|
192
|
-
this.request(
|
|
193
|
-
|
|
189
|
+
this.request(requestMessage);
|
|
190
|
+
timeoutId = setTimeout(() => {
|
|
194
191
|
this.off('broadcast_message', responseHandler);
|
|
195
|
-
reject(new Error(`Fetch timeout after ${timeout}ms for message type ${
|
|
192
|
+
reject(new Error(`Fetch timeout after ${timeout}ms for message type ${requestMessage.type} id ${requestMessage.id} from ${requestMessage.src} to ${requestMessage.dst}`));
|
|
196
193
|
}, timeout);
|
|
197
194
|
});
|
|
198
195
|
}
|
package/dist/checkUpdates.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { plg } from '@matterbridge/types';
|
|
3
|
+
import { getErrorMessage } from '@matterbridge/utils/error';
|
|
4
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
3
5
|
import { isValidString } from '@matterbridge/utils/validate';
|
|
4
6
|
import { AnsiLogger, db, debugStringify, nt, wr } from 'node-ansi-logger';
|
|
5
7
|
import { BroadcastServer } from './broadcastServer.js';
|
|
8
|
+
logModuleLoaded('CheckUpdates');
|
|
9
|
+
let refreshSettingsRequired = false;
|
|
10
|
+
let refreshPluginsRequired = false;
|
|
6
11
|
export async function checkUpdates(matterbridge) {
|
|
7
12
|
const log = new AnsiLogger({ logName: 'MatterbridgeUpdates', logTimestampFormat: 4, logLevel: matterbridge.logLevel });
|
|
8
13
|
const server = new BroadcastServer('updates', log);
|
|
@@ -19,9 +24,15 @@ export async function checkUpdates(matterbridge) {
|
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
catch (error) {
|
|
22
|
-
log.debug(`Error fetching plugins for update check: ${error
|
|
27
|
+
log.debug(`Error fetching plugins for update check: ${getErrorMessage(error)}`);
|
|
23
28
|
}
|
|
24
29
|
await Promise.all([checkUpdatePromise, latestVersionPromise, devVersionPromise, ...pluginsVersionPromises, ...pluginsDevVersionPromises]);
|
|
30
|
+
if (refreshSettingsRequired)
|
|
31
|
+
server.request({ type: 'frontend_refreshrequired', src: server.name, dst: 'frontend', params: { changed: 'settings' } });
|
|
32
|
+
if (refreshPluginsRequired)
|
|
33
|
+
server.request({ type: 'frontend_refreshrequired', src: server.name, dst: 'frontend', params: { changed: 'plugins' } });
|
|
34
|
+
refreshSettingsRequired = false;
|
|
35
|
+
refreshPluginsRequired = false;
|
|
25
36
|
server.close();
|
|
26
37
|
}
|
|
27
38
|
export async function checkUpdatesAndLog(matterbridge, log, server) {
|
|
@@ -54,7 +65,7 @@ export async function checkUpdatesAndLog(matterbridge, log, server) {
|
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
catch (error) {
|
|
57
|
-
log.debug(`Error checking GitHub ${branch} updates: ${error
|
|
68
|
+
log.debug(`Error checking GitHub ${branch} updates: ${getErrorMessage(error)}`);
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
71
|
export async function getMatterbridgeLatestVersion(matterbridge, log, server) {
|
|
@@ -62,7 +73,10 @@ export async function getMatterbridgeLatestVersion(matterbridge, log, server) {
|
|
|
62
73
|
try {
|
|
63
74
|
const version = await getNpmPackageVersion('matterbridge');
|
|
64
75
|
server.request({ type: 'matterbridge_latest_version', src: server.name, dst: 'matterbridge', params: { version } });
|
|
65
|
-
if (matterbridge.matterbridgeVersion
|
|
76
|
+
if (matterbridge.matterbridgeVersion === version) {
|
|
77
|
+
log.debug(`Matterbridge is up to date. Current version: ${matterbridge.matterbridgeVersion}. Latest version: ${version}.`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
66
80
|
log.notice(`Matterbridge is out of date. Current version: ${matterbridge.matterbridgeVersion}. Latest version: ${version}.`);
|
|
67
81
|
server.request({
|
|
68
82
|
type: 'frontend_snackbarmessage',
|
|
@@ -71,15 +85,13 @@ export async function getMatterbridgeLatestVersion(matterbridge, log, server) {
|
|
|
71
85
|
params: { message: 'Matterbridge latest update available', timeout: 0, severity: 'info' },
|
|
72
86
|
});
|
|
73
87
|
server.request({ type: 'frontend_updaterequired', src: server.name, dst: 'frontend', params: { devVersion: false } });
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
log.debug(`Matterbridge is up to date. Current version: ${matterbridge.matterbridgeVersion}. Latest version: ${version}.`);
|
|
88
|
+
refreshSettingsRequired = true;
|
|
78
89
|
}
|
|
79
90
|
return version;
|
|
80
91
|
}
|
|
81
92
|
catch (error) {
|
|
82
|
-
log.warn(`Error getting Matterbridge latest version: ${error
|
|
93
|
+
log.warn(`Error getting Matterbridge latest version: ${getErrorMessage(error)}`);
|
|
94
|
+
return undefined;
|
|
83
95
|
}
|
|
84
96
|
}
|
|
85
97
|
export async function getMatterbridgeDevVersion(matterbridge, log, server) {
|
|
@@ -96,7 +108,7 @@ export async function getMatterbridgeDevVersion(matterbridge, log, server) {
|
|
|
96
108
|
params: { message: 'Matterbridge dev update available', timeout: 0, severity: 'info' },
|
|
97
109
|
});
|
|
98
110
|
server.request({ type: 'frontend_updaterequired', src: server.name, dst: 'frontend', params: { devVersion: true } });
|
|
99
|
-
|
|
111
|
+
refreshSettingsRequired = true;
|
|
100
112
|
}
|
|
101
113
|
else if ((matterbridge.matterbridgeVersion.includes('-dev-') || matterbridge.matterbridgeVersion.includes('-git-')) && matterbridge.matterbridgeVersion === version) {
|
|
102
114
|
log.debug(`Matterbridge@dev is up to date. Current version: ${matterbridge.matterbridgeVersion}. Latest dev version: ${version}.`);
|
|
@@ -104,7 +116,8 @@ export async function getMatterbridgeDevVersion(matterbridge, log, server) {
|
|
|
104
116
|
return version;
|
|
105
117
|
}
|
|
106
118
|
catch (error) {
|
|
107
|
-
log.warn(`Error getting Matterbridge latest dev version: ${error
|
|
119
|
+
log.warn(`Error getting Matterbridge latest dev version: ${getErrorMessage(error)}`);
|
|
120
|
+
return undefined;
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
123
|
export async function getPluginLatestVersion(log, server, plugin) {
|
|
@@ -113,17 +126,18 @@ export async function getPluginLatestVersion(log, server, plugin) {
|
|
|
113
126
|
const version = await getNpmPackageVersion(plugin.name);
|
|
114
127
|
plugin.latestVersion = version;
|
|
115
128
|
server.request({ type: 'plugins_set_latest_version', src: server.name, dst: 'plugins', params: { plugin, version } });
|
|
116
|
-
if (plugin.version
|
|
117
|
-
log.
|
|
118
|
-
server.request({ type: 'frontend_refreshrequired', src: server.name, dst: 'frontend', params: { changed: 'plugins' } });
|
|
129
|
+
if (plugin.version === plugin.latestVersion) {
|
|
130
|
+
log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
119
131
|
}
|
|
120
132
|
else {
|
|
121
|
-
log.
|
|
133
|
+
log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
134
|
+
refreshPluginsRequired = true;
|
|
122
135
|
}
|
|
123
136
|
return version;
|
|
124
137
|
}
|
|
125
138
|
catch (error) {
|
|
126
|
-
log.warn(`Error getting plugin ${plg}${plugin.name}${wr} latest version: ${error
|
|
139
|
+
log.warn(`Error getting plugin ${plg}${plugin.name}${wr} latest version: ${getErrorMessage(error)}`);
|
|
140
|
+
return undefined;
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
export async function getPluginDevVersion(log, server, plugin) {
|
|
@@ -134,7 +148,7 @@ export async function getPluginDevVersion(log, server, plugin) {
|
|
|
134
148
|
server.request({ type: 'plugins_set_dev_version', src: server.name, dst: 'plugins', params: { plugin, version } });
|
|
135
149
|
if ((plugin.version.includes('-dev-') || plugin.version.includes('-git-')) && plugin.version !== plugin.devVersion) {
|
|
136
150
|
log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest dev version: ${plugin.devVersion}.`);
|
|
137
|
-
|
|
151
|
+
refreshPluginsRequired = true;
|
|
138
152
|
}
|
|
139
153
|
else if (plugin.version.includes('-dev-') && plugin.version === plugin.devVersion) {
|
|
140
154
|
log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest dev version: ${plugin.devVersion}.`);
|
|
@@ -142,6 +156,7 @@ export async function getPluginDevVersion(log, server, plugin) {
|
|
|
142
156
|
return version;
|
|
143
157
|
}
|
|
144
158
|
catch (error) {
|
|
145
|
-
log.debug(`Error getting plugin ${plg}${plugin.name}${db} latest dev version: ${error
|
|
159
|
+
log.debug(`Error getting plugin ${plg}${plugin.name}${db} latest dev version: ${getErrorMessage(error)}`);
|
|
160
|
+
return undefined;
|
|
146
161
|
}
|
|
147
162
|
}
|
package/dist/dockerVersion.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { getErrorMessage } from '@matterbridge/utils/error';
|
|
2
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
1
3
|
import { isValidString } from '@matterbridge/utils/validate';
|
|
4
|
+
logModuleLoaded('DockerVersion');
|
|
2
5
|
async function httpsGetJson(url, headers, timeoutMs) {
|
|
3
6
|
const https = await import('node:https');
|
|
4
7
|
return new Promise((resolve, reject) => {
|
|
@@ -46,13 +49,13 @@ async function httpsGetJson(url, headers, timeoutMs) {
|
|
|
46
49
|
resolve(JSON.parse(data));
|
|
47
50
|
}
|
|
48
51
|
catch (error) {
|
|
49
|
-
reject(new Error(`Failed to parse response JSON: ${error
|
|
52
|
+
reject(new Error(`Failed to parse response JSON: ${getErrorMessage(error)}`));
|
|
50
53
|
}
|
|
51
54
|
});
|
|
52
55
|
});
|
|
53
56
|
req.on('error', (error) => {
|
|
54
57
|
clearTimeout(timeoutId);
|
|
55
|
-
reject(new Error(`Request failed: ${error
|
|
58
|
+
reject(new Error(`Request failed: ${getErrorMessage(error)}`));
|
|
56
59
|
});
|
|
57
60
|
});
|
|
58
61
|
}
|
package/dist/spawnCommand.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
2
|
+
import { getErrorMessage } from '@matterbridge/utils/error';
|
|
3
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
2
4
|
import { AnsiLogger } from 'node-ansi-logger';
|
|
3
5
|
import { BroadcastServer } from './broadcastServer.js';
|
|
6
|
+
logModuleLoaded('SpawnCommand');
|
|
4
7
|
export async function spawnCommand(command, args, packageCommand, packageName) {
|
|
5
8
|
const { spawn } = await import('node:child_process');
|
|
6
9
|
const debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-spawn') || hasParameter('verbose-spawn');
|
|
@@ -12,7 +15,7 @@ export async function spawnCommand(command, args, packageCommand, packageName) {
|
|
|
12
15
|
server.request({ type: 'frontend_logmessage', src: 'spawn', dst: 'frontend', params: { level: 'spawn', time: log.now(), name, message } });
|
|
13
16
|
}
|
|
14
17
|
catch (err) {
|
|
15
|
-
log.debug(`Failed to send log message to frontend: ${
|
|
18
|
+
log.debug(`Failed to send log message to frontend: ${getErrorMessage(err)}`);
|
|
16
19
|
}
|
|
17
20
|
};
|
|
18
21
|
if (verbose)
|
|
@@ -38,7 +41,7 @@ export async function spawnCommand(command, args, packageCommand, packageName) {
|
|
|
38
41
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
39
42
|
});
|
|
40
43
|
childProcess.on('error', (err) => {
|
|
41
|
-
log.error(`Failed to start child process "${cmdLine}": ${err
|
|
44
|
+
log.error(`Failed to start child process "${cmdLine}": ${getErrorMessage(err)}`);
|
|
42
45
|
sendLog('Matterbridge:spawn-exit-error', 'Spawn process error');
|
|
43
46
|
resolve(false);
|
|
44
47
|
});
|
package/dist/threadsManager.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[32m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] ThreadsManager loaded.\u001B[40;0m');
|
|
3
1
|
import fs from 'node:fs';
|
|
4
|
-
import path
|
|
2
|
+
import path from 'node:path';
|
|
5
3
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
4
|
import { Worker } from 'node:worker_threads';
|
|
7
5
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
6
|
+
import { getErrorMessage } from '@matterbridge/utils/error';
|
|
7
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
8
8
|
import { AnsiLogger, CYAN, db, debugStringify, MAGENTA, wr } from 'node-ansi-logger';
|
|
9
9
|
import { BroadcastServer } from './broadcastServer.js';
|
|
10
|
+
logModuleLoaded('ThreadsManager');
|
|
10
11
|
export class ThreadsManager {
|
|
11
12
|
debug;
|
|
12
13
|
verbose;
|
|
@@ -51,7 +52,7 @@ export class ThreadsManager {
|
|
|
51
52
|
if (this.verbose)
|
|
52
53
|
this.log.notice(`ThreadsManager destroyed. Broadcast server closed.`);
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
+
msgHandler(msg) {
|
|
55
56
|
if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'manager')) {
|
|
56
57
|
if (this.verbose)
|
|
57
58
|
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
@@ -72,7 +73,7 @@ export class ThreadsManager {
|
|
|
72
73
|
this.server.respond({ ...msg, result: { success: true } });
|
|
73
74
|
}
|
|
74
75
|
catch (err) {
|
|
75
|
-
this.log.warn(`Failed to run thread ${CYAN}${msg.params.name}${wr}: ${err
|
|
76
|
+
this.log.warn(`Failed to run thread ${CYAN}${msg.params.name}${wr}: ${getErrorMessage(err)}`);
|
|
76
77
|
this.server.respond({ ...msg, result: { success: false } });
|
|
77
78
|
}
|
|
78
79
|
break;
|
|
@@ -87,11 +88,11 @@ export class ThreadsManager {
|
|
|
87
88
|
this.log.debug(`Thread ${thread.name} running: ${thread.worker ? 'yes' : 'no'}, lastSeen: ${thread.lastSeen ? new Date(thread.lastSeen).toISOString() : 'never'}, runs: ${thread.runCount ?? 0}, errors: ${thread.errorCount ?? 0}`);
|
|
88
89
|
}
|
|
89
90
|
for (const thread of this.threads) {
|
|
90
|
-
if (thread.worker && Date.now() - (thread.lastSeen
|
|
91
|
+
if (thread.worker && Date.now() - (thread.lastSeen ?? 0) > this.intervalMs) {
|
|
91
92
|
const msg = { type: 'ping', threadId: thread.worker.threadId, threadName: thread.name };
|
|
92
93
|
thread.worker.postMessage(msg);
|
|
93
94
|
}
|
|
94
|
-
if (thread.worker && Date.now() - (thread.lastSeen
|
|
95
|
+
if (thread.worker && Date.now() - (thread.lastSeen ?? 0) > this.intervalMs * 2) {
|
|
95
96
|
this.log.warn(`Thread ${CYAN}${thread.name}${db} has not been seen for more than ${this.intervalMs * 2} ms. It may be unresponsive.`);
|
|
96
97
|
}
|
|
97
98
|
}
|
|
@@ -177,7 +178,7 @@ export class ThreadsManager {
|
|
|
177
178
|
return candidates[0];
|
|
178
179
|
}
|
|
179
180
|
createESMWorker(name, relativePath, workerData, argv, env, execArgv, pipedOutput = false) {
|
|
180
|
-
const fileURL = pathToFileURL(resolve(relativePath));
|
|
181
|
+
const fileURL = pathToFileURL(path.resolve(relativePath));
|
|
181
182
|
const options = {
|
|
182
183
|
workerData: { ...workerData, threadName: name, debug: this.debug, verbose: this.verbose, logLevel: this.log.logLevel, tracker: this.tracker },
|
|
183
184
|
type: 'module',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] ArchiveCommand loaded.\u001B[40;0m');
|
|
3
1
|
import { isArchiveWorkerData } from '@matterbridge/types';
|
|
2
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
4
3
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
5
4
|
import { createZip, readZip, unZip } from './zipjs.js';
|
|
5
|
+
logModuleLoaded('ArchiveCommand', '\u001B[35m');
|
|
6
6
|
export default new WorkerWrapper('ArchiveCommand', async (worker) => {
|
|
7
7
|
if (!isArchiveWorkerData(worker.workerData)) {
|
|
8
8
|
worker.logger("error", `ArchiveCommand invalid parameters`);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] CheckUpdates loaded.\u001B[40;0m');
|
|
3
1
|
import { inspectError } from '@matterbridge/utils/error';
|
|
2
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
4
3
|
import { checkUpdates } from './checkUpdates.js';
|
|
5
4
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
5
|
+
logModuleLoaded('CheckUpdates', '\u001B[35m');
|
|
6
6
|
export default new WorkerWrapper('CheckUpdates', async (worker) => {
|
|
7
7
|
worker.logger("info", `Starting check updates...`);
|
|
8
8
|
let success = false;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] DockerVersion loaded.\u001B[40;0m');
|
|
3
1
|
import { readFileSync } from 'node:fs';
|
|
4
2
|
import { inspectError } from '@matterbridge/utils/error';
|
|
3
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
5
4
|
import { debugStringify } from 'node-ansi-logger';
|
|
6
5
|
import { getDockerVersion } from './dockerVersion.js';
|
|
7
6
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
7
|
+
logModuleLoaded('DockerVersion', '\u001B[35m');
|
|
8
8
|
export default new WorkerWrapper('DockerVersion', async (worker) => {
|
|
9
9
|
worker.logger("info", `Starting docker version check...`);
|
|
10
10
|
let success = false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] GlobalPrefix loaded.\u001B[40;0m');
|
|
3
1
|
import { inspectError } from '@matterbridge/utils/error';
|
|
2
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
4
3
|
import { getGlobalNodeModules } from '@matterbridge/utils/npm-prefix';
|
|
5
4
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
5
|
+
logModuleLoaded('GlobalPrefix', '\u001B[35m');
|
|
6
6
|
export default new WorkerWrapper('GlobalPrefix', async (worker) => {
|
|
7
7
|
let prefix;
|
|
8
8
|
worker.logger("info", `Starting global prefix check...`);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] SpawnCommand loaded.\u001B[40;0m');
|
|
3
1
|
import { isSpawnWorkerData } from '@matterbridge/types';
|
|
2
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
4
3
|
import { spawnCommand } from './spawnCommand.js';
|
|
5
4
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
5
|
+
logModuleLoaded('SpawnCommand', '\u001B[35m');
|
|
6
6
|
export default new WorkerWrapper('SpawnCommand', async (worker) => {
|
|
7
7
|
if (!isSpawnWorkerData(worker.workerData)) {
|
|
8
8
|
worker.logger("error", `SpawnCommand invalid parameters`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[35m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] SystemCheck loaded.\u001B[40;0m');
|
|
3
1
|
import os from 'node:os';
|
|
4
2
|
import { inspectError } from '@matterbridge/utils/error';
|
|
3
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
5
4
|
import { excludedInterfaceNamePattern } from '@matterbridge/utils/network';
|
|
6
5
|
import { WorkerWrapper } from './workerWrapper.js';
|
|
6
|
+
logModuleLoaded('SystemCheck', '\u001B[35m');
|
|
7
7
|
export default new WorkerWrapper('SystemCheck', async (worker) => {
|
|
8
8
|
worker.logger("info", `Starting system check...`);
|
|
9
9
|
let success = false;
|
|
@@ -39,7 +39,7 @@ export default new WorkerWrapper('SystemCheck', async (worker) => {
|
|
|
39
39
|
}
|
|
40
40
|
if (excludedInterfaceNamePattern.test(interfaceName))
|
|
41
41
|
continue;
|
|
42
|
-
for (const detail of interfaceDetails
|
|
42
|
+
for (const detail of interfaceDetails ?? []) {
|
|
43
43
|
if (detail.internal)
|
|
44
44
|
foundInternal = true;
|
|
45
45
|
if (!detail.internal)
|
package/dist/workerWrapper.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
if (process.argv.includes('--loader'))
|
|
2
|
-
console.log('\u001B[32m[' + new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }) + '] WorkerWrapper loaded.\u001B[40;0m');
|
|
3
1
|
import { isMainThread, parentPort, threadId, workerData } from 'node:worker_threads';
|
|
4
|
-
import { inspectError } from '@matterbridge/utils';
|
|
2
|
+
import { getErrorMessage, inspectError } from '@matterbridge/utils';
|
|
5
3
|
import { hasParameter } from '@matterbridge/utils/cli';
|
|
6
4
|
import { formatBytes } from '@matterbridge/utils/format';
|
|
5
|
+
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
7
6
|
import { AnsiLogger, debugStringify, MAGENTA } from 'node-ansi-logger';
|
|
8
7
|
import { BroadcastServer } from './broadcastServer.js';
|
|
9
8
|
import { ThreadsManager } from './threadsManager.js';
|
|
9
|
+
logModuleLoaded('WorkerWrapper');
|
|
10
10
|
export class WorkerWrapper {
|
|
11
11
|
name;
|
|
12
12
|
callback;
|
|
@@ -21,9 +21,9 @@ export class WorkerWrapper {
|
|
|
21
21
|
this.name = name;
|
|
22
22
|
this.callback = callback;
|
|
23
23
|
if (this.workerData) {
|
|
24
|
-
this.debug = this.workerData.debug
|
|
25
|
-
this.verbose = this.workerData.verbose
|
|
26
|
-
this.useTracker = this.workerData.tracker
|
|
24
|
+
this.debug = this.workerData.debug ?? this.debug;
|
|
25
|
+
this.verbose = this.workerData.verbose ?? this.verbose;
|
|
26
|
+
this.useTracker = this.workerData.tracker ?? this.useTracker;
|
|
27
27
|
}
|
|
28
28
|
if (this.useTracker) {
|
|
29
29
|
void import('@matterbridge/utils/tracker')
|
|
@@ -34,7 +34,7 @@ export class WorkerWrapper {
|
|
|
34
34
|
})
|
|
35
35
|
.catch((err) => {
|
|
36
36
|
if (this.debug)
|
|
37
|
-
console.error(`WorkerWrapper ${this.name}: failed to load Tracker
|
|
37
|
+
console.error(`WorkerWrapper ${this.name}: failed to load Tracker ${getErrorMessage(err)}`);
|
|
38
38
|
return;
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -75,17 +75,19 @@ export class WorkerWrapper {
|
|
|
75
75
|
if (this.verbose)
|
|
76
76
|
this.logWorkerInfo(this.log, false);
|
|
77
77
|
if (!isMainThread) {
|
|
78
|
-
setImmediate(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
setImmediate(() => {
|
|
79
|
+
void (async () => {
|
|
80
|
+
let success = false;
|
|
81
|
+
try {
|
|
82
|
+
success = await callback(this);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
inspectError(this.log, `Worker ${this.name} callback failed`, err);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
this.destroy(success);
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
89
91
|
});
|
|
90
92
|
}
|
|
91
93
|
}
|
package/dist/zipjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
|
-
import
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { Uint8ArrayReader, Uint8ArrayWriter, ZipReader, ZipWriter } from '@zip.js/zip.js';
|
|
4
4
|
import { AnsiLogger } from 'node-ansi-logger';
|
|
5
5
|
export async function createZip(zipPath, sourcePaths) {
|
|
@@ -9,11 +9,11 @@ export async function createZip(zipPath, sourcePaths) {
|
|
|
9
9
|
const writer = new ZipWriter(new Uint8ArrayWriter());
|
|
10
10
|
let entryCount = 0;
|
|
11
11
|
for (const sourcePath of sourcePaths) {
|
|
12
|
-
const resolvedSourcePath = resolve(sourcePath);
|
|
13
|
-
entryCount += await addZipEntry(writer, resolvedSourcePath, basename(resolvedSourcePath), log);
|
|
12
|
+
const resolvedSourcePath = path.resolve(sourcePath);
|
|
13
|
+
entryCount += await addZipEntry(writer, resolvedSourcePath, path.basename(resolvedSourcePath), log);
|
|
14
14
|
}
|
|
15
15
|
const zipData = await writer.close();
|
|
16
|
-
await mkdir(dirname(zipPath), { recursive: true });
|
|
16
|
+
await mkdir(path.dirname(zipPath), { recursive: true });
|
|
17
17
|
await writeFile(zipPath, zipData);
|
|
18
18
|
log.info(`Created zip ${zipPath} with ${entryCount} entries (${zipData.byteLength} bytes).`);
|
|
19
19
|
return zipData.byteLength;
|
|
@@ -58,7 +58,7 @@ export async function unZip(zipPath, destinationPath = getDefaultDestinationPath
|
|
|
58
58
|
extractedEntries++;
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
61
|
-
await mkdir(dirname(entryPath), { recursive: true });
|
|
61
|
+
await mkdir(path.dirname(entryPath), { recursive: true });
|
|
62
62
|
const fileData = await entry.getData(new Uint8ArrayWriter());
|
|
63
63
|
await writeFile(entryPath, fileData);
|
|
64
64
|
extractedEntries++;
|
|
@@ -83,7 +83,7 @@ async function addZipEntry(writer, sourcePath, entryName, log) {
|
|
|
83
83
|
children.sort((left, right) => left.name.localeCompare(right.name));
|
|
84
84
|
let childEntries = 1;
|
|
85
85
|
for (const child of children) {
|
|
86
|
-
childEntries += await addZipEntry(writer, join(sourcePath, child.name), `${directoryEntryName}${child.name}`, log);
|
|
86
|
+
childEntries += await addZipEntry(writer, path.join(sourcePath, child.name), `${directoryEntryName}${child.name}`, log);
|
|
87
87
|
}
|
|
88
88
|
log.debug(`Added directory ${directoryEntryName}`);
|
|
89
89
|
return childEntries;
|
|
@@ -96,18 +96,18 @@ async function addZipEntry(writer, sourcePath, entryName, log) {
|
|
|
96
96
|
throw new Error(`Unsupported source path type: ${sourcePath}`);
|
|
97
97
|
}
|
|
98
98
|
function getDefaultDestinationPath(zipPath) {
|
|
99
|
-
const zipExtension = extname(zipPath);
|
|
100
|
-
const zipName = basename(zipPath, zipExtension);
|
|
101
|
-
return join(dirname(zipPath), zipName);
|
|
99
|
+
const zipExtension = path.extname(zipPath);
|
|
100
|
+
const zipName = path.basename(zipPath, zipExtension);
|
|
101
|
+
return path.join(path.dirname(zipPath), zipName);
|
|
102
102
|
}
|
|
103
103
|
function normalizeEntryName(entryName) {
|
|
104
104
|
return entryName.replace(/\\/g, '/');
|
|
105
105
|
}
|
|
106
106
|
function resolveEntryPath(destinationPath, entryName) {
|
|
107
|
-
const resolvedDestinationPath = resolve(destinationPath);
|
|
108
|
-
const resolvedEntryPath = resolve(resolvedDestinationPath, normalizeEntryName(entryName));
|
|
109
|
-
const relativeEntryPath = relative(resolvedDestinationPath, resolvedEntryPath);
|
|
110
|
-
if (isAbsolute(relativeEntryPath) || relativeEntryPath.startsWith('..')) {
|
|
107
|
+
const resolvedDestinationPath = path.resolve(destinationPath);
|
|
108
|
+
const resolvedEntryPath = path.resolve(resolvedDestinationPath, normalizeEntryName(entryName));
|
|
109
|
+
const relativeEntryPath = path.relative(resolvedDestinationPath, resolvedEntryPath);
|
|
110
|
+
if (path.isAbsolute(relativeEntryPath) || relativeEntryPath.startsWith('..')) {
|
|
111
111
|
throw new Error(`Refusing to extract zip entry outside destination: ${entryName}`);
|
|
112
112
|
}
|
|
113
113
|
return resolvedEntryPath;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matterbridge/thread",
|
|
3
|
-
"version": "3.9.1-dev-
|
|
3
|
+
"version": "3.9.1-dev-20260617-b1e1b99",
|
|
4
4
|
"description": "Matterbridge thread library",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"homepage": "https://matterbridge.io/",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"CHANGELOG.md"
|
|
70
70
|
],
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@matterbridge/types": "3.9.1-dev-
|
|
73
|
-
"@matterbridge/utils": "3.9.1-dev-
|
|
72
|
+
"@matterbridge/types": "3.9.1-dev-20260617-b1e1b99",
|
|
73
|
+
"@matterbridge/utils": "3.9.1-dev-20260617-b1e1b99",
|
|
74
74
|
"@zip.js/zip.js": "2.8.26",
|
|
75
75
|
"node-ansi-logger": "3.3.0-dev-20260607-585945a"
|
|
76
76
|
}
|