故事的起点是一条简单的命令:
dsh plugin --profile web add dsh-skills-nexus
期望的结果是安装 dsh-skills-nexus 插件,但实际返回的是:
[ERR_PNPM_FETCH_404] GET https://registry.npmmirror.com/dsh-skills-nexus: Not Found - 404
This error happened while installing a direct dependency of C:\Users\asswsw\.dsh\profiles\web
dsh-skills-nexus is not in the npm registry, or you have no permission to fetch it.
错误信息很明确:dsh-skills-nexus 不在 npm registry 中。这意味着我们需要将它发布到 npm。
但发布一个 npm 包远不止 npm publish 那么简单。本文将完整记录从问题诊断到成功发布的全过程,包括踩过的坑、做出的决策、以及最终验证的闭环。
第一章:发布渠道决策——为什么选择 npmjs 而非 GitHub Packages
1.1 初步调研
首先确认生态现状:
npm view dsh-skills-nexus version
# npm error code E404
# npm error 404 Not Found - GET https://registry.npmjs.org/dsh-skills-nexus - Not found
npm view dsh-research version
# 0.4.3
npm view @deepseek-ai/dsh-host-webserver version
# 0.0.1-rc.1
结论:dsh-skills-nexus 名字在 npmjs 公共 registry 上可用(未被占用),而同类包 dsh-research 和 @deepseek-ai/dsh-host-webserver 都在公共 npm registry 上。
1.2 GitHub Packages 可行性分析
用户提出:「如果发布 GitHub package 也做到安装的话也行」。我们评估了三个硬伤:
硬伤一:强制 scoped 包名
- GitHub Packages 的 npm registry 强制
@owner/name形态 dsh plugin add dsh-skills-nexus需要 unscoped 裸名- 装
dsh-skills-nexus这个裸名在 GitHub Packages 根本不存在
硬伤二:公开包也要鉴权
- GitHub Packages 即使是 public 包,安装也需要
read:packages的 PAT - 错误日志中
No authorization header was set for the request恰好证明 dsh profile 的 pnpm 环境没有任何 auth 配置 - npmmirror 不代理 GitHub Packages
硬伤三:破坏一条命令安装的承诺
- 每个消费者都得改
.npmrc(scope + token) - 与 README 主打的
dsh plugin add单命令体验冲突
最终决策:发布到 npmjs.com 公共 registry 是唯一正解。
第二章:发布就绪度检查
2.1 package.json 审查
检查当前 package.json 是否具备发布形态:
{
"name": "dsh-skills-nexus",
"version": "0.3.0",
"bin": {
"dsh-skills-nexus": "lib/cli/index.js"
},
"exports": {
".": "./lib/index.js",
"./resolve": "./lib/resolve.js",
"./package.json": "./package.json"
},
"files": [
"lib",
"cordis.patch.yml",
"README.md"
],
"engines": {
"node": ">=20.0.0"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/xiaxi626/dsh-skills-nexus.git"
}
}
检查清单:
- ✅
name/version/description/license/repository齐全 - ✅
bin指向lib/cli/index.js - ✅
exports定义清晰 - ✅
files包含lib+cordis.patch.yml(dsh.bundle.patch激活所需)+README.md - ✅
engines声明 Node 版本要求 - ✅
lib/是已提交的构建产物,CI 有新鲜度门禁
结论:package.json 已是完整发布形态,无需修改。
2.2 版本策略决策
检查 git 状态:
git log --oneline -3
# 35a7d22 (HEAD -> master, origin/master) docs(cli): add SSH/git+https forms to --help
# 7585005 docs: add build-on-nexus guide for tool authors
# adc9e7f fix(completions): repair the fish names rule and the zsh expectation
git tag --list
# v0.3.0
git rev-parse v0.3.0
# 0f280b96690493052970f8b5f4a014ca4aba4d6e
git rev-parse HEAD
# 35a7d22795c7fa09d4c96b33932c76647389ad50
发现:v0.3.0 tag(0f280b9)≠ HEAD(35a7d22)。tag 之后又积了 completions P2、双语文档、--help SSH 补充等 [Unreleased] 条目。
两个选择:
- A:发布 v0.3.0 tag 内容(checkout 到 tag 时刻状态)
- B:发布 HEAD 内容(但版本号仍是 0.3.0,会导致 tag ≠ tarball)
决策:选择 A——发布 v0.3.0 tag 内容,保证 npm 包与 GitHub tag 严格对应、可追溯。master 上的 [Unreleased] 属于未来版本(0.4.0),不随 0.3.0 发布。
第三章:发布准备
3.1 添加 prepublishOnly 门禁
为了防止未来发布时手滑发坏包,在 HEAD 的 package.json 添加:
{
"scripts": {
"prepublishOnly": "npm run typecheck && npm run lint && npm test"
}
}
说明:
prepublishOnly会在npm publish时自动运行- 守护未来从 HEAD 发布的版本
- v0.3.0 发布不受影响(worktree 用 tag 的旧 package.json,没有这个脚本)
提交并推送:
git add package.json
git commit -m "build: run typecheck/lint/test automatically before npm publish"
git push
3.2 创建发布工作树
使用 git worktree 隔离 v0.3.0 tag 内容,避免污染当前开发分支:
git worktree add .release-v030 v0.3.0
# Preparing worktree (detached HEAD 35ee9ea)
# HEAD is now at 35ee9ea chore(release): v0.3.0
优势:
- 在独立目录 checkout v0.3.0 tag 内容
- 不影响主工作区的 master 分支
- 发布完成后一键清理
3.3 在工作树跑门禁
进入工作树,安装依赖并运行三道门禁:
cd .release-v030
npm ci
npm run typecheck # ✅ 通过
npm run lint # ✅ 通过
npm test # ✅ 135/135 通过,0 失败
注意:v0.3.0 的 package.json 没有 prepublishOnly,所以这里手动等价执行。
3.4 预览 tarball
确认将要发布的内容:
npm pack --dry-run
输出:
📦 dsh-skills-nexus@0.3.0
Tarball Contents
1.1kB LICENSE
14.8kB README.md
247B cordis.patch.yml
574B lib/cli/args.d.ts
... (共 72 个文件)
Tarball Details
name: dsh-skills-nexus
version: 0.3.0
package size: 42.8 kB
unpacked size: 153.8 kB
shasum: 8e9cfedc03bb0b0e0516e4832833848951daf04d
total files: 72
检查:
- ✅ 包含
lib/、cordis.patch.yml、README.md、LICENSE、package.json - ✅ 不包含
src/、test/、node_modules、.github - ✅ shasum 记录:
8e9cfedc03bb0b0e0516e4832833848951daf04d
第四章:Registry 配置陷阱
4.1 发现问题
检查本机 npm 配置:
npm config get registry
# https://registry.npmmirror.com
问题:用户全局 registry 指向 npmmirror.com(国内镜像),不是 npmjs.org。
进一步追查来源:
npm config get userconfig
# C:\Users\asswsw\.npmrc
Select-String -Path C:\Users\asswsw\.npmrc -Pattern "registry"
# registry=https://registry.npmmirror.com
根因:.npmrc 中配置了 registry=https://registry.npmmirror.com。这会让 npm login / npm publish 都打到镜像上——镜像不支持发布和登录。
4.2 解决方案
方案 A(推荐):保留镜像加速日常安装,登录/发布时显式指向官方源
npm login --registry=https://registry.npmjs.org
npm publish --registry=https://registry.npmjs.org
方案 B:干脆把默认源恢复成 npmjs 官方
npm config delete registry
选择方案 A——日常 npm install 继续享受镜像加速,只在发布时显式指定。
第五章:登录与发布
5.1 登录 npmjs
npm login --registry=https://registry.npmjs.org
# 浏览器打开 https://www.npmjs.com/auth/cli/...
# 授权 xiaxi626 账号
npm whoami --registry=https://registry.npmjs.org
# xiaxi626
5.2 发布
从工作树发布(注意:在 bash 中用 cd,不是 PowerShell 的 Push-Location):
cd .release-v030
npm publish --registry=https://registry.npmjs.org
踩坑记录:
- 第一次尝试时,用户在 bash 中执行了 PowerShell 命令
Push-Location,导致command not found - 结果
npm publish从主工作区(HEAD)执行,触发了prepublishOnly - ESLint 扫到了
.release-v030/lib/*.d.ts产物(因为工作树在主仓库内),报错Unexpected any - 教训:跨 shell 命令要注意语法差异,bash 用
cd,PowerShell 用Push-Location
正确执行后,收到 npm 确认邮件:
Hi xiaxi626!
A new version of the package dsh-skills-nexus (0.3.0) was published at 2026-09-23T12:46:53.537Z
The shasum of this package is 8e9cfedc03bb0b0e0516e4832833848951daf04d.
验证:shasum 8e9cfedc03bb0b0e0516e4832833848951daf04d 与发布前 npm pack --dry-run 预览完全一致——确认发布的包严格对应 v0.3.0 tag(commit 35ee9ea)。
第六章:验证与清理
6.1 验证发布
# 在 npmjs 官方 registry 查询
npm view dsh-skills-nexus version --registry=https://registry.npmjs.org
# 0.3.0
# 在 npmmirror 镜像查询(验证同步)
npm view dsh-skills-nexus version --registry=https://registry.npmmirror.com
# 0.3.0(已自动同步,无需手动触发)
6.2 验证安装
回到最初的命令:
dsh plugin --profile web add dsh-skills-nexus
# ✓ Lockfile passes supply-chain policies
# dependencies:
# + dsh-skills-nexus 0.3.0
# Done in 4.8s
成功!最初报错 404 的命令现在正常工作。
6.3 清理工作树
git worktree remove --force .release-v030
git status --short
# (空,工作区干净)
第七章:经验总结
7.1 Registry 配置陷阱
问题:用户全局 registry 设为镜像时,npm login / npm publish 会打到镜像上失败。
解决:登录/发布时显式 --registry=https://registry.npmjs.org。
最佳实践:
- 日常安装用镜像加速(
.npmrc配置registry=https://registry.npmmirror.com) - 发布时显式指定官方源
- 或者干脆不设全局 registry,用
--registryflag 按需指定
7.2 跨 Shell 命令陷阱
问题:Push-Location / Pop-Location 是 PowerShell 命令,bash 中需用 cd / cd ..。
教训:
- 写文档/教程时明确标注 shell 类型
- 跨 shell 操作时注意语法差异
- 误用会导致命令从错误目录执行
7.3 Worktree 方案优势
场景:从历史 tag 发布包,保证 npm 包与 GitHub tag 严格对应。
方案:
git worktree add .release-vX.Y.Z vX.Y.Z
cd .release-vX.Y.Z
npm publish --registry=https://registry.npmjs.org
cd ..
git worktree remove --force .release-vX.Y.Z
优势:
- 隔离 tag 内容,不污染当前开发分支
- master 上的
[Unreleased]条目属于未来版本,不随当前 tag 发布 - 发布完成后一键清理
7.4 npmmirror 同步
现象:发布后 ~10 分钟内自动同步,通常无需手动触发。
手动触发:访问 https://npmmirror.com/sync/dsh-skills-nexus
验证:npm view <package> version --registry=https://registry.npmmirror.com
7.5 发布后撤回窗口
规则:npm 允许发布后 72 小时内 npm unpublish,之后只能 deprecate。
建议:
- 发布前充分验证(门禁 + tarball 预览)
- 发布后立即验证(
npm view+ 实际安装测试) - 发现问题 72 小时内可撤回
第八章:后续发布流程
以后要发新版本(如把 [Unreleased] 切成 0.4.0):
8.1 切版本
按既有流程:
# 1. 版本 bump(同步改 package.json 与 package-lock.json)
npm version 0.4.0 --no-git-tag-version
# 2. CHANGELOG:在最新条目上方加 `## [0.4.0] - <日期>` 段头
# 3. 跑全门禁
npm run typecheck
npm run lint
npm test
npm run build
# 4. 提交
git add package.json package-lock.json CHANGELOG.md lib/
git commit -m "chore(release): v0.4.0"
# 5. 打 tag
git tag -a v0.4.0 -m "v0.4.0"
# 6. 推送
git push origin master --follow-tags
8.2 发布
从新 tag 的 worktree 发布:
# 1. 创建工作树
git worktree add .release-v0.4.0 v0.4.0
# 2. 进入工作树,安装依赖,跑门禁
cd .release-v0.4.0
npm ci
npm run typecheck
npm run lint
npm test
# 3. 发布(prepublishOnly 会自动跑门禁)
npm publish --registry=https://registry.npmjs.org
# 4. 清理
cd ..
git worktree remove --force .release-v0.4.0
说明:
prepublishOnly门禁会自动跑 typecheck/lint/test- 发布后 npmmirror 自动同步
- 用户
dsh plugin add dsh-skills-nexus会装 latest(0.4.0)
结语
从一条 404 错误到成功发布 npm 包,整个过程涉及:
- 渠道决策:排除 GitHub Packages,选择 npmjs 公共 registry
- 就绪度检查:确认 package.json 发布形态、版本策略
- 发布准备:添加 prepublishOnly 门禁、创建 worktree、跑门禁、预览 tarball
- 配置诊断:发现 registry 指向镜像,显式指定官方源
- 登录发布:浏览器授权、从 worktree 发布、验证 shasum
- 验证清理:确认 npmjs/npmmirror 可查、实际安装成功、清理工作树
关键教训:
- Registry 配置陷阱:镜像不支持发布,需显式指定官方源
- 跨 Shell 命令:bash 用
cd,PowerShell 用Push-Location - Worktree 方案:保证 npm 包与 GitHub tag 严格对应
- npmmirror 同步:自动同步,通常无需手动触发
最终结果:
- ✅
dsh-skills-nexus@0.3.0成功发布至 registry.npmjs.org - ✅ 发布内容与 v0.3.0 tag(commit
35ee9ea)严格对应 - ✅
dsh plugin --profile web add dsh-skills-nexus安装成功 - ✅ 工作区干净,临时工作树已清理
这次发布不仅解决了一个 404 错误,更建立了一套可重复的发布流程,为未来的版本迭代打下基础。
参考资料: