mcp-3d-printer-server 1.2.19 → 1.2.21

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 (51) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +112 -9
  3. package/dist/blender-mcp-bridge.d.ts +21 -0
  4. package/dist/blender-mcp-bridge.js +3 -3
  5. package/dist/counterpart/contract.d.ts +395 -0
  6. package/dist/counterpart/contract.js +220 -0
  7. package/dist/counterpart/progress.d.ts +32 -0
  8. package/dist/counterpart/progress.js +443 -0
  9. package/dist/counterpart/service.d.ts +56 -0
  10. package/dist/counterpart/service.js +224 -0
  11. package/dist/fusion-mcp-bridge.d.ts +166 -0
  12. package/dist/fusion-mcp-bridge.js +529 -0
  13. package/dist/index.js +272 -8
  14. package/dist/printers/creality.d.ts +26 -1
  15. package/dist/printers/creality.js +75 -2
  16. package/dist/printers/duet.d.ts +16 -0
  17. package/dist/printers/duet.js +64 -0
  18. package/dist/printers/generic-base.d.ts +52 -6
  19. package/dist/printers/generic-base.js +134 -7
  20. package/dist/printers/klipper.d.ts +33 -2
  21. package/dist/printers/klipper.js +106 -0
  22. package/dist/printers/octoprint.d.ts +23 -1
  23. package/dist/printers/octoprint.js +81 -0
  24. package/dist/printers/prusa.d.ts +13 -0
  25. package/dist/printers/prusa.js +59 -0
  26. package/dist/printers/repetier.d.ts +17 -1
  27. package/dist/printers/repetier.js +55 -0
  28. package/dist/slicer/bambu-cli.d.ts +2 -0
  29. package/dist/slicer/bambu-cli.js +11 -0
  30. package/dist/slicer/filament-profiles.d.ts +25 -0
  31. package/dist/slicer/filament-profiles.js +249 -0
  32. package/dist/stl/stl-manipulator.d.ts +21 -1
  33. package/dist/stl/stl-manipulator.js +94 -13
  34. package/package.json +7 -2
  35. package/server.json +2 -2
  36. package/src/blender-mcp-bridge.ts +3 -3
  37. package/src/counterpart/contract.ts +326 -0
  38. package/src/counterpart/progress.ts +470 -0
  39. package/src/counterpart/service.ts +247 -0
  40. package/src/fusion-mcp-bridge.ts +512 -0
  41. package/src/index.ts +314 -10
  42. package/src/printers/creality.ts +84 -1
  43. package/src/printers/duet.ts +67 -0
  44. package/src/printers/generic-base.ts +172 -1
  45. package/src/printers/klipper.ts +118 -2
  46. package/src/printers/octoprint.ts +86 -1
  47. package/src/printers/prusa.ts +60 -0
  48. package/src/printers/repetier.ts +63 -1
  49. package/src/slicer/bambu-cli.ts +13 -0
  50. package/src/slicer/filament-profiles.ts +249 -0
  51. package/src/stl/stl-manipulator.ts +108 -13
@@ -0,0 +1,395 @@
1
+ export declare const COUNTERPART_CONTRACT = "counterpart/1";
2
+ export declare const PRINTER_KINDS: readonly ["bambu", "octoprint", "klipper", "prusa", "duet", "repetier", "creality"];
3
+ export type PrinterKind = (typeof PRINTER_KINDS)[number];
4
+ export declare const PROGRESS_STATES: readonly ["idle", "preparing", "printing", "paused", "finished", "failed", "cancelled", "offline", "busy"];
5
+ export type ProgressState = (typeof PROGRESS_STATES)[number];
6
+ export declare const PROBLEM_SEVERITIES: readonly ["fatal", "error", "warning", "info"];
7
+ export type ProblemSeverity = (typeof PROBLEM_SEVERITIES)[number];
8
+ export type CameraCapability = "bambu-tcp" | "rtsp" | "http-snapshot" | "none";
9
+ export type FilamentCapability = "ams" | "single" | "none";
10
+ export interface CounterpartProblem {
11
+ code: string;
12
+ severity: ProblemSeverity;
13
+ message: string;
14
+ url?: string;
15
+ }
16
+ export interface HeaterReading {
17
+ actual: number;
18
+ target: number;
19
+ }
20
+ export interface CounterpartJob {
21
+ name: string;
22
+ id?: string;
23
+ progress?: number;
24
+ elapsedSec?: number;
25
+ remainingSec?: number;
26
+ layer?: number;
27
+ totalLayers?: number;
28
+ stage?: string;
29
+ }
30
+ export interface CounterpartProgress {
31
+ contract: typeof COUNTERPART_CONTRACT;
32
+ observedAt: string;
33
+ state: ProgressState;
34
+ job?: CounterpartJob;
35
+ temps?: {
36
+ nozzle?: HeaterReading;
37
+ bed?: HeaterReading;
38
+ chamber?: number;
39
+ };
40
+ activeFilament?: {
41
+ slot?: string;
42
+ material?: string;
43
+ color?: string;
44
+ };
45
+ problems: CounterpartProblem[];
46
+ }
47
+ export interface CounterpartCapabilities {
48
+ progress: boolean;
49
+ layers: boolean;
50
+ camera: CameraCapability;
51
+ filaments: FilamentCapability;
52
+ pause: boolean;
53
+ resume: boolean;
54
+ cancel: boolean;
55
+ print3mf: boolean;
56
+ gcode: boolean;
57
+ }
58
+ export interface PrinterIdentity {
59
+ model?: string;
60
+ serial?: string;
61
+ name?: string;
62
+ }
63
+ export interface CounterpartPrinterInfo {
64
+ contract: typeof COUNTERPART_CONTRACT;
65
+ server: {
66
+ name: string;
67
+ version: string;
68
+ };
69
+ printer: {
70
+ kind: PrinterKind;
71
+ host: string;
72
+ } & PrinterIdentity;
73
+ capabilities: CounterpartCapabilities;
74
+ }
75
+ export interface FilamentSlot {
76
+ id: "ext";
77
+ material?: string;
78
+ color?: string;
79
+ name?: string;
80
+ source: "spoolman" | "unknown";
81
+ /** Absolute path of the slicer filament profile for this material, as slice_stl's load_filaments takes it. */
82
+ profile?: string;
83
+ }
84
+ export interface CameraSnapshotResult {
85
+ status: "success";
86
+ format: "image/jpeg";
87
+ sizeBytes: number;
88
+ base64: string;
89
+ savedTo?: string;
90
+ capturedAt: string;
91
+ /** Where the frame came from, e.g. "octoprint-settings" or "env-override". */
92
+ source: string;
93
+ }
94
+ /** outputSchema for counterpart_printer_info (PRD §8.4). */
95
+ export declare const PRINTER_INFO_OUTPUT_SCHEMA: {
96
+ readonly type: "object";
97
+ readonly required: readonly ["contract", "server", "printer", "capabilities"];
98
+ readonly properties: {
99
+ readonly contract: {
100
+ readonly type: "string";
101
+ readonly const: "counterpart/1";
102
+ };
103
+ readonly server: {
104
+ readonly type: "object";
105
+ readonly required: readonly ["name", "version"];
106
+ readonly properties: {
107
+ readonly name: {
108
+ readonly type: "string";
109
+ };
110
+ readonly version: {
111
+ readonly type: "string";
112
+ };
113
+ };
114
+ };
115
+ readonly printer: {
116
+ readonly type: "object";
117
+ readonly required: readonly ["kind", "host"];
118
+ readonly properties: {
119
+ readonly kind: {
120
+ readonly type: "string";
121
+ readonly enum: readonly ["bambu", "octoprint", "klipper", "prusa", "duet", "repetier", "creality"];
122
+ };
123
+ readonly model: {
124
+ readonly type: "string";
125
+ };
126
+ readonly serial: {
127
+ readonly type: "string";
128
+ };
129
+ readonly host: {
130
+ readonly type: "string";
131
+ };
132
+ readonly name: {
133
+ readonly type: "string";
134
+ };
135
+ };
136
+ };
137
+ readonly capabilities: {
138
+ readonly type: "object";
139
+ readonly required: readonly ["progress", "layers", "camera", "filaments", "pause", "resume", "cancel", "print3mf", "gcode"];
140
+ readonly properties: {
141
+ readonly progress: {
142
+ readonly type: "boolean";
143
+ };
144
+ readonly layers: {
145
+ readonly type: "boolean";
146
+ };
147
+ readonly camera: {
148
+ readonly type: "string";
149
+ readonly enum: readonly ["bambu-tcp", "rtsp", "http-snapshot", "none"];
150
+ };
151
+ readonly filaments: {
152
+ readonly type: "string";
153
+ readonly enum: readonly ["ams", "single", "none"];
154
+ };
155
+ readonly pause: {
156
+ readonly type: "boolean";
157
+ };
158
+ readonly resume: {
159
+ readonly type: "boolean";
160
+ };
161
+ readonly cancel: {
162
+ readonly type: "boolean";
163
+ };
164
+ readonly print3mf: {
165
+ readonly type: "boolean";
166
+ };
167
+ readonly gcode: {
168
+ readonly type: "boolean";
169
+ };
170
+ };
171
+ };
172
+ };
173
+ };
174
+ /** outputSchema for counterpart_print_progress (PRD §8.4). */
175
+ export declare const PRINT_PROGRESS_OUTPUT_SCHEMA: {
176
+ readonly type: "object";
177
+ readonly required: readonly ["contract", "observedAt", "state", "problems"];
178
+ readonly properties: {
179
+ readonly contract: {
180
+ readonly type: "string";
181
+ readonly const: "counterpart/1";
182
+ };
183
+ readonly observedAt: {
184
+ readonly type: "string";
185
+ };
186
+ readonly state: {
187
+ readonly type: "string";
188
+ readonly enum: readonly ["idle", "preparing", "printing", "paused", "finished", "failed", "cancelled", "offline", "busy"];
189
+ };
190
+ readonly job: {
191
+ readonly type: "object";
192
+ readonly required: readonly ["name"];
193
+ readonly properties: {
194
+ readonly name: {
195
+ readonly type: "string";
196
+ };
197
+ readonly id: {
198
+ readonly type: "string";
199
+ };
200
+ readonly progress: {
201
+ readonly type: "number";
202
+ readonly minimum: 0;
203
+ readonly maximum: 100;
204
+ };
205
+ readonly elapsedSec: {
206
+ readonly type: "number";
207
+ readonly minimum: 0;
208
+ };
209
+ readonly remainingSec: {
210
+ readonly type: "number";
211
+ readonly minimum: 0;
212
+ };
213
+ readonly layer: {
214
+ readonly type: "number";
215
+ };
216
+ readonly totalLayers: {
217
+ readonly type: "number";
218
+ };
219
+ readonly stage: {
220
+ readonly type: "string";
221
+ };
222
+ };
223
+ };
224
+ readonly temps: {
225
+ readonly type: "object";
226
+ readonly properties: {
227
+ readonly nozzle: {
228
+ type: string;
229
+ required: string[];
230
+ properties: {
231
+ actual: {
232
+ type: string;
233
+ };
234
+ target: {
235
+ type: string;
236
+ };
237
+ };
238
+ };
239
+ readonly bed: {
240
+ type: string;
241
+ required: string[];
242
+ properties: {
243
+ actual: {
244
+ type: string;
245
+ };
246
+ target: {
247
+ type: string;
248
+ };
249
+ };
250
+ };
251
+ readonly chamber: {
252
+ readonly type: "number";
253
+ };
254
+ };
255
+ };
256
+ readonly activeFilament: {
257
+ readonly type: "object";
258
+ readonly properties: {
259
+ readonly slot: {
260
+ readonly type: "string";
261
+ };
262
+ readonly material: {
263
+ readonly type: "string";
264
+ };
265
+ readonly color: {
266
+ readonly type: "string";
267
+ };
268
+ };
269
+ };
270
+ readonly problems: {
271
+ readonly type: "array";
272
+ readonly items: {
273
+ type: string;
274
+ required: string[];
275
+ properties: {
276
+ code: {
277
+ type: string;
278
+ };
279
+ severity: {
280
+ type: string;
281
+ enum: ("error" | "fatal" | "warning" | "info")[];
282
+ };
283
+ message: {
284
+ type: string;
285
+ };
286
+ url: {
287
+ type: string;
288
+ };
289
+ };
290
+ };
291
+ };
292
+ };
293
+ };
294
+ /** outputSchema for camera_snapshot; the result matches bambu-printer-mcp's plus capturedAt. */
295
+ export declare const CAMERA_SNAPSHOT_OUTPUT_SCHEMA: {
296
+ readonly type: "object";
297
+ readonly required: readonly ["status", "format", "sizeBytes", "base64", "capturedAt"];
298
+ readonly properties: {
299
+ readonly status: {
300
+ readonly type: "string";
301
+ };
302
+ readonly format: {
303
+ readonly type: "string";
304
+ };
305
+ readonly sizeBytes: {
306
+ readonly type: "number";
307
+ };
308
+ readonly base64: {
309
+ readonly type: "string";
310
+ };
311
+ readonly savedTo: {
312
+ readonly type: "string";
313
+ };
314
+ readonly capturedAt: {
315
+ readonly type: "string";
316
+ };
317
+ readonly source: {
318
+ readonly type: "string";
319
+ };
320
+ };
321
+ };
322
+ /** outputSchema for get_printer_filaments in this server: one external slot. */
323
+ export declare const FILAMENTS_OUTPUT_SCHEMA: {
324
+ readonly type: "object";
325
+ readonly required: readonly ["slots"];
326
+ readonly properties: {
327
+ readonly slots: {
328
+ readonly type: "array";
329
+ readonly items: {
330
+ readonly type: "object";
331
+ readonly required: readonly ["id", "source"];
332
+ readonly properties: {
333
+ readonly id: {
334
+ readonly type: "string";
335
+ };
336
+ readonly material: {
337
+ readonly type: "string";
338
+ };
339
+ readonly color: {
340
+ readonly type: "string";
341
+ };
342
+ readonly name: {
343
+ readonly type: "string";
344
+ };
345
+ readonly source: {
346
+ readonly type: "string";
347
+ readonly enum: readonly ["spoolman", "unknown"];
348
+ };
349
+ readonly profile: {
350
+ readonly type: "string";
351
+ readonly description: "Absolute path of the installed slicer filament profile for this slot's material, which slice_stl accepts as load_filaments. Omitted when none resolves.";
352
+ };
353
+ };
354
+ };
355
+ };
356
+ };
357
+ };
358
+ /** outputSchema for pause_print and resume_print. */
359
+ export declare const CONTROL_OUTPUT_SCHEMA: {
360
+ readonly type: "object";
361
+ readonly required: readonly ["status", "message"];
362
+ readonly properties: {
363
+ readonly status: {
364
+ readonly type: "string";
365
+ };
366
+ readonly message: {
367
+ readonly type: "string";
368
+ };
369
+ readonly action: {
370
+ readonly type: "string";
371
+ readonly enum: readonly ["pause", "resume"];
372
+ };
373
+ readonly jobName: {
374
+ readonly type: "string";
375
+ };
376
+ };
377
+ };
378
+ export declare function finiteNumber(value: unknown): number | undefined;
379
+ /** A percentage clamped to 0–100, rounded to 0.1. */
380
+ export declare function percent(value: unknown): number | undefined;
381
+ /** Non-negative whole seconds. */
382
+ export declare function seconds(value: unknown): number | undefined;
383
+ /** A positive integer layer count or index. */
384
+ export declare function layerNumber(value: unknown): number | undefined;
385
+ export declare function nonEmptyText(value: unknown): string | undefined;
386
+ /** {actual, target} when the actual reading is finite; a missing target means off (0). */
387
+ export declare function heaterReading(actual: unknown, target: unknown): HeaterReading | undefined;
388
+ /**
389
+ * The job name the printer reports, reduced to its file name so it matches the
390
+ * uploaded `jobName` (Duet reports 0:/gcodes/x.gcode, Klipper gcodes-relative paths).
391
+ */
392
+ export declare function remoteJobName(value: unknown): string | undefined;
393
+ /** Remove undefined members recursively so results never carry null/undefined fields. */
394
+ export declare function compact<T>(value: T): T;
395
+ export declare function progressResult(observedAt: string, state: ProgressState, parts: Omit<Partial<CounterpartProgress>, "contract" | "observedAt" | "state">): CounterpartProgress;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The Counterpart tool contract (`counterpart/1`). bambu-printer-mcp and this
3
+ * server return the same shapes so one client can treat both alike. Optional
4
+ * fields are omitted, never null: JSON Schema `number` rejects null.
5
+ */
6
+ import path from "node:path";
7
+ export const COUNTERPART_CONTRACT = "counterpart/1";
8
+ export const PRINTER_KINDS = ["bambu", "octoprint", "klipper", "prusa", "duet", "repetier", "creality"];
9
+ export const PROGRESS_STATES = ["idle", "preparing", "printing", "paused", "finished", "failed", "cancelled", "offline", "busy"];
10
+ export const PROBLEM_SEVERITIES = ["fatal", "error", "warning", "info"];
11
+ const heater = {
12
+ type: "object",
13
+ required: ["actual", "target"],
14
+ properties: { actual: { type: "number" }, target: { type: "number" } },
15
+ };
16
+ const problem = {
17
+ type: "object",
18
+ required: ["code", "severity", "message"],
19
+ properties: {
20
+ code: { type: "string" },
21
+ severity: { type: "string", enum: [...PROBLEM_SEVERITIES] },
22
+ message: { type: "string" },
23
+ url: { type: "string" },
24
+ },
25
+ };
26
+ /** outputSchema for counterpart_printer_info (PRD §8.4). */
27
+ export const PRINTER_INFO_OUTPUT_SCHEMA = {
28
+ type: "object",
29
+ required: ["contract", "server", "printer", "capabilities"],
30
+ properties: {
31
+ contract: { type: "string", const: COUNTERPART_CONTRACT },
32
+ server: {
33
+ type: "object",
34
+ required: ["name", "version"],
35
+ properties: { name: { type: "string" }, version: { type: "string" } },
36
+ },
37
+ printer: {
38
+ type: "object",
39
+ required: ["kind", "host"],
40
+ properties: {
41
+ kind: { type: "string", enum: [...PRINTER_KINDS] },
42
+ model: { type: "string" },
43
+ serial: { type: "string" },
44
+ host: { type: "string" },
45
+ name: { type: "string" },
46
+ },
47
+ },
48
+ capabilities: {
49
+ type: "object",
50
+ required: ["progress", "layers", "camera", "filaments", "pause", "resume", "cancel", "print3mf", "gcode"],
51
+ properties: {
52
+ progress: { type: "boolean" },
53
+ layers: { type: "boolean" },
54
+ camera: { type: "string", enum: ["bambu-tcp", "rtsp", "http-snapshot", "none"] },
55
+ filaments: { type: "string", enum: ["ams", "single", "none"] },
56
+ pause: { type: "boolean" },
57
+ resume: { type: "boolean" },
58
+ cancel: { type: "boolean" },
59
+ print3mf: { type: "boolean" },
60
+ gcode: { type: "boolean" },
61
+ },
62
+ },
63
+ },
64
+ };
65
+ /** outputSchema for counterpart_print_progress (PRD §8.4). */
66
+ export const PRINT_PROGRESS_OUTPUT_SCHEMA = {
67
+ type: "object",
68
+ required: ["contract", "observedAt", "state", "problems"],
69
+ properties: {
70
+ contract: { type: "string", const: COUNTERPART_CONTRACT },
71
+ observedAt: { type: "string" },
72
+ state: { type: "string", enum: [...PROGRESS_STATES] },
73
+ job: {
74
+ type: "object",
75
+ required: ["name"],
76
+ properties: {
77
+ name: { type: "string" },
78
+ id: { type: "string" },
79
+ progress: { type: "number", minimum: 0, maximum: 100 },
80
+ elapsedSec: { type: "number", minimum: 0 },
81
+ remainingSec: { type: "number", minimum: 0 },
82
+ layer: { type: "number" },
83
+ totalLayers: { type: "number" },
84
+ stage: { type: "string" },
85
+ },
86
+ },
87
+ temps: {
88
+ type: "object",
89
+ properties: { nozzle: heater, bed: heater, chamber: { type: "number" } },
90
+ },
91
+ activeFilament: {
92
+ type: "object",
93
+ properties: { slot: { type: "string" }, material: { type: "string" }, color: { type: "string" } },
94
+ },
95
+ problems: { type: "array", items: problem },
96
+ },
97
+ };
98
+ /** outputSchema for camera_snapshot; the result matches bambu-printer-mcp's plus capturedAt. */
99
+ export const CAMERA_SNAPSHOT_OUTPUT_SCHEMA = {
100
+ type: "object",
101
+ required: ["status", "format", "sizeBytes", "base64", "capturedAt"],
102
+ properties: {
103
+ status: { type: "string" },
104
+ format: { type: "string" },
105
+ sizeBytes: { type: "number" },
106
+ base64: { type: "string" },
107
+ savedTo: { type: "string" },
108
+ capturedAt: { type: "string" },
109
+ source: { type: "string" },
110
+ },
111
+ };
112
+ /** outputSchema for get_printer_filaments in this server: one external slot. */
113
+ export const FILAMENTS_OUTPUT_SCHEMA = {
114
+ type: "object",
115
+ required: ["slots"],
116
+ properties: {
117
+ slots: {
118
+ type: "array",
119
+ items: {
120
+ type: "object",
121
+ required: ["id", "source"],
122
+ properties: {
123
+ id: { type: "string" },
124
+ material: { type: "string" },
125
+ color: { type: "string" },
126
+ name: { type: "string" },
127
+ source: { type: "string", enum: ["spoolman", "unknown"] },
128
+ profile: {
129
+ type: "string",
130
+ description: "Absolute path of the installed slicer filament profile for this slot's material, which slice_stl accepts as load_filaments. Omitted when none resolves.",
131
+ },
132
+ },
133
+ },
134
+ },
135
+ },
136
+ };
137
+ /** outputSchema for pause_print and resume_print. */
138
+ export const CONTROL_OUTPUT_SCHEMA = {
139
+ type: "object",
140
+ required: ["status", "message"],
141
+ properties: {
142
+ status: { type: "string" },
143
+ message: { type: "string" },
144
+ action: { type: "string", enum: ["pause", "resume"] },
145
+ jobName: { type: "string" },
146
+ },
147
+ };
148
+ // ---- Value helpers: every optional number is finite or omitted. ----
149
+ export function finiteNumber(value) {
150
+ if (typeof value === "number")
151
+ return Number.isFinite(value) ? value : undefined;
152
+ if (typeof value === "string" && value.trim() !== "") {
153
+ const parsed = Number(value);
154
+ return Number.isFinite(parsed) ? parsed : undefined;
155
+ }
156
+ return undefined;
157
+ }
158
+ /** A percentage clamped to 0–100, rounded to 0.1. */
159
+ export function percent(value) {
160
+ const number = finiteNumber(value);
161
+ if (number === undefined)
162
+ return undefined;
163
+ return Math.round(Math.min(100, Math.max(0, number)) * 10) / 10;
164
+ }
165
+ /** Non-negative whole seconds. */
166
+ export function seconds(value) {
167
+ const number = finiteNumber(value);
168
+ if (number === undefined || number < 0)
169
+ return undefined;
170
+ return Math.round(number);
171
+ }
172
+ /** A positive integer layer count or index. */
173
+ export function layerNumber(value) {
174
+ const number = finiteNumber(value);
175
+ return number !== undefined && number > 0 ? Math.round(number) : undefined;
176
+ }
177
+ export function nonEmptyText(value) {
178
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
179
+ }
180
+ /** {actual, target} when the actual reading is finite; a missing target means off (0). */
181
+ export function heaterReading(actual, target) {
182
+ const value = finiteNumber(actual);
183
+ if (value === undefined)
184
+ return undefined;
185
+ return { actual: Math.round(value * 10) / 10, target: Math.round((finiteNumber(target) ?? 0) * 10) / 10 };
186
+ }
187
+ /**
188
+ * The job name the printer reports, reduced to its file name so it matches the
189
+ * uploaded `jobName` (Duet reports 0:/gcodes/x.gcode, Klipper gcodes-relative paths).
190
+ */
191
+ export function remoteJobName(value) {
192
+ const text = nonEmptyText(value);
193
+ if (!text)
194
+ return undefined;
195
+ const name = path.posix.basename(text.replace(/\\/g, "/").replace(/^\d+:/, ""));
196
+ return name || undefined;
197
+ }
198
+ /** Remove undefined members recursively so results never carry null/undefined fields. */
199
+ export function compact(value) {
200
+ if (Array.isArray(value))
201
+ return value.map((entry) => compact(entry));
202
+ if (value && typeof value === "object") {
203
+ const entries = Object.entries(value)
204
+ .filter(([, entry]) => entry !== undefined && entry !== null && !(typeof entry === "number" && !Number.isFinite(entry)))
205
+ .map(([key, entry]) => [key, compact(entry)]);
206
+ return Object.fromEntries(entries.filter(([, entry]) => !(entry && typeof entry === "object" && !Array.isArray(entry) && Object.keys(entry).length === 0)));
207
+ }
208
+ return value;
209
+ }
210
+ export function progressResult(observedAt, state, parts) {
211
+ return compact({
212
+ contract: COUNTERPART_CONTRACT,
213
+ observedAt,
214
+ state,
215
+ job: parts.job,
216
+ temps: parts.temps,
217
+ activeFilament: parts.activeFilament,
218
+ problems: parts.problems ?? [],
219
+ });
220
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Per-firmware mapping for counterpart_print_progress (PRD §8.4). Each function
3
+ * is pure: it receives the firmware's own JSON and returns the shared shape.
4
+ * Sources are cited per mapper; rows marked "simulator" were also checked
5
+ * against a running instance (see CHANGELOG evidence notes).
6
+ */
7
+ import { type CounterpartProgress, type ProblemSeverity, type ProgressState } from "./contract.js";
8
+ export interface OctoPrintInputs {
9
+ job: any;
10
+ /** GET /api/printer body; undefined when it returned 409 (not operational). */
11
+ printer?: any;
12
+ /** prints.last of the selected file, read only when an Operational job has no progress. */
13
+ lastPrint?: {
14
+ success?: unknown;
15
+ };
16
+ }
17
+ export declare function octoPrintState(text: string, completion: number | undefined, hasFile: boolean, lastPrint?: {
18
+ success?: unknown;
19
+ }): ProgressState;
20
+ export declare function mapOctoPrint(inputs: OctoPrintInputs, observedAt: string): CounterpartProgress;
21
+ export declare function mapMoonraker(status: any, estimatedTime: number | undefined, observedAt: string): CounterpartProgress;
22
+ export declare function mapPrusaLink(status: any, jobDetails: any | undefined, observedAt: string): CounterpartProgress;
23
+ export declare function mapDuet(stateModel: any, jobModel: any, observedAt: string): CounterpartProgress;
24
+ export declare function mapRepetier(printer: any, observedAt: string): CounterpartProgress;
25
+ export declare function mapCrealityDevice(data: any, observedAt: string): CounterpartProgress;
26
+ /** print_error for a user cancel; reported with gcode_state FAILED. */
27
+ export declare const BAMBU_CANCEL_ERROR = 50348044;
28
+ export declare function bambuHmsSeverity(code: number): ProblemSeverity;
29
+ export declare function mapBambu(raw: any, observedAt: string, options: {
30
+ connected: boolean;
31
+ model?: string;
32
+ }): CounterpartProgress;