@fre4x/comfyui 1.0.65 → 1.1.0-beta.2
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 +43 -5
- package/dist/index.js +1020 -112
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30637,8 +30637,9 @@ function normalizeWidgetValue(value, typeName, extraInfo) {
|
|
|
30637
30637
|
}
|
|
30638
30638
|
return value;
|
|
30639
30639
|
}
|
|
30640
|
-
function
|
|
30640
|
+
function convertWebUIToAPIWithMetadata(webUIJson, objectInfo) {
|
|
30641
30641
|
const prompt = {};
|
|
30642
|
+
const widgetPathMap = {};
|
|
30642
30643
|
const links = webUIJson.links || [];
|
|
30643
30644
|
const nodes = webUIJson.nodes || [];
|
|
30644
30645
|
const serverNodeIds = /* @__PURE__ */ new Set();
|
|
@@ -30703,14 +30704,16 @@ function convertWebUIToAPI(webUIJson, objectInfo) {
|
|
|
30703
30704
|
}
|
|
30704
30705
|
continue;
|
|
30705
30706
|
}
|
|
30707
|
+
const widgetSourceIndex = widgetIndex;
|
|
30706
30708
|
const value = normalizeWidgetValue(
|
|
30707
|
-
widgets[
|
|
30709
|
+
widgets[widgetSourceIndex],
|
|
30708
30710
|
typeName,
|
|
30709
30711
|
inputExtraInfo
|
|
30710
30712
|
);
|
|
30711
30713
|
widgetIndex++;
|
|
30712
30714
|
if (!isConnectedWidget) {
|
|
30713
30715
|
promptNode.inputs[inputName] = value;
|
|
30716
|
+
widgetPathMap[`${nodeIdStr}.widgets[${widgetSourceIndex}]`] = `${nodeIdStr}.inputs.${inputName}`;
|
|
30714
30717
|
}
|
|
30715
30718
|
}
|
|
30716
30719
|
};
|
|
@@ -30725,7 +30728,10 @@ function convertWebUIToAPI(webUIJson, objectInfo) {
|
|
|
30725
30728
|
}
|
|
30726
30729
|
prompt[nodeIdStr] = promptNode;
|
|
30727
30730
|
}
|
|
30728
|
-
return
|
|
30731
|
+
return {
|
|
30732
|
+
prompt,
|
|
30733
|
+
widgetPathMap
|
|
30734
|
+
};
|
|
30729
30735
|
}
|
|
30730
30736
|
|
|
30731
30737
|
// src/index.ts
|
|
@@ -30749,6 +30755,20 @@ var WorkflowInputError = class extends Error {
|
|
|
30749
30755
|
this.name = "WorkflowInputError";
|
|
30750
30756
|
}
|
|
30751
30757
|
};
|
|
30758
|
+
var INPUT_SEGMENT = "inputs";
|
|
30759
|
+
var OverrideRecordSchema = z3.record(z3.string(), z3.unknown());
|
|
30760
|
+
var OverridesSchema = z3.union([OverrideRecordSchema, z3.string()]).describe(
|
|
30761
|
+
'Dot-notation overrides as an object or JSON string. e.g. {"6.seed": 7}'
|
|
30762
|
+
);
|
|
30763
|
+
var WorkflowTimeoutError = class extends Error {
|
|
30764
|
+
constructor(promptId, timeoutSeconds, queueSnapshot) {
|
|
30765
|
+
super(`Timeout waiting for workflow ${promptId}`);
|
|
30766
|
+
this.promptId = promptId;
|
|
30767
|
+
this.timeoutSeconds = timeoutSeconds;
|
|
30768
|
+
this.queueSnapshot = queueSnapshot;
|
|
30769
|
+
this.name = "WorkflowTimeoutError";
|
|
30770
|
+
}
|
|
30771
|
+
};
|
|
30752
30772
|
function isRecord2(value) {
|
|
30753
30773
|
return typeof value === "object" && value !== null;
|
|
30754
30774
|
}
|
|
@@ -30961,17 +30981,31 @@ ${detailSummary}` : error48.message
|
|
|
30961
30981
|
isError: true
|
|
30962
30982
|
};
|
|
30963
30983
|
}
|
|
30964
|
-
|
|
30965
|
-
|
|
30984
|
+
if (error48 instanceof WorkflowTimeoutError) {
|
|
30985
|
+
const queueSummary = formatQueueSnapshotText(error48.queueSnapshot);
|
|
30986
|
+
const promptVisibility = getPromptQueueVisibility(
|
|
30987
|
+
error48.promptId,
|
|
30988
|
+
error48.queueSnapshot
|
|
30989
|
+
);
|
|
30990
|
+
const promptVisibilityText = promptVisibility === true ? "The prompt is still visible in the ComfyUI queue snapshot." : promptVisibility === false ? "The prompt is not visible in the ComfyUI queue snapshot; inspect ComfyUI history or server logs if it never completes." : "ComfyUI queue state was unavailable at timeout.";
|
|
30966
30991
|
return {
|
|
30967
30992
|
content: [
|
|
30968
30993
|
{
|
|
30969
30994
|
type: "text",
|
|
30970
|
-
text:
|
|
30971
|
-
|
|
30972
|
-
|
|
30995
|
+
text: [
|
|
30996
|
+
`${error48.message}.`,
|
|
30997
|
+
queueSummary,
|
|
30998
|
+
promptVisibilityText,
|
|
30999
|
+
`You can re-attach by calling 'comfyui_wait_for_workflow' again with prompt_id '${error48.promptId}'.`
|
|
31000
|
+
].filter((line) => line && line.length > 0).join("\n")
|
|
30973
31001
|
}
|
|
30974
31002
|
],
|
|
31003
|
+
structuredContent: {
|
|
31004
|
+
prompt_id: error48.promptId,
|
|
31005
|
+
timeout_seconds: error48.timeoutSeconds,
|
|
31006
|
+
queue: error48.queueSnapshot ?? null,
|
|
31007
|
+
prompt_visible_in_queue: promptVisibility
|
|
31008
|
+
},
|
|
30975
31009
|
isError: true
|
|
30976
31010
|
};
|
|
30977
31011
|
}
|
|
@@ -30994,7 +31028,9 @@ function getWorkflowDirectories(extraPath) {
|
|
|
30994
31028
|
if (extraPath) {
|
|
30995
31029
|
directories.push(extraPath);
|
|
30996
31030
|
}
|
|
30997
|
-
return
|
|
31031
|
+
return [
|
|
31032
|
+
...new Set(directories.map((directory) => path.resolve(directory)))
|
|
31033
|
+
];
|
|
30998
31034
|
}
|
|
30999
31035
|
function getWorkflowNameCandidates(name) {
|
|
31000
31036
|
const trimmed = name.trim();
|
|
@@ -31022,6 +31058,22 @@ function isConnectionValue(value) {
|
|
|
31022
31058
|
async function getObjectInfo() {
|
|
31023
31059
|
return IS_MOCK ? MOCK_FIXTURES.object_info : await fetchComfyJson("/object_info");
|
|
31024
31060
|
}
|
|
31061
|
+
function summarizeErrorMessage(error48) {
|
|
31062
|
+
return error48 instanceof Error ? error48.message : String(error48);
|
|
31063
|
+
}
|
|
31064
|
+
async function tryGetObjectInfo() {
|
|
31065
|
+
try {
|
|
31066
|
+
return {
|
|
31067
|
+
objectInfo: await getObjectInfo()
|
|
31068
|
+
};
|
|
31069
|
+
} catch (error48) {
|
|
31070
|
+
return {
|
|
31071
|
+
warning: `Node schema details unavailable from ComfyUI: ${summarizeErrorMessage(
|
|
31072
|
+
error48
|
|
31073
|
+
)}`
|
|
31074
|
+
};
|
|
31075
|
+
}
|
|
31076
|
+
}
|
|
31025
31077
|
async function resolveWorkflowReference(args) {
|
|
31026
31078
|
if (args.workflow_file_path) {
|
|
31027
31079
|
const targetPath = args.workflow_file_path;
|
|
@@ -31067,6 +31119,7 @@ async function resolveWorkflowReference(args) {
|
|
|
31067
31119
|
"workflow_id"
|
|
31068
31120
|
);
|
|
31069
31121
|
}
|
|
31122
|
+
const objectInfoAvailability = await tryGetObjectInfo();
|
|
31070
31123
|
for (const dir of getWorkflowDirectories()) {
|
|
31071
31124
|
try {
|
|
31072
31125
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
@@ -31075,11 +31128,17 @@ async function resolveWorkflowReference(args) {
|
|
|
31075
31128
|
continue;
|
|
31076
31129
|
}
|
|
31077
31130
|
const fullPath = path.join(dir, entry.name);
|
|
31078
|
-
const
|
|
31079
|
-
|
|
31131
|
+
const inspected = await inspectWorkflowFile(
|
|
31132
|
+
fullPath,
|
|
31133
|
+
objectInfoAvailability.objectInfo
|
|
31134
|
+
);
|
|
31135
|
+
if (!inspected) {
|
|
31136
|
+
continue;
|
|
31137
|
+
}
|
|
31138
|
+
if (requestedId === inspected.id || requestedId === inspected.name || requestedId === entry.name.replace(/\.json$/, "")) {
|
|
31080
31139
|
return {
|
|
31081
31140
|
targetPath: fullPath,
|
|
31082
|
-
content:
|
|
31141
|
+
content: inspected.content
|
|
31083
31142
|
};
|
|
31084
31143
|
}
|
|
31085
31144
|
}
|
|
@@ -31097,7 +31156,7 @@ async function resolveWorkflowReference(args) {
|
|
|
31097
31156
|
"workflow_id"
|
|
31098
31157
|
);
|
|
31099
31158
|
}
|
|
31100
|
-
async function normalizeWorkflowPrompt(workflow) {
|
|
31159
|
+
async function normalizeWorkflowPrompt(workflow, objectInfo) {
|
|
31101
31160
|
if (!isRecord2(workflow)) {
|
|
31102
31161
|
throw new WorkflowInputError(
|
|
31103
31162
|
"Workflow must be a JSON object.",
|
|
@@ -31105,13 +31164,22 @@ async function normalizeWorkflowPrompt(workflow) {
|
|
|
31105
31164
|
);
|
|
31106
31165
|
}
|
|
31107
31166
|
let sourceFormat = "api";
|
|
31167
|
+
let widgetPathMap = {};
|
|
31108
31168
|
let normalizedWorkflow = workflow;
|
|
31109
31169
|
if (isWebUIFormat(normalizedWorkflow)) {
|
|
31170
|
+
if (!objectInfo) {
|
|
31171
|
+
throw new WorkflowInputError(
|
|
31172
|
+
"Web UI workflow normalization requires ComfyUI node definitions. Start the ComfyUI server or save the workflow in API format first.",
|
|
31173
|
+
"workflow"
|
|
31174
|
+
);
|
|
31175
|
+
}
|
|
31110
31176
|
sourceFormat = "webui";
|
|
31111
|
-
|
|
31177
|
+
const converted = convertWebUIToAPIWithMetadata(
|
|
31112
31178
|
normalizedWorkflow,
|
|
31113
|
-
|
|
31179
|
+
objectInfo
|
|
31114
31180
|
);
|
|
31181
|
+
normalizedWorkflow = converted.prompt;
|
|
31182
|
+
widgetPathMap = converted.widgetPathMap;
|
|
31115
31183
|
}
|
|
31116
31184
|
const promptCandidate = isRecord2(normalizedWorkflow.prompt) && normalizedWorkflow.prompt ? normalizedWorkflow.prompt : normalizedWorkflow;
|
|
31117
31185
|
if (!isRecord2(promptCandidate)) {
|
|
@@ -31122,10 +31190,11 @@ async function normalizeWorkflowPrompt(workflow) {
|
|
|
31122
31190
|
}
|
|
31123
31191
|
return {
|
|
31124
31192
|
prompt: extractRunnablePromptNodes(promptCandidate),
|
|
31125
|
-
sourceFormat
|
|
31193
|
+
sourceFormat,
|
|
31194
|
+
widgetPathMap
|
|
31126
31195
|
};
|
|
31127
31196
|
}
|
|
31128
|
-
async function loadWorkflow(args) {
|
|
31197
|
+
async function loadWorkflow(args, objectInfo) {
|
|
31129
31198
|
const { targetPath, content } = await resolveWorkflowReference(args);
|
|
31130
31199
|
let parsed;
|
|
31131
31200
|
try {
|
|
@@ -31136,13 +31205,83 @@ async function loadWorkflow(args) {
|
|
|
31136
31205
|
args.workflow_file_path ? "workflow_file_path" : "workflow_id"
|
|
31137
31206
|
);
|
|
31138
31207
|
}
|
|
31139
|
-
const { prompt, sourceFormat } = await normalizeWorkflowPrompt(parsed);
|
|
31208
|
+
const { prompt, sourceFormat, widgetPathMap } = await normalizeWorkflowPrompt(parsed, objectInfo);
|
|
31140
31209
|
return {
|
|
31141
31210
|
targetPath,
|
|
31142
31211
|
prompt,
|
|
31143
|
-
sourceFormat
|
|
31212
|
+
sourceFormat,
|
|
31213
|
+
widgetPathMap
|
|
31144
31214
|
};
|
|
31145
31215
|
}
|
|
31216
|
+
async function inspectWorkflowFile(fullPath, objectInfo) {
|
|
31217
|
+
const content = await fs.readFile(fullPath, "utf8");
|
|
31218
|
+
const defaultName = path.basename(fullPath, ".json");
|
|
31219
|
+
const defaultId = getWorkflowId(fullPath);
|
|
31220
|
+
let parsed;
|
|
31221
|
+
try {
|
|
31222
|
+
parsed = JSON.parse(content);
|
|
31223
|
+
} catch {
|
|
31224
|
+
return void 0;
|
|
31225
|
+
}
|
|
31226
|
+
let name = defaultName;
|
|
31227
|
+
if (isRecord2(parsed)) {
|
|
31228
|
+
if (typeof parsed.name === "string" && parsed.name.trim().length > 0) {
|
|
31229
|
+
name = parsed.name;
|
|
31230
|
+
} else if (isRecord2(parsed.workflow) && typeof parsed.workflow.name === "string" && parsed.workflow.name.trim().length > 0) {
|
|
31231
|
+
name = parsed.workflow.name;
|
|
31232
|
+
}
|
|
31233
|
+
}
|
|
31234
|
+
if (isWebUIFormat(parsed)) {
|
|
31235
|
+
if (!objectInfo) {
|
|
31236
|
+
return {
|
|
31237
|
+
id: defaultId,
|
|
31238
|
+
name,
|
|
31239
|
+
path: fullPath,
|
|
31240
|
+
source_format: "webui",
|
|
31241
|
+
validation_status: "conversion_requires_node_schemas",
|
|
31242
|
+
content,
|
|
31243
|
+
isWebUI: true
|
|
31244
|
+
};
|
|
31245
|
+
}
|
|
31246
|
+
try {
|
|
31247
|
+
await normalizeWorkflowPrompt(parsed, objectInfo);
|
|
31248
|
+
return {
|
|
31249
|
+
id: defaultId,
|
|
31250
|
+
name,
|
|
31251
|
+
path: fullPath,
|
|
31252
|
+
source_format: "webui",
|
|
31253
|
+
validation_status: "conversion_validated",
|
|
31254
|
+
content,
|
|
31255
|
+
isWebUI: true
|
|
31256
|
+
};
|
|
31257
|
+
} catch (error48) {
|
|
31258
|
+
return {
|
|
31259
|
+
id: defaultId,
|
|
31260
|
+
name,
|
|
31261
|
+
path: fullPath,
|
|
31262
|
+
source_format: "webui",
|
|
31263
|
+
validation_status: "conversion_failed",
|
|
31264
|
+
content,
|
|
31265
|
+
error: summarizeErrorMessage(error48),
|
|
31266
|
+
isWebUI: true
|
|
31267
|
+
};
|
|
31268
|
+
}
|
|
31269
|
+
}
|
|
31270
|
+
try {
|
|
31271
|
+
await normalizeWorkflowPrompt(parsed);
|
|
31272
|
+
return {
|
|
31273
|
+
id: defaultId,
|
|
31274
|
+
name,
|
|
31275
|
+
path: fullPath,
|
|
31276
|
+
source_format: "api",
|
|
31277
|
+
validation_status: "ready",
|
|
31278
|
+
content,
|
|
31279
|
+
isWebUI: false
|
|
31280
|
+
};
|
|
31281
|
+
} catch {
|
|
31282
|
+
return void 0;
|
|
31283
|
+
}
|
|
31284
|
+
}
|
|
31146
31285
|
function inferEditableRole(nodeId, classType, inputName, consumerMap) {
|
|
31147
31286
|
if (classType === "CLIPTextEncode" && inputName === "text") {
|
|
31148
31287
|
const consumers = consumerMap.get(nodeId) ?? [];
|
|
@@ -31154,6 +31293,28 @@ function inferEditableRole(nodeId, classType, inputName, consumerMap) {
|
|
|
31154
31293
|
}
|
|
31155
31294
|
return "prompt_text";
|
|
31156
31295
|
}
|
|
31296
|
+
if (inputName === "value") {
|
|
31297
|
+
const consumers = consumerMap.get(nodeId) ?? [];
|
|
31298
|
+
const textConsumers = consumers.filter(
|
|
31299
|
+
(consumer) => consumer.classType === "CLIPTextEncode" && consumer.inputName === "text"
|
|
31300
|
+
);
|
|
31301
|
+
if (textConsumers.length > 0) {
|
|
31302
|
+
for (const consumer of textConsumers) {
|
|
31303
|
+
const clipConsumers = consumerMap.get(consumer.nodeId) ?? [];
|
|
31304
|
+
if (clipConsumers.some(
|
|
31305
|
+
(clipConsumer) => clipConsumer.inputName === "positive"
|
|
31306
|
+
)) {
|
|
31307
|
+
return "positive_prompt";
|
|
31308
|
+
}
|
|
31309
|
+
if (clipConsumers.some(
|
|
31310
|
+
(clipConsumer) => clipConsumer.inputName === "negative"
|
|
31311
|
+
)) {
|
|
31312
|
+
return "negative_prompt";
|
|
31313
|
+
}
|
|
31314
|
+
}
|
|
31315
|
+
return "prompt_text";
|
|
31316
|
+
}
|
|
31317
|
+
}
|
|
31157
31318
|
switch (inputName) {
|
|
31158
31319
|
case "seed":
|
|
31159
31320
|
return "seed";
|
|
@@ -31197,7 +31358,428 @@ function formatValuePreview(value) {
|
|
|
31197
31358
|
return String(value);
|
|
31198
31359
|
}
|
|
31199
31360
|
}
|
|
31200
|
-
function
|
|
31361
|
+
function getInputDefinition(objectInfo, prompt, nodeId, inputName) {
|
|
31362
|
+
if (!objectInfo) {
|
|
31363
|
+
return void 0;
|
|
31364
|
+
}
|
|
31365
|
+
const node = prompt[nodeId];
|
|
31366
|
+
if (!node) {
|
|
31367
|
+
return void 0;
|
|
31368
|
+
}
|
|
31369
|
+
const nodeDefinition = objectInfo[node.class_type];
|
|
31370
|
+
if (!nodeDefinition) {
|
|
31371
|
+
return void 0;
|
|
31372
|
+
}
|
|
31373
|
+
return nodeDefinition.input?.required?.[inputName] ?? nodeDefinition.input?.optional?.[inputName];
|
|
31374
|
+
}
|
|
31375
|
+
function getExpectedTypeName(inputDefinition) {
|
|
31376
|
+
if (!inputDefinition) {
|
|
31377
|
+
return void 0;
|
|
31378
|
+
}
|
|
31379
|
+
const [typeData] = inputDefinition;
|
|
31380
|
+
return Array.isArray(typeData) ? "COMBO" : String(typeData).toUpperCase();
|
|
31381
|
+
}
|
|
31382
|
+
function getInputOptionsSummary(inputDefinition, previewLimit = 8) {
|
|
31383
|
+
if (!inputDefinition) {
|
|
31384
|
+
return void 0;
|
|
31385
|
+
}
|
|
31386
|
+
const [typeData] = inputDefinition;
|
|
31387
|
+
if (!Array.isArray(typeData)) {
|
|
31388
|
+
return void 0;
|
|
31389
|
+
}
|
|
31390
|
+
return {
|
|
31391
|
+
options_count: typeData.length,
|
|
31392
|
+
options_preview: typeData.slice(0, previewLimit),
|
|
31393
|
+
options_truncated: typeData.length > previewLimit
|
|
31394
|
+
};
|
|
31395
|
+
}
|
|
31396
|
+
function isPlainObject3(value) {
|
|
31397
|
+
return isRecord2(value) && !Array.isArray(value);
|
|
31398
|
+
}
|
|
31399
|
+
function getValueTypeName(value) {
|
|
31400
|
+
if (Array.isArray(value)) {
|
|
31401
|
+
return "array";
|
|
31402
|
+
}
|
|
31403
|
+
if (value === null) {
|
|
31404
|
+
return "null";
|
|
31405
|
+
}
|
|
31406
|
+
return typeof value;
|
|
31407
|
+
}
|
|
31408
|
+
function validateOverrideValueType(path2, value, inputDefinition) {
|
|
31409
|
+
if (!inputDefinition) {
|
|
31410
|
+
return;
|
|
31411
|
+
}
|
|
31412
|
+
const [typeData] = inputDefinition;
|
|
31413
|
+
if (Array.isArray(typeData)) {
|
|
31414
|
+
if (!typeData.some((option) => option === value)) {
|
|
31415
|
+
throw new WorkflowInputError(
|
|
31416
|
+
`Invalid override for '${path2}': expected one of ${typeData.map((option) => JSON.stringify(option)).join(", ")}, received ${JSON.stringify(value)}.`,
|
|
31417
|
+
"overrides"
|
|
31418
|
+
);
|
|
31419
|
+
}
|
|
31420
|
+
return;
|
|
31421
|
+
}
|
|
31422
|
+
const typeName = String(typeData).toUpperCase();
|
|
31423
|
+
switch (typeName) {
|
|
31424
|
+
case "INT":
|
|
31425
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
31426
|
+
throw new WorkflowInputError(
|
|
31427
|
+
`Invalid override for '${path2}': expected INT, received ${getValueTypeName(
|
|
31428
|
+
value
|
|
31429
|
+
)}.`,
|
|
31430
|
+
"overrides"
|
|
31431
|
+
);
|
|
31432
|
+
}
|
|
31433
|
+
return;
|
|
31434
|
+
case "FLOAT":
|
|
31435
|
+
if (typeof value !== "number") {
|
|
31436
|
+
throw new WorkflowInputError(
|
|
31437
|
+
`Invalid override for '${path2}': expected FLOAT, received ${getValueTypeName(
|
|
31438
|
+
value
|
|
31439
|
+
)}.`,
|
|
31440
|
+
"overrides"
|
|
31441
|
+
);
|
|
31442
|
+
}
|
|
31443
|
+
return;
|
|
31444
|
+
case "STRING":
|
|
31445
|
+
if (typeof value !== "string") {
|
|
31446
|
+
throw new WorkflowInputError(
|
|
31447
|
+
`Invalid override for '${path2}': expected STRING, received ${getValueTypeName(
|
|
31448
|
+
value
|
|
31449
|
+
)}.`,
|
|
31450
|
+
"overrides"
|
|
31451
|
+
);
|
|
31452
|
+
}
|
|
31453
|
+
return;
|
|
31454
|
+
case "BOOLEAN":
|
|
31455
|
+
if (typeof value !== "boolean") {
|
|
31456
|
+
throw new WorkflowInputError(
|
|
31457
|
+
`Invalid override for '${path2}': expected BOOLEAN, received ${getValueTypeName(
|
|
31458
|
+
value
|
|
31459
|
+
)}.`,
|
|
31460
|
+
"overrides"
|
|
31461
|
+
);
|
|
31462
|
+
}
|
|
31463
|
+
return;
|
|
31464
|
+
default:
|
|
31465
|
+
return;
|
|
31466
|
+
}
|
|
31467
|
+
}
|
|
31468
|
+
function getEditableAliases(canonicalPath, nodeId, inputName, role, widgetPathMap) {
|
|
31469
|
+
const aliases = /* @__PURE__ */ new Set([`${nodeId}.${inputName}`]);
|
|
31470
|
+
if (role) {
|
|
31471
|
+
aliases.add(`${nodeId}.${role}`);
|
|
31472
|
+
}
|
|
31473
|
+
for (const [widgetPath, resolvedPath] of Object.entries(widgetPathMap)) {
|
|
31474
|
+
if (resolvedPath === canonicalPath) {
|
|
31475
|
+
aliases.add(widgetPath);
|
|
31476
|
+
}
|
|
31477
|
+
}
|
|
31478
|
+
aliases.delete(canonicalPath);
|
|
31479
|
+
return [...aliases];
|
|
31480
|
+
}
|
|
31481
|
+
function buildEditableInputIndex(editableInputs) {
|
|
31482
|
+
const entriesByPath = /* @__PURE__ */ new Map();
|
|
31483
|
+
const aliasToPath = /* @__PURE__ */ new Map();
|
|
31484
|
+
const ambiguousAliases = /* @__PURE__ */ new Map();
|
|
31485
|
+
const pathsByNode = /* @__PURE__ */ new Map();
|
|
31486
|
+
for (const entry of editableInputs) {
|
|
31487
|
+
entriesByPath.set(entry.path, entry);
|
|
31488
|
+
const nodeEntries = pathsByNode.get(entry.node_id) ?? [];
|
|
31489
|
+
nodeEntries.push(entry);
|
|
31490
|
+
pathsByNode.set(entry.node_id, nodeEntries);
|
|
31491
|
+
for (const alias of [entry.path, ...entry.aliases]) {
|
|
31492
|
+
const existing = aliasToPath.get(alias);
|
|
31493
|
+
if (!existing) {
|
|
31494
|
+
aliasToPath.set(alias, entry.path);
|
|
31495
|
+
continue;
|
|
31496
|
+
}
|
|
31497
|
+
if (existing === entry.path) {
|
|
31498
|
+
continue;
|
|
31499
|
+
}
|
|
31500
|
+
const ambiguous = new Set(
|
|
31501
|
+
ambiguousAliases.get(alias) ?? [existing]
|
|
31502
|
+
);
|
|
31503
|
+
ambiguous.add(entry.path);
|
|
31504
|
+
ambiguousAliases.set(alias, [...ambiguous].sort());
|
|
31505
|
+
aliasToPath.delete(alias);
|
|
31506
|
+
}
|
|
31507
|
+
}
|
|
31508
|
+
return {
|
|
31509
|
+
entriesByPath,
|
|
31510
|
+
aliasToPath,
|
|
31511
|
+
ambiguousAliases,
|
|
31512
|
+
pathsByNode
|
|
31513
|
+
};
|
|
31514
|
+
}
|
|
31515
|
+
function getPathSegmentHelp(prompt, path2) {
|
|
31516
|
+
const parts = path2.split(".");
|
|
31517
|
+
if (parts.length === 0 || parts[0].length === 0) {
|
|
31518
|
+
return void 0;
|
|
31519
|
+
}
|
|
31520
|
+
const nodeId = parts[0];
|
|
31521
|
+
return {
|
|
31522
|
+
nodeId,
|
|
31523
|
+
nodeExists: isRecord2(prompt[nodeId])
|
|
31524
|
+
};
|
|
31525
|
+
}
|
|
31526
|
+
function describeAvailableNodePaths(editableIndex, nodeId) {
|
|
31527
|
+
if (!editableIndex) {
|
|
31528
|
+
return "";
|
|
31529
|
+
}
|
|
31530
|
+
const entries = editableIndex.pathsByNode.get(nodeId) ?? [];
|
|
31531
|
+
if (entries.length === 0) {
|
|
31532
|
+
return "";
|
|
31533
|
+
}
|
|
31534
|
+
return entries.map((entry) => {
|
|
31535
|
+
const aliasSuffix = entry.aliases.length > 0 ? ` (aliases: ${entry.aliases.join(", ")})` : "";
|
|
31536
|
+
return `${entry.path}${aliasSuffix}`;
|
|
31537
|
+
}).join("\n");
|
|
31538
|
+
}
|
|
31539
|
+
function getPreferredOverridePath(entry) {
|
|
31540
|
+
const roleAlias = entry.role ? `${entry.node_id}.${entry.role}` : void 0;
|
|
31541
|
+
if (roleAlias && entry.aliases.includes(roleAlias)) {
|
|
31542
|
+
return roleAlias;
|
|
31543
|
+
}
|
|
31544
|
+
const inputAlias = `${entry.node_id}.${entry.input_name}`;
|
|
31545
|
+
if (entry.aliases.includes(inputAlias)) {
|
|
31546
|
+
return inputAlias;
|
|
31547
|
+
}
|
|
31548
|
+
const nonWidgetAlias = entry.aliases.find(
|
|
31549
|
+
(alias) => !alias.includes(".widgets[")
|
|
31550
|
+
);
|
|
31551
|
+
return nonWidgetAlias ?? entry.path;
|
|
31552
|
+
}
|
|
31553
|
+
function formatEditableInputOptionsText(entry) {
|
|
31554
|
+
if (entry.options_count === void 0 || entry.options_preview === void 0 || entry.options_preview.length === 0) {
|
|
31555
|
+
return "";
|
|
31556
|
+
}
|
|
31557
|
+
const preview = entry.options_preview.map((option) => formatValuePreview(option)).join(", ");
|
|
31558
|
+
const truncatedSuffix = entry.options_truncated ? ", ..." : "";
|
|
31559
|
+
return ` | options: ${preview}${truncatedSuffix} (${entry.options_count} total)`;
|
|
31560
|
+
}
|
|
31561
|
+
function getEditableInputGroup(entry) {
|
|
31562
|
+
if (entry.role === "positive_prompt" || entry.role === "negative_prompt" || entry.role === "prompt_text") {
|
|
31563
|
+
return "prompts";
|
|
31564
|
+
}
|
|
31565
|
+
if (entry.role === "checkpoint" || entry.input_name === "ckpt_name" || entry.input_name === "model_name" || entry.input_name === "vae_name" || entry.class_type.includes("Checkpoint") || entry.class_type.includes("VAE")) {
|
|
31566
|
+
return "models";
|
|
31567
|
+
}
|
|
31568
|
+
if (entry.role === "seed" || entry.role === "steps" || entry.role === "cfg" || entry.role === "sampler" || entry.role === "scheduler" || entry.role === "denoise" || entry.class_type.includes("Sampler")) {
|
|
31569
|
+
return "sampling";
|
|
31570
|
+
}
|
|
31571
|
+
if (entry.role === "width" || entry.role === "height" || entry.role === "batch_size") {
|
|
31572
|
+
return "image";
|
|
31573
|
+
}
|
|
31574
|
+
if (entry.input_name === "lora_name" || entry.input_name.startsWith("strength_") || entry.class_type.includes("Lora")) {
|
|
31575
|
+
return "loras";
|
|
31576
|
+
}
|
|
31577
|
+
if (entry.class_type.includes("ControlNet") || entry.class_type.includes("IPAdapter") || entry.class_type.includes("FreeU") || entry.class_type.includes("FaceDetailer")) {
|
|
31578
|
+
return "control";
|
|
31579
|
+
}
|
|
31580
|
+
if (entry.role === "filename_prefix" || entry.input_name === "filename" || entry.input_name === "path" || entry.input_name === "extension" || entry.class_type.includes("Save") || entry.class_type.includes("PromptSaver")) {
|
|
31581
|
+
return "output";
|
|
31582
|
+
}
|
|
31583
|
+
return "other";
|
|
31584
|
+
}
|
|
31585
|
+
function getEditableInputPriority(entry) {
|
|
31586
|
+
const rolePriority = {
|
|
31587
|
+
positive_prompt: 300,
|
|
31588
|
+
negative_prompt: 295,
|
|
31589
|
+
prompt_text: 290,
|
|
31590
|
+
checkpoint: 260,
|
|
31591
|
+
seed: 250,
|
|
31592
|
+
steps: 240,
|
|
31593
|
+
cfg: 235,
|
|
31594
|
+
sampler: 230,
|
|
31595
|
+
scheduler: 225,
|
|
31596
|
+
width: 220,
|
|
31597
|
+
height: 219,
|
|
31598
|
+
batch_size: 218,
|
|
31599
|
+
denoise: 215,
|
|
31600
|
+
filename_prefix: 190,
|
|
31601
|
+
text: 180
|
|
31602
|
+
};
|
|
31603
|
+
let priority = rolePriority[entry.role ?? ""] ?? 0;
|
|
31604
|
+
if (entry.input_name === "lora_name") {
|
|
31605
|
+
priority = Math.max(priority, 170);
|
|
31606
|
+
}
|
|
31607
|
+
if (entry.input_name.startsWith("strength_")) {
|
|
31608
|
+
priority = Math.max(priority, 160);
|
|
31609
|
+
}
|
|
31610
|
+
if (entry.class_type.includes("PromptSaver") || entry.class_type === "SaveImage") {
|
|
31611
|
+
priority -= 70;
|
|
31612
|
+
}
|
|
31613
|
+
if (entry.class_type === "PrimitiveStringMultiline" && (entry.role === "positive_prompt" || entry.role === "negative_prompt")) {
|
|
31614
|
+
priority += 30;
|
|
31615
|
+
}
|
|
31616
|
+
return priority;
|
|
31617
|
+
}
|
|
31618
|
+
function getHighSignalEditableInputs(editableInputs, limit = 12) {
|
|
31619
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31620
|
+
const prioritized = [...editableInputs].sort((left, right) => {
|
|
31621
|
+
const priorityDiff = getEditableInputPriority(right) - getEditableInputPriority(left);
|
|
31622
|
+
if (priorityDiff !== 0) {
|
|
31623
|
+
return priorityDiff;
|
|
31624
|
+
}
|
|
31625
|
+
return getPreferredOverridePath(left).localeCompare(
|
|
31626
|
+
getPreferredOverridePath(right),
|
|
31627
|
+
void 0,
|
|
31628
|
+
{ numeric: true }
|
|
31629
|
+
);
|
|
31630
|
+
}).filter((entry) => {
|
|
31631
|
+
const preferredPath = getPreferredOverridePath(entry);
|
|
31632
|
+
if (seen.has(preferredPath)) {
|
|
31633
|
+
return false;
|
|
31634
|
+
}
|
|
31635
|
+
seen.add(preferredPath);
|
|
31636
|
+
return true;
|
|
31637
|
+
});
|
|
31638
|
+
return prioritized.slice(0, limit);
|
|
31639
|
+
}
|
|
31640
|
+
function summarizeEditableInputGroups(editableInputs) {
|
|
31641
|
+
const labels = {
|
|
31642
|
+
prompts: "Prompts",
|
|
31643
|
+
models: "Models and checkpoints",
|
|
31644
|
+
sampling: "Sampling",
|
|
31645
|
+
image: "Image size and batching",
|
|
31646
|
+
loras: "LoRAs and strengths",
|
|
31647
|
+
control: "Conditioning and adapters",
|
|
31648
|
+
output: "Output and persistence",
|
|
31649
|
+
other: "Other"
|
|
31650
|
+
};
|
|
31651
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
31652
|
+
for (const entry of editableInputs) {
|
|
31653
|
+
const group = getEditableInputGroup(entry);
|
|
31654
|
+
const existing = summaries.get(group) ?? {
|
|
31655
|
+
group,
|
|
31656
|
+
label: labels[group],
|
|
31657
|
+
count: 0,
|
|
31658
|
+
sample_paths: []
|
|
31659
|
+
};
|
|
31660
|
+
existing.count += 1;
|
|
31661
|
+
if (existing.sample_paths.length < 3) {
|
|
31662
|
+
existing.sample_paths.push(getPreferredOverridePath(entry));
|
|
31663
|
+
}
|
|
31664
|
+
summaries.set(group, existing);
|
|
31665
|
+
}
|
|
31666
|
+
const order = [
|
|
31667
|
+
"prompts",
|
|
31668
|
+
"models",
|
|
31669
|
+
"sampling",
|
|
31670
|
+
"image",
|
|
31671
|
+
"loras",
|
|
31672
|
+
"control",
|
|
31673
|
+
"output",
|
|
31674
|
+
"other"
|
|
31675
|
+
];
|
|
31676
|
+
return order.map((group) => summaries.get(group)).filter(
|
|
31677
|
+
(summary) => Boolean(summary)
|
|
31678
|
+
);
|
|
31679
|
+
}
|
|
31680
|
+
function formatEditableInputGroupsText(groupSummaries) {
|
|
31681
|
+
if (groupSummaries.length === 0) {
|
|
31682
|
+
return "No editable input groups were detected.";
|
|
31683
|
+
}
|
|
31684
|
+
return formatListItems(groupSummaries, (summary) => {
|
|
31685
|
+
const examples = summary.sample_paths.length > 0 ? ` | examples: ${summary.sample_paths.join(", ")}` : "";
|
|
31686
|
+
return `${summary.label}: ${summary.count}${examples}`;
|
|
31687
|
+
});
|
|
31688
|
+
}
|
|
31689
|
+
function buildOverrideExamples(editableInputs, limit = 5) {
|
|
31690
|
+
const examples = {};
|
|
31691
|
+
const preferredPaths = [];
|
|
31692
|
+
for (const entry of getHighSignalEditableInputs(editableInputs, limit)) {
|
|
31693
|
+
const preferredPath = getPreferredOverridePath(entry);
|
|
31694
|
+
examples[preferredPath] = entry.value;
|
|
31695
|
+
preferredPaths.push(preferredPath);
|
|
31696
|
+
}
|
|
31697
|
+
return {
|
|
31698
|
+
examples,
|
|
31699
|
+
preferred_paths: preferredPaths
|
|
31700
|
+
};
|
|
31701
|
+
}
|
|
31702
|
+
function formatOverrideExamplesText(examples) {
|
|
31703
|
+
return JSON.stringify(examples, null, 2);
|
|
31704
|
+
}
|
|
31705
|
+
function resolveOverridePath(prompt, rawPath, editableIndex) {
|
|
31706
|
+
if (!editableIndex) {
|
|
31707
|
+
return rawPath;
|
|
31708
|
+
}
|
|
31709
|
+
if (editableIndex.entriesByPath.has(rawPath)) {
|
|
31710
|
+
return rawPath;
|
|
31711
|
+
}
|
|
31712
|
+
const ambiguous = editableIndex.ambiguousAliases.get(rawPath);
|
|
31713
|
+
if (ambiguous) {
|
|
31714
|
+
throw new WorkflowInputError(
|
|
31715
|
+
`Ambiguous override path '${rawPath}'. It could refer to: ${ambiguous.join(
|
|
31716
|
+
", "
|
|
31717
|
+
)}. Use a full '<node>.inputs.<field>' path instead.`,
|
|
31718
|
+
"overrides"
|
|
31719
|
+
);
|
|
31720
|
+
}
|
|
31721
|
+
const resolved = editableIndex.aliasToPath.get(rawPath);
|
|
31722
|
+
if (resolved) {
|
|
31723
|
+
return resolved;
|
|
31724
|
+
}
|
|
31725
|
+
const rawParts = rawPath.split(".");
|
|
31726
|
+
if (rawParts.length >= 3 && rawParts[1] === INPUT_SEGMENT) {
|
|
31727
|
+
const pathHelp2 = getPathSegmentHelp(prompt, rawPath);
|
|
31728
|
+
if (pathHelp2?.nodeId && pathHelp2.nodeExists) {
|
|
31729
|
+
const validPaths = describeAvailableNodePaths(
|
|
31730
|
+
editableIndex,
|
|
31731
|
+
pathHelp2.nodeId
|
|
31732
|
+
);
|
|
31733
|
+
if (validPaths.length > 0) {
|
|
31734
|
+
throw new WorkflowInputError(
|
|
31735
|
+
`Unsupported override path '${rawPath}'. Only detected editable inputs can be overridden. Valid editable paths for node ${pathHelp2.nodeId}:
|
|
31736
|
+
${validPaths}`,
|
|
31737
|
+
"overrides"
|
|
31738
|
+
);
|
|
31739
|
+
}
|
|
31740
|
+
}
|
|
31741
|
+
}
|
|
31742
|
+
const pathHelp = getPathSegmentHelp(prompt, rawPath);
|
|
31743
|
+
if (pathHelp?.nodeId && pathHelp.nodeExists) {
|
|
31744
|
+
const validPaths = describeAvailableNodePaths(
|
|
31745
|
+
editableIndex,
|
|
31746
|
+
pathHelp.nodeId
|
|
31747
|
+
);
|
|
31748
|
+
if (validPaths.length > 0) {
|
|
31749
|
+
throw new WorkflowInputError(
|
|
31750
|
+
`Unknown override path '${rawPath}'. Valid editable paths for node ${pathHelp.nodeId}:
|
|
31751
|
+
${validPaths}`,
|
|
31752
|
+
"overrides"
|
|
31753
|
+
);
|
|
31754
|
+
}
|
|
31755
|
+
}
|
|
31756
|
+
return rawPath;
|
|
31757
|
+
}
|
|
31758
|
+
function parseOverrides(rawOverrides) {
|
|
31759
|
+
if (rawOverrides === void 0) {
|
|
31760
|
+
return void 0;
|
|
31761
|
+
}
|
|
31762
|
+
if (typeof rawOverrides !== "string") {
|
|
31763
|
+
return rawOverrides;
|
|
31764
|
+
}
|
|
31765
|
+
let parsed;
|
|
31766
|
+
try {
|
|
31767
|
+
parsed = JSON.parse(rawOverrides);
|
|
31768
|
+
} catch (error48) {
|
|
31769
|
+
throw new WorkflowInputError(
|
|
31770
|
+
`Overrides must be a valid JSON object string: ${error48 instanceof Error ? error48.message : String(error48)}`,
|
|
31771
|
+
"overrides"
|
|
31772
|
+
);
|
|
31773
|
+
}
|
|
31774
|
+
if (!isPlainObject3(parsed)) {
|
|
31775
|
+
throw new WorkflowInputError(
|
|
31776
|
+
"Overrides JSON string must decode to an object.",
|
|
31777
|
+
"overrides"
|
|
31778
|
+
);
|
|
31779
|
+
}
|
|
31780
|
+
return parsed;
|
|
31781
|
+
}
|
|
31782
|
+
function analyzeEditableInputs(prompt, objectInfo, widgetPathMap = {}) {
|
|
31201
31783
|
const consumerMap = /* @__PURE__ */ new Map();
|
|
31202
31784
|
for (const [nodeId, node] of Object.entries(prompt)) {
|
|
31203
31785
|
for (const [inputName, value] of Object.entries(node.inputs)) {
|
|
@@ -31232,23 +31814,186 @@ function analyzeEditableInputs(prompt) {
|
|
|
31232
31814
|
node.class_type,
|
|
31233
31815
|
inputName,
|
|
31234
31816
|
consumerMap
|
|
31817
|
+
),
|
|
31818
|
+
aliases: [],
|
|
31819
|
+
expected_type: getExpectedTypeName(
|
|
31820
|
+
getInputDefinition(objectInfo, prompt, nodeId, inputName)
|
|
31821
|
+
),
|
|
31822
|
+
...getInputOptionsSummary(
|
|
31823
|
+
getInputDefinition(objectInfo, prompt, nodeId, inputName)
|
|
31235
31824
|
)
|
|
31236
31825
|
});
|
|
31237
31826
|
}
|
|
31238
31827
|
}
|
|
31239
|
-
return editableInputs.
|
|
31828
|
+
return editableInputs.map((entry) => ({
|
|
31829
|
+
...entry,
|
|
31830
|
+
aliases: getEditableAliases(
|
|
31831
|
+
entry.path,
|
|
31832
|
+
entry.node_id,
|
|
31833
|
+
entry.input_name,
|
|
31834
|
+
entry.role,
|
|
31835
|
+
widgetPathMap
|
|
31836
|
+
)
|
|
31837
|
+
})).sort(
|
|
31240
31838
|
(left, right) => left.path.localeCompare(right.path, void 0, { numeric: true })
|
|
31241
31839
|
);
|
|
31242
31840
|
}
|
|
31243
|
-
function formatEditableInputsText(editableInputs) {
|
|
31841
|
+
function formatEditableInputsText(editableInputs, options = {}) {
|
|
31244
31842
|
if (editableInputs.length === 0) {
|
|
31245
31843
|
return "No literal editable inputs were detected.";
|
|
31246
31844
|
}
|
|
31247
31845
|
return formatListItems(editableInputs, (entry) => {
|
|
31248
31846
|
const role = entry.role ? ` [${entry.role}]` : "";
|
|
31249
31847
|
const classType = entry.class_type ? ` (${entry.class_type})` : "";
|
|
31250
|
-
|
|
31848
|
+
const expectedType = entry.expected_type ? ` <${entry.expected_type}>` : "";
|
|
31849
|
+
const aliasSuffix = entry.aliases.length > 0 ? ` | aliases: ${entry.aliases.join(", ")}` : "";
|
|
31850
|
+
const optionsSuffix = options.includeOptions ? formatEditableInputOptionsText(entry) : "";
|
|
31851
|
+
return `${entry.path}${role}${classType}${expectedType}: ${entry.value_preview}${aliasSuffix}${optionsSuffix}`;
|
|
31852
|
+
});
|
|
31853
|
+
}
|
|
31854
|
+
function validatePromptPreflight(prompt, objectInfo, widgetPathMap = {}) {
|
|
31855
|
+
if (!objectInfo) {
|
|
31856
|
+
return {
|
|
31857
|
+
checked_inputs: 0,
|
|
31858
|
+
combo_inputs: 0
|
|
31859
|
+
};
|
|
31860
|
+
}
|
|
31861
|
+
const editableInputs = analyzeEditableInputs(
|
|
31862
|
+
prompt,
|
|
31863
|
+
objectInfo,
|
|
31864
|
+
widgetPathMap
|
|
31865
|
+
);
|
|
31866
|
+
const issues = [];
|
|
31867
|
+
let checkedInputs = 0;
|
|
31868
|
+
let comboInputs = 0;
|
|
31869
|
+
for (const entry of editableInputs) {
|
|
31870
|
+
const inputDefinition = getInputDefinition(
|
|
31871
|
+
objectInfo,
|
|
31872
|
+
prompt,
|
|
31873
|
+
entry.node_id,
|
|
31874
|
+
entry.input_name
|
|
31875
|
+
);
|
|
31876
|
+
if (!inputDefinition) {
|
|
31877
|
+
continue;
|
|
31878
|
+
}
|
|
31879
|
+
checkedInputs += 1;
|
|
31880
|
+
if (Array.isArray(inputDefinition[0])) {
|
|
31881
|
+
comboInputs += 1;
|
|
31882
|
+
}
|
|
31883
|
+
try {
|
|
31884
|
+
validateOverrideValueType(entry.path, entry.value, inputDefinition);
|
|
31885
|
+
} catch (error48) {
|
|
31886
|
+
if (!(error48 instanceof WorkflowInputError)) {
|
|
31887
|
+
throw error48;
|
|
31888
|
+
}
|
|
31889
|
+
const preferredPath = getPreferredOverridePath(entry);
|
|
31890
|
+
const comboHint = formatEditableInputOptionsText(entry);
|
|
31891
|
+
const issueMessage = entry.expected_type === "COMBO" && entry.options_count !== void 0 ? `${formatValuePreview(entry.value)} is not in the server-reported options for ${preferredPath}.` : error48.message;
|
|
31892
|
+
issues.push(`- ${preferredPath}: ${issueMessage}${comboHint}`);
|
|
31893
|
+
}
|
|
31894
|
+
}
|
|
31895
|
+
if (issues.length > 0) {
|
|
31896
|
+
throw new WorkflowInputError(
|
|
31897
|
+
`Workflow preflight failed before submission.
|
|
31898
|
+
${issues.join("\n")}`,
|
|
31899
|
+
"workflow"
|
|
31900
|
+
);
|
|
31901
|
+
}
|
|
31902
|
+
return {
|
|
31903
|
+
checked_inputs: checkedInputs,
|
|
31904
|
+
combo_inputs: comboInputs
|
|
31905
|
+
};
|
|
31906
|
+
}
|
|
31907
|
+
function formatInputDefinitionSummary(inputName, inputDefinition) {
|
|
31908
|
+
const [typeData, extraInfo] = inputDefinition;
|
|
31909
|
+
const typeLabel = Array.isArray(typeData) ? `COMBO (${typeData.length} options)` : String(typeData).toUpperCase();
|
|
31910
|
+
const details = [];
|
|
31911
|
+
if (Array.isArray(typeData)) {
|
|
31912
|
+
details.push(
|
|
31913
|
+
`options: ${typeData.slice(0, 5).map((option) => JSON.stringify(option)).join(", ")}${typeData.length > 5 ? ", ..." : ""}`
|
|
31914
|
+
);
|
|
31915
|
+
}
|
|
31916
|
+
if (isRecord2(extraInfo)) {
|
|
31917
|
+
if (extraInfo.default !== void 0) {
|
|
31918
|
+
details.push(`default: ${formatValuePreview(extraInfo.default)}`);
|
|
31919
|
+
}
|
|
31920
|
+
if (typeof extraInfo.min === "number") {
|
|
31921
|
+
details.push(`min: ${extraInfo.min}`);
|
|
31922
|
+
}
|
|
31923
|
+
if (typeof extraInfo.max === "number") {
|
|
31924
|
+
details.push(`max: ${extraInfo.max}`);
|
|
31925
|
+
}
|
|
31926
|
+
}
|
|
31927
|
+
return `${inputName} <${typeLabel}>${details.length > 0 ? ` | ${details.join(" | ")}` : ""}`;
|
|
31928
|
+
}
|
|
31929
|
+
function formatNodeDefinitionText(nodeClass, node) {
|
|
31930
|
+
const requiredInputs = Object.entries(node.input.required ?? {});
|
|
31931
|
+
const optionalInputs = Object.entries(node.input.optional ?? {});
|
|
31932
|
+
const lines = [
|
|
31933
|
+
`Node: ${nodeClass}`,
|
|
31934
|
+
`Display name: ${node.display_name || node.name || nodeClass}`,
|
|
31935
|
+
`Category: ${node.category || "uncategorized"}`
|
|
31936
|
+
];
|
|
31937
|
+
if (typeof node.description === "string" && node.description.trim().length > 0) {
|
|
31938
|
+
lines.push(`Description: ${node.description.trim()}`);
|
|
31939
|
+
}
|
|
31940
|
+
lines.push(
|
|
31941
|
+
`Required inputs (${requiredInputs.length}): ${requiredInputs.length > 0 ? requiredInputs.map(
|
|
31942
|
+
([inputName, inputDefinition]) => formatInputDefinitionSummary(
|
|
31943
|
+
inputName,
|
|
31944
|
+
inputDefinition
|
|
31945
|
+
)
|
|
31946
|
+
).join("\n") : "None."}`
|
|
31947
|
+
);
|
|
31948
|
+
lines.push(
|
|
31949
|
+
`Optional inputs (${optionalInputs.length}): ${optionalInputs.length > 0 ? optionalInputs.map(
|
|
31950
|
+
([inputName, inputDefinition]) => formatInputDefinitionSummary(
|
|
31951
|
+
inputName,
|
|
31952
|
+
inputDefinition
|
|
31953
|
+
)
|
|
31954
|
+
).join("\n") : "None."}`
|
|
31955
|
+
);
|
|
31956
|
+
lines.push(`Outputs: ${node.output.join(", ") || "None."}`);
|
|
31957
|
+
return lines.join("\n");
|
|
31958
|
+
}
|
|
31959
|
+
function formatNodeListText(classes, rawInfo, pagination) {
|
|
31960
|
+
const text = `Available nodes (${pagination.total} total):
|
|
31961
|
+
` + formatListItems(classes, (nodeClass) => {
|
|
31962
|
+
const node = rawInfo[nodeClass];
|
|
31963
|
+
const displayName = node.display_name && node.display_name !== nodeClass ? ` \u2014 ${node.display_name}` : "";
|
|
31964
|
+
const category = node.category ? ` | category: ${node.category}` : "";
|
|
31965
|
+
const description = typeof node.description === "string" && node.description.trim().length > 0 ? ` | ${node.description.trim()}` : "";
|
|
31966
|
+
return `${nodeClass}${displayName}${category}${description}`;
|
|
31251
31967
|
});
|
|
31968
|
+
if (!pagination.hasMore) {
|
|
31969
|
+
return text;
|
|
31970
|
+
}
|
|
31971
|
+
return `${text}
|
|
31972
|
+
|
|
31973
|
+
${formatPaginationFooter(
|
|
31974
|
+
pagination.offset,
|
|
31975
|
+
pagination.limit,
|
|
31976
|
+
pagination.total
|
|
31977
|
+
)}`;
|
|
31978
|
+
}
|
|
31979
|
+
async function getQueueSnapshot() {
|
|
31980
|
+
try {
|
|
31981
|
+
return IS_MOCK ? MOCK_FIXTURES.queue : await fetchComfyJson("/queue");
|
|
31982
|
+
} catch {
|
|
31983
|
+
return void 0;
|
|
31984
|
+
}
|
|
31985
|
+
}
|
|
31986
|
+
function formatQueueSnapshotText(queueSnapshot) {
|
|
31987
|
+
if (!queueSnapshot) {
|
|
31988
|
+
return "";
|
|
31989
|
+
}
|
|
31990
|
+
return `Queue snapshot: ${queueSnapshot.queue_running.length} running, ${queueSnapshot.queue_pending.length} pending.`;
|
|
31991
|
+
}
|
|
31992
|
+
function getPromptQueueVisibility(promptId, queueSnapshot) {
|
|
31993
|
+
if (!queueSnapshot) {
|
|
31994
|
+
return null;
|
|
31995
|
+
}
|
|
31996
|
+
return JSON.stringify(queueSnapshot).includes(promptId);
|
|
31252
31997
|
}
|
|
31253
31998
|
function deriveSavedWorkflowFileName(sourcePath, requestedName) {
|
|
31254
31999
|
if (requestedName) {
|
|
@@ -31259,21 +32004,39 @@ function deriveSavedWorkflowFileName(sourcePath, requestedName) {
|
|
|
31259
32004
|
}
|
|
31260
32005
|
return `workflow-${Date.now()}.json`;
|
|
31261
32006
|
}
|
|
31262
|
-
function applyOverrides(prompt, overrides) {
|
|
31263
|
-
for (const [
|
|
32007
|
+
function applyOverrides(prompt, overrides, options = {}) {
|
|
32008
|
+
for (const [rawKey, value] of Object.entries(overrides)) {
|
|
32009
|
+
const key = resolveOverridePath(prompt, rawKey, options.editableIndex);
|
|
31264
32010
|
const parts = key.split(".");
|
|
31265
32011
|
let current = prompt;
|
|
31266
32012
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
31267
32013
|
const part = parts[i];
|
|
31268
32014
|
const next = current[part];
|
|
31269
32015
|
if (!isRecord2(next)) {
|
|
32016
|
+
const pathHelp = getPathSegmentHelp(prompt, key);
|
|
32017
|
+
const validPaths = pathHelp?.nodeId && pathHelp.nodeExists ? describeAvailableNodePaths(
|
|
32018
|
+
options.editableIndex,
|
|
32019
|
+
pathHelp.nodeId
|
|
32020
|
+
) : "";
|
|
32021
|
+
const validPathSuffix = validPaths.length > 0 ? `
|
|
32022
|
+
Valid editable paths for node ${pathHelp?.nodeId}:
|
|
32023
|
+
${validPaths}` : "";
|
|
31270
32024
|
throw new WorkflowInputError(
|
|
31271
|
-
`Invalid override path: '${
|
|
32025
|
+
`Invalid override path: '${rawKey}'. The resolved path '${key}' failed at segment '${part}'.${validPathSuffix}`,
|
|
31272
32026
|
"overrides"
|
|
31273
32027
|
);
|
|
31274
32028
|
}
|
|
31275
32029
|
current = next;
|
|
31276
32030
|
}
|
|
32031
|
+
if (parts.length === 3 && parts[1] === INPUT_SEGMENT && typeof parts[0] === "string" && typeof parts[2] === "string") {
|
|
32032
|
+
const inputDefinition = getInputDefinition(
|
|
32033
|
+
options.objectInfo,
|
|
32034
|
+
prompt,
|
|
32035
|
+
parts[0],
|
|
32036
|
+
parts[2]
|
|
32037
|
+
);
|
|
32038
|
+
validateOverrideValueType(key, value, inputDefinition);
|
|
32039
|
+
}
|
|
31277
32040
|
current[parts[parts.length - 1]] = value;
|
|
31278
32041
|
}
|
|
31279
32042
|
}
|
|
@@ -31289,13 +32052,12 @@ var DiscoverWorkflowsSchema = paginationSchema.extend({
|
|
|
31289
32052
|
directory_path: z3.string().optional().describe("Custom directory path to scan for workflows.")
|
|
31290
32053
|
});
|
|
31291
32054
|
var WorkflowRunSchema = z3.object({
|
|
32055
|
+
workflow: z3.unknown().optional().describe("Workflow JSON object to execute immediately."),
|
|
31292
32056
|
workflow_file_path: z3.string().optional().describe(
|
|
31293
32057
|
"Path or filename of workflow JSON. Scans default dirs if simple name provided."
|
|
31294
32058
|
),
|
|
31295
32059
|
workflow_id: z3.string().optional().describe("Workflow identifier returned by discover_workflows."),
|
|
31296
|
-
overrides:
|
|
31297
|
-
'Key-value pairs to override using dot-notation. e.g. {"6.inputs.text": "dragon"}'
|
|
31298
|
-
),
|
|
32060
|
+
overrides: OverridesSchema.optional(),
|
|
31299
32061
|
await: z3.boolean().default(true).describe("Wait for completion and return native images."),
|
|
31300
32062
|
timeout: z3.number().int().min(1).default(120).describe("Wait timeout in seconds.")
|
|
31301
32063
|
});
|
|
@@ -31308,7 +32070,7 @@ var SaveWorkflowSchema = z3.object({
|
|
|
31308
32070
|
workflow: z3.unknown().optional().describe("Workflow JSON object to save locally."),
|
|
31309
32071
|
workflow_file_path: z3.string().optional().describe("Existing workflow path or filename to save."),
|
|
31310
32072
|
workflow_id: z3.string().optional().describe("Existing workflow identifier to save."),
|
|
31311
|
-
overrides:
|
|
32073
|
+
overrides: OverridesSchema.optional(),
|
|
31312
32074
|
output_file_name: z3.string().optional().describe("Filename to write locally. (default: anchored to ./)"),
|
|
31313
32075
|
overwrite: z3.boolean().default(false).describe("Replace an existing local file when true."),
|
|
31314
32076
|
include_prompt: z3.boolean().default(false).describe("Include saved API JSON in structuredContent.")
|
|
@@ -31329,7 +32091,10 @@ async function handleInspectNode(args) {
|
|
|
31329
32091
|
}
|
|
31330
32092
|
return {
|
|
31331
32093
|
content: [
|
|
31332
|
-
{
|
|
32094
|
+
{
|
|
32095
|
+
type: "text",
|
|
32096
|
+
text: formatNodeDefinitionText(args.node_class, node)
|
|
32097
|
+
}
|
|
31333
32098
|
],
|
|
31334
32099
|
structuredContent: { ...node }
|
|
31335
32100
|
};
|
|
@@ -31348,16 +32113,18 @@ async function handleInspectNode(args) {
|
|
|
31348
32113
|
for (const c of paginated.items) {
|
|
31349
32114
|
resultNodes[c] = rawInfo[c];
|
|
31350
32115
|
}
|
|
31351
|
-
const text = `Available nodes (${paginated.total} total):
|
|
31352
|
-
` + paginated.items.join(", ") + (paginated.hasMore ? `
|
|
31353
|
-
|
|
31354
|
-
${formatPaginationFooter(
|
|
31355
|
-
paginated.offset,
|
|
31356
|
-
paginated.limit,
|
|
31357
|
-
paginated.total
|
|
31358
|
-
)}` : "");
|
|
31359
32116
|
return {
|
|
31360
|
-
content: [
|
|
32117
|
+
content: [
|
|
32118
|
+
{
|
|
32119
|
+
type: "text",
|
|
32120
|
+
text: formatNodeListText(paginated.items, rawInfo, {
|
|
32121
|
+
total: paginated.total,
|
|
32122
|
+
offset: paginated.offset,
|
|
32123
|
+
limit: paginated.limit,
|
|
32124
|
+
hasMore: paginated.hasMore
|
|
32125
|
+
})
|
|
32126
|
+
}
|
|
32127
|
+
],
|
|
31361
32128
|
structuredContent: {
|
|
31362
32129
|
nodes: resultNodes,
|
|
31363
32130
|
pagination: {
|
|
@@ -31377,42 +32144,27 @@ function getWorkflowId(absolutePath) {
|
|
|
31377
32144
|
}
|
|
31378
32145
|
async function handleDiscoverWorkflows(args) {
|
|
31379
32146
|
const pathsToScan = getWorkflowDirectories(args.directory_path);
|
|
32147
|
+
const objectInfoAvailability = await tryGetObjectInfo();
|
|
31380
32148
|
const foundWorkflows = [];
|
|
32149
|
+
let skippedNonWorkflowFiles = 0;
|
|
31381
32150
|
for (const dir of pathsToScan) {
|
|
31382
32151
|
try {
|
|
31383
32152
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
31384
32153
|
for (const entry of entries) {
|
|
31385
32154
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
31386
32155
|
const fullPath = path.join(dir, entry.name);
|
|
31387
|
-
const defaultId = getWorkflowId(fullPath);
|
|
31388
|
-
const defaultName = entry.name.replace(/\.json$/, "");
|
|
31389
32156
|
try {
|
|
31390
|
-
const
|
|
31391
|
-
|
|
31392
|
-
|
|
31393
|
-
|
|
31394
|
-
|
|
31395
|
-
|
|
31396
|
-
|
|
31397
|
-
|
|
31398
|
-
}
|
|
31399
|
-
if (typeof json2.name === "string") name = json2.name;
|
|
31400
|
-
else if (json2.workflow && typeof json2.workflow.name === "string")
|
|
31401
|
-
name = json2.workflow.name;
|
|
32157
|
+
const inspected = await inspectWorkflowFile(
|
|
32158
|
+
fullPath,
|
|
32159
|
+
objectInfoAvailability.objectInfo
|
|
32160
|
+
);
|
|
32161
|
+
if (inspected) {
|
|
32162
|
+
foundWorkflows.push(inspected);
|
|
32163
|
+
} else {
|
|
32164
|
+
skippedNonWorkflowFiles += 1;
|
|
31402
32165
|
}
|
|
31403
|
-
|
|
31404
|
-
|
|
31405
|
-
name,
|
|
31406
|
-
path: fullPath,
|
|
31407
|
-
isWebUI
|
|
31408
|
-
});
|
|
31409
|
-
} catch (e) {
|
|
31410
|
-
foundWorkflows.push({
|
|
31411
|
-
id: defaultId,
|
|
31412
|
-
name: defaultName,
|
|
31413
|
-
path: fullPath,
|
|
31414
|
-
error: String(e)
|
|
31415
|
-
});
|
|
32166
|
+
} catch {
|
|
32167
|
+
skippedNonWorkflowFiles += 1;
|
|
31416
32168
|
}
|
|
31417
32169
|
}
|
|
31418
32170
|
}
|
|
@@ -31420,14 +32172,14 @@ async function handleDiscoverWorkflows(args) {
|
|
|
31420
32172
|
}
|
|
31421
32173
|
}
|
|
31422
32174
|
if (foundWorkflows.length === 0) {
|
|
31423
|
-
const
|
|
32175
|
+
const text = `No workflows found in searched directories.
|
|
31424
32176
|
|
|
31425
32177
|
Searched paths:
|
|
31426
32178
|
${pathsToScan.map((p) => `- ${p}`).join("\n")}
|
|
31427
32179
|
|
|
31428
32180
|
Hint: If your workflows are stored elsewhere, provide a custom path using the 'directory_path' parameter.`;
|
|
31429
32181
|
return {
|
|
31430
|
-
content: [{ type: "text", text
|
|
32182
|
+
content: [{ type: "text", text }],
|
|
31431
32183
|
structuredContent: {
|
|
31432
32184
|
workflows: [],
|
|
31433
32185
|
searched_paths: pathsToScan
|
|
@@ -31435,23 +32187,47 @@ Hint: If your workflows are stored elsewhere, provide a custom path using the 'd
|
|
|
31435
32187
|
};
|
|
31436
32188
|
}
|
|
31437
32189
|
const paginated = applyPagination(foundWorkflows, args);
|
|
31438
|
-
const
|
|
31439
|
-
`
|
|
31440
|
-
|
|
31441
|
-
|
|
31442
|
-
|
|
31443
|
-
|
|
31444
|
-
|
|
31445
|
-
|
|
31446
|
-
|
|
31447
|
-
|
|
31448
|
-
|
|
31449
|
-
|
|
31450
|
-
|
|
32190
|
+
const textSections = [
|
|
32191
|
+
`Found ${paginated.total} workflow files:`,
|
|
32192
|
+
formatListItems(paginated.items, (workflow) => {
|
|
32193
|
+
const formatTag = workflow.source_format === "webui" ? workflow.validation_status === "conversion_validated" ? "\u26A0\uFE0F Web UI graph (conversion validated)" : workflow.validation_status === "conversion_requires_node_schemas" ? "\u26A0\uFE0F Web UI graph (needs node schemas to validate)" : "\u274C Web UI graph (conversion failed)" : "\u2705 API workflow";
|
|
32194
|
+
let line = `[ID: ${workflow.id}] [${formatTag}] Name: "${workflow.name}"`;
|
|
32195
|
+
if (workflow.error) {
|
|
32196
|
+
line += ` (Validation error: ${workflow.error})`;
|
|
32197
|
+
}
|
|
32198
|
+
return line;
|
|
32199
|
+
})
|
|
32200
|
+
];
|
|
32201
|
+
if (skippedNonWorkflowFiles > 0) {
|
|
32202
|
+
textSections.push(
|
|
32203
|
+
`Skipped ${skippedNonWorkflowFiles} non-workflow JSON file(s).`
|
|
32204
|
+
);
|
|
32205
|
+
}
|
|
32206
|
+
if (objectInfoAvailability.warning) {
|
|
32207
|
+
textSections.push(objectInfoAvailability.warning);
|
|
32208
|
+
}
|
|
32209
|
+
if (paginated.hasMore) {
|
|
32210
|
+
textSections.push(
|
|
32211
|
+
formatPaginationFooter(
|
|
32212
|
+
paginated.offset,
|
|
32213
|
+
paginated.limit,
|
|
32214
|
+
paginated.total
|
|
32215
|
+
)
|
|
32216
|
+
);
|
|
32217
|
+
}
|
|
31451
32218
|
return {
|
|
31452
|
-
content: [{ type: "text", text }],
|
|
32219
|
+
content: [{ type: "text", text: textSections.join("\n\n") }],
|
|
31453
32220
|
structuredContent: {
|
|
31454
|
-
workflows: paginated.items
|
|
32221
|
+
workflows: paginated.items.map((workflow) => ({
|
|
32222
|
+
id: workflow.id,
|
|
32223
|
+
name: workflow.name,
|
|
32224
|
+
path: workflow.path,
|
|
32225
|
+
isWebUI: workflow.isWebUI,
|
|
32226
|
+
source_format: workflow.source_format,
|
|
32227
|
+
validation_status: workflow.validation_status,
|
|
32228
|
+
...workflow.error ? { error: workflow.error } : {}
|
|
32229
|
+
})),
|
|
32230
|
+
skipped_non_workflow_files: skippedNonWorkflowFiles,
|
|
31455
32231
|
pagination: {
|
|
31456
32232
|
total: paginated.total,
|
|
31457
32233
|
offset: paginated.offset,
|
|
@@ -31480,7 +32256,8 @@ async function waitForWorkflowResult(prompt_id, timeout_seconds) {
|
|
|
31480
32256
|
}
|
|
31481
32257
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
31482
32258
|
}
|
|
31483
|
-
|
|
32259
|
+
const queueSnapshot = await getQueueSnapshot();
|
|
32260
|
+
throw new WorkflowTimeoutError(prompt_id, timeout_seconds, queueSnapshot);
|
|
31484
32261
|
}
|
|
31485
32262
|
async function handleWaitForWorkflow(args) {
|
|
31486
32263
|
try {
|
|
@@ -31552,21 +32329,53 @@ async function handleWaitForWorkflow(args) {
|
|
|
31552
32329
|
}
|
|
31553
32330
|
async function handleGetWorkflow(args) {
|
|
31554
32331
|
try {
|
|
31555
|
-
const
|
|
31556
|
-
const
|
|
32332
|
+
const objectInfoAvailability = await tryGetObjectInfo();
|
|
32333
|
+
const loadedWorkflow = await loadWorkflow(
|
|
32334
|
+
args,
|
|
32335
|
+
objectInfoAvailability.objectInfo
|
|
32336
|
+
);
|
|
32337
|
+
const editableInputs = analyzeEditableInputs(
|
|
32338
|
+
loadedWorkflow.prompt,
|
|
32339
|
+
objectInfoAvailability.objectInfo,
|
|
32340
|
+
loadedWorkflow.widgetPathMap
|
|
32341
|
+
);
|
|
32342
|
+
const highSignalInputs = getHighSignalEditableInputs(editableInputs);
|
|
32343
|
+
const editableInputGroups = summarizeEditableInputGroups(editableInputs);
|
|
32344
|
+
const overrideExamples = buildOverrideExamples(editableInputs);
|
|
31557
32345
|
const workflowId = getWorkflowId(loadedWorkflow.targetPath);
|
|
31558
|
-
const
|
|
32346
|
+
const textSections = [
|
|
31559
32347
|
`Workflow ${workflowId} loaded from ${loadedWorkflow.targetPath}.`,
|
|
31560
|
-
loadedWorkflow.sourceFormat === "webui" ? "Source format: Web UI (normalized to API format)." : "Source format: API format."
|
|
31561
|
-
|
|
31562
|
-
|
|
31563
|
-
|
|
32348
|
+
loadedWorkflow.sourceFormat === "webui" ? "Source format: Web UI (normalized to API format)." : "Source format: API format."
|
|
32349
|
+
];
|
|
32350
|
+
if (objectInfoAvailability.warning) {
|
|
32351
|
+
textSections.push(objectInfoAvailability.warning);
|
|
32352
|
+
}
|
|
32353
|
+
textSections.push(
|
|
32354
|
+
`High-signal overrides (${highSignalInputs.length}):`,
|
|
32355
|
+
formatEditableInputsText(highSignalInputs, {
|
|
32356
|
+
includeOptions: true
|
|
32357
|
+
}),
|
|
32358
|
+
"Editable input groups:",
|
|
32359
|
+
formatEditableInputGroupsText(editableInputGroups),
|
|
32360
|
+
`Editable inputs (${editableInputs.length} total):`,
|
|
32361
|
+
formatEditableInputsText(editableInputs),
|
|
32362
|
+
"Copy-ready override example:",
|
|
32363
|
+
formatOverrideExamplesText(overrideExamples.examples)
|
|
32364
|
+
);
|
|
32365
|
+
const text = textSections.join("\n");
|
|
31564
32366
|
const structuredContent = {
|
|
31565
32367
|
workflow_id: workflowId,
|
|
31566
32368
|
path: loadedWorkflow.targetPath,
|
|
31567
32369
|
source_format: loadedWorkflow.sourceFormat,
|
|
31568
|
-
editable_inputs: editableInputs
|
|
32370
|
+
editable_inputs: editableInputs,
|
|
32371
|
+
high_signal_inputs: highSignalInputs,
|
|
32372
|
+
input_groups: editableInputGroups,
|
|
32373
|
+
override_examples: overrideExamples.examples,
|
|
32374
|
+
preferred_override_paths: overrideExamples.preferred_paths
|
|
31569
32375
|
};
|
|
32376
|
+
if (objectInfoAvailability.warning) {
|
|
32377
|
+
structuredContent.warnings = [objectInfoAvailability.warning];
|
|
32378
|
+
}
|
|
31570
32379
|
if (args.include_prompt) {
|
|
31571
32380
|
structuredContent.prompt = loadedWorkflow.prompt;
|
|
31572
32381
|
}
|
|
@@ -31581,10 +32390,17 @@ async function handleGetWorkflow(args) {
|
|
|
31581
32390
|
async function handleSaveWorkflow(args) {
|
|
31582
32391
|
try {
|
|
31583
32392
|
let prompt;
|
|
32393
|
+
let widgetPathMap = {};
|
|
31584
32394
|
let sourcePath;
|
|
32395
|
+
const parsedOverrides = parseOverrides(args.overrides);
|
|
32396
|
+
const objectInfoAvailability = await tryGetObjectInfo();
|
|
31585
32397
|
if (args.workflow !== void 0) {
|
|
31586
|
-
const normalized = await normalizeWorkflowPrompt(
|
|
32398
|
+
const normalized = await normalizeWorkflowPrompt(
|
|
32399
|
+
args.workflow,
|
|
32400
|
+
objectInfoAvailability.objectInfo
|
|
32401
|
+
);
|
|
31587
32402
|
prompt = normalized.prompt;
|
|
32403
|
+
widgetPathMap = normalized.widgetPathMap;
|
|
31588
32404
|
} else {
|
|
31589
32405
|
if (!args.workflow_file_path && !args.workflow_id) {
|
|
31590
32406
|
return createValidationError(
|
|
@@ -31592,12 +32408,25 @@ async function handleSaveWorkflow(args) {
|
|
|
31592
32408
|
"Provide workflow JSON or an existing workflow reference."
|
|
31593
32409
|
);
|
|
31594
32410
|
}
|
|
31595
|
-
const loadedWorkflow = await loadWorkflow(
|
|
32411
|
+
const loadedWorkflow = await loadWorkflow(
|
|
32412
|
+
args,
|
|
32413
|
+
objectInfoAvailability.objectInfo
|
|
32414
|
+
);
|
|
31596
32415
|
prompt = loadedWorkflow.prompt;
|
|
31597
32416
|
sourcePath = loadedWorkflow.targetPath;
|
|
32417
|
+
widgetPathMap = loadedWorkflow.widgetPathMap;
|
|
31598
32418
|
}
|
|
31599
|
-
|
|
31600
|
-
|
|
32419
|
+
const editableInputs = analyzeEditableInputs(
|
|
32420
|
+
prompt,
|
|
32421
|
+
objectInfoAvailability.objectInfo,
|
|
32422
|
+
widgetPathMap
|
|
32423
|
+
);
|
|
32424
|
+
const editableIndex = buildEditableInputIndex(editableInputs);
|
|
32425
|
+
if (parsedOverrides) {
|
|
32426
|
+
applyOverrides(prompt, parsedOverrides, {
|
|
32427
|
+
editableIndex,
|
|
32428
|
+
objectInfo: objectInfoAvailability.objectInfo
|
|
32429
|
+
});
|
|
31601
32430
|
}
|
|
31602
32431
|
const outputFileName = deriveSavedWorkflowFileName(
|
|
31603
32432
|
sourcePath,
|
|
@@ -31630,25 +32459,57 @@ async function handleSaveWorkflow(args) {
|
|
|
31630
32459
|
`,
|
|
31631
32460
|
"utf8"
|
|
31632
32461
|
);
|
|
31633
|
-
const
|
|
32462
|
+
const updatedEditableInputs = analyzeEditableInputs(
|
|
32463
|
+
prompt,
|
|
32464
|
+
objectInfoAvailability.objectInfo,
|
|
32465
|
+
widgetPathMap
|
|
32466
|
+
);
|
|
32467
|
+
const highSignalInputs = getHighSignalEditableInputs(
|
|
32468
|
+
updatedEditableInputs
|
|
32469
|
+
);
|
|
32470
|
+
const editableInputGroups = summarizeEditableInputGroups(
|
|
32471
|
+
updatedEditableInputs
|
|
32472
|
+
);
|
|
32473
|
+
const overrideExamples = buildOverrideExamples(updatedEditableInputs);
|
|
31634
32474
|
const workflowId = getWorkflowId(outputPath);
|
|
31635
32475
|
const structuredContent = {
|
|
31636
32476
|
workflow_id: workflowId,
|
|
31637
32477
|
path: outputPath,
|
|
31638
32478
|
source_path: sourcePath,
|
|
31639
32479
|
overwritten: existedBeforeSave,
|
|
31640
|
-
editable_inputs:
|
|
32480
|
+
editable_inputs: updatedEditableInputs,
|
|
32481
|
+
high_signal_inputs: highSignalInputs,
|
|
32482
|
+
input_groups: editableInputGroups,
|
|
32483
|
+
override_examples: overrideExamples.examples,
|
|
32484
|
+
preferred_override_paths: overrideExamples.preferred_paths
|
|
31641
32485
|
};
|
|
32486
|
+
if (objectInfoAvailability.warning) {
|
|
32487
|
+
structuredContent.warnings = [objectInfoAvailability.warning];
|
|
32488
|
+
}
|
|
31642
32489
|
if (args.include_prompt) {
|
|
31643
32490
|
structuredContent.prompt = prompt;
|
|
31644
32491
|
}
|
|
31645
|
-
const persistedChanges =
|
|
31646
|
-
const
|
|
32492
|
+
const persistedChanges = parsedOverrides && Object.keys(parsedOverrides).length > 0 ? `Persisted ${Object.keys(parsedOverrides).length} override(s).` : "No overrides were applied.";
|
|
32493
|
+
const textSections = [
|
|
31647
32494
|
`Saved workflow ${workflowId} to ${outputPath}.`,
|
|
31648
|
-
persistedChanges
|
|
31649
|
-
|
|
31650
|
-
|
|
31651
|
-
|
|
32495
|
+
persistedChanges
|
|
32496
|
+
];
|
|
32497
|
+
if (objectInfoAvailability.warning) {
|
|
32498
|
+
textSections.push(objectInfoAvailability.warning);
|
|
32499
|
+
}
|
|
32500
|
+
textSections.push(
|
|
32501
|
+
`High-signal overrides (${highSignalInputs.length}):`,
|
|
32502
|
+
formatEditableInputsText(highSignalInputs, {
|
|
32503
|
+
includeOptions: true
|
|
32504
|
+
}),
|
|
32505
|
+
"Editable input groups:",
|
|
32506
|
+
formatEditableInputGroupsText(editableInputGroups),
|
|
32507
|
+
`Editable inputs (${updatedEditableInputs.length} total):`,
|
|
32508
|
+
formatEditableInputsText(updatedEditableInputs),
|
|
32509
|
+
"Copy-ready override example:",
|
|
32510
|
+
formatOverrideExamplesText(overrideExamples.examples)
|
|
32511
|
+
);
|
|
32512
|
+
const text = textSections.join("\n");
|
|
31652
32513
|
return {
|
|
31653
32514
|
content: [{ type: "text", text }],
|
|
31654
32515
|
structuredContent
|
|
@@ -31659,11 +32520,49 @@ async function handleSaveWorkflow(args) {
|
|
|
31659
32520
|
}
|
|
31660
32521
|
async function handleWorkflowRun(args) {
|
|
31661
32522
|
try {
|
|
31662
|
-
|
|
32523
|
+
let loadedWorkflow;
|
|
32524
|
+
let objectInfo;
|
|
32525
|
+
if (args.workflow !== void 0) {
|
|
32526
|
+
objectInfo = await getObjectInfo();
|
|
32527
|
+
const normalized = await normalizeWorkflowPrompt(
|
|
32528
|
+
args.workflow,
|
|
32529
|
+
objectInfo
|
|
32530
|
+
);
|
|
32531
|
+
loadedWorkflow = {
|
|
32532
|
+
targetPath: "[inline workflow]",
|
|
32533
|
+
prompt: normalized.prompt,
|
|
32534
|
+
sourceFormat: normalized.sourceFormat,
|
|
32535
|
+
widgetPathMap: normalized.widgetPathMap
|
|
32536
|
+
};
|
|
32537
|
+
} else {
|
|
32538
|
+
if (!args.workflow_file_path && !args.workflow_id) {
|
|
32539
|
+
return createValidationError(
|
|
32540
|
+
"workflow",
|
|
32541
|
+
"Provide workflow JSON or an existing workflow reference."
|
|
32542
|
+
);
|
|
32543
|
+
}
|
|
32544
|
+
objectInfo = await getObjectInfo();
|
|
32545
|
+
loadedWorkflow = await loadWorkflow(args, objectInfo);
|
|
32546
|
+
}
|
|
32547
|
+
const { prompt } = loadedWorkflow;
|
|
31663
32548
|
const promptObj = { ...prompt };
|
|
31664
|
-
|
|
31665
|
-
|
|
32549
|
+
const parsedOverrides = parseOverrides(args.overrides);
|
|
32550
|
+
if (parsedOverrides) {
|
|
32551
|
+
const editableInputs = analyzeEditableInputs(
|
|
32552
|
+
prompt,
|
|
32553
|
+
objectInfo,
|
|
32554
|
+
loadedWorkflow.widgetPathMap
|
|
32555
|
+
);
|
|
32556
|
+
applyOverrides(promptObj, parsedOverrides, {
|
|
32557
|
+
editableIndex: buildEditableInputIndex(editableInputs),
|
|
32558
|
+
objectInfo
|
|
32559
|
+
});
|
|
31666
32560
|
}
|
|
32561
|
+
const preflight = validatePromptPreflight(
|
|
32562
|
+
promptObj,
|
|
32563
|
+
objectInfo,
|
|
32564
|
+
loadedWorkflow.widgetPathMap
|
|
32565
|
+
);
|
|
31667
32566
|
const clientId = `mcp-${Date.now()}`;
|
|
31668
32567
|
const result = IS_MOCK ? MOCK_FIXTURES.prompt_response : await fetchComfyJson("/prompt", {
|
|
31669
32568
|
method: "POST",
|
|
@@ -31690,16 +32589,25 @@ async function handleWorkflowRun(args) {
|
|
|
31690
32589
|
timeout: args.timeout
|
|
31691
32590
|
});
|
|
31692
32591
|
}
|
|
32592
|
+
const queueSnapshot = await getQueueSnapshot();
|
|
31693
32593
|
return {
|
|
31694
32594
|
content: [
|
|
31695
32595
|
{
|
|
31696
32596
|
type: "text",
|
|
31697
|
-
text:
|
|
31698
|
-
|
|
31699
|
-
|
|
32597
|
+
text: [
|
|
32598
|
+
`Workflow submitted. prompt_id: ${result.prompt_id}.`,
|
|
32599
|
+
`ComfyUI queue number: ${result.number} (queue number is not an ETA).`,
|
|
32600
|
+
`Preflight checked ${preflight.checked_inputs} literal inputs (${preflight.combo_inputs} combo-backed).`,
|
|
32601
|
+
formatQueueSnapshotText(queueSnapshot),
|
|
32602
|
+
`To retrieve the final results or check its status later, use the 'comfyui_wait_for_workflow' tool with this prompt_id.`
|
|
32603
|
+
].filter((line) => line && line.length > 0).join("\n")
|
|
31700
32604
|
}
|
|
31701
32605
|
],
|
|
31702
|
-
structuredContent: {
|
|
32606
|
+
structuredContent: {
|
|
32607
|
+
...result,
|
|
32608
|
+
preflight,
|
|
32609
|
+
...queueSnapshot ? { queue: queueSnapshot } : {}
|
|
32610
|
+
}
|
|
31703
32611
|
};
|
|
31704
32612
|
} catch (error48) {
|
|
31705
32613
|
return handleComfyError(error48);
|