api-relay-audit
toby-bridges
Local security audit for AI API relays and LLM proxies: detects prompt injection, model substitution, tool-call rewriting, SSE anomalies, error leakage, and Web3 wallet risks.
Noob-stupid/dsh-plugin-gating-hub
DSH plugin - framework upgrade safety & plugin gating: contract pre-check, rollback point, auto-rollback on failure, evidence-based auto-disable; plus a multi-source plugin market. Unofficial. | DSH 插件:框架升级安全 + 插件门控——升级前契约预检、回滚点、失败自动回滚、有确证证据才自动禁用;另带多源插件市场。非官方社区项目。
PROJECT TOPICS
PROJECT README
Web & Desktop — both supported.
English: README.md | 中文: README.zh.md
Renamed from dsh-plugin-hub — old URLs redirect.
Framework-upgrade safety and plugin-version gating for DSH. A failed upgrade rolls back. A framework changed by any update channel is still guarded. A version the host cannot take is refused, not installed.
Install (npm, one line)
dsh plugin --profile web add @noob-stupid/dsh-plugin-console
Then restart the dsh service → refresh the page → Settings → Plugins → Plugin Console.

Three hard guarantees
pnpm: it auto-runs a
read-only pre-flight, and disables a row only with hard evidence (package unresolvable /
file:// target gone / named in a boot-failure log). @deepseek-ai/* and protected / core
rows are never auto-disabled; each auto-disable writes a one-command recovery record.dsh.engines.framework /
engines.dsh / @deepseek-ai/* ranges, built-in zero-dependency semver), a repo-size gate
on source channels, and explicit handling of non-registry sources — a bare version is
written back only once the registry really resolves it, otherwise the package is materialized
as link:. Install scripts are never auto-authorized, and pnpm's supply-chain gate is never
bypassed.Discovery layer on top: a built-in multi-source plugin market & index (500+ plugins / 300+ skills, zero GitHub API calls) — and every source is swappable: install source (incl. a private intranet registry), search source (URL template + headers), index source (self-hosted intranet index), Git source (incl. a local
file://bare repo), so plugins can be installed on an intranet-only or fully offline machine.
/toggle with 409 — unbypassable);
「Check update → Update & adapt」auto-verifies and unlocks them.dsh.engines.framework /
engines.dsh + @deepseek-ai/* ranges, built-in zero-dependency semver engine) decides
whether a plugin version may run on this host, so an upgrade can't silently take plugins out.dsh-plugin repos (500+ by stars) and a Skills tab (up to 300);
browse, search, one-click install, zero GitHub API calls (served via CDN).
Every source is swappable — private intranet npm registry, custom search source (URL template +
headers), self-hosted index source, Git source (incl. local file:// bare repo) — so an
intranet-only / offline machine can still browse and install.# npm release (recommended: prebuilt, no git / build authorization needed)
dsh plugin --profile web add @noob-stupid/dsh-plugin-console
# or install from GitHub source (needs git; allowBuilds authorization on first add)
dsh plugin --profile web add github:Noob-stupid/dsh-plugin-gating-hub
Then restart the dsh service → refresh the page → Settings → Plugins → Plugin Console.
Windows (PowerShell):
git clone https://github.com/Noob-stupid/dsh-plugin-gating-hub "$env:TEMP\dsh-plugin-console" 2>$null; & "$env:TEMP\dsh-plugin-console\deploy.ps1"
Linux / macOS:
git clone https://github.com/Noob-stupid/dsh-plugin-gating-hub /tmp/dsh-plugin-console 2>/dev/null; bash /tmp/dsh-plugin-console/deploy.sh
The script copies the plugin into $DSH_HOME/profiles/<profile>/node_modules/ and
idempotently appends an enable entry to cordis.patch.yml. Afterwards:
Install the DSH plugin hub (dsh-plugin-hub): run
dsh plugin --profile web add @noob-stupid/dsh-plugin-console(npm release); if there is no dsh CLI, clone https://github.com/Noob-stupid/dsh-plugin-gating-hub to~/.dsh/profiles/web/node_modules/and register it incordis.patch.yml(id: plugin-console, name: @noob-stupid/dsh-plugin-console). Restart dsh web afterwards.
Requires: DSH ≥ 0.1.0-rc.6 (web profile, with dsh-client-modules / dsh-host-plugin-inventory).
| Benefit | Detail | |
|---|---|---|
| 🤖 | AI Empower | Give the console a package name or GitHub repo — the local AI reads docs, drafts a deployment plan (install / write config / start service / health check) and executes it safely after your confirmation; server-type components get an automatic control card |
| 🛡️ | Framework upgrade safety & adapt gate | Force-disables plugins incompatible with the new framework (enable locked; 「Check update → Update & adapt」auto-verifies and unlocks); full-tree checkpoint before upgrade, automatic rollback on relaunch failure, one-click rollback to the previous version |
| 🏠 | Family-bundle cards & safety | Same-root subpath exports group into one family card (collapse/expand, batch check-update, one-click unlock-adapted, known-check preview); never-crash safety (pre-enable import probe + patch auto-heal + exports fallback); adapt-gate source-scan hard criterion & migrate detection; deleting a bundle sub-row disables only that row |
| 🚀 | Server component cards | Left-side floating card auto-aligned to the main panel: start / stop / status / open Web UI buttons, multi-server dropdown, collapsible |
| 🧩 | Plugin & skill hub | Auto-collected index of dsh-plugin topic repos (500+ by stars) plus a Skills tab (agent-skills ∪ claude-skills ∪ dsh-skill, up to 300) — browse, search, one-click install, no GitHub API calls |
| 🤖 | Auto-collection CI | GitHub Actions reruns build-index every 6 hours (manual trigger available); authors just add the dsh-plugin / agent-skills / claude-skills / dsh-skill topic — no application needed |
| ⚡ | Instant, rate-limit-free | The index is served as a static marketplace/index.json via jsDelivr CDN (10-min host cache); terminal users make zero GitHub API calls |
| 🔄 | Version detection & one-click update | Semver-based; installed entries match npm dist-tags.latest / beta; subpackage mismatch warnings prevent mixed-version breakage |
| 🔀 | Multi-source | GitHub / Gitee (direct-repo mode) / custom search sources (URL template + header auth + private http); ⊞ merges GitHub + all custom sources in parallel |
| 🔒 | Safe by default | Loopback-only routes; AI fallback behind an explicit cost-consent modal; infrastructure rows are toggle-protected |
⊞ merges all sources; ★ filters to dsh plugin add-installable packages.~/.dsh/skills/<name>/).入口挂在官方插件管理页自己的插槽(
plugins.bundle.config,key = 包名):装完之后它出现在 「设置 → 插件 → 已安装」里我们这一条下面,用的是官方容器与官方风格 —— 不是 DOM 注入,官方更新也不怕。
1 · 已安装列表里的入口(官方页,点开我们这条即见)

2 · 「升级安全」面板(就地展开:运行模式 / 框架版本 / 回滚点 / 环境指纹 / 补声明 / 契约预检 / 各功能入口)

3 · 框架升级 / 回滚(可选目标版本、常驻的升级进度、一键回滚上一版)

4 · 门控明细(谁被隔离、为什么:升级预扫 or 启动失败隔离,附来源与时间)

- id: X + disabled: true to the user patch layer, effective via HMR;disabled: false;dist-tags.latest (curl channel, works even when
node networking is blocked) and warns about subpackages that need syncing (depsOutdated).latest preferred; next channel when latest equals the installed
version); clicking runs the full flow: backup config + framework snapshot (rollback point)
→ online install (service stays up, page never disconnects) → version verification →
auto-restart to apply;DSH-Upgrade console window pops up showing live pnpm download
progress; the in-panel progress card shows the waiting time;corepack pnpm (starts in ~0.4s,
installed rc.8 in 11.5s) against the npmmirror (China) registry, with
dangerouslyAllowAllBuilds so native modules (node-pty/koffi) compile;@deepseek-ai/dsh is a Junction — the
relaunch step resolves bin.js at runtime (follows the Junction to the current version)
instead of using a path baked in at script-generation time;dsh-plugin, browser-direct with server fallback;owner/repo (Chinese paths and full URLs supported) to find and install a repository;{q}/{page} placeholders),
optional header auth (e.g. Authorization: Bearer ...), and local/private http URLs;⊞ toggle searches GitHub + all custom sources in parallel,
merging results with source labels;dsh plugin add — root packages
with a dsh.bundle manifest (official) or aggregate repos whose subpackage carries
dsh.bundle (subpackage-installable); markers are enriched by the server (curl dual-channel)
with a client-side fallback;Network environment notes (why a multi-source setup can look "all dead")
- Accelerators/proxies that rewrite hosts kill the direct sources: tools like Steam++ often point
github.comat127.0.0.1, so the GitHub direct source,raw.githubusercontent.comandapi.github.comall become unreachable — the plugin is not broken. The console now reports this honestly as "local proxy / certificate interception (…) — an accelerator or proxy was detected, turn it off and retry" instead of a vague "network unreachable".- Free mirrors are unreliable for the git protocol on large packs, while archive/raw are usually fast: measured on the same ghproxy.net host, archive (plain HTTP) ran at 4 MB/s while the git protocol moved 0 B/s. Since this version the git channel has a stall criterion (switch source after 20 s below 1 B/s) plus its own budget, and falls back to the archive channel (download the tarball, extract, create the repo — equivalent to a clone) when all git sources fail. No manual source switching needed.
- To reorder transports, use Source Manager: git sources, archive sources and index sources all support primary→backup plus self-hosted mirrors (internal Gitea/GitLab, local
file://bare repos).
Which channel gets used depends on what the package actually is — not on a setting:
| Channel | Target | When it is used | Written in the manifest as | Needs plugin-src |
|---|---|---|---|---|
| ① pnpm (registry) | npm registry | if the package is on the registry, this is the main channel | <name>: <version> |
no |
| ② curl tarball (registry) | npm registry tarball | races in parallel with ① (takes over when pnpm is blocked/stuck) | same as above | no |
| ③ GitHub Release asset | prebuilt tgz of an upstream release | when both registry sources 404 | link:<absolute path> |
yes |
| ④ git clone | repository source | no npm package / no release asset | link:… |
yes |
| ⑤ archive (tar.gz) | codeload / mirrors | when the git protocol cannot move any bytes | link:… |
yes |
In one sentence: has an npm package → ①/②, write a version, keep the lock clean; no npm package →
fall back to ③/④/⑤, materialize into ~/.dsh/plugin-src/<pkg> and write link:.
Environment reminder: an accelerator/proxy that rewrites hosts makes the direct sources unreachable (see above), and free mirrors are unreliable for the git protocol on large packs while archive/raw are usually much faster — the console already switches by itself.
link:)Ordinary installs go through the package manager (pnpm add); that is the baseline, not a feature here.
link: is the separate, explicit route: mount a local directory / plugin you are developing
directly into the profile.
node_modules/<name> in the profile is a link to your
local directory, not a copy — edit the source, refresh the page. No reinstall, no repackaging.dependencies
(link:<absolute path>) and gets a matching entry in pnpm-lock.yaml — so lockfile health checks
and rebuilds never treat it as an orphan, and it survives restarts and instance updates.dependencies: one install run inside that directory, filling in
what the local directory is missing;node_modules. The console links the framework
peers it declares and currently cannot resolve (@deepseek-ai/*) at the plugin root,
pointing at ~/.dsh/profiles/node_modules/@deepseek-ai/<pkg>. It is a link, not a copy —
both sides share the same realpath, so Cordis service classes and LLM error classes keep their
module identity shared with the host.link: then points at a
path that no longer exists);Hybrid architecture: browsing uses the static index (instant, zero GitHub API calls), searching uses live channels (GitHub search API / multi-source parallel) — they complement each other: a brand-new repo can be found by live search even before it enters the index.
marketplace/index.json,
jsDelivr CDN + 10-min host cache): 500+ plugins by stars, instant, zero GitHub API calls;agent-skills ∪ claude-skills ∪ dsh-skill repos (up to 300);dist-tags.latest in the background — cards turn into 「更新 → vX」 buttons;~/.dsh/skills/<name>/
(frontmatter name wins over repo name; SKILL.md found at repo root or first-level
subdirectory). Installed skills show a grey 「已装」 badge.The floating "Sources" button (right of the title row, semi-transparent) opens the manager:
🔒 shows
header count);The mode decides who leads framework upgrades — nothing else:
detectHostShape() criteria), a mode that was not set by hand (compat-mode.json source is auto:*; a missing field counts as manual) is switched back to Observer on startup / first status query, with a one-line notice. Once you switch it by hand it is never changed automatically again; when it is already Observer nothing is written.Boot-failure quarantine is not affected by the mode. When the service cannot start, the console locates the culprit from the boot log and disables it (broken presets are renamed to .broken-*; with no clear culprit it falls back to safe mode so the service can start). A console that cannot boot cannot gate anything — so this rescue path always runs.
Auto-disable on confirmed evidence (0.5.32): when the fingerprint-triggered auto-preflight, or a
boot-failure record, provides confirmed evidence (the row's package resolves on none of the
resolution bases, a file:// target truly does not exist, or the boot log names the row), the console
writes disabled: true for those rows itself (no click needed). Safety rails: only rows with
evidence; @deepseek-ai/* (framework-owned) and protected/core rows are never auto-disabled;
a safe-boot snapshot is written first, so any auto-disable is one command away from a rollback;
when there is nothing to write, not a single byte is written; every auto-disable leaves a record in
dshHome()/plugin-console/auto-disable.log (time / row id / evidence / restore command).
Prefer "report only, let me click" (enabled by default) — replace <host:port> with your instance
(the default dsh web profile listens on 127.0.0.1:3080):
curl -X POST http://<host:port>/plugin-console/compat-ack \
-H 'Content-Type: application/json' -d '{"autoDisableOnEvidence":false}'
How to roll an auto-disable back: the route POST /plugin-console/safe-boot with
{"action":"restore-last-good"} (the current state is backed up first), or — when the service will not
start — from the plugin directory:
node scripts/safe-boot.mjs --list # see which snapshots exist
node scripts/safe-boot.mjs --restore-last-good # restore the newest good snapshot byte-for-byte
node scripts/safe-boot.mjs --disable-suspects # or: append disabled: true to named rows only (nothing deleted)
node scripts/safe-boot.mjs --mark-good # mark a snapshot as good (never removed by retention)
Safe-boot snapshots live under dshHome()/plugin-console/safe-boot/ (not in the profile directory),
keeping the newest 3 plus the one you marked as good.
Measured on isolated instances (fresh DSH_HOME, real HTTP probes) — supported: framework ≥ 0.1.5-rc.2, no workarounds needed.
| 0.1.7-rc.2 (current) | 0.1.5-rc.2 / 0.1.2-rc.1 | |
|---|---|---|
Official plugins page (@deepseek-ai/dsh-client-ui-plugin-manager) |
present | not shipped by the framework at all |
| The official-style Upgrade safety entry | shown (hosted in that page's own plugins.bundle.config slot) |
not shown — there is no page to host it |
Classic Plugins tab (settings.plugins.tab) |
shown | shown |
| Server-side features (upgrade / rollback / gating / marketplace) | all available | all available (every route answered 200 in the matrix) |
| Errors | none | none (the slot registration degrades silently) |
So on an older framework the plugin is the old UI with the full feature set — the new entry simply has nowhere to live. It never white-screens and never throws.
Verification matrix, raw logs and the one framework-side caveat (0.1.2-rc.1 ships patchReload: "live" for a web tree without the HMR service → boot exits 1 after ~10s; unrelated to this plugin) are recorded in .testdir/fw-old-matrix/report.md.
lib/contracts/rules.json,欢迎 PR 补充:每个事故都应该变成一条规则)dsh-skill-filesystem — if the current profile does not enable
that plugin, installed skills stay dormant until it is enabled and DSH restarted.owner/repo); check custom-source URL/headers; retry
during network blackout windows.dsh plugin add-installable packages (official +
subpackage-bundle aggregates); markers are backfilled in 1-3s — no false "none" report.@deepseek-ai/dsh-skill-filesystem in the profile
(cordis.yml) and restart; skills live in ~/.dsh/skills/<name>/.dsh-plugin / agent-skills / claude-skills / dsh-skill — tag your repo and it appears in the market automatically, no application needed;If this panel saves you time or makes DSH more fun to use:
dsh-plugin) to grow the ecosystem;Contributions of all kinds are welcome — issues, PRs, docs, translations.
See CHANGELOG.md.
MIT
The layered-refactor preview lives in its own repository: Noob-stupid/dsh-plugin-hub-refactor
lib/index.js (8547 lines) split into layers — lib/index.js 141 lines + lib/server/** 38 modules; feature-equivalent to 0.3.57, maintainability only;status + response fields) against the stable build;@noob-stupid/dsh-plugin-console (npm view @noob-stupid/dsh-plugin-console version), or main of this repo.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: marketplace、plugin-manager、cli、supply-chain-security、upgrade-safety。