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),

137 lines • 5.51 kB
/** * #1963 — periodic panel version check, with APPLY gated the same way as the * orchestrator self-update. * * Hello-time panel-sync is a FLOOR check (the minimum this orchestrator * needs). Most panel fixes never raise that floor, so a pack three versions * behind still reads "healthy". This path asks the published pyproject for * the newest panel, which is a few KB and safe on a metered link, and only * runs the verified `runPanelAction("update")` when the caller says APPLY. * * Pin / dev-symlink / auto-install-off / remote still refuse to touch the * pack. A failed latest-version probe never applies — we do not churn nightly * just because GitHub did not answer. */ import { parsePyproject } from "./node-authoring.js"; import { isPanelAutoInstallDisabled, PANEL_REGISTRY_ID, panelStatus, runPanelAction, } from "./panel-installer.js"; import { isNewer } from "./self-update.js"; import { unreachableHostMessage } from "../utils/errors.js"; import { logger } from "../utils/logger.js"; import { deferredAutoUpdateNote } from "./auto-update-gate.js"; /** Raw pyproject — the pack's published version, not GitHub Releases (those lag). */ export const PANEL_PYPROJECT_RAW_URL = "https://raw.githubusercontent.com/artokun/comfyui-mcp-panel/main/pyproject.toml"; const LATEST_TIMEOUT_MS = 5_000; async function defaultLatestVersion() { return getLatestPublishedPanelVersion(); } const defaultDeps = { env: () => process.env, panelStatus: () => panelStatus(), latestVersion: defaultLatestVersion, updatePanel: async () => { await runPanelAction("update"); const after = await panelStatus(); return { installedVersion: after.installedVersion }; }, }; /** * Published panel version from the pack's pyproject on main. Never throws. * Refuses a body that is not this pack — a hijacked / wrong-repo response * must not become "latest". */ export async function getLatestPublishedPanelVersion(fetchImpl = fetch) { let res; try { res = await fetchImpl(PANEL_PYPROJECT_RAW_URL, { signal: AbortSignal.timeout(LATEST_TIMEOUT_MS), headers: { accept: "text/plain" }, }); } catch (err) { logger.debug(`[panel-auto-update] latest-version probe unreachable: ${unreachableHostMessage(err, PANEL_PYPROJECT_RAW_URL, "the panel version check").message}`); return undefined; } if (!res.ok) { logger.debug(`[panel-auto-update] latest-version probe HTTP ${res.status} (host answered)`); return undefined; } try { const toml = await res.text(); const parsed = parsePyproject(toml); if (parsed.projectName !== PANEL_REGISTRY_ID) return undefined; const v = parsed.version?.trim(); return v || undefined; } catch { return undefined; } } /** * One periodic tick: CHECK always (when we can), APPLY only when `apply` is * true. The caller owns the transport gate — this function must not re-derive * it, or the two will drift. */ export async function tickPanelAutoUpdate(opts = { apply: true }) { const deps = { ...defaultDeps, ...opts.deps }; try { if (isPanelAutoInstallDisabled(deps.env())) { return { action: "skipped-disabled", note: "Panel auto-update disabled via COMFYUI_MCP_PANEL_AUTOINSTALL." }; } const status = await deps.panelStatus(); if (!status.applicable) { return { action: "unavailable", note: status.note || "Panel auto-update does not apply here." }; } if (status.isDevSymlink) { return { action: "skipped-dev", from: status.installedVersion, note: "Dev symlink — panel auto-update will not modify it.", }; } if (status.pin?.pinned) { return { action: "skipped-pin", from: status.installedVersion, note: "Panel is pinned — auto-update will not move it.", }; } const latest = await deps.latestVersion(); const current = status.installedVersion; if (!latest || !current) { return { action: "unavailable", from: current, to: latest, note: "Could not determine the latest published panel version. This is NOT evidence that you are up to date.", }; } if (!isNewer(latest, current)) { return { action: "up-to-date", from: current, to: latest }; } if (!opts.apply) { return { action: "deferred", from: current, to: latest, note: deferredAutoUpdateNote({ component: "panel", from: current, to: latest }), }; } const after = await deps.updatePanel(); return { action: "updated", from: current, to: after.installedVersion ?? latest, note: `Updated ComfyUI-MCP panel ${current} → ${after.installedVersion ?? latest}. ` + `Restart ComfyUI to load it.`, }; } catch (err) { logger.debug(`[panel-auto-update] tick failed: ${err instanceof Error ? err.message : String(err)}`); return { action: "unavailable", note: err instanceof Error ? err.message : String(err), }; } } //# sourceMappingURL=panel-auto-update.js.map