dsh-comfyui 0.5.1 → 0.5.3
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.en.md +2 -2
- package/README.md +2 -2
- package/client/client.js +120 -22
- package/client/client.js.map +1 -1
- package/lib/analyze.js +24 -2
- package/lib/comfyui.d.ts +3 -0
- package/lib/comfyui.js +60 -5
- package/lib/config.d.ts +47 -31
- package/lib/config.js +59 -12
- package/lib/convert.d.ts +12 -0
- package/lib/convert.js +76 -10
- package/lib/index.d.ts +1 -1
- package/lib/index.js +43 -32
- package/lib/routes.js +14 -2
- package/lib/tools.js +20 -6
- package/package.json +3 -3
package/README.en.md
CHANGED
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
>
|
|
21
21
|
> | dsh-comfyui | Paired DeepSeek Harness | Notes |
|
|
22
22
|
> | --- | --- | --- |
|
|
23
|
-
> | **0.
|
|
23
|
+
> | **0.5.x (latest, `latest` tag)** | **>= 0.1.2** (incl. 0.1.5 / 0.1.6 / 0.1.7 prereleases) | From 0.5.3 the settings page works with dsh 0.1.7's settings model (saves apply live, no restart) |
|
|
24
24
|
> | **0.3.x (beta line, `beta` tag)** | **0.1.1** | On older dsh versions stay on the 0.3.x line |
|
|
25
25
|
>
|
|
26
|
-
> Install: `dsh plugin --profile web add dsh-comfyui` (latest 0.
|
|
26
|
+
> Install: `dsh plugin --profile web add dsh-comfyui` (latest 0.5.x) / `dsh plugin --profile web add dsh-comfyui@beta` (0.3.x for older hosts).
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
|
package/README.md
CHANGED
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
>
|
|
21
21
|
> | dsh-comfyui | 配对的 DeepSeek Harness | 说明 |
|
|
22
22
|
> | --- | --- | --- |
|
|
23
|
-
> | **0.
|
|
23
|
+
> | **0.5.x(最新,`latest` tag)** | **≥ 0.1.2**(含 0.1.5 / 0.1.6 / 0.1.7 预发布版) | 0.5.3 起兼容 dsh 0.1.7 的设置机制(设置页保存直接生效,无需重启) |
|
|
24
24
|
> | **0.3.x(beta 线,`beta` tag)** | **0.1.1** | 老版本 dsh 请留在 0.3.x 线 |
|
|
25
25
|
>
|
|
26
|
-
> 安装:`dsh plugin --profile web add dsh-comfyui`(装最新 0.
|
|
26
|
+
> 安装:`dsh plugin --profile web add dsh-comfyui`(装最新 0.5.x)/ `dsh plugin --profile web add dsh-comfyui@beta`(老宿主装 0.3.x)。
|
|
27
27
|
|
|
28
28
|
## 功能
|
|
29
29
|
|
package/client/client.js
CHANGED
|
@@ -45,6 +45,8 @@ window.__ModuleLoader__.load({
|
|
|
45
45
|
cardFailed: "执行失败",
|
|
46
46
|
cardInterrupted: "已中断",
|
|
47
47
|
cardCollect: "使用 job_output 收集后台任务结果",
|
|
48
|
+
cardRecovered: "已从资产索引恢复(ComfyUI 历史已失效)",
|
|
49
|
+
cardPollStalled: "后台任务状态收集失败(已重试 {n} 次):{message}",
|
|
48
50
|
cardLoadFailed: "加载失败,点下方下载",
|
|
49
51
|
cardDownload: "下载",
|
|
50
52
|
cardWorkflow: "工作流",
|
|
@@ -308,6 +310,8 @@ window.__ModuleLoader__.load({
|
|
|
308
310
|
cardFailed: "Execution failed",
|
|
309
311
|
cardInterrupted: "Interrupted",
|
|
310
312
|
cardCollect: "Collect the background job result with job_output",
|
|
313
|
+
cardRecovered: "Recovered from the asset index (ComfyUI history was evicted)",
|
|
314
|
+
cardPollStalled: "Failed to collect the background job status after {n} retries: {message}",
|
|
311
315
|
cardLoadFailed: "failed to load, use the download link below",
|
|
312
316
|
cardDownload: "Download",
|
|
313
317
|
cardWorkflow: "Workflow",
|
|
@@ -733,17 +737,39 @@ window.__ModuleLoader__.load({
|
|
|
733
737
|
onIndex: setLightbox
|
|
734
738
|
}) : null);
|
|
735
739
|
}
|
|
740
|
+
/** Transient-failure retry budget for the jobs/media poll (backoff steps). */
|
|
741
|
+
const POLL_MAX_FAILURES = 20;
|
|
742
|
+
/** 'unknown' grace polls (~5 min at 3 s) before treating history as evicted.
|
|
743
|
+
* The route answers 'queued' while ComfyUI still holds the prompt, so this
|
|
744
|
+
* only counts polls where the job is in neither history nor the queue. */
|
|
745
|
+
const POLL_UNKNOWN_GRACE = 100;
|
|
736
746
|
function BackgroundCard({ label, promptId, t }) {
|
|
737
747
|
const [result, setResult] = (0, react.useState)(null);
|
|
738
748
|
const [lightbox, setLightbox] = (0, react.useState)(null);
|
|
739
749
|
(0, react.useEffect)(() => {
|
|
740
750
|
let stopped = false;
|
|
741
751
|
let timer;
|
|
752
|
+
let failures = 0;
|
|
753
|
+
let unknowns = 0;
|
|
754
|
+
const schedule = (delay, fn) => {
|
|
755
|
+
timer = window.setTimeout(fn, delay);
|
|
756
|
+
};
|
|
757
|
+
const recoverFromAssets = async () => {
|
|
758
|
+
try {
|
|
759
|
+
const data = await (await fetch("/comfyui/assets", { headers: { accept: "application/json" } })).json();
|
|
760
|
+
if (stopped || data.ok !== true || !Array.isArray(data.assets)) return null;
|
|
761
|
+
const media = data.assets.find((entry) => entry.promptId === promptId)?.media ?? [];
|
|
762
|
+
return media.length > 0 ? media : null;
|
|
763
|
+
} catch {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
742
767
|
const poll = async () => {
|
|
743
768
|
try {
|
|
744
769
|
const data = await (await fetch(`/comfyui/jobs/media?promptId=${encodeURIComponent(promptId)}`, { headers: { accept: "application/json" } })).json();
|
|
745
770
|
if (stopped) return;
|
|
746
|
-
if (data.ok !== true || data.status === void 0)
|
|
771
|
+
if (data.ok !== true || data.status === void 0) throw new Error(data.error ?? "invalid jobs/media response");
|
|
772
|
+
failures = 0;
|
|
747
773
|
if (data.status === "completed" || data.status === "failed") {
|
|
748
774
|
setResult({
|
|
749
775
|
status: data.status,
|
|
@@ -752,13 +778,51 @@ window.__ModuleLoader__.load({
|
|
|
752
778
|
});
|
|
753
779
|
return;
|
|
754
780
|
}
|
|
755
|
-
|
|
781
|
+
if (data.status === "unknown") {
|
|
782
|
+
unknowns += 1;
|
|
783
|
+
const recovered = await recoverFromAssets();
|
|
784
|
+
if (stopped) return;
|
|
785
|
+
if (recovered !== null) {
|
|
786
|
+
setResult({
|
|
787
|
+
status: "completed",
|
|
788
|
+
media: recovered,
|
|
789
|
+
recovered: true
|
|
790
|
+
});
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (unknowns > POLL_UNKNOWN_GRACE) {
|
|
794
|
+
setResult({
|
|
795
|
+
status: "completed",
|
|
796
|
+
media: []
|
|
797
|
+
});
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
schedule(3e3, () => {
|
|
801
|
+
poll();
|
|
802
|
+
});
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
unknowns = 0;
|
|
806
|
+
schedule(3e3, () => {
|
|
756
807
|
poll();
|
|
757
|
-
}
|
|
758
|
-
} catch {
|
|
759
|
-
|
|
808
|
+
});
|
|
809
|
+
} catch (error) {
|
|
810
|
+
if (stopped) return;
|
|
811
|
+
failures += 1;
|
|
812
|
+
if (failures > POLL_MAX_FAILURES) {
|
|
813
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
814
|
+
setResult({
|
|
815
|
+
status: "failed",
|
|
816
|
+
error: t("cardPollStalled", {
|
|
817
|
+
n: POLL_MAX_FAILURES,
|
|
818
|
+
message
|
|
819
|
+
})
|
|
820
|
+
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
schedule(Math.min(3e3 * failures, 3e4), () => {
|
|
760
824
|
poll();
|
|
761
|
-
}
|
|
825
|
+
});
|
|
762
826
|
}
|
|
763
827
|
};
|
|
764
828
|
poll();
|
|
@@ -766,12 +830,12 @@ window.__ModuleLoader__.load({
|
|
|
766
830
|
stopped = true;
|
|
767
831
|
if (timer !== void 0) window.clearTimeout(timer);
|
|
768
832
|
};
|
|
769
|
-
}, [promptId]);
|
|
833
|
+
}, [promptId, t]);
|
|
770
834
|
if (result !== null && (result.status === "completed" || result.status === "failed")) {
|
|
771
835
|
const media = result.media ?? [];
|
|
772
836
|
const urls = media.map((item) => item.url);
|
|
773
837
|
const kinds = media.map((item) => item.kind);
|
|
774
|
-
return (0, react.createElement)("div", { className: "dsc-card" }, (0, react.createElement)("div", { className: "dsc-card-head" }, (0, react.createElement)("span", { className: result.status === "failed" ? "dsc-badge dsc-badge--err" : "dsc-badge dsc-badge--ok" }, result.status === "failed" ? t("cardFailed") : t("cardBackgroundDone")), (0, react.createElement)("span", { className: "dsc-meta" }, `${label} · ${promptId}`)), result.status === "failed" ? (0, react.createElement)("div", { className: "dsc-meta dsc-job-error" }, result.error ?? t("cardFailed")) : media.length > 0 ? (0, react.createElement)("div", { className: "dsc-grid" }, media.map((item, index) => (0, react.createElement)(MediaItem, {
|
|
838
|
+
return (0, react.createElement)("div", { className: "dsc-card" }, (0, react.createElement)("div", { className: "dsc-card-head" }, (0, react.createElement)("span", { className: result.status === "failed" ? "dsc-badge dsc-badge--err" : "dsc-badge dsc-badge--ok" }, result.status === "failed" ? t("cardFailed") : t("cardBackgroundDone")), (0, react.createElement)("span", { className: "dsc-meta" }, `${label} · ${promptId}`)), result.status !== "failed" && result.recovered === true ? (0, react.createElement)("div", { className: "dsc-meta" }, t("cardRecovered")) : null, result.status === "failed" ? (0, react.createElement)("div", { className: "dsc-meta dsc-job-error" }, result.error ?? t("cardFailed")) : media.length > 0 ? (0, react.createElement)("div", { className: "dsc-grid" }, media.map((item, index) => (0, react.createElement)(MediaItem, {
|
|
775
839
|
key: `${item.node}-${item.index}`,
|
|
776
840
|
item,
|
|
777
841
|
t,
|
|
@@ -843,6 +907,19 @@ window.__ModuleLoader__.load({
|
|
|
843
907
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
844
908
|
return await response.json();
|
|
845
909
|
}
|
|
910
|
+
/**
|
|
911
|
+
* GET a list endpoint and return the array under `key`. Routes report failure
|
|
912
|
+
* as HTTP 200 `{ ok: false, error }`, so a plain getJson hands the caller
|
|
913
|
+
* `undefined` for the list — which crashed the whole panel on `.length` inside
|
|
914
|
+
* the slot error boundary, i.e. "click does nothing" (Issue #5). Throwing the
|
|
915
|
+
* route's error instead lets the caller show it next to the section.
|
|
916
|
+
*/
|
|
917
|
+
async function getList(url, key) {
|
|
918
|
+
const data = await getJson(url);
|
|
919
|
+
const list = data[key];
|
|
920
|
+
if (Array.isArray(list)) return list;
|
|
921
|
+
throw new Error(typeof data.error === "string" ? data.error : `unexpected response from ${url}`);
|
|
922
|
+
}
|
|
846
923
|
async function postJson(url, body) {
|
|
847
924
|
const response = await fetch(url, {
|
|
848
925
|
method: "POST",
|
|
@@ -2250,13 +2327,16 @@ window.__ModuleLoader__.load({
|
|
|
2250
2327
|
active: tab === "queue",
|
|
2251
2328
|
label: t("tabQueue"),
|
|
2252
2329
|
onClick: () => panelStore.setTab("queue")
|
|
2253
|
-
})), (0, react.createElement)("div", { className: "dsc-panel-body" },
|
|
2330
|
+
})), (0, react.createElement)("div", { className: "dsc-panel-body" }, (0, react.createElement)(TabBoundary, {
|
|
2331
|
+
key: tab,
|
|
2332
|
+
t
|
|
2333
|
+
}, tab === "workflows" ? (0, react.createElement)(WorkflowsTab, { t }) : tab === "assets" ? (0, react.createElement)(AssetsTab, {
|
|
2254
2334
|
t,
|
|
2255
2335
|
onPreview: openPreview
|
|
2256
2336
|
}) : (0, react.createElement)(QueueTab, {
|
|
2257
2337
|
t,
|
|
2258
2338
|
onPreview: openPreview
|
|
2259
|
-
})), lightbox !== null ? (0, react.createElement)(Lightbox, {
|
|
2339
|
+
}))), lightbox !== null ? (0, react.createElement)(Lightbox, {
|
|
2260
2340
|
t,
|
|
2261
2341
|
images: lightbox.images,
|
|
2262
2342
|
kinds: lightbox.kinds,
|
|
@@ -2284,6 +2364,27 @@ window.__ModuleLoader__.load({
|
|
|
2284
2364
|
function ErrorNote({ t, message }) {
|
|
2285
2365
|
return (0, react.createElement)("div", { className: "dsc-err" }, `${t("error")}: ${message}`);
|
|
2286
2366
|
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Catches a tab's render error and shows it in place. Without this the host's
|
|
2369
|
+
* slot error boundary swallows the whole overlay silently, so a tab crash
|
|
2370
|
+
* reads as "clicking the button does nothing" with a clean console (Issue #5).
|
|
2371
|
+
*/
|
|
2372
|
+
var TabBoundary = class extends react.Component {
|
|
2373
|
+
state = { error: null };
|
|
2374
|
+
static getDerivedStateFromError(error) {
|
|
2375
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
2376
|
+
}
|
|
2377
|
+
componentDidCatch(error) {
|
|
2378
|
+
console.error("[dsh-comfyui] panel tab render error", error);
|
|
2379
|
+
}
|
|
2380
|
+
render() {
|
|
2381
|
+
if (this.state.error !== null) return (0, react.createElement)(ErrorNote, {
|
|
2382
|
+
t: this.props.t,
|
|
2383
|
+
message: this.state.error.message
|
|
2384
|
+
});
|
|
2385
|
+
return this.props.children;
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2287
2388
|
/** Load area at the bottom of the workflow library, modeled on the ComfyUI
|
|
2288
2389
|
* LoadImage node — a list of **slots** rather than a single image.
|
|
2289
2390
|
*
|
|
@@ -3169,15 +3270,13 @@ window.__ModuleLoader__.load({
|
|
|
3169
3270
|
const [deleting, setDeleting] = (0, react.useState)(null);
|
|
3170
3271
|
const load = async () => {
|
|
3171
3272
|
try {
|
|
3172
|
-
|
|
3173
|
-
setList(data.workflows);
|
|
3273
|
+
setList(await getList("/comfyui/workflows", "workflows"));
|
|
3174
3274
|
setError(null);
|
|
3175
3275
|
} catch (cause) {
|
|
3176
3276
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
3177
3277
|
}
|
|
3178
3278
|
try {
|
|
3179
|
-
|
|
3180
|
-
setComfyui(data.workflows);
|
|
3279
|
+
setComfyui(await getList("/comfyui/comfy-workflows", "workflows"));
|
|
3181
3280
|
setComfyuiError(null);
|
|
3182
3281
|
} catch (cause) {
|
|
3183
3282
|
setComfyuiError(cause instanceof Error ? cause.message : String(cause));
|
|
@@ -3185,13 +3284,13 @@ window.__ModuleLoader__.load({
|
|
|
3185
3284
|
};
|
|
3186
3285
|
(0, react.useEffect)(() => {
|
|
3187
3286
|
let cancelled = false;
|
|
3188
|
-
|
|
3189
|
-
if (!cancelled) setList(
|
|
3287
|
+
getList("/comfyui/workflows", "workflows").then((workflows) => {
|
|
3288
|
+
if (!cancelled) setList(workflows);
|
|
3190
3289
|
}).catch((cause) => {
|
|
3191
3290
|
if (!cancelled) setError(cause instanceof Error ? cause.message : String(cause));
|
|
3192
3291
|
});
|
|
3193
|
-
|
|
3194
|
-
if (!cancelled) setComfyui(
|
|
3292
|
+
getList("/comfyui/comfy-workflows", "workflows").then((workflows) => {
|
|
3293
|
+
if (!cancelled) setComfyui(workflows);
|
|
3195
3294
|
}).catch((cause) => {
|
|
3196
3295
|
if (!cancelled) setComfyuiError(cause instanceof Error ? cause.message : String(cause));
|
|
3197
3296
|
});
|
|
@@ -4059,8 +4158,7 @@ window.__ModuleLoader__.load({
|
|
|
4059
4158
|
const [notice, setNotice] = (0, react.useState)(null);
|
|
4060
4159
|
const load = async () => {
|
|
4061
4160
|
try {
|
|
4062
|
-
|
|
4063
|
-
setAssets(data.assets);
|
|
4161
|
+
setAssets(await getList("/comfyui/assets", "assets"));
|
|
4064
4162
|
setError(null);
|
|
4065
4163
|
} catch (cause) {
|
|
4066
4164
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
@@ -4068,8 +4166,8 @@ window.__ModuleLoader__.load({
|
|
|
4068
4166
|
};
|
|
4069
4167
|
(0, react.useEffect)(() => {
|
|
4070
4168
|
let cancelled = false;
|
|
4071
|
-
|
|
4072
|
-
if (!cancelled) setAssets(
|
|
4169
|
+
getList("/comfyui/assets", "assets").then((items) => {
|
|
4170
|
+
if (!cancelled) setAssets(items);
|
|
4073
4171
|
}).catch((cause) => {
|
|
4074
4172
|
if (!cancelled) setError(cause instanceof Error ? cause.message : String(cause));
|
|
4075
4173
|
});
|