comfyui-mcp 0.48.31 → 0.48.32

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.
@@ -193,6 +193,17 @@ let panelRebootTimingOverride = null;
193
193
  // COMFYUI_PANEL_REBOOT_* env overrides are set absurdly high (coordinator codex P2).
194
194
  const MAX_REBOOT_SETTLE_MS = 10_000; // 10s
195
195
  const MAX_REBOOT_BUDGET_MS = 240_000; // 240s → settle+budget ≤ 250s < 300s outer
196
+ // #404: hard ceiling on how long the panel_restart_comfyui CONFIRMATION card waits for
197
+ // a yes/no answer. A restart is user-initiated, so a present user answers in seconds;
198
+ // the failure mode is an unanswered/undelivered card AFTER a prior restart's reconnect
199
+ // (the "second restart in one turn" repro: the panel tab is backgrounded or still
200
+ // re-registering, so the card is never seen/answered). Previously the confirm inherited
201
+ // the full remaining ~255s budget, so it blocked for ~4 minutes and read as an
202
+ // indefinite hang (the user killed it at ~2min). Bounding the WAIT here — well under the
203
+ // ~240s ask deadline and the ~300s tools/call budget — makes an unanswered card fail
204
+ // fast with an actionable retry / restart_comfyui hint. It NEVER shortcuts the
205
+ // confirmation itself (no auto-confirm): it bounds only how long we wait for the answer.
206
+ const RESTART_CONFIRM_TIMEOUT_MS = 90_000; // 90s
196
207
  function parsePositiveNumberEnv(name, fallback) {
197
208
  const raw = process.env[name];
198
209
  if (raw == null || raw === "")
@@ -242,6 +253,8 @@ export const __panelToolsTestHooks = {
242
253
  loopbackProbeUrl,
243
254
  /** Compute reboot timing from env WITH the P2 hard caps (bypasses any override). */
244
255
  computeRebootTimingFromEnv,
256
+ /** #404: the hard ceiling on the panel_restart_comfyui confirmation-card wait. */
257
+ RESTART_CONFIRM_TIMEOUT_MS,
245
258
  /** Zero out the post-drop retry settle so retry-once tests don't sleep. */
246
259
  setRetrySettleMs(ms) {
247
260
  retrySettleMsOverride = ms;
@@ -341,6 +354,7 @@ const MUTATING_GRAPH_EDIT_CMDS = new Set([
341
354
  "graph_connect",
342
355
  "graph_disconnect",
343
356
  "graph_set_widget",
357
+ "graph_set_node_property",
344
358
  "graph_move_node",
345
359
  "graph_resize_node",
346
360
  "graph_set_title",
@@ -2819,6 +2833,15 @@ export function buildPanelToolDefs() {
2819
2833
  // Give the guarded write the bounded refresh ack budget.
2820
2834
  return ctx.call({ cmd: "graph_set_widget", node_id: args.node_id, widget: args.widget, value }, OBJECT_INFO_REFRESH_ACK_TIMEOUT_MS);
2821
2835
  }),
2836
+ def("panel_set_property", "Set a node's LiteGraph PROPERTY (the right-click → Properties panel), NOT a widget — the counterpart to panel_set_widget, which only reaches `widgets`. Many custom nodes are configured entirely through node properties: e.g. the rgthree Fast Groups Bypasser's filters `matchTitle`, `matchColors`, `sort`, and `toggleRestriction` are node properties, and without `matchTitle` the node enumerates EVERY group in the workflow (a footgun). Sets node.properties[name] and, when the node defines an onPropertyChanged callback (rgthree and many LiteGraph nodes do), invokes it so the change takes effect LIVE (e.g. rgthree re-filters its group list). Returns the previous and new value. Undoable with Ctrl+Z.", {
2837
+ node_id: z.number().int().describe("Node id from panel_graph_outline / panel_query_graph."),
2838
+ name: z
2839
+ .string()
2840
+ .describe("Property name from the node's right-click → Properties panel (e.g. 'matchTitle', 'matchColors', 'sort', 'toggleRestriction')."),
2841
+ value: z
2842
+ .union([z.string(), z.number(), z.boolean(), z.null()])
2843
+ .describe("New property value (string/number/boolean/null). For the rgthree Fast Groups Bypasser, matchTitle is a title substring/regex filter."),
2844
+ }, async (args, ctx) => ctx.call({ cmd: "graph_set_node_property", node_id: args.node_id, name: args.name, value: args.value })),
2822
2845
  def("panel_move_node", "Move a node to a new canvas position [x, y] in the user's open graph. Undoable.", {
2823
2846
  node_id: z.number().int().describe("Node id from panel_graph_outline / panel_query_graph."),
2824
2847
  pos: xy().describe("New canvas [x, y] (two numbers)."),
@@ -3830,10 +3853,31 @@ export function buildPanelToolDefs() {
3830
3853
  // to the remaining budget (its deadline+grace can't overrun it — see confirm).
3831
3854
  const OVERALL_MAX_MS = 255_000;
3832
3855
  const overallDeadline = Date.now() + OVERALL_MAX_MS;
3833
- const decision = await ctx.confirm("Restart ComfyUI now? It (and this agent) will go down briefly, then reconnect and resume automatically.", "Restart ComfyUI", Math.max(1, overallDeadline - Date.now()));
3856
+ // #404: if the ONLY reachable client is canvas-less (a mobile mirror / remote /
3857
+ // headless viewer), a yes/no card can't render, so the confirm would block with
3858
+ // no way to answer. Detect that up front — exactly as panel_ask does — and fail
3859
+ // fast with an actionable message instead of dispatching a card into the void and
3860
+ // waiting out the whole budget. Points at the headless restart_comfyui fallback,
3861
+ // which needs no panel card. (A normal interactive tab returns null → proceed.)
3862
+ const surfaceErr = askSurfaceError(ctx);
3863
+ if (surfaceErr) {
3864
+ return fail(surfaceErr +
3865
+ " To restart the server without a panel confirmation card, use restart_comfyui.");
3866
+ }
3867
+ // #404: BOUND the confirmation wait. It previously inherited the full remaining
3868
+ // ~255s budget, so an unanswered/undelivered card (the panel tab backgrounded or
3869
+ // still reconnecting after a PRIOR restart — the second-restart-in-one-turn repro)
3870
+ // blocked for ~4 minutes and read as an indefinite hang. Cap it at
3871
+ // RESTART_CONFIRM_TIMEOUT_MS (still clamped under the outer budget). This bounds
3872
+ // only the WAIT — it never auto-confirms; on timeout we fail fast, below.
3873
+ const confirmBudget = Math.max(1, Math.min(RESTART_CONFIRM_TIMEOUT_MS, overallDeadline - Date.now()));
3874
+ const decision = await ctx.confirm("Restart ComfyUI now? It (and this agent) will go down briefly, then reconnect and resume automatically.", "Restart ComfyUI", confirmBudget);
3834
3875
  if (decision === "timeout") {
3835
- return ok("Timed out waiting for your confirmation, so I did NOT restart ComfyUI. " +
3836
- "Tell me to restart it and I'll go ahead.");
3876
+ return ok(`No confirmation received within ${Math.round(confirmBudget / 1000)}s, so I did NOT ` +
3877
+ "restart ComfyUI. The panel tab may be backgrounded or still reconnecting after a " +
3878
+ "previous restart, so the confirmation card wasn't answered. Tell me to restart it " +
3879
+ "and I'll re-ask, or use restart_comfyui to restart the server directly without a " +
3880
+ "panel card.");
3837
3881
  }
3838
3882
  if (decision !== "yes") {
3839
3883
  return ok("Cancelled — ComfyUI was not restarted.");