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

68 lines • 2.82 kB
// Per-panel-tab workflow target: which open ComfyUI workflow tab the agent // should edit. Without pinning, graph_* commands hit whatever workflow the user // is currently viewing — browsing another tab mid-conversation redirects edits. // // Pinning injects `workflow_path` on workflow-scoped bridge commands. The panel can // only read/edit the workflow currently IN VIEW (its graph executors run against the // active canvas and fail closed on a workflow_path mismatch — panel #349/#186), so a // pin must target the active canvas at pin time (enforced in panel_set_workflow_target) // and the injected `workflow_path` acts as a GUARD: if the user later switches away, the // next graph command fails loudly instead of silently editing the wrong graph. It does // NOT enable background editing of a non-active tab. const DEFAULT_TARGET = { mode: "current" }; /** In-memory per-tab workflow pin. Process-scoped; cleared on orchestrator restart. */ export class WorkflowTargetStore { targets = new Map(); get(tabId) { return this.targets.get(tabId) ?? DEFAULT_TARGET; } set(tabId, target) { const normalized = normalizeTarget(target); if (normalized.mode === "current") { this.targets.delete(tabId); return DEFAULT_TARGET; } this.targets.set(tabId, normalized); return normalized; } clear(tabId) { this.targets.delete(tabId); } } function normalizeTarget(raw) { if (raw.mode !== "pinned") return { mode: "current" }; const path = (raw.path ?? "").trim(); if (!path) return { mode: "current" }; const filename = (raw.filename ?? "").trim() || undefined; return { mode: "pinned", path, filename }; } /** Bridge commands that target a specific workflow canvas (not tab navigation). */ export function shouldInjectWorkflowPath(cmd, args) { if (cmd === "workflow_list" || cmd === "workflow_new" || cmd === "workflow_open") return false; if (cmd.startsWith("graph_")) return true; // workflow_* that default to "active" when path omitted if ((cmd === "workflow_save" || cmd === "workflow_save_as" || cmd === "workflow_rename" || cmd === "workflow_close") && args.path === undefined) { return true; } return false; } /** Attach workflow_path when the tab has a pinned target. */ export function withWorkflowTarget(cmd, target) { if (target.mode !== "pinned" || !target.path) return cmd; const name = typeof cmd.cmd === "string" ? cmd.cmd : ""; if (!name || cmd.workflow_path !== undefined) return cmd; if (!shouldInjectWorkflowPath(name, cmd)) return cmd; return { ...cmd, workflow_path: target.path }; } //# sourceMappingURL=workflow-target-store.js.map