AI Art Engine 中文
Menu

Developers · Marketplace

Plugin Marketplace Developer Docs

For developers publishing content to the plugin marketplace: what the marketplace contains, what the content looks like, how it is validated and distributed, and what the client does with it after install. The author-side, field-by-field writing rules live in the content repository (see “Content repository” below); what follows is the client contract and the delivery pipeline — the part you can only see on the app side.

For the user-facing side of the marketplace, see the manual §5.7 Plugin marketplace.

Related client implementations: src/shared/workflowMarket.ts (contract and parsing), src/main/services/marketPipeline.ts (download / cache / atomic install), src/main/services/workflowMarketService.ts (catalog, compatibility, install bookkeeping), src/renderer/src/views/MarketplaceView.vue (the UI).

1. What the marketplace contains

The marketplace is a separate window (opened by the “Marketplace” button in the top bar, immediately to the left of “Settings”) with four tabs:

Tab Content Hosted at
MCP The built-in aiartengine tool service, the Blender tool surface, and third-party MCP servers the user adds themselves (http / stdio) In-app + the user’s local config
Skills Built-in skill snapshots, custom skills the user drops into the skills directory, and skill bundles installed together with a workflow $DSH_HOME/skills
Workflows Entries from the remote workflow repository (may ship a skill bundle) Content repository (below)
All The three kinds above mixed together, with a search box —

Only Workflows are distributed from a remote repository; MCP and Skills come from local / built-in sources. A workflow entry itself may ship one skill bundle — that is what sets this marketplace apart from “a download site”: installing a workflow also installs the “operator’s manual for the Agent”.

Content repository

  • Primary source: https://github.com/Justin-sky/ai-art-engine-workflow (raw path /main)
  • Mirror: https://gitee.com/beijing_blue_whale_era_zhangjian/ai-art-engine-workflow (raw path /main)

The two repositories must be byte-for-byte identical — any difference produces “same version number, different content”, the hardest class of state to debug. Syncing is described in the repository README.

The author-side field rules, review criteria and per-item rejection reasons live in the content repository’s CONTRIBUTING.md; the directory layout and the meaning of each workflow.json field are in that repository’s README.md. Those rules are not repeated here — this page only covers the client behaviour they don’t.

2. The delivery pipeline: no step can be skipped

Contributor submits workflows/<id>/{workflow.json, cover.png, skill/**}
   ↓  CI validation (validate.mjs + unit tests)
   ↓  build-index.mjs derives index.json from disk (including the skill file list)
Client fetches index.json (primary source first, falls back to the mirror on failure)
   ↓  user clicks “Install”
Downloads workflow.json + cover.png (+ skill files)
   ↓  atomic install (staging → rename; a failure leaves nothing half-installed)
<userData>/workflows/<id>/            the workflow itself
   ↓  within that same install
$DSH_HOME/skills/<skill.name>/        skill bundle (installed together with the workflow)
   ↓
the next dsh conversation picks up the skill → injects <available_skills> → the Agent loads it on demand

2.0 The 15 official plans are exported, not hand-written

The app's built-in One-Click Workflow presets (src/shared/graph/aiWorkflowPresets.ts) and the 15 official marketplace entries (game-ua-video … anim2d-gif) are two copies of the same graph. The marketplace has to be independently distributable and the presets have to work offline, so both must exist — but they must not be kept in sync by hand:

npm run export:market        # regenerate each workflow.json's plan + requires.nodeTypes from the presets
npm run check:market-plans   # verify only; exit code 1 on any mismatch (this is what CI runs)
  • It writes exactly two fields: plan and requires.nodeTypes (the former is the content, the latter its derived value — the marketplace validator requires them to agree). Metadata, cover images and skill/ stay hand-written; not a byte of them is touched.
  • The marketplace repo defaults to ../ai-art-engine-workflow; override with --dir <path> or AAE_WORKFLOW_MARKET_DIR.
  • So to change an official workflow's graph, change the app preset and re-export; editing the plan on the marketplace side is treated as drift. Third-party workflows have no built-in preset: right-click the canvas and pick “Export as marketplace workflow”, fill in the metadata and a cover, and the app writes the current graph as workflows/<id>/{workflow.json, cover.png} (requires.nodeTypes is derived from the plan; runtime write-back fields and any parameter the node type does not declare are kept out of the pack). When it finishes, run node scripts/build-index.mjs && node scripts/validate.mjs in the repo as prompted. The market ids used by the built-in presets (those 15) are refused — their plan can only come from a preset.
The failure mode without this check is worth spelling out: the built-in One-Click Workflow and the marketplace-installed workflow end up sharing a name and version while building two different graphs, and neither side reports an error — users just conclude the model is flaky.

2.1 Derived fields in the index are generated by scripts — never hand-write them

Every one of these fields in index.json is derived from disk by scripts/build-index.mjs:

  • requires.nodeTypes (which node types the workflow uses), requires.appMinVersion
  • nodeCount / edgeCount / sizeBytes
  • skill: the skill bundle’s name, description, whether it contains scripts, its file list, and its total size

The last one matters most, and the reason is worth writing down:

A raw source has no directory-listing API. The client must know every file’s path up front in order to download them one by one, so the skill bundle’s file list can only be derived server-side (by the repository scripts). Having the script generate that list from disk makes “list drifts from disk” impossible — and that drift is exactly the failure a hand-written list is guaranteed to hit.

workflow.json does not carry the skill file list: the only declaration is the fact that skill/SKILL.md exists. CI compares the index against the disk-derived result, and a hand-edited index is rejected.

2.2 Why skill bundles carry the wf- prefix

A skill bundle lands in $DSH_HOME/skills/<name>, and the app’s built-in skills live in that same directory (flat <kebab-id>.md), scanned at the same rank by dsh. So a marketplace skill bundle’s name is forced to wf-<workflow id>, ruling out a name clash with built-in skills at the source. The repository validator blocks this item by item.

2.3 Skill bundle file layout

workflows/<id>/skill/
  SKILL.md          required. frontmatter: name(wf-<id>) / description / workflow: <id>
  references/       optional. port and parameter cheat sheets, long contracts, examples
  scripts/          optional. see §4
  assets/           optional
  • Only the three subdirectories above are allowed; symbolic links are not.
  • Single file ≤ 128KB, whole bundle ≤ 512KB, file count ≤ 40 (the client downloads file by file, so there has to be a ceiling).
  • description ≤ 500 characters: dsh’s skill catalog truncates it, and it is the only thing the Agent can see before deciding whether to load this skill — write “when should this be loaded”, not a feature list.

2.4 Node types mentioned in the body must actually exist

The repository validator checks the backtick-wrapped x.y tokens in a skill’s body: if x is a known node namespace (asset / image / play / ui / model / spatialWorld / frame / note / beat / episode / anim / comic / svg / video / voice / media / output / graph / prompt / decisions …), that token must be a real node type.

Why it deserves its own rule: a skill is an operator’s manual written for the Agent, and the Agent will follow the typeId values in it when it builds a graph with graph_edit. Invent a type that doesn’t exist (asset.pic and the like) and the Agent writes a broken graph, with nothing at runtime to catch it — it only shows up as “the workflow mysteriously won’t run”. Filtering by namespace is there so that spellings like cover.png / references/ports.md / SKILL.md are not caught by mistake. Write parameter names in their own backticks (for example generateAspectRatio) rather than as the dotted form type.param.

3. Client install contract

3.1 Cards and actions

Workflow cards carry Install / Update / Reinstall / Uninstall and Details directly, with no need to open the details page first. Card badges show Includes skill or Includes skill · Includes scripts — so the user knows before installing that the Agent side will receive an extra manual (and the same applies, more so, when scripts are involved).

An installed skill bundle appears in the marketplace’s Skills tab (its source is labelled “Skill bundle”). Skill management actions (open directory / generate an example template / import a custom skill) are also in the marketplace window — they moved there from the settings page when the marketplace was redesigned, and the settings page no longer has a separate skills section.

3.2 Atomicity and rollback

Installation goes through marketPipeline: download into staging first, rename into place only once everything is ready, clean up on failure. A skill bundle and the workflow it belongs to live and die together: if the skill fails to install (or fails validation), the workflow itself is uninstalled as well and the failure reason is returned. The reasoning is direct — a card that says “Includes skill” while the skill never landed means the UI is lying. Install is idempotent, so retrying is all it takes.

3.3 Pre-install validation (otherwise “installed but invisible”)

dsh silently ignores an invalid skill (it writes one log line and skips it). So the client validates SKILL.md during the staging phase: does the frontmatter exist, does name equal the name in the list, is description non-empty, did it use one of the legacy camelCase keys that make dsh throw outright (disableModelInvocation and friends). If it fails, the install fails with a concrete reason and never leaves behind a skill bundle that is installed but does nothing.

3.4 Uninstall

Uninstalling a workflow also deletes the skill bundle that workflow installed — only the one directory name recorded in the bookkeeping, never a bulk delete by prefix (the user may have created a skill with the same name). A skill bundle is not a separately uninstallable artifact.

3.5 Catalog refresh

Opening the marketplace window force-refreshes the catalog (so pushing new content to the repository shows up just by reopening the window, with no need to wait for a cache TTL). When a refresh fails but content is already on hand, the client keeps the previous list and shows a soft hint instead of replacing existing content with an error. The cache is written to disk per source: primary and mirror each get their own copy, so mirror cache is never mistaken for primary data.

5. Executable-code node (script.run) and consent

§4 is about scripts inside a skill bundle (skill/scripts/, files the AI agent runs on the local machine). This section is a separate, independent path: executable code inside the workflow’s own plan.

script.run is a node type whose code (JavaScript) is a parameter written into the plan, run by the app’s graph execution engine in the renderer process — not the harness shell. It runs in a restricted sandbox (QuickJS WASM, src/shared/graph/execute/scriptSandboxQuickjs.ts): no file system, no network, no require, no host globals. Only three things cross the sandbox boundary — upstream inputs in, port outputs out, and a set of governed capability calls await om.<capability>(...): the <capability> must be declared in the node’s capabilities whitelist, and every om.* forwards to an app-governed capability (provider / key / quota / log), not a back door. Plus a timeout (deadline interrupt) and a memory cap.

5.1 Detection and derivation (repo + client, same rule)

  • The repo build-index.mjs derives hasExecutableCode: true and a per-node script summary (node key / title / declared capabilities / code char count / short hash) into index.json; contributors do not hand-write these fields (same as requires.nodeTypes and the skill manifest).
  • The client parseWorkflowMarketEntry / parseWorkflowBundle read those fields and re-validate.
  • The validator checks each script.run node’s bounds: code non-empty and < 100000 chars, capabilities only from the whitelist, port ids valid and unique, limits within hard caps (timeout ≤ 120s, mem ≤ 512MB). Empty / oversized code — things that cannot be normalised — reject the whole workflow (the code is part of the workflow and, unlike a bad skill, cannot be dropped to degrade). Out-of- whitelist capabilities and over-cap resources are normalised in place (dropped / clamped) rather than rejecting the whole thing.

5.2 Install time: executable code needs per-install consent; declining means not imported

  • Workflows with a script.run node show a Contains executable code badge (distinct from a skill’s “includes scripts”).
  • Before install a confirm dialog lists each script node’s declared capabilities and code size.
  • OK = install and allow it to run; Cancel = do not install this workflow — this differs from skill scripts’ “Cancel skips only the scripts and installs the rest”, because the code is part of the workflow and without it the workflow doesn’t stand.
  • Asked on every install, never remembered, no “always allow”. The main process only accepts the literal scriptConsent === true.
  • Locally authored is exempt: script nodes you create / edit in the app yourself don’t go through the marketplace consent flow (you wrote them); only importing from the marketplace gates.
Relationship to skill scripts: the two paths consent independently. A workflow can be both “includes skill · includes scripts” and “contains executable code”, in which case the two dialogs ask separately.

6. Compatibility and versions

  • The index schemaVersion is currently 1; an index newer than the client makes the client report plainly “please update the app” rather than half-render a misleading empty marketplace.
  • An entry may declare requires.appMinVersion: when the app version is below it, the card is disabled and the reason is shown.
  • requires.nodeTypes is derived by the script from the plan: when an older app is missing a node type, that is determined up front and surfaced before install, instead of materialising into a broken graph later.
  • The same content must bump its version: content at an existing version must not be changed again.

7. Local debugging

7.1 In the content repository

node scripts/validate.mjs          # format / dependencies / index consistency / all skill bundle rules
node scripts/build-index.mjs       # rebuild index.json (the derived fields)
node scripts/build-index.mjs --check   # what CI runs: is the index consistent with disk?
node --test scripts/validate.test.mjs  # unit tests for the validator itself

validate.mjs and CI run the very same code — passing locally while failing in CI is not something that should happen.

7.2 On the app side

  • Custom sources: the data layer supports setting workflowMarket.source to one or more addresses (blank / comma-separated; once set, only what you set is used, with no official source mixed in, so fallback can’t lead your debugging astray). ⚠️ The settings UI does not expose this input yet — it is a data-layer capability.
  • To verify “did the Agent really load the skill”, the most reliable evidence is the dsh session log ($DSH_HOME/sessions/<workspace>/<session>/session.v3.jsonl.zstd): it shows the tool/call with name: "skill" and arguments: {"name": "wf-<id>"}, followed immediately by the <skill_content> result. Don’t just look at the UI — the UI cannot tell you whether a skill was loaded.

8. Common rejection reasons (client-side view)

Beyond the ones listed in the content repository’s CONTRIBUTING.md, these directly produce “installed but not in effect”:

Symptom Cause
The Agent side has no such skill after install The SKILL.md frontmatter is invalid (dsh silently ignores it). The client’s pre-install validation exists to turn this failure into one readable error ahead of time
The skill name clashes with a built-in skill name is missing the wf- prefix
The description gets truncated on the Agent side description exceeds 500 characters
Files don’t match after an update index.json was hand-edited; it must be derived by build-index.mjs
A node type written in the skill can’t build a graph The body contains a typeId that doesn’t exist (the repository validator catches it, but re-run it after editing the body)
Features unavailable after the user declines scripts The skill put its critical steps only in scripts/; scripts must be optional