comfyui-mcp 0.52.15 → 0.52.17

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.
@@ -15,7 +15,9 @@ import { dirname, join } from "node:path";
15
15
  import { fileURLToPath } from "node:url";
16
16
  import { randomBytes } from "node:crypto";
17
17
  import readline from "node:readline";
18
- import { startUiBridge, isLoopbackBindHost, isMirrorSafeFrameType, SESSION_EPOCH, } from "../services/ui-bridge.js";
18
+ import { startUiBridge, isLoopbackBindHost, isMirrorSafeFrameType, isUnknownCommandReply, isPanelCmdUnsupportedError, SESSION_EPOCH, } from "../services/ui-bridge.js";
19
+ import { canonicalOrigin } from "../utils/origin.js";
20
+ import { publishFrontendVirtualTypes, } from "../services/frontend-virtual-types.js";
19
21
  import { setupSecureBridge, resolveComfyuiPathForTarget } from "../services/secure-bridge.js";
20
22
  import { judgeHelloRetarget, canonComfyuiTargetUrl } from "../services/hello-retarget.js";
21
23
  import { startQuickTunnel } from "../services/tunnel.js";
@@ -1761,6 +1763,58 @@ export async function runPanelOrchestrator() {
1761
1763
  // mirror-image lie: claiming to be a build we are not running.
1762
1764
  const mcpVersionRunning = MCP_VERSION_RUNNING;
1763
1765
  let latestPanelVersion;
1766
+ // #1400 — the frontend VIRTUAL-node registry each connected canvas-owning tab
1767
+ // has PROVEN (its `graph_get_virtual_types` answer: the registered classes that
1768
+ // set `isVirtualNode === true`, the flag ComfyUI's own serializer reads to keep
1769
+ // a node out of the prompt). The headless `check_runtime` in the spawned tool
1770
+ // servers cannot see a browser registry, so each hello pulls this and the poll
1771
+ // tick republishes it through the progress-dir channel the children read
1772
+ // (services/frontend-virtual-types.ts).
1773
+ //
1774
+ // Keyed by the tab's SERVER-OBSERVED handshake origin (bridge.tabServerOrigin),
1775
+ // NEVER the hello's client-supplied comfyui_url claim — page JS can write the
1776
+ // claim but cannot forge the browser's Origin header (the #756 trust model), so
1777
+ // a tab can only annotate the server it provably fronts. A failed or refused
1778
+ // pull keeps the PREVIOUS answer for the origin; only an ok:true reply
1779
+ // replaces it, so a transient error never erases known-good proof.
1780
+ const frontendVirtualTypesByOrigin = new Map();
1781
+ // Panel versions that PROVED they lack the command (an Unknown-command reply or
1782
+ // the proactive too-old refusal), so an old panel is asked once per process,
1783
+ // not once per hello. Keyed by version, not tab: a panel UPDATE keeps the tab
1784
+ // but moves the version, and must be asked again.
1785
+ const virtualTypesUnsupportedPanelVersions = new Set();
1786
+ async function pullFrontendVirtualTypes(tabId, panelVersion) {
1787
+ // A headless (mobile/mirror) client fronts no canvas registry.
1788
+ if (bridge.isCurrentHeadless(tabId))
1789
+ return;
1790
+ if (panelVersion && virtualTypesUnsupportedPanelVersions.has(panelVersion))
1791
+ return;
1792
+ const origin = bridge.tabServerOrigin(tabId);
1793
+ // No proven handshake origin → the answer would be keyed on a claim. Skip:
1794
+ // an unkeyed registry is not evidence about any server.
1795
+ if (!origin)
1796
+ return;
1797
+ try {
1798
+ const reply = (await bridge.send({ cmd: "graph_get_virtual_types" }, { tabId }));
1799
+ // ok:false (the panel could not read its own registry) or a malformed
1800
+ // answer keeps the previous entry rather than replacing it with nothing.
1801
+ if (reply?.ok !== true || !Array.isArray(reply.virtual_types))
1802
+ return;
1803
+ const types = reply.virtual_types.filter((t) => typeof t === "string" && t !== "");
1804
+ frontendVirtualTypesByOrigin.set(canonicalOrigin(origin) ?? origin, types);
1805
+ }
1806
+ catch (err) {
1807
+ // A panel that predates the command is a STABLE fact for this process —
1808
+ // remember the version and stop re-asking on every hello. Any other
1809
+ // failure (timeout, mid-restart socket drop) is transient: the previous
1810
+ // answer stands and the next hello retries.
1811
+ if (isPanelCmdUnsupportedError(err, "graph_get_virtual_types") ||
1812
+ isUnknownCommandReply(err instanceof Error ? err.message : String(err))) {
1813
+ if (panelVersion)
1814
+ virtualTypesUnsupportedPanelVersions.add(panelVersion);
1815
+ }
1816
+ }
1817
+ }
1764
1818
  let panelSystemAppend = resolvePanelPersona();
1765
1819
  // Set once the manager exists so a later refresh (after a ComfyUI restart) feeds
1766
1820
  // the freshly-gathered env into newly-spawned agents too — Claude reads
@@ -3431,6 +3485,10 @@ export async function runPanelOrchestrator() {
3431
3485
  latestPanelVersion = helloPanelVer;
3432
3486
  void refreshEnvCapabilities();
3433
3487
  }
3488
+ // #1400 — pull this tab's proven frontend-virtual registry for the
3489
+ // check_runtime channel. On EVERY hello, not just the first: a page reload
3490
+ // is how a pack's frontend JS arrives or leaves, and the reload re-hellos.
3491
+ void pullFrontendVirtualTypes(panelTab, typeof helloPanelVer === "string" && helloPanelVer ? helloPanelVer : undefined);
3434
3492
  // #236 — THE VOCABULARY HANDSHAKE, at the handshake.
3435
3493
  //
3436
3494
  // `describeVocabularySkew` and the hash it compares have existed, fully
@@ -5332,6 +5390,21 @@ export async function runPanelOrchestrator() {
5332
5390
  // the child must never quote a panel that has since disconnected. Writes only
5333
5391
  // when the set changed.
5334
5392
  publishConnectedPanelOrigins(progressDir, bridge.connectedServerOrigins());
5393
+ // #1400 — the same level-triggered discipline for the frontend-virtual
5394
+ // registry: republish the CURRENT map, scoped to origins a connected tab
5395
+ // actually fronts, so a disconnected tab's entry drops out of the channel
5396
+ // within one tick rather than exempting types for a page that is gone.
5397
+ {
5398
+ const fronted = new Set(bridge
5399
+ .connectedServerOrigins()
5400
+ .map((o) => canonicalOrigin(o) ?? o));
5401
+ const virtualEntries = [];
5402
+ for (const [origin, types] of frontendVirtualTypesByOrigin) {
5403
+ if (fronted.has(origin))
5404
+ virtualEntries.push({ origin, types });
5405
+ }
5406
+ publishFrontendVirtualTypes(progressDir, virtualEntries);
5407
+ }
5335
5408
  let files = [];
5336
5409
  try {
5337
5410
  files = readdirSync(progressDir).filter((f) => f.endsWith(".json"));