feat: 新增 Windows PowerShell 一键安装脚本 install.ps1,README 安装章节两平台同步

- scripts/install.ps1:官方 CLI 方式一键安装(PowerShell 5.1+/pwsh),
  与 install.sh 对齐:预写 allowBuilds + minimumReleaseAgeExclude(幂等)、
  执行 dsh plugin add、幂等清理旧手动挂载行;支持 -Version/-Restart/-DryRun。
- scripts/install.sh:加固 — -h/--help、Windows Git Bash 下 DSH_HOME 回退
  $USERPROFILE、npm/pnpm view 双通道解析最新版本、失败提示细化、挂载行
  清理改为逐块扫描(删除注释块跟随)。
- README.md:安装章节改为「① 前置 → ② 对应平台一键命令 → ③ 重启硬刷新」,
  macOS/Linux 与 Windows PowerShell 双命令示例。
- package.json:files 列表纳入 scripts/install.ps1。
This commit is contained in:
Menghuan1918
2026-08-14 00:48:44 +08:00
parent 8bf6d1d7ba
commit e128c303bc
4 changed files with 451 additions and 67 deletions
+242
View File
@@ -0,0 +1,242 @@
# =============================================================================
# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,Windows PowerShell 5.1+ / pwsh)
#
# 通过 DSH 官方插件命令安装 npm 包并自动挂载:
# dsh plugin --profile web add dsh-better-sidebar@<version>
#
# 包内声明了 dsh.bundle.patch(cordis.patch.yml):CLI 的 bundle 协调会把它
# 自动加进 profile 的 dsh.profile.bundles,下次启动即挂载——无需手动写
# cordis.patch.yml 挂载行。符合仓库硬约束:不修改 DSH 源码,插件永远作为
# 独立包被 profile 引用。
#
# 用法(任选其一):
# # 默认最新版
# irm https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1 | iex
# # 指定版本 / 装完重启
# & ([scriptblock]::Create((irm 'https://raw.githubusercontent.com/omdsh-dev/DSH-better-sidebar/main/scripts/install.ps1'))) -Version 0.10.2 -Restart
# # 本地保存后运行
# powershell -ExecutionPolicy Bypass -File install.ps1 -Version 0.10.2 -DryRun
#
# 参数:
# -Version npm 版本号/范围,缺省 latest(自动解析为最新)。
# -Restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅提示)。会断开当前页面会话。
# -DryRun 只打印将要执行的操作,不写任何文件。
#
# 环境变量(均可省略):
# DSH_HOME 默认 %USERPROFILE%\.dsh
# REGISTRY 默认 https://registry.npmjs.org
# DSH_CMD 默认优先 PATH 上的 dsh,缺省回退 npx -y --package @deepseek-ai/dsh
#
# 说明:
# - pnpm 11 的 strict-dep-builds 会拦截 node-pty/protobufjs 的构建脚本并使
# `dsh plugin add` 非零退出。脚本会先把这两个构建许可写进 profile 的
# pnpm-workspace.yaml(幂等),保证 CLI 一步成功。
# - pnpm 11 的 minimumReleaseAge 会拒绝发布 <24h 的新版本。脚本会预写
# minimumReleaseAgeExclude(幂等),放行本插件,避免"重跑一次才成功"。
# - 老版本(<0.10.2)用手动挂载行,bundle 通道激活后需移除,否则双挂载。
# 脚本会幂等移除 better-sidebar 挂载行。
# =============================================================================
param(
[string]$Version = '',
[switch]$Restart,
[switch]$DryRun
)
$PKG = 'dsh-better-sidebar'
$REGISTRY = if ($env:REGISTRY) { $env:REGISTRY } else { 'https://registry.npmjs.org' }
# DSH_HOME:DSH_HOME 环境变量 > %USERPROFILE% > $HOME
if ($env:DSH_HOME) {
$DSH_HOME = $env:DSH_HOME
} elseif ($env:USERPROFILE) {
$DSH_HOME = Join-Path $env:USERPROFILE '.dsh'
} else {
$DSH_HOME = Join-Path $HOME '.dsh'
}
$PROFILE_DIR = Join-Path $DSH_HOME 'profiles\web'
$WS_YML = Join-Path $PROFILE_DIR 'pnpm-workspace.yaml'
$PATCH_YML = Join-Path $PROFILE_DIR 'cordis.patch.yml'
function Say([string]$m) { Write-Host "[install] $m" -ForegroundColor Green }
function Warn([string]$m) { Write-Host "[warn] $m" -ForegroundColor Yellow }
function Die([string]$m) { Write-Host "[error] $m" -ForegroundColor Red; exit 1 }
# 解析版本 -> npm spec("x.y.z" / "^x.y.z" / latest)
function Resolve-Spec {
param([string]$Given)
if ([string]::IsNullOrWhiteSpace($Given) -or $Given -eq 'latest') {
$v = $null
foreach ($tool in @('npm', 'pnpm')) {
if (Get-Command $tool -ErrorAction SilentlyContinue) {
$v = (& $tool view $PKG version "--registry=$REGISTRY" 2>$null | Select-Object -Last 1)
if ($v) { break }
}
}
if ($v) { return ([string]$v).Trim() }
Warn '无法联网解析最新版本(npm/pnpm 查询失败),回退为 latest,由 pnpm 直接解析。'
Warn '若已知版本号,可显式传入:-Version 0.10.2'
return 'latest'
}
return $Given
}
# 组装 dsh CLI:优先 PATH 上的 dsh,缺省 npx 拉官方包
function Get-DshCli {
if ($env:DSH_CMD) { return $env:DSH_CMD }
if (Get-Command dsh -ErrorAction SilentlyContinue) { return 'dsh' }
if (Get-Command npx -ErrorAction SilentlyContinue) { return 'npx' }
return $null
}
# 前置校验
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Die '未找到 node(DSH 运行需要 Node.js >= 20),请先安装 Node.js 并加入 PATH。'
}
if (-not (Test-Path $PROFILE_DIR)) {
Die "找不到 profile 目录:$PROFILE_DIR(请先安装并运行过一次 dsh web)"
}
if (-not (Test-Path $WS_YML)) {
Die "找不到 $WS_YML(请先初始化 web profile)"
}
$SPEC = Resolve-Spec $Version
$CLI = Get-DshCli
if (-not $CLI) {
Die '未找到 dsh 或 npx。请先安装 DSH(并确保 Node/npm 可用),或用 DSH_CMD 指定。'
}
Say "目标:$CLI plugin --profile web add $PKG@$SPEC(profile: $PROFILE_DIR)"
if ($DryRun) {
Say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)与 minimumReleaseAgeExclude($PKG)"
Say "[dry-run] 步骤 2:执行 $CLI plugin --profile web add $PKG@$SPEC(安装 + bundle 自动注册)"
Say "[dry-run] 步骤 3:校验 dsh.profile.bundles 含 $PKG"
Say "[dry-run] 步骤 4:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)"
if ($Restart) { Say '[dry-run] 步骤 5:pm2 restart dsh-web' } else { Say '[dry-run] 步骤 5:提示用户手动重启 DSH' }
exit 0
}
# 步骤 1:预写 workspace 设置(幂等),保证 pnpm 不拦截构建、放行本插件新版本
$wsScript = @'
const fs = require("fs");
const p = process.argv[1];
let t = fs.readFileSync(p, "utf8");
const before = t;
t = t.replace(/^(\s*)(node-pty|protobufjs):.*$/gm, "$1$2: true");
if (!/^\s*allowBuilds:\s*$/m.test(t)) {
t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n";
} else {
for (const k of ["node-pty", "protobufjs"]) {
if (!new RegExp("^\\s*" + k + ":\\s*true\\s*$", "m").test(t)) {
t = t.replace(/^(\s*allowBuilds:\s*)$/m, "$1\n " + k + ": true");
}
}
}
if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) {
if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) {
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar");
} else {
t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n";
}
}
if (t !== before) fs.writeFileSync(p, t);
console.log(t === before ? "unchanged" : "updated");
'@
$wsOut = node -e $wsScript "$WS_YML" 2>&1
$wsCode = $LASTEXITCODE
$wsResult = (($wsOut | Out-String)).Trim()
if ($wsCode -ne 0) { Die "处理 $WS_YML 失败(node 退出码 $wsCode):$wsResult" }
if ($wsResult -eq 'updated') {
Say "已确保 $WS_YML:allowBuilds(node-pty/protobufjs: true)+ minimumReleaseAgeExclude($PKG)"
} else {
Say 'workspace 设置已就绪,跳过'
}
# 步骤 2:官方 CLI 安装 + bundle 自动注册(含挂载)
if ($CLI -eq 'dsh') {
$cliArgs = @('plugin', '--profile', 'web', 'add', "$PKG@$SPEC")
} else {
$cliArgs = @('-y', '--package', '@deepseek-ai/dsh', 'dsh', 'plugin', '--profile', 'web', 'add', "$PKG@$SPEC")
}
Say "执行 $CLI plugin --profile web add $PKG@$SPEC ..."
$addOut = & $CLI @cliArgs 2>&1
$addCode = $LASTEXITCODE
$addOut | ForEach-Object { $_ }
if ($addCode -ne 0) {
Warn 'dsh plugin add 失败。已预写 allowBuilds 与 minimumReleaseAgeExclude,仍失败的可能原因:'
Warn ' - 网络/登录问题:npm registry 不可达或需要登录。'
Warn " - 依赖安装冲突:可手动重试 cd $PROFILE_DIR; pnpm install。"
exit 1
}
# 步骤 3:校验 bundle 已注册(挂载生效的判据)
$pkgJson = Get-Content -Raw (Join-Path $PROFILE_DIR 'package.json') | ConvertFrom-Json
$bundles = $pkgJson.dsh.profile.bundles
if ($bundles -notcontains $PKG) {
Warn 'dsh-better-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册。'
Warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。"
exit 1
}
Say "bundle 已注册:dsh.profile.bundles 包含 $PKG(下次启动自动挂载)"
# 步骤 4:幂等移除旧的 manual 挂载行(避免与 bundle 双挂载)
$mountScript = @'
const fs = require("fs");
const p = process.argv[1];
const lines = fs.readFileSync(p, "utf8").split("\n");
const out = [];
let i = 0;
let removed = false;
while (i < lines.length) {
const line = lines[i];
if (/^[ \t]*- insert:\s*$/.test(line)) {
const block = [line];
let j = i + 1;
while (j < lines.length && lines[j].trim() !== "" && !/^-\s/.test(lines[j])) {
block.push(lines[j]);
j++;
}
if (block.some((l) => /id:\s*better-sidebar\b/.test(l))) {
while (out.length && /^[ \t]*#/.test(out[out.length - 1])) out.pop();
i = j;
removed = true;
continue;
}
}
out.push(line);
i++;
}
if (!removed) {
console.log("none");
} else {
const t = out.join("\n").replace(/\n{3,}/g, "\n\n");
fs.writeFileSync(p, t);
console.log("removed");
}
'@
$mountOut = node -e $mountScript "$PATCH_YML" 2>&1
$mountCode = $LASTEXITCODE
$mountResult = (($mountOut | Out-String)).Trim()
if ($mountCode -ne 0) { Die "处理 $PATCH_YML 失败(node 退出码 $mountCode):$mountResult" }
if ($mountResult -eq 'removed') {
Say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)"
} else {
Say '无旧手动挂载行,跳过'
}
Say "安装完成:$PKG@$SPEC"
# 步骤 5:重启提示
if ($Restart) {
if (Get-Command pm2 -ErrorAction SilentlyContinue) {
Say '重启 dsh-web(pm2)...'
pm2 restart dsh-web
if ($LASTEXITCODE -ne 0) { Warn 'pm2 restart 失败,请手动重启 DSH' }
} else {
Warn '未找到 pm2,请手动重启 DSH(如:pm2 restart dsh-web 或 dsh web)'
}
} else {
Say '下一步:重启 DSH 并硬刷新(Ctrl+Shift+R / Cmd+Shift+R)使新副本生效。'
if (Get-Command pm2 -ErrorAction SilentlyContinue) {
Say '本机可用:pm2 restart dsh-web(会短暂断开当前页面会话)'
}
}
+126 -50
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# =============================================================================
# dsh-better-sidebar 一键安装脚本(官方 CLI 方式)
# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,macOS / Linux / Windows Git Bash)
#
# 通过 DSH 官方插件命令安装 npm 包并自动挂载:
# dsh plugin --profile web add dsh-better-sidebar@<version>
@@ -13,14 +13,15 @@
# 用法:
# bash scripts/install.sh [版本] [--restart] [--dry-run]
#
# 版本 npm 版本号/范围,缺省为 latest(自动解析为 ^<最新>)。
# 示例:0.10.2、^0.10.2、~0.10.2、latest
# 版本 npm 版本号/范围,缺省为 latest(自动解析为 ^<最新>)。
# 示例:0.10.2、^0.10.2、~0.10.2、latest
# --restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅打印提示)。
# 注意:重启会断开当前 DSH 页面会话,默认不自动重启。
# --dry-run 只打印将要执行的操作,不写任何文件。
# -h/--help 打印本帮助。
#
# 环境:
# DSH_HOME 默认 ~/.dsh
# 环境(均可省略,脚本会自动探测):
# DSH_HOME 默认 ~/.dsh(Windows Git Bash 下回退 $USERPROFILE/.dsh)
# REGISTRY 默认 https://registry.npmjs.org(发布源;装依赖仍走 pnpm 配置)
# DSH_CMD 默认优先用 PATH 上的 `dsh`,缺省回退 npx -y --package @deepseek-ai/dsh
#
@@ -28,6 +29,8 @@
# - pnpm 11 的 strict-dep-builds 会拦截 node-pty/protobufjs 的构建脚本并使
# `dsh plugin add` 非零退出(bundle 协调因此被跳过)。脚本会先把这两个
# 构建许可写进 profile 的 pnpm-workspace.yaml(幂等),保证 CLI 一步成功。
# - pnpm 11 的 minimumReleaseAge 会拒绝发布 <24h 的新版本。脚本会预写
# minimumReleaseAgeExclude(幂等),放行本插件,避免"重跑一次才成功"。
# - 老版本(<0.10.2)用手动挂载行,bundle 通道激活后需移除,否则双挂载
# (Node 半挂两次、页面两个侧边栏)。脚本会幂等移除 better-sidebar 挂载行。
# - 回滚:dsh plugin --profile web remove dsh-better-sidebar,或把 profile 依赖
@@ -35,7 +38,25 @@
# =============================================================================
set -euo pipefail
DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
# 帮助请求优先处理
for arg in "$@"; do
if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then
cat <<'EOF'
dsh-better-sidebar 一键安装脚本
用法:bash scripts/install.sh [版本] [--restart] [--dry-run]
版本 npm 版本号/范围,缺省 latest(自动解析为最新)。示例:0.10.2、^0.10.2、latest
--restart 装完后尝试 `pm2 restart dsh-web`(无 pm2 时仅提示)
--dry-run 只打印将要执行的操作,不写任何文件
环境变量(可省略):DSH_HOME(默认 ~/.dsh)、REGISTRY(npm 源)、DSH_CMD(dsh 命令)
EOF
exit 0
fi
done
DSH_HOME="${DSH_HOME:-${HOME:-${USERPROFILE:-}}/.dsh}"
PROFILE_DIR="$DSH_HOME/profiles/web"
WS_YML="$PROFILE_DIR/pnpm-workspace.yaml"
PATCH_YML="$PROFILE_DIR/cordis.patch.yml"
@@ -50,7 +71,8 @@ for arg in "$@"; do
case "$arg" in
--restart) RESTART=true ;;
--dry-run) DRY_RUN=true ;;
-*) echo "未知参数: $arg" >&2; exit 2 ;;
-h|--help) : ;; # 已在上面处理
-*) echo "未知参数: ${arg}(用 -h 查看用法)" >&2; exit 2 ;;
*) VERSION_SPEC="$arg" ;;
esac
done
@@ -59,15 +81,25 @@ say() { printf '\033[32m[install]\033[0m %s\n' "$*"; }
warn() { printf '\033[33m[warn]\033[0m %s\n' "$*" >&2; }
die() { printf '\033[31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
# 解析用户给的版本 -> CLI 要用的 npm spec("x.y.z" / "^x.y.z")
# 解析用户给的版本 -> CLI 要用的 npm spec("x.y.z" / "^x.y.z" / latest)
resolve_spec() {
local given="${1:-latest}"
case "$given" in
latest)
local v
v="$(npm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" \
|| die "无法从 $REGISTRY 解析 $PKG 的最新版本(请检查网络/登录)。可显式传版本号:bash scripts/install.sh 0.10.2"
printf '%s' "$v"
local v=""
if command -v npm >/dev/null 2>&1; then
v="$(npm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v=""
fi
if [ -z "$v" ] && command -v pnpm >/dev/null 2>&1; then
v="$(pnpm view "$PKG" version --registry="$REGISTRY" 2>/dev/null)" || v=""
fi
if [ -n "$v" ]; then
printf '%s' "$v"
else
warn "无法联网解析最新版本(npm/pnpm 查询失败),回退为 latest,由 pnpm 直接解析。"
warn "若已知版本号,可显式传入:bash scripts/install.sh 0.10.2"
printf 'latest'
fi
;;
*) printf '%s' "$given" ;;
esac
@@ -77,11 +109,16 @@ resolve_spec() {
dsh_cli() {
if command -v "$DSH_CMD" >/dev/null 2>&1; then
printf '%s' "$DSH_CMD"
else
elif command -v npx >/dev/null 2>&1; then
printf 'npx -y --package @deepseek-ai/dsh dsh'
else
die "未找到 dsh 或 npx。请先安装 DSH(并确保 Node/npm 可用),或用 DSH_CMD 指定 dsh 路径。"
fi
}
# 前置校验
command -v node >/dev/null 2>&1 || die "未找到 node(DSH 运行需要 Node.js ≥ 20),请先安装 Node.js 并加入 PATH。"
[ -d "$PROFILE_DIR" ] || die "找不到 profile 目录:${PROFILE_DIR}(请先安装并运行过一次 dsh web)"
[ -f "$WS_YML" ] || die "找不到 ${WS_YML}(请先初始化 web profile)"
@@ -90,35 +127,52 @@ CLI="$(dsh_cli)"
say "目标:$CLI plugin --profile web add $PKG@${SPEC}(profile: ${PROFILE_DIR})"
if [ "$DRY_RUN" = true ]; then
say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)"
say "[dry-run] 步骤 1:确保 $WS_YML 含 allowBuilds(node-pty/protobufjs: true)与 minimumReleaseAgeExclude(${PKG})"
say "[dry-run] 步骤 2:执行 $CLI plugin --profile web add $PKG@${SPEC}(安装 + bundle 自动注册)"
say "[dry-run] 步骤 3:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)"
if [ "$RESTART" = true ]; then say "[dry-run] 步骤 4:pm2 restart dsh-web"; else say "[dry-run] 步骤 4:提示用户手动重启 DSH"; fi
say "[dry-run] 步骤 3:校验 dsh.profile.bundles 含 $PKG"
say "[dry-run] 步骤 4:幂等移除 $PATCH_YML 里旧的 better-sidebar 手动挂载行(避免双挂载)"
if [ "$RESTART" = true ]; then say "[dry-run] 步骤 5:pm2 restart dsh-web"; else say "[dry-run] 步骤 5:提示用户手动重启 DSH"; fi
exit 0
fi
# 步骤 1:预写 allowBuilds(幂等),保证 pnpm 不为 node-pty/protobufjs 拦截构建
if ! grep -qE '^\s*node-pty: true\s*$' "$WS_YML"; then
# 清除 pnpm 自动写入的占位值("set this to true or false")后写入 true
node -e '
const fs = require("fs");
const p = process.argv[1];
let t = fs.readFileSync(p, "utf8");
t = t.replace(/node-pty:.*/g, "node-pty: true");
t = t.replace(/protobufjs:.*/g, "protobufjs: true");
if (!t.includes("allowBuilds:")) t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n";
fs.writeFileSync(p, t);
' "$WS_YML"
say "已确保 $WS_YML 的 allowBuilds(node-pty/protobufjs: true)"
else
say "allowBuilds 已就绪,跳过"
fi
# 步骤 1:预写 workspace 设置(幂等),保证 pnpm 不拦截构建、不放行旧版本策略
WS_RESULT="$(node -e '
const fs = require("fs");
const p = process.argv[1];
let t = fs.readFileSync(p, "utf8");
const before = t;
// allowBuilds:把 node-pty/protobufjs 归位为 true
t = t.replace(/^(\s*)(node-pty|protobufjs):.*$/gm, "$1$2: true");
if (!/^\s*allowBuilds:\s*$/m.test(t)) {
t += "\nallowBuilds:\n node-pty: true\n protobufjs: true\n";
} else {
for (const k of ["node-pty", "protobufjs"]) {
if (!new RegExp("^\\s*" + k + ":\\s*true\\s*$", "m").test(t)) {
t = t.replace(/^(\s*allowBuilds:\s*)$/m, "$1\n " + k + ": true");
}
}
}
// minimumReleaseAgeExclude:放行本插件(版本无关),避免 <24h 新版本被拒
if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) {
if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) {
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar");
} else {
t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n";
}
}
if (t !== before) fs.writeFileSync(p, t);
console.log(t === before ? "unchanged" : "updated");
' "$WS_YML")"
[ "$WS_RESULT" = "updated" ] \
&& say "已确保 $WS_YML:allowBuilds(node-pty/protobufjs: true)+ minimumReleaseAgeExclude(${PKG})" \
|| say "workspace 设置已就绪,跳过"
# 步骤 2:官方 CLI 安装 + bundle 自动注册(含挂载)
say "执行 $CLI plugin --profile web add $PKG@$SPEC ..."
if ! $CLI plugin --profile web add "$PKG@$SPEC" 2>&1 | tail -n +1; then
warn "dsh plugin add 失败。可能原因:minimumReleaseAge(发布 <24h,pnpm 通常自动写入排除并重试)"
warn "或 allowBuilds 未生效。可手动重试:cd $PROFILE_DIR && pnpm install"
warn "dsh plugin add 失败。已预写 allowBuilds 与 minimumReleaseAgeExclude,仍失败的可能原因:"
warn " - 网络/登录问题:npm registry 不可达或需要登录。"
warn " - 依赖安装冲突:可手动重试 cd $PROFILE_DIR && pnpm install。"
exit 1
fi
@@ -133,24 +187,46 @@ if ! node -e '
warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。"
exit 1
fi
say "bundle 已注册:dsh.profile.bundles 包含 $PKG(下次启动自动挂载)"
say "bundle 已注册:dsh.profile.bundles 包含 ${PKG}(下次启动自动挂载)"
# 步骤 3:幂等移除旧的 manual 挂载行(避免与 bundle 双挂载)
if grep -qE '^\s*- id: better-sidebar\b' "$PATCH_YML"; then
node -e '
const fs = require("fs");
const p = process.argv[1];
let t = fs.readFileSync(p, "utf8");
// 移除 better-sidebar 的 insert 块(含其前的注释行)
t = t.replace(/(?:^[ \t]*#[^\n]*\n)*[ \t]*- insert:\n[ \t]+- id: better-sidebar\n[ \t]+name: '"'"'dsh-better-sidebar'"'"'\n?/g, "");
// 清理可能留下的空行堆积
t = t.replace(/\n{3,}/g, "\n\n");
fs.writeFileSync(p, t);
' "$PATCH_YML"
say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)"
else
say "无旧手动挂载行,跳过"
fi
MOUNT_RESULT="$(node -e '
const fs = require("fs");
const p = process.argv[1];
const lines = fs.readFileSync(p, "utf8").split("\n");
const out = [];
let i = 0;
let removed = false;
while (i < lines.length) {
const line = lines[i];
if (/^[ \t]*- insert:\s*$/.test(line)) {
const block = [line];
let j = i + 1;
while (j < lines.length && lines[j].trim() !== "" && !/^-\s/.test(lines[j])) {
block.push(lines[j]);
j++;
}
if (block.some((l) => /id:\s*better-sidebar\b/.test(l))) {
while (out.length && /^[ \t]*#/.test(out[out.length - 1])) out.pop();
i = j;
removed = true;
continue;
}
}
out.push(line);
i++;
}
if (!removed) {
console.log("none");
} else {
const t = out.join("\n").replace(/\n{3,}/g, "\n\n");
fs.writeFileSync(p, t);
console.log("removed");
}
' "$PATCH_YML")"
[ "$MOUNT_RESULT" = "removed" ] \
&& say "已从 $PATCH_YML 移除旧的 better-sidebar 手动挂载行(bundle 通道接管挂载)" \
|| say "无旧手动挂载行,跳过"
say "安装完成:$PKG@$SPEC"