comfyui-mcp 0.50.6 → 0.50.8

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.
@@ -13,6 +13,75 @@ function targetOf(input) {
13
13
  return String(input);
14
14
  }
15
15
  }
16
+ /**
17
+ * What the CONNECTED panels front, for the drift comparison below (#952).
18
+ *
19
+ * Injected by the orchestrator at startup rather than imported: this module is
20
+ * the bottom of the stack and must not depend on the bridge. Unset (the plain
21
+ * MCP server, tests, any process with no bridge) simply means the comparison is
22
+ * skipped — never that there is no drift.
23
+ *
24
+ * Origins here must be the SERVER-OBSERVED handshake Origin (UiBridge's
25
+ * `tabServerOrigin`), not the client-supplied `hello.comfyui_url`: the browser
26
+ * sets the former and blocks page JS from forging it, so it is the trustworthy
27
+ * answer to "which ComfyUI is this tab actually on".
28
+ */
29
+ let connectedPanelOrigins = null;
30
+ /** Install the panel-origin source. Pass null to clear (tests, shutdown). */
31
+ export function setConnectedPanelOrigins(fn) {
32
+ connectedPanelOrigins = fn;
33
+ }
34
+ /** Origin (scheme://host:port) of a request target, or undefined if unparsable. */
35
+ function originOf(target) {
36
+ try {
37
+ return new URL(target).origin;
38
+ }
39
+ catch {
40
+ return undefined;
41
+ }
42
+ }
43
+ /**
44
+ * Name the drift instead of asking the reader to go and check for it (#952).
45
+ *
46
+ * The shipped message told the user a connected panel "does not imply this
47
+ * address is reachable" and pointed them at install_comfyui(action:"environment")
48
+ * to compare the two by hand. We can usually just do the comparison — the bridge
49
+ * knows what each connected tab fronts — and saying which of the three cases
50
+ * holds is worth far more than the instruction to go and find out:
51
+ *
52
+ * - DIFFERENT → that is very likely the whole answer.
53
+ * - THE SAME → drift is RULED OUT. Worth stating plainly, because it stops
54
+ * the reader chasing the explanation the old text volunteered.
55
+ * The panel reaches it from the browser; this process does not
56
+ * (a firewall, a container boundary, a bound interface).
57
+ * - UNKNOWN → no panel connected, no origin source installed, or the
58
+ * handshake carried no usable Origin. Say nothing about drift;
59
+ * an absent comparison is not a negative result.
60
+ */
61
+ function describeTargetDrift(target) {
62
+ const origins = (() => {
63
+ try {
64
+ return connectedPanelOrigins?.() ?? [];
65
+ }
66
+ catch {
67
+ return []; // a broken source must never replace the real network error
68
+ }
69
+ })();
70
+ if (origins.length === 0)
71
+ return "";
72
+ const want = originOf(target);
73
+ if (!want)
74
+ return "";
75
+ const distinct = [...new Set(origins)];
76
+ if (distinct.includes(want)) {
77
+ return (` A connected panel is on this same origin, so this is NOT a wrong-address problem: ` +
78
+ `the browser can reach ${want} and this process cannot (a firewall, a container ` +
79
+ `boundary, or a server bound to one interface).`);
80
+ }
81
+ return (` A connected panel is on ${distinct.join(", ")} — a DIFFERENT address, which is why ` +
82
+ `the panel works while this call does not. Point COMFYUI_URL at that origin if it is the ` +
83
+ `server you meant.`);
84
+ }
16
85
  /**
17
86
  * Turn a network-layer throw into a diagnostic that says WHAT was attempted.
18
87
  *
@@ -25,14 +94,16 @@ function targetOf(input) {
25
94
  *
26
95
  * The two ARE separate targets by design: the panel talks to whichever ComfyUI
27
96
  * the browser is on, while these calls go to the configured COMFYUI_URL. That is
28
- * not a bug, but it is invisible unless the failure names the address.
97
+ * not a bug, but it is invisible unless the failure names the address — and,
98
+ * where the bridge can tell us, says whether the two actually differ.
29
99
  */
30
100
  function describeComfyFetchFailure(err, target) {
31
101
  const { message, code } = describeFetchFailure(err);
32
102
  const wrapped = new Error(`${message} — while requesting ${target}. ` +
33
103
  `That is the headless ComfyUI target (COMFYUI_URL); a CONNECTED sidebar panel does not imply this address is reachable, ` +
34
- `because the panel talks to whichever ComfyUI its browser tab is on. ` +
35
- `Check the target with install_comfyui (action:"environment"), and confirm the server is up with get_system_stats (action:"health").`, { cause: err });
104
+ `because the panel talks to whichever ComfyUI its browser tab is on.` +
105
+ describeTargetDrift(target) +
106
+ ` Check the target with install_comfyui (action:"environment"), and confirm the server is up with get_system_stats (action:"health").`, { cause: err });
36
107
  if (code)
37
108
  wrapped.code = code;
38
109
  return wrapped;
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/comfyui/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,4EAA4E;AAC5E,SAAS,QAAQ,CAAC,KAA6B;IAC7C,IAAI,CAAC;QACH,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,KAAK,YAAY,GAAG;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAC5C,OAAQ,KAAiB,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,yBAAyB,CAAC,GAAY,EAAE,MAAc;IAC7D,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,KAAK,CACvB,GAAG,OAAO,uBAAuB,MAAM,IAAI;QACzC,yHAAyH;QACzH,sEAAsE;QACtE,qIAAqI,EACvI,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACF,IAAI,IAAI;QAAG,OAA6B,CAAC,IAAI,GAAG,IAAI,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAA6B,EAC7B,OAAoB,EAAE;IAEtB,MAAM,IAAI,GAAG,qBAAqB,EAAE,CAAC;IACrC,IAAI,OAA0B,CAAC;IAC/B,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,6EAA6E;QAC7E,oEAAoE;QACpE,4CAA4C;QAC5C,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,CAAC;QACxC,MAAM,yBAAyB,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/comfyui/fetch.ts"],"names":[],"mappings":"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"}
@@ -24,12 +24,14 @@ import { performPanelSync, reassessPanelAfterSyncFailure } from "../services/pan
24
24
  import { clearPanelDiskObservation } from "../services/panel-workspace.js";
25
25
  import { panelRecoveryContext } from "../services/panel-recovery.js";
26
26
  import { isPanelAutoInstallDisabled } from "../services/panel-installer.js";
27
- import { SelfRestarter } from "../services/self-restart.js";
27
+ import { SelfRestarter, canSelfRestart } from "../services/self-restart.js";
28
+ import { pairUrlDurability } from "./pair-durability.js";
28
29
  import { SessionStore, workflowIdentityParts } from "./session-store.js";
29
30
  import { SHARED_SESSION_SCOPE, isScopeAddress, conversationTabs, shouldRetireSharedAgent, messageOrigin, normalizeHelloBackend, workflowOriginNote, } from "../services/session-scope.js";
30
31
  import { TurnOriginTracker, makeScopeRepinHandler, makeScopeTargetResolver, } from "./turn-origins.js";
31
32
  import { listSessions, loadTranscript } from "./history.js";
32
33
  import { uploadImageHttp, resetClient } from "../comfyui/client.js";
34
+ import { setConnectedPanelOrigins } from "../comfyui/fetch.js";
33
35
  import { logger } from "../utils/logger.js";
34
36
  import { PanelAgentManager, fetchSupportedModels, fetchSupportedCommands, isEffort, } from "./panel-agent.js";
35
37
  import { promptText } from "./error-text.js";
@@ -2466,6 +2468,12 @@ export async function runPanelOrchestrator() {
2466
2468
  // `null` (ambiguous origin) makes the bridge refuse loudly; no entry lets
2467
2469
  // the bridge fall back to active-tab resolution (idle-time probes).
2468
2470
  bridge.setScopeTargetResolver(makeScopeTargetResolver({ tracker: turnOrigins, scopeAgentKeyOf }));
2471
+ // #952 — let a headless `fetch failed` say whether the connected panel is on a
2472
+ // DIFFERENT ComfyUI than COMFYUI_URL. The reporter's readonly tools failed
2473
+ // while every panel tool worked, and nothing in the error connected the two.
2474
+ // SERVER-OBSERVED origins only (tabServerOrigin): the browser sets the
2475
+ // handshake Origin and page JS cannot forge it, unlike hello.comfyui_url.
2476
+ setConnectedPanelOrigins(() => bridge.connectedServerOrigins());
2469
2477
  // #884 P1 (confirming gate 2) — EXPLICIT recovery from a DEAD or AMBIGUOUS
2470
2478
  // pin, and from those only. The bridge's refusal names
2471
2479
  // panel_set_workflow_target as the way out; this is the ONLY path that
@@ -3911,8 +3919,22 @@ export async function runPanelOrchestrator() {
3911
3919
  }
3912
3920
  url = `ws://${ip}:${pairPort}/?token=${token}`;
3913
3921
  }
3914
- logger.info(`[panel-orchestrator] pairing URL minted (${mode})`);
3915
- bridge.push({ type: "pair_url", mode, url }, tabId);
3922
+ // #875 — say at pair time whether this URL survives a restart. The
3923
+ // self-restarter is on by default and rotates the token (always, unless
3924
+ // pinned) and the tunnel hostname (always, quick tunnels cannot be
3925
+ // pinned). A user hit exactly this and reported it as "updating the npm
3926
+ // version bricks my communication with the agent" — the restart was the
3927
+ // cause, and nothing here had told them.
3928
+ const durability = pairUrlDurability({
3929
+ mode,
3930
+ pinnedToken: envPairToken !== null,
3931
+ autoRestart: canSelfRestart(),
3932
+ });
3933
+ logger.info(`[panel-orchestrator] pairing URL minted (${mode}) — ` +
3934
+ (durability.survivesRestart
3935
+ ? "survives restart"
3936
+ : `rotates on restart: ${durability.rotates.join(", ")}`));
3937
+ bridge.push({ type: "pair_url", mode, url, durability }, tabId);
3916
3938
  })().catch((err) => {
3917
3939
  bridge.push({ type: "pair_error", mode, error: err instanceof Error ? err.message : String(err) }, tabId);
3918
3940
  });