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

80 lines • 3.08 kB
import { execFile } from "node:child_process"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { basename, join } from "node:path"; import { promisify } from "node:util"; import { ModelError, ValidationError } from "../../utils/errors.js"; import { withPrefix } from "./utils.js"; const execFileAsync = promisify(execFile); const ASCII_CONTROL_RE = /[\x00-\x1F\x7F]/; async function findHfCli() { for (const command of ["hf", "huggingface-cli"]) { try { await execFileAsync(command, ["--version"]); return command; } catch { // Try the next known CLI name. } } throw new ValidationError("hf upload requires the hf CLI. Install and authenticate `hf` or `huggingface-cli` first."); } function validateRepo(repo) { if (!/^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(repo)) { throw new ValidationError("hf.repo must be in owner/name format."); } } function validateRemotePath(path) { if (ASCII_CONTROL_RE.test(path)) { throw new ValidationError("hf remote path cannot contain ASCII control characters."); } for (const segment of path.split("/")) { if (segment === "..") { throw new ValidationError("hf remote path cannot contain '..' segments."); } if (segment.startsWith("-")) { throw new ValidationError("hf remote path segments cannot start with '-'."); } } } export async function uploadHfFile(source, destination) { validateRepo(destination.repo); const repoType = destination.repo_type ?? "model"; const remotePath = withPrefix(destination.path, source.filename); validateRemotePath(remotePath); const command = await findHfCli(); let tempDir; let localPath = source.path; try { if (!localPath) { tempDir = await mkdtemp(join(tmpdir(), "comfyui-mcp-hf-upload-")); localPath = join(tempDir, basename(source.filename)); await writeFile(localPath, source.data ?? Buffer.alloc(0)); } const args = ["upload"]; if (repoType !== "model") args.push("--repo-type", repoType); args.push("--", destination.repo, localPath, remotePath); await execFileAsync(command, args); const prefix = repoType === "dataset" ? "datasets/" : repoType === "space" ? "spaces/" : ""; return { provider: "hf", url: `https://huggingface.co/${prefix}${destination.repo}/blob/main/${remotePath}`, }; } catch (err) { if (err instanceof ValidationError) throw err; throw new ModelError("hf upload failed", { name: err instanceof Error ? err.name : undefined, code: typeof err?.code === "string" ? err.code : undefined, }); } finally { if (tempDir) await rm(tempDir, { recursive: true, force: true }).catch(() => undefined); } } //# sourceMappingURL=hf.js.map