comfyui-mcp 0.50.82 → 0.50.84
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.
|
@@ -960,51 +960,13 @@ async function probeDeclineRecovery(base, windowMs, intervalMs, probeTimeoutMs,
|
|
|
960
960
|
waited_ms: Date.now() - start,
|
|
961
961
|
};
|
|
962
962
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
* boot and IMMUTABLE. It is deliberately NOT getComfyUIBaseUrl(): that reflects the
|
|
971
|
-
* mutable runtime config a panel `hello` can retarget (applyComfyuiUrl →
|
|
972
|
-
* setComfyuiTarget), so a client could steer it. And it is NEVER the client-advertised
|
|
973
|
-
* `hello.comfyui_url` (spoofable; comfyuiFetch would leak the configured ComfyUI auth
|
|
974
|
-
* headers to an attacker-chosen origin). The tab origin is used ONLY as a gate, and the
|
|
975
|
-
* gate reads the SERVER-OBSERVED handshake Origin (tabServerOrigin) — which the browser
|
|
976
|
-
* sets and blocks page JS from forging — NOT the spoofable hello.comfyui_url: we
|
|
977
|
-
* self-probe our OWN boot endpoint solely when the rebooted tab PROVABLY (by its
|
|
978
|
-
* handshake) fronts THAT SAME instance, so a socket that merely CLAIMS the boot URL can't
|
|
979
|
-
* ride an unrelated boot-instance cycle to a false certification (codex High). Null
|
|
980
|
-
* (→ honest dispatched-unconfirmed) when:
|
|
981
|
-
* - cloud OR remote mode; or
|
|
982
|
-
* - the orchestrator didn't boot against a LOCAL loopback ComfyUI; or
|
|
983
|
-
* - the tab isn't SERVER-TRUSTED-local (tabIsLocal — arrived on the token-less
|
|
984
|
-
* loopback primary listener; relay/tunnel/LAN/pairing → false); or
|
|
985
|
-
* - the tab's HANDSHAKE origin is absent, ambiguous (`localhost`), or does NOT match our
|
|
986
|
-
* boot endpoint by scheme+host+port (concrete-family loopback-canonicalized) — i.e. it
|
|
987
|
-
* drives a DIFFERENT instance / family, or one we can't verify; AND, because a handshake
|
|
988
|
-
* Origin carries NO path, a boot target mounted under a basePath fails path-aware
|
|
989
|
-
* identity and is (soundly) fail-closed to dispatched-unconfirmed.
|
|
990
|
-
*/
|
|
991
|
-
/**
|
|
992
|
-
* #851 — what to tell the caller about `restart_comfyui` when the panel's confirmation
|
|
993
|
-
* card timed out.
|
|
994
|
-
*
|
|
995
|
-
* The old text recommended it unconditionally. It targets COMFYUI_URL, not the ComfyUI
|
|
996
|
-
* the panel runs inside, so a reporter who followed that advice aimed a restart at a
|
|
997
|
-
* different server than the one they had been working on — and got "No ComfyUI process
|
|
998
|
-
* found on port 8188" while the panel was working fine on the live canvas.
|
|
999
|
-
*
|
|
1000
|
-
* `panelBase` comes from captureRebootHealthBase(), which is a PROOF rather than a
|
|
1001
|
-
* comparison: null in remote/cloud mode, and whenever the tab's origin cannot be shown
|
|
1002
|
-
* to front the local boot instance. "Unproven" is deliberately NOT folded into
|
|
1003
|
-
* "different" — that would fire the warning on ordinary remote installs where the two
|
|
1004
|
-
* ARE the same server, trading a wrong recommendation for a wrong alarm.
|
|
1005
|
-
*
|
|
1006
|
-
* Pure, so the three-way decision is testable without a live panel.
|
|
1007
|
-
*/
|
|
963
|
+
export function classifyTabReconnect({ serverReady, baselineCaptured, tabBack, }) {
|
|
964
|
+
if (tabBack === true)
|
|
965
|
+
return true; // a newer hello from the same tab was seen
|
|
966
|
+
if (!serverReady)
|
|
967
|
+
return "unknown"; // never watched — the server never came back
|
|
968
|
+
return baselineCaptured ? false : "unknown";
|
|
969
|
+
}
|
|
1008
970
|
export function restartTimeoutFallbackAdvice({ headlessBase, panelBase, observedOrigin = null, }) {
|
|
1009
971
|
if (panelBase != null && sameHttpBase(headlessBase, panelBase)) {
|
|
1010
972
|
return "or use restart_comfyui to restart the server directly without a panel card.";
|
|
@@ -2615,27 +2577,53 @@ function currentWorkflowFence(ctx) {
|
|
|
2615
2577
|
function corroborateActiveForFence(parsed) {
|
|
2616
2578
|
const active = parsed.active;
|
|
2617
2579
|
if (!active || typeof active !== "object") {
|
|
2618
|
-
|
|
2580
|
+
// SETTLES: mid-restore the frontend genuinely has no active workflow yet —
|
|
2581
|
+
// that is #1184's finding in this reply's terms, and it is the window here.
|
|
2582
|
+
return { ok: false, why: "the reply carried no active-workflow record", settles: true };
|
|
2619
2583
|
}
|
|
2620
2584
|
// #514: the panel says so itself when its active record is not confirmed. An
|
|
2621
2585
|
// explicit `false` is the panel telling us the value is untrustworthy — never
|
|
2622
2586
|
// adopt through that. (Absent = an older panel that cannot say; the list
|
|
2623
2587
|
// corroboration below then has to carry the whole weight.)
|
|
2624
2588
|
if (parsed.active_confirmed === false) {
|
|
2625
|
-
|
|
2589
|
+
// SETTLES: this is the reported window itself — the panel says "not yet",
|
|
2590
|
+
// and a moment later it says otherwise (#1292).
|
|
2591
|
+
return {
|
|
2592
|
+
ok: false,
|
|
2593
|
+
why: "the panel reported its active workflow as UNCONFIRMED",
|
|
2594
|
+
settles: true,
|
|
2595
|
+
};
|
|
2626
2596
|
}
|
|
2627
2597
|
const list = parsed.workflows;
|
|
2628
|
-
|
|
2598
|
+
// TWO DIFFERENT FACTS, and #1292 made the difference cost real time. An ABSENT
|
|
2599
|
+
// `workflows` field is a property of the installed build — it will not appear
|
|
2600
|
+
// by waiting — while an EMPTY list is a snapshot of what is open right now,
|
|
2601
|
+
// which a mid-restore panel reports before its tabs come back. Folding them
|
|
2602
|
+
// would make every recovery on an older panel 2.9s slower for nothing.
|
|
2603
|
+
if (!Array.isArray(list)) {
|
|
2629
2604
|
return {
|
|
2630
2605
|
ok: false,
|
|
2631
2606
|
why: "the reply carried no open-workflow list to corroborate the active record against",
|
|
2607
|
+
settles: false,
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
if (list.length === 0) {
|
|
2611
|
+
return {
|
|
2612
|
+
ok: false,
|
|
2613
|
+
why: "the reply's open-workflow list was empty, so nothing corroborates the active record",
|
|
2614
|
+
settles: true,
|
|
2632
2615
|
};
|
|
2633
2616
|
}
|
|
2634
2617
|
// Only an AFFIRMATIVE per-record flag can nominate the corroborating entry;
|
|
2635
2618
|
// inferring it from the active object would be circular.
|
|
2636
2619
|
const flaggedActive = list.filter((w) => !!w && typeof w === "object" && w.active === true);
|
|
2637
2620
|
if (flaggedActive.length === 0) {
|
|
2638
|
-
|
|
2621
|
+
// SETTLES: a snapshot taken before the panel re-flagged its active tab.
|
|
2622
|
+
return {
|
|
2623
|
+
ok: false,
|
|
2624
|
+
why: "no entry in the open-workflow list is marked active",
|
|
2625
|
+
settles: true,
|
|
2626
|
+
};
|
|
2639
2627
|
}
|
|
2640
2628
|
if (flaggedActive.length > 1) {
|
|
2641
2629
|
// A mixed/partial snapshot. Exactly the shape that would let another canvas's
|
|
@@ -2643,6 +2631,8 @@ function corroborateActiveForFence(parsed) {
|
|
|
2643
2631
|
return {
|
|
2644
2632
|
ok: false,
|
|
2645
2633
|
why: `${flaggedActive.length} entries in the open-workflow list are marked active (a mixed reply)`,
|
|
2634
|
+
// SETTLES: "mixed" IS the half-reconciled state, by definition.
|
|
2635
|
+
settles: true,
|
|
2646
2636
|
};
|
|
2647
2637
|
}
|
|
2648
2638
|
// Same tri-state primitive the pin path uses. `false` = they name DIFFERENT
|
|
@@ -2653,12 +2643,17 @@ function corroborateActiveForFence(parsed) {
|
|
|
2653
2643
|
return {
|
|
2654
2644
|
ok: false,
|
|
2655
2645
|
why: "the active record and the entry marked active in the open-workflow list name DIFFERENT workflows (a stale or mixed reply)",
|
|
2646
|
+
// SETTLES: a stale half of the snapshot catches up.
|
|
2647
|
+
settles: true,
|
|
2656
2648
|
};
|
|
2657
2649
|
}
|
|
2658
2650
|
if (verdict !== true) {
|
|
2659
2651
|
return {
|
|
2660
2652
|
ok: false,
|
|
2661
2653
|
why: "the active record and the open-workflow list share no comparable identity field, so nothing corroborates it",
|
|
2654
|
+
// DOES NOT SETTLE: a field the records do not carry will not appear by
|
|
2655
|
+
// waiting — that is the reply's SHAPE, not its timing.
|
|
2656
|
+
settles: false,
|
|
2662
2657
|
};
|
|
2663
2658
|
}
|
|
2664
2659
|
return { ok: true, active: active };
|
|
@@ -2725,6 +2720,36 @@ WHY THIS READ WAS NEEDED AT ALL: this session's panel is ${v.version}, and a ` +
|
|
|
2725
2720
|
return "";
|
|
2726
2721
|
}
|
|
2727
2722
|
}
|
|
2723
|
+
/**
|
|
2724
|
+
* #1292 — THE RECONCILIATION WINDOW IS OURS TO ABSORB, NOT THE CALLER'S TO WAIT OUT.
|
|
2725
|
+
*
|
|
2726
|
+
* Right after `panel_restart_comfyui` and the tab's reconnect, the panel is
|
|
2727
|
+
* briefly mid-reconciliation: it answers `workflow_list`, but its active record
|
|
2728
|
+
* is marked UNCONFIRMED (or the list is momentarily mixed). Corroboration
|
|
2729
|
+
* correctly refuses — and the caller got a hard failure for a state that fixes
|
|
2730
|
+
* itself, with the remedy "call this again in a moment". The reporter did, and
|
|
2731
|
+
* the identical call returned `already_current`.
|
|
2732
|
+
*
|
|
2733
|
+
* A recovery operation that tells you to retry it is one that has not finished
|
|
2734
|
+
* recovering. So this path rechecks with a FRESH read, three times over ~2.9s.
|
|
2735
|
+
*
|
|
2736
|
+
* WHY THESE AND NOT A LONGER BUDGET: this runs only when the rebind has already
|
|
2737
|
+
* failed, so the cost is paid on a path that was going to fail anyway — but it is
|
|
2738
|
+
* still latency added to a tool call, and the window this covers is a local
|
|
2739
|
+
* process reconnecting. The reporter's five seconds is an upper bound with a
|
|
2740
|
+
* human in it, not a measurement of the settle.
|
|
2741
|
+
*
|
|
2742
|
+
* WHY ONLY `uncorroborated`: the sibling failure `no_uuid` means the installed
|
|
2743
|
+
* panel exposes no workflow identity at all. Rechecking a build that will never
|
|
2744
|
+
* answer differently just makes the same error slower — the distinction the
|
|
2745
|
+
* status already draws, now with teeth.
|
|
2746
|
+
*
|
|
2747
|
+
* The corroboration rule itself does NOT move. It still fails closed on every
|
|
2748
|
+
* attempt; a recheck buys a fresh snapshot to judge, never a lower bar. What
|
|
2749
|
+
* would be unsafe is arbitrating a mixed reply, and that is exactly what is not
|
|
2750
|
+
* happening here.
|
|
2751
|
+
*/
|
|
2752
|
+
const FENCE_CORROBORATION_RECHECK_STEPS_MS = [400, 900, 1600];
|
|
2728
2753
|
async function rebindWorkflowFence(ctx) {
|
|
2729
2754
|
const tabAtStart = ctx.tabId;
|
|
2730
2755
|
let before = currentWorkflowFence(ctx);
|
|
@@ -2738,29 +2763,68 @@ async function rebindWorkflowFence(ctx) {
|
|
|
2738
2763
|
if (ctx.tabId !== tabAtStart)
|
|
2739
2764
|
before = currentWorkflowFence(ctx);
|
|
2740
2765
|
};
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2766
|
+
// One read + corroboration. Returns null when the READ failed in a way that is
|
|
2767
|
+
// its own status (`unreadable`/`healed_by_panel`) — those are not the transient
|
|
2768
|
+
// window this rechecks, and the settle inside unreadableOrHealed already covers
|
|
2769
|
+
// the case where the refusal itself repaired the fence.
|
|
2770
|
+
const readAndCorroborate = async () => {
|
|
2771
|
+
let parsed = null;
|
|
2772
|
+
try {
|
|
2773
|
+
const res = await ctx.call({ cmd: "workflow_list" }, 6000);
|
|
2774
|
+
syncFenceToCurrentTab();
|
|
2775
|
+
if (res?.isError) {
|
|
2776
|
+
return { done: await unreadableOrHealed(ctx, before, toolResultText(res)) };
|
|
2777
|
+
}
|
|
2778
|
+
parsed = parseToolResultJson(res);
|
|
2747
2779
|
}
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2780
|
+
catch (err) {
|
|
2781
|
+
syncFenceToCurrentTab();
|
|
2782
|
+
return {
|
|
2783
|
+
done: await unreadableOrHealed(ctx, before, err instanceof Error ? err.message : String(err ?? "unknown error")),
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
if (!parsed) {
|
|
2787
|
+
return {
|
|
2788
|
+
done: await unreadableOrHealed(ctx, before, "the panel's workflow_list reply was not readable as JSON"),
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
// CORROBORATE before adopting. The uuid must come from a record the panel's own
|
|
2792
|
+
// open-workflow list agrees is the live canvas — otherwise a stale or mixed
|
|
2793
|
+
// reply's uuid (valid in shape, belonging to ANOTHER canvas) would overwrite
|
|
2794
|
+
// this tab's stamp and be reported as a successful rebind.
|
|
2795
|
+
return { corroborated: corroborateActiveForFence(parsed) };
|
|
2796
|
+
};
|
|
2797
|
+
const first = await readAndCorroborate();
|
|
2798
|
+
if ("done" in first)
|
|
2799
|
+
return first.done;
|
|
2800
|
+
let corroborated = first.corroborated;
|
|
2801
|
+
let attempts = 1;
|
|
2802
|
+
let waitedMs = 0;
|
|
2803
|
+
// #1292 — recheck the RECONCILIATION WINDOW with a fresh read, never a lower bar.
|
|
2804
|
+
// Only a failure that CAN settle is worth re-reading (codex review, P2): a reply
|
|
2805
|
+
// whose SHAPE is wrong — an older build that sends no `workflows` array, records
|
|
2806
|
+
// sharing no comparable identity field — will say exactly the same thing 2.9s
|
|
2807
|
+
// later, so retrying it just makes the identical error slower. That is the same
|
|
2808
|
+
// reasoning that keeps `no_uuid` out of this loop, applied one level down.
|
|
2809
|
+
for (const waitMs of FENCE_CORROBORATION_RECHECK_STEPS_MS) {
|
|
2810
|
+
if (corroborated.ok || !corroborated.settles)
|
|
2811
|
+
break;
|
|
2812
|
+
await sleep(waitMs);
|
|
2813
|
+
waitedMs += waitMs;
|
|
2814
|
+
attempts++;
|
|
2815
|
+
const next = await readAndCorroborate();
|
|
2816
|
+
if ("done" in next)
|
|
2817
|
+
return next.done;
|
|
2818
|
+
corroborated = next.corroborated;
|
|
2756
2819
|
}
|
|
2757
|
-
// CORROBORATE before adopting. The uuid must come from a record the panel's own
|
|
2758
|
-
// open-workflow list agrees is the live canvas — otherwise a stale or mixed
|
|
2759
|
-
// reply's uuid (valid in shape, belonging to ANOTHER canvas) would overwrite
|
|
2760
|
-
// this tab's stamp and be reported as a successful rebind.
|
|
2761
|
-
const corroborated = corroborateActiveForFence(parsed);
|
|
2762
2820
|
if (!corroborated.ok) {
|
|
2763
|
-
return {
|
|
2821
|
+
return {
|
|
2822
|
+
status: "no_identity",
|
|
2823
|
+
before,
|
|
2824
|
+
kind: "uncorroborated",
|
|
2825
|
+
why: corroborated.why,
|
|
2826
|
+
rechecks: { attempts, waitedMs, settles: corroborated.settles },
|
|
2827
|
+
};
|
|
2764
2828
|
}
|
|
2765
2829
|
const active = corroborated.active;
|
|
2766
2830
|
const uuid = responseWorkflowUuid(active);
|
|
@@ -3100,9 +3164,28 @@ panelGapNote = "") {
|
|
|
3100
3164
|
`plain reload can serve the same cached bundle again; only a hard refresh replaces ` +
|
|
3101
3165
|
`it. If a hard refresh does not help, the installed pack itself predates the ` +
|
|
3102
3166
|
`per-workflow identity and must be UPDATED — no rebind can add it.`
|
|
3103
|
-
:
|
|
3104
|
-
|
|
3105
|
-
|
|
3167
|
+
: // #1292 — DO NOT PRESCRIBE WHAT WE JUST DID. This path now rechecks
|
|
3168
|
+
// with fresh reads before failing, so "call this again in a moment"
|
|
3169
|
+
// would be advice the tool already took on the caller's behalf — and
|
|
3170
|
+
// taking it a fourth time is the least likely thing left to work.
|
|
3171
|
+
// When the rechecks ran, say so and move on to the remedy that has
|
|
3172
|
+
// not been tried.
|
|
3173
|
+
(r.rechecks && !r.rechecks.settles
|
|
3174
|
+
? // Waiting cannot fix a reply whose SHAPE is wrong, so do not
|
|
3175
|
+
// prescribe waiting. This said "call this again in a moment" to
|
|
3176
|
+
// a caller whose panel will answer identically forever.
|
|
3177
|
+
`\n\nWHY RETRYING WILL NOT HELP: this is the shape of the panel's reply, not a ` +
|
|
3178
|
+
`moment in its reconciliation — a later read answers the same way. ` +
|
|
3179
|
+
`${RELOAD_TAB_REMEDY} If it survives a hard refresh, the installed pack is too ` +
|
|
3180
|
+
`old to corroborate its own active workflow and must be UPDATED.`
|
|
3181
|
+
: r.rechecks && r.rechecks.attempts > 1
|
|
3182
|
+
? `\n\nALREADY TRIED: the panel was re-read ${r.rechecks.attempts} times over ` +
|
|
3183
|
+
`~${(r.rechecks.waitedMs / 1000).toFixed(1)}s and never corroborated. That window ` +
|
|
3184
|
+
`covers the usual post-restart reconciliation, so this is probably NOT the ` +
|
|
3185
|
+
`transient case.\n\nWHAT TO DO: ${RELOAD_TAB_REMEDY}`
|
|
3186
|
+
: `\n\nWHAT TO DO: call this again in a moment. A stale or mixed workflow list is ` +
|
|
3187
|
+
`usually transient — it settles once the panel finishes reconciling its tabs. ` +
|
|
3188
|
+
`If it persists across a few attempts: ${RELOAD_TAB_REMEDY}`);
|
|
3106
3189
|
if (!r.before.known) {
|
|
3107
3190
|
return {
|
|
3108
3191
|
binding: "unverified",
|
|
@@ -9036,12 +9119,20 @@ export function buildPanelToolDefs() {
|
|
|
9036
9119
|
: true
|
|
9037
9120
|
: false;
|
|
9038
9121
|
const graphToolsReady = tabBack && (ctx.tabCanMutateGraph ? ctx.tabCanMutateGraph() : true);
|
|
9122
|
+
// #654 — `ready`/`graph_tools_ready` still come from the BOOLEAN, so an
|
|
9123
|
+
// undetermined reconnect withholds graph tools exactly as before. Only
|
|
9124
|
+
// the reported observation changes.
|
|
9125
|
+
const tabReconnect = classifyTabReconnect({
|
|
9126
|
+
serverReady: recovery.ready,
|
|
9127
|
+
baselineCaptured: preRestartPanelIdentity != null,
|
|
9128
|
+
tabBack,
|
|
9129
|
+
});
|
|
9039
9130
|
return ok({
|
|
9040
9131
|
rebooting: true,
|
|
9041
9132
|
ready: graphToolsReady,
|
|
9042
9133
|
graph_tools_ready: graphToolsReady,
|
|
9043
9134
|
server_ready: recovery.ready,
|
|
9044
|
-
panel_tab_reconnected:
|
|
9135
|
+
panel_tab_reconnected: tabReconnect,
|
|
9045
9136
|
confirmed_cycle: observed, // true = we directly observed the down→up cycle
|
|
9046
9137
|
recovered_ms: recovery.waited_ms,
|
|
9047
9138
|
probes: recovery.attempts,
|
|
@@ -9051,8 +9142,20 @@ export function buildPanelToolDefs() {
|
|
|
9051
9142
|
? "ComfyUI-Manager (legacy 3.x) had no reboot endpoint; the headless managed restart " +
|
|
9052
9143
|
`came back healthy in ${(recovery.waited_ms / 1000).toFixed(1)}s, but ` +
|
|
9053
9144
|
(!tabBack
|
|
9054
|
-
?
|
|
9055
|
-
|
|
9145
|
+
? tabReconnect === "unknown"
|
|
9146
|
+
? "whether the panel tab reconnected could NOT be determined — no pre-restart " +
|
|
9147
|
+
"baseline was captured for it, so nothing was watched. WHY is not established " +
|
|
9148
|
+
"here, and it is one of: the tab's socket was not open at the instant the " +
|
|
9149
|
+
"restart was dispatched; the panel advertised no tab session id (an older " +
|
|
9150
|
+
"build, or its browser-tab lease was refused because a duplicate tab holds " +
|
|
9151
|
+
"it); or the tab did not resolve at all. The tab may well be back. Graph " +
|
|
9152
|
+
"tools are withheld (ready:false) because that is unproven, NOT because the " +
|
|
9153
|
+
'tab is known to be gone: call panel_list_workflows or panel_set_workflow_target({mode:"current"}) ' +
|
|
9154
|
+
"to find out, and only refresh the browser if those also fail. If this " +
|
|
9155
|
+
"repeats on every restart, the panel is probably too old to advertise a tab " +
|
|
9156
|
+
"session id — update it."
|
|
9157
|
+
: "the panel tab has NOT reconnected yet (ready:false). Wait a moment then retry, or " +
|
|
9158
|
+
'rebind with panel_set_workflow_target({mode:"current"}) before issuing graph tools.'
|
|
9056
9159
|
: "the panel tab reconnected but cannot safely run graph mutations (ready:false), usually " +
|
|
9057
9160
|
"because it is still running a stale panel bundle. Hard-refresh the ComfyUI browser tab " +
|
|
9058
9161
|
"(Ctrl+Shift+R) before issuing graph tools; if that does not restore it, update the panel " +
|
|
@@ -9171,6 +9274,14 @@ export function buildPanelToolDefs() {
|
|
|
9171
9274
|
// Old/lightweight contexts have no capability accessor, so preserve their historical
|
|
9172
9275
|
// contract rather than claiming a production tab passed a check it never ran.
|
|
9173
9276
|
const graphToolsReady = tabBack && (ctx.tabCanMutateGraph ? ctx.tabCanMutateGraph() : true);
|
|
9277
|
+
// #654 — same split as the legacy path above. The gate is unchanged:
|
|
9278
|
+
// `ready`/`graph_tools_ready` still key off the boolean, so an undetermined
|
|
9279
|
+
// reconnect still withholds graph tools. Only the reported observation changes.
|
|
9280
|
+
const tabReconnect = classifyTabReconnect({
|
|
9281
|
+
serverReady: true, // this branch only runs on a confirmed healthy cycle
|
|
9282
|
+
baselineCaptured: preRestartPanelIdentity != null,
|
|
9283
|
+
tabBack,
|
|
9284
|
+
});
|
|
9174
9285
|
// #848: WHAT THIS RESTART DID NOT DO. "It came back healthy" answers a
|
|
9175
9286
|
// different question from "did it come back with the launch arguments I just
|
|
9176
9287
|
// configured?", and a user who had edited ComfyUI Desktop's saved launch args
|
|
@@ -9207,7 +9318,7 @@ export function buildPanelToolDefs() {
|
|
|
9207
9318
|
probes: recovery.attempts,
|
|
9208
9319
|
saw_down: recovery.sawDown,
|
|
9209
9320
|
via: recovery.via,
|
|
9210
|
-
panel_tab_reconnected:
|
|
9321
|
+
panel_tab_reconnected: tabReconnect,
|
|
9211
9322
|
note: (tabBack && !graphToolsReady
|
|
9212
9323
|
? `ComfyUI restart accepted and it is healthy again in ${(recovery.waited_ms / 1000).toFixed(1)}s` +
|
|
9213
9324
|
" (observed it go down then come back); the panel tab reconnected but cannot safely run " +
|
|
@@ -9219,9 +9330,20 @@ export function buildPanelToolDefs() {
|
|
|
9219
9330
|
(dropped ? "; connection dropped as expected while it went down" : "") +
|
|
9220
9331
|
(tabBack
|
|
9221
9332
|
? "; the panel tab reconnected — graph tools are ready."
|
|
9222
|
-
:
|
|
9223
|
-
|
|
9224
|
-
|
|
9333
|
+
: tabReconnect === "unknown"
|
|
9334
|
+
? "; ComfyUI is back, but whether the panel tab reconnected could NOT be determined — no " +
|
|
9335
|
+
"pre-restart baseline was captured for it, so nothing was watched. WHY is not " +
|
|
9336
|
+
"established here, and it is one of: its socket was not open at the instant the " +
|
|
9337
|
+
"restart was dispatched; the panel advertised no tab session id (an older build, or " +
|
|
9338
|
+
"a refused browser-tab lease); or the tab did not resolve at all. The tab may well " +
|
|
9339
|
+
"be back. Graph tools are withheld (ready:false) because that is UNPROVEN, not " +
|
|
9340
|
+
"because the tab is known to be gone: call panel_list_workflows or " +
|
|
9341
|
+
'panel_set_workflow_target({mode:"current"}) to find out, and refresh the browser only ' +
|
|
9342
|
+
"if those also fail. If this repeats on every restart, the panel is probably too old " +
|
|
9343
|
+
"to advertise a tab session id — update it."
|
|
9344
|
+
: "; ComfyUI is back but the panel tab has NOT reconnected yet (ready:false) — " +
|
|
9345
|
+
'wait a moment then retry, or rebind with panel_set_workflow_target({mode:"current"}) ' +
|
|
9346
|
+
"before issuing graph tools.") +
|
|
9225
9347
|
".") + argvNote,
|
|
9226
9348
|
});
|
|
9227
9349
|
}),
|