@reaatech/media-pipeline-mcp-comfyui 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,662 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ComfyUIProvider: () => ComfyUIProvider,
34
+ createComfyUIProvider: () => createComfyUIProvider
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/comfyui-provider.ts
39
+ var import_media_pipeline_mcp_core = require("@reaatech/media-pipeline-mcp-core");
40
+ var import_media_pipeline_mcp_provider_core = require("@reaatech/media-pipeline-mcp-provider-core");
41
+
42
+ // src/workflows/flux-text2img.json
43
+ var flux_text2img_default = {
44
+ "3": {
45
+ class_type: "KSampler",
46
+ inputs: {
47
+ seed: -1,
48
+ steps: 20,
49
+ cfg: 1,
50
+ sampler_name: "euler",
51
+ scheduler: "simple",
52
+ denoise: 1,
53
+ model: ["4", 0],
54
+ positive: ["6", 0],
55
+ negative: ["7", 0],
56
+ latent_image: ["5", 0]
57
+ }
58
+ },
59
+ "4": {
60
+ class_type: "CheckpointLoaderSimple",
61
+ inputs: {
62
+ ckpt_name: "flux1-dev.safetensors"
63
+ }
64
+ },
65
+ "5": {
66
+ class_type: "EmptyLatentImage",
67
+ inputs: {
68
+ width: 1024,
69
+ height: 1024,
70
+ batch_size: 1
71
+ }
72
+ },
73
+ "6": {
74
+ class_type: "CLIPTextEncode",
75
+ inputs: {
76
+ text: "",
77
+ clip: ["4", 1]
78
+ }
79
+ },
80
+ "7": {
81
+ class_type: "CLIPTextEncode",
82
+ inputs: {
83
+ text: "",
84
+ clip: ["4", 1]
85
+ }
86
+ },
87
+ "8": {
88
+ class_type: "VAEDecode",
89
+ inputs: {
90
+ samples: ["3", 0],
91
+ vae: ["4", 2]
92
+ }
93
+ },
94
+ "9": {
95
+ class_type: "SaveImage",
96
+ inputs: {
97
+ filename_prefix: "ComfyUI",
98
+ images: ["8", 0]
99
+ }
100
+ }
101
+ };
102
+
103
+ // src/workflows/sdxl-img2img.json
104
+ var sdxl_img2img_default = {
105
+ "3": {
106
+ class_type: "KSampler",
107
+ inputs: {
108
+ seed: -1,
109
+ steps: 20,
110
+ cfg: 7,
111
+ sampler_name: "euler",
112
+ scheduler: "normal",
113
+ denoise: 0.75,
114
+ model: ["4", 0],
115
+ positive: ["6", 0],
116
+ negative: ["7", 0],
117
+ latent_image: ["11", 0]
118
+ }
119
+ },
120
+ "4": {
121
+ class_type: "CheckpointLoaderSimple",
122
+ inputs: {
123
+ ckpt_name: "sd_xl_base_1.0.safetensors"
124
+ }
125
+ },
126
+ "6": {
127
+ class_type: "CLIPTextEncode",
128
+ inputs: {
129
+ text: "",
130
+ clip: ["4", 1]
131
+ }
132
+ },
133
+ "7": {
134
+ class_type: "CLIPTextEncode",
135
+ inputs: {
136
+ text: "",
137
+ clip: ["4", 1]
138
+ }
139
+ },
140
+ "8": {
141
+ class_type: "VAEDecode",
142
+ inputs: {
143
+ samples: ["3", 0],
144
+ vae: ["4", 2]
145
+ }
146
+ },
147
+ "9": {
148
+ class_type: "SaveImage",
149
+ inputs: {
150
+ filename_prefix: "ComfyUI",
151
+ images: ["8", 0]
152
+ }
153
+ },
154
+ "10": {
155
+ class_type: "LoadImage",
156
+ inputs: {
157
+ image: "input.png"
158
+ }
159
+ },
160
+ "11": {
161
+ class_type: "VAEEncode",
162
+ inputs: {
163
+ pixels: ["10", 0],
164
+ vae: ["4", 2]
165
+ }
166
+ }
167
+ };
168
+
169
+ // src/workflows/sdxl-text2img.json
170
+ var sdxl_text2img_default = {
171
+ "3": {
172
+ class_type: "KSampler",
173
+ inputs: {
174
+ seed: -1,
175
+ steps: 20,
176
+ cfg: 7,
177
+ sampler_name: "euler",
178
+ scheduler: "normal",
179
+ denoise: 1,
180
+ model: ["4", 0],
181
+ positive: ["6", 0],
182
+ negative: ["7", 0],
183
+ latent_image: ["5", 0]
184
+ }
185
+ },
186
+ "4": {
187
+ class_type: "CheckpointLoaderSimple",
188
+ inputs: {
189
+ ckpt_name: "sd_xl_base_1.0.safetensors"
190
+ }
191
+ },
192
+ "5": {
193
+ class_type: "EmptyLatentImage",
194
+ inputs: {
195
+ width: 1024,
196
+ height: 1024,
197
+ batch_size: 1
198
+ }
199
+ },
200
+ "6": {
201
+ class_type: "CLIPTextEncode",
202
+ inputs: {
203
+ text: "",
204
+ clip: ["4", 1]
205
+ }
206
+ },
207
+ "7": {
208
+ class_type: "CLIPTextEncode",
209
+ inputs: {
210
+ text: "",
211
+ clip: ["4", 1]
212
+ }
213
+ },
214
+ "8": {
215
+ class_type: "VAEDecode",
216
+ inputs: {
217
+ samples: ["3", 0],
218
+ vae: ["4", 2]
219
+ }
220
+ },
221
+ "9": {
222
+ class_type: "SaveImage",
223
+ inputs: {
224
+ filename_prefix: "ComfyUI",
225
+ images: ["8", 0]
226
+ }
227
+ }
228
+ };
229
+
230
+ // src/workflows/svd-img2vid.json
231
+ var svd_img2vid_default = {
232
+ "3": {
233
+ class_type: "KSampler",
234
+ inputs: {
235
+ seed: -1,
236
+ steps: 20,
237
+ cfg: 2.5,
238
+ sampler_name: "euler",
239
+ scheduler: "karras",
240
+ denoise: 1,
241
+ model: ["15", 0],
242
+ positive: ["12", 0],
243
+ negative: ["12", 1],
244
+ latent_image: ["12", 2]
245
+ }
246
+ },
247
+ "8": {
248
+ class_type: "VAEDecode",
249
+ inputs: {
250
+ samples: ["3", 0],
251
+ vae: ["15", 2]
252
+ }
253
+ },
254
+ "10": {
255
+ class_type: "LoadImage",
256
+ inputs: {
257
+ image: "input.png"
258
+ }
259
+ },
260
+ "12": {
261
+ class_type: "SVD_img2vid_Conditioning",
262
+ inputs: {
263
+ width: 1024,
264
+ height: 576,
265
+ video_frames: 14,
266
+ motion_bucket_id: 127,
267
+ fps: 6,
268
+ augmentation_level: 0,
269
+ clip_vision: ["15", 1],
270
+ init_image: ["10", 0],
271
+ vae: ["15", 2]
272
+ }
273
+ },
274
+ "15": {
275
+ class_type: "ImageOnlyCheckpointLoader",
276
+ inputs: {
277
+ ckpt_name: "svd_xt.safetensors"
278
+ }
279
+ },
280
+ "20": {
281
+ class_type: "SaveAnimatedWEBP",
282
+ inputs: {
283
+ filename_prefix: "SVD",
284
+ fps: 6,
285
+ lossless: false,
286
+ quality: 90,
287
+ method: "default",
288
+ images: ["8", 0]
289
+ }
290
+ }
291
+ };
292
+
293
+ // src/comfyui-provider.ts
294
+ var BUILT_IN_WORKFLOWS = {
295
+ "sdxl-text2img": {
296
+ name: "SDXL Text-to-Image",
297
+ apiFormat: sdxl_text2img_default,
298
+ inputs: {
299
+ prompt: { path: "6.inputs.text", type: "string", required: true },
300
+ negative_prompt: { path: "7.inputs.text", type: "string", default: "" },
301
+ seed: { path: "3.inputs.seed", type: "number", default: -1 },
302
+ steps: { path: "3.inputs.steps", type: "number", default: 20 },
303
+ cfg: { path: "3.inputs.cfg", type: "number", default: 7 },
304
+ width: { path: "5.inputs.width", type: "number", default: 1024 },
305
+ height: { path: "5.inputs.height", type: "number", default: 1024 }
306
+ },
307
+ outputs: {
308
+ "9": "image"
309
+ }
310
+ },
311
+ "sdxl-img2img": {
312
+ name: "SDXL Image-to-Image",
313
+ apiFormat: sdxl_img2img_default,
314
+ inputs: {
315
+ prompt: { path: "6.inputs.text", type: "string", required: true },
316
+ negative_prompt: { path: "7.inputs.text", type: "string", default: "" },
317
+ seed: { path: "3.inputs.seed", type: "number", default: -1 },
318
+ steps: { path: "3.inputs.steps", type: "number", default: 20 },
319
+ cfg: { path: "3.inputs.cfg", type: "number", default: 7 },
320
+ denoise: { path: "3.inputs.denoise", type: "number", default: 0.75 },
321
+ image: { path: "10.inputs.image", type: "string", required: true }
322
+ },
323
+ outputs: {
324
+ "9": "image"
325
+ }
326
+ },
327
+ "flux-text2img": {
328
+ name: "Flux.1-dev Text-to-Image",
329
+ apiFormat: flux_text2img_default,
330
+ inputs: {
331
+ prompt: { path: "6.inputs.text", type: "string", required: true },
332
+ negative_prompt: { path: "7.inputs.text", type: "string", default: "" },
333
+ seed: { path: "3.inputs.seed", type: "number", default: -1 },
334
+ steps: { path: "3.inputs.steps", type: "number", default: 20 },
335
+ cfg: { path: "3.inputs.cfg", type: "number", default: 1 },
336
+ width: { path: "5.inputs.width", type: "number", default: 1024 },
337
+ height: { path: "5.inputs.height", type: "number", default: 1024 }
338
+ },
339
+ outputs: {
340
+ "9": "image"
341
+ }
342
+ },
343
+ "svd-img2vid": {
344
+ name: "Stable Video Diffusion Image-to-Video",
345
+ apiFormat: svd_img2vid_default,
346
+ inputs: {
347
+ image: { path: "10.inputs.image", type: "string", required: true },
348
+ seed: { path: "3.inputs.seed", type: "number", default: -1 },
349
+ steps: { path: "3.inputs.steps", type: "number", default: 20 },
350
+ cfg: { path: "3.inputs.cfg", type: "number", default: 2.5 },
351
+ width: { path: "12.inputs.width", type: "number", default: 1024 },
352
+ height: { path: "12.inputs.height", type: "number", default: 576 },
353
+ video_frames: { path: "12.inputs.video_frames", type: "number", default: 14 },
354
+ motion_bucket_id: { path: "12.inputs.motion_bucket_id", type: "number", default: 127 },
355
+ fps: { path: "12.inputs.fps", type: "number", default: 6 }
356
+ },
357
+ outputs: {
358
+ "20": "video"
359
+ }
360
+ }
361
+ };
362
+ var ComfyUIProvider = class extends import_media_pipeline_mcp_provider_core.MediaProvider {
363
+ static id = "comfyui";
364
+ name = "comfyui";
365
+ supportedOperations = ["image.generate", "image.edit", "video.generate"];
366
+ /**
367
+ * §0.6 capability declarations. ComfyUI exposes per-node progress as it executes a
368
+ * workflow (we surface it via the F6 progress bridge during pollForCompletion), but
369
+ * does not push outbound webhooks.
370
+ */
371
+ supportsStreaming = /* @__PURE__ */ new Set(["image.generate", "image.edit", "video.generate"]);
372
+ supportsWebhooks = false;
373
+ /**
374
+ * F2 cacheConfig per plan §F10 "Per-implementation features":
375
+ * "det per fixed `seed`; cache enabled when seed is provided and non-negative"
376
+ *
377
+ * All workflow inputs are deterministic given a fixed seed. The non-det list is
378
+ * empty because there is no provider-side request id (ComfyUI's `prompt_id` is
379
+ * generated post-submit and is not user-supplied). Normalize trims string params.
380
+ */
381
+ static cacheConfig = {
382
+ deterministicParams: [
383
+ "prompt",
384
+ "negative_prompt",
385
+ "seed",
386
+ "steps",
387
+ "cfg",
388
+ "denoise",
389
+ "width",
390
+ "height",
391
+ "image",
392
+ "model",
393
+ "sampler",
394
+ "scheduler",
395
+ "video_frames",
396
+ "motion_bucket_id",
397
+ "fps",
398
+ "dimensions"
399
+ ],
400
+ nonDeterministicParams: [],
401
+ normalize: (inputs) => {
402
+ const out = {};
403
+ for (const [k, v] of Object.entries(inputs)) {
404
+ out[k] = typeof v === "string" ? v.trim().replace(/\s+/g, " ") : v;
405
+ }
406
+ return out;
407
+ }
408
+ };
409
+ baseUrl;
410
+ pollIntervalMs;
411
+ retentionMs;
412
+ downloadOutputs;
413
+ workflows;
414
+ workflowsDir;
415
+ workflowsDirLoaded = false;
416
+ constructor(config = {}) {
417
+ super();
418
+ this.baseUrl = config.baseUrl ?? "http://localhost:8188";
419
+ this.pollIntervalMs = config.pollIntervalMs ?? 1e3;
420
+ this.retentionMs = config.retentionMs ?? 6e5;
421
+ this.downloadOutputs = config.downloadOutputs ?? true;
422
+ this.workflows = { ...BUILT_IN_WORKFLOWS };
423
+ this.workflowsDir = config.workflowsDir;
424
+ }
425
+ /**
426
+ * Plan §F10 "Mechanism (ComfyUI) #1": load user workflows from `workflowsDir/*.json`
427
+ * at construction time. We do it lazily (first execute / explicit call) so that the
428
+ * constructor stays sync and tests can mock fs.
429
+ *
430
+ * Each file becomes `custom/<basename>` workflow. The JSON must declare `apiFormat`,
431
+ * `inputs`, and `outputs` keys (the ComfyUIWorkflow shape). Files that don't match
432
+ * the shape are skipped with a warning.
433
+ */
434
+ async loadWorkflowsFromDir() {
435
+ if (this.workflowsDirLoaded || !this.workflowsDir) return;
436
+ this.workflowsDirLoaded = true;
437
+ try {
438
+ const fs = await import("fs/promises");
439
+ const path = await import("path");
440
+ const entries = await fs.readdir(this.workflowsDir);
441
+ for (const entry of entries) {
442
+ if (!entry.endsWith(".json")) continue;
443
+ const full = path.join(this.workflowsDir, entry);
444
+ try {
445
+ const raw = await fs.readFile(full, "utf8");
446
+ const parsed = JSON.parse(raw);
447
+ if (typeof parsed.apiFormat !== "object" || typeof parsed.inputs !== "object") {
448
+ console.warn(`ComfyUI: skipping workflow ${entry} \u2014 missing apiFormat/inputs`);
449
+ continue;
450
+ }
451
+ const slug = `custom/${path.basename(entry, ".json")}`;
452
+ this.workflows[slug] = parsed;
453
+ } catch (err) {
454
+ console.warn(`ComfyUI: failed to load workflow ${entry}: ${err.message}`);
455
+ }
456
+ }
457
+ } catch (err) {
458
+ console.warn(
459
+ `ComfyUI: workflowsDir '${this.workflowsDir}' unreadable: ${err.message}`
460
+ );
461
+ }
462
+ }
463
+ async healthCheck() {
464
+ const startTime = Date.now();
465
+ try {
466
+ const response = await fetch(`${this.baseUrl}/system_stats`, {
467
+ signal: AbortSignal.timeout(1e4)
468
+ });
469
+ if (response.ok) {
470
+ return {
471
+ healthy: true,
472
+ latency: Date.now() - startTime
473
+ };
474
+ }
475
+ return {
476
+ healthy: false,
477
+ latency: Date.now() - startTime,
478
+ error: `HTTP ${response.status}: ${response.statusText}`
479
+ };
480
+ } catch (error) {
481
+ return {
482
+ healthy: false,
483
+ latency: Date.now() - startTime,
484
+ error: error.message
485
+ };
486
+ }
487
+ }
488
+ async estimateCost(_input) {
489
+ return {
490
+ costUsd: 0,
491
+ currency: "USD"
492
+ };
493
+ }
494
+ registerWorkflow(name, workflow) {
495
+ this.workflows[name] = workflow;
496
+ }
497
+ getWorkflow(name) {
498
+ return this.workflows[name];
499
+ }
500
+ listWorkflows() {
501
+ return Object.keys(this.workflows);
502
+ }
503
+ async execute(input) {
504
+ await this.loadWorkflowsFromDir();
505
+ const explicit = this.resolveWorkflowName(input);
506
+ if (explicit) {
507
+ return this.runWorkflow(explicit, input);
508
+ }
509
+ switch (input.operation) {
510
+ case "image.generate":
511
+ return this.runWorkflow("sdxl-text2img", input);
512
+ case "image.edit":
513
+ return this.runWorkflow("sdxl-img2img", input);
514
+ case "video.generate":
515
+ return this.runWorkflow("svd-img2vid", input);
516
+ default:
517
+ throw new Error(`Unsupported operation: ${input.operation}`);
518
+ }
519
+ }
520
+ resolveWorkflowName(input) {
521
+ const model = input.params?.model ?? input.model;
522
+ if (!model) return null;
523
+ if (!model.startsWith("workflow:")) return null;
524
+ return model.slice("workflow:".length);
525
+ }
526
+ async runWorkflow(workflowName, input) {
527
+ const startTime = Date.now();
528
+ const workflow = this.workflows[workflowName];
529
+ if (!workflow) {
530
+ throw new import_media_pipeline_mcp_core.WorkflowNotFoundError(workflowName);
531
+ }
532
+ const prompt = structuredClone(workflow.apiFormat);
533
+ for (const [paramName, spec] of Object.entries(workflow.inputs)) {
534
+ const value = input.params[paramName] ?? spec.default;
535
+ if (value === void 0 && spec.required) {
536
+ throw new import_media_pipeline_mcp_core.InvalidInputError(`Missing required input: ${paramName}`);
537
+ }
538
+ if (value !== void 0) {
539
+ this.setNestedValue(prompt, spec.path, value);
540
+ }
541
+ }
542
+ const dims = input.params.dimensions;
543
+ if (dims) {
544
+ const parts = dims.split("x").map(Number);
545
+ if (parts.length === 2 && !Number.isNaN(parts[0]) && !Number.isNaN(parts[1])) {
546
+ this.setNestedValue(prompt, "5.inputs.width", parts[0]);
547
+ this.setNestedValue(prompt, "5.inputs.height", parts[1]);
548
+ }
549
+ }
550
+ const response = await fetch(`${this.baseUrl}/prompt`, {
551
+ method: "POST",
552
+ headers: { "Content-Type": "application/json" },
553
+ body: JSON.stringify({ prompt })
554
+ });
555
+ if (!response.ok) {
556
+ const errorText = await response.text();
557
+ throw new Error(`ComfyUI error: ${errorText}`);
558
+ }
559
+ const promptResult = await response.json();
560
+ const promptId = promptResult.prompt_id;
561
+ const outputs = await this.pollForCompletion(promptId);
562
+ if (this.downloadOutputs && Object.keys(outputs).length > 0) {
563
+ const firstOutput = Object.values(outputs)[0];
564
+ if (firstOutput.images && firstOutput.images.length > 0) {
565
+ const img = firstOutput.images[0];
566
+ const imageUrl = `${this.baseUrl}/view?filename=${img.filename}&subfolder=${img.subfolder}&type=${img.type}`;
567
+ const imageResponse = await fetch(imageUrl);
568
+ if (!imageResponse.ok) {
569
+ throw new Error(`Failed to download output image: ${imageResponse.statusText}`);
570
+ }
571
+ const arrayBuffer = await imageResponse.arrayBuffer();
572
+ const buffer = Buffer.from(arrayBuffer);
573
+ return {
574
+ data: buffer,
575
+ mimeType: this.mimeTypeFromFilename(img.filename),
576
+ metadata: {
577
+ type: "image",
578
+ prompt_id: promptId,
579
+ workflow: workflowName,
580
+ filename: img.filename
581
+ },
582
+ costUsd: 0,
583
+ durationMs: Date.now() - startTime
584
+ };
585
+ }
586
+ }
587
+ throw new Error("No output images produced by workflow");
588
+ }
589
+ async pollForCompletion(promptId) {
590
+ const deadline = Date.now() + this.retentionMs;
591
+ while (Date.now() < deadline) {
592
+ await this.sleep(this.pollIntervalMs);
593
+ try {
594
+ const response = await fetch(`${this.baseUrl}/history/${promptId}`, {
595
+ signal: AbortSignal.timeout(1e4)
596
+ });
597
+ if (response.status === 404) {
598
+ continue;
599
+ }
600
+ if (!response.ok) {
601
+ continue;
602
+ }
603
+ const history = await response.json();
604
+ const entry = history[promptId];
605
+ if (entry?.status.completed) {
606
+ return entry.outputs;
607
+ }
608
+ if (entry && entry.status.status_str === "error") {
609
+ throw new Error("ComfyUI workflow execution failed");
610
+ }
611
+ } catch {
612
+ }
613
+ }
614
+ throw new import_media_pipeline_mcp_core.WorkflowExpiredError();
615
+ }
616
+ setNestedValue(obj, path, value) {
617
+ const keys = path.split(".");
618
+ let current = obj;
619
+ for (let i = 0; i < keys.length - 1; i++) {
620
+ const key = keys[i];
621
+ if (!(key in current)) {
622
+ current[key] = {};
623
+ }
624
+ current = current[key];
625
+ }
626
+ const lastKey = keys[keys.length - 1];
627
+ if (lastKey === "width" || lastKey === "height") {
628
+ current[lastKey] = typeof value === "string" ? Number(value) : value;
629
+ } else {
630
+ current[lastKey] = value;
631
+ }
632
+ }
633
+ mimeTypeFromFilename(filename) {
634
+ const ext = filename.split(".").pop()?.toLowerCase();
635
+ switch (ext) {
636
+ case "png":
637
+ return "image/png";
638
+ case "jpg":
639
+ case "jpeg":
640
+ return "image/jpeg";
641
+ case "webp":
642
+ return "image/webp";
643
+ case "mp4":
644
+ return "video/mp4";
645
+ case "webm":
646
+ return "video/webm";
647
+ default:
648
+ return "application/octet-stream";
649
+ }
650
+ }
651
+ sleep(ms) {
652
+ return new Promise((resolve) => setTimeout(resolve, ms));
653
+ }
654
+ };
655
+ function createComfyUIProvider(config) {
656
+ return new ComfyUIProvider(config);
657
+ }
658
+ // Annotate the CommonJS export names for ESM import in node:
659
+ 0 && (module.exports = {
660
+ ComfyUIProvider,
661
+ createComfyUIProvider
662
+ });