跪拜 Guibai
← All articles
Frontend · Backend

A Wallpaper Skin Plugin for DeepSeek Harness, Built from Scratch

By 犹豫的果冻布丁 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

DeepSeek Harness is gaining traction as a coding agent platform, but its plugin ecosystem is still thin. A worked example that documents the settings whitelist workaround, the CJS factory build constraint, and the `overrideTokens` re-entry hazard saves the next plugin author days of debugging.

Summary

DeepSeek Harness ships with light and dark themes but no custom background support. `dsh-skin-alphacoders` fills that gap by pulling popular wallpapers from Alphacoders and displaying them inside the Web GUI. The plugin supports paginated browsing, keyword search, local image uploads, four fill modes, and a 0–90% overlay intensity slider that automatically switches between white and black mist based on the active theme. Preferences persist in `settings.yaml` and survive restarts.

The build required navigating DSH's dual-half plugin architecture: a Node.js host half handles network fetching, file storage, and settings registration, while a browser half renders the React UI and background layer. Because the browser module system demands a single CJS factory wrapped in `__ModuleLoader__`, the client build chain uses tsdown plus a custom wrapper script. The theme integration hooks into DSH's official `overrideTokens` API rather than touching the DOM directly.

Several sharp edges surfaced during development. The settings wire whitelist blocks browser-side reads and writes to third-party namespaces, so the plugin routes all settings traffic through its own HTTP endpoint on the host. A re-entry storm from `overrideTokens` firing `theme/change` synchronously was tamed with signature-based deduplication. And pnpm's hardlink behavior for `file:` dependencies meant source changes didn't propagate until the dev setup switched to directory junctions.

Takeaways
DSH plugins are dual-half packages: a Node.js host half for files, network, and settings, and a browser half for UI, declared via `dsh.client` in `package.json`.
The browser half must ship as a single CJS file wrapped in a `window.__ModuleLoader__.load()` factory; dependencies outside the module seed table must be bundled.
Theme customization uses `ctx.theme.overrideTokens()` to overlay CSS variable values — no direct DOM manipulation needed.
DSH's settings wire whitelist rejects browser-side reads and writes to third-party namespaces; the workaround is to register an HTTP route on the host and route all settings traffic through it.
Calling `overrideTokens` publishes a `theme/change` event synchronously, which can trigger a recursive storm unless the token layer deduplicates by signature.
Alphacoders' popular-wallpapers page is server-rendered `ImageObject` microdata with 15 items per page and a `thumb-1920-<id>` CDN variant that avoids downloading full-resolution originals.
The plugin runs 23 E2E assertions via puppeteer-core covering gallery rendering, wallpaper application, light/dark linkage, persistence, search, local upload, file cleanup, and uninstall recovery.
Conclusions

The settings whitelist isn't a bug — it's a deliberate security boundary — but the official escape hatch of plugin-owned HTTP routes is under-documented, and this plugin's implementation effectively serves as a reference architecture for any DSH plugin that needs persistent configuration.

Requiring a CJS factory wrapped in a bespoke module loader, rather than standard ESM, adds non-trivial build complexity. This constraint will trip up anyone who assumes modern bundler defaults will work out of the box.

The `overrideTokens` re-entry problem is a classic synchronous event hazard in a plugin system. The fix — signature-based deduplication — is simple but not obvious until you've hit the 500-layer recursion yourself.

Using server-rendered microdata as an API, rather than a formal REST endpoint, is a pragmatic but fragile integration strategy. It works until Alphacoders changes their markup.

Concepts & terms
Cordis runtime
The plugin runtime that DSH is built on. Plugins are organized as services with dependency injection, and configuration is assembled by layering YAML patch files from bundles, profiles, and user overrides.
Dual-half plugin (Host + Client)
A DSH web plugin split into two entry points: a Node.js half (`exports["."]`) running in the host process with filesystem and network access, and a browser half (`exports["./client"]`) running in the page for UI rendering.
`__DSH_BOOT__` and `__ModuleLoader__`
The client-side bootstrap mechanism. The host injects plugin configuration into `window.__DSH_BOOT__`, and each browser-half plugin must register itself as a CJS factory via `window.__ModuleLoader__.load()`. Only dependencies in the module seed table (like React) can be external; everything else must be bundled.
`overrideTokens`
A ThemeRuntime API that overlays CSS variable values onto the active theme. Each token requires light and dark values. Calling it synchronously publishes a `theme/change` event, which can cause re-entry if not guarded.
Settings wire whitelist
A security restriction in DSH that limits browser-side settings access to built-in namespaces like `locale` and `ui-theme`. Third-party plugin namespaces must be read and written through a plugin-owned HTTP route on the host side.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗