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),
63 lines • 3.63 kB
JavaScript
// Helper for lazy-loading optional dependencies.
//
// Heavy or feature-gated packages (cloud SDKs, AI SDKs, cloudflared) live in
// `optionalDependencies` so a `npm install --no-optional comfyui-mcp` install
// still yields a working server. The features that need those deps surface a
// clear "install <pkg>" error instead of crashing on import.
import { ModelError } from "./errors.js";
const cache = new Map();
export function classifyImportFailure(spec, err) {
const e = err;
const message = typeof e?.message === "string" ? e.message : String(err);
const notFound = e?.code === "ERR_MODULE_NOT_FOUND" || e?.code === "MODULE_NOT_FOUND";
if (!notFound)
return { kind: "unloadable", detail: message };
// "Cannot find package 'x' imported from …" / "Cannot find module 'x'".
const named = /Cannot find (?:package|module) ['"]([^'"]+)['"]/.exec(message)?.[1];
if (named === undefined) {
// A not-found we cannot attribute. Treat it as the requested package rather
// than inventing a transitive one — this is the pre-existing behaviour and
// the common case.
return { kind: "missing" };
}
// SUBPATHS RUN BOTH WAYS, and only one direction is obvious. Requesting
// "pkg/sub" makes Node report `Cannot find package 'pkg'` — it names the bare
// PACKAGE, not what was asked for — so a `named === spec` test fails and the
// package gets blamed as its own dependency. Found by a surviving mutant and
// confirmed against real Node before fixing.
const isSelf = named === spec || named.startsWith(`${spec}/`) || spec.startsWith(`${named}/`);
return isSelf ? { kind: "missing" } : { kind: "missing-transitive", detail: named };
}
export async function requireOptionalDep(spec, options) {
const cached = cache.get(spec);
if (cached !== undefined)
return cached;
try {
const mod = (await import(spec));
cache.set(spec, mod);
return mod;
}
catch (err) {
const feature = options?.feature ?? spec;
const installHint = options?.installHint ?? `npm install ${spec}`;
const why = classifyImportFailure(spec, err);
throw new ModelError(why.kind === "missing"
? `Optional dependency for ${feature} is not installed. To enable it, run: ${installHint}`
: why.kind === "missing-transitive"
? `Optional dependency for ${feature} IS installed, but it could not load because ` +
`${why.detail} is missing — one of its own dependencies, not this package. ` +
`Running "${installHint}" will report nothing to do. Reinstall it so its ` +
`dependency tree is resolved (npm install --force ${spec}), or install ${why.detail} directly.`
: `Optional dependency for ${feature} IS installed, but it failed to LOAD: ${why.detail}. ` +
`Running "${installHint}" will report nothing to do — the package is present and ` +
`broken, which is usually a native binary built for a different Node/ABI or platform. ` +
`Reinstall it (npm install --force ${spec}), or check that binary's own requirements.`, {
// The code still distinguishes the two for programmatic callers: only a
// genuinely absent package is MISSING.
code: why.kind === "missing" ? "OPTIONAL_DEP_MISSING" : "OPTIONAL_DEP_UNLOADABLE",
package: spec,
cause: err instanceof Error ? err.message : String(err),
});
}
}
//# sourceMappingURL=optional-dep.js.map