comfyui-mcp 0.52.25 → 0.52.26

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,43 @@
1
+ /**
2
+ * Blind, applied to a run completion.
3
+ *
4
+ * Blind is conversation-wide (#884) and must fail CLOSED (`blind-tab-gate.ts`). A run
5
+ * completion reaches the agent through more than one door: the panel's own `executed`
6
+ * frame arrives at the orchestrator's agent-event ingress, and the #1789 watchdog
7
+ * SYNTHESISES one when that frame never comes.
8
+ *
9
+ * #1861 — the watchdog door recorded the raw payload. That was inert only while its
10
+ * `images` was always empty; #1853 filled it from `/history` and the bypass became live:
11
+ * Blind ON plus a dropped `executed` frame attached the render's pixels to the agent turn
12
+ * ~45 s later. The strip lived inline at one door, so nothing carried it to the next one.
13
+ *
14
+ * It is a pure function here so both doors call the SAME implementation and it can be
15
+ * tested without standing up the orchestrator.
16
+ */
17
+ /** The note appended when pixels are withheld. Also overrides a sighted "review this"
18
+ * instruction that must not be obeyed — the composer renders it verbatim. */
19
+ function blindDisclosure(existing, withheld) {
20
+ const prefix = typeof existing === "string" && existing.trim() ? `${existing.trim()} ` : "";
21
+ return (`${prefix}⚠️ Blind mode is ON: ${withheld} image(s) from this run were withheld ` +
22
+ `from you and are NOT attached — they ARE shown to the user in the panel. Do NOT ` +
23
+ `comment on motion, sharpness, composition or any other visual quality, and do not ` +
24
+ `claim you reviewed them; ask the user to describe what they see, or to turn Blind off.`);
25
+ }
26
+ /**
27
+ * Return `ev` with its pixels removed when `blind`, otherwise unchanged.
28
+ *
29
+ * The disclosure is only added when something was actually withheld: a completion that
30
+ * carried no images has nothing to disclose, and saying otherwise would report a
31
+ * withholding that did not happen.
32
+ */
33
+ export function stripBlindCompletion(ev, blind) {
34
+ if (!blind)
35
+ return ev;
36
+ const withheld = Array.isArray(ev.images) ? ev.images.length : 0;
37
+ return {
38
+ ...ev,
39
+ images: [],
40
+ ...(withheld > 0 ? { note: blindDisclosure(ev.note, withheld) } : {}),
41
+ };
42
+ }
43
+ //# sourceMappingURL=blind-completion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blind-completion.js","sourceRoot":"","sources":["../../src/orchestrator/blind-completion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;8EAC8E;AAC9E,SAAS,eAAe,CAAC,QAAiB,EAAE,QAAgB;IAC1D,MAAM,MAAM,GAAG,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5F,OAAO,CACL,GAAG,MAAM,wBAAwB,QAAQ,wCAAwC;QACjF,kFAAkF;QAClF,oFAAoF;QACpF,wFAAwF,CACzF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,EAAK,EACL,KAAc;IAEd,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,OAAO;QACL,GAAG,EAAE;QACL,MAAM,EAAE,EAAE;QACV,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtE,CAAC;AACJ,CAAC"}
@@ -9,6 +9,7 @@
9
9
  // panel-agent.ts). Each agent runs on the user's Claude SUBSCRIPTION with no API
10
10
  // key. See design/panel-orchestrator.md.
11
11
  import { existsSync, mkdirSync, writeFileSync, unlinkSync, readFileSync, readdirSync, rmSync, appendFileSync, } from "node:fs";
12
+ import { stripBlindCompletion } from "./blind-completion.js";
12
13
  import { execFileSync } from "node:child_process";
13
14
  import { tmpdir, homedir, networkInterfaces } from "node:os";
14
15
  import { dirname, join } from "node:path";
@@ -87,7 +88,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
87
88
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
88
89
  import { registerAllTools } from "../tools/index.js";
89
90
  import { tryInstallRetiredNameRedirect } from "../tools/retired-redirect.js";
90
- import { isForceRemoteFlagSet, isLoopbackHost, detectLocalComfyUIPath, setComfyuiTarget, onComfyuiTargetChanged, isTargetingLocal, isTargetingLocalOrLan, isTargetingPod, getComfyUIBaseUrl, getLocalComfyuiUrl, rescopeLocalTargetFile, getComfyUIAuthHeaders } from "../config.js";
91
+ import { config, isForceRemoteFlagSet, isLoopbackHost, detectLocalComfyUIPath, setComfyuiTarget, onComfyuiTargetChanged, isTargetingLocal, isTargetingLocalOrLan, isTargetingPod, getComfyUIBaseUrl, getLocalComfyuiUrl, rescopeLocalTargetFile, getComfyUIAuthHeaders } from "../config.js";
91
92
  import { normalizeInstallPathEnv } from "../utils/install-path-env.js";
92
93
  import { AGENT_IDENTITY_ENV, agentIdentityPath, publishAgentIdentity, } from "../services/agent-identity.js";
93
94
  import { buildComfyuiMcpEnv, comfyuiSecretKeys, onComfyuiSecretsChanged, hydrateAgentSecretsIntoEnv, onAgentSecretsChanged, setAgentSecret, setComfyuiSecret, isAllowedComfyuiSecretKey, changeJustifiesRetryNudge, } from "../services/panel-secrets.js";
@@ -1267,6 +1268,13 @@ export async function runPanelOrchestrator() {
1267
1268
  logger.info(`[panel-orchestrator] no COMFYUI_PATH/default workspace; adopted ComfyUI ` +
1268
1269
  `base_path from the panel status route: ${comfyuiPath} (#296).`);
1269
1270
  }
1271
+ // #1845 — panel_load_workflow runs IN this process and reconstructs the
1272
+ // workflow library from config.comfyuiPath, not from the spawn env handed to
1273
+ // child MCP servers. A recovered path that never reached config left the
1274
+ // local fallback claiming "COMFYUI_PATH not set" while install_comfyui
1275
+ // environment (the child) reported the same trusted workspace.
1276
+ if (comfyuiPath && !config.comfyuiPath)
1277
+ config.comfyuiPath = comfyuiPath;
1270
1278
  // Force the child remote only when opted in (--force-remote) or the target is
1271
1279
  // non-loopback; a default loopback panel user with no COMFYUI_PATH is left to
1272
1280
  // auto-detect its local install (keeps download_model/apply_manifest/scans).
@@ -1756,6 +1764,16 @@ export async function runPanelOrchestrator() {
1756
1764
  }
1757
1765
  return blind;
1758
1766
  };
1767
+ /**
1768
+ * Blind, applied to a run completion — the SAME implementation at every door.
1769
+ *
1770
+ * #1861: the panel's `executed` frame arrives at the ingress below; the #1789 watchdog
1771
+ * SYNTHESISES one when that frame never comes, and that door recorded the raw payload.
1772
+ * Inert while its `images` was always empty, live once #1853 filled it from /history.
1773
+ * The logic lives in blind-completion.ts so a new arrival point cannot get a
1774
+ * near-copy that drifts, and so it is testable without the orchestrator.
1775
+ */
1776
+ const blindStrippedCompletion = (ev) => stripBlindCompletion(ev, anyTabBlind());
1759
1777
  // ---- live ENVIRONMENT-CAPABILITIES block ----
1760
1778
  // Gather the machine's facts ONCE at startup (CACHED) — OS/CPU/RAM from node,
1761
1779
  // GPU/VRAM/CUDA/torch/python/ComfyUI from /system_stats, Triton/SageAttention by
@@ -3139,6 +3157,8 @@ export async function runPanelOrchestrator() {
3139
3157
  if (comfyuiPath === recovered)
3140
3158
  return;
3141
3159
  comfyuiPath = recovered;
3160
+ if (!config.comfyuiPath)
3161
+ config.comfyuiPath = recovered;
3142
3162
  logger.info(`[panel-orchestrator] recovered ComfyUI base_path from panel status route ` +
3143
3163
  `after retarget: ${recovered} (#296).`);
3144
3164
  // Rebuild the comfyui MCP spawn env (now carrying COMFYUI_PATH) and respawn
@@ -4816,22 +4836,7 @@ export async function runPanelOrchestrator() {
4816
4836
  // stores the stripped copy. Only the SILENCE is fixed, by appending the
4817
4837
  // disclosure to the note the composer already renders verbatim, which also
4818
4838
  // overrides a sighted "review this" instruction that must not be obeyed.
4819
- const blindWithheldCount = anyTabBlind() && Array.isArray(ev.images) ? ev.images.length : 0;
4820
- const evForTab = anyTabBlind()
4821
- ? {
4822
- ...ev,
4823
- images: [],
4824
- ...(blindWithheldCount > 0
4825
- ? {
4826
- note: `${typeof ev.note === "string" && ev.note.trim() ? `${ev.note.trim()} ` : ""}` +
4827
- `⚠️ Blind mode is ON: ${blindWithheldCount} image(s) from this run were withheld ` +
4828
- `from you and are NOT attached — they ARE shown to the user in the panel. Do NOT ` +
4829
- `comment on motion, sharpness, composition or any other visual quality, and do not ` +
4830
- `claim you reviewed them; ask the user to describe what they see, or to turn Blind off.`,
4831
- }
4832
- : {}),
4833
- }
4834
- : ev;
4839
+ const evForTab = blindStrippedCompletion(ev);
4835
4840
  // #468 — a RUN COMPLETION is a promise `panel_run` made ("end your turn,
4836
4841
  // you WILL be notified"), so it goes through the journal: correlated by
4837
4842
  // exact prompt id ONCE, here, and replayed until the turn that carries it
@@ -5900,7 +5905,8 @@ export async function runPanelOrchestrator() {
5900
5905
  // The SAME arrival path the panel's frame takes: correlated once, here,
5901
5906
  // against the ticket that is still open — so the agent is told this is the
5902
5907
  // run IT queued, and the journal's replay/ack durability covers it too.
5903
- const entry = RunCompletions.record(ticket.tabId, payload, {
5908
+ // #1861 the SAME strip the panel ingress applies. Blind is conversation-wide.
5909
+ const entry = RunCompletions.record(ticket.tabId, blindStrippedCompletion(payload), {
5904
5910
  ...(ticket.conversation !== undefined ? { conversation: ticket.conversation } : {}),
5905
5911
  });
5906
5912
  logger.info(`[panel-orchestrator] tab ${ticket.tabId.slice(0, 8)} synthesised run completion for ${describeCorrelation(entry.correlation)} (the panel never reported it — #1789)`);