import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; // import { LGraphNode } from "../../scripts/widgets.js"; LGraphNode = LiteGraph.LGraphNode; import { ComfyDialog, $el } from "../../scripts/ui.js"; import { generateDependencyGraph } from "https://esm.sh/comfyui-json@0.1.25"; import { ComfyDeploy } from "https://esm.sh/comfydeploy@2.0.0-beta.69"; const styles = ` .comfydeploy-menu-item { background: linear-gradient(to right, rgba(74, 144, 226, 0.9), rgba(103, 178, 111, 0.9)) !important; color: white !important; position: relative !important; padding-left: 20px !important; } .comfydeploy-menu-item:hover { filter: brightness(1.1) !important; cursor: pointer !important; } .comfydeploy-menu-item::before { content: ''; position: absolute; left: 4px; top: 50%; transform: translateY(-50%); width: 12px; height: 12px; background-image: url('https://www.comfydeploy.com/icon.svg'); background-size: contain; background-repeat: no-repeat; background-position: center; } `; // Add stylesheet to document const styleSheet = document.createElement("style"); styleSheet.textContent = styles; document.head.appendChild(styleSheet); const loadingIcon = ``; function sendEventToCD(event, data) { const message = { type: event, data: data, }; window.parent.postMessage(JSON.stringify(message), "*"); } function sendDirectEventToCD(event, data) { const message = { type: event, data: data, }; window.parent.postMessage(message, "*"); } function dispatchAPIEventData(data) { const msg = JSON.parse(data); // Custom parse error if (msg.error) { let message = msg.error.message; if (msg.error.details) message += ": " + msg.error.details; for (const [nodeID, nodeError] of Object.entries(msg.node_errors)) { message += "\n" + nodeError.class_type + ":"; for (const errorReason of nodeError.errors) { message += "\n - " + errorReason.message + ": " + errorReason.details; } } app.ui.dialog.show(message); if (msg.node_errors) { app.lastNodeErrors = msg.node_errors; app.canvas.draw(true, true); } } switch (msg.event) { case "error": break; case "status": if (msg.data.sid) { // this.clientId = msg.data.sid; // window.name = this.clientId; // use window name so it isnt reused when duplicating tabs // sessionStorage.setItem("clientId", this.clientId); // store in session storage so duplicate tab can load correct workflow } api.dispatchEvent(new CustomEvent("status", { detail: msg.data.status })); break; case "progress": api.dispatchEvent(new CustomEvent("progress", { detail: msg.data })); break; case "executing": api.dispatchEvent( new CustomEvent("executing", { detail: msg.data.node }), ); break; case "executed": api.dispatchEvent(new CustomEvent("executed", { detail: msg.data })); break; case "execution_start": api.dispatchEvent( new CustomEvent("execution_start", { detail: msg.data }), ); break; case "execution_error": api.dispatchEvent( new CustomEvent("execution_error", { detail: msg.data }), ); break; case "execution_cached": api.dispatchEvent( new CustomEvent("execution_cached", { detail: msg.data }), ); break; default: api.dispatchEvent(new CustomEvent(msg.type, { detail: msg.data })); // default: // if (this.#registered.has(msg.type)) { // } else { // throw new Error(`Unknown message type ${msg.type}`); // } } } const context = { selectedWorkflowInfo: null, }; // let selectedWorkflowInfo = { // workflow_id: "05da8f2b-63af-4c0c-86dd-08d01ec512b7", // machine_id: "45ac5f85-b7b6-436f-8d97-2383b25485f3", // native_run_api_endpoint: "http://localhost:3011/api/run", // }; async function getSelectedWorkflowInfo() { const workflow_info_promise = new Promise((resolve) => { try { const handleMessage = (event) => { try { const message = JSON.parse(event.data); if (message.type === "workflow_info") { resolve(message.data); window.removeEventListener("message", handleMessage); } } catch (error) { console.error(error); resolve(undefined); } }; window.addEventListener("message", handleMessage); sendEventToCD("workflow_info"); } catch (error) { console.error(error); resolve(undefined); } }); return workflow_info_promise; } function setSelectedWorkflowInfo(info) { context.selectedWorkflowInfo = info; } const VALID_TYPES = [ "STRING", "combo", "number", "toggle", "BOOLEAN", "text", "string", ]; function hideWidget(node, widget, suffix = "") { if (widget.type?.startsWith(CONVERTED_TYPE)) return; widget.origType = widget.type; widget.origComputeSize = widget.computeSize; widget.origSerializeValue = widget.serializeValue; widget.computeSize = () => [0, -4]; widget.type = CONVERTED_TYPE + suffix; widget.serializeValue = () => { if (!node.inputs) { return void 0; } let node_input = node.inputs.find((i) => i.widget?.name === widget.name); if (!node_input || !node_input.link) { return void 0; } return widget.origSerializeValue ? widget.origSerializeValue() : widget.value; }; if (widget.linkedWidgets) { for (const w of widget.linkedWidgets) { hideWidget(node, w, ":" + widget.name); } } } function getWidgetType(config) { let type = config[0]; if (type instanceof Array) { type = "COMBO"; } return { type }; } const GET_CONFIG = Symbol(); function convertToInput(node, widget, config) { console.log(node); if (node.type == "LoadImage") { var inputNode = LiteGraph.createNode("ComfyUIDeployExternalImage"); console.log(widget); const currentOutputsLinks = node.outputs[0].links; // const index = node.inputs.findIndex((x) => x.name == widget.name); // console.log(node.widgets_values, index); // inputNode.configure({ // widgets_values: ["input_text", widget.value], // }); inputNode.pos = node.pos; inputNode.id = ++app.graph.last_node_id; // inputNode.pos[0] += node.size[0] + 40; node.pos[0] -= inputNode.size[0] + 20; console.log(inputNode); console.log(app.graph); app.graph.add(inputNode); const links = app.graph.links; console.log(currentOutputsLinks); for (let i = 0; i < currentOutputsLinks.length; i++) { const link = currentOutputsLinks[i]; const llink = links[link]; console.log(links[link]); setTimeout( () => inputNode.connect(0, llink.target_id, llink.target_slot), 100, ); } node.connect(0, inputNode, 0); return null; } hideWidget(node, widget); const { type } = getWidgetType(config); const sz = node.size; const inputIsOptional = !!widget.options?.inputIsOptional; const input = node.addInput(widget.name, type, { widget: { name: widget.name, [GET_CONFIG]: () => config }, ...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {}), }); for (const widget2 of node.widgets) { widget2.last_y += LiteGraph.NODE_SLOT_HEIGHT; } node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]); if (type == "STRING") { var inputNode = LiteGraph.createNode("ComfyUIDeployExternalText"); console.log(widget); const index = node.inputs.findIndex((x) => x.name == widget.name); console.log(node.widgets_values, index); inputNode.configure({ widgets_values: ["input_text", widget.value], }); inputNode.id = ++app.graph.last_node_id; inputNode.pos = node.pos; inputNode.pos[0] -= node.size[0] + 40; console.log(inputNode); console.log(app.graph); app.graph.add(inputNode); inputNode.connect(0, node, index); } return input; } const CONVERTED_TYPE = "converted-widget"; function getConfig(widgetName) { const { nodeData } = this.constructor; return ( nodeData?.input?.required?.[widgetName] ?? nodeData?.input?.optional?.[widgetName] ); } function isConvertibleWidget(widget, config) { return ( (VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) && !widget.options?.forceInput ); } var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); /** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/ /** @type {ComfyExtension} */ const ext = { name: "BennyKok.ComfyUIDeploy", native_mode: false, init(app) { addButton(); const queryParams = new URLSearchParams(window.location.search); const workflow_version_id = queryParams.get("workflow_version_id"); const auth_token = queryParams.get("auth_token"); const org_display = queryParams.get("org_display"); const origin = queryParams.get("origin"); const workspace_mode = queryParams.get("workspace_mode"); this.native_mode = queryParams.get("native_mode") === "true"; if (workspace_mode) { document.querySelector(".comfy-menu").style.display = "none"; sendEventToCD("cd_plugin_onInit"); // app.queuePrompt = ((originalFunction) => async () => { // // const prompt = await app.graphToPrompt(); // sendEventToCD("cd_plugin_onQueuePromptTrigger"); // })(app.queuePrompt); // // Intercept the onkeydown event // window.addEventListener( // "keydown", // (event) => { // // Check for specific keys if necessary // console.log("hi"); // if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { // event.preventDefault(); // event.stopImmediatePropagation(); // event.stopPropagation(); // sendEventToCD("cd_plugin_onQueuePrompt", prompt); // } // }, // true, // ); } const data = getData(); let endpoint = data.endpoint; let apiKey = data.apiKey; // If there is auth token override it if (auth_token) { apiKey = auth_token; endpoint = origin; saveData({ displayName: org_display, endpoint: origin, apiKey: auth_token, displayName: org_display, environment: "cloud", }); localStorage.setItem("comfy_deploy_env", "cloud"); } if (!workflow_version_id) { console.error("No workflow_version_id provided in query parameters."); } else { loadingDialog.showLoading( "Loading workflow from " + org_display, "Please wait...", ); fetch(endpoint + "/api/workflow-version/" + workflow_version_id, { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + apiKey, }, }) .then(async (res) => { const data = await res.json(); const { workflow, workflow_id, error } = data; if (error) { infoDialog.showMessage("Unable to load this workflow", error); return; } // // Adding a delay to wait for the intial graph to load // await new Promise((resolve) => setTimeout(resolve, 2000)); workflow?.nodes.forEach((x) => { if (x?.type === "ComfyDeploy") { x.widgets_values[1] = workflow_id; // x.widgets_values[2] = workflow_version.version; } }); /** @type {LGraph} */ app.loadGraphData(workflow); }) .catch((e) => infoDialog.showMessage("Error", e.message)) .finally(() => { loadingDialog.close(); window.history.replaceState( {}, document.title, window.location.pathname, ); }); } }, async beforeRegisterNodeDef(nodeType, nodeData, app2) { const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions; nodeType.prototype.getExtraMenuOptions = function (_, options) { const r = origGetExtraMenuOptions ? origGetExtraMenuOptions.apply(this, arguments) : void 0; if (this.widgets) { let toInput = []; let toWidget = []; for (const w of this.widgets) { if (w.options?.forceInput) { continue; } if (w.type === CONVERTED_TYPE) { toWidget.push({ content: `Convert ${w.name} to widget`, callback: /* @__PURE__ */ __name( () => convertToWidget(this, w), "callback", ), }); } else { const config = getConfig.call(this, w.name) ?? [ w.type, w.options || {}, ]; if (isConvertibleWidget(w, config)) { toInput.push({ content: `Convert ${w.name} to external input`, callback: /* @__PURE__ */ __name( () => convertToInput(this, w, config), "callback", ), className: "comfydeploy-menu-item", }); } } } if (toInput.length) { if (true) { options.push(); let optionIndex = options.findIndex((o) => o.content === "Outputs"); if (optionIndex === -1) optionIndex = options.length; else optionIndex++; options.splice( 0, 0, // { // content: "[ComfyDeploy] Convert to External Input", // submenu: { // options: toInput, // }, // className: "comfydeploy-menu-item" // }, ...toInput, null, ); } else { options.push(...toInput, null); } } // if (toWidget.length) { // if (useConversionSubmenusSetting.value) { // options.push({ // content: "Convert Input to Widget", // submenu: { // options: toWidget, // }, // }); // } else { // options.push(...toWidget, null); // } // } } return r; }; if ( nodeData?.input?.optional?.default_value_url?.[1]?.image_preview === true ) { nodeData.input.optional.default_value_url = ["IMAGEPREVIEW"]; console.log(nodeData.input.optional.default_value_url); } // const origonNodeCreated = nodeType.prototype.onNodeCreated; // nodeType.prototype.onNodeCreated = function () { // const r = origonNodeCreated // ? origonNodeCreated.apply(this, arguments) // : void 0; // if (!this.widgets) { // return; // } // console.log(this.widgets); // this.widgets.forEach(element => { // if (element.type != "customtext") return // console.log(element.element); // const parent = element.element.parentElement // console.log(element.element.parentElement) // const btn = document.createElement("button"); // // const div = document.createElement("div"); // // parent.removeChild(element.element) // // div.appendChild(element.element) // // parent.appendChild(div) // // element.element = div // // console.log(element.element); // // btn.style = element.element.style // }); // return r // }; }, registerCustomNodes() { /** @type {LGraphNode}*/ class ComfyDeploy extends LGraphNode { constructor() { super(); this.color = LGraphCanvas.node_colors.yellow.color; this.bgcolor = LGraphCanvas.node_colors.yellow.bgcolor; this.groupcolor = LGraphCanvas.node_colors.yellow.groupcolor; if (!this.properties) { this.properties = {}; this.properties.workflow_name = ""; this.properties.workflow_id = ""; this.properties.version = ""; } this.addWidget( "text", "workflow_name", this.properties.workflow_name, (v) => { this.properties.workflow_name = v; }, { multiline: false }, ); this.addWidget( "text", "workflow_id", this.properties.workflow_id, (v) => { this.properties.workflow_id = v; }, { multiline: false }, ); this.addWidget( "text", "version", this.properties.version, (v) => { this.properties.version = v; }, { multiline: false }, ); this.widgets_start_y = 10; this.serialize_widgets = true; this.isVirtualNode = true; } onExecute() { // This method is called when the node is executed // You can add any necessary logic here } onSerialize(o) { // This method is called when the node is being serialized // Ensure all necessary data is saved if (!o.properties) { o.properties = {}; } o.properties.workflow_name = this.properties.workflow_name; o.properties.workflow_id = this.properties.workflow_id; o.properties.version = this.properties.version; } onConfigure(o) { // This method is called when the node is being configured (e.g., when loading a saved graph) // Ensure all necessary data is restored if (o.properties) { this.properties = { ...this.properties, ...o.properties }; this.widgets[0].value = this.properties.workflow_name || ""; this.widgets[1].value = this.properties.workflow_id || ""; this.widgets[2].value = this.properties.version || "1"; } } } // Register the node type LiteGraph.registerNodeType( "ComfyDeploy", Object.assign(ComfyDeploy, { title: "Comfy Deploy", title_mode: LiteGraph.NORMAL_TITLE, collapsable: true, }), ); ComfyDeploy.category = "deploy"; }, getCustomWidgets() { return { IMAGEPREVIEW(node, inputName, inputData) { // Find or create the URL input widget const urlWidget = node.addWidget( "string", inputName, /* value=*/ "", () => {}, { serialize: true }, ); const buttonWidget = node.addWidget( "button", "Open Assets Browser", /* value=*/ "", () => { sendEventToCD("assets", { node: node.id, inputName: inputName, }); // console.log("load image"); }, { serialize: false }, ); console.log(node.widgets); console.log("urlWidget", urlWidget); // Add image preview functionality function showImage(url) { const img = new Image(); img.onload = () => { node.imgs = [img]; app.graph.setDirtyCanvas(true); node.setSizeForImage?.(); }; img.onerror = () => { node.imgs = []; app.graph.setDirtyCanvas(true); }; img.src = url; } // Set up URL widget value handling let default_value = urlWidget.value; Object.defineProperty(urlWidget, "value", { set: function (value) { this._real_value = value; // Preview image when URL changes if (value) { showImage(value); } }, get: function () { return this._real_value || default_value; }, }); // Show initial image if URL exists requestAnimationFrame(() => { if (urlWidget.value) { showImage(urlWidget.value); } }); return { widget: urlWidget }; }, }; }, async setup() { // const graphCanvas = document.getElementById("graph-canvas"); window.addEventListener("message", async (event) => { // console.log("message", event); try { const message = JSON.parse(event.data); if (message.type === "graph_load") { const comfyUIWorkflow = message.data; // console.log("recieved: ", comfyUIWorkflow); // Assuming there's a method to load the workflow data into the ComfyUI // This part of the code would depend on how the ComfyUI expects to receive and process the workflow data // For demonstration, let's assume there's a loadWorkflow method in the ComfyUI API if (comfyUIWorkflow && app && app.loadGraphData) { try { await window["app"].ui.settings.setSettingValueAsync( "Comfy.Validation.Workflows", false, ); } catch (error) { console.warning( "Error setting validation to false, is fine to ignore this", error, ); } console.log("loadGraphData"); app.loadGraphData(comfyUIWorkflow); } } else if (message.type === "deploy") { // deployWorkflow(); const prompt = await app.graphToPrompt(); // api.handlePromptGenerated(prompt); sendEventToCD("cd_plugin_onDeployChanges", prompt); } else if (message.type === "queue_prompt") { const prompt = await app.graphToPrompt(); if (typeof api.handlePromptGenerated === "function") { api.handlePromptGenerated(prompt); } else { console.warn("api.handlePromptGenerated is not a function"); } sendEventToCD("cd_plugin_onQueuePrompt", prompt); } else if (message.type === "configure_queue_buttons") { addQueueButtons(message.data); } else if (message.type === "configure_menu_right_buttons") { addMenuRightButtons(message.data); } else if (message.type === "configure_menu_buttons") { addMenuButtons(message.data); } else if (message.type === "get_prompt") { const prompt = await app.graphToPrompt(); sendEventToCD("cd_plugin_onGetPrompt", prompt); } else if (message.type === "event") { dispatchAPIEventData(message.data); } else if (message.type === "update_widget") { // New handler for updating widget values const { nodeId, widgetName, value } = message.data; const node = app.graph.getNodeById(nodeId); if (!node) { console.warn(`Node with ID ${nodeId} not found`); return; } const widget = node.widgets?.find((w) => w.name === widgetName); if (!widget) { console.warn(`Widget ${widgetName} not found in node ${nodeId}`); return; } widget.value = value; app.graph.setDirtyCanvas(true); } else if (message.type === "add_node") { console.log("add node", message.data); app.graph.beforeChange(); var node = LiteGraph.createNode(message.data.type); node.configure({ widgets_values: message.data.widgets_values, }); console.log("node", node); const graphMouse = app.canvas.graph_mouse; node.pos = [graphMouse[0], graphMouse[1]]; app.graph.add(node); app.graph.afterChange(); } else if (message.type === "zoom_to_node") { const nodeId = message.data.nodeId; const position = message.data.position; const node = app.graph.getNodeById(nodeId); if (!node) return; const canvas = app.canvas; const targetScale = 1; const targetOffsetX = canvas.canvas.width / 4 - position[0] - node.size[0] / 2; const targetOffsetY = canvas.canvas.height / 4 - position[1] - node.size[1] / 2; const startScale = canvas.ds.scale; const startOffsetX = canvas.ds.offset[0]; const startOffsetY = canvas.ds.offset[1]; const duration = 400; // Animation duration in milliseconds const startTime = Date.now(); function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); } function lerp(start, end, t) { return start * (1 - t) + end * t; } function animate() { const currentTime = Date.now(); const elapsedTime = currentTime - startTime; const t = Math.min(elapsedTime / duration, 1); const easedT = easeOutCubic(t); const currentScale = lerp(startScale, targetScale, easedT); const currentOffsetX = lerp(startOffsetX, targetOffsetX, easedT); const currentOffsetY = lerp(startOffsetY, targetOffsetY, easedT); canvas.setZoom(currentScale); canvas.ds.offset = [currentOffsetX, currentOffsetY]; canvas.draw(true, true); if (t < 1) { requestAnimationFrame(animate); } } animate(); } else if (message.type === "workflow_info") { setSelectedWorkflowInfo(message.data); } // else if (message.type === "refresh") { // sendEventToCD("cd_plugin_onRefresh"); // } } catch (error) { // console.error("Error processing message:", error); } }); api.addEventListener("executed", (evt) => { const images = evt.detail?.output.images; // if (images?.length > 0 && images[0].type === "output") { // generatedImages[evt.detail.node] = images[0].filename; // } // if (evt.detail?.output.gltfFilename) { // } }); if (this.native_mode) { // console.log("native mode", window, window.app); try { await app.ui.settings.setSettingValueAsync("Comfy.UseNewMenu", "Top"); await app.ui.settings.setSettingValueAsync( "Comfy.Sidebar.Size", "small", ); await app.ui.settings.setSettingValueAsync( "Comfy.Sidebar.Location", "left", ); // localStorage.setItem("Comfy.MenuPosition.Docked", "true"); console.log("native mode manmanman"); } catch (error) { console.error("Error setting validation to false", error); } } app.graph.onAfterChange = ((originalFunction) => async function () { const prompt = await app.graphToPrompt(); sendEventToCD("cd_plugin_onAfterChange", prompt); if (typeof originalFunction === "function") { originalFunction.apply(this, arguments); } })(app.graph.onAfterChange); sendEventToCD("cd_plugin_setup"); }, }; /** * @typedef {import('../../../web/types/litegraph.js').LGraph} LGraph * @typedef {import('../../../web/types/litegraph.js').LGraphNode} LGraphNode */ function showError(title, message) { infoDialog.show( `

${title}


${message} `, ); } function createDynamicUIHtml(data) { console.log(data); let html = '
'; const bgcolor = "var(--comfy-input-bg)"; const evenBg = "var(--border-color)"; const textColor = "var(--input-text)"; // Custom Nodes html += `
`; html += '

Custom Nodes

'; if (data.missing_nodes?.length > 0) { html += `

Missing Nodes

These nodes are not found with any matching custom_nodes in the ComfyUI Manager Database

${data.missing_nodes .map((node) => { return `

${node}

`; }) .join("")}
`; } Object.values(data.custom_nodes).forEach((node) => { html += `
${ node.name }

${node.hash}

${ node.warning ? `

${node.warning}

` : "" }
`; }); html += "
"; // Models html += `
`; html += '

Models

'; Object.entries(data.models).forEach(([section, items]) => { html += `

${ section.charAt(0).toUpperCase() + section.slice(1) }

`; items.forEach((item) => { html += `

${item.name}

`; }); html += "
"; }); html += "
"; // Models html += `
`; html += '

Files

'; Object.entries(data.files).forEach(([section, items]) => { html += `

${ section.charAt(0).toUpperCase() + section.slice(1) }

`; items.forEach((item) => { html += `

${item.name}

`; }); html += "
"; }); html += "
"; html += "
"; return html; } // Modify the existing deployWorkflow function async function deployWorkflow() { const deploy = document.getElementById("deploy-button"); /** @type {LGraph} */ const graph = app.graph; let { endpoint, apiKey, displayName } = getData(); if (!endpoint || !apiKey || apiKey === "" || endpoint === "") { configDialog.show(); return; } let deployMeta = graph.findNodesByType("ComfyDeploy"); if (deployMeta.length == 0) { const text = await inputDialog.input( "Create your deployment", "Workflow name", ); if (!text) return; console.log(text); app.graph.beforeChange(); var node = LiteGraph.createNode("ComfyDeploy"); node.configure({ widgets_values: [text], }); node.pos = [0, 0]; app.graph.add(node); app.graph.afterChange(); deployMeta = [node]; } const deployMetaNode = deployMeta[0]; const workflow_name = deployMetaNode.widgets[0].value; const workflow_id = deployMetaNode.widgets[1].value; const ok = await confirmDialog.confirm( `Confirm deployment`, `
A new version of will be deployed, do you confirm?





`, ); if (!ok) return; const includeDeps = document.getElementById("include-deps").checked; const reuseHash = document.getElementById("reuse-hash").checked; if (endpoint.endsWith("/")) { endpoint = endpoint.slice(0, -1); } loadingDialog.showLoading("Generating snapshot"); const snapshot = await fetch("/snapshot/get_current").then((x) => x.json()); // console.log(snapshot); loadingDialog.close(); if (!snapshot) { showError( "Error when deploying", "Unable to generate snapshot, please install ComfyUI Manager", ); return; } const title = deploy.querySelector("#button-title"); const prompt = await app.graphToPrompt(); let deps = undefined; if (includeDeps) { loadingDialog.showLoading("Fetching existing version"); const existing_workflow = await fetch( endpoint + "/api/workflow/" + workflow_id, { method: "GET", headers: { "Content-Type": "application/json", Authorization: "Bearer " + apiKey, }, }, ) .then((x) => x.json()) .catch(() => { return {}; }); loadingDialog.close(); loadingDialog.showLoading("Generating dependency graph"); deps = await generateDependencyGraph({ workflow_api: prompt.output, snapshot: snapshot, computeFileHash: async (file) => { console.log(existing_workflow?.dependencies?.models); // Match previous hash for models if (reuseHash && existing_workflow?.dependencies?.models) { const previousModelHash = Object.entries( existing_workflow?.dependencies?.models, ).flatMap(([key, value]) => { return Object.values(value).map((x) => ({ ...x, name: "models/" + key + "/" + x.name, })); }); console.log(previousModelHash); const match = previousModelHash.find((x) => { console.log(file, x.name); return file == x.name; }); console.log(match); if (match && match.hash) { console.log("cached hash used"); return match.hash; } } console.log(file); loadingDialog.showLoading("Generating hash", file); const hash = await fetch( `/comfyui-deploy/get-file-hash?file_path=${encodeURIComponent(file)}`, ).then((x) => x.json()); loadingDialog.showLoading("Generating hash", file); console.log(hash); return hash.file_hash; }, // handleFileUpload: async (file, hash, prevhash) => { // console.log("Uploading ", file); // loadingDialog.showLoading("Uploading file", file); // try { // const { download_url } = await fetch(`/comfyui-deploy/upload-file`, { // method: "POST", // body: JSON.stringify({ // file_path: file, // token: apiKey, // url: endpoint + "/api/upload-url", // }), // }) // .then((x) => x.json()) // .catch(() => { // loadingDialog.close(); // confirmDialog.confirm("Error", "Unable to upload file " + file); // }); // loadingDialog.showLoading("Uploaded file", file); // console.log(download_url); // return download_url; // } catch (error) { // return undefined; // } // }, existingDependencies: existing_workflow.dependencies, }); // Need to find a way to include this if this is not included in comfyui-json level if ( !deps.custom_nodes["https://github.com/BennyKok/comfyui-deploy"] && !deps.custom_nodes["https://github.com/BennyKok/comfyui-deploy.git"] ) deps.custom_nodes["https://github.com/BennyKok/comfyui-deploy"] = { url: "https://github.com/BennyKok/comfyui-deploy", install_type: "git-clone", hash: snapshot?.git_custom_nodes?.[ "https://github.com/BennyKok/comfyui-deploy" ]?.hash ?? "HEAD", name: "ComfyUI Deploy", }; loadingDialog.close(); const depsOk = await confirmDialog.confirm( "Check dependencies", // JSON.stringify(deps, null, 2), `
You will need to create a cloud machine with the following configuration on ComfyDeploy
  1. Review the dependencies listed in the graph below
  2. Create a new cloud machine with the required configuration
  3. Install missing models and check missing files
  4. Deploy your workflow to the newly created machine
${loadingIcon}