@fre4x/comfyui 1.0.58 → 1.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +38 -0
  2. package/dist/index.js +997 -129
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3223,8 +3223,8 @@ var require_utils = __commonJS({
3223
3223
  }
3224
3224
  return ind;
3225
3225
  }
3226
- function removeDotSegments(path) {
3227
- let input = path;
3226
+ function removeDotSegments(path2) {
3227
+ let input = path2;
3228
3228
  const output = [];
3229
3229
  let nextSlash = -1;
3230
3230
  let len = 0;
@@ -3423,8 +3423,8 @@ var require_schemes = __commonJS({
3423
3423
  wsComponent.secure = void 0;
3424
3424
  }
3425
3425
  if (wsComponent.resourceName) {
3426
- const [path, query] = wsComponent.resourceName.split("?");
3427
- wsComponent.path = path && path !== "/" ? path : void 0;
3426
+ const [path2, query] = wsComponent.resourceName.split("?");
3427
+ wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
3428
3428
  wsComponent.query = query;
3429
3429
  wsComponent.resourceName = void 0;
3430
3430
  }
@@ -6786,12 +6786,12 @@ var require_dist = __commonJS({
6786
6786
  throw new Error(`Unknown format "${name}"`);
6787
6787
  return f;
6788
6788
  };
6789
- function addFormats(ajv, list, fs, exportName) {
6789
+ function addFormats(ajv, list, fs2, exportName) {
6790
6790
  var _a2;
6791
6791
  var _b;
6792
6792
  (_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
6793
6793
  for (const f of list)
6794
- ajv.addFormat(f, fs[f]);
6794
+ ajv.addFormat(f, fs2[f]);
6795
6795
  }
6796
6796
  module.exports = exports = formatsPlugin;
6797
6797
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -6800,7 +6800,10 @@ var require_dist = __commonJS({
6800
6800
  });
6801
6801
 
6802
6802
  // src/index.ts
6803
+ import * as fs from "node:fs/promises";
6803
6804
  import { realpathSync } from "node:fs";
6805
+ import { homedir } from "node:os";
6806
+ import path from "node:path";
6804
6807
  import { fileURLToPath } from "node:url";
6805
6808
 
6806
6809
  // ../packages/shared/dist/errors.js
@@ -6873,16 +6876,6 @@ Suggestion: If the problem persists, check server logs.`
6873
6876
  };
6874
6877
  }
6875
6878
 
6876
- // ../packages/shared/dist/format.js
6877
- function formatField(label, value) {
6878
- if (value === null || value === void 0 || value === "")
6879
- return "";
6880
- return `**${label}:** ${value}`;
6881
- }
6882
- function formatFields(fields) {
6883
- return fields.map(([label, value]) => formatField(label, value)).filter(Boolean).join(" \n");
6884
- }
6885
-
6886
6879
  // ../node_modules/zod/index.js
6887
6880
  var zod_exports = {};
6888
6881
  __export(zod_exports, {
@@ -7893,10 +7886,10 @@ function mergeDefs(...defs) {
7893
7886
  function cloneDef(schema) {
7894
7887
  return mergeDefs(schema._zod.def);
7895
7888
  }
7896
- function getElementAtPath(obj, path) {
7897
- if (!path)
7889
+ function getElementAtPath(obj, path2) {
7890
+ if (!path2)
7898
7891
  return obj;
7899
- return path.reduce((acc, key) => acc?.[key], obj);
7892
+ return path2.reduce((acc, key) => acc?.[key], obj);
7900
7893
  }
7901
7894
  function promiseAllObject(promisesObj) {
7902
7895
  const keys = Object.keys(promisesObj);
@@ -8279,11 +8272,11 @@ function aborted(x, startIndex = 0) {
8279
8272
  }
8280
8273
  return false;
8281
8274
  }
8282
- function prefixIssues(path, issues) {
8275
+ function prefixIssues(path2, issues) {
8283
8276
  return issues.map((iss) => {
8284
8277
  var _a2;
8285
8278
  (_a2 = iss).path ?? (_a2.path = []);
8286
- iss.path.unshift(path);
8279
+ iss.path.unshift(path2);
8287
8280
  return iss;
8288
8281
  });
8289
8282
  }
@@ -8466,7 +8459,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
8466
8459
  }
8467
8460
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
8468
8461
  const result = { errors: [] };
8469
- const processError = (error49, path = []) => {
8462
+ const processError = (error49, path2 = []) => {
8470
8463
  var _a2, _b;
8471
8464
  for (const issue2 of error49.issues) {
8472
8465
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -8476,7 +8469,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
8476
8469
  } else if (issue2.code === "invalid_element") {
8477
8470
  processError({ issues: issue2.issues }, issue2.path);
8478
8471
  } else {
8479
- const fullpath = [...path, ...issue2.path];
8472
+ const fullpath = [...path2, ...issue2.path];
8480
8473
  if (fullpath.length === 0) {
8481
8474
  result.errors.push(mapper(issue2));
8482
8475
  continue;
@@ -8508,8 +8501,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
8508
8501
  }
8509
8502
  function toDotPath(_path) {
8510
8503
  const segs = [];
8511
- const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
8512
- for (const seg of path) {
8504
+ const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
8505
+ for (const seg of path2) {
8513
8506
  if (typeof seg === "number")
8514
8507
  segs.push(`[${seg}]`);
8515
8508
  else if (typeof seg === "symbol")
@@ -20486,13 +20479,13 @@ function resolveRef(ref, ctx) {
20486
20479
  if (!ref.startsWith("#")) {
20487
20480
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
20488
20481
  }
20489
- const path = ref.slice(1).split("/").filter(Boolean);
20490
- if (path.length === 0) {
20482
+ const path2 = ref.slice(1).split("/").filter(Boolean);
20483
+ if (path2.length === 0) {
20491
20484
  return ctx.rootSchema;
20492
20485
  }
20493
20486
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
20494
- if (path[0] === defsKey) {
20495
- const key = path[1];
20487
+ if (path2[0] === defsKey) {
20488
+ const key = path2[1];
20496
20489
  if (!key || !ctx.defs[key]) {
20497
20490
  throw new Error(`Reference not found: ${ref}`);
20498
20491
  }
@@ -21284,8 +21277,8 @@ function getErrorMap2() {
21284
21277
 
21285
21278
  // ../node_modules/zod/v3/helpers/parseUtil.js
21286
21279
  var makeIssue = (params) => {
21287
- const { data, path, errorMaps, issueData } = params;
21288
- const fullPath = [...path, ...issueData.path || []];
21280
+ const { data, path: path2, errorMaps, issueData } = params;
21281
+ const fullPath = [...path2, ...issueData.path || []];
21289
21282
  const fullIssue = {
21290
21283
  ...issueData,
21291
21284
  path: fullPath
@@ -21400,11 +21393,11 @@ var errorUtil;
21400
21393
 
21401
21394
  // ../node_modules/zod/v3/types.js
21402
21395
  var ParseInputLazyPath = class {
21403
- constructor(parent, value, path, key) {
21396
+ constructor(parent, value, path2, key) {
21404
21397
  this._cachedPath = [];
21405
21398
  this.parent = parent;
21406
21399
  this.data = value;
21407
- this._path = path;
21400
+ this._path = path2;
21408
21401
  this._key = key;
21409
21402
  }
21410
21403
  get path() {
@@ -30530,7 +30523,16 @@ var MOCK_FIXTURES = {
30530
30523
  ckpt_name: [
30531
30524
  [
30532
30525
  "v1-5-pruned-emaonly.ckpt",
30533
- "sd_xl_base_1.0.safetensors"
30526
+ "sd_xl_base_1.0.safetensors",
30527
+ "cyberrealisticPony_v160.safetensors",
30528
+ "harukiMIX_ponyV23.safetensors",
30529
+ "harukiMIX_ponyV40.safetensors",
30530
+ "ponyRealism_V22.safetensors",
30531
+ "ponyRealism_V23.safetensors",
30532
+ "ponyDiffusion_V6XL.safetensors",
30533
+ "realvisxlV50.safetensors",
30534
+ "juggernautXL_v9.safetensors",
30535
+ "waiNSFWIllustrious_v120.safetensors"
30534
30536
  ],
30535
30537
  {}
30536
30538
  ]
@@ -30584,6 +30586,7 @@ var MOCK_FIXTURES = {
30584
30586
  var zNamespace = zod_exports;
30585
30587
  var z3 = zNamespace.z ?? zNamespace.default ?? zNamespace;
30586
30588
  var COMFYUI_SERVER_URL = process.env.COMFYUI_SERVER_URL || "http://localhost:8188";
30589
+ var COMFYUI_WORKFLOWS_DIR = process.env.COMFYUI_WORKFLOWS_DIR || path.join(homedir(), ".fre4x-comfyui", "workflows");
30587
30590
  var PACKAGE_VERSION = getPackageVersion(import.meta.url);
30588
30591
  var BYTES_PER_GIB = 1024 ** 3;
30589
30592
  var PaginationMetadataSchema = z3.object({
@@ -30612,15 +30615,82 @@ var ObjectInfoSchema = z3.object({
30612
30615
  nodes: z3.record(z3.string(), z3.unknown()),
30613
30616
  pagination: PaginationMetadataSchema.optional()
30614
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
+ });
30615
30624
  var WorkflowResponseSchema = z3.object({
30616
30625
  prompt_id: z3.string(),
30617
30626
  number: z3.number(),
30618
- node_errors: z3.record(z3.string(), z3.unknown())
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()
30619
30635
  });
30620
30636
  var HistorySchema = z3.object({
30621
30637
  history: z3.record(z3.string(), z3.unknown()),
30622
30638
  pagination: PaginationMetadataSchema.optional()
30623
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()
30655
+ ]);
30656
+ var WorkflowEditableInputSchema = z3.object({
30657
+ node_id: z3.string(),
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
+ });
30624
30694
  var QueueSchema = z3.object({
30625
30695
  queue_running: z3.array(z3.unknown()),
30626
30696
  queue_pending: z3.array(z3.unknown())
@@ -30638,7 +30708,9 @@ var ListObjectInfoInputSchema = z3.object({
30638
30708
  }).strict();
30639
30709
  var ExecuteWorkflowInputSchema = z3.object({
30640
30710
  prompt: z3.record(z3.string(), z3.unknown()).describe("Workflow in ComfyUI API JSON format."),
30641
- client_id: z3.string().optional().describe("Optional unique client identifier.")
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.")
30642
30714
  }).strict();
30643
30715
  var GetHistoryInputSchema = z3.object({
30644
30716
  prompt_id: z3.string().optional().describe("Specific prompt ID to retrieve. Omit for full history."),
@@ -30650,6 +30722,40 @@ var GetViewUrlInputSchema = z3.object({
30650
30722
  subfolder: z3.string().optional().describe("Subfolder within the output directory."),
30651
30723
  type: z3.enum(["output", "input", "temp"]).default("output").describe("Storage type.")
30652
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();
30653
30759
  var ComfyApiError = class extends Error {
30654
30760
  constructor(message, statusCode, details) {
30655
30761
  super(message);
@@ -30667,6 +30773,63 @@ function ensureRecord(value, label) {
30667
30773
  }
30668
30774
  return value;
30669
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`);
30785
+ }
30786
+ async function ensureWorkflowStoreDir() {
30787
+ await fs.mkdir(COMFYUI_WORKFLOWS_DIR, { recursive: true });
30788
+ }
30789
+ async function readStoredWorkflowFile(workflowId) {
30790
+ try {
30791
+ const content = await fs.readFile(
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;
30801
+ }
30802
+ }
30803
+ async function writeStoredWorkflowFile(workflow) {
30804
+ await ensureWorkflowStoreDir();
30805
+ await fs.writeFile(
30806
+ getWorkflowFilePath(workflow.workflow.id),
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")) {
30820
+ continue;
30821
+ }
30822
+ const content = await fs.readFile(
30823
+ path.join(COMFYUI_WORKFLOWS_DIR, entry.name),
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
+ }
30670
30833
  function unwrapNamedNode(nodeClass, value) {
30671
30834
  if (!isRecord(value)) {
30672
30835
  return value;
@@ -30720,6 +30883,17 @@ function normalizeSystemInfo(source) {
30720
30883
  function formatGiB(bytes) {
30721
30884
  return `${(bytes / BYTES_PER_GIB).toFixed(1)} GiB`;
30722
30885
  }
30886
+ function formatJsonCodeBlock(value) {
30887
+ return `\`\`\`json
30888
+ ${JSON.stringify(value, null, 2)}
30889
+ \`\`\``;
30890
+ }
30891
+ function formatMarkdownBulletList(items) {
30892
+ return items.length > 0 ? items.map((item) => `- ${item}`).join("\n") : "- none";
30893
+ }
30894
+ async function sleep(ms) {
30895
+ await new Promise((resolve) => setTimeout(resolve, ms));
30896
+ }
30723
30897
  function getEnumLikeOptions(value) {
30724
30898
  if (!Array.isArray(value) || value.length === 0) {
30725
30899
  return [];
@@ -30732,6 +30906,98 @@ function getEnumLikeOptions(value) {
30732
30906
  (entry) => typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean"
30733
30907
  );
30734
30908
  }
30909
+ function buildViewUrl(filename, type, subfolder) {
30910
+ const params = new URLSearchParams({
30911
+ filename,
30912
+ type
30913
+ });
30914
+ if (subfolder) {
30915
+ params.set("subfolder", subfolder);
30916
+ }
30917
+ return new URL(`/view?${params.toString()}`, COMFYUI_SERVER_URL).toString();
30918
+ }
30919
+ async function fetchNodeDefinition(nodeClass) {
30920
+ if (IS_MOCK) {
30921
+ return MOCK_FIXTURES.object_info[nodeClass];
30922
+ }
30923
+ try {
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;
30933
+ }
30934
+ }
30935
+ async function deriveEditableWorkflowInputs(prompt) {
30936
+ const nodeDefinitions = /* @__PURE__ */ new Map();
30937
+ const editableInputs = [];
30938
+ for (const [nodeId, nodeValue] of Object.entries(prompt)) {
30939
+ if (!isRecord(nodeValue)) {
30940
+ continue;
30941
+ }
30942
+ const nodeClass = typeof nodeValue.class_type === "string" ? nodeValue.class_type : void 0;
30943
+ const inputs = isRecord(nodeValue.inputs) ? nodeValue.inputs : void 0;
30944
+ if (!nodeClass || !inputs) {
30945
+ continue;
30946
+ }
30947
+ if (!nodeDefinitions.has(nodeClass)) {
30948
+ nodeDefinitions.set(
30949
+ nodeClass,
30950
+ await fetchNodeDefinition(nodeClass)
30951
+ );
30952
+ }
30953
+ const nodeDefinition = nodeDefinitions.get(nodeClass);
30954
+ const nodeInputRecord = isRecord(nodeDefinition) && isRecord(nodeDefinition.input) ? nodeDefinition.input : void 0;
30955
+ const requiredInputs = isRecord(nodeInputRecord?.required) ? nodeInputRecord.required : void 0;
30956
+ const optionalInputs = isRecord(nodeInputRecord?.optional) ? nodeInputRecord.optional : void 0;
30957
+ for (const [inputName, inputValue] of Object.entries(inputs)) {
30958
+ if (!isEditableWorkflowValue(inputValue)) {
30959
+ continue;
30960
+ }
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
+ }
30973
+ }
30974
+ return editableInputs;
30975
+ }
30976
+ function cloneWorkflowPrompt(prompt) {
30977
+ return JSON.parse(JSON.stringify(prompt));
30978
+ }
30979
+ function applyWorkflowOverrides(prompt, editableInputs, overrides) {
30980
+ const nextPrompt = cloneWorkflowPrompt(prompt);
30981
+ const editableKeys = new Set(
30982
+ editableInputs.map((input) => `${input.node_id}.${input.input_name}`)
30983
+ );
30984
+ for (const override of overrides) {
30985
+ const key = `${override.node_id}.${override.input_name}`;
30986
+ if (!editableKeys.has(key)) {
30987
+ throw new Error(
30988
+ `Override ${key} is not editable for this stored workflow.`
30989
+ );
30990
+ }
30991
+ const node = nextPrompt[override.node_id];
30992
+ if (!isRecord(node) || !isRecord(node.inputs)) {
30993
+ throw new Error(
30994
+ `Stored workflow node ${override.node_id} is invalid.`
30995
+ );
30996
+ }
30997
+ node.inputs[override.input_name] = override.value;
30998
+ }
30999
+ return nextPrompt;
31000
+ }
30735
31001
  function summarizeOptionLines(inputSection) {
30736
31002
  if (!inputSection) {
30737
31003
  return [];
@@ -30741,26 +31007,33 @@ function summarizeOptionLines(inputSection) {
30741
31007
  if (options.length === 0) {
30742
31008
  return [];
30743
31009
  }
30744
- const preview = options.slice(0, 5).map(String).join(", ");
30745
- const suffix = options.length > 5 ? `, ... (${options.length} total)` : "";
30746
- return [`${inputName}: ${preview}${suffix}`];
31010
+ return [`${inputName}: ${options.map(String).join(", ")}`];
30747
31011
  });
30748
31012
  }
30749
31013
  function summarizeSystemInfoText(structuredContent) {
30750
- const deviceSummary = structuredContent.devices.length === 0 ? "_No devices reported._" : structuredContent.devices.map(
30751
- (device) => `- [${device.index}] ${device.name} (${device.type}, ${formatGiB(device.vram_free)} free / ${formatGiB(device.vram_total)} total VRAM)`
30752
- ).join("\n");
31014
+ const deviceSummary = formatMarkdownBulletList(
31015
+ structuredContent.devices.map(
31016
+ (device) => `[${device.index}] ${device.name} (${device.type}, ${formatGiB(device.vram_free)} free / ${formatGiB(device.vram_total)} total VRAM)`
31017
+ )
31018
+ );
30753
31019
  return [
31020
+ "# ComfyUI System Info",
31021
+ "",
30754
31022
  `ComfyUI server is running on ${structuredContent.system.os} with ${structuredContent.devices.length} device(s).`,
30755
- formatFields([
30756
- ["Python", structuredContent.system.python_version],
30757
- [
30758
- "Embedded Python",
30759
- structuredContent.system.embedded_python ? "yes" : "no"
30760
- ]
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}`
30761
31030
  ]),
30762
- `**Devices:**
30763
- ${deviceSummary}`
31031
+ "",
31032
+ "## Devices",
31033
+ deviceSummary,
31034
+ "",
31035
+ "## JSON",
31036
+ formatJsonCodeBlock(structuredContent)
30764
31037
  ].join("\n");
30765
31038
  }
30766
31039
  function summarizeNodeDefinitionText(nodeClass, nodeDefinition) {
@@ -30778,28 +31051,28 @@ function summarizeNodeDefinitionText(nodeClass, nodeDefinition) {
30778
31051
  ...summarizeOptionLines(optionalInputs)
30779
31052
  ];
30780
31053
  return [
31054
+ "# ComfyUI Node Definition",
31055
+ "",
30781
31056
  `Schema retrieved for node: ${nodeClass}`,
30782
- formatFields([
30783
- [
30784
- "Display Name",
30785
- typeof nodeDefinition.display_name === "string" ? nodeDefinition.display_name : typeof nodeDefinition.name === "string" ? nodeDefinition.name : nodeClass
30786
- ],
30787
- [
30788
- "Category",
30789
- typeof nodeDefinition.category === "string" ? nodeDefinition.category : null
30790
- ],
30791
- [
30792
- "Required Inputs",
30793
- requiredInputs ? Object.keys(requiredInputs).join(", ") || "none" : "none"
30794
- ],
30795
- [
30796
- "Optional Inputs",
30797
- optionalInputs ? Object.keys(optionalInputs).join(", ") || null : null
30798
- ],
30799
- ["Outputs", outputNames.join(", ") || null]
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"}`
30800
31066
  ]),
30801
- optionLines.length > 0 ? `**Enumerated options:**
30802
- ${optionLines.map((line) => `- ${line}`).join("\n")}` : ""
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
+ ""
30803
31076
  ].filter(Boolean).join("\n");
30804
31077
  }
30805
31078
  function summarizeHistoryEntryText(promptId, entry) {
@@ -30824,35 +31097,268 @@ function summarizeHistoryEntryText(promptId, entry) {
30824
31097
  }
30825
31098
  }
30826
31099
  return [
31100
+ "# ComfyUI History Entry",
31101
+ "",
30827
31102
  `History retrieved for job: ${promptId}`,
30828
- formatFields([
30829
- [
30830
- "Status",
30831
- typeof statusRecord?.status_str === "string" ? statusRecord.status_str : "unknown"
30832
- ],
30833
- [
30834
- "Completed",
30835
- statusRecord?.completed === true ? "yes" : statusRecord?.completed === false ? "no" : "unknown"
30836
- ],
30837
- [
30838
- "Images",
30839
- imageRefs.length > 0 ? imageRefs.slice(0, 5).join(", ") : "none reported"
30840
- ]
30841
- ])
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");
31120
+ }
31121
+ function extractHistoryImages(entry) {
31122
+ if (!isRecord(entry) || !isRecord(entry.outputs)) {
31123
+ return [];
31124
+ }
31125
+ const images = [];
31126
+ for (const output of Object.values(entry.outputs)) {
31127
+ if (!isRecord(output) || !Array.isArray(output.images)) {
31128
+ continue;
31129
+ }
31130
+ for (const image of output.images) {
31131
+ if (!isRecord(image) || typeof image.filename !== "string" || typeof image.type !== "string") {
31132
+ continue;
31133
+ }
31134
+ const subfolder = typeof image.subfolder === "string" ? image.subfolder : "";
31135
+ images.push({
31136
+ filename: image.filename,
31137
+ subfolder,
31138
+ type: image.type,
31139
+ view_url: buildViewUrl(
31140
+ image.filename,
31141
+ image.type,
31142
+ subfolder || void 0
31143
+ )
31144
+ });
31145
+ }
31146
+ }
31147
+ return images;
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";
31154
+ }
31155
+ function isHistoryCompleted(entry) {
31156
+ if (!isRecord(entry) || !isRecord(entry.status)) {
31157
+ return false;
31158
+ }
31159
+ if (entry.status.completed === true) {
31160
+ return true;
31161
+ }
31162
+ return entry.status.status_str === "error";
31163
+ }
31164
+ function summarizeWaitForWorkflowText(structuredContent) {
31165
+ return [
31166
+ "# ComfyUI Workflow Wait Result",
31167
+ "",
31168
+ "## Summary",
31169
+ formatMarkdownBulletList([
31170
+ `Prompt ID: ${structuredContent.prompt_id}`,
31171
+ `Status: ${structuredContent.status}`,
31172
+ `Completed: ${structuredContent.completed ? "yes" : "no"}`,
31173
+ `Timed Out: ${structuredContent.timed_out ? "yes" : "no"}`,
31174
+ `Elapsed Seconds: ${structuredContent.elapsed_seconds}`,
31175
+ `Images: ${structuredContent.image_count}`
31176
+ ]),
31177
+ "",
31178
+ "## Images",
31179
+ formatMarkdownBulletList(
31180
+ structuredContent.images.map(
31181
+ (image) => `${image.subfolder ? `${image.subfolder}/` : ""}${image.filename} (${image.type}) -> ${image.view_url}`
31182
+ )
31183
+ ),
31184
+ "",
31185
+ "## JSON",
31186
+ formatJsonCodeBlock(structuredContent)
31187
+ ].join("\n");
31188
+ }
31189
+ function summarizeWorkflowListText(heading, workflows, pagination) {
31190
+ return [
31191
+ heading,
31192
+ "",
31193
+ "## Pagination",
31194
+ formatMarkdownBulletList([
31195
+ `Returned: ${workflows.length}`,
31196
+ `Total: ${pagination.total_count}`,
31197
+ `Limit: ${pagination.limit}`,
31198
+ `Offset: ${pagination.offset}`,
31199
+ `Has More: ${pagination.has_more ? "yes" : "no"}`
31200
+ ]),
31201
+ "",
31202
+ "## Workflows",
31203
+ formatMarkdownBulletList(
31204
+ workflows.map(
31205
+ (workflow) => `${workflow.id} \u2014 ${workflow.name} | tags: ${workflow.tags.join(", ") || "none"} | editable inputs: ${workflow.editable_input_count}`
31206
+ )
31207
+ ),
31208
+ "",
31209
+ "## JSON",
31210
+ formatJsonCodeBlock({
31211
+ workflows,
31212
+ pagination
31213
+ })
30842
31214
  ].join("\n");
30843
31215
  }
31216
+ function summarizeStoredWorkflowText(workflow) {
31217
+ return [
31218
+ "# ComfyUI Stored Workflow",
31219
+ "",
31220
+ "## Summary",
31221
+ formatMarkdownBulletList([
31222
+ `Workflow ID: ${workflow.workflow.id}`,
31223
+ `Name: ${workflow.workflow.name}`,
31224
+ `Description: ${workflow.workflow.description ?? "none"}`,
31225
+ `Tags: ${workflow.workflow.tags.join(", ") || "none"}`,
31226
+ `Created At: ${workflow.workflow.created_at}`,
31227
+ `Updated At: ${workflow.workflow.updated_at}`,
31228
+ `Node Count: ${workflow.workflow.node_count}`,
31229
+ `Editable Inputs: ${workflow.workflow.editable_input_count}`,
31230
+ `Source: ${workflow.workflow.source}`
31231
+ ]),
31232
+ "",
31233
+ "## Editable Inputs",
31234
+ formatMarkdownBulletList(
31235
+ workflow.editable_inputs.map(
31236
+ (input) => `${input.node_id}.${input.input_name} (${input.node_class}, ${input.value_type}) = ${String(
31237
+ input.current_value
31238
+ )}${input.options.length > 0 ? ` | options: ${input.options.join(", ")}` : ""}`
31239
+ )
31240
+ ),
31241
+ "",
31242
+ "## JSON",
31243
+ formatJsonCodeBlock(workflow)
31244
+ ].join("\n");
31245
+ }
31246
+ function summarizeSavedWorkflowText(workflow) {
31247
+ return [
31248
+ "# ComfyUI Workflow Saved",
31249
+ "",
31250
+ "## Summary",
31251
+ formatMarkdownBulletList([
31252
+ `Workflow ID: ${workflow.workflow.id}`,
31253
+ `Name: ${workflow.workflow.name}`,
31254
+ `Tags: ${workflow.workflow.tags.join(", ") || "none"}`,
31255
+ `Editable Inputs: ${workflow.workflow.editable_input_count}`
31256
+ ]),
31257
+ "",
31258
+ "## Next Step",
31259
+ `Call comfyui_run_workflow with workflow_id="${workflow.workflow.id}" and optional overrides to reuse it.`,
31260
+ "",
31261
+ "## JSON",
31262
+ formatJsonCodeBlock(workflow)
31263
+ ].join("\n");
31264
+ }
31265
+ async function waitForWorkflowResult(params) {
31266
+ const startTime = Date.now();
31267
+ const timeoutMs = params.timeout_seconds * 1e3;
31268
+ let latestEntry;
31269
+ while (Date.now() - startTime <= timeoutMs) {
31270
+ latestEntry = await fetchHistoryEntry(params.prompt_id);
31271
+ if (latestEntry !== void 0) {
31272
+ const images = extractHistoryImages(latestEntry);
31273
+ const completed = isHistoryCompleted(latestEntry);
31274
+ if (completed && (!params.require_images || images.length > 0)) {
31275
+ return {
31276
+ prompt_id: params.prompt_id,
31277
+ status: getHistoryStatus(latestEntry),
31278
+ completed: true,
31279
+ timed_out: false,
31280
+ elapsed_seconds: Number(
31281
+ ((Date.now() - startTime) / 1e3).toFixed(3)
31282
+ ),
31283
+ image_count: images.length,
31284
+ images,
31285
+ history_entry: ensureRecord(latestEntry, "history entry")
31286
+ };
31287
+ }
31288
+ }
31289
+ await sleep(params.poll_interval_ms);
31290
+ }
31291
+ const timeoutImages = latestEntry !== void 0 ? extractHistoryImages(latestEntry) : [];
31292
+ return {
31293
+ prompt_id: params.prompt_id,
31294
+ status: latestEntry !== void 0 ? getHistoryStatus(latestEntry) : "not_found",
31295
+ completed: latestEntry !== void 0 ? isHistoryCompleted(latestEntry) : false,
31296
+ timed_out: true,
31297
+ elapsed_seconds: Number(((Date.now() - startTime) / 1e3).toFixed(3)),
31298
+ image_count: timeoutImages.length,
31299
+ images: timeoutImages,
31300
+ history_entry: latestEntry !== void 0 ? ensureRecord(latestEntry, "history entry") : null
31301
+ };
31302
+ }
31303
+ async function createStoredWorkflowRecord(params) {
31304
+ const now = (/* @__PURE__ */ new Date()).toISOString();
31305
+ const editableInputs = await deriveEditableWorkflowInputs(params.prompt);
31306
+ const workflowId = params.workflow_id ? slugifyWorkflowId(params.workflow_id) : slugifyWorkflowId(params.name);
31307
+ return {
31308
+ workflow: {
31309
+ id: workflowId,
31310
+ name: params.name,
31311
+ description: params.description,
31312
+ tags: params.tags,
31313
+ created_at: params.existing?.workflow.created_at ?? now,
31314
+ updated_at: now,
31315
+ node_count: Object.keys(params.prompt).length,
31316
+ editable_input_count: editableInputs.length,
31317
+ source: params.source
31318
+ },
31319
+ prompt: params.prompt,
31320
+ editable_inputs: editableInputs
31321
+ };
31322
+ }
31323
+ async function submitWorkflow(args) {
31324
+ const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson(
31325
+ "/prompt",
31326
+ {
31327
+ method: "POST",
31328
+ body: JSON.stringify({
31329
+ prompt: args.prompt,
31330
+ client_id: args.client_id
31331
+ })
31332
+ }
31333
+ );
31334
+ return {
31335
+ prompt_id: result.prompt_id,
31336
+ number: result.number,
31337
+ node_errors: result.node_errors
31338
+ };
31339
+ }
30844
31340
  function summarizeWorkflowResponseText(result) {
30845
31341
  const hasErrors = Object.keys(result.node_errors).length > 0;
30846
31342
  return [
30847
- formatFields([
30848
- [
30849
- "Status",
30850
- hasErrors ? "Submitted with node errors" : "Successfully queued"
30851
- ],
30852
- ["Prompt ID", result.prompt_id],
30853
- ["Queue Number", result.number]
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}`
30854
31351
  ]),
30855
- 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.`
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
+ })
30856
31362
  ].join("\n");
30857
31363
  }
30858
31364
  function createAgentFacingResult(text, structuredContent) {
@@ -30905,6 +31411,21 @@ async function fetchComfyJson(apiPath, options) {
30905
31411
  }
30906
31412
  return body;
30907
31413
  }
31414
+ async function fetchHistoryEntry(promptId) {
31415
+ if (IS_MOCK) {
31416
+ return MOCK_FIXTURES.history[promptId];
31417
+ }
31418
+ try {
31419
+ return await fetchComfyJson(
31420
+ `/history/${encodeURIComponent(promptId)}`
31421
+ );
31422
+ } catch (error48) {
31423
+ if (error48 instanceof ComfyApiError && error48.statusCode === 404) {
31424
+ return void 0;
31425
+ }
31426
+ throw error48;
31427
+ }
31428
+ }
30908
31429
  function handleComfyError(error48) {
30909
31430
  if (error48 instanceof ZodError) {
30910
31431
  return createValidationError(
@@ -30970,13 +31491,29 @@ async function handleListObjectInfo(args) {
30970
31491
  };
30971
31492
  return createAgentFacingResult(
30972
31493
  [
30973
- `Retrieved ${paginated.items.length} of ${paginated.total} node definitions.`,
30974
- paginated.items.length > 0 ? `Node classes: ${paginated.items.slice(0, 10).map(([nodeClass]) => nodeClass).join(
30975
- ", "
30976
- )}${paginated.items.length > 10 ? ", ..." : ""}` : "",
30977
- paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} for more.` : "",
30978
- "If you need a usable workflow field list, call comfyui_list_object_info again with node_class set to the exact node name."
30979
- ].filter(Boolean).join("\n"),
31494
+ "# ComfyUI Node Definitions",
31495
+ "",
31496
+ "## Pagination",
31497
+ formatMarkdownBulletList([
31498
+ `Returned: ${paginated.items.length}`,
31499
+ `Total: ${paginated.total}`,
31500
+ `Limit: ${paginated.limit}`,
31501
+ `Offset: ${paginated.offset}`,
31502
+ `Has More: ${paginated.hasMore ? "yes" : "no"}`
31503
+ ]),
31504
+ "",
31505
+ "## Node Classes In This Page",
31506
+ formatMarkdownBulletList(
31507
+ paginated.items.map(([nodeClass]) => nodeClass)
31508
+ ),
31509
+ "",
31510
+ "## Next Step",
31511
+ paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} to retrieve the next page.` : "This page contains the final set of node classes for the current query.",
31512
+ "If you need a usable workflow field list, call comfyui_list_object_info again with node_class set to the exact node name.",
31513
+ "",
31514
+ "## JSON",
31515
+ formatJsonCodeBlock(structuredContent)
31516
+ ].join("\n"),
30980
31517
  structuredContent
30981
31518
  );
30982
31519
  } catch (error48) {
@@ -30985,21 +31522,39 @@ async function handleListObjectInfo(args) {
30985
31522
  }
30986
31523
  async function handleExecuteWorkflow(args) {
30987
31524
  try {
30988
- const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson(
30989
- "/prompt",
30990
- {
30991
- method: "POST",
30992
- body: JSON.stringify({
30993
- prompt: args.prompt,
30994
- client_id: args.client_id
30995
- })
30996
- }
30997
- );
31525
+ const result = await submitWorkflow(args);
30998
31526
  const structuredContent = {
30999
31527
  prompt_id: result.prompt_id,
31000
31528
  number: result.number,
31001
31529
  node_errors: result.node_errors
31002
31530
  };
31531
+ if (args.await && Object.keys(result.node_errors).length === 0) {
31532
+ const waitResult = await waitForWorkflowResult({
31533
+ prompt_id: result.prompt_id,
31534
+ timeout_seconds: args.timeout,
31535
+ poll_interval_ms: 1e3,
31536
+ require_images: false
31537
+ });
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
+ }
31003
31558
  return createAgentFacingResult(
31004
31559
  summarizeWorkflowResponseText(result),
31005
31560
  structuredContent
@@ -31008,6 +31563,195 @@ async function handleExecuteWorkflow(args) {
31008
31563
  return handleComfyError(error48);
31009
31564
  }
31010
31565
  }
31566
+ async function handleListWorkflows(args) {
31567
+ try {
31568
+ const workflows = (await listStoredWorkflowFiles()).map(
31569
+ (workflow) => workflow.workflow
31570
+ );
31571
+ const paginated = applyPagination(workflows, args);
31572
+ const structuredContent = {
31573
+ workflows: paginated.items,
31574
+ pagination: {
31575
+ total_count: paginated.total,
31576
+ limit: paginated.limit,
31577
+ offset: paginated.offset,
31578
+ has_more: paginated.hasMore
31579
+ }
31580
+ };
31581
+ return createAgentFacingResult(
31582
+ summarizeWorkflowListText(
31583
+ "# ComfyUI Stored Workflows",
31584
+ structuredContent.workflows,
31585
+ structuredContent.pagination
31586
+ ),
31587
+ structuredContent
31588
+ );
31589
+ } catch (error48) {
31590
+ return handleComfyError(error48);
31591
+ }
31592
+ }
31593
+ async function handleSearchWorkflows(args) {
31594
+ try {
31595
+ const normalizedQuery = args.query.trim().toLowerCase();
31596
+ const matching = (await listStoredWorkflowFiles()).map((workflow) => workflow.workflow).filter(
31597
+ (workflow) => [
31598
+ workflow.id,
31599
+ workflow.name,
31600
+ workflow.description ?? "",
31601
+ workflow.tags.join(" ")
31602
+ ].join(" ").toLowerCase().includes(normalizedQuery)
31603
+ );
31604
+ const paginated = applyPagination(matching, args);
31605
+ const structuredContent = {
31606
+ workflows: paginated.items,
31607
+ pagination: {
31608
+ total_count: paginated.total,
31609
+ limit: paginated.limit,
31610
+ offset: paginated.offset,
31611
+ has_more: paginated.hasMore
31612
+ }
31613
+ };
31614
+ return createAgentFacingResult(
31615
+ summarizeWorkflowListText(
31616
+ `# ComfyUI Workflow Search
31617
+
31618
+ Query: ${args.query}`,
31619
+ structuredContent.workflows,
31620
+ structuredContent.pagination
31621
+ ),
31622
+ structuredContent
31623
+ );
31624
+ } catch (error48) {
31625
+ return handleComfyError(error48);
31626
+ }
31627
+ }
31628
+ async function handleGetWorkflow(args) {
31629
+ try {
31630
+ const workflow = await readStoredWorkflowFile(args.workflow_id);
31631
+ if (!workflow) {
31632
+ return createNotFoundError(`Workflow ${args.workflow_id}`);
31633
+ }
31634
+ return createAgentFacingResult(
31635
+ summarizeStoredWorkflowText(workflow),
31636
+ workflow
31637
+ );
31638
+ } catch (error48) {
31639
+ return handleComfyError(error48);
31640
+ }
31641
+ }
31642
+ async function handleSaveWorkflow(args) {
31643
+ try {
31644
+ const workflowId = args.workflow_id ? slugifyWorkflowId(args.workflow_id) : slugifyWorkflowId(args.name);
31645
+ const existing = await readStoredWorkflowFile(workflowId);
31646
+ if (existing && !args.overwrite) {
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
31665
+ );
31666
+ } catch (error48) {
31667
+ return handleComfyError(error48);
31668
+ }
31669
+ }
31670
+ async function handleRunWorkflow(args) {
31671
+ try {
31672
+ const workflow = await readStoredWorkflowFile(args.workflow_id);
31673
+ if (!workflow) {
31674
+ return createNotFoundError(`Workflow ${args.workflow_id}`);
31675
+ }
31676
+ const prompt = applyWorkflowOverrides(
31677
+ workflow.prompt,
31678
+ workflow.editable_inputs,
31679
+ args.overrides
31680
+ );
31681
+ const submission = await submitWorkflow({
31682
+ prompt,
31683
+ client_id: args.client_id
31684
+ });
31685
+ const structuredContent = {
31686
+ prompt_id: submission.prompt_id,
31687
+ number: submission.number,
31688
+ node_errors: submission.node_errors,
31689
+ workflow_id: workflow.workflow.id,
31690
+ workflow_name: workflow.workflow.name,
31691
+ applied_overrides: args.overrides
31692
+ };
31693
+ if (args.await && Object.keys(submission.node_errors).length === 0) {
31694
+ const waitResult = await waitForWorkflowResult({
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
+ );
31731
+ }
31732
+ return createAgentFacingResult(
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"),
31749
+ structuredContent
31750
+ );
31751
+ } catch (error48) {
31752
+ return handleComfyError(error48);
31753
+ }
31754
+ }
31011
31755
  async function handleGetHistory(args) {
31012
31756
  try {
31013
31757
  const rawHistory = IS_MOCK ? args.prompt_id ? MOCK_FIXTURES.history[args.prompt_id] : MOCK_FIXTURES.history : await fetchComfyJson(
@@ -31038,12 +31782,39 @@ async function handleGetHistory(args) {
31038
31782
  };
31039
31783
  return createAgentFacingResult(
31040
31784
  [
31041
- `Retrieved ${paginated.items.length} of ${paginated.total} job(s) from history.`,
31042
- paginated.items.length > 0 ? `Prompt IDs: ${paginated.items.slice(0, 10).map(([promptId]) => promptId).join(
31043
- ", "
31044
- )}${paginated.items.length > 10 ? ", ..." : ""}` : "",
31045
- paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} for more.` : ""
31046
- ].filter(Boolean).join("\n"),
31785
+ "# ComfyUI History",
31786
+ "",
31787
+ "## Pagination",
31788
+ formatMarkdownBulletList([
31789
+ `Returned: ${paginated.items.length}`,
31790
+ `Total: ${paginated.total}`,
31791
+ `Limit: ${paginated.limit}`,
31792
+ `Offset: ${paginated.offset}`,
31793
+ `Has More: ${paginated.hasMore ? "yes" : "no"}`
31794
+ ]),
31795
+ "",
31796
+ "## Prompt IDs In This Page",
31797
+ formatMarkdownBulletList(
31798
+ paginated.items.map(([promptId]) => promptId)
31799
+ ),
31800
+ "",
31801
+ "## Next Step",
31802
+ paginated.hasMore ? `Use offset=${paginated.offset + paginated.limit} to retrieve the next page.` : "This page contains the final set of history entries for the current query.",
31803
+ "",
31804
+ "## JSON",
31805
+ formatJsonCodeBlock(structuredContent)
31806
+ ].join("\n"),
31807
+ structuredContent
31808
+ );
31809
+ } catch (error48) {
31810
+ return handleComfyError(error48);
31811
+ }
31812
+ }
31813
+ async function handleWaitForWorkflow(args) {
31814
+ try {
31815
+ const structuredContent = await waitForWorkflowResult(args);
31816
+ return createAgentFacingResult(
31817
+ summarizeWaitForWorkflowText(structuredContent),
31047
31818
  structuredContent
31048
31819
  );
31049
31820
  } catch (error48) {
@@ -31059,7 +31830,18 @@ async function handleGetQueue() {
31059
31830
  queue_pending: Array.isArray(queueRecord.queue_pending) ? queueRecord.queue_pending : []
31060
31831
  };
31061
31832
  return createAgentFacingResult(
31062
- `Queue status: ${structuredContent.queue_running.length} running, ${structuredContent.queue_pending.length} pending.`,
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"),
31063
31845
  structuredContent
31064
31846
  );
31065
31847
  } catch (error48) {
@@ -31067,20 +31849,22 @@ async function handleGetQueue() {
31067
31849
  }
31068
31850
  }
31069
31851
  async function handleGetViewUrl(args) {
31070
- const params = new URLSearchParams({
31071
- filename: args.filename,
31072
- type: args.type
31073
- });
31074
- if (args.subfolder) {
31075
- params.set("subfolder", args.subfolder);
31076
- }
31077
- const url2 = new URL(
31078
- `/view?${params.toString()}`,
31079
- COMFYUI_SERVER_URL
31080
- ).toString();
31852
+ const url2 = buildViewUrl(args.filename, args.type, args.subfolder);
31081
31853
  return createAgentFacingResult(
31082
- `View URL generated for ${args.filename}
31083
- ${url2}`,
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"),
31084
31868
  { url: url2 }
31085
31869
  );
31086
31870
  }
@@ -31132,6 +31916,77 @@ function createServer() {
31132
31916
  },
31133
31917
  handleExecuteWorkflow
31134
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
31948
+ );
31949
+ server.registerTool(
31950
+ "comfyui_get_workflow",
31951
+ {
31952
+ title: "Get Stored Workflow",
31953
+ description: "Get a stored ComfyUI workflow and its editable inputs.",
31954
+ inputSchema: GetWorkflowInputSchema,
31955
+ outputSchema: StoredWorkflowSchema,
31956
+ annotations: {
31957
+ readOnlyHint: true,
31958
+ idempotentHint: true,
31959
+ openWorldHint: true
31960
+ }
31961
+ },
31962
+ handleGetWorkflow
31963
+ );
31964
+ server.registerTool(
31965
+ "comfyui_save_workflow",
31966
+ {
31967
+ title: "Save Workflow",
31968
+ description: "Save a ComfyUI workflow for later reuse.",
31969
+ inputSchema: SaveWorkflowInputSchema,
31970
+ outputSchema: StoredWorkflowSchema,
31971
+ annotations: {
31972
+ openWorldHint: true
31973
+ }
31974
+ },
31975
+ handleSaveWorkflow
31976
+ );
31977
+ server.registerTool(
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
+ );
31135
31990
  server.registerTool(
31136
31991
  "comfyui_get_history",
31137
31992
  {
@@ -31147,6 +32002,19 @@ function createServer() {
31147
32002
  },
31148
32003
  handleGetHistory
31149
32004
  );
32005
+ server.registerTool(
32006
+ "comfyui_wait_for_workflow",
32007
+ {
32008
+ title: "Wait for ComfyUI Workflow",
32009
+ description: "Wait for a workflow result with timeout-based polling.",
32010
+ inputSchema: WaitForWorkflowInputSchema,
32011
+ outputSchema: WaitForWorkflowSchema,
32012
+ annotations: {
32013
+ openWorldHint: true
32014
+ }
32015
+ },
32016
+ handleWaitForWorkflow
32017
+ );
31150
32018
  server.registerTool(
31151
32019
  "comfyui_get_queue",
31152
32020
  {