comfyui-mcp 0.51.34 → 0.51.35
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.
|
@@ -839,6 +839,69 @@ export class DownloadProgressSnapshots {
|
|
|
839
839
|
return this.rows;
|
|
840
840
|
}
|
|
841
841
|
}
|
|
842
|
+
/**
|
|
843
|
+
* #1524 — a startup that never finishes must not become a silent resident.
|
|
844
|
+
*
|
|
845
|
+
* A respawn was observed alive for hours holding NO listening ports, while an
|
|
846
|
+
* older instance owned 9180/9181/9183. That is not the bind-failure path: that
|
|
847
|
+
* one is bounded (five attempts, then `whenReady()` resolves false), tries to
|
|
848
|
+
* reclaim the port, and exits non-zero with a clear message. A process with no
|
|
849
|
+
* ports at all never got that far — it hung EARLIER, so any guard wrapped around
|
|
850
|
+
* the bind itself would miss it.
|
|
851
|
+
*
|
|
852
|
+
* Hence a deadline armed as early as this function runs and disarmed only once
|
|
853
|
+
* the port is actually held. It does not care WHERE the hang is, which is the
|
|
854
|
+
* point: the failure it prevents is not "bind failed" but "we never found out",
|
|
855
|
+
* and the reporter's framing is that silently staying alive with zero bound ports
|
|
856
|
+
* is the worst of the available outcomes.
|
|
857
|
+
*
|
|
858
|
+
* WHAT IT DOES NOT COVER, because an earlier draft of this comment claimed "the
|
|
859
|
+
* whole of startup" and that was false (codex):
|
|
860
|
+
*
|
|
861
|
+
* - anything before `boot.ts` finishes dynamically importing this module — the
|
|
862
|
+
* timer does not exist yet;
|
|
863
|
+
* - a SYNCHRONOUS stall anywhere, which no timer can interrupt, because the
|
|
864
|
+
* event loop it would fire on is the one that is blocked.
|
|
865
|
+
*
|
|
866
|
+
* So this closes the async-hang shape of the report and cannot close the
|
|
867
|
+
* synchronous one. If a portless process is ever seen again with this in place,
|
|
868
|
+
* that difference is the first thing to check, and it is written down here so the
|
|
869
|
+
* next reader does not have to rediscover it.
|
|
870
|
+
*
|
|
871
|
+
* Generous by default (90s) because a cold `npx` start on a slow disk is
|
|
872
|
+
* legitimately slow, and env-tunable for pathological machines. Exits non-zero so
|
|
873
|
+
* a supervisor restarts rather than inheriting a half-alive process.
|
|
874
|
+
*/
|
|
875
|
+
export function armStartupDeadline(port, deps = {}) {
|
|
876
|
+
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
877
|
+
const findIncumbent = deps.incumbent ?? pidListeningOnPort;
|
|
878
|
+
// CLAMPED, not merely "positive and finite" (codex). Node coerces a
|
|
879
|
+
// sub-millisecond delay AND anything past the 32-bit signed limit to 1ms — so
|
|
880
|
+
// `0.5`, or a large number typed by someone trying to RAISE the deadline, would
|
|
881
|
+
// fire almost instantly and kill every healthy startup. A guard whose escape
|
|
882
|
+
// hatch can cause the outage it prevents is worse than no guard, so out-of-range
|
|
883
|
+
// values fall back to the default rather than being honoured literally.
|
|
884
|
+
const raw = Number(process.env.COMFYUI_MCP_STARTUP_DEADLINE_MS);
|
|
885
|
+
const MIN_MS = 1_000;
|
|
886
|
+
const MAX_MS = 2_147_483_647; // Node's setTimeout ceiling
|
|
887
|
+
const ms = Number.isFinite(raw) && raw >= MIN_MS && raw <= MAX_MS ? Math.floor(raw) : 90_000;
|
|
888
|
+
const timer = setTimeout(() => {
|
|
889
|
+
const incumbent = findIncumbent(port);
|
|
890
|
+
logger.error(`[panel-orchestrator] startup did not complete within ${Math.round(ms / 1000)}s and this ` +
|
|
891
|
+
`process holds no bridge port — exiting rather than lingering with no way to serve panel_* ` +
|
|
892
|
+
`tools.` +
|
|
893
|
+
(incumbent
|
|
894
|
+
? ` Port ${port} is held by pid ${incumbent}; if that is an older comfyui-mcp, stop it ` +
|
|
895
|
+
`and start this one again.`
|
|
896
|
+
: ` Nothing is listening on ${port} either, so the hang is before the bind — please ` +
|
|
897
|
+
`report this with the last lines above (#1524).`) +
|
|
898
|
+
` Raise COMFYUI_MCP_STARTUP_DEADLINE_MS if this machine is legitimately slower than that.`);
|
|
899
|
+
exit(1);
|
|
900
|
+
}, ms);
|
|
901
|
+
// Never hold the event loop open on this timer's account.
|
|
902
|
+
timer.unref?.();
|
|
903
|
+
return () => clearTimeout(timer);
|
|
904
|
+
}
|
|
842
905
|
export async function runPanelOrchestrator() {
|
|
843
906
|
// Crash guard: the orchestrator is a long-lived background process the user
|
|
844
907
|
// can't see. A stray rejection (e.g. a fire-and-forget push to a tab that
|
|
@@ -849,6 +912,10 @@ export async function runPanelOrchestrator() {
|
|
|
849
912
|
// mid-flight, an SDK hiccup) and must NOT kill the orchestrator — log + continue.
|
|
850
913
|
logger.error(`[panel-orchestrator] unhandled rejection (ignored): ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}`);
|
|
851
914
|
});
|
|
915
|
+
// #1524 — armed HERE, before anything that can block, and disarmed only once
|
|
916
|
+
// the bridge port is actually held. The port is re-derived rather than passed
|
|
917
|
+
// in because the deadline has to exist before the block that computes it.
|
|
918
|
+
const disarmStartupDeadline = armStartupDeadline(Number(process.env.COMFYUI_MCP_BRIDGE_PORT) || 9180);
|
|
852
919
|
process.on("uncaughtException", (err) => {
|
|
853
920
|
// A synchronous uncaught throw leaves the process in an UNDEFINED state. The
|
|
854
921
|
// old "log + continue" here was a zombie root cause — the orchestrator stayed
|
|
@@ -1040,6 +1107,9 @@ export async function runPanelOrchestrator() {
|
|
|
1040
1107
|
logger.error(`[panel-orchestrator] could not bind the panel bridge port — another process owns it. Free that port and restart the orchestrator. Override the port with COMFYUI_MCP_BRIDGE_PORT.\n${portKillHint(lockPort)}`);
|
|
1041
1108
|
process.exit(1);
|
|
1042
1109
|
}
|
|
1110
|
+
// The port is ours — startup got where it needed to. Everything after this is
|
|
1111
|
+
// long-running work the deadline must not police.
|
|
1112
|
+
disarmStartupDeadline();
|
|
1043
1113
|
// With a pinned pair token, bring the LAN pairing listener up now so a phone's
|
|
1044
1114
|
// saved URL reconnects across restarts without ever touching the panel. This is
|
|
1045
1115
|
// strictly opt-in (COMFYUI_MCP_PAIR_TOKEN unset → the on-demand behavior above is
|