Skip to main content
S

dsh-nanshan-publish-manager

southm/dsh-nanshan-publish-manager

Publish locally developed DSH components as npm packages: component cards, release settings, publish pre-checks, and a publish history. Its HTTP routes have no DSH access check, and they can publish packages to npm with the token you give it.

Install

dsh plugin --profile web add github:southm/dsh-nanshan-publish-manager

README

【南山】DSH发布Npm助手

把本机开发的 DSH 组件,按 DeepSeek Harness 官方推荐的 npm 包形式发布给其他人安装。

  • 一个组件清单页:卡片形式展示本机所有可发布的 DSH 组件(dsh.bundle / dsh.client),包含版本、发布状态、预检查结果和最近一次发布。
  • 一个组件详情页:点击卡片进入,用「流程化板块」完成发布——版本与标签 → 发布目标与选项 → 发布预检查 → 确认与执行,右侧常驻该组件的 NPM信息查询、发布记录列表。

界面采用现代极简的开发者工具控制台风格(Linear / Vercel / Raycast 一类的浅色克制审美),跟随 Harness 的浅色 / 深色主题。插件图标、侧栏入口和页面标识使用 3D 液态玻璃黑色调;搜索、勾选、关闭等功能图标保持线性,以保证小尺寸下的可读性。


官方发布形式

DSH 组件就是一个 npm 包:

{
  "name": "@your-scope/your-plugin",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": "./index.js",
    "./client": "./client.js",          // 有 UI 时
    "./locale/*.json": "./locale/*.json",
    "./icon": "./icon.svg"
  },
  "files": ["index.js", "client.js", "cordis.patch.yml", "locale/*.json", "icon.svg", "README.md"],
  "dsh": {
    "bundle": { "patch": "./cordis.patch.yml" },
    "client": {
      "platform": "web",
      "immediately": true,
      "inject": ["@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-layout", "@deepseek-ai/dsh-client-ui-sidebar"]
    }
  }
}

cordis.patch.yml 插入插件行:

- insert:
    - id: your-plugin
      name: '@your-scope/your-plugin'

发布到 npm 之后,其他人在 DSH 的插件页安装这个包名即可使用。本插件就是按这套形式检查、打包和发布的。

发布预检查

静态预检查(进入详情页立即显示):

预检查项说明
package.json 可读组件目录必须有可解析的包清单
包名符合 npm 规范小写字母、数字与 - . _ ~,作用域包为 @scope/name
版本号符合语义化版本如 1.2.3、1.3.0-beta.1
未标记 private"private": true 的包无法发布
包含描述 / 许可证影响 npm 与插件页展示
使用可发布的作用域@local/... 只是本地占位,发布会提示
bundle patch 文件存在dsh.bundle.patch 指向的 cordis.patch.yml 必须存在
patch 的 name 与包名一致官方形式要求 insert 行的 name 等于包名
dsh.client 入口存在声明了 dsh.client 就要有 client.js
导出 locale / 图标exports 中的 ./locale/*.json 与 ./icon
files 覆盖运行文件files 必须覆盖 index / client / patch / 图标
图标有效SVG / PNG / JPEG / WebP,且不超过 256 KiB
locale 包含标题与描述读取 meta.title / meta.description(兼容顶层 title)
包含 README说明安装与使用方式

完整预检查(点击「发布预检查」)还会执行:

预检查项说明
找到 npm自动从 PATH、Homebrew、nvm、Volta 等位置查找
已登录 npm registry运行 npm whoami;演练时只提示不阻断
registry 可访问npm view <name> versions dist-tags --json,首次发布 404 视为正常
目标版本未被占用已发布版本不能被覆盖
版本高于已发布版本低于最新版只提示
npm 打包内容完整npm pack --dry-run --json,逐个确认运行文件与资源会进入包
构建脚本存在配置了构建脚本时检查 scripts 中是否存在

发布流程

  1. 版本与标签 —— 目标版本(写回 package.json)、npm tag、public / restricted。
  2. 发布目标与选项 —— registry(默认读取你的 ~/.npmrc)、可选构建脚本(npm run <script>)、自动运行发布预检查、先演练、自动写入版本号、发布备注。
  3. 发布预检查 —— 运行发布预检查;有阻断项时正式发布按钮不可用。
  4. 确认与执行 —— 选择发布验证方式,勾选确认,先「仅演练」(npm publish --dry-run,临时写入的目标版本会在演练后还原)或「正式发布」(带最终确认弹窗);本机会话方式的最终弹窗按钮为:左侧「再想想」,右侧「采用终端发布(推荐)」和「采用 OTP 发布」。
  5. 发布记录 —— 每次演练 / 发布 / 失败都会写入本机记录,包含版本、tag、access、验证方式、耗时、预检查摘要和命令输出;终端发布会在记录里标记「终端」。
  6. NPM信息查询 —— 右侧栏显示当前包名、近 7 天每日下载量柱状图(数据来自 npm downloads API)和「NPM查看该组件」入口。

发布验证方式

npm 已在 2025-12-09 永久撤销全部经典 Token,本插件不再提供该选项,只支持当前仍在使用的三种方式:

方式适用场景凭据处理
本机登录会话 + OTP(默认)本机交互发布;npm login 生成 2 小时会话,发布时 npm 强制 2FAOTP 通过 npm_config_otp 环境变量交给 npm,不写命令、不落盘
Granular Access Token无人值守、CI 之外的自动化;可在 npm 设置中开启 Bypass 2FA令牌只用于本次发布,写入权限为 0600 的临时 npmrc,发布结束立即删除;不保存、不写日志
OIDC 可信发布仅 GitHub Actions(id-token: write)或 GitLab CI(NPM_ID_TOKEN)npm 自己用 CI 的 ID Token 换取短期发布令牌,插件不接触任何长期凭据

本地 DSH 中 OIDC 选项会显示为不可用;在支持的 CI 环境里会自动变为可用。注意:OIDC 还需要在 npm 包的设置里配置对应的 Trusted Publisher。

Granular Access Token 可以在 npm 网站 Settings → Tokens 或 npm token create 创建;写令牌最长 90 天,无人值守发布建议开启 Bypass 2FA。

页面内的 OTP 只支持 TOTP(身份验证器 6 位数字)。如果账号使用安全密钥 / 网页型 2FA,npm 需要在交互式终端里打开浏览器授权;这种情况下可以直接在确认弹窗点「采用终端发布(推荐)」,或创建一个开启 Bypass 2FA 的 Granular Access Token 后使用「细粒度令牌」方式。

npm 凭据不经过插件的存储层:本机会话使用你自己的 ~/.npmrc;Granular Token 的临时 npmrc 在发布后删除;OTP 只作用于一次子进程。三者都不会写入发布记录。

在系统终端发布

在「本机登录会话」方式下,最终确认弹窗提供「采用终端发布(推荐)」:

  • macOS 打开 Terminal.app;
  • Windows 打开 cmd.exe;
  • Linux 尝试 x-terminal-emulator、gnome-terminal、konsole、xfce4-terminal、xterm。

插件会生成一个临时脚本并自动执行实际发布命令(配置了构建脚本时先执行构建)。你在系统终端里完成 OTP 或浏览器/安全密钥验证;插件等待最多 15 分钟,读取退出码后把结果写入发布记录,记录带「终端」标记。终端输出保留在系统终端窗口,不写入插件记录,避免 OTP 进入日志。

如果终端没有自动出现,或等待超时,插件会弹出居中的错误弹窗;终端窗口里的完整输出仍然保留,可据此排查。

OTP 的时机

OTP 不是插件强制每次都要填写的字段:

  • 本机会话:当 npm 对你的账号或这个包要求发布 2FA 时,每一次发布尝试都会发起挑战。填写后才随请求发送;不填时若 npm 挑战,会返回 EOTP,并弹出居中的错误弹窗、清空旧码;关闭弹窗后重新点「正式发布」即可重试。验证码一次性、约 30 秒有效,不能复用。
  • 填写时机:OTP 只在点击「正式发布」后的确认弹窗里填写。UI 已经运行过发布预检查时,服务端不会在点击后再次执行整套 npm 预检查,因此验证码会在确认后的极短时间内使用,避免被前置预检查拖到过期。
  • Granular Access Token:令牌开启 Bypass 2FA 时不需要 OTP;未开启时 npm 仍会挑战,建议改用本机会话 + OTP 或换一个开启 Bypass 2FA 的令牌。
  • OIDC 可信发布:由 CI 的 ID Token 换发短期令牌,不需要 OTP。
  • 仅演练:不访问 registry,不需要 OTP。

发布命令如何组织

实际执行的命令按场景生成,日志里的命令就是真实 argv,并在末尾用注释标出凭据来源:

场景命令
有作用域包(@scope/name)npm publish --tag <tag> --access <public|restricted> --registry <registry>
无作用域包npm publish --tag <tag> --registry <registry>(无作用域包只能是 public,省略 --access)
本机会话 + OTP同上,OTP 通过 npm_config_otp 环境变量传递,日志注 # auth: local session + OTP (npm_config_otp)
细粒度令牌同上,令牌通过一次性 npmrc(npm_config_userconfig)传递,日志注 # auth: granular access token (temporary npmrc)
OIDC同上,npm 自动完成 OIDC 交换,日志注 # auth: OIDC trusted publishing
在系统终端发布同上,在系统终端里交互完成 OTP / 网页 2FA,日志注 # auth: local session (system terminal)
仅演练追加 --dry-run,不注认证信息

预发布版本(如 1.0.0-beta.1)选择 latest 时会给出警告,建议改用 beta / next;目标版本低于已发布最新版时,npm 会拒绝把 latest 指回旧版本,插件会把该错误翻译成明确的提示。

如果 OTP 一直失败

按顺序排查:

  1. 确认验证码来自当前登录账号(npm whoami 显示 wangnanshan)绑定的身份验证器,并且是刚生成的;旧码不能复用。
  2. 确认系统时间自动同步。TOTP 依赖本机时钟;当前机器与 npm registry 的偏差约 0.6 秒,属于正常范围。
  3. 如果账号的 2FA 方式是安全密钥 / 网页认证而不是身份验证器 TOTP,页面内输入 6 位码不会成功。请在确认弹窗点「采用终端发布(推荐)」,让 npm 在系统终端里打开浏览器授权;或使用下面的令牌方案。
  4. 使用 Granular Access Token 时,必须在创建令牌时勾选 Bypass 2FA(npm token create --bypass-2fa);只粘贴一个未开启 Bypass 2FA 的令牌仍会返回 EOTP。
  5. 如果配置了构建脚本,构建会先于发布执行;构建较慢时验证码可能过期,建议使用开启 Bypass 2FA 的令牌。

失败信息现在会附带「到达发布执行时验证码已输入多少秒」:超过 25 秒更可能是过期;如果只有几秒仍被拒绝,则更可能是验证码不属于当前账号、验证码输错,或账号使用的是安全密钥 / 网页型 2FA 而不是 TOTP。

安装

在 Harness 的插件页安装这个 bundle 目录(推荐使用 Harness 的安装入口),安装后左侧栏会出现「【南山】DSH发布Npm助手」图标。

开发时也可以在 profile 里链接:

// ~/.dsh/profiles/<profile>/package.json
{
  "dependencies": {
    "@wangnanshan/dsh-nanshan-publish-manager": "link:/path/to/nanshan-publish-manager"
  },
  "dsh": { "profile": { "bundles": ["@wangnanshan/dsh-nanshan-publish-manager"] } }
}

配置

在 profile 的 cordis.patch.yml 中该插件的 config 里设置:

- insert:
    - id: nanshan-publish-manager
      name: '@wangnanshan/dsh-nanshan-publish-manager'
      config:
        scanRoots: []          # 额外扫描的组件目录(绝对路径或 ~/...)
        maxScanDepth: 2        # 扫描深度 1–3
        allowPublish: true     # false 时禁止正式发布,只能演练
        publishTimeoutMs: 180000
        # registry: https://registry.npmjs.org   # 覆盖默认 registry

组件发现顺序:

  1. 当前 profile 中 link: / file: 依赖指向的本地包;
  2. scanRoots 指定目录(按 maxScanDepth 递归);
  3. 当前工作区(DSH 的当前工作目录,最多两层);
  4. 在页面上用「添加组件」手动选择的目录(持久保存)。

只收录带 dsh.bundle 或 dsh.client 的包。

数据与接口

数据文件:$DSH_HOME/nanshan-publish-manager/publish-state.json

  • 组件发布设置(按目录保存)
  • 发布记录(每个组件最多 120 条)
  • 手动添加的组件目录

HTTP 接口(仅本机、同源写入):

方法路径作用
GET/api/nanshan-publish-manager组件清单、npm 状态、扫描信息;?refresh=1 跳过缓存
GET/api/nanshan-publish-manager/component?dir=组件详情、静态预检查、设置、发布记录
POST/api/nanshan-publish-manager/check运行发布预检查(含 npm 命令)
POST/api/nanshan-publish-manager/settings保存发布设置
POST/api/nanshan-publish-manager/targets添加组件目录
POST/api/nanshan-publish-manager/publish演练或正式发布;body 可带 authMode(session / token / oidc)与 accessToken(仅 token 模式,不落盘)

接口只接受 loopback 请求;写操作校验同源;组件目录必须是带 dsh.bundle / dsh.client 的包,避免把任意目录交给 npm。

已知限制

  • 需要本机安装 Node.js 与 npm(版本检查会显示 npm 版本,找不到时页面顶部提示)。
  • 正式发布需要先 npm login(2 小时会话,默认网页授权);未登录时只能演练。
  • Granular Access Token 若未开启 Bypass 2FA,发布时仍会被要求 OTP;此时请改用本机会话 + OTP。
  • 发布使用本机 npm 配置,不支持在页面里输入 token;OTP 只用于单次发布,不落盘。
  • 系统终端发布支持 macOS Terminal、Windows cmd.exe 和 Linux 常见终端;Windows 的 npm.cmd 已做启动适配,但当前环境主要在 macOS 验证。
  • 终端发布最多等待 15 分钟;关闭终端或未完成验证会超时,不会写入成功记录;终端输出不写入插件记录。
  • 近 7 天下载量来自 npm downloads API,统计通常滞后约 1 天;新包或未发布包可能暂无数据。
  • 页面直接执行 npm publish,请在发布前用「仅演练」确认打包内容。
  • npm publish 会执行组件 package.json 中的 publish 生命周期脚本;请只发布你信任的组件。

Related plugins