Repository navigation
fix(install): require only the distribution files a manifest declares - #182
Merged
dshplugin merged 3 commits intoOct 13, 2026
Merged
Conversation
A bundle that mounts a skill directory declares `dsh.bundle.patch` and no `main` / `exports` at all. The entry check defaulted such a manifest to `index.js` and then demanded that file, so the git channel rejected a complete distribution with "[packaging] … the entry file index.js declared in package.json is not present in the git distribution" and the feedback dialog filed that as a bug on the author's repository (QingYunA/answer-me-with-html#209). A host loads a bundle by reading its manifest and the patch that manifest names, never by importing the package (dsh's bundleManifest is documented as "Read bundle metadata without loading its JavaScript"), and the npm channel already accepts the same shape by design. Both channels now check the files the manifest declares: the entry it names, when it names one, and the bundle patch it declares.
This was referenced Oct 13, 2026
The guard can now trip on a declared bundle patch as well as on a declared entry, so the failure copy and the architecture notes say "a file declared in package.json (main / exports / dsh.bundle.patch)" instead of calling every miss an entry file.
A bundle's patch usually names the package it belongs to, and the host imports that package when it does; a skill-only bundle names a provider instead and needs no entry of its own. The guard now reads the declared patches and asks for the entry only in the first case, so neither shape is judged by the other's rules.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #183
Summary
Both install channels reject a package whose manifest declares no entry file. A skill-only bundle declares
dsh.bundle.patchand nomain/exportsat all; the entry check defaulted such a manifest toindex.jsand then demanded that file, so the git channel answeredfor a distribution that is complete — and whose package.json never mentions
index.js— and the failure dialog filed that on the author's repository: QingYunA/answer-me-with-html#209. The npm channel accepts the same shape by design, so the two channels disagreed about one package.A host loads a bundle by reading its manifest and the patch that manifest names (dsh's own
bundleManifestis documented as "Read bundle metadata without loading its JavaScript"), so the patch is what a bundle distribution has to contain; the JS entry is not needed at all for this shape.The two bundle shapes have to be told apart, and the manifest alone cannot do it: a bundle whose patch names its own package — the pattern dsh's Packaging and installing a plugin guide shows (
- insert: - name: <package>) — is imported by the host, so that one does need an entry after all. This branch reads the declared patches and asks for the entry only in that case.Scope, from the catalogue (top 80 entries by stars; 32 of them have a readable root
package.json): 31 declaredsh.bundle.patch; 4 declare no entry at all —QingYunA/answer-me-with-html,mnemon-dev/mnemon,thebiggavin/marketingdashboard,zuorn/tydora, and none of them resolves to an npm package whose metadata points back to its repository, which is what the market requires before it trusts the npm channel, so git is their only channel today; 4 declare an entry their git distribution does not contain (the guard's true positives — aprepare-builtlib/ordist/), one of which (hust-open-atom-club/oh-dsh) also declares a patch git does not contain, which the new patch check catches; 21 of the 31 mount themselves by name.Changes
package-entry.ts:resolvePackageEntryreturnsnullwhen the manifest declares neitherexports["."]normain(theindex.jsdefault is gone). NewresolveBundlePatchFilesreadsdsh.bundle.patchin both forms dsh'sbundlePatchFilesaccepts — a path, or a list of paths — andrequiredDistributionFilesreturns what a distribution must contain: the entry it declares, then the patches it declares.patchRowNamesOf/mountsItself) puts the entry back into the requirement, so a bundle that mounts its own code is still checked. The reconnaissance is line-shaped, not a YAML parser, and whatever it cannot read is left unjudged rather than turned into a requirement.preflight.ts(git channel) andverifyInstalledEntry(post-install, npm channel) both check those files, so the two channels keep agreeing; a bundle package now has to ship the patch it declares. The tar is listed once instead of once per file.src/client/logic/urls.ts,src/client/locales.ts, both languages), name the file the package needs and where it comes from (its declared entry, an entry a patch mounts, or the declared patch) instead of calling every miss an entry file.docs/architecture.mdanddocs/development.md: the pre-flight, post-install and test notes describe declared distribution files rather than "the entry file".Type of change
How it was verified
npm run typecheckpassesnpm testpassesnpm run buildpassesThe decision itself, base (this branch's parent) vs this branch, computed with the hub's own functions on real codeload tarballs.
preflightTargetcannot run directly from my network (itsgit ls-remotehitsPREFLIGHT_TIMEOUT_MS, and that path returnsok: trueby design), so I ran its steps against the downloaded archives:QingYunA/answer-me-with-html@90c8d49d—dsh.bundle.patch, nomain/exportsentry=index.js→ ok=false, missing=index.jsrequired=['cordis.patch.yml']→ ok=truewjvalue/dsh-answer-me-with-html@57fdca9d— same shapeindex.jsmain: ./lib/index.js, archive has nolib/lib/index.jslib/index.jsA bundle whose declared patch is missing is now caught instead of passing unevaluated, which is the other half of the same rule.
node --test tests/package-entry.test.ts tests/preflight.test.ts— 14 tests, 14 pass. They pin each shape: a manifest that declares no entry has none, the patch rules, the archive-listing decision, and the self-mount rule (the skill-only patch from the report mounts nothing of its own, the guide's- insert: - name: <package>does).npm run checkon the final commit (dacafe6) — typecheck, 131 tests (129 pass, 0 fail, 2 skipped) and build: all pass.QingYunA/answer-me-with-html@90c8d49d— patch rows["@deepseek-ai/dsh-skill-filesystem"]["cordis.patch.yml"]wjvalue/dsh-answer-me-with-html@57fdca9d— same shape["cordis.patch.yml"]["index.js","cordis.patch.yml"]index.jscommitted["index.js","cordis.patch.yml"]DSH_HOME, installing the distribution from #209 (pnpm 10.34.7, the one the hub drives):exit 0, no stderr: the bundle's patch composes into the plugin tree, and nothing imports the package.
Notes for reviewers
install.tsalso gates the catalogue npm-target resolution on the preflight result (root.ok && root.name). An entry-less package now returnsok: truethere, but that branch still requires a registry package under that name to point back to the repo and identify as a DSH plugin (isDshNpmPackageForRepo), so the outcome is unchanged for this shape — it only stops the guard from vetoing the lookup.