comfyui-mcp 0.25.1 → 0.26.0
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 +33 -0
- package/dist/config.js +74 -3
- package/dist/config.js.map +1 -1
- package/dist/orchestrator/gemini-backend.js +6 -6
- package/dist/orchestrator/gemini-backend.js.map +1 -1
- package/dist/orchestrator/index.js +16 -3
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/services/process-control.js +38 -17
- package/dist/services/process-control.js.map +1 -1
- package/dist/services/workflow-converter.js +345 -0
- package/dist/services/workflow-converter.js.map +1 -1
- package/dist/tools/workflow-library.js +42 -6
- package/dist/tools/workflow-library.js.map +1 -1
- package/package.json +3 -2
- package/plugin/skills/comfyui-core/SKILL.md +7 -6
- package/scripts/arena-scenarios.mjs +226 -0
- package/scripts/llm-arena.mjs +1 -222
- package/scripts/runpod-release.mjs +119 -29
|
@@ -1,29 +1,88 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// npm-publish-style release of the RunPod template image to Docker Hub.
|
|
3
3
|
//
|
|
4
|
-
// npm run runpod:release
|
|
5
|
-
// npm run runpod:release --
|
|
4
|
+
// npm run runpod:release # minor bump: 1.6 -> 1.7
|
|
5
|
+
// npm run runpod:release -- major # major bump: 1.6 -> 2.0
|
|
6
|
+
// npm run runpod:release -- 2.3 # explicit tag
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
// Docker Hub) — the GitHub Actions workflow only builds the LEAN variant to
|
|
9
|
-
// ghcr.io (standard runners can't hold the 63 GB donor pull), so the fat
|
|
10
|
-
// template image is released from a dev machine with Docker + the layer cache.
|
|
8
|
+
// Like `npm publish`, nothing reaches the registry unverified. The flow:
|
|
11
9
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// 1. VERSION — resolve the next tag from what Docker Hub actually serves
|
|
11
|
+
// (two-part MAJOR.MINOR scheme; hub is the source of truth,
|
|
12
|
+
// not local docker tags).
|
|
13
|
+
// 2. BUILD — fat image (extras + spotcheck). The extras donor defaults to
|
|
14
|
+
// the PREVIOUS release (it carries the same three artifacts),
|
|
15
|
+
// so no 63 GB aitrepreneur pull; the panel clone layer is
|
|
16
|
+
// cache-busted to the panel repo's current main SHA. The build
|
|
17
|
+
// itself ends in the Dockerfile §9.5 integrity gate.
|
|
18
|
+
// 3. GATE — docker/runpod/deploy-dockerhub.sh runs test_image.sh (static
|
|
19
|
+
// + boot suite: fresh volume, redeploy persistence, ENOSPC
|
|
20
|
+
// self-heal). Any failure aborts before anything is pushed.
|
|
21
|
+
// 4. PUBLISH — push :<version> + :latest, then verify_image_remote.py
|
|
22
|
+
// checks what the registry ACTUALLY serves.
|
|
23
|
+
// 5. TEMPLATE — pin the RunPod template to the new VERSION tag via the
|
|
24
|
+
// GraphQL API (RUNPOD_API_KEY env or .env). NOT runpodctl:
|
|
25
|
+
// its `template update` sends a registry-credentials field
|
|
26
|
+
// that RunPod rejects for PUBLIC templates ("public templates
|
|
27
|
+
// cannot have Registry Credentials", verified 2026-07 on
|
|
28
|
+
// runpodctl 2.6.1). Pods must pull by version tag: RunPod
|
|
29
|
+
// hosts cache per-tag, so a template left on :latest can
|
|
30
|
+
// silently serve a stale build.
|
|
31
|
+
//
|
|
32
|
+
// Env knobs: RUNPOD_DONOR (extras donor image; set to aitrepreneur/comfyui:2.3.5
|
|
33
|
+
// for a from-scratch bootstrap), RUNPOD_TEMPLATE_ID (default bnqtkvcer3),
|
|
34
|
+
// RUNPOD_API_KEY (or .env), SKIP_TEMPLATE=1 to stop after publishing.
|
|
14
35
|
import { execFileSync, execSync } from "node:child_process";
|
|
36
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
15
37
|
import { fileURLToPath } from "node:url";
|
|
16
38
|
import { dirname, join } from "node:path";
|
|
17
39
|
|
|
18
40
|
const REPO = "artokun/comfyui-mcp-runpod";
|
|
41
|
+
const TEMPLATE_ID = process.env.RUNPOD_TEMPLATE_ID || "bnqtkvcer3";
|
|
19
42
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
20
43
|
const CTX = join(ROOT, "docker", "runpod");
|
|
44
|
+
const DONOR = process.env.RUNPOD_DONOR || `${REPO}:latest`;
|
|
21
45
|
|
|
22
46
|
const run = (cmd, args, opts = {}) =>
|
|
23
47
|
execFileSync(cmd, args, { stdio: "inherit", ...opts });
|
|
24
48
|
const capture = (cmd) => execSync(cmd, { encoding: "utf8" }).trim();
|
|
25
49
|
|
|
26
|
-
//
|
|
50
|
+
// ---- 1. VERSION -------------------------------------------------------------
|
|
51
|
+
const arg = process.argv[2] || "minor";
|
|
52
|
+
if (!/^\d+\.\d+$/.test(arg) && !["major", "minor", "patch"].includes(arg)) {
|
|
53
|
+
console.error(`usage: npm run runpod:release [-- major|minor|<X.Y>] (got "${arg}")`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hubTags = await (async () => {
|
|
58
|
+
const res = await fetch(
|
|
59
|
+
`https://hub.docker.com/v2/repositories/${REPO}/tags?page_size=100`,
|
|
60
|
+
);
|
|
61
|
+
if (!res.ok) throw new Error(`Docker Hub tag list failed: ${res.status}`);
|
|
62
|
+
return (await res.json()).results.map((t) => t.name);
|
|
63
|
+
})();
|
|
64
|
+
const versions = hubTags
|
|
65
|
+
.map((t) => /^(\d+)\.(\d+)$/.exec(t))
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.map((m) => [Number(m[1]), Number(m[2])])
|
|
68
|
+
.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
69
|
+
const cur = versions.at(-1) ?? [1, 0];
|
|
70
|
+
|
|
71
|
+
let tag;
|
|
72
|
+
if (/^\d+\.\d+$/.test(arg)) {
|
|
73
|
+
if (hubTags.includes(arg)) {
|
|
74
|
+
console.error(`refusing: ${REPO}:${arg} already exists on Docker Hub`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
tag = arg;
|
|
78
|
+
} else if (arg === "major") {
|
|
79
|
+
tag = `${cur[0] + 1}.0`;
|
|
80
|
+
} else {
|
|
81
|
+
tag = `${cur[0]}.${cur[1] + 1}`; // minor (and patch alias — two-part scheme)
|
|
82
|
+
}
|
|
83
|
+
console.log(`current hub release: ${cur.join(".")} -> releasing: ${tag}`);
|
|
84
|
+
|
|
85
|
+
// current panel main SHA (cache-busts the panel clone layer; no local clone needed)
|
|
27
86
|
const panelSha = capture(
|
|
28
87
|
"git ls-remote https://github.com/artokun/comfyui-mcp-panel.git refs/heads/main",
|
|
29
88
|
).split(/\s+/)[0];
|
|
@@ -32,27 +91,58 @@ if (!/^[0-9a-f]{40}$/.test(panelSha)) {
|
|
|
32
91
|
process.exit(1);
|
|
33
92
|
}
|
|
34
93
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
if (!tag) {
|
|
38
|
-
const tags = capture(`docker images ${REPO} --format "{{.Tag}}"`)
|
|
39
|
-
.split(/\r?\n/)
|
|
40
|
-
.map((t) => /^(\d+)\.(\d+)$/.exec(t))
|
|
41
|
-
.filter(Boolean)
|
|
42
|
-
.map((m) => [Number(m[1]), Number(m[2])])
|
|
43
|
-
.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
44
|
-
const last = tags.at(-1) ?? [1, 0];
|
|
45
|
-
tag = `${last[0]}.${last[1] + 1}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
console.log(`building ${REPO}:${tag} (+latest) — panel @ ${panelSha.slice(0, 8)}, fat (extras + spotcheck)`);
|
|
94
|
+
// ---- 2. BUILD (fat: extras + spotcheck; donor = previous release) -----------
|
|
95
|
+
console.log(`building ${REPO}:build — panel @ ${panelSha.slice(0, 8)}, extras donor: ${DONOR}`);
|
|
49
96
|
run("docker", [
|
|
50
97
|
"build",
|
|
51
98
|
"--build-arg", `PANEL_CACHEBUST=${panelSha}`,
|
|
52
|
-
"-
|
|
53
|
-
"-
|
|
99
|
+
"--build-arg", `RUNPOD_SRC_IMAGE=${DONOR}`,
|
|
100
|
+
"--build-arg", "INCLUDE_RUNPOD_EXTRAS=1",
|
|
101
|
+
"--build-arg", "BAKE_SPOTCHECK_MODEL=1",
|
|
102
|
+
"-t", `${REPO}:build`,
|
|
54
103
|
CTX,
|
|
55
104
|
]);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
105
|
+
|
|
106
|
+
// ---- 3+4. GATE, PUBLISH, VERIFY (deploy-dockerhub.sh refuses unverified pushes)
|
|
107
|
+
run("bash", [join(CTX, "deploy-dockerhub.sh"), tag, `${REPO}:build`]);
|
|
108
|
+
|
|
109
|
+
// ---- 5. TEMPLATE — pin the RunPod template to the immutable version tag -----
|
|
110
|
+
if (process.env.SKIP_TEMPLATE === "1") {
|
|
111
|
+
console.log(`SKIP_TEMPLATE=1 — remember to point the template at ${REPO}:${tag}`);
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
const image = `${REPO}:${tag}`;
|
|
115
|
+
// saveTemplate needs the full field set, so fetch-and-echo everything except
|
|
116
|
+
// imageName. (runpodctl deliberately not used here — see the header note.)
|
|
117
|
+
let key = process.env.RUNPOD_API_KEY;
|
|
118
|
+
if (!key && existsSync(join(ROOT, ".env"))) {
|
|
119
|
+
key = /^RUNPOD_API_KEY=(.+)$/m.exec(readFileSync(join(ROOT, ".env"), "utf8"))?.[1]?.trim();
|
|
120
|
+
}
|
|
121
|
+
if (!key) {
|
|
122
|
+
console.error(`released ${image}, but RUNPOD_API_KEY is not set — update the template manually:`);
|
|
123
|
+
console.error(` console.runpod.io -> Templates -> ${TEMPLATE_ID} -> Container Image -> ${image}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const gql = async (query, variables) => {
|
|
127
|
+
const res = await fetch(`https://api.runpod.io/graphql?api_key=${key}`, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: { "content-type": "application/json" },
|
|
130
|
+
body: JSON.stringify({ query, variables }),
|
|
131
|
+
});
|
|
132
|
+
const body = await res.json();
|
|
133
|
+
if (body.errors) throw new Error(JSON.stringify(body.errors));
|
|
134
|
+
return body.data;
|
|
135
|
+
};
|
|
136
|
+
const { podTemplates } = await gql(
|
|
137
|
+
"query { podTemplates { id name imageName containerDiskInGb volumeInGb volumeMountPath ports dockerArgs env { key value } readme } }",
|
|
138
|
+
);
|
|
139
|
+
const t = podTemplates.find((x) => x.id === TEMPLATE_ID);
|
|
140
|
+
if (!t) throw new Error(`template ${TEMPLATE_ID} not found on this account`);
|
|
141
|
+
await gql(
|
|
142
|
+
"mutation Save($input: SaveTemplateInput!) { saveTemplate(input: $input) { id imageName } }",
|
|
143
|
+
{ input: { ...t, imageName: image } },
|
|
144
|
+
);
|
|
145
|
+
console.log(`template ${TEMPLATE_ID} -> ${image} (via GraphQL)`);
|
|
146
|
+
|
|
147
|
+
console.log(`\nreleased ${image} (+ :latest), registry-verified, template pinned.`);
|
|
148
|
+
console.log("New pods pull the pinned version tag — stale host caches of :latest can no longer bite.");
|