dsh-comfyui-gateway 0.0.0-stage → 0.4.7
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/LICENSE +28 -0
- package/README.md +366 -2
- package/client.js +1519 -0
- package/cordis.patch.yml +4 -0
- package/icon.svg +9 -0
- package/index.js +467 -0
- package/lib/assets.js +123 -0
- package/lib/auth.js +295 -0
- package/lib/comfy.js +191 -0
- package/lib/config.js +180 -0
- package/lib/gateway.js +572 -0
- package/lib/http.js +42 -0
- package/lib/imagesize.js +92 -0
- package/lib/instances.js +415 -0
- package/lib/library.js +314 -0
- package/lib/pages.js +1373 -0
- package/lib/pool.js +591 -0
- package/lib/promptassist.js +402 -0
- package/lib/remote.js +1141 -0
- package/lib/runindex.js +115 -0
- package/lib/tagdata.json +1 -0
- package/lib/tiers.js +170 -0
- package/lib/tools.js +162 -0
- package/lib/viewport.js +76 -0
- package/lib/workflows.js +220 -0
- package/lib/ws.js +106 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +56 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-comfyui-gateway contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This package is an independent implementation. It does not contain code from
|
|
26
|
+
`dsh-comfyui` or `dsh-comfyui-pool`; it speaks ComfyUI's public HTTP protocol
|
|
27
|
+
and reads the on-disk layout that `dsh-knowledge-wiki` publishes for its
|
|
28
|
+
libraries. Those projects are separate works under their own licenses.
|
package/README.md
CHANGED
|
@@ -1,3 +1,367 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-comfyui-gateway
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个 DSH 插件,解决「一张显卡一个 ComfyUI 实例,调用方要自己挑端口」的问题:
|
|
4
|
+
所有机器只把 JSON 发到 **8187**,由它排队、挑端口、转发,并把 `/queue`、`/history`、
|
|
5
|
+
`/system_stats`、`/view` 合并成一份返回。另有一个 **8186 远程控制台**:账号密码 + 桌面端
|
|
6
|
+
显示的 6 位动态配对码登录,可挑工作流、填提示词、传图片、看任务池与历史,还能把某个实例
|
|
7
|
+
以画中画方式借出来用(期间该实例暂停自动派发)。
|
|
8
|
+
|
|
9
|
+
## 两个端口
|
|
10
|
+
|
|
11
|
+
| 端口 | 谁在说 | 说什么 |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| **8187** 网关 | 别的电脑的脚本 / ComfyUI 前端 / 另一个 ComfyUI 节点 | 就是 ComfyUI 自己的协议:`/prompt`、`/queue`、`/history`、`/system_stats`、`/object_info`、`/view`、`/ws` |
|
|
14
|
+
| **8186** 远程控制台 | 人(另一台电脑或手机浏览器) | 登录、工作流、提示词、上传、任务池、历史、画布桥接 |
|
|
15
|
+
|
|
16
|
+
两者分开,是因为网关要保持「纯 API」,而控制台要一套登录与 HTML。混在一个端口上,
|
|
17
|
+
每个 API 调用方都得先过 HTML 401 流程。
|
|
18
|
+
|
|
19
|
+
## 8187:调用方只要改一个数字
|
|
20
|
+
|
|
21
|
+
原来各设备自己决定发 8188 还是 8189:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 原来
|
|
25
|
+
curl -X POST http://192.168.3.253:8188/prompt -d '{...}'
|
|
26
|
+
|
|
27
|
+
# 现在:只改端口,别的都不变
|
|
28
|
+
curl -X POST http://192.168.3.253:8187/prompt \
|
|
29
|
+
-H 'content-type: application/json' \
|
|
30
|
+
-d '{"prompt": {...}, "client_id": "my-box"}'
|
|
31
|
+
# → {"prompt_id": "…", "number": 12, "node_errors": {}}
|
|
32
|
+
|
|
33
|
+
curl http://192.168.3.253:8187/history/<prompt_id> # 轮询结果
|
|
34
|
+
curl http://192.168.3.253:8187/queue # 合并队列
|
|
35
|
+
curl http://192.168.3.253:8187/system_stats # 两张卡的显存
|
|
36
|
+
curl http://192.168.3.253:8187/object_info # 节点表(取自主实例)
|
|
37
|
+
curl "http://192.168.3.253:8187/view?filename=a.png&subfolder=&type=output"
|
|
38
|
+
ws://192.168.3.253:8187/ws # status / executing / progress
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
关键点:**`prompt_id` 由网关签发,永远有效**。上游实例的那个 id 只在网关内部使用,
|
|
42
|
+
`/history/<id>`、`/view?prompt_id=<id>` 都由网关翻译。调用方不需要知道任务落在哪张卡。
|
|
43
|
+
|
|
44
|
+
没列出来的路径一律转发给主实例,所以 `/embeddings`、`/features`、以后新增的接口照样能用。
|
|
45
|
+
|
|
46
|
+
## 两种排队方式
|
|
47
|
+
|
|
48
|
+
- **先后顺序**(关掉动态插队):严格按到达时间;显式 `priority` 仍可插队。
|
|
49
|
+
- **动态插队**(默认开):每个任务按图里的 `steps × 分辨率 × batch`(CFG>1 再乘 2,因为
|
|
50
|
+
CFG 会双跑 UNet)估一个「代价」;每个实例用 EWMA 学习「秒/代价」;派发时把最贵的任务
|
|
51
|
+
交给预计最早跑完的实例(LPT + 机器异构时间模型)。目标是总完成时间最短、显卡不空转。
|
|
52
|
+
面板/远程页的「插队」「上移/下移」是硬优先级,任何模式下都生效。
|
|
53
|
+
|
|
54
|
+
## 审查边界:API 不审查,只有控制台审查
|
|
55
|
+
|
|
56
|
+
这条是硬约定,不是默认行为:
|
|
57
|
+
|
|
58
|
+
| 入口 | 审查 | 为什么 |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| **8187** `/prompt`(脚本、别的 ComfyUI 节点、本插件的 agent 工具) | **不审查** | 它是 ComfyUI 兼容端点,调用方是程序不是人。发什么就执行什么,不扫描、不改写、不加表头、不拦 |
|
|
61
|
+
| **8186** 远程控制台(登录后的页面) | 审查 | 只有这里有账号,所以只有这里有分级闸门 |
|
|
62
|
+
|
|
63
|
+
`lib/gateway.js` 与 `lib/pool.js` **不 import `tiers.js`**,也不含 `nsfw` / `sfw` / `adult` 任何一个词;
|
|
64
|
+
所有闸门都在 `lib/remote.js`。这条边界由 `test/boundary.mjs` 固定:
|
|
65
|
+
往 `enqueue()` 或 `/prompt` 里加过滤会让它失败。
|
|
66
|
+
|
|
67
|
+
实测(真 8187 → 真 8188):
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
POST /prompt -> 200 {"prompt_id":"acbf1900-…"}
|
|
71
|
+
text reached the backend unchanged: true | nude, on_back, bedroom, soft light, 1girl
|
|
72
|
+
state done | backend live | error null
|
|
73
|
+
history files boundary_live_00001_.png
|
|
74
|
+
view 200 233341 png true
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
也就是说:API 提交的成人任务照常排队、照常出图;控制台的 SFW 账号既看不到它,
|
|
78
|
+
也提交不了同类内容——两条路各自独立。
|
|
79
|
+
|
|
80
|
+
## 8186:远程控制台
|
|
81
|
+
|
|
82
|
+
面板上可**开 / 关**,端口也能改(和网关端口一起保存,改完点「重启网关」)。关掉时该端口真的不再监听。
|
|
83
|
+
|
|
84
|
+
### 登录
|
|
85
|
+
|
|
86
|
+
默认要账号密码 **+** 桌面端「任务池」页显示的 6 位动态配对码;面板可**关掉配对码**,关掉后登录页
|
|
87
|
+
连配对码输入框都不显示(`/api/pairing-status` 里 `required:false`)。
|
|
88
|
+
|
|
89
|
+
- 密码存 `scrypt(password, salt)`,明文不出请求;
|
|
90
|
+
- 配对码由本机密钥 + 60 秒时间步 HMAC 派生,**从不落盘**;
|
|
91
|
+
- 会话是 HttpOnly Cookie,30 分钟空闲 / 12 小时上限;
|
|
92
|
+
- 同一账号 10 分钟失败 8 次即锁定窗口内重试。
|
|
93
|
+
|
|
94
|
+
### 外网访问(反向代理)
|
|
95
|
+
|
|
96
|
+
控制台自己不做 TLS,也没有域名白名单——它就是普通 HTTP。要给人从外网用,前面必须有一层
|
|
97
|
+
HTTPS 反代(群晖反向代理、nginx、Caddy、cloudflared 都行),把 `https://你的域名` 转到
|
|
98
|
+
`http://192.168.3.253:8186`。
|
|
99
|
+
|
|
100
|
+
**三条都成立才算配通**(这三条是实测过的,不是清单堆料):
|
|
101
|
+
|
|
102
|
+
1. `X-Forwarded-*` 与 `Host`:**保留原始 Host 或改写都行**。实测把 Host 改成
|
|
103
|
+
`127.0.0.1:8186` 之后登录、取页面、取 `/api/state` 全部照常——控制台不比对 Origin/Host,
|
|
104
|
+
所以不会被常见的「反代默认改写 Host」卡住。但**必须转发 Host**,不能删掉。
|
|
105
|
+
2. **不要挂子路径**。页面里的跳转是绝对路径(`/app`、`/`),挂到 `https://域名/comfy/`
|
|
106
|
+
会 404。要单独一个域名或子域名,根路径直达。
|
|
107
|
+
3. **WebSocket 要放行**:画布桥接走 `/bridge/<token>/` 的 upgrade。nginx 需要
|
|
108
|
+
`proxy_http_version 1.1` + `Upgrade $http_upgrade` + `Connection $connection_upgrade`。
|
|
109
|
+
没放行时控制台其余功能正常,只有「打开画布」转圈。
|
|
110
|
+
|
|
111
|
+
上传单文件上限 48 MB(`MAX_UPLOAD_BYTES`),nginx 默认 `client_max_body_size 1m`
|
|
112
|
+
会让稍大的参考图直接 413;把它调到 64m 或以上。
|
|
113
|
+
|
|
114
|
+
会话 Cookie 是 `HttpOnly; SameSite=Lax`,**没有 Secure**:所以 `https` 与 `http` 下都能登录,
|
|
115
|
+
不需要额外开关。代价是同一域名下若还有别的 HTTP 服务,Cookie 不会被浏览器按 Secure 隔离——
|
|
116
|
+
这是内网自用工具的取舍。
|
|
117
|
+
|
|
118
|
+
登录仍然要过两道:账号密码 + 桌面「任务池」页那 6 位配对码。**外网就靠这个**,
|
|
119
|
+
所以配对码保持开启;不想让别人看到配对码,就别把「任务池」页面共享出去。
|
|
120
|
+
|
|
121
|
+
### 界面
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
┌ 顶栏:任务池 · 账号(等级·角色) · 网关 ── 刷新 · 管理工作台 · 退出登录 ┐
|
|
125
|
+
├─────────────────────────────┬────────────────────────────┤
|
|
126
|
+
│ 工作流(点选) │ 队列(状态 + 插队) │
|
|
127
|
+
│ 提示词 正向/负向(含表头) │ 历史记录(可收起) │
|
|
128
|
+
│ 图片 拖入/上传/资产库 │ 资产库(缩略图 + 像素尺寸) │
|
|
129
|
+
│ 主要参数 steps/cfg/尺寸/种子 │ 显卡(显存、在线) │
|
|
130
|
+
│ [生成并加入队列] │ │
|
|
131
|
+
└─────────────────────────────┴────────────────────────────┘
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- **工作流是下拉框**,选中即加载;管理员会多一个「编辑」按钮,可改名字与正负表头
|
|
135
|
+
(保存时把原 graph 原样回传,改这些不会碰节点)。
|
|
136
|
+
- 选工作流之后,下面三块由它的属性决定:没有文本节点就没有提示词框,没有图片输入就没有上传入口,
|
|
137
|
+
没有采样参数就没有参数面板。
|
|
138
|
+
- 「自然语言转提示词」就是一个大输入框,和提示词框在同一个卡片里:描述 → 转换 → 结果直接写进
|
|
139
|
+
正向/负向的自定义框,表头不动。
|
|
140
|
+
- 右栏只有队列、历史、资产库,不显示显卡;队列每行已写明任务落在哪个实例。
|
|
141
|
+
- 图片可以上传,也可以直接从资产库挑一张当输入。
|
|
142
|
+
- 队列每行有「插队」「取消」;历史可收起;资产库点开是大图,支持 1:1、左右翻页、下载。
|
|
143
|
+
- 资产库的每个瓦片显示像素尺寸,取不到缩略图时显示占位而不是破格子。
|
|
144
|
+
|
|
145
|
+
### 手机端
|
|
146
|
+
|
|
147
|
+
两处判断,顺序是刻意的:服务端看 User-Agent 把结果写进 `<html data-layout>`,首屏就是对的;
|
|
148
|
+
页面脚本再看真实视口(`max-width: 720px`)纠正一次——桌面窗口缩窄、平板分屏、"请求桌面版"的手机
|
|
149
|
+
都和自己的 UA 不一致。两套布局跑同一份脚本,只有摆放和密度不同。
|
|
150
|
+
|
|
151
|
+
- 单列 + 底部标签栏(生成 / 队列 / 历史 / 图库);
|
|
152
|
+
- 顶栏一行放完,按钮缩到 12px;
|
|
153
|
+
- 输入框 16px(再小 iOS 聚焦会放大整页),触摸目标 ≥ 40px;
|
|
154
|
+
- 参数两列;管理台的数字列隐藏,避免横向滚动;登录卡片铺满宽度;
|
|
155
|
+
- 底部留 `env(safe-area-inset-bottom)`。
|
|
156
|
+
|
|
157
|
+
### 管理员台(`/admin`)
|
|
158
|
+
|
|
159
|
+
只对管理员开放(非管理员 302 回控制台,「管理工作台」按钮也不显示):
|
|
160
|
+
|
|
161
|
+
- 我的工作流:列表、删除;
|
|
162
|
+
- 新建工作流:粘贴画布 JSON,填正负表头;
|
|
163
|
+
- 桥接实例:把某个实例的画布打开(期间它退出自动派发),用完交还;
|
|
164
|
+
- 表头模板:新建工作流的默认表头;
|
|
165
|
+
- 退出登录。
|
|
166
|
+
|
|
167
|
+
### 每个账号只看得见自己的东西### 每个账号只看得见自己的东西
|
|
168
|
+
|
|
169
|
+
这是这一版的核心变化:控制台是**按账号**的视图,不是共享视图。
|
|
170
|
+
|
|
171
|
+
| 资源 | 规则 |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| 工作流 | 只列自己创建的;读/删别人的 id 返回 **404**(与不存在同形,避免枚举) |
|
|
174
|
+
| 任务池 | 只显示自己提交的任务 |
|
|
175
|
+
| 历史 / 资产 | 只读自己的运行记录;文件按「自己索引里出现过」放行 |
|
|
176
|
+
| 媒体 | 猜到别人的输出名也是 404 |
|
|
177
|
+
|
|
178
|
+
历史**跨退出、跨重启**保留:提交任务时就把记录写进 `runs.json`,完成后补上文件名,
|
|
179
|
+
所以关掉浏览器、退出登录、甚至 DSH 重启之后,重新登录仍能看到之前的图和运行记录。
|
|
180
|
+
|
|
181
|
+
### 等级:SFW / NSFW
|
|
182
|
+
|
|
183
|
+
账号带等级,面板里加账号时选、之后随时改。
|
|
184
|
+
|
|
185
|
+
- SFW 账号:看不到成人工作流(按 名字 + 标签 + 图里的提示词 判定),提交的提示词里出现成人词会被
|
|
186
|
+
拦下并告诉命中了哪个词,**创建不了** `declared: nsfw` 的工作流;
|
|
187
|
+
- NSFW 账号:两者都能用;等级是**上限**,不是偏好。
|
|
188
|
+
|
|
189
|
+
因为账号隔离在先(每个人只看自己的),资产列表不需要再按等级分——SFW 账号本来就不可能创建成人内容。
|
|
190
|
+
注意这只约束控制台:8187 的 API 不做审查(见上一节),所以别人的脚本仍可提交成人任务,只是它不会出现在任何账号的历史里。
|
|
191
|
+
等级只作为一道兜底:管理员把某账号从 NSFW 降为 SFW 时,它旧的成人工作流不会重新出现在选择器里。
|
|
192
|
+
|
|
193
|
+
### 生成:表头属于工作流
|
|
194
|
+
|
|
195
|
+
表头(正向 / 负向)是**工作流的属性**,不是全局设置:
|
|
196
|
+
|
|
197
|
+
- **创建/编辑工作流时配置**——远程页「新建工作流」对话框里有正向、负向两个表头框;
|
|
198
|
+
选中已有工作流时也能就地改,点「保存本工作流的表头」写回该工作流;
|
|
199
|
+
- **选择工作流后自动带出**——切换工作流,表头跟着换。文生图想要 Pony 质量词、纯放大或换脸
|
|
200
|
+
一个都不想要,全局一套表头迟早会喂给一个读文件名的节点;
|
|
201
|
+
- **只有需要文字输入的工作流才显示提示词区**——没有文本节点的图(纯放大、换脸、插帧)
|
|
202
|
+
不显示提示词框,也不显示「自然语言转提示词」按钮,因为填了没人读;
|
|
203
|
+
- 运行时是「表头 + 自定义」两个框:表头来自工作流,自定义是这一张图的内容,提交时按
|
|
204
|
+
`表头, 自定义` 拼接;页面上临时改过的表头优先。
|
|
205
|
+
|
|
206
|
+
面板里那两个框现在只是**新建工作流的默认值**,新工作流会继承它,之后各自独立。
|
|
207
|
+
|
|
208
|
+
### 自然语言 → 提示词
|
|
209
|
+
|
|
210
|
+
生成页的「自然语言转提示词」按钮打开一个对话框:
|
|
211
|
+
|
|
212
|
+
- 用一句话说需求("短发女生穿白衬衫站在雨夜便利店门口"),助手返回正向/负向两段标签;
|
|
213
|
+
- 接 Harness 的 `llm` 服务和当前默认模型;
|
|
214
|
+
- **参考知识库**:下拉里选 `dsh-knowledge-wiki` 的任意一个库(默认「提示词」),助手会读它的
|
|
215
|
+
正文摘录一起送进模型;选「不使用」就完全不读。回复里会写明「知识库『提示词』3 篇」,
|
|
216
|
+
所以你能分清答案是查过库还是纯靠模型;
|
|
217
|
+
- 没挂模型 / 调用失败时退化成**词库检索**(确定性,仍然可用),并同样报告用了哪个库;
|
|
218
|
+
- 支持多轮("再暗一点""换成金发"),对话保留上下文;
|
|
219
|
+
- 返回的候选标签可点选追加;「填入两个自定义框」只写自定义框,表头保持不动。
|
|
220
|
+
|
|
221
|
+
SFW 账号的成人需求在打模型之前就被拦下;模型若仍答出成人标签,SFW 账号收到的答案会把这些标签剔掉。
|
|
222
|
+
|
|
223
|
+
知识库插件暴露的是**工具**(`kb_query`/`kb_read`)而不是 service,所以这里是直接读它的磁盘布局:
|
|
224
|
+
`~/.dsh/knowledge/<库名>/markdown/<docId>/<docId>.md` + 同目录 `kb.json`。路径可在面板改(`kbName` / `root`),
|
|
225
|
+
另外保留一个「备用目录」给还没导入知识库的纯 Markdown 文件。
|
|
226
|
+
|
|
227
|
+
### 工作流:选、建、改
|
|
228
|
+
|
|
229
|
+
工作流库在插件数据目录(`workflows.json`),每人一份自己的。建/改:粘贴 ComfyUI 画布导出的 JSON
|
|
230
|
+
(UI 图)或直接 API 格式,UI 图会按实例的 `/object_info` 转换成 API 格式:
|
|
231
|
+
|
|
232
|
+
- 连线优先于控件值;
|
|
233
|
+
- 控件值按 `required`/`optional` 声明顺序落位,种子行跳过跟随的 `control_after_generate`;
|
|
234
|
+
- `Note`/`MarkdownNote`、静音/旁路节点丢弃;
|
|
235
|
+
- 实例里没有的节点会**报出来**,不会硬塞到生成时才神秘失败。
|
|
236
|
+
|
|
237
|
+
### 批量导入工作流
|
|
238
|
+
|
|
239
|
+
一批 API 格式的 JSON(`{节点id: {class_type, inputs}}`)要进库时,不必一个个粘贴:直接调
|
|
240
|
+
`WorkflowLibrary.put()` 就是 `POST /api/workflows` 跑的同一段代码,会一并算出参数槽位、
|
|
241
|
+
归属与等级。
|
|
242
|
+
|
|
243
|
+
两点在实现里踩过,写在这里免得再踩:
|
|
244
|
+
|
|
245
|
+
- **API 格式不再被当成 UI 图去转换。** 转换器需要实例的 `/object_info`;批量导入时没有,
|
|
246
|
+
旧代码于是留下 `api = null`,连带 `targets` 与 `nodeCount` 都是空——控制台在文生图工作流上
|
|
247
|
+
连提示词框都不显示。现在先判"这已经是 API 格式",直接当 prompt 用。
|
|
248
|
+
- **库在运行时重读。** `workflows.json` 被外部改写后,控制台会在下一次
|
|
249
|
+
`/api/workflows` 时按 `mtime+size` 判定并重读,不需要重启宿主。
|
|
250
|
+
在此之前只有重启整个 DSH 才看得到新导入的条目——「重启网关」只重启监听器,不重读状态。
|
|
251
|
+
|
|
252
|
+
导入的条目默认 `heads` 为空:这些图是文档的原样转换,擅自加质量词会改变每个工作流本来的行为。
|
|
253
|
+
|
|
254
|
+
## 桥接(画中画借用实例)
|
|
255
|
+
|
|
256
|
+
远程页点某个实例「打开画布」:
|
|
257
|
+
|
|
258
|
+
1. 给这个实例加一把**租约锁**,调度器立刻跳过它(一个人在手动画画布、同时调度器往同一张卡
|
|
259
|
+
塞任务,是显存爆掉、跑到一半失败的经典原因);
|
|
260
|
+
2. 通过 8186 的 `/bridge/<token>/` 反代该实例:页面 HTML 会被注入一段 shim,把
|
|
261
|
+
`fetch` / `WebSocket` / `EventSource` / `img.src` / `<base>` 全部改写到桥接前缀下,
|
|
262
|
+
所以画布在**同一个 8186 页面里**(iframe 画中画)完整可用,远程浏览器不需要直连 8188;
|
|
263
|
+
3. 关闭、退出登录、或 30 分钟无操作 → 自动释放,实例回到派发池。
|
|
264
|
+
|
|
265
|
+
升级后的画布会通过 WebSocket 直连实例,桥接同样支持 upgrade(字节隧道)。
|
|
266
|
+
|
|
267
|
+
## 实例管理(含本机启停)
|
|
268
|
+
|
|
269
|
+
面板「实例」卡片能绑定任意 ComfyUI 地址。要让启动/停止真的作用于进程,把该实例的
|
|
270
|
+
**托管方式**设为 `启动 bat 文件`,然后填那个 `.bat` 的完整路径:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
E:\ComfyUI-aki-v3.2\启动.bat
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**只有 bat,没有命令行,也没有 ssh。** 三种方式里只留有意义的两种:
|
|
277
|
+
|
|
278
|
+
| 托管方式 | 含义 |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| `启动 bat 文件` | 网关执行这个 `.bat`;停止时连同它的子进程一起杀掉 |
|
|
281
|
+
| `不托管` | 只派发,不给启停按钮(进程是别人的) |
|
|
282
|
+
|
|
283
|
+
为什么是文件而不是命令行:启动 ComfyUI 通常要先 `cd`、激活虚拟环境、带上端口参数,
|
|
284
|
+
这些写在 bat 里最清楚,而且双击就能自己验证一次。让面板里再维护一份命令字符串,
|
|
285
|
+
等于同一个启动逻辑存两处,改了一处就会对不上。
|
|
286
|
+
|
|
287
|
+
- 启动:`cmd /d /s /c ""<bat>""` → 轮询 `/system_stats` 直到就绪(超时可配,默认 5 分钟)。
|
|
288
|
+
用 `windowsVerbatimArguments` 传参,否则 Node 会把引号转义成字面量 `\"`,
|
|
289
|
+
cmd 就会把它当成程序名去执行(`'\"C:\...bat\"'` 不是内部或外部命令);
|
|
290
|
+
- 工作目录默认取 bat 所在目录,所以 bat 不必自己 `cd`;也可以单独填;
|
|
291
|
+
- 停止:Windows 上先 `taskkill /T /F` 杀进程树再结束子进程(bat 包装器会留下孙进程,
|
|
292
|
+
只杀直接子进程的话显存不会释放——这一点在测试里踩过),然后轮询端口确认不再响应;
|
|
293
|
+
- 插件卸载 / DSH 退出时杀掉本插件起的所有进程,不留孤儿占显存。
|
|
294
|
+
|
|
295
|
+
**旧配置迁移**:以前的 `spawn + 命令行` 与 `ssh` 已停用。迁移规则保守——只有当一个旧
|
|
296
|
+
`command` 本身就是 `.bat` / `.cmd` 路径时才转成 `启动 bat 文件`;其余(比如
|
|
297
|
+
`python main.py --port 8190`、以及所有 ssh 行)一律退成 `不托管`。理由很直接:
|
|
298
|
+
把一条命令行当文件路径去执行,或者假装能用本地 bat 启动远端主机,都会让「启动」按钮骗人。
|
|
299
|
+
|
|
300
|
+
## 智能体工具 `comfyui_gateway`
|
|
301
|
+
|
|
302
|
+
`status`(默认,含每卡显存/速度/是否托管)、`dynamic`、`start`/`stop`/`restart`、
|
|
303
|
+
`probe`、`add`/`update`/`remove`、`prioritize`、`cancel`、`free`。
|
|
304
|
+
|
|
305
|
+
## 配置与数据
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
<DSH_HOME>/data/dsh-comfyui-gateway/
|
|
309
|
+
├─ settings.json 实例、两个端口、远程开关、配对码开关、账号(scrypt + 等级)、
|
|
310
|
+
│ 会话、配对密钥、新建工作流的默认表头、参考知识库名
|
|
311
|
+
├─ workflows.json 工作流库(每份带 owner 和**自己的**表头;graph + api + 识别出的输入位)
|
|
312
|
+
└─ runs.json 每个账号的运行记录(promptId / 名称 / 状态 / 文件名)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
面板改完立即生效并落盘;只有**网关端口 / 远程端口 / 远程开关**变化需要点「重启网关」。
|
|
316
|
+
|
|
317
|
+
**面板分组按「谁在用这个端口」划**:远程端口(8186,浏览器看的那套)的输入框在「远程控制」卡里,
|
|
318
|
+
网关端口(8187,脚本调的那套)的在「网关」卡里。两个端口共用一套校验(1–65535、互不相同),
|
|
319
|
+
但各存各的——只发自己那一半,另一个字段原样不动。
|
|
320
|
+
|
|
321
|
+
**实例端口就地可改**:每张实例卡右上角有「改端口」,点开直接填端口号(`8290`)或完整地址
|
|
322
|
+
(`http://192.168.3.253:8290`)。只填数字时保留该实例原主机名,只换端口——这是"改了端口"的本意。
|
|
323
|
+
`runs.json` 是「退出再登录还能看到历史和资产」的基础,写入已串行化(并发 rename 会写坏索引)。
|
|
324
|
+
|
|
325
|
+
**旧数据迁移**:启动时跑两条幂等迁移——没有等级的账号(管理员→NSFW、其他→SFW)、
|
|
326
|
+
没有 owner 的工作流归给第一个账号。不做的话旧库在新规则下会「什么都看不见」。
|
|
327
|
+
|
|
328
|
+
## 测试
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
node test/all.mjs # 15 套件 469 项
|
|
332
|
+
node test/live-isolation.mjs # 真机:账号隔离、表头、助手分级、重启后仍能取图
|
|
333
|
+
node test/live-boundary.mjs # 真机:成人提示词走 8187 API 直接出图(不审查)
|
|
334
|
+
node test/live-heads.mjs # 真机:表头随工作流、无文字输入的图不被污染
|
|
335
|
+
node test/live-kb.mjs # 真机:助手真的读到「提示词」库(19 篇)
|
|
336
|
+
node test/live.mjs # 真机:网关协议与出图
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
| 套件 | 覆盖 |
|
|
340
|
+
| --- | --- |
|
|
341
|
+
| `cost` / `settings` / `ordering` | 代价模型、设置合并、FIFO 与动态 LPT、优先级、并发上限 |
|
|
342
|
+
| `gateway` | 真 HTTP 网关:/prompt /queue /history /view /system_stats /object_info /ws |
|
|
343
|
+
| `lifecycle` / `resilience` | 真子进程启停:真 .bat 经 cmd.exe 启动、非 bat 被拒、taskkill 杀树、死实例故障转移、任务消失重排队 |
|
|
344
|
+
| `remote` | 控制台协议、登录、工作流、生成、桥接租约、/media 归属 |
|
|
345
|
+
| `isolation` | 账号隔离(工作流/任务/历史/媒体)、跨重启持久化、配对码开关、端口开关、迁移 |
|
|
346
|
+
| `assist` | 表头随工作流走(继承默认值、显式指定、改后保存、切工作流即换)、无文字输入的工作流不注入表头、助手分级与无模型降级 |
|
|
347
|
+
| `knowledge` | 知识库发现与读取、坏库名如实报错、摘录真的进了给模型的提示、字典模式也报告用了哪个库、分区字典仍按等级过滤 |
|
|
348
|
+
| `boundary` | **API 不审查、只有控制台审查**:成人提示词走 /prompt 原样执行、pool 不过滤、源码级契约(gateway/pool 不含分级词)、控制台侧仍拦截 |
|
|
349
|
+
| `pages` | 三个页面的静态审计:脚本可解析、无裸 window 全局、id 都存在、布局顺序、无渐变/无 emoji;控制台是下拉选择、无显卡面板、描述框与提示词同卡、编辑仅管理员 |
|
|
350
|
+
| `console` | 控制台接口:桥接仅管理员、参数真的写进图、资产像素尺寸与下载、越权取图 404、设置接口仅管理员 |
|
|
351
|
+
| `layout` | 设备适配:UA 判定(iPhone/Android/iPad/平板/桌面/未知)、属性戳在 `<html>`、视口纠正脚本、HTTP 层首屏布局与 `Vary`、手机标签栏、两栏拍平 |
|
|
352
|
+
| `render` | 侧边栏面板真渲染:分组、端口框、开关、账号等级;远程端口只在远程卡里、网关卡不含远程端口、每张实例卡都有「改端口」并真的驱动一次改端口 → 落盘(只填数字保留主机名);托管方式读得到真实类型,且选择在重复轮询后不被重置 |
|
|
353
|
+
|
|
354
|
+
`node test/screenshot.mjs` 用真 Edge 登录后截图到 `_shots/`——布局问题只有看图才发现。
|
|
355
|
+
|
|
356
|
+
`test/live*.mjs` 用临时数据目录和 18187/18186(或 18880/18881)端口,不碰已安装插件的配置。
|
|
357
|
+
|
|
358
|
+
## 发布 npm
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
node scripts/publish.mjs # 预演:身份/补丁/客户端 id/语法/tarball 内容/版本是否被占
|
|
362
|
+
node scripts/publish.mjs --apply # 真发布(需 token)
|
|
363
|
+
NODE_AUTH_TOKEN=npm_xxx node scripts/publish.mjs --apply
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
token 要 npmjs.com 的 **Granular Access Token**(Read and write + **Bypass 2FA**)。
|
|
367
|
+
registry 固定为 `registry.npmjs.org`——本机配置的是 npmmirror,它不接受发布。
|