dshmarket 1.20.0 → 1.20.1

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.
package/lib/dsh-cli.js CHANGED
@@ -143,15 +143,56 @@ export function proxyEnvForPnpm(env = process.env, region = 'global') {
143
143
  }
144
144
  return out;
145
145
  }
146
+ /**
147
+ * Directories to append to PATH so a spawned pnpm can be found (#32, #38,
148
+ * #167, #292).
149
+ *
150
+ * A GUI or desktop launch inherits none of the shell profile, so PATH holds
151
+ * whatever the launcher had — usually not the directory the user's package
152
+ * manager lives in. The market appends the places it is actually installed
153
+ * to rather than telling the user to fix their environment.
154
+ *
155
+ * Windows used to get only the Node directory, which made the market's own
156
+ * advice unfollowable: the error it prints recommends installing pnpm with
157
+ * `iwr https://get.pnpm.io/install.ps1`, and then it did not look where that
158
+ * installer puts it (#292). Both Windows layouts are covered now — the
159
+ * standalone installer's `%LOCALAPPDATA%\pnpm`, and `%APPDATA%\npm` where
160
+ * `npm i -g pnpm` writes `pnpm.cmd`.
161
+ *
162
+ * `PNPM_HOME` comes first on every platform: the installer sets it, so it is
163
+ * the one answer that is right even when the layout is not the default one.
164
+ *
165
+ * @param platform - `process.platform`, injectable for tests.
166
+ * @param env - environment, for PNPM_HOME and the Windows app-data roots.
167
+ * @param home - home directory, injectable for tests.
168
+ */
169
+ export function toolSearchDirs(platform = process.platform, env = process.env, home = homedir()) {
170
+ const dirs = [];
171
+ const pnpmHome = (env.PNPM_HOME ?? '').trim();
172
+ if (pnpmHome !== '')
173
+ dirs.push(pnpmHome);
174
+ if (platform === 'win32') {
175
+ const local = (env.LOCALAPPDATA ?? '').trim();
176
+ const roaming = (env.APPDATA ?? '').trim();
177
+ if (local !== '')
178
+ dirs.push(join(local, 'pnpm'));
179
+ if (roaming !== '')
180
+ dirs.push(join(roaming, 'npm'));
181
+ }
182
+ else {
183
+ dirs.push('/opt/homebrew/bin', '/usr/local/bin', join(home, '.local', 'bin'));
184
+ // Where the standalone installer lands when PNPM_HOME is unset.
185
+ dirs.push(join(home, 'Library', 'pnpm'), join(home, '.local', 'share', 'pnpm'));
186
+ }
187
+ dirs.push(nodeBinDir, ...extraPathDirs);
188
+ return dirs;
189
+ }
146
190
  function spawnEnv() {
147
191
  // pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
148
192
  // CI mode forces it to act or fail instead of asking.
149
193
  const separator = process.platform === 'win32' ? ';' : ':';
150
194
  const parts = (process.env.PATH ?? '').split(separator).filter(part => part !== '');
151
- const candidates = process.platform === 'win32'
152
- ? [nodeBinDir, ...extraPathDirs]
153
- : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), nodeBinDir, ...extraPathDirs];
154
- for (const bin of candidates) {
195
+ for (const bin of toolSearchDirs()) {
155
196
  if (!parts.includes(bin))
156
197
  parts.push(bin);
157
198
  }
@@ -417,7 +458,13 @@ export function provisionHint(corepackOutput, npmOutput, npmFound = true) {
417
458
  // holds on a Windows console that reports the same thing in a codepage we
418
459
  // cannot read (#167); the ENOENT match stays for callers without it.
419
460
  if (!npmFound || (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput))) {
420
- return `这台机器的 dsh 进程找不到 npm/corepack(图形界面或桌面端启动时不继承终端 PATH)。已在 Node 自己的目录里找过(${nodeBinDir})也没有——多半是宿主内置的 Node 运行时不带 npm。请改从终端启动 dsh,或单独装一个 pnpm:Windows 用 iwr https://get.pnpm.io/install.ps1 -useb | iex,macOS/Linux 用 brew install pnpm / This dsh process cannot find npm/corepack (GUI and desktop launches skip your shell PATH). The directory Node itself runs from (${nodeBinDir}) was searched too a bundled Node runtime without npm is the usual cause. Start dsh from a terminal, or install pnpm on its own: \`iwr https://get.pnpm.io/install.ps1 -useb | iex\` (Windows) or \`brew install pnpm\` (macOS/Linux)`;
461
+ // The searched list is spelled out because the previous wording named
462
+ // only the Node directory, which was both incomplete and unhelpful: a
463
+ // user who HAD installed pnpm could not tell whether we looked in the
464
+ // right place (#292). And the restart note matters — the installer sets
465
+ // PNPM_HOME for new sessions, so a dsh already running cannot see it.
466
+ const searched = toolSearchDirs().join(process.platform === 'win32' ? ' ; ' : ' : ');
467
+ return `这台机器的 dsh 进程找不到 npm/corepack(图形界面或桌面端启动时不继承终端 PATH)——多半是宿主内置的 Node 运行时不带 npm。已找过:${searched}。请改从终端启动 dsh,或单独装一个 pnpm:Windows 用 iwr https://get.pnpm.io/install.ps1 -useb | iex,macOS/Linux 用 brew install pnpm。装完后请重启 dsh——安装器只对新开的会话生效,正在运行的进程看不到它 / This dsh process cannot find npm/corepack (GUI and desktop launches skip your shell PATH); a bundled Node runtime without npm is the usual cause. Searched: ${searched}. Start dsh from a terminal, or install pnpm on its own: \`iwr https://get.pnpm.io/install.ps1 -useb | iex\` (Windows) or \`brew install pnpm\` (macOS/Linux). Restart dsh afterwards — the installer only affects new sessions, so an already-running process cannot see it`;
421
468
  }
422
469
  if (/EEXIST|already exists|--force to overwrite/i.test(npmOutput)) {
423
470
  return 'pnpm 的可执行文件已存在(通常是 corepack 先放好了同名 shim),npm 拒绝覆盖。在终端里执行其一即可:corepack prepare pnpm@latest --activate(推荐,直接激活已有 shim)或 npm i -g pnpm --force / A pnpm executable already exists (usually a corepack shim), so npm refused to overwrite it. Run one of these in a terminal: `corepack prepare pnpm@latest --activate` (preferred — activates the shim already there) or `npm i -g pnpm --force`';
@@ -54,6 +54,30 @@ export declare const nodeBinDir: string;
54
54
  * must keep excluding it.
55
55
  */
56
56
  export declare function proxyEnvForPnpm(env?: NodeJS.ProcessEnv, region?: Region): NodeJS.ProcessEnv;
57
+ /**
58
+ * Directories to append to PATH so a spawned pnpm can be found (#32, #38,
59
+ * #167, #292).
60
+ *
61
+ * A GUI or desktop launch inherits none of the shell profile, so PATH holds
62
+ * whatever the launcher had — usually not the directory the user's package
63
+ * manager lives in. The market appends the places it is actually installed
64
+ * to rather than telling the user to fix their environment.
65
+ *
66
+ * Windows used to get only the Node directory, which made the market's own
67
+ * advice unfollowable: the error it prints recommends installing pnpm with
68
+ * `iwr https://get.pnpm.io/install.ps1`, and then it did not look where that
69
+ * installer puts it (#292). Both Windows layouts are covered now — the
70
+ * standalone installer's `%LOCALAPPDATA%\pnpm`, and `%APPDATA%\npm` where
71
+ * `npm i -g pnpm` writes `pnpm.cmd`.
72
+ *
73
+ * `PNPM_HOME` comes first on every platform: the installer sets it, so it is
74
+ * the one answer that is right even when the layout is not the default one.
75
+ *
76
+ * @param platform - `process.platform`, injectable for tests.
77
+ * @param env - environment, for PNPM_HOME and the Windows app-data roots.
78
+ * @param home - home directory, injectable for tests.
79
+ */
80
+ export declare function toolSearchDirs(platform?: string, env?: NodeJS.ProcessEnv, home?: string): string[];
57
81
  /**
58
82
  * Windows npm/corepack/pnpm are `.cmd` shims. Node's `spawn` without a shell
59
83
  * cannot start them (ENOENT / EINVAL). Same pattern as dsh's `plugin` forwarder.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dshmarket",
3
3
  "description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
4
- "version": "1.20.0",
4
+ "version": "1.20.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
package/src/dsh-cli.ts CHANGED
@@ -144,15 +144,57 @@ export function proxyEnvForPnpm(env: NodeJS.ProcessEnv = process.env, region: Re
144
144
  return out
145
145
  }
146
146
 
147
+ /**
148
+ * Directories to append to PATH so a spawned pnpm can be found (#32, #38,
149
+ * #167, #292).
150
+ *
151
+ * A GUI or desktop launch inherits none of the shell profile, so PATH holds
152
+ * whatever the launcher had — usually not the directory the user's package
153
+ * manager lives in. The market appends the places it is actually installed
154
+ * to rather than telling the user to fix their environment.
155
+ *
156
+ * Windows used to get only the Node directory, which made the market's own
157
+ * advice unfollowable: the error it prints recommends installing pnpm with
158
+ * `iwr https://get.pnpm.io/install.ps1`, and then it did not look where that
159
+ * installer puts it (#292). Both Windows layouts are covered now — the
160
+ * standalone installer's `%LOCALAPPDATA%\pnpm`, and `%APPDATA%\npm` where
161
+ * `npm i -g pnpm` writes `pnpm.cmd`.
162
+ *
163
+ * `PNPM_HOME` comes first on every platform: the installer sets it, so it is
164
+ * the one answer that is right even when the layout is not the default one.
165
+ *
166
+ * @param platform - `process.platform`, injectable for tests.
167
+ * @param env - environment, for PNPM_HOME and the Windows app-data roots.
168
+ * @param home - home directory, injectable for tests.
169
+ */
170
+ export function toolSearchDirs(
171
+ platform: string = process.platform,
172
+ env: NodeJS.ProcessEnv = process.env,
173
+ home: string = homedir(),
174
+ ): string[] {
175
+ const dirs: string[] = []
176
+ const pnpmHome = (env.PNPM_HOME ?? '').trim()
177
+ if (pnpmHome !== '') dirs.push(pnpmHome)
178
+ if (platform === 'win32') {
179
+ const local = (env.LOCALAPPDATA ?? '').trim()
180
+ const roaming = (env.APPDATA ?? '').trim()
181
+ if (local !== '') dirs.push(join(local, 'pnpm'))
182
+ if (roaming !== '') dirs.push(join(roaming, 'npm'))
183
+ } else {
184
+ dirs.push('/opt/homebrew/bin', '/usr/local/bin', join(home, '.local', 'bin'))
185
+ // Where the standalone installer lands when PNPM_HOME is unset.
186
+ dirs.push(join(home, 'Library', 'pnpm'), join(home, '.local', 'share', 'pnpm'))
187
+ }
188
+ dirs.push(nodeBinDir, ...extraPathDirs)
189
+ return dirs
190
+ }
191
+
147
192
  function spawnEnv(): NodeJS.ProcessEnv {
148
193
  // pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
149
194
  // CI mode forces it to act or fail instead of asking.
150
195
  const separator = process.platform === 'win32' ? ';' : ':'
151
196
  const parts = (process.env.PATH ?? '').split(separator).filter(part => part !== '')
152
- const candidates = process.platform === 'win32'
153
- ? [nodeBinDir, ...extraPathDirs]
154
- : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), nodeBinDir, ...extraPathDirs]
155
- for (const bin of candidates) {
197
+ for (const bin of toolSearchDirs()) {
156
198
  if (!parts.includes(bin)) parts.push(bin)
157
199
  }
158
200
  return { ...process.env, ...proxyEnvForPnpm(process.env, activeRegion()), CI: 'true', PATH: parts.join(separator) }
@@ -491,7 +533,13 @@ export function provisionHint(corepackOutput: string, npmOutput: string, npmFoun
491
533
  // holds on a Windows console that reports the same thing in a codepage we
492
534
  // cannot read (#167); the ENOENT match stays for callers without it.
493
535
  if (!npmFound || (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput))) {
494
- return `这台机器的 dsh 进程找不到 npm/corepack(图形界面或桌面端启动时不继承终端 PATH)。已在 Node 自己的目录里找过(${nodeBinDir})也没有——多半是宿主内置的 Node 运行时不带 npm。请改从终端启动 dsh,或单独装一个 pnpm:Windows 用 iwr https://get.pnpm.io/install.ps1 -useb | iex,macOS/Linux 用 brew install pnpm / This dsh process cannot find npm/corepack (GUI and desktop launches skip your shell PATH). The directory Node itself runs from (${nodeBinDir}) was searched too a bundled Node runtime without npm is the usual cause. Start dsh from a terminal, or install pnpm on its own: \`iwr https://get.pnpm.io/install.ps1 -useb | iex\` (Windows) or \`brew install pnpm\` (macOS/Linux)`
536
+ // The searched list is spelled out because the previous wording named
537
+ // only the Node directory, which was both incomplete and unhelpful: a
538
+ // user who HAD installed pnpm could not tell whether we looked in the
539
+ // right place (#292). And the restart note matters — the installer sets
540
+ // PNPM_HOME for new sessions, so a dsh already running cannot see it.
541
+ const searched = toolSearchDirs().join(process.platform === 'win32' ? ' ; ' : ' : ')
542
+ return `这台机器的 dsh 进程找不到 npm/corepack(图形界面或桌面端启动时不继承终端 PATH)——多半是宿主内置的 Node 运行时不带 npm。已找过:${searched}。请改从终端启动 dsh,或单独装一个 pnpm:Windows 用 iwr https://get.pnpm.io/install.ps1 -useb | iex,macOS/Linux 用 brew install pnpm。装完后请重启 dsh——安装器只对新开的会话生效,正在运行的进程看不到它 / This dsh process cannot find npm/corepack (GUI and desktop launches skip your shell PATH); a bundled Node runtime without npm is the usual cause. Searched: ${searched}. Start dsh from a terminal, or install pnpm on its own: \`iwr https://get.pnpm.io/install.ps1 -useb | iex\` (Windows) or \`brew install pnpm\` (macOS/Linux). Restart dsh afterwards — the installer only affects new sessions, so an already-running process cannot see it`
495
543
  }
496
544
  if (/EEXIST|already exists|--force to overwrite/i.test(npmOutput)) {
497
545
  return 'pnpm 的可执行文件已存在(通常是 corepack 先放好了同名 shim),npm 拒绝覆盖。在终端里执行其一即可:corepack prepare pnpm@latest --activate(推荐,直接激活已有 shim)或 npm i -g pnpm --force / A pnpm executable already exists (usually a corepack shim), so npm refused to overwrite it. Run one of these in a terminal: `corepack prepare pnpm@latest --activate` (preferred — activates the shim already there) or `npm i -g pnpm --force`'