dsh-plugin 1.3.9 → 1.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
12
12
  import { homedir, release } from 'node:os';
13
13
  import { dirname, join, resolve } from 'node:path';
14
- import { spawn } from 'node:child_process';
14
+ import { spawn, spawnSync } from 'node:child_process';
15
15
  import { fetchViaCurl, gitLsRemote, probeUrl, systemProxy } from '../services/probe.js';
16
16
  import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubRepoOf, githubTarget, globalNpmPackagesOf, hasQueuedTarget, installTargetOf, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName } from '../services/install/install.js';
17
17
  import { recordInstalledVersion, recordResolvedNpmPackage, readInstalledVersions, removeInstalledVersion } from '../services/profile/installed-versions.js';
@@ -121,8 +121,13 @@ export function readInstalled(profile) {
121
121
  return {};
122
122
  }
123
123
  }
124
- /** 宿主 dsh CLI 版本:从入口(process.argv[1])向上找最近的 dsh/harness 相关 package.json;找不到返回 null。 */
124
+ /** 宿主 dsh CLI 版本:优先直接跑 `dsh --version`(与 pnpm/npm/git 同款同步探测,1500ms 超时兜底,
125
+ * 绝不挂起)—— 原先从 process.argv[1] 向上找 package.json 猜版本在部分宿主加载方式下取不到,
126
+ * 导致 issue 环境快照 DSH: unknown;拿不到再退回入口 package.json 查找(dsh 不在 PATH 时兜底)。 */
125
127
  function hostDshVersion() {
128
+ const viaCli = toolVersion('dsh', ['--version']);
129
+ if (viaCli)
130
+ return viaCli;
126
131
  const entry = process.argv[1];
127
132
  if (entry === undefined)
128
133
  return null;
@@ -145,11 +150,29 @@ function hostDshVersion() {
145
150
  }
146
151
  return null;
147
152
  }
153
+ /** 探测命令行工具的版本号(pnpm --version / npm --version / git --version):
154
+ * 未安装 / 不在 PATH / 超时一律返回 null —— issue 环境快照如实写 unknown,绝不挂起。
155
+ * (同步探测仅用于启动期/请求期的低频率 /env 快照,安装通道不经过这里。) */
156
+ function toolVersion(cmd, args) {
157
+ try {
158
+ const r = spawnSync(cmd, args, { timeout: 1500, encoding: 'utf8' });
159
+ if (r.error || r.status !== 0)
160
+ return null;
161
+ const v = (r.stdout ?? '').trim().split(/\r?\n/)[0];
162
+ return v || null;
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
148
168
  /** 宿主环境快照:提交 bug 时随 issue 附上,便于作者复现(前端 /env 端点返回)。 */
149
169
  function hostEnv(profile) {
150
170
  return {
151
171
  dshVersion: hostDshVersion(),
152
172
  nodeVersion: process.version,
173
+ pnpmVersion: toolVersion('pnpm', ['--version']),
174
+ npmVersion: toolVersion('npm', ['--version']),
175
+ gitVersion: toolVersion('git', ['--version']),
153
176
  platform: process.platform,
154
177
  arch: process.arch,
155
178
  release: release(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin",
3
- "version": "1.3.9",
3
+ "version": "1.3.11",
4
4
  "description": "A community plugin marketplace for DeepSeek Harness, built to the official plugin spec — browse, search and install 4000+ human-curated community plugins without leaving the app. · DeepSeek Harness 社区插件市场(遵循官方开发规范):4000+ 人工精选社区插件,每日更新。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -276,6 +276,17 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
276
276
  // 本机缺 pnpm,不是插件问题 —— 给安装指引,不引导提 Issue
277
277
  return h('div', { className: styles.failPrepareHint }, t('failPnpmMissingHint'))
278
278
  }
279
+ if (kind === 'pnpmStore') {
280
+ // pnpm 存在但 store 大版本不匹配(ERR_PNPM_UNEXPECTED_STORE):profile 目录旧依赖是
281
+ // 另一大版本 pnpm 装的,当前 pnpm 不认,任何插件装进该 profile 都会失败 ——
282
+ // 给清理重建指引,不引导提 Issue
283
+ return h('div', { className: styles.failPrepareHint }, t('failPnpmStoreHint'))
284
+ }
285
+ if (kind === 'pnpmPolicy') {
286
+ // pnpm 11 供应链安全策略拦截(minimumReleaseAge 拒收发布太新的包 / untrusted origin):
287
+ // 本机 pnpm 策略不放行,装任何「刚发布/新来源」插件都撞墙 —— 给豁免指引,不引导提 Issue
288
+ return h('div', { className: styles.failPrepareHint }, t('failPnpmPolicyHint'))
289
+ }
279
290
  if (kind === 'network') {
280
291
  // 安装前预检 / 安装日志里的连接失败([network] / git fetch 超时 / DNS / TLS):
281
292
  // 是本机网络不通或代理有问题,不是插件问题 —— 提示检查网络 + 直达「系统诊断」,
@@ -395,6 +395,15 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
395
395
  // 找到了 dsh 但缺 pnpm(dsh 用它管理 profile 插件;dsh 报 pnpm not found on PATH):
396
396
  // 本机缺 pnpm,不是插件问题 —— 给安装指引,不引导提 Issue
397
397
  ? h('div', { className: styles.failPrepareHint }, t('failPnpmMissingHint'))
398
+ : failureKind === 'pnpmStore'
399
+ // pnpm 存在但 store 大版本不匹配(ERR_PNPM_UNEXPECTED_STORE):profile 目录旧依赖是
400
+ // 另一大版本 pnpm 装的,当前 pnpm 不认,任何插件装进该 profile 都会失败 ——
401
+ // 给清理重建指引,不引导提 Issue
402
+ ? h('div', { className: styles.failPrepareHint }, t('failPnpmStoreHint'))
403
+ : failureKind === 'pnpmPolicy'
404
+ // pnpm 11 供应链安全策略拦截(minimumReleaseAge 拒收发布太新的包 / untrusted origin):
405
+ // 本机 pnpm 策略不放行,装任何「刚发布/新来源」插件都撞墙 —— 给豁免指引,不引导提 Issue
406
+ ? h('div', { className: styles.failPrepareHint }, t('failPnpmPolicyHint'))
398
407
  : failureKind === 'network'
399
408
  // 安装前预检 / 安装日志里的连接失败([network] / git fetch 超时 / DNS / TLS):
400
409
  // 是本机网络不通或代理有问题,不是插件问题 —— 提示检查网络 + 直达「系统诊断」,
@@ -313,6 +313,8 @@ export const zh = {
313
313
  failNetworkRunDiag: '去系统诊断检测网络',
314
314
  failDshMissingHint: '系统找不到 dsh 命令(dsh 未加入系统 PATH),无法调用安装器执行安装。请确认 DeepSeek Harness 已正确安装、dsh 已加入 PATH(或重新安装),然后重试。这不是插件本身的问题。',
315
315
  failPnpmMissingHint: '安装器找到了 dsh,但系统里没有 pnpm 命令(dsh 用它来管理 profile 插件)。请先安装 pnpm(如 npm install -g pnpm),确认 pnpm 已加入 PATH 后重试。这不是插件本身的问题。',
316
+ failPnpmStoreHint: '安装器调用 pnpm 时报「store 位置不匹配」(ERR_PNPM_UNEXPECTED_STORE):你的 profile 目录里旧插件依赖是用另一个大版本的 pnpm 生成的,当前 pnpm 出于安全不认旧目录,导致任何插件装进该 profile 都会失败。请删除该 profile 目录下的 node_modules 与 pnpm-lock.yaml(可先到「系统日志」或 ~/.dsh/profiles/<profile>/hub.log 查看完整报错),再重新安装。这不是插件本身的问题。',
317
+ failPnpmPolicyHint: '安装器调用 pnpm 时被其供应链安全策略拦截(ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION / Minimum release age:锁文件里包的发布时间太近被拒;或 untrusted origin:依赖来源未受本机 pnpm 信任)。这是 pnpm 11 的默认安全策略,装任何「刚发布/新来源」的插件都会撞墙,不是插件本身的问题。请在 pnpm 配置(pnpm-workspace.yaml 或 .npmrc)里加入 minimumReleaseAge: 0 豁免,或等发布冷却一段时间后重试。',
316
318
  // 自定义安装三卡片入口的渠道名(报错/日志溯源:本次安装从哪个入口发起)
317
319
  installChannelNpm: 'NPM 包',
318
320
  installChannelGit: 'GitHub 源码',
@@ -668,6 +670,8 @@ export const en = {
668
670
  failNetworkRunDiag: 'Run a network diagnostic',
669
671
  failDshMissingHint: 'The dsh command could not be found (it is not on the system PATH), so the installer could not run. Please make sure DeepSeek Harness is installed correctly and dsh is on your PATH (or reinstall it), then retry. This is not a problem with the plugin itself.',
670
672
  failPnpmMissingHint: 'The installer found dsh, but pnpm is missing on this machine (dsh uses it to manage profile plugins). Please install pnpm first (e.g. npm install -g pnpm), make sure it is on your PATH, then retry. This is not a problem with the plugin itself.',
673
+ failPnpmStoreHint: 'pnpm reported an unexpected store location (ERR_PNPM_UNEXPECTED_STORE): the profile directory was previously set up with a different major version of pnpm, and the current pnpm refuses to reuse it — no plugin can be installed into that profile. Delete node_modules and pnpm-lock.yaml under the profile directory (see System Logs or ~/.dsh/profiles/<profile>/hub.log for the full output), then reinstall. This is not a problem with the plugin itself.',
674
+ failPnpmPolicyHint: 'The installer was blocked by pnpm supply-chain security policy (ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION / Minimum release age: a package in the lockfile was published too recently; or untrusted origin: a dependency source is not trusted by pnpm). This is pnpm 11 default security policy — installing any freshly published or new-origin plugin hits the same wall, so this is not a problem with the plugin itself. Add minimumReleaseAge: 0 to your pnpm config (pnpm-workspace.yaml or .npmrc) to opt out, or retry once the release has cooled down.',
671
675
  // Custom install entry channel names (the three cards) — used in error/log traceability
672
676
  installChannelNpm: 'NPM package',
673
677
  installChannelGit: 'GitHub source',
@@ -151,7 +151,7 @@ export function removeNotification(id: number): NotificationRecord[] {
151
151
  return next
152
152
  }
153
153
 
154
- export type FailureKind = 'npmTooOld' | 'dshMissing' | 'pnpmMissing' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'network' | 'repo'
154
+ export type FailureKind = 'npmTooOld' | 'dshMissing' | 'pnpmMissing' | 'pnpmStore' | 'pnpmPolicy' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'network' | 'repo'
155
155
 
156
156
  /**
157
157
  * 失败归类,七态。无论底层机制如何(pnpm 白名单拦截 / 构建脚本被忽略 / prepare 失败),
@@ -167,6 +167,15 @@ export type FailureKind = 'npmTooOld' | 'dshMissing' | 'pnpmMissing' | 'pnpmIgno
167
167
  * 「pnpm: command not found」/ spawn ENOENT)—— 本机缺 pnpm(dsh 用 pnpm 管理 profile 插件),
168
168
  * 不是插件问题(dsh-plugin-hub#13:Linux 下 `dsh: pnpm not found on PATH` 被误归插件侧失败)
169
169
  * → 提示安装/开启 pnpm,不引导提 Issue
170
+ * - pnpmStore:pnpm 存在但报 store 位置不匹配(`ERR_PNPM_UNEXPECTED_STORE` / `Unexpected store
171
+ * location`)—— profile 目录的 node_modules 是用不同大版本的 pnpm 生成的,当前 pnpm 不认,
172
+ * 任何插件装进该 profile 都会失败;不是插件问题(dsh-plugin-hub#14:macOS 下
173
+ * `ERR_PNPM_UNEXPECTED_STORE` 被误归插件侧失败)→ 提示清理 profile 依赖目录重建,不引导提 Issue
174
+ * - pnpmPolicy:pnpm 11 的供应链安全策略拒绝安装(`ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION` /
175
+ * `Minimum release age` —— 锁文件里包的发布时间太近被拒;`untrusted origin` —— 依赖来源未被
176
+ * 本机 pnpm 信任)。拦的是「发布太新的包」与「未被信任的来源」,装任何新插件都会撞墙,不是
177
+ * 插件问题(dsh-plugin-hub#15/#16:用户装官方 dsh-plugin 也被这两类策略拦下并误归插件侧失败)
178
+ * → 提示在 pnpm 配置里豁免该策略(如 `minimumReleaseAge: 0`)或等发布冷却后重试,不引导提 Issue
170
179
  * - network:安装前连通性预检拦截(服务端 `[network]` 标记)或底层连接失败
171
180
  * (ERR_PNPM_GIT_FETCH_FAILED / ETIMEDOUT / DNS 解析 / TLS 握手 / 代理拒绝)——
172
181
  * 是本机网络不通/被墙/代理有问题,不是插件问题 → 提示检查网络,不引导提 Issue
@@ -196,6 +205,17 @@ export function classifyFailure(message: string): FailureKind {
196
205
  // 否则会被外层 "Command failed" 吞成「插件打包问题」,误导用户去提 Issue
197
206
  // (dsh-plugin-hub#12:Win 下 'dsh' 不在 PATH,cmd 报「不是内部或外部命令」被误归插件侧失败)
198
207
  if (/\[dsh-missing\]|不是内部或外部命令|is not recognized as an internal or external command|command not found|spawn dsh ENOENT/i.test(message)) return 'dshMissing'
208
+ // pnpm 大版本不一致(ERR_PNPM_UNEXPECTED_STORE / Unexpected store location):profile 目录里
209
+ // 旧依赖是另一个大版本 pnpm 生成的,当前 pnpm 出于安全不认旧 store —— 本机环境问题,
210
+ // 任何插件装进该 profile 都会同样失败,不是插件问题(dsh-plugin-hub#14:macOS 下
211
+ // `ERR_PNPM_UNEXPECTED_STORE` 被误归插件侧失败);提示清理依赖目录用当前 pnpm 重建,
212
+ // 不引导提 Issue。必须在 pnpmMissing 之后 —— pnpm 在(能跑起来报错),不是「找不到命令」。
213
+ if (/ERR_PNPM_UNEXPECTED_STORE|Unexpected store location/i.test(message)) return 'pnpmStore'
214
+ // pnpm 供应链安全策略拦截(pnpm 11:minimumReleaseAge 拒收「发布太新」的包 /
215
+ // untrusted origin 来源不受信任):pnpm 在、也连得上,纯粹是本机策略不放行 ——
216
+ // 装任何「新发布/非信任来源」的插件都会同样失败,不是插件问题(dsh-plugin-hub#15/#16)。
217
+ // 必须在 pnpmIgnoredBuild 之前 —— 该策略优先于「构建脚本被白名单拦截」,且两者都不引导提 Issue。
218
+ if (/ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION|Minimum release age|untrusted origin/i.test(message)) return 'pnpmPolicy'
199
219
  // 构建脚本被 pnpm 白名单(allowBuilds)拦截:插件的 prepare 脚本或依赖里的原生模块构建
200
220
  // 被默认拒绝(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED / ERR_PNPM_IGNORED_BUILDS)。
201
221
  // 这类错误出现即说明 pnpm 已成功 fetch 到 tarball(网络是通的),主因是插件构建脚本
@@ -42,6 +42,8 @@ function reasonTitleOf(kind: FailureKind): string {
42
42
  case 'npmTooOld': return 'npm too old to install'
43
43
  case 'dshMissing': return 'dsh command not found on the user machine'
44
44
  case 'pnpmMissing': return 'pnpm command not found on the user machine'
45
+ case 'pnpmStore': return 'pnpm store version mismatch on the user machine'
46
+ case 'pnpmPolicy': return 'pnpm supply-chain policy blocked the install on the user machine'
45
47
  case 'pnpmIgnoredBuild': return 'build scripts blocked by pnpm allowlist'
46
48
  case 'pluginPrepare': return 'plugin distribution incomplete'
47
49
  case 'network': return 'network failure on the user side'
@@ -73,7 +75,9 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
73
75
  ? 'plugin prepare/build script failed during install (packaging/distribution issue)'
74
76
  : kind === 'pnpmIgnoredBuild'
75
77
  ? 'plugin depends on a native module whose build script pnpm blocks by default (use a prebuilt variant)'
76
- : 'plugin-side install failure'
78
+ : kind === 'pnpmPolicy'
79
+ ? 'the pnpm supply-chain policy on the user machine blocked the install (minimum release age for freshly published packages / untrusted origin)'
80
+ : 'plugin-side install failure'
77
81
  const code = coreErrorCode(message)
78
82
  // 按给定核心摘要预算构建完整预填 URL(含 URL 编码);预算可调,供超长时逐档缩小
79
83
  const build = (coreChars: number): string => {
@@ -110,9 +114,16 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
110
114
  `- DSH: ${env.dshVersion ? `v${env.dshVersion}` : 'unknown'}`,
111
115
  `- Plugin Hub: v${PLUGIN_VERSION}`,
112
116
  `- Node: ${env.nodeVersion}`,
117
+ // 工具链版本:pnpm/npm/git —— 排查 ERR_PNPM_UNEXPECTED_STORE(store 大版本不匹配)
118
+ // 这类环境问题,作者一眼就能对号入座;探测不到如实写 unknown
119
+ `- pnpm: ${env.pnpmVersion ? `v${env.pnpmVersion}` : 'unknown'}`,
120
+ `- npm: ${env.npmVersion ? `v${env.npmVersion}` : 'unknown'}`,
121
+ `- git: ${env.gitVersion ? `v${env.gitVersion}` : 'unknown'}`,
113
122
  `- OS: ${env.platform} ${env.arch} (${env.release})`,
114
123
  `- Profile: \`${env.profile}\``,
115
124
  `- DSH Home: \`${env.dshHome}\``,
125
+ // 完整安装日志所在(正文受 URL 长度限制只能带摘要,完整日志在 hub.log,可复制/附加给作者)
126
+ `- Full log: \`~/.dsh/profiles/${env.profile}/hub.log\` (paste or attach for the full output)`,
116
127
  ]
117
128
  : []),
118
129
  // 该插件在插件中心的收录位置(详情页链接)
@@ -87,6 +87,12 @@ export interface ToastState {
87
87
  export interface EnvInfo {
88
88
  dshVersion: string | null
89
89
  nodeVersion: string
90
+ /** 本机 pnpm 版本(pnpm --version 首行;缺失返回 null)—— 排查 ERR_PNPM_UNEXPECTED_STORE 等 pnpm 环境问题 */
91
+ pnpmVersion: string | null
92
+ /** 本机 npm 版本(npm --version 首行;缺失返回 null) */
93
+ npmVersion: string | null
94
+ /** 本机 git 版本(git --version 首行;缺失返回 null) */
95
+ gitVersion: string | null
90
96
  platform: string
91
97
  arch: string
92
98
  release: string
@@ -12,7 +12,7 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
12
12
  import type { IncomingMessage, ServerResponse } from 'node:http'
13
13
  import { homedir, release } from 'node:os'
14
14
  import { dirname, join, resolve } from 'node:path'
15
- import { spawn } from 'node:child_process'
15
+ import { spawn, spawnSync } from 'node:child_process'
16
16
  import { fetchViaCurl, gitLsRemote, probeUrl, systemProxy } from '../services/probe.ts'
17
17
  import { activeTask, cancelTask, dumpLoaderEntries, getTask, githubRepoOf, githubTarget, globalNpmPackagesOf, hasQueuedTarget, installTargetOf, listPendingRestarts, readProfileArg, startPluginMutation, validPackageName, type LoaderHandle } from '../services/install/install.ts'
18
18
  import { recordInstalledVersion, recordResolvedNpmPackage, readInstalledVersions, removeInstalledVersion } from '../services/profile/installed-versions.ts'
@@ -149,8 +149,12 @@ export function readInstalled(profile: string): Record<string, string> {
149
149
  }
150
150
  }
151
151
 
152
- /** 宿主 dsh CLI 版本:从入口(process.argv[1])向上找最近的 dsh/harness 相关 package.json;找不到返回 null。 */
152
+ /** 宿主 dsh CLI 版本:优先直接跑 `dsh --version`(与 pnpm/npm/git 同款同步探测,1500ms 超时兜底,
153
+ * 绝不挂起)—— 原先从 process.argv[1] 向上找 package.json 猜版本在部分宿主加载方式下取不到,
154
+ * 导致 issue 环境快照 DSH: unknown;拿不到再退回入口 package.json 查找(dsh 不在 PATH 时兜底)。 */
153
155
  function hostDshVersion(): string | null {
156
+ const viaCli = toolVersion('dsh', ['--version'])
157
+ if (viaCli) return viaCli
154
158
  const entry = process.argv[1]
155
159
  if (entry === undefined) return null
156
160
  let dir = dirname(resolve(entry))
@@ -171,11 +175,28 @@ function hostDshVersion(): string | null {
171
175
  return null
172
176
  }
173
177
 
178
+ /** 探测命令行工具的版本号(pnpm --version / npm --version / git --version):
179
+ * 未安装 / 不在 PATH / 超时一律返回 null —— issue 环境快照如实写 unknown,绝不挂起。
180
+ * (同步探测仅用于启动期/请求期的低频率 /env 快照,安装通道不经过这里。) */
181
+ function toolVersion(cmd: string, args: string[]): string | null {
182
+ try {
183
+ const r = spawnSync(cmd, args, { timeout: 1500, encoding: 'utf8' })
184
+ if (r.error || r.status !== 0) return null
185
+ const v = (r.stdout ?? '').trim().split(/\r?\n/)[0]
186
+ return v || null
187
+ } catch {
188
+ return null
189
+ }
190
+ }
191
+
174
192
  /** 宿主环境快照:提交 bug 时随 issue 附上,便于作者复现(前端 /env 端点返回)。 */
175
193
  function hostEnv(profile: string): Record<string, string | null> {
176
194
  return {
177
195
  dshVersion: hostDshVersion(),
178
196
  nodeVersion: process.version,
197
+ pnpmVersion: toolVersion('pnpm', ['--version']),
198
+ npmVersion: toolVersion('npm', ['--version']),
199
+ gitVersion: toolVersion('git', ['--version']),
179
200
  platform: process.platform,
180
201
  arch: process.arch,
181
202
  release: release(),