mcpField
C
comfyui-node-packaging

comfyui-node-packaging

github.com/jtydhr88开发308136 次安装2 周前更新
代码仓库

关于 comfyui-node-packaging

为 ComfyUI 自定义节点开发者整理的打包、注册和发布指南,涵盖项目结构、依赖、前端扩展、国际化与注册表发布。也说明使用 comfy-cli 创建项目及部署时需要注意的约束。

主要功能

  1. 01V1 与 V3 节点注册
  2. 02项目结构和依赖管理
  3. 03前端扩展与帮助页面
  4. 04国际化文件组织
  5. 05ComfyUI Registry 发布
  6. 06comfy-cli 项目脚手架

适用场景

  1. 01为自定义节点建立标准项目结构
  2. 02为节点添加前端扩展和翻译
  3. 03准备节点包并发布到注册表
  4. 04检查依赖与部署限制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jtydhr88/comfyui-custom-node-skills --skill comfyui-node-packaging

安装后在 Claude Code 中输入/comfyui-node-packaging确认已加载。

详细介绍

ComfyUI 自定义节点打包

了解如何构建、注册和发布自定义节点包。

项目结构

ComfyUI/custom_nodes/
  my_custom_nodes/
    __init__.py            # Entry point (required)
    nodes.py               # Node class definitions
    requirements.txt       # Python dependencies
    pyproject.toml         # Package metadata
    README.md              # Documentation
    js/                    # Frontend extensions (optional)
    │   └── my_extension.js
    docs/                  # Help pages (optional)
    │   └── MyNode.md
    locales/               # i18n translations (optional)
        └── zh/
            └── main.json

入口点:init.py

V3 注册(推荐)
# __init__.py
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io

from .nodes import MyNode1, MyNode2, MyNode3

WEB_DIRECTORY = "./js"  # optional: frontend JS extensions

class MyNodesExtension(ComfyExtension):
    @override
    async def get_node_list(self) -> list[type[io.ComfyNode]]:
        return [MyNode1, MyNode2, MyNode3]

    @override
    async def on_load(self):
        # Optional: run initialization logic when extension loads
        pass

async def comfy_entrypoint() -> MyNodesExtension:
    return MyNodesExtension()
V1 注册(旧版)
# __init__.py
from .nodes import MyNode1, MyNode2

NODE_CLASS_MAPPINGS = {
    "MyNode1": MyNode1,
    "MyNode2": MyNode2,
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "MyNode1": "My Node 1",
    "MyNode2": "My Node 2",
}

WEB_DIRECTORY = "./js"

__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]

节点定义文件

# nodes.py
import torch
from comfy_api.latest import io

class MyNode1(io.ComfyNode):
    @classmethod
    def define_schema(cls):
        return io.Schema(
            node_id="MyNode1_UniqueID",   # globally unique
            display_name="My Node 1",
            category="my_nodes",
            description="Does something useful",
            inputs=[
                io.Image.Input("image"),
                io.Float.Input("value", default=1.0, min=0.0, max=10.0),
            ],
            outputs=[io.Image.Output("IMAGE")],
        )

    @classmethod
    def execute(cls, image, value):
        return io.NodeOutput(image * value)

依赖项:requirements.txt

# requirements.txt
opencv-python>=4.8.0
requests>=2.28.0

重要:仅列出 ComfyUI 尚未包含的依赖项。ComfyUI 的 requirements.txt 包含:torch、torchvision、torchsde、numpy、einops、transformers、tokenizers、sentencepiece、safetensors、aiohttp、pyyaml、Pillow、scipy、tqdm、psutil、av(>=17)、requests、blake3,以及非必需依赖 kornia、spandrel、pydantic。

  • torchaudio 不再是 ComfyUI 的依赖项(v0.38+)——如果你导入了它,请自行列出;或者使用核心 comfy.audio(resample、MelSpectrogram、bass_biquad、equalizer_biquad、treble_biquad)。accelerate 也未包含在内。
  • 官方支持的最低 PyTorch 版本为 2.7;Python 3.10 仍可使用,但会记录 EOL 警告。

pyproject.toml

[project]
name = "comfyui-my-nodes"
version = "1.0.0"
description = "My custom nodes for ComfyUI"
license = "MIT"
requires-python = ">=3.10"

[project.urls]
Repository = "https://github.com/username/comfyui-my-nodes"

前端扩展(JavaScript)

将 .js 文件放入 WEB_DIRECTORY:

my_custom_nodes/
  js/
    my_widgets.js      # Custom widget implementations
    my_extension.js    # Extension hooks
# __init__.py
WEB_DIRECTORY = "./js"

此目录中的所有 .js 文件都会由前端自动加载。可通过 extensions/my_custom_nodes/filename.css 访问 CSS 和其他资源。

帮助页面

为每个节点创建 Markdown 文档:

my_custom_nodes/
  docs/
    MyNode1.md         # filename matches node_id

# My Node 1

Processes images with adjustable value.

## Inputs
- **image**: The input image
- **value**: Processing strength (0.0 - 10.0)

## Outputs
- **IMAGE**: The processed image

国际化(i18n)

my_custom_nodes/
  locales/
    zh/
      main.json
      nodeDefs.json    # node definition translations
// locales/zh/nodeDefs.json
{
    "MyNode1_UniqueID": {
        "display_name": "我的节点1",
        "description": "处理图像",
        "inputs": {
            "image": { "display_name": "图像" },
            "value": { "display_name": "数值", "tooltip": "处理强度" }
        }
    }
}

单文件节点

对于非常简单的节点,所有内容都可以放在一个文件中:

# ComfyUI/custom_nodes/my_simple_node.py
import torch
from comfy_api.latest import ComfyExtension, io
from typing_extensions import override

class InvertImage(io.ComfyNode):
    @classmethod
    def define_schema(cls):
        return io.Schema(
            node_id="SimpleInvert",
            display_name="Simple Invert",
            category="image",
            inputs=[io.Image.Input("image")],
            outputs=[io.Image.Output("IMAGE")],
        )

    @classmethod
    def execute(cls, image):
        return io.NodeOutput(1.0 - image)


class SimpleExtension(ComfyExtension):
    @override
    async def get_node_list(self):
        return [InvertImage]

async def comfy_entrypoint():
    return SimpleExtension()

关键导入

# V3 API core
from comfy_api.latest import ComfyExtension, io, ui
from comfy_api.latest import ComfyAPI          # async runtime API (use with await)
from comfy_api.latest import ComfyAPISync      # sync runtime API (use in sync execute)
from comfy_api.latest import Input             # Input.Image, Input.Audio, Input.Mask, Input.Latent, Input.Video
from comfy_api.latest import InputImpl         # InputImpl.VideoFromFile, .VideoFromComponents, .VideoFromList
from comfy_api.latest import Types             # Types.MESH, Types.VOXEL, Types.File3D, Types.VideoCodec
from typing_extensions import override

# Common utilities
import folder_paths                            # directory management
from server import PromptServer                # server-to-client messaging
from comfy_execution.graph_utils import GraphBuilder  # node expansion

使用 folder_paths

ComfyUI 提供 folder_paths,用于访问标准目录:

import folder_paths

# Standard directories
input_dir = folder_paths.get_input_directory()
output_dir = folder_paths.get_output_directory()
temp_dir = folder_paths.get_temp_directory()

# Model directories
checkpoint_paths = folder_paths.get_folder_paths("checkpoints")
lora_paths = folder_paths.get_folder_paths("loras")

# Register custom model folder
folder_paths.add_model_folder_path("my_models", "/path/to/models")

# Get model file list
models = folder_paths.get_filename_list("checkpoints")
path = folder_paths.get_full_path_or_raise("checkpoints", name)   # resolve a combo value safely

# Path containment check before reading/writing user-supplied paths
folder_paths.is_within_directory(base_dir, candidate_path)

# Dataset files live in the "datasets" folder type (v0.30+)
dataset_dirs = folder_paths.get_folder_paths("datasets")

发布到 ComfyUI Registry

1. 创建 pyproject.toml
[project]
name = "comfyui-my-nodes"
version = "1.0.0"
description = "My custom nodes"
license = "MIT"

[tool.comfy]
PublisherId = "your-publisher-id"
2. 发布
comfy node publish
使用 GitHub Actions 进行 CI/CD
# .github/workflows/publish.yml
name: Publish to ComfyUI Registry
on:
  push:
    tags:
      - 'v*'
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install comfy-cli
      - run: comfy node publish
        env:
          COMFY_API_KEY: ${{ secrets.COMFY_API_KEY }}

使用 comfy-cli 搭建项目骨架

# Install comfy-cli
pip install comfy-cli

# Create new custom node project
cd ComfyUI/custom_nodes
comfy node scaffold

这会生成包含所有必需文件的样板结构。

节点 ID 最佳实践

  • 使用全局唯一的前缀:"MyProject_NodeName" 或 "username.NodeName"
  • 与核心节点冲突的 node_id 会被完全忽略——包括类和显示名称(v0.35+)
  • 发布后切勿更改 node_id(否则会破坏已保存的工作流)
  • 使用 display_name 更改面向用户的名称
  • 使用 search_aliases 提升可发现性:search_aliases=["alias1", "alias2"]

常见模式

组织多个节点文件
# __init__.py
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io

from .image_nodes import BlurNode, SharpenNode, ResizeNode
from .text_nodes import ConcatNode, FormatNode
from .util_nodes import SwitchNode, DebugNode

class MyExtension(ComfyExtension):
    @override
    async def get_node_list(self):
        return [
            BlurNode, SharpenNode, ResizeNode,
            ConcatNode, FormatNode,
            SwitchNode, DebugNode,
        ]

async def comfy_entrypoint():
    return MyExtension()
条件式加载节点
class MyExtension(ComfyExtension):
    @override
    async def get_node_list(self):
        nodes = [BasicNode]
        try:
            import cv2
            from .opencv_nodes import OpenCVNode
            nodes.append(OpenCVNode)
        except ImportError:
            pass
        return nodes

部署限制

  • 禁用节点:用户可以使用 --disabled-nodes-config file.yaml(disabled_nodes: [ids])裁剪节点 ID,也可以使用 --disable-partner-nodes / --offline(旧参数 --disable-api-nodes 是 --offline 的弃用别名)。不要假设其他包(或合作伙伴)的节点一定存在,例如在 GraphBuilder 扩展中。
  • 受管控的构建(v0.40+,仅适用于组织策略构建)会根据包中 .py/.pyw/.pyd/.so/.dll/.dylib/.js/.mjs 文件的 BLAKE3 摘要固定包版本,并拒绝包含 __pycache__/.pyc 文件或符号链接的包。仅发布源代码,且切勿在运行时改写或自动更新自己的代码文件。

另请参阅

  • comfyui-node-basics - 节点类结构
  • comfyui-node-frontend - JavaScript 扩展详情
  • comfyui-node-migration - 从 V1 迁移到 V3

评论

支持 Markdown