import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; const VAST_STATUS_ROUTE = "/remote/vast/status"; const VAST_VERIFY_ROUTE = "/remote/vast/verify"; const VAST_REAP_ROUTE = "/remote/vast/reap"; const VAST_DESTROY_ROUTE = "/remote/vast/destroy"; /** Return JSON or raise the server's credential-safe diagnostic. */ async function requestJson(route, options = {}) { const response = await api.fetchApi(route, options); const payload = await response.json().catch(() => ({})); if (!response.ok) { throw new Error(payload.error || `${response.status} ${response.statusText}`); } return payload; } /** Format one epoch deadline for local inspection. */ function deadlineLabel(epoch) { const value = Number(epoch); return Number.isFinite(value) ? new Date(value * 1000).toLocaleString() : "unknown"; } /** Build one managed lease row with an exact destructive action. */ function leaseRow(lease, refresh, setStatus) { const row = document.createElement("tr"); const values = [ lease.instance_id, lease.profile_name, `${lease.gpu_count}× ${lease.gpu_name}`, `$${Number(lease.hourly_cost_usd || 0).toFixed(4)}/h`, lease.actual_status, lease.active_invocations, deadlineLabel(lease.idle_deadline_epoch), ]; for (const value of values) { const cell = document.createElement("td"); cell.textContent = String(value); row.appendChild(cell); } const action = document.createElement("td"); const destroy = document.createElement("button"); destroy.type = "button"; destroy.textContent = "Destroy"; destroy.disabled = Number(lease.active_invocations || 0) > 0; destroy.addEventListener("click", async () => { if (typeof window.confirm === "function" && !window.confirm( `Permanently destroy managed Vast.ai instance ${lease.instance_id}?`, )) return; try { setStatus(`Destroying instance ${lease.instance_id}…`); await requestJson(VAST_DESTROY_ROUTE, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ instance_id: lease.instance_id }), }); await refresh(); } catch (error) { setStatus(`Destroy failed: ${String(error?.message ?? error)}`); } }); action.appendChild(destroy); row.appendChild(action); return row; } /** Open credential verification and owned-lease lifecycle controls. */ export async function openVastManager() { const overlay = document.createElement("div"); overlay.className = "comfy-remote-overlay"; const dialog = document.createElement("div"); dialog.className = "comfy-remote-dialog"; const title = document.createElement("h2"); title.textContent = "Vast.ai managed leases"; const intro = document.createElement("p"); intro.className = "intro"; intro.textContent = "Capacity, price ceilings, and idle retention live in disconnected Vast.ai Lease Configuration workflow nodes. Set VAST_API_KEY and COMFY_MODAL_VAST_IMAGE in the ComfyUI process environment; secrets are never saved in workflows."; const table = document.createElement("table"); table.style.width = "100%"; const header = document.createElement("thead"); header.innerHTML = "