NINKCH/dsh-artcraft ↗★ 1

@ninkch/dsh-artcraft

通过AI工具和API驱动ArtCraft开源创意套件 适合需要让AI自动操作PhotoCraft等Rust图像/视频编辑软件的创作者。

套件
@ninkch/dsh-artcraft
相容性
待驗證
版本
0.2.3
授權
Apache-2.0
最近更新
2026年10月10日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:NINKCH/dsh-artcraft

ArtCraft Bridge

Drive the open-source ArtCraft studio apps — PhotoCraft, FilmCraft, EffectCraft — from DeepSeek Harness, in chat or from your own UI.

English | 简体中文

@ninkch/dsh-artcraft · a DeepSeek Harness plugin · Apache-2.0


Why this exists

ArtCraft is a family of free, fair-source reinventions of Adobe's studio tools, written from scratch in pure Rust: PhotoCraft (Photoshop), FilmCraft (Premiere Pro), EffectCraft (After Effects) and more. They share one superpower that most desktop apps don't have: every menu item, slider and dialog is a command, and the exact same commands can be driven from a headless CLI, a JSON control channel, or an MCP server. The apps were built for agents from day one.

This plugin brings that surface into DeepSeek Harness, so an AI agent can operate the apps — not just talk about them:

  • In chat — four agent tools let any Harness session check the toolchain, run CLI commands, send control-channel commands to a running app, and read/write files in a dedicated data root.
  • In the browser — a same-origin HTTP API (/artcraft/api/*) that any Harness web UI can call, no CORS, no keys. It was built for 无限画布 / infinite-canvas, a node-based AI short-drama storyboard canvas, but any front end can use it.
  • On disk — one dataRoot per machine where assets, project files and rendered output live, laid out per project (media/ · craft/ · out/), so generative pipelines and the desktop apps meet on common ground.

A typical AI short-drama loop looks like this: generate storyboard frames with a model → batch color-grade and sharpen them in PhotoCraft → render an animated title in EffectCraft → assemble the final cut in FilmCraft with grading, mixing and burned-in subtitles → export H.264. Every step after "generate" runs through this plugin, headless.

How it fits together

┌──────────────── DeepSeek Harness web UI (browser) ────────────────┐
│   e.g. infinite-canvas: ① upload assets  ② start jobs  ③ fetch   │
└─────────────────────────────┬─────────────────────────────────────┘
                              ▼  same-origin fetch, no CORS
┌──────────────── DSH Host · @ninkch/dsh-artcraft ────────────────┐
│   HTTP routes /artcraft/api/*   +   agent tools artcraft_*        │
│        → child processes (the CLIs)  →  TCP control channel       │
└──────┬───────────────────────┬───────────────────────┬───────────┘
       ▼                       ▼                       ▼
  photocraft-cli          filmcraft-cli          effectcraft-cli
  batch grading, PSD      EDL/XML in-out,        .ecproj (JSON) render,
  headless edits          cut · grade · mix      Lottie export
       └───────────────────────┴───────────────────────┘
                               ▼
              /
/{media, craft, out}

Everything runs locally. No cloud round-trips, no accounts, no telemetry.

Requirements

  • DeepSeek Harness (desktop), any platform the Harness supports.
  • ArtCraft apps — grab what you need from getartcraft.com or the storytold GitHub releases. The portable/installer builds ship the CLI binaries on Windows and Linux (photocraft-cli.exe …); macOS ships them as a separate notarized zip. Versions tested: PhotoCraft 0.6, FilmCraft 0.5, EffectCraft (early).
  • Nothing else. No Python, no FFmpeg — the ArtCraft apps bring their own codecs.

Setup

1. Get the plugin onto disk (this repository). Any folder works; keeping it inside the Harness workspace next to your other local plugins is the convention.

2. Register it with your Harness profile. A Harness profile is a pnpm package at C:\Users\\.dsh\profiles\ \ (desktop profile by default). Two edits:

// package.json in the profile
{
  "dependencies": {
    // ...existing deps...
    "@ninkch/dsh-artcraft": "^0.1.1",                                    // ← from npm
    // or, for local development, a link into this repository:
    // "@ninkch/dsh-artcraft": "link:D:/code/deepseek-plugin/dsh-artcraft"
  },
  "dsh": { "profile": { "bundles": [
    // ...existing bundles...
    "@ninkch/dsh-artcraft"                                               // ← add the name
  ] } }
}

Then link and restart the Harness once (Host-side code loads at startup):

pnpm install --dir C:/Users//.dsh/profiles/desktop

If your Harness build ships a plugin manager UI that installs from a folder, that works too — it ends up writing exactly the two lines above.

3. Configure artcraft.config.json next to index.js (copy from artcraft.config.json.example; the file is git-ignored and re-read on every request — no restart needed for path changes):

{
  "dataRoot": "D:/craft-workspace",              // where assets & renders live
  "bins": {
    // three accepted forms: a directory (the -cli binary is appended),
    // a full path to the executable, or a bare name looked up on PATH
    "photocraft":  "C:/craft/photocraft-0.6.0-windows-x64-portable",
    "filmcraft":   "C:/craft/filmcraft-0.5.0-windows-x64-portable",
    "effectcraft": "C:/craft/effectcraft-windows-x64-portable"
  },
  "control": { "filmcraft": 9876, "effectcraft": 9877 }
}

4. Verify. In any Harness chat, ask your agent to run artcraft_status. Green lights across bins.*.found and you're done.

Driving it from chat

The plugin registers four tools. The agent composes them, so plain language works:

"Color-grade every PNG in demo/media with a soft S-curve and smart sharpening at 80, put the results in demo/out/graded."

Behind that sentence the agent will typically do:

artcraft_cli  { bin:"photocraft", args:["batch","--actions","demo/craft/grade.json",
                                        "--in","demo/media","--out","demo/out/graded"] }
ToolWhat it does
artcraft_statusToolchain health check: which CLIs resolved, data root, control ports, running jobs
artcraft_cliRun any CLI subcommand synchronously (photocraft-cli run/batch, filmcraft-cli commands, …)
artcraft_controlOne command over the JSON control channel of a running FilmCraft / EffectCraft
artcraft_fslist / stat / read / write / mkdir inside dataRoot (writing grade.json, fetching a rendered .ecproj, …)

Driving it from the browser

Same-origin HTTP, JSON in and out. The workhorse pattern is the job lifecycle — start, poll, fetch — which keeps long renders off the request cycle:

POST /artcraft/api/job      { "bin":"effectcraft",
                              "args":["render","--comp","Main",
                                      "--out","demo/out/intro.mp4","demo/craft/intro.ecproj"],
                              "outputs":["demo/out/intro.mp4"] }
                         →  { "id":"7f2c…" }

GET  /artcraft/api/job?id=7f2c…&tail=30
                         →  { "status":"running|done|failed|timeout",
                              "stdout":"…last lines…", "stderr":"…",
                              "outputs":[{"rel":"demo/out/intro.mp4","exists":true,"size":812345}] }

GET  /artcraft/api/file?rel=demo/out/intro.mp4        → raw bytes (streamed)
POST /artcraft/api/job/cancel  { "id":"7f2c…" }

Full route list:

RoutePurpose
GET /artcraft/api/statusToolchain health (same payload as artcraft_status)
GET /artcraft/api/projectsProject directories under dataRoot
POST /artcraft/api/fs?action=write&rel=…Write a file — raw body (Blob upload) or JSON {rel, text|base64}
GET /artcraft/api/fs?op=list|stat|readtext&rel=…Read directories, metadata, small text files
GET /artcraft/api/file?rel=…Download a file — streamed, up to 2 GB
POST /artcraft/api/job · GET ?id=… · POST /artcraft/api/job/cancelJob lifecycle with output statting and tail logs
POST /artcraft/api/controlSynchronous control-channel call

Uploads are capped at 320 MB per request (Blob direct, no base64 needed for the raw path); downloads stream up to 2 GB. Path arguments are always relative to dataRoot and confined to it.

The control channel: drive a running app

CLI runs are headless and one-shot. When you'd rather watch and fine-tune — grading a scene, scrubbing a cut — start the desktop app with its control channel and speak to it live:

filmcraft --control 9876      # or set FILMCRAFT_CONTROL_PORT; loopback only
POST /artcraft/api/control
{ "app": "filmcraft", "method": "engine.execute",
  "params": { "command": "timeline.place",
              "params": { "item": 1, "track": "V1", "seconds": 0, "duration": 1270080000000 } } }

// a Lumetri curve on clip 87 …
{ "command": "effects.setParam",
  "params": { "clip": 87, "effect": "lumetri", "param": "curve_luma",
              "value": [[0,0],[0.25,0.21],[0.75,0.82],[1,1]] } }

// …and a look at what the app shows
{ "method": "ui.screenshot", "params": { "path": "frame.png" } }

Time accepts seconds, frame or timecode (internally FilmCraft counts ticks at 254,016,000,000/s). filmcraft-cli commands lists the 650+ engine commands; the protocol is documented in filmcraft/docs/control-protocol.md. EffectCraft listens the same way on its own port (default 9877).

PhotoCraft's control channel is an authenticated variant and is deliberately not proxied here — for PhotoCraft the headless CLI covers everything this plugin needs.

Working with 无限画布 (infinite-canvas)

The two plugins are decoupled — they meet only on HTTP and the disk layout. The canvas:

  1. syncs its generated assets (IndexedDB blobs) into /media/,
  2. starts PhotoCraft/FilmCraft/EffectCraft jobs with per-project files in /craft/,
  3. fetches finished renders from /out/ back into its asset library.

If you're building a similar front end, mirror that contract: project slug as folder name, keep the media · craft · out triad, and everything composes.

Recipes

Uniform grading across storyboard frames — demo/craft/grade.json:

{ "actions": [
  { "cmd": "layer.newAdjustmentLayer.curves", "params": { "points": [[0,0],[64,48],[192,212],[255,255]] } },
  { "cmd": "filter.sharpen.smartSharpen",     "params": { "amount": 80 } }
] }
photocraft-cli batch --actions demo/craft/grade.json --in demo/media --out demo/out/graded

Final cut without a browser recorder — the canvas exports its storyboard as a Final Cut Pro 7 XML (xmeml) or CMX3600 EDL into demo/craft/timeline.xml; FilmCraft imports it, links media from demo/media/, and exports H.264 (hardware NVENC on Windows), ProRes, DNxHR with EBU R128 loudness normalization and burned-in captions. Interchange is where FilmCraft's automation really pays off — EDL/XML are plain text your pipeline already knows how to write.

Animated titles as code — EffectCraft projects (.ecproj) are versioned JSON, so a model can write one outright or fill a template. Render to MP4/ProRes — ProRes 4444 with alpha drops straight onto a FilmCraft track as a graphics layer — or export Lottie and play it inline in a web UI.

Troubleshooting

  • bins.*.found: false — check the path forms above; the CLI must exist next to the app (or be on PATH). Changes to artcraft.config.json apply on the next request.
  • 404 on /artcraft/api/* — the bundle isn't registered with the profile (see Setup) or the Harness hasn't been restarted since registration. artcraft_status from chat is the quick probe.
  • Control channel refused — the app must be running with --control ; the channel is loopback-only and unauthenticated, so nothing else will answer on that port.
  • A job hangs — GET /artcraft/api/job lists everything with logs; cancel by id. Plugin reloads kill all child jobs by design.
  • Path escape errors — by design: every filesystem argument must resolve inside dataRoot.

Security & trust model

The HTTP surface has no authentication and rides on the Harness web server, which listens on loopback only — treat the port as trusted-local and don't expose it to a network; put a reverse proxy with auth in front if you must. Child processes are locked to the three allow-listed binaries; file access is locked to dataRoot; the control channel only ever dials 127.0.0.1.

Publishing

This plugin is published to npm as @ninkch/dsh-artcraft (scoped packages publish with npm publish --access public). To consume it, add the package name to your Harness profile's dependencies and dsh.profile.bundles (see Setup) — pnpm install in the profile does the rest. For local development, swap the dependency for a "link: " instead. The three places that must agree on the package name are the profile package.json (dependency + bundles) and this repo's cordis.patch.yml.

GitHub: this repository is self-contained; README.md is English-first with a 简体中文 companion.

Releases run through npm Trusted Publishing (GitHub Actions OIDC — no tokens, no OTP): .github/workflows/publish.yml fires on v* tags on main only (a guard step rejects tags from other branches); the trusted publisher on the npm package points at NINKCH/dsh-artcraft / publish.yml. Release flow and rules live in RELEASING.md — in short: changes land on dev, get tested there, and are only merged to main and tagged with the maintainer's explicit approval.

License

This plugin (@ninkch/dsh-artcraft): Apache-2.0 — see LICENSE. It ships none of the ArtCraft apps' code — it only drives their public automation surfaces (headless CLI + JSON control channel).

The ArtCraft apps themselves are not OSI open source: they are distributed under the ArtCraft License (WIP), a "fair source" license (verbatim text). The short version for anyone building on them:

  • ✅ free to use; back up, copy, modify and compile for private purposes
  • ✅ every piece of media you create with the apps is yours — your rendered episodes are commercial assets, full stop
  • ❌ no selling the apps as your own software, no competing products built from their code
  • ❌ no stripping community/donation links from a fork; no using the ArtCraft name, logo or mascots to promote a business without permission
  • 🤝 the team's public promise: if the business ever ends, the software transitions to an OSI-approved open-source license

Practical reading for an AI short-drama studio: run the apps locally, leave their community links alone, and sell your films — not the apps.