The publish step now tries the built-in Gitea job token first, prints which user it belongs to, falls back to NPM_TOKEN when the registry rejects it, and reports which token actually published. A 409 keeps meaning 'already published', so a re-run stays harmless.
pnpm accepts #semver:^1.0.1 on a git address and resolves it against the repository tags; verified against this repository, where it selected the v1.0.1 commit.
The dialog has two inputs: the spec field takes a package name, a git address or a local path, while the install source only decides which registry a package name is looked up in. Git and tarball specs are fetched directly, so the source can stay on its default; the previous wording left that open and the address ended up in the wrong field.
Gitea's npm registry returns a fixed field set, so the dsh field never reaches the installer's registry preflight and the install is refused with 'declares no dsh.bundle'. The tarball and the git address carry the real package.json, so they are the routes that work.
Only the features, the behaviour boundaries, the three ways to install it, and a precise compatibility statement remain; the release pipeline, the file layout and the local-directory install method are gone. The install section now mirrors how a published DSH plugin is described: npm package plus custom registry, a pinned git URL, or a downloaded tgz.
The publish section now says why the workflow avoids external actions, that a repeated version is a skip rather than a failure, and that an npm registry line pointing elsewhere produces a misleading 'version already exists'.
Gitea does not implement the npm whoami endpoint and npm refuses to read an auth token back, so both lines only produced error-looking output. The run now checks the registry first and skips publishing a version that already exists, which also makes a manual re-run safe.
The first tagged run failed inside the publish step within a second, which is the shape of an auth rejection rather than a build problem. The step now prefers a repository NPM_TOKEN secret when one exists, prints the effective registry and a masked token, and runs npm whoami so a re-run names the cause instead of just failing.
The registry owner is the scope the package must publish under, so the name moves from @local/ to @dsh-plugin/ in all three places that must agree: package.json, cordis.patch.yml, and the client module id. `private` goes away because npm refuses to publish such a package, and publishConfig pins the registry to this organization's npm endpoint.
The tag workflow publishes whatever version the pushed v* tag names, using the built-in Gitea job token, and refuses a tag that disagrees with package.json.