dsh-image-studio
A DeepSeek Harness plugin that shows a generated image inside the conversation, with an 编辑 (Edit) button pinned to the image's bottom-left corner. The button opens an editor with annotate / erase / resize; pressing 生成 writes the result back to disk as real PNG files and returns it to the agent as a new message, so the next turn is an ordinary image-editing request.
It was written alongside a gpt-image-2-5-sunburst skill, but the hand-off is plain text plus file
paths, so any image generation workflow can consume it.
中文说明见 README.zh.md.
What it adds
| Piece | Registration |
|---|---|
sunburst_image_card tool |
Host: ctx.tools.register |
| Image card with the edit button | Client slot tool.call.toolview, key sunburst_image_card |
| Editor overlay | Client slot shell.overlay, id sunburst-image-editor |
GET /dsh-image-studio/image?path=… |
Host route: streams the image bytes (no base64 through RPC) |
POST /dsh-image-studio/stage |
Host route: writes the editor output, returns the hand-off message |
The agent calls the tool after a generation finishes:
sunburst_image_card(path="<the path the script printed>", prompt="<the prompt used>",
model="gpt-image-2.5-sunburst", size="3840x2160")
The card renders the file through the plugin's own same-origin route, so multi-megabyte 4K PNGs load like any other image on the page.
The three editing tools
| Tool | The user does | Output |
|---|---|---|
| 标注 | Draws freehand strokes, rectangles or arrows, with a colour picker, brush width, undo and clear. | <stem>-edit-<stamp>-annotated.png — the original with the marks baked in. |
| 擦除 | Paints over whatever should disappear. | <stem>-edit-<stamp>-mask.png — an RGBA PNG at the original image's size whose transparent pixels are exactly the painted region (opaque black elsewhere). |
| 调整尺寸 | Drags a crop box with aspect presets (free, 1:1, 3:2, 2:3, 16:9, 9:16), four corner handles, and an editable output size. | <stem>-edit-<stamp>-crop.png plus a target WxH. |
The output size is validated against the PackyAPI image rules while the user types: width and height multiples of 16, longest side ≤ 3840, aspect ratio ≤ 3:1, total pixels between 655360 and 8294400.
Press 生成 and the plugin:
- writes whichever of the three files the user produced next to the original image;
- composes a message listing the original path, the produced paths, the target size and the user's own wording, ending with the reminder that a mask must match the size of the base image — so a crop and an erase are done in separate passes;
- fills the composer with that message and submits it, so the agent picks the work up in the next turn.
Install
dsh plugin --profile web add github:wu81313-lab/dsh-image-studio
dsh plugin --profile <name> … proxies to pnpm inside that profile, and the CLI writes the package
into both dependencies and dsh.profile.bundles of the profile's package.json — that second
half is what makes DSH load it as a plugin bundle, and what makes it show up as installed in the
plugin market.
A bundle's layers are composed at boot, so restart DSH once after installing, and refresh the
browser page so the client bundle is picked up. The plugin's own cordis.patch.yml is what mounts the
entry:
- insert:
- id: sunburst-image-card
name: "dsh-image-studio"
To mount it without a restart, put that same insert in
$DSH_HOME/profiles/<profile>/cordis.patch.yml instead: the web profile reloads that file live
(patchReload: live), the Host half (the tool and the two routes) activates immediately, and a page
refresh loads the client bundle. Do not use both at once — dsh.profile.bundles and a patch-file
insert must not mount the same entry id twice.
Requirements
- DeepSeek Harness with the Web GUI.
- No npm dependencies: the Host half imports Node builtins only, and the client half takes
reactfrom the page's module table (declared as an optional peer dependency). - The Web profile, because both halves are Web-platform: the card is a client slot registration and
the image/stage routes are registered on
ctx.webServer.
Notes and limits
- The mask is always generated at the original image's size — that is what masked inpainting expects, and it is why the hand-off message tells the agent not to combine a crop with a mask.
GET /dsh-image-studio/imageserves local files, restricted to image extensions (.png .jpg .jpeg .webp .gif .bmp .avif). The route answers on the same origin as the GUI.POST /dsh-image-studio/stageaccepts a JSON body with data URLs and writes the decoded bytes withfs.writeFileSync; the body cap is 128 MiB.- The card is keyed on the tool name, so older cards in an existing conversation re-render as soon as the plugin is installed.
fsis only used forstatSyncon the tool'spathargument; nothing else is read.
License
MIT — see LICENSE.
No comments yet. Be the first to write one.