dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
每次启动 DeepSeek Harness 时随机启用一个已安装皮肤,并在「设置 → 皮肤」页内提供开关与「立即随机换一个」按钮。宿主会拒绝加载的皮肤自动排除;手动切换的皮肤一直有效到下次启动。
dsh-skin-rotation
Smart skin rotation for DeepSeek Harness — random, sequential, weighted or fixed, with a per-skin pool, rotation history and crash-safe config writes. Local-first, recoverable, no extra API.
English | 中文
You have four skins installed. You like all of them. You use one of them.
dsh-skin-rotation applies one rotation policy on every launch of DeepSeek Harness and puts the controls inside the Skins settings page:
| Policy | Behaviour |
|---|---|
random | a different skin each launch, optionally never the last 2 or 3 |
sequential | walks your order, resuming at the right position after a restart |
weighted | probability by weight per skin (weights need not total 100) |
fixed | always one chosen skin — no automatic switching |
Everything about it is inspectable and reversible: which skins are in the pool and why the others are not, what was rotated when and why, and the last successful rotation you can restore with one click.
launch 1 → 深海女仆工坊 launch 2 → 虎鲸链路 launch 3 → 鲸鱼娘昼夜工坊
compatible, excluded, incompatible or unavailable. Excluding a skin only removes it from rotation; installed files are never touched.@deepseek-ai/dsh* peer range excludes the running version, so a naive rotation can land on a skin that never starts. This plugin mirrors that admission check and honours the profile's compatibility.json grants.startup / manual / restore), mode and outcome. Failed rotations are recorded as failures; they never enter the "recent" set.schemaVersion and migrates forward, so upgrades do not lose your preferences.From GitHub:
dsh plugin --profile web add github:luoyuejun9/dsh-skin-rotation
From npm:
dsh plugin --profile web add dsh-skin-rotation
Or from the prebuilt tarball attached to the latest release — no build step, no allowBuilds prompt:
dsh plugin --profile web add https://github.com/luoyuejun9/dsh-skin-rotation/releases/latest/download/dsh-skin-rotation.tgz
Restart once. Then open Settings → Skins: the rotation card sits at the bottom of that page.
Installing into the desktop app instead of the web profile? Use
--profile desktop. The plugin works in both.
Settings → Skins
├── Installed skins ← the skin manager's own page
└── 🎲 Skin rotation ← this plugin
├── [switch] Rotate on launch Mode: [random ▾] Avoid recent: [last 2 ▾]
├── Current skin: 虎鲸链路 · 2 in pool
├── ☑ 虎鲸链路 · weight [3]
├── ☐ 深海女仆工坊 (excluded)
├── ☐ 不兼容皮肤 (incompatible — excluded)
├── [Shuffle now] [Restore previous] [Refresh]
├── Recent rotations
│ 2026-10-11 21:04 · startup · official → 虎鲸链路 · ok
└── ▸ Advanced (weights, reset to defaults)
| Control | What it does |
|---|---|
| Rotate on launch | Off means the launch leaves the current skin alone. Shuffle now still works. |
| Mode | random / sequential / weighted / fixed. In fixed you pick the skin; nothing rotates automatically. |
| Avoid recent | Prefers a skin that was not among the last 2 or 3 successful rotations. It is a preference, never a blocker: with nowhere fresh to go it still picks one and says so. |
| Skin checkboxes | Pool membership. Unchecking is an exclusion, not an uninstall. Incompatible skins cannot be re-enabled. |
| Weights | Shown in weighted mode. 0 removes a skin from the draw; an empty box means the default weight 1. |
| Restore previous | Returns to the most recent successful skin that is still in the pool, and records the restore in the history. |
A manual switch in the skin manager still wins — until the next launch, which is the entire point.
flowchart LR
A[host boots] --> B{rotation on?}
B -- no --> Z[leave the current skin]
B -- yes --> C[discover installed skins]
C --> D[drop skins the host would deny]
D --> E[random pick, never the previous one]
E --> F[rewrite the dsh-skin managed block<br/>in profile patch + home patch]
F --> G[DSH's HMR watcher recomposes the tree]
G --> H[the page opens already wearing it]
apply without rotating again; a real restart does rotate. A browser refresh never re-rolls the dice.# --- dsh-skin managed --- block the skin manager owns, in both the profile patch and the higher-priority home patch. Every line outside that block — hand-written rows, comments, DSH's own appended settings rows — is preserved byte for byte, and writes go through a temp file plus rename.@deepseek-ai/dsh* peer range excludes the running version: the patch still says disabled: false, the row simply never starts, and you get no skin at all. This plugin mirrors that admission check with its own SemVer range evaluator and honours the profile's compatibility.json grants, so a random pick cannot land on a dead skin.skin.json, or the manager's own state.| Path | Content |
|---|---|
<profile>/cordis.patch.yml | the shared dsh-skin managed mutual-exclusion block |
<DSH_HOME>/cordis.patch.yml | the same block — higher priority, so it must agree |
<profile>/.dsh-skin-rotation.json | this plugin's preference: switch, last pick, launch key, client report |
HTTP surface: GET/POST /api/dsh/skin-rotation — same-origin only, cross-site requests rejected.
>=0.2.0-rc.2 <0.3.0-0 (declared as engines.dsh).skin.json (id, package, bodyAttr, wiring.id).settings.section id dsh-skins). Without that manager rotation still works — you drive it through the route instead of a card.@deepseek-ai/* runtime dependency: nothing to resolve, nothing to build, no install-time scripts.L5 install verification — run tested on dsh 0.2.0-rc.2.node --test # 29 tests
npm pack --pack-destination dist # build the release tarball
| Suite | What it covers |
|---|---|
test/host.test.mjs | managed-block rewrite (including foreign-row survival), discovery against a live profile, random-pick rules, the once-per-launch guard, the two-layer write with rollback, route guards |
test/client-card.test.mjs | the whole card render tree, driven through a stub module loader and stub React — no browser needed |
test/semver-oracle.test.mjs | differential test against real node-semver over a generated matrix (skipped when node-semver is not installed) |
tools/e2e-rotation.sh | boots a real host against an isolated DSH_HOME, repeatedly, and asserts the picked skin is what the browser is actually served |
The end-to-end script is the interesting one: it does not test the plugin's own opinion of what it did. It fetches the boot graph the browser receives and checks that exactly one skin is in it.
Will it fight the skin manager? No. Both write the same block using the same rules; the plugin rotates at launch and then stays out of the way.
Does a browser refresh re-roll? No — once per host process. Otherwise every page reload would be a new skin.
What if I only have one skin? Rotation is a no-op, and the block is still normalised.
Can I keep my current skin? Turn the switch off. The launch then leaves the skin alone, and manual switching is unaffected.
This plugin manages rotation over the skins you already installed: the pool, the policy, the history, and the reliability of the config write. It deliberately does not install, download, uninstall or preview skins, and it does not replace a skin manager or a market — those exist and this plugin works alongside them.
MIT © luoyuejun9