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),
65 lines • 2.75 kB
JavaScript
// Live queue-status fan-out for panel/mobile tabs.
//
// The QueueMonitor watchdog (queue-monitor.ts) already tracks ComfyUI's live
// execution state — for EVERY job, including browser-queued ones — but until
// now that state never left the orchestrator process. This module turns its
// snapshot into a `queue_status` bridge frame so a connected tab (the mobile
// app's live queue monitor) can show queue depth + render progress in real
// time.
//
// Throttle contract: the orchestrator ticks the broadcaster on a 1-second
// interval, and `tick()` pushes ONLY when the frame differs from the last one
// sent. An idle rig therefore broadcasts nothing at all, and a running render
// costs at most one frame per second.
/** Build the broadcast frame for one monitor snapshot. `completed` overrides
* the snapshot's own lastCompleted — used to replay a burst of completions
* drained in one tick as distinct frames. */
export function buildQueueStatusFrame(s, completed = s.lastCompleted) {
return {
type: "queue_status",
connected: s.connected,
running: s.running,
queue_depth: s.queueDepth,
prompt_id: s.runningPromptId,
node: s.currentNode,
progress_value: s.progressValue,
progress_max: s.progressMax,
last_completed_prompt_id: completed?.promptId ?? null,
last_completed_status: completed?.status ?? null,
last_completed_at: completed?.at ?? null,
};
}
/**
* Wire a snapshot source to a push sink with change-only semantics. The sink
* is the bridge's broadcast push; it must never throw into the timer (the
* bridge's own push already guarantees that, see ui-bridge.ts).
*
* `drainCompletions` (optional) yields runs that finished since the last tick
* — each one is replayed as its own frame BEFORE the change-only state frame,
* so even a run that started and completed entirely between ticks reaches
* every subscriber exactly once (issue #259). The final state frame carries
* the newest completion too, so it usually dedupes away.
*/
export function createQueueStatusBroadcaster(snapshot, push, drainCompletions) {
let last = "";
const send = (frame) => {
const key = JSON.stringify(frame);
if (key === last)
return;
last = key;
push(frame);
};
return {
tick() {
const snap = snapshot();
for (const ev of drainCompletions ? drainCompletions() : []) {
send(buildQueueStatusFrame(snap, ev));
}
send(buildQueueStatusFrame(snap));
},
current() {
return buildQueueStatusFrame(snapshot());
},
};
}
//# sourceMappingURL=queue-status-broadcast.js.map