dsh-ssh-tunnel 1.0.1 → 1.0.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/CHANGELOG.md +54 -0
- package/README.md +72 -37
- package/README_en.md +72 -37
- package/cordis.patch.yml +4 -4
- package/lib/client.js +1643 -1493
- package/lib/index.js +1739 -1611
- package/lib/session.js +616 -525
- package/lib/shared/args.js +23 -0
- package/lib/shared/host-key.js +32 -32
- package/lib/shared/host-summary.js +75 -75
- package/lib/shared/http-trust.js +59 -59
- package/lib/shared/path.js +21 -22
- package/lib/shared/persist.js +41 -41
- package/lib/shared/session-auth.js +47 -39
- package/lib/shared/session-policy.js +151 -125
- package/lib/shared/shell-buffer.js +10 -10
- package/lib/shared/vendor.js +49 -49
- package/package.json +91 -88
- package/scripts/install.ps1 +191 -93
- package/scripts/install.sh +278 -226
- package/scripts/lib/bundle-check.cjs +108 -0
- package/scripts/lib/strip-mount.cjs +114 -0
- package/scripts/lib/ws-exclude.cjs +143 -0
- package/scripts/smoke-test.mjs +899 -511
- package/scripts/sync-to-dsh.sh +87 -5
package/scripts/install.sh
CHANGED
|
@@ -1,226 +1,278 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# =============================================================================
|
|
3
|
-
# dsh-ssh-tunnel 一键安装(官方 CLI + bundle 自动挂载)
|
|
4
|
-
#
|
|
5
|
-
# dsh plugin --profile
|
|
6
|
-
#
|
|
7
|
-
# 包内 dsh.bundle.patch(cordis.patch.yml)会由 CLI 写入
|
|
8
|
-
# dsh.profile.bundles,无需手写 profile cordis.patch.yml。
|
|
9
|
-
#
|
|
10
|
-
# 用法:
|
|
11
|
-
# bash scripts/install.sh [版本] [--
|
|
12
|
-
#
|
|
13
|
-
# 版本
|
|
14
|
-
# --
|
|
15
|
-
# --
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
[ "$
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
else
|
|
225
|
-
|
|
226
|
-
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# dsh-ssh-tunnel 一键安装(官方 CLI + bundle 自动挂载)
|
|
4
|
+
#
|
|
5
|
+
# dsh plugin --profile <名称> add <spec>
|
|
6
|
+
#
|
|
7
|
+
# 包内 dsh.bundle.patch(cordis.patch.yml)会由 CLI 写入
|
|
8
|
+
# dsh.profile.bundles,无需手写 profile cordis.patch.yml。
|
|
9
|
+
#
|
|
10
|
+
# 用法:
|
|
11
|
+
# bash scripts/install.sh [版本] [--profile <名称>] [--fix-profile] [--from github|npm] [--restart] [--dry-run] [-h|--help]
|
|
12
|
+
#
|
|
13
|
+
# 版本 npm 版本或 github 的 branch/tag/commit;github 源可省略(默认 HEAD)
|
|
14
|
+
# --profile 目标 profile 名称(必填,无默认值;缺失或不存在时列出实存 profile 并退出 2)
|
|
15
|
+
# --fix-profile 可选维护动作:向 pnpm-workspace.yaml 的 minimumReleaseAgeExclude
|
|
16
|
+
# 追加本包(幂等),并清理 cordis.patch.yml 里 id: ssh-tunnel 的
|
|
17
|
+
# 旧手动挂载块(幂等)。两者均写后回读断言,失败回滚并以非零码退出。
|
|
18
|
+
# 不带本参数时绝不改写这两个文件,只在缺条目时打 warn 提示。
|
|
19
|
+
# --from github(默认,仓库尚未上 npm 时)| npm
|
|
20
|
+
# --restart 尝试 pm2 restart dsh-web
|
|
21
|
+
# --dry-run 只打印步骤,不写任何文件
|
|
22
|
+
#
|
|
23
|
+
# 环境:DSH_HOME(默认 ~/.dsh)、DSH_CMD(默认 dsh)、REGISTRY、GITHUB_REPO、
|
|
24
|
+
# DSH_INSTALL_YES=1(跳过 npx 兜底的交互确认)
|
|
25
|
+
# =============================================================================
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
PKG="dsh-ssh-tunnel"
|
|
29
|
+
PLUGIN_ID="ssh-tunnel"
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
31
|
+
LIB_DIR="$SCRIPT_DIR/lib"
|
|
32
|
+
GITHUB_REPO="${GITHUB_REPO:-OMSociety/dsh-ssh-tunnel}"
|
|
33
|
+
REGISTRY="${REGISTRY:-https://registry.npmjs.org}"
|
|
34
|
+
DSH_CMD="${DSH_CMD:-dsh}"
|
|
35
|
+
DSH_HOME="${DSH_HOME:-${HOME:-${USERPROFILE:-}}/.dsh}"
|
|
36
|
+
|
|
37
|
+
usage() {
|
|
38
|
+
cat <<'EOF'
|
|
39
|
+
dsh-ssh-tunnel 一键安装
|
|
40
|
+
|
|
41
|
+
用法:bash scripts/install.sh [版本] [--profile <名称>] [--fix-profile] [--from github|npm] [--restart] [--dry-run] [-h|--help]
|
|
42
|
+
|
|
43
|
+
版本 npm 版本或 github 的 branch/tag/commit;github 源可省略(默认 HEAD)
|
|
44
|
+
--profile <名> 目标 profile 名称(必填,无默认值)
|
|
45
|
+
--fix-profile 可选:补写 minimumReleaseAgeExclude 并清理旧手动挂载(幂等、带回读断言)
|
|
46
|
+
--from github(默认)| npm
|
|
47
|
+
--restart 装完尝试 pm2 restart dsh-web
|
|
48
|
+
--dry-run 只打印操作,不写任何文件
|
|
49
|
+
-h|--help 显示本帮助
|
|
50
|
+
|
|
51
|
+
环境:DSH_HOME(默认 ~/.dsh)、DSH_CMD(默认 dsh)、REGISTRY、GITHUB_REPO、
|
|
52
|
+
DSH_INSTALL_YES=1(跳过 npx 兜底确认)
|
|
53
|
+
|
|
54
|
+
前置:目标 profile 已初始化(含 package.json 与 pnpm-workspace.yaml);
|
|
55
|
+
建议已装 dsh-better-sidebar(侧栏 Tab 依赖)。
|
|
56
|
+
EOF
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
say() { printf '\033[32m[install]\033[0m %s\n' "$*"; }
|
|
60
|
+
warn() { printf '\033[33m[warn]\033[0m %s\n' "$*" >&2; }
|
|
61
|
+
die() { printf '\033[31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
62
|
+
|
|
63
|
+
list_profiles() {
|
|
64
|
+
if [ ! -d "$DSH_HOME/profiles" ]; then
|
|
65
|
+
echo " (未找到 $DSH_HOME/profiles)"
|
|
66
|
+
return 0
|
|
67
|
+
fi
|
|
68
|
+
local any="" d
|
|
69
|
+
for d in "$DSH_HOME"/profiles/*/; do
|
|
70
|
+
[ -d "$d" ] || continue
|
|
71
|
+
any=1
|
|
72
|
+
echo " - $(basename "$d")"
|
|
73
|
+
done
|
|
74
|
+
[ -n "$any" ] || echo " ($DSH_HOME/profiles 下没有 profile)"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ---------- 参数解析 ----------
|
|
78
|
+
PROFILE_NAME=""
|
|
79
|
+
FROM="github"
|
|
80
|
+
VERSION_SPEC=""
|
|
81
|
+
FIX_PROFILE=false
|
|
82
|
+
RESTART=false
|
|
83
|
+
DRY_RUN=false
|
|
84
|
+
SHOW_HELP=false
|
|
85
|
+
|
|
86
|
+
while [ $# -gt 0 ]; do
|
|
87
|
+
case "$1" in
|
|
88
|
+
-h|--help) SHOW_HELP=true; shift ;;
|
|
89
|
+
--profile)
|
|
90
|
+
[ $# -ge 2 ] || { echo "[error] --profile 需要一个名称(用 -h 查看用法)" >&2; exit 2; }
|
|
91
|
+
PROFILE_NAME="$2"; shift 2 ;;
|
|
92
|
+
--profile=*) PROFILE_NAME="${1#*=}"; shift ;;
|
|
93
|
+
--fix-profile) FIX_PROFILE=true; shift ;;
|
|
94
|
+
--from)
|
|
95
|
+
[ $# -ge 2 ] || { echo "[error] --from 需要 github|npm(用 -h 查看用法)" >&2; exit 2; }
|
|
96
|
+
FROM="$2"; shift 2 ;;
|
|
97
|
+
--from=*) FROM="${1#*=}"; shift ;;
|
|
98
|
+
--restart) RESTART=true; shift ;;
|
|
99
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
100
|
+
-*) echo "[error] 未知参数: $1(用 -h 查看用法)" >&2; exit 2 ;;
|
|
101
|
+
*)
|
|
102
|
+
if [ -n "$VERSION_SPEC" ]; then
|
|
103
|
+
echo "[error] 多余的位置参数: $1(版本只能传一个,用 -h 查看用法)" >&2
|
|
104
|
+
exit 2
|
|
105
|
+
fi
|
|
106
|
+
VERSION_SPEC="$1"; shift ;;
|
|
107
|
+
esac
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
if [ "$SHOW_HELP" = true ]; then usage; exit 0; fi
|
|
111
|
+
|
|
112
|
+
case "$FROM" in
|
|
113
|
+
github|npm) ;;
|
|
114
|
+
*) echo "[error] --from 只能是 github 或 npm(当前: $FROM)" >&2; exit 2 ;;
|
|
115
|
+
esac
|
|
116
|
+
|
|
117
|
+
if [ -z "$PROFILE_NAME" ]; then
|
|
118
|
+
echo "[error] 缺少 --profile 参数。$DSH_HOME/profiles/ 下实存 profile:" >&2
|
|
119
|
+
list_profiles >&2
|
|
120
|
+
exit 2
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
PROFILE_DIR="$DSH_HOME/profiles/$PROFILE_NAME"
|
|
124
|
+
WS_YML="$PROFILE_DIR/pnpm-workspace.yaml"
|
|
125
|
+
PATCH_YML="$PROFILE_DIR/cordis.patch.yml"
|
|
126
|
+
PKG_JSON="$PROFILE_DIR/package.json"
|
|
127
|
+
|
|
128
|
+
if [ ! -d "$PROFILE_DIR" ]; then
|
|
129
|
+
echo "[error] profile 不存在: $PROFILE_NAME($PROFILE_DIR)。$DSH_HOME/profiles/ 下实存 profile:" >&2
|
|
130
|
+
list_profiles >&2
|
|
131
|
+
exit 2
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# ---------- 前置检查 ----------
|
|
135
|
+
command -v node >/dev/null 2>&1 || die "未找到 node(需要 Node.js ≥ 20)。"
|
|
136
|
+
NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null)" || NODE_MAJOR=0
|
|
137
|
+
[ "${NODE_MAJOR:-0}" -ge 20 ] || die "Node.js $(node -v 2>/dev/null) 过旧(需要 ≥ 20)。"
|
|
138
|
+
|
|
139
|
+
resolve_add_spec() {
|
|
140
|
+
if [ "$FROM" = "npm" ]; then
|
|
141
|
+
local given="${VERSION_SPEC:-latest}"
|
|
142
|
+
if [ "$given" = "latest" ]; then
|
|
143
|
+
local v=""
|
|
144
|
+
if command -v npm >/dev/null 2>&1; then
|
|
145
|
+
v="$(npm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v=""
|
|
146
|
+
fi
|
|
147
|
+
if [ -z "$v" ] && command -v pnpm >/dev/null 2>&1; then
|
|
148
|
+
v="$(pnpm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v=""
|
|
149
|
+
fi
|
|
150
|
+
if [ -n "$v" ]; then printf '%s@%s' "$PKG" "$v"
|
|
151
|
+
else printf '%s@latest' "$PKG"
|
|
152
|
+
fi
|
|
153
|
+
else
|
|
154
|
+
printf '%s@%s' "$PKG" "$given"
|
|
155
|
+
fi
|
|
156
|
+
else
|
|
157
|
+
local ref="${VERSION_SPEC:-}"
|
|
158
|
+
if [ -n "$ref" ]; then
|
|
159
|
+
printf '%s@github:%s#%s' "$PKG" "$GITHUB_REPO" "$ref"
|
|
160
|
+
else
|
|
161
|
+
printf '%s@github:%s' "$PKG" "$GITHUB_REPO"
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
}
|
|
165
|
+
ADD_SPEC="$(resolve_add_spec)"
|
|
166
|
+
|
|
167
|
+
# ---------- dsh CLI 解析(npx 兜底需交互确认) ----------
|
|
168
|
+
DSH_CLI_CMD=""
|
|
169
|
+
NPX_FALLBACK=false
|
|
170
|
+
if command -v "$DSH_CMD" >/dev/null 2>&1; then
|
|
171
|
+
DSH_CLI_CMD="$DSH_CMD"
|
|
172
|
+
elif command -v npx >/dev/null 2>&1; then
|
|
173
|
+
DSH_CLI_CMD="npx -y --package @deepseek-ai/dsh dsh"
|
|
174
|
+
NPX_FALLBACK=true
|
|
175
|
+
else
|
|
176
|
+
die "未找到 dsh 或 npx。请先安装 DSH,或用 DSH_CMD 指定。"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
say "目标:$DSH_CLI_CMD plugin --profile $PROFILE_NAME add $ADD_SPEC(profile 目录: $PROFILE_DIR)"
|
|
180
|
+
say "来源:$FROM(仓库 $GITHUB_REPO)"
|
|
181
|
+
|
|
182
|
+
if [ "$DRY_RUN" = true ]; then
|
|
183
|
+
say "[dry-run] 1) minimumReleaseAgeExclude 处理(--fix-profile 时写入,否则仅检查+warn):$WS_YML"
|
|
184
|
+
say "[dry-run] 2) $DSH_CLI_CMD plugin --profile $PROFILE_NAME add $ADD_SPEC"
|
|
185
|
+
say "[dry-run] 3) 校验 dsh.profile.bundles 含 $PKG:$PKG_JSON"
|
|
186
|
+
say "[dry-run] 4) allowBuilds 预检(advisory):$PROFILE_DIR/node_modules/.modules.yaml"
|
|
187
|
+
say "[dry-run] 5) 旧手动挂载清理(仅 --fix-profile):$PATCH_YML"
|
|
188
|
+
if [ "$RESTART" = true ]; then
|
|
189
|
+
say "[dry-run] 6) pm2 restart dsh-web"
|
|
190
|
+
else
|
|
191
|
+
say "[dry-run] 6) 提示重启 DSH web 并硬刷新浏览器"
|
|
192
|
+
fi
|
|
193
|
+
exit 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# npx 兜底:任何写操作发生前先交互确认
|
|
197
|
+
if [ "$NPX_FALLBACK" = true ]; then
|
|
198
|
+
say "未找到 dsh,将用 npx 兜底执行:$DSH_CLI_CMD plugin --profile $PROFILE_NAME add $ADD_SPEC"
|
|
199
|
+
if [ "${DSH_INSTALL_YES:-0}" != "1" ]; then
|
|
200
|
+
printf '继续执行将联网下载并运行 @deepseek-ai/dsh,确认? [y/N] '
|
|
201
|
+
REPLY=""
|
|
202
|
+
read -r REPLY || { echo; die "读取确认失败,中止。"; }
|
|
203
|
+
case "$REPLY" in
|
|
204
|
+
y|Y|yes|YES|Yes) ;;
|
|
205
|
+
*) die "已中止(未执行任何安装步骤)。" ;;
|
|
206
|
+
esac
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
[ -f "$PKG_JSON" ] || die "找不到 $PKG_JSON(profile 未初始化?)"
|
|
211
|
+
if [ "$FIX_PROFILE" = true ] && [ ! -f "$WS_YML" ]; then
|
|
212
|
+
die "找不到 $WS_YML(--fix-profile 需要它来补写 minimumReleaseAgeExclude)"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# ---------- 1) minimumReleaseAgeExclude ----------
|
|
216
|
+
if [ "$FIX_PROFILE" = true ]; then
|
|
217
|
+
WS_RESULT="$(node "$LIB_DIR/ws-exclude.cjs" "$WS_YML" "$PKG")" \
|
|
218
|
+
|| die "--fix-profile:补写 minimumReleaseAgeExclude 失败(文件已保持原样)。"
|
|
219
|
+
case "$WS_RESULT" in
|
|
220
|
+
updated) say "已补写 $WS_YML:minimumReleaseAgeExclude += $PKG" ;;
|
|
221
|
+
unchanged) say "minimumReleaseAgeExclude 已含 $PKG,跳过" ;;
|
|
222
|
+
*) say "workspace exclude 状态: $WS_RESULT" ;;
|
|
223
|
+
esac
|
|
224
|
+
else
|
|
225
|
+
if [ -f "$WS_YML" ]; then
|
|
226
|
+
if ! node "$LIB_DIR/ws-exclude.cjs" --check "$WS_YML" "$PKG" >/dev/null 2>&1; then
|
|
227
|
+
warn "minimumReleaseAgeExclude 未包含 ${PKG}——pnpm minimumReleaseAge 可能拒绝安装;可加 --fix-profile 自动补写。"
|
|
228
|
+
fi
|
|
229
|
+
else
|
|
230
|
+
warn "未找到 $WS_YML,跳过 minimumReleaseAgeExclude 检查。"
|
|
231
|
+
fi
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
# ---------- 2) plugin add ----------
|
|
235
|
+
say "执行 $DSH_CLI_CMD plugin --profile $PROFILE_NAME add $ADD_SPEC ..."
|
|
236
|
+
read -r -a CLI_ARR <<< "$DSH_CLI_CMD"
|
|
237
|
+
if ! "${CLI_ARR[@]}" plugin --profile "$PROFILE_NAME" add "$ADD_SPEC"; then
|
|
238
|
+
warn "dsh plugin add 失败。可检查网络、registry,或手动执行:"
|
|
239
|
+
warn " $DSH_CLI_CMD plugin --profile $PROFILE_NAME add \"$ADD_SPEC\""
|
|
240
|
+
warn "前置建议:dsh plugin --profile $PROFILE_NAME add dsh-better-sidebar"
|
|
241
|
+
exit 1
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# ---------- 3) bundle 校验 ----------
|
|
245
|
+
if ! node "$LIB_DIR/bundle-check.cjs" "$PKG_JSON" "$PKG"; then
|
|
246
|
+
warn "$PKG 未出现在 dsh.profile.bundles——挂载未注册。"
|
|
247
|
+
exit 1
|
|
248
|
+
fi
|
|
249
|
+
say "bundle 已注册:dsh.profile.bundles 包含 $PKG"
|
|
250
|
+
|
|
251
|
+
# ---------- 4) allowBuilds 预检(advisory) ----------
|
|
252
|
+
if ! node "$LIB_DIR/bundle-check.cjs" --ignored-builds "$PROFILE_DIR"; then
|
|
253
|
+
warn "allowBuilds 预检异常(不影响安装结果)。"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# ---------- 5) 旧手动挂载清理(仅 --fix-profile) ----------
|
|
257
|
+
if [ "$FIX_PROFILE" = true ] && [ -f "$PATCH_YML" ]; then
|
|
258
|
+
STRIP_RESULT="$(node "$LIB_DIR/strip-mount.cjs" "$PATCH_YML" "$PLUGIN_ID")" \
|
|
259
|
+
|| die "清理旧手动挂载失败($PATCH_YML 已回滚为原内容;插件本体安装不受影响)。"
|
|
260
|
+
case "$STRIP_RESULT" in
|
|
261
|
+
removed) say "已从 $PATCH_YML 移除旧的 $PLUGIN_ID 手动挂载块" ;;
|
|
262
|
+
none) say "无旧手动挂载块,跳过" ;;
|
|
263
|
+
esac
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
say "安装完成:$ADD_SPEC(profile: $PROFILE_NAME)"
|
|
267
|
+
say "验证:dsh --profile $PROFILE_NAME --dump-config | grep -n '${PLUGIN_ID}\\|${PKG}'"
|
|
268
|
+
|
|
269
|
+
if [ "$RESTART" = true ]; then
|
|
270
|
+
if command -v pm2 >/dev/null 2>&1; then
|
|
271
|
+
say "重启 dsh-web(pm2)..."
|
|
272
|
+
pm2 restart dsh-web || warn "pm2 restart 失败,请手动重启 DSH"
|
|
273
|
+
else
|
|
274
|
+
warn "未找到 pm2,请手动重启 DSH(docker compose restart / pm2 / 进程管理器)"
|
|
275
|
+
fi
|
|
276
|
+
else
|
|
277
|
+
say "下一步:重启 DSH web,并硬刷新浏览器(Cmd/Ctrl+Shift+R)。"
|
|
278
|
+
fi
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/*
|
|
4
|
+
* 安装后的两类检查(install.sh / install.ps1 共用)。
|
|
5
|
+
*
|
|
6
|
+
* 1) node bundle-check.cjs <profile/package.json> <pkg>
|
|
7
|
+
* 校验 dsh.profile.bundles 是否包含 pkg:包含退出 0(打印 ok),否则退出 1。
|
|
8
|
+
*
|
|
9
|
+
* 2) node bundle-check.cjs --ignored-builds <profileDir>
|
|
10
|
+
* 读取 <profileDir>/node_modules/.modules.yaml 的 ignoredBuilds 段,
|
|
11
|
+
* 打印 allowBuilds 可复制豁免指引。advisory 性质:恒退出 0;
|
|
12
|
+
* .modules.yaml 不存在时跳过不报错。
|
|
13
|
+
*/
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
function err2(msg) {
|
|
18
|
+
process.stderr.write('[bundle-check] ' + msg + '\n');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const argv = process.argv.slice(2);
|
|
22
|
+
|
|
23
|
+
if (argv[0] === '--ignored-builds') {
|
|
24
|
+
const profileDir = argv[1];
|
|
25
|
+
if (!profileDir || argv.length !== 2) {
|
|
26
|
+
process.stderr.write('用法: node bundle-check.cjs --ignored-builds <profileDir>\n');
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
const modulesYaml = path.join(profileDir, 'node_modules', '.modules.yaml');
|
|
30
|
+
let text;
|
|
31
|
+
try {
|
|
32
|
+
text = fs.readFileSync(modulesYaml, 'utf8');
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.log('[allowBuilds] 未找到 ' + modulesYaml + ',跳过 allowBuilds 预检。');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
const lines = text.split('\n');
|
|
38
|
+
let start = -1;
|
|
39
|
+
let inlineRest = '';
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
const m = lines[i].match(/^ignoredBuilds:(.*)$/);
|
|
42
|
+
if (m) {
|
|
43
|
+
start = i;
|
|
44
|
+
inlineRest = m[1].trim();
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const entries = [];
|
|
49
|
+
if (start !== -1) {
|
|
50
|
+
if (inlineRest && inlineRest !== '[]') {
|
|
51
|
+
// 内联形式(如 ignoredBuilds: [a, b])
|
|
52
|
+
const inner = inlineRest.replace(/^\[/, '').replace(/\]$/, '');
|
|
53
|
+
for (const part of inner.split(',')) {
|
|
54
|
+
const v = part.trim().replace(/^['"]|['"]$/g, '');
|
|
55
|
+
if (v) entries.push(v);
|
|
56
|
+
}
|
|
57
|
+
} else if (!inlineRest) {
|
|
58
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
59
|
+
if (lines[j].trim() === '') continue;
|
|
60
|
+
const im = lines[j].match(/^[ \t]+-[ \t]*(.+)$/);
|
|
61
|
+
if (!im) break;
|
|
62
|
+
const v = im[1].trim().replace(/^['"]|['"]$/g, '');
|
|
63
|
+
if (v) entries.push(v);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (entries.length === 0) {
|
|
68
|
+
console.log('[allowBuilds] ignoredBuilds 为空或不存在,无需处理。');
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
const wsPath = path.join(profileDir, 'pnpm-workspace.yaml');
|
|
72
|
+
const out = [];
|
|
73
|
+
out.push('[allowBuilds] pnpm 跳过了以下依赖的构建脚本(node_modules/.modules.yaml: ignoredBuilds):');
|
|
74
|
+
for (const e of entries) out.push(' - ' + e);
|
|
75
|
+
out.push('这些包的 install 脚本没有执行,相关功能可能不完整。如需豁免,把下面内容并入 ' + wsPath + '(已有 allowBuilds 段则合并键值):');
|
|
76
|
+
out.push('');
|
|
77
|
+
out.push('allowBuilds:');
|
|
78
|
+
for (const e of entries) out.push(' "' + e + '": true');
|
|
79
|
+
out.push('');
|
|
80
|
+
out.push('注意: 键必须是上面 ignoredBuilds 行的原文(与 lockfile 中的 spec 逐字一致);纯包名(如 ssh2)不生效。');
|
|
81
|
+
console.log(out.join('\n'));
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 模式 1:bundle 校验
|
|
86
|
+
const pkgJsonPath = argv[0];
|
|
87
|
+
const pkg = argv[1];
|
|
88
|
+
if (!pkgJsonPath || !pkg || argv.length !== 2) {
|
|
89
|
+
process.stderr.write('用法: node bundle-check.cjs <profile/package.json> <pkg>\n');
|
|
90
|
+
process.exit(2);
|
|
91
|
+
}
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
|
|
95
|
+
} catch (e) {
|
|
96
|
+
err2('读取/解析失败: ' + pkgJsonPath + ' (' + e.message + ')');
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
const bundles =
|
|
100
|
+
parsed && parsed.dsh && parsed.dsh.profile && Array.isArray(parsed.dsh.profile.bundles)
|
|
101
|
+
? parsed.dsh.profile.bundles
|
|
102
|
+
: null;
|
|
103
|
+
if (bundles && bundles.indexOf(pkg) !== -1) {
|
|
104
|
+
console.log('ok: dsh.profile.bundles contains ' + pkg);
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
err2('dsh.profile.bundles 不含 ' + pkg + '(当前: ' + JSON.stringify(bundles) + ')');
|
|
108
|
+
process.exit(1);
|