comfyui-mcp 0.52.41 → 0.52.43

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.
@@ -28,6 +28,7 @@ import { resolveBlindTabGate } from "./blind-tab-gate.js";
28
28
  import { clearPanelDiskObservation } from "../services/panel-workspace.js";
29
29
  import { panelRecoveryContext } from "../services/panel-recovery.js";
30
30
  import { isPanelAutoInstallDisabled } from "../services/panel-installer.js";
31
+ import { releaseOwnedPanelLock } from "../services/panel-pin-guard.js";
31
32
  import { SelfRestarter, canSelfRestart } from "../services/self-restart.js";
32
33
  import { pairUrlDurability } from "./pair-durability.js";
33
34
  import { loadOrCreatePairToken } from "./pair-token-store.js";
@@ -123,7 +124,7 @@ import { startPanelConsoleHttpServer } from "./panel-console-http.js";
123
124
  import { readComfyuiCrashLog, formatCrashNote } from "../services/crash-log.js";
124
125
  import { QueueMonitor } from "../services/queue-monitor.js";
125
126
  import { RunCompletions, describe as describeCorrelation, } from "./run-completion-journal.js";
126
- import { createRunCompletionWatchdog, resolveHistoryCompletionImages } from "./run-completion-watchdog.js";
127
+ import { createRunCompletionWatchdog, resolveHistoryCompletionImages, resolveHistoryCompletionStatus, } from "./run-completion-watchdog.js";
127
128
  import { AskAnswers, preview as previewQuestion } from "./ask-answer-journal.js";
128
129
  import { initRunpodWatcher, getRunpodWatcher } from "../services/runpod-watch.js";
129
130
  import { getPod } from "../services/runpod-client.js";
@@ -2913,6 +2914,12 @@ export async function runPanelOrchestrator() {
2913
2914
  backendForTab,
2914
2915
  backendOfKey: backendOf,
2915
2916
  info: (msg) => logger.info(msg),
2917
+ // panel#1557 — a unique-canvas reconnect that hello'd without `backend`
2918
+ // joined the default conversation; claiming puts it on this one so the
2919
+ // pin we just wrote is not immediately invalidated at resolution.
2920
+ claimTab: (tab, backend) => {
2921
+ tabBackends.set(tab, backend);
2922
+ },
2916
2923
  }));
2917
2924
  // #884 P1 (confirming gate 2) — a hello whose `backend` is absent or unknown
2918
2925
  // JOINS the default conversation, so its backend-qualified buffers must drain
@@ -3503,6 +3510,10 @@ export async function runPanelOrchestrator() {
3503
3510
  logger.warn(`[panel-orchestrator] readiness probe failed: ${err instanceof Error ? err.message : String(err)}`);
3504
3511
  }
3505
3512
  }
3513
+ // Assigned next to the queue-status timer below. Declared here so the hello
3514
+ // path can reconcile owed panel_run tickets the moment the panel reconnects
3515
+ // (#1556) without waiting for that timer to construct it.
3516
+ let runCompletionWatchdog;
3506
3517
  bridge.onPanelMessage = (event) => {
3507
3518
  // Connect ack: the instant a panel tab connects, the orchestrator announces
3508
3519
  // itself so "connected" means "a real agent is attending" — not merely "a
@@ -3957,6 +3968,12 @@ export async function runPanelOrchestrator() {
3957
3968
  // are change-only, so a tab connecting MID-render would otherwise wait for
3958
3969
  // the next state transition to learn a job is already running.
3959
3970
  bridge.push(buildQueueStatusFrame(QueueMonitor.snapshot()), panelTab);
3971
+ // #1556 — a hello is a reconnect (or the first chance after a missed
3972
+ // ComfyUI WS frame). Look up every still-owed panel_run in /history NOW
3973
+ // and journal a completion if ComfyUI already finished it, instead of
3974
+ // waiting the synthesis grace. Exactly-once: the journal skips a run it
3975
+ // already holds.
3976
+ void runCompletionWatchdog?.reconcile(RunCompletions.owedCompletions().map((t) => t.promptId));
3960
3977
  // Seed the RunPod control panel too — a tab that just connected gets the
3961
3978
  // current pod-status frame (or a cleared one when nothing is watched).
3962
3979
  const rpFrame = getRunpodWatcher()?.current();
@@ -5898,9 +5915,10 @@ export async function runPanelOrchestrator() {
5898
5915
  // after the grace is synthesised into the SAME journal the real frame uses.
5899
5916
  // See run-completion-watchdog.ts for why it waits, and why the panel's frame
5900
5917
  // still wins whenever it is coming.
5901
- const runCompletionWatchdog = createRunCompletionWatchdog({
5918
+ const wd = createRunCompletionWatchdog({
5902
5919
  awaiting: (promptId) => RunCompletions.awaitingCompletion(promptId),
5903
5920
  resolveOutputs: (promptId) => resolveHistoryCompletionImages(promptId),
5921
+ lookupStatus: (promptId) => resolveHistoryCompletionStatus(promptId),
5904
5922
  deliver: (payload, ticket) => {
5905
5923
  // The SAME arrival path the panel's frame takes: correlated once, here,
5906
5924
  // against the ticket that is still open — so the agent is told this is the
@@ -5913,13 +5931,14 @@ export async function runPanelOrchestrator() {
5913
5931
  flushRunCompletions(ticket.tabId);
5914
5932
  },
5915
5933
  });
5934
+ runCompletionWatchdog = wd;
5916
5935
  const queueStatusBroadcaster = createQueueStatusBroadcaster(() => QueueMonitor.snapshot(), (frame) => void bridge.push(frame),
5917
5936
  // TEE, not a second drain: drainCompletions() splices, so the watchdog has
5918
5937
  // to read the same array the broadcaster is handed rather than call it
5919
5938
  // again — a second call would race and each consumer would see only half.
5920
5939
  () => {
5921
5940
  const completions = QueueMonitor.drainCompletions();
5922
- runCompletionWatchdog.observe(completions);
5941
+ wd.observe(completions);
5923
5942
  return completions;
5924
5943
  });
5925
5944
  // Each tick first refreshes the monitor over HTTP (GET /queue + /history
@@ -5933,7 +5952,7 @@ export async function runPanelOrchestrator() {
5933
5952
  // …and only THEN expire the watchdog's arms: the broadcaster tick is what
5934
5953
  // feeds it (the drain tee above), so ticking it first would let an
5935
5954
  // observation sit a whole extra second before it is even armed. #1789.
5936
- runCompletionWatchdog.tick();
5955
+ wd.tick();
5937
5956
  });
5938
5957
  }, 1000);
5939
5958
  queueStatusTimer.unref?.();
@@ -6260,6 +6279,15 @@ export async function runPanelOrchestrator() {
6260
6279
  catch {
6261
6280
  // No lockfile / unreadable — nothing to clean up.
6262
6281
  }
6282
+ // Same for panel-op.lock: a clean exit (SIGINT or self-restart) must not
6283
+ // leave a lock whose owner is about to die (#1953). Idempotent if the
6284
+ // restarter already released it; a lock naming any other pid is left.
6285
+ try {
6286
+ releaseOwnedPanelLock();
6287
+ }
6288
+ catch {
6289
+ /* best-effort — unlock/reclaim remains the recovery if this fails */
6290
+ }
6263
6291
  };
6264
6292
  /** The single in-flight teardown, so repeated signals queue behind it. */
6265
6293
  let teardownOnce = null;