comfyui-mcp 0.51.4 → 0.51.5

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,69 @@
1
+ /**
2
+ * #1425 — the wire contract for a node id, including the SUBGRAPH-QUALIFIED shape.
3
+ *
4
+ * #845 made the tools accept back an id they had printed: the graph readers return
5
+ * ids as strings (`"42"`), so `"42"` and `42` both had to mean node 42. That change
6
+ * deliberately stopped short of `"5:12"`:
7
+ *
8
+ * > a subgraph-qualified id is a real shape in newer ComfyUI, and silently
9
+ * > truncating it to `5` would target the WRONG node rather than fail. If those
10
+ * > need supporting, that is a separate, deliberate change to the wire contract.
11
+ *
12
+ * This is that change. Unpacking a subgraph leaves genuine ROOT-level nodes carrying
13
+ * ids like `120:104` and `120:113:78`; the readers list them and render them, and
14
+ * every write tool refused them — 18 of 21 nodes uneditable in the reporter's
15
+ * workflow. So the refusal was not protecting anything by then, it was just the
16
+ * conservative half of a trade whose other half never arrived.
17
+ *
18
+ * The two halves have to move TOGETHER. Widening the pattern alone would send
19
+ * `"263:78"` through `Number.parseInt(…, 10)` and yield `263` — turning a loud
20
+ * refusal into a silent edit of a DIFFERENT node, which is the exact outcome #845
21
+ * refused to risk. So a qualified id is never converted: it stays the string the
22
+ * reader printed, all the way to the panel.
23
+ */
24
+ /** A plain integer id, the form the wire has always carried. */
25
+ const PLAIN = /^-?\d+$/;
26
+ /**
27
+ * A subgraph-qualified id: integer segments joined by colons (`120:104`,
28
+ * `120:113:78`). No depth limit — nesting is arbitrary, and a limit would fail the
29
+ * deep case in exactly the way this fixes the shallow one. Only the FIRST segment
30
+ * may be negative: a leading `-` marks the boundary-rail ids the readers emit, and
31
+ * a `-` inside a path would be a shape nothing produces.
32
+ */
33
+ const QUALIFIED = /^-?\d+(?::\d+)+$/;
34
+ /**
35
+ * Both spellings in ONE pattern, so it can be handed to `z.string().regex()`.
36
+ *
37
+ * That matters beyond tidiness: `.regex()` puts a `pattern` on the advertised MCP
38
+ * input schema, while `.refine()` renders as a bare `{"type":"string"}` (measured
39
+ * with zod 4's toJSONSchema). Validating through a refine would therefore have told
40
+ * every client LESS about a node id than the old integer-only rule did.
41
+ */
42
+ export const NODE_ID_PATTERN = /^-?\d+(?::\d+)*$/;
43
+ /** Does this string name a node at all — either spelling? */
44
+ export function isNodeIdString(v) {
45
+ return NODE_ID_PATTERN.test(v);
46
+ }
47
+ /** True for the qualified form specifically (`"5:12"`), false for `"5"`. */
48
+ export function isQualifiedNodeId(v) {
49
+ return QUALIFIED.test(v);
50
+ }
51
+ /**
52
+ * Normalize an accepted id to what goes on the wire.
53
+ *
54
+ * A plain id becomes the NUMBER the panel has always received, so nothing about
55
+ * the existing surface changes. A qualified id is returned VERBATIM — parsing it
56
+ * is precisely the bug. Callers must therefore treat a node id as `number | string`
57
+ * rather than assuming the number.
58
+ */
59
+ export function normalizeNodeId(v) {
60
+ if (typeof v === "number")
61
+ return v;
62
+ if (QUALIFIED.test(v))
63
+ return v;
64
+ return Number.parseInt(v, 10);
65
+ }
66
+ /** The message a rejected id gets. Names the qualified shape explicitly, because a
67
+ * caller holding `263:78` needs to know whether it is unsupported or malformed. */
68
+ export const NODE_ID_MESSAGE = "a node id must be an integer (e.g. 42) or a subgraph-qualified id (e.g. 120:104)";
69
+ //# sourceMappingURL=node-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-id.js","sourceRoot":"","sources":["../../src/orchestrator/node-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,gEAAgE;AAChE,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB;;;;;;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"}
@@ -37,6 +37,7 @@ import { nodesInstallCommandArgs } from "../services/node-management.js";
37
37
  import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
38
38
  import { parse as parseYaml } from "yaml";
39
39
  import { conversationOfScopeAddress, isScopeAddress, shortTabId } from "../services/session-scope.js";
40
+ import { NODE_ID_MESSAGE, NODE_ID_PATTERN, normalizeNodeId } from "./node-id.js";
40
41
  /** #884 — journal TICKETS (run completions #468, ask answers #486) must be
41
42
  * keyed by the REAL tab a run/card was routed to: the panel reports back under
42
43
  * that tab id, and a ticket keyed by the shared scope address a tool ctx is
@@ -6458,14 +6459,22 @@ function validatePanelEditNodeArgs(args) {
6458
6459
  * Nothing about `"42"` is ambiguous. Accept both spellings and normalize to the
6459
6460
  * number the wire has always carried, so the round trip closes.
6460
6461
  *
6461
- * DELIBERATELY STRICT about what counts as a node id: only an integer, or a
6462
- * string that is exactly an integer. `"42px"`, `"4.5"`, `""` and `"5:12"` are
6463
- * still rejected. The last one matters — a subgraph-qualified id is a real
6464
- * shape in newer ComfyUI, and silently truncating it to `5` would target the
6465
- * WRONG node rather than fail. If those need supporting, that is a separate,
6466
- * deliberate change to the wire contract, not something to fall out of a coerce.
6462
+ * STRICT about what counts as a node id: an integer, a string that is exactly an
6463
+ * integer, or a subgraph-qualified id (`"120:104"`). `"42px"`, `"4.5"` and `""`
6464
+ * are still rejected.
6465
+ *
6466
+ * #1425 added the qualified shape the "separate, deliberate change to the wire
6467
+ * contract" this comment used to defer. Unpacking a subgraph leaves genuine ROOT
6468
+ * nodes carrying those ids, the readers hand them out, and every write tool
6469
+ * refused them. Crucially the qualified form is NOT parsed to a number: see
6470
+ * node-id.ts, where truncating `"263:78"` to `263` would edit the wrong node.
6467
6471
  */
6468
- const nodeId = () => z.union([z.number().int(), z.string().regex(/^-?\d+$/, "a node id must be an integer")]).transform((v) => (typeof v === "number" ? v : Number.parseInt(v, 10)));
6472
+ const nodeId = () => z
6473
+ .union([
6474
+ z.number().int(),
6475
+ z.string().regex(NODE_ID_PATTERN, NODE_ID_MESSAGE),
6476
+ ])
6477
+ .transform(normalizeNodeId);
6469
6478
  /**
6470
6479
  * #845 — which `panel_canvas` arguments the chosen action actually consumes.
6471
6480
  *