@fre4x/comfyui 1.0.60 → 1.0.61
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.js +663 -1363
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6801,40 +6801,13 @@ var require_dist = __commonJS({
|
|
|
6801
6801
|
|
|
6802
6802
|
// src/index.ts
|
|
6803
6803
|
import * as fs from "node:fs/promises";
|
|
6804
|
+
import * as crypto from "node:crypto";
|
|
6804
6805
|
import { realpathSync } from "node:fs";
|
|
6805
6806
|
import { homedir } from "node:os";
|
|
6806
6807
|
import path from "node:path";
|
|
6807
6808
|
import { fileURLToPath } from "node:url";
|
|
6808
6809
|
|
|
6809
6810
|
// ../packages/shared/dist/errors.js
|
|
6810
|
-
function createApiError(message, statusCode) {
|
|
6811
|
-
let hint = "Check your network connection and retry.";
|
|
6812
|
-
let type = "Service Error";
|
|
6813
|
-
if (statusCode === 429) {
|
|
6814
|
-
type = "Rate Limit";
|
|
6815
|
-
hint = "Request volume is too high. Suggestion: Wait briefly before retrying or reduce concurrent calls.";
|
|
6816
|
-
} else if (statusCode === 401 || statusCode === 403) {
|
|
6817
|
-
type = "Authentication Error";
|
|
6818
|
-
hint = "The request could not be authorized. Suggestion: Verify your API key or token in the environment configuration.";
|
|
6819
|
-
} else if (statusCode && statusCode >= 500) {
|
|
6820
|
-
type = "Upstream Error";
|
|
6821
|
-
hint = "The remote service is experiencing temporary issues. Suggestion: Try again in a few minutes.";
|
|
6822
|
-
} else if (statusCode === 404) {
|
|
6823
|
-
type = "Not Found";
|
|
6824
|
-
hint = "The requested information could not be found. Suggestion: Check if the ID, Ticker, or query parameters are correct.";
|
|
6825
|
-
}
|
|
6826
|
-
return {
|
|
6827
|
-
isError: true,
|
|
6828
|
-
content: [
|
|
6829
|
-
{
|
|
6830
|
-
type: "text",
|
|
6831
|
-
text: `${type}: ${message}
|
|
6832
|
-
|
|
6833
|
-
**Next Action**: ${hint}`
|
|
6834
|
-
}
|
|
6835
|
-
]
|
|
6836
|
-
};
|
|
6837
|
-
}
|
|
6838
6811
|
function createValidationError(field, message) {
|
|
6839
6812
|
return {
|
|
6840
6813
|
isError: true,
|
|
@@ -6876,6 +6849,20 @@ Suggestion: If the problem persists, check server logs.`
|
|
|
6876
6849
|
};
|
|
6877
6850
|
}
|
|
6878
6851
|
|
|
6852
|
+
// ../packages/shared/dist/format.js
|
|
6853
|
+
function formatListItems(items, renderer, emptyMessage = "_No results found._") {
|
|
6854
|
+
if (items.length === 0)
|
|
6855
|
+
return emptyMessage;
|
|
6856
|
+
return items.map((item, i) => renderer(item, i)).join("\n\n");
|
|
6857
|
+
}
|
|
6858
|
+
function formatPaginationFooter(offset, limit, total) {
|
|
6859
|
+
const start = offset + 1;
|
|
6860
|
+
const end = Math.min(offset + limit, total);
|
|
6861
|
+
return `
|
|
6862
|
+
---
|
|
6863
|
+
_Showing ${start}\u2013${end} of ${total}. Use \`offset: ${offset + limit}\` for the next page._`;
|
|
6864
|
+
}
|
|
6865
|
+
|
|
6879
6866
|
// ../node_modules/zod/index.js
|
|
6880
6867
|
var zod_exports = {};
|
|
6881
6868
|
__export(zod_exports, {
|
|
@@ -30582,801 +30569,229 @@ var MOCK_FIXTURES = {
|
|
|
30582
30569
|
}
|
|
30583
30570
|
};
|
|
30584
30571
|
|
|
30585
|
-
// src/
|
|
30586
|
-
var
|
|
30587
|
-
var
|
|
30588
|
-
|
|
30589
|
-
|
|
30590
|
-
|
|
30591
|
-
|
|
30592
|
-
var PaginationMetadataSchema = z3.object({
|
|
30593
|
-
total_count: z3.number(),
|
|
30594
|
-
limit: z3.number(),
|
|
30595
|
-
offset: z3.number(),
|
|
30596
|
-
has_more: z3.boolean()
|
|
30597
|
-
});
|
|
30598
|
-
var SystemInfoSchema = z3.object({
|
|
30599
|
-
system: z3.object({
|
|
30600
|
-
os: z3.string(),
|
|
30601
|
-
python_version: z3.string(),
|
|
30602
|
-
embedded_python: z3.boolean()
|
|
30603
|
-
}),
|
|
30604
|
-
devices: z3.array(
|
|
30605
|
-
z3.object({
|
|
30606
|
-
name: z3.string(),
|
|
30607
|
-
type: z3.string(),
|
|
30608
|
-
index: z3.number(),
|
|
30609
|
-
vram_total: z3.number(),
|
|
30610
|
-
vram_free: z3.number()
|
|
30611
|
-
})
|
|
30612
|
-
)
|
|
30613
|
-
});
|
|
30614
|
-
var ObjectInfoSchema = z3.object({
|
|
30615
|
-
nodes: z3.record(z3.string(), z3.unknown()),
|
|
30616
|
-
pagination: PaginationMetadataSchema.optional()
|
|
30617
|
-
});
|
|
30618
|
-
var HistoryImageSchema = z3.object({
|
|
30619
|
-
filename: z3.string(),
|
|
30620
|
-
subfolder: z3.string(),
|
|
30621
|
-
type: z3.string(),
|
|
30622
|
-
view_url: z3.string().url()
|
|
30623
|
-
});
|
|
30624
|
-
var WorkflowResponseSchema = z3.object({
|
|
30625
|
-
prompt_id: z3.string(),
|
|
30626
|
-
number: z3.number(),
|
|
30627
|
-
node_errors: z3.record(z3.string(), z3.unknown()),
|
|
30628
|
-
status: z3.string().optional(),
|
|
30629
|
-
completed: z3.boolean().optional(),
|
|
30630
|
-
timed_out: z3.boolean().optional(),
|
|
30631
|
-
elapsed_seconds: z3.number().optional(),
|
|
30632
|
-
image_count: z3.number().optional(),
|
|
30633
|
-
images: z3.array(HistoryImageSchema).optional(),
|
|
30634
|
-
history_entry: z3.record(z3.string(), z3.unknown()).nullable().optional()
|
|
30635
|
-
});
|
|
30636
|
-
var HistorySchema = z3.object({
|
|
30637
|
-
history: z3.record(z3.string(), z3.unknown()),
|
|
30638
|
-
pagination: PaginationMetadataSchema.optional()
|
|
30639
|
-
});
|
|
30640
|
-
var WaitForWorkflowSchema = z3.object({
|
|
30641
|
-
prompt_id: z3.string(),
|
|
30642
|
-
status: z3.string(),
|
|
30643
|
-
completed: z3.boolean(),
|
|
30644
|
-
timed_out: z3.boolean(),
|
|
30645
|
-
elapsed_seconds: z3.number(),
|
|
30646
|
-
image_count: z3.number(),
|
|
30647
|
-
images: z3.array(HistoryImageSchema),
|
|
30648
|
-
history_entry: z3.record(z3.string(), z3.unknown()).nullable()
|
|
30649
|
-
});
|
|
30650
|
-
var WorkflowTagSchema = z3.string().min(1).max(50);
|
|
30651
|
-
var EditableWorkflowValueSchema = z3.union([
|
|
30652
|
-
z3.string(),
|
|
30653
|
-
z3.number(),
|
|
30654
|
-
z3.boolean()
|
|
30572
|
+
// src/converter.ts
|
|
30573
|
+
var WIDGET_TYPES = /* @__PURE__ */ new Set(["INT", "FLOAT", "STRING", "BOOLEAN"]);
|
|
30574
|
+
var UI_CONTROL_VALUES = /* @__PURE__ */ new Set([
|
|
30575
|
+
"fixed",
|
|
30576
|
+
"increment",
|
|
30577
|
+
"decrement",
|
|
30578
|
+
"randomize"
|
|
30655
30579
|
]);
|
|
30656
|
-
|
|
30657
|
-
|
|
30658
|
-
node_class: z3.string(),
|
|
30659
|
-
input_name: z3.string(),
|
|
30660
|
-
value_type: z3.enum(["string", "number", "boolean", "enum"]),
|
|
30661
|
-
current_value: EditableWorkflowValueSchema,
|
|
30662
|
-
options: z3.array(z3.string()).default([])
|
|
30663
|
-
});
|
|
30664
|
-
var StoredWorkflowMetadataSchema = z3.object({
|
|
30665
|
-
id: z3.string(),
|
|
30666
|
-
name: z3.string(),
|
|
30667
|
-
description: z3.string().optional(),
|
|
30668
|
-
tags: z3.array(WorkflowTagSchema),
|
|
30669
|
-
created_at: z3.string(),
|
|
30670
|
-
updated_at: z3.string(),
|
|
30671
|
-
node_count: z3.number(),
|
|
30672
|
-
editable_input_count: z3.number(),
|
|
30673
|
-
source: z3.enum(["local", "mock"])
|
|
30674
|
-
});
|
|
30675
|
-
var StoredWorkflowSchema = z3.object({
|
|
30676
|
-
workflow: StoredWorkflowMetadataSchema,
|
|
30677
|
-
prompt: z3.record(z3.string(), z3.unknown()),
|
|
30678
|
-
editable_inputs: z3.array(WorkflowEditableInputSchema)
|
|
30679
|
-
});
|
|
30680
|
-
var WorkflowListSchema = z3.object({
|
|
30681
|
-
workflows: z3.array(StoredWorkflowMetadataSchema),
|
|
30682
|
-
pagination: PaginationMetadataSchema.optional()
|
|
30683
|
-
});
|
|
30684
|
-
var WorkflowOverrideSchema = z3.object({
|
|
30685
|
-
node_id: z3.string().min(1).describe("Node ID to override."),
|
|
30686
|
-
input_name: z3.string().min(1).describe("Input field name to override."),
|
|
30687
|
-
value: EditableWorkflowValueSchema.describe("Replacement scalar value.")
|
|
30688
|
-
});
|
|
30689
|
-
var RunStoredWorkflowSchema = WorkflowResponseSchema.extend({
|
|
30690
|
-
workflow_id: z3.string(),
|
|
30691
|
-
workflow_name: z3.string(),
|
|
30692
|
-
applied_overrides: z3.array(WorkflowOverrideSchema)
|
|
30693
|
-
});
|
|
30694
|
-
var QueueSchema = z3.object({
|
|
30695
|
-
queue_running: z3.array(z3.unknown()),
|
|
30696
|
-
queue_pending: z3.array(z3.unknown())
|
|
30697
|
-
});
|
|
30698
|
-
var ViewUrlSchema = z3.object({
|
|
30699
|
-
url: z3.string().url()
|
|
30700
|
-
});
|
|
30701
|
-
var NoArgsSchema = z3.object({}).strict();
|
|
30702
|
-
var ListObjectInfoInputSchema = z3.object({
|
|
30703
|
-
node_class: z3.string().optional().describe(
|
|
30704
|
-
"Specific node class to inspect (for example 'KSampler')."
|
|
30705
|
-
),
|
|
30706
|
-
limit: z3.number().int().min(1).max(100).default(50).describe("Pagination limit."),
|
|
30707
|
-
offset: z3.number().int().min(0).default(0).describe("Pagination offset.")
|
|
30708
|
-
}).strict();
|
|
30709
|
-
var ExecuteWorkflowInputSchema = z3.object({
|
|
30710
|
-
prompt: z3.record(z3.string(), z3.unknown()).describe("Workflow in ComfyUI API JSON format."),
|
|
30711
|
-
client_id: z3.string().optional().describe("Optional unique client identifier."),
|
|
30712
|
-
await: z3.boolean().default(false).describe("Wait for completion before returning."),
|
|
30713
|
-
timeout: z3.number().int().min(1).max(600).default(120).describe("Wait timeout in seconds.")
|
|
30714
|
-
}).strict();
|
|
30715
|
-
var GetHistoryInputSchema = z3.object({
|
|
30716
|
-
prompt_id: z3.string().optional().describe("Specific prompt ID to retrieve. Omit for full history."),
|
|
30717
|
-
limit: z3.number().int().min(1).max(100).default(20).describe("Pagination limit."),
|
|
30718
|
-
offset: z3.number().int().min(0).default(0).describe("Pagination offset.")
|
|
30719
|
-
}).strict();
|
|
30720
|
-
var GetViewUrlInputSchema = z3.object({
|
|
30721
|
-
filename: z3.string().min(1).describe("Name of the file to view."),
|
|
30722
|
-
subfolder: z3.string().optional().describe("Subfolder within the output directory."),
|
|
30723
|
-
type: z3.enum(["output", "input", "temp"]).default("output").describe("Storage type.")
|
|
30724
|
-
}).strict();
|
|
30725
|
-
var ListWorkflowsInputSchema = z3.object({
|
|
30726
|
-
limit: z3.number().int().min(1).max(100).default(50).describe("Pagination limit."),
|
|
30727
|
-
offset: z3.number().int().min(0).default(0).describe("Pagination offset.")
|
|
30728
|
-
}).strict();
|
|
30729
|
-
var SearchWorkflowsInputSchema = z3.object({
|
|
30730
|
-
query: z3.string().min(1).describe("Search text for name, tags, or description."),
|
|
30731
|
-
limit: z3.number().int().min(1).max(100).default(50).describe("Pagination limit."),
|
|
30732
|
-
offset: z3.number().int().min(0).default(0).describe("Pagination offset.")
|
|
30733
|
-
}).strict();
|
|
30734
|
-
var GetWorkflowInputSchema = z3.object({
|
|
30735
|
-
workflow_id: z3.string().min(1).describe("Stored workflow ID.")
|
|
30736
|
-
}).strict();
|
|
30737
|
-
var SaveWorkflowInputSchema = z3.object({
|
|
30738
|
-
workflow_id: z3.string().min(1).optional().describe("Optional stable workflow ID."),
|
|
30739
|
-
name: z3.string().min(1).describe("Human-readable workflow name."),
|
|
30740
|
-
description: z3.string().optional().describe("Optional workflow summary."),
|
|
30741
|
-
tags: z3.array(WorkflowTagSchema).default([]).describe("Optional workflow tags."),
|
|
30742
|
-
prompt: z3.record(z3.string(), z3.unknown()).describe("Workflow in ComfyUI API JSON format."),
|
|
30743
|
-
overwrite: z3.boolean().default(false).describe("Overwrite an existing workflow with the same ID.")
|
|
30744
|
-
}).strict();
|
|
30745
|
-
var RunWorkflowInputSchema = z3.object({
|
|
30746
|
-
workflow_id: z3.string().min(1).describe("Stored workflow ID to execute."),
|
|
30747
|
-
overrides: z3.array(WorkflowOverrideSchema).default([]).describe("Editable input overrides to apply before execution."),
|
|
30748
|
-
client_id: z3.string().optional().describe("Optional unique client identifier."),
|
|
30749
|
-
await: z3.boolean().default(false).describe("Wait for completion before returning."),
|
|
30750
|
-
timeout: z3.number().int().min(1).max(600).default(120).describe("Wait timeout in seconds."),
|
|
30751
|
-
require_images: z3.boolean().default(false).describe("When awaiting, require generated images before return.")
|
|
30752
|
-
}).strict();
|
|
30753
|
-
var WaitForWorkflowInputSchema = z3.object({
|
|
30754
|
-
prompt_id: z3.string().min(1).describe("Prompt ID to wait for."),
|
|
30755
|
-
timeout_seconds: z3.number().int().min(1).max(600).default(120).describe("Maximum wait time in seconds."),
|
|
30756
|
-
poll_interval_ms: z3.number().int().min(100).max(1e4).default(1e3).describe("Polling interval in milliseconds."),
|
|
30757
|
-
require_images: z3.boolean().default(false).describe("Wait until images are available too.")
|
|
30758
|
-
}).strict();
|
|
30759
|
-
var ComfyApiError = class extends Error {
|
|
30760
|
-
constructor(message, statusCode, details) {
|
|
30761
|
-
super(message);
|
|
30762
|
-
this.statusCode = statusCode;
|
|
30763
|
-
this.details = details;
|
|
30764
|
-
this.name = "ComfyApiError";
|
|
30765
|
-
}
|
|
30766
|
-
};
|
|
30767
|
-
function isRecord(value) {
|
|
30768
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30769
|
-
}
|
|
30770
|
-
function ensureRecord(value, label) {
|
|
30771
|
-
if (!isRecord(value)) {
|
|
30772
|
-
throw new Error(`Invalid ${label} response from ComfyUI.`);
|
|
30773
|
-
}
|
|
30774
|
-
return value;
|
|
30775
|
-
}
|
|
30776
|
-
function isEditableWorkflowValue(value) {
|
|
30777
|
-
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
30778
|
-
}
|
|
30779
|
-
function slugifyWorkflowId(value) {
|
|
30780
|
-
const slug = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
30781
|
-
return slug.length > 0 ? slug : "workflow";
|
|
30782
|
-
}
|
|
30783
|
-
function getWorkflowFilePath(workflowId) {
|
|
30784
|
-
return path.join(COMFYUI_WORKFLOWS_DIR, `${workflowId}.json`);
|
|
30580
|
+
function isWebUIFormat(json2) {
|
|
30581
|
+
return typeof json2 === "object" && json2 !== null && Array.isArray(json2.nodes) && Array.isArray(json2.links);
|
|
30785
30582
|
}
|
|
30786
|
-
|
|
30787
|
-
|
|
30583
|
+
function isLinkData(link) {
|
|
30584
|
+
return Array.isArray(link) && link.length >= 6 && typeof link[0] === "number" && typeof link[1] === "number" && typeof link[2] === "number" && typeof link[3] === "number" && typeof link[4] === "number" && typeof link[5] === "string";
|
|
30788
30585
|
}
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
getWorkflowFilePath(workflowId),
|
|
30793
|
-
"utf8"
|
|
30794
|
-
);
|
|
30795
|
-
return StoredWorkflowSchema.parse(JSON.parse(content));
|
|
30796
|
-
} catch (error48) {
|
|
30797
|
-
if (isRecord(error48) && error48.code === "ENOENT") {
|
|
30798
|
-
return void 0;
|
|
30799
|
-
}
|
|
30800
|
-
throw error48;
|
|
30586
|
+
function getOrderedInputEntries(group, orderedNames) {
|
|
30587
|
+
if (!group) {
|
|
30588
|
+
return [];
|
|
30801
30589
|
}
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
|
|
30806
|
-
|
|
30807
|
-
`${JSON.stringify(workflow, null, 2)}
|
|
30808
|
-
`,
|
|
30809
|
-
"utf8"
|
|
30810
|
-
);
|
|
30811
|
-
}
|
|
30812
|
-
async function listStoredWorkflowFiles() {
|
|
30813
|
-
await ensureWorkflowStoreDir();
|
|
30814
|
-
const entries = await fs.readdir(COMFYUI_WORKFLOWS_DIR, {
|
|
30815
|
-
withFileTypes: true
|
|
30816
|
-
});
|
|
30817
|
-
const workflows = [];
|
|
30818
|
-
for (const entry of entries) {
|
|
30819
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
30590
|
+
const seen = /* @__PURE__ */ new Set();
|
|
30591
|
+
const orderedEntries = [];
|
|
30592
|
+
for (const inputName of orderedNames ?? []) {
|
|
30593
|
+
const inputDefinition = group[inputName];
|
|
30594
|
+
if (!inputDefinition) {
|
|
30820
30595
|
continue;
|
|
30821
30596
|
}
|
|
30822
|
-
|
|
30823
|
-
|
|
30824
|
-
"utf8"
|
|
30825
|
-
);
|
|
30826
|
-
workflows.push(StoredWorkflowSchema.parse(JSON.parse(content)));
|
|
30827
|
-
}
|
|
30828
|
-
workflows.sort(
|
|
30829
|
-
(left, right) => right.workflow.updated_at.localeCompare(left.workflow.updated_at)
|
|
30830
|
-
);
|
|
30831
|
-
return workflows;
|
|
30832
|
-
}
|
|
30833
|
-
function unwrapNamedNode(nodeClass, value) {
|
|
30834
|
-
if (!isRecord(value)) {
|
|
30835
|
-
return value;
|
|
30836
|
-
}
|
|
30837
|
-
if (nodeClass in value) {
|
|
30838
|
-
return value[nodeClass];
|
|
30839
|
-
}
|
|
30840
|
-
return value;
|
|
30841
|
-
}
|
|
30842
|
-
function getErrorMessageFromDetails(details) {
|
|
30843
|
-
if (!isRecord(details)) {
|
|
30844
|
-
return void 0;
|
|
30845
|
-
}
|
|
30846
|
-
const errorValue = details.error;
|
|
30847
|
-
if (typeof errorValue === "string") {
|
|
30848
|
-
return errorValue;
|
|
30597
|
+
orderedEntries.push([inputName, inputDefinition]);
|
|
30598
|
+
seen.add(inputName);
|
|
30849
30599
|
}
|
|
30850
|
-
|
|
30851
|
-
|
|
30852
|
-
|
|
30853
|
-
|
|
30854
|
-
|
|
30855
|
-
function getNodeErrors(details) {
|
|
30856
|
-
if (!isRecord(details) || !isRecord(details.node_errors)) {
|
|
30857
|
-
return void 0;
|
|
30600
|
+
for (const [inputName, inputDefinition] of Object.entries(group)) {
|
|
30601
|
+
if (seen.has(inputName)) {
|
|
30602
|
+
continue;
|
|
30603
|
+
}
|
|
30604
|
+
orderedEntries.push([inputName, inputDefinition]);
|
|
30858
30605
|
}
|
|
30859
|
-
return
|
|
30860
|
-
}
|
|
30861
|
-
function normalizeSystemInfo(source) {
|
|
30862
|
-
const record2 = ensureRecord(source, "system info");
|
|
30863
|
-
const systemRecord = ensureRecord(record2.system, "system info.system");
|
|
30864
|
-
const devicesValue = Array.isArray(record2.devices) ? record2.devices : [];
|
|
30865
|
-
return {
|
|
30866
|
-
system: {
|
|
30867
|
-
os: typeof systemRecord.os === "string" ? systemRecord.os : "unknown",
|
|
30868
|
-
python_version: typeof systemRecord.python_version === "string" ? systemRecord.python_version : "unknown",
|
|
30869
|
-
embedded_python: systemRecord.embedded_python === true
|
|
30870
|
-
},
|
|
30871
|
-
devices: devicesValue.map((device) => {
|
|
30872
|
-
const deviceRecord = ensureRecord(device, "system info.devices[]");
|
|
30873
|
-
return {
|
|
30874
|
-
name: typeof deviceRecord.name === "string" ? deviceRecord.name : "unknown",
|
|
30875
|
-
type: typeof deviceRecord.type === "string" ? deviceRecord.type : "unknown",
|
|
30876
|
-
index: typeof deviceRecord.index === "number" ? deviceRecord.index : -1,
|
|
30877
|
-
vram_total: typeof deviceRecord.vram_total === "number" ? deviceRecord.vram_total : 0,
|
|
30878
|
-
vram_free: typeof deviceRecord.vram_free === "number" ? deviceRecord.vram_free : 0
|
|
30879
|
-
};
|
|
30880
|
-
})
|
|
30881
|
-
};
|
|
30882
|
-
}
|
|
30883
|
-
function formatGiB(bytes) {
|
|
30884
|
-
return `${(bytes / BYTES_PER_GIB).toFixed(1)} GiB`;
|
|
30606
|
+
return orderedEntries;
|
|
30885
30607
|
}
|
|
30886
|
-
function
|
|
30887
|
-
return
|
|
30888
|
-
${JSON.stringify(value, null, 2)}
|
|
30889
|
-
\`\`\``;
|
|
30608
|
+
function getWidgetTypeName(inputTypeData) {
|
|
30609
|
+
return Array.isArray(inputTypeData) ? "COMBO" : String(inputTypeData).toUpperCase();
|
|
30890
30610
|
}
|
|
30891
|
-
function
|
|
30892
|
-
|
|
30611
|
+
function isWidgetInput(inputTypeData) {
|
|
30612
|
+
const typeName = getWidgetTypeName(inputTypeData);
|
|
30613
|
+
return typeName === "COMBO" || WIDGET_TYPES.has(typeName);
|
|
30893
30614
|
}
|
|
30894
|
-
|
|
30895
|
-
|
|
30896
|
-
|
|
30897
|
-
function getEnumLikeOptions(value) {
|
|
30898
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
30899
|
-
return [];
|
|
30900
|
-
}
|
|
30901
|
-
const firstEntry = value[0];
|
|
30902
|
-
if (!Array.isArray(firstEntry)) {
|
|
30903
|
-
return [];
|
|
30904
|
-
}
|
|
30905
|
-
return firstEntry.filter(
|
|
30906
|
-
(entry) => typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean"
|
|
30907
|
-
);
|
|
30908
|
-
}
|
|
30909
|
-
function buildViewUrl(filename, type, subfolder) {
|
|
30910
|
-
const params = new URLSearchParams({
|
|
30911
|
-
filename,
|
|
30912
|
-
type
|
|
30913
|
-
});
|
|
30914
|
-
if (subfolder) {
|
|
30915
|
-
params.set("subfolder", subfolder);
|
|
30615
|
+
function isSkippableUiArtifact(value, expectedTypeName) {
|
|
30616
|
+
if (expectedTypeName !== "STRING" && typeof value === "string" && UI_CONTROL_VALUES.has(value)) {
|
|
30617
|
+
return true;
|
|
30916
30618
|
}
|
|
30917
|
-
return
|
|
30619
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30918
30620
|
}
|
|
30919
|
-
|
|
30920
|
-
|
|
30921
|
-
|
|
30922
|
-
|
|
30923
|
-
|
|
30924
|
-
const rawInfo = await fetchComfyJson(
|
|
30925
|
-
`/object_info/${encodeURIComponent(nodeClass)}`
|
|
30926
|
-
);
|
|
30927
|
-
return unwrapNamedNode(nodeClass, rawInfo);
|
|
30928
|
-
} catch (error48) {
|
|
30929
|
-
if (error48 instanceof ComfyApiError && error48.statusCode === 404) {
|
|
30930
|
-
return void 0;
|
|
30931
|
-
}
|
|
30932
|
-
throw error48;
|
|
30621
|
+
function normalizeWidgetValue(value, typeName, extraInfo) {
|
|
30622
|
+
const defaultValue = extraInfo?.default;
|
|
30623
|
+
const shouldUseDefaultForNumeric = (typeName === "INT" || typeName === "FLOAT") && (value === "" || value === void 0 || value === null);
|
|
30624
|
+
if (shouldUseDefaultForNumeric && defaultValue !== void 0) {
|
|
30625
|
+
return defaultValue;
|
|
30933
30626
|
}
|
|
30627
|
+
return value;
|
|
30934
30628
|
}
|
|
30935
|
-
|
|
30936
|
-
const
|
|
30937
|
-
const
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
|
|
30941
|
-
|
|
30942
|
-
|
|
30943
|
-
|
|
30944
|
-
|
|
30945
|
-
|
|
30946
|
-
|
|
30947
|
-
|
|
30948
|
-
|
|
30949
|
-
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
30953
|
-
|
|
30954
|
-
const
|
|
30955
|
-
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
|
|
30959
|
-
|
|
30629
|
+
function convertWebUIToAPI(webUIJson, objectInfo) {
|
|
30630
|
+
const prompt = {};
|
|
30631
|
+
const links = webUIJson.links || [];
|
|
30632
|
+
const nodes = webUIJson.nodes || [];
|
|
30633
|
+
const linkMap = /* @__PURE__ */ new Map();
|
|
30634
|
+
for (const link of links) {
|
|
30635
|
+
if (isLinkData(link)) {
|
|
30636
|
+
linkMap.set(link[0], link);
|
|
30637
|
+
}
|
|
30638
|
+
}
|
|
30639
|
+
for (const node of nodes) {
|
|
30640
|
+
if (!node.id || !node.type) continue;
|
|
30641
|
+
const objInfo = objectInfo[node.type];
|
|
30642
|
+
if (!objInfo) continue;
|
|
30643
|
+
const nodeIdStr = String(node.id);
|
|
30644
|
+
const promptNode = {
|
|
30645
|
+
class_type: node.type,
|
|
30646
|
+
inputs: {}
|
|
30647
|
+
};
|
|
30648
|
+
const connectedInputNames = /* @__PURE__ */ new Set();
|
|
30649
|
+
if (Array.isArray(node.inputs)) {
|
|
30650
|
+
for (const input of node.inputs) {
|
|
30651
|
+
if (input.name && input.link !== null && input.link !== void 0) {
|
|
30652
|
+
const linkData = linkMap.get(input.link);
|
|
30653
|
+
if (Array.isArray(linkData) && linkData.length >= 6) {
|
|
30654
|
+
const originNodeId = String(linkData[1]);
|
|
30655
|
+
const originSlot = linkData[2];
|
|
30656
|
+
promptNode.inputs[input.name] = [
|
|
30657
|
+
originNodeId,
|
|
30658
|
+
originSlot
|
|
30659
|
+
];
|
|
30660
|
+
connectedInputNames.add(input.name);
|
|
30661
|
+
}
|
|
30662
|
+
}
|
|
30960
30663
|
}
|
|
30961
|
-
const definition = requiredInputs?.[inputName] ?? optionalInputs?.[inputName];
|
|
30962
|
-
const options = getEnumLikeOptions(definition).map(String);
|
|
30963
|
-
const valueType = typeof inputValue === "boolean" ? "boolean" : typeof inputValue === "number" ? "number" : options.length > 0 ? "enum" : "string";
|
|
30964
|
-
editableInputs.push({
|
|
30965
|
-
node_id: nodeId,
|
|
30966
|
-
node_class: nodeClass,
|
|
30967
|
-
input_name: inputName,
|
|
30968
|
-
value_type: valueType,
|
|
30969
|
-
current_value: inputValue,
|
|
30970
|
-
options
|
|
30971
|
-
});
|
|
30972
30664
|
}
|
|
30973
|
-
|
|
30974
|
-
|
|
30975
|
-
|
|
30976
|
-
|
|
30977
|
-
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
|
|
30981
|
-
|
|
30982
|
-
|
|
30983
|
-
|
|
30984
|
-
|
|
30985
|
-
|
|
30986
|
-
|
|
30987
|
-
|
|
30988
|
-
|
|
30665
|
+
if (objInfo) {
|
|
30666
|
+
const widgets = Array.isArray(node.widgets_values) ? node.widgets_values : [];
|
|
30667
|
+
let widgetIndex = 0;
|
|
30668
|
+
const processInputGroup = (group, orderedNames) => {
|
|
30669
|
+
for (const [inputName, inputDataRaw] of getOrderedInputEntries(
|
|
30670
|
+
group,
|
|
30671
|
+
orderedNames
|
|
30672
|
+
)) {
|
|
30673
|
+
const [inputTypeData, inputExtraInfo] = inputDataRaw;
|
|
30674
|
+
if (!isWidgetInput(inputTypeData)) {
|
|
30675
|
+
continue;
|
|
30676
|
+
}
|
|
30677
|
+
const typeName = getWidgetTypeName(inputTypeData);
|
|
30678
|
+
while (widgetIndex < widgets.length && isSkippableUiArtifact(widgets[widgetIndex], typeName)) {
|
|
30679
|
+
widgetIndex++;
|
|
30680
|
+
}
|
|
30681
|
+
const isConnectedWidget = connectedInputNames.has(inputName);
|
|
30682
|
+
if (widgetIndex >= widgets.length) {
|
|
30683
|
+
if (!isConnectedWidget && inputExtraInfo?.default !== void 0) {
|
|
30684
|
+
promptNode.inputs[inputName] = inputExtraInfo.default;
|
|
30685
|
+
}
|
|
30686
|
+
continue;
|
|
30687
|
+
}
|
|
30688
|
+
const value = normalizeWidgetValue(
|
|
30689
|
+
widgets[widgetIndex],
|
|
30690
|
+
typeName,
|
|
30691
|
+
inputExtraInfo
|
|
30692
|
+
);
|
|
30693
|
+
widgetIndex++;
|
|
30694
|
+
if (!isConnectedWidget) {
|
|
30695
|
+
promptNode.inputs[inputName] = value;
|
|
30696
|
+
}
|
|
30697
|
+
}
|
|
30698
|
+
};
|
|
30699
|
+
processInputGroup(
|
|
30700
|
+
objInfo.input?.required,
|
|
30701
|
+
objInfo.input_order?.required
|
|
30989
30702
|
);
|
|
30990
|
-
|
|
30991
|
-
|
|
30992
|
-
|
|
30993
|
-
throw new Error(
|
|
30994
|
-
`Stored workflow node ${override.node_id} is invalid.`
|
|
30703
|
+
processInputGroup(
|
|
30704
|
+
objInfo.input?.optional,
|
|
30705
|
+
objInfo.input_order?.optional
|
|
30995
30706
|
);
|
|
30996
30707
|
}
|
|
30997
|
-
|
|
30708
|
+
prompt[nodeIdStr] = promptNode;
|
|
30998
30709
|
}
|
|
30999
|
-
return
|
|
30710
|
+
return prompt;
|
|
31000
30711
|
}
|
|
31001
|
-
|
|
31002
|
-
|
|
31003
|
-
|
|
30712
|
+
|
|
30713
|
+
// src/index.ts
|
|
30714
|
+
var zNamespace = zod_exports;
|
|
30715
|
+
var z3 = zNamespace.z ?? zNamespace.default ?? zNamespace;
|
|
30716
|
+
var COMFYUI_SERVER_URL = process.env.COMFYUI_SERVER_URL || "http://localhost:8188";
|
|
30717
|
+
var PACKAGE_VERSION = getPackageVersion(import.meta.url);
|
|
30718
|
+
var ComfyApiError = class extends Error {
|
|
30719
|
+
constructor(message, statusCode, details) {
|
|
30720
|
+
super(message);
|
|
30721
|
+
this.statusCode = statusCode;
|
|
30722
|
+
this.details = details;
|
|
30723
|
+
this.name = "ComfyApiError";
|
|
31004
30724
|
}
|
|
31005
|
-
|
|
31006
|
-
|
|
31007
|
-
|
|
31008
|
-
return [];
|
|
31009
|
-
}
|
|
31010
|
-
return [`${inputName}: ${options.map(String).join(", ")}`];
|
|
31011
|
-
});
|
|
31012
|
-
}
|
|
31013
|
-
function summarizeSystemInfoText(structuredContent) {
|
|
31014
|
-
const deviceSummary = formatMarkdownBulletList(
|
|
31015
|
-
structuredContent.devices.map(
|
|
31016
|
-
(device) => `[${device.index}] ${device.name} (${device.type}, ${formatGiB(device.vram_free)} free / ${formatGiB(device.vram_total)} total VRAM)`
|
|
31017
|
-
)
|
|
31018
|
-
);
|
|
31019
|
-
return [
|
|
31020
|
-
"# ComfyUI System Info",
|
|
31021
|
-
"",
|
|
31022
|
-
`ComfyUI server is running on ${structuredContent.system.os} with ${structuredContent.devices.length} device(s).`,
|
|
31023
|
-
"",
|
|
31024
|
-
"## Summary",
|
|
31025
|
-
formatMarkdownBulletList([
|
|
31026
|
-
`OS: ${structuredContent.system.os}`,
|
|
31027
|
-
`Python: ${structuredContent.system.python_version}`,
|
|
31028
|
-
`Embedded Python: ${structuredContent.system.embedded_python ? "yes" : "no"}`,
|
|
31029
|
-
`Devices: ${structuredContent.devices.length}`
|
|
31030
|
-
]),
|
|
31031
|
-
"",
|
|
31032
|
-
"## Devices",
|
|
31033
|
-
deviceSummary,
|
|
31034
|
-
"",
|
|
31035
|
-
"## JSON",
|
|
31036
|
-
formatJsonCodeBlock(structuredContent)
|
|
31037
|
-
].join("\n");
|
|
31038
|
-
}
|
|
31039
|
-
function summarizeNodeDefinitionText(nodeClass, nodeDefinition) {
|
|
31040
|
-
if (!isRecord(nodeDefinition)) {
|
|
31041
|
-
return `Schema retrieved for node: ${nodeClass}`;
|
|
31042
|
-
}
|
|
31043
|
-
const inputRecord = isRecord(nodeDefinition.input) ? nodeDefinition.input : void 0;
|
|
31044
|
-
const requiredInputs = isRecord(inputRecord?.required) ? inputRecord.required : void 0;
|
|
31045
|
-
const optionalInputs = isRecord(inputRecord?.optional) ? inputRecord.optional : void 0;
|
|
31046
|
-
const outputNames = Array.isArray(nodeDefinition.output) ? nodeDefinition.output.filter(
|
|
31047
|
-
(output) => typeof output === "string"
|
|
31048
|
-
) : [];
|
|
31049
|
-
const optionLines = [
|
|
31050
|
-
...summarizeOptionLines(requiredInputs),
|
|
31051
|
-
...summarizeOptionLines(optionalInputs)
|
|
31052
|
-
];
|
|
31053
|
-
return [
|
|
31054
|
-
"# ComfyUI Node Definition",
|
|
31055
|
-
"",
|
|
31056
|
-
`Schema retrieved for node: ${nodeClass}`,
|
|
31057
|
-
"",
|
|
31058
|
-
"## Summary",
|
|
31059
|
-
formatMarkdownBulletList([
|
|
31060
|
-
`Node Class: ${nodeClass}`,
|
|
31061
|
-
`Display Name: ${typeof nodeDefinition.display_name === "string" ? nodeDefinition.display_name : typeof nodeDefinition.name === "string" ? nodeDefinition.name : nodeClass}`,
|
|
31062
|
-
`Category: ${typeof nodeDefinition.category === "string" ? nodeDefinition.category : "unknown"}`,
|
|
31063
|
-
`Required Inputs: ${requiredInputs ? Object.keys(requiredInputs).join(", ") || "none" : "none"}`,
|
|
31064
|
-
`Optional Inputs: ${optionalInputs ? Object.keys(optionalInputs).join(", ") || "none" : "none"}`,
|
|
31065
|
-
`Outputs: ${outputNames.join(", ") || "none"}`
|
|
31066
|
-
]),
|
|
31067
|
-
"",
|
|
31068
|
-
optionLines.length > 0 ? `## Enumerated Options
|
|
31069
|
-
${optionLines.map((line) => `- ${line}`).join("\n")}
|
|
31070
|
-
` : "",
|
|
31071
|
-
"## JSON",
|
|
31072
|
-
formatJsonCodeBlock({
|
|
31073
|
-
nodes: { [nodeClass]: nodeDefinition }
|
|
31074
|
-
}),
|
|
31075
|
-
""
|
|
31076
|
-
].filter(Boolean).join("\n");
|
|
31077
|
-
}
|
|
31078
|
-
function summarizeHistoryEntryText(promptId, entry) {
|
|
31079
|
-
if (!isRecord(entry)) {
|
|
31080
|
-
return `History retrieved for job: ${promptId}`;
|
|
31081
|
-
}
|
|
31082
|
-
const statusRecord = isRecord(entry.status) ? entry.status : void 0;
|
|
31083
|
-
const outputsRecord = isRecord(entry.outputs) ? entry.outputs : void 0;
|
|
31084
|
-
const imageRefs = [];
|
|
31085
|
-
if (outputsRecord) {
|
|
31086
|
-
for (const output of Object.values(outputsRecord)) {
|
|
31087
|
-
if (!isRecord(output) || !Array.isArray(output.images)) {
|
|
31088
|
-
continue;
|
|
31089
|
-
}
|
|
31090
|
-
for (const image of output.images) {
|
|
31091
|
-
if (!isRecord(image) || typeof image.filename !== "string") {
|
|
31092
|
-
continue;
|
|
31093
|
-
}
|
|
31094
|
-
const subfolder = typeof image.subfolder === "string" && image.subfolder ? `${image.subfolder}/` : "";
|
|
31095
|
-
imageRefs.push(`${subfolder}${image.filename}`);
|
|
31096
|
-
}
|
|
31097
|
-
}
|
|
31098
|
-
}
|
|
31099
|
-
return [
|
|
31100
|
-
"# ComfyUI History Entry",
|
|
31101
|
-
"",
|
|
31102
|
-
`History retrieved for job: ${promptId}`,
|
|
31103
|
-
"",
|
|
31104
|
-
"## Summary",
|
|
31105
|
-
formatMarkdownBulletList([
|
|
31106
|
-
`Prompt ID: ${promptId}`,
|
|
31107
|
-
`Status: ${typeof statusRecord?.status_str === "string" ? statusRecord.status_str : "unknown"}`,
|
|
31108
|
-
`Completed: ${statusRecord?.completed === true ? "yes" : statusRecord?.completed === false ? "no" : "unknown"}`,
|
|
31109
|
-
`Images: ${imageRefs.length}`
|
|
31110
|
-
]),
|
|
31111
|
-
"",
|
|
31112
|
-
"## Images",
|
|
31113
|
-
formatMarkdownBulletList(imageRefs),
|
|
31114
|
-
"",
|
|
31115
|
-
"## JSON",
|
|
31116
|
-
formatJsonCodeBlock({
|
|
31117
|
-
history: { [promptId]: entry }
|
|
31118
|
-
})
|
|
31119
|
-
].join("\n");
|
|
30725
|
+
};
|
|
30726
|
+
function isRecord(value) {
|
|
30727
|
+
return typeof value === "object" && value !== null;
|
|
31120
30728
|
}
|
|
31121
|
-
function
|
|
31122
|
-
if (!isRecord(
|
|
30729
|
+
function summarizeNodeErrors(nodeErrors) {
|
|
30730
|
+
if (!isRecord(nodeErrors)) {
|
|
31123
30731
|
return [];
|
|
31124
30732
|
}
|
|
31125
|
-
const
|
|
31126
|
-
for (const
|
|
31127
|
-
if (!isRecord(
|
|
30733
|
+
const lines = [];
|
|
30734
|
+
for (const [nodeId, rawNodeError] of Object.entries(nodeErrors)) {
|
|
30735
|
+
if (!isRecord(rawNodeError)) {
|
|
31128
30736
|
continue;
|
|
31129
30737
|
}
|
|
31130
|
-
|
|
31131
|
-
|
|
30738
|
+
const classType = typeof rawNodeError.class_type === "string" ? ` (${rawNodeError.class_type})` : "";
|
|
30739
|
+
const rawErrors = Array.isArray(rawNodeError.errors) ? rawNodeError.errors : [];
|
|
30740
|
+
const missingInputs = [];
|
|
30741
|
+
const otherMessages = [];
|
|
30742
|
+
for (const rawError of rawErrors) {
|
|
30743
|
+
if (!isRecord(rawError)) {
|
|
31132
30744
|
continue;
|
|
31133
30745
|
}
|
|
31134
|
-
const
|
|
31135
|
-
|
|
31136
|
-
|
|
31137
|
-
|
|
31138
|
-
|
|
31139
|
-
|
|
31140
|
-
|
|
31141
|
-
|
|
31142
|
-
|
|
31143
|
-
|
|
31144
|
-
|
|
30746
|
+
const errorType = rawError.type;
|
|
30747
|
+
const errorMessage = rawError.message;
|
|
30748
|
+
const errorDetails = rawError.details;
|
|
30749
|
+
if (errorType === "required_input_missing" && typeof errorDetails === "string") {
|
|
30750
|
+
missingInputs.push(errorDetails);
|
|
30751
|
+
continue;
|
|
30752
|
+
}
|
|
30753
|
+
if (typeof errorMessage === "string") {
|
|
30754
|
+
otherMessages.push(
|
|
30755
|
+
typeof errorDetails === "string" && errorDetails.length > 0 ? `${errorMessage}: ${errorDetails}` : errorMessage
|
|
30756
|
+
);
|
|
30757
|
+
}
|
|
30758
|
+
}
|
|
30759
|
+
const parts = [];
|
|
30760
|
+
if (missingInputs.length > 0) {
|
|
30761
|
+
parts.push(`missing inputs: ${missingInputs.join(", ")}`);
|
|
30762
|
+
}
|
|
30763
|
+
if (otherMessages.length > 0) {
|
|
30764
|
+
parts.push(otherMessages.join("; "));
|
|
30765
|
+
}
|
|
30766
|
+
if (parts.length > 0) {
|
|
30767
|
+
lines.push(`Node ${nodeId}${classType}: ${parts.join("; ")}`);
|
|
31145
30768
|
}
|
|
31146
30769
|
}
|
|
31147
|
-
return
|
|
30770
|
+
return lines;
|
|
31148
30771
|
}
|
|
31149
|
-
function
|
|
31150
|
-
if (!isRecord(
|
|
31151
|
-
return "
|
|
30772
|
+
function summarizeComfyErrorDetails(details) {
|
|
30773
|
+
if (!isRecord(details)) {
|
|
30774
|
+
return "";
|
|
31152
30775
|
}
|
|
31153
|
-
|
|
31154
|
-
|
|
31155
|
-
|
|
31156
|
-
|
|
31157
|
-
|
|
30776
|
+
const lines = [];
|
|
30777
|
+
const errorDetails = isRecord(details.error) ? details.error : void 0;
|
|
30778
|
+
if (typeof errorDetails?.message === "string") {
|
|
30779
|
+
lines.push(errorDetails.message);
|
|
30780
|
+
} else if (typeof details.message === "string") {
|
|
30781
|
+
lines.push(details.message);
|
|
31158
30782
|
}
|
|
31159
|
-
|
|
31160
|
-
|
|
30783
|
+
const nodeLines = summarizeNodeErrors(details.node_errors);
|
|
30784
|
+
if (nodeLines.length > 0) {
|
|
30785
|
+
lines.push(...nodeLines);
|
|
31161
30786
|
}
|
|
31162
|
-
return
|
|
31163
|
-
}
|
|
31164
|
-
function summarizeWaitForWorkflowText(structuredContent) {
|
|
31165
|
-
return [
|
|
31166
|
-
"# ComfyUI Workflow Wait Result",
|
|
31167
|
-
"",
|
|
31168
|
-
"## Summary",
|
|
31169
|
-
formatMarkdownBulletList([
|
|
31170
|
-
`Prompt ID: ${structuredContent.prompt_id}`,
|
|
31171
|
-
`Status: ${structuredContent.status}`,
|
|
31172
|
-
`Completed: ${structuredContent.completed ? "yes" : "no"}`,
|
|
31173
|
-
`Timed Out: ${structuredContent.timed_out ? "yes" : "no"}`,
|
|
31174
|
-
`Elapsed Seconds: ${structuredContent.elapsed_seconds}`,
|
|
31175
|
-
`Images: ${structuredContent.image_count}`
|
|
31176
|
-
]),
|
|
31177
|
-
"",
|
|
31178
|
-
"## Images",
|
|
31179
|
-
formatMarkdownBulletList(
|
|
31180
|
-
structuredContent.images.map(
|
|
31181
|
-
(image) => `${image.subfolder ? `${image.subfolder}/` : ""}${image.filename} (${image.type}) -> ${image.view_url}`
|
|
31182
|
-
)
|
|
31183
|
-
),
|
|
31184
|
-
"",
|
|
31185
|
-
"## JSON",
|
|
31186
|
-
formatJsonCodeBlock(structuredContent)
|
|
31187
|
-
].join("\n");
|
|
31188
|
-
}
|
|
31189
|
-
function summarizeWorkflowListText(heading, workflows, pagination) {
|
|
31190
|
-
return [
|
|
31191
|
-
heading,
|
|
31192
|
-
"",
|
|
31193
|
-
"## Pagination",
|
|
31194
|
-
formatMarkdownBulletList([
|
|
31195
|
-
`Returned: ${workflows.length}`,
|
|
31196
|
-
`Total: ${pagination.total_count}`,
|
|
31197
|
-
`Limit: ${pagination.limit}`,
|
|
31198
|
-
`Offset: ${pagination.offset}`,
|
|
31199
|
-
`Has More: ${pagination.has_more ? "yes" : "no"}`
|
|
31200
|
-
]),
|
|
31201
|
-
"",
|
|
31202
|
-
"## Workflows",
|
|
31203
|
-
formatMarkdownBulletList(
|
|
31204
|
-
workflows.map(
|
|
31205
|
-
(workflow) => `${workflow.id} \u2014 ${workflow.name} | tags: ${workflow.tags.join(", ") || "none"} | editable inputs: ${workflow.editable_input_count}`
|
|
31206
|
-
)
|
|
31207
|
-
),
|
|
31208
|
-
"",
|
|
31209
|
-
"## JSON",
|
|
31210
|
-
formatJsonCodeBlock({
|
|
31211
|
-
workflows,
|
|
31212
|
-
pagination
|
|
31213
|
-
})
|
|
31214
|
-
].join("\n");
|
|
31215
|
-
}
|
|
31216
|
-
function summarizeStoredWorkflowText(workflow) {
|
|
31217
|
-
return [
|
|
31218
|
-
"# ComfyUI Stored Workflow",
|
|
31219
|
-
"",
|
|
31220
|
-
"## Summary",
|
|
31221
|
-
formatMarkdownBulletList([
|
|
31222
|
-
`Workflow ID: ${workflow.workflow.id}`,
|
|
31223
|
-
`Name: ${workflow.workflow.name}`,
|
|
31224
|
-
`Description: ${workflow.workflow.description ?? "none"}`,
|
|
31225
|
-
`Tags: ${workflow.workflow.tags.join(", ") || "none"}`,
|
|
31226
|
-
`Created At: ${workflow.workflow.created_at}`,
|
|
31227
|
-
`Updated At: ${workflow.workflow.updated_at}`,
|
|
31228
|
-
`Node Count: ${workflow.workflow.node_count}`,
|
|
31229
|
-
`Editable Inputs: ${workflow.workflow.editable_input_count}`,
|
|
31230
|
-
`Source: ${workflow.workflow.source}`
|
|
31231
|
-
]),
|
|
31232
|
-
"",
|
|
31233
|
-
"## Editable Inputs",
|
|
31234
|
-
formatMarkdownBulletList(
|
|
31235
|
-
workflow.editable_inputs.map(
|
|
31236
|
-
(input) => `${input.node_id}.${input.input_name} (${input.node_class}, ${input.value_type}) = ${String(
|
|
31237
|
-
input.current_value
|
|
31238
|
-
)}${input.options.length > 0 ? ` | options: ${input.options.join(", ")}` : ""}`
|
|
31239
|
-
)
|
|
31240
|
-
),
|
|
31241
|
-
"",
|
|
31242
|
-
"## JSON",
|
|
31243
|
-
formatJsonCodeBlock(workflow)
|
|
31244
|
-
].join("\n");
|
|
31245
|
-
}
|
|
31246
|
-
function summarizeSavedWorkflowText(workflow) {
|
|
31247
|
-
return [
|
|
31248
|
-
"# ComfyUI Workflow Saved",
|
|
31249
|
-
"",
|
|
31250
|
-
"## Summary",
|
|
31251
|
-
formatMarkdownBulletList([
|
|
31252
|
-
`Workflow ID: ${workflow.workflow.id}`,
|
|
31253
|
-
`Name: ${workflow.workflow.name}`,
|
|
31254
|
-
`Tags: ${workflow.workflow.tags.join(", ") || "none"}`,
|
|
31255
|
-
`Editable Inputs: ${workflow.workflow.editable_input_count}`
|
|
31256
|
-
]),
|
|
31257
|
-
"",
|
|
31258
|
-
"## Next Step",
|
|
31259
|
-
`Call comfyui_run_workflow with workflow_id="${workflow.workflow.id}" and optional overrides to reuse it.`,
|
|
31260
|
-
"",
|
|
31261
|
-
"## JSON",
|
|
31262
|
-
formatJsonCodeBlock(workflow)
|
|
31263
|
-
].join("\n");
|
|
31264
|
-
}
|
|
31265
|
-
async function waitForWorkflowResult(params) {
|
|
31266
|
-
const startTime = Date.now();
|
|
31267
|
-
const timeoutMs = params.timeout_seconds * 1e3;
|
|
31268
|
-
let latestEntry;
|
|
31269
|
-
while (Date.now() - startTime <= timeoutMs) {
|
|
31270
|
-
latestEntry = await fetchHistoryEntry(params.prompt_id);
|
|
31271
|
-
if (latestEntry !== void 0) {
|
|
31272
|
-
const images = extractHistoryImages(latestEntry);
|
|
31273
|
-
const completed = isHistoryCompleted(latestEntry);
|
|
31274
|
-
if (completed && (!params.require_images || images.length > 0)) {
|
|
31275
|
-
return {
|
|
31276
|
-
prompt_id: params.prompt_id,
|
|
31277
|
-
status: getHistoryStatus(latestEntry),
|
|
31278
|
-
completed: true,
|
|
31279
|
-
timed_out: false,
|
|
31280
|
-
elapsed_seconds: Number(
|
|
31281
|
-
((Date.now() - startTime) / 1e3).toFixed(3)
|
|
31282
|
-
),
|
|
31283
|
-
image_count: images.length,
|
|
31284
|
-
images,
|
|
31285
|
-
history_entry: ensureRecord(latestEntry, "history entry")
|
|
31286
|
-
};
|
|
31287
|
-
}
|
|
31288
|
-
}
|
|
31289
|
-
await sleep(params.poll_interval_ms);
|
|
31290
|
-
}
|
|
31291
|
-
const timeoutImages = latestEntry !== void 0 ? extractHistoryImages(latestEntry) : [];
|
|
31292
|
-
return {
|
|
31293
|
-
prompt_id: params.prompt_id,
|
|
31294
|
-
status: latestEntry !== void 0 ? getHistoryStatus(latestEntry) : "not_found",
|
|
31295
|
-
completed: latestEntry !== void 0 ? isHistoryCompleted(latestEntry) : false,
|
|
31296
|
-
timed_out: true,
|
|
31297
|
-
elapsed_seconds: Number(((Date.now() - startTime) / 1e3).toFixed(3)),
|
|
31298
|
-
image_count: timeoutImages.length,
|
|
31299
|
-
images: timeoutImages,
|
|
31300
|
-
history_entry: latestEntry !== void 0 ? ensureRecord(latestEntry, "history entry") : null
|
|
31301
|
-
};
|
|
31302
|
-
}
|
|
31303
|
-
async function createStoredWorkflowRecord(params) {
|
|
31304
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31305
|
-
const editableInputs = await deriveEditableWorkflowInputs(params.prompt);
|
|
31306
|
-
const workflowId = params.workflow_id ? slugifyWorkflowId(params.workflow_id) : slugifyWorkflowId(params.name);
|
|
31307
|
-
return {
|
|
31308
|
-
workflow: {
|
|
31309
|
-
id: workflowId,
|
|
31310
|
-
name: params.name,
|
|
31311
|
-
description: params.description,
|
|
31312
|
-
tags: params.tags,
|
|
31313
|
-
created_at: params.existing?.workflow.created_at ?? now,
|
|
31314
|
-
updated_at: now,
|
|
31315
|
-
node_count: Object.keys(params.prompt).length,
|
|
31316
|
-
editable_input_count: editableInputs.length,
|
|
31317
|
-
source: params.source
|
|
31318
|
-
},
|
|
31319
|
-
prompt: params.prompt,
|
|
31320
|
-
editable_inputs: editableInputs
|
|
31321
|
-
};
|
|
31322
|
-
}
|
|
31323
|
-
async function submitWorkflow(args) {
|
|
31324
|
-
const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson(
|
|
31325
|
-
"/prompt",
|
|
31326
|
-
{
|
|
31327
|
-
method: "POST",
|
|
31328
|
-
body: JSON.stringify({
|
|
31329
|
-
prompt: args.prompt,
|
|
31330
|
-
client_id: args.client_id
|
|
31331
|
-
})
|
|
31332
|
-
}
|
|
31333
|
-
);
|
|
31334
|
-
return {
|
|
31335
|
-
prompt_id: result.prompt_id,
|
|
31336
|
-
number: result.number,
|
|
31337
|
-
node_errors: result.node_errors
|
|
31338
|
-
};
|
|
31339
|
-
}
|
|
31340
|
-
function summarizeWorkflowResponseText(result) {
|
|
31341
|
-
const hasErrors = Object.keys(result.node_errors).length > 0;
|
|
31342
|
-
return [
|
|
31343
|
-
"# ComfyUI Workflow Submission",
|
|
31344
|
-
"",
|
|
31345
|
-
"## Summary",
|
|
31346
|
-
formatMarkdownBulletList([
|
|
31347
|
-
`Status: ${hasErrors ? "Submitted with node errors" : "Successfully queued"}`,
|
|
31348
|
-
`Prompt ID: ${result.prompt_id}`,
|
|
31349
|
-
`Queue Number: ${result.number}`,
|
|
31350
|
-
`Node Errors: ${Object.keys(result.node_errors).length}`
|
|
31351
|
-
]),
|
|
31352
|
-
"",
|
|
31353
|
-
"## Next Step",
|
|
31354
|
-
hasErrors ? "Fix the reported node_errors before retrying this workflow." : `Next step: call comfyui_get_history with prompt_id="${result.prompt_id}" to monitor outputs.`,
|
|
31355
|
-
"",
|
|
31356
|
-
"## JSON",
|
|
31357
|
-
formatJsonCodeBlock({
|
|
31358
|
-
prompt_id: result.prompt_id,
|
|
31359
|
-
number: result.number,
|
|
31360
|
-
node_errors: result.node_errors
|
|
31361
|
-
})
|
|
31362
|
-
].join("\n");
|
|
30787
|
+
return lines.join("\n");
|
|
31363
30788
|
}
|
|
31364
|
-
function
|
|
31365
|
-
const
|
|
31366
|
-
if (
|
|
31367
|
-
|
|
31368
|
-
"Tool response text must be non-empty. Do not rely on structuredContent alone because many MCP clients expose content.text to the model first."
|
|
31369
|
-
);
|
|
30789
|
+
function formatWorkflowNodeErrors(nodeErrors) {
|
|
30790
|
+
const nodeLines = summarizeNodeErrors(nodeErrors);
|
|
30791
|
+
if (nodeLines.length === 0) {
|
|
30792
|
+
return "Workflow submission failed validation.";
|
|
31370
30793
|
}
|
|
31371
|
-
return
|
|
31372
|
-
content: [
|
|
31373
|
-
{
|
|
31374
|
-
type: "text",
|
|
31375
|
-
text: normalizedText
|
|
31376
|
-
}
|
|
31377
|
-
],
|
|
31378
|
-
structuredContent
|
|
31379
|
-
};
|
|
30794
|
+
return ["Workflow submission failed validation.", ...nodeLines].join("\n");
|
|
31380
30795
|
}
|
|
31381
30796
|
async function fetchComfyJson(apiPath, options) {
|
|
31382
30797
|
const url2 = new URL(apiPath, COMFYUI_SERVER_URL).toString();
|
|
@@ -31385,666 +30800,551 @@ async function fetchComfyJson(apiPath, options) {
|
|
|
31385
30800
|
response = await fetch(url2, {
|
|
31386
30801
|
...options,
|
|
31387
30802
|
headers: {
|
|
31388
|
-
|
|
31389
|
-
...options?.headers
|
|
30803
|
+
Accept: "application/json",
|
|
30804
|
+
...options?.headers || {}
|
|
31390
30805
|
}
|
|
31391
30806
|
});
|
|
31392
|
-
} catch {
|
|
31393
|
-
throw new
|
|
31394
|
-
|
|
31395
|
-
503
|
|
30807
|
+
} catch (error48) {
|
|
30808
|
+
throw new Error(
|
|
30809
|
+
`Failed to connect to ComfyUI server at ${COMFYUI_SERVER_URL}: ${error48 instanceof Error ? error48.message : String(error48)}`
|
|
31396
30810
|
);
|
|
31397
30811
|
}
|
|
31398
|
-
|
|
30812
|
+
if (response.status === 404) {
|
|
30813
|
+
throw new ComfyApiError(`Not found: ${apiPath}`, 404, null);
|
|
30814
|
+
}
|
|
30815
|
+
const text = await response.text();
|
|
31399
30816
|
let body;
|
|
31400
|
-
|
|
31401
|
-
body =
|
|
31402
|
-
}
|
|
31403
|
-
|
|
30817
|
+
try {
|
|
30818
|
+
body = text ? JSON.parse(text) : null;
|
|
30819
|
+
} catch {
|
|
30820
|
+
throw new ComfyApiError(
|
|
30821
|
+
`Invalid JSON response from ComfyUI: ${text.slice(0, 100)}`,
|
|
30822
|
+
response.status,
|
|
30823
|
+
text
|
|
30824
|
+
);
|
|
31404
30825
|
}
|
|
31405
30826
|
if (!response.ok) {
|
|
31406
30827
|
throw new ComfyApiError(
|
|
31407
|
-
|
|
30828
|
+
"The ComfyUI service returned an error.",
|
|
31408
30829
|
response.status,
|
|
31409
30830
|
body
|
|
31410
30831
|
);
|
|
31411
30832
|
}
|
|
31412
30833
|
return body;
|
|
31413
30834
|
}
|
|
31414
|
-
async function
|
|
31415
|
-
|
|
31416
|
-
|
|
31417
|
-
|
|
31418
|
-
|
|
31419
|
-
|
|
31420
|
-
|
|
31421
|
-
|
|
31422
|
-
|
|
31423
|
-
|
|
31424
|
-
|
|
31425
|
-
|
|
31426
|
-
|
|
31427
|
-
|
|
30835
|
+
async function fetchImageBase64(filename, type, subfolder) {
|
|
30836
|
+
const params = new URLSearchParams({ filename, type });
|
|
30837
|
+
if (subfolder) params.set("subfolder", subfolder);
|
|
30838
|
+
const url2 = new URL(
|
|
30839
|
+
`/view?${params.toString()}`,
|
|
30840
|
+
COMFYUI_SERVER_URL
|
|
30841
|
+
).toString();
|
|
30842
|
+
const response = await fetch(url2);
|
|
30843
|
+
if (!response.ok)
|
|
30844
|
+
throw new Error(`Failed to fetch image: ${response.statusText}`);
|
|
30845
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
30846
|
+
const mimeType = response.headers.get("content-type") || "image/png";
|
|
30847
|
+
return {
|
|
30848
|
+
data: Buffer.from(arrayBuffer).toString("base64"),
|
|
30849
|
+
mimeType
|
|
30850
|
+
};
|
|
31428
30851
|
}
|
|
31429
30852
|
function handleComfyError(error48) {
|
|
31430
30853
|
if (error48 instanceof ZodError) {
|
|
31431
30854
|
return createValidationError(
|
|
31432
|
-
|
|
30855
|
+
"arguments",
|
|
31433
30856
|
error48.issues[0]?.message || "Invalid input."
|
|
31434
30857
|
);
|
|
31435
30858
|
}
|
|
31436
30859
|
if (error48 instanceof ComfyApiError) {
|
|
31437
|
-
const
|
|
31438
|
-
|
|
31439
|
-
|
|
31440
|
-
|
|
31441
|
-
|
|
31442
|
-
|
|
31443
|
-
|
|
31444
|
-
|
|
31445
|
-
|
|
31446
|
-
|
|
31447
|
-
|
|
30860
|
+
const detailSummary = summarizeComfyErrorDetails(error48.details);
|
|
30861
|
+
return {
|
|
30862
|
+
content: [
|
|
30863
|
+
{
|
|
30864
|
+
type: "text",
|
|
30865
|
+
text: detailSummary.length > 0 ? `${error48.message}
|
|
30866
|
+
${detailSummary}` : error48.message
|
|
30867
|
+
}
|
|
30868
|
+
],
|
|
30869
|
+
structuredContent: error48.details || {},
|
|
30870
|
+
isError: true
|
|
30871
|
+
};
|
|
31448
30872
|
}
|
|
31449
30873
|
return createInternalError(error48);
|
|
31450
30874
|
}
|
|
31451
|
-
async function
|
|
31452
|
-
|
|
31453
|
-
|
|
31454
|
-
|
|
31455
|
-
|
|
31456
|
-
|
|
31457
|
-
|
|
30875
|
+
async function validateWorkflowPath(targetPath) {
|
|
30876
|
+
const resolvedPath = path.resolve(targetPath);
|
|
30877
|
+
const allowedRoots = [
|
|
30878
|
+
path.join(
|
|
30879
|
+
homedir(),
|
|
30880
|
+
"comfy",
|
|
30881
|
+
"ComfyUI",
|
|
30882
|
+
"user",
|
|
30883
|
+
"default",
|
|
30884
|
+
"workflows"
|
|
30885
|
+
),
|
|
30886
|
+
path.join(homedir(), ".fre4x-comfyui", "workflows"),
|
|
30887
|
+
path.join(process.cwd(), "workflows")
|
|
30888
|
+
];
|
|
30889
|
+
const isAllowed = allowedRoots.some(
|
|
30890
|
+
(root) => resolvedPath.startsWith(path.resolve(root))
|
|
30891
|
+
);
|
|
30892
|
+
if (!isAllowed) {
|
|
30893
|
+
throw new Error(
|
|
30894
|
+
`Access denied: ${targetPath} is outside allowed workflow directories.`
|
|
31458
30895
|
);
|
|
31459
|
-
}
|
|
31460
|
-
|
|
30896
|
+
}
|
|
30897
|
+
try {
|
|
30898
|
+
await fs.access(resolvedPath);
|
|
30899
|
+
return resolvedPath;
|
|
30900
|
+
} catch {
|
|
30901
|
+
throw new Error(`Workflow file not found: ${targetPath}`);
|
|
30902
|
+
}
|
|
30903
|
+
}
|
|
30904
|
+
function applyOverrides(prompt, overrides) {
|
|
30905
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
30906
|
+
const parts = key.split(".");
|
|
30907
|
+
let current = prompt;
|
|
30908
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
30909
|
+
const part = parts[i];
|
|
30910
|
+
if (typeof current[part] !== "object" || current[part] === null) {
|
|
30911
|
+
current[part] = {};
|
|
30912
|
+
}
|
|
30913
|
+
current = current[part];
|
|
30914
|
+
}
|
|
30915
|
+
current[parts[parts.length - 1]] = value;
|
|
31461
30916
|
}
|
|
31462
30917
|
}
|
|
31463
|
-
|
|
30918
|
+
var InspectNodeSchema = paginationSchema.extend({
|
|
30919
|
+
node_class: z3.string().optional().describe(
|
|
30920
|
+
"Specific node class to inspect. If omitted, lists all available nodes."
|
|
30921
|
+
),
|
|
30922
|
+
query: z3.string().optional().describe(
|
|
30923
|
+
"Search term to filter the list of available nodes by name or category."
|
|
30924
|
+
)
|
|
30925
|
+
});
|
|
30926
|
+
var DiscoverWorkflowsSchema = paginationSchema.extend({
|
|
30927
|
+
directory_path: z3.string().optional().describe("Custom directory path to scan for workflows.")
|
|
30928
|
+
});
|
|
30929
|
+
var WorkflowRunSchema = z3.object({
|
|
30930
|
+
workflow_file_path: z3.string().optional().describe(
|
|
30931
|
+
"Path or filename of workflow JSON. Scans default dirs if simple name provided."
|
|
30932
|
+
),
|
|
30933
|
+
workflow_id: z3.string().optional().describe("Workflow identifier returned by discover_workflows."),
|
|
30934
|
+
overrides: z3.record(z3.string(), z3.unknown()).optional().describe(
|
|
30935
|
+
'Key-value pairs to override using dot-notation. e.g. {"6.inputs.text": "dragon"}'
|
|
30936
|
+
),
|
|
30937
|
+
await: z3.boolean().default(true).describe("Wait for completion and return native images."),
|
|
30938
|
+
timeout: z3.number().int().min(1).default(120).describe("Wait timeout in seconds.")
|
|
30939
|
+
});
|
|
30940
|
+
var WaitForWorkflowSchema = z3.object({
|
|
30941
|
+
prompt_id: z3.string().describe("The prompt_id of the submitted workflow."),
|
|
30942
|
+
timeout: z3.number().int().min(1).default(120).describe("Wait timeout in seconds.")
|
|
30943
|
+
});
|
|
30944
|
+
async function handleInspectNode(args) {
|
|
31464
30945
|
try {
|
|
31465
|
-
const rawInfo = IS_MOCK ?
|
|
31466
|
-
|
|
30946
|
+
const rawInfo = IS_MOCK ? MOCK_FIXTURES.object_info : await fetchComfyJson(
|
|
30947
|
+
"/object_info"
|
|
31467
30948
|
);
|
|
31468
30949
|
if (args.node_class) {
|
|
31469
|
-
|
|
30950
|
+
const node = rawInfo[args.node_class];
|
|
30951
|
+
if (!node)
|
|
31470
30952
|
return createNotFoundError(`Node class ${args.node_class}`);
|
|
31471
|
-
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
|
|
31476
|
-
|
|
31477
|
-
}
|
|
31478
|
-
);
|
|
30953
|
+
return {
|
|
30954
|
+
content: [
|
|
30955
|
+
{ type: "text", text: JSON.stringify(node, null, 2) }
|
|
30956
|
+
],
|
|
30957
|
+
structuredContent: node
|
|
30958
|
+
};
|
|
31479
30959
|
}
|
|
31480
|
-
|
|
31481
|
-
|
|
31482
|
-
|
|
31483
|
-
|
|
31484
|
-
|
|
31485
|
-
|
|
31486
|
-
|
|
31487
|
-
|
|
31488
|
-
|
|
31489
|
-
|
|
30960
|
+
let classes = Object.keys(rawInfo);
|
|
30961
|
+
if (args.query) {
|
|
30962
|
+
const query = args.query.toLowerCase();
|
|
30963
|
+
classes = classes.filter((c) => {
|
|
30964
|
+
const node = rawInfo[c];
|
|
30965
|
+
const searchStr = `${c} ${node?.display_name || ""} ${node?.category || ""}`.toLowerCase();
|
|
30966
|
+
return searchStr.includes(query);
|
|
30967
|
+
});
|
|
30968
|
+
}
|
|
30969
|
+
const paginated = applyPagination(classes, args);
|
|
30970
|
+
const resultNodes = {};
|
|
30971
|
+
for (const c of paginated.items) {
|
|
30972
|
+
resultNodes[c] = rawInfo[c];
|
|
30973
|
+
}
|
|
30974
|
+
const text = `Available nodes (${paginated.total} total):
|
|
30975
|
+
` + paginated.items.join(", ") + (paginated.hasMore ? `
|
|
30976
|
+
|
|
30977
|
+
${formatPaginationFooter(
|
|
30978
|
+
paginated.offset,
|
|
30979
|
+
paginated.limit,
|
|
30980
|
+
paginated.total
|
|
30981
|
+
)}` : "");
|
|
30982
|
+
return {
|
|
30983
|
+
content: [{ type: "text", text }],
|
|
30984
|
+
structuredContent: {
|
|
30985
|
+
nodes: resultNodes,
|
|
30986
|
+
pagination: {
|
|
30987
|
+
total: paginated.total,
|
|
30988
|
+
offset: paginated.offset,
|
|
30989
|
+
limit: paginated.limit,
|
|
30990
|
+
has_more: paginated.hasMore
|
|
30991
|
+
}
|
|
31490
30992
|
}
|
|
31491
30993
|
};
|
|
31492
|
-
return createAgentFacingResult(
|
|
31493
|
-
[
|
|
31494
|
-
"# ComfyUI Node Definitions",
|
|
31495
|
-
"",
|
|
31496
|
-
"## Pagination",
|
|
31497
|
-
formatMarkdownBulletList([
|
|
31498
|
-
`Returned: ${paginated.items.length}`,
|
|
31499
|
-
`Total: ${paginated.total}`,
|
|
31500
|
-
`Limit: ${paginated.limit}`,
|
|
31501
|
-
`Offset: ${paginated.offset}`,
|
|
31502
|
-
`Has More: ${paginated.hasMore ? "yes" : "no"}`
|
|
31503
|
-
]),
|
|
31504
|
-
"",
|
|
31505
|
-
"## Node Classes In This Page",
|
|
31506
|
-
formatMarkdownBulletList(
|
|
31507
|
-
paginated.items.map(([nodeClass]) => nodeClass)
|
|
31508
|
-
),
|
|
31509
|
-
"",
|
|
31510
|
-
"## Next Step",
|
|
31511
|
-
paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} to retrieve the next page.` : "This page contains the final set of node classes for the current query.",
|
|
31512
|
-
"If you need a usable workflow field list, call comfyui_list_object_info again with node_class set to the exact node name.",
|
|
31513
|
-
"",
|
|
31514
|
-
"## JSON",
|
|
31515
|
-
formatJsonCodeBlock(structuredContent)
|
|
31516
|
-
].join("\n"),
|
|
31517
|
-
structuredContent
|
|
31518
|
-
);
|
|
31519
30994
|
} catch (error48) {
|
|
31520
30995
|
return handleComfyError(error48);
|
|
31521
30996
|
}
|
|
31522
30997
|
}
|
|
31523
|
-
|
|
31524
|
-
|
|
31525
|
-
|
|
31526
|
-
|
|
31527
|
-
|
|
31528
|
-
|
|
31529
|
-
|
|
31530
|
-
|
|
31531
|
-
|
|
31532
|
-
|
|
31533
|
-
|
|
31534
|
-
|
|
31535
|
-
|
|
31536
|
-
|
|
31537
|
-
|
|
31538
|
-
|
|
31539
|
-
|
|
31540
|
-
|
|
31541
|
-
|
|
31542
|
-
|
|
31543
|
-
|
|
31544
|
-
|
|
31545
|
-
|
|
31546
|
-
|
|
31547
|
-
|
|
31548
|
-
|
|
31549
|
-
|
|
31550
|
-
|
|
31551
|
-
|
|
31552
|
-
|
|
31553
|
-
|
|
31554
|
-
|
|
31555
|
-
|
|
31556
|
-
|
|
30998
|
+
function getWorkflowId(absolutePath) {
|
|
30999
|
+
return crypto.createHash("sha256").update(absolutePath).digest("hex").substring(0, 8);
|
|
31000
|
+
}
|
|
31001
|
+
async function handleDiscoverWorkflows(args) {
|
|
31002
|
+
const pathsToScan = [
|
|
31003
|
+
path.join(
|
|
31004
|
+
homedir(),
|
|
31005
|
+
"comfy",
|
|
31006
|
+
"ComfyUI",
|
|
31007
|
+
"user",
|
|
31008
|
+
"default",
|
|
31009
|
+
"workflows"
|
|
31010
|
+
),
|
|
31011
|
+
path.join(homedir(), ".fre4x-comfyui", "workflows"),
|
|
31012
|
+
path.join(process.cwd(), "workflows")
|
|
31013
|
+
];
|
|
31014
|
+
if (args.directory_path) pathsToScan.push(args.directory_path);
|
|
31015
|
+
const foundWorkflows = [];
|
|
31016
|
+
for (const dir of pathsToScan) {
|
|
31017
|
+
try {
|
|
31018
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
31019
|
+
for (const entry of entries) {
|
|
31020
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
31021
|
+
const fullPath = path.join(dir, entry.name);
|
|
31022
|
+
const defaultId = getWorkflowId(fullPath);
|
|
31023
|
+
const defaultName = entry.name.replace(/\.json$/, "");
|
|
31024
|
+
try {
|
|
31025
|
+
const content = await fs.readFile(fullPath, "utf8");
|
|
31026
|
+
const json2 = JSON.parse(content);
|
|
31027
|
+
const id = defaultId;
|
|
31028
|
+
let name = defaultName;
|
|
31029
|
+
let isWebUI = false;
|
|
31030
|
+
if (typeof json2 === "object" && json2 !== null) {
|
|
31031
|
+
if (Array.isArray(json2.nodes) && Array.isArray(json2.links)) {
|
|
31032
|
+
isWebUI = true;
|
|
31033
|
+
}
|
|
31034
|
+
if (typeof json2.name === "string") name = json2.name;
|
|
31035
|
+
else if (json2.workflow && typeof json2.workflow.name === "string")
|
|
31036
|
+
name = json2.workflow.name;
|
|
31037
|
+
}
|
|
31038
|
+
foundWorkflows.push({
|
|
31039
|
+
id,
|
|
31040
|
+
name,
|
|
31041
|
+
path: fullPath,
|
|
31042
|
+
isWebUI
|
|
31043
|
+
});
|
|
31044
|
+
} catch (e) {
|
|
31045
|
+
foundWorkflows.push({
|
|
31046
|
+
id: defaultId,
|
|
31047
|
+
name: defaultName,
|
|
31048
|
+
path: fullPath,
|
|
31049
|
+
error: String(e)
|
|
31050
|
+
});
|
|
31051
|
+
}
|
|
31052
|
+
}
|
|
31053
|
+
}
|
|
31054
|
+
} catch (_e) {
|
|
31557
31055
|
}
|
|
31558
|
-
return createAgentFacingResult(
|
|
31559
|
-
summarizeWorkflowResponseText(result),
|
|
31560
|
-
structuredContent
|
|
31561
|
-
);
|
|
31562
|
-
} catch (error48) {
|
|
31563
|
-
return handleComfyError(error48);
|
|
31564
31056
|
}
|
|
31565
|
-
|
|
31566
|
-
|
|
31567
|
-
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
|
|
31572
|
-
|
|
31573
|
-
|
|
31574
|
-
|
|
31575
|
-
|
|
31576
|
-
limit: paginated.limit,
|
|
31577
|
-
offset: paginated.offset,
|
|
31578
|
-
has_more: paginated.hasMore
|
|
31057
|
+
if (foundWorkflows.length === 0) {
|
|
31058
|
+
return {
|
|
31059
|
+
content: [
|
|
31060
|
+
{
|
|
31061
|
+
type: "text",
|
|
31062
|
+
text: `No workflows found in searched directories.`
|
|
31063
|
+
}
|
|
31064
|
+
],
|
|
31065
|
+
structuredContent: {
|
|
31066
|
+
workflows: [],
|
|
31067
|
+
searched_paths: pathsToScan
|
|
31579
31068
|
}
|
|
31580
31069
|
};
|
|
31581
|
-
return createAgentFacingResult(
|
|
31582
|
-
summarizeWorkflowListText(
|
|
31583
|
-
"# ComfyUI Stored Workflows",
|
|
31584
|
-
structuredContent.workflows,
|
|
31585
|
-
structuredContent.pagination
|
|
31586
|
-
),
|
|
31587
|
-
structuredContent
|
|
31588
|
-
);
|
|
31589
|
-
} catch (error48) {
|
|
31590
|
-
return handleComfyError(error48);
|
|
31591
31070
|
}
|
|
31592
|
-
|
|
31593
|
-
|
|
31594
|
-
|
|
31595
|
-
const
|
|
31596
|
-
|
|
31597
|
-
|
|
31598
|
-
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31071
|
+
const paginated = applyPagination(foundWorkflows, args);
|
|
31072
|
+
const text = `Found ${paginated.total} workflow files:
|
|
31073
|
+
` + formatListItems(paginated.items, (w) => {
|
|
31074
|
+
const formatTag = w.isWebUI ? "\u{1F504} Web UI (Auto-converted on run)" : "\u2705 API Format";
|
|
31075
|
+
let line = `[ID: ${w.id}] [${formatTag}] Name: "${w.name}"`;
|
|
31076
|
+
if (w.error) line += ` (Error: ${w.error})`;
|
|
31077
|
+
return line;
|
|
31078
|
+
}) + (paginated.hasMore ? `
|
|
31079
|
+
|
|
31080
|
+
${formatPaginationFooter(
|
|
31081
|
+
paginated.offset,
|
|
31082
|
+
paginated.limit,
|
|
31083
|
+
paginated.total
|
|
31084
|
+
)}` : "");
|
|
31085
|
+
return {
|
|
31086
|
+
content: [{ type: "text", text }],
|
|
31087
|
+
structuredContent: {
|
|
31606
31088
|
workflows: paginated.items,
|
|
31607
31089
|
pagination: {
|
|
31608
|
-
|
|
31609
|
-
limit: paginated.limit,
|
|
31090
|
+
total: paginated.total,
|
|
31610
31091
|
offset: paginated.offset,
|
|
31092
|
+
limit: paginated.limit,
|
|
31611
31093
|
has_more: paginated.hasMore
|
|
31612
31094
|
}
|
|
31613
|
-
};
|
|
31614
|
-
return createAgentFacingResult(
|
|
31615
|
-
summarizeWorkflowListText(
|
|
31616
|
-
`# ComfyUI Workflow Search
|
|
31617
|
-
|
|
31618
|
-
Query: ${args.query}`,
|
|
31619
|
-
structuredContent.workflows,
|
|
31620
|
-
structuredContent.pagination
|
|
31621
|
-
),
|
|
31622
|
-
structuredContent
|
|
31623
|
-
);
|
|
31624
|
-
} catch (error48) {
|
|
31625
|
-
return handleComfyError(error48);
|
|
31626
|
-
}
|
|
31627
|
-
}
|
|
31628
|
-
async function handleGetWorkflow(args) {
|
|
31629
|
-
try {
|
|
31630
|
-
const workflow = await readStoredWorkflowFile(args.workflow_id);
|
|
31631
|
-
if (!workflow) {
|
|
31632
|
-
return createNotFoundError(`Workflow ${args.workflow_id}`);
|
|
31633
31095
|
}
|
|
31634
|
-
|
|
31635
|
-
summarizeStoredWorkflowText(workflow),
|
|
31636
|
-
workflow
|
|
31637
|
-
);
|
|
31638
|
-
} catch (error48) {
|
|
31639
|
-
return handleComfyError(error48);
|
|
31640
|
-
}
|
|
31096
|
+
};
|
|
31641
31097
|
}
|
|
31642
|
-
async function
|
|
31643
|
-
|
|
31644
|
-
|
|
31645
|
-
|
|
31646
|
-
|
|
31647
|
-
|
|
31648
|
-
|
|
31649
|
-
|
|
31650
|
-
|
|
31098
|
+
async function waitForWorkflowResult(prompt_id, timeout_seconds) {
|
|
31099
|
+
const start = Date.now();
|
|
31100
|
+
const end = start + timeout_seconds * 1e3;
|
|
31101
|
+
while (Date.now() < end) {
|
|
31102
|
+
const history = IS_MOCK ? MOCK_FIXTURES.history : await fetchComfyJson(
|
|
31103
|
+
`/history/${prompt_id}`
|
|
31104
|
+
).catch(() => ({}));
|
|
31105
|
+
if (history?.[prompt_id]) {
|
|
31106
|
+
return history[prompt_id];
|
|
31651
31107
|
}
|
|
31652
|
-
|
|
31653
|
-
workflow_id: workflowId,
|
|
31654
|
-
name: args.name,
|
|
31655
|
-
description: args.description,
|
|
31656
|
-
tags: args.tags,
|
|
31657
|
-
prompt: args.prompt,
|
|
31658
|
-
source: IS_MOCK ? "mock" : "local",
|
|
31659
|
-
existing
|
|
31660
|
-
});
|
|
31661
|
-
await writeStoredWorkflowFile(workflow);
|
|
31662
|
-
return createAgentFacingResult(
|
|
31663
|
-
summarizeSavedWorkflowText(workflow),
|
|
31664
|
-
workflow
|
|
31665
|
-
);
|
|
31666
|
-
} catch (error48) {
|
|
31667
|
-
return handleComfyError(error48);
|
|
31108
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
31668
31109
|
}
|
|
31110
|
+
throw new Error(`Timeout waiting for workflow ${prompt_id}`);
|
|
31669
31111
|
}
|
|
31670
|
-
async function
|
|
31112
|
+
async function handleWaitForWorkflow(args) {
|
|
31671
31113
|
try {
|
|
31672
|
-
const
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
}
|
|
31676
|
-
const prompt = applyWorkflowOverrides(
|
|
31677
|
-
workflow.prompt,
|
|
31678
|
-
workflow.editable_inputs,
|
|
31679
|
-
args.overrides
|
|
31114
|
+
const historyEntry = await waitForWorkflowResult(
|
|
31115
|
+
args.prompt_id,
|
|
31116
|
+
args.timeout
|
|
31680
31117
|
);
|
|
31681
|
-
const
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31685
|
-
const
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
|
|
31689
|
-
|
|
31690
|
-
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
31696
|
-
|
|
31697
|
-
|
|
31698
|
-
|
|
31699
|
-
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31703
|
-
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
|
|
31714
|
-
formatMarkdownBulletList([
|
|
31715
|
-
`Workflow ID: ${workflow.workflow.id}`,
|
|
31716
|
-
`Workflow Name: ${workflow.workflow.name}`,
|
|
31717
|
-
`Prompt ID: ${submission.prompt_id}`,
|
|
31718
|
-
`Applied Overrides: ${args.overrides.length}`
|
|
31719
|
-
]),
|
|
31720
|
-
"",
|
|
31721
|
-
summarizeWorkflowResponseText(structuredContent),
|
|
31722
|
-
"",
|
|
31723
|
-
"## Await Result",
|
|
31724
|
-
summarizeWaitForWorkflowText(waitResult),
|
|
31725
|
-
"",
|
|
31726
|
-
"## JSON",
|
|
31727
|
-
formatJsonCodeBlock(structuredContent)
|
|
31728
|
-
].join("\n"),
|
|
31729
|
-
structuredContent
|
|
31730
|
-
);
|
|
31118
|
+
const outputsRecord = historyEntry.outputs || {};
|
|
31119
|
+
const content = [];
|
|
31120
|
+
let summaryText = `Workflow ${args.prompt_id} completed.
|
|
31121
|
+
`;
|
|
31122
|
+
for (const [, output] of Object.entries(outputsRecord)) {
|
|
31123
|
+
if (output && Array.isArray(output.images)) {
|
|
31124
|
+
for (const image of output.images) {
|
|
31125
|
+
if (image.filename && image.type) {
|
|
31126
|
+
if (!IS_MOCK) {
|
|
31127
|
+
try {
|
|
31128
|
+
const base643 = await fetchImageBase64(
|
|
31129
|
+
image.filename,
|
|
31130
|
+
image.type,
|
|
31131
|
+
image.subfolder
|
|
31132
|
+
);
|
|
31133
|
+
content.push({
|
|
31134
|
+
type: "image",
|
|
31135
|
+
data: base643.data,
|
|
31136
|
+
mimeType: base643.mimeType
|
|
31137
|
+
});
|
|
31138
|
+
summaryText += `Fetched image: ${image.filename}
|
|
31139
|
+
`;
|
|
31140
|
+
} catch (e) {
|
|
31141
|
+
summaryText += `Failed to fetch image ${image.filename}: ${String(e)}
|
|
31142
|
+
`;
|
|
31143
|
+
}
|
|
31144
|
+
} else {
|
|
31145
|
+
summaryText += `Mock image: ${image.filename}
|
|
31146
|
+
`;
|
|
31147
|
+
}
|
|
31148
|
+
}
|
|
31149
|
+
}
|
|
31150
|
+
}
|
|
31731
31151
|
}
|
|
31732
|
-
|
|
31733
|
-
|
|
31734
|
-
|
|
31735
|
-
|
|
31736
|
-
|
|
31737
|
-
formatMarkdownBulletList([
|
|
31738
|
-
`Workflow ID: ${workflow.workflow.id}`,
|
|
31739
|
-
`Workflow Name: ${workflow.workflow.name}`,
|
|
31740
|
-
`Prompt ID: ${submission.prompt_id}`,
|
|
31741
|
-
`Applied Overrides: ${args.overrides.length}`
|
|
31742
|
-
]),
|
|
31743
|
-
"",
|
|
31744
|
-
summarizeWorkflowResponseText(structuredContent),
|
|
31745
|
-
"",
|
|
31746
|
-
"## JSON",
|
|
31747
|
-
formatJsonCodeBlock(structuredContent)
|
|
31748
|
-
].join("\n"),
|
|
31749
|
-
structuredContent
|
|
31750
|
-
);
|
|
31152
|
+
content.unshift({ type: "text", text: summaryText });
|
|
31153
|
+
return {
|
|
31154
|
+
content,
|
|
31155
|
+
structuredContent: historyEntry
|
|
31156
|
+
};
|
|
31751
31157
|
} catch (error48) {
|
|
31752
31158
|
return handleComfyError(error48);
|
|
31753
31159
|
}
|
|
31754
31160
|
}
|
|
31755
|
-
async function
|
|
31161
|
+
async function handleWorkflowRun(args) {
|
|
31756
31162
|
try {
|
|
31757
|
-
|
|
31758
|
-
|
|
31759
|
-
)
|
|
31760
|
-
|
|
31761
|
-
|
|
31762
|
-
|
|
31763
|
-
|
|
31764
|
-
|
|
31765
|
-
|
|
31766
|
-
|
|
31767
|
-
|
|
31163
|
+
let content = "";
|
|
31164
|
+
let targetPath = "";
|
|
31165
|
+
if (args.workflow_file_path) {
|
|
31166
|
+
targetPath = args.workflow_file_path;
|
|
31167
|
+
try {
|
|
31168
|
+
if (!targetPath.includes(path.sep)) {
|
|
31169
|
+
const paths = [
|
|
31170
|
+
path.join(
|
|
31171
|
+
homedir(),
|
|
31172
|
+
"comfy",
|
|
31173
|
+
"ComfyUI",
|
|
31174
|
+
"user",
|
|
31175
|
+
"default",
|
|
31176
|
+
"workflows",
|
|
31177
|
+
targetPath
|
|
31178
|
+
),
|
|
31179
|
+
path.join(
|
|
31180
|
+
homedir(),
|
|
31181
|
+
".fre4x-comfyui",
|
|
31182
|
+
"workflows",
|
|
31183
|
+
targetPath
|
|
31184
|
+
),
|
|
31185
|
+
path.join(process.cwd(), "workflows", targetPath)
|
|
31186
|
+
];
|
|
31187
|
+
let found = false;
|
|
31188
|
+
for (const p of paths) {
|
|
31189
|
+
try {
|
|
31190
|
+
content = await fs.readFile(p, "utf8");
|
|
31191
|
+
targetPath = p;
|
|
31192
|
+
found = true;
|
|
31193
|
+
break;
|
|
31194
|
+
} catch (_e) {
|
|
31195
|
+
}
|
|
31196
|
+
}
|
|
31197
|
+
if (!found) {
|
|
31198
|
+
throw new Error(
|
|
31199
|
+
`Workflow file not found: ${targetPath}`
|
|
31200
|
+
);
|
|
31201
|
+
}
|
|
31202
|
+
} else {
|
|
31203
|
+
targetPath = await validateWorkflowPath(targetPath);
|
|
31204
|
+
content = await fs.readFile(targetPath, "utf8");
|
|
31205
|
+
}
|
|
31206
|
+
} catch (err) {
|
|
31207
|
+
return handleComfyError(err);
|
|
31208
|
+
}
|
|
31209
|
+
} else if (args.workflow_id) {
|
|
31210
|
+
const pathsToScan = [
|
|
31211
|
+
path.join(
|
|
31212
|
+
homedir(),
|
|
31213
|
+
"comfy",
|
|
31214
|
+
"ComfyUI",
|
|
31215
|
+
"user",
|
|
31216
|
+
"default",
|
|
31217
|
+
"workflows"
|
|
31218
|
+
),
|
|
31219
|
+
path.join(homedir(), ".fre4x-comfyui", "workflows"),
|
|
31220
|
+
path.join(process.cwd(), "workflows")
|
|
31221
|
+
];
|
|
31222
|
+
let found = false;
|
|
31223
|
+
for (const dir of pathsToScan) {
|
|
31224
|
+
try {
|
|
31225
|
+
const entries = await fs.readdir(dir, {
|
|
31226
|
+
withFileTypes: true
|
|
31227
|
+
});
|
|
31228
|
+
for (const entry of entries) {
|
|
31229
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
31230
|
+
const fullPath = path.join(dir, entry.name);
|
|
31231
|
+
const defaultId = getWorkflowId(fullPath);
|
|
31232
|
+
if (args.workflow_id === defaultId || args.workflow_id === entry.name.replace(/\.json$/, "")) {
|
|
31233
|
+
content = await fs.readFile(fullPath, "utf8");
|
|
31234
|
+
targetPath = fullPath;
|
|
31235
|
+
found = true;
|
|
31236
|
+
break;
|
|
31237
|
+
}
|
|
31238
|
+
}
|
|
31239
|
+
}
|
|
31240
|
+
if (found) break;
|
|
31241
|
+
} catch (_e) {
|
|
31768
31242
|
}
|
|
31243
|
+
}
|
|
31244
|
+
if (!found) {
|
|
31245
|
+
throw new Error(`Workflow ID not found: ${args.workflow_id}`);
|
|
31246
|
+
}
|
|
31247
|
+
} else {
|
|
31248
|
+
throw new Error(
|
|
31249
|
+
"Must provide either workflow_file_path or workflow_id."
|
|
31769
31250
|
);
|
|
31770
31251
|
}
|
|
31771
|
-
|
|
31772
|
-
|
|
31773
|
-
|
|
31774
|
-
|
|
31775
|
-
|
|
31776
|
-
|
|
31777
|
-
|
|
31778
|
-
|
|
31779
|
-
|
|
31780
|
-
|
|
31252
|
+
let promptObj = {};
|
|
31253
|
+
try {
|
|
31254
|
+
promptObj = JSON.parse(content);
|
|
31255
|
+
} catch (e) {
|
|
31256
|
+
throw new Error(
|
|
31257
|
+
`SyntaxError: Invalid JSON in ${targetPath}: ${e instanceof Error ? e.message : String(e)}`
|
|
31258
|
+
);
|
|
31259
|
+
}
|
|
31260
|
+
if (isWebUIFormat(promptObj)) {
|
|
31261
|
+
let objectInfo;
|
|
31262
|
+
if (IS_MOCK) {
|
|
31263
|
+
objectInfo = MOCK_FIXTURES.object_info;
|
|
31264
|
+
} else {
|
|
31265
|
+
objectInfo = await fetchComfyJson(
|
|
31266
|
+
"/object_info"
|
|
31267
|
+
);
|
|
31781
31268
|
}
|
|
31269
|
+
promptObj = convertWebUIToAPI(
|
|
31270
|
+
promptObj,
|
|
31271
|
+
objectInfo
|
|
31272
|
+
);
|
|
31273
|
+
}
|
|
31274
|
+
if (promptObj.prompt && typeof promptObj.prompt === "object") {
|
|
31275
|
+
promptObj = promptObj.prompt;
|
|
31276
|
+
}
|
|
31277
|
+
if (args.overrides) {
|
|
31278
|
+
applyOverrides(promptObj, args.overrides);
|
|
31279
|
+
}
|
|
31280
|
+
const clientId = `mcp-${Date.now()}`;
|
|
31281
|
+
const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson("/prompt", {
|
|
31282
|
+
method: "POST",
|
|
31283
|
+
body: JSON.stringify({
|
|
31284
|
+
prompt: promptObj,
|
|
31285
|
+
client_id: clientId
|
|
31286
|
+
})
|
|
31287
|
+
});
|
|
31288
|
+
if (result.node_errors && Object.keys(result.node_errors).length > 0) {
|
|
31289
|
+
return {
|
|
31290
|
+
content: [
|
|
31291
|
+
{
|
|
31292
|
+
type: "text",
|
|
31293
|
+
text: formatWorkflowNodeErrors(result.node_errors)
|
|
31294
|
+
}
|
|
31295
|
+
],
|
|
31296
|
+
structuredContent: result,
|
|
31297
|
+
isError: true
|
|
31298
|
+
};
|
|
31299
|
+
}
|
|
31300
|
+
if (args.await) {
|
|
31301
|
+
return await handleWaitForWorkflow({
|
|
31302
|
+
prompt_id: result.prompt_id,
|
|
31303
|
+
timeout: args.timeout
|
|
31304
|
+
});
|
|
31305
|
+
}
|
|
31306
|
+
return {
|
|
31307
|
+
content: [
|
|
31308
|
+
{
|
|
31309
|
+
type: "text",
|
|
31310
|
+
text: `Workflow submitted. prompt_id: ${result.prompt_id}, queue: ${result.number}`
|
|
31311
|
+
}
|
|
31312
|
+
],
|
|
31313
|
+
structuredContent: result
|
|
31782
31314
|
};
|
|
31783
|
-
return createAgentFacingResult(
|
|
31784
|
-
[
|
|
31785
|
-
"# ComfyUI History",
|
|
31786
|
-
"",
|
|
31787
|
-
"## Pagination",
|
|
31788
|
-
formatMarkdownBulletList([
|
|
31789
|
-
`Returned: ${paginated.items.length}`,
|
|
31790
|
-
`Total: ${paginated.total}`,
|
|
31791
|
-
`Limit: ${paginated.limit}`,
|
|
31792
|
-
`Offset: ${paginated.offset}`,
|
|
31793
|
-
`Has More: ${paginated.hasMore ? "yes" : "no"}`
|
|
31794
|
-
]),
|
|
31795
|
-
"",
|
|
31796
|
-
"## Prompt IDs In This Page",
|
|
31797
|
-
formatMarkdownBulletList(
|
|
31798
|
-
paginated.items.map(([promptId]) => promptId)
|
|
31799
|
-
),
|
|
31800
|
-
"",
|
|
31801
|
-
"## Next Step",
|
|
31802
|
-
paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} to retrieve the next page.` : "This page contains the final set of history entries for the current query.",
|
|
31803
|
-
"",
|
|
31804
|
-
"## JSON",
|
|
31805
|
-
formatJsonCodeBlock(structuredContent)
|
|
31806
|
-
].join("\n"),
|
|
31807
|
-
structuredContent
|
|
31808
|
-
);
|
|
31809
|
-
} catch (error48) {
|
|
31810
|
-
return handleComfyError(error48);
|
|
31811
|
-
}
|
|
31812
|
-
}
|
|
31813
|
-
async function handleWaitForWorkflow(args) {
|
|
31814
|
-
try {
|
|
31815
|
-
const structuredContent = await waitForWorkflowResult(args);
|
|
31816
|
-
return createAgentFacingResult(
|
|
31817
|
-
summarizeWaitForWorkflowText(structuredContent),
|
|
31818
|
-
structuredContent
|
|
31819
|
-
);
|
|
31820
|
-
} catch (error48) {
|
|
31821
|
-
return handleComfyError(error48);
|
|
31822
|
-
}
|
|
31823
|
-
}
|
|
31824
|
-
async function handleGetQueue() {
|
|
31825
|
-
try {
|
|
31826
|
-
const queue = IS_MOCK ? MOCK_FIXTURES.queue : await fetchComfyJson("/queue");
|
|
31827
|
-
const queueRecord = ensureRecord(queue, "queue");
|
|
31828
|
-
const structuredContent = {
|
|
31829
|
-
queue_running: Array.isArray(queueRecord.queue_running) ? queueRecord.queue_running : [],
|
|
31830
|
-
queue_pending: Array.isArray(queueRecord.queue_pending) ? queueRecord.queue_pending : []
|
|
31831
|
-
};
|
|
31832
|
-
return createAgentFacingResult(
|
|
31833
|
-
[
|
|
31834
|
-
"# ComfyUI Queue",
|
|
31835
|
-
"",
|
|
31836
|
-
"## Summary",
|
|
31837
|
-
formatMarkdownBulletList([
|
|
31838
|
-
`Running Jobs: ${structuredContent.queue_running.length}`,
|
|
31839
|
-
`Pending Jobs: ${structuredContent.queue_pending.length}`
|
|
31840
|
-
]),
|
|
31841
|
-
"",
|
|
31842
|
-
"## JSON",
|
|
31843
|
-
formatJsonCodeBlock(structuredContent)
|
|
31844
|
-
].join("\n"),
|
|
31845
|
-
structuredContent
|
|
31846
|
-
);
|
|
31847
31315
|
} catch (error48) {
|
|
31848
31316
|
return handleComfyError(error48);
|
|
31849
31317
|
}
|
|
31850
31318
|
}
|
|
31851
|
-
async function handleGetViewUrl(args) {
|
|
31852
|
-
const url2 = buildViewUrl(args.filename, args.type, args.subfolder);
|
|
31853
|
-
return createAgentFacingResult(
|
|
31854
|
-
[
|
|
31855
|
-
"# ComfyUI View URL",
|
|
31856
|
-
"",
|
|
31857
|
-
"## Summary",
|
|
31858
|
-
formatMarkdownBulletList([
|
|
31859
|
-
`Filename: ${args.filename}`,
|
|
31860
|
-
`Subfolder: ${args.subfolder ?? "(root)"}`,
|
|
31861
|
-
`Type: ${args.type}`,
|
|
31862
|
-
`URL: ${url2}`
|
|
31863
|
-
]),
|
|
31864
|
-
"",
|
|
31865
|
-
"## JSON",
|
|
31866
|
-
formatJsonCodeBlock({ url: url2 })
|
|
31867
|
-
].join("\n"),
|
|
31868
|
-
{ url: url2 }
|
|
31869
|
-
);
|
|
31870
|
-
}
|
|
31871
31319
|
function createServer() {
|
|
31872
31320
|
const server = new McpServer({
|
|
31873
31321
|
name: "@fre4x/comfyui",
|
|
31874
31322
|
version: PACKAGE_VERSION
|
|
31875
31323
|
});
|
|
31876
|
-
server.
|
|
31877
|
-
"
|
|
31878
|
-
|
|
31879
|
-
|
|
31880
|
-
|
|
31881
|
-
inputSchema: NoArgsSchema,
|
|
31882
|
-
outputSchema: SystemInfoSchema,
|
|
31883
|
-
annotations: {
|
|
31884
|
-
readOnlyHint: true,
|
|
31885
|
-
idempotentHint: true,
|
|
31886
|
-
openWorldHint: true
|
|
31887
|
-
}
|
|
31888
|
-
},
|
|
31889
|
-
handleGetSystemInfo
|
|
31890
|
-
);
|
|
31891
|
-
server.registerTool(
|
|
31892
|
-
"comfyui_list_object_info",
|
|
31893
|
-
{
|
|
31894
|
-
title: "List ComfyUI Node Definitions",
|
|
31895
|
-
description: "Get definitions of available node classes, with optional node lookup and pagination.",
|
|
31896
|
-
inputSchema: ListObjectInfoInputSchema,
|
|
31897
|
-
outputSchema: ObjectInfoSchema,
|
|
31898
|
-
annotations: {
|
|
31899
|
-
readOnlyHint: true,
|
|
31900
|
-
idempotentHint: true,
|
|
31901
|
-
openWorldHint: true
|
|
31902
|
-
}
|
|
31903
|
-
},
|
|
31904
|
-
handleListObjectInfo
|
|
31905
|
-
);
|
|
31906
|
-
server.registerTool(
|
|
31907
|
-
"comfyui_execute_workflow",
|
|
31908
|
-
{
|
|
31909
|
-
title: "Execute ComfyUI Workflow",
|
|
31910
|
-
description: "Submit a ComfyUI workflow in API JSON format and return its prompt_id.",
|
|
31911
|
-
inputSchema: ExecuteWorkflowInputSchema,
|
|
31912
|
-
outputSchema: WorkflowResponseSchema,
|
|
31913
|
-
annotations: {
|
|
31914
|
-
openWorldHint: true
|
|
31915
|
-
}
|
|
31916
|
-
},
|
|
31917
|
-
handleExecuteWorkflow
|
|
31324
|
+
server.tool(
|
|
31325
|
+
"comfyui_inspect_node",
|
|
31326
|
+
"Inspect a ComfyUI node or list all nodes.",
|
|
31327
|
+
InspectNodeSchema.shape,
|
|
31328
|
+
handleInspectNode
|
|
31918
31329
|
);
|
|
31919
|
-
server.
|
|
31920
|
-
"
|
|
31921
|
-
|
|
31922
|
-
|
|
31923
|
-
|
|
31924
|
-
inputSchema: ListWorkflowsInputSchema,
|
|
31925
|
-
outputSchema: WorkflowListSchema,
|
|
31926
|
-
annotations: {
|
|
31927
|
-
readOnlyHint: true,
|
|
31928
|
-
idempotentHint: true,
|
|
31929
|
-
openWorldHint: true
|
|
31930
|
-
}
|
|
31931
|
-
},
|
|
31932
|
-
handleListWorkflows
|
|
31330
|
+
server.tool(
|
|
31331
|
+
"comfyui_discover_workflows",
|
|
31332
|
+
"Scan standard directories for .json workflow files.",
|
|
31333
|
+
DiscoverWorkflowsSchema.shape,
|
|
31334
|
+
handleDiscoverWorkflows
|
|
31933
31335
|
);
|
|
31934
|
-
server.
|
|
31935
|
-
"
|
|
31936
|
-
|
|
31937
|
-
|
|
31938
|
-
|
|
31939
|
-
inputSchema: SearchWorkflowsInputSchema,
|
|
31940
|
-
outputSchema: WorkflowListSchema,
|
|
31941
|
-
annotations: {
|
|
31942
|
-
readOnlyHint: true,
|
|
31943
|
-
idempotentHint: true,
|
|
31944
|
-
openWorldHint: true
|
|
31945
|
-
}
|
|
31946
|
-
},
|
|
31947
|
-
handleSearchWorkflows
|
|
31948
|
-
);
|
|
31949
|
-
server.registerTool(
|
|
31950
|
-
"comfyui_get_workflow",
|
|
31951
|
-
{
|
|
31952
|
-
title: "Get Stored Workflow",
|
|
31953
|
-
description: "Get a stored ComfyUI workflow and its editable inputs.",
|
|
31954
|
-
inputSchema: GetWorkflowInputSchema,
|
|
31955
|
-
outputSchema: StoredWorkflowSchema,
|
|
31956
|
-
annotations: {
|
|
31957
|
-
readOnlyHint: true,
|
|
31958
|
-
idempotentHint: true,
|
|
31959
|
-
openWorldHint: true
|
|
31960
|
-
}
|
|
31961
|
-
},
|
|
31962
|
-
handleGetWorkflow
|
|
31963
|
-
);
|
|
31964
|
-
server.registerTool(
|
|
31965
|
-
"comfyui_save_workflow",
|
|
31966
|
-
{
|
|
31967
|
-
title: "Save Workflow",
|
|
31968
|
-
description: "Save a ComfyUI workflow for later reuse.",
|
|
31969
|
-
inputSchema: SaveWorkflowInputSchema,
|
|
31970
|
-
outputSchema: StoredWorkflowSchema,
|
|
31971
|
-
annotations: {
|
|
31972
|
-
openWorldHint: true
|
|
31973
|
-
}
|
|
31974
|
-
},
|
|
31975
|
-
handleSaveWorkflow
|
|
31336
|
+
server.tool(
|
|
31337
|
+
"comfyui_workflow_run",
|
|
31338
|
+
"Run a workflow from a file path or ID, applying overrides, and optionally returning images.",
|
|
31339
|
+
WorkflowRunSchema.shape,
|
|
31340
|
+
handleWorkflowRun
|
|
31976
31341
|
);
|
|
31977
|
-
server.
|
|
31978
|
-
"comfyui_run_workflow",
|
|
31979
|
-
{
|
|
31980
|
-
title: "Run Stored Workflow",
|
|
31981
|
-
description: "Run a stored workflow with optional overrides.",
|
|
31982
|
-
inputSchema: RunWorkflowInputSchema,
|
|
31983
|
-
outputSchema: RunStoredWorkflowSchema,
|
|
31984
|
-
annotations: {
|
|
31985
|
-
openWorldHint: true
|
|
31986
|
-
}
|
|
31987
|
-
},
|
|
31988
|
-
handleRunWorkflow
|
|
31989
|
-
);
|
|
31990
|
-
server.registerTool(
|
|
31991
|
-
"comfyui_get_history",
|
|
31992
|
-
{
|
|
31993
|
-
title: "Get ComfyUI History",
|
|
31994
|
-
description: "Get execution history or a specific workflow result, with pagination for list mode.",
|
|
31995
|
-
inputSchema: GetHistoryInputSchema,
|
|
31996
|
-
outputSchema: HistorySchema,
|
|
31997
|
-
annotations: {
|
|
31998
|
-
readOnlyHint: true,
|
|
31999
|
-
idempotentHint: true,
|
|
32000
|
-
openWorldHint: true
|
|
32001
|
-
}
|
|
32002
|
-
},
|
|
32003
|
-
handleGetHistory
|
|
32004
|
-
);
|
|
32005
|
-
server.registerTool(
|
|
31342
|
+
server.tool(
|
|
32006
31343
|
"comfyui_wait_for_workflow",
|
|
32007
|
-
|
|
32008
|
-
|
|
32009
|
-
description: "Wait for a workflow result with timeout-based polling.",
|
|
32010
|
-
inputSchema: WaitForWorkflowInputSchema,
|
|
32011
|
-
outputSchema: WaitForWorkflowSchema,
|
|
32012
|
-
annotations: {
|
|
32013
|
-
openWorldHint: true
|
|
32014
|
-
}
|
|
32015
|
-
},
|
|
31344
|
+
"Wait for a submitted workflow to finish and fetch its images.",
|
|
31345
|
+
WaitForWorkflowSchema.shape,
|
|
32016
31346
|
handleWaitForWorkflow
|
|
32017
31347
|
);
|
|
32018
|
-
server.registerTool(
|
|
32019
|
-
"comfyui_get_queue",
|
|
32020
|
-
{
|
|
32021
|
-
title: "Get ComfyUI Queue",
|
|
32022
|
-
description: "Get the current pending and running jobs in the ComfyUI queue.",
|
|
32023
|
-
inputSchema: NoArgsSchema,
|
|
32024
|
-
outputSchema: QueueSchema,
|
|
32025
|
-
annotations: {
|
|
32026
|
-
readOnlyHint: true,
|
|
32027
|
-
idempotentHint: true,
|
|
32028
|
-
openWorldHint: true
|
|
32029
|
-
}
|
|
32030
|
-
},
|
|
32031
|
-
handleGetQueue
|
|
32032
|
-
);
|
|
32033
|
-
server.registerTool(
|
|
32034
|
-
"comfyui_get_view_url",
|
|
32035
|
-
{
|
|
32036
|
-
title: "Get ComfyUI View URL",
|
|
32037
|
-
description: "Build the direct URL for a generated file in ComfyUI.",
|
|
32038
|
-
inputSchema: GetViewUrlInputSchema,
|
|
32039
|
-
outputSchema: ViewUrlSchema,
|
|
32040
|
-
annotations: {
|
|
32041
|
-
readOnlyHint: true,
|
|
32042
|
-
idempotentHint: true,
|
|
32043
|
-
openWorldHint: true
|
|
32044
|
-
}
|
|
32045
|
-
},
|
|
32046
|
-
handleGetViewUrl
|
|
32047
|
-
);
|
|
32048
31348
|
return server;
|
|
32049
31349
|
}
|
|
32050
31350
|
function isMainModule(url2) {
|