dsh-plugin-workbench 0.0.8 → 0.0.9
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 +11 -0
- package/README.md +12 -4
- package/lib/index.js +78 -2
- package/package.json +1 -1
- package/src/index.ts +82 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
5
5
|
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
|
6
6
|
|
|
7
|
+
## [0.0.9] - 2026-08-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **布局补丁自动修复(自愈)**:插件 host 端在启动时自动检测
|
|
12
|
+
`dsh-client-ui-layout` 客户端 bundle 是否还带 explorer 列补丁;若已被
|
|
13
|
+
DSH 升级 / `pnpm install` 覆盖还原(这正是 workbench 列无声消失的根因),
|
|
14
|
+
会自动重跑 `scripts/patch-layout.mjs` 重打。幂等(已打则跳过)、非阻塞
|
|
15
|
+
(spawn 后台执行,不影响启动)、安全(锚点不匹配时脚本 abort 不写文件,
|
|
16
|
+
仅记录警告,插件照常启动)。
|
|
17
|
+
|
|
7
18
|
## [0.0.8] - 2026-08-20
|
|
8
19
|
|
|
9
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
粘贴 / 在系统中打开 / 在资源管理器打开)+ 图片内联预览,每个工作区独立保存状态。
|
|
12
12
|
装上之后,DSH 网页就是一个轻量代码编辑器。
|
|
13
13
|
|
|
14
|
-
> ⚠️
|
|
14
|
+
> ⚠️ 安装后需打一个布局补丁(见下方安装节);0.0.9 起插件启动时会自动检测并重打,
|
|
15
|
+
> DSH 升级覆盖 bundle 后无需再手动跑。
|
|
15
16
|
|
|
16
17
|
## 功能
|
|
17
18
|
|
|
@@ -53,14 +54,17 @@
|
|
|
53
54
|
|
|
54
55
|
无需环境变量或配置文件,安装后打一次布局补丁即可:
|
|
55
56
|
|
|
56
|
-
-
|
|
57
|
-
|
|
57
|
+
- **布局补丁**:插件 host 端启动时自动检测并重跑 `scripts/patch-layout.mjs`
|
|
58
|
+
(幂等、非阻塞;针对 `dsh-client-ui-layout@0.1.0-rc.6` 编写)。DSH 升级覆盖
|
|
59
|
+
bundle 后插件会自动补回;若自动重打失败(锚点失效),可手动运行
|
|
60
|
+
`node node_modules/dsh-plugin-workbench/scripts/patch-layout.mjs`;
|
|
58
61
|
- **行为偏好**(自动换行、亮暗主题、标签布局、文件树展开状态)按工作区持久化,无需手动配置;
|
|
59
62
|
- **文件操作通道**:经 loopback RPC `/dsh-plugin-files`,写操作显式以 `danger-full-access` 执行,无外部配置项。
|
|
60
63
|
|
|
61
64
|
## 安装
|
|
62
65
|
|
|
63
66
|
> 布局补丁针对 `dsh-client-ui-layout@0.1.0-rc.6` 编写,其他版本需更新锚点。
|
|
67
|
+
> 0.0.9 起插件启动时自动检测并重打补丁(详见「配置」节)。
|
|
64
68
|
|
|
65
69
|
```powershell
|
|
66
70
|
# npm(推荐)
|
|
@@ -86,6 +90,9 @@ node node_modules/dsh-plugin-workbench/scripts/patch-layout.mjs
|
|
|
86
90
|
# 重启 dsh web
|
|
87
91
|
```
|
|
88
92
|
|
|
93
|
+
> 0.0.9 起插件启动时会自动检测并重打布局补丁,DSH 升级后无需再手动跑;
|
|
94
|
+
> 仅当自动重打失败(锚点失效)时才需手动执行上面的命令。
|
|
95
|
+
|
|
89
96
|
## 开发
|
|
90
97
|
|
|
91
98
|
```powershell
|
|
@@ -107,7 +114,8 @@ dsh plugin --profile web remove dsh-plugin-workbench
|
|
|
107
114
|
右键菜单的新建/重命名/删除同样经该通道(loopback 信任,与编辑器保存一致)
|
|
108
115
|
- 图片预览走同源路由 `/dsh-plugin-files/raw/<path>`:仅响应图片扩展名,
|
|
109
116
|
先经 `ctx.fs.resolve → stat`(沙箱一致的路径解析)再读取字节,20MB 上限
|
|
110
|
-
- DSH 升级会覆盖布局 bundle
|
|
117
|
+
- DSH 升级会覆盖布局 bundle;0.0.9 起插件启动时自动检测并重打补丁,
|
|
118
|
+
无需手动重跑;锚点失效时需更新 `scripts/patch-layout.mjs`
|
|
111
119
|
- 本仓库不包含 DSH 编译产物
|
|
112
120
|
|
|
113
121
|
## 参与贡献
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { watch } from "node:fs";
|
|
3
|
-
import { basename, dirname } from "node:path";
|
|
2
|
+
import { existsSync, readFileSync, watch } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { cp, mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { homedir } from "node:os";
|
|
5
7
|
//#region src/index.ts
|
|
6
8
|
const name = "dsh-plugin-workbench";
|
|
7
9
|
const inject = [
|
|
@@ -154,10 +156,84 @@ function pathOf(payload) {
|
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
/**
|
|
159
|
+
* The explorer-column patch markers that scripts/patch-layout.mjs injects into
|
|
160
|
+
* the compiled dsh-client-ui-layout client bundle. A dsh upgrade (or a
|
|
161
|
+
* `pnpm install` that refreshes the ui-layout package) silently reverts that
|
|
162
|
+
* bundle, which makes the workbench column vanish even though this plugin is
|
|
163
|
+
* fine — this is the exact failure this auto-heal guards against.
|
|
164
|
+
*/
|
|
165
|
+
const LAYOUT_PATCH_MARKERS = [
|
|
166
|
+
"\"explorerCol\": \"",
|
|
167
|
+
"setExplorer: (d, px) => {",
|
|
168
|
+
"renderSlot(\"explorer\"",
|
|
169
|
+
"conversationSeat"
|
|
170
|
+
];
|
|
171
|
+
/** Resolve the installed dsh-client-ui-layout client bundle (profile node_modules junction). */
|
|
172
|
+
function layoutClientPath() {
|
|
173
|
+
const dshHome = process.env.DSH_HOME ?? join(homedir(), ".dsh");
|
|
174
|
+
return join(dshHome, "profiles", "node_modules", "@deepseek-ai", "dsh-client-ui-layout", "lib", "client.js");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* True when the ui-layout bundle already carries the explorer-column patch.
|
|
178
|
+
* A missing bundle (non-standard install) is treated as "nothing to patch" so
|
|
179
|
+
* the check never blocks a boot; an unreadable file likewise bails out to the
|
|
180
|
+
* caller rather than throwing.
|
|
181
|
+
*/
|
|
182
|
+
function layoutIsPatched() {
|
|
183
|
+
try {
|
|
184
|
+
const target = layoutClientPath();
|
|
185
|
+
if (!existsSync(target)) return true;
|
|
186
|
+
const text = readFileSync(target, "utf8");
|
|
187
|
+
return LAYOUT_PATCH_MARKERS.every((marker) => text.includes(marker));
|
|
188
|
+
} catch {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Module-level guard so a HMR/re-apply burst never spawns the patch twice concurrently. */
|
|
193
|
+
let layoutPatchScheduled = false;
|
|
194
|
+
/**
|
|
195
|
+
* Re-apply the ui-layout explorer-column patch when it is missing.
|
|
196
|
+
*
|
|
197
|
+
* The workbench column renders into a fourth `explorer` slot that is added to
|
|
198
|
+
* the compiled dsh-client-ui-layout bundle by scripts/patch-layout.mjs. A dsh
|
|
199
|
+
* upgrade silently reverts that bundle, so this re-runs the same
|
|
200
|
+
* version-checked script: anchors that no longer match abort the script
|
|
201
|
+
* WITHOUT writing, so an incompatible dsh version never corrupts the bundle —
|
|
202
|
+
* it only logs a warning and the plugin still boots. Idempotent and
|
|
203
|
+
* non-blocking (spawned fire-and-forget), so it never delays a boot.
|
|
204
|
+
*/
|
|
205
|
+
function ensureLayoutPatch() {
|
|
206
|
+
if (layoutPatchScheduled) return;
|
|
207
|
+
layoutPatchScheduled = true;
|
|
208
|
+
try {
|
|
209
|
+
if (layoutIsPatched()) return;
|
|
210
|
+
const script = join(dirname(dirname(fileURLToPath(import.meta.url))), "scripts", "patch-layout.mjs");
|
|
211
|
+
const child = spawn(process.execPath, [script], {
|
|
212
|
+
stdio: "inherit",
|
|
213
|
+
windowsHide: true
|
|
214
|
+
});
|
|
215
|
+
child.on("error", (err) => {
|
|
216
|
+
console.warn("[dsh-plugin-workbench] re-applying ui-layout explorer patch failed:", err.message);
|
|
217
|
+
layoutPatchScheduled = false;
|
|
218
|
+
});
|
|
219
|
+
child.on("close", (code) => {
|
|
220
|
+
if (code === 0) console.log("[dsh-plugin-workbench] re-applied the missing dsh-client-ui-layout explorer patch (likely reverted by a dsh upgrade).");
|
|
221
|
+
else {
|
|
222
|
+
console.warn(`[dsh-plugin-workbench] ui-layout patch exited ${code}; the dsh version may have changed — run scripts/patch-layout.mjs manually.`);
|
|
223
|
+
layoutPatchScheduled = false;
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.warn("[dsh-plugin-workbench] ui-layout patch check failed:", err);
|
|
228
|
+
layoutPatchScheduled = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
157
232
|
* One filesystem-backed RPC endpoint pair. Reads never mutate; `signal`
|
|
158
233
|
* cancels the underlying fs call (or aborts between steps).
|
|
159
234
|
*/
|
|
160
235
|
function apply(ctx) {
|
|
236
|
+
ensureLayoutPatch();
|
|
161
237
|
const watchState = {
|
|
162
238
|
dirs: /* @__PURE__ */ new Map(),
|
|
163
239
|
files: /* @__PURE__ */ new Map(),
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -27,10 +27,12 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
29
29
|
import { spawn } from 'node:child_process'
|
|
30
|
-
import { watch as watchFs } from 'node:fs'
|
|
30
|
+
import { existsSync, readFileSync, watch as watchFs } from 'node:fs'
|
|
31
31
|
import type { FSWatcher } from 'node:fs'
|
|
32
|
-
import { basename, dirname } from 'node:path'
|
|
32
|
+
import { basename, dirname, join } from 'node:path'
|
|
33
33
|
import { mkdir, rename as renameFs, rm, cp, writeFile as writeFileNode } from 'node:fs/promises'
|
|
34
|
+
import { fileURLToPath } from 'node:url'
|
|
35
|
+
import { homedir } from 'node:os'
|
|
34
36
|
import type { Context } from '@deepseek-ai/cordis'
|
|
35
37
|
|
|
36
38
|
export const name = 'dsh-plugin-workbench'
|
|
@@ -252,11 +254,89 @@ function pathOf(payload: unknown): string | undefined {
|
|
|
252
254
|
return undefined
|
|
253
255
|
}
|
|
254
256
|
|
|
257
|
+
/**
|
|
258
|
+
* The explorer-column patch markers that scripts/patch-layout.mjs injects into
|
|
259
|
+
* the compiled dsh-client-ui-layout client bundle. A dsh upgrade (or a
|
|
260
|
+
* `pnpm install` that refreshes the ui-layout package) silently reverts that
|
|
261
|
+
* bundle, which makes the workbench column vanish even though this plugin is
|
|
262
|
+
* fine — this is the exact failure this auto-heal guards against.
|
|
263
|
+
*/
|
|
264
|
+
const LAYOUT_PATCH_MARKERS = [
|
|
265
|
+
'"explorerCol": "',
|
|
266
|
+
'setExplorer: (d, px) => {',
|
|
267
|
+
'renderSlot("explorer"',
|
|
268
|
+
'conversationSeat',
|
|
269
|
+
] as const
|
|
270
|
+
|
|
271
|
+
/** Resolve the installed dsh-client-ui-layout client bundle (profile node_modules junction). */
|
|
272
|
+
function layoutClientPath(): string {
|
|
273
|
+
const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh')
|
|
274
|
+
return join(dshHome, 'profiles', 'node_modules', '@deepseek-ai', 'dsh-client-ui-layout', 'lib', 'client.js')
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* True when the ui-layout bundle already carries the explorer-column patch.
|
|
279
|
+
* A missing bundle (non-standard install) is treated as "nothing to patch" so
|
|
280
|
+
* the check never blocks a boot; an unreadable file likewise bails out to the
|
|
281
|
+
* caller rather than throwing.
|
|
282
|
+
*/
|
|
283
|
+
function layoutIsPatched(): boolean {
|
|
284
|
+
try {
|
|
285
|
+
const target = layoutClientPath()
|
|
286
|
+
if (!existsSync(target)) return true
|
|
287
|
+
const text = readFileSync(target, 'utf8')
|
|
288
|
+
return LAYOUT_PATCH_MARKERS.every((marker) => text.includes(marker))
|
|
289
|
+
} catch {
|
|
290
|
+
return true
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Module-level guard so a HMR/re-apply burst never spawns the patch twice concurrently. */
|
|
295
|
+
let layoutPatchScheduled = false
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Re-apply the ui-layout explorer-column patch when it is missing.
|
|
299
|
+
*
|
|
300
|
+
* The workbench column renders into a fourth `explorer` slot that is added to
|
|
301
|
+
* the compiled dsh-client-ui-layout bundle by scripts/patch-layout.mjs. A dsh
|
|
302
|
+
* upgrade silently reverts that bundle, so this re-runs the same
|
|
303
|
+
* version-checked script: anchors that no longer match abort the script
|
|
304
|
+
* WITHOUT writing, so an incompatible dsh version never corrupts the bundle —
|
|
305
|
+
* it only logs a warning and the plugin still boots. Idempotent and
|
|
306
|
+
* non-blocking (spawned fire-and-forget), so it never delays a boot.
|
|
307
|
+
*/
|
|
308
|
+
function ensureLayoutPatch(): void {
|
|
309
|
+
if (layoutPatchScheduled) return
|
|
310
|
+
layoutPatchScheduled = true
|
|
311
|
+
try {
|
|
312
|
+
if (layoutIsPatched()) return
|
|
313
|
+
const script = join(dirname(dirname(fileURLToPath(import.meta.url))), 'scripts', 'patch-layout.mjs')
|
|
314
|
+
const child = spawn(process.execPath, [script], { stdio: 'inherit', windowsHide: true })
|
|
315
|
+
child.on('error', (err) => {
|
|
316
|
+
console.warn('[dsh-plugin-workbench] re-applying ui-layout explorer patch failed:', err.message)
|
|
317
|
+
layoutPatchScheduled = false
|
|
318
|
+
})
|
|
319
|
+
child.on('close', (code) => {
|
|
320
|
+
if (code === 0) {
|
|
321
|
+
console.log('[dsh-plugin-workbench] re-applied the missing dsh-client-ui-layout explorer patch (likely reverted by a dsh upgrade).')
|
|
322
|
+
} else {
|
|
323
|
+
console.warn(`[dsh-plugin-workbench] ui-layout patch exited ${code}; the dsh version may have changed — run scripts/patch-layout.mjs manually.`)
|
|
324
|
+
layoutPatchScheduled = false
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
} catch (err) {
|
|
328
|
+
console.warn('[dsh-plugin-workbench] ui-layout patch check failed:', err)
|
|
329
|
+
layoutPatchScheduled = false
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
255
333
|
/**
|
|
256
334
|
* One filesystem-backed RPC endpoint pair. Reads never mutate; `signal`
|
|
257
335
|
* cancels the underlying fs call (or aborts between steps).
|
|
258
336
|
*/
|
|
259
337
|
export function apply(ctx: Context): void {
|
|
338
|
+
// Re-apply the ui-layout explorer-column patch when a dsh upgrade reverted it.
|
|
339
|
+
ensureLayoutPatch()
|
|
260
340
|
// Per-apply watch state: created here (not module-level) so disable/reload
|
|
261
341
|
// cycles never leak watchers or SSE clients across applies.
|
|
262
342
|
const watchState: WatchState = {
|