bambu-printer-mcp 1.1.27 → 1.1.29

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.
Files changed (38) hide show
  1. package/CONTRIBUTORS.md +7 -1
  2. package/README.md +29 -10
  3. package/dist/ams-mapping.d.ts +18 -2
  4. package/dist/ams-mapping.js +36 -5
  5. package/dist/bambu-native.d.ts +55 -0
  6. package/dist/bambu-native.js +212 -13
  7. package/dist/index.js +154 -71
  8. package/dist/printers/bambu.d.ts +29 -2
  9. package/dist/printers/bambu.js +59 -28
  10. package/dist/safety/hidden-hms.d.ts +32 -0
  11. package/dist/safety/hidden-hms.js +405 -0
  12. package/dist/safety/limits.d.ts +6 -0
  13. package/dist/safety/limits.js +9 -0
  14. package/dist/safety/print-file.d.ts +2 -0
  15. package/dist/safety/print-file.js +83 -7
  16. package/dist/safety/printer-state.d.ts +11 -0
  17. package/dist/safety/printer-state.js +115 -22
  18. package/dist/slicer/profile-flatten.js +25 -0
  19. package/dist/slicer/remote-slicer.d.ts +12 -0
  20. package/dist/slicer/remote-slicer.js +240 -0
  21. package/dist/stl/stl-manipulator.d.ts +2 -1
  22. package/dist/stl/stl-manipulator.js +71 -42
  23. package/native/bambu-native-print.cpp +100 -26
  24. package/package.json +7 -5
  25. package/scripts/build-bambu-native.sh +53 -0
  26. package/scripts/setup-bambu-native.mjs +372 -0
  27. package/src/ams-mapping.ts +38 -5
  28. package/src/bambu-native.ts +229 -14
  29. package/src/index.ts +156 -67
  30. package/src/printers/bambu.ts +65 -25
  31. package/src/safety/hidden-hms.ts +406 -0
  32. package/src/safety/limits.ts +9 -0
  33. package/src/safety/print-file.ts +71 -7
  34. package/src/safety/printer-state.ts +117 -22
  35. package/src/slicer/profile-flatten.ts +32 -0
  36. package/src/slicer/remote-slicer.ts +216 -0
  37. package/src/stl/stl-manipulator.ts +70 -43
  38. package/scripts/build-bambu-native.zsh +0 -33
@@ -11,6 +11,7 @@ import * as crypto from 'crypto';
11
11
  import { execFile } from 'child_process';
12
12
  import { flattenForCli, detectProfilesRoot, resolveBblMachineProfile } from '../slicer/profile-flatten.js';
13
13
  import { profileJsonFiles } from '../slicer/profile-files.js';
14
+ import { sliceWithOrcaApi, validateRemoteSliceOptions } from '../slicer/remote-slicer.js';
14
15
 
15
16
  const readFileAsync = promisify(fs.readFile);
16
17
  const writeFileAsync = promisify(fs.writeFile);
@@ -57,6 +58,7 @@ export const SLICER_TYPES = [
57
58
  'bambustudio',
58
59
  'orcaslicer',
59
60
  'orcaslicer-bambulab',
61
+ 'orca-slicer-api',
60
62
  'prusaslicer',
61
63
  'cura',
62
64
  'slic3r',
@@ -81,6 +83,8 @@ const SLICER_TYPE_ALIASES: Record<string, SlicerType> = {
81
83
  orcastudio: 'orcaslicer-bambulab',
82
84
  'orcaslicer-bmcu': 'orcaslicer-bambulab',
83
85
  'orca-bmcu': 'orcaslicer-bambulab',
86
+ 'orca-slicer-api': 'orca-slicer-api',
87
+ 'orcaslicer-api': 'orca-slicer-api',
84
88
  prusaslicer: 'prusaslicer',
85
89
  'prusa-slicer': 'prusaslicer',
86
90
  cura: 'cura',
@@ -105,7 +109,8 @@ export function isBambuCompatibleSlicer(slicerType: SlicerType): boolean {
105
109
  return (
106
110
  slicerType === 'bambustudio' ||
107
111
  slicerType === 'orcaslicer' ||
108
- slicerType === 'orcaslicer-bambulab'
112
+ slicerType === 'orcaslicer-bambulab' ||
113
+ slicerType === 'orca-slicer-api'
109
114
  );
110
115
  }
111
116
 
@@ -166,6 +171,7 @@ export interface BambuSliceOptions {
166
171
  filamentColours?: string[]; // positional #RRGGBB per loaded filament slot
167
172
  bedType?: string; // Bambu bed type, e.g. textured_plate or cool_plate
168
173
  nozzleType?: string; // installed hotend material written into the machine preset
174
+ nozzleVolumeType?: 'Standard' | 'High Flow'; // installed nozzle flow, the machine preset's nozzle_volume_type
169
175
  enableTimelapse?: boolean; // --enable-timelapse: timelapse-aware slicing
170
176
  allowMixTemp?: boolean; // --allow-mix-temp: allow mixed-temp filaments
171
177
  scale?: number; // --scale factor: uniform scale
@@ -563,6 +569,7 @@ export class STLManipulator extends EventEmitter {
563
569
  userProfileRoots: configuredBambuProfileDirs(),
564
570
  tempDir: this.tempDir,
565
571
  bedType: this.resolveBambuStudioBedType(bambuOptions?.bedType),
572
+ nozzleVolumeType: bambuOptions?.nozzleVolumeType,
566
573
  filamentColours: bambuOptions?.filamentColours ?? bundle.filamentColours,
567
574
  projectTowerPosition: bundle.projectTowerPosition,
568
575
  sourceProfiles: {
@@ -1589,7 +1596,7 @@ export class STLManipulator extends EventEmitter {
1589
1596
  const operationId = this.generateOperationId();
1590
1597
  this.activeOperations.set(operationId, true);
1591
1598
 
1592
- if (isPathLikeExecutable(slicerPath) && !fs.existsSync(slicerPath)) {
1599
+ if (slicerType !== 'orca-slicer-api' && isPathLikeExecutable(slicerPath) && !fs.existsSync(slicerPath)) {
1593
1600
  throw new Error(`Slicer executable not found at: ${slicerPath}`);
1594
1601
  }
1595
1602
  if (slicerProfile && !fs.existsSync(slicerProfile)) {
@@ -1636,6 +1643,7 @@ export class STLManipulator extends EventEmitter {
1636
1643
  case 'bambustudio':
1637
1644
  case 'orcaslicer':
1638
1645
  case 'orcaslicer-bambulab':
1646
+ case 'orca-slicer-api':
1639
1647
  // Bambu-compatible CLI: slice and export as 3MF with embedded gcode.
1640
1648
  // FULU's OrcaSlicer-bambulab fork keeps this Bambu-style path.
1641
1649
  // For 3MF input: slice in place and export sliced 3MF
@@ -1643,12 +1651,19 @@ export class STLManipulator extends EventEmitter {
1643
1651
  {
1644
1652
  const is3mf = stlFilePath.toLowerCase().endsWith('.3mf');
1645
1653
  const outputBase = path.basename(stlFilePath, is3mf ? '.3mf' : '.stl');
1646
- const bambuOutputPath = path.join(this.tempDir, outputBase + '_sliced.3mf');
1654
+ const remote = slicerType === 'orca-slicer-api';
1655
+ if (remote) {
1656
+ validateRemoteSliceOptions(bambuOptions);
1657
+ if (!process.env.BAMBU_PROFILES_ROOT) throw new Error('Remote slicing requires BAMBU_PROFILES_ROOT pointing to the matching Orca profile tree (a read-only mount is sufficient).');
1658
+ if (slicerProfile && !fs.existsSync(slicerProfile)) throw new Error('Remote slicing process profile does not exist.');
1659
+ }
1660
+ const profileSlicerType = remote ? 'orcaslicer' : slicerType;
1661
+ const bambuOutputPath = path.join(this.tempDir, outputBase + (remote ? `_${operationId}` : '') + '_sliced.3mf');
1647
1662
  const outputDir = path.dirname(bambuOutputPath);
1648
- const profilesRoot = detectProfilesRoot(slicerPath, slicerType);
1663
+ const profilesRoot = detectProfilesRoot(remote ? undefined : slicerPath, profileSlicerType);
1649
1664
  const rawBundle = await this.resolveBambuLikeSettingsBundle(
1650
1665
  outputBase,
1651
- slicerType,
1666
+ profileSlicerType,
1652
1667
  slicerProfile,
1653
1668
  printerPreset,
1654
1669
  bambuOptions,
@@ -1674,7 +1689,7 @@ export class STLManipulator extends EventEmitter {
1674
1689
  // absolute-extrusion normalization after resolving those ancestors.
1675
1690
  // The machine owns the extrusion mode and layer G-code once process
1676
1691
  // files lose machine keys, so normalize it there as well.
1677
- if (slicerType === 'orcaslicer' && settingsBundle.settingsArg) {
1692
+ if (profileSlicerType === 'orcaslicer' && settingsBundle.settingsArg) {
1678
1693
  const [machinePath, processPath] = settingsBundle.settingsArg.split(';');
1679
1694
  settingsBundle.settingsArg = [
1680
1695
  this.writeTempJson(outputBase, 'machine_orca_resolved',
@@ -1684,6 +1699,16 @@ export class STLManipulator extends EventEmitter {
1684
1699
  ].join(';');
1685
1700
  }
1686
1701
  const cliBundle = this.isolateMachineSettings(settingsBundle);
1702
+ if (remote) {
1703
+ const [machinePath, processPath] = cliBundle.settingsArg?.split(';') ?? [];
1704
+ if (!machinePath || !processPath) throw new Error('Remote slicing requires complete prepared machine and process profiles.');
1705
+ outputFilePath = await sliceWithOrcaApi({
1706
+ inputPath: stlFilePath, machinePath, processPath,
1707
+ filamentPaths: cliBundle.filamentPaths, outputPath: bambuOutputPath,
1708
+ options: bambuOptions, isCancelled: () => !this.activeOperations.get(operationId),
1709
+ });
1710
+ break;
1711
+ }
1687
1712
  args = [
1688
1713
  '--slice', String(bambuOptions?.slicePlate ?? 0),
1689
1714
  '--outputdir', outputDir,
@@ -1727,47 +1752,49 @@ export class STLManipulator extends EventEmitter {
1727
1752
  throw new Error(`Unsupported slicer type: ${slicerType}`);
1728
1753
  }
1729
1754
 
1730
- if (progressCallback) progressCallback(20, `Executing slicer: ${slicerPath} ${args.join(' ')}`);
1731
- console.error(`Executing: ${slicerPath} ${args.join(' ')}`);
1732
-
1733
- // Execute the slicer
1734
- await new Promise<void>((resolve, reject) => {
1735
- const process = execFile(slicerPath, args, (error, stdout, stderr) => {
1736
- if (error) {
1737
- console.error(`Slicer Error: ${error.message}`);
1738
- console.error(`Slicer Stderr: ${stderr}`);
1739
- reject(new Error(`Slicer failed: ${error.message}. Stderr: ${stderr}`));
1740
- } else {
1741
- console.error(`Slicer Stdout: ${stdout}`);
1742
- if (stderr) {
1743
- console.warn(`Slicer Stderr: ${stderr}`); // Log stderr even on success
1744
- }
1745
- resolve();
1746
- }
1747
- });
1755
+ if (slicerType !== 'orca-slicer-api') {
1756
+ if (progressCallback) progressCallback(20, `Executing slicer: ${slicerPath} ${args.join(' ')}`);
1757
+ console.error(`Executing: ${slicerPath} ${args.join(' ')}`);
1758
+
1759
+ // Execute the slicer
1760
+ await new Promise<void>((resolve, reject) => {
1761
+ const process = execFile(slicerPath, args, (error, stdout, stderr) => {
1762
+ if (error) {
1763
+ console.error(`Slicer Error: ${error.message}`);
1764
+ console.error(`Slicer Stderr: ${stderr}`);
1765
+ reject(new Error(`Slicer failed: ${error.message}. Stderr: ${stderr}`));
1766
+ } else {
1767
+ console.error(`Slicer Stdout: ${stdout}`);
1768
+ if (stderr) {
1769
+ console.warn(`Slicer Stderr: ${stderr}`); // Log stderr even on success
1770
+ }
1771
+ resolve();
1772
+ }
1773
+ });
1748
1774
 
1749
- // Optional: Add listeners for stdout/stderr for real-time progress if slicer provides it
1750
- // process.stdout?.on('data', (data) => { console.error(`Slicer stdout: ${data}`); });
1751
- // process.stderr?.on('data', (data) => { console.error(`Slicer stderr: ${data}`); });
1775
+ // Optional: Add listeners for stdout/stderr for real-time progress if slicer provides it
1776
+ // process.stdout?.on('data', (data) => { console.error(`Slicer stdout: ${data}`); });
1777
+ // process.stderr?.on('data', (data) => { console.error(`Slicer stderr: ${data}`); });
1752
1778
 
1753
- // Check for cancellation periodically
1754
- const checkCancel = setInterval(() => {
1755
- if (!this.activeOperations.get(operationId)) {
1756
- clearInterval(checkCancel);
1757
- try {
1758
- process.kill(); // Attempt to kill the slicer process
1759
- reject(new Error("Slicing operation cancelled"));
1760
- } catch (killError) {
1761
- console.error("Error attempting to kill slicer process:", killError);
1762
- reject(new Error("Slicing operation cancelled, but failed to kill process."));
1763
- }
1764
- }
1765
- }, 500);
1779
+ // Check for cancellation periodically
1780
+ const checkCancel = setInterval(() => {
1781
+ if (!this.activeOperations.get(operationId)) {
1782
+ clearInterval(checkCancel);
1783
+ try {
1784
+ process.kill(); // Attempt to kill the slicer process
1785
+ reject(new Error("Slicing operation cancelled"));
1786
+ } catch (killError) {
1787
+ console.error("Error attempting to kill slicer process:", killError);
1788
+ reject(new Error("Slicing operation cancelled, but failed to kill process."));
1789
+ }
1790
+ }
1791
+ }, 500);
1766
1792
 
1767
- process.on('exit', () => clearInterval(checkCancel));
1768
- process.on('error', () => clearInterval(checkCancel)); // Ensure interval cleared on process error too
1793
+ process.on('exit', () => clearInterval(checkCancel));
1794
+ process.on('error', () => clearInterval(checkCancel)); // Ensure interval cleared on process error too
1769
1795
 
1770
- });
1796
+ });
1797
+ }
1771
1798
 
1772
1799
  if (!this.activeOperations.get(operationId)) {
1773
1800
  // This check might be redundant if the promise rejected on kill, but good safety
@@ -1,33 +0,0 @@
1
- #!/bin/zsh
2
-
3
- # Build the optional macOS adapter that calls Bambu Studio's installed
4
- # networking plug-in at runtime. The plug-in itself is not bundled here.
5
- set -euo pipefail
6
-
7
- if [[ "$(uname -s)" != "Darwin" ]]; then
8
- print -u2 "The Bambu native helper currently supports macOS only."
9
- exit 1
10
- fi
11
-
12
- SCRIPT_DIR="${0:A:h}"
13
- PROJECT_ROOT="${SCRIPT_DIR:h}"
14
- CXX_BIN="${CXX:-clang++}"
15
- OUTPUT="${PROJECT_ROOT}/native/bambu-native-print"
16
- SOURCE="${PROJECT_ROOT}/native/bambu-native-print.cpp"
17
-
18
- if ! command -v "$CXX_BIN" >/dev/null 2>&1; then
19
- print -u2 "C++ compiler not found: $CXX_BIN"
20
- exit 1
21
- fi
22
-
23
- "$CXX_BIN" \
24
- -std=c++17 \
25
- -O2 \
26
- -Wall \
27
- -Wextra \
28
- -o "$OUTPUT" \
29
- "$SOURCE" \
30
- -ldl
31
-
32
- chmod 755 "$OUTPUT"
33
- print "Built $OUTPUT"