dsh-comfyui 0.5.0 → 0.5.1

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/lib/analyze.d.ts CHANGED
@@ -20,7 +20,9 @@ export interface GraphGroupLike {
20
20
  }
21
21
  export interface GraphLike {
22
22
  nodes: GraphNodeLike[];
23
- links: Array<[number, number, number, number, number, string] | number[]>;
23
+ /** Saved rows are positional or object entries depending on the frontend
24
+ * version — always consumed through {@link normalizeLinks}. */
25
+ links: unknown[];
24
26
  groups?: GraphGroupLike[];
25
27
  }
26
28
  export interface IsolatedNode {
package/lib/analyze.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * graph links (with bypassed and dangling nodes excluded). The analysis feeds
6
6
  * the extract (拆分) choices in the panel and the agent-facing skill.
7
7
  */
8
+ import { normalizeLinks } from './graph.js';
8
9
  function isObject(value) {
9
10
  return typeof value === 'object' && value !== null && !Array.isArray(value);
10
11
  }
@@ -27,7 +28,7 @@ export function analyzeGraph(graph) {
27
28
  return { ok: false, error: '无法解析图文件(缺少 nodes/links)' };
28
29
  }
29
30
  const nodes = graph.nodes;
30
- const links = graph.links;
31
+ const links = normalizeLinks(graph.links);
31
32
  const groups = Array.isArray(graph.groups) ? graph.groups : [];
32
33
  const active = nodes.filter((node) => node.mode !== 4);
33
34
  const activeById = new Map(active.map((node) => [node.id, node]));
package/lib/convert.d.ts CHANGED
@@ -1,12 +1,3 @@
1
- /**
2
- * Convert a ComfyUI UI-graph workflow (the format the ComfyUI frontend saves
3
- * to the server via /api/userdata) into the API format accepted by POST
4
- * /prompt. Modeled on the frontend's convertToApiFormat: input links become
5
- * [nodeId, slot] references, widgets_values are zipped onto input names in
6
- * object_info order (control_after_generate is the extra seed widget), and
7
- * Reroute / bypassed (mode 4) nodes are skipped with their links rewired.
8
- * Nodes the conversion cannot represent fail loudly with the offending type.
9
- */
10
1
  export interface ApiWorkflow {
11
2
  [nodeId: string]: {
12
3
  class_type: string;
package/lib/convert.js CHANGED
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Convert a ComfyUI UI-graph workflow (the format the ComfyUI frontend saves
3
+ * to the server via /api/userdata) into the API format accepted by POST
4
+ * /prompt. Modeled on the frontend's convertToApiFormat: input links become
5
+ * [nodeId, slot] references, widgets_values are zipped onto input names in
6
+ * object_info order (control_after_generate is the extra seed widget), and
7
+ * Reroute / bypassed (mode 4) nodes are skipped with their links rewired.
8
+ * Nodes the conversion cannot represent fail loudly with the offending type.
9
+ */
10
+ import { normalizeLinks } from './graph.js';
1
11
  /** Node types that exist only in the UI and carry no data flow. */
2
12
  const UI_ONLY = new Set(['Note', 'StickyNote', 'Reroute', 'Fast Groups Bypasser (rgthree)']);
3
13
  function isObject(value) {
@@ -195,13 +205,11 @@ export function convertGraphToApi(graph, objectInfo, options) {
195
205
  };
196
206
  });
197
207
  const links = new Map();
198
- for (const raw of rawLinks) {
199
- if (!Array.isArray(raw) || raw.length < 6)
200
- continue;
201
- const link = raw;
202
- if (typeof link[0] === 'number')
203
- links.set(link[0], link);
204
- }
208
+ // normalizeLinks accepts both the legacy positional rows and the v0.4
209
+ // frontend's object entries; anything unreadable is skipped, exactly like
210
+ // the old array-only guard did.
211
+ for (const link of normalizeLinks(rawLinks))
212
+ links.set(link[0], link);
205
213
  const nodesById = new Map(nodes.map((node) => [node.id, node]));
206
214
  const included = options?.includeNodeIds;
207
215
  const candidates = included !== undefined ? nodes.filter((node) => included.has(node.id)) : nodes;
package/lib/graph.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * ComfyUI UI-graph link normalization. The frontend changed how saved graphs
3
+ * serialize the `links` array: rows used to be positional
4
+ * (`[id, originId, originSlot, targetId, targetSlot, type]`), while the v0.4
5
+ * frontend writes object entries
6
+ * (`{ id, origin_id, origin_slot, target_id, target_slot, type }`). Analysis
7
+ * and conversion both want one canonical shape, so both go through
8
+ * {@link normalizeLinks} before touching a graph.
9
+ */
10
+ /** Canonical link row: [linkId, originId, originSlot, targetId, targetSlot, type]. */
11
+ export type GraphLink = [number, number, number, number, number, string];
12
+ /** Normalize one link entry; undefined when the entry is not a readable link. */
13
+ export declare function normalizeLink(raw: unknown): GraphLink | undefined;
14
+ /** Normalize a whole `links` array, skipping entries that are not readable. */
15
+ export declare function normalizeLinks(raw: unknown): GraphLink[];
package/lib/graph.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * ComfyUI UI-graph link normalization. The frontend changed how saved graphs
3
+ * serialize the `links` array: rows used to be positional
4
+ * (`[id, originId, originSlot, targetId, targetSlot, type]`), while the v0.4
5
+ * frontend writes object entries
6
+ * (`{ id, origin_id, origin_slot, target_id, target_slot, type }`). Analysis
7
+ * and conversion both want one canonical shape, so both go through
8
+ * {@link normalizeLinks} before touching a graph.
9
+ */
10
+ function isObject(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ }
13
+ /** Normalize one link entry; undefined when the entry is not a readable link. */
14
+ export function normalizeLink(raw) {
15
+ if (Array.isArray(raw)) {
16
+ if (raw.length < 6)
17
+ return undefined;
18
+ const [id, origin, originSlot, target, targetSlot, type] = raw;
19
+ if (typeof id !== 'number' || typeof origin !== 'number' ||
20
+ typeof originSlot !== 'number' || typeof target !== 'number' ||
21
+ typeof targetSlot !== 'number') {
22
+ return undefined;
23
+ }
24
+ return [id, origin, originSlot, target, targetSlot, typeof type === 'string' ? type : ''];
25
+ }
26
+ if (isObject(raw)) {
27
+ const { id, origin_id: origin, origin_slot: originSlot, target_id: target, target_slot: targetSlot, type } = raw;
28
+ if (typeof id !== 'number' || typeof origin !== 'number' ||
29
+ typeof originSlot !== 'number' || typeof target !== 'number' ||
30
+ typeof targetSlot !== 'number') {
31
+ return undefined;
32
+ }
33
+ return [id, origin, originSlot, target, targetSlot, typeof type === 'string' ? type : ''];
34
+ }
35
+ return undefined;
36
+ }
37
+ /** Normalize a whole `links` array, skipping entries that are not readable. */
38
+ export function normalizeLinks(raw) {
39
+ if (!Array.isArray(raw))
40
+ return [];
41
+ const links = [];
42
+ for (const entry of raw) {
43
+ const link = normalizeLink(entry);
44
+ if (link !== undefined)
45
+ links.push(link);
46
+ }
47
+ return links;
48
+ }
package/lib/index.js CHANGED
@@ -217,7 +217,9 @@ export async function apply(ctx, entryConfig) {
217
217
  const entries = await client.listUserData('workflows');
218
218
  const library = await store.listWorkflows();
219
219
  return entries
220
- .filter((entry) => entry.type === 'file' && entry.name.endsWith('.json'))
220
+ // Dot entries are ComfyUI bookkeeping (e.g. `.index.json`), not graphs.
221
+ .filter((entry) => entry.type === 'file' && entry.name.endsWith('.json')
222
+ && entry.name.split(/[\\/]/).every((segment) => !segment.startsWith('.')))
221
223
  .map((entry) => {
222
224
  const derived = library.filter((workflow) => workflow.comfyuiFile === entry.name);
223
225
  return {
@@ -319,7 +321,12 @@ export async function apply(ctx, entryConfig) {
319
321
  // service simply never registers it, and the entry config stands as composed.
320
322
  let source = () => resolved;
321
323
  ctx.inject(['settings'], (settingsCtx) => {
322
- settingsCtx.settings.installSection(ctx, COMFYUI_NS, Config, resolved, {
324
+ const settings = settingsCtx.settings;
325
+ if (typeof settings.installSection !== 'function') {
326
+ ctx.logger.warn('comfyui: settings service lacks installSection — settings page stays read-only, entry config stands');
327
+ return;
328
+ }
329
+ settings.installSection(ctx, COMFYUI_NS, Config, resolved, {
323
330
  setSource: (current) => {
324
331
  source = current;
325
332
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-comfyui",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Let the DeepSeek Harness agent smartly drive a local or remote ComfyUI to generate anything, with workflow and asset management panels, per-workflow skill packs, a companion skill and a same-origin media proxy. / 让 DeepSeek Harness 的 Agent 智能驱动本地或远程 ComfyUI 生成任何内容。附带工作流、资产管理面板与技能包管理挂载。配套 skill 与同源媒体代理。",
5
5
  "repository": {
6
6
  "type": "git",