dsh-comfyui-mcp 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -3
- package/assets/icon-prompt.txt +5 -3
- package/assets/icon.png +0 -0
- package/assets/icon.svg +1 -1
- package/client.js +1 -1
- package/index.mjs +28 -25
- package/lazy-tools.mjs +36 -0
- package/package.json +73 -1
- package/runtime.mjs +92 -92
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ ComfyUI 工具来自 [artokun/comfyui-mcp](https://github.com/artokun/comfyui-mc
|
|
|
8
8
|
|
|
9
9
|
## 安装与配置
|
|
10
10
|
|
|
11
|
-
需要 Node.js 22 及兼容的 DSH(当前声明为 `>=0.2.1-rc.3 <0.3.0`)。在 DSH 插件管理器中安装 `dsh-comfyui-mcp`(指定版本可填 `dsh-comfyui-mcp@0.1.
|
|
11
|
+
需要 Node.js 22 及兼容的 DSH(当前声明为 `>=0.2.1-rc.3 <0.3.0`)。在 DSH 插件管理器中安装 `dsh-comfyui-mcp`(指定版本可填 `dsh-comfyui-mcp@0.1.6`),然后启用组件。也支持安装本地目录或 `.tgz`。不要另外添加同名 MCP 桥。
|
|
12
12
|
|
|
13
13
|
打开「插件 → ComfyUI MCP」,点击「测试连接」探测现有服务。在同页下方的「运行配置」表单设置:
|
|
14
14
|
|
|
@@ -21,12 +21,66 @@ ComfyUI 工具来自 [artokun/comfyui-mcp](https://github.com/artokun/comfyui-mc
|
|
|
21
21
|
|
|
22
22
|
本地启动仅接受回环 HTTP 地址,固定禁用浏览器自动打开、自定义节点和 API 节点。已有远程实例不受这些启动参数影响。模型与 Python 环境需由用户事先准备,本插件不自动安装或更新它们。
|
|
23
23
|
|
|
24
|
+
## 工具懒加载
|
|
25
|
+
|
|
26
|
+
`lazyTools` 默认开启。代理最初只看到 `comfyui_load_tools`;需要生图、工作流、图片读取或运行管理时,调用它即可为当前代理加载 `runtime`、`list_tools`、`describe_tool`、`call_tool`。加载不会启动 ComfyUI 或加载模型,其他代理仍保持自己的加载状态。代理重建后可再次调用加载入口。
|
|
27
|
+
|
|
28
|
+
在插件详情页「运行配置」顶部关闭「工具懒加载」并保存,即直接提供原来的 4 个工具。开关与 `autoStart` 独立:前者控制工具定义何时进入模型上下文,后者控制是否允许启动本地进程。配置在代理下一步执行时生效。
|
|
29
|
+
|
|
30
|
+
这项优化将未使用 ComfyUI 时的 4 个工具定义减少为 1 个加载入口;首次使用会多一次工具调用。加载后不再节省这 4 个定义,也不承诺固定 token 降幅。工具动态增加由 Harness 的注册与会话记录机制处理;支持历史工具更新的模型路由可使用原生增量声明,其余路由使用当前工具列表,插件不直接改写请求字段。
|
|
31
|
+
|
|
24
32
|
## 使用
|
|
25
33
|
|
|
26
|
-
`list_tools` 和 `describe_tool` 只发现工具,不启动 ComfyUI 或加载模型。先读取工具 schema,再用 `call_tool` 执行业务工具。原生 Krea2 API 工作流使用上游 `enqueue_workflow`;模型名称和工作流须匹配现有 ComfyUI 安装。结果保留上游 MCP
|
|
34
|
+
`list_tools` 和 `describe_tool` 只发现工具,不启动 ComfyUI 或加载模型。先读取工具 schema,再用 `call_tool` 执行业务工具。原生 Krea2 API 工作流使用上游 `enqueue_workflow`;模型名称和工作流须匹配现有 ComfyUI 安装。结果保留上游 MCP 内容,包括图片和本地保存路径。本机 Krea 2 的提示词写法、采样参数与现成工作流见下文 [技能:krea2-image-prompt](#技能krea2-image-prompt)。
|
|
27
35
|
|
|
28
36
|
`runtime` 提供 `status`、`test`、`start`、`stop`、`free`。状态查询和连接测试不启动进程。开启按需启动且服务未运行时,页面显示「未运行 · 按需启动待命」;执行生图工具时才会启动。生成完成后使用 `stop` 释放插件自有进程;`free` 请求当前连接实例卸载模型,可能影响该实例的其他使用者。
|
|
29
37
|
|
|
38
|
+
## 模型与工作流的决定权
|
|
39
|
+
|
|
40
|
+
插件和上游都不替你选模型。四条路径的决定方各不相同:
|
|
41
|
+
|
|
42
|
+
- `enqueue_workflow`(`action:"enqueue"`):由调用方提交的 API 工作流决定,`UNETLoader` / `CLIPLoader` / `VAELoader` 里写什么文件就加载什么;seed 按原值使用,不随机化。
|
|
43
|
+
- `generate_image`(`action:"image"`):由上游自建 txt2img 图并**自动挑选 checkpoint**——读 safetensors 头部判断是否含文本编码器,跳过视频类模型;其余参数来自默认值(配置文件 → `COMFYUI_DEFAULT_*` 环境变量 → 运行时 `get_defaults`,优先级递增)。没有可用 checkpoint 时会直接报错。Krea 2 是 UNet + CLIP + VAE 三件套、不是 checkpoint 模型,因此**不走这条路径**。
|
|
44
|
+
- `enqueue_workflow`(`action:"run_template"`):由 pack 内的 `workflow.json` 决定,模型名是写死的常量;pack 依赖的自定义节点若被启动参数禁用则无法执行。
|
|
45
|
+
- `action:"rerun"`:复用历史中的工作流,seed 默认重新随机。
|
|
46
|
+
|
|
47
|
+
默认值配置文件位于 `%XDG_CONFIG_HOME%`(缺省 `~/.config`)下的 `comfyui-mcp/config.json`。
|
|
48
|
+
|
|
49
|
+
## 技能:krea2-image-prompt
|
|
50
|
+
|
|
51
|
+
`skills/krea2-image-prompt/` 是一份面向本机 Krea 2 Turbo 的出图技能,教会代理「本机这套模型该怎么写提示词、怎么拼工作流、怎么后处理」,覆盖 UI 图标、背景、空状态、角色立绘、纹理图案等位图素材场景。
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
skills/krea2-image-prompt/
|
|
55
|
+
├── SKILL.md # 入口:机器事实、采样参数、提示词硬规则、六段骨架、调用路径、故障速查
|
|
56
|
+
├── reference/recipes.md # 分类配方(图标/背景/空状态/角色/图案/UI 整屏…)与实测结论
|
|
57
|
+
├── reference/experiments.md # 对比实验记录与耗时基线
|
|
58
|
+
├── reference/workflow.json # 可直接提交的 API 工作流(原生节点,无自定义节点依赖)
|
|
59
|
+
└── scripts/ # krea.py 出图、batch.py 批量、key.py 色键抠透明底
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**路径与发现机制。** DSH 的技能发现根有两类:用户级 `$DSH_HOME/skills`(缺省 `~/.dsh/skills`)与项目级 `<工作目录>/.agents/skills`;仓库里的 `skills/` 本身不是发现根。本技能属于「只有这台机器有对应 MCP」的那种,因此装到**用户级全局根**:真身留在 `skills/`(进版本库),`~/.dsh/skills/krea2-image-prompt` 是指向它的 **Windows 目录联结点(junction)**——单一副本,任意项目都能用,也不占用跨工具的 `~/.agents`。克隆本仓库到新机器、或仓库改名搬家后联结点失效,重建:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
New-Item -ItemType Junction -Path "$HOME\.dsh\skills\krea2-image-prompt" -Target "<仓库路径>\skills\krea2-image-prompt"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**触发方式。** 任务涉及「画图标 / 画背景 / 生成角色素材 / 做纹理」时自动加载;也可用 `skill` 工具或 `/krea2-image-prompt` 显式调用。
|
|
69
|
+
|
|
70
|
+
**直接使用脚本。**
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
cd skills/krea2-image-prompt
|
|
74
|
+
python scripts/krea.py --prompt "a flat two-color vector icon of a whale" --w 1024 --h 1024
|
|
75
|
+
python scripts/key.py in.png out.png # 纯色底 → 透明 PNG
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
脚本通过 HTTP 直连 ComfyUI,服务地址取 `COMFY_URL` 环境变量,默认 `http://127.0.0.1:8188`。技能假定本机已备好 `krea2_turbo_fp8_scaled.safetensors`、`qwen3vl_4b_fp8_scaled.safetensors`、`qwen_image_vae.safetensors`,且不依赖任何自定义节点——因此插件按需启动时固定携带的 `--disable-all-custom-nodes` 不影响它。
|
|
79
|
+
|
|
80
|
+
**与启动方式的关系。** 服务若已由绘世等启动器在 `url` 上运行,插件只探测和连接,启动器里的端口、自定义节点、显存参数全部生效;只有插件**自己**拉起进程时才会追加自己的启动参数。技能里的实测数据来自插件自有启动的精简实例。
|
|
81
|
+
|
|
82
|
+
技能目录属于仓库内资产,不在 `package.json` 的 `files` 白名单中,因此不随 npm 包分发。
|
|
83
|
+
|
|
30
84
|
## 禁用、卸载与迁移
|
|
31
85
|
|
|
32
86
|
通过 DSH 插件管理器禁用、重新启用或卸载。禁用/卸载关闭本插件连接并停止本插件启动的 ComfyUI;不会停止外部进程,也不删除模型、工作流或生成图片。
|
|
@@ -43,7 +97,7 @@ npm run pack:check
|
|
|
43
97
|
npm pack
|
|
44
98
|
```
|
|
45
99
|
|
|
46
|
-
DSH 提供的 peer 包需在插件运行环境中可解析。安装发布包时由 DSH 管理依赖;任何待运行的依赖构建脚本都应先取得用户许可。包采用文件白名单,不包含模型、Python
|
|
100
|
+
DSH 提供的 peer 包需在插件运行环境中可解析。安装发布包时由 DSH 管理依赖;任何待运行的依赖构建脚本都应先取得用户许可。包采用文件白名单,不包含模型、Python、工作流、生成图片、凭据或个人配置。图标由本机 Krea 2 Turbo 生成并缩放,SVG 内嵌同一位图;设计提示词保存在 `assets/icon-prompt.txt`,使用 DSH 的 `./icon` 导出。
|
|
47
101
|
|
|
48
102
|
## 限制与安全
|
|
49
103
|
|
package/assets/icon-prompt.txt
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
Krea 2 Turbo generated artwork, 2026-10-26.
|
|
2
|
+
Prompt: A minimal vector-style icon for a creative workflow bridge: three softly rounded indigo nodes connected by a single flowing turquoise ribbon forming an abstract C, one small four-point white sparkle at the upper right. Bold simple geometric silhouette, crisp edges, centered front view, generous padding, refined modern software brand mark, limited indigo and mint palette, completely flat warm off-white background, no text
|
|
3
|
+
Model: krea2_turbo_fp8_scaled.safetensors; seed 261026; 1024x1024; 8 steps; CFG 1; er_sde/simple.
|
|
4
|
+
Source: v016_00001_.png; icon.svg embeds the same raster artwork.
|
|
5
|
+
License: MIT, same as this plugin.
|
package/assets/icon.png
ADDED
|
Binary file
|
package/assets/icon.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="16" fill="#192b46"/><path d="M16 39V24h14v16h18V25" fill="none" stroke="#79e1d5" stroke-width="5" stroke-linejoin="round"/><circle cx="16" cy="24" r="5" fill="#a8b4ff"/><circle cx="48" cy="25" r="5" fill="#a8b4ff"/><path d="m30 19 4-7 4 7-4 7z" fill="#fff"/></svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><image width="256" height="256" href="data:image/png;base64,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"/></svg>
|
package/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
window.__ModuleLoader__.load({id:'dsh-comfyui-mcp',factory(require){const React=require('react'),h=React.createElement;return {inject:['slots','locale'],apply(ctx){ctx.locale.register('dsh-comfyui-mcp','zh',{title:'连接与运行状态',test:'测试连接',loading:'正在测试…',idle:'尚未测试',online:'连接成功',offline:'未连接',standby:'未运行 · 按需启动待命',owned:'本插件进程',external:'外部进程',hint:'测试只探测地址,不启动 ComfyUI 或加载模型。请在下方运行配置中编辑参数。',safety:'禁用或卸载仅停止本插件进程;保留模型、工作流与图片。',credit:'工具由 artokun/comfyui-mcp 0.52.206 提供。'});ctx.locale.register('dsh-comfyui-mcp','en',{title:'Connection & runtime',test:'Test connection',loading:'Testing…',idle:'Not tested',online:'Connected',offline:'Offline',standby:'Not running · on-demand ready',owned:'Plugin-owned process',external:'External process',hint:'Testing never starts ComfyUI or loads models. Edit runtime parameters in the form below.',safety:'Disabling/uninstalling stops only plugin-owned processes; models, workflows and images are retained.',credit:'Tools powered by artokun/comfyui-mcp 0.52.206.'});const t=ctx.locale.bind('dsh-comfyui-mcp');function Status(){const [s,set]=React.useState(),[busy,setBusy]=React.useState(false);React.useSyncExternalStore(fn=>ctx.locale.subscribe(fn),()=>ctx.locale.getSnapshot());async function test(){setBusy(true);try{const r=await fetch('/api/dsh-comfyui/status',{cache:'no-store'});if(!r.ok)throw Error('HTTP '+r.status);set(await r.json());}catch(e){set({running:false,error:e.message});}finally{setBusy(false);}}const standby=!!s&&!s.running&&s.autoStart===true&&!s.owned&&!s.lastError&&s.error==='fetch failed';return h('section',{className:'dcm-status'},h('style',null,'.dcm-status{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;padding:20px;color:var(--dsw-alias-label-primary);font-size:14px}.dcm-status .dcm-row{display:flex;justify-content:space-between;align-items:center;gap:16px}.dcm-status .dcm-badge{font-size:12px;background:var(--dsw-alias-bg-layer-2);padding:6px 10px;border-radius:20px}.dcm-status h3{font-size:15px;margin:0 0 16px}.dcm-status p{line-height:1.7;color:var(--dsw-alias-label-secondary)}.dcm-status button{font:inherit;padding:9px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background
|
|
1
|
+
window.__ModuleLoader__.load({id:'dsh-comfyui-mcp',factory(require){const React=require('react'),h=React.createElement;return {inject:['slots','locale'],apply(ctx){ctx.locale.register('dsh-comfyui-mcp','zh',{title:'连接与运行状态',test:'测试连接',loading:'正在测试…',idle:'尚未测试',online:'连接成功',offline:'未连接',standby:'未运行 · 按需启动待命',owned:'本插件进程',external:'外部进程',hint:'测试只探测地址,不启动 ComfyUI 或加载模型。请在下方运行配置中编辑参数。',safety:'禁用或卸载仅停止本插件进程;保留模型、工作流与图片。',credit:'工具由 artokun/comfyui-mcp 0.52.206 提供。'});ctx.locale.register('dsh-comfyui-mcp','en',{title:'Connection & runtime',test:'Test connection',loading:'Testing…',idle:'Not tested',online:'Connected',offline:'Offline',standby:'Not running · on-demand ready',owned:'Plugin-owned process',external:'External process',hint:'Testing never starts ComfyUI or loads models. Edit runtime parameters in the form below.',safety:'Disabling/uninstalling stops only plugin-owned processes; models, workflows and images are retained.',credit:'Tools powered by artokun/comfyui-mcp 0.52.206.'});const t=ctx.locale.bind('dsh-comfyui-mcp');function Status(){const [s,set]=React.useState(),[busy,setBusy]=React.useState(false);React.useSyncExternalStore(fn=>ctx.locale.subscribe(fn),()=>ctx.locale.getSnapshot());async function test(){setBusy(true);try{const r=await fetch('/api/dsh-comfyui/status',{cache:'no-store'});if(!r.ok)throw Error('HTTP '+r.status);set(await r.json());}catch(e){set({running:false,error:e.message});}finally{setBusy(false);}}const standby=!!s&&!s.running&&s.autoStart===true&&!s.owned&&!s.lastError&&s.error==='fetch failed';return h('section',{className:'dcm-status'},h('style',null,'.dcm-status{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;padding:20px;color:var(--dsw-alias-label-primary);font-size:14px}.dcm-status .dcm-row{display:flex;justify-content:space-between;align-items:center;gap:16px}.dcm-status .dcm-badge{font-size:12px;background:var(--dsw-alias-bg-layer-2);padding:6px 10px;border-radius:20px}.dcm-status h3{font-size:15px;margin:0 0 16px}.dcm-status p{line-height:1.7;color:var(--dsw-alias-label-secondary)}.dcm-status button{font:inherit;padding:9px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base);cursor:pointer}.dcm-status pre{white-space:pre-wrap;overflow-wrap:anywhere}.dcm-status small{color:var(--dsw-alias-label-secondary)}'),h('div',{className:'dcm-row'},h('h3',null,t('title')),h('span',{className:'dcm-badge',role:'status'},busy?t('loading'):!s?t('idle'):s.running?t('online')+' · '+t(s.owned?'owned':'external'):t(standby?'standby':'offline'))),h('p',null,'工具发现不会启动 ComfyUI 或加载模型。'),s?.error&&!standby?h('pre',{role:'alert'},s.error):null,s?.lastError?h('pre',null,s.lastError):null,h('button',{onClick:test,disabled:busy},t(busy?'loading':'test')),h('p',null,t('hint')),h('p',null,t('safety')),h('small',null,t('credit')));}function ConfigPage({view,form}){const initial=form?.state?.value;const [draft,setDraft]=React.useState(null),[busy,setBusy]=React.useState(false),[message,setMessage]=React.useState('');const zh=ctx.locale.getLocale().id!=='en';const text=(a,b)=>zh?a:b;const fields=['lazyTools','url','autoStart','comfyuiDir','pythonPath','launchArgs','startupTimeoutMs'];const defaults={lazyTools:true,url:'http://127.0.0.1:8188',autoStart:false,comfyuiDir:'',pythonPath:'',launchArgs:[],startupTimeoutMs:120000};if(view==='summary')return text('ComfyUI 连接与本地按需启动参数','ComfyUI connection and local on-demand startup');if(!initial)return h('p',{role:'status'},text('配置尚未就绪或不可用','Configuration is loading or unavailable'));const values=draft||Object.fromEntries(fields.map(k=>[k,k==='launchArgs'?JSON.stringify(initial[k]??defaults[k]):initial[k]??defaults[k]]));async function save(e){e.preventDefault();setBusy(true);setMessage('');try{const v={...values,launchArgs:JSON.parse(values.launchArgs),startupTimeoutMs:Number(values.startupTimeoutMs)};if(!Array.isArray(v.launchArgs)||v.launchArgs.some(x=>typeof x!=='string'))throw Error('launchArgs must be a JSON string array');if(!Number.isFinite(v.startupTimeoutMs)||v.startupTimeoutMs<1000||v.startupTimeoutMs>600000)throw Error('startupTimeoutMs: 1000–600000');const ok=await form.mutate(fields.map(k=>({op:'set',path:[k],value:v[k]})),form.state.revision);setMessage(ok?text('配置已保存','Configuration saved'):text('保存失败或配置已变更,请重新打开页面后重试','Save failed or configuration changed; reopen this page and retry'));if(ok)setDraft(null);}catch(e){setMessage(e.message);}finally{setBusy(false);}}return h('form',{className:'dcm-config',onSubmit:save},h('style',null,'.dcm-config{max-width:760px;font-size:14px}.dcm-config .dcm-lazy-row{display:flex;align-items:center;justify-content:space-between;gap:16px;border-bottom:1px solid var(--dsw-alias-border-l1);padding-bottom:18px;margin-bottom:20px}.dcm-lazy-row p{margin:6px 0 0;font-size:13px;line-height:1.7;color:var(--dsw-alias-label-secondary)}.dcm-config .dcm-switch{flex-shrink:0;display:flex;justify-content:flex-end;width:36px;height:20px;padding:2px;border:0;border-radius:20px;background:var(--dsw-alias-brand-primary)}.dcm-switch::after{content:"";width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-bg-base)}.dcm-config .dcm-switch[aria-checked=false]{background:var(--dsw-alias-state-idle-primary);justify-content:flex-start}.dcm-config button:disabled{opacity:.5;cursor:not-allowed}.dcm-config label{display:block;margin:18px 0 6px}.dcm-config input,.dcm-config select,.dcm-config textarea{display:block;box-sizing:border-box;width:100%;padding:9px 12px;margin-top:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font:inherit}.dcm-config button{font:inherit;padding:9px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-bg-base)}'),h('div',{className:'dcm-lazy-row'},h('div',null,h('span',{id:'dcm-lazy-label'},text('工具懒加载','Lazy tool loading')),h('p',{id:'dcm-lazy-help'},values.lazyTools?text('仅保留加载入口,需要时再展开 4 个 ComfyUI 工具;不启动服务。','Keep one loader; expose four ComfyUI tools on demand without starting the service.'):text('直接提供 4 个 ComfyUI 工具,省去首次加载调用。','Expose four ComfyUI tools immediately; skip the initial loading call.'))),h('button',{className:'dcm-switch',type:'button',role:'switch','aria-checked':values.lazyTools,'aria-labelledby':'dcm-lazy-label','aria-describedby':'dcm-lazy-help',disabled:busy||!form.state.writable,onClick:()=>setDraft({...values,lazyTools:!values.lazyTools})})),...fields.filter(k=>k!=='lazyTools').map(k=>h('label',{key:k},text({url:'ComfyUI 地址',autoStart:'允许本地按需启动',comfyuiDir:'ComfyUI 目录',pythonPath:'Python 可执行文件',launchArgs:'额外启动参数',startupTimeoutMs:'启动超时(毫秒)'}[k],{url:'ComfyUI address',autoStart:'Allow local on-demand startup',comfyuiDir:'ComfyUI directory',pythonPath:'Python executable',launchArgs:'Extra launch arguments',startupTimeoutMs:'Startup timeout (ms)'}[k]),k==='autoStart'?h('select',{value:String(values[k]),disabled:busy||!form.state.writable,onChange:e=>setDraft({...values,[k]:e.target.value==='true'})},h('option',{value:'false'},text('关闭:仅连接现有服务','Off: existing service only')),h('option',{value:'true'},text('开启:首次执行工具时启动','On: start on first tool execution'))):h(k==='launchArgs'?'textarea':'input',{type:k==='startupTimeoutMs'?'number':'text',rows:k==='launchArgs'?3:undefined,placeholder:k==='comfyuiDir'?text('包含 main.py 的目录','Directory containing main.py'):k==='pythonPath'?text('Python 的完整路径','Full Python path'):undefined,value:values[k],disabled:busy||!form.state.writable,onChange:e=>setDraft({...values,[k]:e.target.value})}))),h('p',null,text('仅保存时更新配置;不会安装模型或 Python 依赖。启动参数为 JSON 数组。','Only Save updates configuration. No models or Python dependencies are installed. Launch arguments are a JSON array.')),h('button',{type:'submit',disabled:busy||!form.state.writable},busy?text('保存中…','Saving…'):text('保存配置','Save configuration')),h('p',{role:'status'},message));}function UnifiedPage({scope}){const state=React.useSyncExternalStore(fn=>scope.subscribe(fn),()=>scope.getSnapshot());return h('div',{className:'dcm-page',style:{maxWidth:760}},h(Status),h('section',{style:{border:'1px solid var(--dsw-alias-border-l1)',borderRadius:12,padding:20,marginTop:24}},h('h3',{style:{fontSize:15,margin:'0 0 16px'}},ctx.locale.getLocale().id==='en'?'Runtime configuration · DSH configuration form':'运行配置 · DSH 官方配置表单'),h('p',{style:{lineHeight:1.7,color:'var(--dsw-alias-label-secondary)'}},ctx.locale.getLocale().id==='en'?'This page uses DSH configuration snapshots, revisions and atomic saves.':'表单与连接状态位于同一插件详情页,使用 DSH 官方配置快照、修订号及原子保存操作。'),h(ConfigPage,{view:'page',form:{state,mutate:(ops,revision)=>scope.mutate(ops,revision)}})));}ctx.inject(['configForms'],c=>{const forms=c.configForms;c.effect(()=>forms.whileServed(['dsh-comfyui-mcp'],()=>c.slots.inject('plugins.bundle.config',()=>c.slots.register({name:'plugins.bundle.config',key:'dsh-comfyui-mcp'},()=>h(UnifiedPage,{scope:forms.get('dsh-comfyui-mcp')})))),'comfyui.config-page');});}};}});
|
package/index.mjs
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import z from '@deepseek-ai/schemastery';
|
|
2
|
-
import * as mcp from '@deepseek-ai/dsh-mcp-client';
|
|
3
|
-
import { createServer } from 'node:http';
|
|
4
|
-
import { randomBytes } from 'node:crypto';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { createConfiguredRuntime } from './configured-runtime.mjs';
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
await
|
|
24
|
-
if(
|
|
25
|
-
}
|
|
1
|
+
import z from '@deepseek-ai/schemastery';
|
|
2
|
+
import * as mcp from '@deepseek-ai/dsh-mcp-client';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { createConfiguredRuntime } from './configured-runtime.mjs';
|
|
7
|
+
import { installLazyTools } from './lazy-tools.mjs';
|
|
8
|
+
export const name='dsh-comfyui-mcp';
|
|
9
|
+
export const inject=['tools','webServer','agents'];
|
|
10
|
+
export const Config=z.object({
|
|
11
|
+
lazyTools:z.boolean().default(true).description('Load the four ComfyUI tools only when requested by each agent. Does not start ComfyUI.'),
|
|
12
|
+
url:z.string().default('http://127.0.0.1:8188').description('ComfyUI HTTP(S) address; default is loopback.'),
|
|
13
|
+
autoStart:z.boolean().default(false).description('Allow local on-demand startup. Discovery and connection tests never start ComfyUI.'),
|
|
14
|
+
comfyuiDir:z.string().default('').description('Absolute directory containing main.py. Required for local startup.'),
|
|
15
|
+
pythonPath:z.string().default('').description('Absolute Python executable path. No automatic dependency installation.'),
|
|
16
|
+
launchArgs:z.array(String).default([]).description('Additional ComfyUI arguments; network binding overrides are forbidden.'),
|
|
17
|
+
startupTimeoutMs:z.number().min(1000).max(600000).default(120000)
|
|
18
|
+
}).volatile();
|
|
19
|
+
export async function apply(ctx,config){
|
|
20
|
+
const runtime=createConfiguredRuntime(()=>config.get());const token=randomBytes(32).toString('hex');let disposed=false;
|
|
21
|
+
const control=createServer(async(req,res)=>{res.setHeader('Content-Type','application/json');try{if(req.method!=='POST'||req.headers.authorization!=='Bearer '+token){res.writeHead(403);return res.end('{}');}const action=req.url.slice(1);if(!['status','start','stop','free','config'].includes(action))throw Error('Unknown action');const value=await runtime.run(action);res.end(JSON.stringify(value));}catch(e){res.writeHead(500);res.end(JSON.stringify({error:e.message}));}});
|
|
22
|
+
await new Promise((resolve,reject)=>{control.once('error',reject);control.listen(0,'127.0.0.1',resolve);});
|
|
23
|
+
ctx.effect(()=>async()=>{disposed=true;await runtime.close();control.closeAllConnections();await new Promise(r=>control.close(r));},'comfyui.runtime');
|
|
24
|
+
ctx.effect(()=>ctx.webServer.register({kind:'exact',path:'/api/dsh-comfyui/status',handler:async(req,res)=>{res.setHeader('Content-Type','application/json');res.setHeader('Cache-Control','no-store');if(req.method!=='GET'){res.writeHead(405);return res.end('{}');}try{const s=await runtime.run('status');res.end(JSON.stringify(s));}catch(e){res.writeHead(500);res.end(JSON.stringify({error:e.message}));}}}),'comfyui.status-route');
|
|
25
|
+
await mcp.apply(ctx,mcp.Config({transport:'stdio',serverName:'comfyui',command:process.execPath,args:[fileURLToPath(new URL('./server.mjs',import.meta.url))],env:{ELECTRON_RUN_AS_NODE:'1',DSH_COMFY_CONTROL:'http://127.0.0.1:'+control.address().port,DSH_COMFY_TOKEN:token},toolCallTimeoutMs:1800000,failOnStartupError:true}));
|
|
26
|
+
if(disposed)throw Error('Plugin disposed during startup');
|
|
27
|
+
installLazyTools(ctx,config);
|
|
28
|
+
}
|
package/lazy-tools.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const NAMES=['runtime','list_tools','describe_tool','call_tool'].map(n=>'mcp__comfyui__'+n);
|
|
2
|
+
export function installLazyTools(ctx,config){
|
|
3
|
+
const states=new Map();
|
|
4
|
+
function attach(agent){
|
|
5
|
+
if(states.has(agent))return states.get(agent);
|
|
6
|
+
const state={loaded:false,mode:undefined,disposers:[]};states.set(agent,state);
|
|
7
|
+
state.dispose=agent.ctx.effect(()=>()=>{for(const dispose of state.disposers.splice(0).reverse())dispose();states.delete(agent);},'comfyui.lazy-tools');
|
|
8
|
+
return state;
|
|
9
|
+
}
|
|
10
|
+
function reconcile(agent){
|
|
11
|
+
const state=attach(agent),lazy=config.get().lazyTools!==false;
|
|
12
|
+
if(state.mode===lazy)return state;
|
|
13
|
+
for(const dispose of state.disposers.splice(0).reverse())dispose();
|
|
14
|
+
state.mode=lazy;
|
|
15
|
+
if(!lazy){for(const name of NAMES){const definition=ctx.tools.get(name);if(!definition)throw Error('ComfyUI bridge unavailable: '+name);state.disposers.push(agent.ctx.tools.register({...definition}));}return state;}
|
|
16
|
+
state.disposers.push(agent.ctx.tools.restrict({deny:NAMES}));
|
|
17
|
+
const load=()=>{
|
|
18
|
+
if(state.loaded)return;
|
|
19
|
+
const definitions=NAMES.map(name=>{const definition=ctx.tools.get(name);if(!definition)throw Error('ComfyUI bridge unavailable: '+name);return definition;});
|
|
20
|
+
const installed=[];
|
|
21
|
+
try{for(const definition of definitions)installed.push(agent.ctx.tools.register({...definition}));}
|
|
22
|
+
catch(error){for(const dispose of installed.reverse())dispose();throw error;}
|
|
23
|
+
state.disposers.push(...installed);state.loaded=true;
|
|
24
|
+
};
|
|
25
|
+
if(state.loaded){state.loaded=false;load();}
|
|
26
|
+
state.disposers.push(agent.ctx.tools.register({
|
|
27
|
+
name:'comfyui_load_tools',description:'Load ComfyUI image generation, workflow, image retrieval and runtime tools for this agent. Does not start ComfyUI or load models.',parameters:{type:'object',properties:{},additionalProperties:false},
|
|
28
|
+
output:{schema:{type:'object',properties:{tools:{type:'array',items:{type:'string'}}},required:['tools'],additionalProperties:false},render:(_,value)=>[{type:'text',text:JSON.stringify(value)}]},
|
|
29
|
+
execute:async()=>{load();return {tools:NAMES};}
|
|
30
|
+
}));return state;
|
|
31
|
+
}
|
|
32
|
+
ctx.on('agent/created',({agent})=>{reconcile(agent);});
|
|
33
|
+
ctx.on('agent/pre-step',({agent},next)=>{reconcile(agent);return next();});
|
|
34
|
+
for(const agent of ctx.agents.list())reconcile(agent);
|
|
35
|
+
ctx.effect(()=>()=>{for(const state of [...states.values()])state.dispose();},'comfyui.lazy-tools-owner');
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +1,73 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-comfyui-mcp",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "ComfyUI connection and safe on-demand lifecycle for DSH; powered by artokun/comfyui-mcp.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Kaczev/dsh-comfyui-mcp.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Kaczev/dsh-comfyui-mcp#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Kaczev/dsh-comfyui-mcp/issues"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22",
|
|
21
|
+
"dsh": ">=0.2.1-rc.3 <0.3.0"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./index.mjs",
|
|
25
|
+
"./package.json": "./package.json",
|
|
26
|
+
"./client": "./client.js",
|
|
27
|
+
"./icon": "./assets/icon.svg",
|
|
28
|
+
"./locale/*.json": "./locale/*.json"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"index.mjs",
|
|
32
|
+
"lazy-tools.mjs",
|
|
33
|
+
"runtime.mjs",
|
|
34
|
+
"configured-runtime.mjs",
|
|
35
|
+
"server.mjs",
|
|
36
|
+
"client.js",
|
|
37
|
+
"cordis.patch.yml",
|
|
38
|
+
"assets/icon.svg",
|
|
39
|
+
"assets/icon.png",
|
|
40
|
+
"assets/icon-prompt.txt",
|
|
41
|
+
"locale",
|
|
42
|
+
"LICENSE",
|
|
43
|
+
"THIRD_PARTY_NOTICES.md",
|
|
44
|
+
"README.md"
|
|
45
|
+
],
|
|
46
|
+
"dsh": {
|
|
47
|
+
"bundle": {
|
|
48
|
+
"patch": "./cordis.patch.yml"
|
|
49
|
+
},
|
|
50
|
+
"client": {
|
|
51
|
+
"platform": "web",
|
|
52
|
+
"immediately": true,
|
|
53
|
+
"inject": [
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-plugin-manager",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"check": "node --check index.mjs && node --check runtime.mjs && node --check configured-runtime.mjs && node --check server.mjs && node --check client.js && node --check lazy-tools.mjs",
|
|
61
|
+
"test": "node --test test/*.test.mjs",
|
|
62
|
+
"build": "npm run check",
|
|
63
|
+
"pack:check": "npm pack --dry-run --json"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"comfyui-mcp": "0.52.206",
|
|
67
|
+
"@modelcontextprotocol/sdk": "~1.32.1"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"@deepseek-ai/dsh-mcp-client": "0.2.1-rc.3",
|
|
71
|
+
"@deepseek-ai/schemastery": "~3.18.5"
|
|
72
|
+
}
|
|
73
|
+
}
|
package/runtime.mjs
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
-
|
|
6
|
-
export function validateConfig(input = {}) {
|
|
7
|
-
const c = { url: 'http://127.0.0.1:8188', autoStart: false, comfyuiDir: '', pythonPath: '', launchArgs: [], startupTimeoutMs: 120000, ...input };
|
|
8
|
-
const u = new URL(c.url);
|
|
9
|
-
if (!['http:', 'https:'].includes(u.protocol) || u.username || u.password || u.search || u.hash) throw Error('Use an HTTP(S) address without credentials, query or fragment.');
|
|
10
|
-
c.url = u.href.endsWith('/') ? u.href.slice(0, -1) : u.href;
|
|
11
|
-
if (typeof c.autoStart !== 'boolean') throw Error('autoStart must be boolean');
|
|
12
|
-
if (!Array.isArray(c.launchArgs) || c.launchArgs.some(a => typeof a !== 'string')) throw Error('launchArgs must be an array of strings');
|
|
13
|
-
if (c.autoStart) {
|
|
14
|
-
if (!['127.0.0.1', 'localhost', '[::1]'].includes(u.hostname) || u.protocol !== 'http:' || u.pathname !== '/') throw Error('Local startup requires a loopback HTTP origin.');
|
|
15
|
-
if (!path.isAbsolute(c.comfyuiDir) || !path.isAbsolute(c.pythonPath)) throw Error('Local startup requires absolute ComfyUI and Python paths.');
|
|
16
|
-
}
|
|
17
|
-
// argparse accepts long-option abbreviations. Only explicitly supported non-network options are allowed.
|
|
18
|
-
const flags = new Set(['--use-pytorch-cross-attention', '--disable-xformers', '--lowvram', '--novram', '--highvram', '--normalvram', '--cpu', '--force-fp32', '--force-fp16', '--fp16-vae', '--fp32-vae', '--bf16-vae', '--cpu-vae', '--preview-method']);
|
|
19
|
-
for (let i = 0; i < c.launchArgs.length; i++) {
|
|
20
|
-
const a = c.launchArgs[i];
|
|
21
|
-
if (!flags.has(a)) throw Error('Unsupported launch argument: ' + a);
|
|
22
|
-
if (a === '--preview-method' && !['none', 'auto', 'latent2rgb', 'taesd'].includes(c.launchArgs[++i])) throw Error('Invalid preview method');
|
|
23
|
-
}
|
|
24
|
-
if (!Number.isFinite(c.startupTimeoutMs) || c.startupTimeoutMs < 1000 || c.startupTimeoutMs > 600000) throw Error('Startup timeout must be 1000–600000 ms.');
|
|
25
|
-
return c;
|
|
26
|
-
}
|
|
27
|
-
export async function probe(url) {
|
|
28
|
-
try {
|
|
29
|
-
const r = await fetch(url + '/system_stats', { signal: AbortSignal.timeout(2500) });
|
|
30
|
-
if (!r.ok) throw Error('HTTP ' + r.status);
|
|
31
|
-
const body = await r.json();
|
|
32
|
-
if (!body.system && !body.devices) throw Error('Not a ComfyUI system_stats response');
|
|
33
|
-
return { running: true };
|
|
34
|
-
} catch (e) { return { running: false, error: e.message }; }
|
|
35
|
-
}
|
|
36
|
-
export function createRuntime(input, { spawnProcess = spawn, probeAddress = probe } = {}) {
|
|
37
|
-
const c = validateConfig(input);
|
|
38
|
-
let child, starting, closing = false, lastError;
|
|
39
|
-
const owned = () => !!child && child.exitCode === null && child.signalCode === null;
|
|
40
|
-
async function status() { return { ...await probeAddress(c.url), owned: owned(), pid: owned() ? child.pid : undefined, url: c.url, autoStart: c.autoStart, lastError }; }
|
|
41
|
-
async function waitExit(p, ms) {
|
|
42
|
-
if (p.exitCode !== null || p.signalCode !== null) return;
|
|
43
|
-
await new Promise(resolve => {
|
|
44
|
-
const done = () => { clearTimeout(timer); p.removeListener('exit', done); resolve(); };
|
|
45
|
-
const timer = setTimeout(done, ms);
|
|
46
|
-
p.once('exit', done);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
async function terminate() {
|
|
50
|
-
if (!owned()) return { stopped: false, reason: 'No owned process; external instances are never stopped.' };
|
|
51
|
-
const p = child;
|
|
52
|
-
p.kill(); await waitExit(p, 8000);
|
|
53
|
-
if (owned()) { p.kill('SIGKILL'); await waitExit(p, 2000); }
|
|
54
|
-
if (owned()) throw Error('Owned ComfyUI process could not be stopped: ' + p.pid);
|
|
55
|
-
return { stopped: true, pid: p.pid };
|
|
56
|
-
}
|
|
57
|
-
async function start() {
|
|
58
|
-
if (closing) throw Error('Runtime is closing');
|
|
59
|
-
if (starting) return starting;
|
|
60
|
-
starting = (async () => {
|
|
61
|
-
if ((await probeAddress(c.url)).running) return { running: true, started: false, owned: owned() };
|
|
62
|
-
if (!c.autoStart) throw Error('ComfyUI is offline; local on-demand startup is disabled.');
|
|
63
|
-
if (owned()) throw Error('Owned process exists but is not responding.');
|
|
64
|
-
const main = path.join(c.comfyuiDir, 'main.py');
|
|
65
|
-
fs.accessSync(main); fs.accessSync(c.pythonPath);
|
|
66
|
-
if (closing) throw Error('Runtime is closing');
|
|
67
|
-
const u = new URL(c.url);
|
|
68
|
-
child = spawnProcess(c.pythonPath, [main, '--listen', u.hostname === '[::1]' ? '::1' : '127.0.0.1', '--port', u.port || '80', '--disable-auto-launch', '--disable-all-custom-nodes', '--disable-api-nodes', ...c.launchArgs], { cwd: c.comfyuiDir, env: { ...process.env, PYTHONIOENCODING: 'utf-8' }, windowsHide: true, stdio: ['ignore', 'inherit', 'inherit'] });
|
|
69
|
-
let failure;
|
|
70
|
-
child.on('error', e => { failure = e; });
|
|
71
|
-
const end = Date.now() + c.startupTimeoutMs;
|
|
72
|
-
while (Date.now() < end) {
|
|
73
|
-
if (closing) throw Error('Runtime is closing');
|
|
74
|
-
if (failure) throw failure;
|
|
75
|
-
if (!owned()) throw Error('ComfyUI exited before becoming ready; inspect DSH stderr logs.');
|
|
76
|
-
if ((await probeAddress(c.url)).running) { lastError = undefined; return { running: true, started: true, owned: true, pid: child.pid }; }
|
|
77
|
-
await delay(500);
|
|
78
|
-
}
|
|
79
|
-
throw Error('ComfyUI startup timed out; inspect DSH stderr logs.');
|
|
80
|
-
})().catch(async e => { lastError = e.message; await terminate(); throw e; }).finally(() => { starting = undefined; });
|
|
81
|
-
return starting;
|
|
82
|
-
}
|
|
83
|
-
async function stop() { closing = true; try { await starting?.catch(() => {}); return await terminate(); } finally { closing = false; } }
|
|
84
|
-
async function close() { closing = true; await starting?.catch(() => {}); return terminate(); }
|
|
85
|
-
async function free() {
|
|
86
|
-
if (!(await status()).running) return { freed: false };
|
|
87
|
-
const r = await fetch(c.url + '/free', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ unload_models: true, free_memory: true }), signal: AbortSignal.timeout(10000) });
|
|
88
|
-
if (!r.ok) throw Error('Free failed: HTTP ' + r.status);
|
|
89
|
-
return { freed: true };
|
|
90
|
-
}
|
|
91
|
-
return { status, start, stop, close, free, config: c };
|
|
92
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
+
|
|
6
|
+
export function validateConfig(input = {}) {
|
|
7
|
+
const c = { url: 'http://127.0.0.1:8188', autoStart: false, comfyuiDir: '', pythonPath: '', launchArgs: [], startupTimeoutMs: 120000, ...input };
|
|
8
|
+
const u = new URL(c.url);
|
|
9
|
+
if (!['http:', 'https:'].includes(u.protocol) || u.username || u.password || u.search || u.hash) throw Error('Use an HTTP(S) address without credentials, query or fragment.');
|
|
10
|
+
c.url = u.href.endsWith('/') ? u.href.slice(0, -1) : u.href;
|
|
11
|
+
if (typeof c.autoStart !== 'boolean') throw Error('autoStart must be boolean');
|
|
12
|
+
if (!Array.isArray(c.launchArgs) || c.launchArgs.some(a => typeof a !== 'string')) throw Error('launchArgs must be an array of strings');
|
|
13
|
+
if (c.autoStart) {
|
|
14
|
+
if (!['127.0.0.1', 'localhost', '[::1]'].includes(u.hostname) || u.protocol !== 'http:' || u.pathname !== '/') throw Error('Local startup requires a loopback HTTP origin.');
|
|
15
|
+
if (!path.isAbsolute(c.comfyuiDir) || !path.isAbsolute(c.pythonPath)) throw Error('Local startup requires absolute ComfyUI and Python paths.');
|
|
16
|
+
}
|
|
17
|
+
// argparse accepts long-option abbreviations. Only explicitly supported non-network options are allowed.
|
|
18
|
+
const flags = new Set(['--use-pytorch-cross-attention', '--disable-xformers', '--lowvram', '--novram', '--highvram', '--normalvram', '--cpu', '--force-fp32', '--force-fp16', '--fp16-vae', '--fp32-vae', '--bf16-vae', '--cpu-vae', '--preview-method']);
|
|
19
|
+
for (let i = 0; i < c.launchArgs.length; i++) {
|
|
20
|
+
const a = c.launchArgs[i];
|
|
21
|
+
if (!flags.has(a)) throw Error('Unsupported launch argument: ' + a);
|
|
22
|
+
if (a === '--preview-method' && !['none', 'auto', 'latent2rgb', 'taesd'].includes(c.launchArgs[++i])) throw Error('Invalid preview method');
|
|
23
|
+
}
|
|
24
|
+
if (!Number.isFinite(c.startupTimeoutMs) || c.startupTimeoutMs < 1000 || c.startupTimeoutMs > 600000) throw Error('Startup timeout must be 1000–600000 ms.');
|
|
25
|
+
return c;
|
|
26
|
+
}
|
|
27
|
+
export async function probe(url) {
|
|
28
|
+
try {
|
|
29
|
+
const r = await fetch(url + '/system_stats', { signal: AbortSignal.timeout(2500) });
|
|
30
|
+
if (!r.ok) throw Error('HTTP ' + r.status);
|
|
31
|
+
const body = await r.json();
|
|
32
|
+
if (!body.system && !body.devices) throw Error('Not a ComfyUI system_stats response');
|
|
33
|
+
return { running: true };
|
|
34
|
+
} catch (e) { return { running: false, error: e.message }; }
|
|
35
|
+
}
|
|
36
|
+
export function createRuntime(input, { spawnProcess = spawn, probeAddress = probe } = {}) {
|
|
37
|
+
const c = validateConfig(input);
|
|
38
|
+
let child, starting, closing = false, lastError;
|
|
39
|
+
const owned = () => !!child && child.exitCode === null && child.signalCode === null;
|
|
40
|
+
async function status() { return { ...await probeAddress(c.url), owned: owned(), pid: owned() ? child.pid : undefined, url: c.url, autoStart: c.autoStart, lastError }; }
|
|
41
|
+
async function waitExit(p, ms) {
|
|
42
|
+
if (p.exitCode !== null || p.signalCode !== null) return;
|
|
43
|
+
await new Promise(resolve => {
|
|
44
|
+
const done = () => { clearTimeout(timer); p.removeListener('exit', done); resolve(); };
|
|
45
|
+
const timer = setTimeout(done, ms);
|
|
46
|
+
p.once('exit', done);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async function terminate() {
|
|
50
|
+
if (!owned()) return { stopped: false, reason: 'No owned process; external instances are never stopped.' };
|
|
51
|
+
const p = child;
|
|
52
|
+
p.kill(); await waitExit(p, 8000);
|
|
53
|
+
if (owned()) { p.kill('SIGKILL'); await waitExit(p, 2000); }
|
|
54
|
+
if (owned()) throw Error('Owned ComfyUI process could not be stopped: ' + p.pid);
|
|
55
|
+
return { stopped: true, pid: p.pid };
|
|
56
|
+
}
|
|
57
|
+
async function start() {
|
|
58
|
+
if (closing) throw Error('Runtime is closing');
|
|
59
|
+
if (starting) return starting;
|
|
60
|
+
starting = (async () => {
|
|
61
|
+
if ((await probeAddress(c.url)).running) return { running: true, started: false, owned: owned() };
|
|
62
|
+
if (!c.autoStart) throw Error('ComfyUI is offline; local on-demand startup is disabled.');
|
|
63
|
+
if (owned()) throw Error('Owned process exists but is not responding.');
|
|
64
|
+
const main = path.join(c.comfyuiDir, 'main.py');
|
|
65
|
+
fs.accessSync(main); fs.accessSync(c.pythonPath);
|
|
66
|
+
if (closing) throw Error('Runtime is closing');
|
|
67
|
+
const u = new URL(c.url);
|
|
68
|
+
child = spawnProcess(c.pythonPath, [main, '--listen', u.hostname === '[::1]' ? '::1' : '127.0.0.1', '--port', u.port || '80', '--disable-auto-launch', '--disable-all-custom-nodes', '--disable-api-nodes', ...c.launchArgs], { cwd: c.comfyuiDir, env: { ...process.env, PYTHONIOENCODING: 'utf-8' }, windowsHide: true, stdio: ['ignore', 'inherit', 'inherit'] });
|
|
69
|
+
let failure;
|
|
70
|
+
child.on('error', e => { failure = e; });
|
|
71
|
+
const end = Date.now() + c.startupTimeoutMs;
|
|
72
|
+
while (Date.now() < end) {
|
|
73
|
+
if (closing) throw Error('Runtime is closing');
|
|
74
|
+
if (failure) throw failure;
|
|
75
|
+
if (!owned()) throw Error('ComfyUI exited before becoming ready; inspect DSH stderr logs.');
|
|
76
|
+
if ((await probeAddress(c.url)).running) { lastError = undefined; return { running: true, started: true, owned: true, pid: child.pid }; }
|
|
77
|
+
await delay(500);
|
|
78
|
+
}
|
|
79
|
+
throw Error('ComfyUI startup timed out; inspect DSH stderr logs.');
|
|
80
|
+
})().catch(async e => { lastError = e.message; await terminate(); throw e; }).finally(() => { starting = undefined; });
|
|
81
|
+
return starting;
|
|
82
|
+
}
|
|
83
|
+
async function stop() { closing = true; try { await starting?.catch(() => {}); return await terminate(); } finally { closing = false; } }
|
|
84
|
+
async function close() { closing = true; await starting?.catch(() => {}); return terminate(); }
|
|
85
|
+
async function free() {
|
|
86
|
+
if (!(await status()).running) return { freed: false };
|
|
87
|
+
const r = await fetch(c.url + '/free', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ unload_models: true, free_memory: true }), signal: AbortSignal.timeout(10000) });
|
|
88
|
+
if (!r.ok) throw Error('Free failed: HTTP ' + r.status);
|
|
89
|
+
return { freed: true };
|
|
90
|
+
}
|
|
91
|
+
return { status, start, stop, close, free, config: c };
|
|
92
|
+
}
|