@fre4x/comfyui 1.0.60 → 1.0.62
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/README.md +48 -30
- package/dist/index.js +951 -1337
- 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,237 @@ 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;
|
|
30597
|
+
orderedEntries.push([inputName, inputDefinition]);
|
|
30598
|
+
seen.add(inputName);
|
|
30845
30599
|
}
|
|
30846
|
-
const
|
|
30847
|
-
|
|
30848
|
-
|
|
30849
|
-
|
|
30850
|
-
|
|
30851
|
-
return errorValue.message;
|
|
30852
|
-
}
|
|
30853
|
-
return void 0;
|
|
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
|
-
|
|
30893
|
-
|
|
30894
|
-
async function sleep(ms) {
|
|
30895
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
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
|
-
);
|
|
30611
|
+
function isWidgetInput(inputTypeData) {
|
|
30612
|
+
const typeName = getWidgetTypeName(inputTypeData);
|
|
30613
|
+
return typeName === "COMBO" || WIDGET_TYPES.has(typeName);
|
|
30908
30614
|
}
|
|
30909
|
-
function
|
|
30910
|
-
|
|
30911
|
-
|
|
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
|
-
|
|
31009
|
-
|
|
31010
|
-
|
|
31011
|
-
|
|
31012
|
-
}
|
|
31013
|
-
|
|
31014
|
-
|
|
31015
|
-
|
|
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
|
+
var WorkflowInputError = class extends Error {
|
|
30727
|
+
constructor(message, field, kind = "validation") {
|
|
30728
|
+
super(message);
|
|
30729
|
+
this.field = field;
|
|
30730
|
+
this.kind = kind;
|
|
30731
|
+
this.name = "WorkflowInputError";
|
|
30732
|
+
}
|
|
30733
|
+
};
|
|
30734
|
+
function isRecord(value) {
|
|
30735
|
+
return typeof value === "object" && value !== null;
|
|
31120
30736
|
}
|
|
31121
|
-
function
|
|
31122
|
-
if (!isRecord(
|
|
30737
|
+
function summarizeNodeErrors(nodeErrors) {
|
|
30738
|
+
if (!isRecord(nodeErrors)) {
|
|
31123
30739
|
return [];
|
|
31124
30740
|
}
|
|
31125
|
-
const
|
|
31126
|
-
for (const
|
|
31127
|
-
if (!isRecord(
|
|
30741
|
+
const lines = [];
|
|
30742
|
+
for (const [nodeId, rawNodeError] of Object.entries(nodeErrors)) {
|
|
30743
|
+
if (!isRecord(rawNodeError)) {
|
|
31128
30744
|
continue;
|
|
31129
30745
|
}
|
|
31130
|
-
|
|
31131
|
-
|
|
30746
|
+
const classType = typeof rawNodeError.class_type === "string" ? ` (${rawNodeError.class_type})` : "";
|
|
30747
|
+
const rawErrors = Array.isArray(rawNodeError.errors) ? rawNodeError.errors : [];
|
|
30748
|
+
const missingInputs = [];
|
|
30749
|
+
const otherMessages = [];
|
|
30750
|
+
for (const rawError of rawErrors) {
|
|
30751
|
+
if (!isRecord(rawError)) {
|
|
31132
30752
|
continue;
|
|
31133
30753
|
}
|
|
31134
|
-
const
|
|
31135
|
-
|
|
31136
|
-
|
|
31137
|
-
|
|
31138
|
-
|
|
31139
|
-
|
|
31140
|
-
|
|
31141
|
-
|
|
31142
|
-
|
|
31143
|
-
|
|
31144
|
-
|
|
30754
|
+
const errorType = rawError.type;
|
|
30755
|
+
const errorMessage = rawError.message;
|
|
30756
|
+
const errorDetails = rawError.details;
|
|
30757
|
+
if (errorType === "required_input_missing" && typeof errorDetails === "string") {
|
|
30758
|
+
missingInputs.push(errorDetails);
|
|
30759
|
+
continue;
|
|
30760
|
+
}
|
|
30761
|
+
if (typeof errorMessage === "string") {
|
|
30762
|
+
otherMessages.push(
|
|
30763
|
+
typeof errorDetails === "string" && errorDetails.length > 0 ? `${errorMessage}: ${errorDetails}` : errorMessage
|
|
30764
|
+
);
|
|
30765
|
+
}
|
|
30766
|
+
}
|
|
30767
|
+
const parts = [];
|
|
30768
|
+
if (missingInputs.length > 0) {
|
|
30769
|
+
parts.push(`missing inputs: ${missingInputs.join(", ")}`);
|
|
30770
|
+
}
|
|
30771
|
+
if (otherMessages.length > 0) {
|
|
30772
|
+
parts.push(otherMessages.join("; "));
|
|
30773
|
+
}
|
|
30774
|
+
if (parts.length > 0) {
|
|
30775
|
+
lines.push(`Node ${nodeId}${classType}: ${parts.join("; ")}`);
|
|
31145
30776
|
}
|
|
31146
30777
|
}
|
|
31147
|
-
return
|
|
31148
|
-
}
|
|
31149
|
-
function getHistoryStatus(entry) {
|
|
31150
|
-
if (!isRecord(entry) || !isRecord(entry.status)) {
|
|
31151
|
-
return "unknown";
|
|
31152
|
-
}
|
|
31153
|
-
return typeof entry.status.status_str === "string" ? entry.status.status_str : "unknown";
|
|
30778
|
+
return lines;
|
|
31154
30779
|
}
|
|
31155
|
-
function
|
|
31156
|
-
if (!isRecord(
|
|
31157
|
-
return
|
|
30780
|
+
function summarizeComfyErrorDetails(details) {
|
|
30781
|
+
if (!isRecord(details)) {
|
|
30782
|
+
return "";
|
|
31158
30783
|
}
|
|
31159
|
-
|
|
31160
|
-
|
|
30784
|
+
const lines = [];
|
|
30785
|
+
const errorDetails = isRecord(details.error) ? details.error : void 0;
|
|
30786
|
+
if (typeof errorDetails?.message === "string") {
|
|
30787
|
+
lines.push(errorDetails.message);
|
|
30788
|
+
} else if (typeof details.message === "string") {
|
|
30789
|
+
lines.push(details.message);
|
|
31161
30790
|
}
|
|
31162
|
-
|
|
31163
|
-
|
|
31164
|
-
|
|
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);
|
|
30791
|
+
const nodeLines = summarizeNodeErrors(details.node_errors);
|
|
30792
|
+
if (nodeLines.length > 0) {
|
|
30793
|
+
lines.push(...nodeLines);
|
|
31290
30794
|
}
|
|
31291
|
-
|
|
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");
|
|
30795
|
+
return lines.join("\n");
|
|
31363
30796
|
}
|
|
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
|
-
);
|
|
30797
|
+
function formatWorkflowNodeErrors(nodeErrors) {
|
|
30798
|
+
const nodeLines = summarizeNodeErrors(nodeErrors);
|
|
30799
|
+
if (nodeLines.length === 0) {
|
|
30800
|
+
return "Workflow submission failed validation.";
|
|
31370
30801
|
}
|
|
31371
|
-
return
|
|
31372
|
-
content: [
|
|
31373
|
-
{
|
|
31374
|
-
type: "text",
|
|
31375
|
-
text: normalizedText
|
|
31376
|
-
}
|
|
31377
|
-
],
|
|
31378
|
-
structuredContent
|
|
31379
|
-
};
|
|
30802
|
+
return ["Workflow submission failed validation.", ...nodeLines].join("\n");
|
|
31380
30803
|
}
|
|
31381
30804
|
async function fetchComfyJson(apiPath, options) {
|
|
31382
30805
|
const url2 = new URL(apiPath, COMFYUI_SERVER_URL).toString();
|
|
@@ -31385,666 +30808,857 @@ async function fetchComfyJson(apiPath, options) {
|
|
|
31385
30808
|
response = await fetch(url2, {
|
|
31386
30809
|
...options,
|
|
31387
30810
|
headers: {
|
|
31388
|
-
|
|
31389
|
-
...options?.headers
|
|
30811
|
+
Accept: "application/json",
|
|
30812
|
+
...options?.headers || {}
|
|
31390
30813
|
}
|
|
31391
30814
|
});
|
|
31392
|
-
} catch {
|
|
31393
|
-
throw new
|
|
31394
|
-
|
|
31395
|
-
503
|
|
30815
|
+
} catch (error48) {
|
|
30816
|
+
throw new Error(
|
|
30817
|
+
`Failed to connect to ComfyUI server at ${COMFYUI_SERVER_URL}: ${error48 instanceof Error ? error48.message : String(error48)}`
|
|
31396
30818
|
);
|
|
31397
30819
|
}
|
|
31398
|
-
|
|
30820
|
+
if (response.status === 404) {
|
|
30821
|
+
throw new ComfyApiError(`Not found: ${apiPath}`, 404, null);
|
|
30822
|
+
}
|
|
30823
|
+
const text = await response.text();
|
|
31399
30824
|
let body;
|
|
31400
|
-
|
|
31401
|
-
body =
|
|
31402
|
-
}
|
|
31403
|
-
|
|
30825
|
+
try {
|
|
30826
|
+
body = text ? JSON.parse(text) : null;
|
|
30827
|
+
} catch {
|
|
30828
|
+
throw new ComfyApiError(
|
|
30829
|
+
`Invalid JSON response from ComfyUI: ${text.slice(0, 100)}`,
|
|
30830
|
+
response.status,
|
|
30831
|
+
text
|
|
30832
|
+
);
|
|
31404
30833
|
}
|
|
31405
30834
|
if (!response.ok) {
|
|
31406
30835
|
throw new ComfyApiError(
|
|
31407
|
-
|
|
30836
|
+
"The ComfyUI service returned an error.",
|
|
31408
30837
|
response.status,
|
|
31409
30838
|
body
|
|
31410
30839
|
);
|
|
31411
30840
|
}
|
|
31412
30841
|
return body;
|
|
31413
30842
|
}
|
|
31414
|
-
async function
|
|
31415
|
-
|
|
31416
|
-
|
|
31417
|
-
|
|
31418
|
-
|
|
31419
|
-
|
|
31420
|
-
|
|
31421
|
-
|
|
31422
|
-
|
|
31423
|
-
|
|
31424
|
-
|
|
31425
|
-
|
|
31426
|
-
|
|
31427
|
-
|
|
30843
|
+
async function fetchImageBase64(filename, type, subfolder) {
|
|
30844
|
+
const params = new URLSearchParams({ filename, type });
|
|
30845
|
+
if (subfolder) params.set("subfolder", subfolder);
|
|
30846
|
+
const url2 = new URL(
|
|
30847
|
+
`/view?${params.toString()}`,
|
|
30848
|
+
COMFYUI_SERVER_URL
|
|
30849
|
+
).toString();
|
|
30850
|
+
const response = await fetch(url2);
|
|
30851
|
+
if (!response.ok)
|
|
30852
|
+
throw new Error(`Failed to fetch image: ${response.statusText}`);
|
|
30853
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
30854
|
+
const mimeType = response.headers.get("content-type") || "image/png";
|
|
30855
|
+
return {
|
|
30856
|
+
data: Buffer.from(arrayBuffer).toString("base64"),
|
|
30857
|
+
mimeType
|
|
30858
|
+
};
|
|
31428
30859
|
}
|
|
31429
30860
|
function handleComfyError(error48) {
|
|
31430
30861
|
if (error48 instanceof ZodError) {
|
|
31431
30862
|
return createValidationError(
|
|
31432
|
-
|
|
30863
|
+
"arguments",
|
|
31433
30864
|
error48.issues[0]?.message || "Invalid input."
|
|
31434
30865
|
);
|
|
31435
30866
|
}
|
|
30867
|
+
if (error48 instanceof WorkflowInputError) {
|
|
30868
|
+
return error48.kind === "not_found" ? createNotFoundError(error48.message) : createValidationError(error48.field, error48.message);
|
|
30869
|
+
}
|
|
31436
30870
|
if (error48 instanceof ComfyApiError) {
|
|
31437
|
-
const
|
|
31438
|
-
|
|
31439
|
-
|
|
31440
|
-
|
|
31441
|
-
|
|
31442
|
-
|
|
31443
|
-
|
|
31444
|
-
|
|
31445
|
-
|
|
31446
|
-
|
|
31447
|
-
|
|
30871
|
+
const detailSummary = summarizeComfyErrorDetails(error48.details);
|
|
30872
|
+
return {
|
|
30873
|
+
content: [
|
|
30874
|
+
{
|
|
30875
|
+
type: "text",
|
|
30876
|
+
text: detailSummary.length > 0 ? `${error48.message}
|
|
30877
|
+
${detailSummary}` : error48.message
|
|
30878
|
+
}
|
|
30879
|
+
],
|
|
30880
|
+
structuredContent: error48.details || {},
|
|
30881
|
+
isError: true
|
|
30882
|
+
};
|
|
31448
30883
|
}
|
|
31449
30884
|
return createInternalError(error48);
|
|
31450
30885
|
}
|
|
31451
|
-
async function
|
|
31452
|
-
|
|
31453
|
-
|
|
31454
|
-
|
|
31455
|
-
|
|
31456
|
-
|
|
31457
|
-
|
|
30886
|
+
async function validateWorkflowPath(targetPath) {
|
|
30887
|
+
const resolvedPath = path.resolve(targetPath);
|
|
30888
|
+
const allowedRoots = getWorkflowDirectories();
|
|
30889
|
+
const isAllowed = allowedRoots.some(
|
|
30890
|
+
(root) => resolvedPath.startsWith(path.resolve(root))
|
|
30891
|
+
);
|
|
30892
|
+
if (!isAllowed) {
|
|
30893
|
+
throw new WorkflowInputError(
|
|
30894
|
+
`Access denied: ${targetPath} is outside allowed workflow directories.`,
|
|
30895
|
+
"workflow_file_path"
|
|
31458
30896
|
);
|
|
31459
|
-
} catch (error48) {
|
|
31460
|
-
return handleComfyError(error48);
|
|
31461
30897
|
}
|
|
31462
|
-
}
|
|
31463
|
-
async function handleListObjectInfo(args) {
|
|
31464
30898
|
try {
|
|
31465
|
-
|
|
31466
|
-
|
|
30899
|
+
await fs.access(resolvedPath);
|
|
30900
|
+
return resolvedPath;
|
|
30901
|
+
} catch {
|
|
30902
|
+
throw new WorkflowInputError(
|
|
30903
|
+
`Workflow file not found: ${targetPath}`,
|
|
30904
|
+
"workflow_file_path",
|
|
30905
|
+
"not_found"
|
|
31467
30906
|
);
|
|
31468
|
-
|
|
31469
|
-
|
|
31470
|
-
|
|
31471
|
-
|
|
31472
|
-
|
|
31473
|
-
|
|
31474
|
-
|
|
31475
|
-
|
|
31476
|
-
|
|
30907
|
+
}
|
|
30908
|
+
}
|
|
30909
|
+
function getWorkflowDirectories(extraPath) {
|
|
30910
|
+
const directories = [
|
|
30911
|
+
path.join(
|
|
30912
|
+
homedir(),
|
|
30913
|
+
"comfy",
|
|
30914
|
+
"ComfyUI",
|
|
30915
|
+
"user",
|
|
30916
|
+
"default",
|
|
30917
|
+
"workflows"
|
|
30918
|
+
),
|
|
30919
|
+
path.join(homedir(), ".fre4x-comfyui", "workflows"),
|
|
30920
|
+
path.join(process.cwd(), "workflows")
|
|
30921
|
+
];
|
|
30922
|
+
if (extraPath) {
|
|
30923
|
+
directories.push(extraPath);
|
|
30924
|
+
}
|
|
30925
|
+
return directories;
|
|
30926
|
+
}
|
|
30927
|
+
function getWorkflowNameCandidates(name) {
|
|
30928
|
+
const trimmed = name.trim();
|
|
30929
|
+
if (trimmed.length === 0) {
|
|
30930
|
+
return [];
|
|
30931
|
+
}
|
|
30932
|
+
if (trimmed.endsWith(".json")) {
|
|
30933
|
+
return [trimmed];
|
|
30934
|
+
}
|
|
30935
|
+
return [trimmed, `${trimmed}.json`];
|
|
30936
|
+
}
|
|
30937
|
+
function normalizeWorkflowFileName(fileName) {
|
|
30938
|
+
const trimmed = fileName.trim();
|
|
30939
|
+
if (trimmed.length === 0) {
|
|
30940
|
+
throw new WorkflowInputError(
|
|
30941
|
+
"Filename cannot be empty.",
|
|
30942
|
+
"output_file_name"
|
|
30943
|
+
);
|
|
30944
|
+
}
|
|
30945
|
+
if (path.basename(trimmed) !== trimmed) {
|
|
30946
|
+
throw new WorkflowInputError(
|
|
30947
|
+
"Filename must not include directory separators.",
|
|
30948
|
+
"output_file_name"
|
|
30949
|
+
);
|
|
30950
|
+
}
|
|
30951
|
+
return trimmed.endsWith(".json") ? trimmed : `${trimmed}.json`;
|
|
30952
|
+
}
|
|
30953
|
+
function isConnectionValue(value) {
|
|
30954
|
+
return Array.isArray(value) && value.length >= 2 && (typeof value[0] === "string" || typeof value[0] === "number") && typeof value[1] === "number";
|
|
30955
|
+
}
|
|
30956
|
+
async function getObjectInfo() {
|
|
30957
|
+
return IS_MOCK ? MOCK_FIXTURES.object_info : await fetchComfyJson(
|
|
30958
|
+
"/object_info"
|
|
30959
|
+
);
|
|
30960
|
+
}
|
|
30961
|
+
async function resolveWorkflowReference(args) {
|
|
30962
|
+
if (args.workflow_file_path) {
|
|
30963
|
+
const targetPath = args.workflow_file_path;
|
|
30964
|
+
if (path.basename(targetPath) === targetPath) {
|
|
30965
|
+
const candidates = getWorkflowNameCandidates(targetPath);
|
|
30966
|
+
for (const dir of getWorkflowDirectories()) {
|
|
30967
|
+
for (const candidate of candidates) {
|
|
30968
|
+
const candidatePath = path.join(dir, candidate);
|
|
30969
|
+
try {
|
|
30970
|
+
const content = await fs.readFile(
|
|
30971
|
+
candidatePath,
|
|
30972
|
+
"utf8"
|
|
30973
|
+
);
|
|
30974
|
+
return {
|
|
30975
|
+
targetPath: candidatePath,
|
|
30976
|
+
content
|
|
30977
|
+
};
|
|
30978
|
+
} catch {
|
|
30979
|
+
}
|
|
31477
30980
|
}
|
|
30981
|
+
}
|
|
30982
|
+
throw new WorkflowInputError(
|
|
30983
|
+
`Workflow file not found: ${targetPath}`,
|
|
30984
|
+
"workflow_file_path",
|
|
30985
|
+
"not_found"
|
|
31478
30986
|
);
|
|
31479
30987
|
}
|
|
31480
|
-
const
|
|
31481
|
-
|
|
31482
|
-
|
|
31483
|
-
|
|
31484
|
-
nodes: Object.fromEntries(paginated.items),
|
|
31485
|
-
pagination: {
|
|
31486
|
-
total_count: paginated.total,
|
|
31487
|
-
limit: paginated.limit,
|
|
31488
|
-
offset: paginated.offset,
|
|
31489
|
-
has_more: paginated.hasMore
|
|
31490
|
-
}
|
|
30988
|
+
const validatedPath = await validateWorkflowPath(targetPath);
|
|
30989
|
+
return {
|
|
30990
|
+
targetPath: validatedPath,
|
|
30991
|
+
content: await fs.readFile(validatedPath, "utf8")
|
|
31491
30992
|
};
|
|
31492
|
-
|
|
31493
|
-
|
|
31494
|
-
|
|
31495
|
-
|
|
31496
|
-
|
|
31497
|
-
|
|
31498
|
-
|
|
31499
|
-
|
|
31500
|
-
|
|
31501
|
-
|
|
31502
|
-
|
|
31503
|
-
|
|
31504
|
-
|
|
31505
|
-
|
|
31506
|
-
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
|
|
31510
|
-
|
|
31511
|
-
|
|
31512
|
-
|
|
31513
|
-
|
|
31514
|
-
|
|
31515
|
-
|
|
31516
|
-
|
|
31517
|
-
|
|
30993
|
+
}
|
|
30994
|
+
if (args.workflow_id) {
|
|
30995
|
+
const requestedId = args.workflow_id.trim();
|
|
30996
|
+
if (requestedId.length === 0) {
|
|
30997
|
+
throw new WorkflowInputError(
|
|
30998
|
+
"Workflow ID cannot be empty.",
|
|
30999
|
+
"workflow_id"
|
|
31000
|
+
);
|
|
31001
|
+
}
|
|
31002
|
+
for (const dir of getWorkflowDirectories()) {
|
|
31003
|
+
try {
|
|
31004
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
31005
|
+
for (const entry of entries) {
|
|
31006
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
31007
|
+
continue;
|
|
31008
|
+
}
|
|
31009
|
+
const fullPath = path.join(dir, entry.name);
|
|
31010
|
+
const defaultId = getWorkflowId(fullPath);
|
|
31011
|
+
if (requestedId === defaultId || requestedId === entry.name.replace(/\.json$/, "")) {
|
|
31012
|
+
return {
|
|
31013
|
+
targetPath: fullPath,
|
|
31014
|
+
content: await fs.readFile(fullPath, "utf8")
|
|
31015
|
+
};
|
|
31016
|
+
}
|
|
31017
|
+
}
|
|
31018
|
+
} catch {
|
|
31019
|
+
}
|
|
31020
|
+
}
|
|
31021
|
+
throw new WorkflowInputError(
|
|
31022
|
+
`Workflow ID not found: ${args.workflow_id}`,
|
|
31023
|
+
"workflow_id",
|
|
31024
|
+
"not_found"
|
|
31025
|
+
);
|
|
31026
|
+
}
|
|
31027
|
+
throw new WorkflowInputError(
|
|
31028
|
+
"Must provide either workflow_file_path or workflow_id.",
|
|
31029
|
+
"workflow_id"
|
|
31030
|
+
);
|
|
31031
|
+
}
|
|
31032
|
+
async function normalizeWorkflowPrompt(workflow) {
|
|
31033
|
+
if (!isRecord(workflow)) {
|
|
31034
|
+
throw new WorkflowInputError(
|
|
31035
|
+
"Workflow must be a JSON object.",
|
|
31036
|
+
"workflow"
|
|
31037
|
+
);
|
|
31038
|
+
}
|
|
31039
|
+
let sourceFormat = "api";
|
|
31040
|
+
let normalizedWorkflow = workflow;
|
|
31041
|
+
if (isWebUIFormat(normalizedWorkflow)) {
|
|
31042
|
+
sourceFormat = "webui";
|
|
31043
|
+
normalizedWorkflow = convertWebUIToAPI(
|
|
31044
|
+
normalizedWorkflow,
|
|
31045
|
+
await getObjectInfo()
|
|
31046
|
+
);
|
|
31047
|
+
}
|
|
31048
|
+
const prompt = isRecord(normalizedWorkflow.prompt) && normalizedWorkflow.prompt ? normalizedWorkflow.prompt : normalizedWorkflow;
|
|
31049
|
+
if (!isRecord(prompt)) {
|
|
31050
|
+
throw new WorkflowInputError(
|
|
31051
|
+
"Workflow prompt must be a JSON object.",
|
|
31052
|
+
"workflow"
|
|
31518
31053
|
);
|
|
31054
|
+
}
|
|
31055
|
+
return {
|
|
31056
|
+
prompt,
|
|
31057
|
+
sourceFormat
|
|
31058
|
+
};
|
|
31059
|
+
}
|
|
31060
|
+
async function loadWorkflow(args) {
|
|
31061
|
+
const { targetPath, content } = await resolveWorkflowReference(args);
|
|
31062
|
+
let parsed;
|
|
31063
|
+
try {
|
|
31064
|
+
parsed = JSON.parse(content);
|
|
31519
31065
|
} catch (error48) {
|
|
31520
|
-
|
|
31066
|
+
throw new WorkflowInputError(
|
|
31067
|
+
`Invalid JSON in ${targetPath}: ${error48 instanceof Error ? error48.message : String(error48)}`,
|
|
31068
|
+
args.workflow_file_path ? "workflow_file_path" : "workflow_id"
|
|
31069
|
+
);
|
|
31070
|
+
}
|
|
31071
|
+
const { prompt, sourceFormat } = await normalizeWorkflowPrompt(parsed);
|
|
31072
|
+
return {
|
|
31073
|
+
targetPath,
|
|
31074
|
+
prompt,
|
|
31075
|
+
sourceFormat
|
|
31076
|
+
};
|
|
31077
|
+
}
|
|
31078
|
+
function inferEditableRole(nodeId, classType, inputName, consumerMap) {
|
|
31079
|
+
if (classType === "CLIPTextEncode" && inputName === "text") {
|
|
31080
|
+
const consumers = consumerMap.get(nodeId) ?? [];
|
|
31081
|
+
if (consumers.some((consumer) => consumer.inputName === "positive")) {
|
|
31082
|
+
return "positive_prompt";
|
|
31083
|
+
}
|
|
31084
|
+
if (consumers.some((consumer) => consumer.inputName === "negative")) {
|
|
31085
|
+
return "negative_prompt";
|
|
31086
|
+
}
|
|
31087
|
+
return "prompt_text";
|
|
31088
|
+
}
|
|
31089
|
+
switch (inputName) {
|
|
31090
|
+
case "seed":
|
|
31091
|
+
return "seed";
|
|
31092
|
+
case "steps":
|
|
31093
|
+
return "steps";
|
|
31094
|
+
case "cfg":
|
|
31095
|
+
return "cfg";
|
|
31096
|
+
case "sampler_name":
|
|
31097
|
+
return "sampler";
|
|
31098
|
+
case "scheduler":
|
|
31099
|
+
return "scheduler";
|
|
31100
|
+
case "denoise":
|
|
31101
|
+
return "denoise";
|
|
31102
|
+
case "width":
|
|
31103
|
+
return "width";
|
|
31104
|
+
case "height":
|
|
31105
|
+
return "height";
|
|
31106
|
+
case "batch_size":
|
|
31107
|
+
return "batch_size";
|
|
31108
|
+
case "ckpt_name":
|
|
31109
|
+
return "checkpoint";
|
|
31110
|
+
case "filename_prefix":
|
|
31111
|
+
return "filename_prefix";
|
|
31112
|
+
case "text":
|
|
31113
|
+
return "text";
|
|
31114
|
+
default:
|
|
31115
|
+
return void 0;
|
|
31521
31116
|
}
|
|
31522
31117
|
}
|
|
31523
|
-
|
|
31118
|
+
function formatValuePreview(value) {
|
|
31119
|
+
if (typeof value === "string") {
|
|
31120
|
+
return value.length > 100 ? `${value.slice(0, 97)}...` : value;
|
|
31121
|
+
}
|
|
31524
31122
|
try {
|
|
31525
|
-
const
|
|
31526
|
-
|
|
31527
|
-
|
|
31528
|
-
|
|
31529
|
-
|
|
31530
|
-
|
|
31531
|
-
|
|
31532
|
-
|
|
31533
|
-
|
|
31534
|
-
|
|
31535
|
-
|
|
31536
|
-
|
|
31123
|
+
const serialized = JSON.stringify(value);
|
|
31124
|
+
if (!serialized) {
|
|
31125
|
+
return String(value);
|
|
31126
|
+
}
|
|
31127
|
+
return serialized.length > 100 ? `${serialized.slice(0, 97)}...` : serialized;
|
|
31128
|
+
} catch {
|
|
31129
|
+
return String(value);
|
|
31130
|
+
}
|
|
31131
|
+
}
|
|
31132
|
+
function analyzeEditableInputs(prompt) {
|
|
31133
|
+
const consumerMap = /* @__PURE__ */ new Map();
|
|
31134
|
+
for (const [nodeId, rawNode] of Object.entries(prompt)) {
|
|
31135
|
+
if (!isRecord(rawNode) || !isRecord(rawNode.inputs)) {
|
|
31136
|
+
continue;
|
|
31137
|
+
}
|
|
31138
|
+
const classType = typeof rawNode.class_type === "string" ? rawNode.class_type : "";
|
|
31139
|
+
for (const [inputName, value] of Object.entries(rawNode.inputs)) {
|
|
31140
|
+
if (!isConnectionValue(value)) {
|
|
31141
|
+
continue;
|
|
31142
|
+
}
|
|
31143
|
+
const sourceNodeId = String(value[0]);
|
|
31144
|
+
const consumers = consumerMap.get(sourceNodeId) ?? [];
|
|
31145
|
+
consumers.push({
|
|
31146
|
+
inputName,
|
|
31147
|
+
classType,
|
|
31148
|
+
nodeId
|
|
31149
|
+
});
|
|
31150
|
+
consumerMap.set(sourceNodeId, consumers);
|
|
31151
|
+
}
|
|
31152
|
+
}
|
|
31153
|
+
const editableInputs = [];
|
|
31154
|
+
for (const [nodeId, rawNode] of Object.entries(prompt)) {
|
|
31155
|
+
if (!isRecord(rawNode) || !isRecord(rawNode.inputs)) {
|
|
31156
|
+
continue;
|
|
31157
|
+
}
|
|
31158
|
+
const classType = typeof rawNode.class_type === "string" ? rawNode.class_type : "";
|
|
31159
|
+
for (const [inputName, value] of Object.entries(rawNode.inputs)) {
|
|
31160
|
+
if (isConnectionValue(value)) {
|
|
31161
|
+
continue;
|
|
31162
|
+
}
|
|
31163
|
+
editableInputs.push({
|
|
31164
|
+
path: `${nodeId}.inputs.${inputName}`,
|
|
31165
|
+
node_id: nodeId,
|
|
31166
|
+
class_type: classType,
|
|
31167
|
+
input_name: inputName,
|
|
31168
|
+
value,
|
|
31169
|
+
value_preview: formatValuePreview(value),
|
|
31170
|
+
role: inferEditableRole(
|
|
31171
|
+
nodeId,
|
|
31172
|
+
classType,
|
|
31173
|
+
inputName,
|
|
31174
|
+
consumerMap
|
|
31175
|
+
)
|
|
31537
31176
|
});
|
|
31538
|
-
const awaitedContent = {
|
|
31539
|
-
...structuredContent,
|
|
31540
|
-
status: waitResult.status,
|
|
31541
|
-
completed: waitResult.completed,
|
|
31542
|
-
timed_out: waitResult.timed_out,
|
|
31543
|
-
elapsed_seconds: waitResult.elapsed_seconds,
|
|
31544
|
-
image_count: waitResult.image_count,
|
|
31545
|
-
images: waitResult.images,
|
|
31546
|
-
history_entry: waitResult.history_entry
|
|
31547
|
-
};
|
|
31548
|
-
return createAgentFacingResult(
|
|
31549
|
-
[
|
|
31550
|
-
summarizeWorkflowResponseText(awaitedContent),
|
|
31551
|
-
"",
|
|
31552
|
-
"## Await Result",
|
|
31553
|
-
summarizeWaitForWorkflowText(waitResult)
|
|
31554
|
-
].join("\n"),
|
|
31555
|
-
awaitedContent
|
|
31556
|
-
);
|
|
31557
31177
|
}
|
|
31558
|
-
return createAgentFacingResult(
|
|
31559
|
-
summarizeWorkflowResponseText(result),
|
|
31560
|
-
structuredContent
|
|
31561
|
-
);
|
|
31562
|
-
} catch (error48) {
|
|
31563
|
-
return handleComfyError(error48);
|
|
31564
31178
|
}
|
|
31179
|
+
return editableInputs.sort(
|
|
31180
|
+
(left, right) => left.path.localeCompare(right.path, void 0, { numeric: true })
|
|
31181
|
+
);
|
|
31565
31182
|
}
|
|
31566
|
-
|
|
31183
|
+
function formatEditableInputsText(editableInputs) {
|
|
31184
|
+
if (editableInputs.length === 0) {
|
|
31185
|
+
return "No literal editable inputs were detected.";
|
|
31186
|
+
}
|
|
31187
|
+
return formatListItems(editableInputs, (entry) => {
|
|
31188
|
+
const role = entry.role ? ` [${entry.role}]` : "";
|
|
31189
|
+
const classType = entry.class_type ? ` (${entry.class_type})` : "";
|
|
31190
|
+
return `${entry.path}${role}${classType}: ${entry.value_preview}`;
|
|
31191
|
+
});
|
|
31192
|
+
}
|
|
31193
|
+
function deriveSavedWorkflowFileName(sourcePath, requestedName) {
|
|
31194
|
+
if (requestedName) {
|
|
31195
|
+
return normalizeWorkflowFileName(requestedName);
|
|
31196
|
+
}
|
|
31197
|
+
if (sourcePath) {
|
|
31198
|
+
return normalizeWorkflowFileName(path.basename(sourcePath));
|
|
31199
|
+
}
|
|
31200
|
+
return `workflow-${Date.now()}.json`;
|
|
31201
|
+
}
|
|
31202
|
+
function applyOverrides(prompt, overrides) {
|
|
31203
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
31204
|
+
const parts = key.split(".");
|
|
31205
|
+
let current = prompt;
|
|
31206
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
31207
|
+
const part = parts[i];
|
|
31208
|
+
if (typeof current[part] !== "object" || current[part] === null) {
|
|
31209
|
+
current[part] = {};
|
|
31210
|
+
}
|
|
31211
|
+
current = current[part];
|
|
31212
|
+
}
|
|
31213
|
+
current[parts[parts.length - 1]] = value;
|
|
31214
|
+
}
|
|
31215
|
+
}
|
|
31216
|
+
var InspectNodeSchema = paginationSchema.extend({
|
|
31217
|
+
node_class: z3.string().optional().describe(
|
|
31218
|
+
"Specific node class to inspect. If omitted, lists all available nodes."
|
|
31219
|
+
),
|
|
31220
|
+
query: z3.string().optional().describe(
|
|
31221
|
+
"Search term to filter the list of available nodes by name or category."
|
|
31222
|
+
)
|
|
31223
|
+
});
|
|
31224
|
+
var DiscoverWorkflowsSchema = paginationSchema.extend({
|
|
31225
|
+
directory_path: z3.string().optional().describe("Custom directory path to scan for workflows.")
|
|
31226
|
+
});
|
|
31227
|
+
var WorkflowRunSchema = z3.object({
|
|
31228
|
+
workflow_file_path: z3.string().optional().describe(
|
|
31229
|
+
"Path or filename of workflow JSON. Scans default dirs if simple name provided."
|
|
31230
|
+
),
|
|
31231
|
+
workflow_id: z3.string().optional().describe("Workflow identifier returned by discover_workflows."),
|
|
31232
|
+
overrides: z3.record(z3.string(), z3.unknown()).optional().describe(
|
|
31233
|
+
'Key-value pairs to override using dot-notation. e.g. {"6.inputs.text": "dragon"}'
|
|
31234
|
+
),
|
|
31235
|
+
await: z3.boolean().default(true).describe("Wait for completion and return native images."),
|
|
31236
|
+
timeout: z3.number().int().min(1).default(120).describe("Wait timeout in seconds.")
|
|
31237
|
+
});
|
|
31238
|
+
var GetWorkflowSchema = z3.object({
|
|
31239
|
+
workflow_file_path: z3.string().optional().describe("Workflow path or filename to inspect."),
|
|
31240
|
+
workflow_id: z3.string().optional().describe("Workflow identifier returned by discovery."),
|
|
31241
|
+
include_prompt: z3.boolean().default(false).describe("Include normalized API JSON in structuredContent.")
|
|
31242
|
+
});
|
|
31243
|
+
var SaveWorkflowSchema = z3.object({
|
|
31244
|
+
workflow: z3.unknown().optional().describe("Workflow JSON object to save locally."),
|
|
31245
|
+
workflow_file_path: z3.string().optional().describe("Existing workflow path or filename to save."),
|
|
31246
|
+
workflow_id: z3.string().optional().describe("Existing workflow identifier to save."),
|
|
31247
|
+
overrides: z3.record(z3.string(), z3.unknown()).optional().describe("Dot-notation overrides to persist before saving."),
|
|
31248
|
+
output_file_name: z3.string().optional().describe("Filename to write under ./workflows."),
|
|
31249
|
+
overwrite: z3.boolean().default(false).describe("Replace an existing local file when true."),
|
|
31250
|
+
include_prompt: z3.boolean().default(false).describe("Include saved API JSON in structuredContent.")
|
|
31251
|
+
});
|
|
31252
|
+
var WaitForWorkflowSchema = z3.object({
|
|
31253
|
+
prompt_id: z3.string().describe("The prompt_id of the submitted workflow."),
|
|
31254
|
+
timeout: z3.number().int().min(1).default(120).describe("Wait timeout in seconds.")
|
|
31255
|
+
});
|
|
31256
|
+
async function handleInspectNode(args) {
|
|
31567
31257
|
try {
|
|
31568
|
-
const
|
|
31569
|
-
|
|
31258
|
+
const rawInfo = IS_MOCK ? MOCK_FIXTURES.object_info : await fetchComfyJson(
|
|
31259
|
+
"/object_info"
|
|
31570
31260
|
);
|
|
31571
|
-
|
|
31572
|
-
|
|
31573
|
-
|
|
31574
|
-
|
|
31575
|
-
|
|
31576
|
-
|
|
31577
|
-
|
|
31578
|
-
|
|
31261
|
+
if (args.node_class) {
|
|
31262
|
+
const node = rawInfo[args.node_class];
|
|
31263
|
+
if (!node)
|
|
31264
|
+
return createNotFoundError(`Node class ${args.node_class}`);
|
|
31265
|
+
return {
|
|
31266
|
+
content: [
|
|
31267
|
+
{ type: "text", text: JSON.stringify(node, null, 2) }
|
|
31268
|
+
],
|
|
31269
|
+
structuredContent: node
|
|
31270
|
+
};
|
|
31271
|
+
}
|
|
31272
|
+
let classes = Object.keys(rawInfo);
|
|
31273
|
+
if (args.query) {
|
|
31274
|
+
const query = args.query.toLowerCase();
|
|
31275
|
+
classes = classes.filter((c) => {
|
|
31276
|
+
const node = rawInfo[c];
|
|
31277
|
+
const searchStr = `${c} ${node?.display_name || ""} ${node?.category || ""}`.toLowerCase();
|
|
31278
|
+
return searchStr.includes(query);
|
|
31279
|
+
});
|
|
31280
|
+
}
|
|
31281
|
+
const paginated = applyPagination(classes, args);
|
|
31282
|
+
const resultNodes = {};
|
|
31283
|
+
for (const c of paginated.items) {
|
|
31284
|
+
resultNodes[c] = rawInfo[c];
|
|
31285
|
+
}
|
|
31286
|
+
const text = `Available nodes (${paginated.total} total):
|
|
31287
|
+
` + paginated.items.join(", ") + (paginated.hasMore ? `
|
|
31288
|
+
|
|
31289
|
+
${formatPaginationFooter(
|
|
31290
|
+
paginated.offset,
|
|
31291
|
+
paginated.limit,
|
|
31292
|
+
paginated.total
|
|
31293
|
+
)}` : "");
|
|
31294
|
+
return {
|
|
31295
|
+
content: [{ type: "text", text }],
|
|
31296
|
+
structuredContent: {
|
|
31297
|
+
nodes: resultNodes,
|
|
31298
|
+
pagination: {
|
|
31299
|
+
total: paginated.total,
|
|
31300
|
+
offset: paginated.offset,
|
|
31301
|
+
limit: paginated.limit,
|
|
31302
|
+
has_more: paginated.hasMore
|
|
31303
|
+
}
|
|
31579
31304
|
}
|
|
31580
31305
|
};
|
|
31581
|
-
return createAgentFacingResult(
|
|
31582
|
-
summarizeWorkflowListText(
|
|
31583
|
-
"# ComfyUI Stored Workflows",
|
|
31584
|
-
structuredContent.workflows,
|
|
31585
|
-
structuredContent.pagination
|
|
31586
|
-
),
|
|
31587
|
-
structuredContent
|
|
31588
|
-
);
|
|
31589
31306
|
} catch (error48) {
|
|
31590
31307
|
return handleComfyError(error48);
|
|
31591
31308
|
}
|
|
31592
31309
|
}
|
|
31593
|
-
|
|
31594
|
-
|
|
31595
|
-
|
|
31596
|
-
|
|
31597
|
-
|
|
31598
|
-
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31605
|
-
|
|
31310
|
+
function getWorkflowId(absolutePath) {
|
|
31311
|
+
return crypto.createHash("sha256").update(absolutePath).digest("hex").substring(0, 8);
|
|
31312
|
+
}
|
|
31313
|
+
async function handleDiscoverWorkflows(args) {
|
|
31314
|
+
const pathsToScan = [
|
|
31315
|
+
path.join(
|
|
31316
|
+
homedir(),
|
|
31317
|
+
"comfy",
|
|
31318
|
+
"ComfyUI",
|
|
31319
|
+
"user",
|
|
31320
|
+
"default",
|
|
31321
|
+
"workflows"
|
|
31322
|
+
),
|
|
31323
|
+
path.join(homedir(), ".fre4x-comfyui", "workflows"),
|
|
31324
|
+
path.join(process.cwd(), "workflows")
|
|
31325
|
+
];
|
|
31326
|
+
if (args.directory_path) pathsToScan.push(args.directory_path);
|
|
31327
|
+
const foundWorkflows = [];
|
|
31328
|
+
for (const dir of pathsToScan) {
|
|
31329
|
+
try {
|
|
31330
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
31331
|
+
for (const entry of entries) {
|
|
31332
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
31333
|
+
const fullPath = path.join(dir, entry.name);
|
|
31334
|
+
const defaultId = getWorkflowId(fullPath);
|
|
31335
|
+
const defaultName = entry.name.replace(/\.json$/, "");
|
|
31336
|
+
try {
|
|
31337
|
+
const content = await fs.readFile(fullPath, "utf8");
|
|
31338
|
+
const json2 = JSON.parse(content);
|
|
31339
|
+
const id = defaultId;
|
|
31340
|
+
let name = defaultName;
|
|
31341
|
+
let isWebUI = false;
|
|
31342
|
+
if (typeof json2 === "object" && json2 !== null) {
|
|
31343
|
+
if (Array.isArray(json2.nodes) && Array.isArray(json2.links)) {
|
|
31344
|
+
isWebUI = true;
|
|
31345
|
+
}
|
|
31346
|
+
if (typeof json2.name === "string") name = json2.name;
|
|
31347
|
+
else if (json2.workflow && typeof json2.workflow.name === "string")
|
|
31348
|
+
name = json2.workflow.name;
|
|
31349
|
+
}
|
|
31350
|
+
foundWorkflows.push({
|
|
31351
|
+
id,
|
|
31352
|
+
name,
|
|
31353
|
+
path: fullPath,
|
|
31354
|
+
isWebUI
|
|
31355
|
+
});
|
|
31356
|
+
} catch (e) {
|
|
31357
|
+
foundWorkflows.push({
|
|
31358
|
+
id: defaultId,
|
|
31359
|
+
name: defaultName,
|
|
31360
|
+
path: fullPath,
|
|
31361
|
+
error: String(e)
|
|
31362
|
+
});
|
|
31363
|
+
}
|
|
31364
|
+
}
|
|
31365
|
+
}
|
|
31366
|
+
} catch (_e) {
|
|
31367
|
+
}
|
|
31368
|
+
}
|
|
31369
|
+
if (foundWorkflows.length === 0) {
|
|
31370
|
+
return {
|
|
31371
|
+
content: [
|
|
31372
|
+
{
|
|
31373
|
+
type: "text",
|
|
31374
|
+
text: `No workflows found in searched directories.`
|
|
31375
|
+
}
|
|
31376
|
+
],
|
|
31377
|
+
structuredContent: {
|
|
31378
|
+
workflows: [],
|
|
31379
|
+
searched_paths: pathsToScan
|
|
31380
|
+
}
|
|
31381
|
+
};
|
|
31382
|
+
}
|
|
31383
|
+
const paginated = applyPagination(foundWorkflows, args);
|
|
31384
|
+
const text = `Found ${paginated.total} workflow files:
|
|
31385
|
+
` + formatListItems(paginated.items, (w) => {
|
|
31386
|
+
const formatTag = w.isWebUI ? "\u{1F504} Web UI (Auto-converted on run)" : "\u2705 API Format";
|
|
31387
|
+
let line = `[ID: ${w.id}] [${formatTag}] Name: "${w.name}"`;
|
|
31388
|
+
if (w.error) line += ` (Error: ${w.error})`;
|
|
31389
|
+
return line;
|
|
31390
|
+
}) + (paginated.hasMore ? `
|
|
31391
|
+
|
|
31392
|
+
${formatPaginationFooter(
|
|
31393
|
+
paginated.offset,
|
|
31394
|
+
paginated.limit,
|
|
31395
|
+
paginated.total
|
|
31396
|
+
)}` : "");
|
|
31397
|
+
return {
|
|
31398
|
+
content: [{ type: "text", text }],
|
|
31399
|
+
structuredContent: {
|
|
31606
31400
|
workflows: paginated.items,
|
|
31607
31401
|
pagination: {
|
|
31608
|
-
|
|
31609
|
-
limit: paginated.limit,
|
|
31402
|
+
total: paginated.total,
|
|
31610
31403
|
offset: paginated.offset,
|
|
31404
|
+
limit: paginated.limit,
|
|
31611
31405
|
has_more: paginated.hasMore
|
|
31612
31406
|
}
|
|
31613
|
-
}
|
|
31614
|
-
|
|
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
|
-
}
|
|
31407
|
+
}
|
|
31408
|
+
};
|
|
31627
31409
|
}
|
|
31628
|
-
async function
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
|
|
31410
|
+
async function waitForWorkflowResult(prompt_id, timeout_seconds) {
|
|
31411
|
+
const start = Date.now();
|
|
31412
|
+
const end = start + timeout_seconds * 1e3;
|
|
31413
|
+
while (Date.now() < end) {
|
|
31414
|
+
const history = IS_MOCK ? MOCK_FIXTURES.history : await fetchComfyJson(
|
|
31415
|
+
`/history/${prompt_id}`
|
|
31416
|
+
).catch(() => ({}));
|
|
31417
|
+
if (history?.[prompt_id]) {
|
|
31418
|
+
return history[prompt_id];
|
|
31633
31419
|
}
|
|
31634
|
-
|
|
31635
|
-
summarizeStoredWorkflowText(workflow),
|
|
31636
|
-
workflow
|
|
31637
|
-
);
|
|
31638
|
-
} catch (error48) {
|
|
31639
|
-
return handleComfyError(error48);
|
|
31420
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
31640
31421
|
}
|
|
31422
|
+
throw new Error(`Timeout waiting for workflow ${prompt_id}`);
|
|
31641
31423
|
}
|
|
31642
|
-
async function
|
|
31424
|
+
async function handleWaitForWorkflow(args) {
|
|
31643
31425
|
try {
|
|
31644
|
-
const
|
|
31645
|
-
|
|
31646
|
-
|
|
31647
|
-
return createValidationError(
|
|
31648
|
-
"workflow_id",
|
|
31649
|
-
`Workflow ${workflowId} already exists. Pass overwrite=true to replace it.`
|
|
31650
|
-
);
|
|
31651
|
-
}
|
|
31652
|
-
const workflow = await createStoredWorkflowRecord({
|
|
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
|
|
31426
|
+
const historyEntry = await waitForWorkflowResult(
|
|
31427
|
+
args.prompt_id,
|
|
31428
|
+
args.timeout
|
|
31665
31429
|
);
|
|
31430
|
+
const outputsRecord = historyEntry.outputs || {};
|
|
31431
|
+
const content = [];
|
|
31432
|
+
let summaryText = `Workflow ${args.prompt_id} completed.
|
|
31433
|
+
`;
|
|
31434
|
+
for (const [, output] of Object.entries(outputsRecord)) {
|
|
31435
|
+
if (output && Array.isArray(output.images)) {
|
|
31436
|
+
for (const image of output.images) {
|
|
31437
|
+
if (image.filename && image.type) {
|
|
31438
|
+
if (!IS_MOCK) {
|
|
31439
|
+
try {
|
|
31440
|
+
const base643 = await fetchImageBase64(
|
|
31441
|
+
image.filename,
|
|
31442
|
+
image.type,
|
|
31443
|
+
image.subfolder
|
|
31444
|
+
);
|
|
31445
|
+
content.push({
|
|
31446
|
+
type: "image",
|
|
31447
|
+
data: base643.data,
|
|
31448
|
+
mimeType: base643.mimeType
|
|
31449
|
+
});
|
|
31450
|
+
summaryText += `Fetched image: ${image.filename}
|
|
31451
|
+
`;
|
|
31452
|
+
} catch (e) {
|
|
31453
|
+
summaryText += `Failed to fetch image ${image.filename}: ${String(e)}
|
|
31454
|
+
`;
|
|
31455
|
+
}
|
|
31456
|
+
} else {
|
|
31457
|
+
summaryText += `Mock image: ${image.filename}
|
|
31458
|
+
`;
|
|
31459
|
+
}
|
|
31460
|
+
}
|
|
31461
|
+
}
|
|
31462
|
+
}
|
|
31463
|
+
}
|
|
31464
|
+
content.unshift({ type: "text", text: summaryText });
|
|
31465
|
+
return {
|
|
31466
|
+
content,
|
|
31467
|
+
structuredContent: historyEntry
|
|
31468
|
+
};
|
|
31666
31469
|
} catch (error48) {
|
|
31667
31470
|
return handleComfyError(error48);
|
|
31668
31471
|
}
|
|
31669
31472
|
}
|
|
31670
|
-
async function
|
|
31473
|
+
async function handleGetWorkflow(args) {
|
|
31671
31474
|
try {
|
|
31672
|
-
const
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
|
|
31676
|
-
|
|
31677
|
-
|
|
31678
|
-
|
|
31679
|
-
|
|
31680
|
-
);
|
|
31681
|
-
const submission = await submitWorkflow({
|
|
31682
|
-
prompt,
|
|
31683
|
-
client_id: args.client_id
|
|
31684
|
-
});
|
|
31475
|
+
const loadedWorkflow = await loadWorkflow(args);
|
|
31476
|
+
const editableInputs = analyzeEditableInputs(loadedWorkflow.prompt);
|
|
31477
|
+
const workflowId = getWorkflowId(loadedWorkflow.targetPath);
|
|
31478
|
+
const text = [
|
|
31479
|
+
`Workflow ${workflowId} loaded from ${loadedWorkflow.targetPath}.`,
|
|
31480
|
+
loadedWorkflow.sourceFormat === "webui" ? "Source format: Web UI (normalized to API format)." : "Source format: API format.",
|
|
31481
|
+
`Editable inputs (${editableInputs.length}):`,
|
|
31482
|
+
formatEditableInputsText(editableInputs)
|
|
31483
|
+
].join("\n");
|
|
31685
31484
|
const structuredContent = {
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
|
|
31689
|
-
|
|
31690
|
-
workflow_name: workflow.workflow.name,
|
|
31691
|
-
applied_overrides: args.overrides
|
|
31485
|
+
workflow_id: workflowId,
|
|
31486
|
+
path: loadedWorkflow.targetPath,
|
|
31487
|
+
source_format: loadedWorkflow.sourceFormat,
|
|
31488
|
+
editable_inputs: editableInputs
|
|
31692
31489
|
};
|
|
31693
|
-
if (args.
|
|
31694
|
-
|
|
31695
|
-
prompt_id: submission.prompt_id,
|
|
31696
|
-
timeout_seconds: args.timeout,
|
|
31697
|
-
poll_interval_ms: 1e3,
|
|
31698
|
-
require_images: args.require_images
|
|
31699
|
-
});
|
|
31700
|
-
Object.assign(structuredContent, {
|
|
31701
|
-
status: waitResult.status,
|
|
31702
|
-
completed: waitResult.completed,
|
|
31703
|
-
timed_out: waitResult.timed_out,
|
|
31704
|
-
elapsed_seconds: waitResult.elapsed_seconds,
|
|
31705
|
-
image_count: waitResult.image_count,
|
|
31706
|
-
images: waitResult.images,
|
|
31707
|
-
history_entry: waitResult.history_entry
|
|
31708
|
-
});
|
|
31709
|
-
return createAgentFacingResult(
|
|
31710
|
-
[
|
|
31711
|
-
"# ComfyUI Stored Workflow Run",
|
|
31712
|
-
"",
|
|
31713
|
-
"## Summary",
|
|
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
|
-
);
|
|
31490
|
+
if (args.include_prompt) {
|
|
31491
|
+
structuredContent.prompt = loadedWorkflow.prompt;
|
|
31731
31492
|
}
|
|
31732
|
-
return
|
|
31733
|
-
[
|
|
31734
|
-
"# ComfyUI Stored Workflow Run",
|
|
31735
|
-
"",
|
|
31736
|
-
"## Summary",
|
|
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"),
|
|
31493
|
+
return {
|
|
31494
|
+
content: [{ type: "text", text }],
|
|
31749
31495
|
structuredContent
|
|
31750
|
-
|
|
31496
|
+
};
|
|
31751
31497
|
} catch (error48) {
|
|
31752
31498
|
return handleComfyError(error48);
|
|
31753
31499
|
}
|
|
31754
31500
|
}
|
|
31755
|
-
async function
|
|
31501
|
+
async function handleSaveWorkflow(args) {
|
|
31756
31502
|
try {
|
|
31757
|
-
|
|
31758
|
-
|
|
31759
|
-
)
|
|
31760
|
-
|
|
31761
|
-
|
|
31762
|
-
|
|
31503
|
+
let prompt;
|
|
31504
|
+
let sourcePath;
|
|
31505
|
+
if (args.workflow !== void 0) {
|
|
31506
|
+
const normalized = await normalizeWorkflowPrompt(args.workflow);
|
|
31507
|
+
prompt = normalized.prompt;
|
|
31508
|
+
} else {
|
|
31509
|
+
if (!args.workflow_file_path && !args.workflow_id) {
|
|
31510
|
+
return createValidationError(
|
|
31511
|
+
"workflow",
|
|
31512
|
+
"Provide workflow JSON or an existing workflow reference."
|
|
31513
|
+
);
|
|
31763
31514
|
}
|
|
31764
|
-
|
|
31765
|
-
|
|
31766
|
-
|
|
31767
|
-
|
|
31768
|
-
|
|
31515
|
+
const loadedWorkflow = await loadWorkflow(args);
|
|
31516
|
+
prompt = loadedWorkflow.prompt;
|
|
31517
|
+
sourcePath = loadedWorkflow.targetPath;
|
|
31518
|
+
}
|
|
31519
|
+
if (args.overrides) {
|
|
31520
|
+
applyOverrides(prompt, args.overrides);
|
|
31521
|
+
}
|
|
31522
|
+
const workflowsDir = path.join(process.cwd(), "workflows");
|
|
31523
|
+
await fs.mkdir(workflowsDir, { recursive: true });
|
|
31524
|
+
const outputFileName = deriveSavedWorkflowFileName(
|
|
31525
|
+
sourcePath,
|
|
31526
|
+
args.output_file_name
|
|
31527
|
+
);
|
|
31528
|
+
const outputPath = path.join(workflowsDir, outputFileName);
|
|
31529
|
+
let existedBeforeSave = false;
|
|
31530
|
+
try {
|
|
31531
|
+
await fs.access(outputPath);
|
|
31532
|
+
existedBeforeSave = true;
|
|
31533
|
+
} catch {
|
|
31534
|
+
existedBeforeSave = false;
|
|
31535
|
+
}
|
|
31536
|
+
if (existedBeforeSave && !args.overwrite) {
|
|
31537
|
+
return createValidationError(
|
|
31538
|
+
"output_file_name",
|
|
31539
|
+
`File already exists: ${outputFileName}. Set overwrite=true to replace it.`
|
|
31769
31540
|
);
|
|
31770
31541
|
}
|
|
31771
|
-
|
|
31772
|
-
|
|
31773
|
-
|
|
31542
|
+
await fs.writeFile(
|
|
31543
|
+
outputPath,
|
|
31544
|
+
`${JSON.stringify(prompt, null, 2)}
|
|
31545
|
+
`,
|
|
31546
|
+
"utf8"
|
|
31547
|
+
);
|
|
31548
|
+
const editableInputs = analyzeEditableInputs(prompt);
|
|
31549
|
+
const workflowId = getWorkflowId(outputPath);
|
|
31774
31550
|
const structuredContent = {
|
|
31775
|
-
|
|
31776
|
-
|
|
31777
|
-
|
|
31778
|
-
|
|
31779
|
-
|
|
31780
|
-
has_more: paginated.hasMore
|
|
31781
|
-
}
|
|
31551
|
+
workflow_id: workflowId,
|
|
31552
|
+
path: outputPath,
|
|
31553
|
+
source_path: sourcePath,
|
|
31554
|
+
overwritten: existedBeforeSave,
|
|
31555
|
+
editable_inputs: editableInputs
|
|
31782
31556
|
};
|
|
31783
|
-
|
|
31784
|
-
|
|
31785
|
-
|
|
31786
|
-
|
|
31787
|
-
|
|
31788
|
-
|
|
31789
|
-
|
|
31790
|
-
|
|
31791
|
-
|
|
31792
|
-
|
|
31793
|
-
|
|
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),
|
|
31557
|
+
if (args.include_prompt) {
|
|
31558
|
+
structuredContent.prompt = prompt;
|
|
31559
|
+
}
|
|
31560
|
+
const persistedChanges = args.overrides && Object.keys(args.overrides).length > 0 ? `Persisted ${Object.keys(args.overrides).length} override(s).` : "No overrides were applied.";
|
|
31561
|
+
const text = [
|
|
31562
|
+
`Saved workflow ${workflowId} to ${outputPath}.`,
|
|
31563
|
+
persistedChanges,
|
|
31564
|
+
`Editable inputs (${editableInputs.length}):`,
|
|
31565
|
+
formatEditableInputsText(editableInputs)
|
|
31566
|
+
].join("\n");
|
|
31567
|
+
return {
|
|
31568
|
+
content: [{ type: "text", text }],
|
|
31818
31569
|
structuredContent
|
|
31819
|
-
|
|
31570
|
+
};
|
|
31820
31571
|
} catch (error48) {
|
|
31821
31572
|
return handleComfyError(error48);
|
|
31822
31573
|
}
|
|
31823
31574
|
}
|
|
31824
|
-
async function
|
|
31575
|
+
async function handleWorkflowRun(args) {
|
|
31825
31576
|
try {
|
|
31826
|
-
const
|
|
31827
|
-
const
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31577
|
+
const { prompt } = await loadWorkflow(args);
|
|
31578
|
+
const promptObj = { ...prompt };
|
|
31579
|
+
if (args.overrides) {
|
|
31580
|
+
applyOverrides(promptObj, args.overrides);
|
|
31581
|
+
}
|
|
31582
|
+
const clientId = `mcp-${Date.now()}`;
|
|
31583
|
+
const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson("/prompt", {
|
|
31584
|
+
method: "POST",
|
|
31585
|
+
body: JSON.stringify({
|
|
31586
|
+
prompt: promptObj,
|
|
31587
|
+
client_id: clientId
|
|
31588
|
+
})
|
|
31589
|
+
});
|
|
31590
|
+
if (result.node_errors && Object.keys(result.node_errors).length > 0) {
|
|
31591
|
+
return {
|
|
31592
|
+
content: [
|
|
31593
|
+
{
|
|
31594
|
+
type: "text",
|
|
31595
|
+
text: formatWorkflowNodeErrors(result.node_errors)
|
|
31596
|
+
}
|
|
31597
|
+
],
|
|
31598
|
+
structuredContent: result,
|
|
31599
|
+
isError: true
|
|
31600
|
+
};
|
|
31601
|
+
}
|
|
31602
|
+
if (args.await) {
|
|
31603
|
+
return await handleWaitForWorkflow({
|
|
31604
|
+
prompt_id: result.prompt_id,
|
|
31605
|
+
timeout: args.timeout
|
|
31606
|
+
});
|
|
31607
|
+
}
|
|
31608
|
+
return {
|
|
31609
|
+
content: [
|
|
31610
|
+
{
|
|
31611
|
+
type: "text",
|
|
31612
|
+
text: `Workflow submitted. prompt_id: ${result.prompt_id}, queue: ${result.number}`
|
|
31613
|
+
}
|
|
31614
|
+
],
|
|
31615
|
+
structuredContent: result
|
|
31831
31616
|
};
|
|
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
31617
|
} catch (error48) {
|
|
31848
31618
|
return handleComfyError(error48);
|
|
31849
31619
|
}
|
|
31850
31620
|
}
|
|
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
31621
|
function createServer() {
|
|
31872
31622
|
const server = new McpServer({
|
|
31873
31623
|
name: "@fre4x/comfyui",
|
|
31874
31624
|
version: PACKAGE_VERSION
|
|
31875
31625
|
});
|
|
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
|
|
31626
|
+
server.tool(
|
|
31627
|
+
"comfyui_inspect_node",
|
|
31628
|
+
"Inspect a ComfyUI node or list all nodes.",
|
|
31629
|
+
InspectNodeSchema.shape,
|
|
31630
|
+
handleInspectNode
|
|
31890
31631
|
);
|
|
31891
|
-
server.
|
|
31892
|
-
"
|
|
31893
|
-
|
|
31894
|
-
|
|
31895
|
-
|
|
31896
|
-
inputSchema: ListObjectInfoInputSchema,
|
|
31897
|
-
outputSchema: ObjectInfoSchema,
|
|
31898
|
-
annotations: {
|
|
31899
|
-
readOnlyHint: true,
|
|
31900
|
-
idempotentHint: true,
|
|
31901
|
-
openWorldHint: true
|
|
31902
|
-
}
|
|
31903
|
-
},
|
|
31904
|
-
handleListObjectInfo
|
|
31632
|
+
server.tool(
|
|
31633
|
+
"comfyui_discover_workflows",
|
|
31634
|
+
"Scan standard directories for .json workflow files.",
|
|
31635
|
+
DiscoverWorkflowsSchema.shape,
|
|
31636
|
+
handleDiscoverWorkflows
|
|
31905
31637
|
);
|
|
31906
|
-
server.
|
|
31907
|
-
"
|
|
31908
|
-
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
inputSchema: ExecuteWorkflowInputSchema,
|
|
31912
|
-
outputSchema: WorkflowResponseSchema,
|
|
31913
|
-
annotations: {
|
|
31914
|
-
openWorldHint: true
|
|
31915
|
-
}
|
|
31916
|
-
},
|
|
31917
|
-
handleExecuteWorkflow
|
|
31918
|
-
);
|
|
31919
|
-
server.registerTool(
|
|
31920
|
-
"comfyui_list_workflows",
|
|
31921
|
-
{
|
|
31922
|
-
title: "List Stored Workflows",
|
|
31923
|
-
description: "List stored ComfyUI workflows with pagination.",
|
|
31924
|
-
inputSchema: ListWorkflowsInputSchema,
|
|
31925
|
-
outputSchema: WorkflowListSchema,
|
|
31926
|
-
annotations: {
|
|
31927
|
-
readOnlyHint: true,
|
|
31928
|
-
idempotentHint: true,
|
|
31929
|
-
openWorldHint: true
|
|
31930
|
-
}
|
|
31931
|
-
},
|
|
31932
|
-
handleListWorkflows
|
|
31933
|
-
);
|
|
31934
|
-
server.registerTool(
|
|
31935
|
-
"comfyui_search_workflows",
|
|
31936
|
-
{
|
|
31937
|
-
title: "Search Stored Workflows",
|
|
31938
|
-
description: "Search stored ComfyUI workflows by name, tags, or description.",
|
|
31939
|
-
inputSchema: SearchWorkflowsInputSchema,
|
|
31940
|
-
outputSchema: WorkflowListSchema,
|
|
31941
|
-
annotations: {
|
|
31942
|
-
readOnlyHint: true,
|
|
31943
|
-
idempotentHint: true,
|
|
31944
|
-
openWorldHint: true
|
|
31945
|
-
}
|
|
31946
|
-
},
|
|
31947
|
-
handleSearchWorkflows
|
|
31638
|
+
server.tool(
|
|
31639
|
+
"comfyui_workflow_run",
|
|
31640
|
+
"Run a workflow from a file path or ID, applying overrides, and optionally returning images.",
|
|
31641
|
+
WorkflowRunSchema.shape,
|
|
31642
|
+
handleWorkflowRun
|
|
31948
31643
|
);
|
|
31949
|
-
server.
|
|
31644
|
+
server.tool(
|
|
31950
31645
|
"comfyui_get_workflow",
|
|
31951
|
-
|
|
31952
|
-
|
|
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
|
-
},
|
|
31646
|
+
"Inspect a stored workflow and list editable inputs.",
|
|
31647
|
+
GetWorkflowSchema.shape,
|
|
31962
31648
|
handleGetWorkflow
|
|
31963
31649
|
);
|
|
31964
|
-
server.
|
|
31650
|
+
server.tool(
|
|
31965
31651
|
"comfyui_save_workflow",
|
|
31966
|
-
|
|
31967
|
-
|
|
31968
|
-
description: "Save a ComfyUI workflow for later reuse.",
|
|
31969
|
-
inputSchema: SaveWorkflowInputSchema,
|
|
31970
|
-
outputSchema: StoredWorkflowSchema,
|
|
31971
|
-
annotations: {
|
|
31972
|
-
openWorldHint: true
|
|
31973
|
-
}
|
|
31974
|
-
},
|
|
31652
|
+
"Save a workflow into ./workflows with optional edits.",
|
|
31653
|
+
SaveWorkflowSchema.shape,
|
|
31975
31654
|
handleSaveWorkflow
|
|
31976
31655
|
);
|
|
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(
|
|
31656
|
+
server.tool(
|
|
32006
31657
|
"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
|
-
},
|
|
31658
|
+
"Wait for a submitted workflow to finish and fetch its images.",
|
|
31659
|
+
WaitForWorkflowSchema.shape,
|
|
32016
31660
|
handleWaitForWorkflow
|
|
32017
31661
|
);
|
|
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
31662
|
return server;
|
|
32049
31663
|
}
|
|
32050
31664
|
function isMainModule(url2) {
|