DeepSeek Harness Plugin Hub

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

探索

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

社区

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

相关链接

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

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

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

dsh-plugin-dependency-graph

Plugin Dependency Graph

DeepSeek Harness 的插件依赖图:从实时 Cordis 运行时读取每个插件注入的服务由哪个插件提供。

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

npx -y @deepseek-ai/dsh plugin --profile web add dsh-plugin-dependency-graph@0.2.0
README兼容性版本

兼容性与来源证明

Plugin Dependency Graph 以 dsh-plugin-dependency-graph 发布,当前版本为 0.2.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/9/19

版本

0.2.0stable
2026/9/19

相关插件

正在加载相关插件…

最新版
0.2.0
DSH
*
HMR
重启进程
Tree shaking
已声明 sideEffects: false
解包体积
4.2 MB
文件数
19
Surface
web
许可证
MIT
发布源
npm
GitHub
★ 0
周下载
0
最近提交
2026/9/19
查看源码 ↗
README Badge

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

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

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

认领这个 Plugin →
报告问题

相关插件

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

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Sdk Minimal@deepseek-ai/dsh-sdk-minimal独立的最小 SDK 配置包:JSON-RPC、一个 DeepSeek 适配器、持久化 Shell 和 JSONL 会话Sdk App@deepseek-ai/dsh-sdk-appdsh SDK 配置包:基于 dsh-base 提供 stdio JSON-RPC 服务和进程生命周期管理Subagent Codex@deepseek-ai/dsh-subagent-codex基于官方 app-server 协议的一次性 Codex 子代理提供程序

README

English | 中文

Plugin graph

Which plugin provides the services every other plugin injects — read from the live Cordis runtime, not from a manifest.

A settings section draws the graph, and a standalone page shows the same graph at full width. Both render the same panel: the two hosts differ only in what they can supply (a locale service, a theme, a window instead of a settings column).

Screenshots

The section, as it opens: the counts, the status row (a dot per state the graph has, its count, and a click that filters to it), the drawing, and the four-line key in the corner. The controls above the drawing are ← → (selection history), zoom, reset and fullscreen.

The plugin graph section

Fullscreen. The canvas IS the fullscreen element, so everything the panel put beside it is off screen — which is why the toolbar moves to the top line inside the canvas and the search box takes the second.

Fullscreen

A selected node. Its own description appears under the name, Listens to says which events it subscribes to, and the wires split by direction — the key in the corner names which colour is which — dashed where the dependency is only acquired at runtime.

A node selected

Searching. The matches are listed as a card, each row carrying the package and its state, and the drawing dims everything else. The × in the field clears it and hands the caret back.

Search results

The standalone viewer, opened in a new tab: the same graph at the width of a whole window. Here it has the (harness) node selected — the runtime's own row, which carries everything no plugin claims: the services the root fiber provides, the services it injects, and the events it listens to. It depends on nothing, so it has incoming wires only.

The standalone viewer

The two trees

There are two Cordis runtimes, and they get two graphs:

HostBrowser
Where it runsthe Node processthe page
Collected bycollectGraph(ctx) on the hostcollectGraph(ctx.root) in the page
Shown bythis section, fetched over HTTPreported by the page, read back by the viewer

They are never merged. They are different runtimes with different plugins and different service names, so a merged graph would not be a bigger one — it would be a wrong one. The Browser tab is a second source, not a second set of nodes.

The browser tree cannot be collected by the Node half, so the page is the only thing that can describe it: the section POSTs what it collected to /dsh-plugin-dependency-graph/client, and the viewer GETs it back (one path, two methods). The report carries the instant it was taken, and the viewer prints it — a graph a reader believes is current but is not is worse than one that admits its age.

The browser tree's descriptions come from a route of their own (/dsh-plugin-dependency-graph/descriptions), because the page cannot read node_modules while the packages it is describing are the same ones: this half reads them and serves a name → description table. That read is the one place the two halves differ in capability, and it is why the merge lives in src/describe.ts — pure, so both halves run one implementation, while only the Node half touches node:fs. Resolution tries the plugin's own tree first, then bases learned from the host's profileContext.dir: measured, the obvious bases all land on the checkout, where none of the third-party plugins are installed.

How a dependency is acquired

A plugin can take a service two ways, and the difference is not cosmetic:

  • Declared on the plugin (export const inject = [...], an inject: option, or the @Inject decorator) — the fiber stays pending until every one of them resolves. The plugin does not load without them.
  • At runtime, through the ctx.inject(deps, callback) helper — the callback runs once they appear. The plugin loads either way; only that contribution waits.

Both are real dependencies, both get edges, and the second is what the optional mark means. A missing required service is a broken composition; a missing optional one is the ordinary case the callback form exists for.

The collection reads three things, and the distinction matters: the Loader entries give the ids and package names, the reflection store is the authoritative provider table (keyed by isolation symbol — the fiber's own store is the wrong source and would name consumers as providers), and the registry supplies every live fiber, including ones started at runtime.

Reading the graph

  • The layout is data, not physics. A node sits closer to the middle the more plugins depend on it, and the angle is a golden-angle walk over the ids — so the same composition draws the same picture on every visit, and a refresh never makes the reader find their bearings again. Hubs are the centre; the rim is what nothing depends on.
  • Refresh re-reads the graph. It keeps whatever is on screen while it does, rather than blanking to a spinner: the canvas can be the fullscreen element, and an element that leaves the document takes fullscreen with it.
  • Search filters by name as you type, and the matches are listed as a card with each package's state beside it — the list is also the way to reach a node that the filter has dimmed off to the edge. The × in the field clears the query and returns the caret to it; the field keeps the room for that button whether or not it is showing, so nothing shifts as you type.
  • The status row is a legend, a census and a filter at once: one pill per state the graph actually has, its count, and a click that narrows the drawing to that state. It lists only the states present — a row of mostly-zero pills would read as legend for colours nobody can see. The key is always shown; the swatches keep the selected colours whether or not anything is selected, because a reader who has not clicked yet is the one who needs to know what the colours mean.
  • Scroll zooms, drag pans, a click selects. A scroll inside the match card or the detail card scrolls THAT card: they are panels over the drawing, and reaching for them means scrolling a list, not zooming what is behind it.
  • Back and forward walk the trail of selected nodes. Choosing a new node after going back drops the forward entries — the same rule a browser applies, for the same reason — and "nothing selected" is a step like any other, so a node can be reached again from either side.
  • Fullscreen fills the window. The toolbar and the search box move INSIDE the canvas, on the first two lines, and the detail follows onto it as an overlay: the column beside the drawing is not on screen in that mode.
  • Open in a new tab hands the graph to the standalone viewer — a whole page, served by the Node half, which is the point: an iframe or a route inside the app would inherit the same width the reader is trying to get away from.
  • Open configuration file jumps to this plugin's row in the settings editor.

What a node reports

Selecting a node fills the detail card with everything the runtime knows about that plugin:

  • The package's own description, read from its package.json. Absent rather than empty when the package does not say, or when nothing could read it — drawing no line is the truthful rendering of "this package does not describe itself".
  • Provides — the services its fibers registered.
  • Listens to — the event names its fibers subscribed to, read from the dispatcher's own table. One direction only, and named for it: dispatch never records a publisher, so who emits a name is not knowable. Listeners produce no edges either — a listener waits for a name, not for a provider, so it is not a dependency, and drawing it as an edge would be a false one.
  • Injects and Injected at runtime — the two ways a dependency is acquired, listed apart because they answer different questions; a plugin asking "why did this not load" wants the first.
  • Depends on and Used by — every edge in both directions, each row naming the service that makes it and whether it is optional.

(harness) is the one node that is not a plugin: it is the runtime's own row. Services provided by a fiber with no Loader entry behind it — the root fiber, and anything started outside the entry tree — are credited here rather than dropped, and their injections and listeners come with them. It is what keeps loader and the environment rows from reading as unresolved dependencies of everything that injects them.

What the graph reports

Two blocks sit under the drawing, and both are about work rather than about drawing:

  • Unresolved dependencies — required services with no provider anywhere in this composition. Only required ones: an optional injection with no provider has simply not been offered yet, and the plugin is working as designed.
  • Isolated services — one service name with more than one live implementation, i.e. provided under more than one isolation label.

The standalone viewer

A page the Node half serves as a whole document. It has no Cordis of its own, so it can only show the host's tree plus the browser report the app sent — and it reads two things from the URL it was opened with, because it cannot read them any other way:

ParameterWhy
?scheme=dark|lightthe page styles itself with the app's --dsw-* tokens; the attribute that switches them is the app's, not this page's
?lang=zh|en|ja|ko|es|fr|deso the new tab opens in the language the app is in, instead of guessing from the browser

The locale is whitelisted against the dictionaries before it reaches the document — a query string is not a place to trust, and that value ends up in the page's lang attribute. A page opened directly (no parameter) falls back to the browser's own preferences, then to English.

The browser report lives in memory only, never on disk: it describes a runtime that exists while the page is open, and a report that outlived a host restart would be describing something that is not there.

Language

Seven dictionaries in src/client/locales.ts: zh and en (the two the shell carries) plus ja, ko, es, fr and de, contributed one namespace at a time through the single-locale overload — the language pack a profile carries owns the definition that makes a language selectable, and this plugin adds only its own strings to it. It deliberately does not call addLanguage.

Every dictionary is typed Record<MessagesKey, string>, so a key added to MessagesKey fails to compile until all seven carry it. Wording follows session-messages, which shipped these five first: the plugins sit in one interface, so a reader must meet the same terms in both.

Layout

dsh-plugin-dependency-graph-plugin/
  package.json        dsh.bundle + dsh.client declarations, exports map (private, not published)
  cordis.patch.yml    layer patch: the Loader row
  build.mjs           build script: bundles both halves, and copies the theme at build time
  tsconfig.json       IDE type resolution only, pointing at the checkout source (read-only)
  src/
    index.ts                 Node half: collectGraph, and the routes below
    graph-types.ts           the wire shape both halves share (types + path constants)
    collect.ts               the collector, one function for both runtimes
    describe.ts              the description merge, pure — both halves run it, only the
                             Node half reads package.json
    viewer-page.ts           the standalone page's document, as a string
    viewer/main.tsx          the standalone page's body (React bundled in — that page has no
                             module table to answer a bare `react` import)
    client/
      index.ts               registers the settings section
      GraphPanel.tsx         the panel both hosts render
      graph-canvas.tsx       the drawing: layout, hit testing, zoom/pan
      locales.ts             the seven dictionaries
  docs/                      the screenshots above (chinese variants: `-zh.png`)

Routes, all under /dsh-plugin-dependency-graph: the graph itself, /view (the page), /viewer.js, and /theme.css — plus /client, which takes a POST from the app and answers a GET for the viewer, and /descriptions, which serves the name → description table for the browser tree.

Development

npm run build     # bundles lib/index.js, lib/client.js and lib/viewer.js
npx tsc -p tsconfig.json   # type check

Install as a plugin in a DSH checkout by adding this directory's cordis.patch.yml to the bundle: it inserts the single Loader row. Nothing here needs the host's source to be modified — that is a constraint this plugin is built to, not a coincidence.