DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Plugin Dependency Graph — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

dsh-plugin-dependency-graph

Plugin Dependency Graph

Plugin dependency graph for DeepSeek Harness: which plugin provides the services every other plugin injects, read from the live Cordis runtime.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-plugin-dependency-graph@0.2.0
READMECompatibilityVersions

Compatibility and provenance

Plugin Dependency Graph is published as dsh-plugin-dependency-graph and currently resolves to version 0.2.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
9/19/2026

Versions

0.2.0stable
9/19/2026

Related plugins

Loading related plugins…

Latest
0.2.0
DSH
*
HMR
Process restart
Tree shaking
Declares sideEffects: false
Unpacked size
4.2 MB
Files
19
Surface
web
License
MIT
Source
npm
GitHub
★ 0
Weekly downloads
0
Last push
9/19/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in developer-tools.

Web App@deepseek-ai/dsh-web-appThe dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, web-surface prompt, bash runtime variables, URL line)Sdk Minimal@deepseek-ai/dsh-sdk-minimalThe standalone minimal SDK profile bundle: JSON-RPC, one DeepSeek adapter, persistent shell, and JSONL sessionsSdk App@deepseek-ai/dsh-sdk-appThe dsh SDK profile bundle: stdio JSON-RPC serving and process lifecycle over dsh-baseSubagent Codex@deepseek-ai/dsh-subagent-codexOne-shot Codex subagent provider over the official app-server protocol

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.