chore(sync): re-base the fork onto upstream dsh-better-sidebar v0.19.1
The fork was pinned at upstream v0.18.0. That is the entire reason the window
ran TWO sidebars and TWO near-identical panel toggles: DSH 2.0.10 ships its own
right sidebar (@deepseek-ai/dsh-client-ui-sidebar-right), and upstream only
adopted it in v0.19.0-alpha.1 — which arrived after our fork point. v0.19.1 is
also the one upstream line whose support range actually matches our host
(engines.dsh >=0.1.5-rc.1, 14 DSH peers ^0.1.5-rc.1, host = 0.1.5-rc.2), so it
is the only tag the host's startup precheck accepts.
What upstream v0.18.0 → v0.19.1 brings
- src/client/native/{index,surface,tab-adapter}.tsx: every tab the plugin can
draw becomes an OFFICIAL native tab type + body, and an external plugin's
descriptor registered later gets its native type too. This is what retires
the fork's own right panel — and with it the duplicate toggle cluster
(`toggleCluster` is gone from v0.19.1).
- native tab-chip icons via the `sidebar.right.pane.tab.title` seat, coloured
built-in tab icons, file/folder glyphs from DSH's own `FileTypeIcon`.
- guide descriptions (DSH renders them while the guide lists <= 4 entries),
tab bodies that fill the panel, and the 0.1.5-rc.2 adaptation work we were
carrying by hand (peer/devDep pinning, primitives' transitive devDeps).
How the sync was done
Rebuilt the tree from the v0.19.1 tag rather than resolving 53 conflicted
files: our v0.18.0 delta was measured first and is only (a) one mechanical
rename `dsh-better-sidebar` -> `dsh-apes-sidebar` and (b) ten real edits. So:
tag tree + rename (scoped: code/config/scripts/tests/README, while the 46
historical docs/plans records and switch.sh's upstream package name stay
untouched) + re-applied deltas + our apes module.
Fork deltas re-applied on the new base
- src/client/apes/ (icons / locales / tabs): the three apes:* information-layer
tabs (agents / apps / profile), registered through the same BetterSidebarService
as the builtins — which is exactly why they now ride upstream's native adapter
into the official sidebar for free.
- identity: package.json (name/version 1.2.0/description/forkedFrom), dsh.plugin.json
(id/version), SIDEBAR_SERVICE_VERSION 1.2.0, README fork notice,
docs/external-plugin-guide.md.
- scripts/switch.sh + the pnpm switch* scripts (apes <-> upstream two-line switch).
- tests/apes-tabs.spec.ts (unescaped: it starts from the v0.19.1 base).
Dropped
- the Phase A native-registration spike (`apes/native-tabs.tsx` + its spec): it
re-derived by hand exactly what upstream's native layer already does — the
hand-written bridge is dead weight now, and v0.19.1's own e2e/jsdom lanes
cover the same seat contract far better.
- the unused apesProfileGuideDesc key and the two package.json.bak.* backups.
Verified on the new base: typecheck clean; build clean; 1351 passed / 9 skipped
(128 files), up from 1255 on the old base.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Consumer-facing type surface check: build a tiny standalone consumer that
|
||||
# imports `dsh-better-sidebar/client/service` WITHOUT @types/node and with
|
||||
# imports `dsh-apes-sidebar/client/service` WITHOUT @types/node and with
|
||||
# skipLibCheck: false, then type-check it with the repo's tsc.
|
||||
#
|
||||
# This is the exact scenario the v0.12.0 API work cares about: the shipped
|
||||
@@ -35,14 +35,14 @@ mkdir -p "$WORK/node_modules"
|
||||
# (GetFinalPathNameByHandle on Windows) expands junctions, which bash-level
|
||||
# resolution may leave untouched; node is guaranteed here (tsc runs below).
|
||||
resolve_dir() { node -e 'process.stdout.write(require("node:fs").realpathSync.native(process.argv[1]))' "$1"; }
|
||||
ln -s "$(resolve_dir "$(pwd)")" "$WORK/node_modules/dsh-better-sidebar"
|
||||
ln -s "$(resolve_dir "$(pwd)")" "$WORK/node_modules/dsh-apes-sidebar"
|
||||
# The vendored cordis base (and, through its realpath, cosmokit / standard-schema)
|
||||
# must resolve so the consumer can type `Context` against the DSH runtime scope.
|
||||
mkdir -p "$WORK/node_modules/@deepseek-ai"
|
||||
ln -s "$(resolve_dir "$(pwd)/node_modules/@deepseek-ai/cordis")" "$WORK/node_modules/@deepseek-ai/cordis"
|
||||
# Loud guard: both links must resolve to a package root, or tsc below would
|
||||
# report misleading module-resolution errors.
|
||||
for pkg_json in "$WORK/node_modules/dsh-better-sidebar/package.json" "$WORK/node_modules/@deepseek-ai/cordis/package.json"; do
|
||||
for pkg_json in "$WORK/node_modules/dsh-apes-sidebar/package.json" "$WORK/node_modules/@deepseek-ai/cordis/package.json"; do
|
||||
if [ ! -f "$pkg_json" ]; then
|
||||
echo "[check-consumer-types] FAIL: $pkg_json does not resolve (broken link). Link layer:" >&2
|
||||
ls -la "$WORK/node_modules" "$WORK/node_modules/@deepseek-ai" >&2 || true
|
||||
@@ -51,14 +51,14 @@ for pkg_json in "$WORK/node_modules/dsh-better-sidebar/package.json" "$WORK/node
|
||||
done
|
||||
|
||||
cat > "$WORK/check.ts" <<'EOF'
|
||||
import { SIDEBAR_FEATURES, SIDEBAR_SERVICE_VERSION } from 'dsh-better-sidebar/client/service'
|
||||
import type {} from 'dsh-better-sidebar/client/service'
|
||||
import { SIDEBAR_FEATURES, SIDEBAR_SERVICE_VERSION } from 'dsh-apes-sidebar/client/service'
|
||||
import type {} from 'dsh-apes-sidebar/client/service'
|
||||
import type {
|
||||
BetterSidebarService, FileViewerDescriptor, OpenTabSeed, SidebarSettingsRenderProps,
|
||||
TabComponentProps, TabDescriptor,
|
||||
} from 'dsh-better-sidebar/client/service'
|
||||
import type { SessionScope, SidebarSnapshot, SidebarState, SidebarStore, SidebarTab } from 'dsh-better-sidebar/client/service'
|
||||
import type { SidebarPrefs } from 'dsh-better-sidebar/client/service'
|
||||
} from 'dsh-apes-sidebar/client/service'
|
||||
import type { SessionScope, SidebarSnapshot, SidebarState, SidebarStore, SidebarTab } from 'dsh-apes-sidebar/client/service'
|
||||
import type { SidebarPrefs } from 'dsh-apes-sidebar/client/service'
|
||||
// The vendored-cordis path: `Context` from '@deepseek-ai/cordis' plus the
|
||||
// side-effect type import above must expose `ctx.betterSidebar` — the consumer
|
||||
// never needs to import this package's own Context type.
|
||||
@@ -126,14 +126,14 @@ set -e
|
||||
# Fail only on errors that mention OUR package / declarations / the fixture.
|
||||
# Upstream noise (vendored cordis / cosmokit declaration warnings under the
|
||||
# repo's pinned TS lib) lives anywhere under node_modules and is filtered.
|
||||
if grep -E "dsh-better-sidebar|lib/types|check\.ts" "$WORK/strict.log" | grep -vE "node_modules/[^ ]*(cordis|cosmokit)"; then
|
||||
echo "[check-consumer-types] FAIL: errors in the dsh-better-sidebar declaration surface:" >&2
|
||||
grep -E "dsh-better-sidebar|lib/types|check\.ts" "$WORK/strict.log" >&2
|
||||
if grep -E "dsh-apes-sidebar|lib/types|check\.ts" "$WORK/strict.log" | grep -vE "node_modules/[^ ]*(cordis|cosmokit)"; then
|
||||
echo "[check-consumer-types] FAIL: errors in the dsh-apes-sidebar declaration surface:" >&2
|
||||
grep -E "dsh-apes-sidebar|lib/types|check\.ts" "$WORK/strict.log" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$STATUS" -ne 0 ]; then
|
||||
echo "[check-consumer-types] pass 2 note: strict mode only reports upstream declaration noise:"
|
||||
grep -v "dsh-better-sidebar\|lib/types\|check\.ts" "$WORK/strict.log" | head -5 || true
|
||||
grep -v "dsh-apes-sidebar\|lib/types\|check\.ts" "$WORK/strict.log" | head -5 || true
|
||||
fi
|
||||
echo "[check-consumer-types] OK: the client/service declaration surface is node-free and self-contained."
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# dsh-better-sidebar 聚合双挂载冒烟(CI + 本地):
|
||||
# dsh-apes-sidebar 聚合双挂载冒烟(CI + 本地):
|
||||
#
|
||||
# 场景:一个聚合 bundle(tests/fixtures/aggregate-better-sidebar)先以独立
|
||||
# 条目 id 挂载 dsh-better-sidebar,插件自身的 bundle patch 后到 —— 旧行为
|
||||
# 条目 id 挂载 dsh-apes-sidebar,插件自身的 bundle patch 后到 —— 旧行为
|
||||
# 两个实例都注册 /sidebar/api,整个插件树启动失败(duplicate prefix
|
||||
# route);新行为插件行检测到已有启用中的同包名挂载后自动 disabled,由
|
||||
# 聚合行接管 sidebar。
|
||||
@@ -22,7 +22,7 @@
|
||||
# 环境变量(均可省略):
|
||||
# DSH_CMD dsh 命令;缺省 PATH 上的 `dsh`,回退 npx 拉官方包
|
||||
# (同 e2e-mount.sh)
|
||||
# TARBALL 插件 tarball;缺省仓库根 dsh-better-sidebar-*.tgz(须已 pack)
|
||||
# TARBALL 插件 tarball;缺省仓库根 dsh-apes-sidebar-*.tgz(须已 pack)
|
||||
# PORT 固定端口(默认 0 = OS 分配,从日志解析 URL)
|
||||
# DSH_HOME_BASE scratch 根目录(默认系统临时目录)。脚本始终在其下新建
|
||||
# 本调用拥有的独立子目录,只写入/删除该子目录;调用方提供
|
||||
|
||||
@@ -57,9 +57,9 @@ e2e_resolve_dsh_cmd() {
|
||||
# 过期产物上。未找到候选时返回非零,由调用方按各自文案 die。
|
||||
e2e_resolve_tarball() {
|
||||
if [ -z "${TARBALL:-}" ]; then
|
||||
TARBALL="$(ls -t "$ROOT"/dsh-better-sidebar-*.tgz 2>/dev/null | head -1 || true)"
|
||||
TARBALL="$(ls -t "$ROOT"/dsh-apes-sidebar-*.tgz 2>/dev/null | head -1 || true)"
|
||||
local count
|
||||
count="$(ls "$ROOT"/dsh-better-sidebar-*.tgz 2>/dev/null | wc -l | tr -d ' ' || true)"
|
||||
count="$(ls "$ROOT"/dsh-apes-sidebar-*.tgz 2>/dev/null | wc -l | tr -d ' ' || true)"
|
||||
[ "$count" -le 1 ] || warn "发现 $count 个 tarball,按 mtime 选用最新:$(basename "$TARBALL")(建议清理其余)"
|
||||
fi
|
||||
[ -n "$TARBALL" ] && [ -f "$TARBALL" ]
|
||||
@@ -134,7 +134,7 @@ allowBuilds:
|
||||
protobufjs: true
|
||||
|
||||
minimumReleaseAgeExclude:
|
||||
- dsh-better-sidebar
|
||||
- dsh-apes-sidebar
|
||||
- '@deepseek-ai/*'
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# dsh-better-sidebar 挂载冒烟编排(CI + 本地):
|
||||
# dsh-apes-sidebar 挂载冒烟编排(CI + 本地):
|
||||
#
|
||||
# 1. 用官方 CLI 把 npm 打包产物(tarball)真实挂载进一个全新 scratch
|
||||
# profile(`dsh plugin --profile web add file:<tarball>`,触发
|
||||
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# 环境变量(均可省略):
|
||||
# DSH_CMD dsh 命令;缺省 PATH 上的 `dsh`,回退 npx 拉官方包
|
||||
# TARBALL 插件 tarball;缺省仓库根 dsh-better-sidebar-*.tgz(须已 pack)
|
||||
# TARBALL 插件 tarball;缺省仓库根 dsh-apes-sidebar-*.tgz(须已 pack)
|
||||
# PORT 固定端口(默认 0 = OS 分配,从日志解析 URL)
|
||||
# DSH_HOME_BASE 覆盖 scratch 根目录(默认系统临时目录)。脚本始终在其下
|
||||
# 新建本调用拥有的独立子目录,只写入/删除该子目录;调用方
|
||||
@@ -44,7 +44,7 @@ e2e_require_cmd pnpm "dsh plugin 转发给 pnpm"
|
||||
|
||||
e2e_resolve_dsh_cmd
|
||||
|
||||
e2e_resolve_tarball || die "找不到 tarball(TARBALL 或 \$ROOT/dsh-better-sidebar-*.tgz)——先运行 pnpm build && pnpm pack"
|
||||
e2e_resolve_tarball || die "找不到 tarball(TARBALL 或 \$ROOT/dsh-apes-sidebar-*.tgz)——先运行 pnpm build && pnpm pack"
|
||||
TARBALL="$(cd "$(dirname "$TARBALL")" && pwd)/$(basename "$TARBALL")"
|
||||
say "tarball: $TARBALL"
|
||||
|
||||
@@ -72,18 +72,18 @@ e2e_write_profile "$PROFILE_DIR"
|
||||
say "执行 dsh plugin --profile web add file:$TARBALL ..."
|
||||
$DSH_CMD plugin --profile web add "file:$TARBALL"
|
||||
|
||||
# 步骤 3:校验挂载生效(dsh.profile.bundles 含 dsh-better-sidebar)
|
||||
# 步骤 3:校验挂载生效(dsh.profile.bundles 含 dsh-apes-sidebar)
|
||||
if ! node -e '
|
||||
const fs = require("fs");
|
||||
const p = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||||
const bundles = p.dsh?.profile?.bundles ?? [];
|
||||
process.exit(bundles.includes("dsh-better-sidebar") ? 0 : 1);
|
||||
process.exit(bundles.includes("dsh-apes-sidebar") ? 0 : 1);
|
||||
' "$PROFILE_DIR/package.json"; then
|
||||
warn "dsh-better-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册"
|
||||
warn "dsh-apes-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册"
|
||||
cat "$PROFILE_DIR/package.json"
|
||||
exit 1
|
||||
fi
|
||||
say "挂载已注册:dsh.profile.bundles 包含 dsh-better-sidebar"
|
||||
say "挂载已注册:dsh.profile.bundles 包含 dsh-apes-sidebar"
|
||||
|
||||
# 步骤 4:启动 dsh web(--port 0 = OS 分配,避免端口冲突;keyless 可起)
|
||||
say "启动 dsh web(port=${PORT})..."
|
||||
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
## =============================================================================
|
||||
# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,Windows PowerShell 5.1+ / pwsh)
|
||||
# dsh-apes-sidebar 一键安装脚本(官方 CLI 方式,Windows PowerShell 5.1+ / pwsh)
|
||||
#
|
||||
# 通过 DSH 官方插件命令安装 npm 包并自动挂载:
|
||||
# dsh plugin --profile web add dsh-better-sidebar@<version>
|
||||
# dsh plugin --profile web add dsh-apes-sidebar@<version>
|
||||
#
|
||||
# 包内声明了 dsh.bundle.patch(cordis.patch.yml):CLI 的 bundle 协调会把它
|
||||
# 自动加进 profile 的 dsh.profile.bundles,下次启动即挂载——无需手动写
|
||||
@@ -52,7 +52,7 @@ param(
|
||||
[string]$Profile = 'web'
|
||||
)
|
||||
|
||||
$PKG = 'dsh-better-sidebar'
|
||||
$PKG = 'dsh-apes-sidebar'
|
||||
$REGISTRY = if ($env:REGISTRY) { $env:REGISTRY } else { 'https://registry.npmjs.org' }
|
||||
|
||||
# DSH_HOME:DSH_HOME 环境变量 > %USERPROFILE% > $HOME
|
||||
@@ -116,11 +116,11 @@ if (!/^\s*allowBuilds:\s*$/m.test(t)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) {
|
||||
if (!/^\s*-\s+dsh-apes-sidebar\s*$/m.test(t)) {
|
||||
if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) {
|
||||
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar");
|
||||
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-apes-sidebar");
|
||||
} else {
|
||||
t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n";
|
||||
t += "\nminimumReleaseAgeExclude:\n - dsh-apes-sidebar\n";
|
||||
}
|
||||
}
|
||||
if (t !== before) fs.writeFileSync(p, t);
|
||||
@@ -233,7 +233,7 @@ if ($addCode -ne 0) {
|
||||
$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 'dsh-apes-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册。'
|
||||
Warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。"
|
||||
exit 1
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# dsh-better-sidebar 一键安装脚本(官方 CLI 方式,macOS / Linux / Windows Git Bash)
|
||||
# dsh-apes-sidebar 一键安装脚本(官方 CLI 方式,macOS / Linux / Windows Git Bash)
|
||||
#
|
||||
# 通过 DSH 官方插件命令安装 npm 包并自动挂载:
|
||||
# dsh plugin --profile web add dsh-better-sidebar@<version>
|
||||
# dsh plugin --profile web add dsh-apes-sidebar@<version>
|
||||
#
|
||||
# 包内声明了 dsh.bundle.patch(cordis.patch.yml):CLI 的 bundle 协调会把它
|
||||
# 自动加进 profile 的 dsh.profile.bundles,下次启动即挂载——无需手动写
|
||||
@@ -38,8 +38,8 @@
|
||||
# minimumReleaseAgeExclude(幂等),放行本插件,避免"重跑一次才成功"。
|
||||
# - 老版本(<0.10.2)用手动挂载行,bundle 通道激活后需移除,否则双挂载
|
||||
# (Node 半挂两次、页面两个侧边栏)。脚本会幂等移除 better-sidebar 挂载行。
|
||||
# - 回滚:dsh plugin --profile web remove dsh-better-sidebar,或把 profile 依赖
|
||||
# 改回 "dsh-better-sidebar": "link:<路径>" 再 pnpm install。
|
||||
# - 回滚:dsh plugin --profile web remove dsh-apes-sidebar,或把 profile 依赖
|
||||
# 改回 "dsh-apes-sidebar": "link:<路径>" 再 pnpm install。
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
@@ -47,7 +47,7 @@ set -euo pipefail
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then
|
||||
cat <<'EOF'
|
||||
dsh-better-sidebar 一键安装 / 依赖修复脚本
|
||||
dsh-apes-sidebar 一键安装 / 依赖修复脚本
|
||||
|
||||
用法:
|
||||
bash scripts/install.sh [版本] [--restart] [--dry-run] [--profile <名>]
|
||||
@@ -67,7 +67,7 @@ done
|
||||
|
||||
DSH_HOME="${DSH_HOME:-${HOME:-${USERPROFILE:-}}/.dsh}"
|
||||
REGISTRY="${REGISTRY:-https://registry.npmjs.org}"
|
||||
PKG="dsh-better-sidebar"
|
||||
PKG="dsh-apes-sidebar"
|
||||
DSH_CMD="${DSH_CMD:-dsh}"
|
||||
|
||||
RESTART=false
|
||||
@@ -118,11 +118,11 @@ if (!/^\s*allowBuilds:\s*$/m.test(t)) {
|
||||
}
|
||||
}
|
||||
// minimumReleaseAgeExclude:放行本插件(版本无关),避免 <24h 新版本被拒
|
||||
if (!/^\s*-\s+dsh-better-sidebar\s*$/m.test(t)) {
|
||||
if (!/^\s*-\s+dsh-apes-sidebar\s*$/m.test(t)) {
|
||||
if (/^\s*minimumReleaseAgeExclude:\s*$/m.test(t)) {
|
||||
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-better-sidebar");
|
||||
t = t.replace(/^(\s*minimumReleaseAgeExclude:\s*)$/m, "$1\n - dsh-apes-sidebar");
|
||||
} else {
|
||||
t += "\nminimumReleaseAgeExclude:\n - dsh-better-sidebar\n";
|
||||
t += "\nminimumReleaseAgeExclude:\n - dsh-apes-sidebar\n";
|
||||
}
|
||||
}
|
||||
if (t !== before) fs.writeFileSync(p, t);
|
||||
@@ -224,7 +224,7 @@ if ! node -e '
|
||||
const bundles = p.dsh?.profile?.bundles ?? [];
|
||||
process.exit(bundles.includes(process.argv[2]) ? 0 : 1);
|
||||
' "$PROFILE_DIR/package.json" "$PKG"; then
|
||||
warn "dsh-better-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册。"
|
||||
warn "dsh-apes-sidebar 未出现在 dsh.profile.bundles 中——挂载未注册。"
|
||||
warn "若上面的 pnpm 输出提示 ignored build scripts,请确认 $WS_YML 的 allowBuilds 后重跑本脚本。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* pnpm build
|
||||
* node scripts/package-registry.mjs
|
||||
* dsh registry install ./registry
|
||||
* dsh registry enable dsh-external/dsh-better-sidebar
|
||||
* dsh registry enable dsh-external/dsh-apes-sidebar
|
||||
*
|
||||
* The registry install copies the whole source directory (`cp -r`), so
|
||||
* installing the repo root would drag in `node_modules/` (machine-local
|
||||
@@ -54,4 +54,4 @@ for (const file of files) {
|
||||
cpSync(source, target)
|
||||
}
|
||||
console.log(`registry/ assembled (${files.length} files)`)
|
||||
console.log('next: dsh registry install ./registry && dsh registry enable dsh-external/dsh-better-sidebar')
|
||||
console.log('next: dsh registry install ./registry && dsh registry enable dsh-external/dsh-apes-sidebar')
|
||||
|
||||
Executable
+294
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# dsh-apes-sidebar 两线切换脚本(apes fork 线 ↔ upstream better-sidebar 线)
|
||||
#
|
||||
# 同一个 DSH profile 内**二选一**切换侧边栏插件——双装不可行(两条线都注册
|
||||
# 进 `ctx.betterSidebar`,cordis entry id 冲突,第二个被丢弃)。切换 =
|
||||
# 换 profile 依赖 + 换 dsh.profile.bundles + pnpm install + 提示重启。
|
||||
#
|
||||
# aps 本 fork 线(dsh-apes-sidebar,含 4 个 apes:* 信息层 tab)
|
||||
# 依赖:dsh-apes-sidebar: <file: 本地路径>(本地开发)或
|
||||
# dsh-apes-sidebar@<版本>(npm,发布后)
|
||||
# upstream upstream 线(dsh-better-sidebar,官方 8 tab + 生态)
|
||||
# 依赖:dsh-better-sidebar@<版本>(registry,默认 latest)
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/switch.sh aps # 切到本 fork 线(本地 file: 安装)
|
||||
# bash scripts/switch.sh upstream # 切到 upstream 线(registry 安装)
|
||||
# bash scripts/switch.sh upstream 0.18.0 # 指定 upstream 版本
|
||||
# bash scripts/switch.sh --status # 只看当前线,不改任何东西
|
||||
#
|
||||
# 通用 flag(两线均适用):
|
||||
# --profile <名> 目标 profile(缺省 desktop)
|
||||
# --dry-run 只打印将执行的操作,不写任何文件
|
||||
# -h/--help 打印本帮助
|
||||
#
|
||||
# 环境(均可省略,脚本自动探测):
|
||||
# DSH_HOME 默认 ~/.dsh
|
||||
# REGISTRY 默认 https://registry.npmjs.org
|
||||
# DSH_CMD 默认 PATH 上的 dsh,缺省 npx -y --package @deepseek-ai/dsh
|
||||
# APS_SRC aps 线本地源码路径(缺省 = 本仓库根,即 file: 指向自己)
|
||||
#
|
||||
# 机制(幂等,可重复跑):
|
||||
# 1. 探测当前线(依赖里哪个包在)
|
||||
# 2. 与目标线相同 → 提示后直接退出(不重装)
|
||||
# 3. 移除另一线的依赖 + 从 bundles 摘除另一线的 bundle
|
||||
# 4. 写入目标线依赖 + 确保 workspace 放行 node-pty/protobufjs 构建
|
||||
# 5. cd profile && pnpm install(reconcileProfilePnpmWorkspace 会重建
|
||||
# node_modules——这是预期行为,两线不共存)
|
||||
# 6. 确保 bundles 含目标线包名(bundle 通道挂载生效的判据)
|
||||
# 7. 幂等移除 cordis.patch.yml 里对手线的手动挂载行(避免双挂载)
|
||||
# 8. 提示重启 DSH
|
||||
#
|
||||
# 回滚:跑 `bash scripts/switch.sh <另一线>` 即可切回去。
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then
|
||||
cat <<'EOF'
|
||||
dsh-apes-sidebar 两线切换(同一 profile 内二选一)
|
||||
|
||||
用法:
|
||||
bash scripts/switch.sh <aps|upstream> [版本]
|
||||
bash scripts/switch.sh --status
|
||||
|
||||
aps 切到本 fork 线(dsh-apes-sidebar,4 个 apes:* tab)
|
||||
upstream 切到 upstream 线(dsh-better-sidebar,官方)
|
||||
<版本> 仅对 upstream 生效(缺省 latest)
|
||||
--status 只看当前线,不改动
|
||||
--profile 目标 profile(缺省 desktop)
|
||||
--dry-run 只打印操作,不写文件
|
||||
-h/--help 本帮助
|
||||
|
||||
环境变量:DSH_HOME、REGISTRY、DSH_CMD、APS_SRC(aps 本地源码路径)
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
DSH_HOME="${DSH_HOME:-${HOME:-${USERPROFILE:-}}/.dsh}"
|
||||
REGISTRY="${REGISTRY:-https://registry.npmjs.org}"
|
||||
DSH_CMD="${DSH_CMD:-dsh}"
|
||||
# aps 线本地源码:缺省 = 本仓库根(脚本在 scripts/ 下,根 = 上一级)
|
||||
APS_SRC="${APS_SRC:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
|
||||
APS_PKG="dsh-apes-sidebar"
|
||||
UP_PKG="dsh-better-sidebar"
|
||||
|
||||
DRY_RUN=false
|
||||
STATUS_ONLY=false
|
||||
TARGET=""
|
||||
UP_VERSION=""
|
||||
PROFILE_NAME="desktop"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--status) STATUS_ONLY=true ;;
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--profile)
|
||||
if [ $# -lt 2 ]; then echo "--profile 需要一个 profile 名(如 desktop)" >&2; exit 2; fi
|
||||
PROFILE_NAME="$2"; shift ;;
|
||||
aps|upstream) TARGET="$1" ;;
|
||||
-h|--help) : ;;
|
||||
-*) echo "未知参数: ${1}(用 -h 查看用法)" >&2; exit 2 ;;
|
||||
*) UP_VERSION="$1" ;; # 版本参数(仅 upstream 用)
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
PROFILE_DIR="$DSH_HOME/profiles/$PROFILE_NAME"
|
||||
PKG_JSON="$PROFILE_DIR/package.json"
|
||||
WS_YML="$PROFILE_DIR/pnpm-workspace.yaml"
|
||||
PATCH_YML="$PROFILE_DIR/cordis.patch.yml"
|
||||
|
||||
say() { printf '\033[32m[switch]\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; }
|
||||
|
||||
command -v node >/dev/null 2>&1 || die "未找到 node(DSH 运行需要 Node.js ≥ 20)。"
|
||||
[ -d "$PROFILE_DIR" ] || die "找不到 profile 目录:${PROFILE_DIR}(请先初始化 ${PROFILE_NAME} profile)"
|
||||
[ -f "$PKG_JSON" ] || die "找不到 ${PKG_JSON}(profile 未初始化)"
|
||||
|
||||
# ── 探测当前线(依赖里哪个包在)─────────────────────────────────────────
|
||||
current_line() {
|
||||
node -e '
|
||||
const p = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
|
||||
const deps = p.dependencies || {};
|
||||
if (deps["dsh-apes-sidebar"]) console.log("aps");
|
||||
else if (deps["dsh-better-sidebar"]) console.log("upstream");
|
||||
else console.log("none");
|
||||
' "$PKG_JSON"
|
||||
}
|
||||
|
||||
if [ "$STATUS_ONLY" = true ]; then
|
||||
CUR="$(current_line)"
|
||||
say "profile: ${PROFILE_NAME} (${PROFILE_DIR})"
|
||||
case "$CUR" in
|
||||
aps) say "当前线:aps(${APS_PKG})" ;;
|
||||
upstream) say "当前线:upstream(${UP_PKG})" ;;
|
||||
none) say "当前线:无(两条线都未装)" ;;
|
||||
esac
|
||||
# 附带显示 bundles 里的 sidebar 条目
|
||||
node -e '
|
||||
const p = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
|
||||
const b = (p.dsh && p.dsh.profile && p.dsh.profile.bundles) || [];
|
||||
const side = b.filter(x => /sidebar/.test(x));
|
||||
console.log("bundles 里的 sidebar 条目: " + (side.length ? side.join(", ") : "(无)"));
|
||||
' "$PKG_JSON"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -n "$TARGET" ] || die "缺少目标线(aps | upstream)。用 --status 查看当前线。"
|
||||
|
||||
CUR="$(current_line)"
|
||||
case "$TARGET" in
|
||||
aps) T_PKG="$APS_PKG"; T_DEP="file:${APS_SRC}" ;;
|
||||
upstream)
|
||||
T_PKG="$UP_PKG"
|
||||
# pnpm 从依赖 key 取包名,值只给版本 spec(不含包名,否则双 @)
|
||||
case "${UP_VERSION:-latest}" in
|
||||
latest) T_DEP="latest" ;;
|
||||
*) T_DEP="$UP_VERSION" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
[ "$CUR" = "$TARGET" ] && {
|
||||
say "当前已是 ${TARGET} 线(${T_PKG}),无需切换。"
|
||||
say "如需强制重装:先跑 --status 确认,再手动移除依赖后重跑。"
|
||||
exit 0
|
||||
}
|
||||
|
||||
OTHER_PKG=""
|
||||
case "$TARGET" in
|
||||
aps) OTHER_PKG="$UP_PKG" ;;
|
||||
upstream) OTHER_PKG="$APS_PKG" ;;
|
||||
esac
|
||||
|
||||
say "切换:${CUR} → ${TARGET}(profile: ${PROFILE_NAME})"
|
||||
say "目标依赖:${T_DEP}"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
say "[dry-run] 步骤 1:移除 ${OTHER_PKG:-(无)} 依赖 + 从 bundles 摘除 ${OTHER_PKG:-(无)}"
|
||||
say "[dry-run] 步骤 2:写入 ${T_PKG} 依赖(${T_DEP})"
|
||||
say "[dry-run] 步骤 3:确保 ${WS_YML} 放行 node-pty/protobufjs 构建"
|
||||
say "[dry-run] 步骤 4:cd ${PROFILE_DIR} && pnpm install(重建 node_modules)"
|
||||
say "[dry-run] 步骤 5:确保 bundles 含 ${T_PKG}"
|
||||
say "[dry-run] 步骤 6:幂等移除 ${PATCH_YML} 里 ${OTHER_PKG:-对手线} 的手动挂载行"
|
||||
say "[dry-run] 步骤 7:提示重启 DSH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── 步骤 1+2+5 合并:一次 node 调用完成依赖 + bundles 的对称改写 ─────────
|
||||
# 移除对手线依赖 + bundle,写入目标线依赖 + 确保目标 bundle 在列。幂等。
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const p = process.argv[1];
|
||||
const keep = process.argv[2]; // 目标包名
|
||||
const depSpec = process.argv[3]; // 目标依赖 spec(file:... 或 pkg@ver)
|
||||
const remove = process.argv[4]; // 对手包名(可能为空串)
|
||||
const t = JSON.parse(fs.readFileSync(p, "utf8"));
|
||||
t.dependencies = t.dependencies || {};
|
||||
// 移除对手线依赖
|
||||
if (remove) { const had = t.dependencies[remove]; delete t.dependencies[remove]; if (had) console.log("removed dep: " + remove + "=" + had); }
|
||||
// 写入目标线依赖
|
||||
t.dependencies[keep] = depSpec;
|
||||
// bundles:摘除对手线,确保目标线在列
|
||||
const prof = (t.dsh && t.dsh.profile) || {};
|
||||
const b = prof.bundles || [];
|
||||
const nb = b.filter(x => x !== remove);
|
||||
if (!nb.includes(keep)) nb.push(keep);
|
||||
t.dsh = t.dsh || {}; t.dsh.profile = prof; prof.bundles = nb;
|
||||
fs.writeFileSync(p, JSON.stringify(t, null, 2) + "\n");
|
||||
console.log("deps now: " + JSON.stringify(t.dependencies));
|
||||
console.log("bundles now: " + nb.join(", "));
|
||||
' "$PKG_JSON" "$T_PKG" "$T_DEP" "$OTHER_PKG"
|
||||
say "依赖 + bundles 已改写"
|
||||
|
||||
# ── 步骤 3:确保 workspace 放行 node-pty/protobufjs 构建(幂等)─────────
|
||||
if [ -f "$WS_YML" ]; then
|
||||
WS_RESULT="$(node -e '
|
||||
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 (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)" \
|
||||
|| say "workspace 设置已就绪,跳过"
|
||||
else
|
||||
warn "找不到 ${WS_YML},跳过 workspace 预写(若 pnpm install 报 ignored build scripts 需手动补)"
|
||||
fi
|
||||
|
||||
# ── 步骤 4:pnpm install(重建 node_modules——两线不共存,这是预期)──────
|
||||
command -v pnpm >/dev/null 2>&1 || die "未找到 pnpm。请安装 pnpm 9+ 并加入 PATH。"
|
||||
say "执行 cd ${PROFILE_DIR} && pnpm install(reconcileProfilePnpmWorkspace 会重建 node_modules)..."
|
||||
if ! (cd "$PROFILE_DIR" && pnpm install); then
|
||||
die "pnpm install 失败。请确认网络可用、依赖 spec 正确,然后重试。"
|
||||
fi
|
||||
|
||||
# ── 步骤 5 校验:bundles 含目标包(挂载生效判据)────────────────────────
|
||||
if ! node -e '
|
||||
const p = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
|
||||
const b = (p.dsh && p.dsh.profile && p.dsh.profile.bundles) || [];
|
||||
process.exit(b.includes(process.argv[2]) ? 0 : 1);
|
||||
' "$PKG_JSON" "$T_PKG"; then
|
||||
warn "${T_PKG} 未出现在 dsh.profile.bundles——挂载未注册,请检查 ${PKG_JSON}。"
|
||||
exit 1
|
||||
fi
|
||||
say "bundle 已注册:dsh.profile.bundles 含 ${T_PKG}(下次启动自动挂载)"
|
||||
|
||||
# ── 步骤 6:幂等移除对手线在 cordis.patch.yml 里的手动挂载行 ─────────────
|
||||
if [ -f "$PATCH_YML" ]; then
|
||||
MOUNT_RESULT="$(node -e '
|
||||
const fs = require("fs");
|
||||
const p = process.argv[1];
|
||||
const removePkg = process.argv[2];
|
||||
if (!removePkg) { console.log("none"); process.exit(0); }
|
||||
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) => new RegExp("id:\\s*" + removePkg.replace(/[-/]/g, "\\$&") + "\\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" "$OTHER_PKG")"
|
||||
[ "$MOUNT_RESULT" = "removed" ] && say "已从 ${PATCH_YML} 移除 ${OTHER_PKG} 的手动挂载行" \
|
||||
|| say "无 ${OTHER_PKG} 手动挂载行,跳过"
|
||||
else
|
||||
say "无 ${PATCH_YML},跳过手动挂载行清理"
|
||||
fi
|
||||
|
||||
# ── 步骤 7:提示重启 ────────────────────────────────────────────────────
|
||||
say "切换完成:${CUR} → ${TARGET}(${T_PKG})"
|
||||
say "下一步:重启 DSH 并硬刷新(Cmd/Ctrl+Shift+R)使新副本生效。"
|
||||
Reference in New Issue
Block a user