DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Htmlui — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
H

@mostkia/dsh-htmlui

Htmlui

DeepSeek Harness 的 HTML 会话 UI:代理编写 HTML/CSS/JS,插件在沙盒 iframe 中渲染内容,并以嵌入、停靠、浮动或全屏方式放置,同时通过 POST + SSE 将交互传回模型。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:mostkia/dsh-htmlui#27ea514547bc55c1dbe6723e503dd792d9e42311
README兼容性版本

兼容性与来源证明

Htmlui 以 @mostkia/dsh-htmlui 发布,当前版本为 0.1.3。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/10/8

版本

0.1.3stable
2026/10/8
0.1.2stable
2026/10/7
0.1.0stable
2026/10/6
查看其余 1 个版本收起版本
0.1.1stable
2026/10/6

相关插件

正在加载相关插件…

最新版
0.1.3
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/9
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Experimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profile用于 Agent Teams Remote 和 UI 插件的实验性 Web 配置层Ab Chartdsh-ab-chart带有绘制其调用的浏览器卡片的图表渲染工具,封装在一个可发布的插件包中Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义

README

@mostkia/dsh-htmlui

English | 中文

An HTML UI layer for DeepSeek Harness 🎉

dsh-htmlui inserts HTML into the conversation stream, runs it as a window, docks it in the right column, takes it fullscreen, or mounts it behind everything (experimental). It is deeply integrated with the agent: the model authors interactive HTML UIs directly, and dsh-htmlui acts as the middle layer inside DSH, so long, repetitive, fixed workflows can be automated through those panels — with none of the inefficiency or imprecision of describing the same thing in words again.

It also uploads and keeps custom HTML templates, so any practical HTML tool can be integrated and then handed to the agent to be improved: ordinary HTML pages become AI-enhanced tools. Permission levels, persistence that spans sessions, and the rest of the feature set are here to make your DSH workspace nicer and faster — one plus one, more than two.

What it looks like

Inline, straight into the streamDocked in the right column (collapsible, several at once)
inlineright column
Floating window (draggable, several at once, minimizable)Fullscreen (several at once, minimizable)
floatfullscreen
HTML session management (several open, still manageable)Import a project and set its permissions
session managerimport and permissions
One click from the composerA project can carry anything — here, Live2D streaming
one clicklive2d
A notepad app built from one sentence with an Agent — with this plugin installed, so can you 😋
notepad app

Highlights

  • HTML, rendered globally. In principle any HTML/CSS/JS — canvas, WebGL, video, a third-party library, a whole application.
  • Plenty of places to live. inline in the conversation, dock-right in the session's column, a draggable float, a fullscreen surface, and a click-through background layer.
  • Two-way, without polling. A document calls dshHTML.send(...) and the action reaches the model as a message; the model streams data back over SSE and the page updates in place, as it arrives.
  • A template library you import and export. Point the plugin at a directory of HTML projects, move one in or out, keep a useful tool for the next session — and a template can be created with no model round trip at all.
  • Permissions per project. Levels let safety and capability meet at a point you choose, project by project.
  • A session page, not a hidden store. HTML管理 lists every interface in this session with its placement and source project, and can hide, restore or remove it; records are kept host-side and bound to the session.
  • Sandboxed by construction. Documents run in an opaque-origin iframe and are reached through a per-document capability token, which keeps DSH's own content safe. (A sandbox cannot stop you from granting unrestricted permissions — do that only with care.)
  • Display slots keep their session state. Every display slot dsh-htmlui mounts holds its state: switching sessions, minimizing, or collapsing a window while the page stays open never loses what a session's HTML template was holding. (To survive closing the page and a DSH cold start, use a persistence slot.)
  • It costs no context. What the model reads is a compact tool-result summary (ui_id / placement / bytes / revision); the template itself is loaded by the browser from the carrier's ticket route. Large documents go to a file and are referenced with path, so the model does not pull templates into its context.

Five placements

placementWhere it lives
inlineCreated inside the main conversation: seamless — no chrome, no border, no background, height taken from the document itself
dock-rightCreated in the session's right-hand collapsible column: the most common form, working side by side with the conversation in a left/right split
floatA draggable, resizable, minimizable window (size: "520x360+80+60"); show it again from the HTML manager
fullscreenCovers the session, with a built-in switch back to chat. Surfaces stay mounted while you are elsewhere, so returning does not reload them
backgroundA full-frame, click-through layer at a fixed 25% opacity, so the interface underneath stays readable. It belongs to no view — close it from the session page when you are done

An HTML template can declare its own placement instead of the caller naming it:

<meta name="dsh-htmlui" content="placement=dock-right; size=520x360; title=Orders">

The tool argument wins over the declaration, the declaration wins over the inline default, and an unusable value is ignored.

Install

Straight from the repository (not published to npm yet):

dsh plugin --profile web add github:mostkia/dsh-htmlui

Requires DSH >=0.1.7-0 (the pre-release line is included on purpose, so 0.1.7-rc.* installs too). Hard-refresh the page after installing; when the browser half activates it logs [dsh-htmlui] client active (0.1.1) to the console.

The easiest way to confirm which generation a running host actually has:

curl -s http://127.0.0.1:3080/plugins/@mostkia/dsh-htmlui/health
# {"ok":true,"plugin":"@mostkia/dsh-htmlui","version":"0.1.1",...}

Changing the host half (index.js) does not take effect in a running host: the loader keeps the module generation it activated. Restart dsh for the host half; the browser half only needs a page refresh.

docs/VERIFY.md is the on-machine acceptance checklist: which generation is running, what each placement should look like, how the round trip shows up in the conversation, and what to inspect for a given symptom.

How it works

  • Host half (index.js, plain ESM, zero dependencies): the two tools (html_ui / html_ui_template), storage under $DSH_HOME/htmlui, and an HTTP carrier mounted at /plugins/@mostkia/dsh-htmlui — document tickets, the composed document, per-session listing, the template directory and its use, the POST action channel, the SSE event stream, and a health probe. Every route is governed by the policy described under Security.
  • Browser half (client.js, a hand-written module, no build step): registers the tool card, the composer dock, the frame-wide overlay, and puts each document in an iframe.
  • Bridge (assets/bridge.js, injected when served): exposes window.dshHTML with send, state, store, store.rows, resize, close, on(...) and ready(...). Visible text in the interfaces goes through the client locale service (en/zh dictionaries ship with the package), falling back to English constants when it is absent.
  • Persistence slots ($DSH_HOME/htmlui/store/): the one storage that is not tied to a session or a panel. A document declares the names it uses (<meta name="dsh-htmlui" content="store=notes">) and then reaches them through dshHTML.store. A slot has two layers: its value is small and travels with the document (synchronous reads, 192 KiB cap — settings, the active tab), while its rows live in one SQLite file per slot (<name>.db, via node:sqlite, so still no dependency), are fetched on demand, and are where bulk data belongs — a row may be up to 16 MiB, one write touches one row, and neither the number of rows nor the number of slots is limited. Data survives closing the panel, closing the session, and restarting the host, and every other document that declared the same name hears about a change over SSE (a value event carries the value; a row event carries the key, and the reader fetches the body). Nothing is removed except by an explicit store.remove(name) / store.rows.remove(name, key), or by deleting the file. There is no manager yet: GET /health reports each slot's kind, row count, and bytes.

About persistence slots

dsh-htmlui offers real persistent storage: an HTML template can call on a storage slot whenever it needs to keep something. A slot is large and spans sessions — and because it lives server-side rather than in browser cookies, it can be read from anywhere, not just from the client. That is what makes it genuinely useful for building web apps such as a notebook inside a panel.

How to use it

Declaration — name the slots you want in the document head, and only those names become readable and writable:

<meta name="dsh-htmlui" content="placement=dock-right; store=notes">

A slot has two layers, and choosing between them is the whole design decision:

  • Value layer. dshHTML.store.get(name) is synchronous — the value is inlined with the document, so the first frame can already paint it — and store.set(name, value) returns a promise. Small by construction: 192 KiB, because whatever it holds travels inside every load of that page. Settings, the active tab, the last choice made.
  • Row layer. dshHTML.store.rows holds many records addressed by a key, in one SQLite file per slot (<name>.db, through Node's built-in node:sqlite, so still no dependency). keys(name, { offset, limit }) answers with metadata only (key, title, size, time), so a list is drawn without fetching a single body; get / set / remove move one record at a time, so a large collection never rewrites itself; search(name, text) scans keys, titles, and text — a LIKE scan rather than FTS5, because the trigram tokenizer cannot answer a two-character Chinese query, which is the common case.
Value layerRow layer
Readsynchronous, inlinedpromise, one fetch
One entry192 KiB16 MiB
Entries per slot1unlimited
Slots per document8 declared names—
Slots in totalunlimited—
On disk$DSH_HOME/htmlui/store/<name>.json…/store/<name>.db

A value is any JSON value, so one row can be a whole configuration object — or one row per variable, with title as the human-readable label a list shows and search matches.

Nothing is removed except by store.remove(name) / store.rows.remove(name, key), or by deleting the file: closing the panel, closing the session, restarting dsh, and switching browsers all leave it in place — the data is on the host's disk, not in the panel and not in browser storage. Two panels that declared the same name hear about each other's writes over SSE: a value event carries the new value, a row event carries the key (the reader fetches the body it wants).

The row layer is secured by construction: no SQL text crosses the bridge. Keys and values are bound parameters, the SQL itself is fixed on the host, so an HTML template can only reach its own slot; ATTACH — which would reach any file the process can read — is not offered at all.

GET /plugins/@mostkia/dsh-htmlui/health reports what the store holds (counts.slots, counts.slotBytes, and per slot kind / rows / bytes), and whether this runtime has the row layer at all (rows).

Security

Documents run in an iframe with sandbox="allow-scripts allow-forms allow-modals allow-popups allow-downloads allow-pointer-lock" and without allow-same-origin: an opaque origin, no cookies, no local storage, no access to the host page. Each document carries a capability token derived from it (an HMAC over the plugin's local secret), and the document, action, state and SSE routes are all gated by it. That token appears in exactly one place — the iframe URL handed out by the ticket route — so tool results, session logs, list responses and event-stream frames never contain it, and an address without it cannot load at all.

A strict CSP travels with every document. Note that a sandboxed frame without allow-same-origin is an opaque origin, and an opaque origin matches no URL, so every same-origin allowance names this machine's own origin explicitly rather than 'self' — script-src being the one that lets the injected bridge load.

The carrier's own policy: only loopback Host/Origin pairs are trusted; an opaque-origin frame must present a valid token; cross-site ticket requests are refused outright; writes are POST only; and each document gets a small token bucket so a script cannot flood the model. Nothing secret belongs in a document, and the plugin never asks for one.

What this boundary covers, and what it does not. Loopback is the trust boundary, and it is worth saying plainly: a request with no Origin header at all (curl, a script, another local process) is treated as trusted — a local process already has everything the user has. The carrier cannot tell the DSH page apart from such a caller, so it chooses to narrow the blast radius instead of pretending to authenticate: the page-facing listing route must name its session explicitly, ticket issuance is rate-limited per document, and a document's capability token dies the moment its interface is closed or superseded. Against a browser attacker it is a different matter, and there it refuses everything: other origins are rejected, a sandboxed frame in someone else's page has no token, and a Host name obtained by DNS rebinding fails the loopback check. If you intend to serve this beyond loopback, read the allowedOrigins section below — that is the switch that moves the trust boundary.

If DSH is deliberately exposed beyond loopback (webServer.host: 0.0.0.0, a LAN address, a reverse proxy), the browser origin is one the default policy refuses and the whole plugin answers 403. Naming that origin in allowedOrigins trusts it — that one origin, and nothing else:

      config:
        allowedOrigins:
          - http://dsh.lan:3080

/health reports trust.loopbackOnly and how many origins are listed, so the current posture never has to be guessed.

Configuration

All row configuration is optional, and none of it needs a machine path:

- insert:
    - id: dsh-htmlui
      name: '@mostkia/dsh-htmlui'
      config:
        root: ''              # storage root, defaults to $DSH_HOME/htmlui
        maxInlineBytes: 16384 # cap on one inline html/css/js fragment
        actionPrompt: ''      # sentence appended to an [html-ui:action] message
        allowedOrigins: []    # extra trusted browser origins (see Security)

Runtime data lives under $DSH_HOME/htmlui: ui/<id>/index.html (the document as authored — clean and portable on disk, with nothing injected into it), templates/<name>/ (hosted templates) or templates/<name>.html (hand-written ones), state/<session>.json (per-panel scratch), store/<name>.json (the shared slots), and secret, which backs the capability tokens. Deleting a ui/<id>/ directory by hand is always safe: the record goes with it and the interface disappears from the session. (Records whose session no longer exists are kept rather than collected, so an interface never vanishes just because its session was archived.)

Templates

html_ui_template { "op": "save", "name": "orders-dashboard", "ui_id": "ui-1a2b3c4d" }
html_ui { "op": "render", "template": "orders-dashboard", "variables": { "title": "Orders this week" } }

{{token}} placeholders are replaced from variables. Templates outlive a session.

Hand-written documents are templates too: drop my-panel.html into <root>/templates/ and call it with template: "my-panel" — no manifest to write. A hosted template of the same name wins, and removing it uncovers the hand-written file again.

Beside the composer is the way in: ⟨+⟩ New HTML opens the create dialog, which lists the projects in your templates directory, lets you apply one to this session with no model round trip at all, and offers the create flow for a blank canvas, a project, or a folder you just copied in. What it creates is an ordinary record: the model sees it in html_ui op=list, and can update or close it.

Development

npm test        # 165 assertions: package integrity 13 + doc contract 10 + host 45 + browser 38 + bridge 13 + shallow render 29 + adversarial input 10 + packed artefact 2 + harness schema 5
npm run check   # syntax-checks the three shipped scripts, then runs the tests

Neither half has a build step: the host half is plain ESM, and the browser half is the module the loader materializes directly. Both are plain JavaScript, neither depends on the harness module graph at runtime, and the plugin itself has zero dependencies. CI runs the same suites on Ubuntu and Windows across Node 22 and 24 (.github/workflows/ci.yml). Release steps and the prepared awesome-dsh-plugin entry are in docs/PUBLISHING.md.

License

MIT