dshmarket 1.12.1 → 1.12.2

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/client/client.js CHANGED
@@ -967,155 +967,155 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
967
967
  document.head.appendChild(tag);
968
968
  }
969
969
  var Market_module_css_default = {
970
+ "dragHandle": "eGUBIq_dragHandle",
971
+ "row1": "eGUBIq_row1",
972
+ "src": "eGUBIq_src",
973
+ "sub": "eGUBIq_sub",
974
+ "actWhy": "eGUBIq_actWhy",
975
+ "grid": "eGUBIq_grid",
976
+ "dot": "eGUBIq_dot",
977
+ "depLine": "eGUBIq_depLine",
978
+ "backupGrid": "eGUBIq_backupGrid",
979
+ "specTagFile": "eGUBIq_specTagFile",
980
+ "assignRow": "eGUBIq_assignRow",
981
+ "card": "eGUBIq_card",
982
+ "staleAction": "eGUBIq_staleAction",
983
+ "setTitle": "eGUBIq_setTitle",
984
+ "viewOn": "eGUBIq_viewOn",
970
985
  "tabs": "eGUBIq_tabs",
971
- "groupHead": "eGUBIq_groupHead",
972
- "setCard": "eGUBIq_setCard",
973
- "orphBadge": "eGUBIq_orphBadge",
974
- "switchMixed": "eGUBIq_switchMixed",
975
- "groupName": "eGUBIq_groupName",
976
- "backupCheck": "eGUBIq_backupCheck",
977
- "groupAddPanel": "eGUBIq_groupAddPanel",
978
- "sectionOverview": "eGUBIq_sectionOverview",
979
- "viewBar": "eGUBIq_viewBar",
980
- "diagIndex": "eGUBIq_diagIndex",
981
- "head": "eGUBIq_head",
982
- "setLabel": "eGUBIq_setLabel",
983
- "diagCount": "eGUBIq_diagCount",
984
- "barFill": "eGUBIq_barFill",
985
- "actWarn": "eGUBIq_actWarn",
986
- "diagSummary": "eGUBIq_diagSummary",
987
- "orphRow": "eGUBIq_orphRow",
988
- "dshmSlide": "eGUBIq_dshmSlide",
986
+ "err": "eGUBIq_err",
987
+ "backupInput": "eGUBIq_backupInput",
988
+ "bannerHint": "eGUBIq_bannerHint",
989
+ "grow": "eGUBIq_grow",
990
+ "diagBadgeOfficial": "eGUBIq_diagBadgeOfficial",
991
+ "diagBadgeShadow": "eGUBIq_diagBadgeShadow",
992
+ "cmd": "eGUBIq_cmd",
993
+ "bar": "eGUBIq_bar",
994
+ "catsToggle": "eGUBIq_catsToggle",
995
+ "deprecate": "eGUBIq_deprecate",
996
+ "irow": "eGUBIq_irow",
997
+ "diagRow": "eGUBIq_diagRow",
998
+ "inlineInput": "eGUBIq_inlineInput",
999
+ "panelNote": "eGUBIq_panelNote",
1000
+ "pagerPages": "eGUBIq_pagerPages",
1001
+ "swatches": "eGUBIq_swatches",
1002
+ "setDesc": "eGUBIq_setDesc",
1003
+ "sectAction": "eGUBIq_sectAction",
1004
+ "setRow": "eGUBIq_setRow",
1005
+ "backupWarn": "eGUBIq_backupWarn",
1006
+ "dragging": "eGUBIq_dragging",
1007
+ "tag": "eGUBIq_tag",
989
1008
  "diagKey": "eGUBIq_diagKey",
990
- "dragOver": "eGUBIq_dragOver",
1009
+ "diagSummary": "eGUBIq_diagSummary",
1010
+ "diagBadgeCommunity": "eGUBIq_diagBadgeCommunity",
1011
+ "setCard": "eGUBIq_setCard",
1012
+ "modalNote": "eGUBIq_modalNote",
1013
+ "act": "eGUBIq_act",
1014
+ "specTag": "eGUBIq_specTag",
1015
+ "top": "eGUBIq_top",
1016
+ "srcBtn": "eGUBIq_srcBtn",
1017
+ "irowMissing": "eGUBIq_irowMissing",
1018
+ "diagMeta": "eGUBIq_diagMeta",
1019
+ "actBroken": "eGUBIq_actBroken",
1020
+ "star": "eGUBIq_star",
1021
+ "tab": "eGUBIq_tab",
991
1022
  "backupCard": "eGUBIq_backupCard",
992
- "groupHint": "eGUBIq_groupHint",
993
- "shots": "eGUBIq_shots",
994
- "av": "eGUBIq_av",
995
- "pageEllipsis": "eGUBIq_pageEllipsis",
1023
+ "cats": "eGUBIq_cats",
1024
+ "body": "eGUBIq_body",
1025
+ "dshmSlide": "eGUBIq_dshmSlide",
1026
+ "orphBadge": "eGUBIq_orphBadge",
1027
+ "themesGrid": "eGUBIq_themesGrid",
1028
+ "on": "eGUBIq_on",
996
1029
  "catsWrap": "eGUBIq_catsWrap",
997
- "pager": "eGUBIq_pager",
998
1030
  "catsCollapsed": "eGUBIq_catsCollapsed",
999
- "collapseTitle": "eGUBIq_collapseTitle",
1000
- "src": "eGUBIq_src",
1001
- "body": "eGUBIq_body",
1002
- "depBadge": "eGUBIq_depBadge",
1031
+ "ovArrow": "eGUBIq_ovArrow",
1032
+ "groupName": "eGUBIq_groupName",
1033
+ "ovFrom": "eGUBIq_ovFrom",
1034
+ "fixFallbackText": "eGUBIq_fixFallbackText",
1035
+ "nm": "eGUBIq_nm",
1036
+ "actLive": "eGUBIq_actLive",
1003
1037
  "backupMessage": "eGUBIq_backupMessage",
1004
- "setRevert": "eGUBIq_setRevert",
1038
+ "setHint": "eGUBIq_setHint",
1039
+ "hiddenFile": "eGUBIq_hiddenFile",
1040
+ "collapseIcon": "eGUBIq_collapseIcon",
1041
+ "orphRow": "eGUBIq_orphRow",
1005
1042
  "foot": "eGUBIq_foot",
1006
- "diagRow": "eGUBIq_diagRow",
1007
- "diagArrow": "eGUBIq_diagArrow",
1008
- "setDesc": "eGUBIq_setDesc",
1009
- "pct": "eGUBIq_pct",
1010
- "catsRow": "eGUBIq_catsRow",
1011
- "themesGrid": "eGUBIq_themesGrid",
1012
- "tag": "eGUBIq_tag",
1013
- "panelNote": "eGUBIq_panelNote",
1014
- "diagSummaryMeta": "eGUBIq_diagSummaryMeta",
1015
- "ovRow": "eGUBIq_ovRow",
1016
- "sp": "eGUBIq_sp",
1017
- "spec": "eGUBIq_spec",
1018
- "title": "eGUBIq_title",
1043
+ "viewBar": "eGUBIq_viewBar",
1044
+ "groupHead": "eGUBIq_groupHead",
1045
+ "tabSearch": "eGUBIq_tabSearch",
1046
+ "av": "eGUBIq_av",
1019
1047
  "root": "eGUBIq_root",
1020
- "diagEmpty": "eGUBIq_diagEmpty",
1048
+ "diagVal": "eGUBIq_diagVal",
1049
+ "titleRow": "eGUBIq_titleRow",
1050
+ "switch": "eGUBIq_switch",
1021
1051
  "diagBundle": "eGUBIq_diagBundle",
1022
1052
  "warnLine": "eGUBIq_warnLine",
1023
- "groupCreate": "eGUBIq_groupCreate",
1024
- "grid": "eGUBIq_grid",
1025
- "bar": "eGUBIq_bar",
1026
- "tabSearch": "eGUBIq_tabSearch",
1027
- "assignRow": "eGUBIq_assignRow",
1028
- "switchKnob": "eGUBIq_switchKnob",
1029
- "fixFallback": "eGUBIq_fixFallback",
1030
- "bannerIcon": "eGUBIq_bannerIcon",
1031
- "grow": "eGUBIq_grow",
1032
- "fixFallbackText": "eGUBIq_fixFallbackText",
1033
- "staleAction": "eGUBIq_staleAction",
1034
- "diagSummaryItem": "eGUBIq_diagSummaryItem",
1035
- "hiddenFile": "eGUBIq_hiddenFile",
1036
- "empty": "eGUBIq_empty",
1037
- "switchOn": "eGUBIq_switchOn",
1038
- "specTagFile": "eGUBIq_specTagFile",
1039
- "srcBtn": "eGUBIq_srcBtn",
1040
- "diagAlert": "eGUBIq_diagAlert",
1041
- "desc": "eGUBIq_desc",
1042
- "star": "eGUBIq_star",
1043
- "groupMembers": "eGUBIq_groupMembers",
1044
- "cats": "eGUBIq_cats",
1045
- "diagMeta": "eGUBIq_diagMeta",
1046
- "switch": "eGUBIq_switch",
1047
- "titleRow": "eGUBIq_titleRow",
1048
- "viewOn": "eGUBIq_viewOn",
1049
- "viewBtn": "eGUBIq_viewBtn",
1050
- "nm": "eGUBIq_nm",
1053
+ "actWarn": "eGUBIq_actWarn",
1051
1054
  "specTagGit": "eGUBIq_specTagGit",
1052
- "diagBadgeCommunity": "eGUBIq_diagBadgeCommunity",
1053
- "spin": "eGUBIq_spin",
1055
+ "progress": "eGUBIq_progress",
1056
+ "viewBtn": "eGUBIq_viewBtn",
1057
+ "diagEmpty": "eGUBIq_diagEmpty",
1054
1058
  "backupActions": "eGUBIq_backupActions",
1055
- "shot": "eGUBIq_shot",
1059
+ "setHead": "eGUBIq_setHead",
1060
+ "loading": "eGUBIq_loading",
1061
+ "dragOver": "eGUBIq_dragOver",
1062
+ "backupCheckList": "eGUBIq_backupCheckList",
1063
+ "groupHint": "eGUBIq_groupHint",
1064
+ "fixFallback": "eGUBIq_fixFallback",
1065
+ "setRevert": "eGUBIq_setRevert",
1056
1066
  "assignSelect": "eGUBIq_assignSelect",
1057
- "okState": "eGUBIq_okState",
1058
- "row1": "eGUBIq_row1",
1059
- "backupGrid": "eGUBIq_backupGrid",
1060
- "progress": "eGUBIq_progress",
1061
- "ovByTag": "eGUBIq_ovByTag",
1067
+ "diagSection": "eGUBIq_diagSection",
1068
+ "pager": "eGUBIq_pager",
1069
+ "pageInfo": "eGUBIq_pageInfo",
1070
+ "spin": "eGUBIq_spin",
1071
+ "groupCreate": "eGUBIq_groupCreate",
1072
+ "pct": "eGUBIq_pct",
1073
+ "pageEllipsis": "eGUBIq_pageEllipsis",
1074
+ "diagPage": "eGUBIq_diagPage",
1075
+ "version": "eGUBIq_version",
1076
+ "title": "eGUBIq_title",
1062
1077
  "tabSearchRow": "eGUBIq_tabSearchRow",
1063
- "setHead": "eGUBIq_setHead",
1078
+ "shot": "eGUBIq_shot",
1079
+ "descTight": "eGUBIq_descTight",
1080
+ "head": "eGUBIq_head",
1081
+ "switchOn": "eGUBIq_switchOn",
1082
+ "switchKnob": "eGUBIq_switchKnob",
1083
+ "collapseHead": "eGUBIq_collapseHead",
1084
+ "groupMember": "eGUBIq_groupMember",
1085
+ "setLabel": "eGUBIq_setLabel",
1086
+ "ovRow": "eGUBIq_ovRow",
1087
+ "diagAlert": "eGUBIq_diagAlert",
1064
1088
  "banner": "eGUBIq_banner",
1089
+ "diagCount": "eGUBIq_diagCount",
1090
+ "okState": "eGUBIq_okState",
1091
+ "barFill": "eGUBIq_barFill",
1092
+ "collapseTitle": "eGUBIq_collapseTitle",
1065
1093
  "owner": "eGUBIq_owner",
1066
- "pagerPages": "eGUBIq_pagerPages",
1067
- "inlineInput": "eGUBIq_inlineInput",
1068
- "dragHandle": "eGUBIq_dragHandle",
1069
- "groupMember": "eGUBIq_groupMember",
1070
- "specTag": "eGUBIq_specTag",
1071
- "setTitle": "eGUBIq_setTitle",
1072
- "act": "eGUBIq_act",
1073
- "on": "eGUBIq_on",
1074
- "version": "eGUBIq_version",
1075
- "irowMissing": "eGUBIq_irowMissing",
1076
- "diagBadgeOfficial": "eGUBIq_diagBadgeOfficial",
1077
- "sub": "eGUBIq_sub",
1078
- "collapseIcon": "eGUBIq_collapseIcon",
1079
- "diagList": "eGUBIq_diagList",
1080
- "dot": "eGUBIq_dot",
1081
- "topBtn": "eGUBIq_topBtn",
1094
+ "diagIndex": "eGUBIq_diagIndex",
1095
+ "collapseBody": "eGUBIq_collapseBody",
1096
+ "catsRow": "eGUBIq_catsRow",
1097
+ "shots": "eGUBIq_shots",
1098
+ "empty": "eGUBIq_empty",
1099
+ "groupAddPanel": "eGUBIq_groupAddPanel",
1100
+ "backupCheck": "eGUBIq_backupCheck",
1082
1101
  "setLabelBox": "eGUBIq_setLabelBox",
1083
- "setHint": "eGUBIq_setHint",
1084
- "descTight": "eGUBIq_descTight",
1085
- "tab": "eGUBIq_tab",
1086
- "diagBadgeShadow": "eGUBIq_diagBadgeShadow",
1087
- "sectAction": "eGUBIq_sectAction",
1088
- "collapseHead": "eGUBIq_collapseHead",
1089
- "irow": "eGUBIq_irow",
1090
- "err": "eGUBIq_err",
1091
- "backupCheckList": "eGUBIq_backupCheckList",
1092
- "setRow": "eGUBIq_setRow",
1093
- "modalNote": "eGUBIq_modalNote",
1094
- "ovArrow": "eGUBIq_ovArrow",
1095
- "actLive": "eGUBIq_actLive",
1096
- "backupWarn": "eGUBIq_backupWarn",
1097
- "diagVal": "eGUBIq_diagVal",
1098
- "loading": "eGUBIq_loading",
1099
- "barWave": "eGUBIq_barWave",
1100
- "catsToggle": "eGUBIq_catsToggle",
1101
- "actBroken": "eGUBIq_actBroken",
1102
- "actWhy": "eGUBIq_actWhy",
1103
- "bannerHint": "eGUBIq_bannerHint",
1102
+ "switchMixed": "eGUBIq_switchMixed",
1104
1103
  "groupActions": "eGUBIq_groupActions",
1105
- "card": "eGUBIq_card",
1106
- "depLine": "eGUBIq_depLine",
1107
- "backupInput": "eGUBIq_backupInput",
1108
- "pageInfo": "eGUBIq_pageInfo",
1109
- "diagPage": "eGUBIq_diagPage",
1110
- "swatches": "eGUBIq_swatches",
1111
- "collapseBody": "eGUBIq_collapseBody",
1112
- "diagSection": "eGUBIq_diagSection",
1113
- "ovFrom": "eGUBIq_ovFrom",
1114
- "top": "eGUBIq_top",
1115
- "cmd": "eGUBIq_cmd",
1116
- "dragging": "eGUBIq_dragging",
1104
+ "diagSummaryItem": "eGUBIq_diagSummaryItem",
1105
+ "diagArrow": "eGUBIq_diagArrow",
1106
+ "depBadge": "eGUBIq_depBadge",
1107
+ "bannerIcon": "eGUBIq_bannerIcon",
1108
+ "desc": "eGUBIq_desc",
1109
+ "spec": "eGUBIq_spec",
1110
+ "diagSummaryMeta": "eGUBIq_diagSummaryMeta",
1111
+ "diagList": "eGUBIq_diagList",
1112
+ "barWave": "eGUBIq_barWave",
1113
+ "sectionOverview": "eGUBIq_sectionOverview",
1114
+ "topBtn": "eGUBIq_topBtn",
1115
+ "sp": "eGUBIq_sp",
1117
1116
  "groupRow": "eGUBIq_groupRow",
1118
- "deprecate": "eGUBIq_deprecate"
1117
+ "groupMembers": "eGUBIq_groupMembers",
1118
+ "ovByTag": "eGUBIq_ovByTag"
1119
1119
  };
1120
1120
  //#endregion
1121
1121
  //#region src/client/Diagnostics.tsx
package/lib/dsh-cli.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * the agent's sandboxed executor and denies writes to the profile directory.
8
8
  */
9
9
  import { spawn } from 'node:child_process';
10
+ import { existsSync } from 'node:fs';
10
11
  import { homedir } from 'node:os';
11
12
  import { dirname, isAbsolute, join, resolve } from 'node:path';
12
13
  import { logEvent } from './log.js';
@@ -28,14 +29,27 @@ import { profileDir } from './profile.js';
28
29
  * for the pnpm it just installed to become visible.
29
30
  */
30
31
  const extraPathDirs = [];
32
+ /**
33
+ * The directory holding the Node binary running this process. `npm`,
34
+ * `npm.cmd` and `corepack` are installed alongside it by every official Node
35
+ * distribution, so it is the one place the toolchain can be looked for
36
+ * without guessing — and unlike a PATH entry it cannot be absent, because
37
+ * this process is executing out of it.
38
+ *
39
+ * #167: a Windows desktop host spawned dsh without the Node install
40
+ * directory on PATH. Node itself was running (v24.18.1 in the log) while
41
+ * both `corepack` and `npm` came back "not recognized as an internal or
42
+ * external command", so the one-click setup had no way to succeed.
43
+ */
44
+ export const nodeBinDir = dirname(process.execPath);
31
45
  function spawnEnv() {
32
46
  // pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
33
47
  // CI mode forces it to act or fail instead of asking.
34
48
  const separator = process.platform === 'win32' ? ';' : ':';
35
49
  const parts = (process.env.PATH ?? '').split(separator).filter(part => part !== '');
36
50
  const candidates = process.platform === 'win32'
37
- ? extraPathDirs
38
- : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), ...extraPathDirs];
51
+ ? [nodeBinDir, ...extraPathDirs]
52
+ : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), nodeBinDir, ...extraPathDirs];
39
53
  for (const bin of candidates) {
40
54
  if (!parts.includes(bin))
41
55
  parts.push(bin);
@@ -242,7 +256,38 @@ export async function provisionPnpm() {
242
256
  extraPathDirs.pop();
243
257
  }
244
258
  }
245
- return { ok: false, hint: provisionHint(corepack.output, npm.output) };
259
+ const npmFound = toolOnPath('npm');
260
+ if (!npmFound)
261
+ logEvent('warn', 'setup-pnpm', `npm is not on any searched path (node lives in ${nodeBinDir})`);
262
+ return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound) };
263
+ }
264
+ /** Executable suffixes a bare command name can carry on this platform. */
265
+ const EXECUTABLE_SUFFIXES = process.platform === 'win32'
266
+ ? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(part => part !== '')
267
+ : [''];
268
+ /**
269
+ * Whether a bare command name resolves to a file on the PATH the market
270
+ * hands its children.
271
+ *
272
+ * The market cannot read the reason a spawn failed out of the child's
273
+ * message: cmd.exe reports a missing command in the console's ANSI codepage
274
+ * ("'npm' 不是内部或外部命令" on a Chinese Windows), which is neither the
275
+ * string `ENOENT` nor even valid UTF-8 — so the #32 hint, written against
276
+ * Node's own ENOENT wording, could never fire on Windows and the user was
277
+ * left with no guidance at all (#167). Looking on disk answers the same
278
+ * question in every locale.
279
+ */
280
+ export function toolOnPath(name) {
281
+ const separator = process.platform === 'win32' ? ';' : ':';
282
+ for (const dir of (spawnEnv().PATH ?? '').split(separator)) {
283
+ if (dir === '')
284
+ continue;
285
+ for (const suffix of EXECUTABLE_SUFFIXES) {
286
+ if (existsSync(join(dir, name + suffix)))
287
+ return true;
288
+ }
289
+ }
290
+ return false;
246
291
  }
247
292
  /**
248
293
  * Why the one-click pnpm setup failed, in terms the user can act on.
@@ -254,11 +299,13 @@ export async function provisionPnpm() {
254
299
  * a GUI launch with no Node on PATH at all).
255
300
  * @returns a bilingual, actionable hint, or undefined when unrecognized.
256
301
  */
257
- export function provisionHint(corepackOutput, npmOutput) {
302
+ export function provisionHint(corepackOutput, npmOutput, npmFound = true) {
258
303
  // Node itself unreachable: pointing the user back at this same button
259
- // would be a dead end (#32).
260
- if (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput)) {
261
- return '这台机器的 dsh 进程找不到 Node(从图形界面启动不继承终端 PATH)。请改从终端启动 dsh,或安装 Homebrew 版 pnpm:brew install pnpm / This dsh process cannot find Node (GUI launches skip your shell PATH). Start dsh from a terminal, or install pnpm via Homebrew: brew install pnpm';
304
+ // would be a dead end (#32). `npmFound` answers this from disk, so it
305
+ // holds on a Windows console that reports the same thing in a codepage we
306
+ // cannot read (#167); the ENOENT match stays for callers without it.
307
+ if (!npmFound || (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput))) {
308
+ 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)`;
262
309
  }
263
310
  if (/EEXIST|already exists|--force to overwrite/i.test(npmOutput)) {
264
311
  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`';
@@ -8,6 +8,19 @@
8
8
  */
9
9
  import type { ChildProcess } from 'node:child_process';
10
10
  import { type ProgressPhase } from './ndjson.ts';
11
+ /**
12
+ * The directory holding the Node binary running this process. `npm`,
13
+ * `npm.cmd` and `corepack` are installed alongside it by every official Node
14
+ * distribution, so it is the one place the toolchain can be looked for
15
+ * without guessing — and unlike a PATH entry it cannot be absent, because
16
+ * this process is executing out of it.
17
+ *
18
+ * #167: a Windows desktop host spawned dsh without the Node install
19
+ * directory on PATH. Node itself was running (v24.18.1 in the log) while
20
+ * both `corepack` and `npm` came back "not recognized as an internal or
21
+ * external command", so the one-click setup had no way to succeed.
22
+ */
23
+ export declare const nodeBinDir: string;
11
24
  /**
12
25
  * Windows npm/corepack/pnpm are `.cmd` shims. Node's `spawn` without a shell
13
26
  * cannot start them (ENOENT / EINVAL). Same pattern as dsh's `plugin` forwarder.
@@ -99,6 +112,19 @@ export declare function provisionPnpm(): Promise<{
99
112
  ok: boolean;
100
113
  hint?: string;
101
114
  }>;
115
+ /**
116
+ * Whether a bare command name resolves to a file on the PATH the market
117
+ * hands its children.
118
+ *
119
+ * The market cannot read the reason a spawn failed out of the child's
120
+ * message: cmd.exe reports a missing command in the console's ANSI codepage
121
+ * ("'npm' 不是内部或外部命令" on a Chinese Windows), which is neither the
122
+ * string `ENOENT` nor even valid UTF-8 — so the #32 hint, written against
123
+ * Node's own ENOENT wording, could never fire on Windows and the user was
124
+ * left with no guidance at all (#167). Looking on disk answers the same
125
+ * question in every locale.
126
+ */
127
+ export declare function toolOnPath(name: string): boolean;
102
128
  /**
103
129
  * Why the one-click pnpm setup failed, in terms the user can act on.
104
130
  *
@@ -109,7 +135,7 @@ export declare function provisionPnpm(): Promise<{
109
135
  * a GUI launch with no Node on PATH at all).
110
136
  * @returns a bilingual, actionable hint, or undefined when unrecognized.
111
137
  */
112
- export declare function provisionHint(corepackOutput: string, npmOutput: string): string | undefined;
138
+ export declare function provisionHint(corepackOutput: string, npmOutput: string, npmFound?: boolean): string | undefined;
113
139
  /** Live progress of the running plugin command, for the status route. */
114
140
  export interface InstallProgress {
115
141
  active: boolean;
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.12.1",
4
+ "version": "1.12.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -30,8 +30,9 @@
30
30
  "prepack": "npm run build && node scripts/preflight.mjs",
31
31
  "prepare": "npm run build",
32
32
  "validate:registry": "node scripts/validate-registry.mjs",
33
- "snapshot": "curl -sf https://awesome-dsh-plugin.com/plugins.json -o data/registry-snapshot.json",
34
- "test:web": "vitest run --config vitest.web.config.ts"
33
+ "snapshot": "curl -sf https://awesome-dsh-plugin.com/plugins.json -o data/registry-snapshot.json && curl -sf https://awesome-dsh-plugin.com/readmes.json -o data/readmes-snapshot.json",
34
+ "test:web": "vitest run --config vitest.web.config.ts",
35
+ "build:site": "node scripts/build-site.mjs"
35
36
  },
36
37
  "dependencies": {
37
38
  "js-yaml": "^4.1.0"
@@ -85,6 +86,7 @@
85
86
  "@types/react": "~18.3.1",
86
87
  "jsdom": "^29.1.1",
87
88
  "lightningcss": "^1.33.0",
89
+ "marked": "^18.0.10",
88
90
  "playwright": "^1.62.1",
89
91
  "react": "^18.3.1",
90
92
  "react-dom": "~18.3.1",
package/src/dsh-cli.ts CHANGED
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { spawn } from 'node:child_process'
11
11
  import type { ChildProcess, SpawnOptions } from 'node:child_process'
12
+ import { existsSync } from 'node:fs'
12
13
  import { homedir } from 'node:os'
13
14
  import { dirname, isAbsolute, join, resolve } from 'node:path'
14
15
  import { logEvent } from './log.ts'
@@ -32,14 +33,28 @@ import { profileDir } from './profile.ts'
32
33
  */
33
34
  const extraPathDirs: string[] = []
34
35
 
36
+ /**
37
+ * The directory holding the Node binary running this process. `npm`,
38
+ * `npm.cmd` and `corepack` are installed alongside it by every official Node
39
+ * distribution, so it is the one place the toolchain can be looked for
40
+ * without guessing — and unlike a PATH entry it cannot be absent, because
41
+ * this process is executing out of it.
42
+ *
43
+ * #167: a Windows desktop host spawned dsh without the Node install
44
+ * directory on PATH. Node itself was running (v24.18.1 in the log) while
45
+ * both `corepack` and `npm` came back "not recognized as an internal or
46
+ * external command", so the one-click setup had no way to succeed.
47
+ */
48
+ export const nodeBinDir = dirname(process.execPath)
49
+
35
50
  function spawnEnv(): NodeJS.ProcessEnv {
36
51
  // pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
37
52
  // CI mode forces it to act or fail instead of asking.
38
53
  const separator = process.platform === 'win32' ? ';' : ':'
39
54
  const parts = (process.env.PATH ?? '').split(separator).filter(part => part !== '')
40
55
  const candidates = process.platform === 'win32'
41
- ? extraPathDirs
42
- : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), ...extraPathDirs]
56
+ ? [nodeBinDir, ...extraPathDirs]
57
+ : ['/opt/homebrew/bin', '/usr/local/bin', join(homedir(), '.local', 'bin'), nodeBinDir, ...extraPathDirs]
43
58
  for (const bin of candidates) {
44
59
  if (!parts.includes(bin)) parts.push(bin)
45
60
  }
@@ -304,7 +319,37 @@ export async function provisionPnpm(): Promise<{ ok: boolean; hint?: string }> {
304
319
  extraPathDirs.pop()
305
320
  }
306
321
  }
307
- return { ok: false, hint: provisionHint(corepack.output, npm.output) }
322
+ const npmFound = toolOnPath('npm')
323
+ if (!npmFound) logEvent('warn', 'setup-pnpm', `npm is not on any searched path (node lives in ${nodeBinDir})`)
324
+ return { ok: false, hint: provisionHint(corepack.output, npm.output, npmFound) }
325
+ }
326
+
327
+ /** Executable suffixes a bare command name can carry on this platform. */
328
+ const EXECUTABLE_SUFFIXES = process.platform === 'win32'
329
+ ? (process.env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(part => part !== '')
330
+ : ['']
331
+
332
+ /**
333
+ * Whether a bare command name resolves to a file on the PATH the market
334
+ * hands its children.
335
+ *
336
+ * The market cannot read the reason a spawn failed out of the child's
337
+ * message: cmd.exe reports a missing command in the console's ANSI codepage
338
+ * ("'npm' 不是内部或外部命令" on a Chinese Windows), which is neither the
339
+ * string `ENOENT` nor even valid UTF-8 — so the #32 hint, written against
340
+ * Node's own ENOENT wording, could never fire on Windows and the user was
341
+ * left with no guidance at all (#167). Looking on disk answers the same
342
+ * question in every locale.
343
+ */
344
+ export function toolOnPath(name: string): boolean {
345
+ const separator = process.platform === 'win32' ? ';' : ':'
346
+ for (const dir of (spawnEnv().PATH ?? '').split(separator)) {
347
+ if (dir === '') continue
348
+ for (const suffix of EXECUTABLE_SUFFIXES) {
349
+ if (existsSync(join(dir, name + suffix))) return true
350
+ }
351
+ }
352
+ return false
308
353
  }
309
354
 
310
355
  /**
@@ -317,11 +362,13 @@ export async function provisionPnpm(): Promise<{ ok: boolean; hint?: string }> {
317
362
  * a GUI launch with no Node on PATH at all).
318
363
  * @returns a bilingual, actionable hint, or undefined when unrecognized.
319
364
  */
320
- export function provisionHint(corepackOutput: string, npmOutput: string): string | undefined {
365
+ export function provisionHint(corepackOutput: string, npmOutput: string, npmFound = true): string | undefined {
321
366
  // Node itself unreachable: pointing the user back at this same button
322
- // would be a dead end (#32).
323
- if (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput)) {
324
- return '这台机器的 dsh 进程找不到 Node(从图形界面启动不继承终端 PATH)。请改从终端启动 dsh,或安装 Homebrew 版 pnpm:brew install pnpm / This dsh process cannot find Node (GUI launches skip your shell PATH). Start dsh from a terminal, or install pnpm via Homebrew: brew install pnpm'
367
+ // would be a dead end (#32). `npmFound` answers this from disk, so it
368
+ // holds on a Windows console that reports the same thing in a codepage we
369
+ // cannot read (#167); the ENOENT match stays for callers without it.
370
+ if (!npmFound || (/ENOENT/.test(corepackOutput) && /ENOENT/.test(npmOutput))) {
371
+ 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)`
325
372
  }
326
373
  if (/EEXIST|already exists|--force to overwrite/i.test(npmOutput)) {
327
374
  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`'