🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

ComfyUI 工作流面板 mangfufu/dsh-comfyui-web

DeepSeek Harness
mangfufu11 天前有提交已提供中文文档扫描:含敏感能力

在 dsh 网页中编辑 ComfyUI、实时预览并让 AI 操作工作流。

45.3/100
综合分
22
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/25 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add mangfufu/dsh-comfyui-web
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · ui
装得上吗
实装验证未通过(dependency_conflict),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(dependency_conflict · 2026/10/21) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

未发布到 npm registry,仅可从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-comfyui-web(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 17:40:39

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 72 处,展示前 60 条)
  • 发起网络请求
    client.js:486
    const response = await fetch('/dsh-comfyui/api', body ? { method: 'POST', signal,
  • 写入剪贴板
    client.js:631
    try { await navigator.clipboard.writeText(JSON.stringify(connection.mcp.clientConfig, null, 2)); setCopied(true); }
  • 读取本地文件
    lib/host.js:25
    const config = JSON.parse(await readFile(runtime.configFile, 'utf8').catch(() => '{}'));
  • 写入本地文件
    lib/host.js:32
    await writeFile(runtime.configFile, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
  • 读取本地文件
    lib/host.js:49
    const config = JSON.parse(await readFile(runtime.configFile, 'utf8').catch(() => '{}'));
  • 发起网络请求
    lib/host.js:87
    else if (body.op === 'check') { const stats = await runtime.request('/system_stats'); json(res, 200, { connected: true, system: stats.system }); }
  • 读取本地文件
    lib/runtime.js:139
    try { const config = JSON.parse(await readFile(this.configFile, 'utf8')); this.url = normalizeUrl(config.url || this.url); this.mcpEnabled = config.mc
  • 读取本地文件
    lib/runtime.js:141
    this.bridgeCode = await readFile(new URL('../bridge.js', import.meta.url), 'utf8');
  • 读取本地文件
    lib/runtime.js:142
    this.bridgeVersion = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')).version;
  • 写入本地文件
    lib/runtime.js:158
    await mkdir(dirname(this.configFile), { recursive: true });
  • 写入本地文件
    lib/runtime.js:159
    await writeFile(joinPath(dirname(this.configFile), 'connection.json'), JSON.stringify({ endpoint: this.frameOrigin + '/mcp', token: this.token }) + '\
  • 写入本地文件
    lib/runtime.js:172
    await mkdir(dirname(this.configFile), { recursive: true });
  • 读取本地文件
    lib/runtime.js:173
    const previous = JSON.parse(await readFile(this.configFile, 'utf8').catch(() => '{}'));
  • 写入本地文件
    lib/runtime.js:174
    await writeFile(this.configFile, JSON.stringify({ ...previous, url: next }, null, 2) + '\n', { mode: 0o600 });
  • 读取本地文件
    lib/runtime.js:185
    const previous = JSON.parse(await readFile(this.configFile, 'utf8').catch(() => '{}'));
  • 写入本地文件
    lib/runtime.js:186
    await writeFile(this.configFile, JSON.stringify({ ...previous, url: this.url, mcpEnabled: enabled }, null, 2) + '\n', { mode: 0o600 });
  • 发起网络请求
    lib/runtime.js:239
    const res = await fetch(this.url + path, { ...init, signal: signal ? AbortSignal.any([signal, timeout]) : timeout });
  • 发起网络请求
    lib/runtime.js:417
    const upstream = transport.request(target, { method: req.method, headers }, response => {
  • 发起网络请求
    lib/runtime.js:443
    const upstream = transport.request(target, { headers });
  • 发起网络请求
    lib/mcp.js:54
    try { stats = await runtime.request('/system_stats', {}, signal); } catch(e) { connected = false; error = e.message; }
  • 发起网络请求
    lib/mcp.js:115
    const stats = await runtime.request('/system_stats', {}, signal);
  • 发起网络请求
    lib/mcp.js:120
    const queue = await runtime.request('/queue', {}, signal);
  • 发起网络请求
    lib/mcp.js:153
    const raw = await runtime.request('/queue', {}, signal);
  • 发起网络请求
    lib/mcp.js:161
    const raw = await runtime.request('/history' + (args.promptId ? '/' + encodeURIComponent(args.promptId) : '?max_items=5'), {}, signal);
  • 发起网络请求
    lib/mcp.js:169
    const data = await runtime.request('/object_info' + (args.nodeType ? '/' + encodeURIComponent(args.nodeType) : ''), {}, signal);
  • 读取本地文件
    lib/extension-install.js:25
    const source = await readFile(new URL('../comfy-extension.js', import.meta.url), 'utf8');
  • 写入本地文件
    lib/extension-install.js:26
    await mkdir(web, { recursive: true });
  • 写入本地文件
    lib/extension-install.js:27
    await writeFile(join(web, 'loader.js'), source.replaceAll('__DSH_DEFAULT_BRIDGE__', JSON.stringify(bridgeOrigin)), 'utf8');
  • 写入本地文件
    lib/extension-install.js:28
    if (custom) await writeFile(join(directory, '__init__.py'),
  • 发起网络请求
    lib/reference.js:55
    const response = await fetch(runtime.url + '/upload/image', { method: 'POST',
  • 读取本地文件
    lib/launcher.js:8
    const isFile = path => stat(path).then(value => value.isFile(), () => false);
  • 读取本地文件
    lib/launcher.js:9
    const isDirectory = path => stat(path).then(value => value.isDirectory(), () => false);
  • 读取本地文件
    lib/launcher.js:34
    const value = (await readFile(join(root, '.app_path'), 'utf8')).trim();
  • 读取本地文件
    lib/launcher.js:62
    const entries = await readdir(root, { withFileTypes: true }).catch(() => []);
  • 读取本地文件
    lib/launcher.js:92
    const entries = await readdir(parent, { withFileTypes: true }).catch(() => []);
  • 发起网络请求
    lib/launcher.js:171
    const response = await fetch(url + '/system_stats', { signal: AbortSignal.timeout(4000) });
  • 读取本地文件
    lib/launcher.js:185
    async readConfig() { return JSON.parse(await readFile(this.runtime.configFile, 'utf8').catch(() => '{}')); }
  • 写入本地文件
    lib/launcher.js:217
    await mkdir(dirname(this.runtime.configFile), { recursive: true });
  • 写入本地文件
    lib/launcher.js:219
    await writeFile(this.runtime.configFile, JSON.stringify({ ...config, launcher: { path: selected.path, kind } }, null, 2) + '\n', { mode: 0o600 });
  • 发起网络请求
    lib/generation.js:147
    const data = await this.runtime.request('/history/' + encodeURIComponent(record.promptId));
  • 发起网络请求
    lib/generation.js:157
    const queue = await this.runtime.request('/queue');
  • 发起网络请求
    lib/generation.js:166
    const data = await this.runtime.request('/history/' + encodeURIComponent(record.promptId));
  • 写入本地文件
    lib/media.js:51
    await mkdir(dirname(keyFile), { recursive: true });
  • 写入本地文件
    lib/media.js:52
    try { await writeFile(keyFile, randomBytes(32), { flag: 'wx', mode: 0o600 }); }
  • 读取本地文件
    lib/media.js:54
    this.key = await readFile(keyFile);
  • 发起网络请求
    lib/media.js:92
    const upstream = await fetch(this.runtime.url + '/view?' + query, { method: req.method,
  • 读取本地文件
    lib/state.js:8
    try { const data = JSON.parse(await readFile(this.file, 'utf8')); this.records = data.tasks || []; }
  • 写入本地文件
    lib/state.js:21
    await mkdir(dirname(this.file), { recursive: true });
  • 写入本地文件
    lib/state.js:23
    await writeFile(temp, text, { mode: 0o600 }); await rename(temp, this.file);
  • 写入本地文件
    lib/state.js:23
    await writeFile(temp, text, { mode: 0o600 }); await rename(temp, this.file);
  • 读取本地文件
    lib/notes.js:31
    const data = JSON.parse(await readFile(this.file, 'utf8'));
  • 写入本地文件
    lib/notes.js:96
    await mkdir(dirname(this.file), { recursive: true });
  • 写入本地文件
    lib/notes.js:98
    await writeFile(temp, text, { mode: 0o600 }); await rename(temp, this.file);
  • 写入本地文件
    lib/notes.js:98
    await writeFile(temp, text, { mode: 0o600 }); await rename(temp, this.file);
  • 发起网络请求
    bridge.js:102
    const r = await fetch(bridgeBase + '/__dsh_comfyui/' + path, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Dsh-Comfy-Token': tok
  • 发起网络请求
    bridge.js:104
    const next = await (await fetch(bridgeBase + '/__dsh_comfyui/bootstrap')).json();
  • 发起网络请求
    bridge.js:2187
    const response = await fetch(bridgeBase + '/__dsh_comfyui/bootstrap');
  • 发起网络请求
    comfy-extension.js:13
    const response = await fetch(bridgeBase + '/__dsh_comfyui/bootstrap');
  • 读取本地文件
    mcp-stdio.mjs:11
    const connection = JSON.parse(await readFile(configPath, 'utf8'));
  • 发起网络请求
    mcp-stdio.mjs:12
    const response = await fetch(connection.endpoint, { method: 'POST', headers: { Authorization: 'Bearer ' + connection.token, 'Content-Type': 'applicati
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-jobs-local@deepseek-ai/dsh-mcp-client@deepseek-ai/dsh-tool-jobs
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

3 张 · 点击查看大图

功能亮点

  • 侧栏编辑工作流
  • 实时预览
  • AI操作工作流

README

由 DeepSeek 最新模型翻译生成
dsh-comfyui-web 在 dsh 里直接使用 ComfyUI,让 AI 通过 MCP 操作当前工作流。改提示词、调参数、运行生图,节点变化和生成预览都在同一个页面里看得见。 安装 需要已安装的 dsh、pnpm 和本机 ComfyUI,Node.js 22 及以上。 | 插件版本 | 支持的 dsh 版本 | | 0.5.13–0.5.18 | 0.2.0-rc.2、0.2.1-rc.2 | | 0.5.12 | 0.2.0-rc.2 | 插件通过 engines.dsh 和 DSH peer dependencies 同时声明已验证的宿主版本,兼容新旧宿主的检查方式。其他 DSH 版本需要另行验证;升级后若提示版本不兼容,请先更新插件。 dsh plugin --profile web add github:mangfufu/dsh-comfyui-web 从 DSH 启动本机 ComfyUI(Windows) 只打开 DSH 时,ComfyUI 面板会显示本机启动入口。点击 自动检测,或用 选择文件夹 / 选择启动文件 打开 Windows 文件选择窗口;也可以手动填写绝对路径。
展开完整文档(README 剩余 1 节)
dsh-comfyui-web

在 dsh 里直接使用 ComfyUI,让 AI 通过 MCP 操作当前工作流。改提示词、调参数、运行生图,节点变化和生成预览都在同一个页面里看得见。

安装

需要已安装的 dsh、pnpm 和本机 ComfyUI,Node.js 22 及以上。

| 插件版本 | 支持的 dsh 版本 |
| 0.5.13–0.5.18 | 0.2.0-rc.2、0.2.1-rc.2 |
| 0.5.12 | 0.2.0-rc.2 |

插件通过 engines.dsh 和 DSH peer dependencies 同时声明已验证的宿主版本,兼容新旧宿主的检查方式。其他 DSH 版本需要另行验证;升级后若提示版本不兼容,请先更新插件。

dsh plugin --profile web add github:mangfufu/dsh-comfyui-web

从 DSH 启动本机 ComfyUI(Windows)

只打开 DSH 时,ComfyUI 面板会显示本机启动入口。点击 自动检测,或用 选择文件夹 / 选择启动文件 打开 Windows 文件选择窗口;也可以手动填写绝对路径。支持任意名称的 .exe 启动器、.bat / .cmd / .ps1 启动脚本、.lnk 快捷方式,以及 ComfyUI 目录 / main.py,不限制启动器品牌,也不要求启动器旁边存在 ComfyUI 源码。

- 直接启动 ComfyUI:使用安装目录里的 Python(支持 AKi、Windows 便携包和本地 .venv / venv),按面板配置的本机地址和端口启动。服务就绪后面板自动连接。
- 使用启动器或脚本:使用自己的启动器、脚本或快捷方式,保留原有启动流程;如果打开的是启动器界面,请在其中点击启动。快捷方式保留其目标参数和工作目录。服务地址和端口需与面板设置一致,服务就绪后 DSH 自动连接。

自动检测只检查已配置路径、常见安装位置和带启动特征的文件名;手动选择文件不限制名称,选择文件夹会列出其中的启动文件。检测不会运行程序。点击启动后会保存入口;仅在识别出 ComfyUI 安装目录时配置桥接扩展,独立启动器沿用已有桥接配置。已运行或正在启动时不会重复启动。启动日志位于 ~/.dsh/comfyui/launch.log,快捷方式目标程序的日志由目标程序管理。PowerShell 脚本遵循本机执行策略,失败时可查看日志。直接启动不会读取第三方启动器的自定义参数。远程 / Docker 地址仍需在对应环境中启动服务。

首次安装时,填写 ComfyUI 前端目录

创建或编辑 ~/.dsh/comfyui/config.json,将示例中的前端目录替换为本机实际路径。Windows 的 ~ 是用户目录;Linux 同样填写实际路径。

{
"url": "http://127.0.0.1:8188",
"frontendExtensionsDir": "D:/ComfyUI/python/Lib/site-packages/comfyui_frontend_package/static/extensions"
}

首次安装或更新后,先保存工作流,再刷新已打开的 ComfyUI 原网页和 dsh 内的面板。
目录未配置或写入失败时,面板的 连接设置和 dsh 启动日志会提示原因。能看到 ComfyUI 网页,还需等到 AI 已连接 才能让 AI 操作。

Docker / WSL 中的 ComfyUI

推荐使用已经持久化挂载的 custom_nodes 目录。编辑运行 dsh 的系统中的 ~/.dsh/comfyui/config.json:

{
"url": "http://127.0.0.1:8188",
"customNodesDir": "/mnt/data/ai/comfyui/storage-nodes/custom_nodes"
}

上面是 Linux 路径示例。dsh 在 Windows 运行时,应填写 Windows 能写入的路径,例如 D:/ComfyUI/custom_nodes,或实际 WSL 发行版的共享目录 //wsl.localhost/Ubuntu/mnt/data/ai/comfyui/storage-nodes/custom_nodes。

确认对应宿主目录已挂载到容器的 custom_nodes,例如:

volumes:
- /mnt/data/ai/comfyui/storage-nodes/custom_nodes:/root/ComfyUI/custom_nodes

启动 dsh 后会生成 dsh-comfyui-bridge 扩展。先保存工作流,再重启 ComfyUI、刷新 dsh 面板。扩展保存在挂载目录中,容器重建后仍保留。

customNodesDir 与 frontendExtensionsDir 二选一。如果沿用前端目录方式,目录同样必须是 dsh 可写、容器可见 的持久化挂载路径,不能直接填写容器内的 /usr/local/... 路径。

如果 Windows 无法直接访问挂载目录,也可在该目录手动创建 dsh-comfyui-bridge/__init__.py:

WEB_DIRECTORY = "./web"
NODE_CLASS_MAPPINGS = {}

再将插件中的 comfy-extension.js 复制为 dsh-comfyui-bridge/web/loader.js,重启 ComfyUI。从 dsh 面板打开时会自动取得桥接地址,无需编辑脚本。手动安装完成并连接后,可忽略宿主的“尚未配置”日志提示。

使用

1. 启动并打开

1. 先启动 ComfyUI。
2. 启动 dsh web,打开 http://127.0.0.1:3080。
3. 进入对话,点击右上角 ComfyUI。

ComfyUI 嵌入 dsh 右侧面板左边聊需求,右边改节点、看预览。手动编辑和 AI 操作都落在当前工作流上。

插件界面跟随 dsh 的语言设置,支持中文和 English。ComfyUI 自身的语言在 ComfyUI 设置中选择。

2. 确认连接地址

默认连接 http://127.0.0.1:8188。如果更换了端口,打开 连接设置,修改地址后点击 保存并连接。

连接地址与 MCP 开关

MCP 默认开启,dsh 已自动接入。这里也能关闭 MCP,或复制配置给其他本机 AI 客户端使用。

3. 让 AI 操作工作流

直接在当前对话里告诉 AI:

读取当前工作流,优化正向提示词,保留现有模型和节点连接,生成一张图并查看效果。

AI 修改提示词并生成图片

提示词直接写入当前节点,右侧查看生成预览,左侧查看出图结果。AI 做了哪些修改,画布上实时可见。
工作流较大时,AI 默认先读取结构摘要,再按需读取节点参数,减少对话上下文占用。

通过插件提交生成后,图片和视频会自动显示在发起任务的回复正文下方,不需要 AI 额外调用显示工具;收起工具过程后仍可查看。
多张图片同样按原比例显示,空间不够时自动换行,不会缩成小方块。
同一路图片同时接标准保存和 Preview 节点时,对话只展示保存版;不同分支的图片和只有 Preview 的结果仍正常展示。
已有历史结果也可以让 AI「用插件显示这个图片或视频」。

也可以直接说「列出工作流标签页,切换到 XXX」,或「复制当前工作流,另开一个叫测试的标签页」。已保存的工作流也能打开或复制,源工作流和未保存修改会保留。

更多操作直接在对话里说:

- 「用我刚上传的参考图做图生图,填入图片节点。」
- 「加一个放大节点,接到输出前面。」
- 「保存当前工作流」,或「另存为视频测试」。另存遇到同名文件会提示换名称。
- 「列出修改快照,恢复到这次修改之前。」撤销、重做和快照在同一浏览器刷新后仍可使用;最多保留 20 份,实际数量受浏览器存储空间限制。
- 「查看我的生成任务,取消还没开始的那一个。」排队任务可按任务取消;中断正在生成的任务会影响整个实例当前生成。

生成完成会自动通知原对话,正在进行的回复不会被强行中断。
在 ComfyUI 手动停止后,AI 会收到“主动中断”通知;同一对话收到你的新消息前,插件会拦截再次提交。想继续时,明确告诉 AI「继续生成」。
极简预设也支持完成通知:缺少后台任务控制器时,插件会在当前会话内自动启用,无需手改配置。
插件重启会恢复原任务跟踪和结果;原对话的 AI 恢复运行后,会重新接上尚未送达的完成通知,不会重复提交生成。

想撤销就说「撤销刚才的修改」。界面大小用顶部 − / + 调整,点击 刷新 可重新加载 ComfyUI。

顶部 图片对话中模糊 开关默认关闭,同时适用于视频。开启后,点击模糊图先显示清晰图片,再点可查看大图;视频先解除模糊,再用播放按钮播放。鼠标悬停时,左上角的灰色小眼睛可切换模糊/清晰。关闭开关后,不出现模糊提示和小眼睛。只影响本插件返回到对话中的图片和视频,开关会记住。

友情链接

LINUX DO

Star History
预览分享卡片长什么样 ↓mangfufu/dsh-comfyui-web
分享 · DeepSeek Harness Hub
mangfufu/dsh-comfyui-web

在 dsh 网页中编辑 ComfyUI、实时预览并让 AI 操作工作流。

这是你的仓库?认领条目

回到顶部