Skip to content
dsh.fish
Bundle

dsh-show-picture

DSH (DeepSeek Harness) Cordis plugin that lets the agent display images — local files or URLs — directly inside the conversation.

Source
Yaing-Yan
License
MIT
Updated
Updated 3 days ago

Readme

# dsh-show-picture 🖼️

> A [DeepSeek Harness](https://github.com/deepseek-ai/dsh) (DSH) Cordis plugin that lets the agent display images **directly inside the conversation**.
> 一个让 Agent 直接在对话中展示图片的 DSH(DeepSeek Harness)Cordis 插件。

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![DSH](https://img.shields.io/badge/DSH-Cordis%20Plugin-blueviolet)](https://github.com/deepseek-ai/dsh)

## ✨ What it does / 功能

Give the agent one new model tool, `show_picture`, and render its result as an image card right in the chat:

```text
show_picture({ path: "/workspace/chart.png" })  →  🖼️ chart.png appears in the conversation
show_picture({ url: "https://example.com/photo.webp", alt: "a photo" })  →  🖼️ photo appears
```

- **Local files** — read through the Host `fs` service, delivered to the browser as a base64 data URL (no separate file server needed).
- **Remote URLs** — passed straight through to an `<img>` tag.
- **No downloads, no "see the file at …"** — the user sees the picture inside the chat flow.

## 🧩 How it works / 工作原理

| Half | Role |
| --- | --- |
| **Host** (`plugin/host.js`) | Registers the `show_picture` tool via `harness.defineTool` + `harness.registerTool`, and a package-private RPC `show-picture.read` that resolves the file (`fs.resolve`), checks it is a regular file (`fs.stat`), reads up to 10 MiB (`fs.readBytes`), maps the extension to a MIME type, and base64-encodes the bytes (plain-JS encoder — the sandbox `btoa` is UTF-8 only). |
| **Client** (`plugin/client.js`) | Registers the `tool.call.toolview` card keyed `show_picture`. The card parses the tool-call arguments and renders `<img src="data:…">` (path) or `<img src="url">` (url), plus an optional caption from `alt`. |

```
┌─────────────── Host (Node) ───────────────┐      ┌────────── Client (browser) ──────────┐
│ show_picture tool (global, every session) │      │ tool.call.toolview [show_picture]   │
│   fs.resolve / stat                       │      │  ├─ path → <img src="/dsh-show-      │
│   result: {ok, kind, path, size, mime}    │      │  │         picture/<encoded path>">  │
│                                           │      │  └─ url  → <img src={url}>            │
│ webServer route /dsh-show-picture/* ◄─────┼──────┼── browser fetches the route          │
│   fs.readBytes → bytes + content-type     │      │                                        │
└───────────────────────────────────────────┘      └────────────────────────────────────────┘
```

## 📦 Install / 安装

### A. Global static plugin (recommended — every session, survives restarts)

Install into the `web` profile as a bundle so **every session (including non-creation mode) gets the tool automatically**, even after restarting DSH:

```bash
cd "$DSH_HOME/profiles/web"          # DSH_HOME defaults to ~/.dsh
pnpm add file:/path/to/dsh-show-picture
# then add "dsh-show-picture" to the "dsh.profile.bundles" array in package.json
dsh --profile web                    # restart the app
```

That is exactly how `dsh-at-file` / `dsh-better-status` are installed in this deployment. The Host half (`lib/index.js`) registers the global `show_picture` tool and a `/dsh-show-picture/<encoded-path>` HTTP route; the Client half (`lib/client.js`) is served to the browser as a web module and renders the card.

### B. Dynamic Cordis plugin (session-local)

The plugin can also be created in a single session with `cordis_define` (kind `new`):
- `code.host` ← paste the content of [`plugin/host.js`](plugin/host.js)
- `code.client` ← paste the content of [`plugin/client.js`](plugin/client.js)

then activate with `cordis_run` (mode `run`). First activation of the Client half asks for **user approval** in the UI. Dynamic plugins are process-local: they do **not** survive a DSH restart and belong to the session that created them.

Full step-by-step (including upgrades and rollback) is in [`docs/INSTALL.md`](docs/INSTALL.md).

## 🛠 Usage / 用法

The agent calls the tool whenever the user asks to see an image:

| Argument | Type | Required | Meaning |
| --- | --- | --- | --- |
| `path` | string | one of | Local image file (absolute or workspace-relative). Extensions: png, jpg/jpeg, gif, webp, svg, bmp, ico, avif, tif/tiff. Max 10 MiB. |
| `url` | string | one of | http(s) URL of the image. |
| `alt` | string | no | Short caption shown under the image. |

Result (canonical JSON): `{ ok: true, kind: 'path'|'url', path|url, size?, mime?, alt }` or `{ ok: false, error }`.

A ready-to-use agent skill is included at [`skill/dsh-show-picture/SKILL.md`](skill/dsh-show-picture/SKILL.md) — drop it into a DSH agent preset's `skills/` directory (or follow its instructions manually).

## ⚠️ Limitations / 限制

- Local files > 10 MiB fail with `FS_TOO_LARGE` (compress/resize first, or serve via URL).
- Extension-based MIME detection only; unsupported types are rejected, not sniffed.
- `url` images must be loadable by the browser (CSP, availability, auth).
- The card renders only while the Client half is active and the call goes through the model's tool loop.

## 📁 Repository layout / 目录结构

```
dsh-show-picture/
├── lib/
│   ├── index.js         # Host half (global tool + image HTTP route) — static install
│   └── client.js        # Client half (tool card) — served as a web module
├── plugin/
│   ├── host.js          # Host half (tool + RPC) — paste as code.host (dynamic)
│   └── client.js        # Client half (tool card) — paste as code.client (dynamic)
├── skill/
│   └── dsh-show-picture/
│       └── SKILL.md     # Agent skill: install, usage, limitations
├── docs/
│   └── INSTALL.md       # Step-by-step install / update / rollback
├── cordis.patch.yml     # Bundle patch: inserts the dsh-show-picture row
├── dsh.plugin.json      # Plugin metadata (entry + client platform)
├── package.json
└── LICENSE
```

## 📄 License / 许可证

[MIT](LICENSE) © 2026 dsh-show-picture contributors

Install

dsh plugin --profile web add github:Yaing-Yan/dsh-show-picture

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source