dsh-long-plugins 1.3.1

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.
@@ -0,0 +1,200 @@
1
+ ---
2
+ name: dsh-long-plugins-install
3
+ description: 在一台新装 DSH 的机器上自动安装并配置 dsh-long-plugins(上传文件/工作区输出文件/技能文档/DeepSeek 余额/md2docx 工具/文件预览增强)。自动探测 DSH_HOME 与 Node 环境、安装插件、写 cordis.patch.yml 配置、启动并验证;凡涉及远程 push 一律停下等用户确认。用户说"在另一台电脑安装 dsh-long-plugins"、"新机器部署插件"、"帮我装 dsh-long-plugins"时调用。
4
+ whenToUse: 用户要求在另一台/新机器上安装、部署或配置 dsh-long-plugins 插件,或把本机 DSH 插件环境迁移到新机器时调用。
5
+ ---
6
+
7
+ # dsh-long-plugins 新机器自动安装
8
+
9
+ 作为部署助手,在一台**已装好 DSH** 的机器上自动安装并配置 `dsh-long-plugins`(≥v1.2.6)。目标:让上传管理、工作区「输出文件」、技能文档、DeepSeek 余额、md2docx 工具、文件预览增强(md 渲染真实效果、文件类型标签、预览/删除/下载)都能用。**支持 NAS(Linux/Unix)与 Windows 安装,无需区分平台**。
10
+
11
+ > 本 skill 是**操作指引**,由你(agent)在目标机器上执行。真实 shell/文件权限来自该机器;凡**远程 push / 打 tag / 发 Release** 一律停下,等用户明确确认(硬性安全边界)。
12
+
13
+ ## 前置检查(先探测,再动手)
14
+
15
+ 执行前先探测环境,**不要假设本机路径**。逐项确认,缺了就停下让用户填:
16
+
17
+ ```
18
+ dsh --version # 是否装了 DSH;没有则提示先按官方文档装 DSH
19
+ echo "$DSH_HOME" # 未设则查 /volume1/dsh、$HOME/.dsh 常见位置
20
+ ls -d <候选>/profiles/web # 定位 profile 目录
21
+ node --version # 需 >=22(--expose-internals 依赖)
22
+ python3 -c "import docx" # 可选:md2docx 工具需要;缺则提示 pip install python-docx
23
+ which node ; ls <node套件>/bin/node # 找 node 真实路径;找不到让用户给
24
+ ```
25
+
26
+ - **node 路径**:本机用 `/volume1/@appstore/Node.js_v22/usr/local/bin/node`,但**不要写死**——优先 `command -v node`,找不到再探测套件目录,仍找不到就让用户提供。
27
+ - **DSH_HOME**:默认 `$HOME/.dsh`,但很多用户用 `/volume1/dsh`。以用户实际值为准。
28
+
29
+ ## 跨平台(Windows / NAS)说明
30
+
31
+ 插件**不做"安装时检测平台"**——它靠运行时自动适配,同一份代码在 NAS 和 Windows 都能工作,部署时**不需要区分平台、不用手动改配置**。机制分三层:
32
+
33
+ 1. **服务端(node)**:全程用 Node 跨平台 `path` API(`resolve`/`join`/`relative`/`dirname`/`sep`)。`sep` 在 NAS 是 `/`、在 Windows 是 `\`,Node 运行时自己知道在哪,无需判断。
34
+ 2. **前端(浏览器 JS)**:没有 `process.platform`。改用**按路径形状判断**,同时识别 Unix 与 Windows 两类绝对路径——`isAbsPath(p)` 匹配 `/` 开头(Unix)、`X:\` 盘符或 `\\` UNC(Windows);`normPath(p)` 把 `\` 与 `/` 统一成 `/`。所以不依赖"当前是哪个系统",同一份代码两平台通用(≥v1.2.6 已修复 Windows 下"工作区 0 文件 / 点击文件 500"两个路径 bug)。
35
+ 3. **安装**:纯 npm 包,安装机制 NAS/Windows 完全相同。**Windows 不跑 `install.sh`(那是 shell 脚本,仅 Unix-like)**,改用 DSH 标准方式:`dsh plugin --profile web add file:...`(DSH CLI 在 Windows 也能跑,会转发给 pnpm)。
36
+
37
+ ### Windows 与 NAS 的差异点
38
+ | 项 | NAS / Linux | Windows |
39
+ |---|---|---|
40
+ | 路径分隔符 | `/` | `\` |
41
+ | 安装方式 | `./install.sh web <DSH_HOME>` | `dsh plugin --profile web add file:<path>` |
42
+ | 启动命令 | `<DSH_HOME>/start.sh` 或 node + `--expose-internals` | 同样 `dsh web`(node 命令路径不同) |
43
+ | 无头 `xdg-open` | 无图形界面才需禁原生打开 | 桌面通常有 `xdg-open`(或系统默认浏览器),一般无需设 |
44
+ | python3 | `python3` + `python-docx`(md2docx 用) | 用 `python`(可能无 `python3` 别名),需 `pip install python-docx`;**`python3` 常是 Windows Store 的坏 stub**,插件 `spawn("python3")` 会命中它——需把真实 python 拷贝成 `python3.exe` 放进 `$DSH_HOME\bin` 并加入 DSH 服务的 PATH |
45
+
46
+ > 前端 `client.js`、服务端 `lib/index.js` 都无需按平台改;只需按上表用对命令即可。
47
+
48
+ ## 概念澄清(换机器最容易错的地方)
49
+
50
+ DSH 是"profile 层叠"结构,插件不是放进去就生效,要三处配合:
51
+
52
+ 1. **依赖**:`<DSH_HOME>/profiles/<profile>/package.json` 里加 `"dsh-long-plugins": "file:/path/to/dsh-long-plugins"`。
53
+ 2. **bundles**:同一 `package.json` 的 `dsh.profile.bundles` 数组里**追加** `"dsh-long-plugins"`(数组顺序=层叠顺序,追加到末尾)。
54
+ 3. **配置**:profile 的 `cordis.patch.yml` 里写 `trustedHosts`/`skillsRoot`(见下)。
55
+
56
+ - `cordis.yml` 是 `[]`(勿编辑),补丁一律写 **`cordis.patch.yml`**。
57
+ - `dsh plugin --profile <p> add <pkg>` 只是把参数转发给 pnpm,可以做"依赖注入";bundles 要单独加。
58
+
59
+ ## 目录约定(先用 ask_user_question 确认,再动手)
60
+
61
+ 部署前**先确认 DSH 的目录约定**,尤其 Windows 上曾因未确认导致插件/bin 错落到工作区 `C:\dsh\workspace`。用 `ask_user_question` 问用户,以下几点都要确认(DSH 已有约定/用户已指定则按其值):
62
+
63
+ 1. **插件目录** = `<DSH_HOME>/plugins`(插件源码放 `<DSH_HOME>/plugins/dsh-long-plugins`)——这也是**后续所有 DSH 插件的统一安装目录**。
64
+ 2. **bin 目录** = `<DSH_HOME>/bin`(放 `restart-dsh.ps1`、`python3.exe` 等 DSH 运行时脚本)。
65
+ 3. **若用户/DSH 尚未约定插件目录** → 必须停下来提示:默认用 `<DSH_HOME>/plugins`,并告知"以后安装的所有插件都放这里"。
66
+ 4. **工作目录**:用户的工作目录(名字不固定,如 `C:\dsh\workspace`、`C:\dsh\jobs`、`/home/me/project`),**由用户指定**;在其下(或用户指定位置)放 **`upload` 子目录**作上传根(`DSH_UPLOAD_DIR`)。不强制目录叫 `workspace`。
67
+
68
+ **主动创建目录(不依赖用户提前建好)**:确认后,用命令创建缺失的目录——
69
+ - `<DSH_HOME>/plugins`:插件安装目录,**缺失则创建**(`mkdir -p`);
70
+ - `<DSH_HOME>/bin`:DSH 运行时脚本目录,缺失则创建;
71
+ - **用户工作目录**(用户指定,已有则直接用它)及其下的 **`upload` 子目录**:确认/创建(`mkdir -p <工作目录>/upload`),并设为 `DSH_UPLOAD_DIR`。
72
+
73
+ > 原则:**插件和 bin 都放 `$DSH_HOME` 下(`$DSH_HOME/plugins`、`$DSH_HOME/bin`),不要放到用户工作目录/当前目录**。**用户工作目录**(如 `C:\dsh\workspace`)只放用户内容——其中 `upload` 子目录是上传根(`DSH_UPLOAD_DIR`),路径和目录名由用户指定并创建,不混放插件源码和 bin。**不强制创建名为 `workspace` 的目录**;用户已有工作目录就用它。若这些目录不存在,部署脚本必须 `mkdir -p` 补建,而不是报错或退到其它位置。
74
+
75
+ ## 安装步骤
76
+
77
+ > ⚠️ **安装方式:统一用本地 `file:` 链接**——插件源码放 `$DSH_HOME/plugins/dsh-long-plugins`,`file:` 引用它;更新靠手动 `git pull`。**不用 `github:` 依赖**(github: 会让插件失去本地 clone 目录、且依赖 `@deepseek-ai/dsh-tools` 解析更不稳)。注意 dshmarket 市场对 `file:`/`link:` 安装的插件**不会显示更新提示**(这是市场的硬编码行为,`updateAvailable:false`),如需更新提示请手动 `git pull`。
78
+
79
+ ### 1. 获取插件源码 → 放到 DSH 插件目录
80
+ **clone 到绝对目标目录**(不要只 `git clone` 落在当前目录);`$DSH_HOME/plugins` 不存在则先建:
81
+ ```sh
82
+ # Unix-like / PowerShell 通用(先确保插件目录与 bin 目录存在,缺失则创建)
83
+ mkdir -p "$DSH_HOME/plugins" "$DSH_HOME/bin"
84
+ git clone https://github.com/jackylong1987/dsh-long-plugins.git "$DSH_HOME/plugins/dsh-long-plugins"
85
+ # 已存在则更新
86
+ git -C "$DSH_HOME/plugins/dsh-long-plugins" pull --ff-only origin main
87
+ ```
88
+ > 这样插件源码一定在 `$DSH_HOME/plugins/dsh-long-plugins`,且 `<DSH_HOME>/plugins`、`<DSH_HOME>/bin` 都确保存在(缺失即创建),不随当前目录变化。
89
+
90
+ ### 2. 注入依赖 + 加 bundle
91
+ 用 `file:` 引用**绝对路径**(必用绝对路径,勿用相对路径 `./...`,否则会解析到当前目录/工作区)。
92
+ ```sh
93
+ # Unix-like(NAS/桌面 Linux):在 profile 目录执行
94
+ cd "$DSH_HOME/profiles/<profile>"
95
+ node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.dependencies=p.dependencies||{};p.dependencies['dsh-long-plugins']='file:'+'$DSH_HOME/plugins/dsh-long-plugins';p.dsh=p.dsh||{};p.dsh.profile=p.dsh.profile||{};p.dsh.profile.bundles=p.dsh.profile.bundles||[];if(!p.dsh.profile.bundles.includes('dsh-long-plugins'))p.dsh.profile.bundles.push('dsh-long-plugins');fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')"
96
+ ```
97
+ ```powershell
98
+ # Windows(PowerShell):也用绝对路径,<profile> 通常为 web
99
+ dsh plugin --profile web add "file:C:\Users\iprla\.dsh\plugins\dsh-long-plugins"
100
+ # 再手动把插件加入 bundles(dsh plugin add 只装依赖,不自动加 bundle):
101
+ ```
102
+ > 任何平台都要把 `dsh-long-plugins` 加进 `dsh.profile.bundles`。Windows 下 `dsh plugin add` 只装了依赖,bundle 需手动加(写入 profile 的 `package.json` 的 `dsh.profile.bundles`)。
103
+
104
+ ### 3. pnpm install
105
+ ```sh
106
+ cd "$DSH_HOME/profiles/<profile>"
107
+ pnpm install # 或用探测到的 pnpm 绝对路径
108
+ ```
109
+ > `@deepseek-ai/dsh-tools` 是插件的 `peerDependencies`(optional)——插件**复用宿主 DSH 的 dsh-tools**,不会自行安装(避免遮蔽宿主版本/重复)。宿主需已带该包(DSH 核心自带),不必额外安装。
110
+
111
+ ### 4. 写 cordis.patch.yml 配置
112
+ 在 `<profile>/cordis.patch.yml` 里追加(若文件不存在先建):
113
+ ```yaml
114
+ - id: dsh-long-plugins
115
+ config:
116
+ priority: -10
117
+ trustedHosts: !!js ctx.webRuntime.trustedHosts # 必填,浏览器信任域
118
+ skillsRoot: !!js dshHomePath('skills') # 默认 <DSH_HOME>/skills
119
+ # md2docxScript: /your/path/md2docx.py # 可选,默认用包内 lib/md2docx.py
120
+ ```
121
+
122
+ ## 工作目录 / 上传目录匹配(重要决策点)
123
+
124
+ 上传/工作区根**不是写死的**,靠环境变量推导:
125
+ ```
126
+ 上传目录 = $DSH_UPLOAD_DIR 或默认 <DSH_HOME>/uploads
127
+ 工作区根 = dirname(上传目录) (上传目录的上一级)
128
+ skill根 = dshHomePath('skills') = <DSH_HOME>/skills
129
+ ```
130
+
131
+ > ⚠️ **环境变量是进程启动那一刻定型的**。`DSH_UPLOAD_DIR` 决定「上传目录」→ `工作区根 = 它的上一级`。**改了 `DSH_UPLOAD_DIR`(或换用户工作目录)后,必须重启 dsh 进程**(从已经带上该变量的环境启动)才会生效。若 dsh 进程是在变量生效前启动的、或是裸敲 `node ...web` 起的,其进程环境里没有该变量,插件会回退到 `DSH_HOME`,导致「工作区输出文件」显示 `attachments / bin / plugins / profiles` 等 DSH 自带目录,而不是用户工作区。**判断是否生效的唯一可靠依据**:`GET http://127.0.0.1:3080/api/dsh-uploads/workspace` 返回的 `root`(不要看 agent/tool 进程的 `$env:DSH_UPLOAD_DIR`,那是不相干的独立进程)。
132
+
133
+ **工作目录的名字不固定**(可能叫 `workspace`、`jobs`、`projects` 或其它),完全由用户指定。**先用 `ask_user_question` 弹出提问,让用户选择「工作根目录」**(即输出文件/工作区根 = `DSH_UPLOAD_DIR` 的上一级)——给常见默认值 + 自定义,**别硬猜**:
134
+
135
+ > 弹出提问 `请选择「工作根目录」(输出文件/工作区根)`,选项(推荐项放最前):
136
+ > - `C:\dsh\workspace`(Windows 常用;已有则直接用,缺则 `mkdir -p` 补建)
137
+ > - `D:\dsh\workspace`(Windows 工作区在 D 盘)
138
+ > - `/volume1/dsh/workspace`(NAS/Linux 常用)
139
+ > - **自定义**(选此项后再次 `ask_user_question`,按用户输入的完整路径为准)
140
+
141
+ 拿到「工作根目录」后:**上传目录默认 = `<工作根目录>\upload`**(如需别处,再弹出提问让用户指定),把该值设为 `DSH_UPLOAD_DIR`;`工作区根` 自动 = 用户选的工作根目录,`<工作根目录>\upload` 缺失则 `mkdir -p` 补建。
142
+
143
+ ```sh
144
+ # 例:用户工作目录 = /home/me/project,upload 放在其下
145
+ mkdir -p "/home/me/project/upload"
146
+ export DSH_UPLOAD_DIR="/home/me/project/upload" # 工作区根自动 = /home/me/project
147
+ ```
148
+ - **不强制创建 `workspace` 目录**:若用户已有工作目录,就用它;仅在需要时才 `mkdir -p <工作目录>/upload`。
149
+ - 把 `export DSH_UPLOAD_DIR=...` 写进用户启动脚本(start.sh / restart-dsh.ps1 或等价物),这样工作区根自动 = 用户工作目录。
150
+ - **目录不存在则 `mkdir -p` 主动创建**(`<工作目录>/upload` 缺失则建),不要报错或退到其它位置。
151
+ - **确认上传目录可写**:`dsh` 服务进程需对 `DSH_UPLOAD_DIR` 有写权限(上传/预览/删除都要写),否则启动后上传会失败。
152
+
153
+ ## 无头机器(可选)
154
+ 若目标机器**无图形界面**(`xdg-open` 不存在),点交付物卡片会报 `spawn xdg-open ENOENT`。在 **profile 的 cordis.patch.yml 顶层**加:
155
+ ```yaml
156
+ - id: api-gateway
157
+ config:
158
+ nativeOpen: false
159
+ ```
160
+ > id 是 `api-gateway`(不是 `apiProxy`,用错报 "entry not found")。桌面/有 xdg-open 的机器可不加。这是**环境配置,不属于插件源码**,不随插件 commit。
161
+
162
+ ## 启动
163
+ ```sh
164
+ # 用探测到的 node 与 dsh 入口,务必保留 --expose-internals
165
+ setsid nohup <node绝对路径> --expose-internals --max-old-space-size=8192 \
166
+ <dsh>? 或 <DSH_HOME>/start.sh web ... &
167
+ ```
168
+ > `--expose-internals` **必须有**,否则插件相关路由 404。启动脚本以目标机器现有方式为准;本机参考:`/volume1/@appstore/Node.js_v22/usr/local/bin/node --expose-internals --max-old-space-size=8192 /volume1/npm/global/lib/node_modules/@deepseek-ai/dsh/lib/bin.js web --port 3080`。
169
+
170
+ ## 验证清单(逐项)
171
+ 1. 设置面板出现「上传文件」「输出文件」「技能文档」节。
172
+ 2. 上传/输出列表每行显示类型徽标(PDF/DOCX/MD…)。
173
+ 3. 预览窗有「打开/下载/放大/删除」;图片点预览是**内嵌显示**而非下载;**`.md` 预览显示渲染后 HTML**(标题/表格/列表等,非源码)——≥1.2.5。
174
+ 4. 消息正文文件引用(蓝色 chip/path)点击能内联预览。
175
+ 5. agent 调用 md2docx:`.md` → 带页码 `.docx`,产物作为可点击交付物卡片出现在消息里。
176
+ > **md2docx 工具需新建会话后 agent 才加载**(会话工具集创建时固定),验证时开新会话。
177
+ 6. 窄屏(手机)下预览窗按钮自动换行不截断。
178
+ 7. 用 `dsh --profile <p> --dump-config` 核对组合树正确。
179
+
180
+ ## 硬性安全边界(必须遵守)
181
+ - **不主动 `git push` / 打 tag / 发 Release**;需要发版本时停下用 `ask_user_question` 等用户确认。
182
+ - 所有写操作(改 package.json / cordis.patch.yml / 重启服务)先向用户说明将改哪个文件、做什么,再执行。
183
+ - 探测到 `DSH_HOME`/node/uplaod 目录缺失时**停下提示**,让用户补充,不硬猜。
184
+
185
+ ## 常见坑速查
186
+ | 症状 | 原因 | 解决 |
187
+ |---|---|---|
188
+ | 插件/bin/`file:` 引用错落到工作区(如 Windows 下 `C:\dsh\workspace\...`) | clone 时落在当前目录、或 `file:` 用了相对路径,而非 DSH 插件目录 `$DSH_HOME/plugins/dsh-long-plugins` | 把插件源码放到 `$DSH_HOME/plugins/dsh-long-plugins`,`file:` 用绝对路径指向它;修复后再跑 `pnpm install` |
189
+ | Windows 插件 `git clone` 连 github.com 超时/被墙 | 网络不通 github.com(但 `raw.githubusercontent.com`/`codeload` 常可达) | 用 codeload tarball 下载后解压:`Invoke-WebRequest https://codeload.github.com/jackylong1987/dsh-long-plugins/tar.gz/refs/heads/main -OutFile p.tgz` + `tar -xzf`,把内层目录移为 `$DSH_HOME/plugins/dsh-long-plugins` |
190
+ | Windows 下 md2docx 报 python3 找不到/异常 | `python3` 命中 Windows Store 的坏 stub(`C:\Users\...\WindowsApps\python3.exe`,不返回输出) | 把真实 python 拷贝为 `$DSH_HOME\bin\python3.exe`(+ `python3.dll`)放 PATH 前部;用 `python3 -c "import docx"` 验证 |
191
+ | Windows 里 `bin`/`restart-dsh.ps1` 落在工作区 | 部署时用了相对路径、或把 bin 建在了当前目录 | `bin`(含 `restart-dsh.ps1`、`python3.exe`)应放 `$DSH_HOME\bin`,不要让它们落到工作区 `C:\dsh\workspace` |
192
+ | 重启 DSH web 时把当前会话杀掉 | DSH 服务进程(如 PID 20360 on :3080)正宿主 agent 会话 | 重启脚本由用户手动执行(脚本会先停旧进程再启新进程带 `--expose-internals`);agent 不自重启 |
193
+ | 设置面板无「上传文件」 | 插件未加 bundle | 步骤 2 加 `dsh.profile.bundles` |
194
+ | 插件路由 404 | 缺 `--expose-internals` | 启动命令加该 flag |
195
+ | 文件卡片报 `xdg-open ENOENT` | 无头无 xdg-open | 加 `api-gateway nativeOpen:false` |
196
+ | 图片预览变下载 | 旧版缺图片扩展名 | 升级到 ≥1.2.3 |
197
+ | md2docx 调用失败 | 无 python3/python-docx | `pip install python-docx` 或设 `md2docxScript` |
198
+ | agent 找不到 md2docx | 会话工具集固定 | 新建会话 |
199
+ | 版本号显示旧 | 运行副本版本文件未同步 | 同步 package.json/dsh.plugin.json 到 node_modules 副本 + 重启 |
200
+ | 「工作区输出文件」显示 `attachments/bin/plugins/profiles`(= DSH_HOME)而非用户工作区 | 设了 `DSH_UPLOAD_DIR`,但正在服务的 dsh 进程是旧环境启动的(启动时间在变量生效前,或裸敲 `node ...web`);进程环境在创建时定格,不会自动刷新 | 用「带上变量」的方式重启 dsh:从已设 `DSH_UPLOAD_DIR` 的终端重跑启动命令(含 `--expose-internals`)或面板「重启 dsh web」;随后 `GET http://127.0.0.1:3080/api/dsh-uploads/workspace` 确认 `root` 变为用户工作区 |
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: dsh-upgrade
3
+ description: 升级 DeepSeek Harness(DSH)后重新应用本地补丁,防止升级覆盖核心定制(如反代信任、WebSocket 心跳、插件 dsh-long-plugins)。自动升级核心、重打全部补丁(privileged 信任 / 反代 loopback / 心跳)、同步插件、重启并健康检查。用户说"升级 DSH"、"升级后重打补丁"、"dsh 升级了要重打"时调用。
4
+ whenToUse: 用户要求升级 DSH(@deepseek-ai/dsh)到最新/指定版本,或升级后发现补丁失效、设置 403、提问窗口消失、功能异常时调用。
5
+ ---
6
+
7
+ # DSH 升级后重打补丁
8
+
9
+ 作为升级助手,帮用户**升级 DSH 核心并重新应用本地补丁**。DSH 升级(`npm install -g @deepseek-ai/dsh`)会覆盖核心包的本地定制补丁,需重新应用,否则会出现:设置页 403、反向代理域名访问异常、提问/审批窗口自己消失等。
10
+
11
+ > 本 skill 是**操作指引**,由 agent 在目标机器执行。涉及**远程 push / 打 tag / 发 Release** 一律停下等用户确认。升级本身会重启 DSH 服务(可能中断会话),先向用户说明。
12
+
13
+ ## 前置检查(先探测,再动手)
14
+ ```
15
+ dsh --version # 当前版本
16
+ echo "$DSH_HOME" # DSH_HOME(默认 $HOME/.dsh;NAS 常用 /volume1/dsh)
17
+ ls <DSH_HOME>/bin/upgrade-dsh.sh # 是否有现成升级脚本
18
+ which npm ; npm --version # 升级用 npm -g
19
+ ls <DSH_HOME>/bin/apply-dsh-patches.sh # 是否有重打补丁脚本
20
+ ```
21
+
22
+ ## 推荐方式 A:用现成脚本一键升级(最简单)
23
+ 若目标机器有 `/volume1/dsh/bin/upgrade-dsh.sh`(或等价脚本),直接调它,一条命令完成 升级核心 + 重打补丁 + 同步插件 + 重启:
24
+ ```sh
25
+ /volume1/dsh/bin/upgrade-dsh.sh # 升级到最新
26
+ /volume1/dsh/bin/upgrade-dsh.sh 0.1.1-rc.2 # 升级到指定版本
27
+ ```
28
+ 脚本已内置步骤:备份配置 → 升级核心 → 重打补丁(apply-dsh-patches.sh,含补丁1/2/3)→ 同步 dsh-long-plugins 安装副本 → 重启 → 健康检查。
29
+
30
+ ## 手动方式 B(无脚本/要逐步做)
31
+ ### 1. 备份
32
+ ```sh
33
+ cp "$DSH_HOME/profiles/web/cordis.patch.yml" "$DSH_HOME/profiles/web/cordis.patch.yml.bak-$(date +%Y%m%d-%H%M)"
34
+ ```
35
+
36
+ ### 2. 升级 DSH 核心
37
+ ```sh
38
+ npm install -g --prefix /volume1/npm/global "@deepseek-ai/dsh@latest"
39
+ # 或指定版本:@deepseek-ai/dsh@0.1.1-rc.2
40
+ ```
41
+ > 若 DSH 装在别处,`--prefix` 换成 `npm root -g` 的父目录。
42
+
43
+ ### 3. 重打本地补丁
44
+ ```sh
45
+ /volume1/dsh/bin/apply-dsh-patches.sh
46
+ ```
47
+ 它幂等重打三处(已打过会跳过):
48
+ - 补丁1:privileged 方法接受 trustedHosts(修设置 403)
49
+ - 补丁2:反代域名视为 loopback(修设置页"未暴露命名空间")
50
+ - 补丁3:**WebSocket 心跳**(修反代下提问/审批窗口自己消失)
51
+
52
+ ### 4. 重打心跳补丁(若装 dsh-long-plugins 的 install.sh 带补丁脚本)
53
+ 插件安装时会自动打心跳补丁;若拿不到 apply-dsh-patches.sh,可单独跑插件自带脚本:
54
+ ```sh
55
+ sh <插件目录>/patches/dsh-client-connection-heartbeat.sh
56
+ ```
57
+
58
+ ### 5. 同步插件安装副本
59
+ ```sh
60
+ cd "$DSH_HOME/profiles/web" && pnpm install
61
+ ```
62
+
63
+ ### 6. 重启 dsh + 强刷浏览器
64
+ ```sh
65
+ /volume1/dsh/start.sh # 或按当前环境的重启方式
66
+ # 浏览器 Ctrl/Cmd+Shift+R 强刷
67
+ ```
68
+
69
+ ## 验证清单(逐项)
70
+ 1. `dsh --version` 为升级后的目标版本。
71
+ 2. 设置页各 tab 可进、不再 403/未暴露命名空间(重启后,用浏览器访问,非 raw 127.0.0.1)。
72
+ 3. 反代域名访问正常。
73
+ 4. 提问/审批窗口:弹出一个问题,等 60s+ 不再自己消失(心跳补丁生效)。
74
+ 5. 插件设置面板「上传文件」「输出文件」「技能文档」都在。
75
+ 6. `grep -q WEBSOCKET_HEARTBEAT_MS <核心解压路径>/dsh-client-connection/lib/index.js` → 心跳在位。
76
+
77
+ ## 硬性安全边界(必须遵守)
78
+ - **不主动 push / 打 tag / 发 Release**;需要发布版本时停下用 ask_user_question 等确认。
79
+ - 升级会重启 DSH 服务,可能中断当前会话——先向用户说明再执行。
80
+ - 若升级脚本/补丁脚本报"未找到待替换代码"(DSH 已改逻辑),停下提示用户,不强上。
81
+
82
+ ## 常见坑速查
83
+ | 症状 | 原因 | 解决 |
84
+ |---|---|---|
85
+ | 升级后设置页 403 | 补丁1 被覆盖 | 重跑 apply-dsh-patches.sh |
86
+ | 升级后设置页"未暴露命名空间" | 补丁2 被覆盖 | 重跑 apply-dsh-patches.sh |
87
+ | 升级后提问窗口自己消失 | 心跳补丁(补丁3)被覆盖 | 重跑 apply-dsh-patches.sh 或装插件补丁脚本 |
88
+ | 升级后插件不显示 | 插件安装副本未同步 | 步骤 5 `pnpm install` |
89
+ | 按钮全没了/界面异常 | 浏览器缓存了旧前端 | 强刷 Ctrl/Cmd+Shift+R |