comfyui-mcp 0.50.99 → 0.50.101
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 +95 -10
- package/dist/orchestrator/panel-tools.js.map +1 -1
- package/dist/services/workflow-converter.js +45 -3
- package/dist/services/workflow-converter.js.map +1 -1
- package/dist/tools/vocabulary.js +17 -0
- package/dist/tools/vocabulary.js.map +1 -1
- package/package.json +1 -1
|
@@ -1341,6 +1341,28 @@ function toolResultText(res) {
|
|
|
1341
1341
|
* else about it — including `isError` and any non-text content. Used where the
|
|
1342
1342
|
* original message must reach the caller VERBATIM and we are only adding what we
|
|
1343
1343
|
* additionally know, never restating (or re-classifying) what it already said. */
|
|
1344
|
+
/** All text in a tool result, joined — for quoting one result inside another. */
|
|
1345
|
+
function textOfToolResult(res) {
|
|
1346
|
+
return res.content.map((c) => c.text ?? "").join(" ").trim();
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* #1329 — the panel's STALE NODE SCHEMA refusal, which is worth acting on rather than
|
|
1350
|
+
* relaying.
|
|
1351
|
+
*
|
|
1352
|
+
* It is the one refusal here whose prescribed remedy has no decision in it: the class
|
|
1353
|
+
* this page registered has drifted from the server's, and `refresh_nodes` re-fetches
|
|
1354
|
+
* /object_info and re-registers it in place. The reporter ran that by hand twice and it
|
|
1355
|
+
* worked twice.
|
|
1356
|
+
*
|
|
1357
|
+
* Matched on the drift sentence rather than on "panel_refresh_nodes", which appears in
|
|
1358
|
+
* several unrelated remedies — keying on the recommendation would make any of them
|
|
1359
|
+
* trigger an add-retry.
|
|
1360
|
+
*/
|
|
1361
|
+
function isStaleNodeSchemaRefusal(res) {
|
|
1362
|
+
if (!res.isError)
|
|
1363
|
+
return false;
|
|
1364
|
+
return /added or retyped since this page loaded its node schema/i.test(textOfToolResult(res));
|
|
1365
|
+
}
|
|
1344
1366
|
function appendToolResultText(res, extra) {
|
|
1345
1367
|
const idx = res?.content?.findIndex((c) => c.type === "text") ?? -1;
|
|
1346
1368
|
const block = idx >= 0 ? res.content[idx] : undefined;
|
|
@@ -5168,15 +5190,17 @@ export function makePanelToolCtx(bridge, tabId, workflowTargets) {
|
|
|
5168
5190
|
catch (err) {
|
|
5169
5191
|
// Only a card-reply TIMEOUT is recoverable/honest-as-timeout: poll the late
|
|
5170
5192
|
// buffer, then report "timeout" if still unanswered. Any other error (no
|
|
5171
|
-
// panel, transport failure)
|
|
5172
|
-
// as
|
|
5193
|
+
// panel, transport failure) still SKIPS the destructive op — but it is reported
|
|
5194
|
+
// as `unreachable`, not `no` (#1332). The user did not decline; we never got to
|
|
5195
|
+
// ask them, and a caller that says "cancelled" on this is describing a decision
|
|
5196
|
+
// nobody made.
|
|
5173
5197
|
if (isReplyTimeoutError(err)) {
|
|
5174
5198
|
const late = await pollLateAskReply(bridge, askId, timing, budgetEnd);
|
|
5175
5199
|
if (late !== undefined)
|
|
5176
5200
|
return isAffirmative(late) ? "yes" : "no";
|
|
5177
5201
|
return "timeout";
|
|
5178
5202
|
}
|
|
5179
|
-
return "
|
|
5203
|
+
return "unreachable";
|
|
5180
5204
|
}
|
|
5181
5205
|
};
|
|
5182
5206
|
// EXPLICIT self-heal — see PanelToolCtx.rebindToActiveTab. Only rebinds when
|
|
@@ -6673,12 +6697,46 @@ export function buildPanelToolDefs() {
|
|
|
6673
6697
|
.optional()
|
|
6674
6698
|
.describe("Canvas [x, y] (two numbers). Auto-placed beside existing nodes when omitted."),
|
|
6675
6699
|
title: z.string().optional().describe("Optional custom node title."),
|
|
6676
|
-
}, async (args, ctx) =>
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6700
|
+
}, async (args, ctx) => {
|
|
6701
|
+
// #599: the frontend gates the add on a FRESH /object_info (assertAddNode-
|
|
6702
|
+
// ResolvableRefreshing) so an uninstalled class can't be added as a
|
|
6703
|
+
// placeholder — that fetch can outlast the 6000 ms default on a large
|
|
6704
|
+
// install. Give it the bounded refresh ack budget.
|
|
6705
|
+
const add = () => ctx.call({ cmd: "graph_add_node", class_type: args.class_type, pos: args.pos, title: args.title }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS);
|
|
6706
|
+
const first = await add();
|
|
6707
|
+
if (!isStaleNodeSchemaRefusal(first))
|
|
6708
|
+
return first;
|
|
6709
|
+
// #1329 — DO THE REFRESH THE REFUSAL ASKS FOR, instead of billing the caller.
|
|
6710
|
+
//
|
|
6711
|
+
// The reporter uploaded two images, and both LoadImage adds were refused with
|
|
6712
|
+
// "call panel_refresh_nodes and retry". Doing exactly that worked, both times.
|
|
6713
|
+
// That is a round trip and an agent-visible error for something with one
|
|
6714
|
+
// correct response and no decision in it.
|
|
6715
|
+
//
|
|
6716
|
+
// Safe to automate for a checkable reason, not a hopeful one: the panel's guard
|
|
6717
|
+
// throws BEFORE it creates anything ("Refuse before creating anything"), so the
|
|
6718
|
+
// retry cannot leave two nodes behind. Mutations are otherwise never re-issued
|
|
6719
|
+
// on our own initiative, and that rule is intact — this is not a transport
|
|
6720
|
+
// failure with an unknown outcome, it is a refusal that states its own.
|
|
6721
|
+
const refreshed = await ctx.call({ cmd: "refresh_nodes" }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS);
|
|
6722
|
+
if (refreshed.isError) {
|
|
6723
|
+
// The refusal is the better error: it names what is wrong with the SCHEMA and
|
|
6724
|
+
// what clears it. Carry the refresh failure alongside so the caller knows the
|
|
6725
|
+
// automatic attempt happened and why it did not help.
|
|
6726
|
+
return appendToolResultText(first, `\n\n(Tried to clear this automatically: panel_refresh_nodes was dispatched and FAILED, ` +
|
|
6727
|
+
`so the schema is unchanged and retrying the add will refuse again. ` +
|
|
6728
|
+
`${textOfToolResult(refreshed)})`);
|
|
6729
|
+
}
|
|
6730
|
+
const second = await add();
|
|
6731
|
+
if (!isStaleNodeSchemaRefusal(second))
|
|
6732
|
+
return second;
|
|
6733
|
+
// Still stale after a successful refresh: report THAT, because it means the
|
|
6734
|
+
// remedy the refusal prescribes does not fix this instance and a caller
|
|
6735
|
+
// following it by hand would loop.
|
|
6736
|
+
return appendToolResultText(second, `\n\n(This was already retried ONCE automatically: panel_refresh_nodes reported success ` +
|
|
6737
|
+
`and the add still refuses, so repeating panel_refresh_nodes will not clear it. ` +
|
|
6738
|
+
`Reload the ComfyUI browser tab, which rebuilds the page's node registry from scratch.)`);
|
|
6739
|
+
}),
|
|
6682
6740
|
def("panel_remove_node", "Remove a node (and its connections) from the user's open graph by id. Undoable with Ctrl+Z.", { node_id: nodeId().describe("Node id from panel_graph_outline / panel_query_graph.") }, async (args, ctx) => ctx.call({ cmd: "graph_remove_node", node_id: args.node_id })),
|
|
6683
6741
|
def("panel_clear", "Remove EVERY node from the user's open graph — only for an explicit 'clear/reset the canvas'. Just CALL THIS DIRECTLY when they ask to clear: the tool itself pops a confirm card and only wipes on a yes (don't ask separately first). The wipe is a single Ctrl+Z undo. NEVER use this for a 'new workflow' — that's panel_new_workflow (a new tab, leaves this graph intact).", {}, async (_args, ctx) => {
|
|
6684
6742
|
const decision = await ctx.confirm("Clear the canvas? This removes every node from the open workflow. (One Ctrl+Z undoes it.)", "Clear canvas");
|
|
@@ -6686,6 +6744,14 @@ export function buildPanelToolDefs() {
|
|
|
6686
6744
|
return ok("Timed out waiting for your confirmation, so I left the canvas as-is. " +
|
|
6687
6745
|
"Tell me to clear it again when you're ready.");
|
|
6688
6746
|
}
|
|
6747
|
+
if (decision === "unreachable") {
|
|
6748
|
+
// #1332 — the canvas is still untouched, which is the part that matters, but
|
|
6749
|
+
// the user never saw the question. Saying "cancelled" would credit them with
|
|
6750
|
+
// a decision they were never offered.
|
|
6751
|
+
return ok("The canvas was left as-is — but NOT because it was declined: the panel could " +
|
|
6752
|
+
"not be reached to ask, so the question never appeared. Nothing was cleared. " +
|
|
6753
|
+
"Check the panel tab is connected, then ask again.");
|
|
6754
|
+
}
|
|
6689
6755
|
if (decision !== "yes") {
|
|
6690
6756
|
return ok("Cancelled — the canvas was left as-is.");
|
|
6691
6757
|
}
|
|
@@ -8878,7 +8944,26 @@ export function buildPanelToolDefs() {
|
|
|
8878
8944
|
: "Cancelled — no new restart was dispatched. ComfyUI was briefly " +
|
|
8879
8945
|
"unreachable but is healthy again.");
|
|
8880
8946
|
}
|
|
8881
|
-
|
|
8947
|
+
// #1332 — the reporter's exact string, and it was FALSE. They accepted the
|
|
8948
|
+
// restart, ComfyUI restarted (a fresh startup in the server log), and this
|
|
8949
|
+
// said it had not — because the restart dropped the socket the answer had to
|
|
8950
|
+
// travel back on, and a transport failure used to arrive here as "no".
|
|
8951
|
+
//
|
|
8952
|
+
// The probes above already refuse to claim "not restarted" while the server
|
|
8953
|
+
// is DOWN. This is the remaining case: the server is HEALTHY, which is
|
|
8954
|
+
// equally true of "nothing happened" and of "it restarted and came back".
|
|
8955
|
+
// With an explicit decline we know which; without one we do not, and the
|
|
8956
|
+
// sentence must stop asserting it.
|
|
8957
|
+
return ok(decision === "unreachable"
|
|
8958
|
+
? "This call did NOT dispatch a restart. Whether ComfyUI restarted for some " +
|
|
8959
|
+
"other reason cannot be told from here: the panel could not be reached to " +
|
|
8960
|
+
"ask for confirmation — the question never appeared — so no decision was " +
|
|
8961
|
+
"made either way, and the server is reachable now, which looks the same " +
|
|
8962
|
+
"whether it never went down or went down and came back. If you asked for a " +
|
|
8963
|
+
"restart and one has already happened, this is that transport loss, not a " +
|
|
8964
|
+
"cancellation. Check the ComfyUI log for a fresh startup line before " +
|
|
8965
|
+
"restarting again."
|
|
8966
|
+
: "Cancelled — ComfyUI was not restarted.");
|
|
8882
8967
|
}
|
|
8883
8968
|
// Heal an orphaned session onto the live tab FIRST, then bind the reboot dispatch
|
|
8884
8969
|
// to that ONE tab id (no await between capture and dispatch, so JS run-to-
|