bambu-printer-mcp 1.1.26 → 1.1.28
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 +4 -0
- package/README.md +43 -8
- package/dist/bambu-native.d.ts +55 -0
- package/dist/bambu-native.js +212 -13
- package/dist/index.js +154 -26
- package/dist/printers/bambu.d.ts +29 -2
- package/dist/printers/bambu.js +45 -12
- package/dist/safety/hidden-hms.d.ts +32 -0
- package/dist/safety/hidden-hms.js +405 -0
- package/dist/safety/print-file.js +54 -4
- package/dist/safety/printer-state.d.ts +9 -0
- package/dist/safety/printer-state.js +80 -13
- package/dist/slicer/profile-files.d.ts +2 -0
- package/dist/slicer/profile-files.js +24 -0
- package/dist/slicer/profile-flatten.js +27 -13
- package/dist/stl/stl-manipulator.d.ts +1 -0
- package/dist/stl/stl-manipulator.js +6 -8
- package/native/bambu-native-print.cpp +100 -26
- package/package.json +7 -5
- package/scripts/build-bambu-native.sh +53 -0
- package/scripts/setup-bambu-native.mjs +372 -0
- package/src/bambu-native.ts +229 -14
- package/src/index.ts +152 -25
- package/src/printers/bambu.ts +51 -12
- package/src/safety/hidden-hms.ts +406 -0
- package/src/safety/print-file.ts +43 -4
- package/src/safety/printer-state.ts +85 -13
- package/src/slicer/profile-files.ts +21 -0
- package/src/slicer/profile-flatten.ts +34 -11
- package/src/stl/stl-manipulator.ts +7 -10
- package/scripts/build-bambu-native.zsh +0 -33
|
@@ -27,6 +27,7 @@ import fs from "node:fs/promises";
|
|
|
27
27
|
import { existsSync, realpathSync } from "node:fs";
|
|
28
28
|
import path from "node:path";
|
|
29
29
|
import crypto from "node:crypto";
|
|
30
|
+
import { profileJsonFiles } from './profile-files.js';
|
|
30
31
|
/* -------------------------------------------------------------------------- */
|
|
31
32
|
/* Indexing */
|
|
32
33
|
/* -------------------------------------------------------------------------- */
|
|
@@ -48,19 +49,7 @@ async function buildNameIndex(profilesRoot, vendor, userProfileRoots = []) {
|
|
|
48
49
|
...userProfileRoots.flatMap(root => ['process', 'filament'].map(sub => path.join(root, sub))),
|
|
49
50
|
];
|
|
50
51
|
for (const dir of directories) {
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
entries = await fs.readdir(dir);
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Vendor or subdir missing -- skip silently; flatten will fail later
|
|
57
|
-
// with a precise "name not found" error.
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
for (const entry of entries) {
|
|
61
|
-
if (!entry.endsWith(".json"))
|
|
62
|
-
continue;
|
|
63
|
-
const filePath = path.join(dir, entry);
|
|
52
|
+
for (const filePath of profileJsonFiles(dir)) {
|
|
64
53
|
let raw;
|
|
65
54
|
try {
|
|
66
55
|
raw = await fs.readFile(filePath, "utf8");
|
|
@@ -239,6 +228,29 @@ function enforceMatchingNozzles(arr) {
|
|
|
239
228
|
}
|
|
240
229
|
}
|
|
241
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* BambuStudio picks each per-variant machine, process, and filament value
|
|
233
|
+
* (e.g. filament_max_volumetric_speed) by "<extruder_type> <nozzle_volume_type>"
|
|
234
|
+
* among the variants each extruder declares in extruder_variant_list. A flow
|
|
235
|
+
* type the preset doesn't declare has no values to pick, so refuse it instead
|
|
236
|
+
* of labelling a job for a nozzle it wasn't sliced for.
|
|
237
|
+
*/
|
|
238
|
+
function assertNozzleVariantDeclared(flat, volumeType, machineLeaf) {
|
|
239
|
+
const strings = (key) => Array.isArray(flat[key]) ? flat[key].map(String) : [];
|
|
240
|
+
const extruderTypes = strings("extruder_type");
|
|
241
|
+
const declaredLists = strings("extruder_variant_list");
|
|
242
|
+
const count = flat["nozzle_volume_type"].length;
|
|
243
|
+
for (let i = 0; i < count; i++) {
|
|
244
|
+
const extruderType = extruderTypes[i] ?? extruderTypes[0];
|
|
245
|
+
const declared = (declaredLists[i] ?? declaredLists[0] ?? "").split(",").map((v) => v.trim()).filter(Boolean);
|
|
246
|
+
const matches = (variant) => extruderType ? variant === `${extruderType} ${volumeType}` : variant.endsWith(` ${volumeType}`);
|
|
247
|
+
// Presets without variant declarations only have Standard values.
|
|
248
|
+
if (declared.length === 0 ? volumeType === "Standard" : declared.some(matches))
|
|
249
|
+
continue;
|
|
250
|
+
throw new Error(`Slicing for a ${volumeType} nozzle is refused: "${machineLeaf}" declares no ${volumeType} variant ` +
|
|
251
|
+
`for extruder ${i + 1} (declared: ${declared.join(", ") || "none"}).`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
242
254
|
/** Best-effort extruder count for fallback nozzle_volume_type sizing. */
|
|
243
255
|
/** BambuStudio's built-in filament_colour default. */
|
|
244
256
|
export const DEFAULT_FILAMENT_COLOUR = "#00AE42";
|
|
@@ -556,6 +568,8 @@ export async function flattenForCli(opts) {
|
|
|
556
568
|
});
|
|
557
569
|
// CLI-specific post-processing on machine profile only.
|
|
558
570
|
deriveNozzleVolumeType(machineFlat, opts.nozzleVolumeType);
|
|
571
|
+
if (opts.nozzleVolumeType)
|
|
572
|
+
assertNozzleVariantDeclared(machineFlat, opts.nozzleVolumeType, opts.machineLeaf);
|
|
559
573
|
applyMachineModelBedMetadata(machineFlat, machineIndex);
|
|
560
574
|
// A standalone custom machine can accompany process/filament profiles that
|
|
561
575
|
// need BBL resolution; do not impose bundled model config on that machine.
|
|
@@ -10,6 +10,7 @@ import { EventEmitter } from 'events';
|
|
|
10
10
|
import * as crypto from 'crypto';
|
|
11
11
|
import { execFile } from 'child_process';
|
|
12
12
|
import { flattenForCli, detectProfilesRoot, resolveBblMachineProfile } from '../slicer/profile-flatten.js';
|
|
13
|
+
import { profileJsonFiles } from '../slicer/profile-files.js';
|
|
13
14
|
const readFileAsync = promisify(fs.readFile);
|
|
14
15
|
const writeFileAsync = promisify(fs.writeFile);
|
|
15
16
|
const BAMBU_PROFILE_ROOTS = [
|
|
@@ -134,10 +135,10 @@ export class STLManipulator extends EventEmitter {
|
|
|
134
135
|
return undefined;
|
|
135
136
|
}
|
|
136
137
|
for (const root of roots) {
|
|
137
|
-
const candidate = path.join(root, category
|
|
138
|
-
|
|
138
|
+
const candidate = profileJsonFiles(path.join(root, category))
|
|
139
|
+
.find(file => path.basename(file) === `${profileName}.json`);
|
|
140
|
+
if (candidate)
|
|
139
141
|
return candidate;
|
|
140
|
-
}
|
|
141
142
|
}
|
|
142
143
|
return undefined;
|
|
143
144
|
}
|
|
@@ -148,11 +149,7 @@ export class STLManipulator extends EventEmitter {
|
|
|
148
149
|
if (!fs.existsSync(filamentDir)) {
|
|
149
150
|
continue;
|
|
150
151
|
}
|
|
151
|
-
for (const
|
|
152
|
-
if (!entry.endsWith('.json')) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const filePath = path.join(filamentDir, entry);
|
|
152
|
+
for (const filePath of profileJsonFiles(filamentDir)) {
|
|
156
153
|
try {
|
|
157
154
|
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
158
155
|
const filamentId = typeof parsed?.filament_id === 'string' ? parsed.filament_id.trim() : '';
|
|
@@ -408,6 +405,7 @@ export class STLManipulator extends EventEmitter {
|
|
|
408
405
|
userProfileRoots: configuredBambuProfileDirs(),
|
|
409
406
|
tempDir: this.tempDir,
|
|
410
407
|
bedType: this.resolveBambuStudioBedType(bambuOptions?.bedType),
|
|
408
|
+
nozzleVolumeType: bambuOptions?.nozzleVolumeType,
|
|
411
409
|
filamentColours: bambuOptions?.filamentColours ?? bundle.filamentColours,
|
|
412
410
|
projectTowerPosition: bundle.projectTowerPosition,
|
|
413
411
|
sourceProfiles: {
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
#include <sstream>
|
|
15
15
|
#include <string>
|
|
16
16
|
#include <sys/stat.h>
|
|
17
|
+
#if !defined(__APPLE__)
|
|
18
|
+
#include <pwd.h>
|
|
19
|
+
#include <unistd.h>
|
|
20
|
+
#endif
|
|
17
21
|
#include <thread>
|
|
18
22
|
|
|
19
23
|
// This small adapter intentionally mirrors the public C++ ABI declarations
|
|
@@ -111,13 +115,37 @@ void outputLine(const std::string &line) {
|
|
|
111
115
|
std::cout << line << std::endl;
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
// End a one-shot run once the plug-in's own cleanup (destroy_agent) is done.
|
|
119
|
+
// Its worker threads can outlive that cleanup; process-exit destructors and
|
|
120
|
+
// dlclose then crashed successful command, upload and probe runs with SIGSEGV
|
|
121
|
+
// on Linux. Flush the protocol output and let the OS reclaim the rest.
|
|
122
|
+
[[noreturn]] void exitNow(int code) {
|
|
123
|
+
std::cout.flush();
|
|
124
|
+
std::cerr.flush();
|
|
125
|
+
std::_Exit(code);
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
std::string envOr(const char *name, const std::string &fallback = {}) {
|
|
115
129
|
const char *value = std::getenv(name);
|
|
116
130
|
return value && *value ? std::string(value) : fallback;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
std::string homeDir() {
|
|
134
|
+
#if defined(__APPLE__)
|
|
120
135
|
return envOr("HOME", "/Users/Shared");
|
|
136
|
+
#else
|
|
137
|
+
// Match Node's os.homedir() so the server and helper agree without HOME.
|
|
138
|
+
const std::string home = envOr("HOME");
|
|
139
|
+
if (!home.empty()) return home;
|
|
140
|
+
const passwd *entry = getpwuid(getuid());
|
|
141
|
+
return entry && entry->pw_dir ? std::string(entry->pw_dir) : std::string("/");
|
|
142
|
+
#endif
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#if defined(__APPLE__)
|
|
146
|
+
bool isRegularFile(const std::string &path) {
|
|
147
|
+
struct stat fileStat {};
|
|
148
|
+
return stat(path.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode);
|
|
121
149
|
}
|
|
122
150
|
|
|
123
151
|
std::string defaultPluginPath() {
|
|
@@ -125,13 +153,43 @@ std::string defaultPluginPath() {
|
|
|
125
153
|
// present; older Bambu Studio installations remain a compatible fallback.
|
|
126
154
|
const std::string connectPlugin =
|
|
127
155
|
"/Applications/Bambu Connect.app/Contents/Resources/app.asar.unpacked/plugins/plugins-mac/libbambu_networking.dylib";
|
|
128
|
-
|
|
129
|
-
if (stat(connectPlugin.c_str(), &connectStat) == 0 && S_ISREG(connectStat.st_mode)) {
|
|
130
|
-
return connectPlugin;
|
|
131
|
-
}
|
|
156
|
+
if (isRegularFile(connectPlugin)) return connectPlugin;
|
|
132
157
|
return homeDir() + "/Library/Application Support/BambuStudio/plugins/libbambu_networking.dylib";
|
|
133
158
|
}
|
|
134
159
|
|
|
160
|
+
std::string defaultConfigDir() {
|
|
161
|
+
return envOr("BAMBU_NATIVE_CONFIG_DIR", homeDir() + "/Library/Application Support/BambuStudio");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::string defaultCertDir() {
|
|
165
|
+
return envOr("BAMBU_NATIVE_CERT_DIR", "/Applications/BambuStudio.app/Contents/Resources/cert");
|
|
166
|
+
}
|
|
167
|
+
#else
|
|
168
|
+
// Linux has no application bundle to borrow from. The setup script installs a
|
|
169
|
+
// pinned plug-in and certificates under one private directory. The server
|
|
170
|
+
// passes the resolved paths explicitly; these defaults only serve direct use
|
|
171
|
+
// and mirror bambuNativeManagedPaths() in src/bambu-native.ts.
|
|
172
|
+
std::string nativeHomeDir() {
|
|
173
|
+
const std::string configured = envOr("BAMBU_NATIVE_HOME");
|
|
174
|
+
if (!configured.empty()) return configured;
|
|
175
|
+
return envOr("XDG_CONFIG_HOME", homeDir() + "/.config") + "/bambu-printer-mcp/bambu-native";
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::string defaultPluginPath() {
|
|
179
|
+
// Only the pinned plug-in: Bambu Studio's own copy auto-updates and its
|
|
180
|
+
// PrintParams layout is not checked against this helper.
|
|
181
|
+
return nativeHomeDir() + "/plugins/libbambu_networking.so";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
std::string defaultConfigDir() {
|
|
185
|
+
return envOr("BAMBU_NATIVE_CONFIG_DIR", nativeHomeDir() + "/config");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
std::string defaultCertDir() {
|
|
189
|
+
return envOr("BAMBU_NATIVE_CERT_DIR", nativeHomeDir() + "/cert");
|
|
190
|
+
}
|
|
191
|
+
#endif
|
|
192
|
+
|
|
135
193
|
void *symbol(void *handle, const char *name) {
|
|
136
194
|
return dlsym(handle, name);
|
|
137
195
|
}
|
|
@@ -238,10 +296,8 @@ void runProbe(NativeApi &api) {
|
|
|
238
296
|
|
|
239
297
|
// Avoid calling the C++ string-returning symbol in the probe. The tunnel
|
|
240
298
|
// API is C ABI and is the only part needed to verify the X2D route.
|
|
241
|
-
const std::string configDir =
|
|
242
|
-
|
|
243
|
-
const std::string certDir = envOr("BAMBU_NATIVE_CERT_DIR",
|
|
244
|
-
"/Applications/BambuStudio.app/Contents/Resources/cert");
|
|
299
|
+
const std::string configDir = defaultConfigDir();
|
|
300
|
+
const std::string certDir = defaultCertDir();
|
|
245
301
|
const std::string certFile = envOr("BAMBU_NATIVE_CERT_FILE", "slicer_base64.cer");
|
|
246
302
|
void *agent = api.createAgent(configDir);
|
|
247
303
|
if (!agent) throw std::runtime_error("bambu_network_create_agent returned null in probe");
|
|
@@ -256,7 +312,8 @@ void runProbe(NativeApi &api) {
|
|
|
256
312
|
const FtErr createResult = api.ftTunnelCreate(localUrl(host, token).c_str(), &tunnel);
|
|
257
313
|
if (createResult != 0 || !tunnel) {
|
|
258
314
|
outputLine("probe=failed stage=tunnel_create result=" + std::to_string(createResult));
|
|
259
|
-
|
|
315
|
+
api.destroyAgent(agent);
|
|
316
|
+
exitNow(22);
|
|
260
317
|
}
|
|
261
318
|
|
|
262
319
|
const FtErr connectResult = api.ftTunnelSyncConnect(tunnel);
|
|
@@ -264,6 +321,7 @@ void runProbe(NativeApi &api) {
|
|
|
264
321
|
api.destroyAgent(agent);
|
|
265
322
|
outputLine("probe=local_tunnel version=" + version + " ft_abi=" + std::to_string(abi) +
|
|
266
323
|
" connect_result=" + std::to_string(connectResult));
|
|
324
|
+
if (connectResult != 0) exitNow(22);
|
|
267
325
|
}
|
|
268
326
|
|
|
269
327
|
void runMqttProbe(NativeApi &api) {
|
|
@@ -274,10 +332,8 @@ void runMqttProbe(NativeApi &api) {
|
|
|
274
332
|
throw std::runtime_error("BAMBU_NATIVE_HOST, BAMBU_NATIVE_ACCESS_CODE, and BAMBU_NATIVE_SERIAL are required for --probe-mqtt");
|
|
275
333
|
}
|
|
276
334
|
|
|
277
|
-
const std::string configDir =
|
|
278
|
-
|
|
279
|
-
const std::string certDir = envOr("BAMBU_NATIVE_CERT_DIR",
|
|
280
|
-
"/Applications/BambuStudio.app/Contents/Resources/cert");
|
|
335
|
+
const std::string configDir = defaultConfigDir();
|
|
336
|
+
const std::string certDir = defaultCertDir();
|
|
281
337
|
const std::string certFile = envOr("BAMBU_NATIVE_CERT_FILE", "slicer_base64.cer");
|
|
282
338
|
void *agent = api.createAgent(configDir);
|
|
283
339
|
if (!agent) throw std::runtime_error("bambu_network_create_agent returned null in MQTT probe");
|
|
@@ -384,7 +440,7 @@ void runMqttProbe(NativeApi &api) {
|
|
|
384
440
|
destroyAgent();
|
|
385
441
|
outputLine("probe=mqtt status=" + std::to_string(status) +
|
|
386
442
|
(message.empty() ? std::string{} : " message=" + message));
|
|
387
|
-
|
|
443
|
+
exitNow(ok ? 0 : 21);
|
|
388
444
|
}
|
|
389
445
|
|
|
390
446
|
std::string bedTypeForNative(const std::string &bedType) {
|
|
@@ -475,10 +531,8 @@ void runOperation(NativeApi &api, NativeOperation operation) {
|
|
|
475
531
|
" file is not readable: " + file);
|
|
476
532
|
}
|
|
477
533
|
|
|
478
|
-
const std::string configDir =
|
|
479
|
-
|
|
480
|
-
const std::string certDir = envOr("BAMBU_NATIVE_CERT_DIR",
|
|
481
|
-
"/Applications/BambuStudio.app/Contents/Resources/cert");
|
|
534
|
+
const std::string configDir = defaultConfigDir();
|
|
535
|
+
const std::string certDir = defaultCertDir();
|
|
482
536
|
const std::string certFile = envOr("BAMBU_NATIVE_CERT_FILE", "slicer_base64.cer");
|
|
483
537
|
const std::string country = envOr("BAMBU_NATIVE_COUNTRY", "US");
|
|
484
538
|
|
|
@@ -653,8 +707,7 @@ void runOperation(NativeApi &api, NativeOperation operation) {
|
|
|
653
707
|
}
|
|
654
708
|
outputLine("native_command result=" + std::to_string(result));
|
|
655
709
|
destroyAgent();
|
|
656
|
-
|
|
657
|
-
return;
|
|
710
|
+
exitNow(result == 0 ? 0 : 20);
|
|
658
711
|
}
|
|
659
712
|
|
|
660
713
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
@@ -729,35 +782,56 @@ void runOperation(NativeApi &api, NativeOperation operation) {
|
|
|
729
782
|
// crash after acceptance; let the OS reclaim it without changing that result.
|
|
730
783
|
// outputLine flushes the receipt before exiting. Upload/control paths retain
|
|
731
784
|
// their existing cleanup, and no dispatched print is replayed here.
|
|
732
|
-
if (!uploadOnly)
|
|
785
|
+
if (!uploadOnly) exitNow(result == 0 ? 0 : 20);
|
|
733
786
|
destroyAgent();
|
|
734
|
-
|
|
787
|
+
exitNow(result == 0 ? 0 : 20);
|
|
735
788
|
}
|
|
736
789
|
|
|
737
790
|
} // namespace
|
|
738
791
|
|
|
792
|
+
// Load the plug-in and resolve every required symbol without any network
|
|
793
|
+
// access. On Linux also load the file-tunnel library the plug-in opens from its
|
|
794
|
+
// own directory, so missing system libraries surface at setup time.
|
|
795
|
+
void runPluginCheck(const NativeApi &) {
|
|
796
|
+
#if !defined(__APPLE__)
|
|
797
|
+
const std::string plugin = envOr("BAMBU_NATIVE_PLUGIN", defaultPluginPath());
|
|
798
|
+
// Resolve beside the plug-in, never through the system library path.
|
|
799
|
+
const std::string::size_type slash = plugin.find_last_of('/');
|
|
800
|
+
const std::string source = (slash == std::string::npos ? std::string("./") : plugin.substr(0, slash + 1)) + "libBambuSource.so";
|
|
801
|
+
void *handle = dlopen(source.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
802
|
+
if (!handle) {
|
|
803
|
+
const char *error = dlerror();
|
|
804
|
+
throw std::runtime_error("could not load Bambu tunnel library: " + source + (error ? std::string(": ") + error : std::string()));
|
|
805
|
+
}
|
|
806
|
+
dlclose(handle);
|
|
807
|
+
#endif
|
|
808
|
+
outputLine("plugin=ok");
|
|
809
|
+
}
|
|
810
|
+
|
|
739
811
|
int main(int argc, char **argv) {
|
|
740
812
|
try {
|
|
813
|
+
const bool pluginCheck = argc == 2 && std::strcmp(argv[1], "--check-plugin") == 0;
|
|
741
814
|
const bool probe = argc == 2 && std::strcmp(argv[1], "--probe") == 0;
|
|
742
815
|
const bool mqttProbe = argc == 2 && std::strcmp(argv[1], "--probe-mqtt") == 0;
|
|
743
816
|
const bool print = argc == 2 && std::strcmp(argv[1], "--print-authorized") == 0;
|
|
744
817
|
const bool upload = argc == 2 && std::strcmp(argv[1], "--upload") == 0;
|
|
745
818
|
const bool command = argc == 2 && std::strcmp(argv[1], "--command") == 0;
|
|
746
819
|
const bool authorizedCommand = argc == 2 && std::strcmp(argv[1], "--command-authorized") == 0;
|
|
747
|
-
if (!probe && !mqttProbe && !print && !upload && !command && !authorizedCommand) {
|
|
748
|
-
std::cerr << "usage: bambu-native-print --probe|--probe-mqtt|--print-authorized|--upload|--command|--command-authorized" << std::endl;
|
|
820
|
+
if (!pluginCheck && !probe && !mqttProbe && !print && !upload && !command && !authorizedCommand) {
|
|
821
|
+
std::cerr << "usage: bambu-native-print --check-plugin|--probe|--probe-mqtt|--print-authorized|--upload|--command|--command-authorized" << std::endl;
|
|
749
822
|
return 2;
|
|
750
823
|
}
|
|
751
824
|
NativeApi api = loadApi();
|
|
825
|
+
if (pluginCheck) runPluginCheck(api);
|
|
752
826
|
if (probe) runProbe(api);
|
|
753
827
|
if (mqttProbe) runMqttProbe(api);
|
|
754
828
|
if (print) runOperation(api, NativeOperation::Print);
|
|
755
829
|
if (upload) runOperation(api, NativeOperation::Upload);
|
|
756
830
|
if (command) runOperation(api, NativeOperation::Command);
|
|
757
831
|
if (authorizedCommand) runOperation(api, NativeOperation::AuthorizedCommand);
|
|
758
|
-
|
|
832
|
+
exitNow(0);
|
|
759
833
|
} catch (const std::exception &error) {
|
|
760
834
|
std::cerr << "native_error=" << error.what() << std::endl;
|
|
761
|
-
|
|
835
|
+
exitNow(1);
|
|
762
836
|
}
|
|
763
837
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bambu-printer-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.28",
|
|
4
4
|
"description": "MCP server for Bambu Lab 3D printers with STL manipulation, slicing, and print management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
"package:mcpb": "npm run build && node scripts/package-mcpb.mjs",
|
|
18
18
|
"version": "node scripts/sync-manifest-version.mjs",
|
|
19
19
|
"postinstall": "node scripts/install-patches.mjs",
|
|
20
|
-
"build:native": "scripts/build-bambu-native.
|
|
20
|
+
"build:native": "sh scripts/build-bambu-native.sh"
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
23
|
-
"bambu-printer-mcp": "dist/index.js"
|
|
23
|
+
"bambu-printer-mcp": "dist/index.js",
|
|
24
|
+
"bambu-printer-mcp-native-setup": "scripts/setup-bambu-native.mjs"
|
|
24
25
|
},
|
|
25
26
|
"files": [
|
|
26
27
|
"dist",
|
|
@@ -32,7 +33,8 @@
|
|
|
32
33
|
"LICENSE",
|
|
33
34
|
"native/*.cpp",
|
|
34
35
|
"native/*.h",
|
|
35
|
-
"scripts/build-bambu-native.
|
|
36
|
+
"scripts/build-bambu-native.sh",
|
|
37
|
+
"scripts/setup-bambu-native.mjs"
|
|
36
38
|
],
|
|
37
39
|
"keywords": [
|
|
38
40
|
"mcp",
|
|
@@ -49,7 +51,7 @@
|
|
|
49
51
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
50
52
|
"bambu-js": "^2.2.2",
|
|
51
53
|
"bambu-node": "3.22.21",
|
|
52
|
-
"basic-ftp": "^6.2.
|
|
54
|
+
"basic-ftp": "^6.2.3",
|
|
53
55
|
"dotenv": "^16.3.1",
|
|
54
56
|
"jszip": "^3.10.2",
|
|
55
57
|
"mqtt": "^5.16.0",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
# Build the optional adapter that calls Bambu's networking plug-in at runtime.
|
|
4
|
+
# The plug-in itself is not bundled here. Supported hosts: macOS and Linux.
|
|
5
|
+
# Usage: build-bambu-native.sh [--output PATH]
|
|
6
|
+
set -eu
|
|
7
|
+
|
|
8
|
+
case "$(uname -s)" in
|
|
9
|
+
Darwin) default_cxx=clang++ ;;
|
|
10
|
+
Linux) default_cxx=c++ ;;
|
|
11
|
+
*)
|
|
12
|
+
echo "The Bambu native helper supports macOS and Linux only." >&2
|
|
13
|
+
exit 1
|
|
14
|
+
;;
|
|
15
|
+
esac
|
|
16
|
+
|
|
17
|
+
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
18
|
+
project_root=$(dirname -- "$script_dir")
|
|
19
|
+
source_file="$project_root/native/bambu-native-print.cpp"
|
|
20
|
+
output="$project_root/native/bambu-native-print"
|
|
21
|
+
|
|
22
|
+
while [ "$#" -gt 0 ]; do
|
|
23
|
+
case "$1" in
|
|
24
|
+
--output)
|
|
25
|
+
[ "$#" -ge 2 ] && [ -n "$2" ] || { echo "--output requires a path" >&2; exit 2; }
|
|
26
|
+
output=$2
|
|
27
|
+
shift 2
|
|
28
|
+
;;
|
|
29
|
+
*)
|
|
30
|
+
echo "usage: $0 [--output PATH]" >&2
|
|
31
|
+
exit 2
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
cxx_bin=${CXX:-$default_cxx}
|
|
37
|
+
if ! command -v "$cxx_bin" >/dev/null 2>&1; then
|
|
38
|
+
echo "C++ compiler not found: $cxx_bin (install clang++ or g++, or set CXX)" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
mkdir -p "$(dirname -- "$output")"
|
|
43
|
+
# Compile beside the target and rename so a running server never executes a
|
|
44
|
+
# partially written helper.
|
|
45
|
+
temporary="$output.tmp.$$"
|
|
46
|
+
trap 'rm -f "$temporary"' EXIT
|
|
47
|
+
# dash skips EXIT traps on signals; clean up a killed build explicitly.
|
|
48
|
+
trap 'rm -f "$temporary"; exit 1' HUP INT TERM
|
|
49
|
+
"$cxx_bin" -std=c++17 -O2 -Wall -Wextra -pthread -o "$temporary" "$source_file" -ldl
|
|
50
|
+
chmod 755 "$temporary"
|
|
51
|
+
mv -f "$temporary" "$output"
|
|
52
|
+
trap - EXIT HUP INT TERM
|
|
53
|
+
echo "Built $output"
|