UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

105 lines • 5.92 kB
// Comparing two ORIGINS for "is this the same server" (#1175). // // `http://127.0.0.1:8188` and `http://localhost:8188` are the same ComfyUI, and // comparing the strings says they are not. A reporter's panel was on the first // while the headless target was the second — the headless restart worked and // then probed 127.0.0.1:8188, proving they were one instance — and the // orchestrator still reported "a DIFFERENT address" and advised pointing // COMFYUI_URL at an origin it was already pointed at. // // NOT the workflow-instance fence — this header used to claim it was, and the // claim was wrong. `workflowIdentityParts` (orchestrator/session-store.ts) treats // the origin as a PRESENCE gate: it requires one to exist and never compares it // against a previously bound value. Both of its callers read `.uuid` and nothing // in the repo has ever read `identity.origin`, so two spellings of one host // cannot make a fence "adopted under one spelling refuse under the other". // // That sentence is deleted rather than softened because of what believing it // costs. The same false lead, in the pre-#1255 wording of the fence refusal // itself, already burned one full investigation and a closed-unmerged PR; it // then burned a second (#1321) via a stale quote of that message preserved in an // issue thread. A comment asserting a caller that does not exist is the same // defect as a message describing a check that does not run. // // WIRED, #1615 — and stated only because the wiring landed with it. Nothing else // changed: `QueueMonitor.start()` (services/queue-monitor.ts) is the consumer, and // it compares the PREVIOUS target against the new one to tell a real retarget from // a re-spelling of the same server, so the self-queue ledger survives the latter. // It pairs `sameOrigin` with its own base-path check, because a path prefix is not // part of an origin and two ComfyUI can share one reverse proxy. // // panel_run's duplicate fence then reads that ledger through // `selfAttributedProven` — the fence itself still compares no origins. The two // non-test importers of this module are that one and `comfyui/fetch.ts`. If a // third starts comparing origins, wire it to `sameOrigin` and say so here — but // the wiring must come first, and the sentence second. // // WHY THIS IS ITS OWN MODULE. Three loopback notions already exist // (`isLoopbackServerUrl`, `port-owner`'s LOOPBACK/WILDCARD split, // `env-capabilities`' LOCAL/REMOTE test) and they legitimately differ — one // treats `0.0.0.0` as local, another must keep bind-wildcards separate from // connect addresses. None of them answers "are these two origins the same // server", so this adds that one question rather than bending any of them. /** * Loopback spellings treated as ONE host. * * Deliberately the exact four, not all of 127.0.0.0/8: two servers really can * bind 127.0.0.1:8188 and 127.0.0.2:8188 at the same time, and calling those the * same origin would be a false identity — the failure this exists to prevent, * mirrored. `localhost` can resolve to ::1 rather than 127.0.0.1 on some * systems, which is the one genuine assumption here; it is the right one, * because when it is wrong the resulting message ("the browser can reach this * and this process cannot — a firewall, a container boundary, or a server bound * to one interface") is a BETTER description of a v4/v6 split than "point * COMFYUI_URL somewhere else" was. * * `[::1]` carries its brackets because that is what `URL.hostname` returns for * an IPv6 literal — a bare `"::1"` here would be dead code, since every input * reaches this set through `new URL()`. */ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]); /** The canonical loopback spelling, so every alias collapses to one key. */ const LOOPBACK_CANONICAL = "localhost"; /** * `scheme://host[:port]` with loopback aliases collapsed, or undefined when * `url` is not a URL. The SCHEME is never collapsed: http and https on one host * are different origins by every other rule in this codebase, and a proxy can * genuinely serve different things on each. */ export function canonicalOrigin(url) { if (typeof url !== "string" || !url.trim()) return undefined; let parsed; try { parsed = new URL(url.trim()); } catch { return undefined; } const origin = parsed.origin; // A scheme with no authority (file:, data:) has the literal origin "null". if (!origin || origin === "null") return undefined; // A single trailing dot is the fully-qualified spelling of the same name // (`localhost.` is `localhost` rooted at the DNS root), and browsers will send // it verbatim as an Origin. Stripped before the lookup, and only for the // lookup — a non-loopback host keeps whatever spelling it arrived with, since // rewriting it would change an origin nobody asked us to normalise. const host = parsed.hostname.toLowerCase(); const bare = host.endsWith(".") ? host.slice(0, -1) : host; const canonHost = LOOPBACK_HOSTS.has(bare) ? LOOPBACK_CANONICAL : host; // Rebuild rather than string-replace: `parsed.port` is "" for a default port, // which is exactly the normalisation `origin` already applies, and a host can // otherwise appear inside the scheme or a userinfo section. return `${parsed.protocol}//${canonHost}${parsed.port ? `:${parsed.port}` : ""}`; } /** True when two URLs name the same origin, loopback aliases included. */ export function sameOrigin(a, b) { const left = canonicalOrigin(a); const right = canonicalOrigin(b); // Two unparseable inputs are not "the same origin" — that would make every // piece of junk equal to every other, and callers use this to decide whether // to report a mismatch. return left !== undefined && left === right; } //# sourceMappingURL=origin.js.map