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 +130 -130
- package/lib/dsh-cli.js +54 -7
- package/lib/types/dsh-cli.d.ts +27 -1
- package/package.json +5 -3
- package/src/dsh-cli.ts +54 -7
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
|
-
"
|
|
972
|
-
"
|
|
973
|
-
"
|
|
974
|
-
"
|
|
975
|
-
"
|
|
976
|
-
"
|
|
977
|
-
"
|
|
978
|
-
"
|
|
979
|
-
"
|
|
980
|
-
"
|
|
981
|
-
"
|
|
982
|
-
"
|
|
983
|
-
"
|
|
984
|
-
"
|
|
985
|
-
"
|
|
986
|
-
"
|
|
987
|
-
"
|
|
988
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
993
|
-
"
|
|
994
|
-
"
|
|
995
|
-
"
|
|
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
|
-
"
|
|
1000
|
-
"
|
|
1001
|
-
"
|
|
1002
|
-
"
|
|
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
|
-
"
|
|
1038
|
+
"setHint": "eGUBIq_setHint",
|
|
1039
|
+
"hiddenFile": "eGUBIq_hiddenFile",
|
|
1040
|
+
"collapseIcon": "eGUBIq_collapseIcon",
|
|
1041
|
+
"orphRow": "eGUBIq_orphRow",
|
|
1005
1042
|
"foot": "eGUBIq_foot",
|
|
1006
|
-
"
|
|
1007
|
-
"
|
|
1008
|
-
"
|
|
1009
|
-
"
|
|
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
|
-
"
|
|
1048
|
+
"diagVal": "eGUBIq_diagVal",
|
|
1049
|
+
"titleRow": "eGUBIq_titleRow",
|
|
1050
|
+
"switch": "eGUBIq_switch",
|
|
1021
1051
|
"diagBundle": "eGUBIq_diagBundle",
|
|
1022
1052
|
"warnLine": "eGUBIq_warnLine",
|
|
1023
|
-
"
|
|
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
|
-
"
|
|
1053
|
-
"
|
|
1055
|
+
"progress": "eGUBIq_progress",
|
|
1056
|
+
"viewBtn": "eGUBIq_viewBtn",
|
|
1057
|
+
"diagEmpty": "eGUBIq_diagEmpty",
|
|
1054
1058
|
"backupActions": "eGUBIq_backupActions",
|
|
1055
|
-
"
|
|
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
|
-
"
|
|
1058
|
-
"
|
|
1059
|
-
"
|
|
1060
|
-
"
|
|
1061
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
1067
|
-
"
|
|
1068
|
-
"
|
|
1069
|
-
"
|
|
1070
|
-
"
|
|
1071
|
-
"
|
|
1072
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
1106
|
-
"
|
|
1107
|
-
"
|
|
1108
|
-
"
|
|
1109
|
-
"
|
|
1110
|
-
"
|
|
1111
|
-
"
|
|
1112
|
-
"
|
|
1113
|
-
"
|
|
1114
|
-
"
|
|
1115
|
-
"
|
|
1116
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
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`';
|
package/lib/types/dsh-cli.d.ts
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
|
|
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`'
|