Publish a DeepSeek Harness plugin to npm: build traps
The core value of publishing a DeepSeek Harness plugin to npm is distributing prebuilt code: what users install from npm is already compiled, so installing triggers no build script and needs no allowBuilds permission. A git install pulls sources instead, and without a prepare script there is no lib/ — it breaks the moment it installs. The same constraints apply to every DSH plugin you ship.
First, the trap: a DSH plugin git install gets sources
A git install never runs your build script, so a TypeScript package arrives without its lib/ output. The official docs are blunt about it, and the fix has two halves (source):
- Author side: ship a
preparescript (pnpm runs it after a git install) that builds the published entry points from source, and make it self-contained — it must not assume dev-only context such as a sibling monorepo checkout. - User side: allow the build. pnpm ≥10 refuses to run a git dependency's
prepareby default, so the firstdsh plugin --profile demo add github:you/hello-pluginfails;dshpoints out the fix, which is writing the package key pnpm printed into the profile'spnpm-workspace.yaml— after which the add should be expected to succeed:
allowBuilds:
dsh-hello-plugin: true
Take that allowance seriously: it means "permission to execute this package's code on your machine at install time," outside any sandbox the agent runs under. Allow only packages whose source you trust, and pin a commit (github:you/hello-plugin#<sha>), or a later push could silently change what actually runs.
Why publishing a DSH plugin to npm is easier
Publishing to npm moves the build from the user's machine into your release process. The two distribution paths the official docs describe as needing no build permission at all are:
| Path | What is distributed | User needs build allowance? |
|---|---|---|
pnpm publish to npm | The lib/ built at publish time | No |
pnpm pack tarball | The built artifacts | No |
On the user side, dsh plugin add <package> is enough to get prebuilt code. That is the entire reason to prefer npm — not reputation, but one less install-time authorization and one less class of failure.
DSH plugin pre-publish checklist
Five items, none optional; missing one shows up as "installed but not in effect" rather than a clear error.
main(orexports) points at the built entry — notsrc/;type: module;filesincludes the entry andcordis.patch.yml— drop the patch file and the config layer never installs;dsh.bundle.patchpoints at the patch file — drop it and the package installs as a plain dependency with no layer;versionis bumped — npm refuses to overwrite a published version.
Install it locally with dsh plugin add ./hello-plugin before publishing; the package layout and layer order are covered in packaging into a bundle.
The DSH plugin publish flow
The flow is short; the risk sits in the checklist above. A typical sequence:
- Build the publish artifacts:
pnpm run build, which you should expect to leavelib/current. - Self-test through the install path: in a clean profile run
dsh plugin --profile demo add ./, which you should expect to load the plugin and apply its behavior. - Publish:
pnpm publish.
Two easily missed points: ① a scoped package (@your-scope/xxx) must declare itself public on first publish, or it defaults to private; ② tag the matching git commit afterwards, so "the version users installed" maps to "the commit in your repo" — that saves time when debugging in production.
Alternative: shipping a DSH plugin as a pnpm pack tarball
For internal distribution or a pilot, use a tarball instead of npm. The official route:
- Author side:
pnpm pack, which you should expect to producedsh-hello-plugin-0.1.0.tgz. - User side:
dsh plugin add ./hello-plugin-0.1.0.tgz, which you should expect to install the built artifacts.
A tarball is also built code, so it needs no build permission; the trade-off is no version index, so upgrades mean shipping a new file.
Verifying a published DSH plugin
Verify in a clean environment, or you are testing "dependencies already present locally" rather than "the package is complete."
- Use a different profile (or a clean machine):
dsh plugin --profile fresh add <package>; - Inspect the layer before booting:
dsh --profile fresh --dump-configto confirm the bundle's layer appears; - Boot and watch the plugin's log lines;
- To list it in the plugin directory, follow publishing to the plugin hub, then check the installed list in DSH Plugin Hub.
If install reports a missing entry, first check whether the file main points at is inside files — the most common npm-publishing failure. It works locally because src/ exists there, but the published package does not carry it. Environment-level issues (Node version, pnpm) are covered in environment setup.
FAQ
**Because a DSH plugin git install fetches sources, not built artifacts**: nothing runs your build script, so a TypeScript package arrives without its lib/ output and fails to load. The fix has two halves — the author ships a prepare script (pnpm runs it after a git install) that builds the entry points self-containedly, and the user allowlists the build (source: official "Package and install a plugin").
**allowBuilds is the DSH plugin install-time allowlist that lets a git dependency run its build script**: pnpm ≥10 refuses to run a git dependency's prepare script until it is explicitly allowed, and dsh points at the fix — copying the package key pnpm printed into the profile's pnpm-workspace.yaml under allowBuilds and re-running the add. **Treat it as permission for that package to execute code on your machine at install time**, allow only packages whose source you trust, and pin a commit (github:you/plugin#<sha>) so a later push cannot silently change what runs (source: official "Package and install a plugin").
**Publishing a DSH plugin to npm distributes prebuilt code, so installing triggers no build script and needs no allowBuilds permission**; a GitHub repo distributes sources, which requires a prepare build plus a user allowance. The two paths the official docs call out as needing no build permission are exactly: publish to npm (with lib/ built at pnpm publish time) and ship a tarball from pnpm pack (source: official "Package and install a plugin").
**Before publishing a DSH plugin, check five fields in package.json**: main (or exports) points at the built entry; type: module; files includes the entry and cordis.patch.yml; dsh.bundle.patch points at the patch file; and version is bumped. Missing any of the first four shows up as "installed but not in effect" or "failed to load" rather than a clear error (source: official "Package and install a plugin").
Yes: **run pnpm pack to produce a tarball and have users install it with dsh plugin add ./hello-plugin-0.1.0.tgz**. A tarball contains built artifacts and needs no build permission, which suits internal distribution or a small pilot before publishing to npm (source: official "Package and install a plugin").
Related Terms
- prepare script
- prepare is the DSH plugin package.json lifecycle script that pnpm runs after a git install. Plugin authors use it to build the published entry points self-containedly, without assuming dev-only context such as a sibling monorepo checkout.— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md
- allowBuilds
- allowBuilds is the DSH plugin user-side allowlist in a profile's pnpm-workspace.yaml. pnpm ≥10 refuses to run a git dependency's build script until its key is listed, which amounts to authorizing that package to execute code at install time.— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md
- bundle
- A bundle is an npm package that ships a configuration layer, declaring its patch file via dsh.bundle. When a DSH plugin is published to npm, this package is what users install with dsh plugin add.— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md
- pnpm pack
- pnpm pack is the alternative DSH plugin distribution route: it turns the current package into a tarball (a .tgz by default) whose contents follow the files field. Users can install it directly with dsh plugin add ./xxx.tgz, with no build permission needed.— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.md