@vextrix3d/machine-agent 0.0.0-stage → 2.0.0

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.
@@ -0,0 +1,218 @@
1
+ # Bambu's CAs - BBL CA, and BBL CA2 in RSA and ECC, each of those also as BBL CA signed it -
2
+ # as OpenBambuAPI collects them:
3
+ # https://github.com/Doridian/OpenBambuAPI/blob/main/examples/ca_cert.pem
4
+ -----BEGIN CERTIFICATE-----
5
+ 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
34
+ haGNe/KFNvJBMOIuIUzknRRmiQ==
35
+ -----END CERTIFICATE-----
36
+ -----BEGIN CERTIFICATE-----
37
+ MIIB8zCCAZmgAwIBAgIUe61jGQ4RzIC8k+sNuqbI/CaNqPIwCgYIKoZIzj0EAwIw
38
+ RjELMAkGA1UEBhMCQ04xITAfBgNVBAoMGEJCTCBUZWNobm9sb2dpZXMgQ28uIEx0
39
+ ZDEUMBIGA1UEAwwLQkJMIENBMiBFQ0MwIBcNMjUwNjE3MDEzODM1WhgPMjA1MDA2
40
+ MTcwMTM4MzVaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9n
41
+ aWVzIENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgRUNDMFkwEwYHKoZIzj0CAQYI
42
+ KoZIzj0DAQcDQgAEpKTF7wRSty4DXpGJzgCPwRh8ghLlxUC3qJbyEgLqTvJgbiwY
43
+ APPHK7kVbVmerkqhHOT4QeWRlTG3dOQGLA2VpaNjMGEwHQYDVR0OBBYEFKuRpsjY
44
+ REOyIKH7HwOE6jhGBd6NMB8GA1UdIwQYMBaAFKuRpsjYREOyIKH7HwOE6jhGBd6N
45
+ MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gA
46
+ MEUCIErBiUm3VdtP3rz4kb8aLpI5p+BzL7M9vElBGWWJxpHMAiEA3r5tJWVGwuxi
47
+ YCrB1c40KYFRFyahGrhOJZAj/YhRdnU=
48
+ -----END CERTIFICATE-----
49
+ -----BEGIN CERTIFICATE-----
50
+ MIIEeTCCA2GgAwIBAgIUOq+lNIaC2xsswkFqj5JPyVBl45cwDQYJKoZIhvcNAQEL
51
+ BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
52
+ LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yNTA2MTcwMjAxMjdaFw0zNTA2MTUw
53
+ MjAxMjdaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9naWVz
54
+ IENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgUlNBMIICIjANBgkqhkiG9w0BAQEF
55
+ AAOCAg8AMIICCgKCAgEAo4550G4c42gTKzQqixwKT089RizIdZpyOcGA679rPaOd
56
+ WsMqVwnYPP2FpMqXKkjFbedE+SpGloi2NKCuiPNVRbq9PHOOZwTs7YLobOwf53FJ
57
+ uO6vRFpzFfX1tlc9zlFqJvZnYO9NgHpMysidocWcgrDN/SIDywgPB5CVbYg3Vvzu
58
+ a9fwZx9e5KT9xd5IpTqdTrWS47jQOVKLhdQCbJFIlMrblOwLBAx+fHokwqh6tkI6
59
+ Ktuyyjw8Dysebi1ndWjKtZ2mW47r8xZ/J+z3EZqcyJMY6MRtx/zb1jBFuHtkjrb5
60
+ Kv1DMzSKlkaNJIbvC+Mk+hI97W+SjLSRuIdC7+oJUzWaSzgu9cjXCVfmq8t4IL/3
61
+ 5hP69PK95LgLectIrP96CYAT/aVMG19FrFW0QWEyfT+kzG4jkumfPbHqY2nNkEN0
62
+ +tjj3h4WdzrWgQEojK/lhfcRFVkts74+aZoMpQP+vmL17CKmSzXk5o/eK21xgxJd
63
+ zMbdztfTpibiXk0abfOpN+1VR+3NYa+bROAKNyGaReEGsyW2bjcjNx515Vqzj3SV
64
+ xhMSp5vfF9E4A1jE99M/l9jQDM6RzkT0lMccGAd5tUSdNvDlrqtQaQiKv/ZsXPgX
65
+ LTWfOpvaLNEgwdMgZMuhjpkwvAZyoYfeF9kyydjDh7bvrX//cz/VopAUlxUtQtMC
66
+ AwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
67
+ BBYEFNVJgQad1sNTN0jxVkwbJ/XM1an1MB8GA1UdIwQYMBaAFI80QmjcZ06PxCKe
68
+ xXxJ5avdRL4eMA0GCSqGSIb3DQEBCwUAA4IBAQAvS8tyfagaGsFf9YncA2ko/Na5
69
+ 9BVF+8TlUo+32oznwIVpS1AhSgLP6rNVekXNFKbuP5htudLQ17ZRBJI/UMVyYEDq
70
+ IN7xv7Zj+zJwF6W6haYrjb2Vk8igw1XvNULZfvVNNKIkvJUiVqEslWrC+k74crk/
71
+ Wv8ChVf+zqvfIN6LV3esaGRL02J3AprQGb7DDhR1EefQMScDkNpGJMUmvCmfknrl
72
+ iK8qgvQN1SWO7JRf6fNKHsN1ZQvyP0pgLWxpT3V0/0/WttqX3cMGuJF+jVUzm/Nh
73
+ xYhFewG8vc3KzTjnwQApMA6CW554FOJWFyOD2jn5yJLT3Vue+aYDQRp4bKMx
74
+ -----END CERTIFICATE-----
75
+ -----BEGIN CERTIFICATE-----
76
+ MIICrjCCAZagAwIBAgIUOq+lNIaC2xsswkFqj5JPyVBl45gwDQYJKoZIhvcNAQEL
77
+ BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
78
+ LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yNTA2MTcwMjAxNDdaFw0zNTA2MTUw
79
+ MjAxNDdaMEYxCzAJBgNVBAYTAkNOMSEwHwYDVQQKDBhCQkwgVGVjaG5vbG9naWVz
80
+ IENvLiBMdGQxFDASBgNVBAMMC0JCTCBDQTIgRUNDMFkwEwYHKoZIzj0CAQYIKoZI
81
+ zj0DAQcDQgAEpKTF7wRSty4DXpGJzgCPwRh8ghLlxUC3qJbyEgLqTvJgbiwYAPPH
82
+ K7kVbVmerkqhHOT4QeWRlTG3dOQGLA2VpaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAO
83
+ BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFKuRpsjYREOyIKH7HwOE6jhGBd6NMB8G
84
+ A1UdIwQYMBaAFI80QmjcZ06PxCKexXxJ5avdRL4eMA0GCSqGSIb3DQEBCwUAA4IB
85
+ AQCg6PjUSSZV+4bvejcVMvgXmKzfD95osWn0ctnoMBxPDa+m+Gg+BcLT2IlFAe3E
86
+ KYMvu4T295WQc92rjKYqW6cirFppng9uEFW2mZLimxaSmutsTftE3sbMVMJ/SLYN
87
+ PV7TFv6mcBSIFWXwmBOIpbh4BUcVfONTvdSfIqfyAVxsq4xzc2nc6hPBpAm21Ayj
88
+ ToC1ev/TbDJ8VllFZiEVmWWlIP3aNzAm8S2mOpxPB2WnanaZHSrvXLFhstyzwrjD
89
+ yO1/isOZ7wtr7rcuTJdEvvvCimOZlkfRhaDoTew9tQ0E2FVpzzSinw02qmQ1xIE9
90
+ 5/H5ZzJSPkpeAHWEPnKkxg0v
91
+ -----END CERTIFICATE-----
92
+ -----BEGIN CERTIFICATE-----
93
+ MIIDZTCCAk2gAwIBAgIUV1FckwXElyek1onFnQ9kL7Bk4N8wDQYJKoZIhvcNAQEL
94
+ BQAwQjELMAkGA1UEBhMCQ04xIjAgBgNVBAoMGUJCTCBUZWNobm9sb2dpZXMgQ28u
95
+ LCBMdGQxDzANBgNVBAMMBkJCTCBDQTAeFw0yMjA0MDQwMzQyMTFaFw0zMjA0MDEw
96
+ MzQyMTFaMEIxCzAJBgNVBAYTAkNOMSIwIAYDVQQKDBlCQkwgVGVjaG5vbG9naWVz
97
+ IENvLiwgTHRkMQ8wDQYDVQQDDAZCQkwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
98
+ DwAwggEKAoIBAQDL3pnDdxGOk5Z6vugiT4dpM0ju+3Xatxz09UY7mbj4tkIdby4H
99
+ oeEdiYSZjc5LJngJuCHwtEbBJt1BriRdSVrF6M9D2UaBDyamEo0dxwSaVxZiDVWC
100
+ eeCPdELpFZdEhSNTaT4O7zgvcnFsfHMa/0vMAkvE7i0qp3mjEzYLfz60axcDoJLk
101
+ p7n6xKXI+cJbA4IlToFjpSldPmC+ynOo7YAOsXt7AYKY6Glz0BwUVzSJxU+/+VFy
102
+ /QrmYGNwlrQtdREHeRi0SNK32x1+bOndfJP0sojuIrDjKsdCLye5CSZIvqnbowwW
103
+ 1jRwZgTBR29Zp2nzCoxJYcU9TSQp/4KZuWNVAgMBAAGjUzBRMB0GA1UdDgQWBBSP
104
+ NEJo3GdOj8QinsV8SeWr3US+HjAfBgNVHSMEGDAWgBSPNEJo3GdOj8QinsV8SeWr
105
+ 3US+HjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQABlBIT5ZeG
106
+ fgcK1LOh1CN9sTzxMCLbtTPFF1NGGA13mApu6j1h5YELbSKcUqfXzMnVeAb06Htu
107
+ 3CoCoe+wj7LONTFO++vBm2/if6Jt/DUw1CAEcNyqeh6ES0NX8LJRVSe0qdTxPJuA
108
+ BdOoo96iX89rRPoxeed1cpq5hZwbeka3+CJGV76itWp35Up5rmmUqrlyQOr/Wax6
109
+ itosIzG0MfhgUzU51A2P/hSnD3NDMXv+wUY/AvqgIL7u7fbDKnku1GzEKIkfH8hm
110
+ Rs6d8SCU89xyrwzQ0PR853irHas3WrHVqab3P+qNwR0YirL0Qk7Xt/q3O1griNg2
111
+ Blbjg3obpHo9
112
+ -----END CERTIFICATE-----
113
+
114
+ # The device CAs some models' certificates are issued through, each issued by BBL CA2 RSA, as the
115
+ # Home Assistant integration ships them:
116
+ # https://github.com/greghesp/ha-bambulab/tree/main/custom_components/bambu_lab/pybambu/certs
117
+
118
+ # BBL Device CA N7-V2 - some P2S units
119
+ -----BEGIN CERTIFICATE-----
120
+ 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
149
+ v09+U4m1fOSvF0We4MPCYt+z/E/br+uZ1pIBcg==
150
+ -----END CERTIFICATE-----
151
+
152
+ # BBL Device CA O1C2-V2 - H2C
153
+ -----BEGIN CERTIFICATE-----
154
+ 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
184
+ -----END CERTIFICATE-----
185
+
186
+ # BBL Device CA N6-V2 - X2C
187
+ -----BEGIN CERTIFICATE-----
188
+ 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
217
+ kvkHW8vmTffkFHMWC/YoXRXklNlv79Z4+Rywrw==
218
+ -----END CERTIFICATE-----
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { loadEnvFile } from 'node:process';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { readJpegCamera } from './camera-jpeg-connection.js';
7
+ import { hasCameraStream, readCameraStream, resolveCameraRelay } from './camera-relay.js';
8
+ import { BambuMqttClient } from './clients/BambuMqttClient.js';
9
+ import { MoonrakerClient } from './clients/MoonrakerClient.js';
10
+ import { PrinterConnectionType, Vextrix3dApiClient, } from './clients/Vextrix3dApiClient.js';
11
+ import { createPrintAttemptSynchronizer } from './print-attempt-synchronizer.js';
12
+ import { createPrinterGone } from './printer-gone.js';
13
+ import { createPrinterStateReporter } from './printer-state-reporter.js';
14
+ import { assert, buildMoonrakerAgentState, createBambuStateResolver, formatSubtaskName, once, readBambuTemperature, requireEnvVar, } from './utils.js';
15
+ const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
+ const workDir = process.cwd();
17
+ const { version: AGENT_VERSION } = JSON.parse(readFileSync(resolve(packageDir, 'package.json'), 'utf8'));
18
+ if (process.argv.includes('--version')) {
19
+ console.log(AGENT_VERSION);
20
+ process.exit(0);
21
+ }
22
+ const envFile = process.env.NODE_ENV === 'production' ? '.env.prod' : '.env.local';
23
+ const envPath = resolve(workDir, envFile);
24
+ if (!existsSync(envPath)) {
25
+ console.error(`No ${envFile} in ${workDir}: run the agent in the printer's folder`);
26
+ process.exit(1);
27
+ }
28
+ loadEnvFile(envPath);
29
+ console.info(`Loaded environment from ${envPath}`);
30
+ const fetchesCameraBinaries = process.env.VEXTRIX3D_CAMERA_BINARIES?.trim() !== 'manual';
31
+ const apiClient = new Vextrix3dApiClient({
32
+ functionsUrl: requireEnvVar('VEXTRIX3D_FUNCTIONS_URL'),
33
+ apiKey: requireEnvVar('VEXTRIX3D_MACHINE_AGENT_API_KEY'),
34
+ agentVersion: AGENT_VERSION,
35
+ });
36
+ const parseWifiSignalDbm = (value) => {
37
+ if (typeof value !== 'string') {
38
+ return null;
39
+ }
40
+ const parsed = Number.parseInt(value, 10);
41
+ return Number.isFinite(parsed) ? parsed : null;
42
+ };
43
+ const connectToBambuPrinter = (printer, connection, apiClient, printerGone) => {
44
+ const { ip, serialNumber, accessCode } = connection;
45
+ const jpegCamera = readJpegCamera(serialNumber, ip);
46
+ const cameraRelay = resolveCameraRelay({
47
+ workDir,
48
+ caFile: resolve(packageDir, 'certs', 'bambu-ca.pem'),
49
+ printerId: printer.id,
50
+ printerLabel: printer.label,
51
+ accessCode,
52
+ serialNumber,
53
+ jpegCamera,
54
+ fetchesBinaries: fetchesCameraBinaries,
55
+ getCameraAccess: () => apiClient.getCamera(),
56
+ printerGone,
57
+ });
58
+ const stateReporter = createPrinterStateReporter({
59
+ printerLabel: printer.label,
60
+ reportPrinterState: async (state) => {
61
+ const answer = await apiClient.reportPrinterState(state);
62
+ cameraRelay?.noteRelayKeyFingerprint(answer.cameraRelayKeyFingerprint);
63
+ return answer;
64
+ },
65
+ printerGone,
66
+ });
67
+ const printAttemptSynchronizer = createPrintAttemptSynchronizer({
68
+ printerLabel: printer.label,
69
+ apiClient,
70
+ printerGone,
71
+ });
72
+ const resolveState = createBambuStateResolver();
73
+ const mqttClient = new BambuMqttClient({
74
+ ip,
75
+ serialNumber,
76
+ accessCode,
77
+ onConnect: () => {
78
+ console.log(`Connected to "${printer.label}" (${serialNumber})`);
79
+ void printAttemptSynchronizer.initialise();
80
+ },
81
+ onStatus: print => {
82
+ const state = resolveState(print);
83
+ const cameraStream = readCameraStream(print.ipcam);
84
+ stateReporter.report({
85
+ state,
86
+ subtaskName: formatSubtaskName(print.subtask_name),
87
+ progressPct: print.mc_percent ?? null,
88
+ remainingMinutes: print.mc_remaining_time ?? null,
89
+ nozzleTemperature: print.nozzle_temper ?? null,
90
+ bedTemperature: print.bed_temper ?? null,
91
+ chamberTemperature: readBambuTemperature(print.device?.ctc?.info?.temp),
92
+ wifiSignalDbm: parseWifiSignalDbm(print.wifi_signal),
93
+ hasCameraStream: cameraStream === undefined ? undefined : hasCameraStream(cameraStream, jpegCamera),
94
+ });
95
+ printAttemptSynchronizer.handleState(state);
96
+ cameraRelay?.update(cameraStream);
97
+ },
98
+ onReconnect: () => {
99
+ console.log(`Reconnecting to "${printer.label}"...`);
100
+ },
101
+ onClose: () => {
102
+ console.log(`Connection to "${printer.label}" closed`);
103
+ },
104
+ onError: error => {
105
+ console.error(`MQTT error for "${printer.label}":`, error);
106
+ },
107
+ });
108
+ return {
109
+ stop: once(async () => {
110
+ await cameraRelay?.stop();
111
+ await stateReporter.flush();
112
+ stateReporter.stop();
113
+ await mqttClient.stop();
114
+ }),
115
+ };
116
+ };
117
+ const connectToMoonrakerPrinter = (printer, connection, apiClient, printerGone) => {
118
+ const { ip, port, apiKey } = connection;
119
+ const stateReporter = createPrinterStateReporter({
120
+ printerLabel: printer.label,
121
+ reportPrinterState: state => apiClient.reportPrinterState(state),
122
+ printerGone,
123
+ });
124
+ const printAttemptSynchronizer = createPrintAttemptSynchronizer({
125
+ printerLabel: printer.label,
126
+ apiClient,
127
+ printerGone,
128
+ });
129
+ let estimatedFilename = null;
130
+ let estimatedTimeS = null;
131
+ const loadEstimatedTime = (filename) => {
132
+ if (filename === estimatedFilename) {
133
+ return;
134
+ }
135
+ estimatedFilename = filename;
136
+ estimatedTimeS = null;
137
+ if (!filename) {
138
+ return;
139
+ }
140
+ client
141
+ .getFileEstimatedTime(filename)
142
+ .then(seconds => {
143
+ if (estimatedFilename === filename) {
144
+ estimatedTimeS = seconds;
145
+ }
146
+ })
147
+ .catch(error => {
148
+ console.error(`Failed to read the estimate of "${filename}" on "${printer.label}":`, error);
149
+ });
150
+ };
151
+ const client = new MoonrakerClient({
152
+ ip,
153
+ port,
154
+ apiKey,
155
+ onConnect: () => {
156
+ console.log(`Connected to "${printer.label}" (${ip}:${port})`);
157
+ void printAttemptSynchronizer.initialise();
158
+ },
159
+ onStatus: status => {
160
+ const filename = status.print_stats?.filename;
161
+ loadEstimatedTime(typeof filename === 'string' ? filename : '');
162
+ const agentState = buildMoonrakerAgentState(status, estimatedTimeS);
163
+ stateReporter.report(agentState);
164
+ printAttemptSynchronizer.handleState(agentState.state);
165
+ },
166
+ onReconnect: () => {
167
+ console.log(`Reconnecting to "${printer.label}"...`);
168
+ },
169
+ onClose: () => {
170
+ console.log(`Connection to "${printer.label}" closed`);
171
+ },
172
+ onError: error => {
173
+ console.error(`Moonraker error for "${printer.label}":`, error);
174
+ },
175
+ });
176
+ return {
177
+ stop: once(async () => {
178
+ await stateReporter.flush();
179
+ stateReporter.stop();
180
+ await client.stop();
181
+ }),
182
+ };
183
+ };
184
+ const connectToPrinter = (printer, apiClient, printerGone) => {
185
+ const { connection } = printer;
186
+ switch (connection.type) {
187
+ case PrinterConnectionType.BAMBU_MQTT:
188
+ return connectToBambuPrinter(printer, connection, apiClient, printerGone);
189
+ case PrinterConnectionType.MOONRAKER:
190
+ return connectToMoonrakerPrinter(printer, connection, apiClient, printerGone);
191
+ default:
192
+ throw new Error(`Unsupported printer connection type: ${connection.type}`);
193
+ }
194
+ };
195
+ let printerConnection = null;
196
+ const printerGone = createPrinterGone({
197
+ stop: () => printerConnection?.stop(),
198
+ });
199
+ const stop = once(async () => {
200
+ console.log('Stopping machine agent');
201
+ try {
202
+ await printerConnection?.stop();
203
+ }
204
+ catch (error) {
205
+ console.error('Failed to stop cleanly:', error);
206
+ }
207
+ finally {
208
+ process.exit(0);
209
+ }
210
+ });
211
+ process.once('SIGINT', () => {
212
+ void stop();
213
+ });
214
+ process.once('SIGTERM', () => {
215
+ void stop();
216
+ });
217
+ const bootstrap = async () => {
218
+ console.log(`Starting machine agent ${AGENT_VERSION}`);
219
+ const printer = await apiClient.getPrinterInfo();
220
+ console.log(`Loaded printer "${printer.label}" (${printer.id})`);
221
+ assert(printer.isEnabled, `Printer "${printer.label}" is disabled`);
222
+ printerConnection = connectToPrinter(printer, apiClient, printerGone);
223
+ };
224
+ bootstrap().catch(error => {
225
+ if (printerGone.notice(error)) {
226
+ return;
227
+ }
228
+ console.error('Failed to start:', error);
229
+ process.exitCode = 1;
230
+ });
@@ -0,0 +1,185 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { Readable, Transform } from 'node:stream';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import { promisify } from 'node:util';
8
+ import { describeError, sha256File } from './utils.js';
9
+ const GO2RTC_VERSION = 'v1.9.14';
10
+ const CLOUDFLARED_VERSION = '2026.10.7';
11
+ const pinGo2rtc = (file, sha256) => ({
12
+ name: 'go2rtc',
13
+ version: GO2RTC_VERSION,
14
+ file,
15
+ url: `https://github.com/AlexxIT/go2rtc/releases/download/${GO2RTC_VERSION}/${file}`,
16
+ sha256,
17
+ packing: file.endsWith('.zip') ? 'zip' : 'raw',
18
+ });
19
+ const pinCloudflared = (file, sha256) => ({
20
+ name: 'cloudflared',
21
+ version: CLOUDFLARED_VERSION,
22
+ file,
23
+ url: `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/${file}`,
24
+ sha256,
25
+ packing: file.endsWith('.tgz') ? 'tgz' : 'raw',
26
+ });
27
+ const LINUX_ARM_CLOUDFLARED_PIN = pinCloudflared('cloudflared-linux-arm', '74f690f6707182839b63e0465726c7776dd63f0a174390b911d5d71f35cab94e');
28
+ const CAMERA_BINARY_PINS = {
29
+ 'linux-arm64': [
30
+ pinGo2rtc('go2rtc_linux_arm64', '359fabade8a7a51e81a55fe6df6b0ef81764a5e1d63179577534eaaa71904b50'),
31
+ pinCloudflared('cloudflared-linux-arm64', '3df43aedc9ff291d1735b5cb576cb97e0b157488796db2e32b28aecabb445775'),
32
+ ],
33
+ 'linux-arm': [
34
+ pinGo2rtc('go2rtc_linux_arm', '4d7e1639af5a2722a28e864468fd8099b3c1682565446c798bf9e3b38fde12e4'),
35
+ LINUX_ARM_CLOUDFLARED_PIN,
36
+ ],
37
+ 'linux-x64': [
38
+ pinGo2rtc('go2rtc_linux_amd64', '32d616af226bd731678ffde328b94cfb94e30339bfefc469cfb76323144615a6'),
39
+ pinCloudflared('cloudflared-linux-amd64', '1f99fb518b83dec2ba8d870a7cc991d82a77e6196295a3d2754ba17ab9f91872'),
40
+ ],
41
+ 'darwin-arm64': [
42
+ pinGo2rtc('go2rtc_mac_arm64.zip', '919b78adc759d6b3883d1e1b2ac915ac0985bb903ff1897b4d228527bd64690c'),
43
+ pinCloudflared('cloudflared-darwin-arm64.tgz', '3586ccf397e98e5f437e60e7c307ca6ac463e110580503af8bb8e6f9ebf831bd'),
44
+ ],
45
+ 'darwin-x64': [
46
+ pinGo2rtc('go2rtc_mac_amd64.zip', '9b0b9a27a4dc3a5b8b93376e7e8fc2787c6af624a512842622be84aec0171c7a'),
47
+ pinCloudflared('cloudflared-darwin-amd64.tgz', '02f3edfd87684dd5b705607688e4eb4b323f6e777b81e7b3a85af556aa356848'),
48
+ ],
49
+ };
50
+ const LINUX_ARMV6_PINS = [
51
+ pinGo2rtc('go2rtc_linux_armv6', '4dc20370556b29f3a90f4c7a09dcd95472c8f74cca56d4d1fb91f32bdd15174c'),
52
+ LINUX_ARM_CLOUDFLARED_PIN,
53
+ ];
54
+ export const findCameraBinaryPins = (platform, arch, armVersion) => {
55
+ if (platform === 'linux' && arch === 'arm') {
56
+ if (armVersion === '6') {
57
+ return LINUX_ARMV6_PINS;
58
+ }
59
+ if (armVersion !== '7') {
60
+ console.log(`[camera] Node reports ARM version ${armVersion || 'none'} - fetching go2rtc and cloudflared for ARMv7`);
61
+ }
62
+ }
63
+ const key = `${platform}-${arch}`;
64
+ return Object.hasOwn(CAMERA_BINARY_PINS, key) ? CAMERA_BINARY_PINS[key] : null;
65
+ };
66
+ const execFileAsync = promisify(execFile);
67
+ export const unpackArchiveEntry = async (archivePath, name, dir) => {
68
+ try {
69
+ await execFileAsync('tar', ['-xf', archivePath, '-C', dir, name]);
70
+ }
71
+ catch (error) {
72
+ const reason = error.stderr?.trim().split('\n')[0];
73
+ throw new Error(`tar could not unpack ${name}${reason ? ` (${reason})` : ''}`, {
74
+ cause: error,
75
+ });
76
+ }
77
+ };
78
+ export const resolveCameraBinarySourcePath = (binaryPath) => `${binaryPath}.pin.json`;
79
+ const isPinnedBuild = async (path, pin) => {
80
+ try {
81
+ if (pin.packing === 'raw') {
82
+ return (await sha256File(path)) === pin.sha256;
83
+ }
84
+ const source = JSON.parse(readFileSync(resolveCameraBinarySourcePath(path), 'utf8'));
85
+ return (source.archiveSha256 === pin.sha256 &&
86
+ typeof source.sha256 === 'string' &&
87
+ (await sha256File(path)) === source.sha256);
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ };
93
+ const defaultDependencies = {
94
+ fetch: (url, init) => fetch(url, init),
95
+ timeoutMs: 10 * 60_000,
96
+ unpack: unpackArchiveEntry,
97
+ };
98
+ const download = async (url, path, { fetch, timeoutMs }) => {
99
+ const response = await fetch(url, {
100
+ redirect: 'follow',
101
+ signal: AbortSignal.timeout(timeoutMs),
102
+ });
103
+ if (!response.ok || !response.body) {
104
+ throw new Error(`GitHub answered ${response.status} ${response.statusText}`.trim());
105
+ }
106
+ const hash = createHash('sha256');
107
+ await pipeline(Readable.from(response.body), new Transform({
108
+ transform(chunk, _encoding, callback) {
109
+ hash.update(chunk);
110
+ callback(null, chunk);
111
+ },
112
+ }), createWriteStream(path, {
113
+ mode: 0o600,
114
+ }));
115
+ return hash.digest('hex');
116
+ };
117
+ const ensureCameraBinary = async (cameraDir, pin, dependencies) => {
118
+ const path = join(cameraDir, pin.name);
119
+ if (await isPinnedBuild(path, pin)) {
120
+ if ((statSync(path).mode & 0o111) === 0) {
121
+ chmodSync(path, 0o755);
122
+ }
123
+ return;
124
+ }
125
+ const hadFile = existsSync(path);
126
+ const downloadPath = `${path}.download`;
127
+ const unpackedPath = `${path}.unpacked`;
128
+ const removeLeftovers = () => {
129
+ rmSync(downloadPath, {
130
+ force: true,
131
+ });
132
+ rmSync(unpackedPath, {
133
+ recursive: true,
134
+ force: true,
135
+ });
136
+ };
137
+ mkdirSync(cameraDir, {
138
+ recursive: true,
139
+ });
140
+ removeLeftovers();
141
+ try {
142
+ const sha256 = await download(pin.url, downloadPath, dependencies);
143
+ if (sha256 !== pin.sha256) {
144
+ throw new Error(`its SHA-256 is ${sha256}, not the pinned ${pin.sha256} - deleted unrun`);
145
+ }
146
+ if (pin.packing === 'raw') {
147
+ chmodSync(downloadPath, 0o755);
148
+ renameSync(downloadPath, path);
149
+ }
150
+ else {
151
+ mkdirSync(unpackedPath);
152
+ await dependencies.unpack(downloadPath, pin.name, unpackedPath);
153
+ const unpackedBinaryPath = join(unpackedPath, pin.name);
154
+ const source = {
155
+ file: pin.file,
156
+ version: pin.version,
157
+ archiveSha256: pin.sha256,
158
+ sha256: await sha256File(unpackedBinaryPath),
159
+ };
160
+ chmodSync(unpackedBinaryPath, 0o755);
161
+ renameSync(unpackedBinaryPath, path);
162
+ writeFileSync(resolveCameraBinarySourcePath(path), `${JSON.stringify(source, null, 2)}\n`);
163
+ }
164
+ }
165
+ finally {
166
+ removeLeftovers();
167
+ }
168
+ console.log(`[camera] ${hadFile
169
+ ? `Replaced camera/${pin.name} with ${pin.name} ${pin.version}`
170
+ : `Fetched ${pin.name} ${pin.version} into camera/${pin.name}`} (${pin.file}, its SHA-256 checked)`);
171
+ };
172
+ export const ensureCameraBinaries = async (cameraDir, pins, dependencies = defaultDependencies) => {
173
+ let isReady = true;
174
+ for (const pin of pins) {
175
+ try {
176
+ await ensureCameraBinary(cameraDir, pin, dependencies);
177
+ }
178
+ catch (error) {
179
+ isReady = false;
180
+ const path = join(cameraDir, pin.name);
181
+ console.error(`[camera] Could not fetch ${pin.name} ${pin.version} (${pin.file}): ${describeError(error)}; ${existsSync(path) ? `the camera/${pin.name} there is used meanwhile` : 'trying again later'}`);
182
+ }
183
+ }
184
+ return isReady;
185
+ };