comfyui-mcp 0.52.160 → 0.52.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/orchestrator/panel-tools.js +147 -5
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/orchestrator/root-shape-mismatch.js +163 -0
- package/dist/orchestrator/root-shape-mismatch.js.map +1 -0
- package/dist/services/node-dev.js +225 -13
- package/dist/services/node-dev.js.map +1 -1
- package/dist/services/primitive-force-input-connect.js +4 -0
- package/dist/services/primitive-force-input-connect.js.map +1 -1
- package/dist/services/queue-manager.js +16 -2
- package/dist/services/queue-manager.js.map +1 -1
- package/dist/services/slot-compat.js +10 -2
- package/dist/services/slot-compat.js.map +1 -1
- package/dist/services/wildcard-slot-connect.js +423 -0
- package/dist/services/wildcard-slot-connect.js.map +1 -0
- package/dist/services/workflow-deps.js +6 -16
- package/dist/services/workflow-deps.js.map +1 -1
- package/dist/tools/node-pack.js +3 -3
- package/dist/tools/node-pack.js.map +1 -1
- package/dist/tools/queue-management.js +1 -1
- package/dist/tools/queue-management.js.map +1 -1
- package/package.json +1 -1
|
@@ -47,6 +47,7 @@ import { isPreExecutorRefusal } from "../services/panel-refusal.js";
|
|
|
47
47
|
import { hostLinksAlreadyReindexed, isLandedUnexpose, laterSlotsAfterUnexpose, laterSlotsFromUnexposePayload, unexposeHostLinkShiftNote, } from "../services/unexpose-host-link-shift.js";
|
|
48
48
|
import { retryConnectAgainstLiveGraph } from "../services/connect-live-graph.js";
|
|
49
49
|
import { verifyPrimitiveForceInputAfterConnect } from "../services/primitive-force-input-connect.js";
|
|
50
|
+
import { retryWildcardSlotConnect } from "../services/wildcard-slot-connect.js";
|
|
50
51
|
import { retryExposeSubgraphInput } from "../services/expose-ae-wildcard.js";
|
|
51
52
|
import { applyLiveRootViewing, callAndRememberViewing, callWithRememberedSubgraph, clearStaleSubgraphIdentity, noteConfirmedViewingFromToolResult, parseViewingScope, } from "../services/subgraph-viewing-scope.js";
|
|
52
53
|
import { assertScreenshotPersistAllowed, decodePngBase64, persistScreenshotPng, resolveScreenshotPersistPath, screenshotOverwriteFromArgs, screenshotPersistPathFromArgs, } from "./panel-screenshot-persist.js";
|
|
@@ -66,6 +67,7 @@ import { includeRequestedCreateGroupMembers } from "./create-group-membership.js
|
|
|
66
67
|
import { sizeForUncollapse } from "./edit-node-uncollapse.js";
|
|
67
68
|
import { fastGroupsFilterPropertyNote, isFastGroupsFilterProperty, } from "./rgthree-fast-groups-property.js";
|
|
68
69
|
import { bindImportedTmpWorkflowUuid, bindLoadedWorkflowIdentity, isPlainObject, isStampMismatchSaveRefusal, openLiveMatchesDestAfterReconnect, openLiveMatchesDestContent, patchOpenIdentity, savedPathFromTabId, shouldRebindOpenIdentity, unsavedTmpWorkflowKey, workflowFromSerializeReply, } from "./open-identity-normalization.js";
|
|
70
|
+
import { contentOnlyRootShapeReadNote, isContentOnlyRootShapeMismatch, recoverContentOnlyGraphQuery, } from "./root-shape-mismatch.js";
|
|
69
71
|
import { clearSwitchHold, describeSwitchHold, recordSwitchHold, successProvesSwitchCleared, } from "./switch-hold.js";
|
|
70
72
|
import { NO_ORIGIN_REMEDY } from "./fence-refusal.js";
|
|
71
73
|
import { completeGetErrorsAudit } from "./get-errors-audit.js";
|
|
@@ -5109,6 +5111,120 @@ function stripVerifiedLastObservedSchemaNote(res) {
|
|
|
5109
5111
|
delete next.schema_source;
|
|
5110
5112
|
return rewriteToolResultJson(res, next);
|
|
5111
5113
|
}
|
|
5114
|
+
// ---- #2545: MiniMaxH3Director duration onValueChange write_warning ----------
|
|
5115
|
+
// The panel assigns duration, verifies the value, then invokes the widget
|
|
5116
|
+
// callback. ComfyUI's settingStore onValueChange throws
|
|
5117
|
+
// `Cannot read properties of undefined (reading 'options')` when editor/options
|
|
5118
|
+
// is missing. That throw is a post-write disclosure, not a failed assignment.
|
|
5119
|
+
// Relaying it as isError (or leaving write_warning as the salient failure)
|
|
5120
|
+
// makes a landed 5→6 write look like a retryable error.
|
|
5121
|
+
function isDirectorDurationCallbackWarning(widget, text) {
|
|
5122
|
+
if (widget.toLowerCase() !== "duration")
|
|
5123
|
+
return false;
|
|
5124
|
+
if (!/reading ['"]options['"]/.test(text))
|
|
5125
|
+
return false;
|
|
5126
|
+
return /onValueChange|settingStore|widget_callback/i.test(text);
|
|
5127
|
+
}
|
|
5128
|
+
function scalarWidgetValuesMatch(requested, observed) {
|
|
5129
|
+
if (Object.is(requested, observed))
|
|
5130
|
+
return true;
|
|
5131
|
+
if ((typeof requested === "number" || typeof requested === "string") &&
|
|
5132
|
+
(typeof observed === "number" || typeof observed === "string")) {
|
|
5133
|
+
return String(requested) === String(observed);
|
|
5134
|
+
}
|
|
5135
|
+
return false;
|
|
5136
|
+
}
|
|
5137
|
+
function recordFromUnknown(value) {
|
|
5138
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
5139
|
+
return null;
|
|
5140
|
+
return value;
|
|
5141
|
+
}
|
|
5142
|
+
function parseJsonObjectFromToolText(res) {
|
|
5143
|
+
const parsed = parseToolResultJson(res);
|
|
5144
|
+
if (parsed)
|
|
5145
|
+
return parsed;
|
|
5146
|
+
const text = res?.content?.find((c) => c.type === "text")?.text;
|
|
5147
|
+
if (typeof text !== "string")
|
|
5148
|
+
return null;
|
|
5149
|
+
const trimmed = text.replace(/^Error:\s*/i, "").trim();
|
|
5150
|
+
const start = trimmed.indexOf("{");
|
|
5151
|
+
if (start < 0)
|
|
5152
|
+
return null;
|
|
5153
|
+
try {
|
|
5154
|
+
return recordFromUnknown(JSON.parse(trimmed.slice(start)));
|
|
5155
|
+
// unknown-ok: the reply is not JSON; keep the original widget-write result.
|
|
5156
|
+
}
|
|
5157
|
+
catch {
|
|
5158
|
+
return null;
|
|
5159
|
+
}
|
|
5160
|
+
}
|
|
5161
|
+
function stripWriteWarningFields(record) {
|
|
5162
|
+
const next = { ...record };
|
|
5163
|
+
delete next.write_warning;
|
|
5164
|
+
delete next.write_warning_frame;
|
|
5165
|
+
delete next.write_warning_source;
|
|
5166
|
+
return next;
|
|
5167
|
+
}
|
|
5168
|
+
function landedDurationWriteResult(payload, set) {
|
|
5169
|
+
const next = payload
|
|
5170
|
+
? stripWriteWarningFields({ ...payload, set: stripWriteWarningFields(set) })
|
|
5171
|
+
: { set: stripWriteWarningFields(set) };
|
|
5172
|
+
return ok(next);
|
|
5173
|
+
}
|
|
5174
|
+
async function readNamedWidgetValue(ctx, nodeId, widget) {
|
|
5175
|
+
const probe = await ctx.call({
|
|
5176
|
+
cmd: "graph_query",
|
|
5177
|
+
ids: [nodeId],
|
|
5178
|
+
fields: "detail",
|
|
5179
|
+
limit: 1,
|
|
5180
|
+
max_chars: DYNAMIC_COMBO_PROBE_MAX_CHARS,
|
|
5181
|
+
}, 8000);
|
|
5182
|
+
if (probe.isError)
|
|
5183
|
+
return undefined;
|
|
5184
|
+
const detail = parseVerifiedQueriedNodeDetail(normalizeGraphQueryResult(probe));
|
|
5185
|
+
const requestedId = canonicalQueriedNodeId(nodeId);
|
|
5186
|
+
if (!detail || !requestedId || detail.id !== requestedId || !detail.widgets) {
|
|
5187
|
+
return undefined;
|
|
5188
|
+
}
|
|
5189
|
+
if (!Object.prototype.hasOwnProperty.call(detail.widgets, widget))
|
|
5190
|
+
return undefined;
|
|
5191
|
+
return detail.widgets[widget];
|
|
5192
|
+
}
|
|
5193
|
+
/**
|
|
5194
|
+
* #2545 — a verified Director duration write whose setting-store callback
|
|
5195
|
+
* threw is still a landed write. Swallow that write_warning when `set.value`
|
|
5196
|
+
* matches the request; if the panel reported it as an error without a set
|
|
5197
|
+
* envelope, one graph_query may prove the value. Anything else stays failed.
|
|
5198
|
+
*/
|
|
5199
|
+
async function classifyLandedDirectorDurationWriteWarning(ctx, res, nodeId, widget, requested) {
|
|
5200
|
+
const text = textOfToolResult(res);
|
|
5201
|
+
const payload = parseJsonObjectFromToolText(res);
|
|
5202
|
+
const set = recordFromUnknown(payload?.set);
|
|
5203
|
+
const warningText = [
|
|
5204
|
+
typeof set?.write_warning === "string" ? set.write_warning : "",
|
|
5205
|
+
typeof set?.write_warning_frame === "string" ? set.write_warning_frame : "",
|
|
5206
|
+
typeof set?.write_warning_source === "string" ? set.write_warning_source : "",
|
|
5207
|
+
text,
|
|
5208
|
+
].join("\n");
|
|
5209
|
+
if (!isDirectorDurationCallbackWarning(widget, warningText))
|
|
5210
|
+
return res;
|
|
5211
|
+
if (set && scalarWidgetValuesMatch(requested, set.value)) {
|
|
5212
|
+
return landedDurationWriteResult(payload, set);
|
|
5213
|
+
}
|
|
5214
|
+
if (!res.isError)
|
|
5215
|
+
return res;
|
|
5216
|
+
const observed = await readNamedWidgetValue(ctx, nodeId, widget);
|
|
5217
|
+
if (observed === undefined || !scalarWidgetValuesMatch(requested, observed)) {
|
|
5218
|
+
return res;
|
|
5219
|
+
}
|
|
5220
|
+
return ok({
|
|
5221
|
+
set: {
|
|
5222
|
+
node_id: nodeId,
|
|
5223
|
+
widget,
|
|
5224
|
+
value: observed,
|
|
5225
|
+
},
|
|
5226
|
+
});
|
|
5227
|
+
}
|
|
5112
5228
|
async function exitSubgraphLevels(ctx, count) {
|
|
5113
5229
|
const failures = [];
|
|
5114
5230
|
for (let i = 0; i < count; i++) {
|
|
@@ -15872,6 +15988,30 @@ export function buildPanelToolDefs() {
|
|
|
15872
15988
|
? {}
|
|
15873
15989
|
: { widget_max_chars: widgetMaxChars.value }),
|
|
15874
15990
|
}, (cmd, timeoutMs) => ctx.call(cmd, timeoutMs));
|
|
15991
|
+
// #2544 — content-only [root-shape-mismatch] after custom-widget / builder
|
|
15992
|
+
// edits (same node count, unsaved live canvas). Inspect the live graph
|
|
15993
|
+
// read-only. Never open or rebind: that would discard the builder work.
|
|
15994
|
+
if (panelReply.isError && isContentOnlyRootShapeMismatch(toolResultText(panelReply))) {
|
|
15995
|
+
const recovered = await recoverContentOnlyGraphQuery((cmd, timeoutMs) => ctx.call(cmd, timeoutMs), {
|
|
15996
|
+
types: args.types,
|
|
15997
|
+
title: args.title,
|
|
15998
|
+
where: args.where,
|
|
15999
|
+
ids: args.ids,
|
|
16000
|
+
upstream_of: args.upstream_of,
|
|
16001
|
+
downstream_of: args.downstream_of,
|
|
16002
|
+
depth: args.depth,
|
|
16003
|
+
fields: args.fields,
|
|
16004
|
+
group_by: args.group_by,
|
|
16005
|
+
limit: args.limit,
|
|
16006
|
+
max_chars: args.max_chars,
|
|
16007
|
+
});
|
|
16008
|
+
if (recovered) {
|
|
16009
|
+
const recoveredReply = ok(recovered);
|
|
16010
|
+
rememberLiveRootViewing(ctx, recovered.viewing);
|
|
16011
|
+
return fitQueryGraphReply(recoveredReply, args.max_chars, widgetMaxChars.note ?? legacyWidgetMaxCharsNote(recoveredReply, widgetMaxChars.value));
|
|
16012
|
+
}
|
|
16013
|
+
return fail(contentOnlyRootShapeReadNote(toolResultText(panelReply)));
|
|
16014
|
+
}
|
|
15875
16015
|
rememberLiveRootViewing(ctx, parseToolResultJson(panelReply)?.viewing);
|
|
15876
16016
|
return fitQueryGraphReply(panelReply, args.max_chars, widgetMaxChars.note ?? legacyWidgetMaxCharsNote(panelReply, widgetMaxChars.value));
|
|
15877
16017
|
}),
|
|
@@ -16618,7 +16758,7 @@ export function buildPanelToolDefs() {
|
|
|
16618
16758
|
return fail(err);
|
|
16619
16759
|
}
|
|
16620
16760
|
}),
|
|
16621
|
-
def("panel_connect", "Connect an output slot of one node to an input slot of another in the user's open graph. Slots accept a name ('MODEL', 'samples') or numeric index. If both slot args are omitted the panel picks the first type-compatible pairing. On failure the error lists every slot with its type and [connected] flag — re-check with panel_query_graph ({ids:[node_id], fields:'detail'}). A frontend PrimitiveNode only serializes through a target widget; connecting one to a forceInput-only / non-widget STRING is refused (panel_run would omit the required input) — use a backend STRING producer such as PrimitiveStringMultiline instead. Undoable.", {
|
|
16761
|
+
def("panel_connect", "Connect an output slot of one node to an input slot of another in the user's open graph. Slots accept a name ('MODEL', 'samples') or numeric index. If both slot args are omitted the panel picks the first type-compatible pairing. On failure the error lists every slot with its type and [connected] flag — re-check with panel_query_graph ({ids:[node_id], fields:'detail'}). LiteGraph wildcard-to-wildcard (`*` → `*`) pairings are compatible (a PrimitiveNode 'connect to widget input' output can land on LogicIF.when_true / when_false so the primitive becomes typed from the destination). A frontend PrimitiveNode only serializes through a target widget; connecting one to a forceInput-only / non-widget STRING is refused (panel_run would omit the required input) — use a backend STRING producer such as PrimitiveStringMultiline instead. Undoable.", {
|
|
16622
16762
|
from_node_id: nodeId().describe("Source node id."),
|
|
16623
16763
|
from_output: slotRef
|
|
16624
16764
|
.optional()
|
|
@@ -16651,7 +16791,8 @@ export function buildPanelToolDefs() {
|
|
|
16651
16791
|
};
|
|
16652
16792
|
const call = (cmd, timeoutMs) => ctx.call(cmd, timeoutMs);
|
|
16653
16793
|
const connected = await retryConnectAgainstLiveGraph(connectArgs, call);
|
|
16654
|
-
|
|
16794
|
+
const afterWildcard = await retryWildcardSlotConnect(connectArgs, connected, call);
|
|
16795
|
+
return verifyPrimitiveForceInputAfterConnect(connectArgs, afterWildcard, call);
|
|
16655
16796
|
}),
|
|
16656
16797
|
def("panel_disconnect", "Disconnect an input slot of a node in the user's open graph. Undoable with Ctrl+Z.", {
|
|
16657
16798
|
node_id: nodeId().describe("Node id whose input to disconnect."),
|
|
@@ -16785,14 +16926,15 @@ export function buildPanelToolDefs() {
|
|
|
16785
16926
|
mutationId = rid;
|
|
16786
16927
|
}, beforeDispatch);
|
|
16787
16928
|
const echoed = stripVerifiedLastObservedSchemaNote(summarizeSetWidgetEcho(written, echoFull));
|
|
16929
|
+
const classified = await classifyLandedDirectorDurationWriteWarning(ctx, echoed, nodeId, widget, value);
|
|
16788
16930
|
// #2495 — ONLY a tagged no-reply on a named Power Lora row is settled
|
|
16789
16931
|
// by a read. An acked executor error is a definite outcome.
|
|
16790
|
-
if (isReplyTimeoutResult(
|
|
16932
|
+
if (isReplyTimeoutResult(classified) &&
|
|
16791
16933
|
isPowerLoraDynamicRowWidget(widget) &&
|
|
16792
16934
|
args.defer_until_idle !== true) {
|
|
16793
|
-
return settlePowerLoraWidgetAfterAckTimeout(ctx,
|
|
16935
|
+
return settlePowerLoraWidgetAfterAckTimeout(ctx, classified, nodeId, widget, value, mutationId, dispatchTab);
|
|
16794
16936
|
}
|
|
16795
|
-
return
|
|
16937
|
+
return classified;
|
|
16796
16938
|
};
|
|
16797
16939
|
let writePromotedInner;
|
|
16798
16940
|
const guardedWrite = async (nodeId, widget, targetExpectedNodeType = expectedNodeType) => {
|