comfyui-mcp 0.26.5 → 0.28.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/dist/orchestrator/backend-readiness.js +34 -0
- package/dist/orchestrator/backend-readiness.js.map +1 -1
- package/dist/orchestrator/index.js +132 -16
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/ollama-backend.js +9 -0
- package/dist/orchestrator/ollama-backend.js.map +1 -1
- package/dist/services/lmstudio-lifecycle.js +143 -0
- package/dist/services/lmstudio-lifecycle.js.map +1 -0
- package/dist/services/panel-settings.js +3 -0
- package/dist/services/panel-settings.js.map +1 -1
- package/package.json +5 -3
- package/scripts/gen-changelog.mjs +244 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lmstudio-lifecycle.js","sourceRoot":"","sources":["../../src/services/lmstudio-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -71,6 +71,9 @@ export function setAgentSettings(patch) {
|
|
|
71
71
|
if (patch.ollama !== undefined) {
|
|
72
72
|
next.ollama = { ...prev.ollama, ...patch.ollama };
|
|
73
73
|
}
|
|
74
|
+
if (patch.lmstudio !== undefined) {
|
|
75
|
+
next.lmstudio = { ...prev.lmstudio, ...patch.lmstudio };
|
|
76
|
+
}
|
|
74
77
|
settings.agent = next;
|
|
75
78
|
write(settings);
|
|
76
79
|
return next;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel-settings.js","sourceRoot":"","sources":["../../src/services/panel-settings.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,kEAAkE;AAElE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"panel-settings.js","sourceRoot":"","sources":["../../src/services/panel-settings.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comfyui-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"mcpName": "io.github.artokun/comfyui-mcp",
|
|
5
|
-
"description": "Local-first, agent-native control plane for ComfyUI
|
|
5
|
+
"description": "Local-first, agent-native control plane for ComfyUI \u00e2\u20ac\u201d MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline), or any hosted model via an OpenAI-compatible endpoint (DeepSeek, GLM, MiMo, OpenRouter). Generate images, video & audio, author and run workflows, manage models and custom nodes; a compact tool-router mode keeps even 4B models effective, and a built-in LLM Arena benchmarks them on real ComfyUI tasks. Also a Claude Code plugin with skills, slash commands, and installer packs. Local, LAN, VPS, or Comfy Cloud.",
|
|
6
6
|
"homepage": "https://comfyui-mcp.artokun.io/docs",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
"generations:stats": "node scripts/generation-stats.mjs",
|
|
46
46
|
"release": "npm version patch && git push --follow-tags",
|
|
47
47
|
"release:minor": "npm version minor && git push --follow-tags",
|
|
48
|
-
"release:major": "npm version major && git push --follow-tags"
|
|
48
|
+
"release:major": "npm version major && git push --follow-tags",
|
|
49
|
+
"changelog": "node scripts/gen-changelog.mjs",
|
|
50
|
+
"version": "node scripts/gen-changelog.mjs --from-pkg && git add CHANGELOG.md"
|
|
49
51
|
},
|
|
50
52
|
"dependencies": {
|
|
51
53
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Hybrid changelog generator, wired into the release flow.
|
|
3
|
+
//
|
|
4
|
+
// node scripts/gen-changelog.mjs <version>
|
|
5
|
+
//
|
|
6
|
+
// It stamps a dated section for <version> at the top of CHANGELOG.md by:
|
|
7
|
+
// 1. Promoting whatever you hand-wrote under "## [Unreleased]" VERBATIM
|
|
8
|
+
// (your highlights — the rich prose we care about), then
|
|
9
|
+
// 2. Appending anything in the git history since the last tag that your
|
|
10
|
+
// highlights didn't already mention (deduped by PR number), grouped into
|
|
11
|
+
// COMPONENT sections (MCP / RunPod image) and Keep-a-Changelog buckets
|
|
12
|
+
// (Added / Fixed / Changed) from the conventional-commit type.
|
|
13
|
+
// 3. Resetting "## [Unreleased]" to an empty stub.
|
|
14
|
+
//
|
|
15
|
+
// So nothing in the history is ever missed, and your hand-written notes are
|
|
16
|
+
// never clobbered. Idempotent-ish: safe to re-run before the version is tagged.
|
|
17
|
+
//
|
|
18
|
+
// Repo config (COMPONENTS) below decides how a commit maps to a section — this
|
|
19
|
+
// file is the comfyui-mcp variant (MCP + RunPod); the panel ships its own.
|
|
20
|
+
|
|
21
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { execSync } from "node:child_process";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
|
+
|
|
26
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
27
|
+
const CHANGELOG = join(ROOT, "CHANGELOG.md");
|
|
28
|
+
|
|
29
|
+
// ── Repo config ─────────────────────────────────────────────────────────────
|
|
30
|
+
// First matching component wins; the last (match:()=>true) is the fallback.
|
|
31
|
+
const COMPONENTS = [
|
|
32
|
+
{ name: "RunPod image", match: (scope) => /^runpod/.test(scope || "") },
|
|
33
|
+
{ name: "MCP", match: () => true },
|
|
34
|
+
];
|
|
35
|
+
// conventional-commit type → Keep-a-Changelog bucket. Types not listed are
|
|
36
|
+
// dropped from the changelog (chore/ci/test/build/style/docs housekeeping).
|
|
37
|
+
const TYPE_SECTION = {
|
|
38
|
+
feat: "Added",
|
|
39
|
+
fix: "Fixed",
|
|
40
|
+
perf: "Changed",
|
|
41
|
+
refactor: "Changed",
|
|
42
|
+
revert: "Changed",
|
|
43
|
+
};
|
|
44
|
+
const SECTION_ORDER = ["Added", "Fixed", "Changed"];
|
|
45
|
+
|
|
46
|
+
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
47
|
+
// stderr ignored: several queries (e.g. describe with no tags) fail by design and are try/caught.
|
|
48
|
+
const git = (args) => execSync(`git ${args}`, { cwd: ROOT, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
49
|
+
|
|
50
|
+
/** The ref to diff against = the previous release. Prefer the most recent
|
|
51
|
+
* version tag (mcp); fall back to the most recent release commit (the panel
|
|
52
|
+
* has no per-release tags); else the first commit. */
|
|
53
|
+
function prevTag() {
|
|
54
|
+
try {
|
|
55
|
+
const t = git("describe --tags --abbrev=0");
|
|
56
|
+
if (t) return t;
|
|
57
|
+
} catch {
|
|
58
|
+
/* no tags */
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const sha = git('log -1 --pretty=format:%H --grep="^\\(chore(release)\\|release\\):"');
|
|
62
|
+
if (/^[0-9a-f]{7,40}$/.test(sha)) return sha;
|
|
63
|
+
} catch {
|
|
64
|
+
/* no release commit */
|
|
65
|
+
}
|
|
66
|
+
return git("rev-list --max-parents=0 HEAD").split(/\s+/)[0]; // first commit
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Parsed conventional commits since `range`, newest-first, minus noise. */
|
|
70
|
+
function parseCommits(range) {
|
|
71
|
+
const raw = git(`log ${range} --no-merges --pretty=format:%s`);
|
|
72
|
+
if (!raw) return [];
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const subject of raw.split("\n")) {
|
|
75
|
+
if (/^release:/i.test(subject)) continue; // release commits describe themselves
|
|
76
|
+
const m = subject.match(/^(\w+)(?:\(([^)]+)\))?(!)?:\s*(.+)$/);
|
|
77
|
+
if (!m) continue; // non-conventional → skip (usually already covered by a PR merge)
|
|
78
|
+
const [, type, scope, , desc] = m;
|
|
79
|
+
const section = TYPE_SECTION[type.toLowerCase()];
|
|
80
|
+
if (!section) continue;
|
|
81
|
+
const pr = (desc.match(/\(#(\d+)\)/) || [])[1] || null;
|
|
82
|
+
out.push({ type, scope: scope || "", desc: desc.trim(), section, pr });
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function componentOf(scope) {
|
|
88
|
+
return (COMPONENTS.find((c) => c.match(scope)) || COMPONENTS[COMPONENTS.length - 1]).name;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Build the auto-generated component/section body for commits not already
|
|
92
|
+
* covered by the hand-written highlights (deduped by PR number). */
|
|
93
|
+
function autoBody(commits, coveredPRs) {
|
|
94
|
+
const fresh = commits.filter((c) => !(c.pr && coveredPRs.has(c.pr)));
|
|
95
|
+
if (fresh.length === 0) return "";
|
|
96
|
+
// component -> section -> bullets[]
|
|
97
|
+
const byComp = new Map();
|
|
98
|
+
for (const c of fresh) {
|
|
99
|
+
const comp = componentOf(c.scope);
|
|
100
|
+
if (!byComp.has(comp)) byComp.set(comp, new Map());
|
|
101
|
+
const secs = byComp.get(comp);
|
|
102
|
+
if (!secs.has(c.section)) secs.set(c.section, []);
|
|
103
|
+
secs.get(c.section).push(`- ${c.desc}`);
|
|
104
|
+
}
|
|
105
|
+
// Single-component repos (e.g. the panel) read cleaner with flat `### Added`
|
|
106
|
+
// headers; multi-component repos (mcp) nest `### Component` > `#### Added`.
|
|
107
|
+
const single = COMPONENTS.length === 1;
|
|
108
|
+
const lines = [];
|
|
109
|
+
for (const comp of COMPONENTS.map((c) => c.name)) {
|
|
110
|
+
const secs = byComp.get(comp);
|
|
111
|
+
if (!secs) continue;
|
|
112
|
+
if (!single) lines.push(`### ${comp}`, "");
|
|
113
|
+
for (const section of SECTION_ORDER) {
|
|
114
|
+
const bullets = secs.get(section);
|
|
115
|
+
if (!bullets) continue;
|
|
116
|
+
lines.push(single ? `### ${section}` : `#### ${section}`, ...bullets, "");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return lines.join("\n").trimEnd();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function today() {
|
|
123
|
+
const d = new Date();
|
|
124
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── main ─────────────────────────────────────────────────────────────────────
|
|
128
|
+
const backfill = process.argv.includes("--backfill");
|
|
129
|
+
let version = (process.argv.find((a) => /^v?\d+\.\d+\.\d+/.test(a)) || "").replace(/^v/, "");
|
|
130
|
+
// --from-pkg: read the version npm just wrote to package.json. The lifecycle
|
|
131
|
+
// hook previously passed $npm_package_version, an sh-only expansion that is
|
|
132
|
+
// silently EMPTY under cmd.exe — `npm version` then died mid-release on
|
|
133
|
+
// Windows (files bumped, commit+tag never made).
|
|
134
|
+
if (!version && process.argv.includes("--from-pkg")) {
|
|
135
|
+
version = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8")).version;
|
|
136
|
+
}
|
|
137
|
+
if (!backfill && !/^\d+\.\d+\.\d+/.test(version)) {
|
|
138
|
+
console.error(`usage: node scripts/gen-changelog.mjs <version> | --backfill`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
// Preserve the file's own line-ending convention (mcp is CRLF, panel LF): work
|
|
142
|
+
// in LF internally, restore on write so we never spuriously rewrite the whole file.
|
|
143
|
+
const rawMd = readFileSync(CHANGELOG, "utf-8");
|
|
144
|
+
const EOL = rawMd.includes("\r\n") ? "\r\n" : "\n";
|
|
145
|
+
const writeChangelog = (s) => writeFileSync(CHANGELOG, EOL === "\r\n" ? s.replace(/\n/g, "\r\n") : s);
|
|
146
|
+
|
|
147
|
+
/** Build a dated entry string for `ver` from commits in `range`, folding in any
|
|
148
|
+
* hand-written highlights (deduped by PR). */
|
|
149
|
+
function buildEntry(ver, range, highlights = "") {
|
|
150
|
+
const covered = new Set([...highlights.matchAll(/\(#(\d+)\)/g)].map((m) => m[1]));
|
|
151
|
+
const commits = parseCommits(range);
|
|
152
|
+
const auto = autoBody(commits, covered);
|
|
153
|
+
const parts = [`## [${ver}] - ${today()}`, ""];
|
|
154
|
+
if (highlights) parts.push(highlights, "");
|
|
155
|
+
if (auto) parts.push(auto, "");
|
|
156
|
+
if (!highlights && !auto) parts.push("_No user-facing changes._", "");
|
|
157
|
+
return { text: parts.join("\n").trimEnd(), commits };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const md = rawMd.replace(/\r\n/g, "\n"); // normalized (LF) for matching + building
|
|
161
|
+
// Accept both "## [Unreleased]" (panel) and "## Unreleased" (mcp); preserve the
|
|
162
|
+
// exact header text so we don't reformat the file's own convention.
|
|
163
|
+
const UNREL = /(##[ \t]*(?:\[Unreleased\]|Unreleased))[ \t]*\n([\s\S]*?)(?=\n##[ \t]|\n<!-- end -->|$)/i;
|
|
164
|
+
const um = md.match(UNREL);
|
|
165
|
+
if (!um) {
|
|
166
|
+
console.error("could not find an '## Unreleased' (or '## [Unreleased]') section in CHANGELOG.md");
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
const unrelHeader = um[1]; // e.g. "## Unreleased" or "## [Unreleased]"
|
|
170
|
+
const highlights = um[2].trim();
|
|
171
|
+
|
|
172
|
+
if (backfill) {
|
|
173
|
+
// One-time repair: emit a dated entry for every tag NEWER than the newest one
|
|
174
|
+
// already in the CHANGELOG, oldest→newest, from the commits between tags.
|
|
175
|
+
const tags = git("tag --sort=creatordate")
|
|
176
|
+
.split("\n")
|
|
177
|
+
.filter((t) => /^v?\d+\.\d+\.\d+$/.test(t));
|
|
178
|
+
// Only catch up the changelog from where it left off — the newest version it
|
|
179
|
+
// already documents. Don't resurrect ancient pre-changelog tags.
|
|
180
|
+
const cmp = (a, b) => {
|
|
181
|
+
const pa = a.split("."), pb = b.split(".");
|
|
182
|
+
for (let i = 0; i < 3; i++) if (+pa[i] !== +pb[i]) return +pa[i] - +pb[i];
|
|
183
|
+
return 0;
|
|
184
|
+
};
|
|
185
|
+
const documented = [...md.matchAll(/##\s*\[(\d+\.\d+\.\d+)\]/g)].map((m) => m[1]);
|
|
186
|
+
const newest = documented.sort(cmp).pop() || "0.0.0";
|
|
187
|
+
const missing = tags.filter(
|
|
188
|
+
(t) => cmp(t.replace(/^v/, ""), newest) > 0 && !md.includes(`## [${t.replace(/^v/, "")}]`),
|
|
189
|
+
);
|
|
190
|
+
if (missing.length === 0) {
|
|
191
|
+
console.log("backfill: nothing missing.");
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
const blocks = [];
|
|
195
|
+
for (let i = 0; i < missing.length; i++) {
|
|
196
|
+
const tag = missing[i];
|
|
197
|
+
const idx = tags.indexOf(tag);
|
|
198
|
+
const prev = tags[idx - 1];
|
|
199
|
+
const range = prev ? `${prev}..${tag}` : tag;
|
|
200
|
+
blocks.push(buildEntry(tag.replace(/^v/, ""), range).text);
|
|
201
|
+
}
|
|
202
|
+
// newest first under Unreleased
|
|
203
|
+
const body = blocks.reverse().join("\n\n");
|
|
204
|
+
const next = md.replace(UNREL, `${unrelHeader}\n\n${body}\n\n`);
|
|
205
|
+
writeChangelog(next);
|
|
206
|
+
console.log(`backfill: added ${missing.length} missing version(s): ${missing.join(", ")}`);
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!version) {
|
|
211
|
+
// REFRESH mode (no version arg): fold commits since the last tag into
|
|
212
|
+
// [Unreleased] without stamping a version — keeps the changelog warm between
|
|
213
|
+
// releases (e.g. after a runpod:release). Idempotent: items already present
|
|
214
|
+
// (by PR number or exact text) are not re-added.
|
|
215
|
+
const covered = new Set([...highlights.matchAll(/\(#(\d+)\)/g)].map((m) => m[1]));
|
|
216
|
+
const commits = parseCommits(`${prevTag()}..HEAD`).filter(
|
|
217
|
+
(c) => !(c.pr && covered.has(c.pr)) && !highlights.includes(c.desc),
|
|
218
|
+
);
|
|
219
|
+
const auto = autoBody(commits, new Set());
|
|
220
|
+
if (!auto) {
|
|
221
|
+
console.log("changelog: [Unreleased] already covers every commit since " + prevTag());
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
const body = [highlights, auto].filter(Boolean).join("\n\n");
|
|
225
|
+
writeChangelog(md.replace(UNREL, `${unrelHeader}\n\n${body}\n\n`));
|
|
226
|
+
console.log(`changelog: refreshed [Unreleased] with ${commits.length} new commit(s) since ${prevTag()}`);
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (md.includes(`## [${version}]`)) {
|
|
231
|
+
console.error(`CHANGELOG already has a [${version}] section — nothing to do.`);
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const { text: entry, commits } = buildEntry(version, `${prevTag()}..HEAD`, highlights);
|
|
236
|
+
const next = md.replace(UNREL, `${unrelHeader}\n\n${entry}\n\n`);
|
|
237
|
+
writeChangelog(next);
|
|
238
|
+
|
|
239
|
+
const nComp = new Set(commits.map((c) => componentOf(c.scope))).size;
|
|
240
|
+
console.log(
|
|
241
|
+
`changelog: wrote [${version}] — ${highlights ? "kept hand-written highlights + " : ""}${
|
|
242
|
+
commits.length
|
|
243
|
+
} commit(s) across ${nComp} component(s) since ${prevTag()}`,
|
|
244
|
+
);
|