YuChenLiao/dsh-comfyui-workbench
YuChenLiao/dsh-comfyui-workbench has 27 GitHub stars and 1 forks. Star changes are not measured yet. Last checked 2026-11-14 05:11 UTC. Last push 2026-11-11.
Measured star growth
- in 24h
- Collecting history
- in 7 days
- Collecting history
- in 15 days
- Collecting history
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
DSH ComfyUI workbench: asset library + arbitrary workflow runner
Creative & MediaREADME.en.md
Originaldsh-comfyui-workbench
中文 · English
A ComfyUI workbench for DSH: hand your local ComfyUI workflows to the model, and manage which workflows the model is allowed to use.
There are six user-facing panels — Assets, Workflows, Tags, Poses, Costumes, and the ComfyUI terminal — all backed by ComfyUI's own capabilities rather than a parallel state layer. No built-in generation UI: if you want to generate by hand, just open ComfyUI.
This plugin is the successor to
dsh-comfyui-album; asset browsing is inherited from it (shared code lineage). Running both gives you two entry points and two delete buttons — we recommend uninstalling the album plugin.scripts/migrate-album.mjsships only in the repository: if you installed from a package you'll need to clone the repo and run it there.
Nine components
Each is mounted in the profile's cordis.patch.yml as its own loader entry — visible in the host's Settings → Plugins, and individually toggleable.
| entry | kind | responsibility |
|---|---|---|
comfyui-workbench:core |
server | Initializes the shared runtime (ComfyUI client / process supervision / asset index / output dir) and hosts the browser bundle. Its name must be the package root — the host only assembles exports["./client"] for root specifiers |
comfyui-workbench:gallery |
panel | Assets: listing, filtering, thumbnails, deletion, bulk workflow ZIP export |
comfyui-workbench:workflow |
panel | Workflow admin: listing, the enabled toggle, purpose notes, plus /run, /rerun, /config and the service-supervision endpoints |
comfyui-workbench:tags |
panel | Tag library: search four tag categories, filter by semantic category, copy a tag. Browsing is read-only; the "update tags" button refreshes the data from upstream |
comfyui-workbench:poses |
panel + tools | Pose library: 288 poses / 1,439 angles of human reference sketches — search, filter by system and category, save a sketch into uploads as a workflow image input; also registers the comfyui_pose_search and comfyui_pose_fetch tools |
comfyui-workbench:costumes |
panel + tools | Costume library: 1,407 real-world outfits (no people in frame) — search and filter by category, every entry carries English keywords, save an image into uploads as a style reference; also registers comfyui_costume_search and comfyui_costume_fetch |
comfyui-workbench:generate |
tools | comfyui_workflow_list, comfyui_workflow_inspect, comfyui_run, comfyui_run_batch, comfyui_rerun, comfyui_runs, comfyui_node_search |
comfyui-workbench:edit |
tools | comfyui_workflow_edit |
comfyui-workbench:danbooru |
tools | danbooru_lookup, danbooru_lookup_batch, danbooru_sync: character / artist / copyright / general tag library with Chinese names (99,372 tags). Does not touch the ComfyUI runtime, so it can be disabled on its own |
comfyui-workbench:terminal |
panel | ComfyUI terminal: shows child-process output or user/comfyui.log and can start the service from the panel |
Disabling one affects only that one. Turn off gallery, for example and its routes 404 while workflow admin and the tools keep working. Two exceptions: bulk workflow ZIP export needs images and sidecars, so it depends on the gallery; and disabling core disables the whole plugin — the HTTP API and browser bundle both disappear.
Install
# from the repository
dsh plugin --profile desktop add github:YuChenLiao/dsh-comfyui-workbench
# local development
dsh plugin --profile desktop add link:D:/dsh/dsh-comfyui-workbench
Restart DSH afterwards. Ten entries get inserted into cordis.patch.yml (the root core plus nine components); their config is shared through a YAML anchor so you write it once:
- insert:
- id: comfyui-workbench:core
name: dsh-comfyui-workbench
config: &cwf
baseUrl: http://127.0.0.1:8188
autoStart: true
disabledComponents: []
- id: comfyui-workbench:gallery
name: dsh-comfyui-workbench/gallery
config: *cwf
# :workflow / :tags / :poses / :costumes / :generate / :edit / :danbooru / :terminal follow the same pattern
Configuration
| field | meaning |
|---|---|
baseUrl |
ComfyUI address, default http://127.0.0.1:8188 |
outputDir |
Where artifacts land. Leave it empty and they go to <current workspace>/comfyui-output; if no workspace can be resolved it falls back to <plugin data dir>/outputs (GET /config reports outputSource: data in that case, and workspaceProbe shows how detection went: whether cwd was rejected as a host-internal directory and how many paths workspaceRegistry returned). Pin it explicitly if you want a fixed location |
workflowsDir |
Workflow repository, default ~/.dsh-comfyui-workbench/workflows |
autoStart |
Whether to launch ComfyUI when a local installation is detected. Submitting a run, listing workflows and inspecting a workflow all trigger it (waits until /system_stats responds, capped by readyTimeoutMs), so there is no need to start it by hand. Only processes we started are ever stopped |
idleShutdownMs |
How long an idle instance we started is kept. Defaults to 0 (never reclaimed) — /queue only shows the prompt queue, not model downloads, so reclaiming tends to interrupt you mid-download; set 300000 if you want the memory back. When enabled it asks /queue first and anything running or queued postpones it; external instances are never stopped |
disabledComponents |
Components to turn off, e.g. ['panel.gallery']. An empty array means all are on |
timeoutMs |
Per-request HTTP timeout; also the default wait ceiling for comfyui_run |
pollIntervalMs |
Status polling interval while a run is in flight |
roots |
Extra directories to include in the asset library (the current workspace, dsh-comfyui-image's outputDir and the output dir are already included) |
assetRoots |
Extra writable roots. assertWritable takes the union of all authorized roots (roots / workspaces / cwd / output dir / uploads / assetRoots) and rejects writes that land outside it |
Model tools
| tool | what it does |
|---|---|
comfyui_workflow_list |
Lists available workflows: id, title, purpose note, base model, input kinds, output shape. Disabled ones are omitted (hiddenCount says how many). When /object_info is unreachable, it does not flag missing models — that would report "offline" as "missing models" |
comfyui_workflow_inspect |
Without nodeId: the IO surface plus node list. With it: one node's full config. Also reports missing nodes, missing models and available substitutes |
comfyui_run |
Asynchronous by default: returns a promptId immediately, watches in the background and pushes the result into the conversation when the image is done. Supports wait: true for synchronous waiting and promptId for re-checking. Disabled workflows are rejected outright. Starts ComfyUI automatically when it is not running (waits until /system_stats responds, capped by readyTimeoutMs, 3 minutes by default) and submissions queue behind that startup — no need to start ComfyUI by hand, and do not kill it while it is loading or downloading models |
comfyui_workflow_edit |
Edits title / description / tags and single-node parameters. Changes apply to the next run only by default; writing back to the template requires persist + expectHash. Parameters bound to a variable cannot be edited here (param_bound) — pass the variable through comfyui_run's vars instead |
comfyui_node_search |
Searches installed nodes by class name / display name / alias / category / input-output type / package, with four ranking tiers. Returns a one-line summary by default; detail adds inputs and outputs |
comfyui_rerun |
Runs again with the parameters an existing artifact used (or makes variants). Parameters come from the sidecar next to the artifact, so there is no need to copy seed / prompt by hand |
comfyui_runs |
Checks run state: running (state: running), queued (with queuePosition), recently finished or failed. Progress lands in the record only and never enters the conversation, so it will not interrupt the current turn. progress appears only with progressStream on, and only for runs submitted from the panel via /run or /rerun (not tool submissions) — a missing field is not an anomaly; with it on, the percentage resets per sampling node and can jump backwards |
comfyui_run_batch |
Submits several runs at once (one workflow with different parameters, or different workflows). Reports per item — one failure does not affect the others; asynchronous by default |
danbooru_lookup |
Looks up character, artist, copyright and general tags by Chinese name / Japanese name / Danbooru tag (kind narrows it) and returns tags only. Aligning names before writing a prompt keeps generations on-model. 长发 → long_hair, 东方Project → touhou |
danbooru_lookup_batch |
Looks up many names at once (up to 50), local only, no network — fill the gaps with single danbooru_lookup calls (those go online automatically) |
danbooru_sync |
Pulls character / artist tags (including Chinese names) into the local library. Thousands of requests — call it only when the user asks |
comfyui_pose_search |
Finds human poses in the reference library (288 poses / 1,439 angles): search by Chinese name, code or category; returns the id and angle count, no images. Everything is listed by default — onlySafe narrows it to the 98 regular poses |
comfyui_pose_fetch |
Downloads one angle's sketch (or a pose's front view) into the local uploads directory and returns the path, ready to use as a workflow image input (ControlNet / img2img) |
comfyui_costume_search |
Finds outfits in the costume library (1,407): search by Chinese description, tag, English keyword or code; returns the id plus keywords, which can go straight into a prompt |
comfyui_costume_fetch |
Downloads one outfit's image into the local uploads directory and returns the path plus keywords, for use as a costume reference |
A workflow's "available" flag lives in <id>.meta.json under enabled (default true); the panel and comfyui_workflow_edit write the same file.
Danbooru tag library
When writing prompts, the model often knows the Chinese name for a character but not ganyu_(genshin_impact), or the reverse. danbooru_lookup maps between them and returns tags only:
{ "ok": true, "tags": ["ganyu_(genshin_impact)"], "source": "local" }
Context is scarce, so the result carries no postCount, alias list or match kind — the model only needs to know which tag a name maps to.
Failover to a mirror: safebooru.donmai.us is Danbooru's official SFW mirror — same API, same data (we verified post_count, wiki other_names and alias reverse-lookups field by field against the main site). On timeout (8 s without a response), 5xx, 429 or endpoint 404, the plugin switches over and the result is unchanged. Once a mirror has worked, it is preferred over the main site.
Sites running "the same software, a different database" cannot serve as mirrors. aibooru.online reports hatsune_miku at 1,821 posts versus 148,311 on the main site — using it would hand back wrong popularity and wrong results.
Empty local library still works: if the library is empty or has no match, the lookup goes online (source becomes online). Chinese and Japanese names go through search[other_names_match] (the only field Danbooru exposes for reverse alias lookup); plain ASCII names use search[name_matches] with fuzzy matching. So you can use it without ever syncing — just slower, with a network round-trip per query.
The primary data source is danbooru-tags-data-zh (MIT, © 2026 amenorira), and the data ships inside the plugin under data/danbooru/, so it works out of the box without a sync: a community-maintained Chinese translation library covering Danbooru tags with more than 50 posts, including Chinese names, aliases (community nicknames too — Miku's "葱娘" among them) and a one-line note (notes are stored but never returned; the model only needs the tag).
Four categories, 99,372 tags in total:
| Category | Rows | With Chinese name / alias |
|---|---|---|
| character | 35,384 | 98.7% |
| artist | 24,881 | 94.9% |
| copyright | 8,414 | 96.6% |
| general | 30,693 | 98.9% |
general covers poses, clothing, appearance and composition; copyright is work titles (touhou → 东方Project). All four share one 2-gram inverted index: 129 ms to read and parse, 420 ms to build, ~172 MB resident, ~1 ms per query (56 ms linear, roughly 45× faster).
General tags carry semantic categories. Danbooru's tag groups live in a wiki page (tag_groups is not an API resource), scraped into 91 groups / 15,064 tags and folded into eight buckets: action / clothing / appearance / composition / object / theme / explicit / other. The category parameter on danbooru_lookup narrows a search:
danbooru_lookup({ query: 'skirt', category: 'clothing' }) → ["skirt"]
danbooru_lookup({ query: 'sitting', category: 'action' }) → ["sitting", "seiza", ...]
Coverage is limited: those 91 groups hit only 26.9% of the 30,693 general tags directly (8,261), the rest land in "other" — even ponytail and braid are missing from the wiki groups. So categories are an enhancement, search is the main path; when a filter comes up empty the tool says it was filtered out rather than pretending nothing exists. Refresh with npm run danbooru:groups (92 requests, ~2.5 min).
To bring the local library up to date, pick one of these (run them in the directory holding package.json — the repo root, or node_modules/dsh-comfyui-workbench/ in an install):
npm run danbooru:zh # fetch upstream into the plugin data dir (~5 s depending on network; the first run downloads a few MB of CSV, so call it 10 s), replacing the local copy
npm run danbooru:zh -- --kind character # characters only
npm run danbooru:zh -- --dry # just report how many rows would be imported
Note that it writes to the plugin data directory (~/.dsh-comfyui-workbench/danbooru/), never to data/danbooru/ in the repo — that copy is the snapshot shipped with the package. To update the bundled data as well, import first and then copy it over:
cp ~/.dsh-comfyui-workbench/danbooru/*.jsonl data/danbooru/
Supplementary path: you can also scrape Danbooru directly (tags.json plus per-tag wiki_pages.json). Data is fresher and you get the newest wiki aliases, but the request volume is large — good for a hot slice only:
npm run danbooru:sync # post count >= 100, at most 3000 per kind
npm run danbooru:sync -- --min 0 --max 0 # everything (400k+ character tags, around 30 hours at measured speed)
--min is the popularity threshold (search[post_count]=>N) and --max caps rows per kind; 0 means unlimited. A full sync is slow — the bottleneck is per-tag wiki lookups, measured at roughly 3–4 tags/second with 8 concurrent requests, so 400k tags take about 30 hours. That is what --max is for: pull a hot slice first (e.g. --min 300 --max 400, about four minutes for both kinds) and widen later.
If the local library is empty (an older install, or a setup where only lib/ was copied — github: installs pack by files, link: points at the repo and both carry data/), lookups fall through to online (source: online). Function is unaffected, but it is far slower — about 1 ms for a warm local query (one 2-gram index per category; the first query in a fresh process takes ~530 ms, because it parses ~20 MB of JSONL across the four categories and builds the indexes), versus about 1 s online for a plain ASCII tag (one fuzzy match per category), about 2 s for a single-candidate Chinese name, and 4–6 s when a Chinese name has several candidates (wiki reverse lookup, then a post_count re-query per candidate); worse on a bad network. Results are also narrower: locally, "圣园未花" yields mika_(blue_archive) plus its swimsuit and track variants — three tags; online returns only the main tag.
To add data:
# as above: cwd must be the directory holding package.json, or npm reports a missing script
cd <install dir>/node_modules/dsh-comfyui-workbench && npm run danbooru:zh
# or from any directory, by script path (cwd-independent)
node <install dir>/node_modules/dsh-comfyui-workbench/scripts/import-danbooru-zh.mjs
It writes to the plugin data directory (~/.dsh-comfyui-workbench/danbooru/), not to data/ inside the install — the latter is the bundled snapshot and is never written at runtime (the install directory may also be read-only).
Adding a single row by hand is possible, but three things matter:
First, replacement is per kind, per file: writing only characters.jsonl affects characters; artists are untouched and keep using their own file (falling back to the bundle only when no artists.jsonl exists in the plugin data directory). Second, >> appends: if you already ran danbooru:zh and have a full library, appending just adds a row at the end (it only grows until the next import); only when you rely on the bundled snapshot and have no local copy does creating that file reduce the kind to that single row (shadowing all 35,384 characters / 24,881 artists). Third, restart the plugin afterwards: if that kind was already queried, its tags are in the in-memory cache and changes are invisible until a restart.
mkdir -p ~/.dsh-comfyui-workbench/danbooru
echo '{"tag":"mika_(blue_archive)","kind":"character","postCount":16024,"names":["圣园未花"]}' \
>> ~/.dsh-comfyui-workbench/danbooru/characters.jsonl
The format is JSONL, not the upstream CSV (the runtime reads <kind>s.jsonl only; CSV files placed there are ignored).
Both danbooru:zh and danbooru:sync write by whole-file replacement: they rewrite <plugin data dir>/danbooru/<kind>s.jsonl (temp file, then rename, so an interrupted run never leaves half a file). Any rows you appended by hand are overwritten by the next import or sync.
Entries with no aliases are not left blank: those rows (473 characters and 1,258 artists in the bundled data) have an empty names array but are still findable by tag — looking up chainsaw_man_(character) returns itself, because danbooru_lookup always returns the name Danbooru recognizes.
The scrape path (danbooru:sync) filters out derivative-event names, year suffixes and plain-ASCII variants; the Chinese library import (danbooru:zh) takes everything, since that data is already curated and includes community nicknames and older translations. Ship names (one name covering two characters, like レイマリ) are detected mainly by whether an alias is shared across multiple tags — more reliable than guessing from the text and this does not misfire on 初音ミク; when shared, the alias stays only on the hottest record. There is also a conservative character filter (values containing × or &, literally containing cp, or a lone relationship word). The result is that each alias maps to at most one candidate — no more "one name returns two people, both flagged exact".
Rate limits and network errors during a sync are retried with backoff; rows that still fail are reported by count in the log (those rows are missing aliases and a re-run fills them in).
This component can be disabled on its own (host plugin page, or disabledComponents: ['tool.danbooru']) without affecting the generation path.
Hiding NSFW
The asset toolbar has an ellipsis to the right of the refresh button — hover it to reveal "Hide NSFW". Once enabled, assets judged NSFW disappear from the list and the button shows how many were hidden.
The judgment is a heuristic word list, not a classifier. It scans the file name, the positive prompt, the base model name and the workflow id:
- Strong signals (
nsfw,explicit,hentai, genital terms, and Chinese phrases like完全裸露/完整裸露/赤裸/全裸) match on a single hit - Medium signals (
nude,breast,lingerie, plus裸露and乳房) need two to trigger — in anime tagsbreasts+nipplesare standard for swimsuit and anatomy references, so a single hit would always misfire
Negative prompts are not considered. The officially recommended negatives for Danbooru-family checkpoints (AnimagineXL, Pony) literally contain nsfw — judging on that would hide every image from a whole workflow setup, even when the user is in fact excluding adult content.
Filtering happens at render time only: the data stays in memory, so toggling back is instant with no refetch. With the filter on, "select all" selects only what you can see — otherwise one delete would take the hidden items with it.
Tag library panel
The third tab: search the four Danbooru tag categories (99,372 tags), grab a tag plus its Chinese name, paste it into a prompt. One input matches tag, Chinese name and alias at once — 长发, long_hair and 长髪 all land on long_hair.
- Category chips show their real counts: action / clothing / appearance / composition / object / theme / explicit / other, each chip labelled with how many tags it can filter to. Categories cover only 26.9% of general tags (
ponytailandbraid, among the most common words, are not in Danbooru's wiki groups), so when a search comes up empty check the count first — that means "not covered by a category", and dropping the chip brings the tag back. - Read-only browsing, with copy: the copy button yields the bare tag, with no draft state. To build prompts up over time, use the
{{variable}}workflow templates. - First paint takes about 550 ms (129 ms to read + 420 ms to build the index for all 99,372 tags); queries after that take 1–3 ms. Disable it with
disabledComponents: ["panel.tags"].
Pose library
The fourth panel: 288 poses / 1,439 angles of human reference sketches, for when a workflow needs a "how does the body stand / sit / lie" reference. The sketches come from ギア's (@giaaibusi) AI Pose Gallery; the Chinese names, action categories and the four-system split come from Anhao's Chinese mirror. This plugin only aggregates — not a single sketch is redrawn here. See data sources and credits.
- Images do not ship with the plugin. The repo only carries names, categories and image addresses (
data/poses/catalog.json, 253 KB). Sketches are fetched on demand through a server-side proxy and cached to<plugin data dir>/cache/poses/. The browser never talks to the third-party image host — that would hand out the user's egress IP for a thumbnail and also bypass our cache and failure short-circuit. Licensing is in data/poses/NOTICE.md: the source material may not be used commercially, and this project's MIT license covers none of the sketches. - Fetch once, then it is local: 32 entries in memory plus a disk cache (256 MB cap, evicting least-recently-used), with concurrent requests for the same view collapsed into one upstream call. A few consecutive upstream failures short-circuit for 30 seconds, so the panel says "sketch unavailable" instead of letting forty thumbnails each sit through a 20 s timeout.
- Five angles: front, three-quarter, side, high, low — switchable right on the card. 287 poses have all five; one has four.
- Everything is available by default: all 288 poses show up, no content filtering. The 190 poses flagged adult (the sexy and couple systems, plus two explicit categories) just carry a small badge on the card; tick "Safe only" if you want to narrow it — that's the user's call, not the plugin's.
- Using one: "Get file" saves the sketch into uploads and the returned path is your workflow's image input; from the model side,
comfyui_pose_searchfinds it andcomfyui_pose_fetchfetches it. The panel never calls a generation API itself —comfyui_runstays the only path to an image. - Search is a linear scan over 288 poses (microseconds). No inverted index here: 99,372 tags justify a 2-gram index, 288 poses do not — building one would be a net loss.
- Disable it with
disabledComponents: ["panel.poses"]. Refresh data withnpm run poses:import.
Costume library
The fifth panel: 1,407 real-world outfits (no people in frame), for finding costume references. The outfit set COSTUME-DB-01 comes from ギア's (@giaaibusi) AI Costume Gallery; the Chinese names, Chinese tags and English keywords come from Anhao's mirror. The integration is identical to the pose library — metadata ships with the plugin, images are pulled through the server proxy on demand.
- Every entry carries English keywords — that is what makes this dataset more directly useful than the pose one. Each outfit has a string like
ivory frill knit denim mini skirt coat socks white sneakers, shown on the card and copyable in one click, ready to paste into a prompt. Coverage across all 1,407 is 100%. - Images do not ship:
data/costumes/catalog.json(560 KB) holds only codes, Chinese descriptions, Chinese tags, English keywords and image addresses. Images live underthumbs/costume/andimg/costume/onai-pose-gallery.anhao.appin webp (the pose library uses jpg/png), so the two path allow-lists are separate and do not accept each other's shapes. - Eight categories: spring 215 / summer 254 / autumn 244 / winter 253 / lingerie & sleepwear 199 / sexy 140 / swimwear 54 / party 48.
- All 1,407 are listed by default — no content filtering. The 393 flagged adult (lingerie, swimwear, sexy) only carry a small badge; tick "Safe only" to narrow down to 1,014.
- Caching and short-circuiting share one implementation with the pose library (
createImageCache), differing only in cache directory (cache/costumes/) and path allow-list — duplicating that logic would drift. - Licensing differs from the pose library: upstream allows using these images as creative reference (including in edited and commercial works) but forbids redistributing the image data itself. See data/costumes/NOTICE.md.
- Disable it with
disabledComponents: ["panel.costumes"]. Refresh data withnpm run costumes:import.
Terminal panel
The sixth panel (after Assets, Workflows, Tags, Poses and Costumes) shows ComfyUI's command-line output. What the service is doing, where it is stuck, how far a model has loaded — that only exists in the terminal, not in the GUI.
- Externally started instances work too: a process the plugin launched has its stdout/stderr piped in; an external instance (one Desktop started itself) has no accessible stdout, so the panel reads the
user/comfyui.logit writes. Only one path is active at a time — ComfyUI's logger attaches both a Stream handler and a File handler, so reading both would duplicate every line. - When the service isn't running the panel offers a Start button, launching it directly instead of making you go generate an image first.
- Polls for increments once a second (with a cursor, so the full screen is not re-downloaded); auto-follows the tail but never yanks you back if you scroll up.
- Pause, keyword filter and clear are available. The buffer keeps the last 2,000 lines and tells you when earlier output rolled off.
- ANSI escapes (color, progress-bar
\rredraws) are stripped before storage — otherwise the panel is a wall of garbage. - The child process is forced to
PYTHONIOENCODING=utf-8; without it Python on Windows encodes its output with the console code page and Chinese text turns into tofu.
Disabling it affects only this panel (panel.terminal); everything else keeps working.
Artifacts and notifications
- Artifacts go to
<current workspace>/comfyui-outputby default, with a<file>.jsonsidecar next to the main artifact (prompt, seed, workflow, original dimensions, promptId). When one run yields several images, only the first carries a sidecar. - Images are returned as paths plus original pixel dimensions, never inline bytes: the host rebuilds requests per
{type:'image', attachment}block and a raw base64 block throws and poisons every later request in that session. - When a run finishes, the plugin pushes the result into the conversation (
Agent.followup, sourceplugin:dsh-comfyui-workbench); the success, failure and timeout paths each deliver exactly once.
Layout
src/client/styles.css shell styles (each panel keeps its own .css next to the component)
src/components/panel-gallery/ assets: server/(routes/scan/index/thumbs) + client/
src/components/panel-workflow/ workflow admin: server/(routes/run/import/config) + client/
src/components/panel-tags/ tag library: server/(index/search/sync job) + client/
src/components/panel-poses/ pose library: server/(catalog/image proxy/tools) + client/
src/components/panel-costumes/ costume library: server/(catalog/routes/tools) + client/
src/components/panel-terminal/ terminal: server/(log tail) + client/
src/components/tool-generate/ generation tools
src/components/tool-workflow-edit/ workflow editing tool
src/components/tool-danbooru/ Danbooru tag lookup tools
src/shared/ shared logic: host / comfy / workflow / run / tools / http / authz / media
src/http/ HTTP prefix and dispatch (router)
src/client/ UI shell: tabs, api, i18n, icons
src/index.ts package-root entry (browser bundle + shared runtime init)
src/entries/ nine component entries (core is src/index.ts)
src/runtime.ts cross-entry shared runtime (anchored on ctx.root)
The first step of npm test (node tests/arch.mjs) enforces three boundaries: shared is one-directional internally (host at the bottom), a component's server never imports its client and shell modules never import a specific component. tests/arch-write-routes.mjs right after it walks the router's three tables and demands a boot() + real-fetch case for every write endpoint — unit tests that call a component handler directly have repeatedly passed while the real assembly returned 405. Coverage is currently 9/14; the remaining five (/delete, /delete-many, /assets/rebuild, /service/stop, /free) sit in the script's KNOWN_UNTESTED allow-list, which is only meant to shrink.
Development
npm install
npm run build # src → lib/index.js (host) + lib/client.js (browser) + lib/entries/*.js + lib/{workflow,store,run,comfy}.js (for tests)
npm test # architecture boundaries + write-endpoint coverage + class names + host API + client render + per-component behaviour
npm run typecheck
npm run accept # tick items off against a running DSH (real-environment acceptance); -- --run does one full generation
node scripts/compat.mjs # workflow compatibility scan: missing nodes / missing models / image inputs required
node scripts/smoke-workflows.mjs runs each workflow for real (parameters squeezed to 512 on the long edge, 1 step) to verify only that it submits, generates and retrieves.
Importing a workflow: use the import button at the top right of the panel and paste the JSON you exported from ComfyUI with Export (API) — or pick the file directly. For batches, node scripts/import.mjs <workflow.json> (--list shows what's there). API format only — for UI-format files, use Export (API) inside ComfyUI.
Compatibility
| plugin | relationship |
|---|---|
dsh-comfyui-image |
Companion: reads the images and sidecars it writes, plus its outputDir config. Its code is never imported |
dsh-better-sidebar |
Optional: registers a tab; without it the native sidebar entry still works. The tab must be added once from the sidebar's "+" menu |
dsh-comfyui-album |
Predecessor: uninstall it. To migrate, run node scripts/migrate-album.mjs --write inside this repository (the script is not shipped in the package) |
Docs
- Design: docs/design.md
- Changelog: CHANGELOG.md
Data sources and credits
None of these datasets are this project's work. The plugin does aggregation, field
normalisation and indexing; not a single image ships with it (metadata ships, images are pulled by
the consumer through a server-side proxy on demand). The licences differ from dataset to dataset and
are spelled out in each NOTICE.md; this section just names the authors.
Tag library: Danbooru and its Chinese translation set
data/danbooru/{characters,artists,copyrights,general}.jsonl (99,372 rows in total) comes from
amenorira/danbooru-tags-data-zh
(MIT, © 2026 amenorira), which covers Danbooru tags with more than 50 posts along with their
Chinese names, aliases and notes. The data ships with the plugin under data/danbooru/, and the
upstream license is included verbatim (see data/danbooru/LICENSE and
NOTICE.md).
This plugin performs only these transformations: field-name mapping (CSV → JSONL), row filtering by
category, names normalization (Chinese name first, drop entries identical to the tag, drop empty
strings, deduplicate within a record) and shared-alias removal (aliases used by several records are
kept only on the hottest one). Tag, Chinese name and note text are untouched. Re-import with
danbooru:zh above when upstream updates.
Tag groups: Danbooru's wiki
data/danbooru/groups.jsonl (91 groups / 15,064 tags) comes from Danbooru's
tag_groups wiki article — that grouping exists
only as a wiki page, not as an API, so it is parsed from the article (npm run danbooru:groups).
Group names and membership belong to Danbooru and its contributors; this plugin only merges the 91
groups into the eight categories the UI shows, and indexes them.
Pose library: ギア's AI Pose Gallery
| 288 pose groups (1,439 angles) and the original Japanese data | ギア (@giaaibusi), AI Pose Gallery |
| Chinese names, action categories and the four-system split | Anhao (anhao/ai-pose-gallery-zh), mirror at https://ai-pose-gallery.anhao.app |
Only the mannequin set is used here (depth maps, the portrait library and the anime costume set are not). The plugin aggregates and indexes them and nothing more. The source material may not be used commercially, and this project's MIT license covers none of the sketches — see data/poses/NOTICE.md.
Costume library: ギア's COSTUME-DB-01
| 1,407 outfits and the original data (COSTUME-DB-01) | ギア (@giaaibusi), AI Costume Gallery |
| Chinese names, Chinese tags, English keywords | Anhao (anhao/ai-pose-gallery-zh), mirror at https://ai-pose-gallery.anhao.app |
The licence differs from the pose library: upstream allows using these images as creative reference (including in edited and commercial works) but forbids redistributing the image data itself, at original size or processed. See data/costumes/NOTICE.md.
Design reference
The server-side image proxy approach started from 澜资's gen-ai-pose-image plugin ("metadata ships, images on demand"); this plugin added a disk cache cap, request coalescing, failure cooldown and magic-byte validation on top. The code is a rewrite and reuses none of its source.
License
MIT
Explore related projects
ytdl-org/youtube-dl
Command-line program to download videos from YouTube.com and other video sites
141,560 starsexcalidraw/excalidraw
Virtual whiteboard for sketching hand-drawn like diagrams
135,658 starsgodotengine/godot
Godot Engine – Multi-platform 2D and 3D game engine
120,113 starsmrdoob/three.js
JavaScript 3D Library.
118,345 stars