Download ComfyUI-MiniMaxH3-Context-Loop/web/h3_prompt_assistant_client.mjs from dzmdd/Fast_deploy: direct link, hf CLI and curl.
- Browser
- Download file 7.08 kB
-
https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/web/h3_prompt_assistant_client.mjs
- Command line
-
hf download hf://dzmdd/Fast_deploy/ComfyUI-MiniMaxH3-Context-Loop/web/h3_prompt_assistant_client.mjs
-
curl -L -o h3_prompt_assistant_client.mjs https://huggingface.co/dzmdd/Fast_deploy/resolve/main/ComfyUI-MiniMaxH3-Context-Loop/web/h3_prompt_assistant_client.mjs
7.08 kB
| const DEFAULT_BRIDGE_URL = "ws://127.0.0.1:9180"; | |
| const PANEL_BRIDGE_STORAGE_KEY = "comfyui-mcp.panel.bridgeUrl"; | |
| function storedBridgeUrl(storage) { | |
| try { | |
| const value = storage?.getItem?.(PANEL_BRIDGE_STORAGE_KEY); | |
| return typeof value === "string" && /^wss?:\/\//.test(value.trim()) | |
| ? value.trim() : null; | |
| } catch (_error) { | |
| return null; | |
| } | |
| } | |
| async function jsonFrom(fetchImpl, path) { | |
| try { | |
| const response = await fetchImpl(path, {cache: "no-store"}); | |
| if (!response.ok) return null; | |
| return await response.json(); | |
| } catch (_error) { | |
| return null; | |
| } | |
| } | |
| export async function discoverPromptAssistBridge({ | |
| fetchImpl = globalThis.fetch?.bind(globalThis), | |
| locationLike = globalThis.location, | |
| storage = globalThis.localStorage, | |
| } = {}) { | |
| if (locationLike?.protocol === "https:" && fetchImpl) { | |
| const advertised = await jsonFrom(fetchImpl, "/comfyui_mcp_panel/bridge_url"); | |
| if (typeof advertised?.url === "string" && advertised.url.startsWith("wss://")) { | |
| return advertised.url; | |
| } | |
| } | |
| const stored = storedBridgeUrl(storage); | |
| if (stored) return stored; | |
| if (fetchImpl) { | |
| const status = await jsonFrom(fetchImpl, "/comfyui_mcp_panel/status"); | |
| if (typeof status?.bridge_url === "string" && /^wss?:\/\//.test(status.bridge_url)) { | |
| return status.bridge_url; | |
| } | |
| } | |
| return DEFAULT_BRIDGE_URL; | |
| } | |
| function randomId() { | |
| return globalThis.crypto?.randomUUID?.() | |
| ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; | |
| } | |
| function sessionIdentity(key) { | |
| const storageKey = `h3.prompt-assistant.client.${key}`; | |
| try { | |
| let value = globalThis.sessionStorage?.getItem(storageKey); | |
| if (!value) { | |
| value = randomId(); | |
| globalThis.sessionStorage?.setItem(storageKey, value); | |
| } | |
| return value; | |
| } catch (_error) { | |
| return randomId(); | |
| } | |
| } | |
| export class PromptAssistantClient { | |
| constructor({identityKey, onFrame, onStatus, discover = discoverPromptAssistBridge}) { | |
| this.identity = sessionIdentity(identityKey || randomId()); | |
| this.routeId = `prompt-assistant:${this.identity}`; | |
| this.conversationId = `h3-${this.identity}`; | |
| this.onFrame = onFrame; | |
| this.onStatus = onStatus; | |
| this.discover = discover; | |
| this.socket = null; | |
| this.connecting = null; | |
| this.closed = false; | |
| this.needsReset = false; | |
| } | |
| status(value, detail) { | |
| this.onStatus?.(value, detail); | |
| } | |
| async connect() { | |
| if (this.closed) throw new Error("Prompt assistant client is closed."); | |
| if (this.socket?.readyState === WebSocket.OPEN) return; | |
| if (this.connecting) return this.connecting; | |
| this.status("connecting"); | |
| this.connecting = (async () => { | |
| const url = await this.discover(); | |
| await new Promise((resolve, reject) => { | |
| let ready = false; | |
| const socket = new WebSocket(url); | |
| this.socket = socket; | |
| const timer = globalThis.setTimeout(() => { | |
| if (ready) return; | |
| try { socket.close(); } catch (_error) { /* already closed */ } | |
| reject(new Error("The comfyui-mcp prompt-assist handshake timed out.")); | |
| }, 15_000); | |
| socket.addEventListener("open", () => { | |
| socket.send(JSON.stringify({ | |
| type: "hello", | |
| tab_id: this.routeId, | |
| tab_session_id: this.identity, | |
| title: "H3 Prompt Assistant", | |
| headless: true, | |
| client_kind: "prompt_assistant", | |
| })); | |
| }); | |
| socket.addEventListener("message", (event) => { | |
| let frame; | |
| try { frame = JSON.parse(String(event.data ?? "")); } catch (_error) { return; } | |
| if (frame?.type === "prompt_assist_ready") { | |
| ready = true; | |
| globalThis.clearTimeout(timer); | |
| if (this.needsReset) { | |
| socket.send(JSON.stringify({ | |
| tab_id: this.routeId, | |
| type: "prompt_assist_reset", | |
| conversation_id: this.conversationId, | |
| })); | |
| this.needsReset = false; | |
| } | |
| this.status("connected", frame); | |
| resolve(); | |
| } | |
| this.onFrame?.(frame); | |
| }); | |
| socket.addEventListener("error", () => { | |
| if (!ready) { | |
| globalThis.clearTimeout(timer); | |
| try { socket.close(); } catch (_error) { /* already closed */ } | |
| reject(new Error(`Could not connect to the comfyui-mcp bridge at ${url.split("?")[0]}.`)); | |
| } | |
| }); | |
| socket.addEventListener("close", () => { | |
| globalThis.clearTimeout(timer); | |
| if (this.socket === socket) this.socket = null; | |
| this.status("disconnected"); | |
| if (!ready) reject(new Error("The comfyui-mcp bridge closed before prompt-assist was ready.")); | |
| }); | |
| }); | |
| })().finally(() => { | |
| this.connecting = null; | |
| }); | |
| return this.connecting; | |
| } | |
| async send(frame) { | |
| await this.connect(); | |
| if (!this.socket || this.socket.readyState !== WebSocket.OPEN) { | |
| throw new Error("Prompt assistant is not connected."); | |
| } | |
| this.socket.send(JSON.stringify({tab_id: this.routeId, ...frame})); | |
| } | |
| cancel(requestId) { | |
| if (this.socket?.readyState !== WebSocket.OPEN) return false; | |
| this.socket.send(JSON.stringify({ | |
| tab_id: this.routeId, | |
| type: "prompt_assist_cancel", | |
| request_id: requestId, | |
| })); | |
| return true; | |
| } | |
| reset() { | |
| this.needsReset = true; | |
| if (this.socket?.readyState !== WebSocket.OPEN) return false; | |
| this.socket.send(JSON.stringify({ | |
| tab_id: this.routeId, | |
| type: "prompt_assist_reset", | |
| conversation_id: this.conversationId, | |
| })); | |
| this.needsReset = false; | |
| return true; | |
| } | |
| close() { | |
| this.closed = true; | |
| if (this.socket?.readyState === WebSocket.OPEN) { | |
| try { | |
| this.socket.send(JSON.stringify({tab_id: this.routeId, type: "prompt_assist_close"})); | |
| } catch (_error) { | |
| // Socket is already going away. | |
| } | |
| } | |
| try { this.socket?.close(); } catch (_error) { /* already closed */ } | |
| this.socket = null; | |
| } | |
| } | |