bambu-printer-mcp 1.1.32 → 1.1.34
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/CONTRIBUTORS.md +5 -1
- package/README.md +4 -2
- package/dist/bambu-network-bridge.d.ts +78 -4
- package/dist/bambu-network-bridge.js +292 -47
- package/dist/bambu-network-certs.d.ts +13 -0
- package/dist/bambu-network-certs.js +152 -0
- package/dist/index.js +28 -11
- package/dist/printers/bambu.d.ts +12 -2
- package/dist/printers/bambu.js +74 -41
- package/dist/safety/printer-state.d.ts +2 -0
- package/dist/safety/printer-state.js +42 -1
- package/dist/slicer/orca-profile-cache.d.ts +18 -0
- package/dist/slicer/orca-profile-cache.js +460 -0
- package/dist/slicer/profile-flatten.js +1 -1
- package/dist/stl/stl-manipulator.d.ts +8 -0
- package/dist/stl/stl-manipulator.js +148 -21
- package/package.json +1 -1
- package/src/bambu-network-bridge.ts +324 -55
- package/src/bambu-network-certs.ts +154 -0
- package/src/index.ts +43 -25
- package/src/printers/bambu.ts +74 -58
- package/src/safety/printer-state.ts +42 -1
- package/src/slicer/orca-profile-cache.ts +379 -0
- package/src/slicer/profile-flatten.ts +1 -1
- package/src/stl/stl-manipulator.ts +156 -16
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bambu's public certificates from Bambu Studio v02.08.02.61 resources/cert,
|
|
3
|
+
* the folder Bambu Studio and FULU's GUI pass to the networking plug-in's
|
|
4
|
+
* set_cert_file. printer.cer holds the BBL CA roots that sign each printer's
|
|
5
|
+
* LAN MQTT certificate; slicer_base64.cer is the cloud certificate. FULU's
|
|
6
|
+
* standalone Linux host runtime ships only slicer_base64.cer, so its plug-in
|
|
7
|
+
* cannot verify a LAN printer without these. Exact bytes, pinned by SHA-256
|
|
8
|
+
* (the same pins as scripts/setup-bambu-native.mjs).
|
|
9
|
+
*/
|
|
10
|
+
export const BAMBU_NETWORK_CERTIFICATES = Object.freeze({
|
|
11
|
+
"printer.cer": {
|
|
12
|
+
sha256: "36f2bcee347ec7adce719b5fd350099591a4d3d0ec4e039c7019890d78e152a0",
|
|
13
|
+
base64: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZmekNDQTJlZ0F3SUJBZ0lVWHR6UjZ0UmlML1JIQlJYT295RlUwK1hybGlv" +
|
|
14
|
+
"d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1JqRUxNQWtHQTFVRUJoTUNRMDR4SVRBZkJnTlZCQW9NR0VKQ1RDQlVaV05vYm05c2IyZHBa" +
|
|
15
|
+
"WE1nUTI4dQpJRXgwWkRFVU1CSUdBMVVFQXd3TFFrSk1JRU5CTWlCU1UwRXdJQmNOTWpVd05qRTNNREV6T0RBNFdoZ1BNakExCk1E" +
|
|
16
|
+
"QTJNVGN3TVRNNE1EaGFNRVl4Q3pBSkJnTlZCQVlUQWtOT01TRXdId1lEVlFRS0RCaENRa3dnVkdWamFHNXYKYkc5bmFXVnpJRU52" +
|
|
17
|
+
"TGlCTWRHUXhGREFTQmdOVkJBTU1DMEpDVENCRFFUSWdVbE5CTUlJQ0lqQU5CZ2txaGtpRwo5dzBCQVFFRkFBT0NBZzhBTUlJQ0Nn" +
|
|
18
|
+
"S0NBZ0VBbzQ1NTBHNGM0MmdUS3pRcWl4d0tUMDg5Uml6SWRacHlPY0dBCjY3OXJQYU9kV3NNcVZ3bllQUDJGcE1xWEtrakZiZWRF" +
|
|
19
|
+
"K1NwR2xvaTJOS0N1aVBOVlJicTlQSE9PWndUczdZTG8KYk93ZjUzRkp1TzZ2UkZwekZmWDF0bGM5emxGcUp2Wm5ZTzlOZ0hwTXlz" +
|
|
20
|
+
"aWRvY1djZ3JETi9TSUR5d2dQQjVDVgpiWWczVnZ6dWE5ZndaeDllNUtUOXhkNUlwVHFkVHJXUzQ3alFPVktMaGRRQ2JKRklsTXJi" +
|
|
21
|
+
"bE93TEJBeCtmSG9rCndxaDZ0a0k2S3R1eXlqdzhEeXNlYmkxbmRXakt0WjJtVzQ3cjh4Wi9KK3ozRVpxY3lKTVk2TVJ0eC96YjFq" +
|
|
22
|
+
"QkYKdUh0a2pyYjVLdjFETXpTS2xrYU5KSWJ2QytNaytoSTk3VytTakxTUnVJZEM3K29KVXpXYVN6Z3U5Y2pYQ1ZmbQpxOHQ0SUwv" +
|
|
23
|
+
"MzVoUDY5UEs5NUxnTGVjdElyUDk2Q1lBVC9hVk1HMTlGckZXMFFXRXlmVCtrekc0amt1bWZQYkhxClkybk5rRU4wK3RqajNoNFdk" +
|
|
24
|
+
"enJXZ1FFb2pLL2xoZmNSRlZrdHM3NCthWm9NcFFQK3ZtTDE3Q0ttU3pYazVvL2UKSzIxeGd4SmR6TWJkenRmVHBpYmlYazBhYmZP" +
|
|
25
|
+
"cE4rMVZSKzNOWWErYlJPQUtOeUdhUmVFR3N5VzJiamNqTng1MQo1VnF6ajNTVnhoTVNwNXZmRjlFNEExakU5OU0vbDlqUURNNlJ6" +
|
|
26
|
+
"a1QwbE1jY0dBZDV0VVNkTnZEbHJxdFFhUWlLCnYvWnNYUGdYTFRXZk9wdmFMTkVnd2RNZ1pNdWhqcGt3dkFaeW9ZZmVGOWt5eWRq" +
|
|
27
|
+
"RGg3YnZyWC8vY3ovVm9wQVUKbHhVdFF0TUNBd0VBQWFOak1HRXdIUVlEVlIwT0JCWUVGTlZKZ1FhZDFzTlROMGp4Vmt3YkovWE0x" +
|
|
28
|
+
"YW4xTUI4RwpBMVVkSXdRWU1CYUFGTlZKZ1FhZDFzTlROMGp4Vmt3YkovWE0xYW4xTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3CkRn" +
|
|
29
|
+
"WURWUjBQQVFIL0JBUURBZ0VHTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElDQVFCRlpES01KZnAvTjRnQmVGSGgKTWlGZWhhVXlNUzZl" +
|
|
30
|
+
"OW16clRmTUxKTEpvajZKb3BhOVY5aklmY0NFQkdadVJUaHFGY0FUVitVZEZIU0lOcFVjSAp1cGNDWW5helRSQzRkbjFobnhuUTFv" +
|
|
31
|
+
"alFjSHhkR3A5eEd3L1ljbEFLRDk3ZDhiUFNoZkJNVDF0bzl6Yk1LN1Q1Ckw4emdxZzAxWUlPS2pRazBIY2QwKzBpVXI2bTh6UTVQ" +
|
|
32
|
+
"OFJsM1FYcUF5ZVdncW1ZUXJyalRXd1BzZ2RmSE5YS1gKdkRyeDcvY3FyeTVsS1U4MDJoVXBsS01CeGVsdjRXODQwN1l0ajFsZkpP" +
|
|
33
|
+
"d3Z4cXh4c0ZVNWpTd2NVRzN6bzJ2awpRdGpSczhtNUJLdXA1SzFPUFlra1B1N0xkODlYMFhwVTA3My9kTkRHMTF1eGIxZURLcnRO" +
|
|
34
|
+
"UDZ2WnVaak5FMlBxCjhIQ29JMUV0UCtJdHlxdFVNdkhpNloyenNtbEEyNWJyb1Zpb2VVS3hqbEllY3BROUpSL0ZoRHU5Q1dORi9u" +
|
|
35
|
+
"RFcKTFNPUk5hTU16Z3NNU3pJK0hDaVVocU4rcU1JdlZQNnJ6R1RKendxei9sYzVMZitaUENuR0E5V0pUVDR1UEloZgp1ZmJabW5V" +
|
|
36
|
+
"SjM1V3VXS0h4b3ZEc3FCaDg4elE5c1orZWk0SGk0dlZ6T2hVZ2ZHM2FMb1NRRVlxUm9xYWJvQU5oCndDd3p5dVcyUnY1NHU1UVNC" +
|
|
37
|
+
"YmQ2R3gxT3B2c1dtTFBXZDIvaUwya0lTbDV3Zm1MR1Z5ZHZTSmErcmJPZnVBeTcKeWNWUWFjVkRRQ0FuYmhvVnJReTcrNDU0UXNL" +
|
|
38
|
+
"U1czWlY2QmN5UnJvcmV3Q3lDWWdkN255eGZseEhaVEJFeWtYWApoYUdOZS9LRk52SkJNT0l1SVV6a25SUm1pUT09Ci0tLS0tRU5E" +
|
|
39
|
+
"IENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUI4ekNDQVptZ0F3SUJBZ0lVZTYxakdRNFJ6" +
|
|
40
|
+
"SUM4aytzTnVxYkkvQ2FOcVBJd0NnWUlLb1pJemowRUF3SXcKUmpFTE1Ba0dBMVVFQmhNQ1EwNHhJVEFmQmdOVkJBb01HRUpDVENC" +
|
|
41
|
+
"VVpXTm9ibTlzYjJkcFpYTWdRMjh1SUV4MApaREVVTUJJR0ExVUVBd3dMUWtKTUlFTkJNaUJGUTBNd0lCY05NalV3TmpFM01ERXpP" +
|
|
42
|
+
"RE0xV2hnUE1qQTFNREEyCk1UY3dNVE00TXpWYU1FWXhDekFKQmdOVkJBWVRBa05PTVNFd0h3WURWUVFLREJoQ1Frd2dWR1ZqYUc1" +
|
|
43
|
+
"dmJHOW4KYVdWeklFTnZMaUJNZEdReEZEQVNCZ05WQkFNTUMwSkNUQ0JEUVRJZ1JVTkRNRmt3RXdZSEtvWkl6ajBDQVFZSQpLb1pJ" +
|
|
44
|
+
"emowREFRY0RRZ0FFcEtURjd3UlN0eTREWHBHSnpnQ1B3Umg4Z2hMbHhVQzNxSmJ5RWdMcVR2SmdiaXdZCkFQUEhLN2tWYlZtZXJr" +
|
|
45
|
+
"cWhIT1Q0UWVXUmxURzNkT1FHTEEyVnBhTmpNR0V3SFFZRFZSME9CQllFRkt1UnBzalkKUkVPeUlLSDdId09FNmpoR0JkNk5NQjhH" +
|
|
46
|
+
"QTFVZEl3UVlNQmFBRkt1UnBzallSRU95SUtIN0h3T0U2amhHQmQ2TgpNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEZ1lEVlIwUEFR" +
|
|
47
|
+
"SC9CQVFEQWdFR01Bb0dDQ3FHU000OUJBTUNBMGdBCk1FVUNJRXJCaVVtM1ZkdFAzcno0a2I4YUxwSTVwK0J6TDdNOXZFbEJHV1dK" +
|
|
48
|
+
"eHBITUFpRUEzcjV0SldWR3d1eGkKWUNyQjFjNDBLWUZSRnlhaEdyaE9KWkFqL1loUmRuVT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUt" +
|
|
49
|
+
"LS0tLQotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJRWVUQ0NBMkdnQXdJQkFnSVVPcStsTklhQzJ4c3N3a0ZxajVKUHlW" +
|
|
50
|
+
"Qmw0NWN3RFFZSktvWklodmNOQVFFTApCUUF3UWpFTE1Ba0dBMVVFQmhNQ1EwNHhJakFnQmdOVkJBb01HVUpDVENCVVpXTm9ibTlz" +
|
|
51
|
+
"YjJkcFpYTWdRMjh1CkxDQk1kR1F4RHpBTkJnTlZCQU1NQmtKQ1RDQkRRVEFlRncweU5UQTJNVGN3TWpBeE1qZGFGdzB6TlRBMk1U" +
|
|
52
|
+
"VXcKTWpBeE1qZGFNRVl4Q3pBSkJnTlZCQVlUQWtOT01TRXdId1lEVlFRS0RCaENRa3dnVkdWamFHNXZiRzluYVdWegpJRU52TGlC" +
|
|
53
|
+
"TWRHUXhGREFTQmdOVkJBTU1DMEpDVENCRFFUSWdVbE5CTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGCkFBT0NBZzhBTUlJQ0NnS0NB" +
|
|
54
|
+
"Z0VBbzQ1NTBHNGM0MmdUS3pRcWl4d0tUMDg5Uml6SWRacHlPY0dBNjc5clBhT2QKV3NNcVZ3bllQUDJGcE1xWEtrakZiZWRFK1Nw" +
|
|
55
|
+
"R2xvaTJOS0N1aVBOVlJicTlQSE9PWndUczdZTG9iT3dmNTNGSgp1TzZ2UkZwekZmWDF0bGM5emxGcUp2Wm5ZTzlOZ0hwTXlzaWRv" +
|
|
56
|
+
"Y1djZ3JETi9TSUR5d2dQQjVDVmJZZzNWdnp1CmE5ZndaeDllNUtUOXhkNUlwVHFkVHJXUzQ3alFPVktMaGRRQ2JKRklsTXJibE93" +
|
|
57
|
+
"TEJBeCtmSG9rd3FoNnRrSTYKS3R1eXlqdzhEeXNlYmkxbmRXakt0WjJtVzQ3cjh4Wi9KK3ozRVpxY3lKTVk2TVJ0eC96YjFqQkZ1" +
|
|
58
|
+
"SHRranJiNQpLdjFETXpTS2xrYU5KSWJ2QytNaytoSTk3VytTakxTUnVJZEM3K29KVXpXYVN6Z3U5Y2pYQ1ZmbXE4dDRJTC8zCjVo" +
|
|
59
|
+
"UDY5UEs5NUxnTGVjdElyUDk2Q1lBVC9hVk1HMTlGckZXMFFXRXlmVCtrekc0amt1bWZQYkhxWTJuTmtFTjAKK3RqajNoNFdkenJX" +
|
|
60
|
+
"Z1FFb2pLL2xoZmNSRlZrdHM3NCthWm9NcFFQK3ZtTDE3Q0ttU3pYazVvL2VLMjF4Z3hKZAp6TWJkenRmVHBpYmlYazBhYmZPcE4r" +
|
|
61
|
+
"MVZSKzNOWWErYlJPQUtOeUdhUmVFR3N5VzJiamNqTng1MTVWcXpqM1NWCnhoTVNwNXZmRjlFNEExakU5OU0vbDlqUURNNlJ6a1Qw" +
|
|
62
|
+
"bE1jY0dBZDV0VVNkTnZEbHJxdFFhUWlLdi9ac1hQZ1gKTFRXZk9wdmFMTkVnd2RNZ1pNdWhqcGt3dkFaeW9ZZmVGOWt5eWRqRGg3" +
|
|
63
|
+
"YnZyWC8vY3ovVm9wQVVseFV0UXRNQwpBd0VBQWFOak1HRXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QU9CZ05WSFE4QkFmOEVCQU1D" +
|
|
64
|
+
"QVFZd0hRWURWUjBPCkJCWUVGTlZKZ1FhZDFzTlROMGp4Vmt3YkovWE0xYW4xTUI4R0ExVWRJd1FZTUJhQUZJODBRbWpjWjA2UHhD" +
|
|
65
|
+
"S2UKeFh4SjVhdmRSTDRlTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFBdlM4dHlmYWdhR3NGZjlZbmNBMmtvL05hNQo5QlZGKzhU" +
|
|
66
|
+
"bFVvKzMyb3pud0lWcFMxQWhTZ0xQNnJOVmVrWE5GS2J1UDVodHVkTFExN1pSQkpJL1VNVnlZRURxCklON3h2N1pqK3pKd0Y2VzZo" +
|
|
67
|
+
"YVlyamIyVms4aWd3MVh2TlVMWmZ2Vk5OS0lrdkpVaVZxRXNsV3JDK2s3NGNyay8KV3Y4Q2hWZit6cXZmSU42TFYzZXNhR1JMMDJK" +
|
|
68
|
+
"M0FwclFHYjdERGhSMUVlZlFNU2NEa05wR0pNVW12Q21ma25ybAppSzhxZ3ZRTjFTV083SlJmNmZOS0hzTjFaUXZ5UDBwZ0xXeHBU" +
|
|
69
|
+
"M1YwLzAvV3R0cVgzY01HdUpGK2pWVXptL05oCnhZaEZld0c4dmMzS3pUam53UUFwTUE2Q1c1NTRGT0pXRnlPRDJqbjV5SkxUM1Z1" +
|
|
70
|
+
"ZSthWURRUnA0YktNeAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlDcmpD" +
|
|
71
|
+
"Q0FaYWdBd0lCQWdJVU9xK2xOSWFDMnhzc3drRnFqNUpQeVZCbDQ1Z3dEUVlKS29aSWh2Y05BUUVMCkJRQXdRakVMTUFrR0ExVUVC" +
|
|
72
|
+
"aE1DUTA0eElqQWdCZ05WQkFvTUdVSkNUQ0JVWldOb2JtOXNiMmRwWlhNZ1EyOHUKTENCTWRHUXhEekFOQmdOVkJBTU1Ca0pDVENC" +
|
|
73
|
+
"RFFUQWVGdzB5TlRBMk1UY3dNakF4TkRkYUZ3MHpOVEEyTVRVdwpNakF4TkRkYU1FWXhDekFKQmdOVkJBWVRBa05PTVNFd0h3WURW" +
|
|
74
|
+
"UVFLREJoQ1Frd2dWR1ZqYUc1dmJHOW5hV1Z6CklFTnZMaUJNZEdReEZEQVNCZ05WQkFNTUMwSkNUQ0JEUVRJZ1JVTkRNRmt3RXdZ" +
|
|
75
|
+
"SEtvWkl6ajBDQVFZSUtvWkkKemowREFRY0RRZ0FFcEtURjd3UlN0eTREWHBHSnpnQ1B3Umg4Z2hMbHhVQzNxSmJ5RWdMcVR2Smdi" +
|
|
76
|
+
"aXdZQVBQSApLN2tWYlZtZXJrcWhIT1Q0UWVXUmxURzNkT1FHTEEyVnBhTmpNR0V3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFPCkJn" +
|
|
77
|
+
"TlZIUThCQWY4RUJBTUNBUVl3SFFZRFZSME9CQllFRkt1UnBzallSRU95SUtIN0h3T0U2amhHQmQ2Tk1COEcKQTFVZEl3UVlNQmFB" +
|
|
78
|
+
"Rkk4MFFtamNaMDZQeENLZXhYeEo1YXZkUkw0ZU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQgpBUUNnNlBqVVNTWlYrNGJ2ZWpjVk12" +
|
|
79
|
+
"Z1htS3pmRDk1b3NXbjBjdG5vTUJ4UERhK20rR2crQmNMVDJJbEZBZTNFCktZTXZ1NFQyOTVXUWM5MnJqS1lxVzZjaXJGcHBuZzl1" +
|
|
80
|
+
"RUZXMm1aTGlteGFTbXV0c1RmdEUzc2JNVk1KL1NMWU4KUFY3VEZ2Nm1jQlNJRldYd21CT0lwYmg0QlVjVmZPTlR2ZFNmSXFmeUFW" +
|
|
81
|
+
"eHNxNHh6YzJuYzZoUEJwQW0yMUF5agpUb0MxZXYvVGJESjhWbGxGWmlFVm1XV2xJUDNhTnpBbThTMm1PcHhQQjJXbmFuYVpIU3J2" +
|
|
82
|
+
"WExGaHN0eXp3cmpECnlPMS9pc09aN3d0cjdyY3VUSmRFdnZ2Q2ltT1psa2ZSaGFEb1Rldzl0UTBFMkZWcHp6U2ludzAycW1RMXhJ" +
|
|
83
|
+
"RTkKNS9INVp6SlNQa3BlQUhXRVBuS2t4ZzB2Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FU" +
|
|
84
|
+
"RS0tLS0tCk1JSURaVENDQWsyZ0F3SUJBZ0lVVjFGY2t3WEVseWVrMW9uRm5ROWtMN0JrNE44d0RRWUpLb1pJaHZjTkFRRUwKQlFB" +
|
|
85
|
+
"d1FqRUxNQWtHQTFVRUJoTUNRMDR4SWpBZ0JnTlZCQW9NR1VKQ1RDQlVaV05vYm05c2IyZHBaWE1nUTI4dQpMQ0JNZEdReER6QU5C" +
|
|
86
|
+
"Z05WQkFNTUJrSkNUQ0JEUVRBZUZ3MHlNakEwTURRd016UXlNVEZhRncwek1qQTBNREV3Ck16UXlNVEZhTUVJeEN6QUpCZ05WQkFZ" +
|
|
87
|
+
"VEFrTk9NU0l3SUFZRFZRUUtEQmxDUWt3Z1ZHVmphRzV2Ykc5bmFXVnoKSUVOdkxpd2dUSFJrTVE4d0RRWURWUVFEREFaQ1Frd2dR" +
|
|
88
|
+
"MEV3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQgpEd0F3Z2dFS0FvSUJBUURMM3BuRGR4R09rNVo2dnVnaVQ0ZHBNMGp1KzNY" +
|
|
89
|
+
"YXR4ejA5VVk3bWJqNHRrSWRieTRICm9lRWRpWVNaamM1TEpuZ0p1Q0h3dEViQkp0MUJyaVJkU1ZyRjZNOUQyVWFCRHlhbUVvMGR4" +
|
|
90
|
+
"d1NhVnhaaURWV0MKZWVDUGRFTHBGWmRFaFNOVGFUNE83emd2Y25Gc2ZITWEvMHZNQWt2RTdpMHFwM21qRXpZTGZ6NjBheGNEb0pM" +
|
|
91
|
+
"awpwN242eEtYSStjSmJBNElsVG9GanBTbGRQbUMreW5PbzdZQU9zWHQ3QVlLWTZHbHowQndVVnpTSnhVKy8rVkZ5Ci9Rcm1ZR053" +
|
|
92
|
+
"bHJRdGRSRUhlUmkwU05LMzJ4MStiT25kZkpQMHNvanVJckRqS3NkQ0x5ZTVDU1pJdnFuYm93d1cKMWpSd1pnVEJSMjlacDJuekNv" +
|
|
93
|
+
"eEpZY1U5VFNRcC80S1p1V05WQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUApORUpvM0dkT2o4UWluc1Y4U2VXcjNVUytIakFm" +
|
|
94
|
+
"QmdOVkhTTUVHREFXZ0JTUE5FSm8zR2RPajhRaW5zVjhTZVdyCjNVUytIakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJ" +
|
|
95
|
+
"YjNEUUVCQ3dVQUE0SUJBUUFCbEJJVDVaZUcKZmdjSzFMT2gxQ045c1R6eE1DTGJ0VFBGRjFOR0dBMTNtQXB1NmoxaDVZRUxiU0tj" +
|
|
96
|
+
"VXFmWHpNblZlQWIwNkh0dQozQ29Db2Urd2o3TE9OVEZPKyt2Qm0yL2lmNkp0L0RVdzFDQUVjTnlxZWg2RVMwTlg4TEpSVlNlMHFk" +
|
|
97
|
+
"VHhQSnVBCkJkT29vOTZpWDg5clJQb3hlZWQxY3BxNWhad2Jla2EzK0NKR1Y3Nml0V3AzNVVwNXJtbVVxcmx5UU9yL1dheDYKaXRv" +
|
|
98
|
+
"c0l6RzBNZmhnVXpVNTFBMlAvaFNuRDNORE1Ydit3VVkvQXZxZ0lMN3U3ZmJES25rdTFHekVLSWtmSDhobQpSczZkOFNDVTg5eHly" +
|
|
99
|
+
"d3pRMFBSODUzaXJIYXMzV3JIVnFhYjNQK3FOd1IwWWlyTDBRazdYdC9xM08xZ3JpTmcyCkJsYmpnM29icEhvOQotLS0tLUVORCBD" +
|
|
100
|
+
"RVJUSUZJQ0FURS0tLS0t",
|
|
101
|
+
},
|
|
102
|
+
"slicer_base64.cer": {
|
|
103
|
+
sha256: "f504dc1c37bc9741b7a95198914e67f3f6f1d917b1e45eaa14b9e84eb7b51ec8",
|
|
104
|
+
base64: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUY3ekNDQk5lZ0F3SUJBZ0lRQm5XSDBPTmY1dE1XNlZyNXhtWXhEREFOQmdr" +
|
|
105
|
+
"cWhraUc5dzBCQVFzRkFEQloKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNoTU1SR2xuYVVObGNuUWdTVzVqTVRNd01RWURW" +
|
|
106
|
+
"UVFERXlwUwpZWEJwWkZOVFRDQlVURk1nUkZZZ1VsTkJJRTFwZUdWa0lGTklRVEkxTmlBeU1ESXdJRU5CTFRFd0hoY05NakV3Ck56" +
|
|
107
|
+
"RXpNREF3TURBd1doY05Nakl3TnpFek1qTTFPVFU1V2pBWk1SY3dGUVlEVlFRRERBNHFMbUpoYldKMWJHRmkKTG1OdmJUQ0NBU0l3" +
|
|
108
|
+
"RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUt4c3pMZGpBS21TMkpscQpaNTI4WkVsM0VxWkd2MWp1UnZ2TTR4" +
|
|
109
|
+
"cCtvcGVNWENzMWpLc2IxV2ZpZUZBL0tqWEJ6NG51ZVpzRkxhYnB5aXBBCjlvUFBkOG9HajhzWjRoOENJYk1iejJHeG1VVktUb1Ra" +
|
|
110
|
+
"OWYzd1lwMHNXRFVQVHhoWEd3L0p6cXk2bWN3cWhvQ08KUWJWck5hUUNxUXVOcElLTWx2UFg1YUsxQTBRL0FzZE4ybXBWdFNHMENw" +
|
|
111
|
+
"cGNSZjVaRTQyelZGUGcxaWVReGdpSAp3NkxXTGlaM0NHcHRueVAxc0JsSUx6VTQ4cXJoMUQwN1ptdUVITDA1cEo3ZFFBc2hzK0Jr" +
|
|
112
|
+
"bFJ6eXpUZ2lVKzFlCitRZVBGYXExRG9zekZlMndMS3Z1UjhPL1JaOGZGS0pzTnlodE80b21jMklTR3ZSb3lrN3FLU3JGUU9jNG1l" +
|
|
113
|
+
"a1kKMDJzSUZHVUNBd0VBQWFPQ0F2RXdnZ0x0TUI4R0ExVWRJd1FZTUJhQUZLU041YjU4ZWVSd0kyMHVLVFN0STFqYwo5VEYvTUIw" +
|
|
114
|
+
"R0ExVWREZ1FXQkJUUDIrTDR0YjJhOGZsMXBPWSt6RmxBNEdOamtqQW5CZ05WSFJFRUlEQWVnZzRxCkxtSmhiV0oxYkdGaUxtTnZi" +
|
|
115
|
+
"WUlNWW1GdFluVnNZV0l1WTI5dE1BNEdBMVVkRHdFQi93UUVBd0lGb0RBZEJnTlYKSFNVRUZqQVVCZ2dyQmdFRkJRY0RBUVlJS3dZ" +
|
|
116
|
+
"QkJRVUhBd0l3UGdZRFZSMGdCRGN3TlRBekJnWm5nUXdCQWdFdwpLVEFuQmdnckJnRUZCUWNDQVJZYmFIUjBjRG92TDNkM2R5NWth" +
|
|
117
|
+
"V2RwWTJWeWRDNWpiMjB2UTFCVE1JR0ZCZ2dyCkJnRUZCUWNCQVFSNU1IY3dKQVlJS3dZQkJRVUhNQUdHR0doMGRIQTZMeTl2WTNO" +
|
|
118
|
+
"d0xtUnBaMmxqWlhKMExtTnYKYlRCUEJnZ3JCZ0VGQlFjd0FvWkRhSFIwY0RvdkwyTmhZMlZ5ZEhNdVpHbG5hV05sY25RdVkyOXRM" +
|
|
119
|
+
"MUpoY0dsawpVMU5NVkV4VFJGWlNVMEZOYVhobFpGTklRVEkxTmpJd01qQkRRUzB4TG1OeWREQUpCZ05WSFJNRUFqQUFNSUlCCmZn" +
|
|
120
|
+
"WUtLd1lCQkFIV2VRSUVBZ1NDQVc0RWdnRnFBV2dBZGdBcGViN3duams1SWZCV2M1OWpwWGZsdmxkOW5HQUsKK1BsTlhTWmNKVjNI" +
|
|
121
|
+
"aEFBQUFYcWVwZEJsQUFBRUF3QkhNRVVDSUcrQVVPdnNqV1VIMDdBK0FaTkNyVVJackxYLwo2MldSVXcrM2FTWlhxSG8vQWlFQW9r" +
|
|
122
|
+
"c1dpMm13bTZrNEZDZW45VThNeTNJbmVEU1lxbnY3K2JoODhLOExrdHdBCmRnQlJvN0QxL1FGNW5GWnR1RGQ0and5a2Vzd2JKOHYz" +
|
|
123
|
+
"bm9oQ21nMysxSXNGNVFBQUFYcWVwZERWQUFBRUF3QkgKTUVVQ0lRRDZLYk5YRWZaT1pnN1MvdURIRlRWY00xczByemw0ZWtQNFhW" +
|
|
124
|
+
"cHlRSHQ5MkFJZ0pVbndQMzlDY1NjTwppZ1dZSm1yeGpCQTMxR25XajE4THVLeURWMWpYSmNRQWRnQkJ5TXF4M3lKR1NoREdvVG9K" +
|
|
125
|
+
"UW9kZVRqR0xHd1ByCjYwdkhhUENRWXBZRzlnQUFBWHFlcGRCRUFBQUVBd0JITUVVQ0lRQ3Y3dWZlY21CVmhwbVF1aUpFV1R6dFkw" +
|
|
126
|
+
"ck4KbnBnWnZnQlVNL3ppVlo0d1VBSWdRQXVHbUM5KzZzbkQwUklud3R0Ly9WdmRZcERYT2RpVVN3ZG4zdXc0NXNjdwpEUVlKS29a" +
|
|
127
|
+
"SWh2Y05BUUVMQlFBRGdnRUJBTEJSMFhuSFNBRm0zODN3TDM0VGcvZnFjdWtwWUFFTGZ4MG9zczVpCnZGUWhERUZndVovUXhjaDcw" +
|
|
128
|
+
"UmZBTnYyK0hETFVKNUJIWmZFRnpPQVZFc2xlalBEL2xPZFZKZVozWGE3azhXZXAKZDJudi9hVmR1UmlUOUc4Q0JFa3MrVWxXKzlW" +
|
|
129
|
+
"NmwvWVB5YThJMWh4bWRLWm5YWlhrSm1kOUJia1A4SndieHNzcApxUEYzRzlkc1Y1RTBab0RTWkZuOXgzSkpIM1QzNUZkSXJ4b0sv" +
|
|
130
|
+
"K3MvK3ZvbzdPbkRPWkdqekZrRjUrWW00ajZ1Cks2dW9vMVRXSmFDOW1NQ2FiNElneXJ5alNiVlNvNDlROUdsdXRvaGR2aEZrNEhD" +
|
|
131
|
+
"SWRmejJ3M0JYM29XdFJ3LzIKdUtLTHlEamRYeWYzVFlqWWFTYmJhblNmRDEycGQwWUsyejAvK0daUHZoMUtiN1E9Ci0tLS0tRU5E" +
|
|
132
|
+
"IENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURyekNDQXBlZ0F3SUJBZ0lRQ0R2Z1ZwQkNS" +
|
|
133
|
+
"ckdoZFdySldaSEhTakFOQmdrcWhraUc5dzBCQVFVRkFEQmgKTVFzd0NRWURWUVFHRXdKVlV6RVZNQk1HQTFVRUNoTU1SR2xuYVVO" +
|
|
134
|
+
"bGNuUWdTVzVqTVJrd0Z3WURWUVFMRXhCMwpkM2N1WkdsbmFXTmxjblF1WTI5dE1TQXdIZ1lEVlFRREV4ZEVhV2RwUTJWeWRDQkhi" +
|
|
135
|
+
"RzlpWVd3Z1VtOXZkQ0JEClFUQWVGdzB3TmpFeE1UQXdNREF3TURCYUZ3MHpNVEV4TVRBd01EQXdNREJhTUdFeEN6QUpCZ05WQkFZ" +
|
|
136
|
+
"VEFsVlQKTVJVd0V3WURWUVFLRXd4RWFXZHBRMlZ5ZENCSmJtTXhHVEFYQmdOVkJBc1RFSGQzZHk1a2FXZHBZMlZ5ZEM1agpiMjB4" +
|
|
137
|
+
"SURBZUJnTlZCQU1URjBScFoybERaWEowSUVkc2IySmhiQ0JTYjI5MElFTkJNSUlCSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FR" +
|
|
138
|
+
"OEFNSUlCQ2dLQ0FRRUE0anZoRVhMZXFLVFRvMWVxVUtLUEMzZVF5YUtsN2hMT2xsc0IKQ1NETUFaT25UakMzVS9kRHhHa0FWNTNp" +
|
|
139
|
+
"alNMZGh3WkFBSUVKenM0Ymc3L2Z6VHR4UnVMV1pzY0ZzM1luRm85NwpuaDZWZmU2M1NLTUkydGF2ZWd3NUJtVi9TbDBmdkJmNHE3" +
|
|
140
|
+
"N3VLTmQwZjNwNG1WbUZhRzVjSXpKTHYwN0E2RnB0CjQzQy9keEMvL0FIMmhkbW9SQkJZTXFsMUdOWFJvcjVINGlkcTlKb3orRWtJ" +
|
|
141
|
+
"WUl2VVg3UTZoTCtocWtwTWZUN1AKVDE5c2RsNmdTemVSbnR3aTVtM09GQnFPYXN2K3piTVVaQmZIV3ltZU1yL3k3dnJUQzBMVXE3" +
|
|
142
|
+
"ZEJNdG9NMU8vNApnZFc3alZnL3RSdm9TU2lpY05veEJOMzNzaGJ5VEFwT0I2anRTajFldFgramtNT3ZKd0lEQVFBQm8yTXdZVEFP" +
|
|
143
|
+
"CkJnTlZIUThCQWY4RUJBTUNBWVl3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVQTk1UU5WYlIKVEx0bThLUGlH" +
|
|
144
|
+
"eHZEbDdJOTBWVXdId1lEVlIwakJCZ3dGb0FVQTk1UU5WYlJUTHRtOEtQaUd4dkRsN0k5MFZVdwpEUVlKS29aSWh2Y05BUUVGQlFB" +
|
|
145
|
+
"RGdnRUJBTXVjTjZwSUV4SUsrdDFFbkU5U3NQVGZyZ1QxZVhrSW95UVkvRXNyCmhNQXR1ZFhIL3ZUQkgxakx1RzJjZW5Ubm1DbXJF" +
|
|
146
|
+
"YlhqY0tDaHpVeUltWk9Na1hEaXF3OGN2cE9wLzJQVjVBZGcKMDZPL25Wc0o4ZFdPNDFQMGptUDZQNmZidEdiZlltYlcwVzVCamZJ" +
|
|
147
|
+
"dHRlcDNTcCtkV09JcldjQkFJKzB0S0lKRgpQbmxVa2lhWTRJQklxRGZ2OE5aNVlCYmVyT2dPelc2c1JCYzRMMG5hNFVVK0tyazJV" +
|
|
148
|
+
"ODg2VUFiM0x1akVWMGxzCllTRVkxUVN0ZUR3c09vQnJwK3V2RlJUcDJJbkJ1VGhzNHBGc2l2OWt1WGNsVnpEQUd5U2o0ZHpwMzBk" +
|
|
149
|
+
"OHRiUWsKQ0FVdzdDMjlDNzlGdjFDNXFmUHJtQUVTcmNpSXhwZzBYNDBLUE1icDFaV1ZiZDQ9Ci0tLS0tRU5EIENFUlRJRklDQVRF" +
|
|
150
|
+
"LS0tLS0K",
|
|
151
|
+
},
|
|
152
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import path from "path";
|
|
|
10
10
|
import { createServer as createHttpServer } from "node:http";
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
12
12
|
import { STLManipulator, SLICER_TYPES, normalizeSlicerType, } from "./stl/stl-manipulator.js";
|
|
13
|
-
import { BambuNetworkBridge } from "./bambu-network-bridge.js";
|
|
13
|
+
import { BambuNetworkBridge, describeBambuNetworkPrintError } from "./bambu-network-bridge.js";
|
|
14
14
|
import { assertBambuNativeAvailable, bambuNativeControlsAvailable, bambuNativeUnavailableReason, interruptNativeOperations, validateBambuNativeMetadata, buildBambuNativeFanCommand, buildBambuNativeTemperatureCommand, printWithBambuNative, sendCommandWithBambuNative, uploadWithBambuNative, } from "./bambu-native.js";
|
|
15
15
|
import { importFileViaBambuConnect } from "./bambu-connect.js";
|
|
16
16
|
import { BlenderMcpBridge } from "./blender-mcp-bridge.js";
|
|
@@ -1229,12 +1229,17 @@ class BambuPrinterMCPServer {
|
|
|
1229
1229
|
this.bambu.assertBedClearance(confirmedStatus, bedClearance);
|
|
1230
1230
|
const amsMapping = stringifyBridgeJson(mapping?.map(value => bridgeAmsMappingValue(value, isDualNozzleModel(printModel))));
|
|
1231
1231
|
const remoteFileName = uniquePrintName(threeMFPath);
|
|
1232
|
+
const checkedName = remoteFileName.replace(/(?:\.gcode)?\.3mf$/i, "");
|
|
1232
1233
|
const params = {
|
|
1233
1234
|
dev_id: devId,
|
|
1234
1235
|
// Firmware can report the task name instead of the uploaded filename.
|
|
1235
1236
|
// Bind either report to this inspected artifact for a later safe resume.
|
|
1236
|
-
task_name:
|
|
1237
|
-
|
|
1237
|
+
task_name: checkedName,
|
|
1238
|
+
// The plug-in names the uploaded file and the printer's job from
|
|
1239
|
+
// project_name, so it carries the same unique checked name, as the
|
|
1240
|
+
// native route already passes; a reused name would overwrite a stored
|
|
1241
|
+
// job and break the resume binding.
|
|
1242
|
+
project_name: checkedName,
|
|
1238
1243
|
preset_name: presetName,
|
|
1239
1244
|
filename: snapshot,
|
|
1240
1245
|
config_filename: snapshot,
|
|
@@ -1309,7 +1314,7 @@ class BambuPrinterMCPServer {
|
|
|
1309
1314
|
this.bambu.recordCheckedJob(devIp, devId, remoteFileName, requirements);
|
|
1310
1315
|
return { ...nativeResult, autoSliced, projectName, plateIndex, useAMS, amsMapping: mapping };
|
|
1311
1316
|
}
|
|
1312
|
-
const
|
|
1317
|
+
const bridgeOptions = {
|
|
1313
1318
|
...this.bridgeOptionsFromArgs(args, "print_3mf_bambu_network"),
|
|
1314
1319
|
beforeDispatch: async (method) => {
|
|
1315
1320
|
if (method !== bridgeMethod)
|
|
@@ -1323,7 +1328,15 @@ class BambuPrinterMCPServer {
|
|
|
1323
1328
|
assertActive(); },
|
|
1324
1329
|
onDispatched: method => { if (method === bridgeMethod)
|
|
1325
1330
|
retainForDispatchedUpload(); },
|
|
1326
|
-
}
|
|
1331
|
+
};
|
|
1332
|
+
let jobUpdates = [];
|
|
1333
|
+
let bridgeResult;
|
|
1334
|
+
if (isLocalBridgePrint) {
|
|
1335
|
+
({ result: bridgeResult, jobUpdates } = await this.bambuNetwork.startLocalPrint({ devId, devIp, username: String(params.username), password, useSsl: params.use_ssl_for_mqtt }, bridgeMethod, { client_job_id: clientJobId, params }, bridgeOptions, assertActive));
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
bridgeResult = await this.bambuNetwork.callWithAgent(bridgeMethod, { client_job_id: clientJobId, params }, bridgeOptions);
|
|
1339
|
+
}
|
|
1327
1340
|
if (typeof bridgeResult === "object" && bridgeResult !== null && bridgeResult.ok === false) {
|
|
1328
1341
|
throw new Error(`FULU BambuNetwork bridge method ${bridgeMethod} failed: ${String(bridgeResult.error || "unknown bridge error")}`);
|
|
1329
1342
|
}
|
|
@@ -1332,7 +1345,10 @@ class BambuPrinterMCPServer {
|
|
|
1332
1345
|
typeof bridgeResult.value === "number" &&
|
|
1333
1346
|
bridgeResult.value !== 0) {
|
|
1334
1347
|
const value = bridgeResult.value;
|
|
1335
|
-
|
|
1348
|
+
const reported = jobUpdates.filter(update => update.code !== 0 || update.msg).slice(-3)
|
|
1349
|
+
.map(update => `status ${update.status}, code ${update.code}${update.msg ? `: ${update.msg}` : ""}`).join("; ");
|
|
1350
|
+
throw new Error(`FULU BambuNetwork bridge method ${bridgeMethod} returned non-zero result ${value}${describeBambuNetworkPrintError(value)}.` +
|
|
1351
|
+
(reported ? ` Plug-in reported: ${reported}.` : ""));
|
|
1336
1352
|
}
|
|
1337
1353
|
assertActive();
|
|
1338
1354
|
this.bambu.recordCheckedJob(devIp, devId, remoteFileName, requirements);
|
|
@@ -1346,6 +1362,7 @@ class BambuPrinterMCPServer {
|
|
|
1346
1362
|
projectName,
|
|
1347
1363
|
plateIndex,
|
|
1348
1364
|
bridgePlateIndex: plateIndex + 1,
|
|
1365
|
+
printerJobName: checkedName,
|
|
1349
1366
|
useAMS,
|
|
1350
1367
|
amsMapping: mapping,
|
|
1351
1368
|
params: redactPrintParams(params),
|
|
@@ -1972,11 +1989,11 @@ class BambuPrinterMCPServer {
|
|
|
1972
1989
|
},
|
|
1973
1990
|
use_printer_filaments: { type: "boolean", description: "When true, and no explicit slicer profile or load_filaments override is provided, use the printer's current or first loaded AMS filament as the slicer filament profile. Template 3MF process settings can still be used at the same time." },
|
|
1974
1991
|
uptodate: { type: "boolean", description: "Refresh 3MF preset configs to match the latest BambuStudio version. Use when slicing downloaded or older 3MF files to prevent stale-config failures." },
|
|
1975
|
-
repetitions: { type: "number", description: "Print N identical copies of the model. Each copy gets its own plate placement. Example: 3 prints three copies." },
|
|
1992
|
+
repetitions: { type: "number", description: "Print N identical copies of the model. Each copy gets its own plate placement. Example: 3 prints three copies. OrcaSlicer copies an STL with --clone-objects; do not combine with clone_objects." },
|
|
1976
1993
|
orient: { type: "boolean", description: "Auto-orient the model for optimal printability (minimize supports, maximize bed adhesion). Recommended for raw STL imports that lack a pre-set orientation." },
|
|
1977
|
-
arrange: { type: "boolean", description: "Auto-arrange all objects on the build plate with optimal spacing
|
|
1994
|
+
arrange: { type: "boolean", description: "Auto-arrange all objects on the build plate with optimal spacing; STL input is arranged by default. Set false only to keep a 3MF's existing layout: an STL that is not already on the plate then fails to slice." },
|
|
1978
1995
|
ensure_on_bed: { type: "boolean", description: "Detect models floating above the bed and lower them onto the build surface. Safety net for imported models with incorrect Z origins." },
|
|
1979
|
-
clone_objects: { type: "string", description: "
|
|
1996
|
+
clone_objects: { type: "string", description: "Clone the loaded model: total copies as a single integer, e.g. '3'. The Bambu-style CLIs count copies per input file and this tool loads one file, so a comma-separated list is rejected by the slicer." },
|
|
1980
1997
|
skip_objects: { type: "string", description: "Skip specific objects during slicing by index. Comma-separated, e.g. '3,5,10'. Useful for multi-object 3MFs where you only want to print some parts." },
|
|
1981
1998
|
load_filaments: { type: "string", description: "Override filament profiles. Semicolon-separated paths to filament JSON configs, e.g. 'pla_basic.json;petg_cf.json'." },
|
|
1982
1999
|
filament_profile: { type: "string", description: "Compatibility alias for load_filaments. Semicolon-separated Orca/Bambu filament profile JSON paths." },
|
|
@@ -2078,7 +2095,7 @@ class BambuPrinterMCPServer {
|
|
|
2078
2095
|
nozzle_type: { type: "string", enum: [...VALID_NOZZLE_TYPES], description: "Installed nozzle material, used when the 3MF must be auto-sliced (default: BAMBU_NOZZLE_TYPE, else the preset's stock nozzle)." },
|
|
2079
2096
|
nozzle_volume_type: { type: "string", enum: [...VALID_NOZZLE_VOLUME_TYPES], description: "Installed nozzle flow type, used when the 3MF must be auto-sliced (default: BAMBU_NOZZLE_VOLUME_TYPE, else standard)." },
|
|
2080
2097
|
plate_index: { type: "number", description: "Zero-based plate index to print from the sliced 3MF; converted to FULU's one-based PrintParams plate_index." },
|
|
2081
|
-
project_name: { type: "string", description: "Optional project name
|
|
2098
|
+
project_name: { type: "string", description: "Optional project name returned in the result; defaults to the 3MF filename without extension. The printer job and stored file use the server's unique checked name so resume_print can bind to the inspected file." },
|
|
2082
2099
|
preset_name: { type: "string", description: "Optional preset name sent in FULU PrintParams; defaults to project plus one-based plate index." },
|
|
2083
2100
|
task_name: { type: "string", description: "Optional project label when project_name is omitted. The submitted task name uses a unique inspected-job identity for safe resume." },
|
|
2084
2101
|
config_filename: { type: "string", description: "Optional config 3MF path for cloud print; defaults to the same 3MF path." },
|
|
@@ -2122,7 +2139,7 @@ class BambuPrinterMCPServer {
|
|
|
2122
2139
|
type: "object",
|
|
2123
2140
|
properties: {
|
|
2124
2141
|
save_path: { type: "string", description: "Optional absolute path to write the JPEG to disk. If omitted, only the base64 payload is returned." },
|
|
2125
|
-
timeout_ms: { type: "number", description: "Max ms to wait for a
|
|
2142
|
+
timeout_ms: { type: "number", description: "Max ms to wait for a live frame (default 8000). A1/P1 cameras first replay a buffered frame, which is skipped, so a live frame takes about 3 s, longer on cold start." },
|
|
2126
2143
|
bambu_model: { type: "string", description: "Printer model. Used to route to the correct protocol or fail fast on unsupported models. Defaults to BAMBU_MODEL." },
|
|
2127
2144
|
experimental: { type: "boolean", description: "Deprecated and ignored. Earlier this flag let callers probe H2 series via the A1/P1 TCP-on-6000 path; live testing on an H2S confirmed H2 uses RTSP instead, so the flag has no effect now." },
|
|
2128
2145
|
ffmpeg_path: { type: "string", description: "Override path to the ffmpeg binary used by the RTSP path. Defaults to FFMPEG_PATH or ffmpeg via $PATH. Per-call overrides require MCP_ALLOW_EXECUTABLE_ARG=1. Required only for the RTSP transport (X1, P2S, H2 series, X2D)." },
|
package/dist/printers/bambu.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export type PrintPreflight = {
|
|
|
33
33
|
model: string;
|
|
34
34
|
notices: string[];
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Split the A1/P1 camera stream on port 6000 into JPEG frames. Each frame is
|
|
38
|
+
* a 16-byte header (payload size LE, itrack, flags, reserved) and the JPEG.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cameraFrameParser(onFrame: (jpeg: Buffer) => void, onError: (error: Error) => void): (chunk: Buffer) => void;
|
|
36
41
|
export declare class BambuImplementation {
|
|
37
42
|
private readonly confirm?;
|
|
38
43
|
private printerStore;
|
|
@@ -218,8 +223,13 @@ export declare class BambuImplementation {
|
|
|
218
223
|
*/
|
|
219
224
|
private fetchRtspCameraFrame;
|
|
220
225
|
/**
|
|
221
|
-
* Open the TLS-on-6000 socket, send the 80-byte auth packet, and
|
|
222
|
-
* a
|
|
226
|
+
* Open the TLS-on-6000 socket, send the 80-byte auth packet, and return
|
|
227
|
+
* a live JPEG frame. The printer first replays the last frame it buffered,
|
|
228
|
+
* which after an idle period can show the chamber as it was long ago (a
|
|
229
|
+
* finished part still on the bed), so that frame and any byte-identical
|
|
230
|
+
* repeat of it are discarded and the next frame returned; live frames always
|
|
231
|
+
* differ by sensor noise. If no live frame arrives in time, this fails rather
|
|
232
|
+
* than return a possibly stale image.
|
|
223
233
|
*/
|
|
224
234
|
private fetchTcpCameraFrame;
|
|
225
235
|
/**
|
package/dist/printers/bambu.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readSafe3mfArchive } from "../safety/archive.js";
|
|
|
9
9
|
import { inspectPrintFile } from "../safety/print-file.js";
|
|
10
10
|
import { isDualNozzleModel, normalizeModel, validateTemperature } from "../safety/limits.js";
|
|
11
11
|
import { bridgeAmsMapping2Entry, bridgeAmsMappingValue, externalTrayForNozzle, describeAmsTrayRange } from "../ams-mapping.js";
|
|
12
|
-
import { describeHmsKey, hiddenHmsKeys, hiddenHmsNotices, reportedHmsKey, validatePrinterState, manualHeatingRequirements } from "../safety/printer-state.js";
|
|
12
|
+
import { describeHmsKey, hiddenHmsKeys, hiddenHmsNotices, reportedHmsKey, unchippedNozzles, validatePrinterState, manualHeatingRequirements } from "../safety/printer-state.js";
|
|
13
13
|
import { withPrinterOperation, withPrintSnapshot, uniquePrintName, normalizedRemotePath, cancelPendingPrinterOperations } from "../safety/artifact.js";
|
|
14
14
|
import { Client as FTPClient } from "basic-ftp";
|
|
15
15
|
import { BambuPrinter } from "bambu-js";
|
|
@@ -277,6 +277,7 @@ class BambuClientStore {
|
|
|
277
277
|
}
|
|
278
278
|
async waitForInitialReport(host, serial, token, timeoutMs = 4000) {
|
|
279
279
|
const key = getPrinterKey(host, serial, token);
|
|
280
|
+
const deadline = Date.now() + timeoutMs + 500;
|
|
280
281
|
const existing = this.reportSnapshots.get(key);
|
|
281
282
|
if (!existing || Object.keys(existing).length === 0) {
|
|
282
283
|
// No MQTT data yet — wait for the first push.
|
|
@@ -298,8 +299,12 @@ class BambuClientStore {
|
|
|
298
299
|
// (gcode_state, ams, hms, temperatures, fans, etc.) arrives afterward
|
|
299
300
|
// and gets merged into reportSnapshots via incremental update.
|
|
300
301
|
// This settle runs regardless of whether data arrived before or during
|
|
301
|
-
// the promise race above, so the merge has time to complete.
|
|
302
|
+
// the promise race above, so the merge has time to complete. A busy host
|
|
303
|
+
// can take longer, which returned the sparse report as status UNKNOWN,
|
|
304
|
+
// so keep waiting for the full status within the same bound.
|
|
302
305
|
await sleep(500);
|
|
306
|
+
while (typeof this.reportSnapshots.get(key)?.gcode_state !== "string" && Date.now() < deadline)
|
|
307
|
+
await sleep(50);
|
|
303
308
|
return this.reportSnapshots.get(key) || null;
|
|
304
309
|
}
|
|
305
310
|
async getPrinter(host, serial, token) {
|
|
@@ -382,6 +387,37 @@ class BambuClientStore {
|
|
|
382
387
|
this.initialReportResolvers.clear();
|
|
383
388
|
}
|
|
384
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Split the A1/P1 camera stream on port 6000 into JPEG frames. Each frame is
|
|
392
|
+
* a 16-byte header (payload size LE, itrack, flags, reserved) and the JPEG.
|
|
393
|
+
*/
|
|
394
|
+
export function cameraFrameParser(onFrame, onError) {
|
|
395
|
+
let buffered = Buffer.alloc(0);
|
|
396
|
+
let failed = false;
|
|
397
|
+
return chunk => {
|
|
398
|
+
if (failed)
|
|
399
|
+
return;
|
|
400
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
401
|
+
while (buffered.length >= 16) {
|
|
402
|
+
const payloadSize = buffered.readUInt32LE(0);
|
|
403
|
+
if (payloadSize <= 0 || payloadSize > 5000000) {
|
|
404
|
+
failed = true;
|
|
405
|
+
onError(new Error(`camera_snapshot: unreasonable payload size ${payloadSize} from header; auth likely failed.`));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (buffered.length < 16 + payloadSize)
|
|
409
|
+
return;
|
|
410
|
+
const jpeg = buffered.subarray(16, 16 + payloadSize);
|
|
411
|
+
buffered = buffered.subarray(16 + payloadSize);
|
|
412
|
+
if (jpeg[0] !== 0xff || jpeg[1] !== 0xd8) {
|
|
413
|
+
failed = true;
|
|
414
|
+
onError(new Error(`camera_snapshot: payload does not start with JPEG SOI (FF D8); got ${jpeg[0].toString(16)} ${jpeg[1].toString(16)}.`));
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
onFrame(Buffer.from(jpeg));
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
385
421
|
export class BambuImplementation {
|
|
386
422
|
constructor(confirm) {
|
|
387
423
|
this.confirm = confirm;
|
|
@@ -392,8 +428,14 @@ export class BambuImplementation {
|
|
|
392
428
|
async confirmHardwareAction(message, physicalCheck = false) {
|
|
393
429
|
if (!physicalCheck && process.env.BAMBU_REQUIRE_CONFIRMATION === "0")
|
|
394
430
|
return;
|
|
395
|
-
if (!this.confirm
|
|
396
|
-
throw new Error("Hardware safety confirmation
|
|
431
|
+
if (!this.confirm) {
|
|
432
|
+
throw new Error("Hardware safety confirmation is unavailable. Use an MCP client with elicitation support. No command was sent.");
|
|
433
|
+
}
|
|
434
|
+
if (!(await this.confirm(message))) {
|
|
435
|
+
throw new Error("Hardware safety confirmation was declined or cancelled in the MCP client. No command was sent. " + (physicalCheck
|
|
436
|
+
? "This confirmation covers a physical check at the printer and always needs a person to accept it."
|
|
437
|
+
: "Some clients decline prompts automatically, for example in a non-interactive permission mode. " +
|
|
438
|
+
"A deliberately headless setup that approves jobs another way can set BAMBU_REQUIRE_CONFIRMATION=0 to skip ordinary print and heating prompts."));
|
|
397
439
|
}
|
|
398
440
|
}
|
|
399
441
|
finishedJobIdentity(status) {
|
|
@@ -408,13 +450,16 @@ export class BambuImplementation {
|
|
|
408
450
|
const finishedJob = this.finishedJobIdentity(status);
|
|
409
451
|
const cleared = this.clearedErrors.get(serial);
|
|
410
452
|
const notices = hiddenHmsNotices(status?.raw, serial, inspection.model);
|
|
453
|
+
const unchipped = unchippedNozzles(status?.raw);
|
|
411
454
|
await this.confirmHardwareAction(`Start a checked print on ${inspection.model.toUpperCase()} (${serial})? Nozzles: ${inspection.nozzleDiameters.join(", ")} mm. ` +
|
|
412
455
|
`Materials: ${inspection.materials.join(", ")}. Peak targets: nozzle ${inspection.maxNozzleTemperature}°C, bed ${inspection.maxBedTemperature}°C, chamber ${inspection.maxChamberTemperature}°C. ` +
|
|
413
456
|
`File SHA-256: ${inspection.sha256}. Confirm the physical spool labels and that the build plate is clear.` +
|
|
414
457
|
(finishedJob !== undefined ? " The printer reports FINISH: remove the previous part and debris before confirming." : "") +
|
|
415
458
|
(cleared ? ` Previously cleared hardware codes: ${cleared.map(describeHmsKey).join(", ")}. Confirm their physical causes have been resolved.` : "") +
|
|
416
459
|
(notices.length ? ` The printer also reports ${notices.join(", ")}, which Bambu lists for this model without text and never shows as a fault; ` +
|
|
417
|
-
`${notices.length === 1 ? "it does" : "they do"} not block printing.` : "")
|
|
460
|
+
`${notices.length === 1 ? "it does" : "they do"} not block printing.` : "") +
|
|
461
|
+
(unchipped.length ? ` Printer ${unchipped.length === 1 ? `nozzle ${unchipped[0]} reports` : `nozzles ${unchipped.join(" and ")} report`} no identity chip, ` +
|
|
462
|
+
`so ${unchipped.length === 1 ? "its" : "their"} diameter and type are the printer's configured settings; confirm they match the installed ${unchipped.length === 1 ? "hotend" : "hotends"}.` : ""), finishedJob !== undefined || !!cleared);
|
|
418
463
|
return { finishedJob, serial, model: inspection.model, notices };
|
|
419
464
|
}
|
|
420
465
|
/** Stop a print whose confirmed situation changed: a newly finished job, or a hidden HMS notice the preflight did not name. */
|
|
@@ -1609,10 +1654,15 @@ export class BambuImplementation {
|
|
|
1609
1654
|
});
|
|
1610
1655
|
}
|
|
1611
1656
|
/**
|
|
1612
|
-
* Open the TLS-on-6000 socket, send the 80-byte auth packet, and
|
|
1613
|
-
* a
|
|
1657
|
+
* Open the TLS-on-6000 socket, send the 80-byte auth packet, and return
|
|
1658
|
+
* a live JPEG frame. The printer first replays the last frame it buffered,
|
|
1659
|
+
* which after an idle period can show the chamber as it was long ago (a
|
|
1660
|
+
* finished part still on the bed), so that frame and any byte-identical
|
|
1661
|
+
* repeat of it are discarded and the next frame returned; live frames always
|
|
1662
|
+
* differ by sensor noise. If no live frame arrives in time, this fails rather
|
|
1663
|
+
* than return a possibly stale image.
|
|
1614
1664
|
*/
|
|
1615
|
-
async fetchTcpCameraFrame(host, token, timeoutMs) {
|
|
1665
|
+
async fetchTcpCameraFrame(host, token, timeoutMs, port = 6000) {
|
|
1616
1666
|
const tls = await import("node:tls");
|
|
1617
1667
|
const auth = Buffer.alloc(80, 0);
|
|
1618
1668
|
auth.writeUInt32LE(0x40, 0); // payload size
|
|
@@ -1621,11 +1671,9 @@ export class BambuImplementation {
|
|
|
1621
1671
|
auth.write("bblp", 16, 4, "ascii");
|
|
1622
1672
|
auth.write(token, 48, Math.min(32, Buffer.byteLength(token, "ascii")), "ascii");
|
|
1623
1673
|
return new Promise((resolve, reject) => {
|
|
1624
|
-
const chunks = [];
|
|
1625
|
-
let totalLen = 0;
|
|
1626
|
-
const FRAME_HEADER_BYTES = 16;
|
|
1627
|
-
let payloadSize = null;
|
|
1628
1674
|
let settled = false;
|
|
1675
|
+
let frames = 0;
|
|
1676
|
+
let buffered;
|
|
1629
1677
|
const finish = (err, jpeg) => {
|
|
1630
1678
|
if (settled)
|
|
1631
1679
|
return;
|
|
@@ -1639,44 +1687,29 @@ export class BambuImplementation {
|
|
|
1639
1687
|
else
|
|
1640
1688
|
reject(new Error("camera_snapshot: ended without jpeg payload"));
|
|
1641
1689
|
};
|
|
1642
|
-
const
|
|
1690
|
+
const unfinished = (reason) => new Error(frames
|
|
1691
|
+
? `camera_snapshot: ${reason} after only the printer's buffered frame, which may be stale; no live frame arrived. Retry, or raise timeout_ms.`
|
|
1692
|
+
: `camera_snapshot: ${reason} before a full JPEG frame arrived`);
|
|
1693
|
+
const timer = setTimeout(() => finish(unfinished(`timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1694
|
+
const parse = cameraFrameParser(jpeg => {
|
|
1695
|
+
frames++;
|
|
1696
|
+
if (!buffered)
|
|
1697
|
+
buffered = jpeg;
|
|
1698
|
+
else if (!jpeg.equals(buffered))
|
|
1699
|
+
finish(null, jpeg);
|
|
1700
|
+
}, error => finish(error));
|
|
1643
1701
|
const socket = tls.connect({
|
|
1644
1702
|
host,
|
|
1645
|
-
port
|
|
1703
|
+
port,
|
|
1646
1704
|
rejectUnauthorized: false,
|
|
1647
1705
|
// The printer uses TLS for confidentiality but presents a self-signed cert.
|
|
1648
1706
|
// Same trust posture as the FTPS path (basic-ftp with rejectUnauthorized: false).
|
|
1649
1707
|
}, () => {
|
|
1650
1708
|
socket.write(auth);
|
|
1651
1709
|
});
|
|
1652
|
-
socket.on("data",
|
|
1653
|
-
chunks.push(data);
|
|
1654
|
-
totalLen += data.length;
|
|
1655
|
-
if (payloadSize === null && totalLen >= FRAME_HEADER_BYTES) {
|
|
1656
|
-
const merged = Buffer.concat(chunks, totalLen);
|
|
1657
|
-
payloadSize = merged.readUInt32LE(0);
|
|
1658
|
-
if (payloadSize <= 0 || payloadSize > 5000000) {
|
|
1659
|
-
finish(new Error(`camera_snapshot: unreasonable payload size ${payloadSize} from header; auth likely failed.`));
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
|
-
// Reset chunk list to remaining bytes after the header.
|
|
1663
|
-
const remainder = merged.subarray(FRAME_HEADER_BYTES);
|
|
1664
|
-
chunks.length = 0;
|
|
1665
|
-
chunks.push(remainder);
|
|
1666
|
-
totalLen = remainder.length;
|
|
1667
|
-
}
|
|
1668
|
-
if (payloadSize !== null && totalLen >= payloadSize) {
|
|
1669
|
-
const merged = Buffer.concat(chunks, totalLen);
|
|
1670
|
-
const jpeg = merged.subarray(0, payloadSize);
|
|
1671
|
-
if (jpeg[0] !== 0xff || jpeg[1] !== 0xd8) {
|
|
1672
|
-
finish(new Error(`camera_snapshot: payload does not start with JPEG SOI (FF D8); got ${jpeg[0].toString(16)} ${jpeg[1].toString(16)}.`));
|
|
1673
|
-
return;
|
|
1674
|
-
}
|
|
1675
|
-
finish(null, jpeg);
|
|
1676
|
-
}
|
|
1677
|
-
});
|
|
1710
|
+
socket.on("data", parse);
|
|
1678
1711
|
socket.on("error", (err) => finish(err));
|
|
1679
|
-
socket.on("end", () => finish(
|
|
1712
|
+
socket.on("end", () => finish(unfinished("connection ended")));
|
|
1680
1713
|
});
|
|
1681
1714
|
}
|
|
1682
1715
|
/**
|
|
@@ -39,6 +39,8 @@ export interface ValidatedPrinterState {
|
|
|
39
39
|
reportedNozzles: ReportedNozzle[];
|
|
40
40
|
filaments: ReportedFilament[];
|
|
41
41
|
}
|
|
42
|
+
/** Nozzles whose reported diameter and type are configured settings rather than chip readings. */
|
|
43
|
+
export declare function unchippedNozzles(raw: unknown): number[];
|
|
42
44
|
/** The one key format for a reported entry, shared by the gate and clear_hms_errors. */
|
|
43
45
|
export declare function reportedHmsKey(entry: unknown): string;
|
|
44
46
|
/** Bambu's display form of a cleared-code key such as "hms:83887616:131184"; other keys unchanged. */
|
|
@@ -128,6 +128,41 @@ function nozzleTypeAndFlow(value) {
|
|
|
128
128
|
const type = token(value);
|
|
129
129
|
return { type: ["stainlesssteel", "hardenedsteel", "tungstencarbide", "brass"].includes(type) ? type : undefined };
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* A nozzle without an identity chip reports serial "N/A" and keeps the
|
|
133
|
+
* unreliable bit set permanently (stock H2D HS01 nozzles), so its diameter
|
|
134
|
+
* and type are configured settings, as on single-nozzle printers.
|
|
135
|
+
*/
|
|
136
|
+
function hasNoIdentityChip(entry) {
|
|
137
|
+
return typeof entry?.sn === "string" && entry.sn.trim().toUpperCase() === "N/A";
|
|
138
|
+
}
|
|
139
|
+
/** Bit 0 (information unreliable) is accepted only from a nozzle without an identity chip. */
|
|
140
|
+
function unreliableWithoutChip(entry, stat) {
|
|
141
|
+
return (stat & 1) !== 0 && hasNoIdentityChip(entry);
|
|
142
|
+
}
|
|
143
|
+
/** Nozzles whose reported diameter and type are configured settings rather than chip readings. */
|
|
144
|
+
export function unchippedNozzles(raw) {
|
|
145
|
+
const info = raw?.device?.nozzle?.info;
|
|
146
|
+
if (!Array.isArray(info))
|
|
147
|
+
return [];
|
|
148
|
+
return info.flatMap(entry => {
|
|
149
|
+
const index = finiteNumber(entry?.id);
|
|
150
|
+
const stat = finiteNumber(entry?.stat);
|
|
151
|
+
return index !== undefined && Number.isInteger(index) && index >= 0 && index < 16 &&
|
|
152
|
+
stat !== undefined && Number.isInteger(stat) && unreliableWithoutChip(entry, stat) ? [index] : [];
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const loggedUnchippedNozzles = new Set();
|
|
156
|
+
/** Name each unchipped nozzle a check accepted once per printer on stderr, so headless runs still record it. */
|
|
157
|
+
function logUnchippedNozzles(raw, serial) {
|
|
158
|
+
for (const index of unchippedNozzles(raw)) {
|
|
159
|
+
const key = `${serial}:${index}`;
|
|
160
|
+
if (loggedUnchippedNozzles.has(key))
|
|
161
|
+
continue;
|
|
162
|
+
loggedUnchippedNozzles.add(key);
|
|
163
|
+
console.error(`Printer ${serial || "(unknown serial)"} nozzle ${index} reports no identity chip; its diameter and type are the printer's configured settings.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
131
166
|
function reportedNozzles(raw) {
|
|
132
167
|
const info = raw.device?.nozzle?.info;
|
|
133
168
|
const output = [];
|
|
@@ -141,9 +176,13 @@ function reportedNozzles(raw) {
|
|
|
141
176
|
if (index === undefined || !Number.isInteger(index) || index < 0 || diameter === undefined || diameter <= 0)
|
|
142
177
|
throw new Error("Printer reported malformed nozzle identity or diameter.");
|
|
143
178
|
if (entry.stat !== undefined) {
|
|
179
|
+
// BambuStudio DevNozzle: bit 0 set means the information is unreliable;
|
|
180
|
+
// bits 1-2 are 0 normal, 1 abnormal, 2 unknown.
|
|
144
181
|
const stat = finiteNumber(entry.stat);
|
|
145
|
-
if (stat === undefined || !Number.isInteger(stat) || stat < 0 || (stat &
|
|
182
|
+
if (stat === undefined || !Number.isInteger(stat) || stat < 0 || (stat & 6) !== 0)
|
|
146
183
|
throw new Error(`Printer nozzle ${index} information is unreliable, abnormal or unknown.`);
|
|
184
|
+
if ((stat & 1) !== 0 && !unreliableWithoutChip(entry, stat))
|
|
185
|
+
throw new Error(`Printer nozzle ${index} information is unreliable; refresh the nozzle information on the printer.`);
|
|
147
186
|
}
|
|
148
187
|
if (output.some(nozzle => nozzle.index === index))
|
|
149
188
|
throw new Error("Printer reported ambiguous duplicate nozzle indices.");
|
|
@@ -420,6 +459,8 @@ export function validatePrinterState(status, requirements) {
|
|
|
420
459
|
filaments.push({ position, material, trayIndex, nozzleMin, nozzleMax });
|
|
421
460
|
}
|
|
422
461
|
}
|
|
462
|
+
if (nozzles.length)
|
|
463
|
+
logUnchippedNozzles(raw, identity.observedSerial ?? String(status.serial ?? ""));
|
|
423
464
|
return { model, reportedNozzles: nozzles, filaments };
|
|
424
465
|
}
|
|
425
466
|
/** Manual M104 cannot remap filament slots. Validate the material already at the nozzle. */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface CachedProfile {
|
|
2
|
+
subPath: string;
|
|
3
|
+
data: Record<string, unknown>;
|
|
4
|
+
}
|
|
5
|
+
/** Read raw source diffs, not merged profiles: inheritance/includes still use the shared resolver. */
|
|
6
|
+
export declare function decodeOrcaBblCache(bytes: Buffer): CachedProfile[];
|
|
7
|
+
/** Expand only the selected installation's cache in the owning server's private temp directory. */
|
|
8
|
+
export declare function materializeOrcaProfilesRoot(profilesRoot: string, tempDir: string): Promise<string>;
|
|
9
|
+
interface CliProfiles {
|
|
10
|
+
settingsArg?: string;
|
|
11
|
+
filamentPaths: string[];
|
|
12
|
+
}
|
|
13
|
+
/** Orca v2 cache builds resolve User presets by loaded file identity, not just inherits. */
|
|
14
|
+
export declare function stageOrcaCliProfiles(profilesRoot: string, tempDir: string, bundle: CliProfiles): Promise<CliProfiles & {
|
|
15
|
+
extraArgs: string[];
|
|
16
|
+
dataDir: string;
|
|
17
|
+
}>;
|
|
18
|
+
export {};
|