comfyui-mcp 0.52.28 → 0.52.30

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.
@@ -0,0 +1,263 @@
1
+ /**
2
+ * #1877 — panel_create_group can wrap a collapsed node and still report it
3
+ * missing.
4
+ *
5
+ * The panel sizes the box from live `pos`/`size`. A collapsed node often has
6
+ * `size[1] === 0` (title-chip only), so the box is ~100px tall around the
7
+ * node's origin. Membership then tests the CENTRE of a boundingRect rebuilt
8
+ * with the panel's `finiteExtent` fallback (0 is not a usable height, so the
9
+ * body becomes 100px plus a 30px title). That centre sits a few pixels below
10
+ * the box, so the group is empty even though the node's coordinates are inside.
11
+ *
12
+ * These helpers expand a created group's bounds just enough to contain every
13
+ * membership-plausible centre of a requested node whose origin is already in
14
+ * the box — then the caller writes those bounds with graph_edit_group.
15
+ *
16
+ * Constants match comfyui-mcp-panel `web/js/lib/group-geometry.js`
17
+ * (`COLLAPSED_TITLE_HEIGHT`, `COLLAPSED_PILL_WIDTH`, `finiteExtent` fallbacks)
18
+ * and LiteGraph's containsCentre rule (min edge inclusive, max edge exclusive).
19
+ */
20
+ export const NODE_TITLE_HEIGHT = 30;
21
+ export const COLLAPSED_PILL_WIDTH = 80;
22
+ export const DEFAULT_NODE_WIDTH = 200;
23
+ export const DEFAULT_NODE_BODY_HEIGHT = 100;
24
+ function finiteExtent(value, fallback) {
25
+ const n = Number(value);
26
+ return Number.isFinite(n) && n > 0 ? n : fallback;
27
+ }
28
+ function finiteQuad(v) {
29
+ if (!Array.isArray(v) || v.length !== 4)
30
+ return null;
31
+ const q = [Number(v[0]), Number(v[1]), Number(v[2]), Number(v[3])];
32
+ return q.every(Number.isFinite) ? q : null;
33
+ }
34
+ export function nodePosInsideGroupBounds(pos, bounds) {
35
+ const [gx, gy, gw, gh] = bounds;
36
+ const [x, y] = pos;
37
+ // Inclusive on every edge: the reporter's "bounds cover the node" is a
38
+ // top-left origin sitting on or inside the box, not a centre test.
39
+ return x >= gx && x <= gx + gw && y >= gy && y <= gy + gh;
40
+ }
41
+ /**
42
+ * Centres LiteGraph / the panel might use for containsCentre, given only the
43
+ * geometry panel_query_graph actually returns (pos, size, collapsed, full_height).
44
+ */
45
+ export function membershipCandidateCenters(node) {
46
+ const x = node.pos[0];
47
+ const y = node.pos[1];
48
+ const pillW = finiteExtent(node.size[0], COLLAPSED_PILL_WIDTH);
49
+ const fullW = finiteExtent(node.size[0], DEFAULT_NODE_WIDTH);
50
+ const fullH = finiteExtent(node.size[1], DEFAULT_NODE_BODY_HEIGHT);
51
+ const chipH = finiteExtent(node.full_height, NODE_TITLE_HEIGHT);
52
+ return [
53
+ // Collapsed title-chip (syncNodeArea without forceCollapsed).
54
+ { x: x + pillW / 2, y: y - NODE_TITLE_HEIGHT + chipH / 2 },
55
+ // forceCollapsed wantedNodeArea: size[1]===0 falls back to DEFAULT_NODE_BODY_HEIGHT.
56
+ { x: x + fullW / 2, y: y - NODE_TITLE_HEIGHT + (fullH + NODE_TITLE_HEIGHT) / 2 },
57
+ // Raw pos + stored size (degenerate when size[1] is 0 — origin itself).
58
+ { x: x + fullW / 2, y: y + (node.size[1] > 0 ? node.size[1] : 0) / 2 },
59
+ ];
60
+ }
61
+ /** Grow `bounds` so every point is a containsCentre member (max edge exclusive). */
62
+ export function expandBoundsToContainCenters(bounds, points) {
63
+ let [x, y, w, h] = bounds;
64
+ let x2 = x + w;
65
+ let y2 = y + h;
66
+ for (const p of points) {
67
+ if (!Number.isFinite(p.x) || !Number.isFinite(p.y))
68
+ continue;
69
+ if (p.x < x)
70
+ x = p.x;
71
+ if (p.y < y)
72
+ y = p.y;
73
+ if (p.x >= x2)
74
+ x2 = p.x + 1;
75
+ if (p.y >= y2)
76
+ y2 = p.y + 1;
77
+ }
78
+ return [x, y, x2 - x, y2 - y];
79
+ }
80
+ export function expandGroupBoundsForMembership(bounds, nodes) {
81
+ const points = [];
82
+ for (const n of nodes) {
83
+ if (!nodePosInsideGroupBounds(n.pos, bounds))
84
+ continue;
85
+ points.push(...membershipCandidateCenters(n));
86
+ }
87
+ return points.length ? expandBoundsToContainCenters(bounds, points) : bounds;
88
+ }
89
+ function idKey(id) {
90
+ return String(id);
91
+ }
92
+ export function classifyRequestedMembership(requested, members) {
93
+ const reqKeys = new Set(requested.map(idKey));
94
+ const memberKeys = new Set(members.map(idKey));
95
+ return {
96
+ extra: members.filter((id) => !reqKeys.has(idKey(id))),
97
+ missing: requested.filter((id) => !memberKeys.has(idKey(id))),
98
+ };
99
+ }
100
+ function asIdList(v) {
101
+ if (!Array.isArray(v))
102
+ return [];
103
+ return v.filter((id) => typeof id === "number" || typeof id === "string");
104
+ }
105
+ function parseToolJson(res) {
106
+ if (!res || res.isError)
107
+ return null;
108
+ const text = res.content.find((c) => c.type === "text")?.text;
109
+ if (typeof text !== "string")
110
+ return null;
111
+ try {
112
+ const parsed = JSON.parse(text);
113
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
114
+ ? parsed
115
+ : null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ function withPayload(res, payload) {
122
+ const first = res.content[0];
123
+ if (!first || first.type !== "text")
124
+ return res;
125
+ return {
126
+ ...res,
127
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }, ...res.content.slice(1)],
128
+ };
129
+ }
130
+ function groupFromCreateReply(payload) {
131
+ const inner = payload.group;
132
+ if (inner && typeof inner === "object" && !Array.isArray(inner))
133
+ return inner;
134
+ if ("bounding" in payload || "node_ids" in payload)
135
+ return payload;
136
+ return null;
137
+ }
138
+ export function parseDetailNodesFromQueryText(text) {
139
+ if (typeof text !== "string")
140
+ return [];
141
+ const out = [];
142
+ for (const line of text.split("\n")) {
143
+ const s = line.trim();
144
+ if (!s.startsWith("{"))
145
+ continue;
146
+ try {
147
+ const row = JSON.parse(s);
148
+ const id = row.id;
149
+ if (typeof id !== "number" && typeof id !== "string")
150
+ continue;
151
+ const pos = row.pos;
152
+ const size = row.size;
153
+ if (!Array.isArray(pos) || pos.length < 2 || !Array.isArray(size) || size.length < 2)
154
+ continue;
155
+ const x = Number(pos[0]);
156
+ const y = Number(pos[1]);
157
+ const w = Number(size[0]);
158
+ const h = Number(size[1]);
159
+ if (![x, y, w, h].every(Number.isFinite))
160
+ continue;
161
+ const geom = { id, pos: [x, y], size: [w, h] };
162
+ if (row.collapsed === true)
163
+ geom.collapsed = true;
164
+ if (typeof row.full_height === "number" && Number.isFinite(row.full_height)) {
165
+ geom.full_height = row.full_height;
166
+ }
167
+ out.push(geom);
168
+ }
169
+ catch {
170
+ /* skip a non-JSON detail line */
171
+ }
172
+ }
173
+ return out;
174
+ }
175
+ function honestyWarning(extra, missing) {
176
+ return ("group membership is geometric: the box that wraps the requested nodes " +
177
+ `also captures ${extra} unrelated node(s) (their centre falls inside)` +
178
+ (missing ? ` and misses ${missing} requested node(s)` : "") +
179
+ ". Move the intended nodes into a contiguous region (panel_edit_node / " +
180
+ "panel_auto_layout) before grouping, or edit the group bounds, to get an exact set.");
181
+ }
182
+ function attachHonesty(group, requested) {
183
+ const live = asIdList(group.node_ids);
184
+ const { extra, missing } = classifyRequestedMembership(requested, live);
185
+ const out = { ...group, requested_node_ids: requested };
186
+ delete out.extra_node_ids;
187
+ delete out.missing_node_ids;
188
+ delete out.warning;
189
+ if (extra.length || missing.length) {
190
+ if (extra.length)
191
+ out.extra_node_ids = extra;
192
+ if (missing.length)
193
+ out.missing_node_ids = missing;
194
+ out.warning = honestyWarning(extra.length, missing.length);
195
+ }
196
+ return out;
197
+ }
198
+ function quadsEqual(a, b) {
199
+ return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
200
+ }
201
+ /**
202
+ * After graph_create_group, if requested ids are missing AND their origin sits
203
+ * inside the new box, expand the box so those centres are members and rewrite
204
+ * the reply. Never throws: a failed repair returns the original create result.
205
+ */
206
+ export async function includeRequestedCreateGroupMembers(created, requestedIds, call) {
207
+ try {
208
+ if (created.isError)
209
+ return created;
210
+ const requested = asIdList(requestedIds);
211
+ if (!requested.length)
212
+ return created;
213
+ const payload = parseToolJson(created);
214
+ if (!payload)
215
+ return created;
216
+ const group = groupFromCreateReply(payload);
217
+ if (!group)
218
+ return created;
219
+ const bounds = finiteQuad(group.bounding);
220
+ if (!bounds)
221
+ return created;
222
+ const live = asIdList(group.node_ids);
223
+ const reportedMissing = asIdList(group.missing_node_ids);
224
+ const missing = reportedMissing.length
225
+ ? reportedMissing.filter((id) => requested.some((r) => idKey(r) === idKey(id)))
226
+ : classifyRequestedMembership(requested, live).missing;
227
+ if (!missing.length)
228
+ return created;
229
+ const query = await call({
230
+ cmd: "graph_query",
231
+ ids: missing,
232
+ fields: "detail",
233
+ limit: Math.min(Math.max(missing.length, 1), 200),
234
+ }, 8000);
235
+ if (query.isError)
236
+ return created;
237
+ const qPayload = parseToolJson(query);
238
+ const nodes = parseDetailNodesFromQueryText(qPayload?.text).filter((n) => missing.some((id) => idKey(id) === idKey(n.id)));
239
+ const repairable = nodes.filter((n) => nodePosInsideGroupBounds(n.pos, bounds));
240
+ if (!repairable.length)
241
+ return created;
242
+ const expanded = expandGroupBoundsForMembership(bounds, repairable);
243
+ if (quadsEqual(expanded, bounds))
244
+ return created;
245
+ const groupId = group.id;
246
+ if (typeof groupId !== "number" && typeof groupId !== "string")
247
+ return created;
248
+ const edited = await call({ cmd: "graph_edit_group", group_id: groupId, bounds: expanded }, 15000);
249
+ if (edited.isError)
250
+ return created;
251
+ const ePayload = parseToolJson(edited);
252
+ if (!ePayload)
253
+ return created;
254
+ const editedGroup = groupFromCreateReply(ePayload);
255
+ if (!editedGroup)
256
+ return created;
257
+ return withPayload(created, { ...ePayload, group: attachHonesty(editedGroup, requested) });
258
+ }
259
+ catch {
260
+ return created;
261
+ }
262
+ }
263
+ //# sourceMappingURL=create-group-membership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-group-membership.js","sourceRoot":"","sources":["../../src/orchestrator/create-group-membership.ts"],"names":[],"mappings":"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"}
@@ -76,4 +76,82 @@ export function normalizeNodeId(v) {
76
76
  /** The message a rejected id gets. Names the qualified shape explicitly, because a
77
77
  * caller holding `263:78` needs to know whether it is unsupported or malformed. */
78
78
  export const NODE_ID_MESSAGE = "a node id must be an integer (e.g. 42) or a subgraph-qualified id (e.g. 120:104)";
79
+ /**
80
+ * #1889 — a bare `z.union` renders as the word `Invalid input` and nothing else.
81
+ *
82
+ * Zod 4 reports a failed union as ONE `invalid_union` issue whose own `message`
83
+ * is the constant `"Invalid input"`; the per-member messages (NODE_ID_MESSAGE
84
+ * among them) live in a nested `errors` array. Every renderer that reaches an
85
+ * agent flattens `issue.message` and drops that nest — the MCP SDK's
86
+ * `getParseErrorMessage` (1.30) joins `` `${i.message} at ${path}` ``, zod's own
87
+ * `prettifyError` prints `✖ ${i.message}`, and 1.29 JSON-stringifies the issues.
88
+ * So a node-id rejection read:
89
+ *
90
+ * Invalid input at node_id
91
+ * Invalid input: expected string, received undefined at title
92
+ *
93
+ * NODE_ID_MESSAGE was never dead prose — a BAD STRING (`"42px"`) fails only the
94
+ * string member, zod collapses that to a plain `invalid_format` issue, and the
95
+ * message prints. It is unreachable only for inputs that fail EVERY member
96
+ * (undefined, null, 4.5, an object), which is the common case and the reported
97
+ * one. This is what makes the union say the same thing there.
98
+ *
99
+ * `received` is carried too, because the complaint was the asymmetry with the
100
+ * sibling `title`, and half of what `title` said was what it actually got.
101
+ */
102
+ export function unionErrorFor(expected) {
103
+ return (iss) => `${expected}; received ${describeReceived(iss.input)}`;
104
+ }
105
+ /** How long a rendered value may get before it is cut. A node id is a handful of
106
+ * characters; anything near this is a caller error worth showing, not quoting. */
107
+ const RECEIVED_MAX = 60;
108
+ /**
109
+ * Render an arbitrary rejected input for a message, TOTALLY.
110
+ *
111
+ * This runs inside zod's error path, so it must never throw: a formatter that
112
+ * throws converts a clean validation refusal into a 500. `JSON.stringify` alone
113
+ * is not safe here — it throws on a BigInt and on a circular object, returns the
114
+ * bare `undefined` value for a symbol or a function, and renders NaN/Infinity as
115
+ * `null`, which would tell a caller who passed NaN that they passed null. Each
116
+ * of those is handled before the stringify, and the stringify itself is caught.
117
+ */
118
+ export function describeReceived(input) {
119
+ if (input === null)
120
+ return "null";
121
+ switch (typeof input) {
122
+ case "undefined":
123
+ return "undefined";
124
+ case "bigint":
125
+ return `${input}n`;
126
+ case "symbol":
127
+ return input.toString();
128
+ case "function":
129
+ return "a function";
130
+ case "number":
131
+ case "boolean":
132
+ // `String` and not `JSON.stringify`: the latter renders NaN and ±Infinity
133
+ // as `null`, which would tell a caller who passed NaN that they passed null.
134
+ return String(input);
135
+ default:
136
+ break;
137
+ }
138
+ let rendered;
139
+ try {
140
+ rendered = JSON.stringify(input) ?? String(input);
141
+ }
142
+ catch {
143
+ // The fallback needs its own guard: `Array.isArray` THROWS on a revoked
144
+ // proxy ("Cannot perform 'IsArray' on a proxy that has been revoked"), so
145
+ // the naive recovery path was itself a way for this function to throw.
146
+ // Measured, not assumed — it is the one hostile input of ten that got past
147
+ // the outer catch.
148
+ try {
149
+ return Array.isArray(input) ? "an array" : "an object";
150
+ }
151
+ catch {
152
+ return "an object";
153
+ }
154
+ }
155
+ return rendered.length > RECEIVED_MAX ? `${rendered.slice(0, RECEIVED_MAX)}…` : rendered;
156
+ }
79
157
  //# sourceMappingURL=node-id.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-id.js","sourceRoot":"","sources":["../../src/orchestrator/node-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,CAAS;IACzC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,CAAkB;IAChD,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;oFACoF;AACpF,MAAM,CAAC,MAAM,eAAe,GAC1B,kFAAkF,CAAC"}
1
+ {"version":3,"file":"node-id.js","sourceRoot":"","sources":["../../src/orchestrator/node-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAAC,CAAS;IACzC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,CAAkB;IAChD,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;oFACoF;AACpF,MAAM,CAAC,MAAM,eAAe,GAC1B,kFAAkF,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,QAAQ,cAAc,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AACzE,CAAC;AAED;mFACmF;AACnF,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,WAAW,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,GAAG,KAAK,GAAG,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,UAAU;YACb,OAAO,YAAY,CAAC;QACtB,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,0EAA0E;YAC1E,6EAA6E;YAC7E,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACvB;YACE,MAAM;IACV,CAAC;IACD,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,2EAA2E;QAC3E,mBAAmB;QACnB,IAAI,CAAC;YACH,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,WAAW,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3F,CAAC"}